diff --git a/libclamav/filetypes_int.h b/libclamav/filetypes_int.h index a6cb131877..8cf10e14d5 100644 --- a/libclamav/filetypes_int.h +++ b/libclamav/filetypes_int.h @@ -203,5 +203,6 @@ static const char *ftypes_int[] = { "0:0:4d4d:TIFF Big Endian:CL_TYPE_ANY:CL_TYPE_GRAPHICS:81:121", "1:*:377abcaf271c:7zip-SFX:CL_TYPE_ANY:CL_TYPE_7ZSFX:74", "1:0:3c3f786d6c2076657273696f6e3d22312e3022{0-1024}70726f6769643d22576f72642e446f63756d656e74223f3e:Microsoft Word 2003 XML Document:CL_TYPE_ANY:CL_TYPE_XML_WORD:80", + "1:*:004245413031:ISO9660:CL_TYPE_ANY:CL_TYPE_ISO9660:71", NULL}; #endif diff --git a/libclamav/iso9660.c b/libclamav/iso9660.c index 66fd2fcdbe..cde5a68e6b 100644 --- a/libclamav/iso9660.c +++ b/libclamav/iso9660.c @@ -223,6 +223,847 @@ static cl_error_t iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int return ret; } +static uint16_t getDescriptorTagId(const uint8_t *const buffer) +{ + return le16_to_host(((DescriptorTag *)buffer)->tagId); +} + +static bool isDirectory(FileIdentifierDescriptor *fid) +{ + return (fid->characteristics & 2); +} + +static cl_error_t writeWholeFile(cli_ctx *ctx, const char *const fileName, const uint8_t *const data, const size_t dataLen) +{ + + int fd; + char *tmpf = NULL; + + cl_error_t ret = CL_ETMPFILE; + + if (0 == dataLen || NULL == data) { + cli_warnmsg("writeWholeFile: Invalid arguments\n"); + ret = CL_EARG; + goto done; + } + + /*Not sure if I care about the name that is actually created.*/ + if (cli_gentempfd_with_prefix(ctx->sub_tmpdir, fileName, &tmpf, &fd) != CL_SUCCESS) { + cli_warnmsg("writeWholeFile: Can't create temp file\n"); + return CL_ETMPFILE; + } + + if (cli_writen(fd, data, dataLen) != dataLen) { + cli_warnmsg("iso_scan_file: Can't write to file %s\n", tmpf); + ret = CL_EWRITE; + goto done; + } + + ret = cli_magic_scan_desc(fd, tmpf, ctx, fileName, LAYER_ATTRIBUTES_NONE); + + close(fd); + if (!ctx->engine->keeptmp) { + if (cli_unlink(tmpf)) { + ret = CL_EUNLINK; + goto done; + } + } + +done: + FREE(tmpf); + + return ret; +} + +static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, void *address, uint16_t icbFlags, FileIdentifierDescriptor *fileIdentifierDescriptor) +{ + + cl_error_t ret = CL_SUCCESS; + uint32_t offset = 0; + uint32_t length = 0; + uint8_t *contents = NULL; + + if (isDirectory(fileIdentifierDescriptor)) { + goto done; + } + + switch (icbFlags & 3) { + case 0: { + short_ad *shortDesc = (short_ad *)address; + + offset = pPartitionDescriptor->partitionStartingLocation * pLogicalVolumeDescriptor->logicalBlockSize; + offset += shortDesc->position * pLogicalVolumeDescriptor->logicalBlockSize; + + length = shortDesc->length; + + } break; + case 1: { + long_ad *longDesc = (long_ad *)address; + offset = pPartitionDescriptor->partitionStartingLocation * pLogicalVolumeDescriptor->logicalBlockSize; + length = longDesc->length; + + if (longDesc->extentLocation.partitionReferenceNumber != pPartitionDescriptor->partitionNumber) { + cli_warnmsg("extractFile: Unable to extract the files because the Partition Descriptor Reference Numbers don't match\n"); + goto done; + } + offset += longDesc->extentLocation.blockNumber * pLogicalVolumeDescriptor->logicalBlockSize; + offset += pPartitionDescriptor->partitionStartingLocation; + + } break; + case 2: + + { + ext_ad *extDesc = (ext_ad *)address; + offset = pPartitionDescriptor->partitionStartingLocation * pLogicalVolumeDescriptor->logicalBlockSize; + length = extDesc->recordedLen; + + if (extDesc->extentLocation.partitionReferenceNumber != pPartitionDescriptor->partitionNumber) { + cli_warnmsg("extractFile: Unable to extract the files because the Partition Descriptor Reference Numbers don't match\n"); + goto done; + } + offset += extDesc->extentLocation.blockNumber * pLogicalVolumeDescriptor->logicalBlockSize; + offset += pPartitionDescriptor->partitionStartingLocation; + + } + + break; + default: + //impossible unless the file is malformed. + cli_warnmsg("extractFile: Unknown descriptor type found.\n"); + goto done; + } + + contents = (uint8_t *)fmap_need_off(ctx->fmap, offset, length); + if (NULL == contents) { + cli_warnmsg("extractFile: Unable to get offset referenced in the file.\n"); + goto done; + } + + ret = writeWholeFile(ctx, "Test", contents, length); + + fmap_unneed_off(ctx->fmap, offset, length); + +done: + + return ret; +} + +static int parseFileEntryDescriptor(cli_ctx *ctx, const uint8_t *const data, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, FileIdentifierDescriptor *fileIdentifierDescriptor) +{ + + FileEntryDescriptor *fed = (FileEntryDescriptor *)data; + int ret = -1; + + if (261 != fed->tag.tagId) { + cli_warnmsg("parseFileEntryDescriptor: Tag ID of 0x%x does not match File Entry Descriptor.\n", fed->tag.tagId); + goto done; + } + + if (257 != fileIdentifierDescriptor->tag.tagId) { + cli_warnmsg("parseFileEntryDescriptor: Tag ID of 0x%x does not match File Identifier Descriptor.\n", fed->tag.tagId); + goto done; + } + + extractFile(ctx, pPartitionDescriptor, pLogicalVolumeDescriptor, + (void *)&(data[getFileEntryDescriptorSize(fed) - fed->allocationDescLen]), + fed->icbTag.flags, fileIdentifierDescriptor); + + ret = 0; +done: + return ret; +} + +void dumpTag(DescriptorTag *dt) +{ + fprintf(stderr, "TagId = %d (0x%x)\n", dt->tagId, dt->tagId); + fprintf(stderr, "Version = %d (0x%x)\n", dt->descriptorVersion, dt->descriptorVersion); + fprintf(stderr, "Checksum = %d (0x%x)\n", dt->checksum, dt->checksum); + fprintf(stderr, "Serial Number = %d (0x%x)\n", dt->serialNumber, dt->serialNumber); + + fprintf(stderr, "Descriptor CRC = %d (0x%x)\n", dt->descriptorCRC, dt->descriptorCRC); + fprintf(stderr, "Descriptor CRC Length = %d (0x%x)\n", dt->descriptorCRCLength, dt->descriptorCRCLength); + fprintf(stderr, "Tag Location = %d (0x%x)\n", dt->tagLocation, dt->tagLocation); +} + +typedef struct { + uint8_t structType; + char standardIdentifier[5]; + uint8_t structVersion; + uint8_t rest[2041]; +} GenericVolumeStructureDescriptor; +#define NUM_GENERIC_VOLUME_DESCRIPTORS 3 + +/*If this function fails, idx will not be updated*/ +int skipEmptyDescriptors(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + int ret = -1; + uint8_t *buffer = NULL; + size_t idx = *idxp; + bool allzeros = true; + size_t i; + + while (1) { + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + + allzeros = true; + for (i = 0; i < VOLUME_DESCRIPTOR_SIZE; i++) { + if (0 != buffer[i]) { + allzeros = false; + break; + } + } + if (!allzeros) { + break; + } + idx += VOLUME_DESCRIPTOR_SIZE; + } + + ret = 0; +done: + + *idxp = idx; + *lastOffsetp = idx; + + return ret; +} + +typedef enum { + PRIMARY_VOLUME_DESCRIPTOR = 1, + IMPLEMENTATION_USE_VOLUME_DESCRIPTOR = 4, + LOGICAL_VOLUME_DESCRIPTOR = 6, + PARTITION_DESCRIPTOR = 5, + UNALLOCATED_SPACE_DESCRIPTOR = 7, + TERMINATING_DESCRIPTOR = 8, + LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR = 9, + ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER = 2, + FILE_SET_DESCRIPTOR = 256 +} VOLUME_DESCRIPTOR_TAG; + +static PrimaryVolumeDescriptor *getPrimaryVolumeDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + PrimaryVolumeDescriptor *test = NULL; + PrimaryVolumeDescriptor *ret = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (PrimaryVolumeDescriptor *)buffer; + if (PRIMARY_VOLUME_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + idx += VOLUME_DESCRIPTOR_SIZE; + ret = test; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static ImplementationUseVolumeDescriptor *getImplementationUseVolumeDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + ImplementationUseVolumeDescriptor *test = NULL; + ImplementationUseVolumeDescriptor *ret = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (ImplementationUseVolumeDescriptor *)buffer; + if (IMPLEMENTATION_USE_VOLUME_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static LogicalVolumeDescriptor *getLogicalVolumeDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + LogicalVolumeDescriptor *ret = NULL; + LogicalVolumeDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (LogicalVolumeDescriptor *)buffer; + if (LOGICAL_VOLUME_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + idx += VOLUME_DESCRIPTOR_SIZE; + ret = test; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static PartitionDescriptor *getPartitionDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + PartitionDescriptor *ret = NULL; + PartitionDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (PartitionDescriptor *)buffer; + if (PARTITION_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static UnallocatedSpaceDescriptor *getUnallocatedSpaceDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + UnallocatedSpaceDescriptor *ret = NULL; + UnallocatedSpaceDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (UnallocatedSpaceDescriptor *)buffer; + if (UNALLOCATED_SPACE_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static TerminatingDescriptor *getTerminatingDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + TerminatingDescriptor *ret = NULL; + TerminatingDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (TerminatingDescriptor *)buffer; + if (TERMINATING_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static LogicalVolumeIntegrityDescriptor *getLogicalVolumeIntegrityDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + LogicalVolumeIntegrityDescriptor *ret = NULL; + LogicalVolumeIntegrityDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (LogicalVolumeIntegrityDescriptor *)buffer; + if (LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static AnchorVolumeDescriptorPointer *getAnchorVolumeDescriptorPointer(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + AnchorVolumeDescriptorPointer *ret = NULL; + AnchorVolumeDescriptorPointer *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (AnchorVolumeDescriptorPointer *)buffer; + if (ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER != test->tag.tagId) { + goto done; + } + + ret = test; + + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +static FileSetDescriptor *getFileSetDescriptor(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp) +{ + uint8_t *buffer = NULL; + FileSetDescriptor *ret = NULL; + FileSetDescriptor *test = NULL; + size_t idx = *idxp; + size_t lastOffset = *lastOffsetp; + + if (skipEmptyDescriptors(ctx, idxp, lastOffsetp)) { + goto done; + } + + idx = *idxp; + lastOffset = *lastOffsetp; + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + test = (FileSetDescriptor *)buffer; + if (FILE_SET_DESCRIPTOR != test->tag.tagId) { + goto done; + } + + ret = test; + idx += VOLUME_DESCRIPTOR_SIZE; + +done: + *idxp = idx; + *lastOffsetp = lastOffset; + + return ret; +} + +typedef struct { + + const uint8_t **idxs; + + uint32_t cnt; + + uint32_t capacity; + +} PointerList; +#define POINTER_LIST_INCREMENT 1024 + +static void freePointerList(PointerList *pl) +{ + FREE(pl->idxs); + memset(pl, 0, sizeof(PointerList)); +} + +static cl_error_t initPointerList(PointerList *pl) +{ + cl_error_t ret = CL_SUCCESS; + uint32_t capacity = POINTER_LIST_INCREMENT; + + freePointerList(pl); + CLI_CALLOC(pl->idxs, capacity, sizeof(uint8_t *), + cli_errmsg("initPointerList: Can't allocate memory\n"); + ret = CL_EMEM); + + pl->capacity = capacity; +done: + return ret; +} + +static cl_error_t insertPointer(PointerList *pl, const uint8_t *pointer) +{ + cl_error_t ret = CL_SUCCESS; + + if (pl->cnt == (pl->capacity - 1)) { + uint32_t newCapacity = pl->capacity + POINTER_LIST_INCREMENT; + CLI_REALLOC(pl->idxs, newCapacity * sizeof(uint8_t *), + cli_errmsg("insertPointer: Can't allocate memory\n"); + ret = CL_EMEM); + + pl->capacity = newCapacity; + } + + pl->idxs[pl->cnt++] = pointer; + +done: + return ret; +} + +static cl_error_t findFileIdentifiers(const uint8_t *const input, PointerList *pfil) +{ + + cl_error_t ret = CL_SUCCESS; + const uint8_t *buffer = input; + uint16_t tagId = getDescriptorTagId(buffer); + + while (257 == tagId) { + if (CL_SUCCESS != (ret = insertPointer(pfil, buffer))) { + goto done; + } + + buffer = buffer + getFileIdentifierDescriptorSize((FileIdentifierDescriptor *)buffer); + tagId = getDescriptorTagId(buffer); + } + +done: + return ret; +} + +static cl_error_t findFileEntries(const uint8_t *const input, PointerList *pfil) +{ + + cl_error_t ret = CL_SUCCESS; + const uint8_t *buffer = input; + uint16_t tagId = getDescriptorTagId(buffer); + + while (261 == tagId) { + if (CL_SUCCESS != (ret = insertPointer(pfil, buffer))) { + goto done; + } + + buffer = buffer + getFileEntryDescriptorSize((FileEntryDescriptor *)buffer); + tagId = getDescriptorTagId(buffer); + } + +done: + return ret; +} + +cl_error_t parseBEA01(cli_ctx *ctx, const size_t offset) +{ + cl_error_t ret = CL_SUCCESS; + size_t idx = offset; + size_t lastOffset = 0; + size_t i = 0; + uint8_t *buffer = NULL; + PrimaryVolumeDescriptor *pvd = NULL; + GenericVolumeStructureDescriptor *gvsd = NULL; + ImplementationUseVolumeDescriptor *iuvd = NULL; + LogicalVolumeDescriptor *lvd = NULL; + PartitionDescriptor *pd = NULL; + UnallocatedSpaceDescriptor *usd = NULL; + TerminatingDescriptor *td = NULL; + LogicalVolumeIntegrityDescriptor *lvid = NULL; + AnchorVolumeDescriptorPointer *avdp = NULL; + + bool isInitialized = false; + PointerList fileIdentifierList; + PointerList fileEntryList; + + if (offset < 32768) { + return CL_SUCCESS; /* Need 16 sectors at least 2048 bytes long */ + } + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, NUM_GENERIC_VOLUME_DESCRIPTORS * VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + ret = CL_SUCCESS; + goto done; + } + + /*There seem to always be 3 of these. + * TODO: Maybe still just keep going until + * + * The format matters depending on the standard identifier. + */ + for (i = 0; i < NUM_GENERIC_VOLUME_DESCRIPTORS; i++) { + gvsd = (GenericVolumeStructureDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + lastOffset = idx; + + if (strncmp("BEA01", gvsd->standardIdentifier, 5)) { + } else if (strncmp("BOOT2", gvsd->standardIdentifier, 5)) { + } else if (strncmp("CD001", gvsd->standardIdentifier, 5)) { + } else if (strncmp("CDW02", gvsd->standardIdentifier, 5)) { + } else if (strncmp("NSR02", gvsd->standardIdentifier, 5)) { + } else if (strncmp("NSR03", gvsd->standardIdentifier, 5)) { + } else if (strncmp("TEA01", gvsd->standardIdentifier, 5)) { + } else { + cli_warnmsg("Unknown Standard Identifier '%s'\n", gvsd->standardIdentifier); + break; + } + + idx += VOLUME_DESCRIPTOR_SIZE; + } + + memset(&fileIdentifierList, 0, sizeof(PointerList)); + memset(&fileEntryList, 0, sizeof(PointerList)); + + while (1) { + + if (!isInitialized) { + + if (CL_SUCCESS != (ret = initPointerList(&fileIdentifierList))) { + goto done; + } + + if (CL_SUCCESS != (ret = initPointerList(&fileEntryList))) { + goto done; + } + + if (NULL == (pvd = getPrimaryVolumeDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (iuvd = getImplementationUseVolumeDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (lvd = getLogicalVolumeDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (pd = getPartitionDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (usd = getUnallocatedSpaceDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (td = getTerminatingDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + /*May not be every file, need to verify.*/ + if (NULL == (lvid = getLogicalVolumeIntegrityDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (td = getTerminatingDescriptor(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == (avdp = getAnchorVolumeDescriptorPointer(ctx, &idx, &lastOffset))) { + goto done; + } + + if (NULL == getFileSetDescriptor(ctx, &idx, &lastOffset)) { + goto done; + } + + isInitialized = true; + } + + buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer) { + goto done; + } + lastOffset = idx; + + uint16_t tagId = getDescriptorTagId(buffer); + if (tagId) { + switch (tagId) { + case 257: + + findFileIdentifiers(buffer, &fileIdentifierList); + break; + + case 261: + + findFileEntries(buffer, &fileEntryList); + break; + case 8: + break; + + default: { + /*Dump all the files here.*/ + size_t i; + size_t cnt = fileIdentifierList.cnt; + + /*The number of file entries should match the number of file identifiers, but in the + * case that the file is malformed, we are going to do the best we can to extract as much as we can. + */ + if (fileEntryList.cnt < cnt) { + cnt = fileEntryList.cnt; + } + + for (i = 0; i < cnt; i++) { + if (parseFileEntryDescriptor(ctx, + (const uint8_t *const)fileEntryList.idxs[i], + pd, lvd, (FileIdentifierDescriptor *)fileIdentifierList.idxs[i])) { + goto done; + } + } + + /* Start looking for the next volume */ + isInitialized = false; + break; + } + } + } + + idx += VOLUME_DESCRIPTOR_SIZE; + } + +done: + freePointerList(&fileIdentifierList); + freePointerList(&fileEntryList); + + for (idx = offset; idx <= lastOffset; idx += VOLUME_DESCRIPTOR_SIZE) { + fmap_unneed_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + } + + return ret; +} + +#if 0 +/*Used for debugging.*/ +static void dumpAllTags(cli_ctx *ctx, size_t offset){ + uint8_t * buffer; + size_t lastOffset, startoffset = offset; + size_t i; + + while (1){ + buffer = (uint8_t*) fmap_need_off(ctx->fmap, offset, VOLUME_DESCRIPTOR_SIZE); + if (NULL == buffer){ + break; + } + lastOffset = offset; + + uint16_t tagId = getDescriptorTagId(buffer); + if (tagId){ + + fprintf(stderr, "%s::%d::%lx::tagId = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, tagId, tagId); + } + + offset += VOLUME_DESCRIPTOR_SIZE; + } + + for (i = startoffset; i <= lastOffset; i+= VOLUME_DESCRIPTOR_SIZE){ + fmap_unneed_off(ctx->fmap, i, VOLUME_DESCRIPTOR_SIZE); + } +} +#endif + cl_error_t cli_scaniso(cli_ctx *ctx, size_t offset) { const uint8_t *privol, *next; @@ -237,8 +1078,9 @@ cl_error_t cli_scaniso(cli_ctx *ctx, size_t offset) return CL_SUCCESS; next = (uint8_t *)cli_memstr((char *)privol + 2049, 2448 + 6 - 2049, "CD001", 5); - if (!next) - return CL_SUCCESS; /* Find next volume descriptor */ + if (!next) { + return parseBEA01(ctx, offset); + } iso.sectsz = (next - privol) - 1; if (iso.sectsz * 16 > offset) diff --git a/libclamav/iso9660.h b/libclamav/iso9660.h index e8b2e3d984..6eb289cdc4 100644 --- a/libclamav/iso9660.h +++ b/libclamav/iso9660.h @@ -24,6 +24,510 @@ #include "others.h" + +#define UDF_EMPTY_LEN 32768 + +#define VOLUME_DESCRIPTOR_SIZE 0x800 + +typedef struct { + uint16_t typeTimeZone; /* + 0 Coordinated UTC + 1 Local Time + 2 Up to agreement between originator and recipient + 3 - 15 Reserved + */ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t centiseconds; + uint8_t hundredsMicroSeconds; + uint8_t microseconds; +} timestamp; + + +typedef struct __attribute__((packed)) { + + uint32_t blockNumber; + + uint16_t partitionReferenceNumber; + +} lb_addr; + +typedef struct __attribute__((packed)) { + uint32_t length; //4/14.14.1.1 + /*30 least significant bits are length in bytes. + * + * 2 most significant bits are described in figure 4/42 + * + * 0 extent recorded and allocated + * 1 extent NOT recorded but allocated + * 2 extent NOT recorded and NOT allocated + * 3 the extent is the next extent of allocation descriptors. + * */ + + lb_addr extentLocation; //logical block number. (CAN be zero) + + uint8_t implementationUse[6]; + +} long_ad; + + + +/* + * https://www.ecma-international.org/wp-content/uploads/ECMA-167_3rd_edition_june_1997.pdf + * section 3/7.2 */ +typedef struct __attribute__((packed)) { + uint16_t tagId; + uint16_t descriptorVersion; + uint8_t checksum; + uint8_t reserved; + uint16_t serialNumber; + uint16_t descriptorCRC; + uint16_t descriptorCRCLength; + uint32_t tagLocation; +} DescriptorTag; + +typedef struct { + uint8_t flags; + /* + * 1/7.4 + * characteristics + * bit 0 dirty: If regid has been modified and might not be valid, set to * 1. Otherwise, 0 + * bit 1 protected: If 1, this regid cannot be modified + * bit 2-7 reserved + */ + uint8_t identifier[23]; + /* + * If first byte is 0x2b, then this is covered by ECMA-168 (this spec) + * If first byte is 0x2d, then this is not registered + */ + uint8_t identifierSuffix[8]; +} regid; + + + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + uint32_t volumeDescriptorSequenceNumber; + uint32_t primaryVolumeDescriptorNumber; + uint8_t volumeIdentifier[32]; + uint16_t volumeSequenceNumber; + uint16_t interchangeLevel; + uint16_t maxInterchangeLevel; + uint32_t charSetList; + uint8_t volumeSetIdentifier[128]; + uint8_t descriptorCharSet[64]; + uint8_t explanatoryCharSet[64]; + uint64_t volumeAbstract; + uint64_t volumeCopyrightNotice; + uint8_t applicationIdentifier[32]; + uint8_t recordingDateTime[12]; + uint8_t implementationIdentifier[32]; + uint8_t implementationUse[64]; + uint32_t predVolumeDescSequenceLocation; + uint16_t flags; + uint8_t reserved[22]; + +} PrimaryVolumeDescriptor; + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + uint32_t volumeDescriptorSequenceNumber; + + regid implementationIdentifier; + uint8_t implementationUse[460]; + +} ImplementationUseVolumeDescriptor; + + +/* https://www.ecma-international.org/wp-content/uploads/ECMA-167_3rd_edition_june_1997.pdf */ +/* 4/3 */ +typedef struct __attribute__((packed)) { + uint32_t logicalBlockNumber; + + uint16_t partitionReferenceNumber; +} LBAddr; + +//https://www.ecma-international.org/wp-content/uploads/ECMA-167_3rd_edition_june_1997.pdf +//section 4/23 +typedef struct __attribute__((packed)) { + uint32_t priorRecordedNumberOfDirectEntries; + uint16_t strategyType; + uint8_t strategyParameter[2]; /*described as 'bytes' in docs, so don't want to worry about byte order.*/ + uint16_t maxEntries; + uint8_t reserved_must_be_zero; + + uint8_t fileType; + + LBAddr parentICBLocation; + + uint16_t flags; +} ICBTag; + + + +typedef struct __attribute__((packed)) { + + DescriptorTag tag; + + uint16_t versionNumber; + + uint8_t characteristics; + + uint8_t fileIdentifierLength; + + long_ad icb; + + uint16_t implementationLength; + + uint8_t rest[1]; + +}FileIdentifierDescriptor; + +#define FILE_IDENTIFIER_DESCRIPTOR_SIZE_KNOWN (sizeof(FileIdentifierDescriptor) - 1) + + +/*Section 14.4.9 of https:... */ +static uint32_t getFileIdentifierDescriptorPaddingLength(const FileIdentifierDescriptor * const fid){ + uint32_t ret = 0; + uint32_t tmp = fid->implementationLength + fid->fileIdentifierLength + 38; + ret = tmp + 3; + ret = ret / 4; + + ret = ret * 4; + ret = ret - tmp; + + return ret; +} + +static inline size_t getFileIdentifierDescriptorSize(const FileIdentifierDescriptor * fid){ + + return FILE_IDENTIFIER_DESCRIPTOR_SIZE_KNOWN + fid->implementationLength + fid->fileIdentifierLength + getFileIdentifierDescriptorPaddingLength(fid); + +} + + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + ICBTag icbTag; + + uint32_t uid; + + uint32_t gid; + + uint32_t permissions ; + + uint16_t fileLinkCnt; + + uint8_t recordFormat; + uint8_t recordDisplayAttributes; + + + uint32_t recordLength; + + uint64_t infoLength; + + uint64_t logicalBlocksRecorded; + + timestamp accessDateTime; + + timestamp modificationDateTime; + + timestamp attributeDateTime; + + uint32_t checkpoint; + + long_ad extendedAttrICB; + + regid implementationId; + + uint64_t uniqueId; + + uint32_t extendedAttrLen; + + uint32_t allocationDescLen; + + /* Variable length stuff here, need to handle; + */ + uint8_t rest[1]; + + +} FileEntryDescriptor; + +#define FILE_ENTRY_DESCRIPTOR_SIZE_KNOWN (sizeof(FileEntryDescriptor) - 1) +static inline size_t getFileEntryDescriptorSize(const FileEntryDescriptor* fed){ + return FILE_ENTRY_DESCRIPTOR_SIZE_KNOWN + fed->extendedAttrLen + fed->allocationDescLen; +} + + + +typedef struct { + DescriptorTag tag; + + ICBTag icbTag; + + uint32_t uid; + + uint32_t gid; + + uint32_t permissions ; + + uint16_t fileLinkCnt; + + uint8_t recordFormat; + + uint8_t recordDisplayAttributes; + + uint32_t recordLength; + + uint64_t infoLength; + + uint64_t objectSize; //different + + uint64_t logicalBlocksRecorded; + + timestamp accessDateTime; + + timestamp modificationDateTime; + + timestamp creationDateTime; //different + + timestamp attributeDateTime; + + uint32_t checkpoint; + + uint32_t reserved; //different + + long_ad extendedAttrICB; + + long_ad streamDirectoryICB; //different + + regid implementationId; + + uint64_t uniqueId; + + uint32_t extendedAttrLen; + + uint32_t allocationDescLen; + + /* Variable length stuff here, need to handle; + */ + + +} ExtendedFileEntryDescriptor; + + + + +typedef struct { + + uint32_t length; + + uint32_t position; + +} short_ad; + +typedef struct { + uint32_t extentLen; + uint32_t recordedLen; + + uint32_t infoLen; + + lb_addr extentLocation; + + uint8_t implementationUse[2]; +} ext_ad; + +typedef struct { + uint32_t extentLength; + + uint32_t extentLocation; + +} extent_ad; + + +typedef struct { + + DescriptorTag tag; + + uint32_t volumeDescriptorSequenceNumber; + + uint16_t partitionFlags; + + uint16_t partitionNumber; + + regid partitionContents; + + uint8_t partitionContentsUse[128]; + + uint32_t accessType; + + uint32_t partitionStartingLocation ; + + uint32_t partitionLength; + + regid implementationIdentifier; + + uint8_t implementationUse[128]; + + uint8_t reserved[156]; + +} PartitionDescriptor; + + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + uint32_t volumeDescriptorSequenceNumber; + + uint32_t numAllocationDescriptors; + + uint8_t rest[1]; /*reset is 'numAllocationDescriptors' * sizeof (extent_ad), + and padded out to VOLUME_DESCRIPTOR_SIZE with zeros. */ + +} UnallocatedSpaceDescriptor; + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + uint8_t padding[496]; +} TerminatingDescriptor; + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + timestamp recordingDateTime; + + uint32_t integrityType; + + extent_ad nextIntegrityExtent; + + uint8_t logicalVolumeContents[32]; + + uint32_t numPartitions; + + uint32_t lenImplementationUse ; + + uint32_t freeSpaceTable; + + uint32_t sizeTable; + + uint8_t rest[1]; + +} LogicalVolumeIntegrityDescriptor; + + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + extent_ad mainVolumeDescriptorSequence; + + extent_ad reserveVolumeDescriptorSequence; + + uint8_t reserved[480]; + +} AnchorVolumeDescriptorPointer; + +typedef struct __attribute__((packed)) { + DescriptorTag tag; + + uint32_t volumeDescriptorSequenceNumber; + + extent_ad nextVolumeDescriptorSequence; + + uint8_t reserved[484]; + +} VolumeDescriptorPointer; + + +/* + * charsetType can be + 0 The CS0 coded character set (1/7.2.2). + 1 The CS1 coded character set (1/7.2.3). + 2 The CS2 coded character set (1/7.2.4). + 3 The CS3 coded character set (1/7.2.5). + 4 The CS4 coded character set (1/7.2.6). + 5 The CS5 coded character set (1/7.2.7). + 6 The CS6 coded character set (1/7.2.8). + 7 The CS7 coded character set (1/7.2.9). + 8 The CS8 coded character set (1/7.2.10). + 9-255 Reserved for future standardisation. + * + */ +typedef struct { + uint8_t charSetType; + + uint8_t charSetInfo[63]; +} charspec; + + + +typedef struct { + + DescriptorTag tag; + + uint32_t volumeDescriptorSequenceNumber; + + charspec descriptorCharSet; + + uint8_t logicalVolumeIdentifier[128]; //TODO: handle dstring + + uint32_t logicalBlockSize; + + regid domainIdentifier; + + uint8_t logicalVolumeContentsUse[16]; + + uint32_t mapTableLength; + + uint32_t numPartitionMaps; + + regid implementationIdentifier; + + uint8_t implementationUse[128]; + + ext_ad integritySequenceExtent; + + uint8_t partitionMaps[1]; //actual length of mapTableLength above; + +}LogicalVolumeDescriptor ; + +typedef struct { + DescriptorTag tag; + timestamp recordingDateTime; + + uint16_t interchangeLevel; + + uint16_t maxInterchangeLevel; + uint32_t characterSetList; + uint32_t maxCharacterSetList; + + uint32_t fileSetNumber; + uint32_t fileSetDescriptorNumber; + + charspec logicalVolumeIdentifierCharSet; + uint8_t logicalVolumeIdentifier[128]; + charspec fileSetCharSet; + uint8_t fileSetIdentifier[32]; + + uint8_t copyrightIdentifier[32]; + uint8_t abstractIdentifier[32]; + long_ad rootDirectoryICB; + + regid domainIdentifier; + + long_ad nextExtent; + long_ad systemStreamDirectoryICB; + uint8_t reserved[32]; + +} FileSetDescriptor; + + + cl_error_t cli_scaniso(cli_ctx *ctx, size_t offset); #endif