19 package org.sleuthkit.datamodel;
21 import com.google.common.base.Strings;
22 import org.apache.commons.lang3.StringUtils;
23 import java.sql.PreparedStatement;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.sql.Types;
28 import java.util.Collections;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.NavigableMap;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.UUID;
35 import java.util.concurrent.ConcurrentSkipListMap;
36 import java.util.stream.Collectors;
56 private final Object osAcctInstancesCacheLock;
57 private final NavigableMap<OsAccountInstanceKey, OsAccountInstance> osAccountInstanceCache;
67 osAcctInstancesCacheLock =
new Object();
68 osAccountInstanceCache =
new ConcurrentSkipListMap<>();
87 if (Strings.isNullOrEmpty(uniqueAccountId)) {
104 }
catch (SQLException ex) {
110 Optional<OsAccount> osAccount = this.getOsAccountByAddr(uniqueAccountId, realm);
111 if (osAccount.isPresent()) {
112 return osAccount.get();
116 throw new TskCoreException(String.format(
"Error creating OsAccount with uniqueAccountId = %s in realm id = %d", uniqueAccountId, realm.getRealmId()), ex);
151 if (realmScope == null) {
152 throw new TskCoreException(
"RealmScope cannot be null. Use UNKNOWN if scope is not known.");
154 if (referringHost == null) {
155 throw new TskCoreException(
"A referring host is required to create an account.");
159 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
160 && StringUtils.isBlank(loginName)) {
161 throw new TskCoreException(
"Cannot create OS account with both uniqueId and loginName as null.");
164 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
165 && StringUtils.isBlank(realmName)) {
166 throw new TskCoreException(
"Realm name or SID is required to create a Windows account.");
169 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
174 if (StringUtils.isBlank(sid)
175 && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName)
176 && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
177 sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
182 Optional<OsAccountRealm> anotherRealmWithSameName = Optional.empty();
183 Optional<OsAccountRealm> anotherRealmWithSameAddr = Optional.empty();
187 try (CaseDbConnection connection = db.getConnection()) {
188 realmUpdateResult = db.
getOsAccountRealmManager().getAndUpdateWindowsRealm(sid, realmName, referringHost, connection);
190 Optional<OsAccountRealm> realmOptional = realmUpdateResult.
getUpdatedRealm();
191 if (realmOptional.isPresent()) {
192 realm = realmOptional.get();
200 anotherRealmWithSameName = db.
getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, connection);
203 anotherRealmWithSameAddr = db.
getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, connection);
213 if (anotherRealmWithSameName.isPresent() || anotherRealmWithSameAddr.isPresent()) {
217 if (anotherRealmWithSameName.isPresent()) {
220 if (anotherRealmWithSameAddr.isPresent()) {
225 }
catch (TskCoreException ex) {
256 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
257 && StringUtils.isBlank(loginName)) {
258 throw new TskCoreException(
"Cannot create OS account with both uniqueId and loginName as null.");
261 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
266 String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
272 String uniqueId = (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? sid : null;
273 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
275 String wellKnownLoginName = WindowsAccountUtils.getWindowsWellKnownSidLoginName(sid);
276 if (!StringUtils.isEmpty(wellKnownLoginName)) {
277 resolvedLoginName = wellKnownLoginName;
284 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
285 String fullName = getWindowsWellKnownSidFullName(sid);
286 if (StringUtils.isNotBlank(fullName)) {
296 }
catch (SQLException ex) {
302 Optional<OsAccount> osAccount;
305 if (!Strings.isNullOrEmpty(sid)) {
306 osAccount = getOsAccountByAddr(sid, realm);
307 if (osAccount.isPresent()) {
308 return osAccount.get();
313 if (!Strings.isNullOrEmpty(resolvedLoginName)) {
314 osAccount = getOsAccountByLoginName(resolvedLoginName, realm);
315 if (osAccount.isPresent()) {
316 return osAccount.get();
321 throw new TskCoreException(String.format(
"Error creating OsAccount with sid = %s, loginName = %s, realm = %s, referring host = %s",
322 (sid != null) ? sid :
"Null",
323 (resolvedLoginName != null) ? resolvedLoginName :
"Null",
350 if (Objects.isNull(realm)) {
351 throw new TskCoreException(
"Cannot create an OS Account, realm is NULL.");
354 String signature = getOsAccountSignature(uniqueId, loginName);
357 CaseDbConnection connection = trans.getConnection();
362 long parentObjId = 0;
364 int objTypeId = TskData.ObjectType.OS_ACCOUNT.getObjectType();
365 long osAccountObjId = db.addObject(parentObjId, objTypeId, connection);
367 String accountInsertSQL =
"INSERT INTO tsk_os_accounts(os_account_obj_id, login_name, realm_id, addr, signature, status)"
368 +
" VALUES (?, ?, ?, ?, ?, ?)";
370 PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.NO_GENERATED_KEYS);
371 preparedStatement.clearParameters();
373 preparedStatement.setLong(1, osAccountObjId);
375 preparedStatement.setString(2, loginName);
376 preparedStatement.setLong(3, realm.getRealmId());
378 preparedStatement.setString(4, uniqueId);
379 preparedStatement.setString(5, signature);
380 preparedStatement.setInt(6, accountStatus.getId());
382 connection.executeUpdate(preparedStatement);
384 account =
new OsAccount(db, osAccountObjId, realm.getRealmId(), loginName, uniqueId, signature,
385 null, null, null, accountStatus, OsAccount.OsAccountDbStatus.ACTIVE);
387 trans.registerAddedOsAccount(account);
402 private Optional<OsAccount> getOsAccountByAddr(String addr, Host host)
throws TskCoreException {
404 try (CaseDbConnection connection = db.getConnection()) {
405 return getOsAccountByAddr(addr, host, connection);
421 private Optional<OsAccount> getOsAccountByAddr(String uniqueId, Host host, CaseDbConnection connection)
throws TskCoreException {
423 String whereHostClause = (host == null)
425 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL) ";
427 String queryString =
"SELECT accounts.os_account_obj_id as os_account_obj_id, accounts.login_name, accounts.full_name, "
428 +
" accounts.realm_id, accounts.addr, accounts.signature, "
429 +
" accounts.type, accounts.status, accounts.admin, accounts.created_date, accounts.db_status, "
430 +
" realms.realm_name as realm_name, realms.realm_addr as realm_addr, realms.realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status as realm_db_status "
431 +
" FROM tsk_os_accounts as accounts"
432 +
" LEFT JOIN tsk_os_account_realms as realms"
433 +
" ON accounts.realm_id = realms.id"
434 +
" WHERE " + whereHostClause
436 +
" AND LOWER(accounts.addr) = LOWER('" + uniqueId +
"')";
439 try (Statement s = connection.createStatement();
440 ResultSet rs = connection.executeQuery(s, queryString)) {
443 return Optional.empty();
445 return Optional.of(osAccountFromResultSet(rs));
447 }
catch (SQLException ex) {
448 throw new TskCoreException(String.format(
"Error getting OS account for unique id = %s and host = %s", uniqueId, (host != null ? host.getName() :
"null")), ex);
465 Optional<OsAccount> getOsAccountByAddr(String uniqueId, OsAccountRealm realm)
throws TskCoreException {
467 String queryString =
"SELECT * FROM tsk_os_accounts"
468 +
" WHERE LOWER(addr) = LOWER('" + uniqueId +
"')"
470 +
" AND realm_id = " + realm.getRealmId();
473 try (CaseDbConnection connection = this.db.getConnection();
474 Statement s = connection.createStatement();
475 ResultSet rs = connection.executeQuery(s, queryString)) {
478 return Optional.empty();
480 return Optional.of(osAccountFromResultSet(rs));
482 }
catch (SQLException ex) {
483 throw new TskCoreException(String.format(
"Error getting OS account for realm = %s and uniqueId = %s.", (realm != null) ? realm.getSignature() :
"NULL", uniqueId), ex);
500 Optional<OsAccount> getOsAccountByLoginName(String loginName, OsAccountRealm realm)
throws TskCoreException {
502 String queryString =
"SELECT * FROM tsk_os_accounts"
503 +
" WHERE LOWER(login_name) = LOWER('" + loginName +
"')"
505 +
" AND realm_id = " + realm.getRealmId();
508 try (CaseDbConnection connection = this.db.getConnection();
509 Statement s = connection.createStatement();
510 ResultSet rs = connection.executeQuery(s, queryString)) {
513 return Optional.empty();
515 return Optional.of(osAccountFromResultSet(rs));
517 }
catch (SQLException ex) {
518 throw new TskCoreException(String.format(
"Error getting OS account for realm = %s and loginName = %s.", (realm != null) ? realm.getSignature() :
"NULL", loginName), ex);
535 try (CaseDbConnection connection = this.db.getConnection()) {
552 String queryString =
"SELECT * FROM tsk_os_accounts"
553 +
" WHERE os_account_obj_id = " + osAccountObjId;
556 try (Statement s = connection.createStatement();
557 ResultSet rs = connection.executeQuery(s, queryString)) {
560 throw new TskCoreException(String.format(
"No account found with obj id = %d ", osAccountObjId));
562 return osAccountFromResultSet(rs);
564 }
catch (SQLException ex) {
565 throw new TskCoreException(String.format(
"Error getting account with obj id = %d ", osAccountObjId), ex);
596 if (osAccount == null) {
597 throw new TskCoreException(
"Cannot create account instance with null account.");
599 if (dataSource == null) {
600 throw new TskCoreException(
"Cannot create account instance with null data source.");
604 Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccount.
getId(), dataSource.
getId(), instanceType);
605 if (existingInstance.isPresent()) {
606 return existingInstance.get();
609 try (CaseDbConnection connection = this.db.getConnection()) {
631 Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
632 if (existingInstance.isPresent()) {
633 return existingInstance.get();
641 String accountInsertSQL = db.getInsertOrIgnoreSQL(
"INTO tsk_os_account_instances(os_account_obj_id, data_source_obj_id, instance_type)"
642 +
" VALUES (?, ?, ?)");
643 PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.RETURN_GENERATED_KEYS);
644 preparedStatement.clearParameters();
645 preparedStatement.setLong(1, osAccountId);
646 preparedStatement.setLong(2, dataSourceObjId);
647 preparedStatement.setInt(3, instanceType.getId());
648 connection.executeUpdate(preparedStatement);
649 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
650 if (resultSet.next()) {
651 OsAccountInstance accountInstance =
new OsAccountInstance(db, resultSet.getLong(1), osAccountId, dataSourceObjId, instanceType);
652 synchronized (osAcctInstancesCacheLock) {
653 OsAccountInstanceKey key =
new OsAccountInstanceKey(osAccountId, dataSourceObjId);
655 for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) {
656 if (accountInstance.getInstanceType().compareTo(type) < 0) {
657 osAccountInstanceCache.remove(key);
661 osAccountInstanceCache.put(key, accountInstance);
678 db.fireTSKEvent(
new TskEvent.OsAcctInstancesAddedTskEvent(Collections.singletonList(accountInstance)));
680 return accountInstance;
685 Optional<OsAccountInstance> existingInstanceRetry = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
686 if (existingInstanceRetry.isPresent()) {
687 return existingInstanceRetry.get();
689 throw new TskCoreException(String.format(
"Could not get autogen key after row insert for OS account instance. OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId));
693 }
catch (SQLException ex) {
694 throw new TskCoreException(String.format(
"Error adding OS account instance for OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId), ex);
716 private Optional<OsAccountInstance> cachedAccountInstance(
long osAccountId,
long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType) {
725 synchronized (osAcctInstancesCacheLock) {
726 OsAccountInstanceKey key =
new OsAccountInstanceKey(osAccountId, dataSourceObjId);
727 OsAccountInstance instance = osAccountInstanceCache.get(key);
728 if (instance != null) {
730 if (instanceType.compareTo(instance.getInstanceType()) >= 0) {
731 return Optional.of(instance);
734 return Optional.empty();
748 String queryString =
"SELECT * FROM tsk_os_accounts accounts "
749 +
"WHERE accounts.os_account_obj_id IN "
750 +
"(SELECT instances.os_account_obj_id "
751 +
"FROM tsk_os_account_instances instances "
752 +
"INNER JOIN data_source_info datasources ON datasources.obj_id = instances.data_source_obj_id "
753 +
"WHERE datasources.host_id = " + host.getHostId() +
") "
757 try (CaseDbConnection connection = this.db.getConnection();
758 Statement s = connection.createStatement();
759 ResultSet rs = connection.executeQuery(s, queryString)) {
761 List<OsAccount> accounts =
new ArrayList<>();
763 accounts.add(osAccountFromResultSet(rs));
766 }
catch (SQLException ex) {
767 throw new TskCoreException(String.format(
"Error getting OS accounts for host id = %d", host.getHostId()), ex);
783 String queryString =
"SELECT * FROM tsk_os_accounts acc "
784 +
"WHERE acc.os_account_obj_id IN "
785 +
"(SELECT instance.os_account_obj_id "
786 +
"FROM tsk_os_account_instances instance "
787 +
"WHERE instance.data_source_obj_id = " + dataSourceId +
") "
791 try (CaseDbConnection connection = this.db.getConnection();
792 Statement s = connection.createStatement();
793 ResultSet rs = connection.executeQuery(s, queryString)) {
795 List<OsAccount> accounts =
new ArrayList<>();
797 accounts.add(osAccountFromResultSet(rs));
800 }
catch (SQLException ex) {
801 throw new TskCoreException(String.format(
"Error getting OS accounts for data source id = %d", dataSourceId), ex);
820 List<OsAccount> destinationAccounts =
getOsAccounts(destRealm, trans.getConnection());
821 List<OsAccount> sourceAccounts =
getOsAccounts(sourceRealm, trans.getConnection());
823 for (
OsAccount sourceAccount : sourceAccounts) {
830 if (sourceAccount.getAddr().isPresent() && sourceAccount.getLoginName().isPresent()) {
831 List<OsAccount> duplicateDestAccounts = destinationAccounts.stream()
832 .filter(p -> p.getAddr().equals(sourceAccount.getAddr())
833 || (p.getLoginName().equals(sourceAccount.getLoginName()) && (!p.getAddr().isPresent())))
834 .collect(Collectors.toList());
835 if (duplicateDestAccounts.size() > 1) {
836 OsAccount combinedDestAccount = duplicateDestAccounts.get(0);
837 duplicateDestAccounts.remove(combinedDestAccount);
838 for (
OsAccount dupeDestAccount : duplicateDestAccounts) {
839 mergeOsAccounts(dupeDestAccount, combinedDestAccount, trans);
845 OsAccount matchingDestAccount = null;
848 if (sourceAccount.getAddr().isPresent()) {
849 List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
850 .filter(p -> p.getAddr().equals(sourceAccount.getAddr()))
851 .collect(Collectors.toList());
852 if (!matchingDestAccounts.isEmpty()) {
853 matchingDestAccount = matchingDestAccounts.get(0);
862 if (matchingDestAccount == null && sourceAccount.getLoginName().isPresent()) {
863 List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
864 .filter(p -> p.getLoginName().isPresent())
865 .filter(p -> (p.getLoginName().get().equalsIgnoreCase(sourceAccount.getLoginName().get())
866 && ((!sourceAccount.getAddr().isPresent()) || (!p.getAddr().isPresent()))))
867 .collect(Collectors.toList());
868 if (!matchingDestAccounts.isEmpty()) {
869 matchingDestAccount = matchingDestAccounts.get(0);
874 if (matchingDestAccount != null) {
875 mergeOsAccounts(sourceAccount, matchingDestAccount, trans);
877 String query =
"UPDATE tsk_os_accounts SET realm_id = " + destRealm.getRealmId() +
" WHERE os_account_obj_id = " + sourceAccount.getId();
878 try (Statement s = trans.getConnection().createStatement()) {
879 s.executeUpdate(query);
880 }
catch (SQLException ex) {
881 throw new TskCoreException(
"Error executing SQL update: " + query, ex);
883 trans.registerChangedOsAccount(sourceAccount);
902 private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans)
throws TskCoreException {
905 try (Statement s = trans.getConnection().createStatement()) {
908 query = makeOsAccountUpdateQuery(
"tsk_os_account_attributes", sourceAccount, destAccount);
909 s.executeUpdate(query);
913 query =
"DELETE FROM tsk_os_account_instances "
916 +
" sourceAccountInstance.id "
918 +
" tsk_os_account_instances destAccountInstance "
919 +
"INNER JOIN tsk_os_account_instances sourceAccountInstance ON destAccountInstance.data_source_obj_id = sourceAccountInstance.data_source_obj_id "
920 +
"WHERE destAccountInstance.os_account_obj_id = " + destAccount.getId()
921 +
" AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId()
922 +
" AND sourceAccountInstance.instance_type = destAccountInstance.instance_type" +
")";
924 s.executeUpdate(query);
926 query = makeOsAccountUpdateQuery(
"tsk_os_account_instances", sourceAccount, destAccount);
927 s.executeUpdate(query);
928 synchronized (osAcctInstancesCacheLock) {
929 osAccountInstanceCache.clear();
932 query = makeOsAccountUpdateQuery(
"tsk_files", sourceAccount, destAccount);
933 s.executeUpdate(query);
935 query = makeOsAccountUpdateQuery(
"tsk_data_artifacts", sourceAccount, destAccount);
936 s.executeUpdate(query);
942 String mergedSignature = makeMergedOsAccountSignature();
943 query =
"UPDATE tsk_os_accounts SET merged_into = " + destAccount.getId()
944 +
", db_status = " + OsAccount.OsAccountDbStatus.MERGED.getId()
945 +
", signature = '" + mergedSignature +
"' "
946 +
" WHERE os_account_obj_id = " + sourceAccount.getId();
948 s.executeUpdate(query);
949 trans.registerDeletedOsAccount(sourceAccount.getId());
954 mergeOsAccountObjectsAndUpdateDestAccount(sourceAccount, destAccount, trans);
955 }
catch (SQLException ex) {
956 throw new TskCoreException(
"Error executing SQL update: " + query, ex);
965 private String makeMergedOsAccountSignature() {
966 return "MERGED " + UUID.randomUUID().toString();
978 private String makeOsAccountUpdateQuery(String tableName, OsAccount sourceAccount, OsAccount destAccount) {
979 return "UPDATE " + tableName +
" SET os_account_obj_id = " + destAccount.getId() +
" WHERE os_account_obj_id = " + sourceAccount.getId();
993 private OsAccount mergeOsAccountObjectsAndUpdateDestAccount(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans)
throws TskCoreException {
995 OsAccount mergedDestAccount = destAccount;
997 String destLoginName = null;
998 String destAddr = null;
1001 if (!destAccount.getLoginName().isPresent() && sourceAccount.getLoginName().isPresent()) {
1005 if (!destAccount.getAddr().isPresent() && sourceAccount.getAddr().isPresent()) {
1006 destAddr = sourceAccount.getAddr().get();
1010 OsAccountUpdateResult updateStatus = this.updateOsAccountCore(destAccount, destAddr, destLoginName, trans);
1012 if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
1013 mergedDestAccount = updateStatus.getUpdatedAccount().get();
1016 String destFullName = null;
1017 Long destCreationTime = null;
1018 if (!destAccount.getFullName().isPresent() && sourceAccount.getFullName().isPresent()) {
1019 destFullName = sourceAccount.getFullName().get();
1022 if (!destAccount.getCreationTime().isPresent() && sourceAccount.getCreationTime().isPresent()) {
1023 destCreationTime = sourceAccount.getCreationTime().get();
1029 if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
1030 mergedDestAccount = updateStatus.getUpdatedAccount().get();
1033 return mergedDestAccount;
1046 private List<OsAccount>
getOsAccounts(OsAccountRealm realm, CaseDbConnection connection)
throws TskCoreException {
1047 String queryString =
"SELECT * FROM tsk_os_accounts"
1048 +
" WHERE realm_id = " + realm.getRealmId()
1049 +
" AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
1050 +
" ORDER BY os_account_obj_id";
1052 try (Statement s = connection.createStatement();
1053 ResultSet rs = connection.executeQuery(s, queryString)) {
1055 List<OsAccount> accounts =
new ArrayList<>();
1057 accounts.add(osAccountFromResultSet(rs));
1060 }
catch (SQLException ex) {
1061 throw new TskCoreException(String.format(
"Error getting OS accounts for realm id = %d", realm.getRealmId()), ex);
1073 String queryString =
"SELECT * FROM tsk_os_accounts"
1077 try (CaseDbConnection connection = this.db.getConnection();
1078 Statement s = connection.createStatement();
1079 ResultSet rs = connection.executeQuery(s, queryString)) {
1081 List<OsAccount> accounts =
new ArrayList<>();
1083 accounts.add(osAccountFromResultSet(rs));
1086 }
catch (SQLException ex) {
1087 throw new TskCoreException(String.format(
"Error getting OS accounts"), ex);
1110 if (referringHost == null) {
1111 throw new TskCoreException(
"A referring host is required to get an account.");
1115 if ((StringUtils.isBlank(sid) || (sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ) && StringUtils.isBlank(loginName)) {
1116 throw new TskCoreException(
"Cannot get an OS account with both SID and loginName as null.");
1120 if (StringUtils.isBlank(sid)
1121 && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName)
1122 && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
1123 sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
1129 if (!realm.isPresent()) {
1130 return Optional.empty();
1134 if (!Strings.isNullOrEmpty(sid) && !(sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
1135 if (!WindowsAccountUtils.isWindowsUserSid(sid)) {
1139 Optional<OsAccount> account = this.getOsAccountByAddr(sid, realm.get());
1140 if (account.isPresent()) {
1146 if (!Strings.isNullOrEmpty(loginName)) {
1147 String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
1148 return this.getOsAccountByLoginName(resolvedLoginName, realm.get());
1150 return Optional.empty();
1165 synchronized (account) {
1168 try (CaseDbConnection connection = db.getConnection()) {
1171 String attributeInsertSQL =
"INSERT INTO tsk_os_account_attributes(os_account_obj_id, host_id, source_obj_id, attribute_type_id, value_type, value_byte, value_text, value_int32, value_int64, value_double)"
1172 +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
1174 PreparedStatement preparedStatement = connection.getPreparedStatement(attributeInsertSQL, Statement.RETURN_GENERATED_KEYS);
1175 preparedStatement.clearParameters();
1177 preparedStatement.setLong(1, account.getId());
1178 if (accountAttribute.getHostId().isPresent()) {
1179 preparedStatement.setLong(2, accountAttribute.getHostId().get());
1181 preparedStatement.setNull(2, java.sql.Types.NULL);
1183 if (accountAttribute.getSourceObjectId().isPresent()) {
1184 preparedStatement.setLong(3, accountAttribute.getSourceObjectId().get());
1186 preparedStatement.setNull(3, java.sql.Types.NULL);
1189 preparedStatement.setLong(4, accountAttribute.getAttributeType().getTypeID());
1190 preparedStatement.setLong(5, accountAttribute.getAttributeType().getValueType().getType());
1193 preparedStatement.setBytes(6, accountAttribute.getValueBytes());
1195 preparedStatement.setBytes(6, null);
1200 preparedStatement.setString(7, accountAttribute.getValueString());
1202 preparedStatement.setString(7, null);
1205 preparedStatement.setInt(8, accountAttribute.getValueInt());
1207 preparedStatement.setNull(8, java.sql.Types.NULL);
1212 preparedStatement.setLong(9, accountAttribute.getValueLong());
1214 preparedStatement.setNull(9, java.sql.Types.NULL);
1218 preparedStatement.setDouble(10, accountAttribute.getValueDouble());
1220 preparedStatement.setNull(10, java.sql.Types.NULL);
1223 connection.executeUpdate(preparedStatement);
1225 }
catch (SQLException ex) {
1226 throw new TskCoreException(String.format(
"Error adding OS Account attribute for account id = %d", account.getId()), ex);
1231 List<OsAccountAttribute> currentAttribsList = getOsAccountAttributes(account);
1232 account.setAttributesInternal(currentAttribsList);
1234 fireChangeEvent(account);
1246 List<OsAccountAttribute> getOsAccountAttributes(
OsAccount account)
throws TskCoreException {
1248 String queryString =
"SELECT attributes.os_account_obj_id as os_account_obj_id, attributes.host_id as host_id, attributes.source_obj_id as source_obj_id, "
1249 +
" attributes.attribute_type_id as attribute_type_id, attributes.value_type as value_type, attributes.value_byte as value_byte, "
1250 +
" attributes.value_text as value_text, attributes.value_int32 as value_int32, attributes.value_int64 as value_int64, attributes.value_double as value_double, "
1251 +
" hosts.id, hosts.name as host_name, hosts.db_status as host_status "
1252 +
" FROM tsk_os_account_attributes as attributes"
1253 +
" LEFT JOIN tsk_hosts as hosts "
1254 +
" ON attributes.host_id = hosts.id "
1255 +
" WHERE os_account_obj_id = " + account.getId();
1258 try (CaseDbConnection connection = this.db.getConnection();
1259 Statement s = connection.createStatement();
1260 ResultSet rs = connection.executeQuery(s, queryString)) {
1262 List<OsAccountAttribute> attributes =
new ArrayList<>();
1266 long hostId = rs.getLong(
"host_id");
1267 if (!rs.wasNull()) {
1268 host =
new Host(hostId, rs.getString(
"host_name"),
Host.
HostDbStatus.fromID(rs.getInt(
"host_status")));
1271 Content sourceContent = null;
1272 long sourceObjId = rs.getLong(
"source_obj_id");
1273 if (!rs.wasNull()) {
1277 OsAccountAttribute attribute = account.new OsAccountAttribute(attributeType, rs.getInt(
"value_int32"), rs.getLong(
"value_int64"),
1278 rs.getDouble(
"value_double"), rs.getString(
"value_text"), rs.getBytes(
"value_byte"),
1279 db, account, host, sourceContent);
1281 attributes.add(attribute);
1284 }
catch (SQLException ex) {
1285 throw new TskCoreException(String.format(
"Error getting OS account attributes for account obj id = %d", account.getId()), ex);
1301 String whereClause =
"tsk_os_account_instances.os_account_obj_id = " + account.getId();
1316 String instanceIds = instanceIDs.stream().map(
id ->
id.toString()).collect(Collectors.joining(
","));
1318 List<OsAccountInstance> osAcctInstances =
new ArrayList<>();
1320 String querySQL =
"SELECT * FROM tsk_os_account_instances "
1321 +
" WHERE tsk_os_account_instances.id IN (" + instanceIds +
")";
1324 try (CaseDbConnection connection = db.getConnection();
1325 PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
1326 ResultSet results = connection.executeQuery(preparedStatement)) {
1328 osAcctInstances = getOsAccountInstancesFromResultSet(results);
1330 }
catch (SQLException ex) {
1331 throw new TskCoreException(
"Failed to get OsAccountInstances (SQL = " + querySQL +
")", ex);
1335 return osAcctInstances;
1352 List<OsAccountInstance> osAcctInstances =
new ArrayList<>();
1355 =
"SELECT tsk_os_account_instances.* "
1356 +
" FROM tsk_os_account_instances "
1357 +
" INNER JOIN ( SELECT os_account_obj_id, data_source_obj_id, MIN(instance_type) AS min_instance_type "
1358 +
" FROM tsk_os_account_instances"
1359 +
" GROUP BY os_account_obj_id, data_source_obj_id ) grouped_instances "
1360 +
" ON tsk_os_account_instances.os_account_obj_id = grouped_instances.os_account_obj_id "
1361 +
" AND tsk_os_account_instances.instance_type = grouped_instances.min_instance_type "
1362 +
" WHERE " + whereClause;
1365 try (CaseDbConnection connection = db.getConnection();
1366 PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
1367 ResultSet results = connection.executeQuery(preparedStatement)) {
1369 osAcctInstances = getOsAccountInstancesFromResultSet(results);
1371 }
catch (SQLException ex) {
1372 throw new TskCoreException(
"Failed to get OsAccountInstances (SQL = " + querySQL +
")", ex);
1376 return osAcctInstances;
1388 private List<OsAccountInstance> getOsAccountInstancesFromResultSet(ResultSet results)
throws SQLException {
1390 List<OsAccountInstance> osAcctInstances =
new ArrayList<>();
1391 while (results.next()) {
1392 long instanceId = results.getLong(
"id");
1393 long osAccountObjID = results.getLong(
"os_account_obj_id");
1394 long dataSourceObjId = results.getLong(
"data_source_obj_id");
1395 int instanceType = results.getInt(
"instance_type");
1396 osAcctInstances.add(
new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType)));
1399 return osAcctInstances;
1427 return updateStatus;
1429 if (trans != null) {
1454 OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1457 CaseDbConnection connection = trans.getConnection();
1459 if (!StringUtils.isBlank(fullName)) {
1460 updateAccountColumn(osAccount.getId(),
"full_name", fullName, connection);
1461 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1464 if (Objects.nonNull(accountType)) {
1465 updateAccountColumn(osAccount.getId(),
"type", accountType, connection);
1466 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1469 if (Objects.nonNull(accountStatus)) {
1470 updateAccountColumn(osAccount.getId(),
"status", accountStatus, connection);
1471 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1474 if (Objects.nonNull(creationTime)) {
1475 updateAccountColumn(osAccount.getId(),
"created_date", creationTime, connection);
1476 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1480 if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1481 return new OsAccountUpdateResult(updateStatusCode, null);
1488 trans.registerChangedOsAccount(updatedAccount);
1490 return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
1492 }
catch (SQLException ex) {
1493 throw new TskCoreException(String.format(
"Error updating account with addr = %s, account id = %d", osAccount.getAddr().orElse(
"Unknown"), osAccount.getId()), ex);
1510 private <T>
void updateAccountColumn(
long accountObjId, String colName, T colValue, CaseDbConnection connection)
throws SQLException, TskCoreException {
1512 String updateSQL =
"UPDATE tsk_os_accounts "
1513 +
" SET " + colName +
" = ? "
1514 +
" WHERE os_account_obj_id = ?";
1518 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1519 preparedStatement.clearParameters();
1521 if (Objects.isNull(colValue)) {
1522 preparedStatement.setNull(1, Types.NULL);
1524 if (colValue instanceof String) {
1525 preparedStatement.setString(1, (String) colValue);
1526 }
else if (colValue instanceof Long) {
1527 preparedStatement.setLong(1, (Long) colValue);
1528 }
else if (colValue instanceof Integer) {
1529 preparedStatement.setInt(1, (Integer) colValue);
1531 throw new TskCoreException(String.format(
"Unhandled column data type received while updating the account (%d) ", accountObjId));
1535 preparedStatement.setLong(2, accountObjId);
1537 connection.executeUpdate(preparedStatement);
1553 private void updateAccountSignature(
long accountObjId, String signature, CaseDbConnection connection)
throws SQLException {
1555 String updateSQL =
"UPDATE tsk_os_accounts SET "
1557 +
" CASE WHEN db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId() +
" THEN ? ELSE signature END "
1558 +
" WHERE os_account_obj_id = ?";
1560 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1561 preparedStatement.clearParameters();
1563 preparedStatement.setString(1, signature);
1564 preparedStatement.setLong(2, accountObjId);
1566 connection.executeUpdate(preparedStatement);
1596 return updateStatus;
1598 if (trans != null) {
1627 if ((!StringUtils.isBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) || !StringUtils.isBlank(realmName)) {
1629 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
1635 Optional<OsAccountRealm> realmOptional = realmUpdateResult.
getUpdatedRealm();
1637 if (realmOptional.isPresent()) {
1644 Optional<OsAccountRealm> anotherRealmWithSameName = db.
getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, trans.getConnection());
1647 Optional<OsAccountRealm> anotherRealmWithSameAddr = db.
getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, trans.getConnection());
1649 if (anotherRealmWithSameName.isPresent()) {
1652 if (anotherRealmWithSameAddr.isPresent()) {
1660 String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
1661 OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, accountSid, resolvedLoginName, trans);
1663 return updateStatus;
1688 private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String address, String loginName, CaseDbTransaction trans)
throws TskCoreException {
1690 OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1691 OsAccount updatedAccount;
1694 CaseDbConnection connection = trans.getConnection();
1697 if (!StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(
""))) {
1698 throw new TskCoreException(String.format(
"Account (%d) already has an address (%s), address cannot be updated.", osAccount.getId(), osAccount.getAddr().orElse(
"NULL")));
1701 if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
1702 updateAccountColumn(osAccount.getId(),
"addr", address, connection);
1703 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1706 if (StringUtils.isBlank(osAccount.getLoginName().orElse(null)) && !StringUtils.isBlank(loginName)) {
1707 updateAccountColumn(osAccount.getId(),
"login_name", loginName, connection);
1708 updateStatusCode = OsAccountUpdateStatus.UPDATED;
1712 if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1713 return new OsAccountUpdateResult(updateStatusCode, osAccount);
1718 String newAddress = currAccount.
getAddr().orElse(null);
1719 String newLoginName = currAccount.getLoginName().orElse(null);
1721 String newSignature = getOsAccountSignature(newAddress, newLoginName);
1722 updateAccountSignature(osAccount.getId(), newSignature, connection);
1728 trans.registerChangedOsAccount(updatedAccount);
1730 return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
1732 }
catch (SQLException ex) {
1733 throw new TskCoreException(String.format(
"Error updating account with unique id = %s, account id = %d", osAccount.getAddr().orElse(
"Unknown"), osAccount.getId()), ex);
1747 List<Host> hostList =
new ArrayList<>();
1749 String query =
"SELECT tsk_hosts.id AS hostId, name, db_status FROM tsk_hosts "
1750 +
" JOIN data_source_info ON tsk_hosts.id = data_source_info.host_id"
1751 +
" JOIN tsk_os_account_instances ON data_source_info.obj_id = tsk_os_account_instances.data_source_obj_id"
1752 +
" WHERE os_account_obj_id = " + account.getId();
1755 try (CaseDbConnection connection = db.getConnection();
1756 Statement s = connection.createStatement();
1757 ResultSet rs = connection.executeQuery(s, query)) {
1760 hostList.add(
new Host(rs.getLong(
"hostId"), rs.getString(
"name"),
Host.
HostDbStatus.fromID(rs.getInt(
"db_status"))));
1763 }
catch (SQLException ex) {
1764 throw new TskCoreException(String.format(
"Failed to get host list for os account %d", account.getId()), ex);
1782 private OsAccount osAccountFromResultSet(ResultSet rs)
throws SQLException {
1785 int typeId = rs.getInt(
"type");
1786 if (!rs.wasNull()) {
1790 Long creationTime = rs.getLong(
"created_date");
1792 creationTime = null;
1795 return new OsAccount(db, rs.getLong(
"os_account_obj_id"), rs.getLong(
"realm_id"), rs.getString(
"login_name"), rs.getString(
"addr"),
1796 rs.getString(
"signature"), rs.getString(
"full_name"), creationTime, accountType, OsAccount.OsAccountStatus.
fromID(rs.getInt(
"status")),
1797 OsAccount.OsAccountDbStatus.
fromID(rs.getInt(
"db_status")));
1807 private void fireChangeEvent(OsAccount account) {
1808 db.fireTSKEvent(
new OsAccountsUpdatedTskEvent(Collections.singletonList(account)));
1825 static String getOsAccountSignature(String uniqueId, String loginName)
throws TskCoreException {
1828 if (Strings.isNullOrEmpty(uniqueId) ==
false) {
1829 signature = uniqueId;
1830 }
else if (Strings.isNullOrEmpty(loginName) ==
false) {
1831 signature = loginName;
1833 throw new TskCoreException(
"OS Account must have either a uniqueID or a login name.");
1844 private static final long serialVersionUID = 1L;
1850 super(
"No error message available.");
1893 this.updateStatus = updateStatus;
1894 this.updatedAccount = updatedAccount;
1898 return updateStatus;
1902 return Optional.ofNullable(updatedAccount);
1910 private class OsAccountInstanceKey
implements Comparable<OsAccountInstanceKey>{
1912 private final long osAccountId;
1913 private final long dataSourceId;
1915 OsAccountInstanceKey(
long osAccountId,
long dataSourceId) {
1916 this.osAccountId = osAccountId;
1917 this.dataSourceId = dataSourceId;
1921 public boolean equals(Object other) {
1922 if (
this == other) {
1925 if (other == null) {
1928 if (getClass() != other.getClass()) {
1932 final OsAccountInstanceKey otherKey = (OsAccountInstanceKey) other;
1934 if (osAccountId != otherKey.osAccountId) {
1938 return dataSourceId == otherKey.dataSourceId;
1942 public int hashCode() {
1944 hash = 53 * hash + (int) (this.osAccountId ^ (this.osAccountId >>> 32));
1945 hash = 53 * hash + (int) (this.dataSourceId ^ (this.dataSourceId >>> 32));
1950 public int compareTo(OsAccountInstanceKey other) {
1951 if(this.equals(other)) {
1955 if (dataSourceId != other.dataSourceId) {
1956 return Long.compare(dataSourceId, other.dataSourceId);
1959 return Long.compare(osAccountId, other.osAccountId);
NotUserSIDException(String msg, Exception ex)
List< OsAccount > getOsAccounts()
Optional< String > getAddr()
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
CaseDbTransaction beginTransaction()
List< Host > getHosts(OsAccount account)
OsAccount getOsAccountByObjectId(long osAccountObjId)
Optional< OsAccountRealm > getUpdatedRealm()
static OsAccountType fromID(int typeId)
Blackboard getBlackboard()
Optional< OsAccount > getWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost)
OsAccountUpdateStatus getUpdateStatusCode()
OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime)
Content getContentById(long id)
OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm)
NotUserSIDException(String msg)
List< OsAccount > getOsAccounts(Host host)
OsAccountRealmManager getOsAccountRealmManager()
void releaseSingleUserCaseReadLock()
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)
List< OsAccount > getOsAccountsByDataSourceObjId(long dataSourceId)
List< OsAccountInstance > getOsAccountInstances(List< Long > instanceIDs)
void acquireSingleUserCaseWriteLock()
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
void releaseSingleUserCaseWriteLock()
List< OsAccountInstance > getOsAccountInstances(OsAccount account)
UPDATED
no change was made to account.
Optional< String > getLoginName()
Optional< Host > getScopeHost()
void acquireSingleUserCaseReadLock()
UPDATED
no change was made to account.
void addExtendedOsAccountAttributes(OsAccount account, List< OsAccountAttribute > accountAttributes)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
OsRealmUpdateStatus getUpdateStatus()
Optional< OsAccount > getUpdatedAccount()
OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost)
OsAccount newWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
List< String > getRealmNames()