libclamav/others.c
b151ef55
 /*
eedd6182
  *  Copyright (C) 1999 - 2005 Tomasz Kojm <tkojm@clamav.net>
b151ef55
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
 
8b242bb9
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b151ef55
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <dirent.h>
 #include <time.h>
 #include <fcntl.h>
 #include <pwd.h>
 #include <errno.h>
 #include <target.h>
c4f87775
 #include <sys/time.h>
771e8818
 #include <sys/param.h>
b151ef55
 
0d01fcb2
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
f91f55e0
 pthread_mutex_t cli_gentemp_mutex = PTHREAD_MUTEX_INITIALIZER;
0d01fcb2
 #endif
 
a45ec9cc
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
8c7e16b8
 #include <stddef.h>
a45ec9cc
 #endif
 
b151ef55
 #include "clamav.h"
 #include "others.h"
 #include "md5.h"
fbbf7cd2
 #include "cltypes.h"
b151ef55
 
771e8818
 /* Maximum filenames under various systems - njh */
 #ifndef	NAME_MAX	/* e.g. Linux */
 # ifdef	MAXNAMELEN	/* e.g. Solaris */
 #   define	NAME_MAX	MAXNAMELEN
 # else
 #   ifdef	FILENAME_MAX	/* e.g. SCO */
 #     define	NAME_MAX	FILENAME_MAX
8425b1a6
 #   else
 #     define	NAME_MAX	256
771e8818
 #   endif
 # endif
 #endif
 
ed4b4196
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
f0635204
 #define CL_FLEVEL 8 /* don't touch it */
ead674a2
 
3506c157
 short cli_debug_flag = 0, cli_leavetemps_flag = 0;
0bcad2b1
 
