Sleuth Kit Java Bindings (JNI)  4.12.0
Java bindings for using The Sleuth Kit
CaseDatabaseFactory.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2020-2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
21 import java.io.File;
22 import java.io.UnsupportedEncodingException;
23 import java.net.URLEncoder;
24 import java.sql.Connection;
25 import java.sql.DriverManager;
26 import java.sql.SQLException;
27 import java.sql.Statement;
28 import java.util.Properties;
29 import java.util.logging.Logger;
30 import java.util.logging.Level;
33 
37 class CaseDatabaseFactory {
38 
39  private static final Logger logger = Logger.getLogger(CaseDatabaseFactory.class.getName());
40  private final SQLHelper dbQueryHelper;
41  private final DbCreationHelper dbCreationHelper;
42 
48  CaseDatabaseFactory(String dbPath) {
49  this.dbQueryHelper = new SQLiteHelper();
50  this.dbCreationHelper = new SQLiteDbCreationHelper(dbPath);
51  }
52 
63  CaseDatabaseFactory(String caseName, CaseDbConnectionInfo info) {
64  this.dbQueryHelper = new PostgreSQLHelper();
65  this.dbCreationHelper = new PostgreSQLDbCreationHelper(caseName, info);
66  }
67 
74  void createCaseDatabase() throws TskCoreException {
75  createDatabase();
76  initializeSchema();
77  }
78 
84  private void createDatabase() throws TskCoreException {
85  dbCreationHelper.createDatabase();
86  }
87 
93  private void initializeSchema() throws TskCoreException {
94  try (Connection conn = dbCreationHelper.getConnection()) {
95  // Perform any needed steps before creating the tables
96  dbCreationHelper.performPreInitialization(conn);
97 
98  // Add schema version
99  addDbInfo(conn);
100 
101  // Add tables
102  addTables(conn);
103  dbCreationHelper.performPostTableInitialization(conn);
104 
105  // Add indexes
106  addIndexes(conn);
107  } catch (SQLException ex) {
108  throw new TskCoreException("Error initializing case database", ex);
109  }
110  }
111 
119  private void addDbInfo(Connection conn) throws TskCoreException {
120  CaseDbSchemaVersionNumber version = SleuthkitCase.CURRENT_DB_SCHEMA_VERSION;
121  long tskVersionNum = SleuthkitJNI.getSleuthkitVersion(); // This is the current version of TSK
122 
123  try (Statement stmt = conn.createStatement()) {
124  stmt.execute("CREATE TABLE tsk_db_info (schema_ver INTEGER, tsk_ver INTEGER, schema_minor_ver INTEGER)");
125  stmt.execute("INSERT INTO tsk_db_info (schema_ver, tsk_ver, schema_minor_ver) VALUES (" +
126  version.getMajor() + ", " + tskVersionNum + ", " + version.getMinor() + ");");
127 
128  stmt.execute("CREATE TABLE tsk_db_info_extended (name TEXT PRIMARY KEY, value TEXT NOT NULL);");
129  stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('TSK_VERSION', '" + tskVersionNum + "');");
130  stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('SCHEMA_MAJOR_VERSION', '" + version.getMajor() + "');");
131  stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + version.getMinor() + "');");
132  stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('CREATION_SCHEMA_MAJOR_VERSION', '" + version.getMajor() + "');");
133  stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('CREATION_SCHEMA_MINOR_VERSION', '" + version.getMinor() + "');");
134  } catch (SQLException ex) {
135  throw new TskCoreException("Error initializing db_info tables", ex);
136  }
137  }
138 
146  private void addTables(Connection conn) throws TskCoreException {
147  try (Statement stmt = conn.createStatement()) {
148  createTskObjects(stmt);
149  createHostTables(stmt);
150  createAccountTables(stmt);
151  createFileTables(stmt);
152  createArtifactTables(stmt);
153  createAnalysisResultsTables(stmt);
154  createTagTables(stmt);
155  createIngestTables(stmt);
156  createEventTables(stmt);
157  createAttributeTables(stmt);
158  createAccountInstancesAndArtifacts(stmt);
159  } catch (SQLException ex) {
160  throw new TskCoreException("Error initializing tables", ex);
161  }
162  }
163 
164  // tsk_objects is referenced by many other tables and should be created first
165  private void createTskObjects(Statement stmt) throws SQLException {
166  // The UNIQUE here on the object ID is to create an index
167  stmt.execute("CREATE TABLE tsk_objects (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, par_obj_id " + dbQueryHelper.getBigIntType()
168  + ", type INTEGER NOT NULL, UNIQUE (obj_id), FOREIGN KEY (par_obj_id) REFERENCES tsk_objects (obj_id) ON DELETE CASCADE)");
169  }
170 
171  private void createFileTables(Statement stmt) throws SQLException {
172 
173  stmt.execute("CREATE TABLE tsk_image_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, type INTEGER, ssize INTEGER, "
174  + "tzone TEXT, size " + dbQueryHelper.getBigIntType() + ", md5 TEXT, sha1 TEXT, sha256 TEXT, display_name TEXT, "
175  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
176 
177  stmt.execute("CREATE TABLE tsk_image_names (obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, name TEXT NOT NULL, "
178  + "sequence INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
179 
180  stmt.execute("CREATE TABLE tsk_vs_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, vs_type INTEGER NOT NULL, "
181  + "img_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, block_size " + dbQueryHelper.getBigIntType() + " NOT NULL, "
182  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
183 
184  stmt.execute("CREATE TABLE tsk_vs_parts (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
185  + "addr " + dbQueryHelper.getBigIntType() + " NOT NULL, start " + dbQueryHelper.getBigIntType() + " NOT NULL, "
186  + "length " + dbQueryHelper.getBigIntType() + " NOT NULL, "
187  + dbQueryHelper.getVSDescColName() + " TEXT, "
188  + "flags INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
189 
190  stmt.execute("CREATE TABLE tsk_pool_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
191  + "pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
192 
193  stmt.execute("CREATE TABLE data_source_info (obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, device_id TEXT NOT NULL, "
194  + "time_zone TEXT NOT NULL, acquisition_details TEXT, added_date_time "+ dbQueryHelper.getBigIntType() + ", "
195  + "acquisition_tool_settings TEXT, acquisition_tool_name TEXT, acquisition_tool_version TEXT, "
196  + "host_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
197  + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id), "
198  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
199 
200  stmt.execute("CREATE TABLE tsk_fs_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
201  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
202  + "img_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, fs_type INTEGER NOT NULL, "
203  + "block_size " + dbQueryHelper.getBigIntType() + " NOT NULL, "
204  + "block_count " + dbQueryHelper.getBigIntType() + " NOT NULL, root_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, "
205  + "first_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, last_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, "
206  + "display_name TEXT, "
207  + "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
208  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
209 
210  stmt.execute("CREATE TABLE file_collection_status_types (collection_status_type INTEGER PRIMARY KEY, name TEXT NOT NULL)");
211 
212  stmt.execute("CREATE TABLE tsk_files (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
213  + "fs_obj_id " + dbQueryHelper.getBigIntType() + ", data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
214  + "attr_type INTEGER, attr_id INTEGER, "
215  + "name TEXT NOT NULL, meta_addr " + dbQueryHelper.getBigIntType() + ", meta_seq " + dbQueryHelper.getBigIntType() + ", "
216  + "type INTEGER, has_layout INTEGER, has_path INTEGER, "
217  + "dir_type INTEGER, meta_type INTEGER, dir_flags INTEGER, meta_flags INTEGER, size " + dbQueryHelper.getBigIntType() + ", "
218  + "ctime " + dbQueryHelper.getBigIntType() + ", "
219  + "crtime " + dbQueryHelper.getBigIntType() + ", atime " + dbQueryHelper.getBigIntType() + ", "
220  + "mtime " + dbQueryHelper.getBigIntType() + ", mode INTEGER, uid INTEGER, gid INTEGER, md5 TEXT, sha256 TEXT, sha1 TEXT,"
221  + "known INTEGER, "
222  + "parent_path TEXT, mime_type TEXT, extension TEXT, "
223  + "owner_uid TEXT DEFAULT NULL, "
224  + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
225  + "collected INTEGER NOT NULL, "
226  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
227  + "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, "
228  + "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
229  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL, "
230  + "FOREIGN KEY(collected) REFERENCES file_collection_status_types (collection_status_type))" );
231 
232  stmt.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL)");
233 
234  stmt.execute("CREATE TABLE tsk_files_path (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, path TEXT NOT NULL, "
235  + "encoding_type INTEGER NOT NULL, FOREIGN KEY(encoding_type) references file_encoding_types(encoding_type), "
236  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
237 
238  stmt.execute("CREATE TABLE tsk_files_derived (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
239  + "derived_id " + dbQueryHelper.getBigIntType() + " NOT NULL, rederive TEXT, "
240  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
241 
242  stmt.execute("CREATE TABLE tsk_files_derived_method (derived_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
243  + "tool_name TEXT NOT NULL, tool_version TEXT NOT NULL, other TEXT)");
244 
245  stmt.execute("CREATE TABLE tsk_file_layout (obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
246  + "byte_start " + dbQueryHelper.getBigIntType() + " NOT NULL, byte_len " + dbQueryHelper.getBigIntType() + " NOT NULL, "
247  + "sequence INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
248 
249  stmt.execute("CREATE TABLE reports (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, path TEXT NOT NULL, "
250  + "crtime INTEGER NOT NULL, src_module_name TEXT NOT NULL, report_name TEXT NOT NULL, "
251  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
252  }
253 
254  private void createArtifactTables(Statement stmt) throws SQLException {
255  stmt.execute("CREATE TABLE blackboard_artifact_types (artifact_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
256  + "type_name TEXT NOT NULL, display_name TEXT,"
257  + "category_type INTEGER DEFAULT 0)");
258 
259  stmt.execute("CREATE TABLE blackboard_attribute_types (attribute_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
260  + "type_name TEXT NOT NULL, display_name TEXT, value_type INTEGER NOT NULL)");
261 
262  stmt.execute("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, "
263  + "review_status_name TEXT NOT NULL, "
264  + "display_name TEXT NOT NULL)");
265 
266  stmt.execute("CREATE TABLE blackboard_artifacts (artifact_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
267  + "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
268  + "artifact_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
269  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + ", "
270  + "artifact_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
271  + "review_status_id INTEGER NOT NULL, "
272  + "UNIQUE (artifact_obj_id),"
273  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
274  + "FOREIGN KEY(artifact_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
275  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
276  + "FOREIGN KEY(artifact_type_id) REFERENCES blackboard_artifact_types(artifact_type_id), "
277  + "FOREIGN KEY(review_status_id) REFERENCES review_statuses(review_status_id))");
278 
279  /* Binary representation of BYTEA is a bunch of bytes, which could
280  * include embedded nulls so we have to pay attention to field length.
281  * http://www.postgresql.org/docs/9.4/static/libpq-example.html
282  */
283  stmt.execute("CREATE TABLE blackboard_attributes (artifact_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
284  + "artifact_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
285  + "source TEXT, context TEXT, attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
286  + "value_type INTEGER NOT NULL, value_byte " + dbQueryHelper.getBlobType() + ", "
287  + "value_text TEXT, value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", value_double NUMERIC(20, 10), "
288  + "FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE, "
289  + "FOREIGN KEY(artifact_type_id) REFERENCES blackboard_artifact_types(artifact_type_id), "
290  + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
291  }
292 
293  private void createAnalysisResultsTables(Statement stmt) throws SQLException {
294  stmt.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
295  + "conclusion TEXT, "
296  + "significance INTEGER NOT NULL, "
297  + "priority INTEGER NOT NULL, "
298  + "configuration TEXT, justification TEXT, "
299  + "ignore_score INTEGER DEFAULT 0, " // boolean
300  + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE"
301  + ")");
302 
303  stmt.execute("CREATE TABLE tsk_aggregate_score( obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
304  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + ", "
305  + "significance INTEGER NOT NULL, "
306  + "priority INTEGER NOT NULL, "
307  + "UNIQUE (obj_id),"
308  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
309  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE "
310  + ")");
311 
312  }
313  private void createTagTables(Statement stmt) throws SQLException {
314  stmt.execute("CREATE TABLE tsk_tag_sets (tag_set_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, name TEXT UNIQUE)");
315  stmt.execute("CREATE TABLE tag_names (tag_name_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, display_name TEXT UNIQUE, "
316  + "description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL,"
317  + " tag_set_id " + dbQueryHelper.getBigIntType() + ", rank INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)");
318 
319  stmt.execute("CREATE TABLE tsk_examiners (examiner_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
320  + "login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))");
321 
322  stmt.execute("CREATE TABLE content_tags (tag_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
323  + "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, tag_name_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
324  + "comment TEXT NOT NULL, begin_byte_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, "
325  + "end_byte_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, "
326  + "examiner_id " + dbQueryHelper.getBigIntType() + ", "
327  + "FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id) ON DELETE CASCADE, "
328  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
329  + "FOREIGN KEY(tag_name_id) REFERENCES tag_names(tag_name_id) ON DELETE CASCADE)");
330 
331  stmt.execute("CREATE TABLE blackboard_artifact_tags (tag_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
332  + "artifact_id " + dbQueryHelper.getBigIntType() + " NOT NULL, tag_name_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
333  + "comment TEXT NOT NULL, examiner_id " + dbQueryHelper.getBigIntType() + ", "
334  + "FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id) ON DELETE CASCADE, "
335  + "FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE, "
336  + "FOREIGN KEY(tag_name_id) REFERENCES tag_names(tag_name_id) ON DELETE CASCADE)");
337  }
338 
345  private void addIndexes(Connection conn) throws TskCoreException {
346  try (Statement stmt = conn.createStatement()) {
347  // tsk_objects index
348  stmt.execute("CREATE INDEX parObjId ON tsk_objects(par_obj_id)");
349 
350  // file layout index
351  stmt.execute("CREATE INDEX layout_objID ON tsk_file_layout(obj_id)");
352 
353  // blackboard indexes
354  stmt.execute("CREATE INDEX artifact_objID ON blackboard_artifacts(obj_id)");
355  stmt.execute("CREATE INDEX artifact_artifact_objID ON blackboard_artifacts(artifact_obj_id)");
356  stmt.execute("CREATE INDEX artifact_typeID ON blackboard_artifacts(artifact_type_id)");
357  stmt.execute("CREATE INDEX attrsArtifactID ON blackboard_attributes(artifact_id)");
358 
359  //file type indexes
360  stmt.execute("CREATE INDEX mime_type ON tsk_files(dir_type,mime_type,type)");
361  stmt.execute("CREATE INDEX file_extension ON tsk_files(extension)");
362 
363  // account indexes
364  stmt.execute("CREATE INDEX relationships_account1 ON account_relationships(account1_id)");
365  stmt.execute("CREATE INDEX relationships_account2 ON account_relationships(account2_id)");
366  stmt.execute("CREATE INDEX relationships_relationship_source_obj_id ON account_relationships(relationship_source_obj_id)");
367  stmt.execute("CREATE INDEX relationships_date_time ON account_relationships(date_time)");
368  stmt.execute("CREATE INDEX relationships_relationship_type ON account_relationships(relationship_type)");
369  stmt.execute("CREATE INDEX relationships_data_source_obj_id ON account_relationships(data_source_obj_id)");
370 
371  //tsk_events indices
372  stmt.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id)");
373  stmt.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id)");
374  stmt.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id)");
375  stmt.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time)");
376  stmt.execute("CREATE INDEX events_time ON tsk_events(time)");
377 
378  // analysis results and scores indices
379  stmt.execute("CREATE INDEX score_significance_priority ON tsk_aggregate_score(significance, priority)");
380  stmt.execute("CREATE INDEX score_datasource_obj_id ON tsk_aggregate_score(data_source_obj_id)");
381 
382  stmt.execute("CREATE INDEX tsk_file_attributes_obj_id ON tsk_file_attributes(obj_id)");
383 
384 
385  } catch (SQLException ex) {
386  throw new TskCoreException("Error initializing db_info tables", ex);
387  }
388  }
389 
390  private void createIngestTables(Statement stmt) throws SQLException {
391  stmt.execute("CREATE TABLE ingest_module_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)");
392 
393  stmt.execute("CREATE TABLE ingest_job_status_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)");
394 
395  stmt.execute("CREATE TABLE ingest_modules (ingest_module_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
396  + "display_name TEXT NOT NULL, unique_name TEXT UNIQUE NOT NULL, type_id INTEGER NOT NULL, "
397  + "version TEXT NOT NULL, FOREIGN KEY(type_id) REFERENCES ingest_module_types(type_id) ON DELETE CASCADE);");
398 
399  stmt.execute("CREATE TABLE ingest_jobs (ingest_job_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
400  + "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, host_name TEXT NOT NULL, "
401  + "start_date_time " + dbQueryHelper.getBigIntType() + " NOT NULL, "
402  + "end_date_time " + dbQueryHelper.getBigIntType() + " NOT NULL, status_id INTEGER NOT NULL, "
403  + "settings_dir TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
404  + "FOREIGN KEY(status_id) REFERENCES ingest_job_status_types(type_id) ON DELETE CASCADE);");
405 
406  stmt.execute("CREATE TABLE ingest_job_modules (ingest_job_id INTEGER, ingest_module_id INTEGER, "
407  + "pipeline_position INTEGER, PRIMARY KEY(ingest_job_id, ingest_module_id), "
408  + "FOREIGN KEY(ingest_job_id) REFERENCES ingest_jobs(ingest_job_id) ON DELETE CASCADE, "
409  + "FOREIGN KEY(ingest_module_id) REFERENCES ingest_modules(ingest_module_id) ON DELETE CASCADE);");
410  }
411 
412  private void createHostTables(Statement stmt) throws SQLException {
413 
414  stmt.execute("CREATE TABLE tsk_persons (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
415  + "name TEXT NOT NULL, " // person name
416  + "UNIQUE(name)) ");
417 
418  // References tsk_persons
419  stmt.execute("CREATE TABLE tsk_hosts (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
420  + "name TEXT NOT NULL, " // host name
421  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
422  + "person_id INTEGER, "
423  + "merged_into " + dbQueryHelper.getBigIntType() + ", "
424  + "FOREIGN KEY(person_id) REFERENCES tsk_persons(id) ON DELETE SET NULL, "
425  + "FOREIGN KEY(merged_into) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
426  + "UNIQUE(name)) ");
427 
428  stmt.execute("CREATE TABLE tsk_host_addresses (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
429  + "address_type INTEGER NOT NULL, "
430  + "address TEXT NOT NULL, "
431  + "UNIQUE(address_type, address)) ");
432 
433  stmt.execute("CREATE TABLE tsk_host_address_map (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
434  + "host_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
435  + "addr_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
436  + "source_obj_id " + dbQueryHelper.getBigIntType() + ", " // object id of the source where this mapping was found.
437  + "time " + dbQueryHelper.getBigIntType() + ", " // time at which the mapping existed
438  + "UNIQUE(host_id, addr_obj_id, time), "
439  + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
440  + "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id), "
441  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
442 
443  // stores associations between DNS name and IP address
444  stmt.execute("CREATE TABLE tsk_host_address_dns_ip_map (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
445  + "dns_address_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
446  + "ip_address_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
447  + "source_obj_id " + dbQueryHelper.getBigIntType() + ", "
448  + "time " + dbQueryHelper.getBigIntType() + ", " // time at which the mapping existed
449  + "UNIQUE(dns_address_id, ip_address_id, time), "
450  + "FOREIGN KEY(dns_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
451  + "FOREIGN KEY(ip_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE,"
452  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
453 
454  // maps an address to an content/item using it
455  stmt.execute("CREATE TABLE tsk_host_address_usage (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
456  + "addr_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
457  + "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // obj id of the content/item using the address
458  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // data source where the usage was found
459  + "UNIQUE(addr_obj_id, obj_id), "
460  + "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
461  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
462  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE )");
463  }
464 
465  // Must be called after tsk_persons, tsk_hosts and tsk_objects have been created.
466  private void createAccountTables(Statement stmt) throws SQLException {
467  stmt.execute("CREATE TABLE account_types (account_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
468  + "type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)");
469 
470  // References account_types
471  stmt.execute("CREATE TABLE accounts (account_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
472  + "account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, "
473  + "UNIQUE(account_type_id, account_unique_identifier), "
474  + "FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))");
475 
476  // References accounts, tsk_objects
477  stmt.execute("CREATE TABLE account_relationships (relationship_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
478  + "account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, "
479  + "relationship_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
480  + "date_time " + dbQueryHelper.getBigIntType() + ", relationship_type INTEGER NOT NULL, "
481  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
482  + "UNIQUE(account1_id, account2_id, relationship_source_obj_id), "
483  + "FOREIGN KEY(account1_id) REFERENCES accounts(account_id), "
484  + "FOREIGN KEY(account2_id) REFERENCES accounts(account_id), "
485  + "FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
486  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
487 
488  // References tsk_hosts
489  stmt.execute("CREATE TABLE tsk_os_account_realms (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
490  + "realm_name TEXT DEFAULT NULL, " // realm name - for a domain realm, may be null
491  + "realm_addr TEXT DEFAULT NULL, " // a sid/uid or some some other identifier, may be null
492  + "realm_signature TEXT NOT NULL, " // Signature exists only to prevent duplicates. It is made up of realm address/name and scope host
493  + "scope_host_id " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " // if the realm scope is a single host
494  + "scope_confidence INTEGER, " // indicates whether we know for sure the realm scope or if we are inferring it
495  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
496  + "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
497  + "UNIQUE(realm_signature), "
498  + "FOREIGN KEY(scope_host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE,"
499  + "FOREIGN KEY(merged_into) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE )");
500 
501  // References tsk_objects, tsk_os_account_realms, tsk_persons
502  stmt.execute("CREATE TABLE tsk_os_accounts (os_account_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
503  + "login_name TEXT DEFAULT NULL, " // login name, if available, may be null
504  + "full_name TEXT DEFAULT NULL, " // full name, if available, may be null
505  + "realm_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // realm for the account
506  + "addr TEXT DEFAULT NULL, " // SID/UID, if available
507  + "signature TEXT NOT NULL, " // This exists only to prevent duplicates. It is either the addr or the login_name whichever is not null.
508  + "status INTEGER, " // enabled/disabled/deleted
509  + "type INTEGER, " // service/interactive
510  + "created_date " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
511  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
512  + "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
513  + "UNIQUE(signature, realm_id), "
514  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
515  + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE,"
516  + "FOREIGN KEY(merged_into) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE )");
517 
518  }
519  // Must be called after createAccountTables() and blackboard_attribute_types, blackboard_artifacts creation.
520  private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLException {
521 
522  // References tsk_os_accounts, tsk_hosts, tsk_objects, blackboard_attribute_types
523  stmt.execute("CREATE TABLE tsk_os_account_attributes (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
524  + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
525  + "host_id " + dbQueryHelper.getBigIntType() + ", "
526  + "source_obj_id " + dbQueryHelper.getBigIntType() + ", "
527  + "attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
528  + "value_type INTEGER NOT NULL, "
529  + "value_byte " + dbQueryHelper.getBlobType() + ", "
530  + "value_text TEXT, "
531  + "value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", "
532  + "value_double NUMERIC(20, 10), "
533  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, "
534  + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
535  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL, "
536  + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
537 
538  // References tsk_os_accounts, tsk_objects, tsk_hosts
539  stmt.execute("CREATE TABLE tsk_os_account_instances (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
540  + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
541  + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
542  + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn
543  + "UNIQUE(os_account_obj_id, data_source_obj_id, instance_type), "
544  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, "
545  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) ");
546 
547  // References blackboard_artifacts, tsk_os_accounts
548  stmt.execute("CREATE TABLE tsk_data_artifacts ( "
549  + "artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
550  + "os_account_obj_id " + dbQueryHelper.getBigIntType() + ", "
551  + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, "
552  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL) ");
553  }
554 
555  private void createEventTables(Statement stmt) throws SQLException {
556  stmt.execute("CREATE TABLE tsk_event_types ("
557  + " event_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY,"
558  + " display_name TEXT UNIQUE NOT NULL , "
559  + " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )");
560 
561  /*
562  * Regarding the timeline event tables schema, note that several columns
563  * in the tsk_event_descriptions table seem, at first glance, to be
564  * attributes of events rather than their descriptions and would appear
565  * to belong in tsk_events table instead. The rationale for putting the
566  * data source object ID, content object ID, artifact ID and the flags
567  * indicating whether or not the event source has a hash set hit or is
568  * tagged were motivated by the fact that these attributes are identical
569  * for each event in a set of file system file MAC time events. The
570  * decision was made to avoid duplication and save space by placing this
571  * data in the tsk_event-descriptions table.
572  */
573  stmt.execute(
574  "CREATE TABLE tsk_event_descriptions ( "
575  + " event_description_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
576  + " full_description TEXT NOT NULL, "
577  + " med_description TEXT, "
578  + " short_description TEXT,"
579  + " data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
580  + " content_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
581  + " artifact_id " + dbQueryHelper.getBigIntType() + ", "
582  + " hash_hit INTEGER NOT NULL, " //boolean
583  + " tagged INTEGER NOT NULL, " //boolean
584  + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
585  + " FOREIGN KEY(content_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
586  + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE,"
587  + " UNIQUE (full_description, content_obj_id, artifact_id))");
588 
589  stmt.execute(
590  "CREATE TABLE tsk_events ("
591  + " event_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
592  + " event_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
593  + " event_description_id " + dbQueryHelper.getBigIntType() + " NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ON DELETE CASCADE ,"
594  + " time " + dbQueryHelper.getBigIntType() + " NOT NULL , "
595  + " UNIQUE (event_type_id, event_description_id, time))");
596  }
597 
598  private void createAttributeTables(Statement stmt) throws SQLException {
599  /*
600  * Binary representation of BYTEA is a bunch of bytes, which could
601  * include embedded nulls so we have to pay attention to field length.
602  * http://www.postgresql.org/docs/9.4/static/libpq-example.html
603  */
604  stmt.execute("CREATE TABLE tsk_file_attributes ( id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
605  + "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
606  + "attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
607  + "value_type INTEGER NOT NULL, value_byte " + dbQueryHelper.getBlobType() + ", "
608  + "value_text TEXT, value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", value_double NUMERIC(20, 10), "
609  + "FOREIGN KEY(obj_id) REFERENCES tsk_files(obj_id) ON DELETE CASCADE, "
610  + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
611  }
612 
616  private abstract class DbCreationHelper {
617 
623  abstract void createDatabase() throws TskCoreException;
624 
630  abstract Connection getConnection() throws TskCoreException;
631 
640  abstract void performPreInitialization(Connection conn) throws TskCoreException;
641 
648  abstract void performPostTableInitialization(Connection conn) throws TskCoreException;
649  }
650 
654  private class PostgreSQLDbCreationHelper extends DbCreationHelper {
655 
656  private final static String JDBC_BASE_URI = "jdbc:postgresql://"; // NON-NLS
657  private final static String JDBC_DRIVER = "org.postgresql.Driver"; // NON-NLS
658 
659  final private String caseName;
660  final private CaseDbConnectionInfo info;
661 
662  PostgreSQLDbCreationHelper(String caseName, CaseDbConnectionInfo info) {
663  this.caseName = caseName;
664  this.info = info;
665  }
666 
667  @Override
668  void createDatabase() throws TskCoreException{
669  try(Connection conn = getPostgresConnection();
670  Statement stmt = conn.createStatement()) {
671  stmt.execute("CREATE DATABASE \"" + caseName + "\" WITH ENCODING='UTF8'");
672  } catch (SQLException ex) {
673  throw new TskCoreException("Error creating PostgreSQL case " + caseName, ex);
674  }
675  }
676 
677  @Override
678  Connection getConnection() throws TskCoreException {
679  return getConnection(caseName);
680  }
681 
687  Connection getPostgresConnection() throws TskCoreException {
688  return getConnection("postgres");
689  }
690 
698  Connection getConnection(String databaseName) throws TskCoreException {
699  String encodedDbName;
700  try {
701  encodedDbName = URLEncoder.encode(databaseName, "UTF-8");
702  } catch (UnsupportedEncodingException ex) {
703  // Print the warning and continue with the unencoded name
704  logger.log(Level.WARNING, "Error encoding database name " + databaseName, ex);
705  encodedDbName = databaseName;
706  }
707 
708  StringBuilder url = new StringBuilder();
709  url.append(JDBC_BASE_URI)
710  .append(info.getHost())
711  .append(":")
712  .append(info.getPort())
713  .append('/') // NON-NLS
714  .append(encodedDbName);
715 
716  Connection conn;
717  try {
718  Properties props = new Properties();
719  props.setProperty("user", info.getUserName()); // NON-NLS
720  props.setProperty("password", info.getPassword()); // NON-NLS
721 
722  Class.forName(JDBC_DRIVER);
723  conn = DriverManager.getConnection(url.toString(), props);
724  } catch (ClassNotFoundException | SQLException ex) {
725  throw new TskCoreException("Failed to acquire ephemeral connection to PostgreSQL database " + databaseName, ex); // NON-NLS
726  }
727  return conn;
728  }
729 
730  @Override
731  void performPreInitialization(Connection conn) throws TskCoreException {
732  // Nothing to do here for PostgreSQL
733  }
734 
735  @Override
736  void performPostTableInitialization(Connection conn) throws TskCoreException {
737  try (Statement stmt = conn.createStatement()) {
738  stmt.execute("ALTER SEQUENCE blackboard_artifacts_artifact_id_seq minvalue -9223372036854775808 restart with -9223372036854775808");
739  } catch (SQLException ex) {
740  throw new TskCoreException("Error altering artifact ID sequence", ex);
741  }
742  }
743  }
744 
748  private class SQLiteDbCreationHelper extends DbCreationHelper {
749 
750  private final static String PRAGMA_SYNC_OFF = "PRAGMA synchronous = OFF"; // NON-NLS
751  private final static String PRAGMA_READ_UNCOMMITTED_TRUE = "PRAGMA read_uncommitted = True"; // NON-NLS
752  private final static String PRAGMA_ENCODING_UTF8 = "PRAGMA encoding = 'UTF-8'"; // NON-NLS
753  private final static String PRAGMA_PAGE_SIZE_4096 = "PRAGMA page_size = 4096"; // NON-NLS
754  private final static String PRAGMA_FOREIGN_KEYS_ON = "PRAGMA foreign_keys = ON"; // NON-NLS
755 
756  private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
757  private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
758 
759  String dbPath;
760 
761  SQLiteDbCreationHelper(String dbPath) {
762  this.dbPath = dbPath;
763  }
764 
765  @Override
766  void createDatabase() throws TskCoreException {
767  // SQLite doesn't need to explicitly create the case database but we will
768  // check that the folder exists and the database does not
769  File dbFile = new File(dbPath);
770  if (dbFile.exists()) {
771  throw new TskCoreException("Case database already exists : " + dbPath);
772  }
773 
774  if (dbFile.getParentFile() != null && !dbFile.getParentFile().exists()) {
775  throw new TskCoreException("Case database folder does not exist : " + dbFile.getParent());
776  }
777  }
778 
779  @Override
780  Connection getConnection() throws TskCoreException {
781 
782  StringBuilder url = new StringBuilder();
783  url.append(JDBC_BASE_URI)
784  .append(dbPath);
785 
786  Connection conn;
787  try {
788  Class.forName(JDBC_DRIVER);
789  conn = DriverManager.getConnection(url.toString());
790  } catch (ClassNotFoundException | SQLException ex) {
791  throw new TskCoreException("Failed to acquire ephemeral connection SQLite database " + dbPath, ex); // NON-NLS
792  }
793  return conn;
794  }
795 
796  @Override
797  void performPreInitialization(Connection conn) throws TskCoreException {
798  try (Statement stmt = conn.createStatement()) {
799  stmt.execute(PRAGMA_SYNC_OFF);
800  stmt.execute(PRAGMA_READ_UNCOMMITTED_TRUE);
801  stmt.execute(PRAGMA_ENCODING_UTF8);
802  stmt.execute(PRAGMA_PAGE_SIZE_4096);
803  stmt.execute(PRAGMA_FOREIGN_KEYS_ON);
804  } catch (SQLException ex) {
805  throw new TskCoreException("Error setting pragmas", ex);
806  }
807  }
808 
809  @Override
810  void performPostTableInitialization(Connection conn) throws TskCoreException {
811  // Nothing to do here for SQLite
812  }
813  }
814 }

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.