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) && StringUtils.isBlank(realmName)) {
86 throw new TskCoreException(
"Either an address or a name is required to create a realm.");
98 scopeHost = referringHost;
105 boolean isHostRealmKnown = isHostRealmKnown(referringHost);
106 if (isHostRealmKnown) {
110 scopeHost = referringHost;
118 String realmAddr = null;
119 if (!Strings.isNullOrEmpty(accountSid)) {
121 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
125 realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
128 if (realmAddr.equals(WindowsAccountUtils.SPECIAL_WINDOWS_REALM_ADDR)) {
129 scopeHost = referringHost;
134 String signature = makeRealmSignature(realmAddr, realmName, scopeHost);
137 return newRealm(realmName, realmAddr, signature, scopeHost, scopeConfidence);
159 if (referringHost == null) {
160 throw new TskCoreException(
"A referring host is required get a realm.");
164 if (Strings.isNullOrEmpty(accountSid) && Strings.isNullOrEmpty(realmName)) {
165 throw new TskCoreException(
"Realm address or name is required get a realm.");
168 try (CaseDbConnection connection = this.db.getConnection()) {
169 return getWindowsRealm(accountSid, realmName, referringHost, connection);
190 if (referringHost == null) {
191 throw new TskCoreException(
"A referring host is required get a realm.");
195 if (StringUtils.isBlank(accountSid) && StringUtils.isBlank(realmName)) {
196 throw new TskCoreException(
"Realm address or name is required get a realm.");
200 if (!Strings.isNullOrEmpty(accountSid)) {
202 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
203 throw new OsAccountManager.NotUserSIDException(String.format(
"SID = %s is not a user SID.", accountSid ));
206 String realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
207 Optional<OsAccountRealm> realm = getRealmByAddr(realmAddr, referringHost, connection);
208 if (realm.isPresent()) {
214 Optional<OsAccountRealm> realm = getRealmByName(realmName, referringHost, connection);
215 if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid)) {
218 if (realm.get().getRealmAddr().isPresent()) {
219 return Optional.empty();
245 Optional<OsAccountRealm> getAndUpdateWindowsRealm(String accountSid, String realmName, Host referringHost, CaseDbConnection connection)
throws TskCoreException, OsAccountManager.NotUserSIDException {
248 Optional<OsAccountRealm> realmOptional =
getWindowsRealm(accountSid, realmName, referringHost, connection );
251 if (realmOptional.isPresent()) {
252 String realmAddr = StringUtils.isNotBlank(accountSid) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null;
253 OsRealmUpdateResult realmUpdateResult =
updateRealm(realmOptional.get(), realmAddr, realmName, connection);
256 if (realmUpdateResult.getUpdateStatus() == OsRealmUpdateStatus.UPDATED) {
261 return realmOptional;
287 try (CaseDbConnection connection = db.getConnection()) {
288 return updateRealm(realm, realmAddr, realmName, connection);
308 private OsRealmUpdateResult
updateRealm(
OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection)
throws TskCoreException {
311 if (StringUtils.isBlank(realmAddr) && StringUtils.isBlank(realmName)) {
312 throw new TskCoreException(
"Realm address or name is required to update realm.");
315 OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE;
316 OsAccountRealm updatedRealm = null;
320 List<String> realmNames = realm.getRealmNames();
321 String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0);
322 String currRealmAddr = realm.getRealmAddr().orElse(null);
325 if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) {
326 updateRealmColumn(realm.getRealmId(),
"realm_addr", realmAddr, connection);
327 updateStatusCode = OsRealmUpdateStatus.UPDATED;
330 if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) {
331 updateRealmColumn(realm.getRealmId(),
"realm_name", realmName, connection);
332 updateStatusCode = OsRealmUpdateStatus.UPDATED;
336 if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) {
337 return new OsRealmUpdateResult(updateStatusCode, realm);
342 String newRealmAddr = currRealm.
getRealmAddr().orElse(null);
343 String newRealmName = (currRealm.getRealmNames().isEmpty() ==
false) ? currRealm.getRealmNames().get(0) : null;
346 String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null));
349 String updateSQL =
"UPDATE tsk_os_account_realms SET "
350 +
" realm_signature = "
351 +
" CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() +
" THEN ? ELSE realm_signature END "
353 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
354 preparedStatement.clearParameters();
356 preparedStatement.setString(1, newSignature);
357 preparedStatement.setLong(2, realm.getRealmId());
358 connection.executeUpdate(preparedStatement);
363 return new OsRealmUpdateResult(updateStatusCode, updatedRealm);
364 }
catch (SQLException ex) {
365 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);
384 private <T>
void updateRealmColumn(
long realmId, String colName, T colValue, CaseDbConnection connection)
throws SQLException, TskCoreException {
386 String updateSQL =
"UPDATE tsk_os_account_realms "
387 +
" SET " + colName +
" = ? "
392 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
393 preparedStatement.clearParameters();
395 if (Objects.isNull(colValue)) {
396 preparedStatement.setNull(1, Types.NULL);
398 if (colValue instanceof String) {
399 preparedStatement.setString(1, (String) colValue);
400 }
else if (colValue instanceof Long) {
401 preparedStatement.setLong(1, (Long) colValue);
402 }
else if (colValue instanceof Integer) {
403 preparedStatement.setInt(1, (Integer) colValue);
405 throw new TskCoreException(String.format(
"Unhandled column data type received while updating the realm (id = %d) ", realmId));
409 preparedStatement.setLong(2, realmId);
411 connection.executeUpdate(preparedStatement);
417 private final static String REALM_QUERY_STRING =
"SELECT realms.id as realm_id, realms.realm_name as realm_name,"
418 +
" realms.realm_addr as realm_addr, realms.realm_signature as realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status,"
419 +
" hosts.id, hosts.name as host_name "
420 +
" FROM tsk_os_account_realms as realms"
421 +
" LEFT JOIN tsk_hosts as hosts"
422 +
" ON realms.scope_host_id = hosts.id";
434 try (CaseDbConnection connection = this.db.getConnection()) {
450 String queryString = REALM_QUERY_STRING
451 +
" WHERE realms.id = " + id;
454 try ( Statement s = connection.createStatement();
455 ResultSet rs = connection.executeQuery(s, queryString)) {
458 accountRealm = resultSetToAccountRealm(rs);
460 throw new TskCoreException(String.format(
"No realm found with id = %d",
id));
464 }
catch (SQLException ex) {
465 throw new TskCoreException(String.format(
"Error running the realms query = %s", queryString), ex);
483 Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConnection connection)
throws TskCoreException {
487 String whereHostClause = (host == null)
489 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL) ";
490 String queryString = REALM_QUERY_STRING
491 +
" WHERE LOWER(realms.realm_addr) = LOWER('"+ realmAddr +
"') "
492 +
" AND " + whereHostClause
493 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
494 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
497 try ( Statement s = connection.createStatement();
498 ResultSet rs = connection.executeQuery(s, queryString)) {
500 OsAccountRealm accountRealm = null;
502 Host realmHost = null;
503 long hostId = rs.getLong(
"scope_host_id");
508 realmHost =
new Host(hostId, rs.getString(
"host_name"));
512 accountRealm =
new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
513 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
514 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
515 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
517 return Optional.ofNullable(accountRealm);
518 }
catch (SQLException ex) {
519 throw new TskCoreException(String.format(
"Error running the realms query = %s with realmaddr = %s and host name = %s",
520 queryString, realmAddr, (host != null ? host.getName() :
"Null")), ex);
536 Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConnection connection)
throws TskCoreException {
540 String whereHostClause = (host == null)
542 :
" ( realms.scope_host_id = " + host.getHostId() +
" OR realms.scope_host_id IS NULL ) ";
543 String queryString = REALM_QUERY_STRING
544 +
" WHERE LOWER(realms.realm_name) = LOWER('" + realmName +
"')"
545 +
" AND " + whereHostClause
546 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
547 +
" ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id";
550 try (Statement s = connection.createStatement();
551 ResultSet rs = connection.executeQuery(s, queryString)) {
553 OsAccountRealm accountRealm = null;
555 Host realmHost = null;
556 long hostId = rs.getLong(
"scope_host_id");
561 realmHost =
new Host(hostId, rs.getString(
"host_name"));
565 accountRealm =
new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
566 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
567 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
568 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
571 return Optional.ofNullable(accountRealm);
572 }
catch (SQLException ex) {
573 throw new TskCoreException(String.format(
"Error getting account realm for with name = %s", realmName), ex);
589 private boolean isHostRealmKnown(Host host)
throws TskCoreException {
592 String queryString = REALM_QUERY_STRING
593 +
" WHERE realms.scope_host_id = " + host.getHostId()
594 +
" AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId()
595 +
" AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
596 +
" AND LOWER(realms.realm_addr) <> LOWER('"+ WindowsAccountUtils.SPECIAL_WINDOWS_REALM_ADDR +
"') ";
599 try (CaseDbConnection connection = this.db.getConnection();
600 Statement s = connection.createStatement();
601 ResultSet rs = connection.executeQuery(s, queryString)) {
605 }
catch (SQLException ex) {
606 throw new TskCoreException(String.format(
"Error getting account realm for with host = %s", host.getName()), ex);
621 private OsAccountRealm resultSetToAccountRealm(ResultSet rs)
throws SQLException {
623 long hostId = rs.getLong(
"scope_host_id");
624 Host realmHost = null;
626 realmHost =
new Host(hostId, rs.getString(
"host_name"));
629 return new OsAccountRealm(rs.getLong(
"realm_id"), rs.getString(
"realm_name"),
630 rs.getString(
"realm_addr"), rs.getString(
"realm_signature"),
631 realmHost, ScopeConfidence.fromID(rs.getInt(
"scope_confidence")),
632 OsAccountRealm.RealmDbStatus.fromID(rs.getInt(
"db_status")));
692 private OsAccountRealm newRealm(String realmName, String realmAddr, String signature, Host host, OsAccountRealm.ScopeConfidence scopeConfidence) throws TskCoreException {
695 try (CaseDbConnection connection = this.db.getConnection()) {
696 String realmInsertSQL =
"INSERT INTO tsk_os_account_realms(realm_name, realm_addr, realm_signature, scope_host_id, scope_confidence)"
697 +
" VALUES (?, ?, ?, ?, ?)";
699 PreparedStatement preparedStatement = connection.getPreparedStatement(realmInsertSQL, Statement.RETURN_GENERATED_KEYS);
700 preparedStatement.clearParameters();
702 preparedStatement.setString(1, realmName);
703 preparedStatement.setString(2, realmAddr);
704 preparedStatement.setString(3, signature);
706 preparedStatement.setLong(4, host.getHostId());
708 preparedStatement.setNull(4, java.sql.Types.BIGINT);
710 preparedStatement.setInt(5, scopeConfidence.getId());
712 connection.executeUpdate(preparedStatement);
715 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
716 long rowId = resultSet.getLong(1);
717 return new OsAccountRealm(rowId, realmName, realmAddr, signature, host, scopeConfidence, OsAccountRealm.RealmDbStatus.ACTIVE);
720 }
catch (SQLException ex) {
722 try (CaseDbConnection connection = this.db.getConnection()) {
723 if (!Strings.isNullOrEmpty(realmAddr)) {
724 Optional<OsAccountRealm> accountRealm = this.getRealmByAddr(realmAddr, host, connection);
725 if (accountRealm.isPresent()) {
726 return accountRealm.get();
728 }
else if (!Strings.isNullOrEmpty(realmName)) {
729 Optional<OsAccountRealm> accountRealm = this.getRealmByName(realmName, host, connection);
730 if (accountRealm.isPresent()) {
731 return accountRealm.get();
736 throw new TskCoreException(String.format(
"Error creating realm with address = %s and name = %s, with host = %s",
737 realmAddr != null ? realmAddr :
"", realmName != null ? realmName :
"", host != null ? host.getName() :
""), ex);
761 static String makeRealmSignature(String realmAddr, String realmName, Host scopeHost)
throws TskCoreException {
764 if (Strings.isNullOrEmpty(realmAddr) && Strings.isNullOrEmpty(realmName)) {
765 throw new TskCoreException(
"Realm address and name can't both be null.");
768 String signature = String.format(
"%s_%s", !Strings.isNullOrEmpty(realmAddr) ? realmAddr : realmName,
769 scopeHost != null ? scopeHost.getHostId() :
"DOMAIN");
778 private String makeMergedRealmSignature() {
779 return "MERGED " + UUID.randomUUID().toString();
791 void moveOrMergeRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
793 Optional<OsAccountRealm> optDestRealmAddr = Optional.empty();
794 if (sourceRealm.getRealmAddr().isPresent()) {
795 optDestRealmAddr = db.
getOsAccountRealmManager().getRealmByAddr(sourceRealm.getRealmAddr().get(), destHost, trans.getConnection());
799 Optional<OsAccountRealm> optDestRealmName = Optional.empty();
800 if (!sourceRealm.getRealmNames().isEmpty()) {
801 optDestRealmName = db.
getOsAccountRealmManager().getRealmByName(sourceRealm.getRealmNames().get(0), destHost, trans.getConnection());
816 OsAccountRealm destRealm = null;
817 if (optDestRealmAddr.isPresent() && optDestRealmName.isPresent()) {
818 if (optDestRealmAddr.get().getRealmId() == optDestRealmName.get().getRealmId()) {
820 destRealm = optDestRealmAddr.get();
822 if (optDestRealmName.get().getRealmAddr().isPresent()) {
824 destRealm = optDestRealmAddr.get();
828 mergeRealms(optDestRealmName.get(), optDestRealmAddr.get(), trans);
829 destRealm =
getRealmByRealmId(optDestRealmAddr.get().getRealmId(), trans.getConnection());
832 }
else if (optDestRealmAddr.isPresent()) {
834 destRealm = optDestRealmAddr.get();
835 }
else if (optDestRealmName.isPresent()) {
839 if (! (optDestRealmName.get().getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent())) {
840 destRealm = optDestRealmName.get();
845 if (destRealm == null) {
846 moveRealm(sourceRealm, destHost, trans);
848 mergeRealms(sourceRealm, destRealm, trans);
863 private void moveRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans)
throws TskCoreException {
864 try(Statement s = trans.getConnection().createStatement()) {
865 String query =
"UPDATE tsk_os_account_realms SET scope_host_id = " + destHost.getHostId() +
" WHERE id = " + sourceRealm.getRealmId();
866 s.executeUpdate(query);
867 }
catch (SQLException ex) {
868 throw new TskCoreException(
"Error moving realm with id: " + sourceRealm.getRealmId() +
" to host with id: " + destHost.getHostId(), ex);
882 void mergeRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans)
throws TskCoreException {
888 CaseDbConnection connection = trans.getConnection();
889 try (Statement statement = connection.createStatement()) {
890 String updateStr =
"UPDATE tsk_os_account_realms SET db_status = " + OsAccountRealm.RealmDbStatus.MERGED.getId()
891 +
", merged_into = " + destRealm.getRealmId()
892 +
", realm_signature = '" + makeMergedRealmSignature() +
"' "
893 +
" WHERE id = " + sourceRealm.getRealmId();
894 connection.executeUpdate(statement, updateStr);
895 }
catch (SQLException ex) {
896 throw new TskCoreException (
"Error updating status of realm with id: " + sourceRealm.getRealmId(), ex);
900 if (!destRealm.getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent()) {
901 updateRealm(destRealm, sourceRealm.getRealmAddr().get(), null, trans.getConnection());
902 }
else if (destRealm.getRealmNames().isEmpty() && !sourceRealm.getRealmNames().isEmpty()) {
903 updateRealm(destRealm, null, sourceRealm.getRealmNames().get(0), trans.getConnection());
917 List<OsAccountRealm> getRealmsByHost(Host host, CaseDbConnection connection)
throws TskCoreException {
918 List<OsAccountRealm> results =
new ArrayList<>();
919 String queryString = REALM_QUERY_STRING
920 +
" WHERE realms.scope_host_id = " + host.getHostId();
923 try ( Statement s = connection.createStatement();
924 ResultSet rs = connection.executeQuery(s, queryString)) {
926 results.add(resultSetToAccountRealm(rs));
929 }
catch (SQLException ex) {
930 throw new TskCoreException(String.format(
"Error gettings realms for host with id = " + host.getHostId()), ex);
957 this.updateStatus = updateStatus;
958 this.updatedRealm = updatedRealm;
966 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()