1 /*
2 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 * Copyright (C) 2010-2013 Sourcefire, Inc.
4 *
5 * Authors: aCaB
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include "matcher.h"
26 #include "others.h"
27 #include "str.h"
28
hm_addhash_str(struct cli_matcher * root,const char * strhash,uint32_t size,const char * virusname)29 int hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_t size, const char *virusname)
30 {
31 enum CLI_HASH_TYPE type;
32 char binhash[CLI_HASHLEN_MAX];
33 int hlen;
34
35 if (!root || !strhash) {
36 cli_errmsg("hm_addhash_str: NULL root or hash\n");
37 return CL_ENULLARG;
38 }
39
40 /* size 0 here is now a wildcard size match */
41 if (size == (uint32_t)-1) {
42 cli_errmsg("hm_addhash_str: null or invalid size (%u)\n", size);
43 return CL_EARG;
44 }
45
46 hlen = strlen(strhash);
47 switch (hlen) {
48 case 32:
49 type = CLI_HASH_MD5;
50 break;
51 case 40:
52 type = CLI_HASH_SHA1;
53 break;
54 case 64:
55 type = CLI_HASH_SHA256;
56 break;
57 default:
58 cli_errmsg("hm_addhash_str: invalid hash %s -- FIXME!\n", strhash);
59 return CL_EARG;
60 }
61 if (cli_hex2str_to(strhash, (char *)binhash, hlen)) {
62 cli_errmsg("hm_addhash_str: invalid hash %s\n", strhash);
63 return CL_EARG;
64 }
65
66 return hm_addhash_bin(root, binhash, type, size, virusname);
67 }
68
69 const unsigned int hashlen[] = {
70 CLI_HASHLEN_MD5,
71 CLI_HASHLEN_SHA1,
72 CLI_HASHLEN_SHA256};
73
hm_addhash_bin(struct cli_matcher * root,const void * binhash,enum CLI_HASH_TYPE type,uint32_t size,const char * virusname)74 int hm_addhash_bin(struct cli_matcher *root, const void *binhash, enum CLI_HASH_TYPE type, uint32_t size, const char *virusname)
75 {
76 const unsigned int hlen = hashlen[type];
77 const struct cli_htu32_element *item;
78 struct cli_sz_hash *szh;
79 struct cli_htu32 *ht;
80 int i;
81
82 if (size) {
83 /* size non-zero, find sz_hash element in size-driven hashtable */
84 ht = &root->hm.sizehashes[type];
85 if (!root->hm.sizehashes[type].capacity) {
86 i = cli_htu32_init(ht, 64, root->mempool);
87 if (i) return i;
88 }
89
90 item = cli_htu32_find(ht, size);
91 if (!item) {
92 struct cli_htu32_element htitem;
93 szh = MPOOL_CALLOC(root->mempool, 1, sizeof(*szh));
94 if (!szh) {
95 cli_errmsg("hm_addhash_bin: failed to allocate size hash\n");
96 return CL_EMEM;
97 }
98
99 htitem.key = size;
100 htitem.data.as_ptr = szh;
101 i = cli_htu32_insert(ht, &htitem, root->mempool);
102 if (i) {
103 cli_errmsg("hm_addhash_bin: failed to add item to hashtab");
104 MPOOL_FREE(root->mempool, szh);
105 return i;
106 }
107 } else
108 szh = (struct cli_sz_hash *)item->data.as_ptr;
109 } else {
110 /* size 0 = wildcard */
111 szh = &root->hwild.hashes[type];
112 }
113 szh->items++;
114
115 szh->hash_array = MPOOL_REALLOC2(root->mempool, szh->hash_array, hlen * szh->items);
116 if (!szh->hash_array) {
117 cli_errmsg("hm_addhash_bin: failed to grow hash array to %u entries\n", szh->items);
118 szh->items = 0;
119 MPOOL_FREE(root->mempool, szh->virusnames);
120 szh->virusnames = NULL;
121 return CL_EMEM;
122 }
123
124 szh->virusnames = MPOOL_REALLOC2(root->mempool, szh->virusnames, sizeof(*szh->virusnames) * szh->items);
125 if (!szh->virusnames) {
126 cli_errmsg("hm_addhash_bin: failed to grow virusname array to %u entries\n", szh->items);
127 szh->items = 0;
128 MPOOL_FREE(root->mempool, szh->hash_array);
129 szh->hash_array = NULL;
130 return CL_EMEM;
131 }
132
133 memcpy(&szh->hash_array[(szh->items - 1) * hlen], binhash, hlen);
134 szh->virusnames[(szh->items - 1)] = virusname;
135
136 return 0;
137 }
138
hm_cmp(const uint8_t * itm,const uint8_t * ref,unsigned int keylen)139 static inline int hm_cmp(const uint8_t *itm, const uint8_t *ref, unsigned int keylen)
140 {
141 #if WORDS_BIGENDIAN == 0
142 uint32_t i = *(uint32_t *)itm, r = *(uint32_t *)ref;
143 if (i != r)
144 return (i < r) * 2 - 1;
145 return memcmp(&itm[4], &ref[4], keylen - 4);
146 #else
147 return memcmp(itm, ref, keylen);
148 #endif
149 }
150
hm_sort(struct cli_sz_hash * szh,size_t l,size_t r,unsigned int keylen)151 static void hm_sort(struct cli_sz_hash *szh, size_t l, size_t r, unsigned int keylen)
152 {
153 uint8_t piv[CLI_HASHLEN_MAX], tmph[CLI_HASHLEN_MAX];
154 size_t l1, r1;
155
156 const char *tmpv;
157
158 if (l + 1 >= r)
159 return;
160
161 l1 = l + 1, r1 = r;
162
163 memcpy(piv, &szh->hash_array[keylen * l], keylen);
164 while (l1 < r1) {
165 if (hm_cmp(&szh->hash_array[keylen * l1], piv, keylen) > 0) {
166 r1--;
167 if (l1 == r1) break;
168 memcpy(tmph, &szh->hash_array[keylen * l1], keylen);
169 tmpv = szh->virusnames[l1];
170 memcpy(&szh->hash_array[keylen * l1], &szh->hash_array[keylen * r1], keylen);
171 szh->virusnames[l1] = szh->virusnames[r1];
172 memcpy(&szh->hash_array[keylen * r1], tmph, keylen);
173 szh->virusnames[r1] = tmpv;
174 } else
175 l1++;
176 }
177
178 l1--;
179 if (l1 != l) {
180 memcpy(tmph, &szh->hash_array[keylen * l1], keylen);
181 tmpv = szh->virusnames[l1];
182 memcpy(&szh->hash_array[keylen * l1], &szh->hash_array[keylen * l], keylen);
183 szh->virusnames[l1] = szh->virusnames[l];
184 memcpy(&szh->hash_array[keylen * l], tmph, keylen);
185 szh->virusnames[l] = tmpv;
186 }
187
188 hm_sort(szh, l, l1, keylen);
189 hm_sort(szh, r1, r, keylen);
190 }
191
192 /* flush both size-specific and agnostic hash sets */
hm_flush(struct cli_matcher * root)193 void hm_flush(struct cli_matcher *root)
194 {
195 enum CLI_HASH_TYPE type;
196 unsigned int keylen;
197 struct cli_sz_hash *szh;
198
199 if (!root)
200 return;
201
202 for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
203 struct cli_htu32 *ht = &root->hm.sizehashes[type];
204 const struct cli_htu32_element *item = NULL;
205 szh = NULL;
206
207 if (!root->hm.sizehashes[type].capacity)
208 continue;
209
210 while ((item = cli_htu32_next(ht, item))) {
211 szh = (struct cli_sz_hash *)item->data.as_ptr;
212 keylen = hashlen[type];
213
214 if (szh->items > 1)
215 hm_sort(szh, 0, szh->items, keylen);
216 }
217 }
218
219 for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
220 szh = &root->hwild.hashes[type];
221 keylen = hashlen[type];
222
223 if (szh->items > 1)
224 hm_sort(szh, 0, szh->items, keylen);
225 }
226 }
227
cli_hm_have_size(const struct cli_matcher * root,enum CLI_HASH_TYPE type,uint32_t size)228 int cli_hm_have_size(const struct cli_matcher *root, enum CLI_HASH_TYPE type, uint32_t size)
229 {
230 return (size && size != 0xffffffff && root && root->hm.sizehashes[type].capacity && cli_htu32_find(&root->hm.sizehashes[type], size));
231 }
232
cli_hm_have_wild(const struct cli_matcher * root,enum CLI_HASH_TYPE type)233 int cli_hm_have_wild(const struct cli_matcher *root, enum CLI_HASH_TYPE type)
234 {
235 return (root && root->hwild.hashes[type].items);
236 }
237
cli_hm_have_any(const struct cli_matcher * root,enum CLI_HASH_TYPE type)238 int cli_hm_have_any(const struct cli_matcher *root, enum CLI_HASH_TYPE type)
239 {
240 return (root && (root->hwild.hashes[type].items || root->hm.sizehashes[type].capacity));
241 }
242
243 /* cli_hm_scan will scan only size-specific hashes, if any */
hm_scan(const unsigned char * digest,const char ** virname,const struct cli_sz_hash * szh,enum CLI_HASH_TYPE type)244 static int hm_scan(const unsigned char *digest, const char **virname, const struct cli_sz_hash *szh, enum CLI_HASH_TYPE type)
245 {
246 unsigned int keylen;
247 size_t l, r;
248
249 if (!digest || !szh || !szh->items)
250 return CL_CLEAN;
251
252 keylen = hashlen[type];
253
254 l = 0;
255 r = szh->items - 1;
256 while (l <= r) {
257 size_t c = (l + r) / 2;
258 int res = hm_cmp(digest, &szh->hash_array[keylen * c], keylen);
259
260 if (res < 0) {
261 if (!c)
262 break;
263 r = c - 1;
264 } else if (res > 0)
265 l = c + 1;
266 else {
267 if (virname)
268 *virname = szh->virusnames[c];
269 return CL_VIRUS;
270 }
271 }
272 return CL_CLEAN;
273 }
274
275 /* cli_hm_scan will scan only size-specific hashes, if any */
cli_hm_scan(const unsigned char * digest,uint32_t size,const char ** virname,const struct cli_matcher * root,enum CLI_HASH_TYPE type)276 int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type)
277 {
278 const struct cli_htu32_element *item;
279 struct cli_sz_hash *szh;
280
281 if (!digest || !size || size == 0xffffffff || !root || !root->hm.sizehashes[type].capacity)
282 return CL_CLEAN;
283
284 item = cli_htu32_find(&root->hm.sizehashes[type], size);
285 if (!item)
286 return CL_CLEAN;
287
288 szh = (struct cli_sz_hash *)item->data.as_ptr;
289
290 return hm_scan(digest, virname, szh, type);
291 }
292
293 /* cli_hm_scan_wild will scan only size-agnostic hashes, if any */
cli_hm_scan_wild(const unsigned char * digest,const char ** virname,const struct cli_matcher * root,enum CLI_HASH_TYPE type)294 int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type)
295 {
296 if (!digest || !root || !root->hwild.hashes[type].items)
297 return CL_CLEAN;
298
299 return hm_scan(digest, virname, &root->hwild.hashes[type], type);
300 }
301
302 /* free both size-specific and agnostic hash sets */
hm_free(struct cli_matcher * root)303 void hm_free(struct cli_matcher *root)
304 {
305 enum CLI_HASH_TYPE type;
306
307 if (!root)
308 return;
309
310 for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
311 struct cli_htu32 *ht = &root->hm.sizehashes[type];
312 const struct cli_htu32_element *item = NULL;
313
314 if (!root->hm.sizehashes[type].capacity)
315 continue;
316
317 while ((item = cli_htu32_next(ht, item))) {
318 struct cli_sz_hash *szh = (struct cli_sz_hash *)item->data.as_ptr;
319
320 MPOOL_FREE(root->mempool, szh->hash_array);
321 while (szh->items)
322 MPOOL_FREE(root->mempool, (void *)szh->virusnames[--szh->items]);
323 MPOOL_FREE(root->mempool, szh->virusnames);
324 MPOOL_FREE(root->mempool, szh);
325 }
326 cli_htu32_free(ht, root->mempool);
327 }
328
329 for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
330 struct cli_sz_hash *szh = &root->hwild.hashes[type];
331
332 if (!szh->items)
333 continue;
334
335 MPOOL_FREE(root->mempool, szh->hash_array);
336 while (szh->items)
337 MPOOL_FREE(root->mempool, (void *)szh->virusnames[--szh->items]);
338 MPOOL_FREE(root->mempool, szh->virusnames);
339 }
340 }
341