libclamav/others.c
e3aaff8e
 /*
2023340a
  *  Copyright (C) 2007-2008 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>
a946fc1c
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
a946fc1c
 #endif
e3aaff8e
 #include <sys/types.h>
 #include <sys/stat.h>
a946fc1c
 #ifndef	C_WINDOWS
e3aaff8e
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <dirent.h>
a946fc1c
 #endif
e3aaff8e
 #include <time.h>
 #include <fcntl.h>
a946fc1c
 #ifndef	C_WINDOWS
e3aaff8e
 #include <pwd.h>
a946fc1c
 #endif
e3aaff8e
 #include <errno.h>
a946fc1c
 #include "target.h"
 #ifndef	C_WINDOWS
feec9e31
 #include <sys/time.h>
a946fc1c
 #endif
 #ifdef	HAVE_SYS_PARAM_H
15edd45f
 #include <sys/param.h>
a946fc1c
 #endif
 #ifdef	HAVE_MALLOC_H
 #include <malloc.h>
 #endif
 #if	defined(_MSC_VER) && defined(_DEBUG)
 #include <crtdbg.h>
 #endif
e3aaff8e
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
e3aaff8e
 #include "clamav.h"
 #include "others.h"
 #include "md5.h"
5ae8bdd5
 #include "cltypes.h"
92f585fb
 #include "regex/regex.h"
f5a4018b
 #include "ltdl.h"
ab0d2f05
 #include "matcher-ac.h"
589d8d8e
 #include "default.h"
e3aaff8e
 
b067a7dd
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
97eb8797
 #ifdef        C_WINDOWS
 #undef        P_tmpdir
 #define       P_tmpdir        "C:\\WINDOWS\\TEMP"
 #endif
 
f5a4018b
 int (*cli_unrar_open)(int fd, const char *dirname, unrar_state_t *state);
 int (*cli_unrar_extract_next_prepare)(unrar_state_t *state, const char *dirname);
 int (*cli_unrar_extract_next)(unrar_state_t *state, const char *dirname);
 void (*cli_unrar_close)(unrar_state_t *state);
 int have_rar = 0;
 static int is_rar_initd = 0;
 
cd0a934f
 static int warn_dlerror(const char *msg)
 {
     const char *err = lt_dlerror();
     if (err)
 	cli_warnmsg("%s: %s\n", msg, err);
     else
 	cli_warnmsg("%s\n", err);
     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
 
 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
f5a4018b
     if(lt_dlinit()) {
cd0a934f
         warn_dlerror("Cannot init ltdl - unrar support unavailable");
         return -1;
     }
     return 0;
 }
 
0b44bb9f
 #define PASTE(a,b) a#b
 
cd0a934f
 static lt_dlhandle lt_dlfind(const char *name, const char *featurename)
 {
     static const char *suffixes[] = {
 	LT_MODULE_EXT"."LIBCLAMAV_FULLVER,
0b44bb9f
 	PASTE(LT_MODULE_EXT".", LIBCLAMAV_MAJORVER),
cd0a934f
 	LT_MODULE_EXT,
 	"."LT_LIBEXT
     };
 
     const char *searchpath;
     const lt_dlinfo *info;
     char modulename[128];
     lt_dlhandle rhandle;
     int canretry=1;
     unsigned i;
 
     if (lt_dladdsearchdir(SEARCH_LIBDIR)) {
 	cli_dbgmsg("lt_dladdsearchdir failed for %s\n", SEARCH_LIBDIR);
f5a4018b
     }
cd0a934f
 
     searchpath = lt_dlgetsearchpath();
     if (!searchpath)
 	searchpath = "";
 
     cli_dbgmsg("searching for %s, user-searchpath: %s\n", featurename, searchpath);
     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);
     }
 
33d52089
     if (!rhandle) {
cd0a934f
 	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)
 	cli_dbgmsg("%s support loaded from %s %s\n", featurename, info->filename ? info->filename : "?", info->name ? info->name : "");
     return rhandle;
 }
 
 static void cli_rarload(void) {
     lt_dlhandle rhandle;
 
     if(is_rar_initd) return;
     is_rar_initd = 1;
 
     rhandle = lt_dlfind("libclamunrar_iface", "unrar");
     if (!rhandle)
 	return;
 
0b44bb9f
     if (!(cli_unrar_open = (int(*)(int, const char *, unrar_state_t *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_open")) ||
 	!(cli_unrar_extract_next_prepare = (int(*)(unrar_state_t *, const char *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_extract_next_prepare")) ||
 	!(cli_unrar_extract_next = (int(*)(unrar_state_t *, const char *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_extract_next")) ||
 	!(cli_unrar_close = (void(*)(unrar_state_t *))lt_dlsym(rhandle, "libclamunrar_iface_LTX_unrar_close"))
f5a4018b
 	) {
 	/* ideally we should never land here, we'd better warn so */
         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
 }
 
