summaryrefslogtreecommitdiffstats
path: root/security/nss/cmd/signtool/sign.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /security/nss/cmd/signtool/sign.c
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/cmd/signtool/sign.c')
-rw-r--r--security/nss/cmd/signtool/sign.c872
1 files changed, 872 insertions, 0 deletions
diff --git a/security/nss/cmd/signtool/sign.c b/security/nss/cmd/signtool/sign.c
new file mode 100644
index 0000000000..168bb1b9ec
--- /dev/null
+++ b/security/nss/cmd/signtool/sign.c
@@ -0,0 +1,872 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "signtool.h"
+#include "zip.h"
+#include "prmem.h"
+#include "blapi.h"
+#include "sechash.h" /* for HASH_GetHashObject() */
+
+static int create_pk7(char *dir, char *keyName, int *keyType);
+static int jar_find_key_type(CERTCertificate *cert);
+static int manifesto(char *dirname, char *install_script, PRBool recurse);
+static int manifesto_fn(char *relpath, char *basedir, char *reldir,
+ char *filename, void *arg);
+static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
+ char *filename, void *arg);
+static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
+ char *filename, void *arg);
+static int add_meta(FILE *fp, char *name);
+static int SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert);
+static int generate_SF_file(char *manifile, char *who);
+static int calculate_MD5_range(FILE *fp, long r1, long r2,
+ JAR_Digest *dig);
+static void SignOut(void *arg, const char *buf, unsigned long len);
+
+static char *metafile = NULL;
+static int optimize = 0;
+static FILE *mf;
+static ZIPfile *zipfile = NULL;
+
+/*
+ * S i g n A r c h i v e
+ *
+ * Sign an individual archive tree. A directory
+ * called META-INF is created underneath this.
+ *
+ */
+int
+SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
+ char *meta_file, char *install_script, int _optimize, PRBool recurse)
+{
+ int status;
+ char tempfn[FNSIZE], fullfn[FNSIZE];
+ int keyType = rsaKey;
+ int count;
+
+ metafile = meta_file;
+ optimize = _optimize;
+
+ /* To create XPI compatible Archive manifesto() must be run before
+ * the zipfile is opened. This is so the signed files are not added
+ * the archive before the crucial rsa/dsa file*/
+ if (xpi_arc) {
+ manifesto(tree, install_script, recurse);
+ }
+
+ if (zip_file) {
+ zipfile = JzipOpen(zip_file, NULL /*no comment*/);
+ }
+
+ /*Sign and add files to the archive normally with manifesto()*/
+ if (!xpi_arc) {
+ manifesto(tree, install_script, recurse);
+ }
+
+ if (keyName) {
+ status = create_pk7(tree, keyName, &keyType);
+ if (status < 0) {
+ PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
+ tree);
+ errorCount++;
+ exit(ERRX);
+ }
+ }
+
+ /* Add the rsa/dsa file as the first file in the archive. This is crucial
+ * for a XPInstall compatible archive */
+ if (xpi_arc) {
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "%s \n", XPI_TEXT);
+ }
+
+ /* rsa/dsa to zip */
+ count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, (keyType == dsaKey ? "dsa" : "rsa"));
+ if (count >= sizeof(tempfn)) {
+ PR_fprintf(errorFD, "unable to write key metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
+ if (count >= sizeof(fullfn)) {
+ PR_fprintf(errorFD, "unable to write key metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ JzipAdd(fullfn, tempfn, zipfile, compression_level);
+
+ /* Loop through all files & subdirectories, add to archive */
+ foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
+ (void *)NULL)
+ ;
+ }
+ /* mf to zip */
+ strcpy(tempfn, "META-INF/manifest.mf");
+ count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
+ if (count >= sizeof(fullfn)) {
+ PR_fprintf(errorFD, "unable to write manifest\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ JzipAdd(fullfn, tempfn, zipfile, compression_level);
+
+ /* sf to zip */
+ count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.sf", base);
+ if (count >= sizeof(tempfn)) {
+ PR_fprintf(errorFD, "unable to write sf metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
+ if (count >= sizeof(fullfn)) {
+ PR_fprintf(errorFD, "unable to write sf metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ JzipAdd(fullfn, tempfn, zipfile, compression_level);
+
+ /* Add the rsa/dsa file to the zip archive normally */
+ if (!xpi_arc) {
+ /* rsa/dsa to zip */
+ count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, (keyType == dsaKey ? "dsa" : "rsa"));
+ if (count >= sizeof(tempfn)) {
+ PR_fprintf(errorFD, "unable to write key metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
+ if (count >= sizeof(fullfn)) {
+ PR_fprintf(errorFD, "unable to write key metadata\n");
+ errorCount++;
+ exit(ERRX);
+ }
+ JzipAdd(fullfn, tempfn, zipfile, compression_level);
+ }
+
+ JzipClose(zipfile);
+
+ if (verbosity >= 0) {
+ if (javascript) {
+ PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
+ zip_file);
+ } else {
+ PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
+ tree);
+ }
+ }
+
+ return 0;
+}
+
+typedef struct {
+ char *keyName;
+ int javascript;
+ char *metafile;
+ char *install_script;
+ int optimize;
+} SignArcInfo;
+
+/*
+ * S i g n A l l A r c
+ *
+ * Javascript may generate multiple .arc directories, one
+ * for each jar archive needed. Sign them all.
+ *
+ */
+int
+SignAllArc(char *jartree, char *keyName, int javascript, char *metafilename,
+ char *install_script, int optimize_level, PRBool recurse)
+{
+ SignArcInfo info;
+
+ info.keyName = keyName;
+ info.javascript = javascript;
+ info.metafile = metafilename;
+ info.install_script = install_script;
+ info.optimize = optimize_level;
+
+ return foreach (jartree, "", sign_all_arc_fn, recurse,
+ PR_TRUE /*include dirs*/, (void *)&info);
+}
+
+static int
+sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
+ void *arg)
+{
+ char *zipfilename = NULL;
+ char *arc = NULL, *archive = NULL;
+ int retval = 0;
+ SignArcInfo *infop = (SignArcInfo *)arg;
+
+ /* Make sure there is one and only one ".arc" in the relative path,
+ * and that it is at the end of the path (don't sign .arcs within .arcs) */
+ if ((PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) - 4) &&
+ (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4)) {
+
+ if (!infop) {
+ PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
+ errorCount++;
+ retval = -1;
+ goto finish;
+ }
+ archive = PR_smprintf("%s/%s", basedir, relpath);
+
+ zipfilename = PL_strdup(archive);
+ arc = PORT_Strrchr(zipfilename, '.');
+
+ if (arc == NULL) {
+ PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
+ errorCount++;
+ retval = -1;
+ goto finish;
+ }
+
+ PL_strcpy(arc, ".jar");
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "\nsigning: %s\n", zipfilename);
+ }
+ retval = SignArchive(archive, infop->keyName, zipfilename,
+ infop->javascript, infop->metafile, infop->install_script,
+ infop->optimize, PR_TRUE /* recurse */);
+ }
+finish:
+ if (archive)
+ PR_Free(archive);
+ if (zipfilename)
+ PR_Free(zipfilename);
+
+ return retval;
+}
+
+/*********************************************************************
+ *
+ * c r e a t e _ p k 7
+ */
+static int
+create_pk7(char *dir, char *keyName, int *keyType)
+{
+ int status = 0;
+ char *file_ext;
+
+ CERTCertificate *cert;
+ CERTCertDBHandle *db;
+
+ FILE *in, *out;
+
+ char sf_file[FNSIZE];
+ char pk7_file[FNSIZE];
+
+ /* open cert database */
+ db = CERT_GetDefaultCertDB();
+
+ if (db == NULL)
+ return -1;
+
+ /* find cert */
+ /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
+ cert = PK11_FindCertFromNickname(keyName, &pwdata);
+
+ if (cert == NULL) {
+ SECU_PrintError(PROGRAM_NAME,
+ "Cannot find the cert \"%s\"", keyName);
+ return -1;
+ }
+
+ /* determine the key type, which sets the extension for pkcs7 object */
+
+ *keyType = jar_find_key_type(cert);
+ file_ext = (*keyType == dsaKey) ? "dsa" : "rsa";
+
+ snprintf(sf_file, sizeof(sf_file), "%s/META-INF/%s.sf", dir, base);
+ snprintf(pk7_file, sizeof(pk7_file), "%s/META-INF/%s.%s", dir, base, file_ext);
+
+ if ((in = fopen(sf_file, "rb")) == NULL) {
+ PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
+ sf_file);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ if ((out = fopen(pk7_file, "wb")) == NULL) {
+ PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
+ sf_file);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ status = SignFile(out, in, cert);
+
+ CERT_DestroyCertificate(cert);
+ fclose(in);
+ fclose(out);
+
+ if (status) {
+ PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
+ PROGRAM_NAME, SECU_Strerror(PORT_GetError()));
+ errorCount++;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * j a r _ f i n d _ k e y _ t y p e
+ *
+ * Determine the key type for a given cert, which
+ * should be rsaKey or dsaKey. Any error return 0.
+ *
+ */
+static int
+jar_find_key_type(CERTCertificate *cert)
+{
+ SECKEYPrivateKey *privk = NULL;
+ KeyType keyType;
+
+ /* determine its type */
+ privk = PK11_FindKeyByAnyCert(cert, &pwdata);
+ if (privk == NULL) {
+ PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
+ warningCount++;
+ return 0;
+ }
+
+ keyType = privk->keyType;
+ SECKEY_DestroyPrivateKey(privk);
+ return keyType;
+}
+
+/*
+ * m a n i f e s t o
+ *
+ * Run once for every subdirectory in which a
+ * manifest is to be created -- usually exactly once.
+ *
+ */
+static int
+manifesto(char *dirname, char *install_script, PRBool recurse)
+{
+ char metadir[FNSIZE], sfname[FNSIZE];
+
+ /* Create the META-INF directory to hold signing info */
+
+ if (PR_Access(dirname, PR_ACCESS_READ_OK)) {
+ PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
+ PROGRAM_NAME, dirname);
+ errorCount++;
+ perror(dirname);
+ exit(ERRX);
+ }
+
+ if (PR_Access(dirname, PR_ACCESS_WRITE_OK)) {
+ PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
+ PROGRAM_NAME, dirname);
+ errorCount++;
+ perror(dirname);
+ exit(ERRX);
+ }
+
+ snprintf(metadir, sizeof(metadir), "%s/META-INF", dirname);
+
+ strcpy(sfname, metadir);
+
+ PR_MkDir(metadir, 0777);
+
+ strcat(metadir, "/");
+ strcat(metadir, MANIFEST);
+
+ if ((mf = fopen(metadir, "wb")) == NULL) {
+ perror(MANIFEST);
+ PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
+ " sign has\n",
+ PROGRAM_NAME);
+ PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "Generating %s file..\n", metadir);
+ }
+
+ fprintf(mf, "Manifest-Version: 1.0\n");
+ fprintf(mf, "Created-By: %s\n", CREATOR);
+ fprintf(mf, "Comments: %s\n", BREAKAGE);
+
+ if (scriptdir) {
+ fprintf(mf, "Comments: --\n");
+ fprintf(mf, "Comments: --\n");
+ fprintf(mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
+ fprintf(mf, "Comments: -- be included in the physical jar file.\n");
+ fprintf(mf, "Comments: --\n");
+ fprintf(mf, "Comments: --\n");
+ }
+
+ if (install_script)
+ fprintf(mf, "Install-Script: %s\n", install_script);
+
+ if (metafile)
+ add_meta(mf, "+");
+
+ /* Loop through all files & subdirectories */
+ foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
+ (void *)NULL)
+ ;
+
+ fclose(mf);
+
+ strcat(sfname, "/");
+ strcat(sfname, base);
+ strcat(sfname, ".sf");
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
+ }
+ generate_SF_file(metadir, sfname);
+
+ return 0;
+}
+
+/*
+ * m a n i f e s t o _ x p i _ f n
+ *
+ * Called by pointer from SignArchive(), once for
+ * each file within the directory. This function
+ * is only used for adding to XPI compatible archive
+ *
+ */
+static int
+manifesto_xpi_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
+{
+ char fullname[FNSIZE];
+ int count;
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "--> %s\n", relpath);
+ }
+
+ /* extension matching */
+ if (extensionsGiven) {
+ char *ext = PL_strrchr(relpath, '.');
+ if (!ext)
+ return 0;
+ if (!PL_HashTableLookup(extensions, ext))
+ return 0;
+ }
+ count = snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
+ if (count >= sizeof(fullname)) {
+ return 1;
+ }
+ JzipAdd(fullname, relpath, zipfile, compression_level);
+
+ return 0;
+}
+
+/*
+ * m a n i f e s t o _ f n
+ *
+ * Called by pointer from manifesto(), once for
+ * each file within the directory.
+ *
+ */
+static int
+manifesto_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
+{
+ int use_js;
+ char *md5, *sha1;
+
+ JAR_Digest dig;
+ char fullname[FNSIZE];
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "--> %s\n", relpath);
+ }
+
+ /* extension matching */
+ if (extensionsGiven) {
+ char *ext = PL_strrchr(relpath, '.');
+ if (!ext)
+ return 0;
+ if (!PL_HashTableLookup(extensions, ext))
+ return 0;
+ }
+
+ snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
+
+ fprintf(mf, "\n");
+
+ use_js = 0;
+
+ if (scriptdir && !PORT_Strcmp(scriptdir, reldir))
+ use_js++;
+
+ /* sign non-.js files inside .arc directories using the javascript magic */
+
+ if ((PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3) &&
+ (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
+ use_js++;
+
+ if (use_js) {
+ fprintf(mf, "Name: %s\n", filename);
+ fprintf(mf, "Magic: javascript\n");
+
+ if (optimize == 0)
+ fprintf(mf, "javascript.id: %s\n", filename);
+
+ if (metafile)
+ add_meta(mf, filename);
+ } else {
+ fprintf(mf, "Name: %s\n", relpath);
+ if (metafile)
+ add_meta(mf, relpath);
+ }
+
+ JAR_digest_file(fullname, &dig);
+
+ if (optimize == 0) {
+ fprintf(mf, "Digest-Algorithms: MD5 SHA1\n");
+
+ md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
+ fprintf(mf, "MD5-Digest: %s\n", md5);
+ PORT_Free(md5);
+ }
+
+ sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
+ fprintf(mf, "SHA1-Digest: %s\n", sha1);
+ PORT_Free(sha1);
+
+ if (!use_js) {
+ JzipAdd(fullname, relpath, zipfile, compression_level);
+ }
+
+ return 0;
+}
+
+/*
+ * a d d _ m e t a
+ *
+ * Parse the metainfo file, and add any details
+ * necessary to the manifest file. In most cases you
+ * should be using the -i option (ie, for SmartUpdate).
+ *
+ */
+static int
+add_meta(FILE *fp, char *name)
+{
+ FILE *met;
+ char buf[BUFSIZ];
+
+ int place;
+ char *pattern, *meta;
+
+ int num = 0;
+
+ if ((met = fopen(metafile, "r")) != NULL) {
+ while (fgets(buf, BUFSIZ, met)) {
+ char *s;
+
+ for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
+ ;
+ *s = 0;
+
+ if (*buf == 0)
+ continue;
+
+ pattern = buf;
+
+ /* skip to whitespace */
+ for (s = buf; *s && *s != ' ' && *s != '\t'; s++)
+ ;
+
+ /* terminate pattern */
+ if (*s == ' ' || *s == '\t')
+ *s++ = 0;
+
+ /* eat through whitespace */
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ meta = s;
+
+ /* this will eventually be regexp matching */
+
+ place = 0;
+ if (!PORT_Strcmp(pattern, name))
+ place = 1;
+
+ if (place) {
+ num++;
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "[%s] %s\n", name, meta);
+ }
+ fprintf(fp, "%s\n", meta);
+ }
+ }
+ fclose(met);
+ } else {
+ PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
+ metafile);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ return num;
+}
+
+/**********************************************************************
+ *
+ * S i g n F i l e
+ */
+static int
+SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert)
+{
+ int nb;
+ char ibuf[4096], digestdata[32];
+ const SECHashObject *hashObj;
+ void *hashcx;
+ unsigned int len;
+
+ SECItem digest;
+ SEC_PKCS7ContentInfo *cinfo;
+ SECStatus rv;
+
+ if (outFile == NULL || inFile == NULL || cert == NULL)
+ return -1;
+
+ /* XXX probably want to extend interface to allow other hash algorithms */
+ hashObj = HASH_GetHashObject(HASH_AlgSHA1);
+
+ hashcx = (*hashObj->create)();
+ if (hashcx == NULL)
+ return -1;
+
+ (*hashObj->begin)(hashcx);
+
+ for (;;) {
+ if (feof(inFile))
+ break;
+ nb = fread(ibuf, 1, sizeof(ibuf), inFile);
+ if (nb == 0) {
+ if (ferror(inFile)) {
+ PORT_SetError(SEC_ERROR_IO);
+ (*hashObj->destroy)(hashcx, PR_TRUE);
+ return -1;
+ }
+ /* eof */
+ break;
+ }
+ (*hashObj->update)(hashcx, (unsigned char *)ibuf, nb);
+ }
+
+ (*hashObj->end)(hashcx, (unsigned char *)digestdata, &len, 32);
+ (*hashObj->destroy)(hashcx, PR_TRUE);
+
+ digest.data = (unsigned char *)digestdata;
+ digest.len = len;
+
+ cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL,
+ SEC_OID_SHA1, &digest, NULL, NULL);
+
+ if (cinfo == NULL)
+ return -1;
+
+ rv = SEC_PKCS7IncludeCertChain(cinfo, NULL);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo(cinfo);
+ return -1;
+ }
+
+ if (no_time == 0) {
+ rv = SEC_PKCS7AddSigningTime(cinfo);
+ if (rv != SECSuccess) {
+ /* don't check error */
+ }
+ }
+
+ rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata);
+
+ SEC_PKCS7DestroyContentInfo(cinfo);
+
+ if (rv != SECSuccess)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * g e n e r a t e _ S F _ f i l e
+ *
+ * From the supplied manifest file, calculates
+ * digests on the various sections, creating a .SF
+ * file in the process.
+ *
+ */
+static int
+generate_SF_file(char *manifile, char *who)
+{
+ FILE *sfFile;
+ FILE *mfFile;
+ long r1, r2, r3;
+ char whofile[FNSIZE];
+ char *buf, *name = NULL;
+ char *md5, *sha1;
+ JAR_Digest dig;
+ int line = 0;
+
+ strcpy(whofile, who);
+
+ if ((mfFile = fopen(manifile, "rb")) == NULL) {
+ perror(manifile);
+ exit(ERRX);
+ }
+
+ if ((sfFile = fopen(whofile, "wb")) == NULL) {
+ perror(who);
+ exit(ERRX);
+ }
+
+ buf = (char *)PORT_ZAlloc(BUFSIZ);
+
+ if (buf)
+ name = (char *)PORT_ZAlloc(BUFSIZ);
+
+ if (buf == NULL || name == NULL)
+ out_of_memory();
+
+ fprintf(sfFile, "Signature-Version: 1.0\n");
+ fprintf(sfFile, "Created-By: %s\n", CREATOR);
+ fprintf(sfFile, "Comments: %s\n", BREAKAGE);
+
+ if (fgets(buf, BUFSIZ, mfFile) == NULL) {
+ PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ if (strncmp(buf, "Manifest-Version:", 17)) {
+ PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ fseek(mfFile, 0L, SEEK_SET);
+
+ /* Process blocks of headers, and calculate their hashen */
+
+ while (1) {
+ /* Beginning range */
+ r1 = ftell(mfFile);
+
+ if (fgets(name, BUFSIZ, mfFile) == NULL)
+ break;
+
+ line++;
+
+ if (r1 != 0 && strncmp(name, "Name:", 5)) {
+ PR_fprintf(errorFD,
+ "warning: unexpected input in manifest file \"%s\" at line %d:\n",
+ manifile, line);
+ PR_fprintf(errorFD, "%s\n", name);
+ warningCount++;
+ }
+
+ r2 = r1;
+ while (fgets(buf, BUFSIZ, mfFile)) {
+ if (*buf == 0 || *buf == '\n' || *buf == '\r')
+ break;
+
+ line++;
+
+ /* Ending range for hashing */
+ r2 = ftell(mfFile);
+ }
+
+ r3 = ftell(mfFile);
+
+ if (r1) {
+ fprintf(sfFile, "\n");
+ fprintf(sfFile, "%s", name);
+ }
+
+ calculate_MD5_range(mfFile, r1, r2, &dig);
+
+ if (optimize == 0) {
+ fprintf(sfFile, "Digest-Algorithms: MD5 SHA1\n");
+
+ md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
+ fprintf(sfFile, "MD5-Digest: %s\n", md5);
+ PORT_Free(md5);
+ }
+
+ sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
+ fprintf(sfFile, "SHA1-Digest: %s\n", sha1);
+ PORT_Free(sha1);
+
+ /* restore normalcy after changing offset position */
+ fseek(mfFile, r3, SEEK_SET);
+ }
+
+ PORT_Free(buf);
+ PORT_Free(name);
+
+ fclose(sfFile);
+ fclose(mfFile);
+
+ return 0;
+}
+
+/*
+ * c a l c u l a t e _ M D 5 _ r a n g e
+ *
+ * Calculate the MD5 digest on a range of bytes in
+ * the specified fopen'd file. Returns base64.
+ *
+ */
+static int
+calculate_MD5_range(FILE *fp, long r1, long r2, JAR_Digest *dig)
+{
+ int num;
+ int range;
+ unsigned char *buf;
+ SECStatus rv;
+
+ range = r2 - r1;
+
+ /* position to the beginning of range */
+ fseek(fp, r1, SEEK_SET);
+
+ buf = (unsigned char *)PORT_ZAlloc(range);
+ if (buf == NULL)
+ out_of_memory();
+
+ if ((num = fread(buf, 1, range, fp)) != range) {
+ PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
+ range, num);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range);
+ if (rv == SECSuccess) {
+ rv = PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range);
+ }
+ if (rv != SECSuccess) {
+ PR_fprintf(errorFD, "%s: can't generate digest context\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ PORT_Free(buf);
+
+ return 0;
+}
+
+static void
+SignOut(void *arg, const char *buf, unsigned long len)
+{
+ fwrite(buf, len, 1, (FILE *)arg);
+}