Sleuth Kit Java Bindings (JNI) 4.14.0
Java bindings for using The Sleuth Kit
Loading...
Searching...
No Matches
OsAccountRealmManager.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 */
19package org.sleuthkit.datamodel;
20
21import com.google.common.annotations.Beta;
22import com.google.common.base.Strings;
23import org.apache.commons.lang3.StringUtils;
24import java.sql.PreparedStatement;
25import java.sql.ResultSet;
26import java.sql.SQLException;
27import java.sql.Statement;
28import java.sql.Types;
29import java.util.ArrayList;
30import java.util.List;
31import java.util.Locale;
32import java.util.Objects;
33import java.util.Optional;
34import java.util.UUID;
35import java.util.logging.Logger;
36import org.sleuthkit.datamodel.OsAccountRealm.ScopeConfidence;
37import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
38import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
39
40
45public final class OsAccountRealmManager {
46
47 private static final Logger LOGGER = Logger.getLogger(OsAccountRealmManager.class.getName());
48 private static final String LOCAL_REALM_NAME = "local";
49
50 private final SleuthkitCase db;
51
58 OsAccountRealmManager(SleuthkitCase skCase) {
59 this.db = skCase;
60 }
61
80 public OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope) throws TskCoreException, OsAccountManager.NotUserSIDException {
81
82 if (realmScope == null) {
83 throw new TskCoreException("RealmScope cannot be null. Use UNKNOWN if scope is not known.");
84 }
85 if (referringHost == null) {
86 throw new TskCoreException("A referring host is required to create a realm.");
87 }
88 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
89 && StringUtils.isBlank(realmName)) {
90 throw new TskCoreException("Either an address or a name is required to create a realm.");
91 }
92
93 if (StringUtils.isNotBlank(accountSid)) {
94 // SID Normalized to uppercase
95 accountSid = accountSid.toUpperCase(Locale.ENGLISH);
96 }
97 if (StringUtils.isNotBlank(realmName)) {
98 // Windows realm names are case insensitive. saving them in lower case.
99 realmName = realmName.toLowerCase(Locale.ENGLISH);
100 }
101
102 Host scopeHost;
103 OsAccountRealm.ScopeConfidence scopeConfidence;
104
105 switch (realmScope) {
106 case DOMAIN:
107 scopeHost = null;
108 scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN;
109 break;
110 case LOCAL:
111 scopeHost = referringHost;
112 scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN;
113 break;
114
115 case UNKNOWN:
116 default:
117 // NOTE: if there's a well known SID, the scope will be changed to LOCAL later.
118 // check if the referring host already has a realm
119 boolean isHostRealmKnown = isHostRealmKnown(referringHost);
120 if (isHostRealmKnown) {
121 scopeHost = null; // the realm does not scope to the referring host since it already has one.
122 scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN;
123 } else {
124 scopeHost = referringHost;
125 scopeConfidence = OsAccountRealm.ScopeConfidence.INFERRED;
126 }
127 break;
128
129 }
130
131 // get windows realm address from sid
132 String realmAddr = null;
133 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
134 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
135
136 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
137 throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", accountSid ));
138 }
139
140 realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
141
142
143 if (WindowsAccountUtils.isWindowsWellKnownSid(accountSid)) {
144
145 // if the sid is a Windows well known SID, create a local realm for it.
146 scopeHost = referringHost;
147 scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN;
148
149 // if the SID is a Windows well known SID, then prefer to use the default well known name to create the realm
150 String wellKnownRealmName = WindowsAccountUtils.getWindowsWellKnownSidRealmName(accountSid);
151 if (!StringUtils.isEmpty(wellKnownRealmName)) {
152 resolvedRealmName = wellKnownRealmName;
153 }
154 }
155 }
156
157 String signature = makeRealmSignature(realmAddr, resolvedRealmName, scopeHost);
158
159 // create a realm
160 return newRealm(resolvedRealmName, realmAddr, signature, scopeHost, scopeConfidence);
161 }
162
173 @Beta
175
176 if (referringHost == null) {
177 throw new TskCoreException("A referring host is required to create a realm.");
178 }
179
180 String realmName = LOCAL_REALM_NAME;
182 String signature = makeRealmSignature("", realmName, referringHost);
183
184 // create a realm
185 return newRealm(realmName, "", signature, referringHost, scopeConfidence);
186 }
187
198 @Beta
199 public Optional<OsAccountRealm> getLocalLinuxRealm(Host referringHost) throws TskCoreException {
200 if (referringHost == null) {
201 throw new TskCoreException("A referring host is required get a realm.");
202 }
203
204 try (CaseDbConnection connection = this.db.getConnection()) {
205 return getRealmByName(LOCAL_REALM_NAME, referringHost, connection);
206 }
207 }
208
226 public Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmName, Host referringHost) throws TskCoreException, OsAccountManager.NotUserSIDException {
227
228 if (referringHost == null) {
229 throw new TskCoreException("A referring host is required get a realm.");
230 }
231
232 // need at least one of the two, the addr or name to look up
233 if ((Strings.isNullOrEmpty(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) )
234 && Strings.isNullOrEmpty(realmName)) {
235 throw new TskCoreException("Realm address or name is required get a realm.");
236 }
237 if (StringUtils.isNotBlank(accountSid)) {
238 // SID Normalized to uppercase
239 accountSid = accountSid.toUpperCase(Locale.ENGLISH);
240 }
241 if (StringUtils.isNotBlank(realmName)) {
242 // Windows realm names are case insensitive. saving them in lower case.
243 realmName = realmName.toLowerCase(Locale.ENGLISH);
244 }
245
246 try (CaseDbConnection connection = this.db.getConnection()) {
247 return getWindowsRealm(accountSid, realmName, referringHost, connection);
248 }
249 }
250
251
266 Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmName, Host referringHost, CaseDbConnection connection) throws TskCoreException, OsAccountManager.NotUserSIDException {
267
268 if (referringHost == null) {
269 throw new TskCoreException("A referring host is required get a realm.");
270 }
271
272 // need at least one of the two, the addr or name to look up
273 if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
274 && StringUtils.isBlank(realmName)) {
275 throw new TskCoreException("Realm address or name is required get a realm.");
276 }
277
278 // If a non null accountSID is provided search for realm by addr.
279 if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
280
281 if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) {
282 throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", accountSid ));
283 }
284 // get realm addr from the account SID.
285 String realmAddr = WindowsAccountUtils.getWindowsRealmAddress(accountSid);
286 Optional<OsAccountRealm> realm = getRealmByAddr(realmAddr, referringHost, connection);
287 if (realm.isPresent()) {
288 return realm;
289 }
290 }
291
292 // ensure we are using English names for any well known SIDs.
293 String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
294
295 // No realm addr so search by name.
296 Optional<OsAccountRealm> realm = getRealmByName(resolvedRealmName, referringHost, connection);
297 if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
298 // If we were given a non-null accountSID, make sure there isn't one set on the matching realm.
299 // We know it won't match because the previous search by SID failed.
300 if (realm.get().getRealmAddr().isPresent()) {
301 return Optional.empty();
302 }
303 }
304 return realm;
305 }
306
307
326 OsRealmUpdateResult getAndUpdateWindowsRealm(String accountSid, String realmName, Host referringHost, CaseDbConnection connection) throws TskCoreException, OsAccountManager.NotUserSIDException {
327
328 // get realm
329 Optional<OsAccountRealm> realmOptional = getWindowsRealm(accountSid, realmName, referringHost, connection);
330
331 // if found, update it if needed
332 if (realmOptional.isPresent()) {
333 String realmAddr = (StringUtils.isNotBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null;
334 OsRealmUpdateResult realmUpdateResult = updateRealm(realmOptional.get(), realmAddr, realmName, connection);
335
336 return realmUpdateResult;
337
338 } else {
340 }
341
342 }
343
344
365 public OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName) throws TskCoreException {
366
367 try (CaseDbConnection connection = db.getConnection()) {
368 return updateRealm(realm, realmAddr, realmName, connection);
369 }
370 }
371
391 private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException {
392
393 // need at least one of the two
394 if ( (StringUtils.isBlank(realmAddr) || realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))
395 && StringUtils.isBlank(realmName)) {
396 throw new TskCoreException("Realm address or name is required to update realm.");
397 }
398
399 OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE;
400 OsAccountRealm updatedRealm = null;
401
403 try {
404 String currRealmAddr = realm.getRealmAddr().orElse(null);
405
406 // set name and address to new values only if the current value is blank and the new value isn't.
407 if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr) && !realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
408 updateRealmColumn(realm.getRealmId(), "realm_addr", realmAddr, connection);
409 currRealmAddr = realmAddr;
410 updateStatusCode = OsRealmUpdateStatus.UPDATED;
411 }
412
413 List<String> realmNames = realm.getRealmNames();
414 String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name.
415
416 // Update realm name if:
417 // Current realm name is empty
418 // The passed in realm name is not empty
419 if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) {
420 updateRealmColumn(realm.getRealmId(), "realm_name", realmName, connection);
421 updateStatusCode = OsRealmUpdateStatus.UPDATED;
422 }
423
424 // if nothing is to be changed, return
425 if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) {
426 return new OsRealmUpdateResult(updateStatusCode, realm);
427 }
428
429 // update realm signature - based on the most current address and name
430 OsAccountRealm currRealm = getRealmByRealmId(realm.getRealmId(), connection);
431 String newRealmAddr = currRealm.getRealmAddr().orElse(null);
432 String newRealmName = (currRealm.getRealmNames().isEmpty() == false) ? currRealm.getRealmNames().get(0) : null;
433
434 // make new signature
435 String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null));
436
437 // Use a random string as the signature if the realm is not active.
438 String updateSQL = "UPDATE tsk_os_account_realms SET "
439 + " realm_signature = "
440 + " CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() + " THEN ? ELSE realm_signature END "
441 + " WHERE id = ?";
442 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
443 preparedStatement.clearParameters();
444
445 preparedStatement.setString(1, newSignature); // Is only set for active accounts
446 preparedStatement.setLong(2, realm.getRealmId());
447 connection.executeUpdate(preparedStatement);
448
449 // read the updated realm
450 updatedRealm = this.getRealmByRealmId(realm.getRealmId(), connection);
451
452 return new OsRealmUpdateResult(updateStatusCode, updatedRealm);
453 } catch (SQLException ex) {
454 throw new TskCoreException(String.format("Error updating realm with id = %d, name = %s, addr = %s", realm.getRealmId(), realmName != null ? realmName : "Null", realm.getRealmAddr().orElse("Null")), ex);
455 } finally {
456 db.releaseSingleUserCaseWriteLock();
457 }
458
459 }
460
473 private <T> void updateRealmColumn(long realmId, String colName, T colValue, CaseDbConnection connection) throws SQLException, TskCoreException {
474
475 String updateSQL = "UPDATE tsk_os_account_realms "
476 + " SET " + colName + " = ? "
477 + " WHERE id = ?";
478
479 db.acquireSingleUserCaseWriteLock();
480 try {
481 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
482 preparedStatement.clearParameters();
483
484 if (Objects.isNull(colValue)) {
485 preparedStatement.setNull(1, Types.NULL); // handle null value
486 } else {
487 if (colValue instanceof String) {
488 preparedStatement.setString(1, (String) colValue);
489 } else if (colValue instanceof Long) {
490 preparedStatement.setLong(1, (Long) colValue);
491 } else if (colValue instanceof Integer) {
492 preparedStatement.setInt(1, (Integer) colValue);
493 } else {
494 throw new TskCoreException(String.format("Unhandled column data type received while updating the realm (id = %d) ", realmId));
495 }
496 }
497
498 preparedStatement.setLong(2, realmId);
499
500 connection.executeUpdate(preparedStatement);
501 } finally {
502 db.releaseSingleUserCaseWriteLock();
503 }
504 }
505
506 private final static String REALM_QUERY_STRING = "SELECT realms.id as realm_id, realms.realm_name as realm_name,"
507 + " realms.realm_addr as realm_addr, realms.realm_signature as realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status,"
508 + " hosts.id, hosts.name as host_name "
509 + " FROM tsk_os_account_realms as realms"
510 + " LEFT JOIN tsk_hosts as hosts"
511 + " ON realms.scope_host_id = hosts.id";
512
521
523 try (CaseDbConnection connection = this.db.getConnection()) {
524 return getRealmByRealmId(id, connection);
525 }
526 }
527
537 OsAccountRealm getRealmByRealmId(long id, CaseDbConnection connection) throws TskCoreException {
538
539 String queryString = REALM_QUERY_STRING
540 + " WHERE realms.id = " + id;
541
543 try ( Statement s = connection.createStatement();
544 ResultSet rs = connection.executeQuery(s, queryString)) {
545 OsAccountRealm accountRealm = null;
546 if (rs.next()) {
547 accountRealm = resultSetToAccountRealm(rs);
548 } else {
549 throw new TskCoreException(String.format("No realm found with id = %d", id));
550 }
551
552 return accountRealm;
553 } catch (SQLException ex) {
554 throw new TskCoreException(String.format("Error running the realms query = %s", queryString), ex);
555 }
556 finally {
557 db.releaseSingleUserCaseReadLock();
558 }
559 }
560
572 Optional<OsAccountRealm> getRealmByAddr(String realmAddr, Host host, CaseDbConnection connection) throws TskCoreException {
573
574 // If a host is specified, we want to match the realm with matching addr and specified host, or a realm with matching addr and no host.
575 // If no host is specified, then we return the first realm with matching addr.
576 String whereHostClause = (host == null)
577 ? " 1 = 1 "
578 : " ( realms.scope_host_id = " + host.getHostId() + " OR realms.scope_host_id IS NULL) ";
579 String queryString = REALM_QUERY_STRING
580 + " WHERE realms.realm_addr = '"+ realmAddr + "' "
581 + " AND " + whereHostClause
582 + " AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
583 + " ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id"; // ensure that non null host_id is at the front
584
585 return getRealmUsingQuery(queryString, host, connection);
586 }
587
600 Optional<OsAccountRealm> getAnotherRealmByAddr(OsAccountRealm realm, String realmAddr, Host host, CaseDbConnection connection) throws TskCoreException {
601
602 // If the given realm has a host id, then the other realm should have the same host id
603 // If the given realm has no host id, then the other realm should have no host id
604 String whereHostClause = realm.getScopeHost().isPresent()
605 ? " ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() + " ) "
606 : " realms.scope_host_id IS NULL ";
607 String queryString = REALM_QUERY_STRING
608 + " WHERE realms.realm_addr = '"+ realmAddr + "' "
609 + " AND " + whereHostClause
610 + " AND realms.id <> " + realm.getRealmId()
611 + " AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
612 + " ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id"; // ensure that non null host_id is at the front
613
614 return getRealmUsingQuery(queryString, host, connection);
615 }
616
627 Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConnection connection) throws TskCoreException {
628
629 // If a host is specified, we want to match the realm with matching name and specified host, or a realm with matching name and no host.
630 // If no host is specified, then we return the first realm with matching name.
631 String whereHostClause = (host == null)
632 ? " 1 = 1 "
633 : " ( realms.scope_host_id = " + host.getHostId() + " OR realms.scope_host_id IS NULL ) ";
634 String queryString = REALM_QUERY_STRING
635 + " WHERE realms.realm_name = '" + realmName + "'"
636 + " AND " + whereHostClause
637 + " AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
638 + " ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id"; // ensure that non null host_id are at the front
639
640 return getRealmUsingQuery(queryString, host, connection);
641 }
642
655 Optional<OsAccountRealm> getAnotherRealmByName(OsAccountRealm realm, String realmName, Host host, CaseDbConnection connection) throws TskCoreException {
656
657 // If the given realm has a host id, then the other realm should have the same host id
658 // If the given realm has no host id, then the other realm should have no host id
659 String whereHostClause = realm.getScopeHost().isPresent()
660 ? " ( realms.scope_host_id = " + realm.getScopeHost().get().getHostId() + " ) "
661 : " realms.scope_host_id IS NULL ";
662 String queryString = REALM_QUERY_STRING
663 + " WHERE realms.realm_name = '" + realmName + "'"
664 + " AND " + whereHostClause
665 + " AND realms.id <> " + realm.getRealmId()
666 + " AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId()
667 + " ORDER BY realms.scope_host_id IS NOT NULL, realms.scope_host_id"; // ensure that non null host_id are at the front
668
669 return getRealmUsingQuery(queryString, host, connection);
670
671 }
672
684 private Optional<OsAccountRealm> getRealmUsingQuery(String queryString, Host host, CaseDbConnection connection) throws TskCoreException {
685
686 db.acquireSingleUserCaseReadLock();
687 try (Statement s = connection.createStatement();
688 ResultSet rs = connection.executeQuery(s, queryString)) {
689
690 OsAccountRealm accountRealm = null;
691 if (rs.next()) {
692 Host realmHost = null;
693 long hostId = rs.getLong("scope_host_id");
694 if (!rs.wasNull()) {
695 if (host != null ) {
696 realmHost = host;
697 } else {
698 realmHost = new Host(hostId, rs.getString("host_name"));
699 }
700 }
701
702 accountRealm = new OsAccountRealm(rs.getLong("realm_id"), rs.getString("realm_name"),
703 rs.getString("realm_addr"), rs.getString("realm_signature"),
704 realmHost, ScopeConfidence.fromID(rs.getInt("scope_confidence")),
705 OsAccountRealm.RealmDbStatus.fromID(rs.getInt("db_status")));
706
707 }
708 return Optional.ofNullable(accountRealm);
709 } catch (SQLException ex) {
710 throw new TskCoreException(String.format("Error getting realm using query = %s", queryString), ex);
711 } finally {
712 db.releaseSingleUserCaseReadLock();
713 }
714 }
715
730
731 private boolean isHostRealmKnown(Host host) throws TskCoreException {
732
733 // check if this host has a local known realm aleady, other than the special windows realm.
734 String queryString = REALM_QUERY_STRING
735 + " WHERE realms.scope_host_id = " + host.getHostId()
736 + " AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId()
737 + " AND realms.db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId();
738
739 db.acquireSingleUserCaseReadLock();
740 try (CaseDbConnection connection = this.db.getConnection();
741 Statement s = connection.createStatement();
742 ResultSet rs = connection.executeQuery(s, queryString)) {
743
744 // return true if there is any match.
745 return rs.next();
746 } catch (SQLException ex) {
747 throw new TskCoreException(String.format("Error getting account realm for with host = %s", host.getName()), ex);
748 }
749 finally {
750 db.releaseSingleUserCaseReadLock();
751 }
752
753 }
754
762 private OsAccountRealm resultSetToAccountRealm(ResultSet rs) throws SQLException {
763
764 long hostId = rs.getLong("scope_host_id");
765 Host realmHost = null;
766 if (!rs.wasNull()) {
767 realmHost = new Host(hostId, rs.getString("host_name"));
768 }
769
770 return new OsAccountRealm(rs.getLong("realm_id"), rs.getString("realm_name"),
771 rs.getString("realm_addr"), rs.getString("realm_signature"),
772 realmHost, ScopeConfidence.fromID(rs.getInt("scope_confidence")),
773 OsAccountRealm.RealmDbStatus.fromID(rs.getInt("db_status")));
774 }
775
776// /**
777// * Get all realms.
778// *
779// * @return Collection of OsAccountRealm
780// */
781// Collection<OsAccountRealm> getRealms() throws TskCoreException {
782// String queryString = "SELECT realms.id as realm_id, realms.realm_name as realm_name, realms.realm_addr as realm_addr, realms.scope_host_id, realms.scope_confidence, "
783// + " hosts.id, hosts.name as host_name "
784// + " FROM tsk_os_account_realms as realms"
785// + " LEFT JOIN tsk_hosts as hosts"
786// + " ON realms.scope_host_id = hosts.id";
787//
788// db.acquireSingleUserCaseReadLock();
789// try (CaseDbConnection connection = this.db.getConnection();
790// Statement s = connection.createStatement();
791// ResultSet rs = connection.executeQuery(s, queryString)) {
792//
793// ArrayList<OsAccountRealm> accountRealms = new ArrayList<>();
794// while (rs.next()) {
795// long hostId = rs.getLong("scope_host_id");
796// Host host = null;
797// if (!rs.wasNull()) {
798// host = new Host(hostId, rs.getString("host_name"));
799// }
800//
801// accountRealms.add(new OsAccountRealm(rs.getLong("realm_id"), rs.getString("realm_name"),
802// ScopeConfidence.fromID(rs.getInt("scope_confidence")),
803// rs.getString("realm_addr"), host));
804// }
805//
806// return accountRealms;
807// } catch (SQLException ex) {
808// throw new TskCoreException(String.format("Error running the realms query = %s", queryString), ex);
809// }
810// finally {
811// db.releaseSingleUserCaseReadLock();
812// }
813// }
814
815
833 private OsAccountRealm newRealm(String realmName, String realmAddr, String signature, Host host, OsAccountRealm.ScopeConfidence scopeConfidence) throws TskCoreException {
834
835 db.acquireSingleUserCaseWriteLock();
836 try (CaseDbConnection connection = this.db.getConnection()) {
837 String realmInsertSQL = "INSERT INTO tsk_os_account_realms(realm_name, realm_addr, realm_signature, scope_host_id, scope_confidence)"
838 + " VALUES (?, ?, ?, ?, ?)"; // NON-NLS
839
840 PreparedStatement preparedStatement = connection.getPreparedStatement(realmInsertSQL, Statement.RETURN_GENERATED_KEYS);
841 preparedStatement.clearParameters();
842
843 preparedStatement.setString(1, realmName);
844 preparedStatement.setString(2, realmAddr);
845 preparedStatement.setString(3, signature);
846 if (host != null) {
847 preparedStatement.setLong(4, host.getHostId());
848 } else {
849 preparedStatement.setNull(4, java.sql.Types.BIGINT);
850 }
851 preparedStatement.setInt(5, scopeConfidence.getId());
852
853 connection.executeUpdate(preparedStatement);
854
855 // Read back the row id
856 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
857 long rowId = resultSet.getLong(1); // last_insert_rowid()
858 return new OsAccountRealm(rowId, realmName, realmAddr, signature, host, scopeConfidence, OsAccountRealm.RealmDbStatus.ACTIVE);
859 }
860
861 } catch (SQLException ex) {
862 // Create may have failed if the realm already exists. Try and get the matching realm
863 try (CaseDbConnection connection = this.db.getConnection()) {
864 if (!Strings.isNullOrEmpty(realmAddr)) {
865 Optional<OsAccountRealm> accountRealm = this.getRealmByAddr(realmAddr, host, connection);
866 if (accountRealm.isPresent()) {
867 return accountRealm.get();
868 }
869 } else if (!Strings.isNullOrEmpty(realmName)) {
870 Optional<OsAccountRealm> accountRealm = this.getRealmByName(realmName, host, connection);
871 if (accountRealm.isPresent()) {
872 return accountRealm.get();
873 }
874 }
875
876 // some other failure - throw an exception
877 throw new TskCoreException(String.format("Error creating realm with address = %s and name = %s, with host = %s",
878 realmAddr != null ? realmAddr : "", realmName != null ? realmName : "", host != null ? host.getName() : ""), ex);
879 }
880 } finally {
881 db.releaseSingleUserCaseWriteLock();
882 }
883 }
884
885
902 static String makeRealmSignature(String realmAddr, String realmName, Host scopeHost) throws TskCoreException {
903
904 // need at least one of the two, the addr or name to look up
905 if (Strings.isNullOrEmpty(realmAddr) && Strings.isNullOrEmpty(realmName)) {
906 throw new TskCoreException("Realm address and name can't both be null.");
907 }
908
909 String signature = String.format("%s_%s", !Strings.isNullOrEmpty(realmAddr) ? realmAddr : realmName,
910 scopeHost != null ? scopeHost.getHostId() : "DOMAIN");
911 return signature;
912 }
913
919 private String makeMergedRealmSignature() {
920 return "MERGED " + UUID.randomUUID().toString();
921 }
922
923
932 void moveOrMergeRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans) throws TskCoreException {
933 // Look for a matching realm by address
934 Optional<OsAccountRealm> optDestRealmAddr = Optional.empty();
935 if (sourceRealm.getRealmAddr().isPresent()) {
936 optDestRealmAddr = db.getOsAccountRealmManager().getRealmByAddr(sourceRealm.getRealmAddr().get(), destHost, trans.getConnection());
937 }
938
939 // Look for a matching realm by name
940 Optional<OsAccountRealm> optDestRealmName = Optional.empty();
941 if (!sourceRealm.getRealmNames().isEmpty()) {
942 optDestRealmName = db.getOsAccountRealmManager().getRealmByName(sourceRealm.getRealmNames().get(0), destHost, trans.getConnection());
943 }
944
945 // Decide how to proceed:
946 // - If we only got one match:
947 // -- If the address matched, set destRealm to the matching address realm
948 // -- If the name matched but the original and the matching realm have different addresses, leave destRealm null (it'll be a move)
949 // -- If the name matched and at least one of the address fields was null, set destRealm to the matching name realm
950 // - If we got no matches, leave destRealm null (we'll do a move not a merge)
951 // - If we got two of the same matches, set destRealm to that realm
952 // - If we got two different matches:
953 // -- If the name match has no address set, merge the matching name realm into the matching address realm, then
954 // set destRealm to the matching address realm
955 // -- Otherwise we're in the case where the addresses are different. We will consider the address the
956 // stronger match and set destRealm to the matching address realm and leave the matching name realm as-is.
957 OsAccountRealm destRealm = null;
958 if (optDestRealmAddr.isPresent() && optDestRealmName.isPresent()) {
959 if (optDestRealmAddr.get().getRealmId() == optDestRealmName.get().getRealmId()) {
960 // The two matches are the same
961 destRealm = optDestRealmAddr.get();
962 } else {
963 if (optDestRealmName.get().getRealmAddr().isPresent()) {
964 // The addresses are different, so use the one with the matching address
965 destRealm = optDestRealmAddr.get();
966 } else {
967 // Merge the realm with the matching name into the realm with the matching address.
968 // Reload from database afterward to make sure everything is up-to-date.
969 mergeRealms(optDestRealmName.get(), optDestRealmAddr.get(), trans);
970 destRealm = getRealmByRealmId(optDestRealmAddr.get().getRealmId(), trans.getConnection());
971 }
972 }
973 } else if (optDestRealmAddr.isPresent()) {
974 // Only address matched - use it
975 destRealm = optDestRealmAddr.get();
976 } else if (optDestRealmName.isPresent()) {
977 // Only name matched - check whether both have addresses set.
978 // Due to earlier checks we know the address fields can't be the same, so
979 // don't do anything if both have addresses - we consider the address to be a stronger identifier than the name
980 if (! (optDestRealmName.get().getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent())) {
981 destRealm = optDestRealmName.get();
982 }
983 }
984
985 // Move or merge the source realm
986 if (destRealm == null) {
987 moveRealm(sourceRealm, destHost, trans);
988 } else {
989 mergeRealms(sourceRealm, destRealm, trans);
990 }
991 }
992
1004 private void moveRealm(OsAccountRealm sourceRealm, Host destHost, CaseDbTransaction trans) throws TskCoreException {
1005 try(Statement s = trans.getConnection().createStatement()) {
1006 String query = "UPDATE tsk_os_account_realms SET scope_host_id = " + destHost.getHostId() + " WHERE id = " + sourceRealm.getRealmId();
1007 s.executeUpdate(query);
1008 } catch (SQLException ex) {
1009 throw new TskCoreException("Error moving realm with id: " + sourceRealm.getRealmId() + " to host with id: " + destHost.getHostId(), ex);
1010 }
1011 }
1012
1013
1023 void mergeRealms(OsAccountRealm sourceRealm, OsAccountRealm destRealm, CaseDbTransaction trans) throws TskCoreException {
1024
1025 // Update accounts
1026 db.getOsAccountManager().mergeOsAccountsForRealms(sourceRealm, destRealm, trans);
1027
1028 // Update the sourceRealm realm
1029 CaseDbConnection connection = trans.getConnection();
1030 try (Statement statement = connection.createStatement()) {
1031 String updateStr = "UPDATE tsk_os_account_realms SET db_status = " + OsAccountRealm.RealmDbStatus.MERGED.getId()
1032 + ", merged_into = " + destRealm.getRealmId()
1033 + ", realm_signature = '" + makeMergedRealmSignature() + "' "
1034 + " WHERE id = " + sourceRealm.getRealmId();
1035 connection.executeUpdate(statement, updateStr);
1036 } catch (SQLException ex) {
1037 throw new TskCoreException ("Error updating status of realm with id: " + sourceRealm.getRealmId(), ex);
1038 }
1039
1040 // Update the destination realm if it doesn't have the name or addr set and the source realm does
1041 if (!destRealm.getRealmAddr().isPresent() && sourceRealm.getRealmAddr().isPresent()) {
1042 updateRealm(destRealm, sourceRealm.getRealmAddr().get(), null, trans.getConnection());
1043 } else if (destRealm.getRealmNames().isEmpty() && !sourceRealm.getRealmNames().isEmpty()) {
1044 updateRealm(destRealm, null, sourceRealm.getRealmNames().get(0), trans.getConnection());
1045 }
1046 }
1047
1058 List<OsAccountRealm> getRealmsByHost(Host host, CaseDbConnection connection) throws TskCoreException {
1059 List<OsAccountRealm> results = new ArrayList<>();
1060 String queryString = REALM_QUERY_STRING
1061 + " WHERE realms.scope_host_id = " + host.getHostId();
1062
1063 db.acquireSingleUserCaseReadLock();
1064 try ( Statement s = connection.createStatement();
1065 ResultSet rs = connection.executeQuery(s, queryString)) {
1066 while (rs.next()) {
1067 results.add(resultSetToAccountRealm(rs));
1068 }
1069 return results;
1070 } catch (SQLException ex) {
1071 throw new TskCoreException(String.format("Error gettings realms for host with id = " + host.getHostId()), ex);
1072 }
1073 finally {
1074 db.releaseSingleUserCaseReadLock();
1075 }
1076 }
1077
1087
1092 public final static class OsRealmUpdateResult {
1093
1094 private final OsRealmUpdateStatus updateStatus;
1095 private final OsAccountRealm updatedRealm;
1096
1097 OsRealmUpdateResult(OsRealmUpdateStatus updateStatus, OsAccountRealm updatedRealm) {
1098 this.updateStatus = updateStatus;
1099 this.updatedRealm = updatedRealm;
1100 }
1101
1103 return updateStatus;
1104 }
1105
1106 public Optional<OsAccountRealm> getUpdatedRealm() {
1107 return Optional.ofNullable(updatedRealm);
1108 }
1109 }
1110}
OsAccountRealm newLocalLinuxRealm(Host referringHost)
OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName)
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
Optional< OsAccountRealm > getLocalLinuxRealm(Host referringHost)
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)

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