Back to index

lightning-sunbird  0.9+nobinonly
sign.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "signtool.h"
00038 #include "zip.h" 
00039 #include "prmem.h"
00040 #include "blapi.h"
00041 #include "sechash.h" /* for HASH_GetHashObject() */
00042 
00043 static int    create_pk7 (char *dir, char *keyName, int *keyType);
00044 static int    jar_find_key_type (CERTCertificate *cert);
00045 static int    manifesto (char *dirname, char *install_script, PRBool recurse);
00046 static int    manifesto_fn(char *relpath, char *basedir, char *reldir,
00047                      char *filename, void *arg);
00048 static int    manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
00049                      char *filename, void *arg);
00050 static int    sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
00051                      char *filename, void *arg);
00052 static int    add_meta (FILE *fp, char *name);
00053 static int    SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert);
00054 static int    generate_SF_file (char *manifile, char *who);
00055 static int    calculate_MD5_range (FILE *fp, long r1, long r2, 
00056                      JAR_Digest *dig);
00057 static void   SignOut (void *arg, const char *buf, unsigned long len);
00058 
00059 static char   *metafile = NULL;
00060 static int    optimize = 0;
00061 static FILE *mf;
00062 static ZIPfile *zipfile = NULL;
00063 
00064 /* 
00065  *  S i g n A r c h i v e
00066  *
00067  *  Sign an individual archive tree. A directory 
00068  *  called META-INF is created underneath this.
00069  *
00070  */
00071 int
00072 SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
00073        char *meta_file, char *install_script, int _optimize, PRBool recurse)
00074 {
00075     int       status;
00076     char      tempfn [FNSIZE], fullfn [FNSIZE];
00077     int       keyType = rsaKey;
00078 
00079     metafile = meta_file;
00080     optimize = _optimize;
00081 
00082     /* To create XPI compatible Archive manifesto() must be run before 
00083      * the zipfile is opened. This is so the signed files are not added
00084      * the archive before the crucial rsa/dsa file*/
00085     if (xpi_arc) {
00086        manifesto (tree, install_script, recurse);
00087     }
00088 
00089     if (zip_file) {
00090        zipfile = JzipOpen(zip_file, NULL /*no comment*/);
00091     }
00092 
00093     /*Sign and add files to the archive normally with manifesto()*/
00094     if (!xpi_arc) {
00095        manifesto (tree, install_script, recurse);
00096     }
00097 
00098     if (keyName) {
00099        status = create_pk7 (tree, keyName, &keyType);
00100        if (status < 0) {
00101            PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
00102                 tree);
00103            errorCount++;
00104            exit (ERRX);
00105        }
00106     }
00107 
00108     /* Add the rsa/dsa file as the first file in the archive. This is crucial
00109      * for a XPInstall compatible archive */
00110     if (xpi_arc) {
00111        if (verbosity >= 0) {
00112            PR_fprintf(outputFD, "%s \n", XPI_TEXT);
00113        }
00114 
00115        /* rsa/dsa to zip */
00116        sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
00117            "dsa" : "rsa"));
00118        sprintf (fullfn, "%s/%s", tree, tempfn);
00119        JzipAdd(fullfn, tempfn, zipfile, compression_level);
00120 
00121        /* Loop through all files & subdirectories, add to archive */
00122        foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
00123                      (void * )NULL);
00124     }
00125     /* mf to zip */
00126     strcpy (tempfn, "META-INF/manifest.mf");
00127     sprintf (fullfn, "%s/%s", tree, tempfn);
00128     JzipAdd(fullfn, tempfn, zipfile, compression_level);
00129 
00130     /* sf to zip */
00131     sprintf (tempfn, "META-INF/%s.sf", base);
00132     sprintf (fullfn, "%s/%s", tree, tempfn);
00133     JzipAdd(fullfn, tempfn, zipfile, compression_level);
00134 
00135     /* Add the rsa/dsa file to the zip archive normally */
00136     if (!xpi_arc) {
00137        /* rsa/dsa to zip */
00138        sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
00139            "dsa" : "rsa"));
00140        sprintf (fullfn, "%s/%s", tree, tempfn);
00141        JzipAdd(fullfn, tempfn, zipfile, compression_level);
00142     }
00143 
00144     JzipClose(zipfile);
00145 
00146     if (verbosity >= 0) {
00147        if (javascript) {
00148            PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
00149                                           zip_file);
00150        } else {
00151            PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
00152                 tree);
00153        }
00154     }
00155 
00156     return 0;
00157 }
00158 
00159 
00160 typedef struct {
00161     char      *keyName;
00162     int       javascript;
00163     char      *metafile;
00164     char      *install_script;
00165     int       optimize;
00166 } SignArcInfo;
00167 
00168 /* 
00169  *  S i g n A l l A r c
00170  *
00171  *  Javascript may generate multiple .arc directories, one
00172  *  for each jar archive needed. Sign them all.
00173  *
00174  */
00175 int
00176 SignAllArc(char *jartree, char *keyName, int javascript, char *metafile,
00177 char *install_script, int optimize, PRBool recurse)
00178 {
00179     SignArcInfo info;
00180 
00181     info.keyName = keyName;
00182     info.javascript = javascript;
00183     info.metafile = metafile;
00184     info.install_script = install_script;
00185     info.optimize = optimize;
00186 
00187     return foreach(jartree, "", sign_all_arc_fn, recurse,
00188         PR_TRUE /*include dirs*/, (void * )&info);
00189 }
00190 
00191 
00192 static int    
00193 sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
00194        void *arg)
00195 {
00196     char      *zipfile = NULL;
00197     char      *arc = NULL, *archive = NULL;
00198     int       retval = 0;
00199     SignArcInfo * infop = (SignArcInfo * )arg;
00200 
00201     /* Make sure there is one and only one ".arc" in the relative path, 
00202      * and that it is at the end of the path (don't sign .arcs within .arcs) */
00203     if ( (PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) -
00204         4) && 
00205         (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4) ) {
00206 
00207        if (!infop) {
00208            PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
00209            errorCount++;
00210            retval = -1;
00211            goto finish;
00212        }
00213        archive = PR_smprintf("%s/%s", basedir, relpath);
00214 
00215        zipfile = PL_strdup(archive);
00216        arc = PORT_Strrchr (zipfile, '.');
00217 
00218        if (arc == NULL) {
00219            PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
00220            errorCount++;
00221            retval = -1;
00222            goto finish;
00223        }
00224 
00225        PL_strcpy (arc, ".jar");
00226 
00227        if (verbosity >= 0) {
00228            PR_fprintf(outputFD, "\nsigning: %s\n", zipfile);
00229        }
00230        retval = SignArchive(archive, infop->keyName, zipfile,
00231            infop->javascript, infop->metafile, infop->install_script,
00232                             infop->optimize, PR_TRUE /* recurse */);
00233     }
00234 finish:
00235     if (archive) 
00236        PR_Free(archive);
00237     if (zipfile) 
00238        PR_Free(zipfile);
00239 
00240     return retval;
00241 }
00242 
00243 
00244 /*********************************************************************
00245  *
00246  * c r e a t e _ p k 7
00247  */
00248 static int    
00249 create_pk7 (char *dir, char *keyName, int *keyType)
00250 {
00251     int       status = 0;
00252     char      *file_ext;
00253 
00254     CERTCertificate * cert;
00255     CERTCertDBHandle * db;
00256 
00257     FILE * in, *out;
00258 
00259     char      sf_file [FNSIZE];
00260     char      pk7_file [FNSIZE];
00261 
00262     /* open cert database */
00263     db = CERT_GetDefaultCertDB();
00264 
00265     if (db == NULL)
00266        return - 1;
00267 
00268     /* find cert */
00269     /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
00270     cert = PK11_FindCertFromNickname(keyName, NULL /*wincx*/);
00271 
00272     if (cert == NULL) {
00273        SECU_PrintError ( PROGRAM_NAME,
00274            "the cert \"%s\" does not exist in the database", keyName);
00275        return -1;
00276     }
00277 
00278 
00279     /* determine the key type, which sets the extension for pkcs7 object */
00280 
00281     *keyType = jar_find_key_type (cert);
00282     file_ext = (*keyType == dsaKey) ? "dsa" : "rsa";
00283 
00284     sprintf (sf_file, "%s/META-INF/%s.sf", dir, base);
00285     sprintf (pk7_file, "%s/META-INF/%s.%s", dir, base, file_ext);
00286 
00287     if ((in = fopen (sf_file, "rb")) == NULL) {
00288        PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
00289             sf_file);
00290        errorCount++;
00291        exit (ERRX);
00292     }
00293 
00294     if ((out = fopen (pk7_file, "wb")) == NULL) {
00295        PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
00296             sf_file);
00297        errorCount++;
00298        exit (ERRX);
00299     }
00300 
00301     status = SignFile (out, in, cert);
00302 
00303     CERT_DestroyCertificate (cert);
00304     fclose (in);
00305     fclose (out);
00306 
00307     if (status) {
00308        PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
00309            PROGRAM_NAME, SECU_ErrorString ((int16) PORT_GetError()));
00310        errorCount++;
00311        return - 1;
00312     }
00313 
00314     return 0;
00315 }
00316 
00317 
00318 /*
00319  *  j a r _ f i n d _ k e y _ t y p e
00320  * 
00321  *  Determine the key type for a given cert, which 
00322  * should be rsaKey or dsaKey. Any error return 0.
00323  *
00324  */
00325 static int    
00326 jar_find_key_type (CERTCertificate *cert)
00327 {
00328     PK11SlotInfo * slot = NULL;
00329     SECKEYPrivateKey * privk = NULL;
00330     KeyType keyType;
00331 
00332     /* determine its type */
00333     PK11_FindObjectForCert (cert, /*wincx*/ NULL, &slot);
00334 
00335     if (slot == NULL) {
00336        PR_fprintf(errorFD, "warning - can't find slot for this cert\n");
00337        warningCount++;
00338        return 0;
00339     }
00340 
00341     privk = PK11_FindPrivateKeyFromCert (slot, cert, /*wincx*/ NULL);
00342     PK11_FreeSlot (slot);
00343 
00344     if (privk == NULL) {
00345        PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
00346        warningCount++;
00347        return 0;
00348     }
00349 
00350     keyType = privk->keyType;
00351     SECKEY_DestroyPrivateKey (privk);
00352     return keyType;
00353 }
00354 
00355 
00356 /*
00357  *  m a n i f e s t o
00358  *
00359  *  Run once for every subdirectory in which a 
00360  *  manifest is to be created -- usually exactly once.
00361  *
00362  */
00363 static int    
00364 manifesto (char *dirname, char *install_script, PRBool recurse)
00365 {
00366     char      metadir [FNSIZE], sfname [FNSIZE];
00367 
00368     /* Create the META-INF directory to hold signing info */
00369 
00370     if (PR_Access (dirname, PR_ACCESS_READ_OK)) {
00371        PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
00372             PROGRAM_NAME, dirname);
00373        errorCount++;
00374        perror (dirname);
00375        exit (ERRX);
00376     }
00377 
00378     if (PR_Access (dirname, PR_ACCESS_WRITE_OK)) {
00379        PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
00380                             PROGRAM_NAME, dirname);
00381        errorCount++;
00382        perror(dirname);
00383        exit(ERRX);
00384     }
00385 
00386     sprintf (metadir, "%s/META-INF", dirname);
00387 
00388     strcpy (sfname, metadir);
00389 
00390     PR_MkDir (metadir, 0777);
00391 
00392     strcat (metadir, "/");
00393     strcat (metadir, MANIFEST);
00394 
00395     if ((mf = fopen (metadir, "wb")) == NULL) {
00396        perror (MANIFEST);
00397        PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
00398                          " sign has\n", PROGRAM_NAME);
00399        PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
00400                      PROGRAM_NAME);
00401        errorCount++;
00402        exit (ERRX);
00403     }
00404 
00405     if (verbosity >= 0) {
00406        PR_fprintf(outputFD, "Generating %s file..\n", metadir);
00407     }
00408 
00409     fprintf(mf, "Manifest-Version: 1.0\n");
00410     fprintf (mf, "Created-By: %s\n", CREATOR);
00411     fprintf (mf, "Comments: %s\n", BREAKAGE);
00412 
00413     if (scriptdir) {
00414        fprintf (mf, "Comments: --\n");
00415        fprintf (mf, "Comments: --\n");
00416        fprintf (mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
00417        fprintf (mf, "Comments: -- be included in the physical jar file.\n");
00418        fprintf (mf, "Comments: --\n");
00419        fprintf (mf, "Comments: --\n");
00420     }
00421 
00422     if (install_script)
00423        fprintf (mf, "Install-Script: %s\n", install_script);
00424 
00425     if (metafile)
00426        add_meta (mf, "+");
00427 
00428     /* Loop through all files & subdirectories */
00429     foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
00430                      (void * )NULL);
00431 
00432     fclose (mf);
00433 
00434     strcat (sfname, "/");
00435     strcat (sfname, base);
00436     strcat (sfname, ".sf");
00437 
00438     if (verbosity >= 0) {
00439        PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
00440     }
00441     generate_SF_file (metadir, sfname);
00442 
00443     return 0;
00444 }
00445 
00446 
00447 /*
00448  *  m a n i f e s t o _ x p i _ f n
00449  *
00450  *  Called by pointer from SignArchive(), once for
00451  *  each file within the directory. This function
00452  *  is only used for adding to XPI compatible archive
00453  *
00454  */
00455 static int    manifesto_xpi_fn 
00456 (char *relpath, char *basedir, char *reldir, char *filename, void *arg)
00457 {
00458     char      fullname [FNSIZE];
00459 
00460     if (verbosity >= 0) {
00461        PR_fprintf(outputFD, "--> %s\n", relpath);
00462     }
00463 
00464     /* extension matching */
00465     if (extensionsGiven) {
00466        char   *ext = PL_strrchr(relpath, '.');
00467        if (!ext) 
00468            return 0;
00469        if (!PL_HashTableLookup(extensions, ext)) 
00470            return 0;
00471     }
00472     sprintf (fullname, "%s/%s", basedir, relpath);
00473     JzipAdd(fullname, relpath, zipfile, compression_level);
00474 
00475     return 0;
00476 }
00477 
00478 
00479 /*
00480  *  m a n i f e s t o _ f n
00481  *
00482  *  Called by pointer from manifesto(), once for
00483  *  each file within the directory.
00484  *
00485  */
00486 static int    manifesto_fn 
00487 (char *relpath, char *basedir, char *reldir, char *filename, void *arg)
00488 {
00489     int       use_js;
00490 
00491     JAR_Digest dig;
00492     char      fullname [FNSIZE];
00493 
00494     if (verbosity >= 0) {
00495        PR_fprintf(outputFD, "--> %s\n", relpath);
00496     }
00497 
00498     /* extension matching */
00499     if (extensionsGiven) {
00500        char   *ext = PL_strrchr(relpath, '.');
00501        if (!ext) 
00502            return 0;
00503        if (!PL_HashTableLookup(extensions, ext)) 
00504            return 0;
00505     }
00506 
00507     sprintf (fullname, "%s/%s", basedir, relpath);
00508 
00509     fprintf (mf, "\n");
00510 
00511     use_js = 0;
00512 
00513     if (scriptdir && !PORT_Strcmp (scriptdir, reldir))
00514        use_js++;
00515 
00516     /* sign non-.js files inside .arc directories using the javascript magic */
00517 
00518     if ( (PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3)
00519          && (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
00520        use_js++;
00521 
00522     if (use_js) {
00523        fprintf (mf, "Name: %s\n", filename);
00524        fprintf (mf, "Magic: javascript\n");
00525 
00526        if (optimize == 0)
00527            fprintf (mf, "javascript.id: %s\n", filename);
00528 
00529        if (metafile)
00530            add_meta (mf, filename);
00531     } else {
00532        fprintf (mf, "Name: %s\n", relpath);
00533        if (metafile)
00534            add_meta (mf, relpath);
00535     }
00536 
00537     JAR_digest_file (fullname, &dig);
00538 
00539 
00540     if (optimize == 0) {
00541        fprintf (mf, "Digest-Algorithms: MD5 SHA1\n");
00542        fprintf (mf, "MD5-Digest: %s\n", BTOA_DataToAscii (dig.md5,
00543             MD5_LENGTH));
00544     }
00545 
00546     fprintf (mf, "SHA1-Digest: %s\n", BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
00547 
00548     if (!use_js) {
00549        JzipAdd(fullname, relpath, zipfile, compression_level);
00550     }
00551 
00552     return 0;
00553 }
00554 
00555 
00556 /*
00557  *  a d d _ m e t a
00558  *
00559  *  Parse the metainfo file, and add any details
00560  *  necessary to the manifest file. In most cases you
00561  *  should be using the -i option (ie, for SmartUpdate).
00562  *
00563  */
00564 static int    add_meta (FILE *fp, char *name)
00565 {
00566     FILE * met;
00567     char      buf [BUFSIZ];
00568 
00569     int       place;
00570     char      *pattern, *meta;
00571 
00572     int       num = 0;
00573 
00574     if ((met = fopen (metafile, "r")) != NULL) {
00575        while (fgets (buf, BUFSIZ, met)) {
00576            char      *s;
00577 
00578            for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
00579               ;
00580            *s = 0;
00581 
00582            if (*buf == 0)
00583               continue;
00584 
00585            pattern = buf;
00586 
00587            /* skip to whitespace */
00588            for (s = buf; *s && *s != ' ' && *s != '\t'; s++)
00589               ;
00590 
00591            /* terminate pattern */
00592            if (*s == ' ' || *s == '\t') 
00593               *s++ = 0;
00594 
00595            /* eat through whitespace */
00596            while (*s == ' ' || *s == '\t') 
00597               s++;
00598 
00599            meta = s;
00600 
00601            /* this will eventually be regexp matching */
00602 
00603            place = 0;
00604            if (!PORT_Strcmp (pattern, name))
00605               place = 1;
00606 
00607            if (place) {
00608               num++;
00609               if (verbosity >= 0) {
00610                   PR_fprintf(outputFD, "[%s] %s\n", name, meta);
00611               }
00612               fprintf (fp, "%s\n", meta);
00613            }
00614        }
00615        fclose (met);
00616     } else {
00617        PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
00618             metafile);
00619        errorCount++;
00620        exit (ERRX);
00621     }
00622 
00623     return num;
00624 }
00625 
00626 
00627 /**********************************************************************
00628  *
00629  * S i g n F i l e
00630  */
00631 static int    
00632 SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert)
00633 {
00634     int       nb;
00635     char      ibuf[4096], digestdata[32];
00636     const SECHashObject *hashObj;
00637     void      *hashcx;
00638     unsigned int     len;
00639 
00640     SECItem digest;
00641     SEC_PKCS7ContentInfo * cinfo;
00642     SECStatus rv;
00643 
00644     if (outFile == NULL || inFile == NULL || cert == NULL)
00645        return - 1;
00646 
00647     /* XXX probably want to extend interface to allow other hash algorithms */
00648     hashObj = HASH_GetHashObject(HASH_AlgSHA1);
00649 
00650     hashcx = (*hashObj->create)();
00651     if (hashcx == NULL)
00652        return - 1;
00653 
00654     (*hashObj->begin)(hashcx);
00655 
00656     for (; ; ) {
00657        if (feof(inFile)) 
00658            break;
00659        nb = fread(ibuf, 1, sizeof(ibuf), inFile);
00660        if (nb == 0) {
00661            if (ferror(inFile)) {
00662               PORT_SetError(SEC_ERROR_IO);
00663               (*hashObj->destroy)(hashcx, PR_TRUE);
00664               return - 1;
00665            }
00666            /* eof */
00667            break;
00668        }
00669        (*hashObj->update)(hashcx, (unsigned char *) ibuf, nb);
00670     }
00671 
00672     (*hashObj->end)(hashcx, (unsigned char *) digestdata, &len, 32);
00673     (*hashObj->destroy)(hashcx, PR_TRUE);
00674 
00675     digest.data = (unsigned char *) digestdata;
00676     digest.len = len;
00677 
00678     cinfo = SEC_PKCS7CreateSignedData 
00679         (cert, certUsageObjectSigner, NULL, 
00680         SEC_OID_SHA1, &digest, NULL, NULL);
00681 
00682     if (cinfo == NULL)
00683        return - 1;
00684 
00685     rv = SEC_PKCS7IncludeCertChain (cinfo, NULL);
00686     if (rv != SECSuccess) {
00687        SEC_PKCS7DestroyContentInfo (cinfo);
00688        return - 1;
00689     }
00690 
00691     if (no_time == 0) {
00692        rv = SEC_PKCS7AddSigningTime (cinfo);
00693        if (rv != SECSuccess) {
00694            /* don't check error */
00695        }
00696     }
00697 
00698     if (password) {
00699        rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, 
00700            (SECKEYGetPasswordKey) password_hardcode, NULL);
00701     } else {
00702        rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL,
00703                             NULL);
00704     }
00705 
00706 
00707     SEC_PKCS7DestroyContentInfo (cinfo);
00708 
00709     if (rv != SECSuccess)
00710        return - 1;
00711 
00712     return 0;
00713 }
00714 
00715 
00716 /*
00717  *  g e n e r a t e _ S F _ f i l e 
00718  *
00719  *  From the supplied manifest file, calculates
00720  *  digests on the various sections, creating a .SF
00721  *  file in the process.
00722  * 
00723  */
00724 static int    generate_SF_file (char *manifile, char *who)
00725 {
00726     FILE * sf;
00727     FILE * mf;
00728     long      r1, r2, r3;
00729     char      whofile [FNSIZE];
00730     char      *buf, *name = NULL;
00731     JAR_Digest dig;
00732     int       line = 0;
00733 
00734     strcpy (whofile, who);
00735 
00736     if ((mf = fopen (manifile, "rb")) == NULL) {
00737        perror (manifile);
00738        exit (ERRX);
00739     }
00740 
00741     if ((sf = fopen (whofile, "wb")) == NULL) {
00742        perror (who);
00743        exit (ERRX);
00744     }
00745 
00746     buf = (char *) PORT_ZAlloc (BUFSIZ);
00747 
00748     if (buf)
00749        name = (char *) PORT_ZAlloc (BUFSIZ);
00750 
00751     if (buf == NULL || name == NULL)
00752        out_of_memory();
00753 
00754     fprintf (sf, "Signature-Version: 1.0\n");
00755     fprintf (sf, "Created-By: %s\n", CREATOR);
00756     fprintf (sf, "Comments: %s\n", BREAKAGE);
00757 
00758     if (fgets (buf, BUFSIZ, mf) == NULL) {
00759        PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
00760        errorCount++;
00761        exit (ERRX);
00762     }
00763 
00764     if (strncmp (buf, "Manifest-Version:", 17)) {
00765        PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
00766        errorCount++;
00767        exit (ERRX);
00768     }
00769 
00770     fseek (mf, 0L, SEEK_SET);
00771 
00772     /* Process blocks of headers, and calculate their hashen */
00773 
00774     while (1) {
00775        /* Beginning range */
00776        r1 = ftell (mf);
00777 
00778        if (fgets (name, BUFSIZ, mf) == NULL)
00779            break;
00780 
00781        line++;
00782 
00783        if (r1 != 0 && strncmp (name, "Name:", 5)) {
00784            PR_fprintf(errorFD, 
00785            "warning: unexpected input in manifest file \"%s\" at line %d:\n",
00786                 manifile, line);
00787            PR_fprintf(errorFD, "%s\n", name);
00788            warningCount++;
00789        }
00790 
00791        r2 = r1;
00792        while (fgets (buf, BUFSIZ, mf)) {
00793            if (*buf == 0 || *buf == '\n' || *buf == '\r')
00794               break;
00795 
00796            line++;
00797 
00798            /* Ending range for hashing */
00799            r2 = ftell (mf);
00800        }
00801 
00802        r3 = ftell (mf);
00803 
00804        if (r1) {
00805            fprintf (sf, "\n");
00806            fprintf (sf, "%s", name);
00807        }
00808 
00809        calculate_MD5_range (mf, r1, r2, &dig);
00810 
00811        if (optimize == 0) {
00812            fprintf (sf, "Digest-Algorithms: MD5 SHA1\n");
00813            fprintf (sf, "MD5-Digest: %s\n", 
00814                BTOA_DataToAscii (dig.md5, MD5_LENGTH));
00815        }
00816 
00817        fprintf (sf, "SHA1-Digest: %s\n", 
00818            BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
00819 
00820        /* restore normalcy after changing offset position */
00821        fseek (mf, r3, SEEK_SET);
00822     }
00823 
00824     PORT_Free (buf);
00825     PORT_Free (name);
00826 
00827     fclose (sf);
00828     fclose (mf);
00829 
00830     return 0;
00831 }
00832 
00833 
00834 /*
00835  *  c a l c u l a t e _ M D 5 _ r a n g e
00836  *
00837  *  Calculate the MD5 digest on a range of bytes in
00838  *  the specified fopen'd file. Returns base64.
00839  *
00840  */
00841 static int    
00842 calculate_MD5_range (FILE *fp, long r1, long r2, JAR_Digest *dig)
00843 {
00844     int       num;
00845     int       range;
00846     unsigned char    *buf;
00847 
00848     MD5Context * md5 = 0;
00849     SHA1Context * sha1 = 0;
00850 
00851     unsigned int     sha1_length, md5_length;
00852 
00853     range = r2 - r1;
00854 
00855     /* position to the beginning of range */
00856     fseek (fp, r1, SEEK_SET);
00857 
00858     buf = (unsigned char *) PORT_ZAlloc (range);
00859     if (buf == NULL)
00860        out_of_memory();
00861 
00862     if ((num = fread (buf, 1, range, fp)) != range) {
00863        PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
00864                      range, num);
00865        errorCount++;
00866        exit (ERRX);
00867     }
00868 
00869     md5 = MD5_NewContext();
00870     sha1 = SHA1_NewContext();
00871 
00872     if (md5 == NULL || sha1 == NULL) {
00873        PR_fprintf(errorFD, "%s: can't generate digest context\n",
00874             PROGRAM_NAME);
00875        errorCount++;
00876        exit (ERRX);
00877     }
00878 
00879     MD5_Begin (md5);
00880     SHA1_Begin (sha1);
00881 
00882     MD5_Update (md5, buf, range);
00883     SHA1_Update (sha1, buf, range);
00884 
00885     MD5_End (md5, dig->md5, &md5_length, MD5_LENGTH);
00886     SHA1_End (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
00887 
00888     MD5_DestroyContext (md5, PR_TRUE);
00889     SHA1_DestroyContext (sha1, PR_TRUE);
00890 
00891     PORT_Free (buf);
00892 
00893     return 0;
00894 }
00895 
00896 
00897 static void   SignOut (void *arg, const char *buf, unsigned long len)
00898 {
00899     fwrite (buf, len, 1, (FILE * ) arg);
00900 }
00901 
00902