Coverage Report

Created: 2023-12-08 06:48

/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
}