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.ArrayList;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.Optional;
32 import java.util.UUID;
33 import java.util.logging.Logger;
79 if (realmScope == null) {
80 throw new TskCoreException(
"RealmScope cannot be null. Use UNKNOWN if scope is not known.");
82 if (referringHost == null) {
83 throw new TskCoreException(
"A referring host is required to create a realm.");
85 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
86 && StringUtils.isBlank(realmName)) {
87 throw new TskCoreException(
"Either an address or a name is required to create a realm.");
99 scopeHost = referringHost;
107 boolean isHostRealmKnown = isHostRealmKnown(referringHost);
108 if (isHostRealmKnown) {
112 scopeHost = referringHost;
120 String realmAddr = null;
121 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
122 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
124 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
128 realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
131 if (WindowsAccountUtils.isWindowsWellKnownSid(accountSid)) {
134 scopeHost = referringHost;
138 String wellKnownRealmName = WindowsAccountUtils.getWindowsWellKnownSidRealmName(accountSid);
139 if (!StringUtils.isEmpty(wellKnownRealmName)) {
140 resolvedRealmName = wellKnownRealmName;
145 String signature = makeRealmSignature(realmAddr, resolvedRealmName, scopeHost);
148 return newRealm(resolvedRealmName, realmAddr, signature, scopeHost, scopeConfidence);
170 if (referringHost == null) {
171 throw new TskCoreException(
"A referring host is required get a realm.");
175 if ((Strings.isNullOrEmpty(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) )
176 && Strings.isNullOrEmpty(realmName)) {
177 throw new TskCoreException(
"Realm address or name is required get a realm.");
180 try (CaseDbConnection connection = this.db.getConnection()) {
181 return getWindowsRealm(accountSid, realmName, referringHost, connection);
202 if (referringHost == null) {
203 throw new TskCoreException(
"A referring host is required get a realm.");
207 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
208 && StringUtils.isBlank(realmName)) {
209 throw new TskCoreException(
"Realm address or name is required get a realm.");
213 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
215 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
216 throw new OsAccountManager.NotUserSIDException(String.format(
"SID = %s is not a user SID.", accountSid ));
219 String realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
220 Optional<OsAccountRealm> realm = getRealmByAddr(realmAddr, referringHost, connection);
221 if (realm.isPresent()) {
227 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
230 Optional<OsAccountRealm> realm = getRealmByName(resolvedRealmName, referringHost, connection);
231 if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
234 if (realm.get().getRealmAddr().isPresent()) {
235 return Optional.empty();
260 OsRealmUpdateResult getAndUpdateWindowsRealm(String accountSid, String realmName, Host referringHost, CaseDbConnection connection)
throws TskCoreException, OsAccountManager.NotUserSIDException {
263 Optional<OsAccountRealm> realmOptional =
getWindowsRealm(accountSid, realmName, referringHost, connection);
266 if (realmOptional.isPresent()) {
267 String realmAddr = (StringUtils.isNotBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null;
268 OsRealmUpdateResult realmUpdateResult =
updateRealm(realmOptional.get(), realmAddr, realmName, connection);
270 return realmUpdateResult;
273 return new OsRealmUpdateResult(OsRealmUpdateStatus.NO_CHANGE, null);
301 try (CaseDbConnection connection = db.getConnection()) {
302 return updateRealm(realm, realmAddr, realmName, connection);
328 if ( (StringUtils.isBlank(realmAddr) || realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
329 && StringUtils.isBlank(realmName)) {
330 throw new TskCoreException(
"Realm address or name is required to update realm.");
333 OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE;
334 OsAccountRealm updatedRealm = null;
338 String currRealmAddr = realm.getRealmAddr().orElse(null);
341 if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr) && !realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
342 updateRealmColumn(realm.getRealmId(),
"realm_addr", realmAddr, connection);
343 currRealmAddr = realmAddr;
344 updateStatusCode = OsRealmUpdateStatus.UPDATED;
347 List<String> realmNames = realm.getRealmNames();
348 String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0);
353 if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) {
354 updateRealmColumn(realm.getRealmId(),
"realm_name", realmName, connection);
355 updateStatusCode = OsRealmUpdateStatus.UPDATED;
359 if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) {
360 return new OsRealmUpdateResult(updateStatusCode, realm);
365 String newRealmAddr = currRealm.
getRealmAddr().orElse(null);
366 String newRealmName = (currRealm.getRealmNames().isEmpty() ==
false) ? currRealm.getRealmNames().get(0) : null;
369 String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null));
372 String updateSQL =
"UPDATE tsk_os_account_realms SET "
373 +
" realm_signature = "
374 +
" CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() +
" THEN ? ELSE realm_signature END "
376 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
377 preparedStatement.clearParameters();
379 preparedStatement.setString(1, newSignature);
380 preparedStatement.setLong(2, realm.getRealmId());
381 connection.executeUpdate(preparedStatement);
386 return new OsRealmUpdateResult(updateStatusCode, updatedRealm);
387 }
catch (SQLException ex) {
388 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);
407 private <T>
void updateRealmColumn(
long realmId, String colName, T colValue, CaseDbConnection connection)
throws SQLException, TskCoreException {
409 String updateSQL =
"UPDATE tsk_os_account_realms "
410 +
" SET " + colName +
" = ? "
415 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
416 preparedStatement.clearParameters();
418 if (Objects.isNull(colValue)) {
419 preparedStatement.setNull(1, Types.NULL);
421 if (colValue instanceof String) {
422 preparedStatement.setString(1, (String) colValue);
423 }
else if (colValue instanceof Long) {
424 preparedStatement.setLong(1, (Long) colValue);
425 }
else if (colValue instanceof Integer) {
426 preparedStatement.setInt(1, (Integer) colValue);
428 throw new TskCoreException(String.format(
"Unhandled column data type received while updating the realm (id = %d) ", realmId));
432 preparedStatement.setLong(2, realmId);
434 connection.executeUpdate(preparedStatement);
440 private final static String REALM_QUERY_STRING =
"SELECT realms.id as realm_id, realms.realm_name as realm_name,"
441 +
" realms.realm_addr as realm_addr, realms.realm_signature as realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status,"
442 +
" hosts.id, hosts.name as host_name "
443 +
" FROM tsk_os_account_realms as realms"
444 +
" LEFT JOIN tsk_hosts as hosts"
445 +
" ON realms.scope_host_id = hosts.id";
457 try (CaseDbConnection connection = this.db.getConnection()) {
473 String queryString = REALM_QUERY_STRING
474 +
" WHERE realms.id = " + id;
477 try ( Statement s = connection.createStatement();
478 ResultSet rs = connection.executeQuery(s, queryString)) {
481 accountRealm = resultSetToAccountRealm(rs);
483 throw new TskCoreException(String.format(
"No realm found with id = %d",
id));
487 }
catch (SQLException ex) {
488 throw new TskCoreException(String.format(
"Error running the realms query = %s", queryString), ex);
506 Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConnection connection)
throws TskCoreException {
510 String whereHostClause = (host == null)
512 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL) ";
513 String queryString = REALM_QUERY_STRING
514 +
" WHERE LOWER(realms.realm_addr) = LOWER('"+ realmAddr +
"') "
515 +
" AND " + whereHostClause
516 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
517 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
519 return getRealmUsingQuery(queryString, host, connection);
534 Optional<OsAccountRealm> getAnotherRealmByAddr(OsAccountRealm realm, String realmAddr, Host host, CaseDbConnection connection)
throws TskCoreException {
538 String whereHostClause = realm.getScopeHost().isPresent()
539 ?
" ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() +
" ) "
540 :
" realms.scope_host_id IS NULL ";
541 String queryString = REALM_QUERY_STRING
542 +
" WHERE LOWER(realms.realm_addr) = LOWER('"+ realmAddr +
"') "
543 +
" AND " + whereHostClause
544 +
" AND realms.id <> " + realm.getRealmId()
545 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
546 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
548 return getRealmUsingQuery(queryString, host, connection);
561 Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConnection connection)
throws TskCoreException {
565 String whereHostClause = (host == null)
567 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL ) ";
568 String queryString = REALM_QUERY_STRING
569 +
" WHERE LOWER(realms.realm_name) = LOWER('" + realmName +
"')"
570 +
" AND " + whereHostClause
571 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
572 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
574 return getRealmUsingQuery(queryString, host, connection);
589 Optional<OsAccountRealm> getAnotherRealmByName(OsAccountRealm realm, String realmName, Host host, CaseDbConnection connection)
throws TskCoreException {
593 String whereHostClause = realm.getScopeHost().isPresent()
594 ?
" ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() +
" ) "
595 :
" realms.scope_host_id IS NULL ";
596 String queryString = REALM_QUERY_STRING
597 +
" WHERE LOWER(realms.realm_name) = LOWER('" + realmName +
"')"
598 +
" AND " + whereHostClause
599 +
" AND realms.id <> " + realm.getRealmId()
600 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
601 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
603 return getRealmUsingQuery(queryString, host, connection);
618 private Optional<OsAccountRealm> getRealmUsingQuery(String queryString, Host host, CaseDbConnection connection)
throws TskCoreException {
621 try (Statement s = connection.createStatement();
622 ResultSet rs = connection.executeQuery(s, queryString)) {
624 OsAccountRealm accountRealm = null;
626 Host realmHost = null;
627 long hostId = rs.getLong(
"scope_host_id");
632 realmHost =
new Host(hostId, rs.getString(
"host_name"));
636 accountRealm =
new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
637 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
638 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
639 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
642 return Optional.ofNullable(accountRealm);
643 }
catch (SQLException ex) {
644 throw new TskCoreException(String.format(
"Error getting realm using query = %s", queryString), ex);
665 private boolean isHostRealmKnown(Host host)
throws TskCoreException {
668 String queryString = REALM_QUERY_STRING
669 +
" WHERE realms.scope_host_id = " + host.getHostId()
670 +
" AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId()
671 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId();
674 try (CaseDbConnection connection = this.db.getConnection();
675 Statement s = connection.createStatement();
676 ResultSet rs = connection.executeQuery(s, queryString)) {
680 }
catch (SQLException ex) {
681 throw new TskCoreException(String.format(
"Error getting account realm for with host = %s", host.getName()), ex);
696 private OsAccountRealm resultSetToAccountRealm(ResultSet rs)
throws SQLException {
698 long hostId = rs.getLong(
"scope_host_id");
699 Host realmHost = null;
701 realmHost =
new Host(hostId, rs.getString(
"host_name"));
704 return new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
705 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
706 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
707 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
767 private OsAccountRealm newRealm(String realmName, String realmAddr, String signature, Host host, OsAccountRealm.ScopeConfidence scopeConfidence) throws TskCoreException {
770 try (CaseDbConnection connection = this.db.getConnection()) {
771 String realmInsertSQL =
"INSERT INTO tsk_os_account_realms(realm_name, realm_addr, realm_signature, scope_host_id, scope_confidence)"
772 +
" VALUES (?, ?, ?, ?, ?)";
774 PreparedStatement preparedStatement = connection.getPreparedStatement(realmInsertSQL, Statement.RETURN_GENERATED_KEYS);
775 preparedStatement.clearParameters();
777 preparedStatement.setString(1, realmName);
778 preparedStatement.setString(2, realmAddr);
779 preparedStatement.setString(3, signature);
781 preparedStatement.setLong(4, host.getHostId());
783 preparedStatement.setNull(4, java.sql.Types.BIGINT);
785 preparedStatement.setInt(5, scopeConfidence.getId());
787 connection.executeUpdate(preparedStatement);
790 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
791 long rowId = resultSet.getLong(1);
792 return new OsAccountRealm(rowId, realmName, realmAddr, signature, host, scopeConfidence, OsAccountRealm.RealmDbStatus.ACTIVE);
795 }
catch (SQLException ex) {
797 try (CaseDbConnection connection = this.db.getConnection()) {
798 if (!Strings.isNullOrEmpty(realmAddr)) {
799 Optional<OsAccountRealm> accountRealm = this.getRealmByAddr(realmAddr, host, connection);
800 if (accountRealm.isPresent()) {
801 return accountRealm.get();
803 }
else if (!Strings.isNullOrEmpty(realmName)) {
804 Optional<OsAccountRealm> accountRealm = this.getRealmByName(realmName, host, connection);
805 if (accountRealm.isPresent()) {
806 return accountRealm.get();
811 throw new TskCoreException(String.format(
"Error creating realm with address = %s and name = %s, with host = %s",
812 realmAddr != null ? realmAddr :
"", realmName != null ? realmName :
"", host != null ? host.getName() :
""), ex);
836 static String makeRealmSignature(String realmAddr, String realmName, Host scopeHost)
throws TskCoreException {
839 if (Strings.isNullOrEmpty(realmAddr) && Strings.isNullOrEmpty(realmName)) {
840 throw new TskCoreException(
"Realm address and name can't both be null.");
843 String signature = String.format(
"%s_%s", !Strings.isNullOrEmpty(realmAddr) ? realmAddr : realmName,
844 scopeHost != null ? scopeHost.getHostId() :
"DOMAIN");
853 private String makeMergedRealmSignature() {
854 return "MERGED " + UUID.randomUUID().toString();
866 void moveOrMergeRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
868 Optional<OsAccountRealm> optDestRealmAddr = Optional.empty();
869 if (sourceRealm.getRealmAddr().isPresent()) {
870 optDestRealmAddr = db.
getOsAccountRealmManager().getRealmByAddr(sourceRealm.getRealmAddr().get(), destHost, trans.getConnection());
874 Optional<OsAccountRealm> optDestRealmName = Optional.empty();
875 if (!sourceRealm.getRealmNames().isEmpty()) {
876 optDestRealmName = db.
getOsAccountRealmManager().getRealmByName(sourceRealm.getRealmNames().get(0), destHost, trans.getConnection());
891 OsAccountRealm destRealm = null;
892 if (optDestRealmAddr.isPresent() && optDestRealmName.isPresent()) {
893 if (optDestRealmAddr.get().getRealmId() == optDestRealmName.get().getRealmId()) {
895 destRealm = optDestRealmAddr.get();
897 if (optDestRealmName.get().getRealmAddr().isPresent()) {
899 destRealm = optDestRealmAddr.get();
903 mergeRealms(optDestRealmName.get(), optDestRealmAddr.get(), trans);
904 destRealm =
getRealmByRealmId(optDestRealmAddr.get().getRealmId(), trans.getConnection());
907 }
else if (optDestRealmAddr.isPresent()) {
909 destRealm = optDestRealmAddr.get();
910 }
else if (optDestRealmName.isPresent()) {
914 if (! (optDestRealmName.get().getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent())) {
915 destRealm = optDestRealmName.get();
920 if (destRealm == null) {
921 moveRealm(sourceRealm, destHost, trans);
923 mergeRealms(sourceRealm, destRealm, trans);
938 private void moveRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
939 try(Statement s = trans.getConnection().createStatement()) {
940 String query =
"UPDATE tsk_os_account_realms SET scope_host_id = " + destHost.getHostId() +
" WHERE id = " + sourceRealm.getRealmId();
941 s.executeUpdate(query);
942 }
catch (SQLException ex) {
943 throw new TskCoreException(
"Error moving realm with id: " + sourceRealm.getRealmId() +
" to host with id: " + destHost.getHostId(), ex);
957 void mergeRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans)
throws TskCoreException {
963 CaseDbConnection connection = trans.getConnection();
964 try (Statement statement = connection.createStatement()) {
965 String updateStr =
"UPDATE tsk_os_account_realms SET db_status = " + OsAccountRealm.RealmDbStatus.MERGED.getId()
966 +
", merged_into = " + destRealm.getRealmId()
967 +
", realm_signature = '" + makeMergedRealmSignature() +
"' "
968 +
" WHERE id = " + sourceRealm.getRealmId();
969 connection.executeUpdate(statement, updateStr);
970 }
catch (SQLException ex) {
971 throw new TskCoreException (
"Error updating status of realm with id: " + sourceRealm.getRealmId(), ex);
975 if (!destRealm.getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent()) {
976 updateRealm(destRealm, sourceRealm.getRealmAddr().get(), null, trans.getConnection());
977 }
else if (destRealm.getRealmNames().isEmpty() && !sourceRealm.getRealmNames().isEmpty()) {
978 updateRealm(destRealm, null, sourceRealm.getRealmNames().get(0), trans.getConnection());
992 List<OsAccountRealm> getRealmsByHost(Host host, CaseDbConnection connection)
throws TskCoreException {
993 List<OsAccountRealm> results =
new ArrayList<>();
994 String queryString = REALM_QUERY_STRING
995 +
" WHERE realms.scope_host_id = " + host.getHostId();
998 try ( Statement s = connection.createStatement();
999 ResultSet rs = connection.executeQuery(s, queryString)) {
1001 results.add(resultSetToAccountRealm(rs));
1004 }
catch (SQLException ex) {
1005 throw new TskCoreException(String.format(
"Error gettings realms for host with id = " + host.getHostId()), ex);
1032 this.updateStatus = updateStatus;
1033 this.updatedRealm = updatedRealm;
1037 return updateStatus;
1041 return Optional.ofNullable(updatedRealm);
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
Optional< OsAccountRealm > getUpdatedRealm()
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()