libclamav/matcher.c
b151ef55
 /*
d119f7a0
  *  Copyright (C) 2002 - 2006 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
30738099
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
b151ef55
  */
 
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>
59afa53d
 #ifdef	HAVE_UNISTD_H
856d9c84
 #include <unistd.h>
59afa53d
 #endif
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"
1ec94c6b
 #include "elf.h"
 #include "execs.h"
958dc41c
 #include "special.h"
7f9ddaeb
 #include "str.h"
b151ef55
 
7def75f3
 #ifdef HAVE_NCORE
d9a1a832
 #include "matcher-ncore.h"
538a6756
 #endif
 
674f4b3c
 static cli_file_t targettab[CL_TARGET_TABLE_SIZE] = { 0, CL_TYPE_MSEXE, CL_TYPE_MSOLE2, CL_TYPE_HTML, CL_TYPE_MAIL, CL_TYPE_GRAPHICS, CL_TYPE_ELF };
d9a1a832
 
 extern short cli_debug_flag;
7f9ddaeb
 
674f4b3c
 int cli_scanbuff(const unsigned char *buffer, unsigned int length, const char **virname, const struct cl_engine *engine, cli_file_t ftype)
f91f55e0
 {
3d9239d0
 	int ret = CL_CLEAN, i;
d4405156
 	struct cli_ac_data mdata;
f477691c
 	struct cli_matcher *groot, *troot = NULL;
b151ef55
 
 
f477691c
     if(!engine) {
 	cli_errmsg("cli_scanbuff: engine == NULL\n");
 	return CL_ENULLARG;
     }
 
7def75f3
 #ifdef HAVE_NCORE
d9a1a832
     if(engine->ncore)
 	return cli_ncore_scanbuff(buffer, length, virname, engine, ftype, targettab);
 #endif
d119f7a0
 
f477691c
     groot = engine->root[0]; /* generic signatures */
 
     if(ftype) {
3d9239d0
 	for(i = 1; i < CL_TARGET_TABLE_SIZE; i++) {
f477691c
 	    if(targettab[i] == ftype) {
3d9239d0
 		troot = engine->root[i];
f477691c
 		break;
 	    }
 	}
     }
 
     if(troot) {
 
d4405156
 	if((ret = cli_ac_initdata(&mdata, troot->ac_partsigs, AC_DEFAULT_TRACKLEN)))
 	    return ret;
f477691c
 
9f986368
 	if(troot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, troot, 0, ftype, -1)) != CL_VIRUS)
d4405156
 	    ret = cli_ac_scanbuff(buffer, length, virname, troot, &mdata, 0, 0, ftype, -1, NULL);
f477691c
 
d4405156
 	cli_ac_freedata(&mdata);
f477691c
 
 	if(ret == CL_VIRUS)
 	    return ret;
     }
 
d4405156
     if((ret = cli_ac_initdata(&mdata, groot->ac_partsigs, AC_DEFAULT_TRACKLEN)))
 	return ret;
b151ef55
 
9f986368
     if(groot->ac_only || (ret = cli_bm_scanbuff(buffer, length, virname, groot, 0, ftype, -1)) != CL_VIRUS)
d4405156
 	ret = cli_ac_scanbuff(buffer, length, virname, groot, &mdata, 0, 0, ftype, -1, NULL);
 
     cli_ac_freedata(&mdata);
f91f55e0
 
     return ret;
b151ef55
 }
 
d9a1a832
 struct cli_md5_node *cli_vermd5(const unsigned char *md5, const struct cl_engine *engine)
b151ef55
 {
f91f55e0
 	struct cli_md5_node *pt;
b151ef55
 
 
f477691c
     if(!(pt = engine->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
 }
 
674f4b3c
 off_t cli_caloff(const char *offstr, struct cli_target_info *info, int fd, cli_file_t ftype, int *ret)
856d9c84
 {
1ec94c6b
 	int (*einfo)(int, struct cli_exe_info *) = NULL;
54c0fba0
 	unsigned int n;
 	off_t pos, offset;
856d9c84
 
 
54c0fba0
     *ret = 0;
 
     if(!strncmp(offstr, "EP", 2) || offstr[0] == 'S') {
 
 	if(info->status == -1) {
 	    *ret = -1;
 	    return 0;
 
 	} else if(!info->status) {
 
 	    if(ftype == CL_TYPE_MSEXE)
 		einfo = cli_peheader;
 	    else if(ftype == CL_TYPE_ELF)
 		einfo = cli_elfheader;
 
 	    if(einfo) {
 		if((pos = lseek(fd, 0, SEEK_CUR)) == -1) {
 		    cli_dbgmsg("Invalid descriptor\n");
 		    info->status = *ret = -1;
 		    return 0;
 		}
 
 		lseek(fd, 0, SEEK_SET);
 		if(einfo(fd, &info->exeinfo)) {
 		    lseek(fd, pos, SEEK_SET);
 		    info->status = *ret = -1;
 		    return 0;
 		}
 		lseek(fd, pos, SEEK_SET);
 		info->status = 1;
 	    }
 	}
     }
1ec94c6b
 
856d9c84
     if(isdigit(offstr[0])) {
 	return atoi(offstr);
1ec94c6b
 
54c0fba0
     } else if(info->status == 1 && (!strncmp(offstr, "EP+", 3) || !strncmp(offstr, "EP-", 3))) {
3af12024
 
 	if(offstr[2] == '+')
54c0fba0
 	    return info->exeinfo.ep + atoi(offstr + 3);
3af12024
 	else
54c0fba0
 	    return info->exeinfo.ep - atoi(offstr + 3);
3af12024
 
54c0fba0
     } else if(info->status == 1 && offstr[0] == 'S') {
856d9c84
 
e6bea1aa
 	if(!strncmp(offstr, "SL", 2) && info->exeinfo.section[info->exeinfo.nsections - 1].rsz) {
856d9c84
 
54c0fba0
 	    if(sscanf(offstr, "SL+%lu", &offset) != 1) {
 		*ret = -1;
 		return 0;
d19fc3f0
 	    }
 
54c0fba0
 	    offset += info->exeinfo.section[info->exeinfo.nsections - 1].raw;
d19fc3f0
 
 	} else {
 
54c0fba0
 	    if(sscanf(offstr, "S%u+%lu", &n, &offset) != 2) {
 		*ret = -1;
 		return 0;
d19fc3f0
 	    }
 
e6bea1aa
 	    if(n >= info->exeinfo.nsections || !info->exeinfo.section[n].rsz) {
54c0fba0
 		*ret = -1;
 		return 0;
d19fc3f0
 	    }
 
54c0fba0
 	    offset += info->exeinfo.section[n].raw;
856d9c84
 	}
 
 	return offset;
d19fc3f0
 
856d9c84
     } else if(!strncmp(offstr, "EOF-", 4)) {
 	    struct stat sb;
 
54c0fba0
 	if(!info->fsize) {
 	    if(fstat(fd, &sb) == -1) {
 		info->status = *ret = -1;
 		return 0;
 	    }
 	    info->fsize = sb.st_size;
 	}
856d9c84
 
54c0fba0
 	return info->fsize - atoi(offstr + 4);
856d9c84
     }
 
54c0fba0
     *ret = -1;
     return 0;
856d9c84
 }
 
a2c0f775
 static int cli_checkfp(int fd, const struct cl_engine *engine)
3f66a5af
 {
 	struct cli_md5_node *md5_node;
3c9c82bf
 	unsigned char *digest;
3f66a5af
 
 
f477691c
     if(engine->md5_hlist) {
3f66a5af
 
 	if(!(digest = cli_md5digest(fd))) {
 	    cli_errmsg("cli_checkfp(): Can't generate MD5 checksum\n");
 	    return 0;
 	}
 
f477691c
 	if((md5_node = cli_vermd5(digest, engine)) && md5_node->fp) {
3f66a5af
 		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;
 }
 
674f4b3c
 int cli_validatesig(cli_file_t ftype, const char *offstr, off_t fileoff, struct cli_target_info *info, int desc, const char *virname)
856d9c84
 {
54c0fba0
 	off_t offset;
 	int ret;
 
856d9c84
 
     if(offstr && desc != -1) {
54c0fba0
 	offset = cli_caloff(offstr, info, desc, ftype, &ret);
856d9c84
 
54c0fba0
 	if(ret == -1) {
 	    cli_dbgmsg("cli_validatesig: Can't calculate offset for signature %s\n", virname);
856d9c84
 	    return 0;
 	}
 
54c0fba0
 	if(fileoff != offset) {
 	    cli_dbgmsg("Signature offset: %lu, expected: %lu (%s)\n", fileoff, offset, virname);
856d9c84
 	    return 0;
 	}
     }
 
     return 1;
 }
 
674f4b3c
 int cli_scandesc(int desc, cli_ctx *ctx, unsigned short otfrec, cli_file_t ftype, unsigned short ftonly, struct cli_matched_type **ftoffset)
b151ef55
 {
d4405156
  	unsigned char *buffer, *buff, *endbl, *upt;
3d9239d0
 	int ret = CL_CLEAN, type = CL_CLEAN, i, bytes;
3b4ee075
 	unsigned int buffersize, length, maxpatlen, shift = 0;
d4405156
 	unsigned long int offset = 0;
 	struct cli_ac_data gdata, tdata;
605b8cf0
 	MD5_CTX md5ctx;
d2a12ffd
 	unsigned char digest[16];
f91f55e0
 	struct cli_md5_node *md5_node;
674f4b3c
 	struct cli_matcher *groot = NULL, *troot = NULL;
2fe19b26
 
 
605b8cf0
     if(!ctx->engine) {
f477691c
 	cli_errmsg("cli_scandesc: engine == NULL\n");
f91f55e0
 	return CL_ENULLARG;
ff8cb48b
     }
 
7def75f3
 #ifdef HAVE_NCORE
     if(ctx->engine->ncore) {
d9a1a832
 	    int cont;
d119f7a0
 
d9a1a832
 	ret = cli_ncore_scandesc(desc, ctx, ftype, &cont, targettab, &md5ctx);
 	if(!cont)
7f9ddaeb
 	    return ret;
d119f7a0
     }
d9a1a832
 #endif
d119f7a0
 
674f4b3c
     if(!ftonly)
 	groot = ctx->engine->root[0]; /* generic signatures */
f477691c
 
     if(ftype) {
3d9239d0
 	for(i = 1; i < CL_TARGET_TABLE_SIZE; i++) {
f477691c
 	    if(targettab[i] == ftype) {
3d9239d0
 		troot = ctx->engine->root[i];
f477691c
 		break;
 	    }
 	}
     }
 
674f4b3c
     if(ftonly) {
 	if(!troot)
 	    return CL_CLEAN;
 
 	maxpatlen = troot->maxpatlen;
     } else {
 	if(troot)
 	    maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen);
 	else
 	    maxpatlen = groot->maxpatlen;
     }
f477691c
 
f91f55e0
     /* prepare the buffer */
cec4e61d
     buffersize = maxpatlen + SCANBUFF;
d4405156
     if(!(buffer = (unsigned char *) cli_calloc(buffersize, sizeof(unsigned char)))) {
54c0fba0
 	cli_dbgmsg("cli_scandesc(): unable to cli_calloc(%u)\n", buffersize);
f91f55e0
 	return CL_EMEM;
     }
2fe19b26
 
674f4b3c
     if(!ftonly && (ret = cli_ac_initdata(&gdata, groot->ac_partsigs, AC_DEFAULT_TRACKLEN)))
d4405156
 	return ret;
b151ef55
 
f477691c
     if(troot) {
d4405156
 	if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, AC_DEFAULT_TRACKLEN)))
 	    return ret;
f477691c
     }
 
674f4b3c
     if(!ftonly && ctx->engine->md5_hlist)
605b8cf0
 	MD5_Init(&md5ctx);
b151ef55
 
f91f55e0
     buff = buffer;
f477691c
     buff += maxpatlen; /* pointer to read data block */
     endbl = buff + SCANBUFF - maxpatlen; /* pointer to the last block
d4405156
 					  * length of maxpatlen
 					  */
b151ef55
 
d4405156
     upt = buff;
cec4e61d
     while((bytes = cli_readn(desc, buff + shift, SCANBUFF - shift)) > 0) {
b151ef55
 
605b8cf0
 	if(ctx->scanned)
 	    *ctx->scanned += bytes / CL_COUNT_PRECISION;
b151ef55
 
cec4e61d
 	length = shift + bytes;
d4405156
 	if(upt == buffer)
cec4e61d
 	    length += maxpatlen;
ff8cb48b
 
f477691c
 	if(troot) {
d4405156
 	    if(troot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, troot, offset, ftype, desc)) != CL_VIRUS)
 		ret = cli_ac_scanbuff(upt, length, ctx->virname, troot, &tdata, otfrec, offset, ftype, desc, ftoffset);
9f986368
 
 	    if(ret == CL_VIRUS) {
f477691c
 		free(buffer);
674f4b3c
 		if(!ftonly)
 		    cli_ac_freedata(&gdata);
d4405156
 		cli_ac_freedata(&tdata);
f477691c
 
 		lseek(desc, 0, SEEK_SET);
605b8cf0
 		if(cli_checkfp(desc, ctx->engine))
f477691c
 		    return CL_CLEAN;
 		else
 		    return CL_VIRUS;
 	    }
 	}
