tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mar.c (13718B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include "mar.h"
     11 #include "mar_cmdline.h"
     12 
     13 #ifdef XP_WIN
     14 #  include <windows.h>
     15 #  include <direct.h>
     16 #  define chdir _chdir
     17 #else
     18 #  include <unistd.h>
     19 #endif
     20 
     21 #if !defined(NO_SIGN_VERIFY) && defined(MAR_NSS)
     22 #  include "cert.h"
     23 #  include "nss.h"
     24 #  include "pk11pub.h"
     25 int NSSInitCryptoContext(const char* NSSConfigDir);
     26 #endif
     27 
     28 int mar_repackage_and_sign(const char* NSSConfigDir,
     29                           const char* const* certNames, uint32_t certCount,
     30                           const char* src, const char* dest);
     31 
     32 static void print_version() {
     33  printf("Version: %s\n", MOZ_APP_VERSION);
     34  printf("Default Channel ID: %s\n", MAR_CHANNEL_ID);
     35 }
     36 
     37 static void print_usage() {
     38  printf("usage:\n");
     39  printf("Create a MAR file:\n");
     40  printf(
     41      "  mar -H MARChannelID -V ProductVersion [-C workingDir] "
     42      "-c archive.mar [files...]\n");
     43 
     44  printf("Extract a MAR file:\n");
     45  printf("  mar [-C workingDir] -x archive.mar\n");
     46 #ifndef NO_SIGN_VERIFY
     47  printf("Sign a MAR file:\n");
     48  printf(
     49      "  mar [-C workingDir] -d NSSConfigDir -n certname -s "
     50      "archive.mar out_signed_archive.mar\n");
     51 
     52  printf("Strip a MAR signature:\n");
     53  printf(
     54      "  mar [-C workingDir] -r "
     55      "signed_input_archive.mar output_archive.mar\n");
     56 
     57  printf("Extract a MAR signature:\n");
     58  printf(
     59      "  mar [-C workingDir] -n(i) -X "
     60      "signed_input_archive.mar base_64_encoded_signature_file\n");
     61 
     62  printf("Import a MAR signature:\n");
     63  printf(
     64      "  mar [-C workingDir] -n(i) -I "
     65      "signed_input_archive.mar base_64_encoded_signature_file "
     66      "changed_signed_output.mar\n");
     67  printf("(i) is the index of the certificate to extract\n");
     68 #  if !defined(MAR_NSS)
     69  printf("Verify a MAR file:\n");
     70  printf("  mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
     71  printf(
     72      "At most %d signature certificate DER files are specified by "
     73      "-D0 DERFilePath1 -D1 DERFilePath2, ...\n",
     74      MAX_SIGNATURES);
     75 #  else
     76  printf("Verify a MAR file:\n");
     77  printf(
     78      "  mar [-C workingDir] -d NSSConfigDir -n certname "
     79      "-v signed_archive.mar\n");
     80  printf(
     81      "At most %d signature certificate names are specified by "
     82      "-n0 certName -n1 certName2, ...\n",
     83      MAX_SIGNATURES);
     84 #  endif
     85  printf(
     86      "At most %d verification certificate names are specified by "
     87      "-n0 certName -n1 certName2, ...\n",
     88      MAX_SIGNATURES);
     89 #endif
     90  printf("Print information on a MAR file:\n");
     91  printf("  mar -t archive.mar\n");
     92 
     93  printf("Print detailed information on a MAR file including signatures:\n");
     94  printf("  mar -T archive.mar\n");
     95 
     96  printf("Refresh the product information block of a MAR file:\n");
     97  printf(
     98      "  mar -H MARChannelID -V ProductVersion [-C workingDir] "
     99      "-i unsigned_archive_to_refresh.mar\n");
    100 
    101  printf("Print executable version:\n");
    102  printf("  mar --version\n");
    103  printf("This program does not handle unicode file paths properly\n");
    104 }
    105 
    106 static int mar_test_callback(MarFile* mar, const MarItem* item, void* unused) {
    107  printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
    108  return 0;
    109 }
    110 
    111 static int mar_test(const char* path) {
    112  MarFile* mar;
    113 
    114  MarReadResult result = mar_open(path, &mar);
    115  if (result != MAR_READ_SUCCESS) {
    116    return -1;
    117  }
    118 
    119  printf("SIZE\tMODE\tNAME\n");
    120  mar_enum_items(mar, mar_test_callback, NULL);
    121 
    122  mar_close(mar);
    123  return 0;
    124 }
    125 
    126 int main(int argc, char** argv) {
    127  const char* certNames[MAX_SIGNATURES];
    128  char* MARChannelID = NULL;
    129  char* productVersion = NULL;
    130  int rv = -1;
    131 #if !defined(NO_SIGN_VERIFY)
    132  char* NSSConfigDir = NULL;
    133  uint32_t k;
    134  uint32_t certCount = 0;
    135  int32_t sigIndex = -1;
    136  uint32_t fileSizes[MAX_SIGNATURES];
    137  const uint8_t* certBuffers[MAX_SIGNATURES];
    138 #  if !defined(MAR_NSS)
    139  char* DERFilePaths[MAX_SIGNATURES];
    140 #  else
    141  CERTCertificate* certs[MAX_SIGNATURES];
    142 #  endif
    143 #endif
    144 
    145  memset((void*)certNames, 0, sizeof(certNames));
    146 #if !defined(NO_SIGN_VERIFY)
    147  memset(fileSizes, 0, sizeof(fileSizes));
    148  memset((void*)certBuffers, 0, sizeof(certBuffers));
    149 #  if !defined(MAR_NSS)
    150  memset(DERFilePaths, 0, sizeof(DERFilePaths));
    151 #  else
    152  memset(certs, 0, sizeof(certs));
    153 #  endif
    154 #endif
    155 
    156  if (argc > 1 && 0 == strcmp(argv[1], "--version")) {
    157    print_version();
    158    return 0;
    159  }
    160 
    161  if (argc < 3) {
    162    print_usage();
    163    return -1;
    164  }
    165 
    166  while (argc > 0) {
    167    if (argv[1][0] == '-' &&
    168        (argv[1][1] == 'c' || argv[1][1] == 't' || argv[1][1] == 'x' ||
    169         argv[1][1] == 'v' || argv[1][1] == 's' || argv[1][1] == 'i' ||
    170         argv[1][1] == 'T' || argv[1][1] == 'r' || argv[1][1] == 'X' ||
    171         argv[1][1] == 'I')) {
    172      break;
    173      /* -C workingdirectory */
    174    }
    175    if (argv[1][0] == '-' && argv[1][1] == 'C') {
    176      if (chdir(argv[2]) != 0) {
    177        return -1;
    178      }
    179      argv += 2;
    180      argc -= 2;
    181    }
    182 #if !defined(NO_SIGN_VERIFY)
    183 #  if !defined(MAR_NSS)
    184    /* -D DERFilePath, also matches -D[index] DERFilePath
    185       We allow an index for verifying to be symmetric
    186       with the import and export command line arguments. */
    187    else if (argv[1][0] == '-' && argv[1][1] == 'D' &&
    188             (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) {
    189      if (certCount >= MAX_SIGNATURES) {
    190        print_usage();
    191        return -1;
    192      }
    193      DERFilePaths[certCount++] = argv[2];
    194      argv += 2;
    195      argc -= 2;
    196    }
    197 #  endif
    198    /* -d NSSConfigdir */
    199    else if (argv[1][0] == '-' && argv[1][1] == 'd') {
    200      NSSConfigDir = argv[2];
    201      argv += 2;
    202      argc -= 2;
    203      /* -n certName, also matches -n[index] certName
    204         We allow an index for verifying to be symmetric
    205         with the import and export command line arguments. */
    206    } else if (argv[1][0] == '-' && argv[1][1] == 'n' &&
    207               (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0' ||
    208                !strcmp(argv[2], "-X") || !strcmp(argv[2], "-I"))) {
    209      if (certCount >= MAX_SIGNATURES) {
    210        print_usage();
    211        return -1;
    212      }
    213      certNames[certCount++] = argv[2];
    214      if (strlen(argv[1]) > 2 &&
    215          (!strcmp(argv[2], "-X") || !strcmp(argv[2], "-I")) &&
    216          argv[1][2] >= '0' && argv[1][2] <= '9') {
    217        sigIndex = argv[1][2] - '0';
    218        argv++;
    219        argc--;
    220      } else {
    221        argv += 2;
    222        argc -= 2;
    223      }
    224    }
    225 #endif
    226    else if (argv[1][0] == '-' && argv[1][1] == 'H') {  // MAR channel ID
    227      MARChannelID = argv[2];
    228      argv += 2;
    229      argc -= 2;
    230    } else if (argv[1][0] == '-' && argv[1][1] == 'V') {  // Product Version
    231      productVersion = argv[2];
    232      argv += 2;
    233      argc -= 2;
    234    } else {
    235      print_usage();
    236      return -1;
    237    }
    238  }
    239 
    240  if (argv[1][0] != '-') {
    241    print_usage();
    242    return -1;
    243  }
    244 
    245  switch (argv[1][1]) {
    246    case 'c': {
    247      struct ProductInformationBlock infoBlock;
    248      if (!productVersion) {
    249        fprintf(stderr,
    250                "ERROR: Version not specified (pass `-V <version>`).\n");
    251        return -1;
    252      }
    253      if (!MARChannelID) {
    254        fprintf(stderr,
    255                "ERROR: MAR channel ID not specified (pass `-H "
    256                "<mar-channel-id>`).\n");
    257        return -1;
    258      }
    259      infoBlock.MARChannelID = MARChannelID;
    260      infoBlock.productVersion = productVersion;
    261      return mar_create(argv[2], argc - 3, argv + 3, &infoBlock);
    262    }
    263    case 'i': {
    264      if (!productVersion) {
    265        fprintf(stderr,
    266                "ERROR: Version not specified (pass `-V <version>`).\n");
    267        return -1;
    268      }
    269      if (!MARChannelID) {
    270        fprintf(stderr,
    271                "ERROR: MAR channel ID not specified (pass `-H "
    272                "<mar-channel-id>`).\n");
    273        return -1;
    274      }
    275      struct ProductInformationBlock infoBlock;
    276      infoBlock.MARChannelID = MARChannelID;
    277      infoBlock.productVersion = productVersion;
    278      return refresh_product_info_block(argv[2], &infoBlock);
    279    }
    280    case 'T': {
    281      struct ProductInformationBlock infoBlock;
    282      uint32_t numSignatures, numAdditionalBlocks;
    283      int hasSignatureBlock, hasAdditionalBlock;
    284      if (!get_mar_file_info(argv[2], &hasSignatureBlock, &numSignatures,
    285                             &hasAdditionalBlock, NULL, &numAdditionalBlocks)) {
    286        if (hasSignatureBlock) {
    287          printf("Signature block found with %d signature%s\n", numSignatures,
    288                 numSignatures != 1 ? "s" : "");
    289        }
    290        if (hasAdditionalBlock) {
    291          printf("%d additional block%s found:\n", numAdditionalBlocks,
    292                 numAdditionalBlocks != 1 ? "s" : "");
    293        }
    294 
    295        rv = read_product_info_block(argv[2], &infoBlock);
    296        if (!rv) {
    297          printf("  - Product Information Block:\n");
    298          printf(
    299              "    - MAR channel name: %s\n"
    300              "    - Product version: %s\n",
    301              infoBlock.MARChannelID, infoBlock.productVersion);
    302          free((void*)infoBlock.MARChannelID);
    303          free((void*)infoBlock.productVersion);
    304        }
    305      }
    306      printf("\n");
    307      /* The fall through from 'T' to 't' is intentional */
    308    }
    309    case 't':
    310      return mar_test(argv[2]);
    311 
    312    case 'x':  // Extract a MAR file
    313      return mar_extract(argv[2]);
    314 
    315 #ifndef NO_SIGN_VERIFY
    316    case 'X':  // Extract a MAR signature
    317      if (sigIndex == -1) {
    318        fprintf(stderr, "ERROR: Signature index was not passed.\n");
    319        return -1;
    320      }
    321      if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) {
    322        fprintf(stderr, "ERROR: Signature index is out of range: %d.\n",
    323                sigIndex);
    324        return -1;
    325      }
    326      return extract_signature(argv[2], sigIndex, argv[3]);
    327 
    328    case 'I':  // Import a MAR signature
    329      if (sigIndex == -1) {
    330        fprintf(stderr, "ERROR: signature index was not passed.\n");
    331        return -1;
    332      }
    333      if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) {
    334        fprintf(stderr, "ERROR: Signature index is out of range: %d.\n",
    335                sigIndex);
    336        return -1;
    337      }
    338      if (argc < 5) {
    339        print_usage();
    340        return -1;
    341      }
    342      return import_signature(argv[2], sigIndex, argv[3], argv[4]);
    343 
    344    case 'v':
    345      if (certCount == 0) {
    346        print_usage();
    347        return -1;
    348      }
    349 
    350 #  if defined(MAR_NSS)
    351      if (!NSSConfigDir || certCount == 0) {
    352        print_usage();
    353        return -1;
    354      }
    355 
    356      if (NSSInitCryptoContext(NSSConfigDir)) {
    357        fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
    358        return -1;
    359      }
    360 #  endif
    361 
    362      rv = 0;
    363      for (k = 0; k < certCount; ++k) {
    364 #  if !defined(MAR_NSS)
    365        rv = mar_read_entire_file(DERFilePaths[k], MAR_MAX_CERT_SIZE,
    366                                  &certBuffers[k], &fileSizes[k]);
    367 
    368        if (rv) {
    369          fprintf(stderr, "ERROR: could not read file %s", DERFilePaths[k]);
    370          break;
    371        }
    372 #  else
    373        /* It is somewhat circuitous to look up a CERTCertificate and then pass
    374         * in its DER encoding just so we can later re-create that
    375         * CERTCertificate to extract the public key out of it. However, by
    376         * doing things this way, we maximize the reuse of the
    377         * mar_verify_signatures function and also we keep the control flow as
    378         * similar as possible between programs and operating systems, at least
    379         * for the functions that are critically important to security.
    380         */
    381        certs[k] = PK11_FindCertFromNickname(certNames[k], NULL);
    382        if (certs[k]) {
    383          certBuffers[k] = certs[k]->derCert.data;
    384          fileSizes[k] = certs[k]->derCert.len;
    385        } else {
    386          rv = -1;
    387          fprintf(stderr, "ERROR: could not find cert from nickname %s",
    388                  certNames[k]);
    389          break;
    390        }
    391 #  endif
    392      }
    393 
    394      if (!rv) {
    395        MarFile* mar;
    396        MarReadResult result = mar_open(argv[2], &mar);
    397        if (result == MAR_READ_SUCCESS) {
    398          rv = mar_verify_signatures(mar, certBuffers, fileSizes, certCount);
    399          mar_close(mar);
    400        } else {
    401          fprintf(stderr, "ERROR: Could not open MAR file.\n");
    402          rv = -1;
    403        }
    404      }
    405      for (k = 0; k < certCount; ++k) {
    406 #  if !defined(MAR_NSS)
    407        free((void*)certBuffers[k]);
    408 #  else
    409        /* certBuffers[k] is owned by certs[k] so don't free it */
    410        CERT_DestroyCertificate(certs[k]);
    411 #  endif
    412      }
    413      if (rv) {
    414        /* Determine if the source MAR file has the new fields for signing */
    415        int hasSignatureBlock;
    416        if (get_mar_file_info(argv[2], &hasSignatureBlock, NULL, NULL, NULL,
    417                              NULL)) {
    418          fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
    419        } else if (!hasSignatureBlock) {
    420          fprintf(stderr,
    421                  "ERROR: The MAR file is in the old format so has"
    422                  " no signature to verify.\n");
    423        }
    424      }
    425 #  if defined(MAR_NSS)
    426      (void)NSS_Shutdown();
    427 #  endif
    428      return rv ? -1 : 0;
    429 
    430    case 's':
    431      if (!NSSConfigDir || certCount == 0 || argc < 4) {
    432        print_usage();
    433        return -1;
    434      }
    435      return mar_repackage_and_sign(NSSConfigDir, certNames, certCount, argv[2],
    436                                    argv[3]);
    437 
    438    case 'r':
    439      return strip_signature_block(argv[2], argv[3]);
    440 #endif /* endif NO_SIGN_VERIFY disabled */
    441 
    442    default:
    443      print_usage();
    444      return -1;
    445  }
    446 }