michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "mar.h" michael@0: #include "mar_cmdline.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include michael@0: #define chdir _chdir michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS)) michael@0: int NSSInitCryptoContext(const char *NSSConfigDir); michael@0: #endif michael@0: michael@0: int mar_repackage_and_sign(const char *NSSConfigDir, michael@0: const char * const *certNames, michael@0: uint32_t certCount, michael@0: const char *src, michael@0: const char * dest); michael@0: michael@0: static void print_version() { michael@0: printf("Version: %s\n", MOZ_APP_VERSION); michael@0: printf("Default Channel ID: %s\n", MAR_CHANNEL_ID); michael@0: } michael@0: michael@0: static void print_usage() { michael@0: printf("usage:\n"); michael@0: printf("Create a MAR file:\n"); michael@0: printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " michael@0: "-c archive.mar [files...]\n"); michael@0: michael@0: printf("Extract a MAR file:\n"); michael@0: printf(" mar [-C workingDir] -x archive.mar\n"); michael@0: #ifndef NO_SIGN_VERIFY michael@0: printf("Sign a MAR file:\n"); michael@0: printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s " michael@0: "archive.mar out_signed_archive.mar\n"); michael@0: michael@0: printf("Strip a MAR signature:\n"); michael@0: printf(" mar [-C workingDir] -r " michael@0: "signed_input_archive.mar output_archive.mar\n"); michael@0: michael@0: printf("Extract a MAR signature:\n"); michael@0: printf(" mar [-C workingDir] -n(i) -X " michael@0: "signed_input_archive.mar base_64_encoded_signature_file\n"); michael@0: michael@0: printf("Import a MAR signature:\n"); michael@0: printf(" mar [-C workingDir] -n(i) -I " michael@0: "signed_input_archive.mar base_64_encoded_signature_file " michael@0: "changed_signed_output.mar\n"); michael@0: printf("(i) is the index of the certificate to extract\n"); michael@0: #if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(MAR_NSS)) michael@0: printf("Verify a MAR file:\n"); michael@0: printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); michael@0: printf("At most %d signature certificate DER files are specified by " michael@0: "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); michael@0: #else michael@0: printf("Verify a MAR file:\n"); michael@0: printf(" mar [-C workingDir] -d NSSConfigDir -n certname " michael@0: "-v signed_archive.mar\n"); michael@0: printf("At most %d signature certificate names are specified by " michael@0: "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); michael@0: #endif michael@0: printf("At most %d verification certificate names are specified by " michael@0: "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); michael@0: #endif michael@0: printf("Print information on a MAR file:\n"); michael@0: printf(" mar -t archive.mar\n"); michael@0: michael@0: printf("Print detailed information on a MAR file including signatures:\n"); michael@0: printf(" mar -T archive.mar\n"); michael@0: michael@0: printf("Refresh the product information block of a MAR file:\n"); michael@0: printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " michael@0: "-i unsigned_archive_to_refresh.mar\n"); michael@0: michael@0: printf("Print executable version:\n"); michael@0: printf(" mar --version\n"); michael@0: printf("This program does not handle unicode file paths properly\n"); michael@0: } michael@0: michael@0: static int mar_test_callback(MarFile *mar, michael@0: const MarItem *item, michael@0: void *unused) { michael@0: printf("%u\t0%o\t%s\n", item->length, item->flags, item->name); michael@0: return 0; michael@0: } michael@0: michael@0: static int mar_test(const char *path) { michael@0: MarFile *mar; michael@0: michael@0: mar = mar_open(path); michael@0: if (!mar) michael@0: return -1; michael@0: michael@0: printf("SIZE\tMODE\tNAME\n"); michael@0: mar_enum_items(mar, mar_test_callback, NULL); michael@0: michael@0: mar_close(mar); michael@0: return 0; michael@0: } michael@0: michael@0: int main(int argc, char **argv) { michael@0: char *NSSConfigDir = NULL; michael@0: const char *certNames[MAX_SIGNATURES]; michael@0: char *MARChannelID = MAR_CHANNEL_ID; michael@0: char *productVersion = MOZ_APP_VERSION; michael@0: uint32_t i, k; michael@0: int rv = -1; michael@0: uint32_t certCount = 0; michael@0: int32_t sigIndex = -1; michael@0: michael@0: #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) michael@0: HANDLE certFile; michael@0: uint8_t *certBuffers[MAX_SIGNATURES]; michael@0: #endif michael@0: #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ michael@0: defined(XP_MACOSX)) michael@0: char* DERFilePaths[MAX_SIGNATURES]; michael@0: uint32_t fileSizes[MAX_SIGNATURES]; michael@0: uint32_t read; michael@0: #endif michael@0: michael@0: memset(certNames, 0, sizeof(certNames)); michael@0: #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) michael@0: memset(certBuffers, 0, sizeof(certBuffers)); michael@0: #endif michael@0: #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ michael@0: defined(XP_MACOSX)) michael@0: memset(DERFilePaths, 0, sizeof(DERFilePaths)); michael@0: memset(fileSizes, 0, sizeof(fileSizes)); michael@0: #endif michael@0: michael@0: if (argc > 1 && 0 == strcmp(argv[1], "--version")) { michael@0: print_version(); michael@0: return 0; michael@0: } michael@0: michael@0: if (argc < 3) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: michael@0: while (argc > 0) { michael@0: if (argv[1][0] == '-' && (argv[1][1] == 'c' || michael@0: argv[1][1] == 't' || argv[1][1] == 'x' || michael@0: argv[1][1] == 'v' || argv[1][1] == 's' || michael@0: argv[1][1] == 'i' || argv[1][1] == 'T' || michael@0: argv[1][1] == 'r' || argv[1][1] == 'X' || michael@0: argv[1][1] == 'I')) { michael@0: break; michael@0: /* -C workingdirectory */ michael@0: } else if (argv[1][0] == '-' && argv[1][1] == 'C') { michael@0: chdir(argv[2]); michael@0: argv += 2; michael@0: argc -= 2; michael@0: } michael@0: #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ michael@0: defined(XP_MACOSX)) michael@0: /* -D DERFilePath, also matches -D[index] DERFilePath michael@0: We allow an index for verifying to be symmetric michael@0: with the import and export command line arguments. */ michael@0: else if (argv[1][0] == '-' && michael@0: argv[1][1] == 'D' && michael@0: (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) { michael@0: if (certCount >= MAX_SIGNATURES) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: DERFilePaths[certCount++] = argv[2]; michael@0: argv += 2; michael@0: argc -= 2; michael@0: } michael@0: #endif michael@0: /* -d NSSConfigdir */ michael@0: else if (argv[1][0] == '-' && argv[1][1] == 'd') { michael@0: NSSConfigDir = argv[2]; michael@0: argv += 2; michael@0: argc -= 2; michael@0: /* -n certName, also matches -n[index] certName michael@0: We allow an index for verifying to be symmetric michael@0: with the import and export command line arguments. */ michael@0: } else if (argv[1][0] == '-' && michael@0: argv[1][1] == 'n' && michael@0: (argv[1][2] == (char)('0' + certCount) || michael@0: argv[1][2] == '\0' || michael@0: !strcmp(argv[2], "-X") || michael@0: !strcmp(argv[2], "-I"))) { michael@0: if (certCount >= MAX_SIGNATURES) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: certNames[certCount++] = argv[2]; michael@0: if (strlen(argv[1]) > 2 && michael@0: (!strcmp(argv[2], "-X") || !strcmp(argv[2], "-I")) && michael@0: argv[1][2] >= '0' && argv[1][2] <= '9') { michael@0: sigIndex = argv[1][2] - '0'; michael@0: argv++; michael@0: argc--; michael@0: } else { michael@0: argv += 2; michael@0: argc -= 2; michael@0: } michael@0: /* MAR channel ID */ michael@0: } else if (argv[1][0] == '-' && argv[1][1] == 'H') { michael@0: MARChannelID = argv[2]; michael@0: argv += 2; michael@0: argc -= 2; michael@0: /* Product Version */ michael@0: } else if (argv[1][0] == '-' && argv[1][1] == 'V') { michael@0: productVersion = argv[2]; michael@0: argv += 2; michael@0: argc -= 2; michael@0: } michael@0: else { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: if (argv[1][0] != '-') { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: michael@0: switch (argv[1][1]) { michael@0: case 'c': { michael@0: struct ProductInformationBlock infoBlock; michael@0: infoBlock.MARChannelID = MARChannelID; michael@0: infoBlock.productVersion = productVersion; michael@0: return mar_create(argv[2], argc - 3, argv + 3, &infoBlock); michael@0: } michael@0: case 'i': { michael@0: struct ProductInformationBlock infoBlock; michael@0: infoBlock.MARChannelID = MARChannelID; michael@0: infoBlock.productVersion = productVersion; michael@0: return refresh_product_info_block(argv[2], &infoBlock); michael@0: } michael@0: case 'T': { michael@0: struct ProductInformationBlock infoBlock; michael@0: uint32_t numSignatures, numAdditionalBlocks; michael@0: int hasSignatureBlock, hasAdditionalBlock; michael@0: if (!get_mar_file_info(argv[2], michael@0: &hasSignatureBlock, michael@0: &numSignatures, michael@0: &hasAdditionalBlock, michael@0: NULL, &numAdditionalBlocks)) { michael@0: if (hasSignatureBlock) { michael@0: printf("Signature block found with %d signature%s\n", michael@0: numSignatures, michael@0: numSignatures != 1 ? "s" : ""); michael@0: } michael@0: if (hasAdditionalBlock) { michael@0: printf("%d additional block%s found:\n", michael@0: numAdditionalBlocks, michael@0: numAdditionalBlocks != 1 ? "s" : ""); michael@0: } michael@0: michael@0: rv = read_product_info_block(argv[2], &infoBlock); michael@0: if (!rv) { michael@0: printf(" - Product Information Block:\n"); michael@0: printf(" - MAR channel name: %s\n" michael@0: " - Product version: %s\n", michael@0: infoBlock.MARChannelID, michael@0: infoBlock.productVersion); michael@0: free((void *)infoBlock.MARChannelID); michael@0: free((void *)infoBlock.productVersion); michael@0: } michael@0: } michael@0: printf("\n"); michael@0: /* The fall through from 'T' to 't' is intentional */ michael@0: } michael@0: case 't': michael@0: return mar_test(argv[2]); michael@0: michael@0: /* Extract a MAR file */ michael@0: case 'x': michael@0: return mar_extract(argv[2]); michael@0: michael@0: #ifndef NO_SIGN_VERIFY michael@0: /* Extract a MAR signature */ michael@0: case 'X': michael@0: if (sigIndex == -1) { michael@0: fprintf(stderr, "ERROR: Signature index was not passed.\n"); michael@0: return -1; michael@0: } michael@0: if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { michael@0: fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", michael@0: sigIndex); michael@0: return -1; michael@0: } michael@0: return extract_signature(argv[2], sigIndex, argv[3]); michael@0: michael@0: /* Import a MAR signature */ michael@0: case 'I': michael@0: if (sigIndex == -1) { michael@0: fprintf(stderr, "ERROR: signature index was not passed.\n"); michael@0: return -1; michael@0: } michael@0: if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { michael@0: fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", michael@0: sigIndex); michael@0: return -1; michael@0: } michael@0: if (argc < 5) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: return import_signature(argv[2], sigIndex, argv[3], argv[4]); michael@0: michael@0: case 'v': michael@0: michael@0: #if defined(XP_WIN) && !defined(MAR_NSS) michael@0: if (certCount == 0) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: michael@0: for (k = 0; k < certCount; ++k) { michael@0: /* If the mar program was built using CryptoAPI, then read in the buffer michael@0: containing the cert from disk. */ michael@0: certFile = CreateFileA(DERFilePaths[k], GENERIC_READ, michael@0: FILE_SHARE_READ | michael@0: FILE_SHARE_WRITE | michael@0: FILE_SHARE_DELETE, michael@0: NULL, michael@0: OPEN_EXISTING, michael@0: 0, NULL); michael@0: if (INVALID_HANDLE_VALUE == certFile) { michael@0: return -1; michael@0: } michael@0: fileSizes[k] = GetFileSize(certFile, NULL); michael@0: certBuffers[k] = malloc(fileSizes[k]); michael@0: if (!ReadFile(certFile, certBuffers[k], fileSizes[k], &read, NULL) || michael@0: fileSizes[k] != read) { michael@0: CloseHandle(certFile); michael@0: for (i = 0; i <= k; i++) { michael@0: free(certBuffers[i]); michael@0: } michael@0: return -1; michael@0: } michael@0: CloseHandle(certFile); michael@0: } michael@0: michael@0: rv = mar_verify_signatures(argv[2], certBuffers, fileSizes, michael@0: NULL, certCount); michael@0: for (k = 0; k < certCount; ++k) { michael@0: free(certBuffers[k]); michael@0: } michael@0: if (rv) { michael@0: /* Determine if the source MAR file has the new fields for signing */ michael@0: int hasSignatureBlock; michael@0: if (get_mar_file_info(argv[2], &hasSignatureBlock, michael@0: NULL, NULL, NULL, NULL)) { michael@0: fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); michael@0: } else if (!hasSignatureBlock) { michael@0: fprintf(stderr, "ERROR: The MAR file is in the old format so has" michael@0: " no signature to verify.\n"); michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: michael@0: #elif defined(XP_MACOSX) michael@0: return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths, michael@0: 0, NULL, certCount); michael@0: #else michael@0: if (!NSSConfigDir || certCount == 0) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: michael@0: if (NSSInitCryptoContext(NSSConfigDir)) { michael@0: fprintf(stderr, "ERROR: Could not initialize crypto library.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: return mar_verify_signatures(argv[2], NULL, 0, certNames, certCount); michael@0: michael@0: #endif /* defined(XP_WIN) && !defined(MAR_NSS) */ michael@0: case 's': michael@0: if (!NSSConfigDir || certCount == 0 || argc < 4) { michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: return mar_repackage_and_sign(NSSConfigDir, certNames, certCount, michael@0: argv[2], argv[3]); michael@0: michael@0: case 'r': michael@0: return strip_signature_block(argv[2], argv[3]); michael@0: #endif /* endif NO_SIGN_VERIFY disabled */ michael@0: michael@0: default: michael@0: print_usage(); michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: }