From d781b5bdfff1b44bd427e3a27bf64e9bc9537453 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Fri, 24 Feb 2012 16:29:42 -0500 Subject: [PATCH] Bug 720688 - Ability to strip MAR signatures in libmar. r=rstrong --- modules/libmar/sign/mar_sign.c | 321 +++++++++++++++++++++++++++++-- modules/libmar/src/mar_cmdline.h | 12 ++ modules/libmar/tool/mar.c | 9 +- 3 files changed, 326 insertions(+), 16 deletions(-) diff --git a/modules/libmar/sign/mar_sign.c b/modules/libmar/sign/mar_sign.c index a68fe9a5361..36e892ad934 100644 --- a/modules/libmar/sign/mar_sign.c +++ b/modules/libmar/sign/mar_sign.c @@ -173,6 +173,33 @@ WriteAndUpdateSignature(FILE *fpDest, void *buffer, return 0; } +/** + * Adjusts each entry's content offset in the the passed in index by the + * specified amount. + * + * @param indexBuf A buffer containing the MAR index + * @param indexLength The length of the MAR index + * @param offsetAmount The amount to adjust each index entry by +*/ +void +AdjustIndexContentOffsets(char *indexBuf, PRUint32 indexLength, PRUint32 offsetAmount) +{ + PRUint32 *offsetToContent; + char *indexBufLoc = indexBuf; + + /* Consume the index and adjust each index by the specified amount */ + while (indexBufLoc != (indexBuf + indexLength)) { + /* Adjust the offset */ + offsetToContent = (PRUint32 *)indexBufLoc; + *offsetToContent = ntohl(*offsetToContent); + *offsetToContent += offsetAmount; + *offsetToContent = htonl(*offsetToContent); + /* Skip past the offset, length, and flags */ + indexBufLoc += 3 * sizeof(PRUint32); + indexBufLoc += strlen(indexBufLoc) + 1; + } +} + /** * Reads from fpSrc, writes it to fpDest, and updates the signature context. * @@ -205,7 +232,275 @@ ReadWriteAndUpdateSignature(FILE *fpSrc, FILE *fpDest, void *buffer, } -/** +/** + * Reads from fpSrc, writes it to fpDest. + * + * @param fpSrc The file pointer to read from. + * @param fpDest The file pointer to write to. + * @param buffer The buffer to write. + * @param size The size of the buffer to write. + * @param err The name of what is being written to in case of error. + * @return 0 on success + * -1 on read error + * -2 on write error +*/ +int +ReadAndWrite(FILE *fpSrc, FILE *fpDest, void *buffer, + PRUint32 size, const char *err) +{ + if (!size) { + return 0; + } + + if (fread(buffer, size, 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could not read %s\n", err); + return -1; + } + + if (fwrite(buffer, size, 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write %s\n", err); + return -2; + } + + return 0; +} + +/** + * Writes out a copy of the MAR at src but with the signature block stripped. + * + * @param src The path of the source MAR file + * @param dest The path of the MAR file to write out that + has no signature block + * @return 0 on success + * -1 on error +*/ +int +strip_signature_block(const char *src, const char * dest) +{ + PRUint32 offsetToIndex, dstOffsetToIndex, indexLength, + numSignatures = 0, leftOver; + PRInt32 stripAmount = 0; + PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, numBytesToCopy, + numChunks, i; + FILE *fpSrc = NULL, *fpDest = NULL; + int rv = -1, hasSignatureBlock; + char buf[BLOCKSIZE]; + char *indexBuf = NULL, *indexBufLoc; + + if (!src || !dest) { + fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); + return -1; + } + + fpSrc = fopen(src, "rb"); + if (!fpSrc) { + fprintf(stderr, "ERROR: could not open source file: %s\n", dest); + goto failure; + } + + fpDest = fopen(dest, "wb"); + if (!fpDest) { + fprintf(stderr, "ERROR: could not create target file: %s\n", dest); + goto failure; + } + + /* Determine if the source MAR file has the new fields for signing or not */ + if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) { + fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); + goto failure; + } + + /* MAR ID */ + if (ReadAndWrite(fpSrc, fpDest, buf, MAR_ID_SIZE, "MAR ID")) { + goto failure; + } + + /* Offset to index */ + if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could not read offset\n"); + goto failure; + } + offsetToIndex = ntohl(offsetToIndex); + + /* Get the real size of the MAR */ + oldPos = ftello(fpSrc); + if (fseeko(fpSrc, 0, SEEK_END)) { + fprintf(stderr, "ERROR: Could not seek to end of file.\n"); + goto failure; + } + realSizeOfSrcMAR = ftello(fpSrc); + if (fseeko(fpSrc, oldPos, SEEK_SET)) { + fprintf(stderr, "ERROR: Could not seek back to current location.\n"); + goto failure; + } + + if (hasSignatureBlock) { + /* Get the MAR length and adjust its size */ + if (fread(&sizeOfEntireMAR, + sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could read mar size\n"); + goto failure; + } + sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); + if (sizeOfEntireMAR != realSizeOfSrcMAR) { + fprintf(stderr, "ERROR: Source MAR is not of the right size\n"); + goto failure; + } + + /* Get the num signatures in the source file so we know what to strip */ + if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could read num signatures\n"); + goto failure; + } + numSignatures = ntohl(numSignatures); + + for (i = 0; i < numSignatures; i++) { + PRUint32 signatureLen; + + /* Skip past the signature algorithm ID */ + if (fseeko(fpSrc, sizeof(PRUint32), SEEK_CUR)) { + fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n"); + } + + /* Read in the length of the signature so we know how far to skip */ + if (fread(&signatureLen, sizeof(PRUint32), 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could not read signatures length.\n"); + return CryptoX_Error; + } + signatureLen = ntohl(signatureLen); + + /* Skip past the signature */ + if (fseeko(fpSrc, signatureLen, SEEK_CUR)) { + fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n"); + } + + stripAmount += sizeof(PRUint32) + sizeof(PRUint32) + signatureLen; + } + + } else { + sizeOfEntireMAR = realSizeOfSrcMAR; + numSignatures = 0; + } + + if (((PRInt64)offsetToIndex) > sizeOfEntireMAR) { + fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n"); + goto failure; + } + + dstOffsetToIndex = offsetToIndex; + if (!hasSignatureBlock) { + dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); + } + dstOffsetToIndex -= stripAmount; + + /* Write out the index offset */ + dstOffsetToIndex = htonl(dstOffsetToIndex); + if (fwrite(&dstOffsetToIndex, sizeof(dstOffsetToIndex), 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write offset to index\n"); + goto failure; + } + dstOffsetToIndex = ntohl(dstOffsetToIndex); + + /* Write out the new MAR file size */ + if (!hasSignatureBlock) { + sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); + } + sizeOfEntireMAR -= stripAmount; + + /* Write out the MAR size */ + sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); + if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write size of MAR\n"); + goto failure; + } + sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); + + /* Write out the number of signatures, which is 0 */ + numSignatures = 0; + if (fwrite(&numSignatures, sizeof(numSignatures), 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write out num signatures\n"); + goto failure; + } + + /* Write out the rest of the MAR excluding the index header and index + offsetToIndex unfortunately has to remain 32-bit because for backwards + compatibility with the old MAR file format. */ + if (ftello(fpSrc) > ((PRInt64)offsetToIndex)) { + fprintf(stderr, "ERROR: Index offset is too small.\n"); + goto failure; + } + numBytesToCopy = ((PRInt64)offsetToIndex) - ftello(fpSrc); + numChunks = numBytesToCopy / BLOCKSIZE; + leftOver = numBytesToCopy % BLOCKSIZE; + + /* Read each file and write it to the MAR file */ + for (i = 0; i < numChunks; ++i) { + if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) { + goto failure; + } + } + + /* Write out the left over */ + if (ReadAndWrite(fpSrc, fpDest, buf, + leftOver, "left over content block")) { + goto failure; + } + + /* Length of the index */ + if (ReadAndWrite(fpSrc, fpDest, &indexLength, + sizeof(indexLength), "index length")) { + goto failure; + } + indexLength = ntohl(indexLength); + + /* Consume the index and adjust each index by the difference */ + indexBuf = malloc(indexLength); + indexBufLoc = indexBuf; + if (fread(indexBuf, indexLength, 1, fpSrc) != 1) { + fprintf(stderr, "ERROR: Could not read index\n"); + goto failure; + } + + /* Adjust each entry in the index */ + if (hasSignatureBlock) { + AdjustIndexContentOffsets(indexBuf, indexLength, -stripAmount); + } else { + AdjustIndexContentOffsets(indexBuf, indexLength, + sizeof(sizeOfEntireMAR) + + sizeof(numSignatures) - + stripAmount); + } + + if (fwrite(indexBuf, indexLength, 1, fpDest) != 1) { + fprintf(stderr, "ERROR: Could not write index\n"); + goto failure; + } + + rv = 0; +failure: + if (fpSrc) { + fclose(fpSrc); + } + + if (fpDest) { + fclose(fpDest); + } + + if (rv) { + remove(dest); + } + + if (indexBuf) { + free(indexBuf); + } + + if (rv) { + remove(dest); + } + return rv; +} + +/** * Writes out a copy of the MAR at src but with an embedded signature. * The passed in MAR file must not already be signed or an error will * be returned. @@ -225,7 +520,7 @@ mar_repackage_and_sign(const char *NSSConfigDir, { PRUint32 offsetToIndex, dstOffsetToIndex, indexLength, numSignatures = 0, signatureLength, leftOver, - signatureAlgorithmID, *offsetToContent, signatureSectionLength; + signatureAlgorithmID, signatureSectionLength; PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, signaturePlaceholderOffset, numBytesToCopy, numChunks, i; @@ -441,19 +736,17 @@ mar_repackage_and_sign(const char *NSSConfigDir, fprintf(stderr, "ERROR: Could not read index\n"); goto failure; } - while (indexBufLoc != (indexBuf + indexLength)) { - /* Adjust the offset */ - offsetToContent = (PRUint32 *)indexBufLoc; - *offsetToContent = ntohl(*offsetToContent); - if (!hasSignatureBlock) { - *offsetToContent += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); - } - *offsetToContent += signatureSectionLength; - *offsetToContent = htonl(*offsetToContent); - /* Skip past the offset, length, and flags */ - indexBufLoc += 3 * sizeof(PRUint32); - indexBufLoc += strlen(indexBufLoc) + 1; + + /* Adjust each entry in the index */ + if (hasSignatureBlock) { + AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength); + } else { + AdjustIndexContentOffsets(indexBuf, indexLength, + sizeof(sizeOfEntireMAR) + + sizeof(numSignatures) + + signatureSectionLength); } + if (WriteAndUpdateSignature(fpDest, indexBuf, indexLength, ctx, "index")) { goto failure; diff --git a/modules/libmar/src/mar_cmdline.h b/modules/libmar/src/mar_cmdline.h index f3136a5efcc..6e033744fdb 100644 --- a/modules/libmar/src/mar_cmdline.h +++ b/modules/libmar/src/mar_cmdline.h @@ -103,6 +103,18 @@ int read_product_info_block(char *path, struct ProductInformationBlock *infoBlock); +/** + * Writes out a copy of the MAR at src but with the signature block stripped. + * + * @param src The path of the source MAR file + * @param dest The path of the MAR file to write out that + has no signature block + * @return 0 on success + * -1 on error +*/ +int +strip_signature_block(const char *src, const char * dest); + #ifdef __cplusplus } #endif diff --git a/modules/libmar/tool/mar.c b/modules/libmar/tool/mar.c index cdbf69c4c63..8ab53173872 100644 --- a/modules/libmar/tool/mar.c +++ b/modules/libmar/tool/mar.c @@ -65,7 +65,8 @@ static void print_usage() { #ifndef NO_SIGN_VERIFY printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s " "archive.mar out_signed_archive.mar\n"); - + printf(" mar [-C workingDir] -r " + "signed_input_archive.mar output_archive.mar\n"); #if defined(XP_WIN) && !defined(MAR_NSS) printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); #else @@ -120,7 +121,8 @@ int main(int argc, char **argv) { if (argv[1][0] == '-' && (argv[1][1] == 'c' || 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] == 'i' || argv[1][1] == 'T' || + argv[1][1] == 'r')) { break; /* -C workingdirectory */ } else if (argv[1][0] == '-' && argv[1][1] == 'C') { @@ -289,6 +291,9 @@ int main(int argc, char **argv) { return -1; } return mar_repackage_and_sign(NSSConfigDir, certName, argv[2], argv[3]); + + case 'r': + return strip_signature_block(argv[2], argv[3]); #endif /* endif NO_SIGN_VERIFY disabled */ default: