tor-browser

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

vfyutil.c (19554B)


      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 "vfyserv.h"
      6 #include "secerr.h"
      7 #include "sslerr.h"
      8 #include "nspr.h"
      9 #include "secutil.h"
     10 
     11 extern PRBool dumpChain;
     12 extern void dumpCertChain(CERTCertificate *, SECCertUsage);
     13 
     14 /* Declare SSL cipher suites. */
     15 
     16 int ssl3CipherSuites[] = {
     17    -1,                                /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */
     18    -1,                                /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA     * b */
     19    TLS_RSA_WITH_RC4_128_MD5,          /* c */
     20    TLS_RSA_WITH_3DES_EDE_CBC_SHA,     /* d */
     21    TLS_RSA_WITH_DES_CBC_SHA,          /* e */
     22    -1,                                /* TLS_RSA_EXPORT_WITH_RC4_40_MD5        * f */
     23    -1,                                /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5    * g */
     24    -1,                                /* SSL_FORTEZZA_DMS_WITH_NULL_SHA        * h */
     25    TLS_RSA_WITH_NULL_MD5,             /* i */
     26    -1,                                /* SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA    * j */
     27    -1,                                /* SSL_RSA_FIPS_WITH_DES_CBC_SHA         * k */
     28    -1,                                /* TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA   * l */
     29    -1,                                /* TLS_RSA_EXPORT1024_WITH_RC4_56_SHA    * m */
     30    TLS_RSA_WITH_RC4_128_SHA,          /* n */
     31    TLS_DHE_DSS_WITH_RC4_128_SHA,      /* o */
     32    TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */
     33    TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */
     34    TLS_DHE_RSA_WITH_DES_CBC_SHA,      /* r */
     35    TLS_DHE_DSS_WITH_DES_CBC_SHA,      /* s */
     36    TLS_DHE_DSS_WITH_AES_128_CBC_SHA,  /* t */
     37    TLS_DHE_RSA_WITH_AES_128_CBC_SHA,  /* u */
     38    TLS_RSA_WITH_AES_128_CBC_SHA,      /* v */
     39    TLS_DHE_DSS_WITH_AES_256_CBC_SHA,  /* w */
     40    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,  /* x */
     41    TLS_RSA_WITH_AES_256_CBC_SHA,      /* y */
     42    TLS_RSA_WITH_NULL_SHA,             /* z */
     43    0
     44 };
     45 int numSSL3CipherSuites = PR_ARRAY_SIZE(ssl3CipherSuites);
     46 
     47 /**************************************************************************
     48 **
     49 ** SSL callback routines.
     50 **
     51 **************************************************************************/
     52 
     53 /* Function: char * myPasswd()
     54 *
     55 * Purpose: This function is our custom password handler that is called by
     56 * SSL when retreiving private certs and keys from the database. Returns a
     57 * pointer to a string that with a password for the database. Password pointer
     58 * should point to dynamically allocated memory that will be freed later.
     59 */
     60 char *
     61 myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
     62 {
     63    char *passwd = NULL;
     64 
     65    if ((!retry) && arg) {
     66        passwd = PORT_Strdup((char *)arg);
     67    }
     68    return passwd;
     69 }
     70 
     71 /* Function: SECStatus myAuthCertificate()
     72 *
     73 * Purpose: This function is our custom certificate authentication handler.
     74 *
     75 * Note: This implementation is essentially the same as the default
     76 *       SSL_AuthCertificate().
     77 */
     78 SECStatus
     79 myAuthCertificate(void *arg, PRFileDesc *socket,
     80                  PRBool checksig, PRBool isServer)
     81 {
     82 
     83    SECCertificateUsage certUsage;
     84    CERTCertList *peerChain;
     85    CERTCertificate *cert;
     86    void *pinArg;
     87    char *hostName;
     88    SECStatus secStatus;
     89 
     90    if (!arg || !socket) {
     91        errWarn("myAuthCertificate");
     92        return SECFailure;
     93    }
     94 
     95    /* Define how the cert is being used based upon the isServer flag. */
     96 
     97    certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
     98 
     99    /* Construct, or obtain references to, CERTCertificates for the chain
    100     * presented by the peer. This ensures that the certificates are in
    101     * the certificate DB during CERT_VerifyCertificateNow.
    102     */
    103    peerChain = SSL_PeerCertificateChain(socket);
    104    if (!peerChain) {
    105        return SECFailure;
    106    }
    107 
    108    if (CERT_LIST_EMPTY(peerChain) || !CERT_LIST_HEAD(peerChain)->cert) {
    109        CERT_DestroyCertList(peerChain);
    110        return SECFailure;
    111    }
    112    cert = CERT_LIST_HEAD(peerChain)->cert;
    113 
    114    pinArg = SSL_RevealPinArg(socket);
    115 
    116    if (dumpChain == PR_TRUE) {
    117        dumpCertChain(cert, certUsage);
    118    }
    119 
    120    secStatus = CERT_VerifyCertificateNow((CERTCertDBHandle *)arg,
    121                                          cert,
    122                                          checksig,
    123                                          certUsage,
    124                                          pinArg,
    125                                          NULL);
    126 
    127    /* If this is a server, we're finished. */
    128    if (isServer || secStatus != SECSuccess) {
    129        SECU_printCertProblems(stderr, (CERTCertDBHandle *)arg, cert,
    130                               checksig, certUsage, pinArg, PR_FALSE);
    131        CERT_DestroyCertList(peerChain);
    132        return secStatus;
    133    }
    134 
    135    /* Certificate is OK.  Since this is the client side of an SSL
    136     * connection, we need to verify that the name field in the cert
    137     * matches the desired hostname.  This is our defense against
    138     * man-in-the-middle attacks.
    139     */
    140 
    141    /* SSL_RevealURL returns a hostName, not an URL. */
    142    hostName = SSL_RevealURL(socket);
    143 
    144    if (hostName && hostName[0]) {
    145        secStatus = CERT_VerifyCertName(cert, hostName);
    146    } else {
    147        PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
    148        secStatus = SECFailure;
    149    }
    150 
    151    if (hostName)
    152        PR_Free(hostName);
    153 
    154    CERT_DestroyCertList(peerChain);
    155    return secStatus;
    156 }
    157 
    158 /* Function: SECStatus myBadCertHandler()
    159 *
    160 * Purpose: This callback is called when the incoming certificate is not
    161 * valid. We define a certain set of parameters that still cause the
    162 * certificate to be "valid" for this session, and return SECSuccess to cause
    163 * the server to continue processing the request when any of these conditions
    164 * are met. Otherwise, SECFailure is return and the server rejects the
    165 * request.
    166 */
    167 SECStatus
    168 myBadCertHandler(void *arg, PRFileDesc *socket)
    169 {
    170 
    171    SECStatus secStatus = SECFailure;
    172    PRErrorCode err;
    173 
    174    /* log invalid cert here */
    175 
    176    if (!arg) {
    177        return secStatus;
    178    }
    179 
    180    *(PRErrorCode *)arg = err = PORT_GetError();
    181 
    182    /* If any of the cases in the switch are met, then we will proceed   */
    183    /* with the processing of the request anyway. Otherwise, the default */
    184    /* case will be reached and we will reject the request.              */
    185 
    186    switch (err) {
    187        case SEC_ERROR_INVALID_AVA:
    188        case SEC_ERROR_INVALID_TIME:
    189        case SEC_ERROR_BAD_SIGNATURE:
    190        case SEC_ERROR_EXPIRED_CERTIFICATE:
    191        case SEC_ERROR_UNKNOWN_ISSUER:
    192        case SEC_ERROR_UNTRUSTED_CERT:
    193        case SEC_ERROR_CERT_VALID:
    194        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
    195        case SEC_ERROR_CRL_EXPIRED:
    196        case SEC_ERROR_CRL_BAD_SIGNATURE:
    197        case SEC_ERROR_EXTENSION_VALUE_INVALID:
    198        case SEC_ERROR_CA_CERT_INVALID:
    199        case SEC_ERROR_CERT_USAGES_INVALID:
    200        case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
    201            secStatus = SECSuccess;
    202            break;
    203        default:
    204            secStatus = SECFailure;
    205            break;
    206    }
    207 
    208    fprintf(stderr, "Bad certificate: %d, %s\n", err, SECU_Strerror(err));
    209 
    210    return secStatus;
    211 }
    212 
    213 /* Function: SECStatus ownGetClientAuthData()
    214 *
    215 * Purpose: This callback is used by SSL to pull client certificate
    216 * information upon server request.
    217 */
    218 SECStatus
    219 myGetClientAuthData(void *arg,
    220                    PRFileDesc *socket,
    221                    struct CERTDistNamesStr *caNames,
    222                    struct CERTCertificateStr **pRetCert,
    223                    struct SECKEYPrivateKeyStr **pRetKey)
    224 {
    225 
    226    CERTCertificate *cert;
    227    SECKEYPrivateKey *privKey;
    228    char *chosenNickName = (char *)arg;
    229    void *proto_win = NULL;
    230    SECStatus secStatus = SECFailure;
    231 
    232    proto_win = SSL_RevealPinArg(socket);
    233 
    234    if (chosenNickName) {
    235        cert = PK11_FindCertFromNickname(chosenNickName, proto_win);
    236        if (cert) {
    237            privKey = PK11_FindKeyByAnyCert(cert, proto_win);
    238            if (privKey) {
    239                secStatus = SECSuccess;
    240            } else {
    241                CERT_DestroyCertificate(cert);
    242            }
    243        }
    244    } else { /* no nickname given, automatically find the right cert */
    245        CERTCertNicknames *names;
    246        int i;
    247 
    248        names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
    249                                      SEC_CERT_NICKNAMES_USER, proto_win);
    250 
    251        if (names != NULL) {
    252            for (i = 0; i < names->numnicknames; i++) {
    253 
    254                cert = PK11_FindCertFromNickname(names->nicknames[i],
    255                                                 proto_win);
    256                if (!cert) {
    257                    continue;
    258                }
    259 
    260                /* Only check unexpired certs */
    261                if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE) !=
    262                    secCertTimeValid) {
    263                    CERT_DestroyCertificate(cert);
    264                    continue;
    265                }
    266 
    267                secStatus = NSS_CmpCertChainWCANames(cert, caNames);
    268                if (secStatus == SECSuccess) {
    269                    privKey = PK11_FindKeyByAnyCert(cert, proto_win);
    270                    if (privKey) {
    271                        break;
    272                    }
    273                    secStatus = SECFailure;
    274                }
    275                CERT_DestroyCertificate(cert);
    276            } /* for loop */
    277            CERT_FreeNicknames(names);
    278        }
    279    }
    280 
    281    if (secStatus == SECSuccess) {
    282        *pRetCert = cert;
    283        *pRetKey = privKey;
    284    }
    285 
    286    return secStatus;
    287 }
    288 
    289 /* Function: void myHandshakeCallback()
    290 *
    291 * Purpose: Called by SSL to inform application that the handshake is
    292 * complete. This function is mostly used on the server side of an SSL
    293 * connection, although it is provided for a client as well.
    294 * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake
    295 * is used to initiate a handshake.
    296 *
    297 * A typical scenario would be:
    298 *
    299 * 1. Server accepts an SSL connection from the client without client auth.
    300 * 2. Client sends a request.
    301 * 3. Server determines that to service request it needs to authenticate the
    302 * client and initiates another handshake requesting client auth.
    303 * 4. While handshake is in progress, server can do other work or spin waiting
    304 * for the handshake to complete.
    305 * 5. Server is notified that handshake has been successfully completed by
    306 * the custom handshake callback function and it can service the client's
    307 * request.
    308 *
    309 * Note: This function is not implemented in this sample, as we are using
    310 * blocking sockets.
    311 */
    312 void
    313 myHandshakeCallback(PRFileDesc *socket, void *arg)
    314 {
    315    fprintf(stderr, "Handshake Complete: SERVER CONFIGURED CORRECTLY\n");
    316 }
    317 
    318 /**************************************************************************
    319 **
    320 ** Routines for disabling SSL ciphers.
    321 **
    322 **************************************************************************/
    323 
    324 void
    325 disableAllSSLCiphers(void)
    326 {
    327    const PRUint16 *allSuites = SSL_ImplementedCiphers;
    328    int i = SSL_NumImplementedCiphers;
    329    SECStatus rv;
    330 
    331    /* disable all the SSL3 cipher suites */
    332    while (--i >= 0) {
    333        PRUint16 suite = allSuites[i];
    334        rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
    335        if (rv != SECSuccess) {
    336            fprintf(stderr,
    337                    "SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
    338                    suite, i);
    339            errWarn("SSL_CipherPrefSetDefault");
    340            exit(2);
    341        }
    342    }
    343 }
    344 
    345 /**************************************************************************
    346 **
    347 ** Error and information routines.
    348 **
    349 **************************************************************************/
    350 
    351 void
    352 errWarn(char *function)
    353 {
    354    PRErrorCode errorNumber = PR_GetError();
    355    const char *errorString = SECU_Strerror(errorNumber);
    356 
    357    fprintf(stderr, "Error in function %s: %d\n - %s\n",
    358            function, errorNumber, errorString);
    359 }
    360 
    361 void
    362 exitErr(char *function)
    363 {
    364    errWarn(function);
    365    /* Exit gracefully. */
    366    /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/
    367    (void)NSS_Shutdown();
    368    PR_Cleanup();
    369    exit(1);
    370 }
    371 
    372 void
    373 printSecurityInfo(FILE *outfile, PRFileDesc *fd)
    374 {
    375    char *cp; /* bulk cipher name */
    376    char *ip; /* cert issuer DN */
    377    char *sp; /* cert subject DN */
    378    int op;   /* High, Low, Off */
    379    int kp0;  /* total key bits */
    380    int kp1;  /* secret key bits */
    381    int result;
    382    SSL3Statistics *ssl3stats = SSL_GetStatistics();
    383 
    384    if (!outfile) {
    385        outfile = stdout;
    386    }
    387 
    388    result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp);
    389    if (result != SECSuccess)
    390        return;
    391    fprintf(outfile,
    392            "   bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
    393            "   subject DN:\n %s\n"
    394            "   issuer  DN:\n %s\n",
    395            cp, kp1, kp0, op, sp, ip);
    396    PR_Free(cp);
    397    PR_Free(ip);
    398    PR_Free(sp);
    399 
    400    fprintf(outfile,
    401            "   %ld cache hits; %ld cache misses, %ld cache not reusable\n",
    402            ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
    403            ssl3stats->hch_sid_cache_not_ok);
    404 }
    405 
    406 /**************************************************************************
    407 ** Begin thread management routines and data.
    408 **************************************************************************/
    409 
    410 void
    411 thread_wrapper(void *arg)
    412 {
    413    GlobalThreadMgr *threadMGR = (GlobalThreadMgr *)arg;
    414    perThread *slot = &threadMGR->threads[threadMGR->index];
    415 
    416    /* wait for parent to finish launching us before proceeding. */
    417    PR_Lock(threadMGR->threadLock);
    418    PR_Unlock(threadMGR->threadLock);
    419 
    420    slot->rv = (*slot->startFunc)(slot->a, slot->b);
    421 
    422    PR_Lock(threadMGR->threadLock);
    423    slot->running = rs_zombie;
    424 
    425    /* notify the thread exit handler. */
    426    PR_NotifyCondVar(threadMGR->threadEndQ);
    427 
    428    PR_Unlock(threadMGR->threadLock);
    429 }
    430 
    431 SECStatus
    432 launch_thread(GlobalThreadMgr *threadMGR,
    433              startFn *startFunc,
    434              void *a,
    435              int b)
    436 {
    437    perThread *slot;
    438    int i;
    439 
    440    if (!threadMGR->threadStartQ) {
    441        threadMGR->threadLock = PR_NewLock();
    442        threadMGR->threadStartQ = PR_NewCondVar(threadMGR->threadLock);
    443        threadMGR->threadEndQ = PR_NewCondVar(threadMGR->threadLock);
    444    }
    445    PR_Lock(threadMGR->threadLock);
    446    while (threadMGR->numRunning >= MAX_THREADS) {
    447        PR_WaitCondVar(threadMGR->threadStartQ, PR_INTERVAL_NO_TIMEOUT);
    448    }
    449    for (i = 0; i < threadMGR->numUsed; ++i) {
    450        slot = &threadMGR->threads[i];
    451        if (slot->running == rs_idle)
    452            break;
    453    }
    454    if (i >= threadMGR->numUsed) {
    455        if (i >= MAX_THREADS) {
    456            /* something's really wrong here. */
    457            PORT_Assert(i < MAX_THREADS);
    458            PR_Unlock(threadMGR->threadLock);
    459            return SECFailure;
    460        }
    461        ++(threadMGR->numUsed);
    462        PORT_Assert(threadMGR->numUsed == i + 1);
    463        slot = &threadMGR->threads[i];
    464    }
    465 
    466    slot->a = a;
    467    slot->b = b;
    468    slot->startFunc = startFunc;
    469 
    470    threadMGR->index = i;
    471 
    472    slot->prThread = PR_CreateThread(PR_USER_THREAD,
    473                                     thread_wrapper, threadMGR,
    474                                     PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
    475                                     PR_JOINABLE_THREAD, 0);
    476 
    477    if (slot->prThread == NULL) {
    478        PR_Unlock(threadMGR->threadLock);
    479        printf("Failed to launch thread!\n");
    480        return SECFailure;
    481    }
    482 
    483    slot->inUse = 1;
    484    slot->running = 1;
    485    ++(threadMGR->numRunning);
    486    PR_Unlock(threadMGR->threadLock);
    487 
    488    return SECSuccess;
    489 }
    490 
    491 SECStatus
    492 reap_threads(GlobalThreadMgr *threadMGR)
    493 {
    494    perThread *slot;
    495    int i;
    496 
    497    if (!threadMGR->threadLock)
    498        return SECSuccess;
    499    PR_Lock(threadMGR->threadLock);
    500    while (threadMGR->numRunning > 0) {
    501        PR_WaitCondVar(threadMGR->threadEndQ, PR_INTERVAL_NO_TIMEOUT);
    502        for (i = 0; i < threadMGR->numUsed; ++i) {
    503            slot = &threadMGR->threads[i];
    504            if (slot->running == rs_zombie) {
    505                /* Handle cleanup of thread here. */
    506 
    507                /* Now make sure the thread has ended OK. */
    508                PR_JoinThread(slot->prThread);
    509                slot->running = rs_idle;
    510                --threadMGR->numRunning;
    511 
    512                /* notify the thread launcher. */
    513                PR_NotifyCondVar(threadMGR->threadStartQ);
    514            }
    515        }
    516    }
    517 
    518    /* Safety Sam sez: make sure count is right. */
    519    for (i = 0; i < threadMGR->numUsed; ++i) {
    520        slot = &threadMGR->threads[i];
    521        if (slot->running != rs_idle) {
    522            fprintf(stderr, "Thread in slot %d is in state %d!\n",
    523                    i, slot->running);
    524        }
    525    }
    526    PR_Unlock(threadMGR->threadLock);
    527    return SECSuccess;
    528 }
    529 
    530 void
    531 destroy_thread_data(GlobalThreadMgr *threadMGR)
    532 {
    533    PORT_Memset(threadMGR->threads, 0, sizeof(threadMGR->threads));
    534 
    535    if (threadMGR->threadEndQ) {
    536        PR_DestroyCondVar(threadMGR->threadEndQ);
    537        threadMGR->threadEndQ = NULL;
    538    }
    539    if (threadMGR->threadStartQ) {
    540        PR_DestroyCondVar(threadMGR->threadStartQ);
    541        threadMGR->threadStartQ = NULL;
    542    }
    543    if (threadMGR->threadLock) {
    544        PR_DestroyLock(threadMGR->threadLock);
    545        threadMGR->threadLock = NULL;
    546    }
    547 }
    548 
    549 /**************************************************************************
    550 ** End	 thread management routines.
    551 **************************************************************************/
    552 
    553 void
    554 lockedVars_Init(lockedVars *lv)
    555 {
    556    lv->count = 0;
    557    lv->waiters = 0;
    558    lv->lock = PR_NewLock();
    559    lv->condVar = PR_NewCondVar(lv->lock);
    560 }
    561 
    562 void
    563 lockedVars_Destroy(lockedVars *lv)
    564 {
    565    PR_DestroyCondVar(lv->condVar);
    566    lv->condVar = NULL;
    567 
    568    PR_DestroyLock(lv->lock);
    569    lv->lock = NULL;
    570 }
    571 
    572 void
    573 lockedVars_WaitForDone(lockedVars *lv)
    574 {
    575    PR_Lock(lv->lock);
    576    while (lv->count > 0) {
    577        PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
    578    }
    579    PR_Unlock(lv->lock);
    580 }
    581 
    582 int /* returns count */
    583 lockedVars_AddToCount(lockedVars *lv, int addend)
    584 {
    585    int rv;
    586 
    587    PR_Lock(lv->lock);
    588    rv = lv->count += addend;
    589    if (rv <= 0) {
    590        PR_NotifyCondVar(lv->condVar);
    591    }
    592    PR_Unlock(lv->lock);
    593    return rv;
    594 }
    595 
    596 /*
    597 * Dump cert chain in to cert.* files. This function is will
    598 * create collisions while dumping cert chains if called from
    599 * multiple treads. But it should not be a problem since we
    600 * consider vfyserv to be single threaded(see bug 353477).
    601 */
    602 
    603 void
    604 dumpCertChain(CERTCertificate *cert, SECCertUsage usage)
    605 {
    606    CERTCertificateList *certList;
    607    unsigned int count = 0;
    608 
    609    certList = CERT_CertChainFromCert(cert, usage, PR_TRUE);
    610    if (certList == NULL) {
    611        errWarn("CERT_CertChainFromCert");
    612        return;
    613    }
    614 
    615    for (count = 0; count < (unsigned int)certList->len; count++) {
    616        char certFileName[16];
    617        PRFileDesc *cfd;
    618 
    619        PR_snprintf(certFileName, sizeof certFileName, "cert.%03d",
    620                    count);
    621        cfd = PR_Open(certFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
    622                      0664);
    623        if (!cfd) {
    624            PR_fprintf(PR_STDOUT,
    625                       "Error: couldn't save cert der in file '%s'\n",
    626                       certFileName);
    627        } else {
    628            PR_Write(cfd, certList->certs[count].data, certList->certs[count].len);
    629            PR_Close(cfd);
    630            PR_fprintf(PR_STDOUT, "Cert file %s was created.\n", certFileName);
    631        }
    632    }
    633    CERT_DestroyCertificateList(certList);
    634 }