e3aaff8e |
/* |
8ca8a18e |
* Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net> |
bb34cb31 |
* CDIFF code (C) 2006 Sensory Networks, Inc.
* Written by Tomasz Kojm |
e3aaff8e |
*
* 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. |
e3aaff8e |
*
* 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. |
e3aaff8e |
*/
|
6d6e8271 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
e3aaff8e |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> |
8139fd99 |
#include <zlib.h>
#include <time.h>
#include <locale.h> |
ae307914 |
#include <sys/types.h> |
08d6b1e3 |
#include <sys/stat.h>
#include <fcntl.h> |
ae307914 |
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h> |
2d70a403 |
#include <sys/wait.h> |
0a2ad257 |
#include <dirent.h> |
e3aaff8e |
|
f0d0a4c0 |
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
|
6a31c2b4 |
#include "vba.h" |
e3aaff8e |
|
3d167464 |
#include "shared/options.h"
#include "shared/output.h"
#include "shared/cfgparser.h"
#include "shared/misc.h" |
bb990ecd |
#include "shared/cdiff.h" |
d1c685b8 |
#include "shared/sha256.h" |
e3aaff8e |
|
e48dcea9 |
#include "libclamav/clamav.h" |
3d167464 |
#include "libclamav/cvd.h"
#include "libclamav/others.h"
#include "libclamav/str.h"
#include "libclamav/ole2_extract.h"
#include "libclamav/htmlnorm.h" |
e3aaff8e |
|
6b5d21a3 |
#define MAX_DEL_LOOKAHEAD 200 |
e3aaff8e |
|
3d167464 |
static int hexdump(void)
{
char buffer[FILEBUFF], *pt;
int bytes; |
6a2532ca |
|
e3aaff8e |
|
3d167464 |
while((bytes = read(0, buffer, FILEBUFF)) > 0) {
pt = cli_str2hex(buffer, bytes);
if(write(1, pt, 2 * bytes) == -1) { |
efa239e2 |
mprintf("!hexdump: Can't write to stdout\n"); |
3d167464 |
free(pt);
return -1;
}
free(pt); |
e3aaff8e |
}
|
3d167464 |
if(bytes == -1)
return -1; |
e3aaff8e |
|
3d167464 |
return 0;
} |
e3aaff8e |
|
60892bc1 |
static int md5sig(struct optstruct *opt, unsigned int mdb) |
3d167464 |
{
char *md5, *filename;
int i;
struct stat sb; |
a36e6e5c |
|
bcf3dc79 |
|
3d167464 |
if(opt->filename) {
for(i = 0; (filename = cli_strtok(opt->filename, i, "\t")); i++) {
if(stat(filename, &sb) == -1) { |
efa239e2 |
mprintf("!md5sig: Can't access file %s\n", filename); |
3d167464 |
perror("md5sig");
free(filename);
return -1;
} else {
if((sb.st_mode & S_IFMT) == S_IFREG) {
if((md5 = cli_md5file(filename))) { |
60892bc1 |
if(mdb) |
797c5b1e |
mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, md5, filename); |
60892bc1 |
else |
797c5b1e |
mprintf("%s:%u:%s\n", md5, (unsigned int) sb.st_size, filename); |
3d167464 |
free(md5);
} else { |
efa239e2 |
mprintf("!md5sig: Can't generate MD5 checksum for %s\n", filename); |
3d167464 |
free(filename);
return -1; |
bcf3dc79 |
}
}
}
|
3d167464 |
free(filename); |
08d6b1e3 |
}
|
3d167464 |
} else { /* stream */
md5 = cli_md5stream(stdin, NULL);
if(!md5) { |
efa239e2 |
mprintf("!md5sig: Can't generate MD5 checksum for input stream\n"); |
3d167464 |
return -1; |
ae307914 |
} |
efa239e2 |
mprintf("%s\n", md5); |
3d167464 |
free(md5);
} |
8139fd99 |
|
3d167464 |
return 0;
} |
8139fd99 |
|
3d167464 |
static int htmlnorm(struct optstruct *opt)
{
int fd; |
0f387b1b |
|
3d167464 |
if((fd = open(opt_arg(opt, "html-normalise"), O_RDONLY)) == -1) { |
efa239e2 |
mprintf("!htmlnorm: Can't open file %s\n", opt_arg(opt, "html-normalise")); |
3d167464 |
return -1;
} |
55216b6e |
|
692bda68 |
html_normalise_fd(fd, ".", NULL, NULL); |
3d167464 |
close(fd); |
55216b6e |
|
3d167464 |
return 0;
} |
8139fd99 |
|
ec5e029e |
static int utf16decode(struct optstruct *opt)
{
const char *fname;
char *newname, buff[512], *decoded; |
4f411f34 |
int fd1, fd2, bytes; |
ec5e029e |
fname = opt_arg(opt, "utf16-decode");
if((fd1 = open(fname, O_RDONLY)) == -1) {
mprintf("!utf16decode: Can't open file %s\n", fname);
return -1;
}
newname = malloc(strlen(fname) + 7);
sprintf(newname, "%s.ascii", fname);
if((fd2 = open(newname, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
mprintf("!utf16decode: Can't create file %s\n", newname);
free(newname);
close(fd1);
return -1;
}
while((bytes = read(fd1, buff, sizeof(buff))) > 0) {
decoded = cli_utf16toascii(buff, bytes);
if(decoded) {
if(write(fd2, decoded, strlen(decoded)) == -1) {
mprintf("!utf16decode: Can't write to file %s\n", newname);
free(decoded);
unlink(newname);
free(newname);
close(fd1);
close(fd2);
return -1;
}
free(decoded);
}
}
free(newname);
close(fd1);
close(fd2);
return 0;
}
|
3d167464 |
static unsigned int countlines(const char *filename)
{
FILE *fd;
char buff[1024];
unsigned int lines = 0; |
8139fd99 |
|
0a2ad257 |
|
3d167464 |
if((fd = fopen(filename, "r")) == NULL)
return 0; |
0a2ad257 |
|
3d167464 |
while(fgets(buff, sizeof(buff), fd)) {
if(buff[0] == '#') continue;
lines++;
} |
9d0b7ebd |
|
3d167464 |
fclose(fd);
return lines;
} |
9d0b7ebd |
|
5b68299b |
static char *getdsig(const char *host, const char *user, const char *data, unsigned int datalen, unsigned short mode) |
3d167464 |
{ |
5b68299b |
char buff[512], cmd[128], pass[30], *pt; |
3d167464 |
struct sockaddr_in server;
int sockd, bread, len; |
f0d0a4c0 |
#ifdef HAVE_TERMIOS_H
struct termios old, new;
#endif |
9d0b7ebd |
|
4f411f34 |
if((pt = getenv("SIGNDPASS"))) {
strncpy(pass, pt, sizeof(pass));
} else {
fflush(stdin);
mprintf("Password: ");
#ifdef HAVE_TERMIOS_H
if(tcgetattr(0, &old)) {
mprintf("!getdsig: tcgetattr() failed\n");
return NULL;
}
new = old;
new.c_lflag &= ~ECHO;
if(tcsetattr(0, TCSAFLUSH, &new)) {
mprintf("!getdsig: tcsetattr() failed\n");
return NULL;
}
#endif
if(fgets(pass, sizeof(pass), stdin)) {
cli_chomp(pass);
} else {
mprintf("!getdsig: Can't get password\n");
return NULL;
}
#ifdef HAVE_TERMIOS_H
if(tcsetattr(0, TCSAFLUSH, &old)) { |
797c5b1e |
mprintf("!getdsig: tcsetattr() failed\n"); |
4f411f34 |
memset(pass, 0, strlen(pass));
return NULL;
}
#endif
mprintf("\n");
}
|
3d167464 |
#ifdef PF_INET
if((sockd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
#else
if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#endif
perror("socket()"); |
efa239e2 |
mprintf("!getdsig: Can't create socket\n"); |
4f411f34 |
memset(pass, 0, strlen(pass)); |
3d167464 |
return NULL;
} |
9d0b7ebd |
|
3d167464 |
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(host);
server.sin_port = htons(33101); |
e3aaff8e |
|
3d167464 |
if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0) {
close(sockd);
perror("connect()"); |
efa239e2 |
mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host); |
4f411f34 |
memset(pass, 0, strlen(pass)); |
3d167464 |
return NULL; |
e3aaff8e |
} |
3d167464 |
memset(cmd, 0, sizeof(cmd)); |
f0d0a4c0 |
|
5b68299b |
if(mode == 1)
snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass);
else
snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
|
3d167464 |
len = strlen(cmd);
pt = cmd + len; |
5b68299b |
memcpy(pt, data, datalen);
len += datalen; |
e4ae7726 |
|
3d167464 |
if(write(sockd, cmd, len) < 0) { |
efa239e2 |
mprintf("!getdsig: Can't write to socket\n"); |
3d167464 |
close(sockd);
memset(cmd, 0, len);
memset(pass, 0, strlen(pass));
return NULL;
} |
19e2ac07 |
|
3d167464 |
memset(cmd, 0, len);
memset(pass, 0, strlen(pass));
memset(buff, 0, sizeof(buff)); |
e4ae7726 |
|
827bb6ca |
if((bread = cli_readn(sockd, buff, sizeof(buff))) > 0) { |
3d167464 |
if(!strstr(buff, "Signature:")) { |
efa239e2 |
mprintf("!getdsig: Error generating digital signature\n");
mprintf("!getdsig: Answer from remote server: %s\n", buff); |
3d167464 |
close(sockd);
return NULL;
} else { |
827bb6ca |
mprintf("Signature received (length = %u)\n", strlen(buff) - 10); |
3d167464 |
} |
827bb6ca |
} else {
mprintf("!getdsig: Communication error with remote server\n");
close(sockd);
return NULL; |
19e2ac07 |
} |
e4ae7726 |
|
3d167464 |
close(sockd); |
827bb6ca |
|
3d167464 |
pt = buff;
pt += 10;
return strdup(pt); |
e4ae7726 |
}
|
e3bcc33a |
static int writeinfo(const char *db, const char *header) |
0a4afbbd |
{
FILE *fh;
int i;
struct stat sb;
char file[32], *md5; |
70edb085 |
const char *const extlist[] = { "db", "fp", "hdb", "hdu", "mdb", "mdu", "ndb", "ndu", "pdb", "wdb", "rmd", "zmd", "sdb", "cfg", NULL }; |
0a4afbbd |
snprintf(file, sizeof(file), "%s.info", db);
if(stat(file, &sb) != -1) {
if(unlink(file) == -1) { |
efa239e2 |
mprintf("!writeinfo: Can't unlink %s\n", file); |
0a4afbbd |
return -1;
}
}
if(!(fh = fopen(file, "w"))) { |
efa239e2 |
mprintf("!writeinfo: Can't create file %s\n", file); |
0a4afbbd |
return -1;
}
|
e3bcc33a |
if(fprintf(fh, "%s\n", header) < 0) {
mprintf("!writeinfo: Can't write to %s\n", file); |
0a4afbbd |
fclose(fh);
return -1;
}
for(i = 0; extlist[i]; i++) {
snprintf(file, sizeof(file), "%s.%s", db, extlist[i]);
if(stat(file, &sb) != -1) {
if(!(md5 = cli_md5file(file))) { |
efa239e2 |
mprintf("!writeinfo: Can't generate MD5 checksum for %s\n", file); |
0a4afbbd |
fclose(fh);
return -1;
}
if(fprintf(fh, "%s.%s:%s\n", db, extlist[i], md5) < 0) { |
efa239e2 |
mprintf("!writeinfo: Can't write to info file\n"); |
0a4afbbd |
fclose(fh);
free(md5);
return -1;
}
free(md5);
}
}
fclose(fh);
return 0;
}
|
1aa405c3 |
static int diffdirs(const char *old, const char *new, const char *patch); |
5b68299b |
static int verifydiff(const char *diff, const char *cvd, const char *incdir);
static int script2cdiff(const char *script, const char *builder, struct optstruct *opt)
{
char *cdiff, *pt, buffer[FILEBUFF];
unsigned char digest[32];
SHA256_CTX ctx;
struct stat sb;
FILE *scripth, *cdiffh;
gzFile *gzh;
unsigned int ver, osize;
int bytes;
if(stat(script, &sb) == -1) {
mprintf("!script2diff: Can't stat file %s\n", script);
return -1;
}
osize = (unsigned int) sb.st_size;
cdiff = strdup(script);
pt = strstr(cdiff, ".script");
if(!pt) {
mprintf("!script2cdiff: Incorrect file name (no .script extension)\n");
free(cdiff);
return -1;
}
strcpy(pt, ".cdiff");
if(!(pt = strchr(script, '-'))) {
mprintf("!script2cdiff: Incorrect file name syntax\n");
free(cdiff);
return -1;
}
sscanf(++pt, "%u.script", &ver);
if(!(cdiffh = fopen(cdiff, "wb"))) {
mprintf("!script2cdiff: Can't open %s for writing\n", cdiff);
free(cdiff);
return -1;
}
if(fprintf(cdiffh, "ClamAV-Diff:%u:%u:", ver, osize) < 0) {
mprintf("!script2cdiff: Can't write to %s\n", cdiff);
fclose(cdiffh);
free(cdiff);
return -1;
}
fclose(cdiffh);
if(!(scripth = fopen(script, "rb"))) {
mprintf("!script2cdiff: Can't open file %s for reading\n", script);
unlink(cdiff);
free(cdiff);
return -1;
}
if(!(gzh = gzopen(cdiff, "ab"))) {
mprintf("!script2cdiff: Can't open file %s for appending\n", cdiff);
unlink(cdiff);
free(cdiff);
fclose(scripth);
return -1;
}
while((bytes = fread(buffer, 1, sizeof(buffer), scripth)) > 0) {
if(!gzwrite(gzh, buffer, bytes)) {
mprintf("!script2cdiff: Can't gzwrite to %s\n", cdiff);
unlink(cdiff);
free(cdiff);
fclose(scripth);
gzclose(gzh);
return -1;
}
}
fclose(scripth);
gzclose(gzh);
if(!(cdiffh = fopen(cdiff, "rb"))) {
mprintf("!script2cdiff: Can't open %s for reading/writing\n", cdiff);
unlink(cdiff);
free(cdiff);
return -1;
}
sha256_init(&ctx);
while((bytes = fread(buffer, 1, sizeof(buffer), cdiffh)))
sha256_update(&ctx, (unsigned char *) buffer, bytes);
fclose(cdiffh);
sha256_final(&ctx);
sha256_digest(&ctx, digest);
if(!(pt = getdsig(opt_arg(opt, "server"), builder, (char *) digest, 32, 1))) {
mprintf("!script2cdiff: Can't get digital signature from remote server\n");
unlink(cdiff);
free(cdiff);
return -1;
}
if(!(cdiffh = fopen(cdiff, "ab"))) {
mprintf("!script2cdiff: Can't open %s for appending\n", cdiff);
unlink(cdiff);
free(cdiff);
return -1;
}
fprintf(cdiffh, ":%s", pt);
free(pt);
fclose(cdiffh);
mprintf("Created %s\n", cdiff);
free(cdiff);
return 0;
} |
1aa405c3 |
|
3d167464 |
static int build(struct optstruct *opt) |
8139fd99 |
{ |
050c6e4d |
int ret, inc = 1, dn; |
3d167464 |
size_t bytes; |
d08d2bd0 |
unsigned int sigs = 0, oldsigs = 0, lines = 0, version, real_header, fl; |
8139fd99 |
struct stat foo; |
1aa405c3 |
char buffer[FILEBUFF], *tarfile, *gzfile, header[513], smbuff[32], |
797c5b1e |
builder[32], *pt, olddb[512], patch[32], broken[32];
const char *dbname; |
200e94b5 |
struct cl_engine *engine = NULL; |
3d167464 |
FILE *tar, *cvd; |
8139fd99 |
gzFile *gz;
time_t timet;
struct tm *brokent; |
0a4afbbd |
struct cl_cvd *oldcvd; |
8139fd99 |
|
3d167464 |
if(!opt_check(opt, "server")) { |
efa239e2 |
mprintf("!build: --server is required for --build\n"); |
3d167464 |
return -1; |
8139fd99 |
}
|
3d167464 |
if(stat("COPYING", &foo) == -1) { |
efa239e2 |
mprintf("!build: COPYING file not found in current working directory.\n"); |
3d167464 |
return -1; |
8139fd99 |
}
|
3d167464 |
if(stat("main.db", &foo) == -1 && stat("daily.db", &foo) == -1 &&
stat("main.hdb", &foo) == -1 && stat("daily.hdb", &foo) == -1 && |
70edb085 |
stat("main.hdu", &foo) == -1 && stat("daily.hdu", &foo) == -1 && |
c09d6c19 |
stat("main.mdb", &foo) == -1 && stat("daily.mdb", &foo) == -1 && |
70edb085 |
stat("main.mdu", &foo) == -1 && stat("daily.mdu", &foo) == -1 && |
3d167464 |
stat("main.ndb", &foo) == -1 && stat("daily.ndb", &foo) == -1 && |
70edb085 |
stat("main.ndu", &foo) == -1 && stat("daily.ndu", &foo) == -1 && |
508268f3 |
stat("main.pdb", &foo) == -1 && stat("daily.pdb", &foo) == -1 && |
0a4afbbd |
stat("main.sdb", &foo) == -1 && stat("daily.sdb", &foo) == -1 && |
3d167464 |
stat("main.zmd", &foo) == -1 && stat("daily.zmd", &foo) == -1 &&
stat("main.rmd", &foo) == -1 && stat("daily.rmd", &foo) == -1)
{ |
efa239e2 |
mprintf("!build: No virus database file found in current directory\n"); |
3d167464 |
return -1; |
8139fd99 |
}
|
4f411f34 |
if((ret = cl_load(".", &engine, &sigs, CL_DB_STDOPT))) { |
efa239e2 |
mprintf("!build: Can't load database: %s\n", cl_strerror(ret)); |
3d167464 |
return -1;
} else { |
200e94b5 |
cl_free(engine); |
3d167464 |
} |
8139fd99 |
|
3d167464 |
if(!sigs) { |
efa239e2 |
mprintf("!build: There are no signatures in database files\n"); |
0f387b1b |
} else { |
3d167464 |
lines = countlines("main.db") + countlines("daily.db") +
countlines("main.hdb") + countlines("daily.hdb") + |
70edb085 |
countlines("main.hdu") + countlines("daily.hdu") + |
c09d6c19 |
countlines("main.mdb") + countlines("daily.mdb") + |
70edb085 |
countlines("main.mdu") + countlines("daily.mdu") + |
3d167464 |
countlines("main.ndb") + countlines("daily.ndb") + |
70edb085 |
countlines("main.ndu") + countlines("daily.ndu") + |
0a4afbbd |
countlines("main.sdb") + countlines("daily.sdb") + |
3d167464 |
countlines("main.zmd") + countlines("daily.zmd") +
countlines("main.rmd") + countlines("daily.rmd") +
countlines("main.fp") + countlines("daily.fp");
if(lines != sigs) { |
d08d2bd0 |
mprintf("^build: Signatures in database: %d, loaded by libclamav: %d\n", lines, sigs);
mprintf("^build: Please check the current directory and remove unnecessary databases\n");
mprintf("^build: or install the latest ClamAV version.\n"); |
0f387b1b |
}
} |
8139fd99 |
|
0a4afbbd |
/* try to read cvd header of current database */ |
1aa405c3 |
dbname = opt_arg(opt, "build");
if(strstr(dbname, "main"))
dbname = "main";
else
dbname = "daily";
|
c4cbb847 |
if(opt->filename) {
if(cli_strbcasestr(opt->filename, ".cvd")) {
strncpy(olddb, opt->filename, sizeof(olddb));
inc = 0;
} else if(cli_strbcasestr(opt->filename, ".inc")) {
snprintf(olddb, sizeof(olddb), "%s/%s.info", opt->filename, dbname);
} else {
mprintf("!build: The optional argument points to neither CVD nor incremental directory\n");
return -1;
}
} else {
pt = freshdbdir();
snprintf(olddb, sizeof(olddb), "%s/%s.inc/%s.info", pt, dbname, dbname);
if(stat(olddb, &foo) == -1) {
inc = 0;
snprintf(olddb, sizeof(olddb), "%s/%s.cvd", pt, dbname);
}
free(pt);
} |
1aa405c3 |
if(!(oldcvd = cl_cvdhead(olddb))) { |
c4cbb847 |
mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s\n", olddb); |
0a4afbbd |
sleep(3);
}
if(oldcvd) {
version = oldcvd->version + 1; |
b336436b |
oldsigs = oldcvd->sigs; |
0a4afbbd |
cl_cvdfree(oldcvd);
} else {
fflush(stdin); |
efa239e2 |
mprintf("Version number: "); |
0a4afbbd |
scanf("%u", &version);
}
|
b336436b |
mprintf("Total sigs: %u\n", sigs);
if(sigs > oldsigs)
mprintf("New sigs: %u\n", sigs - oldsigs);
|
e3bcc33a |
strcpy(header, "ClamAV-VDB:");
/* time */
time(&timet);
brokent = localtime(&timet);
setlocale(LC_TIME, "C");
strftime(smbuff, sizeof(smbuff), "%d %b %Y %H-%M %z", brokent);
strcat(header, smbuff);
/* version */
sprintf(smbuff, ":%d:", version);
strcat(header, smbuff);
/* number of signatures */
sprintf(smbuff, "%d:", sigs);
strcat(header, smbuff);
/* functionality level */ |
d08d2bd0 |
if(!strcmp(dbname, "main")) {
fflush(stdin);
mprintf("Functionality level: "); |
a47ed53d |
if(fgets(smbuff, sizeof(smbuff), stdin)) {
cli_chomp(smbuff);
} else {
mprintf("!build: Can't get functionality level\n");
return -1;
}
fl = atoi(smbuff);
if(!fl || fl > 99) {
mprintf("!build: Incorrect functionality level\n");
return -1;
} |
d08d2bd0 |
} else {
fl = cl_retflevel();
}
sprintf(smbuff, "%u:", fl); |
e3bcc33a |
strcat(header, smbuff);
real_header = strlen(header);
/* add fake MD5 and dsig (for writeinfo) */
strcat(header, "X:X:");
|
4f411f34 |
if((pt = getenv("SIGNDUSER"))) {
strncpy(builder, pt, sizeof(builder)); |
e3bcc33a |
} else { |
4f411f34 |
/* ask for builder name */
fflush(stdin);
mprintf("Builder name: ");
if(fgets(builder, sizeof(builder), stdin)) {
cli_chomp(builder);
} else {
mprintf("!build: Can't get builder name\n");
return -1;
} |
e3bcc33a |
}
/* add builder */
strcat(header, builder);
/* add current time */
sprintf(header + strlen(header), ":%d", (int) timet);
|
1aa405c3 |
if(writeinfo(dbname, header) == -1) { |
efa239e2 |
mprintf("!build: Can't generate info file\n"); |
0a4afbbd |
return -1;
}
|
e3bcc33a |
header[real_header] = 0;
|
3d167464 |
if(!(tarfile = cli_gentemp("."))) { |
efa239e2 |
mprintf("!build: Can't generate temporary name for tarfile\n"); |
3d167464 |
return -1;
} |
8139fd99 |
switch(fork()) {
case -1: |
efa239e2 |
mprintf("!build: Can't fork.\n"); |
3d167464 |
free(tarfile);
return -1; |
8139fd99 |
case 0:
{ |
797c5b1e |
const char *args[] = { "tar", "-cvf", NULL, "COPYING", "main.db", |
3d167464 |
"daily.db", "main.hdb", "daily.hdb", |
70edb085 |
"main.hdu", "daily.hdu", "main.ndb",
"daily.ndb", "main.ndu", "daily.ndu",
"main.sdb", "daily.sdb", "main.zmd",
"daily.zmd", "main.rmd", "daily.rmd", |
7021b545 |
"main.fp", "daily.fp", "daily.ft", "main.mdb", |
70edb085 |
"daily.mdb", "main.mdu", "daily.mdu", |
508268f3 |
"daily.info", "main.info", "main.wdb",
"daily.wdb", "main.pdb", "daily.pdb", |
0714ac10 |
"main.cfg", "daily.cfg", |
7dec7955 |
NULL }; |
0aaf4b37 |
args[2] = tarfile; |
1aa405c3 |
if(!opt_check(opt, "debug")) { |
050c6e4d |
if((dn = open("/dev/null", O_WRONLY)) == -1) {
mprintf("^Cannot open /dev/null\n");
close(1);
close(2);
} else {
dup2(dn, 1);
dup2(dn, 2);
close(dn);
} |
1aa405c3 |
} |
8139fd99 |
execv("/bin/tar", args); |
efa239e2 |
mprintf("!build: Can't execute tar\n"); |
8139fd99 |
perror("tar"); |
3d167464 |
free(tarfile);
return -1; |
8139fd99 |
}
default:
wait(NULL);
}
if(stat(tarfile, &foo) == -1) { |
efa239e2 |
mprintf("!build: Tar archive was not created\n"); |
3d167464 |
free(tarfile);
return -1; |
8139fd99 |
}
if((tar = fopen(tarfile, "rb")) == NULL) { |
efa239e2 |
mprintf("!build: Can't open file %s\n", tarfile); |
3d167464 |
free(tarfile);
return -1;
}
if(!(gzfile = cli_gentemp("."))) { |
efa239e2 |
mprintf("!build: Can't generate temporary name for gzfile\n"); |
3d167464 |
free(tarfile);
fclose(tar);
return -1; |
8139fd99 |
}
if((gz = gzopen(gzfile, "wb")) == NULL) { |
efa239e2 |
mprintf("!build: Can't open file %s to write.\n", gzfile); |
3d167464 |
free(tarfile);
fclose(tar);
free(gzfile);
return -1; |
8139fd99 |
}
|
3d167464 |
while((bytes = fread(buffer, 1, FILEBUFF, tar)) > 0) {
if(!gzwrite(gz, buffer, bytes)) { |
efa239e2 |
mprintf("!build: Can't gzwrite to %s\n", gzfile); |
3d167464 |
fclose(tar);
gzclose(gz);
free(tarfile);
free(gzfile);
return -1;
}
} |
8139fd99 |
fclose(tar); |
3d167464 |
gzclose(gz); |
8139fd99 |
unlink(tarfile);
free(tarfile);
/* MD5 */ |
3d167464 |
if(!(pt = cli_md5file(gzfile))) { |
efa239e2 |
mprintf("!build: Can't generate MD5 checksum for gzfile\n"); |
3d167464 |
unlink(gzfile);
free(gzfile);
return -1;
} |
658f19f8 |
strcat(header, pt); |
ae307914 |
free(pt); |
658f19f8 |
strcat(header, ":"); |
8139fd99 |
/* digital signature */ |
3d167464 |
if(!(tar = fopen(gzfile, "rb"))) { |
efa239e2 |
mprintf("!build: Can't open file %s for reading\n", gzfile); |
3d167464 |
unlink(gzfile);
free(gzfile);
return -1;
}
if(!(pt = cli_md5stream(tar, (unsigned char *) buffer))) { |
efa239e2 |
mprintf("!build: Can't generate MD5 checksum for %s\n", gzfile); |
3d167464 |
unlink(gzfile);
free(gzfile);
return -1;
} |
335d1663 |
free(pt); |
3d167464 |
rewind(tar);
|
5b68299b |
if(!(pt = getdsig(opt_arg(opt, "server"), builder, buffer, 16, 0))) { |
efa239e2 |
mprintf("!build: Can't get digital signature from remote server\n"); |
0f387b1b |
unlink(gzfile); |
3d167464 |
free(gzfile);
fclose(tar);
return -1; |
ae307914 |
} |
658f19f8 |
strcat(header, pt); |
ae307914 |
free(pt); |
658f19f8 |
strcat(header, ":"); |
8139fd99 |
|
3365db23 |
/* add builder */ |
e3bcc33a |
strcat(header, builder); |
8139fd99 |
|
3365db23 |
/* add current time */
sprintf(header + strlen(header), ":%d", (int) timet);
|
8139fd99 |
/* fill up with spaces */ |
3d167464 |
while(strlen(header) < sizeof(header) - 1) |
658f19f8 |
strcat(header, " "); |
8139fd99 |
/* build the final database */ |
7b8edc5c |
pt = opt_arg(opt, "build"); |
3d167464 |
if(!(cvd = fopen(pt, "wb"))) { |
efa239e2 |
mprintf("!build: Can't create final database %s\n", pt); |
0f387b1b |
unlink(gzfile); |
3d167464 |
free(gzfile);
fclose(tar);
return -1; |
8139fd99 |
}
|
3d167464 |
if(fwrite(header, 1, 512, cvd) != 512) { |
efa239e2 |
mprintf("!build: Can't write to %s\n", pt); |
3d167464 |
fclose(cvd);
fclose(tar);
unlink(pt);
unlink(gzfile);
free(gzfile);
return -1; |
8139fd99 |
}
|
3d167464 |
while((bytes = fread(buffer, 1, FILEBUFF, tar)) > 0) {
if(fwrite(buffer, 1, bytes, cvd) != bytes) {
fclose(tar);
fclose(cvd);
unlink(pt); |
efa239e2 |
mprintf("!build: Can't write to %s\n", gzfile); |
3d167464 |
unlink(gzfile);
free(gzfile);
return -1;
}
} |
8139fd99 |
fclose(tar);
fclose(cvd); |
3d167464 |
if(unlink(gzfile) == -1) { |
efa239e2 |
mprintf("^build: Can't unlink %s\n", gzfile); |
3d167464 |
return -1;
} |
8139fd99 |
free(gzfile);
|
5b68299b |
mprintf("Created %s\n", pt); |
3d167464 |
|
1aa405c3 |
/* generate patch */ |
c4cbb847 |
if(opt->filename) {
strncpy(olddb, opt->filename, sizeof(olddb)); |
1aa405c3 |
} else { |
c4cbb847 |
if(inc) {
pt = freshdbdir();
snprintf(olddb, sizeof(olddb), "%s/%s.inc", pt, dbname);
free(pt);
} else {
pt = freshdbdir();
snprintf(olddb, sizeof(olddb), "%s/%s.cvd", pt, dbname);
free(pt);
}
} |
1aa405c3 |
|
c4cbb847 |
if(!inc) { |
1aa405c3 |
pt = cli_gentemp(NULL);
if(mkdir(pt, 0700)) {
mprintf("!build: Can't create temporary directory %s\n", pt);
return -1;
}
if(cvd_unpack(olddb, pt) == -1) {
mprintf("!build: Can't unpack CVD file %s\n", olddb); |
66ba1785 |
cli_rmdirs(pt); |
1aa405c3 |
free(pt);
return -1;
}
strncpy(olddb, pt, sizeof(olddb));
}
pt = cli_gentemp(NULL);
if(mkdir(pt, 0700)) {
mprintf("!build: Can't create temporary directory %s\n", pt);
free(pt);
if(!inc) |
66ba1785 |
cli_rmdirs(olddb); |
1aa405c3 |
return -1;
}
if(cvd_unpack(opt_arg(opt, "build"), pt) == -1) {
mprintf("!build: Can't unpack CVD file %s\n", opt_arg(opt, "build")); |
66ba1785 |
cli_rmdirs(pt); |
1aa405c3 |
free(pt);
if(!inc) |
66ba1785 |
cli_rmdirs(olddb); |
1aa405c3 |
return -1;
}
if(!strcmp(dbname, "main")) |
5b68299b |
snprintf(patch, sizeof(patch), "main-%u.script", version); |
1aa405c3 |
else |
5b68299b |
snprintf(patch, sizeof(patch), "daily-%u.script", version); |
1aa405c3 |
ret = diffdirs(olddb, pt, patch);
|
66ba1785 |
cli_rmdirs(pt); |
1aa405c3 |
free(pt);
if(ret == -1) {
if(!inc) |
66ba1785 |
cli_rmdirs(olddb); |
1aa405c3 |
return -1;
}
|
5b68299b |
ret = verifydiff(patch, NULL, olddb); |
1aa405c3 |
if(!inc) |
66ba1785 |
cli_rmdirs(olddb); |
1aa405c3 |
if(ret == -1) {
snprintf(broken, sizeof(broken), "%s.broken", patch);
if(rename(patch, broken)) {
unlink(patch);
mprintf("!Generated file is incorrect, removed");
} else {
mprintf("!Generated file is incorrect, renamed to %s\n", broken);
} |
5b68299b |
} else {
ret = script2cdiff(patch, builder, opt); |
1aa405c3 |
}
return ret; |
3d167464 |
}
static int unpack(struct optstruct *opt)
{
char *name, *dbdir; |
1aa405c3 |
struct stat sb; |
3d167464 |
if(opt_check(opt, "unpack-current")) {
dbdir = freshdbdir(); |
8ca8a18e |
name = malloc(strlen(dbdir) + strlen(opt_arg(opt, "unpack-current")) + 32); |
1aa405c3 |
sprintf(name, "%s/%s.inc", dbdir, opt_arg(opt, "unpack-current"));
if(stat(name, &sb) != -1) {
if(dircopy(name, ".") == -1) {
mprintf("!unpack: Can't copy incremental directory %s to local directory\n", name);
free(name);
free(dbdir);
return -1;
}
return 0;
} else {
sprintf(name, "%s/%s.cvd", dbdir, opt_arg(opt, "unpack-current"));
} |
3d167464 |
free(dbdir); |
1aa405c3 |
|
3d167464 |
} else
name = strdup(opt_arg(opt, "unpack")); |
8139fd99 |
|
3d167464 |
if(cvd_unpack(name, ".") == -1) { |
efa239e2 |
mprintf("!unpack: Can't unpack CVD file %s\n", name); |
3d167464 |
free(name);
return -1;
}
free(name); |
2d70a403 |
return 0; |
8139fd99 |
}
|
3d167464 |
static int cvdinfo(struct optstruct *opt) |
8139fd99 |
{
struct cl_cvd *cvd;
char *pt; |
6a2532ca |
int ret; |
8139fd99 |
|
3d167464 |
|
7b8edc5c |
pt = opt_arg(opt, "info"); |
6a2532ca |
if((cvd = cl_cvdhead(pt)) == NULL) { |
efa239e2 |
mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt); |
3d167464 |
return -1; |
8139fd99 |
} |
d22970ec |
mprintf("File: %s\n", pt); |
8139fd99 |
|
3d167464 |
pt = strchr(cvd->time, '-');
*pt = ':'; |
efa239e2 |
mprintf("Build time: %s\n", cvd->time);
mprintf("Version: %d\n", cvd->version);
mprintf("Signatures: %d\n", cvd->sigs);
mprintf("Functionality level: %d\n", cvd->fl);
mprintf("Builder: %s\n", cvd->builder);
mprintf("MD5: %s\n", cvd->md5);
mprintf("Digital signature: %s\n", cvd->dsig); |
8139fd99 |
|
1cdb6cfb |
#ifndef HAVE_LIBGMP |
efa239e2 |
mprintf("^Digital signature support not compiled in.\n"); |
6a2532ca |
#endif
|
3d167464 |
pt = opt_arg(opt, "info"); |
6a2532ca |
if((ret = cl_cvdverify(pt))) |
efa239e2 |
mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret)); |
6a2532ca |
else |
efa239e2 |
mprintf("Verification OK.\n"); |
6a2532ca |
|
3d167464 |
cl_cvdfree(cvd);
return 0; |
8139fd99 |
}
|
3d167464 |
static int listdb(const char *filename); |
ae307914 |
|
3d167464 |
static int listdir(const char *dirname)
{
DIR *dd;
struct dirent *dent;
char *dbfile; |
ae307914 |
|
3d167464 |
if((dd = opendir(dirname)) == NULL) { |
efa239e2 |
mprintf("!listdir: Can't open directory %s\n", dirname); |
3d167464 |
return -1; |
ae307914 |
}
|
3d167464 |
while((dent = readdir(dd))) {
#ifndef C_INTERIX
if(dent->d_ino)
#endif
{
if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
(cli_strbcasestr(dent->d_name, ".db") ||
cli_strbcasestr(dent->d_name, ".hdb") || |
70edb085 |
cli_strbcasestr(dent->d_name, ".hdu") || |
c09d6c19 |
cli_strbcasestr(dent->d_name, ".mdb") || |
70edb085 |
cli_strbcasestr(dent->d_name, ".mdu") || |
3d167464 |
cli_strbcasestr(dent->d_name, ".ndb") || |
70edb085 |
cli_strbcasestr(dent->d_name, ".ndu") || |
0a4afbbd |
cli_strbcasestr(dent->d_name, ".sdb") || |
3d167464 |
cli_strbcasestr(dent->d_name, ".zmd") ||
cli_strbcasestr(dent->d_name, ".rmd") || |
89f4fd0f |
cli_strbcasestr(dent->d_name, ".inc") || |
3d167464 |
cli_strbcasestr(dent->d_name, ".cvd"))) { |
ae307914 |
|
8ca8a18e |
dbfile = (char *) malloc(strlen(dent->d_name) + strlen(dirname) + 2); |
3d167464 |
if(!dbfile) { |
efa239e2 |
mprintf("!listdir: Can't allocate memory for dbfile\n"); |
3d167464 |
closedir(dd);
return -1;
}
sprintf(dbfile, "%s/%s", dirname, dent->d_name); |
ae307914 |
|
3d167464 |
if(listdb(dbfile) == -1) { |
efa239e2 |
mprintf("!listdb: Error listing database %s\n", dbfile); |
3d167464 |
free(dbfile);
closedir(dd);
return -1;
}
free(dbfile);
} |
2d70a403 |
}
} |
ae307914 |
|
3d167464 |
closedir(dd);
return 0; |
0f387b1b |
} |
0a2ad257 |
|
3d167464 |
static int listdb(const char *filename) |
0a2ad257 |
{ |
3d167464 |
FILE *fd;
char *buffer, *pt, *start, *dir;
int line = 0; |
0a2ad257 |
const char *tmpdir;
|
89f4fd0f |
if(cli_strbcasestr(filename, ".inc")) { /* incremental directory */
if(listdir(filename) == -1) {
mprintf("!listdb: Can't list incremental directory %s\n", filename);
return -1;
}
return 0;
}
|
0a2ad257 |
if((fd = fopen(filename, "rb")) == NULL) { |
efa239e2 |
mprintf("!listdb: Can't open file %s\n", filename); |
0a2ad257 |
return -1;
}
|
8ca8a18e |
if(!(buffer = (char *) malloc(FILEBUFF))) { |
efa239e2 |
mprintf("!listdb: Can't allocate memory for buffer\n"); |
bcf3dc79 |
fclose(fd); |
0a2ad257 |
return -1;
}
/* check for CVD file */
fgets(buffer, 12, fd);
rewind(fd);
if(!strncmp(buffer, "ClamAV-VDB:", 11)) { |
3d167464 |
free(buffer);
fclose(fd); |
0a2ad257 |
tmpdir = getenv("TMPDIR");
if(tmpdir == NULL)
#ifdef P_tmpdir
tmpdir = P_tmpdir;
#else
tmpdir = "/tmp";
#endif
|
3d167464 |
if(!(dir = cli_gentemp(tmpdir))) { |
efa239e2 |
mprintf("!listdb: Can't generate temporary name\n"); |
0a2ad257 |
return -1;
}
|
3d167464 |
if(mkdir(dir, 0700)) { |
efa239e2 |
mprintf("!listdb: Can't create temporary directory %s\n", dir); |
0a2ad257 |
free(dir);
return -1;
}
|
3d167464 |
if(cvd_unpack(filename, dir) == -1) { |
efa239e2 |
mprintf("!listdb: Can't unpack CVD file %s\n", filename); |
66ba1785 |
cli_rmdirs(dir); |
0a2ad257 |
free(dir);
return -1;
}
/* list extracted directory */ |
3d167464 |
if(listdir(dir) == -1) { |
89f4fd0f |
mprintf("!listdb: Can't list directory %s\n", filename); |
66ba1785 |
cli_rmdirs(dir); |
3d167464 |
free(dir);
return -1;
} |
0a2ad257 |
|
66ba1785 |
cli_rmdirs(dir); |
0a2ad257 |
free(dir);
return 0;
}
|
3d167464 |
if(cli_strbcasestr(filename, ".db")) { /* old style database */ |
3365db23 |
while(fgets(buffer, FILEBUFF, fd)) {
line++;
pt = strchr(buffer, '=');
if(!pt) { |
efa239e2 |
mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename); |
3365db23 |
fclose(fd);
free(buffer);
return -1;
} |
0a2ad257 |
|
3365db23 |
start = buffer;
*pt = 0; |
0a2ad257 |
|
3365db23 |
if((pt = strstr(start, " (Clam)")))
*pt = 0;
|
efa239e2 |
mprintf("%s\n", start); |
0a2ad257 |
}
|
70edb085 |
} else if(cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".hdu") || cli_strbcasestr(filename, ".mdb") || cli_strbcasestr(filename, ".mdu")) { /* hash database */ |
0a2ad257 |
|
3365db23 |
while(fgets(buffer, FILEBUFF, fd)) {
line++;
cli_chomp(buffer);
start = cli_strtok(buffer, 2, ":");
if(!start) { |
efa239e2 |
mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename); |
3365db23 |
fclose(fd);
free(buffer);
return -1;
}
if((pt = strstr(start, " (Clam)")))
*pt = 0;
|
efa239e2 |
mprintf("%s\n", start); |
3365db23 |
free(start);
} |
0a2ad257 |
|
70edb085 |
} else if(cli_strbcasestr(filename, ".ndb") || cli_strbcasestr(filename, ".ndu") || cli_strbcasestr(filename, ".sdb") || cli_strbcasestr(filename, ".zmd") || cli_strbcasestr(filename, ".rmd")) { |
7ec67e94 |
while(fgets(buffer, FILEBUFF, fd)) {
line++;
cli_chomp(buffer);
start = cli_strtok(buffer, 0, ":");
if(!start) { |
efa239e2 |
mprintf("!listdb: Malformed pattern line %d (file %s)\n", line, filename); |
7ec67e94 |
fclose(fd);
free(buffer);
return -1;
}
if((pt = strstr(start, " (Clam)")))
*pt = 0;
|
efa239e2 |
mprintf("%s\n", start); |
7ec67e94 |
free(start);
} |
0a2ad257 |
}
fclose(fd);
free(buffer);
return 0;
}
|
3d167464 |
static int listsigs(struct optstruct *opt) |
0a2ad257 |
{
int ret;
const char *name; |
98ce643b |
char *dbdir; |
0a2ad257 |
|
3d167464 |
|
0a2ad257 |
mprintf_stdout = 1;
|
7b8edc5c |
if((name = opt_arg(opt, "list-sigs"))) { |
0a2ad257 |
ret = listdb(name); |
98ce643b |
} else {
dbdir = freshdbdir();
ret = listdir(dbdir);
free(dbdir);
} |
0a2ad257 |
|
3d167464 |
return ret;
}
static int vbadump(struct optstruct *opt)
{
int fd, hex_output=0;
char *dir;
if(opt_check(opt, "vba-hex"))
hex_output = 1;
if((fd = open(opt_arg(opt, "vba"), O_RDONLY)) == -1) {
if((fd = open(opt_arg(opt, "vba-hex"), O_RDONLY)) == -1) { |
efa239e2 |
mprintf("!vbadump: Can't open file %s\n", opt_arg(opt, "vba")); |
3d167464 |
return -1;
}
}
/* generate the temporary directory */
dir = cli_gentemp(NULL);
if(mkdir(dir, 0700)) { |
efa239e2 |
mprintf("!vbadump: Can't create temporary directory %s\n", dir); |
3d167464 |
free(dir);
close(fd);
return -1;
}
if(cli_ole2_extract(fd, dir, NULL)) {
cli_rmdirs(dir);
free(dir);
close(fd);
return -1;
}
close(fd);
sigtool_vba_scandir(dir, hex_output); |
66ba1785 |
cli_rmdirs(dir); |
3d167464 |
free(dir);
return 0; |
ee039e40 |
}
|
5b68299b |
static int rundiff(struct optstruct *opt) |
bb990ecd |
{
int fd, ret; |
5b68299b |
unsigned short mode;
const char *diff;
|
bb990ecd |
|
5b68299b |
diff = opt_arg(opt, "run-cdiff");
if(strstr(diff, ".cdiff")) {
mode = 1;
} else if(strstr(diff, ".script")) {
mode = 0;
} else {
mprintf("!rundiff: Incorrect file name (no .cdiff/.script extension)\n");
return -1;
} |
bb990ecd |
|
5b68299b |
if((fd = open(diff, O_RDONLY)) == -1) {
mprintf("!rundiff: Can't open file %s\n", diff); |
bb990ecd |
return -1; |
6edef583 |
} |
bb990ecd |
|
5b68299b |
ret = cdiff_apply(fd, mode); |
bb990ecd |
close(fd);
return ret;
}
|
37eca903 |
static int compare(const char *oldpath, const char *newpath, FILE *diff)
{
FILE *old, *new; |
fb30f402 |
char obuff[1024], nbuff[1024], tbuff[1024], *pt, *omd5, *nmd5;
unsigned int oline = 0, tline, found, i;
long opos; |
37eca903 |
if(!(new = fopen(newpath, "r"))) {
mprintf("!compare: Can't open file %s for reading\n", newpath);
return -1;
}
if((omd5 = cli_md5file(oldpath))) {
if(!(nmd5 = cli_md5file(newpath))) {
mprintf("!compare: Can't get MD5 checksum of %s\n", newpath);
free(omd5);
return -1;
}
if(!strcmp(omd5, nmd5)) {
free(omd5);
free(nmd5);
return 0;
}
free(omd5);
free(nmd5);
}
fprintf(diff, "OPEN %s\n", newpath);
old = fopen(oldpath, "r");
while(fgets(nbuff, sizeof(nbuff), new)) {
cli_chomp(nbuff);
if(!old) {
fprintf(diff, "ADD %s\n", nbuff);
} else {
if(fgets(obuff, sizeof(obuff), old)) { |
fb30f402 |
oline++; |
37eca903 |
cli_chomp(obuff);
if(!strcmp(nbuff, obuff)) {
continue;
} else { |
fb30f402 |
tline = 0;
found = 0;
opos = ftell(old);
while(fgets(tbuff, sizeof(tbuff), old)) {
tline++;
cli_chomp(tbuff);
if(tline > MAX_DEL_LOOKAHEAD)
break;
if(!strcmp(tbuff, nbuff)) {
found = 1;
break;
}
}
fseek(old, opos, SEEK_SET);
if(found) {
strncpy(tbuff, obuff, sizeof(tbuff));
for(i = 0; i < tline; i++) {
tbuff[16] = 0;
if((pt = strchr(tbuff, ' ')))
*pt = 0;
fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
fgets(tbuff, sizeof(tbuff), old);
}
oline += tline;
} else {
obuff[16] = 0; |
37eca903 |
if((pt = strchr(obuff, ' ')))
*pt = 0; |
fb30f402 |
fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff); |
37eca903 |
}
}
} else {
fclose(old);
old = NULL;
fprintf(diff, "ADD %s\n", nbuff);
}
}
}
|
fb30f402 |
if(old) {
while(fgets(obuff, sizeof(obuff), old)) {
oline++;
obuff[16] = 0;
if((pt = strchr(obuff, ' ')))
*pt = 0;
fprintf(diff, "DEL %u %s\n", oline, obuff);
} |
37eca903 |
fclose(old); |
fb30f402 |
}
|
37eca903 |
fprintf(diff, "CLOSE\n");
return 0;
}
|
5b68299b |
static int verifydiff(const char *diff, const char *cvd, const char *incdir) |
6edef583 |
{ |
1aa405c3 |
char *tempdir, cwd[512], buff[1024], info[32], *md5, *pt;
const char *cpt; |
6edef583 |
FILE *fh;
int ret = 0, fd; |
5b68299b |
unsigned short mode;
|
6edef583 |
|
5b68299b |
if(strstr(diff, ".cdiff")) {
mode = 1;
} else if(strstr(diff, ".script")) {
mode = 0;
} else {
mprintf("!verifydiff: Incorrect file name (no .cdiff/.script extension)\n");
return -1;
} |
6edef583 |
tempdir = cli_gentemp(NULL);
if(!tempdir) { |
5b68299b |
mprintf("!verifydiff: Can't generate temporary name for tempdir\n"); |
6edef583 |
return -1;
}
if(mkdir(tempdir, 0700) == -1) { |
5b68299b |
mprintf("!verifydiff: Can't create directory %s\n", tempdir); |
6edef583 |
free(tempdir);
return -1;
}
|
1aa405c3 |
if(cvd) {
if(cvd_unpack(cvd, tempdir) == -1) { |
5b68299b |
mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd); |
66ba1785 |
cli_rmdirs(tempdir); |
1aa405c3 |
free(tempdir);
return -1;
}
} else {
if(dircopy(incdir, tempdir) == -1) { |
5b68299b |
mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir); |
66ba1785 |
cli_rmdirs(tempdir); |
1aa405c3 |
free(tempdir);
return -1;
} |
6edef583 |
}
if((fd = open(diff, O_RDONLY)) == -1) { |
5b68299b |
mprintf("!verifydiff: Can't open diff file %s\n", diff); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
return -1;
}
getcwd(cwd, sizeof(cwd));
if(chdir(tempdir) == -1) { |
5b68299b |
mprintf("!verifydiff: Can't chdir to %s\n", tempdir); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
close(fd);
return -1;
}
|
5b68299b |
if(cdiff_apply(fd, mode) == -1) {
mprintf("!verifydiff: Can't apply %s\n", diff); |
6edef583 |
chdir(cwd); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
close(fd);
return -1;
}
close(fd);
|
1aa405c3 |
cvd ? (cpt = cvd) : (cpt = incdir);
|
a47ed53d |
if(strstr(cpt, "main")) |
6edef583 |
strcpy(info, "main.info");
else
strcpy(info, "daily.info");
if(!(fh = fopen(info, "r"))) { |
5b68299b |
mprintf("!verifydiff: Can't open %s\n", info); |
6edef583 |
chdir(cwd); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
return -1;
}
fgets(buff, sizeof(buff), fh);
if(strncmp(buff, "ClamAV-VDB", 10)) { |
5b68299b |
mprintf("!verifydiff: Incorrect info file %s\n", info); |
6edef583 |
chdir(cwd); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
return -1;
}
while(fgets(buff, sizeof(buff), fh)) {
cli_chomp(buff);
if(!(pt = strchr(buff, ':'))) { |
5b68299b |
mprintf("!verifydiff: Incorrect format of %s\n", info); |
6edef583 |
ret = -1;
break;
}
*pt++ = 0;
if(!(md5 = cli_md5file(buff))) { |
5b68299b |
mprintf("!verifydiff: Can't generate MD5 for %s\n", buff); |
6edef583 |
ret = -1;
break;
}
if(strcmp(pt, md5)) { |
5b68299b |
mprintf("!verifydiff: %s has incorrect checksum\n", buff); |
6edef583 |
ret = -1;
break;
}
}
fclose(fh);
chdir(cwd); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
|
1aa405c3 |
if(!ret) {
if(cvd)
mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
else |
b336436b |
mprintf("Verification: %s correctly applies to the previous version\n", diff); |
1aa405c3 |
} |
6edef583 |
return ret;
}
|
1aa405c3 |
static int diffdirs(const char *old, const char *new, const char *patch) |
37eca903 |
{
FILE *diff;
DIR *dd;
struct dirent *dent; |
1aa405c3 |
char cwd[512], opath[1024];
getcwd(cwd, sizeof(cwd));
if(!(diff = fopen(patch, "w"))) {
mprintf("!diffdirs: Can't open %s for writing\n", patch);
return -1;
}
if(chdir(new) == -1) {
mprintf("!diffdirs: Can't chdir to %s\n", new);
fclose(diff);
return -1;
}
if((dd = opendir(new)) == NULL) {
mprintf("!diffdirs: Can't open directory %s\n", new);
fclose(diff);
return -1;
}
while((dent = readdir(dd))) {
#ifndef C_INTERIX
if(dent->d_ino)
#endif
{
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
snprintf(opath, sizeof(opath), "%s/%s", old, dent->d_name);
if(compare(opath, dent->d_name, diff) == -1) {
fclose(diff);
unlink(patch);
closedir(dd);
return -1;
}
}
}
closedir(dd);
fclose(diff); |
5b68299b |
mprintf("Generated diff file %s\n", patch); |
1aa405c3 |
chdir(cwd);
return 0;
}
static int makediff(struct optstruct *opt)
{
char *odir, *ndir, name[32], broken[32]; |
37eca903 |
struct cl_cvd *cvd;
unsigned int oldver, newver; |
1aa405c3 |
int ret; |
37eca903 |
if(!opt->filename) {
mprintf("!makediff: --diff requires two arguments\n");
return -1;
}
if(!(cvd = cl_cvdhead(opt->filename))) {
mprintf("!makediff: Can't read CVD header from %s\n", opt->filename);
return -1;
}
newver = cvd->version;
free(cvd);
if(!(cvd = cl_cvdhead(opt_arg(opt, "diff")))) {
mprintf("!makediff: Can't read CVD header from %s\n", opt_arg(opt, "diff"));
return -1;
}
oldver = cvd->version;
free(cvd);
if(oldver + 1 != newver) {
mprintf("!makediff: The old CVD must be %u\n", newver - 1);
return -1;
}
odir = cli_gentemp(NULL);
if(!odir) {
mprintf("!makediff: Can't generate temporary name for odir\n");
return -1;
}
if(mkdir(odir, 0700) == -1) {
mprintf("!makediff: Can't create directory %s\n", odir);
free(odir);
return -1;
}
if(cvd_unpack(opt_arg(opt, "diff"), odir) == -1) {
mprintf("!makediff: Can't unpack CVD file %s\n", opt_arg(opt, "diff")); |
66ba1785 |
cli_rmdirs(odir); |
37eca903 |
free(odir);
return -1;
}
ndir = cli_gentemp(NULL);
if(!ndir) {
mprintf("!makediff: Can't generate temporary name for ndir\n"); |
66ba1785 |
cli_rmdirs(odir); |
37eca903 |
free(odir);
return -1;
}
if(mkdir(ndir, 0700) == -1) {
mprintf("!makediff: Can't create directory %s\n", ndir);
free(ndir); |
66ba1785 |
cli_rmdirs(odir); |
37eca903 |
free(odir);
return -1;
}
if(cvd_unpack(opt->filename, ndir) == -1) {
mprintf("!makediff: Can't unpack CVD file %s\n", opt->filename); |
66ba1785 |
cli_rmdirs(odir);
cli_rmdirs(ndir); |
37eca903 |
free(odir);
free(ndir);
return -1;
}
if(strstr(opt->filename, "main")) |
5b68299b |
snprintf(name, sizeof(name), "main-%u.script", newver); |
37eca903 |
else |
5b68299b |
snprintf(name, sizeof(name), "daily-%u.script", newver); |
37eca903 |
|
1aa405c3 |
ret = diffdirs(odir, ndir, name); |
37eca903 |
|
66ba1785 |
cli_rmdirs(odir);
cli_rmdirs(ndir); |
37eca903 |
free(odir);
free(ndir); |
6edef583 |
|
1aa405c3 |
if(ret == -1)
return -1; |
6edef583 |
|
5b68299b |
if(verifydiff(name, opt_arg(opt, "diff"), NULL) == -1) { |
6edef583 |
snprintf(broken, sizeof(broken), "%s.broken", name);
if(rename(name, broken)) {
unlink(name);
mprintf("!Generated file is incorrect, removed");
} else {
mprintf("!Generated file is incorrect, renamed to %s\n", broken);
}
return -1;
}
|
37eca903 |
return 0;
}
|
797c5b1e |
static void help(void) |
ee039e40 |
{
mprintf("\n"); |
4d89cb07 |
mprintf(" Clam AntiVirus: Signature Tool (sigtool) "VERSION"\n"); |
61409916 |
mprintf(" (C) 2002 - 2007 ClamAV Team - http://www.clamav.net/team\n\n"); |
ee039e40 |
mprintf(" --help -h show help\n");
mprintf(" --version -V print version number and exit\n");
mprintf(" --quiet be quiet, output only error messages\n");
mprintf(" --debug enable debug messages\n");
mprintf(" --stdout write to stdout instead of stderr\n");
mprintf(" --hex-dump convert data from stdin to a hex\n");
mprintf(" string and print it on stdout\n"); |
bcf3dc79 |
mprintf(" --md5 [FILES] generate MD5 checksum from stdin\n");
mprintf(" or MD5 sigs for FILES\n"); |
60892bc1 |
mprintf(" --mdb [FILES] generate .mdb sigs\n"); |
08d6b1e3 |
mprintf(" --html-normalise=FILE create normalised parts of HTML file\n"); |
ec5e029e |
mprintf(" --utf16-decode=FILE decode UTF16 encoded files\n"); |
ee039e40 |
mprintf(" --info=FILE -i FILE print database information\n"); |
c4cbb847 |
mprintf(" --build=NAME [cvd/inc] -b NAME build a CVD file\n"); |
ee039e40 |
mprintf(" --server=ADDR ClamAV Signing Service address\n");
mprintf(" --unpack=FILE -u FILE Unpack a CVD file\n"); |
1aa405c3 |
mprintf(" --unpack-current=SHORTNAME Unpack local CVD/INCDIR in cwd\n"); |
ee039e40 |
mprintf(" --list-sigs[=FILE] -l[FILE] List signature names\n"); |
9d0b7ebd |
mprintf(" --vba=FILE Extract VBA/Word6 macro code\n");
mprintf(" --vba-hex=FILE Extract Word6 macro code with hex values\n"); |
37eca903 |
mprintf(" --diff=OLD NEW -d OLD NEW Create diff for OLD and NEW CVDs\n"); |
bb990ecd |
mprintf(" --run-cdiff=FILE -r FILE Execute update script FILE in cwd\n"); |
1aa405c3 |
mprintf(" --verify-cdiff=DIFF CVD/INCDIR Verify DIFF against CVD\n"); |
ee039e40 |
mprintf("\n");
|
3d167464 |
return;
}
int main(int argc, char **argv)
{
int ret = 1;
struct optstruct *opt; |
f25fa788 |
struct stat sb; |
37eca903 |
const char *short_options = "hvVb:i:u:l::r:d:"; |
3d167464 |
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"quiet", 0, 0, 0},
{"debug", 0, 0, 0},
{"verbose", 0, 0, 'v'},
{"stdout", 0, 0, 0},
{"version", 0, 0, 'V'},
{"tempdir", 1, 0, 0},
{"hex-dump", 0, 0, 0},
{"md5", 0, 0, 0}, |
60892bc1 |
{"mdb", 0, 0, 0}, |
3d167464 |
{"html-normalise", 1, 0, 0}, |
ec5e029e |
{"utf16-decode", 1, 0, 0}, |
3d167464 |
{"build", 1, 0, 'b'},
{"server", 1, 0, 0},
{"unpack", 1, 0, 'u'},
{"unpack-current", 1, 0, 0},
{"info", 1, 0, 'i'},
{"list-sigs", 2, 0, 'l'},
{"vba", 1, 0 ,0},
{"vba-hex", 1, 0, 0}, |
37eca903 |
{"diff", 1, 0, 'd'}, |
bb990ecd |
{"run-cdiff", 1, 0, 'r'}, |
6edef583 |
{"verify-cdiff", 1, 0, 0}, |
3d167464 |
{0, 0, 0, 0}
};
opt = opt_parse(argc, argv, short_options, long_options, NULL);
if(!opt) {
mprintf("!Can't parse the command line\n");
return 1;
}
if(opt_check(opt, "quiet"))
mprintf_quiet = 1;
if(opt_check(opt, "stdout"))
mprintf_stdout = 1;
if(opt_check(opt, "debug"))
cl_debug();
if(opt_check(opt, "version")) { |
0aa3ba06 |
print_version(NULL); |
3d167464 |
opt_free(opt);
return 0;
}
if(opt_check(opt, "help")) {
opt_free(opt);
help(); |
6edef583 |
return 0; |
3d167464 |
}
if(opt_check(opt, "hex-dump"))
ret = hexdump();
else if(opt_check(opt, "md5")) |
60892bc1 |
ret = md5sig(opt, 0);
else if(opt_check(opt, "mdb"))
ret = md5sig(opt, 1); |
3d167464 |
else if(opt_check(opt, "html-normalise"))
ret = htmlnorm(opt); |
ec5e029e |
else if(opt_check(opt, "utf16-decode"))
ret = utf16decode(opt); |
3d167464 |
else if(opt_check(opt, "build"))
ret = build(opt);
else if(opt_check(opt, "unpack"))
ret = unpack(opt);
else if(opt_check(opt, "unpack-current"))
ret = unpack(opt);
else if(opt_check(opt, "info"))
ret = cvdinfo(opt);
else if(opt_check(opt, "list-sigs"))
ret = listsigs(opt);
else if(opt_check(opt, "vba") || opt_check(opt, "vba-hex"))
ret = vbadump(opt); |
37eca903 |
else if(opt_check(opt, "diff"))
ret = makediff(opt); |
bb990ecd |
else if(opt_check(opt, "run-cdiff")) |
5b68299b |
ret = rundiff(opt); |
6edef583 |
else if(opt_check(opt, "verify-cdiff")) {
if(!opt->filename) { |
1aa405c3 |
mprintf("!--verify-cdiff requires two arguments\n"); |
6edef583 |
ret = -1;
} else { |
f25fa788 |
if(stat(opt->filename, &sb) == -1) {
mprintf("--verify-cdiff: Can't get status of %s\n", opt->filename);
ret = -1;
} else {
if(S_ISDIR(sb.st_mode)) |
5b68299b |
ret = verifydiff(opt_arg(opt, "verify-cdiff"), NULL, opt->filename); |
f25fa788 |
else |
5b68299b |
ret = verifydiff(opt_arg(opt, "verify-cdiff"), opt->filename, NULL); |
f25fa788 |
} |
6edef583 |
}
} else |
3d167464 |
help();
opt_free(opt);
return ret ? 1 : 0; |
0a2ad257 |
} |