1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 /*
6 * SIGNTOOL
7 *
8 * A command line tool to create manifest files
9 * from a directory hierarchy. It is assumed that
10 * the tree will be equivalent to what resides
11 * or will reside in an archive.
12 *
13 *
14 */
15
16 #include "nss.h"
17 #include "signtool.h"
18 #include "prmem.h"
19 #include "prio.h"
20
21 /***********************************************************************
22 * Global Variable Definitions
23 */
24 char *progName; /* argv[0] */
25
26 /* password data */
27 secuPWData pwdata = { PW_NONE, 0 };
28
29 /* directories or files to exclude in descent */
30 PLHashTable *excludeDirs = NULL;
31 static PRBool exclusionsGiven = PR_FALSE;
32
33 /* zatharus is the man who knows no time, dies tragic death */
34 int no_time = 0;
35
36 /* -b basename of .rsa, .sf files */
37 char *base = DEFAULT_BASE_NAME;
38
39 /* Only sign files with this extension */
40 PLHashTable *extensions = NULL;
41 PRBool extensionsGiven = PR_FALSE;
42
43 char *scriptdir = NULL;
44
45 int verbosity = 0;
46
47 PRFileDesc *outputFD = NULL, *errorFD = NULL;
48
49 int errorCount = 0, warningCount = 0;
50
51 int compression_level = DEFAULT_COMPRESSION_LEVEL;
52 PRBool compression_level_specified = PR_FALSE;
53
54 int xpi_arc = 0;
55
56 /* Command-line arguments */
57 static char *genkey = NULL;
58 static char *verify = NULL;
59 static char *zipfile = NULL;
60 static char *cert_dir = NULL;
61 static int javascript = 0;
62 static char *jartree = NULL;
63 static char *keyName = NULL;
64 static char *metafile = NULL;
65 static char *install_script = NULL;
66 static int list_certs = 0;
67 static int list_modules = 0;
68 static int optimize = 0;
69 static int enableOCSP = 0;
70 static char *tell_who = NULL;
71 static char *outfile = NULL;
72 static char *cmdFile = NULL;
73 static PRBool noRecurse = PR_FALSE;
74 static PRBool leaveArc = PR_FALSE;
75 static int keySize = -1;
76 static char *token = NULL;
77
78 typedef enum {
79 UNKNOWN_OPT,
80 HELP_OPT,
81 LONG_HELP_OPT,
82 BASE_OPT,
83 COMPRESSION_OPT,
84 CERT_DIR_OPT,
85 EXTENSION_OPT,
86 INSTALL_SCRIPT_OPT,
87 SCRIPTDIR_OPT,
88 CERTNAME_OPT,
89 LIST_OBJSIGN_CERTS_OPT,
90 LIST_ALL_CERTS_OPT,
91 METAFILE_OPT,
92 OPTIMIZE_OPT,
93 ENABLE_OCSP_OPT,
94 PASSWORD_OPT,
95 VERIFY_OPT,
96 WHO_OPT,
97 EXCLUDE_OPT,
98 NO_TIME_OPT,
99 JAVASCRIPT_OPT,
100 ZIPFILE_OPT,
101 GENKEY_OPT,
102 MODULES_OPT,
103 NORECURSE_OPT,
104 SIGNDIR_OPT,
105 OUTFILE_OPT,
106 COMMAND_FILE_OPT,
107 LEAVE_ARC_OPT,
108 VERBOSITY_OPT,
109 KEYSIZE_OPT,
110 TOKEN_OPT,
111 XPI_ARC_OPT
112 }
113
114 OPT_TYPE;
115
116 typedef enum {
117 DUPLICATE_OPTION_ERR = 0,
118 OPTION_NEEDS_ARG_ERR
119 }
120
121 Error;
122
123 static char *errStrings[] = {
124 "warning: %s option specified more than once.\n"
125 "Only last specification will be used.\n",
126 "ERROR: option \"%s\" requires an argument.\n"
127 };
128
129 static int ProcessOneOpt(OPT_TYPE type, char *arg);
130
131 /*********************************************************************
132 *
133 * P r o c e s s C o m m a n d F i l e
134 */
135 int
ProcessCommandFile()136 ProcessCommandFile()
137 {
138 PRFileDesc *fd;
139 #define CMD_FILE_BUFSIZE 1024
140 char buf[CMD_FILE_BUFSIZE];
141 char *equals;
142 int linenum = 0;
143 int retval = -1;
144 OPT_TYPE type;
145
146 fd = PR_Open(cmdFile, PR_RDONLY, 0777);
147 if (!fd) {
148 PR_fprintf(errorFD, "ERROR: Unable to open command file %s.\n");
149 errorCount++;
150 return -1;
151 }
152
153 while (pr_fgets(buf, CMD_FILE_BUFSIZE, fd)) {
154 char *eol;
155 linenum++;
156
157 /* Chop off final newline */
158 eol = PL_strchr(buf, '\r');
159 if (!eol) {
160 eol = PL_strchr(buf, '\n');
161 }
162 if (eol)
163 *eol = '\0';
164
165 equals = PL_strchr(buf, '=');
166 if (!equals) {
167 continue;
168 }
169
170 *equals = '\0';
171 equals++;
172
173 /* Now buf points to the attribute, and equals points to the value. */
174
175 /* This is pretty straightforward, just deal with whatever attribute
176 * this is */
177 if (!PL_strcasecmp(buf, "basename")) {
178 type = BASE_OPT;
179 } else if (!PL_strcasecmp(buf, "compression")) {
180 type = COMPRESSION_OPT;
181 } else if (!PL_strcasecmp(buf, "certdir")) {
182 type = CERT_DIR_OPT;
183 } else if (!PL_strcasecmp(buf, "extension")) {
184 type = EXTENSION_OPT;
185 } else if (!PL_strcasecmp(buf, "generate")) {
186 type = GENKEY_OPT;
187 } else if (!PL_strcasecmp(buf, "installScript")) {
188 type = INSTALL_SCRIPT_OPT;
189 } else if (!PL_strcasecmp(buf, "javascriptdir")) {
190 type = SCRIPTDIR_OPT;
191 } else if (!PL_strcasecmp(buf, "htmldir")) {
192 type = JAVASCRIPT_OPT;
193 if (jartree) {
194 PR_fprintf(errorFD,
195 "warning: directory to be signed specified more than once."
196 " Only last specification will be used.\n");
197 warningCount++;
198 PR_Free(jartree);
199 jartree = NULL;
200 }
201 jartree = PL_strdup(equals);
202 } else if (!PL_strcasecmp(buf, "certname")) {
203 type = CERTNAME_OPT;
204 } else if (!PL_strcasecmp(buf, "signdir")) {
205 type = SIGNDIR_OPT;
206 } else if (!PL_strcasecmp(buf, "list")) {
207 type = LIST_OBJSIGN_CERTS_OPT;
208 } else if (!PL_strcasecmp(buf, "listall")) {
209 type = LIST_ALL_CERTS_OPT;
210 } else if (!PL_strcasecmp(buf, "metafile")) {
211 type = METAFILE_OPT;
212 } else if (!PL_strcasecmp(buf, "modules")) {
213 type = MODULES_OPT;
214 } else if (!PL_strcasecmp(buf, "optimize")) {
215 type = OPTIMIZE_OPT;
216 } else if (!PL_strcasecmp(buf, "ocsp")) {
217 type = ENABLE_OCSP_OPT;
218 } else if (!PL_strcasecmp(buf, "password")) {
219 type = PASSWORD_OPT;
220 } else if (!PL_strcasecmp(buf, "verify")) {
221 type = VERIFY_OPT;
222 } else if (!PL_strcasecmp(buf, "who")) {
223 type = WHO_OPT;
224 } else if (!PL_strcasecmp(buf, "exclude")) {
225 type = EXCLUDE_OPT;
226 } else if (!PL_strcasecmp(buf, "notime")) {
227 type = NO_TIME_OPT;
228 } else if (!PL_strcasecmp(buf, "jarfile")) {
229 type = ZIPFILE_OPT;
230 } else if (!PL_strcasecmp(buf, "outfile")) {
231 type = OUTFILE_OPT;
232 } else if (!PL_strcasecmp(buf, "leavearc")) {
233 type = LEAVE_ARC_OPT;
234 } else if (!PL_strcasecmp(buf, "verbosity")) {
235 type = VERBOSITY_OPT;
236 } else if (!PL_strcasecmp(buf, "keysize")) {
237 type = KEYSIZE_OPT;
238 } else if (!PL_strcasecmp(buf, "token")) {
239 type = TOKEN_OPT;
240 } else if (!PL_strcasecmp(buf, "xpi")) {
241 type = XPI_ARC_OPT;
242 } else {
243 PR_fprintf(errorFD,
244 "warning: unknown attribute \"%s\" in command file, line %d.\n",
245 buf, linenum);
246 warningCount++;
247 type = UNKNOWN_OPT;
248 }
249
250 /* Process the option, whatever it is */
251 if (type != UNKNOWN_OPT) {
252 if (ProcessOneOpt(type, equals) == -1) {
253 goto finish;
254 }
255 }
256 }
257
258 retval = 0;
259
260 finish:
261 PR_Close(fd);
262 return retval;
263 }
264
265 /*********************************************************************
266 *
267 * p a r s e _ a r g s
268 */
269 static int
parse_args(int argc,char * argv[])270 parse_args(int argc, char *argv[])
271 {
272 char *opt;
273 char *arg;
274 int needsInc = 0;
275 int i;
276 OPT_TYPE type;
277
278 /* Loop over all arguments */
279 for (i = 1; i < argc; i++) {
280 opt = argv[i];
281 arg = NULL;
282
283 if (opt[0] == '-') {
284 if (opt[1] == '-') {
285 /* word option */
286 if (i < argc - 1) {
287 needsInc = 1;
288 arg = argv[i + 1];
289 } else {
290 arg = NULL;
291 }
292
293 if (!PL_strcasecmp(opt + 2, "norecurse")) {
294 type = NORECURSE_OPT;
295 } else if (!PL_strcasecmp(opt + 2, "leavearc")) {
296 type = LEAVE_ARC_OPT;
297 } else if (!PL_strcasecmp(opt + 2, "verbosity")) {
298 type = VERBOSITY_OPT;
299 } else if (!PL_strcasecmp(opt + 2, "outfile")) {
300 type = OUTFILE_OPT;
301 } else if (!PL_strcasecmp(opt + 2, "keysize")) {
302 type = KEYSIZE_OPT;
303 } else if (!PL_strcasecmp(opt + 2, "token")) {
304 type = TOKEN_OPT;
305 } else {
306 PR_fprintf(errorFD, "warning: unknown option: %s\n",
307 opt);
308 warningCount++;
309 type = UNKNOWN_OPT;
310 }
311 } else {
312 /* char option */
313 if (opt[2] != '\0') {
314 arg = opt + 2;
315 } else if (i < argc - 1) {
316 needsInc = 1;
317 arg = argv[i + 1];
318 } else {
319 arg = NULL;
320 }
321
322 switch (opt[1]) {
323 case 'b':
324 type = BASE_OPT;
325 break;
326 case 'c':
327 type = COMPRESSION_OPT;
328 break;
329 case 'd':
330 type = CERT_DIR_OPT;
331 break;
332 case 'e':
333 type = EXTENSION_OPT;
334 break;
335 case 'f':
336 type = COMMAND_FILE_OPT;
337 break;
338 case 'h':
339 type = HELP_OPT;
340 break;
341 case 'H':
342 type = LONG_HELP_OPT;
343 break;
344 case 'i':
345 type = INSTALL_SCRIPT_OPT;
346 break;
347 case 'j':
348 type = SCRIPTDIR_OPT;
349 break;
350 case 'k':
351 type = CERTNAME_OPT;
352 break;
353 case 'l':
354 type = LIST_OBJSIGN_CERTS_OPT;
355 break;
356 case 'L':
357 type = LIST_ALL_CERTS_OPT;
358 break;
359 case 'm':
360 type = METAFILE_OPT;
361 break;
362 case 'o':
363 type = OPTIMIZE_OPT;
364 break;
365 case 'O':
366 type = ENABLE_OCSP_OPT;
367 break;
368 case 'p':
369 type = PASSWORD_OPT;
370 break;
371 case 'v':
372 type = VERIFY_OPT;
373 break;
374 case 'w':
375 type = WHO_OPT;
376 break;
377 case 'x':
378 type = EXCLUDE_OPT;
379 break;
380 case 'X':
381 type = XPI_ARC_OPT;
382 break;
383 case 'z':
384 type = NO_TIME_OPT;
385 break;
386 case 'J':
387 type = JAVASCRIPT_OPT;
388 break;
389 case 'Z':
390 type = ZIPFILE_OPT;
391 break;
392 case 'G':
393 type = GENKEY_OPT;
394 break;
395 case 'M':
396 type = MODULES_OPT;
397 break;
398 case 's':
399 type = KEYSIZE_OPT;
400 break;
401 case 't':
402 type = TOKEN_OPT;
403 break;
404 default:
405 type = UNKNOWN_OPT;
406 PR_fprintf(errorFD, "warning: unrecognized option: -%c.\n",
407 opt[1]);
408 warningCount++;
409 break;
410 }
411 }
412 } else {
413 type = UNKNOWN_OPT;
414 if (i == argc - 1) {
415 if (jartree) {
416 PR_fprintf(errorFD,
417 "warning: directory to be signed specified more than once.\n"
418 " Only last specification will be used.\n");
419 warningCount++;
420 PR_Free(jartree);
421 jartree = NULL;
422 }
423 jartree = PL_strdup(opt);
424 } else {
425 PR_fprintf(errorFD, "warning: unrecognized option: %s\n", opt);
426 warningCount++;
427 }
428 }
429
430 if (type != UNKNOWN_OPT) {
431 short ateArg = ProcessOneOpt(type, arg);
432 if (ateArg == -1) {
433 /* error */
434 return -1;
435 }
436 if (ateArg && needsInc) {
437 i++;
438 }
439 }
440 }
441
442 return 0;
443 }
444
445 /*********************************************************************
446 *
447 * P r o c e s s O n e O p t
448 *
449 * Since options can come from different places (command file, word options,
450 * char options), this is a central function that is called to deal with
451 * them no matter where they come from.
452 *
453 * type is the type of option.
454 * arg is the argument to the option, possibly NULL.
455 * Returns 1 if the argument was eaten, 0 if it wasn't, and -1 for error.
456 */
457 static int
ProcessOneOpt(OPT_TYPE type,char * arg)458 ProcessOneOpt(OPT_TYPE type, char *arg)
459 {
460 int ate = 0;
461
462 switch (type) {
463 case HELP_OPT:
464 Usage();
465 break;
466 case LONG_HELP_OPT:
467 LongUsage();
468 break;
469 case BASE_OPT:
470 if (base) {
471 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-b");
472 warningCount++;
473 PR_Free(base);
474 base = NULL;
475 }
476 if (!arg) {
477 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-b");
478 errorCount++;
479 goto loser;
480 }
481 base = PL_strdup(arg);
482 ate = 1;
483 break;
484 case COMPRESSION_OPT:
485 if (compression_level_specified) {
486 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-c");
487 warningCount++;
488 }
489 if (!arg) {
490 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-c");
491 errorCount++;
492 goto loser;
493 }
494 compression_level = atoi(arg);
495 compression_level_specified = PR_TRUE;
496 ate = 1;
497 break;
498 case CERT_DIR_OPT:
499 if (cert_dir) {
500 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-d");
501 warningCount++;
502 PR_Free(cert_dir);
503 cert_dir = NULL;
504 }
505 if (!arg) {
506 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-d");
507 errorCount++;
508 goto loser;
509 }
510 cert_dir = PL_strdup(arg);
511 ate = 1;
512 break;
513 case EXTENSION_OPT:
514 if (!arg) {
515 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
516 "extension (-e)");
517 errorCount++;
518 goto loser;
519 }
520 PL_HashTableAdd(extensions, arg, arg);
521 extensionsGiven = PR_TRUE;
522 ate = 1;
523 break;
524 case INSTALL_SCRIPT_OPT:
525 if (install_script) {
526 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
527 "installScript (-i)");
528 warningCount++;
529 PR_Free(install_script);
530 install_script = NULL;
531 }
532 if (!arg) {
533 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
534 "installScript (-i)");
535 errorCount++;
536 goto loser;
537 }
538 install_script = PL_strdup(arg);
539 ate = 1;
540 break;
541 case SCRIPTDIR_OPT:
542 if (scriptdir) {
543 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
544 "javascriptdir (-j)");
545 warningCount++;
546 PR_Free(scriptdir);
547 scriptdir = NULL;
548 }
549 if (!arg) {
550 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
551 "javascriptdir (-j)");
552 errorCount++;
553 goto loser;
554 }
555 scriptdir = PL_strdup(arg);
556 ate = 1;
557 break;
558 case CERTNAME_OPT:
559 if (keyName) {
560 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
561 "keyName (-k)");
562 warningCount++;
563 PR_Free(keyName);
564 keyName = NULL;
565 }
566 if (!arg) {
567 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
568 "keyName (-k)");
569 errorCount++;
570 goto loser;
571 }
572 keyName = PL_strdup(arg);
573 ate = 1;
574 break;
575 case LIST_OBJSIGN_CERTS_OPT:
576 case LIST_ALL_CERTS_OPT:
577 if (list_certs != 0) {
578 PR_fprintf(errorFD,
579 "warning: only one of -l and -L may be specified.\n");
580 warningCount++;
581 }
582 list_certs = (type == LIST_OBJSIGN_CERTS_OPT ? 1 : 2);
583 break;
584 case METAFILE_OPT:
585 if (metafile) {
586 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
587 "metafile (-m)");
588 warningCount++;
589 PR_Free(metafile);
590 metafile = NULL;
591 }
592 if (!arg) {
593 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
594 "metafile (-m)");
595 errorCount++;
596 goto loser;
597 }
598 metafile = PL_strdup(arg);
599 ate = 1;
600 break;
601 case OPTIMIZE_OPT:
602 optimize = 1;
603 break;
604 case ENABLE_OCSP_OPT:
605 enableOCSP = 1;
606 break;
607 case PASSWORD_OPT:
608 if (pwdata.data) {
609 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
610 "password (-p)");
611 warningCount++;
612 PR_Free(pwdata.data);
613 pwdata.data = NULL;
614 }
615 if (!arg) {
616 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
617 "password (-p)");
618 errorCount++;
619 goto loser;
620 }
621 pwdata.source = PW_PLAINTEXT;
622 pwdata.data = PL_strdup(arg);
623 ate = 1;
624 break;
625 case VERIFY_OPT:
626 if (verify) {
627 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
628 "verify (-v)");
629 warningCount++;
630 PR_Free(verify);
631 verify = NULL;
632 }
633 if (!arg) {
634 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
635 "verify (-v)");
636 errorCount++;
637 goto loser;
638 }
639 verify = PL_strdup(arg);
640 ate = 1;
641 break;
642 case WHO_OPT:
643 if (tell_who) {
644 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
645 "who (-v)");
646 warningCount++;
647 PR_Free(tell_who);
648 tell_who = NULL;
649 }
650 if (!arg) {
651 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
652 "who (-w)");
653 errorCount++;
654 goto loser;
655 }
656 tell_who = PL_strdup(arg);
657 ate = 1;
658 break;
659 case EXCLUDE_OPT:
660 if (!arg) {
661 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
662 "exclude (-x)");
663 errorCount++;
664 goto loser;
665 }
666 PL_HashTableAdd(excludeDirs, arg, arg);
667 exclusionsGiven = PR_TRUE;
668 ate = 1;
669 break;
670 case NO_TIME_OPT:
671 no_time = 1;
672 break;
673 case JAVASCRIPT_OPT:
674 javascript++;
675 break;
676 case ZIPFILE_OPT:
677 if (zipfile) {
678 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
679 "jarfile (-Z)");
680 warningCount++;
681 PR_Free(zipfile);
682 zipfile = NULL;
683 }
684 if (!arg) {
685 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
686 "jarfile (-Z)");
687 errorCount++;
688 goto loser;
689 }
690 zipfile = PL_strdup(arg);
691 ate = 1;
692 break;
693 case GENKEY_OPT:
694 if (genkey) {
695 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
696 "generate (-G)");
697 warningCount++;
698 PR_Free(genkey);
699 genkey = NULL;
700 }
701 if (!arg) {
702 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
703 "generate (-G)");
704 errorCount++;
705 goto loser;
706 }
707 genkey = PL_strdup(arg);
708 ate = 1;
709 break;
710 case MODULES_OPT:
711 list_modules++;
712 break;
713 case SIGNDIR_OPT:
714 if (jartree) {
715 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
716 "signdir");
717 warningCount++;
718 PR_Free(jartree);
719 jartree = NULL;
720 }
721 if (!arg) {
722 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
723 "signdir");
724 errorCount++;
725 goto loser;
726 }
727 jartree = PL_strdup(arg);
728 ate = 1;
729 break;
730 case OUTFILE_OPT:
731 if (outfile) {
732 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
733 "outfile");
734 warningCount++;
735 PR_Free(outfile);
736 outfile = NULL;
737 }
738 if (!arg) {
739 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
740 "outfile");
741 errorCount++;
742 goto loser;
743 }
744 outfile = PL_strdup(arg);
745 ate = 1;
746 break;
747 case COMMAND_FILE_OPT:
748 if (cmdFile) {
749 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
750 "-f");
751 warningCount++;
752 PR_Free(cmdFile);
753 cmdFile = NULL;
754 }
755 if (!arg) {
756 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
757 "-f");
758 errorCount++;
759 goto loser;
760 }
761 cmdFile = PL_strdup(arg);
762 ate = 1;
763 break;
764 case NORECURSE_OPT:
765 noRecurse = PR_TRUE;
766 break;
767 case LEAVE_ARC_OPT:
768 leaveArc = PR_TRUE;
769 break;
770 case VERBOSITY_OPT:
771 if (!arg) {
772 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
773 "--verbosity");
774 errorCount++;
775 goto loser;
776 }
777 verbosity = atoi(arg);
778 ate = 1;
779 break;
780 case KEYSIZE_OPT:
781 if (keySize != -1) {
782 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-s");
783 warningCount++;
784 }
785 keySize = atoi(arg);
786 ate = 1;
787 if (keySize < 1 || keySize > MAX_RSA_KEY_SIZE) {
788 PR_fprintf(errorFD, "Invalid key size: %d.\n", keySize);
789 errorCount++;
790 goto loser;
791 }
792 break;
793 case TOKEN_OPT:
794 if (token) {
795 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-t");
796 PR_Free(token);
797 token = NULL;
798 }
799 if (!arg) {
800 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-t");
801 errorCount++;
802 goto loser;
803 }
804 token = PL_strdup(arg);
805 ate = 1;
806 break;
807 case XPI_ARC_OPT:
808 xpi_arc = 1;
809 break;
810 default:
811 PR_fprintf(errorFD, "warning: unknown option\n");
812 warningCount++;
813 break;
814 }
815
816 return ate;
817 loser:
818 return -1;
819 }
820
821 /*********************************************************************
822 *
823 * m a i n
824 */
825 int
main(int argc,char * argv[])826 main(int argc, char *argv[])
827 {
828 PRBool readOnly;
829 int retval = 0;
830
831 outputFD = PR_STDOUT;
832 errorFD = PR_STDERR;
833
834 progName = argv[0];
835
836 if (argc < 2) {
837 Usage();
838 }
839
840 excludeDirs = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
841 PL_CompareStrings, NULL, NULL);
842 extensions = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
843 PL_CompareStrings, NULL, NULL);
844
845 if (parse_args(argc, argv)) {
846 retval = -1;
847 goto cleanup;
848 }
849
850 /* Parse the command file if one was given */
851 if (cmdFile) {
852 if (ProcessCommandFile()) {
853 retval = -1;
854 goto cleanup;
855 }
856 }
857
858 /* Set up output redirection */
859 if (outfile) {
860 if (PR_Access(outfile, PR_ACCESS_EXISTS) == PR_SUCCESS) {
861 /* delete the file if it is already present */
862 PR_fprintf(errorFD,
863 "warning: %s already exists and will be overwritten.\n",
864 outfile);
865 warningCount++;
866 if (PR_Delete(outfile) != PR_SUCCESS) {
867 PR_fprintf(errorFD, "ERROR: unable to delete %s.\n", outfile);
868 errorCount++;
869 exit(ERRX);
870 }
871 }
872 outputFD = PR_Open(outfile,
873 PR_WRONLY |
874 PR_CREATE_FILE | PR_TRUNCATE,
875 0777);
876 if (!outputFD) {
877 PR_fprintf(errorFD, "ERROR: Unable to create %s.\n",
878 outfile);
879 errorCount++;
880 exit(ERRX);
881 }
882 errorFD = outputFD;
883 }
884
885 /* This seems to be a fairly common user error */
886
887 if (verify && list_certs > 0) {
888 PR_fprintf(errorFD, "%s: Can't use -l and -v at the same time\n",
889 PROGRAM_NAME);
890 errorCount++;
891 retval = -1;
892 goto cleanup;
893 }
894
895 /* -J assumes -Z now */
896
897 if (javascript && zipfile) {
898 PR_fprintf(errorFD, "%s: Can't use -J and -Z at the same time\n",
899 PROGRAM_NAME);
900 PR_fprintf(errorFD, "%s: -J option will create the jar files for you\n",
901 PROGRAM_NAME);
902 errorCount++;
903 retval = -1;
904 goto cleanup;
905 }
906
907 /* -X needs -Z */
908
909 if (xpi_arc && !zipfile) {
910 PR_fprintf(errorFD, "%s: option XPI (-X) requires option jarfile (-Z)\n",
911 PROGRAM_NAME);
912 errorCount++;
913 retval = -1;
914 goto cleanup;
915 }
916
917 /* Less common mixing of -L with various options */
918
919 if (list_certs > 0 &&
920 (tell_who || zipfile || javascript ||
921 scriptdir || extensionsGiven || exclusionsGiven || install_script)) {
922 PR_fprintf(errorFD, "%s: Can't use -l or -L with that option\n",
923 PROGRAM_NAME);
924 errorCount++;
925 retval = -1;
926 goto cleanup;
927 }
928
929 if (!cert_dir)
930 cert_dir = get_default_cert_dir();
931
932 VerifyCertDir(cert_dir, keyName);
933
934 if (compression_level < MIN_COMPRESSION_LEVEL ||
935 compression_level > MAX_COMPRESSION_LEVEL) {
936 PR_fprintf(errorFD, "Compression level must be between %d and %d.\n",
937 MIN_COMPRESSION_LEVEL, MAX_COMPRESSION_LEVEL);
938 errorCount++;
939 retval = -1;
940 goto cleanup;
941 }
942
943 if (jartree && !keyName) {
944 PR_fprintf(errorFD, "You must specify a key with which to sign.\n");
945 errorCount++;
946 retval = -1;
947 goto cleanup;
948 }
949
950 readOnly = (genkey == NULL); /* only key generation requires write */
951 if (InitCrypto(cert_dir, readOnly)) {
952 PR_fprintf(errorFD, "ERROR: Cryptographic initialization failed.\n");
953 errorCount++;
954 retval = -1;
955 goto cleanup;
956 }
957
958 if (enableOCSP) {
959 SECStatus rv = CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
960 if (rv != SECSuccess) {
961 PR_fprintf(errorFD, "ERROR: Attempt to enable OCSP Checking failed.\n");
962 errorCount++;
963 retval = -1;
964 }
965 }
966
967 if (verify) {
968 if (VerifyJar(verify)) {
969 errorCount++;
970 retval = -1;
971 goto cleanup;
972 }
973 } else if (list_certs) {
974 if (ListCerts(keyName, list_certs)) {
975 errorCount++;
976 retval = -1;
977 goto cleanup;
978 }
979 } else if (list_modules) {
980 JarListModules();
981 } else if (genkey) {
982 if (GenerateCert(genkey, keySize, token)) {
983 errorCount++;
984 retval = -1;
985 goto cleanup;
986 }
987 } else if (tell_who) {
988 if (JarWho(tell_who)) {
989 errorCount++;
990 retval = -1;
991 goto cleanup;
992 }
993 } else if (javascript && jartree) {
994 /* make sure directory exists */
995 PRDir *dir;
996 dir = PR_OpenDir(jartree);
997 if (!dir) {
998 PR_fprintf(errorFD, "ERROR: unable to open directory %s.\n",
999 jartree);
1000 errorCount++;
1001 retval = -1;
1002 goto cleanup;
1003 } else {
1004 PR_CloseDir(dir);
1005 }
1006
1007 /* undo junk from prior runs of signtool*/
1008 if (RemoveAllArc(jartree)) {
1009 PR_fprintf(errorFD, "Error removing archive directories under %s\n",
1010 jartree);
1011 errorCount++;
1012 retval = -1;
1013 goto cleanup;
1014 }
1015
1016 /* traverse all the htm|html files in the directory */
1017 if (InlineJavaScript(jartree, !noRecurse)) {
1018 retval = -1;
1019 goto cleanup;
1020 }
1021
1022 /* sign any resultant .arc directories created in above step */
1023 if (SignAllArc(jartree, keyName, javascript, metafile, install_script,
1024 optimize, !noRecurse)) {
1025 retval = -1;
1026 goto cleanup;
1027 }
1028
1029 if (!leaveArc) {
1030 RemoveAllArc(jartree);
1031 }
1032
1033 if (errorCount > 0 || warningCount > 0) {
1034 PR_fprintf(outputFD, "%d error%s, %d warning%s.\n",
1035 errorCount,
1036 errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s");
1037 } else {
1038 PR_fprintf(outputFD, "Directory %s signed successfully.\n",
1039 jartree);
1040 }
1041 } else if (jartree) {
1042 SignArchive(jartree, keyName, zipfile, javascript, metafile,
1043 install_script, optimize, !noRecurse);
1044 } else
1045 Usage();
1046
1047 cleanup:
1048 if (extensions) {
1049 PL_HashTableDestroy(extensions);
1050 extensions = NULL;
1051 }
1052 if (excludeDirs) {
1053 PL_HashTableDestroy(excludeDirs);
1054 excludeDirs = NULL;
1055 }
1056 if (outputFD != PR_STDOUT) {
1057 PR_Close(outputFD);
1058 }
1059 rm_dash_r(TMP_OUTPUT);
1060 if (retval == 0) {
1061 if (NSS_Shutdown() != SECSuccess) {
1062 exit(1);
1063 }
1064 }
1065 return retval;
1066 }
1067