libclamav/readdb.c
b151ef55
 /*
50099661
  *  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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #include "clamav.h"
442d8407
 #include "cvd.h"
b151ef55
 #include "strings.h"
 #include "matcher.h"
 #include "others.h"
 #include "str.h"
 #include "defaults.h"
 
41b894c7
 static int cli_parse_add(struct cl_node *root, const char *virname, const char *hexstr, int sigid, int parts, int partno)
b151ef55
 {
442d8407
 	struct cli_patt *new;
b151ef55
 	const char *pt;
 	int ret, virlen;
 
     /* decode a hexstring and prepare a new entry */
 
442d8407
     if((new = (struct cli_patt *) cli_calloc(1, sizeof(struct cli_patt))) == NULL)
b151ef55
 	return CL_EMEM;
 
     new->sigid = sigid;
     new->parts = parts;
     new->partno = partno;
 
     new->length = strlen(hexstr) / 2;
 
     if(new->length > root->maxpatlen)
 	root->maxpatlen = new->length;
 
     if((new->pattern = cl_hex2str(hexstr)) == NULL) {
 	free(new);
 	return CL_EMALFDB;
     }
 
     if((pt = strstr(virname, "(Clam)")))
 	virlen = strlen(virname) - strlen(pt) - 1;
     else
 	virlen = strlen(virname);
 
e295ab3d
     if(virlen <= 0) {
 	free(new);
 	return CL_EMALFDB;
     }
 
50099661
     if((new->virname = cli_calloc(virlen + 1, sizeof(char))) == NULL) {
 	free(new);
b151ef55
 	return CL_EMEM;
50099661
     }
 
7cc3891c
     strncpy(new->virname, virname, virlen);
b151ef55
 
50099661
     if((ret = cli_addpatt(root, new))) {
 	free(new->virname);
 	free(new);
b151ef55
 	return ret;
50099661
     }
