libclamav/matcher.c
b151ef55
 /*
34e96745
  *  Copyright (C) 2002 - 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 <string.h>
856d9c84
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
b151ef55
 
 #include "clamav.h"
 #include "others.h"
f91f55e0
 #include "matcher-ac.h"
 #include "matcher-bm.h"
 #include "md5.h"
2fe19b26
 #include "filetypes.h"
df757556
 #include "matcher.h"
856d9c84
 #include "pe.h"
958dc41c
 #include "special.h"
b151ef55
 
f91f55e0
 #define MD5_BLOCKSIZE 4096
b151ef55
 
856d9c84
 #define TARGET_TABLE_SIZE 6
 static int targettab[TARGET_TABLE_SIZE] = { 0, CL_TYPE_MSEXE, CL_TYPE_MSOLE2, CL_TYPE_HTML, CL_TYPE_MAIL, CL_TYPE_GRAPHICS };
df757556
 
9bdb71d0
 extern short cli_debug_flag;
b151ef55
 
538a6756
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
 pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
856d9c84
 int cli_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root, unsigned short ftype)
f91f55e0
 {
 	int ret, *partcnt;
 	unsigned long int *partoff;
b151ef55
 
 
f91f55e0
     if((partcnt = (int *) cli_calloc(root->ac_partsigs + 1, sizeof(int))) == NULL) {
 	cli_dbgmsg("cl_scanbuff(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(int));
 	return CL_EMEM;
b151ef55
     }
 
f91f55e0
     if((partoff = (unsigned long int *) cli_calloc(root->ac_partsigs + 1, sizeof(unsigned long int))) == NULL) {
 	cli_dbgmsg("cl_scanbuff(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(unsigned long int));
 	free(partcnt);
e8217f5a
 	return CL_EMEM;
9c1c9007
     }
b151ef55
 
856d9c84
     if((ret = cli_bm_scanbuff(buffer, length, virname, root, 0, ftype, -1)) != CL_VIRUS)
 	ret = cli_ac_scanbuff(buffer, length, virname, root, partcnt, 0, 0, partoff, ftype, -1);
f91f55e0
 
     free(partcnt);
     free(partoff);
     return ret;
b151ef55
 }
 
856d9c84
 int cl_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root)
 {
     return cli_scanbuff(buffer, length, virname, root, 0);
 }
 
7c0991e0
 static struct cli_md5_node *cli_vermd5(const unsigned char *md5, const struct cl_node *root)
b151ef55
 {
f91f55e0
 	struct cli_md5_node *pt;
b151ef55
 
 
f91f55e0
     if(!(pt = root->md5_hlist[md5[0] & 0xff]))
b151ef55
 	return NULL;
 
f91f55e0
     while(pt) {
 	if(!memcmp(pt->md5, md5, 16))
 	    return pt;
b151ef55
 
f91f55e0
 	pt = pt->next;
e8217f5a
     }
b151ef55
 
f91f55e0
     return NULL;
b151ef55
 }
 
856d9c84
 static long int cli_caloff(const char *offstr, int fd)
 {
 	struct cli_pe_info peinfo;
 	long int offset = -1;
 	int n;
 
 
     if(isdigit(offstr[0])) {
 	return atoi(offstr);
     } if(!strncmp(offstr, "EP+", 3)) {
 	if((n = lseek(fd, 0, SEEK_CUR)) == -1) {
 	    cli_dbgmsg("Invalid descriptor\n");
 	    return -1;
 	}
 	lseek(fd, 0, SEEK_SET);
9bd73bcb
 	if(cli_peheader(fd, &peinfo)) {
 	    lseek(fd, n, SEEK_SET);
856d9c84
 	    return -1;
9bd73bcb
 	}
856d9c84
 	free(peinfo.section);
 	lseek(fd, n, SEEK_SET);
 	return peinfo.ep + atoi(offstr + 3);
     } else if(offstr[0] == 'S') {
 	if((n = lseek(fd, 0, SEEK_CUR)) == -1) {
 	    cli_dbgmsg("Invalid descriptor\n");
 	    return -1;
 	}
 	lseek(fd, 0, SEEK_SET);
9bd73bcb
 	if(cli_peheader(fd, &peinfo)) {
 	    lseek(fd, n, SEEK_SET);
856d9c84
 	    return -1;
9bd73bcb
 	}
856d9c84
 	lseek(fd, n, SEEK_SET);
 
d19fc3f0
 	if(!strncmp(offstr, "SL", 2)) {
856d9c84
 
d19fc3f0
 	    if(sscanf(offstr, "SL+%ld", &offset) != 1) {
 		free(peinfo.section);
 		return -1;
 	    }
 
 	    offset += peinfo.section[peinfo.nsections - 1].raw;
 
 	} else {
 
 	    if(sscanf(offstr, "S%d+%ld", &n, &offset) != 2) {
 		free(peinfo.section);
 		return -1;
 	    }
 
 	    if(n >= peinfo.nsections) {
 		free(peinfo.section);
 		return -1;
 	    }
 
 	    offset += peinfo.section[n].raw;
856d9c84
 	}
 
 	free(peinfo.section);
 	return offset;
d19fc3f0
 
856d9c84
     } else if(!strncmp(offstr, "EOF-", 4)) {
 	    struct stat sb;
 
 	if(fstat(fd, &sb) == -1)
 	    return -1;
 
 	return sb.st_size - atoi(offstr + 4);
     }
 
     return -1;
 }
 
3f66a5af
 int cli_checkfp(int fd, const struct cl_node *root)
 {
 	struct cli_md5_node *md5_node;
 	char *digest;
 
 
     if(root->md5_hlist) {
 
 	if(!(digest = cli_md5digest(fd))) {
 	    cli_errmsg("cli_checkfp(): Can't generate MD5 checksum\n");
 	    return 0;
 	}
 
 	if((md5_node = cli_vermd5(digest, root)) && md5_node->fp) {
 		struct stat sb;
 
 	    if(fstat(fd, &sb))
 		return CL_EIO;
 
a8b53539
 	    if((unsigned int) sb.st_size != md5_node->size) {
3f66a5af
 		cli_warnmsg("Detected false positive MD5 match. Please report.\n");
 	    } else {
 		cli_dbgmsg("Eliminated false positive match (fp sig: %s)\n", md5_node->virname);
 		free(digest);
 		return 1;
 	    }
 	}
 
 	free(digest);
     }
 
     return 0;
 }
 
856d9c84
 int cli_validatesig(unsigned short target, unsigned short ftype, const char *offstr, unsigned long int fileoff, int desc, const char *virname)
 {
 
40e3161f
 
856d9c84
     if(target) {
 	if(target >= TARGET_TABLE_SIZE) {
 	    cli_errmsg("Bad target in signature (%s)\n", virname);
 	    return 0;
40e3161f
 	} else {
856d9c84
 	    if(targettab[target] != ftype) {
 		cli_dbgmsg("Type: %d, expected: %d (%s)\n", ftype, targettab[target], virname);
 		return 0;
 	    }
 	} 
 
     }
 
     if(offstr && desc != -1) {
 	    long int off = cli_caloff(offstr, desc);
 
 	if(off == -1) {
 	    cli_dbgmsg("Bad offset in signature (%s)\n", virname);
 	    return 0;
 	}
 
a8b53539
 	if(fileoff != (unsigned long int) off) {
856d9c84
 	    cli_dbgmsg("Virus offset: %d, expected: %d (%s)\n", fileoff, off, virname);
 	    return 0;
 	}
     }
 
     return 1;
 }
 
df757556
 int cli_scandesc(int desc, const char **virname, long int *scanned, const struct cl_node *root, short otfrec, unsigned short ftype)
b151ef55
 {
f91f55e0
  	char *buffer, *buff, *endbl, *pt;
 	int bytes, buffsize, length, ret, *partcnt, type = CL_CLEAN;
 	unsigned long int *partoff, offset = 0;
63a83426
 	MD5_CTX ctx;
d2a12ffd
 	unsigned char digest[16];
f91f55e0
 	struct cli_md5_node *md5_node;
2fe19b26
 
 
f91f55e0
     if(!root) {
 	cli_errmsg("cli_scandesc: root == NULL\n");
 	return CL_ENULLARG;
ff8cb48b
     }
 
f91f55e0
     /* prepare the buffer */
     buffsize = root->maxpatlen + SCANBUFF;
     if(!(buffer = (char *) cli_calloc(buffsize, sizeof(char)))) {
a0231a19
 	cli_dbgmsg("cli_scandesc(): unable to cli_calloc(%d)\n", buffsize);
f91f55e0
 	return CL_EMEM;
     }
