diff --git a/ChangeLog b/ChangeLog index 1a955b9c4..4a2f493c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue May 27 17:39:06 CEST 2008 +----------------------------- + * improve handling of PDF, CAB, RTF, OLE2 and HTML files (sync with + branch/0.93) + Sat May 24 21:38:47 EEST 2008 (edwin) ------------------------------------- * clamd/others.c, session.c, m4/fdpassing.m4: diff --git a/clamav-milter/clamav-milter.c b/clamav-milter/clamav-milter.c index 9188f88f8..c31c49358 100644 --- a/clamav-milter/clamav-milter.c +++ b/clamav-milter/clamav-milter.c @@ -1167,6 +1167,7 @@ main(int argc, char **argv) memset(&ifr, '\0', sizeof(struct ifreq)); strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1); + ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0'; if(setsockopt(broadcastSock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { perror(iface); return EX_CONFIG; @@ -1515,6 +1516,7 @@ main(int argc, char **argv) memset((char *)&sockun, 0, sizeof(struct sockaddr_un)); sockun.sun_family = AF_UNIX; strncpy(sockun.sun_path, localSocket, sizeof(sockun.sun_path)); + sockun.sun_path[sizeof(sockun.sun_path)-1]='\0'; sessions = (struct session *)cli_malloc(sizeof(struct session)); if((sessions[0].sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { @@ -2197,6 +2199,7 @@ pingServer(int serverNumber) memset((char *)&server, 0, sizeof(struct sockaddr_un)); server.sun_family = AF_UNIX; strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror(localSocket); @@ -2749,6 +2752,7 @@ clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) } #else strncpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr), sizeof(ip)); + ip[sizeof(ip)-1]='\0'; #endif /* @@ -3409,6 +3413,7 @@ clamfi_eom(SMFICTX *ctx) memset((char *)&server, 0, sizeof(struct sockaddr_un)); server.sun_family = AF_UNIX; strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) { perror(localSocket); @@ -3530,19 +3535,20 @@ clamfi_eom(SMFICTX *ctx) const char *j = smfi_getsymval(ctx, "{j}"); if(j) - strncpy(hostname, j, - sizeof(hostname) - 1); + strncpy(hostname, j, sizeof(hostname) - 1); else strcpy(hostname, _("Error determining host")); + hostname[sizeof(hostname)-1]='\0'; } else if(strchr(hostname, '.') == NULL) { /* * Determine fully qualified name */ struct hostent hostent; - if((r_gethostbyname(hostname, &hostent, buf, sizeof(buf)) == 0) && - hostent.h_name) + if((r_gethostbyname(hostname, &hostent, buf, sizeof(buf)) == 0) && hostent.h_name) { strncpy(hostname, hostent.h_name, sizeof(hostname)); + hostname[sizeof(hostname)-1]='\0'; + } } #ifdef SESSION @@ -4557,6 +4563,7 @@ connect2clamd(struct privdata *privdata) memset((char *)&server, 0, sizeof(struct sockaddr_un)); server.sun_family = AF_UNIX; strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); @@ -6405,6 +6412,7 @@ spf(struct privdata *privdata, table_t *prevhosts) continue; } strncpy(txt, (const char *)&p[1], sizeof(txt) - 1); + txt[sizeof(txt)-1]='\0'; txt[len - 1] = '\0'; if((strncmp(txt, "v=spf1 ", 7) == 0) || (strncmp(txt, "spf2.0/pra ", 11) == 0)) { int j; diff --git a/clamd/dazukoio.c b/clamd/dazukoio.c index 7c499c622..9506c839c 100644 --- a/clamd/dazukoio.c +++ b/clamd/dazukoio.c @@ -173,11 +173,13 @@ int dazukoRegister_TS(dazuko_id_t **dazuko_id, const char *groupName, const char if (strcasecmp(mode, "r") == 0) { strncpy(regMode, "R", sizeof(regMode)); + regMode[sizeof(regMode)-1]='\0'; write_mode = 0; } else if (strcasecmp(mode, "r+") == 0 || strcasecmp(mode, "rw") == 0) { strncpy(regMode, "RW", sizeof(regMode)); + regMode[sizeof(regMode)-1]='\0'; write_mode = 1; } else diff --git a/clamd/dazukoio_compat12.c b/clamd/dazukoio_compat12.c index 6bacbc18e..d56f42ec1 100644 --- a/clamd/dazukoio_compat12.c +++ b/clamd/dazukoio_compat12.c @@ -116,6 +116,7 @@ int dazukoRegister_TS_compat12(struct dazuko_id *dazuko, const char *groupName) opt->command = REGISTER; strncpy(opt->buffer, groupName, sizeof(opt->buffer) - 1); + opt->buffer[sizeof(opt->buffer)-1]='\0'; opt->buffer_length = strlen(opt->buffer) + 1; if (ioctl(dazuko->device, _IOW(dazuko->dev_major, IOCTL_SET_OPTION, void *), opt) != 0) @@ -186,6 +187,7 @@ int dazuko_set_path_compat12(struct dazuko_id *dazuko, const char *path, int com opt->command = command; strncpy(opt->buffer, path, sizeof(opt->buffer) - 1); + opt->buffer[sizeof(opt->buffer)-1]='\0'; opt->buffer_length = strlen(opt->buffer) + 1; if (ioctl(dazuko->device, _IOW(dazuko->dev_major, IOCTL_SET_OPTION, void *), opt) != 0) diff --git a/clamd/localserver.c b/clamd/localserver.c index 9f60ea345..1ee576c87 100644 --- a/clamd/localserver.c +++ b/clamd/localserver.c @@ -64,6 +64,7 @@ int localserver(const struct cfgstruct *copt) memset((char *) &server, 0, sizeof(server)); server.sun_family = AF_UNIX; strncpy(server.sun_path, cfgopt(copt, "LocalSocket")->strarg, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { estr = strerror(errno); diff --git a/clamd/others.c b/clamd/others.c index 75c494018..430235de1 100644 --- a/clamd/others.c +++ b/clamd/others.c @@ -376,6 +376,8 @@ int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeou break; } else { n = recv(sockfd, buf+boff, n, 0); + if(n < 0) + return -1; if((boff+n) == size) break; boff += n; diff --git a/clamdscan/client.c b/clamdscan/client.c index d8b696e6c..23876d4e5 100644 --- a/clamdscan/client.c +++ b/clamdscan/client.c @@ -284,6 +284,7 @@ static int dconnect(const struct optstruct *opt) server.sun_family = AF_UNIX; strncpy(server.sun_path, cpt->strarg, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket()"); diff --git a/clamscan/manager.c b/clamscan/manager.c index 644bf3207..f04e32acd 100644 --- a/clamscan/manager.c +++ b/clamscan/manager.c @@ -226,6 +226,7 @@ int scanmanager(const struct optstruct *opt) if(tolower(ptr[strlen(ptr) - 1]) == 'm') { cpy = calloc(strlen(ptr), 1); strncpy(cpy, ptr, strlen(ptr) - 1); + cpy[strlen(ptr)-1]='\0'; limits.maxscansize = atoi(cpy) * 1024 * 1024; free(cpy); } else @@ -239,6 +240,7 @@ int scanmanager(const struct optstruct *opt) if(tolower(ptr[strlen(ptr) - 1]) == 'm') { cpy = calloc(strlen(ptr), 1); strncpy(cpy, ptr, strlen(ptr) - 1); + cpy[strlen(ptr)-1]='\0'; limits.maxfilesize = atoi(cpy) * 1024 * 1024; free(cpy); } else @@ -467,6 +469,7 @@ static int clamav_unpack(const char *prog, const char **args, const char *tmpdir if(tolower(ptr[strlen(ptr) - 1]) == 'm') { /* megabytes */ cpy = calloc(strlen(ptr), 1); strncpy(cpy, ptr, strlen(ptr) - 1); + cpy[strlen(ptr)-1]='\0'; maxscansize = atoi(cpy) * 1024; free(cpy); } else /* default - kilobytes */ diff --git a/clamscan/others.c b/clamscan/others.c index ddcf8cb71..fd6d3dc8c 100644 --- a/clamscan/others.c +++ b/clamscan/others.c @@ -150,14 +150,17 @@ int match_regex(const char *filename, const char *pattern) #else if(pattern[strlen(pattern) - 1] == '\\') { strncpy(fname, filename, 510); + fname[509]='\0'; len = strlen(fname); if(fname[len - 1] != '\\') { fname[len] = '\\'; fname[len + 1] = 0; } #endif - } else + } else { strncpy(fname, filename, 513); + fname[512]='\0'; + } match = (cli_regexec(®, fname, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1; cli_regfree(®); diff --git a/freshclam/notify.c b/freshclam/notify.c index 20c83c049..ed6cd5b2e 100644 --- a/freshclam/notify.c +++ b/freshclam/notify.c @@ -73,6 +73,7 @@ int notify(const char *cfgfile) socktype = "UNIX"; server.sun_family = AF_UNIX; strncpy(server.sun_path, cpt->strarg, sizeof(server.sun_path)); + server.sun_path[sizeof(server.sun_path)-1]='\0'; if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { logg("^Clamd was NOT notified: Can't create socket endpoint for %s\n", cpt->strarg); diff --git a/libclamav/cab.c b/libclamav/cab.c index 4773723b5..4825a6846 100644 --- a/libclamav/cab.c +++ b/libclamav/cab.c @@ -122,7 +122,6 @@ static char *cab_readstr(int fd, int *ret) } if(lseek(fd, (off_t) (pos + i + 1), SEEK_SET) == -1) { - /* *ret = CL_EIO; */ *ret = CL_EFORMAT; /* most likely a corrupted file */ return NULL; } @@ -136,15 +135,17 @@ static char *cab_readstr(int fd, int *ret) return str; } -static int cab_chkname(const char *name) +static int cab_chkname(char *name, int san) { size_t i, len = strlen(name); for(i = 0; i < len; i++) { - if(strchr("%/*?|\\\"+=<>;:\t ", name[i]) || !isascii(name[i])) { + if(!san && (strchr("%/*?|\\\"+=<>;:\t ", name[i]) || !isascii(name[i]))) { cli_dbgmsg("cab_chkname: File name contains disallowed characters\n"); return 1; + } else if(san && !isalnum(name[i])) { + name[i] = '*'; } } @@ -189,7 +190,7 @@ void cab_free(struct cab_archive *cab) int cab_open(int fd, off_t offset, struct cab_archive *cab) { - unsigned int i, bscore = 0, badname = 0; + unsigned int i, folders = 0; struct cab_file *file, *lfile = NULL; struct cab_folder *folder, *lfolder = NULL; struct cab_hdr hdr; @@ -210,7 +211,6 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) if(cli_readn(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cab_open: Can't read cabinet header\n"); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } @@ -232,8 +232,8 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) cab->length = EC32(hdr.cbCabinet); cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length); if((off_t) cab->length > rsize) { + cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize); cab->length = (uint32_t) rsize; - bscore++; } cab->nfolders = EC16(hdr.cFolders); @@ -245,7 +245,6 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) if(cab->nfolders > CAB_FOLDER_LIMIT) { cab->nfolders = CAB_FOLDER_LIMIT; cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders); - bscore++; } } @@ -258,19 +257,15 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) if(cab->nfiles > CAB_FILE_LIMIT) { cab->nfiles = CAB_FILE_LIMIT; cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles); - bscore++; } } cli_dbgmsg("CAB: File format version: %u.%u\n", hdr.versionMajor, hdr.versionMinor); - if(hdr.versionMajor != 1 || hdr.versionMinor != 3) - bscore++; cab->flags = EC16(hdr.flags); if(cab->flags & 0x0004) { if(cli_readn(fd, &hdr_opt, sizeof(hdr_opt)) != sizeof(hdr_opt)) { cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n"); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } @@ -281,7 +276,6 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) if(cab->reshdr) { if(lseek(fd, cab->reshdr, SEEK_CUR) == -1) { cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } } @@ -292,8 +286,8 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) pt = cab_readstr(fd, &ret); if(ret) return ret; - if(cab_chkname(pt)) - badname = 1; + if(cab_chkname(pt, 0)) + cli_dbgmsg("CAB: Invalid name of preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet name: %s\n", pt); free(pt); @@ -301,8 +295,8 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) pt = cab_readstr(fd, &ret); if(ret) return ret; - if(cab_chkname(pt)) - badname = 1; + if(cab_chkname(pt, 0)) + cli_dbgmsg("CAB: Invalid info for preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet info: %s\n", pt); free(pt); @@ -313,8 +307,8 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) pt = cab_readstr(fd, &ret); if(ret) return ret; - if(cab_chkname(pt)) - badname = 1; + if(cab_chkname(pt, 0)) + cli_dbgmsg("CAB: Invalid name of next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet name: %s\n", pt); free(pt); @@ -322,37 +316,37 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) pt = cab_readstr(fd, &ret); if(ret) return ret; - if(cab_chkname(pt)) - badname = 1; + if(cab_chkname(pt, 0)) + cli_dbgmsg("CAB: Invalid info for next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet info: %s\n", pt); free(pt); } - bscore += badname; - - if(bscore >= 4) { - cli_dbgmsg("CAB: bscore == %u, most likely a fake cabinet\n", bscore); - return CL_EFORMAT; - } /* folders */ for(i = 0; i < cab->nfolders; i++) { if(cli_readn(fd, &folder_hdr, sizeof(folder_hdr)) != sizeof(folder_hdr)) { - cli_errmsg("cab_open: Can't read header for folder %u\n", i); - cab_free(cab); - /* return CL_EIO; */ - return CL_EFORMAT; /* most likely a corrupted file */ + cli_dbgmsg("cab_open: Can't read header for folder %u\n", i); + break; } if(resfold) { if(lseek(fd, resfold, SEEK_CUR) == -1) { - cli_errmsg("cab_open: Can't lseek to %u (resfold)\n", (unsigned int) resfold); - cab_free(cab); - /* return CL_EIO; */ - return CL_EFORMAT; /* most likely a corrupted file */ + cli_dbgmsg("cab_open: Can't lseek to %u (resfold)\n", (unsigned int) resfold); + break; } } + if(EC32(folder_hdr.coffCabStart) + offset > rsize) { + cli_dbgmsg("CAB: Folder out of file\n"); + continue; + } + + if((EC16(folder_hdr.typeCompress) & 0x000f) > 3) { + cli_dbgmsg("CAB: Unknown compression method\n"); + continue; + } + folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder)); if(!folder) { cli_errmsg("cab_open: Can't allocate memory for folder\n"); @@ -362,16 +356,12 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) folder->cab = (struct cab_archive *) cab; folder->offset = (off_t) EC32(folder_hdr.coffCabStart) + offset; - if(folder->offset > rsize) - bscore++; folder->nblocks = EC16(folder_hdr.cCFData); folder->cmethod = EC16(folder_hdr.typeCompress); cli_dbgmsg("CAB: Folder record %u\n", i); cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset); cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod); - if((folder->cmethod & 0x000f) > 3) - bscore++; if(!lfolder) cab->folders = folder; @@ -379,27 +369,20 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) lfolder->next = folder; lfolder = folder; - - if(bscore > 10) { - cab_free(cab); - cli_dbgmsg("CAB: bscore == %u, most likely a fake cabinet\n", bscore); - return CL_EFORMAT; - } + folders++; } + cli_dbgmsg("CAB: Recorded folders: %u\n", folders); /* files */ + if(cab->nfolders != folders && lseek(fd, EC16(hdr.coffFiles), SEEK_SET) == -1) { + cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n"); + cab_free(cab); + return CL_EFORMAT; + } for(i = 0; i < cab->nfiles; i++) { - if(bscore > 10) { - cab_free(cab); - cli_dbgmsg("CAB: bscore == %u, most likely a fake cabinet\n", bscore); - return CL_EFORMAT; - } - if(cli_readn(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { - cli_errmsg("cab_open: Can't read file %u header\n", i); - cab_free(cab); - /* return CL_EIO; */ - return CL_EFORMAT; /* most likely a corrupted file */ + cli_dbgmsg("cab_open: Can't read file %u header\n", i); + break; } file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file)); @@ -411,17 +394,18 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) file->cab = cab; file->fd = fd; - file->length = EC32(file_hdr.cbFile); file->offset = EC32(file_hdr.uoffFolderStart); + file->length = EC32(file_hdr.cbFile); file->attribs = EC32(file_hdr.attribs); fidx = EC32(file_hdr.iFolder); + file->error = CL_SUCCESS; file->name = cab_readstr(fd, &ret); if(ret) { free(file); - cab_free(cab); - return ret; + continue; } + cab_chkname(file->name, 1); cli_dbgmsg("CAB: File record %u\n", i); cli_dbgmsg("CAB: File name: %s\n", file->name); @@ -444,9 +428,7 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) /* folder index */ if(fidx < 0xfffd) { if(fidx > cab->nfolders) { - if(bscore < 3) - cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name); - bscore++; + cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name); free(file->name); free(file); continue; @@ -457,11 +439,10 @@ int cab_open(int fd, off_t offset, struct cab_archive *cab) file->folder = file->folder->next; if(!file->folder) { - cli_errmsg("cab_open: Folder not found for file %s\n", file->name); + cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name); free(file->name); free(file); - cab_free(cab); - return CL_EFORMAT; + continue; } } else { @@ -490,13 +471,11 @@ static int cab_read_block(int fd, struct cab_state *state, uint16_t resdata) if(cli_readn(fd, &block_hdr, sizeof(block_hdr)) != sizeof(block_hdr)) { cli_dbgmsg("cab_read_block: Can't read block header\n"); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } if(resdata && lseek(fd, (off_t) resdata, SEEK_CUR) == -1) { cli_dbgmsg("cab_read_block: lseek failed\n"); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } @@ -515,7 +494,6 @@ static int cab_read_block(int fd, struct cab_state *state, uint16_t resdata) if(cli_readn(fd, state->block, state->blklen) != state->blklen) { cli_dbgmsg("cab_read_block: Can't read block data\n"); - /* return CL_EIO; */ return CL_EFORMAT; /* most likely a corrupted file */ } @@ -530,6 +508,11 @@ static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes) uint16_t todo, left; + if((file->cab->state->blknum > file->folder->nblocks) && !file->lread) { + file->error = CL_BREAK; + return -1; + } + todo = bytes; while(todo > 0) { left = file->cab->state->end - file->cab->state->pt; @@ -544,10 +527,8 @@ static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes) todo -= left; } else { - if(file->cab->state->blknum++ >= file->folder->nblocks) { - file->error = CL_EFORMAT; + if(file->cab->state->blknum++ >= file->folder->nblocks) break; - } file->error = cab_read_block(file->fd, file->cab->state, file->cab->resdata); if(file->error) @@ -568,7 +549,7 @@ static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes) } } - return bytes - todo; + return file->lread = bytes - todo; } static int cab_unstore(struct cab_file *file, int bytes) @@ -721,11 +702,14 @@ int cab_extract(struct cab_file *file, const char *name) break; default: - cli_warnmsg("CAB: Not supported compression method: 0x%x\n", file->folder->cmethod & 0x000f); + cli_dbgmsg("CAB: Not supported compression method: 0x%x\n", file->folder->cmethod & 0x000f); ret = CL_EFORMAT; } close(file->ofd); + if(ret == CL_BREAK) + ret = CL_SUCCESS; + return ret; } diff --git a/libclamav/cab.h b/libclamav/cab.h index abcadcd37..9704ca732 100644 --- a/libclamav/cab.h +++ b/libclamav/cab.h @@ -54,6 +54,7 @@ struct cab_file { char *name; uint32_t length; int error; + int lread; int fd; int ofd; struct cab_folder *folder; diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c index 00c7c1937..34d9cc36c 100644 --- a/libclamav/filetypes.c +++ b/libclamav/filetypes.c @@ -135,7 +135,7 @@ int is_tar(unsigned char *buf, unsigned int nbytes); cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) { - unsigned char smallbuff[MAGIC_BUFFER_SIZE + 1], *decoded, *bigbuff; + unsigned char buff[MAGIC_BUFFER_SIZE + 1], *decoded; int bread, sret; cli_file_t ret = CL_TYPE_BINARY_DATA; struct cli_matcher *root; @@ -147,12 +147,13 @@ cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) return CL_TYPE_ERROR; } - memset(smallbuff, 0, sizeof(smallbuff)); - bread = cli_readn(desc, smallbuff, MAGIC_BUFFER_SIZE); + memset(buff, 0, sizeof(buff)); + bread = cli_readn(desc, buff, MAGIC_BUFFER_SIZE); if(bread == -1) return CL_TYPE_ERROR; + buff[bread] = 0; - ret = cli_filetype(smallbuff, bread, engine); + ret = cli_filetype(buff, bread, engine); if(ret >= CL_TYPE_TEXT_ASCII && ret <= CL_TYPE_BINARY_DATA) { /* HTML files may contain special characters and could be @@ -165,7 +166,7 @@ cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) if(cli_ac_initdata(&mdata, root->ac_partsigs, AC_DEFAULT_TRACKLEN)) return ret; - sret = cli_ac_scanbuff(smallbuff, bread, NULL, engine->root[0], &mdata, 0, ret, desc, NULL, AC_SCAN_FT, NULL); + sret = cli_ac_scanbuff(buff, bread, NULL, engine->root[0], &mdata, 0, ret, desc, NULL, AC_SCAN_FT, NULL); cli_ac_freedata(&mdata); @@ -175,7 +176,7 @@ cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) if(cli_ac_initdata(&mdata, root->ac_partsigs, AC_DEFAULT_TRACKLEN)) return ret; - decoded = (unsigned char *) cli_utf16toascii((char *) smallbuff, bread); + decoded = (unsigned char *) cli_utf16toascii((char *) buff, bread); if(decoded) { sret = cli_ac_scanbuff(decoded, strlen((char *) decoded), NULL, engine->root[0], &mdata, 0, CL_TYPE_TEXT_ASCII, desc, NULL, AC_SCAN_FT, NULL); free(decoded); @@ -190,11 +191,11 @@ cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) /* check if we can autodetect this encoding. * If we can't don't try to detect HTML sig, since * we just tried that above, and failed */ - if((encoding = encoding_detect_bom(smallbuff, bread))) { - unsigned char decodedbuff[sizeof(smallbuff)*2]; + if((encoding = encoding_detect_bom(buff, bread))) { + unsigned char decodedbuff[sizeof(buff)*2]; m_area_t in_area, out_area; - in_area.buffer = (unsigned char *) smallbuff; + in_area.buffer = (unsigned char *) buff; in_area.length = bread; in_area.offset = 0; out_area.buffer = decodedbuff; @@ -227,38 +228,16 @@ cli_file_t cli_filetype2(int desc, const struct cl_engine *engine) } if(ret == CL_TYPE_BINARY_DATA) { - - if(!(bigbuff = (unsigned char *) cli_calloc(37638 + 1, sizeof(unsigned char)))) - return ret; - - lseek(desc, 0, SEEK_SET); - if((bread = cli_readn(desc, bigbuff, 37638)) > 0) { - - bigbuff[bread] = 0; - - switch(is_tar(bigbuff, bread)) { - case 1: - ret = CL_TYPE_OLD_TAR; - cli_dbgmsg("Recognized old fashioned tar file\n"); - break; - case 2: - ret = CL_TYPE_POSIX_TAR; - cli_dbgmsg("Recognized POSIX tar file\n"); - break; - } + switch(is_tar(buff, bread)) { + case 1: + ret = CL_TYPE_OLD_TAR; + cli_dbgmsg("Recognized old fashioned tar file\n"); + break; + case 2: + ret = CL_TYPE_POSIX_TAR; + cli_dbgmsg("Recognized POSIX tar file\n"); + break; } - - if(ret == CL_TYPE_BINARY_DATA) { - if(!memcmp(bigbuff + 32769, "CD001" , 5) || !memcmp(bigbuff + 37633, "CD001" , 5)) { - cli_dbgmsg("Recognized ISO 9660 CD-ROM data\n"); - ret = CL_TYPE_IGNORED; - } else if(!memcmp(bigbuff + 32776, "CDROM" , 5)) { - cli_dbgmsg("Recognized High Sierra CD-ROM data\n"); - ret = CL_TYPE_IGNORED; - } - } - - free(bigbuff); } return ret; diff --git a/libclamav/hashtab.c b/libclamav/hashtab.c index e298aa7bc..c198d9fd4 100644 --- a/libclamav/hashtab.c +++ b/libclamav/hashtab.c @@ -315,6 +315,7 @@ int hashtab_insert(struct hashtable *s, const char* key, const size_t len, const if(!thekey) return CL_EMEM; strncpy(thekey, key, len+1); + thekey[len]='\0'; element->key = thekey; element->data = data; element->len = len; @@ -557,3 +558,26 @@ ssize_t hashset_toarray(const struct hashset* hs, uint32_t** array) } return j; } + +struct uniq *uniq_init(uint32_t count) { + struct uniq *U; + if(!count) return NULL; + count = count <= 256 ? 256 : count + (count*20/100); + U = (struct uniqueid *)cli_calloc(1, sizeof(U)+sizeof(uint32_t)*count); + if(!U) return NULL; + U->count = count; + return U; +} + +uint32_t uniq_add(struct uniq *U, const char *key, uint32_t key_len, uint32_t *rhash) { + uint32_t h = hash((const unsigned char *)key, key_len, U->count); + if(rhash) *rhash = h; + return U->uniques[h]++; +} + +uint32_t uniq_get(struct uniq *U, const char *key, uint32_t key_len, uint32_t *rhash) { + uint32_t h = hash((const unsigned char *)key, key_len, U->count); + if(rhash) *rhash = h; + return U->uniques[h]; +} + diff --git a/libclamav/hashtab.h b/libclamav/hashtab.h index 13483566d..1829cde54 100644 --- a/libclamav/hashtab.h +++ b/libclamav/hashtab.h @@ -20,11 +20,11 @@ * MA 02110-1301, USA. */ -#include -#include #ifndef _HASHTAB_H #define _HASHTAB_H - +#include +#include +#include "cltypes.h" typedef long element_data; /* define this for debugging/profiling purposes only, NOT in production/release code */ @@ -105,5 +105,17 @@ int hashset_clear(struct hashset* hs); void hashset_destroy(struct hashset* hs); ssize_t hashset_toarray(const struct hashset* hs, uint32_t** array); + +/* A basic storage for unique IDs */ +struct uniq { + uint32_t count; + uint32_t uniques[]; +}; + +struct uniq *uniq_init(uint32_t); +#define uniq_free(X) free(X); +uint32_t uniq_add(struct uniq *, const char *, uint32_t, uint32_t *); +uint32_t uniq_get(struct uniq *, const char *, uint32_t, uint32_t *); + #endif diff --git a/libclamav/htmlnorm.c b/libclamav/htmlnorm.c index e0f8090bc..d34af38bf 100644 --- a/libclamav/htmlnorm.c +++ b/libclamav/htmlnorm.c @@ -555,7 +555,7 @@ static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag /* this will still contains scripts that are inside comments */ snprintf(filename, 1024, "%s/nocomment.html", dirname); file_buff_o2->fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); - if (!file_buff_o2->fd) { + if (file_buff_o2->fd == -1) { cli_dbgmsg("open failed: %s\n", filename); free(file_buff_o2); file_buff_o2 = file_buff_text = NULL; @@ -564,6 +564,7 @@ static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag file_buff_text = (file_buff_t *) cli_malloc(sizeof(file_buff_t)); if(!file_buff_text) { + close(file_buff_o2->fd); free(file_buff_o2); file_buff_o2 = file_buff_text = NULL; goto abort; @@ -571,12 +572,13 @@ static int cli_html_normalise(int fd, m_area_t *m_area, const char *dirname, tag snprintf(filename, 1024, "%s/notags.html", dirname); file_buff_text->fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); - if(!file_buff_text->fd) { + if(file_buff_text->fd == -1) { cli_dbgmsg("open failed: %s\n", filename); close(file_buff_o2->fd); free(file_buff_o2); free(file_buff_text); file_buff_o2 = file_buff_text = NULL; + goto abort; } file_buff_o2->length = 0; file_buff_text->length = 0; diff --git a/libclamav/libclamav.map b/libclamav/libclamav.map index 0a0f7ab26..1d437c8f5 100644 --- a/libclamav/libclamav.map +++ b/libclamav/libclamav.map @@ -74,6 +74,7 @@ CLAMAV_PRIVATE { cli_warnmsg; cli_strtokbuf; cli_leavetemps_flag; + uniq_get; local: *; }; diff --git a/libclamav/matcher-ac.c b/libclamav/matcher-ac.c index 1316babfe..ad56f9125 100644 --- a/libclamav/matcher-ac.c +++ b/libclamav/matcher-ac.c @@ -1079,6 +1079,7 @@ int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hex return CL_EMEM; } strncpy(new->virname, virname, namelen); + new->virname[namelen]='\0'; if(offset) { new->offset = cli_strdup(offset); diff --git a/libclamav/mspack.c b/libclamav/mspack.c index 795508318..a3547b784 100644 --- a/libclamav/mspack.c +++ b/libclamav/mspack.c @@ -104,7 +104,7 @@ static const unsigned short mszip_bit_mask_tab[17] = { if (mszip_read_input(zip)) return zip->error; \ i_ptr = zip->i_ptr; \ i_end = zip->i_end; \ - if(i_ptr == i_end) return CL_EFORMAT; \ + if(i_ptr == i_end) break; \ } \ bit_buffer |= *i_ptr++ << bits_left; bits_left += 8; \ } \ @@ -125,7 +125,12 @@ static const unsigned short mszip_bit_mask_tab[17] = { static int mszip_read_input(struct mszip_stream *zip) { int nread = zip->read_cb ? zip->read_cb(zip->file, zip->inbuf, (int)zip->inbuf_size) : cli_readn(zip->fd, zip->inbuf, (int)zip->inbuf_size); - if (nread < 0) return zip->error = CL_EFORMAT; + if (nread < 0) { + if (zip->file->error == CL_BREAK) + return zip->error = CL_BREAK; + else + return zip->error = CL_EFORMAT; + } zip->i_ptr = &zip->inbuf[0]; zip->i_end = &zip->inbuf[nread]; @@ -749,7 +754,12 @@ void mszip_free(struct mszip_stream *zip) { static int lzx_read_input(struct lzx_stream *lzx) { int bread = lzx->read_cb ? lzx->read_cb(lzx->file, &lzx->inbuf[0], (int)lzx->inbuf_size) : cli_readn(lzx->fd, &lzx->inbuf[0], (int)lzx->inbuf_size); - if (bread < 0) return lzx->error = CL_EFORMAT; + if (bread < 0) { + if (lzx->file->error == CL_BREAK) + return lzx->error = CL_BREAK; + else + return lzx->error = CL_EFORMAT; + } /* huff decode's ENSURE_BYTES(16) might overrun the input stream, even * if those bits aren't used, so fake 2 more bytes */ @@ -1577,7 +1587,12 @@ void lzx_free(struct lzx_stream *lzx) { static int qtm_read_input(struct qtm_stream *qtm) { int nread = qtm->read_cb ? qtm->read_cb(qtm->file, &qtm->inbuf[0], (int)qtm->inbuf_size) : cli_readn(qtm->fd, &qtm->inbuf[0], (int)qtm->inbuf_size); - if (nread < 0) return qtm->error = CL_EFORMAT; + if (nread < 0) { + if (qtm->file->error == CL_BREAK) + return qtm->error = CL_BREAK; + else + return qtm->error = CL_EFORMAT; + } qtm->i_ptr = &qtm->inbuf[0]; qtm->i_end = &qtm->inbuf[nread]; diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index ebbd92143..4e7a156af 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -47,9 +47,10 @@ #include "cltypes.h" #include "others.h" #include "ole2_extract.h" +#include "scanners.h" +#include "hashtab.h" #include "mbox.h" -#include "blob.h" /* sanitiseName() */ #define ole2_endian_convert_16(v) le16_to_host((uint16_t)(v)) #define ole2_endian_convert_32(v) le32_to_host((uint32_t)(v)) @@ -70,6 +71,7 @@ #define O_BINARY 0 #endif + typedef struct ole2_header_tag { unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */ @@ -102,8 +104,11 @@ typedef struct ole2_header_tag unsigned char *m_area; off_t m_length; bitset_t *bitset; + struct uniq *U; + int has_vba; } ole2_header_t; + typedef struct property_tag { char name[64]; /* in unicode */ @@ -136,7 +141,8 @@ typedef struct property_tag static unsigned char magic_id[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}; -static char *get_property_name(char *name, int size) + +static char *get_property_name2(char *name, int size) { int i, j; char *newname; @@ -153,7 +159,7 @@ static char *get_property_name(char *name, int size) /* size-2 to ignore trailing NULL */ for (i=0 ; i < size-2; i+=2) { if((!(name[i]&0x80)) && isprint(name[i])) { - newname[j++] = name[i]; + newname[j++] = tolower(name[i]); } else { if (name[i] < 10 && name[i] >= 0) { newname[j++] = '_'; @@ -179,50 +185,72 @@ static char *get_property_name(char *name, int size) return newname; } -static void print_property_name(char *pname, int size) -{ - char *name; - - name = get_property_name(pname, size); - if (!name) { - return; - } - cli_dbgmsg("%34s ", name); - free(name); - return; +static char *get_property_name(char *name, int size) { + const char *carray = "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz._"; + int csize = size>>1; + char *newname, *cname; + char *oname = name; + + if (csize<=0) return NULL; + + newname = cname = (char *)cli_malloc(size); + if (!newname) return NULL; + + while(--csize) { + uint16_t lo, hi, u=cli_readint16(oname)-0x3800; + oname+=2; + if (u > 0x1040) { + free(newname); + return get_property_name2(name, size); + } + lo = u % 64; + u >>= 6; + hi = u % 64; + *cname++=carray[lo]; + if(csize!=1 || u!= 64) *cname++=carray[hi]; + } + *cname='\0'; + return newname; } + static void print_ole2_property(property_t *property) { + char spam[128], *buf; if (property->name_size > 64) { cli_dbgmsg("[err name len: %d]\n", property->name_size); return; } - print_property_name(property->name, property->name_size); + buf = get_property_name(property->name, property->name_size); + snprintf(spam, sizeof(spam), "OLE2: %s ", buf ? buf : ""); + spam[sizeof(spam)-1]='\0'; + if (buf) free(buf); switch (property->type) { case 2: - cli_dbgmsg(" [file] "); + strncat(spam, " [file] ", sizeof(spam) - 1 - strlen(spam)); break; case 1: - cli_dbgmsg(" [dir ] "); + strncat(spam, " [dir ] ", sizeof(spam) - 1 - strlen(spam)); break; case 5: - cli_dbgmsg(" [root] "); + strncat(spam, " [root] ", sizeof(spam) - 1 - strlen(spam)); break; default: - cli_dbgmsg(" [%d]", property->type); + strncat(spam, " [unkn] ", sizeof(spam) - 1 - strlen(spam)); } + spam[sizeof(spam)-1]='\0'; switch (property->color) { case 0: - cli_dbgmsg(" r "); + strncat(spam, " r ", sizeof(spam) - 1 - strlen(spam)); break; case 1: - cli_dbgmsg(" b "); + strncat(spam, " b ", sizeof(spam) - 1 - strlen(spam)); break; default: - cli_dbgmsg(" u "); + strncat(spam, " u ", sizeof(spam) - 1 - strlen(spam)); } - cli_dbgmsg(" 0x%.8x 0x%.8x\n", property->size, property->user_flags); + spam[sizeof(spam)-1]='\0'; + cli_dbgmsg("%s size:0x%.8x flags:0x%.8x\n", spam, property->size, property->user_flags); } static void print_ole2_header(ole2_header_t *hdr) @@ -461,44 +489,45 @@ static void ole2_read_property_tree(int fd, ole2_header_t *hdr, const char *dir, } */ -static void ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, int32_t prop_index, - int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir), - unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize) +static int ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, int32_t prop_index, + int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx), + unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize) { property_t prop_block[4]; int32_t idx, current_block, i; char *dirname; + int ret; const struct cl_limits *limits = ctx ? ctx->limits : NULL; current_block = hdr->prop_start; if ((prop_index < 0) || (prop_index > (int32_t) hdr->max_block_no) || (rec_level > 100) || (*file_count > 100000)) { - return; + return CL_SUCCESS; } if (limits && limits->maxfiles && (*file_count > limits->maxfiles)) { cli_dbgmsg("OLE2: File limit reached (max: %d)\n", limits->maxfiles); - return; + return CL_SUCCESS; } if (limits && limits->maxreclevel && (rec_level > limits->maxreclevel)) { cli_dbgmsg("OLE2: Recursion limit reached (max: %d)\n", limits->maxreclevel); - return; + return CL_SUCCESS; } idx = prop_index / 4; for (i=0 ; i < idx ; i++) { current_block = ole2_get_next_block_number(fd, hdr, current_block); if (current_block < 0) { - return; + return CL_SUCCESS; } } idx = prop_index % 4; if (!ole2_read_block(fd, hdr, prop_block, current_block)) { - return; + return CL_SUCCESS; } if (prop_block[idx].type <= 0) { - return; + return CL_SUCCESS; } prop_block[idx].name_size = ole2_endian_convert_16(prop_block[idx].name_size); prop_block[idx].prev = ole2_endian_convert_32(prop_block[idx].prev); @@ -512,16 +541,16 @@ static void ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, prop_block[idx].start_block = ole2_endian_convert_32(prop_block[idx].start_block); prop_block[idx].size = ole2_endian_convert_32(prop_block[idx].size); - print_ole2_property(&prop_block[idx]); + if (dir) print_ole2_property(&prop_block[idx]); /* Check we aren't in a loop */ if (cli_bitset_test(hdr->bitset, (unsigned long) prop_index)) { /* Loop in property tree detected */ cli_dbgmsg("OLE2: Property tree loop detected at index %d\n", prop_index); - return; + return CL_BREAK; } if (!cli_bitset_set(hdr->bitset, (unsigned long) prop_index)) { - return; + return CL_SUCCESS; } switch (prop_block[idx].type) { @@ -530,162 +559,143 @@ static void ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, (*file_count != 0)) { /* Can only have RootEntry as the top */ cli_dbgmsg("ERROR: illegal Root Entry\n"); - return; + return CL_SUCCESS; } hdr->sbat_root_start = prop_block[idx].start_block; - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize); + if ( + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + ) return ret; break; case 2: /* File */ if (limits && limits->maxfiles && ctx->scannedfiles + *file_count > limits->maxfiles) { - cli_dbgmsg("ole2: files limit reached (max: %u)\n", ctx->limits->maxfiles); - break; + cli_dbgmsg("OLE2: files limit reached (max: %u)\n", ctx->limits->maxfiles); + return CL_BREAK; } if (!limits || !limits->maxfilesize || prop_block[idx].size <= limits->maxfilesize || *scansize == -1 || prop_block[idx].size <= *scansize) { (*file_count)++; *scansize-=prop_block[idx].size; - if (!handler(fd, hdr, &prop_block[idx], dir)) { - cli_dbgmsg("ERROR: handler failed\n"); - /* If we don't return on this error then - we can sometimes pull VBA code - from corrupted files. - */ - - } + if ((ret=handler(fd, hdr, &prop_block[idx], dir, ctx)) != CL_SUCCESS) + return ret; } else { - cli_dbgmsg("ole2: filesize exceeded\n"); + cli_dbgmsg("OLE2: filesize exceeded\n"); } - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].prev, handler, rec_level, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].next, handler, rec_level, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].child, handler, rec_level, file_count, ctx, scansize); + if ( + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS + ) return ret; break; case 1: /* Directory */ - dirname = (char *) cli_malloc(strlen(dir)+8); - if (!dirname) { - return; - } - snprintf(dirname, strlen(dir)+8, "%s/%.6d", dir, prop_index); - if (mkdir(dirname, 0700) != 0) { - free(dirname); - return; - } - cli_dbgmsg("OLE2 dir entry: %s\n",dirname); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dir, - prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize); - ole2_walk_property_tree(fd, hdr, dirname, - prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize); - free(dirname); + if (dir) { + dirname = (char *) cli_malloc(strlen(dir)+8); + if (!dirname) return CL_BREAK; + snprintf(dirname, strlen(dir)+8, "%s/%.6d", dir, prop_index); + if (mkdir(dirname, 0700) != 0) { + free(dirname); + return CL_BREAK; + } + cli_dbgmsg("OLE2 dir entry: %s\n",dirname); + } else dirname = NULL; + if ( + (ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + || + (ret=ole2_walk_property_tree(fd, hdr, dirname, prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS + ) {} + if (dirname) free(dirname); + return ret; break; default: cli_dbgmsg("ERROR: unknown OLE2 entry type: %d\n", prop_block[idx].type); break; } - return; + return CL_SUCCESS; } /* Write file Handler - write the contents of the entry to a file */ -static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const char *dir) +static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx) { unsigned char *buff; int32_t current_block, ofd, len, offset; - char *name, *newname; + char *name, newname[1024]; bitset_t *blk_bitset; + uint32_t hash, cnt; if (prop->type != 2) { /* Not a file */ - return TRUE; + return CL_SUCCESS; } if (prop->name_size > 64) { - cli_dbgmsg("\nERROR: property name too long: %d\n", prop->name_size); - return FALSE; + cli_dbgmsg("OLE2 [handler_writefile]: property name too long: %d\n", prop->name_size); + return CL_SUCCESS; } - if (! (name = get_property_name(prop->name, prop->name_size))) { - /* File without a name - create a name for it */ - off_t i; - - i = lseek(fd, 0, SEEK_CUR); - name = (char *) cli_malloc(11); - if (!name) { - return FALSE; - } - snprintf(name, 11, "%.10ld", (long int) (i + (long int) prop)); - } else { - /* Sanitize the file name */ - sanitiseName(name); - } - - newname = (char *) cli_malloc(strlen(name) + strlen(dir) + 2); - if (!newname) { - free(name); - return FALSE; - } - - sprintf(newname, "%s/%s", dir, name); - free(name); + name = get_property_name2(prop->name, prop->name_size); + if (name) cnt = uniq_add(hdr->U, name, strlen(name), &hash); + else cnt = uniq_add(hdr->U, NULL, 0, &hash); + snprintf(newname, sizeof(newname), "%s/%u_%u", dir, hash, cnt); + newname[sizeof(newname)-1]='\0'; + cli_dbgmsg("OLE2 [handler_writefile]: Dumping '%s' to '%s'\n", name ? name : "", newname); + if (name) free(name); ofd = open(newname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU); if (ofd < 0) { - cli_errmsg("ERROR: failed to create file: %s\n", newname); - free(newname); - return FALSE; + cli_errmsg("OLE2 [handler_writefile]: failed to create file: %s\n", newname); + return CL_SUCCESS; } - free(newname); current_block = prop->start_block; len = prop->size; buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size); if (!buff) { close(ofd); - return FALSE; + return CL_BREAK; } blk_bitset = cli_bitset_init(); if (!blk_bitset) { - cli_errmsg("ERROR [handler_writefile]: init bitset failed\n"); + cli_errmsg("OLE2 [handler_writefile]: init bitset failed\n"); close(ofd); - return FALSE; + return CL_BREAK; } while((current_block >= 0) && (len > 0)) { if (current_block > (int32_t) hdr->max_block_no) { - cli_dbgmsg("OLE2: Max block number for file size exceeded: %d\n", current_block); + cli_dbgmsg("OLE2 [handler_writefile]: Max block number for file size exceeded: %d\n", current_block); close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_SUCCESS; } /* Check we aren't in a loop */ if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) { /* Loop in block list */ - cli_dbgmsg("OLE2: Block list loop detected\n"); + cli_dbgmsg("OLE2 [handler_writefile]: Block list loop detected\n"); close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_BREAK; } if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) { close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_BREAK; } if (prop->size < (int64_t)hdr->sbat_cutoff) { /* Small block file */ if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) { - cli_dbgmsg("ole2_get_sbat_data_block failed\n"); + cli_dbgmsg("OLE2 [handler_writefile]: ole2_get_sbat_data_block failed\n"); close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_SUCCESS; } /* buff now contains the block with 8 small blocks in it */ offset = 64 * (current_block % 8); @@ -693,7 +703,7 @@ static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_BREAK; } len -= MIN(len,64); @@ -704,14 +714,14 @@ static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_SUCCESS; } if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) != MIN(len,(1 << hdr->log2_big_block_size))) { close(ofd); free(buff); cli_bitset_free(blk_bitset); - return FALSE; + return CL_BREAK; } current_block = ole2_get_next_block_number(fd, hdr, current_block); @@ -721,7 +731,188 @@ static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const close(ofd); free(buff); cli_bitset_free(blk_bitset); - return TRUE; + return CL_SUCCESS; +} + +/* enum file Handler - checks for VBA presence */ +static int handler_enum(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx) +{ + char *name; + + if (!hdr->has_vba) { + name = get_property_name2(prop->name, prop->name_size); + if (name) { + if (!strcmp(name, "_vba_project") || !strcmp(name, "powerpoint document") || !strcmp(name, "worddocument") || !strcmp(name, "_1_ole10native")) + hdr->has_vba = 1; + free(name); + } + } + return CL_SUCCESS; +} + + +static int handler_otf(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx) +{ + char *tempfile; + unsigned char *buff; + int32_t current_block, len, offset; + int ofd, ret; + bitset_t *blk_bitset; + + if (prop->type != 2) { + /* Not a file */ + return CL_SUCCESS; + } + + print_ole2_property(prop); + + if(!(tempfile = cli_gentemp(NULL))) + return CL_EMEM; + + if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { + cli_dbgmsg("OLE2: Can't create file %s\n", tempfile); + free(tempfile); + return CL_EIO; + } + + current_block = prop->start_block; + len = prop->size; + + buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size); + if (!buff) { + close(ofd); + cli_unlink(tempfile); + free(tempfile); + return CL_EMEM; + } + + blk_bitset = cli_bitset_init(); + + if (!blk_bitset) { + cli_errmsg("OLE2: OTF handler init bitset failed\n"); + free(buff); + close(ofd); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_BREAK; + } + + while((current_block >= 0) && (len > 0)) { + if (current_block > (int32_t) hdr->max_block_no) { + cli_dbgmsg("OLE2: Max block number for file size exceeded: %d\n", current_block); + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_SUCCESS; + } + /* Check we aren't in a loop */ + if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) { + /* Loop in block list */ + cli_dbgmsg("OLE2: Block list loop detected\n"); + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_BREAK; + } + if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) { + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_BREAK; + } + if (prop->size < (int64_t)hdr->sbat_cutoff) { + /* Small block file */ + if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) { + cli_dbgmsg("ole2_get_sbat_data_block failed\n"); + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_SUCCESS; + } + /* buff now contains the block with 8 small blocks in it */ + offset = 64 * (current_block % 8); + if (cli_writen(ofd, &buff[offset], MIN(len,64)) != MIN(len,64)) { + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_BREAK; + } + + len -= MIN(len,64); + current_block = ole2_get_next_sbat_block(fd, hdr, current_block); + } else { + /* Big block file */ + if (!ole2_read_block(fd, hdr, buff, current_block)) { + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_SUCCESS; + } + if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) != + MIN(len,(1 << hdr->log2_big_block_size))) { + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + free(tempfile); + return CL_BREAK; + } + + current_block = ole2_get_next_block_number(fd, hdr, current_block); + len -= MIN(len,(1 << hdr->log2_big_block_size)); + } + } + + lseek(ofd, 0, SEEK_SET); + ret=cli_magic_scandesc(ofd, ctx); + close(ofd); + free(buff); + cli_bitset_free(blk_bitset); + if(!cli_leavetemps_flag) { + if (cli_unlink(tempfile)) { + free(tempfile); + return CL_EIO; + } + } + free(tempfile); + return ret==CL_VIRUS ? CL_VIRUS : CL_SUCCESS; + } #if !defined(HAVE_ATTRIB_PACKED) && !defined(HAVE_PRAGMA_PACK) && !defined(HAVE_PRAGMA_PACK_HPPA) @@ -786,13 +977,13 @@ static int ole2_read_header(int fd, ole2_header_t *hdr) } #endif -int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx) +int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx, struct uniq **vba) { ole2_header_t hdr; - int hdr_size; + int hdr_size, ret=CL_CLEAN; struct stat statbuf; unsigned int file_count=0; - unsigned long scansize; + unsigned long scansize, scansize2; cli_dbgmsg("in cli_ole2_extract()\n"); @@ -802,17 +993,19 @@ int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx) else return CL_EMAXSIZE; } else scansize = -1; + + scansize2 = scansize; /* size of header - size of other values in struct */ - hdr_size = sizeof(struct ole2_header_tag) - sizeof(int32_t) - + hdr_size = sizeof(struct ole2_header_tag) - sizeof(int32_t) - sizeof(uint32_t) - sizeof(unsigned char *) - sizeof(off_t) - sizeof(bitset_t *) - - sizeof(uint32_t); + sizeof(struct uniq *) - sizeof(int); hdr.m_area = NULL; if (fstat(fd, &statbuf) == 0) { if (statbuf.st_size < hdr_size) { - return 0; + return CL_CLEAN; } #ifdef HAVE_MMAP hdr.m_length = statbuf.st_size; @@ -827,13 +1020,14 @@ int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx) } if (hdr.m_area == NULL) { + hdr.bitset = NULL; #if defined(HAVE_ATTRIB_PACKED) || defined(HAVE_PRAGMA_PACK) || defined(HAVE_PRAGMA_PACK_HPPA) if (cli_readn(fd, &hdr, hdr_size) != hdr_size) { - return 0; + goto abort; } #else if (!ole2_read_header(fd, &hdr)) { - return 0; + goto abort; } #endif } @@ -854,19 +1048,14 @@ int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx) hdr.sbat_root_start = -1; hdr.bitset = cli_bitset_init(); - if (!hdr.bitset) { + if (!hdr.bitset) { /* FIXME: mmap leaks here */ return CL_EOLE2; } if (memcmp(hdr.magic, magic_id, 8) != 0) { cli_dbgmsg("OLE2 magic failed!\n"); -#ifdef HAVE_MMAP - if (hdr.m_area != NULL) { - munmap(hdr.m_area, hdr.m_length); - } -#endif - cli_bitset_free(hdr.bitset); - return CL_EOLE2; + ret=CL_EOLE2; + goto abort; } if (hdr.log2_big_block_size != 9) { @@ -894,7 +1083,35 @@ int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx) /* OR */ - ole2_walk_property_tree(fd, &hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize); + + /* PASS 1 : Count files and check for VBA */ + // __asm__ __volatile__("int3"); + hdr.has_vba = 0; + ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize); + cli_bitset_free(hdr.bitset); + hdr.bitset = NULL; + if (!file_count || !(hdr.bitset = cli_bitset_init())) + goto abort; + + /* If there's no VBA we scan OTF */ + if (hdr.has_vba) { + /* PASS 2/A : VBA scan */ + cli_dbgmsg("OLE2: VBA project found\n"); + if (!(hdr.U = uniq_init(file_count))) { + cli_dbgmsg("OLE2: uniq_init() failed\n"); + ret = CL_EMEM; + goto abort; + } + file_count = 0; + ole2_walk_property_tree(fd, &hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2); + ret = CL_CLEAN; + *vba = hdr.U; + } else { + cli_dbgmsg("OLE2: no VBA projects found %d\n", ret); + /* PASS 2/B : OTF scan */ + file_count = 0; + ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2); + } abort: #ifdef HAVE_MMAP @@ -902,6 +1119,8 @@ abort: munmap(hdr.m_area, hdr.m_length); } #endif - cli_bitset_free(hdr.bitset); - return 0; + if(hdr.bitset) + cli_bitset_free(hdr.bitset); + + return ret; } diff --git a/libclamav/ole2_extract.h b/libclamav/ole2_extract.h index 395a06e76..37265aa47 100644 --- a/libclamav/ole2_extract.h +++ b/libclamav/ole2_extract.h @@ -24,7 +24,8 @@ #define __OLE2_EXTRACT_H #include "others.h" +#include "hashtab.h" -int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx); +int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx, struct uniq **); #endif diff --git a/libclamav/others.c b/libclamav/others.c index 37d622773..6d0972901 100644 --- a/libclamav/others.c +++ b/libclamav/others.c @@ -99,6 +99,7 @@ static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, int len = sizeof(x) - 1; \ char buff[BUFSIZ]; \ strncpy(buff, x, len); \ + buff[BUFSIZ-1]='\0'; \ va_start(args, str); \ vsnprintf(buff + len, sizeof(buff) - len, str, args); \ buff[sizeof(buff) - 1] = '\0'; \ diff --git a/libclamav/pdf.c b/libclamav/pdf.c index 36587a87e..092e76e31 100644 --- a/libclamav/pdf.c +++ b/libclamav/pdf.c @@ -79,7 +79,7 @@ static const char *cli_pmemstr(const char *haystack, size_t hs, const char *need * TODO: handle embedded URLs if (options&CL_SCAN_MAILURL) */ int -cli_pdf(const char *dir, int desc, cli_ctx *ctx) +cli_pdf(const char *dir, int desc, cli_ctx *ctx, off_t offset) { off_t size; /* total number of bytes in the file */ off_t bytesleft, trailerlength; @@ -99,12 +99,12 @@ cli_pdf(const char *dir, int desc, cli_ctx *ctx) return CL_EOPEN; } - size = statb.st_size; + size = statb.st_size - offset; if(size <= 7) /* doesn't even include the file header */ return CL_CLEAN; - p = buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, desc, 0); + p = buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, desc, offset); if(buf == MAP_FAILED) { cli_errmsg("cli_pdf: mmap() failed\n"); return CL_EMEM; @@ -115,28 +115,24 @@ cli_pdf(const char *dir, int desc, cli_ctx *ctx) /* Lines are terminated by \r, \n or both */ /* File Header */ - if(memcmp(p, "%PDF-1.", 7) != 0) { - munmap(buf, size); - cli_dbgmsg("cli_pdf: file header not found\n"); - return CL_CLEAN; + bytesleft = size - 5; + for(q = p; bytesleft; bytesleft--, q++) { + if(!strncasecmp(q, "%PDF-", 5)) { + bytesleft = size - (off_t) (q - p); + p = q; + break; + } } -#if 0 - q = pdf_nextlinestart(&p[6], size - 6); - if(q == NULL) { - munmap(buf, size); - return CL_CLEAN; + if(!bytesleft) { + munmap(buf, size); + cli_dbgmsg("cli_pdf: file header not found\n"); + return CL_CLEAN; } - bytesleft = size - (long)(q - p); - p = q; -#else - p = &p[6]; - bytesleft = size - 6; -#endif /* Find the file trailer */ - for(q = &p[bytesleft - 6]; q > p; --q) - if(memcmp(q, "%%EOF", 5) == 0) + for(q = &p[bytesleft - 5]; q > p; --q) + if(strncasecmp(q, "%%EOF", 5) == 0) break; if(q <= p) { @@ -868,7 +864,7 @@ cli_pmemstr(const char *haystack, size_t hs, const char *needle, size_t ns) #include "pdf.h" int -cli_pdf(const char *dir, int desc, cli_ctx *ctx) +cli_pdf(const char *dir, int desc, cli_ctx *ctx, off_t offset) { cli_dbgmsg("File not decoded - PDF decoding needs mmap() (for now)\n"); return CL_CLEAN; diff --git a/libclamav/pdf.h b/libclamav/pdf.h index 45219889a..f1423c9c5 100644 --- a/libclamav/pdf.h +++ b/libclamav/pdf.h @@ -22,6 +22,6 @@ #include "others.h" -int cli_pdf(const char *dir, int desc, cli_ctx *ctx); +int cli_pdf(const char *dir, int desc, cli_ctx *ctx, off_t offset); #endif diff --git a/libclamav/readdb.c b/libclamav/readdb.c index 58c14030b..9b167409c 100644 --- a/libclamav/readdb.c +++ b/libclamav/readdb.c @@ -260,6 +260,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex } strncpy(bm_new->virname, virname, virlen); + bm_new->virname[virlen]='\0'; if(offset) { bm_new->offset = cli_strdup(offset); @@ -687,6 +688,7 @@ static int cli_loadftm(FILE *fs, struct cl_engine **engine, unsigned int options if(!ftypes_int[line]) break; strncpy(buffer, ftypes_int[line], sizeof(buffer)); + buffer[sizeof(buffer)-1]='\0'; } else { if(!cli_dbgets(buffer, FILEBUFF, fs, dbio)) break; diff --git a/libclamav/rtf.c b/libclamav/rtf.c index da21e88aa..6ece3dc5e 100644 --- a/libclamav/rtf.c +++ b/libclamav/rtf.c @@ -137,14 +137,12 @@ static void init_rtf_state(struct rtf_state* state) static int compare_state(const struct rtf_state* a,const struct rtf_state* b) { - return (a->controlword_param == b->controlword_param && - a->parse_state == b->parse_state && - a->encounteredTopLevel == b->encounteredTopLevel && - memcmp(a->controlword,b->controlword,33)==0 && - a->cb_begin == b->cb_begin && - a->cb_process == b->cb_process && - a->cb_end == b->cb_end && - a->cb_data == b->cb_data); + return (a->parse_state == b->parse_state && + a->encounteredTopLevel == b->encounteredTopLevel && + a->cb_begin == b->cb_begin && + a->cb_process == b->cb_process && + a->cb_end == b->cb_end && + a->cb_data == b->cb_data); } @@ -161,10 +159,12 @@ static int push_state(struct stack* stack,struct rtf_state* state) } if(stack->stack_cnt >= stack->stack_size) { /* grow stack */ + struct rtf_state *states; stack->stack_size += 128; - stack->states = cli_realloc2(stack->states, stack->stack_size*sizeof(*stack->states)); - if(!stack->states) + states = cli_realloc2(stack->states, stack->stack_size*sizeof(*stack->states)); + if(!states) return CL_EMEM; + stack->states = states; } stack->states[stack->stack_cnt++] = *state; toplevel = state->encounteredTopLevel; @@ -233,17 +233,12 @@ static int rtf_object_begin(struct rtf_state* state,cli_ctx* ctx,const char* tmp static int decode_and_scan(struct rtf_object_data* data, cli_ctx* ctx) { - int ofd, ret=0; + int ret=CL_CLEAN; cli_dbgmsg("RTF:Scanning embedded object:%s\n",data->name); if(data->bread == 1 && data->fd > 0) { cli_dbgmsg("Decoding ole object\n"); - lseek(data->fd,0,SEEK_SET); - ofd = cli_decode_ole_object(data->fd,data->tmpdir); - if (ofd >= 0) { - ret = cli_magic_scandesc(ofd, ctx); - close(ofd); - } + ret = cli_scan_ole10(data->fd, ctx); } else if(data->fd > 0) ret = cli_magic_scandesc(data->fd,ctx); @@ -483,6 +478,8 @@ static void rtf_action(struct rtf_state* state,long action) static void cleanup_stack(struct stack* stack,struct rtf_state* state,cli_ctx* ctx) { + if(!stack || !stack->states) + return; while(stack && stack->stack_cnt /* && state->default_elements*/) { pop_state(stack,state); if(state->cb_data && state->cb_end) diff --git a/libclamav/scanners.c b/libclamav/scanners.c index 728f8cdd5..a5056c443 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -722,9 +722,9 @@ static int cli_scanmscab(int desc, cli_ctx *ctx, off_t sfx_offset) return ret; } -static int cli_vba_scandir(const char *dirname, cli_ctx *ctx) +static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U) { - int ret = CL_CLEAN, i, fd, ofd, data_len; + int ret = CL_CLEAN, i, j, fd, ofd, data_len; vba_project_t *vba_project; DIR *dd; struct dirent *dent; @@ -735,84 +735,88 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx) } result; #endif struct stat statbuf; - char *fname, *fullname; + char *fullname, vbaname[1024]; unsigned char *data; + uint32_t hash, hashcnt; cli_dbgmsg("VBADir: %s\n", dirname); - if((vba_project = (vba_project_t *)cli_vba_readdir(dirname))) { + hashcnt = uniq_get(U, "_vba_project", 12, NULL); + while(hashcnt--) { + if(!(vba_project = (vba_project_t *)cli_vba_readdir(dirname, U, hashcnt))) continue; for(i = 0; i < vba_project->count; i++) { - fullname = (char *) cli_malloc(strlen(vba_project->dir) + strlen(vba_project->name[i]) + 2); - if(!fullname) { - ret = CL_EMEM; - break; - } - sprintf(fullname, "%s/%s", vba_project->dir, vba_project->name[i]); - fd = open(fullname, O_RDONLY|O_BINARY); - if(fd == -1) { - cli_dbgmsg("VBADir: Can't open file %s\n", fullname); - free(fullname); - ret = CL_EOPEN; - break; - } - free(fullname); - cli_dbgmsg("VBADir: Decompress VBA project '%s'\n", vba_project->name[i]); - data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len); - close(fd); + for(j = 0; j < vba_project->colls[i]; j++) { + snprintf(vbaname, 1024, "%s/%u_%u", vba_project->dir, vba_project->name[i], j); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if(fd == -1) continue; + cli_dbgmsg("VBADir: Decompress VBA project '%u_%u'\n", vba_project->name[i], j); + data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len); + close(fd); - if(!data) { - cli_dbgmsg("VBADir: WARNING: VBA project '%s' decompressed to NULL\n", vba_project->name[i]); - } else { - if(ctx->scanned) - *ctx->scanned += data_len / CL_COUNT_PRECISION; - - if(cli_scanbuff(data, data_len, ctx, CL_TYPE_MSOLE2) == CL_VIRUS) { + if(!data) { + cli_dbgmsg("VBADir: WARNING: VBA project '%u_%u' decompressed to NULL\n", vba_project->name[i], j); + } else { + /* cli_dbgmsg("Project content:\n%s", data); */ + if(ctx->scanned) + *ctx->scanned += data_len / CL_COUNT_PRECISION; + if(cli_scanbuff(data, data_len, ctx, CL_TYPE_MSOLE2) == CL_VIRUS) { + free(data); + ret = CL_VIRUS; + break; + } free(data); - ret = CL_VIRUS; - break; } - - free(data); } } - for(i = 0; i < vba_project->count; i++) - free(vba_project->name[i]); free(vba_project->name); + free(vba_project->colls); free(vba_project->dir); free(vba_project->offset); free(vba_project); - } else if ((fullname = cli_ppt_vba_read(dirname))) { - if(cli_scandir(fullname, ctx, 0) == CL_VIRUS) { - ret = CL_VIRUS; - } - if(!cli_leavetemps_flag) - cli_rmdirs(fullname); - free(fullname); - } else if ((vba_project = (vba_project_t *)cli_wm_readdir(dirname))) { - for (i = 0; i < vba_project->count; i++) { - fullname = (char *) cli_malloc(strlen(vba_project->dir) + strlen(vba_project->name[i]) + 2); - if(!fullname) { - ret = CL_EMEM; - break; - } - sprintf(fullname, "%s/%s", vba_project->dir, vba_project->name[i]); - fd = open(fullname, O_RDONLY|O_BINARY); - if(fd == -1) { - cli_dbgmsg("VBADir: Can't open file %s\n", fullname); - free(fullname); - ret = CL_EOPEN; - break; + if (ret == CL_VIRUS) break; + } + + if(ret == CL_CLEAN && (hashcnt = uniq_get(U, "powerpoint document", 19, &hash))) { + while(hashcnt--) { + snprintf(vbaname, 1024, "%s/%u_%u", dirname, hash, hashcnt); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if (fd == -1) continue; + if ((fullname = cli_ppt_vba_read(fd))) { + if(cli_scandir(fullname, ctx, 0) == CL_VIRUS) { + ret = CL_VIRUS; } + if(!cli_leavetemps_flag) + cli_rmdirs(fullname); free(fullname); - cli_dbgmsg("VBADir: Decompress WM project '%s' macro:%d key:%d length:%d\n", vba_project->name[i], i, vba_project->key[i], vba_project->length[i]); - data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], vba_project->key[i]); + } + close(fd); + } + } + + if (ret == CL_CLEAN && (hashcnt = uniq_get(U, "worddocument", 12, &hash))) { + while(hashcnt--) { + snprintf(vbaname, sizeof(vbaname), "%s/%u_%u", dirname, hash, hashcnt); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if (fd == -1) continue; + + if (!(vba_project = (vba_project_t *)cli_wm_readdir(fd))) { close(fd); + continue; + } + + for (i = 0; i < vba_project->count; i++) { + cli_dbgmsg("VBADir: Decompress WM project macro:%d key:%d length:%d\n", i, vba_project->key[i], vba_project->length[i]); + data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], vba_project->key[i]); if(!data) { cli_dbgmsg("VBADir: WARNING: WM project '%s' macro %d decrypted to NULL\n", vba_project->name[i], i); } else { + cli_dbgmsg("Project content:\n%s", data); if(ctx->scanned) *ctx->scanned += vba_project->length[i] / CL_COUNT_PRECISION; if(cli_scanbuff(data, vba_project->length[i], ctx, CL_TYPE_MSOLE2) == CL_VIRUS) { @@ -822,39 +826,43 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx) } free(data); } + } + + close(fd); + free(vba_project->name); + free(vba_project->colls); + free(vba_project->dir); + free(vba_project->offset); + free(vba_project->key); + free(vba_project->length); + free(vba_project); + if(ret == CL_VIRUS) break; } - for(i = 0; i < vba_project->count; i++) - free(vba_project->name[i]); - free(vba_project->key); - free(vba_project->length); - free(vba_project->offset); - free(vba_project->name); - free(vba_project->dir); - free(vba_project); } if(ret != CL_CLEAN) return ret; /* Check directory for embedded OLE objects */ - fullname = (char *) cli_malloc(strlen(dirname) + 16); - if(!fullname) - return CL_EMEM; + hashcnt = uniq_get(U, "_1_ole10native", 14, &hash); + while(hashcnt--) { + snprintf(vbaname, sizeof(vbaname), "%s/%u_%u", dirname, hash, hashcnt); + vbaname[sizeof(vbaname)-1] = '\0'; - sprintf(fullname, "%s/_1_Ole10Native", dirname); - fd = open(fullname, O_RDONLY|O_BINARY); - free(fullname); - if (fd >= 0) { - ofd = cli_decode_ole_object(fd, dirname); - if (ofd >= 0) { - ret = cli_scandesc(ofd, ctx, 0, 0, NULL, AC_SCAN_VIR); - close(ofd); + fd = open(vbaname, O_RDONLY|O_BINARY); + if (fd >= 0) { + ret = cli_scan_ole10(fd, ctx); + close(fd); + if(ret != CL_CLEAN) + return ret; } - close(fd); - if(ret != CL_CLEAN) - return ret; } + + /* ACAB: since we now hash filenames and handle collisions we + * could avoid recursion by removing the block below and by + * flattening the paths in ole2_walk_property_tree (case 1) */ + if((dd = opendir(dirname)) != NULL) { #ifdef HAVE_READDIR_R_3 while(!readdir_r(dd, &result.d, &dent) && dent) { @@ -869,23 +877,23 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx) { if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { /* build the full name */ - fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2); - if(!fname) { + fullname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 2); + if(!fullname) { ret = CL_EMEM; break; } - sprintf(fname, "%s/%s", dirname, dent->d_name); + sprintf(fullname, "%s/%s", dirname, dent->d_name); /* stat the file */ - if(lstat(fname, &statbuf) != -1) { + if(lstat(fullname, &statbuf) != -1) { if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) - if (cli_vba_scandir(fname, ctx) == CL_VIRUS) { + if (cli_vba_scandir(fullname, ctx, U) == CL_VIRUS) { ret = CL_VIRUS; - free(fname); + free(fullname); break; } } - free(fname); + free(fullname); } } } @@ -1086,7 +1094,7 @@ static int cli_scanole2(int desc, cli_ctx *ctx) { char *dir; int ret = CL_CLEAN; - + struct uniq *vba = NULL; cli_dbgmsg("in cli_scanole2()\n"); @@ -1103,25 +1111,26 @@ static int cli_scanole2(int desc, cli_ctx *ctx) return CL_ETMPDIR; } - if((ret = cli_ole2_extract(desc, dir, ctx))) { + ret = cli_ole2_extract(desc, dir, ctx, &vba); + if(ret!=CL_CLEAN && ret!=CL_VIRUS) { cli_dbgmsg("OLE2: %s\n", cl_strerror(ret)); if(!cli_leavetemps_flag) cli_rmdirs(dir); free(dir); - ctx->recursion--; return ret; } - ctx->recursion++; + if (vba) { + ctx->recursion++; - if((ret = cli_vba_scandir(dir, ctx)) != CL_VIRUS) { - if(cli_scandir(dir, ctx, 0) == CL_VIRUS) { - ret = CL_VIRUS; - } + ret = cli_vba_scandir(dir, ctx, vba); + free(vba); + if(ret != CL_VIRUS) + if(cli_scandir(dir, ctx, 0) == CL_VIRUS) + ret = CL_VIRUS; + ctx->recursion--; } - ctx->recursion--; - if(!cli_leavetemps_flag) cli_rmdirs(dir); free(dir); @@ -1380,7 +1389,7 @@ static int cli_scancryptff(int desc, cli_ctx *ctx) return ret; } -static int cli_scanpdf(int desc, cli_ctx *ctx) +static int cli_scanpdf(int desc, cli_ctx *ctx, off_t offset) { int ret; char *dir = cli_gentemp(NULL); @@ -1394,7 +1403,7 @@ static int cli_scanpdf(int desc, cli_ctx *ctx) return CL_ETMPDIR; } - ret = cli_pdf(dir, desc, ctx); + ret = cli_pdf(dir, desc, ctx, offset); if(!cli_leavetemps_flag) cli_rmdirs(dir); @@ -1717,6 +1726,13 @@ static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type, uint8_t typercg, } break; + case CL_TYPE_PDF: + if(SCAN_PDF && (DCONF_DOC & DOC_CONF_PDF)) { + cli_dbgmsg("PDF signature found at %u\n", (unsigned int) fpt->offset); + nret = cli_scanpdf(desc, ctx, fpt->offset); + } + break; + case CL_TYPE_MSEXE: if(SCAN_PE && ctx->dconf->pe && fpt->offset) { cli_dbgmsg("PE signature found at %u\n", (unsigned int) fpt->offset); @@ -1969,7 +1985,7 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx) case CL_TYPE_PDF: /* FIXMELIMITS: pdf should be an archive! */ if(SCAN_PDF && (DCONF_DOC & DOC_CONF_PDF)) - ret = cli_scanpdf(desc, ctx); + ret = cli_scanpdf(desc, ctx, 0); break; case CL_TYPE_CRYPTFF: @@ -2105,3 +2121,9 @@ int cl_scanfile(const char *filename, const char **virname, unsigned long int *s return ret; } + +/* +Local Variables: + c-basic-offset: 4 +End: +*/ diff --git a/libclamav/vba_extract.c b/libclamav/vba_extract.c index 1489c641c..5624130dc 100644 --- a/libclamav/vba_extract.c +++ b/libclamav/vba_extract.c @@ -37,6 +37,7 @@ #include "clamav.h" #include "others.h" +#include "scanners.h" #include "vba_extract.h" #ifdef CL_DEBUG #include "mbox.h" @@ -71,29 +72,11 @@ typedef struct { int big_endian; /* e.g. MAC Office */ } vba_version_t; -static const vba_version_t vba_versions[] = { - { 0x0100005e, "97", FALSE }, - { 0x0100005f, "97 SR1", FALSE }, - { 0x01000065, "2000 alpha", FALSE }, - { 0x0100006b, "2000 beta", FALSE }, - { 0x0100006d, "2000", FALSE }, - { 0x0100006f, "2000", FALSE }, - { 0x01000070, "XP beta 1/2", FALSE }, - { 0x01000073, "XP", FALSE }, - { 0x01000073, "2003", FALSE }, - { 0x01000079, "2003", FALSE }, - { 0x0e000060, "98", TRUE }, - { 0x0e000062, "2001", TRUE }, - { 0x0e000063, "X", TRUE }, - { 0x0e000064, "2004", TRUE }, - { 0x00000000, NULL, FALSE }, -}; - static int skip_past_nul(int fd); static int read_uint16(int fd, uint16_t *u, int big_endian); static int read_uint32(int fd, uint32_t *u, int big_endian); static int seekandread(int fd, off_t offset, int whence, void *data, size_t len); -static vba_project_t *create_vba_project(int record_count, const char *dir); +static vba_project_t *create_vba_project(int record_count, const char *dir, struct uniq *U); static uint16_t vba_endian_convert_16(uint16_t value, int big_endian) @@ -114,6 +97,7 @@ vba_endian_convert_32(uint32_t value, int big_endian) return le32_to_host(value); } + static char * get_unicode_name(const char *name, int size, int big_endian) { @@ -136,9 +120,9 @@ get_unicode_name(const char *name, int size, int big_endian) ret = newname; for(i = 0; i < size; i += increment) { - if(isprint(name[i])) - *ret++ = name[i]; - else { + if((!(name[i]&0x80)) && isprint(name[i])) { + *ret++ = tolower(name[i]); + } else { if((name[i] < 10) && (name[i] >= 0)) { *ret++ = '_'; *ret++ = (char)(name[i] + '0'); @@ -195,17 +179,16 @@ vba_read_project_strings(int fd, int big_endian) { unsigned char *buf = NULL; uint16_t buflen = 0; + int ret = 0; for(;;) { off_t offset; uint16_t length; char *name; - if(!read_uint16(fd, &length, big_endian)) { - if(buf) - free(buf); - return FALSE; - } + if(!read_uint16(fd, &length, big_endian)) + break; + if (length < 6) { lseek(fd, -2, SEEK_CUR); break; @@ -215,7 +198,7 @@ vba_read_project_strings(int fd, int big_endian) if(newbuf == NULL) { if(buf) free(buf); - return FALSE; + return 0; } buflen = length; buf = newbuf; @@ -232,7 +215,7 @@ vba_read_project_strings(int fd, int big_endian) cli_dbgmsg("length: %d, name: %s\n", length, (name) ? name : "[null]"); if((name == NULL) || (memcmp("*\\", name, 2) != 0) || - (strchr("GCHD", name[2]) == NULL)) { + (strchr("ghcd", name[2]) == NULL)) { /* Not a string */ lseek(fd, -(length+2), SEEK_CUR); if(name) @@ -244,9 +227,11 @@ vba_read_project_strings(int fd, int big_endian) if(!read_uint16(fd, &length, big_endian)) { if(buf) free(buf); - return FALSE; + break; } + ret++; + if ((length != 0) && (length != 65535)) { lseek(fd, -2, SEEK_CUR); continue; @@ -257,21 +242,22 @@ vba_read_project_strings(int fd, int big_endian) } if(buf) free(buf); - return TRUE; + return ret; } vba_project_t * -cli_vba_readdir(const char *dir) +cli_vba_readdir(const char *dir, struct uniq *U, uint32_t which) { unsigned char *buf; const unsigned char vba56_signature[] = { 0xcc, 0x61 }; uint16_t record_count, buflen, ffff, byte_count; - uint32_t offset, sig; - int i, fd, big_endian; + uint32_t offset, sig, hash, colls; + int i, j, fd, big_endian = FALSE; vba_project_t *vba_project; const vba_version_t *v; struct vba56_header v56h; - char fullname[NAME_MAX + 1]; + off_t seekback; + char fullname[1024]; cli_dbgmsg("in cli_vba_readdir()\n"); @@ -281,13 +267,15 @@ cli_vba_readdir(const char *dir) /* * _VBA_PROJECT files are embedded within office documents (OLE2) */ - snprintf(fullname, sizeof(fullname) - 1, "%s/_VBA_PROJECT", dir); + + if (!uniq_get(U, "_vba_project", 12, &hash)) + return NULL; + snprintf(fullname, sizeof(fullname), "%s/%u_%u", dir, hash, which); + fullname[sizeof(fullname)-1] = '\0'; fd = open(fullname, O_RDONLY|O_BINARY); - if(fd == -1) { - cli_dbgmsg("Can't open %s\n", fullname); + if(fd == -1) return NULL; - } if(cli_readn(fd, &v56h, sizeof(struct vba56_header)) != sizeof(struct vba56_header)) { close(fd); @@ -298,37 +286,22 @@ cli_vba_readdir(const char *dir) return NULL; } - sig = cli_readint32(v56h.version); - for(v = vba_versions; v->sig; v++) - if(v->sig == sig) - break; - - if(!v->sig) { - cli_warnmsg("Unknown VBA version signature %x %x %x %x\n", - v56h.version[0], v56h.version[1], - v56h.version[2], v56h.version[3]); - switch(v56h.version[3]) { - case 0x01: - cli_warnmsg("Guessing little-endian\n"); - big_endian = FALSE; - break; - case 0x0E: - cli_warnmsg("Guessing big-endian\n"); - big_endian = TRUE; - break; - default: - cli_warnmsg("Unable to guess VBA type\n"); - close(fd); - return NULL; - } - } else { - cli_dbgmsg("VBA Project: %s %s\n", v->big_endian ? "MacOffice" : "Office", v->ver); - big_endian = v->big_endian; - } - - if (!vba_read_project_strings(fd, big_endian)) { - close(fd); + i = vba_read_project_strings(fd, TRUE); + seekback = lseek(fd, 0, SEEK_CUR); + if (lseek(fd, sizeof(struct vba56_header), SEEK_SET) == -1) return NULL; + j = vba_read_project_strings(fd, FALSE); + if(!i && !j) { + close(fd); + cli_warnmsg("vba_readdir: Unable to guess VBA type\n"); + return NULL; + } + if (i > j) { + big_endian = TRUE; + lseek(fd, seekback, SEEK_SET); + cli_dbgmsg("vba_readdir: Guessing big-endian\n"); + } else { + cli_dbgmsg("vba_readdir: Guessing little-endian\n"); } /* junk some more stuff */ @@ -369,7 +342,7 @@ cli_vba_readdir(const char *dir) close(fd); return NULL; } - cli_dbgmsg("VBA Record count: %d\n", record_count); + cli_dbgmsg("vba_readdir: VBA Record count %d\n", record_count); if (record_count == 0) { /* No macros, assume clean */ close(fd); @@ -377,12 +350,12 @@ cli_vba_readdir(const char *dir) } if (record_count > MAX_VBA_COUNT) { /* Almost certainly an error */ - cli_dbgmsg("VBA Record count too big\n"); + cli_dbgmsg("vba_readdir: VBA Record count too big\n"); close(fd); return NULL; } - vba_project = create_vba_project(record_count, dir); + vba_project = create_vba_project(record_count, dir, U); if(vba_project == NULL) { close(fd); return NULL; @@ -393,11 +366,12 @@ cli_vba_readdir(const char *dir) uint16_t length; char *ptr; + vba_project->colls[i] = 0; if(!read_uint16(fd, &length, big_endian)) break; if (length == 0) { - cli_dbgmsg("zero name length\n"); + cli_dbgmsg("vba_readdir: zero name length\n"); break; } if(length > buflen) { @@ -408,51 +382,39 @@ cli_vba_readdir(const char *dir) buf = newbuf; } if (cli_readn(fd, buf, length) != length) { - cli_dbgmsg("read name failed\n"); + cli_dbgmsg("vba_readdir: read name failed\n"); break; } ptr = get_unicode_name((const char *)buf, length, big_endian); - if(ptr == NULL) { - offset = lseek(fd, 0, SEEK_CUR); - ptr = (char *)cli_malloc(18); - if(ptr == NULL) - break; - sprintf(ptr, "clamav-%.10d", (int)offset); - } - cli_dbgmsg("project name: %s\n", ptr); - - if(!read_uint16(fd, &length, big_endian)) { - free(ptr); + if(ptr == NULL) break; + if (!(vba_project->colls[i]=uniq_get(U, ptr, strlen(ptr), &hash))) { + cli_dbgmsg("vba_readdir: cannot find project %s (%u)\n", ptr, hash); break; } + cli_dbgmsg("vba_readdir: project name: %s (%u)\n", ptr, hash); + free(ptr); + vba_project->name[i] = hash; + if(!read_uint16(fd, &length, big_endian)) + break; lseek(fd, length, SEEK_CUR); - if(!read_uint16(fd, &ffff, big_endian)) { - free(ptr); + if(!read_uint16(fd, &ffff, big_endian)) break; - } if (ffff == 0xFFFF) { lseek(fd, 2, SEEK_CUR); - if(!read_uint16(fd, &ffff, big_endian)) { - free(ptr); + if(!read_uint16(fd, &ffff, big_endian)) break; - } lseek(fd, ffff + 8, SEEK_CUR); } else lseek(fd, ffff + 10, SEEK_CUR); - if(!read_uint16(fd, &byte_count, big_endian)) { - free(ptr); + if(!read_uint16(fd, &byte_count, big_endian)) break; - } lseek(fd, (8 * byte_count) + 5, SEEK_CUR); - if(!read_uint32(fd, &offset, big_endian)) { - free(ptr); + if(!read_uint32(fd, &offset, big_endian)) break; - } - cli_dbgmsg("offset: %u\n", (unsigned int)offset); + cli_dbgmsg("vba_readdir: offset: %u\n", (unsigned int)offset); vba_project->offset[i] = offset; - vba_project->name[i] = ptr; lseek(fd, 2, SEEK_CUR); } @@ -462,10 +424,8 @@ cli_vba_readdir(const char *dir) close(fd); if(i < record_count) { - while(--i >= 0) - free(vba_project->name[i]); - free(vba_project->name); + free(vba_project->colls); free(vba_project->dir); free(vba_project->offset); free(vba_project); @@ -585,66 +545,68 @@ ole_copy_file_data(int s, int d, uint32_t len) } int -cli_decode_ole_object(int fd, const char *dir) +cli_scan_ole10(int fd, cli_ctx *ctx) { - int ofd; + int ofd, ret; uint32_t object_size; struct stat statbuf; char *fullname; - if(dir == NULL) - return -1; - if(fd < 0) - return -1; + return CL_CLEAN; + lseek(fd, 0, SEEK_SET); if(!read_uint32(fd, &object_size, FALSE)) - return -1; + return CL_CLEAN; if(fstat(fd, &statbuf) == -1) - return -1; + return CL_EIO; if ((statbuf.st_size - object_size) >= 4) { /* Probably the OLE type id */ if (lseek(fd, 2, SEEK_CUR) == -1) { - return -1; + return CL_CLEAN; } /* Attachment name */ if(!skip_past_nul(fd)) - return -1; + return CL_CLEAN; /* Attachment full path */ if(!skip_past_nul(fd)) - return -1; + return CL_CLEAN; /* ??? */ if(lseek(fd, 8, SEEK_CUR) == -1) - return -1; + return CL_CLEAN; /* Attachment full path */ if(!skip_past_nul(fd)) - return -1; + return CL_CLEAN; if(!read_uint32(fd, &object_size, FALSE)) - return -1; + return CL_CLEAN; } - if(!(fullname = cli_gentemp(dir))) { - return -1; + if(!(fullname = cli_gentemp(NULL))) { + return CL_EMEM; } ofd = open(fullname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY|O_EXCL, S_IWUSR|S_IRUSR); if (ofd < 0) { cli_warnmsg("cli_decode_ole_object: can't create %s\n", fullname); free(fullname); - return -1; - } else { - cli_dbgmsg("cli_decode_ole_object: decoding to %s\n", fullname); + return CL_EIO; } - free(fullname); + cli_dbgmsg("cli_decode_ole_object: decoding to %s\n", fullname); ole_copy_file_data(fd, ofd, object_size); lseek(ofd, 0, SEEK_SET); - return ofd; + ret = cli_magic_scandesc(ofd, ctx); + close(ofd); + if(!cli_leavetemps_flag) + if (cli_unlink(fullname)) + ret = CL_EIO; + free(fullname); + return ret; } /* @@ -797,23 +759,10 @@ ppt_stream_iter(int fd, const char *dir) } char * -cli_ppt_vba_read(const char *filename) +cli_ppt_vba_read(int ifd) { char *dir; const char *ret; - int fd; - char fullname[NAME_MAX + 1]; - - if(filename == NULL) - return NULL; - - snprintf(fullname, sizeof(fullname) - 1, "%s/PowerPoint Document", - filename); - fd = open(fullname, O_RDONLY|O_BINARY); - if (fd == -1) { - cli_dbgmsg("Open PowerPoint Document failed\n"); - return NULL; - } /* Create a directory to store the extracted OLE2 objects */ dir = cli_gentemp(NULL); @@ -824,8 +773,7 @@ cli_ppt_vba_read(const char *filename) free(dir); return NULL; } - ret = ppt_stream_iter(fd, dir); - close(fd); + ret = ppt_stream_iter(ifd, dir); if(ret == NULL) { cli_rmdirs(dir); free(dir); @@ -1083,43 +1031,32 @@ word_skip_macro_intnames(int fd) } vba_project_t * -cli_wm_readdir(const char *dir) +cli_wm_readdir(int fd) { - int fd, done; + int done; off_t end_offset; unsigned char info_id; macro_info_t macro_info; vba_project_t *vba_project; mso_fib_t fib; - char fullname[NAME_MAX + 1]; + uint32_t hash, hashcnt; + char fullname[1024]; - if(dir == NULL) + + if (!word_read_fib(fd, &fib)) return NULL; - snprintf(fullname, sizeof(fullname) - 1, "%s/WordDocument", dir); - fd = open(fullname, O_RDONLY|O_BINARY); - if (fd == -1) { - cli_dbgmsg("Open WordDocument failed\n"); - return NULL; - } - - if (!word_read_fib(fd, &fib)) { - close(fd); - return NULL; - } if(fib.macro_len == 0) { - cli_dbgmsg("No macros detected\n"); + cli_dbgmsg("wm_readdir: No macros detected\n"); /* Must be clean */ - close(fd); return NULL; } - cli_dbgmsg("macro offset: 0x%.4x\n", (int)fib.macro_offset); - cli_dbgmsg("macro len: 0x%.4x\n\n", (int)fib.macro_len); + cli_dbgmsg("wm_readdir: macro offset: 0x%.4x\n", (int)fib.macro_offset); + cli_dbgmsg("wm_readdir: macro len: 0x%.4x\n\n", (int)fib.macro_len); /* Go one past the start to ignore start_id */ if (lseek(fd, fib.macro_offset + 1, SEEK_SET) != (off_t)(fib.macro_offset + 1)) { - cli_dbgmsg("lseek macro_offset failed\n"); - close(fd); + cli_dbgmsg("wm_readdir: lseek macro_offset failed\n"); return NULL; } @@ -1129,7 +1066,7 @@ cli_wm_readdir(const char *dir) while((lseek(fd, 0, SEEK_CUR) < end_offset) && !done) { if (cli_readn(fd, &info_id, 1) != 1) { - cli_dbgmsg("read macro_info failed\n"); + cli_dbgmsg("wm_readdir: read macro_info failed\n"); break; } switch (info_id) { @@ -1160,17 +1097,16 @@ cli_wm_readdir(const char *dir) done = TRUE; break; default: - cli_dbgmsg("unknown type: 0x%x\n", info_id); + cli_dbgmsg("wm_readdir: unknown type: 0x%x\n", info_id); done = TRUE; } } - close(fd); if(macro_info.count == 0) return NULL; - vba_project = create_vba_project(macro_info.count, dir); + vba_project = create_vba_project(macro_info.count, "", NULL); if(vba_project) { vba_project->length = (uint32_t *)cli_malloc(sizeof(uint32_t) * @@ -1183,7 +1119,6 @@ cli_wm_readdir(const char *dir) const macro_entry_t *m = macro_info.entries; for(i = 0; i < macro_info.count; i++) { - vba_project->name[i] = cli_strdup("WordDocument"); vba_project->offset[i] = m->offset; vba_project->length[i] = m->len; vba_project->key[i] = m->key; @@ -1191,6 +1126,7 @@ cli_wm_readdir(const char *dir) } } else { free(vba_project->name); + free(vba_project->colls); free(vba_project->dir); free(vba_project->offset); if(vba_project->length) @@ -1295,7 +1231,7 @@ seekandread(int fd, off_t offset, int whence, void *data, size_t len) * Create and initialise a vba_project structure */ static vba_project_t * -create_vba_project(int record_count, const char *dir) +create_vba_project(int record_count, const char *dir, struct uniq *U) { vba_project_t *ret; @@ -1304,13 +1240,16 @@ create_vba_project(int record_count, const char *dir) if(ret == NULL) return NULL; - ret->name = (char **)cli_malloc(sizeof(char *) * record_count); + ret->name = (uint32_t *)cli_malloc(sizeof(uint32_t) * record_count); + ret->colls = (uint32_t *)cli_malloc(sizeof(uint32_t) * record_count); ret->dir = cli_strdup(dir); ret->offset = (uint32_t *)cli_malloc (sizeof(uint32_t) * record_count); if((ret->name == NULL) || (ret->dir == NULL) || (ret->offset == NULL)) { if(ret->dir) free(ret->dir); + if(ret->colls) + free(ret->colls); if(ret->name) free(ret->name); if(ret->offset) @@ -1319,6 +1258,7 @@ create_vba_project(int record_count, const char *dir) return NULL; } ret->count = record_count; + ret->U = U; return ret; } diff --git a/libclamav/vba_extract.h b/libclamav/vba_extract.h index 57629de24..1e0b58901 100644 --- a/libclamav/vba_extract.h +++ b/libclamav/vba_extract.h @@ -23,22 +23,26 @@ #ifndef __VBA_EXTRACT_H #define __VBA_EXTRACT_H +#include "others.h" #include "cltypes.h" +#include "hashtab.h" typedef struct vba_project_tag { - char **name; + uint32_t *name; + uint32_t *colls; uint32_t *offset; uint32_t *length; /* for Word 6 macros */ unsigned char *key; /* for Word 6 macros */ char *dir; + struct uniq *U; int count; } vba_project_t; -vba_project_t *cli_vba_readdir(const char *dir); +vba_project_t *cli_vba_readdir(const char *dir, struct uniq *U, uint32_t which); +vba_project_t *cli_wm_readdir(int fd); unsigned char *cli_vba_inflate(int fd, off_t offset, int *size); -int cli_decode_ole_object(int fd, const char *dir); -char *cli_ppt_vba_read(const char *filename); -vba_project_t *cli_wm_readdir(const char *dir); +int cli_scan_ole10(int fd, cli_ctx *ctx); +char *cli_ppt_vba_read(int fd); unsigned char *cli_wm_decrypt_macro(int fd, off_t offset, uint32_t len, unsigned char key); diff --git a/shared/cfgparser.c b/shared/cfgparser.c index 64bdb9b4a..eff1a1fb6 100644 --- a/shared/cfgparser.c +++ b/shared/cfgparser.c @@ -293,6 +293,7 @@ struct cfgstruct *getcfg(const char *cfgfile, int verbose) if(ctype == 'm' || ctype == 'k') { char *cpy = (char *) calloc(strlen(arg), 1); strncpy(cpy, arg, strlen(arg) - 1); + cpy[strlen(arg)-1]='\0'; if(!cli_isnumber(cpy)) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name); diff --git a/shared/tar.c b/shared/tar.c index 153cf856d..cc68cebc3 100644 --- a/shared/tar.c +++ b/shared/tar.c @@ -72,6 +72,7 @@ int tar_addfile(int fd, gzFile *gzs, const char *file) memset(&hdr, 0, TARBLK); strncpy(hdr.name, file, 100); + hdr.name[99]='\0'; snprintf(hdr.size, 12, "%o", (unsigned int) sb.st_size); pt = (unsigned char *) &hdr; for(i = 0; i < TARBLK; i++) diff --git a/sigtool/sigtool.c b/sigtool/sigtool.c index 158430f8f..c1a851b05 100644 --- a/sigtool/sigtool.c +++ b/sigtool/sigtool.c @@ -258,6 +258,7 @@ static char *getdsig(const char *host, const char *user, const unsigned char *da if((pt = getenv("SIGNDPASS"))) { strncpy(pass, pt, sizeof(pass)); + pass[sizeof(pass)-1]='\0'; } else { mprintf("Password: "); @@ -281,12 +282,13 @@ static char *getdsig(const char *host, const char *user, const unsigned char *da return NULL; } strncpy(pass, pt, sizeof(pass)); + pass[sizeof(pass)-1]='\0'; free(pt); #ifdef HAVE_TERMIOS_H if(tcsetattr(0, TCSAFLUSH, &old)) { mprintf("!getdsig: tcsetattr() failed\n"); - memset(pass, 0, strlen(pass)); + memset(pass, 0, sizeof(pass)); return NULL; } #endif @@ -300,7 +302,7 @@ static char *getdsig(const char *host, const char *user, const unsigned char *da #endif perror("socket()"); mprintf("!getdsig: Can't create socket\n"); - memset(pass, 0, strlen(pass)); + memset(pass, 0, sizeof(pass)); return NULL; } @@ -312,7 +314,7 @@ static char *getdsig(const char *host, const char *user, const unsigned char *da close(sockd); perror("connect()"); mprintf("!getdsig: Can't connect to ClamAV Signing Service at %s\n", host); - memset(pass, 0, strlen(pass)); + memset(pass, 0, sizeof(pass)); return NULL; } memset(cmd, 0, sizeof(cmd)); @@ -330,13 +332,13 @@ static char *getdsig(const char *host, const char *user, const unsigned char *da if(write(sockd, cmd, len) < 0) { mprintf("!getdsig: Can't write to socket\n"); close(sockd); - memset(cmd, 0, len); - memset(pass, 0, strlen(pass)); + memset(cmd, 0, sizeof(cmd)); + memset(pass, 0, sizeof(pass)); return NULL; } - memset(cmd, 0, len); - memset(pass, 0, strlen(pass)); + memset(cmd, 0, sizeof(cmd)); + memset(pass, 0, sizeof(pass)); memset(buff, 0, sizeof(buff)); if((bread = cli_readn(sockd, buff, sizeof(buff))) > 0) { @@ -587,6 +589,7 @@ static int build(struct optstruct *opt) if(opt->filename) { if(cli_strbcasestr(opt->filename, ".cvd") || cli_strbcasestr(opt->filename, ".cld")) { strncpy(olddb, opt->filename, sizeof(olddb)); + olddb[sizeof(olddb)-1]='\0'; } else { mprintf("!build: Not a CVD/CLD file\n"); return -1; @@ -655,6 +658,7 @@ static int build(struct optstruct *opt) if((pt = getenv("SIGNDUSER"))) { strncpy(builder, pt, sizeof(builder)); + builder[sizeof(builder)-1]='\0'; } else { mprintf("Builder name: "); if(scanf("%as", &pt) == EOF) { @@ -662,6 +666,7 @@ static int build(struct optstruct *opt) return -1; } strncpy(builder, pt, sizeof(builder)); + builder[sizeof(builder)-1]='\0'; free(pt); } @@ -816,6 +821,7 @@ static int build(struct optstruct *opt) return -1; } strncpy(olddb, pt, sizeof(olddb)); + olddb[sizeof(olddb)-1]='\0'; free(pt); if(!(pt = cli_gentemp(NULL))) { @@ -896,6 +902,7 @@ static int unpack(struct optstruct *opt) } else { strncpy(name, opt_arg(opt, "unpack"), sizeof(name)); + name[sizeof(name)-1]='\0'; } if(cvd_unpack(name, ".") == -1) { @@ -1171,6 +1178,7 @@ static int vbadump(struct optstruct *opt) int fd, hex_output; char *dir; const char *pt; + struct uniq *vba; if(opt_check(opt, "vba-hex")) { @@ -1200,15 +1208,15 @@ static int vbadump(struct optstruct *opt) return -1; } - if(cli_ole2_extract(fd, dir, NULL)) { + if(cli_ole2_extract(fd, dir, NULL, &vba)) { cli_rmdirs(dir); free(dir); close(fd); return -1; } - close(fd); - sigtool_vba_scandir(dir, hex_output); + if (vba) + sigtool_vba_scandir(dir, hex_output, vba); cli_rmdirs(dir); free(dir); return 0; @@ -1305,6 +1313,7 @@ static int compare(const char *oldpath, const char *newpath, FILE *diff) if(found) { strncpy(tbuff, obuff, sizeof(tbuff)); + tbuff[sizeof(tbuff)-1]='\0'; for(i = 0; i < tline; i++) { tbuff[16] = 0; if((pt = strchr(tbuff, ' '))) diff --git a/sigtool/vba.c b/sigtool/vba.c index 91b4ce988..9a45d9652 100644 --- a/sigtool/vba.c +++ b/sigtool/vba.c @@ -33,6 +33,10 @@ #include "libclamav/cltypes.h" #include "libclamav/ole2_extract.h" +#ifndef O_BINARY +#define O_BINARY 0 +#endif + typedef struct mac_token_tag { unsigned char token; @@ -46,7 +50,7 @@ typedef struct mac_token2_tag } mac_token2_t; -int sigtool_vba_scandir(const char *dirname, int hex_output); +int sigtool_vba_scandir(const char *dirname, int hex_output, struct uniq *U); static char *get_unicode_name (char *name, int size) { @@ -979,6 +983,7 @@ static int sigtool_scandir (const char *dirname, int hex_output) } } else { if (S_ISREG (statbuf.st_mode)) { + struct uniq *vba; tmpdir = getenv ("TMPDIR"); if (tmpdir == NULL) @@ -1000,14 +1005,14 @@ static int sigtool_scandir (const char *dirname, int hex_output) return 1; } - if ((ret = cli_ole2_extract (desc, dir, NULL))) { + if ((ret = cli_ole2_extract (desc, dir, NULL, &vba))) { printf ("ERROR %s\n", cl_strerror (ret)); cli_rmdirs (dir); free (dir); return ret; } - sigtool_vba_scandir (dir, hex_output); + sigtool_vba_scandir (dir, hex_output, vba); cli_rmdirs (dir); free (dir); @@ -1028,94 +1033,93 @@ static int sigtool_scandir (const char *dirname, int hex_output) return 0; } -int sigtool_vba_scandir (const char *dirname, int hex_output) +int sigtool_vba_scandir (const char *dirname, int hex_output, struct uniq *U) { - int ret = CL_CLEAN, i, fd, data_len; + int ret = CL_CLEAN, i, j, fd, data_len; vba_project_t *vba_project; DIR *dd; struct dirent *dent; struct stat statbuf; - char *fname, *fullname; + char *fullname, vbaname[1024]; unsigned char *data; + uint32_t hashcnt, hash; - cli_dbgmsg ("VBA scan dir: %s\n", dirname); - if ((vba_project = (vba_project_t *)cli_vba_readdir(dirname))) { + hashcnt = uniq_get(U, "_vba_project", 12, NULL); + while(hashcnt--) { + if(!(vba_project = (vba_project_t *)cli_vba_readdir(dirname, U, hashcnt))) continue; - for (i = 0; i < vba_project->count; i++) { - fullname = (char *) malloc (strlen (vba_project->dir) + strlen (vba_project->name[i]) + 2); - sprintf (fullname, "%s/%s", vba_project->dir, vba_project->name[i]); - fd = open (fullname, O_RDONLY); - if (fd == -1) { - cli_errmsg ("Scan->OLE2 -> Can't open file %s\n", fullname); - free (fullname); - ret = CL_EOPEN; - break; + for(i = 0; i < vba_project->count; i++) { + for(j = 0; j < vba_project->colls[i]; j++) { + snprintf(vbaname, 1024, "%s/%u_%u", vba_project->dir, vba_project->name[i], j); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if(fd == -1) continue; + data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len); + close(fd); + + if(data) { + data = (unsigned char *) realloc (data, data_len + 1); + data[data_len]='\0'; + printf ("-------------- start of code ------------------\n%s\n-------------- end of code ------------------\n", data); + free(data); + } } - free (fullname); - cli_dbgmsg ("decompress VBA project '%s'\n", vba_project->name[i]); - printf ("-------------- start of %s ------------------\n", vba_project->name[i]); - data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len); - close (fd); - - if (!data) { - cli_dbgmsg ("WARNING: VBA project '%s' decompressed to NULL\n", vba_project->name[i]); - } else { - data = (unsigned char *) realloc (data, data_len + 1); - data[data_len] = '\0'; - printf ("%s", data); - free (data); - - } - printf ("-------------- end of %s ------------------\n", vba_project->name[i]); } - for (i = 0; i < vba_project->count; i++) - free (vba_project->name[i]); - free (vba_project->name); - free (vba_project->dir); - free (vba_project->offset); - free (vba_project); - } else if ((fullname = cli_ppt_vba_read(dirname))) { - if (sigtool_scandir (fullname, hex_output) == CL_VIRUS) { - ret = CL_VIRUS; - } - cli_rmdirs (fullname); - free (fullname); - } else if ((vba_project = (vba_project_t *)cli_wm_readdir(dirname))) { - for (i = 0; i < vba_project->count; i++) { - fullname = (char *) malloc (strlen (vba_project->dir) + strlen (vba_project->name[i]) + 2); - sprintf (fullname, "%s/%s", vba_project->dir, vba_project->name[i]); - fd = open (fullname, O_RDONLY); - if (fd == -1) { - cli_errmsg ("Scan->OLE2 -> Can't open file %s\n", fullname); - free (fullname); - ret = CL_EOPEN; - break; - } - free (fullname); - cli_dbgmsg ("decompress WM project '%s' macro %d\n", vba_project->name[i], i); - printf ("\n\n-------------- start of macro:%d key:%d length:%d ------------------\n", i, - vba_project->key[i], vba_project->length[i]); - data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], - vba_project->key[i]); - close (fd); + free(vba_project->name); + free(vba_project->colls); + free(vba_project->dir); + free(vba_project->offset); + free(vba_project); + } - if (!data) { - cli_dbgmsg ("WARNING: WM project '%s' macro %d decrypted to NULL\n", vba_project->name[i], i); - } else { - wm_decode_macro (data, vba_project->length[i], hex_output); - free (data); - } - printf ("\n-------------- end of macro %d ------------------\n\n", i); + + if((hashcnt = uniq_get(U, "powerpoint document", 19, &hash))) { + while(hashcnt--) { + snprintf(vbaname, 1024, "%s/%u_%u", dirname, hash, hashcnt); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if (fd == -1) continue; + if ((fullname = cli_ppt_vba_read(fd))) + sigtool_scandir(fullname, hex_output); + cli_rmdirs(fullname); + free(fullname); + close(fd); + } + } + + + if ((hashcnt = uniq_get(U, "worddocument", 12, &hash))) { + while(hashcnt--) { + snprintf(vbaname, sizeof(vbaname), "%s/%u_%u", dirname, hash, hashcnt); + vbaname[sizeof(vbaname)-1] = '\0'; + fd = open(vbaname, O_RDONLY|O_BINARY); + if (fd == -1) continue; + + if (!(vba_project = (vba_project_t *)cli_wm_readdir(fd))) { + close(fd); + continue; + } + + for (i = 0; i < vba_project->count; i++) { + data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], vba_project->key[i]); + if(data) { + data = (unsigned char *) realloc (data, data_len + 1); + data[data_len]='\0'; + printf ("-------------- start of code ------------------\n%s\n-------------- end of code ------------------\n", data); + free(data); + } + } + + close(fd); + free(vba_project->name); + free(vba_project->colls); + free(vba_project->dir); + free(vba_project->offset); + free(vba_project->key); + free(vba_project->length); + free(vba_project); } - for (i = 0; i < vba_project->count; i++) - free (vba_project->name[i]); - free (vba_project->key); - free (vba_project->length); - free (vba_project->offset); - free (vba_project->name); - free (vba_project->dir); - free (vba_project); } if ((dd = opendir (dirname)) != NULL) { @@ -1123,15 +1127,15 @@ int sigtool_vba_scandir (const char *dirname, int hex_output) if (dent->d_ino) { if (strcmp (dent->d_name, ".") && strcmp (dent->d_name, "..")) { /* build the full name */ - fname = calloc (strlen (dirname) + strlen (dent->d_name) + 2, sizeof (char)); - sprintf (fname, "%s/%s", dirname, dent->d_name); + fullname = calloc (strlen (dirname) + strlen (dent->d_name) + 2, sizeof (char)); + sprintf (fullname, "%s/%s", dirname, dent->d_name); /* stat the file */ - if (lstat (fname, &statbuf) != -1) { + if (lstat (fullname, &statbuf) != -1) { if (S_ISDIR (statbuf.st_mode) && !S_ISLNK (statbuf.st_mode)) - sigtool_vba_scandir (fname, hex_output); + sigtool_vba_scandir (fullname, hex_output, U); } - free (fname); + free (fullname); } } } diff --git a/sigtool/vba.h b/sigtool/vba.h index e73ce02f8..920c932c4 100644 --- a/sigtool/vba.h +++ b/sigtool/vba.h @@ -20,6 +20,7 @@ #ifndef __VBA_H #define __VBA_H -int sigtool_vba_scandir(const char *dirname, int hex_output); +#include "libclamav/hashtab.h" +int sigtool_vba_scandir(const char *dirname, int hex_output, struct uniq *U); #endif