tor-browser

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

shvfy.c (17891B)


      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 #ifdef FREEBL_NO_DEPEND
      6 #include "stubs.h"
      7 #endif
      8 
      9 #include "shsign.h"
     10 #include "prlink.h"
     11 #include "prio.h"
     12 #include "blapi.h"
     13 #include "seccomon.h"
     14 #include "secerr.h"
     15 #include "stdio.h"
     16 #include "prmem.h"
     17 #include "hasht.h"
     18 #include "pqg.h"
     19 #include "blapii.h"
     20 #include "secitem.h"
     21 #include "pkcs11t.h"
     22 
     23 #ifndef NSS_FIPS_DISABLED
     24 
     25 /*
     26 * Most modern version of Linux support a speed optimization scheme where an
     27 * application called prelink modifies programs and shared libraries to quickly
     28 * load if they fit into an already designed address space. In short, prelink
     29 * scans the list of programs and libraries on your system, assigns them a
     30 * predefined space in the the address space, then provides the fixups to the
     31 * library.
     32 
     33 * The modification of the shared library is correctly detected by the freebl
     34 * FIPS checksum scheme where we check a signed hash of the library against the
     35 * library itself.
     36 *
     37 * The prelink command itself can reverse the process of modification and
     38 * output the prestine shared library as it was before prelink made it's
     39 * changes. If FREEBL_USE_PRELINK is set Freebl uses prelink to output the
     40 * original copy of the shared library before prelink modified it.
     41 */
     42 #ifdef FREEBL_USE_PRELINK
     43 #ifndef FREELB_PRELINK_COMMAND
     44 #define FREEBL_PRELINK_COMMAND "/usr/sbin/prelink -u -o -"
     45 #endif
     46 #include "private/pprio.h"
     47 
     48 #include <stdlib.h>
     49 #include <unistd.h>
     50 #include <fcntl.h>
     51 #include <sys/wait.h>
     52 #include <sys/stat.h>
     53 
     54 /*
     55 * This function returns an NSPR PRFileDesc * which the caller can read to
     56 * obtain the prestine value of the shared library, before any OS related
     57 * changes to it (usually address fixups).
     58 *
     59 * If prelink is installed, this
     60 * file descriptor is a pipe connecting the output of
     61 *            /usr/sbin/prelink -u -o - {Library}
     62 * and *pid returns the process id of the prelink child.
     63 *
     64 * If prelink is not installed, it returns a normal readonly handle to the
     65 * library itself and *pid is set to '0'.
     66 */
     67 PRFileDesc *
     68 bl_OpenUnPrelink(const char *shName, int *pid)
     69 {
     70    char *command = strdup(FREEBL_PRELINK_COMMAND);
     71    char *argString = NULL;
     72    char **argv = NULL;
     73    char *shNameArg = NULL;
     74    char *cp;
     75    pid_t child;
     76    int argc = 0, argNext = 0;
     77    struct stat statBuf;
     78    int pipefd[2] = { -1, -1 };
     79    int ret;
     80 
     81    *pid = 0;
     82 
     83    /* make sure the prelink command exists first. If not, fall back to
     84     * just reading the file */
     85    for (cp = command; *cp; cp++) {
     86        if (*cp == ' ') {
     87            *cp++ = 0;
     88            argString = cp;
     89            break;
     90        }
     91    }
     92    memset(&statBuf, 0, sizeof(statBuf));
     93    /* stat the file, follow the link */
     94    ret = stat(command, &statBuf);
     95    if (ret < 0) {
     96        free(command);
     97        return PR_Open(shName, PR_RDONLY, 0);
     98    }
     99    /* file exits, make sure it's an executable */
    100    if (!S_ISREG(statBuf.st_mode) ||
    101        ((statBuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)) {
    102        free(command);
    103        return PR_Open(shName, PR_RDONLY, 0);
    104    }
    105 
    106    /* OK, the prelink command exists and looks correct, use it */
    107    /* build the arglist while we can still malloc */
    108    /* count the args if any */
    109    if (argString && *argString) {
    110        /* argString may have leading spaces, strip them off*/
    111        for (cp = argString; *cp && *cp == ' '; cp++)
    112            ;
    113        argString = cp;
    114        if (*cp) {
    115            /* there is at least one arg.. */
    116            argc = 1;
    117        }
    118 
    119        /* count the rest: Note there is no provision for escaped
    120         * spaces here */
    121        for (cp = argString; *cp; cp++) {
    122            if (*cp == ' ') {
    123                while (*cp && *cp == ' ')
    124                    cp++;
    125                if (*cp)
    126                    argc++;
    127            }
    128        }
    129    }
    130 
    131    /* add the additional args: argv[0] (command), shName, NULL*/
    132    argc += 3;
    133    argv = PORT_NewArray(char *, argc);
    134    if (argv == NULL) {
    135        goto loser;
    136    }
    137 
    138    /* fill in the arglist */
    139    argv[argNext++] = command;
    140    if (argString && *argString) {
    141        argv[argNext++] = argString;
    142        for (cp = argString; *cp; cp++) {
    143            if (*cp == ' ') {
    144                *cp++ = 0;
    145                while (*cp && *cp == ' ')
    146                    cp++;
    147                if (*cp)
    148                    argv[argNext++] = cp;
    149            }
    150        }
    151    }
    152    /* exec doesn't advertise taking const char **argv, do the paranoid
    153     * copy */
    154    shNameArg = strdup(shName);
    155    if (shNameArg == NULL) {
    156        goto loser;
    157    }
    158    argv[argNext++] = shNameArg;
    159    argv[argNext++] = 0;
    160 
    161    ret = pipe(pipefd);
    162    if (ret < 0) {
    163        goto loser;
    164    }
    165 
    166    /* use vfork() so we don't trigger the pthread_at_fork() handlers */
    167    child = vfork();
    168    if (child < 0)
    169        goto loser;
    170    if (child == 0) {
    171        /* set up the file descriptors */
    172        /* if we need to support BSD, this will need to be an open of
    173         * /dev/null and dup2(nullFD, 0)*/
    174        close(0);
    175        /* associate pipefd[1] with stdout */
    176        if (pipefd[1] != 1)
    177            dup2(pipefd[1], 1);
    178        close(2);
    179        close(pipefd[0]);
    180        /* should probably close the other file descriptors? */
    181 
    182        execv(command, argv);
    183        /* avoid at_exit() handlers */
    184        _exit(1); /* shouldn't reach here except on an error */
    185    }
    186    close(pipefd[1]);
    187    pipefd[1] = -1;
    188 
    189    /* this is safe because either vfork() as full fork() semantics, and thus
    190     * already has it's own address space, or because vfork() has paused
    191     * the parent util the exec or exit */
    192    free(command);
    193    free(shNameArg);
    194    PORT_Free(argv);
    195 
    196    *pid = child;
    197 
    198    return PR_ImportPipe(pipefd[0]);
    199 
    200 loser:
    201    if (pipefd[0] != -1) {
    202        close(pipefd[0]);
    203    }
    204    if (pipefd[1] != -1) {
    205        close(pipefd[1]);
    206    }
    207    free(command);
    208    free(shNameArg);
    209    PORT_Free(argv);
    210 
    211    return NULL;
    212 }
    213 
    214 /*
    215 * bl_CloseUnPrelink -
    216 *
    217 * This closes the file descripter and reaps and children openned and crated by
    218 * b;_OpenUnprelink. It's primary difference between it and just close is
    219 * that it calls wait on the pid if one is supplied, preventing zombie children
    220 * from hanging around.
    221 */
    222 void
    223 bl_CloseUnPrelink(PRFileDesc *file, int pid)
    224 {
    225    /* close the file descriptor */
    226    PR_Close(file);
    227    /* reap the child */
    228    if (pid) {
    229        waitpid(pid, NULL, 0);
    230    }
    231 }
    232 #endif
    233 
    234 /* #define DEBUG_SHVERIFY 1 */
    235 
    236 static char *
    237 mkCheckFileName(const char *libName)
    238 {
    239    int ln_len = PORT_Strlen(libName);
    240    int index = ln_len + 1 - sizeof("." SHLIB_SUFFIX);
    241    char *output = PORT_Alloc(ln_len + sizeof(SGN_SUFFIX));
    242    if (!output) {
    243        PORT_SetError(SEC_ERROR_NO_MEMORY);
    244        return NULL;
    245    }
    246 
    247    if ((index > 0) &&
    248        (PORT_Strncmp(&libName[index],
    249                      "." SHLIB_SUFFIX, sizeof("." SHLIB_SUFFIX)) == 0)) {
    250        ln_len = index;
    251    }
    252    PORT_Memcpy(output, libName, ln_len);
    253    PORT_Memcpy(&output[ln_len], SGN_SUFFIX, sizeof(SGN_SUFFIX));
    254    return output;
    255 }
    256 
    257 static int
    258 decodeInt(unsigned char *buf)
    259 {
    260    return (buf[3]) | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24);
    261 }
    262 
    263 static SECStatus
    264 readItem(PRFileDesc *fd, SECItem *item)
    265 {
    266    unsigned char buf[4];
    267    int bytesRead;
    268 
    269    bytesRead = PR_Read(fd, buf, 4);
    270    if (bytesRead != 4) {
    271        return SECFailure;
    272    }
    273    item->len = decodeInt(buf);
    274 
    275    item->data = PORT_Alloc(item->len);
    276    if (item->data == NULL) {
    277        item->len = 0;
    278        return SECFailure;
    279    }
    280    bytesRead = PR_Read(fd, item->data, item->len);
    281    if (bytesRead != item->len) {
    282        PORT_Free(item->data);
    283        item->data = NULL;
    284        item->len = 0;
    285        return SECFailure;
    286    }
    287    return SECSuccess;
    288 }
    289 
    290 static PRBool blapi_SHVerifyFile(const char *shName, PRBool self, PRBool rerun);
    291 
    292 static PRBool
    293 blapi_SHVerify(const char *name, PRFuncPtr addr, PRBool self, PRBool rerun)
    294 {
    295    PRBool result = PR_FALSE; /* if anything goes wrong,
    296                               * the signature does not verify */
    297    /* find our shared library name */
    298    char *shName = PR_GetLibraryFilePathname(name, addr);
    299    if (!shName) {
    300        goto loser;
    301    }
    302    result = blapi_SHVerifyFile(shName, self, rerun);
    303 
    304 loser:
    305    if (shName != NULL) {
    306        PR_Free(shName);
    307    }
    308 
    309    return result;
    310 }
    311 
    312 PRBool
    313 BLAPI_SHVerify(const char *name, PRFuncPtr addr)
    314 {
    315    PRBool rerun = PR_FALSE;
    316    if (name && *name == BLAPI_FIPS_RERUN_FLAG) {
    317        name++;
    318        rerun = PR_TRUE;
    319    }
    320    return blapi_SHVerify(name, addr, PR_FALSE, rerun);
    321 }
    322 
    323 PRBool
    324 BLAPI_SHVerifyFile(const char *shName)
    325 {
    326    PRBool rerun = PR_FALSE;
    327    if (shName && *shName == BLAPI_FIPS_RERUN_FLAG) {
    328        shName++;
    329        rerun = PR_TRUE;
    330    }
    331    return blapi_SHVerifyFile(shName, PR_FALSE, rerun);
    332 }
    333 
    334 #ifndef NSS_STRICT_INTEGRITY
    335 /* This allows checks with old shlibsign .chk files. If NSS_STRICT_INTEGRITY
    336 * is set, we don't accept DSA */
    337 static PRBool
    338 blapi_SHVerifyDSACheck(PRFileDesc *shFD, const SECHashObject *hashObj,
    339                       DSAPublicKey *key, const SECItem *signature)
    340 {
    341    void *hashcx = NULL;
    342    SECItem hash;
    343    int bytesRead;
    344    unsigned char hashBuf[HASH_LENGTH_MAX];
    345    unsigned char buf[4096];
    346    SECStatus rv;
    347 
    348    hash.type = siBuffer;
    349    hash.data = hashBuf;
    350    hash.len = sizeof(hashBuf);
    351 
    352    /* hash our library file */
    353    hashcx = hashObj->create();
    354    if (hashcx == NULL) {
    355        return PR_FALSE;
    356    }
    357    hashObj->begin(hashcx);
    358 
    359    while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) {
    360        hashObj->update(hashcx, buf, bytesRead);
    361    }
    362    hashObj->end(hashcx, hash.data, &hash.len, hash.len);
    363    hashObj->destroy(hashcx, PR_TRUE);
    364 
    365    /* verify the hash against the check file */
    366    rv = DSA_VerifyDigest(key, signature, &hash);
    367    PORT_SafeZero(hashBuf, sizeof hashBuf);
    368    return (rv == SECSuccess) ? PR_TRUE : PR_FALSE;
    369 }
    370 #endif
    371 
    372 #ifdef NSS_STRICT_INTEGRITY
    373 /* don't allow MD2, MD5, SHA1 or SHA224 as your integrity hash */
    374 static PRBool
    375 blapi_HashAllowed(SECHashObject *hashObj)
    376 {
    377    switch (hashObj->type) {
    378        case HASH_AlgSHA256:
    379        case HASH_AlgSHA384:
    380        case HASH_AlgSHA512:
    381            return PR_TRUE;
    382        default:
    383            break;
    384    }
    385    return PR_FALSE;
    386 }
    387 #endif
    388 
    389 static PRBool
    390 blapi_SHVerifyHMACCheck(PRFileDesc *shFD, const SECHashObject *hashObj,
    391                        const SECItem *key, const SECItem *signature)
    392 {
    393    HMACContext *hmaccx = NULL;
    394    SECItem hash;
    395    int bytesRead;
    396    unsigned char hashBuf[HASH_LENGTH_MAX];
    397    unsigned char buf[4096];
    398    SECStatus rv;
    399    PRBool result = PR_FALSE;
    400 
    401 #ifdef NSS_STRICT_INTEGRITY
    402    if (!blapi_HashAllowed(hashObj)) {
    403        return PR_FALSE;
    404    }
    405 #endif
    406 
    407    hash.type = siBuffer;
    408    hash.data = hashBuf;
    409    hash.len = hashObj->length;
    410 
    411    /* create an hmac for the library file */
    412    hmaccx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE);
    413    if (hmaccx == NULL) {
    414        return PR_FALSE;
    415    }
    416    HMAC_Begin(hmaccx);
    417 
    418    while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) {
    419        HMAC_Update(hmaccx, buf, bytesRead);
    420    }
    421    rv = HMAC_Finish(hmaccx, hash.data, &hash.len, hash.len);
    422 
    423    HMAC_Destroy(hmaccx, PR_TRUE);
    424 
    425    /* verify the hmac against the check file */
    426    if (rv == SECSuccess) {
    427        result = SECITEM_ItemsAreEqual(signature, &hash);
    428    }
    429    PORT_SafeZero(hashBuf, sizeof hashBuf);
    430    return result;
    431 }
    432 
    433 static PRBool
    434 blapi_SHVerifyFile(const char *shName, PRBool self, PRBool rerun)
    435 {
    436    char *checkName = NULL;
    437    PRFileDesc *checkFD = NULL;
    438    PRFileDesc *shFD = NULL;
    439    const SECHashObject *hashObj = NULL;
    440    SECItem signature = { 0, NULL, 0 };
    441    int bytesRead, offset, type;
    442    SECStatus rv;
    443    SECItem hmacKey = { 0, NULL, 0 };
    444 #ifdef FREEBL_USE_PRELINK
    445    int pid = 0;
    446 #endif
    447    PRBool result = PR_FALSE; /* if anything goes wrong,
    448                               * the signature does not verify */
    449    NSSSignChkHeader header;
    450 #ifndef NSS_STRICT_INTEGRITY
    451    DSAPublicKey key;
    452 
    453    PORT_SafeZero(&key, sizeof(key));
    454 #endif
    455 
    456    /* If our integrity check was never ran or failed, fail any other
    457     * integrity checks to prevent any token going into FIPS mode. */
    458    if (!self && (BL_FIPSEntryOK(PR_FALSE, rerun) != SECSuccess)) {
    459        return PR_FALSE;
    460    }
    461 
    462    if (!shName) {
    463        goto loser;
    464    }
    465 
    466    /* figure out the name of our check file */
    467    checkName = mkCheckFileName(shName);
    468    if (!checkName) {
    469        goto loser;
    470    }
    471 
    472    /* open the check File */
    473    checkFD = PR_Open(checkName, PR_RDONLY, 0);
    474    if (checkFD == NULL) {
    475 #ifdef DEBUG_SHVERIFY
    476        fprintf(stderr, "Failed to open the check file %s: (%d, %d)\n",
    477                checkName, (int)PR_GetError(), (int)PR_GetOSError());
    478 #endif /* DEBUG_SHVERIFY */
    479        goto loser;
    480    }
    481 
    482    /* read and Verify the headerthe header */
    483    bytesRead = PR_Read(checkFD, &header, sizeof(header));
    484    if (bytesRead != sizeof(header)) {
    485        goto loser;
    486    }
    487    if ((header.magic1 != NSS_SIGN_CHK_MAGIC1) ||
    488        (header.magic2 != NSS_SIGN_CHK_MAGIC2)) {
    489        goto loser;
    490    }
    491    /* we've bumped the version number so that newly signed .check
    492     * files will fail nicely on old version of nss */
    493    if (header.majorVersion > NSS_SIGN_CHK_MAJOR_VERSION) {
    494        goto loser;
    495    }
    496    if (header.minorVersion < NSS_SIGN_CHK_MINOR_VERSION) {
    497        goto loser;
    498    }
    499    type = decodeInt(header.type);
    500 
    501    /* seek past any future header extensions */
    502    offset = decodeInt(header.offset);
    503    if (PR_Seek(checkFD, offset, PR_SEEK_SET) < 0) {
    504        goto loser;
    505    }
    506 
    507    switch (type) {
    508        case CKK_DSA:
    509 #ifdef NSS_STRICT_INTEGRITY
    510            goto loser;
    511 #else
    512            /* accept old dsa check files if NSS_STRICT_INTEGRITY is not set*/
    513            /* read the key */
    514            rv = readItem(checkFD, &key.params.prime);
    515            if (rv != SECSuccess) {
    516                goto loser;
    517            }
    518            rv = readItem(checkFD, &key.params.subPrime);
    519            if (rv != SECSuccess) {
    520                goto loser;
    521            }
    522            rv = readItem(checkFD, &key.params.base);
    523            if (rv != SECSuccess) {
    524                goto loser;
    525            }
    526            rv = readItem(checkFD, &key.publicValue);
    527            if (rv != SECSuccess) {
    528                goto loser;
    529            }
    530            /* read the signature */
    531            rv = readItem(checkFD, &signature);
    532            if (rv != SECSuccess) {
    533                goto loser;
    534            }
    535            hashObj = HASH_GetRawHashObject(PQG_GetHashType(&key.params));
    536            break;
    537 #endif
    538        default:
    539            if ((type & NSS_SIGN_CHK_TYPE_FLAGS) != NSS_SIGN_CHK_FLAG_HMAC) {
    540                goto loser;
    541            }
    542            /* read the HMAC Key */
    543            rv = readItem(checkFD, &hmacKey);
    544            if (rv != SECSuccess) {
    545                goto loser;
    546            }
    547            /* read the siganture */
    548            rv = readItem(checkFD, &signature);
    549            if (rv != SECSuccess) {
    550                goto loser;
    551            }
    552            hashObj = HASH_GetRawHashObject(type & ~NSS_SIGN_CHK_TYPE_FLAGS);
    553    }
    554 
    555    /* done with the check file */
    556    PR_Close(checkFD);
    557    checkFD = NULL;
    558 
    559    if (hashObj == NULL) {
    560        goto loser;
    561    }
    562 
    563 /* open our library file */
    564 #ifdef FREEBL_USE_PRELINK
    565    shFD = bl_OpenUnPrelink(shName, &pid);
    566 #else
    567    shFD = PR_Open(shName, PR_RDONLY, 0);
    568 #endif
    569    if (shFD == NULL) {
    570 #ifdef DEBUG_SHVERIFY
    571        fprintf(stderr, "Failed to open the library file %s: (%d, %d)\n",
    572                shName, (int)PR_GetError(), (int)PR_GetOSError());
    573 #endif /* DEBUG_SHVERIFY */
    574        goto loser;
    575    }
    576 
    577    switch (type) {
    578        case CKK_DSA:
    579 #ifndef NSS_STRICT_INTEGRITY
    580            result = blapi_SHVerifyDSACheck(shFD, hashObj, &key, &signature);
    581 #endif
    582            break;
    583        default:
    584            if ((type & NSS_SIGN_CHK_TYPE_FLAGS) != NSS_SIGN_CHK_FLAG_HMAC) {
    585                break;
    586            }
    587            result = blapi_SHVerifyHMACCheck(shFD, hashObj, &hmacKey, &signature);
    588            break;
    589    }
    590 
    591 #ifdef FREEBL_USE_PRELINK
    592    bl_CloseUnPrelink(shFD, pid);
    593 #else
    594    PR_Close(shFD);
    595 #endif
    596    shFD = NULL;
    597 
    598 loser:
    599    PORT_SafeZero(&header, sizeof header);
    600    if (checkName != NULL) {
    601        PORT_Free(checkName);
    602    }
    603    if (checkFD != NULL) {
    604        PR_Close(checkFD);
    605    }
    606    if (shFD != NULL) {
    607        PR_Close(shFD);
    608    }
    609    if (hmacKey.data != NULL) {
    610        SECITEM_ZfreeItem(&hmacKey, PR_FALSE);
    611    }
    612    if (signature.data != NULL) {
    613        SECITEM_ZfreeItem(&signature, PR_FALSE);
    614    }
    615 #ifndef NSS_STRICT_INTEGRITY
    616    if (key.params.prime.data != NULL) {
    617        SECITEM_ZfreeItem(&key.params.prime, PR_FALSE);
    618    }
    619    if (key.params.subPrime.data != NULL) {
    620        SECITEM_ZfreeItem(&key.params.subPrime, PR_FALSE);
    621    }
    622    if (key.params.base.data != NULL) {
    623        SECITEM_ZfreeItem(&key.params.base, PR_FALSE);
    624    }
    625    if (key.publicValue.data != NULL) {
    626        SECITEM_ZfreeItem(&key.publicValue, PR_FALSE);
    627    }
    628 #endif
    629    return result;
    630 }
    631 
    632 PRBool
    633 BLAPI_VerifySelf(const char *name)
    634 {
    635    if (name == NULL) {
    636        /*
    637         * If name is NULL, freebl is statically linked into softoken.
    638         * softoken will call BLAPI_SHVerify next to verify itself.
    639         */
    640        return PR_TRUE;
    641    }
    642    return blapi_SHVerify(name, (PRFuncPtr)decodeInt, PR_TRUE, PR_FALSE);
    643 }
    644 
    645 #else /* NSS_FIPS_DISABLED */
    646 
    647 PRBool
    648 BLAPI_SHVerifyFile(const char *shName)
    649 {
    650    return PR_FALSE;
    651 }
    652 PRBool
    653 BLAPI_SHVerify(const char *name, PRFuncPtr addr)
    654 {
    655    return PR_FALSE;
    656 }
    657 PRBool
    658 BLAPI_VerifySelf(const char *name)
    659 {
    660    return PR_FALSE;
    661 }
    662 
    663 #endif /* NSS_FIPS_DISABLED */