libclamav/readdb.c
e3aaff8e
 /*
ace26bfe
  *  Copyright (C) 2007-2010 Sourcefire, Inc.
4addba22
  *
2023340a
  *  Authors: Tomasz Kojm
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
b58fdfc2
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
b58fdfc2
 #endif
e3aaff8e
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
b58fdfc2
 #ifdef	HAVE_SYS_PARAM_H
15edd45f
 #include <sys/param.h>
b58fdfc2
 #endif
e3aaff8e
 #include <fcntl.h>
056d95dc
 #include <zlib.h>
99ca7f53
 #include <errno.h>
e3aaff8e
 
 #include "clamav.h"
e4ae7726
 #include "cvd.h"
b58fdfc2
 #ifdef	HAVE_STRINGS_H
 #include <strings.h>
 #endif
8000d078
 #include "matcher-ac.h"
 #include "matcher-bm.h"
44712fcb
 #include "matcher-md5.h"
bedc58de
 #include "matcher.h"
e3aaff8e
 #include "others.h"
 #include "str.h"
bc93eda0
 #include "dconf.h"
7021b545
 #include "filetypes.h"
 #include "filetypes_int.h"
fc83da82
 #include "readdb.h"
677fc4ba
 #include "cltypes.h"
4367454d
 #include "default.h"
04133ff9
 #include "md5.h"
de351ee1
 #include "sha256.h"
 #include "dsig.h"
e3aaff8e
 
fc83da82
 #include "phishcheck.h"
7dec7955
 #include "phish_whitelist.h"
 #include "phish_domaincheck_db.h"
3da4dd4c
 #include "regex_list.h"
c3671221
 #include "hashtab.h"
7dec7955
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
b94e66c4
 #include "mpool.h"
52dd3a6b
 #include "bytecode.h"
ab636570
 #include "bytecode_api.h"
52dd3a6b
 #include "bytecode_priv.h"
261e29da
 #include "cache.h"
f51e962f
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
 static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
d6e1ef16
 char *cli_virname(char *virname, unsigned int official)
b5513f8d
 {
 	char *newname, *pt;
 
 
     if(!virname)
 	return NULL;
 
     if((pt = strstr(virname, " (Clam)")))
d6e1ef16
 	*pt='\0';
b5513f8d
 
d6e1ef16
     if(!virname[0]) {
b5513f8d
 	cli_errmsg("cli_virname: Empty virus name\n");
 	return NULL;
     }
 
d6e1ef16
     if(official)
         return cli_strdup(virname);
b5513f8d
 
d6e1ef16
     newname = (char *) cli_malloc(strlen(virname) + 11 + 1);
     if(!newname) {
       cli_errmsg("cli_virname: Can't allocate memory for newname\n");
       return NULL;
b5513f8d
     }
d6e1ef16
     sprintf(newname, "%s.UNOFFICIAL", virname);
     return newname;
b5513f8d
 }
 
ec285505
 int 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)
888f5794
 {
8000d078
 	struct cli_bm_patt *bm_new;
084ee140
 	char *pt, *hexcpy, *start, *n;
b5513f8d
 	int ret, asterisk = 0;
33872a43
 	unsigned int i, j, hexlen, parts = 0;
084ee140
 	int mindist = 0, maxdist = 0, error = 0;
 
888f5794
 
33872a43
     hexlen = strlen(hexsig);
ab893605
     if (hexsig[0] == '$') {
 	/* macro */
 	unsigned smin, smax, tid;
2979de20
 	struct cli_ac_patt *patt;
ab893605
 	if (hexsig[hexlen-1] != '$') {
 	    cli_errmsg("cli_parseadd(): missing terminator $\n");
 	    return CL_EMALFDB;
 	}
 	if (!lsigid) {
 	    cli_errmsg("cli_parseadd(): macro signatures only valid inside logical signatures\n");
 	    return CL_EMALFDB;
 	}
 	if (sscanf(hexsig,"${%u-%u}%u$",
 		   &smin, &smax, &tid)  != 3) {
 	    cli_errmsg("cli_parseadd(): invalid macro signature format\n");
 	    return CL_EMALFDB;
 	}
 	if (tid >= 32) {
 	    cli_errmsg("cli_parseadd(): only 32 macro groups are supported\n");
 	    return CL_EMALFDB;
 	}
2979de20
 	patt = mpool_calloc(root->mempool, 1, sizeof(*patt));
 	if (!patt)
ab893605
 	    return CL_EMEM;
 	/* this is not a pattern that will be matched by AC itself, rather it is a
 	 * pattern checked by the lsig code */
2979de20
 	patt->ch_mindist[0] = smin;
 	patt->ch_maxdist[0] = smax;
 	patt->sigid = tid;
 	patt->length = root->ac_mindepth;
ab893605
 	/* dummy */
2979de20
 	patt->pattern = mpool_calloc(root->mempool, patt->length, sizeof(*patt->pattern));
 	if (patt->pattern) {
 	    free(patt);
cc4b3fca
 	    return CL_EMEM;
 	}
2979de20
 	if ((ret = cli_ac_addpatt(root, patt))) {
 	    mpool_free(root->mempool, patt->pattern);
 	    free(patt);
ab893605
 	    return ret;
 	}
 	return CL_SUCCESS;
     }
084ee140
     if(strchr(hexsig, '{')) {
888f5794
 
8000d078
 	root->ac_partsigs++;
084ee140
 
6f38c939
 	if(!(hexcpy = cli_strdup(hexsig)))
084ee140
 	    return CL_EMEM;
 
33872a43
 	for(i = 0; i < hexlen; i++)
a8f79297
 	    if(hexsig[i] == '{' || hexsig[i] == '*')
084ee140
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	start = pt = hexcpy;
 	for(i = 1; i <= parts; i++) {
 
 	    if(i != parts) {
a8f79297
 		for(j = 0; j < strlen(start); j++) {
 		    if(start[j] == '{') {
 			asterisk = 0;
 			pt = start + j;
 			break;
 		    }
 		    if(start[j] == '*') {
 			asterisk = 1;
 			pt = start + j;
 			break;
 		    }
 		}
084ee140
 		*pt++ = 0;
 	    }
 
341faf60
 	    if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
084ee140
 		error = 1;
 		break;
 	    }
 
 	    if(i == parts)
 		break;
 
a8f79297
 	    mindist = maxdist = 0;
 
 	    if(asterisk) {
 		start = pt;
 		continue;
 	    }
 
084ee140
 	    if(!(start = strchr(pt, '}'))) {
 		error = 1;
 		break;
 	    }
 	    *start++ = 0;
 
 	    if(!pt) {
 		error = 1;
 		break;
 	    }
 
 	    if(!strchr(pt, '-')) {
a3fe2c5b
 		if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
084ee140
 		    error = 1;
 		    break;
 		}
 	    } else {
b68d11d2
 		if((n = cli_strtok(pt, 0, "-"))) {
a3fe2c5b
 		    if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
084ee140
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
b68d11d2
 		if((n = cli_strtok(pt, 1, "-"))) {
a3fe2c5b
 		    if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
084ee140
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
a3fe2c5b
 
 		if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
 		    error = 1;
 		    free(n);
9b0004bf
 		    break;
a3fe2c5b
 		}
084ee140
 	    }
 	}
 
 	free(hexcpy);
 	if(error)
 	    return CL_EMALFDB;
 
     } else if(strchr(hexsig, '*')) {
8000d078
 	root->ac_partsigs++;
888f5794
 
33872a43
 	for(i = 0; i < hexlen; i++)
888f5794
 	    if(hexsig[i] == '*')
 		parts++;
 
8000d078
 	if(parts)
888f5794
 	    parts++;
 
 	for(i = 1; i <= parts; i++) {
 	    if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
3ed92fe1
 		cli_errmsg("Can't extract part %d of partial signature.\n", i);
888f5794
 		return CL_EMALFDB;
 	    }
 
341faf60
 	    if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
888f5794
 		free(pt);
 		return ret;
 	    }
 
 	    free(pt);
 	}
 
ab893605
     } else if(root->ac_only || type || lsigid || strpbrk(hexsig, "?([") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strchr(offset, '$')) {
341faf60
 	if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
8000d078
 	    return ret;
 	}
 
     } else {
47d40feb
 	bm_new = (struct cli_bm_patt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_bm_patt));
8000d078
 	if(!bm_new)
 	    return CL_EMEM;
47d40feb
         bm_new->pattern = (unsigned char *) cli_mpool_hex2str(root->mempool, hexsig);
b583d6ed
 	if(!bm_new->pattern) {
47d40feb
 	    mpool_free(root->mempool, bm_new);
8000d078
 	    return CL_EMALFDB;
091df0e3
 	}
33872a43
 	bm_new->length = hexlen / 2;
8000d078
 
12c6a97e
 	bm_new->virname = cli_mpool_virname(root->mempool, virname, options & CL_DB_OFFICIAL);
b5513f8d
 	if(!bm_new->virname) {
47d40feb
 	    mpool_free(root->mempool, bm_new->pattern);
 	    mpool_free(root->mempool, bm_new);
8000d078
 	    return CL_EMEM;
 	}
 
3b33bd68
 	if(bm_new->length > root->maxpatlen) {
8000d078
 	    root->maxpatlen = bm_new->length;
3b33bd68
 	}
8000d078
 
33872a43
 	if((ret = cli_bm_addpatt(root, bm_new, offset))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
47d40feb
 	    mpool_free(root->mempool, bm_new->pattern);
 	    mpool_free(root->mempool, bm_new->virname);
 	    mpool_free(root->mempool, bm_new);
888f5794
 	    return ret;
 	}
     }
 
8d3aca30
     return CL_SUCCESS;
888f5794
 }
e3aaff8e
 
a96eead4
 int cli_initroots(struct cl_engine *engine, unsigned int options)
5612732c
 {
 	int i, ret;
 	struct cli_matcher *root;
 
 
4addba22
     for(i = 0; i < CLI_MTARGETS; i++) {
5612732c
 	if(!engine->root[i]) {
 	    cli_dbgmsg("Initializing engine->root[%d]\n", i);
47d40feb
 	    root = engine->root[i] = (struct cli_matcher *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_matcher));
5612732c
 	    if(!root) {
0a3d4094
 		cli_errmsg("cli_initroots: Can't allocate memory for cli_matcher\n");
5612732c
 		return CL_EMEM;
 	    }
33872a43
 #ifdef USE_MPOOL
 	    root->mempool = engine->mempool;
 #endif
 	    root->type = i;
ab0d2f05
 	    if(cli_mtargets[i].ac_only || engine->ac_only)
83fa5305
 		root->ac_only = 1;
 
5612732c
 	    cli_dbgmsg("Initialising AC pattern matcher of root[%d]\n", i);
5b74e89a
 	    if((ret = cli_ac_init(root, engine->ac_mindepth, engine->ac_maxdepth, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
5612732c
 		/* no need to free previously allocated memory here */
0a3d4094
 		cli_errmsg("cli_initroots: Can't initialise AC pattern matcher\n");
fbcef1b0
 		return ret;
5612732c
 	    }
 
83fa5305
 	    if(!root->ac_only) {
0a3d4094
 		cli_dbgmsg("cli_initroots: Initializing BM tables of root[%d]\n", i);
83fa5305
 		if((ret = cli_bm_init(root))) {
0a3d4094
 		    cli_errmsg("cli_initroots: Can't initialise BM pattern matcher\n");
83fa5305
 		    return ret;
 		}
5612732c
 	    }
8000d078
 	}
     }
9995c517
     engine->root[1]->bm_offmode = 1; /* BM offset mode for PE files */
8d3aca30
     return CL_SUCCESS;
5612732c
 }
 
e8ae4fae
 char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio)
056d95dc
 {
a6a98456
     if(fs)
056d95dc
 	return fgets(buff, size, fs);
 
a6a98456
     if(dbio->usebuf) {
 	    int bread;
 	    char *nl;
 
 	while(1) {
 	    if(!dbio->bufpt) {
 		if(!dbio->size)
 		    return NULL;
 
 		if(dbio->gzs) {
 		    bread = gzread(dbio->gzs, dbio->readpt, dbio->readsize);
 		    if(bread == -1) {
 			cli_errmsg("cli_dbgets: gzread() failed\n");
 			return NULL;
 		    }
 		} else {
 		    bread = fread(dbio->readpt, 1, dbio->readsize, dbio->fs);
 		    if(!bread && ferror(dbio->fs)) {
 			cli_errmsg("cli_dbgets: gzread() failed\n");
 			return NULL;
 		    }
 		}
 		if(!bread)
 		    return NULL;
 		dbio->readpt[bread] = 0;
 		dbio->bufpt = dbio->buf;
 		dbio->size -= bread;
ace26bfe
 		dbio->bread += bread;
de351ee1
 		sha256_update(&dbio->sha256ctx, dbio->readpt, bread);
a6a98456
 	    }
 	    nl = strchr(dbio->bufpt, '\n');
 	    if(nl) {
 		if(nl - dbio->bufpt >= size) {
 		    cli_errmsg("cli_dbgets: Line too long for provided buffer\n");
 		    return NULL;
 		}
 		strncpy(buff, dbio->bufpt, nl - dbio->bufpt);
 		buff[nl - dbio->bufpt] = 0;
 		if(nl < dbio->buf + dbio->bufsize) {
 		    dbio->bufpt = ++nl;
 		} else {
 		    dbio->bufpt = NULL;
 		    dbio->readpt = dbio->buf;
 		    dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
 		}
 		return buff;
 	    } else {
 		    unsigned int remain = dbio->buf + dbio->bufsize - 1 - dbio->bufpt;
 
 		if(dbio->bufpt == dbio->buf) {
 		    cli_errmsg("cli_dbgets: Invalid data or internal buffer too small\n");
 		    return NULL;
 		}
 		memmove(dbio->buf, dbio->bufpt, remain);
 		dbio->readpt = dbio->buf + remain;
 		dbio->readsize = dbio->bufsize - remain;
 		dbio->readsize = dbio->size < dbio->bufsize - remain ? dbio->size : dbio->bufsize - remain - 1;
 		dbio->bufpt = NULL;
 	    }
 	}
     } else { /* use gzgets/fgets */
056d95dc
 	    char *pt;
 	    unsigned int bs;
 
e8ae4fae
 	if(!dbio->size)
056d95dc
 	    return NULL;
 
e8ae4fae
 	bs = dbio->size < size ? dbio->size + 1 : size;
 	if(dbio->gzs)
 	    pt = gzgets(dbio->gzs, buff, bs);
 	else
 	    pt = fgets(buff, bs, dbio->fs);
 
ace26bfe
 	if(!pt) {
056d95dc
 	    cli_errmsg("cli_dbgets: Preliminary end of data\n");
ace26bfe
 	    return pt;
 	}
 	bs = strlen(buff);
 	dbio->size -= bs;
 	dbio->bread += bs;
de351ee1
 	sha256_update(&dbio->sha256ctx, buff, bs);
056d95dc
 	return pt;
     }
 }
 
04133ff9
 static int cli_chkign(const struct cli_matcher *ignored, const char *signame, const char *entry)
ed9753e9
 {
04133ff9
 	const char *md5_expected = NULL;
         cli_md5_ctx md5ctx;
         unsigned char digest[16];
ed9753e9
 
04133ff9
     if(!ignored || !signame || !entry)
ed9753e9
 	return 0;
 
12c6a97e
     if(cli_bm_scanbuff((const unsigned char *) signame, strlen(signame), &md5_expected, NULL, ignored, 0, NULL, NULL) == CL_VIRUS) {
04133ff9
 	if(md5_expected) {
 	    cli_md5_init(&md5ctx);
             cli_md5_update(&md5ctx, entry, strlen(entry));
 	    cli_md5_final(digest, &md5ctx);
 	    if(memcmp(digest, (const unsigned char *) md5_expected, 16))
 		return 0;
ed9753e9
 	}
04133ff9
 	cli_dbgmsg("Ignoring signature %s\n", signame);
 	return 1;
ed9753e9
     }
 
     return 0;
 }
 
b023c36d
 static int cli_chkpua(const char *signame, const char *pua_cats, unsigned int options)
 {
 	char cat[32], *pt;
 	const char *sig;
 	int ret;
 
     if(strncmp(signame, "PUA.", 4)) {
 	cli_dbgmsg("Skipping signature %s - no PUA prefix\n", signame);
 	return 1;
     }
     sig = signame + 3;
     if(!(pt = strchr(sig + 1, '.'))) {
 	cli_dbgmsg("Skipping signature %s - bad syntax\n", signame);
 	return 1;
     }
 
a45c7039
     if((unsigned int) (pt - sig + 2) > sizeof(cat)) {
b023c36d
 	cli_dbgmsg("Skipping signature %s - too long category name\n", signame);
 	return 1;
     }
 
     strncpy(cat, sig, pt - signame + 1);
     cat[pt - sig + 1] = 0;
     pt = strstr(pua_cats, cat);
 
     if(options & CL_DB_PUA_INCLUDE)
 	ret = pt ? 0 : 1;
     else
 	ret = pt ? 1 : 0;
 
     if(ret)
 	cli_dbgmsg("Skipping PUA signature %s - excluded category\n", signame);
 
     return ret;
 }
 
15850fc6
 static int cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
5612732c
 {
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL, *pt, *start;
ed9753e9
 	unsigned int line = 0, sigs = 0;
 	int ret = 0;
5612732c
 	struct cli_matcher *root;
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
5612732c
 	return ret;
 
15850fc6
     root = engine->root[0];
5612732c
 
04133ff9
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
 	    return CL_EMEM;
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
4048c4f6
 	line++;
 	cli_chomp(buffer);
04133ff9
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
4048c4f6
 
 	pt = strchr(buffer, '=');
 	if(!pt) {
 	    cli_errmsg("Malformed pattern line %d\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	start = buffer;
 	*pt++ = 0;
ee039e40
 
04133ff9
 	if(engine->ignored && cli_chkign(engine->ignored, start, buffer_cpy))
ed9753e9
 	    continue;
 
eb422a03
 	if(engine->cb_sigload && engine->cb_sigload("db", start, engine->cb_sigload_ctx)) {
 	    cli_dbgmsg("cli_loaddb: skipping %s due to callback\n", start);
 	    continue;
 	}
 
4048c4f6
 	if(*pt == '=') continue;
ee039e40
 
33872a43
 	if((ret = cli_parse_add(root, start, pt, 0, 0, "*", 0, NULL, options))) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
ed9753e9
 	sigs++;
b68d11d2
     }
 
04133ff9
     if(engine->ignored)
 	free(buffer_cpy);
 
b68d11d2
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	return ret;
     }
 
     if(signo)
ed9753e9
 	*signo += sigs;
b68d11d2
 
8d3aca30
     return CL_SUCCESS;
b68d11d2
 }
 
cca29953
 #define ICO_TOKENS 4
182e40db
 static int cli_loadidb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
73d8cded
 {
         const char *tokens[ICO_TOKENS + 1];
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
182e40db
 	uint8_t *hash;
73d8cded
 	int ret = CL_SUCCESS;
182e40db
 	unsigned int line = 0, sigs = 0, tokens_count, i, size, enginesize;
73d8cded
 	struct icomtr *metric;
cca29953
 	struct icon_matcher *matcher;
73d8cded
 
cca29953
 
     if(!(matcher = (struct icon_matcher *)mpool_calloc(engine->mempool, sizeof(*matcher),1))) 
 	return CL_EMEM;
     
73d8cded
     if(engine->ignored)
a2f709da
 	if(!(buffer_cpy = cli_malloc(FILEBUFF))) {
 	    mpool_free(engine->mempool, matcher);
73d8cded
 	    return CL_EMEM;
a2f709da
 	}
73d8cded
 
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
 	line++;
1a1ee68a
 	if(buffer[0] == '#')
 	    continue;
 
73d8cded
 	cli_chomp(buffer);
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
 
 	tokens_count = cli_strtokenize(buffer, ':', ICO_TOKENS + 1, tokens);
 	if(tokens_count != ICO_TOKENS) {
419e2be4
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong token count)\n", line);
73d8cded
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
cca29953
 	if(strlen(tokens[3]) != 124) {
419e2be4
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (wrong length)\n", line);
73d8cded
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	if(engine->ignored && cli_chkign(engine->ignored, tokens[0], buffer_cpy))
 	    continue;
 
eb422a03
 	if(engine->cb_sigload && engine->cb_sigload("idb", tokens[0], engine->cb_sigload_ctx)) {
 	    cli_dbgmsg("cli_loadidb: skipping %s due to callback\n", tokens[0]);
 	    continue;
 	}
 
cca29953
 	hash = (uint8_t *)tokens[3];
4e968cd3
 	if(cli_hexnibbles((char *)hash, 124)) {
73d8cded
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad chars)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	size = (hash[0] << 4) + hash[1];
 	if(size != 32 && size != 24 && size != 16) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad size)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
182e40db
 	enginesize = (size >> 3) - 2;
73d8cded
 	hash+=2;
 
cca29953
 	metric = (struct icomtr *)mpool_realloc(engine->mempool, matcher->icons[enginesize], sizeof(struct icomtr) * (matcher->icon_counts[enginesize] + 1));
73d8cded
 	if(!metric) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
cca29953
 	matcher->icons[enginesize] = metric;
 	metric += matcher->icon_counts[enginesize];
 	matcher->icon_counts[enginesize]++;
73d8cded
 
 	for(i=0; i<3; i++) {
 	    if((metric->color_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072)
 		break;
 	    if((metric->color_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8)
 		break;
 	    if((metric->color_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8)
 		break;
 	    hash += 7;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad color data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	for(i=0; i<3; i++) {
 	    if((metric->gray_avg[i] = (hash[0] << 8) | (hash[1] << 4) | hash[2]) > 4072)
 		break;
 	    if((metric->gray_x[i] = (hash[3] << 4) | hash[4]) > size - size / 8)
 		break;
 	    if((metric->gray_y[i] = (hash[5] << 4) | hash[6]) > size - size / 8)
 		break;
 	    hash += 7;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad gray data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	for(i=0; i<3; i++) {
 	    metric->bright_avg[i] = (hash[0] << 4) | hash[1];
 	    if((metric->bright_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
 		break;
182e40db
 	    if((metric->bright_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
73d8cded
 		break;
 	    hash += 6;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad bright data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	for(i=0; i<3; i++) {
 	    metric->dark_avg[i] = (hash[0] << 4) | hash[1];
 	    if((metric->dark_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
 		break;
182e40db
 	    if((metric->dark_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
73d8cded
 		break;
 	    hash += 6;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad dark data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	for(i=0; i<3; i++) {
 	    metric->edge_avg[i] = (hash[0] << 4) | hash[1];
 	    if((metric->edge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
 		break;
182e40db
 	    if((metric->edge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
73d8cded
 		break;
 	    hash += 6;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad edge data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	for(i=0; i<3; i++) {
 	    metric->noedge_avg[i] = (hash[0] << 4) | hash[1];
 	    if((metric->noedge_x[i] = (hash[2] << 4) | hash[3]) > size - size / 8)
 		break;
182e40db
 	    if((metric->noedge_y[i] = (hash[4] << 4) | hash[5]) > size - size / 8)
73d8cded
 		break;
 	    hash += 6;
 	}
 	if(i!=3) {
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad noedge data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	metric->rsum = (hash[0] << 4) | hash[1];
 	metric->gsum = (hash[2] << 4) | hash[3];
 	metric->bsum = (hash[4] << 4) | hash[5];
 	metric->ccount = (hash[6] << 4) | hash[7];
fd183cf9
 	if(metric->rsum + metric->gsum + metric->bsum > 103 || metric->ccount > 100) {
73d8cded
 	    cli_errmsg("cli_loadidb: Malformed hash at line %u (bad spread data)\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	if(!(metric->name = cli_mpool_strdup(engine->mempool, tokens[0]))) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
cca29953
 	for(i=0; i<matcher->group_counts[0]; i++) {
 	    if(!strcmp(tokens[1], matcher->group_names[0][i]))
 		break;
 	}
 	if(i==matcher->group_counts[0]) {
 	    if(!(matcher->group_names[0] = mpool_realloc(engine->mempool, matcher->group_names[0], sizeof(char *) * (i + 1))) ||
 	       !(matcher->group_names[0][i] = cli_mpool_strdup(engine->mempool, tokens[1]))) {
 		ret = CL_EMEM;
 		break;
 	    }
 	    matcher->group_counts[0]++;
 	}
 	metric->group[0] = i;
 
 	for(i=0; i<matcher->group_counts[1]; i++) {
 	    if(!strcmp(tokens[2], matcher->group_names[1][i]))
 		break;
 	}
 	if(i==matcher->group_counts[1]) {
 	    if(!(matcher->group_names[1] = mpool_realloc(engine->mempool, matcher->group_names[1], sizeof(char *) * (i + 1))) ||
 	       !(matcher->group_names[1][i] = cli_mpool_strdup(engine->mempool, tokens[2]))) {
419e2be4
 		ret = CL_EMEM;
cca29953
 		break;
 	    }
 	    matcher->group_counts[1]++;
 	}
 	metric->group[1] = i;
419e2be4
 
 	if(matcher->group_counts[0] > 256 || matcher->group_counts[1] > 256) {
 	    cli_errmsg("cli_loadidb: too many icon groups!\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
cca29953
 
73d8cded
 	sigs++;
     }
     if(engine->ignored)
 	free(buffer_cpy);
 
     if(!line) {
182e40db
 	cli_errmsg("cli_loadidb: Empty database file\n");
73d8cded
 	return CL_EMALFDB;
     }
 
     if(ret) {
182e40db
 	cli_errmsg("cli_loadidb: Problem parsing database at line %u\n", line);
73d8cded
 	return ret;
     }
 
     if(signo)
 	*signo += sigs;
 
cca29953
     engine->iconcheck = matcher;
73d8cded
     return CL_SUCCESS;
 }
a8d621cf
 
15850fc6
 static int cli_loadwdb(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
3da4dd4c
 {
 	int ret = 0;
 
ec481027
 
15850fc6
     if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
692bda68
 	return CL_SUCCESS;
 
15850fc6
     if(!engine->whitelist_matcher) {
 	if((ret = init_whitelist(engine))) {
ec481027
 	    return ret;
3da4dd4c
 	}
ec481027
     }
3da4dd4c
 
9eff97c0
     if((ret = load_regex_matcher(engine->whitelist_matcher, fs, NULL, options, 1, dbio, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
ec481027
 	return ret;
     }
3da4dd4c
 
ec481027
     return CL_SUCCESS;
3da4dd4c
 }
 
03527bee
 static int cli_loadpdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
3da4dd4c
 {
 	int ret = 0;
 
ec481027
 
15850fc6
     if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
692bda68
 	return CL_SUCCESS;
 
15850fc6
     if(!engine->domainlist_matcher) {
 	if((ret = init_domainlist(engine))) {
ec481027
 	    return ret;
3da4dd4c
 	}
ec481027
     }
3da4dd4c
 
9eff97c0
     if((ret = load_regex_matcher(engine->domainlist_matcher, fs, signo, options, 0, dbio, engine->dconf->other&OTHER_CONF_PREFILTERING))) {
ec481027
 	return ret;
     }
 
     return CL_SUCCESS;
3da4dd4c
 }
ec481027
 
e4e8366f
 #define NDB_TOKENS 6
15850fc6
 static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned short sdb, unsigned int options, struct cli_dbio *dbio, const char *dbname)
b68d11d2
 {
72fb25ea
 	const char *tokens[NDB_TOKENS + 1];
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
e4e8366f
 	const char *sig, *virname, *offset, *pt;
5612732c
 	struct cli_matcher *root;
72fb25ea
 	int line = 0, sigs = 0, ret = 0, tokens_count;
b68d11d2
 	unsigned short target;
9f8098c0
 	unsigned int phish = options & CL_DB_PHISHING;
b68d11d2
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
5612732c
 	return ret;
b68d11d2
 
04133ff9
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
 	    return CL_EMEM;
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
b68d11d2
 	line++;
7afdc309
 
9f8098c0
 	if(!phish)
d6449522
 	    if(!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14))
 		continue;
 
b68d11d2
 	cli_chomp(buffer);
04133ff9
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
b68d11d2
 
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', NDB_TOKENS + 1, tokens);
486fd3da
 	if(tokens_count < 4 || tokens_count > 6) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	virname = tokens[0];
 
15850fc6
 	if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
 	    if(cli_chkpua(virname, engine->pua_cats, options))
b023c36d
 		continue;
 
04133ff9
 	if(engine->ignored && cli_chkign(engine->ignored, virname, buffer_cpy))
ed9753e9
 	    continue;
 
7f3ac446
 	if(engine->cb_sigload && engine->cb_sigload("ndb", virname, engine->cb_sigload_ctx)) {
eb422a03
 	    cli_dbgmsg("cli_loadndb: skipping %s due to callback\n", virname);
 	    continue;
 	}
 
72fb25ea
 	if(tokens_count > 4) { /* min version */
 	    pt = tokens[4];
039c9565
 
 	    if(!cli_isnumber(pt)) {
95083a4f
 		ret = CL_EMALFDB;
 		break;
 	    }
 
53721687
 	    if((unsigned int) atoi(pt) > cl_retflevel()) {
38d1d109
 		cli_dbgmsg("Signature for %s not loaded (required f-level: %d)\n", virname, atoi(pt));
95083a4f
 		continue;
 	    }
 
72fb25ea
 	    if(tokens_count == 6) { /* max version */
 		pt = tokens[5];
039c9565
 		if(!cli_isnumber(pt)) {
555c5390
 		    ret = CL_EMALFDB;
 		    break;
 		}
 
53721687
 		if((unsigned int) atoi(pt) < cl_retflevel()) {
555c5390
 		    continue;
 		}
 	    }
95083a4f
 	}
 
039c9565
 	if(!(pt = tokens[1]) || (strcmp(pt, "*") && !cli_isnumber(pt))) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	target = (unsigned short) atoi(pt);
 
4addba22
 	if(target >= CLI_MTARGETS) {
5612732c
 	    cli_dbgmsg("Not supported target type in signature for %s\n", virname);
 	    continue;
 	}
 
15850fc6
 	root = engine->root[target];
5612732c
 
72fb25ea
 	offset = tokens[2];
 	sig = tokens[3];
b68d11d2
 
677fc4ba
 	if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, NULL, options))) {
4048c4f6
 	    ret = CL_EMALFDB;
 	    break;
 	}
ed9753e9
 	sigs++;
ee039e40
     }
04133ff9
     if(engine->ignored)
 	free(buffer_cpy);
ee039e40
 
4048c4f6
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
8139fd99
 
4048c4f6
     if(ret) {
b68d11d2
 	cli_errmsg("Problem parsing database at line %d\n", line);
8139fd99
 	return ret;
     }
 
3805ebcb
     if(signo)
95083a4f
 	*signo += sigs;
4048c4f6
 
15850fc6
     if(sdb && sigs && !engine->sdb) {
 	engine->sdb = 1;
555c5390
 	cli_dbgmsg("*** Self protection mechanism activated.\n");
     }
 
8d3aca30
     return CL_SUCCESS;
4048c4f6
 }
 
677fc4ba
 struct lsig_attrib {
     const char *name;
     unsigned int type;
     void **pt;
 };
 
 /* TODO: rework this */
 static int lsigattribs(char *attribs, struct cli_lsig_tdb *tdb)
 {
 	struct lsig_attrib attrtab[] = {
4168b010
 #define ATTRIB_TOKENS	8
17cfd76f
 	    { "Target",		    CLI_TDB_UINT,	(void **) &tdb->target	    },
 	    { "Engine",		    CLI_TDB_RANGE,	(void **) &tdb->engine	    },
0f7ba617
 
17cfd76f
 	    { "FileSize",	    CLI_TDB_RANGE,	(void **) &tdb->filesize    },
 	    { "EntryPoint",	    CLI_TDB_RANGE,	(void **) &tdb->ep	    },
 	    { "NumberOfSections",   CLI_TDB_RANGE,	(void **) &tdb->nos	    },
 
 	    { "IconGroup1",	    CLI_TDB_STR,	(void **) &tdb->icongrp1    },
 	    { "IconGroup2",	    CLI_TDB_STR,	(void **) &tdb->icongrp2    },
0f7ba617
 
4168b010
 	    { "Container",	    CLI_TDB_FTYPE,	(void **) &tdb->container   },
677fc4ba
 /*
 	    { "SectOff",    CLI_TDB_RANGE2,	(void **) &tdb->sectoff	    },
 	    { "SectRVA",    CLI_TDB_RANGE2,	(void **) &tdb->sectrva	    },
 	    { "SectVSZ",    CLI_TDB_RANGE2,	(void **) &tdb->sectvsz	    },
 	    { "SectRAW",    CLI_TDB_RANGE2,	(void **) &tdb->sectraw	    },
 	    { "SectRSZ",    CLI_TDB_RANGE2,	(void **) &tdb->sectrsz	    },
 	    { "SectURVA",   CLI_TDB_RANGE2,	(void **) &tdb->secturva    },
 	    { "SectUVSZ",   CLI_TDB_RANGE2,	(void **) &tdb->sectuvsz    },
 	    { "SectURAW",   CLI_TDB_RANGE2,	(void **) &tdb->secturaw    },
 	    { "SectURSZ",   CLI_TDB_RANGE2,	(void **) &tdb->sectursz    },
 */
 	    { NULL,	    0,			NULL,			    }
 	};
 	struct lsig_attrib *apt;
 	char *tokens[ATTRIB_TOKENS], *pt, *pt2;
fa4d6353
 	unsigned int v1, v2, v3, i, j, tokens_count, have_newext = 0;
677fc4ba
 	uint32_t cnt, off[ATTRIB_TOKENS];
 
 
72fb25ea
     tokens_count = cli_strtokenize(attribs, ',', ATTRIB_TOKENS, (const char **) tokens);
677fc4ba
 
72fb25ea
     for(i = 0; i < tokens_count; i++) {
677fc4ba
 	if(!(pt = strchr(tokens[i], ':'))) {
 	    cli_errmsg("lsigattribs: Incorrect format of attribute '%s'\n", tokens[i]);
 	    return -1;
 	}
 	*pt++ = 0;
 
 	apt = NULL;
 	for(j = 0; attrtab[j].name; j++) {
 	    if(!strcmp(attrtab[j].name, tokens[i])) {
 		apt = &attrtab[j];
 		break;
 	    }
 	}
 
 	if(!apt) {
 	    cli_dbgmsg("lsigattribs: Unknown attribute name '%s'\n", tokens[i]);
8d2654bb
 	    return 1;
677fc4ba
 	}
 
fa4d6353
 	if(!strcmp(apt->name, "Engine")) {
 	    if(i) {
 		cli_errmsg("lsigattribs: For backward compatibility the Engine attribute must be on the first position\n");
 		return -1;
 	    }
 	} else if(strcmp(apt->name, "Target"))
 	    have_newext = 1;
 
677fc4ba
 	switch(apt->type) {
 	    case CLI_TDB_UINT:
039c9565
 		if(!cli_isnumber(pt)) {
 		    cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]);
 		    return -1;
 		}
677fc4ba
 		off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
47d40feb
 		tdb->val = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->val) {
 		    tdb->cnt[CLI_TDB_UINT] = 0;
 		    return -1;
 		}
 		tdb->val[cnt] = atoi(pt);
 		break;
 
4168b010
 	    case CLI_TDB_FTYPE:
 		if((v1 = cli_ftcode(pt)) == CL_TYPE_ERROR) {
 		    cli_dbgmsg("lsigattribs: Unknown file type in %s\n", tokens[i]);
 		    return 1; /* skip */
 		}
 		off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
 		tdb->val = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
 		if(!tdb->val) {
 		    tdb->cnt[CLI_TDB_UINT] = 0;
 		    return -1;
 		}
 		tdb->val[cnt] = v1;
 		break;
 
677fc4ba
 	    case CLI_TDB_RANGE:
 		if(!(pt2 = strchr(pt, '-'))) {
 		    cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		*pt2++ = 0;
 		off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
 		tdb->cnt[CLI_TDB_RANGE] += 2;
47d40feb
 		tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->range) {
 		    tdb->cnt[CLI_TDB_RANGE] = 0;
 		    return -1;
 		}
039c9565
 		if(!cli_isnumber(pt) || !cli_isnumber(pt2)) {
 		    cli_errmsg("lsigattribs: Invalid argument for %s\n", tokens[i]);
 		    return -1;
 		}
677fc4ba
 		tdb->range[cnt] = atoi(pt);
 		tdb->range[cnt + 1] = atoi(pt2);
 		break;
 
 	    case CLI_TDB_RANGE2:
 		if(!strchr(pt, '-') || !strchr(pt, '.')) {
 		    cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
 		tdb->cnt[CLI_TDB_RANGE] += 3;
47d40feb
 		tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->range) {
 		    tdb->cnt[CLI_TDB_RANGE] = 0;
 		    return -1;
 		}
 		if(sscanf(pt, "%u.%u-%u", &v1, &v2, &v3) != 3) {
 		    cli_errmsg("lsigattribs: Can't parse parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		tdb->range[cnt] = (uint32_t) v1;
 		tdb->range[cnt + 1] = (uint32_t) v2;
 		tdb->range[cnt + 2] = (uint32_t) v3;
 		break;
 
 	    case CLI_TDB_STR:
 		off[i] = cnt = tdb->cnt[CLI_TDB_STR];
 		tdb->cnt[CLI_TDB_STR] += strlen(pt) + 1;
47d40feb
 		tdb->str = (char *) mpool_realloc2(tdb->mempool, tdb->str, tdb->cnt[CLI_TDB_STR] * sizeof(char));
677fc4ba
 		if(!tdb->str) {
 		    cli_errmsg("lsigattribs: Can't allocate memory for tdb->str\n");
 		    return -1;
 		}
 		memcpy(&tdb->str[cnt], pt, strlen(pt));
 		tdb->str[tdb->cnt[CLI_TDB_STR] - 1] = 0;
 		break;
 	}
     }
 
     if(!i) {
 	cli_errmsg("lsigattribs: Empty TDB\n");
 	return -1;
     }
 
72fb25ea
     for(i = 0; i < tokens_count; i++) {
677fc4ba
 	for(j = 0; attrtab[j].name; j++) {
 	    if(!strcmp(attrtab[j].name, tokens[i])) {
 		apt = &attrtab[j];
 		break;
 	    }
 	}
c9157be3
 	if(!apt)
 	    continue;
677fc4ba
 	switch(apt->type) {
 	    case CLI_TDB_UINT:
4168b010
 	    case CLI_TDB_FTYPE:
677fc4ba
 		*apt->pt = (uint32_t *) &tdb->val[off[i]];
 		break;
 
 	    case CLI_TDB_RANGE:
 	    case CLI_TDB_RANGE2:
 		*apt->pt = (uint32_t *) &tdb->range[off[i]];
 		break;
 
 	    case CLI_TDB_STR:
 		*apt->pt = (char *) &tdb->str[off[i]];
 		break;
 	}
     }
 
fa4d6353
     if(have_newext && (!tdb->engine || tdb->engine[0] < 51)) {
 	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");
 	return -1;
     }
677fc4ba
     return 0;
 }
 
0d9dbdef
 #define FREE_TDB(x) do {		\
   if(x.cnt[CLI_TDB_UINT])		\
47d40feb
     mpool_free(x.mempool, x.val);		\
0d9dbdef
   if(x.cnt[CLI_TDB_RANGE])		\
47d40feb
     mpool_free(x.mempool, x.range);	\
0d9dbdef
   if(x.cnt[CLI_TDB_STR])		\
47d40feb
     mpool_free(x.mempool, x.str);		\
ab893605
   if(x.macro_ptids)\
     mpool_free(x.mempool, x.macro_ptids);\
0d9dbdef
   } while(0);
677fc4ba
 
 #define LDB_TOKENS 67
57f14280
 static 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)
677fc4ba
 {
df8af6d3
     const char *sig, *virname, *offset, *logic;
     struct cli_ac_lsig **newtable, *lsig;
1d406931
     char *tokens[LDB_TOKENS+1], *pt;
df8af6d3
     int i, subsigs, tokens_count;
     unsigned short target = 0;
     struct cli_matcher *root;
     struct cli_lsig_tdb tdb;
     uint32_t lsigid[2];
     int ret;
677fc4ba
 
8d2654bb
     tokens_count = cli_strtokenize(buffer, ';', LDB_TOKENS + 1, (const char **) tokens);
df8af6d3
     if(tokens_count < 4) {
 	return CL_EMALFDB;
     }
     virname = tokens[0];
     logic = tokens[2];
677fc4ba
 
df8af6d3
     if (chkpua && cli_chkpua(virname, engine->pua_cats, options))
 	    return CL_SUCCESS;
04133ff9
 
72a03f9b
     if (chkign && cli_chkign(engine->ignored, virname, buffer_cpy))
df8af6d3
 	return CL_SUCCESS;
677fc4ba
 
eb422a03
     if(engine->cb_sigload && engine->cb_sigload("ldb", virname, engine->cb_sigload_ctx)) {
 	cli_dbgmsg("cli_loadldb: skipping %s due to callback\n", virname);
 	return CL_SUCCESS;
     }
 
df8af6d3
     subsigs = cli_ac_chklsig(logic, logic + strlen(logic), NULL, NULL, NULL, 1);
     if(subsigs == -1) {
 	return CL_EMALFDB;
     }
     subsigs++;
     if(subsigs > 64) {
 	cli_errmsg("cli_loadldb: Broken logical expression or too many subsignatures\n");
 	return CL_EMALFDB;
     }
d38d6dad
     if (!line) {
 	/* This is a logical signature from the bytecode, we need all
 	 * subsignatures, even if not referenced from the logical expression */
 	if (subsigs > tokens_count-3) {
 	    cli_errmsg("load_oneldb: Too many subsignatures: %u (max %u)\n",
 		       subsigs, tokens_count-3);
 	    return CL_EMALFDB;
677fc4ba
 	}
d38d6dad
 	subsigs = tokens_count-3;
74b00233
     } else if(subsigs != tokens_count - 3) {
 	cli_errmsg("cli_loadldb: The number of subsignatures (== %u) doesn't match the IDs in the logical expression (== %u)\n", tokens_count - 3, subsigs);
 	return CL_EMALFDB;
d38d6dad
     }
677fc4ba
 
df8af6d3
     /* TDB */
     memset(&tdb, 0, sizeof(tdb));
 #ifdef USE_MPOOL
     tdb.mempool = engine->mempool;
 #endif
8d2654bb
     if((ret = lsigattribs(tokens[1], &tdb))) {
df8af6d3
 	FREE_TDB(tdb);
8d2654bb
 	if(ret == 1) {
 	    cli_dbgmsg("cli_loadldb: Not supported attribute(s) in logical signature for %s, skipping\n", virname);
12c6a97e
 	    (*sigs)--;
8d2654bb
 	    return CL_SUCCESS;
 	}
df8af6d3
 	return CL_EMALFDB;
     }
8d2654bb
 
df8af6d3
     if(!tdb.target) {
 	cli_errmsg("cli_loadldb: No target specified in TDB\n");
 	FREE_TDB(tdb);
 	return CL_EMALFDB;
     } else if(tdb.target[0] >= CLI_MTARGETS) {
8d2654bb
 	cli_dbgmsg("cli_loadldb: Not supported target type in logical signature for %s, skipping\n", virname);
df8af6d3
 	FREE_TDB(tdb);
12c6a97e
 	(*sigs)--;
df8af6d3
 	return CL_SUCCESS;
     }
72fb25ea
 
0f7ba617
     if((tdb.icongrp1 || tdb.icongrp2) && tdb.target[0] != 1) {
 	cli_errmsg("cli_loadldb: IconGroup is only supported in PE (target 1) signatures\n");
 	FREE_TDB(tdb);
 	return CL_EMALFDB;
     }
 
17cfd76f
     if((tdb.ep || tdb.nos) && tdb.target[0] != 1 && tdb.target[0] != 6 && tdb.target[0] != 9) {
01b695fd
 	cli_errmsg("cli_loadldb: EntryPoint/NumberOfSections is only supported in PE/ELF/Mach-O signatures\n");
17cfd76f
 	FREE_TDB(tdb);
 	return CL_EMALFDB;
     }
 
df8af6d3
     root = engine->root[tdb.target[0]];
b023c36d
 
df8af6d3
     lsig = (struct cli_ac_lsig *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_ac_lsig));
     if(!lsig) {
 	cli_errmsg("cli_loadldb: Can't allocate memory for lsig\n");
 	FREE_TDB(tdb);
 	return CL_EMEM;
     }
677fc4ba
 
df8af6d3
     lsig->logic = cli_mpool_strdup(engine->mempool, logic);
     if(!lsig->logic) {
 	cli_errmsg("cli_loadldb: Can't allocate memory for lsig->logic\n");
 	FREE_TDB(tdb);
 	mpool_free(engine->mempool, lsig);
 	return CL_EMEM;
     }
677fc4ba
 
df8af6d3
     lsigid[0] = lsig->id = root->ac_lsigs;
677fc4ba
 
df8af6d3
     root->ac_lsigs++;
     newtable = (struct cli_ac_lsig **) mpool_realloc(engine->mempool, root->ac_lsigtable, root->ac_lsigs * sizeof(struct cli_ac_lsig *));
     if(!newtable) {
 	root->ac_lsigs--;
 	cli_errmsg("cli_loadldb: Can't realloc root->ac_lsigtable\n");
 	FREE_TDB(tdb);
 	mpool_free(engine->mempool, lsig);
 	return CL_EMEM;
     }
57f14280
     /* 0 marks no bc, we can't use a pointer to bc, since that is
      * realloced/moved during load */
     lsig->bc_idx = bc_idx;
df8af6d3
     newtable[root->ac_lsigs - 1] = lsig;
     root->ac_lsigtable = newtable;
ab893605
     tdb.subsigs = subsigs;
5ccfa0b7
 
df8af6d3
     for(i = 0; i < subsigs; i++) {
 	lsigid[1] = i;
 	sig = tokens[3 + i];
677fc4ba
 
df8af6d3
 	if((pt = strchr(tokens[3 + i], ':'))) {
 	    *pt = 0;
 	    sig = ++pt;
 	    offset = tokens[3 + i];
 	} else {
 	    offset = "*";
 	    sig = tokens[3 + i];
 	}
677fc4ba
 
8d2654bb
 	if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, lsigid, options)))
 	    return ret;
ab893605
 	if(sig[0] == '$' && i) {
 	    /* allow mapping from lsig back to pattern for macros */
 	    if (!tdb.macro_ptids)
 		tdb.macro_ptids = mpool_calloc(root->mempool, subsigs, sizeof(*tdb.macro_ptids));
 	    if (!tdb.macro_ptids)
 		return CL_EMEM;
 	    tdb.macro_ptids[i-1] = root->ac_patterns-1;
 	}
677fc4ba
 
 	if(tdb.engine) {
 	    if(tdb.engine[0] > cl_retflevel()) {
 		cli_dbgmsg("cli_loadldb: Signature for %s not loaded (required f-level: %u)\n", virname, tdb.engine[0]);
 		FREE_TDB(tdb);
12c6a97e
 		(*sigs)--;
df8af6d3
 		return CL_SUCCESS;
677fc4ba
 	    } else if(tdb.engine[1] < cl_retflevel()) {
 		FREE_TDB(tdb);
12c6a97e
 		(*sigs)--;
df8af6d3
 		return CL_SUCCESS;
677fc4ba
 	    }
 	}
df8af6d3
     }
ab893605
     memcpy(&lsig->tdb, &tdb, sizeof(tdb));
df8af6d3
     return CL_SUCCESS;
 }
677fc4ba
 
df8af6d3
 static int cli_loadldb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
 {
12c6a97e
 	char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1], *buffer_cpy = NULL;
df8af6d3
 	unsigned int line = 0, sigs = 0;
 	int ret;
677fc4ba
 
 
df8af6d3
     if((ret = cli_initroots(engine, options)))
 	return ret;
677fc4ba
 
72a03f9b
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(sizeof(buffer))))
 	    return CL_EMEM;
df8af6d3
     while(cli_dbgets(buffer, sizeof(buffer), fs, dbio)) {
 	line++;
 	sigs++;
 	cli_chomp(buffer);
677fc4ba
 
72a03f9b
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
df8af6d3
 	ret = load_oneldb(buffer,
 			  engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)),
 			  !!engine->ignored,
57f14280
 			  engine, options, dbname, line, &sigs, 0, buffer_cpy);
df8af6d3
 	if (ret)
677fc4ba
 	    break;
     }
04133ff9
     if(engine->ignored)
 	free(buffer_cpy);
677fc4ba
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     if(signo)
 	*signo += sigs;
 
     return CL_SUCCESS;
 }
 
52dd3a6b
 static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
 {
1453c0a3
     char buf[4096];
52dd3a6b
     int rc;
     struct cli_all_bc *bcs = &engine->bcs;
     struct cli_bc *bc;
459b13ed
     unsigned sigs = 0;
be43f951
     unsigned security_trust = 0;
d5ffa2ac
     unsigned i;
be43f951
 
459b13ed
 
ab636570
     /* TODO: virusname have a common prefix, and whitelist by that */
459b13ed
     if((rc = cli_initroots(engine, options)))
 	return rc;
 
52dd3a6b
     if(!(engine->dconf->bytecode & BYTECODE_ENGINE_MASK)) {
 	return CL_SUCCESS;
     }
92a08a03
 #ifndef CL_BCUNSIGNED
9c92344b
     if (!(options & CL_DB_SIGNED)) {
 	cli_warnmsg("Only loading signed bytecode, skipping load of unsigned bytecode!\n");
 	return CL_SUCCESS;
     }
 #endif
52dd3a6b
     bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs)*(bcs->count+1));
     if (!bcs->all_bcs) {
 	cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n");
 	return CL_EMEM;
     }
     bcs->count++;
     bc = &bcs->all_bcs[bcs->count-1];
be43f951
 
     switch (engine->bytecode_security) {
 	case CL_BYTECODE_TRUST_ALL:
 	    security_trust = 1;
 	    cli_dbgmsg("bytecode: trusting all bytecode!\n");
 	    break;
 	case CL_BYTECODE_TRUST_SIGNED:
de351ee1
 	    security_trust = !!(options & CL_DB_SIGNED);
be43f951
 	    break;
 	default:
 	    security_trust = 0;
     }
 
     rc = cli_bytecode_load(bc, fs, dbio, security_trust);
1453c0a3
     /* read remainder of DB, needed because cvd.c checks that we read the entire
      * file */
     while (cli_dbgets(buf, sizeof(buf), fs, dbio)) {}
 
52dd3a6b
     if (rc != CL_SUCCESS) {
cbb9db19
 	cli_bytecode_destroy(bc);
ab636570
 	cli_errmsg("Unable to load %s bytecode: %s\n", dbname, cl_strerror(rc));
52dd3a6b
 	return rc;
     }
039af772
     if (bc->state == bc_skip) {
 	cli_bytecode_destroy(bc);
 	bcs->count--;
 	return CL_SUCCESS;
     }
57f14280
     bc->id = bcs->count;/* must set after _load, since load zeroes */
e293229d
     sigs++;
f4e34215
     if (bc->kind == BC_LOGICAL || bc->lsig) {
3735fda1
         unsigned oldsigs = sigs;
ab636570
 	if (!bc->lsig) {
 	    cli_errmsg("Bytecode %s has logical kind, but missing logical signature!\n", dbname);
 	    return CL_EMALFDB;
 	}
a35cfe51
 	cli_dbgmsg("Bytecode %s(%u) has logical signature: %s\n", dbname, bc->id, bc->lsig);
57f14280
 	rc = load_oneldb(bc->lsig, 0, 0, engine, options, dbname, 0, &sigs, bcs->count, NULL);
459b13ed
 	if (rc != CL_SUCCESS) {
ab636570
 	    cli_errmsg("Problem parsing logical signature %s for bytecode %s: %s\n",
 		       bc->lsig, dbname, cl_strerror(rc));
459b13ed
 	    return rc;
 	}
3735fda1
         if (sigs != oldsigs) {
           /* compiler ensures Engine field in lsig matches the one in bytecode,
            * so this should never happen. */
           cli_errmsg("Bytecode logical signature skipped, but bytecode itself not?");
           return CL_EMALFDB;
         }
f4e34215
     }
     if (bc->kind != BC_LOGICAL) {
ab636570
 	if (bc->lsig) {
f4e34215
 	    /* runlsig will only flip a status bit, not report a match,
 	     * when the hooks are executed we only execute the hook if its
 	     * status bit is on */
 	    bc->hook_lsig_id = ++engine->hook_lsig_ids;
ab636570
 	}
 	if (bc->kind >= _BC_START_HOOKS && bc->kind < _BC_LAST_HOOK) {
 	    unsigned hook = bc->kind - _BC_START_HOOKS;
 	    unsigned cnt = ++engine->hooks_cnt[hook];
 	    engine->hooks[hook] = cli_realloc2(engine->hooks[hook],
 					       sizeof(*engine->hooks[0])*cnt);
 	    if (!engine->hooks[hook]) {
 		cli_errmsg("Out of memory allocating memory for hook %u", hook);
 		return CL_EMEM;
 	    }
 	    engine->hooks[hook][cnt-1] = bcs->count-1;
 	} else switch (bc->kind) {
d5ffa2ac
 	    case BC_STARTUP:
 		for (i=0;i<bcs->count-1;i++)
 		    if (bcs->all_bcs[i].kind == BC_STARTUP) {
 			struct cli_bc *bc0 = &bcs->all_bcs[i];
 			cli_errmsg("Can only load 1 BC_STARTUP bytecode, attempted to load 2nd!\n");
 			cli_warnmsg("Previous BC_STARTUP: %d %d by %s\n",
88d54dcb
 				    bc0->id, (uint32_t)bc0->metadata.timestamp,
d5ffa2ac
 				    bc0->metadata.sigmaker ? bc0->metadata.sigmaker : "N/A");
 			cli_warnmsg("Conflicting BC_STARTUP: %d %d by %s\n",
88d54dcb
 				    bc->id, (uint32_t)bc->metadata.timestamp,
d5ffa2ac
 				    bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A");
 			return CL_EMALFDB;
 		    }
 		break;
ab636570
 	    default:
 		cli_errmsg("Bytecode: unhandled bytecode kind %u\n", bc->kind);
 		return CL_EMALFDB;
 	}
df8af6d3
     }
459b13ed
     if (signo)
 	*signo += sigs;
52dd3a6b
     return CL_SUCCESS;
 }
 
72fb25ea
 #define FTM_TOKENS 8
15850fc6
 static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio)
7021b545
 {
72fb25ea
 	const char *tokens[FTM_TOKENS + 1], *pt;
7021b545
 	char buffer[FILEBUFF];
72fb25ea
 	unsigned int line = 0, sigs = 0, tokens_count;
7021b545
 	struct cli_ftype *new;
6038397e
 	cli_file_t rtype, type;
7021b545
 	int ret;
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
7021b545
 	return ret;
 
     while(1) {
 	if(internal) {
8dd6c63c
 	    options |= CL_DB_OFFICIAL;
7021b545
 	    if(!ftypes_int[line])
 		break;
 	    strncpy(buffer, ftypes_int[line], sizeof(buffer));
72ce4b70
 	    buffer[sizeof(buffer)-1]='\0';
7021b545
 	} else {
e8ae4fae
 	    if(!cli_dbgets(buffer, FILEBUFF, fs, dbio))
7021b545
 		break;
 	    cli_chomp(buffer);
 	}
 	line++;
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', FTM_TOKENS + 1, tokens);
7021b545
 
72fb25ea
 	if(tokens_count < 6 || tokens_count > 8) {
7021b545
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	if(tokens_count > 6) { /* min version */
 	    pt = tokens[6];
6038397e
 	    if(!cli_isnumber(pt)) {
 		ret = CL_EMALFDB;
 		break;
 	    }
 	    if((unsigned int) atoi(pt) > cl_retflevel()) {
 		cli_dbgmsg("cli_loadftm: File type signature for %s not loaded (required f-level: %u)\n", tokens[3], atoi(pt));
 		continue;
 	    }
72fb25ea
 	    if(tokens_count == 8) { /* max version */
 		pt = tokens[7];
6038397e
 		if(!cli_isnumber(pt)) {
 		    ret = CL_EMALFDB;
 		    break;
 		}
 		if((unsigned int) atoi(pt) < cl_retflevel())
 		    continue;
 	    }
 	}
 
 	rtype = cli_ftcode(tokens[4]);
 	type = cli_ftcode(tokens[5]);
 	if(rtype == CL_TYPE_ERROR || type == CL_TYPE_ERROR) {
7021b545
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
039c9565
 	if(!cli_isnumber(tokens[0])) {
 	    cli_errmsg("cli_loadftm: Invalid value for the first field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
6038397e
 	if(atoi(tokens[0]) == 1) { /* A-C */
33872a43
 	    if((ret = cli_parse_add(engine->root[0], tokens[3], tokens[2], rtype, type, tokens[1], 0, NULL, options)))
7021b545
 		break;
 
6038397e
 	} else if(atoi(tokens[0]) == 0) { /* memcmp() */
039c9565
 	    if(!cli_isnumber(tokens[1])) {
 		cli_errmsg("cli_loadftm: Invalid offset\n");
 		ret = CL_EMALFDB;
 		break;
 	    }
47d40feb
 	    new = (struct cli_ftype *) mpool_malloc(engine->mempool, sizeof(struct cli_ftype));
7021b545
 	    if(!new) {
 		ret = CL_EMEM;
 		break;
 	    }
 	    new->type = type;
6038397e
 	    new->offset = atoi(tokens[1]);
47d40feb
 	    new->magic = (unsigned char *) cli_mpool_hex2str(engine->mempool, tokens[2]);
7021b545
 	    if(!new->magic) {
6038397e
 		cli_errmsg("cli_loadftm: Can't decode the hex string\n");
7021b545
 		ret = CL_EMALFDB;
47d40feb
 		mpool_free(engine->mempool, new);
7021b545
 		break;
 	    }
6038397e
 	    new->length = strlen(tokens[2]) / 2;
47d40feb
 	    new->tname = cli_mpool_strdup(engine->mempool, tokens[3]);
7021b545
 	    if(!new->tname) {
47d40feb
 		mpool_free(engine->mempool, new->magic);
 		mpool_free(engine->mempool, new);
7021b545
 		ret = CL_EMEM;
 		break;
 	    }
15850fc6
 	    new->next = engine->ftypes;
 	    engine->ftypes = new;
6038397e
 
 	} else {
 	    cli_dbgmsg("cli_loadftm: Unsupported mode %u\n", atoi(tokens[0]));
 	    continue;
7021b545
 	}
6038397e
 	sigs++;
7021b545
     }
 
6038397e
     if(ret) {
 	cli_errmsg("Problem parsing %s filetype database at line %u\n", internal ? "built-in" : "external", line);
 	return ret;
7021b545
     }
 
6038397e
     if(!sigs) {
 	cli_errmsg("Empty %s filetype database\n", internal ? "built-in" : "external");
 	return CL_EMALFDB;
7021b545
     }
 
6038397e
     cli_dbgmsg("Loaded %u filetype definitions\n", sigs);
7021b545
     return CL_SUCCESS;
 }
 
de351ee1
 #define INFO_NSTR "11088894983048545473659556106627194923928941791795047620591658697413581043322715912172496806525381055880964520618400224333320534660299233983755341740679502866829909679955734391392668378361221524205396631090105151641270857277080310734320951653700508941717419168723942507890702904702707587451621691050754307850383399865346487203798464178537392211402786481359824461197231102895415093770394216666324484593935762408468516826633192140826667923494822045805347809932848454845886971706424360558667862775876072059437703365380209101697738577515476935085469455279994113145977994084618328482151013142393373316337519977244732747977"
 #define INFO_ESTR "100002049"
 #define INFO_TOKENS 3
ace26bfe
 static int cli_loadinfo(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
 {
de351ee1
 	const char *tokens[INFO_TOKENS + 1];
ace26bfe
 	char buffer[FILEBUFF];
de351ee1
 	unsigned int line = 0, tokens_count, len;
 	unsigned char hash[32];
ace26bfe
         struct cli_dbinfo *last = NULL, *new;
de351ee1
 	int ret = CL_SUCCESS, dsig = 0;
 	SHA256_CTX ctx;
ace26bfe
 
7eb6bac7
 
     if(!dbio) {
 	cli_errmsg("cli_loadinfo: .info files can only be loaded from within database container files\n");
 	return CL_EMALFDB;
     }
de351ee1
     sha256_init(&ctx);
ace26bfe
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
 	line++;
de351ee1
 	if(!strncmp(buffer, "DSIG:", 5)) {
 	    dsig = 1;
 	    sha256_final(&ctx, hash);
 	    if(cli_versig2(hash, buffer + 5, INFO_NSTR, INFO_ESTR) != CL_SUCCESS) {
 		cli_errmsg("cli_loadinfo: Incorrect digital signature\n");
 		ret = CL_EMALFDB;
 	    }
 	    break;
 	}
 	len = strlen(buffer);
 	if(dbio->usebuf && buffer[len - 1] != '\n' && len + 1 < FILEBUFF) {
 	    /* cli_dbgets in buffered mode strips \n */
 	    buffer[len] = '\n';
 	    buffer[len + 1] = 0;
 	}
 	sha256_update(&ctx, buffer, strlen(buffer));
ace26bfe
 	cli_chomp(buffer);
 	if(!strncmp("ClamAV-VDB:", buffer, 11)) {
 	    if(engine->dbinfo) { /* shouldn't be initialized at this point */
 		cli_errmsg("cli_loadinfo: engine->dbinfo already initialized\n");
 		ret = CL_EMALFDB;
 		break;
 	    }
 	    last = engine->dbinfo = (struct cli_dbinfo *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_bm_patt));
 	    if(!engine->dbinfo) {
 		ret = CL_EMEM;
 		break;
 	    }
 	    engine->dbinfo->cvd = cl_cvdparse(buffer);
 	    if(!engine->dbinfo->cvd) {
 		cli_errmsg("cli_loadinfo: Can't parse header entry\n");
 		ret = CL_EMALFDB;
 		break;
 	    }
 	    continue;
 	}
 
 	if(!last) {
 	    cli_errmsg("cli_loadinfo: Incorrect file format\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
de351ee1
 	tokens_count = cli_strtokenize(buffer, ':', INFO_TOKENS + 1, tokens);
 	if(tokens_count != INFO_TOKENS) {
ace26bfe
 	    ret = CL_EMALFDB;
 	    break;
 	}
         new = (struct cli_dbinfo *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_dbinfo));
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
e4a0f2c9
 	new->name = cli_mpool_strdup(engine->mempool, tokens[0]);
ace26bfe
 	if(!new->name) {
 	    mpool_free(engine->mempool, new);
 	    ret = CL_EMEM;
 	    break;
 	}
 
de351ee1
 	if(!cli_isnumber(tokens[1])) {
 	    cli_errmsg("cli_loadinfo: Invalid value in the size field\n");
ace26bfe
 	    mpool_free(engine->mempool, new->name);
 	    mpool_free(engine->mempool, new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
de351ee1
 	new->size = atoi(tokens[1]);
ace26bfe
 
de351ee1
 	if(strlen(tokens[2]) != 64 || !(new->hash = cli_mpool_hex2str(engine->mempool, tokens[2]))) {
 	    cli_errmsg("cli_loadinfo: Malformed SHA256 string at line %u\n", line);
 	    mpool_free(engine->mempool, new->name);
 	    mpool_free(engine->mempool, new);
 	    ret = CL_EMALFDB;
 	    break;
 	}
ace26bfe
 	last->next = new;
 	last = new;
     }
 
de351ee1
     if(!dsig) {
 	cli_errmsg("cli_loadinfo: Digital signature not found\n");
 	return CL_EMALFDB;
     }
 
ace26bfe
     if(ret) {
 	cli_errmsg("cli_loadinfo: Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     return CL_SUCCESS;
 }
 
04133ff9
 #define IGN_MAX_TOKENS   3
15850fc6
 static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
ed9753e9
 {
04133ff9
 	const char *tokens[IGN_MAX_TOKENS + 1], *signame, *hash = NULL;
b9b47784
 	char buffer[FILEBUFF];
12c6a97e
 	unsigned int line = 0, tokens_count, len;
04133ff9
         struct cli_bm_patt *new;
ac1b219c
 	int ret = CL_SUCCESS;
ed9753e9
 
15850fc6
     if(!engine->ignored) {
04133ff9
 	engine->ignored = (struct cli_matcher *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_matcher));
 	if(!engine->ignored)
ed9753e9
 	    return CL_EMEM;
04133ff9
 #ifdef USE_MPOOL
 	engine->ignored->mempool = engine->mempool;
 #endif
 	if((ret = cli_bm_init(engine->ignored))) {
 	    cli_errmsg("cli_loadign: Can't initialise AC pattern matcher\n");
 	    return ret;
 	}
ed9753e9
     }
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
ed9753e9
 	line++;
 	cli_chomp(buffer);
04133ff9
 
 	tokens_count = cli_strtokenize(buffer, ':', IGN_MAX_TOKENS + 1, tokens);
 	if(tokens_count > IGN_MAX_TOKENS) {
72fb25ea
 	    ret = CL_EMALFDB;
 	    break;
 	}
d6e1ef16
 
04133ff9
 	if(tokens_count == 1) {
 	    signame = buffer;
 	} else if(tokens_count == 2) {
 	    signame = tokens[0];
 	    hash = tokens[1];
 	} else { /* old mode */
 	    signame = tokens[2];
 	}
 	if(!(len = strlen(signame))) {
 	    cli_errmsg("cli_loadign: No signature name provided\n");
039c9565
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
04133ff9
         new = (struct cli_bm_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_bm_patt));
ed9753e9
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
12c6a97e
 	new->pattern = (unsigned char *) cli_mpool_strdup(engine->mempool, signame);
04133ff9
 	if(!new->pattern) {
47d40feb
 	    mpool_free(engine->mempool, new);
04133ff9
 	    ret = CL_EMEM;
ed9753e9
 	    break;
 	}
04133ff9
 	if(hash) {
12c6a97e
 	    if(strlen(hash) != 32 || !(new->virname = (char *) cli_mpool_hex2str(engine->mempool, hash))) {
04133ff9
 		cli_errmsg("cli_loadign: Malformed MD5 string at line %u\n", line);
 		mpool_free(engine->mempool, new->pattern);
 		mpool_free(engine->mempool, new);
 		ret = CL_EMALFDB;
 		break;
 	    }
 	}
 	new->length = len;
 	new->boundary |= BM_BOUNDARY_EOL;
ed9753e9
 
04133ff9
         if((ret = cli_bm_addpatt(engine->ignored, new, "0"))) {
 	    if(hash)
 		mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new->pattern);
47d40feb
 	    mpool_free(engine->mempool, new);
ed9753e9
 	    break;
 	}
     }
 
     if(ret) {
 	cli_errmsg("cli_loadign: Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     return CL_SUCCESS;
 }
 
3dad68eb
 #define MD5_HDB	    0
 #define MD5_MDB	    1
 #define MD5_FP	    2
2b459819
 
15850fc6
 static int cli_md5db_init(struct cl_engine *engine, unsigned int mode)
2b459819
 {
 	struct cli_matcher *bm = NULL;
 	int ret;
 
 
     if(mode == MD5_HDB) {
47d40feb
 	bm = engine->md5_hdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     } else if(mode == MD5_MDB) {
47d40feb
 	bm = engine->md5_mdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     } else {
47d40feb
 	bm = engine->md5_fp = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     }
 
     if(!bm)
 	return CL_EMEM;
0728972e
 #ifdef USE_MPOOL
15850fc6
     bm->mempool = engine->mempool;
0728972e
 #endif
44712fcb
     if((ret = cli_md5m_init(bm))) {
 	cli_errmsg("cli_md5db_init: Failed to initialize MD5 matcher\n");
2b459819
 	return ret;
     }
 
     return CL_SUCCESS;
 }
 
 #define MD5_DB			    \
     if(mode == MD5_HDB)		    \
15850fc6
 	db = engine->md5_hdb;    \
2b459819
     else if(mode == MD5_MDB)	    \
15850fc6
 	db = engine->md5_mdb;    \
2b459819
     else			    \
15850fc6
 	db = engine->md5_fp;
2b459819
 
a72b7d2e
 #define MD5_TOKENS 3
15850fc6
 static 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)
4048c4f6
 {
72fb25ea
 	const char *tokens[MD5_TOKENS + 1];
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
a72b7d2e
 	const char *pt;
44712fcb
 	unsigned char *md5;
0a3d4094
 	int ret = CL_SUCCESS;
72fb25ea
 	unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
44712fcb
 	struct cli_md5m_patt *new;
2b459819
 	struct cli_matcher *db = NULL;
e3aaff8e
 
4048c4f6
 
3dad68eb
     if(mode == MD5_MDB) {
 	size_field = 0;
 	md5_field = 1;
8ce42de3
     }
 
04133ff9
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
 	    return CL_EMEM;
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
e3aaff8e
 	line++;
 	cli_chomp(buffer);
04133ff9
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
 
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens);
 	if(tokens_count != MD5_TOKENS) {
b023c36d
 	    ret = CL_EMALFDB;
 	    break;
 	}
039c9565
 	if(!cli_isnumber(tokens[size_field])) {
 	    cli_errmsg("cli_loadmd5: Invalid value for the size field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
b023c36d
 
72fb25ea
 	pt = tokens[2]; /* virname */
15850fc6
 	if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
 	    if(cli_chkpua(pt, engine->pua_cats, options))
b023c36d
 		continue;
 
04133ff9
 	if(engine->ignored && cli_chkign(engine->ignored, pt, buffer_cpy))
b023c36d
 	    continue;
 
7f3ac446
 	if(engine->cb_sigload && engine->cb_sigload("md5", pt, engine->cb_sigload_ctx)) {
 	    cli_dbgmsg("cli_loadmd5: skipping %s due to callback\n", pt);
 	    continue;
eb422a03
 	}
 
44712fcb
 	new = (struct cli_md5m_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_md5m_patt));
4048c4f6
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
e3aaff8e
 	}
 
72fb25ea
 	pt = tokens[md5_field]; /* md5 */
44712fcb
 	if(strlen(pt) != 32 || !(md5 = (unsigned char *) cli_mpool_hex2str(engine->mempool, pt))) {
0728972e
 	    cli_errmsg("cli_loadmd5: Malformed MD5 string at line %u\n", line);
47d40feb
 	    mpool_free(engine->mempool, new);
0728972e
 	    ret = CL_EMALFDB;
 	    break;
 	}
44712fcb
 	memcpy(new->md5, md5, 16);
 	mpool_free(engine->mempool, md5);
e3aaff8e
 
6c26e99c
 	new->filesize = atoi(tokens[size_field]);
022a21cf
 
12c6a97e
 	new->virname = cli_mpool_virname(engine->mempool, tokens[2], options & CL_DB_OFFICIAL);
0728972e
 	if(!new->virname) {
47d40feb
 	    mpool_free(engine->mempool, new);
4048c4f6
 	    ret = CL_EMALFDB;
 	    break;
e3aaff8e
 	}
4048c4f6
 
2b459819
 	MD5_DB;
 	if(!db && (ret = cli_md5db_init(engine, mode))) {
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
2b459819
 	    break;
 	} else {
 	    MD5_DB;
 	}
0a3d4094
 
44712fcb
 	if((ret = cli_md5m_addpatt(db, new))) {
2b459819
 	    cli_errmsg("cli_loadmd5: Error adding BM pattern\n");
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
2b459819
 	    break;
 	}
0a3d4094
 
2b459819
 	if(mode == MD5_MDB) { /* section MD5 */
c3671221
 	    if(!db->md5_sizes_hs.capacity) {
cc447ac8
 		    cli_hashset_init(&db->md5_sizes_hs, 65536, 80);
8000d078
 	    }
6c26e99c
 	    cli_hashset_addkey(&db->md5_sizes_hs, new->filesize);
c09d6c19
 	}
ed9753e9
 
 	sigs++;
e3aaff8e
     }
04133ff9
     if(engine->ignored)
 	free(buffer_cpy);
e3aaff8e
 
4048c4f6
     if(!line) {
3dad68eb
 	cli_errmsg("cli_loadmd5: Empty database file\n");
4048c4f6
 	return CL_EMALFDB;
     }
e3aaff8e
 
4048c4f6
     if(ret) {
3dad68eb
 	cli_errmsg("cli_loadmd5: Problem parsing database at line %u\n", line);
4048c4f6
 	return ret;
     }
ee039e40
 
3805ebcb
     if(signo)
ed9753e9
 	*signo += sigs;
3365db23
 
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
d6e1ef16
 #define MD_TOKENS 9
15850fc6
 static int cli_loadmd(FILE *fs, struct cl_engine *engine, unsigned int *signo, int type, unsigned int options, struct cli_dbio *dbio, const char *dbname)
e5916a51
 {
72fb25ea
 	const char *tokens[MD_TOKENS + 1];
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
72fb25ea
 	unsigned int line = 0, sigs = 0, tokens_count;
12c6a97e
 	int ret = CL_SUCCESS;
15f413d1
 	struct cli_cdb *new;
e5916a51
 
 
04133ff9
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
 	    return CL_EMEM;
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
e5916a51
 	line++;
ed9753e9
 	if(buffer[0] == '#')
e5916a51
 	    continue;
 
 	cli_chomp(buffer);
04133ff9
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
 
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', MD_TOKENS + 1, tokens);
 	if(tokens_count != MD_TOKENS) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
e5916a51
 
039c9565
 	if(strcmp(tokens[1], "*") && !cli_isnumber(tokens[1])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'encrypted' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	if(strcmp(tokens[3], "*") && !cli_isnumber(tokens[3])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'original size' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	if(strcmp(tokens[4], "*") && !cli_isnumber(tokens[4])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'compressed size' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	if(strcmp(tokens[6], "*") && !cli_isnumber(tokens[6])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'compression method' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	if(strcmp(tokens[7], "*") && !cli_isnumber(tokens[7])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'file number' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	if(strcmp(tokens[8], "*") && !cli_isnumber(tokens[8])) {
 	    cli_errmsg("cli_loadmd: Invalid value for the 'max depth' field\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
15f413d1
 	new = (struct cli_cdb *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_cdb));
e5916a51
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
12c6a97e
 	new->virname = cli_mpool_virname(engine->mempool, tokens[0], options & CL_DB_OFFICIAL);
0728972e
 	if(!new->virname) {
47d40feb
 	    mpool_free(engine->mempool, new);
b5513f8d
 	    ret = CL_EMEM;
e5916a51
 	    break;
 	}
15f413d1
 	new->ctype = (type == 1) ? CL_TYPE_ZIP : CL_TYPE_RAR;
e5916a51
 
04133ff9
 	if(engine->ignored && cli_chkign(engine->ignored, new->virname, buffer/*_cpy*/)) {
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
ed9753e9
 	    continue;
 	}
 
eb422a03
 	if(engine->cb_sigload && engine->cb_sigload("md", new->virname, engine->cb_sigload_ctx)) {
 	    cli_dbgmsg("cli_loadmd: skipping %s due to callback\n", new->virname);
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
 	    continue;
 	}
 
545247af
 	new->encrypted = strcmp(tokens[1], "*") ? atoi(tokens[1]) : 2;
15f413d1
 
 	if(strcmp(tokens[2], "*") && cli_regcomp(&new->name, tokens[2], REG_EXTENDED | REG_NOSUB)) {
 	    cli_errmsg("cli_loadmd: Can't compile regular expression %s in signature for %s\n", tokens[2], tokens[0]);
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
15f413d1
 	    ret = CL_EMEM;
e5916a51
 	    break;
 	}
15f413d1
 	new->csize[0] = new->csize[1] = CLI_OFF_ANY;
e5916a51
 
d6e1ef16
 	if(!strcmp(tokens[3], "*"))
15f413d1
 	    new->fsizer[0] = new->fsizer[1] = CLI_OFF_ANY;
d6e1ef16
 	else
15f413d1
 	    new->fsizer[0] = new->fsizer[1] = atoi(tokens[3]);
e5916a51
 
d6e1ef16
 	if(!strcmp(tokens[4], "*"))
15f413d1
 	    new->fsizec[0] = new->fsizec[1] = CLI_OFF_ANY;
d6e1ef16
 	else
15f413d1
 	    new->fsizec[0] = new->fsizec[1] = atoi(tokens[4]);
e5916a51
 
15f413d1
 	if(strcmp(tokens[5], "*")) {
 	    new->res1 = cli_hex2num(tokens[5]);
 	    if(new->res1 == -1) {
 		mpool_free(engine->mempool, new->virname);
 		mpool_free(engine->mempool, new);
 		if(new->name.re_magic)
 		    cli_regfree(&new->name);
d6e1ef16
 	        ret = CL_EMALFDB;
 		break;
e5916a51
 	    }
 	}
 
15f413d1
 	/* tokens[6] - not used */
e5916a51
 
bb1e844c
 	new->filepos[0] = new->filepos[1] = strcmp(tokens[7], "*") ? atoi(tokens[7]) : (int) CLI_OFF_ANY;
19e2ac07
 
15f413d1
 	/* tokens[8] - not used */
ed9753e9
 
15f413d1
 	new->next = engine->cdb;
 	engine->cdb = new;
ed9753e9
 	sigs++;
e5916a51
     }
04133ff9
     if(engine->ignored)
 	free(buffer_cpy);
e5916a51
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	return ret;
     }
 
     if(signo)
ed9753e9
 	*signo += sigs;
e5916a51
 
8d3aca30
     return CL_SUCCESS;
e5916a51
 }
 
570b1d00
 /*    0		 1		2		3	         4	       5	      6	      7	      8   9    10     11
  * VirusName:ContainerType:ContainerSize:FileNameREGEX:FileSizeInContainer:FileSizeReal:IsEncrypted:FilePos:Res1:Res2[:MinFL[:MaxFL]]
55094a9c
  */
570b1d00
 
 #define CDB_TOKENS 12
55094a9c
 static int cli_loadcdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
 {
20e41993
 	const char *tokens[CDB_TOKENS + 1];
12c6a97e
 	char buffer[FILEBUFF], *buffer_cpy = NULL;
55094a9c
 	unsigned int line = 0, sigs = 0, tokens_count, n0, n1;
 	int ret = CL_SUCCESS;
 	struct cli_cdb *new;
 
 
     if(engine->ignored)
 	if(!(buffer_cpy = cli_malloc(FILEBUFF)))
 	    return CL_EMEM;
 
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
 	line++;
 	if(buffer[0] == '#')
 	    continue;
 
 	cli_chomp(buffer);
 	if(engine->ignored)
 	    strcpy(buffer_cpy, buffer);
 
 	tokens_count = cli_strtokenize(buffer, ':', CDB_TOKENS + 1, tokens);
15f413d1
 	if(tokens_count > CDB_TOKENS || tokens_count < CDB_TOKENS - 2) {
55094a9c
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
570b1d00
 	if(tokens_count > 10) { /* min version */
 	    if(!cli_isnumber(tokens[10])) {
55094a9c
 		ret = CL_EMALFDB;
 		break;
 	    }
570b1d00
 	    if((unsigned int) atoi(tokens[10]) > cl_retflevel()) {
55094a9c
 		cli_dbgmsg("cli_loadcdb: Container signature for %s not loaded (required f-level: %u)\n", tokens[0], atoi(tokens[10]));
 		continue;
 	    }
570b1d00
 	    if(tokens_count == CDB_TOKENS) { /* max version */
 		if(!cli_isnumber(tokens[11])) {
55094a9c
 		    ret = CL_EMALFDB;
 		    break;
 		}
570b1d00
 		if((unsigned int) atoi(tokens[11]) < cl_retflevel())
55094a9c
 		    continue;
 	    }
 	}
 
 	new = (struct cli_cdb *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_cdb));
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
12c6a97e
 	new->virname = cli_mpool_virname(engine->mempool, tokens[0], options & CL_DB_OFFICIAL);
55094a9c
 	if(!new->virname) {
 	    mpool_free(engine->mempool, new);
 	    ret = CL_EMEM;
 	    break;
 	}
 
 	if(engine->ignored && cli_chkign(engine->ignored, new->virname, buffer/*_cpy*/)) {
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
 	    continue;
 	}
 
eb422a03
 	if(engine->cb_sigload && engine->cb_sigload("cdb", new->virname, engine->cb_sigload_ctx)) {
 	    cli_dbgmsg("cli_loadcdb: skipping %s due to callback\n", new->virname);
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
 	    continue;
 	}
 
55094a9c
 	if(!strcmp(tokens[1], "*")) {
 	    new->ctype = CL_TYPE_ANY;
 	} else if((new->ctype = cli_ftcode(tokens[1])) == CL_TYPE_ERROR) {
15f413d1
 	    cli_dbgmsg("cli_loadcdb: Unknown container type %s in signature for %s, skipping\n", tokens[1], tokens[0]);
55094a9c
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
 	    continue;
 	}
 
 	if(strcmp(tokens[3], "*") && cli_regcomp(&new->name, tokens[3], REG_EXTENDED | REG_NOSUB)) {
15f413d1
 	    cli_errmsg("cli_loadcdb: Can't compile regular expression %s in signature for %s\n", tokens[3], tokens[0]);
55094a9c
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
 	    ret = CL_EMEM;
 	    break;
 	}
 
 #define CDBRANGE(token_str, dest)					    \
 	if(strcmp(token_str, "*")) {					    \
 	    if(strchr(token_str, '-')) {				    \
 		if(sscanf(token_str, "%u-%u", &n0, &n1) != 2) {		    \
 		    ret = CL_EMALFDB;					    \
 		} else {						    \
 		    dest[0] = n0;					    \
 		    dest[1] = n1;					    \
 		}							    \
 	    } else {							    \
 		if(!cli_isnumber(token_str))				    \
 		    ret = CL_EMALFDB;					    \
 		else							    \
 		    dest[0] = dest[1] = atoi(token_str);		    \
 	    }								    \
 	    if(ret != CL_SUCCESS) {					    \
15f413d1
 		cli_errmsg("cli_loadcdb: Invalid value %s in signature for %s\n",\
55094a9c
 		    token_str, tokens[0]);				    \
 		if(new->name.re_magic)					    \
 		    cli_regfree(&new->name);				    \
 		mpool_free(engine->mempool, new->virname);		    \
 		mpool_free(engine->mempool, new);			    \
 		ret = CL_EMEM;						    \
 		break;							    \
 	    }								    \
 	} else {							    \
 	    dest[0] = dest[1] = CLI_OFF_ANY;				    \
 	}
 
570b1d00
 	CDBRANGE(tokens[2], new->csize);
 	CDBRANGE(tokens[4], new->fsizec);
 	CDBRANGE(tokens[5], new->fsizer);
 	CDBRANGE(tokens[7], new->filepos);
55094a9c
 
570b1d00
 	if(!strcmp(tokens[6], "*")) {
55094a9c
 	    new->encrypted = 2;
 	} else {
570b1d00
 	    if(strcmp(tokens[6], "0") && strcmp(tokens[6], "1")) {
15f413d1
 		cli_errmsg("cli_loadcdb: Invalid encryption flag value in signature for %s\n", tokens[0]);
55094a9c
 		if(new->name.re_magic)
 		    cli_regfree(&new->name);
 		mpool_free(engine->mempool, new->virname);
 		mpool_free(engine->mempool, new);
 		ret = CL_EMEM;
 		break;
 	    }
570b1d00
 	    new->encrypted = *tokens[6] - 0x30;
55094a9c
 	}
 
570b1d00
 	if(strcmp(tokens[9], "*")) {
 	    new->res2 = cli_mpool_strdup(engine->mempool, tokens[9]);
55094a9c
 	    if(!new->res2) {
15f413d1
 		cli_errmsg("cli_loadcdb: Can't allocate memory for res2 in signature for %s\n", tokens[0]);
55094a9c
 		if(new->name.re_magic)
 		    cli_regfree(&new->name);
 		mpool_free(engine->mempool, new->virname);
 		mpool_free(engine->mempool, new);
 		ret = CL_EMEM;
 		break;
 	    }
 	}
 
 	new->next = engine->cdb;
 	engine->cdb = new;
 	sigs++;
     }
     if(engine->ignored)
 	free(buffer_cpy);
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     if(signo)
 	*signo += sigs;
 
     return CL_SUCCESS;
 }
 
15850fc6
 static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options);
52c67b86
 
15850fc6
 int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
e3aaff8e
 {
056d95dc
 	FILE *fs = NULL;
f51e962f
 	int ret = CL_SUCCESS;
4efca56b
 	uint8_t skipped = 0;
ed9753e9
 	const char *dbname;
f51e962f
 
4048c4f6
 
e8ae4fae
     if(!dbio && (fs = fopen(filename, "rb")) == NULL) {
99ca7f53
 	if(options & CL_DB_DIRECTORY) { /* bb#1624 */
 	    if(access(filename, R_OK)) {
 		if(errno == ENOENT) {
 		    cli_dbgmsg("Detected race condition, ignoring old file %s\n", filename);
 		    return CL_SUCCESS;
 		}
 	    }
 	}
f51e962f
 	cli_errmsg("cli_load(): Can't open file %s\n", filename);
4048c4f6
 	return CL_EOPEN;
     }
 
58481352
     if((dbname = strrchr(filename, *PATHSEP)))
ed9753e9
 	dbname++;
     else
 	dbname = filename;
4048c4f6
 
ed9753e9
     if(cli_strbcasestr(dbname, ".db")) {
e8ae4fae
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
ed9753e9
 
     } else if(cli_strbcasestr(dbname, ".cvd")) {
02e46f3f
 	ret = cli_cvdload(fs, engine, signo, options, 0, filename);
9d193ff2
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".cld")) {
02e46f3f
 	ret = cli_cvdload(fs, engine, signo, options, 1, filename);
4048c4f6
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".hdb")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options, dbio, dbname);
db65451b
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".hdu")) {
70edb085
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 	else
 	    skipped = 1;
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".fp")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_FP, options, dbio, dbname);
4048c4f6
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".mdb")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options, dbio, dbname);
c09d6c19
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".mdu")) {
70edb085
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 	else
 	    skipped = 1;
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".ndb")) {
e8ae4fae
 	ret = cli_loadndb(fs, engine, signo, 0, options, dbio, dbname);
555c5390
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".ndu")) {
09eab32a
 	if(!(options & CL_DB_PUA))
70edb085
 	    skipped = 1;
 	else
b023c36d
 	    ret = cli_loadndb(fs, engine, signo, 0, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 
677fc4ba
     } else if(cli_strbcasestr(filename, ".ldb")) {
        ret = cli_loadldb(fs, engine, signo, options, dbio, dbname);
 
     } else if(cli_strbcasestr(filename, ".ldu")) {
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname);
677fc4ba
 	else
 	    skipped = 1;
52dd3a6b
     } else if(cli_strbcasestr(filename, ".cbc")) {
 	if(options & CL_DB_BYTECODE)
 	    ret = cli_loadcbc(fs, engine, signo, options, dbio, dbname);
 	else
 	    skipped = 1;
ed9753e9
     } else if(cli_strbcasestr(dbname, ".sdb")) {
e8ae4fae
 	ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname);
b68d11d2
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".zmd")) {
e8ae4fae
 	ret = cli_loadmd(fs, engine, signo, 1, options, dbio, dbname);
a62ae54f
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".rmd")) {
e8ae4fae
 	ret = cli_loadmd(fs, engine, signo, 2, options, dbio, dbname);
e5916a51
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".cfg")) {
e8ae4fae
 	ret = cli_dconf_load(fs, engine, options, dbio);
bc93eda0
 
ace26bfe
     } else if(cli_strbcasestr(dbname, ".info")) {
 	ret = cli_loadinfo(fs, engine, options, dbio);
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".wdb")) {
056d95dc
 	if(options & CL_DB_PHISHING_URLS) {
e8ae4fae
 	    ret = cli_loadwdb(fs, engine, options, dbio);
056d95dc
 	} else
7dec7955
 	    skipped = 1;
0810d861
     } else if(cli_strbcasestr(dbname, ".pdb") || cli_strbcasestr(dbname, ".gdb")) {
056d95dc
 	if(options & CL_DB_PHISHING_URLS) {
03527bee
 	    ret = cli_loadpdb(fs, engine, signo, options, dbio);
056d95dc
 	} else
7dec7955
 	    skipped = 1;
6038397e
     } else if(cli_strbcasestr(dbname, ".ftm")) {
e8ae4fae
 	ret = cli_loadftm(fs, engine, options, 0, dbio);
7021b545
 
04133ff9
     } else if(cli_strbcasestr(dbname, ".ign") || cli_strbcasestr(dbname, ".ign2")) {
e8ae4fae
 	ret = cli_loadign(fs, engine, options, dbio);
ed9753e9
 
182e40db
     } else if(cli_strbcasestr(dbname, ".idb")) {
     	ret = cli_loadidb(fs, engine, signo, options, dbio);
a8d621cf
 
55094a9c
     } else if(cli_strbcasestr(dbname, ".cdb")) {
     	ret = cli_loadcdb(fs, engine, signo, options, dbio);
 
4048c4f6
     } else {
f51e962f
 	cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
e8ae4fae
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
4048c4f6
     }
 
