LibreOffice Module onlineupdate (master) 1
mar.c
Go to the documentation of this file.
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 <onlineupdate/mar.h>
12
13#ifdef _WIN32
14#include <windows.h>
15#include <direct.h>
16#define chdir _chdir
17#else
18#include <unistd.h>
19#include <errno.h>
20#endif
21
22#ifndef APP_VERSION
23#error "Missing APP_VERSION"
24#endif
25
26#define MAR_CHANNEL_ID "LOOnlineUpdater" /* Dummy value; replace or
27 remove in the future */
28
29#if !defined(NO_SIGN_VERIFY) && (!defined(_WIN32) || defined(MAR_NSS))
30#include "cert.h"
31#include "pk11pub.h"
32int NSSInitCryptoContext(const char *NSSConfigDir);
33#endif
34
35int mar_repackage_and_sign(const char *NSSConfigDir,
36 const char * const *certNames,
37 uint32_t certCount,
38 const char *src,
39 const char * dest);
41static void print_version(void) {
42 printf("Version: %s\n", APP_VERSION);
43 printf("Default Channel ID: %s\n", MAR_CHANNEL_ID);
44}
46static void print_usage(void) {
47 printf("usage:\n");
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");
53
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");
60
61 printf("Strip a MAR signature:\n");
62 printf(" mar [-C workingDir] -r "
63 "signed_input_archive.mar output_archive.mar\n");
64
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");
68
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);
79#else
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);
85#endif
86 printf("At most %d verification certificate names are specified by "
87 "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
88#endif
89 printf("Print information on a MAR file:\n");
90 printf(" mar -t archive.mar\n");
91
92 printf("Print detailed information on a MAR file including signatures:\n");
93 printf(" mar -T archive.mar\n");
94
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");
98
99 printf("Print executable version:\n");
100 printf(" mar --version\n");
101 printf("This program does not handle unicode file paths properly\n");
102}
104static int mar_test_callback(MarFile *mar,
105 const MarItem *item,
106 void *unused) {
107 (void) mar; (void) unused; // avoid warnings
108
109 printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
110 return 0;
111}
113static int mar_test(const char *path) {
114 MarFile *mar;
115
116 mar = mar_open(path);
117 if (!mar)
118 return -1;
119
120 printf("SIZE\tMODE\tNAME\n");
122
123 mar_close(mar);
124 return 0;
125}
127int 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
133 uint32_t k;
134#endif
135 int rv = -1;
136 uint32_t certCount = 0;
137 int32_t sigIndex = -1;
138
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];
145#endif
146#endif
147
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));
151#endif
152#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(_WIN32)) || \
153 defined(MACOSX))
154 memset(DERFilePaths, 0, sizeof(DERFilePaths));
155 memset(fileSizes, 0, sizeof(fileSizes));
156#endif
157
158 if (argc > 1 && 0 == strcmp(argv[1], "--version")) {
160 return 0;
161 }
162
163 if (argc < 3) {
164 print_usage();
165 return -1;
166 }
167
168 while (argc > 0) {
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')) {
175 break;
176 /* -C workingdirectory */
177 } else if (argv[1][0] == '-' && argv[1][1] == 'C') {
178 chdir(argv[2]);
179 argv += 2;
180 argc -= 2;
181 }
182#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(_WIN32)) || \
183 defined(MACOSX))
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] == '-' &&
188 argv[1][1] == 'D' &&
189 (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) {
190 if (certCount >= MAX_SIGNATURES) {
191 print_usage();
192 return -1;
193 }
194 DERFilePaths[certCount++] = argv[2];
195 argv += 2;
196 argc -= 2;
197 }
198#endif
199 /* -d NSSConfigdir */
200 else if (argv[1][0] == '-' && argv[1][1] == 'd') {
201 NSSConfigDir = argv[2];
202 argv += 2;
203 argc -= 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] == '-' &&
208 argv[1][1] == 'n' &&
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) {
214 print_usage();
215 return -1;
216 }
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';
222 argv++;
223 argc--;
224 } else {
225 argv += 2;
226 argc -= 2;
227 }
228 /* MAR channel ID */
229 } else if (argv[1][0] == '-' && argv[1][1] == 'H') {
230 MARChannelID = argv[2];
231 argv += 2;
232 argc -= 2;
233 /* Product Version */
234 } else if (argv[1][0] == '-' && argv[1][1] == 'V') {
235 productVersion = argv[2];
236 argv += 2;
237 argc -= 2;
238 }
239 else {
240 print_usage();
241 return -1;
242 }
243 }
244
245 if (argv[1][0] != '-') {
246 print_usage();
247 return -1;
248 }
249
250 switch (argv[1][1]) {
251 case 'c': {
252 struct ProductInformationBlock infoBlock;
253 infoBlock.MARChannelID = MARChannelID;
254 infoBlock.productVersion = productVersion;
255 if (argv[argc - 2][0] == '-' && argv[argc - 2][1] == 'f')
256 {
257 char buf[1000];
258 FILE* file;
259 char** files;
260 int num_files = 0;
261
262 files = (char **)malloc(sizeof(char*)*10000);
263 errno = 0;
264 file = fopen(argv[argc - 1], "r");
265 if (!file)
266 {
267 printf("%d %s", errno, strerror(errno));
268 printf("Could not open file: %s", argv[argc - 1]);
269 exit(1);
270 }
271
272 while(fgets(buf, 1000, file) != NULL)
273 {
274 int j;
275 size_t str_len;
276 for (j=strlen(buf)-1;j>=0 && (buf[j]=='\n' || buf[j]=='\r');j--)
277 ;
278 buf[j+1]='\0';
279 str_len = strlen(buf) + 1;
280 files[num_files] = (char*)malloc(sizeof(char)*str_len);
281 strcpy(files[num_files], buf);
282 ++num_files;
283 }
284 fclose(file);
285 return mar_create(argv[2], num_files, files, &infoBlock);
286 }
287 else
288 return mar_create(argv[2], argc - 3, argv + 3, &infoBlock);
289 }
290 case 'i': {
291 struct ProductInformationBlock infoBlock;
292 infoBlock.MARChannelID = MARChannelID;
293 infoBlock.productVersion = productVersion;
294 return refresh_product_info_block(argv[2], &infoBlock);
295 }
296 case 'T': {
297 struct ProductInformationBlock infoBlock;
298 uint32_t numSignatures, numAdditionalBlocks;
299 int hasSignatureBlock, hasAdditionalBlock;
300 if (!get_mar_file_info(argv[2],
301 &hasSignatureBlock,
302 &numSignatures,
303 &hasAdditionalBlock,
304 NULL, &numAdditionalBlocks)) {
305 if (hasSignatureBlock) {
306 printf("Signature block found with %d signature%s\n",
307 numSignatures,
308 numSignatures != 1 ? "s" : "");
309 }
310 if (hasAdditionalBlock) {
311 printf("%d additional block%s found:\n",
312 numAdditionalBlocks,
313 numAdditionalBlocks != 1 ? "s" : "");
314 }
315
316 rv = read_product_info_block(argv[2], &infoBlock);
317 if (!rv) {
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);
325 }
326 }
327 printf("\n");
328 /* The fall through from 'T' to 't' is intentional */
329 }
330 /* Fall through */
331 case 't':
332 return mar_test(argv[2]);
333
334 /* Extract a MAR file */
335 case 'x':
336 return mar_extract(argv[2]);
337
338#ifndef NO_SIGN_VERIFY
339 /* Extract a MAR signature */
340 case 'X':
341 if (sigIndex == -1) {
342 fprintf(stderr, "ERROR: Signature index was not passed.\n");
343 return -1;
344 }
345 if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) {
346 fprintf(stderr, "ERROR: Signature index is out of range: %d.\n",
347 sigIndex);
348 return -1;
349 }
350 return extract_signature(argv[2], sigIndex, argv[3]);
351
352 /* Import a MAR signature */
353 case 'I':
354 if (sigIndex == -1) {
355 fprintf(stderr, "ERROR: signature index was not passed.\n");
356 return -1;
357 }
358 if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) {
359 fprintf(stderr, "ERROR: Signature index is out of range: %d.\n",
360 sigIndex);
361 return -1;
362 }
363 if (argc < 5) {
364 print_usage();
365 return -1;
366 }
367 return import_signature(argv[2], sigIndex, argv[3], argv[4]);
368
369 case 'v':
370 if (certCount == 0) {
371 print_usage();
372 return -1;
373 }
374
375#if (!defined(_WIN32) && !defined(MACOSX)) || defined(MAR_NSS)
376 if (!NSSConfigDir || certCount == 0) {
377 print_usage();
378 return -1;
379 }
380
381 if (NSSInitCryptoContext(NSSConfigDir)) {
382 fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
383 return -1;
384 }
385#endif
386
387 rv = 0;
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]);
392#else
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.
400 */
401 certs[k] = PK11_FindCertFromNickname(certNames[k], NULL);
402 if (certs[k]) {
403 certBuffers[k] = certs[k]->derCert.data;
404 fileSizes[k] = certs[k]->derCert.len;
405 } else {
406 rv = -1;
407 }
408#endif
409 if (rv) {
410 fprintf(stderr, "ERROR: could not read file %s", DERFilePaths[k]);
411 break;
412 }
413 }
414
415 if (!rv) {
416 MarFile *mar = mar_open(argv[2]);
417 if (mar) {
418 rv = mar_verify_signatures(mar, certBuffers, fileSizes, certCount);
419 mar_close(mar);
420 } else {
421 fprintf(stderr, "ERROR: Could not open MAR file.\n");
422 rv = -1;
423 }
424 }
425 for (k = 0; k < certCount; ++k) {
426#if (defined(_WIN32) || defined(MACOSX)) && !defined(MAR_NSS)
427 free((void*)certBuffers[k]);
428#else
429 /* certBuffers[k] is owned by certs[k] so don't free it */
430 CERT_DestroyCertificate(certs[k]);
431#endif
432 }
433 if (rv) {
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");
442 }
443 return -1;
444 }
445 return 0;
446
447 case 's':
448 if (!NSSConfigDir || certCount == 0 || argc < 4) {
449 print_usage();
450 return -1;
451 }
452 return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
453 argv[2], argv[3]);
454
455 case 'r':
456 return strip_signature_block(argv[2], argv[3]);
457#endif /* endif NO_SIGN_VERIFY disabled */
458
459 default:
460 print_usage();
461 return -1;
462 }
463}
return NULL
static int mar_test_callback(MarFile *mar, const MarItem *item, void *unused)
Definition: mar.c:103
static void print_usage(void)
Definition: mar.c:45
int NSSInitCryptoContext(const char *NSSConfigDir)
Initializes the NSS context.
Definition: mar_sign.c:35
static void print_version(void)
Definition: mar.c:40
int main(int argc, char **argv)
Definition: mar.c:126
#define MAR_CHANNEL_ID
Definition: mar.c:26
int mar_repackage_and_sign(const char *NSSConfigDir, const char *const *certNames, uint32_t certCount, const char *src, const char *dest)
Writes out a copy of the MAR at src but with embedded signatures.
Definition: mar_sign.c:818
static int mar_test(const char *path)
Definition: mar.c:112
#define MAX_SIGNATURES
Definition: mar.h:23
#define MAR_MAX_CERT_SIZE
Definition: mar.h:137
int refresh_product_info_block(const char *path, struct ProductInformationBlock *infoBlock)
Refreshes the product information block with the new information.
Definition: mar_create.c:193
int mar_create(const char *dest, int num_files, char **files, struct ProductInformationBlock *infoBlock)
Create a MAR file from a set of files.
Definition: mar_create.c:293
int mar_extract(const char *path)
Extract a MAR file to the current working directory.
Definition: mar_extract.c:73
int get_mar_file_info(const char *path, int *hasSignatureBlock, uint32_t *numSignatures, int *hasAdditionalBlocks, uint32_t *offsetAdditionalBlocks, uint32_t *numAdditionalBlocks)
Determines the MAR file information.
Definition: mar_read.c:551
void mar_close(MarFile *mar)
Close a MAR file that was opened using mar_open.
Definition: mar_read.c:195
int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure)
Enumerate all MAR items via callback function.
Definition: mar_read.c:497
MarFile * mar_open(const char *path)
Open a MAR file for reading.
Definition: mar_read.c:167
int read_product_info_block(char *path, struct ProductInformationBlock *infoBlock)
Reads the product info block from the MAR file's additional block section.
Definition: mar_read.c:369
int import_signature(const char *src, uint32_t sigIndex, const char *base64SigFile, const char *dest)
Imports a base64 encoded signature into a MAR file.
Definition: mar_sign.c:605
int extract_signature(const char *src, uint32_t sigIndex, const char *dest)
Extracts a signature from a MAR file, base64 encodes it, and writes it out.
Definition: mar_sign.c:489
int strip_signature_block(const char *src, const char *dest)
Writes out a copy of the MAR at src but with the signature block stripped.
Definition: mar_sign.c:255
int mar_read_entire_file(const char *filePath, uint32_t maxSize, const uint8_t **data, uint32_t *size)
Definition: mar_verify.c:22
int mar_verify_signatures(MarFile *mar, const uint8_t *const *certData, const uint32_t *certDataSizes, uint32_t certCount)
Verifies a MAR file by verifying each signature with the corresponding certificate.
Definition: mar_verify.c:136
Definition: mar.h:48
The MAR item data structure.
Definition: mar.h:38
uint32_t flags
Definition: mar.h:42
char name[1]
Definition: mar.h:43
uint32_t length
Definition: mar.h:41
const char * productVersion
Definition: mar.h:32
const char * MARChannelID
Definition: mar.h:31