michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "signtool.h" michael@0: #include "prio.h" michael@0: #include "prmem.h" michael@0: #include "nss.h" michael@0: michael@0: static int is_dir (char *filename); michael@0: michael@0: /*********************************************************** michael@0: * Nasty hackish function definitions michael@0: */ michael@0: michael@0: long *mozilla_event_queue = 0; michael@0: michael@0: #ifndef XP_WIN michael@0: char *XP_GetString (int i) michael@0: { michael@0: return SECU_Strerror (i); michael@0: } michael@0: #endif michael@0: michael@0: void FE_SetPasswordEnabled() michael@0: { michael@0: } michael@0: michael@0: michael@0: void /*MWContext*/ *FE_GetInitContext (void) michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: void /*MWContext*/ *XP_FindSomeContext() michael@0: { michael@0: /* No windows context in command tools */ michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: void ET_moz_CallFunction() michael@0: { michael@0: } michael@0: michael@0: michael@0: /* michael@0: * R e m o v e A l l A r c michael@0: * michael@0: * Remove .arc directories that are lingering michael@0: * from a previous run of signtool. michael@0: * michael@0: */ michael@0: int michael@0: RemoveAllArc(char *tree) michael@0: { michael@0: PRDir * dir; michael@0: PRDirEntry * entry; michael@0: char *archive = NULL; michael@0: int retval = 0; michael@0: michael@0: dir = PR_OpenDir (tree); michael@0: if (!dir) michael@0: return - 1; michael@0: michael@0: for (entry = PR_ReadDir (dir, 0); entry; entry = PR_ReadDir (dir, michael@0: 0)) { michael@0: michael@0: if (entry->name[0] == '.') { michael@0: continue; michael@0: } michael@0: michael@0: if (archive) michael@0: PR_Free(archive); michael@0: archive = PR_smprintf("%s/%s", tree, entry->name); michael@0: michael@0: if (PL_strcaserstr (entry->name, ".arc") michael@0: == (entry->name + strlen(entry->name) - 4) ) { michael@0: michael@0: if (verbosity >= 0) { michael@0: PR_fprintf(outputFD, "removing: %s\n", archive); michael@0: } michael@0: michael@0: if (rm_dash_r(archive)) { michael@0: PR_fprintf(errorFD, "Error removing %s\n", archive); michael@0: errorCount++; michael@0: retval = -1; michael@0: goto finish; michael@0: } michael@0: } else if (is_dir(archive)) { michael@0: if (RemoveAllArc(archive)) { michael@0: retval = -1; michael@0: goto finish; michael@0: } michael@0: } michael@0: } michael@0: michael@0: finish: michael@0: PR_CloseDir (dir); michael@0: if (archive) michael@0: PR_Free(archive); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * r m _ d a s h _ r michael@0: * michael@0: * Remove a file, or a directory recursively. michael@0: * michael@0: */ michael@0: int rm_dash_r (char *path) michael@0: { michael@0: PRDir * dir; michael@0: PRDirEntry * entry; michael@0: PRFileInfo fileinfo; michael@0: char filename[FNSIZE]; michael@0: michael@0: if (PR_GetFileInfo(path, &fileinfo) != PR_SUCCESS) { michael@0: /*fprintf(stderr, "Error: Unable to access %s\n", filename);*/ michael@0: return - 1; michael@0: } michael@0: if (fileinfo.type == PR_FILE_DIRECTORY) { michael@0: michael@0: dir = PR_OpenDir(path); michael@0: if (!dir) { michael@0: PR_fprintf(errorFD, "Error: Unable to open directory %s.\n", path); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: michael@0: /* Recursively delete all entries in the directory */ michael@0: while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) { michael@0: sprintf(filename, "%s/%s", path, entry->name); michael@0: if (rm_dash_r(filename)) michael@0: return - 1; michael@0: } michael@0: michael@0: if (PR_CloseDir(dir) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, "Error: Could not close %s.\n", path); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: michael@0: /* Delete the directory itself */ michael@0: if (PR_RmDir(path) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, "Error: Unable to delete %s\n", path); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: } else { michael@0: if (PR_Delete(path) != PR_SUCCESS) { michael@0: PR_fprintf(errorFD, "Error: Unable to delete %s\n", path); michael@0: errorCount++; michael@0: return - 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * u s a g e michael@0: * michael@0: * Print some useful help information michael@0: * michael@0: */ michael@0: michael@0: michael@0: void michael@0: Usage (void) michael@0: { michael@0: #define FPS PR_fprintf(outputFD, michael@0: FPS "%s %s -a signing tool for jar files\n", LONG_PROGRAM_NAME,NSS_VERSION); michael@0: FPS "\n\nType %s -H for more detailed descriptions\n", PROGRAM_NAME); michael@0: FPS "\nUsage: %s -k keyName [-b basename] [-c Compression Level]\n" michael@0: "\t\t [-d cert-dir] [-i installer script] [-m metafile] [-x name]\n" michael@0: "\t\t [-e extension] [-o] [-z] [-X] [--outfile] [--verbose value]\n" michael@0: "\t\t [--norecurse] [--leavearc] [-j directory] [-Z jarfile] [-O]\n" michael@0: "\t\t [-p password] directory-tree\n", PROGRAM_NAME); michael@0: FPS "\t%s -J -k keyName [-b basename] [-c Compression Level]\n" michael@0: "\t\t [-d cert-dir][-i installer script] [-m metafile] [-x name]\n" michael@0: "\t\t [-e extension] [-o] [-z] [-X] [--outfile] [--verbose value]\n" michael@0: "\t\t [--norecurse] [--leavearc] [-j directory] [-p password] [-O] \n" michael@0: "\t\t directory-tree\n", PROGRAM_NAME); michael@0: FPS "\t%s -h \n", PROGRAM_NAME); michael@0: FPS "\t%s -H \n", PROGRAM_NAME); michael@0: FPS "\t%s -l [-k keyName] [-d cert-dir] [--outfile] [-O] \n", PROGRAM_NAME); michael@0: FPS "\t%s -L [-k keyName] [-d cert-dir] [--outfile] [-O] \n", PROGRAM_NAME); michael@0: FPS "\t%s -M [--outfile] [-O] \n", PROGRAM_NAME); michael@0: FPS "\t%s -v [-d cert-dir] [--outfile] [-O] archive\n", PROGRAM_NAME); michael@0: FPS "\t%s -w [--outfile] [-O] archive\n" , PROGRAM_NAME); michael@0: FPS "\t%s -G nickname [--keysize|-s size] [-t |--token tokenname]\n" michael@0: "\t\t [--outfile] [-O] \n", PROGRAM_NAME); michael@0: FPS "\t%s -f filename\n" , PROGRAM_NAME); michael@0: exit (ERRX); michael@0: } michael@0: michael@0: void michael@0: LongUsage(void) michael@0: { michael@0: FPS "%s %s -a signing tool for jar files\n", LONG_PROGRAM_NAME,NSS_VERSION); michael@0: FPS "\n%-20s Signs the directory-tree\n", michael@0: "signtool directory-tree"); michael@0: FPS "%-30s Nickname (key) of the certificate to sign with\n", michael@0: " -k keyname"); michael@0: FPS "%-30s Base filename for the .rsa and.sf files in the\n", michael@0: " -b basename"); michael@0: FPS "%-30s META-INF directory\n"," "); michael@0: FPS "%-30s Set the compression level. 0-9, 0=none\n", michael@0: " -c CompressionLevel"); michael@0: FPS "%-30s Certificate database directory containing cert*db\n", michael@0: " -d certificate directory"); michael@0: FPS "%-30s and key*db\n"," "); michael@0: FPS "%-30s Name of the installer script for SmartUpdate\n", michael@0: " -i installer script"); michael@0: FPS "%-30s Name of a metadata control file\n", michael@0: " -m metafile"); michael@0: FPS "%-30s For optimizing the archive for size.\n", michael@0: " -o"); michael@0: FPS "%-30s Omit Optional Headers\n"," "); michael@0: FPS "%-30s Excludes the specified directory or file from\n", michael@0: " -x directory or file name"); michael@0: FPS "%-30s signing\n"," "); michael@0: FPS "%-30s To not store the signing time in digital\n", michael@0: " -z directory or file name"); michael@0: FPS "%-30s signature\n"," "); michael@0: FPS "%-30s Create XPI Compatible Archive. It requires -Z\n", michael@0: " -X directory or file name"); michael@0: FPS "%-30s option\n"," "); michael@0: FPS "%-30s Sign only files with the given extension\n", michael@0: " -e"); michael@0: FPS "%-30s Causes the specified directory to be signed and\n", michael@0: " -j"); michael@0: FPS "%-30s tags its entries as inline JavaScript\n"," "); michael@0: FPS "%-30s Creates a JAR file with the specified name.\n", michael@0: " -Z"); michael@0: FPS "%-30s -Z option cannot be used with -J option\n"," "); michael@0: FPS "%-30s Specifies a password for the private-key database\n", michael@0: " -p"); michael@0: FPS "%-30s (insecure)\n"," "); michael@0: FPS "%-30s File to receive redirected output\n", michael@0: " --outfile filename"); michael@0: FPS "%-30s Sets the quantity of information generated in\n", michael@0: " --verbosity value"); michael@0: FPS "%-30s operation\n"," "); michael@0: FPS "%-30s Blocks recursion into subdirectories\n", michael@0: " --norecurse"); michael@0: FPS "%-30s Retains the temporary .arc (archive) directories\n", michael@0: " --leavearc"); michael@0: FPS "%-30s -J option creates\n"," "); michael@0: michael@0: FPS "\n%-20s Signs a directory of HTML files containing JavaScript and\n", michael@0: "-J" ); michael@0: FPS "%-20s creates as many archive files as are in the HTML tags.\n"," "); michael@0: michael@0: FPS "%-20s The options are same as without any command option given\n"," "); michael@0: FPS "%-20s above. -Z and -J options are not allowed together\n"," "); michael@0: michael@0: FPS "\n%-20s Generates a new private-public key pair and corresponding\n", michael@0: "-G nickname"); michael@0: FPS "%-20s object-signing certificates with the given nickname\n"," "); michael@0: FPS "%-30s Specifies the size of the key for generated \n", michael@0: " --keysize|-s keysize"); michael@0: FPS "%-30s certificate\n"," "); michael@0: FPS "%-30s Specifies which available token should generate\n", michael@0: " --token|-t token name "); michael@0: FPS "%-30s the key and receive the certificate\n"," "); michael@0: FPS "%-30s Specifies a file to receive redirected output\n", michael@0: " --outfile filename "); michael@0: michael@0: FPS "\n%-20s Display signtool help\n", michael@0: "-h "); michael@0: michael@0: FPS "\n%-20s Display signtool help(Detailed)\n", michael@0: "-H "); michael@0: michael@0: FPS "\n%-20s Lists signing certificates, including issuing CAs\n", michael@0: "-l "); michael@0: FPS "%-30s Certificate database directory containing cert*db\n", michael@0: " -d certificate directory"); michael@0: FPS "%-30s and key*db\n"," "); michael@0: michael@0: FPS "%-30s Specifies a file to receive redirected output\n", michael@0: " --outfile filename "); michael@0: FPS "%-30s Specifies the nickname (key) of the certificate\n", michael@0: " -k keyname"); michael@0: michael@0: michael@0: FPS "\n%-20s Lists the certificates in your database\n", michael@0: "-L "); michael@0: FPS "%-30s Certificate database directory containing cert*db\n", michael@0: " -d certificate directory"); michael@0: FPS "%-30s and key*db\n"," "); michael@0: michael@0: FPS "%-30s Specifies a file to receive redirected output\n", michael@0: " --outfile filename "); michael@0: FPS "%-30s Specifies the nickname (key) of the certificate\n", michael@0: " -k keyname"); michael@0: michael@0: FPS "\n%-20s Lists the PKCS #11 modules available to signtool\n", michael@0: "-M "); michael@0: michael@0: FPS "\n%-20s Displays the contents of an archive and verifies\n", michael@0: "-v archive"); michael@0: FPS "%-20s cryptographic integrity\n"," "); michael@0: FPS "%-30s Certificate database directory containing cert*db\n", michael@0: " -d certificate directory"); michael@0: FPS "%-30s and key*db\n"," "); michael@0: FPS "%-30s Specifies a file to receive redirected output\n", michael@0: " --outfile filename "); michael@0: michael@0: FPS "\n%-20s Displays the names of signers in the archive\n", michael@0: "-w archive"); michael@0: FPS "%-30s Specifies a file to receive redirected output\n", michael@0: " --outfile filename "); michael@0: michael@0: michael@0: FPS "\n%-30s Common option to all the above.\n", michael@0: " -O"); michael@0: FPS "%-30s Enable OCSP checking\n"," "); michael@0: michael@0: FPS "\n%-20s Specifies a text file containing options and arguments in\n", michael@0: "-f command-file"); michael@0: FPS "%-20s keyword=value format. Commands are taken from this file\n"," "); michael@0: michael@0: FPS "\n\n\n"); michael@0: FPS "Example:\n"); michael@0: FPS "%-10s -d \"certificate directory\" -k \"certnickname\" \\", michael@0: PROGRAM_NAME); michael@0: FPS "\n%-10s -p \"password\" -X -Z \"file.xpi\" directory-tree\n"," " ); michael@0: FPS "Common syntax to create an XPInstall compatible" michael@0: " signed archive\n\n"," "); michael@0: FPS "\nCommand File Keywords and Example:\n"); michael@0: FPS "\nKeyword\t\tValue\n"); michael@0: FPS "basename\tSame as -b option\n"); michael@0: FPS "compression\tSame as -c option\n"); michael@0: FPS "certdir\t\tSame as -d option\n"); michael@0: FPS "extension\tSame as -e option\n"); michael@0: FPS "generate\tSame as -G option\n"); michael@0: FPS "installscript\tSame as -i option\n"); michael@0: FPS "javascriptdir\tSame as -j option\n"); michael@0: FPS "htmldir\t\tSame as -J option\n"); michael@0: FPS "certname\tNickname of certificate, as with -k option\n"); michael@0: FPS "signdir\t\tThe directory to be signed, as with -k option\n"); michael@0: FPS "list\t\tSame as -l option. Value is ignored,\n" michael@0: " \t\tbut = sign must be present\n"); michael@0: FPS "listall\t\tSame as -L option. Value is ignored\n" michael@0: " \t\tbut = sign must be present\n"); michael@0: FPS "metafile\tSame as -m option\n"); michael@0: FPS "modules\t\tSame as -M option. Value is ignored,\n" michael@0: " \t\tbut = sign must be present\n"); michael@0: FPS "optimize\tSame as -o option. Value is ignored,\n" michael@0: " \tbut = sign must be present\n"); michael@0: FPS "ocsp\t\tSame as -O option\n"); michael@0: FPS "password\tSame as -p option\n"); michael@0: FPS "verify\t\tSame as -v option\n"); michael@0: FPS "who\t\tSame as -w option\n"); michael@0: FPS "exclude\t\tSame as -x option\n"); michael@0: FPS "notime\t\tSame as -z option. Value is ignored,\n" michael@0: " \t\tbut = sign must be present\n"); michael@0: FPS "jarfile\t\tSame as -Z option\n"); michael@0: FPS "outfile\t\tSame as --outfile option. The argument\n"); michael@0: FPS " \t\tis the name of a file to which output\n"); michael@0: FPS " \t\tof a file and error messages will be \n"); michael@0: FPS " \t\tredirected\n"); michael@0: FPS "leavearc\tSame as --leavearc option\n"); michael@0: FPS "verbosity\tSame as --verbosity option\n"); michael@0: FPS "keysize\t\tSame as -s option\n"); michael@0: FPS "token\t\tSame as -t option\n"); michael@0: FPS "xpi\t\tSame as -X option\n"); michael@0: FPS "\n\n"); michael@0: FPS "Here's an example of the use of the command file. The command\n\n"); michael@0: FPS " signtool -d c:\\netscape\\users\\james -k mycert -Z myjar.jar \\\n" michael@0: " signdir > output.txt\n\n"); michael@0: FPS "becomes\n\n"); michael@0: FPS " signtool -f somefile\n\n"); michael@0: FPS "where somefile contains the following lines:\n\n"); michael@0: FPS " certdir=c:\\netscape\\users\\james\n"," "); michael@0: FPS " certname=mycert\n"," "); michael@0: FPS " jarfile=myjar.jar\n"," "); michael@0: FPS " signdir=signdir\n"," "); michael@0: FPS " outfile=output.txt\n"," "); michael@0: exit (ERRX); michael@0: #undef FPS michael@0: } michael@0: michael@0: /* michael@0: * p r i n t _ e r r o r michael@0: * michael@0: * For the undocumented -E function. If an older version michael@0: * of communicator gives you a numeric error, we can see what michael@0: * really happened without doing hex math. michael@0: * michael@0: */ michael@0: michael@0: void michael@0: print_error (int err) michael@0: { michael@0: PR_fprintf(errorFD, "Error %d: %s\n", err, JAR_get_error (err)); michael@0: errorCount++; michael@0: give_help (err); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * o u t _ o f _ m e m o r y michael@0: * michael@0: * Out of memory, exit Signtool. michael@0: * michael@0: */ michael@0: void michael@0: out_of_memory (void) michael@0: { michael@0: PR_fprintf(errorFD, "%s: out of memory\n", PROGRAM_NAME); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * V e r i f y C e r t D i r michael@0: * michael@0: * Validate that the specified directory michael@0: * contains a certificate database michael@0: * michael@0: */ michael@0: void michael@0: VerifyCertDir(char *dir, char *keyName) michael@0: { michael@0: char fn [FNSIZE]; michael@0: michael@0: /* don't try verifying if we don't have a local directory */ michael@0: if (strncmp(dir, "multiaccess:", sizeof("multiaccess:") - 1) == 0) { michael@0: return; michael@0: } michael@0: /* this function is truly evil. Tools and applications should not have michael@0: * any knowledge of actual cert databases! */ michael@0: return; michael@0: michael@0: /* This code is really broken because it makes underlying assumptions about michael@0: * how the NSS profile directory is laid out, but these names can change michael@0: * from release to release. */ michael@0: sprintf (fn, "%s/cert8.db", dir); michael@0: michael@0: if (PR_Access (fn, PR_ACCESS_EXISTS)) { michael@0: PR_fprintf(errorFD, "%s: No certificate database in \"%s\"\n", michael@0: PROGRAM_NAME, dir); michael@0: PR_fprintf(errorFD, "%s: Check the -d arguments that you gave\n", michael@0: PROGRAM_NAME); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: michael@0: if (verbosity >= 0) { michael@0: PR_fprintf(outputFD, "using certificate directory: %s\n", dir); michael@0: } michael@0: michael@0: if (keyName == NULL) michael@0: return; michael@0: michael@0: /* if the user gave the -k key argument, verify that michael@0: a key database already exists */ michael@0: michael@0: sprintf (fn, "%s/key3.db", dir); michael@0: michael@0: if (PR_Access (fn, PR_ACCESS_EXISTS)) { michael@0: PR_fprintf(errorFD, "%s: No private key database in \"%s\"\n", michael@0: PROGRAM_NAME, michael@0: dir); michael@0: PR_fprintf(errorFD, "%s: Check the -d arguments that you gave\n", michael@0: PROGRAM_NAME); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * f o r e a c h michael@0: * michael@0: * A recursive function to loop through all names in michael@0: * the specified directory, as well as all subdirectories. michael@0: * michael@0: * FIX: Need to see if all platforms allow multiple michael@0: * opendir's to be called. michael@0: * michael@0: */ michael@0: michael@0: int michael@0: foreach(char *dirname, char *prefix, michael@0: int (*fn)(char *relpath, char *basedir, char *reldir, char *filename, michael@0: void*arg), michael@0: PRBool recurse, PRBool includeDirs, void *arg) michael@0: { michael@0: char newdir [FNSIZE]; michael@0: int retval = 0; michael@0: michael@0: PRDir * dir; michael@0: PRDirEntry * entry; michael@0: michael@0: strcpy (newdir, dirname); michael@0: if (*prefix) { michael@0: strcat (newdir, "/"); michael@0: strcat (newdir, prefix); michael@0: } michael@0: michael@0: dir = PR_OpenDir (newdir); michael@0: if (!dir) michael@0: return - 1; michael@0: michael@0: for (entry = PR_ReadDir (dir, 0); entry; entry = PR_ReadDir (dir, 0)) { michael@0: if ( strcmp(entry->name, ".") == 0 || michael@0: strcmp(entry->name, "..") == 0 ) { michael@0: /* no infinite recursion, please */ michael@0: continue; michael@0: } michael@0: michael@0: /* can't sign self */ michael@0: if (!strcmp (entry->name, "META-INF")) michael@0: continue; michael@0: michael@0: /* -x option */ michael@0: if (PL_HashTableLookup(excludeDirs, entry->name)) michael@0: continue; michael@0: michael@0: strcpy (newdir, dirname); michael@0: if (*dirname) michael@0: strcat (newdir, "/"); michael@0: michael@0: if (*prefix) { michael@0: strcat (newdir, prefix); michael@0: strcat (newdir, "/"); michael@0: } michael@0: strcat (newdir, entry->name); michael@0: michael@0: if (!is_dir(newdir) || includeDirs) { michael@0: char newpath [FNSIZE]; michael@0: michael@0: strcpy (newpath, prefix); michael@0: if (*newpath) michael@0: strcat (newpath, "/"); michael@0: strcat (newpath, entry->name); michael@0: michael@0: if ( (*fn) (newpath, dirname, prefix, (char *) entry->name, michael@0: arg)) { michael@0: retval = -1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (is_dir (newdir)) { michael@0: if (recurse) { michael@0: char newprefix [FNSIZE]; michael@0: michael@0: strcpy (newprefix, prefix); michael@0: if (*newprefix) { michael@0: strcat (newprefix, "/"); michael@0: } michael@0: strcat (newprefix, entry->name); michael@0: michael@0: if (foreach (dirname, newprefix, fn, recurse, michael@0: includeDirs, arg)) { michael@0: retval = -1; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: PR_CloseDir (dir); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * i s _ d i r michael@0: * michael@0: * Return 1 if file is a directory. michael@0: * Wonder if this runs on a mac, trust not. michael@0: * michael@0: */ michael@0: static int is_dir (char *filename) michael@0: { michael@0: PRFileInfo finfo; michael@0: michael@0: if ( PR_GetFileInfo(filename, &finfo) != PR_SUCCESS ) { michael@0: printf("Unable to get information about %s\n", filename); michael@0: return 0; michael@0: } michael@0: michael@0: return ( finfo.type == PR_FILE_DIRECTORY ); michael@0: } michael@0: michael@0: michael@0: /*************************************************************** michael@0: * michael@0: * s e c E r r o r S t r i n g michael@0: * michael@0: * Returns an error string corresponding to the given error code. michael@0: * Doesn't cover all errors; returns a default for many. michael@0: * Returned string is only valid until the next call of this function. michael@0: */ michael@0: const char * michael@0: secErrorString(long code) michael@0: { michael@0: static char errstring[80]; /* dynamically constructed error string */ michael@0: char *c; /* the returned string */ michael@0: michael@0: switch (code) { michael@0: case SEC_ERROR_IO: michael@0: c = "io error"; michael@0: break; michael@0: case SEC_ERROR_LIBRARY_FAILURE: michael@0: c = "security library failure"; michael@0: break; michael@0: case SEC_ERROR_BAD_DATA: michael@0: c = "bad data"; michael@0: break; michael@0: case SEC_ERROR_OUTPUT_LEN: michael@0: c = "output length"; michael@0: break; michael@0: case SEC_ERROR_INPUT_LEN: michael@0: c = "input length"; michael@0: break; michael@0: case SEC_ERROR_INVALID_ARGS: michael@0: c = "invalid args"; michael@0: break; michael@0: case SEC_ERROR_EXPIRED_CERTIFICATE: michael@0: c = "expired certificate"; michael@0: break; michael@0: case SEC_ERROR_REVOKED_CERTIFICATE: michael@0: c = "revoked certificate"; michael@0: break; michael@0: case SEC_ERROR_INADEQUATE_KEY_USAGE: michael@0: c = "inadequate key usage"; michael@0: break; michael@0: case SEC_ERROR_INADEQUATE_CERT_TYPE: michael@0: c = "inadequate certificate type"; michael@0: break; michael@0: case SEC_ERROR_UNTRUSTED_CERT: michael@0: c = "untrusted cert"; michael@0: break; michael@0: case SEC_ERROR_NO_KRL: michael@0: c = "no key revocation list"; michael@0: break; michael@0: case SEC_ERROR_KRL_BAD_SIGNATURE: michael@0: c = "key revocation list: bad signature"; michael@0: break; michael@0: case SEC_ERROR_KRL_EXPIRED: michael@0: c = "key revocation list expired"; michael@0: break; michael@0: case SEC_ERROR_REVOKED_KEY: michael@0: c = "revoked key"; michael@0: break; michael@0: case SEC_ERROR_CRL_BAD_SIGNATURE: michael@0: c = "certificate revocation list: bad signature"; michael@0: break; michael@0: case SEC_ERROR_CRL_EXPIRED: michael@0: c = "certificate revocation list expired"; michael@0: break; michael@0: case SEC_ERROR_CRL_NOT_YET_VALID: michael@0: c = "certificate revocation list not yet valid"; michael@0: break; michael@0: case SEC_ERROR_UNKNOWN_ISSUER: michael@0: c = "unknown issuer"; michael@0: break; michael@0: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: michael@0: c = "expired issuer certificate"; michael@0: break; michael@0: case SEC_ERROR_BAD_SIGNATURE: michael@0: c = "bad signature"; michael@0: break; michael@0: case SEC_ERROR_BAD_KEY: michael@0: c = "bad key"; michael@0: break; michael@0: case SEC_ERROR_NOT_FORTEZZA_ISSUER: michael@0: c = "not fortezza issuer"; michael@0: break; michael@0: case SEC_ERROR_CA_CERT_INVALID: michael@0: c = "Certificate Authority certificate invalid"; michael@0: break; michael@0: case SEC_ERROR_EXTENSION_NOT_FOUND: michael@0: c = "extension not found"; michael@0: break; michael@0: case SEC_ERROR_CERT_NOT_IN_NAME_SPACE: michael@0: c = "certificate not in name space"; michael@0: break; michael@0: case SEC_ERROR_UNTRUSTED_ISSUER: michael@0: c = "untrusted issuer"; michael@0: break; michael@0: default: michael@0: sprintf(errstring, "security error %ld", code); michael@0: c = errstring; michael@0: break; michael@0: } michael@0: michael@0: return c; michael@0: } michael@0: michael@0: michael@0: /*************************************************************** michael@0: * michael@0: * d i s p l a y V e r i f y L o g michael@0: * michael@0: * Prints the log of a cert verification. michael@0: */ michael@0: void michael@0: displayVerifyLog(CERTVerifyLog *log) michael@0: { michael@0: CERTVerifyLogNode * node; michael@0: CERTCertificate * cert; michael@0: char *name; michael@0: michael@0: if ( !log || (log->count <= 0) ) { michael@0: return; michael@0: } michael@0: michael@0: for (node = log->head; node != NULL; node = node->next) { michael@0: michael@0: if ( !(cert = node->cert) ) { michael@0: continue; michael@0: } michael@0: michael@0: /* Get a name for this cert */ michael@0: if (cert->nickname != NULL) { michael@0: name = cert->nickname; michael@0: } else if (cert->emailAddr && cert->emailAddr[0]) { michael@0: name = cert->emailAddr; michael@0: } else { michael@0: name = cert->subjectName; michael@0: } michael@0: michael@0: printf( "%s%s:\n", name, michael@0: (node->depth > 0) ? " [Certificate Authority]" : ""); michael@0: michael@0: printf("\t%s\n", secErrorString(node->error)); michael@0: michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * J a r L i s t M o d u l e s michael@0: * michael@0: * Print a list of the PKCS11 modules that are michael@0: * available. This is useful for smartcard people to michael@0: * make sure they have the drivers loaded. michael@0: * michael@0: */ michael@0: void michael@0: JarListModules(void) michael@0: { michael@0: int i; michael@0: int count = 0; michael@0: michael@0: SECMODModuleList * modules = NULL; michael@0: static SECMODListLock *moduleLock = NULL; michael@0: michael@0: SECMODModuleList * mlp; michael@0: michael@0: if ((moduleLock = SECMOD_GetDefaultModuleListLock()) == NULL) { michael@0: /* this is the wrong text */ michael@0: PR_fprintf(errorFD, "%s: unable to acquire lock on module list\n", michael@0: PROGRAM_NAME); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: michael@0: SECMOD_GetReadLock (moduleLock); michael@0: michael@0: modules = SECMOD_GetDefaultModuleList(); michael@0: michael@0: if (modules == NULL) { michael@0: SECMOD_ReleaseReadLock (moduleLock); michael@0: PR_fprintf(errorFD, "%s: Can't get module list\n", PROGRAM_NAME); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: michael@0: PR_fprintf(outputFD, "\nListing of PKCS11 modules\n"); michael@0: PR_fprintf(outputFD, "-----------------------------------------------\n"); michael@0: michael@0: for (mlp = modules; mlp != NULL; mlp = mlp->next) { michael@0: count++; michael@0: PR_fprintf(outputFD, "%3d. %s\n", count, mlp->module->commonName); michael@0: michael@0: if (mlp->module->internal) michael@0: PR_fprintf(outputFD, " (this module is internally loaded)\n"); michael@0: else michael@0: PR_fprintf(outputFD, " (this is an external module)\n"); michael@0: michael@0: if (mlp->module->dllName) michael@0: PR_fprintf(outputFD, " DLL name: %s\n", michael@0: mlp->module->dllName); michael@0: michael@0: if (mlp->module->slotCount == 0) michael@0: PR_fprintf(outputFD, " slots: There are no slots attached to this module\n"); michael@0: else michael@0: PR_fprintf(outputFD, " slots: %d slots attached\n", michael@0: mlp->module->slotCount); michael@0: michael@0: if (mlp->module->loaded == 0) michael@0: PR_fprintf(outputFD, " status: Not loaded\n"); michael@0: else michael@0: PR_fprintf(outputFD, " status: loaded\n"); michael@0: michael@0: for (i = 0; i < mlp->module->slotCount; i++) { michael@0: PK11SlotInfo * slot = mlp->module->slots[i]; michael@0: michael@0: PR_fprintf(outputFD, "\n"); michael@0: PR_fprintf(outputFD, " slot: %s\n", PK11_GetSlotName(slot)); michael@0: PR_fprintf(outputFD, " token: %s\n", PK11_GetTokenName(slot)); michael@0: } michael@0: } michael@0: michael@0: PR_fprintf(outputFD, "-----------------------------------------------\n"); michael@0: michael@0: if (count == 0) michael@0: PR_fprintf(outputFD, michael@0: "Warning: no modules were found (should have at least one)\n"); michael@0: michael@0: SECMOD_ReleaseReadLock (moduleLock); michael@0: } michael@0: michael@0: michael@0: /********************************************************************** michael@0: * c h o p michael@0: * michael@0: * Eliminates leading and trailing whitespace. Returns a pointer to the michael@0: * beginning of non-whitespace, or an empty string if it's all whitespace. michael@0: */ michael@0: char* michael@0: chop(char *str) michael@0: { michael@0: char *start, *end; michael@0: michael@0: if (str) { michael@0: start = str; michael@0: michael@0: /* Nip leading whitespace */ michael@0: while (isspace(*start)) { michael@0: start++; michael@0: } michael@0: michael@0: /* Nip trailing whitespace */ michael@0: if (*start) { michael@0: end = start + strlen(start) - 1; michael@0: while (isspace(*end) && end > start) { michael@0: end--; michael@0: } michael@0: *(end + 1) = '\0'; michael@0: } michael@0: michael@0: return start; michael@0: } else { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: /*********************************************************************** michael@0: * michael@0: * F a t a l E r r o r michael@0: * michael@0: * Outputs an error message and bails out of the program. michael@0: */ michael@0: void michael@0: FatalError(char *msg) michael@0: { michael@0: if (!msg) michael@0: msg = ""; michael@0: michael@0: PR_fprintf(errorFD, "FATAL ERROR: %s\n", msg); michael@0: errorCount++; michael@0: exit(ERRX); michael@0: } michael@0: michael@0: michael@0: /************************************************************************* michael@0: * michael@0: * I n i t C r y p t o michael@0: */ michael@0: int michael@0: InitCrypto(char *cert_dir, PRBool readOnly) michael@0: { michael@0: SECStatus rv; michael@0: static int prior = 0; michael@0: PK11SlotInfo * slotinfo; michael@0: michael@0: if (prior == 0) { michael@0: /* some functions such as OpenKeyDB expect this path to be michael@0: * implicitly set prior to calling */ michael@0: if (readOnly) { michael@0: rv = NSS_Init(cert_dir); michael@0: } else { michael@0: rv = NSS_InitReadWrite(cert_dir); michael@0: } michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintPRandOSError(PROGRAM_NAME); michael@0: exit(-1); michael@0: } michael@0: michael@0: SECU_ConfigDirectory (cert_dir); michael@0: michael@0: /* Been there done that */ michael@0: prior++; michael@0: michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: /* Must login to FIPS before you do anything else */ michael@0: if (PK11_IsFIPS()) { michael@0: slotinfo = PK11_GetInternalSlot(); michael@0: if (!slotinfo) { michael@0: fprintf(stderr, "%s: Unable to get PKCS #11 Internal Slot." michael@0: "\n", PROGRAM_NAME); michael@0: return - 1; michael@0: } michael@0: if (PK11_Authenticate(slotinfo, PR_FALSE /*loadCerts*/, michael@0: &pwdata) != SECSuccess) { michael@0: fprintf(stderr, "%s: Unable to authenticate to %s.\n", michael@0: PROGRAM_NAME, PK11_GetSlotName(slotinfo)); michael@0: PK11_FreeSlot(slotinfo); michael@0: return - 1; michael@0: } michael@0: PK11_FreeSlot(slotinfo); michael@0: } michael@0: michael@0: /* Make sure there is a password set on the internal key slot */ michael@0: slotinfo = PK11_GetInternalKeySlot(); michael@0: if (!slotinfo) { michael@0: fprintf(stderr, "%s: Unable to get PKCS #11 Internal Key Slot." michael@0: "\n", PROGRAM_NAME); michael@0: return - 1; michael@0: } michael@0: if (PK11_NeedUserInit(slotinfo)) { michael@0: PR_fprintf(errorFD, michael@0: "\nWARNING: No password set on internal key database. Most operations will fail." michael@0: "\nYou must create a password.\n"); michael@0: warningCount++; michael@0: } michael@0: michael@0: /* Make sure we can authenticate to the key slot in FIPS mode */ michael@0: if (PK11_IsFIPS()) { michael@0: if (PK11_Authenticate(slotinfo, PR_FALSE /*loadCerts*/, michael@0: &pwdata) != SECSuccess) { michael@0: fprintf(stderr, "%s: Unable to authenticate to %s.\n", michael@0: PROGRAM_NAME, PK11_GetSlotName(slotinfo)); michael@0: PK11_FreeSlot(slotinfo); michael@0: return - 1; michael@0: } michael@0: } michael@0: PK11_FreeSlot(slotinfo); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /* Windows foolishness is now in the secutil lib */ michael@0: michael@0: /***************************************************************** michael@0: * g e t _ d e f a u l t _ c e r t _ d i r michael@0: * michael@0: * Attempt to locate a certificate directory. michael@0: * Failing that, complain that the user needs to michael@0: * use the -d(irectory) parameter. michael@0: * michael@0: */ michael@0: char *get_default_cert_dir (void) michael@0: { michael@0: char *home; michael@0: michael@0: char *cd = NULL; michael@0: static char db [FNSIZE]; michael@0: michael@0: #ifdef XP_UNIX michael@0: home = getenv ("HOME"); michael@0: michael@0: if (home && *home) { michael@0: sprintf (db, "%s/.netscape", home); michael@0: cd = db; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_PC michael@0: FILE * fp; michael@0: michael@0: /* first check the environment override */ michael@0: michael@0: home = getenv ("JAR_HOME"); michael@0: michael@0: if (home && *home) { michael@0: sprintf (db, "%s/cert7.db", home); michael@0: michael@0: if ((fp = fopen (db, "r")) != NULL) { michael@0: fclose (fp); michael@0: cd = home; michael@0: } michael@0: } michael@0: michael@0: /* try the old navigator directory */ michael@0: michael@0: if (cd == NULL) { michael@0: home = "c:/Program Files/Netscape/Navigator"; michael@0: michael@0: sprintf (db, "%s/cert7.db", home); michael@0: michael@0: if ((fp = fopen (db, "r")) != NULL) { michael@0: fclose (fp); michael@0: cd = home; michael@0: } michael@0: } michael@0: michael@0: /* Try the current directory, I wonder if this michael@0: is really a good idea. Remember, Windows only.. */ michael@0: michael@0: if (cd == NULL) { michael@0: home = "."; michael@0: michael@0: sprintf (db, "%s/cert7.db", home); michael@0: michael@0: if ((fp = fopen (db, "r")) != NULL) { michael@0: fclose (fp); michael@0: cd = home; michael@0: } michael@0: } michael@0: michael@0: #endif michael@0: michael@0: if (!cd) { michael@0: PR_fprintf(errorFD, michael@0: "You must specify the location of your certificate directory\n"); michael@0: PR_fprintf(errorFD, michael@0: "with the -d option. Example: -d ~/.netscape in many cases with Unix.\n"); michael@0: errorCount++; michael@0: exit (ERRX); michael@0: } michael@0: michael@0: return cd; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * g i v e _ h e l p michael@0: */ michael@0: void give_help (int status) michael@0: { michael@0: if (status == SEC_ERROR_UNKNOWN_ISSUER) { michael@0: PR_fprintf(errorFD, michael@0: "The Certificate Authority (CA) for this certificate\n"); michael@0: PR_fprintf(errorFD, michael@0: "does not appear to be in your database. You should contact\n"); michael@0: PR_fprintf(errorFD, michael@0: "the organization which issued this certificate to obtain\n"); michael@0: PR_fprintf(errorFD, "a copy of its CA Certificate.\n"); michael@0: } michael@0: } michael@0: michael@0: michael@0: /************************************************************************** michael@0: * michael@0: * p r _ f g e t s michael@0: * michael@0: * fgets implemented with NSPR. michael@0: */ michael@0: char* michael@0: pr_fgets(char *buf, int size, PRFileDesc *file) michael@0: { michael@0: int i; michael@0: int status; michael@0: char c; michael@0: michael@0: i = 0; michael@0: while (i < size - 1) { michael@0: status = PR_Read(file, &c, 1); michael@0: if (status == -1) { michael@0: return NULL; michael@0: } else if (status == 0) { michael@0: if (i == 0) { michael@0: return NULL; michael@0: } michael@0: break; michael@0: } michael@0: buf[i++] = c; michael@0: if (c == '\n') { michael@0: break; michael@0: } michael@0: } michael@0: buf[i] = '\0'; michael@0: michael@0: return buf; michael@0: } michael@0: michael@0: