/* Header Record : fix endianness for useful fields */ static void headerrecord_to_host(hfsHeaderRecord *hdr) { hdr->treeDepth = be16_to_host(hdr->treeDepth); hdr->rootNode = be32_to_host(hdr->rootNode); hdr->leafRecords = be32_to_host(hdr->leafRecords); hdr->firstLeafNode = be32_to_host(hdr->firstLeafNode); hdr->lastLeafNode = be32_to_host(hdr->lastLeafNode); hdr->nodeSize = be16_to_host(hdr->nodeSize); hdr->maxKeyLength = be16_to_host(hdr->maxKeyLength); hdr->totalNodes = be32_to_host(hdr->totalNodes); hdr->freeNodes = be32_to_host(hdr->freeNodes); hdr->attributes = be32_to_host(hdr->attributes); /* not too useful */ }
static uint16_t vba_endian_convert_16(uint16_t value, int big_endian) { if (big_endian) return (uint16_t)be16_to_host(value); else return le16_to_host(value); }
void mbr_convert_to_host(struct mbr_boot_record *record) { struct mbr_partition_entry *entry; unsigned i; for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) { entry = &record->entries[i]; entry->firstLBA = le32_to_host(entry->firstLBA); entry->numLBA = le32_to_host(entry->numLBA); } record->signature = be16_to_host(record->signature); }
/* Node Descriptor : fix endianness for useful fields */ static void nodedescriptor_to_host(hfsNodeDescriptor *node) { node->fLink = be32_to_host(node->fLink); node->bLink = be32_to_host(node->bLink); node->numRecords = be16_to_host(node->numRecords); }
/* Given the catalog and other details, scan all the volume contents */ static int hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader, hfsHeaderRecord *extHeader, const char *dirname) { int ret = CL_CLEAN; unsigned int has_alerts = 0; uint32_t thisNode, nodeLimit, nodesScanned = 0; uint16_t nodeSize, recordNum, topOfOffsets; uint16_t distance, recordStart, nextDist, nextStart; uint8_t *nodeBuf = NULL; hfsPlusForkData *catFork; catFork = &(volHeader->catalogFile); nodeLimit = MIN(catHeader->totalNodes, HFSPLUS_NODE_LIMIT); thisNode = catHeader->firstLeafNode; nodeSize = catHeader->nodeSize; /* Need to buffer current node, map will keep moving */ nodeBuf = cli_malloc(nodeSize); if (!nodeBuf) { cli_dbgmsg("hfsplus_walk_catalog: failed to acquire node buffer, " "size " STDu32 "\n", nodeSize); return CL_EMEM; } /* Walk catalog leaf nodes, and scan contents of each */ /* Because we want to scan them all, the index nodes add no value */ while (ret == CL_CLEAN) { hfsNodeDescriptor nodeDesc; if (thisNode == 0) { cli_dbgmsg("hfsplus_walk_catalog: reached end of leaf nodes.\n"); break; } if (nodesScanned++ > nodeLimit) { cli_dbgmsg("hfsplus_walk_catalog: node scan limit reached.\n"); break; } /* fetch node into buffer */ ret = hfsplus_fetch_node(ctx, volHeader, catHeader, extHeader, thisNode, nodeBuf); if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_walk_catalog: node fetch failed.\n"); break; } memcpy(&nodeDesc, nodeBuf, 14); /* convert and validate node */ nodedescriptor_to_host(&nodeDesc); nodedescriptor_print("leaf node", &nodeDesc); if ((nodeDesc.kind != HFS_NODEKIND_LEAF) || (nodeDesc.height != 1)) { cli_dbgmsg("hfsplus_walk_catalog: invalid leaf node!\n"); ret = CL_EFORMAT; break; } if ((nodeSize / 4) < nodeDesc.numRecords) { cli_dbgmsg("hfsplus_walk_catalog: too many leaf records for one node!\n"); ret = CL_EFORMAT; break; } /* Walk this node's records and scan */ distance = nodeSize; recordStart = 14; /* 1st record can be after end of node descriptor */ /* offsets take 1 u16 per at the end of the node, along with an empty space offset */ topOfOffsets = nodeSize - (nodeDesc.numRecords * 2) - 2; for (recordNum = 0; recordNum < nodeDesc.numRecords; recordNum++) { uint16_t keylen; int16_t rectype; hfsPlusCatalogFile fileRec; /* Locate next record */ nextDist = nodeSize - (recordNum * 2) - 2; nextStart = nodeBuf[nextDist] * 0x100 + nodeBuf[nextDist+1]; /* Check record location */ if ((nextStart > topOfOffsets-1) || (nextStart < recordStart)) { cli_dbgmsg("hfsplus_walk_catalog: bad record location %x for %u!\n", nextStart, recordNum); ret = CL_EFORMAT; break; } distance = nextDist; recordStart = nextStart; /* Get record key length */ keylen = nodeBuf[recordStart] * 0x100 + nodeBuf[recordStart+1]; keylen += keylen % 2; /* pad 1 byte if required to make 2-byte align */ /* Validate keylen */ if (recordStart + keylen + 4 >= topOfOffsets) { cli_dbgmsg("hfsplus_walk_catalog: key too long for location %x for %u!\n", nextStart, recordNum); ret = CL_EFORMAT; break; } /* Copy type (after key, which is after keylength field) */ memcpy(&rectype, &(nodeBuf[recordStart+keylen+2]), 2); rectype = be16_to_host(rectype); cli_dbgmsg("hfsplus_walk_catalog: record %u nextStart %x keylen %u type %d\n", recordNum, nextStart, keylen, rectype); /* Non-file records are not needed */ if (rectype != HFSPLUS_RECTYPE_FILE) { continue; } /* Check file record location */ if (recordStart+keylen+2+sizeof(hfsPlusCatalogFile) >= topOfOffsets) { cli_dbgmsg("hfsplus_walk_catalog: not enough bytes for file record!\n"); ret = CL_EFORMAT; break; } memcpy(&fileRec, &(nodeBuf[recordStart+keylen+2]), sizeof(hfsPlusCatalogFile)); /* Only scan files */ fileRec.permissions.fileMode = be16_to_host(fileRec.permissions.fileMode); if ((fileRec.permissions.fileMode & HFS_MODE_TYPEMASK) == HFS_MODE_FILE) { /* Convert forks and scan */ forkdata_to_host(&(fileRec.dataFork)); forkdata_print("data fork:", &(fileRec.dataFork)); if (fileRec.dataFork.logicalSize) { ret = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.dataFork), dirname); } /* Check return code */ if (ret == CL_VIRUS) { has_alerts = 1; if (SCAN_ALLMATCHES) { /* Continue scanning in SCAN_ALLMATCHES mode */ cli_dbgmsg("hfsplus_walk_catalog: data fork alert, continuing"); ret = CL_CLEAN; } } if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_walk_catalog: data fork retcode %d\n", ret); break; } /* Scan resource fork */ forkdata_to_host(&(fileRec.resourceFork)); forkdata_print("resource fork:", &(fileRec.resourceFork)); if (fileRec.resourceFork.logicalSize) { ret = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.resourceFork), dirname); } /* Check return code */ if (ret == CL_VIRUS) { has_alerts = 1; if (SCAN_ALLMATCHES) { /* Continue scanning in SCAN_ALLMATCHES mode */ cli_dbgmsg("hfsplus_walk_catalog: resource fork alert, continuing"); ret = CL_CLEAN; } } if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_walk_catalog: resource fork retcode %d", ret); break; } } else { cli_dbgmsg("hfsplus_walk_catalog: record mode %o is not File\n", fileRec.permissions.fileMode); } } /* if return code, exit loop, message already logged */ if (ret != CL_CLEAN) { break; } /* After that, proceed to next node */ if (thisNode == nodeDesc.fLink) { /* Future heuristic */ cli_warnmsg("hfsplus_walk_catalog: simple cycle detected!\n"); ret = CL_EFORMAT; break; } else { thisNode = nodeDesc.fLink; } } free(nodeBuf); if (has_alerts) { ret = CL_VIRUS; } return ret; }
/* Read and convert the HFS+ volume header */ static int hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header) { hfsPlusVolumeHeader *volHeader; const uint8_t *mPtr; if (!header) { return CL_ENULLARG; } /* Start with volume header, 512 bytes at offset 1024 */ if ((*ctx->fmap)->len < 1536) { cli_dbgmsg("cli_scanhfsplus: too short for HFS+\n"); return CL_EFORMAT; } mPtr = fmap_need_off_once(*ctx->fmap, 1024, 512); if (!mPtr) { cli_errmsg("cli_scanhfsplus: cannot read header from map\n"); return CL_EMAP; } volHeader = cli_malloc(sizeof(hfsPlusVolumeHeader)); if (!volHeader) { cli_errmsg("cli_scanhfsplus: header malloc failed\n"); return CL_EMEM; } *header = volHeader; memcpy(volHeader, mPtr, 512); volHeader->signature = be16_to_host(volHeader->signature); volHeader->version = be16_to_host(volHeader->version); if ((volHeader->signature == 0x482B) && (volHeader->version == 4)) { cli_dbgmsg("cli_scanhfsplus: HFS+ signature matched\n"); } else if ((volHeader->signature == 0x4858) && (volHeader->version == 5)) { cli_dbgmsg("cli_scanhfsplus: HFSX v5 signature matched\n"); } else { cli_dbgmsg("cli_scanhfsplus: no matching signature\n"); return CL_EFORMAT; } /* skip fields that will definitely be ignored */ volHeader->attributes = be32_to_host(volHeader->attributes); volHeader->fileCount = be32_to_host(volHeader->fileCount); volHeader->folderCount = be32_to_host(volHeader->folderCount); volHeader->blockSize = be32_to_host(volHeader->blockSize); volHeader->totalBlocks = be32_to_host(volHeader->totalBlocks); cli_dbgmsg("HFS+ Header:\n"); cli_dbgmsg("Signature: %x\n", volHeader->signature); cli_dbgmsg("Attributes: %x\n", volHeader->attributes); cli_dbgmsg("File Count: " STDu32 "\n", volHeader->fileCount); cli_dbgmsg("Folder Count: " STDu32 "\n", volHeader->folderCount); cli_dbgmsg("Block Size: " STDu32 "\n", volHeader->blockSize); cli_dbgmsg("Total Blocks: " STDu32 "\n", volHeader->totalBlocks); /* Block Size must be power of 2 between 512 and 1 MB */ if ((volHeader->blockSize < 512) || (volHeader->blockSize > (1 << 20))) { cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); return CL_EFORMAT; } if (volHeader->blockSize & (volHeader->blockSize - 1)) { cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); return CL_EFORMAT; } forkdata_to_host(&(volHeader->allocationFile)); forkdata_to_host(&(volHeader->extentsFile)); forkdata_to_host(&(volHeader->catalogFile)); forkdata_to_host(&(volHeader->attributesFile)); forkdata_to_host(&(volHeader->startupFile)); if (cli_debug_flag) { forkdata_print("allocationFile", &(volHeader->allocationFile)); forkdata_print("extentsFile", &(volHeader->extentsFile)); forkdata_print("catalogFile", &(volHeader->catalogFile)); forkdata_print("attributesFile", &(volHeader->attributesFile)); forkdata_print("startupFile", &(volHeader->startupFile)); } return CL_CLEAN; }
int cli_scanapm(cli_ctx *ctx) { struct apm_driver_desc_map ddm; struct apm_partition_info aptable, apentry; int ret = CL_CLEAN, detection = CL_CLEAN, old_school = 0; size_t sectorsize, maplen, partsize, sectorcheck; off_t pos = 0, partoff = 0; unsigned i; uint32_t max_prtns = 0; if (!ctx || !ctx->fmap) { cli_errmsg("cli_scanapm: Invalid context\n"); return CL_ENULLARG; } /* read driver description map at sector 0 */ if (fmap_readn(*ctx->fmap, &ddm, pos, sizeof(ddm)) != sizeof(ddm)) { cli_dbgmsg("cli_scanapm: Invalid Apple driver description map\n"); return CL_EFORMAT; } /* convert driver description map big-endian to host */ ddm.signature = be16_to_host(ddm.signature); ddm.blockSize = be16_to_host(ddm.blockSize); ddm.blockCount = be32_to_host(ddm.blockCount); /* check DDM signature */ if (ddm.signature != DDM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple driver description map signature mismatch\n"); return CL_EFORMAT; } /* sector size is determined by the ddm */ sectorsize = ddm.blockSize; /* size of total file must be described by the ddm */ maplen = (*ctx->fmap)->real_len; if ((ddm.blockSize * ddm.blockCount) != maplen) { cli_dbgmsg("cli_scanapm: File described %u size does not match %lu actual size\n", (ddm.blockSize * ddm.blockCount), (unsigned long)maplen); return CL_EFORMAT; } /* check for old-school partition map */ if (sectorsize == 2048) { if (fmap_readn(*ctx->fmap, &aptable, APM_FALLBACK_SECTOR_SIZE, sizeof(aptable)) != sizeof(aptable)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n"); return CL_EFORMAT; } aptable.signature = be16_to_host(aptable.signature); if (aptable.signature == APM_SIGNATURE) { sectorsize = APM_FALLBACK_SECTOR_SIZE; old_school = 1; } } /* read partition table at sector 1 (or after the ddm if old-school) */ pos = APM_PTABLE_BLOCK * sectorsize; if (fmap_readn(*ctx->fmap, &aptable, pos, sizeof(aptable)) != sizeof(aptable)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition table\n"); return CL_EFORMAT; } /* convert partition table big endian to host */ aptable.signature = be16_to_host(aptable.signature); aptable.numPartitions = be32_to_host(aptable.numPartitions); aptable.pBlockStart = be32_to_host(aptable.pBlockStart); aptable.pBlockCount = be32_to_host(aptable.pBlockCount); /* check the partition entry signature */ if (aptable.signature != APM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple partition table signature mismatch\n"); return CL_EFORMAT; } /* check if partition table partition */ if (strncmp((char*)aptable.type, "Apple_Partition_Map", 32) && strncmp((char*)aptable.type, "Apple_partition_map", 32) && strncmp((char*)aptable.type, "Apple_patition_map", 32)) { cli_dbgmsg("cli_scanapm: Initial Apple Partition Map partition is not detected\n"); return CL_EFORMAT; } /* check that the partition table fits in the space specified - HEURISTICS */ if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) { ret = apm_prtn_intxn(ctx, &aptable, sectorsize, old_school); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } /* print debugging info on partition tables */ cli_dbgmsg("APM Partition Table:\n"); cli_dbgmsg("Name: %s\n", (char*)aptable.name); cli_dbgmsg("Type: %s\n", (char*)aptable.type); cli_dbgmsg("Signature: %x\n", aptable.signature); cli_dbgmsg("Partition Count: %u\n", aptable.numPartitions); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", aptable.pBlockStart, aptable.pBlockCount, (unsigned long)(aptable.pBlockStart * sectorsize), (unsigned long)(aptable.pBlockCount * sectorsize)); /* check engine maxpartitions limit */ if (aptable.numPartitions < ctx->engine->maxpartitions) { max_prtns = aptable.numPartitions; } else { max_prtns = ctx->engine->maxpartitions; } /* partition table is a partition [at index 1], so skip it */ for (i = 2; i <= max_prtns; ++i) { /* read partition table entry */ pos = i * sectorsize; if (fmap_readn(*ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n"); return CL_EFORMAT; } /* convert partition entry big endian to host */ apentry.signature = be16_to_host(apentry.signature); apentry.reserved = be16_to_host(apentry.reserved); apentry.numPartitions = be32_to_host(apentry.numPartitions); apentry.pBlockStart = be32_to_host(apentry.pBlockStart); apentry.pBlockCount = be32_to_host(apentry.pBlockCount); /* check the partition entry signature */ if (aptable.signature != APM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple partition entry signature mismatch\n"); return CL_EFORMAT; } /* check if a out-of-order partition map */ if (!strncmp((char*)apentry.type, "Apple_Partition_Map", 32) || !strncmp((char*)apentry.type, "Apple_partition_map", 32) || !strncmp((char*)apentry.type, "Apple_patition_map", 32)) { cli_dbgmsg("cli_scanapm: Out of order Apple Partition Map partition\n"); continue; } partoff = apentry.pBlockStart * sectorsize; partsize = apentry.pBlockCount * sectorsize; /* re-calculate if old_school and aligned [512 * 4 => 2048] */ if (old_school && ((i % 4) == 0)) { if (!strncmp((char*)apentry.type, "Apple_Driver", 32) || !strncmp((char*)apentry.type, "Apple_Driver43", 32) || !strncmp((char*)apentry.type, "Apple_Driver43_CD", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATA", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATAPI", 32) || !strncmp((char*)apentry.type, "Apple_Patches", 32)) { partsize = apentry.pBlockCount * 2048;; } } /* check if invalid partition */ if ((partoff == 0) || (partoff+partsize > maplen)) { cli_dbgmsg("cli_scanapm: Detected invalid Apple partition entry\n"); continue; } /* print debugging info on partition */ cli_dbgmsg("APM Partition Entry %u:\n", i); cli_dbgmsg("Name: %s\n", (char*)apentry.name); cli_dbgmsg("Type: %s\n", (char*)apentry.type); cli_dbgmsg("Signature: %x\n", apentry.signature); cli_dbgmsg("Partition Count: %u\n", apentry.numPartitions); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", apentry.pBlockStart, apentry.pBlockCount, (long unsigned)partoff, (long unsigned)partsize); /* send the partition to cli_map_scan */ ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } if (i >= ctx->engine->maxpartitions) { cli_dbgmsg("cli_scanapm: max partitions reached\n"); } return detection; }
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; }