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: michael@0: michael@0: static int jar_cb(int status, JAR *jar, const char *metafile, michael@0: char *pathname, char *errortext); michael@0: static int verify_global (JAR *jar); michael@0: michael@0: /************************************************************************* michael@0: * michael@0: * V e r i f y J a r michael@0: */ michael@0: int michael@0: VerifyJar(char *filename) michael@0: { michael@0: FILE * fp; michael@0: michael@0: int ret; michael@0: int status; michael@0: int failed = 0; michael@0: char *err; michael@0: michael@0: JAR * jar; michael@0: JAR_Context * ctx; michael@0: michael@0: JAR_Item * it; michael@0: michael@0: jar = JAR_new(); michael@0: michael@0: if ((fp = fopen (filename, "r")) == NULL) { michael@0: perror (filename); michael@0: exit (ERRX); michael@0: } else michael@0: fclose (fp); michael@0: michael@0: JAR_set_callback (JAR_CB_SIGNAL, jar, jar_cb); michael@0: michael@0: michael@0: status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url"); michael@0: michael@0: if (status < 0 || jar->valid < 0) { michael@0: failed = 1; michael@0: PR_fprintf(outputFD, michael@0: "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", michael@0: filename); michael@0: if (status < 0) { michael@0: const char *errtext; michael@0: michael@0: if (status >= JAR_BASE && status <= JAR_BASE_END) { michael@0: errtext = JAR_get_error (status); michael@0: } else { michael@0: errtext = SECU_Strerror(PORT_GetError()); michael@0: } michael@0: michael@0: PR_fprintf(outputFD, " (reported reason: %s)\n\n", michael@0: errtext); michael@0: michael@0: /* corrupt files should not have their contents listed */ michael@0: michael@0: if (status == JAR_ERR_CORRUPT) michael@0: return - 1; michael@0: } michael@0: PR_fprintf(outputFD, michael@0: "entries shown below will have their digests checked only.\n"); michael@0: jar->valid = 0; michael@0: } else michael@0: PR_fprintf(outputFD, michael@0: "archive \"%s\" has passed crypto verification.\n", filename); michael@0: michael@0: if (verify_global (jar)) michael@0: failed = 1; michael@0: michael@0: PR_fprintf(outputFD, "\n"); michael@0: PR_fprintf(outputFD, "%16s %s\n", "status", "path"); michael@0: PR_fprintf(outputFD, "%16s %s\n", "------------", "-------------------"); michael@0: michael@0: ctx = JAR_find (jar, NULL, jarTypeMF); michael@0: michael@0: while (JAR_find_next (ctx, &it) >= 0) { michael@0: if (it && it->pathname) { michael@0: rm_dash_r(TMP_OUTPUT); michael@0: ret = JAR_verified_extract (jar, it->pathname, TMP_OUTPUT); michael@0: /* if (ret < 0) printf ("error %d on %s\n", ret, it->pathname); */ michael@0: if (ret < 0) michael@0: failed = 1; michael@0: michael@0: if (ret == JAR_ERR_PNF) michael@0: err = "NOT PRESENT"; michael@0: else if (ret == JAR_ERR_HASH) michael@0: err = "HASH FAILED"; michael@0: else michael@0: err = "NOT VERIFIED"; michael@0: michael@0: PR_fprintf(outputFD, "%16s %s\n", michael@0: ret >= 0 ? "verified" : err, it->pathname); michael@0: michael@0: if (ret != 0 && ret != JAR_ERR_PNF && ret != JAR_ERR_HASH) michael@0: PR_fprintf(outputFD, " (reason: %s)\n", michael@0: JAR_get_error (ret)); michael@0: } michael@0: } michael@0: michael@0: JAR_find_end (ctx); michael@0: michael@0: if (status < 0 || jar->valid < 0) { michael@0: failed = 1; michael@0: PR_fprintf(outputFD, michael@0: "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", michael@0: filename); michael@0: give_help (status); michael@0: } michael@0: michael@0: JAR_destroy (jar); michael@0: michael@0: if (failed) michael@0: return - 1; michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * michael@0: * v e r i f y _ g l o b a l michael@0: */ michael@0: static int michael@0: verify_global (JAR *jar) michael@0: { michael@0: FILE * fp; michael@0: JAR_Context * ctx; michael@0: JAR_Item * it; michael@0: JAR_Digest * globaldig; michael@0: char * ext; michael@0: unsigned char *md5_digest, *sha1_digest; michael@0: unsigned int sha1_length, md5_length; michael@0: int retval = 0; michael@0: char buf [BUFSIZ]; michael@0: michael@0: ctx = JAR_find (jar, "*", jarTypePhy); michael@0: michael@0: while (JAR_find_next (ctx, &it) >= 0) { michael@0: if (!PORT_Strncmp (it->pathname, "META-INF", 8)) { michael@0: for (ext = it->pathname; *ext; ext++) michael@0: ; michael@0: while (ext > it->pathname && *ext != '.') michael@0: ext--; michael@0: michael@0: if (verbosity >= 0) { michael@0: if (!PORT_Strcasecmp (ext, ".rsa")) { michael@0: PR_fprintf(outputFD, "found a RSA signature file: %s\n", michael@0: it->pathname); michael@0: } michael@0: michael@0: if (!PORT_Strcasecmp (ext, ".dsa")) { michael@0: PR_fprintf(outputFD, "found a DSA signature file: %s\n", michael@0: it->pathname); michael@0: } michael@0: michael@0: if (!PORT_Strcasecmp (ext, ".mf")) { michael@0: PR_fprintf(outputFD, michael@0: "found a MF master manifest file: %s\n", michael@0: it->pathname); michael@0: } michael@0: } michael@0: michael@0: if (!PORT_Strcasecmp (ext, ".sf")) { michael@0: if (verbosity >= 0) { michael@0: PR_fprintf(outputFD, michael@0: "found a SF signature manifest file: %s\n", michael@0: it->pathname); michael@0: } michael@0: michael@0: rm_dash_r(TMP_OUTPUT); michael@0: if (JAR_extract (jar, it->pathname, TMP_OUTPUT) < 0) { michael@0: PR_fprintf(errorFD, "%s: error extracting %s\n", michael@0: PROGRAM_NAME, it->pathname); michael@0: errorCount++; michael@0: retval = -1; michael@0: continue; michael@0: } michael@0: michael@0: md5_digest = NULL; michael@0: sha1_digest = NULL; michael@0: michael@0: if ((fp = fopen (TMP_OUTPUT, "rb")) != NULL) { michael@0: while (fgets (buf, BUFSIZ, fp)) { michael@0: char *s; michael@0: michael@0: if (*buf == 0 || *buf == '\n' || *buf == '\r') michael@0: break; michael@0: michael@0: for (s = buf; *s && *s != '\n' && *s != '\r'; s++) michael@0: ; michael@0: *s = 0; michael@0: michael@0: if (!PORT_Strncmp (buf, "MD5-Digest: ", 12)) { michael@0: md5_digest = michael@0: ATOB_AsciiToData (buf + 12, &md5_length); michael@0: } michael@0: if (!PORT_Strncmp (buf, "SHA1-Digest: ", 13)) { michael@0: sha1_digest = michael@0: ATOB_AsciiToData (buf + 13, &sha1_length); michael@0: } michael@0: if (!PORT_Strncmp (buf, "SHA-Digest: ", 12)) { michael@0: sha1_digest = michael@0: ATOB_AsciiToData (buf + 12, &sha1_length); michael@0: } michael@0: } michael@0: michael@0: globaldig = jar->globalmeta; michael@0: michael@0: if (globaldig && md5_digest && verbosity >= 0) { michael@0: PR_fprintf(outputFD, michael@0: " md5 digest on global metainfo: %s\n", michael@0: PORT_Memcmp(md5_digest, globaldig->md5, MD5_LENGTH) michael@0: ? "no match" : "match"); michael@0: } michael@0: michael@0: if (globaldig && sha1_digest && verbosity >= 0) { michael@0: PR_fprintf(outputFD, michael@0: " sha digest on global metainfo: %s\n", michael@0: PORT_Memcmp(sha1_digest, globaldig->sha1, SHA1_LENGTH) michael@0: ? "no match" : "match"); michael@0: } michael@0: michael@0: if (globaldig == NULL && verbosity >= 0) { michael@0: PR_fprintf(outputFD, michael@0: "global metadigest is not available, strange.\n"); michael@0: } michael@0: michael@0: fclose (fp); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: JAR_find_end (ctx); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * michael@0: * J a r W h o michael@0: */ michael@0: int michael@0: JarWho(char *filename) michael@0: { michael@0: FILE * fp; michael@0: michael@0: JAR * jar; michael@0: JAR_Context * ctx; michael@0: michael@0: int status; michael@0: int retval = 0; michael@0: michael@0: JAR_Item * it; michael@0: JAR_Cert * fing; michael@0: michael@0: CERTCertificate * cert, *prev = NULL; michael@0: michael@0: jar = JAR_new(); michael@0: michael@0: if ((fp = fopen (filename, "r")) == NULL) { michael@0: perror (filename); michael@0: exit (ERRX); michael@0: } michael@0: fclose (fp); michael@0: michael@0: status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url"); michael@0: michael@0: if (status < 0 || jar->valid < 0) { michael@0: PR_fprintf(outputFD, michael@0: "NOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", michael@0: filename); michael@0: retval = -1; michael@0: if (jar->valid < 0 || status != -1) { michael@0: const char *errtext; michael@0: michael@0: if (status >= JAR_BASE && status <= JAR_BASE_END) { michael@0: errtext = JAR_get_error (status); michael@0: } else { michael@0: errtext = SECU_Strerror(PORT_GetError()); michael@0: } michael@0: michael@0: PR_fprintf(outputFD, " (reported reason: %s)\n\n", errtext); michael@0: } michael@0: } michael@0: michael@0: PR_fprintf(outputFD, "\nSigner information:\n\n"); michael@0: michael@0: ctx = JAR_find (jar, NULL, jarTypeSign); michael@0: michael@0: while (JAR_find_next (ctx, &it) >= 0) { michael@0: fing = (JAR_Cert * ) it->data; michael@0: cert = fing->cert; michael@0: michael@0: if (cert) { michael@0: if (prev == cert) michael@0: break; michael@0: michael@0: if (cert->nickname) michael@0: PR_fprintf(outputFD, "nickname: %s\n", cert->nickname); michael@0: if (cert->subjectName) michael@0: PR_fprintf(outputFD, "subject name: %s\n", michael@0: cert->subjectName); michael@0: if (cert->issuerName) michael@0: PR_fprintf(outputFD, "issuer name: %s\n", cert->issuerName); michael@0: } else { michael@0: PR_fprintf(outputFD, "no certificate could be found\n"); michael@0: retval = -1; michael@0: } michael@0: michael@0: prev = cert; michael@0: } michael@0: michael@0: JAR_find_end (ctx); michael@0: michael@0: JAR_destroy (jar); michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: /************************************************************************ michael@0: * j a r _ c b michael@0: */ michael@0: static int jar_cb(int status, JAR *jar, const char *metafile, michael@0: char *pathname, char *errortext) michael@0: { michael@0: PR_fprintf(errorFD, "error %d: %s IN FILE %s\n", status, errortext, michael@0: pathname); michael@0: errorCount++; michael@0: return 0; michael@0: } michael@0: michael@0: