mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2270 lines
51 KiB
2270 lines
51 KiB
/*
|
|
* Copyright (C) 2007-2008 Sourcefire, Inc.
|
|
*
|
|
* Authors: Tomasz Kojm
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifndef C_WINDOWS
|
|
#include <dirent.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <zlib.h>
|
|
|
|
#include "clamav.h"
|
|
#include "cvd.h"
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#include "matcher-ac.h"
|
|
#include "matcher-bm.h"
|
|
#include "matcher.h"
|
|
#include "others.h"
|
|
#include "str.h"
|
|
#include "dconf.h"
|
|
#include "filetypes.h"
|
|
#include "filetypes_int.h"
|
|
#include "readdb.h"
|
|
#include "cltypes.h"
|
|
|
|
#include "phishcheck.h"
|
|
#include "phish_whitelist.h"
|
|
#include "phish_domaincheck_db.h"
|
|
#include "regex_list.h"
|
|
#include "hashtab.h"
|
|
|
|
#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#endif
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
# include <pthread.h>
|
|
static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
#endif
|
|
|
|
struct cli_ignsig {
|
|
char *dbname, *signame;
|
|
unsigned int line;
|
|
struct cli_ignsig *next;
|
|
};
|
|
|
|
struct cli_ignored {
|
|
struct hashset hs;
|
|
struct cli_ignsig *list;
|
|
};
|
|
|
|
/* Prototypes for old public functions just to shut up some gcc warnings;
|
|
* to be removed in 1.0
|
|
*/
|
|
int cl_loaddb(const char *filename, struct cl_engine **engine, unsigned int *signo);
|
|
int cl_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo);
|
|
|
|
|
|
char *cli_virname(char *virname, unsigned int official, unsigned int allocated)
|
|
{
|
|
unsigned int len;
|
|
char *newname, *pt;
|
|
|
|
|
|
if(!virname)
|
|
return NULL;
|
|
|
|
if((pt = strstr(virname, " (Clam)")))
|
|
len = strlen(virname) - strlen(pt);
|
|
else
|
|
len = strlen(virname);
|
|
|
|
if(!len) {
|
|
cli_errmsg("cli_virname: Empty virus name\n");
|
|
return NULL;
|
|
}
|
|
|
|
if(!official) {
|
|
newname = (char *) cli_malloc(len + 11 + 1);
|
|
if(!newname) {
|
|
cli_errmsg("cli_virname: Can't allocate memory for newname\n");
|
|
if(allocated)
|
|
free(virname);
|
|
return NULL;
|
|
}
|
|
strncpy(newname, virname, len);
|
|
newname[len] = 0;
|
|
strcat(newname, ".UNOFFICIAL");
|
|
newname[len + 11] = 0;
|
|
if(allocated)
|
|
free(virname);
|
|
return newname;
|
|
}
|
|
|
|
if(!allocated) {
|
|
newname = (char *) cli_malloc(len + 1);
|
|
if(!newname) {
|
|
cli_errmsg("cli_virname: Can't allocate memory for newname\n");
|
|
return NULL;
|
|
}
|
|
strncpy(newname, virname, len);
|
|
newname[len] = 0;
|
|
return newname;
|
|
}
|
|
|
|
return virname;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct cli_bm_patt *bm_new;
|
|
char *pt, *hexcpy, *start, *n;
|
|
int ret, asterisk = 0;
|
|
unsigned int i, j, len, parts = 0;
|
|
int mindist = 0, maxdist = 0, error = 0;
|
|
|
|
|
|
if(strchr(hexsig, '{')) {
|
|
|
|
root->ac_partsigs++;
|
|
|
|
if(!(hexcpy = cli_strdup(hexsig)))
|
|
return CL_EMEM;
|
|
|
|
len = strlen(hexsig);
|
|
for(i = 0; i < len; i++)
|
|
if(hexsig[i] == '{' || hexsig[i] == '*')
|
|
parts++;
|
|
|
|
if(parts)
|
|
parts++;
|
|
|
|
start = pt = hexcpy;
|
|
for(i = 1; i <= parts; i++) {
|
|
|
|
if(i != parts) {
|
|
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;
|
|
}
|
|
}
|
|
*pt++ = 0;
|
|
}
|
|
|
|
if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
|
|
cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
|
|
error = 1;
|
|
break;
|
|
}
|
|
|
|
if(i == parts)
|
|
break;
|
|
|
|
mindist = maxdist = 0;
|
|
|
|
if(asterisk) {
|
|
start = pt;
|
|
continue;
|
|
}
|
|
|
|
if(!(start = strchr(pt, '}'))) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
*start++ = 0;
|
|
|
|
if(!pt) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
|
|
if(!strchr(pt, '-')) {
|
|
if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
|
|
error = 1;
|
|
break;
|
|
}
|
|
} else {
|
|
if((n = cli_strtok(pt, 0, "-"))) {
|
|
if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
|
|
error = 1;
|
|
free(n);
|
|
break;
|
|
}
|
|
free(n);
|
|
}
|
|
|
|
if((n = cli_strtok(pt, 1, "-"))) {
|
|
if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
|
|
error = 1;
|
|
free(n);
|
|
break;
|
|
}
|
|
free(n);
|
|
}
|
|
|
|
if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
|
|
error = 1;
|
|
free(n);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(hexcpy);
|
|
if(error)
|
|
return CL_EMALFDB;
|
|
|
|
} else if(strchr(hexsig, '*')) {
|
|
root->ac_partsigs++;
|
|
|
|
len = strlen(hexsig);
|
|
for(i = 0; i < len; i++)
|
|
if(hexsig[i] == '*')
|
|
parts++;
|
|
|
|
if(parts)
|
|
parts++;
|
|
|
|
for(i = 1; i <= parts; i++) {
|
|
if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
|
|
cli_errmsg("Can't extract part %d of partial signature.\n", i);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
|
|
cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
|
|
free(pt);
|
|
return ret;
|
|
}
|
|
|
|
free(pt);
|
|
}
|
|
|
|
} else if(root->ac_only || strpbrk(hexsig, "?(") || type || lsigid) {
|
|
if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
|
|
cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
|
|
return ret;
|
|
}
|
|
|
|
} else {
|
|
bm_new = (struct cli_bm_patt *) cli_calloc(1, sizeof(struct cli_bm_patt));
|
|
if(!bm_new)
|
|
return CL_EMEM;
|
|
|
|
if(!(bm_new->pattern = (unsigned char *) cli_hex2str(hexsig))) {
|
|
free(bm_new);
|
|
return CL_EMALFDB;
|
|
}
|
|
bm_new->length = strlen(hexsig) / 2;
|
|
|
|
bm_new->virname = cli_virname((char *) virname, options & CL_DB_OFFICIAL, 0);
|
|
if(!bm_new->virname) {
|
|
free(bm_new->pattern);
|
|
free(bm_new);
|
|
return CL_EMEM;
|
|
}
|
|
|
|
if(offset) {
|
|
bm_new->offset = cli_strdup(offset);
|
|
if(!bm_new->offset) {
|
|
free(bm_new->pattern);
|
|
free(bm_new->virname);
|
|
free(bm_new);
|
|
return CL_EMEM;
|
|
}
|
|
}
|
|
|
|
bm_new->target = target;
|
|
|
|
if(bm_new->length > root->maxpatlen)
|
|
root->maxpatlen = bm_new->length;
|
|
|
|
if((ret = cli_bm_addpatt(root, bm_new))) {
|
|
cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
|
|
free(bm_new->pattern);
|
|
free(bm_new->virname);
|
|
free(bm_new);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
int cli_initengine(struct cl_engine **engine, unsigned int options)
|
|
{
|
|
int ret;
|
|
|
|
|
|
if(!*engine) {
|
|
#ifdef CL_EXPERIMENTAL
|
|
cli_dbgmsg("Initializing the engine (%s-exp)\n", cl_retver());
|
|
#else
|
|
cli_dbgmsg("Initializing the engine (%s)\n", cl_retver());
|
|
#endif
|
|
|
|
*engine = (struct cl_engine *) cli_calloc(1, sizeof(struct cl_engine));
|
|
if(!*engine) {
|
|
cli_errmsg("Can't allocate memory for the engine structure!\n");
|
|
return CL_EMEM;
|
|
}
|
|
|
|
(*engine)->refcount = 1;
|
|
|
|
(*engine)->root = cli_calloc(CLI_MTARGETS, sizeof(struct cli_matcher *));
|
|
if(!(*engine)->root) {
|
|
/* no need to free previously allocated memory here */
|
|
cli_errmsg("Can't allocate memory for roots!\n");
|
|
return CL_EMEM;
|
|
}
|
|
|
|
(*engine)->dconf = cli_dconf_init();
|
|
if(!(*engine)->dconf) {
|
|
cli_errmsg("Can't initialize dynamic configuration\n");
|
|
return CL_EMEM;
|
|
}
|
|
}
|
|
|
|
if((options & CL_DB_PHISHING_URLS) && (((struct cli_dconf*) (*engine)->dconf)->phishing & PHISHING_CONF_ENGINE))
|
|
if((ret = phishing_init(*engine)))
|
|
return ret;
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static int cli_initroots(struct cl_engine *engine, unsigned int options)
|
|
{
|
|
int i, ret;
|
|
struct cli_matcher *root;
|
|
|
|
|
|
for(i = 0; i < CLI_MTARGETS; i++) {
|
|
if(!engine->root[i]) {
|
|
cli_dbgmsg("Initializing engine->root[%d]\n", i);
|
|
root = engine->root[i] = (struct cli_matcher *) cli_calloc(1, sizeof(struct cli_matcher));
|
|
if(!root) {
|
|
cli_errmsg("cli_initroots: Can't allocate memory for cli_matcher\n");
|
|
return CL_EMEM;
|
|
}
|
|
|
|
if(cli_mtargets[i].ac_only || (options & CL_DB_ACONLY))
|
|
root->ac_only = 1;
|
|
|
|
cli_dbgmsg("Initialising AC pattern matcher of root[%d]\n", i);
|
|
if((ret = cli_ac_init(root, cli_ac_mindepth, cli_ac_maxdepth))) {
|
|
/* no need to free previously allocated memory here */
|
|
cli_errmsg("cli_initroots: Can't initialise AC pattern matcher\n");
|
|
return ret;
|
|
}
|
|
|
|
if(!root->ac_only) {
|
|
cli_dbgmsg("cli_initroots: Initializing BM tables of root[%d]\n", i);
|
|
if((ret = cli_bm_init(root))) {
|
|
cli_errmsg("cli_initroots: Can't initialise BM pattern matcher\n");
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio)
|
|
{
|
|
if(fs) {
|
|
return fgets(buff, size, fs);
|
|
|
|
} else {
|
|
char *pt;
|
|
unsigned int bs;
|
|
|
|
if(!dbio->size)
|
|
return NULL;
|
|
|
|
bs = dbio->size < size ? dbio->size + 1 : size;
|
|
if(dbio->gzs)
|
|
pt = gzgets(dbio->gzs, buff, bs);
|
|
else
|
|
pt = fgets(buff, bs, dbio->fs);
|
|
|
|
dbio->size -= strlen(buff);
|
|
if(!pt)
|
|
cli_errmsg("cli_dbgets: Preliminary end of data\n");
|
|
return pt;
|
|
}
|
|
}
|
|
|
|
static int cli_chkign(const struct cli_ignored *ignored, const char *dbname, unsigned int line, const char *signame)
|
|
{
|
|
struct cli_ignsig *pt;
|
|
|
|
if(!ignored || !dbname || !signame)
|
|
return 0;
|
|
|
|
if(hashset_contains(&ignored->hs, line)) {
|
|
pt = ignored->list;
|
|
while(pt) {
|
|
if(pt->line == line && !strcmp(pt->dbname, dbname) && !strcmp(pt->signame, signame)) {
|
|
cli_dbgmsg("Skipping signature %s @ %s:%u\n", signame, dbname, line);
|
|
return 1;
|
|
}
|
|
pt = pt->next;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if((unsigned int) (pt - sig + 2) > sizeof(cat)) {
|
|
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;
|
|
}
|
|
|
|
static int cli_loaddb(FILE *fs, struct cl_engine **engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
|
|
{
|
|
char buffer[FILEBUFF], *pt, *start;
|
|
unsigned int line = 0, sigs = 0;
|
|
int ret = 0;
|
|
struct cli_matcher *root;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if((ret = cli_initroots(*engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
root = (*engine)->root[0];
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
cli_chomp(buffer);
|
|
|
|
pt = strchr(buffer, '=');
|
|
if(!pt) {
|
|
cli_errmsg("Malformed pattern line %d\n", line);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
start = buffer;
|
|
*pt++ = 0;
|
|
|
|
if((*engine)->ignored && cli_chkign((*engine)->ignored, dbname, line, start))
|
|
continue;
|
|
|
|
if(*pt == '=') continue;
|
|
|
|
if((ret = cli_parse_add(root, start, pt, 0, 0, NULL, 0, NULL, options))) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
sigs++;
|
|
}
|
|
|
|
if(!line) {
|
|
cli_errmsg("Empty database file\n");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Problem parsing database at line %d\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(signo)
|
|
*signo += sigs;
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static int cli_loadwdb(FILE *fs, struct cl_engine **engine, unsigned int options, struct cli_dbio *dbio)
|
|
{
|
|
int ret = 0;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(!(((struct cli_dconf *) (*engine)->dconf)->phishing & PHISHING_CONF_ENGINE))
|
|
return CL_SUCCESS;
|
|
|
|
if(!(*engine)->whitelist_matcher) {
|
|
if((ret = init_whitelist(*engine))) {
|
|
phishing_done(*engine);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if((ret = load_regex_matcher((*engine)->whitelist_matcher, fs, options, 1, dbio))) {
|
|
phishing_done(*engine);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static int cli_loadpdb(FILE *fs, struct cl_engine **engine, unsigned int options, struct cli_dbio *dbio)
|
|
{
|
|
int ret = 0;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(!(((struct cli_dconf *) (*engine)->dconf)->phishing & PHISHING_CONF_ENGINE))
|
|
return CL_SUCCESS;
|
|
|
|
if(!(*engine)->domainlist_matcher) {
|
|
if((ret = init_domainlist(*engine))) {
|
|
phishing_done(*engine);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if((ret = load_regex_matcher((*engine)->domainlist_matcher, fs, options, 0, dbio))) {
|
|
phishing_done(*engine);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
#define NDB_TOKENS 6
|
|
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)
|
|
{
|
|
const char *tokens[NDB_TOKENS];
|
|
char buffer[FILEBUFF];
|
|
const char *sig, *virname, *offset, *pt;
|
|
struct cli_matcher *root;
|
|
int line = 0, sigs = 0, ret = 0;
|
|
unsigned short target;
|
|
unsigned int phish = options & CL_DB_PHISHING;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if((ret = cli_initroots(*engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
|
|
if(!strncmp(buffer, "Exploit.JPEG.Comment", 20)) /* temporary */
|
|
continue;
|
|
|
|
if(!phish)
|
|
if(!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14))
|
|
continue;
|
|
|
|
cli_chomp(buffer);
|
|
|
|
cli_strtokenize(buffer, ':', NDB_TOKENS, tokens);
|
|
|
|
if(!(virname = tokens[0])) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
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))
|
|
continue;
|
|
|
|
if((*engine)->ignored && cli_chkign((*engine)->ignored, dbname, line, virname))
|
|
continue;
|
|
|
|
if((pt = tokens[4])) { /* min version */
|
|
if(!isdigit(*pt)) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if((unsigned int) atoi(pt) > cl_retflevel()) {
|
|
cli_dbgmsg("Signature for %s not loaded (required f-level: %d)\n", virname, atoi(pt));
|
|
continue;
|
|
}
|
|
|
|
|
|
if((pt = tokens[5])) { /* max version */
|
|
if(!isdigit(*pt)) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if((unsigned int) atoi(pt) < cl_retflevel()) {
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if(!(pt = tokens[1]) || !isdigit(*pt)) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
target = (unsigned short) atoi(pt);
|
|
|
|
if(target >= CLI_MTARGETS) {
|
|
cli_dbgmsg("Not supported target type in signature for %s\n", virname);
|
|
continue;
|
|
}
|
|
|
|
root = (*engine)->root[target];
|
|
|
|
if(!(offset = tokens[2])) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else if(!strcmp(offset, "*")) {
|
|
offset = NULL;
|
|
}
|
|
|
|
if(!(sig = tokens[3])) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, NULL, options))) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
sigs++;
|
|
}
|
|
|
|
if(!line) {
|
|
cli_errmsg("Empty database file\n");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Problem parsing database at line %d\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(signo)
|
|
*signo += sigs;
|
|
|
|
if(sdb && sigs && !(*engine)->sdb) {
|
|
(*engine)->sdb = 1;
|
|
cli_dbgmsg("*** Self protection mechanism activated.\n");
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
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[] = {
|
|
#define ATTRIB_TOKENS 2
|
|
{ "Target", CLI_TDB_UINT, (void **) &tdb->target },
|
|
{ "Engine", CLI_TDB_RANGE, (void **) &tdb->engine },
|
|
/*
|
|
{ "NoS", CLI_TDB_RANGE, (void **) &tdb->nos },
|
|
{ "EP", CLI_TDB_RANGE, (void **) &tdb->ep },
|
|
{ "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;
|
|
unsigned int v1, v2, v3, i, j;
|
|
uint32_t cnt, off[ATTRIB_TOKENS];
|
|
|
|
|
|
cli_strtokenize(attribs, ',', ATTRIB_TOKENS, (const char **) tokens);
|
|
|
|
for(i = 0; tokens[i]; i++) {
|
|
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]);
|
|
continue;
|
|
}
|
|
|
|
switch(apt->type) {
|
|
case CLI_TDB_UINT:
|
|
off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
|
|
tdb->val = (uint32_t *) cli_realloc2(tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
|
|
if(!tdb->val) {
|
|
tdb->cnt[CLI_TDB_UINT] = 0;
|
|
return -1;
|
|
}
|
|
tdb->val[cnt] = atoi(pt);
|
|
break;
|
|
|
|
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;
|
|
tdb->range = (uint32_t *) cli_realloc2(tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
|
|
if(!tdb->range) {
|
|
tdb->cnt[CLI_TDB_RANGE] = 0;
|
|
return -1;
|
|
}
|
|
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;
|
|
tdb->range = (uint32_t *) cli_realloc2(tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
|
|
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;
|
|
tdb->str = (char *) cli_realloc2(tdb->str, tdb->cnt[CLI_TDB_STR] * sizeof(char));
|
|
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;
|
|
}
|
|
|
|
for(i = 0; tokens[i]; i++) {
|
|
for(j = 0; attrtab[j].name; j++) {
|
|
if(!strcmp(attrtab[j].name, tokens[i])) {
|
|
apt = &attrtab[j];
|
|
break;
|
|
}
|
|
}
|
|
switch(apt->type) {
|
|
case CLI_TDB_UINT:
|
|
*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;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define FREE_TDB(x) \
|
|
if(x.cnt[CLI_TDB_UINT]) \
|
|
free(x.val); \
|
|
if(x.cnt[CLI_TDB_RANGE]) \
|
|
free(x.range); \
|
|
if(x.cnt[CLI_TDB_STR]) \
|
|
free(x.str);
|
|
|
|
#define LDB_TOKENS 67
|
|
static int cli_loadldb(FILE *fs, struct cl_engine **engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
|
|
{
|
|
char *tokens[LDB_TOKENS];
|
|
char buffer[32768], *pt;
|
|
const char *sig, *virname, *offset, *logic;
|
|
struct cli_matcher *root;
|
|
unsigned int line = 0, sigs = 0;
|
|
unsigned short target = 0;
|
|
struct cli_ac_lsig **newtable, *lsig;
|
|
uint32_t lsigid[2];
|
|
int ret = CL_SUCCESS, i, subsigs;
|
|
struct cli_lsig_tdb tdb;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if((ret = cli_initroots(*engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
sigs++;
|
|
cli_chomp(buffer);
|
|
|
|
cli_strtokenize(buffer, ';', LDB_TOKENS, (const char **) tokens);
|
|
|
|
if(!(virname = tokens[0])) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
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))
|
|
continue;
|
|
|
|
if((*engine)->ignored && cli_chkign((*engine)->ignored, dbname, line, virname))
|
|
continue;
|
|
|
|
if(!(logic = tokens[2])) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
subsigs = cli_ac_chklsig(logic, logic + strlen(logic), NULL, NULL, NULL, 1);
|
|
if(subsigs == -1) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
subsigs++;
|
|
|
|
if(subsigs > 64) {
|
|
cli_errmsg("cli_loadldb: Broken logical expression or too many subsignatures\n");
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
/* TDB */
|
|
memset(&tdb, 0, sizeof(tdb));
|
|
|
|
if(lsigattribs(tokens[1], &tdb) == -1) {
|
|
FREE_TDB(tdb);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
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);
|
|
sigs--;
|
|
continue;
|
|
} else if(tdb.engine[1] < cl_retflevel()) {
|
|
FREE_TDB(tdb);
|
|
sigs--;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(!tdb.target) {
|
|
cli_errmsg("cli_loadldb: No target specified in TDB\n");
|
|
FREE_TDB(tdb);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else if(tdb.target[0] >= CLI_MTARGETS) {
|
|
cli_dbgmsg("cli_loadldb: Not supported target type in logical signature for %s\n", virname);
|
|
FREE_TDB(tdb);
|
|
sigs--;
|
|
continue;
|
|
}
|
|
|
|
root = (*engine)->root[tdb.target[0]];
|
|
|
|
lsig = (struct cli_ac_lsig *) cli_calloc(1, sizeof(struct cli_ac_lsig));
|
|
if(!lsig) {
|
|
cli_errmsg("cli_loadldb: Can't allocate memory for lsig\n");
|
|
FREE_TDB(tdb);
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
lsig->logic = cli_strdup(logic);
|
|
if(!lsig->logic) {
|
|
cli_errmsg("cli_loadldb: Can't allocate memory for lsig->logic\n");
|
|
FREE_TDB(tdb);
|
|
ret = CL_EMEM;
|
|
free(lsig);
|
|
break;
|
|
}
|
|
|
|
lsigid[0] = lsig->id = root->ac_lsigs;
|
|
memcpy(&lsig->tdb, &tdb, sizeof(tdb));
|
|
|
|
root->ac_lsigs++;
|
|
newtable = (struct cli_ac_lsig **) cli_realloc(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);
|
|
free(lsig);
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
newtable[root->ac_lsigs - 1] = lsig;
|
|
root->ac_lsigtable = newtable;
|
|
|
|
for(i = 0; i < subsigs; i++) {
|
|
if(!tokens[3 + i]) {
|
|
cli_errmsg("cli_loadldb: Missing subsignature id %u\n", i);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
lsigid[1] = i;
|
|
sig = tokens[3 + i];
|
|
|
|
if((pt = strchr(tokens[3 + i], ':'))) {
|
|
*pt = 0;
|
|
sig = ++pt;
|
|
offset = tokens[3 + i];
|
|
if(!strcmp(offset, "*"))
|
|
offset = NULL;
|
|
} else {
|
|
offset = NULL;
|
|
sig = tokens[3 + i];
|
|
}
|
|
|
|
if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, lsigid, options))) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
}
|
|
if(ret)
|
|
break;
|
|
}
|
|
|
|
if(!line) {
|
|
cli_errmsg("Empty database file\n");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Problem parsing database at line %u\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(signo)
|
|
*signo += sigs;
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
#define FTM_TOKENS 8
|
|
static int cli_loadftm(FILE *fs, struct cl_engine **engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio)
|
|
{
|
|
const char *tokens[FTM_TOKENS], *pt;
|
|
char buffer[FILEBUFF];
|
|
unsigned int line = 0, sigs = 0;
|
|
struct cli_ftype *new;
|
|
cli_file_t rtype, type;
|
|
int ret;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if((ret = cli_initroots(*engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
while(1) {
|
|
if(internal) {
|
|
if(!ftypes_int[line])
|
|
break;
|
|
strncpy(buffer, ftypes_int[line], sizeof(buffer));
|
|
buffer[sizeof(buffer)-1]='\0';
|
|
} else {
|
|
if(!cli_dbgets(buffer, FILEBUFF, fs, dbio))
|
|
break;
|
|
cli_chomp(buffer);
|
|
}
|
|
line++;
|
|
cli_strtokenize(buffer, ':', FTM_TOKENS, tokens);
|
|
|
|
if(!tokens[0] || !tokens[1] || !tokens[2] || !tokens[3] || !tokens[4] || !tokens[5]) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if((pt = tokens[6])) { /* min version */
|
|
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;
|
|
}
|
|
if((pt = tokens[7])) { /* max version */
|
|
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) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if(atoi(tokens[0]) == 1) { /* A-C */
|
|
if((ret = cli_parse_add((*engine)->root[0], tokens[3], tokens[2], rtype, type, strcmp(tokens[1], "*") ? tokens[1] : NULL, 0, NULL, options)))
|
|
break;
|
|
|
|
} else if(atoi(tokens[0]) == 0) { /* memcmp() */
|
|
new = (struct cli_ftype *) cli_malloc(sizeof(struct cli_ftype));
|
|
if(!new) {
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
new->type = type;
|
|
new->offset = atoi(tokens[1]);
|
|
new->magic = (unsigned char *) cli_hex2str(tokens[2]);
|
|
if(!new->magic) {
|
|
cli_errmsg("cli_loadftm: Can't decode the hex string\n");
|
|
ret = CL_EMALFDB;
|
|
free(new);
|
|
break;
|
|
}
|
|
new->length = strlen(tokens[2]) / 2;
|
|
new->tname = cli_strdup(tokens[3]);
|
|
if(!new->tname) {
|
|
free(new->magic);
|
|
free(new);
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
new->next = (*engine)->ftypes;
|
|
(*engine)->ftypes = new;
|
|
|
|
} else {
|
|
cli_dbgmsg("cli_loadftm: Unsupported mode %u\n", atoi(tokens[0]));
|
|
continue;
|
|
}
|
|
sigs++;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Problem parsing %s filetype database at line %u\n", internal ? "built-in" : "external", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(!sigs) {
|
|
cli_errmsg("Empty %s filetype database\n", internal ? "built-in" : "external");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
cli_dbgmsg("Loaded %u filetype definitions\n", sigs);
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static int cli_loadign(FILE *fs, struct cl_engine **engine, unsigned int options, struct cli_dbio *dbio)
|
|
{
|
|
char buffer[FILEBUFF], *pt;
|
|
unsigned int line = 0;
|
|
struct cli_ignsig *new, *last = NULL;
|
|
struct cli_ignored *ignored;
|
|
int ret;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(!(ignored = (*engine)->ignored)) {
|
|
ignored = (*engine)->ignored = (struct cli_ignored *) cli_calloc(sizeof(struct cli_ignored), 1);
|
|
if(!ignored || hashset_init(&ignored->hs, 64, 50)) {
|
|
cl_free(*engine);
|
|
return CL_EMEM;
|
|
}
|
|
}
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
cli_chomp(buffer);
|
|
|
|
new = (struct cli_ignsig *) cli_calloc(1, sizeof(struct cli_ignsig));
|
|
if(!new) {
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
|
|
if(!(new->dbname = cli_strtok(buffer, 0, ":"))) {
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 1, ":"))) {
|
|
free(new->dbname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
new->line = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if((ret = hashset_addkey(&ignored->hs, new->line)))
|
|
break;
|
|
|
|
if(!(new->signame = cli_strtok(buffer, 2, ":"))) {
|
|
free(new->dbname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if(!last) {
|
|
last = ignored->list = new;
|
|
} else {
|
|
last->next = new;
|
|
last = new;
|
|
}
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("cli_loadign: Problem parsing database at line %u\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static void cli_freeign(struct cl_engine *engine)
|
|
{
|
|
struct cli_ignsig *pt;
|
|
struct cli_ignored *ignored;
|
|
|
|
if((ignored = engine->ignored)) {
|
|
while(ignored->list) {
|
|
pt = ignored->list;
|
|
ignored->list = ignored->list->next;
|
|
free(pt->dbname);
|
|
free(pt->signame);
|
|
free(pt);
|
|
}
|
|
hashset_destroy(&ignored->hs);
|
|
free(engine->ignored);
|
|
engine->ignored = NULL;
|
|
}
|
|
}
|
|
|
|
static int scomp(const void *a, const void *b)
|
|
{
|
|
return *(const uint32_t *)a - *(const uint32_t *)b;
|
|
}
|
|
|
|
#define MD5_HDB 0
|
|
#define MD5_MDB 1
|
|
#define MD5_FP 2
|
|
|
|
static int cli_md5db_init(struct cl_engine **engine, unsigned int mode)
|
|
{
|
|
struct cli_matcher *bm = NULL;
|
|
int ret;
|
|
|
|
|
|
if(mode == MD5_HDB) {
|
|
bm = (*engine)->md5_hdb = (struct cli_matcher *) cli_calloc(sizeof(struct cli_matcher), 1);
|
|
} else if(mode == MD5_MDB) {
|
|
bm = (*engine)->md5_mdb = (struct cli_matcher *) cli_calloc(sizeof(struct cli_matcher), 1);
|
|
} else {
|
|
bm = (*engine)->md5_fp = (struct cli_matcher *) cli_calloc(sizeof(struct cli_matcher), 1);
|
|
}
|
|
|
|
if(!bm)
|
|
return CL_EMEM;
|
|
|
|
if((ret = cli_bm_init(bm))) {
|
|
cli_errmsg("cli_md5db_init: Failed to initialize B-M\n");
|
|
return ret;
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
#define MD5_DB \
|
|
if(mode == MD5_HDB) \
|
|
db = (*engine)->md5_hdb; \
|
|
else if(mode == MD5_MDB) \
|
|
db = (*engine)->md5_mdb; \
|
|
else \
|
|
db = (*engine)->md5_fp;
|
|
|
|
#define MD5_TOKENS 3
|
|
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)
|
|
{
|
|
const char *tokens[MD5_TOKENS];
|
|
char buffer[FILEBUFF];
|
|
const char *pt;
|
|
int ret = CL_SUCCESS;
|
|
unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0;
|
|
uint32_t size;
|
|
struct cli_bm_patt *new;
|
|
struct cli_matcher *db = NULL;
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(mode == MD5_MDB) {
|
|
size_field = 0;
|
|
md5_field = 1;
|
|
}
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
cli_chomp(buffer);
|
|
cli_strtokenize(buffer, ':', MD5_TOKENS, tokens);
|
|
|
|
if(!(pt = tokens[2])) { /* virname */
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
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))
|
|
continue;
|
|
|
|
if((*engine)->ignored && cli_chkign((*engine)->ignored, dbname, line, pt))
|
|
continue;
|
|
|
|
|
|
new = (struct cli_bm_patt *) cli_calloc(1, sizeof(struct cli_bm_patt));
|
|
if(!new) {
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
|
|
if(!(pt = tokens[md5_field])) {
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
if(strlen(pt) != 32 || !(new->pattern = (unsigned char *) cli_hex2str(pt))) {
|
|
cli_errmsg("cli_loadmd5: Malformed MD5 string at line %u\n", line);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
new->length = 16;
|
|
|
|
if(!(pt = tokens[size_field])) {
|
|
free(new->pattern);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
size = atoi(pt);
|
|
|
|
if(!(new->virname = cli_virname((char *) tokens[2], options & CL_DB_OFFICIAL, 0))) {
|
|
free(new->pattern);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
|
|
MD5_DB;
|
|
if(!db && (ret = cli_md5db_init(engine, mode))) {
|
|
free(new->pattern);
|
|
free(new->virname);
|
|
free(new);
|
|
break;
|
|
} else {
|
|
MD5_DB;
|
|
}
|
|
|
|
if((ret = cli_bm_addpatt(db, new))) {
|
|
cli_errmsg("cli_loadmd5: Error adding BM pattern\n");
|
|
free(new->pattern);
|
|
free(new->virname);
|
|
free(new);
|
|
break;
|
|
}
|
|
|
|
if(mode == MD5_MDB) { /* section MD5 */
|
|
if(!db->md5_sizes_hs.capacity) {
|
|
hashset_init(&db->md5_sizes_hs, 32768, 80);
|
|
}
|
|
hashset_addkey(&db->md5_sizes_hs, size);
|
|
}
|
|
|
|
sigs++;
|
|
}
|
|
|
|
if(!line) {
|
|
cli_errmsg("cli_loadmd5: Empty database file\n");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("cli_loadmd5: Problem parsing database at line %u\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(signo)
|
|
*signo += sigs;
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
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)
|
|
{
|
|
char buffer[FILEBUFF], *pt;
|
|
unsigned int line = 0, sigs = 0;
|
|
int ret, crc;
|
|
struct cli_meta_node *new;
|
|
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
|
line++;
|
|
if(buffer[0] == '#')
|
|
continue;
|
|
|
|
cli_chomp(buffer);
|
|
|
|
new = (struct cli_meta_node *) cli_calloc(1, sizeof(struct cli_meta_node));
|
|
if(!new) {
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
|
|
if(!(new->virname = cli_virname(cli_strtok(buffer, 0, ":"), options & CL_DB_OFFICIAL, 1))) {
|
|
free(new);
|
|
ret = CL_EMEM;
|
|
break;
|
|
}
|
|
|
|
if((*engine)->ignored && cli_chkign((*engine)->ignored, dbname, line, new->virname)) {
|
|
free(new->virname);
|
|
free(new);
|
|
continue;
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 1, ":"))) {
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
new->encrypted = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(!(new->filename = cli_strtok(buffer, 2, ":"))) {
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(new->filename, "*")) {
|
|
free(new->filename);
|
|
new->filename = NULL;
|
|
}
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 3, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*"))
|
|
new->size = -1;
|
|
else
|
|
new->size = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 4, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*"))
|
|
new->csize = -1;
|
|
else
|
|
new->csize = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 5, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*")) {
|
|
new->crc32 = 0;
|
|
} else {
|
|
crc = cli_hex2num(pt);
|
|
if(crc == -1) {
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
}
|
|
new->crc32 = (unsigned int) crc;
|
|
}
|
|
free(pt);
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 6, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*"))
|
|
new->method = -1;
|
|
else
|
|
new->method = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 7, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*"))
|
|
new->fileno = 0;
|
|
else
|
|
new->fileno = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(!(pt = cli_strtok(buffer, 8, ":"))) {
|
|
free(new->filename);
|
|
free(new->virname);
|
|
free(new);
|
|
ret = CL_EMALFDB;
|
|
break;
|
|
} else {
|
|
if(!strcmp(pt, "*"))
|
|
new->maxdepth = 0;
|
|
else
|
|
new->maxdepth = atoi(pt);
|
|
free(pt);
|
|
}
|
|
|
|
if(type == 1) {
|
|
new->next = (*engine)->zip_mlist;
|
|
(*engine)->zip_mlist = new;
|
|
} else {
|
|
new->next = (*engine)->rar_mlist;
|
|
(*engine)->rar_mlist = new;
|
|
}
|
|
|
|
sigs++;
|
|
}
|
|
|
|
if(!line) {
|
|
cli_errmsg("Empty database file\n");
|
|
cl_free(*engine);
|
|
return CL_EMALFDB;
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Problem parsing database at line %d\n", line);
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
if(signo)
|
|
*signo += sigs;
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
static int cli_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo, unsigned int options);
|
|
|
|
int cli_load(const char *filename, struct cl_engine **engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
|
|
{
|
|
FILE *fs = NULL;
|
|
int ret = CL_SUCCESS;
|
|
uint8_t skipped = 0;
|
|
const char *dbname;
|
|
|
|
|
|
if(!dbio && (fs = fopen(filename, "rb")) == NULL) {
|
|
cli_errmsg("cli_load(): Can't open file %s\n", filename);
|
|
return CL_EOPEN;
|
|
}
|
|
|
|
/*
|
|
#ifdef C_WINDOWS
|
|
if((dbname = strrchr(filename, '\\')))
|
|
#else
|
|
*/
|
|
if((dbname = strrchr(filename, '/')))
|
|
/*#endif */
|
|
dbname++;
|
|
else
|
|
dbname = filename;
|
|
|
|
if(cli_strbcasestr(dbname, ".db")) {
|
|
ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".cvd")) {
|
|
int warn = 0;
|
|
|
|
if(!strcmp(dbname, "daily.cvd"))
|
|
warn = 1;
|
|
|
|
ret = cli_cvdload(fs, engine, signo, warn, options, 0);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".cld")) {
|
|
int warn = 0;
|
|
|
|
if(!strcmp(dbname, "daily.cld"))
|
|
warn = 1;
|
|
|
|
ret = cli_cvdload(fs, engine, signo, warn, options | CL_DB_CVDNOTMP, 1);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".hdb")) {
|
|
ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".hdu")) {
|
|
if(options & CL_DB_PUA)
|
|
ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
|
|
else
|
|
skipped = 1;
|
|
|
|
} else if(cli_strbcasestr(dbname, ".fp")) {
|
|
ret = cli_loadmd5(fs, engine, signo, MD5_FP, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".mdb")) {
|
|
ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".mdu")) {
|
|
if(options & CL_DB_PUA)
|
|
ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
|
|
else
|
|
skipped = 1;
|
|
|
|
} else if(cli_strbcasestr(dbname, ".ndb")) {
|
|
ret = cli_loadndb(fs, engine, signo, 0, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".ndu")) {
|
|
if(!(options & CL_DB_PUA))
|
|
skipped = 1;
|
|
else
|
|
ret = cli_loadndb(fs, engine, signo, 0, options | CL_DB_PUA_MODE, dbio, dbname);
|
|
|
|
} 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)
|
|
ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname);
|
|
else
|
|
skipped = 1;
|
|
|
|
} else if(cli_strbcasestr(dbname, ".sdb")) {
|
|
ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".zmd")) {
|
|
ret = cli_loadmd(fs, engine, signo, 1, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".rmd")) {
|
|
ret = cli_loadmd(fs, engine, signo, 2, options, dbio, dbname);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".cfg")) {
|
|
ret = cli_dconf_load(fs, engine, options, dbio);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".wdb")) {
|
|
if(options & CL_DB_PHISHING_URLS) {
|
|
ret = cli_loadwdb(fs, engine, options, dbio);
|
|
} else
|
|
skipped = 1;
|
|
} else if(cli_strbcasestr(dbname, ".pdb")) {
|
|
if(options & CL_DB_PHISHING_URLS) {
|
|
ret = cli_loadpdb(fs, engine, options, dbio);
|
|
} else
|
|
skipped = 1;
|
|
} else if(cli_strbcasestr(dbname, ".ftm")) {
|
|
ret = cli_loadftm(fs, engine, options, 0, dbio);
|
|
|
|
} else if(cli_strbcasestr(dbname, ".ign")) {
|
|
ret = cli_loadign(fs, engine, options, dbio);
|
|
|
|
} else {
|
|
cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
|
|
ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
|
|
}
|
|
|
|
if(ret) {
|
|
cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
|
|
} else {
|
|
if(skipped)
|
|
cli_dbgmsg("%s skipped\n", filename);
|
|
else
|
|
cli_dbgmsg("%s loaded\n", filename);
|
|
}
|
|
|
|
if(fs)
|
|
fclose(fs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int cl_loaddb(const char *filename, struct cl_engine **engine, unsigned int *signo) {
|
|
return cli_load(filename, engine, signo, CL_DB_STDOPT, NULL);
|
|
}
|
|
|
|
static int cli_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo, unsigned int options)
|
|
{
|
|
DIR *dd;
|
|
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
|
|
char *dbfile;
|
|
int ret = CL_ESUPPORT;
|
|
|
|
|
|
cli_dbgmsg("Loading databases from %s\n", dirname);
|
|
dbfile = (char *) cli_malloc(strlen(dirname) + 20);
|
|
if(!dbfile)
|
|
return CL_EMEM;
|
|
|
|
/* try to load local.ign and daily.cvd/daily.ign first */
|
|
sprintf(dbfile, "%s/local.ign", dirname);
|
|
if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
|
|
free(dbfile);
|
|
return ret;
|
|
}
|
|
|
|
sprintf(dbfile, "%s/daily.cld", dirname);
|
|
if(access(dbfile, R_OK))
|
|
sprintf(dbfile, "%s/daily.cvd", dirname);
|
|
if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
|
|
free(dbfile);
|
|
return ret;
|
|
}
|
|
|
|
sprintf(dbfile, "%s/daily.ign", dirname);
|
|
if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
|
|
free(dbfile);
|
|
return ret;
|
|
}
|
|
|
|
/* check for and load daily.cfg */
|
|
sprintf(dbfile, "%s/daily.cfg", dirname);
|
|
if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
|
|
free(dbfile);
|
|
return ret;
|
|
}
|
|
free(dbfile);
|
|
|
|
if((dd = opendir(dirname)) == NULL) {
|
|
cli_errmsg("cli_loaddbdir(): Can't open directory %s\n", dirname);
|
|
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 (!defined(C_INTERIX)) && (!defined(C_WINDOWS))
|
|
if(dent->d_ino)
|
|
#endif
|
|
{
|
|
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.ign") && strcmp(dent->d_name, "daily.cfg") && strcmp(dent->d_name, "local.ign") && CLI_DBEXT(dent->d_name)) {
|
|
|
|
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/%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);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dd);
|
|
if(ret == CL_ESUPPORT)
|
|
cli_errmsg("cli_loaddb(): No supported database files found in %s\n", dirname);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int cl_loaddbdir(const char *dirname, struct cl_engine **engine, unsigned int *signo) {
|
|
return cli_loaddbdir(dirname, engine, signo, CL_DB_STDOPT);
|
|
}
|
|
|
|
int cl_load(const char *path, struct cl_engine **engine, unsigned int *signo, unsigned int options)
|
|
{
|
|
struct stat sb;
|
|
int ret;
|
|
|
|
|
|
if(stat(path, &sb) == -1) {
|
|
cli_errmsg("cl_loaddbdir(): Can't get status of %s\n", path);
|
|
return CL_EIO;
|
|
}
|
|
|
|
if((ret = cli_initengine(engine, options))) {
|
|
cl_free(*engine);
|
|
return ret;
|
|
}
|
|
|
|
(*engine)->dboptions = options;
|
|
|
|
switch(sb.st_mode & S_IFMT) {
|
|
case S_IFREG:
|
|
ret = cli_load(path, engine, signo, options, NULL);
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
ret = cli_loaddbdir(path, engine, signo, options);
|
|
break;
|
|
|
|
default:
|
|
cli_errmsg("cl_load(%s): Not supported database file type\n", path);
|
|
return CL_EOPEN;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
const char *cl_retdbdir(void)
|
|
{
|
|
return DATADIR;
|
|
}
|
|
|
|
int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
|
|
{
|
|
DIR *dd;
|
|
const 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
|
|
char *fname;
|
|
|
|
|
|
if(dbstat) {
|
|
dbstat->entries = 0;
|
|
dbstat->stattab = NULL;
|
|
dbstat->statdname = NULL;
|
|
dbstat->dir = cli_strdup(dirname);
|
|
} 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);
|
|
cl_statfree(dbstat);
|
|
return CL_EOPEN;
|
|
}
|
|
|
|
cli_dbgmsg("Stat()ing files in %s\n", dirname);
|
|
|
|
#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 (!defined(C_INTERIX)) && (!defined(C_WINDOWS))
|
|
if(dent->d_ino)
|
|
#endif
|
|
{
|
|
if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
|
|
dbstat->entries++;
|
|
dbstat->stattab = (struct stat *) cli_realloc2(dbstat->stattab, dbstat->entries * sizeof(struct stat));
|
|
if(!dbstat->stattab) {
|
|
cl_statfree(dbstat);
|
|
closedir(dd);
|
|
return CL_EMEM;
|
|
}
|
|
|
|
#if defined(C_INTERIX) || defined(C_OS2)
|
|
dbstat->statdname = (char **) cli_realloc2(dbstat->statdname, dbstat->entries * sizeof(char *));
|
|
if(!dbstat->statdname) {
|
|
cl_statfree(dbstat);
|
|
closedir(dd);
|
|
return CL_EMEM;
|
|
}
|
|
#endif
|
|
|
|
fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 32);
|
|
if(!fname) {
|
|
cl_statfree(dbstat);
|
|
closedir(dd);
|
|
return CL_EMEM;
|
|
}
|
|
sprintf(fname, "%s/%s", dirname, dent->d_name);
|
|
#if defined(C_INTERIX) || defined(C_OS2)
|
|
dbstat->statdname[dbstat->entries - 1] = (char *) cli_malloc(strlen(dent->d_name) + 1);
|
|
if(!dbstat->statdname[dbstat->entries - 1]) {
|
|
cl_statfree(dbstat);
|
|
closedir(dd);
|
|
return CL_EMEM;
|
|
}
|
|
|
|
strcpy(dbstat->statdname[dbstat->entries - 1], dent->d_name);
|
|
#endif
|
|
stat(fname, &dbstat->stattab[dbstat->entries - 1]);
|
|
free(fname);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dd);
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
int cl_statchkdir(const struct cl_stat *dbstat)
|
|
{
|
|
DIR *dd;
|
|
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
|
|
struct stat sb;
|
|
unsigned int i, found;
|
|
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);
|
|
|
|
#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 (!defined(C_INTERIX)) && (!defined(C_WINDOWS))
|
|
if(dent->d_ino)
|
|
#endif
|
|
{
|
|
if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
|
|
fname = cli_malloc(strlen(dbstat->dir) + strlen(dent->d_name) + 32);
|
|
if(!fname) {
|
|
closedir(dd);
|
|
return CL_EMEM;
|
|
}
|
|
|
|
sprintf(fname, "%s/%s", dbstat->dir, dent->d_name);
|
|
stat(fname, &sb);
|
|
free(fname);
|
|
|
|
found = 0;
|
|
for(i = 0; i < dbstat->entries; i++)
|
|
#if defined(C_INTERIX) || defined(C_OS2)
|
|
if(!strcmp(dbstat->statdname[i], dent->d_name)) {
|
|
#else
|
|
if(dbstat->stattab[i].st_ino == sb.st_ino) {
|
|
#endif
|
|
found = 1;
|
|
if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
|
|
closedir(dd);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if(!found) {
|
|
closedir(dd);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dd);
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
int cl_statfree(struct cl_stat *dbstat)
|
|
{
|
|
|
|
if(dbstat) {
|
|
|
|
#if defined(C_INTERIX) || defined(C_OS2)
|
|
int i;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
if(dbstat->stattab) {
|
|
free(dbstat->stattab);
|
|
dbstat->stattab = NULL;
|
|
}
|
|
dbstat->entries = 0;
|
|
|
|
if(dbstat->dir) {
|
|
free(dbstat->dir);
|
|
dbstat->dir = NULL;
|
|
}
|
|
} else {
|
|
cli_errmsg("cl_statfree(): Null argument passed\n");
|
|
return CL_ENULLARG;
|
|
}
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
void cl_free(struct cl_engine *engine)
|
|
{
|
|
unsigned int i, j;
|
|
struct cli_meta_node *metapt, *metah;
|
|
struct cli_matcher *root;
|
|
|
|
|
|
if(!engine) {
|
|
cli_errmsg("cl_free: engine == NULL\n");
|
|
return;
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
pthread_mutex_lock(&cli_ref_mutex);
|
|
#endif
|
|
|
|
if(engine->refcount)
|
|
engine->refcount--;
|
|
|
|
if(engine->refcount) {
|
|
#ifdef CL_THREAD_SAFE
|
|
pthread_mutex_unlock(&cli_ref_mutex);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
pthread_mutex_unlock(&cli_ref_mutex);
|
|
#endif
|
|
|
|
if(engine->root) {
|
|
for(i = 0; i < CLI_MTARGETS; i++) {
|
|
if((root = engine->root[i])) {
|
|
if(!root->ac_only)
|
|
cli_bm_free(root);
|
|
cli_ac_free(root);
|
|
if(root->ac_lsigtable) {
|
|
for(j = 0; j < root->ac_lsigs; j++) {
|
|
free(root->ac_lsigtable[j]->logic);
|
|
FREE_TDB(root->ac_lsigtable[j]->tdb);
|
|
free(root->ac_lsigtable[j]);
|
|
}
|
|
free(root->ac_lsigtable);
|
|
}
|
|
free(root);
|
|
}
|
|
}
|
|
free(engine->root);
|
|
}
|
|
|
|
if((root = engine->md5_hdb)) {
|
|
cli_bm_free(root);
|
|
free(root);
|
|
}
|
|
|
|
if((root = engine->md5_mdb)) {
|
|
cli_bm_free(root);
|
|
free(root->soff);
|
|
if(root->md5_sizes_hs.capacity) {
|
|
hashset_destroy(&root->md5_sizes_hs);
|
|
}
|
|
free(root);
|
|
}
|
|
|
|
if((root = engine->md5_fp)) {
|
|
cli_bm_free(root);
|
|
free(root);
|
|
}
|
|
|
|
metapt = engine->zip_mlist;
|
|
while(metapt) {
|
|
metah = metapt;
|
|
metapt = metapt->next;
|
|
free(metah->virname);
|
|
if(metah->filename)
|
|
free(metah->filename);
|
|
free(metah);
|
|
}
|
|
|
|
metapt = engine->rar_mlist;
|
|
while(metapt) {
|
|
metah = metapt;
|
|
metapt = metapt->next;
|
|
free(metah->virname);
|
|
if(metah->filename)
|
|
free(metah->filename);
|
|
free(metah);
|
|
}
|
|
|
|
if(((struct cli_dconf *) engine->dconf)->phishing & PHISHING_CONF_ENGINE)
|
|
phishing_done(engine);
|
|
|
|
if(engine->dconf)
|
|
free(engine->dconf);
|
|
|
|
if(engine->pua_cats)
|
|
free(engine->pua_cats);
|
|
|
|
cli_ftfree(engine->ftypes);
|
|
cli_freeign(engine);
|
|
free(engine);
|
|
}
|
|
|
|
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*/
|
|
cli_dbgmsg("Converting hashset to array: %lu entries\n", root->md5_sizes_hs.count);
|
|
root->soff_len = hashset_toarray(&root->md5_sizes_hs, &root->soff);
|
|
hashset_destroy(&root->md5_sizes_hs);
|
|
qsort(root->soff, root->soff_len, sizeof(uint32_t), scomp);
|
|
}
|
|
}
|
|
|
|
|
|
int cl_build(struct cl_engine *engine)
|
|
{
|
|
unsigned int i;
|
|
int ret;
|
|
struct cli_matcher *root;
|
|
|
|
|
|
if(!engine)
|
|
return CL_ENULLARG;
|
|
|
|
if(!engine->ftypes)
|
|
if((ret = cli_loadftm(NULL, &engine, 0, 1, NULL)))
|
|
return ret;
|
|
|
|
for(i = 0; i < CLI_MTARGETS; i++) {
|
|
if((root = engine->root[i])) {
|
|
if((ret = cli_ac_buildtrie(root)))
|
|
return ret;
|
|
cli_dbgmsg("matcher[%u]: %s: AC sigs: %u BM sigs: %u %s\n", i, cli_mtargets[i].name, root->ac_patterns, root->bm_patterns, root->ac_only ? "(ac_only mode)" : "");
|
|
}
|
|
}
|
|
|
|
if((ret = cli_build_regex_list(engine->whitelist_matcher))) {
|
|
return ret;
|
|
}
|
|
if((ret = cli_build_regex_list(engine->domainlist_matcher))) {
|
|
return ret;
|
|
}
|
|
cli_md5db_build(engine->md5_mdb);
|
|
cli_freeign(engine);
|
|
cli_dconf_print(engine->dconf);
|
|
|
|
return CL_SUCCESS;
|
|
}
|
|
|
|
struct cl_engine *cl_dup(struct cl_engine *engine)
|
|
{
|
|
if(!engine) {
|
|
cli_errmsg("cl_dup: engine == NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
pthread_mutex_lock(&cli_ref_mutex);
|
|
#endif
|
|
|
|
engine->refcount++;
|
|
|
|
#ifdef CL_THREAD_SAFE
|
|
pthread_mutex_unlock(&cli_ref_mutex);
|
|
#endif
|
|
|
|
return engine;
|
|
}
|
|
|