1/*
2 * Copyright (c) 2005 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 03-Apr-2005
31 * DRI: Rob Braun <bbraun@opendarwin.org>
32 */
33
34#include <stdlib.h>
35#include <stdio.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <fts.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <inttypes.h>
43#include <netinet/in.h>
44#include <libxml/xmlreader.h>
45#include <libxml/xmlwriter.h>
46#include <libxml/xmlstring.h>
47#include <limits.h>
48#include <getopt.h>
49#include <regex.h>
50#include <errno.h>
51#ifdef XARSIG_BUILDING_WITH_XAR
52#include "xar.h"
53#else
54#include <xar/xar.h>
55#endif // XARSIG_BUILDING_WITH_XAR
56#include "filetree.h"
57
58#define SYMBOLIC 1
59#define NUMERIC  2
60
61// error codes for B&I
62#define E_NOSIG		60
63#define E_SIGEXISTS 61
64
65static int Perms = 0;
66static int Local = 0;
67static char *Subdoc = NULL;
68static char *SubdocName = NULL;
69static char *Toccksum = NULL;
70static char *Filecksum = NULL;
71static char *Compression = NULL;
72static char *Rsize = NULL;
73static char *DataToSignDumpPath = NULL;
74static char *SigOffsetDumpPath = NULL;
75static char *LeafCertPath = NULL;
76static char *IntermediateCertPath = NULL;
77static char *RootCertPath = NULL;
78
79static int Err = 0;
80static int Verbose = 0;
81static int Coalesce = 0;
82static int LinkSame = 0;
83static int DoSign = 0;
84
85static long SigSize = 0;
86
87struct lnode {
88	char *str;
89	regex_t reg;
90	struct lnode *next;
91};
92struct __stack_element {
93	struct __stack_element *prev;
94	struct __stack_element *next;
95	void *data;
96};
97struct __stack {
98	struct __stack_element *bottom;
99	struct __stack_element *top;
100};
101
102typedef struct __stack_element *stack_element;
103typedef struct __stack *stack;
104
105struct lnode *Exclude = NULL;
106struct lnode *Exclude_Tail = NULL;
107struct lnode *NoCompress = NULL;
108struct lnode *NoCompress_Tail = NULL;
109
110static int32_t err_callback(int32_t sev, int32_t err, xar_errctx_t ctx, void *usrctx);
111static int32_t signingCallback(xar_signature_t sig, void *context, uint8_t *data, uint32_t length, uint8_t **signed_data, uint32_t *signed_len);
112static void insert_cert(xar_signature_t sig, const char *cert_path);
113static void add_subdoc(xar_t x);
114
115static void _set_xarsig_opts_from_args(xar_t x) {
116	// Update checksum algorithms before we start messing with the heap length in xar_signature_new()
117	if( Toccksum )
118		xar_opt_set(x, XAR_OPT_TOCCKSUM, Toccksum);
119
120	if( Filecksum )
121		xar_opt_set(x, XAR_OPT_FILECKSUM, Filecksum);
122
123	if ( SigSize ) {
124		xar_signature_t sig = xar_signature_new(x, "RSA", SigSize, &signingCallback, NULL);
125		if ( LeafCertPath )
126			insert_cert(sig, LeafCertPath);
127		if ( IntermediateCertPath )
128			insert_cert(sig, IntermediateCertPath);
129		if ( RootCertPath )
130			insert_cert(sig, RootCertPath);
131	}
132
133	if( Compression )
134		xar_opt_set(x, XAR_OPT_COMPRESSION, Compression);
135
136	if( Coalesce )
137		xar_opt_set(x, XAR_OPT_COALESCE, "true");
138
139	if( LinkSame )
140		xar_opt_set(x, XAR_OPT_LINKSAME, "true");
141
142	if ( Rsize != NULL )
143		xar_opt_set(x, XAR_OPT_RSIZE, Rsize);
144
145	xar_register_errhandler(x, err_callback, NULL);
146
147	if( Subdoc )
148		add_subdoc(x);
149
150	if( Perms == SYMBOLIC ) {
151		xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_SYMBOLIC);
152	}
153	if( Perms == NUMERIC ) {
154		xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_NUMERIC);
155	}
156}
157
158static void print_file(xar_file_t f) {
159	if( Verbose ) {
160		char *path = xar_get_path(f);
161		printf("%s\n", path);
162		free(path);
163	}
164}
165
166static void add_subdoc(xar_t x) {
167	xar_subdoc_t s;
168	int fd;
169	unsigned char *buf;
170	unsigned int len;
171	struct stat sb;
172
173	if( SubdocName == NULL ) SubdocName = "subdoc";
174	s = xar_subdoc_new(x, (const char *)SubdocName);
175
176	fd = open(Subdoc, O_RDONLY);
177	if( fd < 0 )
178		return;
179	fstat(fd, &sb);
180	len = sb.st_size;
181	buf = malloc(len+1);
182	if( buf == NULL ) {
183		close(fd);
184		return;
185	}
186	memset(buf, 0, len+1);
187	read(fd, buf, len);
188	close(fd);
189
190	xar_subdoc_copyin(s, buf, len);
191
192
193	return;
194}
195
196static void extract_subdoc(xar_t x, const char *name) {
197	xar_subdoc_t i;
198
199	for( i = xar_subdoc_first(x); i; i = xar_subdoc_next(i) ) {
200		const char *sname = xar_subdoc_name(i);
201		unsigned char *sdoc;
202		int fd, size;
203		if( name && strcmp(name, sname) != 0 )
204			continue;
205		xar_subdoc_copyout(i, &sdoc, (unsigned int *)&size);
206		fd = open(Subdoc, O_WRONLY|O_CREAT|O_TRUNC, 0644);
207		if( fd < 0 ) return;
208		write(fd, sdoc, size);
209		close(fd);
210		free(sdoc);
211	}
212
213	return;
214}
215
216static void extract_data_to_sign(const char *filename) {
217	xar_signature_t sig;
218	off_t signatureOffset;
219	FILE *file;
220	xar_t x;
221	int i;
222	uint32_t dataToSignOffset = 0;
223	uint32_t dataToSignSize = 0;
224	char *buffer = NULL;
225	const char *value;
226
227	// find signature stub
228	x = xar_open(filename, READ);
229	if ( x == NULL ) {
230		fprintf(stderr, "Could not open %s to extract data to sign!\n", filename);
231		exit(1);
232	}
233	sig = xar_signature_first(x);
234	if ( !sig ) {
235		fprintf(stderr, "No signatures found to extract data from.\n");
236		exit(E_NOSIG);
237	}
238
239	// locate data to sign
240	if( 0 != xar_prop_get((xar_file_t)x, "checksum/offset" ,&value) ) {
241		fprintf(stderr, "Could not locate checksum/offset in archive.\n");
242		exit(1);
243	}
244	dataToSignOffset = xar_get_heap_offset(x);
245	dataToSignOffset += strtoull(value, (char **)NULL, 10);
246	if( 0 != xar_prop_get((xar_file_t)x, "checksum/size" ,&value) ) {
247		fprintf(stderr, "Could not locate checksum/size in archive.\n");
248		exit(1);
249	}
250	dataToSignSize = strtoull(value, (char **)NULL, 10);
251
252	// get signature offset (inject signature here)
253	xar_signature_copy_signed_data(sig, NULL, NULL, NULL, NULL, &signatureOffset);
254	signatureOffset += xar_get_heap_offset(x);
255	xar_close(x);
256
257	// now get data to be signed, using offset and size
258	file = fopen(filename, "r");
259	if (!file) {
260		fprintf(stderr, "Could not open %s for reading data to sign!\n", filename);
261		exit(1);
262	}
263	fseek(file, dataToSignOffset, SEEK_SET);
264	buffer = malloc(dataToSignSize);
265	i = fread(buffer, dataToSignSize, 1, file);
266	if (i != 1) {
267		fprintf(stderr, "Failed to read data to sign from %s!\n", filename);
268		exit(1);
269	}
270	fclose(file);
271
272	// save data to sign
273	file = fopen(DataToSignDumpPath, "w");
274	if (!file) {
275		fprintf(stderr, "Could not open %s for saving data to sign!\n", DataToSignDumpPath);
276		exit(1);
277	}
278	i = fwrite(buffer, dataToSignSize, 1, file);
279	if (i != 1) {
280		fprintf(stderr, "Failed to write data to sign to %s (fwrite() returned %i)!\n", DataToSignDumpPath, i);
281		exit(1);
282	}
283	fclose(file);
284
285	// save signature offset
286	file = fopen(SigOffsetDumpPath, "w");
287	if (!file) {
288		fprintf(stderr, "Could not open %s for saving signature offset!\n", SigOffsetDumpPath);
289		exit(1);
290	}
291	i = fprintf(file, "%lli\n", signatureOffset);
292	if (i < 0) {
293		fprintf(stderr, "Failed to write signature offset to %s (fprintf() returned %i)!\n", SigOffsetDumpPath, i);
294		exit(1);
295	}
296	fclose(file);
297
298	free(buffer);
299}
300
301static void extract_certs(char *filename, char *cert_base_path) {
302	xar_signature_t sig;
303	xar_t x;
304	int32_t count;
305	int i;
306	const uint8_t *cert_data;
307	uint32_t cert_len;
308	FILE *file;
309	char *cert_path;
310
311	// open xar, get signature
312	x = xar_open(filename, READ);
313	if ( x == NULL ) {
314		fprintf(stderr, "Could not open %s to extract certificates!\n", filename);
315		exit(1);
316	}
317	sig = xar_signature_first(x);
318	if ( !sig ) {
319		fprintf(stderr, "No signatures found to extract data from.\n");
320		exit(E_NOSIG);
321	}
322
323	// iterate through all certificates associated with that signature, write them to disk
324	count = xar_signature_get_x509certificate_count(sig);
325	if (!count) {
326		fprintf(stderr, "Signature bears no certificates. Odd.\n");
327		exit(1);
328	}
329	for (i=0; i<count; i++) {
330		xar_signature_get_x509certificate_data(sig, i, &cert_data, &cert_len);
331		asprintf(&cert_path, "%s/cert%02i", cert_base_path, i);
332		file = fopen(cert_path, "w");
333		if (!file) {
334			fprintf(stderr, "Could not save certificate %i to %s.\n", i, cert_path);
335			exit(1);
336		}
337		int n = fwrite(cert_data, cert_len, 1, file);
338		if (n < 0) {
339			fprintf(stderr, "Failed to write certificate to %s (fwrite() returned %i)!\n", cert_path, n);
340			exit(1);
341		}
342		fclose(file);
343		free(cert_path);
344	}
345
346	// clean up
347	xar_close(x);
348}
349
350static off_t get_sig_offset(xar_t x) {
351	off_t signedDataOffset = 0;
352
353	xar_signature_t sig = xar_signature_first(x);
354	xar_signature_copy_signed_data(sig,NULL,NULL,NULL,NULL,&signedDataOffset);
355	signedDataOffset += xar_get_heap_offset(x);
356	return signedDataOffset;
357}
358
359static int extract_sig_offset(xar_t x, const char *filename) {
360	off_t signedDataOffset;
361	FILE *file;
362	int i;
363
364	// get offset
365	signedDataOffset = get_sig_offset(x);
366
367	// and save it to file
368	file = fopen(filename, "w");
369	if (!file) {
370		fprintf(stderr, "Could not open %s for saving signature offset!\n", filename);
371		return 1;
372	}
373	i = fprintf(file, "%lli\n", signedDataOffset);
374	if (i < 0) {
375		fprintf(stderr, "Failed to write signature offset to %s (fprintf() returned %i)!\n", filename, i);
376		return 1;
377	}
378	fclose(file);
379
380	return 0;
381}
382
383stack stack_new() {
384	stack s = (stack)malloc(sizeof(struct __stack));
385	s->bottom = s->top = NULL;
386	return s;
387}
388void stack_free(stack s) {
389	free(s);
390}
391void stack_push(stack s, void *data) {
392	stack_element e = malloc(sizeof(struct __stack_element));
393	e->data = data;
394	if (s->top) {
395		s->top->next = e;
396		e->prev = s->top;
397	} else {
398		s->top = s->bottom = e;
399		e->prev = NULL;
400	}
401	e->next = NULL;
402	s->top = e;
403}
404void *stack_pop(stack s) {
405	if (!s->top)
406		return NULL;
407	void *ret = s->top->data;
408	stack_element temp = s->top;
409	s->top = s->top->prev;
410	free(temp);
411	if (s->top)
412		s->top->next = NULL;
413	else
414		s->bottom = NULL;
415	return ret;
416}
417
418/* replace_sign: rip out all current signatures and certs and insert a new pair
419		Since libxar is currently not capable of doing this directly, we have to create a new xar archive,
420		copy all the files and options from the current archive, and sign the new archive
421*/
422static void replace_sign(const char *filename) {
423
424	xar_t old_xar, new_xar;
425	xar_signature_t sig;
426	char *new_xar_path = (char *)malloc(15);
427	strncpy(new_xar_path, "/tmp/xar.XXXXX", 15);
428	new_xar_path = mktemp(new_xar_path);
429	char *systemcall;
430	int err;
431
432	// open the first archive
433	old_xar = xar_open(filename, READ);
434	if ( old_xar == NULL ) {
435		fprintf(stderr, "Could not open archive %s!\n", filename);
436		exit(1);
437	}
438
439	// use command-line arguments to set xar opts; the copy operation below won't work unless they're set in the first place!
440	_set_xarsig_opts_from_args(old_xar);
441
442	// open the second archive
443	new_xar = xar_open(new_xar_path, WRITE);
444	if( !new_xar ) {
445		fprintf(stderr, "Error creating new archive %s\n", new_xar_path);
446		exit(1);
447	}
448
449	// copy options
450	char *opts[7] = {XAR_OPT_TOCCKSUM, XAR_OPT_FILECKSUM, XAR_OPT_COMPRESSION, XAR_OPT_COALESCE, XAR_OPT_LINKSAME, XAR_OPT_RSIZE, XAR_OPT_OWNERSHIP};
451	int i;
452	const char *opt;
453	for (i=0; i<7; i++) {
454		opt = xar_opt_get(old_xar, opts[i]);
455		if (opt)
456			xar_opt_set(new_xar, opts[i], opt);
457	}
458
459	// install new signature and new certs in new_xar
460	sig = xar_signature_new(new_xar, "RSA", SigSize, &signingCallback, NULL);
461	if ( LeafCertPath )
462		insert_cert(sig, LeafCertPath);
463	if ( IntermediateCertPath )
464		insert_cert(sig, IntermediateCertPath);
465	if ( RootCertPath )
466		insert_cert(sig, RootCertPath);
467
468	// skip copy subdocs for now since we don't use them yet
469
470	// copy files
471	xar_iter_t iter = xar_iter_new();
472	xar_file_t f = xar_file_first(old_xar, iter);
473		// xar_file_next iterates the archive depth-first, i.e. all children are enumerated before the siblings.
474	const char *name;
475	stack s_new = stack_new();
476	stack s_old = stack_new();
477	xar_file_t last_copied = NULL, last_added;
478
479	xar_iter_t loopIter = xar_iter_new();
480	xar_file_t current_xar_file;
481	for (current_xar_file = xar_file_first(old_xar, loopIter); current_xar_file; current_xar_file = xar_file_next(loopIter))
482	{
483		printf("old_xar -> %s (parent: %s)\n",xar_get_path(current_xar_file),XAR_FILE(current_xar_file)->parent?xar_get_path(XAR_FILE(current_xar_file)->parent):"(nil)");
484	}
485
486	do {
487		// parent is the parent in the new archive!
488		// 3 cases:
489		//  1. the file has no parent. Happens for every file at the top level of the archive.
490		//  2. the file's parent is the last file we added. Happens while descending down a path
491		//  3. the file's parent is one of the ancestors of the last file (and not NULL, that would be case 1)
492		//		that means we either go back up the tree and add a sibling of one of the ancestors, or we add a
493		//		sibling on the same level
494		xar_prop_get(f, "name", &name);	// filename, without any path info
495		if (!XAR_FILE(f)->parent) {	// case 1
496			printf("root: %s\n",xar_get_path(f));
497			last_added = xar_add_from_archive(new_xar, NULL, name, old_xar, f);
498			last_copied = f;
499			stack_push(s_new, (void *)last_added);
500			stack_push(s_old, (void *)last_copied);
501		} else if (f->parent == last_copied) {	// case 2
502			printf("child: %s -> %s\n",xar_get_path(f->parent),xar_get_path(f));
503			last_added = xar_add_from_archive(new_xar, last_added, name, old_xar, f);
504			last_copied = f;
505			stack_push(s_new, (void *)last_added);
506			stack_push(s_old, (void *)last_copied);
507		} else {	// case 3
508			printf("searching for parent: %s ?\n",xar_get_path(f));
509			while (XAR_FILE(f)->parent != XAR_FILE(s_old->top->data)->parent) {
510				printf("popping: %s\n",xar_get_path(XAR_FILE(s_old->top->data)));
511				stack_pop(s_new);
512				stack_pop(s_old);
513			}
514			printf("found: %s -> %s\n",xar_get_path(XAR_FILE(s_new->top->data)),xar_get_path(f));
515			stack_pop(s_new);
516			stack_pop(s_old);
517			last_added = xar_add_from_archive(new_xar, (xar_file_t)(s_new->top->data), name, old_xar, f);
518			last_copied = f;
519			stack_push(s_new, (void *)last_added);
520			stack_push(s_old, (void *)last_copied);
521		}
522	} while ((f = xar_file_next(iter)));
523
524	loopIter = xar_iter_new();
525	for (current_xar_file = xar_file_first(new_xar, loopIter); current_xar_file; current_xar_file = xar_file_next(loopIter))
526	{
527		char * current_path = xar_get_path(current_xar_file);
528		printf("new_xar -> %s\n",current_path);
529	}
530
531	xar_iter_free(iter);
532	stack_free(s_new);
533	stack_free(s_old);
534	if( xar_close(new_xar) != 0 ) {
535		fprintf(stderr, "Error creating the archive\n");
536		if( !Err )
537			Err = 42;
538	}
539	xar_close(old_xar);
540
541	// write signature offset to file (have to re-open so xar_close can figure out the correct offset)
542	new_xar = xar_open(new_xar_path, READ);
543	if( !new_xar ) {
544		fprintf(stderr, "Error re-opening new archive %s\n", new_xar_path);
545		unlink(new_xar_path);
546		exit(1);
547	}
548
549	if (extract_sig_offset(new_xar, SigOffsetDumpPath)) {
550		xar_close(new_xar);
551		unlink(new_xar_path);
552		exit(1);
553	}
554
555	xar_close(new_xar);
556
557	// delete old archive, move new in its place
558	unlink(filename);
559	asprintf(&systemcall, "cp \"%s\" \"%s\"", new_xar_path, filename);
560	err = system(systemcall);
561	if (err) {
562		fprintf(stderr, "Could not copy new archive to final location (system() returned %i)\n", err);
563		unlink(new_xar_path);
564		exit(1);
565	}
566
567	// Delete the tmp archive
568	unlink(new_xar_path);
569
570	free(systemcall);
571}
572
573/*	belated_sign
574	Prepare a previously unsigned archive for signing by creating a signature placeholder and inserting the certificates.
575	Since libxar is currently not capable of doing this directly, we have to create a new xar archive,
576	copy all the files and options from the current archive, and sign the new archive
577*/
578static void belated_sign(const char *filename) {
579	xar_t x = xar_open(filename, READ);
580	if ( x == NULL ) {
581		fprintf(stderr, "Could not open archive %s!\n", filename);
582		exit(1);
583	}
584	xar_signature_t sig = xar_signature_first(x);
585	if ( sig ) {
586		fprintf(stderr, "Archive has already been signed. Use --replace-sign instead.\n");
587		exit(E_SIGEXISTS);
588	}
589	xar_close(x);
590	replace_sign(filename);
591}
592
593static int32_t signingCallback(xar_signature_t sig, void *context, uint8_t *data, uint32_t length, uint8_t **signed_data, uint32_t *signed_len) {
594
595	// save data to file for later signature
596	if (DataToSignDumpPath) {
597		FILE *file = fopen(DataToSignDumpPath, "w");
598		if (!file) {
599			fprintf(stderr, "Could not open %s for saving data to sign!\n", DataToSignDumpPath);
600			exit(1);
601		}
602		int i = fwrite(data, length, 1, file);
603		if (i != 1) {
604			fprintf(stderr, "Failed to write data to sign to %s (fwrite() returned %i)!\n", DataToSignDumpPath, i);
605			exit(1);
606		}
607		fclose(file);
608	}
609
610	// now return blank placeholder data
611	*signed_data = (uint8_t *)malloc(SigSize);
612	memset(*signed_data, 0, SigSize);
613	strncpy((char *)*signed_data, "helloworld", 10);	// debug
614	*signed_len = SigSize;
615	return 0;	// no error
616}
617
618static void insert_cert(xar_signature_t sig, const char *cert_path) {
619	struct stat *s = malloc(sizeof(struct stat));
620	if (stat(cert_path, s) == -1) {
621		fprintf(stderr, "Could not stat() certificate file %s (errno == %i)\n", cert_path, errno);
622		exit(1);
623	}
624	void *cert = malloc(s->st_size);
625	FILE *file = fopen(cert_path, "r");
626	if (!file) {
627		fprintf(stderr, "Could not open %s for reading certificate!\n", cert_path);
628		exit(1);
629	}
630	int i = fread(cert, s->st_size, 1, file);
631	if (i != 1) {
632		fprintf(stderr, "Failed to read certificate from %s!\n", cert_path);
633		exit(1);
634	}
635	fclose(file);
636	xar_signature_add_x509certificate(sig, cert, s->st_size);
637	free(s);
638	free(cert);
639}
640
641static void inject_signature(const char *xar_path, const char *sig_path) {
642	// since there is no API to insert a signature other than during signingCallback time, we have to
643	// inject it by editing the raw file
644	int buffer_size = 1024;
645	void *buffer = malloc(buffer_size);
646	FILE *sig, *xar;
647	off_t signedDataOffset;
648	xar_t x;
649	int i;
650
651	printf("inject_signature(%s, %s)",xar_path,sig_path);
652
653	// open xar via the API first to determine the signature offset
654	x = xar_open(xar_path, READ);
655	if ( x == NULL ) {
656		fprintf(stderr, "Could not open xar archive %s to get signature offset!\n", xar_path);
657		exit(1);
658	}
659	signedDataOffset = get_sig_offset(x);
660	xar_close(x);
661
662	// then re-open xar and signature files raw...
663	sig = fopen(sig_path, "r");
664	if (!sig) {
665		fprintf(stderr, "Could not open %s for reading signature!\n", sig_path);
666		exit(1);
667	}
668	xar = fopen(xar_path, "r+");
669	if (!xar) {
670		fprintf(stderr, "Could not open xar archive %s for injecting signature!\n", xar_path);
671		exit(1);
672	}
673
674	// ...and inject the signature
675	fseek(xar, signedDataOffset, SEEK_SET);
676	do {
677		i = fread(buffer, 1, buffer_size, sig);
678		if (ferror(sig)) {
679			fprintf(stderr, "Failed to read signature from %s!\n", sig_path);
680			exit(1);
681		}
682		fwrite(buffer, 1, i, xar);
683	} while (!feof(sig));
684	fclose(sig);
685	fclose(xar);
686
687	free(buffer);
688}
689
690static int archive(const char *filename, int arglen, char *args[]) {
691	xar_t x;
692	FTS *fts;
693	FTSENT *ent;
694	int flags;
695	struct lnode *i;
696	const char *default_compression;
697
698	x = xar_open(filename, WRITE);
699	if( !x ) {
700		fprintf(stderr, "Error creating archive %s\n", filename);
701		exit(1);
702	}
703
704	_set_xarsig_opts_from_args(x);
705
706	default_compression = strdup(xar_opt_get(x, XAR_OPT_COMPRESSION));
707	if( !default_compression )
708		default_compression = strdup(XAR_OPT_VAL_GZIP);
709
710	flags = FTS_PHYSICAL|FTS_NOSTAT|FTS_NOCHDIR;
711	if( Local )
712		flags |= FTS_XDEV;
713	fts = fts_open(args, flags, NULL);
714	if( !fts ) {
715		fprintf(stderr, "Error traversing file tree\n");
716		exit(1);
717	}
718
719	while( (ent = fts_read(fts)) ) {
720		xar_file_t f;
721		int exclude_match = 1;
722		int nocompress_match = 1;
723		if( ent->fts_info == FTS_DP )
724			continue;
725
726		if( strcmp(ent->fts_path, "/") == 0 )
727			continue;
728		if( strcmp(ent->fts_path, ".") == 0 )
729			continue;
730
731		for( i = Exclude; i; i=i->next ) {
732			exclude_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0);
733			if( !exclude_match )
734				break;
735		}
736		if( !exclude_match ) {
737			if( Verbose )
738				printf("Excluding %s\n", ent->fts_path);
739			continue;
740		}
741
742		for( i = NoCompress; i; i=i->next ) {
743			nocompress_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0);
744			if( !nocompress_match ) {
745				xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE);
746				break;
747			}
748		}
749		f = xar_add(x, ent->fts_path);
750		if( !f ) {
751			fprintf(stderr, "Error adding file %s\n", ent->fts_path);
752		} else {
753			print_file(f);
754		}
755		if( !nocompress_match )
756			xar_opt_set(x, XAR_OPT_COMPRESSION, default_compression);
757	}
758	fts_close(fts);
759	if( xar_close(x) != 0 ) {
760		fprintf(stderr, "Error creating the archive\n");
761		if( !Err )
762			Err = 42;
763	}
764
765	free((char *)default_compression);
766	for( i = Exclude; i; ) {
767		struct lnode *tmp;
768		regfree(&i->reg);
769		tmp = i;
770		i = i->next;
771		free(tmp);
772	}
773	for( i = NoCompress; i; ) {
774		struct lnode *tmp;
775		regfree(&i->reg);
776		tmp = i;
777		i = i->next;
778		free(tmp);
779	}
780
781	if ( SigOffsetDumpPath ) {
782		x = xar_open(filename, READ);
783		if( !x ) {
784			fprintf(stderr, "Error re-opening archive %s\n", filename);
785			exit(1);
786		}
787		if (extract_sig_offset(x, SigOffsetDumpPath))
788			exit(1);
789	}
790
791	return Err;
792}
793
794static int extract(const char *filename, int arglen, char *args[]) {
795	xar_t x;
796	xar_iter_t i;
797	xar_file_t f;
798	int files_extracted = 0;
799
800	x = xar_open(filename, READ);
801	if( !x ) {
802		fprintf(stderr, "Error opening xar archive: %s\n", filename);
803		exit(1);
804	}
805
806	xar_register_errhandler(x, err_callback, NULL);
807
808	if( Perms == SYMBOLIC ) {
809		xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_SYMBOLIC);
810	}
811	if( Perms == NUMERIC ) {
812		xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_NUMERIC);
813	}
814	if ( Rsize != NULL ) {
815		xar_opt_set(x, XAR_OPT_RSIZE, Rsize);
816	}
817
818	i = xar_iter_new();
819	if( !i ) {
820		fprintf(stderr, "Error creating xar iterator\n");
821		exit(1);
822	}
823
824	for(f = xar_file_first(x, i); f; f = xar_file_next(i)) {
825		int matched = 0;
826		int exclude_match = 1;
827		struct lnode *i;
828
829		char *path = xar_get_path(f);
830
831		if( args[0] ) {
832			int i;
833			for(i = 0; args[i]; i++) {
834				if( strcmp(path, args[i]) == 0 ) {
835					matched = 1;
836					break;
837				}
838			}
839		} else {
840			matched = 1;
841		}
842
843		for( i = Exclude; i; i=i->next ) {
844			exclude_match = regexec(&i->reg, path, 0, NULL, 0);
845			if( !exclude_match )
846				break;
847		}
848		if( !exclude_match ) {
849			if( Verbose )
850				printf("Excluding %s\n", path);
851			free(path);
852			continue;
853		}
854
855		if( matched ) {
856			files_extracted++;
857			print_file(f);
858			xar_extract(x, f);
859		}
860		free(path);
861	}
862	if( args[0] && (files_extracted == 0) ) {
863		fprintf(stderr, "No files matched extraction criteria.\n");
864		Err = 3;
865	}
866
867	if( Subdoc )
868		extract_subdoc(x, NULL);
869
870	xar_iter_free(i);
871	if( xar_close(x) != 0 ) {
872		fprintf(stderr, "Error extracting the archive\n");
873		if( !Err )
874			Err = 42;
875	}
876	return Err;
877}
878
879static int list_subdocs(const char *filename) {
880	xar_t x;
881	xar_subdoc_t s;
882
883	x = xar_open(filename, READ);
884	if( !x ) {
885		fprintf(stderr, "Error opening xar archive: %s\n", filename);
886		exit(1);
887	}
888
889	for(s = xar_subdoc_first(x); s; s = xar_subdoc_next(s)) {
890		printf("%s\n", xar_subdoc_name(s));
891	}
892	xar_close(x);
893
894	return Err;
895}
896
897static int list(const char *filename, int arglen, char *args[]) {
898	xar_t x;
899	xar_iter_t i;
900	xar_file_t f;
901
902	x = xar_open(filename, READ);
903	if( !x ) {
904		fprintf(stderr, "Error opening xar archive: %s\n", filename);
905		exit(1);
906	}
907
908	i = xar_iter_new();
909	if( !i ) {
910		fprintf(stderr, "Error creating xar iterator\n");
911		exit(1);
912	}
913
914	for(f = xar_file_first(x, i); f; f = xar_file_next(i)) {
915		print_file(f);
916	}
917
918	xar_iter_free(i);
919	xar_close(x);
920
921	return Err;
922}
923
924static int dumptoc(const char *filename, const char* tocfile) {
925	xar_t x;
926	x = xar_open(filename, READ);
927	if( !x ) {
928		fprintf(stderr, "Error opening xar archive: %s\n", filename);
929		exit(1);
930	}
931
932	xar_serialize(x, tocfile);
933	xar_close(x);
934	return Err;
935}
936
937static int dump_header(const char *filename) {
938	int fd;
939	xar_header_t xh;
940
941	if(filename == NULL)
942		fd = 0;
943	else {
944		fd = open(filename, O_RDONLY);
945		if( fd < 0 ) {
946			perror("open");
947			exit(1);
948		}
949	}
950
951	if( read(fd, &xh, sizeof(xh)) < sizeof(xh) ) {
952		fprintf(stderr, "error reading header\n");
953		exit(1);
954	}
955
956	printf("magic:                  0x%x ", ntohl(xh.magic));
957	if( ntohl(xh.magic) != XAR_HEADER_MAGIC )
958		printf("(BAD)\n");
959	else
960		printf("(OK)\n");
961	printf("size:                   %d\n", ntohs(xh.size));
962	printf("version:                %d\n", ntohs(xh.version));
963	printf("Compressed TOC length:  %" PRId64 "\n", xar_ntoh64(xh.toc_length_compressed));
964	printf("Uncompressed TOC length: %" PRId64 "\n", xar_ntoh64(xh.toc_length_uncompressed));
965	printf("Checksum algorithm:     %d ", ntohl(xh.cksum_alg));
966	switch( ntohl(xh.cksum_alg) ) {
967	case XAR_CKSUM_NONE: printf("(none)\n");
968	                     break;
969	case XAR_CKSUM_SHA1: printf("(SHA1)\n");
970	                     break;
971	case XAR_CKSUM_SHA256: printf("(SHA256)\n");
972	                     break;
973	case XAR_CKSUM_SHA512: printf("(SHA512)\n");
974	                     break;
975	case XAR_CKSUM_MD5: printf("(MD5)\n");
976	                    break;
977	default: printf("(unknown)\n");
978	         break;
979	};
980
981	return 0;
982}
983
984static int32_t err_callback(int32_t sev, int32_t err, xar_errctx_t ctx, void *usrctx) {
985	xar_file_t f;
986	const char *str;
987	int e;
988
989	f = xar_err_get_file(ctx);
990	str = xar_err_get_string(ctx);
991	e = xar_err_get_errno(ctx);
992
993	switch(sev) {
994	case XAR_SEVERITY_DEBUG:
995	case XAR_SEVERITY_INFO:
996		break;
997	case XAR_SEVERITY_WARNING:
998		printf("%s\n", str);
999		break;
1000	case XAR_SEVERITY_NORMAL:
1001		if( (err = XAR_ERR_ARCHIVE_CREATION) && f )
1002    			print_file(f);
1003		break;
1004	case XAR_SEVERITY_NONFATAL:
1005	case XAR_SEVERITY_FATAL:
1006		Err = 2;
1007		printf("Error while ");
1008		if( err == XAR_ERR_ARCHIVE_CREATION ) printf("creating");
1009		if( err == XAR_ERR_ARCHIVE_EXTRACTION ) printf("extracting");
1010		printf(" archive");
1011		if( f ) {
1012			const char *file = xar_get_path(f);
1013			if( file ) printf(":(%s)", file);
1014			free((char *)file);
1015		}
1016		if( str ) printf(": %s", str);
1017		if( err ) printf(" (%s)", strerror(e));
1018		if( sev == XAR_SEVERITY_NONFATAL ) {
1019			printf(" - ignored");
1020			printf("\n");
1021		} else {
1022			printf("\n");
1023			exit(1);
1024		}
1025		break;
1026	}
1027	return 0;
1028}
1029
1030static void usage(const char *prog) {
1031	fprintf(stderr, "Usage: %s -[ctx][v] -f <archive> ...\n", prog);
1032	fprintf(stderr, "\t-c               Creates an archive\n");
1033	fprintf(stderr, "\t-x               Extracts an archive\n");
1034	fprintf(stderr, "\t-t               Lists an archive\n");
1035	fprintf(stderr, "\t--sign           Creates a placeholder signature and saves\n");
1036	fprintf(stderr, "\t                 the data to sign to disk. Works with -c or -f, requires\n");
1037	fprintf(stderr, "\t                 --sig-size, --leaf-cert-loc and \n");
1038	fprintf(stderr, "\t                 --intermediate-cert-loc to be set. Setting\n");
1039	fprintf(stderr, "\t                 --data-to-sign and --sig-offset is optional.\n");
1040	fprintf(stderr, "\t                 Fails with error code %i if the archive has already\n", E_SIGEXISTS);
1041	fprintf(stderr, "\t                 been signed.\n");
1042	fprintf(stderr, "\t--replace-sign   Rips out existing signature(s) and makes a new one.\n");
1043	fprintf(stderr, "\t                 Same required parameter set as --sign, \n");
1044	fprintf(stderr, "\t                 but -f instead of -c.\n");
1045	fprintf(stderr, "\t--extract-data-to-sign Extracts data to be signed from an\n");
1046	fprintf(stderr, "\t                 existing archive. Requires --data-to-sign\n");
1047	fprintf(stderr, "\t                 and --sig-offset (and -f) to be set.\n");
1048	fprintf(stderr, "\t--extract-certs <dir> Extracts all certificates into the\n");
1049	fprintf(stderr, "\t                 specified directory, naming them 'cert1', 'cert2' etc.\n");
1050	fprintf(stderr, "\t                 Requires -f.\n");
1051	fprintf(stderr, "\t--inject-sig <filename>     After extracting the data to be signed and\n");
1052	fprintf(stderr, "\t                 doing the signing externally, injects.\n");
1053	fprintf(stderr, "\t                 the signature. Requires -f\n");
1054	fprintf(stderr, "\t-f <filename>    Specifies an archive to operate on [REQUIRED!]\n");
1055	fprintf(stderr, "\t-v               Print filenames as they are archived\n");
1056	fprintf(stderr, "\t-n name          Provides a name for a subdocument\n");
1057	fprintf(stderr, "\t-s <filename>    On extract, specifies the file to extract\n");
1058	fprintf(stderr, "\t                      subdocuments to.\n");
1059	fprintf(stderr, "\t                 On archival, specifies an xml file to add\n");
1060	fprintf(stderr, "\t                      as a subdocument.\n");
1061	fprintf(stderr, "\t-l               On archival, stay on the local device.\n");
1062	fprintf(stderr, "\t-p               On extract, set ownership based on symbolic\n");
1063	fprintf(stderr, "\t                      names, if possible.\n");
1064	fprintf(stderr, "\t-P               On extract, set ownership based on uid/gid.\n");
1065	fprintf(stderr, "\t--toc-cksum      Specifies the hashing algorithm to use for\n");
1066	fprintf(stderr, "\t                      xml header verification.\n");
1067	fprintf(stderr, "\t                      Valid values: none, md5, sha1, sha256, and sha512\n");
1068	fprintf(stderr, "\t                      Default: sha1\n");
1069	fprintf(stderr, "\t--file-cksum     Specifies the hashing algorithm to use for\n");
1070	fprintf(stderr, "\t                      xml header verification.\n");
1071	fprintf(stderr, "\t                      Valid values: none, md5, sha1, sha256, and sha512\n");
1072	fprintf(stderr, "\t                      Default: sha1\n");
1073	fprintf(stderr, "\t--dump-toc=<filename> Has xar dump the xml header into the\n");
1074	fprintf(stderr, "\t                      specified file.\n");
1075	fprintf(stderr, "\t--dump-header    Prints out the xar binary header information\n");
1076	fprintf(stderr, "\t--compression    Specifies the compression type to use.\n");
1077	fprintf(stderr, "\t                      Valid values: none, gzip, bzip2\n");
1078	fprintf(stderr, "\t                      Default: gzip\n");
1079	fprintf(stderr, "\t--list-subdocs   List the subdocuments in the xml header\n");
1080	fprintf(stderr, "\t--extract-subdoc=name Extracts the specified subdocument\n");
1081	fprintf(stderr, "\t                      to a document in cwd named <name>.xml\n");
1082	fprintf(stderr, "\t--exclude        POSIX regular expression of files to \n");
1083	fprintf(stderr, "\t                      ignore while archiving.\n");
1084	fprintf(stderr, "\t--rsize          Specifies the size of the buffer used\n");
1085	fprintf(stderr, "\t                      for read IO operations in bytes.\n");
1086	fprintf(stderr, "\t--coalesce-heap  When archived files are identical, only store one copy\n");
1087	fprintf(stderr, "\t                      This option creates an archive which\n");
1088	fprintf(stderr, "\t                      is not streamable\n");
1089	fprintf(stderr, "\t--link-same      Hardlink identical files\n");
1090	fprintf(stderr, "\t--no-compress    POSIX regular expression of files\n");
1091	fprintf(stderr, "\t                      not to archive, but not compress.\n");
1092	fprintf(stderr, "\t--sig-size n     Size (in bytes) of the signature placeholder\n");
1093	fprintf(stderr, "\t                      to generate.\n");
1094	fprintf(stderr, "\t--data-to-sign=file   Path where to dump the data to be signed.\n");
1095	fprintf(stderr, "\t--sig-offset=file     Path where to dump the signature's offset\n");
1096	fprintf(stderr, "\t                      within the xar.\n");
1097	fprintf(stderr, "\t--leaf-cert-loc=file         Location of the leaf certificate\n");
1098	fprintf(stderr, "\t--intermediate-cert-loc=file Location of the intermediate certificate\n");
1099	fprintf(stderr, "\t--root-cert-loc=file         Location of the root certificate\n");
1100	fprintf(stderr, "\t--version        Print xar's version number\n");
1101
1102	return;
1103}
1104
1105static void print_version() {
1106	printf("xar %s\n", XAR_VERSION);
1107}
1108
1109int main(int argc, char *argv[]) {
1110	char *filename = NULL;
1111	char *sig_path = NULL;
1112	char *cert_path = NULL;
1113	char command = 0, c;
1114	char **args;
1115	const char *tocfile = NULL;
1116	int arglen, i, err;
1117	xar_t x;
1118	int loptind = 0;
1119	int required_dash_f = 0;  /* This release requires us to use -f */
1120	struct lnode *tmp;
1121	long int longtmp;
1122	struct stat stat_struct;
1123	struct option o[] = {
1124		{"toc-cksum", 1, 0, 1},
1125		{"file-cksum", 1, 0, 22}, // note out-of-order argument number
1126		{"dump-toc", 1, 0, 'd'},
1127		{"compression", 1, 0, 2},
1128		{"list-subdocs", 0, 0, 3},
1129		{"help", 0, 0, 'h'},
1130		{"version", 0, 0, 4},
1131		{"dump-header", 0, 0, 5},
1132		{"extract-subdoc", 1, 0, 6},
1133		{"exclude", 1, 0, 7},
1134		{"rsize", 1, 0, 8},
1135		{"coalesce-heap", 0, 0, 9},
1136		{"link-same", 0, 0, 10},
1137		{"no-compress", 1, 0, 11},
1138		{"sig-size", 1, 0, 12},
1139		{"data-to-sign", 1, 0, 13},
1140		{"sig-offset", 1, 0, 14},
1141		{"leaf-cert-loc", 1, 0, 15},
1142		{"intermediate-cert-loc", 1, 0, 16},
1143		{"extract-data-to-sign", 0, 0, 17},
1144		{"sign", 0, 0, 18},
1145		{"replace-sign", 0, 0, 19},
1146		{"inject-sig", 1, 0, 20},
1147		{"extract-certs", 1, 0, 21},
1148		{"root-cert-loc", 1, 0, 23},
1149		{ 0, 0, 0, 0}
1150	};
1151
1152	if( argc < 2 ) {
1153		usage(argv[0]);
1154		exit(1);
1155	}
1156
1157	while( (c = getopt_long(argc, argv, "xcvtf:hpPln:s:d:v", o, &loptind)) != -1 ) {
1158		switch(c) {
1159		case  1 : if( !optarg ) {
1160		          	usage(argv[0]);
1161		          	exit(1);
1162		          }
1163		          if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) &&
1164		              (strcmp(optarg, XAR_OPT_VAL_SHA1) != 0) &&
1165		              (strcmp(optarg, XAR_OPT_VAL_SHA256) != 0) &&
1166		              (strcmp(optarg, XAR_OPT_VAL_SHA512) != 0) &&
1167		              (strcmp(optarg, XAR_OPT_VAL_MD5)  != 0) ) {
1168		          	usage(argv[0]);
1169		          	exit(1);
1170		          }
1171		          Toccksum = optarg;
1172		          break;
1173		case  22: if( !optarg ) {
1174		            usage(argv[0]);
1175		            exit(1);
1176				  }
1177		          if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) &&
1178		            (strcmp(optarg, XAR_OPT_VAL_SHA1) != 0) &&
1179		            (strcmp(optarg, XAR_OPT_VAL_SHA256) != 0) &&
1180		            (strcmp(optarg, XAR_OPT_VAL_SHA512) != 0) &&
1181		            (strcmp(optarg, XAR_OPT_VAL_MD5)  != 0) ) {
1182				  usage(argv[0]);
1183				  exit(1);
1184				  }
1185				  Filecksum = optarg;
1186				  break;
1187		case  2 : if( !optarg ) {
1188		          	usage(argv[0]);
1189		          	exit(1);
1190		          }
1191		          if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) &&
1192		              (strcmp(optarg, XAR_OPT_VAL_GZIP) != 0) &&
1193		              (strcmp(optarg, XAR_OPT_VAL_BZIP) != 0) ) {
1194		          	usage(argv[0]);
1195		          	exit(1);
1196		          }
1197		          Compression = optarg;
1198		          break;
1199		case  3 : if( command && (command != 3) ) {
1200		          	fprintf(stderr, "Conflicting commands specified\n");
1201				exit(1);
1202		          }
1203			  command = 3;
1204			  break;
1205		case  4 : print_version();
1206		          exit(0);
1207		case 'd':
1208			if( !optarg ) {
1209				usage(argv[0]);
1210				exit(1);
1211			}
1212			tocfile = optarg;
1213			command = 'd';
1214			break;
1215		case  5 : command = 5;
1216		          break;
1217		case  6 :
1218			SubdocName = optarg;
1219			asprintf(&Subdoc, "%s.xml", SubdocName);
1220			if( !command )
1221				command = 6;
1222			break;
1223		case  7 :
1224			tmp = malloc(sizeof(struct lnode));
1225			tmp->str = optarg;
1226			tmp->next = NULL;
1227			err = regcomp(&tmp->reg, tmp->str, REG_NOSUB);
1228			if( err ) {
1229				char errstr[1024];
1230				regerror(err, &tmp->reg, errstr, sizeof(errstr));
1231				printf("Error with regular expression %s: %s\n", tmp->str, errstr);
1232				exit(1);
1233			}
1234			if( Exclude == NULL ) {
1235				Exclude = tmp;
1236				Exclude_Tail = tmp;
1237			} else {
1238				Exclude_Tail->next = tmp;
1239				Exclude_Tail = tmp;
1240			}
1241			break;
1242		case  8 :
1243			if ( !optarg ) {
1244				usage(argv[0]);
1245				exit(1);
1246			}
1247			longtmp = strtol(optarg, NULL, 10);
1248			if( (((longtmp == LONG_MIN) || (longtmp == LONG_MAX)) && (errno == ERANGE)) || (longtmp < 1) ) {
1249				fprintf(stderr, "Invalid rsize value: %s\n", optarg);
1250				exit(5);
1251			}
1252			Rsize = optarg;
1253			break;
1254		case  9 : Coalesce = 1; break;
1255		case 10 : LinkSame = 1; break;
1256		case 11 :
1257			tmp = malloc(sizeof(struct lnode));
1258			tmp->str = optarg;
1259			tmp->next = NULL;
1260			err = regcomp(&tmp->reg, tmp->str, REG_NOSUB);
1261			if( err ) {
1262				char errstr[1024];
1263				regerror(err, &tmp->reg, errstr, sizeof(errstr));
1264				printf("Error with regular expression %s: %s\n", tmp->str, errstr);
1265				exit(1);
1266			}
1267			if( NoCompress == NULL ) {
1268				NoCompress = tmp;
1269				NoCompress_Tail = tmp;
1270			} else {
1271				NoCompress_Tail->next = tmp;
1272				NoCompress_Tail = tmp;
1273			}
1274			break;
1275		case  12 :
1276			if( !optarg ) {
1277				usage(argv[0]);
1278				exit(1);
1279			}
1280			SigSize = strtol(optarg, (char **)NULL, 10);
1281			break;
1282		case  13 :
1283			if( !optarg ) {
1284				usage(argv[0]);
1285				exit(1);
1286			}
1287			DataToSignDumpPath = optarg;
1288			break;
1289		case  14 :
1290			if( !optarg ) {
1291				usage(argv[0]);
1292				exit(1);
1293			}
1294			SigOffsetDumpPath = optarg;
1295			break;
1296		case  15 :
1297			if( !optarg ) {
1298				usage(argv[0]);
1299				exit(1);
1300			}
1301			LeafCertPath = optarg;
1302			break;
1303		case  16 :
1304			if( !optarg ) {
1305				usage(argv[0]);
1306				exit(1);
1307			}
1308			IntermediateCertPath = optarg;
1309			break;
1310		case 17 :	// extract-data-to-sign
1311			command = 'e';
1312			break;
1313		case 18 :	// sign
1314			DoSign = 1;
1315			break;
1316		case 19 :	// replace-sign
1317			command = 'r';
1318			break;
1319		case 20 :	// inject signature
1320			if( !optarg ) {
1321				usage(argv[0]);
1322				exit(1);
1323			}
1324			sig_path = optarg;
1325			command = 'i';
1326			break;
1327		case 21 :	// extract-certs
1328			if( !optarg ) {
1329				usage(argv[0]);
1330				exit(1);
1331			}
1332			cert_path = optarg;
1333			stat(cert_path, &stat_struct);
1334			if (!(stat_struct.st_mode & S_IFDIR)) {
1335				usage(argv[0]);
1336				fprintf(stderr, "%s is not a directory.\n", cert_path);
1337				exit(1);
1338			}
1339			command = 'j';
1340			break;
1341		case 23 :
1342			if( !optarg ) {
1343				usage(argv[0]);
1344				exit(1);
1345			}
1346			RootCertPath = optarg;
1347			break;
1348		case 'c':
1349		case 'x':
1350		case 't':
1351			if( command && (command != 's') ) {
1352				usage(argv[0]);
1353				fprintf(stderr, "Conflicting command flags: %c and %c specified\n", c, command);
1354				exit(1);
1355			}
1356			if( c == 't' )
1357				Verbose++;
1358			command = c;
1359			break;
1360		case 'f':
1361			required_dash_f = 1;
1362			filename = optarg;
1363			break;
1364		case 'p':
1365			Perms = SYMBOLIC;
1366			break;
1367		case 'P':
1368			Perms = NUMERIC;
1369			break;
1370		case 'l':
1371			Local = 1;
1372			break;
1373		case 'n':
1374			SubdocName = optarg;
1375			break;
1376		case 's':
1377			Subdoc = optarg;
1378			if( !command )
1379				command = 's';
1380			break;
1381		case 'v':
1382			Verbose++;
1383			break;
1384		case 'h':
1385		default:
1386			usage(argv[0]);
1387			exit(1);
1388		}
1389	}
1390
1391	if (! required_dash_f)	{
1392		usage(argv[0]);
1393		fprintf(stderr, "\n -f option is REQUIRED\n");
1394		exit(1);
1395	}
1396
1397	// extract-data-to-sign
1398	if ( (command == 'e') && ((!filename) || (!DataToSignDumpPath) || (!SigOffsetDumpPath)) ) {
1399		usage(argv[0]);
1400		exit(1);
1401	}
1402
1403	if ( DoSign ) { // XXX: should we require RootCertPath as well?
1404		if (  ( !SigSize || !LeafCertPath || !IntermediateCertPath )
1405			  || ((command != 'c') && (!filename)) ) {
1406			usage(argv[0]);
1407			exit(1);
1408		}
1409		if (!command)
1410			command = 'n';
1411	}
1412
1413	if (command == 'r') {
1414		/*if ( !SigSize || !LeafCertPath || !IntermediateCertPath || !filename) {
1415			usage(argv[0]);
1416			exit(1);
1417		}
1418		xar_t x = xar_open(filename, READ);
1419		if ( x == NULL ) {
1420			fprintf(stderr, "Could not open archive %s!\n", filename);
1421			exit(1);
1422		}
1423		xar_signature_t sig = xar_signature_first(x);
1424		if ( !sig ) {
1425			fprintf(stderr, "No signature found to replace.\n");
1426			exit(E_NOSIG);
1427		}
1428		xar_close(x);*/
1429	}
1430
1431	if ((command == 'i') && ((!filename) || (!sig_path))) {
1432		usage(argv[0]);
1433		exit(1);
1434	}
1435
1436	if ((command == 'j') && (!filename)) {
1437		usage(argv[0]);
1438		exit(1);
1439	}
1440
1441	switch(command) {
1442		case  5 :
1443		        return dump_header(filename);
1444		case  3 :
1445			return list_subdocs(filename);
1446		case 'c':
1447			if( optind == argc ) {
1448				usage(argv[0]);
1449				fprintf(stderr, "No files to operate on.\n");
1450				exit(1);
1451			}
1452			arglen = argc - optind;
1453			args = malloc(sizeof(char*) * (arglen+1));
1454			memset(args, 0, sizeof(char*) * (arglen+1));
1455			for( i = 0; i < arglen; i++ )
1456				args[i] = strdup(argv[optind + i]);
1457
1458			return archive(filename, arglen, args);
1459		case 'd':
1460			if( !tocfile ) {
1461				usage(argv[0]);
1462				exit(1);
1463			}
1464			return dumptoc(filename, tocfile);
1465		case 'x':
1466			arglen = argc - optind;
1467			args = malloc(sizeof(char*) * (arglen+1));
1468			for( i = 0; i < arglen; i++ )
1469				args[i] = strdup(argv[optind + i]);
1470			args[i] = NULL;
1471			return extract(filename, arglen, args);
1472		case 't':
1473			arglen = argc - optind;
1474			args = malloc(sizeof(char*) * (arglen+1));
1475			for( i = 0; i < arglen; i++ )
1476				args[i] = strdup(argv[optind + i]);
1477			return list(filename, arglen, args);
1478		case  6 :
1479		case 's':
1480			x = xar_open(filename, READ);
1481			if( !x ) {
1482				fprintf(stderr, "Error opening xar archive: %s\n", filename);
1483				exit(1);
1484			}
1485			xar_register_errhandler(x, err_callback, NULL);
1486			extract_subdoc(x, SubdocName);
1487			xar_close(x);
1488			exit(Err);
1489			break;
1490		case 'e':
1491			extract_data_to_sign(filename);
1492			exit(Err);
1493		case 'r':
1494			replace_sign(filename);
1495			exit(Err);
1496		case 'i':
1497			inject_signature(filename, sig_path);
1498			exit(Err);
1499		case 'n':
1500			belated_sign(filename);
1501			exit(Err);
1502		case 'j':
1503			extract_certs(filename, cert_path);
1504			exit(Err);
1505		default:
1506			usage(argv[0]);
1507			fprintf(stderr, "Unrecognized command.\n");
1508			exit(1);
1509	}
1510
1511	/* unreached */
1512	exit(0);
1513}
1514