tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

sign.c (23132B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "signtool.h"
      6 #include "zip.h"
      7 #include "prmem.h"
      8 #include "blapi.h"
      9 #include "sechash.h" /* for HASH_GetHashObject() */
     10 
     11 static int create_pk7(char *dir, char *keyName, KeyType *keyType);
     12 static KeyType jar_find_key_type(CERTCertificate *cert);
     13 static int manifesto(char *dirname, char *install_script, PRBool recurse);
     14 static int manifesto_fn(char *relpath, char *basedir, char *reldir,
     15                        char *filename, void *arg);
     16 static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
     17                            char *filename, void *arg);
     18 static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
     19                           char *filename, void *arg);
     20 static int add_meta(FILE *fp, char *name);
     21 static int SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert);
     22 static int generate_SF_file(char *manifile, char *who);
     23 static int calculate_MD5_range(FILE *fp, long r1, long r2,
     24                               JAR_Digest *dig);
     25 static void SignOut(void *arg, const char *buf, unsigned long len);
     26 
     27 static char *metafile = NULL;
     28 static int optimize = 0;
     29 static FILE *mf;
     30 static ZIPfile *zipfile = NULL;
     31 
     32 /*
     33 *  S i g n A r c h i v e
     34 *
     35 *  Sign an individual archive tree. A directory
     36 *  called META-INF is created underneath this.
     37 *
     38 */
     39 int
     40 SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
     41            char *meta_file, char *install_script, int _optimize, PRBool recurse)
     42 {
     43    int status;
     44    char tempfn[FNSIZE], fullfn[FNSIZE];
     45    KeyType keyType = rsaKey;
     46    int count;
     47 
     48    metafile = meta_file;
     49    optimize = _optimize;
     50 
     51    /* To create XPI compatible Archive manifesto() must be run before
     52     * the zipfile is opened. This is so the signed files are not added
     53     * the archive before the crucial rsa/dsa file*/
     54    if (xpi_arc) {
     55        manifesto(tree, install_script, recurse);
     56    }
     57 
     58    if (zip_file) {
     59        zipfile = JzipOpen(zip_file, NULL /*no comment*/);
     60    }
     61 
     62    /*Sign and add files to the archive normally with manifesto()*/
     63    if (!xpi_arc) {
     64        manifesto(tree, install_script, recurse);
     65    }
     66 
     67    if (keyName) {
     68        status = create_pk7(tree, keyName, &keyType);
     69        if (status < 0) {
     70            PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
     71                       tree);
     72            errorCount++;
     73            exit(ERRX);
     74        }
     75    }
     76 
     77    /* Add the rsa/dsa file as the first file in the archive. This is crucial
     78     * for a XPInstall compatible archive */
     79    if (xpi_arc) {
     80        if (verbosity >= 0) {
     81            PR_fprintf(outputFD, "%s \n", XPI_TEXT);
     82        }
     83 
     84        /* rsa/dsa to zip */
     85        count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base,
     86                         SECKEY_GetKeyTypeString(keyType));
     87        if (count >= sizeof(tempfn)) {
     88            PR_fprintf(errorFD, "unable to write key metadata\n");
     89            errorCount++;
     90            exit(ERRX);
     91        }
     92        count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
     93        if (count >= sizeof(fullfn)) {
     94            PR_fprintf(errorFD, "unable to write key metadata\n");
     95            errorCount++;
     96            exit(ERRX);
     97        }
     98        JzipAdd(fullfn, tempfn, zipfile, compression_level);
     99 
    100        /* Loop through all files & subdirectories, add to archive */
    101        foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
    102                 (void *)NULL)
    103            ;
    104    }
    105    /* mf to zip */
    106    strcpy(tempfn, "META-INF/manifest.mf");
    107    count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
    108    if (count >= sizeof(fullfn)) {
    109        PR_fprintf(errorFD, "unable to write manifest\n");
    110        errorCount++;
    111        exit(ERRX);
    112    }
    113    JzipAdd(fullfn, tempfn, zipfile, compression_level);
    114 
    115    /* sf to zip */
    116    count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.sf", base);
    117    if (count >= sizeof(tempfn)) {
    118        PR_fprintf(errorFD, "unable to write sf metadata\n");
    119        errorCount++;
    120        exit(ERRX);
    121    }
    122    count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
    123    if (count >= sizeof(fullfn)) {
    124        PR_fprintf(errorFD, "unable to write sf metadata\n");
    125        errorCount++;
    126        exit(ERRX);
    127    }
    128    JzipAdd(fullfn, tempfn, zipfile, compression_level);
    129 
    130    /* Add the rsa/dsa file to the zip archive normally */
    131    if (!xpi_arc) {
    132        /* rsa/dsa to zip */
    133        count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base,
    134                         SECKEY_GetKeyTypeString(keyType));
    135        if (count >= sizeof(tempfn)) {
    136            PR_fprintf(errorFD, "unable to write key metadata\n");
    137            errorCount++;
    138            exit(ERRX);
    139        }
    140        count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
    141        if (count >= sizeof(fullfn)) {
    142            PR_fprintf(errorFD, "unable to write key metadata\n");
    143            errorCount++;
    144            exit(ERRX);
    145        }
    146        JzipAdd(fullfn, tempfn, zipfile, compression_level);
    147    }
    148 
    149    JzipClose(zipfile);
    150 
    151    if (verbosity >= 0) {
    152        if (javascript) {
    153            PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
    154                       zip_file);
    155        } else {
    156            PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
    157                       tree);
    158        }
    159    }
    160 
    161    return 0;
    162 }
    163 
    164 typedef struct {
    165    char *keyName;
    166    int javascript;
    167    char *metafile;
    168    char *install_script;
    169    int optimize;
    170 } SignArcInfo;
    171 
    172 /*
    173 *  S i g n A l l A r c
    174 *
    175 *  Javascript may generate multiple .arc directories, one
    176 *  for each jar archive needed. Sign them all.
    177 *
    178 */
    179 int
    180 SignAllArc(char *jartree, char *keyName, int javascript, char *metafilename,
    181           char *install_script, int optimize_level, PRBool recurse)
    182 {
    183    SignArcInfo info;
    184 
    185    info.keyName = keyName;
    186    info.javascript = javascript;
    187    info.metafile = metafilename;
    188    info.install_script = install_script;
    189    info.optimize = optimize_level;
    190 
    191    return foreach (jartree, "", sign_all_arc_fn, recurse,
    192                    PR_TRUE /*include dirs*/, (void *)&info);
    193 }
    194 
    195 static int
    196 sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
    197                void *arg)
    198 {
    199    char *zipfilename = NULL;
    200    char *arc = NULL, *archive = NULL;
    201    int retval = 0;
    202    SignArcInfo *infop = (SignArcInfo *)arg;
    203 
    204    /* Make sure there is one and only one ".arc" in the relative path,
    205     * and that it is at the end of the path (don't sign .arcs within .arcs) */
    206    if ((PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) - 4) &&
    207        (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4)) {
    208 
    209        if (!infop) {
    210            PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
    211            errorCount++;
    212            retval = -1;
    213            goto finish;
    214        }
    215        archive = PR_smprintf("%s/%s", basedir, relpath);
    216 
    217        zipfilename = PL_strdup(archive);
    218        arc = PORT_Strrchr(zipfilename, '.');
    219 
    220        if (arc == NULL) {
    221            PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
    222            errorCount++;
    223            retval = -1;
    224            goto finish;
    225        }
    226 
    227        PL_strcpy(arc, ".jar");
    228 
    229        if (verbosity >= 0) {
    230            PR_fprintf(outputFD, "\nsigning: %s\n", zipfilename);
    231        }
    232        retval = SignArchive(archive, infop->keyName, zipfilename,
    233                             infop->javascript, infop->metafile, infop->install_script,
    234                             infop->optimize, PR_TRUE /* recurse */);
    235    }
    236 finish:
    237    if (archive)
    238        PR_Free(archive);
    239    if (zipfilename)
    240        PR_Free(zipfilename);
    241 
    242    return retval;
    243 }
    244 
    245 /*********************************************************************
    246 *
    247 * c r e a t e _ p k 7
    248 */
    249 static int
    250 create_pk7(char *dir, char *keyName, KeyType *keyType)
    251 {
    252    int status = 0;
    253    const char *file_ext;
    254 
    255    CERTCertificate *cert;
    256    CERTCertDBHandle *db;
    257 
    258    FILE *in, *out;
    259 
    260    char sf_file[FNSIZE];
    261    char pk7_file[FNSIZE];
    262 
    263    /* open cert database */
    264    db = CERT_GetDefaultCertDB();
    265 
    266    if (db == NULL)
    267        return -1;
    268 
    269    /* find cert */
    270    /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
    271    cert = PK11_FindCertFromNickname(keyName, &pwdata);
    272 
    273    if (cert == NULL) {
    274        SECU_PrintError(PROGRAM_NAME,
    275                        "Cannot find the cert \"%s\"", keyName);
    276        return -1;
    277    }
    278 
    279    /* determine the key type, which sets the extension for pkcs7 object */
    280 
    281    *keyType = jar_find_key_type(cert);
    282    file_ext = SECKEY_GetKeyTypeString(*keyType);
    283 
    284    snprintf(sf_file, sizeof(sf_file), "%s/META-INF/%s.sf", dir, base);
    285    snprintf(pk7_file, sizeof(pk7_file), "%s/META-INF/%s.%s", dir, base, file_ext);
    286 
    287    if ((in = fopen(sf_file, "rb")) == NULL) {
    288        PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
    289                   sf_file);
    290        errorCount++;
    291        exit(ERRX);
    292    }
    293 
    294    if ((out = fopen(pk7_file, "wb")) == NULL) {
    295        PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
    296                   sf_file);
    297        errorCount++;
    298        exit(ERRX);
    299    }
    300 
    301    status = SignFile(out, in, cert);
    302 
    303    CERT_DestroyCertificate(cert);
    304    fclose(in);
    305    fclose(out);
    306 
    307    if (status) {
    308        PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
    309                   PROGRAM_NAME, SECU_Strerror(PORT_GetError()));
    310        errorCount++;
    311        return -1;
    312    }
    313 
    314    return 0;
    315 }
    316 
    317 /*
    318 *  j a r _ f i n d _ k e y _ t y p e
    319 *
    320 *  Determine the key type for a given cert, which
    321 * should be rsaKey or dsaKey. Any error return 0.
    322 *
    323 */
    324 static KeyType
    325 jar_find_key_type(CERTCertificate *cert)
    326 {
    327    SECKEYPrivateKey *privk = NULL;
    328    KeyType keyType;
    329 
    330    /* determine its type */
    331    privk = PK11_FindKeyByAnyCert(cert, &pwdata);
    332    if (privk == NULL) {
    333        PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
    334        warningCount++;
    335        return 0;
    336    }
    337 
    338    keyType = privk->keyType;
    339    SECKEY_DestroyPrivateKey(privk);
    340    return keyType;
    341 }
    342 
    343 /*
    344 *  m a n i f e s t o
    345 *
    346 *  Run once for every subdirectory in which a
    347 *  manifest is to be created -- usually exactly once.
    348 *
    349 */
    350 static int
    351 manifesto(char *dirname, char *install_script, PRBool recurse)
    352 {
    353    char metadir[FNSIZE], sfname[FNSIZE];
    354 
    355    /* Create the META-INF directory to hold signing info */
    356 
    357    if (PR_Access(dirname, PR_ACCESS_READ_OK)) {
    358        PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
    359                   PROGRAM_NAME, dirname);
    360        errorCount++;
    361        perror(dirname);
    362        exit(ERRX);
    363    }
    364 
    365    if (PR_Access(dirname, PR_ACCESS_WRITE_OK)) {
    366        PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
    367                   PROGRAM_NAME, dirname);
    368        errorCount++;
    369        perror(dirname);
    370        exit(ERRX);
    371    }
    372 
    373    snprintf(metadir, sizeof(metadir), "%s/META-INF", dirname);
    374 
    375    strcpy(sfname, metadir);
    376 
    377    PR_MkDir(metadir, 0777);
    378 
    379    strcat(metadir, "/");
    380    strcat(metadir, MANIFEST);
    381 
    382    if ((mf = fopen(metadir, "wb")) == NULL) {
    383        perror(MANIFEST);
    384        PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
    385                            " sign has\n",
    386                   PROGRAM_NAME);
    387        PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
    388                   PROGRAM_NAME);
    389        errorCount++;
    390        exit(ERRX);
    391    }
    392 
    393    if (verbosity >= 0) {
    394        PR_fprintf(outputFD, "Generating %s file..\n", metadir);
    395    }
    396 
    397    fprintf(mf, "Manifest-Version: 1.0\n");
    398    fprintf(mf, "Created-By: %s\n", CREATOR);
    399    fprintf(mf, "Comments: %s\n", BREAKAGE);
    400 
    401    if (scriptdir) {
    402        fprintf(mf, "Comments: --\n");
    403        fprintf(mf, "Comments: --\n");
    404        fprintf(mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
    405        fprintf(mf, "Comments: -- be included in the physical jar file.\n");
    406        fprintf(mf, "Comments: --\n");
    407        fprintf(mf, "Comments: --\n");
    408    }
    409 
    410    if (install_script)
    411        fprintf(mf, "Install-Script: %s\n", install_script);
    412 
    413    if (metafile)
    414        add_meta(mf, "+");
    415 
    416    /* Loop through all files & subdirectories */
    417    foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
    418             (void *)NULL)
    419        ;
    420 
    421    fclose(mf);
    422 
    423    strcat(sfname, "/");
    424    strcat(sfname, base);
    425    strcat(sfname, ".sf");
    426 
    427    if (verbosity >= 0) {
    428        PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
    429    }
    430    generate_SF_file(metadir, sfname);
    431 
    432    return 0;
    433 }
    434 
    435 /*
    436 *  m a n i f e s t o _ x p i _ f n
    437 *
    438 *  Called by pointer from SignArchive(), once for
    439 *  each file within the directory. This function
    440 *  is only used for adding to XPI compatible archive
    441 *
    442 */
    443 static int
    444 manifesto_xpi_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
    445 {
    446    char fullname[FNSIZE];
    447    int count;
    448 
    449    if (verbosity >= 0) {
    450        PR_fprintf(outputFD, "--> %s\n", relpath);
    451    }
    452 
    453    /* extension matching */
    454    if (extensionsGiven) {
    455        char *ext = PL_strrchr(relpath, '.');
    456        if (!ext)
    457            return 0;
    458        if (!PL_HashTableLookup(extensions, ext))
    459            return 0;
    460    }
    461    count = snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
    462    if (count >= sizeof(fullname)) {
    463        return 1;
    464    }
    465    JzipAdd(fullname, relpath, zipfile, compression_level);
    466 
    467    return 0;
    468 }
    469 
    470 /*
    471 *  m a n i f e s t o _ f n
    472 *
    473 *  Called by pointer from manifesto(), once for
    474 *  each file within the directory.
    475 *
    476 */
    477 static int
    478 manifesto_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
    479 {
    480    int use_js;
    481    char *md5, *sha1;
    482 
    483    JAR_Digest dig;
    484    char fullname[FNSIZE];
    485 
    486    if (verbosity >= 0) {
    487        PR_fprintf(outputFD, "--> %s\n", relpath);
    488    }
    489 
    490    /* extension matching */
    491    if (extensionsGiven) {
    492        char *ext = PL_strrchr(relpath, '.');
    493        if (!ext)
    494            return 0;
    495        if (!PL_HashTableLookup(extensions, ext))
    496            return 0;
    497    }
    498 
    499    snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
    500 
    501    fprintf(mf, "\n");
    502 
    503    use_js = 0;
    504 
    505    if (scriptdir && !PORT_Strcmp(scriptdir, reldir))
    506        use_js++;
    507 
    508    /* sign non-.js files inside .arc directories using the javascript magic */
    509 
    510    if ((PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3) &&
    511        (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
    512        use_js++;
    513 
    514    if (use_js) {
    515        fprintf(mf, "Name: %s\n", filename);
    516        fprintf(mf, "Magic: javascript\n");
    517 
    518        if (optimize == 0)
    519            fprintf(mf, "javascript.id: %s\n", filename);
    520 
    521        if (metafile)
    522            add_meta(mf, filename);
    523    } else {
    524        fprintf(mf, "Name: %s\n", relpath);
    525        if (metafile)
    526            add_meta(mf, relpath);
    527    }
    528 
    529    JAR_digest_file(fullname, &dig);
    530 
    531    if (optimize == 0) {
    532        fprintf(mf, "Digest-Algorithms: MD5 SHA1\n");
    533 
    534        md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
    535        fprintf(mf, "MD5-Digest: %s\n", md5);
    536        PORT_Free(md5);
    537    }
    538 
    539    sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
    540    fprintf(mf, "SHA1-Digest: %s\n", sha1);
    541    PORT_Free(sha1);
    542 
    543    if (!use_js) {
    544        JzipAdd(fullname, relpath, zipfile, compression_level);
    545    }
    546 
    547    return 0;
    548 }
    549 
    550 /*
    551 *  a d d _ m e t a
    552 *
    553 *  Parse the metainfo file, and add any details
    554 *  necessary to the manifest file. In most cases you
    555 *  should be using the -i option (ie, for SmartUpdate).
    556 *
    557 */
    558 static int
    559 add_meta(FILE *fp, char *name)
    560 {
    561    FILE *met;
    562    char buf[BUFSIZ];
    563 
    564    int place;
    565    char *pattern, *meta;
    566 
    567    int num = 0;
    568 
    569    if ((met = fopen(metafile, "r")) != NULL) {
    570        while (fgets(buf, BUFSIZ, met)) {
    571            char *s;
    572 
    573            for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
    574                ;
    575            *s = 0;
    576 
    577            if (*buf == 0)
    578                continue;
    579 
    580            pattern = buf;
    581 
    582            /* skip to whitespace */
    583            for (s = buf; *s && *s != ' ' && *s != '\t'; s++)
    584                ;
    585 
    586            /* terminate pattern */
    587            if (*s == ' ' || *s == '\t')
    588                *s++ = 0;
    589 
    590            /* eat through whitespace */
    591            while (*s == ' ' || *s == '\t')
    592                s++;
    593 
    594            meta = s;
    595 
    596            /* this will eventually be regexp matching */
    597 
    598            place = 0;
    599            if (!PORT_Strcmp(pattern, name))
    600                place = 1;
    601 
    602            if (place) {
    603                num++;
    604                if (verbosity >= 0) {
    605                    PR_fprintf(outputFD, "[%s] %s\n", name, meta);
    606                }
    607                fprintf(fp, "%s\n", meta);
    608            }
    609        }
    610        fclose(met);
    611    } else {
    612        PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
    613                   metafile);
    614        errorCount++;
    615        exit(ERRX);
    616    }
    617 
    618    return num;
    619 }
    620 
    621 /**********************************************************************
    622 *
    623 * S i g n F i l e
    624 */
    625 static int
    626 SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert)
    627 {
    628    int nb;
    629    char ibuf[4096], digestdata[32];
    630    const SECHashObject *hashObj;
    631    void *hashcx;
    632    unsigned int len;
    633 
    634    SECItem digest;
    635    SEC_PKCS7ContentInfo *cinfo;
    636    SECStatus rv;
    637 
    638    if (outFile == NULL || inFile == NULL || cert == NULL)
    639        return -1;
    640 
    641    /* XXX probably want to extend interface to allow other hash algorithms */
    642    hashObj = HASH_GetHashObject(HASH_AlgSHA1);
    643 
    644    hashcx = (*hashObj->create)();
    645    if (hashcx == NULL)
    646        return -1;
    647 
    648    (*hashObj->begin)(hashcx);
    649 
    650    for (;;) {
    651        if (feof(inFile))
    652            break;
    653        nb = fread(ibuf, 1, sizeof(ibuf), inFile);
    654        if (nb == 0) {
    655            if (ferror(inFile)) {
    656                PORT_SetError(SEC_ERROR_IO);
    657                (*hashObj->destroy)(hashcx, PR_TRUE);
    658                return -1;
    659            }
    660            /* eof */
    661            break;
    662        }
    663        (*hashObj->update)(hashcx, (unsigned char *)ibuf, nb);
    664    }
    665 
    666    (*hashObj->end)(hashcx, (unsigned char *)digestdata, &len, 32);
    667    (*hashObj->destroy)(hashcx, PR_TRUE);
    668 
    669    digest.data = (unsigned char *)digestdata;
    670    digest.len = len;
    671 
    672    cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL,
    673                                      SEC_OID_SHA1, &digest, NULL, NULL);
    674 
    675    if (cinfo == NULL)
    676        return -1;
    677 
    678    rv = SEC_PKCS7IncludeCertChain(cinfo, NULL);
    679    if (rv != SECSuccess) {
    680        SEC_PKCS7DestroyContentInfo(cinfo);
    681        return -1;
    682    }
    683 
    684    if (no_time == 0) {
    685        rv = SEC_PKCS7AddSigningTime(cinfo);
    686        if (rv != SECSuccess) {
    687            /* don't check error */
    688        }
    689    }
    690 
    691    rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata);
    692 
    693    SEC_PKCS7DestroyContentInfo(cinfo);
    694 
    695    if (rv != SECSuccess)
    696        return -1;
    697 
    698    return 0;
    699 }
    700 
    701 /*
    702 *  g e n e r a t e _ S F _ f i l e
    703 *
    704 *  From the supplied manifest file, calculates
    705 *  digests on the various sections, creating a .SF
    706 *  file in the process.
    707 *
    708 */
    709 static int
    710 generate_SF_file(char *manifile, char *who)
    711 {
    712    FILE *sfFile;
    713    FILE *mfFile;
    714    long r1, r2, r3;
    715    char whofile[FNSIZE];
    716    char *buf, *name = NULL;
    717    char *md5, *sha1;
    718    JAR_Digest dig;
    719    int line = 0;
    720 
    721    strcpy(whofile, who);
    722 
    723    if ((mfFile = fopen(manifile, "rb")) == NULL) {
    724        perror(manifile);
    725        exit(ERRX);
    726    }
    727 
    728    if ((sfFile = fopen(whofile, "wb")) == NULL) {
    729        perror(who);
    730        exit(ERRX);
    731    }
    732 
    733    buf = (char *)PORT_ZAlloc(BUFSIZ);
    734 
    735    if (buf)
    736        name = (char *)PORT_ZAlloc(BUFSIZ);
    737 
    738    if (buf == NULL || name == NULL)
    739        out_of_memory();
    740 
    741    fprintf(sfFile, "Signature-Version: 1.0\n");
    742    fprintf(sfFile, "Created-By: %s\n", CREATOR);
    743    fprintf(sfFile, "Comments: %s\n", BREAKAGE);
    744 
    745    if (fgets(buf, BUFSIZ, mfFile) == NULL) {
    746        PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
    747        errorCount++;
    748        exit(ERRX);
    749    }
    750 
    751    if (strncmp(buf, "Manifest-Version:", 17)) {
    752        PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
    753        errorCount++;
    754        exit(ERRX);
    755    }
    756 
    757    fseek(mfFile, 0L, SEEK_SET);
    758 
    759    /* Process blocks of headers, and calculate their hashen */
    760 
    761    while (1) {
    762        /* Beginning range */
    763        r1 = ftell(mfFile);
    764 
    765        if (fgets(name, BUFSIZ, mfFile) == NULL)
    766            break;
    767 
    768        line++;
    769 
    770        if (r1 != 0 && strncmp(name, "Name:", 5)) {
    771            PR_fprintf(errorFD,
    772                       "warning: unexpected input in manifest file \"%s\" at line %d:\n",
    773                       manifile, line);
    774            PR_fprintf(errorFD, "%s\n", name);
    775            warningCount++;
    776        }
    777 
    778        r2 = r1;
    779        while (fgets(buf, BUFSIZ, mfFile)) {
    780            if (*buf == 0 || *buf == '\n' || *buf == '\r')
    781                break;
    782 
    783            line++;
    784 
    785            /* Ending range for hashing */
    786            r2 = ftell(mfFile);
    787        }
    788 
    789        r3 = ftell(mfFile);
    790 
    791        if (r1) {
    792            fprintf(sfFile, "\n");
    793            fprintf(sfFile, "%s", name);
    794        }
    795 
    796        calculate_MD5_range(mfFile, r1, r2, &dig);
    797 
    798        if (optimize == 0) {
    799            fprintf(sfFile, "Digest-Algorithms: MD5 SHA1\n");
    800 
    801            md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
    802            fprintf(sfFile, "MD5-Digest: %s\n", md5);
    803            PORT_Free(md5);
    804        }
    805 
    806        sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
    807        fprintf(sfFile, "SHA1-Digest: %s\n", sha1);
    808        PORT_Free(sha1);
    809 
    810        /* restore normalcy after changing offset position */
    811        fseek(mfFile, r3, SEEK_SET);
    812    }
    813 
    814    PORT_Free(buf);
    815    PORT_Free(name);
    816 
    817    fclose(sfFile);
    818    fclose(mfFile);
    819 
    820    return 0;
    821 }
    822 
    823 /*
    824 *  c a l c u l a t e _ M D 5 _ r a n g e
    825 *
    826 *  Calculate the MD5 digest on a range of bytes in
    827 *  the specified fopen'd file. Returns base64.
    828 *
    829 */
    830 static int
    831 calculate_MD5_range(FILE *fp, long r1, long r2, JAR_Digest *dig)
    832 {
    833    int num;
    834    int range;
    835    unsigned char *buf;
    836    SECStatus rv;
    837 
    838    range = r2 - r1;
    839 
    840    /* position to the beginning of range */
    841    fseek(fp, r1, SEEK_SET);
    842 
    843    buf = (unsigned char *)PORT_ZAlloc(range);
    844    if (buf == NULL)
    845        out_of_memory();
    846 
    847    if ((num = fread(buf, 1, range, fp)) != range) {
    848        PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
    849                   range, num);
    850        errorCount++;
    851        exit(ERRX);
    852    }
    853 
    854    rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range);
    855    if (rv == SECSuccess) {
    856        rv = PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range);
    857    }
    858    if (rv != SECSuccess) {
    859        PR_fprintf(errorFD, "%s: can't generate digest context\n",
    860                   PROGRAM_NAME);
    861        errorCount++;
    862        exit(ERRX);
    863    }
    864 
    865    PORT_Free(buf);
    866 
    867    return 0;
    868 }
    869 
    870 static void
    871 SignOut(void *arg, const char *buf, unsigned long len)
    872 {
    873    fwrite(buf, len, 1, (FILE *)arg);
    874 }