tor-browser

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

vfyserv.c (16751B)


      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 *  SSL client program that tests  a server for proper operation of SSL2,   *
      7 *  SSL3, and TLS. Test propder certificate installation.                   *
      8 *                                                                          *
      9 *  This code was modified from the SSLSample code also kept in the NSS     *
     10 *  directory.                                                              *
     11 ****************************************************************************/
     12 
     13 #include <stdio.h>
     14 #include <string.h>
     15 
     16 #if defined(XP_UNIX)
     17 #include <unistd.h>
     18 #endif
     19 
     20 #include "prerror.h"
     21 
     22 #include "pk11func.h"
     23 #include "secmod.h"
     24 #include "secitem.h"
     25 
     26 #include <stdlib.h>
     27 #include <errno.h>
     28 #include <fcntl.h>
     29 #include <stdarg.h>
     30 
     31 #include "nspr.h"
     32 #include "plgetopt.h"
     33 #include "prio.h"
     34 #include "prnetdb.h"
     35 #include "nss.h"
     36 #include "secutil.h"
     37 #include "ocsp.h"
     38 
     39 #include "vfyserv.h"
     40 
     41 #define RD_BUF_SIZE (60 * 1024)
     42 
     43 extern int ssl3CipherSuites[];
     44 extern int numSSL3CipherSuites;
     45 
     46 GlobalThreadMgr threadMGR;
     47 char *certNickname = NULL;
     48 char *hostName = NULL;
     49 secuPWData pwdata = { PW_NONE, 0 };
     50 unsigned short port = 0;
     51 PRBool dumpChain;
     52 
     53 static void
     54 Usage(const char *progName)
     55 {
     56    PRFileDesc *pr_stderr;
     57 
     58    pr_stderr = PR_STDERR;
     59 
     60    PR_fprintf(pr_stderr, "Usage:\n"
     61                          "   %s  [-c ] [-o] [-p port] [-d dbdir] [-w password] [-f pwfile]\n"
     62                          "   \t\t[-C cipher(s)]  [-l <url> -t <nickname> ] hostname",
     63               progName);
     64    PR_fprintf(pr_stderr, "\nWhere:\n");
     65    PR_fprintf(pr_stderr,
     66               "  %-13s dump server cert chain into files\n",
     67               "-c");
     68    PR_fprintf(pr_stderr,
     69               "  %-13s perform server cert OCSP check\n",
     70               "-o");
     71    PR_fprintf(pr_stderr,
     72               "  %-13s server port to be used\n",
     73               "-p");
     74    PR_fprintf(pr_stderr,
     75               "  %-13s use security databases in \"dbdir\"\n",
     76               "-d dbdir");
     77    PR_fprintf(pr_stderr,
     78               "  %-13s key database password\n",
     79               "-w password");
     80    PR_fprintf(pr_stderr,
     81               "  %-13s token password file\n",
     82               "-f pwfile");
     83    PR_fprintf(pr_stderr,
     84               "  %-13s communication cipher list\n",
     85               "-C cipher(s)");
     86    PR_fprintf(pr_stderr,
     87               "  %-13s OCSP responder location. This location is used to\n"
     88               "  %-13s check  status  of a server  certificate.  If  not \n"
     89               "  %-13s specified, location  will  be taken  from the AIA\n"
     90               "  %-13s server certificate extension.\n",
     91               "-l url", "", "", "");
     92    PR_fprintf(pr_stderr,
     93               "  %-13s OCSP Trusted Responder Cert nickname\n\n",
     94               "-t nickname");
     95 
     96    exit(1);
     97 }
     98 
     99 PRFileDesc *
    100 setupSSLSocket(PRNetAddr *addr)
    101 {
    102    PRFileDesc *tcpSocket;
    103    PRFileDesc *sslSocket;
    104    PRSocketOptionData socketOption;
    105    PRStatus prStatus;
    106    SECStatus secStatus;
    107 
    108    tcpSocket = PR_NewTCPSocket();
    109    if (tcpSocket == NULL) {
    110        errWarn("PR_NewTCPSocket");
    111    }
    112 
    113    /* Make the socket blocking. */
    114    socketOption.option = PR_SockOpt_Nonblocking;
    115    socketOption.value.non_blocking = PR_FALSE;
    116 
    117    prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
    118    if (prStatus != PR_SUCCESS) {
    119        errWarn("PR_SetSocketOption");
    120        goto loser;
    121    }
    122 
    123    /* Import the socket into the SSL layer. */
    124    sslSocket = SSL_ImportFD(NULL, tcpSocket);
    125    if (!sslSocket) {
    126        errWarn("SSL_ImportFD");
    127        goto loser;
    128    }
    129 
    130    /* Set configuration options. */
    131    secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
    132    if (secStatus != SECSuccess) {
    133        errWarn("SSL_OptionSet:SSL_SECURITY");
    134        goto loser;
    135    }
    136 
    137    secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
    138    if (secStatus != SECSuccess) {
    139        errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
    140        goto loser;
    141    }
    142 
    143    /* Set SSL callback routines. */
    144    secStatus = SSL_GetClientAuthDataHook(sslSocket,
    145                                          (SSLGetClientAuthData)myGetClientAuthData,
    146                                          (void *)certNickname);
    147    if (secStatus != SECSuccess) {
    148        errWarn("SSL_GetClientAuthDataHook");
    149        goto loser;
    150    }
    151 
    152    secStatus = SSL_AuthCertificateHook(sslSocket,
    153                                        (SSLAuthCertificate)myAuthCertificate,
    154                                        (void *)CERT_GetDefaultCertDB());
    155    if (secStatus != SECSuccess) {
    156        errWarn("SSL_AuthCertificateHook");
    157        goto loser;
    158    }
    159 
    160    secStatus = SSL_BadCertHook(sslSocket,
    161                                (SSLBadCertHandler)myBadCertHandler, NULL);
    162    if (secStatus != SECSuccess) {
    163        errWarn("SSL_BadCertHook");
    164        goto loser;
    165    }
    166 
    167    secStatus = SSL_HandshakeCallback(sslSocket,
    168                                      myHandshakeCallback,
    169                                      NULL);
    170    if (secStatus != SECSuccess) {
    171        errWarn("SSL_HandshakeCallback");
    172        goto loser;
    173    }
    174 
    175    return sslSocket;
    176 
    177 loser:
    178 
    179    PR_Close(tcpSocket);
    180    return NULL;
    181 }
    182 
    183 const char requestString[] = { "GET /testfile HTTP/1.0\r\n\r\n" };
    184 
    185 SECStatus
    186 handle_connection(PRFileDesc *sslSocket, int connection)
    187 {
    188    int countRead = 0;
    189    PRInt32 numBytes;
    190    char *readBuffer;
    191 
    192    readBuffer = PORT_Alloc(RD_BUF_SIZE);
    193    if (!readBuffer) {
    194        exitErr("PORT_Alloc");
    195    }
    196 
    197    /* compose the http request here. */
    198 
    199    numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
    200    if (numBytes <= 0) {
    201        errWarn("PR_Write");
    202        PR_Free(readBuffer);
    203        readBuffer = NULL;
    204        return SECFailure;
    205    }
    206 
    207    /* read until EOF */
    208    while (PR_TRUE) {
    209        numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
    210        if (numBytes == 0) {
    211            break; /* EOF */
    212        }
    213        if (numBytes < 0) {
    214            errWarn("PR_Read");
    215            break;
    216        }
    217        countRead += numBytes;
    218    }
    219 
    220    printSecurityInfo(stderr, sslSocket);
    221 
    222    PR_Free(readBuffer);
    223    readBuffer = NULL;
    224 
    225    /* Caller closes the socket. */
    226 
    227    fprintf(stderr,
    228            "***** Connection %d read %d bytes total.\n",
    229            connection, countRead);
    230 
    231    return SECSuccess; /* success */
    232 }
    233 
    234 #define BYTE(n, i) (((i) >> ((n)*8)) & 0xff)
    235 
    236 /* one copy of this function is launched in a separate thread for each
    237 ** connection to be made.
    238 */
    239 SECStatus
    240 do_connects(void *a, int connection)
    241 {
    242    PRNetAddr *addr = (PRNetAddr *)a;
    243    PRFileDesc *sslSocket;
    244    PRHostEnt hostEntry;
    245    char buffer[PR_NETDB_BUF_SIZE];
    246    PRStatus prStatus;
    247    PRIntn hostenum;
    248    PRInt32 ip;
    249    SECStatus secStatus;
    250 
    251    /* Set up SSL secure socket. */
    252    sslSocket = setupSSLSocket(addr);
    253    if (sslSocket == NULL) {
    254        errWarn("setupSSLSocket");
    255        return SECFailure;
    256    }
    257 
    258    secStatus = SSL_SetPKCS11PinArg(sslSocket, &pwdata);
    259    if (secStatus != SECSuccess) {
    260        errWarn("SSL_SetPKCS11PinArg");
    261        return secStatus;
    262    }
    263 
    264    secStatus = SSL_SetURL(sslSocket, hostName);
    265    if (secStatus != SECSuccess) {
    266        errWarn("SSL_SetURL");
    267        return secStatus;
    268    }
    269 
    270    /* Prepare and setup network connection. */
    271    prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
    272    if (prStatus != PR_SUCCESS) {
    273        errWarn("PR_GetHostByName");
    274        return SECFailure;
    275    }
    276 
    277    hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
    278    if (hostenum == -1) {
    279        errWarn("PR_EnumerateHostEnt");
    280        return SECFailure;
    281    }
    282 
    283    ip = PR_ntohl(addr->inet.ip);
    284    fprintf(stderr,
    285            "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n",
    286            hostName, BYTE(3, ip), BYTE(2, ip), BYTE(1, ip),
    287            BYTE(0, ip), PR_ntohs(addr->inet.port));
    288 
    289    prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
    290    if (prStatus != PR_SUCCESS) {
    291        errWarn("PR_Connect");
    292        return SECFailure;
    293    }
    294 
    295 /* Established SSL connection, ready to send data. */
    296 #if 0
    297    secStatus = SSL_ForceHandshake(sslSocket);
    298    if (secStatus != SECSuccess) {
    299        errWarn("SSL_ForceHandshake");
    300        return secStatus;
    301    }
    302 #endif
    303 
    304    secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
    305    if (secStatus != SECSuccess) {
    306        errWarn("SSL_ResetHandshake");
    307        prStatus = PR_Close(sslSocket);
    308        if (prStatus != PR_SUCCESS) {
    309            errWarn("PR_Close");
    310        }
    311        return secStatus;
    312    }
    313 
    314    secStatus = handle_connection(sslSocket, connection);
    315    if (secStatus != SECSuccess) {
    316        /* error already printed out in handle_connection */
    317        /* errWarn("handle_connection"); */
    318        prStatus = PR_Close(sslSocket);
    319        if (prStatus != PR_SUCCESS) {
    320            errWarn("PR_Close");
    321        }
    322        return secStatus;
    323    }
    324 
    325    PR_Close(sslSocket);
    326    return SECSuccess;
    327 }
    328 
    329 void
    330 client_main(int connections)
    331 {
    332    int i;
    333    SECStatus secStatus;
    334    PRStatus prStatus;
    335    PRInt32 rv;
    336    PRNetAddr addr;
    337    PRHostEnt hostEntry;
    338    char buffer[PR_NETDB_BUF_SIZE];
    339 
    340    /* Setup network connection. */
    341    prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
    342    if (prStatus != PR_SUCCESS) {
    343        PORT_Free(hostName);
    344        exitErr("PR_GetHostByName");
    345    }
    346 
    347    rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
    348    if (rv < 0) {
    349        PORT_Free(hostName);
    350        exitErr("PR_EnumerateHostEnt");
    351    }
    352 
    353    secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
    354    if (secStatus != SECSuccess) {
    355        PORT_Free(hostName);
    356        exitErr("launch_thread");
    357    }
    358 
    359    if (connections > 1) {
    360        /* wait for the first connection to terminate, then launch the rest. */
    361        reap_threads(&threadMGR);
    362        /* Start up the connections */
    363        for (i = 2; i <= connections; ++i) {
    364            secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
    365            if (secStatus != SECSuccess) {
    366                errWarn("launch_thread");
    367            }
    368        }
    369    }
    370 
    371    reap_threads(&threadMGR);
    372    destroy_thread_data(&threadMGR);
    373 }
    374 
    375 #define HEXCHAR_TO_INT(c, i)                   \
    376    if (((c) >= '0') && ((c) <= '9')) {        \
    377        i = (c) - '0';                         \
    378    } else if (((c) >= 'a') && ((c) <= 'f')) { \
    379        i = (c) - 'a' + 10;                    \
    380    } else if (((c) >= 'A') && ((c) <= 'F')) { \
    381        i = (c) - 'A' + 10;                    \
    382    } else {                                   \
    383        Usage(progName);                       \
    384    }
    385 
    386 int
    387 main(int argc, char **argv)
    388 {
    389    char *certDir = NULL;
    390    char *progName = NULL;
    391    int connections = 1;
    392    char *cipherString = NULL;
    393    char *respUrl = NULL;
    394    char *respCertName = NULL;
    395    SECStatus secStatus;
    396    PLOptState *optstate;
    397    PLOptStatus status;
    398    PRBool doOcspCheck = PR_FALSE;
    399 
    400    /* Call the NSPR initialization routines */
    401    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
    402 
    403    progName = PORT_Strdup(argv[0]);
    404 
    405    hostName = NULL;
    406    optstate = PL_CreateOptState(argc, argv, "C:cd:f:l:n:p:ot:w:");
    407    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
    408        switch (optstate->option) {
    409            case 'C':
    410                cipherString = PL_strdup(optstate->value);
    411                break;
    412            case 'c':
    413                dumpChain = PR_TRUE;
    414                break;
    415            case 'd':
    416                certDir = PL_strdup(optstate->value);
    417                break;
    418            case 'l':
    419                respUrl = PL_strdup(optstate->value);
    420                break;
    421            case 'p':
    422                port = PORT_Atoi(optstate->value);
    423                break;
    424            case 'o':
    425                doOcspCheck = PR_TRUE;
    426                break;
    427            case 't':
    428                respCertName = PL_strdup(optstate->value);
    429                break;
    430            case 'w':
    431                pwdata.source = PW_PLAINTEXT;
    432                pwdata.data = PORT_Strdup(optstate->value);
    433                break;
    434 
    435            case 'f':
    436                pwdata.source = PW_FROMFILE;
    437                pwdata.data = PORT_Strdup(optstate->value);
    438                break;
    439            case '\0':
    440                hostName = PL_strdup(optstate->value);
    441                break;
    442            default:
    443                Usage(progName);
    444        }
    445    }
    446    PL_DestroyOptState(optstate);
    447    optstate = NULL;
    448 
    449    if (port == 0) {
    450        port = 443;
    451    }
    452 
    453    if (port == 0 || hostName == NULL)
    454        Usage(progName);
    455 
    456    if (doOcspCheck &&
    457        ((respCertName != NULL && respUrl == NULL) ||
    458         (respUrl != NULL && respCertName == NULL))) {
    459        SECU_PrintError(progName, "options -l <url> and -t "
    460                                  "<responder> must be used together");
    461        Usage(progName);
    462    }
    463 
    464    PK11_SetPasswordFunc(SECU_GetModulePassword);
    465 
    466    /* Initialize the NSS libraries. */
    467    if (certDir) {
    468        secStatus = NSS_Init(certDir);
    469        PR_Free(certDir);
    470        certDir = NULL;
    471    } else {
    472        secStatus = NSS_NoDB_Init(NULL);
    473 
    474        /* load the builtins */
    475        SECMOD_AddNewModule("Builtins",
    476                            DLL_PREFIX "nssckbi." DLL_SUFFIX, 0, 0);
    477    }
    478    if (secStatus != SECSuccess) {
    479        exitErr("NSS_Init");
    480    }
    481    SECU_RegisterDynamicOids();
    482 
    483    if (doOcspCheck == PR_TRUE) {
    484        SECStatus rv;
    485        CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
    486        if (handle == NULL) {
    487            SECU_PrintError(progName, "problem getting certdb handle");
    488            goto cleanup;
    489        }
    490 
    491        rv = CERT_EnableOCSPChecking(handle);
    492        if (rv != SECSuccess) {
    493            SECU_PrintError(progName, "error enabling OCSP checking");
    494            goto cleanup;
    495        }
    496 
    497        if (respUrl != NULL) {
    498            rv = CERT_SetOCSPDefaultResponder(handle, respUrl,
    499                                              respCertName);
    500            if (rv != SECSuccess) {
    501                SECU_PrintError(progName,
    502                                "error setting default responder");
    503                goto cleanup;
    504            }
    505 
    506            rv = CERT_EnableOCSPDefaultResponder(handle);
    507            if (rv != SECSuccess) {
    508                SECU_PrintError(progName,
    509                                "error enabling default responder");
    510                goto cleanup;
    511            }
    512        }
    513    }
    514 
    515    /* All cipher suites except RSA_NULL_MD5 are enabled by
    516     * Domestic Policy. */
    517    NSS_SetDomesticPolicy();
    518    SSL_CipherPrefSetDefault(TLS_RSA_WITH_NULL_MD5, PR_TRUE);
    519 
    520    /* all the SSL2 and SSL3 cipher suites are enabled by default. */
    521    if (cipherString) {
    522        int ndx;
    523 
    524        /* disable all the ciphers, then enable the ones we want. */
    525        disableAllSSLCiphers();
    526 
    527        while (0 != (ndx = *cipherString++)) {
    528            int cipher = 0;
    529 
    530            if (ndx == ':') {
    531                int ctmp = 0;
    532 
    533                HEXCHAR_TO_INT(*cipherString, ctmp)
    534                cipher |= (ctmp << 12);
    535                cipherString++;
    536                HEXCHAR_TO_INT(*cipherString, ctmp)
    537                cipher |= (ctmp << 8);
    538                cipherString++;
    539                HEXCHAR_TO_INT(*cipherString, ctmp)
    540                cipher |= (ctmp << 4);
    541                cipherString++;
    542                HEXCHAR_TO_INT(*cipherString, ctmp)
    543                cipher |= ctmp;
    544                cipherString++;
    545            } else {
    546                if (!isalpha((unsigned char)ndx))
    547                    Usage(progName);
    548                ndx = tolower((unsigned char)ndx) - 'a';
    549                if (ndx < numSSL3CipherSuites) {
    550                    cipher = ssl3CipherSuites[ndx];
    551                }
    552            }
    553            if (cipher > 0) {
    554                SECStatus rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
    555                if (rv != SECSuccess) {
    556                    SECU_PrintError(progName,
    557                                    "error setting cipher default preference");
    558                    goto cleanup;
    559                }
    560            } else {
    561                Usage(progName);
    562            }
    563        }
    564    }
    565 
    566    client_main(connections);
    567 
    568 cleanup:
    569    if (doOcspCheck) {
    570        CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
    571        CERT_DisableOCSPDefaultResponder(handle);
    572        CERT_DisableOCSPChecking(handle);
    573    }
    574 
    575    if (NSS_Shutdown() != SECSuccess) {
    576        exit(1);
    577    }
    578 
    579    PR_Cleanup();
    580    PORT_Free(progName);
    581    return 0;
    582 }