c4f87775
 static unsigned char oldmd5buff[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
 
1ae303c2
 
b151ef55
 void cli_warnmsg(const char *str, ...)
 {
 	va_list args;
1ae303c2
 	int sz = sizeof("LibClamAV Warning: ") - 1;
 	char buff[256];
b151ef55
 
1ae303c2
     strncpy(buff, "LibClamAV Warning: ", sz);
b151ef55
     va_start(args, str);
1ae303c2
     vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
     buff[sizeof(buff) - 1] = '\0';
     fputs(buff, stderr);
b151ef55
     va_end(args);
 }
 
 void cli_errmsg(const char *str, ...)
 {
 	va_list args;
1ae303c2
 	int sz = sizeof("LibClamAV Error: ") - 1;
 	char buff[256];
b151ef55
 
1ae303c2
     strncpy(buff, "LibClamAV Error: ", sz);
b151ef55
     va_start(args, str);
1ae303c2
     vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
     buff[sizeof(buff) - 1] = '\0';
     fputs(buff, stderr);
b151ef55
     va_end(args);
 }
 
 void cli_dbgmsg(const char *str, ...)
 {
 
0bcad2b1
     if(cli_debug_flag) {
1ae303c2
 	    va_list args;
 	    int sz = sizeof("LibClamAV debug: ") - 1;
 	    char buff[256];
 
 	memcpy(buff, "LibClamAV debug: ", sz);
0bcad2b1
 	va_start(args, str);
1ae303c2
 	vsnprintf(buff + sz, sizeof(buff) - sz, str, args);
 	buff[sizeof(buff) - 1] = '\0';
 	fputs(buff, stderr);
0bcad2b1
 	va_end(args);
     } else
 	return;
 }
 
 void cl_debug(void)
 {
     cli_debug_flag = 1;
b151ef55
 }
 
ead674a2
 int cl_retflevel(void)
 {
     return CL_FLEVEL;
 }
 
5e8d60b3
 const char *cl_retver(void)
 {
     return VERSION;
 }
 
41b894c7
 const char *cl_strerror(int clerror)
b151ef55
 {
     switch(clerror) {
 	case CL_CLEAN:
e3886d61
 	    return "No viruses detected";
b151ef55
 	case CL_VIRUS:
e3886d61
 	    return "Virus(es) detected";
b151ef55
 	case CL_EMAXREC:
e3886d61
 	    return "Recursion limit exceeded";
b151ef55
 	case CL_EMAXSIZE:
e3886d61
 	    return "File size limit exceeded";
b151ef55
 	case CL_EMAXFILES:
e3886d61
 	    return "Files number limit exceeded";
b151ef55
 	case CL_ERAR:
e3886d61
 	    return "RAR module failure";
b151ef55
 	case CL_EZIP:
e3886d61
 	    return "Zip module failure";
b151ef55
 	case CL_EMALFZIP:
e3886d61
 	    return "Malformed Zip detected";
b151ef55
 	case CL_EGZIP:
e3886d61
 	    return "GZip module failure";
e2dc6ace
 	case CL_EMSCOMP:
e3886d61
 	    return "MS Expand module failure";
1ed6a845
 	case CL_EMSCAB:
e3886d61
 	    return "MS CAB module failure";
c561d2a3
 	case CL_EOLE2:
e3886d61
 	    return "OLE2 module failure";
b151ef55
 	case CL_ETMPFILE:
e3886d61
 	    return "Unable to create temporary file";
b151ef55
 	case CL_ETMPDIR:
e3886d61
 	    return "Unable to create temporary directory";
b151ef55
 	case CL_EFSYNC:
e3886d61
 	    return "Unable to synchronize file <-> disk";
b151ef55
 	case CL_EMEM:
e3886d61
 	    return "Unable to allocate memory";
b151ef55
 	case CL_EOPEN:
e3886d61
 	    return "Unable to open file or directory";
b151ef55
 	case CL_EMALFDB:
e3886d61
 	    return "Malformed database";
b151ef55
 	case CL_EPATSHORT:
e3886d61
 	    return "Too short pattern detected";
183ee7e7
 	case CL_ECVD:
e3886d61
 	    return "Broken or not a CVD file";
4cd4319e
 	case CL_ECVDEXTR:
e3886d61
 	    return "CVD extraction failure";
183ee7e7
 	case CL_EMD5:
e3886d61
 	    return "MD5 verification error";
183ee7e7
 	case CL_EDSIG:
e3886d61
 	    return "Digital signature verification error";
b151ef55
 	case CL_ENULLARG:
e3886d61
 	    return "Null argument passed while initialized is required";
 	case CL_EIO:
 	    return "Input/Output error";
b49235bb
 	case CL_EFORMAT:
 	    return "Bad format or broken data";
b151ef55
 	default:
e3886d61
 	    return "Unknown error code";
b151ef55
     }
 }
 
41b894c7
 const char *cl_perror(int clerror)
c6259ac5
 {
     return cl_strerror(clerror);
 }
 
3f66a5af
 unsigned char *cli_md5digest(int desc)
 {
 	unsigned char *digest;
 	char buff[FILEBUFF];
 	MD5_CTX ctx;
 	int bytes;
 
 
     if(!(digest = cli_malloc(16)))
 	return NULL;
 
     MD5_Init(&ctx);
 
     while((bytes = cli_readn(desc, buff, FILEBUFF)))
 	MD5_Update(&ctx, buff, bytes);
 
     MD5_Final(digest, &ctx);
 
     return digest;
 }
 
d2a12ffd
 char *cli_md5stream(FILE *fs, unsigned char *digcpy)
b151ef55
 {
d2a12ffd
 	unsigned char digest[16];
 	char buff[FILEBUFF];
63a83426
 	MD5_CTX ctx;
d2a12ffd
 	char *md5str, *pt;
 	int i, bytes;
 
 
63a83426
     MD5_Init(&ctx);
b151ef55
 
d2a12ffd
     while((bytes = fread(buff, 1, FILEBUFF, fs)))
63a83426
 	MD5_Update(&ctx, buff, bytes);
d2a12ffd
 
63a83426
     MD5_Final(digest, &ctx);
d2a12ffd
 
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
b151ef55
 
d2a12ffd
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
b151ef55
 
d2a12ffd
     if(digcpy)
 	memcpy(digcpy, digest, 16);
b151ef55
 
d2a12ffd
     return md5str;
b151ef55
 }
 
f91f55e0
 char *cli_md5file(const char *filename)
183ee7e7
 {
d2a12ffd
 	FILE *fs;
183ee7e7
 	char *md5str;
 
f91f55e0
 
d2a12ffd
     if((fs = fopen(filename, "rb")) == NULL) {
f91f55e0
 	cli_errmsg("cli_md5file(): Can't read file %s\n", filename);
 	return NULL;
     }
 
d2a12ffd
     md5str = cli_md5stream(fs, NULL);
     fclose(fs);
183ee7e7
 
d2a12ffd
     return md5str;
183ee7e7
 }
 
f91f55e0
 static char *cli_md5buff(const char *buffer, unsigned int len)
b151ef55
 {
d2a12ffd
 	unsigned char digest[16];
 	char *md5str, *pt;
63a83426
 	MD5_CTX ctx;
d2a12ffd
 	int i;
b151ef55
 
 
63a83426
     MD5_Init(&ctx);
     MD5_Update(&ctx, (unsigned char *) buffer, len);
     MD5_Final(digest, &ctx);
d2a12ffd
     memcpy(oldmd5buff, digest, 16);
b151ef55
 
d2a12ffd
     if(!(md5str = (char *) cli_calloc(32 + 1, sizeof(char))))
 	return NULL;
b151ef55
 
d2a12ffd
     pt = md5str;
     for(i = 0; i < 16; i++) {
 	sprintf(pt, "%02x", digest[i]);
 	pt += 2;
     }
b151ef55
 
d2a12ffd
     return md5str;
b151ef55
 }
 
 void *cli_malloc(size_t size)
 {
 	void *alloc;
 
599f27c8
 
e82a5185
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("Attempt to allocate %u bytes. Please report to bugs@clamav.net\n", size);
599f27c8
 	return NULL;
     }
 
b151ef55
     alloc = malloc(size);
 
     if(!alloc) {
e82a5185
 	cli_errmsg("cli_malloc(): Can't allocate memory (%u bytes).\n", size);
b151ef55
 	perror("malloc_problem");
fdfb0dd7
 	/* _exit(1); */
b151ef55
 	return NULL;
     } else return alloc;
 }
 
 void *cli_calloc(size_t nmemb, size_t size)
 {
 	void *alloc;
 
e8c623dd
 
e82a5185
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("Attempt to allocate %u bytes. Please report to bugs@clamav.net\n", size);
599f27c8
 	return NULL;
     }
 
b151ef55
     alloc = calloc(nmemb, size);
 
     if(!alloc) {
e82a5185
 	cli_errmsg("cli_calloc(): Can't allocate memory (%u bytes).\n", nmemb * size);
b151ef55
 	perror("calloc_problem");
fdfb0dd7
 	/* _exit(1); */
b151ef55
 	return NULL;
     } else return alloc;
 }
 
4cd4319e
 void *cli_realloc(void *ptr, size_t size)
 {
 	void *alloc;
 
599f27c8
 
e82a5185
     if(!size || size > CLI_MAX_ALLOCATION) {
 	cli_errmsg("Attempt to allocate %u bytes. Please report to bugs@clamav.net\n", size);
c840fa41
 	return NULL;
     }
 
4cd4319e
     alloc = realloc(ptr, size);
 
     if(!alloc) {
e82a5185
 	cli_errmsg("cli_realloc(): Can't re-allocate memory to %u byte.\n", size);
4cd4319e
 	perror("realloc_problem");
 	return NULL;
     } else return alloc;
 }
 
f91f55e0
 unsigned int cli_rndnum(unsigned int max)
b151ef55
 {
     struct timeval tv;
 
   gettimeofday(&tv, (struct timezone *) 0);
   srand(tv.tv_usec+clock());
 
   return rand() % max;
 }
 
