clamav-devel/libclamav/matcher.c
e3aaff8e
 /*
ed026d36
  *  Copyright (C) 2002 - 2004 Tomasz Kojm <tkojm@clamav.net>
e3aaff8e
  *
  *  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.
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <string.h>
7ec67e94
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
e3aaff8e
 
 #include "clamav.h"
 #include "others.h"
8000d078
 #include "matcher-ac.h"
 #include "matcher-bm.h"
 #include "md5.h"
888f5794
 #include "filetypes.h"
b68d11d2
 #include "matcher.h"
7ec67e94
 #include "pe.h"
e3aaff8e
 
8000d078
 #define MD5_BLOCKSIZE 4096
e3aaff8e
 
7ec67e94
 #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 };
b68d11d2
 
e3aaff8e
 
7ec67e94
 int cli_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root, unsigned short ftype)
8000d078
 {
 	int ret, *partcnt;
 	unsigned long int *partoff;
e3aaff8e
 
 
8000d078
     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;
e3aaff8e
     }
 
8000d078
     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);
2d70a403
 	return CL_EMEM;
9e431a95
     }
e3aaff8e
 
7ec67e94
     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);
8000d078
 
     free(partcnt);
     free(partoff);
     return ret;
e3aaff8e
 }
 
7ec67e94
 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);
 }
 
058ccefe
 static struct cli_md5_node *cli_vermd5(const unsigned char *md5, const struct cl_node *root)
e3aaff8e
 {
8000d078
 	struct cli_md5_node *pt;
e3aaff8e
 
 
8000d078
     if(!(pt = root->md5_hlist[md5[0] & 0xff]))
e3aaff8e
 	return NULL;
 
8000d078
     while(pt) {
 	if(!memcmp(pt->md5, md5, 16))
 	    return pt;
e3aaff8e
 
8000d078
 	pt = pt->next;
2d70a403
     }
e3aaff8e
 
8000d078
     return NULL;
e3aaff8e
 }
 
7ec67e94
 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);
015e31e1
 	if(cli_peheader(fd, &peinfo)) {
 	    lseek(fd, n, SEEK_SET);
7ec67e94
 	    return -1;
015e31e1
 	}
7ec67e94
 	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);
015e31e1
 	if(cli_peheader(fd, &peinfo)) {
 	    lseek(fd, n, SEEK_SET);
7ec67e94
 	    return -1;
015e31e1
 	}
7ec67e94
 	lseek(fd, n, SEEK_SET);
 
 	if(sscanf(offstr, "S%d+%ld", &n, &offset) != 2)
 	    return -1;
 
 	if(n >= peinfo.nsections) {
 	    free(peinfo.section);
 	    return -1;
 	}
 
 	offset += peinfo.section[n].raw;
 	free(peinfo.section);
 	return offset;
     } 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;
 }
 
 int cli_validatesig(unsigned short target, unsigned short ftype, const char *offstr, unsigned long int fileoff, int desc, const char *virname)
 {
 
     if(target) {
 	if(target >= TARGET_TABLE_SIZE) {
 	    cli_errmsg("Bad target in signature (%s)\n", virname);
 	    return 0;
 	} else if(ftype) {
 	    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;
 	}
 
 	if(fileoff != off) {
 	    cli_dbgmsg("Virus offset: %d, expected: %d (%s)\n", fileoff, off, virname);
 	    return 0;
 	}
     }
 
     return 1;
 }
 
b68d11d2
 int cli_scandesc(int desc, const char **virname, long int *scanned, const struct cl_node *root, short otfrec, unsigned short ftype)
e3aaff8e
 {
8000d078
  	char *buffer, *buff, *endbl, *pt;
 	int bytes, buffsize, length, ret, *partcnt, type = CL_CLEAN;
 	unsigned long int *partoff, offset = 0;
335d1663
 	struct MD5Context ctx;
 	unsigned char digest[16];
8000d078
 	struct cli_md5_node *md5_node;
888f5794
 
 
8000d078
     if(!root) {
 	cli_errmsg("cli_scandesc: root == NULL\n");
 	return CL_ENULLARG;
4048c4f6
     }
 
8000d078
     /* prepare the buffer */
     buffsize = root->maxpatlen + SCANBUFF;
     if(!(buffer = (char *) cli_calloc(buffsize, sizeof(char)))) {
 	cli_dbgmsg("cli_scandesc(): unable to cli_malloc(%d)\n", buffsize);
 	return CL_EMEM;
     }
