tor-browser

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

ocspclnt.c (37714B)


      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 * Test program for client-side OCSP.
      7 */
      8 
      9 #include "secutil.h"
     10 #include "nspr.h"
     11 #include "plgetopt.h"
     12 #include "nss.h"
     13 #include "cert.h"
     14 #include "ocsp.h"
     15 #include "xconst.h" /*
     16                      * XXX internal header file; needed to get at
     17                      * cert_DecodeAuthInfoAccessExtension -- would be
     18                      * nice to not need this, but that would require
     19                      * better/different APIs.
     20                      */
     21 
     22 #ifndef NO_PP       /*                                               \
     23                     * Compile with this every once in a while to be \
     24                     * sure that no dependencies on it get added     \
     25                     * outside of the pretty-printing routines.      \
     26                     */
     27 #include "ocspti.h" /* internals for pretty-printing routines *only* */
     28 #endif              /* NO_PP */
     29 
     30 #if defined(_WIN32)
     31 #include "fcntl.h"
     32 #include "io.h"
     33 #endif
     34 
     35 #define DEFAULT_DB_DIR "~/.netscape"
     36 
     37 /* global */
     38 char *program_name;
     39 
     40 static void
     41 synopsis(char *progname)
     42 {
     43    PRFileDesc *pr_stderr;
     44 
     45    pr_stderr = PR_STDERR;
     46    PR_fprintf(pr_stderr, "Usage:");
     47    PR_fprintf(pr_stderr,
     48               "\t%s -p [-d <dir>]\n",
     49               progname);
     50    PR_fprintf(pr_stderr,
     51               "\t%s -P [-d <dir>]\n",
     52               progname);
     53    PR_fprintf(pr_stderr,
     54               "\t%s -r <name> [-a] [-L] [-s <name>] [-d <dir>]\n",
     55               progname);
     56    PR_fprintf(pr_stderr,
     57               "\t%s -R <name> [-a] [-l <location>] [-s <name>] [-d <dir>]\n",
     58               progname);
     59    PR_fprintf(pr_stderr,
     60               "\t%s -S <name> [-a] [-l <location> -t <name>]\n",
     61               progname);
     62    PR_fprintf(pr_stderr,
     63               "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
     64    PR_fprintf(pr_stderr,
     65               "\t%s -V <name> [-a] -u <usage> [-l <location> -t <name>]\n",
     66               progname);
     67    PR_fprintf(pr_stderr,
     68               "\t\t [-s <name>] [-w <time>] [-d <dir>]\n");
     69 }
     70 
     71 static void
     72 short_usage(char *progname)
     73 {
     74    PR_fprintf(PR_STDERR,
     75               "Type %s -H for more detailed descriptions\n",
     76               progname);
     77    synopsis(progname);
     78 }
     79 
     80 static void
     81 long_usage(char *progname)
     82 {
     83    PRFileDesc *pr_stderr;
     84 
     85    pr_stderr = PR_STDERR;
     86    synopsis(progname);
     87    PR_fprintf(pr_stderr, "\nCommands (must specify exactly one):\n");
     88    PR_fprintf(pr_stderr,
     89               "  %-13s Pretty-print a binary request read from stdin\n",
     90               "-p");
     91    PR_fprintf(pr_stderr,
     92               "  %-13s Pretty-print a binary response read from stdin\n",
     93               "-P");
     94    PR_fprintf(pr_stderr,
     95               "  %-13s Create a request for cert \"nickname\" on stdout\n",
     96               "-r nickname");
     97    PR_fprintf(pr_stderr,
     98               "  %-13s Get response for cert \"nickname\", dump to stdout\n",
     99               "-R nickname");
    100    PR_fprintf(pr_stderr,
    101               "  %-13s Get status for cert \"nickname\"\n",
    102               "-S nickname");
    103    PR_fprintf(pr_stderr,
    104               "  %-13s Fully verify cert \"nickname\", w/ status check\n",
    105               "-V nickname");
    106    PR_fprintf(pr_stderr,
    107               "\n     %-10s also can be the name of the file with DER or\n"
    108               "  %-13s PEM(use -a option) cert encoding\n",
    109               "nickname", "");
    110    PR_fprintf(pr_stderr, "Options:\n");
    111    PR_fprintf(pr_stderr,
    112               "  %-13s Decode input cert from PEM format. DER is default\n",
    113               "-a");
    114    PR_fprintf(pr_stderr,
    115               "  %-13s Add the service locator extension to the request\n",
    116               "-L");
    117    PR_fprintf(pr_stderr,
    118               "  %-13s Find security databases in \"dbdir\" (default %s)\n",
    119               "-d dbdir", DEFAULT_DB_DIR);
    120    PR_fprintf(pr_stderr,
    121               "  %-13s Use \"location\" as URL of responder\n",
    122               "-l location");
    123    PR_fprintf(pr_stderr,
    124               "  %-13s Trust cert \"nickname\" as response signer\n",
    125               "-t nickname");
    126    PR_fprintf(pr_stderr,
    127               "  %-13s Sign requests with cert \"nickname\"\n",
    128               "-s nickname");
    129    PR_fprintf(pr_stderr,
    130               "  %-13s Type of certificate usage for verification:\n",
    131               "-u usage");
    132    PR_fprintf(pr_stderr,
    133               "%-17s c   SSL Client\n", "");
    134    PR_fprintf(pr_stderr,
    135               "%-17s s   SSL Server\n", "");
    136    PR_fprintf(pr_stderr,
    137               "%-17s I   IPsec\n", "");
    138    PR_fprintf(pr_stderr,
    139               "%-17s e   Email Recipient\n", "");
    140    PR_fprintf(pr_stderr,
    141               "%-17s E   Email Signer\n", "");
    142    PR_fprintf(pr_stderr,
    143               "%-17s S   Object Signer\n", "");
    144    PR_fprintf(pr_stderr,
    145               "%-17s C   CA\n", "");
    146    PR_fprintf(pr_stderr,
    147               "  %-13s Validity time (default current time), one of:\n",
    148               "-w time");
    149    PR_fprintf(pr_stderr,
    150               "%-17s %-25s (GMT)\n", "", "YYMMDDhhmm[ss]Z");
    151    PR_fprintf(pr_stderr,
    152               "%-17s %-25s (later than GMT)\n", "", "YYMMDDhhmm[ss]+hhmm");
    153    PR_fprintf(pr_stderr,
    154               "%-17s %-25s (earlier than GMT)\n", "", "YYMMDDhhmm[ss]-hhmm");
    155 }
    156 
    157 #if defined(WIN32)
    158 /* We're going to write binary data to stdout, or read binary from stdin.
    159 * We must put stdout or stdin into O_BINARY mode or else
    160   outgoing \n's will become \r\n's, and incoming \r\n's will become \n's.
    161 */
    162 static SECStatus
    163 make_file_binary(FILE *binfile)
    164 {
    165    int smrv = _setmode(_fileno(binfile), _O_BINARY);
    166    if (smrv == -1) {
    167        fprintf(stderr, "%s: Cannot change stdout to binary mode.\n",
    168                program_name);
    169    }
    170    return smrv;
    171 }
    172 #define MAKE_FILE_BINARY make_file_binary
    173 #else
    174 #define MAKE_FILE_BINARY(file)
    175 #endif
    176 
    177 /*
    178 * XXX This is a generic function that would probably make a good
    179 * replacement for SECU_DER_Read (which is not at all specific to DER,
    180 * despite its name), but that requires fixing all of the tools...
    181 * Still, it should be done, whenenver I/somebody has the time.
    182 * (Also, consider whether this actually belongs in the security
    183 * library itself, not just in the command library.)
    184 *
    185 * This function takes an open file (a PRFileDesc *) and reads the
    186 * entire file into a SECItem.  (Obviously, the file is intended to
    187 * be small enough that such a thing is advisable.)  Both the SECItem
    188 * and the buffer it points to are allocated from the heap; the caller
    189 * is expected to free them.  ("SECITEM_FreeItem(item, PR_TRUE)")
    190 */
    191 static SECItem *
    192 read_file_into_item(PRFileDesc *in_file, SECItemType si_type)
    193 {
    194    PRStatus prv;
    195    SECItem *item;
    196    PRFileInfo file_info;
    197    PRInt32 bytes_read;
    198 
    199    prv = PR_GetOpenFileInfo(in_file, &file_info);
    200    if (prv != PR_SUCCESS)
    201        return NULL;
    202 
    203    if (file_info.size == 0) {
    204        /* XXX Need a better error; just grabbed this one for expediency. */
    205        PORT_SetError(SEC_ERROR_INPUT_LEN);
    206        return NULL;
    207    }
    208 
    209    if (file_info.size > 0xffff) { /* I think this is too big. */
    210        PORT_SetError(SEC_ERROR_NO_MEMORY);
    211        return NULL;
    212    }
    213 
    214    item = PORT_Alloc(sizeof(SECItem));
    215    if (item == NULL)
    216        return NULL;
    217 
    218    item->type = si_type;
    219    item->len = (unsigned int)file_info.size;
    220    item->data = PORT_Alloc((size_t)item->len);
    221    if (item->data == NULL)
    222        goto loser;
    223 
    224    bytes_read = PR_Read(in_file, item->data, (PRInt32)item->len);
    225    if (bytes_read < 0) {
    226        /* Something went wrong; error is already set for us. */
    227        goto loser;
    228    } else if (bytes_read == 0) {
    229        /* Something went wrong; we read nothing.  But no system/nspr error. */
    230        /* XXX Need to set an error here. */
    231        goto loser;
    232    } else if (item->len != (unsigned int)bytes_read) {
    233        /* Something went wrong; we read less (or more!?) than we expected. */
    234        /* XXX Need to set an error here. */
    235        goto loser;
    236    }
    237 
    238    return item;
    239 
    240 loser:
    241    SECITEM_FreeItem(item, PR_TRUE);
    242    return NULL;
    243 }
    244 
    245 /*
    246 * Create a DER-encoded OCSP request (for the certificate whose nickname
    247 * is "name") and dump it out.
    248 */
    249 static SECStatus
    250 create_request(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
    251               PRBool add_service_locator, PRBool add_acceptable_responses)
    252 {
    253    CERTCertList *certs = NULL;
    254    CERTCertificate *myCert = NULL;
    255    CERTOCSPRequest *request = NULL;
    256    PRTime now = PR_Now();
    257    SECItem *encoding = NULL;
    258    SECStatus rv = SECFailure;
    259 
    260    if (handle == NULL || cert == NULL)
    261        return rv;
    262 
    263    myCert = CERT_DupCertificate(cert);
    264    if (myCert == NULL)
    265        goto loser;
    266 
    267    /*
    268     * We need to create a list of one.
    269     */
    270    certs = CERT_NewCertList();
    271    if (certs == NULL)
    272        goto loser;
    273 
    274    if (CERT_AddCertToListTail(certs, myCert) != SECSuccess)
    275        goto loser;
    276 
    277    /*
    278     * Now that cert is included in the list, we need to be careful
    279     * that we do not try to destroy it twice.  This will prevent that.
    280     */
    281    myCert = NULL;
    282 
    283    request = CERT_CreateOCSPRequest(certs, now, add_service_locator, NULL);
    284    if (request == NULL)
    285        goto loser;
    286 
    287    if (add_acceptable_responses) {
    288        rv = CERT_AddOCSPAcceptableResponses(request,
    289                                             SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
    290        if (rv != SECSuccess)
    291            goto loser;
    292    }
    293 
    294    encoding = CERT_EncodeOCSPRequest(NULL, request, NULL);
    295    if (encoding == NULL)
    296        goto loser;
    297 
    298    MAKE_FILE_BINARY(out_file);
    299    if (fwrite(encoding->data, encoding->len, 1, out_file) != 1)
    300        goto loser;
    301 
    302    rv = SECSuccess;
    303 
    304 loser:
    305    if (encoding != NULL)
    306        SECITEM_FreeItem(encoding, PR_TRUE);
    307    if (request != NULL)
    308        CERT_DestroyOCSPRequest(request);
    309    if (certs != NULL)
    310        CERT_DestroyCertList(certs);
    311    if (myCert != NULL)
    312        CERT_DestroyCertificate(myCert);
    313 
    314    return rv;
    315 }
    316 
    317 /*
    318 * Create a DER-encoded OCSP request (for the certificate whose nickname is
    319 * "cert_name"), then get and dump a corresponding response.  The responder
    320 * location is either specified explicitly (as "responder_url") or found
    321 * via the AuthorityInfoAccess URL in the cert.
    322 */
    323 static SECStatus
    324 dump_response(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
    325              const char *responder_url)
    326 {
    327    CERTCertList *certs = NULL;
    328    CERTCertificate *myCert = NULL;
    329    char *loc = NULL;
    330    PRTime now = PR_Now();
    331    SECItem *response = NULL;
    332    SECStatus rv = SECFailure;
    333    PRBool includeServiceLocator;
    334 
    335    if (handle == NULL || cert == NULL)
    336        return rv;
    337 
    338    myCert = CERT_DupCertificate(cert);
    339    if (myCert == NULL)
    340        goto loser;
    341 
    342    if (responder_url != NULL) {
    343        loc = (char *)responder_url;
    344        includeServiceLocator = PR_TRUE;
    345    } else {
    346        loc = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
    347        if (loc == NULL)
    348            goto loser;
    349        includeServiceLocator = PR_FALSE;
    350    }
    351 
    352    /*
    353     * We need to create a list of one.
    354     */
    355    certs = CERT_NewCertList();
    356    if (certs == NULL)
    357        goto loser;
    358 
    359    if (CERT_AddCertToListTail(certs, myCert) != SECSuccess)
    360        goto loser;
    361 
    362    /*
    363     * Now that cert is included in the list, we need to be careful
    364     * that we do not try to destroy it twice.  This will prevent that.
    365     */
    366    myCert = NULL;
    367 
    368    response = CERT_GetEncodedOCSPResponse(NULL, certs, loc, now,
    369                                           includeServiceLocator,
    370                                           NULL, NULL, NULL);
    371    if (response == NULL)
    372        goto loser;
    373 
    374    MAKE_FILE_BINARY(out_file);
    375    if (fwrite(response->data, response->len, 1, out_file) != 1)
    376        goto loser;
    377 
    378    rv = SECSuccess;
    379 
    380 loser:
    381    if (response != NULL)
    382        SECITEM_FreeItem(response, PR_TRUE);
    383    if (certs != NULL)
    384        CERT_DestroyCertList(certs);
    385    if (myCert != NULL)
    386        CERT_DestroyCertificate(myCert);
    387    if (loc != NULL && loc != responder_url)
    388        PORT_Free(loc);
    389 
    390    return rv;
    391 }
    392 
    393 /*
    394 * Get the status for the specified certificate (whose nickname is "cert_name").
    395 * Directly use the OCSP function rather than doing a full verification.
    396 */
    397 static SECStatus
    398 get_cert_status(FILE *out_file, CERTCertDBHandle *handle,
    399                CERTCertificate *cert, const char *cert_name,
    400                PRTime verify_time)
    401 {
    402    SECStatus rv = SECFailure;
    403 
    404    if (handle == NULL || cert == NULL)
    405        goto loser;
    406 
    407    rv = CERT_CheckOCSPStatus(handle, cert, verify_time, NULL);
    408 
    409    fprintf(out_file, "Check of certificate \"%s\" ", cert_name);
    410    if (rv == SECSuccess) {
    411        fprintf(out_file, "succeeded.\n");
    412    } else {
    413        const char *error_string = SECU_Strerror(PORT_GetError());
    414        fprintf(out_file, "failed.  Reason:\n");
    415        if (error_string != NULL && PORT_Strlen(error_string) > 0)
    416            fprintf(out_file, "%s\n", error_string);
    417        else
    418            fprintf(out_file, "Unknown\n");
    419    }
    420 
    421    rv = SECSuccess;
    422 
    423 loser:
    424 
    425    return rv;
    426 }
    427 
    428 /*
    429 * Verify the specified certificate (whose nickname is "cert_name").
    430 * OCSP is already turned on, so we just need to call the standard
    431 * certificate verification API and let it do all the work.
    432 */
    433 static SECStatus
    434 verify_cert(FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
    435            const char *cert_name, SECCertUsage cert_usage, PRTime verify_time)
    436 {
    437    SECStatus rv = SECFailure;
    438 
    439    if (handle == NULL || cert == NULL)
    440        return rv;
    441 
    442    rv = CERT_VerifyCert(handle, cert, PR_TRUE, cert_usage, verify_time,
    443                         NULL, NULL);
    444 
    445    fprintf(out_file, "Verification of certificate \"%s\" ", cert_name);
    446    if (rv == SECSuccess) {
    447        fprintf(out_file, "succeeded.\n");
    448    } else {
    449        const char *error_string = SECU_Strerror(PORT_GetError());
    450        fprintf(out_file, "failed.  Reason:\n");
    451        if (error_string != NULL && PORT_Strlen(error_string) > 0)
    452            fprintf(out_file, "%s\n", error_string);
    453        else
    454            fprintf(out_file, "Unknown\n");
    455    }
    456 
    457    rv = SECSuccess;
    458 
    459    return rv;
    460 }
    461 
    462 CERTCertificate *
    463 find_certificate(CERTCertDBHandle *handle, const char *name, PRBool ascii)
    464 {
    465    CERTCertificate *cert = NULL;
    466    SECItem der;
    467    PRFileDesc *certFile;
    468 
    469    if (handle == NULL || name == NULL)
    470        return NULL;
    471 
    472    if (ascii == PR_FALSE) {
    473        /* by default need to check if there is cert nick is given */
    474        cert = CERT_FindCertByNicknameOrEmailAddr(handle, (char *)name);
    475        if (cert != NULL)
    476            return cert;
    477    }
    478 
    479    certFile = PR_Open(name, PR_RDONLY, 0);
    480    if (certFile == NULL) {
    481        return NULL;
    482    }
    483 
    484    if (SECU_ReadDERFromFile(&der, certFile, ascii, PR_FALSE) == SECSuccess) {
    485        cert = CERT_DecodeCertFromPackage((char *)der.data, der.len);
    486        SECITEM_FreeItem(&der, PR_FALSE);
    487    }
    488    PR_Close(certFile);
    489 
    490    return cert;
    491 }
    492 
    493 #ifdef NO_PP
    494 
    495 static SECStatus
    496 print_request(FILE *out_file, SECItem *data)
    497 {
    498    fprintf(out_file, "Cannot pretty-print request compiled with NO_PP.\n");
    499    return SECSuccess;
    500 }
    501 
    502 static SECStatus
    503 print_response(FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
    504 {
    505    fprintf(out_file, "Cannot pretty-print response compiled with NO_PP.\n");
    506    return SECSuccess;
    507 }
    508 
    509 #else /* NO_PP */
    510 
    511 static void
    512 print_ocsp_version(FILE *out_file, SECItem *version, int level)
    513 {
    514    if (version->len > 0) {
    515        SECU_PrintInteger(out_file, version, "Version", level);
    516    } else {
    517        SECU_Indent(out_file, level);
    518        fprintf(out_file, "Version: DEFAULT\n");
    519    }
    520 }
    521 
    522 static void
    523 print_ocsp_cert_id(FILE *out_file, CERTOCSPCertID *cert_id, int level)
    524 {
    525    SECU_Indent(out_file, level);
    526    fprintf(out_file, "Cert ID:\n");
    527    level++;
    528 
    529    SECU_PrintAlgorithmID(out_file, &(cert_id->hashAlgorithm),
    530                          "Hash Algorithm", level);
    531    SECU_PrintAsHex(out_file, &(cert_id->issuerNameHash),
    532                    "Issuer Name Hash", level);
    533    SECU_PrintAsHex(out_file, &(cert_id->issuerKeyHash),
    534                    "Issuer Key Hash", level);
    535    SECU_PrintInteger(out_file, &(cert_id->serialNumber),
    536                      "Serial Number", level);
    537    /* XXX lookup the cert; if found, print something nice (nickname?) */
    538 }
    539 
    540 static void
    541 print_raw_certificates(FILE *out_file, SECItem **raw_certs, int level)
    542 {
    543    SECItem *raw_cert;
    544    int i = 0;
    545    char cert_label[50];
    546 
    547    SECU_Indent(out_file, level);
    548 
    549    if (raw_certs == NULL) {
    550        fprintf(out_file, "No Certificates.\n");
    551        return;
    552    }
    553 
    554    fprintf(out_file, "Certificate List:\n");
    555    while ((raw_cert = raw_certs[i++]) != NULL) {
    556        snprintf(cert_label, sizeof(cert_label), "Certificate (%d)", i);
    557        (void)SECU_PrintSignedData(out_file, raw_cert, cert_label, level + 1,
    558                                   SECU_PrintCertificate);
    559    }
    560 }
    561 
    562 static void
    563 print_ocsp_extensions(FILE *out_file, CERTCertExtension **extensions,
    564                      char *msg, int level)
    565 {
    566    if (extensions) {
    567        SECU_PrintExtensions(out_file, extensions, msg, level);
    568    } else {
    569        SECU_Indent(out_file, level);
    570        fprintf(out_file, "No %s\n", msg);
    571    }
    572 }
    573 
    574 static void
    575 print_single_request(FILE *out_file, ocspSingleRequest *single, int level)
    576 {
    577    print_ocsp_cert_id(out_file, single->reqCert, level);
    578    print_ocsp_extensions(out_file, single->singleRequestExtensions,
    579                          "Single Request Extensions", level);
    580 }
    581 
    582 /*
    583 * Decode the DER/BER-encoded item "data" as an OCSP request
    584 * and pretty-print the subfields.
    585 */
    586 static SECStatus
    587 print_request(FILE *out_file, SECItem *data)
    588 {
    589    CERTOCSPRequest *request;
    590    ocspTBSRequest *tbsRequest;
    591    int level = 0;
    592 
    593    PORT_Assert(out_file != NULL);
    594    PORT_Assert(data != NULL);
    595    if (out_file == NULL || data == NULL) {
    596        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    597        return SECFailure;
    598    }
    599 
    600    request = CERT_DecodeOCSPRequest(data);
    601    if (request == NULL || request->tbsRequest == NULL)
    602        return SECFailure;
    603 
    604    tbsRequest = request->tbsRequest;
    605 
    606    fprintf(out_file, "TBS Request:\n");
    607    level++;
    608 
    609    print_ocsp_version(out_file, &(tbsRequest->version), level);
    610 
    611    /*
    612     * XXX Probably should be an interface to get the signer name
    613     * without looking inside the tbsRequest at all.
    614     */
    615    if (tbsRequest->requestorName != NULL) {
    616        SECU_Indent(out_file, level);
    617        fprintf(out_file, "XXX print the requestorName\n");
    618    } else {
    619        SECU_Indent(out_file, level);
    620        fprintf(out_file, "No Requestor Name.\n");
    621    }
    622 
    623    if (tbsRequest->requestList != NULL) {
    624        int i;
    625 
    626        for (i = 0; tbsRequest->requestList[i] != NULL; i++) {
    627            SECU_Indent(out_file, level);
    628            fprintf(out_file, "Request %d:\n", i);
    629            print_single_request(out_file, tbsRequest->requestList[i],
    630                                 level + 1);
    631        }
    632    } else {
    633        fprintf(out_file, "Request list is empty.\n");
    634    }
    635 
    636    print_ocsp_extensions(out_file, tbsRequest->requestExtensions,
    637                          "Request Extensions", level);
    638 
    639    if (request->optionalSignature != NULL) {
    640        ocspSignature *whole_sig;
    641        SECItem rawsig;
    642 
    643        fprintf(out_file, "Signature:\n");
    644 
    645        whole_sig = request->optionalSignature;
    646        SECU_PrintAlgorithmID(out_file, &(whole_sig->signatureAlgorithm),
    647                              "Signature Algorithm", level);
    648 
    649        rawsig = whole_sig->signature;
    650        DER_ConvertBitString(&rawsig);
    651        SECU_PrintAsHex(out_file, &rawsig, "Signature", level);
    652 
    653        print_raw_certificates(out_file, whole_sig->derCerts, level);
    654 
    655        fprintf(out_file, "XXX verify the sig and print result\n");
    656    } else {
    657        fprintf(out_file, "No Signature\n");
    658    }
    659 
    660    CERT_DestroyOCSPRequest(request);
    661    return SECSuccess;
    662 }
    663 
    664 static void
    665 print_revoked_info(FILE *out_file, ocspRevokedInfo *revoked_info, int level)
    666 {
    667    SECU_PrintGeneralizedTime(out_file, &(revoked_info->revocationTime),
    668                              "Revocation Time", level);
    669 
    670    if (revoked_info->revocationReason != NULL) {
    671        SECU_PrintAsHex(out_file, revoked_info->revocationReason,
    672                        "Revocation Reason", level);
    673    } else {
    674        SECU_Indent(out_file, level);
    675        fprintf(out_file, "No Revocation Reason.\n");
    676    }
    677 }
    678 
    679 static void
    680 print_cert_status(FILE *out_file, ocspCertStatus *status, int level)
    681 {
    682    SECU_Indent(out_file, level);
    683    fprintf(out_file, "Status: ");
    684 
    685    switch (status->certStatusType) {
    686        case ocspCertStatus_good:
    687            fprintf(out_file, "Cert is good.\n");
    688            break;
    689        case ocspCertStatus_revoked:
    690            fprintf(out_file, "Cert has been revoked.\n");
    691            print_revoked_info(out_file, status->certStatusInfo.revokedInfo,
    692                               level + 1);
    693            break;
    694        case ocspCertStatus_unknown:
    695            fprintf(out_file, "Cert is unknown to responder.\n");
    696            break;
    697        default:
    698            fprintf(out_file, "Unrecognized status.\n");
    699            break;
    700    }
    701 }
    702 
    703 static void
    704 print_single_response(FILE *out_file, CERTOCSPSingleResponse *single,
    705                      int level)
    706 {
    707    print_ocsp_cert_id(out_file, single->certID, level);
    708 
    709    print_cert_status(out_file, single->certStatus, level);
    710 
    711    SECU_PrintGeneralizedTime(out_file, &(single->thisUpdate),
    712                              "This Update", level);
    713 
    714    if (single->nextUpdate != NULL) {
    715        SECU_PrintGeneralizedTime(out_file, single->nextUpdate,
    716                                  "Next Update", level);
    717    } else {
    718        SECU_Indent(out_file, level);
    719        fprintf(out_file, "No Next Update\n");
    720    }
    721 
    722    print_ocsp_extensions(out_file, single->singleExtensions,
    723                          "Single Response Extensions", level);
    724 }
    725 
    726 static void
    727 print_responder_id(FILE *out_file, ocspResponderID *responderID, int level)
    728 {
    729    SECU_Indent(out_file, level);
    730    fprintf(out_file, "Responder ID ");
    731 
    732    switch (responderID->responderIDType) {
    733        case ocspResponderID_byName:
    734            fprintf(out_file, "(byName):\n");
    735            SECU_PrintName(out_file, &(responderID->responderIDValue.name),
    736                           "Name", level + 1);
    737            break;
    738        case ocspResponderID_byKey:
    739            fprintf(out_file, "(byKey):\n");
    740            SECU_PrintAsHex(out_file, &(responderID->responderIDValue.keyHash),
    741                            "Key Hash", level + 1);
    742            break;
    743        default:
    744            fprintf(out_file, "Unrecognized Responder ID Type\n");
    745            break;
    746    }
    747 }
    748 
    749 static void
    750 print_response_data(FILE *out_file, ocspResponseData *responseData, int level)
    751 {
    752    SECU_Indent(out_file, level);
    753    fprintf(out_file, "Response Data:\n");
    754    level++;
    755 
    756    print_ocsp_version(out_file, &(responseData->version), level);
    757 
    758    print_responder_id(out_file, responseData->responderID, level);
    759 
    760    SECU_PrintGeneralizedTime(out_file, &(responseData->producedAt),
    761                              "Produced At", level);
    762 
    763    if (responseData->responses != NULL) {
    764        int i;
    765 
    766        for (i = 0; responseData->responses[i] != NULL; i++) {
    767            SECU_Indent(out_file, level);
    768            fprintf(out_file, "Response %d:\n", i);
    769            print_single_response(out_file, responseData->responses[i],
    770                                  level + 1);
    771        }
    772    } else {
    773        fprintf(out_file, "Response list is empty.\n");
    774    }
    775 
    776    print_ocsp_extensions(out_file, responseData->responseExtensions,
    777                          "Response Extensions", level);
    778 }
    779 
    780 static void
    781 print_basic_response(FILE *out_file, ocspBasicOCSPResponse *basic, int level)
    782 {
    783    SECItem rawsig;
    784 
    785    SECU_Indent(out_file, level);
    786    fprintf(out_file, "Basic OCSP Response:\n");
    787    level++;
    788 
    789    print_response_data(out_file, basic->tbsResponseData, level);
    790 
    791    SECU_PrintAlgorithmID(out_file,
    792                          &(basic->responseSignature.signatureAlgorithm),
    793                          "Signature Algorithm", level);
    794 
    795    rawsig = basic->responseSignature.signature;
    796    DER_ConvertBitString(&rawsig);
    797    SECU_PrintAsHex(out_file, &rawsig, "Signature", level);
    798 
    799    print_raw_certificates(out_file, basic->responseSignature.derCerts, level);
    800 }
    801 
    802 /*
    803 * Note this must match (exactly) the enumeration ocspResponseStatus.
    804 */
    805 static char *responseStatusNames[] = {
    806    "successful (Response has valid confirmations)",
    807    "malformedRequest (Illegal confirmation request)",
    808    "internalError (Internal error in issuer)",
    809    "tryLater (Try again later)",
    810    "unused ((4) is not used)",
    811    "sigRequired (Must sign the request)",
    812    "unauthorized (Request unauthorized)"
    813 };
    814 
    815 /*
    816 * Decode the DER/BER-encoded item "data" as an OCSP response
    817 * and pretty-print the subfields.
    818 */
    819 static SECStatus
    820 print_response(FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
    821 {
    822    CERTOCSPResponse *response;
    823    int level = 0;
    824 
    825    PORT_Assert(out_file != NULL);
    826    PORT_Assert(data != NULL);
    827    if (out_file == NULL || data == NULL) {
    828        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    829        return SECFailure;
    830    }
    831 
    832    response = CERT_DecodeOCSPResponse(data);
    833    if (response == NULL)
    834        return SECFailure;
    835 
    836    if (response->statusValue >= ocspResponse_min &&
    837        response->statusValue <= ocspResponse_max) {
    838        fprintf(out_file, "Response Status: %s\n",
    839                responseStatusNames[response->statusValue]);
    840    } else {
    841        fprintf(out_file,
    842                "Response Status: other (Status value %d out of defined range)\n",
    843                (int)response->statusValue);
    844    }
    845 
    846    if (response->statusValue == ocspResponse_successful) {
    847        ocspResponseBytes *responseBytes = response->responseBytes;
    848        SECStatus sigStatus;
    849        CERTCertificate *signerCert = NULL;
    850 
    851        PORT_Assert(responseBytes != NULL);
    852 
    853        level++;
    854        fprintf(out_file, "Response Bytes:\n");
    855        SECU_PrintObjectID(out_file, &(responseBytes->responseType),
    856                           "Response Type", level);
    857        switch (response->responseBytes->responseTypeTag) {
    858            case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
    859                print_basic_response(out_file,
    860                                     responseBytes->decodedResponse.basic,
    861                                     level);
    862                break;
    863            default:
    864                SECU_Indent(out_file, level);
    865                fprintf(out_file, "Unknown response syntax\n");
    866                break;
    867        }
    868 
    869        sigStatus = CERT_VerifyOCSPResponseSignature(response, handle,
    870                                                     NULL, &signerCert, NULL);
    871        SECU_Indent(out_file, level);
    872        fprintf(out_file, "Signature verification ");
    873        if (sigStatus != SECSuccess) {
    874            fprintf(out_file, "failed: %s\n", SECU_Strerror(PORT_GetError()));
    875        } else {
    876            fprintf(out_file, "succeeded.\n");
    877            if (signerCert != NULL) {
    878                SECU_PrintName(out_file, &signerCert->subject, "Signer",
    879                               level);
    880                CERT_DestroyCertificate(signerCert);
    881            } else {
    882                SECU_Indent(out_file, level);
    883                fprintf(out_file, "No signer cert returned?\n");
    884            }
    885        }
    886    } else {
    887        SECU_Indent(out_file, level);
    888        fprintf(out_file, "Unsuccessful response, no more information.\n");
    889    }
    890 
    891    CERT_DestroyOCSPResponse(response);
    892    return SECSuccess;
    893 }
    894 
    895 #endif /* NO_PP */
    896 
    897 static SECStatus
    898 cert_usage_from_char(const char *cert_usage_str, SECCertUsage *cert_usage)
    899 {
    900    PORT_Assert(cert_usage_str != NULL);
    901    PORT_Assert(cert_usage != NULL);
    902 
    903    if (PORT_Strlen(cert_usage_str) != 1)
    904        return SECFailure;
    905 
    906    switch (*cert_usage_str) {
    907        case 'c':
    908            *cert_usage = certUsageSSLClient;
    909            break;
    910        case 's':
    911            *cert_usage = certUsageSSLServer;
    912            break;
    913        case 'I':
    914            *cert_usage = certUsageIPsec;
    915            break;
    916        case 'e':
    917            *cert_usage = certUsageEmailRecipient;
    918            break;
    919        case 'E':
    920            *cert_usage = certUsageEmailSigner;
    921            break;
    922        case 'S':
    923            *cert_usage = certUsageObjectSigner;
    924            break;
    925        case 'C':
    926            *cert_usage = certUsageVerifyCA;
    927            break;
    928        default:
    929            return SECFailure;
    930    }
    931 
    932    return SECSuccess;
    933 }
    934 
    935 int
    936 main(int argc, char **argv)
    937 {
    938    int retval;
    939    PRFileDesc *in_file;
    940    FILE *out_file; /* not PRFileDesc until SECU accepts it */
    941    int crequest, dresponse;
    942    int prequest, presponse;
    943    int ccert, vcert;
    944    const char *db_dir, *date_str, *cert_usage_str, *name;
    945    const char *responder_name, *responder_url, *signer_name;
    946    PRBool add_acceptable_responses, add_service_locator;
    947    SECItem *data = NULL;
    948    PLOptState *optstate;
    949    SECStatus rv;
    950    CERTCertDBHandle *handle = NULL;
    951    SECCertUsage cert_usage = certUsageSSLClient;
    952    PRTime verify_time;
    953    CERTCertificate *cert = NULL;
    954    PRBool ascii = PR_FALSE;
    955 
    956    retval = -1; /* what we return/exit with on error */
    957 
    958    program_name = PL_strrchr(argv[0], '/');
    959    program_name = program_name ? (program_name + 1) : argv[0];
    960 
    961    in_file = PR_STDIN;
    962    out_file = stdout;
    963 
    964    crequest = 0;
    965    dresponse = 0;
    966    prequest = 0;
    967    presponse = 0;
    968    ccert = 0;
    969    vcert = 0;
    970 
    971    db_dir = NULL;
    972    date_str = NULL;
    973    cert_usage_str = NULL;
    974    name = NULL;
    975    responder_name = NULL;
    976    responder_url = NULL;
    977    signer_name = NULL;
    978 
    979    add_acceptable_responses = PR_FALSE;
    980    add_service_locator = PR_FALSE;
    981 
    982    optstate = PL_CreateOptState(argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:");
    983    if (optstate == NULL) {
    984        SECU_PrintError(program_name, "PL_CreateOptState failed");
    985        return retval;
    986    }
    987 
    988    while (PL_GetNextOpt(optstate) == PL_OPT_OK) {
    989        switch (optstate->option) {
    990            case '?':
    991                short_usage(program_name);
    992                return retval;
    993 
    994            case 'A':
    995                add_acceptable_responses = PR_TRUE;
    996                break;
    997 
    998            case 'H':
    999                long_usage(program_name);
   1000                return retval;
   1001 
   1002            case 'L':
   1003                add_service_locator = PR_TRUE;
   1004                break;
   1005 
   1006            case 'P':
   1007                presponse = 1;
   1008                break;
   1009 
   1010            case 'R':
   1011                dresponse = 1;
   1012                name = optstate->value;
   1013                break;
   1014 
   1015            case 'S':
   1016                ccert = 1;
   1017                name = optstate->value;
   1018                break;
   1019 
   1020            case 'V':
   1021                vcert = 1;
   1022                name = optstate->value;
   1023                break;
   1024 
   1025            case 'a':
   1026                ascii = PR_TRUE;
   1027                break;
   1028 
   1029            case 'd':
   1030                db_dir = optstate->value;
   1031                break;
   1032 
   1033            case 'l':
   1034                responder_url = optstate->value;
   1035                break;
   1036 
   1037            case 'p':
   1038                prequest = 1;
   1039                break;
   1040 
   1041            case 'r':
   1042                crequest = 1;
   1043                name = optstate->value;
   1044                break;
   1045 
   1046            case 's':
   1047                signer_name = optstate->value;
   1048                break;
   1049 
   1050            case 't':
   1051                responder_name = optstate->value;
   1052                break;
   1053 
   1054            case 'u':
   1055                cert_usage_str = optstate->value;
   1056                break;
   1057 
   1058            case 'w':
   1059                date_str = optstate->value;
   1060                break;
   1061        }
   1062    }
   1063 
   1064    PL_DestroyOptState(optstate);
   1065 
   1066    if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) {
   1067        PR_fprintf(PR_STDERR, "%s: must specify exactly one command\n\n",
   1068                   program_name);
   1069        short_usage(program_name);
   1070        return retval;
   1071    }
   1072 
   1073    if (vcert) {
   1074        if (cert_usage_str == NULL) {
   1075            PR_fprintf(PR_STDERR, "%s: verification requires cert usage\n\n",
   1076                       program_name);
   1077            short_usage(program_name);
   1078            return retval;
   1079        }
   1080 
   1081        rv = cert_usage_from_char(cert_usage_str, &cert_usage);
   1082        if (rv != SECSuccess) {
   1083            PR_fprintf(PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n",
   1084                       program_name, cert_usage_str);
   1085            long_usage(program_name);
   1086            return retval;
   1087        }
   1088    }
   1089 
   1090    if (ccert + vcert) {
   1091        if (responder_url != NULL || responder_name != NULL) {
   1092            /*
   1093             * To do a full status check, both the URL and the cert name
   1094             * of the responder must be specified if either one is.
   1095             */
   1096            if (responder_url == NULL || responder_name == NULL) {
   1097                if (responder_url == NULL)
   1098                    PR_fprintf(PR_STDERR,
   1099                               "%s: must also specify responder location\n\n",
   1100                               program_name);
   1101                else
   1102                    PR_fprintf(PR_STDERR,
   1103                               "%s: must also specify responder name\n\n",
   1104                               program_name);
   1105                short_usage(program_name);
   1106                return retval;
   1107            }
   1108        }
   1109 
   1110        if (date_str != NULL) {
   1111            rv = DER_AsciiToTime(&verify_time, (char *)date_str);
   1112            if (rv != SECSuccess) {
   1113                SECU_PrintError(program_name, "error converting time string");
   1114                PR_fprintf(PR_STDERR, "\n");
   1115                long_usage(program_name);
   1116                return retval;
   1117            }
   1118        } else {
   1119            verify_time = PR_Now();
   1120        }
   1121    }
   1122 
   1123    retval = -2; /* errors change from usage to runtime */
   1124 
   1125    /*
   1126     * Initialize the NSPR and Security libraries.
   1127     */
   1128    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
   1129    db_dir = SECU_ConfigDirectory(db_dir);
   1130    rv = NSS_Init(db_dir);
   1131    if (rv != SECSuccess) {
   1132        SECU_PrintError(program_name, "NSS_Init failed");
   1133        goto prdone;
   1134    }
   1135    SECU_RegisterDynamicOids();
   1136 
   1137    if (prequest + presponse) {
   1138        MAKE_FILE_BINARY(stdin);
   1139        data = read_file_into_item(in_file, siBuffer);
   1140        if (data == NULL) {
   1141            SECU_PrintError(program_name, "problem reading input");
   1142            goto nssdone;
   1143        }
   1144    }
   1145 
   1146    if (crequest + dresponse + presponse + ccert + vcert) {
   1147        handle = CERT_GetDefaultCertDB();
   1148        if (handle == NULL) {
   1149            SECU_PrintError(program_name, "problem getting certdb handle");
   1150            goto nssdone;
   1151        }
   1152 
   1153        /*
   1154         * It would be fine to do the enable for all of these commands,
   1155         * but this way we check that everything but an overall verify
   1156         * can be done without it.  That is, that the individual pieces
   1157         * work on their own.
   1158         */
   1159        if (vcert) {
   1160            rv = CERT_EnableOCSPChecking(handle);
   1161            if (rv != SECSuccess) {
   1162                SECU_PrintError(program_name, "error enabling OCSP checking");
   1163                goto nssdone;
   1164            }
   1165        }
   1166 
   1167        if ((ccert + vcert) && (responder_name != NULL)) {
   1168            rv = CERT_SetOCSPDefaultResponder(handle, responder_url,
   1169                                              responder_name);
   1170            if (rv != SECSuccess) {
   1171                SECU_PrintError(program_name,
   1172                                "error setting default responder");
   1173                goto nssdone;
   1174            }
   1175 
   1176            rv = CERT_EnableOCSPDefaultResponder(handle);
   1177            if (rv != SECSuccess) {
   1178                SECU_PrintError(program_name,
   1179                                "error enabling default responder");
   1180                goto nssdone;
   1181            }
   1182        }
   1183    }
   1184 
   1185 #define NOTYET(opt)                                         \
   1186    {                                                       \
   1187        PR_fprintf(PR_STDERR, "%s not yet working\n", opt); \
   1188        exit(-1);                                           \
   1189    }
   1190 
   1191    if (name) {
   1192        cert = find_certificate(handle, name, ascii);
   1193    }
   1194 
   1195    if (crequest) {
   1196        if (signer_name != NULL) {
   1197            NOTYET("-s");
   1198        }
   1199        rv = create_request(out_file, handle, cert, add_service_locator,
   1200                            add_acceptable_responses);
   1201    } else if (dresponse) {
   1202        if (signer_name != NULL) {
   1203            NOTYET("-s");
   1204        }
   1205        rv = dump_response(out_file, handle, cert, responder_url);
   1206    } else if (prequest) {
   1207        rv = print_request(out_file, data);
   1208    } else if (presponse) {
   1209        rv = print_response(out_file, data, handle);
   1210    } else if (ccert) {
   1211        if (signer_name != NULL) {
   1212            NOTYET("-s");
   1213        }
   1214        rv = get_cert_status(out_file, handle, cert, name, verify_time);
   1215    } else if (vcert) {
   1216        if (signer_name != NULL) {
   1217            NOTYET("-s");
   1218        }
   1219        rv = verify_cert(out_file, handle, cert, name, cert_usage, verify_time);
   1220    }
   1221 
   1222    if (rv != SECSuccess)
   1223        SECU_PrintError(program_name, "error performing requested operation");
   1224    else
   1225        retval = 0;
   1226 
   1227 nssdone:
   1228    if (cert) {
   1229        CERT_DestroyCertificate(cert);
   1230    }
   1231 
   1232    if (data != NULL) {
   1233        SECITEM_FreeItem(data, PR_TRUE);
   1234    }
   1235 
   1236    if (handle != NULL) {
   1237        CERT_DisableOCSPDefaultResponder(handle);
   1238        CERT_DisableOCSPChecking(handle);
   1239    }
   1240 
   1241    if (NSS_Shutdown() != SECSuccess) {
   1242        retval = 1;
   1243    }
   1244 
   1245 prdone:
   1246    PR_Cleanup();
   1247    return retval;
   1248 }