8139fd99 |
/* |
2023340a |
* Copyright (C) 2007-2008 Sourcefire, Inc. |
8139fd99 |
* |
2023340a |
* Authors: Tomasz Kojm |
8139fd99 |
*
* This program is free software; you can redistribute it and/or modify |
bb34cb31 |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
8139fd99 |
*
* 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 |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
8139fd99 |
*/
|
6d6e8271 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
8139fd99 |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> |
b58fdfc2 |
#ifdef HAVE_UNISTD_H |
5ff81952 |
#include <unistd.h> |
b58fdfc2 |
#endif
#include "zlib.h" |
c48c9d2b |
#include <time.h> |
b58fdfc2 |
#include <errno.h> |
8139fd99 |
#include "clamav.h" |
6a2532ca |
#include "others.h"
#include "dsig.h" |
e4ae7726 |
#include "str.h" |
079229d6 |
#include "cvd.h" |
056d95dc |
#include "readdb.h" |
8139fd99 |
#define TAR_BLOCKSIZE 512
|
afff80ef |
#ifndef O_BINARY
#define O_BINARY 0
#endif
static int cli_untgz(int fd, const char *destdir) |
8139fd99 |
{ |
a7ac5978 |
char *path, osize[13], name[101], type; |
8139fd99 |
char block[TAR_BLOCKSIZE]; |
a7ac5978 |
int nbytes, nread, nwritten, in_block = 0, fdd;
unsigned int size, pathlen = strlen(destdir) + 100 + 5; |
8139fd99 |
FILE *outfile = NULL; |
638d881d |
struct stat foo; |
8139fd99 |
gzFile *infile;
|
a7ac5978 |
|
8139fd99 |
cli_dbgmsg("in cli_untgz()\n");
|
a7ac5978 |
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); |
638d881d |
if(fstat(fdd, &foo) == 0)
close(fdd); |
8139fd99 |
return -1;
}
|
a7ac5978 |
path = (char *) cli_calloc(sizeof(char), pathlen);
if(!path) {
cli_errmsg("cli_untgz: Can't allocate memory for path\n"); |
68a364d4 |
gzclose(infile); |
e12c29d2 |
return -1;
} |
8139fd99 |
while(1) {
nread = gzread(infile, block, TAR_BLOCKSIZE);
|
a7ac5978 |
if(!in_block && !nread) |
8139fd99 |
break;
if(nread != TAR_BLOCKSIZE) { |
a7ac5978 |
cli_errmsg("cli_untgz: Incomplete block read\n");
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
}
if(!in_block) {
if (block[0] == '\0') /* We're done */
break;
|
658f19f8 |
strncpy(name, block, 100);
name[100] = '\0'; |
4fac726f |
if(strchr(name, '/')) { |
a7ac5978 |
cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n");
free(path); |
4fac726f |
gzclose(infile);
return -1;
}
|
a7ac5978 |
snprintf(path, pathlen, "%s/%s", destdir, name);
cli_dbgmsg("cli_untgz: Unpacking %s\n", path); |
8139fd99 |
type = block[156];
switch(type) {
case '0':
case '\0':
break;
case '5': |
a7ac5978 |
cli_errmsg("cli_untgz: Directories are not supported in CVD\n");
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
default: |
a7ac5978 |
cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type);
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
}
in_block = 1;
if(outfile) {
if(fclose(outfile)) { |
a7ac5978 |
cli_errmsg("cli_untgz: Cannot close file %s\n", path);
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
}
outfile = NULL;
}
|
a7ac5978 |
if(!(outfile = fopen(path, "wb"))) {
cli_errmsg("cli_untgz: Cannot create file %s\n", path);
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
}
|
658f19f8 |
strncpy(osize, block + 124, 12);
osize[12] = '\0'; |
8139fd99 |
|
bddfdc19 |
if((sscanf(osize, "%o", &size)) == 0) { |
a7ac5978 |
cli_errmsg("cli_untgz: Invalid size in header\n");
free(path); |
9e431a95 |
gzclose(infile); |
bcf3dc79 |
fclose(outfile); |
8139fd99 |
return -1;
}
} else { /* write or continue writing file contents */
nbytes = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size;
nwritten = fwrite(block, 1, nbytes, outfile);
if(nwritten != nbytes) { |
a7ac5978 |
cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path);
free(path); |
9e431a95 |
gzclose(infile); |
8139fd99 |
return -1;
}
size -= nbytes;
if(size == 0)
in_block = 0;
}
}
|
6a2532ca |
if(outfile)
fclose(outfile);
|
9e431a95 |
gzclose(infile); |
a7ac5978 |
free(path); |
8139fd99 |
return 0;
}
|
15850fc6 |
static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options) |
056d95dc |
{
char osize[13], name[101];
char block[TAR_BLOCKSIZE];
int nread, fdd, ret; |
4d7a1184 |
unsigned int type, size, pad, compr = 1; |
e8ae4fae |
off_t off;
struct cli_dbio dbio; |
056d95dc |
|
e8ae4fae |
#define CLOSE_DBIO \
if(compr) \
gzclose(dbio.gzs); \
else \
fclose(dbio.fs) |
056d95dc |
|
9d193ff2 |
cli_dbgmsg("in cli_tgzload()\n"); |
056d95dc |
|
4d7a1184 |
lseek(fd, 512, SEEK_SET);
if(cli_readn(fd, block, 7) != 7)
return CL_EFORMAT; /* truncated file? */
if(!strncmp(block, "COPYING", 7))
compr = 0;
|
e8ae4fae |
lseek(fd, 512, SEEK_SET);
|
056d95dc |
if((fdd = dup(fd)) == -1) {
cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd); |
871177cd |
return CL_EDUP; |
056d95dc |
}
|
e8ae4fae |
if(compr) {
if((dbio.gzs = gzdopen(fdd, "rb")) == NULL) {
cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno); |
871177cd |
return CL_EOPEN; |
e8ae4fae |
} |
283363ff |
dbio.fs = NULL; |
e8ae4fae |
} else {
if((dbio.fs = fdopen(fdd, "rb")) == NULL) {
cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno); |
871177cd |
return CL_EOPEN; |
e8ae4fae |
} |
283363ff |
dbio.gzs = NULL; |
056d95dc |
}
while(1) {
|
e8ae4fae |
if(compr)
nread = gzread(dbio.gzs, block, TAR_BLOCKSIZE);
else
nread = fread(block, 1, TAR_BLOCKSIZE, dbio.fs); |
056d95dc |
if(!nread)
break;
if(nread != TAR_BLOCKSIZE) {
cli_errmsg("cli_tgzload: Incomplete block read\n"); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
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"); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
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"); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
return CL_EMALFDB;
default:
cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
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"); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
return CL_EMALFDB;
} |
e8ae4fae |
dbio.size = size; |
056d95dc |
/* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */ |
e8ae4fae |
if(compr)
off = (off_t) gzseek(dbio.gzs, 0, SEEK_CUR);
else
off = ftell(dbio.fs);
|
056d95dc |
if(CLI_DBEXT(name)) { |
e8ae4fae |
ret = cli_load(name, engine, signo, options, &dbio); |
056d95dc |
if(ret) { |
283363ff |
cli_errmsg("cli_tgzload: Can't load %s\n", name); |
e8ae4fae |
CLOSE_DBIO; |
056d95dc |
return CL_EMALFDB;
}
} |
0a26aef4 |
pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0; |
e8ae4fae |
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);
} |
056d95dc |
}
|
e8ae4fae |
CLOSE_DBIO;
return CL_SUCCESS; |
056d95dc |
}
|
e4ae7726 |
struct cl_cvd *cl_cvdparse(const char *head) |
8139fd99 |
{
struct cl_cvd *cvd; |
a7ac5978 |
char *pt;
|
6a2532ca |
if(strncmp(head, "ClamAV-VDB:", 11)) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Not a CVD file\n"); |
6a2532ca |
return NULL;
} |
8139fd99 |
|
a7ac5978 |
if(!(cvd = (struct cl_cvd *) cli_malloc(sizeof(struct cl_cvd)))) { |
e12c29d2 |
cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n");
return NULL;
} |
8139fd99 |
|
2d70a403 |
if(!(cvd->time = cli_strtok(head, 1, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the creation time\n"); |
976bcd2a |
free(cvd);
return NULL;
}
|
2d70a403 |
if(!(pt = cli_strtok(head, 2, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the version number\n"); |
976bcd2a |
free(cvd->time);
free(cvd);
return NULL;
} |
8139fd99 |
cvd->version = atoi(pt);
free(pt);
|
2d70a403 |
if(!(pt = cli_strtok(head, 3, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n"); |
976bcd2a |
free(cvd->time);
free(cvd);
return NULL;
} |
8139fd99 |
cvd->sigs = atoi(pt);
free(pt);
|
2d70a403 |
if(!(pt = cli_strtok(head, 4, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the functionality level\n"); |
976bcd2a |
free(cvd->time);
free(cvd);
return NULL;
} |
b5134815 |
cvd->fl = atoi(pt); |
8139fd99 |
free(pt);
|
2d70a403 |
if(!(cvd->md5 = cli_strtok(head, 5, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n"); |
976bcd2a |
free(cvd->time);
free(cvd);
return NULL;
}
|
2d70a403 |
if(!(cvd->dsig = cli_strtok(head, 6, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the digital signature\n"); |
976bcd2a |
free(cvd->time);
free(cvd->md5);
free(cvd);
return NULL;
}
|
2d70a403 |
if(!(cvd->builder = cli_strtok(head, 7, ":"))) { |
a7ac5978 |
cli_errmsg("cli_cvdparse: Can't parse the builder name\n"); |
976bcd2a |
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd);
return NULL;
} |
8139fd99 |
|
c48c9d2b |
if((pt = cli_strtok(head, 8, ":"))) {
cvd->stime = atoi(pt);
free(pt); |
a7ac5978 |
} else {
cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n");
cvd->stime = 0;
} |
c48c9d2b |
|
8139fd99 |
return cvd;
}
struct cl_cvd *cl_cvdhead(const char *file)
{ |
1ccd6e94 |
FILE *fs; |
bd5f3ce0 |
char head[513], *pt; |
e4ae7726 |
int i; |
b4bd1749 |
unsigned int bread;
|
8139fd99 |
|
1ccd6e94 |
if((fs = fopen(file, "rb")) == NULL) { |
a7ac5978 |
cli_errmsg("cl_cvdhead: Can't open file %s\n", file); |
8139fd99 |
return NULL;
}
|
b4bd1749 |
if(!(bread = fread(head, 1, 512, fs))) { |
a7ac5978 |
cli_errmsg("cl_cvdhead: Can't read CVD header in %s\n", file); |
1ccd6e94 |
fclose(fs); |
e4ae7726 |
return NULL;
}
|
1ccd6e94 |
fclose(fs); |
e4ae7726 |
|
b4bd1749 |
head[bread] = 0; |
bd5f3ce0 |
if((pt = strpbrk(head, "\n\r")))
*pt = 0;
|
b4bd1749 |
for(i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--); |
e4ae7726 |
return cl_cvdparse(head); |
8139fd99 |
}
void cl_cvdfree(struct cl_cvd *cvd)
{
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd->builder);
free(cvd);
}
|
9d193ff2 |
static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int cld) |
6a2532ca |
{ |
e4ae7726 |
struct cl_cvd *cvd;
char *md5, head[513];
int i;
|
a7ac5978 |
|
1ccd6e94 |
fseek(fs, 0, SEEK_SET);
if(fread(head, 1, 512, fs) != 512) { |
a7ac5978 |
cli_errmsg("cli_cvdverify: Can't read CVD header\n"); |
e4ae7726 |
return CL_ECVD;
}
head[512] = 0;
for(i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--); |
6a2532ca |
|
e4ae7726 |
if((cvd = cl_cvdparse(head)) == NULL) |
6a2532ca |
return CL_ECVD;
|
e685f551 |
if(cvdpt) |
c48c9d2b |
memcpy(cvdpt, cvd, sizeof(struct cl_cvd)); |
6a2532ca |
|
9d193ff2 |
if(cld) {
cl_cvdfree(cvd);
return CL_SUCCESS;
}
|
1ccd6e94 |
md5 = cli_md5stream(fs, NULL); |
6a2532ca |
cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
|
e4ae7726 |
if(strncmp(md5, cvd->md5, 32)) { |
a7ac5978 |
cli_dbgmsg("cli_cvdverify: MD5 verification error\n"); |
9e431a95 |
free(md5);
cl_cvdfree(cvd); |
871177cd |
return CL_EVERIFY; |
6a2532ca |
}
|
e4ae7726 |
if(cli_versig(md5, cvd->dsig)) { |
a7ac5978 |
cli_dbgmsg("cli_cvdverify: Digital signature verification error\n"); |
9e431a95 |
free(md5);
cl_cvdfree(cvd); |
871177cd |
return CL_EVERIFY; |
6a2532ca |
}
|
e4ae7726 |
free(md5); |
9e431a95 |
cl_cvdfree(cvd); |
9d193ff2 |
return CL_SUCCESS; |
6a2532ca |
}
|
e4ae7726 |
int cl_cvdverify(const char *file) |
6a2532ca |
{ |
1ccd6e94 |
FILE *fs; |
e4ae7726 |
int ret; |
6a2532ca |
|
a7ac5978 |
|
1ccd6e94 |
if((fs = fopen(file, "rb")) == NULL) { |
a7ac5978 |
cli_errmsg("cl_cvdverify: Can't open file %s\n", file); |
e4ae7726 |
return CL_EOPEN; |
6a2532ca |
}
|
9d193ff2 |
ret = cli_cvdverify(fs, NULL, 0); |
1ccd6e94 |
fclose(fs); |
e4ae7726 |
return ret; |
6a2532ca |
}
|
15850fc6 |
int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int daily, unsigned int options, unsigned int cld) |
8139fd99 |
{ |
1ccd6e94 |
char *dir; |
c48c9d2b |
struct cl_cvd cvd; |
a7ac5978 |
int ret; |
fc83da82 |
time_t s_time; |
2782c743 |
int cfd; |
8139fd99 |
cli_dbgmsg("in cli_cvdload()\n");
|
6a2532ca |
/* verify */ |
8139fd99 |
|
9d193ff2 |
if((ret = cli_cvdverify(fs, &cvd, cld))) |
6a2532ca |
return ret; |
8139fd99 |
|
ac1b219c |
if(cvd.stime && daily) { |
fc83da82 |
time(&s_time); |
72a82dd6 |
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) { |
c48c9d2b |
cli_warnmsg("**************************************************\n"); |
a7ac5978 |
cli_warnmsg("*** The virus database is older than 7 days! ***\n");
cli_warnmsg("*** Please update it as soon as possible. ***\n"); |
c48c9d2b |
cli_warnmsg("**************************************************\n");
}
}
|
059662de |
if(cvd.fl > cl_retflevel()) { |
61409916 |
cli_warnmsg("***********************************************************\n");
cli_warnmsg("*** This version of the ClamAV engine is outdated. ***\n");
cli_warnmsg("*** DON'T PANIC! Read http://www.clamav.net/support/faq ***\n");
cli_warnmsg("***********************************************************\n"); |
059662de |
}
|
2782c743 |
cfd = fileno(fs);
/* use only operations on file descriptors, and not on the FILE* from here on
* if we seek the FILE*, the underlying descriptor may not seek as expected
* (for example on OpenBSD, cygwin, etc.).
* So seek the descriptor directly.
*/
if(lseek(cfd, 512, SEEK_SET) == -1) {
cli_errmsg("cli_cvdload(): lseek(fs, 512, SEEK_SET) failed\n"); |
871177cd |
return CL_ESEEK; |
2782c743 |
}
|
ac1b219c |
if(daily) { |
15850fc6 |
engine->dbversion[0] = cvd.version;
engine->dbversion[1] = cvd.stime; |
ac1b219c |
}
|
056d95dc |
if(options & CL_DB_CVDNOTMP) { |
8139fd99 |
|
b5513f8d |
return cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL); |
8139fd99 |
|
056d95dc |
} else { |
8139fd99 |
|
33068e09 |
if(!(dir = cli_gentemp(engine->tmpdir))) |
5fc380f1 |
return CL_EMEM;
|
056d95dc |
if(mkdir(dir, 0700)) {
cli_errmsg("cli_cvdload(): Can't create temporary directory %s\n", dir);
free(dir);
return CL_ETMPDIR;
}
if(cli_untgz(cfd, dir)) {
cli_errmsg("cli_cvdload(): Can't unpack CVD file.\n");
free(dir); |
871177cd |
return CL_ECVD; |
056d95dc |
}
/* load extracted directory */ |
b5513f8d |
ret = cl_load(dir, engine, signo, options | CL_DB_OFFICIAL); |
056d95dc |
cli_rmdirs(dir);
free(dir);
return ret;
} |
8139fd99 |
} |
afff80ef |
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;
} |