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