libclamav/matcher.c
b151ef55
 /*
cc938d61
  *  Copyright (C) 2002 - 2004 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>
 
 #include "clamav.h"
 #include "others.h"
f91f55e0
 #include "matcher-ac.h"
 #include "matcher-bm.h"
 #include "md5.h"
2fe19b26
 #include "filetypes.h"
b151ef55
 
f91f55e0
 #define MD5_BLOCKSIZE 4096
b151ef55
 
 
f91f55e0
 int cl_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root)
 {
 	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
 
f91f55e0
     if((ret = cli_bm_scanbuff(buffer, length, virname, root)) != CL_VIRUS)
 	ret = cli_ac_scanbuff(buffer, length, virname, root, partcnt, 0, 0, partoff);
 
     free(partcnt);
     free(partoff);
     return ret;
b151ef55
 }
 
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
 }
 
f91f55e0
 int cli_scandesc(int desc, const char **virname, long int *scanned, const struct cl_node *root, int typerec)
b151ef55
 {
f91f55e0
  	char *buffer, *buff, *endbl, *pt;
 	int bytes, buffsize, length, ret, *partcnt, type = CL_CLEAN;
 	unsigned long int *partoff, offset = 0;
d2a12ffd
 	struct MD5Context ctx;
 	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)))) {
 	cli_dbgmsg("cli_scandesc(): unable to cli_malloc(%d)\n", buffsize);
 	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) {
 	cli_dbgmsg("cli_scanbuff(): unable to cli_calloc(%d, %d)\n", root->ac_partsigs + 1, sizeof(unsigned long int));
 	free(buffer);
 	free(partcnt);
 	return CL_EMEM;
     }
b151ef55
 
f91f55e0
     if(root->md5_hlist)
d2a12ffd
 	MD5Init(&ctx);
b151ef55
 
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
 
f91f55e0
 	if(cli_bm_scanbuff(pt, length, virname, root) == CL_VIRUS ||
 	   (ret = cli_ac_scanbuff(pt, length, virname, root, partcnt, typerec, offset, partoff)) == CL_VIRUS) {
 	    free(buffer);
 	    free(partcnt);
 	    free(partoff);
 	    return CL_VIRUS;
b151ef55
 
f91f55e0
 	} else if(typerec && ret >= CL_TYPENO) {
 	    if(ret >= type)
 		type = ret;
 	}
b151ef55
 
f91f55e0
 	if(bytes == SCANBUFF) {
 	    memmove(buffer, endbl, root->maxpatlen);
 	    offset += bytes - root->maxpatlen;
 	}
1f73f3ff
 
f91f55e0
         pt = buffer;
         length = buffsize;
d898865b
 
d2a12ffd
 	if(root->md5_hlist)
 	    MD5Update(&ctx, buff, bytes);
f91f55e0
     }
d898865b
 
f91f55e0
     free(buffer);
     free(partcnt);
     free(partoff);
1f73f3ff
 
f91f55e0
     if(root->md5_hlist) {
d2a12ffd
 	MD5Final(digest, &ctx);
1f73f3ff
 
d2a12ffd
 	if((md5_node = cli_vermd5(digest, root))) {
61f31052
 		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;
 	    }
f91f55e0
 	}
1f73f3ff
     }
 
f91f55e0
     return typerec ? type : CL_CLEAN;
1f73f3ff
 }
 
f91f55e0
 int cl_build(struct cl_node *root)
b151ef55
 {
f91f55e0
     return cli_ac_buildtrie(root);
 }
b151ef55
 
f91f55e0
 void cl_free(struct cl_node *root)
 {
 	int i;
 	struct cli_md5_node *pt, *h;
cc938d61
 
b151ef55
 
f91f55e0
     if(!root) {
 	cli_errmsg("cl_free: root == NULL\n");
 	return;
9c1c9007
     }
 
f91f55e0
     cli_ac_free(root);
     cli_bm_free(root);
b151ef55
 
f91f55e0
     if(root->md5_hlist) {
 	for(i = 0; i < 256; i++) {
61f31052
 	    pt = root->md5_hlist[i];
 	    while(pt) {
f91f55e0
 		h = pt;
b151ef55
 		pt = pt->next;
85ce7ea9
 		free(h->md5);
 		free(h->virname);
 		if(h->viralias)
 		    free(h->viralias);
f91f55e0
 		free(h);
b151ef55
 	    }
 	}
f91f55e0
 	free(root->md5_hlist);
b151ef55
     }
 
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
 }