diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index d2c8a826cc23d5580d76f904d5926ebf2b7fa61c..d369e2f8f408630ae9ef747878d5ea74f607a845 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -212,8 +212,8 @@ private void createFileTables(Statement stmt) throws SQLException { + "mtime " + dbQueryHelper.getBigIntType() + ", mode INTEGER, uid INTEGER, gid INTEGER, md5 TEXT, sha256 TEXT, " + "known INTEGER, " + "parent_path TEXT, mime_type TEXT, extension TEXT, " - + "owner_uid TEXT, " - + "os_account_obj_id " + dbQueryHelper.getBigIntType() + ", " + + "owner_uid TEXT DEFAULT NULL, " + + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, " @@ -281,7 +281,7 @@ private void createArtifactTables(Statement stmt) throws SQLException { } private void createAnalysisResultsTables(Statement stmt) throws SQLException { - stmt.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + stmt.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, " + "conclusion TEXT, " + "significance INTEGER NOT NULL, " + "confidence INTEGER NOT NULL, " @@ -290,7 +290,7 @@ private void createAnalysisResultsTables(Statement stmt) throws SQLException { + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE" + ")"); - stmt.execute("CREATE TABLE tsk_aggregate_score( obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + stmt.execute("CREATE TABLE tsk_aggregate_score( obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, " + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "significance INTEGER NOT NULL, " + "confidence INTEGER NOT NULL, " @@ -442,7 +442,7 @@ private void createAccountTables(Statement stmt) throws SQLException { + "status INTEGER, " // enabled/disabled/deleted + "admin INTEGER DEFAULT 0," // is admin account + "type INTEGER, " // service/interactive - + "created_date " + dbQueryHelper.getBigIntType() + ", " + + "created_date " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " + "UNIQUE(signature), " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id) )"); @@ -474,7 +474,7 @@ private void createAccountTables(Statement stmt) throws SQLException { stmt.execute("CREATE TABLE tsk_data_artifacts ( " + "artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, " - + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + + "os_account_obj_id " + dbQueryHelper.getBigIntType() + ", " + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE) "); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java index 237dd4b4ab21d3bc0d552a04ff0d043011348910..243c32c9083a0f63fb466d8b6cf497113504f138 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java @@ -53,7 +53,7 @@ public final class OsAccount extends AbstractContent { private boolean isAdmin = false; // is admin account. private OsAccountType osAccountType = OsAccountType.UNKNOWN; private OsAccountStatus osAccountStatus; - private long creationTime = 0; + private Long creationTime = null; private final List<OsAccountAttribute> osAccountAttributes = new ArrayList<>(); @@ -132,7 +132,7 @@ public enum OsAccountType { * * @return Account type id. */ - int getId() { + public int getId() { return id; } @@ -141,7 +141,7 @@ int getId() { * * @return Account type name. */ - String getName() { + public String getName() { return name; } @@ -371,8 +371,8 @@ public boolean isAdmin() { * * @return Account creation time, returns 0 if creation time is not known. */ - public long getCreationTime() { - return creationTime; + public Optional<Long> getCreationTime() { + return Optional.of(creationTime); } /** diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountAttribute.java index 657c012bc68195c9fe77a3772e4da40743432440..d1be2cd722af4152bc6c5b5fb90e3cede911b901 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountAttribute.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountAttribute.java @@ -30,18 +30,18 @@ public final class OsAccountAttribute extends AbstractAttribute { private final long osAccountObjId; // OS account to which this attribute belongs. private final Long hostId; // Host to which this attribute applies, may be null - private final long sourceObjId; // Object id of the source where the attribute was discoevered. + private final long sourceObjId; // Object id of the source where the attribute was discovered. /** * Creates an os account attribute with int value. * * @param attributeType Attribute type. * @param valueInt Int value. - * @param osAccountObjId Obj id of account which the attribute pertains to. - * @param sourceObjId Object id of the source where the attribute was - * found. + * @param osAccountObjId Obj id of account which the attribute pertains to. * @param hostId Id of host on which the attribute applies to. Pass * Null if it applies across hosts. + * @param sourceObjId Object id of the source where the attribute was + * found. */ public OsAccountAttribute(BlackboardAttribute.Type attributeType, int valueInt, long osAccountObjId, Long hostId, long sourceObjId) { super(attributeType, valueInt); @@ -57,10 +57,10 @@ public OsAccountAttribute(BlackboardAttribute.Type attributeType, int valueInt, * @param attributeType Attribute type. * @param valueLong Long value. * @param osAccountObjId Obj id of account which the attribute pertains to. - * @param sourceObjId Object id of the source where the attribute was - * found. * @param hostId Id of host on which the attribute applies to. Pass * Null if it applies across hosts. + * @param sourceObjId Object id of the source where the attribute was + * found. */ public OsAccountAttribute(BlackboardAttribute.Type attributeType, long valueLong, long osAccountObjId, Long hostId, long sourceObjId) { super(attributeType, valueLong); @@ -76,10 +76,10 @@ public OsAccountAttribute(BlackboardAttribute.Type attributeType, long valueLong * @param attributeType Attribute type. * @param valueDouble Double value. * @param osAccountObjId Obj id of account which the attribute pertains to. - * @param sourceObjId Object id of the source where the attribute was - * found. * @param hostId Id of host on which the attribute applies to. Pass * Null if it applies across hosts. + * @param sourceObjId Object id of the source where the attribute was + * found. */ public OsAccountAttribute(BlackboardAttribute.Type attributeType, double valueDouble, long osAccountObjId, Long hostId, long sourceObjId) { super(attributeType, valueDouble); @@ -95,10 +95,10 @@ public OsAccountAttribute(BlackboardAttribute.Type attributeType, double valueDo * @param attributeType Attribute type. * @param valueString String value. * @param osAccountObjId Obj id of account which the attribute pertains to. - * @param sourceObjId Object id of the source where the attribute was - * found. * @param hostId Id of host on which the attribute applies to. Pass * Null if it applies across hosts. + * @param sourceObjId Object id of the source where the attribute was + * found. */ public OsAccountAttribute(BlackboardAttribute.Type attributeType, String valueString, long osAccountObjId, Long hostId, long sourceObjId) { super(attributeType, valueString); @@ -114,10 +114,10 @@ public OsAccountAttribute(BlackboardAttribute.Type attributeType, String valueSt * @param attributeType Attribute type. * @param valueBytes Bytes value. * @param osAccountObjId Obj id of account which the attribute pertains to. - * @param sourceObjId Object id of the source where the attribute was - * found. * @param hostId Id of host on which the attribute applies to. Pass * Null if it applies across hosts. + * @param sourceObjId Object id of the source where the attribute was + * found. */ public OsAccountAttribute(Type attributeType, byte[] valueBytes, long osAccountObjId, Long hostId, long sourceObjId) { super(attributeType, valueBytes); @@ -145,7 +145,7 @@ public OsAccountAttribute(Type attributeType, byte[] valueBytes, long osAccountO * Null if it applies across hosts. */ OsAccountAttribute(BlackboardAttribute.Type attributeType, int valueInt, long valueLong, double valueDouble, String valueString, byte[] valueBytes, - SleuthkitCase sleuthkitCase, long osAccountObjId, long sourceObjId, Long hostId) { + SleuthkitCase sleuthkitCase, long osAccountObjId, Long hostId, long sourceObjId) { super( attributeType, valueInt, valueLong, valueDouble, valueString, valueBytes, diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java index 6ca2b9254b8af0e74f3dd8944746f18767ad249c..50e6929bac4231d93b202fae179456de950ec694 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java @@ -55,13 +55,13 @@ public final class OsAccountManager { } /** - * Creates an OS account with given unique id or given realm/loginname. + * Creates an OS account with given unique id or given realm and login name. * If an account already exists with the given id or realm/login, then the * existing OS account is returned. * * @param uniqueAccountId Account sid/uid. * @param loginName Login name. - * @param realmName Realm within which the accountId/loginName is + * @param realmName Realm within which the accountId or login name is * unique. * @param host Host for the realm, may be null. * @@ -81,7 +81,8 @@ public OsAccount createOsAccount(String uniqueAccountId, String loginName, Strin } Optional<OsAccountRealm> realm = Optional.empty(); - try (CaseDbConnection connection = this.db.getConnection()) { + + try (CaseDbConnection connection = this.db.getConnection();) { // get the realm with given name realm = db.getOsAccountRealmManager().getRealmByName(realmName, host, connection); @@ -89,49 +90,54 @@ public OsAccount createOsAccount(String uniqueAccountId, String loginName, Strin // realm was not found, create it. realm = Optional.of(db.getOsAccountRealmManager().createRealmByName(realmName, host)); } - - return createOsAccount(uniqueAccountId, loginName, realm.get(), OsAccount.OsAccountStatus.UNKNOWN, connection); - } catch (SQLException ex) { - - // Create may fail if an OsAccount already exists. - try (CaseDbConnection connection = this.db.getConnection()) { - - Optional<OsAccount> osAccount; - - // First search for account by uniqueId - if (!Strings.isNullOrEmpty(uniqueAccountId)) { - osAccount = getOsAccountByUniqueId(uniqueAccountId, host, connection); - if (osAccount.isPresent()) { - return osAccount.get(); + + // try to create account + try { + return createOsAccount(uniqueAccountId, loginName, realm.get(), OsAccount.OsAccountStatus.UNKNOWN, connection); + } catch (SQLException ex) { + + // Create may fail if an OsAccount already exists. + try (CaseDbConnection newConnection = this.db.getConnection()) { + + Optional<OsAccount> osAccount; + + // First search for account by uniqueId + if (!Strings.isNullOrEmpty(uniqueAccountId)) { + osAccount = getOsAccountByUniqueId(uniqueAccountId, host, newConnection); + if (osAccount.isPresent()) { + return osAccount.get(); + } } - } - // search by loginName - if (!Strings.isNullOrEmpty(loginName)) { - osAccount = getOsAccountByLoginName(loginName, realm.get(), connection); - if (osAccount.isPresent()) { - return osAccount.get(); + // search by loginName + if (!Strings.isNullOrEmpty(loginName)) { + osAccount = getOsAccountByLoginName(loginName, realm.get(), newConnection); + if (osAccount.isPresent()) { + return osAccount.get(); + } } - } - // create failed for some other reason, throw an exception - throw new TskCoreException(String.format("Error creating OsAccount with loginName = %s", loginName), ex); + // create failed for some other reason, throw an exception + throw new TskCoreException(String.format("Error creating OsAccount with loginName = %s", loginName), ex); + } } - } + } } /** - * Get the OS account with given unique id or given realm name/login name. + * Get the OS account with given unique id or given realm name and login name. * * @param uniqueAccountId Account sid/uid. * @param loginName Login name. - * @param realmName Realm within which the accountId/loginName is + * @param realmName Realm within which the accountId & login name is * unique. * @param host Host for the realm, may be null. * @param transaction Transaction to use for database operation. * * @return Optional with OsAccount matching the given uniqueId, or * realm/login name. Optional.empty if no match is found. + * + * @throws TskCoreException If there is an error getting the account. */ public Optional<OsAccount> getOsAccount(String uniqueAccountId, String loginName, String realmName, Host host, CaseDbTransaction transaction) throws TskCoreException { @@ -144,19 +150,24 @@ public Optional<OsAccount> getOsAccount(String uniqueAccountId, String loginName throw new IllegalArgumentException("Realm name is required to create an OS account."); } - Optional<OsAccount> osAccount = this.getOsAccountByUniqueId(uniqueAccountId, host, transaction.getConnection()); - - if (osAccount.isPresent()) { - return osAccount; + if (!Strings.isNullOrEmpty(uniqueAccountId)) { + Optional<OsAccount> osAccount = this.getOsAccountByUniqueId(uniqueAccountId, host, transaction.getConnection()); + if (osAccount.isPresent()) { + return osAccount; + } } - // first get the realm - Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getRealmByName(realmName, host, transaction); - if (!realm.isPresent()) { - throw new TskCoreException(String.format("No realm found with name %s", realmName)); + if (!Strings.isNullOrEmpty(loginName)) { + // first get the realm + Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getRealmByName(realmName, host, transaction); + if (!realm.isPresent()) { + throw new TskCoreException(String.format("No realm found with name %s", realmName)); + } + + return getOsAccountByLoginName(loginName, realm.get(), transaction.getConnection()); } - return getOsAccountByLoginName(loginName, realm.get(), transaction.getConnection()); + return Optional.empty(); } /** @@ -178,16 +189,7 @@ private OsAccount createOsAccount(String uniqueId, String loginName, OsAccountRe throw new IllegalArgumentException("Cannot create an OS Account, realm is NULL."); } - // Create a signature. This signature is simply to prevent duplicate accounts from being created. - // Signature is set to realmId/uniqueId or realmId/loginName - String signature; - if (Strings.isNullOrEmpty(uniqueId) == false) { - signature = String.format("%d/%s", realm.getId(), uniqueId); - } else if (Strings.isNullOrEmpty(loginName) == false) { - signature = String.format("%d/%s", realm.getId(), loginName); - } else { - throw new IllegalArgumentException("Cannot create OS Account, either a uniqueID or a login name must be provided."); - } + String signature = makeAccountSignature(realm, uniqueId, loginName); db.acquireSingleUserCaseWriteLock(); try { @@ -251,8 +253,8 @@ public Optional<OsAccount> getOsAccount(String uniqueId, Host host, CaseDbTransa /** * Gets the OS account for the given unique id. * - * @param uniqueId Account SID/uid. - * @param Host Host to match the realm, may be null. + * @param uniqueId Account SID/uid. + * @param host Host to match the realm, may be null. * @param connection Database connection to use. * * @return Optional with OsAccount, Optional.empty if no account with matching uniqueId is found. @@ -292,12 +294,12 @@ private Optional<OsAccount> getOsAccountByUniqueId(String uniqueId, Host host, C return Optional.of(osAccountFromResultSet(rs, realm)); } } catch (SQLException ex) { - throw new TskCoreException(String.format("Error getting OS account for unique id = %s", uniqueId), ex); + throw new TskCoreException(String.format("Error getting OS account for unique id = %s and host = %s", uniqueId, (host != null ? host.getName() : "null")), ex); } } /** - * Gets a OS Account by the realm/loginName. + * Gets a OS Account by the realm and login name. * * @param loginName Login name. * @param realm Account Realm. @@ -311,12 +313,9 @@ private Optional<OsAccount> getOsAccountByUniqueId(String uniqueId, Host host, C private Optional<OsAccount> getOsAccountByLoginName(String loginName, OsAccountRealm realm, CaseDbConnection connection) throws TskCoreException { String queryString = "SELECT * FROM tsk_os_accounts" - + " WHERE LOWER(login_name) = LOWER('" + loginName + "')"; + + " WHERE LOWER(login_name) = LOWER('" + loginName + "')" + + " AND realm_id = " + realm.getId(); - if (realm != null) { - queryString += " AND realm_id = " + realm.getId(); - } - try (Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryString)) { @@ -505,8 +504,8 @@ public OsAccount createOsAccountByWindowsSID(String sid, Host host) throws TskCo /** * Gets an OS account with the given SID. * - * @param sid Account SID. - * @param host Host for the realm. + * @param sid Account SID. + * @param host Host for the realm. * @param transaction Transaction to use. * * @return Optional with OsAccount, Optional.empty if no matching OsAccount is found. @@ -609,8 +608,9 @@ public Optional<OsAccount> getOsAccountByLogin(String loginName, String realmNam * Update the account login name. * * @param accountObjId Object id of the account to update. - * @param loginName Account login name. - * @param transaction Transaction + * @param loginName Account login name. + * @param transaction Transaction + * * * @return OsAccount Updated account. * @@ -736,14 +736,7 @@ OsAccount updateAccount(OsAccount osAccount, CaseDbTransaction transaction ) thr CaseDbConnection connection = transaction.getConnection(); db.acquireSingleUserCaseWriteLock(); - String signature; - if (osAccount.getUniqueIdWithinRealm().isPresent()) { - signature = String.format("%d/%s", osAccount.getRealm().getId(), osAccount.getUniqueIdWithinRealm().get()); - } else if (osAccount.getLoginName().isPresent() == false) { - signature = String.format("%d/%s", osAccount.getRealm().getId(), osAccount.getLoginName().get()); - } else { - throw new IllegalArgumentException("Cannot update OS Account, either a uniqueID or a login name must be provided."); - } + String signature = makeAccountSignature(osAccount.getRealm(), osAccount.getUniqueIdWithinRealm().orElse(null), osAccount.getLoginName().orElse(null)); try { String updateSQL = "UPDATE tsk_os_accounts SET " @@ -771,8 +764,8 @@ OsAccount updateAccount(OsAccount osAccount, CaseDbTransaction transaction ) thr preparedStatement.setInt(5, osAccount.getOsAccountStatus().getId()); preparedStatement.setInt(6, osAccount.isAdmin() ? 1 : 0); preparedStatement.setInt(7, osAccount.getOsAccountType().getId()); - preparedStatement.setLong(8, osAccount.getCreationTime()); - + + preparedStatement.setLong(8, osAccount.getCreationTime().orElse(null)); preparedStatement.setLong(9, osAccount.getId()); connection.executeUpdate(preparedStatement); @@ -827,4 +820,29 @@ private OsAccount osAccountFromResultSet(ResultSet rs, OsAccountRealm realm) thr return osAccount; } + + /** + * Created an account signature for an OS Account. This signature is simply + * to prevent duplicate accounts from being created. Signature is set to: + * realmId/uniqueId: if the account has a uniqueId, otherwise + * realmId/loginName: if the account has a login name. + * + * @param realm Account realm + * @param uniqueId Unique id. + * @param loginName Login name. + * + * @return Account signature. + */ + private String makeAccountSignature(OsAccountRealm realm, String uniqueId, String loginName) { + // Create a signature. + String signature; + if (Strings.isNullOrEmpty(uniqueId) == false) { + signature = String.format("%d/%s", realm.getId(), uniqueId); + } else if (Strings.isNullOrEmpty(loginName) == false) { + signature = String.format("%d/%s", realm.getId(), loginName); + } else { + throw new IllegalArgumentException("OS Account must have either a uniqueID or a login name."); + } + return signature; + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java index e31dba03e9b52afb6ecfe57bbb16e062167e713d..43d3b7bfe5f5dc8f88d69f74120880ba7bd2751e 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java @@ -57,7 +57,7 @@ public final class OsAccountRealmManager { * * @param realmName Realm name. * @param host Host that realm reference was found on. May be null if - * you know the realm is a domain and not host-specific. + * the realm is a domain and not host-specific. * * @return OsAccountRealm Realm. * @@ -78,7 +78,7 @@ public OsAccountRealm createRealmByName(String realmName, Host host) throws TskC if (accountRealm.isPresent()) { return accountRealm.get(); } else { - throw new TskCoreException(String.format("Error creating realm with name = %s", realmName), ex); + throw new TskCoreException(String.format("Error creating realm with name = %s and host name = %s", realmName, (host != null ? host.getName() : "null")), ex); } } } @@ -121,17 +121,14 @@ public OsAccountRealm createRealmByWindowsSid(String sid, Host host) throws TskC } // get subAuthority sid - if (org.apache.commons.lang3.StringUtils.countMatches(sid, "-") < 5 ) { - throw new IllegalArgumentException(String.format("Invalid SID %s for a host/domain", sid)); - } - String subAuthorityId = sid.substring(0, sid.lastIndexOf('-')); + String subAuthorityId = getSubAuthorityId(sid); // RAMAN TBD: can the SID be parsed in some way to determine local vs domain ?? try (CaseDbConnection connection = this.db.getConnection()) { return createRealm("Unknown Domain Name", OsAccountRealm.RealmNameType.INFERRED, subAuthorityId, host, connection); } catch (SQLException ex) { - // Create may have failed if the realm already exists. try to get the realm by name. + // Create may have failed if the realm already exists. try to get the realm by addr. try (CaseDbConnection connection = this.db.getConnection()) { Optional<OsAccountRealm >accountRealm = this.getRealmByAddr(subAuthorityId, host, connection); if (accountRealm.isPresent()) { @@ -148,8 +145,8 @@ public OsAccountRealm createRealmByWindowsSid(String sid, Host host) throws TskC * Get the realm for the given user/group SID. The input SID is a user/group * SID. The domain SID is extracted from this incoming SID. * - * @param sid user SID. - * @param host Host for realm, may be null. + * @param sid User SID. + * @param host Host for realm, may be null. * @param transaction Transaction to use for database connection. * * @return Optional with OsAccountRealm, Optional.empty if no realm found with matching real address. @@ -159,10 +156,7 @@ public OsAccountRealm createRealmByWindowsSid(String sid, Host host) throws TskC public Optional<OsAccountRealm> getRealmByWindowsSid(String sid, Host host, CaseDbTransaction transaction) throws TskCoreException { // get subAuthority sid - if (org.apache.commons.lang3.StringUtils.countMatches(sid, "-") < 5 ) { - throw new IllegalArgumentException(String.format("Invalid SID %s for a host/domain", sid)); - } - String subAuthorityId = sid.substring(0, sid.lastIndexOf('-')); + String subAuthorityId = getSubAuthorityId(sid); CaseDbConnection connection = transaction.getConnection(); return this.getRealmByAddr(subAuthorityId, host, connection); @@ -194,14 +188,13 @@ OsAccountRealm updateRealmName(long realmId, String realmName, OsAccountRealm.Re return getRealm(realmId, connection ); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, null, ex); throw new TskCoreException(String.format("Error updating realm with name = %s, id = %d", realmName, realmId), ex); } finally { db.releaseSingleUserCaseWriteLock(); } } - private final String REALM_QUERY_STRING = "SELECT realms.id as realm_id, realms.name as realm_name," + private final static String REALM_QUERY_STRING = "SELECT realms.id as realm_id, realms.name as realm_name," + " realms.realm_addr as realm_addr, realms.host_id, realms.name_type, " + " hosts.id, hosts.name as host_name " + " FROM tsk_os_account_realms as realms" @@ -227,6 +220,8 @@ OsAccountRealm getRealm(long id, CaseDbConnection connection) throws TskCoreExce OsAccountRealm accountRealm = null; if (rs.next()) { accountRealm = resultSetToAccountRealm(rs); + } else { + throw new TskCoreException(String.format("No realm found with id = %d", id)); } return accountRealm; @@ -248,6 +243,8 @@ OsAccountRealm getRealm(long id, CaseDbConnection connection) throws TskCoreExce */ Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConnection connection) throws TskCoreException { + // If a host is specified, we want to match the realm with matching name and specified host, or a realm with matching name and no host. + // If no host is specified, then we return the first realm with matching name. String whereHostClause = (host == null) ? " 1 = 1 " : " ( realms.host_id = " + host.getId() + " OR realms.host_id IS NULL) "; @@ -277,7 +274,8 @@ Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConne } return Optional.ofNullable(accountRealm); } catch (SQLException ex) { - throw new TskCoreException(String.format("Error running the realms query = %s", queryString), ex); + throw new TskCoreException(String.format("Error running the realms query = %s with realmaddr = %s and host name = %s", + queryString, realmAddr, (host != null ? host.getName() : "Null") ), ex); } } @@ -293,6 +291,8 @@ Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConne */ Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConnection connection) throws TskCoreException { + // If a host is specified, we want to match the realm with matching name and specified host, or a realm with matching name and no host. + // If no host is specified, then we return the first realm with matching name. String whereHostClause = (host == null) ? " 1 = 1 " : " ( realms.host_id = " + host.getId() + " OR realms.host_id IS NULL ) "; @@ -348,11 +348,11 @@ private OsAccountRealm resultSetToAccountRealm(ResultSet rs) throws SQLException rs.getString("realm_addr"), realmhost); } - /** - * Get all realms. - * - * @return Collection of OsAccountRealm - */ +// /** +// * Get all realms. +// * +// * @return Collection of OsAccountRealm +// */ // Collection<OsAccountRealm> getRealms() throws TskCoreException { // String queryString = "SELECT realms.id as realm_id, realms.name as realm_name, realms.realm_addr as realm_addr, realms.host_id, realms.name_type, " // + " hosts.id, hosts.name as host_name " @@ -421,15 +421,27 @@ private OsAccountRealm createRealm(String realmName, OsAccountRealm.RealmNameTyp // Read back the row id try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) { - if (resultSet.next()) { - long rowId = resultSet.getLong(1); ;//last_insert_rowid() - return new OsAccountRealm(rowId, realmName, nameType, realmAddr, host); - } else { - throw new SQLException("Error executing " + realmInsertSQL); - } + long rowId = resultSet.getLong(1); // last_insert_rowid() + return new OsAccountRealm(rowId, realmName, nameType, realmAddr, host); } } finally { db.releaseSingleUserCaseWriteLock(); } } + + /** + * Gets the sub authority id from the given SID. + * + * @param sid SID + * + * @return Sub-authority id string. + */ + private String getSubAuthorityId(String sid) { + if (org.apache.commons.lang3.StringUtils.countMatches(sid, "-") < 5 ) { + throw new IllegalArgumentException(String.format("Invalid SID %s for a host/domain", sid)); + } + String subAuthorityId = sid.substring(0, sid.lastIndexOf('-')); + + return subAuthorityId; + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 159e8c88cd69e7a756a7b27fe25a51c06222ad43..10530f2d45b2566c1eb50d17f5f74b48ea68f5d5 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -2357,7 +2357,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))"); // create analysis results tables - statement.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + bigIntDataType + " NOT NULL, " + statement.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + bigIntDataType + " PRIMARY KEY, " + "conclusion TEXT, " + "significance INTEGER NOT NULL, " + "confidence INTEGER NOT NULL, " @@ -2366,7 +2366,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE" + ")"); - statement.execute("CREATE TABLE tsk_aggregate_score( obj_id " + bigIntDataType + " NOT NULL, " + statement.execute("CREATE TABLE tsk_aggregate_score( obj_id " + bigIntDataType + " PRIMARY KEY, " + "data_source_obj_id " + bigIntDataType + " NOT NULL, " + "significance INTEGER NOT NULL, " + "confidence INTEGER NOT NULL, " @@ -2383,7 +2383,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem + "status INTEGER DEFAULT 0, " // to indicate if the host was merged/deleted + "UNIQUE(name)) "); - // Create OS Account and realted tables + // Create OS Account and related tables statement.execute("CREATE TABLE tsk_os_account_realms (id " + primaryKeyType + " PRIMARY KEY, " + "name TEXT NOT NULL, " // realm name - host name or domain name + "realm_addr TEXT DEFAULT NULL, " // a sid/uid or some some other unique identifier, may be null @@ -2402,7 +2402,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem + "status INTEGER, " // enabled/disabled/deleted + "admin INTEGER DEFAULT 0," // is admin account + "type INTEGER, " // service/interactive - + "created_date " + bigIntDataType + ", " + + "created_date " + bigIntDataType + " DEFAULT NULL, " + "UNIQUE(signature), " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id) )"); @@ -2434,13 +2434,13 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem statement.execute("CREATE TABLE tsk_data_artifacts ( " + "artifact_obj_id " + bigIntDataType + " PRIMARY KEY, " - + "os_account_obj_id " + bigIntDataType + " NOT NULL, " + + "os_account_obj_id " + bigIntDataType + ", " + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE) "); // add owner_uid & os_account_obj_id columns to tsk_files statement.execute("ALTER TABLE tsk_files ADD COLUMN owner_uid TEXT DEFAULT NULL"); - statement.execute("ALTER TABLE tsk_files ADD COLUMN os_account_obj_id " + bigIntDataType + " REFERENCES tsk_os_accounts(os_account_obj_id) DEFAULT NULL"); + statement.execute("ALTER TABLE tsk_files ADD COLUMN os_account_obj_id " + bigIntDataType + " DEFAULT NULL REFERENCES tsk_os_accounts(os_account_obj_id) DEFAULT NULL"); return new CaseDbSchemaVersionNumber(8, 7); @@ -9212,10 +9212,7 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnecti parentPath = "/"; //NON-NLS } - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); LayoutFile lf = new LayoutFile(this, rs.getLong("obj_id"), //NON-NLS @@ -9293,10 +9290,7 @@ org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLExcepti * @throws SQLException thrown if SQL error occurred */ Directory directory(ResultSet rs, FileSystem fs) throws SQLException { - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); Directory dir = new Directory(this, rs.getLong("obj_id"), rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS @@ -9446,10 +9440,7 @@ private DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long parentPath = ""; } - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); final DerivedFile df = new DerivedFile(this, objId, rs.getLong("data_source_obj_id"), rs.getString("name"), //NON-NLS @@ -9505,10 +9496,7 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare if (null == parentPath) { parentPath = ""; } - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); LocalFile file = new LocalFile(this, objId, rs.getString("name"), //NON-NLS TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type")), //NON-NLS @@ -9536,10 +9524,7 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare * @throws SQLException */ org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws SQLException { - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); org.sleuthkit.datamodel.SlackFile f = new org.sleuthkit.datamodel.SlackFile(this, rs.getLong("obj_id"), //NON-NLS rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS @@ -9606,10 +9591,7 @@ List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long paren if (parentPath == null) { parentPath = ""; } - long osAccountObjId = rs.getInt("os_account_obj_id"); - if (rs.wasNull()) { - osAccountObjId = OsAccount.NO_ACCOUNT; - } + Long osAccountObjId = rs.getLong("os_account_obj_id"); final LayoutFile lf = new LayoutFile(this, rs.getLong("obj_id"), rs.getLong("data_source_obj_id"), rs.getString("name"), type, TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java index 48990b68833621fd2812482b5882b89f8f0333a9..b6a0f90fd73e280a473dd4a6791061cd6bfb89c3 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java @@ -252,14 +252,14 @@ public void basicOsAccountTests() throws TskCoreException { // Let's update osAccount1 String fullName1 = "Johnny Depp"; - long creationTime1 = 1611858618; + Long creationTime1 = 1611858618L; osAccount1.setCreationTime(creationTime1); osAccount1.setFullName(fullName1); osAccount1.setIsAdmin(true); osAccount1 = caseDB.getOsAccountManager().updateAccount(osAccount1, transaction); - assertEquals(osAccount1.getCreationTime(), creationTime1); + assertEquals(osAccount1.getCreationTime().orElse(null), creationTime1); transaction.commit(); @@ -279,7 +279,7 @@ public void basicOsAccountTests() throws TskCoreException { assertEquals(osAccount1_copy1.isAdmin(), true); // should be true now assertEquals(osAccount1_copy1.getFullName().orElse("").equalsIgnoreCase(fullName1), true); - assertEquals(osAccount1_copy1.getCreationTime(), creationTime1); + assertEquals(osAccount1.getCreationTime().orElse(null), creationTime1); }