b5134815
 unsigned int cl_retflevel(void)
a3ee0766
 {
     return CL_FLEVEL;
 }
 
68a6f51f
 const char *cl_strerror(int clerror)
e3aaff8e
 {
     switch(clerror) {
871177cd
 	/* libclamav specific codes */
e3aaff8e
 	case CL_CLEAN:
ac75a532
 	    return "No viruses detected";
e3aaff8e
 	case CL_VIRUS:
ac75a532
 	    return "Virus(es) detected";
871177cd
 	case CL_ENULLARG:
 	    return "Null argument passed to function";
2accc66f
 	case CL_EARG:
 	    return "Invalid argument passed to function";
e3aaff8e
 	case CL_EMALFDB:
ac75a532
 	    return "Malformed database";
d9e258d5
 	case CL_ECVD:
ac75a532
 	    return "Broken or not a CVD file";
871177cd
 	case CL_EVERIFY:
 	    return "Can't verify database integrity";
 	case CL_EUNPACK:
 	    return "Can't unpack some 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";
81d06055
 	/* 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";
e3aaff8e
 	default:
ac75a532
 	    return "Unknown error code";
e3aaff8e
     }
 }
 
2accc66f
 int cl_init(unsigned int initoptions)
724b2bf7
 {
     /* put dlopen() stuff here, etc. */
cd0a934f
     if (lt_init() == 0) {
 	cli_rarload();
     }
724b2bf7
     return CL_SUCCESS;
 }
 
b8fe70b3
 struct cl_engine *cl_engine_new(void)
724b2bf7
 {
 	struct cl_engine *new;
 
 
     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;
     }
 
     /* Setup default limits */
589d8d8e
     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;
     new->min_ssn_count = CLI_DEFAULT_MIN_SSN_COUNT;
724b2bf7
 
     new->refcount = 1;
ab0d2f05
     new->ac_only = 0;
589d8d8e
     new->ac_mindepth = CLI_DEFAULT_AC_MINDEPTH;
     new->ac_maxdepth = CLI_DEFAULT_AC_MAXDEPTH;
724b2bf7
 
 #ifdef USE_MPOOL
47d40feb
     if(!(new->mempool = mpool_create())) {
724b2bf7
 	cli_errmsg("cl_engine_new: Can't allocate memory for memory pool\n");
 	free(new);
 	return NULL;
     }
 #endif
 
47d40feb
     new->root = mpool_calloc(new->mempool, CLI_MTARGETS, sizeof(struct cli_matcher *));
724b2bf7
     if(!new->root) {
 	cli_errmsg("cl_engine_new: Can't allocate memory for roots\n");
 #ifdef USE_MPOOL
47d40feb
 	mpool_destroy(new->mempool);
724b2bf7
 #endif
 	free(new);
 	return NULL;
     }
 
47d40feb
     new->dconf = cli_mpool_dconf_init(new->mempool);
724b2bf7
     if(!new->dconf) {
 	cli_errmsg("cl_engine_new: Can't initialize dynamic configuration\n");
47d40feb
 	mpool_free(new->mempool, new->root);
724b2bf7
 #ifdef USE_MPOOL
47d40feb
 	mpool_destroy(new->mempool);
724b2bf7
 #endif
 	free(new);
 	return NULL;
     }
 
     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
 {
2accc66f
     if(!engine)
c45c4c3d
 	return CL_ENULLARG;
 
2accc66f
     /* TODO: consider adding checks and warn/errs when num overflows the
      * destination type
      */
c45c4c3d
     switch(field) {
 	case CL_ENGINE_MAX_SCANSIZE:
2accc66f
 	    engine->maxscansize = num;
c45c4c3d
 	    break;
 	case CL_ENGINE_MAX_FILESIZE:
2accc66f
 	    engine->maxfilesize = num;
c45c4c3d
 	    break;
 	case CL_ENGINE_MAX_RECURSION:
2accc66f
 	    engine->maxreclevel = num;
c45c4c3d
 	    break;
 	case CL_ENGINE_MAX_FILES:
2accc66f
 	    engine->maxfiles = num;
c45c4c3d
 	    break;
 	case CL_ENGINE_MIN_CC_COUNT:
2accc66f
 	    engine->min_cc_count = num;
c45c4c3d
 	    break;
 	case CL_ENGINE_MIN_SSN_COUNT:
2accc66f
 	    engine->min_ssn_count = num;
c45c4c3d
 	    break;
6396a96a
 	case CL_ENGINE_DB_OPTIONS:
370892d0
 	case CL_ENGINE_DB_VERSION:
 	case CL_ENGINE_DB_TIME:
2accc66f
 	    cli_warnmsg("cl_engine_set_num: The field is read only\n");
 	    return CL_EARG;
ab0d2f05
 	case CL_ENGINE_AC_ONLY:
2accc66f
 	    engine->ac_only = num;
ab0d2f05
 	    break;
 	case CL_ENGINE_AC_MINDEPTH:
2accc66f
 	    engine->ac_mindepth = num;
ab0d2f05
 	    break;
 	case CL_ENGINE_AC_MAXDEPTH:
2accc66f
 	    engine->ac_maxdepth = num;
33068e09
 	    break;
 	case CL_ENGINE_KEEPTMP:
2accc66f
 	    engine->keeptmp = num;
33068e09
 	    break;
c45c4c3d
 	default:
2accc66f
 	    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
 {
2accc66f
     if(!engine) {
 	cli_errmsg("cl_engine_get_num: engine == NULL\n");
 	if(err)
 	    *err = CL_ENULLARG;
 	return -1;
     }
 
     if(err)
 	*err = CL_SUCCESS;
c45c4c3d
 
     switch(field) {
6396a96a
 	case CL_ENGINE_DB_OPTIONS:
 	    return engine->dboptions;
c45c4c3d
 	case CL_ENGINE_MAX_SCANSIZE:
2accc66f
 	    return engine->maxscansize;
c45c4c3d
 	case CL_ENGINE_MAX_FILESIZE:
2accc66f
 	    return engine->maxfilesize;
c45c4c3d
 	case CL_ENGINE_MAX_RECURSION:
2accc66f
 	    return engine->maxreclevel;
c45c4c3d
 	case CL_ENGINE_MAX_FILES:
2accc66f
 	    return engine->maxfiles;
c45c4c3d
 	case CL_ENGINE_MIN_CC_COUNT:
2accc66f
 	    return engine->min_cc_count;
c45c4c3d
 	case CL_ENGINE_MIN_SSN_COUNT:
2accc66f
 	    return engine->min_ssn_count;
370892d0
 	case CL_ENGINE_DB_VERSION:
2accc66f
 	    return engine->dbversion[0];
370892d0
 	case CL_ENGINE_DB_TIME:
2accc66f
 	    return engine->dbversion[1];
ab0d2f05
 	case CL_ENGINE_AC_ONLY:
2accc66f
 	    return engine->ac_only;
ab0d2f05
 	case CL_ENGINE_AC_MINDEPTH:
2accc66f
 	    return engine->ac_mindepth;
ab0d2f05
 	case CL_ENGINE_AC_MAXDEPTH:
2accc66f
 	    return engine->ac_maxdepth;
 	case CL_ENGINE_KEEPTMP:
 	    return engine->keeptmp;
 	default:
 	    cli_errmsg("cl_engine_get: Incorrect field number\n");
 	    if(err)
 		*err = CL_EARG;
 	    return -1;
     }
 }
 
 int cl_engine_set_str(struct cl_engine *engine, enum cl_engine_field field, const char *str)
 {
     if(!engine)
 	return CL_ENULLARG;
 
     switch(field) {
 	case CL_ENGINE_PUA_CATEGORIES:
 	    engine->pua_cats = cli_mpool_strdup(engine->mempool, str);
 	    if(!engine->pua_cats)
 		return CL_EMEM;
ab0d2f05
 	    break;
33068e09
 	case CL_ENGINE_TMPDIR:
2accc66f
 	    engine->tmpdir = cli_mpool_strdup(engine->mempool, str);
 	    if(!engine->tmpdir)
 		return CL_EMEM;
33068e09
 	    break;
c45c4c3d
 	default:
2accc66f
 	    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)
 {
     if(!engine) {
 	cli_errmsg("cl_engine_get_str: engine == NULL\n");
 	if(err)
 	    *err = CL_ENULLARG;
 	return NULL;
     }
 
     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;
     }
 }
 
99f817e7
 struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine)
 {
 	struct cl_settings *settings;
 
     settings = (struct cl_settings *) malloc(sizeof(struct cl_settings));
     if(!settings)
 	return NULL;
 
     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;
     settings->maxscansize = engine->maxscansize;
     settings->maxfilesize = engine->maxfilesize;
     settings->maxreclevel = engine->maxreclevel;
     settings->maxfiles = engine->maxfiles;
     settings->min_cc_count = engine->min_cc_count;
     settings->min_ssn_count = engine->min_ssn_count;
     settings->pua_cats = engine->pua_cats ? strdup(engine->pua_cats) : NULL;
 
     return settings;
 }
 
 int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings *settings)
 {
     engine->ac_only = settings->ac_only;
     engine->ac_mindepth = settings->ac_mindepth;
     engine->ac_maxdepth = settings->ac_maxdepth;
     engine->keeptmp = settings->keeptmp;
     engine->maxscansize = settings->maxscansize;
     engine->maxfilesize = settings->maxfilesize;
     engine->maxreclevel = settings->maxreclevel;
     engine->maxfiles = settings->maxfiles;
     engine->min_cc_count = settings->min_cc_count;
     engine->min_ssn_count = settings->min_ssn_count;
 
     if(engine->tmpdir)
 	mpool_free(engine->mempool, engine->tmpdir);
     if(settings->tmpdir) {
 	engine->tmpdir = cli_mpool_strdup(engine->mempool, settings->tmpdir);
 	if(!engine->tmpdir)
 	    return CL_EMEM;
     } else {
 	engine->tmpdir = NULL;
     }
 
     if(engine->pua_cats)
 	mpool_free(engine->mempool, engine->pua_cats);
     if(settings->pua_cats) {
 	engine->pua_cats = cli_mpool_strdup(engine->mempool, settings->pua_cats);
 	if(!engine->pua_cats)
 	    return CL_EMEM;
     } else {
 	engine->pua_cats = NULL;
     }
 
     return CL_SUCCESS;
 }
 
 int cl_engine_settings_free(struct cl_settings *settings)
 {
     if(!settings)
 	return CL_ENULLARG;
 
     free(settings->tmpdir);
     free(settings->pua_cats);
     free(settings);
     return CL_SUCCESS;
 }
 
b80ae277
 int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3) {
     int ret = CL_SUCCESS;
     unsigned long needed;
 
     /* if called without limits, go on, unpack, scan */
724b2bf7
     if(!ctx) return CL_CLEAN;
b80ae277
 
     needed = (need1>need2)?need1:need2;
     needed = (needed>need3)?needed:need3;
 
     /* if we have global scan limits */
724b2bf7
     if(needed && ctx->engine->maxscansize) {
b80ae277
         /* if the remaining scansize is too small... */
724b2bf7
         if(ctx->engine->maxscansize-ctx->scansize<needed) {
d91ab809
 	    /* ... we tell the caller to skip this file */
724b2bf7
 	    cli_dbgmsg("%s: scansize exceeded (initial: %lu, remaining: %lu, needed: %lu)\n", who, ctx->engine->maxscansize, ctx->scansize, needed);
b80ae277
 	    ret = CL_EMAXSIZE;
 	}
4ad62d4e
     }
 
b80ae277
     /* if we have per-file size limits, and we are overlimit... */
724b2bf7
     if(needed && ctx->engine->maxfilesize && ctx->engine->maxfilesize<needed) {
d91ab809
 	/* ... we tell the caller to skip this file */
724b2bf7
         cli_dbgmsg("%s: filesize exceeded (allowed: %lu, needed: %lu)\n", who, ctx->engine->maxfilesize, needed);
b80ae277
 	ret = CL_EMAXSIZE;
     }
d91ab809
 
724b2bf7
     if(ctx->engine->maxfiles && ctx->scannedfiles>=ctx->engine->maxfiles) {
         cli_dbgmsg("%s: files limit reached (max: %u)\n", who, ctx->engine->maxfiles);
d91ab809
 	return CL_EMAXFILES;
     }
b80ae277
     return ret;
36582be0
 }
 
