4cd4319e |
/* |
8b242bb9 |
* Copyright (C) 2003 - 2004 Tomasz Kojm <tkojm@clamav.net> |
4cd4319e |
*
* untgz() is based on public domain minitar utility by Charles G. Waldman
*
* 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
|
4cd4319e |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zlib.h> |
93ef7dc2 |
#include <time.h> |
4cd4319e |
#include "clamav.h" |
c0eb3ceb |
#include "others.h"
#include "dsig.h" |
442d8407 |
#include "str.h" |
4cd4319e |
#define TAR_BLOCKSIZE 512
int cli_untgz(int fd, const char *destdir)
{
char *fullname, osize[13], name[101], type;
char block[TAR_BLOCKSIZE];
int nbytes, nread, nwritten, in_block = 0;
unsigned int size;
FILE *outfile = NULL;
gzFile *infile;
cli_dbgmsg("in cli_untgz()\n");
if((infile = gzdopen(fd, "rb")) == NULL) {
cli_errmsg("Can't gzdopen() descriptor %d\n", fd);
return -1;
}
fullname = (char *) calloc(sizeof(char), strlen(destdir) + 100 + 5);
while(1) {
nread = gzread(infile, block, TAR_BLOCKSIZE);
if(!in_block && nread == 0)
break;
if(nread != TAR_BLOCKSIZE) {
cli_errmsg("Incomplete block read.\n");
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
}
if(!in_block) {
if (block[0] == '\0') /* We're done */
break;
|
7cc3891c |
strncpy(name, block, 100);
name[100] = '\0'; |
4cd4319e |
strcpy(fullname, destdir);
strcat(fullname, "/");
strcat(fullname, name);
cli_dbgmsg("Unpacking %s\n",fullname);
type = block[156];
switch(type) {
case '0':
case '\0':
break;
case '5':
cli_errmsg("Directories in CVD are not supported.\n");
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
default:
cli_errmsg("Unknown type flag %c.\n",type);
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
}
in_block = 1;
if(outfile) {
if(fclose(outfile)) {
cli_errmsg("Cannot close file %s.\n", fullname);
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
}
outfile = NULL;
}
if(!(outfile = fopen(fullname, "wb"))) {
cli_errmsg("Cannot create file %s.\n", fullname);
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
}
|
7cc3891c |
strncpy(osize, block + 124, 12);
osize[12] = '\0'; |
4cd4319e |
|
233c32f2 |
if((sscanf(osize, "%o", &size)) == 0) { |
4cd4319e |
cli_errmsg("Invalid size in header.\n");
free(fullname); |
9c1c9007 |
gzclose(infile); |
25133aef |
fclose(outfile); |
4cd4319e |
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) {
cli_errmsg("Wrote %d instead of %d (%s).\n", nwritten, nbytes, fullname);
free(fullname); |
9c1c9007 |
gzclose(infile); |
4cd4319e |
return -1;
}
size -= nbytes;
if(size == 0)
in_block = 0;
}
}
|
c0eb3ceb |
if(outfile)
fclose(outfile);
|
9c1c9007 |
gzclose(infile);
free(fullname); |
4cd4319e |
return 0;
}
|
442d8407 |
struct cl_cvd *cl_cvdparse(const char *head) |
4cd4319e |
{ |
442d8407 |
char *pt; |
4cd4319e |
struct cl_cvd *cvd; |
c0eb3ceb |
if(strncmp(head, "ClamAV-VDB:", 11)) { |
442d8407 |
cli_dbgmsg("Not a CVD head.\n"); |
c0eb3ceb |
return NULL;
} |
4cd4319e |
cvd = (struct cl_cvd *) cli_calloc(1, sizeof(struct cl_cvd));
|
e8217f5a |
if(!(cvd->time = cli_strtok(head, 1, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract time from header.\n");
free(cvd);
return NULL;
}
|
e8217f5a |
if(!(pt = cli_strtok(head, 2, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract version from header.\n");
free(cvd->time);
free(cvd);
return NULL;
} |
4cd4319e |
cvd->version = atoi(pt);
free(pt);
|
e8217f5a |
if(!(pt = cli_strtok(head, 3, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract signature number from header.\n");
free(cvd->time);
free(cvd);
return NULL;
} |
4cd4319e |
cvd->sigs = atoi(pt);
free(pt);
|
e8217f5a |
if(!(pt = cli_strtok(head, 4, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract functionality level from header.\n");
free(cvd->time);
free(cvd);
return NULL;
} |
4cd4319e |
cvd->fl = (short int) atoi(pt);
free(pt);
|
e8217f5a |
if(!(cvd->md5 = cli_strtok(head, 5, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract MD5 checksum from header.\n");
free(cvd->time);
free(cvd);
return NULL;
}
|
e8217f5a |
if(!(cvd->dsig = cli_strtok(head, 6, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract digital signature from header.\n");
free(cvd->time);
free(cvd->md5);
free(cvd);
return NULL;
}
|
e8217f5a |
if(!(cvd->builder = cli_strtok(head, 7, ":"))) { |
03186fe8 |
cli_errmsg("CVD -> Can't extract builder name from header.\n");
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd);
return NULL;
} |
4cd4319e |
|
93ef7dc2 |
if((pt = cli_strtok(head, 8, ":"))) {
cvd->stime = atoi(pt);
free(pt);
} else
cli_dbgmsg("CVD -> No creation time in seconds (old file format)\n");
|
4cd4319e |
return cvd;
}
struct cl_cvd *cl_cvdhead(const char *file)
{
FILE *fd; |
442d8407 |
char head[513];
int i; |
4cd4319e |
if((fd = fopen(file, "rb")) == NULL) { |
442d8407 |
cli_dbgmsg("Can't open CVD file %s\n", file); |
4cd4319e |
return NULL;
}
|
93ef7dc2 |
if((i = fread(head, 1, 512, fd)) != 512) { |
9c1c9007 |
cli_dbgmsg("Short read (%d) while reading CVD head from %s\n", i, file);
fclose(fd); |
442d8407 |
return NULL;
}
fclose(fd);
head[512] = 0;
for(i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--);
return cl_cvdparse(head); |
4cd4319e |
}
void cl_cvdfree(struct cl_cvd *cvd)
{
free(cvd->time);
free(cvd->md5);
free(cvd->dsig);
free(cvd->builder);
free(cvd);
}
|
93ef7dc2 |
int cli_cvdverify(FILE *fd, struct cl_cvd *cvdpt) |
c0eb3ceb |
{ |
442d8407 |
struct cl_cvd *cvd;
char *md5, head[513];
int i;
fseek(fd, 0, SEEK_SET);
if(fread(head, 1, 512, fd) != 512) {
cli_dbgmsg("Can't read CVD head from stream\n");
return CL_ECVD;
}
head[512] = 0;
for(i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--); |
c0eb3ceb |
|
442d8407 |
if((cvd = cl_cvdparse(head)) == NULL) |
c0eb3ceb |
return CL_ECVD;
|
996d199e |
if(cvdpt) |
93ef7dc2 |
memcpy(cvdpt, cvd, sizeof(struct cl_cvd)); |
c0eb3ceb |
|
d2a12ffd |
md5 = cli_md5stream(fd, NULL); |
c0eb3ceb |
cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
|
442d8407 |
if(strncmp(md5, cvd->md5, 32)) { |
c0eb3ceb |
cli_dbgmsg("MD5 verification error.\n"); |
9c1c9007 |
free(md5);
cl_cvdfree(cvd); |
c0eb3ceb |
return CL_EMD5;
}
#ifdef HAVE_GMP |
442d8407 |
if(cli_versig(md5, cvd->dsig)) { |
c0eb3ceb |
cli_dbgmsg("Digital signature verification error.\n"); |
9c1c9007 |
free(md5);
cl_cvdfree(cvd); |
c0eb3ceb |
return CL_EDSIG;
}
#endif
|
442d8407 |
free(md5); |
9c1c9007 |
cl_cvdfree(cvd); |
c0eb3ceb |
return 0;
}
|
442d8407 |
int cl_cvdverify(const char *file) |
c0eb3ceb |
{
FILE *fd; |
442d8407 |
int ret; |
c0eb3ceb |
if((fd = fopen(file, "rb")) == NULL) {
cli_errmsg("Can't open CVD file %s\n", file); |
442d8407 |
return CL_EOPEN; |
c0eb3ceb |
}
|
93ef7dc2 |
ret = cli_cvdverify(fd, NULL); |
442d8407 |
fclose(fd);
return ret; |
c0eb3ceb |
}
|
4e91e4c7 |
int cli_cvdload(FILE *fd, struct cl_node **root, unsigned int *signo, short warn) |
4cd4319e |
{ |
7b7b3ca5 |
char *dir, *tmp, *buffer; |
93ef7dc2 |
struct cl_cvd cvd; |
c0eb3ceb |
int bytes, ret; |
4cd4319e |
FILE *tmpd; |
93ef7dc2 |
time_t stime;
|
4cd4319e |
cli_dbgmsg("in cli_cvdload()\n");
|
c0eb3ceb |
/* verify */ |
4cd4319e |
|
93ef7dc2 |
if((ret = cli_cvdverify(fd, &cvd))) |
c0eb3ceb |
return ret; |
4cd4319e |
|
4e91e4c7 |
if(cvd.stime && warn) { |
93ef7dc2 |
time(&stime);
if((int) stime - cvd.stime > 604800) {
cli_warnmsg("**************************************************\n");
cli_warnmsg("*** The virus database is older than 7 days. ***\n");
cli_warnmsg("*** Please update it IMMEDIATELY! ***\n");
cli_warnmsg("**************************************************\n");
}
}
|
558d2f03 |
if(cvd.fl > cl_retflevel()) { |
d5e37688 |
cli_warnmsg("********************************************************\n");
cli_warnmsg("*** This version of the ClamAV engine is outdated. ***\n");
cli_warnmsg("*** DON'T PANIC! Read http://www.clamav.net/faq.html ***\n");
cli_warnmsg("********************************************************\n"); |
558d2f03 |
}
|
c0eb3ceb |
fseek(fd, 512, SEEK_SET); |
4cd4319e |
|
e5fa5bab |
dir = cli_gentemp(NULL); |
4cd4319e |
if(mkdir(dir, 0700)) {
cli_errmsg("cli_cvdload(): Can't create temporary directory %s\n", dir);
return CL_ETMPDIR;
}
/*
if(cli_untgz(fileno(fd), dir)) {
cli_errmsg("cli_cvdload(): Can't unpack CVD file.\n");
return CL_ECVDEXTR;
}
*/
|
c0eb3ceb |
/* FIXME: it seems there is some problem with current position indicator
* after gzdopen() call in cli_untgz(). Temporarily we need this wrapper: |
4cd4319e |
*/
/* start */
|
e5fa5bab |
tmp = cli_gentemp(NULL); |
4cd4319e |
if((tmpd = fopen(tmp, "wb+")) == NULL) {
cli_errmsg("Can't create temporary file %s\n", tmp);
free(dir);
free(tmp); |
7b7b3ca5 |
return CL_ETMPFILE; |
4cd4319e |
} |
7b7b3ca5 |
|
25133aef |
if(!(buffer = (char *) cli_malloc(FILEBUFF))) {
free(dir);
free(tmp);
fclose(tmpd); |
7b7b3ca5 |
return CL_EMEM; |
25133aef |
} |
7b7b3ca5 |
while((bytes = fread(buffer, 1, FILEBUFF, fd)) > 0) |
4cd4319e |
fwrite(buffer, 1, bytes, tmpd);
|
7b7b3ca5 |
free(buffer);
|
4cd4319e |
fflush(tmpd);
fseek(tmpd, 0L, SEEK_SET);
if(cli_untgz(fileno(tmpd), dir)) { |
d4494521 |
perror("cli_untgz"); |
4cd4319e |
cli_errmsg("cli_cvdload(): Can't unpack CVD file.\n");
cli_rmdirs(dir);
free(dir); |
25133aef |
fclose(tmpd); |
4cd4319e |
unlink(tmp);
free(tmp);
return CL_ECVDEXTR;
}
fclose(tmpd);
unlink(tmp);
free(tmp);
/* end */
/* load extracted directory */ |
06d4e856 |
cl_loaddbdir(dir, root, signo); |
4cd4319e |
cli_rmdirs(dir);
free(dir);
return 0;
} |