4efca56b
     if(ret) {
975ef8ce
 	cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
4efca56b
     } else  {
 	if(skipped)
 	    cli_dbgmsg("%s skipped\n", filename);
 	else
 	    cli_dbgmsg("%s loaded\n", filename);
     }
4048c4f6
 
056d95dc
     if(fs)
 	fclose(fs);
 
4048c4f6
     return ret;
e3aaff8e
 }
 
15850fc6
 static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options)
e3aaff8e
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	char *dbfile;
284e1ee4
 	int ret = CL_EOPEN, have_cld;
 	struct cl_cvd *daily_cld, *daily_cvd;
e3aaff8e
 
 
0eacacec
     cli_dbgmsg("Loading databases from %s\n", dirname);
04133ff9
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cli_loaddbdir(): Can't open directory %s\n", dirname);
         return CL_EOPEN;
     }
 
     /* first round - load .ign and .ign2 files */
 #ifdef HAVE_READDIR_R_3
     while(!readdir_r(dd, &result.d, &dent) && dent) {
 #elif defined(HAVE_READDIR_R_2)
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
 #else
     while((dent = readdir(dd))) {
 #endif
 	if(dent->d_ino)
 	{
 	    if(cli_strbcasestr(dent->d_name, ".ign") || cli_strbcasestr(dent->d_name, ".ign2")) {
 		dbfile = (char *) cli_malloc(strlen(dent->d_name) + strlen(dirname) + 2);
 		if(!dbfile) {
 		    cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 		sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
 		ret = cli_load(dbfile, engine, signo, options, NULL);
 		if(ret) {
 		    cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
 
     /* the daily db must be loaded before main */
ed9753e9
     dbfile = (char *) cli_malloc(strlen(dirname) + 20);
04133ff9
     if(!dbfile) {
 	closedir(dd);
0eacacec
 	return CL_EMEM;
ed9753e9
     }
 
58481352
     sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
284e1ee4
     have_cld = !access(dbfile, R_OK);
     if(have_cld) {
 	daily_cld = cl_cvdhead(dbfile);
 	if(!daily_cld) {
 	    cli_errmsg("cli_loaddbdir(): error parsing header of %s\n", dbfile);
 	    free(dbfile);
 	    closedir(dd);
 	    return CL_EMALFDB;
 	}
     }
     sprintf(dbfile, "%s"PATHSEP"daily.cvd", dirname); 
     if(!access(dbfile, R_OK)) {
 	if(have_cld) {
 	    daily_cvd = cl_cvdhead(dbfile);
 	    if(!daily_cvd) {
 		cli_errmsg("cli_loaddbdir(): error parsing header of %s\n", dbfile);
 		free(dbfile);
2979de20
 		cl_cvdfree(daily_cld);
284e1ee4
 		closedir(dd);
 		return CL_EMALFDB;
 	    }
 	    if(daily_cld->version > daily_cvd->version)
 		sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
 	    cl_cvdfree(daily_cvd);
 	}
     } else {
 	sprintf(dbfile, "%s"PATHSEP"daily.cld", dirname);
     }
     if(have_cld)
 	cl_cvdfree(daily_cld);
 
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
04133ff9
 	closedir(dd);
ed9753e9
 	return ret;
     }
 
816d66a8
     /* try to load local.gdb next */
58481352
     sprintf(dbfile, "%s"PATHSEP"local.gdb", dirname);
816d66a8
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
 	free(dbfile);
04133ff9
 	closedir(dd);
816d66a8
 	return ret;
     }
 
ed9753e9
     /* check for and load daily.cfg */
58481352
     sprintf(dbfile, "%s"PATHSEP"daily.cfg", dirname);
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
04133ff9
 	closedir(dd);
ed9753e9
 	return ret;
0eacacec
     }
     free(dbfile);
 
04133ff9
     /* second round - load everything else */
     rewinddir(dd);
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 	if(dent->d_ino)
 	{
04133ff9
 	    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)) {
208ceae5
 		if((options & CL_DB_OFFICIAL_ONLY) && !strstr(dirname, "clamav-") && !cli_strbcasestr(dent->d_name, ".cld") && !cli_strbcasestr(dent->d_name, ".cvd")) {
 		    cli_dbgmsg("Skipping unofficial database %s\n", dent->d_name);
 		    continue;
 		}
 
0eacacec
 		dbfile = (char *) cli_malloc(strlen(dent->d_name) + strlen(dirname) + 2);
e3aaff8e
 		if(!dbfile) {
15e1a7cd
 		    cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
e3aaff8e
 		    closedir(dd);
 		    return CL_EMEM;
 		}
58481352
 		sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
e8ae4fae
 		ret = cli_load(dbfile, engine, signo, options, NULL);
3dad68eb
 		if(ret) {
15e1a7cd
 		    cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
e3aaff8e
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
     closedir(dd);
871177cd
     if(ret == CL_EOPEN)
d6898042
 	cli_errmsg("cli_loaddb(): No supported database files found in %s\n", dirname);
 
     return ret;
e3aaff8e
 }
 
2accc66f
 int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int dboptions)
04933acd
 {
 	struct stat sb;
f7470773
 	int ret;
04933acd
 
15850fc6
     if(!engine) {
724b2bf7
 	cli_errmsg("cl_load: engine == NULL\n");
 	return CL_ENULLARG;
     }
04933acd
 
d7c3f6e2
     if(engine->dboptions & CL_DB_COMPILED) {
 	cli_errmsg("cl_load(): can't load new databases when engine is already compiled\n");
 	return CL_EARG;
     }
 
04933acd
     if(stat(path, &sb) == -1) {
ac1b219c
         cli_errmsg("cl_load(): Can't get status of %s\n", path);
871177cd
         return CL_ESTAT;
04933acd
     }
 
2accc66f
     if((dboptions & CL_DB_PHISHING_URLS) && !engine->phishcheck && (engine->dconf->phishing & PHISHING_CONF_ENGINE))
15850fc6
 	if((ret = phishing_init(engine)))
724b2bf7
 	    return ret;
f7470773
 
927d0548
     if((dboptions & CL_DB_BYTECODE) && !engine->bcs.inited) {
 	if((ret = cli_bytecode_init(&engine->bcs)))
52dd3a6b
 	    return ret;
     } else {
 	cli_dbgmsg("Bytecode engine disabled\n");
     }
 
6223810b
     if(cli_cache_init(engine))
 	return CL_EMEM;
 
d7c3f6e2
     engine->dboptions |= dboptions;
616bc3b9
 
f51e962f
     switch(sb.st_mode & S_IFMT) {
52dd3a6b
 	case S_IFREG:
2accc66f
 	    ret = cli_load(path, engine, signo, dboptions, NULL);
bc93eda0
 	    break;
04933acd
 
 	case S_IFDIR:
99ca7f53
 	    ret = cli_loaddbdir(path, engine, signo, dboptions | CL_DB_DIRECTORY);
bc93eda0
 	    break;
04933acd
 
 	default:
bc93eda0
 	    cli_errmsg("cl_load(%s): Not supported database file type\n", path);
04933acd
 	    return CL_EOPEN;
     }
bc93eda0
     return ret;
04933acd
 }
 
4048c4f6
 const char *cl_retdbdir(void)
 {
     return DATADIR;
 }
 
e3aaff8e
 int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
 {
 	DIR *dd;
2bb229f6
 	const struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
         char *fname;
 
 
     if(dbstat) {
b5134815
 	dbstat->entries = 0;
e3aaff8e
 	dbstat->stattab = NULL;
75ccac9f
 	dbstat->statdname = NULL;
6f38c939
 	dbstat->dir = cli_strdup(dirname);
e3aaff8e
     } else {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname);
cd69cf20
 	cl_statfree(dbstat);
e3aaff8e
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dirname);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 	if(dent->d_ino)
 	{
3dad68eb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
b5134815
 		dbstat->entries++;
84fd5a61
 		dbstat->stattab = (struct stat *) cli_realloc2(dbstat->stattab, dbstat->entries * sizeof(struct stat));
cd69cf20
 		if(!dbstat->stattab) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
b2354dc1
 #ifdef _WIN32
84fd5a61
 		dbstat->statdname = (char **) cli_realloc2(dbstat->statdname, dbstat->entries * sizeof(char *));
cd69cf20
 		if(!dbstat->statdname) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
75ccac9f
 #endif
 
0eacacec
                 fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 32);
cd69cf20
 		if(!fname) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
58481352
 		sprintf(fname, "%s"PATHSEP"%s", dirname, dent->d_name);
b2354dc1
 #ifdef _WIN32
0eacacec
 		dbstat->statdname[dbstat->entries - 1] = (char *) cli_malloc(strlen(dent->d_name) + 1);
cd69cf20
 		if(!dbstat->statdname[dbstat->entries - 1]) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
b5134815
 		strcpy(dbstat->statdname[dbstat->entries - 1], dent->d_name);
75ccac9f
 #endif
b5134815
 		stat(fname, &dbstat->stattab[dbstat->entries - 1]);
e3aaff8e
 		free(fname);
 	    }
 	}
     }
 
     closedir(dd);
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
 int cl_statchkdir(const struct cl_stat *dbstat)
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	struct stat sb;
53721687
 	unsigned int i, found;
