Sleuth Kit Java Bindings (JNI) 4.14.0
Java bindings for using The Sleuth Kit
Loading...
Searching...
No Matches
HostManager.java
Go to the documentation of this file.
1/*
2 * Sleuth Kit Data Model
3 *
4 * Copyright 2020-2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.datamodel;
20
21import com.google.common.base.Strings;
22import java.sql.PreparedStatement;
23import java.sql.ResultSet;
24import java.sql.Savepoint;
25import java.sql.SQLException;
26import java.sql.Statement;
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.List;
30import java.util.Optional;
31import java.util.UUID;
32import org.sleuthkit.datamodel.Host.HostDbStatus;
33import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
34import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
35import org.sleuthkit.datamodel.TskEvent.HostsUpdatedTskEvent;
36import org.sleuthkit.datamodel.TskEvent.HostsDeletedTskEvent;
37
41public final class HostManager {
42
43 private final SleuthkitCase db;
44
51 HostManager(SleuthkitCase skCase) {
52 this.db = skCase;
53 }
54
65 public Host newHost(String name) throws TskCoreException {
66 CaseDbTransaction transaction = db.beginTransaction();
67 try {
68 Host host = newHost(name, transaction);
69 transaction.commit();
70 transaction = null;
71 return host;
72 } finally {
73 if (transaction != null) {
74 transaction.rollback();
75 }
76 }
77 }
78
103 Host newHost(String name, CaseDbTransaction trans) throws TskCoreException {
104 // must have a name
105 if (Strings.isNullOrEmpty(name)) {
106 throw new TskCoreException("Illegal argument passed to createHost: Host name is required.");
107 }
108
109 CaseDbConnection connection = trans.getConnection();
110 Savepoint savepoint = null;
111
112 try {
113 savepoint = connection.getConnection().setSavepoint();
114 String hostInsertSQL = "INSERT INTO tsk_hosts(name) VALUES (?)"; // NON-NLS
115 PreparedStatement preparedStatement = connection.getPreparedStatement(hostInsertSQL, Statement.RETURN_GENERATED_KEYS);
116
117 preparedStatement.clearParameters();
118 preparedStatement.setString(1, name);
119
120 connection.executeUpdate(preparedStatement);
121
122 // Read back the row id
123 Host host = null;
124 try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
125 if (resultSet.next()) {
126 host = new Host(resultSet.getLong(1), name); //last_insert_rowid()
127 } else {
128 throw new SQLException("Error executing " + hostInsertSQL);
129 }
130 }
131
132 if (host != null) {
133 trans.registerAddedHost(host);
134 }
135 return host;
136 } catch (SQLException ex) {
137 if (savepoint != null) {
138 try {
139 connection.getConnection().rollback(savepoint);
140 } catch (SQLException ex2) {
141 throw new TskCoreException(String.format("Error adding host with name = %s and unable to rollback", name), ex);
142 }
143 }
144
145 // It may be the case that the host already exists, so try to get it.
146 Optional<Host> optHost = getHostByName(name, connection);
147 if (optHost.isPresent()) {
148 return optHost.get();
149 }
150 throw new TskCoreException(String.format("Error adding host with name = %s", name), ex);
151 }
152 }
153
164 public Host updateHostName(Host host, String newName) throws TskCoreException {
165 if (host == null) {
166 throw new TskCoreException("Illegal argument passed to updateHost: No host argument provided.");
167 } else if (newName == null) {
168 throw new TskCoreException(String.format("Illegal argument passed to updateHost: Host with id %d has no name", host.getHostId()));
169 }
170
171 long hostId = host.getHostId();
172 Host updatedHost = null;
173 db.acquireSingleUserCaseWriteLock();
174 try (CaseDbConnection connection = db.getConnection()) {
175 // Don't update the name for non-active hosts
176 String hostInsertSQL = "UPDATE tsk_hosts "
177 + "SET name = "
178 + " CASE WHEN db_status = " + Host.HostDbStatus.ACTIVE.getId() + " THEN ? ELSE name END "
179 + "WHERE id = ?";
180
181 PreparedStatement preparedStatement = connection.getPreparedStatement(hostInsertSQL, Statement.RETURN_GENERATED_KEYS);
182
183 preparedStatement.clearParameters();
184 preparedStatement.setString(1, newName);
185 preparedStatement.setLong(2, hostId);
186
187 connection.executeUpdate(preparedStatement);
188
189 updatedHost = getHostById(hostId, connection).orElseThrow(()
190 -> new TskCoreException((String.format("Error while fetching newly updated host with id: %d, "))));
191
192 } catch (SQLException ex) {
193 throw new TskCoreException(String.format("Error updating host with name = %s", newName), ex);
194 } finally {
195 db.releaseSingleUserCaseWriteLock();
196 }
197
198 if (updatedHost != null) {
199 fireChangeEvent(updatedHost);
200 }
201 return updatedHost;
202 }
203
213 public Long deleteHost(String name) throws TskCoreException {
214 if (name == null) {
215 throw new TskCoreException("Illegal argument passed to deleteHost: Name provided must be non-null");
216 }
217
218 // query to check if there are any dependencies on this host. If so, don't delete.
219 String queryString = "SELECT COUNT(*) AS count FROM\n"
220 + "(SELECT obj_id AS id, host_id FROM data_source_info\n"
221 + "UNION\n"
222 + "SELECT id, scope_host_id AS host_id FROM tsk_os_account_realms\n"
223 + "UNION\n"
224 + "SELECT id, host_id FROM tsk_os_account_attributes\n"
225 + "UNION\n"
226 + "SELECT id, host_id FROM tsk_host_address_map) children\n"
227 + "INNER JOIN tsk_hosts h ON children.host_id = h.id WHERE LOWER(h.name)=LOWER(?)";
228
229 String deleteString = "DELETE FROM tsk_hosts WHERE LOWER(name) = LOWER(?)";
230
231 CaseDbTransaction trans = this.db.beginTransaction();
232 try {
233 // check if host has any child data sources. if so, don't delete and throw exception.
234 PreparedStatement query = trans.getConnection().getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
235 query.clearParameters();
236 query.setString(1, name);
237 try (ResultSet queryResults = query.executeQuery()) {
238 if (queryResults.next() && queryResults.getLong("count") > 0) {
239 throw new TskCoreException(String.format("Host with name '%s' has child data and cannot be deleted.", name));
240 }
241 }
242
243 // otherwise, delete the host
244 PreparedStatement update = trans.getConnection().getPreparedStatement(deleteString, Statement.RETURN_GENERATED_KEYS);
245 update.clearParameters();
246 update.setString(1, name);
247 int numUpdated = update.executeUpdate();
248
249 // get ids for deleted.
250 Long hostId = null;
251
252 if (numUpdated > 0) {
253 try (ResultSet updateResult = update.getGeneratedKeys()) {
254 if (updateResult.next()) {
255 hostId = updateResult.getLong(1);
256 }
257 }
258 }
259
260 trans.commit();
261 trans = null;
262
263 fireDeletedEvent(new Host(hostId, name));
264 return hostId;
265 } catch (SQLException ex) {
266 throw new TskCoreException(String.format("Error deleting host with name %s", name), ex);
267 } finally {
268 if (trans != null) {
269 trans.rollback();
270 }
271 }
272 }
273
283 public List<DataSource> getDataSourcesForHost(Host host) throws TskCoreException {
284 String queryString = "SELECT * FROM data_source_info WHERE host_id = " + host.getHostId();
285
286 List<DataSource> dataSources = new ArrayList<>();
287 db.acquireSingleUserCaseReadLock();
288 try (CaseDbConnection connection = this.db.getConnection();
289 Statement s = connection.createStatement();
290 ResultSet rs = connection.executeQuery(s, queryString)) {
291
292 while (rs.next()) {
293 dataSources.add(db.getDataSource(rs.getLong("obj_id")));
294 }
295
296 return dataSources;
297 } catch (SQLException | TskDataException ex) {
298 throw new TskCoreException(String.format("Error getting data sources for host %s", host.getName()), ex);
299 } finally {
300 db.releaseSingleUserCaseReadLock();
301 }
302 }
303
313 public Optional<Host> getHostByName(String name) throws TskCoreException {
314 try (CaseDbConnection connection = db.getConnection()) {
315 return getHostByName(name, connection);
316 }
317 }
318
329 private Optional<Host> getHostByName(String name, CaseDbConnection connection) throws TskCoreException {
330
331 String queryString = "SELECT * FROM tsk_hosts"
332 + " WHERE LOWER(name) = LOWER(?)"
333 + " AND db_status = " + Host.HostDbStatus.ACTIVE.getId();
334
336 try {
337 PreparedStatement s = connection.getPreparedStatement(queryString, Statement.RETURN_GENERATED_KEYS);
338 s.clearParameters();
339 s.setString(1, name);
340
341 try (ResultSet rs = s.executeQuery()) {
342 if (!rs.next()) {
343 return Optional.empty(); // no match found
344 } else {
345 return Optional.of(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
346 }
347 }
348 } catch (SQLException ex) {
349 throw new TskCoreException(String.format("Error getting host with name = %s", name), ex);
350 } finally {
351 db.releaseSingleUserCaseReadLock();
352 }
353 }
354
364 public Optional<Host> getHostById(long id) throws TskCoreException {
365 try (CaseDbConnection connection = db.getConnection()) {
366 return getHostById(id, connection);
367 }
368 }
369
380 private Optional<Host> getHostById(long id, CaseDbConnection connection) throws TskCoreException {
381
382 String queryString = "SELECT * FROM tsk_hosts WHERE id = " + id;
383
385 try (Statement s = connection.createStatement();
386 ResultSet rs = connection.executeQuery(s, queryString)) {
387
388 if (rs.next()) {
389 return Optional.of(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
390 } else {
391 return Optional.empty();
392 }
393 } catch (SQLException ex) {
394 throw new TskCoreException(String.format("Error getting host with id: " + id), ex);
395 } finally {
396 db.releaseSingleUserCaseReadLock();
397 }
398 }
399
407 public List<Host> getAllHosts() throws TskCoreException {
408 String queryString = "SELECT * FROM tsk_hosts WHERE db_status = " + HostDbStatus.ACTIVE.getId();
409
410 List<Host> hosts = new ArrayList<>();
411 db.acquireSingleUserCaseReadLock();
412 try (CaseDbConnection connection = this.db.getConnection();
413 Statement s = connection.createStatement();
414 ResultSet rs = connection.executeQuery(s, queryString)) {
415
416 while (rs.next()) {
417 hosts.add(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
418 }
419
420 return hosts;
421 } catch (SQLException ex) {
422 throw new TskCoreException(String.format("Error getting hosts"), ex);
423 } finally {
424 db.releaseSingleUserCaseReadLock();
425 }
426 }
427
438 return getHostByDataSource(dataSource.getId());
439 }
440
450 Host getHostByDataSource(long dataSourceId) throws TskCoreException {
451 String queryString = "SELECT tsk_hosts.id AS hostId, tsk_hosts.name AS name, tsk_hosts.db_status AS db_status FROM \n"
452 + "tsk_hosts INNER JOIN data_source_info \n"
453 + "ON tsk_hosts.id = data_source_info.host_id \n"
454 + "WHERE data_source_info.obj_id = " + dataSourceId;
455
457 try (CaseDbConnection connection = this.db.getConnection();
458 Statement s = connection.createStatement();
459 ResultSet rs = connection.executeQuery(s, queryString)) {
460
461 if (!rs.next()) {
462 throw new TskCoreException(String.format("Host not found for data source with ID = %d", dataSourceId));
463 } else {
464 return new Host(rs.getLong("hostId"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status")));
465 }
466 } catch (SQLException ex) {
467 throw new TskCoreException(String.format("Error getting host for data source with ID = %d", dataSourceId), ex);
468 } finally {
469 db.releaseSingleUserCaseReadLock();
470 }
471 }
472
486 public void mergeHosts(Host sourceHost, Host destHost) throws TskCoreException {
487 String query = "";
488 CaseDbTransaction trans = null;
489 try {
490 trans = db.beginTransaction();
491
492 // Merge or move any realms associated with the source host
493 List<OsAccountRealm> realms = db.getOsAccountRealmManager().getRealmsByHost(sourceHost, trans.getConnection());
494 for (OsAccountRealm realm : realms) {
495 db.getOsAccountRealmManager().moveOrMergeRealm(realm, destHost, trans);
496 }
497
498 try (Statement s = trans.getConnection().createStatement()) {
499 // Update references to the source host
500
501 // tsk_host_address_map has a unique constraint on host_id, addr_obj_id, time,
502 // so delete any rows that would be duplicates.
503 query = "DELETE FROM tsk_host_address_map "
504 + "WHERE id IN ( "
505 + "SELECT "
506 + " sourceMapRow.id "
507 + "FROM "
508 + " tsk_host_address_map destMapRow "
509 + "INNER JOIN tsk_host_address_map sourceMapRow ON destMapRow.addr_obj_id = sourceMapRow.addr_obj_id AND destMapRow.time = sourceMapRow.time "
510 + "WHERE destMapRow.host_id = " + destHost.getHostId()
511 + " AND sourceMapRow.host_id = " + sourceHost.getHostId() + " )";
512 s.executeUpdate(query);
513 query = makeOsAccountUpdateQuery("tsk_host_address_map", "host_id", sourceHost, destHost);
514 s.executeUpdate(query);
515
516 query = makeOsAccountUpdateQuery("tsk_os_account_attributes", "host_id", sourceHost, destHost);
517 s.executeUpdate(query);
518
519 query = makeOsAccountUpdateQuery("data_source_info", "host_id", sourceHost, destHost);
520 s.executeUpdate(query);
521
522 // Mark the source host as merged and change the name to a random string.
523 String mergedName = makeMergedHostName();
524 query = "UPDATE tsk_hosts SET merged_into = " + destHost.getHostId()
525 + ", db_status = " + Host.HostDbStatus.MERGED.getId()
526 + ", name = '" + mergedName + "' "
527 + " WHERE id = " + sourceHost.getHostId();
528 s.executeUpdate(query);
529 }
530
531 trans.commit();
532 trans = null;
533
534 // Fire events for updated and deleted hosts
535 fireChangeEvent(sourceHost);
536 fireDeletedEvent(destHost);
537 } catch (SQLException ex) {
538 throw new TskCoreException("Error executing query: " + query, ex);
539 } finally {
540 if (trans != null) {
541 trans.rollback();
542 }
543 }
544 }
545
556 private String makeOsAccountUpdateQuery(String tableName, String columnName, Host sourceHost, Host destHost) {
557 return "UPDATE " + tableName + " SET " + columnName + " = " + destHost.getHostId() + " WHERE " + columnName + " = " + sourceHost.getHostId();
558 }
559
565 private String makeMergedHostName() {
566 return "MERGED " + UUID.randomUUID().toString();
567 }
568
575 private void fireChangeEvent(Host newValue) {
576 db.fireTSKEvent(new HostsUpdatedTskEvent(Collections.singletonList(newValue)));
577 }
578
585 private void fireDeletedEvent(Host deleted) {
586 db.fireTSKEvent(new HostsDeletedTskEvent(Collections.singletonList(deleted.getHostId())));
587 }
588}
List< DataSource > getDataSourcesForHost(Host host)
Host updateHostName(Host host, String newName)
Optional< Host > getHostById(long id)
Host getHostByDataSource(DataSource dataSource)
void mergeHosts(Host sourceHost, Host destHost)
Optional< Host > getHostByName(String name)
static HostDbStatus fromID(int typeId)
Definition Host.java:125

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.