1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
40 * A command line tool to create manifest files
41 * from a directory hierarchy. It is assumed that
42 * the tree will be equivalent to what resides
43 * or will reside in an archive.
53 /***********************************************************************
54 * Global Variable Definitions
56 char *progName; /* argv[0] */
59 secuPWData pwdata = { PW_NONE, 0 };
61 /* directories or files to exclude in descent */
62 PLHashTable *excludeDirs = NULL;
63 static PRBool exclusionsGiven = PR_FALSE;
65 /* zatharus is the man who knows no time, dies tragic death */
68 /* -b basename of .rsa, .sf files */
69 char *base = DEFAULT_BASE_NAME;
71 /* Only sign files with this extension */
72 PLHashTable *extensions = NULL;
73 PRBool extensionsGiven = PR_FALSE;
75 char *scriptdir = NULL;
79 PRFileDesc *outputFD = NULL, *errorFD = NULL;
81 int errorCount = 0, warningCount = 0;
83 int compression_level = DEFAULT_COMPRESSION_LEVEL;
84 PRBool compression_level_specified = PR_FALSE;
88 /* Command-line arguments */
89 static char *genkey = NULL;
90 static char *verify = NULL;
91 static char *zipfile = NULL;
92 static char *cert_dir = NULL;
93 static int javascript = 0;
94 static char *jartree = NULL;
95 static char *keyName = NULL;
96 static char *metafile = NULL;
97 static char *install_script = NULL;
98 static int list_certs = 0;
99 static int list_modules = 0;
100 static int optimize = 0;
101 static int enableOCSP = 0;
102 static char *tell_who = NULL;
103 static char *outfile = NULL;
104 static char *cmdFile = NULL;
105 static PRBool noRecurse = PR_FALSE;
106 static PRBool leaveArc = PR_FALSE;
107 static int keySize = -1;
108 static char *token = NULL;
121 LIST_OBJSIGN_CERTS_OPT,
150 DUPLICATE_OPTION_ERR = 0,
157 static char *errStrings[] = {
158 "warning: %s option specified more than once.\n"
159 "Only last specification will be used.\n",
160 "ERROR: option \"%s\" requires an argument.\n"
164 static int ProcessOneOpt(OPT_TYPE type, char *arg);
166 /*********************************************************************
168 * P r o c e s s C o m m a n d F i l e
174 #define CMD_FILE_BUFSIZE 1024
175 char buf[CMD_FILE_BUFSIZE];
181 fd = PR_Open(cmdFile, PR_RDONLY, 0777);
183 PR_fprintf(errorFD, "ERROR: Unable to open command file %s.\n");
188 while (pr_fgets(buf, CMD_FILE_BUFSIZE, fd)) {
192 /* Chop off final newline */
193 eol = PL_strchr(buf, '\r');
195 eol = PL_strchr(buf, '\n');
200 equals = PL_strchr(buf, '=');
208 /* Now buf points to the attribute, and equals points to the value. */
210 /* This is pretty straightforward, just deal with whatever attribute
212 if (!PL_strcasecmp(buf, "basename")) {
214 } else if (!PL_strcasecmp(buf, "compression")) {
215 type = COMPRESSION_OPT;
216 } else if (!PL_strcasecmp(buf, "certdir")) {
218 } else if (!PL_strcasecmp(buf, "extension")) {
219 type = EXTENSION_OPT;
220 } else if (!PL_strcasecmp(buf, "generate")) {
222 } else if (!PL_strcasecmp(buf, "installScript")) {
223 type = INSTALL_SCRIPT_OPT;
224 } else if (!PL_strcasecmp(buf, "javascriptdir")) {
225 type = SCRIPTDIR_OPT;
226 } else if (!PL_strcasecmp(buf, "htmldir")) {
227 type = JAVASCRIPT_OPT;
230 "warning: directory to be signed specified more than once."
231 " Only last specification will be used.\n");
236 jartree = PL_strdup(equals);
237 } else if (!PL_strcasecmp(buf, "certname")) {
239 } else if (!PL_strcasecmp(buf, "signdir")) {
241 } else if (!PL_strcasecmp(buf, "list")) {
242 type = LIST_OBJSIGN_CERTS_OPT;
243 } else if (!PL_strcasecmp(buf, "listall")) {
244 type = LIST_ALL_CERTS_OPT;
245 } else if (!PL_strcasecmp(buf, "metafile")) {
247 } else if (!PL_strcasecmp(buf, "modules")) {
249 } else if (!PL_strcasecmp(buf, "optimize")) {
251 } else if (!PL_strcasecmp(buf, "ocsp")) {
252 type = ENABLE_OCSP_OPT;
253 } else if (!PL_strcasecmp(buf, "password")) {
255 } else if (!PL_strcasecmp(buf, "verify")) {
257 } else if (!PL_strcasecmp(buf, "who")) {
259 } else if (!PL_strcasecmp(buf, "exclude")) {
261 } else if (!PL_strcasecmp(buf, "notime")) {
263 } else if (!PL_strcasecmp(buf, "jarfile")) {
265 } else if (!PL_strcasecmp(buf, "outfile")) {
267 } else if (!PL_strcasecmp(buf, "leavearc")) {
268 type = LEAVE_ARC_OPT;
269 } else if (!PL_strcasecmp(buf, "verbosity")) {
270 type = VERBOSITY_OPT;
271 } else if (!PL_strcasecmp(buf, "keysize")) {
273 } else if (!PL_strcasecmp(buf, "token")) {
275 } else if (!PL_strcasecmp(buf, "xpi")) {
279 "warning: unknown attribute \"%s\" in command file, line %d.\n",
285 /* Process the option, whatever it is */
286 if (type != UNKNOWN_OPT) {
287 if (ProcessOneOpt(type, equals) == -1) {
301 /*********************************************************************
303 * p a r s e _ a r g s
306 parse_args(int argc, char *argv[])
314 /* Loop over all arguments */
315 for (i = 1; i < argc; i++) {
329 if ( !PL_strcasecmp(opt + 2, "norecurse")) {
330 type = NORECURSE_OPT;
331 } else if ( !PL_strcasecmp(opt + 2, "leavearc")) {
332 type = LEAVE_ARC_OPT;
333 } else if ( !PL_strcasecmp(opt + 2, "verbosity")) {
334 type = VERBOSITY_OPT;
335 } else if ( !PL_strcasecmp(opt + 2, "outfile")) {
337 } else if ( !PL_strcasecmp(opt + 2, "keysize")) {
339 } else if ( !PL_strcasecmp(opt + 2, "token")) {
342 PR_fprintf(errorFD, "warning: unknown option: %s\n",
349 if (opt[2] != '\0') {
351 } else if (i < argc - 1) {
363 type = COMPRESSION_OPT;
369 type = EXTENSION_OPT;
372 type = COMMAND_FILE_OPT;
378 type = LONG_HELP_OPT;
381 type = INSTALL_SCRIPT_OPT;
384 type = SCRIPTDIR_OPT;
390 type = LIST_OBJSIGN_CERTS_OPT;
393 type = LIST_ALL_CERTS_OPT;
402 type = ENABLE_OCSP_OPT;
423 type = JAVASCRIPT_OPT;
442 PR_fprintf(errorFD, "warning: unrecognized option: -%c.\n",
454 "warning: directory to be signed specified more than once.\n"
455 " Only last specification will be used.\n");
460 jartree = PL_strdup(opt);
462 PR_fprintf(errorFD, "warning: unrecognized option: %s\n", opt);
467 if (type != UNKNOWN_OPT) {
468 short ateArg = ProcessOneOpt(type, arg);
473 if (ateArg && needsInc) {
483 /*********************************************************************
485 * P r o c e s s O n e O p t
487 * Since options can come from different places (command file, word options,
488 * char options), this is a central function that is called to deal with
489 * them no matter where they come from.
491 * type is the type of option.
492 * arg is the argument to the option, possibly NULL.
493 * Returns 1 if the argument was eaten, 0 if it wasn't, and -1 for error.
496 ProcessOneOpt(OPT_TYPE type, char *arg)
509 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-b");
515 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-b");
519 base = PL_strdup(arg);
522 case COMPRESSION_OPT:
523 if (compression_level_specified) {
524 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-c");
528 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-c");
532 compression_level = atoi(arg);
533 compression_level_specified = PR_TRUE;
538 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-d");
544 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-d");
548 cert_dir = PL_strdup(arg);
553 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
558 PL_HashTableAdd(extensions, arg, arg);
559 extensionsGiven = PR_TRUE;
562 case INSTALL_SCRIPT_OPT:
563 if (install_script) {
564 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
565 "installScript (-i)");
567 PR_Free(install_script);
568 install_script = NULL;
571 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
572 "installScript (-i)");
576 install_script = PL_strdup(arg);
581 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
582 "javascriptdir (-j)");
588 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
589 "javascriptdir (-j)");
593 scriptdir = PL_strdup(arg);
598 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
605 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
610 keyName = PL_strdup(arg);
613 case LIST_OBJSIGN_CERTS_OPT:
614 case LIST_ALL_CERTS_OPT:
615 if (list_certs != 0) {
617 "warning: only one of -l and -L may be specified.\n");
620 list_certs = (type == LIST_OBJSIGN_CERTS_OPT ? 1 : 2);
624 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
631 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
636 metafile = PL_strdup(arg);
642 case ENABLE_OCSP_OPT:
647 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
650 PR_Free(pwdata.data);
654 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
659 pwdata.source = PW_PLAINTEXT;
660 pwdata.data = PL_strdup(arg);
665 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
672 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
677 verify = PL_strdup(arg);
682 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
689 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
694 tell_who = PL_strdup(arg);
699 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
704 PL_HashTableAdd(excludeDirs, arg, arg);
705 exclusionsGiven = PR_TRUE;
716 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
723 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
728 zipfile = PL_strdup(arg);
733 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
740 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
745 genkey = PL_strdup(arg);
753 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
760 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
765 jartree = PL_strdup(arg);
770 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
777 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
782 outfile = PL_strdup(arg);
785 case COMMAND_FILE_OPT:
787 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
794 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
799 cmdFile = PL_strdup(arg);
810 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
815 verbosity = atoi(arg);
819 if ( keySize != -1 ) {
820 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-s");
825 if ( keySize < 1 || keySize > MAX_RSA_KEY_SIZE ) {
826 PR_fprintf(errorFD, "Invalid key size: %d.\n", keySize);
833 PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-t");
838 PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-t");
842 token = PL_strdup(arg);
849 PR_fprintf(errorFD, "warning: unknown option\n");
860 /*********************************************************************
865 main(int argc, char *argv[])
870 outputFD = PR_STDOUT;
879 excludeDirs = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
880 PL_CompareStrings, NULL, NULL);
881 extensions = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
882 PL_CompareStrings, NULL, NULL);
884 if (parse_args(argc, argv)) {
889 /* Parse the command file if one was given */
891 if (ProcessCommandFile()) {
897 /* Set up output redirection */
899 if (PR_Access(outfile, PR_ACCESS_EXISTS) == PR_SUCCESS) {
900 /* delete the file if it is already present */
902 "warning: %s already exists and will be overwritten.\n",
905 if (PR_Delete(outfile) != PR_SUCCESS) {
906 PR_fprintf(errorFD, "ERROR: unable to delete %s.\n", outfile);
911 outputFD = PR_Open(outfile,
912 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777);
914 PR_fprintf(errorFD, "ERROR: Unable to create %s.\n",
922 /* This seems to be a fairly common user error */
924 if (verify && list_certs > 0) {
925 PR_fprintf (errorFD, "%s: Can't use -l and -v at the same time\n",
932 /* -J assumes -Z now */
934 if (javascript && zipfile) {
935 PR_fprintf (errorFD, "%s: Can't use -J and -Z at the same time\n",
937 PR_fprintf (errorFD, "%s: -J option will create the jar files for you\n",
946 if (xpi_arc && !zipfile) {
947 PR_fprintf (errorFD, "%s: option XPI (-X) requires option jarfile (-Z)\n",
954 /* Less common mixing of -L with various options */
956 if (list_certs > 0 &&
957 (tell_who || zipfile || javascript ||
958 scriptdir || extensionsGiven || exclusionsGiven || install_script)) {
959 PR_fprintf(errorFD, "%s: Can't use -l or -L with that option\n",
968 cert_dir = get_default_cert_dir();
970 VerifyCertDir(cert_dir, keyName);
973 if ( compression_level < MIN_COMPRESSION_LEVEL ||
974 compression_level > MAX_COMPRESSION_LEVEL) {
975 PR_fprintf(errorFD, "Compression level must be between %d and %d.\n",
976 MIN_COMPRESSION_LEVEL, MAX_COMPRESSION_LEVEL);
982 if (jartree && !keyName) {
983 PR_fprintf(errorFD, "You must specify a key with which to sign.\n");
989 readOnly = (genkey == NULL); /* only key generation requires write */
990 if (InitCrypto(cert_dir, readOnly)) {
991 PR_fprintf(errorFD, "ERROR: Cryptographic initialization failed.\n");
998 SECStatus rv = CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
999 if (rv != SECSuccess) {
1000 PR_fprintf(errorFD, "ERROR: Attempt to enable OCSP Checking failed.\n");
1007 if (VerifyJar(verify)) {
1012 } else if (list_certs) {
1013 if (ListCerts(keyName, list_certs)) {
1018 } else if (list_modules) {
1020 } else if (genkey) {
1021 if (GenerateCert(genkey, keySize, token)) {
1026 } else if (tell_who) {
1027 if (JarWho(tell_who)) {
1032 } else if (javascript && jartree) {
1033 /* make sure directory exists */
1035 dir = PR_OpenDir(jartree);
1037 PR_fprintf(errorFD, "ERROR: unable to open directory %s.\n",
1046 /* undo junk from prior runs of signtool*/
1047 if (RemoveAllArc(jartree)) {
1048 PR_fprintf(errorFD, "Error removing archive directories under %s\n",
1055 /* traverse all the htm|html files in the directory */
1056 if (InlineJavaScript(jartree, !noRecurse)) {
1061 /* sign any resultant .arc directories created in above step */
1062 if (SignAllArc(jartree, keyName, javascript, metafile, install_script,
1063 optimize, !noRecurse)) {
1069 RemoveAllArc(jartree);
1072 if (errorCount > 0 || warningCount > 0) {
1073 PR_fprintf(outputFD, "%d error%s, %d warning%s.\n",
1075 errorCount == 1 ? "" : "s", warningCount, warningCount
1078 PR_fprintf(outputFD, "Directory %s signed successfully.\n",
1081 } else if (jartree) {
1082 SignArchive(jartree, keyName, zipfile, javascript, metafile,
1083 install_script, optimize, !noRecurse);
1089 PL_HashTableDestroy(extensions);
1093 PL_HashTableDestroy(excludeDirs);
1096 if (outputFD != PR_STDOUT) {
1099 rm_dash_r(TMP_OUTPUT);
1101 if (NSS_Shutdown() != SECSuccess) {