3f66a5af
 
674f4b3c
 	if(!ftonly) {
 	    if(groot->ac_only || (ret = cli_bm_scanbuff(upt, length, ctx->virname, groot, offset, ftype, desc)) != CL_VIRUS)
 		ret = cli_ac_scanbuff(upt, length, ctx->virname, groot, &gdata, otfrec, offset, ftype, desc, ftoffset);
9f986368
 
674f4b3c
 	    if(ret == CL_VIRUS) {
 		free(buffer);
 		cli_ac_freedata(&gdata);
 		if(troot)
 		    cli_ac_freedata(&tdata);
 		lseek(desc, 0, SEEK_SET);
 		if(cli_checkfp(desc, ctx->engine))
 		    return CL_CLEAN;
 		else
 		    return CL_VIRUS;
b151ef55
 
674f4b3c
 	    } else if(otfrec && ret >= CL_TYPENO) {
 		if(ret > type)
 		    type = ret;
 	    }
b151ef55
 
674f4b3c
 	    if(ctx->engine->md5_hlist)
 		MD5_Update(&md5ctx, buff + shift, bytes);
 	}
cec4e61d
 
 	if(bytes + shift == SCANBUFF) {
f477691c
 	    memmove(buffer, endbl, maxpatlen);
cec4e61d
 	    offset += SCANBUFF;
1f73f3ff
 
d4405156
 	    if(upt == buff) {
 		upt = buffer;
cec4e61d
 		offset -= maxpatlen;
34dd51a6
 	    }
cec4e61d
 
 	    shift = 0;
 
 	} else {
 	    shift += bytes;
34dd51a6
 	}
d898865b
 
f91f55e0
     }
d898865b
 
f91f55e0
     free(buffer);
674f4b3c
     if(!ftonly)
 	cli_ac_freedata(&gdata);
d4405156
     if(troot)
 	cli_ac_freedata(&tdata);
1f73f3ff
 
674f4b3c
     if(!ftonly && ctx->engine->md5_hlist) {
605b8cf0
 	MD5_Final(digest, &md5ctx);
1f73f3ff
 
605b8cf0
 	if((md5_node = cli_vermd5(digest, ctx->engine)) && !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 {
605b8cf0
 		if(ctx->virname)
 		    *ctx->virname = md5_node->virname;
61f31052
 
 		return CL_VIRUS;
 	    }
f91f55e0
 	}
1f73f3ff
     }
 
df757556
     return otfrec ? type : CL_CLEAN;
1f73f3ff
 }