1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libmar/tool/mar.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,410 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <stdio.h> 1.11 +#include <stdlib.h> 1.12 +#include <string.h> 1.13 +#include "mar.h" 1.14 +#include "mar_cmdline.h" 1.15 + 1.16 +#ifdef XP_WIN 1.17 +#include <windows.h> 1.18 +#include <direct.h> 1.19 +#define chdir _chdir 1.20 +#else 1.21 +#include <unistd.h> 1.22 +#endif 1.23 + 1.24 +#if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS)) 1.25 +int NSSInitCryptoContext(const char *NSSConfigDir); 1.26 +#endif 1.27 + 1.28 +int mar_repackage_and_sign(const char *NSSConfigDir, 1.29 + const char * const *certNames, 1.30 + uint32_t certCount, 1.31 + const char *src, 1.32 + const char * dest); 1.33 + 1.34 +static void print_version() { 1.35 + printf("Version: %s\n", MOZ_APP_VERSION); 1.36 + printf("Default Channel ID: %s\n", MAR_CHANNEL_ID); 1.37 +} 1.38 + 1.39 +static void print_usage() { 1.40 + printf("usage:\n"); 1.41 + printf("Create a MAR file:\n"); 1.42 + printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " 1.43 + "-c archive.mar [files...]\n"); 1.44 + 1.45 + printf("Extract a MAR file:\n"); 1.46 + printf(" mar [-C workingDir] -x archive.mar\n"); 1.47 +#ifndef NO_SIGN_VERIFY 1.48 + printf("Sign a MAR file:\n"); 1.49 + printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s " 1.50 + "archive.mar out_signed_archive.mar\n"); 1.51 + 1.52 + printf("Strip a MAR signature:\n"); 1.53 + printf(" mar [-C workingDir] -r " 1.54 + "signed_input_archive.mar output_archive.mar\n"); 1.55 + 1.56 + printf("Extract a MAR signature:\n"); 1.57 + printf(" mar [-C workingDir] -n(i) -X " 1.58 + "signed_input_archive.mar base_64_encoded_signature_file\n"); 1.59 + 1.60 + printf("Import a MAR signature:\n"); 1.61 + printf(" mar [-C workingDir] -n(i) -I " 1.62 + "signed_input_archive.mar base_64_encoded_signature_file " 1.63 + "changed_signed_output.mar\n"); 1.64 + printf("(i) is the index of the certificate to extract\n"); 1.65 +#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(MAR_NSS)) 1.66 + printf("Verify a MAR file:\n"); 1.67 + printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); 1.68 + printf("At most %d signature certificate DER files are specified by " 1.69 + "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); 1.70 +#else 1.71 + printf("Verify a MAR file:\n"); 1.72 + printf(" mar [-C workingDir] -d NSSConfigDir -n certname " 1.73 + "-v signed_archive.mar\n"); 1.74 + printf("At most %d signature certificate names are specified by " 1.75 + "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); 1.76 +#endif 1.77 + printf("At most %d verification certificate names are specified by " 1.78 + "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); 1.79 +#endif 1.80 + printf("Print information on a MAR file:\n"); 1.81 + printf(" mar -t archive.mar\n"); 1.82 + 1.83 + printf("Print detailed information on a MAR file including signatures:\n"); 1.84 + printf(" mar -T archive.mar\n"); 1.85 + 1.86 + printf("Refresh the product information block of a MAR file:\n"); 1.87 + printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " 1.88 + "-i unsigned_archive_to_refresh.mar\n"); 1.89 + 1.90 + printf("Print executable version:\n"); 1.91 + printf(" mar --version\n"); 1.92 + printf("This program does not handle unicode file paths properly\n"); 1.93 +} 1.94 + 1.95 +static int mar_test_callback(MarFile *mar, 1.96 + const MarItem *item, 1.97 + void *unused) { 1.98 + printf("%u\t0%o\t%s\n", item->length, item->flags, item->name); 1.99 + return 0; 1.100 +} 1.101 + 1.102 +static int mar_test(const char *path) { 1.103 + MarFile *mar; 1.104 + 1.105 + mar = mar_open(path); 1.106 + if (!mar) 1.107 + return -1; 1.108 + 1.109 + printf("SIZE\tMODE\tNAME\n"); 1.110 + mar_enum_items(mar, mar_test_callback, NULL); 1.111 + 1.112 + mar_close(mar); 1.113 + return 0; 1.114 +} 1.115 + 1.116 +int main(int argc, char **argv) { 1.117 + char *NSSConfigDir = NULL; 1.118 + const char *certNames[MAX_SIGNATURES]; 1.119 + char *MARChannelID = MAR_CHANNEL_ID; 1.120 + char *productVersion = MOZ_APP_VERSION; 1.121 + uint32_t i, k; 1.122 + int rv = -1; 1.123 + uint32_t certCount = 0; 1.124 + int32_t sigIndex = -1; 1.125 + 1.126 +#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) 1.127 + HANDLE certFile; 1.128 + uint8_t *certBuffers[MAX_SIGNATURES]; 1.129 +#endif 1.130 +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ 1.131 + defined(XP_MACOSX)) 1.132 + char* DERFilePaths[MAX_SIGNATURES]; 1.133 + uint32_t fileSizes[MAX_SIGNATURES]; 1.134 + uint32_t read; 1.135 +#endif 1.136 + 1.137 + memset(certNames, 0, sizeof(certNames)); 1.138 +#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) 1.139 + memset(certBuffers, 0, sizeof(certBuffers)); 1.140 +#endif 1.141 +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ 1.142 + defined(XP_MACOSX)) 1.143 + memset(DERFilePaths, 0, sizeof(DERFilePaths)); 1.144 + memset(fileSizes, 0, sizeof(fileSizes)); 1.145 +#endif 1.146 + 1.147 + if (argc > 1 && 0 == strcmp(argv[1], "--version")) { 1.148 + print_version(); 1.149 + return 0; 1.150 + } 1.151 + 1.152 + if (argc < 3) { 1.153 + print_usage(); 1.154 + return -1; 1.155 + } 1.156 + 1.157 + while (argc > 0) { 1.158 + if (argv[1][0] == '-' && (argv[1][1] == 'c' || 1.159 + argv[1][1] == 't' || argv[1][1] == 'x' || 1.160 + argv[1][1] == 'v' || argv[1][1] == 's' || 1.161 + argv[1][1] == 'i' || argv[1][1] == 'T' || 1.162 + argv[1][1] == 'r' || argv[1][1] == 'X' || 1.163 + argv[1][1] == 'I')) { 1.164 + break; 1.165 + /* -C workingdirectory */ 1.166 + } else if (argv[1][0] == '-' && argv[1][1] == 'C') { 1.167 + chdir(argv[2]); 1.168 + argv += 2; 1.169 + argc -= 2; 1.170 + } 1.171 +#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ 1.172 + defined(XP_MACOSX)) 1.173 + /* -D DERFilePath, also matches -D[index] DERFilePath 1.174 + We allow an index for verifying to be symmetric 1.175 + with the import and export command line arguments. */ 1.176 + else if (argv[1][0] == '-' && 1.177 + argv[1][1] == 'D' && 1.178 + (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) { 1.179 + if (certCount >= MAX_SIGNATURES) { 1.180 + print_usage(); 1.181 + return -1; 1.182 + } 1.183 + DERFilePaths[certCount++] = argv[2]; 1.184 + argv += 2; 1.185 + argc -= 2; 1.186 + } 1.187 +#endif 1.188 + /* -d NSSConfigdir */ 1.189 + else if (argv[1][0] == '-' && argv[1][1] == 'd') { 1.190 + NSSConfigDir = argv[2]; 1.191 + argv += 2; 1.192 + argc -= 2; 1.193 + /* -n certName, also matches -n[index] certName 1.194 + We allow an index for verifying to be symmetric 1.195 + with the import and export command line arguments. */ 1.196 + } else if (argv[1][0] == '-' && 1.197 + argv[1][1] == 'n' && 1.198 + (argv[1][2] == (char)('0' + certCount) || 1.199 + argv[1][2] == '\0' || 1.200 + !strcmp(argv[2], "-X") || 1.201 + !strcmp(argv[2], "-I"))) { 1.202 + if (certCount >= MAX_SIGNATURES) { 1.203 + print_usage(); 1.204 + return -1; 1.205 + } 1.206 + certNames[certCount++] = argv[2]; 1.207 + if (strlen(argv[1]) > 2 && 1.208 + (!strcmp(argv[2], "-X") || !strcmp(argv[2], "-I")) && 1.209 + argv[1][2] >= '0' && argv[1][2] <= '9') { 1.210 + sigIndex = argv[1][2] - '0'; 1.211 + argv++; 1.212 + argc--; 1.213 + } else { 1.214 + argv += 2; 1.215 + argc -= 2; 1.216 + } 1.217 + /* MAR channel ID */ 1.218 + } else if (argv[1][0] == '-' && argv[1][1] == 'H') { 1.219 + MARChannelID = argv[2]; 1.220 + argv += 2; 1.221 + argc -= 2; 1.222 + /* Product Version */ 1.223 + } else if (argv[1][0] == '-' && argv[1][1] == 'V') { 1.224 + productVersion = argv[2]; 1.225 + argv += 2; 1.226 + argc -= 2; 1.227 + } 1.228 + else { 1.229 + print_usage(); 1.230 + return -1; 1.231 + } 1.232 + } 1.233 + 1.234 + if (argv[1][0] != '-') { 1.235 + print_usage(); 1.236 + return -1; 1.237 + } 1.238 + 1.239 + switch (argv[1][1]) { 1.240 + case 'c': { 1.241 + struct ProductInformationBlock infoBlock; 1.242 + infoBlock.MARChannelID = MARChannelID; 1.243 + infoBlock.productVersion = productVersion; 1.244 + return mar_create(argv[2], argc - 3, argv + 3, &infoBlock); 1.245 + } 1.246 + case 'i': { 1.247 + struct ProductInformationBlock infoBlock; 1.248 + infoBlock.MARChannelID = MARChannelID; 1.249 + infoBlock.productVersion = productVersion; 1.250 + return refresh_product_info_block(argv[2], &infoBlock); 1.251 + } 1.252 + case 'T': { 1.253 + struct ProductInformationBlock infoBlock; 1.254 + uint32_t numSignatures, numAdditionalBlocks; 1.255 + int hasSignatureBlock, hasAdditionalBlock; 1.256 + if (!get_mar_file_info(argv[2], 1.257 + &hasSignatureBlock, 1.258 + &numSignatures, 1.259 + &hasAdditionalBlock, 1.260 + NULL, &numAdditionalBlocks)) { 1.261 + if (hasSignatureBlock) { 1.262 + printf("Signature block found with %d signature%s\n", 1.263 + numSignatures, 1.264 + numSignatures != 1 ? "s" : ""); 1.265 + } 1.266 + if (hasAdditionalBlock) { 1.267 + printf("%d additional block%s found:\n", 1.268 + numAdditionalBlocks, 1.269 + numAdditionalBlocks != 1 ? "s" : ""); 1.270 + } 1.271 + 1.272 + rv = read_product_info_block(argv[2], &infoBlock); 1.273 + if (!rv) { 1.274 + printf(" - Product Information Block:\n"); 1.275 + printf(" - MAR channel name: %s\n" 1.276 + " - Product version: %s\n", 1.277 + infoBlock.MARChannelID, 1.278 + infoBlock.productVersion); 1.279 + free((void *)infoBlock.MARChannelID); 1.280 + free((void *)infoBlock.productVersion); 1.281 + } 1.282 + } 1.283 + printf("\n"); 1.284 + /* The fall through from 'T' to 't' is intentional */ 1.285 + } 1.286 + case 't': 1.287 + return mar_test(argv[2]); 1.288 + 1.289 + /* Extract a MAR file */ 1.290 + case 'x': 1.291 + return mar_extract(argv[2]); 1.292 + 1.293 +#ifndef NO_SIGN_VERIFY 1.294 + /* Extract a MAR signature */ 1.295 + case 'X': 1.296 + if (sigIndex == -1) { 1.297 + fprintf(stderr, "ERROR: Signature index was not passed.\n"); 1.298 + return -1; 1.299 + } 1.300 + if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { 1.301 + fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", 1.302 + sigIndex); 1.303 + return -1; 1.304 + } 1.305 + return extract_signature(argv[2], sigIndex, argv[3]); 1.306 + 1.307 + /* Import a MAR signature */ 1.308 + case 'I': 1.309 + if (sigIndex == -1) { 1.310 + fprintf(stderr, "ERROR: signature index was not passed.\n"); 1.311 + return -1; 1.312 + } 1.313 + if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { 1.314 + fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", 1.315 + sigIndex); 1.316 + return -1; 1.317 + } 1.318 + if (argc < 5) { 1.319 + print_usage(); 1.320 + return -1; 1.321 + } 1.322 + return import_signature(argv[2], sigIndex, argv[3], argv[4]); 1.323 + 1.324 + case 'v': 1.325 + 1.326 +#if defined(XP_WIN) && !defined(MAR_NSS) 1.327 + if (certCount == 0) { 1.328 + print_usage(); 1.329 + return -1; 1.330 + } 1.331 + 1.332 + for (k = 0; k < certCount; ++k) { 1.333 + /* If the mar program was built using CryptoAPI, then read in the buffer 1.334 + containing the cert from disk. */ 1.335 + certFile = CreateFileA(DERFilePaths[k], GENERIC_READ, 1.336 + FILE_SHARE_READ | 1.337 + FILE_SHARE_WRITE | 1.338 + FILE_SHARE_DELETE, 1.339 + NULL, 1.340 + OPEN_EXISTING, 1.341 + 0, NULL); 1.342 + if (INVALID_HANDLE_VALUE == certFile) { 1.343 + return -1; 1.344 + } 1.345 + fileSizes[k] = GetFileSize(certFile, NULL); 1.346 + certBuffers[k] = malloc(fileSizes[k]); 1.347 + if (!ReadFile(certFile, certBuffers[k], fileSizes[k], &read, NULL) || 1.348 + fileSizes[k] != read) { 1.349 + CloseHandle(certFile); 1.350 + for (i = 0; i <= k; i++) { 1.351 + free(certBuffers[i]); 1.352 + } 1.353 + return -1; 1.354 + } 1.355 + CloseHandle(certFile); 1.356 + } 1.357 + 1.358 + rv = mar_verify_signatures(argv[2], certBuffers, fileSizes, 1.359 + NULL, certCount); 1.360 + for (k = 0; k < certCount; ++k) { 1.361 + free(certBuffers[k]); 1.362 + } 1.363 + if (rv) { 1.364 + /* Determine if the source MAR file has the new fields for signing */ 1.365 + int hasSignatureBlock; 1.366 + if (get_mar_file_info(argv[2], &hasSignatureBlock, 1.367 + NULL, NULL, NULL, NULL)) { 1.368 + fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); 1.369 + } else if (!hasSignatureBlock) { 1.370 + fprintf(stderr, "ERROR: The MAR file is in the old format so has" 1.371 + " no signature to verify.\n"); 1.372 + } 1.373 + return -1; 1.374 + } 1.375 + 1.376 + return 0; 1.377 + 1.378 +#elif defined(XP_MACOSX) 1.379 + return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths, 1.380 + 0, NULL, certCount); 1.381 +#else 1.382 + if (!NSSConfigDir || certCount == 0) { 1.383 + print_usage(); 1.384 + return -1; 1.385 + } 1.386 + 1.387 + if (NSSInitCryptoContext(NSSConfigDir)) { 1.388 + fprintf(stderr, "ERROR: Could not initialize crypto library.\n"); 1.389 + return -1; 1.390 + } 1.391 + 1.392 + return mar_verify_signatures(argv[2], NULL, 0, certNames, certCount); 1.393 + 1.394 +#endif /* defined(XP_WIN) && !defined(MAR_NSS) */ 1.395 + case 's': 1.396 + if (!NSSConfigDir || certCount == 0 || argc < 4) { 1.397 + print_usage(); 1.398 + return -1; 1.399 + } 1.400 + return mar_repackage_and_sign(NSSConfigDir, certNames, certCount, 1.401 + argv[2], argv[3]); 1.402 + 1.403 + case 'r': 1.404 + return strip_signature_block(argv[2], argv[3]); 1.405 +#endif /* endif NO_SIGN_VERIFY disabled */ 1.406 + 1.407 + default: 1.408 + print_usage(); 1.409 + return -1; 1.410 + } 1.411 + 1.412 + return 0; 1.413 +}