/* * R e m o v e A l l A r c * * Remove .arc directories that are lingering * from a previous run of signtool. * */ int RemoveAllArc(char *tree) { PRDir * dir; PRDirEntry * entry; char *archive = NULL; int retval = 0; dir = PR_OpenDir (tree); if (!dir) return - 1; for (entry = PR_ReadDir (dir, 0); entry; entry = PR_ReadDir (dir, 0)) { if (entry->name[0] == '.') { continue; } if (archive) PR_Free(archive); archive = PR_smprintf("%s/%s", tree, entry->name); if (PL_strcaserstr (entry->name, ".arc") == (entry->name + strlen(entry->name) - 4) ) { if (verbosity >= 0) { PR_fprintf(outputFD, "removing: %s\n", archive); } if (rm_dash_r(archive)) { PR_fprintf(errorFD, "Error removing %s\n", archive); errorCount++; retval = -1; goto finish; } } else if (is_dir(archive)) { if (RemoveAllArc(archive)) { retval = -1; goto finish; } } } finish: PR_CloseDir (dir); if (archive) PR_Free(archive); return retval; }
/* * r m _ d a s h _ r * * Remove a file, or a directory recursively. * */ int rm_dash_r(char *path) { PRDir *dir; PRDirEntry *entry; PRFileInfo fileinfo; char filename[FNSIZE]; if (PR_GetFileInfo(path, &fileinfo) != PR_SUCCESS) { /*fprintf(stderr, "Error: Unable to access %s\n", filename);*/ return -1; } if (fileinfo.type == PR_FILE_DIRECTORY) { dir = PR_OpenDir(path); if (!dir) { PR_fprintf(errorFD, "Error: Unable to open directory %s.\n", path); errorCount++; return -1; } /* Recursively delete all entries in the directory */ while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) { sprintf(filename, "%s/%s", path, entry->name); if (rm_dash_r(filename)) return -1; } if (PR_CloseDir(dir) != PR_SUCCESS) { PR_fprintf(errorFD, "Error: Could not close %s.\n", path); errorCount++; return -1; } /* Delete the directory itself */ if (PR_RmDir(path) != PR_SUCCESS) { PR_fprintf(errorFD, "Error: Unable to delete %s\n", path); errorCount++; return -1; } } else { if (PR_Delete(path) != PR_SUCCESS) { PR_fprintf(errorFD, "Error: Unable to delete %s\n", path); errorCount++; return -1; } } return 0; }
/********************************************************************* * * m a i n */ int main(int argc, char *argv[]) { PRBool readOnly; int retval = 0; outputFD = PR_STDOUT; errorFD = PR_STDERR; progName = argv[0]; if (argc < 2) { Usage(); } excludeDirs = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL); extensions = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL); if (parse_args(argc, argv)) { retval = -1; goto cleanup; } /* Parse the command file if one was given */ if (cmdFile) { if (ProcessCommandFile()) { retval = -1; goto cleanup; } } /* Set up output redirection */ if (outfile) { if (PR_Access(outfile, PR_ACCESS_EXISTS) == PR_SUCCESS) { /* delete the file if it is already present */ PR_fprintf(errorFD, "warning: %s already exists and will be overwritten.\n", outfile); warningCount++; if (PR_Delete(outfile) != PR_SUCCESS) { PR_fprintf(errorFD, "ERROR: unable to delete %s.\n", outfile); errorCount++; exit(ERRX); } } outputFD = PR_Open(outfile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777); if (!outputFD) { PR_fprintf(errorFD, "ERROR: Unable to create %s.\n", outfile); errorCount++; exit(ERRX); } errorFD = outputFD; } /* This seems to be a fairly common user error */ if (verify && list_certs > 0) { PR_fprintf(errorFD, "%s: Can't use -l and -v at the same time\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* -J assumes -Z now */ if (javascript && zipfile) { PR_fprintf(errorFD, "%s: Can't use -J and -Z at the same time\n", PROGRAM_NAME); PR_fprintf(errorFD, "%s: -J option will create the jar files for you\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* -X needs -Z */ if (xpi_arc && !zipfile) { PR_fprintf(errorFD, "%s: option XPI (-X) requires option jarfile (-Z)\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* Less common mixing of -L with various options */ if (list_certs > 0 && (tell_who || zipfile || javascript || scriptdir || extensionsGiven || exclusionsGiven || install_script)) { PR_fprintf(errorFD, "%s: Can't use -l or -L with that option\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } if (!cert_dir) cert_dir = get_default_cert_dir(); VerifyCertDir(cert_dir, keyName); if (compression_level < MIN_COMPRESSION_LEVEL || compression_level > MAX_COMPRESSION_LEVEL) { PR_fprintf(errorFD, "Compression level must be between %d and %d.\n", MIN_COMPRESSION_LEVEL, MAX_COMPRESSION_LEVEL); errorCount++; retval = -1; goto cleanup; } if (jartree && !keyName) { PR_fprintf(errorFD, "You must specify a key with which to sign.\n"); errorCount++; retval = -1; goto cleanup; } readOnly = (genkey == NULL); /* only key generation requires write */ if (InitCrypto(cert_dir, readOnly)) { PR_fprintf(errorFD, "ERROR: Cryptographic initialization failed.\n"); errorCount++; retval = -1; goto cleanup; } if (enableOCSP) { SECStatus rv = CERT_EnableOCSPChecking(CERT_GetDefaultCertDB()); if (rv != SECSuccess) { PR_fprintf(errorFD, "ERROR: Attempt to enable OCSP Checking failed.\n"); errorCount++; retval = -1; } } if (verify) { if (VerifyJar(verify)) { errorCount++; retval = -1; goto cleanup; } } else if (list_certs) { if (ListCerts(keyName, list_certs)) { errorCount++; retval = -1; goto cleanup; } } else if (list_modules) { JarListModules(); } else if (genkey) { if (GenerateCert(genkey, keySize, token)) { errorCount++; retval = -1; goto cleanup; } } else if (tell_who) { if (JarWho(tell_who)) { errorCount++; retval = -1; goto cleanup; } } else if (javascript && jartree) { /* make sure directory exists */ PRDir *dir; dir = PR_OpenDir(jartree); if (!dir) { PR_fprintf(errorFD, "ERROR: unable to open directory %s.\n", jartree); errorCount++; retval = -1; goto cleanup; } else { PR_CloseDir(dir); } /* undo junk from prior runs of signtool*/ if (RemoveAllArc(jartree)) { PR_fprintf(errorFD, "Error removing archive directories under %s\n", jartree); errorCount++; retval = -1; goto cleanup; } /* traverse all the htm|html files in the directory */ if (InlineJavaScript(jartree, !noRecurse)) { retval = -1; goto cleanup; } /* sign any resultant .arc directories created in above step */ if (SignAllArc(jartree, keyName, javascript, metafile, install_script, optimize, !noRecurse)) { retval = -1; goto cleanup; } if (!leaveArc) { RemoveAllArc(jartree); } if (errorCount > 0 || warningCount > 0) { PR_fprintf(outputFD, "%d error%s, %d warning%s.\n", errorCount, errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s"); } else { PR_fprintf(outputFD, "Directory %s signed successfully.\n", jartree); } } else if (jartree) { SignArchive(jartree, keyName, zipfile, javascript, metafile, install_script, optimize, !noRecurse); } else Usage(); cleanup: if (extensions) { PL_HashTableDestroy(extensions); extensions = NULL; } if (excludeDirs) { PL_HashTableDestroy(excludeDirs); excludeDirs = NULL; } if (outputFD != PR_STDOUT) { PR_Close(outputFD); } rm_dash_r(TMP_OUTPUT); if (retval == 0) { if (NSS_Shutdown() != SECSuccess) { exit(1); } } return retval; }
/************************************************************************* * * V e r i f y J a r */ int VerifyJar(char *filename) { FILE *fp; int ret; int status; int failed = 0; char *err; JAR *jar; JAR_Context *ctx; JAR_Item *it; jar = JAR_new(); if ((fp = fopen(filename, "r")) == NULL) { perror(filename); exit(ERRX); } else fclose(fp); JAR_set_callback(JAR_CB_SIGNAL, jar, jar_cb); status = JAR_pass_archive(jar, jarArchGuess, filename, "some-url"); if (status < 0 || jar->valid < 0) { failed = 1; PR_fprintf(outputFD, "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", filename); if (status < 0) { const char *errtext; if (status >= JAR_BASE && status <= JAR_BASE_END) { errtext = JAR_get_error(status); } else { errtext = SECU_Strerror(PORT_GetError()); } PR_fprintf(outputFD, " (reported reason: %s)\n\n", errtext); /* corrupt files should not have their contents listed */ if (status == JAR_ERR_CORRUPT) return -1; } PR_fprintf(outputFD, "entries shown below will have their digests checked only.\n"); jar->valid = 0; } else PR_fprintf(outputFD, "archive \"%s\" has passed crypto verification.\n", filename); if (verify_global(jar)) failed = 1; PR_fprintf(outputFD, "\n"); PR_fprintf(outputFD, "%16s %s\n", "status", "path"); PR_fprintf(outputFD, "%16s %s\n", "------------", "-------------------"); ctx = JAR_find(jar, NULL, jarTypeMF); while (JAR_find_next(ctx, &it) >= 0) { if (it && it->pathname) { rm_dash_r(TMP_OUTPUT); ret = JAR_verified_extract(jar, it->pathname, TMP_OUTPUT); /* if (ret < 0) printf ("error %d on %s\n", ret, it->pathname); */ if (ret < 0) failed = 1; if (ret == JAR_ERR_PNF) err = "NOT PRESENT"; else if (ret == JAR_ERR_HASH) err = "HASH FAILED"; else err = "NOT VERIFIED"; PR_fprintf(outputFD, "%16s %s\n", ret >= 0 ? "verified" : err, it->pathname); if (ret != 0 && ret != JAR_ERR_PNF && ret != JAR_ERR_HASH) PR_fprintf(outputFD, " (reason: %s)\n", JAR_get_error(ret)); } } JAR_find_end(ctx); if (status < 0 || jar->valid < 0) { failed = 1; PR_fprintf(outputFD, "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", filename); give_help(status); } JAR_destroy(jar); if (failed) return -1; return 0; }
/*************************************************************************** * * v e r i f y _ g l o b a l */ static int verify_global(JAR *jar) { FILE *fp; JAR_Context *ctx; JAR_Item *it; JAR_Digest *globaldig; char *ext; unsigned char *md5_digest, *sha1_digest; unsigned int sha1_length, md5_length; int retval = 0; char buf[BUFSIZ]; ctx = JAR_find(jar, "*", jarTypePhy); while (JAR_find_next(ctx, &it) >= 0) { if (!PORT_Strncmp(it->pathname, "META-INF", 8)) { for (ext = it->pathname; *ext; ext++) ; while (ext > it->pathname && *ext != '.') ext--; if (verbosity >= 0) { if (!PORT_Strcasecmp(ext, ".rsa")) { PR_fprintf(outputFD, "found a RSA signature file: %s\n", it->pathname); } if (!PORT_Strcasecmp(ext, ".dsa")) { PR_fprintf(outputFD, "found a DSA signature file: %s\n", it->pathname); } if (!PORT_Strcasecmp(ext, ".mf")) { PR_fprintf(outputFD, "found a MF master manifest file: %s\n", it->pathname); } } if (!PORT_Strcasecmp(ext, ".sf")) { if (verbosity >= 0) { PR_fprintf(outputFD, "found a SF signature manifest file: %s\n", it->pathname); } rm_dash_r(TMP_OUTPUT); if (JAR_extract(jar, it->pathname, TMP_OUTPUT) < 0) { PR_fprintf(errorFD, "%s: error extracting %s\n", PROGRAM_NAME, it->pathname); errorCount++; retval = -1; continue; } md5_digest = NULL; sha1_digest = NULL; if ((fp = fopen(TMP_OUTPUT, "rb")) != NULL) { while (fgets(buf, BUFSIZ, fp)) { char *s; if (*buf == 0 || *buf == '\n' || *buf == '\r') break; for (s = buf; *s && *s != '\n' && *s != '\r'; s++) ; *s = 0; if (!PORT_Strncmp(buf, "MD5-Digest: ", 12)) { md5_digest = ATOB_AsciiToData(buf + 12, &md5_length); } if (!PORT_Strncmp(buf, "SHA1-Digest: ", 13)) { sha1_digest = ATOB_AsciiToData(buf + 13, &sha1_length); } if (!PORT_Strncmp(buf, "SHA-Digest: ", 12)) { sha1_digest = ATOB_AsciiToData(buf + 12, &sha1_length); } } globaldig = jar->globalmeta; if (globaldig && md5_digest && verbosity >= 0) { PR_fprintf(outputFD, " md5 digest on global metainfo: %s\n", PORT_Memcmp(md5_digest, globaldig->md5, MD5_LENGTH) ? "no match" : "match"); } if (globaldig && sha1_digest && verbosity >= 0) { PR_fprintf(outputFD, " sha digest on global metainfo: %s\n", PORT_Memcmp(sha1_digest, globaldig->sha1, SHA1_LENGTH) ? "no match" : "match"); } if (globaldig == NULL && verbosity >= 0) { PR_fprintf(outputFD, "global metadigest is not available, strange.\n"); } PORT_Free(md5_digest); PORT_Free(sha1_digest); fclose(fp); } } } } JAR_find_end(ctx); return retval; }
/* ///////////////////////////////////////////////////////////////////////// // actually run the installation, copying files to and fro */ static Pk11Install_Error DoInstall(JAR *jar, const char *installDir, const char *tempDir, Pk11Install_Platform *platform, PRFileDesc *feedback, PRBool noverify) { Pk11Install_File *file; Pk11Install_Error ret; char *reldir; char *dest; char *modDest; char *cp; int i; int status; char *tempname, *temp; StringList executables; StringNode *execNode; PRProcessAttr *attr; PRProcess *proc; char *argv[2]; char *envp[1]; int errcode; ret = PK11_INSTALL_UNSPECIFIED; reldir = NULL; dest = NULL; modDest = NULL; tempname = NULL; StringList_new(&executables); /* // Create Temporary directory */ tempname = PR_smprintf("%s/%s", tempDir, TEMPORARY_DIRECTORY_NAME); if (PR_Access(tempname, PR_ACCESS_EXISTS) == PR_SUCCESS) { /* Left over from previous run? Delete it. */ rm_dash_r(tempname); } if (PR_MkDir(tempname, 0700) != PR_SUCCESS) { error(PK11_INSTALL_CREATE_DIR, tempname); ret = PK11_INSTALL_CREATE_DIR; goto loser; } /* // Install all the files */ for (i = 0; i < platform->numFiles; i++) { file = &platform->files[i]; if (file->relativePath) { PRBool foundMarker = PR_FALSE; reldir = PR_Strdup(file->relativePath); /* Replace all the markers with the directories for which they stand */ while (1) { if ((cp = PL_strcasestr(reldir, ROOT_MARKER))) { /* Has a %root% marker */ *cp = '\0'; temp = PR_smprintf("%s%s%s", reldir, installDir, cp + strlen(ROOT_MARKER)); PR_Free(reldir); reldir = temp; foundMarker = PR_TRUE; } else if ((cp = PL_strcasestr(reldir, TEMP_MARKER))) { /* Has a %temp% marker */ *cp = '\0'; temp = PR_smprintf("%s%s%s", reldir, tempname, cp + strlen(TEMP_MARKER)); PR_Free(reldir); reldir = temp; foundMarker = PR_TRUE; } else { break; } } if (!foundMarker) { /* Has no markers...this isn't really a relative directory */ error(PK11_INSTALL_BOGUS_REL_DIR, file->relativePath); ret = PK11_INSTALL_BOGUS_REL_DIR; goto loser; } dest = reldir; reldir = NULL; } else if (file->absolutePath) { dest = PR_Strdup(file->absolutePath); } /* Remember if this is the module file, we'll need to add it later */ if (i == platform->modFile) { modDest = PR_Strdup(dest); } /* Remember is this is an executable, we'll need to run it later */ if (file->executable) { StringList_Append(&executables, dest); /*executables.Append(dest);*/ } /* Make sure the directory we are targetting exists */ if (make_dirs(dest, file->permissions)) { ret = PK11_INSTALL_CREATE_DIR; goto loser; } /* Actually extract the file onto the filesystem */ if (noverify) { status = JAR_extract(jar, (char *)file->jarPath, dest); } else { status = JAR_verified_extract(jar, (char *)file->jarPath, dest); } if (status) { if (status >= JAR_BASE && status <= JAR_BASE_END) { error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, JAR_get_error(status)); } else { error(PK11_INSTALL_JAR_EXTRACT, file->jarPath, mySECU_ErrorString(PORT_GetError())); } ret = PK11_INSTALL_JAR_EXTRACT; goto loser; } if (feedback) { PR_fprintf(feedback, msgStrings[INSTALLED_FILE_MSG], file->jarPath, dest); } /* no NSPR command to change permissions? */ #ifdef XP_UNIX chmod(dest, file->permissions); #endif /* Memory clean-up tasks */ if (reldir) { PR_Free(reldir); reldir = NULL; } if (dest) { PR_Free(dest); dest = NULL; } } /* Make sure we found the module file */ if (!modDest) { /* Internal problem here, since every platform is supposed to have a module file */ error(PK11_INSTALL_NO_MOD_FILE, platform->moduleName); ret = PK11_INSTALL_NO_MOD_FILE; goto loser; } /* // Execute any executable files */ { argv[1] = NULL; envp[0] = NULL; for (execNode = executables.head; execNode; execNode = execNode->next) { attr = PR_NewProcessAttr(); argv[0] = PR_Strdup(execNode->str); /* Announce our intentions */ if (feedback) { PR_fprintf(feedback, msgStrings[EXEC_FILE_MSG], execNode->str); } /* start the process */ if (!(proc = PR_CreateProcess(execNode->str, argv, envp, attr))) { PR_Free(argv[0]); PR_DestroyProcessAttr(attr); error(PK11_INSTALL_EXEC_FILE, execNode->str); ret = PK11_INSTALL_EXEC_FILE; goto loser; } /* wait for it to finish */ if (PR_WaitProcess(proc, &errcode) != PR_SUCCESS) { PR_Free(argv[0]); PR_DestroyProcessAttr(attr); error(PK11_INSTALL_WAIT_PROCESS, execNode->str); ret = PK11_INSTALL_WAIT_PROCESS; goto loser; } /* What happened? */ if (errcode) { /* process returned an error */ error(PK11_INSTALL_PROC_ERROR, execNode->str, errcode); } else if (feedback) { /* process ran successfully */ PR_fprintf(feedback, msgStrings[EXEC_SUCCESS], execNode->str); } PR_Free(argv[0]); PR_DestroyProcessAttr(attr); } } /* // Add the module */ status = Pk11Install_AddNewModule((char *)platform->moduleName, (char *)modDest, platform->mechFlags, platform->cipherFlags); if (status != SECSuccess) { error(PK11_INSTALL_ADD_MODULE, platform->moduleName); ret = PK11_INSTALL_ADD_MODULE; goto loser; } if (feedback) { PR_fprintf(feedback, msgStrings[INSTALLED_MODULE_MSG], platform->moduleName); } if (feedback) { PR_fprintf(feedback, msgStrings[INSTALLATION_COMPLETE_MSG]); } ret = PK11_INSTALL_SUCCESS; loser: if (reldir) { PR_Free(reldir); } if (dest) { PR_Free(dest); } if (modDest) { PR_Free(modDest); } if (tempname) { PRFileInfo info; if (PR_GetFileInfo(tempname, &info) == PR_SUCCESS) { if (info.type == PR_FILE_DIRECTORY) { /* Recursively remove temporary directory */ if (rm_dash_r(tempname)) { error(PK11_INSTALL_REMOVE_DIR, tempname); ret = PK11_INSTALL_REMOVE_DIR; } } } PR_Free(tempname); } StringList_delete(&executables); return ret; }