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/. */
10 #include <onlineupdate/mar.h>
11 #include <onlineupdate/mar_cmdline.h>
23 #error "Missing APP_VERSION"
26 #define MAR_CHANNEL_ID "LOOnlineUpdater" /* Dummy value; replace or
27 remove in the future */
29 #if !defined(NO_SIGN_VERIFY) && (!defined(_WIN32) || defined(MAR_NSS))
32 int NSSInitCryptoContext(const char *NSSConfigDir
);
35 int mar_repackage_and_sign(const char *NSSConfigDir
,
36 const char * const *certNames
,
41 static void print_version(void) {
42 printf("Version: %s\n", APP_VERSION
);
43 printf("Default Channel ID: %s\n", MAR_CHANNEL_ID
);
46 static void print_usage(void) {
48 printf("Create a MAR file:\n");
49 printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
50 "-c archive.mar [files...]\n");
51 printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
52 "-c archive.mar -f input_file.txt\n");
54 printf("Extract a MAR file:\n");
55 printf(" mar [-C workingDir] -x archive.mar\n");
56 #ifndef NO_SIGN_VERIFY
57 printf("Sign a MAR file:\n");
58 printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s "
59 "archive.mar out_signed_archive.mar\n");
61 printf("Strip a MAR signature:\n");
62 printf(" mar [-C workingDir] -r "
63 "signed_input_archive.mar output_archive.mar\n");
65 printf("Extract a MAR signature:\n");
66 printf(" mar [-C workingDir] -n(i) -X "
67 "signed_input_archive.mar base_64_encoded_signature_file\n");
69 printf("Import a MAR signature:\n");
70 printf(" mar [-C workingDir] -n(i) -I "
71 "signed_input_archive.mar base_64_encoded_signature_file "
72 "changed_signed_output.mar\n");
73 printf("(i) is the index of the certificate to extract\n");
74 #if defined(MACOSX) || (defined(_WIN32) && !defined(MAR_NSS))
75 printf("Verify a MAR file:\n");
76 printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
77 printf("At most %d signature certificate DER files are specified by "
78 "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES
);
80 printf("Verify a MAR file:\n");
81 printf(" mar [-C workingDir] -d NSSConfigDir -n certname "
82 "-v signed_archive.mar\n");
83 printf("At most %d signature certificate names are specified by "
84 "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES
);
86 printf("At most %d verification certificate names are specified by "
87 "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES
);
89 printf("Print information on a MAR file:\n");
90 printf(" mar -t archive.mar\n");
92 printf("Print detailed information on a MAR file including signatures:\n");
93 printf(" mar -T archive.mar\n");
95 printf("Refresh the product information block of a MAR file:\n");
96 printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
97 "-i unsigned_archive_to_refresh.mar\n");
99 printf("Print executable version:\n");
100 printf(" mar --version\n");
101 printf("This program does not handle unicode file paths properly\n");
104 static int mar_test_callback(MarFile
*mar
,
107 (void) mar
; (void) unused
; // avoid warnings
109 printf("%u\t0%o\t%s\n", item
->length
, item
->flags
, item
->name
);
113 static int mar_test(const char *path
) {
116 mar
= mar_open(path
);
120 printf("SIZE\tMODE\tNAME\n");
121 mar_enum_items(mar
, mar_test_callback
, NULL
);
127 int main(int argc
, char **argv
) {
128 char *NSSConfigDir
= NULL
;
129 const char *certNames
[MAX_SIGNATURES
];
130 char *MARChannelID
= MAR_CHANNEL_ID
;
131 char *productVersion
= APP_VERSION
;
132 #ifndef NO_SIGN_VERIFY
136 uint32_t certCount
= 0;
137 int32_t sigIndex
= -1;
139 #if !defined(NO_SIGN_VERIFY)
140 uint32_t fileSizes
[MAX_SIGNATURES
];
141 const uint8_t* certBuffers
[MAX_SIGNATURES
];
142 char* DERFilePaths
[MAX_SIGNATURES
];
143 #if (!defined(_WIN32) && !defined(MACOSX)) || defined(MAR_NSS)
144 CERTCertificate
* certs
[MAX_SIGNATURES
];
148 memset((void*)certNames
, 0, sizeof(certNames
));
149 #if defined(_WIN32) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
150 memset((void*)certBuffers
, 0, sizeof(certBuffers
));
152 #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(_WIN32)) || \
154 memset(DERFilePaths
, 0, sizeof(DERFilePaths
));
155 memset(fileSizes
, 0, sizeof(fileSizes
));
158 if (argc
> 1 && 0 == strcmp(argv
[1], "--version")) {
169 if (argv
[1][0] == '-' && (argv
[1][1] == 'c' ||
170 argv
[1][1] == 't' || argv
[1][1] == 'x' ||
171 argv
[1][1] == 'v' || argv
[1][1] == 's' ||
172 argv
[1][1] == 'i' || argv
[1][1] == 'T' ||
173 argv
[1][1] == 'r' || argv
[1][1] == 'X' ||
174 argv
[1][1] == 'I')) {
176 /* -C workingdirectory */
177 } else if (argv
[1][0] == '-' && argv
[1][1] == 'C') {
182 #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(_WIN32)) || \
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] == '-' &&
189 (argv
[1][2] == (char)('0' + certCount
) || argv
[1][2] == '\0')) {
190 if (certCount
>= MAX_SIGNATURES
) {
194 DERFilePaths
[certCount
++] = argv
[2];
199 /* -d NSSConfigdir */
200 else if (argv
[1][0] == '-' && argv
[1][1] == 'd') {
201 NSSConfigDir
= argv
[2];
204 /* -n certName, also matches -n[index] certName
205 We allow an index for verifying to be symmetric
206 with the import and export command line arguments. */
207 } else if (argv
[1][0] == '-' &&
209 (argv
[1][2] == (char)('0' + certCount
) ||
210 argv
[1][2] == '\0' ||
211 !strcmp(argv
[2], "-X") ||
212 !strcmp(argv
[2], "-I"))) {
213 if (certCount
>= MAX_SIGNATURES
) {
217 certNames
[certCount
++] = argv
[2];
218 if (strlen(argv
[1]) > 2 &&
219 (!strcmp(argv
[2], "-X") || !strcmp(argv
[2], "-I")) &&
220 argv
[1][2] >= '0' && argv
[1][2] <= '9') {
221 sigIndex
= argv
[1][2] - '0';
229 } else if (argv
[1][0] == '-' && argv
[1][1] == 'H') {
230 MARChannelID
= argv
[2];
233 /* Product Version */
234 } else if (argv
[1][0] == '-' && argv
[1][1] == 'V') {
235 productVersion
= argv
[2];
245 if (argv
[1][0] != '-') {
250 switch (argv
[1][1]) {
252 struct ProductInformationBlock infoBlock
;
253 infoBlock
.MARChannelID
= MARChannelID
;
254 infoBlock
.productVersion
= productVersion
;
255 if (argv
[argc
- 2][0] == '-' && argv
[argc
- 2][1] == 'f')
262 files
= (char **)malloc(sizeof(char*)*10000);
264 file
= fopen(argv
[argc
- 1], "r");
267 printf("%d %s", errno
, strerror(errno
));
268 printf("Could not open file: %s", argv
[argc
- 1]);
272 while(fgets(buf
, 1000, file
) != NULL
)
276 for (j
=strlen(buf
)-1;j
>=0 && (buf
[j
]=='\n' || buf
[j
]=='\r');j
--)
279 str_len
= strlen(buf
) + 1;
280 files
[num_files
] = (char*)malloc(sizeof(char)*str_len
);
281 strcpy(files
[num_files
], buf
);
285 return mar_create(argv
[2], num_files
, files
, &infoBlock
);
288 return mar_create(argv
[2], argc
- 3, argv
+ 3, &infoBlock
);
291 struct ProductInformationBlock infoBlock
;
292 infoBlock
.MARChannelID
= MARChannelID
;
293 infoBlock
.productVersion
= productVersion
;
294 return refresh_product_info_block(argv
[2], &infoBlock
);
297 struct ProductInformationBlock infoBlock
;
298 uint32_t numSignatures
, numAdditionalBlocks
;
299 int hasSignatureBlock
, hasAdditionalBlock
;
300 if (!get_mar_file_info(argv
[2],
304 NULL
, &numAdditionalBlocks
)) {
305 if (hasSignatureBlock
) {
306 printf("Signature block found with %d signature%s\n",
308 numSignatures
!= 1 ? "s" : "");
310 if (hasAdditionalBlock
) {
311 printf("%d additional block%s found:\n",
313 numAdditionalBlocks
!= 1 ? "s" : "");
316 rv
= read_product_info_block(argv
[2], &infoBlock
);
318 printf(" - Product Information Block:\n");
319 printf(" - MAR channel name: %s\n"
320 " - Product version: %s\n",
321 infoBlock
.MARChannelID
,
322 infoBlock
.productVersion
);
323 free((void *)infoBlock
.MARChannelID
);
324 free((void *)infoBlock
.productVersion
);
328 /* The fall through from 'T' to 't' is intentional */
332 return mar_test(argv
[2]);
334 /* Extract a MAR file */
336 return mar_extract(argv
[2]);
338 #ifndef NO_SIGN_VERIFY
339 /* Extract a MAR signature */
341 if (sigIndex
== -1) {
342 fprintf(stderr
, "ERROR: Signature index was not passed.\n");
345 if (sigIndex
>= MAX_SIGNATURES
|| sigIndex
< -1) {
346 fprintf(stderr
, "ERROR: Signature index is out of range: %d.\n",
350 return extract_signature(argv
[2], sigIndex
, argv
[3]);
352 /* Import a MAR signature */
354 if (sigIndex
== -1) {
355 fprintf(stderr
, "ERROR: signature index was not passed.\n");
358 if (sigIndex
>= MAX_SIGNATURES
|| sigIndex
< -1) {
359 fprintf(stderr
, "ERROR: Signature index is out of range: %d.\n",
367 return import_signature(argv
[2], sigIndex
, argv
[3], argv
[4]);
370 if (certCount
== 0) {
375 #if (!defined(_WIN32) && !defined(MACOSX)) || defined(MAR_NSS)
376 if (!NSSConfigDir
|| certCount
== 0) {
381 if (NSSInitCryptoContext(NSSConfigDir
)) {
382 fprintf(stderr
, "ERROR: Could not initialize crypto library.\n");
388 for (k
= 0; k
< certCount
; ++k
) {
389 #if (defined(_WIN32) || defined(MACOSX)) && !defined(MAR_NSS)
390 rv
= mar_read_entire_file(DERFilePaths
[k
], MAR_MAX_CERT_SIZE
,
391 &certBuffers
[k
], &fileSizes
[k
]);
393 /* It is somewhat circuitous to look up a CERTCertificate and then pass
394 * in its DER encoding just so we can later re-create that
395 * CERTCertificate to extract the public key out of it. However, by doing
396 * things this way, we maximize the reuse of the mar_verify_signatures
397 * function and also we keep the control flow as similar as possible
398 * between programs and operating systems, at least for the functions
399 * that are critically important to security.
401 certs
[k
] = PK11_FindCertFromNickname(certNames
[k
], NULL
);
403 certBuffers
[k
] = certs
[k
]->derCert
.data
;
404 fileSizes
[k
] = certs
[k
]->derCert
.len
;
410 fprintf(stderr
, "ERROR: could not read file %s", DERFilePaths
[k
]);
416 MarFile
*mar
= mar_open(argv
[2]);
418 rv
= mar_verify_signatures(mar
, certBuffers
, fileSizes
, certCount
);
421 fprintf(stderr
, "ERROR: Could not open MAR file.\n");
425 for (k
= 0; k
< certCount
; ++k
) {
426 #if (defined(_WIN32) || defined(MACOSX)) && !defined(MAR_NSS)
427 free((void*)certBuffers
[k
]);
429 /* certBuffers[k] is owned by certs[k] so don't free it */
430 CERT_DestroyCertificate(certs
[k
]);
434 /* Determine if the source MAR file has the new fields for signing */
435 int hasSignatureBlock
;
436 if (get_mar_file_info(argv
[2], &hasSignatureBlock
,
437 NULL
, NULL
, NULL
, NULL
)) {
438 fprintf(stderr
, "ERROR: could not determine if MAR is old or new.\n");
439 } else if (!hasSignatureBlock
) {
440 fprintf(stderr
, "ERROR: The MAR file is in the old format so has"
441 " no signature to verify.\n");
448 if (!NSSConfigDir
|| certCount
== 0 || argc
< 4) {
452 return mar_repackage_and_sign(NSSConfigDir
, certNames
, certCount
,
456 return strip_signature_block(argv
[2], argv
[3]);
457 #endif /* endif NO_SIGN_VERIFY disabled */