3506c157
 void cl_settempdir(const char *dir, short leavetemps)
 {
 	char *var;
 
     if(dir) {
 	var = (char *) cli_malloc(8 + strlen(dir));
 	sprintf(var, "TMPDIR=%s", dir);
 	if(!putenv(var))
 	    cli_dbgmsg("Setting %s as global temporary directory\n", dir);
 	else
 	    cli_warnmsg("Can't set TMPDIR variable - insufficient space in the environment.\n");
 
 	/* WARNING: var must not be released - see putenv(3) */
     }
 
     cli_leavetemps_flag = leavetemps;
 }
 
f91f55e0
 char *cli_gentemp(const char *dir)
b151ef55
 {
41b894c7
 	char *name, *tmp;
         const char *mdir;
c4f87775
 	unsigned char salt[16 + 32];
 	int i;
b151ef55
 	struct stat foo;
 
c4f87775
 
e5fa5bab
     if(!dir) {
 	if((mdir = getenv("TMPDIR")) == NULL)
 #ifdef P_tmpdir
 	    mdir = P_tmpdir;
 #else
 	    mdir = "/tmp";
 #endif
     } else
 	mdir = dir;
b151ef55
 
b5ad6489
     name = (char*) cli_calloc(strlen(mdir) + 1 + 16 + 1 + 7, sizeof(char));
e8217f5a
     if(name == NULL) {
f91f55e0
 	cli_dbgmsg("cli_gentemp('%s'): out of memory\n", dir);
e8217f5a
 	return NULL;
     }
c4f87775
 
 #ifdef CL_THREAD_SAFE
f91f55e0
     pthread_mutex_lock(&cli_gentemp_mutex);
c4f87775
 #endif
 
     memcpy(salt, oldmd5buff, 16);
b151ef55
 
     do {
c4f87775
 	for(i = 16; i < 48; i++)
f91f55e0
 	    salt[i] = cli_rndnum(255);
b151ef55
 
f91f55e0
 	tmp = cli_md5buff(( char* ) salt, 48);
b5ad6489
 	sprintf(name, "%s/clamav-", mdir);
7cc3891c
 	strncat(name, tmp, 16);
b151ef55
 	free(tmp);
     } while(stat(name, &foo) != -1);
 
c4f87775
 #ifdef CL_THREAD_SAFE
f91f55e0
     pthread_mutex_unlock(&cli_gentemp_mutex);
c4f87775
 #endif
 
b151ef55
     return(name);
 }
 
 int cli_rmdirs(const char *dirname)
 {
 	DIR *dd;
 	struct dirent *dent;
ec748835
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
8c7e16b8
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
a45ec9cc
 #endif
b151ef55
 	struct stat maind, statbuf;
 	char *fname;
 
521b19b4
 
     chmod(dirname, 0700);
b151ef55
     if((dd = opendir(dirname)) != NULL) {
 	while(stat(dirname, &maind) != -1) {
 	    if(!rmdir(dirname)) break;
d18eac06
 	    if(errno != ENOTEMPTY && errno != EEXIST && errno != EBADF) {
c840fa41
 		cli_errmsg("Can't remove temporary directory %s: %s\n", dirname, strerror(errno));
 		closedir(dd);
 		return 0;
 	    }
b151ef55
 
ec748835
 #ifdef HAVE_READDIR_R_3
8c7e16b8
 	    while(!readdir_r(dd, &result.d, &dent) && dent) {
ec748835
 #elif defined(HAVE_READDIR_R_2)
8c7e16b8
 	    while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
ec748835
 #else
b151ef55
 	    while((dent = readdir(dd))) {
ec748835
 #endif
a6d49269
 #if ((!defined(C_CYGWIN)) && (!defined(C_INTERIX)))
618a038b
 		if(dent->d_ino)
 #endif
 		{
b151ef55
 		    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
 			fname = cli_calloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char));
 			sprintf(fname, "%s/%s", dirname, dent->d_name);
 
 			/* stat the file */
 			if(lstat(fname, &statbuf) != -1) {
 			    if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
 				if(rmdir(fname) == -1) { /* can't be deleted */
 				    if(errno == EACCES) {
 					cli_errmsg("Can't remove some temporary directories due to access problem.\n");
 					closedir(dd);
c6259ac5
 					free(fname);
b151ef55
 					return 0;
 				    }
 				    cli_rmdirs(fname);
 				}
 			    } else
 				unlink(fname);
 			}
 
 			free(fname);
 		    }
 		}
 	    }
 
 	    rewinddir(dd);
 
 	}
 
     } else { 
 	return 53;
     }
 
     closedir(dd);
     return 0;
 }
