libclamav/others.c
e3aaff8e
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
24555841
  *
2023340a
  *  Authors: Tomasz Kojm, Trog
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 <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
288057e9
 #ifdef HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
a946fc1c
 #endif
e3aaff8e
 #include <sys/types.h>
 #include <sys/stat.h>
081f6473
 #include <dirent.h>
288057e9
 #ifndef _WIN32
e3aaff8e
 #include <sys/wait.h>
 #include <sys/time.h>
a946fc1c
 #endif
e3aaff8e
 #include <time.h>
 #include <fcntl.h>
081f6473
 #ifdef HAVE_PWD_H
e3aaff8e
 #include <pwd.h>
a946fc1c
 #endif
e3aaff8e
 #include <errno.h>
a946fc1c
 #include "target.h"
288057e9
 #ifdef HAVE_SYS_PARAM_H
15edd45f
 #include <sys/param.h>
a946fc1c
 #endif
288057e9
 #ifdef HAVE_MALLOC_H
a946fc1c
 #include <malloc.h>
 #endif
e3aaff8e
 
f2571e34
 #ifdef CL_THREAD_SAFE
 #include <pthread.h>
 #endif
 
e731850d
 #ifdef HAVE_LIBXML2
 #include <libxml/parser.h>
 #endif
 
e3aaff8e
 #include "clamav.h"
 #include "others.h"
92f585fb
 #include "regex/regex.h"
f5a4018b
 #include "ltdl.h"
ab0d2f05
 #include "matcher-ac.h"
a6d2b523
 #include "matcher-pcre.h"
589d8d8e
 #include "default.h"
435aabeb
 #include "scanners.h"
29227461
 #include "bytecode.h"
3a5b31ab
 #include "bytecode_api_impl.h"
239cdec6
 #include "cache.h"
b289385d
 #include "readdb.h"
f2571e34
 #include "stats.h"
e3aaff8e
 
01eebc13
 cl_unrar_error_t (*cli_unrar_open)(const char *filename, void **hArchive, char **comment, uint32_t *comment_size, uint8_t debug_flag);
 cl_unrar_error_t (*cli_unrar_peek_file_header)(void *hArchive, unrar_metadata_t *file_metadata);
288057e9
 cl_unrar_error_t (*cli_unrar_extract_file)(void *hArchive, const char *destPath, char *outputBuffer);
01eebc13
 cl_unrar_error_t (*cli_unrar_skip_file)(void *hArchive);
 void (*cli_unrar_close)(void *hArchive);
 
288057e9
 int have_rar             = 0;
7cd9337a
 static int is_rar_inited = 0;
f5a4018b
 
cd0a934f
 static int warn_dlerror(const char *msg)
 {
     const char *err = lt_dlerror();
     if (err)
288057e9
         cli_warnmsg("%s: %s\n", msg, err);
cd0a934f
     else
288057e9
         cli_warnmsg("%s\n", err);
cd0a934f
     return 0;
 }
f5a4018b
 
00c3dfe5
 #if 0
cd0a934f
 #define lt_preload_symbols lt_libclamav_LTX_preloaded_symbols
 extern const lt_dlsymlist lt_preload_symbols[];
00c3dfe5
 #endif
cd0a934f
 
288057e9
 static int lt_init(void)
 {
00c3dfe5
 #if 0
     /* doesn't work yet */
cd0a934f
     if (lt_dlpreload_default(lt_preload_symbols)) {
         warn_dlerror("Cannot init ltdl preloaded symbols");
 	/* not fatal */
     }
00c3dfe5
 #endif
288057e9
     if (lt_dlinit()) {
cd0a934f
         warn_dlerror("Cannot init ltdl - unrar support unavailable");
         return -1;
     }
     return 0;
 }
 
288057e9
 #define PASTE2(a, b) a #b
 #define PASTE(a, b) PASTE2(a, b)
0b44bb9f
 
cd0a934f
 static lt_dlhandle lt_dlfind(const char *name, const char *featurename)
 {
     static const char *suffixes[] = {
288057e9
         LT_MODULE_EXT "." LIBCLAMAV_FULLVER,
         PASTE(LT_MODULE_EXT ".", LIBCLAMAV_MAJORVER),
         LT_MODULE_EXT,
         "." LT_LIBEXT};
cd0a934f
 
     const char *searchpath;
     const lt_dlinfo *info;
     char modulename[128];
     lt_dlhandle rhandle;
     unsigned i;
 
     if (lt_dladdsearchdir(SEARCH_LIBDIR)) {
288057e9
         cli_dbgmsg("lt_dladdsearchdir failed for %s\n", SEARCH_LIBDIR);
f5a4018b
     }
cd0a934f
 
     searchpath = lt_dlgetsearchpath();
     if (!searchpath)
288057e9
         searchpath = "";
cd0a934f
 
     cli_dbgmsg("searching for %s, user-searchpath: %s\n", featurename, searchpath);
288057e9
     for (i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); i++) {
         snprintf(modulename, sizeof(modulename), "%s%s", name, suffixes[i]);
         rhandle = lt_dlopen(modulename);
         if (rhandle)
             break;
         cli_dbgmsg("searching for %s: %s not found\n", featurename, modulename);
cd0a934f
     }
 
33d52089
     if (!rhandle) {
288057e9
         const char *err = lt_dlerror();
         if (!err) err = "";
33d52089
 #ifdef WARN_DLOPEN_FAIL
cd0a934f
         cli_warnmsg("Cannot dlopen %s: %s - %s support unavailable\n", name, err, featurename);
33d52089
 #else
cd0a934f
         cli_dbgmsg("Cannot dlopen %s: %s - %s support unavailable\n", name, err, featurename);
33d52089
 #endif
cd0a934f
         return rhandle;
f5a4018b
     }
cd0a934f
 
     info = lt_dlgetinfo(rhandle);
     if (info)
288057e9
         cli_dbgmsg("%s support loaded from %s %s\n", featurename, info->filename ? info->filename : "?", info->name ? info->name : "");
cd0a934f
     return rhandle;
 }
 
