From 7a16470b9b06603be1332f75f5b0ef046e1cb5ae Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Thu, 11 Apr 2024 15:44:45 -0400 Subject: [PATCH] UDF: Minor code cleanup Immediately store pointers as new pointer type rather than using intermediate uint8_t pointer. Also "unneed" some of the "needed" pointers as soon as we're able to release them rather than holding on until the end of the UDF image. Add assorted debug messages and code comments. Make FileSetDescriptor optional as minor step towards supporting ExtendedFileEntries. Minor variable name changes for readability. Use tag_identifier enum for variable type rather than uint16_t and add "INVALID_DESCRIPTOR" (0) to enum and use it in the switch. This way we're not comparing enums with ints. Move GenericVolumeStructureDescriptor to udf.h. --- libclamav/udf.c | 403 +++++++++++++++++++++++++++++------------------- libclamav/udf.h | 13 +- 2 files changed, 254 insertions(+), 162 deletions(-) diff --git a/libclamav/udf.c b/libclamav/udf.c index 7004a1ea64..a2f4100af3 100644 --- a/libclamav/udf.c +++ b/libclamav/udf.c @@ -29,6 +29,7 @@ #include "hashtab.h" typedef enum { + INVALID_DESCRIPTOR = 0, PRIMARY_VOLUME_DESCRIPTOR = 1, IMPLEMENTATION_USE_VOLUME_DESCRIPTOR = 4, LOGICAL_VOLUME_DESCRIPTOR = 6, @@ -36,17 +37,16 @@ typedef enum { UNALLOCATED_SPACE_DESCRIPTOR = 7, TERMINATING_DESCRIPTOR = 8, LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR = 9, - ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER = 2 - - , - FILE_SET_DESCRIPTOR = 256, - FILE_IDENTIFIER_DESCRIPTOR = 257, - FILE_ENTRY_DESCRIPTOR = 261 -} VOLUME_DESCRIPTOR_TAG; - -static uint16_t getDescriptorTagId(const uint8_t *const buffer) + ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER = 2, + FILE_SET_DESCRIPTOR = 256, + FILE_IDENTIFIER_DESCRIPTOR = 257, + FILE_ENTRY_DESCRIPTOR = 261, + EXTENDED_FILE_ENTRY_DESCRIPTOR = 266 +} tag_identifier; + +static tag_identifier getDescriptorTagId(DescriptorTag *tag) { - return le16_to_host(((DescriptorTag *)buffer)->tagId); + return le16_to_host(tag->tagId); } static bool isDirectory(FileIdentifierDescriptor *fid) @@ -56,7 +56,6 @@ static bool isDirectory(FileIdentifierDescriptor *fid) static cl_error_t writeWholeFile(cli_ctx *ctx, const char *const fileName, const uint8_t *const data, const size_t dataLen) { - int fd = -1; char *tmpf = NULL; @@ -105,23 +104,32 @@ static cl_error_t writeWholeFile(cli_ctx *ctx, const char *const fileName, const return status; } -static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, void *address, uint16_t icbFlags, FileIdentifierDescriptor *fileIdentifierDescriptor) +static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, + void *allocation_descriptor, + size_t allocation_descriptor_len, + uint16_t icbFlags, FileIdentifierDescriptor *fileIdentifierDescriptor) { - - cl_error_t ret = CL_SUCCESS; - uint32_t offset = 0; - uint32_t length = 0; - uint8_t *contents = NULL; - uint32_t partitionStartingLocation = le32_to_host(pPartitionDescriptor->partitionStartingLocation); - uint32_t logicalBlockSize = le32_to_host( pLogicalVolumeDescriptor->logicalBlockSize); + cl_error_t ret = CL_EPARSE; + uint32_t offset = 0; + uint32_t length = 0; + uint8_t *contents = NULL; + uint32_t partitionStartingLocation = le32_to_host(pPartitionDescriptor->partitionStartingLocation); + uint32_t logicalBlockSize = le32_to_host(pLogicalVolumeDescriptor->logicalBlockSize); if (isDirectory(fileIdentifierDescriptor)) { + cli_dbgmsg("extractFile: Skipping directory\n"); + ret = CL_SUCCESS; goto done; } switch (icbFlags & 3) { case 0: { - short_ad *shortDesc = (short_ad *)address; + if (sizeof(short_ad) != allocation_descriptor_len) { + cli_warnmsg("extractFile: Short Allocation Descriptor length is incorrect.\n"); + goto done; + } + + short_ad *shortDesc = (short_ad *)allocation_descriptor; offset = partitionStartingLocation * logicalBlockSize; offset += le32_to_host(shortDesc->position) * logicalBlockSize; @@ -130,12 +138,17 @@ static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescr } break; case 1: { - long_ad *longDesc = (long_ad *)address; - offset = partitionStartingLocation * logicalBlockSize; - length = le32_to_host(longDesc->length); + if (sizeof(long_ad) != allocation_descriptor_len) { + cli_warnmsg("extractFile: Long Allocation Descriptor length is incorrect.\n"); + goto done; + } + + long_ad *longDesc = (long_ad *)allocation_descriptor; + + offset = partitionStartingLocation * logicalBlockSize; + length = le32_to_host(longDesc->length); - if (le16_to_host(longDesc->extentLocation.partitionReferenceNumber) - != le16_to_host(pPartitionDescriptor->partitionNumber)) { + if (le16_to_host(longDesc->extentLocation.partitionReferenceNumber) != le16_to_host(pPartitionDescriptor->partitionNumber)) { cli_warnmsg("extractFile: Unable to extract the files because the Partition Descriptor Reference Numbers don't match\n"); goto done; } @@ -144,12 +157,17 @@ static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescr } break; case 2: { - ext_ad *extDesc = (ext_ad *)address; - offset = partitionStartingLocation * logicalBlockSize; - length = le32_to_host(extDesc->recordedLen); + if (sizeof(ext_ad) != allocation_descriptor_len) { + cli_warnmsg("extractFile: Extended Allocation Descriptor length is incorrect.\n"); + goto done; + } + + ext_ad *extDesc = (ext_ad *)allocation_descriptor; + + offset = partitionStartingLocation * logicalBlockSize; + length = le32_to_host(extDesc->recordedLen); - if (le16_to_host(extDesc->extentLocation.partitionReferenceNumber) - != le16_to_host(pPartitionDescriptor->partitionNumber)) { + if (le16_to_host(extDesc->extentLocation.partitionReferenceNumber) != le16_to_host(pPartitionDescriptor->partitionNumber)) { cli_warnmsg("extractFile: Unable to extract the files because the Partition Descriptor Reference Numbers don't match\n"); goto done; } @@ -177,28 +195,49 @@ static cl_error_t extractFile(cli_ctx *ctx, PartitionDescriptor *pPartitionDescr return ret; } -static bool parseFileEntryDescriptor(cli_ctx *ctx, const uint8_t *const data, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, FileIdentifierDescriptor *fileIdentifierDescriptor) +static bool parseFileEntryDescriptor(cli_ctx *ctx, FileEntryDescriptor *fed, PartitionDescriptor *pPartitionDescriptor, LogicalVolumeDescriptor *pLogicalVolumeDescriptor, FileIdentifierDescriptor *fileIdentifierDescriptor) { + bool ret = false; + uint16_t tagId = getDescriptorTagId(&fed->tag); + void *allocation_descriptor = NULL; - FileEntryDescriptor *fed = (FileEntryDescriptor *)data; - bool ret = false; - uint16_t tagId = getDescriptorTagId((const uint8_t*) &(fed->tag)); + size_t file_entry_descriptor_size; + size_t allocation_descriptor_len; if (FILE_ENTRY_DESCRIPTOR != tagId) { cli_warnmsg("parseFileEntryDescriptor: Tag ID of 0x%x does not match File Entry Descriptor.\n", tagId); goto done; } - tagId = getDescriptorTagId((const uint8_t*) &(fileIdentifierDescriptor->tag)); + tagId = getDescriptorTagId(&fileIdentifierDescriptor->tag); if (FILE_IDENTIFIER_DESCRIPTOR != tagId) { cli_warnmsg("parseFileEntryDescriptor: Tag ID of 0x%x does not match File Identifier Descriptor.\n", tagId); goto done; } + // Calculate pointer for the allocation descriptor. + // The allocation descriptors are the last bytes of the Extended File Entry. + // See Section 14.17 in https://www.ecma-international.org/wp-content/uploads/ECMA-167_3rd_edition_june_1997.pdf + file_entry_descriptor_size = getFileEntryDescriptorSize(fed); + allocation_descriptor_len = le32_to_host(fed->allocationDescLen); + + if (allocation_descriptor_len > file_entry_descriptor_size) { + cli_dbgmsg("parseFileEntryDescriptor: Allocation Descriptor Length is greater than the File Entry Descriptor Size.\n"); + goto done; + } + allocation_descriptor = (void *)((uint8_t *)fed + (file_entry_descriptor_size - allocation_descriptor_len)); + + // The Allocation Descriptor was taken from the end of the File Entry Descriptor. + // We already verified that the File Entry Descriptor is within the fmap, + // so it's safe to say the Allocation Descriptor is also within the fmap. + // No need to use an `fmap_need...()` function here. + + // Extract the file. if (CL_SUCCESS != extractFile(ctx, pPartitionDescriptor, pLogicalVolumeDescriptor, - (void *)&(data[getFileEntryDescriptorSize(fed) - le32_to_host(fed->allocationDescLen)]), + allocation_descriptor, + allocation_descriptor_len, le16_to_host(fed->icbTag.flags), fileIdentifierDescriptor)) { - ret = false; + cli_dbgmsg("parseFileEntryDescriptor: Failed to extract file.\n"); goto done; } @@ -222,12 +261,6 @@ static void dumpTag (DescriptorTag *dt) } */ -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 */ @@ -240,7 +273,6 @@ static bool skipEmptyDescriptors(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp size_t i; while (1) { - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); if (NULL == buffer) { goto done; @@ -272,7 +304,6 @@ static bool skipEmptyDescriptors(cli_ctx *ctx, size_t *idxp, size_t *lastOffsetp * Return error if the next non-empty descriptor is not a PrimaryVolumeDescriptor. */ 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; @@ -285,15 +316,13 @@ static PrimaryVolumeDescriptor *getPrimaryVolumeDescriptor(cli_ctx *ctx, size_t idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (PrimaryVolumeDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (PrimaryVolumeDescriptor *)buffer; - - if (PRIMARY_VOLUME_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (PRIMARY_VOLUME_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -311,7 +340,6 @@ static PrimaryVolumeDescriptor *getPrimaryVolumeDescriptor(cli_ctx *ctx, size_t * Return error if the next non-empty descriptor is not an ImplementationUseVolumeDescriptor. */ 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; @@ -324,14 +352,13 @@ static ImplementationUseVolumeDescriptor *getImplementationUseVolumeDescriptor(c idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (ImplementationUseVolumeDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (ImplementationUseVolumeDescriptor *)buffer; - if (IMPLEMENTATION_USE_VOLUME_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (IMPLEMENTATION_USE_VOLUME_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -349,7 +376,6 @@ static ImplementationUseVolumeDescriptor *getImplementationUseVolumeDescriptor(c * Return error if the next non-empty descriptor is not a LogicalVolumeDescriptor. */ 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; @@ -362,14 +388,13 @@ static LogicalVolumeDescriptor *getLogicalVolumeDescriptor(cli_ctx *ctx, size_t idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (LogicalVolumeDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (LogicalVolumeDescriptor *)buffer; - if (LOGICAL_VOLUME_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (LOGICAL_VOLUME_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -387,7 +412,6 @@ static LogicalVolumeDescriptor *getLogicalVolumeDescriptor(cli_ctx *ctx, size_t * Return error if the next non-empty descriptor is not a PartitionDescriptor. */ 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; @@ -400,14 +424,13 @@ static PartitionDescriptor *getPartitionDescriptor(cli_ctx *ctx, size_t *idxp, s idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (PartitionDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (PartitionDescriptor *)buffer; - if (PARTITION_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (PARTITION_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -425,7 +448,6 @@ static PartitionDescriptor *getPartitionDescriptor(cli_ctx *ctx, size_t *idxp, s * Return error if the next non-empty descriptor is not a UnallocatedSpaceDescriptor. */ 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; @@ -438,14 +460,13 @@ static UnallocatedSpaceDescriptor *getUnallocatedSpaceDescriptor(cli_ctx *ctx, s idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (UnallocatedSpaceDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (UnallocatedSpaceDescriptor *)buffer; - if (UNALLOCATED_SPACE_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (UNALLOCATED_SPACE_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -463,7 +484,6 @@ static UnallocatedSpaceDescriptor *getUnallocatedSpaceDescriptor(cli_ctx *ctx, s * Return error if the next non-empty descriptor is not a TerminatingDescriptor. */ 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; @@ -476,14 +496,13 @@ static TerminatingDescriptor *getTerminatingDescriptor(cli_ctx *ctx, size_t *idx idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (TerminatingDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (TerminatingDescriptor *)buffer; - if (TERMINATING_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (TERMINATING_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -501,7 +520,6 @@ static TerminatingDescriptor *getTerminatingDescriptor(cli_ctx *ctx, size_t *idx * Return error if the next non-empty descriptor is not a LogicalVolumeIntegrityDescriptor. */ 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; @@ -514,14 +532,13 @@ static LogicalVolumeIntegrityDescriptor *getLogicalVolumeIntegrityDescriptor(cli idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (LogicalVolumeIntegrityDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (LogicalVolumeIntegrityDescriptor *)buffer; - if (LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -539,7 +556,6 @@ static LogicalVolumeIntegrityDescriptor *getLogicalVolumeIntegrityDescriptor(cli * Return error if the next non-empty descriptor is not an AnchorVolumeDescriptor. */ 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; @@ -552,19 +568,17 @@ static AnchorVolumeDescriptorPointer *getAnchorVolumeDescriptorPointer(cli_ctx * idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (AnchorVolumeDescriptorPointer *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (AnchorVolumeDescriptorPointer *)buffer; - if (ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (ANCHOR_VOLUME_DESCRIPTOR_DESCRIPTOR_POINTER != getDescriptorTagId(&test->tag)) { goto done; } ret = test; - idx += VOLUME_DESCRIPTOR_SIZE; done: @@ -578,7 +592,6 @@ static AnchorVolumeDescriptorPointer *getAnchorVolumeDescriptorPointer(cli_ctx * * Return error if the next non-empty descriptor is not a FileSetDescriptor. */ 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; @@ -591,14 +604,13 @@ static FileSetDescriptor *getFileSetDescriptor(cli_ctx *ctx, size_t *idxp, size_ idx = *idxp; lastOffset = *lastOffsetp; - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + test = (FileSetDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == test) { goto done; } lastOffset = idx; - test = (FileSetDescriptor *)buffer; - if (FILE_SET_DESCRIPTOR != getDescriptorTagId((const uint8_t*) &(test->tag))) { + if (FILE_SET_DESCRIPTOR != getDescriptorTagId(&test->tag)) { goto done; } @@ -665,11 +677,10 @@ static cl_error_t insertPointer(PointerList *pl, const uint8_t *pointer) 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); - uint64_t bufUsed; + uint16_t tagId = getDescriptorTagId((DescriptorTag *)buffer); + size_t bufUsed; size_t fidDescSize; while (FILE_IDENTIFIER_DESCRIPTOR == tagId) { @@ -680,16 +691,16 @@ static cl_error_t findFileIdentifiers(const uint8_t *const input, PointerList *p } /*This is how far into the Volume we already are.*/ - bufUsed = ((uint64_t) buffer) - ((uint64_t) input); + bufUsed = buffer - input; fidDescSize = getFileIdentifierDescriptorSize((FileIdentifierDescriptor *)buffer); /* Check that it's safe to read the header for the next FileIdentifierDescriptor */ - if (VOLUME_DESCRIPTOR_SIZE < (fidDescSize + bufUsed + FILE_IDENTIFIER_DESCRIPTOR_SIZE_KNOWN)){ + if (VOLUME_DESCRIPTOR_SIZE < (fidDescSize + bufUsed + FILE_IDENTIFIER_DESCRIPTOR_SIZE_KNOWN)) { break; } buffer = buffer + fidDescSize; - tagId = getDescriptorTagId(buffer); + tagId = getDescriptorTagId((DescriptorTag *)buffer); } done: @@ -698,11 +709,10 @@ static cl_error_t findFileIdentifiers(const uint8_t *const input, PointerList *p 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); - uint64_t bufUsed; + uint16_t tagId = getDescriptorTagId((DescriptorTag *)buffer); + size_t bufUsed; size_t fedDescSize; while (FILE_ENTRY_DESCRIPTOR == tagId) { @@ -711,16 +721,16 @@ static cl_error_t findFileEntries(const uint8_t *const input, PointerList *pfil) } /*This is how far into the Volume we already are.*/ - bufUsed = ((uint64_t) buffer) - ((uint64_t) input); + bufUsed = buffer - input; fedDescSize = getFileEntryDescriptorSize((FileEntryDescriptor *)buffer); /* Check that it's safe to read the header for the next FileEntryDescriptor */ - if (VOLUME_DESCRIPTOR_SIZE < (fedDescSize + bufUsed + FILE_ENTRY_DESCRIPTOR_SIZE_KNOWN )){ + if (VOLUME_DESCRIPTOR_SIZE < (fedDescSize + bufUsed + FILE_ENTRY_DESCRIPTOR_SIZE_KNOWN)) { break; } buffer = buffer + fedDescSize; - tagId = getDescriptorTagId(buffer); + tagId = getDescriptorTagId((DescriptorTag *)buffer); } done: @@ -733,7 +743,6 @@ cl_error_t cli_scanudf(cli_ctx *ctx, const size_t offset) 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; @@ -743,6 +752,8 @@ cl_error_t cli_scanudf(cli_ctx *ctx, const size_t offset) TerminatingDescriptor *td = NULL; LogicalVolumeIntegrityDescriptor *lvid = NULL; AnchorVolumeDescriptorPointer *avdp = NULL; + FileSetDescriptor *fsd = NULL; + DescriptorTag *file_volume_tag = NULL; bool isInitialized = false; PointerList fileIdentifierList = {0}; @@ -752,14 +763,16 @@ cl_error_t cli_scanudf(cli_ctx *ctx, const size_t offset) 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; - } + cli_dbgmsg("Scanning UDF file\n"); for (i = 0; i < NUM_GENERIC_VOLUME_DESCRIPTORS; i++) { - gvsd = (GenericVolumeStructureDescriptor *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + gvsd = (GenericVolumeStructureDescriptor *)fmap_need_off(ctx->fmap, idx, sizeof(GenericVolumeStructureDescriptor)); + if (NULL == gvsd) { + // File isn't long enough to store the required generic volume structure descriptors at the given offset. + ret = CL_SUCCESS; + goto done; + } + lastOffset = idx; if (strncmp("BEA01", gvsd->standardIdentifier, 5)) { @@ -781,121 +794,187 @@ cl_error_t cli_scanudf(cli_ctx *ctx, const size_t offset) break; } - idx += VOLUME_DESCRIPTOR_SIZE; + fmap_unneed_ptr(ctx->fmap, gvsd, sizeof(GenericVolumeStructureDescriptor)); + + idx += sizeof(GenericVolumeStructureDescriptor); } while (1) { if (!isInitialized) { + /* We don't use most of these descriptors, but verify they all exist because + * they are part of a properly formatted udf file. */ if (CL_SUCCESS != (ret = initPointerList(&fileIdentifierList))) { + cli_dbgmsg("Failed to initialize fileIdentifierList\n"); goto done; } if (CL_SUCCESS != (ret = initPointerList(&fileEntryList))) { + cli_dbgmsg("Failed to initialize fileEntryList\n"); goto done; } if (NULL == (pvd = getPrimaryVolumeDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Primary Volume Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, pvd, VOLUME_DESCRIPTOR_SIZE); if (NULL == (iuvd = getImplementationUseVolumeDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Implementation Use Volume Descriptor\n"); goto done; } + // Hold on to this pointer, we'll use it later. + // We'll release it after `done`. if (NULL == (lvd = getLogicalVolumeDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Logical Volume Descriptor\n"); goto done; } + // Hold on to this pointer, we'll use it later. + // We'll release it after `done`. if (NULL == (pd = getPartitionDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Partition Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, pd, VOLUME_DESCRIPTOR_SIZE); if (NULL == (usd = getUnallocatedSpaceDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Unallocated Space Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, usd, VOLUME_DESCRIPTOR_SIZE); if (NULL == (td = getTerminatingDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Terminating Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, td, VOLUME_DESCRIPTOR_SIZE); - /* May not be every file, need to verify. */ if (NULL == (lvid = getLogicalVolumeIntegrityDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Logical Volume Integrity Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, lvid, VOLUME_DESCRIPTOR_SIZE); if (NULL == (td = getTerminatingDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Terminating Descriptor\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, td, VOLUME_DESCRIPTOR_SIZE); if (NULL == (avdp = getAnchorVolumeDescriptorPointer(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get Anchor Volume Descriptor Pointer\n"); goto done; } + fmap_unneed_ptr(ctx->fmap, avdp, VOLUME_DESCRIPTOR_SIZE); - if (NULL == getFileSetDescriptor(ctx, &idx, &lastOffset)) { - /*We don't actually use the FileSetDescriptor, but verify it is there because - * that is part of a properly formatted udf file.*/ - goto done; + if (NULL == (fsd = getFileSetDescriptor(ctx, &idx, &lastOffset))) { + cli_dbgmsg("Failed to get File Set Descriptor\n"); + + // The file set descriptor may come after an extended file entry descriptor. + idx = lastOffset; + } else { + fmap_unneed_ptr(ctx->fmap, fsd, VOLUME_DESCRIPTOR_SIZE); } isInitialized = true; } - buffer = (uint8_t *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); - if (NULL == buffer) { + /* + * Find all of the file identifier descriptors and file entry descriptors. + */ + + // Need the entire volume descriptor. We'll un-need it at the end. + file_volume_tag = (DescriptorTag *)fmap_need_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL == file_volume_tag) { + cli_dbgmsg("Failed to get File Volume Tag\n"); goto done; } lastOffset = idx; - uint16_t tagId = getDescriptorTagId(buffer); - if (tagId) { - switch (tagId) { - case FILE_IDENTIFIER_DESCRIPTOR: { - cl_error_t temp = findFileIdentifiers(buffer, &fileIdentifierList); - if (CL_SUCCESS != temp) { - ret = temp; - goto done; - } - break; + tag_identifier tagId = getDescriptorTagId(file_volume_tag); + + cli_dbgmsg("UDF Descriptor Tag ID: %d\n", tagId); + + switch (tagId) { + case FILE_IDENTIFIER_DESCRIPTOR: { + cl_error_t temp = findFileIdentifiers((const uint8_t *)file_volume_tag, &fileIdentifierList); + if (CL_SUCCESS != temp) { + ret = temp; + goto done; } + break; + } - case FILE_ENTRY_DESCRIPTOR: { - cl_error_t temp = findFileEntries(buffer, &fileEntryList); - if (CL_SUCCESS != temp) { - ret = temp; - goto done; - } - break; + case FILE_ENTRY_DESCRIPTOR: { + cl_error_t temp = findFileEntries((const uint8_t *)file_volume_tag, &fileEntryList); + if (CL_SUCCESS != temp) { + ret = temp; + goto done; } + break; + } - case TERMINATING_DESCRIPTOR: - break; + case EXTENDED_FILE_ENTRY_DESCRIPTOR: { + // Not supported yet. Skip. + break; + } - default: { - /* Dump all the files here. */ - size_t i; - size_t cnt = fileIdentifierList.cnt; + case TERMINATING_DESCRIPTOR: { + // Skip. + break; + } - /* 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; - } + case INVALID_DESCRIPTOR: { + // Skip. + break; + } - for (i = 0; i < cnt; i++) { - if (!parseFileEntryDescriptor(ctx, - (const uint8_t *const)fileEntryList.idxs[i], - pd, lvd, (FileIdentifierDescriptor *)fileIdentifierList.idxs[i])) { - goto done; - } - } + default: { + // TODO: Something feels wrong about doing this in `default:`. + // Is there a specific value we can look for to be certain we found them all? + // Right now this code appears to work by running into an invalid tagId when + // actually is out of descriptors and starts indexing into file data. + // Ideally we would end the loop when we know we've found all the descriptors, + // and then do this after the loop. + + cli_dbgmsg("cli_scanudf: Parsing %d file entries.\n", fileEntryList.cnt); + + /* Dump all the files here. */ + size_t cnt = fileIdentifierList.cnt; - /* Start looking for the next volume */ - isInitialized = false; - break; + /* 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, + (FileEntryDescriptor *)fileEntryList.idxs[i], + pd, lvd, (FileIdentifierDescriptor *)fileIdentifierList.idxs[i])) { + cli_dbgmsg("cli_scanudf: Failed to extract file %zu\n", i); + goto done; + } + } + + /* + * We're done with this volume. Release our pointers and free up our pointer lists. + * Start looking for the next volume. + */ + fmap_unneed_ptr(ctx->fmap, iuvd, VOLUME_DESCRIPTOR_SIZE); + iuvd = NULL; + fmap_unneed_ptr(ctx->fmap, lvd, VOLUME_DESCRIPTOR_SIZE); + lvd = NULL; + fmap_unneed_ptr(ctx->fmap, file_volume_tag, VOLUME_DESCRIPTOR_SIZE); + file_volume_tag = NULL; + + isInitialized = false; + break; } } @@ -906,8 +985,14 @@ cl_error_t cli_scanudf(cli_ctx *ctx, const size_t offset) freePointerList(&fileIdentifierList); freePointerList(&fileEntryList); - for (idx = offset; idx <= lastOffset; idx += VOLUME_DESCRIPTOR_SIZE) { - fmap_unneed_off(ctx->fmap, idx, VOLUME_DESCRIPTOR_SIZE); + if (NULL != iuvd) { + fmap_unneed_ptr(ctx->fmap, iuvd, VOLUME_DESCRIPTOR_SIZE); + } + if (NULL != lvd) { + fmap_unneed_ptr(ctx->fmap, lvd, VOLUME_DESCRIPTOR_SIZE); + } + if (NULL != file_volume_tag) { + fmap_unneed_ptr(ctx->fmap, file_volume_tag, VOLUME_DESCRIPTOR_SIZE); } return ret; diff --git a/libclamav/udf.h b/libclamav/udf.h index 0a3bea9916..edb6e3278d 100644 --- a/libclamav/udf.h +++ b/libclamav/udf.h @@ -25,6 +25,7 @@ #define UDF_EMPTY_LEN 32768 +// All Volume Descriptors are the same size. #define VOLUME_DESCRIPTOR_SIZE 0x800 #ifndef HAVE_ATTRIB_PACKED @@ -66,6 +67,7 @@ typedef struct __attribute__((packed)) { } lb_addr; +// Long allocation descriptor typedef struct __attribute__((packed)) { uint32_t length; //4/14.14.1.1 /*30 least significant bits are length in bytes. @@ -209,9 +211,6 @@ static uint32_t getFileIdentifierDescriptorPaddingLength(const FileIdentifierDes static inline size_t getFileIdentifierDescriptorSize(const FileIdentifierDescriptor* fid) { - - fprintf(stderr, "%s::%d::Could we check for VOLUME_DESCRIPTOR_SIZE???\n", __FUNCTION__, __LINE__); - return FILE_IDENTIFIER_DESCRIPTOR_SIZE_KNOWN + le16_to_host(fid->implementationLength) + fid->fileIdentifierLength @@ -324,6 +323,7 @@ typedef struct __attribute__((packed)) { } ExtendedFileEntryDescriptor; +// Short allocation descriptor typedef struct __attribute__((packed)) { uint32_t length; @@ -521,6 +521,13 @@ typedef struct __attribute__((packed)) { } FileSetDescriptor; +typedef struct __attribute__((packed)) { + uint8_t structType; + char standardIdentifier[5]; + uint8_t structVersion; + uint8_t rest[2041]; +} GenericVolumeStructureDescriptor; + #ifdef HAVE_PRAGMA_PACK #pragma pack() #endif