e3aaff8e |
/* |
e1cbc270 |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
* Copyright (C) 2002-2007 Tomasz Kojm <tkojm@clamav.net>
* |
bb34cb31 |
* CDIFF code (C) 2006 Sensory Networks, Inc. |
e1cbc270 |
* |
e18f93ee |
* Author: Tomasz Kojm <tkojm@clamav.net> |
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> |
8f5bb092 |
#ifdef HAVE_UNISTD_H |
e3aaff8e |
#include <unistd.h> |
8f5bb092 |
#endif |
8139fd99 |
#include <zlib.h>
#include <time.h>
#include <locale.h> |
ae307914 |
#include <sys/types.h> |
08d6b1e3 |
#include <sys/stat.h>
#include <fcntl.h> |
081f6473 |
#ifndef _WIN32 |
ae307914 |
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h> |
2d70a403 |
#include <sys/wait.h> |
c1bc49e7 |
#else
#include "w32_stat.h" |
4cd80898 |
#endif |
0a2ad257 |
#include <dirent.h> |
105950c4 |
#include <ctype.h> |
1a697e4b |
#include <libgen.h> |
e3aaff8e |
|
f0d0a4c0 |
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
|
6a31c2b4 |
#include "vba.h" |
e3aaff8e |
|
3d167464 |
#include "shared/output.h" |
d6af38e7 |
#include "shared/optparser.h" |
3d167464 |
#include "shared/misc.h" |
bb990ecd |
#include "shared/cdiff.h" |
e18f93ee |
#include "shared/tar.h" |
e3aaff8e |
|
e48dcea9 |
#include "libclamav/clamav.h" |
93173872 |
#include "libclamav/matcher.h" |
3d167464 |
#include "libclamav/cvd.h"
#include "libclamav/str.h"
#include "libclamav/ole2_extract.h"
#include "libclamav/htmlnorm.h" |
c1bc49e7 |
#include "libclamav/textnorm.h" |
4367454d |
#include "libclamav/default.h" |
084d19aa |
#include "libclamav/fmap.h" |
a96eead4 |
#include "libclamav/readdb.h" |
fc8d26a1 |
#include "libclamav/others.h" |
9bbe52b8 |
#include "libclamav/pe.h" |
e3aaff8e |
|
288057e9 |
#define MAX_DEL_LOOKAHEAD 5000 |
e3aaff8e |
|
9bbe52b8 |
//struct s_info info;
short recursion = 0, bell = 0;
short printinfected = 0, printclean = 1;
|
e18f93ee |
static const struct dblist_s { |
467ab203 |
const char *ext; |
e18f93ee |
unsigned int count;
} dblist[] = {
/* special files */ |
288057e9 |
{"info", 0},
{"cfg", 0},
{"ign", 0},
{"ign2", 0},
{"ftm", 0}, |
e18f93ee |
/* databases */ |
288057e9 |
{"db", 1},
{"hdb", 1},
{"hdu", 1},
{"hsb", 1},
{"hsu", 1},
{"mdb", 1},
{"mdu", 1},
{"msb", 1},
{"msu", 1},
{"ndb", 1},
{"ndu", 1},
{"ldb", 1},
{"ldu", 1},
{"sdb", 1},
{"zmd", 1},
{"rmd", 1},
{"idb", 0}, |
2a106f57 |
{"fp", 1}, // TODO Should count be 0 here? We don't count others like this |
288057e9 |
{"sfp", 0},
{"gdb", 1},
{"pdb", 1},
{"wdb", 0},
{"crb", 1},
{"cdb", 1},
{"imp", 1}, |
2a106f57 |
// TODO Should we add .ioc, .yar, .yara, and .pwdb so that sigtool will
// include these sigs in a build (just in case we need this functionality
// in the future?) |
288057e9 |
{NULL, 0}}; |
e18f93ee |
|
467ab203 |
static char *getdbname(const char *str, char *dst, int dstlen) |
03527bee |
{ |
288057e9 |
int len = strlen(str); |
467ab203 |
|
288057e9 |
if (cli_strbcasestr(str, ".cvd") || cli_strbcasestr(str, ".cld") || cli_strbcasestr(str, ".cud"))
len -= 4; |
467ab203 |
|
288057e9 |
if (dst) {
strncpy(dst, str, MIN(dstlen - 1, len));
dst[MIN(dstlen - 1, len)] = 0; |
467ab203 |
} else { |
288057e9 |
dst = (char *)malloc(len + 1);
if (!dst)
return NULL;
strncpy(dst, str, len - 4);
dst[MIN(dstlen - 1, len - 4)] = 0; |
03527bee |
} |
467ab203 |
return dst; |
03527bee |
}
|
3d167464 |
static int hexdump(void)
{ |
288057e9 |
char buffer[FILEBUFF], *pt;
int bytes;
while ((bytes = read(0, buffer, FILEBUFF)) > 0) {
pt = cli_str2hex(buffer, bytes);
if (write(1, pt, 2 * bytes) == -1) {
mprintf("!hexdump: Can't write to stdout\n");
free(pt);
return -1;
}
free(pt); |
e3aaff8e |
}
|
288057e9 |
if (bytes == -1)
return -1; |
e3aaff8e |
|
3d167464 |
return 0;
} |
e3aaff8e |
|
3cc632ad |
static int hashpe(const char *filename, unsigned int class, int type)
{
STATBUF sb;
const char *fmptr;
struct cl_engine *engine;
cli_ctx ctx; |
ee39ff06 |
struct cl_scan_options options; |
3cc632ad |
int fd, ret;
/* build engine */ |
288057e9 |
if (!(engine = cl_engine_new())) {
mprintf("!hashpe: Can't create new engine\n");
return -1; |
3cc632ad |
}
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
|
288057e9 |
if (cli_initroots(engine, 0) != CL_SUCCESS) {
mprintf("!hashpe: cli_initroots() failed\n");
cl_engine_free(engine);
return -1; |
3cc632ad |
}
|
288057e9 |
if (cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
mprintf("!hashpe: Can't parse signature\n");
cl_engine_free(engine);
return -1; |
3cc632ad |
}
|
288057e9 |
if (cl_engine_compile(engine) != CL_SUCCESS) {
mprintf("!hashpe: Can't compile engine\n");
cl_engine_free(engine);
return -1; |
3cc632ad |
}
/* prepare context */
memset(&ctx, '\0', sizeof(cli_ctx)); |
ee39ff06 |
memset(&options, 0, sizeof(struct cl_scan_options)); |
288057e9 |
ctx.engine = engine;
ctx.options = &options; |
048a88e6 |
ctx.options->parse = ~0; |
288057e9 |
ctx.containers = cli_calloc(sizeof(cli_ctx_container), engine->maxreclevel + 2);
if (!ctx.containers) {
cl_engine_free(engine);
return -1; |
031fe00a |
}
ctx.containers[0].type = CL_TYPE_ANY; |
288057e9 |
ctx.dconf = (struct cli_dconf *)engine->dconf;
ctx.fmap = calloc(sizeof(fmap_t *), 1);
if (!ctx.fmap) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
cl_engine_free(engine);
return -1; |
3cc632ad |
}
/* Prepare file */
fd = open(filename, O_RDONLY); |
288057e9 |
if (fd < 0) {
mprintf("!hashpe: Can't open file %s!\n", filename); |
631cb6a0 |
free(ctx.containers); |
735d623d |
free(ctx.fmap); |
3cc632ad |
cl_engine_free(engine);
return -1;
}
lseek(fd, 0, SEEK_SET);
FSTAT(fd, &sb); |
288057e9 |
if (!(*ctx.fmap = fmap(fd, 0, sb.st_size))) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
free(ctx.fmap);
close(fd);
cl_engine_free(engine);
return -1; |
3cc632ad |
}
fmptr = fmap_need_off_once(*ctx.fmap, 0, sb.st_size); |
288057e9 |
if (!fmptr) { |
3cc632ad |
mprintf("!hashpe: fmap_need_off_once failed!\n"); |
631cb6a0 |
free(ctx.containers); |
735d623d |
funmap(*ctx.fmap); |
3cc632ad |
free(ctx.fmap);
close(fd);
cl_engine_free(engine); |
288057e9 |
return -1; |
3cc632ad |
}
cl_debug();
/* Send to PE-specific hasher */ |
288057e9 |
switch (class) { |
3cc632ad |
case 1: |
dd25061b |
ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_SECTION, type, NULL); |
288057e9 |
break; |
3cc632ad |
case 2: |
dd25061b |
ret = cli_genhash_pe(&ctx, CL_GENHASH_PE_CLASS_IMPTBL, type, NULL); |
288057e9 |
break; |
3cc632ad |
default: |
288057e9 |
mprintf("!hashpe: unknown classification(%u) for pe hash!\n", class); |
735d623d |
free(ctx.containers);
funmap(*ctx.fmap);
free(ctx.fmap);
close(fd); |
288057e9 |
cl_engine_free(engine);
return -1; |
3cc632ad |
}
/* THIS MAY BE UNNECESSARY */ |
288057e9 |
switch (ret) { |
3cc632ad |
case CL_CLEAN:
break;
case CL_VIRUS:
mprintf("*hashpe: CL_VIRUS after cli_genhash_pe()!\n");
break;
case CL_BREAK:
mprintf("*hashpe: CL_BREAK after cli_genhash_pe()!\n");
break;
case CL_EFORMAT:
mprintf("!hashpe: Not a valid PE file!\n");
break;
default:
mprintf("!hashpe: Other error %d inside cli_genhash_pe.\n", ret);
break;
}
/* Cleanup */ |
631cb6a0 |
free(ctx.containers); |
735d623d |
funmap(*ctx.fmap); |
3cc632ad |
free(ctx.fmap);
close(fd);
cl_engine_free(engine);
return 0;
}
static int hashsig(const struct optstruct *opts, unsigned int class, int type) |
3d167464 |
{ |
288057e9 |
char *hash;
unsigned int i;
STATBUF sb;
if (opts->filename) {
for (i = 0; opts->filename[i]; i++) {
if (CLAMSTAT(opts->filename[i], &sb) == -1) {
perror("hashsig");
mprintf("!hashsig: Can't access file %s\n", opts->filename[i]);
return -1;
} else {
if ((sb.st_mode & S_IFMT) == S_IFREG) {
if ((class == 0) && (hash = cli_hashfile(opts->filename[i], type))) {
mprintf("%s:%u:%s\n", hash, (unsigned int)sb.st_size, basename(opts->filename[i]));
free(hash);
} else if ((class > 0) && (hashpe(opts->filename[i], class, type) == 0)) {
/* intentionally empty - printed in cli_genhash_pe() */
} else {
mprintf("!hashsig: Can't generate hash for %s\n", opts->filename[i]);
return -1;
}
}
}
} |
08d6b1e3 |
|
3d167464 |
} else { /* stream */ |
288057e9 |
if (class > 0) {
mprintf("!hashsig: Can't generate requested hash for input stream\n");
return -1;
}
hash = cli_hashstream(stdin, NULL, type);
if (!hash) {
mprintf("!hashsig: Can't generate hash for input stream\n");
return -1;
}
mprintf("%s\n", hash);
free(hash); |
3d167464 |
} |
8139fd99 |
|
3d167464 |
return 0;
} |
8139fd99 |
|
d6af38e7 |
static int htmlnorm(const struct optstruct *opts) |
3d167464 |
{ |
288057e9 |
int fd;
fmap_t *map; |
0f387b1b |
|
288057e9 |
if ((fd = open(optget(opts, "html-normalise")->strarg, O_RDONLY)) == -1) {
mprintf("!htmlnorm: Can't open file %s\n", optget(opts, "html-normalise")->strarg);
return -1; |
3d167464 |
} |
55216b6e |
|
288057e9 |
if ((map = fmap(fd, 0, 0))) {
html_normalise_map(map, ".", NULL, NULL);
funmap(map); |
084d19aa |
} else |
288057e9 |
mprintf("!fmap failed\n");
|
3d167464 |
close(fd); |
55216b6e |
|
3d167464 |
return 0;
} |
8139fd99 |
|
c1bc49e7 |
static int asciinorm(const struct optstruct *opts)
{
const char *fname;
unsigned char *norm_buff;
struct text_norm_state state;
size_t map_off; |
288057e9 |
fmap_t *map; |
c1bc49e7 |
int fd, ofd;
fname = optget(opts, "ascii-normalise")->strarg; |
288057e9 |
fd = open(fname, O_RDONLY); |
c1bc49e7 |
if (fd == -1) { |
288057e9 |
mprintf("!asciinorm: Can't open file %s\n", fname);
return -1; |
c1bc49e7 |
}
|
288057e9 |
if (!(norm_buff = malloc(ASCII_FILE_BUFF_LENGTH))) {
mprintf("!asciinorm: Can't allocate memory\n");
close(fd);
return -1; |
c1bc49e7 |
}
if (!(map = fmap(fd, 0, 0))) { |
288057e9 |
mprintf("!fmap: Could not map fd %d\n", fd);
close(fd);
free(norm_buff);
return -1; |
c1bc49e7 |
}
if (map->len > MAX_ASCII_FILE_SIZE) { |
288057e9 |
mprintf("!asciinorm: File size of %zu too large\n", map->len);
close(fd);
free(norm_buff);
funmap(map);
return -1; |
c1bc49e7 |
}
ofd = open("./normalised_text", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (ofd == -1) { |
288057e9 |
mprintf("!asciinorm: Can't open file ./normalised_text\n");
close(fd);
free(norm_buff);
funmap(map);
return -1; |
c1bc49e7 |
}
text_normalize_init(&state, norm_buff, ASCII_FILE_BUFF_LENGTH);
map_off = 0; |
288057e9 |
while (map_off != map->len) {
size_t written;
if (!(written = text_normalize_map(&state, map, map_off))) break;
map_off += written;
if (write(ofd, norm_buff, state.out_pos) == -1) {
mprintf("!asciinorm: Can't write to file ./normalised_text\n");
close(fd);
close(ofd);
free(norm_buff);
funmap(map);
return -1;
}
text_normalize_reset(&state); |
c1bc49e7 |
}
close(fd);
close(ofd);
free(norm_buff);
funmap(map);
return 0;
}
|
d6af38e7 |
static int utf16decode(const struct optstruct *opts) |
ec5e029e |
{ |
288057e9 |
const char *fname;
char *newname, buff[512], *decoded;
int fd1, fd2, bytes; |
ec5e029e |
|
d6af38e7 |
fname = optget(opts, "utf16-decode")->strarg; |
288057e9 |
if ((fd1 = open(fname, O_RDONLY)) == -1) {
mprintf("!utf16decode: Can't open file %s\n", fname);
return -1; |
ec5e029e |
}
newname = malloc(strlen(fname) + 7); |
288057e9 |
if (!newname) {
mprintf("!utf16decode: Can't allocate memory\n");
close(fd1);
return -1; |
e18f93ee |
} |
ec5e029e |
sprintf(newname, "%s.ascii", fname);
|
288057e9 |
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);
close(fd1);
close(fd2);
unlink(newname);
free(newname);
return -1;
}
free(decoded);
} |
ec5e029e |
}
free(newname);
close(fd1);
close(fd2);
return 0;
}
|
e18f93ee |
static char *getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode) |
3d167464 |
{ |
288057e9 |
char buff[512], cmd[128], pass[30], *pt;
struct sockaddr_in server;
int sockd, bread, len; |
f0d0a4c0 |
#ifdef HAVE_TERMIOS_H |
288057e9 |
struct termios old, new; |
f0d0a4c0 |
#endif |
9d0b7ebd |
|
9afa433f |
memset(&server, 0x00, sizeof(struct sockaddr_in)); |
9d0b7ebd |
|
288057e9 |
if ((pt = getenv("SIGNDPASS"))) {
strncpy(pass, pt, sizeof(pass));
pass[sizeof(pass) - 1] = '\0'; |
4f411f34 |
} else { |
288057e9 |
mprintf("Password: "); |
4f411f34 |
#ifdef HAVE_TERMIOS_H |
288057e9 |
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;
} |
4f411f34 |
#endif |
288057e9 |
if (scanf("%30s", pass) == EOF) {
mprintf("!getdsig: Can't get password\n"); |
e18f93ee |
#ifdef HAVE_TERMIOS_H |
288057e9 |
tcsetattr(0, TCSAFLUSH, &old); |
e18f93ee |
#endif |
288057e9 |
return NULL;
} |
4f411f34 |
#ifdef HAVE_TERMIOS_H |
288057e9 |
if (tcsetattr(0, TCSAFLUSH, &old)) {
mprintf("!getdsig: tcsetattr() failed\n");
memset(pass, 0, sizeof(pass));
return NULL;
} |
4f411f34 |
#endif |
288057e9 |
mprintf("\n"); |
4f411f34 |
}
|
288057e9 |
if ((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket()");
mprintf("!getdsig: Can't create socket\n");
memset(pass, 0, sizeof(pass));
return NULL; |
3d167464 |
} |
9d0b7ebd |
|
288057e9 |
server.sin_family = AF_INET; |
3d167464 |
server.sin_addr.s_addr = inet_addr(host); |
288057e9 |
server.sin_port = htons(33101); |
e3aaff8e |
|
288057e9 |
if (connect(sockd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
perror("connect()"); |
b89ebf3c |
closesocket(sockd); |
288057e9 |
mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host);
memset(pass, 0, sizeof(pass));
return NULL; |
e3aaff8e |
} |
3d167464 |
memset(cmd, 0, sizeof(cmd)); |
f0d0a4c0 |
|
288057e9 |
if (mode == 1)
snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
else if (mode == 2)
snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass); |
5b68299b |
else |
288057e9 |
snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS2:%s:%s:", user, pass); |
5b68299b |
|
3d167464 |
len = strlen(cmd); |
288057e9 |
pt = cmd + len; |
5b68299b |
memcpy(pt, data, datalen);
len += datalen; |
e4ae7726 |
|
288057e9 |
if (send(sockd, cmd, len, 0) < 0) {
mprintf("!getdsig: Can't write to socket\n");
closesocket(sockd);
memset(cmd, 0, sizeof(cmd));
memset(pass, 0, sizeof(pass));
return NULL; |
3d167464 |
} |
19e2ac07 |
|
72ce4b70 |
memset(cmd, 0, sizeof(cmd));
memset(pass, 0, sizeof(pass)); |
3d167464 |
memset(buff, 0, sizeof(buff)); |
e4ae7726 |
|
288057e9 |
if ((bread = recv(sockd, buff, sizeof(buff) - 1, 0)) > 0) {
buff[bread] = '\0';
if (!strstr(buff, "Signature:")) {
mprintf("!getdsig: Error generating digital signature\n");
mprintf("!getdsig: Answer from remote server: %s\n", buff);
closesocket(sockd);
return NULL;
} else {
mprintf("Signature received (length = %lu)\n", (unsigned long)strlen(buff) - 10);
} |
827bb6ca |
} else { |
288057e9 |
mprintf("!getdsig: Communication error with remote server\n");
closesocket(sockd);
return NULL; |
19e2ac07 |
} |
e4ae7726 |
|
081f6473 |
closesocket(sockd); |
827bb6ca |
|
3d167464 |
pt = buff;
pt += 10;
return strdup(pt); |
e4ae7726 |
}
|
b2742f88 |
static char *sha256file(const char *file, unsigned int *size) |
0a4afbbd |
{ |
288057e9 |
FILE *fh;
unsigned int i, bytes;
unsigned char digest[32], buffer[FILEBUFF];
char *sha;
void *ctx; |
b2742f88 |
|
da6e06dd |
ctx = cl_hash_init("sha256"); |
f077c617 |
if (!(ctx))
return NULL; |
b2742f88 |
|
288057e9 |
if (!(fh = fopen(file, "rb"))) {
mprintf("!sha256file: Can't open file %s\n", file);
cl_hash_destroy(ctx);
return NULL; |
b2742f88 |
} |
288057e9 |
if (size)
*size = 0;
while ((bytes = fread(buffer, 1, sizeof(buffer), fh))) {
cl_update_hash(ctx, buffer, bytes);
if (size)
*size += bytes; |
b2742f88 |
} |
da6e06dd |
cl_finish_hash(ctx, digest); |
288057e9 |
sha = (char *)malloc(65);
if (!sha) { |
80803371 |
fclose(fh); |
288057e9 |
return NULL;
}
for (i = 0; i < 32; i++)
sprintf(sha + i * 2, "%02x", digest[i]); |
80803371 |
|
288057e9 |
fclose(fh); |
b2742f88 |
return sha;
} |
0a4afbbd |
|
288057e9 |
static int writeinfo(const char *dbname, const char *builder, const char *header, const struct optstruct *opts, char *const *dblist2, unsigned int dblist2cnt) |
b2742f88 |
{ |
288057e9 |
FILE *fh;
unsigned int i, bytes;
char file[32], *pt, dbfile[32];
unsigned char digest[32], buffer[FILEBUFF];
void *ctx; |
0a4afbbd |
|
e18f93ee |
snprintf(file, sizeof(file), "%s.info", dbname); |
288057e9 |
if (!access(file, R_OK)) {
if (unlink(file) == -1) {
mprintf("!writeinfo: Can't unlink %s\n", file);
return -1;
}
}
if (!(fh = fopen(file, "wb+"))) {
mprintf("!writeinfo: Can't create file %s\n", file);
return -1;
}
if (fprintf(fh, "%s\n", header) < 0) {
mprintf("!writeinfo: Can't write to %s\n", file); |
f077c617 |
fclose(fh);
return -1;
}
|
288057e9 |
if (dblist2cnt) {
for (i = 0; i < dblist2cnt; i++) {
if (!(pt = sha256file(dblist2[i], &bytes))) {
mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
fclose(fh);
return -1;
}
if (fprintf(fh, "%s:%u:%s\n", dblist2[i], bytes, pt) < 0) {
mprintf("!writeinfo: Can't write to info file\n");
fclose(fh);
free(pt);
return -1;
}
free(pt);
}
}
if (!dblist2cnt || optget(opts, "hybrid")->enabled) {
for (i = 0; dblist[i].ext; i++) {
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
if (strcmp(dblist[i].ext, "info") && !access(dbfile, R_OK)) {
if (!(pt = sha256file(dbfile, &bytes))) {
mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
fclose(fh);
return -1;
}
if (fprintf(fh, "%s:%u:%s\n", dbfile, bytes, pt) < 0) {
mprintf("!writeinfo: Can't write to info file\n");
fclose(fh);
free(pt);
return -1;
}
free(pt);
}
}
}
if (!optget(opts, "unsigned")->enabled) {
rewind(fh);
ctx = cl_hash_init("sha256");
if (!(ctx)) {
fclose(fh);
return -1;
}
while ((bytes = fread(buffer, 1, sizeof(buffer), fh)))
cl_update_hash(ctx, buffer, bytes);
cl_finish_hash(ctx, digest);
if (!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 3))) {
mprintf("!writeinfo: Can't get digital signature from remote server\n");
fclose(fh);
return -1;
}
fprintf(fh, "DSIG:%s\n", pt);
free(pt); |
b2742f88 |
} |
0a4afbbd |
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);
|
d6af38e7 |
static int script2cdiff(const char *script, const char *builder, const struct optstruct *opts) |
5b68299b |
{ |
288057e9 |
char *cdiff, *pt, buffer[FILEBUFF];
unsigned char digest[32];
void *ctx;
STATBUF sb;
FILE *scripth, *cdiffh;
gzFile gzh;
unsigned int ver, osize;
int bytes; |
5b68299b |
|
288057e9 |
if (CLAMSTAT(script, &sb) == -1) {
mprintf("!script2diff: Can't stat file %s\n", script);
return -1; |
5b68299b |
} |
288057e9 |
osize = (unsigned int)sb.st_size; |
5b68299b |
cdiff = strdup(script); |
6b4a19c9 |
if (NULL == cdiff) { |
288057e9 |
mprintf("!script2cdiff: Unable to allocate memory for file name\n");
return -1; |
6b4a19c9 |
} |
5b68299b |
pt = strstr(cdiff, ".script"); |
288057e9 |
if (!pt) {
mprintf("!script2cdiff: Incorrect file name (no .script extension)\n");
free(cdiff);
return -1; |
5b68299b |
}
strcpy(pt, ".cdiff");
|
288057e9 |
if (!(pt = strchr(script, '-'))) {
mprintf("!script2cdiff: Incorrect file name syntax\n");
free(cdiff);
return -1; |
5b68299b |
} |
e18f93ee |
|
288057e9 |
if (sscanf(++pt, "%u.script", &ver) == EOF) {
mprintf("!script2cdiff: Incorrect file name syntax\n");
free(cdiff);
return -1; |
e18f93ee |
} |
5b68299b |
|
288057e9 |
if (!(cdiffh = fopen(cdiff, "wb"))) {
mprintf("!script2cdiff: Can't open %s for writing\n", cdiff);
free(cdiff);
return -1; |
5b68299b |
}
|
288057e9 |
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; |
5b68299b |
}
fclose(cdiffh);
|
288057e9 |
if (!(scripth = fopen(script, "rb"))) {
mprintf("!script2cdiff: Can't open file %s for reading\n", script);
unlink(cdiff);
free(cdiff);
return -1; |
5b68299b |
}
|
288057e9 |
if (!(gzh = gzopen(cdiff, "ab9f"))) {
mprintf("!script2cdiff: Can't open file %s for appending\n", cdiff);
unlink(cdiff);
free(cdiff);
fclose(scripth);
return -1; |
5b68299b |
}
|
288057e9 |
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;
} |
5b68299b |
}
fclose(scripth);
gzclose(gzh);
|
288057e9 |
if (!(cdiffh = fopen(cdiff, "rb"))) {
mprintf("!script2cdiff: Can't open %s for reading/writing\n", cdiff);
unlink(cdiff);
free(cdiff);
return -1; |
5b68299b |
}
|
da6e06dd |
ctx = cl_hash_init("sha256"); |
f077c617 |
if (!(ctx)) {
unlink(cdiff);
free(cdiff); |
5c428be3 |
fclose(cdiffh); |
f077c617 |
return -1;
} |
5b68299b |
|
288057e9 |
while ((bytes = fread(buffer, 1, sizeof(buffer), cdiffh)))
cl_update_hash(ctx, (unsigned char *)buffer, bytes); |
5b68299b |
fclose(cdiffh); |
da6e06dd |
cl_finish_hash(ctx, digest); |
5b68299b |
|
288057e9 |
if (!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 2))) {
mprintf("!script2cdiff: Can't get digital signature from remote server\n");
unlink(cdiff);
free(cdiff);
return -1; |
5b68299b |
}
|
288057e9 |
if (!(cdiffh = fopen(cdiff, "ab"))) {
mprintf("!script2cdiff: Can't open %s for appending\n", cdiff);
unlink(cdiff);
free(cdiff);
return -1; |
5b68299b |
}
fprintf(cdiffh, ":%s", pt);
free(pt);
fclose(cdiffh);
mprintf("Created %s\n", cdiff);
free(cdiff);
return 0;
} |
1aa405c3 |
|
a771899a |
static int qcompare(const void *a, const void *b)
{ |
288057e9 |
return strcmp(*(char *const *)a, *(char *const *)b); |
a771899a |
}
|
d6af38e7 |
static int build(const struct optstruct *opts) |
8139fd99 |
{ |
288057e9 |
int ret, bc = 0, hy = 0;
size_t bytes;
unsigned int i, sigs = 0, oldsigs = 0, entries = 0, version, real_header, fl, maxentries;
STATBUF foo;
unsigned char buffer[FILEBUFF];
char *tarfile, header[513], smbuff[32], builder[32], *pt, olddb[512];
char patch[32], broken[32], dbname[32], dbfile[32];
const char *newcvd, *localdbdir = NULL;
struct cl_engine *engine;
FILE *cvd, *fh;
gzFile tar;
time_t timet;
struct tm *brokent;
struct cl_cvd *oldcvd;
char **dblist2 = NULL;
unsigned int dblist2cnt = 0;
DIR *dd;
struct dirent *dent;
#define FREE_LS(x) \
for (i = 0; i < dblist2cnt; i++) \
free(x[i]); \ |
a771899a |
free(x); |
8139fd99 |
|
288057e9 |
if (!optget(opts, "server")->enabled && !optget(opts, "unsigned")->enabled) {
mprintf("!build: --server is required for --build\n");
return -1; |
8139fd99 |
}
|
288057e9 |
if (optget(opts, "datadir")->active)
localdbdir = optget(opts, "datadir")->strarg; |
8478b04a |
|
288057e9 |
if (CLAMSTAT("COPYING", &foo) == -1) {
mprintf("!build: COPYING file not found in current working directory.\n");
return -1; |
8139fd99 |
}
|
467ab203 |
getdbname(optget(opts, "build")->strarg, dbname, sizeof(dbname)); |
288057e9 |
if (!strcmp(dbname, "bytecode"))
bc = 1; |
8139fd99 |
|
288057e9 |
if (optget(opts, "hybrid")->enabled)
hy = 1; |
9c30a4fc |
|
288057e9 |
if (!(engine = cl_engine_new())) {
mprintf("!build: Can't initialize antivirus engine\n");
return 50; |
370892d0 |
}
|
288057e9 |
if ((ret = cl_load(".", engine, &sigs, CL_DB_STDOPT | CL_DB_PUA | CL_DB_SIGNED))) {
mprintf("!build: Can't load database: %s\n", cl_strerror(ret));
cl_engine_free(engine);
return -1; |
3d167464 |
} |
370892d0 |
cl_engine_free(engine); |
8139fd99 |
|
288057e9 |
if (!sigs) {
mprintf("!build: There are no signatures in database files\n"); |
0f387b1b |
} else { |
288057e9 |
if (bc || hy) {
if ((dd = opendir(".")) == NULL) {
mprintf("!build: Can't open current directory\n");
return -1;
}
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (cli_strbcasestr(dent->d_name, ".cbc")) {
dblist2 = (char **)realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
if (!dblist2) { /* dblist2 leaked but we don't really care */
mprintf("!build: Memory allocation error\n"); |
f9389d09 |
closedir(dd); |
288057e9 |
return -1;
}
dblist2[dblist2cnt] = strdup(dent->d_name);
if (!dblist2[dblist2cnt]) {
FREE_LS(dblist2);
mprintf("!build: Memory allocation error\n");
return -1;
}
dblist2cnt++;
}
}
}
closedir(dd);
entries += dblist2cnt;
if (dblist2 != NULL) {
qsort(dblist2, dblist2cnt, sizeof(char *), qcompare);
}
if (!access("last.hdb", R_OK)) {
if (!dblist2cnt) {
mprintf("!build: dblist2 == NULL (no .cbc files?)\n");
return -1;
}
dblist2 = (char **)realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
if (!dblist2) {
mprintf("!build: Memory allocation error\n");
return -1;
}
dblist2[dblist2cnt] = strdup("last.hdb");
if (!dblist2[dblist2cnt]) {
FREE_LS(dblist2);
mprintf("!build: Memory allocation error\n");
return -1;
}
dblist2cnt++;
entries += countlines("last.hdb");
}
}
if (!bc || hy) {
for (i = 0; dblist[i].ext; i++) {
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
if (dblist[i].count && !access(dbfile, R_OK))
entries += countlines(dbfile);
}
}
if (entries != sigs)
mprintf("^build: Signatures in %s db files: %u, loaded by libclamav: %u\n", dbname, entries, sigs);
maxentries = optget(opts, "max-bad-sigs")->numarg;
if (maxentries) {
if (!entries || (sigs > entries && sigs - entries >= maxentries)) {
mprintf("!Bad number of signatures in database files\n");
FREE_LS(dblist2);
return -1;
}
} |
0f387b1b |
} |
8139fd99 |
|
0a4afbbd |
/* try to read cvd header of current database */ |
288057e9 |
if (opts->filename) {
if (cli_strbcasestr(opts->filename[0], ".cvd") || cli_strbcasestr(opts->filename[0], ".cld") || cli_strbcasestr(opts->filename[0], ".cud")) {
strncpy(olddb, opts->filename[0], sizeof(olddb));
olddb[sizeof(olddb) - 1] = '\0';
} else {
mprintf("!build: Not a CVD/CLD/CUD file\n");
FREE_LS(dblist2);
return -1;
} |
c4cbb847 |
} else { |
288057e9 |
pt = freshdbdir();
snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cvd", localdbdir ? localdbdir : pt, dbname);
if (access(olddb, R_OK))
snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cld", localdbdir ? localdbdir : pt, dbname);
if (access(olddb, R_OK))
snprintf(olddb, sizeof(olddb), "%s" PATHSEP "%s.cud", localdbdir ? localdbdir : pt, dbname);
free(pt); |
c4cbb847 |
} |
1aa405c3 |
|
288057e9 |
if (!(oldcvd = cl_cvdhead(olddb)) && !optget(opts, "unsigned")->enabled) {
mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s (wait 3 s)\n", olddb);
sleep(3); |
0a4afbbd |
}
|
288057e9 |
if (oldcvd) {
version = oldcvd->version + 1;
oldsigs = oldcvd->sigs;
cl_cvdfree(oldcvd); |
3be7f9a9 |
} else if (optget(opts, "cvd-version")->numarg != 0) {
version = optget(opts, "cvd-version")->numarg; |
0a4afbbd |
} else { |
288057e9 |
mprintf("Version number: ");
if (scanf("%u", &version) == EOF) {
mprintf("!build: scanf() failed\n");
FREE_LS(dblist2);
return -1;
} |
0a4afbbd |
}
|
b336436b |
mprintf("Total sigs: %u\n", sigs); |
288057e9 |
if (sigs > oldsigs)
mprintf("New sigs: %u\n", sigs - oldsigs); |
b336436b |
|
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 */ |
e18f93ee |
sprintf(header + strlen(header), ":%u:", version); |
e3bcc33a |
/* number of signatures */ |
e18f93ee |
sprintf(header + strlen(header), "%u:", sigs); |
e3bcc33a |
/* functionality level */ |
5e0e6032 |
fl = (unsigned int)(optget(opts, "flevel")->numarg); |
e18f93ee |
sprintf(header + strlen(header), "%u:", fl); |
e3bcc33a |
real_header = strlen(header);
/* add fake MD5 and dsig (for writeinfo) */
strcat(header, "X:X:");
|
288057e9 |
if ((pt = getenv("SIGNDUSER"))) {
strncpy(builder, pt, sizeof(builder));
builder[sizeof(builder) - 1] = '\0'; |
e3bcc33a |
} else { |
288057e9 |
mprintf("Builder name: ");
if (scanf("%32s", builder) == EOF) {
mprintf("!build: Can't get builder name\n");
free(dblist2);
return -1;
} |
e3bcc33a |
}
/* add builder */
strcat(header, builder);
/* add current time */ |
288057e9 |
sprintf(header + strlen(header), ":%u", (unsigned int)timet); |
e3bcc33a |
|
288057e9 |
if (writeinfo(dbname, builder, header, opts, dblist2, dblist2cnt) == -1) {
mprintf("!build: Can't generate info file\n");
FREE_LS(dblist2);
return -1; |
0a4afbbd |
}
|
e3bcc33a |
header[real_header] = 0;
|
288057e9 |
if (!(tarfile = cli_gentemp("."))) {
mprintf("!build: Can't generate temporary name for tarfile\n");
FREE_LS(dblist2);
return -1;
}
if ((tar = gzopen(tarfile, "wb9f")) == NULL) {
mprintf("!build: Can't open file %s for writing\n", tarfile);
free(tarfile);
FREE_LS(dblist2);
return -1;
}
if (tar_addfile(-1, tar, "COPYING") == -1) {
mprintf("!build: Can't add COPYING to tar archive\n");
gzclose(tar);
unlink(tarfile);
free(tarfile);
FREE_LS(dblist2);
return -1;
}
if (bc || hy) {
if (!hy && tar_addfile(-1, tar, "bytecode.info") == -1) {
gzclose(tar);
unlink(tarfile);
free(tarfile);
FREE_LS(dblist2);
return -1;
}
for (i = 0; i < dblist2cnt; i++) {
if (tar_addfile(-1, tar, dblist2[i]) == -1) {
gzclose(tar);
unlink(tarfile);
free(tarfile);
FREE_LS(dblist2);
return -1;
}
}
}
if (!bc || hy) {
for (i = 0; dblist[i].ext; i++) {
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
if (!access(dbfile, R_OK)) {
if (tar_addfile(-1, tar, dbfile) == -1) {
gzclose(tar);
unlink(tarfile);
free(tarfile);
FREE_LS(dblist2);
return -1;
}
}
} |
3d167464 |
} |
e18f93ee |
gzclose(tar); |
490d6c41 |
FREE_LS(dblist2); |
8139fd99 |
|
e18f93ee |
/* MD5 + dsig */ |
288057e9 |
if (!(fh = fopen(tarfile, "rb"))) {
mprintf("!build: Can't open file %s for reading\n", tarfile);
unlink(tarfile);
free(tarfile);
return -1; |
3d167464 |
}
|
288057e9 |
if (!(pt = cli_hashstream(fh, buffer, 1))) {
mprintf("!build: Can't generate MD5 checksum for %s\n", tarfile);
fclose(fh);
unlink(tarfile);
free(tarfile);
return -1; |
3d167464 |
} |
e18f93ee |
rewind(fh);
sprintf(header + strlen(header), "%s:", pt); |
335d1663 |
free(pt); |
3d167464 |
|
288057e9 |
if (!optget(opts, "unsigned")->enabled) {
if (!(pt = getdsig(optget(opts, "server")->strarg, builder, buffer, 16, 1))) {
mprintf("!build: Can't get digital signature from remote server\n");
fclose(fh);
unlink(tarfile);
free(tarfile);
return -1;
}
sprintf(header + strlen(header), "%s:", pt);
free(pt); |
cdddd014 |
} else { |
288057e9 |
sprintf(header + strlen(header), "X:"); |
ae307914 |
} |
8139fd99 |
|
3365db23 |
/* add builder */ |
e3bcc33a |
strcat(header, builder); |
8139fd99 |
|
3365db23 |
/* add current time */ |
288057e9 |
sprintf(header + strlen(header), ":%u", (unsigned int)timet); |
3365db23 |
|
8139fd99 |
/* fill up with spaces */ |
288057e9 |
while (strlen(header) < sizeof(header) - 1)
strcat(header, " "); |
8139fd99 |
/* build the final database */ |
d6af38e7 |
newcvd = optget(opts, "build")->strarg; |
288057e9 |
if (!(cvd = fopen(newcvd, "wb"))) {
mprintf("!build: Can't create final database %s\n", newcvd);
fclose(fh);
unlink(tarfile);
free(tarfile);
return -1;
}
if (fwrite(header, 1, 512, cvd) != 512) {
mprintf("!build: Can't write to %s\n", newcvd);
fclose(fh);
unlink(tarfile);
free(tarfile);
fclose(cvd);
unlink(newcvd);
return -1;
}
while ((bytes = fread(buffer, 1, FILEBUFF, fh)) > 0) {
if (fwrite(buffer, 1, bytes, cvd) != bytes) {
mprintf("!build: Can't write to %s\n", newcvd);
fclose(fh);
unlink(tarfile);
free(tarfile);
fclose(cvd);
unlink(newcvd);
return -1;
} |
3d167464 |
} |
8139fd99 |
|
e18f93ee |
fclose(fh); |
8139fd99 |
fclose(cvd); |
e18f93ee |
|
288057e9 |
if (unlink(tarfile) == -1) {
mprintf("^build: Can't unlink %s\n", tarfile);
unlink(tarfile);
free(tarfile);
unlink(newcvd);
return -1; |
3d167464 |
} |
e18f93ee |
free(tarfile); |
8139fd99 |
|
e18f93ee |
mprintf("Created %s\n", newcvd); |
3d167464 |
|
288057e9 |
if (optget(opts, "unsigned")->enabled)
return 0; |
cdddd014 |
|
288057e9 |
if (!oldcvd || optget(opts, "no-cdiff")->enabled) {
mprintf("Skipping .cdiff creation\n");
return 0; |
03527bee |
}
|
1aa405c3 |
/* generate patch */ |
288057e9 |
if (!(pt = cli_gentemp(NULL))) {
mprintf("!build: Can't generate temporary name\n");
unlink(newcvd);
return -1; |
c4cbb847 |
} |
1aa405c3 |
|
288057e9 |
if (mkdir(pt, 0700)) {
mprintf("!build: Can't create temporary directory %s\n", pt);
free(pt);
unlink(newcvd);
return -1; |
e18f93ee |
}
|
288057e9 |
if (cli_cvdunpack(olddb, pt) == -1) {
mprintf("!build: Can't unpack CVD file %s\n", olddb);
cli_rmdirs(pt);
free(pt);
unlink(newcvd);
return -1; |
e18f93ee |
}
strncpy(olddb, pt, sizeof(olddb)); |
288057e9 |
olddb[sizeof(olddb) - 1] = '\0'; |
e18f93ee |
free(pt);
|
288057e9 |
if (!(pt = cli_gentemp(NULL))) {
mprintf("!build: Can't generate temporary name\n");
cli_rmdirs(olddb);
unlink(newcvd);
return -1; |
1aa405c3 |
}
|
288057e9 |
if (mkdir(pt, 0700)) {
mprintf("!build: Can't create temporary directory %s\n", pt);
free(pt);
cli_rmdirs(olddb);
unlink(newcvd);
return -1; |
1aa405c3 |
} |
e18f93ee |
|
288057e9 |
if (cli_cvdunpack(newcvd, pt) == -1) {
mprintf("!build: Can't unpack CVD file %s\n", newcvd);
cli_rmdirs(pt);
free(pt);
cli_rmdirs(olddb);
unlink(newcvd);
return -1; |
1aa405c3 |
}
|
03527bee |
snprintf(patch, sizeof(patch), "%s-%u.script", dbname, version); |
1aa405c3 |
ret = diffdirs(olddb, pt, patch);
|
66ba1785 |
cli_rmdirs(pt); |
1aa405c3 |
free(pt);
|
288057e9 |
if (ret == -1) {
cli_rmdirs(olddb);
unlink(newcvd);
return -1; |
1aa405c3 |
}
|
5b68299b |
ret = verifydiff(patch, NULL, olddb); |
e18f93ee |
cli_rmdirs(olddb); |
1aa405c3 |
|
288057e9 |
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 { |
288057e9 |
ret = script2cdiff(patch, builder, opts); |
1aa405c3 |
}
return ret; |
3d167464 |
}
|
d6af38e7 |
static int unpack(const struct optstruct *opts) |
3d167464 |
{ |
288057e9 |
char name[512], *dbdir;
const char *localdbdir = NULL;
if (optget(opts, "datadir")->active)
localdbdir = optget(opts, "datadir")->strarg;
if (optget(opts, "unpack-current")->enabled) {
dbdir = freshdbdir();
snprintf(name, sizeof(name), "%s" PATHSEP "%s.cvd", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
if (access(name, R_OK)) {
snprintf(name, sizeof(name), "%s" PATHSEP "%s.cld", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
if (access(name, R_OK)) {
mprintf("!unpack: Couldn't find %s CLD/CVD database in %s\n", optget(opts, "unpack-current")->strarg, localdbdir ? localdbdir : dbdir);
free(dbdir);
return -1;
}
}
free(dbdir); |
1aa405c3 |
|
e18f93ee |
} else { |
288057e9 |
strncpy(name, optget(opts, "unpack")->strarg, sizeof(name));
name[sizeof(name) - 1] = '\0'; |
e18f93ee |
} |
8139fd99 |
|
ff1445a0 |
if (cl_cvdverify(name) != CL_SUCCESS) {
mprintf("!unpack: %s is not a valid CVD\n", name);
return -1;
}
|
288057e9 |
if (cli_cvdunpack(name, ".") == -1) {
mprintf("!unpack: Can't unpack file %s\n", name);
return -1; |
3d167464 |
}
|
2d70a403 |
return 0; |
8139fd99 |
}
|
d6af38e7 |
static int cvdinfo(const struct optstruct *opts) |
8139fd99 |
{ |
288057e9 |
struct cl_cvd *cvd;
char *pt;
int ret; |
3d167464 |
|
d6af38e7 |
pt = optget(opts, "info")->strarg; |
288057e9 |
if ((cvd = cl_cvdhead(pt)) == NULL) {
mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt);
return -1; |
8139fd99 |
} |
d22970ec |
mprintf("File: %s\n", pt); |
8139fd99 |
|
3d167464 |
pt = strchr(cvd->time, '-'); |
288057e9 |
if (!pt) { |
557b448f |
cl_cvdfree(cvd); |
0e4995ce |
return -1;
} |
3d167464 |
*pt = ':'; |
efa239e2 |
mprintf("Build time: %s\n", cvd->time); |
e18f93ee |
mprintf("Version: %u\n", cvd->version);
mprintf("Signatures: %u\n", cvd->sigs);
mprintf("Functionality level: %u\n", cvd->fl); |
efa239e2 |
mprintf("Builder: %s\n", cvd->builder); |
e18f93ee |
|
d6af38e7 |
pt = optget(opts, "info")->strarg; |
288057e9 |
if (cli_strbcasestr(pt, ".cvd")) {
mprintf("MD5: %s\n", cvd->md5);
mprintf("Digital signature: %s\n", cvd->dsig); |
e18f93ee |
} |
3d167464 |
cl_cvdfree(cvd); |
288057e9 |
if (cli_strbcasestr(pt, ".cud"))
mprintf("Verification: Unsigned container\n");
else if ((ret = cl_cvdverify(pt))) {
mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
return -1; |
06d9e7f3 |
} else |
288057e9 |
mprintf("Verification OK.\n"); |
06d9e7f3 |
|
3d167464 |
return 0; |
8139fd99 |
}
|
b4561aa2 |
static int listdb(const char *filename, const regex_t *regex); |
ae307914 |
|
b4561aa2 |
static int listdir(const char *dirname, const regex_t *regex) |
3d167464 |
{ |
288057e9 |
DIR *dd;
struct dirent *dent;
char *dbfile; |
ae307914 |
|
288057e9 |
if ((dd = opendir(dirname)) == NULL) { |
efa239e2 |
mprintf("!listdir: Can't open directory %s\n", dirname); |
3d167464 |
return -1; |
ae307914 |
}
|
288057e9 |
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
(cli_strbcasestr(dent->d_name, ".db") ||
cli_strbcasestr(dent->d_name, ".hdb") ||
cli_strbcasestr(dent->d_name, ".hdu") ||
cli_strbcasestr(dent->d_name, ".hsb") ||
cli_strbcasestr(dent->d_name, ".hsu") ||
cli_strbcasestr(dent->d_name, ".mdb") ||
cli_strbcasestr(dent->d_name, ".mdu") ||
cli_strbcasestr(dent->d_name, ".msb") ||
cli_strbcasestr(dent->d_name, ".msu") ||
cli_strbcasestr(dent->d_name, ".ndb") ||
cli_strbcasestr(dent->d_name, ".ndu") ||
cli_strbcasestr(dent->d_name, ".ldb") ||
cli_strbcasestr(dent->d_name, ".ldu") ||
cli_strbcasestr(dent->d_name, ".sdb") ||
cli_strbcasestr(dent->d_name, ".zmd") ||
cli_strbcasestr(dent->d_name, ".rmd") ||
cli_strbcasestr(dent->d_name, ".cdb") ||
cli_strbcasestr(dent->d_name, ".cbc") ||
cli_strbcasestr(dent->d_name, ".cld") ||
cli_strbcasestr(dent->d_name, ".cvd") ||
cli_strbcasestr(dent->d_name, ".crb") ||
cli_strbcasestr(dent->d_name, ".imp"))) {
dbfile = (char *)malloc(strlen(dent->d_name) + strlen(dirname) + 2);
if (!dbfile) {
mprintf("!listdir: Can't allocate memory for dbfile\n");
closedir(dd);
return -1;
}
sprintf(dbfile, "%s" PATHSEP "%s", dirname, dent->d_name);
if (listdb(dbfile, regex) == -1) {
mprintf("!listdb: Error listing database %s\n", dbfile);
free(dbfile);
closedir(dd);
return -1;
}
free(dbfile);
}
} |
2d70a403 |
} |
ae307914 |
|
3d167464 |
closedir(dd);
return 0; |
0f387b1b |
} |
0a2ad257 |
|
b4561aa2 |
static int listdb(const char *filename, const regex_t *regex) |
0a2ad257 |
{ |
288057e9 |
FILE *fh;
char *buffer, *pt, *start, *dir;
const char *dbname, *pathsep = PATHSEP;
unsigned int line = 0; |
0a2ad257 |
|
288057e9 |
if ((fh = fopen(filename, "rb")) == NULL) {
mprintf("!listdb: Can't open file %s\n", filename);
return -1; |
0a2ad257 |
}
|
288057e9 |
if (!(buffer = (char *)malloc(FILEBUFF))) {
mprintf("!listdb: Can't allocate memory for buffer\n");
fclose(fh);
return -1; |
0a2ad257 |
}
/* check for CVD file */ |
288057e9 |
if (!fgets(buffer, 12, fh)) {
mprintf("!listdb: fgets failed\n");
free(buffer);
fclose(fh);
return -1; |
e18f93ee |
}
rewind(fh); |
0a2ad257 |
|
288057e9 |
if (!strncmp(buffer, "ClamAV-VDB:", 11)) {
free(buffer);
fclose(fh); |
0a2ad257 |
|
288057e9 |
if (!(dir = cli_gentemp(NULL))) {
mprintf("!listdb: Can't generate temporary name\n");
return -1;
} |
0a2ad257 |
|
288057e9 |
if (mkdir(dir, 0700)) {
mprintf("!listdb: Can't create temporary directory %s\n", dir);
free(dir);
return -1;
} |
0a2ad257 |
|
288057e9 |
if (cli_cvdunpack(filename, dir) == -1) {
mprintf("!listdb: Can't unpack CVD file %s\n", filename);
cli_rmdirs(dir);
free(dir);
return -1;
} |
0a2ad257 |
|
288057e9 |
/* list extracted directory */
if (listdir(dir, regex) == -1) {
mprintf("!listdb: Can't list directory %s\n", filename);
cli_rmdirs(dir);
free(dir);
return -1;
} |
0a2ad257 |
|
288057e9 |
cli_rmdirs(dir);
free(dir); |
0a2ad257 |
|
288057e9 |
return 0; |
0a2ad257 |
}
|
288057e9 |
if (!(dbname = strrchr(filename, *pathsep))) {
mprintf("!listdb: Invalid filename %s\n", filename);
fclose(fh);
free(buffer);
return -1; |
ca141d78 |
}
dbname++;
|
288057e9 |
if (cli_strbcasestr(filename, ".db")) { /* old style database */ |
3365db23 |
|
288057e9 |
while (fgets(buffer, FILEBUFF, fh)) {
if (regex) {
cli_chomp(buffer);
if (!cli_regexec(regex, buffer, 0, NULL, 0))
mprintf("[%s] %s\n", dbname, buffer);
continue;
}
line++; |
a86c6003 |
|
288057e9 |
if (buffer && buffer[0] == '#') |
a86c6003 |
continue;
|
288057e9 |
pt = strchr(buffer, '=');
if (!pt) {
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
fclose(fh);
free(buffer);
return -1;
} |
0a2ad257 |
|
288057e9 |
start = buffer;
*pt = 0; |
0a2ad257 |
|
288057e9 |
if ((pt = strstr(start, " (Clam)")))
*pt = 0; |
3365db23 |
|
288057e9 |
mprintf("%s\n", start);
} |
0a2ad257 |
|
c05ac24f |
} else if (cli_strbcasestr(filename, ".crb")) {
while (fgets(buffer, FILEBUFF, fh)) {
cli_chomp(buffer); |
288057e9 |
|
c05ac24f |
if (buffer[0] == '#')
continue;
if (regex) { |
288057e9 |
if (!cli_regexec(regex, buffer, 0, NULL, 0)) |
c05ac24f |
mprintf("[%s] %s\n", dbname, buffer);
continue;
}
line++;
mprintf("%s\n", buffer);
} |
288057e9 |
} else if (cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".hdu") || cli_strbcasestr(filename, ".mdb") || cli_strbcasestr(filename, ".mdu") || cli_strbcasestr(filename, ".hsb") || cli_strbcasestr(filename, ".hsu") || cli_strbcasestr(filename, ".msb") || cli_strbcasestr(filename, ".msu") || cli_strbcasestr(filename, ".imp")) { /* hash database */ |
0a2ad257 |
|
288057e9 |
while (fgets(buffer, FILEBUFF, fh)) {
cli_chomp(buffer);
if (regex) {
if (!cli_regexec(regex, buffer, 0, NULL, 0))
mprintf("[%s] %s\n", dbname, buffer);
continue;
}
line++; |
a86c6003 |
|
288057e9 |
if (buffer && buffer[0] == '#') |
a86c6003 |
continue;
start = cli_strtok(buffer, 2, ":"); |
3365db23 |
|
288057e9 |
if (!start) {
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
fclose(fh);
free(buffer);
return -1;
} |
3365db23 |
|
288057e9 |
if ((pt = strstr(start, " (Clam)")))
*pt = 0; |
3365db23 |
|
288057e9 |
mprintf("%s\n", start);
free(start);
} |
0a2ad257 |
|
288057e9 |
} else if (cli_strbcasestr(filename, ".ndb") || cli_strbcasestr(filename, ".ndu") || cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu") || cli_strbcasestr(filename, ".sdb") || cli_strbcasestr(filename, ".zmd") || cli_strbcasestr(filename, ".rmd") || cli_strbcasestr(filename, ".cdb")) { |
7ec67e94 |
|
288057e9 |
while (fgets(buffer, FILEBUFF, fh)) {
cli_chomp(buffer);
if (regex) {
if (!cli_regexec(regex, buffer, 0, NULL, 0))
mprintf("[%s] %s\n", dbname, buffer);
continue;
}
line++; |
1437615c |
|
288057e9 |
if (buffer && buffer[0] == '#') |
a86c6003 |
continue;
|
288057e9 |
if (cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu"))
pt = strchr(buffer, ';');
else
pt = strchr(buffer, ':');
if (!pt) {
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
fclose(fh);
free(buffer);
return -1;
}
*pt = 0;
if ((pt = strstr(buffer, " (Clam)")))
*pt = 0;
mprintf("%s\n", buffer);
}
} else if (cli_strbcasestr(filename, ".cbc")) {
if (fgets(buffer, FILEBUFF, fh) && fgets(buffer, FILEBUFF, fh)) {
pt = strchr(buffer, ';');
if (!pt) { /* not a real sig */
fclose(fh);
free(buffer);
return 0;
}
if (regex) {
if (!cli_regexec(regex, buffer, 0, NULL, 0)) {
mprintf("[%s BYTECODE] %s", dbname, buffer);
}
} else {
*pt = 0;
mprintf("%s\n", buffer);
}
} |
0a2ad257 |
} |
e18f93ee |
fclose(fh); |
0a2ad257 |
free(buffer);
return 0;
}
|
b4561aa2 |
static int listsigs(const struct optstruct *opts, int mode) |
0a2ad257 |
{ |
288057e9 |
int ret;
const char *name;
char *dbdir;
STATBUF sb;
regex_t reg;
const char *localdbdir = NULL;
if (optget(opts, "datadir")->active)
localdbdir = optget(opts, "datadir")->strarg;
if (mode == 0) {
name = optget(opts, "list-sigs")->strarg;
if (access(name, R_OK) && localdbdir)
name = localdbdir;
if (CLAMSTAT(name, &sb) == -1) {
mprintf("--list-sigs: Can't get status of %s\n", name);
return -1;
}
mprintf_stdout = 1;
if (S_ISDIR(sb.st_mode)) {
if (!strcmp(name, DATADIR)) {
dbdir = freshdbdir();
ret = listdir(localdbdir ? localdbdir : dbdir, NULL);
free(dbdir);
} else {
ret = listdir(name, NULL);
}
} else {
ret = listdb(name, NULL);
} |
b4561aa2 |
|
98ce643b |
} else { |
288057e9 |
if (cli_regcomp(®, optget(opts, "find-sigs")->strarg, REG_EXTENDED | REG_NOSUB) != 0) {
mprintf("--find-sigs: Can't compile regex\n");
return -1;
}
mprintf_stdout = 1;
dbdir = freshdbdir();
ret = listdir(localdbdir ? localdbdir : dbdir, ®);
free(dbdir);
cli_regfree(®); |
98ce643b |
} |
0a2ad257 |
|
3d167464 |
return ret;
}
|
d6af38e7 |
static int vbadump(const struct optstruct *opts) |
3d167464 |
{ |
288057e9 |
int fd, hex_output;
char *dir;
const char *pt;
struct uniq *vba = NULL;
cli_ctx *ctx;
if (optget(opts, "vba-hex")->enabled) {
hex_output = 1;
pt = optget(opts, "vba-hex")->strarg; |
05cb44b2 |
} else { |
288057e9 |
hex_output = 0;
pt = optget(opts, "vba")->strarg; |
05cb44b2 |
} |
288057e9 |
if ((fd = open(pt, O_RDONLY | O_BINARY)) == -1) {
mprintf("!vbadump: Can't open file %s\n", pt);
return -1; |
3d167464 |
}
/* generate the temporary directory */ |
288057e9 |
if (!(dir = cli_gentemp(NULL))) {
mprintf("!vbadump: Can't generate temporary name\n");
close(fd);
return -1; |
e18f93ee |
}
|
288057e9 |
if (mkdir(dir, 0700)) {
mprintf("!vbadump: Can't create temporary directory %s\n", dir);
free(dir);
close(fd); |
3d167464 |
return -1;
} |
288057e9 |
if (!(ctx = convenience_ctx(fd))) {
close(fd);
free(dir);
return -1; |
034c02fd |
} |
288057e9 |
if (cli_ole2_extract(dir, ctx, &vba)) {
destroy_ctx(-1, ctx);
cli_rmdirs(dir); |
3d167464 |
free(dir); |
1e574d54 |
close(fd); |
3d167464 |
return -1;
} |
e0f667e7 |
destroy_ctx(-1, ctx); |
288057e9 |
if (vba)
sigtool_vba_scandir(dir, hex_output, vba); |
66ba1785 |
cli_rmdirs(dir); |
3d167464 |
free(dir); |
1e574d54 |
close(fd); |
3d167464 |
return 0; |
ee039e40 |
}
|
105950c4 |
static int comparesha(const char *diff) |
e669f71e |
{ |
288057e9 |
char info[32], buff[FILEBUFF], *sha, *pt, *name;
const char *tokens[3];
FILE *fh;
int ret = 0, tokens_count; |
e669f71e |
|
105950c4 |
name = strdup(diff); |
288057e9 |
if (!name) {
mprintf("!verifydiff: strdup() failed\n");
return -1; |
105950c4 |
} |
288057e9 |
if (!(pt = strrchr(name, '-')) || !isdigit(pt[1])) {
mprintf("!verifydiff: Invalid diff name\n");
free(name);
return -1; |
105950c4 |
}
*pt = 0; |
288057e9 |
if ((pt = strrchr(name, *PATHSEP)))
pt++; |
105950c4 |
else |
288057e9 |
pt = name; |
e669f71e |
|
105950c4 |
snprintf(info, sizeof(info), "%s.info", pt);
free(name); |
e669f71e |
|
288057e9 |
if (!(fh = fopen(info, "rb"))) {
mprintf("!verifydiff: Can't open %s\n", info);
return -1;
}
if (!fgets(buff, sizeof(buff), fh) || strncmp(buff, "ClamAV-VDB", 10)) {
mprintf("!verifydiff: Incorrect info file %s\n", info);
fclose(fh);
return -1;
}
while (fgets(buff, sizeof(buff), fh)) {
cli_chomp(buff);
tokens_count = cli_strtokenize(buff, ':', 3, tokens);
if (tokens_count != 3) {
if (!strcmp(tokens[0], "DSIG"))
continue;
mprintf("!verifydiff: Incorrect format of %s\n", info);
ret = -1;
break;
}
if (!(sha = sha256file(tokens[0], NULL))) {
mprintf("!verifydiff: Can't generate SHA256 for %s\n", buff);
ret = -1;
break;
}
if (strcmp(sha, tokens[2])) {
mprintf("!verifydiff: %s has incorrect checksum\n", buff);
ret = -1;
free(sha);
break;
}
free(sha); |
e669f71e |
}
fclose(fh);
return ret;
}
|
d6af38e7 |
static int rundiff(const struct optstruct *opts) |
bb990ecd |
{ |
288057e9 |
int fd, ret;
unsigned short mode;
const char *diff; |
bb990ecd |
|
d6af38e7 |
diff = optget(opts, "run-cdiff")->strarg; |
288057e9 |
if (strstr(diff, ".cdiff")) {
mode = 1;
} else if (strstr(diff, ".script")) {
mode = 0; |
5b68299b |
} else { |
288057e9 |
mprintf("!rundiff: Incorrect file name (no .cdiff/.script extension)\n");
return -1; |
5b68299b |
} |
bb990ecd |
|
288057e9 |
if ((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
mprintf("!rundiff: Can't open file %s\n", diff);
return -1; |
6edef583 |
} |
bb990ecd |
|
5b68299b |
ret = cdiff_apply(fd, mode); |
bb990ecd |
close(fd);
|
288057e9 |
if (!ret)
ret = comparesha(diff); |
e669f71e |
|
bb990ecd |
return ret;
}
|
73e996dc |
static int maxlinelen(const char *file)
{ |
288057e9 |
int fd, bytes, n = 0, nmax = 0, i;
char buff[512]; |
73e996dc |
|
288057e9 |
if ((fd = open(file, O_RDONLY)) == -1) {
mprintf("!maxlinelen: Can't open file %s\n", file);
return -1; |
73e996dc |
}
|
288057e9 |
while ((bytes = read(fd, buff, 512)) > 0) {
for (i = 0; i < bytes; i++, ++n) {
if (buff[i] == '\n') {
if (n > nmax)
nmax = n;
n = 0;
}
} |
73e996dc |
}
|
288057e9 |
if (bytes == -1) {
mprintf("!maxlinelen: Can't read file %s\n", file);
close(fd);
return -1; |
73e996dc |
} |
288057e9 |
|
a252900c |
close(fd); |
e00c869a |
return nmax + 1; |
73e996dc |
}
|
37eca903 |
static int compare(const char *oldpath, const char *newpath, FILE *diff)
{ |
288057e9 |
FILE *old, *new;
char *obuff, *nbuff, *tbuff, *pt, *omd5, *nmd5;
unsigned int oline = 0, tline, found, i, badxchg = 0;
int l1 = 0, l2;
long opos;
if (!access(oldpath, R_OK) && (omd5 = cli_hashfile(oldpath, 1))) {
if (!(nmd5 = cli_hashfile(newpath, 1))) {
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);
l1 = maxlinelen(oldpath); |
37eca903 |
}
|
73e996dc |
l2 = maxlinelen(newpath); |
288057e9 |
if (l1 == -1 || l2 == -1)
return -1; |
73e996dc |
l1 = MAX(l1, l2) + 1;
obuff = malloc(l1); |
288057e9 |
if (!obuff) {
mprintf("!compare: Can't allocate memory for 'obuff'\n");
return -1; |
73e996dc |
}
nbuff = malloc(l1); |
288057e9 |
if (!nbuff) {
mprintf("!compare: Can't allocate memory for 'nbuff'\n");
free(obuff);
return -1; |
73e996dc |
}
tbuff = malloc(l1); |
288057e9 |
if (!tbuff) {
mprintf("!compare: Can't allocate memory for 'tbuff'\n");
free(obuff);
free(nbuff);
return -1; |
73e996dc |
}
|
288057e9 |
if (l1 > CLI_DEFAULT_LSIG_BUFSIZE)
fprintf(diff, "#LSIZE %u\n", l1 + 32); |
73e996dc |
|
37eca903 |
fprintf(diff, "OPEN %s\n", newpath);
|
288057e9 |
if (!(new = fopen(newpath, "rb"))) {
mprintf("!compare: Can't open file %s for reading\n", newpath);
free(obuff);
free(nbuff);
free(tbuff);
return -1; |
9b3e1e85 |
} |
25fa9b70 |
old = fopen(oldpath, "rb"); |
37eca903 |
|
288057e9 |
while (fgets(nbuff, l1, new)) {
i = strlen(nbuff);
if (i >= 2 && (nbuff[i - 1] == '\r' || (nbuff[i - 1] == '\n' && nbuff[i - 2] == '\r'))) {
mprintf("!compare: New %s file contains lines terminated with CRLF or CR\n", newpath);
if (old)
fclose(old);
fclose(new);
free(obuff);
free(nbuff);
free(tbuff);
return -1;
}
cli_chomp(nbuff);
if (!old) {
fprintf(diff, "ADD %s\n", nbuff);
} else {
if (fgets(obuff, l1, old)) {
oline++;
cli_chomp(obuff);
if (!strcmp(nbuff, obuff)) {
continue;
} else {
tline = 0;
found = 0;
opos = ftell(old);
while (fgets(tbuff, l1, 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, l1);
tbuff[l1 - 1] = '\0';
for (i = 0; i < tline; i++) {
tbuff[MIN(16, l1 - 1)] = 0;
if ((pt = strchr(tbuff, ' ')))
*pt = 0;
fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
if (!fgets(tbuff, l1, old))
break;
}
oline += tline;
} else {
if (!*obuff || *obuff == ' ') {
badxchg = 1;
break;
}
obuff[MIN(16, l1 - 1)] = 0;
if ((pt = strchr(obuff, ' ')))
*pt = 0;
fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff);
}
}
} else {
fclose(old);
old = NULL;
fprintf(diff, "ADD %s\n", nbuff);
}
}
}
if (old) {
if (!badxchg) {
while (fgets(obuff, l1, old)) {
oline++;
cli_chomp(obuff);
obuff[MIN(16, l1 - 1)] = 0;
if ((pt = strchr(obuff, ' ')))
*pt = 0;
fprintf(diff, "DEL %u %s\n", oline, obuff);
}
}
fclose(old); |
fb30f402 |
} |
37eca903 |
fprintf(diff, "CLOSE\n"); |
73e996dc |
free(obuff);
free(tbuff); |
288057e9 |
if (badxchg) {
fprintf(diff, "UNLINK %s\n", newpath);
fprintf(diff, "OPEN %s\n", newpath);
rewind(new);
while (fgets(nbuff, l1, new)) {
cli_chomp(nbuff);
fprintf(diff, "ADD %s\n", nbuff);
}
fprintf(diff, "CLOSE\n"); |
478e8c3f |
}
free(nbuff);
fclose(new); |
37eca903 |
return 0;
}
|
f00041c1 |
static int compareone(const struct optstruct *opts)
{ |
288057e9 |
if (!opts->filename) {
mprintf("!makediff: --compare requires two arguments\n");
return -1; |
f00041c1 |
} |
288057e9 |
return compare(optget(opts, "compare")->strarg, opts->filename[0], stdout); |
f00041c1 |
}
|
afff80ef |
static int dircopy(const char *src, const char *dest)
{ |
288057e9 |
DIR *dd;
struct dirent *dent;
STATBUF sb;
char spath[512], dpath[512]; |
afff80ef |
|
288057e9 |
if (CLAMSTAT(dest, &sb) == -1) {
if (mkdir(dest, 0755)) {
/* mprintf("!dircopy: Can't create temporary directory %s\n", dest); */
return -1;
} |
afff80ef |
}
|
288057e9 |
if ((dd = opendir(src)) == NULL) { |
afff80ef |
/* mprintf("!dircopy: Can't open directory %s\n", src); */
return -1;
}
|
288057e9 |
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue; |
afff80ef |
|
288057e9 |
snprintf(spath, sizeof(spath), "%s" PATHSEP "%s", src, dent->d_name);
snprintf(dpath, sizeof(dpath), "%s" PATHSEP "%s", dest, dent->d_name); |
afff80ef |
|
288057e9 |
if (filecopy(spath, dpath) == -1) {
/* mprintf("!dircopy: Can't copy %s to %s\n", spath, dpath); */
cli_rmdirs(dest);
closedir(dd);
return -1;
}
} |
afff80ef |
}
closedir(dd);
return 0;
}
|
5b68299b |
static int verifydiff(const char *diff, const char *cvd, const char *incdir) |
6edef583 |
{ |
288057e9 |
char *tempdir, cwd[512];
int ret = 0, fd;
unsigned short mode;
if (strstr(diff, ".cdiff")) {
mode = 1;
} else if (strstr(diff, ".script")) {
mode = 0; |
5b68299b |
} else { |
288057e9 |
mprintf("!verifydiff: Incorrect file name (no .cdiff/.script extension)\n");
return -1; |
5b68299b |
} |
6edef583 |
tempdir = cli_gentemp(NULL); |
288057e9 |
if (!tempdir) {
mprintf("!verifydiff: Can't generate temporary name for tempdir\n");
return -1; |
6edef583 |
}
|
288057e9 |
if (mkdir(tempdir, 0700) == -1) {
mprintf("!verifydiff: Can't create directory %s\n", tempdir);
free(tempdir);
return -1; |
6edef583 |
}
|
288057e9 |
if (cvd) {
if (cli_cvdunpack(cvd, tempdir) == -1) {
mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd);
cli_rmdirs(tempdir);
free(tempdir);
return -1;
} |
1aa405c3 |
} else { |
288057e9 |
if (dircopy(incdir, tempdir) == -1) {
mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir);
cli_rmdirs(tempdir);
free(tempdir);
return -1;
} |
6edef583 |
}
|
288057e9 |
if (!getcwd(cwd, sizeof(cwd))) {
mprintf("!verifydiff: getcwd() failed\n");
cli_rmdirs(tempdir);
free(tempdir);
return -1; |
6edef583 |
}
|
288057e9 |
if ((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
mprintf("!verifydiff: Can't open diff file %s\n", diff);
cli_rmdirs(tempdir);
free(tempdir);
return -1; |
e18f93ee |
} |
6edef583 |
|
288057e9 |
if (chdir(tempdir) == -1) {
mprintf("!verifydiff: Can't chdir to %s\n", tempdir);
cli_rmdirs(tempdir);
free(tempdir);
close(fd);
return -1; |
6edef583 |
}
|
288057e9 |
if (cdiff_apply(fd, mode) == -1) {
mprintf("!verifydiff: Can't apply %s\n", diff);
if (chdir(cwd) == -1)
mprintf("^verifydiff: Can't chdir to %s\n", cwd);
cli_rmdirs(tempdir);
free(tempdir);
close(fd);
return -1; |
6edef583 |
}
close(fd);
|
b2742f88 |
ret = comparesha(diff); |
6edef583 |
|
288057e9 |
if (chdir(cwd) == -1)
mprintf("^verifydiff: Can't chdir to %s\n", cwd); |
66ba1785 |
cli_rmdirs(tempdir); |
6edef583 |
free(tempdir);
|
288057e9 |
if (!ret) {
if (cvd)
mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
else
mprintf("Verification: %s correctly applies to the previous version\n", diff); |
1aa405c3 |
} |
6edef583 |
return ret;
}
|
56b5bd3e |
static void matchsig(const char *sig, const char *offset, int fd) |
a96eead4 |
{ |
288057e9 |
struct cl_engine *engine;
struct cli_ac_result *acres = NULL, *res;
STATBUF sb;
unsigned int matches = 0;
cli_ctx ctx;
struct cl_scan_options options;
int ret; |
048a88e6 |
|
0d370098 |
mprintf("SUBSIG: %s\n", sig);
|
288057e9 |
if (!(engine = cl_engine_new())) {
mprintf("!matchsig: Can't create new engine\n");
return; |
a96eead4 |
} |
ffa9b060 |
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1); |
a96eead4 |
|
288057e9 |
if (cli_initroots(engine, 0) != CL_SUCCESS) {
mprintf("!matchsig: cli_initroots() failed\n");
cl_engine_free(engine);
return; |
a96eead4 |
}
|
288057e9 |
if (cli_parse_add(engine->root[0], "test", sig, 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
mprintf("!matchsig: Can't parse signature\n");
cl_engine_free(engine);
return; |
a96eead4 |
}
|
288057e9 |
if (cl_engine_compile(engine) != CL_SUCCESS) {
mprintf("!matchsig: Can't compile engine\n");
cl_engine_free(engine);
return; |
ffa9b060 |
}
memset(&ctx, '\0', sizeof(cli_ctx)); |
ee39ff06 |
memset(&options, 0, sizeof(struct cl_scan_options)); |
288057e9 |
ctx.engine = engine;
ctx.options = &options; |
048a88e6 |
ctx.options->parse = ~0; |
288057e9 |
ctx.containers = cli_calloc(sizeof(cli_ctx_container), engine->maxreclevel + 2);
if (!ctx.containers) {
cl_engine_free(engine);
return; |
031fe00a |
}
ctx.containers[0].type = CL_TYPE_ANY; |
288057e9 |
ctx.dconf = (struct cli_dconf *)engine->dconf;
ctx.fmap = calloc(sizeof(fmap_t *), 1);
if (!ctx.fmap) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
cl_engine_free(engine);
return; |
a96eead4 |
}
lseek(fd, 0, SEEK_SET); |
a2a004df |
FSTAT(fd, &sb); |
288057e9 |
if (!(*ctx.fmap = fmap(fd, 0, sb.st_size))) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
free(ctx.fmap);
cl_engine_free(engine);
return; |
ffa9b060 |
}
ret = cli_fmap_scandesc(&ctx, 0, 0, NULL, AC_SCAN_VIR, &acres, NULL);
res = acres; |
288057e9 |
while (res) {
matches++;
res = res->next;
}
if (matches) {
/* TODO: check offsets automatically */
mprintf("MATCH: ** YES%s ** (%u %s:", offset ? "/CHECK OFFSET" : "", matches, matches > 1 ? "matches at offsets" : "match at offset");
res = acres;
while (res) {
mprintf(" %u", (unsigned int)res->offset);
res = res->next;
}
mprintf(")\n"); |
ffa9b060 |
} else { |
288057e9 |
mprintf("MATCH: ** NO **\n"); |
ffa9b060 |
} |
288057e9 |
while (acres) {
res = acres;
acres = acres->next;
free(res); |
ffa9b060 |
} |
631cb6a0 |
free(ctx.containers); |
735d623d |
funmap(*ctx.fmap); |
ffa9b060 |
free(ctx.fmap); |
a96eead4 |
cl_engine_free(engine);
}
|
252a354b |
static char *decodehexstr(const char *hex, unsigned int *dlen) |
93173872 |
{ |
288057e9 |
uint16_t *str16;
char *decoded;
unsigned int i, p = 0, wildcard = 0, len = strlen(hex) / 2; |
93173872 |
str16 = cli_hex2ui(hex); |
288057e9 |
if (!str16)
return NULL; |
93173872 |
|
288057e9 |
for (i = 0; i < len; i++)
if (str16[i] & CLI_MATCH_WILDCARD)
wildcard++; |
93173872 |
|
7e8402ac |
decoded = calloc(len + 1 + wildcard * 32, sizeof(char)); |
288057e9 |
if (!decoded) { |
5ef34e67 |
free(str16); |
288057e9 |
mprintf("!decodehexstr: Can't allocate memory for decoded\n");
return NULL;
}
for (i = 0; i < len; i++) {
if (str16[i] & CLI_MATCH_WILDCARD) {
switch (str16[i] & CLI_MATCH_WILDCARD) {
case CLI_MATCH_IGNORE:
p += sprintf(decoded + p, "{WILDCARD_IGNORE}");
break;
case CLI_MATCH_NIBBLE_HIGH:
p += sprintf(decoded + p, "{WILDCARD_NIBBLE_HIGH:0x%x}", str16[i] & 0x00f0);
break;
case CLI_MATCH_NIBBLE_LOW:
p += sprintf(decoded + p, "{WILDCARD_NIBBLE_LOW:0x%x}", str16[i] & 0x000f);
break;
default:
mprintf("!decodehexstr: Unknown wildcard (0x%x@%u)\n", str16[i] & CLI_MATCH_WILDCARD, i);
free(decoded);
free(str16);
return NULL;
}
} else {
decoded[p] = str16[i];
p++;
}
}
if (dlen)
*dlen = p; |
5ef34e67 |
free(str16); |
93173872 |
return decoded;
}
|
336365ec |
inline static char *get_paren_end(char *hexstr)
{
char *pt;
int level = 0;
pt = hexstr; |
288057e9 |
while (*pt != '\0') {
if (*pt == '(') {
level++;
} else if (*pt == ')') {
if (!level)
return pt;
level--;
}
pt++; |
336365ec |
}
return NULL;
}
|
6f003ed6 |
static char *decodehexspecial(const char *hex, unsigned int *dlen)
{ |
288057e9 |
char *pt, *start, *hexcpy, *decoded, *h, *e, *c, op, lop; |
102cd430 |
unsigned int len = 0, hlen, negative; |
288057e9 |
int level;
char *buff; |
6f003ed6 |
|
f2a76e0b |
hexcpy = NULL; |
288057e9 |
buff = NULL; |
6f003ed6 |
hexcpy = strdup(hex); |
288057e9 |
if (!hexcpy) {
mprintf("!decodehexspecial: strdup(hex) failed\n");
return NULL; |
6f003ed6 |
}
pt = strchr(hexcpy, '('); |
288057e9 |
if (!pt) {
free(hexcpy);
return decodehexstr(hex, dlen); |
6f003ed6 |
} else { |
288057e9 |
buff = calloc(strlen(hex) + 512, sizeof(char));
if (!buff) {
mprintf("!decodehexspecial: Can't allocate memory for buff\n");
free(hexcpy);
return NULL;
}
start = hexcpy;
do {
negative = 0;
*pt++ = 0;
if (!start) {
mprintf("!decodehexspecial: Unexpected EOL\n");
free(hexcpy);
free(buff);
return NULL;
}
if (pt >= hexcpy + 2) {
if (pt[-2] == '!') {
negative = 1;
pt[-2] = 0;
}
}
if (!(decoded = decodehexstr(start, &hlen))) {
mprintf("!Decoding failed (1): %s\n", pt);
free(hexcpy);
free(buff);
return NULL;
}
memcpy(&buff[len], decoded, hlen);
len += hlen;
free(decoded);
if (!(start = get_paren_end(pt))) {
mprintf("!decodehexspecial: Missing closing parenthesis\n");
free(hexcpy);
free(buff);
return NULL;
}
*start++ = 0;
if (!strlen(pt)) {
mprintf("!decodehexspecial: Empty block\n");
free(hexcpy);
free(buff);
return NULL;
}
if (!strcmp(pt, "B")) {
if (!*start) {
if (negative)
len += sprintf(buff + len, "{NOT_BOUNDARY_RIGHT}");
else
len += sprintf(buff + len, "{BOUNDARY_RIGHT}");
continue;
} else if (pt - 1 == hexcpy) {
if (negative)
len += sprintf(buff + len, "{NOT_BOUNDARY_LEFT}");
else
len += sprintf(buff + len, "{BOUNDARY_LEFT}");
continue;
}
} else if (!strcmp(pt, "L")) {
if (!*start) {
if (negative)
len += sprintf(buff + len, "{NOT_LINE_MARKER_RIGHT}");
else
len += sprintf(buff + len, "{LINE_MARKER_RIGHT}");
continue;
} else if (pt - 1 == hexcpy) {
if (negative)
len += sprintf(buff + len, "{NOT_LINE_MARKER_LEFT}");
else
len += sprintf(buff + len, "{LINE_MARKER_LEFT}");
continue;
}
} else if (!strcmp(pt, "W")) {
if (!*start) {
if (negative)
len += sprintf(buff + len, "{NOT_WORD_MARKER_RIGHT}");
else
len += sprintf(buff + len, "{WORD_MARKER_RIGHT}");
continue;
} else if (pt - 1 == hexcpy) {
if (negative)
len += sprintf(buff + len, "{NOT_WORD_MARKER_LEFT}");
else
len += sprintf(buff + len, "{WORD_MARKER_LEFT}");
continue;
}
} else {
if (!strlen(pt)) {
mprintf("!decodehexspecial: Empty block\n");
free(hexcpy);
free(buff);
return NULL;
}
/* TODO: analyze string alternative for typing */
if (negative)
len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
else
len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
level = 0;
h = e = pt;
op = '\0';
while ((level >= 0) && (e = strpbrk(h, "()|"))) {
lop = op;
op = *e;
*e++ = 0;
if (op != '(' && lop != ')' && !strlen(h)) {
mprintf("!decodehexspecial: Empty string alternative block\n");
free(hexcpy);
free(buff);
return NULL;
}
//mprintf("decodehexspecial: %s\n", h);
if (!(c = cli_hex2str(h))) {
mprintf("!Decoding failed (3): %s\n", h);
free(hexcpy);
free(buff);
return NULL;
}
memcpy(&buff[len], c, strlen(h) / 2);
len += strlen(h) / 2;
free(c);
switch (op) {
case '(':
level++;
negative = 0;
if (e >= pt + 2) {
if (e[-2] == '!') {
negative = 1;
e[-2] = 0;
}
}
if (negative)
len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
else
len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
break;
case ')':
level--;
buff[len++] = '}';
break;
case '|':
buff[len++] = '|';
break;
default:;
}
h = e;
}
if (!(c = cli_hex2str(h))) {
mprintf("!Decoding failed (4): %s\n", h);
free(hexcpy);
free(buff);
return NULL;
}
memcpy(&buff[len], c, strlen(h) / 2);
len += strlen(h) / 2;
free(c);
buff[len++] = '}';
if (level != 0) {
mprintf("!decodehexspecial: Invalid string alternative nesting\n");
free(hexcpy);
free(buff);
return NULL;
}
}
} while ((pt = strchr(start, '(')));
if (start) {
if (!(decoded = decodehexstr(start, &hlen))) {
mprintf("!Decoding failed (2)\n");
free(buff);
free(hexcpy);
return NULL;
}
memcpy(&buff[len], decoded, hlen);
len += hlen;
free(decoded);
} |
6f003ed6 |
}
free(hexcpy); |
288057e9 |
if (dlen)
*dlen = len; |
6f003ed6 |
return buff;
}
|
7e8402ac |
static int decodehex(const char *hexsig) |
93173872 |
{ |
288057e9 |
char *pt, *hexcpy, *start, *n, *decoded, *wild;
int asterisk = 0;
unsigned int i, j, hexlen, dlen, parts = 0, bw;
int mindist = 0, maxdist = 0, error = 0; |
93173872 |
hexlen = strlen(hexsig); |
0d370098 |
if ((wild = strchr(hexsig, '/'))) { |
288057e9 |
/* ^offset:trigger-logic/regex/options$ */
char *trigger, *regex, *regex_end, *cflags;
size_t tlen = wild - hexsig, rlen, clen;
/* check for trigger */
if (!tlen) {
mprintf("!pcre without logical trigger\n");
return -1;
}
/* locate end of regex for options start, locate options length */
if ((regex_end = strchr(wild + 1, '/')) == NULL) {
mprintf("!missing regex expression terminator /\n");
return -1;
}
rlen = regex_end - wild - 1;
clen = hexlen - tlen - rlen - 2; /* 2 from regex boundaries '/' */
/* get the trigger statement */
trigger = cli_calloc(tlen + 1, sizeof(char));
if (!trigger) {
mprintf("!cannot allocate memory for trigger string\n");
return -1;
}
strncpy(trigger, hexsig, tlen);
trigger[tlen] = '\0';
/* get the regex expression */
regex = cli_calloc(rlen + 1, sizeof(char));
if (!regex) {
mprintf("!cannot allocate memory for regex expression\n");
free(trigger);
return -1;
}
strncpy(regex, hexsig + tlen + 1, rlen);
regex[rlen] = '\0';
/* get the compile flags */
if (clen) {
cflags = cli_calloc(clen + 1, sizeof(char));
if (!cflags) {
mprintf("!cannot allocate memory for compile flags\n");
free(trigger);
free(regex);
return -1;
}
strncpy(cflags, hexsig + tlen + rlen + 2, clen);
cflags[clen] = '\0';
} else {
cflags = NULL;
}
/* print components of regex subsig */
mprintf(" +-> TRIGGER: %s\n", trigger);
mprintf(" +-> REGEX: %s\n", regex);
mprintf(" +-> CFLAGS: %s\n", cflags);
free(trigger);
free(regex);
if (cflags)
free(cflags); |
0d370098 |
#if HAVE_PCRE |
288057e9 |
return 0; |
0d370098 |
#else |
288057e9 |
mprintf("!PCRE subsig cannot be loaded without PCRE support\n");
return -1; |
0d370098 |
#endif |
288057e9 |
} else if (strchr(hexsig, '{') || strchr(hexsig, '[')) {
if (!(hexcpy = strdup(hexsig)))
return -1;
for (i = 0; i < hexlen; i++)
if (hexsig[i] == '{' || hexsig[i] == '[' || hexsig[i] == '*')
parts++;
if (parts)
parts++;
start = pt = hexcpy;
for (i = 1; i <= parts; i++) {
if (i != parts) {
for (j = 0; j < strlen(start); j++) {
if (start[j] == '{' || start[j] == '[') {
asterisk = 0;
pt = start + j;
break;
}
if (start[j] == '*') {
asterisk = 1;
pt = start + j;
break;
}
}
*pt++ = 0;
}
if (mindist && maxdist) {
if (mindist == maxdist)
mprintf("{WILDCARD_ANY_STRING(LENGTH==%u)}", mindist);
else
mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u&&<=%u)}", mindist, maxdist);
} else if (mindist)
mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u)}", mindist);
else if (maxdist)
mprintf("{WILDCARD_ANY_STRING(LENGTH<=%u)}", maxdist);
if (!(decoded = decodehexspecial(start, &dlen))) {
mprintf("!Decoding failed\n");
free(hexcpy);
return -1;
}
bw = write(1, decoded, dlen);
free(decoded);
if (i == parts)
break;
if (asterisk)
mprintf("{WILDCARD_ANY_STRING}");
mindist = maxdist = 0;
if (asterisk) {
start = pt;
continue;
}
if (!(start = strchr(pt, '}')) && !(start = strchr(pt, ']'))) {
error = 1;
break;
}
*start++ = 0;
if (!pt) {
error = 1;
break;
}
if (!strchr(pt, '-')) {
if (!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
error = 1;
break;
}
} else {
if ((n = cli_strtok(pt, 0, "-"))) {
if (!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
error = 1;
free(n);
break;
}
free(n);
}
if ((n = cli_strtok(pt, 1, "-"))) {
if (!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
error = 1;
free(n);
break;
}
free(n);
}
if ((n = cli_strtok(pt, 2, "-"))) { /* strict check */
error = 1;
free(n);
break;
}
}
}
free(hexcpy);
if (error)
return -1;
} else if (strchr(hexsig, '*')) {
for (i = 0; i < hexlen; i++)
if (hexsig[i] == '*')
parts++;
if (parts)
parts++;
for (i = 1; i <= parts; i++) {
if ((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
mprintf("!Can't extract part %u of partial signature\n", i);
return -1;
}
if (!(decoded = decodehexspecial(pt, &dlen))) {
mprintf("!Decoding failed\n");
free(pt);
return -1;
}
bw = write(1, decoded, dlen);
free(decoded);
if (i < parts)
mprintf("{WILDCARD_ANY_STRING}");
free(pt);
} |
93173872 |
} else { |
288057e9 |
if (!(decoded = decodehexspecial(hexsig, &dlen))) {
mprintf("!Decoding failed\n");
return -1;
}
bw = write(1, decoded, dlen);
free(decoded); |
93173872 |
}
|
7e8402ac |
mprintf("\n");
return 0; |
93173872 |
}
|
d002f43e |
static int decodesigmod(const char *sigmod)
{ |
102cd430 |
size_t i; |
288057e9 |
for (i = 0; i < strlen(sigmod); i++) {
mprintf(" ");
switch (sigmod[i]) {
case 'i':
mprintf("NOCASE");
break;
case 'f':
mprintf("FULLWORD");
break;
case 'w':
mprintf("WIDE");
break;
case 'a':
mprintf("ASCII");
break;
default:
mprintf("UNKNOWN");
return -1;
} |
d002f43e |
}
mprintf("\n");
return 0;
}
|
3ed58f86 |
static int decodecdb(char **tokens) |
4b581a2d |
{ |
102cd430 |
int sz = 0; |
288057e9 |
char *range[2];
if (!tokens)
return -1;
mprintf("VIRUS NAME: %s\n", tokens[0]);
mprintf("CONTAINER TYPE: %s\n", (strcmp(tokens[1], "*") ? tokens[1] : "ANY"));
mprintf("CONTAINER SIZE: ");
if (!cli_isnumber(tokens[2])) {
if (!strcmp(tokens[2], "*")) {
mprintf("ANY\n");
} else if (strchr(tokens[2], '-')) {
sz = cli_strtokenize(tokens[2], '-', 2 + 1, (const char **)range);
if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
mprintf("!decodesig: Invalid container size range\n");
return -1;
}
mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
} else {
mprintf("!decodesig: Invalid container size\n");
return -1;
}
} else {
mprintf("%s\n", tokens[2]);
}
mprintf("FILENAME REGEX: %s\n", tokens[3]);
mprintf("COMPRESSED FILESIZE: ");
if (!cli_isnumber(tokens[4])) {
if (!strcmp(tokens[4], "*")) {
mprintf("ANY\n");
} else if (strchr(tokens[4], '-')) {
sz = cli_strtokenize(tokens[4], '-', 2 + 1, (const char **)range);
if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
mprintf("!decodesig: Invalid container size range\n");
return -1;
}
mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
} else {
mprintf("!decodesig: Invalid compressed filesize\n");
return -1;
}
} else {
mprintf("%s\n", tokens[4]);
}
mprintf("UNCOMPRESSED FILESIZE: ");
if (!cli_isnumber(tokens[5])) {
if (!strcmp(tokens[5], "*")) {
mprintf("ANY\n");
} else if (strchr(tokens[5], '-')) {
sz = cli_strtokenize(tokens[5], '-', 2 + 1, (const char **)range);
if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
mprintf("!decodesig: Invalid container size range\n");
return -1;
}
mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
} else {
mprintf("!decodesig: Invalid uncompressed filesize\n");
return -1;
}
} else {
mprintf("%s\n", tokens[5]);
}
mprintf("ENCRYPTION: ");
if (!cli_isnumber(tokens[6])) {
if (!strcmp(tokens[6], "*")) {
mprintf("IGNORED\n");
} else {
mprintf("!decodesig: Invalid encryption flag\n");
return -1;
}
} else {
mprintf("%s\n", (atoi(tokens[6]) ? "YES" : "NO"));
}
mprintf("FILE POSITION: ");
if (!cli_isnumber(tokens[7])) {
if (!strcmp(tokens[7], "*")) {
mprintf("ANY\n");
} else if (strchr(tokens[7], '-')) {
sz = cli_strtokenize(tokens[7], '-', 2 + 1, (const char **)range);
if (sz != 2 || !cli_isnumber(range[0]) || !cli_isnumber(range[1])) {
mprintf("!decodesig: Invalid container size range\n");
return -1;
}
mprintf("WITHIN RANGE %s to %s\n", range[0], range[1]);
} else {
mprintf("!decodesig: Invalid file position\n");
return -1;
}
} else {
mprintf("%s\n", tokens[7]);
}
if (!strcmp(tokens[1], "CL_TYPE_ZIP") || !strcmp(tokens[1], "CL_TYPE_RAR")) {
if (!strcmp(tokens[8], "*")) {
mprintf("CRC SUM: ANY\n");
} else {
errno = 0;
sz = (int)strtol(tokens[8], NULL, 16);
if (!sz && errno) {
mprintf("!decodesig: Invalid cyclic redundancy check sum\n");
return -1;
} else {
mprintf("CRC SUM: %d\n", sz);
}
}
}
return 0; |
4b581a2d |
}
|
09e0a9a7 |
static int decodeftm(char **tokens, int tokens_count)
{
mprintf("FILE TYPE NAME: %s\n", tokens[3]);
mprintf("FILE SIGNATURE TYPE: %s\n", tokens[0]);
mprintf("FILE MAGIC OFFSET: %s\n", tokens[1]);
mprintf("FILE MAGIC HEX: %s\n", tokens[2]);
mprintf("FILE MAGIC DECODED:\n");
decodehex(tokens[2]);
mprintf("FILE TYPE REQUIRED: %s\n", tokens[4]);
mprintf("FILE TYPE DETECTED: %s\n", tokens[5]); |
288057e9 |
if (tokens_count == 7) |
09e0a9a7 |
mprintf("FTM FLEVEL: >=%s\n", tokens[6]); |
288057e9 |
else if (tokens_count == 8) |
09e0a9a7 |
mprintf("FTM FLEVEL: %s..%s\n", tokens[6], tokens[7]);
return 0;
}
|
a96eead4 |
static int decodesig(char *sig, int fd) |
93173872 |
{ |
288057e9 |
char *pt;
char *tokens[68], *subtokens[4], *subhex;
int tokens_count, subtokens_count, subsigs, i, bc = 0;
if (*sig == '[') {
if (!(pt = strchr(sig, ']'))) {
mprintf("!decodesig: Invalid input\n");
return -1;
}
sig = &pt[2];
}
if (strchr(sig, ';')) { /* lsig */
tokens_count = cli_ldbtokenize(sig, ';', 67 + 1, (const char **)tokens, 2);
if (tokens_count < 4) {
mprintf("!decodesig: Invalid or not supported signature format\n");
return -1;
}
mprintf("VIRUS NAME: %s\n", tokens[0]);
if (strlen(tokens[0]) && strstr(tokens[0], ".{") && tokens[0][strlen(tokens[0]) - 1] == '}')
bc = 1;
mprintf("TDB: %s\n", tokens[1]);
mprintf("LOGICAL EXPRESSION: %s\n", tokens[2]);
subsigs = cli_ac_chklsig(tokens[2], tokens[2] + strlen(tokens[2]), NULL, NULL, NULL, 1);
if (subsigs == -1) {
mprintf("!decodesig: Broken logical expression\n");
return -1;
}
subsigs++;
if (subsigs > 64) {
mprintf("!decodesig: Too many subsignatures\n");
return -1;
}
if (!bc && subsigs != tokens_count - 3) {
mprintf("!decodesig: The number of subsignatures (==%u) doesn't match the IDs in the logical expression (==%u)\n", tokens_count - 3, subsigs);
return -1;
}
for (i = 0; i < tokens_count - 3; i++) {
if (i >= subsigs)
mprintf(" * BYTECODE SUBSIG\n");
else
mprintf(" * SUBSIG ID %d\n", i);
subtokens_count = cli_ldbtokenize(tokens[3 + i], ':', 4, (const char **)subtokens, 0);
if (!subtokens_count) {
mprintf("!decodesig: Invalid or not supported subsignature format\n");
return -1;
}
if ((subtokens_count % 2) == 0)
mprintf(" +-> OFFSET: %s\n", subtokens[0]);
else
mprintf(" +-> OFFSET: ANY\n");
if (subtokens_count == 3) {
mprintf(" +-> SIGMOD:");
decodesigmod(subtokens[2]);
} else if (subtokens_count == 4) {
mprintf(" +-> SIGMOD:");
decodesigmod(subtokens[3]);
} else {
mprintf(" +-> SIGMOD: NONE\n");
}
subhex = (subtokens_count % 2) ? subtokens[0] : subtokens[1];
if (fd == -1) { |
0d370098 |
mprintf(" +-> DECODED SUBSIGNATURE:\n"); |
d002f43e |
decodehex(subhex); |
288057e9 |
} else {
mprintf(" +-> ");
matchsig(subhex, subhex, fd);
}
}
} else if (strchr(sig, ':')) { /* ndb or cdb or ftm*/
tokens_count = cli_strtokenize(sig, ':', 12 + 1, (const char **)tokens);
if (tokens_count > 9 && tokens_count < 13) { /* cdb*/
return decodecdb(tokens);
} |
4b581a2d |
|
09e0a9a7 |
if (tokens_count > 5 && tokens_count < 9) { /* ftm */
long ftmsigtype; |
288057e9 |
char *end; |
09e0a9a7 |
ftmsigtype = strtol(tokens[0], &end, 10); |
288057e9 |
if (end == tokens[0] + 1 && (ftmsigtype == 0 || ftmsigtype == 1 || ftmsigtype == 4)) |
09e0a9a7 |
return decodeftm(tokens, tokens_count);
}
|
288057e9 |
if (tokens_count < 4 || tokens_count > 6) {
mprintf("!decodesig: Invalid or not supported signature format\n");
mprintf("TOKENS COUNT: %u\n", tokens_count);
return -1;
}
mprintf("VIRUS NAME: %s\n", tokens[0]);
if (tokens_count == 5)
mprintf("FUNCTIONALITY LEVEL: >=%s\n", tokens[4]);
else if (tokens_count == 6)
mprintf("FUNCTIONALITY LEVEL: %s..%s\n", tokens[4], tokens[5]);
if (!cli_isnumber(tokens[1])) {
mprintf("!decodesig: Invalid target type\n");
return -1;
}
mprintf("TARGET TYPE: ");
switch (atoi(tokens[1])) {
case 0:
mprintf("ANY FILE\n");
break;
case 1:
mprintf("PE\n");
break;
case 2:
mprintf("OLE2\n");
break;
case 3:
mprintf("HTML\n");
break;
case 4:
mprintf("MAIL\n");
break;
case 5:
mprintf("GRAPHICS\n");
break;
case 6:
mprintf("ELF\n");
break;
case 7:
mprintf("NORMALIZED ASCII TEXT\n");
break;
case 8:
mprintf("DISASM DATA\n");
break;
case 9:
mprintf("MACHO\n");
break;
case 10:
mprintf("PDF\n");
break;
case 11:
mprintf("FLASH\n");
break;
case 12:
mprintf("JAVA CLASS\n");
break;
default:
mprintf("!decodesig: Invalid target type\n");
return -1;
}
mprintf("OFFSET: %s\n", tokens[2]);
if (fd == -1) {
mprintf("DECODED SIGNATURE:\n");
decodehex(tokens[3]);
} else {
matchsig(tokens[3], strcmp(tokens[2], "*") ? tokens[2] : NULL, fd);
}
} else if ((pt = strchr(sig, '='))) {
*pt++ = 0;
mprintf("VIRUS NAME: %s\n", sig);
if (fd == -1) {
mprintf("DECODED SIGNATURE:\n");
decodehex(pt);
} else {
matchsig(pt, NULL, fd);
} |
93173872 |
} else { |
288057e9 |
mprintf("decodesig: Not supported signature format\n");
return -1; |
93173872 |
} |
7e8402ac |
return 0;
}
static int decodesigs(void)
{ |
288057e9 |
char buffer[32769]; |
7e8402ac |
fflush(stdin); |
288057e9 |
while (fgets(buffer, sizeof(buffer), stdin)) {
cli_chomp(buffer);
if (!strlen(buffer))
break;
if (decodesig(buffer, -1) == -1)
return -1; |
7e8402ac |
}
return 0; |
93173872 |
}
|
a96eead4 |
static int testsigs(const struct optstruct *opts)
{ |
288057e9 |
char buffer[32769];
FILE *sigs;
int ret = 0, fd; |
a96eead4 |
|
288057e9 |
if (!opts->filename) {
mprintf("!--test-sigs requires two arguments\n");
return -1; |
a96eead4 |
}
sigs = fopen(optget(opts, "test-sigs")->strarg, "rb"); |
288057e9 |
if (!sigs) {
mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
return -1; |
a96eead4 |
}
|
288057e9 |
fd = open(opts->filename[0], O_RDONLY | O_BINARY);
if (fd == -1) {
mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
fclose(sigs);
return -1; |
a96eead4 |
}
|
288057e9 |
while (fgets(buffer, sizeof(buffer), sigs)) {
cli_chomp(buffer);
if (!strlen(buffer))
break;
if (decodesig(buffer, fd) == -1) {
ret = -1;
break;
} |
a96eead4 |
}
close(fd);
fclose(sigs);
return ret;
}
|
1aa405c3 |
static int diffdirs(const char *old, const char *new, const char *patch) |
37eca903 |
{ |
288057e9 |
FILE *diff;
DIR *dd;
struct dirent *dent;
char cwd[512], path[1024]; |
1aa405c3 |
|
288057e9 |
if (!getcwd(cwd, sizeof(cwd))) {
mprintf("!diffdirs: getcwd() failed\n");
return -1; |
e18f93ee |
} |
1aa405c3 |
|
288057e9 |
if (!(diff = fopen(patch, "wb"))) { |
1aa405c3 |
mprintf("!diffdirs: Can't open %s for writing\n", patch); |
288057e9 |
return -1; |
1aa405c3 |
}
|
288057e9 |
if (chdir(new) == -1) {
mprintf("!diffdirs: Can't chdir to %s\n", new);
fclose(diff);
return -1; |
1aa405c3 |
}
|
288057e9 |
if ((dd = opendir(new)) == NULL) { |
1aa405c3 |
mprintf("!diffdirs: Can't open directory %s\n", new); |
288057e9 |
fclose(diff);
return -1;
}
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
snprintf(path, sizeof(path), "%s" PATHSEP "%s", old, dent->d_name);
if (compare(path, dent->d_name, diff) == -1) {
if (chdir(cwd) == -1)
mprintf("^diffdirs: Can't chdir to %s\n", cwd);
fclose(diff);
unlink(patch);
closedir(dd);
return -1;
}
} |
1aa405c3 |
} |
5dc9a067 |
closedir(dd);
/* check for removed files */ |
288057e9 |
if ((dd = opendir(old)) == NULL) { |
5dc9a067 |
mprintf("!diffdirs: Can't open directory %s\n", old); |
288057e9 |
fclose(diff);
return -1; |
5dc9a067 |
} |
1aa405c3 |
|
288057e9 |
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue; |
5dc9a067 |
|
288057e9 |
snprintf(path, sizeof(path), "%s" PATHSEP "%s", new, dent->d_name);
if (access(path, R_OK))
fprintf(diff, "UNLINK %s\n", dent->d_name);
} |
5dc9a067 |
} |
1aa405c3 |
closedir(dd);
fclose(diff); |
5b68299b |
mprintf("Generated diff file %s\n", patch); |
288057e9 |
if (chdir(cwd) == -1)
mprintf("^diffdirs: Can't chdir to %s\n", cwd); |
1aa405c3 |
return 0;
}
|
d6af38e7 |
static int makediff(const struct optstruct *opts) |
1aa405c3 |
{ |
288057e9 |
char *odir, *ndir, name[32], broken[32], dbname[32];
struct cl_cvd *cvd;
unsigned int oldver, newver;
int ret; |
37eca903 |
|
288057e9 |
if (!opts->filename) {
mprintf("!makediff: --diff requires two arguments\n");
return -1; |
37eca903 |
}
|
288057e9 |
if (!(cvd = cl_cvdhead(opts->filename[0]))) {
mprintf("!makediff: Can't read CVD header from %s\n", opts->filename[0]);
return -1; |
37eca903 |
}
newver = cvd->version;
free(cvd);
|
288057e9 |
if (!(cvd = cl_cvdhead(optget(opts, "diff")->strarg))) {
mprintf("!makediff: Can't read CVD header from %s\n", optget(opts, "diff")->strarg);
return -1; |
37eca903 |
}
oldver = cvd->version;
free(cvd);
|
288057e9 |
if (oldver + 1 != newver) {
mprintf("!makediff: The old CVD must be %u\n", newver - 1);
return -1; |
37eca903 |
}
odir = cli_gentemp(NULL); |
288057e9 |
if (!odir) {
mprintf("!makediff: Can't generate temporary name for odir\n");
return -1; |
37eca903 |
}
|
288057e9 |
if (mkdir(odir, 0700) == -1) {
mprintf("!makediff: Can't create directory %s\n", odir);
free(odir);
return -1; |
37eca903 |
}
|
288057e9 |
if (cli_cvdunpack(optget(opts, "diff")->strarg, odir) == -1) {
mprintf("!makediff: Can't unpack CVD file %s\n", optget(opts, "diff")->strarg);
cli_rmdirs(odir);
free(odir);
return -1; |
37eca903 |
}
ndir = cli_gentemp(NULL); |
288057e9 |
if (!ndir) {
mprintf("!makediff: Can't generate temporary name for ndir\n");
cli_rmdirs(odir);
free(odir);
return -1; |
37eca903 |
}
|
288057e9 |
if (mkdir(ndir, 0700) == -1) {
mprintf("!makediff: Can't create directory %s\n", ndir);
free(ndir);
cli_rmdirs(odir);
free(odir);
return -1; |
37eca903 |
}
|
288057e9 |
if (cli_cvdunpack(opts->filename[0], ndir) == -1) {
mprintf("!makediff: Can't unpack CVD file %s\n", opts->filename[0]);
cli_rmdirs(odir);
cli_rmdirs(ndir);
free(odir);
free(ndir);
return -1; |
37eca903 |
}
|
467ab203 |
snprintf(name, sizeof(name), "%s-%u.script", getdbname(opts->filename[0], dbname, sizeof(dbname)), newver); |
1aa405c3 |
ret = diffdirs(odir, ndir, name); |
37eca903 |
|
66ba1785 |
cli_rmdirs(odir);
cli_rmdirs(ndir); |
37eca903 |
free(odir);
free(ndir); |
6edef583 |
|
288057e9 |
if (ret == -1)
return -1; |
6edef583 |
|
288057e9 |
if (verifydiff(name, optget(opts, "diff")->strarg, NULL) == -1) {
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; |
6edef583 |
}
|
37eca903 |
return 0;
}
|
9bbe52b8 |
static int dumpcerts(const struct optstruct *opts)
{ |
288057e9 |
char *filename = NULL; |
9bbe52b8 |
STATBUF sb;
struct cl_engine *engine;
cli_ctx ctx; |
ee39ff06 |
struct cl_scan_options options; |
b9c3525b |
int fd;
cl_error_t ret;
|
9bbe52b8 |
logg_file = NULL;
filename = optget(opts, "print-certs")->strarg; |
288057e9 |
if (!filename) {
mprintf("!dumpcerts: No filename!\n"); |
9bbe52b8 |
return -1;
}
/* build engine */ |
288057e9 |
if (!(engine = cl_engine_new())) {
mprintf("!dumpcerts: Can't create new engine\n");
return -1; |
9bbe52b8 |
}
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
|
288057e9 |
if (cli_initroots(engine, 0) != CL_SUCCESS) {
mprintf("!dumpcerts: cli_initroots() failed\n");
cl_engine_free(engine);
return -1; |
9bbe52b8 |
}
|
288057e9 |
if (cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
mprintf("!dumpcerts: Can't parse signature\n");
cl_engine_free(engine);
return -1; |
9bbe52b8 |
}
|
288057e9 |
if (cl_engine_compile(engine) != CL_SUCCESS) {
mprintf("!dumpcerts: Can't compile engine\n");
cl_engine_free(engine);
return -1; |
9bbe52b8 |
}
|
5eaf0b32 |
cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1); |
9bbe52b8 |
cl_debug();
/* prepare context */
memset(&ctx, '\0', sizeof(cli_ctx)); |
ee39ff06 |
memset(&options, 0, sizeof(struct cl_scan_options)); |
288057e9 |
ctx.engine = engine;
ctx.options = &options; |
048a88e6 |
ctx.options->parse = ~0; |
288057e9 |
ctx.containers = cli_calloc(sizeof(cli_ctx_container), engine->maxreclevel + 2);
if (!ctx.containers) {
cl_engine_free(engine);
return -1; |
031fe00a |
}
ctx.containers[0].type = CL_TYPE_ANY; |
288057e9 |
ctx.dconf = (struct cli_dconf *)engine->dconf;
ctx.fmap = calloc(sizeof(fmap_t *), 1);
if (!ctx.fmap) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
cl_engine_free(engine);
return -1; |
9bbe52b8 |
}
/* Prepare file */
fd = open(filename, O_RDONLY); |
288057e9 |
if (fd < 0) {
mprintf("!dumpcerts: Can't open file %s!\n", filename); |
631cb6a0 |
free(ctx.containers); |
735d623d |
free(ctx.fmap); |
9bbe52b8 |
cl_engine_free(engine);
return -1;
}
lseek(fd, 0, SEEK_SET);
FSTAT(fd, &sb); |
288057e9 |
if (!(*ctx.fmap = fmap(fd, 0, sb.st_size))) { |
631cb6a0 |
free(ctx.containers); |
288057e9 |
free(ctx.fmap);
close(fd);
cl_engine_free(engine);
return -1; |
9bbe52b8 |
}
|
d92c0129 |
ret = cli_check_auth_header(&ctx, NULL); |
288057e9 |
switch (ret) { |
1d7f6b27 |
case CL_VERIFIED: |
9bbe52b8 |
case CL_VIRUS: |
1d7f6b27 |
// These shouldn't happen, since sigtool doesn't load in any sigs |
9bbe52b8 |
break; |
dd25061b |
case CL_EVERIFY: |
1d7f6b27 |
// The Authenticode header was parsed successfully but there were
// no applicable whitelist/blacklist rules
break;
case CL_BREAK:
mprintf("*dumpcerts: No Authenticode signature detected\n"); |
dd25061b |
break; |
9bbe52b8 |
case CL_EFORMAT: |
b9c3525b |
mprintf("!dumpcerts: An error occurred when parsing the file\n"); |
9bbe52b8 |
break;
default: |
1d7f6b27 |
mprintf("!dumpcerts: Other error %d inside cli_check_auth_header.\n", ret); |
9bbe52b8 |
break;
}
/* Cleanup */ |
631cb6a0 |
free(ctx.containers); |
735d623d |
funmap(*ctx.fmap); |
9bbe52b8 |
free(ctx.fmap);
close(fd);
cl_engine_free(engine);
return 0;
}
|
797c5b1e |
static void help(void) |
ee039e40 |
{
mprintf("\n"); |
e098cdc5 |
mprintf(" Clam AntiVirus: Signature Tool %s\n", get_version()); |
964a1e73 |
mprintf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); |
e1cbc270 |
mprintf(" (C) 2019 Cisco Systems, Inc.\n"); |
e098cdc5 |
mprintf("\n");
mprintf(" sigtool [options]\n");
mprintf("\n");
mprintf(" --help -h Show this 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"); |
edb23158 |
mprintf(" --stdout Write to stdout instead of stderr. Does not affect 'debug' messages.\n"); |
e098cdc5 |
mprintf(" --hex-dump Convert data from stdin to a hex\n"); |
ee039e40 |
mprintf(" string and print it on stdout\n"); |
e098cdc5 |
mprintf(" --md5 [FILES] Generate MD5 checksum from stdin\n"); |
bcf3dc79 |
mprintf(" or MD5 sigs for FILES\n"); |
e098cdc5 |
mprintf(" --sha1 [FILES] Generate SHA1 checksum from stdin\n"); |
d5fde2eb |
mprintf(" or SHA1 sigs for FILES\n"); |
e098cdc5 |
mprintf(" --sha256 [FILES] Generate SHA256 checksum from stdin\n"); |
d5fde2eb |
mprintf(" or SHA256 sigs for FILES\n"); |
e098cdc5 |
mprintf(" --mdb [FILES] Generate .mdb (section hash) sigs\n");
mprintf(" --imp [FILES] Generate .imp (import table hash) sigs\n");
mprintf(" --html-normalise=FILE Create normalised parts of HTML file\n");
mprintf(" --ascii-normalise=FILE Create normalised text file from ascii source\n");
mprintf(" --utf16-decode=FILE Decode UTF16 encoded files\n");
mprintf(" --info=FILE -i FILE Print database information\n");
mprintf(" --build=NAME [cvd] -b NAME Build a CVD file\n"); |
f8d05d6d |
mprintf(" --max-bad-sigs=NUMBER Maximum number of mismatched signatures\n"); |
e098cdc5 |
mprintf(" When building a CVD. Default: 3000\n"); |
f8d05d6d |
mprintf(" --flevel=FLEVEL Specify a custom flevel.\n");
mprintf(" Default: %u\n", cl_retflevel());
mprintf(" --cvd-version=NUMBER Specify the version number to use for\n");
mprintf(" the build. Default is to use the value+1\n");
mprintf(" from the current CVD in --datadir.\n");
mprintf(" If no datafile is found the default\n");
mprintf(" behaviour is to prompt for a version\n");
mprintf(" number, this switch will prevent the\n");
mprintf(" prompt. NOTE: If a CVD is found in the\n");
mprintf(" --datadir its version+1 is used and\n");
mprintf(" this value is ignored.\n"); |
03527bee |
mprintf(" --no-cdiff Don't generate .cdiff file\n"); |
cdddd014 |
mprintf(" --unsigned Create unsigned database file (.cud)\n"); |
9c30a4fc |
mprintf(" --hybrid Create a hybrid (standard and bytecode) database file\n"); |
9bbe52b8 |
mprintf(" --print-certs=FILE Print Authenticode details from a PE\n"); |
ee039e40 |
mprintf(" --server=ADDR ClamAV Signing Service address\n"); |
3be7f9a9 |
mprintf(" --datadir=DIR Use DIR as default database directory\n"); |
e18f93ee |
mprintf(" --unpack=FILE -u FILE Unpack a CVD/CLD file\n");
mprintf(" --unpack-current=SHORTNAME Unpack local CVD/CLD into cwd\n"); |
ee039e40 |
mprintf(" --list-sigs[=FILE] -l[FILE] List signature names\n"); |
b4561aa2 |
mprintf(" --find-sigs=REGEX -fREGEX Find signatures matching REGEX\n"); |
7e8402ac |
mprintf(" --decode-sigs Decode signatures from stdin\n"); |
f8d05d6d |
mprintf(" --test-sigs=DATABASE TARGET_FILE Test signatures from DATABASE against \n");
mprintf(" TARGET_FILE\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"); |
f8d05d6d |
mprintf(" --compare=OLD NEW -c OLD NEW Show diff between OLD and NEW files in\n");
mprintf(" cdiff format\n"); |
bb990ecd |
mprintf(" --run-cdiff=FILE -r FILE Execute update script FILE in cwd\n"); |
e18f93ee |
mprintf(" --verify-cdiff=DIFF CVD/CLD Verify DIFF against CVD/CLD\n"); |
ee039e40 |
mprintf("\n");
|
3d167464 |
return;
}
int main(int argc, char **argv)
{ |
288057e9 |
int ret;
struct optstruct *opts;
STATBUF sb; |
d6af38e7 |
|
288057e9 |
if (check_flevel())
exit(1); |
bca0b679 |
|
288057e9 |
if ((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
mprintf("!Can't initialize libclamav: %s\n", cl_strerror(ret));
return -1; |
ffa9b060 |
}
ret = 1;
|
d6af38e7 |
opts = optparse(NULL, argc, argv, 1, OPT_SIGTOOL, 0, NULL); |
288057e9 |
if (!opts) {
mprintf("!Can't parse command line options\n");
return 1;
}
if (optget(opts, "quiet")->enabled)
mprintf_quiet = 1;
if (optget(opts, "stdout")->enabled)
mprintf_stdout = 1;
if (optget(opts, "debug")->enabled)
cl_debug();
if (optget(opts, "version")->enabled) {
print_version(NULL);
optfree(opts);
return 0;
}
if (optget(opts, "help")->enabled) {
optfree(opts);
help();
return 0;
}
if (optget(opts, "hex-dump")->enabled)
ret = hexdump();
else if (optget(opts, "md5")->enabled)
ret = hashsig(opts, 0, 1);
else if (optget(opts, "sha1")->enabled)
ret = hashsig(opts, 0, 2);
else if (optget(opts, "sha256")->enabled)
ret = hashsig(opts, 0, 3);
else if (optget(opts, "mdb")->enabled)
ret = hashsig(opts, 1, 1);
else if (optget(opts, "imp")->enabled)
ret = hashsig(opts, 2, 1);
else if (optget(opts, "html-normalise")->enabled)
ret = htmlnorm(opts);
else if (optget(opts, "ascii-normalise")->enabled)
ret = asciinorm(opts);
else if (optget(opts, "utf16-decode")->enabled)
ret = utf16decode(opts);
else if (optget(opts, "build")->enabled)
ret = build(opts);
else if (optget(opts, "unpack")->enabled)
ret = unpack(opts);
else if (optget(opts, "unpack-current")->enabled)
ret = unpack(opts);
else if (optget(opts, "info")->enabled)
ret = cvdinfo(opts);
else if (optget(opts, "list-sigs")->active)
ret = listsigs(opts, 0);
else if (optget(opts, "find-sigs")->active)
ret = listsigs(opts, 1);
else if (optget(opts, "decode-sigs")->active)
ret = decodesigs();
else if (optget(opts, "test-sigs")->enabled)
ret = testsigs(opts);
else if (optget(opts, "vba")->enabled || optget(opts, "vba-hex")->enabled)
ret = vbadump(opts);
else if (optget(opts, "diff")->enabled)
ret = makediff(opts);
else if (optget(opts, "compare")->enabled)
ret = compareone(opts);
else if (optget(opts, "print-certs")->enabled)
ret = dumpcerts(opts);
else if (optget(opts, "run-cdiff")->enabled)
ret = rundiff(opts);
else if (optget(opts, "verify-cdiff")->enabled) {
if (!opts->filename) {
mprintf("!--verify-cdiff requires two arguments\n");
ret = -1;
} else {
if (CLAMSTAT(opts->filename[0], &sb) == -1) {
mprintf("--verify-cdiff: Can't get status of %s\n", opts->filename[0]);
ret = -1;
} else {
if (S_ISDIR(sb.st_mode))
ret = verifydiff(optget(opts, "verify-cdiff")->strarg, NULL, opts->filename[0]);
else
ret = verifydiff(optget(opts, "verify-cdiff")->strarg, opts->filename[0], NULL);
}
} |
6edef583 |
} else |
288057e9 |
help(); |
3d167464 |
|
d6af38e7 |
optfree(opts); |
694e7882 |
|
3d167464 |
return ret ? 1 : 0; |
0a2ad257 |
} |