b151ef55
 
     return 0;
 }
 
 /* this functions returns a pointer to the root of trie */
 
 int cl_loaddb(const char *filename, struct cl_node **root, int *virnum)
 {
 	FILE *fd;
7b7b3ca5
 	char *buffer, *pt, *start, *pt2;
b151ef55
 	int line = 0, ret, parts, i, sigid = 0;
 
 
     if((fd = fopen(filename, "rb")) == NULL) {
 	cli_errmsg("cl_loaddb(): Can't open file %s\n", filename);
 	return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading %s\n", filename);
 
50099661
     if(!(buffer = (char *) cli_malloc(FILEBUFF))) {
 	fclose(fd);
7b7b3ca5
 	return CL_EMEM;
50099661
     }
7b7b3ca5
 
9c1c9007
     memset(buffer, 0, FILEBUFF);
50099661
 
d5314812
     /* test for CVD file */
50099661
 
     if (fgets(buffer, 12, fd) == NULL) {
 	cli_dbgmsg("%s: failure reading header\n", filename);
 	free(buffer);
 	fclose(fd);
 	return CL_EMALFDB;
     }
 
c0eb3ceb
     rewind(fd);
4cd4319e
 
     if(!strncmp(buffer, "ClamAV-VDB:", 11)) {
 	cli_dbgmsg("%s: CVD file detected\n", filename);
 	ret = cli_cvdload(fd, root, virnum);
7b7b3ca5
 	free(buffer);
50099661
 	fclose(fd);
4cd4319e
 	return ret;
     }
 
7b7b3ca5
     while(fgets(buffer, FILEBUFF, fd)) {
b151ef55
 
 	/* for forward compatibility */
 	if(strchr(buffer, '{') || strchr(buffer, '}')) {
 	    cli_dbgmsg("Not suported signature type detected at line %d. Skipping.\n", line);
 	    continue;
 	}
 
 	line++;
 	cli_chomp(buffer);
 
 	pt = strchr(buffer, '=');
 	if(!pt) {
 	    cli_errmsg("readdb(): Malformed pattern line %d (file %s).\n", line, filename);
7b7b3ca5
 	    free(buffer);
50099661
 	    fclose(fd);
b151ef55
 	    return CL_EMALFDB;
 	}
 
 	start = buffer;
 	*pt++ = 0;
 
 	if(*pt == '=') continue;
 
 	if(!*root) {
 	    cli_dbgmsg("Initializing trie.\n");
 	    *root = (struct cl_node *) cli_calloc(1, sizeof(struct cl_node));
7b7b3ca5
 	    if(!*root) {
 		free(buffer);
50099661
 		fclose(fd);
b151ef55
 		return CL_EMEM;
7b7b3ca5
 	    }
b151ef55
 	    (*root)->maxpatlen = 0;
 	}
 
 	if(strchr(pt, '*')) { /* new type signature */
 	    (*root)->partsigs++;
 	    sigid++;
 	    parts = 0;
41b894c7
 	    for(i = 0; i < (int) strlen(pt); i++)
b151ef55
 		if(pt[i] == '*')
 		    parts++;
 
 	    if(parts) /* there's always one part more */
 		parts++;
ca6008cc
 	    for(i = 1; i <= parts; i++) {
 		if((pt2 = cli_strtok(pt, i - 1, "*")) == NULL) {
e8217f5a
 		    cli_errmsg("Can't extract part %d of partial signature in line %d\n", i + 1, line);
50099661
 		    free(buffer);
 		    fclose(fd);
03186fe8
 		    return CL_EMALFDB;
 		}
 
b151ef55
 		if((ret = cli_parse_add(*root, start, pt2, sigid, parts, i))) {
 		    cli_dbgmsg("parse_add() return code: %d\n", ret);
 		    cli_errmsg("readdb(): Malformed pattern line %d (file %s).\n", line, filename);
50099661
 		    free(pt2);
7b7b3ca5
 		    free(buffer);
50099661
 		    fclose(fd);
b151ef55
 		    return ret;
 		}
8a05efc5
 /*
 		cli_dbgmsg("Added part %d of partial signature (id %d)\n", i, sigid);
 */
b151ef55
 		free(pt2);
 	    }
 
 	} else { /* old type */
 	    if((ret = cli_parse_add(*root, start, pt, 0, 0, 0))) {
 		cli_dbgmsg("parse_add() return code: %d\n", ret);
 		cli_errmsg("readdb(): Malformed pattern line %d (file %s).\n", line, filename);
7b7b3ca5
 		free(buffer);
50099661
 		fclose(fd);
b151ef55
 		return ret;
 	    }
 	}
     }
 
     if(virnum != NULL)
 	*virnum += line;
 
50099661
     free(buffer);
     fclose(fd);
 
b151ef55
     return 0;
 }
 
41b894c7
 const char *cl_retdbdir(void)
b151ef55
 {
     return DATADIR;
 }
 
 int cl_loaddbdir(const char *dirname, struct cl_node **root, int *virnum)
 {
 	DIR *dd;
 	struct dirent *dent;
 	char *dbfile;
 	int ret;
 
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_loaddbdir(): Can't open directory %s\n", dirname);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Loading databases from %s\n", dirname);
 
     while((dent = readdir(dd))) {
618a038b
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
4cd4319e
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
 	     cli_strbcasestr(dent->d_name, ".db2") ||
 	     cli_strbcasestr(dent->d_name, ".cvd"))) {
 
b151ef55
 		dbfile = (char *) cli_calloc(strlen(dent->d_name) + strlen(dirname) + 2, sizeof(char));
 
 		if(!dbfile) {
4cd4319e
 		    cli_dbgmsg("cl_loaddbdir(): dbfile == NULL\n");
b151ef55
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 		sprintf(dbfile, "%s/%s", dirname, dent->d_name);
 		if((ret = cl_loaddb(dbfile, root, virnum))) {
c0eb3ceb
 		    cli_dbgmsg("cl_loaddbdir(): error loading database %s\n", dbfile);
b151ef55
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
 {
 	DIR *dd;
 	struct dirent *dent;
         char *fname;
 
 
     if(dbstat) {
 	dbstat->no = 0;
 	dbstat->stattab = NULL;
 	dbstat->dir = strdup(dirname);
     } else {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dirname);
 
     while((dent = readdir(dd))) {
618a038b
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
089fbbbb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && (cli_strbcasestr(dent->d_name, ".db") || cli_strbcasestr(dent->d_name, ".db2") || cli_strbcasestr(dent->d_name, ".cvd"))) {
b151ef55
 
 		dbstat->no++;
 		dbstat->stattab = (struct stat *) realloc(dbstat->stattab, dbstat->no * sizeof(struct stat));
                 fname = cli_calloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char));
 		sprintf(fname, "%s/%s", dirname, dent->d_name);
 		stat(fname, &dbstat->stattab[dbstat->no - 1]);
 		free(fname);
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statchkdir(const struct cl_stat *dbstat)
 {
 	DIR *dd;
 	struct dirent *dent;
 	struct stat sb;
 	int i, found;
 	char *fname;
 
 
     if(!dbstat || !dbstat->dir) {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dbstat->dir)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir);
 
     while((dent = readdir(dd))) {
618a038b
 #ifndef C_INTERIX
 	if(dent->d_ino)
 #endif
 	{
089fbbbb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && (cli_strbcasestr(dent->d_name, ".db") || cli_strbcasestr(dent->d_name, ".db2") || cli_strbcasestr(dent->d_name, ".cvd"))) {
b151ef55
 
                 fname = cli_calloc(strlen(dbstat->dir) + strlen(dent->d_name) + 2, sizeof(char));
 		sprintf(fname, "%s/%s", dbstat->dir, dent->d_name);
 		stat(fname, &sb);
 		free(fname);
 
 		found = 0;
 		for(i = 0; i < dbstat->no; i++)
 		    if(dbstat->stattab[i].st_ino == sb.st_ino) {
 			found = 1;
82b133ea
 			if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
 			    closedir(dd);
b151ef55
 			    return 1;
82b133ea
 			}
b151ef55
 		    }
 
82b133ea
 		if(!found) {
 		    closedir(dd);
b151ef55
 		    return 1;
82b133ea
 		}
b151ef55
 	    }
 	}
     }
 
     closedir(dd);
     return 0;
 }
 
 int cl_statfree(struct cl_stat *dbstat)
 {
 
     if(dbstat) {
 	free(dbstat->stattab);
 	dbstat->stattab = NULL;
 	dbstat->no = 0;
9c1c9007
 	if(dbstat->dir) {
b151ef55
 	    free(dbstat->dir);
9c1c9007
 	    dbstat->dir = NULL;
 	}
b151ef55
     } else {
         cli_errmsg("cl_statfree(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     return 0;
 }