1 /*
2 * The Sleuth Kit
3 *
4 * Brian Carrier [carrier <at> sleuthkit [dot] org]
5 * Copyright (c) 2012-2014 Brian Carrier.  All rights reserved
6 *
7 *
8 * This software is distributed under the Common Public License 1.0
9 */
10 
11 /**
12 * \file encase.c
13 * Contains the Encase hash database specific extraction and printing routines.
14 */
15 
16 #include "tsk_hashdb_i.h"
17 
18 /**
19 * Test the file to see if it is an Encase database
20 *
21 * @param hFile File handle to hash database
22 *
23 * @return 1 if encase and 0 if not
24 */
25 uint8_t
encase_test(FILE * hFile)26     encase_test(FILE * hFile)
27 {
28     char buf[8];
29 
30     fseeko(hFile, 0, SEEK_SET);
31     if (8 != fread(buf, sizeof(char), 8, hFile))
32         return 0;
33 
34     if (memcmp(buf, "HASH\x0d\x0a\xff\x00", 8))
35         return 0;
36 
37     return 1;
38 }
39 
40 /**
41 * Set db_name using information from this database type
42 *
43 * @param hdb_info the hash database object
44 */
45 static void
encase_name(TSK_HDB_BINSRCH_INFO * hdb_info)46     encase_name(TSK_HDB_BINSRCH_INFO * hdb_info)
47 {
48     FILE * hFile = hdb_info->hDb;
49     wchar_t buf[40];
50     UTF16 *utf16;
51     UTF8 *utf8;
52     size_t ilen;
53     memset(hdb_info->base.db_name, '\0', TSK_HDB_NAME_MAXLEN);
54     if(!hFile) {
55         if (tsk_verbose)
56             fprintf(stderr,
57             "Error getting name from Encase hash db; using file name instead");
58         hdb_base_db_name_from_path(&hdb_info->base);
59         return;
60     }
61 
62     memset(buf, '\0', 40);
63 
64     fseeko(hFile, 1032, SEEK_SET);
65     if (39 != fread(buf, sizeof(wchar_t), 39, hFile)) {
66         if (tsk_verbose)
67             fprintf(stderr,
68             "Error getting name from Encase hash db; using file name instead");
69         hdb_base_db_name_from_path(&hdb_info->base);
70         return;
71     }
72 
73     // do some arithmetic on type sizes to be platform independent
74     ilen = wcslen(buf) * (sizeof(wchar_t) / sizeof(UTF16));
75 
76     utf8 = (UTF8 *) hdb_info->base.db_name;
77     utf16 = (UTF16 *) buf;
78 
79     tsk_UTF16toUTF8(TSK_LIT_ENDIAN,
80         (const UTF16 **) &utf16,
81         &utf16[ilen], &utf8, &utf8[78],
82         TSKlenientConversion);
83 }
84 
85 
encase_open(FILE * hDb,const TSK_TCHAR * db_path)86 TSK_HDB_INFO *encase_open(FILE *hDb, const TSK_TCHAR *db_path)
87 {
88     TSK_HDB_BINSRCH_INFO *hdb_binsrch_info = NULL;
89 
90     // get the basic binary-search info struct
91     hdb_binsrch_info = hdb_binsrch_open(hDb, db_path);
92     if (NULL == hdb_binsrch_info) {
93         return NULL;
94     }
95 
96     // overwrite the database-specific ones
97     hdb_binsrch_info->base.db_type = TSK_HDB_DBTYPE_ENCASE_ID;
98     encase_name(hdb_binsrch_info);
99     hdb_binsrch_info->base.make_index = encase_make_index;
100     hdb_binsrch_info->get_entry = encase_get_entry;
101 
102     return (TSK_HDB_INFO*)hdb_binsrch_info;
103 }
104 
105 /**
106 * Process the database to create a sorted index of it. Consecutive
107 * entries with the same hash value are not added to the index, but
108 * will be found during lookup.
109 *
110 * @param hdb_info_base Hash database to make index of.
111 * @param dbtype Type of hash database (should always be TSK_HDB_DBTYPE_ENCASE_STR)
112 *
113 * @return 1 on error and 0 on success.
114 */
115 uint8_t
encase_make_index(TSK_HDB_INFO * hdb_info_base,TSK_TCHAR * dbtype)116     encase_make_index(TSK_HDB_INFO * hdb_info_base, TSK_TCHAR * dbtype)
117 {
118     TSK_HDB_BINSRCH_INFO *hdb_binsrch_info = (TSK_HDB_BINSRCH_INFO*)hdb_info_base;
119     unsigned char buf[19];
120     char phash[19];
121     TSK_OFF_T offset = 0;
122     int db_cnt = 0, idx_cnt = 0;
123 
124     /* Initialize the TSK index file */
125     if (hdb_binsrch_idx_initialize(hdb_binsrch_info, dbtype)) {
126         tsk_error_set_errstr2( "encase_makeindex");
127         return 1;
128     }
129 
130     /* Status */
131     if (tsk_verbose)
132         TFPRINTF(stderr, _TSK_T("Extracting Data from Database (%s)\n"),
133         hdb_binsrch_info->base.db_fname);
134 
135     memset(phash, '0', sizeof(phash));
136     memset(buf, '0', sizeof(buf));
137 
138     /* read the file and add to the index */
139     fseek(hdb_binsrch_info->hDb, 1152, SEEK_SET);
140     while (18 == fread(buf,sizeof(char),18,hdb_binsrch_info->hDb)) {
141         db_cnt++;
142 
143         /* We only want to add one of each hash to the index */
144         if (memcmp(buf, phash, 18) == 0) {
145             continue;
146         }
147 
148         /* Add the entry to the index */
149         if (hdb_binsrch_idx_add_entry_bin(hdb_binsrch_info, buf, 16, offset)) {
150             tsk_error_set_errstr2( "encase_make_index");
151             return 1;
152         }
153 
154         idx_cnt++;
155 
156         /* Set the previous has value */
157         memcpy(phash, buf, 18);
158         offset += 18;
159     }
160 
161     if (idx_cnt > 0) {
162         if (tsk_verbose) {
163             fprintf(stderr, "  Valid Database Entries: %d\n", db_cnt);
164             fprintf(stderr, "  Index File Entries %s: %d\n",
165                 (idx_cnt == db_cnt) ? "" : "(optimized)", idx_cnt);
166         }
167 
168         /* Close and sort the index */
169         if (hdb_binsrch_idx_finalize(hdb_binsrch_info)) {
170             tsk_error_set_errstr2( "encase_makeindex");
171             return 1;
172         }
173     }
174     else {
175         tsk_error_reset();
176         tsk_error_set_errno(TSK_ERR_HDB_CORRUPT);
177         tsk_error_set_errstr(
178             "encase_makeindex: No valid entries found in database");
179         return 1;
180     }
181 
182     return 0;
183 }
184 
185 /**
186 * Find the entry at a
187 * given offset.  The offset was likely determined from the index.
188 * The callback is called for each entry. EnCase does not store names,
189 * so the callback is called with just the hash value.
190 *
191 * @param hdb_info Hash database to get data from
192 * @param hash MD5 hash value that was searched for
193 * @param offset Byte offset where hash value should be located in db_file
194 * @param flags (not used)
195 * @param action Callback used for each entry found in lookup
196 * @param cb_ptr Pointer to data passed to callback
197 *
198 * @return 1 on error and 0 on succuss
199 */
200 uint8_t
encase_get_entry(TSK_HDB_INFO * hdb_info,const char * hash,TSK_OFF_T offset,TSK_HDB_FLAG_ENUM flags,TSK_HDB_LOOKUP_FN action,void * cb_ptr)201     encase_get_entry(TSK_HDB_INFO * hdb_info, const char *hash,
202     TSK_OFF_T offset, TSK_HDB_FLAG_ENUM flags,
203     TSK_HDB_LOOKUP_FN action, void *cb_ptr)
204 {
205     TSK_HDB_BINSRCH_INFO *hdb_binsrch_info = (TSK_HDB_BINSRCH_INFO*)hdb_info;
206     int found = 0;
207     char buf[19];
208 
209     if (tsk_verbose)
210         fprintf(stderr,
211         "encase_getentry: Lookup up hash %s at offset %" PRIdOFF
212         "\n", hash, offset);
213 
214     if (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN) {
215         tsk_error_reset();
216         tsk_error_set_errno(TSK_ERR_HDB_ARG);
217         tsk_error_set_errstr(
218             "encase_getentry: Invalid hash value: %s", hash);
219         return 1;
220     }
221 
222     memset(buf, 0, sizeof(buf));
223 
224     /* Loop so that we can find multiple occurrences of the same hash */
225     fseeko(hdb_binsrch_info->hDb, offset, SEEK_SET);
226     while (1) {
227         int retval;
228         char hash_str[TSK_HDB_HTYPE_MD5_LEN+1];
229 
230         if (18 != fread(buf,sizeof(char),18,hdb_binsrch_info->hDb)) {
231             if (feof(hdb_binsrch_info->hDb)) {
232                 break;
233             }
234             tsk_error_reset();
235             tsk_error_set_errno(TSK_ERR_HDB_READDB);
236             tsk_error_set_errstr(
237                 "encase_getentry: Error reading database");
238             return 1;
239         }
240 
241         snprintf(hash_str, TSK_HDB_HTYPE_MD5_LEN+1, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
242             buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
243             buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
244 
245         /* Is this the one that we want? */
246         if (0 != strcasecmp(hash_str, hash)) {
247             break;
248         }
249 
250         retval = action(hdb_info, hash, "", cb_ptr);
251         if (retval == TSK_WALK_ERROR) {
252             return 1;
253         }
254         else if (retval == TSK_WALK_STOP) {
255             return 0;
256         }
257         found = 1;
258 
259         /* Advance to the next row */
260         offset += 18;
261     }
262 
263     if (found == 0) {
264         tsk_error_reset();
265         tsk_error_set_errno(TSK_ERR_HDB_ARG);
266         tsk_error_set_errstr(
267             "encase_getentry: Hash not found in file at offset: %lu",
268             (unsigned long) offset);
269         return 1;
270     }
271 
272     return 0;
273 }
274