19 package org.sleuthkit.datamodel;
21 import com.google.common.annotations.Beta;
22 import com.google.common.base.Strings;
23 import org.apache.commons.lang3.StringUtils;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.sql.Statement;
28 import java.sql.Types;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.Optional;
33 import java.util.UUID;
34 import java.util.logging.Logger;
47 private static final String LOCAL_REALM_NAME =
"local";
81 if (realmScope == null) {
82 throw new TskCoreException(
"RealmScope cannot be null. Use UNKNOWN if scope is not known.");
84 if (referringHost == null) {
85 throw new TskCoreException(
"A referring host is required to create a realm.");
87 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
88 && StringUtils.isBlank(realmName)) {
89 throw new TskCoreException(
"Either an address or a name is required to create a realm.");
101 scopeHost = referringHost;
109 boolean isHostRealmKnown = isHostRealmKnown(referringHost);
110 if (isHostRealmKnown) {
114 scopeHost = referringHost;
122 String realmAddr = null;
123 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
124 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
126 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
130 realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
133 if (WindowsAccountUtils.isWindowsWellKnownSid(accountSid)) {
136 scopeHost = referringHost;
140 String wellKnownRealmName = WindowsAccountUtils.getWindowsWellKnownSidRealmName(accountSid);
141 if (!StringUtils.isEmpty(wellKnownRealmName)) {
142 resolvedRealmName = wellKnownRealmName;
147 String signature = makeRealmSignature(realmAddr, resolvedRealmName, scopeHost);
150 return newRealm(resolvedRealmName, realmAddr, signature, scopeHost, scopeConfidence);
166 if (referringHost == null) {
167 throw new TskCoreException(
"A referring host is required to create a realm.");
170 String realmName = LOCAL_REALM_NAME;
172 String signature = makeRealmSignature(
"", realmName, referringHost);
175 return newRealm(realmName,
"", signature, referringHost, scopeConfidence);
190 if (referringHost == null) {
194 try (CaseDbConnection connection = this.db.getConnection()) {
195 return getRealmByName(LOCAL_REALM_NAME, referringHost, connection);
218 if (referringHost == null) {
219 throw new TskCoreException(
"A referring host is required get a realm.");
223 if ((Strings.isNullOrEmpty(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) )
224 && Strings.isNullOrEmpty(realmName)) {
225 throw new TskCoreException(
"Realm address or name is required get a realm.");
228 try (CaseDbConnection connection = this.db.getConnection()) {
229 return getWindowsRealm(accountSid, realmName, referringHost, connection);
250 if (referringHost == null) {
251 throw new TskCoreException(
"A referring host is required get a realm.");
255 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
256 && StringUtils.isBlank(realmName)) {
257 throw new TskCoreException(
"Realm address or name is required get a realm.");
261 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
263 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
264 throw new OsAccountManager.NotUserSIDException(String.format(
"SID = %s is not a user SID.", accountSid ));
267 String realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
268 Optional<OsAccountRealm> realm = getRealmByAddr(realmAddr, referringHost, connection);
269 if (realm.isPresent()) {
275 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
278 Optional<OsAccountRealm> realm = getRealmByName(resolvedRealmName, referringHost, connection);
279 if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
282 if (realm.get().getRealmAddr().isPresent()) {
283 return Optional.empty();
308 OsRealmUpdateResult getAndUpdateWindowsRealm(String accountSid, String realmName, Host referringHost, CaseDbConnection connection)
throws TskCoreException, OsAccountManager.NotUserSIDException {
311 Optional<OsAccountRealm> realmOptional =
getWindowsRealm(accountSid, realmName, referringHost, connection);
314 if (realmOptional.isPresent()) {
315 String realmAddr = (StringUtils.isNotBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null;
316 OsRealmUpdateResult realmUpdateResult =
updateRealm(realmOptional.get(), realmAddr, realmName, connection);
318 return realmUpdateResult;
321 return new OsRealmUpdateResult(OsRealmUpdateStatus.NO_CHANGE, null);
349 try (CaseDbConnection connection = db.getConnection()) {
350 return updateRealm(realm, realmAddr, realmName, connection);
376 if ( (StringUtils.isBlank(realmAddr) || realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
377 && StringUtils.isBlank(realmName)) {
378 throw new TskCoreException(
"Realm address or name is required to update realm.");
381 OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE;
382 OsAccountRealm updatedRealm = null;
386 String currRealmAddr = realm.getRealmAddr().orElse(null);
389 if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr) && !realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
390 updateRealmColumn(realm.getRealmId(),
"realm_addr", realmAddr, connection);
391 currRealmAddr = realmAddr;
392 updateStatusCode = OsRealmUpdateStatus.UPDATED;
395 List<String> realmNames = realm.getRealmNames();
396 String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0);
401 if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) {
402 updateRealmColumn(realm.getRealmId(),
"realm_name", realmName, connection);
403 updateStatusCode = OsRealmUpdateStatus.UPDATED;
407 if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) {
408 return new OsRealmUpdateResult(updateStatusCode, realm);
413 String newRealmAddr = currRealm.
getRealmAddr().orElse(null);
414 String newRealmName = (currRealm.getRealmNames().isEmpty() ==
false) ? currRealm.getRealmNames().get(0) : null;
417 String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null));
420 String updateSQL =
"UPDATE tsk_os_account_realms SET "
421 +
" realm_signature = "
422 +
" CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() +
" THEN ? ELSE realm_signature END "
424 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
425 preparedStatement.clearParameters();
427 preparedStatement.setString(1, newSignature);
428 preparedStatement.setLong(2, realm.getRealmId());
429 connection.executeUpdate(preparedStatement);
434 return new OsRealmUpdateResult(updateStatusCode, updatedRealm);
435 }
catch (SQLException ex) {
436 throw new TskCoreException(String.format(
"Error updating realm with id = %d, name = %s, addr = %s", realm.getRealmId(), realmName != null ? realmName :
"Null", realm.getRealmAddr().orElse(
"Null")), ex);
455 private <T>
void updateRealmColumn(
long realmId, String colName, T colValue, CaseDbConnection connection)
throws SQLException, TskCoreException {
457 String updateSQL =
"UPDATE tsk_os_account_realms "
458 +
" SET " + colName +
" = ? "
463 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
464 preparedStatement.clearParameters();
466 if (Objects.isNull(colValue)) {
467 preparedStatement.setNull(1, Types.NULL);
469 if (colValue instanceof String) {
470 preparedStatement.setString(1, (String) colValue);
471 }
else if (colValue instanceof Long) {
472 preparedStatement.setLong(1, (Long) colValue);
473 }
else if (colValue instanceof Integer) {
474 preparedStatement.setInt(1, (Integer) colValue);
476 throw new TskCoreException(String.format(
"Unhandled column data type received while updating the realm (id = %d) ", realmId));
480 preparedStatement.setLong(2, realmId);
482 connection.executeUpdate(preparedStatement);
488 private final static String REALM_QUERY_STRING =
"SELECT realms.id as realm_id, realms.realm_name as realm_name,"
489 +
" realms.realm_addr as realm_addr, realms.realm_signature as realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status,"
490 +
" hosts.id, hosts.name as host_name "
491 +
" FROM tsk_os_account_realms as realms"
492 +
" LEFT JOIN tsk_hosts as hosts"
493 +
" ON realms.scope_host_id = hosts.id";
505 try (CaseDbConnection connection = this.db.getConnection()) {
521 String queryString = REALM_QUERY_STRING
522 +
" WHERE realms.id = " + id;
525 try ( Statement s = connection.createStatement();
526 ResultSet rs = connection.executeQuery(s, queryString)) {
529 accountRealm = resultSetToAccountRealm(rs);
531 throw new TskCoreException(String.format(
"No realm found with id = %d",
id));
535 }
catch (SQLException ex) {
536 throw new TskCoreException(String.format(
"Error running the realms query = %s", queryString), ex);
554 Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConnection connection)
throws TskCoreException {
558 String whereHostClause = (host == null)
560 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL) ";
561 String queryString = REALM_QUERY_STRING
562 +
" WHERE LOWER(realms.realm_addr) = LOWER('"+ realmAddr +
"') "
563 +
" AND " + whereHostClause
564 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
565 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
567 return getRealmUsingQuery(queryString, host, connection);
582 Optional<OsAccountRealm> getAnotherRealmByAddr(OsAccountRealm realm, String realmAddr, Host host, CaseDbConnection connection)
throws TskCoreException {
586 String whereHostClause = realm.getScopeHost().isPresent()
587 ?
" ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() +
" ) "
588 :
" realms.scope_host_id IS NULL ";
589 String queryString = REALM_QUERY_STRING
590 +
" WHERE LOWER(realms.realm_addr) = LOWER('"+ realmAddr +
"') "
591 +
" AND " + whereHostClause
592 +
" AND realms.id <> " + realm.getRealmId()
593 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
594 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
596 return getRealmUsingQuery(queryString, host, connection);
609 Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConnection connection)
throws TskCoreException {
613 String whereHostClause = (host == null)
615 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL ) ";
616 String queryString = REALM_QUERY_STRING
617 +
" WHERE LOWER(realms.realm_name) = LOWER('" + realmName +
"')"
618 +
" AND " + whereHostClause
619 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
620 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
622 return getRealmUsingQuery(queryString, host, connection);
637 Optional<OsAccountRealm> getAnotherRealmByName(OsAccountRealm realm, String realmName, Host host, CaseDbConnection connection)
throws TskCoreException {
641 String whereHostClause = realm.getScopeHost().isPresent()
642 ?
" ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() +
" ) "
643 :
" realms.scope_host_id IS NULL ";
644 String queryString = REALM_QUERY_STRING
645 +
" WHERE LOWER(realms.realm_name) = LOWER('" + realmName +
"')"
646 +
" AND " + whereHostClause
647 +
" AND realms.id <> " + realm.getRealmId()
648 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
649 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
651 return getRealmUsingQuery(queryString, host, connection);
666 private Optional<OsAccountRealm> getRealmUsingQuery(String queryString, Host host, CaseDbConnection connection)
throws TskCoreException {
669 try (Statement s = connection.createStatement();
670 ResultSet rs = connection.executeQuery(s, queryString)) {
672 OsAccountRealm accountRealm = null;
674 Host realmHost = null;
675 long hostId = rs.getLong(
"scope_host_id");
680 realmHost =
new Host(hostId, rs.getString(
"host_name"));
684 accountRealm =
new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
685 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
686 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
687 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
690 return Optional.ofNullable(accountRealm);
691 }
catch (SQLException ex) {
692 throw new TskCoreException(String.format(
"Error getting realm using query = %s", queryString), ex);
713 private boolean isHostRealmKnown(Host host)
throws TskCoreException {
716 String queryString = REALM_QUERY_STRING
717 +
" WHERE realms.scope_host_id = " + host.getHostId()
718 +
" AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId()
719 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId();
722 try (CaseDbConnection connection = this.db.getConnection();
723 Statement s = connection.createStatement();
724 ResultSet rs = connection.executeQuery(s, queryString)) {
728 }
catch (SQLException ex) {
729 throw new TskCoreException(String.format(
"Error getting account realm for with host = %s", host.getName()), ex);
744 private OsAccountRealm resultSetToAccountRealm(ResultSet rs)
throws SQLException {
746 long hostId = rs.getLong(
"scope_host_id");
747 Host realmHost = null;
749 realmHost =
new Host(hostId, rs.getString(
"host_name"));
752 return new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
753 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
754 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
755 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
815 private OsAccountRealm newRealm(String realmName, String realmAddr, String signature, Host host, OsAccountRealm.ScopeConfidence scopeConfidence) throws TskCoreException {
818 try (CaseDbConnection connection = this.db.getConnection()) {
819 String realmInsertSQL =
"INSERT INTO tsk_os_account_realms(realm_name, realm_addr, realm_signature, scope_host_id, scope_confidence)"
820 +
" VALUES (?, ?, ?, ?, ?)";
822 PreparedStatement preparedStatement = connection.getPreparedStatement(realmInsertSQL, Statement.RETURN_GENERATED_KEYS);
823 preparedStatement.clearParameters();
825 preparedStatement.setString(1, realmName);
826 preparedStatement.setString(2, realmAddr);
827 preparedStatement.setString(3, signature);
829 preparedStatement.setLong(4, host.getHostId());
831 preparedStatement.setNull(4, java.sql.Types.BIGINT);
833 preparedStatement.setInt(5, scopeConfidence.getId());
835 connection.executeUpdate(preparedStatement);
838 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
839 long rowId = resultSet.getLong(1);
840 return new OsAccountRealm(rowId, realmName, realmAddr, signature, host, scopeConfidence, OsAccountRealm.RealmDbStatus.ACTIVE);
843 }
catch (SQLException ex) {
845 try (CaseDbConnection connection = this.db.getConnection()) {
846 if (!Strings.isNullOrEmpty(realmAddr)) {
847 Optional<OsAccountRealm> accountRealm = this.getRealmByAddr(realmAddr, host, connection);
848 if (accountRealm.isPresent()) {
849 return accountRealm.get();
851 }
else if (!Strings.isNullOrEmpty(realmName)) {
852 Optional<OsAccountRealm> accountRealm = this.getRealmByName(realmName, host, connection);
853 if (accountRealm.isPresent()) {
854 return accountRealm.get();
859 throw new TskCoreException(String.format(
"Error creating realm with address = %s and name = %s, with host = %s",
860 realmAddr != null ? realmAddr :
"", realmName != null ? realmName :
"", host != null ? host.getName() :
""), ex);
884 static String makeRealmSignature(String realmAddr, String realmName, Host scopeHost)
throws TskCoreException {
887 if (Strings.isNullOrEmpty(realmAddr) && Strings.isNullOrEmpty(realmName)) {
888 throw new TskCoreException(
"Realm address and name can't both be null.");
891 String signature = String.format(
"%s_%s", !Strings.isNullOrEmpty(realmAddr) ? realmAddr : realmName,
892 scopeHost != null ? scopeHost.getHostId() :
"DOMAIN");
901 private String makeMergedRealmSignature() {
902 return "MERGED " + UUID.randomUUID().toString();
914 void moveOrMergeRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
916 Optional<OsAccountRealm> optDestRealmAddr = Optional.empty();
917 if (sourceRealm.getRealmAddr().isPresent()) {
918 optDestRealmAddr = db.
getOsAccountRealmManager().getRealmByAddr(sourceRealm.getRealmAddr().get(), destHost, trans.getConnection());
922 Optional<OsAccountRealm> optDestRealmName = Optional.empty();
923 if (!sourceRealm.getRealmNames().isEmpty()) {
924 optDestRealmName = db.
getOsAccountRealmManager().getRealmByName(sourceRealm.getRealmNames().get(0), destHost, trans.getConnection());
939 OsAccountRealm destRealm = null;
940 if (optDestRealmAddr.isPresent() && optDestRealmName.isPresent()) {
941 if (optDestRealmAddr.get().getRealmId() == optDestRealmName.get().getRealmId()) {
943 destRealm = optDestRealmAddr.get();
945 if (optDestRealmName.get().getRealmAddr().isPresent()) {
947 destRealm = optDestRealmAddr.get();
951 mergeRealms(optDestRealmName.get(), optDestRealmAddr.get(), trans);
952 destRealm =
getRealmByRealmId(optDestRealmAddr.get().getRealmId(), trans.getConnection());
955 }
else if (optDestRealmAddr.isPresent()) {
957 destRealm = optDestRealmAddr.get();
958 }
else if (optDestRealmName.isPresent()) {
962 if (! (optDestRealmName.get().getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent())) {
963 destRealm = optDestRealmName.get();
968 if (destRealm == null) {
969 moveRealm(sourceRealm, destHost, trans);
971 mergeRealms(sourceRealm, destRealm, trans);
986 private void moveRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
987 try(Statement s = trans.getConnection().createStatement()) {
988 String query =
"UPDATE tsk_os_account_realms SET scope_host_id = " + destHost.getHostId() +
" WHERE id = " + sourceRealm.getRealmId();
989 s.executeUpdate(query);
990 }
catch (SQLException ex) {
991 throw new TskCoreException(
"Error moving realm with id: " + sourceRealm.getRealmId() +
" to host with id: " + destHost.getHostId(), ex);
1005 void mergeRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans)
throws TskCoreException {
1011 CaseDbConnection connection = trans.getConnection();
1012 try (Statement statement = connection.createStatement()) {
1013 String updateStr =
"UPDATE tsk_os_account_realms SET db_status = " + OsAccountRealm.RealmDbStatus.MERGED.getId()
1014 +
", merged_into = " + destRealm.getRealmId()
1015 +
", realm_signature = '" + makeMergedRealmSignature() +
"' "
1016 +
" WHERE id = " + sourceRealm.getRealmId();
1017 connection.executeUpdate(statement, updateStr);
1018 }
catch (SQLException ex) {
1019 throw new TskCoreException (
"Error updating status of realm with id: " + sourceRealm.getRealmId(), ex);
1023 if (!destRealm.getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent()) {
1024 updateRealm(destRealm, sourceRealm.getRealmAddr().get(), null, trans.getConnection());
1025 }
else if (destRealm.getRealmNames().isEmpty() && !sourceRealm.getRealmNames().isEmpty()) {
1026 updateRealm(destRealm, null, sourceRealm.getRealmNames().get(0), trans.getConnection());
1040 List<OsAccountRealm> getRealmsByHost(Host host, CaseDbConnection connection)
throws TskCoreException {
1041 List<OsAccountRealm> results =
new ArrayList<>();
1042 String queryString = REALM_QUERY_STRING
1043 +
" WHERE realms.scope_host_id = " + host.getHostId();
1046 try ( Statement s = connection.createStatement();
1047 ResultSet rs = connection.executeQuery(s, queryString)) {
1049 results.add(resultSetToAccountRealm(rs));
1052 }
catch (SQLException ex) {
1053 throw new TskCoreException(String.format(
"Error gettings realms for host with id = " + host.getHostId()), ex);
1080 this.updateStatus = updateStatus;
1081 this.updatedRealm = updatedRealm;
1085 return updateStatus;
1089 return Optional.ofNullable(updatedRealm);
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
Optional< OsAccountRealm > getUpdatedRealm()
OsAccountRealm newLocalLinuxRealm(Host referringHost)
Optional< OsAccountRealm > getLocalLinuxRealm(Host referringHost)
OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName)
OsAccountRealmManager getOsAccountRealmManager()
void releaseSingleUserCaseReadLock()
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)
void acquireSingleUserCaseWriteLock()
void releaseSingleUserCaseWriteLock()
Optional< String > getRealmAddr()
OsAccountManager getOsAccountManager()
void acquireSingleUserCaseReadLock()
UPDATED
no change was made to account.
OsAccountRealm getRealmByRealmId(long id)
OsRealmUpdateStatus getUpdateStatus()