d91ab809
 int cli_updatelimits(cli_ctx *ctx, unsigned long needed) {
850db69e
     int ret=cli_checklimits("cli_updatelimits", ctx, needed, 0, 0);
b80ae277
 
850db69e
     if (ret != CL_CLEAN) return ret;
d91ab809
     ctx->scannedfiles++;
     ctx->scansize+=needed;
724b2bf7
     if(ctx->scansize > ctx->engine->maxscansize)
         ctx->scansize = ctx->engine->maxscansize;
850db69e
     return CL_CLEAN;
4ad62d4e
 }
 
db65451b
 unsigned char *cli_md5digest(int desc)
 {
 	unsigned char *digest;
 	char buff[FILEBUFF];
2a9e6ac8
 	cli_md5_ctx ctx;
db65451b
 	int bytes;
 
 
     if(!(digest = cli_malloc(16)))
 	return NULL;
 
2a9e6ac8
     cli_md5_init(&ctx);
db65451b
 
     while((bytes = cli_readn(desc, buff, FILEBUFF)))
2a9e6ac8
 	cli_md5_update(&ctx, buff, bytes);
db65451b
 
2a9e6ac8
     cli_md5_final(digest, &ctx);
db65451b
 
     return digest;
 }
 
335d1663
 char *cli_md5stream(FILE *fs, unsigned char *digcpy)
