clamav/libclamav/cvd.c

735 lines
22 KiB
C
Raw Normal View History

2003-09-29 11:44:52 +00:00
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
2003-09-29 11:44:52 +00:00
*
* Authors: Tomasz Kojm
*
* Summary: Code to parse Clamav CVD database format.
*
* Acknowledgements: ClamAV untar code is based on a public domain minitar utility
* by Charles G. Waldman.
2003-09-29 11:44:52 +00:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
2003-09-29 11:44:52 +00:00
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
2003-09-29 11:44:52 +00:00
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
2003-09-29 11:44:52 +00:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "zlib.h"
#include <time.h>
#include <errno.h>
2003-09-29 11:44:52 +00:00
#include "clamav.h"
2003-10-08 10:40:53 +00:00
#include "others.h"
#include "dsig.h"
2003-10-26 06:01:03 +00:00
#include "str.h"
#include "cvd.h"
#include "readdb.h"
#include "default.h"
2003-09-29 11:44:52 +00:00
#define TAR_BLOCKSIZE 512
2012-06-22 16:55:29 -04:00
static void cli_untgz_cleanup(char *path, gzFile infile, FILE *outfile, int fdd)
{
UNUSEDPARAM(fdd);
2012-06-22 16:55:29 -04:00
cli_dbgmsg("in cli_untgz_cleanup()\n");
if (path != NULL)
free(path);
if (infile != NULL)
gzclose(infile);
2012-06-22 16:55:29 -04:00
if (outfile != NULL)
fclose(outfile);
}
static int cli_untgz(int fd, const char *destdir)
2003-09-29 11:44:52 +00:00
{
char *path, osize[13], name[101], type;
char block[TAR_BLOCKSIZE];
int nbytes, nread, nwritten, in_block = 0, fdd = -1;
unsigned int size, pathlen = strlen(destdir) + 100 + 5;
FILE *outfile = NULL;
STATBUF foo;
gzFile infile = NULL;
2003-09-29 11:44:52 +00:00
cli_dbgmsg("in cli_untgz()\n");
if ((fdd = dup(fd)) == -1) {
cli_errmsg("cli_untgz: Can't duplicate descriptor %d\n", fd);
return -1;
}
if ((infile = gzdopen(fdd, "rb")) == NULL) {
cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
if (FSTAT(fdd, &foo) == 0)
close(fdd);
return -1;
}
path = (char *)cli_calloc(sizeof(char), pathlen);
if (!path) {
cli_errmsg("cli_untgz: Can't allocate memory for path\n");
cli_untgz_cleanup(NULL, infile, NULL, fdd);
return -1;
}
while (1) {
nread = gzread(infile, block, TAR_BLOCKSIZE);
if (!in_block && !nread)
break;
if (nread != TAR_BLOCKSIZE) {
cli_errmsg("cli_untgz: Incomplete block read\n");
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
if (!in_block) {
if (block[0] == '\0') /* We're done */
break;
strncpy(name, block, 100);
name[100] = '\0';
if (strchr(name, '/')) {
cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n");
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
snprintf(path, pathlen, "%s" PATHSEP "%s", destdir, name);
cli_dbgmsg("cli_untgz: Unpacking %s\n", path);
type = block[156];
switch (type) {
case '0':
case '\0':
break;
case '5':
cli_errmsg("cli_untgz: Directories are not supported in CVD\n");
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
default:
cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type);
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
if (outfile) {
if (fclose(outfile)) {
cli_errmsg("cli_untgz: Cannot close file %s\n", path);
outfile = NULL;
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
outfile = NULL;
}
if (!(outfile = fopen(path, "wb"))) {
cli_errmsg("cli_untgz: Cannot create file %s\n", path);
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
strncpy(osize, block + 124, 12);
osize[12] = '\0';
if ((sscanf(osize, "%o", &size)) == 0) {
cli_errmsg("cli_untgz: Invalid size in header\n");
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
if (size > 0)
in_block = 1;
} else { /* write or continue writing file contents */
nbytes = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size;
nwritten = fwrite(block, 1, nbytes, outfile);
if (nwritten != nbytes) {
cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path);
cli_untgz_cleanup(path, infile, outfile, fdd);
return -1;
}
size -= nbytes;
if (size == 0)
in_block = 0;
}
2003-09-29 11:44:52 +00:00
}
2012-06-22 16:55:29 -04:00
cli_untgz_cleanup(path, infile, outfile, fdd);
2003-09-29 11:44:52 +00:00
return 0;
}
2012-06-22 16:55:29 -04:00
static void cli_tgzload_cleanup(int comp, struct cli_dbio *dbio, int fdd)
{
UNUSEDPARAM(fdd);
2012-06-22 16:55:29 -04:00
cli_dbgmsg("in cli_tgzload_cleanup()\n");
if (comp) {
2012-06-22 16:55:29 -04:00
gzclose(dbio->gzs);
dbio->gzs = NULL;
} else {
2012-06-22 16:55:29 -04:00
fclose(dbio->fs);
dbio->fs = NULL;
}
if (dbio->buf != NULL) {
2012-06-22 16:55:29 -04:00
free(dbio->buf);
dbio->buf = NULL;
}
if (dbio->hashctx) {
cl_hash_destroy(dbio->hashctx);
dbio->hashctx = NULL;
}
2012-06-22 16:55:29 -04:00
}
static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, struct cli_dbinfo *dbinfo)
{
char osize[13], name[101];
char block[TAR_BLOCKSIZE];
int nread, fdd, ret;
unsigned int type, size, pad, compr = 1;
off_t off;
struct cli_dbinfo *db;
char hash[32];
cli_dbgmsg("in cli_tgzload()\n");
if (lseek(fd, 512, SEEK_SET) < 0) {
2013-02-12 17:33:21 -05:00
return CL_ESEEK;
}
if (cli_readn(fd, block, 7) != 7)
return CL_EFORMAT; /* truncated file? */
if (!strncmp(block, "COPYING", 7))
compr = 0;
if (lseek(fd, 512, SEEK_SET) < 0) {
2013-02-12 17:33:21 -05:00
return CL_ESEEK;
}
if ((fdd = dup(fd)) == -1) {
cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd);
return CL_EDUP;
}
if (compr) {
if ((dbio->gzs = gzdopen(fdd, "rb")) == NULL) {
cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
if (fdd > -1)
close(fdd);
return CL_EOPEN;
}
dbio->fs = NULL;
} else {
if ((dbio->fs = fdopen(fdd, "rb")) == NULL) {
cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno);
if (fdd > -1)
close(fdd);
return CL_EOPEN;
}
dbio->gzs = NULL;
}
dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE;
dbio->buf = cli_malloc(dbio->bufsize);
if (!dbio->buf) {
cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n");
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
dbio->bufpt = NULL;
dbio->usebuf = 1;
dbio->readpt = dbio->buf;
while (1) {
if (compr)
nread = gzread(dbio->gzs, block, TAR_BLOCKSIZE);
else
nread = fread(block, 1, TAR_BLOCKSIZE, dbio->fs);
if (!nread)
break;
if (nread != TAR_BLOCKSIZE) {
cli_errmsg("cli_tgzload: Incomplete block read\n");
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
if (block[0] == '\0') /* We're done */
break;
strncpy(name, block, 100);
name[100] = '\0';
if (strchr(name, '/')) {
cli_errmsg("cli_tgzload: Slash separators are not allowed in CVD\n");
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
type = block[156];
switch (type) {
case '0':
case '\0':
break;
case '5':
cli_errmsg("cli_tgzload: Directories are not supported in CVD\n");
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
default:
cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type);
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
strncpy(osize, block + 124, 12);
osize[12] = '\0';
if ((sscanf(osize, "%o", &size)) == 0) {
cli_errmsg("cli_tgzload: Invalid size in header\n");
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
dbio->size = size;
dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
dbio->bufpt = NULL;
dbio->readpt = dbio->buf;
if (!(dbio->hashctx)) {
dbio->hashctx = cl_hash_init("sha256");
if (!(dbio->hashctx)) {
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
}
dbio->bread = 0;
/* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */
if (compr)
off = (off_t)gzseek(dbio->gzs, 0, SEEK_CUR);
else
off = ftell(dbio->fs);
PE parsing code improvements, db loading bug fixes Consolidate the PE parsing code into one function. I tried to preserve all existing functionality from the previous, distinct implementations to a large extent (with the exceptions mentioned below). If I noticed potential bugs/improvements, I added a TODO statement about those so that they can be fixed in a smaller commit later. Also, there are more TODOs in places where I'm not entirely sure why certain actions are performed - more research is needed for these. I'm submitting a pull request now so that regression testing can be done, and because merging what I have thus far now will likely have fewer conflicts than if I try to merge later PE parsing code improvements: - PEs without all 16 data directories are parsed more appropriately now - Added lots more debug statements Also: - Allow MAX_BC and MAX_TRACKED_PCRE to be specified via CFLAGS When doing performance testing with the latest CVD, MAX_BC and MAX_TRACKED_PCRE need to be raised to track all the events. Allow these to be specified via CFLAGS by not redefining them if they are already defined - Fix an issue preventing wildcard sizes in .MDB/.MSB rules I'm not sure what the original intent of the check I removed was, but it prevents using wildcard sizes in .MDB/.MSB rules. AFAICT these wildcard sizes should be handled appropriately by the MD5 section hash computation code, so I don't think a check on that is needed. - Fix several issues related to db loading - .imp files will now get loaded if they exist in a directory passed via clamscan's '-d' flag - .pwdb files will now get loaded if they exist in a directory passed via clamscan's '-d' flag even when compiling without yara support - Changes to .imp, .ign, and .ign2 files will now be reflected in calls to cl_statinidir and cl_statchkdir (and also .pwdb files, even when compiling without yara support) - The contents of .sfp files won't be included in some of the signature counts, and the contents of .cud files will be - Any local.gdb files will no longer be loaded twice - For .imp files, you are no longer required to specify a minimum flevel for wildcard rules, since this isn't needed
2019-01-08 00:09:08 -05:00
if ((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && CLI_DBEXT(name))) {
ret = cli_load(name, engine, signo, options, dbio);
if (ret) {
cli_errmsg("cli_tgzload: Can't load %s\n", name);
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
if (!dbinfo) {
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_SUCCESS;
} else {
db = dbinfo;
while (db && strcmp(db->name, name))
db = db->next;
if (!db) {
cli_errmsg("cli_tgzload: File %s not found in .info\n", name);
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
if (dbio->bread) {
if (db->size != dbio->bread) {
cli_errmsg("cli_tgzload: File %s not correctly loaded\n", name);
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
cl_finish_hash(dbio->hashctx, hash);
dbio->hashctx = cl_hash_init("sha256");
if (!(dbio->hashctx)) {
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
if (memcmp(db->hash, hash, 32)) {
cli_errmsg("cli_tgzload: Invalid checksum for file %s\n", name);
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_EMALFDB;
}
}
}
}
pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0;
if (compr) {
if (off == gzseek(dbio->gzs, 0, SEEK_CUR))
gzseek(dbio->gzs, size + pad, SEEK_CUR);
else if (pad)
gzseek(dbio->gzs, pad, SEEK_CUR);
} else {
if (off == ftell(dbio->fs))
fseek(dbio->fs, size + pad, SEEK_CUR);
else if (pad)
fseek(dbio->fs, pad, SEEK_CUR);
}
}
2012-06-22 16:55:29 -04:00
cli_tgzload_cleanup(compr, dbio, fdd);
return CL_SUCCESS;
}
2003-10-26 06:01:03 +00:00
struct cl_cvd *cl_cvdparse(const char *head)
2003-09-29 11:44:52 +00:00
{
struct cl_cvd *cvd;
char *pt;
2003-10-08 10:40:53 +00:00
if (strncmp(head, "ClamAV-VDB:", 11)) {
cli_errmsg("cli_cvdparse: Not a CVD file\n");
return NULL;
2003-10-08 10:40:53 +00:00
}
2003-09-29 11:44:52 +00:00
if (!(cvd = (struct cl_cvd *)cli_malloc(sizeof(struct cl_cvd)))) {
cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n");
return NULL;
2006-06-17 21:00:44 +00:00
}
2003-09-29 11:44:52 +00:00
if (!(cvd->time = cli_strtok(head, 1, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the creation time\n");
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
if (!(pt = cli_strtok(head, 2, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the version number\n");
free(cvd->time);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
2003-09-29 11:44:52 +00:00
cvd->version = atoi(pt);
free(pt);
if (!(pt = cli_strtok(head, 3, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n");
free(cvd->time);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
2003-09-29 11:44:52 +00:00
cvd->sigs = atoi(pt);
free(pt);
if (!(pt = cli_strtok(head, 4, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the functionality level\n");
free(cvd->time);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
cvd->fl = atoi(pt);
2003-09-29 11:44:52 +00:00
free(pt);
if (!(cvd->md5 = cli_strtok(head, 5, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n");
free(cvd->time);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
if (!(cvd->dsig = cli_strtok(head, 6, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the digital signature\n");
free(cvd->time);
free(cvd->md5);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
if (!(cvd->builder = cli_strtok(head, 7, ":"))) {
cli_errmsg("cli_cvdparse: Can't parse the builder name\n");
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd);
return NULL;
2003-11-24 16:19:39 +00:00
}
2003-09-29 11:44:52 +00:00
if ((pt = cli_strtok(head, 8, ":"))) {
cvd->stime = atoi(pt);
free(pt);
} else {
cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n");
cvd->stime = 0;
}
2003-09-29 11:44:52 +00:00
return cvd;
}
struct cl_cvd *cl_cvdhead(const char *file)
{
FILE *fs;
char head[513], *pt;
int i;
unsigned int bread;
2003-09-29 11:44:52 +00:00
if ((fs = fopen(file, "rb")) == NULL) {
cli_errmsg("cl_cvdhead: Can't open file %s\n", file);
return NULL;
2003-09-29 11:44:52 +00:00
}
if (!(bread = fread(head, 1, 512, fs))) {
cli_errmsg("cl_cvdhead: Can't read CVD header in %s\n", file);
fclose(fs);
return NULL;
2003-10-26 06:01:03 +00:00
}
2006-06-08 18:56:22 +00:00
fclose(fs);
2003-10-26 06:01:03 +00:00
2006-07-25 16:34:40 +00:00
head[bread] = 0;
if ((pt = strpbrk(head, "\n\r")))
*pt = 0;
for (i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--)
;
2003-10-26 06:01:03 +00:00
return cl_cvdparse(head);
2003-09-29 11:44:52 +00:00
}
void cl_cvdfree(struct cl_cvd *cvd)
{
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd->builder);
free(cvd);
}
static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int skipsig)
2003-10-08 10:40:53 +00:00
{
struct cl_cvd *cvd;
char *md5, head[513];
int i;
2006-06-08 18:56:22 +00:00
fseek(fs, 0, SEEK_SET);
if (fread(head, 1, 512, fs) != 512) {
cli_errmsg("cli_cvdverify: Can't read CVD header\n");
return CL_ECVD;
2003-10-26 06:01:03 +00:00
}
head[512] = 0;
for (i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--)
;
2003-10-08 10:40:53 +00:00
if ((cvd = cl_cvdparse(head)) == NULL)
return CL_ECVD;
2003-10-08 10:40:53 +00:00
if (cvdpt)
memcpy(cvdpt, cvd, sizeof(struct cl_cvd));
2003-10-08 10:40:53 +00:00
if (skipsig) {
cl_cvdfree(cvd);
return CL_SUCCESS;
}
md5 = cli_hashstream(fs, NULL, 1);
2013-04-10 15:28:20 -04:00
if (md5 == NULL) {
cli_dbgmsg("cli_cvdverify: Cannot generate hash, out of memory\n");
cl_cvdfree(cvd);
return CL_EMEM;
2013-04-10 15:28:20 -04:00
}
2003-10-08 10:40:53 +00:00
cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
if (strncmp(md5, cvd->md5, 32)) {
cli_dbgmsg("cli_cvdverify: MD5 verification error\n");
free(md5);
cl_cvdfree(cvd);
return CL_EVERIFY;
2003-10-08 10:40:53 +00:00
}
if (cli_versig(md5, cvd->dsig)) {
cli_dbgmsg("cli_cvdverify: Digital signature verification error\n");
free(md5);
cl_cvdfree(cvd);
return CL_EVERIFY;
2003-10-08 10:40:53 +00:00
}
2003-10-26 06:01:03 +00:00
free(md5);
cl_cvdfree(cvd);
return CL_SUCCESS;
2003-10-08 10:40:53 +00:00
}
bb12506: Fix phishing/heuristic alert verbosity Some detections, like phishing, are considered heuristic alerts because they match based on behavior more than on content. A subset of these are considered "potentially unwanted" (low-severity). These low-severity alerts include: - phishing - PDFs with obfuscated object names - bytecode signature alerts that start with "BC.Heuristics" The concept is that unless you enable "heuristic precedence" (a method of lowing the threshold to immediateley alert on low-severity detections), the scan should continue after a match in case a higher severity match is found. Only at the end will it print the low-severity match if nothing else was found. The current implementation is buggy though. Scanning of archives does not correctly bail out for the entire archive if one email contains a phishing link. Instead, it sets the "heuristic found" flag then and alerts for every subsequent file in the archive because it doesn't know if the heuristic was found in an embedded file or the target file. Because it's just a heuristic and the status is "clean", it keeps scanning. This patch corrects the behavior by checking if a low-severity alerts were found at the end of scanning the target file, instead of at the end of each embedded file. Additionally, this patch fixes an in issue with phishing alerts wherein heuristic precedence mode did not cause a scan to stop after the first alert. The above changes required restructuring to create an fmap inside of cl_scandesc_callback() so that scan_common() could be modified to require an fmap and set up so that the current *ctx->fmap pointer is never NULL when scan_common() evaluates match results. Also fixed a couple minor bugs in the phishing unit tests and cleaned up the test code for improved legitibility and type safety.
2020-04-18 10:46:57 -04:00
cl_error_t cl_cvdverify(const char *file)
2003-10-08 10:40:53 +00:00
{
struct cl_engine *engine;
FILE *fs;
bb12506: Fix phishing/heuristic alert verbosity Some detections, like phishing, are considered heuristic alerts because they match based on behavior more than on content. A subset of these are considered "potentially unwanted" (low-severity). These low-severity alerts include: - phishing - PDFs with obfuscated object names - bytecode signature alerts that start with "BC.Heuristics" The concept is that unless you enable "heuristic precedence" (a method of lowing the threshold to immediateley alert on low-severity detections), the scan should continue after a match in case a higher severity match is found. Only at the end will it print the low-severity match if nothing else was found. The current implementation is buggy though. Scanning of archives does not correctly bail out for the entire archive if one email contains a phishing link. Instead, it sets the "heuristic found" flag then and alerts for every subsequent file in the archive because it doesn't know if the heuristic was found in an embedded file or the target file. Because it's just a heuristic and the status is "clean", it keeps scanning. This patch corrects the behavior by checking if a low-severity alerts were found at the end of scanning the target file, instead of at the end of each embedded file. Additionally, this patch fixes an in issue with phishing alerts wherein heuristic precedence mode did not cause a scan to stop after the first alert. The above changes required restructuring to create an fmap inside of cl_scandesc_callback() so that scan_common() could be modified to require an fmap and set up so that the current *ctx->fmap pointer is never NULL when scan_common() evaluates match results. Also fixed a couple minor bugs in the phishing unit tests and cleaned up the test code for improved legitibility and type safety.
2020-04-18 10:46:57 -04:00
cl_error_t ret;
int dbtype = 0;
if ((fs = fopen(file, "rb")) == NULL) {
cli_errmsg("cl_cvdverify: Can't open file %s\n", file);
return CL_EOPEN;
2003-10-08 10:40:53 +00:00
}
if (!(engine = cl_engine_new())) {
cli_errmsg("cld_cvdverify: Can't create new engine\n");
fclose(fs);
return CL_EMEM;
}
engine->cb_stats_submit = NULL; /* Don't submit stats if we're just verifying a CVD */
if (!!cli_strbcasestr(file, ".cld"))
dbtype = 1;
else if (!!cli_strbcasestr(file, ".cud"))
dbtype = 2;
ret = cli_cvdload(fs, engine, NULL, CL_DB_STDOPT | CL_DB_PUA, dbtype, file, 1);
2003-10-26 06:01:03 +00:00
cl_engine_free(engine);
fclose(fs);
2003-10-26 06:01:03 +00:00
return ret;
2003-10-08 10:40:53 +00:00
}
int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int dbtype, const char *filename, unsigned int chkonly)
2003-09-29 11:44:52 +00:00
{
struct cl_cvd cvd, dupcvd;
FILE *dupfs;
int ret;
time_t s_time;
int cfd;
struct cli_dbio dbio;
struct cli_dbinfo *dbinfo = NULL;
char *dupname;
2003-09-29 11:44:52 +00:00
dbio.hashctx = NULL;
2003-09-29 11:44:52 +00:00
cli_dbgmsg("in cli_cvdload()\n");
2003-10-08 10:40:53 +00:00
/* verify */
if ((ret = cli_cvdverify(fs, &cvd, dbtype)))
return ret;
if (dbtype <= 1) {
/* check for duplicate db */
dupname = cli_strdup(filename);
if (!dupname)
return CL_EMEM;
dupname[strlen(dupname) - 2] = (dbtype == 1 ? 'v' : 'l');
if (!access(dupname, R_OK) && (dupfs = fopen(dupname, "rb"))) {
if ((ret = cli_cvdverify(dupfs, &dupcvd, !dbtype))) {
fclose(dupfs);
free(dupname);
return ret;
}
fclose(dupfs);
if (dupcvd.version > cvd.version) {
cli_warnmsg("Detected duplicate databases %s and %s. The %s database is older and will not be loaded, you should manually remove it from the database directory.\n", filename, dupname, filename);
free(dupname);
return CL_SUCCESS;
} else if (dupcvd.version == cvd.version && !dbtype) {
cli_warnmsg("Detected duplicate databases %s and %s, please manually remove one of them\n", filename, dupname);
free(dupname);
return CL_SUCCESS;
}
}
free(dupname);
}
if (strstr(filename, "daily.")) {
time(&s_time);
if (cvd.stime > s_time) {
if (cvd.stime - (unsigned int)s_time > 3600) {
cli_warnmsg("******************************************************\n");
cli_warnmsg("*** Virus database timestamp in the future! ***\n");
cli_warnmsg("*** Please check the timezone and clock settings ***\n");
cli_warnmsg("******************************************************\n");
}
} else if ((unsigned int)s_time - cvd.stime > 604800) {
cli_warnmsg("**************************************************\n");
cli_warnmsg("*** The virus database is older than 7 days! ***\n");
cli_warnmsg("*** Please update it as soon as possible. ***\n");
cli_warnmsg("**************************************************\n");
}
engine->dbversion[0] = cvd.version;
engine->dbversion[1] = cvd.stime;
}
if (cvd.fl > cl_retflevel()) {
cli_warnmsg("*******************************************************************\n");
cli_warnmsg("*** This version of the ClamAV engine is outdated. ***\n");
cli_warnmsg("*** Read https://docs.clamav.net/manual/Installing.html ***\n");
cli_warnmsg("*******************************************************************\n");
}
cfd = fileno(fs);
dbio.chkonly = 0;
if (dbtype == 2)
ret = cli_tgzload(cfd, engine, signo, options | CL_DB_UNSIGNED, &dbio, NULL);
else
ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, NULL);
if (ret != CL_SUCCESS)
return ret;
dbinfo = engine->dbinfo;
if (!dbinfo || !dbinfo->cvd || (dbinfo->cvd->version != cvd.version) || (dbinfo->cvd->sigs != cvd.sigs) || (dbinfo->cvd->fl != cvd.fl) || (dbinfo->cvd->stime != cvd.stime)) {
cli_errmsg("cli_cvdload: Corrupted CVD header\n");
return CL_EMALFDB;
}
dbinfo = engine->dbinfo ? engine->dbinfo->next : NULL;
if (!dbinfo) {
cli_errmsg("cli_cvdload: dbinfo error\n");
return CL_EMALFDB;
2012-08-10 10:17:22 -04:00
}
dbio.chkonly = chkonly;
if (dbtype == 2)
options |= CL_DB_UNSIGNED;
else
options |= CL_DB_SIGNED | CL_DB_OFFICIAL;
ret = cli_tgzload(cfd, engine, signo, options, &dbio, dbinfo);
while (engine->dbinfo) {
dbinfo = engine->dbinfo;
engine->dbinfo = dbinfo->next;
MPOOL_FREE(engine->mempool, dbinfo->name);
MPOOL_FREE(engine->mempool, dbinfo->hash);
if (dbinfo->cvd)
cl_cvdfree(dbinfo->cvd);
MPOOL_FREE(engine->mempool, dbinfo);
}
return ret;
2003-09-29 11:44:52 +00:00
}
int cli_cvdunpack(const char *file, const char *dir)
{
int fd, ret;
fd = open(file, O_RDONLY | O_BINARY);
if (fd == -1)
return -1;
if (lseek(fd, 512, SEEK_SET) < 0) {
close(fd);
return -1;
}
ret = cli_untgz(fd, dir);
close(fd);
return ret;
}