66fcd9f8
 
 /* Function: readn
         Try hard to read the requested number of bytes
 */
 int cli_readn(int fd, void *buff, unsigned int count)
 {
         int retval;
         unsigned int todo;
         unsigned char *current;
3fc8c606
 
 
66fcd9f8
         todo = count;
         current = (unsigned char *) buff;
3fc8c606
 
66fcd9f8
         do {
                 retval = read(fd, current, todo);
                 if (retval == 0) {
                         return (count - todo);
                 }
                 if (retval < 0) {
b7fe0c97
 			if (errno == EINTR) {
 				continue;
 			}
66fcd9f8
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
3fc8c606
 
 
66fcd9f8
         return count;
 }
3fc8c606
 
66fcd9f8
 /* Function: writen
         Try hard to write the specified number of bytes
 */
 int cli_writen(int fd, void *buff, unsigned int count)
 {
         int retval;
         unsigned int todo;
         unsigned char *current;
3fc8c606
 
 
66fcd9f8
         todo = count;
         current = (unsigned char *) buff;
3fc8c606
 
66fcd9f8
         do {
                 retval = write(fd, current, todo);
                 if (retval < 0) {
b7fe0c97
 			if (errno == EINTR) {
 				continue;
 			}
66fcd9f8
                         return -1;
                 }
                 todo -= retval;
                 current += retval;
         } while (todo > 0);
3fc8c606
 
 
66fcd9f8
         return count;
 }
 
fbbf7cd2
 int32_t cli_readint32(const char *buff)
 {
f91f55e0
 	int32_t ret;
fbbf7cd2
 
 #if WORDS_BIGENDIAN == 0
     ret = *(int32_t *) buff;
 #else
b3171bcd
     ret = buff[0] & 0xff;
     ret |= (buff[1] & 0xff) << 8;
     ret |= (buff[2] & 0xff) << 16;
     ret |= (buff[3] & 0xff) << 24;
fbbf7cd2
 #endif
 
     return ret;
 }
00cd2a7b
 
b3171bcd
 void cli_writeint32(char *offset, uint32_t value)
 {
     offset[0] = value & 0xff;
     offset[1] = (value & 0xff00) >> 8;
     offset[2] = (value & 0xff0000) >> 16;
     offset[3] = (value & 0xff000000) >> 24;
 }
 
ed4b4196
 int cli_filecopy(const char *src, const char *dest)
 {
 	char *buffer;
 	int s, d, bytes;
 
 
     if((s = open(src, O_RDONLY)) == -1)
 	return -1;
 
     if((d = open(dest, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)) == -1) {
 	close(s);
 	return -1;
     }
 
     if(!(buffer = cli_malloc(FILEBUFF)))
 	return -1;
 
     while((bytes = cli_readn(s, buffer, FILEBUFF)) > 0)
 	cli_writen(d, buffer, bytes);
 
     free(buffer);
     close(s);
 
     return close(d);
 }
55b1a5f8
 
 /* Implement a generic bitset, trog@clamav.net */
 
 #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;
 }
 
 bitset_t *cli_bitset_init()
 {
 	bitset_t *bs;
 	
 	bs = cli_malloc(sizeof(bitset_t));
 	if (!bs) {
 		return NULL;
 	}
 	bs->length = BITSET_DEFAULT_SIZE;
 	bs->bitset = cli_calloc(BITSET_DEFAULT_SIZE, 1);
 	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;
 	
 	new_length = nearest_power(min_size);
 	bs->bitset = (unsigned char *) cli_realloc(bs->bitset, new_length);
 	if (!bs->bitset) {
 		return NULL;
 	}
 	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;
e82a5185
 
 	if (char_offset >= bs->length) {
 		return FALSE;
 	}
 
55b1a5f8
 	return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
 }