diff --git a/libclamav/udf.c b/libclamav/udf.c index b7201a4187..f18313d55e 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 a 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 a 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 f8d0c99264..e42a99d6e7 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