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