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