2fe19b26
 
f91f55e0
     if((partcnt = (int *) cli_calloc(root->ac_partsigs + 1, sizeof(int))) == NULL) {
 	cli_dbgmsg("cli_scandesc(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(int));
 	free(buffer);
 	return CL_EMEM;
     }
b151ef55
 
f91f55e0
     if((partoff = (unsigned long int *) cli_calloc(root->ac_partsigs + 1, sizeof(unsigned long int))) == NULL) {
856d9c84
 	cli_dbgmsg("cli_scandesc(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(unsigned long int));
f91f55e0
 	free(buffer);
 	free(partcnt);
 	return CL_EMEM;
     }
b151ef55
 
f91f55e0
     if(root->md5_hlist)
63a83426
 	MD5_Init(&ctx);
b151ef55
 
9ce8ad74
 
f91f55e0
     buff = buffer;
     buff += root->maxpatlen; /* pointer to read data block */
     endbl = buff + SCANBUFF - root->maxpatlen; /* pointer to the last block
 						* length of root->maxpatlen
 						*/
b151ef55
 
d2a12ffd
     pt = buff;
f91f55e0
     length = SCANBUFF;
     while((bytes = read(desc, buff, SCANBUFF)) > 0) {
b151ef55
 
f91f55e0
 	if(scanned)
 	    *scanned += bytes / CL_COUNT_PRECISION;
b151ef55
 
f91f55e0
 	if(bytes < SCANBUFF)
 	    length -= SCANBUFF - bytes;
ff8cb48b
 
856d9c84
 	if(cli_bm_scanbuff(pt, length, virname, root, offset, ftype, desc) == CL_VIRUS ||
 	   (ret = cli_ac_scanbuff(pt, length, virname, root, partcnt, otfrec, offset, partoff, ftype, desc)) == CL_VIRUS) {
f91f55e0
 	    free(buffer);
 	    free(partcnt);
 	    free(partoff);
3f66a5af
 
 	    lseek(desc, 0, SEEK_SET);
 	    if(cli_checkfp(desc, root))
 		return CL_CLEAN;
 	    else
 		return CL_VIRUS;
b151ef55
 
df757556
 	} else if(otfrec && ret >= CL_TYPENO) {
f91f55e0
 	    if(ret >= type)
 		type = ret;
 	}
b151ef55
 
f91f55e0
 	if(bytes == SCANBUFF) {
 	    memmove(buffer, endbl, root->maxpatlen);
1f73f3ff
 
e483f51a
 	    if(pt == buffer) {
 		offset += SCANBUFF;
 	    } else {
 		offset += SCANBUFF - root->maxpatlen;
 		pt = buffer;
 		length = buffsize;
 	    }
 	}
d898865b
 
d2a12ffd
 	if(root->md5_hlist)
63a83426
 	    MD5_Update(&ctx, buff, bytes);
f91f55e0
     }
d898865b
 
f91f55e0
     free(buffer);
     free(partcnt);
     free(partoff);
1f73f3ff
 
f91f55e0
     if(root->md5_hlist) {
63a83426
 	MD5_Final(digest, &ctx);
1f73f3ff
 
9bdb71d0
 	if(cli_debug_flag) {
 		char md5str[33];
 		int i;
 
 	    pt = md5str;
 	    for(i = 0; i < 16; i++) {
 		sprintf(pt, "%02x", digest[i]);
 		pt += 2;
 	    }
 	    md5str[32] = 0;
 	    cli_dbgmsg("Calculated MD5 checksum: %s\n", md5str);
 	}
 
3f66a5af
 	if((md5_node = cli_vermd5(digest, root)) && !md5_node->fp) {
61f31052
 		struct stat sb;
 
 	    if(fstat(desc, &sb))
 		return CL_EIO;
 
a8b53539
 	    if((unsigned int) sb.st_size != md5_node->size) {
61f31052
 		cli_warnmsg("Detected false positive MD5 match. Please report.\n");
 	    } else {
 		if(virname)
 		    *virname = md5_node->virname;
 
 		return CL_VIRUS;
 	    }
f91f55e0
 	}
1f73f3ff
     }
 
df757556
     return otfrec ? type : CL_CLEAN;
1f73f3ff
 }
 
f91f55e0
 int cl_build(struct cl_node *root)
b151ef55
 {
f91f55e0
     return cli_ac_buildtrie(root);
 }
b151ef55
 
538a6756
 struct cl_node *cl_dup(struct cl_node *root)
 {
     if(!root) {
 	cli_errmsg("cl_dup: root == NULL\n");
 	return NULL;
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     root->refcount++;
     
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
     return root;
 }
 
f91f55e0
 void cl_free(struct cl_node *root)
 {
 	int i;
34e96745
 	struct cli_md5_node *md5pt, *md5h;
92152740
 	struct cli_meta_node *metapt, *metah;
b151ef55
 
f91f55e0
     if(!root) {
 	cli_errmsg("cl_free: root == NULL\n");
 	return;
9c1c9007
     }
 
538a6756
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     root->refcount--;
     if (root->refcount) {
 #ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 	return;
     }
     
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
f91f55e0
     cli_ac_free(root);
     cli_bm_free(root);
b151ef55
 
f91f55e0
     if(root->md5_hlist) {
 	for(i = 0; i < 256; i++) {
34e96745
 	    md5pt = root->md5_hlist[i];
 	    while(md5pt) {
 		md5h = md5pt;
 		md5pt = md5pt->next;
 		free(md5h->md5);
 		free(md5h->virname);
 		if(md5h->viralias)
 		    free(md5h->viralias);
 		free(md5h);
b151ef55
 	    }
 	}
f91f55e0
 	free(root->md5_hlist);
b151ef55
     }
 
92152740
     metapt = root->zip_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
 	free(metah->virname);
 	if(metah->filename)
 	    free(metah->filename);
 	free(metah);
     }
 
     metapt = root->rar_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
 	free(metah->virname);
 	if(metah->filename)
 	    free(metah->filename);
 	free(metah);
34e96745
     }
 
f91f55e0
     free(root);
b151ef55
 }
 
f91f55e0
 int cl_buildtrie(struct cl_node *root) /* for backward compatibility */
fdfb0dd7
 {
f91f55e0
     return cl_build(root);
 }
cc938d61
 
f91f55e0
 void cl_freetrie(struct cl_node *root) /* for backward compatibility */
 {
7c0991e0
     cl_free(root);
fdfb0dd7
 }