tor-browser

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

jarver.c (32187B)


      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 /*
      6 *  JARVER
      7 *
      8 *  Jarnature Parsing & Verification
      9 */
     10 
     11 #include "nssrenam.h"
     12 #include "jar.h"
     13 #include "jarint.h"
     14 #include "certdb.h"
     15 #include "certt.h"
     16 #include "secpkcs7.h"
     17 #include "secder.h"
     18 
     19 #define SZ 512
     20 
     21 static int
     22 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length);
     23 
     24 static void
     25 jar_catch_bytes(void *arg, const char *buf, unsigned long len);
     26 
     27 static int
     28 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo);
     29 
     30 static char *
     31 jar_eat_line(int lines, int eating, char *data, long *len);
     32 
     33 static JAR_Digest *
     34 jar_digest_section(char *manifest, long length);
     35 
     36 static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path);
     37 
     38 static int
     39 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
     40                            long length, JAR *jar);
     41 
     42 static int
     43 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert);
     44 
     45 static char *jar_basename(const char *path);
     46 
     47 static int
     48 jar_signal(int status, JAR *jar, const char *metafile, char *pathname);
     49 
     50 #ifdef DEBUG
     51 static int jar_insanity_check(char *data, long length);
     52 #endif
     53 
     54 int
     55 jar_parse_mf(JAR *jar, char *raw_manifest, long length,
     56             const char *path, const char *url);
     57 
     58 int
     59 jar_parse_sf(JAR *jar, char *raw_manifest, long length,
     60             const char *path, const char *url);
     61 
     62 int
     63 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
     64              long length);
     65 
     66 int
     67 jar_parse_any(JAR *jar, int type, JAR_Signer *signer,
     68              char *raw_manifest, long length, const char *path,
     69              const char *url);
     70 
     71 static int
     72 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig);
     73 
     74 /*
     75 *  J A R _ p a r s e _ m a n i f e s t
     76 *
     77 *  Pass manifest files to this function. They are
     78 *  decoded and placed into internal representations.
     79 *
     80 *  Accepts both signature and manifest files. Use
     81 *  the same "jar" for both.
     82 *
     83 */
     84 int
     85 JAR_parse_manifest(JAR *jar, char *raw_manifest, long length,
     86                   const char *path, const char *url)
     87 {
     88    int filename_free = 0;
     89 
     90    /* fill in the path, if supplied. This is the location
     91       of the jar file on disk, if known */
     92 
     93    if (jar->filename == NULL && path) {
     94        jar->filename = PORT_Strdup(path);
     95        if (jar->filename == NULL)
     96            return JAR_ERR_MEMORY;
     97        filename_free = 1;
     98    }
     99 
    100    /* fill in the URL, if supplied. This is the place
    101       from which the jar file was retrieved. */
    102 
    103    if (jar->url == NULL && url) {
    104        jar->url = PORT_Strdup(url);
    105        if (jar->url == NULL) {
    106            if (filename_free) {
    107                PORT_Free(jar->filename);
    108            }
    109            return JAR_ERR_MEMORY;
    110        }
    111    }
    112 
    113    /* Determine what kind of file this is from the META-INF
    114       directory. It could be MF, SF, or a binary RSA/DSA file */
    115 
    116    if (!PORT_Strncasecmp(raw_manifest, "Manifest-Version:", 17)) {
    117        return jar_parse_mf(jar, raw_manifest, length, path, url);
    118    } else if (!PORT_Strncasecmp(raw_manifest, "Signature-Version:", 18)) {
    119        return jar_parse_sf(jar, raw_manifest, length, path, url);
    120    } else {
    121        /* This is probably a binary signature */
    122        return jar_parse_sig(jar, path, raw_manifest, length);
    123    }
    124 }
    125 
    126 /*
    127 *  j a r _ p a r s e _ s i g
    128 *
    129 *  Pass some manner of RSA or DSA digital signature
    130 *  on, after checking to see if it comes at an appropriate state.
    131 *
    132 */
    133 int
    134 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
    135              long length)
    136 {
    137    JAR_Signer *signer;
    138    int status = JAR_ERR_ORDER;
    139 
    140    if (length <= 128) {
    141        /* signature is way too small */
    142        return JAR_ERR_SIG;
    143    }
    144 
    145    /* make sure that MF and SF have already been processed */
    146 
    147    if (jar->globalmeta == NULL)
    148        return JAR_ERR_ORDER;
    149 
    150    /* Determine whether or not this RSA file has
    151       has an associated SF file */
    152 
    153    if (path) {
    154        char *owner;
    155        owner = jar_basename(path);
    156 
    157        if (owner == NULL)
    158            return JAR_ERR_MEMORY;
    159 
    160        signer = jar_get_signer(jar, owner);
    161        PORT_Free(owner);
    162    } else
    163        signer = jar_get_signer(jar, "*");
    164 
    165    if (signer == NULL)
    166        return JAR_ERR_ORDER;
    167 
    168    /* Do not pass a huge pointer to this function,
    169       since the underlying security code is unaware. We will
    170       never pass >64k through here. */
    171 
    172    if (length > 64000) {
    173        /* this digital signature is way too big */
    174        return JAR_ERR_SIG;
    175    }
    176 
    177    /* don't expense unneeded calloc overhead on non-win16 */
    178    status = jar_parse_digital_signature(raw_manifest, signer, length, jar);
    179 
    180    return status;
    181 }
    182 
    183 /*
    184 *  j a r _ p a r s e _ m f
    185 *
    186 *  Parse the META-INF/manifest.mf file, whose
    187 *  information applies to all signers.
    188 *
    189 */
    190 int
    191 jar_parse_mf(JAR *jar, char *raw_manifest, long length,
    192             const char *path, const char *url)
    193 {
    194    if (jar->globalmeta) {
    195        /* refuse a second manifest file, if passed for some reason */
    196        return JAR_ERR_ORDER;
    197    }
    198 
    199    /* remember a digest for the global section */
    200    jar->globalmeta = jar_digest_section(raw_manifest, length);
    201    if (jar->globalmeta == NULL)
    202        return JAR_ERR_MEMORY;
    203    return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length,
    204                         path, url);
    205 }
    206 
    207 /*
    208 *  j a r _ p a r s e _ s f
    209 *
    210 *  Parse META-INF/xxx.sf, a digitally signed file
    211 *  pointing to a subset of MF sections.
    212 *
    213 */
    214 int
    215 jar_parse_sf(JAR *jar, char *raw_manifest, long length,
    216             const char *path, const char *url)
    217 {
    218    JAR_Signer *signer = NULL;
    219    int status = JAR_ERR_MEMORY;
    220 
    221    if (jar->globalmeta == NULL) {
    222        /* It is a requirement that the MF file be passed before the SF file */
    223        return JAR_ERR_ORDER;
    224    }
    225 
    226    signer = JAR_new_signer();
    227    if (signer == NULL)
    228        goto loser;
    229 
    230    if (path) {
    231        signer->owner = jar_basename(path);
    232        if (signer->owner == NULL)
    233            goto loser;
    234    }
    235 
    236    /* check for priors. When someone doctors a jar file
    237       to contain identical path entries, prevent the second
    238       one from affecting JAR functions */
    239    if (jar_get_signer(jar, signer->owner)) {
    240        /* someone is trying to spoof us */
    241        status = JAR_ERR_ORDER;
    242        goto loser;
    243    }
    244 
    245    /* remember its digest */
    246    signer->digest = JAR_calculate_digest(raw_manifest, length);
    247    if (signer->digest == NULL)
    248        goto loser;
    249 
    250    /* Add this signer to the jar */
    251    ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer,
    252            sizeof(JAR_Signer));
    253 
    254    return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length,
    255                         path, url);
    256 
    257 loser:
    258    if (signer)
    259        JAR_destroy_signer(signer);
    260    return status;
    261 }
    262 
    263 /*
    264 *  j a r _ p a r s e _ a n y
    265 *
    266 *  Parse a MF or SF manifest file.
    267 *
    268 */
    269 int
    270 jar_parse_any(JAR *jar, int type, JAR_Signer *signer,
    271              char *raw_manifest, long length, const char *path,
    272              const char *url)
    273 {
    274    int status;
    275    long raw_len;
    276    JAR_Digest *dig, *mfdig = NULL;
    277    char line[SZ];
    278    char x_name[SZ], x_md5[SZ], x_sha[SZ];
    279    char *x_info;
    280    char *sf_md5 = NULL, *sf_sha1 = NULL;
    281 
    282    *x_name = 0;
    283    *x_md5 = 0;
    284    *x_sha = 0;
    285 
    286    PORT_Assert(length > 0);
    287    raw_len = length;
    288 
    289 #ifdef DEBUG
    290    if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0)
    291        return status;
    292 #endif
    293 
    294    /* null terminate the first line */
    295    raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len);
    296 
    297    /* skip over the preliminary section */
    298    /* This is one section at the top of the file with global metainfo */
    299    while (raw_len > 0) {
    300        JAR_Metainfo *met;
    301 
    302        raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
    303        if (raw_len <= 0 || !*raw_manifest)
    304            break;
    305 
    306        met = PORT_ZNew(JAR_Metainfo);
    307        if (met == NULL)
    308            return JAR_ERR_MEMORY;
    309 
    310        /* Parse out the header & info */
    311        if (PORT_Strlen(raw_manifest) >= SZ) {
    312            /* almost certainly nonsense */
    313            PORT_Free(met);
    314            continue;
    315        }
    316 
    317        PORT_Strcpy(line, raw_manifest);
    318        x_info = line;
    319 
    320        while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
    321            x_info++;
    322 
    323        if (*x_info)
    324            *x_info++ = 0;
    325 
    326        while (*x_info == ' ' || *x_info == '\t')
    327            x_info++;
    328 
    329        /* metainfo (name, value) pair is now (line, x_info) */
    330        met->header = PORT_Strdup(line);
    331        met->info = PORT_Strdup(x_info);
    332 
    333        if (type == jarTypeMF) {
    334            ADDITEM(jar->metainfo, jarTypeMeta,
    335                    /* pathname */ NULL, met, sizeof(JAR_Metainfo));
    336        }
    337 
    338        /* For SF files, this metadata may be the digests
    339           of the MF file, still in the "met" structure. */
    340 
    341        if (type == jarTypeSF) {
    342            if (!PORT_Strcasecmp(line, "MD5-Digest")) {
    343                sf_md5 = (char *)met->info;
    344            } else if (!PORT_Strcasecmp(line, "SHA1-Digest") ||
    345                       !PORT_Strcasecmp(line, "SHA-Digest")) {
    346                sf_sha1 = (char *)met->info;
    347            } else {
    348                PORT_Free(met->info);
    349                met->info = NULL;
    350            }
    351        }
    352 
    353        if (type != jarTypeMF) {
    354            PORT_Free(met->header);
    355            if ((type != jarTypeSF || !jar->globalmeta) && met->info) {
    356                PORT_Free(met->info);
    357            }
    358            PORT_Free(met);
    359        }
    360    }
    361 
    362    if (type == jarTypeSF && jar->globalmeta) {
    363        /* this is a SF file which may contain a digest of the manifest.mf's
    364           global metainfo. */
    365 
    366        int match = 0;
    367        JAR_Digest *glob = jar->globalmeta;
    368 
    369        if (sf_md5) {
    370            unsigned int md5_length;
    371            unsigned char *md5_digest;
    372 
    373            md5_digest = ATOB_AsciiToData(sf_md5, &md5_length);
    374            PORT_Assert(md5_length == MD5_LENGTH);
    375            PORT_Free(sf_md5);
    376 
    377            if (md5_length != MD5_LENGTH)
    378                return JAR_ERR_CORRUPT;
    379 
    380            match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH);
    381            PORT_Free(md5_digest);
    382        }
    383 
    384        if (sf_sha1 && match == 0) {
    385            unsigned int sha1_length;
    386            unsigned char *sha1_digest;
    387 
    388            sha1_digest = ATOB_AsciiToData(sf_sha1, &sha1_length);
    389            PORT_Assert(sha1_length == SHA1_LENGTH);
    390            PORT_Free(sf_sha1);
    391 
    392            if (sha1_length != SHA1_LENGTH)
    393                return JAR_ERR_CORRUPT;
    394 
    395            match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH);
    396            PORT_Free(sha1_digest);
    397        }
    398 
    399        if (match != 0) {
    400            /* global digest doesn't match, SF file therefore invalid */
    401            jar->valid = JAR_ERR_METADATA;
    402            return JAR_ERR_METADATA;
    403        }
    404    }
    405 
    406    /* done with top section of global data */
    407    while (raw_len > 0) {
    408        *x_md5 = 0;
    409        *x_sha = 0;
    410        *x_name = 0;
    411 
    412        /* If this is a manifest file, attempt to get a digest of the following
    413           section, without damaging it. This digest will be saved later. */
    414 
    415        if (type == jarTypeMF) {
    416            char *sec;
    417            long sec_len = raw_len;
    418 
    419            if (!*raw_manifest || *raw_manifest == '\n') {
    420                /* skip the blank line */
    421                sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len);
    422            } else
    423                sec = raw_manifest;
    424 
    425            if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) {
    426                if (type == jarTypeMF)
    427                    mfdig = jar_digest_section(sec, sec_len);
    428                else
    429                    mfdig = NULL;
    430            }
    431        }
    432 
    433        while (raw_len > 0) {
    434            raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
    435            if (raw_len <= 0 || !*raw_manifest)
    436                break; /* blank line, done with this entry */
    437 
    438            if (PORT_Strlen(raw_manifest) >= SZ) {
    439                /* almost certainly nonsense */
    440                continue;
    441            }
    442 
    443            /* Parse out the name/value pair */
    444            PORT_Strcpy(line, raw_manifest);
    445            x_info = line;
    446 
    447            while (*x_info && *x_info != ' ' && *x_info != '\t' &&
    448                   *x_info != ':')
    449                x_info++;
    450 
    451            if (*x_info)
    452                *x_info++ = 0;
    453 
    454            while (*x_info == ' ' || *x_info == '\t')
    455                x_info++;
    456 
    457            if (!PORT_Strcasecmp(line, "Name"))
    458                PORT_Strcpy(x_name, x_info);
    459            else if (!PORT_Strcasecmp(line, "MD5-Digest"))
    460                PORT_Strcpy(x_md5, x_info);
    461            else if (!PORT_Strcasecmp(line, "SHA1-Digest") ||
    462                     !PORT_Strcasecmp(line, "SHA-Digest"))
    463                PORT_Strcpy(x_sha, x_info);
    464 
    465            /* Algorithm list is meta info we don't care about; keeping it out
    466               of metadata saves significant space for large jar files */
    467            else if (!PORT_Strcasecmp(line, "Digest-Algorithms") ||
    468                     !PORT_Strcasecmp(line, "Hash-Algorithms"))
    469                continue;
    470 
    471            /* Meta info is only collected for the manifest.mf file,
    472               since the JAR_get_metainfo call does not support identity */
    473            else if (type == jarTypeMF) {
    474                JAR_Metainfo *met;
    475 
    476                /* this is meta-data */
    477                met = PORT_ZNew(JAR_Metainfo);
    478                if (met == NULL)
    479                    return JAR_ERR_MEMORY;
    480 
    481                /* metainfo (name, value) pair is now (line, x_info) */
    482                if ((met->header = PORT_Strdup(line)) == NULL) {
    483                    PORT_Free(met);
    484                    return JAR_ERR_MEMORY;
    485                }
    486 
    487                if ((met->info = PORT_Strdup(x_info)) == NULL) {
    488                    PORT_Free(met->header);
    489                    PORT_Free(met);
    490                    return JAR_ERR_MEMORY;
    491                }
    492 
    493                ADDITEM(jar->metainfo, jarTypeMeta,
    494                        x_name, met, sizeof(JAR_Metainfo));
    495            }
    496        }
    497 
    498        if (!*x_name) {
    499            /* Whatever that was, it wasn't an entry, because we didn't get a
    500               name. We don't really have anything, so don't record this. */
    501            continue;
    502        }
    503 
    504        dig = PORT_ZNew(JAR_Digest);
    505        if (dig == NULL)
    506            return JAR_ERR_MEMORY;
    507 
    508        if (*x_md5) {
    509            unsigned int binary_length;
    510            unsigned char *binary_digest;
    511 
    512            binary_digest = ATOB_AsciiToData(x_md5, &binary_length);
    513            PORT_Assert(binary_length == MD5_LENGTH);
    514            if (binary_length != MD5_LENGTH) {
    515                PORT_Free(dig);
    516                return JAR_ERR_CORRUPT;
    517            }
    518            memcpy(dig->md5, binary_digest, MD5_LENGTH);
    519            dig->md5_status = jarHashPresent;
    520            PORT_Free(binary_digest);
    521        }
    522 
    523        if (*x_sha) {
    524            unsigned int binary_length;
    525            unsigned char *binary_digest;
    526 
    527            binary_digest = ATOB_AsciiToData(x_sha, &binary_length);
    528            PORT_Assert(binary_length == SHA1_LENGTH);
    529            if (binary_length != SHA1_LENGTH) {
    530                PORT_Free(dig);
    531                return JAR_ERR_CORRUPT;
    532            }
    533            memcpy(dig->sha1, binary_digest, SHA1_LENGTH);
    534            dig->sha1_status = jarHashPresent;
    535            PORT_Free(binary_digest);
    536        }
    537 
    538        PORT_Assert(type == jarTypeMF || type == jarTypeSF);
    539        if (type == jarTypeMF) {
    540            ADDITEM(jar->hashes, jarTypeMF, x_name, dig, sizeof(JAR_Digest));
    541        } else if (type == jarTypeSF) {
    542            ADDITEM(signer->sf, jarTypeSF, x_name, dig, sizeof(JAR_Digest));
    543        } else {
    544            PORT_Free(dig);
    545            return JAR_ERR_ORDER;
    546        }
    547 
    548        /* we're placing these calculated digests of manifest.mf
    549           sections in a list where they can subsequently be forgotten */
    550        if (type == jarTypeMF && mfdig) {
    551            ADDITEM(jar->manifest, jarTypeSect,
    552                    x_name, mfdig, sizeof(JAR_Digest));
    553            mfdig = NULL;
    554        }
    555 
    556        /* Retrieve our saved SHA1 digest from saved copy and check digests.
    557           This is just comparing the digest of the MF section as indicated in
    558           the SF file with the one we remembered from parsing the MF file */
    559 
    560        if (type == jarTypeSF) {
    561            if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0)
    562                return status;
    563        }
    564    }
    565 
    566    return 0;
    567 }
    568 
    569 static int
    570 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig)
    571 {
    572    int cv;
    573    int status;
    574 
    575    JAR_Digest *savdig;
    576 
    577    savdig = jar_get_mf_digest(jar, x_name);
    578    if (savdig == NULL) {
    579        /* no .mf digest for this pathname */
    580        status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name);
    581        if (status < 0)
    582            return 0; /* was continue; */
    583        return status;
    584    }
    585 
    586    /* check for md5 consistency */
    587    if (dig->md5_status) {
    588        cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH);
    589        /* md5 hash of .mf file is not what expected */
    590        if (cv) {
    591            status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
    592 
    593            /* bad hash, man */
    594            dig->md5_status = jarHashBad;
    595            savdig->md5_status = jarHashBad;
    596 
    597            if (status < 0)
    598                return 0; /* was continue; */
    599            return status;
    600        }
    601    }
    602 
    603    /* check for sha1 consistency */
    604    if (dig->sha1_status) {
    605        cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH);
    606        /* sha1 hash of .mf file is not what expected */
    607        if (cv) {
    608            status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
    609 
    610            /* bad hash, man */
    611            dig->sha1_status = jarHashBad;
    612            savdig->sha1_status = jarHashBad;
    613 
    614            if (status < 0)
    615                return 0; /* was continue; */
    616            return status;
    617        }
    618    }
    619    return 0;
    620 }
    621 
    622 #ifdef DEBUG
    623 /*
    624 *  j a r _ i n s a n i t y _ c h e c k
    625 *
    626 *  Check for illegal characters (or possibly so)
    627 *  in the manifest files, to detect potential memory
    628 *  corruption by our neighbors. Debug only, since
    629 *  not I18N safe.
    630 *
    631 */
    632 static int
    633 jar_insanity_check(char *data, long length)
    634 {
    635    int c;
    636    long off;
    637 
    638    for (off = 0; off < length; off++) {
    639        c = data[off];
    640        if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128))
    641            continue;
    642        return JAR_ERR_CORRUPT;
    643    }
    644    return 0;
    645 }
    646 #endif
    647 
    648 /*
    649 *  j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
    650 *
    651 *  Parse an RSA or DSA (or perhaps other) digital signature.
    652 *  Right now everything is PKCS7.
    653 *
    654 */
    655 static int
    656 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
    657                            long length, JAR *jar)
    658 {
    659    return jar_validate_pkcs7(jar, signer, raw_manifest, length);
    660 }
    661 
    662 /*
    663 *  j a r _ a d d _ c e r t
    664 *
    665 *  Add information for the given certificate
    666 *  (or whatever) to the JAR linked list. A pointer
    667 *  is passed for some relevant reference, say
    668 *  for example the original certificate.
    669 *
    670 */
    671 static int
    672 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert)
    673 {
    674    JAR_Cert *fing;
    675    unsigned char *keyData;
    676 
    677    if (cert == NULL)
    678        return JAR_ERR_ORDER;
    679 
    680    fing = PORT_ZNew(JAR_Cert);
    681    if (fing == NULL)
    682        goto loser;
    683 
    684    fing->cert = CERT_DupCertificate(cert);
    685 
    686    /* get the certkey */
    687    fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len;
    688    fing->key = keyData = (unsigned char *)PORT_ZAlloc(fing->length);
    689    if (fing->key == NULL)
    690        goto loser;
    691    keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff;
    692    keyData[1] = ((cert->derIssuer.len) & 0xff);
    693    PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len);
    694    PORT_Memcpy(&keyData[2 + cert->derIssuer.len], cert->serialNumber.data,
    695                cert->serialNumber.len);
    696 
    697    ADDITEM(signer->certs, type, NULL, fing, sizeof(JAR_Cert));
    698    return 0;
    699 
    700 loser:
    701    if (fing) {
    702        if (fing->cert)
    703            CERT_DestroyCertificate(fing->cert);
    704        PORT_Free(fing);
    705    }
    706    return JAR_ERR_MEMORY;
    707 }
    708 
    709 /*
    710 *  e a t _ l i n e
    711 *
    712 * Reads and/or modifies input buffer "data" of length "*len".
    713 * This function does zero, one or two of the following tasks:
    714 * 1) if "lines" is non-zero, it reads and discards that many lines from
    715 *    the input.  NUL characters are treated as end-of-line characters,
    716 *    not as end-of-input characters.  The input is NOT NUL terminated.
    717 *    Note: presently, all callers pass either 0 or 1 for lines.
    718 * 2) After skipping the specified number of input lines, if "eating" is
    719 *    non-zero, it finds the end of the next line of input and replaces
    720 *    the end of line character(s) with a NUL character.
    721 *  This function modifies the input buffer, containing the file, in place.
    722 *  This function handles PC, Mac, and Unix style text files.
    723 *  On entry, *len contains the maximum number of characters that this
    724 *  function should ever examine, starting with the character in *data.
    725 *  On return, *len is reduced by the number of characters skipped by the
    726 *  first task, if any;
    727 *  If lines is zero and eating is false, this function returns
    728 *  the value in the data argument, but otherwise does nothing.
    729 */
    730 static char *
    731 jar_eat_line(int lines, int eating, char *data, long *len)
    732 {
    733    char *start = data;
    734    long maxLen = *len;
    735 
    736    if (maxLen <= 0)
    737        return start;
    738 
    739 #define GO_ON ((data - start) < maxLen)
    740 
    741    /* Eat the requisite number of lines, if any;
    742       prior to terminating the current line with a 0. */
    743    for (/* yip */; lines > 0; lines--) {
    744        while (GO_ON && *data && *data != '\r' && *data != '\n')
    745            data++;
    746 
    747        /* Eat any leading CR */
    748        if (GO_ON && *data == '\r')
    749            data++;
    750 
    751        /* After the CR, ok to eat one LF */
    752        if (GO_ON && *data == '\n')
    753            data++;
    754 
    755        /* If there are NULs, this function probably put them there */
    756        while (GO_ON && !*data)
    757            data++;
    758    }
    759    maxLen -= data - start; /* we have this many characters left. */
    760    *len = maxLen;
    761    start = data; /* now start again here.            */
    762    if (maxLen > 0 && eating) {
    763        /* Terminate this line with a 0 */
    764        while (GO_ON && *data && *data != '\n' && *data != '\r')
    765            data++;
    766 
    767        /* If not past the end, we are allowed to eat one CR */
    768        if (GO_ON && *data == '\r')
    769            *data++ = 0;
    770 
    771        /* After the CR (if any), if not past the end, ok to eat one LF */
    772        if (GO_ON && *data == '\n')
    773            *data++ = 0;
    774    }
    775    return start;
    776 }
    777 #undef GO_ON
    778 
    779 /*
    780 *  j a r _ d i g e s t _ s e c t i o n
    781 *
    782 *  Return the digests of the next section of the manifest file.
    783 *  Does not damage the manifest file, unlike parse_manifest.
    784 *
    785 */
    786 static JAR_Digest *
    787 jar_digest_section(char *manifest, long length)
    788 {
    789    long global_len;
    790    char *global_end;
    791 
    792    global_end = manifest;
    793    global_len = length;
    794 
    795    while (global_len > 0) {
    796        global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len);
    797        if (global_len > 0 && (*global_end == 0 || *global_end == '\n'))
    798            break;
    799    }
    800    return JAR_calculate_digest(manifest, global_end - manifest);
    801 }
    802 
    803 /*
    804 *  J A R _ v e r i f y _ d i g e s t
    805 *
    806 *  Verifies that a precalculated digest matches the
    807 *  expected value in the manifest.
    808 *
    809 */
    810 int PR_CALLBACK
    811 JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig)
    812 {
    813    JAR_Item *it;
    814    JAR_Digest *shindig;
    815    ZZLink *link;
    816    ZZList *list = jar->hashes;
    817    int result1 = 0;
    818    int result2 = 0;
    819 
    820    if (jar->valid < 0) {
    821        /* signature not valid */
    822        return JAR_ERR_SIG;
    823    }
    824    if (ZZ_ListEmpty(list)) {
    825        /* empty list */
    826        return JAR_ERR_PNF;
    827    }
    828 
    829    for (link = ZZ_ListHead(list);
    830         !ZZ_ListIterDone(list, link);
    831         link = link->next) {
    832        it = link->thing;
    833        if (it->type == jarTypeMF &&
    834            it->pathname && !PORT_Strcmp(it->pathname, name)) {
    835            shindig = (JAR_Digest *)it->data;
    836            if (shindig->md5_status) {
    837                if (shindig->md5_status == jarHashBad)
    838                    return JAR_ERR_HASH;
    839                result1 = memcmp(dig->md5, shindig->md5, MD5_LENGTH);
    840            }
    841            if (shindig->sha1_status) {
    842                if (shindig->sha1_status == jarHashBad)
    843                    return JAR_ERR_HASH;
    844                result2 = memcmp(dig->sha1, shindig->sha1, SHA1_LENGTH);
    845            }
    846            return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH;
    847        }
    848    }
    849    return JAR_ERR_PNF;
    850 }
    851 
    852 /*
    853 *  J A R _ f e t c h _ c e r t
    854 *
    855 *  Given an opaque identifier of a certificate,
    856 *  return the full certificate.
    857 *
    858 * The new function, which retrieves by key.
    859 *
    860 */
    861 CERTCertificate *
    862 JAR_fetch_cert(long length, void *key)
    863 {
    864    CERTIssuerAndSN issuerSN;
    865    CERTCertificate *cert = NULL;
    866    CERTCertDBHandle *certdb;
    867 
    868    certdb = JAR_open_database();
    869    if (certdb) {
    870        unsigned char *keyData = (unsigned char *)key;
    871        issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0];
    872        issuerSN.derIssuer.data = &keyData[2];
    873        issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len);
    874        issuerSN.serialNumber.data = &keyData[2 + issuerSN.derIssuer.len];
    875        cert = CERT_FindCertByIssuerAndSN(certdb, &issuerSN);
    876        JAR_close_database(certdb);
    877    }
    878    return cert;
    879 }
    880 
    881 /*
    882 *  j a r _ g e t _ m f _ d i g e s t
    883 *
    884 *  Retrieve a corresponding saved digest over a section
    885 *  of the main manifest file.
    886 *
    887 */
    888 static JAR_Digest *
    889 jar_get_mf_digest(JAR *jar, char *pathname)
    890 {
    891    JAR_Item *it;
    892    JAR_Digest *dig;
    893    ZZLink *link;
    894    ZZList *list = jar->manifest;
    895 
    896    if (ZZ_ListEmpty(list))
    897        return NULL;
    898 
    899    for (link = ZZ_ListHead(list);
    900         !ZZ_ListIterDone(list, link);
    901         link = link->next) {
    902        it = link->thing;
    903        if (it->type == jarTypeSect &&
    904            it->pathname && !PORT_Strcmp(it->pathname, pathname)) {
    905            dig = (JAR_Digest *)it->data;
    906            return dig;
    907        }
    908    }
    909    return NULL;
    910 }
    911 
    912 /*
    913 *  j a r _ b a s e n a m e
    914 *
    915 *  Return the basename -- leading components of path stripped off,
    916 *  extension ripped off -- of a path.
    917 *
    918 */
    919 static char *
    920 jar_basename(const char *path)
    921 {
    922    char *pith, *e, *basename, *ext;
    923 
    924    if (path == NULL)
    925        return PORT_Strdup("");
    926 
    927    pith = PORT_Strdup(path);
    928    basename = pith;
    929    while (1) {
    930        for (e = basename; *e && *e != '/' && *e != '\\'; e++)
    931            /* yip */;
    932        if (*e)
    933            basename = ++e;
    934        else
    935            break;
    936    }
    937 
    938    if ((ext = PORT_Strrchr(basename, '.')) != NULL)
    939        *ext = 0;
    940 
    941    /* We already have the space allocated */
    942    PORT_Strcpy(pith, basename);
    943    return pith;
    944 }
    945 
    946 /*
    947 *  + + + + + + + + + + + + + + +
    948 *
    949 *  CRYPTO ROUTINES FOR JAR
    950 *
    951 *  The following functions are the cryptographic
    952 *  interface to PKCS7 for Jarnatures.
    953 *
    954 *  + + + + + + + + + + + + + + +
    955 *
    956 */
    957 
    958 /*
    959 *  j a r _ c a t c h _ b y t e s
    960 *
    961 *  In the event signatures contain enveloped data, it will show up here.
    962 *  But note that the lib/pkcs7 routines aren't ready for it.
    963 *
    964 */
    965 static void
    966 jar_catch_bytes(void *arg, const char *buf, unsigned long len)
    967 {
    968    /* Actually this should never be called, since there is
    969     presumably no data in the signature itself. */
    970 }
    971 
    972 /*
    973 *  j a r _ v a l i d a t e _ p k c s 7
    974 *
    975 *  Validate (and decode, if necessary) a binary pkcs7
    976 *  signature in DER format.
    977 *
    978 */
    979 static int
    980 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length)
    981 {
    982 
    983    SEC_PKCS7ContentInfo *cinfo = NULL;
    984    SEC_PKCS7DecoderContext *dcx;
    985    PRBool goodSig;
    986    int status = 0;
    987    SECItem detdig;
    988 
    989    PORT_Assert(jar != NULL && signer != NULL);
    990 
    991    if (jar == NULL || signer == NULL)
    992        return JAR_ERR_ORDER;
    993 
    994    signer->valid = JAR_ERR_SIG;
    995 
    996    /* We need a context if we can get one */
    997    dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/,
    998                                NULL /*getpassword*/, jar->mw,
    999                                NULL, NULL, NULL);
   1000    if (dcx == NULL) {
   1001        /* strange pkcs7 failure */
   1002        return JAR_ERR_PK7;
   1003    }
   1004 
   1005    SEC_PKCS7DecoderUpdate(dcx, data, length);
   1006    cinfo = SEC_PKCS7DecoderFinish(dcx);
   1007    if (cinfo == NULL) {
   1008        /* strange pkcs7 failure */
   1009        return JAR_ERR_PK7;
   1010    }
   1011    if (SEC_PKCS7ContentIsEncrypted(cinfo)) {
   1012        /* content was encrypted, fail */
   1013        return JAR_ERR_PK7;
   1014    }
   1015    if (SEC_PKCS7ContentIsSigned(cinfo) == PR_FALSE) {
   1016        /* content was not signed, fail */
   1017        return JAR_ERR_PK7;
   1018    }
   1019 
   1020    PORT_SetError(0);
   1021 
   1022    /* use SHA1 only */
   1023    detdig.len = SHA1_LENGTH;
   1024    detdig.data = signer->digest->sha1;
   1025    goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo,
   1026                                               certUsageObjectSigner,
   1027                                               &detdig, HASH_AlgSHA1,
   1028                                               PR_FALSE);
   1029    jar_gather_signers(jar, signer, cinfo);
   1030    if (goodSig == PR_TRUE) {
   1031        /* signature is valid */
   1032        signer->valid = 0;
   1033    } else {
   1034        status = PORT_GetError();
   1035        PORT_Assert(status < 0);
   1036        if (status >= 0)
   1037            status = JAR_ERR_SIG;
   1038        jar->valid = status;
   1039        signer->valid = status;
   1040    }
   1041    jar->pkcs7 = PR_TRUE;
   1042    signer->pkcs7 = PR_TRUE;
   1043    SEC_PKCS7DestroyContentInfo(cinfo);
   1044    return status;
   1045 }
   1046 
   1047 /*
   1048 *  j a r _ g a t h e r _ s i g n e r s
   1049 *
   1050 *  Add the single signer of this signature to the
   1051 *  certificate linked list.
   1052 *
   1053 */
   1054 static int
   1055 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo)
   1056 {
   1057    int result;
   1058    CERTCertificate *cert;
   1059    CERTCertDBHandle *certdb;
   1060    SEC_PKCS7SignedData *sdp = cinfo->content.signedData;
   1061    SEC_PKCS7SignerInfo **pksigners, *pksigner;
   1062 
   1063    if (sdp == NULL)
   1064        return JAR_ERR_PK7;
   1065 
   1066    pksigners = sdp->signerInfos;
   1067    /* permit exactly one signer */
   1068    if (pksigners == NULL || pksigners[0] == NULL || pksigners[1] != NULL)
   1069        return JAR_ERR_PK7;
   1070 
   1071    pksigner = *pksigners;
   1072    cert = pksigner->cert;
   1073 
   1074    if (cert == NULL)
   1075        return JAR_ERR_PK7;
   1076 
   1077    certdb = JAR_open_database();
   1078    if (certdb == NULL)
   1079        return JAR_ERR_GENERAL;
   1080 
   1081    result = jar_add_cert(jar, signer, jarTypeSign, cert);
   1082    JAR_close_database(certdb);
   1083    return result;
   1084 }
   1085 
   1086 /*
   1087 *  j a r _ o p e n _ d a t a b a s e
   1088 *
   1089 *  Open the certificate database,
   1090 *  for use by JAR functions.
   1091 *
   1092 */
   1093 CERTCertDBHandle *
   1094 JAR_open_database(void)
   1095 {
   1096    return CERT_GetDefaultCertDB();
   1097 }
   1098 
   1099 /*
   1100 *  j a r _ c l o s e _ d a t a b a s e
   1101 *
   1102 *  Close the certificate database.
   1103 *  For use by JAR functions.
   1104 *
   1105 */
   1106 int
   1107 JAR_close_database(CERTCertDBHandle *certdb)
   1108 {
   1109    return 0;
   1110 }
   1111 
   1112 /*
   1113 *  j a r _ s i g n a l
   1114 *
   1115 *  Nonfatal errors come here to callback Java.
   1116 *
   1117 */
   1118 static int
   1119 jar_signal(int status, JAR *jar, const char *metafile, char *pathname)
   1120 {
   1121    char *errstring = JAR_get_error(status);
   1122    if (jar->signal) {
   1123        (*jar->signal)(status, jar, metafile, pathname, errstring);
   1124        return 0;
   1125    }
   1126    return status;
   1127 }
   1128 
   1129 /*
   1130 *  j a r _ a p p e n d
   1131 *
   1132 *  Tack on an element to one of a JAR's linked
   1133 *  lists, with rudimentary error handling.
   1134 *
   1135 */
   1136 int
   1137 jar_append(ZZList *list, int type, char *pathname, void *data, size_t size)
   1138 {
   1139    JAR_Item *it = PORT_ZNew(JAR_Item);
   1140    ZZLink *entity;
   1141 
   1142    if (it == NULL)
   1143        goto loser;
   1144 
   1145    if (pathname) {
   1146        it->pathname = PORT_Strdup(pathname);
   1147        if (it->pathname == NULL)
   1148            goto loser;
   1149    }
   1150 
   1151    it->type = (jarType)type;
   1152    it->data = (unsigned char *)data;
   1153    it->size = size;
   1154    entity = ZZ_NewLink(it);
   1155    if (entity) {
   1156        ZZ_AppendLink(list, entity);
   1157        return 0;
   1158    }
   1159 
   1160 loser:
   1161    if (it) {
   1162        if (it->pathname)
   1163            PORT_Free(it->pathname);
   1164        PORT_Free(it);
   1165    }
   1166    return JAR_ERR_MEMORY;
   1167 }