modules/libmar/tool/mar.c

changeset 0
6474c204b198
     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 +}

mercurial