e3aaff8e
 {
335d1663
 	unsigned char digest[16];
 	char buff[FILEBUFF];
2a9e6ac8
 	cli_md5_ctx ctx;
335d1663
 	char *md5str, *pt;
 	int i, bytes;
 
 
2a9e6ac8
     cli_md5_init(&ctx);
e3aaff8e
 
335d1663
     while((bytes = fread(buff, 1, FILEBUFF, fs)))
2a9e6ac8
 	cli_md5_update(&ctx, buff, bytes);
335d1663
 
2a9e6ac8
     cli_md5_final(digest, &ctx);
335d1663
 
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
e3aaff8e
 
335d1663
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
e3aaff8e
 
335d1663
     if(digcpy)
 	memcpy(digcpy, digest, 16);
e3aaff8e
 
335d1663
     return md5str;
e3aaff8e
 }
 
8000d078
 char *cli_md5file(const char *filename)
d9e258d5
 {
335d1663
 	FILE *fs;
d9e258d5
 	char *md5str;
 
8000d078
 
335d1663
     if((fs = fopen(filename, "rb")) == NULL) {
8000d078
 	cli_errmsg("cli_md5file(): Can't read file %s\n", filename);
 	return NULL;
     }
 
335d1663
     md5str = cli_md5stream(fs, NULL);
     fclose(fs);
d9e258d5
 
335d1663
     return md5str;
d9e258d5
 }
 
