|
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(XP_WIN) || defined(MAR_NSS)) |
|
22 int NSSInitCryptoContext(const char *NSSConfigDir); |
|
23 #endif |
|
24 |
|
25 int mar_repackage_and_sign(const char *NSSConfigDir, |
|
26 const char * const *certNames, |
|
27 uint32_t certCount, |
|
28 const char *src, |
|
29 const char * dest); |
|
30 |
|
31 static void print_version() { |
|
32 printf("Version: %s\n", MOZ_APP_VERSION); |
|
33 printf("Default Channel ID: %s\n", MAR_CHANNEL_ID); |
|
34 } |
|
35 |
|
36 static void print_usage() { |
|
37 printf("usage:\n"); |
|
38 printf("Create a MAR file:\n"); |
|
39 printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " |
|
40 "-c archive.mar [files...]\n"); |
|
41 |
|
42 printf("Extract a MAR file:\n"); |
|
43 printf(" mar [-C workingDir] -x archive.mar\n"); |
|
44 #ifndef NO_SIGN_VERIFY |
|
45 printf("Sign a MAR file:\n"); |
|
46 printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s " |
|
47 "archive.mar out_signed_archive.mar\n"); |
|
48 |
|
49 printf("Strip a MAR signature:\n"); |
|
50 printf(" mar [-C workingDir] -r " |
|
51 "signed_input_archive.mar output_archive.mar\n"); |
|
52 |
|
53 printf("Extract a MAR signature:\n"); |
|
54 printf(" mar [-C workingDir] -n(i) -X " |
|
55 "signed_input_archive.mar base_64_encoded_signature_file\n"); |
|
56 |
|
57 printf("Import a MAR signature:\n"); |
|
58 printf(" mar [-C workingDir] -n(i) -I " |
|
59 "signed_input_archive.mar base_64_encoded_signature_file " |
|
60 "changed_signed_output.mar\n"); |
|
61 printf("(i) is the index of the certificate to extract\n"); |
|
62 #if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(MAR_NSS)) |
|
63 printf("Verify a MAR file:\n"); |
|
64 printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n"); |
|
65 printf("At most %d signature certificate DER files are specified by " |
|
66 "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES); |
|
67 #else |
|
68 printf("Verify a MAR file:\n"); |
|
69 printf(" mar [-C workingDir] -d NSSConfigDir -n certname " |
|
70 "-v signed_archive.mar\n"); |
|
71 printf("At most %d signature certificate names are specified by " |
|
72 "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); |
|
73 #endif |
|
74 printf("At most %d verification certificate names are specified by " |
|
75 "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES); |
|
76 #endif |
|
77 printf("Print information on a MAR file:\n"); |
|
78 printf(" mar -t archive.mar\n"); |
|
79 |
|
80 printf("Print detailed information on a MAR file including signatures:\n"); |
|
81 printf(" mar -T archive.mar\n"); |
|
82 |
|
83 printf("Refresh the product information block of a MAR file:\n"); |
|
84 printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " |
|
85 "-i unsigned_archive_to_refresh.mar\n"); |
|
86 |
|
87 printf("Print executable version:\n"); |
|
88 printf(" mar --version\n"); |
|
89 printf("This program does not handle unicode file paths properly\n"); |
|
90 } |
|
91 |
|
92 static int mar_test_callback(MarFile *mar, |
|
93 const MarItem *item, |
|
94 void *unused) { |
|
95 printf("%u\t0%o\t%s\n", item->length, item->flags, item->name); |
|
96 return 0; |
|
97 } |
|
98 |
|
99 static int mar_test(const char *path) { |
|
100 MarFile *mar; |
|
101 |
|
102 mar = mar_open(path); |
|
103 if (!mar) |
|
104 return -1; |
|
105 |
|
106 printf("SIZE\tMODE\tNAME\n"); |
|
107 mar_enum_items(mar, mar_test_callback, NULL); |
|
108 |
|
109 mar_close(mar); |
|
110 return 0; |
|
111 } |
|
112 |
|
113 int main(int argc, char **argv) { |
|
114 char *NSSConfigDir = NULL; |
|
115 const char *certNames[MAX_SIGNATURES]; |
|
116 char *MARChannelID = MAR_CHANNEL_ID; |
|
117 char *productVersion = MOZ_APP_VERSION; |
|
118 uint32_t i, k; |
|
119 int rv = -1; |
|
120 uint32_t certCount = 0; |
|
121 int32_t sigIndex = -1; |
|
122 |
|
123 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) |
|
124 HANDLE certFile; |
|
125 uint8_t *certBuffers[MAX_SIGNATURES]; |
|
126 #endif |
|
127 #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ |
|
128 defined(XP_MACOSX)) |
|
129 char* DERFilePaths[MAX_SIGNATURES]; |
|
130 uint32_t fileSizes[MAX_SIGNATURES]; |
|
131 uint32_t read; |
|
132 #endif |
|
133 |
|
134 memset(certNames, 0, sizeof(certNames)); |
|
135 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) |
|
136 memset(certBuffers, 0, sizeof(certBuffers)); |
|
137 #endif |
|
138 #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ |
|
139 defined(XP_MACOSX)) |
|
140 memset(DERFilePaths, 0, sizeof(DERFilePaths)); |
|
141 memset(fileSizes, 0, sizeof(fileSizes)); |
|
142 #endif |
|
143 |
|
144 if (argc > 1 && 0 == strcmp(argv[1], "--version")) { |
|
145 print_version(); |
|
146 return 0; |
|
147 } |
|
148 |
|
149 if (argc < 3) { |
|
150 print_usage(); |
|
151 return -1; |
|
152 } |
|
153 |
|
154 while (argc > 0) { |
|
155 if (argv[1][0] == '-' && (argv[1][1] == 'c' || |
|
156 argv[1][1] == 't' || argv[1][1] == 'x' || |
|
157 argv[1][1] == 'v' || argv[1][1] == 's' || |
|
158 argv[1][1] == 'i' || argv[1][1] == 'T' || |
|
159 argv[1][1] == 'r' || argv[1][1] == 'X' || |
|
160 argv[1][1] == 'I')) { |
|
161 break; |
|
162 /* -C workingdirectory */ |
|
163 } else if (argv[1][0] == '-' && argv[1][1] == 'C') { |
|
164 chdir(argv[2]); |
|
165 argv += 2; |
|
166 argc -= 2; |
|
167 } |
|
168 #if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \ |
|
169 defined(XP_MACOSX)) |
|
170 /* -D DERFilePath, also matches -D[index] DERFilePath |
|
171 We allow an index for verifying to be symmetric |
|
172 with the import and export command line arguments. */ |
|
173 else if (argv[1][0] == '-' && |
|
174 argv[1][1] == 'D' && |
|
175 (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) { |
|
176 if (certCount >= MAX_SIGNATURES) { |
|
177 print_usage(); |
|
178 return -1; |
|
179 } |
|
180 DERFilePaths[certCount++] = argv[2]; |
|
181 argv += 2; |
|
182 argc -= 2; |
|
183 } |
|
184 #endif |
|
185 /* -d NSSConfigdir */ |
|
186 else if (argv[1][0] == '-' && argv[1][1] == 'd') { |
|
187 NSSConfigDir = argv[2]; |
|
188 argv += 2; |
|
189 argc -= 2; |
|
190 /* -n certName, also matches -n[index] certName |
|
191 We allow an index for verifying to be symmetric |
|
192 with the import and export command line arguments. */ |
|
193 } else if (argv[1][0] == '-' && |
|
194 argv[1][1] == 'n' && |
|
195 (argv[1][2] == (char)('0' + certCount) || |
|
196 argv[1][2] == '\0' || |
|
197 !strcmp(argv[2], "-X") || |
|
198 !strcmp(argv[2], "-I"))) { |
|
199 if (certCount >= MAX_SIGNATURES) { |
|
200 print_usage(); |
|
201 return -1; |
|
202 } |
|
203 certNames[certCount++] = argv[2]; |
|
204 if (strlen(argv[1]) > 2 && |
|
205 (!strcmp(argv[2], "-X") || !strcmp(argv[2], "-I")) && |
|
206 argv[1][2] >= '0' && argv[1][2] <= '9') { |
|
207 sigIndex = argv[1][2] - '0'; |
|
208 argv++; |
|
209 argc--; |
|
210 } else { |
|
211 argv += 2; |
|
212 argc -= 2; |
|
213 } |
|
214 /* MAR channel ID */ |
|
215 } else if (argv[1][0] == '-' && argv[1][1] == 'H') { |
|
216 MARChannelID = argv[2]; |
|
217 argv += 2; |
|
218 argc -= 2; |
|
219 /* Product Version */ |
|
220 } else if (argv[1][0] == '-' && argv[1][1] == 'V') { |
|
221 productVersion = argv[2]; |
|
222 argv += 2; |
|
223 argc -= 2; |
|
224 } |
|
225 else { |
|
226 print_usage(); |
|
227 return -1; |
|
228 } |
|
229 } |
|
230 |
|
231 if (argv[1][0] != '-') { |
|
232 print_usage(); |
|
233 return -1; |
|
234 } |
|
235 |
|
236 switch (argv[1][1]) { |
|
237 case 'c': { |
|
238 struct ProductInformationBlock infoBlock; |
|
239 infoBlock.MARChannelID = MARChannelID; |
|
240 infoBlock.productVersion = productVersion; |
|
241 return mar_create(argv[2], argc - 3, argv + 3, &infoBlock); |
|
242 } |
|
243 case 'i': { |
|
244 struct ProductInformationBlock infoBlock; |
|
245 infoBlock.MARChannelID = MARChannelID; |
|
246 infoBlock.productVersion = productVersion; |
|
247 return refresh_product_info_block(argv[2], &infoBlock); |
|
248 } |
|
249 case 'T': { |
|
250 struct ProductInformationBlock infoBlock; |
|
251 uint32_t numSignatures, numAdditionalBlocks; |
|
252 int hasSignatureBlock, hasAdditionalBlock; |
|
253 if (!get_mar_file_info(argv[2], |
|
254 &hasSignatureBlock, |
|
255 &numSignatures, |
|
256 &hasAdditionalBlock, |
|
257 NULL, &numAdditionalBlocks)) { |
|
258 if (hasSignatureBlock) { |
|
259 printf("Signature block found with %d signature%s\n", |
|
260 numSignatures, |
|
261 numSignatures != 1 ? "s" : ""); |
|
262 } |
|
263 if (hasAdditionalBlock) { |
|
264 printf("%d additional block%s found:\n", |
|
265 numAdditionalBlocks, |
|
266 numAdditionalBlocks != 1 ? "s" : ""); |
|
267 } |
|
268 |
|
269 rv = read_product_info_block(argv[2], &infoBlock); |
|
270 if (!rv) { |
|
271 printf(" - Product Information Block:\n"); |
|
272 printf(" - MAR channel name: %s\n" |
|
273 " - Product version: %s\n", |
|
274 infoBlock.MARChannelID, |
|
275 infoBlock.productVersion); |
|
276 free((void *)infoBlock.MARChannelID); |
|
277 free((void *)infoBlock.productVersion); |
|
278 } |
|
279 } |
|
280 printf("\n"); |
|
281 /* The fall through from 'T' to 't' is intentional */ |
|
282 } |
|
283 case 't': |
|
284 return mar_test(argv[2]); |
|
285 |
|
286 /* Extract a MAR file */ |
|
287 case 'x': |
|
288 return mar_extract(argv[2]); |
|
289 |
|
290 #ifndef NO_SIGN_VERIFY |
|
291 /* Extract a MAR signature */ |
|
292 case 'X': |
|
293 if (sigIndex == -1) { |
|
294 fprintf(stderr, "ERROR: Signature index was not passed.\n"); |
|
295 return -1; |
|
296 } |
|
297 if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { |
|
298 fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", |
|
299 sigIndex); |
|
300 return -1; |
|
301 } |
|
302 return extract_signature(argv[2], sigIndex, argv[3]); |
|
303 |
|
304 /* Import a MAR signature */ |
|
305 case 'I': |
|
306 if (sigIndex == -1) { |
|
307 fprintf(stderr, "ERROR: signature index was not passed.\n"); |
|
308 return -1; |
|
309 } |
|
310 if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) { |
|
311 fprintf(stderr, "ERROR: Signature index is out of range: %d.\n", |
|
312 sigIndex); |
|
313 return -1; |
|
314 } |
|
315 if (argc < 5) { |
|
316 print_usage(); |
|
317 return -1; |
|
318 } |
|
319 return import_signature(argv[2], sigIndex, argv[3], argv[4]); |
|
320 |
|
321 case 'v': |
|
322 |
|
323 #if defined(XP_WIN) && !defined(MAR_NSS) |
|
324 if (certCount == 0) { |
|
325 print_usage(); |
|
326 return -1; |
|
327 } |
|
328 |
|
329 for (k = 0; k < certCount; ++k) { |
|
330 /* If the mar program was built using CryptoAPI, then read in the buffer |
|
331 containing the cert from disk. */ |
|
332 certFile = CreateFileA(DERFilePaths[k], GENERIC_READ, |
|
333 FILE_SHARE_READ | |
|
334 FILE_SHARE_WRITE | |
|
335 FILE_SHARE_DELETE, |
|
336 NULL, |
|
337 OPEN_EXISTING, |
|
338 0, NULL); |
|
339 if (INVALID_HANDLE_VALUE == certFile) { |
|
340 return -1; |
|
341 } |
|
342 fileSizes[k] = GetFileSize(certFile, NULL); |
|
343 certBuffers[k] = malloc(fileSizes[k]); |
|
344 if (!ReadFile(certFile, certBuffers[k], fileSizes[k], &read, NULL) || |
|
345 fileSizes[k] != read) { |
|
346 CloseHandle(certFile); |
|
347 for (i = 0; i <= k; i++) { |
|
348 free(certBuffers[i]); |
|
349 } |
|
350 return -1; |
|
351 } |
|
352 CloseHandle(certFile); |
|
353 } |
|
354 |
|
355 rv = mar_verify_signatures(argv[2], certBuffers, fileSizes, |
|
356 NULL, certCount); |
|
357 for (k = 0; k < certCount; ++k) { |
|
358 free(certBuffers[k]); |
|
359 } |
|
360 if (rv) { |
|
361 /* Determine if the source MAR file has the new fields for signing */ |
|
362 int hasSignatureBlock; |
|
363 if (get_mar_file_info(argv[2], &hasSignatureBlock, |
|
364 NULL, NULL, NULL, NULL)) { |
|
365 fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); |
|
366 } else if (!hasSignatureBlock) { |
|
367 fprintf(stderr, "ERROR: The MAR file is in the old format so has" |
|
368 " no signature to verify.\n"); |
|
369 } |
|
370 return -1; |
|
371 } |
|
372 |
|
373 return 0; |
|
374 |
|
375 #elif defined(XP_MACOSX) |
|
376 return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths, |
|
377 0, NULL, certCount); |
|
378 #else |
|
379 if (!NSSConfigDir || certCount == 0) { |
|
380 print_usage(); |
|
381 return -1; |
|
382 } |
|
383 |
|
384 if (NSSInitCryptoContext(NSSConfigDir)) { |
|
385 fprintf(stderr, "ERROR: Could not initialize crypto library.\n"); |
|
386 return -1; |
|
387 } |
|
388 |
|
389 return mar_verify_signatures(argv[2], NULL, 0, certNames, certCount); |
|
390 |
|
391 #endif /* defined(XP_WIN) && !defined(MAR_NSS) */ |
|
392 case 's': |
|
393 if (!NSSConfigDir || certCount == 0 || argc < 4) { |
|
394 print_usage(); |
|
395 return -1; |
|
396 } |
|
397 return mar_repackage_and_sign(NSSConfigDir, certNames, certCount, |
|
398 argv[2], argv[3]); |
|
399 |
|
400 case 'r': |
|
401 return strip_signature_block(argv[2], argv[3]); |
|
402 #endif /* endif NO_SIGN_VERIFY disabled */ |
|
403 |
|
404 default: |
|
405 print_usage(); |
|
406 return -1; |
|
407 } |
|
408 |
|
409 return 0; |
|
410 } |