888f5794
 
8000d078
     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;
     }
e3aaff8e
 
8000d078
     if((partoff = (unsigned long int *) cli_calloc(root->ac_partsigs + 1, sizeof(unsigned long int))) == NULL) {
7ec67e94
 	cli_dbgmsg("cli_scandesc(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(unsigned long int));
8000d078
 	free(buffer);
 	free(partcnt);
 	return CL_EMEM;
     }
e3aaff8e
 
8000d078
     if(root->md5_hlist)
335d1663
 	MD5Init(&ctx);
e3aaff8e
 
cfeb200c
 
8000d078
     buff = buffer;
     buff += root->maxpatlen; /* pointer to read data block */
     endbl = buff + SCANBUFF - root->maxpatlen; /* pointer to the last block
 						* length of root->maxpatlen
 						*/
e3aaff8e
 
335d1663
     pt = buff;
8000d078
     length = SCANBUFF;
     while((bytes = read(desc, buff, SCANBUFF)) > 0) {
e3aaff8e
 
8000d078
 	if(scanned)
 	    *scanned += bytes / CL_COUNT_PRECISION;
e3aaff8e
 
8000d078
 	if(bytes < SCANBUFF)
 	    length -= SCANBUFF - bytes;
4048c4f6
 
7ec67e94
 	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) {
8000d078
 	    free(buffer);
 	    free(partcnt);
 	    free(partoff);
 	    return CL_VIRUS;
e3aaff8e
 
b68d11d2
 	} else if(otfrec && ret >= CL_TYPENO) {
8000d078
 	    if(ret >= type)
 		type = ret;
 	}
e3aaff8e
 
8000d078
 	if(bytes == SCANBUFF) {
 	    memmove(buffer, endbl, root->maxpatlen);
 	    offset += bytes - root->maxpatlen;
 	}
fc8e8ca2
 
8000d078
         pt = buffer;
         length = buffsize;
084ee140
 
335d1663
 	if(root->md5_hlist)
 	    MD5Update(&ctx, buff, bytes);
8000d078
     }
084ee140
 
8000d078
     free(buffer);
     free(partcnt);
     free(partoff);
fc8e8ca2
 
8000d078
     if(root->md5_hlist) {
335d1663
 	MD5Final(digest, &ctx);
fc8e8ca2
 
335d1663
 	if((md5_node = cli_vermd5(digest, root))) {
022a21cf
 		struct stat sb;
 
 	    if(fstat(desc, &sb))
 		return CL_EIO;
 
 	    if(sb.st_size != md5_node->size) {
 		cli_warnmsg("Detected false positive MD5 match. Please report.\n");
 	    } else {
 		if(virname)
 		    *virname = md5_node->virname;
 
 		return CL_VIRUS;
 	    }
8000d078
 	}
fc8e8ca2
     }
 
b68d11d2
     return otfrec ? type : CL_CLEAN;
fc8e8ca2
 }
 
8000d078
 int cl_build(struct cl_node *root)
e3aaff8e
 {
8000d078
     return cli_ac_buildtrie(root);
 }
e3aaff8e
 
8000d078
 void cl_free(struct cl_node *root)
 {
 	int i;
 	struct cli_md5_node *pt, *h;
ed026d36
 
e3aaff8e
 
8000d078
     if(!root) {
 	cli_errmsg("cl_free: root == NULL\n");
 	return;
9e431a95
     }
 
8000d078
     cli_ac_free(root);
     cli_bm_free(root);
e3aaff8e
 
8000d078
     if(root->md5_hlist) {
 	for(i = 0; i < 256; i++) {
022a21cf
 	    pt = root->md5_hlist[i];
 	    while(pt) {
8000d078
 		h = pt;
e3aaff8e
 		pt = pt->next;
091df0e3
 		free(h->md5);
 		free(h->virname);
 		if(h->viralias)
 		    free(h->viralias);
8000d078
 		free(h);
e3aaff8e
 	    }
 	}
8000d078
 	free(root->md5_hlist);
e3aaff8e
     }
 
8000d078
     free(root);
e3aaff8e
 }
 
8000d078
 int cl_buildtrie(struct cl_node *root) /* for backward compatibility */
b5b62ca7
 {
8000d078
     return cl_build(root);
 }
ed026d36
 
8000d078
 void cl_freetrie(struct cl_node *root) /* for backward compatibility */
 {
058ccefe
     cl_free(root);
b5b62ca7
 }