Sleuth Kit Java Bindings (JNI)  4.11.0
Java bindings for using The Sleuth Kit
OsAccountManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2020-2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
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.Collections;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.NavigableSet;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.UUID;
35 import java.util.concurrent.ConcurrentSkipListSet;
36 import java.util.stream.Collectors;
44 import static org.sleuthkit.datamodel.WindowsAccountUtils.getWindowsSpecialSidName;
45 import static org.sleuthkit.datamodel.WindowsAccountUtils.isWindowsSpecialSid;
46 
51 public final class OsAccountManager {
52 
53  private final SleuthkitCase db;
54  private final Object osAcctInstancesCacheLock;
55  private final NavigableSet<OsAccountInstance> osAccountInstanceCache;
56 
64  db = skCase;
65  osAcctInstancesCacheLock = new Object();
66  osAccountInstanceCache = new ConcurrentSkipListSet<>();
67  }
68 
82  OsAccount newOsAccount(String uniqueAccountId, OsAccountRealm realm) throws TskCoreException {
83 
84  // ensure unique id is provided
85  if (Strings.isNullOrEmpty(uniqueAccountId)) {
86  throw new TskCoreException("Cannot create OS account with null uniqueId.");
87  }
88 
89  if (realm == null) {
90  throw new TskCoreException("Cannot create OS account without a realm.");
91  }
92 
94  try {
95 
96  // try to create account
97  try {
98  OsAccount account = newOsAccount(uniqueAccountId, null, realm, OsAccount.OsAccountStatus.UNKNOWN, trans);
99  trans.commit();
100  trans = null;
101  return account;
102  } catch (SQLException ex) {
103  // Close the transaction before moving on
104  trans.rollback();
105  trans = null;
106 
107  // Create may fail if an OsAccount already exists.
108  Optional<OsAccount> osAccount = this.getOsAccountByAddr(uniqueAccountId, realm);
109  if (osAccount.isPresent()) {
110  return osAccount.get();
111  }
112 
113  // create failed for some other reason, throw an exception
114  throw new TskCoreException(String.format("Error creating OsAccount with uniqueAccountId = %s in realm id = %d", uniqueAccountId, realm.getRealmId()), ex);
115  }
116  } finally {
117  if (trans != null) {
118  trans.rollback();
119  }
120  }
121  }
122 
147  public OsAccount newWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope) throws TskCoreException, NotUserSIDException {
148 
149  if (realmScope == null) {
150  throw new TskCoreException("RealmScope cannot be null. Use UNKNOWN if scope is not known.");
151  }
152  if (referringHost == null) {
153  throw new TskCoreException("A referring host is required to create an account.");
154  }
155 
156  // ensure at least one of the two is supplied - unique id or a login name
157  if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) {
158  throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
159  }
160  // Realm name is required if the sid is null.
161  if (StringUtils.isBlank(sid) && StringUtils.isBlank(realmName)) {
162  throw new TskCoreException("Realm name or SID is required to create a Windows account.");
163  }
164 
165  if (!StringUtils.isBlank(sid) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
166  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
167  }
168 
169  // get the realm for the account, and update it if it is missing addr or name.
170  Optional<OsAccountRealm> realmOptional;
171  try (CaseDbConnection connection = db.getConnection()) {
172  realmOptional = db.getOsAccountRealmManager().getAndUpdateWindowsRealm(sid, realmName, referringHost, connection);
173  }
174  OsAccountRealm realm;
175  if (realmOptional.isPresent()) {
176  realm = realmOptional.get();
177  } else {
178  // realm was not found, create it.
179  realm = db.getOsAccountRealmManager().newWindowsRealm(sid, realmName, referringHost, realmScope);
180  }
181 
182  return newWindowsOsAccount(sid, loginName, realm);
183  }
184 
202  public OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm) throws TskCoreException, NotUserSIDException {
203 
204  // ensure at least one of the two is supplied - unique id or a login name
205  if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) {
206  throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
207  }
208 
209  if (!StringUtils.isBlank(sid) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
210  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
211  }
212 
213  CaseDbTransaction trans = db.beginTransaction();
214  try {
215  // try to create account
216  try {
217  OsAccount account = newOsAccount(sid, loginName, realm, OsAccount.OsAccountStatus.UNKNOWN, trans);
218 
219  // If the SID indicates a special windows account, then set its full name.
220  if (!StringUtils.isBlank(sid) && isWindowsSpecialSid(sid)) {
221  String fullName = getWindowsSpecialSidName(sid);
222  if (StringUtils.isNotBlank(fullName)) {
223  OsAccountUpdateResult updateResult = updateStandardOsAccountAttributes(account, fullName, null, null, null, trans);
224  if (updateResult.getUpdatedAccount().isPresent()) {
225  account = updateResult.getUpdatedAccount().get();
226  }
227  }
228  }
229  trans.commit();
230  trans = null;
231  return account;
232  } catch (SQLException ex) {
233  // Rollback the transaction before proceeding
234  trans.rollback();
235  trans = null;
236 
237  // Create may fail if an OsAccount already exists.
238  Optional<OsAccount> osAccount;
239 
240  // First search for account by uniqueId
241  if (!Strings.isNullOrEmpty(sid)) {
242  osAccount = getOsAccountByAddr(sid, realm);
243  if (osAccount.isPresent()) {
244  return osAccount.get();
245  }
246  }
247 
248  // search by loginName
249  if (!Strings.isNullOrEmpty(loginName)) {
250  osAccount = getOsAccountByLoginName(loginName, realm);
251  if (osAccount.isPresent()) {
252  return osAccount.get();
253  }
254  }
255 
256  // create failed for some other reason, throw an exception
257  throw new TskCoreException(String.format("Error creating OsAccount with sid = %s, loginName = %s, realm = %s, referring host = %s",
258  (sid != null) ? sid : "Null",
259  (loginName != null) ? loginName : "Null",
260  (!realm.getRealmNames().isEmpty()) ? realm.getRealmNames().get(0) : "Null",
261  realm.getScopeHost().isPresent() ? realm.getScopeHost().get().getName() : "Null"), ex);
262 
263  }
264  } finally {
265  if (trans != null) {
266  trans.rollback();
267  }
268  }
269  }
270 
284  private OsAccount newOsAccount(String uniqueId, String loginName, OsAccountRealm realm, OsAccount.OsAccountStatus accountStatus, CaseDbTransaction trans) throws TskCoreException, SQLException {
285 
286  if (Objects.isNull(realm)) {
287  throw new TskCoreException("Cannot create an OS Account, realm is NULL.");
288  }
289 
290  String signature = getOsAccountSignature(uniqueId, loginName);
291  OsAccount account;
292 
293  CaseDbConnection connection = trans.getConnection();
294 
295  // first create a tsk_object for the OsAccount.
296  // RAMAN TODO: need to get the correct parent obj id.
297  // Create an Object Directory parent and used its id.
298  long parentObjId = 0;
299 
300  int objTypeId = TskData.ObjectType.OS_ACCOUNT.getObjectType();
301  long osAccountObjId = db.addObject(parentObjId, objTypeId, connection);
302 
303  String accountInsertSQL = "INSERT INTO tsk_os_accounts(os_account_obj_id, login_name, realm_id, addr, signature, status)"
304  + " VALUES (?, ?, ?, ?, ?, ?)"; // NON-NLS
305 
306  PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.NO_GENERATED_KEYS);
307  preparedStatement.clearParameters();
308 
309  preparedStatement.setLong(1, osAccountObjId);
310 
311  preparedStatement.setString(2, loginName);
312  preparedStatement.setLong(3, realm.getRealmId());
313 
314  preparedStatement.setString(4, uniqueId);
315  preparedStatement.setString(5, signature);
316  preparedStatement.setInt(6, accountStatus.getId());
317 
318  connection.executeUpdate(preparedStatement);
319 
320  account = new OsAccount(db, osAccountObjId, realm.getRealmId(), loginName, uniqueId, signature,
321  null, null, null, accountStatus, OsAccount.OsAccountDbStatus.ACTIVE);
322 
323  trans.registerAddedOsAccount(account);
324  return account;
325  }
326 
338  private Optional<OsAccount> getOsAccountByAddr(String addr, Host host) throws TskCoreException {
339 
340  try (CaseDbConnection connection = db.getConnection()) {
341  return getOsAccountByAddr(addr, host, connection);
342  }
343  }
344 
357  private Optional<OsAccount> getOsAccountByAddr(String uniqueId, Host host, CaseDbConnection connection) throws TskCoreException {
358 
359  String whereHostClause = (host == null)
360  ? " 1 = 1 "
361  : " ( realms.scope_host_id = " + host.getHostId() + " OR realms.scope_host_id IS NULL) ";
362 
363  String queryString = "SELECT accounts.os_account_obj_id as os_account_obj_id, accounts.login_name, accounts.full_name, "
364  + " accounts.realm_id, accounts.addr, accounts.signature, "
365  + " accounts.type, accounts.status, accounts.admin, accounts.created_date, accounts.db_status, "
366  + " realms.realm_name as realm_name, realms.realm_addr as realm_addr, realms.realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status as realm_db_status "
367  + " FROM tsk_os_accounts as accounts"
368  + " LEFT JOIN tsk_os_account_realms as realms"
369  + " ON accounts.realm_id = realms.id"
370  + " WHERE " + whereHostClause
371  + " AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
372  + " AND LOWER(accounts.addr) = LOWER('" + uniqueId + "')";
373 
375  try (Statement s = connection.createStatement();
376  ResultSet rs = connection.executeQuery(s, queryString)) {
377 
378  if (!rs.next()) {
379  return Optional.empty(); // no match found
380  } else {
381  return Optional.of(osAccountFromResultSet(rs));
382  }
383  } catch (SQLException ex) {
384  throw new TskCoreException(String.format("Error getting OS account for unique id = %s and host = %s", uniqueId, (host != null ? host.getName() : "null")), ex);
385  } finally {
387  }
388  }
389 
401  Optional<OsAccount> getOsAccountByAddr(String uniqueId, OsAccountRealm realm) throws TskCoreException {
402 
403  String queryString = "SELECT * FROM tsk_os_accounts"
404  + " WHERE LOWER(addr) = LOWER('" + uniqueId + "')"
405  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
406  + " AND realm_id = " + realm.getRealmId();
407 
409  try (CaseDbConnection connection = this.db.getConnection();
410  Statement s = connection.createStatement();
411  ResultSet rs = connection.executeQuery(s, queryString)) {
412 
413  if (!rs.next()) {
414  return Optional.empty(); // no match found
415  } else {
416  return Optional.of(osAccountFromResultSet(rs));
417  }
418  } catch (SQLException ex) {
419  throw new TskCoreException(String.format("Error getting OS account for realm = %s and uniqueId = %s.", (realm != null) ? realm.getSignature() : "NULL", uniqueId), ex);
420  } finally {
422  }
423  }
424 
436  Optional<OsAccount> getOsAccountByLoginName(String loginName, OsAccountRealm realm) throws TskCoreException {
437 
438  String queryString = "SELECT * FROM tsk_os_accounts"
439  + " WHERE LOWER(login_name) = LOWER('" + loginName + "')"
440  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
441  + " AND realm_id = " + realm.getRealmId();
442 
444  try (CaseDbConnection connection = this.db.getConnection();
445  Statement s = connection.createStatement();
446  ResultSet rs = connection.executeQuery(s, queryString)) {
447 
448  if (!rs.next()) {
449  return Optional.empty(); // no match found
450  } else {
451  return Optional.of(osAccountFromResultSet(rs));
452  }
453  } catch (SQLException ex) {
454  throw new TskCoreException(String.format("Error getting OS account for realm = %s and loginName = %s.", (realm != null) ? realm.getSignature() : "NULL", loginName), ex);
455  } finally {
457  }
458  }
459 
469  public OsAccount getOsAccountByObjectId(long osAccountObjId) throws TskCoreException {
470 
471  try (CaseDbConnection connection = this.db.getConnection()) {
472  return getOsAccountByObjectId(osAccountObjId, connection);
473  }
474  }
475 
486  OsAccount getOsAccountByObjectId(long osAccountObjId, CaseDbConnection connection) throws TskCoreException {
487 
488  String queryString = "SELECT * FROM tsk_os_accounts"
489  + " WHERE os_account_obj_id = " + osAccountObjId;
490 
492  try (Statement s = connection.createStatement();
493  ResultSet rs = connection.executeQuery(s, queryString)) {
494 
495  if (!rs.next()) {
496  throw new TskCoreException(String.format("No account found with obj id = %d ", osAccountObjId));
497  } else {
498  return osAccountFromResultSet(rs);
499  }
500  } catch (SQLException ex) {
501  throw new TskCoreException(String.format("Error getting account with obj id = %d ", osAccountObjId), ex);
502  } finally {
504  }
505  }
506 
531  public OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType) throws TskCoreException {
532  if (osAccount == null) {
533  throw new TskCoreException("Cannot create account instance with null account.");
534  }
535  if (dataSource == null) {
536  throw new TskCoreException("Cannot create account instance with null data source.");
537  }
538 
539  /*
540  * Check the cache of OS account instances for an existing instance for
541  * this OS account and data source. Note that the account instance
542  * created here has a bogus instance ID. This is possible since the
543  * instance ID is not considered in the equals() and hashCode() methods
544  * of this class.
545  */
546  OsAccountInstance bogus = new OsAccountInstance(db, 0, osAccount.getId(), dataSource.getId(), instanceType);
547  synchronized (osAcctInstancesCacheLock) {
548  if (osAccountInstanceCache.contains(bogus)) {
549  // since we checked for contains(bogus), floor(bogus) should return the exact match and not a less than match.
550  return osAccountInstanceCache.floor(bogus);
551  }
552  }
553 
554  try (CaseDbConnection connection = this.db.getConnection()) {
555  return newOsAccountInstance(osAccount.getId(), dataSource.getId(), instanceType, connection);
556  }
557  }
558 
574  OsAccountInstance newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType, CaseDbConnection connection) throws TskCoreException {
575  /*
576  * Check the cache of OS account instances for an existing instance for
577  * this OS account and data source. Note that the account instance
578  * created here has a bogus instance ID. This is possible since the
579  * instance ID is not considered in the equals() and hashCode() methods
580  * of this class.
581  */
582  OsAccountInstance bogus = new OsAccountInstance(db, 0, osAccountId, dataSourceObjId, instanceType);
583  synchronized (osAcctInstancesCacheLock) {
584  if (osAccountInstanceCache.contains(bogus)) {
585  // since we checked for contains(bogus), floor(bogus) should return the exact match and not a less than match.
586  return osAccountInstanceCache.floor(bogus);
587  }
588  }
589 
590  /*
591  * Create the OS account instance.
592  */
594  try {
595  String accountInsertSQL = db.getInsertOrIgnoreSQL("INTO tsk_os_account_instances(os_account_obj_id, data_source_obj_id, instance_type)"
596  + " VALUES (?, ?, ?)"); // NON-NLS
597  PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.RETURN_GENERATED_KEYS);
598  preparedStatement.clearParameters();
599  preparedStatement.setLong(1, osAccountId);
600  preparedStatement.setLong(2, dataSourceObjId);
601  preparedStatement.setInt(3, instanceType.getId());
602  connection.executeUpdate(preparedStatement);
603  try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
604  if (resultSet.next()) {
605  OsAccountInstance accountInstance = new OsAccountInstance(db, resultSet.getLong(1), osAccountId, dataSourceObjId, instanceType);
606  synchronized (osAcctInstancesCacheLock) {
607  osAccountInstanceCache.add(accountInstance);
608  }
609  /*
610  * There is a potential issue here. The cache of OS account
611  * instances is an optimization and was not intended to be
612  * used as an authoritative indicator of whether or not a
613  * particular OS account instance was already added to the
614  * case. In fact, the entire cache is flushed during merge
615  * operations. But regardless, there is a check-then-act
616  * race condition for multi-user cases, with or without the
617  * cache. And although the case database schema and the SQL
618  * returned by getInsertOrIgnoreSQL() seamlessly prevents
619  * duplicates in the case database, a valid row ID is
620  * returned here even if the INSERT is not done. So the
621  * bottom line is that a redundant event may be published
622  * from time to time.
623  */
624  db.fireTSKEvent(new TskEvent.OsAcctInstancesAddedTskEvent(Collections.singletonList(accountInstance)));
625 
626  return accountInstance;
627  } else {
628  throw new TskCoreException(String.format("Could not get autogen key after row insert for OS account instance. OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId));
629  }
630  }
631  } catch (SQLException ex) {
632  throw new TskCoreException(String.format("Error adding OS account instance for OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId), ex);
633  } finally {
635  }
636  }
637 
647  public List<OsAccount> getOsAccounts(Host host) throws TskCoreException {
648 
649  String queryString = "SELECT * FROM tsk_os_accounts as accounts "
650  + " JOIN tsk_os_account_instances as instances "
651  + " ON instances.os_account_obj_id = accounts.os_account_obj_id "
652  + " JOIN data_source_info as datasources "
653  + " ON datasources.obj_id = instances.data_source_obj_id "
654  + " WHERE datasources.host_id = " + host.getHostId()
655  + " AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId();
656 
658  try (CaseDbConnection connection = this.db.getConnection();
659  Statement s = connection.createStatement();
660  ResultSet rs = connection.executeQuery(s, queryString)) {
661 
662  List<OsAccount> accounts = new ArrayList<>();
663  while (rs.next()) {
664  accounts.add(osAccountFromResultSet(rs));
665  }
666  return accounts;
667  } catch (SQLException ex) {
668  throw new TskCoreException(String.format("Error getting OS accounts for host id = %d", host.getHostId()), ex);
669  } finally {
671  }
672  }
673 
686  void mergeOsAccountsForRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans) throws TskCoreException {
687  List<OsAccount> destinationAccounts = getOsAccounts(destRealm, trans.getConnection());
688  List<OsAccount> sourceAccounts = getOsAccounts(sourceRealm, trans.getConnection());
689 
690  for (OsAccount sourceAccount : sourceAccounts) {
691 
692  // First a check for the case where the source account has both the login name and unique ID set and
693  // we have separate matches in the destination account for both. If we find this case, we need to first merge
694  // the two accounts in the destination realm. This will ensure that all source accounts match at most one
695  // destination account.
696  // Note that we only merge accounts based on login name if the unique ID is empty.
697  if (sourceAccount.getAddr().isPresent() && sourceAccount.getLoginName().isPresent()) {
698  List<OsAccount> duplicateDestAccounts = destinationAccounts.stream()
699  .filter(p -> p.getAddr().equals(sourceAccount.getAddr())
700  || (p.getLoginName().equals(sourceAccount.getLoginName()) && (!p.getAddr().isPresent())))
701  .collect(Collectors.toList());
702  if (duplicateDestAccounts.size() > 1) {
703  OsAccount combinedDestAccount = duplicateDestAccounts.get(0);
704  duplicateDestAccounts.remove(combinedDestAccount);
705  for (OsAccount dupeDestAccount : duplicateDestAccounts) {
706  mergeOsAccounts(dupeDestAccount, combinedDestAccount, trans);
707  }
708  }
709  }
710 
711  // Look for matching destination account
712  OsAccount matchingDestAccount = null;
713 
714  // First look for matching unique id
715  if (sourceAccount.getAddr().isPresent()) {
716  List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
717  .filter(p -> p.getAddr().equals(sourceAccount.getAddr()))
718  .collect(Collectors.toList());
719  if (!matchingDestAccounts.isEmpty()) {
720  matchingDestAccount = matchingDestAccounts.get(0);
721  }
722  }
723 
724  // If a match wasn't found yet, look for a matching login name.
725  // We will merge only if:
726  // - We didn't already find a unique ID match
727  // - The source account has no unique ID OR the destination account has no unique ID
728  if (matchingDestAccount == null && sourceAccount.getLoginName().isPresent()) {
729  List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
730  .filter(p -> (p.getLoginName().equals(sourceAccount.getLoginName())
731  && ((!sourceAccount.getAddr().isPresent()) || (!p.getAddr().isPresent()))))
732  .collect(Collectors.toList());
733  if (!matchingDestAccounts.isEmpty()) {
734  matchingDestAccount = matchingDestAccounts.get(0);
735  }
736  }
737 
738  // If we found a match, merge the accounts. Otherwise simply update the realm id
739  if (matchingDestAccount != null) {
740  mergeOsAccounts(sourceAccount, matchingDestAccount, trans);
741  } else {
742  String query = "UPDATE tsk_os_accounts SET realm_id = " + destRealm.getRealmId() + " WHERE os_account_obj_id = " + sourceAccount.getId();
743  try (Statement s = trans.getConnection().createStatement()) {
744  s.executeUpdate(query);
745  } catch (SQLException ex) {
746  throw new TskCoreException("Error executing SQL update: " + query, ex);
747  }
748  trans.registerChangedOsAccount(sourceAccount);
749  }
750  }
751  }
752 
767  private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) throws TskCoreException {
768 
769  String query = "";
770  try (Statement s = trans.getConnection().createStatement()) {
771 
772  // Update all references
773  query = makeOsAccountUpdateQuery("tsk_os_account_attributes", sourceAccount, destAccount);
774  s.executeUpdate(query);
775 
776  // tsk_os_account_instances has a unique constraint on os_account_obj_id, data_source_obj_id, host_id,
777  // so delete any rows that would be duplicates.
778  query = "DELETE FROM tsk_os_account_instances "
779  + "WHERE id IN ( "
780  + "SELECT "
781  + " sourceAccountInstance.id "
782  + "FROM "
783  + " tsk_os_account_instances destAccountInstance "
784  + "INNER JOIN tsk_os_account_instances sourceAccountInstance ON destAccountInstance.data_source_obj_id = sourceAccountInstance.data_source_obj_id "
785  + "WHERE destAccountInstance.os_account_obj_id = " + destAccount.getId()
786  + " AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId() + " )";
787  s.executeUpdate(query);
788 
789  query = makeOsAccountUpdateQuery("tsk_os_account_instances", sourceAccount, destAccount);
790  s.executeUpdate(query);
791  synchronized (osAcctInstancesCacheLock) {
792  osAccountInstanceCache.clear();
793  }
794 
795  query = makeOsAccountUpdateQuery("tsk_files", sourceAccount, destAccount);
796  s.executeUpdate(query);
797 
798  query = makeOsAccountUpdateQuery("tsk_data_artifacts", sourceAccount, destAccount);
799  s.executeUpdate(query);
800 
801  // Update the source account. Make a dummy signature to prevent problems with the unique constraint.
802  String mergedSignature = makeMergedOsAccountSignature();
803  query = "UPDATE tsk_os_accounts SET merged_into = " + destAccount.getId()
804  + ", db_status = " + OsAccount.OsAccountDbStatus.MERGED.getId()
805  + ", signature = '" + mergedSignature + "' "
806  + " WHERE os_account_obj_id = " + sourceAccount.getId();
807 
808  s.executeUpdate(query);
809  trans.registerDeletedOsAccount(sourceAccount.getId());
810 
811  // Merge and update the destination account. Note that this must be done after updating
812  // the source account to prevent conflicts when merging two accounts in the
813  // same realm.
814  mergeOsAccountObjectsAndUpdateDestAccount(sourceAccount, destAccount, trans);
815  } catch (SQLException ex) {
816  throw new TskCoreException("Error executing SQL update: " + query, ex);
817  }
818  }
819 
825  private String makeMergedOsAccountSignature() {
826  return "MERGED " + UUID.randomUUID().toString();
827  }
828 
838  private String makeOsAccountUpdateQuery(String tableName, OsAccount sourceAccount, OsAccount destAccount) {
839  return "UPDATE " + tableName + " SET os_account_obj_id = " + destAccount.getId() + " WHERE os_account_obj_id = " + sourceAccount.getId();
840  }
841 
853  private OsAccount mergeOsAccountObjectsAndUpdateDestAccount(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) throws TskCoreException {
854 
855  OsAccount mergedDestAccount = destAccount;
856 
857  String destLoginName = null;
858  String destAddr = null;
859 
860  // Copy any fields that aren't set in the destination to the value from the source account.
861  if (!destAccount.getLoginName().isPresent() && sourceAccount.getLoginName().isPresent()) {
862  destLoginName = sourceAccount.getLoginName().get();
863  }
864 
865  if (!destAccount.getAddr().isPresent() && sourceAccount.getAddr().isPresent()) {
866  destAddr = sourceAccount.getAddr().get();
867  }
868 
869  // update the dest account core
870  OsAccountUpdateResult updateStatus = this.updateOsAccountCore(destAccount, destAddr, destLoginName, trans);
871 
872  if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
873  mergedDestAccount = updateStatus.getUpdatedAccount().get();
874  }
875 
876  String destFullName = null;
877  Long destCreationTime = null;
878  if (!destAccount.getFullName().isPresent() && sourceAccount.getFullName().isPresent()) {
879  destFullName = sourceAccount.getFullName().get();
880  }
881 
882  if (!destAccount.getCreationTime().isPresent() && sourceAccount.getCreationTime().isPresent()) {
883  destCreationTime = sourceAccount.getCreationTime().get();
884  }
885 
886  // update the dest account properties
887  updateStatus = this.updateStandardOsAccountAttributes(destAccount, destFullName, null, null, destCreationTime, trans);
888 
889  if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
890  mergedDestAccount = updateStatus.getUpdatedAccount().get();
891  }
892 
893  return mergedDestAccount;
894  }
895 
906  private List<OsAccount> getOsAccounts(OsAccountRealm realm, CaseDbConnection connection) throws TskCoreException {
907  String queryString = "SELECT * FROM tsk_os_accounts"
908  + " WHERE realm_id = " + realm.getRealmId()
909  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
910  + " ORDER BY os_account_obj_id";
911 
912  try (Statement s = connection.createStatement();
913  ResultSet rs = connection.executeQuery(s, queryString)) {
914 
915  List<OsAccount> accounts = new ArrayList<>();
916  while (rs.next()) {
917  accounts.add(osAccountFromResultSet(rs));
918  }
919  return accounts;
920  } catch (SQLException ex) {
921  throw new TskCoreException(String.format("Error getting OS accounts for realm id = %d", realm.getRealmId()), ex);
922  }
923  }
924 
932  public List<OsAccount> getOsAccounts() throws TskCoreException {
933  String queryString = "SELECT * FROM tsk_os_accounts"
934  + " WHERE db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId();
935 
937  try (CaseDbConnection connection = this.db.getConnection();
938  Statement s = connection.createStatement();
939  ResultSet rs = connection.executeQuery(s, queryString)) {
940 
941  List<OsAccount> accounts = new ArrayList<>();
942  while (rs.next()) {
943  accounts.add(osAccountFromResultSet(rs));
944  }
945  return accounts;
946  } catch (SQLException ex) {
947  throw new TskCoreException(String.format("Error getting OS accounts"), ex);
948  } finally {
950  }
951  }
952 
968  public Optional<OsAccount> getWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost) throws TskCoreException, NotUserSIDException {
969 
970  if (referringHost == null) {
971  throw new TskCoreException("A referring host is required to get an account.");
972  }
973 
974  // ensure at least one of the two is supplied - sid or a login name
975  if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) {
976  throw new TskCoreException("Cannot get an OS account with both SID and loginName as null.");
977  }
978 
979  // first get the realm for the given sid
980  Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getWindowsRealm(sid, realmName, referringHost);
981  if (!realm.isPresent()) {
982  return Optional.empty();
983  }
984 
985  // search by SID
986  if (!Strings.isNullOrEmpty(sid)) {
987  if (!WindowsAccountUtils.isWindowsUserSid(sid)) {
988  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
989  }
990 
991  Optional<OsAccount> account = this.getOsAccountByAddr(sid, realm.get());
992  if (account.isPresent()) {
993  return account;
994  }
995  }
996 
997  // search by login name
998  return this.getOsAccountByLoginName(loginName, realm.get());
999  }
1000 
1010  public void addExtendedOsAccountAttributes(OsAccount account, List<OsAccountAttribute> accountAttributes) throws TskCoreException {
1011 
1012  synchronized (account) { // synchronized to prevent multiple threads trying to add osAccount attributes concurrently to the same osAccount.
1014 
1015  try (CaseDbConnection connection = db.getConnection()) {
1016  for (OsAccountAttribute accountAttribute : accountAttributes) {
1017 
1018  String attributeInsertSQL = "INSERT INTO tsk_os_account_attributes(os_account_obj_id, host_id, source_obj_id, attribute_type_id, value_type, value_byte, value_text, value_int32, value_int64, value_double)"
1019  + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
1020 
1021  PreparedStatement preparedStatement = connection.getPreparedStatement(attributeInsertSQL, Statement.RETURN_GENERATED_KEYS);
1022  preparedStatement.clearParameters();
1023 
1024  preparedStatement.setLong(1, account.getId());
1025  if (accountAttribute.getHostId().isPresent()) {
1026  preparedStatement.setLong(2, accountAttribute.getHostId().get());
1027  } else {
1028  preparedStatement.setNull(2, java.sql.Types.NULL);
1029  }
1030  if (accountAttribute.getSourceObjectId().isPresent()) {
1031  preparedStatement.setLong(3, accountAttribute.getSourceObjectId().get());
1032  } else {
1033  preparedStatement.setNull(3, java.sql.Types.NULL);
1034  }
1035 
1036  preparedStatement.setLong(4, accountAttribute.getAttributeType().getTypeID());
1037  preparedStatement.setLong(5, accountAttribute.getAttributeType().getValueType().getType());
1038 
1039  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE) {
1040  preparedStatement.setBytes(6, accountAttribute.getValueBytes());
1041  } else {
1042  preparedStatement.setBytes(6, null);
1043  }
1044 
1045  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
1046  || accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1047  preparedStatement.setString(7, accountAttribute.getValueString());
1048  } else {
1049  preparedStatement.setString(7, null);
1050  }
1051  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER) {
1052  preparedStatement.setInt(8, accountAttribute.getValueInt());
1053  } else {
1054  preparedStatement.setNull(8, java.sql.Types.NULL);
1055  }
1056 
1057  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME
1058  || accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG) {
1059  preparedStatement.setLong(9, accountAttribute.getValueLong());
1060  } else {
1061  preparedStatement.setNull(9, java.sql.Types.NULL);
1062  }
1063 
1064  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE) {
1065  preparedStatement.setDouble(10, accountAttribute.getValueDouble());
1066  } else {
1067  preparedStatement.setNull(10, java.sql.Types.NULL);
1068  }
1069 
1070  connection.executeUpdate(preparedStatement);
1071  }
1072  } catch (SQLException ex) {
1073  throw new TskCoreException(String.format("Error adding OS Account attribute for account id = %d", account.getId()), ex);
1074  } finally {
1076  }
1077  // set the atrribute list in account to the most current list from the database
1078  List<OsAccountAttribute> currentAttribsList = getOsAccountAttributes(account);
1079  account.setAttributesInternal(currentAttribsList);
1080  }
1081  fireChangeEvent(account);
1082  }
1083 
1093  List<OsAccountAttribute> getOsAccountAttributes(OsAccount account) throws TskCoreException {
1094 
1095  String queryString = "SELECT attributes.os_account_obj_id as os_account_obj_id, attributes.host_id as host_id, attributes.source_obj_id as source_obj_id, "
1096  + " attributes.attribute_type_id as attribute_type_id, attributes.value_type as value_type, attributes.value_byte as value_byte, "
1097  + " attributes.value_text as value_text, attributes.value_int32 as value_int32, attributes.value_int64 as value_int64, attributes.value_double as value_double, "
1098  + " hosts.id, hosts.name as host_name, hosts.db_status as host_status "
1099  + " FROM tsk_os_account_attributes as attributes"
1100  + " LEFT JOIN tsk_hosts as hosts "
1101  + " ON attributes.host_id = hosts.id "
1102  + " WHERE os_account_obj_id = " + account.getId();
1103 
1105  try (CaseDbConnection connection = this.db.getConnection();
1106  Statement s = connection.createStatement();
1107  ResultSet rs = connection.executeQuery(s, queryString)) {
1108 
1109  List<OsAccountAttribute> attributes = new ArrayList<>();
1110  while (rs.next()) {
1111 
1112  Host host = null;
1113  long hostId = rs.getLong("host_id");
1114  if (!rs.wasNull()) {
1115  host = new Host(hostId, rs.getString("host_name"), Host.HostDbStatus.fromID(rs.getInt("host_status")));
1116  }
1117 
1118  Content sourceContent = null;
1119  long sourceObjId = rs.getLong("source_obj_id");
1120  if (!rs.wasNull()) {
1121  sourceContent = this.db.getContentById(sourceObjId);
1122  }
1123  BlackboardAttribute.Type attributeType = db.getAttributeType(rs.getInt("attribute_type_id"));
1124  OsAccountAttribute attribute = account.new OsAccountAttribute(attributeType, rs.getInt("value_int32"), rs.getLong("value_int64"),
1125  rs.getDouble("value_double"), rs.getString("value_text"), rs.getBytes("value_byte"),
1126  db, account, host, sourceContent);
1127 
1128  attributes.add(attribute);
1129  }
1130  return attributes;
1131  } catch (SQLException ex) {
1132  throw new TskCoreException(String.format("Error getting OS account attributes for account obj id = %d", account.getId()), ex);
1133  } finally {
1135  }
1136  }
1137 
1147  List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreException {
1148  String whereClause = "tsk_os_account_instances.os_account_obj_id = " + account.getId();
1149  return getOsAccountInstances(whereClause);
1150  }
1151 
1162  public List<OsAccountInstance> getOsAccountInstances(List<Long> instanceIDs) throws TskCoreException {
1163  String instanceIds = instanceIDs.stream().map(id -> id.toString()).collect(Collectors.joining(","));
1164  String whereClause = "tsk_os_account_instances.id IN (" + instanceIds + ")";
1165  return getOsAccountInstances(whereClause);
1166  }
1167 
1178  private List<OsAccountInstance> getOsAccountInstances(String whereClause) throws TskCoreException {
1179  List<OsAccountInstance> osAcctInstances = new ArrayList<>();
1180  String querySQL = "SELECT * FROM tsk_os_account_instances WHERE " + whereClause;
1182  try (CaseDbConnection connection = db.getConnection();
1183  PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
1184  ResultSet results = connection.executeQuery(preparedStatement)) {
1185  while (results.next()) {
1186  long instanceId = results.getLong("id");
1187  long osAccountObjID = results.getLong("os_account_obj_id");
1188  long dataSourceObjId = results.getLong("data_source_obj_id");
1189  int instanceType = results.getInt("instance_type");
1190  osAcctInstances.add(new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType)));
1191  }
1192  } catch (SQLException ex) {
1193  throw new TskCoreException("Failed to get OsAccountInstances (SQL = " + querySQL + ")", ex);
1194  } finally {
1196  }
1197  return osAcctInstances;
1198  }
1199 
1216  public OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime) throws TskCoreException {
1217 
1218  CaseDbTransaction trans = db.beginTransaction();
1219  try {
1220  OsAccountUpdateResult updateStatus = updateStandardOsAccountAttributes(osAccount, fullName, accountType, accountStatus, creationTime, trans);
1221 
1222  trans.commit();
1223  trans = null;
1224 
1225  return updateStatus;
1226  } finally {
1227  if (trans != null) {
1228  trans.rollback();
1229  }
1230  }
1231  }
1232 
1250  OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime, CaseDbTransaction trans) throws TskCoreException {
1251 
1252  OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1253 
1254  try {
1255  CaseDbConnection connection = trans.getConnection();
1256 
1257  if (!StringUtils.isBlank(fullName)) {
1258  updateAccountColumn(osAccount.getId(), "full_name", fullName, connection);
1259  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1260  }
1261 
1262  if (Objects.nonNull(accountType)) {
1263  updateAccountColumn(osAccount.getId(), "type", accountType, connection);
1264  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1265  }
1266 
1267  if (Objects.nonNull(accountStatus)) {
1268  updateAccountColumn(osAccount.getId(), "status", accountStatus, connection);
1269  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1270  }
1271 
1272  if (Objects.nonNull(creationTime)) {
1273  updateAccountColumn(osAccount.getId(), "created_date", creationTime, connection);
1274  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1275  }
1276 
1277  // if nothing has been changed, return
1278  if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1279  return new OsAccountUpdateResult(updateStatusCode, null);
1280  }
1281 
1282  // get the updated account from database
1283  OsAccount updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection);
1284 
1285  // register the updated account with the transaction to fire off an event
1286  trans.registerChangedOsAccount(updatedAccount);
1287 
1288  return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
1289 
1290  } catch (SQLException ex) {
1291  throw new TskCoreException(String.format("Error updating account with addr = %s, account id = %d", osAccount.getAddr().orElse("Unknown"), osAccount.getId()), ex);
1292  }
1293  }
1294 
1308  private <T> void updateAccountColumn(long accountObjId, String colName, T colValue, CaseDbConnection connection) throws SQLException, TskCoreException {
1309 
1310  String updateSQL = "UPDATE tsk_os_accounts "
1311  + " SET " + colName + " = ? "
1312  + " WHERE os_account_obj_id = ?";
1313 
1315  try {
1316  PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1317  preparedStatement.clearParameters();
1318 
1319  if (Objects.isNull(colValue)) {
1320  preparedStatement.setNull(1, Types.NULL); // handle null value
1321  } else {
1322  if (colValue instanceof String) {
1323  preparedStatement.setString(1, (String) colValue);
1324  } else if (colValue instanceof Long) {
1325  preparedStatement.setLong(1, (Long) colValue);
1326  } else if (colValue instanceof Integer) {
1327  preparedStatement.setInt(1, (Integer) colValue);
1328  } else {
1329  throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId));
1330  }
1331  }
1332 
1333  preparedStatement.setLong(2, accountObjId);
1334 
1335  connection.executeUpdate(preparedStatement);
1336  } finally {
1338  }
1339  }
1340 
1351  private void updateAccountSignature(long accountObjId, String signature, CaseDbConnection connection) throws SQLException {
1352 
1353  String updateSQL = "UPDATE tsk_os_accounts SET "
1354  + " signature = "
1355  + " CASE WHEN db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId() + " THEN ? ELSE signature END "
1356  + " WHERE os_account_obj_id = ?"; // 8
1357 
1358  PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1359  preparedStatement.clearParameters();
1360 
1361  preparedStatement.setString(1, signature);
1362  preparedStatement.setLong(2, accountObjId);
1363 
1364  connection.executeUpdate(preparedStatement);
1365  }
1366 
1387  public OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost) throws TskCoreException, NotUserSIDException {
1388  CaseDbTransaction trans = db.beginTransaction();
1389  try {
1390  OsAccountUpdateResult updateStatus = this.updateCoreWindowsOsAccountAttributes(osAccount, accountSid, loginName, realmName, referringHost, trans);
1391 
1392  trans.commit();
1393  trans = null;
1394  return updateStatus;
1395  } finally {
1396  if (trans != null) {
1397  trans.rollback();
1398  }
1399  }
1400  }
1401 
1421  private OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost, CaseDbTransaction trans) throws TskCoreException, NotUserSIDException {
1422 
1423  // first get and update the realm - if we have the info to find the realm
1424  if (!StringUtils.isBlank(accountSid) || !StringUtils.isBlank(realmName)) {
1425  db.getOsAccountRealmManager().getAndUpdateWindowsRealm(accountSid, realmName, referringHost, trans.getConnection());
1426  }
1427 
1428  // now update the account core data
1429  OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, accountSid, loginName, trans);
1430 
1431  return updateStatus;
1432  }
1433 
1456  private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String address, String loginName, CaseDbTransaction trans) throws TskCoreException {
1457 
1458  OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1459  OsAccount updatedAccount;
1460 
1461  try {
1462  CaseDbConnection connection = trans.getConnection();
1463 
1464  // if a new addr is provided and the account already has an address, and they are not the same, throw an exception
1465  if (!StringUtils.isBlank(address) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(""))) {
1466  throw new TskCoreException(String.format("Account (%d) already has an address (%s), address cannot be updated.", osAccount.getId(), osAccount.getAddr().orElse("NULL")));
1467  }
1468 
1469  if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address)) {
1470  updateAccountColumn(osAccount.getId(), "addr", address, connection);
1471  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1472  }
1473 
1474  if (StringUtils.isBlank(osAccount.getLoginName().orElse(null)) && !StringUtils.isBlank(loginName)) {
1475  updateAccountColumn(osAccount.getId(), "login_name", loginName, connection);
1476  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1477  }
1478 
1479  // if nothing is changed, return
1480  if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1481  return new OsAccountUpdateResult(updateStatusCode, osAccount);
1482  }
1483 
1484  // update signature if needed, based on the most current addr/loginName
1485  OsAccount currAccount = getOsAccountByObjectId(osAccount.getId(), connection);
1486  String newAddress = currAccount.getAddr().orElse(null);
1487  String newLoginName = currAccount.getLoginName().orElse(null);
1488 
1489  String newSignature = getOsAccountSignature(newAddress, newLoginName);
1490  updateAccountSignature(osAccount.getId(), newSignature, connection);
1491 
1492  // get the updated account from database
1493  updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection);
1494 
1495  // register the updated account with the transaction to fire off an event
1496  trans.registerChangedOsAccount(updatedAccount);
1497 
1498  return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
1499 
1500  } catch (SQLException ex) {
1501  throw new TskCoreException(String.format("Error updating account with unique id = %s, account id = %d", osAccount.getAddr().orElse("Unknown"), osAccount.getId()), ex);
1502  }
1503  }
1504 
1514  public List<Host> getHosts(OsAccount account) throws TskCoreException {
1515  List<Host> hostList = new ArrayList<>();
1516 
1517  String query = "SELECT tsk_hosts.id AS hostId, name, db_status FROM tsk_hosts "
1518  + " JOIN data_source_info ON tsk_hosts.id = data_source_info.host_id"
1519  + " JOIN tsk_os_account_instances ON data_source_info.obj_id = tsk_os_account_instances.data_source_obj_id"
1520  + " WHERE os_account_obj_id = " + account.getId();
1521 
1523  try (CaseDbConnection connection = db.getConnection();
1524  Statement s = connection.createStatement();
1525  ResultSet rs = connection.executeQuery(s, query)) {
1526 
1527  while (rs.next()) {
1528  hostList.add(new Host(rs.getLong("hostId"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
1529  }
1530 
1531  } catch (SQLException ex) {
1532  throw new TskCoreException(String.format("Failed to get host list for os account %d", account.getId()), ex);
1533  } finally {
1535  }
1536  return hostList;
1537  }
1538 
1550  private OsAccount osAccountFromResultSet(ResultSet rs) throws SQLException {
1551 
1552  OsAccountType accountType = null;
1553  int typeId = rs.getInt("type");
1554  if (!rs.wasNull()) {
1555  accountType = OsAccount.OsAccountType.fromID(typeId);
1556  }
1557 
1558  Long creationTime = rs.getLong("created_date"); // getLong returns 0 if value is null
1559  if (rs.wasNull()) {
1560  creationTime = null;
1561  }
1562 
1563  return new OsAccount(db, rs.getLong("os_account_obj_id"), rs.getLong("realm_id"), rs.getString("login_name"), rs.getString("addr"),
1564  rs.getString("signature"), rs.getString("full_name"), creationTime, accountType, OsAccount.OsAccountStatus.fromID(rs.getInt("status")),
1565  OsAccount.OsAccountDbStatus.fromID(rs.getInt("db_status")));
1566 
1567  }
1568 
1575  private void fireChangeEvent(OsAccount account) {
1576  db.fireTSKEvent(new OsAccountsUpdatedTskEvent(Collections.singletonList(account)));
1577  }
1578 
1593  static String getOsAccountSignature(String uniqueId, String loginName) throws TskCoreException {
1594  // Create a signature.
1595  String signature;
1596  if (Strings.isNullOrEmpty(uniqueId) == false) {
1597  signature = uniqueId;
1598  } else if (Strings.isNullOrEmpty(loginName) == false) {
1599  signature = loginName;
1600  } else {
1601  throw new TskCoreException("OS Account must have either a uniqueID or a login name.");
1602  }
1603  return signature;
1604  }
1605 
1610  public static class NotUserSIDException extends TskException {
1611 
1612  private static final long serialVersionUID = 1L;
1613 
1618  super("No error message available.");
1619  }
1620 
1626  public NotUserSIDException(String msg) {
1627  super(msg);
1628  }
1629 
1636  public NotUserSIDException(String msg, Exception ex) {
1637  super(msg, ex);
1638  }
1639  }
1640 
1645 
1648  MERGED
1649  }
1650 
1655  public final static class OsAccountUpdateResult {
1656 
1657  private final OsAccountUpdateStatus updateStatus;
1658  private final OsAccount updatedAccount;
1659 
1660  OsAccountUpdateResult(OsAccountUpdateStatus updateStatus, OsAccount updatedAccount) {
1661  this.updateStatus = updateStatus;
1662  this.updatedAccount = updatedAccount;
1663  }
1664 
1666  return updateStatus;
1667  }
1668 
1669  public Optional<OsAccount> getUpdatedAccount() {
1670  return Optional.ofNullable(updatedAccount);
1671  }
1672  }
1673 }
Optional< String > getAddr()
Definition: OsAccount.java:266
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
List< Host > getHosts(OsAccount account)
OsAccount getOsAccountByObjectId(long osAccountObjId)
static OsAccountType fromID(int typeId)
Definition: OsAccount.java:193
Optional< OsAccount > getWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost)
OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime)
OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm)
List< OsAccount > getOsAccounts(Host host)
OsAccountRealmManager getOsAccountRealmManager()
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)
List< OsAccountInstance > getOsAccountInstances(List< Long > instanceIDs)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
Optional< String > getLoginName()
Definition: OsAccount.java:286
void addExtendedOsAccountAttributes(OsAccount account, List< OsAccountAttribute > accountAttributes)
OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost)
OsAccount newWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)

Copyright © 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.