int nc_connect_rand(int *main, int *alt, int *local) { struct CP_ENTRY *cpe = cpool_get_rand(main); if(!cpe) return 1; *local = (cpe->server->sa_family == AF_UNIX); if(*local) { char *unlinkme; if(cli_gentempfd(tempdir, &unlinkme, alt) != CL_SUCCESS) { logg("!Failed to create temporary file\n"); close(*main); return 1; } unlink(unlinkme); free(unlinkme); if(nc_send(*main, "nFILDES\n", 8)) { logg("!FD scan request failed\n"); close(*alt); close(*main); return 1; } } else { if(nc_send(*main, "nINSTREAM\n", 10)) { logg("!Failed to communicate with clamd\n"); close(*main); return 1; } } return 0; }
static char *dump_xdp(cli_ctx *ctx, const char *start, size_t sz) { int fd; char *filename; size_t nwritten=0; ssize_t writeret; if (cli_gentempfd(ctx->engine->tmpdir, &filename, &fd) != CL_SUCCESS) return NULL; while (nwritten < sz) { writeret = write(fd, start+nwritten, sz-nwritten); if (writeret < 0) { if (errno == EAGAIN) continue; close(fd); cli_unlink(filename); free(filename); return NULL; } nwritten += writeret; } cli_dbgmsg("dump_xdp: Dumped payload to %s\n", filename); close(fd); return filename; }
/* xar_process_subdocument - check TOC for xml subdocument. If found, extract and scan in memory. Parameters: reader - xmlTextReaderPtr ctx - pointer to cli_ctx Returns: CL_SUCCESS - subdoc found and clean scan (or virus found and SCAN_ALL), or no subdocument other - error return code from cli_mem_scandesc() */ static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx) { int rc = CL_SUCCESS, subdoc_len, fd; xmlChar * subdoc; const xmlChar *name; char * tmpname; while (xmlTextReaderRead(reader) == 1) { name = xmlTextReaderConstLocalName(reader); if (name == NULL) { cli_dbgmsg("cli_scanxar: xmlTextReaderConstLocalName() no name.\n"); rc = CL_EFORMAT; break; } if (xmlStrEqual(name, (const xmlChar *)"toc") && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) return CL_SUCCESS; if (xmlStrEqual(name, (const xmlChar *)"subdoc") && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { subdoc = xmlTextReaderReadInnerXml(reader); if (subdoc == NULL) { cli_dbgmsg("cli_scanxar: no content in subdoc element.\n"); xmlTextReaderNext(reader); continue; } subdoc_len = xmlStrlen(subdoc); cli_dbgmsg("cli_scanxar: in-memory scan of xml subdocument, len %i.\n", subdoc_len); rc = cli_mem_scandesc(subdoc, subdoc_len, ctx); if (rc == CL_VIRUS && SCAN_ALL) rc = CL_SUCCESS; /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for subdocument.\n"); } else { cli_dbgmsg("cli_scanxar: Writing subdoc to temp file %s.\n", tmpname); if (cli_writen(fd, subdoc, subdoc_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing subdoc temporary file.\n"); rc = CL_EWRITE; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); } } xmlFree(subdoc); if (rc != CL_SUCCESS) return rc; xmlTextReaderNext(reader); } } return rc; }
void fileblobSetFilename(fileblob *fb, const char *dir, const char *filename) { char *fullname; if(fb->b.name) return; assert(filename != NULL); assert(dir != NULL); blobSetFilename(&fb->b, dir, filename); /* * Reload the filename, it may be different from the one we've * asked for, e.g. '/'s taken out */ filename = blobGetFilename(&fb->b); assert(filename != NULL); if (cli_gentempfd(dir, &fullname, &fb->fd)!=CL_SUCCESS) return; cli_dbgmsg("fileblobSetFilename: file %s saved to %s\n", filename, fullname); fb->fp = fdopen(fb->fd, "wb"); if(fb->fp == NULL) { cli_errmsg("fileblobSetFilename: fdopen failed\n"); close(fb->fd); free(fullname); return; } if(fb->b.data) if(fileblobAddData(fb, fb->b.data, fb->b.len) == 0) { free(fb->b.data); fb->b.data = NULL; fb->b.len = fb->b.size = 0; fb->isNotEmpty = 1; } fb->fullname = fullname; }
int fmap_dump_to_file(fmap_t *map, const char *tmpdir, char **outname, int *outfd) { char *tmpname; int tmpfd, ret; size_t pos = 0, len; cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n"); ret = cli_gentempfd(tmpdir, &tmpname, &tmpfd); if(ret != CL_SUCCESS) { cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n"); return ret; } do { const char *b; len = 0; b = fmap_need_off_once_len(map, pos, BUFSIZ, &len); pos += len; if(b && (len > 0)) { if ((size_t)cli_writen(tmpfd, b, len) != len) { cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname); close(tmpfd); unlink(tmpname); free(tmpname); return CL_EWRITE; } } } while (len > 0); if(lseek(tmpfd, 0, SEEK_SET) == -1) { cli_dbgmsg("fmap_dump_to_file: lseek failed\n"); } *outname = tmpname; *outfd = tmpfd; return CL_SUCCESS; }
/* returns: * <0 for error * -1 out of memory * -2 other * 0 for async dispatched * 1 for command completed (connection can be closed) */ int execute_or_dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument) { int desc = conn->sd; char term = conn->term; const struct cl_engine *engine = conn->engine; /* execute commands that can be executed quickly on the recvloop thread, * these must: * - not involve any operation that can block for a long time, such as disk * I/O * - send of atomic message is allowed. * Dispatch other commands */ if (conn->group) { switch (cmd) { case COMMAND_FILDES: case COMMAND_SCAN: case COMMAND_END: case COMMAND_INSTREAM: case COMMAND_INSTREAMSCAN: case COMMAND_VERSION: case COMMAND_PING: case COMMAND_STATS: case COMMAND_COMMANDS: /* These commands are accepted inside IDSESSION */ break; default: /* these commands are not recognized inside an IDSESSION */ conn_reply_error(conn, "Command invalid inside IDSESSION."); logg("$SESSION: command is not valid inside IDSESSION: %d\n", cmd); conn->group = NULL; return 1; } } switch (cmd) { case COMMAND_SHUTDOWN: pthread_mutex_lock(&exit_mutex); progexit = 1; pthread_mutex_unlock(&exit_mutex); return 1; case COMMAND_RELOAD: pthread_mutex_lock(&reload_mutex); reload = 1; pthread_mutex_unlock(&reload_mutex); mdprintf(desc, "RELOADING%c", term); /* we set reload flag, and we'll reload before closing the * connection */ return 1; case COMMAND_PING: if (conn->group) mdprintf(desc, "%u: PONG%c", conn->id, term); else mdprintf(desc, "PONG%c", term); return conn->group ? 0 : 1; case COMMAND_VERSION: { if (conn->group) mdprintf(desc, "%u: ", conn->id); print_ver(desc, conn->term, engine); return conn->group ? 0 : 1; } case COMMAND_COMMANDS: { if (conn->group) mdprintf(desc, "%u: ", conn->id); print_commands(desc, conn->term, engine); return conn->group ? 0 : 1; } case COMMAND_DETSTATSCLEAR: { detstats_clear(); return 1; } case COMMAND_DETSTATS: { detstats_print(desc, conn->term); return 1; } case COMMAND_INSTREAM: { int rc = cli_gentempfd(optget(conn->opts, "TemporaryDirectory")->strarg, &conn->filename, &conn->scanfd); if (rc != CL_SUCCESS) return rc; conn->quota = optget(conn->opts, "StreamMaxLength")->numarg; conn->mode = MODE_STREAM; return 0; } case COMMAND_STREAM: case COMMAND_MULTISCAN: case COMMAND_CONTSCAN: case COMMAND_STATS: case COMMAND_FILDES: case COMMAND_SCAN: case COMMAND_INSTREAMSCAN: return dispatch_command(conn, cmd, argument); case COMMAND_IDSESSION: conn->group = thrmgr_group_new(); if (!conn->group) return CL_EMEM; return 0; case COMMAND_END: if (!conn->group) { /* end without idsession? */ conn_reply_single(conn, NULL, "UNKNOWN COMMAND"); return 1; } /* need to close connection if we were last in group */ return 1; /*case COMMAND_UNKNOWN:*/ default: conn_reply_single(conn, NULL, "UNKNOWN COMMAND"); return 1; } }
int cli_7unz (cli_ctx *ctx, size_t offset) { CFileInStream archiveStream; CLookToRead lookStream; CSzArEx db; SRes res; UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf; int namelen = UTFBUFSZ, found = CL_CLEAN; Int64 begin_of_archive = offset; UInt32 viruses_found = 0; /* Replacement for FileInStream_CreateVTable(&archiveStream); */ archiveStream.s.Read = FileInStream_fmap_Read; archiveStream.s.Seek = FileInStream_fmap_Seek; archiveStream.s.curpos = 0; archiveStream.file.fmap = *ctx->fmap; LookToRead_CreateVTable(&lookStream, False); if(archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0) return CL_CLEAN; lookStream.realStream = &archiveStream.s; LookToRead_Init(&lookStream); SzArEx_Init(&db); res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); if(res == SZ_OK) { UInt32 i, blockIndex = 0xFFFFFFFF; Byte *outBuffer = 0; size_t outBufferSize = 0; unsigned int encrypted = 0; for (i = 0; i < db.db.NumFiles; i++) { size_t offset = 0; size_t outSizeProcessed = 0; const CSzFileItem *f = db.db.Files + i; char *name; size_t j; int newnamelen, fd; if((found = cli_checklimits("7unz", ctx, 0, 0, 0))) break; if (f->IsDir) continue; if(cli_checklimits("7unz", ctx, f->Size, 0, 0)) continue; if (!db.FileNameOffsets) newnamelen = 0; /* no filename */ else { newnamelen = SzArEx_GetFileNameUtf16(&db, i, NULL); if (newnamelen > namelen) { if(namelen > UTFBUFSZ) free(utf16name); utf16name = cli_malloc(newnamelen*2); if(!utf16name) { found = CL_EMEM; break; } namelen = newnamelen; } SzArEx_GetFileNameUtf16(&db, i, utf16name); } name = (char *)utf16name; for(j=0; j<(size_t)newnamelen; j++) /* FIXME */ name[j] = utf16name[j]; name[j] = 0; cli_dbgmsg("cli_7unz: extracting %s\n", name); res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp); if(res == SZ_ERROR_ENCRYPTED) { encrypted = 1; if(DETECT_ENCRYPTED) { cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n"); cli_append_virus(ctx, "Heuristics.Encrypted.7Zip"); viruses_found++; if(!SCAN_ALL) { found = CL_VIRUS; break; } } } if(cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) { found = CL_VIRUS; viruses_found++; if (!SCAN_ALL) break; } if (res != SZ_OK) cli_dbgmsg("cli_unz: extraction failed with %d\n", res); else { if((found = cli_gentempfd(ctx->engine->tmpdir, &name, &fd))) break; cli_dbgmsg("cli_7unz: Saving to %s\n", name); if((size_t)cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed) found = CL_EWRITE; else if ((found = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) viruses_found++; close(fd); if(!ctx->engine->keeptmp && cli_unlink(name)) found = CL_EUNLINK; free(name); if(found != CL_CLEAN) if (!(SCAN_ALL && found == CL_VIRUS)) break; } } IAlloc_Free(&allocImp, outBuffer); } SzArEx_Free(&db, &allocImp); if(namelen > UTFBUFSZ) free(utf16name); if (res == SZ_OK) cli_dbgmsg("cli_7unz: completed successfully\n"); else if (res == SZ_ERROR_UNSUPPORTED) cli_dbgmsg("cli_7unz: unsupported\n"); else if (res == SZ_ERROR_MEM) cli_dbgmsg("cli_7unz: oom\n"); else if (res == SZ_ERROR_CRC) cli_dbgmsg("cli_7unz: crc mismatch\n"); else cli_dbgmsg("cli_7unz: error %d\n", res); if (SCAN_ALL && viruses_found) return CL_VIRUS; return found; }
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term) { int ret, sockfd, acceptd; int tmpd, bread, retval, firsttimeout, timeout, btread; unsigned int port = 0, portscan, min_port, max_port; unsigned long int quota = 0, maxsize = 0; short bound = 0; const char *virname; char buff[FILEBUFF]; char peer_addr[32]; struct cb_context context; struct sockaddr_in server; struct sockaddr_in peer; socklen_t addrlen; char *tmpname; min_port = optget(opts, "StreamMinPort")->numarg; max_port = optget(opts, "StreamMaxPort")->numarg; /* search for a free port to bind to */ port = cli_rndnum(max_port - min_port); bound = 0; for (portscan = 0; portscan < 1000; portscan++) { port = (port - 1) % (max_port - min_port + 1); memset((char *) &server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(min_port + port); server.sin_addr.s_addr = htonl(INADDR_ANY); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) continue; if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1) closesocket(sockfd); else { bound = 1; break; } } port += min_port; timeout = optget(opts, "ReadTimeout")->numarg; firsttimeout = optget(opts, "CommandReadTimeout")->numarg; if(!bound) { logg("!ScanStream: Can't find any free port.\n"); mdprintf(odesc, "Can't find any free port. ERROR%c", term); return -1; } else { if (listen(sockfd, 1) == -1) { logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno)); closesocket(sockfd); return -1; } if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) { logg("!ScanStream: error transmitting port.\n"); closesocket(sockfd); return -1; } } retval = poll_fd(sockfd, firsttimeout, 0); if (!retval || retval == -1) { const char *reason = !retval ? "timeout" : "poll"; mdprintf(odesc, "Accept %s. ERROR%c", reason, term); logg("!ScanStream %u: accept %s.\n", port, reason); closesocket(sockfd); return -1; } addrlen = sizeof(peer); if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) { closesocket(sockfd); mdprintf(odesc, "accept() ERROR%c", term); logg("!ScanStream %u: accept() failed.\n", port); return -1; } *peer_addr = '\0'; inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr)); logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd); if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) { shutdown(sockfd, 2); closesocket(sockfd); closesocket(acceptd); mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term); logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port); return -1; } quota = maxsize = optget(opts, "StreamMaxLength")->numarg; while((retval = poll_fd(acceptd, timeout, 0)) == 1) { /* only read up to max */ btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff); if (!btread) { logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize); break; /* Scan what we have */ } bread = recv(acceptd, buff, btread, 0); if(bread <= 0) break; quota -= bread; if(writen(tmpd, buff, bread) != bread) { shutdown(sockfd, 2); closesocket(sockfd); closesocket(acceptd); mdprintf(odesc, "Temporary file -> write ERROR%c", term); logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port); close(tmpd); if(!optget(opts, "LeaveTemporaryFiles")->enabled) unlink(tmpname); free(tmpname); return -1; } } switch(retval) { case 0: /* timeout */ mdprintf(odesc, "read timeout ERROR%c", term); logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port); break; case -1: mdprintf(odesc, "read poll ERROR%c", term); logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port); break; } if(retval == 1) { lseek(tmpd, 0, SEEK_SET); thrmgr_setactivetask(peer_addr, NULL); context.filename = peer_addr; context.virsize = 0; context.scandata = NULL; ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context); thrmgr_setactivetask(NULL, NULL); } else { ret = -1; } close(tmpd); if(!optget(opts, "LeaveTemporaryFiles")->enabled) unlink(tmpname); free(tmpname); closesocket(acceptd); closesocket(sockfd); if(ret == CL_VIRUS) { if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) { mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term); logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize); } else { mdprintf(odesc, "stream: %s FOUND%c", virname, term); logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname); } virusaction("stream", virname, opts); } else if(ret != CL_CLEAN) { if(retval == 1) { mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term); logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret)); } } else { mdprintf(odesc, "stream: OK%c", term); if(logok) logg("stream(%s@%u): OK\n", peer_addr, port); } return ret; }
/* Read and dump a file for scanning */ static int hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *extHeader, hfsPlusForkData *fork, const char *dirname) { hfsPlusExtentDescriptor *currExt; const uint8_t *mPtr = NULL; char *tmpname = NULL; int ofd, ret = CL_CLEAN; uint64_t targetSize; uint32_t outputBlocks = 0; uint8_t ext; UNUSEDPARAM(extHeader); /* bad record checks */ if (!fork || (fork->logicalSize == 0) || (fork->totalBlocks == 0)) { cli_dbgmsg("hfsplus_dumpfile: Empty file.\n"); return CL_CLEAN; } /* check limits */ targetSize = fork->logicalSize; #if SIZEOF_LONG < 8 if (targetSize > ULONG_MAX) { cli_dbgmsg("hfsplus_dumpfile: File too large for limit check.\n"); return CL_EFORMAT; } #endif ret = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0); if (ret != CL_CLEAN) { return ret; } /* open file */ ret = cli_gentempfd(dirname, &tmpname, &ofd); if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_dumpfile: Cannot generate temporary file.\n"); return ret; } cli_dbgmsg("hfsplus_dumpfile: Extracting to %s\n", tmpname); ext = 0; /* Dump file, extent by extent */ do { uint32_t currBlock, endBlock, outputSize = 0; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: output complete\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } /* Prepare extent */ if (ext < 8) { currExt = &(fork->extents[ext]); cli_dbgmsg("hfsplus_dumpfile: extent %u\n", ext); } else { cli_dbgmsg("hfsplus_dumpfile: need next extent from ExtentOverflow\n"); /* Not implemented yet */ ret = CL_EFORMAT; break; } /* have extent, so validate and get block range */ if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) { cli_dbgmsg("hfsplus_dumpfile: next extent empty, done\n"); break; } if ((currExt->startBlock & 0x10000000) && (currExt->blockCount & 0x10000000)) { cli_dbgmsg("hfsplus_dumpfile: next extent illegal!\n"); ret = CL_EFORMAT; break; } currBlock = currExt->startBlock; endBlock = currExt->startBlock + currExt->blockCount - 1; if ((currBlock > volHeader->totalBlocks) || (endBlock > volHeader->totalBlocks) || (currExt->blockCount > volHeader->totalBlocks)) { cli_dbgmsg("hfsplus_dumpfile: bad extent!\n"); ret = CL_EFORMAT; break; } /* Write the blocks, walking the map */ while (currBlock <= endBlock) { size_t to_write = MIN(targetSize, volHeader->blockSize); ssize_t written; off_t offset = currBlock * volHeader->blockSize; /* move map to next block */ mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); if (!mPtr) { cli_errmsg("hfsplus_dumpfile: map error\n"); ret = CL_EMAP; break; } written = cli_writen(ofd, mPtr, to_write); if ((size_t)written != to_write) { cli_errmsg("hfsplus_dumpfile: write error\n"); ret = CL_EWRITE; break; } targetSize -= to_write; outputSize += to_write; currBlock++; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: all data written\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } } /* Finished the extent, move to next */ ext++; } while (ret == CL_CLEAN); /* if successful so far, scan the output */ if (ret == CL_CLEAN) { ret = cli_magic_scandesc(ofd, tmpname, ctx); } if (ofd >= 0) { close(ofd); } if (!ctx->engine->keeptmp) { if (cli_unlink(tmpname)) { ret = CL_EUNLINK; } } free(tmpname); return ret; }
int cli_scanxar(cli_ctx *ctx) { int rc = CL_SUCCESS; unsigned int cksum_fails = 0; unsigned int extract_errors = 0; #if HAVE_LIBXML2 int fd = -1; struct xar_header hdr; fmap_t *map = *ctx->fmap; long length, offset, size, at; int encoding; z_stream strm; char *toc, *tmpname; xmlTextReaderPtr reader = NULL; int a_hash, e_hash; unsigned char *a_cksum = NULL, *e_cksum = NULL; memset(&strm, 0x00, sizeof(z_stream)); /* retrieve xar header */ if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scanxar: Invalid header, too short.\n"); return CL_EFORMAT; } hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == XAR_HEADER_MAGIC) { cli_dbgmsg("cli_scanxar: Matched magic\n"); } else { cli_dbgmsg("cli_scanxar: Invalid magic\n"); return CL_EFORMAT; } hdr.size = be16_to_host(hdr.size); hdr.version = be16_to_host(hdr.version); hdr.toc_length_compressed = be64_to_host(hdr.toc_length_compressed); hdr.toc_length_decompressed = be64_to_host(hdr.toc_length_decompressed); hdr.chksum_alg = be32_to_host(hdr.chksum_alg); /* cli_dbgmsg("hdr.magic %x\n", hdr.magic); */ /* cli_dbgmsg("hdr.size %i\n", hdr.size); */ /* cli_dbgmsg("hdr.version %i\n", hdr.version); */ /* cli_dbgmsg("hdr.toc_length_compressed %lu\n", hdr.toc_length_compressed); */ /* cli_dbgmsg("hdr.toc_length_decompressed %lu\n", hdr.toc_length_decompressed); */ /* cli_dbgmsg("hdr.chksum_alg %i\n", hdr.chksum_alg); */ /* Uncompress TOC */ strm.next_in = (unsigned char *)fmap_need_off_once(*ctx->fmap, hdr.size, hdr.toc_length_compressed); if (strm.next_in == NULL) { cli_dbgmsg("cli_scanxar: fmap_need_off_once fails on TOC.\n"); return CL_EREAD; } strm.avail_in = hdr.toc_length_compressed; toc = cli_malloc(hdr.toc_length_decompressed+1); if (toc == NULL) { cli_dbgmsg("cli_scanxar: cli_malloc fails on TOC decompress buffer.\n"); return CL_EMEM; } toc[hdr.toc_length_decompressed] = '\0'; strm.avail_out = hdr.toc_length_decompressed; strm.next_out = (unsigned char *)toc; rc = inflateInit(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateInit error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflate(&strm, Z_SYNC_FLUSH); if (rc != Z_OK && rc != Z_STREAM_END) { cli_dbgmsg("cli_scanxar:inflate error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflateEnd(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateEnd error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } /* cli_dbgmsg("cli_scanxar: TOC xml:\n%s\n", toc); */ /* printf("cli_scanxar: TOC xml:\n%s\n", toc); */ /* cli_dbgmsg("cli_scanxar: TOC end:\n"); */ /* printf("cli_scanxar: TOC end:\n"); */ /* scan the xml */ cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n"); rc = cli_mem_scandesc(toc, hdr.toc_length_decompressed, ctx); if (rc != CL_SUCCESS) { if (rc != CL_VIRUS || !SCAN_ALL) goto exit_toc; } /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for TOC.\n"); goto exit_toc; } if (cli_writen(fd, toc, hdr.toc_length_decompressed) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing TOC.\n"); rc = CL_EWRITE; xar_cleanup_temp_file(ctx, fd, tmpname); goto exit_toc; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_toc; } reader = xmlReaderForMemory(toc, hdr.toc_length_decompressed, "noname.xml", NULL, 0); if (reader == NULL) { cli_dbgmsg("cli_scanxar: xmlReaderForMemory error for TOC\n"); goto exit_toc; } rc = xar_scan_subdocuments(reader, ctx); if (rc != CL_SUCCESS) { cli_dbgmsg("xar_scan_subdocuments returns %i.\n", rc); goto exit_reader; } /* Walk the TOC XML and extract files */ fd = -1; tmpname = NULL; while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding, &a_cksum, &a_hash, &e_cksum, &e_hash))) { int do_extract_cksum = 1; unsigned char * blockp; void *a_sc, *e_sc; void *a_mc, *e_mc; void *a_hash_ctx, *e_hash_ctx; char result[SHA1_HASH_SIZE]; char * expected; /* clean up temp file from previous loop iteration */ if (fd > -1 && tmpname) { rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_reader; } at = offset + hdr.toc_length_compressed + hdr.size; if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't generate temporary file.\n"); goto exit_reader; } cli_dbgmsg("cli_scanxar: decompress into temp file:\n%s, size %li,\n" "from xar heap offset %li length %li\n", tmpname, size, offset, length); a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc); e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc); switch (encoding) { case CL_TYPE_GZ: /* inflate gzip directly because file segments do not contain magic */ memset(&strm, 0, sizeof(strm)); if ((rc = inflateInit(&strm)) != Z_OK) { cli_dbgmsg("cli_scanxar: InflateInit failed: %d\n", rc); rc = CL_EFORMAT; extract_errors++; break; } while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { unsigned long avail_in; void * next_in; unsigned int bytes = MIN(map->len - at, map->pgsz); bytes = MIN(length, bytes); if(!(strm.next_in = next_in = (void*)fmap_need_off_once(map, at, bytes))) { cli_dbgmsg("cli_scanxar: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at); inflateEnd(&strm); rc = CL_EREAD; goto exit_tmpfile; } at += bytes; strm.avail_in = avail_in = bytes; do { int inf, outsize = 0; unsigned char buff[FILEBUFF]; strm.avail_out = sizeof(buff); strm.next_out = buff; inf = inflate(&strm, Z_SYNC_FLUSH); if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { cli_dbgmsg("cli_scanxar: inflate error %i %s.\n", inf, strm.msg?strm.msg:""); rc = CL_EFORMAT; extract_errors++; break; } bytes = sizeof(buff) - strm.avail_out; xar_hash_update(e_hash_ctx, buff, bytes, e_hash); if (cli_writen(fd, buff, bytes) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error file %s.\n", tmpname); inflateEnd(&strm); rc = CL_EWRITE; goto exit_tmpfile; } outsize += sizeof(buff) - strm.avail_out; if (cli_checklimits("cli_scanxar", ctx, outsize, 0, 0) != CL_CLEAN) { break; } if (inf == Z_STREAM_END) { break; } } while (strm.avail_out == 0); if (rc != CL_SUCCESS) break; avail_in -= strm.avail_in; xar_hash_update(a_hash_ctx, next_in, avail_in, a_hash); } inflateEnd(&strm); break; case CL_TYPE_7Z: #define CLI_LZMA_OBUF_SIZE 1024*1024 #define CLI_LZMA_HDR_SIZE LZMA_PROPS_SIZE+8 #define CLI_LZMA_IBUF_SIZE CLI_LZMA_OBUF_SIZE>>2 /* estimated compression ratio 25% */ { struct CLI_LZMA lz; unsigned long in_remaining = length; unsigned long out_size = 0; unsigned char * buff = __lzma_wrap_alloc(NULL, CLI_LZMA_OBUF_SIZE); int lret; memset(&lz, 0, sizeof(lz)); if (buff == NULL) { cli_dbgmsg("cli_scanxar: memory request for lzma decompression buffer fails.\n"); rc = CL_EMEM; goto exit_tmpfile; } blockp = (void*)fmap_need_off_once(map, at, CLI_LZMA_HDR_SIZE); if (blockp == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); goto exit_tmpfile; } lz.next_in = blockp; lz.avail_in = CLI_LZMA_HDR_SIZE; xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash); lret = cli_LzmaInit(&lz, 0); if (lret != LZMA_RESULT_OK) { cli_dbgmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", lret); rc = CL_EFORMAT; __lzma_wrap_free(NULL, buff); extract_errors++; break; } at += CLI_LZMA_HDR_SIZE; in_remaining -= CLI_LZMA_HDR_SIZE; while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { SizeT avail_in; SizeT avail_out; void * next_in; unsigned long in_consumed; lz.next_out = buff; lz.avail_out = CLI_LZMA_OBUF_SIZE; lz.avail_in = avail_in = MIN(CLI_LZMA_IBUF_SIZE, in_remaining); lz.next_in = next_in = (void*)fmap_need_off_once(map, at, lz.avail_in); if (lz.next_in == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno: %s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); goto exit_tmpfile; } lret = cli_LzmaDecode(&lz); if (lret != LZMA_RESULT_OK && lret != LZMA_STREAM_END) { cli_dbgmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", lret); rc = CL_EFORMAT; extract_errors++; break; } in_consumed = avail_in - lz.avail_in; in_remaining -= in_consumed; at += in_consumed; avail_out = CLI_LZMA_OBUF_SIZE - lz.avail_out; if (avail_out == 0) cli_dbgmsg("cli_scanxar: cli_LzmaDecode() produces no output for " "avail_in %lu, avail_out %lu.\n", avail_in, avail_out); xar_hash_update(a_hash_ctx, next_in, in_consumed, a_hash); xar_hash_update(e_hash_ctx, buff, avail_out, e_hash); /* Write a decompressed block. */ /* cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, " */ /* "consumed %li of %li available compressed bytes.\n", */ /* avail_out, in_consumed, avail_in); */ if (cli_writen(fd, buff, avail_out) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing lzma temp file for %li bytes.\n", avail_out); __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); rc = CL_EWRITE; goto exit_tmpfile; } /* Check file size limitation. */ out_size += avail_out; if (cli_checklimits("cli_scanxar", ctx, out_size, 0, 0) != CL_CLEAN) { break; } if (lret == LZMA_STREAM_END) break; } cli_LzmaShutdown(&lz); __lzma_wrap_free(NULL, buff); } break; case CL_TYPE_ANY: default: case CL_TYPE_BZ: case CL_TYPE_XZ: /* for uncompressed, bzip2, xz, and unknown, just pull the file, cli_magic_scandesc does the rest */ do_extract_cksum = 0; { unsigned long write_len; if (ctx->engine->maxfilesize) write_len = MIN((size_t)(ctx->engine->maxfilesize), (size_t)length); else write_len = length; if (!(blockp = (void*)fmap_need_off_once(map, at, length))) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; goto exit_tmpfile; } xar_hash_update(a_hash_ctx, blockp, length, a_hash); if (cli_writen(fd, blockp, write_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at); rc = CL_EWRITE; goto exit_tmpfile; } /*break;*/ } } if (rc == CL_SUCCESS) { xar_hash_final(a_hash_ctx, result, a_hash); if (a_cksum != NULL) { expected = cli_hex2str((char *)a_cksum); if (xar_hash_check(a_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: archived-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: archived-checksum matched.\n"); } free(expected); } if (e_cksum != NULL) { if (do_extract_cksum) { xar_hash_final(e_hash_ctx, result, e_hash); expected = cli_hex2str((char *)e_cksum); if (xar_hash_check(e_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: extracted-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n"); } free(expected); } } rc = cli_magic_scandesc(fd, ctx); if (rc != CL_SUCCESS) { if (rc == CL_VIRUS) { cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx)); if (!SCAN_ALL) goto exit_tmpfile; } else if (rc != CL_BREAK) { cli_dbgmsg("cli_scanxar: cli_magic_scandesc error %i\n", rc); goto exit_tmpfile; } } } if (a_cksum != NULL) { xmlFree(a_cksum); a_cksum = NULL; } if (e_cksum != NULL) { xmlFree(e_cksum); e_cksum = NULL; } } exit_tmpfile: xar_cleanup_temp_file(ctx, fd, tmpname); exit_reader: if (a_cksum != NULL) xmlFree(a_cksum); if (e_cksum != NULL) xmlFree(e_cksum); xmlTextReaderClose(reader); xmlFreeTextReader(reader); exit_toc: free(toc); if (rc == CL_BREAK) rc = CL_SUCCESS; #else cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n"); #endif if (cksum_fails + extract_errors != 0) { cli_warnmsg("cli_scanxar: %u checksum errors and %u extraction errors, use --debug for more info.\n", cksum_fails, extract_errors); } return rc; }
static int rtf_object_process(struct rtf_state* state, const unsigned char* input,const size_t len) { struct rtf_object_data* data = state->cb_data; unsigned char outdata[BUFF_SIZE]; const unsigned char* out_data; size_t out_cnt = 0; size_t i; int ret; if(!data || !len) return 0; if(data->has_partial) { for(i=0;i<len && !isxdigit(input[i]);i++) ; if(i<len) { outdata[out_cnt++] = data->partial | hextable[input[i++]]; data->has_partial = 0; } else return 0; } else i = 0; for(;i<len;i++) { if(isxdigit(input[i])) { const unsigned char byte = hextable[ input[i++] ] << 4; while(i<len && !isxdigit(input[i])) i++; if(i == len) { data->partial = byte; data->has_partial = 1; break; } outdata[out_cnt++] = byte | hextable[ input[i] ]; } } out_data = outdata; while(out_data && out_cnt) { switch(data->internal_state) { case WAIT_MAGIC: { cli_dbgmsg("RTF: waiting for magic\n"); for(i=0; i<out_cnt && data->bread < rtf_data_magic_len; i++, data->bread++) if(rtf_data_magic[data->bread] != out_data[i]) { cli_dbgmsg("Warning: rtf objdata magic number not matched, expected:%d, got: %d, at pos:%lu\n",rtf_data_magic[i],out_data[i], (unsigned long int) data->bread); } out_cnt -= i; if(data->bread == rtf_data_magic_len) { out_data += i; data->bread = 0; data->internal_state = WAIT_DESC_LEN; } break; } case WAIT_DESC_LEN: { if(data->bread == 0) data->desc_len = 0; for(i=0; i<out_cnt && data->bread < 4; i++,data->bread++) data->desc_len |= ((size_t)out_data[i]) << (data->bread*8); out_cnt -= i; if(data->bread == 4) { out_data += i; data->bread=0; if(data->desc_len > 64) { cli_dbgmsg("Description length too big (%lu), showing only 64 bytes of it\n", (unsigned long int) data->desc_len); data->desc_name = cli_malloc(65); } else data->desc_name = cli_malloc(data->desc_len+1); if(!data->desc_name) { cli_errmsg("rtf_object_process: Unable to allocate memory for data->desc_name\n"); return CL_EMEM; } data->internal_state = WAIT_DESC; cli_dbgmsg("RTF: description length:%lu\n", (unsigned long int) data->desc_len); } break; } case WAIT_DESC:{ cli_dbgmsg("RTF: in WAIT_DESC\n"); for(i=0;i<out_cnt && data->bread < data->desc_len && data->bread < 64;i++, data->bread++) data->desc_name[data->bread] = out_data[i]; out_cnt -= i; out_data += i; if(data->bread < data->desc_len && data->bread < 64) { cli_dbgmsg("RTF: waiting for more data(1)\n"); return 0;/* wait for more data */ } data->desc_name[data->bread] = '\0'; if(data->desc_len - data->bread > out_cnt) { data->desc_len -= out_cnt; cli_dbgmsg("RTF: waiting for more data(2)\n"); return 0;/* wait for more data */ } out_cnt -= data->desc_len - data->bread; if(data->bread >= data->desc_len) { out_data += data->desc_len - data->bread; data->bread = 0; cli_dbgmsg("Preparing to dump rtf embedded object, description:%s\n",data->desc_name); free(data->desc_name); data->desc_name = NULL; data->internal_state = WAIT_ZERO; } break; } case WAIT_ZERO:{ if(out_cnt < 8-data->bread) { out_cnt = 0; data->bread += out_cnt; } else { out_cnt -= 8-data->bread; data->bread = 8; } if(data->bread == 8) { out_data += 8; data->bread = 0; cli_dbgmsg("RTF: next state: wait_data_size\n"); data->internal_state = WAIT_DATA_SIZE; } break; } case WAIT_DATA_SIZE: { cli_dbgmsg("RTF: in WAIT_DATA_SIZE\n"); if(data->bread == 0) data->desc_len = 0; for(i=0; i<out_cnt && data->bread < 4; i++,data->bread++) data->desc_len |= ((size_t)out_data[i]) << (8*data->bread); out_cnt -= i; if(data->bread == 4) { out_data += i; data->bread=0; cli_dbgmsg("Dumping rtf embedded object of size:%lu\n", (unsigned long int) data->desc_len); if((ret = cli_gentempfd(data->tmpdir, &data->name, &data->fd))) return ret; data->internal_state = DUMP_DATA; cli_dbgmsg("RTF: next state: DUMP_DATA\n"); } break; } case DUMP_DATA: { ssize_t out_want = out_cnt < data->desc_len ? out_cnt : data->desc_len; if(!data->bread) { if(out_data[0] != 0xd0 || out_data[1]!=0xcf) { /* this is not an ole2 doc, but some ole (stream?) to be * decoded by cli_decode_ole_object*/ char out[4]; data->bread = 1;/* flag to indicate this needs to be scanned with cli_decode_ole_object*/ cli_writeint32(out,data->desc_len); if(cli_writen(data->fd,out,4)!=4) return CL_EWRITE; } else data->bread = 2; } data->desc_len -= out_want; if(cli_writen(data->fd,out_data,out_want) != out_want) { return CL_EWRITE; } out_data += out_want; out_cnt -= out_want; if(!data->desc_len) { int rc; if(( rc = decode_and_scan(data, data->ctx) )) return rc; data->bread=0; data->internal_state = WAIT_MAGIC; } break; } case DUMP_DISCARD: default: out_cnt = 0; ; } } return 0; }
static int scancws(cli_ctx *ctx, struct swf_file_hdr *hdr) { z_stream stream; char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; int offset = 8, ret, zret, outsize = 8, count, zend; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scancws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } stream.avail_in = 0; stream.next_in = (Bytef *)inbuff; stream.next_out = (Bytef *)outbuff; stream.zalloc = (alloc_func) NULL; stream.zfree = (free_func) NULL; stream.opaque = (voidpf) 0; stream.avail_out = FILEBUFF; zret = inflateInit(&stream); if(zret != Z_OK) { cli_errmsg("scancws: inflateInit() failed\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } do { if(stream.avail_in == 0) { stream.next_in = (Bytef *)inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if(ret < 0) { cli_errmsg("scancws: Error reading SWF file\n"); close(fd); inflateEnd(&stream); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if(!ret) break; stream.avail_in = ret; offset += ret; } zret = inflate(&stream, Z_SYNC_FLUSH); count = FILEBUFF - stream.avail_out; if(count) { if(cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if(cli_writen(fd, outbuff, count) != count) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); inflateEnd(&stream); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } stream.next_out = (Bytef *)outbuff; stream.avail_out = FILEBUFF; } while(zret == Z_OK); zend = inflateEnd(&stream); if((zret != Z_STREAM_END && zret != Z_OK) || zend != Z_OK) { /* * outsize is initialized to 8, it being 8 here means that we couldn't even read a single byte. * If outsize > 8, then we have data. Let's scan what we have. */ if (outsize == 8) { cli_infomsg(ctx, "scancws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scancws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[zlib] to %s, size %d\n", tmpname, outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if(!ctx->engine->keeptmp) { if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }
static int scanzws(cli_ctx *ctx, struct swf_file_hdr *hdr) { struct CLI_LZMA lz; unsigned char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; /* strip off header */ off_t offset = 8; uint32_t d_insize; size_t outsize = 8; int ret, lret, count; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scanzws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } /* read 4 bytes (for compressed 32-bit filesize) [not used for LZMA] */ if (fmap_readn(map, &d_insize, offset, sizeof(d_insize)) != sizeof(d_insize)) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EREAD; } offset += sizeof(d_insize); /* check if declared input size matches actual output size */ /* map->len = header (8 bytes) + d_insize (4 bytes) + flags (5 bytes) + compressed stream */ if (d_insize != (map->len - 17)) { cli_warnmsg("SWF: declared input length != compressed stream size, %u != %llu\n", d_insize, (long long unsigned)(map->len - 17)); } else { cli_dbgmsg("SWF: declared input length == compressed stream size, %u == %llu\n", d_insize, (long long unsigned)(map->len - 17)); } /* first buffer required for initializing LZMA */ ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } /* nothing written, likely truncated */ if (!ret) { cli_errmsg("scanzws: possibly truncated file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EFORMAT; } offset += ret; memset(&lz, 0, sizeof(lz)); lz.next_in = inbuff; lz.next_out = outbuff; lz.avail_in = ret; lz.avail_out = FILEBUFF; lret = cli_LzmaInit(&lz, hdr->filesize); if (lret != LZMA_RESULT_OK) { cli_errmsg("scanzws: LzmaInit() failed\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } while (lret == LZMA_RESULT_OK) { if (lz.avail_in == 0) { lz.next_in = inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if (!ret) break; lz.avail_in = ret; offset += ret; } lret = cli_LzmaDecode(&lz); count = FILEBUFF - lz.avail_out; if (count) { if (cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if (cli_writen(fd, outbuff, count) != count) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } lz.next_out = outbuff; lz.avail_out = FILEBUFF; } cli_LzmaShutdown(&lz); if (lret != LZMA_STREAM_END && lret != LZMA_RESULT_OK) { /* outsize starts at 8, therefore, if its still 8, nothing was decompressed */ if (outsize == 8) { cli_infomsg(ctx, "scanzws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scanzws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[LZMA] to %s, size %llu\n", tmpname, (long long unsigned)outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if (!(ctx->engine->keeptmp)) { if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }