From 73f5fc7a036649225871918774a4a1607a692e8b Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Wed, 17 Oct 2012 09:39:44 -0400 Subject: [PATCH] Bug 798413 - Export signature from MAR files implementation. r=bsmith, a=blocking-basecamp --- modules/libmar/sign/mar_sign.c | 114 +++++++++++++++++++++++++++++++ modules/libmar/src/mar.h | 7 +- modules/libmar/src/mar_cmdline.h | 12 ++++ modules/libmar/tool/mar.c | 43 ++++++++++-- 4 files changed, 170 insertions(+), 6 deletions(-) diff --git a/modules/libmar/sign/mar_sign.c b/modules/libmar/sign/mar_sign.c index 9cc2b7d8867f..c5be32791af9 100644 --- a/modules/libmar/sign/mar_sign.c +++ b/modules/libmar/sign/mar_sign.c @@ -22,6 +22,7 @@ #endif #include "nss_secutil.h" +#include "base64.h" /** * Initializes the NSS context. @@ -477,6 +478,119 @@ failure: return rv; } +/** + * Extracts a signature from a MAR file, base64 encodes it, and writes it out + * + * @param src The path of the source MAR file + * @param sigIndex The index of the signature to extract + * @param dest The path of file to write the signature to + * @return 0 on success + * -1 on error +*/ +int +extract_signature(const char *src, uint32_t sigIndex, const char * dest) +{ + FILE *fpSrc = NULL, *fpDest = NULL; + uint32_t i; + uint32_t signatureCount; + uint32_t signatureLen; + uint8_t *extractedSignature = NULL; + char *base64Encoded = NULL; + int rv = -1; + if (!src || !dest) { + fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); + goto failure; + } + + fpSrc = fopen(src, "rb"); + if (!fpSrc) { + fprintf(stderr, "ERROR: could not open source file: %s\n", src); + goto failure; + } + + fpDest = fopen(dest, "wb"); + if (!fpDest) { + fprintf(stderr, "ERROR: could not create target file: %s\n", dest); + goto failure; + } + + /* Skip to the start of the signature block */ + if (fseeko(fpSrc, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) { + fprintf(stderr, "ERROR: could not seek to signature block\n"); + goto failure; + } + + /* Get the number of signatures */ + if (fread(&signatureCount, sizeof(signatureCount), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: could not read signature count\n"); + goto failure; + } + signatureCount = ntohl(signatureCount); + if (sigIndex >= signatureCount) { + fprintf(stderr, "ERROR: Signature index was out of range\n"); + goto failure; + } + + /* Skip to the correct signature */ + for (i = 0; i <= sigIndex; i++) { + /* skip past the signature algorithm ID */ + if (fseeko(fpSrc, sizeof(uint32_t), SEEK_CUR)) { + fprintf(stderr, "ERROR: Could not seek past sig algorithm ID.\n"); + goto failure; + } + + /* Get the signature length */ + if (fread(&signatureLen, sizeof(signatureLen), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: could not read signature length\n"); + goto failure; + } + signatureLen = ntohl(signatureLen); + + /* Get the signature */ + extractedSignature = malloc(signatureLen); + if (fread(extractedSignature, signatureLen, 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: could not read signature\n"); + goto failure; + } + } + + base64Encoded = BTOA_DataToAscii(extractedSignature, signatureLen); + if (!base64Encoded) { + fprintf(stderr, "ERROR: could not obtain base64 encoded data\n"); + goto failure; + } + + if (fwrite(base64Encoded, strlen(base64Encoded), 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write base64 encoded string\n"); + goto failure; + } + + rv = 0; +failure: + if (base64Encoded) { + PORT_Free(base64Encoded); + } + + if (extractedSignature) { + free(extractedSignature); + } + + if (fpSrc) { + fclose(fpSrc); + } + + if (fpDest) { + fclose(fpDest); + } + + if (rv) { + remove(dest); + } + + return rv; +} + + /** * Writes out a copy of the MAR at src but with embedded signatures. * The passed in MAR file must not already be signed or an error will diff --git a/modules/libmar/src/mar.h b/modules/libmar/src/mar.h index 8d7e8f00543a..a794e838d711 100644 --- a/modules/libmar/src/mar.h +++ b/modules/libmar/src/mar.h @@ -16,8 +16,11 @@ extern "C" { #endif /* We have a MAX_SIGNATURES limit so that an invalid MAR will never - waste too much of either updater's or signmar's time. - It is also used at various places internally and will affect memory usage. */ + * waste too much of either updater's or signmar's time. + * It is also used at various places internally and will affect memory usage. + * If you want to increase this value above 9 then you need to adjust parsing + * code in tool/mar.c. +*/ #define MAX_SIGNATURES 8 PR_STATIC_ASSERT(MAX_SIGNATURES <= 9); diff --git a/modules/libmar/src/mar_cmdline.h b/modules/libmar/src/mar_cmdline.h index 8fd3dafcb4ee..d18209d093ff 100644 --- a/modules/libmar/src/mar_cmdline.h +++ b/modules/libmar/src/mar_cmdline.h @@ -108,6 +108,18 @@ refresh_product_info_block(const char *path, int strip_signature_block(const char *src, const char * dest); +/** + * Extracts a signature from a MAR file, base64 encodes it, and writes it out + * + * @param src The path of the source MAR file + * @param sigIndex The index of the signature to extract + * @param dest The path of file to write the signature to + * @return 0 on success + * -1 on error +*/ +int +extract_signature(const char *src, uint32_t sigIndex, const char * dest); + #ifdef __cplusplus } #endif diff --git a/modules/libmar/tool/mar.c b/modules/libmar/tool/mar.c index b91eecd38bd0..c7d6f7de5988 100644 --- a/modules/libmar/tool/mar.c +++ b/modules/libmar/tool/mar.c @@ -30,18 +30,27 @@ int mar_repackage_and_sign(const char *NSSConfigDir, static void print_usage() { printf("usage:\n"); + printf("Create a MAR file:\n"); printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " "{-c|-x|-t|-T} archive.mar [files...]\n"); #ifndef NO_SIGN_VERIFY + printf("Sign a MAR file:\n"); printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s " "archive.mar out_signed_archive.mar\n"); + printf("Strip a MAR signature:\n"); printf(" mar [-C workingDir] -r " "signed_input_archive.mar output_archive.mar\n"); + printf("Extract a MAR signature:\n"); + printf(" mar [-C workingDir] -n(i) -X " + "signed_input_archive.mar base_64_encoded_signature_file\n"); + printf("(i) is the index of the certificate to extract\n"); #if defined(XP_WIN) && !defined(MAR_NSS) + printf("Verify a MAR file:\n"); printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); printf("At most %d signature certificate DER files are specified by " "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); #else + printf("Verify a MAR file:\n"); printf(" mar [-C workingDir] -d NSSConfigDir -n certname " "-v signed_archive.mar\n"); printf("At most %d signature certificate names are specified by " @@ -50,6 +59,7 @@ static void print_usage() { printf("At most %d verification certificate names are specified by " "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); #endif + printf("Print information on a MAR file:\n"); printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " "-i unsigned_archive_to_refresh.mar\n"); printf("This program does not handle unicode file paths properly\n"); @@ -84,6 +94,7 @@ int main(int argc, char **argv) { uint32_t i, k; int rv = -1; uint32_t certCount = 0; + int32_t sigIndex = -1; #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) HANDLE certFile; /* We use DWORD here instead of uint64_t because it simplifies code with @@ -112,7 +123,7 @@ int main(int argc, char **argv) { argv[1][1] == 't' || argv[1][1] == 'x' || argv[1][1] == 'v' || argv[1][1] == 's' || argv[1][1] == 'i' || argv[1][1] == 'T' || - argv[1][1] == 'r')) { + argv[1][1] == 'r' || argv[1][1] == 'X')) { break; /* -C workingdirectory */ } else if (argv[1][0] == '-' && argv[1][1] == 'C') { @@ -146,14 +157,24 @@ int main(int argc, char **argv) { with the import and export command line arguments. */ } else if (argv[1][0] == '-' && argv[1][1] == 'n' && - (argv[1][2] == '0' + certCount || argv[1][2] == '\0')) { + (argv[1][2] == '0' + certCount || + argv[1][2] == '\0' || + !strcmp(argv[2], "-X"))) { if (certCount >= MAX_SIGNATURES) { print_usage(); return -1; } certNames[certCount++] = argv[2]; - argv += 2; - argc -= 2; + if (strlen(argv[1]) > 2 && + !strcmp(argv[2], "-X") && + argv[1][2] >= '0' && argv[1][2] <= '9') { + sigIndex = argv[1][2] - '0'; + argv++; + argc--; + } else { + argv += 2; + argc -= 2; + } /* MAR channel ID */ } else if (argv[1][0] == '-' && argv[1][1] == 'H') { MARChannelID = argv[2]; @@ -226,10 +247,24 @@ int main(int argc, char **argv) { case 't': return mar_test(argv[2]); + /* Extract a MAR file */ case 'x': return mar_extract(argv[2]); #ifndef NO_SIGN_VERIFY + /* Extract a MAR signature */ + case 'X': + if (sigIndex == -1) { + fprintf(stderr, "ERROR: Signature index was not passed.\n"); + return -1; + } + if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { + fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", + sigIndex); + return -1; + } + return extract_signature(argv[2], sigIndex, argv[3]); + case 'v': #if defined(XP_WIN) && !defined(MAR_NSS)