e3aaff8e
 	char *fname;
 
 
     if(!dbstat || !dbstat->dir) {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dbstat->dir)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
feeaa333
 	if(dent->d_ino)
 	{
3dad68eb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
0eacacec
                 fname = cli_malloc(strlen(dbstat->dir) + strlen(dent->d_name) + 32);
cd69cf20
 		if(!fname) {
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
58481352
 		sprintf(fname, "%s"PATHSEP"%s", dbstat->dir, dent->d_name);
e3aaff8e
 		stat(fname, &sb);
 		free(fname);
 
 		found = 0;
b5134815
 		for(i = 0; i < dbstat->entries; i++)
b2354dc1
 #ifdef _WIN32
75ccac9f
 		    if(!strcmp(dbstat->statdname[i], dent->d_name)) {
 #else
e3aaff8e
 		    if(dbstat->stattab[i].st_ino == sb.st_ino) {
75ccac9f
 #endif
e3aaff8e
 			found = 1;
e3f0de57
 			if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
 			    closedir(dd);
e3aaff8e
 			    return 1;
e3f0de57
 			}
e3aaff8e
 		    }
 
e3f0de57
 		if(!found) {
 		    closedir(dd);
e3aaff8e
 		    return 1;
e3f0de57
 		}
e3aaff8e
 	    }
 	}
     }
 
     closedir(dd);
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
 int cl_statfree(struct cl_stat *dbstat)
 {
 
     if(dbstat) {
75ccac9f
 
b2354dc1
 #ifdef _WIN32
75ccac9f
 	    int i;
 
cd69cf20
 	if(dbstat->statdname) {
 	    for(i = 0; i < dbstat->entries; i++) {
 		if(dbstat->statdname[i])
 		    free(dbstat->statdname[i]);
 		dbstat->statdname[i] = NULL;
 	    }
 	    free(dbstat->statdname);
 	    dbstat->statdname = NULL;
75ccac9f
 	}
 #endif
 
cd69cf20
 	if(dbstat->stattab) {
 	    free(dbstat->stattab);
 	    dbstat->stattab = NULL;
 	}
b5134815
 	dbstat->entries = 0;
cd69cf20
 
9e431a95
 	if(dbstat->dir) {
e3aaff8e
 	    free(dbstat->dir);
9e431a95
 	    dbstat->dir = NULL;
 	}
e3aaff8e
     } else {
8000d078
         cli_errmsg("cl_statfree(): Null argument passed\n");
e3aaff8e
 	return CL_ENULLARG;
     }
 
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
f51e962f
 
724b2bf7
 int cl_engine_free(struct cl_engine *engine)
f51e962f
 {
677fc4ba
 	unsigned int i, j;
f51e962f
 	struct cli_matcher *root;
c9c463fe
 
f51e962f
 
     if(!engine) {
 	cli_errmsg("cl_free: engine == NULL\n");
724b2bf7
 	return CL_ENULLARG;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
04ba76d2
     if(engine->refcount)
 	engine->refcount--;
 
f51e962f
     if(engine->refcount) {
 #ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ref_mutex);
 #endif
724b2bf7
 	return CL_SUCCESS;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
bc93eda0
     if(engine->root) {
4addba22
 	for(i = 0; i < CLI_MTARGETS; i++) {
bc93eda0
 	    if((root = engine->root[i])) {
bedc58de
 		if(!root->ac_only)
bc93eda0
 		    cli_bm_free(root);
bedc58de
 		cli_ac_free(root);
677fc4ba
 		if(root->ac_lsigtable) {
 		    for(j = 0; j < root->ac_lsigs; j++) {
47d40feb
 			mpool_free(engine->mempool, root->ac_lsigtable[j]->logic);
677fc4ba
 			FREE_TDB(root->ac_lsigtable[j]->tdb);
47d40feb
 			mpool_free(engine->mempool, root->ac_lsigtable[j]);
677fc4ba
 		    }
47d40feb
 		    mpool_free(engine->mempool, root->ac_lsigtable);
677fc4ba
 		}
47d40feb
 		mpool_free(engine->mempool, root);
bc93eda0
 	    }
f51e962f
 	}
47d40feb
 	mpool_free(engine->mempool, engine->root);
f51e962f
     }
 
2b459819
     if((root = engine->md5_hdb)) {
44712fcb
 	cli_md5m_free(root);
47d40feb
 	mpool_free(engine->mempool, root);
f51e962f
     }
 
2b459819
     if((root = engine->md5_mdb)) {
44712fcb
 	cli_md5m_free(root);
47d40feb
 	mpool_free(engine->mempool, root->soff);
c3671221
 	if(root->md5_sizes_hs.capacity) {
cc447ac8
 		cli_hashset_destroy(&root->md5_sizes_hs);
c3671221
 	}
47d40feb
 	mpool_free(engine->mempool, root);
2f942502
     }
 
2b459819
     if((root = engine->md5_fp)) {
44712fcb
 	cli_md5m_free(root);
47d40feb
 	mpool_free(engine->mempool, root);
2b459819
     }
 
55094a9c
     while(engine->cdb) {
 	struct cli_cdb *pt = engine->cdb;
 	engine->cdb = pt->next;
 	if(pt->name.re_magic)
 	    cli_regfree(&pt->name);
 	mpool_free(engine->mempool, pt->res2);
 	mpool_free(engine->mempool, pt->virname);
 	mpool_free(engine->mempool, pt);
     }
 
ace26bfe
     while(engine->dbinfo) {
 	struct cli_dbinfo *pt = engine->dbinfo;
 	engine->dbinfo = pt->next;
 	mpool_free(engine->mempool, pt->name);
 	mpool_free(engine->mempool, pt->hash);
4f3997d6
 	if(pt->cvd)
 	    cl_cvdfree(pt->cvd);
ace26bfe
 	mpool_free(engine->mempool, pt);
     }
 
52dd3a6b
     if(engine->dconf->bytecode & BYTECODE_ENGINE_MASK) {
 	if (engine->bcs.all_bcs)
 	    for(i=0;i<engine->bcs.count;i++)
 		cli_bytecode_destroy(&engine->bcs.all_bcs[i]);
 	cli_bytecode_done(&engine->bcs);
 	free(engine->bcs.all_bcs);
ab636570
 	for (i=0;i<_BC_LAST_HOOK - _BC_START_HOOKS;i++) {
 	    free (engine->hooks[i]);
 	}
52dd3a6b
     }
15850fc6
     if(engine->dconf->phishing & PHISHING_CONF_ENGINE)
692bda68
 	phishing_done(engine);
bc93eda0
     if(engine->dconf)
47d40feb
 	mpool_free(engine->mempool, engine->dconf);
bc93eda0
 
b023c36d
     if(engine->pua_cats)
47d40feb
 	mpool_free(engine->mempool, engine->pua_cats);
b023c36d
 
cca29953
     if(engine->iconcheck) {
 	struct icon_matcher *iconcheck = engine->iconcheck;
 	for(i=0; i<3; i++) {
 	    if(iconcheck->icons[i]) {
a2f709da
 		for (j=0;j<iconcheck->icon_counts[i];j++) {
 		    struct icomtr* metric = iconcheck->icons[i];
 		    mpool_free(engine->mempool, metric[j].name);
 		}
cca29953
 		mpool_free(engine->mempool, iconcheck->icons[i]);
 	    }
d1a0a37b
 	}
cca29953
 	if(iconcheck->group_names[0]) {
 	    for(i=0; i<iconcheck->group_counts[0]; i++)
 		mpool_free(engine->mempool, iconcheck->group_names[0][i]);
 	    mpool_free(engine->mempool, iconcheck->group_names[0]);
 	}
 	if(iconcheck->group_names[1]) {
 	    for(i=0; i<iconcheck->group_counts[1]; i++)
 		mpool_free(engine->mempool, iconcheck->group_names[1][i]);
 	    mpool_free(engine->mempool, iconcheck->group_names[1]);
 	}
 	mpool_free(engine->mempool, iconcheck);
     }	
c318ce5b
 
33068e09
     if(engine->tmpdir)
47d40feb
 	mpool_free(engine->mempool, engine->tmpdir);
33068e09
 
6223810b
     if(engine->cache)
 	cli_cache_destroy(engine);
 
0d9dbdef
     cli_ftfree(engine);
04133ff9
     if(engine->ignored) {
 	cli_bm_free(engine->ignored);
 	mpool_free(engine->mempool, engine->ignored);
     }
 
0d9dbdef
 #ifdef USE_MPOOL
47d40feb
     if(engine->mempool) mpool_destroy(engine->mempool);
e21657df
 #endif
f51e962f
     free(engine);
724b2bf7
     return CL_SUCCESS;
f51e962f
 }
 
c3671221
 static void cli_md5db_build(struct cli_matcher* root)
 {
 	if(root && root->md5_sizes_hs.capacity) {
 		/* TODO: use hashset directly, instead of the array when matching*/
a1c9ad2c
 		cli_dbgmsg("Converting hashset to array: %u entries\n", root->md5_sizes_hs.count);
d22176ef
 
 #ifdef USE_MPOOL
 		{
 		uint32_t *mpoolht;
 		unsigned int mpoolhtsz = root->md5_sizes_hs.count * sizeof(*mpoolht);
47d40feb
 		root->soff = mpool_malloc(root->mempool, mpoolhtsz);
cc447ac8
 		root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &mpoolht);
d22176ef
 		memcpy(root->soff, mpoolht, mpoolhtsz);
 		free(mpoolht);
 		}
 #else
cc447ac8
 		root->soff_len = cli_hashset_toarray(&root->md5_sizes_hs, &root->soff);
d22176ef
 #endif
cc447ac8
 		cli_hashset_destroy(&root->md5_sizes_hs);
424d41d3
 		cli_qsort(root->soff, root->soff_len, sizeof(uint32_t), NULL);
c3671221
 	}
 }
 
724b2bf7
 int cl_engine_compile(struct cl_engine *engine)
f51e962f
 {
7021b545
 	unsigned int i;
 	int ret;
f51e962f
 	struct cli_matcher *root;
 
 
7021b545
     if(!engine)
 	return CL_ENULLARG;
 
     if(!engine->ftypes)
15850fc6
 	if((ret = cli_loadftm(NULL, engine, 0, 1, NULL)))
7021b545
 	    return ret;
f51e962f
 
4addba22
     for(i = 0; i < CLI_MTARGETS; i++) {
 	if((root = engine->root[i])) {
 	    if((ret = cli_ac_buildtrie(root)))
 		return ret;
44712fcb
 	    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)" : "");
4addba22
 	}
     }
44712fcb
     if(engine->md5_hdb)
 	cli_dbgmsg("MD5 sigs (files): %u\n", engine->md5_hdb->md5_patterns);
 
     if(engine->md5_mdb)
 	cli_dbgmsg("MD5 sigs (PE sections): %u\n", engine->md5_mdb->md5_patterns);
f51e962f
 
2e11bcdf
     if((ret = cli_build_regex_list(engine->whitelist_matcher))) {
 	    return ret;
     }
     if((ret = cli_build_regex_list(engine->domainlist_matcher))) {
 	    return ret;
     }
c3671221
     cli_md5db_build(engine->md5_mdb);
04133ff9
     if(engine->ignored) {
 	cli_bm_free(engine->ignored);
 	mpool_free(engine->mempool, engine->ignored);
 	engine->ignored = NULL;
     }
02ce73de
     cli_dconf_print(engine->dconf);
47d40feb
     mpool_flush(engine->mempool);
02ce73de
 
52dd3a6b
     /* Compile bytecode */
540fc128
     if((ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode))) {
ab636570
 	cli_errmsg("Unable to compile/load bytecode: %s\n", cl_strerror(ret));
52dd3a6b
 	return ret;
     }
 
b3df93db
     engine->dboptions |= CL_DB_COMPILED;
8d3aca30
     return CL_SUCCESS;
f51e962f
 }
 
