diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java index 3e883622979eebc9656d2ef50970ed47a959ae91..24297c6f4cc51398de0b39ef1b48b023a64c34c0 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java @@ -1277,26 +1277,31 @@ private <T> void updateAccountColumn(long accountObjId, String colName, T colVal + " SET " + colName + " = ? " + " WHERE os_account_obj_id = ?"; - PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); - preparedStatement.clearParameters(); + db.acquireSingleUserCaseWriteLock(); + try { + PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); + preparedStatement.clearParameters(); - if (Objects.isNull(colValue)) { - preparedStatement.setNull(1, Types.NULL); // handle null value - } else { - if (colValue instanceof String) { - preparedStatement.setString(1, (String) colValue); - } else if (colValue instanceof Long) { - preparedStatement.setLong(1, (Long) colValue); - } else if (colValue instanceof Integer) { - preparedStatement.setInt(1, (Integer) colValue); + if (Objects.isNull(colValue)) { + preparedStatement.setNull(1, Types.NULL); // handle null value } else { - throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId)); + if (colValue instanceof String) { + preparedStatement.setString(1, (String) colValue); + } else if (colValue instanceof Long) { + preparedStatement.setLong(1, (Long) colValue); + } else if (colValue instanceof Integer) { + preparedStatement.setInt(1, (Integer) colValue); + } else { + throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId)); + } } - } - preparedStatement.setLong(2, accountObjId); - - connection.executeUpdate(preparedStatement); + preparedStatement.setLong(2, accountObjId); + + connection.executeUpdate(preparedStatement); + } finally { + db.releaseSingleUserCaseWriteLock(); + } } /** @@ -1444,16 +1449,19 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad updateStatusCode = OsAccountUpdateStatus.UPDATED; } - // update signature if needed - if (updateStatusCode == OsAccountUpdateStatus.UPDATED) { - String newSignature = getOsAccountSignature(address, loginName); - updateAccountSignature(osAccount.getId(), newSignature, connection); - } - // if nothing is changed, return if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) { return new OsAccountUpdateResult(updateStatusCode, osAccount); } + + // update signature if needed, based on the most current addr/loginName + OsAccount currAccount = getOsAccountByObjectId(osAccount.getId(), connection); + String newAddress = currAccount.getAddr().orElse(null); + String newLoginName = currAccount.getLoginName().orElse(null); + + String newSignature = getOsAccountSignature(newAddress, newLoginName); + updateAccountSignature(osAccount.getId(), newSignature, connection); + // get the updated account from database updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection); @@ -1467,7 +1475,6 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad } } - /** * Returns a list of hosts where the OsAccount has appeared. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java index c9ebe104eb3b02a50ea099822e1a590233a0bea0..f3b3751415e00671e658fad7adef03e87590109c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java @@ -24,8 +24,10 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; @@ -303,74 +305,113 @@ public OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, S * @throws TskCoreException If there is a database error or if a realm * already exists with that information. */ - private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException { - + private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException { + // need at least one of the two if (StringUtils.isBlank(realmAddr) && StringUtils.isBlank(realmName)) { throw new TskCoreException("Realm address or name is required to update realm."); } - + OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE; OsAccountRealm updatedRealm = null; - - List<String> realmNames = realm.getRealmNames(); - String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. - String currRealmAddr = realm.getRealmAddr().orElse(null); - - - // set name and address to new values only if the current value is blank and the new value isn't. - String newRealmAddr; - if ( (StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) { - newRealmAddr = realmAddr; - updateStatusCode = OsRealmUpdateStatus.UPDATED; - } else { - newRealmAddr = currRealmAddr; - } - - String newRealmName; - if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) { - newRealmName = realmName; - updateStatusCode = OsRealmUpdateStatus.UPDATED; - } else { - newRealmName = currRealmName; - } - - // make new signature - String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null)); - - // if nothing is to be changed, return - if ( updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) { - return new OsRealmUpdateResult(updateStatusCode, realm); - } - - + db.acquireSingleUserCaseWriteLock(); try { - // We only alow realm addr, name and signature to be updated at this time. + List<String> realmNames = realm.getRealmNames(); + String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. + String currRealmAddr = realm.getRealmAddr().orElse(null); + + // set name and address to new values only if the current value is blank and the new value isn't. + if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) { + updateRealmColumn(realm.getRealmId(), "realm_addr", realmAddr, connection); + updateStatusCode = OsRealmUpdateStatus.UPDATED; + } + + if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) { + updateRealmColumn(realm.getRealmId(), "realm_name", realmName, connection); + updateStatusCode = OsRealmUpdateStatus.UPDATED; + } + + // if nothing is to be changed, return + if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) { + return new OsRealmUpdateResult(updateStatusCode, realm); + } + + // update realm signature - based on the most current address and name + OsAccountRealm currRealm = getRealmByRealmId(realm.getRealmId(), connection); + String newRealmAddr = currRealm.getRealmAddr().orElse(null); + String newRealmName = (currRealm.getRealmNames().isEmpty() == false) ? currRealm.getRealmNames().get(0) : null; + + // make new signature + String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null)); + // Use a random string as the signature if the realm is not active. - String updateSQL = "UPDATE tsk_os_account_realms SET realm_name = ?, realm_addr = ?, " - + " realm_signature = " + String updateSQL = "UPDATE tsk_os_account_realms SET " + + " realm_signature = " + " CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() + " THEN ? ELSE realm_signature END " + " WHERE id = ?"; PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); preparedStatement.clearParameters(); - - preparedStatement.setString(1, newRealmName); - preparedStatement.setString(2, newRealmAddr); - preparedStatement.setString(3, newSignature); // Is only set for active accounts - preparedStatement.setLong(4, realm.getRealmId()); + + preparedStatement.setString(1, newSignature); // Is only set for active accounts + preparedStatement.setLong(2, realm.getRealmId()); connection.executeUpdate(preparedStatement); - + // read the updated realm updatedRealm = this.getRealmByRealmId(realm.getRealmId(), connection); - + return new OsRealmUpdateResult(updateStatusCode, updatedRealm); } catch (SQLException ex) { - 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); + 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); + } finally { + db.releaseSingleUserCaseWriteLock(); + } + + } + + /** + * Updates specified column in the tsk_os_account_realms table to the specified value. + * + * @param <T> Type of value - must be a String, Long or an Integer. + * @param realmId Id of the realm to be updated. + * @param colName Name of column o be updated. + * @param colValue New column value. + * @param connection Database connection to use. + * + * @throws SQLException If there is an error updating the database. + * @throws TskCoreException If the value type is not handled. + */ + private <T> void updateRealmColumn(long realmId, String colName, T colValue, CaseDbConnection connection) throws SQLException, TskCoreException { + + String updateSQL = "UPDATE tsk_os_account_realms " + + " SET " + colName + " = ? " + + " WHERE id = ?"; + + db.acquireSingleUserCaseWriteLock(); + try { + PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); + preparedStatement.clearParameters(); + + if (Objects.isNull(colValue)) { + preparedStatement.setNull(1, Types.NULL); // handle null value + } else { + if (colValue instanceof String) { + preparedStatement.setString(1, (String) colValue); + } else if (colValue instanceof Long) { + preparedStatement.setLong(1, (Long) colValue); + } else if (colValue instanceof Integer) { + preparedStatement.setInt(1, (Integer) colValue); + } else { + throw new TskCoreException(String.format("Unhandled column data type received while updating the realm (id = %d) ", realmId)); + } + } + + preparedStatement.setLong(2, realmId); + + connection.executeUpdate(preparedStatement); } finally { db.releaseSingleUserCaseWriteLock(); } - } private final static String REALM_QUERY_STRING = "SELECT realms.id as realm_id, realms.realm_name as realm_name," diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java index eeb5d566e5c381dc38112b6787574e8f8c1ad34c..7c14f3e124b799ad10e4d468b58166d0f3021974 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java @@ -906,4 +906,70 @@ public void windowsAccountRealmUpdateTests() throws TskCoreException, OsAccountM } + + @Test + public void windowsAccountUpdateTests() throws TskCoreException, OsAccountManager.NotUserSIDException { + + + String hostname1 = "host55555"; + Host host1 = caseDB.getHostManager().newHost(hostname1); + + + // Test 1: create an account with a SID alone. Then update the loginName. + + String ownerUid1 = "S-1-5-21-111111111-222222222-555555555-0001"; + OsAccount osAccount1 = caseDB.getOsAccountManager().newWindowsOsAccount(ownerUid1, null, null, host1, OsAccountRealm.RealmScope.DOMAIN); + + + // now update the account login name + String loginname1 = "jbravo"; + + OsAccountUpdateResult updateResult = caseDB.getOsAccountManager().updateCoreWindowsOsAccountAttributes(osAccount1, null, loginname1, null, host1); + assertEquals(updateResult.getUpdateStatusCode(), OsAccountManager.OsAccountUpdateStatus.UPDATED); + assertEquals(updateResult.getUpdatedAccount().isPresent(), true); + OsAccount updatedAccount = updateResult.getUpdatedAccount().orElseThrow(() -> new TskCoreException("Updated account not found.")); + + // verify that account has both addr and loginName, and that signature is the addr + assertTrue(updatedAccount.getAddr().orElse("").equalsIgnoreCase(ownerUid1)); + assertTrue(updatedAccount.getLoginName().orElse("").equalsIgnoreCase(loginname1)); + assertTrue(updatedAccount.getSignature().equalsIgnoreCase(ownerUid1)); // account signature should not change + + + String realmAddr1 = "S-1-5-21-111111111-222222222-555555555"; + String realmSignature1 = realmAddr1 + "_DOMAIN"; // for a domain realm - signature is sid/name + "_DOMAIN" + + OsAccountRealm realm1 = caseDB.getOsAccountRealmManager().getRealmByRealmId(updatedAccount.getRealmId()); + assertTrue(realm1.getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1)); + assertTrue(realm1.getSignature().equalsIgnoreCase(realmSignature1)); + + + // TBD Test2: create an account with realmName/loginname and then update the SID + + String loginname2 = "janeB"; + String realmName2 = "realm55555"; + OsAccount osAccount2 = caseDB.getOsAccountManager().newWindowsOsAccount(null, loginname2, realmName2, host1, OsAccountRealm.RealmScope.DOMAIN); + + assertFalse(osAccount2.getAddr().isPresent()); + assertTrue(osAccount2.getLoginName().orElse("").equalsIgnoreCase(loginname2)); + assertTrue(osAccount2.getSignature().equalsIgnoreCase(loginname2)); // account signature should be the login name + + // now update the account SID + String ownerUid2 = "S-1-5-21-111111111-222222222-555555555-0007"; + OsAccountUpdateResult updateResult2 = caseDB.getOsAccountManager().updateCoreWindowsOsAccountAttributes(osAccount2, ownerUid2, null, realmName2, host1); + assertEquals(updateResult2.getUpdateStatusCode(), OsAccountManager.OsAccountUpdateStatus.UPDATED); + assertEquals(updateResult2.getUpdatedAccount().isPresent(), true); + OsAccount updatedAccount2 = updateResult2.getUpdatedAccount().orElseThrow(() -> new TskCoreException("Updated account not found.")); + + // verify that account has both addr and loginName, and that signature is the addr + assertTrue(updatedAccount2.getAddr().orElse("").equalsIgnoreCase(ownerUid2)); + assertTrue(updatedAccount2.getLoginName().orElse("").equalsIgnoreCase(loginname2)); + assertTrue(updatedAccount2.getSignature().equalsIgnoreCase(ownerUid2)); // account signature should now be addr + + // RAMAN TODO: CT-4284 +// OsAccountRealm realm2 = caseDB.getOsAccountRealmManager().getRealmByRealmId(updatedAccount2.getRealmId()); +// assertTrue(realm2.getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1)); +// assertTrue(realm2.getSignature().equalsIgnoreCase(realmSignature1)); + } + + }