288057e9
 static void cli_rarload(void)
 {
cd0a934f
     lt_dlhandle rhandle;
 
288057e9
     if (is_rar_inited) return;
7cd9337a
     is_rar_inited = 1;
cd0a934f
 
288057e9
     if (have_rar) return;
3a7a213c
 
cd0a934f
     rhandle = lt_dlfind("libclamunrar_iface", "unrar");
     if (!rhandle)
288057e9
         return;
cd0a934f
 
01eebc13
     if (!(cli_unrar_open = (cl_unrar_error_t(*)(const char *, void **, char **, uint32_t *, uint8_t))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_open")) ||
288057e9
         !(cli_unrar_peek_file_header = (cl_unrar_error_t(*)(void *, unrar_metadata_t *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_peek_file_header")) ||
         !(cli_unrar_extract_file = (cl_unrar_error_t(*)(void *, const char *, char *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_extract_file")) ||
         !(cli_unrar_skip_file = (cl_unrar_error_t(*)(void *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_skip_file")) ||
         !(cli_unrar_close = (void (*)(void *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_close"))) {
         /* ideally we should never land here, we'd better warn so */
f5a4018b
         cli_warnmsg("Cannot resolve: %s (version mismatch?) - unrar support unavailable\n", lt_dlerror());
         return;
     }
     have_rar = 1;
 }
a7ac5978
 
d4d14218
 void cl_debug(void)
 {
     cli_debug_flag = 1;
e3aaff8e
 }
 
3ca11170
 void cl_always_gen_section_hash(void)
 {
     cli_always_gen_section_hash = 1;
 }
 
b5134815
 unsigned int cl_retflevel(void)
a3ee0766
 {
     return CL_FLEVEL;
 }
 
68a6f51f
 const char *cl_strerror(int clerror)
e3aaff8e
 {
288057e9
     switch (clerror) {
         /* libclamav specific codes */
         case CL_CLEAN:
             return "No viruses detected";
         case CL_VIRUS:
             return "Virus(es) detected";
         case CL_ENULLARG:
             return "Null argument passed to function";
         case CL_EARG:
             return "Invalid argument passed to function";
         case CL_EMALFDB:
             return "Malformed database";
         case CL_ECVD:
             return "Broken or not a CVD file";
         case CL_EVERIFY:
             return "Can't verify database integrity";
         case CL_EUNPACK:
             return "Can't unpack some data";
         case CL_EPARSE: /* like CL_EFORMAT but reported outside magicscan() */
             return "Can't parse data";
 
         /* I/O and memory errors */
         case CL_EOPEN:
             return "Can't open file or directory";
         case CL_ECREAT:
             return "Can't create new file";
         case CL_EUNLINK:
             return "Can't unlink file";
         case CL_ESTAT:
             return "Can't get file status";
         case CL_EREAD:
             return "Can't read file";
         case CL_ESEEK:
             return "Can't set file offset";
         case CL_EWRITE:
             return "Can't write to file";
         case CL_EDUP:
             return "Can't duplicate file descriptor";
         case CL_EACCES:
             return "Can't access file";
         case CL_ETMPFILE:
             return "Can't create temporary file";
         case CL_ETMPDIR:
             return "Can't create temporary directory";
         case CL_EMAP:
             return "Can't map file into memory";
         case CL_EMEM:
             return "Can't allocate memory";
         case CL_ETIMEOUT:
b056e8e4
             return "CL_ETIMEOUT: Time limit reached";
288057e9
         /* internal (needed for debug messages) */
         case CL_EMAXREC:
             return "CL_EMAXREC";
         case CL_EMAXSIZE:
             return "CL_EMAXSIZE";
         case CL_EMAXFILES:
             return "CL_EMAXFILES";
         case CL_EFORMAT:
             return "CL_EFORMAT: Bad format or broken data";
         case CL_EBYTECODE:
             return "Error during bytecode execution";
         case CL_EBYTECODE_TESTFAIL:
             return "Failure in bytecode testmode";
         case CL_ELOCK:
             return "Mutex lock failed";
         case CL_EBUSY:
             return "Scanner still active";
         case CL_ESTATE:
             return "Bad state (engine not initialized, or already initialized)";
1d7f6b27
         case CL_VERIFIED:
             return "The scanned object was verified and deemed trusted";
288057e9
         default:
             return "Unknown error code";
e3aaff8e
     }
 }
 
2accc66f
 int cl_init(unsigned int initoptions)
724b2bf7
 {
288057e9
     int rc;
     struct timeval tv;
     unsigned int pid = (unsigned int)getpid();
53e8cd6f
 
cd94be7a
     UNUSEDPARAM(initoptions);
 
78ee2250
     cl_initialize_crypto();
 
724b2bf7
     /* put dlopen() stuff here, etc. */
cd0a934f
     if (lt_init() == 0) {
288057e9
         cli_rarload();
cd0a934f
     }
288057e9
     gettimeofday(&tv, (struct timezone *)0);
     srand(pid + tv.tv_usec * (pid + 1) + clock());
d1487222
     rc = bytecode_init();
     if (rc)
288057e9
         return rc;
5a43ac17
 #ifdef HAVE_LIBXML2
     xmlInitParser();
 #endif
724b2bf7
     return CL_SUCCESS;
 }
 
b8fe70b3
 struct cl_engine *cl_engine_new(void)
724b2bf7
 {
d03c18be
     struct cl_engine *new;
f2571e34
     cli_intel_t *intel;
724b2bf7
 
288057e9
     new = (struct cl_engine *)cli_calloc(1, sizeof(struct cl_engine));
     if (!new) {
         cli_errmsg("cl_engine_new: Can't allocate memory for cl_engine\n");
         return NULL;
724b2bf7
     }
 
     /* Setup default limits */
b056e8e4
     new->maxscantime   = CLI_DEFAULT_TIMELIMIT;
288057e9
     new->maxscansize   = CLI_DEFAULT_MAXSCANSIZE;
     new->maxfilesize   = CLI_DEFAULT_MAXFILESIZE;
     new->maxreclevel   = CLI_DEFAULT_MAXRECLEVEL;
     new->maxfiles      = CLI_DEFAULT_MAXFILES;
     new->min_cc_count  = CLI_DEFAULT_MIN_CC_COUNT;
589d8d8e
     new->min_ssn_count = CLI_DEFAULT_MIN_SSN_COUNT;
b2726a53
     /* Engine Max sizes */
288057e9
     new->maxembeddedpe      = CLI_DEFAULT_MAXEMBEDDEDPE;
     new->maxhtmlnormalize   = CLI_DEFAULT_MAXHTMLNORMALIZE;
     new->maxhtmlnotags      = CLI_DEFAULT_MAXHTMLNOTAGS;
b2726a53
     new->maxscriptnormalize = CLI_DEFAULT_MAXSCRIPTNORMALIZE;
288057e9
     new->maxziptypercg      = CLI_DEFAULT_MAXZIPTYPERCG;
724b2bf7
 
be43f951
     new->bytecode_security = CL_BYTECODE_TRUST_SIGNED;
b63681a5
     /* 5 seconds timeout */
12876d3c
     new->bytecode_timeout = 60000;
288057e9
     new->bytecode_mode    = CL_BYTECODE_MODE_AUTO;
     new->refcount         = 1;
     new->ac_only          = 0;
     new->ac_mindepth      = CLI_DEFAULT_AC_MINDEPTH;
     new->ac_maxdepth      = CLI_DEFAULT_AC_MAXDEPTH;
724b2bf7
 
 #ifdef USE_MPOOL
288057e9
     if (!(new->mempool = mpool_create())) {
         cli_errmsg("cl_engine_new: Can't allocate memory for memory pool\n");
         free(new);
         return NULL;
724b2bf7
     }
 #endif
 
544fa973
     new->root = MPOOL_CALLOC(new->mempool, CLI_MTARGETS, sizeof(struct cli_matcher *));
288057e9
     if (!new->root) {
         cli_errmsg("cl_engine_new: Can't allocate memory for roots\n");
724b2bf7
 #ifdef USE_MPOOL
288057e9
         mpool_destroy(new->mempool);
724b2bf7
 #endif
288057e9
         free(new);
         return NULL;
724b2bf7
     }
 
47d40feb
     new->dconf = cli_mpool_dconf_init(new->mempool);
288057e9
     if (!new->dconf) {
         cli_errmsg("cl_engine_new: Can't initialize dynamic configuration\n");
544fa973
         MPOOL_FREE(new->mempool, new->root);
724b2bf7
 #ifdef USE_MPOOL
288057e9
         mpool_destroy(new->mempool);
724b2bf7
 #endif
288057e9
         free(new);
         return NULL;
724b2bf7
     }
 
544fa973
     new->pwdbs = MPOOL_CALLOC(new->mempool, CLI_PWDB_COUNT, sizeof(struct cli_pwdb *));
038cb67a
     if (!new->pwdbs) {
288057e9
         cli_errmsg("cl_engine_new: Can't initialize password databases\n");
544fa973
         MPOOL_FREE(new->mempool, new->dconf);
         MPOOL_FREE(new->mempool, new->root);
038cb67a
 #ifdef USE_MPOOL
288057e9
         mpool_destroy(new->mempool);
038cb67a
 #endif
288057e9
         free(new);
         return NULL;
038cb67a
     }
 
56b4f4b0
     crtmgr_init(&(new->cmgr));
a133cd8e
     if (crtmgr_add_roots(new, &(new->cmgr), 0)) {
288057e9
         cli_errmsg("cl_engine_new: Can't initialize root certificates\n");
544fa973
         MPOOL_FREE(new->mempool, new->pwdbs);
         MPOOL_FREE(new->mempool, new->dconf);
         MPOOL_FREE(new->mempool, new->root);
2501f747
 #ifdef USE_MPOOL
288057e9
         mpool_destroy(new->mempool);
2501f747
 #endif
288057e9
         free(new);
         return NULL;
2501f747
     }
8997495d
 
f2571e34
     /* Set up default stats/intel gathering callbacks */
     intel = cli_calloc(1, sizeof(cli_intel_t));
e198df77
     if ((intel)) {
f2571e34
 #ifdef CL_THREAD_SAFE
e198df77
         if (pthread_mutex_init(&(intel->mutex), NULL)) {
             cli_errmsg("cli_engine_new: Cannot initialize stats gathering mutex\n");
544fa973
             MPOOL_FREE(new->mempool, new->pwdbs);
             MPOOL_FREE(new->mempool, new->dconf);
             MPOOL_FREE(new->mempool, new->root);
f2571e34
 #ifdef USE_MPOOL
e198df77
             mpool_destroy(new->mempool);
f2571e34
 #endif
e198df77
             free(new);
             free(intel);
             return NULL;
         }
f2571e34
 #endif
288057e9
         intel->engine     = new;
e198df77
         intel->maxsamples = STATS_MAX_SAMPLES;
288057e9
         intel->maxmem     = STATS_MAX_MEM;
         intel->timeout    = 10;
         new->stats_data   = intel;
e198df77
     } else {
         new->stats_data = NULL;
     }
 
288057e9
     new->cb_stats_add_sample      = NULL;
     new->cb_stats_submit          = NULL;
     new->cb_stats_flush           = clamav_stats_flush;
     new->cb_stats_remove_sample   = clamav_stats_remove_sample;
4473a0a9
     new->cb_stats_decrement_count = clamav_stats_decrement_count;
288057e9
     new->cb_stats_get_num         = clamav_stats_get_num;
     new->cb_stats_get_size        = clamav_stats_get_size;
     new->cb_stats_get_hostid      = clamav_stats_get_hostid;
f2571e34
 
067bce5f
     /* Setup raw disk image max settings */
     new->maxpartitions = CLI_DEFAULT_MAXPARTITIONS;
 
     /* Engine max settings */
     new->maxiconspe = CLI_DEFAULT_MAXICONSPE;
731c8e62
     new->maxrechwp3 = CLI_DEFAULT_MAXRECHWP3;
97fbb02b
 
9bc7c138
     /* PCRE matching limitations */
7af020b2
 #if HAVE_PCRE
effb4f51
     cli_pcre_init();
7af020b2
 #endif
288057e9
     new->pcre_match_limit    = CLI_DEFAULT_PCRE_MATCH_LIMIT;
9bc7c138
     new->pcre_recmatch_limit = CLI_DEFAULT_PCRE_RECMATCH_LIMIT;
288057e9
     new->pcre_max_filesize   = CLI_DEFAULT_PCRE_MAX_FILESIZE;
9bc7c138
 
baeb6253
 #ifdef HAVE_YARA
 
ac0cbde8
     /* YARA */
b6ad4322
     if (cli_yara_init(new) != CL_SUCCESS) {
5842265f
         cli_errmsg("cli_engine_new: failed to initialize YARA\n");
544fa973
         MPOOL_FREE(new->mempool, new->pwdbs);
         MPOOL_FREE(new->mempool, new->dconf);
         MPOOL_FREE(new->mempool, new->root);
d03c18be
 #ifdef USE_MPOOL
         mpool_destroy(new->mempool);
 #endif
         free(new);
         free(intel);
         return NULL;
     }
 
baeb6253
 #endif
 
724b2bf7
     cli_dbgmsg("Initialized %s engine\n", cl_retver());
     return new;
 }
 
2accc66f
 int cl_engine_set_num(struct cl_engine *engine, enum cl_engine_field field, long long num)
c45c4c3d
 {
288057e9
     if (!engine)
         return CL_ENULLARG;
c45c4c3d
 
2accc66f
     /* TODO: consider adding checks and warn/errs when num overflows the
      * destination type
      */
288057e9
     switch (field) {
         case CL_ENGINE_MAX_SCANSIZE:
             engine->maxscansize = num;
             break;
         case CL_ENGINE_MAX_FILESIZE:
             engine->maxfilesize = num;
             break;
         case CL_ENGINE_MAX_RECURSION:
             if (!num) {
                 cli_warnmsg("MaxRecursion: the value of 0 is not allowed, using default: %u\n", CLI_DEFAULT_MAXRECLEVEL);
                 engine->maxreclevel = CLI_DEFAULT_MAXRECLEVEL;
             } else
                 engine->maxreclevel = num;
             break;
         case CL_ENGINE_MAX_FILES:
             engine->maxfiles = num;
             break;
         case CL_ENGINE_MAX_EMBEDDEDPE:
             if (num < 0) {
                 cli_warnmsg("MaxEmbeddedPE: negative values are not allowed, using default: %u\n", CLI_DEFAULT_MAXEMBEDDEDPE);
                 engine->maxembeddedpe = CLI_DEFAULT_MAXEMBEDDEDPE;
             } else
                 engine->maxembeddedpe = num;
             break;
         case CL_ENGINE_MAX_HTMLNORMALIZE:
             if (num < 0) {
                 cli_warnmsg("MaxHTMLNormalize: negative values are not allowed, using default: %u\n", CLI_DEFAULT_MAXHTMLNORMALIZE);
                 engine->maxhtmlnormalize = CLI_DEFAULT_MAXHTMLNORMALIZE;
             } else
                 engine->maxhtmlnormalize = num;
             break;
         case CL_ENGINE_MAX_HTMLNOTAGS:
             if (num < 0) {
                 cli_warnmsg("MaxHTMLNoTags: negative values are not allowed, using default: %u\n", CLI_DEFAULT_MAXHTMLNOTAGS);
                 engine->maxhtmlnotags = CLI_DEFAULT_MAXHTMLNOTAGS;
             } else
                 engine->maxhtmlnotags = num;
             break;
         case CL_ENGINE_MAX_SCRIPTNORMALIZE:
             if (num < 0) {
                 cli_warnmsg("MaxScriptNormalize: negative values are not allowed, using default: %u\n", CLI_DEFAULT_MAXSCRIPTNORMALIZE);
                 engine->maxscriptnormalize = CLI_DEFAULT_MAXSCRIPTNORMALIZE;
             } else
                 engine->maxscriptnormalize = num;
             break;
         case CL_ENGINE_MAX_ZIPTYPERCG:
             if (num < 0) {
                 cli_warnmsg("MaxZipTypeRcg: negative values are not allowed, using default: %u\n", CLI_DEFAULT_MAXZIPTYPERCG);
                 engine->maxziptypercg = CLI_DEFAULT_MAXZIPTYPERCG;
             } else
                 engine->maxziptypercg = num;
             break;
         case CL_ENGINE_MIN_CC_COUNT:
             engine->min_cc_count = num;
             break;
         case CL_ENGINE_MIN_SSN_COUNT:
             engine->min_ssn_count = num;
             break;
         case CL_ENGINE_DB_OPTIONS:
         case CL_ENGINE_DB_VERSION:
         case CL_ENGINE_DB_TIME:
             cli_warnmsg("cl_engine_set_num: The field is read only\n");
             return CL_EARG;
         case CL_ENGINE_AC_ONLY:
             engine->ac_only = num;
             break;
         case CL_ENGINE_AC_MINDEPTH:
             engine->ac_mindepth = num;
             break;
         case CL_ENGINE_AC_MAXDEPTH:
             engine->ac_maxdepth = num;
             break;
         case CL_ENGINE_KEEPTMP:
             engine->keeptmp = num;
             break;
         case CL_ENGINE_FORCETODISK:
             if (num)
                 engine->engine_options |= ENGINE_OPTIONS_FORCE_TO_DISK;
             else
                 engine->engine_options &= ~(ENGINE_OPTIONS_FORCE_TO_DISK);
             break;
         case CL_ENGINE_BYTECODE_SECURITY:
             if (engine->dboptions & CL_DB_COMPILED) {
                 cli_errmsg("cl_engine_set_num: CL_ENGINE_BYTECODE_SECURITY cannot be set after engine was compiled\n");
                 return CL_EARG;
             }
             engine->bytecode_security = num;
             break;
         case CL_ENGINE_BYTECODE_TIMEOUT:
             engine->bytecode_timeout = num;
             break;
         case CL_ENGINE_BYTECODE_MODE:
             if (engine->dboptions & CL_DB_COMPILED) {
                 cli_errmsg("cl_engine_set_num: CL_ENGINE_BYTECODE_MODE cannot be set after engine was compiled\n");
                 return CL_EARG;
             }
             if (num == CL_BYTECODE_MODE_OFF) {
                 cli_errmsg("cl_engine_set_num: CL_BYTECODE_MODE_OFF is not settable, use dboptions to turn off!\n");
                 return CL_EARG;
             }
             engine->bytecode_mode = num;
             if (num == CL_BYTECODE_MODE_TEST)
                 cli_infomsg(NULL, "bytecode engine in test mode\n");
             break;
         case CL_ENGINE_DISABLE_CACHE:
             if (num) {
                 engine->engine_options |= ENGINE_OPTIONS_DISABLE_CACHE;
             } else {
                 engine->engine_options &= ~(ENGINE_OPTIONS_DISABLE_CACHE);
                 if (!(engine->cache))
                     cli_cache_init(engine);
             }
             break;
         case CL_ENGINE_DISABLE_PE_STATS:
             if (num) {
                 engine->engine_options |= ENGINE_OPTIONS_DISABLE_PE_STATS;
             } else {
                 engine->engine_options &= ~(ENGINE_OPTIONS_DISABLE_PE_STATS);
             }
             break;
         case CL_ENGINE_STATS_TIMEOUT:
             if ((engine->stats_data)) {
                 cli_intel_t *intel = (cli_intel_t *)(engine->stats_data);
 
                 intel->timeout = (uint32_t)num;
             }
             break;
         case CL_ENGINE_MAX_PARTITIONS:
             engine->maxpartitions = (uint32_t)num;
             break;
         case CL_ENGINE_MAX_ICONSPE:
             engine->maxiconspe = (uint32_t)num;
             break;
         case CL_ENGINE_MAX_RECHWP3:
             engine->maxrechwp3 = (uint32_t)num;
             break;
b056e8e4
         case CL_ENGINE_MAX_SCANTIME:
             engine->maxscantime = (uint32_t)num;
288057e9
             break;
         case CL_ENGINE_PCRE_MATCH_LIMIT:
             engine->pcre_match_limit = (uint64_t)num;
             break;
         case CL_ENGINE_PCRE_RECMATCH_LIMIT:
             engine->pcre_recmatch_limit = (uint64_t)num;
             break;
         case CL_ENGINE_PCRE_MAX_FILESIZE:
             engine->pcre_max_filesize = (uint64_t)num;
             break;
         case CL_ENGINE_DISABLE_PE_CERTS:
             if (num) {
                 engine->engine_options |= ENGINE_OPTIONS_DISABLE_PE_CERTS;
             } else {
                 engine->engine_options &= ~(ENGINE_OPTIONS_DISABLE_PE_CERTS);
             }
             break;
         case CL_ENGINE_PE_DUMPCERTS:
             if (num) {
                 engine->engine_options |= ENGINE_OPTIONS_PE_DUMPCERTS;
             } else {
                 engine->engine_options &= ~(ENGINE_OPTIONS_PE_DUMPCERTS);
             }
             break;
         default:
             cli_errmsg("cl_engine_set_num: Incorrect field number\n");
             return CL_EARG;
c45c4c3d
     }
 
     return CL_SUCCESS;
 }
 
2accc66f
 long long cl_engine_get_num(const struct cl_engine *engine, enum cl_engine_field field, int *err)
c45c4c3d
 {
288057e9
     if (!engine) {
         cli_errmsg("cl_engine_get_num: engine == NULL\n");
         if (err)
             *err = CL_ENULLARG;
         return -1;
2accc66f
     }
 
288057e9
     if (err)
         *err = CL_SUCCESS;
 
     switch (field) {
         case CL_ENGINE_DB_OPTIONS:
             return engine->dboptions;
         case CL_ENGINE_MAX_SCANSIZE:
             return engine->maxscansize;
         case CL_ENGINE_MAX_FILESIZE:
             return engine->maxfilesize;
         case CL_ENGINE_MAX_RECURSION:
             return engine->maxreclevel;
         case CL_ENGINE_MAX_FILES:
             return engine->maxfiles;
         case CL_ENGINE_MAX_EMBEDDEDPE:
             return engine->maxembeddedpe;
         case CL_ENGINE_MAX_HTMLNORMALIZE:
             return engine->maxhtmlnormalize;
         case CL_ENGINE_MAX_HTMLNOTAGS:
             return engine->maxhtmlnotags;
         case CL_ENGINE_MAX_SCRIPTNORMALIZE:
             return engine->maxscriptnormalize;
         case CL_ENGINE_MAX_ZIPTYPERCG:
             return engine->maxziptypercg;
         case CL_ENGINE_MIN_CC_COUNT:
             return engine->min_cc_count;
         case CL_ENGINE_MIN_SSN_COUNT:
             return engine->min_ssn_count;
         case CL_ENGINE_DB_VERSION:
             return engine->dbversion[0];
         case CL_ENGINE_DB_TIME:
             return engine->dbversion[1];
         case CL_ENGINE_AC_ONLY:
             return engine->ac_only;
         case CL_ENGINE_AC_MINDEPTH:
             return engine->ac_mindepth;
         case CL_ENGINE_AC_MAXDEPTH:
             return engine->ac_maxdepth;
         case CL_ENGINE_KEEPTMP:
             return engine->keeptmp;
         case CL_ENGINE_FORCETODISK:
             return engine->engine_options & ENGINE_OPTIONS_FORCE_TO_DISK;
         case CL_ENGINE_BYTECODE_SECURITY:
             return engine->bytecode_security;
         case CL_ENGINE_BYTECODE_TIMEOUT:
             return engine->bytecode_timeout;
         case CL_ENGINE_BYTECODE_MODE:
             return engine->bytecode_mode;
         case CL_ENGINE_DISABLE_CACHE:
             return engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE;
         case CL_ENGINE_STATS_TIMEOUT:
             return ((cli_intel_t *)(engine->stats_data))->timeout;
         case CL_ENGINE_MAX_PARTITIONS:
             return engine->maxpartitions;
         case CL_ENGINE_MAX_ICONSPE:
             return engine->maxiconspe;
         case CL_ENGINE_MAX_RECHWP3:
             return engine->maxrechwp3;
b056e8e4
         case CL_ENGINE_MAX_SCANTIME:
             return engine->maxscantime;
288057e9
         case CL_ENGINE_PCRE_MATCH_LIMIT:
             return engine->pcre_match_limit;
         case CL_ENGINE_PCRE_RECMATCH_LIMIT:
             return engine->pcre_recmatch_limit;
         case CL_ENGINE_PCRE_MAX_FILESIZE:
             return engine->pcre_max_filesize;
         default:
             cli_errmsg("cl_engine_get: Incorrect field number\n");
             if (err)
                 *err = CL_EARG;
             return -1;
2accc66f
     }
 }
 
 int cl_engine_set_str(struct cl_engine *engine, enum cl_engine_field field, const char *str)
 {
288057e9
     if (!engine)
         return CL_ENULLARG;
 
     switch (field) {
         case CL_ENGINE_PUA_CATEGORIES:
544fa973
             engine->pua_cats = CLI_MPOOL_STRDUP(engine->mempool, str);
288057e9
             if (!engine->pua_cats)
                 return CL_EMEM;
             break;
         case CL_ENGINE_TMPDIR:
544fa973
             engine->tmpdir = CLI_MPOOL_STRDUP(engine->mempool, str);
288057e9
             if (!engine->tmpdir)
                 return CL_EMEM;
             break;
         default:
             cli_errmsg("cl_engine_set_num: Incorrect field number\n");
             return CL_EARG;
c45c4c3d
     }
 
     return CL_SUCCESS;
 }
 
2accc66f
 const char *cl_engine_get_str(const struct cl_engine *engine, enum cl_engine_field field, int *err)
 {
288057e9
     if (!engine) {
         cli_errmsg("cl_engine_get_str: engine == NULL\n");
         if (err)
             *err = CL_ENULLARG;
         return NULL;
2accc66f
     }
 
288057e9
     if (err)
         *err = CL_SUCCESS;
 
     switch (field) {
         case CL_ENGINE_PUA_CATEGORIES:
             return engine->pua_cats;
         case CL_ENGINE_TMPDIR:
             return engine->tmpdir;
         default:
             cli_errmsg("cl_engine_get: Incorrect field number\n");
             if (err)
                 *err = CL_EARG;
             return NULL;
2accc66f
     }
 }
 
99f817e7
 struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine)
 {
288057e9
     struct cl_settings *settings;
99f817e7
 
288057e9
     settings = (struct cl_settings *)malloc(sizeof(struct cl_settings));
     if (!settings) {
059ca614
         cli_errmsg("cl_engine_settings_copy: Unable to allocate memory for settings %llu\n",
                    (long long unsigned)sizeof(struct cl_settings));
241e7eb1
         return NULL;
     }
99f817e7
 
288057e9
     settings->ac_only            = engine->ac_only;
     settings->ac_mindepth        = engine->ac_mindepth;
     settings->ac_maxdepth        = engine->ac_maxdepth;
     settings->tmpdir             = engine->tmpdir ? strdup(engine->tmpdir) : NULL;
     settings->keeptmp            = engine->keeptmp;
b056e8e4
     settings->maxscantime        = engine->maxscantime;
288057e9
     settings->maxscansize        = engine->maxscansize;
     settings->maxfilesize        = engine->maxfilesize;
     settings->maxreclevel        = engine->maxreclevel;
     settings->maxfiles           = engine->maxfiles;
     settings->maxembeddedpe      = engine->maxembeddedpe;
     settings->maxhtmlnormalize   = engine->maxhtmlnormalize;
     settings->maxhtmlnotags      = engine->maxhtmlnotags;
b2726a53
     settings->maxscriptnormalize = engine->maxscriptnormalize;
288057e9
     settings->maxziptypercg      = engine->maxziptypercg;
     settings->min_cc_count       = engine->min_cc_count;
     settings->min_ssn_count      = engine->min_ssn_count;
     settings->bytecode_security  = engine->bytecode_security;
     settings->bytecode_timeout   = engine->bytecode_timeout;
     settings->bytecode_mode      = engine->bytecode_mode;
     settings->pua_cats           = engine->pua_cats ? strdup(engine->pua_cats) : NULL;
 
     settings->cb_pre_cache   = engine->cb_pre_cache;
     settings->cb_pre_scan    = engine->cb_pre_scan;
     settings->cb_post_scan   = engine->cb_post_scan;
1f1bf36b
     settings->cb_virus_found = engine->cb_virus_found;
288057e9
     settings->cb_sigload     = engine->cb_sigload;
2e3e4acc
     settings->cb_sigload_ctx = engine->cb_sigload_ctx;
288057e9
     settings->cb_hash        = engine->cb_hash;
     settings->cb_meta        = engine->cb_meta;
     settings->cb_file_props  = engine->cb_file_props;
f456c5ad
     settings->engine_options = engine->engine_options;
2e3e4acc
 
288057e9
     settings->cb_stats_add_sample      = engine->cb_stats_add_sample;
     settings->cb_stats_remove_sample   = engine->cb_stats_remove_sample;
a1094954
     settings->cb_stats_decrement_count = engine->cb_stats_decrement_count;
288057e9
     settings->cb_stats_submit          = engine->cb_stats_submit;
     settings->cb_stats_flush           = engine->cb_stats_flush;
     settings->cb_stats_get_num         = engine->cb_stats_get_num;
     settings->cb_stats_get_size        = engine->cb_stats_get_size;
     settings->cb_stats_get_hostid      = engine->cb_stats_get_hostid;
a1094954
 
97fbb02b
     settings->maxpartitions = engine->maxpartitions;
 
067bce5f
     settings->maxiconspe = engine->maxiconspe;
731c8e62
     settings->maxrechwp3 = engine->maxrechwp3;
067bce5f
 
288057e9
     settings->pcre_match_limit    = engine->pcre_match_limit;
9bc7c138
     settings->pcre_recmatch_limit = engine->pcre_recmatch_limit;
288057e9
     settings->pcre_max_filesize   = engine->pcre_max_filesize;
9bc7c138
 
99f817e7
     return settings;
 }
 
 int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings *settings)
 {
288057e9
     engine->ac_only            = settings->ac_only;
     engine->ac_mindepth        = settings->ac_mindepth;
     engine->ac_maxdepth        = settings->ac_maxdepth;
     engine->keeptmp            = settings->keeptmp;
b056e8e4
     engine->maxscantime        = settings->maxscantime;
288057e9
     engine->maxscansize        = settings->maxscansize;
     engine->maxfilesize        = settings->maxfilesize;
     engine->maxreclevel        = settings->maxreclevel;
     engine->maxfiles           = settings->maxfiles;
     engine->maxembeddedpe      = settings->maxembeddedpe;
     engine->maxhtmlnormalize   = settings->maxhtmlnormalize;
     engine->maxhtmlnotags      = settings->maxhtmlnotags;
b2726a53
     engine->maxscriptnormalize = settings->maxscriptnormalize;
288057e9
     engine->maxziptypercg      = settings->maxziptypercg;
     engine->min_cc_count       = settings->min_cc_count;
     engine->min_ssn_count      = settings->min_ssn_count;
     engine->bytecode_security  = settings->bytecode_security;
     engine->bytecode_timeout   = settings->bytecode_timeout;
     engine->bytecode_mode      = settings->bytecode_mode;
     engine->engine_options     = settings->engine_options;
 
     if (engine->tmpdir)
544fa973
         MPOOL_FREE(engine->mempool, engine->tmpdir);
288057e9
     if (settings->tmpdir) {
544fa973
         engine->tmpdir = CLI_MPOOL_STRDUP(engine->mempool, settings->tmpdir);
288057e9
         if (!engine->tmpdir)
             return CL_EMEM;
99f817e7
     } else {
288057e9
         engine->tmpdir = NULL;
99f817e7
     }
 
288057e9
     if (engine->pua_cats)
544fa973
         MPOOL_FREE(engine->mempool, engine->pua_cats);
288057e9
     if (settings->pua_cats) {
544fa973
         engine->pua_cats = CLI_MPOOL_STRDUP(engine->mempool, settings->pua_cats);
288057e9
         if (!engine->pua_cats)
             return CL_EMEM;
99f817e7
     } else {
288057e9
         engine->pua_cats = NULL;
99f817e7
     }
 
288057e9
     engine->cb_pre_cache   = settings->cb_pre_cache;
     engine->cb_pre_scan    = settings->cb_pre_scan;
     engine->cb_post_scan   = settings->cb_post_scan;
1f1bf36b
     engine->cb_virus_found = settings->cb_virus_found;
288057e9
     engine->cb_sigload     = settings->cb_sigload;
2e3e4acc
     engine->cb_sigload_ctx = settings->cb_sigload_ctx;
288057e9
     engine->cb_hash        = settings->cb_hash;
     engine->cb_meta        = settings->cb_meta;
     engine->cb_file_props  = settings->cb_file_props;
2e3e4acc
 
288057e9
     engine->cb_stats_add_sample      = settings->cb_stats_add_sample;
     engine->cb_stats_remove_sample   = settings->cb_stats_remove_sample;
a1094954
     engine->cb_stats_decrement_count = settings->cb_stats_decrement_count;
288057e9
     engine->cb_stats_submit          = settings->cb_stats_submit;
     engine->cb_stats_flush           = settings->cb_stats_flush;
     engine->cb_stats_get_num         = settings->cb_stats_get_num;
     engine->cb_stats_get_size        = settings->cb_stats_get_size;
     engine->cb_stats_get_hostid      = settings->cb_stats_get_hostid;
a1094954
 
97fbb02b
     engine->maxpartitions = settings->maxpartitions;
 
067bce5f
     engine->maxiconspe = settings->maxiconspe;
731c8e62
     engine->maxrechwp3 = settings->maxrechwp3;
067bce5f
 
288057e9
     engine->pcre_match_limit    = settings->pcre_match_limit;
9bc7c138
     engine->pcre_recmatch_limit = settings->pcre_recmatch_limit;
288057e9
     engine->pcre_max_filesize   = settings->pcre_max_filesize;
9bc7c138
 
99f817e7
     return CL_SUCCESS;
 }
 
 int cl_engine_settings_free(struct cl_settings *settings)
 {
288057e9
     if (!settings)
         return CL_ENULLARG;
99f817e7
 
     free(settings->tmpdir);
     free(settings->pua_cats);
     free(settings);
     return CL_SUCCESS;
 }
 
312b7e53
 void cli_check_blockmax(cli_ctx *ctx, int rc)
 {
048a88e6
     if (SCAN_HEURISTIC_EXCEEDS_MAX && !ctx->limit_exceeded) {
288057e9
         cli_append_virus(ctx, "Heuristics.Limits.Exceeded");
312b7e53
         ctx->limit_exceeded = 1;
288057e9
         cli_dbgmsg("Limit %s Exceeded: scanning may be incomplete and additional analysis needed for this file.\n",
                    cl_strerror(rc));
312b7e53
     }
 }
 
b056e8e4
 cl_error_t cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3)
288057e9
 {
b056e8e4
     cl_error_t ret = CL_SUCCESS;
b80ae277
     unsigned long needed;
 
     /* if called without limits, go on, unpack, scan */
288057e9
     if (!ctx) return CL_CLEAN;
b80ae277
 
288057e9
     needed = (need1 > need2) ? need1 : need2;
     needed = (needed > need3) ? needed : need3;
b80ae277
 
b056e8e4
     /* Enforce timelimit */
     ret = cli_checktimelimit(ctx);
 
b80ae277
     /* if we have global scan limits */
288057e9
     if (needed && ctx->engine->maxscansize) {
b80ae277
         /* if the remaining scansize is too small... */
288057e9
         if (ctx->engine->maxscansize - ctx->scansize < needed) {
             /* ... we tell the caller to skip this file */
             cli_dbgmsg("%s: scansize exceeded (initial: %lu, consumed: %lu, needed: %lu)\n", who, (unsigned long int)ctx->engine->maxscansize, (unsigned long int)ctx->scansize, needed);
             ret = CL_EMAXSIZE;
         }
4ad62d4e
     }
 
b80ae277
     /* if we have per-file size limits, and we are overlimit... */
288057e9
     if (needed && ctx->engine->maxfilesize && ctx->engine->maxfilesize < needed) {
         /* ... we tell the caller to skip this file */
         cli_dbgmsg("%s: filesize exceeded (allowed: %lu, needed: %lu)\n", who, (unsigned long int)ctx->engine->maxfilesize, needed);
         ret = CL_EMAXSIZE;
b80ae277
     }
d91ab809
 
288057e9
     if (ctx->engine->maxfiles && ctx->scannedfiles >= ctx->engine->maxfiles) {
724b2bf7
         cli_dbgmsg("%s: files limit reached (max: %u)\n", who, ctx->engine->maxfiles);
288057e9
         ret = CL_EMAXFILES;
d91ab809
     }
312b7e53
 
     if (ret != CL_SUCCESS)
         cli_check_blockmax(ctx, ret);
 
b80ae277
     return ret;
36582be0
 }
 
b056e8e4
 cl_error_t cli_updatelimits(cli_ctx *ctx, unsigned long needed)
288057e9
 {
b056e8e4
     cl_error_t ret = cli_checklimits("cli_updatelimits", ctx, needed, 0, 0);
b80ae277
 
850db69e
     if (ret != CL_CLEAN) return ret;
d91ab809
     ctx->scannedfiles++;
288057e9
     ctx->scansize += needed;
     if (ctx->scansize > ctx->engine->maxscansize)
724b2bf7
         ctx->scansize = ctx->engine->maxscansize;
850db69e
     return CL_CLEAN;
4ad62d4e
 }
 
b056e8e4
 /**
  * @brief Check if we've exceeded the time limit.
  * If ctx is NULL, there can be no timelimit so just return success.
  *
  * @param ctx         The scanning context.
  * @return cl_error_t CL_SUCCESS if has not exceeded, CL_ETIMEOUT if has exceeded.
  */
 cl_error_t cli_checktimelimit(cli_ctx *ctx)
49b33289
 {
b056e8e4
     cl_error_t ret = CL_SUCCESS;
 
     if (NULL == ctx) {
         goto done;
     }
 
49b33289
     if (ctx->time_limit.tv_sec != 0) {
         struct timeval now;
         if (gettimeofday(&now, NULL) == 0) {
b056e8e4
             if (now.tv_sec > ctx->time_limit.tv_sec)
                 ret = CL_ETIMEOUT;
             else if (now.tv_sec == ctx->time_limit.tv_sec && now.tv_usec > ctx->time_limit.tv_usec)
                 ret = CL_ETIMEOUT;
49b33289
         }
     }
b056e8e4
 
 done:
     return ret;
49b33289
 }
 
d5fde2eb
 /*
  * Type: 1 = MD5, 2 = SHA1, 3 = SHA256
  */
 char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type)
e3aaff8e
 {
b2e7c931
     unsigned char digest[32];
     char buff[FILEBUFF];
     char *hashstr, *pt;
288057e9
     const char *alg = NULL;
b2e7c931
     int i, bytes, size;
da6e06dd
     void *ctx;
 
     switch (type) {
         case 1:
288057e9
             alg  = "md5";
da6e06dd
             size = 16;
             break;
         case 2:
288057e9
             alg  = "sha1";
da6e06dd
             size = 20;
             break;
         default:
288057e9
             alg  = "sha256";
da6e06dd
             size = 32;
             break;
     }
f077c617
 
da6e06dd
     ctx = cl_hash_init(alg);
f077c617
     if (!(ctx))
         return NULL;
335d1663
 
288057e9
     while ((bytes = fread(buff, 1, FILEBUFF, fs)))
da6e06dd
         cl_update_hash(ctx, buff, bytes);
b2e7c931
 
da6e06dd
     cl_finish_hash(ctx, digest);
335d1663
 
288057e9
     if (!(hashstr = (char *)cli_calloc(size * 2 + 1, sizeof(char))))
b2e7c931
         return NULL;
e3aaff8e
 
d5fde2eb
     pt = hashstr;
288057e9
     for (i = 0; i < size; i++) {
b2e7c931
         sprintf(pt, "%02x", digest[i]);
         pt += 2;
335d1663
     }
e3aaff8e
 
288057e9
     if (digcpy)
b2e7c931
         memcpy(digcpy, digest, size);
e3aaff8e
 
d5fde2eb
     return hashstr;
e3aaff8e
 }
 
d5fde2eb
 char *cli_hashfile(const char *filename, int type)
d9e258d5
 {
288057e9
     FILE *fs;
     char *hashstr;
d9e258d5
 
288057e9
     if ((fs = fopen(filename, "rb")) == NULL) {
         cli_errmsg("cli_hashfile(): Can't open file %s\n", filename);
         return NULL;
8000d078
     }
 
d5fde2eb
     hashstr = cli_hashstream(fs, NULL, type);
d9e258d5
 
d5fde2eb
     fclose(fs);
     return hashstr;
d9e258d5
 }
 
c0a95e0c
 /* Function: unlink
         unlink() with error checking
 */
288057e9
 int cli_unlink(const char *pathname)
c0a95e0c
 {
01eebc13
     if (unlink(pathname) == -1) {
 #ifdef _WIN32
c31a98d5
         /* Windows may fail to unlink a file if it is marked read-only,
01eebc13
 		 * even if the user has permissions to delete the file. */
         if (-1 == _chmod(pathname, _S_IWRITE)) {
             char err[128];
             cli_warnmsg("cli_unlink: _chmod failure - %s\n", cli_strerror(errno, err, sizeof(err)));
             return 1;
         } else if (unlink(pathname) == -1) {
             char err[128];
             cli_warnmsg("cli_unlink: unlink failure - %s\n", cli_strerror(errno, err, sizeof(err)));
             return 1;
         }
         return 0;
 #else
         char err[128];
         cli_warnmsg("cli_unlink: unlink failure - %s\n", cli_strerror(errno, err, sizeof(err)));
         return 1;
 #endif
     }
     return 0;
c0a95e0c
 }
 
288057e9
 void cli_virus_found_cb(cli_ctx *ctx)
cbf5017a
 {
     if (ctx->engine->cb_virus_found)
         ctx->engine->cb_virus_found(fmap_fd(*ctx->fmap), (const char *)*ctx->virname, ctx->cb_ctx);
 }
 
b056e8e4
 cl_error_t cli_append_possibly_unwanted(cli_ctx *ctx, const char *virname)
cbf5017a
 {
048a88e6
     if (SCAN_ALLMATCHES)
cbf5017a
         return cli_append_virus(ctx, virname);
048a88e6
     else if (SCAN_HEURISTIC_PRECEDENCE)
cbf5017a
         return cli_append_virus(ctx, virname);
     else if (ctx->num_viruses == 0 && ctx->virname != NULL && *ctx->virname == NULL) {
         ctx->found_possibly_unwanted = 1;
         ctx->num_viruses++;
         *ctx->virname = virname;
     }
     return CL_CLEAN;
 }
 
102cd430
 cl_error_t cli_append_virus(cli_ctx *ctx, const char *virname)
6ad45a29
 {
1f1bf36b
     if (ctx->virname == NULL)
cbf5017a
         return CL_CLEAN;
     if (ctx->fmap != NULL && (*ctx->fmap) != NULL && CL_VIRUS != cli_checkfp_virus((*ctx->fmap)->maphash, (*ctx->fmap)->len, ctx, virname))
         return CL_CLEAN;
048a88e6
     if (!SCAN_ALLMATCHES && ctx->num_viruses != 0)
         if (SCAN_HEURISTIC_PRECEDENCE)
cbf5017a
             return CL_CLEAN;
288057e9
     if (ctx->limit_exceeded == 0 || SCAN_ALLMATCHES) {
90f29efa
         ctx->num_viruses++;
         *ctx->virname = virname;
cbf5017a
         cli_virus_found_cb(ctx);
90f29efa
     }
2f842846
 #if HAVE_JSON
048a88e6
     if (SCAN_COLLECT_METADATA && ctx->wrkproperty) {
2f842846
         json_object *arrobj, *virobj;
6c048b8a
         if (!json_object_object_get_ex(ctx->wrkproperty, "Viruses", &arrobj)) {
2f842846
             arrobj = json_object_new_array();
             if (NULL == arrobj) {
                 cli_errmsg("cli_append_virus: no memory for json virus array\n");
cbf5017a
                 return CL_EMEM;
2f842846
             }
             json_object_object_add(ctx->wrkproperty, "Viruses", arrobj);
         }
         virobj = json_object_new_string(virname);
         if (NULL == virobj) {
             cli_errmsg("cli_append_virus: no memory for json virus name object\n");
cbf5017a
             return CL_EMEM;
2f842846
         }
         json_object_array_add(arrobj, virobj);
     }
 #endif
56eed3ed
     return CL_VIRUS;
6ad45a29
 }
 
288057e9
 const char *cli_get_last_virus(const cli_ctx *ctx)
6ad45a29
 {
b81cbc26
     if (!ctx || !ctx->virname || !(*ctx->virname))
288057e9
         return NULL;
1f1bf36b
     return *ctx->virname;
6ad45a29
 }
 
288057e9
 const char *cli_get_last_virus_str(const cli_ctx *ctx)
b81cbc26
 {
288057e9
     const char *ret;
b81cbc26
     if ((ret = cli_get_last_virus(ctx)))
288057e9
         return ret;
b81cbc26
     return "";
 }
 
031fe00a
 void cli_set_container(cli_ctx *ctx, cli_file_t type, size_t size)
 {
     ctx->containers[ctx->recursion].type = type;
     ctx->containers[ctx->recursion].size = size;
288057e9
     if (type >= CL_TYPE_MSEXE && type != CL_TYPE_HTML && type != CL_TYPE_OTHER && type != CL_TYPE_IGNORED)
167c0079
         ctx->containers[ctx->recursion].flag = CONTAINER_FLAG_VALID;
     else
         ctx->containers[ctx->recursion].flag = 0;
031fe00a
 }
 
167c0079
 cli_file_t cli_get_container(cli_ctx *ctx, int index)
031fe00a
 {
288057e9
     if (index < 0)
         index = ctx->recursion + index + 1;
     while (index >= 0 && index <= (int)ctx->recursion) {
         if (ctx->containers[index].flag & CONTAINER_FLAG_VALID)
             return ctx->containers[index].type;
         index--;
     }
     return CL_TYPE_ANY;
167c0079
 }
 
 cli_file_t cli_get_container_intermediate(cli_ctx *ctx, int index)
 {
288057e9
     if (index < 0)
         index = ctx->recursion + index + 1;
     if (index >= 0 && index <= (int)ctx->recursion)
         return ctx->containers[index].type;
     return CL_TYPE_ANY;
031fe00a
 }
 
 size_t cli_get_container_size(cli_ctx *ctx, int index)
 {
288057e9
     if (index < 0)
         index = ctx->recursion + index + 1;
     while (index >= 0 && index <= (int)ctx->recursion) {
         if (ctx->containers[index].flag & CONTAINER_FLAG_VALID)
             return ctx->containers[index].size;
         index--;
     }
     return ctx->containers[0].size;
031fe00a
 }
 
288057e9
 #ifdef C_WINDOWS
a946fc1c
 /*
  * Windows doesn't allow you to delete a directory while it is still open
  */
288057e9
 int cli_rmdirs(const char *name)
a946fc1c
 {
288057e9
     int rc;
     STATBUF statb;
     DIR *dd;
     struct dirent *dent;
     char err[128];
a946fc1c
 
288057e9
     if (CLAMSTAT(name, &statb) < 0) {
         cli_warnmsg("cli_rmdirs: Can't locate %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
         return -1;
a946fc1c
     }
 
288057e9
     if (!S_ISDIR(statb.st_mode)) {
         if (cli_unlink(name)) return -1;
         return 0;
a946fc1c
     }
 
288057e9
     if ((dd = opendir(name)) == NULL)
         return -1;
a946fc1c
 
     rc = 0;
 
288057e9
     while ((dent = readdir(dd)) != NULL) {
         char *path;
 
         if (strcmp(dent->d_name, ".") == 0)
             continue;
         if (strcmp(dent->d_name, "..") == 0)
             continue;
 
         path = cli_malloc(strlen(name) + strlen(dent->d_name) + 2);
 
         if (path == NULL) {
             cli_errmsg("cli_rmdirs: Unable to allocate memory for path %u\n", strlen(name) + strlen(dent->d_name) + 2);
             closedir(dd);
             return -1;
         }
 
         sprintf(path, "%s\\%s", name, dent->d_name);
         rc = cli_rmdirs(path);
         free(path);
         if (rc != 0)
             break;
a946fc1c
     }
 
     closedir(dd);
 
288057e9
     if (rmdir(name) < 0) {
         cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
         return -1;
a946fc1c
     }
 
288057e9
     return rc;
a946fc1c
 }
 #else
e3aaff8e
 int cli_rmdirs(const char *dirname)
 {
288057e9
     DIR *dd;
     struct dirent *dent;
     STATBUF maind, statbuf;
     char *path;
     char err[128];
a9ebff44
 
     chmod(dirname, 0700);
288057e9
     if ((dd = opendir(dirname)) != NULL) {
         while (CLAMSTAT(dirname, &maind) != -1) {
             if (!rmdir(dirname)) break;
             if (errno != ENOTEMPTY && errno != EEXIST && errno != EBADF) {
                 cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", dirname, cli_strerror(errno, err, sizeof(err)));
                 closedir(dd);
                 return -1;
             }
e3aaff8e
 
288057e9
             while ((dent = readdir(dd))) {
                 if (dent->d_ino) {
                     if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
                         path = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
                         if (!path) {
                             cli_errmsg("cli_rmdirs: Unable to allocate memory for path %llu\n", (long long unsigned)(strlen(dirname) + strlen(dent->d_name) + 2));
                             closedir(dd);
                             return -1;
                         }
 
                         sprintf(path, "%s" PATHSEP "%s", dirname, dent->d_name);
 
                         /* stat the file */
                         if (LSTAT(path, &statbuf) != -1) {
                             if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
                                 if (rmdir(path) == -1) { /* can't be deleted */
                                     if (errno == EACCES) {
                                         cli_errmsg("cli_rmdirs: Can't remove some temporary directories due to access problem.\n");
                                         closedir(dd);
                                         free(path);
                                         return -1;
                                     }
                                     if (cli_rmdirs(path)) {
                                         cli_warnmsg("cli_rmdirs: Can't remove nested directory %s\n", path);
                                         free(path);
                                         closedir(dd);
                                         return -1;
                                     }
                                 }
                             } else {
                                 if (cli_unlink(path)) {
                                     free(path);
                                     closedir(dd);
                                     return -1;
                                 }
                             }
                         }
                         free(path);
                     }
                 }
             }
             rewinddir(dd);
         }
 
     } else {
         return -1;
e3aaff8e
     }
 
     closedir(dd);
     return 0;
 }
a946fc1c
 #endif
5b25b5e8
 
49760905
 /* Implement a generic bitset, trog@clamav.net */
8a9c2d19
 
 #define BITS_PER_CHAR (8)
 #define BITSET_DEFAULT_SIZE (1024)
 
 static unsigned long nearest_power(unsigned long num)
 {
288057e9
     unsigned long n = BITSET_DEFAULT_SIZE;
 
     while (n < num) {
         n <<= 1;
         if (n == 0) {
             return num;
         }
     }
     return n;
8a9c2d19
 }
 
079229d6
 bitset_t *cli_bitset_init(void)
8a9c2d19
 {
288057e9
     bitset_t *bs;
 
     bs = cli_malloc(sizeof(bitset_t));
     if (!bs) {
059ca614
         cli_errmsg("cli_bitset_init: Unable to allocate memory for bs %llu\n", (long long unsigned)sizeof(bitset_t));
288057e9
         return NULL;
     }
     bs->length = BITSET_DEFAULT_SIZE;
     bs->bitset = cli_calloc(BITSET_DEFAULT_SIZE, 1);
     if (!bs->bitset) {
241e7eb1
         cli_errmsg("cli_bitset_init: Unable to allocate memory for bs->bitset %u\n", BITSET_DEFAULT_SIZE);
288057e9
         free(bs);
         return NULL;
     }
     return bs;
8a9c2d19
 }
 
 void cli_bitset_free(bitset_t *bs)
 {
288057e9
     if (!bs) {
         return;
     }
     if (bs->bitset) {
         free(bs->bitset);
     }
     free(bs);
8a9c2d19
 }
 
 static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size)
 {
288057e9
     unsigned long new_length;
     unsigned char *new_bitset;
 
     new_length = nearest_power(min_size);
     new_bitset = (unsigned char *)cli_realloc(bs->bitset, new_length);
     if (!new_bitset) {
         return NULL;
     }
     bs->bitset = new_bitset;
     memset(bs->bitset + bs->length, 0, new_length - bs->length);
     bs->length = new_length;
     return bs;
8a9c2d19
 }
 
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset)
 {
288057e9
     unsigned long char_offset;
 
     char_offset = bit_offset / BITS_PER_CHAR;
     bit_offset  = bit_offset % BITS_PER_CHAR;
 
     if (char_offset >= bs->length) {
         bs = bitset_realloc(bs, char_offset + 1);
         if (!bs) {
             return FALSE;
         }
     }
     bs->bitset[char_offset] |= ((unsigned char)1 << bit_offset);
     return TRUE;
8a9c2d19
 }
 
 int cli_bitset_test(bitset_t *bs, unsigned long bit_offset)
 {
288057e9
     unsigned long char_offset;
 
     char_offset = bit_offset / BITS_PER_CHAR;
     bit_offset  = bit_offset % BITS_PER_CHAR;
 
     if (char_offset >= bs->length) {
         return FALSE;
     }
     return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
8a9c2d19
 }
eb422a03
 
288057e9
 void cl_engine_set_clcb_pre_cache(struct cl_engine *engine, clcb_pre_cache callback)
 {
a217d9a7
     engine->cb_pre_cache = callback;
eb422a03
 }
 
288057e9
 void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback)
 {
a217d9a7
     engine->cb_pre_scan = callback;
a0eb7910
 }
 
288057e9
 void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback)
 {
eb422a03
     engine->cb_post_scan = callback;
 }
 
288057e9
 void cl_engine_set_clcb_virus_found(struct cl_engine *engine, clcb_virus_found callback)
 {
1f1bf36b
     engine->cb_virus_found = callback;
 }
 
288057e9
 void cl_engine_set_clcb_sigload(struct cl_engine *engine, clcb_sigload callback, void *context)
 {
     engine->cb_sigload     = callback;
eb422a03
     engine->cb_sigload_ctx = callback ? context : NULL;
 }
769f37a6
 
 void cl_engine_set_clcb_hash(struct cl_engine *engine, clcb_hash callback)
 {
     engine->cb_hash = callback;
 }
e86311ab
 
 void cl_engine_set_clcb_meta(struct cl_engine *engine, clcb_meta callback)
 {
     engine->cb_meta = callback;
 }
6606d050
 
20a3b53b
 void cl_engine_set_clcb_file_props(struct cl_engine *engine, clcb_file_props callback)
6606d050
 {
     engine->cb_file_props = callback;
 }