c0a95e0c
 /* Function: unlink
         unlink() with error checking
 */
997a0e0b
 int cli_unlink(const char *pathname)
c0a95e0c
 {
997a0e0b
 	if (unlink(pathname)==-1) {
e68d70e7
 	    char err[128];
 	    cli_warnmsg("cli_unlink: failure - %s\n", cli_strerror(errno, err, sizeof(err)));
997a0e0b
 	    return 1;
 	}
 	return 0;
c0a95e0c
 }
 
a946fc1c
 #ifdef	C_WINDOWS
 /*
  * Windows doesn't allow you to delete a directory while it is still open
  */
 int
 cli_rmdirs(const char *name)
 {
 	int rc;
 	struct stat statb;	
 	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
e68d70e7
 	char err[128];
a946fc1c
 
 
     if(stat(name, &statb) < 0) {
e68d70e7
 	cli_warnmsg("cli_rmdirs: Can't locate %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
a946fc1c
 	return -1;
     }
 
     if(!S_ISDIR(statb.st_mode)) {
997a0e0b
 	if(cli_unlink(name)) return -1;
a946fc1c
 	return 0;
     }
 
     if((dd = opendir(name)) == NULL)
 	return -1;
 
     rc = 0;
 
 #ifdef HAVE_READDIR_R_3
     while((readdir_r(dd, &result.d, &dent) == 0) && dent) {
 #elif defined(HAVE_READDIR_R_2)
     while((dent = (struct dirent *)readdir_r(dd, &result.d)) != NULL) {
 #else
     while((dent = readdir(dd)) != NULL) {
 #endif
a7ac5978
 	    char *path;
a946fc1c
 
 	if(strcmp(dent->d_name, ".") == 0)
 	    continue;
 	if(strcmp(dent->d_name, "..") == 0)
 	    continue;
 
a7ac5978
 	path = cli_malloc(strlen(name) + strlen(dent->d_name) + 2);
220d58ba
 
a7ac5978
 	if(path == NULL) {
a946fc1c
 	    closedir(dd);
 	    return -1;
 	}
 
a7ac5978
 	sprintf(path, "%s\\%s", name, dent->d_name);
 	rc = cli_rmdirs(path);
 	free(path);
a946fc1c
 	if(rc != 0)
 	    break;
     }
 
     closedir(dd);
 
     if(rmdir(name) < 0) {
e68d70e7
 	cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
a946fc1c
 	return -1;
     }
 
     return rc;	
 }
 #else
e3aaff8e
 int cli_rmdirs(const char *dirname)
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	struct stat maind, statbuf;
a7ac5978
 	char *path;
e68d70e7
 	char err[128];
e3aaff8e
 
a9ebff44
 
     chmod(dirname, 0700);
e3aaff8e
     if((dd = opendir(dirname)) != NULL) {
 	while(stat(dirname, &maind) != -1) {
 	    if(!rmdir(dirname)) break;
ee212426
 	    if(errno != ENOTEMPTY && errno != EEXIST && errno != EBADF) {
e68d70e7
 		cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", dirname, cli_strerror(errno, err, sizeof(err)));
256e2dd4
 		closedir(dd);
1d670fef
 		return -1;
256e2dd4
 	    }
e3aaff8e
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
 	    while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
 	    while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
 	    while((dent = readdir(dd))) {
72a1b240
 #endif
6670d61d
 #if	(!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 		if(dent->d_ino)
 #endif
 		{
e3aaff8e
 		    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
a7ac5978
 			path = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
 			if(!path) {
a946fc1c
 			    closedir(dd);
 			    return -1;
 			}
 
a7ac5978
 			sprintf(path, "%s/%s", dirname, dent->d_name);
e3aaff8e
 
 			/* stat the file */
a7ac5978
 			if(lstat(path, &statbuf) != -1) {
e3aaff8e
 			    if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
a7ac5978
 				if(rmdir(path) == -1) { /* can't be deleted */
e3aaff8e
 				    if(errno == EACCES) {
a7ac5978
 					cli_errmsg("cli_rmdirs: Can't remove some temporary directories due to access problem.\n");
e3aaff8e
 					closedir(dd);
a7ac5978
 					free(path);
1d670fef
 					return -1;
 				    }
a7ac5978
 				    if(cli_rmdirs(path)) {
 					cli_warnmsg("cli_rmdirs: Can't remove nested directory %s\n", path);
 					free(path);
1d670fef
 					closedir(dd);
 					return -1;
e3aaff8e
 				    }
 				}
997a0e0b
 			    } else {
 				if(cli_unlink(path)) {
 				    free(path);
 				    closedir(dd);
 				    return -1;
 				}
 			    }
e3aaff8e
 			}
a7ac5978
 			free(path);
e3aaff8e
 		    }
 		}
 	    }
 	    rewinddir(dd);
 	}
 
     } else { 
a946fc1c
 	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)
 #define FALSE (0)
 #define TRUE (1)
 
 static unsigned long nearest_power(unsigned long num)
 {
 	unsigned long n = BITSET_DEFAULT_SIZE;
 
 	while (n < num) {
 		n <<= 1;
 		if (n == 0) {
 			return num;
 		}
 	}
 	return n;
 }
 
079229d6
 bitset_t *cli_bitset_init(void)
8a9c2d19
 {
 	bitset_t *bs;
 	
49760905
 	bs = cli_malloc(sizeof(bitset_t));
8a9c2d19
 	if (!bs) {
 		return NULL;
 	}
 	bs->length = BITSET_DEFAULT_SIZE;
49760905
 	bs->bitset = cli_calloc(BITSET_DEFAULT_SIZE, 1);
8a9c2d19
 	return bs;
 }
 
 void cli_bitset_free(bitset_t *bs)
 {
 	if (!bs) {
 		return;
 	}
 	if (bs->bitset) {
 		free(bs->bitset);
 	}
 	free(bs);
 }
 
 static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size)
 {
 	unsigned long new_length;
9358b217
 	unsigned char *new_bitset;
8a9c2d19
 	
 	new_length = nearest_power(min_size);
9358b217
 	new_bitset = (unsigned char *) cli_realloc(bs->bitset, new_length);
 	if (!new_bitset) {
8a9c2d19
 		return NULL;
 	}
9358b217
 	bs->bitset = new_bitset;
8a9c2d19
 	memset(bs->bitset+bs->length, 0, new_length-bs->length);
 	bs->length = new_length;
 	return bs;
 }
 
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset)
 {
 	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;
 }
 
 int cli_bitset_test(bitset_t *bs, unsigned long bit_offset)
 {
 	unsigned long char_offset;
 	
 	char_offset = bit_offset / BITS_PER_CHAR;
 	bit_offset = bit_offset % BITS_PER_CHAR;
7bd10c36
 
 	if (char_offset >= bs->length) {	
 		return FALSE;
 	}
8a9c2d19
 	return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
 }
24555841