b8fe70b3
 int cl_engine_addref(struct cl_engine *engine)
f51e962f
 {
     if(!engine) {
b8fe70b3
 	cli_errmsg("cl_engine_addref: engine == NULL\n");
 	return CL_ENULLARG;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     engine->refcount++;
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
b8fe70b3
     return CL_SUCCESS;
f51e962f
 }
d46cdd59
 
 static int countentries(const char *dbname, unsigned int *sigs)
 {
 	char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1];
 	FILE *fs;
 	unsigned int entry = 0;
 
     fs = fopen(dbname, "r");
     if(!fs) {
 	cli_errmsg("countentries: Can't open file %s\n", dbname);
 	return CL_EOPEN;
     }
     while(fgets(buffer, sizeof(buffer), fs)) {
 	if(buffer[0] == '#')
 	    continue;
 	entry++;
     }
     fclose(fs);
     *sigs += entry;
     return CL_SUCCESS;
 }
 
 static int countsigs(const char *dbname, unsigned int options, unsigned int *sigs)
 {
     if((cli_strbcasestr(dbname, ".cvd") || cli_strbcasestr(dbname, ".cld"))) {
 	if(options & CL_COUNTSIGS_OFFICIAL) {
 		struct cl_cvd *cvd = cl_cvdhead(dbname);
 	    if(!cvd) {
 		cli_errmsg("countsigs: Can't parse %s\n", dbname);
 		return CL_ECVD;
 	    }
 	    *sigs += cvd->sigs;
 	    cl_cvdfree(cvd);
 	}
     } else if(cli_strbcasestr(dbname, ".cbc")) {
 	if(options & CL_COUNTSIGS_UNOFFICIAL)
 	    (*sigs)++;
 
     } else if(cli_strbcasestr(dbname, ".wdb") || cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".ftm") || cli_strbcasestr(dbname, ".cfg")) {
 	/* ignore */
 
     } else if((options & CL_COUNTSIGS_UNOFFICIAL) && CLI_DBEXT(dbname)) {
 	return countentries(dbname, sigs);
     }
 
     return CL_SUCCESS;
 }
 
 int cl_countsigs(const char *path, unsigned int countoptions, unsigned int *sigs)
 {
 	struct stat sb;
 	char fname[1024];
 	struct dirent *dent;
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
 #endif
 	DIR *dd;
 	int ret;
 
     if(!sigs)
 	return CL_ENULLARG;
 
     if(stat(path, &sb) == -1) {
 	cli_errmsg("cl_countsigs: Can't stat %s\n", path);
 	return CL_ESTAT;
     }
 
     if((sb.st_mode & S_IFMT) == S_IFREG) {
 	return countsigs(path, countoptions, sigs);
 
     } else if((sb.st_mode & S_IFMT) == S_IFDIR) {
 	if((dd = opendir(path)) == NULL) {
 	    cli_errmsg("cl_countsigs: Can't open directory %s\n", path);
 	    return CL_EOPEN;
 	}
 #ifdef HAVE_READDIR_R_3
 	while(!readdir_r(dd, &result.d, &dent) && dent) {
 #elif defined(HAVE_READDIR_R_2)
 	while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
 #else
 	while((dent = readdir(dd))) {
 #endif
 	    if(dent->d_ino) {
 		if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
 		    snprintf(fname, sizeof(fname), "%s"PATHSEP"%s", path, dent->d_name);
 		    fname[sizeof(fname) - 1] = 0;
 		    ret = countsigs(fname, countoptions, sigs);
 		    if(ret != CL_SUCCESS) {
 			closedir(dd);
 			return ret;
 		    }
 		}
 	    }
 	}
 	closedir(dd);
     } else {
 	cli_errmsg("cl_countsigs: Unsupported file type\n");
 	return CL_EARG;
     }
 
     return CL_SUCCESS;
 }