source: clamav/vendor/current/libclamav/readdb.c@ 189

Last change on this file since 189 was 189, checked in by Yuri Dario, 14 years ago

clamav: initial clamav 0.96.4 import.

File size: 86.5 KB
Line 
1/*
2 * Copyright (C) 2007-2010 Sourcefire, Inc.
3 *
4 * Authors: Tomasz Kojm
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 * MA 02110-1301, USA.
19 */
20
21#if HAVE_CONFIG_H
22#include "clamav-config.h"
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <ctype.h>
29#ifdef HAVE_UNISTD_H
30#include <unistd.h>
31#endif
32#include <dirent.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#ifdef HAVE_SYS_PARAM_H
36#include <sys/param.h>
37#endif
38#include <fcntl.h>
39#include <zlib.h>
40#include <errno.h>
41
42#include "clamav.h"
43#include "cvd.h"
44#ifdef HAVE_STRINGS_H
45#include <strings.h>
46#endif
47#include "matcher-ac.h"
48#include "matcher-bm.h"
49#include "matcher-md5.h"
50#include "matcher.h"
51#include "others.h"
52#include "str.h"
53#include "dconf.h"
54#include "filetypes.h"
55#include "filetypes_int.h"
56#include "readdb.h"
57#include "cltypes.h"
58#include "default.h"
59#include "md5.h"
60#include "sha256.h"
61#include "dsig.h"
62
63#include "phishcheck.h"
64#include "phish_whitelist.h"
65#include "phish_domaincheck_db.h"
66#include "regex_list.h"
67#include "hashtab.h"
68
69#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
70#include <limits.h>
71#include <stddef.h>
72#endif
73
74#include "mpool.h"
75#include "bytecode.h"
76#include "bytecode_api.h"
77#include "bytecode_priv.h"
78#include "cache.h"
79#ifdef CL_THREAD_SAFE
80# include <pthread.h>
81static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
82#endif
83
84char *cli_virname(char *virname, unsigned int official)
85{
86 char *newname, *pt;
87
88
89 if(!virname)
90 return NULL;
91
92 if((pt = strstr(virname, " (Clam)")))
93 *pt='\0';
94
95 if(!virname[0]) {
96 cli_errmsg("cli_virname: Empty virus name\n");
97 return NULL;
98 }
99
100 if(official)
101 return cli_strdup(virname);
102
103 newname = (char *) cli_malloc(strlen(virname) + 11 + 1);
104 if(!newname) {
105 cli_errmsg("cli_virname: Can't allocate memory for newname\n");
106 return NULL;
107 }
108 sprintf(newname, "%s.UNOFFICIAL", virname);
109 return newname;
110}
111
112int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hexsig, uint16_t rtype, uint16_t type, const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
113{
114 struct cli_bm_patt *bm_new;
115 char *pt, *hexcpy, *start, *n;
116 int ret, asterisk = 0;
117 unsigned int i, j, hexlen, parts = 0;
118 int mindist = 0, maxdist = 0, error = 0;
119
120
121 hexlen = strlen(hexsig);
122 if (hexsig[0] == '$') {
123 /* macro */
124 unsigned smin, smax, tid;
125 struct cli_ac_patt *patt;
126 if (hexsig[hexlen-1] != '$') {
127 cli_errmsg("cli_parseadd(): missing terminator $\n");
128 return CL_EMALFDB;
129 }
130 if (!lsigid) {
131 cli_errmsg("cli_parseadd(): macro signatures only valid inside logical signatures\n");
132 return CL_EMALFDB;
133 }
134 if (sscanf(hexsig,"${%u-%u}%u$",
135 &smin, &smax, &tid) != 3) {
136 cli_errmsg("cli_parseadd(): invalid macro signature format\n");
137 return CL_EMALFDB;
138 }
139 if (tid >= 32) {
140 cli_errmsg("cli_parseadd(): only 32 macro groups are supported\n");
141 return CL_EMALFDB;
142 }
143 patt = mpool_calloc(root->mempool, 1, sizeof(*patt));
144 if (!patt)
145 return CL_EMEM;
146 /* this is not a pattern that will be matched by AC itself, rather it is a
147 * pattern checked by the lsig code */
148 patt->ch_mindist[0] = smin;
149 patt->ch_maxdist[0] = smax;
150 patt->sigid = tid;
151 patt->length = root->ac_mindepth;
152 /* dummy */
153 patt->pattern = mpool_calloc(root->mempool, patt->length, sizeof(*patt->pattern));
154 if (patt->pattern) {
155 free(patt);
156 return CL_EMEM;
157 }
158 if ((ret = cli_ac_addpatt(root, patt))) {
159 mpool_free(root->mempool, patt->pattern);
160 free(patt);
161 return ret;
162 }
163 return CL_SUCCESS;
164 }
165 if(strchr(hexsig, '{')) {
166
167 root->ac_partsigs++;
168
169 if(!(hexcpy = cli_strdup(hexsig)))
170 return CL_EMEM;
171
172 for(i = 0; i < hexlen; i++)
173 if(hexsig[i] == '{' || hexsig[i] == '*')
174 parts++;
175
176 if(parts)
177 parts++;
178
179 start = pt = hexcpy;
180 for(i = 1; i <= parts; i++) {
181
182 if(i != parts) {
183 for(j = 0; j < strlen(start); j++) {
184 if(start[j] == '{') {
185 asterisk = 0;
186 pt = start + j;
187 break;
188 }
189 if(start[j] == '*') {
190 asterisk = 1;
191 pt = start + j;
192 break;
193 }
194 }
195 *pt++ = 0;
196 }
197
198 if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
199 cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
200 error = 1;
201 break;
202 }
203
204 if(i == parts)
205 break;
206
207 mindist = maxdist = 0;
208
209 if(asterisk) {
210 start = pt;
211 continue;
212 }
213
214 if(!(start = strchr(pt, '}'))) {
215 error = 1;
216 break;
217 }
218 *start++ = 0;
219
220 if(!pt) {
221 error = 1;
222 break;
223 }
224
225 if(!strchr(pt, '-')) {
226 if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
227 error = 1;
228 break;
229 }
230 } else {
231 if((n = cli_strtok(pt, 0, "-"))) {
232 if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
233 error = 1;
234 free(n);
235 break;
236 }
237 free(n);
238 }
239
240 if((n = cli_strtok(pt, 1, "-"))) {
241 if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
242 error = 1;
243 free(n);
244 break;
245 }
246 free(n);
247 }
248
249 if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
250 error = 1;
251 free(n);
252 break;
253 }
254 }
255 }
256
257 free(hexcpy);
258 if(error)
259 return CL_EMALFDB;
260
261 } else if(strchr(hexsig, '*')) {
262 root->ac_partsigs++;
263
264 for(i = 0; i < hexlen; i++)
265 if(hexsig[i] == '*')
266 parts++;
267
268 if(parts)
269 parts++;
270
271 for(i = 1; i <= parts; i++) {
272 if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
273 cli_errmsg("Can't extract part %d of partial signature.\n", i);
274 return CL_EMALFDB;
275 }
276
277 if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
278 cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
279 free(pt);
280 return ret;
281 }
282
283 free(pt);
284 }
285
286 } else if(root->ac_only || type || lsigid || strpbrk(hexsig, "?([") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strchr(offset, '$')) {
287 if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
288 cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
289 return ret;
290 }
291
292 } else {
293 bm_new = (struct cli_bm_patt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_bm_patt));
294 if(!bm_new)
295 return CL_EMEM;
296 bm_new->pattern = (unsigned char *) cli_mpool_hex2str(root->mempool, hexsig);
297 if(!bm_new->pattern) {
298 mpool_free(root->mempool, bm_new);
299 return CL_EMALFDB;
300 }
301 bm_new->length = hexlen / 2;
302
303 bm_new->virname = cli_mpool_virname(root->mempool, virname, options & CL_DB_OFFICIAL);
304 if(!bm_new->virname) {
305 mpool_free(root->mempool, bm_new->pattern);
306 mpool_free(root->mempool, bm_new);
307 return CL_EMEM;
308 }
309
310 if(bm_new->length > root->maxpatlen) {
311 root->maxpatlen = bm_new->length;
312 }
313
314 if((ret = cli_bm_addpatt(root, bm_new, offset))) {
315 cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
316 mpool_free(root->mempool, bm_new->pattern);
317 mpool_free(root->mempool, bm_new->virname);
318 mpool_free(root->mempool, bm_new);
319 return ret;
320 }
321 }
322
323 return CL_SUCCESS;
324}
325
326int cli_initroots(struct cl_engine *engine, unsigned int options)
327{
328 int i, ret;
329 struct cli_matcher *root;
330
331
332 for(i = 0; i < CLI_MTARGETS; i++) {
333 if(!engine->root[i]) {
334 cli_dbgmsg("Initializing engine->root[%d]\n", i);
335 root = engine->root[i] = (struct cli_matcher *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_matcher));
336 if(!root) {
337 cli_errmsg("cli_initroots: Can't allocate memory for cli_matcher\n");
338 return CL_EMEM;
339 }
340#ifdef USE_MPOOL
341 root->mempool = engine->mempool;
342#endif
343 root->type = i;
344 if(cli_mtargets[i].ac_only || engine->ac_only)
345 root->ac_only = 1;
346
347 cli_dbgmsg("Initialising AC pattern matcher of root[%d]\n", i);
348 if((ret = cli_ac_init(root, engine->ac_mindepth, engine->ac_maxdepth, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
349 /* no need to free previously allocated memory here */
350 cli_errmsg("cli_initroots: Can't initialise AC pattern matcher\n");
351 return ret;
352 }
353
354 if(!root->ac_only) {
355 cli_dbgmsg("cli_initroots: Initializing BM tables of root[%d]\n", i);
356 if((ret = cli_bm_init(root))) {
357 cli_errmsg("cli_initroots: Can't initialise BM pattern matcher\n");
358 return ret;
359 }
360 }
361 }
362 }
363 engine->root[1]->bm_offmode = 1; /* BM offset mode for PE files */
364 return CL_SUCCESS;
365}
366
367char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio)
368{
369 if(fs)
370 return fgets(buff, size, fs);
371
372 if(dbio->usebuf) {
373 int bread;
374 char *nl;
375
376 while(1) {
377 if(!dbio->bufpt) {
378 if(!dbio->size)
379 return NULL;
380
381 if(dbio->gzs) {
382 bread = gzread(dbio->gzs, dbio->readpt, dbio->readsize);
383 if(bread == -1) {
384 cli_errmsg("cli_dbgets: gzread() failed\n");
385 return NULL;
386 }
387 } else {
388 bread = fread(dbio->readpt, 1, dbio->readsize, dbio->fs);
389 if(!bread && ferror(dbio->fs)) {
390 cli_errmsg("cli_dbgets: gzread() failed\n");
391 return NULL;
392 }
393 }
394 if(!bread)
395 return NULL;
396 dbio->readpt[bread] = 0;
397 dbio->bufpt = dbio->buf;
398 dbio->size -= bread;
399 dbio->bread += bread;
400 sha256_update(&dbio->sha256ctx, dbio->readpt, bread);
401 }
402 nl = strchr(dbio->bufpt, '\n');
403 if(nl) {
404 if(nl - dbio->bufpt >= size) {
405 cli_errmsg("cli_dbgets: Line too long for provided buffer\n");
406 return NULL;
407 }
408 strncpy(buff, dbio->bufpt, nl - dbio->bufpt);
409 buff[nl - dbio->bufpt] = 0;
410 if(nl < dbio->buf + dbio->bufsize) {
411 dbio->bufpt = ++nl;
412 } else {
413 dbio->bufpt = NULL;
414 dbio->readpt = dbio->buf;
415 dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
416 }
417 return buff;
418 } else {
419 unsigned int remain = dbio->buf + dbio->bufsize - 1 - dbio->bufpt;
420
421 if(dbio->bufpt == dbio->buf) {
422 cli_errmsg("cli_dbgets: Invalid data or internal buffer too small\n");
423 return NULL;
424 }
425 memmove(dbio->buf, dbio->bufpt, remain);
426 dbio->readpt = dbio->buf + remain;
427 dbio->readsize = dbio->bufsize - remain;
428 dbio->readsize = dbio->size < dbio->bufsize - remain ? dbio->size : dbio->bufsize - remain - 1;
429 dbio->bufpt = NULL;
430 }
431 }
432 } else { /* use gzgets/fgets */
433 char *pt;
434 unsigned int bs;
435
436 if(!dbio->size)
437 return NULL;
438
439 bs = dbio->size < size ? dbio->size + 1 : size;
440 if(dbio->gzs)
441 pt = gzgets(dbio->gzs, buff, bs);
442 else
443 pt = fgets(buff, bs, dbio->fs);
444
445 if(!pt) {
446 cli_errmsg("cli_dbgets: Preliminary end of data\n");
447 return pt;
448 }
449 bs = strlen(buff);
450 dbio->size -= bs;
451 dbio->bread += bs;
452 sha256_update(&dbio->sha256ctx, buff, bs);
453 return pt;
454 }
455}
456
457static int cli_chkign(const struct cli_matcher *ignored, const char *signame, const char *entry)
458{
459 const char *md5_expected = NULL;
460 cli_md5_ctx md5ctx;
461 unsigned char digest[16];
462
463 if(!ignored || !signame || !entry)
464 return 0;
465
466 if(cli_bm_scanbuff((const unsigned char *) signame, strlen(signame), &md5_expected, NULL, ignored, 0, NULL, NULL) == CL_VIRUS) {
467 if(md5_expected) {
468 cli_md5_init(&md5ctx);
469 cli_md5_update(&md5ctx, entry, strlen(entry));
470 cli_md5_final(digest, &md5ctx);
471 if(memcmp(digest, (const unsigned char *) md5_expected, 16))
472 return 0;
473 }
474 cli_dbgmsg("Ignoring signature %s\n", signame);
475 return 1;
476 }
477
478 return 0;
479}
480
481static int cli_chkpua(const char *signame, const char *pua_cats, unsigned int options)
482{
483 char cat[32], *pt;
484 const char *sig;
485 int ret;
486
487 if(strncmp(signame, "PUA.", 4)) {
488 cli_dbgmsg("Skipping signature %s - no PUA prefix\n", signame);
489 return 1;
490 }
491 sig = signame + 3;
492 if(!(pt = strchr(sig + 1, '.'))) {
493 cli_dbgmsg("Skipping signature %s - bad syntax\n", signame);
494 return 1;
495 }
496
497 if((unsigned int) (pt - sig + 2) > sizeof(cat)) {
498 cli_dbgmsg("Skipping signature %s - too long category name\n", signame);
499 return 1;
500 }
501
502 strncpy(cat, sig, pt - signame + 1);
503 cat[pt - sig + 1] = 0;
504 pt = strstr(pua_cats, cat);
505
506 if(options & CL_DB_PUA_INCLUDE)
507 ret = pt ? 0 : 1;
508 else
509 ret = pt ? 1 : 0;
510
511 if(ret)
512 cli_dbgmsg("Skipping PUA signature %s - excluded category\n", signame);
513
514 return ret;
515}
516
517static int cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
518{
519 char buffer[FILEBUFF], *buffer_cpy = NULL, *pt, *start;
520 unsigned int line = 0, sigs = 0;
521 int ret = 0;
522 struct cli_matcher *root;
523
524
525 if((ret = cli_initroots(engine, options)))
526 return ret;
527
528 root = engine->root[0];
529
530 if(engine->ignored)
531 if(!(buffer_cpy = cli_malloc(FILEBUFF)))
532 return CL_EMEM;
533
534 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
535 line++;
536 cli_chomp(buffer);
537 if(engine->ignored)
538 strcpy(buffer_cpy, buffer);
539
540 pt = strchr(buffer, '=');
541 if(!pt) {
542 cli_errmsg("Malformed pattern line %d\n", line);
543 ret = CL_EMALFDB;
544 break;
545 }
546
547 start = buffer;
548 *pt++ = 0;
549
550 if(engine->ignored && cli_chkign(engine->ignored, start, buffer_cpy))
551 continue;
552
553 if(engine->cb_sigload && engine->cb_sigload("db", start, engine->cb_sigload_ctx)) {
554 cli_dbgmsg("cli_loaddb: skipping %s due to callback\n", start);
555 continue;
556 }
557
558 if(*pt == '=') continue;
559
560 if((ret = cli_parse_add(root, start, pt, 0, 0, "*", 0, NULL, options))) {
561 ret = CL_EMALFDB;
562 break;
563 }
564 sigs++;
565 }
566
567 if(engine->ignored)
568 free(buffer_cpy);
569
570 if(!line) {
571 cli_errmsg("Empty database file\n");
572 return CL_EMALFDB;
573 }
574
575 if(ret) {
576 cli_errmsg("Problem parsing database at line %d\n", line);
577 return ret;
578 }
579
580 if(signo)
581 *signo += sigs;
582
583 return CL_SUCCESS;
584}
585
586#define ICO_TOKENS 4
587static int cli_loadidb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
588{
589 const char *tokens[ICO_TOKENS + 1];
590 char buffer[FILEBUFF], *buffer_cpy = NULL;
591 uint8_t *hash;
592 int ret = CL_SUCCESS;
593 unsigned int line = 0, sigs = 0, tokens_count, i, size, enginesize;
594 struct icomtr *metric;
595 struct icon_matcher *matcher;
596
597
598 if(!(matcher = (struct icon_matcher *)mpool_calloc(engine->mempool, sizeof(*matcher),1)))
599 return CL_EMEM;
600
601 if(engine->ignored)
602 if(!(buffer_cpy = cli_malloc(FILEBUFF))) {
603 mpool_free(engine->mempool, matcher);
604 return CL_EMEM;
605 }
606
607 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
608 line++;
609 if(buffer[0] == '#')
610 continue;
611
612 cli_chomp(buffer);
613 if(engine->ignored)
614 strcpy(buffer_cpy, buffer);
615
616 tokens_count = cli_strtokenize(buffer, ':', ICO_TOKENS + 1, tokens);
617 if(tokens_count != ICO_TOKENS) {
618 cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong token count)\n", line);
619 ret = CL_EMALFDB;
620 break;
621 }
622
623 if(strlen(tokens[3]) != 124) {
624 cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong length)\n", line);
625 ret = CL_EMALFDB;
626 break;
627 }
628
629 if(engine->ignored && cli_chkign(engine->ignored, tokens[0], buffer_cpy))
630 continue;
631
632 if(engine->cb_sigload && engine->cb_sigload("idb", tokens[0], engine->cb_sigload_ctx)) {
633 cli_dbgmsg("cli_loadidb: skipping %s due to callback\n", tokens[0]);
634 continue;
635 }
636
637 hash = (uint8_t *)tokens[3];
638 if(cli_hexnibbles((char *)hash, 124)) {
639 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad chars)\n", line);
640 ret = CL_EMALFDB;
641 break;
642 }
643 size = (hash[0] << 4) + hash[1];
644 if(size != 32 && size != 24 && size != 16) {
645 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad size)\n", line);
646 ret = CL_EMALFDB;
647 break;
648 }
649 enginesize = (size >> 3) - 2;
650 hash+=2;
651
652 metric = (struct icomtr *)mpool_realloc(engine->mempool, matcher->icons[enginesize], sizeof(struct icomtr) * (matcher->icon_counts[enginesize] + 1));
653 if(!metric) {
654 ret = CL_EMEM;
655 break;
656 }
657
658 matcher->icons[enginesize] = metric;
659 metric += matcher->icon_counts[enginesize];
660 matcher->icon_counts[enginesize]++;
661
662 for(i=0; i<3; i++) {
663 if((metric->color_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072)
664 break;
665 if((metric->color_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8)
666 break;
667 if((metric->color_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8)
668 break;
669 hash += 7;
670 }
671 if(i!=3) {
672 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad color data)\n", line);
673 ret = CL_EMALFDB;
674 break;
675 }
676
677 for(i=0; i<3; i++) {
678 if((metric->gray_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072)
679 break;
680 if((metric->gray_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8)
681 break;
682 if((metric->gray_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8)
683 break;
684 hash += 7;
685 }
686 if(i!=3) {
687 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad gray data)\n", line);
688 ret = CL_EMALFDB;
689 break;
690 }
691
692 for(i=0; i<3; i++) {
693 metric->bright_avg[i] = (hash[0] << 4) | hash[1];
694 if((metric->bright_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
695 break;
696 if((metric->bright_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
697 break;
698 hash += 6;
699 }
700 if(i!=3) {
701 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad bright data)\n", line);
702 ret = CL_EMALFDB;
703 break;
704 }
705
706 for(i=0; i<3; i++) {
707 metric->dark_avg[i] = (hash[0] << 4) | hash[1];
708 if((metric->dark_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
709 break;
710 if((metric->dark_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
711 break;
712 hash += 6;
713 }
714 if(i!=3) {
715 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad dark data)\n", line);
716 ret = CL_EMALFDB;
717 break;
718 }
719
720 for(i=0; i<3; i++) {
721 metric->edge_avg[i] = (hash[0] << 4) | hash[1];
722 if((metric->edge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
723 break;
724 if((metric->edge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
725 break;
726 hash += 6;
727 }
728 if(i!=3) {
729 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad edge data)\n", line);
730 ret = CL_EMALFDB;
731 break;
732 }
733
734 for(i=0; i<3; i++) {
735 metric->noedge_avg[i] = (hash[0] << 4) | hash[1];
736 if((metric->noedge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
737 break;
738 if((metric->noedge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
739 break;
740 hash += 6;
741 }
742 if(i!=3) {
743 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad noedge data)\n", line);
744 ret = CL_EMALFDB;
745 break;
746 }
747
748 metric->rsum = (hash[0] << 4) | hash[1];
749 metric->gsum = (hash[2] << 4) | hash[3];
750 metric->bsum = (hash[4] << 4) | hash[5];
751 metric->ccount = (hash[6] << 4) | hash[7];
752 if(metric->rsum + metric->gsum + metric->bsum > 103 || metric->ccount > 100) {
753 cli_errmsg("cli_loadidb: Malformed hash at line %u (bad spread data)\n", line);
754 ret = CL_EMALFDB;
755 break;
756 }
757
758 if(!(metric->name = cli_mpool_strdup(engine->mempool, tokens[0]))) {
759 ret = CL_EMEM;
760 break;
761 }
762
763 for(i=0; i<matcher->group_counts[0]; i++) {
764 if(!strcmp(tokens[1], matcher->group_names[0][i]))
765 break;
766 }
767 if(i==matcher->group_counts[0]) {
768 if(!(matcher->group_names[0] = mpool_realloc(engine->mempool, matcher->group_names[0], sizeof(char *) * (i + 1))) ||
769 !(matcher->group_names[0][i] = cli_mpool_strdup(engine->mempool, tokens[1]))) {
770 ret = CL_EMEM;
771 break;
772 }
773 matcher->group_counts[0]++;
774 }
775 metric->group[0] = i;
776
777 for(i=0; i<matcher->group_counts[1]; i++) {
778 if(!strcmp(tokens[2], matcher->group_names[1][i]))
779 break;
780 }
781 if(i==matcher->group_counts[1]) {
782 if(!(matcher->group_names[1] = mpool_realloc(engine->mempool, matcher->group_names[1], sizeof(char *) * (i + 1))) ||
783 !(matcher->group_names[1][i] = cli_mpool_strdup(engine->mempool, tokens[2]))) {
784 ret = CL_EMEM;
785 break;
786 }
787 matcher->group_counts[1]++;
788 }
789 metric->group[1] = i;
790
791 if(matcher->group_counts[0] > 256 || matcher->group_counts[1] > 256) {
792 cli_errmsg("cli_loadidb: too many icon groups!\n");
793 ret = CL_EMALFDB;
794 break;
795 }
796
797 sigs++;
798 }
799 if(engine->ignored)
800 free(buffer_cpy);
801
802 if(!line) {
803 cli_errmsg("cli_loadidb: Empty database file\n");
804 return CL_EMALFDB;
805 }
806
807 if(ret) {
808 cli_errmsg("cli_loadidb: Problem parsing database at line %u\n", line);
809 return ret;
810 }
811
812 if(signo)
813 *signo += sigs;
814
815 engine->iconcheck = matcher;
816 return CL_SUCCESS;
817}
818
819static int cli_loadwdb(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
820{
821 int ret = 0;
822
823
824 if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
825 return CL_SUCCESS;
826
827 if(!engine->whitelist_matcher) {
828 if((ret = init_whitelist(engine))) {
829 return ret;
830 }
831 }
832
833 if((ret = load_regex_matcher(engine, engine->whitelist_matcher, fs, NULL, options, 1, dbio, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
834 return ret;
835 }
836
837 return CL_SUCCESS;
838}
839
840static int cli_loadpdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
841{
842 int ret = 0;
843
844
845 if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
846 return CL_SUCCESS;
847
848 if(!engine->domainlist_matcher) {
849 if((ret = init_domainlist(engine))) {
850 return ret;
851 }
852 }
853
854 if((ret = load_regex_matcher(engine, engine->domainlist_matcher, fs, signo, options, 0, dbio, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
855 return ret;
856 }
857
858 return CL_SUCCESS;
859}
860
861#define NDB_TOKENS 6
862static 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)
863{
864 const char *tokens[NDB_TOKENS + 1];
865 char buffer[FILEBUFF], *buffer_cpy = NULL;
866 const char *sig, *virname, *offset, *pt;
867 struct cli_matcher *root;
868 int line = 0, sigs = 0, ret = 0, tokens_count;
869 unsigned short target;
870 unsigned int phish = options & CL_DB_PHISHING;
871
872
873 if((ret = cli_initroots(engine, options)))
874 return ret;
875
876 if(engine->ignored)
877 if(!(buffer_cpy = cli_malloc(FILEBUFF)))
878 return CL_EMEM;
879
880 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
881 line++;
882
883 if(!phish)
884 if(!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14))
885 continue;
886
887 cli_chomp(buffer);
888 if(engine->ignored)
889 strcpy(buffer_cpy, buffer);
890
891 tokens_count = cli_strtokenize(buffer, ':', NDB_TOKENS + 1, tokens);
892 if(tokens_count < 4 || tokens_count > 6) {
893 ret = CL_EMALFDB;
894 break;
895 }
896
897 virname = tokens[0];
898
899 if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
900 if(cli_chkpua(virname, engine->pua_cats, options))
901 continue;
902
903 if(engine->ignored && cli_chkign(engine->ignored, virname, buffer_cpy))
904 continue;
905
906 if(!sdb && engine->cb_sigload && engine->cb_sigload("ndb", virname, engine->cb_sigload_ctx)) {
907 cli_dbgmsg("cli_loadndb: skipping %s due to callback\n", virname);
908 continue;
909 }
910
911 if(tokens_count > 4) { /* min version */
912 pt = tokens[4];
913
914 if(!cli_isnumber(pt)) {
915 ret = CL_EMALFDB;
916 break;
917 }
918
919 if((unsigned int) atoi(pt) > cl_retflevel()) {
920 cli_dbgmsg("Signature for %s not loaded (required f-level: %d)\n", virname, atoi(pt));
921 continue;
922 }
923
924 if(tokens_count == 6) { /* max version */
925 pt = tokens[5];
926 if(!cli_isnumber(pt)) {
927 ret = CL_EMALFDB;
928 break;
929 }
930
931 if((unsigned int) atoi(pt) < cl_retflevel()) {
932 continue;
933 }
934 }
935 }
936
937 if(!(pt = tokens[1]) || (strcmp(pt, "*") && !cli_isnumber(pt))) {
938 ret = CL_EMALFDB;
939 break;
940 }
941 target = (unsigned short) atoi(pt);
942
943 if(target >= CLI_MTARGETS) {
944 cli_dbgmsg("Not supported target type in signature for %s\n", virname);
945 continue;
946 }
947
948 root = engine->root[target];
949
950 offset = tokens[2];
951 sig = tokens[3];
952
953 if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, NULL, options))) {
954 ret = CL_EMALFDB;
955 break;
956 }
957 sigs++;
958 }
959 if(engine->ignored)
960 free(buffer_cpy);
961
962 if(!line) {
963 cli_errmsg("Empty database file\n");
964 return CL_EMALFDB;
965 }
966
967 if(ret) {
968 cli_errmsg("Problem parsing database at line %d\n", line);
969 return ret;
970 }
971
972 if(signo)
973 *signo += sigs;
974
975 if(sdb && sigs && !engine->sdb) {
976 engine->sdb = 1;
977 cli_dbgmsg("*** Self protection mechanism activated.\n");
978 }
979
980 return CL_SUCCESS;
981}
982
983struct lsig_attrib {
984 const char *name;
985 unsigned int type;
986 void **pt;
987};
988
989/* TODO: rework this */
990static int lsigattribs(char *attribs, struct cli_lsig_tdb *tdb)
991{
992 struct lsig_attrib attrtab[] = {
993#define ATTRIB_TOKENS 9
994 { "Target", CLI_TDB_UINT, (void **) &tdb->target },
995 { "Engine", CLI_TDB_RANGE, (void **) &tdb->engine },
996
997 { "FileSize", CLI_TDB_RANGE, (void **) &tdb->filesize },
998 { "EntryPoint", CLI_TDB_RANGE, (void **) &tdb->ep },
999 { "NumberOfSections", CLI_TDB_RANGE, (void **) &tdb->nos },
1000
1001 { "IconGroup1", CLI_TDB_STR, (void **) &tdb->icongrp1 },
1002 { "IconGroup2", CLI_TDB_STR, (void **) &tdb->icongrp2 },
1003
1004 { "Container", CLI_TDB_FTYPE, (void **) &tdb->container },
1005 { "HandlerType", CLI_TDB_FTYPE, (void **) &tdb->handlertype },
1006/*
1007 { "SectOff", CLI_TDB_RANGE2, (void **) &tdb->sectoff },
1008 { "SectRVA", CLI_TDB_RANGE2, (void **) &tdb->sectrva },
1009 { "SectVSZ", CLI_TDB_RANGE2, (void **) &tdb->sectvsz },
1010 { "SectRAW", CLI_TDB_RANGE2, (void **) &tdb->sectraw },
1011 { "SectRSZ", CLI_TDB_RANGE2, (void **) &tdb->sectrsz },
1012 { "SectURVA", CLI_TDB_RANGE2, (void **) &tdb->secturva },
1013 { "SectUVSZ", CLI_TDB_RANGE2, (void **) &tdb->sectuvsz },
1014 { "SectURAW", CLI_TDB_RANGE2, (void **) &tdb->secturaw },
1015 { "SectURSZ", CLI_TDB_RANGE2, (void **) &tdb->sectursz },
1016*/
1017 { NULL, 0, NULL, }
1018 };
1019 struct lsig_attrib *apt;
1020 char *tokens[ATTRIB_TOKENS], *pt, *pt2;
1021 unsigned int v1, v2, v3, i, j, tokens_count, have_newext = 0;
1022 uint32_t cnt, off[ATTRIB_TOKENS];
1023
1024
1025 tokens_count = cli_strtokenize(attribs, ',', ATTRIB_TOKENS, (const char **) tokens);
1026
1027 for(i = 0; i < tokens_count; i++) {
1028 if(!(pt = strchr(tokens[i], ':'))) {
1029 cli_errmsg("lsigattribs: Incorrect format of attribute '%s'\n", tokens[i]);
1030 return -1;
1031 }
1032 *pt++ = 0;
1033
1034 apt = NULL;
1035 for(j = 0; attrtab[j].name; j++) {
1036 if(!strcmp(attrtab[j].name, tokens[i])) {
1037 apt = &attrtab[j];
1038 break;
1039 }
1040 }
1041
1042 if(!apt) {
1043 cli_dbgmsg("lsigattribs: Unknown attribute name '%s'\n", tokens[i]);
1044 return 1;
1045 }
1046
1047 if(!strcmp(apt->name, "Engine")) {
1048 if(i) {
1049 cli_errmsg("lsigattribs: For backward compatibility the Engine attribute must be on the first position\n");
1050 return -1;
1051 }
1052 } else if(strcmp(apt->name, "Target"))
1053 have_newext = 1;
1054
1055 switch(apt->type) {
1056 case CLI_TDB_UINT:
1057 if(!cli_isnumber(pt)) {
1058 cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]);
1059 return -1;
1060 }
1061 off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
1062 tdb->val = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
1063 if(!tdb->val) {
1064 tdb->cnt[CLI_TDB_UINT] = 0;
1065 return -1;
1066 }
1067 tdb->val[cnt] = atoi(pt);
1068 break;
1069
1070 case CLI_TDB_FTYPE:
1071 if((v1 = cli_ftcode(pt)) == CL_TYPE_ERROR) {
1072 cli_dbgmsg("lsigattribs: Unknown file type in %s\n", tokens[i]);
1073 return 1; /* skip */
1074 }
1075 off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
1076 tdb->val = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
1077 if(!tdb->val) {
1078 tdb->cnt[CLI_TDB_UINT] = 0;
1079 return -1;
1080 }
1081 tdb->val[cnt] = v1;
1082 break;
1083
1084 case CLI_TDB_RANGE:
1085 if(!(pt2 = strchr(pt, '-'))) {
1086 cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
1087 return -1;
1088 }
1089 *pt2++ = 0;
1090 off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
1091 tdb->cnt[CLI_TDB_RANGE] += 2;
1092 tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
1093 if(!tdb->range) {
1094 tdb->cnt[CLI_TDB_RANGE] = 0;
1095 return -1;
1096 }
1097 if(!cli_isnumber(pt) || !cli_isnumber(pt2)) {
1098 cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]);
1099 return -1;
1100 }
1101 tdb->range[cnt] = atoi(pt);
1102 tdb->range[cnt + 1] = atoi(pt2);
1103 break;
1104
1105 case CLI_TDB_RANGE2:
1106 if(!strchr(pt, '-') || !strchr(pt, '.')) {
1107 cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
1108 return -1;
1109 }
1110 off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
1111 tdb->cnt[CLI_TDB_RANGE] += 3;
1112 tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
1113 if(!tdb->range) {
1114 tdb->cnt[CLI_TDB_RANGE] = 0;
1115 return -1;
1116 }
1117 if(sscanf(pt, "%u.%u-%u", &v1, &v2, &v3) != 3) {
1118 cli_errmsg("lsigattribs: Can't parse parameters in '%s'\n", tokens[i]);
1119 return -1;
1120 }
1121 tdb->range[cnt] = (uint32_t) v1;
1122 tdb->range[cnt + 1] = (uint32_t) v2;
1123 tdb->range[cnt + 2] = (uint32_t) v3;
1124 break;
1125
1126 case CLI_TDB_STR:
1127 off[i] = cnt = tdb->cnt[CLI_TDB_STR];
1128 tdb->cnt[CLI_TDB_STR] += strlen(pt) + 1;
1129 tdb->str = (char *) mpool_realloc2(tdb->mempool, tdb->str, tdb->cnt[CLI_TDB_STR] * sizeof(char));
1130 if(!tdb->str) {
1131 cli_errmsg("lsigattribs: Can't allocate memory for tdb->str\n");
1132 return -1;
1133 }
1134 memcpy(&tdb->str[cnt], pt, strlen(pt));
1135 tdb->str[tdb->cnt[CLI_TDB_STR] - 1] = 0;
1136 break;
1137 }
1138 }
1139
1140 if(!i) {
1141 cli_errmsg("lsigattribs: Empty TDB\n");
1142 return -1;
1143 }
1144
1145 for(i = 0; i < tokens_count; i++) {
1146 for(j = 0; attrtab[j].name; j++) {
1147 if(!strcmp(attrtab[j].name, tokens[i])) {
1148 apt = &attrtab[j];
1149 break;
1150 }
1151 }
1152 if(!apt)
1153 continue;
1154 switch(apt->type) {
1155 case CLI_TDB_UINT:
1156 case CLI_TDB_FTYPE:
1157 *apt->pt = (uint32_t *) &tdb->val[off[i]];
1158 break;
1159
1160 case CLI_TDB_RANGE:
1161 case CLI_TDB_RANGE2:
1162 *apt->pt = (uint32_t *) &tdb->range[off[i]];
1163 break;
1164
1165 case CLI_TDB_STR:
1166 *apt->pt = (char *) &tdb->str[off[i]];
1167 break;
1168 }
1169 }
1170
1171 if(have_newext && (!tdb->engine || tdb->engine[0] < 51)) {
1172 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");
1173 return -1;
1174 }
1175 return 0;
1176}
1177
1178#define FREE_TDB(x) do { \
1179 if(x.cnt[CLI_TDB_UINT]) \
1180 mpool_free(x.mempool, x.val); \
1181 if(x.cnt[CLI_TDB_RANGE]) \
1182 mpool_free(x.mempool, x.range); \
1183 if(x.cnt[CLI_TDB_STR]) \
1184 mpool_free(x.mempool, x.str); \
1185 if(x.macro_ptids)\
1186 mpool_free(x.mempool, x.macro_ptids);\
1187 } while(0);
1188
1189#define LDB_TOKENS 67
1190static int load_oneldb(char *buffer, int chkpua, int chkign, struct cl_engine *engine, unsigned int options, const char *dbname, unsigned int line, unsigned int *sigs, unsigned bc_idx, const char *buffer_cpy)
1191{
1192 const char *sig, *virname, *offset, *logic;
1193 struct cli_ac_lsig **newtable, *lsig;
1194 char *tokens[LDB_TOKENS+1], *pt;
1195 int i, subsigs, tokens_count;
1196 unsigned short target = 0;
1197 struct cli_matcher *root;
1198 struct cli_lsig_tdb tdb;
1199 uint32_t lsigid[2];
1200 int ret;
1201
1202 tokens_count = cli_strtokenize(buffer, ';', LDB_TOKENS + 1, (const char **) tokens);
1203 if(tokens_count < 4) {
1204 return CL_EMALFDB;
1205 }
1206 virname = tokens[0];
1207 logic = tokens[2];
1208
1209 if (chkpua && cli_chkpua(virname, engine->pua_cats, options))
1210 return CL_SUCCESS;
1211
1212 if (chkign && cli_chkign(engine->ignored, virname, buffer_cpy))
1213 return CL_SUCCESS;
1214
1215 if(engine->cb_sigload && engine->cb_sigload("ldb", virname, engine->cb_sigload_ctx)) {
1216 cli_dbgmsg("cli_loadldb: skipping %s due to callback\n", virname);
1217 (*sigs)--;
1218 return CL_SUCCESS;
1219 }
1220
1221 subsigs = cli_ac_chklsig(logic, logic + strlen(logic), NULL, NULL, NULL, 1);
1222 if(subsigs == -1) {
1223 return CL_EMALFDB;
1224 }
1225 subsigs++;
1226 if(subsigs > 64) {
1227 cli_errmsg("cli_loadldb: Broken logical expression or too many subsignatures\n");
1228 return CL_EMALFDB;
1229 }
1230 if (!line) {
1231 /* This is a logical signature from the bytecode, we need all
1232 * subsignatures, even if not referenced from the logical expression */
1233 if (subsigs > tokens_count-3) {
1234 cli_errmsg("load_oneldb: Too many subsignatures: %u (max %u)\n",
1235 subsigs, tokens_count-3);
1236 return CL_EMALFDB;
1237 }
1238 subsigs = tokens_count-3;
1239 } else if(subsigs != tokens_count - 3) {
1240 cli_errmsg("cli_loadldb: The number of subsignatures (== %u) doesn't match the IDs in the logical expression (== %u)\n", tokens_count - 3, subsigs);
1241 return CL_EMALFDB;
1242 }
1243
1244 /* TDB */
1245 memset(&tdb, 0, sizeof(tdb));
1246#ifdef USE_MPOOL
1247 tdb.mempool = engine->mempool;
1248#endif
1249 if((ret = lsigattribs(tokens[1], &tdb))) {
1250 FREE_TDB(tdb);
1251 if(ret == 1) {
1252 cli_dbgmsg("cli_loadldb: Not supported attribute(s) in logical signature for %s, skipping\n", virname);
1253 (*sigs)--;
1254 return CL_SUCCESS;
1255 }
1256 return CL_EMALFDB;
1257 }
1258
1259 if(tdb.engine) {
1260 if(tdb.engine[0] > cl_retflevel()) {
1261 cli_dbgmsg("cli_loadldb: Signature for %s not loaded (required f-level: %u)\n", virname, tdb.engine[0]);
1262 FREE_TDB(tdb);
1263 (*sigs)--;
1264 return CL_SUCCESS;
1265 } else if(tdb.engine[1] < cl_retflevel()) {
1266 FREE_TDB(tdb);
1267 (*sigs)--;
1268 return CL_SUCCESS;
1269 }
1270 }
1271
1272 if(!tdb.target) {
1273 cli_errmsg("cli_loadldb: No target specified in TDB\n");
1274 FREE_TDB(tdb);
1275 return CL_EMALFDB;
1276 } else if(tdb.target[0] >= CLI_MTARGETS) {
1277 cli_dbgmsg("cli_loadldb: Not supported target type in logical signature for %s, skipping\n", virname);
1278 FREE_TDB(tdb);
1279 (*sigs)--;
1280 return CL_SUCCESS;
1281 }
1282
1283 if((tdb.icongrp1 || tdb.icongrp2) && tdb.target[0] != 1) {
1284 cli_errmsg("cli_loadldb: IconGroup is only supported in PE (target 1) signatures\n");
1285 FREE_TDB(tdb);
1286 return CL_EMALFDB;
1287 }
1288
1289 if((tdb.ep || tdb.nos) && tdb.target[0] != 1 && tdb.target[0] != 6 && tdb.target[0] != 9) {
1290 cli_errmsg("cli_loadldb: EntryPoint/NumberOfSections is only supported in PE/ELF/Mach-O signatures\n");
1291 FREE_TDB(tdb);
1292 return CL_EMALFDB;
1293 }
1294
1295 root = engine->root[tdb.target[0]];
1296
1297 lsig = (struct cli_ac_lsig *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_ac_lsig));
1298 if(!lsig) {
1299 cli_errmsg("cli_loadldb: Can't allocate memory for lsig\n");
1300 FREE_TDB(tdb);
1301 return CL_EMEM;
1302 }
1303
1304 lsig->logic = cli_mpool_strdup(engine->mempool, logic);
1305 if(!lsig->logic) {
1306 cli_errmsg("cli_loadldb: Can't allocate memory for lsig->logic\n");
1307 FREE_TDB(tdb);
1308 mpool_free(engine->mempool, lsig);
1309 return CL_EMEM;
1310 }
1311
1312 lsigid[0] = lsig->id = root->ac_lsigs;
1313
1314 root->ac_lsigs++;
1315 newtable = (struct cli_ac_lsig **) mpool_realloc(engine->mempool, root->ac_lsigtable, root->ac_lsigs * sizeof(struct cli_ac_lsig *));
1316 if(!newtable) {
1317 root->ac_lsigs--;
1318 cli_errmsg("cli_loadldb: Can't realloc root->ac_lsigtable\n");
1319 FREE_TDB(tdb);
1320 mpool_free(engine->mempool, lsig);
1321 return CL_EMEM;
1322 }
1323 /* 0 marks no bc, we can't use a pointer to bc, since that is
1324 * realloced/moved during load */
1325 lsig->bc_idx = bc_idx;
1326 newtable[root->ac_lsigs - 1] = lsig;
1327 root->ac_lsigtable = newtable;
1328 tdb.subsigs = subsigs;
1329
1330 for(i = 0; i < subsigs; i++) {
1331 lsigid[1] = i;
1332 sig = tokens[3 + i];
1333
1334 if((pt = strchr(tokens[3 + i], ':'))) {
1335 *pt = 0;
1336 sig = ++pt;
1337 offset = tokens[3 + i];
1338 } else {
1339 offset = "*";
1340 sig = tokens[3 + i];
1341 }
1342
1343 if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, lsigid, options)))
1344 return ret;
1345 if(sig[0] == '$' && i) {
1346 /* allow mapping from lsig back to pattern for macros */
1347 if (!tdb.macro_ptids)
1348 tdb.macro_ptids = mpool_calloc(root->mempool, subsigs, sizeof(*tdb.macro_ptids));
1349 if (!tdb.macro_ptids)
1350 return CL_EMEM;
1351 tdb.macro_ptids[i-1] = root->ac_patterns-1;
1352 }
1353 }
1354 memcpy(&lsig->tdb, &tdb, sizeof(tdb));
1355 return CL_SUCCESS;
1356}
1357
1358static int cli_loadldb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
1359{
1360 char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1], *buffer_cpy = NULL;
1361 unsigned int line = 0, sigs = 0;
1362 int ret;
1363
1364
1365 if((ret = cli_initroots(engine, options)))
1366 return ret;
1367
1368 if(engine->ignored)
1369 if(!(buffer_cpy = cli_malloc(sizeof(buffer))))
1370 return CL_EMEM;
1371 while(cli_dbgets(buffer, sizeof(buffer), fs, dbio)) {
1372 line++;
1373 sigs++;
1374 cli_chomp(buffer);
1375
1376 if(engine->ignored)
1377 strcpy(buffer_cpy, buffer);
1378 ret = load_oneldb(buffer,
1379 engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)),
1380 !!engine->ignored,
1381 engine, options, dbname, line, &sigs, 0, buffer_cpy);
1382 if (ret)
1383 break;
1384 }
1385 if(engine->ignored)
1386 free(buffer_cpy);
1387
1388 if(!line) {
1389 cli_errmsg("Empty database file\n");
1390 return CL_EMALFDB;
1391 }
1392
1393 if(ret) {
1394 cli_errmsg("Problem parsing database at line %u\n", line);
1395 return ret;
1396 }
1397
1398 if(signo)
1399 *signo += sigs;
1400
1401 return CL_SUCCESS;
1402}
1403
1404static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
1405{
1406 char buf[4096];
1407 int rc;
1408 struct cli_all_bc *bcs = &engine->bcs;
1409 struct cli_bc *bc;
1410 unsigned sigs = 0;
1411 unsigned security_trust = 0;
1412 unsigned i;
1413
1414
1415 /* TODO: virusname have a common prefix, and whitelist by that */
1416 if((rc = cli_initroots(engine, options)))
1417 return rc;
1418
1419 if(!(engine->dconf->bytecode & BYTECODE_ENGINE_MASK)) {
1420 return CL_SUCCESS;
1421 }
1422
1423 if(engine->cb_sigload && engine->cb_sigload("cbc", dbname, engine->cb_sigload_ctx)) {
1424 cli_dbgmsg("cli_loadcbc: skipping %s due to callback\n", dbname);
1425 return CL_SUCCESS;
1426 }
1427
1428#ifndef CL_BCUNSIGNED
1429 if (!(options & CL_DB_SIGNED)) {
1430 cli_warnmsg("Only loading signed bytecode, skipping load of unsigned bytecode!\n");
1431 cli_warnmsg("Build with ./configure --enable-unsigned-bytecode to enable loading of unsigned bytecode\n");
1432 return CL_SUCCESS;
1433 }
1434#endif
1435 bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs)*(bcs->count+1));
1436 if (!bcs->all_bcs) {
1437 cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n");
1438 return CL_EMEM;
1439 }
1440 bcs->count++;
1441 bc = &bcs->all_bcs[bcs->count-1];
1442
1443 switch (engine->bytecode_security) {
1444 case CL_BYTECODE_TRUST_ALL:
1445 security_trust = 1;
1446 cli_dbgmsg("bytecode: trusting all bytecode!\n");
1447 break;
1448 case CL_BYTECODE_TRUST_SIGNED:
1449 security_trust = !!(options & CL_DB_SIGNED);
1450 break;
1451 default:
1452 security_trust = 0;
1453 }
1454
1455 rc = cli_bytecode_load(bc, fs, dbio, security_trust);
1456 /* read remainder of DB, needed because cvd.c checks that we read the entire
1457 * file */
1458 while (cli_dbgets(buf, sizeof(buf), fs, dbio)) {}
1459
1460 if (rc != CL_SUCCESS) {
1461 cli_bytecode_destroy(bc);
1462 cli_errmsg("Unable to load %s bytecode: %s\n", dbname, cl_strerror(rc));
1463 return rc;
1464 }
1465 if (bc->state == bc_skip) {
1466 cli_bytecode_destroy(bc);
1467 bcs->count--;
1468 return CL_SUCCESS;
1469 }
1470 bc->id = bcs->count;/* must set after _load, since load zeroes */
1471 if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
1472 cli_infomsg(NULL, "bytecode %u -> %s\n", bc->id, dbname);
1473 sigs++;
1474 if (bc->kind == BC_LOGICAL || bc->lsig) {
1475 unsigned oldsigs = sigs;
1476 if (!bc->lsig) {
1477 cli_errmsg("Bytecode %s has logical kind, but missing logical signature!\n", dbname);
1478 return CL_EMALFDB;
1479 }
1480 cli_dbgmsg("Bytecode %s(%u) has logical signature: %s\n", dbname, bc->id, bc->lsig);
1481 rc = load_oneldb(bc->lsig, 0, 0, engine, options, dbname, 0, &sigs, bcs->count, NULL);
1482 if (rc != CL_SUCCESS) {
1483 cli_errmsg("Problem parsing logical signature %s for bytecode %s: %s\n",
1484 bc->lsig, dbname, cl_strerror(rc));
1485 return rc;
1486 }
1487 if (sigs != oldsigs) {
1488 /* compiler ensures Engine field in lsig matches the one in bytecode,
1489 * so this should never happen. */
1490 cli_errmsg("Bytecode logical signature skipped, but bytecode itself not?");
1491 return CL_EMALFDB;
1492 }
1493 }
1494 if (bc->kind != BC_LOGICAL) {
1495 if (bc->lsig) {
1496 /* runlsig will only flip a status bit, not report a match,
1497 * when the hooks are executed we only execute the hook if its
1498 * status bit is on */
1499 bc->hook_lsig_id = ++engine->hook_lsig_ids;
1500 }
1501 if (bc->kind >= _BC_START_HOOKS && bc->kind < _BC_LAST_HOOK) {
1502 unsigned hook = bc->kind - _BC_START_HOOKS;
1503 unsigned cnt = ++engine->hooks_cnt[hook];
1504 engine->hooks[hook] = cli_realloc2(engine->hooks[hook],
1505 sizeof(*engine->hooks[0])*cnt);
1506 if (!engine->hooks[hook]) {
1507 cli_errmsg("Out of memory allocating memory for hook %u", hook);
1508 return CL_EMEM;
1509 }
1510 engine->hooks[hook][cnt-1] = bcs->count-1;
1511 } else switch (bc->kind) {
1512 case BC_STARTUP:
1513 for (i=0;i<bcs->count-1;i++)
1514 if (bcs->all_bcs[i].kind == BC_STARTUP) {
1515 struct cli_bc *bc0 = &bcs->all_bcs[i];
1516 cli_errmsg("Can only load 1 BC_STARTUP bytecode, attempted to load 2nd!\n");
1517 cli_warnmsg("Previous BC_STARTUP: %d %d by %s\n",
1518 bc0->id, (uint32_t)bc0->metadata.timestamp,
1519 bc0->metadata.sigmaker ? bc0->metadata.sigmaker : "N/A");
1520 cli_warnmsg("Conflicting BC_STARTUP: %d %d by %s\n",
1521 bc->id, (uint32_t)bc->metadata.timestamp,
1522 bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A");
1523 return CL_EMALFDB;
1524 }
1525 break;
1526 default:
1527 cli_errmsg("Bytecode: unhandled bytecode kind %u\n", bc->kind);
1528 return CL_EMALFDB;
1529 }
1530 }
1531 if (signo)
1532 *signo += sigs;
1533 return CL_SUCCESS;
1534}
1535
1536#define FTM_TOKENS 8
1537static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio)
1538{
1539 const char *tokens[FTM_TOKENS + 1], *pt;
1540 char buffer[FILEBUFF];
1541 unsigned int line = 0, sigs = 0, tokens_count;
1542 struct cli_ftype *new;
1543 cli_file_t rtype, type;
1544 int ret;
1545
1546
1547 if((ret = cli_initroots(engine, options)))
1548 return ret;
1549
1550 while(1) {
1551 if(internal) {
1552 options |= CL_DB_OFFICIAL;
1553 if(!ftypes_int[line])
1554 break;
1555 strncpy(buffer, ftypes_int[line], sizeof(buffer));
1556 buffer[sizeof(buffer)-1]='\0';
1557 } else {
1558 if(!cli_dbgets(buffer, FILEBUFF, fs, dbio))
1559 break;
1560 cli_chomp(buffer);
1561 }
1562 line++;
1563 tokens_count = cli_strtokenize(buffer, ':', FTM_TOKENS + 1, tokens);
1564
1565 if(tokens_count < 6 || tokens_count > 8) {
1566 ret = CL_EMALFDB;
1567 break;
1568 }
1569
1570 if(tokens_count > 6) { /* min version */
1571 pt = tokens[6];
1572 if(!cli_isnumber(pt)) {
1573 ret = CL_EMALFDB;
1574 break;
1575 }
1576 if((unsigned int) atoi(pt) > cl_retflevel()) {
1577 cli_dbgmsg("cli_loadftm: File type signature for %s not loaded (required f-level: %u)\n", tokens[3], atoi(pt));
1578 continue;
1579 }
1580 if(tokens_count == 8) { /* max version */
1581 pt = tokens[7];
1582 if(!cli_isnumber(pt)) {
1583 ret = CL_EMALFDB;
1584 break;
1585 }
1586 if((unsigned int) atoi(pt) < cl_retflevel())
1587 continue;
1588 }
1589 }
1590
1591 rtype = cli_ftcode(tokens[4]);
1592 type = cli_ftcode(tokens[5]);
1593 if(rtype == CL_TYPE_ERROR || type == CL_TYPE_ERROR) {
1594 ret = CL_EMALFDB;
1595 break;
1596 }
1597
1598 if(!cli_isnumber(tokens[0])) {
1599 cli_errmsg("cli_loadftm: Invalid value for the first field\n");
1600 ret = CL_EMALFDB;
1601 break;
1602 }
1603
1604 if(atoi(tokens[0]) == 1) { /* A-C */
1605 if((ret = cli_parse_add(engine->root[0], tokens[3], tokens[2], rtype, type, tokens[1], 0, NULL, options)))
1606 break;
1607
1608 } else if(atoi(tokens[0]) == 0) { /* memcmp() */
1609 if(!cli_isnumber(tokens[1])) {
1610 cli_errmsg("cli_loadftm: Invalid offset\n");
1611 ret = CL_EMALFDB;
1612 break;
1613 }
1614 new = (struct cli_ftype *) mpool_malloc(engine->mempool, sizeof(struct cli_ftype));
1615 if(!new) {
1616 ret = CL_EMEM;
1617 break;
1618 }
1619 new->type = type;
1620 new->offset = atoi(tokens[1]);
1621 new->magic = (unsigned char *) cli_mpool_hex2str(engine->mempool, tokens[2]);
1622 if(!new->magic) {
1623 cli_errmsg("cli_loadftm: Can't decode the hex string\n");
1624 ret = CL_EMALFDB;
1625 mpool_free(engine->mempool, new);
1626 break;
1627 }
1628 new->length = strlen(tokens[2]) / 2;
1629 new->tname = cli_mpool_strdup(engine->mempool, tokens[3]);
1630 if(!new->tname) {
1631 mpool_free(engine->mempool, new->magic);
1632 mpool_free(engine->mempool, new);
1633 ret = CL_EMEM;
1634 break;
1635 }
1636 new->next = engine->ftypes;
1637 engine->ftypes = new;
1638
1639 } else {
1640 cli_dbgmsg("cli_loadftm: Unsupported mode %u\n", atoi(tokens[0]));
1641 continue;
1642 }
1643 sigs++;
1644 }
1645
1646 if(ret) {
1647 cli_errmsg("Problem parsing %s filetype database at line %u\n", internal ? "built-in" : "external", line);
1648 return ret;
1649 }
1650
1651 if(!sigs) {
1652 cli_errmsg("Empty %s filetype database\n", internal ? "built-in" : "external");
1653 return CL_EMALFDB;
1654 }
1655
1656 cli_dbgmsg("Loaded %u filetype definitions\n", sigs);
1657 return CL_SUCCESS;
1658}
1659
1660#define INFO_NSTR "11088894983048545473659556106627194923928941791795047620591658697413581043322715912172496806525381055880964520618400224333320534660299233983755341740679502866829909679955734391392668378361221524205396631090105151641270857277080310734320951653700508941717419168723942507890702904702707587451621691050754307850383399865346487203798464178537392211402786481359824461197231102895415093770394216666324484593935762408468516826633192140826667923494822045805347809932848454845886971706424360558667862775876072059437703365380209101697738577515476935085469455279994113145977994084618328482151013142393373316337519977244732747977"
1661#define INFO_ESTR "100002049"
1662#define INFO_TOKENS 3
1663static int cli_loadinfo(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
1664{
1665 const char *tokens[INFO_TOKENS + 1];
1666 char buffer[FILEBUFF];
1667 unsigned int line = 0, tokens_count, len;
1668 unsigned char hash[32];
1669 struct cli_dbinfo *last = NULL, *new;
1670 int ret = CL_SUCCESS, dsig = 0;
1671 SHA256_CTX ctx;
1672
1673
1674 if(!dbio) {
1675 cli_errmsg("cli_loadinfo: .info files can only be loaded from within database container files\n");
1676 return CL_EMALFDB;
1677 }
1678 sha256_init(&ctx);
1679 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
1680 line++;
1681 if(!strncmp(buffer, "DSIG:", 5)) {
1682 dsig = 1;
1683 sha256_final(&ctx, hash);
1684 if(cli_versig2(hash, buffer + 5, INFO_NSTR, INFO_ESTR) != CL_SUCCESS) {
1685 cli_errmsg("cli_loadinfo: Incorrect digital signature\n");
1686 ret = CL_EMALFDB;
1687 }
1688 break;
1689 }
1690 len = strlen(buffer);
1691 if(dbio->usebuf && buffer[len - 1] != '\n' && len + 1 < FILEBUFF) {
1692 /* cli_dbgets in buffered mode strips \n */
1693 buffer[len] = '\n';
1694 buffer[len + 1] = 0;
1695 }
1696 sha256_update(&ctx, buffer, strlen(buffer));
1697 cli_chomp(buffer);
1698 if(!strncmp("ClamAV-VDB:", buffer, 11)) {
1699 if(engine->dbinfo) { /* shouldn't be initialized at this point */
1700 cli_errmsg("cli_loadinfo: engine->dbinfo already initialized\n");
1701 ret = CL_EMALFDB;
1702 break;
1703 }
1704 last = engine->dbinfo = (struct cli_dbinfo *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_bm_patt));
1705 if(!engine->dbinfo) {
1706 ret = CL_EMEM;
1707 break;
1708 }
1709 engine->dbinfo->cvd = cl_cvdparse(buffer);
1710 if(!engine->dbinfo->cvd) {
1711 cli_errmsg("cli_loadinfo: Can't parse header entry\n");
1712 ret = CL_EMALFDB;
1713 break;
1714 }
1715 continue;
1716 }
1717
1718 if(!last) {
1719 cli_errmsg("cli_loadinfo: Incorrect file format\n");
1720 ret = CL_EMALFDB;
1721 break;
1722 }
1723 tokens_count = cli_strtokenize(buffer, ':', INFO_TOKENS + 1, tokens);
1724 if(tokens_count != INFO_TOKENS) {
1725 ret = CL_EMALFDB;
1726 break;
1727 }
1728 new = (struct cli_dbinfo *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_dbinfo));
1729 if(!new) {
1730 ret = CL_EMEM;
1731 break;
1732 }
1733 new->name = cli_mpool_strdup(engine->mempool, tokens[0]);
1734 if(!new->name) {
1735 mpool_free(engine->mempool, new);
1736 ret = CL_EMEM;
1737 break;
1738 }
1739
1740 if(!cli_isnumber(tokens[1])) {
1741 cli_errmsg("cli_loadinfo: Invalid value in the size field\n");
1742 mpool_free(engine->mempool, new->name);
1743 mpool_free(engine->mempool, new);
1744 ret = CL_EMALFDB;
1745 break;
1746 }
1747 new->size = atoi(tokens[1]);
1748
1749 if(strlen(tokens[2]) != 64 || !(new->hash = cli_mpool_hex2str(engine->mempool, tokens[2]))) {
1750 cli_errmsg("cli_loadinfo: Malformed SHA256 string at line %u\n", line);
1751 mpool_free(engine->mempool, new->name);
1752 mpool_free(engine->mempool, new);
1753 ret = CL_EMALFDB;
1754 break;
1755 }
1756 last->next = new;
1757 last = new;
1758 }
1759
1760 if(!dsig) {
1761 cli_errmsg("cli_loadinfo: Digital signature not found\n");
1762 return CL_EMALFDB;
1763 }
1764
1765 if(ret) {
1766 cli_errmsg("cli_loadinfo: Problem parsing database at line %u\n", line);
1767 return ret;
1768 }
1769
1770 return CL_SUCCESS;
1771}
1772
1773#define IGN_MAX_TOKENS 3
1774static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
1775{
1776 const char *tokens[IGN_MAX_TOKENS + 1], *signame, *hash = NULL;
1777 char buffer[FILEBUFF];
1778 unsigned int line = 0, tokens_count, len;
1779 struct cli_bm_patt *new;
1780 int ret = CL_SUCCESS;
1781
1782 if(!engine->ignored) {
1783 engine->ignored = (struct cli_matcher *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_matcher));
1784 if(!engine->ignored)
1785 return CL_EMEM;
1786#ifdef USE_MPOOL
1787 engine->ignored->mempool = engine->mempool;
1788#endif
1789 if((ret = cli_bm_init(engine->ignored))) {
1790 cli_errmsg("cli_loadign: Can't initialise AC pattern matcher\n");
1791 return ret;
1792 }
1793 }
1794
1795 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
1796 line++;
1797 cli_chomp(buffer);
1798
1799 tokens_count = cli_strtokenize(buffer, ':', IGN_MAX_TOKENS + 1, tokens);
1800 if(tokens_count > IGN_MAX_TOKENS) {
1801 ret = CL_EMALFDB;
1802 break;
1803 }
1804
1805 if(tokens_count == 1) {
1806 signame = buffer;
1807 } else if(tokens_count == 2) {
1808 signame = tokens[0];
1809 hash = tokens[1];
1810 } else { /* old mode */
1811 signame = tokens[2];
1812 }
1813 if(!(len = strlen(signame))) {
1814 cli_errmsg("cli_loadign: No signature name provided\n");
1815 ret = CL_EMALFDB;
1816 break;
1817 }
1818
1819 new = (struct cli_bm_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_bm_patt));
1820 if(!new) {
1821 ret = CL_EMEM;
1822 break;
1823 }
1824 new->pattern = (unsigned char *) cli_mpool_strdup(engine->mempool, signame);
1825 if(!new->pattern) {
1826 mpool_free(engine->mempool, new);
1827 ret = CL_EMEM;
1828 break;
1829 }
1830 if(hash) {
1831 if(strlen(hash) != 32 || !(new->virname = (char *) cli_mpool_hex2str(engine->mempool, hash))) {
1832 cli_errmsg("cli_loadign: Malformed MD5 string at line %u\n", line);
1833 mpool_free(engine->mempool, new->pattern);
1834 mpool_free(engine->mempool, new);
1835 ret = CL_EMALFDB;
1836 break;
1837 }
1838 }
1839 new->length = len;
1840 new->boundary |= BM_BOUNDARY_EOL;
1841
1842 if((ret = cli_bm_addpatt(engine->ignored, new, "0"))) {
1843 if(hash)
1844 mpool_free(engine->mempool, new->virname);
1845 mpool_free(engine->mempool, new->pattern);
1846 mpool_free(engine->mempool, new);
1847 break;
1848 }
1849 }
1850
1851 if(ret) {
1852 cli_errmsg("cli_loadign: Problem parsing database at line %u\n", line);
1853 return ret;
1854 }
1855
1856 return CL_SUCCESS;
1857}
1858
1859#define MD5_HDB 0
1860#define MD5_MDB 1
1861#define MD5_FP 2
1862
1863static int cli_md5db_init(struct cl_engine *engine, unsigned int mode)
1864{
1865 struct cli_matcher *bm = NULL;
1866 int ret;
1867
1868
1869 if(mode == MD5_HDB) {
1870 bm = engine->md5_hdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
1871 } else if(mode == MD5_MDB) {
1872 bm = engine->md5_mdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
1873 } else {
1874 bm = engine->md5_fp = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
1875 }
1876
1877 if(!bm)
1878 return CL_EMEM;
1879#ifdef USE_MPOOL
1880 bm->mempool = engine->mempool;
1881#endif
1882 if((ret = cli_md5m_init(bm))) {
1883 cli_errmsg("cli_md5db_init: Failed to initialize MD5 matcher\n");
1884 return ret;
1885 }
1886
1887 return CL_SUCCESS;
1888}
1889
1890#define MD5_DB \
1891 if(mode == MD5_HDB) \
1892 db = engine->md5_hdb; \
1893 else if(mode == MD5_MDB) \
1894 db = engine->md5_mdb; \
1895 else \
1896 db = engine->md5_fp;
1897
1898#define MD5_TOKENS 3
1899static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname)
1900{
1901 const char *tokens[MD5_TOKENS + 1];
1902 char buffer[FILEBUFF], *buffer_cpy = NULL;
1903 const char *pt;
1904 unsigned char *md5;
1905 int ret = CL_SUCCESS;
1906 unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
1907 struct cli_md5m_patt *new;
1908 struct cli_matcher *db = NULL;
1909
1910
1911 if(mode == MD5_MDB) {
1912 size_field = 0;
1913 md5_field = 1;
1914 }
1915
1916 if(engine->ignored)
1917 if(!(buffer_cpy = cli_malloc(FILEBUFF)))
1918 return CL_EMEM;
1919
1920 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
1921 line++;
1922 cli_chomp(buffer);
1923 if(engine->ignored)
1924 strcpy(buffer_cpy, buffer);
1925
1926 tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens);
1927 if(tokens_count != MD5_TOKENS) {
1928 ret = CL_EMALFDB;
1929 break;
1930 }
1931 if(!cli_isnumber(tokens[size_field])) {
1932 cli_errmsg("cli_loadmd5: Invalid value for the size field\n");
1933 ret = CL_EMALFDB;
1934 break;
1935 }
1936
1937 pt = tokens[2]; /* virname */
1938 if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
1939 if(cli_chkpua(pt, engine->pua_cats, options))
1940 continue;
1941
1942 if(engine->ignored && cli_chkign(engine->ignored, pt, buffer_cpy))
1943 continue;
1944
1945 if(engine->cb_sigload) {
1946 const char *dot = strchr(dbname, '.');
1947 if(!dot)
1948 dot = dbname;
1949 else
1950 dot++;
1951 if(engine->cb_sigload(dot, pt, engine->cb_sigload_ctx)) {
1952 cli_dbgmsg("cli_loadmd5: skipping %s due to callback\n", pt);
1953 continue;
1954 }
1955 }
1956
1957 new = (struct cli_md5m_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_md5m_patt));
1958 if(!new) {
1959 ret = CL_EMEM;
1960 break;
1961 }
1962
1963 pt = tokens[md5_field]; /* md5 */
1964 if(strlen(pt) != 32 || !(md5 = (unsigned char *) cli_mpool_hex2str(engine->mempool, pt))) {
1965 cli_errmsg("cli_loadmd5: Malformed MD5 string at line %u\n", line);
1966 mpool_free(engine->mempool, new);
1967 ret = CL_EMALFDB;
1968 break;
1969 }
1970 memcpy(new->md5, md5, 16);
1971 mpool_free(engine->mempool, md5);
1972
1973 new->filesize = atoi(tokens[size_field]);
1974
1975 new->virname = cli_mpool_virname(engine->mempool, tokens[2], options & CL_DB_OFFICIAL);
1976 if(!new->virname) {
1977 mpool_free(engine->mempool, new);
1978 ret = CL_EMALFDB;
1979 break;
1980 }
1981
1982 MD5_DB;
1983 if(!db && (ret = cli_md5db_init(engine, mode))) {
1984 mpool_free(engine->mempool, new->virname);
1985 mpool_free(engine->mempool, new);
1986 break;
1987 } else {
1988 MD5_DB;
1989 }
1990
1991 if((ret = cli_md5m_addpatt(db, new))) {
1992 cli_errmsg("cli_loadmd5: Error adding BM pattern\n");
1993 mpool_free(engine->mempool, new->virname);
1994 mpool_free(engine->mempool, new);
1995 break;
1996 }
1997
1998 if(mode == MD5_MDB) { /* section MD5 */
1999 if(!db->md5_sizes_hs.capacity) {
2000 cli_hashset_init_pool(&db->md5_sizes_hs, 65536, 80, engine->mempool);
2001 }
2002 cli_hashset_addkey(&db->md5_sizes_hs, new->filesize);
2003 }
2004
2005 sigs++;
2006 }
2007 if(engine->ignored)
2008 free(buffer_cpy);
2009
2010 if(!line) {
2011 cli_errmsg("cli_loadmd5: Empty database file\n");
2012 return CL_EMALFDB;
2013 }
2014
2015 if(ret) {
2016 cli_errmsg("cli_loadmd5: Problem parsing database at line %u\n", line);
2017 return ret;
2018 }
2019
2020 if(signo)
2021 *signo += sigs;
2022
2023 return CL_SUCCESS;
2024}
2025
2026#define MD_TOKENS 9
2027static int cli_loadmd(FILE *fs, struct cl_engine *engine, unsigned int *signo, int type, unsigned int options, struct cli_dbio *dbio, const char *dbname)
2028{
2029 const char *tokens[MD_TOKENS + 1];
2030 char buffer[FILEBUFF], *buffer_cpy = NULL;
2031 unsigned int line = 0, sigs = 0, tokens_count;
2032 int ret = CL_SUCCESS;
2033 struct cli_cdb *new;
2034
2035
2036 if(engine->ignored)
2037 if(!(buffer_cpy = cli_malloc(FILEBUFF)))
2038 return CL_EMEM;
2039
2040 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
2041 line++;
2042 if(buffer[0] == '#')
2043 continue;
2044
2045 cli_chomp(buffer);
2046 if(engine->ignored)
2047 strcpy(buffer_cpy, buffer);
2048
2049 tokens_count = cli_strtokenize(buffer, ':', MD_TOKENS + 1, tokens);
2050 if(tokens_count != MD_TOKENS) {
2051 ret = CL_EMALFDB;
2052 break;
2053 }
2054
2055 if(strcmp(tokens[1], "*") && !cli_isnumber(tokens[1])) {
2056 cli_errmsg("cli_loadmd: Invalid value for the 'encrypted' field\n");
2057 ret = CL_EMALFDB;
2058 break;
2059 }
2060 if(strcmp(tokens[3], "*") && !cli_isnumber(tokens[3])) {
2061 cli_errmsg("cli_loadmd: Invalid value for the 'original size' field\n");
2062 ret = CL_EMALFDB;
2063 break;
2064 }
2065 if(strcmp(tokens[4], "*") && !cli_isnumber(tokens[4])) {
2066 cli_errmsg("cli_loadmd: Invalid value for the 'compressed size' field\n");
2067 ret = CL_EMALFDB;
2068 break;
2069 }
2070 if(strcmp(tokens[6], "*") && !cli_isnumber(tokens[6])) {
2071 cli_errmsg("cli_loadmd: Invalid value for the 'compression method' field\n");
2072 ret = CL_EMALFDB;
2073 break;
2074 }
2075 if(strcmp(tokens[7], "*") && !cli_isnumber(tokens[7])) {
2076 cli_errmsg("cli_loadmd: Invalid value for the 'file number' field\n");
2077 ret = CL_EMALFDB;
2078 break;
2079 }
2080 if(strcmp(tokens[8], "*") && !cli_isnumber(tokens[8])) {
2081 cli_errmsg("cli_loadmd: Invalid value for the 'max depth' field\n");
2082 ret = CL_EMALFDB;
2083 break;
2084 }
2085
2086 new = (struct cli_cdb *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_cdb));
2087 if(!new) {
2088 ret = CL_EMEM;
2089 break;
2090 }
2091
2092 new->virname = cli_mpool_virname(engine->mempool, tokens[0], options & CL_DB_OFFICIAL);
2093 if(!new->virname) {
2094 mpool_free(engine->mempool, new);
2095 ret = CL_EMEM;
2096 break;
2097 }
2098 new->ctype = (type == 1) ? CL_TYPE_ZIP : CL_TYPE_RAR;
2099
2100 if(engine->ignored && cli_chkign(engine->ignored, new->virname, buffer/*_cpy*/)) {
2101 mpool_free(engine->mempool, new->virname);
2102 mpool_free(engine->mempool, new);
2103 continue;
2104 }
2105
2106 if(engine->cb_sigload && engine->cb_sigload("md", new->virname, engine->cb_sigload_ctx)) {
2107 cli_dbgmsg("cli_loadmd: skipping %s due to callback\n", new->virname);
2108 mpool_free(engine->mempool, new->virname);
2109 mpool_free(engine->mempool, new);
2110 continue;
2111 }
2112
2113 new->encrypted = strcmp(tokens[1], "*") ? atoi(tokens[1]) : 2;
2114
2115 if(strcmp(tokens[2], "*") && cli_regcomp(&new->name, tokens[2], REG_EXTENDED | REG_NOSUB)) {
2116 cli_errmsg("cli_loadmd: Can't compile regular expression %s in signature for %s\n", tokens[2], tokens[0]);
2117 mpool_free(engine->mempool, new->virname);
2118 mpool_free(engine->mempool, new);
2119 ret = CL_EMEM;
2120 break;
2121 }
2122 new->csize[0] = new->csize[1] = CLI_OFF_ANY;
2123
2124 if(!strcmp(tokens[3], "*"))
2125 new->fsizer[0] = new->fsizer[1] = CLI_OFF_ANY;
2126 else
2127 new->fsizer[0] = new->fsizer[1] = atoi(tokens[3]);
2128
2129 if(!strcmp(tokens[4], "*"))
2130 new->fsizec[0] = new->fsizec[1] = CLI_OFF_ANY;
2131 else
2132 new->fsizec[0] = new->fsizec[1] = atoi(tokens[4]);
2133
2134 if(strcmp(tokens[5], "*")) {
2135 new->res1 = cli_hex2num(tokens[5]);
2136 if(new->res1 == -1) {
2137 mpool_free(engine->mempool, new->virname);
2138 mpool_free(engine->mempool, new);
2139 if(new->name.re_magic)
2140 cli_regfree(&new->name);
2141 ret = CL_EMALFDB;
2142 break;
2143 }
2144 }
2145
2146 /* tokens[6] - not used */
2147
2148 new->filepos[0] = new->filepos[1] = strcmp(tokens[7], "*") ? atoi(tokens[7]) : (int) CLI_OFF_ANY;
2149
2150 /* tokens[8] - not used */
2151
2152 new->next = engine->cdb;
2153 engine->cdb = new;
2154 sigs++;
2155 }
2156 if(engine->ignored)
2157 free(buffer_cpy);
2158
2159 if(!line) {
2160 cli_errmsg("Empty database file\n");
2161 return CL_EMALFDB;
2162 }
2163
2164 if(ret) {
2165 cli_errmsg("Problem parsing database at line %d\n", line);
2166 return ret;
2167 }
2168
2169 if(signo)
2170 *signo += sigs;
2171
2172 return CL_SUCCESS;
2173}
2174
2175/* 0 1 2 3 4 5 6 7 8 9 10 11
2176 * VirusName:ContainerType:ContainerSize:FileNameREGEX:FileSizeInContainer:FileSizeReal:IsEncrypted:FilePos:Res1:Res2[:MinFL[:MaxFL]]
2177 */
2178
2179#define CDB_TOKENS 12
2180static int cli_loadcdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
2181{
2182 const char *tokens[CDB_TOKENS + 1];
2183 char buffer[FILEBUFF], *buffer_cpy = NULL;
2184 unsigned int line = 0, sigs = 0, tokens_count, n0, n1;
2185 int ret = CL_SUCCESS;
2186 struct cli_cdb *new;
2187
2188
2189 if(engine->ignored)
2190 if(!(buffer_cpy = cli_malloc(FILEBUFF)))
2191 return CL_EMEM;
2192
2193 while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
2194 line++;
2195 if(buffer[0] == '#')
2196 continue;
2197
2198 cli_chomp(buffer);
2199 if(engine->ignored)
2200 strcpy(buffer_cpy, buffer);
2201
2202 tokens_count = cli_strtokenize(buffer, ':', CDB_TOKENS + 1, tokens);
2203 if(tokens_count > CDB_TOKENS || tokens_count < CDB_TOKENS - 2) {
2204 ret = CL_EMALFDB;
2205 break;
2206 }
2207
2208 if(tokens_count > 10) { /* min version */
2209 if(!cli_isnumber(tokens[10])) {
2210 ret = CL_EMALFDB;
2211 break;
2212 }
2213 if((unsigned int) atoi(tokens[10]) > cl_retflevel()) {
2214 cli_dbgmsg("cli_loadcdb: Container signature for %s not loaded (required f-level: %u)\n", tokens[0], atoi(tokens[10]));
2215 continue;
2216 }
2217 if(tokens_count == CDB_TOKENS) { /* max version */
2218 if(!cli_isnumber(tokens[11])) {
2219 ret = CL_EMALFDB;
2220 break;
2221 }
2222 if((unsigned int) atoi(tokens[11]) < cl_retflevel())
2223 continue;
2224 }
2225 }
2226
2227 new = (struct cli_cdb *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_cdb));
2228 if(!new) {
2229 ret = CL_EMEM;
2230 break;
2231 }
2232
2233 new->virname = cli_mpool_virname(engine->mempool, tokens[0], options & CL_DB_OFFICIAL);
2234 if(!new->virname) {
2235 mpool_free(engine->mempool, new);
2236 ret = CL_EMEM;
2237 break;
2238 }
2239
2240 if(engine->ignored && cli_chkign(engine->ignored, new->virname, buffer/*_cpy*/)) {
2241 mpool_free(engine->mempool, new->virname);
2242 mpool_free(engine->mempool, new);
2243 continue;
2244 }
2245
2246 if(engine->cb_sigload && engine->cb_sigload("cdb", new->virname, engine->cb_sigload_ctx)) {
2247 cli_dbgmsg("cli_loadcdb: skipping %s due to callback\n", new->virname);
2248 mpool_free(engine->mempool, new->virname);
2249 mpool_free(engine->mempool, new);
2250 continue;
2251 }
2252
2253 if(!strcmp(tokens[1], "*")) {
2254 new->ctype = CL_TYPE_ANY;
2255 } else if((new->ctype = cli_ftcode(tokens[1])) == CL_TYPE_ERROR) {
2256 cli_dbgmsg("cli_loadcdb: Unknown container type %s in signature for %s, skipping\n", tokens[1], tokens[0]);
2257 mpool_free(engine->mempool, new->virname);
2258 mpool_free(engine->mempool, new);
2259 continue;
2260 }
2261
2262 if(strcmp(tokens[3], "*") && cli_regcomp(&new->name, tokens[3], REG_EXTENDED | REG_NOSUB)) {
2263 cli_errmsg("cli_loadcdb: Can't compile regular expression %s in signature for %s\n", tokens[3], tokens[0]);
2264 mpool_free(engine->mempool, new->virname);
2265 mpool_free(engine->mempool, new);
2266 ret = CL_EMEM;
2267 break;
2268 }
2269
2270#define CDBRANGE(token_str, dest) \
2271 if(strcmp(token_str, "*")) { \
2272 if(strchr(token_str, '-')) { \
2273 if(sscanf(token_str, "%u-%u", &n0, &n1) != 2) { \
2274 ret = CL_EMALFDB; \
2275 } else { \
2276 dest[0] = n0; \
2277 dest[1] = n1; \
2278 } \
2279 } else { \
2280 if(!cli_isnumber(token_str)) \
2281 ret = CL_EMALFDB; \
2282 else \
2283 dest[0] = dest[1] = atoi(token_str); \
2284 } \
2285 if(ret != CL_SUCCESS) { \
2286 cli_errmsg("cli_loadcdb: Invalid value %s in signature for %s\n",\
2287 token_str, tokens[0]); \
2288 if(new->name.re_magic) \
2289 cli_regfree(&new->name); \
2290 mpool_free(engine->mempool, new->virname); \
2291 mpool_free(engine->mempool, new); \
2292 ret = CL_EMEM; \
2293 break; \
2294 } \
2295 } else { \
2296 dest[0] = dest[1] = CLI_OFF_ANY; \
2297 }
2298
2299 CDBRANGE(tokens[2], new->csize);
2300 CDBRANGE(tokens[4], new->fsizec);
2301 CDBRANGE(tokens[5], new->fsizer);
2302 CDBRANGE(tokens[7], new->filepos);
2303
2304 if(!strcmp(tokens[6], "*")) {
2305 new->encrypted = 2;
2306 } else {
2307 if(strcmp(tokens[6], "0") && strcmp(tokens[6], "1")) {
2308 cli_errmsg("cli_loadcdb: Invalid encryption flag value in signature for %s\n", tokens[0]);
2309 if(new->name.re_magic)
2310 cli_regfree(&new->name);
2311 mpool_free(engine->mempool, new->virname);
2312 mpool_free(engine->mempool, new);
2313 ret = CL_EMEM;
2314 break;
2315 }
2316 new->encrypted = *tokens[6] - 0x30;
2317 }
2318
2319 if(strcmp(tokens[9], "*")) {
2320 new->res2 = cli_mpool_strdup(engine->mempool, tokens[9]);
2321 if(!new->res2) {
2322 cli_errmsg("cli_loadcdb: Can't allocate memory for res2 in signature for %s\n", tokens[0]);
2323 if(new->name.re_magic)
2324 cli_regfree(&new->name);
2325 mpool_free(engine->mempool, new->virname);
2326 mpool_free(engine->mempool, new);
2327 ret = CL_EMEM;
2328 break;
2329 }
2330 }
2331
2332 new->next = engine->cdb;
2333 engine->cdb = new;
2334 sigs++;
2335 }
2336 if(engine->ignored)
2337 free(buffer_cpy);
2338
2339 if(!line) {
2340 cli_errmsg("Empty database file\n");
2341 return CL_EMALFDB;
2342 }
2343
2344 if(ret) {
2345 cli_errmsg("Problem parsing database at line %u\n", line);
2346 return ret;
2347 }
2348
2349 if(signo)
2350 *signo += sigs;
2351
2352 return CL_SUCCESS;
2353}
2354
2355static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options);
2356
2357int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
2358{
2359 FILE *fs = NULL;
2360 int ret = CL_SUCCESS;
2361 uint8_t skipped = 0;
2362 const char *dbname;
2363
2364
2365 if(!dbio && (fs = fopen(filename, "rb")) == NULL) {
2366 if(options & CL_DB_DIRECTORY) { /* bb#1624 */
2367 if(access(filename, R_OK)) {
2368 if(errno == ENOENT) {
2369 cli_dbgmsg("Detected race condition, ignoring old file %s\n", filename);
2370 return CL_SUCCESS;
2371 }
2372 }
2373 }
2374 cli_errmsg("cli_load(): Can't open file %s\n", filename);
2375 return CL_EOPEN;
2376 }
2377
2378 if((dbname = strrchr(filename, *PATHSEP)))
2379 dbname++;
2380 else
2381 dbname = filename;
2382
2383 if(cli_strbcasestr(dbname, ".db")) {
2384 ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
2385
2386 } else if(cli_strbcasestr(dbname, ".cvd")) {
2387 ret = cli_cvdload(fs, engine, signo, options, 0, filename);
2388
2389 } else if(cli_strbcasestr(dbname, ".cld")) {
2390 ret = cli_cvdload(fs, engine, signo, options, 1, filename);
2391
2392 } else if(cli_strbcasestr(dbname, ".hdb")) {
2393 ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options, dbio, dbname);
2394
2395 } else if(cli_strbcasestr(dbname, ".hdu")) {
2396 if(options & CL_DB_PUA)
2397 ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
2398 else
2399 skipped = 1;
2400
2401 } else if(cli_strbcasestr(dbname, ".fp")) {
2402 ret = cli_loadmd5(fs, engine, signo, MD5_FP, options, dbio, dbname);
2403
2404 } else if(cli_strbcasestr(dbname, ".mdb")) {
2405 ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options, dbio, dbname);
2406
2407 } else if(cli_strbcasestr(dbname, ".mdu")) {
2408 if(options & CL_DB_PUA)
2409 ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
2410 else
2411 skipped = 1;
2412
2413 } else if(cli_strbcasestr(dbname, ".ndb")) {
2414 ret = cli_loadndb(fs, engine, signo, 0, options, dbio, dbname);
2415
2416 } else if(cli_strbcasestr(dbname, ".ndu")) {
2417 if(!(options & CL_DB_PUA))
2418 skipped = 1;
2419 else
2420 ret = cli_loadndb(fs, engine, signo, 0, options | CL_DB_PUA_MODE, dbio, dbname);
2421
2422 } else if(cli_strbcasestr(filename, ".ldb")) {
2423 ret = cli_loadldb(fs, engine, signo, options, dbio, dbname);
2424
2425 } else if(cli_strbcasestr(filename, ".ldu")) {
2426 if(options & CL_DB_PUA)
2427 ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname);
2428 else
2429 skipped = 1;
2430 } else if(cli_strbcasestr(filename, ".cbc")) {
2431 if(options & CL_DB_BYTECODE)
2432 ret = cli_loadcbc(fs, engine, signo, options, dbio, dbname);
2433 else
2434 skipped = 1;
2435 } else if(cli_strbcasestr(dbname, ".sdb")) {
2436 ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname);
2437
2438 } else if(cli_strbcasestr(dbname, ".zmd")) {
2439 ret = cli_loadmd(fs, engine, signo, 1, options, dbio, dbname);
2440
2441 } else if(cli_strbcasestr(dbname, ".rmd")) {
2442 ret = cli_loadmd(fs, engine, signo, 2, options, dbio, dbname);
2443
2444 } else if(cli_strbcasestr(dbname, ".cfg")) {
2445 ret = cli_dconf_load(fs, engine, options, dbio);
2446
2447 } else if(cli_strbcasestr(dbname, ".info")) {
2448 ret = cli_loadinfo(fs, engine, options, dbio);
2449
2450 } else if(cli_strbcasestr(dbname, ".wdb")) {
2451 if(options & CL_DB_PHISHING_URLS) {
2452 ret = cli_loadwdb(fs, engine, options, dbio);
2453 } else
2454 skipped = 1;
2455 } else if(cli_strbcasestr(dbname, ".pdb") || cli_strbcasestr(dbname, ".gdb")) {
2456 if(options & CL_DB_PHISHING_URLS) {
2457 ret = cli_loadpdb(fs, engine, signo, options, dbio);
2458 } else
2459 skipped = 1;
2460 } else if(cli_strbcasestr(dbname, ".ftm")) {
2461 ret = cli_loadftm(fs, engine, options, 0, dbio);
2462
2463 } else if(cli_strbcasestr(dbname, ".ign") || cli_strbcasestr(dbname, ".ign2")) {
2464 ret = cli_loadign(fs, engine, options, dbio);
2465
2466 } else if(cli_strbcasestr(dbname, ".idb")) {
2467 ret = cli_loadidb(fs, engine, signo, options, dbio);
2468
2469 } else if(cli_strbcasestr(dbname, ".cdb")) {
2470 ret = cli_loadcdb(fs, engine, signo, options, dbio);
2471
2472 } else {
2473 cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
2474 ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
2475 }
2476
2477 if(ret) {
2478 cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
2479 } else {
2480 if(skipped)
2481 cli_dbgmsg("%s skipped\n", filename);
2482 else
2483 cli_dbgmsg("%s loaded\n", filename);
2484 }
2485
2486 if(fs)
2487 fclose(fs);
2488
2489 return ret;
2490}
2491
2492static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options)
2493{
2494 DIR *dd;
2495 struct dirent *dent;
2496#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
2497 union {
2498 struct dirent d;
2499 char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
2500 } result;
2501#endif
2502 char *dbfile;
2503 int ret = CL_EOPEN, have_cld;
2504 struct cl_cvd *daily_cld, *daily_cvd;
2505
2506
2507 cli_dbgmsg("Loading databases from %s\n", dirname);
2508
2509 if((dd = opendir(dirname)) == NULL) {
2510 cli_errmsg("cli_loaddbdir(): Can't open directory %s\n", dirname);
2511 return CL_EOPEN;
2512 }
2513
2514 /* first round - load .ign and .ign2 files */
2515#ifdef HAVE_READDIR_R_3
2516 while(!readdir_r(dd, &result.d, &dent) && dent) {
2517#elif defined(HAVE_READDIR_R_2)
2518 while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
2519#else
2520 while((dent = readdir(dd))) {
2521#endif
2522 if(dent->d_ino)
2523 {
2524 if(cli_strbcasestr(dent->d_name, ".ign") || cli_strbcasestr(dent->d_name, ".ign2")) {
2525 dbfile = (char *) cli_malloc(strlen(dent->d_name) + strlen(dirname) + 2);
2526 if(!dbfile) {
2527 cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
2528 closedir(dd);
2529 return CL_EMEM;
2530 }
2531 sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
2532 ret = cli_load(dbfile, engine, signo, options, NULL);
2533 if(ret) {
2534 cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
2535 free(dbfile);
2536 closedir(dd);
2537 return ret;
2538 }
2539 free(dbfile);
2540 }
2541 }
2542 }
2543
2544 /* the daily db must be loaded before main */
2545 dbfile = (char *) cli_malloc(strlen(dirname) + 20);
2546 if(!dbfile) {
2547 closedir(dd);
2548 return CL_EMEM;
2549 }
2550
2551 sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
2552 have_cld = !access(dbfile, R_OK);
2553 if(have_cld) {
2554 daily_cld = cl_cvdhead(dbfile);
2555 if(!daily_cld) {
2556 cli_errmsg("cli_loaddbdir(): error parsing header of %s\n", dbfile);
2557 free(dbfile);
2558 closedir(dd);
2559 return CL_EMALFDB;
2560 }
2561 }
2562 sprintf(dbfile, "%s"PATHSEP"daily.cvd", dirname);
2563 if(!access(dbfile, R_OK)) {
2564 if(have_cld) {
2565 daily_cvd = cl_cvdhead(dbfile);
2566 if(!daily_cvd) {
2567 cli_errmsg("cli_loaddbdir(): error parsing header of %s\n", dbfile);
2568 free(dbfile);
2569 cl_cvdfree(daily_cld);
2570 closedir(dd);
2571 return CL_EMALFDB;
2572 }
2573 if(daily_cld->version > daily_cvd->version)
2574 sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
2575 cl_cvdfree(daily_cvd);
2576 }
2577 } else {
2578 sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
2579 }
2580 if(have_cld)
2581 cl_cvdfree(daily_cld);
2582
2583 if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
2584 free(dbfile);
2585 closedir(dd);
2586 return ret;
2587 }
2588
2589 /* try to load local.gdb next */
2590 sprintf(dbfile, "%s"PATHSEP"local.gdb", dirname);
2591 if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
2592 free(dbfile);
2593 closedir(dd);
2594 return ret;
2595 }
2596
2597 /* check for and load daily.cfg */
2598 sprintf(dbfile, "%s"PATHSEP"daily.cfg", dirname);
2599 if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
2600 free(dbfile);
2601 closedir(dd);
2602 return ret;
2603 }
2604 free(dbfile);
2605
2606 /* second round - load everything else */
2607 rewinddir(dd);
2608#ifdef HAVE_READDIR_R_3
2609 while(!readdir_r(dd, &result.d, &dent) && dent) {
2610#elif defined(HAVE_READDIR_R_2)
2611 while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
2612#else
2613 while((dent = readdir(dd))) {
2614#endif
2615 if(dent->d_ino)
2616 {
2617 if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && strcmp(dent->d_name, "daily.cvd") && strcmp(dent->d_name, "daily.cld") && strcmp(dent->d_name, "daily.cfg") && CLI_DBEXT(dent->d_name)) {
2618 if((options & CL_DB_OFFICIAL_ONLY) && !strstr(dirname, "clamav-") && !cli_strbcasestr(dent->d_name, ".cld") && !cli_strbcasestr(dent->d_name, ".cvd")) {
2619 cli_dbgmsg("Skipping unofficial database %s\n", dent->d_name);
2620 continue;
2621 }
2622
2623 dbfile = (char *) cli_malloc(strlen(dent->d_name) + strlen(dirname) + 2);
2624 if(!dbfile) {
2625 cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
2626 closedir(dd);
2627 return CL_EMEM;
2628 }
2629 sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
2630 ret = cli_load(dbfile, engine, signo, options, NULL);
2631 if(ret) {
2632 cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
2633 free(dbfile);
2634 closedir(dd);
2635 return ret;
2636 }
2637 free(dbfile);
2638 }
2639 }
2640 }
2641 closedir(dd);
2642 if(ret == CL_EOPEN)
2643 cli_errmsg("cli_loaddb(): No supported database files found in %s\n", dirname);
2644
2645 return ret;
2646}
2647
2648int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int dboptions)
2649{
2650 struct stat sb;
2651 int ret;
2652
2653 if(!engine) {
2654 cli_errmsg("cl_load: engine == NULL\n");
2655 return CL_ENULLARG;
2656 }
2657
2658 if(engine->dboptions & CL_DB_COMPILED) {
2659 cli_errmsg("cl_load(): can't load new databases when engine is already compiled\n");
2660 return CL_EARG;
2661 }
2662
2663 if(stat(path, &sb) == -1) {
2664 cli_errmsg("cl_load(): Can't get status of %s\n", path);
2665 return CL_ESTAT;
2666 }
2667
2668 if((dboptions & CL_DB_PHISHING_URLS) && !engine->phishcheck && (engine->dconf->phishing & PHISHING_CONF_ENGINE))
2669 if((ret = phishing_init(engine)))
2670 return ret;
2671
2672 if((dboptions & CL_DB_BYTECODE) && !engine->bcs.inited) {
2673 if((ret = cli_bytecode_init(&engine->bcs)))
2674 return ret;
2675 } else {
2676 cli_dbgmsg("Bytecode engine disabled\n");
2677 }
2678
2679 if(cli_cache_init(engine))
2680 return CL_EMEM;
2681
2682 engine->dboptions |= dboptions;
2683
2684 switch(sb.st_mode & S_IFMT) {
2685 case S_IFREG:
2686 ret = cli_load(path, engine, signo, dboptions, NULL);
2687 break;
2688
2689 case S_IFDIR:
2690 ret = cli_loaddbdir(path, engine, signo, dboptions | CL_DB_DIRECTORY);
2691 break;
2692
2693 default:
2694 cli_errmsg("cl_load(%s): Not supported database file type\n", path);
2695 return CL_EOPEN;
2696 }
2697 return ret;
2698}
2699
2700const char *cl_retdbdir(void)
2701{
2702 return DATADIR;
2703}
2704
2705int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
2706{
2707 DIR *dd;
2708 const struct dirent *dent;
2709#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
2710 union {
2711 struct dirent d;
2712 char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
2713 } result;
2714#endif
2715 char *fname;
2716
2717
2718 if(dbstat) {
2719 dbstat->entries = 0;
2720 dbstat->stattab = NULL;
2721 dbstat->statdname = NULL;
2722 dbstat->dir = cli_strdup(dirname);
2723 } else {
2724 cli_errmsg("cl_statdbdir(): Null argument passed.\n");
2725 return CL_ENULLARG;
2726 }
2727
2728 if((dd = opendir(dirname)) == NULL) {
2729 cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname);
2730 cl_statfree(dbstat);
2731 return CL_EOPEN;
2732 }
2733
2734 cli_dbgmsg("Stat()ing files in %s\n", dirname);
2735
2736#ifdef HAVE_READDIR_R_3
2737 while(!readdir_r(dd, &result.d, &dent) && dent) {
2738#elif defined(HAVE_READDIR_R_2)
2739 while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
2740#else
2741 while((dent = readdir(dd))) {
2742#endif
2743 if(dent->d_ino)
2744 {
2745 if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
2746 dbstat->entries++;
2747 dbstat->stattab = (struct stat *) cli_realloc2(dbstat->stattab, dbstat->entries * sizeof(struct stat));
2748 if(!dbstat->stattab) {
2749 cl_statfree(dbstat);
2750 closedir(dd);
2751 return CL_EMEM;
2752 }
2753
2754#ifdef _WIN32
2755 dbstat->statdname = (char **) cli_realloc2(dbstat->statdname, dbstat->entries * sizeof(char *));
2756 if(!dbstat->statdname) {
2757 cl_statfree(dbstat);
2758 closedir(dd);
2759 return CL_EMEM;
2760 }
2761#endif
2762
2763 fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 32);
2764 if(!fname) {
2765 cl_statfree(dbstat);
2766 closedir(dd);
2767 return CL_EMEM;
2768 }
2769 sprintf(fname, "%s"PATHSEP"%s", dirname, dent->d_name);
2770#ifdef _WIN32
2771 dbstat->statdname[dbstat->entries - 1] = (char *) cli_malloc(strlen(dent->d_name) + 1);
2772 if(!dbstat->statdname[dbstat->entries - 1]) {
2773 cl_statfree(dbstat);
2774 closedir(dd);
2775 return CL_EMEM;
2776 }
2777
2778 strcpy(dbstat->statdname[dbstat->entries - 1], dent->d_name);
2779#endif
2780 stat(fname, &dbstat->stattab[dbstat->entries - 1]);
2781 free(fname);
2782 }
2783 }
2784 }
2785
2786 closedir(dd);
2787 return CL_SUCCESS;
2788}
2789
2790int cl_statchkdir(const struct cl_stat *dbstat)
2791{
2792 DIR *dd;
2793 struct dirent *dent;
2794#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
2795 union {
2796 struct dirent d;
2797 char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
2798 } result;
2799#endif
2800 struct stat sb;
2801 unsigned int i, found;
2802 char *fname;
2803
2804
2805 if(!dbstat || !dbstat->dir) {
2806 cli_errmsg("cl_statdbdir(): Null argument passed.\n");
2807 return CL_ENULLARG;
2808 }
2809
2810 if((dd = opendir(dbstat->dir)) == NULL) {
2811 cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir);
2812 return CL_EOPEN;
2813 }
2814
2815 cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir);
2816
2817#ifdef HAVE_READDIR_R_3
2818 while(!readdir_r(dd, &result.d, &dent) && dent) {
2819#elif defined(HAVE_READDIR_R_2)
2820 while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
2821#else
2822 while((dent = readdir(dd))) {
2823#endif
2824 if(dent->d_ino)
2825 {
2826 if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
2827 fname = cli_malloc(strlen(dbstat->dir) + strlen(dent->d_name) + 32);
2828 if(!fname) {
2829 closedir(dd);
2830 return CL_EMEM;
2831 }
2832
2833 sprintf(fname, "%s"PATHSEP"%s", dbstat->dir, dent->d_name);
2834 stat(fname, &sb);
2835 free(fname);
2836
2837 found = 0;
2838 for(i = 0; i < dbstat->entries; i++)
2839#ifdef _WIN32
2840 if(!strcmp(dbstat->statdname[i], dent->d_name)) {
2841#else
2842 if(dbstat->stattab[i].st_ino == sb.st_ino) {
2843#endif
2844 found = 1;
2845 if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
2846 closedir(dd);
2847 return 1;
2848 }
2849 }
2850
2851 if(!found) {
2852 closedir(dd);
2853 return 1;
2854 }
2855 }
2856 }
2857 }
2858
2859 closedir(dd);
2860 return CL_SUCCESS;
2861}
2862
2863int cl_statfree(struct cl_stat *dbstat)
2864{
2865
2866 if(dbstat) {
2867
2868#ifdef _WIN32
2869 int i;
2870
2871 if(dbstat->statdname) {
2872 for(i = 0; i < dbstat->entries; i++) {
2873 if(dbstat->statdname[i])
2874 free(dbstat->statdname[i]);
2875 dbstat->statdname[i] = NULL;
2876 }
2877 free(dbstat->statdname);
2878 dbstat->statdname = NULL;
2879 }
2880#endif
2881
2882 if(dbstat->stattab) {
2883 free(dbstat->stattab);
2884 dbstat->stattab = NULL;
2885 }
2886 dbstat->entries = 0;
2887
2888 if(dbstat->dir) {
2889 free(dbstat->dir);
2890 dbstat->dir = NULL;
2891 }
2892 } else {
2893 cli_errmsg("cl_statfree(): Null argument passed\n");
2894 return CL_ENULLARG;
2895 }
2896
2897 return CL_SUCCESS;
2898}
2899
2900int cl_engine_free(struct cl_engine *engine)
2901{
2902 unsigned int i, j;
2903 struct cli_matcher *root;
2904
2905
2906 if(!engine) {
2907 cli_errmsg("cl_free: engine == NULL\n");
2908 return CL_ENULLARG;
2909 }
2910
2911#ifdef CL_THREAD_SAFE
2912 pthread_mutex_lock(&cli_ref_mutex);
2913#endif
2914
2915 if(engine->refcount)
2916 engine->refcount--;
2917
2918 if(engine->refcount) {
2919#ifdef CL_THREAD_SAFE
2920 pthread_mutex_unlock(&cli_ref_mutex);
2921#endif
2922 return CL_SUCCESS;
2923 }
2924
2925#ifdef CL_THREAD_SAFE
2926 pthread_mutex_unlock(&cli_ref_mutex);
2927#endif
2928 if(engine->root) {
2929 for(i = 0; i < CLI_MTARGETS; i++) {
2930 if((root = engine->root[i])) {
2931 if(!root->ac_only)
2932 cli_bm_free(root);
2933 cli_ac_free(root);
2934 if(root->ac_lsigtable) {
2935 for(j = 0; j < root->ac_lsigs; j++) {
2936 mpool_free(engine->mempool, root->ac_lsigtable[j]->logic);
2937 FREE_TDB(root->ac_lsigtable[j]->tdb);
2938 mpool_free(engine->mempool, root->ac_lsigtable[j]);
2939 }
2940 mpool_free(engine->mempool, root->ac_lsigtable);
2941 }
2942 mpool_free(engine->mempool, root);
2943 }
2944 }
2945 mpool_free(engine->mempool, engine->root);
2946 }
2947
2948 if((root = engine->md5_hdb)) {
2949 cli_md5m_free(root);
2950 mpool_free(engine->mempool, root);
2951 }
2952
2953 if((root = engine->md5_mdb)) {
2954 cli_md5m_free(root);
2955 mpool_free(engine->mempool, root->soff);
2956 if(root->md5_sizes_hs.capacity) {
2957 cli_hashset_destroy(&root->md5_sizes_hs);
2958 }
2959 mpool_free(engine->mempool, root);
2960 }
2961
2962 if((root = engine->md5_fp)) {
2963 cli_md5m_free(root);
2964 mpool_free(engine->mempool, root);
2965 }
2966
2967 while(engine->cdb) {
2968 struct cli_cdb *pt = engine->cdb;
2969 engine->cdb = pt->next;
2970 if(pt->name.re_magic)
2971 cli_regfree(&pt->name);
2972 mpool_free(engine->mempool, pt->res2);
2973 mpool_free(engine->mempool, pt->virname);
2974 mpool_free(engine->mempool, pt);
2975 }
2976
2977 while(engine->dbinfo) {
2978 struct cli_dbinfo *pt = engine->dbinfo;
2979 engine->dbinfo = pt->next;
2980 mpool_free(engine->mempool, pt->name);
2981 mpool_free(engine->mempool, pt->hash);
2982 if(pt->cvd)
2983 cl_cvdfree(pt->cvd);
2984 mpool_free(engine->mempool, pt);
2985 }
2986
2987 if(engine->dconf->bytecode & BYTECODE_ENGINE_MASK) {
2988 if (engine->bcs.all_bcs)
2989 for(i=0;i<engine->bcs.count;i++)
2990 cli_bytecode_destroy(&engine->bcs.all_bcs[i]);
2991 cli_bytecode_done(&engine->bcs);
2992 free(engine->bcs.all_bcs);
2993 for (i=0;i<_BC_LAST_HOOK - _BC_START_HOOKS;i++) {
2994 free (engine->hooks[i]);
2995 }
2996 }
2997 if(engine->dconf->phishing & PHISHING_CONF_ENGINE)
2998 phishing_done(engine);
2999 if(engine->dconf)
3000 mpool_free(engine->mempool, engine->dconf);
3001
3002 if(engine->pua_cats)
3003 mpool_free(engine->mempool, engine->pua_cats);
3004
3005 if(engine->iconcheck) {
3006 struct icon_matcher *iconcheck = engine->iconcheck;
3007 for(i=0; i<3; i++) {
3008 if(iconcheck->icons[i]) {
3009 for (j=0;j<iconcheck->icon_counts[i];j++) {
3010 struct icomtr* metric = iconcheck->icons[i];
3011 mpool_free(engine->mempool, metric[j].name);
3012 }
3013 mpool_free(engine->mempool, iconcheck->icons[i]);
3014 }
3015 }
3016 if(iconcheck->group_names[0]) {
3017 for(i=0; i<iconcheck->group_counts[0]; i++)
3018 mpool_free(engine->mempool, iconcheck->group_names[0][i]);
3019 mpool_free(engine->mempool, iconcheck->group_names[0]);
3020 }
3021 if(iconcheck->group_names[1]) {
3022 for(i=0; i<iconcheck->group_counts[1]; i++)
3023 mpool_free(engine->mempool, iconcheck->group_names[1][i]);
3024 mpool_free(engine->mempool, iconcheck->group_names[1]);
3025 }
3026 mpool_free(engine->mempool, iconcheck);
3027 }
3028
3029 if(engine->tmpdir)
3030 mpool_free(engine->mempool, engine->tmpdir);
3031
3032 if(engine->cache)
3033 cli_cache_destroy(engine);
3034
3035 cli_ftfree(engine);
3036 if(engine->ignored) {
3037 cli_bm_free(engine->ignored);
3038 mpool_free(engine->mempool, engine->ignored);
3039 }
3040
3041#ifdef USE_MPOOL
3042 if(engine->mempool) mpool_destroy(engine->mempool);
3043#endif
3044 free(engine);
3045 return CL_SUCCESS;
3046}
3047
3048static void cli_md5db_build(struct cli_matcher* root)
3049{
3050 if(root && root->md5_sizes_hs.capacity) {
3051 /* TODO: use hashset directly, instead of the array when matching*/
3052 cli_dbgmsg("Converting hashset to array: %u entries\n", root->md5_sizes_hs.count);
3053
3054#ifdef USE_MPOOL
3055 {
3056 uint32_t *mpoolht;
3057 unsigned int mpoolhtsz = root->md5_sizes_hs.count * sizeof(*mpoolht);
3058 root->soff = mpool_malloc(root->mempool, mpoolhtsz);
3059 root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &mpoolht);
3060 memcpy(root->soff, mpoolht, mpoolhtsz);
3061 free(mpoolht);
3062 }
3063#else
3064 root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &root->soff);
3065#endif
3066 cli_hashset_destroy(&root->md5_sizes_hs);
3067 cli_qsort(root->soff, root->soff_len, sizeof(uint32_t), NULL);
3068 }
3069}
3070
3071int cl_engine_compile(struct cl_engine *engine)
3072{
3073 unsigned int i;
3074 int ret;
3075 struct cli_matcher *root;
3076
3077
3078 if(!engine)
3079 return CL_ENULLARG;
3080
3081 if(!engine->ftypes)
3082 if((ret = cli_loadftm(NULL, engine, 0, 1, NULL)))
3083 return ret;
3084
3085 for(i = 0; i < CLI_MTARGETS; i++) {
3086 if((root = engine->root[i])) {
3087 if((ret = cli_ac_buildtrie(root)))
3088 return ret;
3089 cli_dbgmsg("Matcher[%u]: %s: AC sigs: %u (reloff: %u, absoff: %u) BM sigs: %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->maxpatlen, root->ac_only ? "(ac_only mode)" : "");
3090 }
3091 }
3092 if(engine->md5_hdb)
3093 cli_dbgmsg("MD5 sigs (files): %u\n", engine->md5_hdb->md5_patterns);
3094
3095 if(engine->md5_mdb)
3096 cli_dbgmsg("MD5 sigs (PE sections): %u\n", engine->md5_mdb->md5_patterns);
3097
3098 if((ret = cli_build_regex_list(engine->whitelist_matcher))) {
3099 return ret;
3100 }
3101 if((ret = cli_build_regex_list(engine->domainlist_matcher))) {
3102 return ret;
3103 }
3104 cli_md5db_build(engine->md5_mdb);
3105 if(engine->ignored) {
3106 cli_bm_free(engine->ignored);
3107 mpool_free(engine->mempool, engine->ignored);
3108 engine->ignored = NULL;
3109 }
3110 cli_dconf_print(engine->dconf);
3111 mpool_flush(engine->mempool);
3112
3113 /* Compile bytecode */
3114 if((ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode))) {
3115 cli_errmsg("Unable to compile/load bytecode: %s\n", cl_strerror(ret));
3116 return ret;
3117 }
3118
3119 engine->dboptions |= CL_DB_COMPILED;
3120 return CL_SUCCESS;
3121}
3122
3123int cl_engine_addref(struct cl_engine *engine)
3124{
3125 if(!engine) {
3126 cli_errmsg("cl_engine_addref: engine == NULL\n");
3127 return CL_ENULLARG;
3128 }
3129
3130#ifdef CL_THREAD_SAFE
3131 pthread_mutex_lock(&cli_ref_mutex);
3132#endif
3133
3134 engine->refcount++;
3135
3136#ifdef CL_THREAD_SAFE
3137 pthread_mutex_unlock(&cli_ref_mutex);
3138#endif
3139
3140 return CL_SUCCESS;
3141}
3142
3143static int countentries(const char *dbname, unsigned int *sigs)
3144{
3145 char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1];
3146 FILE *fs;
3147 unsigned int entry = 0;
3148
3149 fs = fopen(dbname, "r");
3150 if(!fs) {
3151 cli_errmsg("countentries: Can't open file %s\n", dbname);
3152 return CL_EOPEN;
3153 }
3154 while(fgets(buffer, sizeof(buffer), fs)) {
3155 if(buffer[0] == '#')
3156 continue;
3157 entry++;
3158 }
3159 fclose(fs);
3160 *sigs += entry;
3161 return CL_SUCCESS;
3162}
3163
3164static int countsigs(const char *dbname, unsigned int options, unsigned int *sigs)
3165{
3166 if((cli_strbcasestr(dbname, ".cvd") || cli_strbcasestr(dbname, ".cld"))) {
3167 if(options & CL_COUNTSIGS_OFFICIAL) {
3168 struct cl_cvd *cvd = cl_cvdhead(dbname);
3169 if(!cvd) {
3170 cli_errmsg("countsigs: Can't parse %s\n", dbname);
3171 return CL_ECVD;
3172 }
3173 *sigs += cvd->sigs;
3174 cl_cvdfree(cvd);
3175 }
3176 } else if(cli_strbcasestr(dbname, ".cbc")) {
3177 if(options & CL_COUNTSIGS_UNOFFICIAL)
3178 (*sigs)++;
3179
3180 } else if(cli_strbcasestr(dbname, ".wdb") || cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".ftm") || cli_strbcasestr(dbname, ".cfg")) {
3181 /* ignore */
3182
3183 } else if((options & CL_COUNTSIGS_UNOFFICIAL) && CLI_DBEXT(dbname)) {
3184 return countentries(dbname, sigs);
3185 }
3186
3187 return CL_SUCCESS;
3188}
3189
3190int cl_countsigs(const char *path, unsigned int countoptions, unsigned int *sigs)
3191{
3192 struct stat sb;
3193 char fname[1024];
3194 struct dirent *dent;
3195#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
3196 union {
3197 struct dirent d;
3198 char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
3199 } result;
3200#endif
3201 DIR *dd;
3202 int ret;
3203
3204 if(!sigs)
3205 return CL_ENULLARG;
3206
3207 if(stat(path, &sb) == -1) {
3208 cli_errmsg("cl_countsigs: Can't stat %s\n", path);
3209 return CL_ESTAT;
3210 }
3211
3212 if((sb.st_mode & S_IFMT) == S_IFREG) {
3213 return countsigs(path, countoptions, sigs);
3214
3215 } else if((sb.st_mode & S_IFMT) == S_IFDIR) {
3216 if((dd = opendir(path)) == NULL) {
3217 cli_errmsg("cl_countsigs: Can't open directory %s\n", path);
3218 return CL_EOPEN;
3219 }
3220#ifdef HAVE_READDIR_R_3
3221 while(!readdir_r(dd, &result.d, &dent) && dent) {
3222#elif defined(HAVE_READDIR_R_2)
3223 while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
3224#else
3225 while((dent = readdir(dd))) {
3226#endif
3227 if(dent->d_ino) {
3228 if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
3229 snprintf(fname, sizeof(fname), "%s"PATHSEP"%s", path, dent->d_name);
3230 fname[sizeof(fname) - 1] = 0;
3231 ret = countsigs(fname, countoptions, sigs);
3232 if(ret != CL_SUCCESS) {
3233 closedir(dd);
3234 return ret;
3235 }
3236 }
3237 }
3238 }
3239 closedir(dd);
3240 } else {
3241 cli_errmsg("cl_countsigs: Unsupported file type\n");
3242 return CL_EARG;
3243 }
3244
3245 return CL_SUCCESS;
3246}
Note: See TracBrowser for help on using the repository browser.