Sleuth Kit Java Bindings (JNI)  4.12.1
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-2022 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 com.google.common.annotations.Beta;
23 import org.apache.commons.lang3.StringUtils;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.sql.Statement;
28 import java.sql.Types;
29 import java.util.Collections;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.NavigableMap;
33 import java.util.Objects;
34 import java.util.Optional;
35 import java.util.UUID;
36 import java.util.concurrent.ConcurrentSkipListMap;
37 import java.util.stream.Collectors;
47 import static org.sleuthkit.datamodel.WindowsAccountUtils.isWindowsWellKnownSid;
48 import static org.sleuthkit.datamodel.WindowsAccountUtils.getWindowsWellKnownSidFullName;
49 
54 public final class OsAccountManager {
55 
56  private final SleuthkitCase db;
57  private final Object osAcctInstancesCacheLock;
58  private final NavigableMap<OsAccountInstanceKey, OsAccountInstance> osAccountInstanceCache;
59 
67  db = skCase;
68  osAcctInstancesCacheLock = new Object();
69  osAccountInstanceCache = new ConcurrentSkipListMap<>();
70  }
71 
85  OsAccount newOsAccount(String uniqueAccountId, OsAccountRealm realm) throws TskCoreException {
86 
87  // ensure unique id is provided
88  if (Strings.isNullOrEmpty(uniqueAccountId)) {
89  throw new TskCoreException("Cannot create OS account with null uniqueId.");
90  }
91 
92  if (realm == null) {
93  throw new TskCoreException("Cannot create OS account without a realm.");
94  }
95 
97  try {
98 
99  // try to create account
100  try {
101  OsAccount account = newOsAccount(uniqueAccountId, null, realm, OsAccount.OsAccountStatus.UNKNOWN, trans);
102  trans.commit();
103  trans = null;
104  return account;
105  } catch (SQLException ex) {
106  // Close the transaction before moving on
107  trans.rollback();
108  trans = null;
109 
110  // Create may fail if an OsAccount already exists.
111  Optional<OsAccount> osAccount = this.getOsAccountByAddr(uniqueAccountId, realm);
112  if (osAccount.isPresent()) {
113  return osAccount.get();
114  }
115 
116  // create failed for some other reason, throw an exception
117  throw new TskCoreException(String.format("Error creating OsAccount with uniqueAccountId = %s in realm id = %d", uniqueAccountId, realm.getRealmId()), ex);
118  }
119  } finally {
120  if (trans != null) {
121  trans.rollback();
122  }
123  }
124  }
125 
150  public OsAccount newWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope) throws TskCoreException, NotUserSIDException {
151 
152  if (realmScope == null) {
153  throw new TskCoreException("RealmScope cannot be null. Use UNKNOWN if scope is not known.");
154  }
155  if (referringHost == null) {
156  throw new TskCoreException("A referring host is required to create an account.");
157  }
158 
159  // ensure at least one of the two is supplied - a non-null unique id or a login name
160  if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
161  && StringUtils.isBlank(loginName)) {
162  throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
163  }
164  // Realm name is required if the sid is null.
165  if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
166  && StringUtils.isBlank(realmName)) {
167  throw new TskCoreException("Realm name or SID is required to create a Windows account.");
168  }
169 
170  if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
171  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
172  }
173 
174  // If no SID is given and the given realm/login names is a well known account, get and use the well known SID
175  if (StringUtils.isBlank(sid)
176  && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName)
177  && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
178  sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
179  }
180 
181 
182  OsRealmUpdateResult realmUpdateResult;
183  Optional<OsAccountRealm> anotherRealmWithSameName = Optional.empty();
184  Optional<OsAccountRealm> anotherRealmWithSameAddr = Optional.empty();
185 
186  // get the realm for the account, and update it if it is missing addr or name.
187  OsAccountRealm realm = null;
188  try (CaseDbConnection connection = db.getConnection()) {
189  realmUpdateResult = db.getOsAccountRealmManager().getAndUpdateWindowsRealm(sid, realmName, referringHost, connection);
190 
191  Optional<OsAccountRealm> realmOptional = realmUpdateResult.getUpdatedRealm();
192  if (realmOptional.isPresent()) {
193  realm = realmOptional.get();
194 
195  if (realmUpdateResult.getUpdateStatus() == OsRealmUpdateStatus.UPDATED) {
196 
197  // Check if update of the realm triggers a merge with any other realm,
198  // say another realm with same name but no SID, or same SID but no name
199 
200  //1. Check if there is any OTHER realm with the same name, same host but no addr
201  anotherRealmWithSameName = db.getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, connection);
202 
203  // 2. Check if there is any OTHER realm with same addr and host, but NO name
204  anotherRealmWithSameAddr = db.getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, connection);
205  }
206  }
207  }
208 
209  if (null == realm) {
210  // realm was not found, create it.
211  realm = db.getOsAccountRealmManager().newWindowsRealm(sid, realmName, referringHost, realmScope);
212  } else if (realmUpdateResult.getUpdateStatus() == OsRealmUpdateStatus.UPDATED) {
213  // if the realm already existed and was updated, and there are other realms with same name or addr that should now be merged into the updated realm
214  if (anotherRealmWithSameName.isPresent() || anotherRealmWithSameAddr.isPresent()) {
215 
216  CaseDbTransaction trans = this.db.beginTransaction();
217  try {
218  if (anotherRealmWithSameName.isPresent()) {
219  db.getOsAccountRealmManager().mergeRealms(anotherRealmWithSameName.get(), realm, trans);
220  }
221  if (anotherRealmWithSameAddr.isPresent()) {
222  db.getOsAccountRealmManager().mergeRealms(anotherRealmWithSameAddr.get(), realm, trans);
223  }
224 
225  trans.commit();
226  } catch (TskCoreException ex) {
227  trans.rollback();
228  throw ex; // rethrow
229  }
230  }
231  }
232 
233 
234  return newWindowsOsAccount(sid, loginName, realm);
235  }
236 
254  public OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm) throws TskCoreException, NotUserSIDException {
255 
256  // ensure at least one of the two is supplied - a non-null unique id or a login name
257  if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
258  && StringUtils.isBlank(loginName)) {
259  throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
260  }
261 
262  if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
263  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
264  }
265 
266  // If the login name is well known, we use the well known english name.
267  String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
268 
269  CaseDbTransaction trans = db.beginTransaction();
270  try {
271  // try to create account
272  try {
273  String uniqueId = (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? sid : null;
274  if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
275  // if the SID is a Windows well known SID, then prefer to use the default well known login name
276  String wellKnownLoginName = WindowsAccountUtils.getWindowsWellKnownSidLoginName(sid);
277  if (!StringUtils.isEmpty(wellKnownLoginName)) {
278  resolvedLoginName = wellKnownLoginName;
279  }
280  }
281 
282  OsAccount account = newOsAccount(uniqueId, resolvedLoginName, realm, OsAccount.OsAccountStatus.UNKNOWN, trans);
283 
284  // If the SID indicates a special windows account, then set its full name.
285  if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
286  String fullName = getWindowsWellKnownSidFullName(sid);
287  if (StringUtils.isNotBlank(fullName)) {
288  OsAccountUpdateResult updateResult = updateStandardOsAccountAttributes(account, fullName, null, null, null, trans);
289  if (updateResult.getUpdatedAccount().isPresent()) {
290  account = updateResult.getUpdatedAccount().get();
291  }
292  }
293  }
294  trans.commit();
295  trans = null;
296  return account;
297  } catch (SQLException ex) {
298  // Rollback the transaction before proceeding
299  trans.rollback();
300  trans = null;
301 
302  // Create may fail if an OsAccount already exists.
303  Optional<OsAccount> osAccount;
304 
305  // First search for account by uniqueId
306  if (!Strings.isNullOrEmpty(sid)) {
307  osAccount = getOsAccountByAddr(sid, realm);
308  if (osAccount.isPresent()) {
309  return osAccount.get();
310  }
311  }
312 
313  // search by loginName
314  if (!Strings.isNullOrEmpty(resolvedLoginName)) {
315  osAccount = getOsAccountByLoginName(resolvedLoginName, realm);
316  if (osAccount.isPresent()) {
317  return osAccount.get();
318  }
319  }
320 
321  // create failed for some other reason, throw an exception
322  throw new TskCoreException(String.format("Error creating OsAccount with sid = %s, loginName = %s, realm = %s, referring host = %s",
323  (sid != null) ? sid : "Null",
324  (resolvedLoginName != null) ? resolvedLoginName : "Null",
325  (!realm.getRealmNames().isEmpty()) ? realm.getRealmNames().get(0) : "Null",
326  realm.getScopeHost().isPresent() ? realm.getScopeHost().get().getName() : "Null"), ex);
327 
328  }
329  } finally {
330  if (trans != null) {
331  trans.rollback();
332  }
333  }
334  }
335 
351  @Beta
352  public OsAccount newLocalLinuxOsAccount(String uid, String loginName, Host referringHost) throws TskCoreException {
353 
354  if (referringHost == null) {
355  throw new TskCoreException("A referring host is required to create a local OS account.");
356  }
357 
358  // Ensure at least one of the two is supplied - a non-null unique id or a login name
359  if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
360  throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
361  }
362 
363  OsAccountRealm localRealm = db.getOsAccountRealmManager().newLocalLinuxRealm(referringHost);
364 
365  CaseDbTransaction trans = db.beginTransaction();
366  try {
367 
368  // try to create account
369  try {
370  OsAccount account = newOsAccount(uid, loginName, localRealm, OsAccount.OsAccountStatus.UNKNOWN, trans);
371  trans.commit();
372  trans = null;
373  return account;
374  } catch (SQLException ex) {
375  // Rollback the transaction before proceeding
376  trans.rollback();
377  trans = null;
378 
379  // Create may fail if an OsAccount already exists.
380  Optional<OsAccount> osAccount;
381 
382  // First search for account by uniqueId
383  if (!Strings.isNullOrEmpty(uid)) {
384  osAccount = getOsAccountByAddr(uid, localRealm);
385  if (osAccount.isPresent()) {
386  return osAccount.get();
387  }
388  }
389 
390  // search by loginName
391  if (!Strings.isNullOrEmpty(loginName)) {
392  osAccount = getOsAccountByLoginName(loginName, localRealm);
393  if (osAccount.isPresent()) {
394  return osAccount.get();
395  }
396  }
397 
398  // create failed for some other reason, throw an exception
399  throw new TskCoreException(String.format("Error creating OsAccount with uid = %s, loginName = %s, realm = %s, referring host = %s",
400  (uid != null) ? uid : "Null",
401  (loginName != null) ? loginName : "Null",
402  (!localRealm.getRealmNames().isEmpty()) ? localRealm.getRealmNames().get(0) : "Null",
403  localRealm.getScopeHost().isPresent() ? localRealm.getScopeHost().get().getName() : "Null"), ex);
404 
405  }
406  } finally {
407  if (trans != null) {
408  trans.rollback();
409  }
410  }
411  }
412 
413 
427  private OsAccount newOsAccount(String uniqueId, String loginName, OsAccountRealm realm, OsAccount.OsAccountStatus accountStatus, CaseDbTransaction trans) throws TskCoreException, SQLException {
428 
429  if (Objects.isNull(realm)) {
430  throw new TskCoreException("Cannot create an OS Account, realm is NULL.");
431  }
432 
433  String signature = getOsAccountSignature(uniqueId, loginName);
434  OsAccount account;
435 
436  CaseDbConnection connection = trans.getConnection();
437 
438  // first create a tsk_object for the OsAccount.
439  // RAMAN TODO: need to get the correct parent obj id.
440  // Create an Object Directory parent and used its id.
441  long parentObjId = 0;
442 
443  int objTypeId = TskData.ObjectType.OS_ACCOUNT.getObjectType();
444  long osAccountObjId = db.addObject(parentObjId, objTypeId, connection);
445 
446  String accountInsertSQL = "INSERT INTO tsk_os_accounts(os_account_obj_id, login_name, realm_id, addr, signature, status)"
447  + " VALUES (?, ?, ?, ?, ?, ?)"; // NON-NLS
448 
449  PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.NO_GENERATED_KEYS);
450  preparedStatement.clearParameters();
451 
452  preparedStatement.setLong(1, osAccountObjId);
453 
454  preparedStatement.setString(2, loginName);
455  preparedStatement.setLong(3, realm.getRealmId());
456 
457  preparedStatement.setString(4, uniqueId);
458  preparedStatement.setString(5, signature);
459  preparedStatement.setInt(6, accountStatus.getId());
460 
461  connection.executeUpdate(preparedStatement);
462 
463  account = new OsAccount(db, osAccountObjId, realm.getRealmId(), loginName, uniqueId, signature,
464  null, null, null, accountStatus, OsAccount.OsAccountDbStatus.ACTIVE);
465 
466  trans.registerAddedOsAccount(account);
467  return account;
468  }
469 
481  private Optional<OsAccount> getOsAccountByAddr(String addr, Host host) throws TskCoreException {
482 
483  try (CaseDbConnection connection = db.getConnection()) {
484  return getOsAccountByAddr(addr, host, connection);
485  }
486  }
487 
500  private Optional<OsAccount> getOsAccountByAddr(String uniqueId, Host host, CaseDbConnection connection) throws TskCoreException {
501 
502  String whereHostClause = (host == null)
503  ? " 1 = 1 "
504  : " ( realms.scope_host_id = " + host.getHostId() + " OR realms.scope_host_id IS NULL) ";
505 
506  String queryString = "SELECT accounts.os_account_obj_id as os_account_obj_id, accounts.login_name, accounts.full_name, "
507  + " accounts.realm_id, accounts.addr, accounts.signature, "
508  + " accounts.type, accounts.status, accounts.created_date, accounts.db_status, "
509  + " 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 "
510  + " FROM tsk_os_accounts as accounts"
511  + " LEFT JOIN tsk_os_account_realms as realms"
512  + " ON accounts.realm_id = realms.id"
513  + " WHERE " + whereHostClause
514  + " AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
515  + " AND LOWER(accounts.addr) = LOWER('" + uniqueId + "')";
516 
518  try (Statement s = connection.createStatement();
519  ResultSet rs = connection.executeQuery(s, queryString)) {
520 
521  if (!rs.next()) {
522  return Optional.empty(); // no match found
523  } else {
524  return Optional.of(osAccountFromResultSet(rs));
525  }
526  } catch (SQLException ex) {
527  throw new TskCoreException(String.format("Error getting OS account for unique id = %s and host = %s", uniqueId, (host != null ? host.getName() : "null")), ex);
528  } finally {
530  }
531  }
532 
544  Optional<OsAccount> getOsAccountByAddr(String uniqueId, OsAccountRealm realm) throws TskCoreException {
545 
546  String queryString = "SELECT * FROM tsk_os_accounts"
547  + " WHERE LOWER(addr) = LOWER('" + uniqueId + "')"
548  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
549  + " AND realm_id = " + realm.getRealmId();
550 
552  try (CaseDbConnection connection = this.db.getConnection();
553  Statement s = connection.createStatement();
554  ResultSet rs = connection.executeQuery(s, queryString)) {
555 
556  if (!rs.next()) {
557  return Optional.empty(); // no match found
558  } else {
559  return Optional.of(osAccountFromResultSet(rs));
560  }
561  } catch (SQLException ex) {
562  throw new TskCoreException(String.format("Error getting OS account for realm = %s and uniqueId = %s.", (realm != null) ? realm.getSignature() : "NULL", uniqueId), ex);
563  } finally {
565  }
566  }
567 
579  Optional<OsAccount> getOsAccountByLoginName(String loginName, OsAccountRealm realm) throws TskCoreException {
580 
581  String queryString = "SELECT * FROM tsk_os_accounts"
582  + " WHERE LOWER(login_name) = LOWER('" + loginName + "')"
583  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
584  + " AND realm_id = " + realm.getRealmId();
585 
587  try (CaseDbConnection connection = this.db.getConnection();
588  Statement s = connection.createStatement();
589  ResultSet rs = connection.executeQuery(s, queryString)) {
590 
591  if (!rs.next()) {
592  return Optional.empty(); // no match found
593  } else {
594  return Optional.of(osAccountFromResultSet(rs));
595  }
596  } catch (SQLException ex) {
597  throw new TskCoreException(String.format("Error getting OS account for realm = %s and loginName = %s.", (realm != null) ? realm.getSignature() : "NULL", loginName), ex);
598  } finally {
600  }
601  }
602 
612  public OsAccount getOsAccountByObjectId(long osAccountObjId) throws TskCoreException {
613 
614  try (CaseDbConnection connection = this.db.getConnection()) {
615  return getOsAccountByObjectId(osAccountObjId, connection);
616  }
617  }
618 
629  OsAccount getOsAccountByObjectId(long osAccountObjId, CaseDbConnection connection) throws TskCoreException {
630 
631  String queryString = "SELECT * FROM tsk_os_accounts"
632  + " WHERE os_account_obj_id = " + osAccountObjId;
633 
635  try (Statement s = connection.createStatement();
636  ResultSet rs = connection.executeQuery(s, queryString)) {
637 
638  if (!rs.next()) {
639  throw new TskCoreException(String.format("No account found with obj id = %d ", osAccountObjId));
640  } else {
641  return osAccountFromResultSet(rs);
642  }
643  } catch (SQLException ex) {
644  throw new TskCoreException(String.format("Error getting account with obj id = %d ", osAccountObjId), ex);
645  } finally {
647  }
648  }
649 
674  public OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType) throws TskCoreException {
675  if (osAccount == null) {
676  throw new TskCoreException("Cannot create account instance with null account.");
677  }
678  if (dataSource == null) {
679  throw new TskCoreException("Cannot create account instance with null data source.");
680  }
681 
682  // check the cache first
683  Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccount.getId(), dataSource.getId(), instanceType);
684  if (existingInstance.isPresent()) {
685  return existingInstance.get();
686  }
687 
688  try (CaseDbConnection connection = this.db.getConnection()) {
689  return newOsAccountInstance(osAccount.getId(), dataSource.getId(), instanceType, connection);
690  }
691  }
692 
708  OsAccountInstance newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType, CaseDbConnection connection) throws TskCoreException {
709 
710  Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
711  if (existingInstance.isPresent()) {
712  return existingInstance.get();
713  }
714 
715  /*
716  * Create the OS account instance.
717  */
719  try {
720  String accountInsertSQL = db.getInsertOrIgnoreSQL("INTO tsk_os_account_instances(os_account_obj_id, data_source_obj_id, instance_type)"
721  + " VALUES (?, ?, ?)"); // NON-NLS
722  PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.RETURN_GENERATED_KEYS);
723  preparedStatement.clearParameters();
724  preparedStatement.setLong(1, osAccountId);
725  preparedStatement.setLong(2, dataSourceObjId);
726  preparedStatement.setInt(3, instanceType.getId());
727  connection.executeUpdate(preparedStatement);
728  try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
729  if (resultSet.next()) {
730  OsAccountInstance accountInstance = new OsAccountInstance(db, resultSet.getLong(1), osAccountId, dataSourceObjId, instanceType);
731  synchronized (osAcctInstancesCacheLock) {
732  OsAccountInstanceKey key = new OsAccountInstanceKey(osAccountId, dataSourceObjId);
733  // remove from cache any instances less significant (higher ordinal) than this instance
734  for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) {
735  if (accountInstance.getInstanceType().compareTo(type) < 0) {
736  osAccountInstanceCache.remove(key);
737  }
738  }
739  // add the new most significant instance to the cache
740  osAccountInstanceCache.put(key, accountInstance);
741  }
742  /*
743  * There is a potential issue here. The cache of OS account
744  * instances is an optimization and was not intended to be
745  * used as an authoritative indicator of whether or not a
746  * particular OS account instance was already added to the
747  * case. In fact, the entire cache is flushed during merge
748  * operations. But regardless, there is a check-then-act
749  * race condition for multi-user cases, with or without the
750  * cache. And although the case database schema and the SQL
751  * returned by getInsertOrIgnoreSQL() seamlessly prevents
752  * duplicates in the case database, a valid row ID is
753  * returned here even if the INSERT is not done. So the
754  * bottom line is that a redundant event may be published
755  * from time to time.
756  */
757  db.fireTSKEvent(new TskEvent.OsAcctInstancesAddedTskEvent(Collections.singletonList(accountInstance)));
758 
759  return accountInstance;
760  } else {
761  // there is the possibility that another thread may be adding the same os account instance at the same time
762  // the database may be updated prior to the cache being updated so this provides an extra opportunity to check
763  // the cache before throwing the exception
764  Optional<OsAccountInstance> existingInstanceRetry = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
765  if (existingInstanceRetry.isPresent()) {
766  return existingInstanceRetry.get();
767  }
768  }
769  }
770  } catch (SQLException ex) {
771  throw new TskCoreException(String.format("Error adding OS account instance for OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId), ex);
772  } finally {
774  }
775 
776  // It's possible that we weren't able to load the account instance because it
777  // is already in the database but the instance cache was cleared during an account merge.
778  // Try loading it here and re-adding to the cache.
779  String whereClause = "tsk_os_account_instances.os_account_obj_id = " + osAccountId
780  + "AND tsk_os_account_instances.data_source_obj_id = " + dataSourceObjId;
781  List<OsAccountInstance> instances = getOsAccountInstances(whereClause);
782  if (instances.isEmpty()) {
783  throw new TskCoreException(String.format("Could not get autogen key after row insert or reload instance for OS account instance. OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId));
784  }
785 
786  OsAccountInstance accountInstance = instances.get(0);
787  synchronized (osAcctInstancesCacheLock) {
788  OsAccountInstanceKey key = new OsAccountInstanceKey(osAccountId, dataSourceObjId);
789  // remove from cache any instances less significant (higher ordinal) than this instance
790  for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) {
791  if (accountInstance.getInstanceType().compareTo(type) < 0) {
792  osAccountInstanceCache.remove(key);
793  }
794  }
795  // add the most significant instance to the cache
796  osAccountInstanceCache.put(key, accountInstance);
797  }
798  return accountInstance;
799  }
800 
817  private Optional<OsAccountInstance> cachedAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType) {
818 
819  /*
820  * Check the cache of OS account instances for an existing instance for
821  * this OS account and data source. Note that the account instance
822  * created here has a bogus instance ID. This is possible since the
823  * instance ID is not considered in the equals() and hashCode() methods
824  * of this class.
825  */
826  synchronized (osAcctInstancesCacheLock) {
827  OsAccountInstanceKey key = new OsAccountInstanceKey(osAccountId, dataSourceObjId);
828  OsAccountInstance instance = osAccountInstanceCache.get(key);
829  if (instance != null) {
830  // if the new instance type same or less significant than the existing instance (i.e. same or higher ordinal value) it's a match.
831  if (instanceType.compareTo(instance.getInstanceType()) >= 0) {
832  return Optional.of(instance);
833  }
834  }
835  return Optional.empty();
836  }
837  }
838 
848  public List<OsAccount> getOsAccounts(Host host) throws TskCoreException {
849  String queryString = "SELECT * FROM tsk_os_accounts accounts "
850  + "WHERE accounts.os_account_obj_id IN "
851  + "(SELECT instances.os_account_obj_id "
852  + "FROM tsk_os_account_instances instances "
853  + "INNER JOIN data_source_info datasources ON datasources.obj_id = instances.data_source_obj_id "
854  + "WHERE datasources.host_id = " + host.getHostId() + ") "
855  + "AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId();
856 
858  try (CaseDbConnection connection = this.db.getConnection();
859  Statement s = connection.createStatement();
860  ResultSet rs = connection.executeQuery(s, queryString)) {
861 
862  List<OsAccount> accounts = new ArrayList<>();
863  while (rs.next()) {
864  accounts.add(osAccountFromResultSet(rs));
865  }
866  return accounts;
867  } catch (SQLException ex) {
868  throw new TskCoreException(String.format("Error getting OS accounts for host id = %d", host.getHostId()), ex);
869  } finally {
871  }
872  }
873 
883  public List<OsAccount> getOsAccountsByDataSourceObjId(long dataSourceId) throws TskCoreException {
884  String queryString = "SELECT * FROM tsk_os_accounts acc "
885  + "WHERE acc.os_account_obj_id IN "
886  + "(SELECT instance.os_account_obj_id "
887  + "FROM tsk_os_account_instances instance "
888  + "WHERE instance.data_source_obj_id = " + dataSourceId + ") "
889  + "AND acc.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId();
890 
892  try (CaseDbConnection connection = this.db.getConnection();
893  Statement s = connection.createStatement();
894  ResultSet rs = connection.executeQuery(s, queryString)) {
895 
896  List<OsAccount> accounts = new ArrayList<>();
897  while (rs.next()) {
898  accounts.add(osAccountFromResultSet(rs));
899  }
900  return accounts;
901  } catch (SQLException ex) {
902  throw new TskCoreException(String.format("Error getting OS accounts for data source id = %d", dataSourceId), ex);
903  } finally {
905  }
906  }
907 
920  void mergeOsAccountsForRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans) throws TskCoreException {
921  List<OsAccount> destinationAccounts = getOsAccounts(destRealm, trans.getConnection());
922  List<OsAccount> sourceAccounts = getOsAccounts(sourceRealm, trans.getConnection());
923 
924  for (OsAccount sourceAccount : sourceAccounts) {
925 
926  // First a check for the case where the source account has both the login name and unique ID set and
927  // we have separate matches in the destination account for both. If we find this case, we need to first merge
928  // the two accounts in the destination realm. This will ensure that all source accounts match at most one
929  // destination account.
930  // Note that we only merge accounts based on login name if the unique ID is empty.
931  if (sourceAccount.getAddr().isPresent() && sourceAccount.getLoginName().isPresent()) {
932  List<OsAccount> duplicateDestAccounts = destinationAccounts.stream()
933  .filter(p -> p.getAddr().equals(sourceAccount.getAddr())
934  || (p.getLoginName().equals(sourceAccount.getLoginName()) && (!p.getAddr().isPresent())))
935  .collect(Collectors.toList());
936  if (duplicateDestAccounts.size() > 1) {
937  OsAccount combinedDestAccount = duplicateDestAccounts.get(0);
938  duplicateDestAccounts.remove(combinedDestAccount);
939  for (OsAccount dupeDestAccount : duplicateDestAccounts) {
940  mergeOsAccounts(dupeDestAccount, combinedDestAccount, trans);
941  }
942  }
943  }
944 
945  // Look for matching destination account
946  Optional<OsAccount> matchingDestAccount = getMatchingAccountForMerge(sourceAccount, destinationAccounts);
947 
948  // If we found a match, merge the accounts. Otherwise simply update the realm id
949  if (matchingDestAccount.isPresent()) {
950  mergeOsAccounts(sourceAccount, matchingDestAccount.get(), trans);
951  } else {
952  String query = "UPDATE tsk_os_accounts SET realm_id = " + destRealm.getRealmId() + " WHERE os_account_obj_id = " + sourceAccount.getId();
953  try (Statement s = trans.getConnection().createStatement()) {
954  s.executeUpdate(query);
955  } catch (SQLException ex) {
956  throw new TskCoreException("Error executing SQL update: " + query, ex);
957  }
958  trans.registerChangedOsAccount(sourceAccount);
959  }
960  }
961  }
962 
969  private Optional<OsAccount> getMatchingAccountForMerge(OsAccount sourceAccount, List<OsAccount> destinationAccounts) {
970  // Look for matching destination account
971  OsAccount matchingDestAccount = null;
972 
973  // First look for matching unique id
974  if (sourceAccount.getAddr().isPresent()) {
975  List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
976  .filter(p -> p.getAddr().equals(sourceAccount.getAddr()))
977  .collect(Collectors.toList());
978  if (!matchingDestAccounts.isEmpty()) {
979  matchingDestAccount = matchingDestAccounts.get(0);
980  }
981  }
982 
983  // If a match wasn't found yet, look for a matching login name.
984  // We will merge only if:
985  // - We didn't already find a unique ID match
986  // - The source account has no unique ID OR the destination account has no unique ID
987  // - destination account has a login name and matches the source account login name
988  if (matchingDestAccount == null && sourceAccount.getLoginName().isPresent()) {
989  List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
990  .filter(p -> p.getLoginName().isPresent())
991  .filter(p -> (p.getLoginName().get().equalsIgnoreCase(sourceAccount.getLoginName().get())
992  && ((!sourceAccount.getAddr().isPresent()) || (!p.getAddr().isPresent()))))
993  .collect(Collectors.toList());
994  if (!matchingDestAccounts.isEmpty()) {
995  matchingDestAccount = matchingDestAccounts.get(0);
996  }
997  }
998 
999  return Optional.ofNullable(matchingDestAccount);
1000  }
1001 
1009  private void mergeOsAccount(OsAccount account, CaseDbTransaction trans) throws TskCoreException {
1010  // Get the realm for the account
1011  Long realmId = account.getRealmId();
1012  OsAccountRealm realm = db.getOsAccountRealmManager().getRealmByRealmId(realmId, trans.getConnection());
1013 
1014  // Get all users in the realm (excluding the account)
1015  List<OsAccount> osAccounts = getOsAccounts(realm, trans.getConnection());
1016  osAccounts.removeIf(acc -> Objects.equals(acc.getId(), account.getId()));
1017 
1018  // Look for matching account
1019  Optional<OsAccount> matchingAccount = getMatchingAccountForMerge(account, osAccounts);
1020 
1021  // If we find a match, merge the accounts.
1022  if (matchingAccount.isPresent()) {
1023  mergeOsAccounts(matchingAccount.get(), account, trans);
1024  }
1025  }
1026 
1041  private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) throws TskCoreException {
1042 
1043  String query = "";
1044  try (Statement s = trans.getConnection().createStatement()) {
1045 
1046  // Update all references
1047  query = makeOsAccountUpdateQuery("tsk_os_account_attributes", sourceAccount, destAccount);
1048  s.executeUpdate(query);
1049 
1050  // tsk_os_account_instances has a unique constraint on os_account_obj_id, data_source_obj_id, and instance_type,
1051  // so delete any rows that would be duplicates.
1052  query = "DELETE FROM tsk_os_account_instances "
1053  + "WHERE id IN ( "
1054  + "SELECT "
1055  + " sourceAccountInstance.id "
1056  + "FROM "
1057  + " tsk_os_account_instances destAccountInstance "
1058  + "INNER JOIN tsk_os_account_instances sourceAccountInstance ON destAccountInstance.data_source_obj_id = sourceAccountInstance.data_source_obj_id "
1059  + "WHERE destAccountInstance.os_account_obj_id = " + destAccount.getId()
1060  + " AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId()
1061  + " AND sourceAccountInstance.instance_type = destAccountInstance.instance_type" + ")";
1062 
1063  s.executeUpdate(query);
1064 
1065  query = makeOsAccountUpdateQuery("tsk_os_account_instances", sourceAccount, destAccount);
1066  s.executeUpdate(query);
1067  synchronized (osAcctInstancesCacheLock) {
1068  osAccountInstanceCache.clear();
1069  }
1070 
1071  query = makeOsAccountUpdateQuery("tsk_files", sourceAccount, destAccount);
1072  s.executeUpdate(query);
1073 
1074  query = makeOsAccountUpdateQuery("tsk_data_artifacts", sourceAccount, destAccount);
1075  s.executeUpdate(query);
1076 
1077 
1078  // register the merged accounts with the transaction to fire off an event
1079  trans.registerMergedOsAccount(sourceAccount.getId(), destAccount.getId());
1080 
1081  // Update the source account. Make a dummy signature to prevent problems with the unique constraint.
1082  String mergedSignature = makeMergedOsAccountSignature();
1083  query = "UPDATE tsk_os_accounts SET merged_into = " + destAccount.getId()
1084  + ", db_status = " + OsAccount.OsAccountDbStatus.MERGED.getId()
1085  + ", signature = '" + mergedSignature + "' "
1086  + " WHERE os_account_obj_id = " + sourceAccount.getId();
1087 
1088  s.executeUpdate(query);
1089  trans.registerDeletedOsAccount(sourceAccount.getId());
1090 
1091  // Merge and update the destination account. Note that this must be done after updating
1092  // the source account to prevent conflicts when merging two accounts in the
1093  // same realm.
1094  mergeOsAccountObjectsAndUpdateDestAccount(sourceAccount, destAccount, trans);
1095  } catch (SQLException ex) {
1096  throw new TskCoreException("Error executing SQL update: " + query, ex);
1097  }
1098  }
1099 
1105  private String makeMergedOsAccountSignature() {
1106  return "MERGED " + UUID.randomUUID().toString();
1107  }
1108 
1118  private String makeOsAccountUpdateQuery(String tableName, OsAccount sourceAccount, OsAccount destAccount) {
1119  return "UPDATE " + tableName + " SET os_account_obj_id = " + destAccount.getId() + " WHERE os_account_obj_id = " + sourceAccount.getId();
1120  }
1121 
1133  private OsAccount mergeOsAccountObjectsAndUpdateDestAccount(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) throws TskCoreException {
1134 
1135  OsAccount mergedDestAccount = destAccount;
1136 
1137  String destLoginName = null;
1138  String destAddr = null;
1139 
1140  // Copy any fields that aren't set in the destination to the value from the source account.
1141  if (!destAccount.getLoginName().isPresent() && sourceAccount.getLoginName().isPresent()) {
1142  destLoginName = sourceAccount.getLoginName().get();
1143  }
1144 
1145  if (!destAccount.getAddr().isPresent() && sourceAccount.getAddr().isPresent()) {
1146  destAddr = sourceAccount.getAddr().get();
1147  }
1148 
1149  // update the dest account core
1150  OsAccountUpdateResult updateStatus = this.updateOsAccountCore(destAccount, destAddr, destLoginName, trans);
1151 
1152  if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
1153  mergedDestAccount = updateStatus.getUpdatedAccount().get();
1154  }
1155 
1156  String destFullName = null;
1157  Long destCreationTime = null;
1158  if (!destAccount.getFullName().isPresent() && sourceAccount.getFullName().isPresent()) {
1159  destFullName = sourceAccount.getFullName().get();
1160  }
1161 
1162  if (!destAccount.getCreationTime().isPresent() && sourceAccount.getCreationTime().isPresent()) {
1163  destCreationTime = sourceAccount.getCreationTime().get();
1164  }
1165 
1166  // update the dest account properties
1167  updateStatus = this.updateStandardOsAccountAttributes(destAccount, destFullName, null, null, destCreationTime, trans);
1168 
1169  if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
1170  mergedDestAccount = updateStatus.getUpdatedAccount().get();
1171  }
1172 
1173  return mergedDestAccount;
1174  }
1175 
1186  private List<OsAccount> getOsAccounts(OsAccountRealm realm, CaseDbConnection connection) throws TskCoreException {
1187  String queryString = "SELECT * FROM tsk_os_accounts"
1188  + " WHERE realm_id = " + realm.getRealmId()
1189  + " AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
1190  + " ORDER BY os_account_obj_id";
1191 
1192  try (Statement s = connection.createStatement();
1193  ResultSet rs = connection.executeQuery(s, queryString)) {
1194 
1195  List<OsAccount> accounts = new ArrayList<>();
1196  while (rs.next()) {
1197  accounts.add(osAccountFromResultSet(rs));
1198  }
1199  return accounts;
1200  } catch (SQLException ex) {
1201  throw new TskCoreException(String.format("Error getting OS accounts for realm id = %d", realm.getRealmId()), ex);
1202  }
1203  }
1204 
1212  public List<OsAccount> getOsAccounts() throws TskCoreException {
1213  String queryString = "SELECT * FROM tsk_os_accounts"
1214  + " WHERE db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId();
1215 
1217  try (CaseDbConnection connection = this.db.getConnection();
1218  Statement s = connection.createStatement();
1219  ResultSet rs = connection.executeQuery(s, queryString)) {
1220 
1221  List<OsAccount> accounts = new ArrayList<>();
1222  while (rs.next()) {
1223  accounts.add(osAccountFromResultSet(rs));
1224  }
1225  return accounts;
1226  } catch (SQLException ex) {
1227  throw new TskCoreException(String.format("Error getting OS accounts"), ex);
1228  } finally {
1230  }
1231  }
1232 
1248  public Optional<OsAccount> getWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost) throws TskCoreException, NotUserSIDException {
1249 
1250  if (referringHost == null) {
1251  throw new TskCoreException("A referring host is required to get an account.");
1252  }
1253 
1254  // ensure at least one of the two is supplied - a non-null sid or a login name
1255  if ((StringUtils.isBlank(sid) || (sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ) && StringUtils.isBlank(loginName)) {
1256  throw new TskCoreException("Cannot get an OS account with both SID and loginName as null.");
1257  }
1258 
1259  // If no SID is given and the given realm/login names is a well known account, get and use the well known SID
1260  if (StringUtils.isBlank(sid)
1261  && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName)
1262  && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
1263  sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
1264 
1265  }
1266 
1267  // first get the realm for the given sid
1268  Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getWindowsRealm(sid, realmName, referringHost);
1269  if (!realm.isPresent()) {
1270  return Optional.empty();
1271  }
1272 
1273  // search by SID
1274  if (!Strings.isNullOrEmpty(sid) && !(sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
1275  if (!WindowsAccountUtils.isWindowsUserSid(sid)) {
1276  throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid));
1277  }
1278 
1279  Optional<OsAccount> account = this.getOsAccountByAddr(sid, realm.get());
1280  if (account.isPresent()) {
1281  return account;
1282  }
1283  }
1284 
1285  // search by login name
1286  if (!Strings.isNullOrEmpty(loginName)) {
1287  String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
1288  return this.getOsAccountByLoginName(resolvedLoginName, realm.get());
1289  } else {
1290  return Optional.empty();
1291  }
1292  }
1293 
1306  @Beta
1307  public Optional<OsAccount> getLocalLinuxOsAccount(String uid, String loginName, Host referringHost) throws TskCoreException {
1308 
1309  if (referringHost == null) {
1310  throw new TskCoreException("A referring host is required to get an account.");
1311  }
1312 
1313  // ensure at least one of the two is supplied - a non-null uid or a login name
1314  if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
1315  throw new TskCoreException("Cannot get an OS account with both UID and loginName as null.");
1316  }
1317 
1318  // First get the local realm
1319  Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getLocalLinuxRealm(referringHost);
1320  if (!realm.isPresent()) {
1321  return Optional.empty();
1322  }
1323 
1324  // Search by UID
1325  if (!Strings.isNullOrEmpty(uid)) {
1326  Optional<OsAccount> account = this.getOsAccountByAddr(uid, realm.get());
1327  if (account.isPresent()) {
1328  return account;
1329  }
1330  }
1331 
1332  // Search by login name
1333  if (!Strings.isNullOrEmpty(loginName)) {
1334  return this.getOsAccountByLoginName(loginName, realm.get());
1335  } else {
1336  return Optional.empty();
1337  }
1338  }
1339 
1349  public void addExtendedOsAccountAttributes(OsAccount account, List<OsAccountAttribute> accountAttributes) throws TskCoreException {
1350 
1351  synchronized (account) { // synchronized to prevent multiple threads trying to add osAccount attributes concurrently to the same osAccount.
1353 
1354  try (CaseDbConnection connection = db.getConnection()) {
1355  for (OsAccountAttribute accountAttribute : accountAttributes) {
1356 
1357  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)"
1358  + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
1359 
1360  PreparedStatement preparedStatement = connection.getPreparedStatement(attributeInsertSQL, Statement.RETURN_GENERATED_KEYS);
1361  preparedStatement.clearParameters();
1362 
1363  preparedStatement.setLong(1, account.getId());
1364  if (accountAttribute.getHostId().isPresent()) {
1365  preparedStatement.setLong(2, accountAttribute.getHostId().get());
1366  } else {
1367  preparedStatement.setNull(2, java.sql.Types.NULL);
1368  }
1369  if (accountAttribute.getSourceObjectId().isPresent()) {
1370  preparedStatement.setLong(3, accountAttribute.getSourceObjectId().get());
1371  } else {
1372  preparedStatement.setNull(3, java.sql.Types.NULL);
1373  }
1374 
1375  preparedStatement.setLong(4, accountAttribute.getAttributeType().getTypeID());
1376  preparedStatement.setLong(5, accountAttribute.getAttributeType().getValueType().getType());
1377 
1378  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE) {
1379  preparedStatement.setBytes(6, accountAttribute.getValueBytes());
1380  } else {
1381  preparedStatement.setBytes(6, null);
1382  }
1383 
1384  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
1385  || accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1386  preparedStatement.setString(7, accountAttribute.getValueString());
1387  } else {
1388  preparedStatement.setString(7, null);
1389  }
1390  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER) {
1391  preparedStatement.setInt(8, accountAttribute.getValueInt());
1392  } else {
1393  preparedStatement.setNull(8, java.sql.Types.NULL);
1394  }
1395 
1396  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME
1397  || accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG) {
1398  preparedStatement.setLong(9, accountAttribute.getValueLong());
1399  } else {
1400  preparedStatement.setNull(9, java.sql.Types.NULL);
1401  }
1402 
1403  if (accountAttribute.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE) {
1404  preparedStatement.setDouble(10, accountAttribute.getValueDouble());
1405  } else {
1406  preparedStatement.setNull(10, java.sql.Types.NULL);
1407  }
1408 
1409  connection.executeUpdate(preparedStatement);
1410  }
1411  } catch (SQLException ex) {
1412  throw new TskCoreException(String.format("Error adding OS Account attribute for account id = %d", account.getId()), ex);
1413  } finally {
1415  }
1416  // set the atrribute list in account to the most current list from the database
1417  List<OsAccountAttribute> currentAttribsList = getOsAccountAttributes(account);
1418  account.setAttributesInternal(currentAttribsList);
1419  }
1420  fireChangeEvent(account);
1421  }
1422 
1432  List<OsAccountAttribute> getOsAccountAttributes(OsAccount account) throws TskCoreException {
1433 
1434  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, "
1435  + " attributes.attribute_type_id as attribute_type_id, attributes.value_type as value_type, attributes.value_byte as value_byte, "
1436  + " attributes.value_text as value_text, attributes.value_int32 as value_int32, attributes.value_int64 as value_int64, attributes.value_double as value_double, "
1437  + " hosts.id, hosts.name as host_name, hosts.db_status as host_status "
1438  + " FROM tsk_os_account_attributes as attributes"
1439  + " LEFT JOIN tsk_hosts as hosts "
1440  + " ON attributes.host_id = hosts.id "
1441  + " WHERE os_account_obj_id = " + account.getId();
1442 
1444  try (CaseDbConnection connection = this.db.getConnection();
1445  Statement s = connection.createStatement();
1446  ResultSet rs = connection.executeQuery(s, queryString)) {
1447 
1448  List<OsAccountAttribute> attributes = new ArrayList<>();
1449  while (rs.next()) {
1450 
1451  Host host = null;
1452  long hostId = rs.getLong("host_id");
1453  if (!rs.wasNull()) {
1454  host = new Host(hostId, rs.getString("host_name"), Host.HostDbStatus.fromID(rs.getInt("host_status")));
1455  }
1456 
1457  Content sourceContent = null;
1458  long sourceObjId = rs.getLong("source_obj_id");
1459  if (!rs.wasNull()) {
1460  sourceContent = this.db.getContentById(sourceObjId);
1461  }
1462  BlackboardAttribute.Type attributeType = db.getBlackboard().getAttributeType(rs.getInt("attribute_type_id"));
1463  OsAccountAttribute attribute = account.new OsAccountAttribute(attributeType, rs.getInt("value_int32"), rs.getLong("value_int64"),
1464  rs.getDouble("value_double"), rs.getString("value_text"), rs.getBytes("value_byte"),
1465  db, account, host, sourceContent);
1466 
1467  attributes.add(attribute);
1468  }
1469  return attributes;
1470  } catch (SQLException ex) {
1471  throw new TskCoreException(String.format("Error getting OS account attributes for account obj id = %d", account.getId()), ex);
1472  } finally {
1474  }
1475  }
1476 
1486  public List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreException {
1487  String whereClause = "tsk_os_account_instances.os_account_obj_id = " + account.getId();
1488  return getOsAccountInstances(whereClause);
1489  }
1490 
1501  public List<OsAccountInstance> getOsAccountInstances(List<Long> instanceIDs) throws TskCoreException {
1502  String instanceIds = instanceIDs.stream().map(id -> id.toString()).collect(Collectors.joining(","));
1503 
1504  List<OsAccountInstance> osAcctInstances = new ArrayList<>();
1505 
1506  String querySQL = "SELECT * FROM tsk_os_account_instances "
1507  + " WHERE tsk_os_account_instances.id IN (" + instanceIds + ")";
1508 
1510  try (CaseDbConnection connection = db.getConnection();
1511  PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
1512  ResultSet results = connection.executeQuery(preparedStatement)) {
1513 
1514  osAcctInstances = getOsAccountInstancesFromResultSet(results);
1515 
1516  } catch (SQLException ex) {
1517  throw new TskCoreException("Failed to get OsAccountInstances (SQL = " + querySQL + ")", ex);
1518  } finally {
1520  }
1521  return osAcctInstances;
1522  }
1523 
1537  private List<OsAccountInstance> getOsAccountInstances(String whereClause) throws TskCoreException {
1538  List<OsAccountInstance> osAcctInstances = new ArrayList<>();
1539 
1540  String querySQL
1541  = "SELECT tsk_os_account_instances.* "
1542  + " FROM tsk_os_account_instances "
1543  + " INNER JOIN ( SELECT os_account_obj_id, data_source_obj_id, MIN(instance_type) AS min_instance_type "
1544  + " FROM tsk_os_account_instances"
1545  + " GROUP BY os_account_obj_id, data_source_obj_id ) grouped_instances "
1546  + " ON tsk_os_account_instances.os_account_obj_id = grouped_instances.os_account_obj_id "
1547  + " AND tsk_os_account_instances.instance_type = grouped_instances.min_instance_type "
1548  + " WHERE " + whereClause;
1549 
1551  try (CaseDbConnection connection = db.getConnection();
1552  PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
1553  ResultSet results = connection.executeQuery(preparedStatement)) {
1554 
1555  osAcctInstances = getOsAccountInstancesFromResultSet(results);
1556 
1557  } catch (SQLException ex) {
1558  throw new TskCoreException("Failed to get OsAccountInstances (SQL = " + querySQL + ")", ex);
1559  } finally {
1561  }
1562  return osAcctInstances;
1563  }
1564 
1574  private List<OsAccountInstance> getOsAccountInstancesFromResultSet(ResultSet results) throws SQLException {
1575 
1576  List<OsAccountInstance> osAcctInstances = new ArrayList<>();
1577  while (results.next()) {
1578  long instanceId = results.getLong("id");
1579  long osAccountObjID = results.getLong("os_account_obj_id");
1580  long dataSourceObjId = results.getLong("data_source_obj_id");
1581  int instanceType = results.getInt("instance_type");
1582  osAcctInstances.add(new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType)));
1583  }
1584 
1585  return osAcctInstances;
1586  }
1587 
1604  public OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime) throws TskCoreException {
1605 
1606  CaseDbTransaction trans = db.beginTransaction();
1607  try {
1608  OsAccountUpdateResult updateStatus = updateStandardOsAccountAttributes(osAccount, fullName, accountType, accountStatus, creationTime, trans);
1609 
1610  trans.commit();
1611  trans = null;
1612 
1613  return updateStatus;
1614  } finally {
1615  if (trans != null) {
1616  trans.rollback();
1617  }
1618  }
1619  }
1620 
1638  OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime, CaseDbTransaction trans) throws TskCoreException {
1639 
1640  OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1641 
1642  try {
1643  CaseDbConnection connection = trans.getConnection();
1644 
1645  if (!StringUtils.isBlank(fullName)) {
1646  updateAccountColumn(osAccount.getId(), "full_name", fullName, connection);
1647  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1648  }
1649 
1650  if (Objects.nonNull(accountType)) {
1651  updateAccountColumn(osAccount.getId(), "type", accountType.getId(), connection);
1652  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1653  }
1654 
1655  if (Objects.nonNull(accountStatus)) {
1656  updateAccountColumn(osAccount.getId(), "status", accountStatus.getId(), connection);
1657  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1658  }
1659 
1660  if (Objects.nonNull(creationTime)) {
1661  updateAccountColumn(osAccount.getId(), "created_date", creationTime, connection);
1662  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1663  }
1664 
1665  // if nothing has been changed, return
1666  if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1667  return new OsAccountUpdateResult(updateStatusCode, null);
1668  }
1669 
1670  // get the updated account from database
1671  OsAccount updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection);
1672 
1673  // register the updated account with the transaction to fire off an event
1674  trans.registerChangedOsAccount(updatedAccount);
1675 
1676  return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
1677 
1678  } catch (SQLException ex) {
1679  throw new TskCoreException(String.format("Error updating account with addr = %s, account id = %d", osAccount.getAddr().orElse("Unknown"), osAccount.getId()), ex);
1680  }
1681  }
1682 
1696  private <T> void updateAccountColumn(long accountObjId, String colName, T colValue, CaseDbConnection connection) throws SQLException, TskCoreException {
1697 
1698  String updateSQL = "UPDATE tsk_os_accounts "
1699  + " SET " + colName + " = ? "
1700  + " WHERE os_account_obj_id = ?";
1701 
1703  try {
1704  PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1705  preparedStatement.clearParameters();
1706 
1707  if (Objects.isNull(colValue)) {
1708  preparedStatement.setNull(1, Types.NULL); // handle null value
1709  } else {
1710  if (colValue instanceof String) {
1711  preparedStatement.setString(1, (String) colValue);
1712  } else if (colValue instanceof Long) {
1713  preparedStatement.setLong(1, (Long) colValue);
1714  } else if (colValue instanceof Integer) {
1715  preparedStatement.setInt(1, (Integer) colValue);
1716  } else {
1717  throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId));
1718  }
1719  }
1720 
1721  preparedStatement.setLong(2, accountObjId);
1722 
1723  connection.executeUpdate(preparedStatement);
1724  } finally {
1726  }
1727  }
1728 
1739  private void updateAccountSignature(long accountObjId, String signature, CaseDbConnection connection) throws SQLException {
1740 
1741  String updateSQL = "UPDATE tsk_os_accounts SET "
1742  + " signature = "
1743  + " CASE WHEN db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId() + " THEN ? ELSE signature END "
1744  + " WHERE os_account_obj_id = ?"; // 8
1745 
1746  PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
1747  preparedStatement.clearParameters();
1748 
1749  preparedStatement.setString(1, signature);
1750  preparedStatement.setLong(2, accountObjId);
1751 
1752  connection.executeUpdate(preparedStatement);
1753  }
1754 
1775  public OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost) throws TskCoreException, NotUserSIDException {
1776  CaseDbTransaction trans = db.beginTransaction();
1777  try {
1778  OsAccountUpdateResult updateStatus = this.updateCoreWindowsOsAccountAttributes(osAccount, accountSid, loginName, realmName, referringHost, trans);
1779 
1780  trans.commit();
1781  trans = null;
1782  return updateStatus;
1783  } finally {
1784  if (trans != null) {
1785  trans.rollback();
1786  }
1787  }
1788  }
1789 
1809  private OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost, CaseDbTransaction trans) throws TskCoreException, NotUserSIDException {
1810 
1811  // first get and update the realm - if we have the info to find the realm
1812 
1813  if ((!StringUtils.isBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) || !StringUtils.isBlank(realmName)) {
1814  // If the SID is a well known SID, ensure we use the well known english name
1815  String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
1816 
1817 
1818  OsRealmUpdateResult realmUpdateResult = db.getOsAccountRealmManager().getAndUpdateWindowsRealm(accountSid, resolvedRealmName, referringHost, trans.getConnection());
1819 
1820 
1821  Optional<OsAccountRealm> realmOptional = realmUpdateResult.getUpdatedRealm();
1822 
1823  if (realmOptional.isPresent()) {
1824 
1825  if (realmUpdateResult.getUpdateStatus() == OsRealmUpdateStatus.UPDATED) {
1826 
1827  // Check if update of the realm triggers a merge with any other realm,
1828  // say another realm with same name but no SID, or same SID but no name
1829  //1. Check if there is any OTHER realm with the same name, same host but no addr
1830  Optional<OsAccountRealm> anotherRealmWithSameName = db.getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, trans.getConnection());
1831 
1832  // 2. Check if there is any OTHER realm with same addr and host, but NO name
1833  Optional<OsAccountRealm> anotherRealmWithSameAddr = db.getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, trans.getConnection());
1834 
1835  if (anotherRealmWithSameName.isPresent()) {
1836  db.getOsAccountRealmManager().mergeRealms(anotherRealmWithSameName.get(), realmOptional.get(), trans);
1837  }
1838  if (anotherRealmWithSameAddr.isPresent()) {
1839  db.getOsAccountRealmManager().mergeRealms(anotherRealmWithSameAddr.get(), realmOptional.get(), trans);
1840  }
1841  }
1842  }
1843  }
1844 
1845  // now update the account core data
1846  String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
1847  OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, accountSid, resolvedLoginName, trans);
1848 
1849  Optional<OsAccount> updatedAccount = updateStatus.getUpdatedAccount();
1850  if (updatedAccount.isPresent()) {
1851  // After updating account data, check if there is matching account to merge
1852  mergeOsAccount(updatedAccount.get(), trans);
1853  }
1854 
1855  return updateStatus;
1856  }
1857 
1875  public OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName) throws TskCoreException {
1876  CaseDbTransaction trans = db.beginTransaction();
1877  try {
1878  OsAccountUpdateResult updateStatus = this.updateCoreLocalLinuxOsAccountAttributes(osAccount, uid, loginName, trans);
1879 
1880  trans.commit();
1881  trans = null;
1882  return updateStatus;
1883  } finally {
1884  if (trans != null) {
1885  trans.rollback();
1886  }
1887  }
1888  }
1889 
1907  @Beta
1908  private OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName, CaseDbTransaction trans) throws TskCoreException {
1909 
1910  // Update the account core data
1911  OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, uid, loginName, trans);
1912 
1913  Optional<OsAccount> updatedAccount = updateStatus.getUpdatedAccount();
1914  if (updatedAccount.isPresent()) {
1915  // After updating account data, check if there is matching account to merge
1916  mergeOsAccount(updatedAccount.get(), trans);
1917  }
1918 
1919  return updateStatus;
1920  }
1921 
1944  private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String address, String loginName, CaseDbTransaction trans) throws TskCoreException {
1945 
1946  OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
1947  OsAccount updatedAccount;
1948 
1949  try {
1950  CaseDbConnection connection = trans.getConnection();
1951 
1952  // if a new non-null addr is provided and the account already has an address, and they are not the same, throw an exception
1953  if (!StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(""))) {
1954  throw new TskCoreException(String.format("Account (%d) already has an address (%s), address cannot be updated.", osAccount.getId(), osAccount.getAddr().orElse("NULL")));
1955  }
1956 
1957  if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
1958  updateAccountColumn(osAccount.getId(), "addr", address, connection);
1959  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1960  }
1961 
1962  if (StringUtils.isBlank(osAccount.getLoginName().orElse(null)) && !StringUtils.isBlank(loginName)) {
1963  updateAccountColumn(osAccount.getId(), "login_name", loginName, connection);
1964  updateStatusCode = OsAccountUpdateStatus.UPDATED;
1965  }
1966 
1967  // if nothing is changed, return
1968  if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
1969  return new OsAccountUpdateResult(updateStatusCode, osAccount);
1970  }
1971 
1972  // update signature if needed, based on the most current addr/loginName
1973  OsAccount currAccount = getOsAccountByObjectId(osAccount.getId(), connection);
1974  String newAddress = currAccount.getAddr().orElse(null);
1975  String newLoginName = currAccount.getLoginName().orElse(null);
1976 
1977  String newSignature = getOsAccountSignature(newAddress, newLoginName);
1978 
1979  try {
1980  updateAccountSignature(osAccount.getId(), newSignature, connection);
1981  } catch (SQLException ex) {
1982  // There's a slight chance that we're in the case where we are trying to add an addr to an OS account
1983  // with only a name where the addr already exists on a different OS account. This will cause a unique
1984  // constraint failure in updateAccountSignature(). This is unlikely to happen in normal use
1985  // since we lookup OS accounts by addr before name when we have both (i.e., it would be strange to have an
1986  // OsAccount in hand with only the loginName set when we also know the addr).
1987  // Correctly handling every case here is non-trivial, so for the moment only look for the specific case where
1988  // we had an OsAccount with just an addr and and OsAccount with just a login name that we now
1989  // want to combine.
1990  if (osAccount.getAddr().isEmpty() && !StringUtils.isBlank(address)) {
1991  OsAccountRealm realm = db.getOsAccountRealmManager().getRealmByRealmId(osAccount.getRealmId(), connection);
1992  Optional<OsAccount> matchingAddrAcct = getOsAccountByAddr(address, realm.getScopeHost().get(), connection);
1993  if (matchingAddrAcct.isEmpty()
1994  || matchingAddrAcct.get().getId() == osAccount.getId()
1995  || matchingAddrAcct.get().getLoginName().isPresent()) {
1996  throw ex; // Rethrow the original error
1997  }
1998 
1999  // What we should have is osAccount with just a loginName and matchingAddrAcct with
2000  // just an address, so merge them.
2001  mergeOsAccounts(matchingAddrAcct.get(), osAccount, trans);
2002  }
2003  }
2004 
2005  // get the updated account from database
2006  updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection);
2007 
2008  // register the updated account with the transaction to fire off an event
2009  trans.registerChangedOsAccount(updatedAccount);
2010 
2011  return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
2012 
2013  } catch (SQLException ex) {
2014  throw new TskCoreException(String.format("Error updating account with unique id = %s, account id = %d", osAccount.getAddr().orElse("Unknown"), osAccount.getId()), ex);
2015  }
2016  }
2017 
2027  public List<Host> getHosts(OsAccount account) throws TskCoreException {
2028  List<Host> hostList = new ArrayList<>();
2029 
2030  String query = "SELECT tsk_hosts.id AS hostId, name, db_status FROM tsk_hosts "
2031  + " JOIN data_source_info ON tsk_hosts.id = data_source_info.host_id"
2032  + " JOIN tsk_os_account_instances ON data_source_info.obj_id = tsk_os_account_instances.data_source_obj_id"
2033  + " WHERE os_account_obj_id = " + account.getId();
2034 
2036  try (CaseDbConnection connection = db.getConnection();
2037  Statement s = connection.createStatement();
2038  ResultSet rs = connection.executeQuery(s, query)) {
2039 
2040  while (rs.next()) {
2041  hostList.add(new Host(rs.getLong("hostId"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
2042  }
2043 
2044  } catch (SQLException ex) {
2045  throw new TskCoreException(String.format("Failed to get host list for os account %d", account.getId()), ex);
2046  } finally {
2048  }
2049  return hostList;
2050  }
2051 
2063  private OsAccount osAccountFromResultSet(ResultSet rs) throws SQLException {
2064 
2065  OsAccountType accountType = null;
2066  int typeId = rs.getInt("type");
2067  if (!rs.wasNull()) {
2068  accountType = OsAccount.OsAccountType.fromID(typeId);
2069  }
2070 
2071  Long creationTime = rs.getLong("created_date"); // getLong returns 0 if value is null
2072  if (rs.wasNull()) {
2073  creationTime = null;
2074  }
2075 
2076  return new OsAccount(db, rs.getLong("os_account_obj_id"), rs.getLong("realm_id"), rs.getString("login_name"), rs.getString("addr"),
2077  rs.getString("signature"), rs.getString("full_name"), creationTime, accountType, OsAccount.OsAccountStatus.fromID(rs.getInt("status")),
2078  OsAccount.OsAccountDbStatus.fromID(rs.getInt("db_status")));
2079 
2080  }
2081 
2088  private void fireChangeEvent(OsAccount account) {
2089  db.fireTSKEvent(new OsAccountsUpdatedTskEvent(Collections.singletonList(account)));
2090  }
2091 
2106  static String getOsAccountSignature(String uniqueId, String loginName) throws TskCoreException {
2107  // Create a signature.
2108  String signature;
2109  if (Strings.isNullOrEmpty(uniqueId) == false) {
2110  signature = uniqueId;
2111  } else if (Strings.isNullOrEmpty(loginName) == false) {
2112  signature = loginName;
2113  } else {
2114  throw new TskCoreException("OS Account must have either a uniqueID or a login name.");
2115  }
2116  return signature;
2117  }
2118 
2123  public static class NotUserSIDException extends TskException {
2124 
2125  private static final long serialVersionUID = 1L;
2126 
2131  super("No error message available.");
2132  }
2133 
2139  public NotUserSIDException(String msg) {
2140  super(msg);
2141  }
2142 
2149  public NotUserSIDException(String msg, Exception ex) {
2150  super(msg, ex);
2151  }
2152  }
2153 
2158 
2161  MERGED
2162  }
2163 
2168  public final static class OsAccountUpdateResult {
2169 
2170  private final OsAccountUpdateStatus updateStatus;
2171  private final OsAccount updatedAccount;
2172 
2173  OsAccountUpdateResult(OsAccountUpdateStatus updateStatus, OsAccount updatedAccount) {
2174  this.updateStatus = updateStatus;
2175  this.updatedAccount = updatedAccount;
2176  }
2177 
2179  return updateStatus;
2180  }
2181 
2182  public Optional<OsAccount> getUpdatedAccount() {
2183  return Optional.ofNullable(updatedAccount);
2184  }
2185  }
2186 
2191  private class OsAccountInstanceKey implements Comparable<OsAccountInstanceKey>{
2192 
2193  private final long osAccountId;
2194  private final long dataSourceId;
2195 
2196  OsAccountInstanceKey(long osAccountId, long dataSourceId) {
2197  this.osAccountId = osAccountId;
2198  this.dataSourceId = dataSourceId;
2199  }
2200 
2201  @Override
2202  public boolean equals(Object other) {
2203  if (this == other) {
2204  return true;
2205  }
2206  if (other == null) {
2207  return false;
2208  }
2209  if (getClass() != other.getClass()) {
2210  return false;
2211  }
2212 
2213  final OsAccountInstanceKey otherKey = (OsAccountInstanceKey) other;
2214 
2215  if (osAccountId != otherKey.osAccountId) {
2216  return false;
2217  }
2218 
2219  return dataSourceId == otherKey.dataSourceId;
2220  }
2221 
2222  @Override
2223  public int hashCode() {
2224  int hash = 5;
2225  hash = 53 * hash + (int) (this.osAccountId ^ (this.osAccountId >>> 32));
2226  hash = 53 * hash + (int) (this.dataSourceId ^ (this.dataSourceId >>> 32));
2227  return hash;
2228  }
2229 
2230  @Override
2231  public int compareTo(OsAccountInstanceKey other) {
2232  if(this.equals(other)) {
2233  return 0;
2234  }
2235 
2236  if (dataSourceId != other.dataSourceId) {
2237  return Long.compare(dataSourceId, other.dataSourceId);
2238  }
2239 
2240  return Long.compare(osAccountId, other.osAccountId);
2241  }
2242  }
2243 }
Optional< String > getAddr()
Definition: OsAccount.java:268
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:195
OsAccountRealm newLocalLinuxRealm(Host referringHost)
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)
Optional< OsAccount > getLocalLinuxOsAccount(String uid, String loginName, Host referringHost)
Optional< OsAccountRealm > getLocalLinuxRealm(Host referringHost)
OsAccountRealmManager getOsAccountRealmManager()
OsAccount newLocalLinuxOsAccount(String uid, String loginName, Host referringHost)
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)
List< OsAccount > getOsAccountsByDataSourceObjId(long dataSourceId)
List< OsAccountInstance > getOsAccountInstances(List< Long > instanceIDs)
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
List< OsAccountInstance > getOsAccountInstances(OsAccount account)
Optional< String > getLoginName()
Definition: OsAccount.java:288
void addExtendedOsAccountAttributes(OsAccount account, List< OsAccountAttribute > accountAttributes)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName)
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.