From ce421714b4e4ea3b19308048ebde2b5193303632 Mon Sep 17 00:00:00 2001 From: olszomal Date: Fri, 4 Jul 2025 15:15:29 +0200 Subject: [PATCH 1/2] Added support for Excel macro-enabled workbook files (XLSM) --- NEWS.md | 2 + README.md | 2 +- appx.c | 123 ++++++++++++++++++++++++++------------ cmake/CMakeTest.cmake | 4 +- tests/files/unsigned.xlsm | Bin 0 -> 8652 bytes 5 files changed, 91 insertions(+), 40 deletions(-) create mode 100644 tests/files/unsigned.xlsm diff --git a/NEWS.md b/NEWS.md index 4890978..2237984 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ ### 2.11 (unreleased) +- added support for Excel macro-enabled workbook files (XLSM) + ### 2.10 (2025.06.23) - added JavaScript signing diff --git a/README.md b/README.md index 834ad05..169f32f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ tool would fail. And, so, osslsigncode was born. ## WHAT CAN IT DO? -It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB, CAT, MSI and APPX files, +It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB, CAT, MSI, APPX and XLSM files, as well as script files with extensions `.ps1`, `.ps1xml`, `.psc1`, `.psd1`, `.psm1`, `.cdxml`, `.mof`, and `.js`. It supports the equivalent of signtool.exe's "-j javasign.dll -jp low", diff --git a/appx.c b/appx.c index 8faba43..61bbead 100644 --- a/appx.c +++ b/appx.c @@ -237,6 +237,7 @@ struct appx_ctx_st { u_char *existingDataHash; u_char *existingCIHash; int isBundle; + int isAppxBlockMap; const EVP_MD *md; int hashlen; } appx_ctx_t; @@ -282,7 +283,8 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con static int appx_compare_hashes(FILE_FORMAT_CTX *ctx); static int appx_remove_ct_signature_entry(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry); static int appx_append_ct_signature_entry(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry); -static const EVP_MD *appx_get_md(ZIP_FILE *zip); +static const EVP_MD *appx_get_md(FILE_FORMAT_CTX *ctx, const EVP_MD *md, ZIP_FILE *zip); +static const EVP_MD *appx_get_md_from_signature(FILE_FORMAT_CTX *ctx); static ZIP_CENTRAL_DIRECTORY_ENTRY *zipGetCDEntryByName(ZIP_FILE *zip, const char *name); static void zipWriteCentralDirectoryEntry(BIO *bio, uint64_t *sizeOnDisk, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, uint64_t offsetDiff); static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64_t dataSize); @@ -332,7 +334,6 @@ static void bioAddU16(BIO *bio, uint16_t v); static FILE_FORMAT_CTX *appx_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata) { FILE_FORMAT_CTX *ctx; - const EVP_MD *md; ZIP_FILE *zip = openZip(options->infile); /* squash unused parameter warnings */ @@ -345,22 +346,18 @@ static FILE_FORMAT_CTX *appx_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *ou if (options->verbose) { zipPrintCentralDirectory(zip); } - md = appx_get_md(zip); - if (!md) { - freeZip(zip); - return NULL; /* FAILED */ - } ctx = OPENSSL_malloc(sizeof(FILE_FORMAT_CTX)); ctx->appx_ctx = OPENSSL_zalloc(sizeof(appx_ctx_t)); ctx->appx_ctx->zip = zip; ctx->format = &file_format_appx; ctx->options = options; - ctx->appx_ctx->md = md; + ctx->appx_ctx->md = appx_get_md(ctx, options->md, zip); if (zipGetCDEntryByName(zip, APPXBUNDLE_MANIFEST_FILENAME)) { ctx->appx_ctx->isBundle = 1; } - if (options->cmd == CMD_SIGN || options->cmd==CMD_ATTACH - || options->cmd==CMD_ADD || options->cmd == CMD_EXTRACT_DATA) { + if (ctx->appx_ctx->isAppxBlockMap && + (options->cmd == CMD_SIGN || options->cmd==CMD_ATTACH + || options->cmd==CMD_ADD || options->cmd == CMD_EXTRACT_DATA)) { printf("Warning: Ignore -h option, use the hash algorithm specified in AppxBlockMap.xml\n"); } if (options->pagehash == 1) @@ -513,7 +510,6 @@ static PKCS7 *appx_pkcs7_extract(FILE_FORMAT_CTX *ctx) /* Check if the signature exists */ if (!zipEntryExist(ctx->appx_ctx->zip, APP_SIGNATURE_FILENAME)) { - fprintf(stderr, "%s does not exist\n", APP_SIGNATURE_FILENAME); return NULL; /* FAILED */ } dataSize = zipReadFileDataByName(&data, ctx->appx_ctx->zip, APP_SIGNATURE_FILENAME); @@ -798,14 +794,17 @@ static BIO *appx_calculate_hashes(FILE_FORMAT_CTX *ctx) { uint64_t cdOffset = 0; - ctx->appx_ctx->calculatedBMHash = zipCalcDigest(ctx->appx_ctx->zip, BLOCK_MAP_FILENAME, ctx->appx_ctx->md); + if (ctx->appx_ctx->isAppxBlockMap) { + ctx->appx_ctx->calculatedBMHash = zipCalcDigest(ctx->appx_ctx->zip, BLOCK_MAP_FILENAME, ctx->appx_ctx->md); + } ctx->appx_ctx->calculatedCTHash = zipCalcDigest(ctx->appx_ctx->zip, CONTENT_TYPES_FILENAME, ctx->appx_ctx->md); ctx->appx_ctx->calculatedDataHash = appx_calc_zip_data_hash(&cdOffset, ctx->appx_ctx->zip, ctx->appx_ctx->md); ctx->appx_ctx->calculatedCDHash = appx_calc_zip_central_directory_hash(ctx->appx_ctx->zip, ctx->appx_ctx->md, cdOffset); ctx->appx_ctx->calculatedCIHash = zipCalcDigest(ctx->appx_ctx->zip, CODE_INTEGRITY_FILENAME, ctx->appx_ctx->md); - if (!ctx->appx_ctx->calculatedBMHash || !ctx->appx_ctx->calculatedCTHash - || !ctx->appx_ctx->calculatedCDHash || !ctx->appx_ctx->calculatedDataHash) { + if ((ctx->appx_ctx->isAppxBlockMap && !ctx->appx_ctx->calculatedBMHash) + || !ctx->appx_ctx->calculatedCTHash || !ctx->appx_ctx->calculatedCDHash + || !ctx->appx_ctx->calculatedDataHash) { fprintf(stderr, "One or more hashes calculation failed\n"); return NULL; /* FAILED */ } @@ -843,10 +842,12 @@ static BIO *appx_hash_blob_get(FILE_FORMAT_CTX *ctx) pos += 4; memcpy(data + pos, ctx->appx_ctx->calculatedCTHash, (size_t)mdlen); pos += mdlen; - memcpy(data + pos, AXBM_SIGNATURE, 4); - pos += 4; - memcpy(data + pos, ctx->appx_ctx->calculatedBMHash, (size_t)mdlen); - pos += mdlen; + if (ctx->appx_ctx->calculatedBMHash) { + memcpy(data + pos, AXBM_SIGNATURE, 4); + pos += 4; + memcpy(data + pos, ctx->appx_ctx->calculatedBMHash, (size_t)mdlen); + pos += mdlen; + } if (ctx->appx_ctx->calculatedCIHash) { memcpy(data + pos, AXCI_SIGNATURE, 4); pos += 4; @@ -854,7 +855,7 @@ static BIO *appx_hash_blob_get(FILE_FORMAT_CTX *ctx) pos += mdlen; } if (ctx->options->verbose) { - print_hash("Hash of file: ", "\n", data, pos); + print_hash("Hash of file ", "\n", data, pos); } ctx->appx_ctx->hashlen = BIO_write(hashes, data, pos); OPENSSL_free(data); @@ -1081,9 +1082,11 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con uint8_t *data = content->messageDigest->digest->data; int mdlen = EVP_MD_size(ctx->appx_ctx->md); int pos = 4; + int expected_hashes = ctx->appx_ctx->isAppxBlockMap ? 4 : 3; - /* we are expecting at least 4 hashes + 4 byte header */ - if (length < 4 * mdlen + 4) { + /* Expecting 4 hashes + 4-byte header if isAppxBlockMap is set, + * otherwise 3 hashes + 4-byte header */ + if (length < expected_hashes * mdlen + 4) { fprintf(stderr, "Hash too short\n"); return 0; /* FAILED */ } @@ -1121,7 +1124,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con fprintf(stderr, "Central directory hash missing\n"); return 0; /* FAILED */ } - if (!ctx->appx_ctx->existingBMHash) { + if (ctx->appx_ctx->isAppxBlockMap && !ctx->appx_ctx->existingBMHash) { fprintf(stderr, "Block map hash missing\n"); return 0; /* FAILED */ } @@ -1145,14 +1148,16 @@ static int appx_compare_hashes(FILE_FORMAT_CTX *ctx) { int mdtype = EVP_MD_nid(ctx->appx_ctx->md); - if (ctx->appx_ctx->calculatedBMHash && ctx->appx_ctx->existingBMHash) { - printf("Checking Block Map hashes:\n"); - if (!compare_digests(ctx->appx_ctx->existingBMHash, ctx->appx_ctx->calculatedBMHash, mdtype)) { + if (ctx->appx_ctx->isAppxBlockMap) { + if (ctx->appx_ctx->calculatedBMHash && ctx->appx_ctx->existingBMHash) { + printf("Checking Block Map hashes:\n"); + if (!compare_digests(ctx->appx_ctx->existingBMHash, ctx->appx_ctx->calculatedBMHash, mdtype)) { + return 0; /* FAILED */ + } + } else { + fprintf(stderr, "Block map hash missing ctx->appx_ctx->isAppxBlockMap=%d\n", ctx->appx_ctx->isAppxBlockMap); return 0; /* FAILED */ } - } else { - fprintf(stderr, "Block map hash missing\n"); - return 0; /* FAILED */ } if (ctx->appx_ctx->calculatedCTHash && ctx->appx_ctx->existingCTHash) { printf("Checking Content Types hashes:\n"); @@ -1270,23 +1275,35 @@ static int appx_append_ct_signature_entry(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_E } /* - * Get a hash algorithm specified in the AppxBlockMap.xml file. + * Determine the message digest algorithm to use when verifying or signing + * 1. From the AppxBlockMap.xml (if available) + * 2. From the PKCS7 signature (if present) + * 3. Fall back to the provided default digest + * [in, out] ctx: structure holds input and output data + * [in] md: default digest algorithm to use if none found elsewhere * [in] zip: structure holds specific ZIP data * [returns] one of SHA256/SHA384/SHA512 digest algorithms */ -static const EVP_MD *appx_get_md(ZIP_FILE *zip) +static const EVP_MD *appx_get_md(FILE_FORMAT_CTX *ctx, const EVP_MD *md, ZIP_FILE *zip) { uint8_t *data = NULL; char *start, *end, *pos; char *valueStart = NULL, *valueEnd = NULL; - const EVP_MD *md = NULL; + const EVP_MD *appx_md = NULL; size_t slen, dataSize; + /* Try to get a hash algorithm specified in the AppxBlockMap.xml file */ dataSize = zipReadFileDataByName(&data, zip, BLOCK_MAP_FILENAME); if (dataSize <= 0) { - fprintf(stderr, "Could not read: %s\n", BLOCK_MAP_FILENAME); - return NULL; /* FAILED */ + /* Excel Spreadsheet Macro-enabled (XLSM) file */ + appx_md = appx_get_md_from_signature(ctx); /* signed XLSM file */ + if (!appx_md) + appx_md = md; /* unsigned, use default message digest algorithm */ + printf("Message digest algorithm: %s\n", EVP_MD_get0_name(appx_md)); + return appx_md; } + ctx->appx_ctx->isAppxBlockMap = 1; /* AppxBlockMap.xml detected */ + start = strstr((const char *)data, HASH_METHOD_TAG); if (!start) { fprintf(stderr, "Parse error: tag: %s not found in %s\n", HASH_METHOD_TAG, BLOCK_MAP_FILENAME); @@ -1322,20 +1339,52 @@ static const EVP_MD *appx_get_md(ZIP_FILE *zip) slen = (size_t)(valueEnd - valueStart + 1); if (strlen(HASH_METHOD_SHA256) == slen && !memcmp(valueStart, HASH_METHOD_SHA256, slen)) { printf("Hash method is SHA256\n"); - md = EVP_sha256(); + appx_md = EVP_sha256(); } else if (strlen(HASH_METHOD_SHA384) == slen && !memcmp(valueStart, HASH_METHOD_SHA384, slen)) { printf("Hash method is SHA384\n"); - md = EVP_sha384(); + appx_md = EVP_sha384(); } else if (strlen(HASH_METHOD_SHA512) == slen && !memcmp(valueStart, HASH_METHOD_SHA512, slen)) { printf("Hash method is SHA512\n"); - md = EVP_sha512(); + appx_md = EVP_sha512(); } else { fprintf(stderr, "Unsupported hash method\n"); OPENSSL_free(data); return NULL; /* FAILED */ } OPENSSL_free(data); - return md; + return appx_md; +} + +/* + * Extract the message digest algorithm used in the PKCS7 signature + * [in] ctx: structure holds input and output data + */ +static const EVP_MD *appx_get_md_from_signature(FILE_FORMAT_CTX *ctx) +{ + + int mdtype = -1; + PKCS7 *p7 = appx_pkcs7_extract(ctx); + + if (!p7) + return NULL; /* FAILED */ + + if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { + ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; + const u_char *p = content_val->data; + SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); + + if (idc) { + if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) { + mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); + } + SpcIndirectDataContent_free(idc); + } + } + if (mdtype == -1) { + fprintf(stderr, "Failed to extract current message digest\n"); + return NULL; /* FAILED */ + } + return EVP_get_digestbynid(mdtype); } /* diff --git a/cmake/CMakeTest.cmake b/cmake/CMakeTest.cmake index 417b27c..44d408a 100644 --- a/cmake/CMakeTest.cmake +++ b/cmake/CMakeTest.cmake @@ -75,8 +75,8 @@ if(Python3_FOUND) endif(NOT client_result) endif(EXISTS "${LOGS}/url.log") - set(extensions_all "exe" "ex_" "msi" "256appx" "512appx" "cat" "ps1" "psc1" "mof" "js") - set(extensions_nocat "exe" "ex_" "msi" "256appx" "512appx" "ps1" "psc1" "mof" "js") + set(extensions_all "exe" "ex_" "msi" "256appx" "512appx" "xlsm" "cat" "ps1" "psc1" "mof" "js") + set(extensions_nocat "exe" "ex_" "msi" "256appx" "512appx" "xlsm" "ps1" "psc1" "mof" "js") set(extensions_nocatappx "exe" "ex_" "msi" "ps1" "psc1" "mof" "js") set(formats "pem" "der") diff --git a/tests/files/unsigned.xlsm b/tests/files/unsigned.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..6ed122dea2009c79ed60c57273516a1472ed7e5f GIT binary patch literal 8652 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 literal 0 HcmV?d00001 From 24233a85cef7ca3504822720d0f4c72b9becd472 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 7 Jul 2025 15:59:11 +0200 Subject: [PATCH 2/2] Replace ftello() with BIO_tell() in get_current_position() --- appx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/appx.c b/appx.c index 61bbead..fa0ae1e 100644 --- a/appx.c +++ b/appx.c @@ -2790,11 +2790,12 @@ static int readZip64EOCDR(ZIP64_EOCDR *eocdr, FILE *file, uint64_t offset) static int get_current_position(BIO *bio, uint64_t *offset) { - FILE *file = NULL; int64_t pos; - BIO_get_fp(bio, &file); - pos = ftello(file); + if (!bio) { + return 0; /* FAILED */ + } + pos = BIO_tell(bio); if (pos < 0) { return 0; /* FAILED */ }