2
* Copyright (C) 2014 Cisco Systems, Inc.
3
* Copyright (C) 2007 - 2013 Sourcefire, Inc.
4
* Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net>
5
* CDIFF code (C) 2006 Sensory Networks, Inc.
6
* Author: Tomasz Kojm <tkojm@clamav.net>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2 as
10
* published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25
#include "clamav-config.h"
37
#include <sys/types.h>
41
#include <sys/socket.h>
43
#include <netinet/in.h>
44
#include <arpa/inet.h>
57
#include "shared/output.h"
58
#include "shared/optparser.h"
59
#include "shared/misc.h"
60
#include "shared/cdiff.h"
61
#include "shared/tar.h"
63
#include "libclamav/clamav.h"
64
#include "libclamav/matcher.h"
65
#include "libclamav/cvd.h"
66
#include "libclamav/str.h"
67
#include "libclamav/ole2_extract.h"
68
#include "libclamav/htmlnorm.h"
69
#include "libclamav/default.h"
70
#include "libclamav/fmap.h"
71
#include "libclamav/readdb.h"
72
#include "libclamav/others.h"
73
#include "libclamav/pe.h"
75
#define MAX_DEL_LOOKAHEAD 5000
78
short recursion = 0, bell = 0;
79
short printinfected = 0, printclean = 1;
81
static const struct dblist_s {
121
static char *getdbname(const char *str, char *dst, int dstlen)
123
int len = strlen(str);
125
if(cli_strbcasestr(str, ".cvd") || cli_strbcasestr(str, ".cld") || cli_strbcasestr(str, ".cud"))
129
strncpy(dst, str, MIN(dstlen - 1, len));
130
dst[MIN(dstlen - 1, len)] = 0;
132
dst = (char *) malloc(len + 1);
135
strncpy(dst, str, len - 4);
136
dst[MIN(dstlen - 1, len - 4)] = 0;
141
static int hexdump(void)
143
char buffer[FILEBUFF], *pt;
147
while((bytes = read(0, buffer, FILEBUFF)) > 0) {
148
pt = cli_str2hex(buffer, bytes);
149
if(write(1, pt, 2 * bytes) == -1) {
150
mprintf("!hexdump: Can't write to stdout\n");
163
static int hashsig(const struct optstruct *opts, unsigned int mdb, int type)
171
for(i = 0; opts->filename[i]; i++) {
172
if(CLAMSTAT(opts->filename[i], &sb) == -1) {
174
mprintf("!hashsig: Can't access file %s\n", opts->filename[i]);
177
if((sb.st_mode & S_IFMT) == S_IFREG) {
178
if((hash = cli_hashfile(opts->filename[i], type))) {
180
mprintf("%u:%s:%s\n", (unsigned int) sb.st_size, hash, basename(opts->filename[i]));
182
mprintf("%s:%u:%s\n", hash, (unsigned int) sb.st_size, basename(opts->filename[i]));
185
mprintf("!hashsig: Can't generate hash for %s\n", opts->filename[i]);
192
} else { /* stream */
193
hash = cli_hashstream(stdin, NULL, type);
195
mprintf("!hashsig: Can't generate hash for input stream\n");
198
mprintf("%s\n", hash);
205
static int htmlnorm(const struct optstruct *opts)
210
if((fd = open(optget(opts, "html-normalise")->strarg, O_RDONLY)) == -1) {
211
mprintf("!htmlnorm: Can't open file %s\n", optget(opts, "html-normalise")->strarg);
215
if((map = fmap(fd, 0, 0))) {
216
html_normalise_map(map, ".", NULL, NULL);
219
mprintf("!fmap failed\n");
226
static int utf16decode(const struct optstruct *opts)
229
char *newname, buff[512], *decoded;
233
fname = optget(opts, "utf16-decode")->strarg;
234
if((fd1 = open(fname, O_RDONLY)) == -1) {
235
mprintf("!utf16decode: Can't open file %s\n", fname);
239
newname = malloc(strlen(fname) + 7);
241
mprintf("!utf16decode: Can't allocate memory\n");
245
sprintf(newname, "%s.ascii", fname);
247
if((fd2 = open(newname, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) {
248
mprintf("!utf16decode: Can't create file %s\n", newname);
254
while((bytes = read(fd1, buff, sizeof(buff))) > 0) {
255
decoded = cli_utf16toascii(buff, bytes);
257
if(write(fd2, decoded, strlen(decoded)) == -1) {
258
mprintf("!utf16decode: Can't write to file %s\n", newname);
277
static char *getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode)
279
char buff[512], cmd[128], pass[30], *pt;
280
struct sockaddr_in server;
281
int sockd, bread, len;
282
#ifdef HAVE_TERMIOS_H
283
struct termios old, new;
286
memset(&server, 0x00, sizeof(struct sockaddr_in));
288
if((pt = getenv("SIGNDPASS"))) {
289
strncpy(pass, pt, sizeof(pass));
290
pass[sizeof(pass)-1]='\0';
292
mprintf("Password: ");
294
#ifdef HAVE_TERMIOS_H
295
if(tcgetattr(0, &old)) {
296
mprintf("!getdsig: tcgetattr() failed\n");
300
new.c_lflag &= ~ECHO;
301
if(tcsetattr(0, TCSAFLUSH, &new)) {
302
mprintf("!getdsig: tcsetattr() failed\n");
306
if(scanf("%30s", pass) == EOF || !pt) {
307
mprintf("!getdsig: Can't get password\n");
308
#ifdef HAVE_TERMIOS_H
309
tcsetattr(0, TCSAFLUSH, &old);
314
#ifdef HAVE_TERMIOS_H
315
if(tcsetattr(0, TCSAFLUSH, &old)) {
316
mprintf("!getdsig: tcsetattr() failed\n");
317
memset(pass, 0, sizeof(pass));
324
if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
326
mprintf("!getdsig: Can't create socket\n");
327
memset(pass, 0, sizeof(pass));
331
server.sin_family = AF_INET;
332
server.sin_addr.s_addr = inet_addr(host);
333
server.sin_port = htons(33101);
335
if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0) {
338
mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host);
339
memset(pass, 0, sizeof(pass));
342
memset(cmd, 0, sizeof(cmd));
345
snprintf(cmd, sizeof(cmd) - datalen, "ClamSign:%s:%s:", user, pass);
347
snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS:%s:%s:", user, pass);
349
snprintf(cmd, sizeof(cmd) - datalen, "ClamSignPSS2:%s:%s:", user, pass);
353
memcpy(pt, data, datalen);
356
if(send(sockd, cmd, len, 0) < 0) {
357
mprintf("!getdsig: Can't write to socket\n");
359
memset(cmd, 0, sizeof(cmd));
360
memset(pass, 0, sizeof(pass));
364
memset(cmd, 0, sizeof(cmd));
365
memset(pass, 0, sizeof(pass));
366
memset(buff, 0, sizeof(buff));
368
if((bread = recv(sockd, buff, sizeof(buff)-1, 0)) > 0) {
370
if(!strstr(buff, "Signature:")) {
371
mprintf("!getdsig: Error generating digital signature\n");
372
mprintf("!getdsig: Answer from remote server: %s\n", buff);
376
mprintf("Signature received (length = %lu)\n", (unsigned long)strlen(buff) - 10);
379
mprintf("!getdsig: Communication error with remote server\n");
391
static char *sha256file(const char *file, unsigned int *size)
394
unsigned int i, bytes;
395
unsigned char digest[32], buffer[FILEBUFF];
399
ctx = cl_hash_init("sha256");
403
if(!(fh = fopen(file, "rb"))) {
404
mprintf("!sha256file: Can't open file %s\n", file);
405
cl_hash_destroy(ctx);
410
while((bytes = fread(buffer, 1, sizeof(buffer), fh))) {
411
cl_update_hash(ctx, buffer, bytes);
415
cl_finish_hash(ctx, digest);
416
sha = (char *) malloc(65);
422
for(i = 0; i < 32; i++)
423
sprintf(sha + i * 2, "%02x", digest[i]);
429
static int writeinfo(const char *dbname, const char *builder, const char *header, const struct optstruct *opts, char * const *dblist2, unsigned int dblist2cnt)
432
unsigned int i, bytes;
433
char file[32], *pt, dbfile[32];
434
unsigned char digest[32], buffer[FILEBUFF];
437
snprintf(file, sizeof(file), "%s.info", dbname);
438
if(!access(file, R_OK)) {
439
if(unlink(file) == -1) {
440
mprintf("!writeinfo: Can't unlink %s\n", file);
445
if(!(fh = fopen(file, "wb+"))) {
446
mprintf("!writeinfo: Can't create file %s\n", file);
450
if(fprintf(fh, "%s\n", header) < 0) {
451
mprintf("!writeinfo: Can't write to %s\n", file);
457
for(i = 0; i < dblist2cnt; i++) {
458
if(!(pt = sha256file(dblist2[i], &bytes))) {
459
mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
463
if(fprintf(fh, "%s:%u:%s\n", dblist2[i], bytes, pt) < 0) {
464
mprintf("!writeinfo: Can't write to info file\n");
472
for(i = 0; dblist[i].ext; i++) {
473
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
474
if(strcmp(dblist[i].ext, "info") && !access(dbfile, R_OK)) {
475
if(!(pt = sha256file(dbfile, &bytes))) {
476
mprintf("!writeinfo: Can't generate SHA256 for %s\n", file);
480
if(fprintf(fh, "%s:%u:%s\n", dbfile, bytes, pt) < 0) {
481
mprintf("!writeinfo: Can't write to info file\n");
490
if(!optget(opts, "unsigned")->enabled) {
492
ctx = cl_hash_init("sha256");
498
while((bytes = fread(buffer, 1, sizeof(buffer), fh)))
499
cl_update_hash(ctx, buffer, bytes);
500
cl_finish_hash(ctx, digest);
501
if(!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 3))) {
502
mprintf("!writeinfo: Can't get digital signature from remote server\n");
506
fprintf(fh, "DSIG:%s\n", pt);
513
static int diffdirs(const char *old, const char *new, const char *patch);
514
static int verifydiff(const char *diff, const char *cvd, const char *incdir);
516
static int script2cdiff(const char *script, const char *builder, const struct optstruct *opts)
518
char *cdiff, *pt, buffer[FILEBUFF];
519
unsigned char digest[32];
522
FILE *scripth, *cdiffh;
524
unsigned int ver, osize;
528
if(CLAMSTAT(script, &sb) == -1) {
529
mprintf("!script2diff: Can't stat file %s\n", script);
532
osize = (unsigned int) sb.st_size;
534
cdiff = strdup(script);
535
pt = strstr(cdiff, ".script");
537
mprintf("!script2cdiff: Incorrect file name (no .script extension)\n");
541
strcpy(pt, ".cdiff");
543
if(!(pt = strchr(script, '-'))) {
544
mprintf("!script2cdiff: Incorrect file name syntax\n");
549
if(sscanf(++pt, "%u.script", &ver) == EOF) {
550
mprintf("!script2cdiff: Incorrect file name syntax\n");
555
if(!(cdiffh = fopen(cdiff, "wb"))) {
556
mprintf("!script2cdiff: Can't open %s for writing\n", cdiff);
561
if(fprintf(cdiffh, "ClamAV-Diff:%u:%u:", ver, osize) < 0) {
562
mprintf("!script2cdiff: Can't write to %s\n", cdiff);
569
if(!(scripth = fopen(script, "rb"))) {
570
mprintf("!script2cdiff: Can't open file %s for reading\n", script);
576
if(!(gzh = gzopen(cdiff, "ab9f"))) {
577
mprintf("!script2cdiff: Can't open file %s for appending\n", cdiff);
584
while((bytes = fread(buffer, 1, sizeof(buffer), scripth)) > 0) {
585
if(!gzwrite(gzh, buffer, bytes)) {
586
mprintf("!script2cdiff: Can't gzwrite to %s\n", cdiff);
597
if(!(cdiffh = fopen(cdiff, "rb"))) {
598
mprintf("!script2cdiff: Can't open %s for reading/writing\n", cdiff);
604
ctx = cl_hash_init("sha256");
612
while((bytes = fread(buffer, 1, sizeof(buffer), cdiffh)))
613
cl_update_hash(ctx, (unsigned char *) buffer, bytes);
616
cl_finish_hash(ctx, digest);
618
if(!(pt = getdsig(optget(opts, "server")->strarg, builder, digest, 32, 2))) {
619
mprintf("!script2cdiff: Can't get digital signature from remote server\n");
625
if(!(cdiffh = fopen(cdiff, "ab"))) {
626
mprintf("!script2cdiff: Can't open %s for appending\n", cdiff);
631
fprintf(cdiffh, ":%s", pt);
635
mprintf("Created %s\n", cdiff);
641
static int qcompare(const void *a, const void *b)
643
return strcmp(*(char * const *) a, *(char * const *) b);
646
static int build(const struct optstruct *opts)
650
unsigned int i, sigs = 0, oldsigs = 0, entries = 0, version, real_header, fl, maxentries;
652
unsigned char buffer[FILEBUFF];
653
char *tarfile, header[513], smbuff[32], builder[32], *pt, olddb[512];
654
char patch[32], broken[32], dbname[32], dbfile[32];
655
const char *newcvd, *localdbdir = NULL;
656
struct cl_engine *engine;
661
struct cl_cvd *oldcvd;
662
char **dblist2 = NULL;
663
unsigned int dblist2cnt = 0;
668
for(i = 0; i < dblist2cnt; i++) \
672
if(!optget(opts, "server")->enabled && !optget(opts, "unsigned")->enabled) {
673
mprintf("!build: --server is required for --build\n");
677
if(optget(opts, "datadir")->active)
678
localdbdir = optget(opts, "datadir")->strarg;
680
if(CLAMSTAT("COPYING", &foo) == -1) {
681
mprintf("!build: COPYING file not found in current working directory.\n");
685
getdbname(optget(opts, "build")->strarg, dbname, sizeof(dbname));
686
if(!strcmp(dbname, "bytecode"))
689
if(!(engine = cl_engine_new())) {
690
mprintf("!build: Can't initialize antivirus engine\n");
694
if((ret = cl_load(".", engine, &sigs, CL_DB_STDOPT | CL_DB_PUA | CL_DB_SIGNED))) {
695
mprintf("!build: Can't load database: %s\n", cl_strerror(ret));
696
cl_engine_free(engine);
699
cl_engine_free(engine);
702
mprintf("!build: There are no signatures in database files\n");
705
if((dd = opendir(".")) == NULL) {
706
mprintf("!build: Can't open current directory\n");
709
while((dent = readdir(dd))) {
711
if(cli_strbcasestr(dent->d_name, ".cbc")) {
712
dblist2 = (char **) realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
713
if(!dblist2) { /* dblist2 leaked but we don't really care */
714
mprintf("!build: Memory allocation error\n");
718
dblist2[dblist2cnt] = strdup(dent->d_name);
719
if(!dblist2[dblist2cnt]) {
721
mprintf("!build: Memory allocation error\n");
729
entries += dblist2cnt;
730
if(dblist2 != NULL) {
731
qsort(dblist2, dblist2cnt, sizeof(char *), qcompare);
734
if(!access("last.hdb", R_OK)) {
736
mprintf("!build: dblist2 == NULL (no .cbc files?)\n");
739
dblist2 = (char **) realloc(dblist2, (dblist2cnt + 1) * sizeof(char *));
741
mprintf("!build: Memory allocation error\n");
744
dblist2[dblist2cnt] = strdup("last.hdb");
745
if(!dblist2[dblist2cnt]) {
747
mprintf("!build: Memory allocation error\n");
751
entries += countlines("last.hdb");
754
for(i = 0; dblist[i].ext; i++) {
755
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
756
if(dblist[i].count && !access(dbfile, R_OK))
757
entries += countlines(dbfile);
762
mprintf("^build: Signatures in %s db files: %u, loaded by libclamav: %u\n", dbname, entries, sigs);
764
maxentries = optget(opts, "max-bad-sigs")->numarg;
767
if(!entries || (sigs > entries && sigs - entries >= maxentries)) {
768
mprintf("!Bad number of signatures in database files\n");
775
/* try to read cvd header of current database */
777
if(cli_strbcasestr(opts->filename[0], ".cvd") || cli_strbcasestr(opts->filename[0], ".cld") || cli_strbcasestr(opts->filename[0], ".cud")) {
778
strncpy(olddb, opts->filename[0], sizeof(olddb));
779
olddb[sizeof(olddb)-1]='\0';
781
mprintf("!build: Not a CVD/CLD/CUD file\n");
788
snprintf(olddb, sizeof(olddb), "%s"PATHSEP"%s.cvd", localdbdir ? localdbdir : pt, dbname);
789
if(access(olddb, R_OK))
790
snprintf(olddb, sizeof(olddb), "%s"PATHSEP"%s.cld", localdbdir ? localdbdir : pt, dbname);
791
if(access(olddb, R_OK))
792
snprintf(olddb, sizeof(olddb), "%s"PATHSEP"%s.cud", localdbdir ? localdbdir : pt, dbname);
796
if(!(oldcvd = cl_cvdhead(olddb)) && !optget(opts, "unsigned")->enabled) {
797
mprintf("^build: CAN'T READ CVD HEADER OF CURRENT DATABASE %s (wait 3 s)\n", olddb);
802
version = oldcvd->version + 1;
803
oldsigs = oldcvd->sigs;
805
} else if (optget(opts, "cvd-version")->numarg != 0) {
806
version = optget(opts, "cvd-version")->numarg;
808
mprintf("Version number: ");
809
if(scanf("%u", &version) == EOF) {
810
mprintf("!build: scanf() failed\n");
816
mprintf("Total sigs: %u\n", sigs);
818
mprintf("New sigs: %u\n", sigs - oldsigs);
820
strcpy(header, "ClamAV-VDB:");
824
brokent = localtime(&timet);
825
setlocale(LC_TIME, "C");
826
strftime(smbuff, sizeof(smbuff), "%d %b %Y %H-%M %z", brokent);
827
strcat(header, smbuff);
830
sprintf(header + strlen(header), ":%u:", version);
832
/* number of signatures */
833
sprintf(header + strlen(header), "%u:", sigs);
835
/* functionality level */
836
fl = (unsigned int)(optget(opts, "flevel")->numarg);
837
sprintf(header + strlen(header), "%u:", fl);
839
real_header = strlen(header);
841
/* add fake MD5 and dsig (for writeinfo) */
842
strcat(header, "X:X:");
844
if((pt = getenv("SIGNDUSER"))) {
845
strncpy(builder, pt, sizeof(builder));
846
builder[sizeof(builder)-1]='\0';
848
mprintf("Builder name: ");
849
if(scanf("%32s", builder) == EOF || !pt) {
850
mprintf("!build: Can't get builder name\n");
857
strcat(header, builder);
859
/* add current time */
860
sprintf(header + strlen(header), ":%u", (unsigned int) timet);
862
if(writeinfo(dbname, builder, header, opts, dblist2, dblist2cnt) == -1) {
863
mprintf("!build: Can't generate info file\n");
868
header[real_header] = 0;
870
if(!(tarfile = cli_gentemp("."))) {
871
mprintf("!build: Can't generate temporary name for tarfile\n");
876
if((tar = gzopen(tarfile, "wb9f")) == NULL) {
877
mprintf("!build: Can't open file %s for writing\n", tarfile);
883
if(tar_addfile(-1, tar, "COPYING") == -1) {
884
mprintf("!build: Can't add COPYING to tar archive\n");
893
if(tar_addfile(-1, tar, "bytecode.info") == -1) {
900
for(i = 0; i < dblist2cnt; i++) {
901
if(tar_addfile(-1, tar, dblist2[i]) == -1) {
910
for(i = 0; dblist[i].ext; i++) {
911
snprintf(dbfile, sizeof(dbfile), "%s.%s", dbname, dblist[i].ext);
912
if(!access(dbfile, R_OK)) {
913
if(tar_addfile(-1, tar, dbfile) == -1) {
927
if(!(fh = fopen(tarfile, "rb"))) {
928
mprintf("!build: Can't open file %s for reading\n", tarfile);
934
if(!(pt = cli_hashstream(fh, buffer, 1))) {
935
mprintf("!build: Can't generate MD5 checksum for %s\n", tarfile);
942
sprintf(header + strlen(header), "%s:", pt);
945
if(!optget(opts, "unsigned")->enabled) {
946
if(!(pt = getdsig(optget(opts, "server")->strarg, builder, buffer, 16, 1))) {
947
mprintf("!build: Can't get digital signature from remote server\n");
953
sprintf(header + strlen(header), "%s:", pt);
956
sprintf(header + strlen(header), "X:");
960
strcat(header, builder);
962
/* add current time */
963
sprintf(header + strlen(header), ":%u", (unsigned int) timet);
965
/* fill up with spaces */
966
while(strlen(header) < sizeof(header) - 1)
969
/* build the final database */
970
newcvd = optget(opts, "build")->strarg;
971
if(!(cvd = fopen(newcvd, "wb"))) {
972
mprintf("!build: Can't create final database %s\n", newcvd);
979
if(fwrite(header, 1, 512, cvd) != 512) {
980
mprintf("!build: Can't write to %s\n", newcvd);
989
while((bytes = fread(buffer, 1, FILEBUFF, fh)) > 0) {
990
if(fwrite(buffer, 1, bytes, cvd) != bytes) {
991
mprintf("!build: Can't write to %s\n", newcvd);
1004
if(unlink(tarfile) == -1) {
1005
mprintf("^build: Can't unlink %s\n", tarfile);
1013
mprintf("Created %s\n", newcvd);
1015
if(optget(opts, "unsigned")->enabled)
1018
if(!oldcvd || optget(opts, "no-cdiff")->enabled) {
1019
mprintf("Skipping .cdiff creation\n");
1023
/* generate patch */
1024
if(!(pt = cli_gentemp(NULL))) {
1025
mprintf("!build: Can't generate temporary name\n");
1030
if(mkdir(pt, 0700)) {
1031
mprintf("!build: Can't create temporary directory %s\n", pt);
1037
if(cli_cvdunpack(olddb, pt) == -1) {
1038
mprintf("!build: Can't unpack CVD file %s\n", olddb);
1044
strncpy(olddb, pt, sizeof(olddb));
1045
olddb[sizeof(olddb)-1]='\0';
1048
if(!(pt = cli_gentemp(NULL))) {
1049
mprintf("!build: Can't generate temporary name\n");
1055
if(mkdir(pt, 0700)) {
1056
mprintf("!build: Can't create temporary directory %s\n", pt);
1063
if(cli_cvdunpack(newcvd, pt) == -1) {
1064
mprintf("!build: Can't unpack CVD file %s\n", newcvd);
1072
snprintf(patch, sizeof(patch), "%s-%u.script", dbname, version);
1073
ret = diffdirs(olddb, pt, patch);
1084
ret = verifydiff(patch, NULL, olddb);
1088
snprintf(broken, sizeof(broken), "%s.broken", patch);
1089
if(rename(patch, broken)) {
1091
mprintf("!Generated file is incorrect, removed");
1093
mprintf("!Generated file is incorrect, renamed to %s\n", broken);
1096
ret = script2cdiff(patch, builder, opts);
1102
static int unpack(const struct optstruct *opts)
1104
char name[512], *dbdir;
1105
const char *localdbdir = NULL;
1107
if(optget(opts, "datadir")->active)
1108
localdbdir = optget(opts, "datadir")->strarg;
1110
if(optget(opts, "unpack-current")->enabled) {
1111
dbdir = freshdbdir();
1112
snprintf(name, sizeof(name), "%s"PATHSEP"%s.cvd", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
1113
if(access(name, R_OK)) {
1114
snprintf(name, sizeof(name), "%s"PATHSEP"%s.cld", localdbdir ? localdbdir : dbdir, optget(opts, "unpack-current")->strarg);
1115
if(access(name, R_OK)) {
1116
mprintf("!unpack: Couldn't find %s CLD/CVD database in %s\n", optget(opts, "unpack-current")->strarg, localdbdir ? localdbdir : dbdir);
1124
strncpy(name, optget(opts, "unpack")->strarg, sizeof(name));
1125
name[sizeof(name)-1]='\0';
1128
if (cl_cvdverify(name) != CL_SUCCESS) {
1129
mprintf("!unpack: %s is not a valid CVD\n", name);
1133
if(cli_cvdunpack(name, ".") == -1) {
1134
mprintf("!unpack: Can't unpack file %s\n", name);
1141
static int cvdinfo(const struct optstruct *opts)
1148
pt = optget(opts, "info")->strarg;
1149
if((cvd = cl_cvdhead(pt)) == NULL) {
1150
mprintf("!cvdinfo: Can't read/parse CVD header of %s\n", pt);
1153
mprintf("File: %s\n", pt);
1155
pt = strchr(cvd->time, '-');
1161
mprintf("Build time: %s\n", cvd->time);
1162
mprintf("Version: %u\n", cvd->version);
1163
mprintf("Signatures: %u\n", cvd->sigs);
1164
mprintf("Functionality level: %u\n", cvd->fl);
1165
mprintf("Builder: %s\n", cvd->builder);
1167
pt = optget(opts, "info")->strarg;
1168
if(cli_strbcasestr(pt, ".cvd")) {
1169
mprintf("MD5: %s\n", cvd->md5);
1170
mprintf("Digital signature: %s\n", cvd->dsig);
1173
if(cli_strbcasestr(pt, ".cud"))
1174
mprintf("Verification: Unsigned container\n");
1175
else if((ret = cl_cvdverify(pt))) {
1176
mprintf("!cvdinfo: Verification: %s\n", cl_strerror(ret));
1179
mprintf("Verification OK.\n");
1184
static int listdb(const char *filename, const regex_t *regex);
1186
static int listdir(const char *dirname, const regex_t *regex)
1189
struct dirent *dent;
1192
if((dd = opendir(dirname)) == NULL) {
1193
mprintf("!listdir: Can't open directory %s\n", dirname);
1197
while((dent = readdir(dd))) {
1200
if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
1201
(cli_strbcasestr(dent->d_name, ".db") ||
1202
cli_strbcasestr(dent->d_name, ".hdb") ||
1203
cli_strbcasestr(dent->d_name, ".hdu") ||
1204
cli_strbcasestr(dent->d_name, ".hsb") ||
1205
cli_strbcasestr(dent->d_name, ".hsu") ||
1206
cli_strbcasestr(dent->d_name, ".mdb") ||
1207
cli_strbcasestr(dent->d_name, ".mdu") ||
1208
cli_strbcasestr(dent->d_name, ".msb") ||
1209
cli_strbcasestr(dent->d_name, ".msu") ||
1210
cli_strbcasestr(dent->d_name, ".ndb") ||
1211
cli_strbcasestr(dent->d_name, ".ndu") ||
1212
cli_strbcasestr(dent->d_name, ".ldb") ||
1213
cli_strbcasestr(dent->d_name, ".ldu") ||
1214
cli_strbcasestr(dent->d_name, ".sdb") ||
1215
cli_strbcasestr(dent->d_name, ".zmd") ||
1216
cli_strbcasestr(dent->d_name, ".rmd") ||
1217
cli_strbcasestr(dent->d_name, ".cdb") ||
1218
cli_strbcasestr(dent->d_name, ".cbc") ||
1219
cli_strbcasestr(dent->d_name, ".cld") ||
1220
cli_strbcasestr(dent->d_name, ".cvd") ||
1221
cli_strbcasestr(dent->d_name, ".crb"))) {
1223
dbfile = (char *) malloc(strlen(dent->d_name) + strlen(dirname) + 2);
1225
mprintf("!listdir: Can't allocate memory for dbfile\n");
1229
sprintf(dbfile, "%s"PATHSEP"%s", dirname, dent->d_name);
1231
if(listdb(dbfile, regex) == -1) {
1232
mprintf("!listdb: Error listing database %s\n", dbfile);
1246
static int listdb(const char *filename, const regex_t *regex)
1249
char *buffer, *pt, *start, *dir;
1250
const char *dbname, *pathsep = PATHSEP;
1251
unsigned int line = 0;
1254
if((fh = fopen(filename, "rb")) == NULL) {
1255
mprintf("!listdb: Can't open file %s\n", filename);
1259
if(!(buffer = (char *) malloc(FILEBUFF))) {
1260
mprintf("!listdb: Can't allocate memory for buffer\n");
1265
/* check for CVD file */
1266
if(!fgets(buffer, 12, fh)) {
1267
mprintf("!listdb: fgets failed\n");
1274
if(!strncmp(buffer, "ClamAV-VDB:", 11)) {
1278
if(!(dir = cli_gentemp(NULL))) {
1279
mprintf("!listdb: Can't generate temporary name\n");
1283
if(mkdir(dir, 0700)) {
1284
mprintf("!listdb: Can't create temporary directory %s\n", dir);
1289
if(cli_cvdunpack(filename, dir) == -1) {
1290
mprintf("!listdb: Can't unpack CVD file %s\n", filename);
1296
/* list extracted directory */
1297
if(listdir(dir, regex) == -1) {
1298
mprintf("!listdb: Can't list directory %s\n", filename);
1310
if(!(dbname = strrchr(filename, *pathsep))) {
1311
mprintf("!listdb: Invalid filename %s\n", filename);
1318
if(cli_strbcasestr(filename, ".db")) { /* old style database */
1320
while(fgets(buffer, FILEBUFF, fh)) {
1323
if(!cli_regexec(regex, buffer, 0, NULL, 0))
1324
mprintf("[%s] %s\n", dbname, buffer);
1328
pt = strchr(buffer, '=');
1330
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1339
if((pt = strstr(start, " (Clam)")))
1342
mprintf("%s\n", start);
1345
} else if (cli_strbcasestr(filename, ".crb")) {
1346
while (fgets(buffer, FILEBUFF, fh)) {
1349
if (buffer[0] == '#')
1353
if (!cli_regexec(regex, buffer, 0 , NULL, 0))
1354
mprintf("[%s] %s\n", dbname, buffer);
1359
mprintf("%s\n", buffer);
1361
} 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")) { /* hash database */
1363
while(fgets(buffer, FILEBUFF, fh)) {
1366
if(!cli_regexec(regex, buffer, 0, NULL, 0))
1367
mprintf("[%s] %s\n", dbname, buffer);
1371
start = cli_strtok(buffer, 2, ":");
1374
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1380
if((pt = strstr(start, " (Clam)")))
1383
mprintf("%s\n", start);
1387
} 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")) {
1389
while(fgets(buffer, FILEBUFF, fh)) {
1392
if(!cli_regexec(regex, buffer, 0, NULL, 0))
1393
mprintf("[%s] %s\n", dbname, buffer);
1398
if(cli_strbcasestr(filename, ".ldb") || cli_strbcasestr(filename, ".ldu"))
1399
pt = strchr(buffer, ';');
1401
pt = strchr(buffer, ':');
1404
mprintf("!listdb: Malformed pattern line %u (file %s)\n", line, filename);
1411
if((pt = strstr(buffer, " (Clam)")))
1414
mprintf("%s\n", buffer);
1417
} else if(cli_strbcasestr(filename, ".cbc")) {
1418
if(fgets(buffer, FILEBUFF, fh) && fgets(buffer, FILEBUFF, fh)) {
1419
pt = strchr(buffer, ';');
1420
if(!pt) { /* not a real sig */
1426
if(!cli_regexec(regex, buffer, 0, NULL, 0)) {
1427
mprintf("[%s BYTECODE] %s", dbname, buffer);
1431
mprintf("%s\n", buffer);
1440
static int listsigs(const struct optstruct *opts, int mode)
1447
const char *localdbdir = NULL;
1449
if(optget(opts, "datadir")->active)
1450
localdbdir = optget(opts, "datadir")->strarg;
1453
name = optget(opts, "list-sigs")->strarg;
1454
if(access(name, R_OK) && localdbdir)
1456
if(CLAMSTAT(name, &sb) == -1) {
1457
mprintf("--list-sigs: Can't get status of %s\n", name);
1462
if(S_ISDIR(sb.st_mode)) {
1463
if(!strcmp(name, DATADIR)) {
1464
dbdir = freshdbdir();
1465
ret = listdir(localdbdir ? localdbdir : dbdir, NULL);
1468
ret = listdir(name, NULL);
1471
ret = listdb(name, NULL);
1475
if(cli_regcomp(®, optget(opts, "find-sigs")->strarg, REG_EXTENDED | REG_NOSUB) != 0) {
1476
mprintf("--find-sigs: Can't compile regex\n");
1480
dbdir = freshdbdir();
1481
ret = listdir(localdbdir ? localdbdir : dbdir, ®);
1489
static int vbadump(const struct optstruct *opts)
1494
struct uniq *vba = NULL;
1498
if(optget(opts, "vba-hex")->enabled) {
1500
pt = optget(opts, "vba-hex")->strarg;
1503
pt = optget(opts, "vba")->strarg;
1506
if((fd = open(pt, O_RDONLY|O_BINARY)) == -1) {
1507
mprintf("!vbadump: Can't open file %s\n", pt);
1511
/* generate the temporary directory */
1512
if(!(dir = cli_gentemp(NULL))) {
1513
mprintf("!vbadump: Can't generate temporary name\n");
1518
if(mkdir(dir, 0700)) {
1519
mprintf("!vbadump: Can't create temporary directory %s\n", dir);
1524
if(!(ctx = convenience_ctx(fd))) {
1529
if(cli_ole2_extract(dir, ctx, &vba)) {
1530
destroy_ctx(-1, ctx);
1535
destroy_ctx(-1, ctx);
1537
sigtool_vba_scandir(dir, hex_output, vba);
1543
static int comparesha(const char *diff)
1545
char info[32], buff[FILEBUFF], *sha, *pt, *name;
1546
const char *tokens[3];
1548
int ret = 0, tokens_count;
1550
name = strdup(diff);
1552
mprintf("!verifydiff: strdup() failed\n");
1555
if(!(pt = strrchr(name, '-')) || !isdigit(pt[1])) {
1556
mprintf("!verifydiff: Invalid diff name\n");
1561
if((pt = strrchr(name, *PATHSEP)))
1566
snprintf(info, sizeof(info), "%s.info", pt);
1569
if(!(fh = fopen(info, "rb"))) {
1570
mprintf("!verifydiff: Can't open %s\n", info);
1574
if(!fgets(buff, sizeof(buff), fh) || strncmp(buff, "ClamAV-VDB", 10)) {
1575
mprintf("!verifydiff: Incorrect info file %s\n", info);
1580
while(fgets(buff, sizeof(buff), fh)) {
1582
tokens_count = cli_strtokenize(buff, ':', 3, tokens);
1583
if(tokens_count != 3) {
1584
if(!strcmp(tokens[0], "DSIG"))
1586
mprintf("!verifydiff: Incorrect format of %s\n", info);
1590
if(!(sha = sha256file(tokens[0], NULL))) {
1591
mprintf("!verifydiff: Can't generate SHA256 for %s\n", buff);
1595
if(strcmp(sha, tokens[2])) {
1596
mprintf("!verifydiff: %s has incorrect checksum\n", buff);
1608
static int rundiff(const struct optstruct *opts)
1611
unsigned short mode;
1615
diff = optget(opts, "run-cdiff")->strarg;
1616
if(strstr(diff, ".cdiff")) {
1618
} else if(strstr(diff, ".script")) {
1621
mprintf("!rundiff: Incorrect file name (no .cdiff/.script extension)\n");
1625
if((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
1626
mprintf("!rundiff: Can't open file %s\n", diff);
1630
ret = cdiff_apply(fd, mode);
1634
ret = comparesha(diff);
1639
static int maxlinelen(const char *file)
1641
int fd, bytes, n = 0, nmax = 0, i;
1644
if((fd = open(file, O_RDONLY)) == -1) {
1645
mprintf("!maxlinelen: Can't open file %s\n", file);
1649
while((bytes = read(fd, buff, 512)) > 0) {
1650
for(i = 0; i < bytes; i++, ++n) {
1651
if(buff[i] == '\n') {
1660
mprintf("!maxlinelen: Can't read file %s\n", file);
1669
static int compare(const char *oldpath, const char *newpath, FILE *diff)
1672
char *obuff, *nbuff, *tbuff, *pt, *omd5, *nmd5;
1673
unsigned int oline = 0, tline, found, i, badxchg = 0;
1677
if(!access(oldpath, R_OK) && (omd5 = cli_hashfile(oldpath, 1))) {
1678
if(!(nmd5 = cli_hashfile(newpath, 1))) {
1679
mprintf("!compare: Can't get MD5 checksum of %s\n", newpath);
1683
if(!strcmp(omd5, nmd5)) {
1690
l1 = maxlinelen(oldpath);
1693
l2 = maxlinelen(newpath);
1694
if(l1 == -1 || l2 == -1)
1696
l1 = MAX(l1, l2) + 1;
1700
mprintf("!compare: Can't allocate memory for 'obuff'\n");
1705
mprintf("!compare: Can't allocate memory for 'nbuff'\n");
1711
mprintf("!compare: Can't allocate memory for 'tbuff'\n");
1717
if(l1 > CLI_DEFAULT_LSIG_BUFSIZE)
1718
fprintf(diff, "#LSIZE %u\n", l1 + 32);
1720
fprintf(diff, "OPEN %s\n", newpath);
1722
if(!(new = fopen(newpath, "rb"))) {
1723
mprintf("!compare: Can't open file %s for reading\n", newpath);
1729
old = fopen(oldpath, "rb");
1731
while(fgets(nbuff, l1, new)) {
1733
if(i >= 2 && (nbuff[i - 1] == '\r' || (nbuff[i - 1] == '\n' && nbuff[i - 2] == '\r'))) {
1734
mprintf("!compare: New %s file contains lines terminated with CRLF or CR\n", newpath);
1745
fprintf(diff, "ADD %s\n", nbuff);
1747
if(fgets(obuff, l1, old)) {
1750
if(!strcmp(nbuff, obuff)) {
1756
while(fgets(tbuff, l1, old)) {
1760
if(tline > MAX_DEL_LOOKAHEAD)
1763
if(!strcmp(tbuff, nbuff)) {
1768
fseek(old, opos, SEEK_SET);
1771
strncpy(tbuff, obuff, l1);
1773
for(i = 0; i < tline; i++) {
1774
tbuff[MIN(16, l1-1)] = 0;
1775
if((pt = strchr(tbuff, ' ')))
1777
fprintf(diff, "DEL %u %s\n", oline + i, tbuff);
1778
if(!fgets(tbuff, l1, old))
1784
if(!*obuff || *obuff == ' ') {
1788
obuff[MIN(16, l1-1)] = 0;
1789
if((pt = strchr(obuff, ' ')))
1791
fprintf(diff, "XCHG %u %s %s\n", oline, obuff, nbuff);
1797
fprintf(diff, "ADD %s\n", nbuff);
1804
while(fgets(obuff, l1, old)) {
1807
obuff[MIN(16, l1-1)] = 0;
1808
if((pt = strchr(obuff, ' ')))
1810
fprintf(diff, "DEL %u %s\n", oline, obuff);
1815
fprintf(diff, "CLOSE\n");
1819
fprintf(diff, "UNLINK %s\n", newpath);
1820
fprintf(diff, "OPEN %s\n", newpath);
1822
while(fgets(nbuff, l1, new)) {
1824
fprintf(diff, "ADD %s\n", nbuff);
1826
fprintf(diff, "CLOSE\n");
1833
static int compareone(const struct optstruct *opts)
1835
if(!opts->filename) {
1836
mprintf("!makediff: --compare requires two arguments\n");
1839
return compare(optget(opts,"compare")->strarg, opts->filename[0], stdout);
1842
static int dircopy(const char *src, const char *dest)
1845
struct dirent *dent;
1847
char spath[512], dpath[512];
1850
if(CLAMSTAT(dest, &sb) == -1) {
1851
if(mkdir(dest, 0755)) {
1852
/* mprintf("!dircopy: Can't create temporary directory %s\n", dest); */
1857
if((dd = opendir(src)) == NULL) {
1858
/* mprintf("!dircopy: Can't open directory %s\n", src); */
1862
while((dent = readdir(dd))) {
1865
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
1868
snprintf(spath, sizeof(spath), "%s"PATHSEP"%s", src, dent->d_name);
1869
snprintf(dpath, sizeof(dpath), "%s"PATHSEP"%s", dest, dent->d_name);
1871
if(filecopy(spath, dpath) == -1) {
1872
/* mprintf("!dircopy: Can't copy %s to %s\n", spath, dpath); */
1884
static int verifydiff(const char *diff, const char *cvd, const char *incdir)
1886
char *tempdir, cwd[512];
1888
unsigned short mode;
1891
if(strstr(diff, ".cdiff")) {
1893
} else if(strstr(diff, ".script")) {
1896
mprintf("!verifydiff: Incorrect file name (no .cdiff/.script extension)\n");
1900
tempdir = cli_gentemp(NULL);
1902
mprintf("!verifydiff: Can't generate temporary name for tempdir\n");
1906
if(mkdir(tempdir, 0700) == -1) {
1907
mprintf("!verifydiff: Can't create directory %s\n", tempdir);
1913
if(cli_cvdunpack(cvd, tempdir) == -1) {
1914
mprintf("!verifydiff: Can't unpack CVD file %s\n", cvd);
1915
cli_rmdirs(tempdir);
1920
if(dircopy(incdir, tempdir) == -1) {
1921
mprintf("!verifydiff: Can't copy dir %s to %s\n", incdir, tempdir);
1922
cli_rmdirs(tempdir);
1928
if(!getcwd(cwd, sizeof(cwd))) {
1929
mprintf("!verifydiff: getcwd() failed\n");
1930
cli_rmdirs(tempdir);
1935
if((fd = open(diff, O_RDONLY | O_BINARY)) == -1) {
1936
mprintf("!verifydiff: Can't open diff file %s\n", diff);
1937
cli_rmdirs(tempdir);
1942
if(chdir(tempdir) == -1) {
1943
mprintf("!verifydiff: Can't chdir to %s\n", tempdir);
1944
cli_rmdirs(tempdir);
1950
if(cdiff_apply(fd, mode) == -1) {
1951
mprintf("!verifydiff: Can't apply %s\n", diff);
1952
if(chdir(cwd) == -1)
1953
mprintf("^verifydiff: Can't chdir to %s\n", cwd);
1954
cli_rmdirs(tempdir);
1961
ret = comparesha(diff);
1963
if(chdir(cwd) == -1)
1964
mprintf("^verifydiff: Can't chdir to %s\n", cwd);
1965
cli_rmdirs(tempdir);
1970
mprintf("Verification: %s correctly applies to %s\n", diff, cvd);
1972
mprintf("Verification: %s correctly applies to the previous version\n", diff);
1978
static void matchsig(const char *sig, const char *offset, int fd)
1980
struct cl_engine *engine;
1981
struct cli_ac_result *acres = NULL, *res;
1983
unsigned int matches = 0;
1987
if(!(engine = cl_engine_new())) {
1988
mprintf("!matchsig: Can't create new engine\n");
1991
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
1993
if(cli_initroots(engine, 0) != CL_SUCCESS) {
1994
mprintf("!matchsig: cli_initroots() failed\n");
1995
cl_engine_free(engine);
1999
if(cli_parse_add(engine->root[0], "test", sig, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2000
mprintf("!matchsig: Can't parse signature\n");
2001
cl_engine_free(engine);
2005
if(cl_engine_compile(engine) != CL_SUCCESS) {
2006
mprintf("!matchsig: Can't compile engine\n");
2007
cl_engine_free(engine);
2010
memset(&ctx, '\0', sizeof(cli_ctx));
2011
ctx.engine = engine;
2012
ctx.options = CL_SCAN_STDOPT;
2013
ctx.container_type = CL_TYPE_ANY;
2014
ctx.dconf = (struct cli_dconf *) engine->dconf;
2015
ctx.fmap = calloc(sizeof(fmap_t *), 1);
2017
cl_engine_free(engine);
2020
lseek(fd, 0, SEEK_SET);
2022
if(!(*ctx.fmap = fmap(fd, 0, sb.st_size))) {
2024
cl_engine_free(engine);
2027
ret = cli_fmap_scandesc(&ctx, 0, 0, NULL, AC_SCAN_VIR, &acres, NULL);
2034
/* TODO: check offsets automatically */
2035
mprintf("MATCH: ** YES%s ** (%u %s:", offset ? "/CHECK OFFSET" : "", matches, matches > 1 ? "matches at offsets" : "match at offset");
2038
mprintf(" %u", (unsigned int) res->offset);
2043
mprintf("MATCH: ** NO **\n");
2047
acres = acres->next;
2051
cl_engine_free(engine);
2054
static char *decodehexstr(const char *hex, unsigned int *dlen)
2058
unsigned int i, p = 0, wildcard = 0, len = strlen(hex)/2;
2060
str16 = cli_hex2ui(hex);
2064
for(i = 0; i < len; i++)
2065
if(str16[i] & CLI_MATCH_WILDCARD)
2068
decoded = calloc(len + 1 + wildcard * 32, sizeof(char));
2071
mprintf("!decodehexstr: Can't allocate memory for decoded\n");
2075
for(i = 0; i < len; i++) {
2076
if(str16[i] & CLI_MATCH_WILDCARD) {
2077
switch(str16[i] & CLI_MATCH_WILDCARD) {
2078
case CLI_MATCH_IGNORE:
2079
p += sprintf(decoded + p, "{WILDCARD_IGNORE}");
2082
case CLI_MATCH_NIBBLE_HIGH:
2083
p += sprintf(decoded + p, "{WILDCARD_NIBBLE_HIGH:0x%x}", str16[i] & 0x00f0);
2086
case CLI_MATCH_NIBBLE_LOW:
2087
p += sprintf(decoded + p, "{WILDCARD_NIBBLE_LOW:0x%x}", str16[i] & 0x000f);
2091
mprintf("!decodehexstr: Unknown wildcard (0x%x@%u)\n", str16[i] & CLI_MATCH_WILDCARD, i);
2097
decoded[p] = str16[i];
2108
static char *decodehexspecial(const char *hex, unsigned int *dlen)
2110
char *pt, *start, *hexcpy, *decoded, *h, *c;
2111
unsigned int i, len = 0, hlen, negative, altnum, alttype;
2118
hexcpy = strdup(hex);
2120
mprintf("!decodehexspecial: strdup(hex) failed\n");
2123
pt = strchr(hexcpy, '(');
2126
return decodehexstr(hex, dlen);
2128
buff = calloc(strlen(hex) + 512, sizeof(char));
2130
mprintf("!decodehexspecial: Can't allocate memory for buff\n");
2139
mprintf("!decodehexspecial: Unexpected EOL\n");
2144
if(pt >= hexcpy + 2) {
2150
if(!(decoded = decodehexstr(start, &hlen))) {
2151
mprintf("!Decoding failed (1): %s\n", pt);
2156
memcpy(&buff[len], decoded, hlen);
2160
if(!(start = strchr(pt, ')'))) {
2161
mprintf("!decodehexspecial: Missing closing parethesis\n");
2169
mprintf("!decodehexspecial: Empty block\n");
2175
if(!strcmp(pt, "B")) {
2178
len += sprintf(buff + len, "{NOT_BOUNDARY_RIGHT}");
2180
len += sprintf(buff + len, "{BOUNDARY_RIGHT}");
2182
} else if(pt - 1 == hexcpy) {
2184
len += sprintf(buff + len, "{NOT_BOUNDARY_LEFT}");
2186
len += sprintf(buff + len, "{BOUNDARY_LEFT}");
2189
} else if(!strcmp(pt, "L")) {
2192
len += sprintf(buff + len, "{NOT_LINE_MARKER_RIGHT}");
2194
len += sprintf(buff + len, "{LINE_MARKER_RIGHT}");
2196
} else if(pt - 1 == hexcpy) {
2198
len += sprintf(buff + len, "{NOT_LINE_MARKER_LEFT}");
2200
len += sprintf(buff + len, "{LINE_MARKER_LEFT}");
2205
for(i = 0; i < strlen(pt); i++)
2210
mprintf("!decodehexspecial: Empty block\n");
2217
if(3 * altnum - 1 == (uint16_t) strlen(pt)) {
2218
alttype = 1; /* char */
2220
len += sprintf(buff + len, "{EXCLUDING_CHAR_ALTERNATIVE:");
2222
len += sprintf(buff + len, "{CHAR_ALTERNATIVE:");
2224
alttype = 2; /* str */
2226
len += sprintf(buff + len, "{EXCLUDING_STRING_ALTERNATIVE:");
2228
len += sprintf(buff + len, "{STRING_ALTERNATIVE:");
2231
for(i = 0; i < altnum; i++) {
2232
if(!(h = cli_strtok(pt, i, "|"))) {
2238
if(!(c = cli_hex2str(h))) {
2248
memcpy(&buff[len], c, strlen(h) / 2);
2249
len += strlen(h) / 2;
2259
} while((pt = strchr(start, '(')));
2262
if(!(decoded = decodehexstr(start, &hlen))) {
2263
mprintf("!Decoding failed (2)\n");
2268
memcpy(&buff[len], decoded, hlen);
2279
static int decodehex(const char *hexsig)
2281
char *pt, *hexcpy, *start, *n, *decoded;
2283
unsigned int i, j, hexlen, dlen, parts = 0, bw;
2284
int mindist = 0, maxdist = 0, error = 0;
2287
hexlen = strlen(hexsig);
2288
if(strchr(hexsig, '{') || strchr(hexsig, '[')) {
2289
if(!(hexcpy = strdup(hexsig)))
2292
for(i = 0; i < hexlen; i++)
2293
if(hexsig[i] == '{' || hexsig[i] == '[' || hexsig[i] == '*')
2299
start = pt = hexcpy;
2300
for(i = 1; i <= parts; i++) {
2302
for(j = 0; j < strlen(start); j++) {
2303
if(start[j] == '{' || start[j] == '[') {
2308
if(start[j] == '*') {
2317
if(mindist && maxdist) {
2318
if(mindist == maxdist)
2319
mprintf("{WILDCARD_ANY_STRING(LENGTH==%u)}", mindist);
2321
mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u&&<=%u)}", mindist, maxdist);
2323
mprintf("{WILDCARD_ANY_STRING(LENGTH>=%u)}", mindist);
2325
mprintf("{WILDCARD_ANY_STRING(LENGTH<=%u)}", maxdist);
2327
if(!(decoded = decodehexspecial(start, &dlen))) {
2328
mprintf("!Decoding failed\n");
2332
bw = write(1, decoded, dlen);
2339
mprintf("{WILDCARD_ANY_STRING}");
2341
mindist = maxdist = 0;
2348
if(!(start = strchr(pt, '}')) && !(start = strchr(pt, ']'))) {
2359
if(!strchr(pt, '-')) {
2360
if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
2365
if((n = cli_strtok(pt, 0, "-"))) {
2366
if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
2374
if((n = cli_strtok(pt, 1, "-"))) {
2375
if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
2383
if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
2395
} else if(strchr(hexsig, '*')) {
2396
for(i = 0; i < hexlen; i++)
2397
if(hexsig[i] == '*')
2403
for(i = 1; i <= parts; i++) {
2404
if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
2405
mprintf("!Can't extract part %u of partial signature\n", i);
2408
if(!(decoded = decodehexspecial(pt, &dlen))) {
2409
mprintf("!Decoding failed\n");
2413
bw = write(1, decoded, dlen);
2416
mprintf("{WILDCARD_ANY_STRING}");
2421
if(!(decoded = decodehexspecial(hexsig, &dlen))) {
2422
mprintf("!Decoding failed\n");
2425
bw = write(1, decoded, dlen);
2433
static int decodesig(char *sig, int fd)
2436
const char *tokens[68];
2437
int tokens_count, subsigs, i, bc = 0;
2440
if(!(pt = strchr(sig, ']'))) {
2441
mprintf("!decodesig: Invalid input\n");
2447
if(strchr(sig, ';')) { /* lsig */
2448
tokens_count = cli_strtokenize(sig, ';', 67 + 1, (const char **) tokens);
2449
if(tokens_count < 4) {
2450
mprintf("!decodesig: Invalid or not supported signature format\n");
2453
mprintf("VIRUS NAME: %s\n", tokens[0]);
2454
if(strlen(tokens[0]) && strstr(tokens[0], ".{") && tokens[0][strlen(tokens[0]) - 1] == '}')
2456
mprintf("TDB: %s\n", tokens[1]);
2457
mprintf("LOGICAL EXPRESSION: %s\n", tokens[2]);
2458
subsigs = cli_ac_chklsig(tokens[2], tokens[2] + strlen(tokens[2]), NULL, NULL, NULL, 1);
2460
mprintf("!decodesig: Broken logical expression\n");
2465
mprintf("!decodesig: Too many subsignatures\n");
2468
if(!bc && subsigs != tokens_count - 3) {
2469
mprintf("!decodesig: The number of subsignatures (==%u) doesn't match the IDs in the logical expression (==%u)\n", tokens_count - 3, subsigs);
2472
for(i = 0; i < tokens_count - 3; i++) {
2474
mprintf(" * BYTECODE SUBSIG\n");
2476
mprintf(" * SUBSIG ID %d\n", i);
2477
if((pt = strchr(tokens[3 + i], ':'))) {
2479
mprintf(" +-> OFFSET: %s\n", tokens[3 + i]);
2481
mprintf(" +-> OFFSET: ANY\n");
2484
mprintf(" +-> DECODED SUBSIGNATURE:\n");
2485
decodehex(pt ? pt : tokens[3 + i]);
2488
matchsig(pt ? pt : tokens[3 + i], pt ? tokens[3 + i] : NULL, fd);
2491
} else if(strchr(sig, ':')) { /* ndb */
2492
tokens_count = cli_strtokenize(sig, ':', 6 + 1, tokens);
2493
if(tokens_count < 4 || tokens_count > 6) {
2494
mprintf("!decodesig: Invalid or not supported signature format\n");
2495
mprintf("TOKENS COUNT: %u\n", tokens_count);
2498
mprintf("VIRUS NAME: %s\n", tokens[0]);
2499
if(tokens_count == 5)
2500
mprintf("FUNCTIONALITY LEVEL: >=%s\n", tokens[4]);
2501
else if(tokens_count == 6)
2502
mprintf("FUNCTIONALITY LEVEL: %s..%s\n", tokens[4], tokens[5]);
2504
if(!cli_isnumber(tokens[1])) {
2505
mprintf("!decodesig: Invalid target type\n");
2508
mprintf("TARGET TYPE: ");
2509
switch(atoi(tokens[1])) {
2511
mprintf("ANY FILE\n");
2526
mprintf("GRAPHICS\n");
2532
mprintf("NORMALIZED ASCII TEXT\n");
2535
mprintf("DISASM DATA\n");
2547
mprintf("JAVA CLASS\n");
2550
mprintf("!decodesig: Invalid target type\n");
2553
mprintf("OFFSET: %s\n", tokens[2]);
2555
mprintf("DECODED SIGNATURE:\n");
2556
decodehex(tokens[3]);
2558
matchsig(tokens[3], strcmp(tokens[2], "*") ? tokens[2] : NULL, fd);
2560
} else if((pt = strchr(sig, '='))) {
2562
mprintf("VIRUS NAME: %s\n", sig);
2564
mprintf("DECODED SIGNATURE:\n");
2567
matchsig(pt, NULL, fd);
2570
mprintf("decodesig: Not supported signature format\n");
2577
static int decodesigs(void)
2582
while(fgets(buffer, sizeof(buffer), stdin)) {
2586
if(decodesig(buffer, -1) == -1)
2592
static int testsigs(const struct optstruct *opts)
2599
if(!opts->filename) {
2600
mprintf("!--test-sigs requires two arguments\n");
2604
sigs = fopen(optget(opts, "test-sigs")->strarg, "rb");
2606
mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
2610
fd = open(opts->filename[0], O_RDONLY|O_BINARY);
2612
mprintf("!testsigs: Can't open file %s\n", optget(opts, "test-sigs")->strarg);
2617
while(fgets(buffer, sizeof(buffer), sigs)) {
2621
if(decodesig(buffer, fd) == -1) {
2632
static int diffdirs(const char *old, const char *new, const char *patch)
2636
struct dirent *dent;
2637
char cwd[512], path[1024];
2640
if(!getcwd(cwd, sizeof(cwd))) {
2641
mprintf("!diffdirs: getcwd() failed\n");
2645
if(!(diff = fopen(patch, "wb"))) {
2646
mprintf("!diffdirs: Can't open %s for writing\n", patch);
2650
if(chdir(new) == -1) {
2651
mprintf("!diffdirs: Can't chdir to %s\n", new);
2656
if((dd = opendir(new)) == NULL) {
2657
mprintf("!diffdirs: Can't open directory %s\n", new);
2662
while((dent = readdir(dd))) {
2665
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
2668
snprintf(path, sizeof(path), "%s"PATHSEP"%s", old, dent->d_name);
2669
if(compare(path, dent->d_name, diff) == -1) {
2670
if(chdir(cwd) == -1)
2671
mprintf("^diffdirs: Can't chdir to %s\n", cwd);
2681
/* check for removed files */
2682
if((dd = opendir(old)) == NULL) {
2683
mprintf("!diffdirs: Can't open directory %s\n", old);
2688
while((dent = readdir(dd))) {
2691
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
2694
snprintf(path, sizeof(path), "%s"PATHSEP"%s", new, dent->d_name);
2695
if(access(path, R_OK))
2696
fprintf(diff, "UNLINK %s\n", dent->d_name);
2702
mprintf("Generated diff file %s\n", patch);
2703
if(chdir(cwd) == -1)
2704
mprintf("^diffdirs: Can't chdir to %s\n", cwd);
2709
static int makediff(const struct optstruct *opts)
2711
char *odir, *ndir, name[32], broken[32], dbname[32];
2713
unsigned int oldver, newver;
2717
if(!opts->filename) {
2718
mprintf("!makediff: --diff requires two arguments\n");
2722
if(!(cvd = cl_cvdhead(opts->filename[0]))) {
2723
mprintf("!makediff: Can't read CVD header from %s\n", opts->filename[0]);
2726
newver = cvd->version;
2729
if(!(cvd = cl_cvdhead(optget(opts, "diff")->strarg))) {
2730
mprintf("!makediff: Can't read CVD header from %s\n", optget(opts, "diff")->strarg);
2733
oldver = cvd->version;
2736
if(oldver + 1 != newver) {
2737
mprintf("!makediff: The old CVD must be %u\n", newver - 1);
2741
odir = cli_gentemp(NULL);
2743
mprintf("!makediff: Can't generate temporary name for odir\n");
2747
if(mkdir(odir, 0700) == -1) {
2748
mprintf("!makediff: Can't create directory %s\n", odir);
2753
if(cli_cvdunpack(optget(opts, "diff")->strarg, odir) == -1) {
2754
mprintf("!makediff: Can't unpack CVD file %s\n", optget(opts, "diff")->strarg);
2760
ndir = cli_gentemp(NULL);
2762
mprintf("!makediff: Can't generate temporary name for ndir\n");
2768
if(mkdir(ndir, 0700) == -1) {
2769
mprintf("!makediff: Can't create directory %s\n", ndir);
2776
if(cli_cvdunpack(opts->filename[0], ndir) == -1) {
2777
mprintf("!makediff: Can't unpack CVD file %s\n", opts->filename[0]);
2785
snprintf(name, sizeof(name), "%s-%u.script", getdbname(opts->filename[0], dbname, sizeof(dbname)), newver);
2786
ret = diffdirs(odir, ndir, name);
2796
if(verifydiff(name, optget(opts, "diff")->strarg, NULL) == -1) {
2797
snprintf(broken, sizeof(broken), "%s.broken", name);
2798
if(rename(name, broken)) {
2800
mprintf("!Generated file is incorrect, removed");
2802
mprintf("!Generated file is incorrect, renamed to %s\n", broken);
2810
static int dumpcerts(const struct optstruct *opts)
2812
char * filename = NULL;
2815
struct cl_engine *engine;
2818
uint8_t shash1[SHA1_HASH_SIZE];
2822
filename = optget(opts, "print-certs")->strarg;
2824
mprintf("!dumpcerts: No filename!\n");
2829
if(!(engine = cl_engine_new())) {
2830
mprintf("!dumpcerts: Can't create new engine\n");
2833
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
2835
if(cli_initroots(engine, 0) != CL_SUCCESS) {
2836
mprintf("!dumpcerts: cli_initroots() failed\n");
2837
cl_engine_free(engine);
2841
if(cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2842
mprintf("!dumpcerts: Can't parse signature\n");
2843
cl_engine_free(engine);
2847
if(cl_engine_compile(engine) != CL_SUCCESS) {
2848
mprintf("!dumpcerts: Can't compile engine\n");
2849
cl_engine_free(engine);
2853
engine->dconf->pe |= PE_CONF_DUMPCERT;
2856
/* prepare context */
2857
memset(&ctx, '\0', sizeof(cli_ctx));
2858
ctx.engine = engine;
2859
ctx.options = CL_SCAN_STDOPT;
2860
ctx.container_type = CL_TYPE_ANY;
2861
ctx.dconf = (struct cli_dconf *) engine->dconf;
2862
ctx.fmap = calloc(sizeof(fmap_t *), 1);
2864
cl_engine_free(engine);
2869
fd = open(filename, O_RDONLY);
2871
mprintf("!dumpcerts: Can't open file %s!\n", filename);
2872
cl_engine_free(engine);
2876
lseek(fd, 0, SEEK_SET);
2878
if(!(*ctx.fmap = fmap(fd, 0, sb.st_size))) {
2881
cl_engine_free(engine);
2885
fmptr = fmap_need_off_once(*ctx.fmap, 0, sb.st_size);
2887
mprintf("!dumpcerts: fmap_need_off_once failed!\n");
2890
cl_engine_free(engine);
2895
cl_sha1(fmptr, sb.st_size, shash1, NULL);
2897
ret = cli_checkfp_pe(&ctx, shash1, NULL, CL_CHECKFP_PE_FLAG_AUTHENTICODE);
2901
mprintf("*dumpcerts: CL_CLEAN after cli_checkfp_pe()!\n");
2904
mprintf("*dumpcerts: CL_VIRUS after cli_checkfp_pe()!\n");
2907
mprintf("*dumpcerts: CL_BREAK after cli_checkfp_pe()!\n");
2910
mprintf("!dumpcerts: Not a valid PE file!\n");
2913
mprintf("!dumpcerts: Other error %d inside cli_checkfp_pe.\n", ret);
2920
cl_engine_free(engine);
2924
static void help(void)
2927
mprintf("Clam AntiVirus: Signature Tool (sigtool) %s\n", get_version());
2928
mprintf(" By The ClamAV Team: http://www.clamav.net/team\n");
2929
mprintf(" (C) 2007-2009 Sourcefire, Inc. et al.\n\n");
2931
mprintf(" --help -h show help\n");
2932
mprintf(" --version -V print version number and exit\n");
2933
mprintf(" --quiet be quiet, output only error messages\n");
2934
mprintf(" --debug enable debug messages\n");
2935
mprintf(" --stdout write to stdout instead of stderr\n");
2936
mprintf(" --hex-dump convert data from stdin to a hex\n");
2937
mprintf(" string and print it on stdout\n");
2938
mprintf(" --md5 [FILES] generate MD5 checksum from stdin\n");
2939
mprintf(" or MD5 sigs for FILES\n");
2940
mprintf(" --sha1 [FILES] generate SHA1 checksum from stdin\n");
2941
mprintf(" or SHA1 sigs for FILES\n");
2942
mprintf(" --sha256 [FILES] generate SHA256 checksum from stdin\n");
2943
mprintf(" or SHA256 sigs for FILES\n");
2944
mprintf(" --mdb [FILES] generate .mdb sigs\n");
2945
mprintf(" --html-normalise=FILE create normalised parts of HTML file\n");
2946
mprintf(" --utf16-decode=FILE decode UTF16 encoded files\n");
2947
mprintf(" --info=FILE -i FILE print database information\n");
2948
mprintf(" --build=NAME [cvd] -b NAME build a CVD file\n");
2949
mprintf(" --max-bad-sigs=NUMBER Maximum number of mismatched signatures\n");
2950
mprintf(" when building a CVD. Default: 3000\n");
2951
mprintf(" --flevel=FLEVEL Specify a custom flevel.\n");
2952
mprintf(" Default: %u\n", cl_retflevel());
2953
mprintf(" --cvd-version=NUMBER Specify the version number to use for\n");
2954
mprintf(" the build. Default is to use the value+1\n");
2955
mprintf(" from the current CVD in --datadir.\n");
2956
mprintf(" If no datafile is found the default\n");
2957
mprintf(" behaviour is to prompt for a version\n");
2958
mprintf(" number, this switch will prevent the\n");
2959
mprintf(" prompt. NOTE: If a CVD is found in the\n");
2960
mprintf(" --datadir its version+1 is used and\n");
2961
mprintf(" this value is ignored.\n");
2962
mprintf(" --no-cdiff Don't generate .cdiff file\n");
2963
mprintf(" --unsigned Create unsigned database file (.cud)\n");
2964
mprintf(" --print-certs=FILE Print Authenticode details from a PE\n");
2965
mprintf(" --server=ADDR ClamAV Signing Service address\n");
2966
mprintf(" --datadir=DIR Use DIR as default database directory\n");
2967
mprintf(" --unpack=FILE -u FILE Unpack a CVD/CLD file\n");
2968
mprintf(" --unpack-current=SHORTNAME Unpack local CVD/CLD into cwd\n");
2969
mprintf(" --list-sigs[=FILE] -l[FILE] List signature names\n");
2970
mprintf(" --find-sigs=REGEX -fREGEX Find signatures matching REGEX\n");
2971
mprintf(" --decode-sigs Decode signatures from stdin\n");
2972
mprintf(" --test-sigs=DATABASE TARGET_FILE Test signatures from DATABASE against \n");
2973
mprintf(" TARGET_FILE\n");
2974
mprintf(" --vba=FILE Extract VBA/Word6 macro code\n");
2975
mprintf(" --vba-hex=FILE Extract Word6 macro code with hex values\n");
2976
mprintf(" --diff=OLD NEW -d OLD NEW Create diff for OLD and NEW CVDs\n");
2977
mprintf(" --compare=OLD NEW -c OLD NEW Show diff between OLD and NEW files in\n");
2978
mprintf(" cdiff format\n");
2979
mprintf(" --run-cdiff=FILE -r FILE Execute update script FILE in cwd\n");
2980
mprintf(" --verify-cdiff=DIFF CVD/CLD Verify DIFF against CVD/CLD\n");
2986
int main(int argc, char **argv)
2989
struct optstruct *opts;
2995
if((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
2996
mprintf("!Can't initialize libclamav: %s\n", cl_strerror(ret));
3001
opts = optparse(NULL, argc, argv, 1, OPT_SIGTOOL, 0, NULL);
3003
mprintf("!Can't parse command line options\n");
3007
if(optget(opts, "quiet")->enabled)
3010
if(optget(opts, "stdout")->enabled)
3013
if(optget(opts, "debug")->enabled)
3016
if(optget(opts, "version")->enabled) {
3017
print_version(NULL);
3022
if(optget(opts, "help")->enabled) {
3028
if(optget(opts, "hex-dump")->enabled)
3030
else if(optget(opts, "md5")->enabled)
3031
ret = hashsig(opts, 0, 1);
3032
else if(optget(opts, "sha1")->enabled)
3033
ret = hashsig(opts, 0, 2);
3034
else if(optget(opts, "sha256")->enabled)
3035
ret = hashsig(opts, 0, 3);
3036
else if(optget(opts, "mdb")->enabled)
3037
ret = hashsig(opts, 1, 1);
3038
else if(optget(opts, "html-normalise")->enabled)
3039
ret = htmlnorm(opts);
3040
else if(optget(opts, "utf16-decode")->enabled)
3041
ret = utf16decode(opts);
3042
else if(optget(opts, "build")->enabled)
3044
else if(optget(opts, "unpack")->enabled)
3046
else if(optget(opts, "unpack-current")->enabled)
3048
else if(optget(opts, "info")->enabled)
3049
ret = cvdinfo(opts);
3050
else if(optget(opts, "list-sigs")->active)
3051
ret = listsigs(opts, 0);
3052
else if(optget(opts, "find-sigs")->active)
3053
ret = listsigs(opts, 1);
3054
else if(optget(opts, "decode-sigs")->active)
3056
else if(optget(opts, "test-sigs")->enabled)
3057
ret = testsigs(opts);
3058
else if(optget(opts, "vba")->enabled || optget(opts, "vba-hex")->enabled)
3059
ret = vbadump(opts);
3060
else if(optget(opts, "diff")->enabled)
3061
ret = makediff(opts);
3062
else if(optget(opts, "compare")->enabled)
3063
ret = compareone(opts);
3064
else if(optget(opts, "print-certs")->enabled)
3065
ret = dumpcerts(opts);
3066
else if(optget(opts, "run-cdiff")->enabled)
3067
ret = rundiff(opts);
3068
else if(optget(opts, "verify-cdiff")->enabled) {
3069
if(!opts->filename) {
3070
mprintf("!--verify-cdiff requires two arguments\n");
3073
if(CLAMSTAT(opts->filename[0], &sb) == -1) {
3074
mprintf("--verify-cdiff: Can't get status of %s\n", opts->filename[0]);
3077
if(S_ISDIR(sb.st_mode))
3078
ret = verifydiff(optget(opts, "verify-cdiff")->strarg, NULL, opts->filename[0]);
3080
ret = verifydiff(optget(opts, "verify-cdiff")->strarg, opts->filename[0], NULL);
3087
cl_cleanup_crypto();