/src/clamav/libclamav/readdb.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2013-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
3 | | * Copyright (C) 2007-2013 Sourcefire, Inc. |
4 | | * Copyright (C) 2002-2007 Tomasz Kojm <tkojm@clamav.net> |
5 | | * |
6 | | * Authors: Tomasz Kojm |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License version 2 as |
10 | | * published by the Free Software Foundation. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
20 | | * MA 02110-1301, USA. |
21 | | */ |
22 | | |
23 | | #if HAVE_CONFIG_H |
24 | | #include "clamav-config.h" |
25 | | #endif |
26 | | |
27 | | #include <stdio.h> |
28 | | #include <stdbool.h> |
29 | | #include <stdint.h> |
30 | | #include <stdlib.h> |
31 | | #include <string.h> |
32 | | #include <ctype.h> |
33 | | #ifdef HAVE_UNISTD_H |
34 | | #include <unistd.h> |
35 | | #endif |
36 | | #include <dirent.h> |
37 | | #include <sys/types.h> |
38 | | #include <sys/stat.h> |
39 | | #ifdef HAVE_SYS_PARAM_H |
40 | | #include <sys/param.h> |
41 | | #endif |
42 | | #include <fcntl.h> |
43 | | #include <zlib.h> |
44 | | #include <errno.h> |
45 | | |
46 | | #ifdef _WIN32 |
47 | | #include "libgen.h" |
48 | | #endif |
49 | | |
50 | | #include "clamav.h" |
51 | | #include "clamav_rust.h" |
52 | | #include "cvd.h" |
53 | | #ifdef HAVE_STRINGS_H |
54 | | #include <strings.h> |
55 | | #endif |
56 | | #include "matcher-ac.h" |
57 | | #include "matcher-bm.h" |
58 | | #include "matcher-pcre.h" |
59 | | #include "matcher-byte-comp.h" |
60 | | #include "matcher-hash.h" |
61 | | #include "matcher.h" |
62 | | #include "others.h" |
63 | | #include "str.h" |
64 | | #include "dconf.h" |
65 | | #include "filetypes.h" |
66 | | #include "filetypes_int.h" |
67 | | #include "readdb.h" |
68 | | #include "default.h" |
69 | | #include "dsig.h" |
70 | | #include "asn1.h" |
71 | | |
72 | | #include "phishcheck.h" |
73 | | #include "phish_allow_list.h" |
74 | | #include "phish_domaincheck_db.h" |
75 | | #include "regex_list.h" |
76 | | #include "hashtab.h" |
77 | | |
78 | | #include "mpool.h" |
79 | | #include "bytecode.h" |
80 | | #include "bytecode_api.h" |
81 | | #include "bytecode_priv.h" |
82 | | #include "cache.h" |
83 | | #include "openioc.h" |
84 | | |
85 | | #ifdef CL_THREAD_SAFE |
86 | | #include <pthread.h> |
87 | | static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER; |
88 | | #endif |
89 | | |
90 | | #ifdef HAVE_YARA |
91 | | #include "yara_clam.h" |
92 | | #include "yara_compiler.h" |
93 | | #include "yara_grammar.h" |
94 | | #include "yara_lexer.h" |
95 | | #endif |
96 | | |
97 | | #ifdef _WIN32 |
98 | | static char DATABASE_DIRECTORY[MAX_PATH] = ""; |
99 | | #endif |
100 | | |
101 | | char *cli_virname(const char *virname, unsigned int official) |
102 | 4.41M | { |
103 | 4.41M | char *newname, *pt; |
104 | | |
105 | 4.41M | if (!virname) |
106 | 0 | return NULL; |
107 | | |
108 | 4.41M | if ((pt = strstr(virname, " (Clam)"))) |
109 | 14.8k | *pt = '\0'; |
110 | | |
111 | 4.41M | if (!virname[0]) { |
112 | 1.06k | cli_errmsg("cli_virname: Empty virus name\n"); |
113 | 1.06k | return NULL; |
114 | 1.06k | } |
115 | | |
116 | 4.40M | if (official) |
117 | 1.74M | return cli_strdup(virname); |
118 | | |
119 | 2.66M | newname = (char *)cli_malloc(strlen(virname) + 11 + 1); |
120 | 2.66M | if (!newname) { |
121 | 0 | cli_errmsg("cli_virname: Can't allocate memory for newname\n"); |
122 | 0 | return NULL; |
123 | 0 | } |
124 | 2.66M | sprintf(newname, "%s.UNOFFICIAL", virname); |
125 | 2.66M | return newname; |
126 | 2.66M | } |
127 | | |
128 | | cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, const char *hexsig, |
129 | | uint8_t sigopts, uint16_t rtype, uint16_t type, |
130 | | const char *offset, const uint32_t *lsigid, unsigned int options) |
131 | 65.7k | { |
132 | 65.7k | char *hexcpy, *start, *end, *mid; |
133 | 65.7k | unsigned int i; |
134 | 65.7k | int ret = CL_SUCCESS; |
135 | | |
136 | | /* |
137 | | * cyclic loops with cli_add_content_match_pattern are impossible now as cli_add_content_match_pattern |
138 | | * no longer calls cli_sigopts_handler; leaving here for safety |
139 | | */ |
140 | 65.7k | if (sigopts & ACPATT_OPTION_ONCE) { |
141 | 0 | cli_errmsg("cli_sigopts_handler: invalidly called multiple times!\n"); |
142 | 0 | return CL_EPARSE; |
143 | 0 | } |
144 | | |
145 | 65.7k | hexcpy = cli_strdup(hexsig); |
146 | 65.7k | if (!hexcpy) |
147 | 0 | return CL_EMEM; |
148 | | |
149 | 65.7k | sigopts |= ACPATT_OPTION_ONCE; |
150 | | |
151 | | /* REGEX testing and sigopt handling */ |
152 | 65.7k | start = strchr(hexcpy, '/'); |
153 | 65.7k | end = strrchr(hexcpy, '/'); |
154 | | |
155 | 65.7k | if (start != end) { |
156 | | /* FULLWORD regex sigopt handling */ |
157 | 0 | if (sigopts & ACPATT_OPTION_FULLWORD) { |
158 | 0 | size_t ovrlen = strlen(hexcpy) + 21; |
159 | 0 | char *hexovr = cli_calloc(ovrlen, sizeof(char)); |
160 | 0 | if (!hexovr) { |
161 | 0 | free(hexcpy); |
162 | 0 | return CL_EMEM; |
163 | 0 | } |
164 | | |
165 | 0 | *start++ = '\0'; |
166 | 0 | *end++ = '\0'; |
167 | |
|
168 | 0 | snprintf(hexovr, ovrlen, "%s/([\\W_]|\\A)%s([\\W_]|\\Z)/%s", hexcpy, start, end); |
169 | |
|
170 | 0 | free(hexcpy); |
171 | 0 | hexcpy = hexovr; |
172 | 0 | } |
173 | | /* NOCASE sigopt is passed onto the regex-opt handler */ |
174 | 0 | if (sigopts & ACPATT_OPTION_NOCASE) { |
175 | 0 | size_t ovrlen = strlen(hexcpy) + 2; |
176 | 0 | char *hexovr = cli_calloc(ovrlen, sizeof(char)); |
177 | 0 | if (!hexovr) { |
178 | 0 | free(hexcpy); |
179 | 0 | return CL_EMEM; |
180 | 0 | } |
181 | | |
182 | 0 | snprintf(hexovr, ovrlen, "%si", hexcpy); |
183 | |
|
184 | 0 | free(hexcpy); |
185 | 0 | hexcpy = hexovr; |
186 | 0 | } |
187 | | /* WIDE sigopt is unsupported */ |
188 | 0 | if (sigopts & ACPATT_OPTION_WIDE) { |
189 | 0 | cli_errmsg("cli_sigopts_handler: wide modifier [w] is not supported for regex subsigs\n"); |
190 | 0 | free(hexcpy); |
191 | 0 | return CL_EMALFDB; |
192 | 0 | } |
193 | | |
194 | 0 | ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options); |
195 | 0 | free(hexcpy); |
196 | 0 | return ret; |
197 | 0 | } |
198 | | |
199 | | /* BCOMP sigopt handling */ |
200 | 65.7k | start = strchr(hexcpy, '#'); |
201 | 65.7k | end = strrchr(hexcpy, '#'); |
202 | 65.7k | mid = strchr(hexcpy, '('); |
203 | | |
204 | 65.7k | if (start != end && mid && (*(++mid) == '#' || !strncmp(mid, ">>", 2) || !strncmp(mid, "<<", 2) || !strncmp(mid, "0#", 2))) { |
205 | | /* TODO byte compare currently does not have support for sigopts, pass through */ |
206 | 0 | ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options); |
207 | 0 | free(hexcpy); |
208 | 0 | return ret; |
209 | 0 | } |
210 | | |
211 | | /* NORMAL HEXSIG sigopt handling */ |
212 | | /* FULLWORD sigopt handling - only happens once */ |
213 | 65.7k | if (sigopts & ACPATT_OPTION_FULLWORD) { |
214 | 3.57k | char *rechar; |
215 | 3.57k | size_t ovrlen = strlen(hexcpy) + 7; |
216 | 3.57k | char *hexovr = cli_calloc(ovrlen, sizeof(char)); |
217 | 3.57k | if (!hexovr) { |
218 | 0 | free(hexcpy); |
219 | 0 | return CL_EMEM; |
220 | 0 | } |
221 | | |
222 | 3.57k | snprintf(hexovr, ovrlen, "(W)%s(W)", hexcpy); |
223 | | |
224 | | /* change the '[' and ']' to '{' and '}' since there are now two bytes */ |
225 | 3.57k | rechar = hexovr; |
226 | 3.84k | while ((rechar = strchr(rechar, '['))) { // TEST TODO |
227 | 280 | *rechar = '{'; |
228 | | |
229 | 280 | if (!(rechar = strchr(rechar, ']'))) { |
230 | 8 | cli_errmsg("cli_sigopts_handler: unmatched '[' in signature %s\n", virname); |
231 | 8 | free(hexcpy); |
232 | 8 | free(hexovr); |
233 | 8 | return CL_EMALFDB; |
234 | 8 | } |
235 | 272 | *rechar = '}'; |
236 | 272 | } |
237 | | |
238 | 3.56k | free(hexcpy); |
239 | 3.56k | hexcpy = hexovr; |
240 | 3.56k | } |
241 | | |
242 | | /* WIDE sigopt handling - only happens once (after fullword) |
243 | | * TODO - consider handling in cli_ac_addpatt? (two pattern possibility) |
244 | | */ |
245 | 65.7k | if (sigopts & ACPATT_OPTION_WIDE) { |
246 | 4.78k | size_t hexcpylen = strlen(hexcpy); |
247 | 4.78k | size_t ovrlen = 2 * hexcpylen + 1; |
248 | 4.78k | char *hexovr = cli_calloc(ovrlen, sizeof(char)); |
249 | 4.78k | if (!hexovr) { |
250 | 0 | free(hexcpy); |
251 | 0 | return CL_EMEM; |
252 | 0 | } |
253 | | |
254 | | /* clamav-specific wildcards need to be handled here! */ |
255 | 469k | for (i = 0; i < hexcpylen; ++i) { |
256 | 464k | size_t len = strlen(hexovr); |
257 | | |
258 | 464k | if (hexcpy[i] == '*' || hexcpy[i] == '|' || hexcpy[i] == ')') { |
259 | 42.3k | hexovr[len] = hexcpy[i]; |
260 | 422k | } else if (hexcpy[i] == '[') { |
261 | | /* change the '[' and ']' to '{' and '}' since there are now two bytes */ |
262 | 819 | hexovr[len++] = '{'; |
263 | 819 | ++i; |
264 | 53.7k | while (i < strlen(hexcpy) && hexcpy[i] != ']') |
265 | 52.8k | hexovr[len++] = hexcpy[i++]; |
266 | | |
267 | 819 | hexovr[len] = '}'; |
268 | 421k | } else if (hexcpy[i] == '{') { |
269 | 40.3k | while (i < hexcpylen && hexcpy[i] != '}') |
270 | 32.7k | hexovr[len++] = hexcpy[i++]; |
271 | | |
272 | 7.62k | hexovr[len] = '}'; |
273 | 414k | } else if (hexcpy[i] == '!' || hexcpy[i] == '(') { |
274 | 35.4k | if (hexcpy[i] == '!') |
275 | 1.91k | hexovr[len++] = hexcpy[i++]; |
276 | | |
277 | | /* copies '(' */ |
278 | 35.4k | hexovr[len] = hexcpy[i]; |
279 | | |
280 | 35.4k | if (i + 2 >= hexcpylen) { |
281 | 23 | free(hexcpy); |
282 | 23 | free(hexovr); |
283 | 23 | return CL_EMALFDB; |
284 | 35.4k | } else if (hexcpy[i + 1] == 'B' || hexcpy[i + 1] == 'L' || hexcpy[i + 1] == 'W') { |
285 | 4.80k | ++len; |
286 | 4.80k | ++i; |
287 | 4.80k | hexovr[len++] = hexcpy[i++]; |
288 | 4.80k | if (hexcpy[i] != ')') { |
289 | 18 | free(hexcpy); |
290 | 18 | free(hexovr); |
291 | 18 | return CL_EMALFDB; |
292 | 18 | } |
293 | 4.78k | hexovr[len] = hexcpy[i]; |
294 | 4.78k | } |
295 | 378k | } else { |
296 | | // snprintf(hexovr+len, ovrlen-len, "%02x%c%c", 0, hexcpy[i], hexcpy[i+1]); |
297 | 378k | snprintf(hexovr + len, ovrlen - len, "%c%c%02x", hexcpy[i], hexcpy[i + 1], 0); |
298 | 378k | ++i; |
299 | 378k | } |
300 | 464k | } |
301 | | |
302 | | /* NOCASE sigopt is handled in cli_ac_addsig */ |
303 | 4.73k | ret = cli_add_content_match_pattern(root, virname, hexovr, sigopts, rtype, type, offset, lsigid, options); |
304 | 4.73k | free(hexovr); |
305 | 4.73k | if (ret != CL_SUCCESS || !(sigopts & ACPATT_OPTION_ASCII)) { |
306 | 2.37k | free(hexcpy); |
307 | 2.37k | return ret; |
308 | 2.37k | } else { |
309 | | /* disable wide sigopt for ascii variant */ |
310 | 2.36k | sigopts &= ~ACPATT_OPTION_WIDE; |
311 | 2.36k | } |
312 | 4.73k | } |
313 | | |
314 | | /* ASCII sigopt; NOCASE sigopt is handled in cli_ac_addsig */ |
315 | 63.3k | ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options); |
316 | 63.3k | free(hexcpy); |
317 | 63.3k | return ret; |
318 | 65.7k | } |
319 | | |
320 | | /** |
321 | | * @brief Parse a regex term: a logical subsignature or yara regex string |
322 | | * |
323 | | * expected format => ^offset:trigger/regex/[cflags]$ |
324 | | * |
325 | | * @param root The matcher root (engine structure containing loaded signature patterns for matching) |
326 | | * @param virname Name of signature that this regex subsig came from. |
327 | | * @param hexsig The string containing the regex |
328 | | * @param offset The string offset where the pattern starts |
329 | | * @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing. |
330 | | * @param options Database options. See CL_DB_* macros in clamav.h. |
331 | | * @return cl_error_t |
332 | | */ |
333 | | static cl_error_t readdb_load_regex_subsignature(struct cli_matcher *root, const char *virname, char *hexsig, |
334 | | const char *offset, const uint32_t *lsigid, unsigned int options) |
335 | 3.49k | { |
336 | 3.49k | cl_error_t status = CL_EPARSE; |
337 | 3.49k | cl_error_t ret; |
338 | 3.49k | char *hexcpy = NULL; |
339 | 3.49k | char *start = NULL; |
340 | 3.49k | char *end = NULL; |
341 | | |
342 | 3.49k | const char *trigger, *pattern, *cflags; |
343 | | |
344 | | // The maximum number of `:` delimited fields in a regex subsignature. |
345 | 3.49k | #define MAX_REGEX_SUB_TOKENS 4 |
346 | 3.49k | char *subtokens[MAX_REGEX_SUB_TOKENS + 1]; |
347 | 3.49k | const char *sig; |
348 | | |
349 | 3.49k | if (0 == strncmp(virname, "YARA", 4)) { |
350 | | // Do not tokenize for ':' in yara regex strings. ':' do not have special meaning in yara regex strings. |
351 | | // Also, Yara regex strings may use '/' without escape characters, which confuses the "within_pcre" feature of `cli_ldbtokenize()`. |
352 | 576 | sig = hexsig; |
353 | | |
354 | 2.91k | } else { |
355 | | // LDB PCRE subsignatures have this structure: |
356 | | // [Offset:]Trigger/PCRE/[Flags] |
357 | | // We need to split on the ':' character in case the offset was specified. |
358 | | |
359 | 2.91k | size_t subtokens_count = cli_ldbtokenize(hexsig, ':', MAX_REGEX_SUB_TOKENS + 1, (const char **)subtokens, 0); |
360 | 2.91k | if (!subtokens_count) { |
361 | 0 | cli_errmsg("Invalid or unsupported ldb subsignature format\n"); |
362 | 0 | status = CL_EMALFDB; |
363 | 0 | goto done; |
364 | 0 | } |
365 | | |
366 | 2.91k | if (subtokens_count == 2) { |
367 | | // Offset was specified |
368 | 2.46k | offset = subtokens[0]; |
369 | 2.46k | sig = subtokens[1]; |
370 | 2.46k | } else { |
371 | 448 | sig = subtokens[0]; |
372 | 448 | } |
373 | 2.91k | } |
374 | | |
375 | | /* get copied */ |
376 | 3.49k | hexcpy = cli_strdup(sig); |
377 | 3.49k | if (!hexcpy) { |
378 | 0 | status = CL_EMEM; |
379 | 0 | goto done; |
380 | 0 | } |
381 | | |
382 | | /* get delimiters-ed */ |
383 | 3.49k | start = strchr(hexcpy, '/'); |
384 | 3.49k | end = strrchr(hexcpy, '/'); |
385 | | |
386 | | /* get pcre-ed */ |
387 | 3.49k | if (start == end) { |
388 | 11 | cli_errmsg("PCRE subsig mismatched '/' delimiter\n"); |
389 | 11 | status = CL_EMALFDB; |
390 | 11 | goto done; |
391 | 11 | } |
392 | | |
393 | | /* get checked */ |
394 | 3.47k | if (hexsig[0] == '/') { |
395 | 5 | cli_errmsg("PCRE subsig must contain logical trigger\n"); |
396 | 5 | status = CL_EMALFDB; |
397 | 5 | goto done; |
398 | 5 | } |
399 | | |
400 | | /* get NULL-ed */ |
401 | 3.47k | *start = '\0'; |
402 | 3.47k | *end = '\0'; |
403 | | |
404 | | /* get tokens-ed */ |
405 | 3.47k | trigger = hexcpy; |
406 | 3.47k | pattern = start + 1; |
407 | 3.47k | cflags = end + 1; |
408 | 3.47k | if (*cflags == '\0') /* get compat-ed */ |
409 | 1.12k | cflags = NULL; |
410 | | |
411 | | /* normal trigger, get added */ |
412 | 3.47k | ret = cli_pcre_addpatt(root, virname, trigger, pattern, cflags, offset, lsigid, options); |
413 | 3.47k | if (CL_SUCCESS != ret) { |
414 | 309 | cli_errmsg("Problem adding PCRE subsignature.\n"); |
415 | 309 | status = ret; |
416 | 309 | goto done; |
417 | 309 | } |
418 | | |
419 | 3.16k | status = CL_SUCCESS; |
420 | | |
421 | 3.49k | done: |
422 | | |
423 | 3.49k | FREE(hexcpy); |
424 | | |
425 | 3.49k | return status; |
426 | 3.16k | } |
427 | | |
428 | | cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *virname, char *hexsig, |
429 | | const char *offset, const uint32_t *lsigid, unsigned int options, |
430 | | int current_subsig_index, int num_subsigs, struct cli_lsig_tdb *tdb) |
431 | 50.7k | { |
432 | 50.7k | cl_error_t status = CL_EPARSE; |
433 | 50.7k | cl_error_t ret; |
434 | 50.7k | char *hexcpy = NULL; |
435 | | |
436 | 50.7k | char *start = NULL, *mid = NULL, *end = NULL; |
437 | | |
438 | 50.7k | FFIError *fuzzy_hash_load_error = NULL; |
439 | | |
440 | 50.7k | if (hexsig[0] == '$') { |
441 | | /* |
442 | | * Looks like a macro subsignature |
443 | | */ |
444 | 2.59k | size_t hexlen; |
445 | 2.59k | unsigned int smin, smax, tid; |
446 | 2.59k | struct cli_ac_patt *patt; |
447 | | |
448 | 2.59k | hexlen = strlen(hexsig); |
449 | | |
450 | 2.59k | if (hexsig[hexlen - 1] != '$') { |
451 | 6 | cli_errmsg("Logical signature macro subsignature is missing the '$' terminator: %s\n", hexsig); |
452 | 6 | status = CL_EMALFDB; |
453 | 6 | goto done; |
454 | 6 | } |
455 | | |
456 | 2.58k | if (!lsigid) { |
457 | 0 | cli_errmsg("Macro subsignatures are only valid inside logical signatures\n"); |
458 | 0 | status = CL_EMALFDB; |
459 | 0 | goto done; |
460 | 0 | } |
461 | | |
462 | 2.58k | if (sscanf(hexsig, "${%u-%u}%u$", &smin, &smax, &tid) != 3) { |
463 | 3 | cli_errmsg("Invalid logical macro subsignature format: %s\n", hexsig); |
464 | 3 | status = CL_EMALFDB; |
465 | 3 | goto done; |
466 | 3 | } |
467 | | |
468 | 2.58k | if (tid >= 32) { |
469 | 19 | cli_errmsg("Invalid logical subsignature: only 32 macro groups are supported. %u macro groups found.\n", tid); |
470 | 19 | status = CL_EMALFDB; |
471 | 19 | goto done; |
472 | 19 | } |
473 | | |
474 | 2.56k | patt = MPOOL_CALLOC(root->mempool, 1, sizeof(*patt)); |
475 | 2.56k | if (!patt) { |
476 | 0 | cli_errmsg("Failed to allocate memory for macro AC pattern struct\n"); |
477 | 0 | status = CL_EMEM; |
478 | 0 | goto done; |
479 | 0 | } |
480 | | |
481 | | /* this is not a pattern that will be matched by AC itself, rather it is a |
482 | | * pattern checked by the lsig code */ |
483 | 2.56k | patt->ch_mindist[0] = smin; |
484 | 2.56k | patt->ch_maxdist[0] = smax; |
485 | 2.56k | patt->sigid = tid; |
486 | 2.56k | patt->length[0] = root->ac_mindepth; |
487 | | |
488 | | /* dummy */ |
489 | 2.56k | patt->pattern = MPOOL_CALLOC(root->mempool, patt->length[0], sizeof(*patt->pattern)); |
490 | 2.56k | if (!patt->pattern) { |
491 | 0 | free(patt); |
492 | 0 | status = CL_EMEM; |
493 | 0 | goto done; |
494 | 0 | } |
495 | | |
496 | 2.56k | if (CL_SUCCESS != (ret = cli_ac_addpatt(root, patt))) { |
497 | 0 | MPOOL_FREE(root->mempool, patt->pattern); |
498 | 0 | free(patt); |
499 | 0 | status = ret; |
500 | 0 | goto done; |
501 | 0 | } |
502 | | |
503 | 2.56k | if (current_subsig_index > 0) { |
504 | | /* allow mapping from lsig back to pattern for macros */ |
505 | 2.56k | if (!tdb->macro_ptids) |
506 | 1.70k | tdb->macro_ptids = MPOOL_CALLOC(root->mempool, num_subsigs, sizeof(*tdb->macro_ptids)); |
507 | 2.56k | if (!tdb->macro_ptids) { |
508 | 0 | status = CL_EMEM; |
509 | 0 | goto done; |
510 | 0 | } |
511 | | |
512 | 2.56k | tdb->macro_ptids[current_subsig_index - 1] = root->ac_patterns - 1; |
513 | 2.56k | } |
514 | | |
515 | 48.1k | } else if (strchr(hexsig, '/')) { |
516 | | /* |
517 | | * Looks like a pcre subsignature. |
518 | | */ |
519 | 2.98k | ret = readdb_load_regex_subsignature(root, virname, hexsig, offset, lsigid, options); |
520 | 2.98k | if (CL_SUCCESS != ret) { |
521 | 303 | status = ret; |
522 | 303 | goto done; |
523 | 303 | } |
524 | | |
525 | 45.2k | } else if ((start = strchr(hexsig, '(')) && (mid = strchr(hexsig, '#')) && (end = strrchr(hexsig, '#')) && mid != end) { |
526 | | /* |
527 | | * Looks like an byte_compare subsignature. |
528 | | */ |
529 | 230 | if (CL_SUCCESS != (ret = cli_bcomp_addpatt(root, virname, hexsig, lsigid, options))) { |
530 | 229 | cli_errmsg("Problem adding byte compare subsignature: %s\n", hexsig); |
531 | 229 | status = ret; |
532 | 229 | goto done; |
533 | 229 | } |
534 | | |
535 | 44.9k | } else if (0 == strncmp(hexsig, "fuzzy_img#", strlen("fuzzy_img#"))) { |
536 | | /* |
537 | | * format seems to match fuzzy image hash |
538 | | */ |
539 | 231 | bool load_successful; |
540 | | |
541 | 231 | if (lsigid != NULL) { |
542 | | /* fuzzy hash is a part of a logical signature (normal use case) */ |
543 | 231 | load_successful = fuzzy_hash_load_subsignature(root->fuzzy_hashmap, hexsig, lsigid[0], lsigid[1], &fuzzy_hash_load_error); |
544 | 231 | } else { |
545 | | /* No logical signature, must be `sigtool --test-sigs` |
546 | | * TODO: sigtool should really load the logical sig properly and we can get rid of this logic. |
547 | | * Note: similar functionality is inside of cli_bcomp_addpatt() and cli_pcre_addpatt() */ |
548 | 0 | load_successful = fuzzy_hash_load_subsignature(root->fuzzy_hashmap, hexsig, 0, 0, &fuzzy_hash_load_error); |
549 | 0 | } |
550 | | |
551 | 231 | if (!load_successful) { |
552 | 1 | cli_errmsg( |
553 | 1 | "Failed to load fuzzy hash logical subsignature '%s': %s\n" |
554 | 1 | "Expected format: algorithm#hash[#hammingdistance]\n" |
555 | 1 | " where\n" |
556 | 1 | " - algorithm: Must be 'fuzzy_img'\n" |
557 | 1 | " - hash: Must be an 8-byte hex string\n" |
558 | 1 | " - hammingdistance: (optional) Must be an unsigned integer\n", |
559 | 1 | hexsig, ffierror_fmt(fuzzy_hash_load_error)); |
560 | | |
561 | 1 | status = CL_EFORMAT; |
562 | 1 | goto done; |
563 | 1 | } |
564 | | |
565 | 44.7k | } else { |
566 | | /* |
567 | | * Looks like an AC/BM content match subsignature. |
568 | | */ |
569 | 44.7k | const char *sigopts = NULL; |
570 | 44.7k | uint8_t subsig_opts = 0; |
571 | 44.7k | int subtokens_count; |
572 | 44.7k | const char *sig; |
573 | | // The maximum number of `:` delimited fields in a regex subsignature. |
574 | 44.7k | #define MAX_CONTENTMATCH_SUB_TOKENS 4 |
575 | 44.7k | char *subtokens[MAX_CONTENTMATCH_SUB_TOKENS + 1]; |
576 | | |
577 | 44.7k | subtokens_count = cli_ldbtokenize(hexsig, ':', MAX_CONTENTMATCH_SUB_TOKENS + 1, (const char **)subtokens, 0); |
578 | 44.7k | if (!subtokens_count) { |
579 | 0 | cli_errmsg("Invalid or unsupported ldb subsignature format\n"); |
580 | 0 | status = CL_EMALFDB; |
581 | 0 | goto done; |
582 | 0 | } |
583 | | |
584 | 44.7k | if ((subtokens_count % 2) == 0) |
585 | 33.5k | offset = subtokens[0]; |
586 | | |
587 | 44.7k | if (subtokens_count == 3) |
588 | 4.43k | sigopts = subtokens[2]; |
589 | 40.3k | else if (subtokens_count == 4) |
590 | 3.99k | sigopts = subtokens[3]; |
591 | | |
592 | 44.7k | if (sigopts) { /* signature modifiers */ |
593 | 8.42k | size_t j; |
594 | 60.7k | for (j = 0; j < strlen(sigopts); j++) |
595 | 52.3k | switch (sigopts[j]) { |
596 | 3.89k | case 'i': |
597 | 3.89k | subsig_opts |= ACPATT_OPTION_NOCASE; |
598 | 3.89k | break; |
599 | 3.56k | case 'f': |
600 | 3.56k | subsig_opts |= ACPATT_OPTION_FULLWORD; |
601 | 3.56k | break; |
602 | 7.46k | case 'w': |
603 | 7.46k | subsig_opts |= ACPATT_OPTION_WIDE; |
604 | 7.46k | break; |
605 | 37.3k | case 'a': |
606 | 37.3k | subsig_opts |= ACPATT_OPTION_ASCII; |
607 | 37.3k | break; |
608 | 23 | default: |
609 | 23 | cli_errmsg("Signature for %s uses invalid option: %02x\n", virname, sigopts[j]); |
610 | 23 | status = CL_EMALFDB; |
611 | 23 | goto done; |
612 | 52.3k | } |
613 | 8.42k | } |
614 | | |
615 | 44.7k | sig = (subtokens_count % 2) ? subtokens[0] : subtokens[1]; |
616 | | |
617 | 44.7k | if (subsig_opts) { |
618 | 7.68k | ret = cli_sigopts_handler(root, virname, sig, subsig_opts, 0, 0, offset, lsigid, options); |
619 | 37.0k | } else { |
620 | 37.0k | ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, lsigid, options); |
621 | 37.0k | } |
622 | | |
623 | 44.7k | if (CL_SUCCESS != ret) { |
624 | 3.49k | status = ret; |
625 | 3.49k | goto done; |
626 | 3.49k | } |
627 | 44.7k | } |
628 | | |
629 | 46.7k | status = CL_SUCCESS; |
630 | | |
631 | 50.7k | done: |
632 | | |
633 | 50.7k | if (NULL != fuzzy_hash_load_error) { |
634 | 1 | ffierror_free(fuzzy_hash_load_error); |
635 | 1 | } |
636 | | |
637 | 50.7k | FREE(hexcpy); |
638 | | |
639 | 50.7k | return status; |
640 | 46.7k | } |
641 | | |
642 | | /** |
643 | | * @brief Parse a yara string (subsignature equivalent in yara lingo). |
644 | | * |
645 | | * @param root The matcher root (engine structure containing loaded signature patterns for matching) |
646 | | * @param virname Name of signature that this regex subsig came from. |
647 | | * @param hexsig The string containing the regex |
648 | | * @param subsig_opts Content match pattern options. See ACPATT_* macros in matcher-ac.h. |
649 | | * @param offset The string offset where the pattern starts |
650 | | * @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing. |
651 | | * @param options Database options. See CL_DB_* macros in clamav.h. |
652 | | * @return cl_error_t |
653 | | */ |
654 | | static cl_error_t readdb_parse_yara_string(struct cli_matcher *root, const char *virname, char *hexsig, uint8_t subsig_opts, |
655 | | const char *offset, const uint32_t *lsigid, unsigned int options) |
656 | 34.9k | { |
657 | 34.9k | cl_error_t status = CL_EPARSE; |
658 | 34.9k | cl_error_t ret; |
659 | | |
660 | 34.9k | if (strchr(hexsig, '/')) { |
661 | | /* |
662 | | * Looks like a pcre subsignature. |
663 | | */ |
664 | 509 | ret = readdb_load_regex_subsignature(root, virname, hexsig, offset, lsigid, options); |
665 | | |
666 | 34.4k | } else { |
667 | | /* |
668 | | * Looks like an AC/BM content match subsignature. |
669 | | */ |
670 | 34.4k | if (subsig_opts) { |
671 | 34.4k | ret = cli_sigopts_handler(root, virname, hexsig, subsig_opts, 0, 0, offset, lsigid, options); |
672 | 34.4k | } else { |
673 | 0 | ret = cli_add_content_match_pattern(root, virname, hexsig, 0, 0, 0, offset, lsigid, options); |
674 | 0 | } |
675 | 34.4k | } |
676 | | |
677 | 34.9k | if (CL_SUCCESS != ret) { |
678 | 25 | status = ret; |
679 | 25 | goto done; |
680 | 25 | } |
681 | | |
682 | 34.9k | status = CL_SUCCESS; |
683 | | |
684 | 34.9k | done: |
685 | | |
686 | 34.9k | return status; |
687 | 34.9k | } |
688 | | |
689 | | #define PCRE_TOKENS 4 |
690 | | /** |
691 | | * @brief Load body-based content patterns that will be matched with AC or BM matchers |
692 | | * |
693 | | * May be used when loading db, ndb, ldb subsignatures, ftm, and even patterns crafted from yara rules. |
694 | | * |
695 | | * @param root The matcher root (engine structure containing loaded signature patterns for matching) |
696 | | * @param virname Name of signature that this regex subsig came from. |
697 | | * @param hexsig The string containing the regex |
698 | | * @param sigopts Content match pattern options. See ACPATT_* macros in matcher-ac.h. |
699 | | * @param rtype |
700 | | * @param type |
701 | | * @param offset The string offset where the pattern starts |
702 | | * @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing. |
703 | | * @param options Database options. See CL_DB_* macros in clamav.h. |
704 | | * @return cl_error_t |
705 | | */ |
706 | | cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *virname, const char *hexsig, |
707 | | uint8_t sigopts, uint16_t rtype, uint16_t type, |
708 | | const char *offset, const uint32_t *lsigid, unsigned int options) |
709 | 1.89M | { |
710 | 1.89M | struct cli_bm_patt *bm_new; |
711 | 1.89M | char *pt, *hexcpy, *n, l, r; |
712 | 1.89M | const char *wild; |
713 | 1.89M | cl_error_t ret; |
714 | 1.89M | bool asterisk = false; |
715 | 1.89M | size_t range, i, j, hexlen, nest; |
716 | 1.89M | int mindist = 0, maxdist = 0, error = 0; |
717 | 1.89M | char *start = NULL; |
718 | | |
719 | 1.89M | hexlen = strlen(hexsig); |
720 | | |
721 | 1.89M | if ((wild = strchr(hexsig, '{'))) { |
722 | | /* |
723 | | * hexsig contains '{' for "{n}" or "{n-m}" wildcard |
724 | | */ |
725 | 784k | uint16_t parts = 1; |
726 | | |
727 | 784k | if (sscanf(wild, "%c%zu%c", &l, &range, &r) == 3 && l == '{' && r == '}' && range > 0 && range < 128) { |
728 | | /* |
729 | | * Parse "{n}" wildcard - Not a "{n-m}" range-style one. |
730 | | * Replaces it with: "??" * n and then re-parses the modified hexsig with recursion. |
731 | | */ |
732 | 425k | hexcpy = cli_calloc(hexlen + 2 * range, sizeof(char)); |
733 | 425k | if (!hexcpy) |
734 | 0 | return CL_EMEM; |
735 | | |
736 | 425k | strncpy(hexcpy, hexsig, wild - hexsig); |
737 | 13.7M | for (i = 0; i < range; i++) { |
738 | 13.3M | strcat(hexcpy, "??"); |
739 | 13.3M | } |
740 | | |
741 | 425k | if (!(wild = strchr(wild, '}'))) { |
742 | 0 | cli_errmsg("cli_add_content_match_pattern: Problem adding signature: missing bracket\n"); |
743 | 0 | free(hexcpy); |
744 | 0 | return CL_EMALFDB; |
745 | 0 | } |
746 | | |
747 | 425k | strcat(hexcpy, ++wild); |
748 | 425k | ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options); |
749 | 425k | free(hexcpy); |
750 | | |
751 | 425k | return ret; |
752 | 425k | } |
753 | | |
754 | 359k | root->ac_partsigs++; |
755 | | |
756 | | /* |
757 | | * Identify number of signature pattern parts (e.g. patterns separated by "{n}" or '*') |
758 | | * Have to figure this out in advance because `cli_ac_addsig()` needs to know which i-out-of-n parts when adding. |
759 | | */ |
760 | 359k | nest = 0; |
761 | 34.2M | for (i = 0; i < hexlen; i++) { |
762 | 33.9M | if (hexsig[i] == '(') { |
763 | 863k | nest++; |
764 | | |
765 | 33.0M | } else if (hexsig[i] == ')') { |
766 | 858k | nest--; |
767 | | |
768 | 32.1M | } else if (hexsig[i] == '{') { |
769 | | /* Found "{n}" or "{min-max}" wildcard. That means we've found a new hexsig "part" */ |
770 | | |
771 | 392k | if (nest) { |
772 | | /* Can't use "{n}" or "{min-max}" wildcard inside of a (aa|bb) alternative match pattern. */ |
773 | 150 | cli_errmsg("cli_add_content_match_pattern: Alternative match contains unsupported ranged wildcard\n"); |
774 | 150 | return CL_EMALFDB; |
775 | 150 | } |
776 | | |
777 | 392k | parts++; |
778 | | |
779 | 31.7M | } else if (hexsig[i] == '*') { |
780 | | /* Found '*' wildcard. That means we've found a new hexsig "part" */ |
781 | | |
782 | 254k | if (nest) { |
783 | | /* Can't use '*' wildcard inside of a (aa|bb) alternative match pattern. */ |
784 | 81 | cli_errmsg("cli_add_content_match_pattern: Alternative match cannot contain unbounded wildcards\n"); |
785 | 81 | return CL_EMALFDB; |
786 | 81 | } |
787 | | |
788 | 254k | parts++; |
789 | 254k | } |
790 | 33.9M | } |
791 | | |
792 | | /* |
793 | | * Now find each part again *cough*, and this time call cli_ac_addsig() for each. |
794 | | */ |
795 | | |
796 | | // Make a copy of the whole pattern so that we can NULL-terminate the hexsig |
797 | | // and pass it to cli_ac_addsig() without having to pass the part-length. |
798 | 359k | if (!(hexcpy = cli_strdup(hexsig))) |
799 | 0 | return CL_EMEM; |
800 | | |
801 | 359k | start = pt = hexcpy; |
802 | 978k | for (i = 1; i <= parts; i++) { |
803 | 978k | if (i != parts) { |
804 | 17.8M | for (j = 0; j < strlen(start); j++) { |
805 | 17.8M | if (start[j] == '{') { |
806 | 369k | asterisk = false; |
807 | 369k | pt = start + j; |
808 | 369k | break; |
809 | 369k | } |
810 | | |
811 | 17.4M | if (start[j] == '*') { |
812 | 250k | asterisk = true; |
813 | 250k | pt = start + j; |
814 | 250k | break; |
815 | 250k | } |
816 | 17.4M | } |
817 | | |
818 | 620k | *pt++ = 0; |
819 | 620k | } |
820 | | |
821 | 978k | if (CL_SUCCESS != (ret = cli_ac_addsig(root, virname, start, sigopts, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) { |
822 | 1.38k | cli_errmsg("cli_add_content_match_pattern: Problem adding signature (1).\n"); |
823 | 1.38k | error = 1; |
824 | 1.38k | break; |
825 | 1.38k | } |
826 | | |
827 | 976k | if (i == parts) |
828 | 357k | break; |
829 | | |
830 | | // This time around, we need to parse the integer values from "{n}" or "{min-max}" |
831 | | // to be used when we call `cli_ac_addsig()` for the next part. |
832 | 619k | mindist = maxdist = 0; |
833 | | |
834 | 619k | if (asterisk) { |
835 | 250k | start = pt; |
836 | 250k | continue; |
837 | 250k | } |
838 | | |
839 | 368k | if (!(start = strchr(pt, '}'))) { |
840 | 148 | error = 1; |
841 | 148 | break; |
842 | 148 | } |
843 | | |
844 | 368k | *start++ = 0; |
845 | | |
846 | 368k | if (!pt) { |
847 | 0 | error = 1; |
848 | 0 | break; |
849 | 0 | } |
850 | | |
851 | 368k | if (!strchr(pt, '-')) { |
852 | | // Pattern is "{n}" |
853 | 48.6k | if (!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) { |
854 | 162 | error = 1; |
855 | 162 | break; |
856 | 162 | } |
857 | 319k | } else { |
858 | | // pattern is "{min-max}" |
859 | 319k | if ((n = cli_strtok(pt, 0, "-"))) { |
860 | 149k | if (!cli_isnumber(n) || (mindist = atoi(n)) < 0) { |
861 | 119 | error = 1; |
862 | 119 | free(n); |
863 | 119 | break; |
864 | 119 | } |
865 | | |
866 | 149k | free(n); |
867 | 149k | } |
868 | | |
869 | 319k | if ((n = cli_strtok(pt, 1, "-"))) { |
870 | 314k | if (!cli_isnumber(n) || (maxdist = atoi(n)) < 0) { |
871 | 122 | error = 1; |
872 | 122 | free(n); |
873 | 122 | break; |
874 | 122 | } |
875 | | |
876 | 314k | free(n); |
877 | 314k | } |
878 | | |
879 | 319k | if ((n = cli_strtok(pt, 2, "-"))) { /* strict check */ |
880 | 18 | error = 1; |
881 | 18 | free(n); |
882 | 18 | break; |
883 | 18 | } |
884 | 319k | } |
885 | 368k | } |
886 | | |
887 | 359k | free(hexcpy); |
888 | 359k | if (error) { |
889 | 1.95k | cli_errmsg("cli_add_content_match_pattern: Problem adding signature (1b).\n"); |
890 | 1.95k | return CL_EMALFDB; |
891 | 1.95k | } |
892 | | |
893 | 1.10M | } else if (strchr(hexsig, '*')) { |
894 | | /* |
895 | | * hexsig contains '*' for `*` wildcard |
896 | | */ |
897 | 118k | uint16_t parts = 1; |
898 | | |
899 | 118k | root->ac_partsigs++; |
900 | | |
901 | 118k | nest = 0; |
902 | 15.8M | for (i = 0; i < hexlen; i++) { |
903 | 15.7M | if (hexsig[i] == '(') |
904 | 306k | nest++; |
905 | 15.4M | else if (hexsig[i] == ')') |
906 | 357k | nest--; |
907 | 15.1M | else if (hexsig[i] == '*') { |
908 | 847k | if (nest) { |
909 | 118 | cli_errmsg("cli_add_content_match_pattern: Alternative match cannot contain unbounded wildcards\n"); |
910 | 118 | return CL_EMALFDB; |
911 | 118 | } |
912 | 846k | parts++; |
913 | 846k | } |
914 | 15.7M | } |
915 | | |
916 | 1.06M | for (i = 1; i <= parts; i++) { |
917 | 948k | if ((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) { |
918 | 347 | cli_errmsg("cli_add_content_match_pattern: Can't extract part %zu of partial signature.\n", i); |
919 | 347 | return CL_EMALFDB; |
920 | 347 | } |
921 | | |
922 | 947k | if (CL_SUCCESS != (ret = cli_ac_addsig(root, virname, pt, sigopts, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) { |
923 | 949 | cli_errmsg("cli_add_content_match_pattern: Problem adding signature (2).\n"); |
924 | 949 | free(pt); |
925 | 949 | return ret; |
926 | 949 | } |
927 | | |
928 | 946k | free(pt); |
929 | 946k | } |
930 | | |
931 | 989k | } else if (root->ac_only || type || lsigid || sigopts || strpbrk(hexsig, "?([") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strchr(offset, '$')) { |
932 | | /* |
933 | | * format seems like it must be handled with the Aho-Corasick (AC) pattern matcher. |
934 | | */ |
935 | 982k | if (CL_SUCCESS != (ret = cli_ac_addsig(root, virname, hexsig, sigopts, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) { |
936 | 9.19k | cli_errmsg("cli_add_content_match_pattern: Problem adding signature (3).\n"); |
937 | 9.19k | return ret; |
938 | 9.19k | } |
939 | | |
940 | 982k | } else { |
941 | | /* |
942 | | * format seems like it can be handled with the Boyer-Moore (BM) pattern matcher. |
943 | | */ |
944 | 6.45k | bm_new = (struct cli_bm_patt *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_bm_patt)); |
945 | 6.45k | if (!bm_new) |
946 | 0 | return CL_EMEM; |
947 | | |
948 | 6.45k | bm_new->pattern = (unsigned char *)CLI_MPOOL_HEX2STR(root->mempool, hexsig); |
949 | 6.45k | if (!bm_new->pattern) { |
950 | 26 | MPOOL_FREE(root->mempool, bm_new); |
951 | 26 | return CL_EMALFDB; |
952 | 26 | } |
953 | | |
954 | 6.43k | bm_new->length = hexlen / 2; |
955 | | |
956 | 6.43k | bm_new->virname = CLI_MPOOL_VIRNAME(root->mempool, virname, options & CL_DB_OFFICIAL); |
957 | 6.43k | if (!bm_new->virname) { |
958 | 93 | MPOOL_FREE(root->mempool, bm_new->pattern); |
959 | 93 | MPOOL_FREE(root->mempool, bm_new); |
960 | 93 | return CL_EMEM; |
961 | 93 | } |
962 | | |
963 | 6.33k | if (bm_new->length > root->maxpatlen) |
964 | 471 | root->maxpatlen = bm_new->length; |
965 | | |
966 | 6.33k | if (CL_SUCCESS != (ret = cli_bm_addpatt(root, bm_new, offset))) { |
967 | 45 | cli_errmsg("cli_add_content_match_pattern: Problem adding signature (4).\n"); |
968 | 45 | MPOOL_FREE(root->mempool, bm_new->pattern); |
969 | 45 | MPOOL_FREE(root->mempool, bm_new->virname); |
970 | 45 | MPOOL_FREE(root->mempool, bm_new); |
971 | 45 | return ret; |
972 | 45 | } |
973 | 6.33k | } |
974 | | |
975 | 1.45M | return CL_SUCCESS; |
976 | 1.89M | } |
977 | | |
978 | | cl_error_t cli_initroots(struct cl_engine *engine, unsigned int options) |
979 | 45.3k | { |
980 | 45.3k | int i, ret; |
981 | 45.3k | struct cli_matcher *root; |
982 | | |
983 | 45.3k | UNUSEDPARAM(options); |
984 | 45.3k | cli_dbgmsg("Initializing engine matching structures\n"); |
985 | | |
986 | 725k | for (i = 0; i < CLI_MTARGETS; i++) { |
987 | 680k | if (!engine->root[i]) { |
988 | 534k | root = engine->root[i] = (struct cli_matcher *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_matcher)); |
989 | 534k | if (!root) { |
990 | 0 | cli_errmsg("cli_initroots: Can't allocate memory for cli_matcher\n"); |
991 | 0 | return CL_EMEM; |
992 | 0 | } |
993 | | #ifdef USE_MPOOL |
994 | | root->mempool = engine->mempool; |
995 | | #endif |
996 | 534k | root->type = i; |
997 | 534k | if (cli_mtargets[i].ac_only || engine->ac_only) |
998 | 463k | root->ac_only = 1; |
999 | | |
1000 | 534k | if (CL_SUCCESS != (ret = cli_ac_init(root, engine->ac_mindepth, engine->ac_maxdepth, engine->dconf->other & OTHER_CONF_PREFILTERING))) { |
1001 | | /* no need to free previously allocated memory here */ |
1002 | 0 | cli_errmsg("cli_initroots: Can't initialise AC pattern matcher\n"); |
1003 | 0 | return ret; |
1004 | 0 | } |
1005 | | |
1006 | 534k | if (!root->ac_only) { |
1007 | 71.3k | if (CL_SUCCESS != (ret = cli_bm_init(root))) { |
1008 | 0 | cli_errmsg("cli_initroots: Can't initialise BM pattern matcher\n"); |
1009 | 0 | return ret; |
1010 | 0 | } |
1011 | 71.3k | } |
1012 | | |
1013 | 534k | root->fuzzy_hashmap = fuzzy_hashmap_new(); |
1014 | 534k | } |
1015 | 680k | } |
1016 | 45.3k | engine->root[1]->bm_offmode = 1; /* BM offset mode for PE files */ |
1017 | 45.3k | return CL_SUCCESS; |
1018 | 45.3k | } |
1019 | | |
1020 | | char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio) |
1021 | 2.86M | { |
1022 | 2.86M | if (fs) |
1023 | 2.86M | return fgets(buff, size, fs); |
1024 | | |
1025 | 0 | if (dbio->usebuf) { |
1026 | 0 | int bread; |
1027 | 0 | char *nl; |
1028 | |
|
1029 | 0 | while (1) { |
1030 | 0 | if (!dbio->bufpt) { |
1031 | 0 | if (!dbio->size) |
1032 | 0 | return NULL; |
1033 | | |
1034 | 0 | if (dbio->gzs) { |
1035 | 0 | bread = gzread(dbio->gzs, dbio->readpt, dbio->readsize); |
1036 | 0 | if (bread == -1) { |
1037 | 0 | cli_errmsg("cli_dbgets: gzread() failed\n"); |
1038 | 0 | return NULL; |
1039 | 0 | } |
1040 | 0 | } else { |
1041 | 0 | bread = fread(dbio->readpt, 1, dbio->readsize, dbio->fs); |
1042 | 0 | if (!bread && ferror(dbio->fs)) { |
1043 | 0 | cli_errmsg("cli_dbgets: fread() failed\n"); |
1044 | 0 | return NULL; |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 | if (!bread) |
1048 | 0 | return NULL; |
1049 | 0 | dbio->readpt[bread] = 0; |
1050 | 0 | dbio->bufpt = dbio->buf; |
1051 | 0 | dbio->size -= bread; |
1052 | 0 | dbio->bread += bread; |
1053 | 0 | if (dbio->hashctx) |
1054 | 0 | cl_update_hash(dbio->hashctx, dbio->readpt, bread); |
1055 | 0 | } |
1056 | 0 | if (dbio->chkonly && dbio->bufpt) { |
1057 | 0 | dbio->bufpt = NULL; |
1058 | 0 | dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1; |
1059 | 0 | continue; |
1060 | 0 | } |
1061 | 0 | nl = strchr(dbio->bufpt, '\n'); |
1062 | 0 | if (nl) { |
1063 | 0 | if (nl - dbio->bufpt >= size) { |
1064 | 0 | cli_errmsg("cli_dbgets: Line too long for provided buffer\n"); |
1065 | 0 | return NULL; |
1066 | 0 | } |
1067 | 0 | strncpy(buff, dbio->bufpt, nl - dbio->bufpt); |
1068 | 0 | buff[nl - dbio->bufpt] = 0; |
1069 | 0 | if (nl < dbio->buf + dbio->bufsize) { |
1070 | 0 | dbio->bufpt = ++nl; |
1071 | 0 | } else { |
1072 | 0 | dbio->bufpt = NULL; |
1073 | 0 | dbio->readpt = dbio->buf; |
1074 | 0 | dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1; |
1075 | 0 | } |
1076 | 0 | return buff; |
1077 | 0 | } else { |
1078 | 0 | unsigned int remain = dbio->buf + dbio->bufsize - 1 - dbio->bufpt; |
1079 | |
|
1080 | 0 | if (dbio->bufpt == dbio->buf) { |
1081 | 0 | cli_errmsg("cli_dbgets: Invalid data or internal buffer too small\n"); |
1082 | 0 | return NULL; |
1083 | 0 | } |
1084 | 0 | memmove(dbio->buf, dbio->bufpt, remain); |
1085 | 0 | dbio->readpt = dbio->buf + remain; |
1086 | 0 | dbio->readsize = dbio->bufsize - remain; |
1087 | 0 | dbio->readsize = dbio->size < dbio->bufsize - remain ? dbio->size : dbio->bufsize - remain - 1; |
1088 | 0 | dbio->bufpt = NULL; |
1089 | 0 | } |
1090 | 0 | } |
1091 | 0 | } else { /* use gzgets/fgets */ |
1092 | 0 | char *pt; |
1093 | 0 | unsigned int bs; |
1094 | |
|
1095 | 0 | if (!dbio->size) |
1096 | 0 | return NULL; |
1097 | | |
1098 | 0 | bs = dbio->size < size ? dbio->size + 1 : size; |
1099 | 0 | if (dbio->gzs) |
1100 | 0 | pt = gzgets(dbio->gzs, buff, bs); |
1101 | 0 | else |
1102 | 0 | pt = fgets(buff, bs, dbio->fs); |
1103 | |
|
1104 | 0 | if (!pt) { |
1105 | 0 | cli_errmsg("cli_dbgets: Preliminary end of data\n"); |
1106 | 0 | return pt; |
1107 | 0 | } |
1108 | 0 | bs = strlen(buff); |
1109 | 0 | dbio->size -= bs; |
1110 | 0 | dbio->bread += bs; |
1111 | 0 | if (dbio->hashctx) |
1112 | 0 | cl_update_hash(dbio->hashctx, buff, bs); |
1113 | 0 | return pt; |
1114 | 0 | } |
1115 | 0 | } |
1116 | | |
1117 | | static char *cli_signorm(const char *signame) |
1118 | 0 | { |
1119 | 0 | char *new_signame = NULL; |
1120 | 0 | size_t pad = 0; |
1121 | 0 | size_t nsz; |
1122 | |
|
1123 | 0 | if (!signame) |
1124 | 0 | return NULL; |
1125 | | |
1126 | 0 | nsz = strlen(signame); |
1127 | |
|
1128 | 0 | if (nsz > 3 && signame[nsz - 1] == '}') { |
1129 | 0 | char *pt = strstr(signame, ".{"); |
1130 | 0 | if (pt) /* strip the ".{ }" clause at the end of signame */ |
1131 | 0 | nsz = pt - signame; |
1132 | 0 | else |
1133 | 0 | return NULL; |
1134 | 0 | } else if (nsz > 11) { |
1135 | 0 | if (!strncmp(signame + nsz - 11, ".UNOFFICIAL", 11)) |
1136 | 0 | nsz -= 11; |
1137 | 0 | else |
1138 | 0 | return NULL; |
1139 | 0 | } else if (nsz > 2) |
1140 | 0 | return NULL; |
1141 | | |
1142 | 0 | if (nsz < 3) { |
1143 | 0 | pad = 3 - nsz; |
1144 | 0 | nsz = 3; |
1145 | 0 | } |
1146 | |
|
1147 | 0 | new_signame = cli_calloc((nsz + 1), sizeof(char)); |
1148 | 0 | if (!new_signame) |
1149 | 0 | return NULL; |
1150 | | |
1151 | 0 | memcpy(new_signame, signame, nsz - pad); |
1152 | 0 | new_signame[nsz] = '\0'; |
1153 | |
|
1154 | 0 | while (pad > 0) |
1155 | 0 | new_signame[nsz - pad--] = '\x20'; |
1156 | |
|
1157 | 0 | return new_signame; |
1158 | 0 | } |
1159 | | |
1160 | | static int cli_chkign(const struct cli_matcher *ignored, const char *signame, const char *entry) |
1161 | 0 | { |
1162 | |
|
1163 | 0 | const char *md5_expected = NULL; |
1164 | 0 | char *norm_signame; |
1165 | 0 | unsigned char digest[16]; |
1166 | 0 | int ret = 0; |
1167 | |
|
1168 | 0 | if (!ignored || !signame || !entry) |
1169 | 0 | return 0; |
1170 | | |
1171 | 0 | norm_signame = cli_signorm(signame); |
1172 | 0 | if (norm_signame != NULL) |
1173 | 0 | signame = norm_signame; |
1174 | |
|
1175 | 0 | if (cli_bm_scanbuff((const unsigned char *)signame, strlen(signame), &md5_expected, NULL, ignored, 0, NULL, NULL, NULL) == CL_VIRUS) |
1176 | 0 | do { |
1177 | 0 | if (md5_expected) { |
1178 | 0 | cl_hash_data("md5", entry, strlen(entry), digest, NULL); |
1179 | 0 | if (memcmp(digest, (const unsigned char *)md5_expected, 16)) |
1180 | 0 | break; |
1181 | 0 | } |
1182 | | |
1183 | 0 | cli_dbgmsg("Ignoring signature %s\n", signame); |
1184 | 0 | ret = 1; |
1185 | 0 | } while (0); |
1186 | | |
1187 | 0 | if (norm_signame) |
1188 | 0 | free(norm_signame); |
1189 | 0 | return ret; |
1190 | 0 | } |
1191 | | |
1192 | | static int cli_chkpua(const char *signame, const char *pua_cats, unsigned int options) |
1193 | 0 | { |
1194 | 0 | char cat[32], *cat_pt, *pt1, *pt2, *endsig; |
1195 | 0 | const char *sig; |
1196 | 0 | size_t catlen; |
1197 | 0 | int ret; |
1198 | |
|
1199 | 0 | cli_dbgmsg("cli_chkpua: Checking signature [%s]\n", signame); |
1200 | |
|
1201 | 0 | if (strncmp(signame, "PUA.", 4)) { |
1202 | 0 | cli_dbgmsg("Skipping signature %s - no PUA prefix\n", signame); |
1203 | 0 | return 1; |
1204 | 0 | } |
1205 | 0 | sig = signame + 3; |
1206 | 0 | if (!(pt1 = strchr(sig + 1, '.'))) { |
1207 | 0 | cli_dbgmsg("Skipping signature %s - bad syntax\n", signame); |
1208 | 0 | return 1; |
1209 | 0 | } |
1210 | 0 | if ((pt2 = strrchr(sig + 1, '.')) != pt1) { |
1211 | 0 | cli_dbgmsg("Signature has at least three dots [%s]\n", signame); |
1212 | 0 | } |
1213 | 0 | if ((unsigned int)(pt1 - sig + 2) > sizeof(cat)) { |
1214 | 0 | cli_dbgmsg("Skipping signature %s - too long category name, length approaching %d characters\n", signame, (unsigned int)(pt1 - sig + 2)); |
1215 | 0 | return 1; |
1216 | 0 | } |
1217 | 0 | if ((unsigned int)(pt2 - sig + 2) > sizeof(cat)) { |
1218 | 0 | cli_dbgmsg("Skipping signature %s - too long category name, length approaching %d characters\n", signame, (unsigned int)(pt2 - sig + 2)); |
1219 | 0 | return 1; |
1220 | 0 | } |
1221 | | |
1222 | 0 | endsig = strrchr(sig, '.'); |
1223 | |
|
1224 | 0 | catlen = MIN(sizeof(cat), strlen(sig) - strlen(endsig)); |
1225 | |
|
1226 | 0 | memcpy(cat, sig, catlen + 1); |
1227 | | |
1228 | | // Add null terminator. |
1229 | 0 | cat[catlen + 1] = '\0'; |
1230 | |
|
1231 | 0 | cat_pt = strstr(cat, pua_cats); |
1232 | 0 | cli_dbgmsg("cli_chkpua: cat=[%s]\n", cat); |
1233 | 0 | cli_dbgmsg("cli_chkpua: sig=[%s]\n", sig); |
1234 | 0 | if (options & CL_DB_PUA_INCLUDE) |
1235 | 0 | ret = cat_pt ? 0 : 1; |
1236 | 0 | else |
1237 | 0 | ret = cat_pt ? 1 : 0; |
1238 | |
|
1239 | 0 | if (ret) |
1240 | 0 | cli_dbgmsg("Skipping PUA signature %s - excluded category %s\n", signame, cat); |
1241 | 0 | return ret; |
1242 | 0 | } |
1243 | | |
1244 | | static cl_error_t cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
1245 | 0 | { |
1246 | 0 | char buffer[FILEBUFF], *buffer_cpy = NULL, *pt, *start; |
1247 | 0 | unsigned int line = 0, sigs = 0; |
1248 | 0 | int ret = 0; |
1249 | 0 | struct cli_matcher *root; |
1250 | |
|
1251 | 0 | UNUSEDPARAM(dbname); |
1252 | |
|
1253 | 0 | if (CL_SUCCESS != (ret = cli_initroots(engine, options))) |
1254 | 0 | return ret; |
1255 | | |
1256 | 0 | root = engine->root[0]; |
1257 | |
|
1258 | 0 | if (engine->ignored) |
1259 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
1260 | 0 | cli_errmsg("cli_loaddb: Can't allocate memory for buffer_cpy\n"); |
1261 | 0 | return CL_EMEM; |
1262 | 0 | } |
1263 | | |
1264 | 0 | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
1265 | 0 | line++; |
1266 | 0 | if (buffer[0] == '#') |
1267 | 0 | continue; |
1268 | 0 | cli_chomp(buffer); |
1269 | 0 | if (engine->ignored) |
1270 | 0 | strcpy(buffer_cpy, buffer); |
1271 | |
|
1272 | 0 | pt = strchr(buffer, '='); |
1273 | 0 | if (!pt) { |
1274 | 0 | cli_errmsg("Malformed pattern line %d\n", line); |
1275 | 0 | ret = CL_EMALFDB; |
1276 | 0 | break; |
1277 | 0 | } |
1278 | | |
1279 | 0 | start = buffer; |
1280 | 0 | *pt++ = 0; |
1281 | |
|
1282 | 0 | if (engine->ignored && cli_chkign(engine->ignored, start, buffer_cpy)) |
1283 | 0 | continue; |
1284 | | |
1285 | 0 | if (engine->cb_sigload && engine->cb_sigload("db", start, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
1286 | 0 | cli_dbgmsg("cli_loaddb: skipping %s due to callback\n", start); |
1287 | 0 | continue; |
1288 | 0 | } |
1289 | | |
1290 | 0 | if (*pt == '=') continue; |
1291 | | |
1292 | 0 | if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, start, pt, 0, 0, 0, "*", NULL, options))) { |
1293 | 0 | cli_dbgmsg("cli_loaddb: cli_add_content_match_pattern failed on line %d\n", line); |
1294 | 0 | ret = CL_EMALFDB; |
1295 | 0 | break; |
1296 | 0 | } |
1297 | 0 | sigs++; |
1298 | 0 | } |
1299 | |
|
1300 | 0 | if (engine->ignored) |
1301 | 0 | free(buffer_cpy); |
1302 | |
|
1303 | 0 | if (!line) { |
1304 | 0 | cli_errmsg("Empty database file\n"); |
1305 | 0 | return CL_EMALFDB; |
1306 | 0 | } |
1307 | | |
1308 | 0 | if (ret) { |
1309 | 0 | cli_errmsg("Problem parsing database at line %d\n", line); |
1310 | 0 | return ret; |
1311 | 0 | } |
1312 | | |
1313 | 0 | if (signo) |
1314 | 0 | *signo += sigs; |
1315 | |
|
1316 | 0 | return CL_SUCCESS; |
1317 | 0 | } |
1318 | | |
1319 | 25.2k | #define ICO_TOKENS 4 |
1320 | | static cl_error_t cli_loadidb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio) |
1321 | 882 | { |
1322 | 882 | const char *tokens[ICO_TOKENS + 1] = {0}; |
1323 | 882 | char buffer[FILEBUFF] = {0}, *buffer_cpy = NULL; |
1324 | 882 | uint8_t *hash = NULL; |
1325 | 882 | int ret = CL_SUCCESS; |
1326 | 882 | unsigned int line = 0, sigs = 0, tokens_count, i, size, enginesize; |
1327 | 882 | struct icomtr *metric = NULL; |
1328 | 882 | struct icon_matcher *matcher = NULL; |
1329 | | |
1330 | 882 | if (!(matcher = (struct icon_matcher *)MPOOL_CALLOC(engine->mempool, sizeof(*matcher), 1))) |
1331 | 0 | return CL_EMEM; |
1332 | | |
1333 | 882 | if (engine->ignored) |
1334 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
1335 | 0 | cli_errmsg("cli_loadidb: Can't allocate memory for buffer_cpy\n"); |
1336 | 0 | MPOOL_FREE(engine->mempool, matcher); |
1337 | 0 | return CL_EMEM; |
1338 | 0 | } |
1339 | | |
1340 | 32.5k | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
1341 | 32.1k | line++; |
1342 | 32.1k | if (buffer[0] == '#') |
1343 | 19.5k | continue; |
1344 | | |
1345 | 12.6k | cli_chomp(buffer); |
1346 | 12.6k | if (engine->ignored) |
1347 | 0 | strcpy(buffer_cpy, buffer); |
1348 | | |
1349 | 12.6k | tokens_count = cli_strtokenize(buffer, ':', ICO_TOKENS + 1, tokens); |
1350 | 12.6k | if (tokens_count != ICO_TOKENS) { |
1351 | 95 | cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong token count)\n", line); |
1352 | 95 | ret = CL_EMALFDB; |
1353 | 95 | break; |
1354 | 95 | } |
1355 | | |
1356 | 12.5k | if (strlen(tokens[3]) != 124) { |
1357 | 41 | cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong length)\n", line); |
1358 | 41 | ret = CL_EMALFDB; |
1359 | 41 | break; |
1360 | 41 | } |
1361 | | |
1362 | 12.4k | if (engine->ignored && cli_chkign(engine->ignored, tokens[0], buffer_cpy)) |
1363 | 0 | continue; |
1364 | | |
1365 | 12.4k | if (engine->cb_sigload && engine->cb_sigload("idb", tokens[0], ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
1366 | 0 | cli_dbgmsg("cli_loadidb: skipping %s due to callback\n", tokens[0]); |
1367 | 0 | continue; |
1368 | 0 | } |
1369 | | |
1370 | 12.4k | hash = (uint8_t *)tokens[3]; |
1371 | 12.4k | if (cli_hexnibbles((char *)hash, 124)) { |
1372 | 6 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad chars)\n", line); |
1373 | 6 | ret = CL_EMALFDB; |
1374 | 6 | break; |
1375 | 6 | } |
1376 | 12.4k | size = (hash[0] << 4) + hash[1]; |
1377 | 12.4k | if (size != 32 && size != 24 && size != 16) { |
1378 | 8 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad size)\n", line); |
1379 | 8 | ret = CL_EMALFDB; |
1380 | 8 | break; |
1381 | 8 | } |
1382 | 12.4k | enginesize = (size >> 3) - 2; |
1383 | 12.4k | hash += 2; |
1384 | | |
1385 | 12.4k | metric = (struct icomtr *)MPOOL_REALLOC(engine->mempool, matcher->icons[enginesize], sizeof(struct icomtr) * (matcher->icon_counts[enginesize] + 1)); |
1386 | 12.4k | if (!metric) { |
1387 | 0 | ret = CL_EMEM; |
1388 | 0 | break; |
1389 | 0 | } |
1390 | | |
1391 | 12.4k | matcher->icons[enginesize] = metric; |
1392 | 12.4k | metric += matcher->icon_counts[enginesize]; |
1393 | 12.4k | matcher->icon_counts[enginesize]++; |
1394 | | |
1395 | 49.7k | for (i = 0; i < 3; i++) { |
1396 | 37.3k | if ((metric->color_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072) |
1397 | 5 | break; |
1398 | 37.3k | if ((metric->color_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8) |
1399 | 20 | break; |
1400 | 37.3k | if ((metric->color_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8) |
1401 | 25 | break; |
1402 | 37.3k | hash += 7; |
1403 | 37.3k | } |
1404 | 12.4k | if (i != 3) { |
1405 | 50 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad color data)\n", line); |
1406 | 50 | ret = CL_EMALFDB; |
1407 | 50 | break; |
1408 | 50 | } |
1409 | | |
1410 | 49.6k | for (i = 0; i < 3; i++) { |
1411 | 37.2k | if ((metric->gray_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072) |
1412 | 3 | break; |
1413 | 37.2k | if ((metric->gray_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8) |
1414 | 22 | break; |
1415 | 37.2k | if ((metric->gray_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8) |
1416 | 25 | break; |
1417 | 37.1k | hash += 7; |
1418 | 37.1k | } |
1419 | 12.4k | if (i != 3) { |
1420 | 50 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad gray data)\n", line); |
1421 | 50 | ret = CL_EMALFDB; |
1422 | 50 | break; |
1423 | 50 | } |
1424 | | |
1425 | 49.4k | for (i = 0; i < 3; i++) { |
1426 | 37.0k | metric->bright_avg[i] = (hash[0] << 4) | hash[1]; |
1427 | 37.0k | if ((metric->bright_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8) |
1428 | 26 | break; |
1429 | 37.0k | if ((metric->bright_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8) |
1430 | 21 | break; |
1431 | 37.0k | hash += 6; |
1432 | 37.0k | } |
1433 | 12.3k | if (i != 3) { |
1434 | 47 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad bright data)\n", line); |
1435 | 47 | ret = CL_EMALFDB; |
1436 | 47 | break; |
1437 | 47 | } |
1438 | | |
1439 | 49.2k | for (i = 0; i < 3; i++) { |
1440 | 36.9k | metric->dark_avg[i] = (hash[0] << 4) | hash[1]; |
1441 | 36.9k | if ((metric->dark_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8) |
1442 | 25 | break; |
1443 | 36.9k | if ((metric->dark_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8) |
1444 | 30 | break; |
1445 | 36.8k | hash += 6; |
1446 | 36.8k | } |
1447 | 12.3k | if (i != 3) { |
1448 | 55 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad dark data)\n", line); |
1449 | 55 | ret = CL_EMALFDB; |
1450 | 55 | break; |
1451 | 55 | } |
1452 | | |
1453 | 49.0k | for (i = 0; i < 3; i++) { |
1454 | 36.7k | metric->edge_avg[i] = (hash[0] << 4) | hash[1]; |
1455 | 36.7k | if ((metric->edge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8) |
1456 | 21 | break; |
1457 | 36.7k | if ((metric->edge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8) |
1458 | 26 | break; |
1459 | 36.7k | hash += 6; |
1460 | 36.7k | } |
1461 | 12.2k | if (i != 3) { |
1462 | 47 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad edge data)\n", line); |
1463 | 47 | ret = CL_EMALFDB; |
1464 | 47 | break; |
1465 | 47 | } |
1466 | | |
1467 | 48.8k | for (i = 0; i < 3; i++) { |
1468 | 36.6k | metric->noedge_avg[i] = (hash[0] << 4) | hash[1]; |
1469 | 36.6k | if ((metric->noedge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8) |
1470 | 26 | break; |
1471 | 36.6k | if ((metric->noedge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8) |
1472 | 21 | break; |
1473 | 36.5k | hash += 6; |
1474 | 36.5k | } |
1475 | 12.2k | if (i != 3) { |
1476 | 47 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad noedge data)\n", line); |
1477 | 47 | ret = CL_EMALFDB; |
1478 | 47 | break; |
1479 | 47 | } |
1480 | | |
1481 | 12.1k | metric->rsum = (hash[0] << 4) | hash[1]; |
1482 | 12.1k | metric->gsum = (hash[2] << 4) | hash[3]; |
1483 | 12.1k | metric->bsum = (hash[4] << 4) | hash[5]; |
1484 | 12.1k | metric->ccount = (hash[6] << 4) | hash[7]; |
1485 | 12.1k | if (metric->rsum + metric->gsum + metric->bsum > 103 || metric->ccount > 100) { |
1486 | 6 | cli_errmsg("cli_loadidb: Malformed hash at line %u (bad spread data)\n", line); |
1487 | 6 | ret = CL_EMALFDB; |
1488 | 6 | break; |
1489 | 6 | } |
1490 | | |
1491 | 12.1k | if (!(metric->name = CLI_MPOOL_STRDUP(engine->mempool, tokens[0]))) { |
1492 | 0 | ret = CL_EMEM; |
1493 | 0 | break; |
1494 | 0 | } |
1495 | | |
1496 | 72.5k | for (i = 0; i < matcher->group_counts[0]; i++) { |
1497 | 70.5k | if (!strcmp(tokens[1], matcher->group_names[0][i])) |
1498 | 10.1k | break; |
1499 | 70.5k | } |
1500 | 12.1k | if (i == matcher->group_counts[0]) { |
1501 | 1.99k | if (!(matcher->group_names[0] = MPOOL_REALLOC(engine->mempool, matcher->group_names[0], sizeof(char *) * (i + 1))) || |
1502 | 1.99k | !(matcher->group_names[0][i] = CLI_MPOOL_STRDUP(engine->mempool, tokens[1]))) { |
1503 | 0 | ret = CL_EMEM; |
1504 | 0 | break; |
1505 | 0 | } |
1506 | 1.99k | matcher->group_counts[0]++; |
1507 | 1.99k | } |
1508 | 12.1k | metric->group[0] = i; |
1509 | | |
1510 | 83.3k | for (i = 0; i < matcher->group_counts[1]; i++) { |
1511 | 81.1k | if (!strcmp(tokens[2], matcher->group_names[1][i])) |
1512 | 9.94k | break; |
1513 | 81.1k | } |
1514 | 12.1k | if (i == matcher->group_counts[1]) { |
1515 | 2.22k | if (!(matcher->group_names[1] = MPOOL_REALLOC(engine->mempool, matcher->group_names[1], sizeof(char *) * (i + 1))) || |
1516 | 2.22k | !(matcher->group_names[1][i] = CLI_MPOOL_STRDUP(engine->mempool, tokens[2]))) { |
1517 | 0 | ret = CL_EMEM; |
1518 | 0 | break; |
1519 | 0 | } |
1520 | 2.22k | matcher->group_counts[1]++; |
1521 | 2.22k | } |
1522 | 12.1k | metric->group[1] = i; |
1523 | | |
1524 | 12.1k | if (matcher->group_counts[0] > 256 || matcher->group_counts[1] > 256) { |
1525 | 0 | cli_errmsg("cli_loadidb: too many icon groups!\n"); |
1526 | 0 | ret = CL_EMALFDB; |
1527 | 0 | break; |
1528 | 0 | } |
1529 | | |
1530 | 12.1k | sigs++; |
1531 | 12.1k | } |
1532 | 882 | if (engine->ignored) |
1533 | 0 | free(buffer_cpy); |
1534 | | |
1535 | 882 | if (!line) { |
1536 | 0 | cli_errmsg("cli_loadidb: Empty database file\n"); |
1537 | 0 | ret = CL_EMALFDB; |
1538 | 0 | } |
1539 | | |
1540 | 882 | if (ret) { |
1541 | 452 | cli_errmsg("cli_loadidb: Problem parsing database at line %u\n", line); |
1542 | 452 | MPOOL_FREE(engine->mempool, matcher); |
1543 | 452 | return ret; |
1544 | 452 | } |
1545 | | |
1546 | 430 | if (signo) |
1547 | 430 | *signo += sigs; |
1548 | | |
1549 | 430 | engine->iconcheck = matcher; |
1550 | 430 | return CL_SUCCESS; |
1551 | 882 | } |
1552 | | |
1553 | | static int cli_loadwdb(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio) |
1554 | 4.17k | { |
1555 | 4.17k | int ret = 0; |
1556 | | |
1557 | 4.17k | if (!(engine->dconf->phishing & PHISHING_CONF_ENGINE)) |
1558 | 0 | return CL_SUCCESS; |
1559 | | |
1560 | 4.17k | if (!engine->allow_list_matcher) { |
1561 | 4.17k | if (CL_SUCCESS != (ret = init_allow_list(engine))) { |
1562 | 0 | return ret; |
1563 | 0 | } |
1564 | 4.17k | } |
1565 | | |
1566 | 4.17k | if (CL_SUCCESS != (ret = load_regex_matcher(engine, engine->allow_list_matcher, fs, NULL, options, 1, dbio, engine->dconf->other & OTHER_CONF_PREFILTERING))) { |
1567 | 2.63k | return ret; |
1568 | 2.63k | } |
1569 | | |
1570 | 1.54k | return CL_SUCCESS; |
1571 | 4.17k | } |
1572 | | |
1573 | | static int cli_loadpdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio) |
1574 | 4.35k | { |
1575 | 4.35k | int ret = 0; |
1576 | | |
1577 | 4.35k | if (!(engine->dconf->phishing & PHISHING_CONF_ENGINE)) |
1578 | 0 | return CL_SUCCESS; |
1579 | | |
1580 | 4.35k | if (!engine->domain_list_matcher) { |
1581 | 4.35k | if (CL_SUCCESS != (ret = init_domain_list(engine))) { |
1582 | 0 | return ret; |
1583 | 0 | } |
1584 | 4.35k | } |
1585 | | |
1586 | 4.35k | if (CL_SUCCESS != (ret = load_regex_matcher(engine, engine->domain_list_matcher, fs, signo, options, 0, dbio, engine->dconf->other & OTHER_CONF_PREFILTERING))) { |
1587 | 2.75k | return ret; |
1588 | 2.75k | } |
1589 | | |
1590 | 1.60k | return CL_SUCCESS; |
1591 | 4.35k | } |
1592 | | |
1593 | 54.9k | #define NDB_TOKENS 6 |
1594 | | static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned short sdb, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
1595 | 6.09k | { |
1596 | 6.09k | const char *tokens[NDB_TOKENS + 1]; |
1597 | 6.09k | char buffer[FILEBUFF], *buffer_cpy = NULL; |
1598 | 6.09k | const char *sig, *virname, *offset, *pt; |
1599 | 6.09k | struct cli_matcher *root; |
1600 | 6.09k | int line = 0, sigs = 0, ret = 0, tokens_count; |
1601 | 6.09k | cli_target_t target; |
1602 | 6.09k | unsigned int phish = options & CL_DB_PHISHING; |
1603 | | |
1604 | 6.09k | UNUSEDPARAM(dbname); |
1605 | | |
1606 | 6.09k | if (CL_SUCCESS != (ret = cli_initroots(engine, options))) |
1607 | 0 | return ret; |
1608 | | |
1609 | 6.09k | if (engine->ignored) |
1610 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
1611 | 0 | cli_errmsg("cli_loadndb: Can't allocate memory for buffer_cpy\n"); |
1612 | 0 | return CL_EMEM; |
1613 | 0 | } |
1614 | | |
1615 | 58.2k | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
1616 | 56.3k | line++; |
1617 | 56.3k | if (buffer[0] == '#') |
1618 | 1.38k | continue; |
1619 | | |
1620 | 54.9k | if (!phish) |
1621 | 0 | if (!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14)) |
1622 | 0 | continue; |
1623 | | |
1624 | 54.9k | cli_chomp(buffer); |
1625 | 54.9k | if (engine->ignored) |
1626 | 0 | strcpy(buffer_cpy, buffer); |
1627 | | |
1628 | 54.9k | tokens_count = cli_strtokenize(buffer, ':', NDB_TOKENS + 1, tokens); |
1629 | 54.9k | if (tokens_count < 4 || tokens_count > 6) { |
1630 | 126 | ret = CL_EMALFDB; |
1631 | 126 | break; |
1632 | 126 | } |
1633 | | |
1634 | 54.7k | virname = tokens[0]; |
1635 | | |
1636 | 54.7k | if (engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE))) |
1637 | 0 | if (cli_chkpua(virname, engine->pua_cats, options)) |
1638 | 0 | continue; |
1639 | | |
1640 | 54.7k | if (engine->ignored && cli_chkign(engine->ignored, virname, buffer_cpy)) |
1641 | 0 | continue; |
1642 | | |
1643 | 54.7k | if (!sdb && engine->cb_sigload && engine->cb_sigload("ndb", virname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
1644 | 0 | cli_dbgmsg("cli_loadndb: skipping %s due to callback\n", virname); |
1645 | 0 | continue; |
1646 | 0 | } |
1647 | | |
1648 | 54.7k | if (tokens_count > 4) { /* min version */ |
1649 | 1.77k | pt = tokens[4]; |
1650 | | |
1651 | 1.77k | if (!cli_isnumber(pt)) { |
1652 | 7 | ret = CL_EMALFDB; |
1653 | 7 | break; |
1654 | 7 | } |
1655 | | |
1656 | 1.76k | if ((unsigned int)atoi(pt) > cl_retflevel()) { |
1657 | 277 | cli_dbgmsg("Signature for %s not loaded (required f-level: %d)\n", virname, atoi(pt)); |
1658 | 277 | continue; |
1659 | 277 | } |
1660 | | |
1661 | 1.48k | if (tokens_count == 6) { /* max version */ |
1662 | 857 | pt = tokens[5]; |
1663 | 857 | if (!cli_isnumber(pt)) { |
1664 | 3 | ret = CL_EMALFDB; |
1665 | 3 | break; |
1666 | 3 | } |
1667 | | |
1668 | 854 | if ((unsigned int)atoi(pt) < cl_retflevel()) { |
1669 | 619 | continue; |
1670 | 619 | } |
1671 | 854 | } |
1672 | 1.48k | } |
1673 | | |
1674 | 53.8k | if (!(pt = tokens[1]) || (strcmp(pt, "*") && !cli_isnumber(pt))) { |
1675 | 11 | ret = CL_EMALFDB; |
1676 | 11 | break; |
1677 | 11 | } |
1678 | 53.8k | target = (cli_target_t)atoi(pt); |
1679 | | |
1680 | 53.8k | if (target >= CLI_MTARGETS || target < 0) { |
1681 | 1.00k | cli_dbgmsg("Not supported target type (%d) in signature for %s\n", (int)target, virname); |
1682 | 1.00k | continue; |
1683 | 1.00k | } |
1684 | | |
1685 | 52.8k | root = engine->root[(size_t)target]; |
1686 | | |
1687 | 52.8k | offset = tokens[2]; |
1688 | 52.8k | sig = tokens[3]; |
1689 | | |
1690 | 52.8k | if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, NULL, options))) { |
1691 | 3.97k | ret = CL_EMALFDB; |
1692 | 3.97k | break; |
1693 | 3.97k | } |
1694 | 48.9k | sigs++; |
1695 | | |
1696 | 48.9k | if (engine->cb_sigload_progress && ((*signo + sigs) % 10000 == 0)) { |
1697 | | /* Let the progress callback function know how we're doing */ |
1698 | 0 | (void)engine->cb_sigload_progress(engine->num_total_signatures, *signo + sigs, engine->cb_sigload_progress_ctx); |
1699 | 0 | } |
1700 | 48.9k | } |
1701 | 6.09k | if (engine->ignored) |
1702 | 0 | free(buffer_cpy); |
1703 | | |
1704 | 6.09k | if (!line) { |
1705 | 0 | cli_errmsg("Empty database file\n"); |
1706 | 0 | return CL_EMALFDB; |
1707 | 0 | } |
1708 | | |
1709 | 6.09k | if (ret) { |
1710 | 4.11k | cli_errmsg("Problem parsing database at line %d\n", line); |
1711 | 4.11k | return ret; |
1712 | 4.11k | } |
1713 | | |
1714 | 1.98k | if (signo) |
1715 | 1.98k | *signo += sigs; |
1716 | | |
1717 | 1.98k | if (sdb && sigs && !engine->sdb) { |
1718 | 0 | engine->sdb = 1; |
1719 | 0 | cli_dbgmsg("*** Self protection mechanism activated.\n"); |
1720 | 0 | } |
1721 | | |
1722 | 1.98k | return CL_SUCCESS; |
1723 | 6.09k | } |
1724 | | |
1725 | | struct lsig_attrib { |
1726 | | const char *name; |
1727 | | unsigned int type; |
1728 | | void **pt; |
1729 | | }; |
1730 | | |
1731 | | /* TODO: rework this */ |
1732 | | static int lsigattribs(char *attribs, struct cli_lsig_tdb *tdb) |
1733 | 49.4k | { |
1734 | | // clang-format off |
1735 | 49.4k | #define ATTRIB_TOKENS 10 |
1736 | 49.4k | #define EXPR_TOKEN_MAX 16 |
1737 | 49.4k | struct lsig_attrib attrtab[] = { |
1738 | 49.4k | { "Target", CLI_TDB_UINT, (void **) &tdb->target }, |
1739 | 49.4k | { "Engine", CLI_TDB_RANGE, (void **) &tdb->engine }, |
1740 | | |
1741 | 49.4k | { "FileSize", CLI_TDB_RANGE, (void **) &tdb->filesize }, |
1742 | 49.4k | { "EntryPoint", CLI_TDB_RANGE, (void **) &tdb->ep }, |
1743 | 49.4k | { "NumberOfSections", CLI_TDB_RANGE, (void **) &tdb->nos }, |
1744 | | |
1745 | 49.4k | { "IconGroup1", CLI_TDB_STR, (void **) &tdb->icongrp1 }, |
1746 | 49.4k | { "IconGroup2", CLI_TDB_STR, (void **) &tdb->icongrp2 }, |
1747 | | |
1748 | 49.4k | { "Container", CLI_TDB_FTYPE, (void **) &tdb->container }, |
1749 | 49.4k | { "HandlerType", CLI_TDB_FTYPE, (void **) &tdb->handlertype }, |
1750 | 49.4k | { "Intermediates", CLI_TDB_FTYPE_EXPR, (void **) &tdb->intermediates }, |
1751 | | /* |
1752 | | { "SectOff", CLI_TDB_RANGE2, (void **) &tdb->sectoff }, |
1753 | | { "SectRVA", CLI_TDB_RANGE2, (void **) &tdb->sectrva }, |
1754 | | { "SectVSZ", CLI_TDB_RANGE2, (void **) &tdb->sectvsz }, |
1755 | | { "SectRAW", CLI_TDB_RANGE2, (void **) &tdb->sectraw }, |
1756 | | { "SectRSZ", CLI_TDB_RANGE2, (void **) &tdb->sectrsz }, |
1757 | | { "SectURVA", CLI_TDB_RANGE2, (void **) &tdb->secturva }, |
1758 | | { "SectUVSZ", CLI_TDB_RANGE2, (void **) &tdb->sectuvsz }, |
1759 | | { "SectURAW", CLI_TDB_RANGE2, (void **) &tdb->secturaw }, |
1760 | | { "SectURSZ", CLI_TDB_RANGE2, (void **) &tdb->sectursz }, |
1761 | | */ |
1762 | 49.4k | { NULL, 0, NULL, } |
1763 | 49.4k | }; |
1764 | | // clang-format on |
1765 | | |
1766 | 49.4k | struct lsig_attrib *apt; |
1767 | 49.4k | char *tokens[ATTRIB_TOKENS], *pt, *pt2; |
1768 | 49.4k | unsigned int v1, v2, v3, i, j, tokens_count, have_newext = 0; |
1769 | 49.4k | uint32_t cnt, off[ATTRIB_TOKENS]; |
1770 | | |
1771 | 49.4k | tokens_count = cli_strtokenize(attribs, ',', ATTRIB_TOKENS, (const char **)tokens); |
1772 | | |
1773 | 117k | for (i = 0; i < tokens_count; i++) { |
1774 | 74.1k | if (!(pt = strchr(tokens[i], ':'))) { |
1775 | 66 | cli_errmsg("lsigattribs: Incorrect format of attribute '%s'\n", tokens[i]); |
1776 | 66 | return -1; |
1777 | 66 | } |
1778 | 74.1k | *pt++ = 0; |
1779 | | |
1780 | 74.1k | apt = NULL; |
1781 | 251k | for (j = 0; attrtab[j].name; j++) { |
1782 | 246k | if (!strcmp(attrtab[j].name, tokens[i])) { |
1783 | 68.3k | apt = &attrtab[j]; |
1784 | 68.3k | break; |
1785 | 68.3k | } |
1786 | 246k | } |
1787 | | |
1788 | 74.1k | if (!apt) { |
1789 | 5.74k | cli_dbgmsg("lsigattribs: Unknown attribute name '%s'\n", tokens[i]); |
1790 | 5.74k | return 1; |
1791 | 5.74k | } |
1792 | | |
1793 | 68.3k | if (!strcmp(apt->name, "Engine")) { |
1794 | 5.78k | if (i) { |
1795 | 2 | cli_errmsg("lsigattribs: For backward compatibility the Engine attribute must be on the first position\n"); |
1796 | 2 | return -1; |
1797 | 2 | } |
1798 | 62.5k | } else if (strcmp(apt->name, "Target")) { |
1799 | 20.8k | have_newext = 1; |
1800 | 20.8k | } |
1801 | | |
1802 | 68.3k | switch (apt->type) { |
1803 | 41.7k | case CLI_TDB_UINT: |
1804 | 41.7k | if (!cli_isnumber(pt)) { |
1805 | 6 | cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]); |
1806 | 6 | return -1; |
1807 | 6 | } |
1808 | | |
1809 | 41.7k | off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++; |
1810 | 41.7k | tdb->val = (uint32_t *)MPOOL_REALLOC2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t)); |
1811 | 41.7k | if (!tdb->val) { |
1812 | 0 | tdb->cnt[CLI_TDB_UINT] = 0; |
1813 | 0 | return -1; |
1814 | 0 | } |
1815 | | |
1816 | 41.7k | tdb->val[cnt] = atoi(pt); |
1817 | 41.7k | break; |
1818 | | |
1819 | 4.11k | case CLI_TDB_FTYPE: |
1820 | 4.11k | if ((v1 = cli_ftcode(pt)) == CL_TYPE_ERROR) { |
1821 | 391 | cli_dbgmsg("lsigattribs: Unknown file type '%s' in %s\n", pt, tokens[i]); |
1822 | 391 | return 1; /* skip */ |
1823 | 391 | } |
1824 | | |
1825 | 3.72k | off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++; |
1826 | 3.72k | tdb->val = (uint32_t *)MPOOL_REALLOC2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t)); |
1827 | 3.72k | if (!tdb->val) { |
1828 | 0 | tdb->cnt[CLI_TDB_UINT] = 0; |
1829 | 0 | return -1; |
1830 | 0 | } |
1831 | | |
1832 | 3.72k | tdb->val[cnt] = v1; |
1833 | 3.72k | break; |
1834 | | |
1835 | 2.83k | case CLI_TDB_FTYPE_EXPR: { |
1836 | 2.83k | char *ftypes[EXPR_TOKEN_MAX]; |
1837 | 2.83k | unsigned int ftypes_count; |
1838 | | |
1839 | 2.83k | off[i] = cnt = tdb->cnt[CLI_TDB_UINT]; |
1840 | 2.83k | ftypes_count = cli_strtokenize(pt, '>', EXPR_TOKEN_MAX, (const char **)ftypes); |
1841 | 2.83k | if (!ftypes_count) { |
1842 | 0 | cli_dbgmsg("lsigattribs: No intermediate container tokens found."); |
1843 | 0 | return 1; |
1844 | 0 | } |
1845 | 2.83k | tdb->cnt[CLI_TDB_UINT] += (ftypes_count + 1); |
1846 | 2.83k | tdb->val = (uint32_t *)MPOOL_REALLOC2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t)); |
1847 | 2.83k | if (!tdb->val) { |
1848 | 0 | tdb->cnt[CLI_TDB_UINT] = 0; |
1849 | 0 | return -1; |
1850 | 0 | } |
1851 | | |
1852 | 2.83k | tdb->val[cnt++] = ftypes_count; |
1853 | 8.16k | for (j = 0; j < ftypes_count; j++) { |
1854 | 5.68k | if ((v1 = cli_ftcode(ftypes[j])) == CL_TYPE_ERROR) { |
1855 | 355 | cli_dbgmsg("lsigattribs: Unknown file type '%s' in %s\n", ftypes[j], tokens[i]); |
1856 | 355 | return 1; /* skip */ |
1857 | 355 | } |
1858 | 5.32k | tdb->val[cnt++] = v1; |
1859 | 5.32k | } |
1860 | 2.83k | } break; |
1861 | | |
1862 | 13.0k | case CLI_TDB_RANGE: |
1863 | 13.0k | if (!(pt2 = strchr(pt, '-'))) { |
1864 | 5 | cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]); |
1865 | 5 | return -1; |
1866 | 5 | } |
1867 | | |
1868 | 13.0k | *pt2++ = 0; |
1869 | 13.0k | off[i] = cnt = tdb->cnt[CLI_TDB_RANGE]; |
1870 | 13.0k | tdb->cnt[CLI_TDB_RANGE] += 2; |
1871 | 13.0k | tdb->range = (uint32_t *)MPOOL_REALLOC2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t)); |
1872 | 13.0k | if (!tdb->range) { |
1873 | 0 | tdb->cnt[CLI_TDB_RANGE] = 0; |
1874 | 0 | return -1; |
1875 | 0 | } |
1876 | | |
1877 | 13.0k | if (!cli_isnumber(pt) || !cli_isnumber(pt2)) { |
1878 | 7 | cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]); |
1879 | 7 | return -1; |
1880 | 7 | } |
1881 | | |
1882 | 13.0k | tdb->range[cnt] = atoi(pt); |
1883 | 13.0k | tdb->range[cnt + 1] = atoi(pt2); |
1884 | 13.0k | break; |
1885 | | |
1886 | 0 | case CLI_TDB_RANGE2: |
1887 | 0 | if (!strchr(pt, '-') || !strchr(pt, '.')) { |
1888 | 0 | cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]); |
1889 | 0 | return -1; |
1890 | 0 | } |
1891 | | |
1892 | 0 | off[i] = cnt = tdb->cnt[CLI_TDB_RANGE]; |
1893 | 0 | tdb->cnt[CLI_TDB_RANGE] += 3; |
1894 | 0 | tdb->range = (uint32_t *)MPOOL_REALLOC2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t)); |
1895 | 0 | if (!tdb->range) { |
1896 | 0 | tdb->cnt[CLI_TDB_RANGE] = 0; |
1897 | 0 | return -1; |
1898 | 0 | } |
1899 | | |
1900 | 0 | if (sscanf(pt, "%u.%u-%u", &v1, &v2, &v3) != 3) { |
1901 | 0 | cli_errmsg("lsigattribs: Can't parse parameters in '%s'\n", tokens[i]); |
1902 | 0 | return -1; |
1903 | 0 | } |
1904 | | |
1905 | 0 | tdb->range[cnt] = (uint32_t)v1; |
1906 | 0 | tdb->range[cnt + 1] = (uint32_t)v2; |
1907 | 0 | tdb->range[cnt + 2] = (uint32_t)v3; |
1908 | 0 | break; |
1909 | | |
1910 | 6.59k | case CLI_TDB_STR: |
1911 | 6.59k | off[i] = cnt = tdb->cnt[CLI_TDB_STR]; |
1912 | 6.59k | tdb->cnt[CLI_TDB_STR] += strlen(pt) + 1; |
1913 | 6.59k | tdb->str = (char *)MPOOL_REALLOC2(tdb->mempool, tdb->str, tdb->cnt[CLI_TDB_STR] * sizeof(char)); |
1914 | 6.59k | if (!tdb->str) { |
1915 | 0 | cli_errmsg("lsigattribs: Can't allocate memory for tdb->str\n"); |
1916 | 0 | return -1; |
1917 | 0 | } |
1918 | 6.59k | memcpy(&tdb->str[cnt], pt, strlen(pt)); |
1919 | 6.59k | tdb->str[tdb->cnt[CLI_TDB_STR] - 1] = 0; |
1920 | 6.59k | break; |
1921 | | |
1922 | 0 | default: |
1923 | | /* All known TDB types handled above, skip unknown */ |
1924 | 0 | cli_dbgmsg("lsigattribs: Unknown attribute type '%u'\n", apt->type); |
1925 | 0 | return 1; /* +1 = skip */ |
1926 | 68.3k | } |
1927 | 68.3k | } |
1928 | | |
1929 | 42.9k | if (!i) { |
1930 | 0 | cli_errmsg("lsigattribs: Empty TDB\n"); |
1931 | 0 | return -1; |
1932 | 0 | } |
1933 | | |
1934 | 107k | for (i = 0; i < tokens_count; i++) { |
1935 | 168k | for (j = 0; attrtab[j].name; j++) { |
1936 | 168k | if (!strcmp(attrtab[j].name, tokens[i])) { |
1937 | 64.6k | apt = &attrtab[j]; |
1938 | 64.6k | break; |
1939 | 64.6k | } |
1940 | 168k | } |
1941 | | |
1942 | 64.6k | if (!apt) |
1943 | 0 | continue; |
1944 | | |
1945 | 64.6k | switch (apt->type) { |
1946 | 41.6k | case CLI_TDB_UINT: |
1947 | 44.9k | case CLI_TDB_FTYPE: |
1948 | 47.3k | case CLI_TDB_FTYPE_EXPR: |
1949 | 47.3k | *apt->pt = (uint32_t *)&tdb->val[off[i]]; |
1950 | 47.3k | break; |
1951 | | |
1952 | 11.3k | case CLI_TDB_RANGE: |
1953 | 11.3k | case CLI_TDB_RANGE2: |
1954 | 11.3k | *apt->pt = (uint32_t *)&tdb->range[off[i]]; |
1955 | 11.3k | break; |
1956 | | |
1957 | 5.98k | case CLI_TDB_STR: |
1958 | 5.98k | *apt->pt = (char *)&tdb->str[off[i]]; |
1959 | 5.98k | break; |
1960 | 64.6k | } |
1961 | 64.6k | } |
1962 | | |
1963 | 42.9k | if (have_newext && (!tdb->engine || tdb->engine[0] < 51)) { |
1964 | 27 | cli_errmsg("lsigattribs: For backward compatibility all signatures using new attributes must have the Engine attribute present and set to min_level of at least 51 (0.96)\n"); |
1965 | 27 | return -1; |
1966 | 27 | } |
1967 | | |
1968 | 42.8k | return 0; |
1969 | 42.9k | } |
1970 | | |
1971 | | #define FREE_TDB(x) \ |
1972 | 43.0k | do { \ |
1973 | 43.0k | if (x.cnt[CLI_TDB_UINT]) \ |
1974 | 43.0k | MPOOL_FREE(x.mempool, x.val); \ |
1975 | 43.0k | if (x.cnt[CLI_TDB_RANGE]) \ |
1976 | 43.0k | MPOOL_FREE(x.mempool, x.range); \ |
1977 | 43.0k | if (x.cnt[CLI_TDB_STR]) \ |
1978 | 43.0k | MPOOL_FREE(x.mempool, x.str); \ |
1979 | 43.0k | if (x.macro_ptids) \ |
1980 | 43.0k | MPOOL_FREE(x.mempool, x.macro_ptids); \ |
1981 | 43.0k | } while (0); |
1982 | | |
1983 | | #define FREE_TDB_P(x) \ |
1984 | 9.46k | do { \ |
1985 | 9.46k | if (x->cnt[CLI_TDB_UINT]) \ |
1986 | 9.46k | MPOOL_FREE(x->mempool, x->val); \ |
1987 | 9.46k | if (x->cnt[CLI_TDB_RANGE]) \ |
1988 | 9.46k | MPOOL_FREE(x->mempool, x->range); \ |
1989 | 9.46k | if (x->cnt[CLI_TDB_STR]) \ |
1990 | 9.46k | MPOOL_FREE(x->mempool, x->str); \ |
1991 | 9.46k | if (x->macro_ptids) \ |
1992 | 9.46k | MPOOL_FREE(x->mempool, x->macro_ptids); \ |
1993 | 9.46k | } while (0); |
1994 | | |
1995 | | static inline int init_tdb(struct cli_lsig_tdb *tdb, struct cl_engine *engine, char *target, const char *virname) |
1996 | 49.4k | { |
1997 | 49.4k | int ret; |
1998 | | |
1999 | | #ifdef USE_MPOOL |
2000 | | tdb->mempool = engine->mempool; |
2001 | | #else |
2002 | 49.4k | UNUSEDPARAM(engine); |
2003 | 49.4k | #endif |
2004 | | |
2005 | 49.4k | if (CL_SUCCESS != (ret = lsigattribs(target, tdb))) { |
2006 | 6.60k | FREE_TDB_P(tdb); |
2007 | 6.60k | if (ret == 1) { |
2008 | 6.49k | cli_dbgmsg("init_tdb: Not supported attribute(s) in signature for %s, skipping\n", virname); |
2009 | 6.49k | return CL_BREAK; |
2010 | 6.49k | } |
2011 | 113 | return CL_EMALFDB; |
2012 | 6.60k | } |
2013 | | |
2014 | 42.8k | if (tdb->engine) { |
2015 | 4.58k | if (tdb->engine[0] > cl_retflevel()) { |
2016 | 838 | cli_dbgmsg("init_tdb: Signature for %s not loaded (required f-level: %u)\n", virname, tdb->engine[0]); |
2017 | 838 | FREE_TDB_P(tdb); |
2018 | 838 | return CL_BREAK; |
2019 | 3.74k | } else if (tdb->engine[1] < cl_retflevel()) { |
2020 | 1.00k | FREE_TDB_P(tdb); |
2021 | 1.00k | return CL_BREAK; |
2022 | 1.00k | } |
2023 | 4.58k | } |
2024 | | |
2025 | 41.0k | if (!tdb->target) { |
2026 | 23 | FREE_TDB_P(tdb); |
2027 | 23 | cli_errmsg("init_tdb: No target specified in TDB\n"); |
2028 | 23 | return CL_EMALFDB; |
2029 | 41.0k | } else if (tdb->target[0] >= CLI_MTARGETS) { |
2030 | 993 | FREE_TDB_P(tdb); |
2031 | 993 | cli_dbgmsg("init_tdb: Not supported target type in signature for %s, skipping\n", virname); |
2032 | 993 | return CL_BREAK; |
2033 | 993 | } |
2034 | | |
2035 | 40.0k | if ((tdb->icongrp1 || tdb->icongrp2) && tdb->target[0] != TARGET_PE) { |
2036 | 6 | FREE_TDB_P(tdb); |
2037 | 6 | cli_errmsg("init_tdb: IconGroup is only supported in PE (target 1) signatures\n"); |
2038 | 6 | return CL_EMALFDB; |
2039 | 6 | } |
2040 | | |
2041 | 40.0k | if ((tdb->ep || tdb->nos) && tdb->target[0] != TARGET_PE && tdb->target[0] != TARGET_ELF && tdb->target[0] != TARGET_MACHO) { |
2042 | 2 | FREE_TDB_P(tdb); |
2043 | 2 | cli_errmsg("init_tdb: EntryPoint/NumberOfSections is only supported in PE/ELF/Mach-O signatures\n"); |
2044 | 2 | return CL_EMALFDB; |
2045 | 2 | } |
2046 | | |
2047 | 40.0k | return CL_SUCCESS; |
2048 | 40.0k | } |
2049 | | |
2050 | | /* 0 1 2 3 4 5 ... (max 66) |
2051 | | * VirusName;Attributes;Logic;SubSig1[;SubSig2[;SubSig3 ... ]] |
2052 | | * NOTE: Maximum of 64(see MAX_LDB_SUBSIGS) subsignatures (last would be token 66) |
2053 | | */ |
2054 | 48.1k | #define LDB_TOKENS 67 |
2055 | | static cl_error_t load_oneldb(char *buffer, int chkpua, struct cl_engine *engine, unsigned int options, const char *dbname, unsigned int line, unsigned int *sigs, unsigned bc_idx, const char *buffer_cpy, int *skip) |
2056 | 48.1k | { |
2057 | 48.1k | cl_error_t status = CL_EMALFDB; |
2058 | 48.1k | cl_error_t ret; |
2059 | 48.1k | const char *virname, *logic; |
2060 | 48.1k | struct cli_ac_lsig **newtable; |
2061 | 48.1k | struct cli_ac_lsig *lsig = NULL; |
2062 | 48.1k | char *tokens[LDB_TOKENS + 1]; |
2063 | 48.1k | int i, subsigs, tokens_count; |
2064 | 48.1k | struct cli_matcher *root; |
2065 | 48.1k | struct cli_lsig_tdb tdb; |
2066 | 48.1k | uint32_t lsigid[2]; |
2067 | 48.1k | bool tdb_initialized = false; |
2068 | | |
2069 | 48.1k | UNUSEDPARAM(dbname); |
2070 | | |
2071 | 48.1k | tokens_count = cli_ldbtokenize(buffer, ';', LDB_TOKENS + 1, (const char **)tokens, 2); |
2072 | 48.1k | if (tokens_count < 4) { |
2073 | 254 | cli_errmsg("Invalid or unsupported ldb signature format\n"); |
2074 | 254 | status = CL_EMALFDB; |
2075 | 254 | goto done; |
2076 | 254 | } |
2077 | | |
2078 | 47.9k | virname = tokens[0]; |
2079 | 47.9k | logic = tokens[2]; |
2080 | | |
2081 | 47.9k | if (chkpua && cli_chkpua(virname, engine->pua_cats, options)) { |
2082 | 0 | cli_dbgmsg("cli_loadldb: Skipping PUA signature %s\n", virname); |
2083 | 0 | status = CL_BREAK; |
2084 | 0 | goto done; |
2085 | 0 | } |
2086 | | |
2087 | 47.9k | if (engine->ignored && cli_chkign(engine->ignored, virname, buffer_cpy ? buffer_cpy : virname)) { |
2088 | 0 | if (skip) |
2089 | 0 | *skip = 1; |
2090 | 0 | cli_dbgmsg("cli_loadldb: Skipping ignored signature %s\n", virname); |
2091 | 0 | status = CL_BREAK; |
2092 | 0 | goto done; |
2093 | 0 | } |
2094 | | |
2095 | 47.9k | if (engine->cb_sigload && engine->cb_sigload("ldb", virname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
2096 | 0 | cli_dbgmsg("cli_loadldb: skipping %s due to callback\n", virname); |
2097 | 0 | status = CL_BREAK; |
2098 | 0 | goto done; |
2099 | 0 | } |
2100 | | |
2101 | 47.9k | subsigs = cli_ac_chklsig(logic, logic + strlen(logic), NULL, NULL, NULL, 1); |
2102 | 47.9k | if (subsigs == -1) { |
2103 | 399 | cli_errmsg("Invalid or unsupported ldb logic\n"); |
2104 | 399 | status = CL_EMALFDB; |
2105 | 399 | goto done; |
2106 | 399 | } |
2107 | 47.5k | subsigs++; |
2108 | | |
2109 | 47.5k | if (!line) { |
2110 | | /* This is a logical signature from the bytecode, we need all |
2111 | | * subsignatures, even if not referenced from the logical expression */ |
2112 | 0 | if (subsigs > tokens_count - 3) { |
2113 | 0 | cli_errmsg("load_oneldb: Too many subsignatures: %u (max %u)\n", |
2114 | 0 | subsigs, tokens_count - 3); |
2115 | 0 | return CL_EMALFDB; |
2116 | 0 | } |
2117 | 0 | subsigs = tokens_count - 3; |
2118 | 47.5k | } else if (subsigs != tokens_count - 3) { |
2119 | 200 | cli_errmsg("cli_loadldb: The number of subsignatures (== %u) doesn't match the IDs in the logical expression (== %u)\n", tokens_count - 3, subsigs); |
2120 | 200 | status = CL_EMALFDB; |
2121 | 200 | goto done; |
2122 | 200 | } |
2123 | | |
2124 | | #if !HAVE_PCRE |
2125 | | /* Regex Usage and Support Check */ |
2126 | | for (i = 0; i < subsigs; ++i) { |
2127 | | char *slash = strchr(tokens[i + 3], '/'); |
2128 | | if (slash && strchr(slash + 1, '/')) { |
2129 | | cli_warnmsg("cli_loadldb: logical signature for %s uses PCREs but support is disabled, skipping\n", virname); |
2130 | | status = CL_BREAK; |
2131 | | goto done; |
2132 | | } |
2133 | | } |
2134 | | #endif |
2135 | | |
2136 | | /* enforce MAX_LDB_SUBSIGS subsig cap */ |
2137 | 47.3k | if (subsigs > MAX_LDB_SUBSIGS) { |
2138 | 1 | cli_errmsg("cli_loadldb: Broken logical expression or too many subsignatures\n"); |
2139 | 1 | status = CL_EMALFDB; |
2140 | 1 | goto done; |
2141 | 1 | } |
2142 | | |
2143 | | /* Initialize Target Description Block (TDB) */ |
2144 | 47.3k | memset(&tdb, 0, sizeof(tdb)); |
2145 | 47.3k | if (CL_SUCCESS != (ret = init_tdb(&tdb, engine, tokens[1], virname))) { |
2146 | 9.46k | if (CL_BREAK == ret) { |
2147 | 9.32k | status = CL_SUCCESS; |
2148 | 9.32k | } else { |
2149 | 144 | cli_errmsg("cli_loadldb: Failed to initialize target description block\n"); |
2150 | 144 | } |
2151 | 9.46k | status = ret; |
2152 | 9.46k | goto done; |
2153 | 9.46k | } |
2154 | | |
2155 | 37.8k | tdb_initialized = true; |
2156 | | |
2157 | 37.8k | root = engine->root[tdb.target[0]]; |
2158 | | |
2159 | 37.8k | lsig = (struct cli_ac_lsig *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_ac_lsig)); |
2160 | 37.8k | if (!lsig) { |
2161 | 0 | cli_errmsg("cli_loadldb: Can't allocate memory for lsig\n"); |
2162 | 0 | status = CL_EMEM; |
2163 | 0 | goto done; |
2164 | 0 | } |
2165 | | |
2166 | 37.8k | lsig->type = CLI_LSIG_NORMAL; |
2167 | 37.8k | lsig->u.logic = CLI_MPOOL_STRDUP(engine->mempool, logic); |
2168 | 37.8k | if (!lsig->u.logic) { |
2169 | 0 | cli_errmsg("cli_loadldb: Can't allocate memory for lsig->logic\n"); |
2170 | 0 | status = CL_EMEM; |
2171 | 0 | goto done; |
2172 | 0 | } |
2173 | | |
2174 | 37.8k | lsigid[0] = lsig->id = root->ac_lsigs; |
2175 | | |
2176 | 37.8k | newtable = (struct cli_ac_lsig **)MPOOL_REALLOC(engine->mempool, root->ac_lsigtable, (root->ac_lsigs + 1) * sizeof(struct cli_ac_lsig *)); |
2177 | 37.8k | if (!newtable) { |
2178 | 0 | cli_errmsg("cli_loadldb: Can't realloc root->ac_lsigtable\n"); |
2179 | 0 | status = CL_EMEM; |
2180 | 0 | goto done; |
2181 | 0 | } |
2182 | | |
2183 | | /* 0 marks no bc, we can't use a pointer to bc, since that is |
2184 | | * realloced/moved during load */ |
2185 | 37.8k | lsig->bc_idx = bc_idx; |
2186 | 37.8k | newtable[root->ac_lsigs] = lsig; |
2187 | 37.8k | root->ac_lsigtable = newtable; |
2188 | 37.8k | tdb.subsigs = subsigs; |
2189 | | |
2190 | | /* For logical subsignatures, only store the virname in the lsigtable entry. */ |
2191 | 37.8k | lsig->virname = CLI_MPOOL_VIRNAME(engine->mempool, virname, options & CL_DB_OFFICIAL); |
2192 | 37.8k | if (NULL == lsig->virname) { |
2193 | 6 | cli_errmsg("cli_loadldb: Can't allocate memory for virname in lsig table\n"); |
2194 | 6 | status = CL_EMEM; |
2195 | 6 | goto done; |
2196 | 6 | } |
2197 | | |
2198 | 84.5k | for (i = 0; i < subsigs; i++) { |
2199 | 50.7k | lsigid[1] = i; |
2200 | | |
2201 | | // handle each LDB subsig |
2202 | 50.7k | ret = readdb_parse_ldb_subsignature(root, virname, tokens[3 + i], "*", lsigid, options, i, subsigs, &tdb); |
2203 | 50.7k | if (CL_SUCCESS != ret) { |
2204 | 4.07k | cli_errmsg("cli_loadldb: failed to parse subsignature %d in %s\n", i, virname); |
2205 | 4.07k | status = ret; |
2206 | 4.07k | goto done; |
2207 | 4.07k | } |
2208 | 50.7k | } |
2209 | | |
2210 | 33.7k | memcpy(&lsig->tdb, &tdb, sizeof(tdb)); |
2211 | | |
2212 | | /* Increment signature counts */ |
2213 | 33.7k | (*sigs) += 1; |
2214 | | |
2215 | 33.7k | if (bc_idx) { |
2216 | 0 | root->linked_bcs++; |
2217 | 0 | } |
2218 | 33.7k | root->ac_lsigs++; |
2219 | | |
2220 | 33.7k | status = CL_SUCCESS; |
2221 | | |
2222 | 48.1k | done: |
2223 | | |
2224 | 48.1k | if (CL_SUCCESS != status) { |
2225 | 14.4k | if (NULL != lsig) { |
2226 | 4.08k | if (NULL != lsig->virname) { |
2227 | 4.07k | MPOOL_FREE(engine->mempool, lsig->virname); |
2228 | 4.07k | } |
2229 | | |
2230 | 4.08k | if (NULL != lsig->u.logic) { |
2231 | 4.08k | MPOOL_FREE(engine->mempool, lsig->u.logic); |
2232 | 4.08k | } |
2233 | | |
2234 | 4.08k | MPOOL_FREE(engine->mempool, lsig); |
2235 | 4.08k | } |
2236 | 14.4k | if (tdb_initialized) { |
2237 | 4.08k | FREE_TDB(tdb); |
2238 | 4.08k | } |
2239 | 14.4k | } |
2240 | | |
2241 | 48.1k | if (status == CL_BREAK) { |
2242 | 9.32k | status = CL_SUCCESS; |
2243 | 9.32k | } |
2244 | 48.1k | return status; |
2245 | 33.7k | } |
2246 | | |
2247 | | static int cli_loadldb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
2248 | 6.96k | { |
2249 | 6.96k | char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1], *buffer_cpy = NULL; |
2250 | 6.96k | unsigned int line = 0, sigs = 0; |
2251 | 6.96k | int ret; |
2252 | | |
2253 | 6.96k | if (CL_SUCCESS != (ret = cli_initroots(engine, options))) |
2254 | 0 | return ret; |
2255 | | |
2256 | 6.96k | if (engine->ignored) { |
2257 | 0 | if (!(buffer_cpy = cli_malloc(sizeof(buffer)))) { |
2258 | 0 | cli_errmsg("cli_loadldb: Can't allocate memory for buffer_cpy\n"); |
2259 | 0 | return CL_EMEM; |
2260 | 0 | } |
2261 | 0 | } |
2262 | | |
2263 | 51.0k | while (cli_dbgets(buffer, sizeof(buffer), fs, dbio)) { |
2264 | 49.1k | line++; |
2265 | 49.1k | if (buffer[0] == '#') |
2266 | 979 | continue; |
2267 | | |
2268 | 48.1k | cli_chomp(buffer); |
2269 | | |
2270 | 48.1k | if (engine->ignored) |
2271 | 0 | strcpy(buffer_cpy, buffer); |
2272 | | |
2273 | 48.1k | ret = load_oneldb(buffer, |
2274 | 48.1k | engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)), |
2275 | 48.1k | engine, options, dbname, line, &sigs, 0, buffer_cpy, NULL); |
2276 | 48.1k | if (ret) |
2277 | 5.08k | break; |
2278 | | |
2279 | 43.1k | if (engine->cb_sigload_progress && ((*signo + sigs) % 10000 == 0)) { |
2280 | | /* Let the progress callback function know how we're doing */ |
2281 | 0 | (void)engine->cb_sigload_progress(engine->num_total_signatures, *signo + sigs, engine->cb_sigload_progress_ctx); |
2282 | 0 | } |
2283 | 43.1k | } |
2284 | | |
2285 | 6.96k | if (engine->ignored) |
2286 | 0 | free(buffer_cpy); |
2287 | | |
2288 | 6.96k | if (!line) { |
2289 | 0 | cli_errmsg("Empty database file\n"); |
2290 | 0 | return CL_EMALFDB; |
2291 | 0 | } |
2292 | | |
2293 | 6.96k | if (ret) { |
2294 | 5.08k | cli_errmsg("Problem parsing database at line %u\n", line); |
2295 | 5.08k | return ret; |
2296 | 5.08k | } |
2297 | | |
2298 | 1.88k | if (signo) |
2299 | 1.88k | *signo += sigs; |
2300 | | |
2301 | 1.88k | return CL_SUCCESS; |
2302 | 6.96k | } |
2303 | | |
2304 | | static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
2305 | 0 | { |
2306 | 0 | char buf[4096]; |
2307 | 0 | int rc, skip = 0; |
2308 | 0 | struct cli_all_bc *bcs = &engine->bcs; |
2309 | 0 | struct cli_bc *bc; |
2310 | 0 | unsigned sigs = 0; |
2311 | 0 | unsigned security_trust = 0; |
2312 | 0 | unsigned i; |
2313 | | |
2314 | | /* TODO: virusname have a common prefix, and allow by that */ |
2315 | 0 | if ((rc = cli_initroots(engine, options))) |
2316 | 0 | return rc; |
2317 | | |
2318 | 0 | if (!(engine->dconf->bytecode & BYTECODE_ENGINE_MASK)) { |
2319 | 0 | return CL_SUCCESS; |
2320 | 0 | } |
2321 | | |
2322 | 0 | if (engine->cb_sigload && engine->cb_sigload("cbc", dbname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
2323 | 0 | cli_dbgmsg("cli_loadcbc: skipping %s due to callback\n", dbname); |
2324 | 0 | return CL_SUCCESS; |
2325 | 0 | } |
2326 | | |
2327 | 0 | if (!(options & CL_DB_BYTECODE_UNSIGNED) && !(options & CL_DB_SIGNED)) { |
2328 | 0 | cli_warnmsg("Only loading signed bytecode, skipping load of unsigned bytecode!\n"); |
2329 | 0 | cli_warnmsg("Turn on BytecodeUnsigned/--bytecode-unsigned to enable loading of unsigned bytecode\n"); |
2330 | 0 | return CL_SUCCESS; |
2331 | 0 | } |
2332 | | |
2333 | 0 | bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs) * (bcs->count + 1)); |
2334 | 0 | if (!bcs->all_bcs) { |
2335 | 0 | cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n"); |
2336 | 0 | return CL_EMEM; |
2337 | 0 | } |
2338 | 0 | bcs->count++; |
2339 | 0 | bc = &bcs->all_bcs[bcs->count - 1]; |
2340 | |
|
2341 | 0 | switch (engine->bytecode_security) { |
2342 | 0 | case CL_BYTECODE_TRUST_SIGNED: |
2343 | 0 | security_trust = !!(options & CL_DB_SIGNED); |
2344 | 0 | break; |
2345 | 0 | default: |
2346 | 0 | security_trust = 0; |
2347 | 0 | } |
2348 | | |
2349 | 0 | rc = cli_bytecode_load(bc, fs, dbio, security_trust, options & CL_DB_BYTECODE_STATS); |
2350 | | /* read remainder of DB, needed because cvd.c checks that we read the entire |
2351 | | * file */ |
2352 | 0 | while (cli_dbgets(buf, sizeof(buf), fs, dbio)) { |
2353 | 0 | } |
2354 | |
|
2355 | 0 | if (rc != CL_SUCCESS) { |
2356 | 0 | cli_bytecode_destroy(bc); |
2357 | 0 | cli_errmsg("Unable to load %s bytecode: %s\n", dbname, cl_strerror(rc)); |
2358 | 0 | return rc; |
2359 | 0 | } |
2360 | 0 | if (bc->state == bc_skip) { |
2361 | 0 | cli_bytecode_destroy(bc); |
2362 | 0 | bcs->count--; |
2363 | 0 | return CL_SUCCESS; |
2364 | 0 | } |
2365 | 0 | bc->id = bcs->count; /* must set after _load, since load zeroes */ |
2366 | 0 | if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST) |
2367 | 0 | cli_infomsg(NULL, "bytecode %u -> %s\n", bc->id, dbname); |
2368 | 0 | if (bc->kind == BC_LOGICAL || bc->lsig) { |
2369 | 0 | unsigned oldsigs = sigs; |
2370 | 0 | if (!bc->lsig) { |
2371 | 0 | cli_errmsg("Bytecode %s has logical kind, but missing logical signature!\n", dbname); |
2372 | 0 | return CL_EMALFDB; |
2373 | 0 | } |
2374 | 0 | cli_dbgmsg("Bytecode %s(%u) has logical signature: %s\n", dbname, bc->id, bc->lsig); |
2375 | 0 | rc = load_oneldb(bc->lsig, 0, engine, options, dbname, 0, &sigs, bcs->count, NULL, &skip); |
2376 | 0 | if (rc != CL_SUCCESS) { |
2377 | 0 | cli_errmsg("Problem parsing logical signature %s for bytecode %s: %s\n", |
2378 | 0 | bc->lsig, dbname, cl_strerror(rc)); |
2379 | 0 | return rc; |
2380 | 0 | } |
2381 | 0 | if (skip) { |
2382 | 0 | cli_bytecode_destroy(bc); |
2383 | 0 | bcs->count--; |
2384 | 0 | return CL_SUCCESS; |
2385 | 0 | } |
2386 | 0 | if (sigs == oldsigs) { |
2387 | | /* compiler ensures Engine field in lsig matches the one in bytecode, |
2388 | | * so this should never happen. */ |
2389 | 0 | cli_errmsg("Bytecode logical signature skipped, but bytecode itself not?"); |
2390 | 0 | return CL_EMALFDB; |
2391 | 0 | } |
2392 | 0 | } |
2393 | | |
2394 | 0 | if (bc->kind != BC_LOGICAL) { |
2395 | 0 | if (bc->lsig) { |
2396 | | /* runlsig will only flip a status bit, not report a match, |
2397 | | * when the hooks are executed we only execute the hook if its |
2398 | | * status bit is on */ |
2399 | 0 | bc->hook_lsig_id = ++engine->hook_lsig_ids; |
2400 | 0 | } |
2401 | 0 | if (bc->kind >= _BC_START_HOOKS && bc->kind < _BC_LAST_HOOK) { |
2402 | 0 | unsigned hook = bc->kind - _BC_START_HOOKS; |
2403 | 0 | unsigned cnt = ++engine->hooks_cnt[hook]; |
2404 | 0 | engine->hooks[hook] = cli_realloc2(engine->hooks[hook], |
2405 | 0 | sizeof(*engine->hooks[0]) * cnt); |
2406 | 0 | if (!engine->hooks[hook]) { |
2407 | 0 | cli_errmsg("Out of memory allocating memory for hook %u", hook); |
2408 | 0 | return CL_EMEM; |
2409 | 0 | } |
2410 | 0 | engine->hooks[hook][cnt - 1] = bcs->count - 1; |
2411 | 0 | } else |
2412 | 0 | switch (bc->kind) { |
2413 | 0 | case BC_STARTUP: |
2414 | 0 | for (i = 0; i < bcs->count - 1; i++) |
2415 | 0 | if (bcs->all_bcs[i].kind == BC_STARTUP) { |
2416 | 0 | struct cli_bc *bc0 = &bcs->all_bcs[i]; |
2417 | 0 | cli_errmsg("Can only load 1 BC_STARTUP bytecode, attempted to load 2nd!\n"); |
2418 | 0 | cli_warnmsg("Previous BC_STARTUP: %d %d by %s\n", |
2419 | 0 | bc0->id, (uint32_t)bc0->metadata.timestamp, |
2420 | 0 | bc0->metadata.sigmaker ? bc0->metadata.sigmaker : "N/A"); |
2421 | 0 | cli_warnmsg("Conflicting BC_STARTUP: %d %d by %s\n", |
2422 | 0 | bc->id, (uint32_t)bc->metadata.timestamp, |
2423 | 0 | bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A"); |
2424 | 0 | return CL_EMALFDB; |
2425 | 0 | } |
2426 | 0 | break; |
2427 | 0 | default: |
2428 | 0 | cli_errmsg("Bytecode: unhandled bytecode kind %u\n", bc->kind); |
2429 | 0 | return CL_EMALFDB; |
2430 | 0 | } |
2431 | 0 | } |
2432 | 0 | if (signo) |
2433 | 0 | *signo += sigs; |
2434 | 0 | return CL_SUCCESS; |
2435 | 0 | } |
2436 | | |
2437 | | /* 0 1 2 3 4 5 6 7 |
2438 | | * MagicType:Offset:HexSig:Name:RequiredType:DetectedType[:MinFL[:MaxFL]] |
2439 | | */ |
2440 | 3.49M | #define FTM_TOKENS 8 |
2441 | | static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio) |
2442 | 26.2k | { |
2443 | 26.2k | const char *tokens[FTM_TOKENS + 1], *pt; |
2444 | 26.2k | char buffer[FILEBUFF]; |
2445 | 26.2k | unsigned int line = 0, sigs = 0, tokens_count; |
2446 | 26.2k | struct cli_ftype *new; |
2447 | 26.2k | cli_file_t rtype, type; |
2448 | 26.2k | int ret; |
2449 | 26.2k | int magictype; |
2450 | | |
2451 | 26.2k | if (CL_SUCCESS != (ret = cli_initroots(engine, options))) |
2452 | 0 | return ret; |
2453 | | |
2454 | 3.51M | while (1) { |
2455 | 3.51M | if (internal) { |
2456 | 3.49M | options |= CL_DB_OFFICIAL; |
2457 | 3.49M | if (!ftypes_int[line]) |
2458 | 20.5k | break; |
2459 | 3.47M | strncpy(buffer, ftypes_int[line], sizeof(buffer)); |
2460 | 3.47M | buffer[sizeof(buffer) - 1] = '\0'; |
2461 | 3.47M | } else { |
2462 | 21.4k | if (!cli_dbgets(buffer, FILEBUFF, fs, dbio)) |
2463 | 1.58k | break; |
2464 | 19.8k | if (buffer[0] == '#') |
2465 | 1.79k | continue; |
2466 | 18.0k | cli_chomp(buffer); |
2467 | 18.0k | } |
2468 | 3.49M | line++; |
2469 | 3.49M | tokens_count = cli_strtokenize(buffer, ':', FTM_TOKENS + 1, tokens); |
2470 | | |
2471 | 3.49M | if (tokens_count < 6 || tokens_count > 8) { |
2472 | 152 | ret = CL_EMALFDB; |
2473 | 152 | break; |
2474 | 152 | } |
2475 | | |
2476 | 3.49M | if (tokens_count > 6) { /* min version */ |
2477 | 1.33M | pt = tokens[6]; |
2478 | 1.33M | if (!cli_isnumber(pt)) { |
2479 | 15 | ret = CL_EMALFDB; |
2480 | 15 | break; |
2481 | 15 | } |
2482 | 1.33M | if ((unsigned int)atoi(pt) > cl_retflevel()) { |
2483 | 1.25k | cli_dbgmsg("cli_loadftm: File type signature for %s not loaded (required f-level: %u)\n", tokens[3], atoi(pt)); |
2484 | 1.25k | continue; |
2485 | 1.25k | } |
2486 | 1.33M | if (tokens_count == 8) { /* max version */ |
2487 | 247k | pt = tokens[7]; |
2488 | 247k | if (!cli_isnumber(pt)) { |
2489 | 1 | ret = CL_EMALFDB; |
2490 | 1 | break; |
2491 | 1 | } |
2492 | 247k | if ((unsigned int)atoi(pt) < cl_retflevel()) |
2493 | 246k | continue; |
2494 | 247k | } |
2495 | 1.33M | } |
2496 | | |
2497 | 3.24M | rtype = cli_ftcode(tokens[4]); |
2498 | 3.24M | type = cli_ftcode(tokens[5]); |
2499 | 3.24M | if (rtype == CL_TYPE_ERROR || type == CL_TYPE_ERROR) { |
2500 | 202 | ret = CL_EMALFDB; |
2501 | 202 | break; |
2502 | 202 | } |
2503 | | |
2504 | 3.24M | if (!cli_isnumber(tokens[0])) { |
2505 | 1 | cli_errmsg("cli_loadftm: Invalid value for the first field\n"); |
2506 | 1 | ret = CL_EMALFDB; |
2507 | 1 | break; |
2508 | 1 | } |
2509 | | |
2510 | 3.24M | magictype = atoi(tokens[0]); |
2511 | 3.24M | if (magictype == 1) { /* A-C */ |
2512 | 1.30M | if (CL_SUCCESS != (ret = cli_add_content_match_pattern(engine->root[0], tokens[3], tokens[2], 0, rtype, type, tokens[1], NULL, options))) |
2513 | 3.72k | break; |
2514 | | |
2515 | 1.93M | } else if ((magictype == 0) || (magictype == 4)) { /* memcmp() */ |
2516 | 1.93M | if (!cli_isnumber(tokens[1])) { |
2517 | 1 | cli_errmsg("cli_loadftm: Invalid offset\n"); |
2518 | 1 | ret = CL_EMALFDB; |
2519 | 1 | break; |
2520 | 1 | } |
2521 | 1.93M | new = (struct cli_ftype *)MPOOL_MALLOC(engine->mempool, sizeof(struct cli_ftype)); |
2522 | 1.93M | if (!new) { |
2523 | 0 | ret = CL_EMEM; |
2524 | 0 | break; |
2525 | 0 | } |
2526 | 1.93M | new->type = type; |
2527 | 1.93M | new->offset = atoi(tokens[1]); |
2528 | 1.93M | new->magic = (unsigned char *)CLI_MPOOL_HEX2STR(engine->mempool, tokens[2]); |
2529 | 1.93M | if (!new->magic) { |
2530 | 7 | cli_errmsg("cli_loadftm: Can't decode the hex string\n"); |
2531 | 7 | ret = CL_EMALFDB; |
2532 | 7 | MPOOL_FREE(engine->mempool, new); |
2533 | 7 | break; |
2534 | 7 | } |
2535 | 1.93M | new->length = (uint16_t)strlen(tokens[2]) / 2; |
2536 | 1.93M | new->tname = CLI_MPOOL_STRDUP(engine->mempool, tokens[3]); |
2537 | 1.93M | if (!new->tname) { |
2538 | 0 | MPOOL_FREE(engine->mempool, new->magic); |
2539 | 0 | MPOOL_FREE(engine->mempool, new); |
2540 | 0 | ret = CL_EMEM; |
2541 | 0 | break; |
2542 | 0 | } |
2543 | | /* files => ftypes, partitions => ptypes */ |
2544 | 1.93M | if (magictype == 4) { |
2545 | 41.5k | new->next = engine->ptypes; |
2546 | 41.5k | engine->ptypes = new; |
2547 | 1.89M | } else { |
2548 | 1.89M | new->next = engine->ftypes; |
2549 | 1.89M | engine->ftypes = new; |
2550 | 1.89M | } |
2551 | 1.93M | } else { |
2552 | 330 | cli_dbgmsg("cli_loadftm: Unsupported mode %u\n", atoi(tokens[0])); |
2553 | 330 | continue; |
2554 | 330 | } |
2555 | 3.23M | sigs++; |
2556 | 3.23M | } |
2557 | | |
2558 | 26.2k | if (ret) { |
2559 | 4.10k | cli_errmsg("Problem parsing %s filetype database at line %u\n", internal ? "built-in" : "external", line); |
2560 | 4.10k | return ret; |
2561 | 4.10k | } |
2562 | | |
2563 | 22.1k | if (!sigs) { |
2564 | 153 | cli_errmsg("Empty %s filetype database\n", internal ? "built-in" : "external"); |
2565 | 153 | return CL_EMALFDB; |
2566 | 153 | } |
2567 | | |
2568 | 21.9k | cli_dbgmsg("Loaded %u filetype definitions\n", sigs); |
2569 | 21.9k | return CL_SUCCESS; |
2570 | 22.1k | } |
2571 | | |
2572 | 0 | #define INFO_NSTR "11088894983048545473659556106627194923928941791795047620591658697413581043322715912172496806525381055880964520618400224333320534660299233983755341740679502866829909679955734391392668378361221524205396631090105151641270857277080310734320951653700508941717419168723942507890702904702707587451621691050754307850383399865346487203798464178537392211402786481359824461197231102895415093770394216666324484593935762408468516826633192140826667923494822045805347809932848454845886971706424360558667862775876072059437703365380209101697738577515476935085469455279994113145977994084618328482151013142393373316337519977244732747977" |
2573 | 0 | #define INFO_ESTR "100002049" |
2574 | 0 | #define INFO_TOKENS 3 |
2575 | | static int cli_loadinfo(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio) |
2576 | 0 | { |
2577 | 0 | const char *tokens[INFO_TOKENS + 1]; |
2578 | 0 | char buffer[FILEBUFF]; |
2579 | 0 | unsigned int line = 0, tokens_count, len; |
2580 | 0 | char hash[32]; |
2581 | 0 | struct cli_dbinfo *last = NULL, *new; |
2582 | 0 | int ret = CL_SUCCESS, dsig = 0; |
2583 | 0 | void *ctx; |
2584 | |
|
2585 | 0 | if (!dbio) { |
2586 | 0 | cli_errmsg("cli_loadinfo: .info files can only be loaded from within database container files\n"); |
2587 | 0 | return CL_EMALFDB; |
2588 | 0 | } |
2589 | | |
2590 | 0 | ctx = cl_hash_init("sha256"); |
2591 | 0 | if (!(ctx)) |
2592 | 0 | return CL_EMALFDB; |
2593 | | |
2594 | 0 | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
2595 | 0 | line++; |
2596 | 0 | if (!(options & CL_DB_UNSIGNED) && !strncmp(buffer, "DSIG:", 5)) { |
2597 | 0 | dsig = 1; |
2598 | 0 | cl_finish_hash(ctx, hash); |
2599 | 0 | if (cli_versig2((unsigned char *)hash, buffer + 5, INFO_NSTR, INFO_ESTR) != CL_SUCCESS) { |
2600 | 0 | cli_errmsg("cli_loadinfo: Incorrect digital signature\n"); |
2601 | 0 | ret = CL_EMALFDB; |
2602 | 0 | } |
2603 | 0 | break; |
2604 | 0 | } |
2605 | 0 | len = strlen(buffer); |
2606 | 0 | if (!len) { |
2607 | 0 | buffer[len] = '\n'; |
2608 | 0 | buffer[len + 1] = 0; |
2609 | 0 | } else { |
2610 | 0 | if (dbio->usebuf && buffer[len - 1] != '\n' && len + 1 < FILEBUFF) { |
2611 | | /* cli_dbgets in buffered mode strips \n */ |
2612 | 0 | buffer[len] = '\n'; |
2613 | 0 | buffer[len + 1] = 0; |
2614 | 0 | } |
2615 | 0 | } |
2616 | 0 | cl_update_hash(ctx, buffer, strlen(buffer)); |
2617 | 0 | cli_chomp(buffer); |
2618 | 0 | if (!strncmp("ClamAV-VDB:", buffer, 11)) { |
2619 | 0 | if (engine->dbinfo) { /* shouldn't be initialized at this point */ |
2620 | 0 | cli_errmsg("cli_loadinfo: engine->dbinfo already initialized\n"); |
2621 | 0 | ret = CL_EMALFDB; |
2622 | 0 | break; |
2623 | 0 | } |
2624 | 0 | last = engine->dbinfo = (struct cli_dbinfo *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_bm_patt)); |
2625 | 0 | if (!engine->dbinfo) { |
2626 | 0 | ret = CL_EMEM; |
2627 | 0 | break; |
2628 | 0 | } |
2629 | 0 | engine->dbinfo->cvd = cl_cvdparse(buffer); |
2630 | 0 | if (!engine->dbinfo->cvd) { |
2631 | 0 | cli_errmsg("cli_loadinfo: Can't parse header entry\n"); |
2632 | 0 | ret = CL_EMALFDB; |
2633 | 0 | break; |
2634 | 0 | } |
2635 | 0 | continue; |
2636 | 0 | } |
2637 | | |
2638 | 0 | if (!last) { |
2639 | 0 | cli_errmsg("cli_loadinfo: Incorrect file format\n"); |
2640 | 0 | ret = CL_EMALFDB; |
2641 | 0 | break; |
2642 | 0 | } |
2643 | 0 | tokens_count = cli_strtokenize(buffer, ':', INFO_TOKENS + 1, tokens); |
2644 | 0 | if (tokens_count != INFO_TOKENS) { |
2645 | 0 | ret = CL_EMALFDB; |
2646 | 0 | break; |
2647 | 0 | } |
2648 | 0 | new = (struct cli_dbinfo *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_dbinfo)); |
2649 | 0 | if (!new) { |
2650 | 0 | ret = CL_EMEM; |
2651 | 0 | break; |
2652 | 0 | } |
2653 | 0 | new->name = CLI_MPOOL_STRDUP(engine->mempool, tokens[0]); |
2654 | 0 | if (!new->name) { |
2655 | 0 | MPOOL_FREE(engine->mempool, new); |
2656 | 0 | ret = CL_EMEM; |
2657 | 0 | break; |
2658 | 0 | } |
2659 | | |
2660 | 0 | if (!cli_isnumber(tokens[1])) { |
2661 | 0 | cli_errmsg("cli_loadinfo: Invalid value in the size field\n"); |
2662 | 0 | MPOOL_FREE(engine->mempool, new->name); |
2663 | 0 | MPOOL_FREE(engine->mempool, new); |
2664 | 0 | ret = CL_EMALFDB; |
2665 | 0 | break; |
2666 | 0 | } |
2667 | 0 | new->size = atoi(tokens[1]); |
2668 | |
|
2669 | 0 | if (strlen(tokens[2]) != 64 || !(new->hash = CLI_MPOOL_HEX2STR(engine->mempool, tokens[2]))) { |
2670 | 0 | cli_errmsg("cli_loadinfo: Malformed SHA256 string at line %u\n", line); |
2671 | 0 | MPOOL_FREE(engine->mempool, new->name); |
2672 | 0 | MPOOL_FREE(engine->mempool, new); |
2673 | 0 | ret = CL_EMALFDB; |
2674 | 0 | break; |
2675 | 0 | } |
2676 | 0 | last->next = new; |
2677 | 0 | last = new; |
2678 | 0 | } |
2679 | |
|
2680 | 0 | if (!(options & CL_DB_UNSIGNED) && !dsig) { |
2681 | 0 | cli_errmsg("cli_loadinfo: Digital signature not found\n"); |
2682 | 0 | return CL_EMALFDB; |
2683 | 0 | } |
2684 | | |
2685 | 0 | if (ret) { |
2686 | 0 | cli_errmsg("cli_loadinfo: Problem parsing database at line %u\n", line); |
2687 | 0 | return ret; |
2688 | 0 | } |
2689 | | |
2690 | 0 | return CL_SUCCESS; |
2691 | 0 | } |
2692 | | |
2693 | 17.6k | #define IGN_MAX_TOKENS 3 |
2694 | | static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio) |
2695 | 722 | { |
2696 | 722 | const char *tokens[IGN_MAX_TOKENS + 1], *signame, *hash = NULL; |
2697 | 722 | char buffer[FILEBUFF]; |
2698 | 722 | unsigned int line = 0, tokens_count, len; |
2699 | 722 | struct cli_bm_patt *new; |
2700 | 722 | int ret = CL_SUCCESS; |
2701 | | |
2702 | 722 | UNUSEDPARAM(options); |
2703 | | |
2704 | 722 | if (!engine->ignored) { |
2705 | 722 | engine->ignored = (struct cli_matcher *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_matcher)); |
2706 | 722 | if (!engine->ignored) |
2707 | 0 | return CL_EMEM; |
2708 | | #ifdef USE_MPOOL |
2709 | | engine->ignored->mempool = engine->mempool; |
2710 | | #endif |
2711 | 722 | if (CL_SUCCESS != (ret = cli_bm_init(engine->ignored))) { |
2712 | 0 | cli_errmsg("cli_loadign: Can't initialise AC pattern matcher\n"); |
2713 | 0 | return ret; |
2714 | 0 | } |
2715 | 722 | } |
2716 | | |
2717 | 9.95k | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
2718 | 9.37k | line++; |
2719 | 9.37k | if (buffer[0] == '#') |
2720 | 542 | continue; |
2721 | 8.83k | cli_chomp(buffer); |
2722 | | |
2723 | 8.83k | tokens_count = cli_strtokenize(buffer, ':', IGN_MAX_TOKENS + 1, tokens); |
2724 | 8.83k | if (tokens_count > IGN_MAX_TOKENS) { |
2725 | 4 | ret = CL_EMALFDB; |
2726 | 4 | break; |
2727 | 4 | } |
2728 | | |
2729 | 8.82k | if (tokens_count == 1) { |
2730 | 7.56k | signame = buffer; |
2731 | 7.56k | } else if (tokens_count == 2) { |
2732 | 485 | signame = tokens[0]; |
2733 | 485 | hash = tokens[1]; |
2734 | 782 | } else { /* old mode */ |
2735 | 782 | signame = tokens[2]; |
2736 | 782 | } |
2737 | 8.82k | if (!(len = strlen(signame))) { |
2738 | 91 | cli_errmsg("cli_loadign: No signature name provided\n"); |
2739 | 91 | ret = CL_EMALFDB; |
2740 | 91 | break; |
2741 | 91 | } |
2742 | 8.73k | if (len < 3) { |
2743 | 7.25k | int pad = 3 - len; |
2744 | | /* patch-up for Boyer-Moore minimum length of 3: pad with spaces */ |
2745 | 7.25k | if (signame != buffer) { |
2746 | 765 | memcpy(buffer, signame, len); |
2747 | 765 | signame = buffer; |
2748 | 765 | } |
2749 | 7.25k | buffer[3] = '\0'; |
2750 | 21.5k | while (pad > 0) |
2751 | 14.2k | buffer[3 - pad--] = '\x20'; |
2752 | 7.25k | len = 3; |
2753 | 7.25k | } |
2754 | | |
2755 | 8.73k | new = (struct cli_bm_patt *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_bm_patt)); |
2756 | 8.73k | if (!new) { |
2757 | 0 | ret = CL_EMEM; |
2758 | 0 | break; |
2759 | 0 | } |
2760 | 8.73k | new->pattern = (unsigned char *)CLI_MPOOL_STRDUP(engine->mempool, signame); |
2761 | 8.73k | if (!new->pattern) { |
2762 | 0 | MPOOL_FREE(engine->mempool, new); |
2763 | 0 | ret = CL_EMEM; |
2764 | 0 | break; |
2765 | 0 | } |
2766 | 8.73k | if (hash) { |
2767 | 2.04k | if (strlen(hash) != 32 || !(new->virname = CLI_MPOOL_HEX2STR(engine->mempool, hash))) { |
2768 | 48 | cli_errmsg("cli_loadign: Malformed MD5 string at line %u\n", line); |
2769 | 48 | MPOOL_FREE(engine->mempool, new->pattern); |
2770 | 48 | MPOOL_FREE(engine->mempool, new); |
2771 | 48 | ret = CL_EMALFDB; |
2772 | 48 | break; |
2773 | 48 | } |
2774 | 2.04k | } |
2775 | 8.68k | new->length = len; |
2776 | 8.68k | new->boundary |= BM_BOUNDARY_EOL; |
2777 | | |
2778 | 8.68k | if (CL_SUCCESS != (ret = cli_bm_addpatt(engine->ignored, new, "0"))) { |
2779 | 0 | if (hash) |
2780 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
2781 | 0 | MPOOL_FREE(engine->mempool, new->pattern); |
2782 | 0 | MPOOL_FREE(engine->mempool, new); |
2783 | 0 | break; |
2784 | 0 | } |
2785 | 8.68k | } |
2786 | | |
2787 | 722 | if (ret) { |
2788 | 143 | cli_errmsg("cli_loadign: Problem parsing database at line %u\n", line); |
2789 | 143 | return ret; |
2790 | 143 | } |
2791 | | |
2792 | 579 | return CL_SUCCESS; |
2793 | 722 | } |
2794 | | |
2795 | 13.3k | #define MD5_HDB 0 |
2796 | 13.3k | #define MD5_MDB 1 |
2797 | 1.30k | #define MD5_FP 2 |
2798 | 505k | #define MD5_IMP 3 |
2799 | | |
2800 | 4.50M | #define MD5_TOKENS 5 |
2801 | | static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
2802 | 6.67k | { |
2803 | 6.67k | const char *tokens[MD5_TOKENS + 1]; |
2804 | 6.67k | char buffer[FILEBUFF], *buffer_cpy = NULL; |
2805 | 6.67k | const char *pt, *virname; |
2806 | 6.67k | int ret = CL_SUCCESS; |
2807 | 6.67k | unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count; |
2808 | 6.67k | unsigned int req_fl = 0; |
2809 | 6.67k | struct cli_matcher *db; |
2810 | 6.67k | unsigned long size; |
2811 | | |
2812 | 6.67k | if (mode == MD5_MDB) { |
2813 | 2.69k | size_field = 0; |
2814 | 2.69k | md5_field = 1; |
2815 | 2.69k | db = engine->hm_mdb; |
2816 | 3.98k | } else if (mode == MD5_HDB) |
2817 | 2.67k | db = engine->hm_hdb; |
2818 | 1.30k | else if (mode == MD5_IMP) |
2819 | 0 | db = engine->hm_imp; |
2820 | 1.30k | else |
2821 | 1.30k | db = engine->hm_fp; |
2822 | | |
2823 | 6.67k | if (!db) { |
2824 | 6.67k | if (!(db = MPOOL_CALLOC(engine->mempool, 1, sizeof(*db)))) |
2825 | 0 | return CL_EMEM; |
2826 | | #ifdef USE_MPOOL |
2827 | | db->mempool = engine->mempool; |
2828 | | #endif |
2829 | 6.67k | if (mode == MD5_HDB) |
2830 | 2.67k | engine->hm_hdb = db; |
2831 | 4.00k | else if (mode == MD5_MDB) |
2832 | 2.69k | engine->hm_mdb = db; |
2833 | 1.30k | else if (mode == MD5_IMP) |
2834 | 0 | engine->hm_imp = db; |
2835 | 1.30k | else |
2836 | 1.30k | engine->hm_fp = db; |
2837 | 6.67k | } |
2838 | | |
2839 | 6.67k | if (engine->ignored) |
2840 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
2841 | 0 | cli_errmsg("cli_loadhash: Can't allocate memory for buffer_cpy\n"); |
2842 | 0 | return CL_EMEM; |
2843 | 0 | } |
2844 | | |
2845 | 2.61M | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
2846 | 2.60M | line++; |
2847 | 2.60M | if (buffer[0] == '#') |
2848 | 912k | continue; |
2849 | 1.69M | cli_chomp(buffer); |
2850 | 1.69M | if (engine->ignored) |
2851 | 0 | strcpy(buffer_cpy, buffer); |
2852 | | |
2853 | 1.69M | tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens); |
2854 | 1.69M | if (tokens_count < 3) { |
2855 | 389 | ret = CL_EMALFDB; |
2856 | 389 | break; |
2857 | 389 | } |
2858 | 1.69M | if (tokens_count > MD5_TOKENS - 2) { |
2859 | 286k | req_fl = atoi(tokens[MD5_TOKENS - 2]); |
2860 | | |
2861 | 286k | if (tokens_count > MD5_TOKENS) { |
2862 | 41 | ret = CL_EMALFDB; |
2863 | 41 | break; |
2864 | 41 | } |
2865 | | |
2866 | 286k | if (cl_retflevel() < req_fl) |
2867 | 5.92k | continue; |
2868 | 280k | if (tokens_count == MD5_TOKENS) { |
2869 | 13.7k | int max_fl = atoi(tokens[MD5_TOKENS - 1]); |
2870 | 13.7k | if (cl_retflevel() > (unsigned int)max_fl) |
2871 | 9.26k | continue; |
2872 | 13.7k | } |
2873 | 280k | } |
2874 | | |
2875 | 1.67M | if (strcmp(tokens[size_field], "*")) { |
2876 | 1.42M | size = strtoul(tokens[size_field], (char **)&pt, 10); |
2877 | 1.42M | if (*pt || !size || size >= 0xffffffff) { |
2878 | 844 | cli_errmsg("cli_loadhash: Invalid value for the size field\n"); |
2879 | 844 | ret = CL_EMALFDB; |
2880 | 844 | break; |
2881 | 844 | } |
2882 | 1.42M | } else { |
2883 | 251k | size = 0; |
2884 | | // The wildcard feature was added in FLEVEL 73, so for backwards |
2885 | | // compatibility with older clients, ensure that a minimum FLEVEL |
2886 | | // is specified. This check doesn't apply to .imp rules, though, |
2887 | | // since this rule category wasn't introduced until FLEVEL 90, and |
2888 | | // has always supported wildcard usage in rules. |
2889 | 251k | if (mode != MD5_IMP && ((tokens_count < MD5_TOKENS - 1) || (req_fl < 73))) { |
2890 | 85 | cli_errmsg("cli_loadhash: Minimum FLEVEL field must be at least 73 for wildcard size hash signatures." |
2891 | 85 | " For reference, running FLEVEL is %d\n", |
2892 | 85 | cl_retflevel()); |
2893 | 85 | ret = CL_EMALFDB; |
2894 | 85 | break; |
2895 | 85 | } |
2896 | 251k | } |
2897 | | |
2898 | 1.67M | pt = tokens[2]; /* virname */ |
2899 | 1.67M | if (engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE))) |
2900 | 0 | if (cli_chkpua(pt, engine->pua_cats, options)) |
2901 | 0 | continue; |
2902 | | |
2903 | 1.67M | if (engine->ignored && cli_chkign(engine->ignored, pt, buffer_cpy)) |
2904 | 0 | continue; |
2905 | | |
2906 | 1.67M | if (engine->cb_sigload) { |
2907 | 0 | const char *dot = strchr(dbname, '.'); |
2908 | 0 | if (!dot) |
2909 | 0 | dot = dbname; |
2910 | 0 | else |
2911 | 0 | dot++; |
2912 | 0 | if (engine->cb_sigload(dot, pt, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
2913 | 0 | cli_dbgmsg("cli_loadhash: skipping %s (%s) due to callback\n", pt, dot); |
2914 | 0 | continue; |
2915 | 0 | } |
2916 | 0 | } |
2917 | | |
2918 | 1.67M | virname = CLI_MPOOL_VIRNAME(engine->mempool, pt, options & CL_DB_OFFICIAL); |
2919 | 1.67M | if (!virname) { |
2920 | 424 | ret = CL_EMALFDB; |
2921 | 424 | break; |
2922 | 424 | } |
2923 | | |
2924 | 1.67M | if (CL_SUCCESS != (ret = hm_addhash_str(db, tokens[md5_field], size, virname))) { |
2925 | 457 | cli_errmsg("cli_loadhash: Malformed hash string at line %u\n", line); |
2926 | 457 | MPOOL_FREE(engine->mempool, (void *)virname); |
2927 | 457 | break; |
2928 | 457 | } |
2929 | | |
2930 | 1.67M | sigs++; |
2931 | | |
2932 | 1.67M | if (engine->cb_sigload_progress && ((*signo + sigs) % 10000 == 0)) { |
2933 | | /* Let the progress callback function know how we're doing */ |
2934 | 0 | (void)engine->cb_sigload_progress(engine->num_total_signatures, *signo + sigs, engine->cb_sigload_progress_ctx); |
2935 | 0 | } |
2936 | 1.67M | } |
2937 | 6.67k | if (engine->ignored) |
2938 | 0 | free(buffer_cpy); |
2939 | | |
2940 | 6.67k | if (!line) { |
2941 | 0 | cli_errmsg("cli_loadhash: Empty database file\n"); |
2942 | 0 | return CL_EMALFDB; |
2943 | 0 | } |
2944 | | |
2945 | 6.67k | if (ret) { |
2946 | 2.24k | cli_errmsg("cli_loadhash: Problem parsing database at line %u\n", line); |
2947 | 2.24k | return ret; |
2948 | 2.24k | } |
2949 | | |
2950 | 4.43k | if (signo) |
2951 | 4.43k | *signo += sigs; |
2952 | | |
2953 | 4.43k | return CL_SUCCESS; |
2954 | 6.67k | } |
2955 | | |
2956 | 0 | #define MD_TOKENS 9 |
2957 | | static int cli_loadmd(FILE *fs, struct cl_engine *engine, unsigned int *signo, int type, unsigned int options, struct cli_dbio *dbio, const char *dbname) |
2958 | 0 | { |
2959 | 0 | const char *tokens[MD_TOKENS + 1]; |
2960 | 0 | char buffer[FILEBUFF], *buffer_cpy = NULL; |
2961 | 0 | unsigned int line = 0, sigs = 0, tokens_count; |
2962 | 0 | int ret = CL_SUCCESS; |
2963 | 0 | struct cli_cdb *new; |
2964 | |
|
2965 | 0 | UNUSEDPARAM(dbname); |
2966 | |
|
2967 | 0 | if (engine->ignored) |
2968 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
2969 | 0 | cli_errmsg("cli_loadmd: Can't allocate memory for buffer_cpy\n"); |
2970 | 0 | return CL_EMEM; |
2971 | 0 | } |
2972 | | |
2973 | 0 | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
2974 | 0 | line++; |
2975 | 0 | if (buffer[0] == '#') |
2976 | 0 | continue; |
2977 | | |
2978 | 0 | cli_chomp(buffer); |
2979 | 0 | if (engine->ignored) |
2980 | 0 | strcpy(buffer_cpy, buffer); |
2981 | |
|
2982 | 0 | tokens_count = cli_strtokenize(buffer, ':', MD_TOKENS + 1, tokens); |
2983 | 0 | if (tokens_count != MD_TOKENS) { |
2984 | 0 | ret = CL_EMALFDB; |
2985 | 0 | break; |
2986 | 0 | } |
2987 | | |
2988 | 0 | if (strcmp(tokens[1], "*") && !cli_isnumber(tokens[1])) { |
2989 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'encrypted' field\n"); |
2990 | 0 | ret = CL_EMALFDB; |
2991 | 0 | break; |
2992 | 0 | } |
2993 | 0 | if (strcmp(tokens[3], "*") && !cli_isnumber(tokens[3])) { |
2994 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'original size' field\n"); |
2995 | 0 | ret = CL_EMALFDB; |
2996 | 0 | break; |
2997 | 0 | } |
2998 | 0 | if (strcmp(tokens[4], "*") && !cli_isnumber(tokens[4])) { |
2999 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'compressed size' field\n"); |
3000 | 0 | ret = CL_EMALFDB; |
3001 | 0 | break; |
3002 | 0 | } |
3003 | 0 | if (strcmp(tokens[6], "*") && !cli_isnumber(tokens[6])) { |
3004 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'compression method' field\n"); |
3005 | 0 | ret = CL_EMALFDB; |
3006 | 0 | break; |
3007 | 0 | } |
3008 | 0 | if (strcmp(tokens[7], "*") && !cli_isnumber(tokens[7])) { |
3009 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'file number' field\n"); |
3010 | 0 | ret = CL_EMALFDB; |
3011 | 0 | break; |
3012 | 0 | } |
3013 | 0 | if (strcmp(tokens[8], "*") && !cli_isnumber(tokens[8])) { |
3014 | 0 | cli_errmsg("cli_loadmd: Invalid value for the 'max depth' field\n"); |
3015 | 0 | ret = CL_EMALFDB; |
3016 | 0 | break; |
3017 | 0 | } |
3018 | | |
3019 | 0 | new = (struct cli_cdb *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_cdb)); |
3020 | 0 | if (!new) { |
3021 | 0 | ret = CL_EMEM; |
3022 | 0 | break; |
3023 | 0 | } |
3024 | | |
3025 | 0 | new->virname = CLI_MPOOL_VIRNAME(engine->mempool, tokens[0], options & CL_DB_OFFICIAL); |
3026 | 0 | if (!new->virname) { |
3027 | 0 | MPOOL_FREE(engine->mempool, new); |
3028 | 0 | ret = CL_EMEM; |
3029 | 0 | break; |
3030 | 0 | } |
3031 | 0 | new->ctype = (type == 1) ? CL_TYPE_ZIP : CL_TYPE_RAR; |
3032 | |
|
3033 | 0 | if (engine->ignored && cli_chkign(engine->ignored, new->virname, buffer /*_cpy*/)) { |
3034 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3035 | 0 | MPOOL_FREE(engine->mempool, new); |
3036 | 0 | continue; |
3037 | 0 | } |
3038 | | |
3039 | 0 | if (engine->cb_sigload && engine->cb_sigload("md", new->virname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
3040 | 0 | cli_dbgmsg("cli_loadmd: skipping %s due to callback\n", new->virname); |
3041 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3042 | 0 | MPOOL_FREE(engine->mempool, new); |
3043 | 0 | continue; |
3044 | 0 | } |
3045 | | |
3046 | 0 | new->encrypted = strcmp(tokens[1], "*") ? atoi(tokens[1]) : 2; |
3047 | |
|
3048 | 0 | if (strcmp(tokens[2], "*") && cli_regcomp(&new->name, tokens[2], REG_EXTENDED | REG_NOSUB)) { |
3049 | 0 | cli_errmsg("cli_loadmd: Can't compile regular expression %s in signature for %s\n", tokens[2], tokens[0]); |
3050 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3051 | 0 | MPOOL_FREE(engine->mempool, new); |
3052 | 0 | ret = CL_EMEM; |
3053 | 0 | break; |
3054 | 0 | } |
3055 | 0 | new->csize[0] = new->csize[1] = CLI_OFF_ANY; |
3056 | |
|
3057 | 0 | if (!strcmp(tokens[3], "*")) |
3058 | 0 | new->fsizer[0] = new->fsizer[1] = CLI_OFF_ANY; |
3059 | 0 | else |
3060 | 0 | new->fsizer[0] = new->fsizer[1] = atoi(tokens[3]); |
3061 | |
|
3062 | 0 | if (!strcmp(tokens[4], "*")) |
3063 | 0 | new->fsizec[0] = new->fsizec[1] = CLI_OFF_ANY; |
3064 | 0 | else |
3065 | 0 | new->fsizec[0] = new->fsizec[1] = atoi(tokens[4]); |
3066 | |
|
3067 | 0 | if (strcmp(tokens[5], "*")) { |
3068 | 0 | new->res1 = cli_hex2num(tokens[5]); |
3069 | 0 | if (new->res1 == -1) { |
3070 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3071 | 0 | MPOOL_FREE(engine->mempool, new); |
3072 | 0 | if (new->name.re_magic) |
3073 | 0 | cli_regfree(&new->name); |
3074 | 0 | ret = CL_EMALFDB; |
3075 | 0 | break; |
3076 | 0 | } |
3077 | 0 | } |
3078 | | |
3079 | | /* tokens[6] - not used */ |
3080 | | |
3081 | 0 | new->filepos[0] = new->filepos[1] = strcmp(tokens[7], "*") ? (unsigned int)atoi(tokens[7]) : (unsigned int)CLI_OFF_ANY; |
3082 | | |
3083 | | /* tokens[8] - not used */ |
3084 | |
|
3085 | 0 | new->next = engine->cdb; |
3086 | 0 | engine->cdb = new; |
3087 | 0 | sigs++; |
3088 | 0 | } |
3089 | 0 | if (engine->ignored) |
3090 | 0 | free(buffer_cpy); |
3091 | |
|
3092 | 0 | if (!line) { |
3093 | 0 | cli_errmsg("Empty database file\n"); |
3094 | 0 | return CL_EMALFDB; |
3095 | 0 | } |
3096 | | |
3097 | 0 | if (ret) { |
3098 | 0 | cli_errmsg("Problem parsing database at line %d\n", line); |
3099 | 0 | return ret; |
3100 | 0 | } |
3101 | | |
3102 | 0 | if (signo) |
3103 | 0 | *signo += sigs; |
3104 | |
|
3105 | 0 | return CL_SUCCESS; |
3106 | 0 | } |
3107 | | |
3108 | | /* 0 1 2 3 4 5 6 7 8 9 10 11 |
3109 | | * VirusName:ContainerType:ContainerSize:FileNameREGEX:FileSizeInContainer:FileSizeReal:IsEncrypted:FilePos:Res1:Res2[:MinFL[:MaxFL]] |
3110 | | */ |
3111 | | |
3112 | 59.2k | #define CDB_TOKENS 12 |
3113 | | static int cli_loadcdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio) |
3114 | 3.13k | { |
3115 | 3.13k | const char *tokens[CDB_TOKENS + 1]; |
3116 | 3.13k | char buffer[FILEBUFF], *buffer_cpy = NULL; |
3117 | 3.13k | unsigned int line = 0, sigs = 0, tokens_count, n0, n1; |
3118 | 3.13k | int ret = CL_SUCCESS; |
3119 | 3.13k | struct cli_cdb *new; |
3120 | | |
3121 | 3.13k | if (engine->ignored) |
3122 | 0 | if (!(buffer_cpy = cli_malloc(FILEBUFF))) { |
3123 | 0 | cli_errmsg("cli_loadcdb: Can't allocate memory for buffer_cpy\n"); |
3124 | 0 | return CL_EMEM; |
3125 | 0 | } |
3126 | | |
3127 | 15.3k | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
3128 | 15.1k | line++; |
3129 | 15.1k | if (buffer[0] == '#') |
3130 | 644 | continue; |
3131 | | |
3132 | 14.5k | cli_chomp(buffer); |
3133 | 14.5k | if (engine->ignored) |
3134 | 0 | strcpy(buffer_cpy, buffer); |
3135 | | |
3136 | 14.5k | tokens_count = cli_strtokenize(buffer, ':', CDB_TOKENS + 1, tokens); |
3137 | 14.5k | if (tokens_count > CDB_TOKENS || tokens_count < CDB_TOKENS - 2) { |
3138 | 142 | ret = CL_EMALFDB; |
3139 | 142 | break; |
3140 | 142 | } |
3141 | | |
3142 | 14.3k | if (tokens_count > 10) { /* min version */ |
3143 | 2.04k | if (!cli_isnumber(tokens[10])) { |
3144 | 6 | ret = CL_EMALFDB; |
3145 | 6 | break; |
3146 | 6 | } |
3147 | 2.03k | if ((unsigned int)atoi(tokens[10]) > cl_retflevel()) { |
3148 | 791 | cli_dbgmsg("cli_loadcdb: Container signature for %s not loaded (required f-level: %u)\n", tokens[0], atoi(tokens[10])); |
3149 | 791 | continue; |
3150 | 791 | } |
3151 | 1.24k | if (tokens_count == CDB_TOKENS) { /* max version */ |
3152 | 706 | if (!cli_isnumber(tokens[11])) { |
3153 | 1 | ret = CL_EMALFDB; |
3154 | 1 | break; |
3155 | 1 | } |
3156 | 705 | if ((unsigned int)atoi(tokens[11]) < cl_retflevel()) |
3157 | 471 | continue; |
3158 | 705 | } |
3159 | 1.24k | } |
3160 | | |
3161 | 13.0k | new = (struct cli_cdb *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_cdb)); |
3162 | 13.0k | if (!new) { |
3163 | 0 | ret = CL_EMEM; |
3164 | 0 | break; |
3165 | 0 | } |
3166 | | |
3167 | 13.0k | new->virname = CLI_MPOOL_VIRNAME(engine->mempool, tokens[0], options & CL_DB_OFFICIAL); |
3168 | 13.0k | if (!new->virname) { |
3169 | 71 | MPOOL_FREE(engine->mempool, new); |
3170 | 71 | ret = CL_EMEM; |
3171 | 71 | break; |
3172 | 71 | } |
3173 | | |
3174 | 13.0k | if (engine->ignored && cli_chkign(engine->ignored, new->virname, buffer /*_cpy*/)) { |
3175 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3176 | 0 | MPOOL_FREE(engine->mempool, new); |
3177 | 0 | continue; |
3178 | 0 | } |
3179 | | |
3180 | 13.0k | if (engine->cb_sigload && engine->cb_sigload("cdb", new->virname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
3181 | 0 | cli_dbgmsg("cli_loadcdb: skipping %s due to callback\n", new->virname); |
3182 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3183 | 0 | MPOOL_FREE(engine->mempool, new); |
3184 | 0 | continue; |
3185 | 0 | } |
3186 | | |
3187 | 13.0k | if (!strcmp(tokens[1], "*")) { |
3188 | 11.4k | new->ctype = CL_TYPE_ANY; |
3189 | 11.4k | } else if ((new->ctype = cli_ftcode(tokens[1])) == CL_TYPE_ERROR) { |
3190 | 110 | cli_errmsg("cli_loadcdb: Unknown container type %s in signature for %s, skipping\n", tokens[1], tokens[0]); |
3191 | 110 | ret = CL_EMALFDB; |
3192 | 110 | MPOOL_FREE(engine->mempool, new->virname); |
3193 | 110 | MPOOL_FREE(engine->mempool, new); |
3194 | 110 | break; |
3195 | 110 | } |
3196 | | |
3197 | 12.9k | if (strcmp(tokens[3], "*") && cli_regcomp(&new->name, tokens[3], REG_EXTENDED | REG_NOSUB)) { |
3198 | 1.61k | cli_errmsg("cli_loadcdb: Can't compile regular expression %s in signature for %s\n", tokens[3], tokens[0]); |
3199 | 1.61k | MPOOL_FREE(engine->mempool, new->virname); |
3200 | 1.61k | MPOOL_FREE(engine->mempool, new); |
3201 | 1.61k | ret = CL_EMEM; |
3202 | 1.61k | break; |
3203 | 1.61k | } |
3204 | | |
3205 | 11.2k | #define CDBRANGE(token_str, dest) \ |
3206 | 44.7k | if (strcmp(token_str, "*")) { \ |
3207 | 32.9k | if (strchr(token_str, '-')) { \ |
3208 | 783 | if (sscanf(token_str, "%u-%u", &n0, &n1) != 2) { \ |
3209 | 6 | ret = CL_EMALFDB; \ |
3210 | 777 | } else { \ |
3211 | 777 | dest[0] = n0; \ |
3212 | 777 | dest[1] = n1; \ |
3213 | 777 | } \ |
3214 | 32.1k | } else { \ |
3215 | 32.1k | if (!cli_isnumber(token_str)) \ |
3216 | 32.1k | ret = CL_EMALFDB; \ |
3217 | 32.1k | else \ |
3218 | 32.1k | dest[0] = dest[1] = (unsigned int)atoi(token_str); \ |
3219 | 32.1k | } \ |
3220 | 32.9k | if (ret != CL_SUCCESS) { \ |
3221 | 244 | cli_errmsg("cli_loadcdb: Invalid value %s in signature for %s\n", \ |
3222 | 244 | token_str, tokens[0]); \ |
3223 | 244 | if (new->name.re_magic) \ |
3224 | 244 | cli_regfree(&new->name); \ |
3225 | 244 | MPOOL_FREE(engine->mempool, new->virname); \ |
3226 | 244 | MPOOL_FREE(engine->mempool, new); \ |
3227 | 244 | ret = CL_EMEM; \ |
3228 | 244 | break; \ |
3229 | 244 | } \ |
3230 | 32.9k | } else { \ |
3231 | 11.8k | dest[0] = dest[1] = CLI_OFF_ANY; \ |
3232 | 11.8k | } |
3233 | | |
3234 | 11.2k | CDBRANGE(tokens[2], new->csize); |
3235 | 11.2k | CDBRANGE(tokens[4], new->fsizec); |
3236 | 11.1k | CDBRANGE(tokens[5], new->fsizer); |
3237 | 11.0k | CDBRANGE(tokens[7], new->filepos); |
3238 | | |
3239 | 11.0k | if (!strcmp(tokens[6], "*")) { |
3240 | 8.35k | new->encrypted = 2; |
3241 | 8.35k | } else { |
3242 | 2.69k | if (strcmp(tokens[6], "0") && strcmp(tokens[6], "1")) { |
3243 | 724 | cli_errmsg("cli_loadcdb: Invalid encryption flag value in signature for %s\n", tokens[0]); |
3244 | 724 | if (new->name.re_magic) |
3245 | 703 | cli_regfree(&new->name); |
3246 | 724 | MPOOL_FREE(engine->mempool, new->virname); |
3247 | 724 | MPOOL_FREE(engine->mempool, new); |
3248 | 724 | ret = CL_EMEM; |
3249 | 724 | break; |
3250 | 724 | } |
3251 | 1.97k | new->encrypted = *tokens[6] - 0x30; |
3252 | 1.97k | } |
3253 | | |
3254 | 10.3k | if (strcmp(tokens[9], "*")) { |
3255 | 9.53k | new->res2 = CLI_MPOOL_STRDUP(engine->mempool, tokens[9]); |
3256 | 9.53k | if (!new->res2) { |
3257 | 0 | cli_errmsg("cli_loadcdb: Can't allocate memory for res2 in signature for %s\n", tokens[0]); |
3258 | 0 | if (new->name.re_magic) |
3259 | 0 | cli_regfree(&new->name); |
3260 | 0 | MPOOL_FREE(engine->mempool, new->virname); |
3261 | 0 | MPOOL_FREE(engine->mempool, new); |
3262 | 0 | ret = CL_EMEM; |
3263 | 0 | break; |
3264 | 0 | } |
3265 | 9.53k | } |
3266 | | |
3267 | 10.3k | new->next = engine->cdb; |
3268 | 10.3k | engine->cdb = new; |
3269 | 10.3k | sigs++; |
3270 | 10.3k | } |
3271 | 3.13k | if (engine->ignored) |
3272 | 0 | free(buffer_cpy); |
3273 | | |
3274 | 3.13k | if (!line) { |
3275 | 0 | cli_errmsg("Empty database file\n"); |
3276 | 0 | return CL_EMALFDB; |
3277 | 0 | } |
3278 | | |
3279 | 3.13k | if (ret) { |
3280 | 2.90k | cli_errmsg("Problem parsing database at line %u\n", line); |
3281 | 2.90k | return ret; |
3282 | 2.90k | } |
3283 | | |
3284 | 228 | if (signo) |
3285 | 228 | *signo += sigs; |
3286 | | |
3287 | 228 | return CL_SUCCESS; |
3288 | 3.13k | } |
3289 | | |
3290 | | /*convert the ascii sha1 in 'token' to binary and store in |
3291 | | * hashDest. |
3292 | | */ |
3293 | | static cl_error_t set_sha1(const char *const token, uint8_t hashDest[SHA1_HASH_SIZE], |
3294 | | const char *const varname, uint32_t line) |
3295 | 5.32k | { |
3296 | | |
3297 | 5.32k | cl_error_t ret = CL_SUCCESS; |
3298 | 5.32k | uint8_t hash[SHA1_HASH_SIZE] = {0}; |
3299 | | |
3300 | 5.32k | if ((2 * SHA1_HASH_SIZE) != strlen(token)) { |
3301 | 62 | cli_errmsg("cli_loadcrt: line %u: %s is not the appropriate length for a SHA1 Hash\n", (unsigned int)line, varname); |
3302 | 62 | ret = CL_EMALFDB; |
3303 | 62 | goto done; |
3304 | 62 | } |
3305 | | |
3306 | 5.26k | if (0 > cli_hex2str_to(token, (char *)hash, strlen(token))) { |
3307 | 5 | cli_errmsg("cli_loadcrt: line %u: Cannot convert %s to binary string\n", (unsigned int)line, varname); |
3308 | 5 | ret = CL_EMALFDB; |
3309 | 5 | goto done; |
3310 | 5 | } |
3311 | 5.25k | memcpy(hashDest, hash, SHA1_HASH_SIZE); |
3312 | | |
3313 | 5.32k | done: |
3314 | 5.32k | return ret; |
3315 | 5.25k | } |
3316 | | |
3317 | | /* |
3318 | | * name;trusted;subject;serial;pubkey;exp;codesign;timesign;certsign;notbefore;comment[;minFL[;maxFL]] |
3319 | | * Name and comment are ignored. They're just for the end user. |
3320 | | * Exponent is ignored for now and hardcoded to \x01\x00\x01. |
3321 | | */ |
3322 | 37.3k | #define CRT_TOKENS 13 |
3323 | | static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio) |
3324 | 868 | { |
3325 | 868 | char buffer[FILEBUFF]; |
3326 | 868 | char *tokens[CRT_TOKENS + 1]; |
3327 | 868 | size_t line = 0, tokens_count; |
3328 | 868 | cli_crt ca; |
3329 | 868 | int ret = CL_SUCCESS; |
3330 | | |
3331 | 868 | if (!(engine->dconf->pe & PE_CONF_CERTS)) { |
3332 | 0 | cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to DCONF configuration\n"); |
3333 | 0 | return ret; |
3334 | 0 | } |
3335 | | |
3336 | 868 | if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) { |
3337 | 0 | cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to engine options\n"); |
3338 | 0 | return ret; |
3339 | 0 | } |
3340 | | |
3341 | 868 | if (cli_crt_init(&ca) < 0) { |
3342 | 0 | cli_dbgmsg("cli_loadcrt: No mem for CA init.\n"); |
3343 | 0 | return CL_EMEM; |
3344 | 0 | } |
3345 | 868 | memset(ca.issuer, 0xca, sizeof(ca.issuer)); |
3346 | | |
3347 | 7.65k | while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) { |
3348 | 7.06k | line++; |
3349 | | |
3350 | 7.06k | if (buffer[0] == '#') |
3351 | 221 | continue; |
3352 | | |
3353 | 6.84k | cli_chomp(buffer); |
3354 | 6.84k | if (!strlen(buffer)) |
3355 | 928 | continue; |
3356 | | |
3357 | 5.91k | tokens_count = cli_strtokenize(buffer, ';', CRT_TOKENS + 1, (const char **)tokens); |
3358 | 5.91k | if (tokens_count > CRT_TOKENS || tokens_count < CRT_TOKENS - 2) { |
3359 | 79 | cli_errmsg("cli_loadcrt: line %u: Invalid number of tokens: %u\n", (unsigned int)line, (unsigned int)tokens_count); |
3360 | 79 | ret = CL_EMALFDB; |
3361 | 79 | goto done; |
3362 | 79 | } |
3363 | | |
3364 | 5.83k | if (tokens_count > CRT_TOKENS - 2) { |
3365 | 1.63k | if (!cli_isnumber(tokens[CRT_TOKENS - 2])) { |
3366 | 10 | cli_errmsg("cli_loadcrt: line %u: Invalid minimum feature level\n", (unsigned int)line); |
3367 | 10 | ret = CL_EMALFDB; |
3368 | 10 | goto done; |
3369 | 10 | } |
3370 | 1.62k | if ((unsigned int)atoi(tokens[CRT_TOKENS - 2]) > cl_retflevel()) { |
3371 | 241 | cli_dbgmsg("cli_loadcrt: Cert %s not loaded (required f-level: %u)\n", tokens[0], cl_retflevel()); |
3372 | 241 | continue; |
3373 | 241 | } |
3374 | | |
3375 | 1.38k | if (tokens_count == CRT_TOKENS) { |
3376 | 1.13k | if (!cli_isnumber(tokens[CRT_TOKENS - 1])) { |
3377 | 5 | cli_errmsg("cli_loadcrt: line %u: Invalid maximum feature level\n", (unsigned int)line); |
3378 | 5 | ret = CL_EMALFDB; |
3379 | 5 | goto done; |
3380 | 5 | } |
3381 | | |
3382 | 1.13k | if ((unsigned int)atoi(tokens[CRT_TOKENS - 1]) < cl_retflevel()) { |
3383 | 901 | cli_dbgmsg("cli_ladcrt: Cert %s not loaded (maximum f-level: %s)\n", tokens[0], tokens[CRT_TOKENS - 1]); |
3384 | 901 | continue; |
3385 | 901 | } |
3386 | 1.13k | } |
3387 | 1.38k | } |
3388 | | |
3389 | 4.67k | switch (tokens[1][0]) { |
3390 | 3.10k | case '1': |
3391 | 3.10k | ca.isBlocked = 0; |
3392 | 3.10k | break; |
3393 | 1.50k | case '0': |
3394 | 1.50k | ca.isBlocked = 1; |
3395 | 1.50k | break; |
3396 | 71 | default: |
3397 | 71 | cli_errmsg("cli_loadcrt: line %u: Invalid trust specification. Expected 0 or 1\n", (unsigned int)line); |
3398 | 71 | ret = CL_EMALFDB; |
3399 | 71 | goto done; |
3400 | 4.67k | } |
3401 | | |
3402 | 4.60k | if (strlen(tokens[3])) { |
3403 | 743 | ret = set_sha1(tokens[3], ca.serial, "serial", line); |
3404 | 743 | if (CL_SUCCESS != ret) { |
3405 | 23 | goto done; |
3406 | 23 | } |
3407 | 3.86k | } else { |
3408 | 3.86k | ca.ignore_serial = 1; |
3409 | 3.86k | memset(ca.serial, 0xca, sizeof(ca.serial)); |
3410 | 3.86k | } |
3411 | | |
3412 | 4.58k | if (engine->engine_options & ENGINE_OPTIONS_PE_DUMPCERTS) { |
3413 | 0 | cli_dbgmsg("cli_loadcrt: subject: %s\n", tokens[2]); |
3414 | 0 | cli_dbgmsg("cli_loadcrt: public key: %s\n", tokens[4]); |
3415 | 0 | } |
3416 | | |
3417 | 4.58k | ret = set_sha1(tokens[2], ca.subject, "subject", line); |
3418 | 4.58k | if (CL_SUCCESS != ret) { |
3419 | 44 | goto done; |
3420 | 44 | } |
3421 | | |
3422 | 4.53k | if (BN_hex2bn(&ca.n, tokens[4]) == 0) { |
3423 | 5 | cli_errmsg("cli_loadcrt: line %u: Cannot convert public key to binary string\n", (unsigned int)line); |
3424 | 5 | ret = CL_EMALFDB; |
3425 | 5 | goto done; |
3426 | 5 | } |
3427 | | /* Set the RSA exponent of 65537 */ |
3428 | 4.53k | if (!BN_set_word(ca.e, 65537)) { |
3429 | 0 | cli_errmsg("cli_loadcrt: Cannot set the exponent.\n"); |
3430 | 0 | goto done; |
3431 | 0 | } |
3432 | | |
3433 | 4.53k | switch (tokens[6][0]) { |
3434 | 1.84k | case '1': |
3435 | 1.84k | ca.codeSign = 1; |
3436 | 1.84k | break; |
3437 | 2.67k | case '0': |
3438 | 2.67k | ca.codeSign = 0; |
3439 | 2.67k | break; |
3440 | 12 | default: |
3441 | 12 | cli_errmsg("cli_loadcrt: line %u: Invalid code sign specification. Expected 0 or 1\n", (unsigned int)line); |
3442 | 12 | ret = CL_EMALFDB; |
3443 | 12 | goto done; |
3444 | 4.53k | } |
3445 | | |
3446 | 4.52k | switch (tokens[7][0]) { |
3447 | 3.48k | case '1': |
3448 | 3.48k | ca.timeSign = 1; |
3449 | 3.48k | break; |
3450 | 1.02k | case '0': |
3451 | 1.02k | ca.timeSign = 0; |
3452 | 1.02k | break; |
3453 | 13 | default: |
3454 | 13 | cli_errmsg("cli_loadcrt: line %u: Invalid time sign specification. Expected 0 or 1\n", (unsigned int)line); |
3455 | 13 | ret = CL_EMALFDB; |
3456 | 13 | goto done; |
3457 | 4.52k | } |
3458 | | |
3459 | 4.50k | switch (tokens[8][0]) { |
3460 | 3.19k | case '1': |
3461 | 3.19k | ca.certSign = 1; |
3462 | 3.19k | break; |
3463 | 1.29k | case '0': |
3464 | 1.29k | ca.certSign = 0; |
3465 | 1.29k | break; |
3466 | 13 | default: |
3467 | 13 | cli_errmsg("cli_loadcrt: line %u: Invalid cert sign specification. Expected 0 or 1\n", (unsigned int)line); |
3468 | 13 | ret = CL_EMALFDB; |
3469 | 13 | goto done; |
3470 | 4.50k | } |
3471 | | |
3472 | 4.49k | if (strlen(tokens[0])) |
3473 | 489 | ca.name = tokens[0]; |
3474 | 4.00k | else |
3475 | 4.00k | ca.name = NULL; |
3476 | | |
3477 | 4.49k | if (strlen(tokens[9])) |
3478 | 447 | ca.not_before = atoi(tokens[8]); |
3479 | 4.49k | ca.not_after = (-1U) >> 1; |
3480 | | |
3481 | 4.49k | ca.hashtype = CLI_HASHTYPE_ANY; |
3482 | 4.49k | crtmgr_add(&(engine->cmgr), &ca); |
3483 | 4.49k | } |
3484 | | |
3485 | 868 | done: |
3486 | 868 | cli_dbgmsg("Number of certs: %d\n", engine->cmgr.items); |
3487 | 868 | cli_crt_clear(&ca); |
3488 | 868 | return ret; |
3489 | 868 | } |
3490 | | |
3491 | | static int cli_loadmscat(FILE *fs, const char *dbname, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio) |
3492 | 0 | { |
3493 | 0 | fmap_t *map; |
3494 | |
|
3495 | 0 | UNUSEDPARAM(options); |
3496 | 0 | UNUSEDPARAM(dbio); |
3497 | | |
3498 | | /* If loading in signatures stored in .cat files is disabled, then skip. |
3499 | | * If Authenticoded signature parsing in general is disabled, then also |
3500 | | * skip. */ |
3501 | 0 | if (!(engine->dconf->pe & PE_CONF_CATALOG) || !(engine->dconf->pe & PE_CONF_CERTS)) { |
3502 | 0 | cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to DCONF configuration\n"); |
3503 | 0 | return 0; |
3504 | 0 | } |
3505 | | |
3506 | 0 | if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) { |
3507 | 0 | cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to engine options\n"); |
3508 | 0 | return 0; |
3509 | 0 | } |
3510 | | |
3511 | 0 | if (!(map = fmap(fileno(fs), 0, 0, dbname))) { |
3512 | 0 | cli_dbgmsg("Can't map cat: %s\n", dbname); |
3513 | 0 | return 0; |
3514 | 0 | } |
3515 | | |
3516 | 0 | if (asn1_load_mscat(map, engine)) { |
3517 | 0 | cli_dbgmsg("Failed to load certificates from cat: %s\n", dbname); |
3518 | 0 | } |
3519 | |
|
3520 | 0 | funmap(map); |
3521 | 0 | return 0; |
3522 | 0 | } |
3523 | | |
3524 | | static int cli_loadopenioc(FILE *fs, const char *dbname, struct cl_engine *engine, unsigned int options) |
3525 | 0 | { |
3526 | 0 | int rc; |
3527 | 0 | rc = openioc_parse(dbname, fileno(fs), engine, options); |
3528 | 0 | if (rc != CL_SUCCESS) |
3529 | 0 | return CL_EMALFDB; |
3530 | 0 | return rc; |
3531 | 0 | } |
3532 | | |
3533 | | #ifdef HAVE_YARA |
3534 | | #define YARA_DEBUG 1 |
3535 | | #if (YARA_DEBUG == 2) |
3536 | | #define cli_yaramsg(...) cli_errmsg(__VA_ARGS__) |
3537 | | #elif (YARA_DEBUG == 1) |
3538 | 1.21M | #define cli_yaramsg(...) cli_dbgmsg(__VA_ARGS__) |
3539 | | #else |
3540 | | #define cli_yaramsg(...) |
3541 | | #endif |
3542 | | |
3543 | | static char *parse_yara_hex_string(YR_STRING *string, int *ret); |
3544 | | |
3545 | | static char *parse_yara_hex_string(YR_STRING *string, int *ret) |
3546 | 23.7k | { |
3547 | 23.7k | char *res, *str, *ovr; |
3548 | 23.7k | size_t slen, reslen = 0, i, j; |
3549 | | |
3550 | 23.7k | if (!(string) || !(string->string)) { |
3551 | 0 | if (ret) *ret = CL_ENULLARG; |
3552 | 0 | return NULL; |
3553 | 0 | } |
3554 | | |
3555 | 23.7k | if (!STRING_IS_HEX(string)) { |
3556 | 0 | if (ret) *ret = CL_EARG; |
3557 | 0 | return NULL; |
3558 | 0 | } |
3559 | | |
3560 | 23.7k | str = (char *)(string->string); |
3561 | | |
3562 | 23.7k | if ((slen = string->length) == 0) { |
3563 | 0 | if (ret) *ret = CL_EARG; |
3564 | 0 | return NULL; |
3565 | 0 | } |
3566 | | |
3567 | 23.7k | str = strchr(str, '{') + 1; |
3568 | | |
3569 | 14.2M | for (i = 0; i < slen - 1; i++) { |
3570 | 14.2M | switch (str[i]) { |
3571 | 127k | case ' ': |
3572 | 254k | case '\t': |
3573 | 254k | case '\r': |
3574 | 259k | case '\n': |
3575 | 283k | case '}': /* end of hex string */ |
3576 | 283k | break; |
3577 | 13.9M | default: |
3578 | 13.9M | reslen++; |
3579 | 13.9M | break; |
3580 | 14.2M | } |
3581 | 14.2M | } |
3582 | | |
3583 | 23.7k | reslen++; |
3584 | 23.7k | res = cli_calloc(reslen, 1); |
3585 | 23.7k | if (!(res)) { |
3586 | 0 | if (ret) *ret = CL_EMEM; |
3587 | 0 | return NULL; |
3588 | 0 | } |
3589 | | |
3590 | 14.2M | for (i = 0, j = 0; i < slen - 1 && j < reslen; i++) { |
3591 | 14.2M | switch (str[i]) { |
3592 | 127k | case ' ': |
3593 | 254k | case '\t': |
3594 | 254k | case '\r': |
3595 | 259k | case '\n': |
3596 | 283k | case '}': |
3597 | 283k | break; |
3598 | 152k | case '[': |
3599 | | /* unbounded range check */ |
3600 | 152k | if ((i + 2 < slen - 1) && (str[i + 1] == '-') && (str[i + 2] == ']')) { |
3601 | 4.93k | res[j++] = '*'; |
3602 | 4.93k | i += 2; |
3603 | 147k | } else { |
3604 | 147k | res[j++] = '{'; |
3605 | 147k | } |
3606 | 152k | break; |
3607 | 169k | case ']': |
3608 | 169k | res[j++] = '}'; |
3609 | 169k | break; |
3610 | 13.6M | default: |
3611 | 13.6M | res[j++] = str[i]; |
3612 | 13.6M | break; |
3613 | 14.2M | } |
3614 | 14.2M | } |
3615 | | |
3616 | | /* FIXME: removing this code because anchored bytes are not sufficiently |
3617 | | general for the purposes of yara rule to ClamAV sig conversions. |
3618 | | 1. ClamAV imposes a maximum value for the upper range limit of 32: |
3619 | | #define AC_CH_MAXDIST 32 |
3620 | | Values larger cause an error in matcher-ac.c |
3621 | | 2. If the upper range values is not present, ClamAV sets the missing |
3622 | | range value to be equal to the lower range value. This changes the |
3623 | | semantic of yara jumps. |
3624 | | */ |
3625 | | #ifdef YARA_ANCHOR_SUPPORT |
3626 | | /* backward anchor overwrite, 2 (hex chars in one byte) */ |
3627 | | if ((ovr = strchr(res, '{')) && ((ovr - res) == 2)) { |
3628 | | *ovr = '['; |
3629 | | if ((ovr = strchr(ovr, '}'))) |
3630 | | *ovr = ']'; |
3631 | | else { |
3632 | | free(res); |
3633 | | if (ret) *ret = CL_EMALFDB; |
3634 | | return NULL; |
3635 | | } |
3636 | | } |
3637 | | /* forward anchor overwrite, 2 (hex chars in one byte) +1 (NULL char) */ |
3638 | | if ((ovr = strrchr(res, '}')) && ((res + j - ovr) == 3)) { |
3639 | | *ovr = ']'; |
3640 | | if ((ovr = strrchr(res, '{'))) |
3641 | | *ovr = '['; |
3642 | | else { |
3643 | | free(res); |
3644 | | if (ret) *ret = CL_EMALFDB; |
3645 | | return NULL; |
3646 | | } |
3647 | | } |
3648 | | #else |
3649 | 23.7k | if (((ovr = strchr(res, '{')) && ((ovr - res) == 2)) || |
3650 | 23.7k | ((ovr = strrchr(res, '}')) && ((res + j - ovr) == 3))) { |
3651 | 33 | cli_errmsg("parse_yara_hex_string: Single byte subpatterns unsupported in ClamAV\n"); |
3652 | 33 | free(res); |
3653 | 33 | if (ret != NULL) |
3654 | 33 | *ret = CL_EMALFDB; |
3655 | 33 | return NULL; |
3656 | 33 | } |
3657 | 23.7k | #endif |
3658 | | |
3659 | 23.7k | if (ret) |
3660 | 23.7k | *ret = CL_SUCCESS; |
3661 | 23.7k | return res; |
3662 | 23.7k | } |
3663 | | |
3664 | | struct cli_ytable_entry { |
3665 | | char *offset; |
3666 | | char *hexstr; |
3667 | | uint8_t sigopts; |
3668 | | }; |
3669 | | |
3670 | | struct cli_ytable { |
3671 | | struct cli_ytable_entry **table; |
3672 | | int32_t tbl_cnt; |
3673 | | }; |
3674 | | |
3675 | | static int32_t ytable_lookup(const char *hexsig) |
3676 | 0 | { |
3677 | 0 | (void)hexsig; |
3678 | | /* TODO - WRITE ME! */ |
3679 | 0 | return -1; |
3680 | 0 | } |
3681 | | |
3682 | | static cl_error_t ytable_add_attrib(struct cli_ytable *ytable, const char *hexsig, const char *value, int type) |
3683 | 711k | { |
3684 | 711k | int32_t lookup; |
3685 | | |
3686 | 711k | if (!ytable || !value) |
3687 | 0 | return CL_ENULLARG; |
3688 | | |
3689 | 711k | if (!hexsig) |
3690 | 711k | lookup = ytable->tbl_cnt - 1; /* assuming to attach to current string */ |
3691 | 0 | else |
3692 | 0 | lookup = ytable_lookup(hexsig); |
3693 | | |
3694 | 711k | if (lookup < 0) { |
3695 | 0 | cli_yaramsg("ytable_add_attrib: hexsig cannot be found\n"); |
3696 | 0 | return CL_EARG; |
3697 | 0 | } |
3698 | | |
3699 | 711k | if (type) { |
3700 | | /* add to sigopts */ |
3701 | 466k | switch (*value) { |
3702 | 218k | case 'i': |
3703 | 218k | ytable->table[lookup]->sigopts |= ACPATT_OPTION_NOCASE; |
3704 | 218k | break; |
3705 | 1.06k | case 'f': |
3706 | 1.06k | ytable->table[lookup]->sigopts |= ACPATT_OPTION_FULLWORD; |
3707 | 1.06k | break; |
3708 | 1.21k | case 'w': |
3709 | 1.21k | ytable->table[lookup]->sigopts |= ACPATT_OPTION_WIDE; |
3710 | 1.21k | break; |
3711 | 245k | case 'a': |
3712 | 245k | ytable->table[lookup]->sigopts |= ACPATT_OPTION_ASCII; |
3713 | 245k | break; |
3714 | 0 | default: |
3715 | 0 | cli_yaramsg("ytable_add_attrib: invalid sigopt %02x\n", *value); |
3716 | 0 | return CL_EARG; |
3717 | 466k | } |
3718 | 466k | } else { |
3719 | | /* overwrite the previous offset */ |
3720 | 245k | if (ytable->table[lookup]->offset) |
3721 | 0 | free(ytable->table[lookup]->offset); |
3722 | | |
3723 | 245k | ytable->table[lookup]->offset = cli_strdup(value); |
3724 | | |
3725 | 245k | if (!ytable->table[lookup]->offset) { |
3726 | 0 | cli_yaramsg("ytable_add_attrib: ran out of memory for offset\n"); |
3727 | 0 | return CL_EMEM; |
3728 | 0 | } |
3729 | 245k | } |
3730 | | |
3731 | 711k | return CL_SUCCESS; |
3732 | 711k | } |
3733 | | |
3734 | | /* function is dumb - TODO - rewrite using hashtable */ |
3735 | | static int ytable_add_string(struct cli_ytable *ytable, const char *hexsig) |
3736 | 245k | { |
3737 | 245k | struct cli_ytable_entry *new; |
3738 | 245k | struct cli_ytable_entry **newtable; |
3739 | 245k | int ret; |
3740 | | |
3741 | 245k | if (!ytable || !hexsig) |
3742 | 0 | return CL_ENULLARG; |
3743 | | |
3744 | 245k | new = cli_calloc(1, sizeof(struct cli_ytable_entry)); |
3745 | 245k | if (!new) { |
3746 | 0 | cli_yaramsg("ytable_add_string: out of memory for new ytable entry\n"); |
3747 | 0 | return CL_EMEM; |
3748 | 0 | } |
3749 | | |
3750 | 245k | new->hexstr = cli_strdup(hexsig); |
3751 | 245k | if (!new->hexstr) { |
3752 | 0 | cli_yaramsg("ytable_add_string: out of memory for hexsig copy\n"); |
3753 | 0 | free(new); |
3754 | 0 | return CL_EMEM; |
3755 | 0 | } |
3756 | | |
3757 | 245k | ytable->tbl_cnt++; |
3758 | 245k | newtable = cli_realloc(ytable->table, ytable->tbl_cnt * sizeof(struct cli_ytable_entry *)); |
3759 | 245k | if (!newtable) { |
3760 | 0 | cli_yaramsg("ytable_add_string: failed to reallocate new ytable table\n"); |
3761 | 0 | free(new->hexstr); |
3762 | 0 | free(new); |
3763 | 0 | ytable->tbl_cnt--; |
3764 | 0 | return CL_EMEM; |
3765 | 0 | } |
3766 | | |
3767 | 245k | newtable[ytable->tbl_cnt - 1] = new; |
3768 | 245k | ytable->table = newtable; |
3769 | | |
3770 | 245k | if (CL_SUCCESS != (ret = ytable_add_attrib(ytable, NULL, "*", 0))) { |
3771 | 0 | cli_yaramsg("ytable_add_string: failed to add default offset\n"); |
3772 | 0 | free(new->hexstr); |
3773 | 0 | free(new); |
3774 | 0 | ytable->tbl_cnt--; |
3775 | 0 | return ret; |
3776 | 0 | } |
3777 | | |
3778 | 245k | return CL_SUCCESS; |
3779 | 245k | } |
3780 | | |
3781 | | static void ytable_delete(struct cli_ytable *ytable) |
3782 | 5.05k | { |
3783 | 5.05k | int32_t i; |
3784 | 5.05k | if (!ytable) |
3785 | 0 | return; |
3786 | | |
3787 | 5.05k | if (ytable->table) { |
3788 | 248k | for (i = 0; i < ytable->tbl_cnt; ++i) { |
3789 | 245k | free(ytable->table[i]->offset); |
3790 | 245k | free(ytable->table[i]->hexstr); |
3791 | 245k | free(ytable->table[i]); |
3792 | 245k | } |
3793 | 3.13k | free(ytable->table); |
3794 | 3.13k | } |
3795 | 5.05k | } |
3796 | | |
3797 | | /* should only operate on HEX STRINGS */ |
3798 | | static int yara_hexstr_verify(YR_STRING *string, const char *hexstr, uint32_t *lsigid, struct cl_engine *engine, unsigned int options) |
3799 | 23.7k | { |
3800 | 23.7k | int ret = CL_SUCCESS; |
3801 | | |
3802 | | /* Quick Check 1: NULL String */ |
3803 | 23.7k | if (!hexstr || !string) { |
3804 | 0 | cli_warnmsg("load_oneyara[verify]: string is empty\n"); |
3805 | 0 | return CL_ENULLARG; |
3806 | 0 | } |
3807 | | |
3808 | | /* Quick Check 2: String Too Short */ |
3809 | 23.7k | if (strlen(hexstr) / 2 < CLI_DEFAULT_AC_MINDEPTH) { |
3810 | 145 | cli_warnmsg("load_oneyara[verify]: string is too short: %s\n", string->identifier); |
3811 | 145 | return CL_EMALFDB; |
3812 | 145 | } |
3813 | | |
3814 | | /* Long Check: Attempt to load hexstr */ |
3815 | 23.5k | if (CL_SUCCESS != (ret = cli_sigopts_handler(engine->test_root, "test-hex", hexstr, 0, 0, 0, "*", lsigid, options))) { |
3816 | 1.81k | if (ret == CL_EMALFDB) { |
3817 | 1.79k | cli_warnmsg("load_oneyara[verify]: recovered from database loading error\n"); |
3818 | | /* TODO: if necessary, reset testing matcher if error occurs */ |
3819 | 1.79k | cli_warnmsg("load_oneyara[verify]: string failed test insertion: %s\n", string->identifier); |
3820 | 1.79k | } |
3821 | 1.81k | return ret; |
3822 | 1.81k | } |
3823 | | |
3824 | 21.7k | return CL_SUCCESS; |
3825 | 23.5k | } |
3826 | | |
3827 | | static unsigned int yara_total, yara_loaded, yara_malform, yara_empty, yara_complex; |
3828 | 2.15k | #define YARATARGET0 "Target:0" |
3829 | | #define YARATARGET1 "Target:1" |
3830 | | #define EPSTR "EP+0:" |
3831 | | |
3832 | | /* yara has no apparent cap on the number of strings; TODO - should we have one? */ |
3833 | | /* function base off load_oneldb */ |
3834 | | static int load_oneyara(YR_RULE *rule, int chkpua, struct cl_engine *engine, unsigned int options, unsigned int *sigs) |
3835 | 5.05k | { |
3836 | 5.05k | YR_STRING *string; |
3837 | 5.05k | struct cli_ytable ytable; |
3838 | 5.05k | size_t i; |
3839 | 5.05k | int str_error = 0, ret = CL_SUCCESS; |
3840 | 5.05k | struct cli_lsig_tdb tdb; |
3841 | 5.05k | uint32_t lsigid[2]; |
3842 | 5.05k | struct cli_matcher *root; |
3843 | 5.05k | struct cli_ac_lsig **newtable, *lsig, *tsig = NULL; |
3844 | 5.05k | char *logic = NULL, *target_str = NULL; |
3845 | 5.05k | char *newident = NULL; |
3846 | | /* size_t lsize; */ // only used in commented out code |
3847 | | /* char *exp_op = "|"; */ // only used in commented out code |
3848 | | |
3849 | 5.05k | cli_yaramsg("load_oneyara: attempting to load %s\n", rule->identifier); |
3850 | | |
3851 | 5.05k | if (!rule) { |
3852 | 0 | cli_errmsg("load_oneyara: empty rule passed as argument\n"); |
3853 | 0 | return CL_ENULLARG; |
3854 | 0 | } |
3855 | | |
3856 | | /* PUA and IGN checks */ |
3857 | 5.05k | if (chkpua && cli_chkpua(rule->identifier, engine->pua_cats, options)) |
3858 | 0 | return CL_SUCCESS; |
3859 | | |
3860 | 5.05k | if (engine->ignored && cli_chkign(engine->ignored, rule->identifier, rule->identifier)) { |
3861 | 0 | return CL_SUCCESS; |
3862 | 0 | } |
3863 | | |
3864 | 5.05k | newident = cli_malloc(strlen(rule->identifier) + 5 + 1); |
3865 | 5.05k | if (!newident) { |
3866 | 0 | cli_errmsg("cli_loadyara(): newident == NULL\n"); |
3867 | 0 | return CL_EMEM; |
3868 | 0 | } |
3869 | | |
3870 | 5.05k | snprintf(newident, strlen(rule->identifier) + 5 + 1, "YARA.%s", rule->identifier); |
3871 | | |
3872 | 5.05k | if (engine->cb_sigload && engine->cb_sigload("yara", newident, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
3873 | 0 | cli_dbgmsg("cli_loadyara: skipping %s due to callback\n", newident); |
3874 | 0 | free(newident); |
3875 | 0 | (*sigs)--; |
3876 | 0 | return CL_SUCCESS; |
3877 | 0 | } |
3878 | | |
3879 | 5.05k | memset(&ytable, 0, sizeof(ytable)); |
3880 | | |
3881 | | /*** rule specific checks ***/ |
3882 | | #ifdef YARA_FINISHED |
3883 | | if (RULE_IS_PRIVATE(rule)) { |
3884 | | cli_warnmsg("load_oneyara: private modifier for yara rule is unsupported\n"); |
3885 | | cli_yaramsg("RULE_IS_PRIVATE yes\n"); |
3886 | | } |
3887 | | if (RULE_IS_GLOBAL(rule)) { |
3888 | | cli_warnmsg("load_oneyara: global modifier for yara rule is unsupported\n"); |
3889 | | cli_yaramsg("RULE_IS_GLOBAL yes\n"); |
3890 | | } |
3891 | | if ((rule->g_flags) & RULE_GFLAGS_REQUIRE_FILE) { |
3892 | | cli_warnmsg("load_oneyara: RULE_GFLAGS_REQUIRE_FILE for yara rule is unsupported\n"); |
3893 | | cli_yaramsg("RULE_GFLAGS_REQUIRE_FILE yes\n"); |
3894 | | } |
3895 | | |
3896 | | if (RULE_IS_NULL(rule) || ((rule->g_flags) & RULE_GFLAGS_REQUIRE_EXECUTABLE)) { |
3897 | | |
3898 | | cli_warnmsg("load_oneyara: skipping %s due to unsupported rule gflags\n", newident); |
3899 | | |
3900 | | cli_yaramsg("RULE_IS_NULL %s\n", RULE_IS_NULL(rule) ? "yes" : "no"); |
3901 | | cli_yaramsg("RULE_GFLAGS_REQUIRE_EXECUTABLE %s\n", ((rule->g_flags) & RULE_GFLAGS_REQUIRE_EXECUTABLE) ? "yes" : "no"); |
3902 | | |
3903 | | free(newident); |
3904 | | (*sigs)--; |
3905 | | return CL_SUCCESS; |
3906 | | } |
3907 | | #else |
3908 | | /* |
3909 | | cli_warnmsg("load_oneyara: yara support is incomplete, rule flags are ignored\n"); |
3910 | | |
3911 | | if (RULE_IS_PRIVATE(rule)) |
3912 | | cli_yaramsg("RULE_IS_PRIVATE yes\n"); |
3913 | | if (RULE_IS_GLOBAL(rule)) |
3914 | | cli_yaramsg("RULE_IS_GLOBAL yes\n"); |
3915 | | if (RULE_IS_NULL(rule)) |
3916 | | cli_yaramsg("RULE_IS_NULL yes\n"); |
3917 | | if ((rule->g_flags) & RULE_GFLAGS_REQUIRE_FILE) |
3918 | | cli_yaramsg("RULE_GFLAGS_REQUIRE_FILE yes\n"); |
3919 | | if ((rule->g_flags) & RULE_GFLAGS_REQUIRE_EXECUTABLE) |
3920 | | cli_yaramsg("RULE_GFLAGS_REQUIRE_EXECUTABLE yes\n"); |
3921 | | */ |
3922 | 5.05k | #endif |
3923 | | |
3924 | | /*** verification step - can clamav load it? ***/ |
3925 | | /*** initial population pass for the strings table ***/ |
3926 | 5.05k | STAILQ_FOREACH(string, &rule->strings, link) |
3927 | 248k | { |
3928 | 248k | char *substr = NULL; |
3929 | | |
3930 | | /* string type handler */ |
3931 | 248k | if (STRING_IS_NULL(string)) { |
3932 | 0 | cli_warnmsg("load_oneyara: skipping NULL string %s\n", newident); |
3933 | | // str_error++; /* kill the insertion? */ |
3934 | 0 | continue; |
3935 | | #ifdef YARA_FINISHED |
3936 | | } else if (STRING_IS_LITERAL(string)) { |
3937 | | /* TODO - handle literal strings, short-circuits other string type handling */ |
3938 | | cli_yaramsg("load_oneyara: literal string: [%.*s] => [%s]\n", string->length, string->string, substr); |
3939 | | #else |
3940 | 248k | } else if (STRING_IS_LITERAL(string)) { |
3941 | 0 | cli_errmsg("load_oneyara: literal strings are unsupported, reorganize existing code\n"); |
3942 | 0 | #endif |
3943 | 248k | } else if (STRING_IS_HEX(string)) { |
3944 | 23.7k | substr = parse_yara_hex_string(string, &ret); |
3945 | 23.7k | if (ret != CL_SUCCESS) { |
3946 | 33 | cli_errmsg("load_oneyara: error in parsing yara hex string\n"); |
3947 | 33 | str_error++; |
3948 | 33 | break; |
3949 | 33 | } |
3950 | | |
3951 | | /* handle lack of hexstr support here in order to suppress */ |
3952 | | /* initialize testing matcher */ |
3953 | 23.7k | if (!engine->test_root) { |
3954 | 2.31k | engine->test_root = (struct cli_matcher *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_matcher)); |
3955 | 2.31k | if (!engine->test_root) { |
3956 | 0 | cli_errmsg("load_oneyara[verify]: cannot allocate memory for test cli_matcher\n"); |
3957 | 0 | free(substr); |
3958 | 0 | free(newident); |
3959 | 0 | return CL_EMEM; |
3960 | 0 | } |
3961 | | #ifdef USE_MPOOL |
3962 | | engine->test_root->mempool = engine->mempool; |
3963 | | #endif |
3964 | 2.31k | if (CL_SUCCESS != (ret = cli_ac_init(engine->test_root, engine->ac_mindepth, engine->ac_maxdepth, engine->dconf->other & OTHER_CONF_PREFILTERING))) { |
3965 | 0 | cli_errmsg("load_oneyara: cannot initialize test ac root\n"); |
3966 | 0 | free(substr); |
3967 | 0 | free(newident); |
3968 | 0 | return ret; |
3969 | 0 | } |
3970 | 2.31k | } |
3971 | | |
3972 | | /* generate a test lsig if one does not exist */ |
3973 | 23.7k | if (!tsig) { |
3974 | | /*** populating lsig ***/ |
3975 | 3.06k | tsig = (struct cli_ac_lsig *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_ac_lsig)); |
3976 | 3.06k | if (!tsig) { |
3977 | 0 | cli_errmsg("load_oneyara: cannot allocate memory for test lsig\n"); |
3978 | 0 | free(substr); |
3979 | 0 | free(newident); |
3980 | 0 | return CL_EMEM; |
3981 | 0 | } |
3982 | | |
3983 | 3.06k | root = engine->test_root; |
3984 | | |
3985 | 3.06k | tsig->type = CLI_YARA_NORMAL; |
3986 | 3.06k | lsigid[0] = tsig->id = root->ac_lsigs; |
3987 | | |
3988 | | /* For logical subsignatures, only store the virname in the lsigtable entry. */ |
3989 | 3.06k | tsig->virname = CLI_MPOOL_VIRNAME(engine->mempool, newident, options & CL_DB_OFFICIAL); |
3990 | 3.06k | if (NULL == tsig->virname) { |
3991 | 0 | root->ac_lsigs--; |
3992 | 0 | cli_errmsg("load_oneyara: failed to allocate signature name for yara test lsig\n"); |
3993 | 0 | MPOOL_FREE(engine->mempool, tsig); |
3994 | 0 | free(substr); |
3995 | 0 | free(newident); |
3996 | 0 | return CL_EMEM; |
3997 | 0 | } |
3998 | | |
3999 | 3.06k | root->ac_lsigs++; |
4000 | 3.06k | newtable = (struct cli_ac_lsig **)MPOOL_REALLOC(engine->mempool, root->ac_lsigtable, root->ac_lsigs * sizeof(struct cli_ac_lsig *)); |
4001 | 3.06k | if (!newtable) { |
4002 | 0 | root->ac_lsigs--; |
4003 | 0 | cli_errmsg("load_oneyara: cannot allocate test root->ac_lsigtable\n"); |
4004 | 0 | MPOOL_FREE(engine->mempool, tsig->virname); |
4005 | 0 | MPOOL_FREE(engine->mempool, tsig); |
4006 | 0 | free(substr); |
4007 | 0 | free(newident); |
4008 | 0 | return CL_EMEM; |
4009 | 0 | } |
4010 | | |
4011 | 3.06k | newtable[root->ac_lsigs - 1] = tsig; |
4012 | 3.06k | root->ac_lsigtable = newtable; |
4013 | 3.06k | } |
4014 | | |
4015 | | /* attempt to insert hexsig */ |
4016 | 23.7k | lsigid[1] = 0; |
4017 | 23.7k | ret = yara_hexstr_verify(string, substr, lsigid, engine, options); |
4018 | 23.7k | if (ret != CL_SUCCESS) { |
4019 | 1.96k | str_error++; |
4020 | 1.96k | free(substr); |
4021 | 1.96k | break; |
4022 | 1.96k | } |
4023 | | |
4024 | 21.7k | cli_yaramsg("load_oneyara: hex string: [%.*s] => [%s]\n", string->length, string->string, substr); |
4025 | | |
4026 | 21.7k | ytable_add_string(&ytable, substr); |
4027 | 21.7k | free(substr); |
4028 | 224k | } else if (STRING_IS_REGEXP(string)) { |
4029 | | /* TODO - rewrite to NOT use PCRE_BYPASS */ |
4030 | 672 | #if HAVE_PCRE |
4031 | 672 | size_t length = strlen(PCRE_BYPASS) + string->length + 3; |
4032 | | |
4033 | 672 | substr = cli_calloc(length, sizeof(char)); |
4034 | 672 | if (!substr) { |
4035 | 0 | cli_errmsg("load_oneyara: cannot allocate memory for converted regex string\n"); |
4036 | 0 | str_error++; |
4037 | 0 | ret = CL_EMEM; |
4038 | 0 | break; |
4039 | 0 | } |
4040 | | |
4041 | 672 | snprintf(substr, length, "%s/%.*s/", PCRE_BYPASS, string->length, string->string); |
4042 | | |
4043 | 672 | cli_yaramsg("load_oneyara: regex string: [%.*s] => [%s]\n", string->length, string->string, substr); |
4044 | | |
4045 | 672 | ytable_add_string(&ytable, substr); |
4046 | 672 | free(substr); |
4047 | | #else |
4048 | | cli_warnmsg("cli_loadyara: %s uses PCREs but support is disabled\n", newident); |
4049 | | str_error++; |
4050 | | ret = CL_SUCCESS; |
4051 | | break; |
4052 | | #endif |
4053 | 223k | } else { |
4054 | | /* TODO - extract the string length to handle NULL hex-escaped characters |
4055 | | * For now, we'll just use the strlen we get which crudely finds the length |
4056 | | */ |
4057 | 223k | size_t length = string->length; |
4058 | 223k | size_t totsize = 2 * length + 1; |
4059 | | |
4060 | 223k | if (length < CLI_DEFAULT_AC_MINDEPTH) { |
4061 | 354 | cli_warnmsg("load_oneyara: string is too short %s\n", newident); |
4062 | 354 | str_error++; |
4063 | 354 | continue; |
4064 | 354 | } |
4065 | | |
4066 | 223k | substr = cli_calloc(totsize, sizeof(char)); |
4067 | 223k | if (!substr) { |
4068 | 0 | cli_errmsg("load_oneyara: cannot allocate memory for converted generic string\n"); |
4069 | 0 | str_error++; |
4070 | 0 | ret = CL_EMEM; |
4071 | 0 | break; |
4072 | 0 | } |
4073 | | |
4074 | 3.06M | for (i = 0; i < length; ++i) { |
4075 | 2.83M | size_t len = strlen(substr); |
4076 | 2.83M | snprintf(substr + len, totsize - len, "%02x", string->string[i]); |
4077 | 2.83M | } |
4078 | | |
4079 | 223k | cli_yaramsg("load_oneyara: generic string: [%.*s] => [%s]\n", string->length, string->string, substr); |
4080 | | |
4081 | 223k | ytable_add_string(&ytable, substr); |
4082 | 223k | free(substr); |
4083 | 223k | } |
4084 | | |
4085 | | /* modifier handler */ |
4086 | 245k | if (STRING_IS_NO_CASE(string)) { |
4087 | 218k | cli_yaramsg("STRING_IS_NO_CASE %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4088 | 218k | if (CL_SUCCESS != (ret = ytable_add_attrib(&ytable, NULL, "i", 1))) { |
4089 | 0 | cli_warnmsg("load_oneyara: failed to add 'nocase' sigopt\n"); |
4090 | 0 | str_error++; |
4091 | 0 | break; |
4092 | 0 | } |
4093 | 218k | } |
4094 | 245k | if (STRING_IS_ASCII(string)) { |
4095 | 245k | cli_yaramsg("STRING_IS_ASCII %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4096 | 245k | if (CL_SUCCESS != (ret = ytable_add_attrib(&ytable, NULL, "a", 1))) { |
4097 | 0 | cli_warnmsg("load_oneyara: failed to add 'ascii' sigopt\n"); |
4098 | 0 | str_error++; |
4099 | 0 | break; |
4100 | 0 | } |
4101 | 245k | } |
4102 | 245k | if (STRING_IS_WIDE(string)) { |
4103 | 1.22k | cli_yaramsg("STRING_IS_WIDE %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4104 | | /* handle lack of 'wide' support for regex here in order to suppress */ |
4105 | 1.22k | if (STRING_IS_REGEXP(string)) { |
4106 | 12 | cli_warnmsg("load_oneyara[verify]: wide modifier [w] is not supported for regex subsigs\n"); |
4107 | 12 | str_error++; |
4108 | 12 | break; |
4109 | 12 | } |
4110 | 1.21k | if (CL_SUCCESS != (ret = ytable_add_attrib(&ytable, NULL, "w", 1))) { |
4111 | 0 | cli_warnmsg("load_oneyara: failed to add 'wide' sigopt\n"); |
4112 | 0 | str_error++; |
4113 | 0 | break; |
4114 | 0 | } |
4115 | 1.21k | } |
4116 | 245k | if (STRING_IS_FULL_WORD(string)) { |
4117 | 1.06k | cli_yaramsg("STRING_IS_FULL_WORD %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4118 | 1.06k | if (CL_SUCCESS != (ret = ytable_add_attrib(&ytable, NULL, "f", 1))) { |
4119 | 0 | cli_warnmsg("load_oneyara: failed to add 'fullword' sigopt\n"); |
4120 | 0 | str_error++; |
4121 | 0 | break; |
4122 | 0 | } |
4123 | 1.06k | } |
4124 | | |
4125 | | #ifdef YARA_FINISHED |
4126 | | /* special modifier handler */ |
4127 | | if (STRING_IS_ANONYMOUS(string)) |
4128 | | cli_yaramsg("STRING_IS_ANONYMOUS %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4129 | | |
4130 | | /* unsupported(?) modifier handler */ |
4131 | | if (STRING_IS_SINGLE_MATCH(string)) |
4132 | | cli_yaramsg("STRING_IS_SINGLE_MATCH %s\n", STRING_IS_SINGLE_MATCH(string) ? "yes" : "no"); |
4133 | | |
4134 | | if (STRING_IS_REFERENCED(string) || STRING_IS_FAST_HEX_REGEXP(string) || STRING_IS_CHAIN_PART(string) || |
4135 | | STRING_IS_CHAIN_TAIL(string) || STRING_FITS_IN_ATOM(string)) { |
4136 | | |
4137 | | cli_warnmsg("load_oneyara: skipping unsupported string %s\n", newident); |
4138 | | |
4139 | | cli_yaramsg("STRING_IS_REFERENCED %s\n", STRING_IS_REFERENCED(string) ? "yes" : "no"); |
4140 | | cli_yaramsg("STRING_IS_FAST_HEX_REGEXP %s\n", STRING_IS_FAST_HEX_REGEXP(string) ? "yes" : "no"); |
4141 | | cli_yaramsg("STRING_IS_CHAIN_PART %s\n", STRING_IS_CHAIN_PART(string) ? "yes" : "no"); |
4142 | | cli_yaramsg("STRING_IS_CHAIN_TAIL %s\n", STRING_IS_CHAIN_TAIL(string) ? "yes" : "no"); |
4143 | | cli_yaramsg("STRING_FITS_IN_ATOM %s\n", STRING_FITS_IN_ATOM(string) ? "yes" : "no"); |
4144 | | |
4145 | | str_error++; |
4146 | | continue; |
4147 | | } |
4148 | | #else |
4149 | | /* |
4150 | | cli_warnmsg("load_oneyara: yara support is incomplete, rule flags are ignored\n"); |
4151 | | if (STRING_IS_ANONYMOUS(string)) |
4152 | | cli_yaramsg("STRING_IS_ANONYMOUS yes\n"); |
4153 | | if (STRING_IS_SINGLE_MATCH(string)) |
4154 | | cli_yaramsg("STRING_IS_SINGLE_MATCH yes\n"); |
4155 | | if (STRING_IS_REFERENCED(string)) |
4156 | | cli_yaramsg("STRING_IS_REFERENCED yes\n"); |
4157 | | if (STRING_IS_FAST_HEX_REGEXP(string)) |
4158 | | cli_yaramsg("STRING_IS_FAST_HEX_REGEXP yes\n"); |
4159 | | if (STRING_IS_CHAIN_PART(string)) |
4160 | | cli_yaramsg("STRING_IS_CHAIN_PART yes\n"); |
4161 | | if (STRING_IS_CHAIN_TAIL(string)) |
4162 | | cli_yaramsg("STRING_IS_CHAIN_TAIL yes\n"); |
4163 | | if (STRING_FITS_IN_ATOM(string)) |
4164 | | cli_yaramsg("STRING_FITS_IN_ATOM yes\n"); |
4165 | | */ |
4166 | 245k | #endif |
4167 | 245k | string->subsig_id = ytable.tbl_cnt - 1; |
4168 | 245k | } |
4169 | | |
4170 | 5.05k | if (str_error > 0) { |
4171 | 2.02k | cli_warnmsg("load_oneyara: clamav cannot support %d input strings, skipping %s\n", str_error, newident); |
4172 | 2.02k | yara_malform++; |
4173 | 2.02k | ytable_delete(&ytable); |
4174 | 2.02k | free(newident); |
4175 | 2.02k | (*sigs)--; |
4176 | 2.02k | return ret; |
4177 | 3.03k | } else if (ytable.tbl_cnt == 0) { |
4178 | 815 | cli_warnmsg("load_oneyara: yara rule contains no supported strings, skipping %s\n", newident); |
4179 | 815 | yara_malform++; |
4180 | 815 | ytable_delete(&ytable); |
4181 | 815 | free(newident); |
4182 | 815 | (*sigs)--; |
4183 | 815 | return CL_SUCCESS; /* TODO - kill signature instead? */ |
4184 | 2.21k | } else if (ytable.tbl_cnt > MAX_LDB_SUBSIGS) { |
4185 | 63 | cli_warnmsg("load_oneyara: yara rule contains too many subsigs (%d, max: %d), skipping %s\n", ytable.tbl_cnt, MAX_LDB_SUBSIGS, newident); |
4186 | 63 | yara_malform++; |
4187 | 63 | ytable_delete(&ytable); |
4188 | 63 | free(newident); |
4189 | 63 | (*sigs)--; |
4190 | 63 | return CL_SUCCESS; |
4191 | 63 | } |
4192 | | |
4193 | | /*** conditional verification step (ex. do we define too many strings versus used?) ***/ |
4194 | | /*** additional string table population (ex. offsets), second translation table pass ***/ |
4195 | | #if 0 |
4196 | | if (rule->cl_flags & RULE_ALL || rule->cl_flags & RULE_ANY) { |
4197 | | lsize = 3*ytable.tbl_cnt; |
4198 | | logic = cli_calloc(lsize, sizeof(char)); |
4199 | | if (!logic) { |
4200 | | cli_errmsg("load_oneyara: cannot allocate memory for logic statement\n"); |
4201 | | ytable_delete(&ytable); |
4202 | | return CL_EMEM; |
4203 | | } |
4204 | | |
4205 | | if (rule->cl_flags & RULE_ALL && rule->cl_flags & RULE_THEM) |
4206 | | exp_op = "&"; |
4207 | | else { |
4208 | | exp_op = "|"; |
4209 | | if ((!(rule->cl_flags & RULE_ANY && rule->cl_flags & RULE_THEM) && ytable.tbl_cnt > 1) && |
4210 | | !(rule->cl_flags & RULE_EP && ytable.tbl_cnt == 1)) |
4211 | | yara_complex++; |
4212 | | } |
4213 | | |
4214 | | for (i=0; i<ytable.tbl_cnt; i++) { |
4215 | | size_t len=strlen(logic); |
4216 | | snprintf(logic+len, lsize-len, "%u%s", i, (i+1 == ytable.tbl_cnt) ? "" : exp_op); |
4217 | | } |
4218 | | |
4219 | | /*** END CONDITIONAL HANDLING ***/ |
4220 | | } |
4221 | | |
4222 | | /* TDB */ |
4223 | | if (rule->cl_flags & RULE_EP && ytable.tbl_cnt == 1) |
4224 | | target_str = cli_strdup(YARATARGET1); |
4225 | | else |
4226 | | #endif |
4227 | 2.15k | target_str = cli_strdup(YARATARGET0); |
4228 | | |
4229 | 2.15k | memset(&tdb, 0, sizeof(tdb)); |
4230 | 2.15k | if (CL_SUCCESS != (ret = init_tdb(&tdb, engine, target_str, newident))) { |
4231 | 0 | ytable_delete(&ytable); |
4232 | 0 | free(logic); |
4233 | 0 | free(target_str); |
4234 | 0 | free(newident); |
4235 | 0 | (*sigs)--; |
4236 | 0 | if (ret == CL_BREAK) |
4237 | 0 | return CL_SUCCESS; |
4238 | 0 | return ret; |
4239 | 0 | } |
4240 | 2.15k | free(target_str); |
4241 | | |
4242 | | /*** populating lsig ***/ |
4243 | 2.15k | root = engine->root[tdb.target[0]]; |
4244 | | |
4245 | 2.15k | lsig = (struct cli_ac_lsig *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_ac_lsig)); |
4246 | 2.15k | if (!lsig) { |
4247 | 0 | cli_errmsg("load_oneyara: Can't allocate memory for lsig\n"); |
4248 | 0 | FREE_TDB(tdb); |
4249 | 0 | ytable_delete(&ytable); |
4250 | 0 | free(logic); |
4251 | 0 | free(newident); |
4252 | 0 | return CL_EMEM; |
4253 | 0 | } |
4254 | | |
4255 | 2.15k | if (logic) { |
4256 | 0 | cli_yaramsg("normal lsig triggered yara: %s\n", logic); |
4257 | |
|
4258 | 0 | lsig->type = CLI_LSIG_NORMAL; |
4259 | 0 | lsig->u.logic = CLI_MPOOL_STRDUP(engine->mempool, logic); |
4260 | 0 | free(logic); |
4261 | 0 | if (!lsig->u.logic) { |
4262 | 0 | cli_errmsg("load_oneyara: Can't allocate memory for lsig->logic\n"); |
4263 | 0 | FREE_TDB(tdb); |
4264 | 0 | ytable_delete(&ytable); |
4265 | 0 | MPOOL_FREE(engine->mempool, lsig); |
4266 | 0 | free(newident); |
4267 | 0 | return CL_EMEM; |
4268 | 0 | } |
4269 | 2.15k | } else { |
4270 | 2.15k | if (NULL != (lsig->u.code_start = rule->code_start)) { |
4271 | 2.15k | lsig->type = (rule->cl_flags & RULE_OFFSETS) ? CLI_YARA_OFFSET : CLI_YARA_NORMAL; |
4272 | 2.15k | if (RULE_IS_PRIVATE(rule)) |
4273 | 3 | lsig->flag |= CLI_LSIG_FLAG_PRIVATE; |
4274 | 2.15k | } else { |
4275 | 0 | cli_errmsg("load_oneyara: code start is NULL\n"); |
4276 | 0 | FREE_TDB(tdb); |
4277 | 0 | ytable_delete(&ytable); |
4278 | 0 | MPOOL_FREE(engine->mempool, lsig); |
4279 | 0 | free(newident); |
4280 | 0 | return CL_EMEM; |
4281 | 0 | } |
4282 | 2.15k | } |
4283 | | |
4284 | | /* For logical subsignatures, only store the virname in the lsigtable entry. */ |
4285 | 2.15k | lsig->virname = CLI_MPOOL_VIRNAME(engine->mempool, newident, options & CL_DB_OFFICIAL); |
4286 | 2.15k | if (NULL == lsig->virname) { |
4287 | 0 | cli_errmsg("load_oneyara: failed to allocate signature name for yara lsig\n"); |
4288 | 0 | FREE_TDB(tdb); |
4289 | 0 | ytable_delete(&ytable); |
4290 | 0 | MPOOL_FREE(engine->mempool, lsig); |
4291 | 0 | free(newident); |
4292 | 0 | return CL_EMEM; |
4293 | 0 | } |
4294 | | |
4295 | 2.15k | lsigid[0] = lsig->id = root->ac_lsigs; |
4296 | | |
4297 | 2.15k | root->ac_lsigs++; |
4298 | 2.15k | newtable = (struct cli_ac_lsig **)MPOOL_REALLOC(engine->mempool, root->ac_lsigtable, root->ac_lsigs * sizeof(struct cli_ac_lsig *)); |
4299 | 2.15k | if (!newtable) { |
4300 | 0 | root->ac_lsigs--; |
4301 | 0 | cli_errmsg("cli_loadldb: Can't realloc root->ac_lsigtable\n"); |
4302 | 0 | FREE_TDB(tdb); |
4303 | 0 | ytable_delete(&ytable); |
4304 | 0 | MPOOL_FREE(engine->mempool, lsig); |
4305 | 0 | free(newident); |
4306 | 0 | return CL_EMEM; |
4307 | 0 | } |
4308 | | |
4309 | 2.15k | newtable[root->ac_lsigs - 1] = lsig; |
4310 | 2.15k | root->ac_lsigtable = newtable; |
4311 | 2.15k | tdb.subsigs = ytable.tbl_cnt; |
4312 | | |
4313 | | /*** loading step - put things into the AC trie ***/ |
4314 | 37.1k | for (i = 0; i < (size_t)ytable.tbl_cnt; ++i) { |
4315 | 34.9k | lsigid[1] = i; |
4316 | | |
4317 | 34.9k | cli_yaramsg("%zu: [%s] [%s] [%s%s%s%s]\n", i, ytable.table[i]->hexstr, ytable.table[i]->offset, |
4318 | 34.9k | (ytable.table[i]->sigopts & ACPATT_OPTION_NOCASE) ? "i" : "", |
4319 | 34.9k | (ytable.table[i]->sigopts & ACPATT_OPTION_FULLWORD) ? "f" : "", |
4320 | 34.9k | (ytable.table[i]->sigopts & ACPATT_OPTION_WIDE) ? "w" : "", |
4321 | 34.9k | (ytable.table[i]->sigopts & ACPATT_OPTION_ASCII) ? "a" : ""); |
4322 | | |
4323 | 34.9k | ret = readdb_parse_yara_string(root, newident, ytable.table[i]->hexstr, ytable.table[i]->sigopts, |
4324 | 34.9k | ytable.table[i]->offset, lsigid, options); |
4325 | 34.9k | if (CL_SUCCESS != ret) { |
4326 | 25 | root->ac_lsigs--; |
4327 | 25 | FREE_TDB(tdb); |
4328 | 25 | ytable_delete(&ytable); |
4329 | 25 | MPOOL_FREE(engine->mempool, lsig); |
4330 | | |
4331 | 25 | yara_malform++; |
4332 | 25 | free(newident); |
4333 | 25 | return ret; |
4334 | 25 | } |
4335 | 34.9k | } |
4336 | | |
4337 | 2.13k | memcpy(&lsig->tdb, &tdb, sizeof(tdb)); |
4338 | 2.13k | ytable_delete(&ytable); |
4339 | | |
4340 | 2.13k | rule->lsigid = root->ac_lsigs - 1; |
4341 | 2.13k | yara_loaded++; |
4342 | 2.13k | cli_yaramsg("load_oneyara: successfully loaded %s\n", newident); |
4343 | 2.13k | free(newident); |
4344 | 2.13k | return CL_SUCCESS; |
4345 | 2.15k | } |
4346 | | |
4347 | | struct _yara_global { |
4348 | | YR_ARENA *the_arena; |
4349 | | YR_HASH_TABLE *rules_table; |
4350 | | YR_HASH_TABLE *objects_table; |
4351 | | YR_HASH_TABLE *db_table; |
4352 | | }; |
4353 | | |
4354 | | cl_error_t cli_yara_init(struct cl_engine *engine) |
4355 | 47.2k | { |
4356 | | /* Initialize YARA */ |
4357 | 47.2k | engine->yara_global = cli_calloc(1, sizeof(struct _yara_global)); |
4358 | 47.2k | if (NULL == engine->yara_global) { |
4359 | 0 | cli_errmsg("cli_yara_init: failed to create YARA global\n"); |
4360 | 0 | return CL_EMEM; |
4361 | 0 | } |
4362 | 47.2k | if (ERROR_SUCCESS != yr_arena_create(1024, 0, &engine->yara_global->the_arena)) { |
4363 | 0 | cli_errmsg("cli_yara_init: failed to create the YARA arena\n"); |
4364 | 0 | free(engine->yara_global); |
4365 | 0 | engine->yara_global = NULL; |
4366 | 0 | return CL_EMEM; |
4367 | 0 | } |
4368 | 47.2k | if (ERROR_SUCCESS != yr_hash_table_create(10007, &engine->yara_global->rules_table)) { |
4369 | 0 | cli_errmsg("cli_yara_init: failed to create the YARA rules table\n"); |
4370 | 0 | yr_arena_destroy(engine->yara_global->the_arena); |
4371 | 0 | engine->yara_global->the_arena = NULL; |
4372 | 0 | free(engine->yara_global); |
4373 | 0 | engine->yara_global = NULL; |
4374 | 0 | return CL_EMEM; |
4375 | 0 | } |
4376 | 47.2k | if (ERROR_SUCCESS != yr_hash_table_create(10007, &engine->yara_global->objects_table)) { |
4377 | 0 | cli_errmsg("cli_yara_init: failed to create the YARA objects table\n"); |
4378 | 0 | yr_hash_table_destroy(engine->yara_global->rules_table, NULL); |
4379 | 0 | yr_arena_destroy(engine->yara_global->the_arena); |
4380 | 0 | engine->yara_global->rules_table = NULL; |
4381 | 0 | engine->yara_global->the_arena = NULL; |
4382 | 0 | free(engine->yara_global); |
4383 | 0 | engine->yara_global = NULL; |
4384 | 0 | engine->yara_global = NULL; |
4385 | 0 | return CL_EMEM; |
4386 | 0 | } |
4387 | 47.2k | if (ERROR_SUCCESS != yr_hash_table_create(10007, &engine->yara_global->db_table)) { |
4388 | 0 | cli_errmsg("cli_yara_init: failed to create the YARA objects table\n"); |
4389 | 0 | yr_hash_table_destroy(engine->yara_global->objects_table, NULL); |
4390 | 0 | yr_hash_table_destroy(engine->yara_global->rules_table, NULL); |
4391 | 0 | yr_arena_destroy(engine->yara_global->the_arena); |
4392 | 0 | engine->yara_global->objects_table = NULL; |
4393 | 0 | engine->yara_global->rules_table = NULL; |
4394 | 0 | engine->yara_global->the_arena = NULL; |
4395 | 0 | free(engine->yara_global); |
4396 | 0 | engine->yara_global = NULL; |
4397 | 0 | return CL_EMEM; |
4398 | 0 | } |
4399 | 47.2k | return CL_SUCCESS; |
4400 | 47.2k | } |
4401 | | |
4402 | | void cli_yara_free(struct cl_engine *engine) |
4403 | 47.1k | { |
4404 | 47.1k | if (engine->yara_global != NULL) { |
4405 | 47.1k | if (engine->yara_global->db_table != NULL) { |
4406 | 47.1k | yr_hash_table_destroy(engine->yara_global->db_table, NULL); |
4407 | 47.1k | engine->yara_global->db_table = NULL; |
4408 | 47.1k | } |
4409 | 47.1k | if (engine->yara_global->rules_table != NULL) { |
4410 | 26.5k | yr_hash_table_destroy(engine->yara_global->rules_table, NULL); |
4411 | 26.5k | engine->yara_global->rules_table = NULL; |
4412 | 26.5k | } |
4413 | 47.1k | if (engine->yara_global->objects_table != NULL) { |
4414 | 26.5k | yr_hash_table_destroy(engine->yara_global->objects_table, NULL); |
4415 | 26.5k | engine->yara_global->objects_table = NULL; |
4416 | 26.5k | } |
4417 | 47.1k | if (engine->yara_global->the_arena != NULL) { |
4418 | 47.1k | yr_arena_destroy(engine->yara_global->the_arena); |
4419 | 47.1k | engine->yara_global->the_arena = NULL; |
4420 | 47.1k | } |
4421 | 47.1k | free(engine->yara_global); |
4422 | 47.1k | engine->yara_global = NULL; |
4423 | 47.1k | } |
4424 | 47.1k | } |
4425 | | |
4426 | | // TODO - pua? dbio? |
4427 | | static int cli_loadyara(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *filename) |
4428 | 6.06k | { |
4429 | 6.06k | YR_COMPILER compiler; |
4430 | 6.06k | YR_NAMESPACE ns; |
4431 | 6.06k | YR_RULE *rule; |
4432 | 6.06k | unsigned int sigs = 0, rules = 0, rule_errors = 0; |
4433 | 6.06k | int rc; |
4434 | | |
4435 | 6.06k | UNUSEDPARAM(dbio); |
4436 | | |
4437 | 6.06k | if ((rc = cli_initroots(engine, options))) |
4438 | 0 | return rc; |
4439 | | |
4440 | 6.06k | memset(&compiler, 0, sizeof(YR_COMPILER)); |
4441 | | |
4442 | 6.06k | compiler.last_result = ERROR_SUCCESS; |
4443 | 6.06k | STAILQ_INIT(&compiler.rule_q); |
4444 | 6.06k | STAILQ_INIT(&compiler.current_rule_string_q); |
4445 | | |
4446 | 6.06k | rc = yr_arena_create(65536, 0, &compiler.sz_arena); |
4447 | 6.06k | if (rc == ERROR_SUCCESS) |
4448 | 6.06k | rc = yr_arena_create(65536, 0, &compiler.rules_arena); |
4449 | 6.06k | if (rc == ERROR_SUCCESS) |
4450 | 6.06k | rc = yr_arena_create(65536, 0, &compiler.code_arena); |
4451 | 6.06k | if (rc == ERROR_SUCCESS) |
4452 | 6.06k | rc = yr_arena_create(65536, 0, &compiler.strings_arena); |
4453 | 6.06k | if (rc == ERROR_SUCCESS) |
4454 | 6.06k | rc = yr_arena_create(65536, 0, &compiler.metas_arena); |
4455 | 6.06k | if (rc != ERROR_SUCCESS) |
4456 | 0 | return CL_EMEM; |
4457 | 6.06k | compiler.loop_for_of_mem_offset = -1; |
4458 | 6.06k | ns.name = "default"; |
4459 | 6.06k | compiler.current_namespace = &ns; |
4460 | 6.06k | compiler.the_arena = engine->yara_global->the_arena; |
4461 | 6.06k | compiler.rules_table = engine->yara_global->rules_table; |
4462 | 6.06k | compiler.objects_table = engine->yara_global->objects_table; |
4463 | 6.06k | compiler.allow_includes = 1; |
4464 | 6.06k | _yr_compiler_push_file_name(&compiler, filename); |
4465 | | |
4466 | 6.06k | rc = yr_lex_parse_rules_file(fs, &compiler); |
4467 | 6.06k | if (rc > 0) { /* rc = number of errors */ |
4468 | | /* TODO - handle the various errors? */ |
4469 | | #ifdef YARA_FINISHED |
4470 | | cli_errmsg("cli_loadyara: failed to parse rules file %s, error count %i\n", filename, rc); |
4471 | | if (compiler.sz_arena != NULL) |
4472 | | yr_arena_destroy(compiler.sz_arena); |
4473 | | if (compiler.rules_arena != NULL) |
4474 | | yr_arena_destroy(compiler.rules_arena); |
4475 | | if (compiler.code_arena != NULL) |
4476 | | yr_arena_destroy(compiler.code_arena); |
4477 | | if (compiler.strings_arena != NULL) |
4478 | | yr_arena_destroy(compiler.strings_arena); |
4479 | | if (compiler.metas_arena != NULL) |
4480 | | yr_arena_destroy(compiler.metas_arena); |
4481 | | _yr_compiler_pop_file_name(&compiler); |
4482 | | return CL_EMALFDB; |
4483 | | #else |
4484 | 5.35k | if (compiler.last_result == ERROR_INSUFICIENT_MEMORY) |
4485 | 0 | return CL_EMEM; |
4486 | 5.35k | rule_errors = rc; |
4487 | 5.35k | rc = CL_SUCCESS; |
4488 | 5.35k | #endif |
4489 | 5.35k | } |
4490 | | |
4491 | 11.1k | while (!STAILQ_EMPTY(&compiler.rule_q)) { |
4492 | 5.05k | rule = STAILQ_FIRST(&compiler.rule_q); |
4493 | 5.05k | STAILQ_REMOVE(&compiler.rule_q, rule, _yc_rule, link); |
4494 | | |
4495 | 5.05k | rules++; |
4496 | 5.05k | sigs++; /* can be decremented by load_oneyara */ |
4497 | | |
4498 | 5.05k | rc = load_oneyara(rule, |
4499 | 5.05k | engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)), |
4500 | 5.05k | engine, options, &sigs); |
4501 | 5.05k | if (rc != CL_SUCCESS) { |
4502 | 2.02k | cli_warnmsg("cli_loadyara: problem parsing yara file %s, yara rule %s\n", filename, rule->identifier); |
4503 | 2.02k | continue; |
4504 | 2.02k | } |
4505 | 5.05k | } |
4506 | | |
4507 | 6.06k | if (0 != rule_errors) |
4508 | 5.35k | cli_warnmsg("cli_loadyara: failed to parse or load %u yara rules from file %s, successfully loaded %u rules.\n", rule_errors + rules - sigs, filename, sigs); |
4509 | | |
4510 | 6.06k | yr_arena_append(engine->yara_global->the_arena, compiler.sz_arena); |
4511 | 6.06k | yr_arena_append(engine->yara_global->the_arena, compiler.rules_arena); |
4512 | 6.06k | yr_arena_append(engine->yara_global->the_arena, compiler.strings_arena); |
4513 | 6.06k | yr_arena_destroy(compiler.code_arena); |
4514 | 6.06k | yr_arena_destroy(compiler.metas_arena); |
4515 | 6.06k | _yr_compiler_pop_file_name(&compiler); |
4516 | | |
4517 | 6.06k | if (rc) |
4518 | 1.55k | return rc; |
4519 | | |
4520 | | #ifdef YARA_FINISHED |
4521 | | if (!rules) { |
4522 | | cli_errmsg("cli_loadyara: empty database file\n"); |
4523 | | return CL_EMALFDB; |
4524 | | } |
4525 | | #else |
4526 | 4.51k | if (!rules) { |
4527 | 2.66k | cli_warnmsg("cli_loadyara: empty database file\n"); |
4528 | 2.66k | yara_empty++; |
4529 | 2.66k | } |
4530 | 4.51k | #endif |
4531 | | |
4532 | | /* globals */ |
4533 | 4.51k | yara_total += rules; |
4534 | | |
4535 | 4.51k | if (signo) |
4536 | 4.51k | *signo += sigs; |
4537 | | |
4538 | 4.51k | cli_yaramsg("cli_loadyara: loaded %u of %u yara signatures from %s\n", sigs, rules, filename); |
4539 | | |
4540 | 4.51k | return CL_SUCCESS; |
4541 | 6.06k | } |
4542 | | #endif |
4543 | | |
4544 | | /* 0 1 2 3 |
4545 | | * PasswordName;Attributes;PWStorageType;Password |
4546 | | */ |
4547 | 0 | #define PWDB_TOKENS 4 |
4548 | | static int cli_loadpwdb(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio) |
4549 | 20.6k | { |
4550 | 20.6k | const char *tokens[PWDB_TOKENS + 1], *passname; |
4551 | 20.6k | char *attribs; |
4552 | 20.6k | char buffer[FILEBUFF]; |
4553 | 20.6k | unsigned int line = 0, skip = 0, pwcnt = 0, tokens_count; |
4554 | 20.6k | struct cli_pwdb *new; |
4555 | 20.6k | cl_pwdb_t container; |
4556 | 20.6k | struct cli_lsig_tdb tdb; |
4557 | 20.6k | int ret = CL_SUCCESS, pwstype; |
4558 | | |
4559 | 20.6k | while (1) { |
4560 | 20.6k | if (internal) { |
4561 | 20.6k | options |= CL_DB_OFFICIAL; |
4562 | | /* TODO - read default passwords */ |
4563 | 20.6k | return CL_SUCCESS; |
4564 | 20.6k | } else { |
4565 | 0 | if (!cli_dbgets(buffer, FILEBUFF, fs, dbio)) |
4566 | 0 | break; |
4567 | 0 | if (buffer[0] == '#') |
4568 | 0 | continue; |
4569 | 0 | cli_chomp(buffer); |
4570 | 0 | } |
4571 | 0 | line++; |
4572 | 0 | tokens_count = cli_strtokenize(buffer, ';', PWDB_TOKENS, tokens); |
4573 | |
|
4574 | 0 | if (tokens_count != PWDB_TOKENS) { |
4575 | 0 | ret = CL_EMALFDB; |
4576 | 0 | break; |
4577 | 0 | } |
4578 | | |
4579 | 0 | passname = tokens[0]; |
4580 | | |
4581 | | /* check if password is ignored, note that name is not stored */ |
4582 | 0 | if (engine->ignored && cli_chkign(engine->ignored, passname, passname)) { |
4583 | 0 | skip++; |
4584 | 0 | continue; |
4585 | 0 | } |
4586 | | |
4587 | 0 | if (engine->cb_sigload && engine->cb_sigload("pwdb", passname, ~options & CL_DB_OFFICIAL, engine->cb_sigload_ctx)) { |
4588 | 0 | cli_dbgmsg("cli_loadpwdb: skipping %s due to callback\n", passname); |
4589 | 0 | skip++; |
4590 | 0 | continue; |
4591 | 0 | } |
4592 | | |
4593 | | /* append target type 0 to tdb string if needed */ |
4594 | 0 | if ((tokens[1][0] == '\0') || (strstr(tokens[1], "Target:") != NULL)) { |
4595 | 0 | attribs = cli_strdup(tokens[1]); |
4596 | 0 | if (!attribs) { |
4597 | 0 | cli_errmsg("cli_loadpwdb: Can't allocate memory for attributes\n"); |
4598 | 0 | ret = CL_EMEM; |
4599 | 0 | break; |
4600 | 0 | } |
4601 | 0 | } else { |
4602 | 0 | size_t attlen = strlen(tokens[1]) + 10; |
4603 | 0 | attribs = cli_calloc(attlen, sizeof(char)); |
4604 | 0 | if (!attribs) { |
4605 | 0 | cli_errmsg("cli_loadpwdb: Can't allocate memory for attributes\n"); |
4606 | 0 | ret = CL_EMEM; |
4607 | 0 | break; |
4608 | 0 | } |
4609 | 0 | snprintf(attribs, attlen, "%s,Target:0", tokens[1]); |
4610 | 0 | } |
4611 | | |
4612 | | /* use the tdb to track filetypes and check flevels */ |
4613 | 0 | memset(&tdb, 0, sizeof(tdb)); |
4614 | 0 | ret = init_tdb(&tdb, engine, attribs, passname); |
4615 | 0 | free(attribs); |
4616 | 0 | if (ret != CL_SUCCESS) { |
4617 | 0 | skip++; |
4618 | 0 | if (ret == CL_BREAK) |
4619 | 0 | continue; |
4620 | 0 | else |
4621 | 0 | break; |
4622 | 0 | } |
4623 | | |
4624 | | /* check container type */ |
4625 | 0 | if (!tdb.container) { |
4626 | 0 | container = CLI_PWDB_ANY; |
4627 | 0 | } else { |
4628 | 0 | switch (*(tdb.container)) { |
4629 | 0 | case CL_TYPE_ANY: |
4630 | 0 | container = CLI_PWDB_ANY; |
4631 | 0 | break; |
4632 | 0 | case CL_TYPE_ZIP: |
4633 | 0 | container = CLI_PWDB_ZIP; |
4634 | 0 | break; |
4635 | 0 | case CL_TYPE_RAR: |
4636 | 0 | container = CLI_PWDB_RAR; |
4637 | 0 | break; |
4638 | 0 | default: |
4639 | 0 | cli_errmsg("cli_loadpwdb: Invalid container specified to .pwdb signature\n"); |
4640 | 0 | return CL_EMALFDB; |
4641 | 0 | } |
4642 | 0 | } |
4643 | 0 | FREE_TDB(tdb); |
4644 | | |
4645 | | /* check the PWStorageType */ |
4646 | 0 | if (!cli_isnumber(tokens[2])) { |
4647 | 0 | cli_errmsg("cli_loadpwdb: Invalid value for PWStorageType (third entry)\n"); |
4648 | 0 | ret = CL_EMALFDB; |
4649 | 0 | break; |
4650 | 0 | } |
4651 | | |
4652 | 0 | pwstype = atoi(tokens[2]); |
4653 | 0 | if ((pwstype == 0) || (pwstype == 1)) { |
4654 | 0 | new = (struct cli_pwdb *)MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_pwdb)); |
4655 | 0 | if (!new) { |
4656 | 0 | ret = CL_EMEM; |
4657 | 0 | break; |
4658 | 0 | } |
4659 | | |
4660 | | /* copy passwd name */ |
4661 | 0 | new->name = CLI_MPOOL_STRDUP(engine->mempool, tokens[0]); |
4662 | 0 | if (!new->name) { |
4663 | 0 | ret = CL_EMEM; |
4664 | 0 | MPOOL_FREE(engine->mempool, new); |
4665 | 0 | break; |
4666 | 0 | } |
4667 | | |
4668 | 0 | if (pwstype == 0) { /* cleartext */ |
4669 | 0 | new->passwd = CLI_MPOOL_STRDUP(engine->mempool, tokens[3]); |
4670 | 0 | new->length = (uint16_t)strlen(tokens[3]); |
4671 | 0 | } else { /* 1 => hex-encoded */ |
4672 | 0 | new->passwd = CLI_MPOOL_HEX2STR(engine->mempool, tokens[3]); |
4673 | 0 | new->length = (uint16_t)strlen(tokens[3]) / 2; |
4674 | 0 | } |
4675 | 0 | if (!new->passwd) { |
4676 | 0 | cli_errmsg("cli_loadpwdb: Can't decode or add new password entry\n"); |
4677 | 0 | if (pwstype == 0) |
4678 | 0 | ret = CL_EMEM; |
4679 | 0 | else |
4680 | 0 | ret = CL_EMALFDB; |
4681 | 0 | MPOOL_FREE(engine->mempool, new->name); |
4682 | 0 | MPOOL_FREE(engine->mempool, new); |
4683 | 0 | break; |
4684 | 0 | } |
4685 | | |
4686 | | /* add to the engine list, sorted by target type */ |
4687 | 0 | new->next = engine->pwdbs[container]; |
4688 | 0 | engine->pwdbs[container] = new; |
4689 | 0 | } else { |
4690 | 0 | cli_dbgmsg("cli_loadpwdb: Unsupported PWStorageType %u\n", pwstype); |
4691 | 0 | continue; |
4692 | 0 | } |
4693 | | |
4694 | 0 | pwcnt++; |
4695 | 0 | } |
4696 | | |
4697 | | /* error reporting */ |
4698 | 0 | if (ret) { |
4699 | 0 | cli_errmsg("Problem processing %s password database at line %u\n", internal ? "built-in" : "external", line); |
4700 | 0 | return ret; |
4701 | 0 | } |
4702 | | |
4703 | 0 | if (!pwcnt) { |
4704 | 0 | cli_errmsg("Empty %s password database\n", internal ? "built-in" : "external"); |
4705 | 0 | return CL_EMALFDB; |
4706 | 0 | } |
4707 | | |
4708 | 0 | cli_dbgmsg("Loaded %u (%u skipped) password entries\n", pwcnt, skip); |
4709 | 0 | return CL_SUCCESS; |
4710 | 0 | } |
4711 | | |
4712 | | static cl_error_t cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options); |
4713 | | |
4714 | | cl_error_t cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio) |
4715 | 47.1k | { |
4716 | 47.1k | cl_error_t ret = CL_SUCCESS; |
4717 | | |
4718 | 47.1k | FILE *fs = NULL; |
4719 | 47.1k | uint8_t skipped = 0; |
4720 | 47.1k | const char *dbname; |
4721 | 47.1k | char buff[FILEBUFF]; |
4722 | | |
4723 | 47.1k | if (dbio && dbio->chkonly) { |
4724 | 0 | while (cli_dbgets(buff, FILEBUFF, NULL, dbio)) continue; |
4725 | 0 | return CL_SUCCESS; |
4726 | 0 | } |
4727 | | |
4728 | 47.1k | if (!dbio && (fs = fopen(filename, "rb")) == NULL) { |
4729 | 0 | if (options & CL_DB_DIRECTORY) { /* bb#1624 */ |
4730 | 0 | if (access(filename, R_OK)) { |
4731 | 0 | if (errno == ENOENT) { |
4732 | 0 | cli_dbgmsg("Detected race condition, ignoring old file %s\n", filename); |
4733 | 0 | return CL_SUCCESS; |
4734 | 0 | } |
4735 | 0 | } |
4736 | 0 | } |
4737 | 0 | cli_errmsg("cli_load(): Can't open file %s\n", filename); |
4738 | 0 | return CL_EOPEN; |
4739 | 0 | } |
4740 | | |
4741 | 47.1k | if ((dbname = strrchr(filename, *PATHSEP))) |
4742 | 0 | dbname++; |
4743 | 47.1k | else |
4744 | 47.1k | dbname = filename; |
4745 | | |
4746 | 47.1k | #ifdef HAVE_YARA |
4747 | 47.1k | if (options & CL_DB_YARA_ONLY) { |
4748 | 0 | if (cli_strbcasestr(dbname, ".yar") || cli_strbcasestr(dbname, ".yara")) |
4749 | 0 | ret = cli_loadyara(fs, engine, signo, options, dbio, filename); |
4750 | 0 | else |
4751 | 0 | skipped = 1; |
4752 | 0 | } else |
4753 | 47.1k | #endif |
4754 | 47.1k | if (cli_strbcasestr(dbname, ".db")) { |
4755 | 0 | ret = cli_loaddb(fs, engine, signo, options, dbio, dbname); |
4756 | |
|
4757 | 47.1k | } else if (cli_strbcasestr(dbname, ".cvd")) { |
4758 | 0 | ret = cli_cvdload(fs, engine, signo, options, 0, filename, 0); |
4759 | |
|
4760 | 47.1k | } else if (cli_strbcasestr(dbname, ".cld")) { |
4761 | 0 | ret = cli_cvdload(fs, engine, signo, options, 1, filename, 0); |
4762 | |
|
4763 | 47.1k | } else if (cli_strbcasestr(dbname, ".cud")) { |
4764 | 0 | ret = cli_cvdload(fs, engine, signo, options, 2, filename, 0); |
4765 | |
|
4766 | 47.1k | } else if (cli_strbcasestr(dbname, ".crb")) { |
4767 | 868 | ret = cli_loadcrt(fs, engine, dbio); |
4768 | | |
4769 | 46.2k | } else if (cli_strbcasestr(dbname, ".hdb") || cli_strbcasestr(dbname, ".hsb")) { |
4770 | 2.67k | ret = cli_loadhash(fs, engine, signo, MD5_HDB, options, dbio, dbname); |
4771 | 43.6k | } else if (cli_strbcasestr(dbname, ".hdu") || cli_strbcasestr(dbname, ".hsu")) { |
4772 | 0 | if (options & CL_DB_PUA) |
4773 | 0 | ret = cli_loadhash(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname); |
4774 | 0 | else |
4775 | 0 | skipped = 1; |
4776 | |
|
4777 | 43.6k | } else if (cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".sfp")) { |
4778 | 1.30k | ret = cli_loadhash(fs, engine, signo, MD5_FP, options, dbio, dbname); |
4779 | 42.3k | } else if (cli_strbcasestr(dbname, ".mdb") || cli_strbcasestr(dbname, ".msb")) { |
4780 | 2.69k | ret = cli_loadhash(fs, engine, signo, MD5_MDB, options, dbio, dbname); |
4781 | 39.6k | } else if (cli_strbcasestr(dbname, ".imp")) { |
4782 | 0 | ret = cli_loadhash(fs, engine, signo, MD5_IMP, options, dbio, dbname); |
4783 | |
|
4784 | 39.6k | } else if (cli_strbcasestr(dbname, ".mdu") || cli_strbcasestr(dbname, ".msu")) { |
4785 | 0 | if (options & CL_DB_PUA) |
4786 | 0 | ret = cli_loadhash(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname); |
4787 | 0 | else |
4788 | 0 | skipped = 1; |
4789 | |
|
4790 | 39.6k | } else if (cli_strbcasestr(dbname, ".ndb")) { |
4791 | 6.09k | ret = cli_loadndb(fs, engine, signo, 0, options, dbio, dbname); |
4792 | | |
4793 | 33.5k | } else if (cli_strbcasestr(dbname, ".ndu")) { |
4794 | 0 | if (!(options & CL_DB_PUA)) |
4795 | 0 | skipped = 1; |
4796 | 0 | else |
4797 | 0 | ret = cli_loadndb(fs, engine, signo, 0, options | CL_DB_PUA_MODE, dbio, dbname); |
4798 | |
|
4799 | 33.5k | } else if (cli_strbcasestr(filename, ".ldb")) { |
4800 | 6.96k | ret = cli_loadldb(fs, engine, signo, options, dbio, dbname); |
4801 | | |
4802 | 26.5k | } else if (cli_strbcasestr(filename, ".ldu")) { |
4803 | 0 | if (options & CL_DB_PUA) |
4804 | 0 | ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname); |
4805 | 0 | else |
4806 | 0 | skipped = 1; |
4807 | 26.5k | } else if (cli_strbcasestr(filename, ".cbc")) { |
4808 | 0 | if (options & CL_DB_BYTECODE) |
4809 | 0 | ret = cli_loadcbc(fs, engine, signo, options, dbio, dbname); |
4810 | 0 | else |
4811 | 0 | skipped = 1; |
4812 | 26.5k | } else if (cli_strbcasestr(dbname, ".sdb")) { |
4813 | 0 | ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname); |
4814 | |
|
4815 | 26.5k | } else if (cli_strbcasestr(dbname, ".zmd")) { |
4816 | 0 | ret = cli_loadmd(fs, engine, signo, 1, options, dbio, dbname); |
4817 | |
|
4818 | 26.5k | } else if (cli_strbcasestr(dbname, ".rmd")) { |
4819 | 0 | ret = cli_loadmd(fs, engine, signo, 2, options, dbio, dbname); |
4820 | |
|
4821 | 26.5k | } else if (cli_strbcasestr(dbname, ".cfg")) { |
4822 | 1.52k | ret = cli_dconf_load(fs, engine, options, dbio); |
4823 | | |
4824 | 25.0k | } else if (cli_strbcasestr(dbname, ".info")) { |
4825 | 0 | ret = cli_loadinfo(fs, engine, options, dbio); |
4826 | |
|
4827 | 25.0k | } else if (cli_strbcasestr(dbname, ".wdb")) { |
4828 | 4.17k | if (options & CL_DB_PHISHING_URLS) { |
4829 | 4.17k | ret = cli_loadwdb(fs, engine, options, dbio); |
4830 | 4.17k | } else |
4831 | 0 | skipped = 1; |
4832 | 20.8k | } else if (cli_strbcasestr(dbname, ".pdb") || cli_strbcasestr(dbname, ".gdb")) { |
4833 | 4.35k | if (options & CL_DB_PHISHING_URLS) { |
4834 | 4.35k | ret = cli_loadpdb(fs, engine, signo, options, dbio); |
4835 | 4.35k | } else |
4836 | 0 | skipped = 1; |
4837 | 16.4k | } else if (cli_strbcasestr(dbname, ".ftm")) { |
4838 | 5.68k | ret = cli_loadftm(fs, engine, options, 0, dbio); |
4839 | | |
4840 | 10.8k | } else if (cli_strbcasestr(dbname, ".ign") || cli_strbcasestr(dbname, ".ign2")) { |
4841 | 722 | ret = cli_loadign(fs, engine, options, dbio); |
4842 | | |
4843 | 10.0k | } else if (cli_strbcasestr(dbname, ".idb")) { |
4844 | 882 | ret = cli_loadidb(fs, engine, signo, options, dbio); |
4845 | | |
4846 | 9.20k | } else if (cli_strbcasestr(dbname, ".cdb")) { |
4847 | 3.13k | ret = cli_loadcdb(fs, engine, signo, options, dbio); |
4848 | 6.06k | } else if (cli_strbcasestr(dbname, ".cat")) { |
4849 | 0 | ret = cli_loadmscat(fs, dbname, engine, options, dbio); |
4850 | 6.06k | } else if (cli_strbcasestr(dbname, ".ioc")) { |
4851 | 0 | ret = cli_loadopenioc(fs, dbname, engine, options); |
4852 | 0 | #ifdef HAVE_YARA |
4853 | 6.06k | } else if (cli_strbcasestr(dbname, ".yar") || cli_strbcasestr(dbname, ".yara")) { |
4854 | 6.06k | if (!(options & CL_DB_YARA_EXCLUDE)) |
4855 | 6.06k | ret = cli_loadyara(fs, engine, signo, options, dbio, filename); |
4856 | 0 | else |
4857 | 0 | skipped = 1; |
4858 | 6.06k | #endif |
4859 | 6.06k | } else if (cli_strbcasestr(dbname, ".pwdb")) { |
4860 | 0 | ret = cli_loadpwdb(fs, engine, options, 0, dbio); |
4861 | 0 | } else { |
4862 | 0 | cli_warnmsg("cli_load: unknown extension - skipping %s\n", filename); |
4863 | 0 | skipped = 1; |
4864 | 0 | } |
4865 | | |
4866 | 47.1k | if (ret) { |
4867 | 26.5k | cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret)); |
4868 | 26.5k | } else { |
4869 | 20.6k | if (skipped) |
4870 | 0 | cli_dbgmsg("%s skipped\n", filename); |
4871 | 20.6k | else |
4872 | 20.6k | cli_dbgmsg("%s loaded\n", filename); |
4873 | 20.6k | } |
4874 | | |
4875 | 47.1k | if (fs) |
4876 | 47.1k | fclose(fs); |
4877 | | |
4878 | 47.1k | if (CL_SUCCESS == ret) { |
4879 | 20.6k | if (engine->cb_sigload_progress) { |
4880 | | /* Let the progress callback function know how we're doing */ |
4881 | 0 | (void)engine->cb_sigload_progress(engine->num_total_signatures, *signo, engine->cb_sigload_progress_ctx); |
4882 | 0 | } |
4883 | 20.6k | } |
4884 | | |
4885 | 47.1k | return ret; |
4886 | 47.1k | } |
4887 | | |
4888 | | struct db_ll_entry { |
4889 | | char *path; |
4890 | | unsigned int load_priority; |
4891 | | struct db_ll_entry *next; |
4892 | | }; |
4893 | | |
4894 | | static void |
4895 | | cli_insertdbtoll(struct db_ll_entry **head, struct db_ll_entry *entry) |
4896 | 0 | { |
4897 | 0 | struct db_ll_entry *iter, *prev; |
4898 | 0 | if (NULL == *head) { |
4899 | 0 | *head = entry; |
4900 | 0 | entry->next = NULL; |
4901 | 0 | return; |
4902 | 0 | } |
4903 | 0 | for (prev = NULL, iter = *head; iter != NULL; prev = iter, iter = iter->next) { |
4904 | 0 | if (entry->load_priority < iter->load_priority) { |
4905 | 0 | if (NULL == prev) { |
4906 | 0 | *head = entry; |
4907 | 0 | } else { |
4908 | 0 | prev->next = entry; |
4909 | 0 | } |
4910 | 0 | entry->next = iter; |
4911 | 0 | return; |
4912 | 0 | } |
4913 | 0 | } |
4914 | 0 | prev->next = entry; |
4915 | 0 | entry->next = NULL; |
4916 | 0 | return; |
4917 | 0 | } |
4918 | | |
4919 | | /** |
4920 | | * @brief Count the number of signatures in a line-based signature file |
4921 | | * |
4922 | | * Ignores lines starting with # comment character |
4923 | | * |
4924 | | * @param filepath |
4925 | | * @return size_t |
4926 | | */ |
4927 | | static size_t count_line_based_signatures(const char *filepath) |
4928 | 41.0k | { |
4929 | 41.0k | FILE *fp = NULL; |
4930 | 41.0k | int current_character = 0; |
4931 | 41.0k | size_t sig_count = 0; |
4932 | 41.0k | bool in_sig = false; |
4933 | | |
4934 | 41.0k | fp = fopen(filepath, "r"); |
4935 | 41.0k | if (fp == NULL) { |
4936 | 0 | return 0; |
4937 | 0 | } |
4938 | | |
4939 | 41.0k | sig_count++; |
4940 | 115M | while (0 == feof(fp)) { |
4941 | | /* Get the next character */ |
4942 | 115M | current_character = fgetc(fp); |
4943 | | |
4944 | 115M | if (!in_sig) { |
4945 | | /* Not inside of a signature, yet */ |
4946 | 3.89M | if (!isspace(current_character) && // Ignore newlines and other forms of white space before a signature |
4947 | 3.89M | ('#' != current_character)) // Ignore lines that begin with a # comment character |
4948 | 2.06M | { |
4949 | | /* Found first character of a new signatures */ |
4950 | 2.06M | sig_count++; |
4951 | 2.06M | in_sig = true; |
4952 | 2.06M | } |
4953 | 111M | } else { |
4954 | | /* Inside of a signature */ |
4955 | 111M | if (current_character == '\n') { |
4956 | 2.02M | in_sig = false; |
4957 | 2.02M | } |
4958 | 111M | } |
4959 | 115M | } |
4960 | | |
4961 | 41.0k | fclose(fp); |
4962 | 41.0k | return sig_count; |
4963 | 41.0k | } |
4964 | | |
4965 | | /** |
4966 | | * @brief Count the number of signatures in a database file. |
4967 | | * |
4968 | | * Non-database files will be ignored, and count as 0 signatures. |
4969 | | * Database validation is not done, just signature counting. |
4970 | | * |
4971 | | * CVD/CLD/CUD database archives are not counted the hard way, we just trust |
4972 | | * signature count in the header. Yara rules and bytecode sigs count as 1 each. |
4973 | | * |
4974 | | * @param filepath Filepath of the database file to count. |
4975 | | * @return size_t The number of signatures. |
4976 | | */ |
4977 | | static size_t count_signatures(const char *filepath, struct cl_engine *engine, unsigned int options) |
4978 | 47.1k | { |
4979 | 47.1k | size_t num_signatures = 0; |
4980 | 47.1k | struct cl_cvd *db_archive_header = NULL; |
4981 | | |
4982 | 47.1k | if (cli_strbcasestr(filepath, ".cld") || |
4983 | 47.1k | cli_strbcasestr(filepath, ".cvd") || |
4984 | 47.1k | cli_strbcasestr(filepath, ".cud")) { |
4985 | | /* use the CVD head to get the sig count. */ |
4986 | 0 | if (0 == access(filepath, R_OK)) { |
4987 | 0 | db_archive_header = cl_cvdhead(filepath); |
4988 | 0 | if (!db_archive_header) { |
4989 | 0 | cli_errmsg("cli_loaddbdir: error parsing header of %s\n", filepath); |
4990 | 0 | goto done; |
4991 | 0 | } |
4992 | | |
4993 | 0 | num_signatures += db_archive_header->sigs; |
4994 | 0 | } |
4995 | |
|
4996 | 47.1k | } else if ((CL_BYTECODE_TRUST_ALL == engine->bytecode_security) && |
4997 | 47.1k | cli_strbcasestr(filepath, ".cbc")) { |
4998 | | /* Counts as 1 signature if loading plain .cbc files. */ |
4999 | 0 | num_signatures += 1; |
5000 | |
|
5001 | 47.1k | } else if ((options & CL_DB_YARA_ONLY) && |
5002 | 47.1k | (cli_strbcasestr(filepath, ".yar") || cli_strbcasestr(filepath, ".yara"))) { |
5003 | | /* Counts as 1 signature. */ |
5004 | 0 | num_signatures += 1; |
5005 | |
|
5006 | 47.1k | } else if (cli_strbcasestr(filepath, ".db") || |
5007 | 47.1k | cli_strbcasestr(filepath, ".crb") || |
5008 | 47.1k | cli_strbcasestr(filepath, ".hdb") || cli_strbcasestr(filepath, ".hsb") || |
5009 | 47.1k | cli_strbcasestr(filepath, ".hdu") || cli_strbcasestr(filepath, ".hsu") || |
5010 | 47.1k | cli_strbcasestr(filepath, ".fp") || cli_strbcasestr(filepath, ".sfp") || |
5011 | 47.1k | cli_strbcasestr(filepath, ".mdb") || cli_strbcasestr(filepath, ".msb") || |
5012 | 47.1k | cli_strbcasestr(filepath, ".imp") || |
5013 | 47.1k | cli_strbcasestr(filepath, ".mdu") || cli_strbcasestr(filepath, ".msu") || |
5014 | 47.1k | cli_strbcasestr(filepath, ".ndb") || cli_strbcasestr(filepath, ".ndu") || cli_strbcasestr(filepath, ".sdb") || |
5015 | 47.1k | cli_strbcasestr(filepath, ".ldb") || cli_strbcasestr(filepath, ".ldu") || |
5016 | 47.1k | cli_strbcasestr(filepath, ".zmd") || cli_strbcasestr(filepath, ".rmd") || |
5017 | 47.1k | cli_strbcasestr(filepath, ".cfg") || |
5018 | 47.1k | cli_strbcasestr(filepath, ".wdb") || |
5019 | 47.1k | cli_strbcasestr(filepath, ".pdb") || cli_strbcasestr(filepath, ".gdb") || |
5020 | 47.1k | cli_strbcasestr(filepath, ".ftm") || |
5021 | 47.1k | cli_strbcasestr(filepath, ".ign") || cli_strbcasestr(filepath, ".ign2") || |
5022 | 47.1k | cli_strbcasestr(filepath, ".idb") || |
5023 | 47.1k | cli_strbcasestr(filepath, ".cdb") || |
5024 | 47.1k | cli_strbcasestr(filepath, ".cat") || |
5025 | 47.1k | cli_strbcasestr(filepath, ".ioc") || |
5026 | 47.1k | cli_strbcasestr(filepath, ".pwdb")) { |
5027 | | /* Should be a line-based signaure file, count it the old fashioned way */ |
5028 | 41.0k | num_signatures += count_line_based_signatures(filepath); |
5029 | 41.0k | } |
5030 | | |
5031 | 47.1k | done: |
5032 | 47.1k | if (NULL != db_archive_header) { |
5033 | 0 | cl_cvdfree(db_archive_header); |
5034 | 0 | } |
5035 | | |
5036 | 47.1k | return num_signatures; |
5037 | 47.1k | } |
5038 | | |
5039 | | static cl_error_t cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options) |
5040 | 0 | { |
5041 | 0 | cl_error_t ret = CL_EOPEN; |
5042 | |
|
5043 | 0 | DIR *dd = NULL; |
5044 | 0 | struct dirent *dent; |
5045 | 0 | char *dbfile = NULL; |
5046 | 0 | int ends_with_sep = 0; |
5047 | 0 | size_t dirname_len; |
5048 | 0 | struct cl_cvd *daily_cld = NULL; |
5049 | 0 | struct cl_cvd *daily_cvd = NULL; |
5050 | 0 | struct db_ll_entry *head = NULL; |
5051 | 0 | struct db_ll_entry *iter; |
5052 | 0 | struct db_ll_entry *next; |
5053 | |
|
5054 | 0 | cli_dbgmsg("Loading databases from %s\n", dirname); |
5055 | |
|
5056 | 0 | if ((dd = opendir(dirname)) == NULL) { |
5057 | 0 | cli_errmsg("cli_loaddbdir: Can't open directory %s\n", dirname); |
5058 | 0 | ret = CL_EOPEN; |
5059 | 0 | goto done; |
5060 | 0 | } |
5061 | | |
5062 | 0 | dirname_len = strlen(dirname); |
5063 | 0 | if (dirname_len >= strlen(PATHSEP)) { |
5064 | 0 | if (strcmp(dirname + dirname_len - strlen(PATHSEP), PATHSEP) == 0) { |
5065 | 0 | cli_dbgmsg("cli_loaddbdir: dirname ends with separator\n"); |
5066 | 0 | ends_with_sep = 1; |
5067 | 0 | } |
5068 | 0 | } |
5069 | |
|
5070 | 0 | while ((dent = readdir(dd))) { |
5071 | 0 | struct db_ll_entry *entry; |
5072 | 0 | unsigned int load_priority; |
5073 | |
|
5074 | 0 | if (!dent->d_ino) { |
5075 | 0 | continue; |
5076 | 0 | } |
5077 | 0 | if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { |
5078 | 0 | continue; |
5079 | 0 | } |
5080 | 0 | if (!CLI_DBEXT(dent->d_name)) { |
5081 | 0 | continue; |
5082 | 0 | } |
5083 | | |
5084 | 0 | dbfile = (char *)cli_malloc(strlen(dent->d_name) + dirname_len + 2); |
5085 | 0 | if (!dbfile) { |
5086 | 0 | cli_errmsg("cli_loaddbdir: dbfile == NULL\n"); |
5087 | 0 | ret = CL_EMEM; |
5088 | 0 | goto done; |
5089 | 0 | } |
5090 | 0 | if (ends_with_sep) |
5091 | 0 | sprintf(dbfile, "%s%s", dirname, dent->d_name); |
5092 | 0 | else |
5093 | 0 | sprintf(dbfile, "%s" PATHSEP "%s", dirname, dent->d_name); |
5094 | |
|
5095 | 0 | #define DB_LOAD_PRIORITY_IGN 1 |
5096 | 0 | #define DB_LOAD_PRIORITY_DAILY_CLD 2 |
5097 | 0 | #define DB_LOAD_PRIORITY_DAILY_CVD 3 |
5098 | 0 | #define DB_LOAD_PRIORITY_LOCAL_GDB 4 |
5099 | 0 | #define DB_LOAD_PRIORITY_DAILY_CFG 5 |
5100 | 0 | #define DB_LOAD_PRIORITY_CRB 6 |
5101 | 0 | #define DB_LOAD_PRIORITY_NORMAL 7 |
5102 | |
|
5103 | 0 | if (cli_strbcasestr(dent->d_name, ".ign") || cli_strbcasestr(dent->d_name, ".ign2")) { |
5104 | | /* load .ign and .ign2 files first */ |
5105 | 0 | load_priority = DB_LOAD_PRIORITY_IGN; |
5106 | |
|
5107 | 0 | engine->num_total_signatures += count_line_based_signatures(dbfile); |
5108 | |
|
5109 | 0 | } else if (!strcmp(dent->d_name, "daily.cld")) { |
5110 | | /* The daily db must be loaded before main, this way, the |
5111 | | daily ign & ign2 signatures prevent ign'ored signatures |
5112 | | in all databases from being loaded. */ |
5113 | 0 | load_priority = DB_LOAD_PRIORITY_DAILY_CLD; |
5114 | |
|
5115 | 0 | if (0 == access(dbfile, R_OK)) { |
5116 | 0 | daily_cld = cl_cvdhead(dbfile); |
5117 | 0 | if (!daily_cld) { |
5118 | 0 | cli_errmsg("cli_loaddbdir: error parsing header of %s\n", dbfile); |
5119 | 0 | ret = CL_EMALFDB; |
5120 | 0 | goto done; |
5121 | 0 | } |
5122 | | |
5123 | | /* Successfully opened the daily CLD file and read the header info. */ |
5124 | 0 | engine->num_total_signatures += daily_cld->sigs; |
5125 | 0 | } else { |
5126 | 0 | free(dbfile); |
5127 | 0 | dbfile = NULL; |
5128 | 0 | continue; |
5129 | 0 | } |
5130 | |
|
5131 | 0 | } else if (!strcmp(dent->d_name, "daily.cvd")) { |
5132 | 0 | load_priority = DB_LOAD_PRIORITY_DAILY_CVD; |
5133 | |
|
5134 | 0 | if (0 == access(dbfile, R_OK)) { |
5135 | 0 | daily_cvd = cl_cvdhead(dbfile); |
5136 | 0 | if (!daily_cvd) { |
5137 | 0 | cli_errmsg("cli_loaddbdir: error parsing header of %s\n", dbfile); |
5138 | 0 | ret = CL_EMALFDB; |
5139 | 0 | goto done; |
5140 | 0 | } |
5141 | | /* Successfully opened the daily CVD file and ready the header info. */ |
5142 | 0 | engine->num_total_signatures += daily_cvd->sigs; |
5143 | 0 | } else { |
5144 | 0 | free(dbfile); |
5145 | 0 | dbfile = NULL; |
5146 | 0 | continue; |
5147 | 0 | } |
5148 | |
|
5149 | 0 | } else if (!strcmp(dent->d_name, "local.gdb")) { |
5150 | 0 | load_priority = DB_LOAD_PRIORITY_LOCAL_GDB; |
5151 | |
|
5152 | 0 | engine->num_total_signatures += count_line_based_signatures(dbfile); |
5153 | |
|
5154 | 0 | } else if (!strcmp(dent->d_name, "daily.cfg")) { |
5155 | 0 | load_priority = DB_LOAD_PRIORITY_DAILY_CFG; |
5156 | |
|
5157 | 0 | engine->num_total_signatures += count_line_based_signatures(dbfile); |
5158 | |
|
5159 | 0 | } else if ((options & CL_DB_OFFICIAL_ONLY) && |
5160 | 0 | !strstr(dirname, "clamav-") && // Official databases that are temp-files (in the process of updating). |
5161 | 0 | !cli_strbcasestr(dent->d_name, ".cld") && // Official databases that have been updated using incremental updates. |
5162 | 0 | !cli_strbcasestr(dent->d_name, ".cvd")) { // Official databases. |
5163 | | // TODO Should this be higher up in the list? Should we |
5164 | | // ignore .ign/.ign2 files and the local.gdb file when this |
5165 | | // flag is set? |
5166 | 0 | cli_dbgmsg("Skipping unofficial database %s\n", dent->d_name); |
5167 | 0 | free(dbfile); |
5168 | 0 | dbfile = NULL; |
5169 | 0 | continue; |
5170 | |
|
5171 | 0 | } else if (cli_strbcasestr(dent->d_name, ".crb")) { |
5172 | | /* .cat files cannot be loaded successfully unless there are .crb |
5173 | | * rules that trust the certs used to sign the catalog files. |
5174 | | * Therefore, we need to ensure the .crb rules are loaded prior */ |
5175 | 0 | load_priority = DB_LOAD_PRIORITY_CRB; |
5176 | |
|
5177 | 0 | engine->num_total_signatures += count_line_based_signatures(dbfile); |
5178 | |
|
5179 | 0 | } else { |
5180 | 0 | load_priority = DB_LOAD_PRIORITY_NORMAL; |
5181 | |
|
5182 | 0 | engine->num_total_signatures += count_signatures(dbfile, engine, options); |
5183 | 0 | } |
5184 | | |
5185 | 0 | entry = malloc(sizeof(*entry)); |
5186 | 0 | if (NULL == entry) { |
5187 | 0 | cli_errmsg("cli_loaddbdir: failed to allocate memory for database load list entry\n"); |
5188 | 0 | ret = CL_EMEM; |
5189 | 0 | goto done; |
5190 | 0 | } |
5191 | | |
5192 | 0 | entry->path = dbfile; |
5193 | 0 | dbfile = NULL; |
5194 | 0 | entry->load_priority = load_priority; |
5195 | 0 | cli_insertdbtoll(&head, entry); |
5196 | 0 | } |
5197 | | |
5198 | | /* The list entries are stored in priority order, so now just loop through |
5199 | | * and load everything. |
5200 | | * NOTE: If there's a daily.cld and a daily.cvd, we'll only load whichever |
5201 | | * has the highest version number. If they have the same version number |
5202 | | * we load daily.cld, since that will load faster (it won't attempt to |
5203 | | * verify the digital signature of the db). |
5204 | | * |
5205 | | * TODO It'd be ideal if we treated all cld/cvd pairs like we do the daily |
5206 | | * ones, and only loaded the one with the highest version. */ |
5207 | 0 | for (iter = head; iter != NULL; iter = iter->next) { |
5208 | |
|
5209 | 0 | if (DB_LOAD_PRIORITY_DAILY_CLD == iter->load_priority) { |
5210 | | /* iter is the daily.cld. If we also have the cvd and the cvd is newer, skip the cld. */ |
5211 | 0 | if ((NULL != daily_cvd) && (daily_cld->version < daily_cvd->version)) { |
5212 | 0 | continue; |
5213 | 0 | } |
5214 | |
|
5215 | 0 | } else if (DB_LOAD_PRIORITY_DAILY_CVD == iter->load_priority) { |
5216 | | /* iter is the daily.cvd. If we also have the cld and the cld is same or newer, skip the cvd. */ |
5217 | 0 | if ((NULL != daily_cld) && (daily_cld->version >= daily_cvd->version)) { |
5218 | 0 | continue; |
5219 | 0 | } |
5220 | 0 | } |
5221 | | |
5222 | 0 | ret = cli_load(iter->path, engine, signo, options, NULL); |
5223 | 0 | if (ret) { |
5224 | 0 | cli_errmsg("cli_loaddbdir: error loading database %s\n", iter->path); |
5225 | 0 | goto done; |
5226 | 0 | } |
5227 | 0 | } |
5228 | | |
5229 | 0 | done: |
5230 | 0 | for (iter = head; iter != NULL; iter = next) { |
5231 | 0 | next = iter->next; |
5232 | 0 | free(iter->path); |
5233 | 0 | free(iter); |
5234 | 0 | } |
5235 | |
|
5236 | 0 | if (NULL != dbfile) { |
5237 | 0 | free(dbfile); |
5238 | 0 | } |
5239 | |
|
5240 | 0 | if (NULL != dd) { |
5241 | 0 | closedir(dd); |
5242 | 0 | } |
5243 | |
|
5244 | 0 | if (NULL != daily_cld) { |
5245 | 0 | cl_cvdfree(daily_cld); |
5246 | 0 | } |
5247 | |
|
5248 | 0 | if (NULL != daily_cvd) { |
5249 | 0 | cl_cvdfree(daily_cvd); |
5250 | 0 | } |
5251 | |
|
5252 | 0 | if (ret == CL_EOPEN) |
5253 | 0 | cli_errmsg("cli_loaddbdir: No supported database files found in %s\n", dirname); |
5254 | |
|
5255 | 0 | return ret; |
5256 | 0 | } |
5257 | | |
5258 | | cl_error_t cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int dboptions) |
5259 | 47.1k | { |
5260 | 47.1k | STATBUF sb; |
5261 | 47.1k | int ret; |
5262 | | |
5263 | 47.1k | if (!engine) { |
5264 | 0 | cli_errmsg("cl_load: engine == NULL\n"); |
5265 | 0 | return CL_ENULLARG; |
5266 | 0 | } |
5267 | | |
5268 | 47.1k | if (engine->dboptions & CL_DB_COMPILED) { |
5269 | 0 | cli_errmsg("cl_load(): can't load new databases when engine is already compiled\n"); |
5270 | 0 | return CL_EARG; |
5271 | 0 | } |
5272 | | |
5273 | 47.1k | if (CLAMSTAT(path, &sb) == -1) { |
5274 | 0 | switch (errno) { |
5275 | 0 | #if defined(EACCES) |
5276 | 0 | case EACCES: |
5277 | 0 | cli_errmsg("cl_load(): Access denied for path: %s\n", path); |
5278 | 0 | break; |
5279 | 0 | #endif |
5280 | 0 | #if defined(ENOENT) |
5281 | 0 | case ENOENT: |
5282 | 0 | cli_errmsg("cl_load(): No such file or directory: %s\n", path); |
5283 | 0 | break; |
5284 | 0 | #endif |
5285 | 0 | #if defined(ELOOP) |
5286 | 0 | case ELOOP: |
5287 | 0 | cli_errmsg("cl_load(): Too many symbolic links encountered in path: %s\n", path); |
5288 | 0 | break; |
5289 | 0 | #endif |
5290 | 0 | #if defined(EOVERFLOW) |
5291 | 0 | case EOVERFLOW: |
5292 | 0 | cli_errmsg("cl_load(): File size is too large to be recognized. Path: %s\n", path); |
5293 | 0 | break; |
5294 | 0 | #endif |
5295 | 0 | #if defined(EIO) |
5296 | 0 | case EIO: |
5297 | 0 | cli_errmsg("cl_load(): An I/O error occurred while reading from path: %s\n", path); |
5298 | 0 | break; |
5299 | 0 | #endif |
5300 | 0 | default: |
5301 | 0 | cli_errmsg("cl_load: Can't get status of: %s\n", path); |
5302 | 0 | break; |
5303 | 0 | } |
5304 | 0 | return CL_ESTAT; |
5305 | 0 | } |
5306 | | |
5307 | 47.1k | if ((dboptions & CL_DB_PHISHING_URLS) && !engine->phishcheck && (engine->dconf->phishing & PHISHING_CONF_ENGINE)) |
5308 | 47.1k | if (CL_SUCCESS != (ret = phishing_init(engine))) |
5309 | 0 | return ret; |
5310 | | |
5311 | 47.1k | if ((dboptions & CL_DB_BYTECODE) && !engine->bcs.inited) { |
5312 | 47.1k | if (CL_SUCCESS != (ret = cli_bytecode_init(&engine->bcs))) |
5313 | 0 | return ret; |
5314 | 47.1k | } else { |
5315 | 0 | cli_dbgmsg("Bytecode engine disabled\n"); |
5316 | 0 | } |
5317 | | |
5318 | 47.1k | if (!engine->cache && clean_cache_init(engine)) |
5319 | 0 | return CL_EMEM; |
5320 | | |
5321 | 47.1k | engine->dboptions |= dboptions; |
5322 | | |
5323 | 47.1k | switch (sb.st_mode & S_IFMT) { |
5324 | 47.1k | case S_IFREG: |
5325 | | /* Count # of sigs in the database now */ |
5326 | 47.1k | engine->num_total_signatures += count_signatures(path, engine, dboptions); |
5327 | | |
5328 | 47.1k | ret = cli_load(path, engine, signo, dboptions, NULL); |
5329 | 47.1k | break; |
5330 | | |
5331 | 0 | case S_IFDIR: |
5332 | | /* Count # of signatures inside cli_loaddbdir(), before loading */ |
5333 | 0 | ret = cli_loaddbdir(path, engine, signo, dboptions | CL_DB_DIRECTORY); |
5334 | 0 | break; |
5335 | | |
5336 | 0 | default: |
5337 | 0 | cli_errmsg("cl_load(%s): Not supported database file type\n", path); |
5338 | 0 | return CL_EOPEN; |
5339 | 47.1k | } |
5340 | | |
5341 | 47.1k | if (engine->cb_sigload_progress) { |
5342 | | /* Let the progress callback function know we're done! */ |
5343 | 0 | (void)engine->cb_sigload_progress(*signo, *signo, engine->cb_sigload_progress_ctx); |
5344 | 0 | } |
5345 | | |
5346 | 47.1k | #ifdef YARA_PROTO |
5347 | 47.1k | if (yara_total) { |
5348 | 5.48k | cli_yaramsg("$$$$$$$$$$$$ YARA $$$$$$$$$$$$\n"); |
5349 | 5.48k | cli_yaramsg("\tTotal Rules: %u\n", yara_total); |
5350 | 5.48k | cli_yaramsg("\tRules Loaded: %u\n", yara_loaded); |
5351 | 5.48k | cli_yaramsg("\tComplex Conditions: %u\n", yara_complex); |
5352 | 5.48k | cli_yaramsg("\tMalformed/Unsupported Rules: %u\n", yara_malform); |
5353 | 5.48k | cli_yaramsg("\tEmpty Rules: %u\n", yara_empty); |
5354 | 5.48k | cli_yaramsg("$$$$$$$$$$$$ YARA $$$$$$$$$$$$\n"); |
5355 | 5.48k | } |
5356 | 47.1k | #endif |
5357 | 47.1k | return ret; |
5358 | 47.1k | } |
5359 | | |
5360 | | const char *cl_retdbdir(void) |
5361 | 0 | { |
5362 | | #ifdef _WIN32 |
5363 | | int have_ddir = 0; |
5364 | | char path[MAX_PATH] = ""; |
5365 | | DWORD sizof; |
5366 | | HKEY key; |
5367 | | |
5368 | | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\ClamAV", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS || RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\ClamAV", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { |
5369 | | sizof = sizeof(path); |
5370 | | if (RegQueryValueEx(key, "DataDir", 0, NULL, path, &sizof) == ERROR_SUCCESS) { |
5371 | | have_ddir = 1; |
5372 | | memcpy(DATABASE_DIRECTORY, path, sizof); |
5373 | | } |
5374 | | RegCloseKey(key); |
5375 | | } |
5376 | | if (!(have_ddir) && GetModuleFileName(NULL, path, sizeof(path))) { |
5377 | | char *dir = NULL; |
5378 | | path[sizeof(path) - 1] = '\0'; |
5379 | | dir = dirname(path); |
5380 | | snprintf(DATABASE_DIRECTORY, sizeof(DATABASE_DIRECTORY), "%s\\database", dir); |
5381 | | } |
5382 | | DATABASE_DIRECTORY[sizeof(DATABASE_DIRECTORY) - 1] = '\0'; |
5383 | | |
5384 | | return (const char *)DATABASE_DIRECTORY; |
5385 | | #else |
5386 | 0 | return DATADIR; |
5387 | 0 | #endif |
5388 | 0 | } |
5389 | | |
5390 | | cl_error_t cl_statinidir(const char *dirname, struct cl_stat *dbstat) |
5391 | 0 | { |
5392 | 0 | DIR *dd; |
5393 | 0 | struct dirent *dent; |
5394 | 0 | char *fname; |
5395 | |
|
5396 | 0 | if (dbstat) { |
5397 | 0 | dbstat->entries = 0; |
5398 | 0 | dbstat->stattab = NULL; |
5399 | 0 | dbstat->statdname = NULL; |
5400 | 0 | dbstat->dir = cli_strdup(dirname); |
5401 | 0 | } else { |
5402 | 0 | cli_errmsg("cl_statdbdir(): Null argument passed.\n"); |
5403 | 0 | return CL_ENULLARG; |
5404 | 0 | } |
5405 | | |
5406 | 0 | if ((dd = opendir(dirname)) == NULL) { |
5407 | 0 | cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname); |
5408 | 0 | cl_statfree(dbstat); |
5409 | 0 | return CL_EOPEN; |
5410 | 0 | } |
5411 | | |
5412 | 0 | cli_dbgmsg("Stat()ing files in %s\n", dirname); |
5413 | |
|
5414 | 0 | while ((dent = readdir(dd))) { |
5415 | 0 | if (dent->d_ino) { |
5416 | 0 | if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) { |
5417 | 0 | dbstat->entries++; |
5418 | 0 | dbstat->stattab = (STATBUF *)cli_realloc2(dbstat->stattab, dbstat->entries * sizeof(STATBUF)); |
5419 | 0 | if (!dbstat->stattab) { |
5420 | 0 | cl_statfree(dbstat); |
5421 | 0 | closedir(dd); |
5422 | 0 | return CL_EMEM; |
5423 | 0 | } |
5424 | | |
5425 | | #ifdef _WIN32 |
5426 | | dbstat->statdname = (char **)cli_realloc2(dbstat->statdname, dbstat->entries * sizeof(char *)); |
5427 | | if (!dbstat->statdname) { |
5428 | | cli_errmsg("cl_statinidir: Can't allocate memory for dbstat->statdname\n"); |
5429 | | cl_statfree(dbstat); |
5430 | | closedir(dd); |
5431 | | return CL_EMEM; |
5432 | | } |
5433 | | #endif |
5434 | | |
5435 | 0 | fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 32); |
5436 | 0 | if (!fname) { |
5437 | 0 | cli_errmsg("cl_statinidir: Cant' allocate memory for fname\n"); |
5438 | 0 | cl_statfree(dbstat); |
5439 | 0 | closedir(dd); |
5440 | 0 | return CL_EMEM; |
5441 | 0 | } |
5442 | 0 | sprintf(fname, "%s" PATHSEP "%s", dirname, dent->d_name); |
5443 | | #ifdef _WIN32 |
5444 | | dbstat->statdname[dbstat->entries - 1] = (char *)cli_malloc(strlen(dent->d_name) + 1); |
5445 | | if (!dbstat->statdname[dbstat->entries - 1]) { |
5446 | | cli_errmsg("cli_statinidir: Can't allocate memory for dbstat->statdname\n"); |
5447 | | cl_statfree(dbstat); |
5448 | | closedir(dd); |
5449 | | return CL_EMEM; |
5450 | | } |
5451 | | |
5452 | | strcpy(dbstat->statdname[dbstat->entries - 1], dent->d_name); |
5453 | | #endif |
5454 | 0 | CLAMSTAT(fname, &dbstat->stattab[dbstat->entries - 1]); |
5455 | 0 | free(fname); |
5456 | 0 | } |
5457 | 0 | } |
5458 | 0 | } |
5459 | | |
5460 | 0 | closedir(dd); |
5461 | 0 | return CL_SUCCESS; |
5462 | 0 | } |
5463 | | |
5464 | | int cl_statchkdir(const struct cl_stat *dbstat) |
5465 | 0 | { |
5466 | 0 | DIR *dd; |
5467 | 0 | struct dirent *dent; |
5468 | 0 | STATBUF sb; |
5469 | 0 | unsigned int i, found; |
5470 | 0 | char *fname; |
5471 | |
|
5472 | 0 | if (!dbstat || !dbstat->dir) { |
5473 | 0 | cli_errmsg("cl_statdbdir(): Null argument passed.\n"); |
5474 | 0 | return CL_ENULLARG; |
5475 | 0 | } |
5476 | | |
5477 | 0 | if ((dd = opendir(dbstat->dir)) == NULL) { |
5478 | 0 | cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir); |
5479 | 0 | return CL_EOPEN; |
5480 | 0 | } |
5481 | | |
5482 | 0 | cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir); |
5483 | |
|
5484 | 0 | while ((dent = readdir(dd))) { |
5485 | 0 | if (dent->d_ino) { |
5486 | 0 | if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) { |
5487 | 0 | fname = cli_malloc(strlen(dbstat->dir) + strlen(dent->d_name) + 32); |
5488 | 0 | if (!fname) { |
5489 | 0 | cli_errmsg("cl_statchkdir: can't allocate memory for fname\n"); |
5490 | 0 | closedir(dd); |
5491 | 0 | return CL_EMEM; |
5492 | 0 | } |
5493 | | |
5494 | 0 | sprintf(fname, "%s" PATHSEP "%s", dbstat->dir, dent->d_name); |
5495 | 0 | CLAMSTAT(fname, &sb); |
5496 | 0 | free(fname); |
5497 | |
|
5498 | 0 | found = 0; |
5499 | 0 | for (i = 0; i < dbstat->entries; i++) |
5500 | | #ifdef _WIN32 |
5501 | | if (!strcmp(dbstat->statdname[i], dent->d_name)) { |
5502 | | #else |
5503 | 0 | if (dbstat->stattab[i].st_ino == sb.st_ino) { |
5504 | 0 | #endif |
5505 | 0 | found = 1; |
5506 | 0 | if (dbstat->stattab[i].st_mtime != sb.st_mtime) { |
5507 | 0 | closedir(dd); |
5508 | 0 | return 1; |
5509 | 0 | } |
5510 | 0 | } |
5511 | | |
5512 | 0 | if (!found) { |
5513 | 0 | closedir(dd); |
5514 | 0 | return 1; |
5515 | 0 | } |
5516 | 0 | } |
5517 | 0 | } |
5518 | 0 | } |
5519 | | |
5520 | 0 | closedir(dd); |
5521 | 0 | return CL_SUCCESS; |
5522 | 0 | } |
5523 | | |
5524 | | void cli_pwdb_list_free(struct cl_engine *engine, struct cli_pwdb *pwdb) |
5525 | 0 | { |
5526 | 0 | struct cli_pwdb *thiz, *that; |
5527 | |
|
5528 | 0 | #ifndef USE_MPOOL |
5529 | 0 | UNUSEDPARAM(engine); |
5530 | 0 | #endif |
5531 | |
|
5532 | 0 | thiz = pwdb; |
5533 | 0 | while (thiz) { |
5534 | 0 | that = thiz->next; |
5535 | |
|
5536 | 0 | MPOOL_FREE(engine->mempool, thiz->name); |
5537 | 0 | MPOOL_FREE(engine->mempool, thiz->passwd); |
5538 | 0 | MPOOL_FREE(engine->mempool, thiz); |
5539 | |
|
5540 | 0 | thiz = that; |
5541 | 0 | } |
5542 | 0 | } |
5543 | | |
5544 | | cl_error_t cl_statfree(struct cl_stat *dbstat) |
5545 | 0 | { |
5546 | 0 | if (dbstat) { |
5547 | |
|
5548 | | #ifdef _WIN32 |
5549 | | int i; |
5550 | | |
5551 | | if (dbstat->statdname) { |
5552 | | for (i = 0; i < dbstat->entries; i++) { |
5553 | | if (dbstat->statdname[i]) |
5554 | | free(dbstat->statdname[i]); |
5555 | | dbstat->statdname[i] = NULL; |
5556 | | } |
5557 | | free(dbstat->statdname); |
5558 | | dbstat->statdname = NULL; |
5559 | | } |
5560 | | #endif |
5561 | |
|
5562 | 0 | if (dbstat->stattab) { |
5563 | 0 | free(dbstat->stattab); |
5564 | 0 | dbstat->stattab = NULL; |
5565 | 0 | } |
5566 | 0 | dbstat->entries = 0; |
5567 | |
|
5568 | 0 | if (dbstat->dir) { |
5569 | 0 | free(dbstat->dir); |
5570 | 0 | dbstat->dir = NULL; |
5571 | 0 | } |
5572 | 0 | } else { |
5573 | 0 | cli_errmsg("cl_statfree(): Null argument passed\n"); |
5574 | 0 | return CL_ENULLARG; |
5575 | 0 | } |
5576 | | |
5577 | 0 | return CL_SUCCESS; |
5578 | 0 | } |
5579 | | |
5580 | | cl_error_t cl_engine_free(struct cl_engine *engine) |
5581 | 47.1k | { |
5582 | 47.1k | unsigned int i, j; |
5583 | 47.1k | struct cli_matcher *root; |
5584 | | |
5585 | 47.1k | size_t tasks_to_do = 0; |
5586 | 47.1k | size_t tasks_complete = 0; |
5587 | | |
5588 | 47.1k | if (!engine) { |
5589 | 0 | cli_errmsg("cl_free: engine == NULL\n"); |
5590 | 0 | return CL_ENULLARG; |
5591 | 0 | } |
5592 | | |
5593 | 47.1k | #ifdef CL_THREAD_SAFE |
5594 | 47.1k | pthread_mutex_lock(&cli_ref_mutex); |
5595 | 47.1k | #endif |
5596 | | |
5597 | 47.1k | if (engine->refcount) |
5598 | 47.1k | engine->refcount--; |
5599 | | |
5600 | 47.1k | if (engine->refcount) { |
5601 | 0 | #ifdef CL_THREAD_SAFE |
5602 | 0 | pthread_mutex_unlock(&cli_ref_mutex); |
5603 | 0 | #endif |
5604 | 0 | return CL_SUCCESS; |
5605 | 0 | } |
5606 | | |
5607 | 47.1k | if (engine->cb_stats_submit) |
5608 | 0 | engine->cb_stats_submit(engine, engine->stats_data); |
5609 | | |
5610 | 47.1k | #ifdef CL_THREAD_SAFE |
5611 | 47.1k | if (engine->stats_data) { |
5612 | 47.1k | cli_intel_t *intel = (cli_intel_t *)(engine->stats_data); |
5613 | | |
5614 | 47.1k | pthread_mutex_destroy(&(intel->mutex)); |
5615 | 47.1k | } |
5616 | | |
5617 | 47.1k | pthread_mutex_unlock(&cli_ref_mutex); |
5618 | 47.1k | #endif |
5619 | | |
5620 | 47.1k | if (engine->stats_data) |
5621 | 47.1k | free(engine->stats_data); |
5622 | | |
5623 | | /* |
5624 | | * Pre-calculate number of "major" tasks to complete for the progress callback |
5625 | | */ |
5626 | 47.1k | if (engine->root) { |
5627 | 754k | for (i = 0; i < CLI_MTARGETS; i++) { |
5628 | 707k | if ((root = engine->root[i])) { |
5629 | 534k | if (!root->ac_only) { |
5630 | 71.2k | tasks_to_do += 1; // bm root |
5631 | 71.2k | } |
5632 | 534k | tasks_to_do += 1; // ac root |
5633 | 534k | if (root->ac_lsigtable) { |
5634 | 7.55k | tasks_to_do += root->ac_lsigs / 1000; // every 1000 logical sigs |
5635 | 7.55k | tasks_to_do += 1; // ac lsig table |
5636 | 7.55k | } |
5637 | 534k | #if HAVE_PCRE |
5638 | 534k | tasks_to_do += 1; // pcre table |
5639 | 534k | #endif |
5640 | 534k | tasks_to_do += 1; // root mempool |
5641 | 534k | } |
5642 | 707k | } |
5643 | 47.1k | tasks_to_do += 1; // engine root mempool |
5644 | 47.1k | } |
5645 | 47.1k | tasks_to_do += 7; // hdb, mdb, imp, fp, crtmgr, cdb, dbinfo |
5646 | | |
5647 | 47.1k | if (engine->dconf) { |
5648 | 47.1k | if (engine->bcs.all_bcs) { |
5649 | 0 | tasks_to_do += engine->bcs.count; |
5650 | 0 | } |
5651 | 47.1k | tasks_to_do += 1; // bytecode done |
5652 | 47.1k | tasks_to_do += 1; // bytecode hooks |
5653 | 47.1k | tasks_to_do += 1; // phishing cleanup |
5654 | 47.1k | tasks_to_do += 1; // dconf mempool |
5655 | 47.1k | } |
5656 | 47.1k | tasks_to_do += 7; // pwdbs, pua cats, iconcheck, tempdir, cache, engine, ignored |
5657 | | |
5658 | 47.1k | if (engine->test_root) { |
5659 | 1.53k | root = engine->test_root; |
5660 | 1.53k | if (!root->ac_only) { |
5661 | 1.53k | tasks_to_do += 1; // bm root |
5662 | 1.53k | } |
5663 | 1.53k | tasks_to_do += 1; // ac root |
5664 | 1.53k | if (root->ac_lsigtable) { |
5665 | 1.53k | tasks_to_do += root->ac_lsigs / 1000; // every 1000 logical sigs |
5666 | 1.53k | tasks_to_do += 1; // ac lsig table |
5667 | 1.53k | } |
5668 | 1.53k | #if HAVE_PCRE |
5669 | 1.53k | tasks_to_do += 1; // pcre table |
5670 | 1.53k | #endif |
5671 | 1.53k | tasks_to_do += 1; // fuzzy hashmap |
5672 | | |
5673 | 1.53k | tasks_to_do += 1; // engine root mempool |
5674 | 1.53k | } |
5675 | | |
5676 | | #ifdef USE_MPOOL |
5677 | | tasks_to_do += 1; // mempool |
5678 | | #endif |
5679 | | |
5680 | 47.1k | #ifdef HAVE_YARA |
5681 | 47.1k | tasks_to_do += 1; // yara |
5682 | 47.1k | #endif |
5683 | | |
5684 | | /* |
5685 | | * Ok, now actually free everything. |
5686 | | */ |
5687 | 47.1k | #define TASK_COMPLETE() \ |
5688 | 3.20M | if (engine->cb_engine_free_progress) { \ |
5689 | 0 | (void)engine->cb_engine_free_progress( \ |
5690 | 0 | tasks_to_do, \ |
5691 | 0 | ++tasks_complete, \ |
5692 | 0 | engine->cb_engine_free_progress_ctx); \ |
5693 | 0 | } |
5694 | | |
5695 | 47.1k | if (engine->root) { |
5696 | 754k | for (i = 0; i < CLI_MTARGETS; i++) { |
5697 | 707k | if ((root = engine->root[i])) { |
5698 | 534k | if (!root->ac_only) { |
5699 | 71.2k | cli_bm_free(root); |
5700 | 71.2k | TASK_COMPLETE(); |
5701 | 71.2k | } |
5702 | | |
5703 | 534k | cli_ac_free(root); |
5704 | 534k | TASK_COMPLETE(); |
5705 | | |
5706 | 534k | if (root->ac_lsigtable) { |
5707 | 43.4k | for (j = 0; j < root->ac_lsigs; j++) { |
5708 | 35.9k | if (root->ac_lsigtable[j]->type == CLI_LSIG_NORMAL) { |
5709 | 33.7k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[j]->u.logic); |
5710 | 33.7k | } |
5711 | 35.9k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[j]->virname); |
5712 | 35.9k | FREE_TDB(root->ac_lsigtable[j]->tdb); |
5713 | 35.9k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[j]); |
5714 | | |
5715 | 35.9k | TASK_COMPLETE(); |
5716 | 35.9k | } |
5717 | | |
5718 | 7.55k | MPOOL_FREE(engine->mempool, root->ac_lsigtable); |
5719 | 7.55k | TASK_COMPLETE(); |
5720 | 7.55k | } |
5721 | 534k | #if HAVE_PCRE |
5722 | 534k | cli_pcre_freetable(root); |
5723 | 534k | TASK_COMPLETE(); |
5724 | 534k | #endif /* HAVE_PCRE */ |
5725 | 534k | fuzzy_hash_free_hashmap(root->fuzzy_hashmap); |
5726 | 534k | TASK_COMPLETE(); |
5727 | | |
5728 | 534k | MPOOL_FREE(engine->mempool, root); |
5729 | 534k | TASK_COMPLETE(); |
5730 | 534k | } |
5731 | 707k | } |
5732 | 47.1k | MPOOL_FREE(engine->mempool, engine->root); |
5733 | 47.1k | TASK_COMPLETE(); |
5734 | 47.1k | } |
5735 | | |
5736 | 47.1k | if ((root = engine->hm_hdb)) { |
5737 | 2.67k | hm_free(root); |
5738 | 2.67k | MPOOL_FREE(engine->mempool, root); |
5739 | 2.67k | } |
5740 | 47.1k | TASK_COMPLETE(); |
5741 | | |
5742 | 47.1k | if ((root = engine->hm_mdb)) { |
5743 | 2.69k | hm_free(root); |
5744 | 2.69k | MPOOL_FREE(engine->mempool, root); |
5745 | 2.69k | } |
5746 | 47.1k | TASK_COMPLETE(); |
5747 | | |
5748 | 47.1k | if ((root = engine->hm_imp)) { |
5749 | 0 | hm_free(root); |
5750 | 0 | MPOOL_FREE(engine->mempool, root); |
5751 | 0 | } |
5752 | 47.1k | TASK_COMPLETE(); |
5753 | | |
5754 | 47.1k | if ((root = engine->hm_fp)) { |
5755 | 1.30k | hm_free(root); |
5756 | 1.30k | MPOOL_FREE(engine->mempool, root); |
5757 | 1.30k | } |
5758 | 47.1k | TASK_COMPLETE(); |
5759 | | |
5760 | 47.1k | crtmgr_free(&engine->cmgr); |
5761 | 47.1k | TASK_COMPLETE(); |
5762 | | |
5763 | 57.4k | while (engine->cdb) { |
5764 | 10.3k | struct cli_cdb *pt = engine->cdb; |
5765 | 10.3k | engine->cdb = pt->next; |
5766 | 10.3k | if (pt->name.re_magic) |
5767 | 9.43k | cli_regfree(&pt->name); |
5768 | 10.3k | MPOOL_FREE(engine->mempool, pt->res2); |
5769 | 10.3k | MPOOL_FREE(engine->mempool, pt->virname); |
5770 | 10.3k | MPOOL_FREE(engine->mempool, pt); |
5771 | 10.3k | } |
5772 | 47.1k | TASK_COMPLETE(); |
5773 | | |
5774 | 47.1k | while (engine->dbinfo) { |
5775 | 0 | struct cli_dbinfo *pt = engine->dbinfo; |
5776 | 0 | engine->dbinfo = pt->next; |
5777 | 0 | MPOOL_FREE(engine->mempool, pt->name); |
5778 | 0 | MPOOL_FREE(engine->mempool, pt->hash); |
5779 | 0 | if (pt->cvd) |
5780 | 0 | cl_cvdfree(pt->cvd); |
5781 | 0 | MPOOL_FREE(engine->mempool, pt); |
5782 | 0 | } |
5783 | 47.1k | TASK_COMPLETE(); |
5784 | | |
5785 | 47.1k | if (engine->dconf) { |
5786 | 47.1k | if (engine->bcs.all_bcs) { |
5787 | 0 | for (i = 0; i < engine->bcs.count; i++) { |
5788 | 0 | cli_bytecode_destroy(&engine->bcs.all_bcs[i]); |
5789 | 0 | TASK_COMPLETE(); |
5790 | 0 | } |
5791 | 0 | } |
5792 | | |
5793 | 47.1k | cli_bytecode_done(&engine->bcs); |
5794 | 47.1k | TASK_COMPLETE(); |
5795 | | |
5796 | 47.1k | if (engine->bcs.all_bcs) { |
5797 | 0 | free(engine->bcs.all_bcs); |
5798 | 0 | } |
5799 | | |
5800 | 377k | for (i = 0; i < _BC_LAST_HOOK - _BC_START_HOOKS; i++) { |
5801 | 330k | free(engine->hooks[i]); |
5802 | 330k | } |
5803 | 47.1k | TASK_COMPLETE(); |
5804 | | |
5805 | 47.1k | phishing_done(engine); |
5806 | 47.1k | TASK_COMPLETE(); |
5807 | | |
5808 | 47.1k | MPOOL_FREE(engine->mempool, engine->dconf); |
5809 | 47.1k | TASK_COMPLETE(); |
5810 | 47.1k | } |
5811 | | |
5812 | 47.1k | if (engine->pwdbs) { |
5813 | 188k | for (i = 0; i < CLI_PWDB_COUNT; i++) |
5814 | 141k | if (engine->pwdbs[i]) |
5815 | 0 | cli_pwdb_list_free(engine, engine->pwdbs[i]); |
5816 | 47.1k | MPOOL_FREE(engine->mempool, engine->pwdbs); |
5817 | 47.1k | } |
5818 | 47.1k | TASK_COMPLETE(); |
5819 | | |
5820 | 47.1k | if (engine->pua_cats) { |
5821 | 0 | MPOOL_FREE(engine->mempool, engine->pua_cats); |
5822 | 0 | } |
5823 | 47.1k | TASK_COMPLETE(); |
5824 | | |
5825 | 47.1k | if (engine->iconcheck) { |
5826 | 430 | struct icon_matcher *iconcheck = engine->iconcheck; |
5827 | 1.72k | for (i = 0; i < 3; i++) { |
5828 | 1.29k | if (iconcheck->icons[i]) { |
5829 | 8.65k | for (j = 0; j < iconcheck->icon_counts[i]; j++) { |
5830 | 8.30k | struct icomtr *metric = iconcheck->icons[i]; |
5831 | 8.30k | MPOOL_FREE(engine->mempool, metric[j].name); |
5832 | 8.30k | } |
5833 | 348 | MPOOL_FREE(engine->mempool, iconcheck->icons[i]); |
5834 | 348 | } |
5835 | 1.29k | } |
5836 | 430 | if (iconcheck->group_names[0]) { |
5837 | 1.99k | for (i = 0; i < iconcheck->group_counts[0]; i++) |
5838 | 1.64k | MPOOL_FREE(engine->mempool, iconcheck->group_names[0][i]); |
5839 | 348 | MPOOL_FREE(engine->mempool, iconcheck->group_names[0]); |
5840 | 348 | } |
5841 | 430 | if (iconcheck->group_names[1]) { |
5842 | 2.20k | for (i = 0; i < iconcheck->group_counts[1]; i++) |
5843 | 1.85k | MPOOL_FREE(engine->mempool, iconcheck->group_names[1][i]); |
5844 | 348 | MPOOL_FREE(engine->mempool, iconcheck->group_names[1]); |
5845 | 348 | } |
5846 | 430 | MPOOL_FREE(engine->mempool, iconcheck); |
5847 | 430 | } |
5848 | 47.1k | TASK_COMPLETE(); |
5849 | | |
5850 | 47.1k | if (engine->tmpdir) { |
5851 | 0 | MPOOL_FREE(engine->mempool, engine->tmpdir); |
5852 | 0 | } |
5853 | 47.1k | TASK_COMPLETE(); |
5854 | | |
5855 | 47.1k | if (engine->cache) { |
5856 | 47.1k | clean_cache_destroy(engine); |
5857 | 47.1k | } |
5858 | 47.1k | TASK_COMPLETE(); |
5859 | | |
5860 | 47.1k | cli_ftfree(engine); |
5861 | 47.1k | TASK_COMPLETE(); |
5862 | | |
5863 | 47.1k | if (engine->ignored) { |
5864 | 143 | cli_bm_free(engine->ignored); |
5865 | 143 | MPOOL_FREE(engine->mempool, engine->ignored); |
5866 | 143 | } |
5867 | 47.1k | TASK_COMPLETE(); |
5868 | | |
5869 | 47.1k | if (engine->test_root) { |
5870 | 1.53k | root = engine->test_root; |
5871 | 1.53k | if (!root->ac_only) { |
5872 | 1.53k | cli_bm_free(root); |
5873 | 1.53k | TASK_COMPLETE(); |
5874 | 1.53k | } |
5875 | 1.53k | cli_ac_free(root); |
5876 | 1.53k | TASK_COMPLETE(); |
5877 | | |
5878 | 1.53k | if (root->ac_lsigtable) { |
5879 | 3.54k | for (i = 0; i < root->ac_lsigs; i++) { |
5880 | 2.01k | if (root->ac_lsigtable[i]->type == CLI_LSIG_NORMAL) { |
5881 | 0 | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]->u.logic); |
5882 | 0 | } |
5883 | 2.01k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]->virname); |
5884 | 2.01k | FREE_TDB(root->ac_lsigtable[i]->tdb); |
5885 | 2.01k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]); |
5886 | | |
5887 | 2.01k | TASK_COMPLETE(); |
5888 | 2.01k | } |
5889 | 1.53k | MPOOL_FREE(engine->mempool, root->ac_lsigtable); |
5890 | 1.53k | TASK_COMPLETE(); |
5891 | 1.53k | } |
5892 | 1.53k | #if HAVE_PCRE |
5893 | 1.53k | cli_pcre_freetable(root); |
5894 | 1.53k | TASK_COMPLETE(); |
5895 | 1.53k | #endif /* HAVE_PCRE */ |
5896 | 1.53k | MPOOL_FREE(engine->mempool, root); |
5897 | 1.53k | TASK_COMPLETE(); |
5898 | 1.53k | } |
5899 | | |
5900 | | #ifdef USE_MPOOL |
5901 | | if (engine->mempool) mpool_destroy(engine->mempool); |
5902 | | TASK_COMPLETE(); |
5903 | | #endif |
5904 | | |
5905 | 47.1k | #ifdef HAVE_YARA |
5906 | 47.1k | cli_yara_free(engine); |
5907 | 47.1k | TASK_COMPLETE(); |
5908 | 47.1k | #endif |
5909 | | |
5910 | 47.1k | free(engine); |
5911 | | |
5912 | 47.1k | return CL_SUCCESS; |
5913 | 47.1k | } |
5914 | | |
5915 | | cl_error_t cl_engine_compile(struct cl_engine *engine) |
5916 | 20.6k | { |
5917 | 20.6k | unsigned int i; |
5918 | 20.6k | cl_error_t ret; |
5919 | 20.6k | struct cli_matcher *root; |
5920 | | |
5921 | 20.6k | size_t tasks_to_do = 0; |
5922 | 20.6k | size_t tasks_complete = 0; |
5923 | | |
5924 | 20.6k | if (!engine) { |
5925 | 0 | return CL_ENULLARG; |
5926 | 0 | } |
5927 | | |
5928 | | /* |
5929 | | * Pre-calculate number of "major" tasks to complete for the progress callback |
5930 | | */ |
5931 | 20.6k | #ifdef HAVE_YARA |
5932 | 20.6k | tasks_to_do += 1; // yara free |
5933 | 20.6k | #endif |
5934 | 20.6k | tasks_to_do += 1; // load ftm |
5935 | | |
5936 | 20.6k | tasks_to_do += 1; // load pwdb |
5937 | | |
5938 | 330k | for (i = 0; i < CLI_MTARGETS; i++) { |
5939 | 309k | if ((root = engine->root[i])) { |
5940 | 147k | tasks_to_do += 1; // build ac trie |
5941 | 147k | #if HAVE_PCRE |
5942 | 147k | tasks_to_do += 1; // compile pcre regex |
5943 | 147k | #endif |
5944 | 147k | } |
5945 | 309k | } |
5946 | 20.6k | tasks_to_do += 1; // flush hdb |
5947 | 20.6k | tasks_to_do += 1; // flush mdb |
5948 | 20.6k | tasks_to_do += 1; // flush imp |
5949 | 20.6k | tasks_to_do += 1; // flush fp |
5950 | 20.6k | tasks_to_do += 1; // build allow list regex list |
5951 | 20.6k | tasks_to_do += 1; // build domain list regex list |
5952 | 20.6k | if (engine->ignored) { |
5953 | 579 | tasks_to_do += 1; // free list of ignored sigs (no longer needed) |
5954 | 579 | } |
5955 | 20.6k | if (engine->test_root) { |
5956 | 786 | tasks_to_do += 1; // free test root (no longer needed) |
5957 | 786 | } |
5958 | 20.6k | tasks_to_do += 1; // prepare bytecode sigs |
5959 | | // Note: Adding a task to compile each bytecode is doable |
5960 | | // but would be painful to implement. For now, just |
5961 | | // having it all as one task should be good enough. |
5962 | | |
5963 | | /* |
5964 | | * Ok, now actually compile everything. |
5965 | | */ |
5966 | 20.6k | #undef TASK_COMPLETE |
5967 | 20.6k | #define TASK_COMPLETE() \ |
5968 | 825k | if (engine->cb_engine_compile_progress) { \ |
5969 | 0 | (void)engine->cb_engine_compile_progress( \ |
5970 | 0 | tasks_to_do, \ |
5971 | 0 | ++tasks_complete, \ |
5972 | 0 | engine->cb_engine_compile_progress_ctx); \ |
5973 | 0 | } |
5974 | | |
5975 | 20.6k | #ifdef HAVE_YARA |
5976 | | /* Free YARA hash tables - only needed for parse and load */ |
5977 | 20.6k | if (engine->yara_global != NULL) { |
5978 | 20.6k | if (engine->yara_global->rules_table) |
5979 | 20.6k | yr_hash_table_destroy(engine->yara_global->rules_table, NULL); |
5980 | 20.6k | if (engine->yara_global->objects_table) |
5981 | 20.6k | yr_hash_table_destroy(engine->yara_global->objects_table, NULL); |
5982 | 20.6k | engine->yara_global->rules_table = engine->yara_global->objects_table = NULL; |
5983 | 20.6k | } |
5984 | 20.6k | TASK_COMPLETE(); |
5985 | 20.6k | #endif |
5986 | | |
5987 | 20.6k | if (!engine->ftypes) |
5988 | 20.5k | if ((ret = cli_loadftm(NULL, engine, 0, 1, NULL))) |
5989 | 0 | return ret; |
5990 | 20.6k | TASK_COMPLETE(); |
5991 | | |
5992 | | /* handle default passwords */ |
5993 | 20.6k | if (!engine->pwdbs[0] && !engine->pwdbs[1] && !engine->pwdbs[2]) |
5994 | 20.6k | if ((ret = cli_loadpwdb(NULL, engine, 0, 1, NULL))) |
5995 | 0 | return ret; |
5996 | 20.6k | TASK_COMPLETE(); |
5997 | | |
5998 | 329k | for (i = 0; i < CLI_MTARGETS; i++) { |
5999 | 309k | if ((root = engine->root[i])) { |
6000 | 309k | if ((ret = cli_ac_buildtrie(root))) |
6001 | 0 | return ret; |
6002 | 309k | TASK_COMPLETE(); |
6003 | 309k | #if HAVE_PCRE |
6004 | 309k | if ((ret = cli_pcre_build(root, engine->pcre_match_limit, engine->pcre_recmatch_limit, engine->dconf))) |
6005 | 47 | return ret; |
6006 | 309k | TASK_COMPLETE(); |
6007 | | |
6008 | 309k | cli_dbgmsg("Matcher[%u]: %s: AC sigs: %u (reloff: %u, absoff: %u) BM sigs: %u (reloff: %u, absoff: %u) PCREs: %u (reloff: %u, absoff: %u) maxpatlen %u %s\n", i, cli_mtargets[i].name, root->ac_patterns, root->ac_reloff_num, root->ac_absoff_num, root->bm_patterns, root->bm_reloff_num, root->bm_absoff_num, root->pcre_metas, root->pcre_reloff_num, root->pcre_absoff_num, root->maxpatlen, root->ac_only ? "(ac_only mode)" : ""); |
6009 | | #else |
6010 | | cli_dbgmsg("Matcher[%u]: %s: AC sigs: %u (reloff: %u, absoff: %u) BM sigs: %u (reloff: %u, absoff: %u) maxpatlen %u PCREs: 0 (disabled) %s\n", i, cli_mtargets[i].name, root->ac_patterns, root->ac_reloff_num, root->ac_absoff_num, root->bm_patterns, root->bm_reloff_num, root->bm_absoff_num, root->maxpatlen, root->ac_only ? "(ac_only mode)" : ""); |
6011 | | #endif |
6012 | 309k | } |
6013 | 309k | } |
6014 | | |
6015 | 20.6k | if (engine->hm_hdb) |
6016 | 1.79k | hm_flush(engine->hm_hdb); |
6017 | 20.6k | TASK_COMPLETE(); |
6018 | | |
6019 | 20.6k | if (engine->hm_mdb) |
6020 | 1.78k | hm_flush(engine->hm_mdb); |
6021 | 20.6k | TASK_COMPLETE(); |
6022 | | |
6023 | 20.6k | if (engine->hm_imp) |
6024 | 0 | hm_flush(engine->hm_imp); |
6025 | 20.6k | TASK_COMPLETE(); |
6026 | | |
6027 | 20.6k | if (engine->hm_fp) |
6028 | 857 | hm_flush(engine->hm_fp); |
6029 | 20.6k | TASK_COMPLETE(); |
6030 | | |
6031 | 20.6k | if ((ret = cli_build_regex_list(engine->allow_list_matcher))) { |
6032 | 0 | return ret; |
6033 | 0 | } |
6034 | 20.6k | TASK_COMPLETE(); |
6035 | | |
6036 | 20.6k | if ((ret = cli_build_regex_list(engine->domain_list_matcher))) { |
6037 | 0 | return ret; |
6038 | 0 | } |
6039 | 20.6k | TASK_COMPLETE(); |
6040 | | |
6041 | 20.6k | if (engine->ignored) { |
6042 | 579 | cli_bm_free(engine->ignored); |
6043 | 579 | MPOOL_FREE(engine->mempool, engine->ignored); |
6044 | 579 | engine->ignored = NULL; |
6045 | 579 | TASK_COMPLETE(); |
6046 | 579 | } |
6047 | | |
6048 | 20.6k | if (engine->test_root) { |
6049 | 786 | root = engine->test_root; |
6050 | 786 | if (!root->ac_only) |
6051 | 786 | cli_bm_free(root); |
6052 | 786 | cli_ac_free(root); |
6053 | 786 | if (root->ac_lsigtable) { |
6054 | 1.83k | for (i = 0; i < root->ac_lsigs; i++) { |
6055 | 1.04k | if (root->ac_lsigtable[i]->type == CLI_LSIG_NORMAL) { |
6056 | 0 | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]->u.logic); |
6057 | 0 | } |
6058 | 1.04k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]->virname); |
6059 | 1.04k | FREE_TDB(root->ac_lsigtable[i]->tdb); |
6060 | 1.04k | MPOOL_FREE(engine->mempool, root->ac_lsigtable[i]); |
6061 | 1.04k | } |
6062 | 786 | MPOOL_FREE(engine->mempool, root->ac_lsigtable); |
6063 | 786 | } |
6064 | 786 | #if HAVE_PCRE |
6065 | 786 | cli_pcre_freetable(root); |
6066 | 786 | #endif /* HAVE_PCRE */ |
6067 | 786 | MPOOL_FREE(engine->mempool, root); |
6068 | 786 | engine->test_root = NULL; |
6069 | 786 | TASK_COMPLETE(); |
6070 | 786 | } |
6071 | | |
6072 | 20.6k | cli_dconf_print(engine->dconf); |
6073 | 20.6k | MPOOL_FLUSH(engine->mempool); |
6074 | | |
6075 | | /* Compile bytecode */ |
6076 | 20.6k | if (CL_SUCCESS != (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode))) { |
6077 | 0 | cli_errmsg("Unable to compile/load bytecode: %s\n", cl_strerror(ret)); |
6078 | 0 | return ret; |
6079 | 0 | } |
6080 | 20.6k | TASK_COMPLETE(); |
6081 | | |
6082 | 20.6k | engine->dboptions |= CL_DB_COMPILED; |
6083 | 20.6k | return CL_SUCCESS; |
6084 | 20.6k | } |
6085 | | |
6086 | | cl_error_t cl_engine_addref(struct cl_engine *engine) |
6087 | 0 | { |
6088 | 0 | if (!engine) { |
6089 | 0 | cli_errmsg("cl_engine_addref: engine == NULL\n"); |
6090 | 0 | return CL_ENULLARG; |
6091 | 0 | } |
6092 | | |
6093 | 0 | #ifdef CL_THREAD_SAFE |
6094 | 0 | pthread_mutex_lock(&cli_ref_mutex); |
6095 | 0 | #endif |
6096 | |
|
6097 | 0 | engine->refcount++; |
6098 | |
|
6099 | 0 | #ifdef CL_THREAD_SAFE |
6100 | 0 | pthread_mutex_unlock(&cli_ref_mutex); |
6101 | 0 | #endif |
6102 | |
|
6103 | 0 | return CL_SUCCESS; |
6104 | 0 | } |
6105 | | |
6106 | | static int countentries(const char *dbname, unsigned int *sigs) |
6107 | 0 | { |
6108 | 0 | char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1]; |
6109 | 0 | FILE *fs; |
6110 | 0 | unsigned int entry = 0; |
6111 | |
|
6112 | 0 | fs = fopen(dbname, "r"); |
6113 | 0 | if (!fs) { |
6114 | 0 | cli_errmsg("countentries: Can't open file %s\n", dbname); |
6115 | 0 | return CL_EOPEN; |
6116 | 0 | } |
6117 | 0 | while (fgets(buffer, sizeof(buffer), fs)) { |
6118 | 0 | if (buffer[0] == '#') |
6119 | 0 | continue; |
6120 | 0 | entry++; |
6121 | 0 | } |
6122 | 0 | fclose(fs); |
6123 | 0 | *sigs += entry; |
6124 | 0 | return CL_SUCCESS; |
6125 | 0 | } |
6126 | | |
6127 | | static int countsigs(const char *dbname, unsigned int options, unsigned int *sigs) |
6128 | 0 | { |
6129 | 0 | if ((cli_strbcasestr(dbname, ".cvd") || cli_strbcasestr(dbname, ".cld"))) { |
6130 | 0 | if (options & CL_COUNTSIGS_OFFICIAL) { |
6131 | 0 | struct cl_cvd *cvd = cl_cvdhead(dbname); |
6132 | 0 | if (!cvd) { |
6133 | 0 | cli_errmsg("countsigs: Can't parse %s\n", dbname); |
6134 | 0 | return CL_ECVD; |
6135 | 0 | } |
6136 | 0 | *sigs += cvd->sigs; |
6137 | 0 | cl_cvdfree(cvd); |
6138 | 0 | } |
6139 | 0 | } else if ((cli_strbcasestr(dbname, ".cud"))) { |
6140 | 0 | if (options & CL_COUNTSIGS_UNOFFICIAL) { |
6141 | 0 | struct cl_cvd *cvd = cl_cvdhead(dbname); |
6142 | 0 | if (!cvd) { |
6143 | 0 | cli_errmsg("countsigs: Can't parse %s\n", dbname); |
6144 | 0 | return CL_ECVD; |
6145 | 0 | } |
6146 | 0 | *sigs += cvd->sigs; |
6147 | 0 | cl_cvdfree(cvd); |
6148 | 0 | } |
6149 | 0 | } else if (cli_strbcasestr(dbname, ".cbc")) { |
6150 | 0 | if (options & CL_COUNTSIGS_UNOFFICIAL) |
6151 | 0 | (*sigs)++; |
6152 | |
|
6153 | 0 | } else if (cli_strbcasestr(dbname, ".wdb") || cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".sfp") || cli_strbcasestr(dbname, ".ign") || cli_strbcasestr(dbname, ".ign2") || cli_strbcasestr(dbname, ".ftm") || cli_strbcasestr(dbname, ".cfg") || cli_strbcasestr(dbname, ".cat")) { |
6154 | | /* ignore allow list/FP signatures and metadata files */ |
6155 | | |
6156 | | // TODO .crb sigs can contain both allow/trust and block signatures. |
6157 | | // For now we will just include both in the count by not excluding this |
6158 | | // sig type here, but in the future we could extract just the number of |
6159 | | // block list rules manually so that the count is more accurate. |
6160 | | |
6161 | | // NOTE: We implicitly ignore .info files because they aren't currently |
6162 | | // in the list of ones checked for by CLI_DBEXT |
6163 | 0 | } else if ((options & CL_COUNTSIGS_UNOFFICIAL) && CLI_DBEXT(dbname)) { |
6164 | 0 | return countentries(dbname, sigs); |
6165 | 0 | } |
6166 | | |
6167 | 0 | return CL_SUCCESS; |
6168 | 0 | } |
6169 | | |
6170 | | cl_error_t cl_countsigs(const char *path, unsigned int countoptions, unsigned int *sigs) |
6171 | 0 | { |
6172 | 0 | STATBUF sb; |
6173 | 0 | char fname[1024]; |
6174 | 0 | struct dirent *dent; |
6175 | 0 | DIR *dd; |
6176 | 0 | cl_error_t ret; |
6177 | |
|
6178 | 0 | if (!sigs) |
6179 | 0 | return CL_ENULLARG; |
6180 | | |
6181 | 0 | if (CLAMSTAT(path, &sb) == -1) { |
6182 | 0 | cli_errmsg("cl_countsigs: Can't stat %s\n", path); |
6183 | 0 | return CL_ESTAT; |
6184 | 0 | } |
6185 | | |
6186 | 0 | if ((sb.st_mode & S_IFMT) == S_IFREG) { |
6187 | 0 | return countsigs(path, countoptions, sigs); |
6188 | |
|
6189 | 0 | } else if ((sb.st_mode & S_IFMT) == S_IFDIR) { |
6190 | 0 | if ((dd = opendir(path)) == NULL) { |
6191 | 0 | cli_errmsg("cl_countsigs: Can't open directory %s\n", path); |
6192 | 0 | return CL_EOPEN; |
6193 | 0 | } |
6194 | 0 | while ((dent = readdir(dd))) { |
6195 | 0 | if (dent->d_ino) { |
6196 | 0 | if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) { |
6197 | 0 | snprintf(fname, sizeof(fname), "%s" PATHSEP "%s", path, dent->d_name); |
6198 | 0 | fname[sizeof(fname) - 1] = 0; |
6199 | 0 | ret = countsigs(fname, countoptions, sigs); |
6200 | 0 | if (ret != CL_SUCCESS) { |
6201 | 0 | closedir(dd); |
6202 | 0 | return ret; |
6203 | 0 | } |
6204 | 0 | } |
6205 | 0 | } |
6206 | 0 | } |
6207 | 0 | closedir(dd); |
6208 | 0 | } else { |
6209 | 0 | cli_errmsg("cl_countsigs: Unsupported file type\n"); |
6210 | 0 | return CL_EARG; |
6211 | 0 | } |
6212 | | |
6213 | 0 | return CL_SUCCESS; |
6214 | 0 | } |