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>
 
 #include "clamav.h"
 #include "others.h"
8000d078
 #include "matcher-ac.h"
 #include "matcher-bm.h"
 #include "md5.h"
888f5794
 #include "filetypes.h"
e3aaff8e
 
8000d078
 #define MD5_BLOCKSIZE 4096
e3aaff8e
 
 
8000d078
 int cl_scanbuff(const char *buffer, unsigned int length, const char **virname, const struct cl_node *root)
 {
 	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
 
8000d078
     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;
e3aaff8e
 }
 
8000d078
 static struct cli_md5_node *cli_vermd5(const 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
 }
 
8000d078
 int cli_scandesc(int desc, const char **virname, long int *scanned, const struct cl_node *root, int typerec)
e3aaff8e
 {
8000d078
  	char *buffer, *buff, *endbl, *pt;
 	int bytes, buffsize, length, ret, *partcnt, type = CL_CLEAN;
 	unsigned long int *partoff, offset = 0;
 	struct md5_ctx ctx;
         unsigned char md5buff[16];
 	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) {
 	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;
     }
e3aaff8e
 
8000d078
     if(root->md5_hlist)
 	md5_init_ctx (&ctx);
e3aaff8e
 
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
 
8000d078
     pt= buff;
     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
 
8000d078
 	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;
e3aaff8e
 
8000d078
 	} else if(typerec && ret >= CL_TYPENO) {
 	    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
 
8000d078
 	/* compute MD5 */
fc8e8ca2
 
8000d078
 	if(root->md5_hlist) {
 	    if(bytes % 64 == 0) {
 		md5_process_block(buff, bytes, &ctx);
 	    } else {
 		    int block = bytes;
 		    char *mpt = buff;
fc8e8ca2
 
8000d078
 		while(block >= MD5_BLOCKSIZE) {
 		    md5_process_block(mpt, MD5_BLOCKSIZE, &ctx);
 		    mpt += MD5_BLOCKSIZE;
 		    block -= MD5_BLOCKSIZE;
 		}
fc8e8ca2
 
8000d078
 		if(block)
 		    md5_process_bytes(mpt, block, &ctx);
084ee140
 	    }
8000d078
 	}
     }
084ee140
 
8000d078
     free(buffer);
     free(partcnt);
     free(partoff);
fc8e8ca2
 
8000d078
     if(root->md5_hlist) {
 	md5_finish_ctx(&ctx, &md5buff);
fc8e8ca2
 
8000d078
 	if((md5_node = cli_vermd5(md5buff, 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
     }
 
8000d078
     return typerec ? 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 */
 {
     return cl_free(root);
b5b62ca7
 }