michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0:
michael@0: /*
michael@0: * Test program for client-side OCSP.
michael@0: */
michael@0:
michael@0: #include "secutil.h"
michael@0: #include "nspr.h"
michael@0: #include "plgetopt.h"
michael@0: #include "nss.h"
michael@0: #include "cert.h"
michael@0: #include "ocsp.h"
michael@0: #include "xconst.h" /*
michael@0: * XXX internal header file; needed to get at
michael@0: * cert_DecodeAuthInfoAccessExtension -- would be
michael@0: * nice to not need this, but that would require
michael@0: * better/different APIs.
michael@0: */
michael@0:
michael@0: #ifndef NO_PP /*
michael@0: * Compile with this every once in a while to be
michael@0: * sure that no dependencies on it get added
michael@0: * outside of the pretty-printing routines.
michael@0: */
michael@0: #include "ocspti.h" /* internals for pretty-printing routines *only* */
michael@0: #endif /* NO_PP */
michael@0:
michael@0: #if defined(_WIN32)
michael@0: #include "fcntl.h"
michael@0: #include "io.h"
michael@0: #endif
michael@0:
michael@0: #define DEFAULT_DB_DIR "~/.netscape"
michael@0:
michael@0: /* global */
michael@0: char *program_name;
michael@0:
michael@0:
michael@0: static void
michael@0: synopsis (char *program_name)
michael@0: {
michael@0: PRFileDesc *pr_stderr;
michael@0:
michael@0: pr_stderr = PR_STDERR;
michael@0: PR_fprintf (pr_stderr, "Usage:");
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -p [-d
]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -P [-d ]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -r [-a] [-L] [-s ] [-d ]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -R [-a] [-l ] [-s ] [-d ]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -S [-a] [-l -t ]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t\t [-s ] [-w ] [-d ]\n");
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t%s -V [-a] -u [-l -t ]\n",
michael@0: program_name);
michael@0: PR_fprintf (pr_stderr,
michael@0: "\t\t [-s ] [-w ] [-d ]\n");
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: short_usage (char *program_name)
michael@0: {
michael@0: PR_fprintf (PR_STDERR,
michael@0: "Type %s -H for more detailed descriptions\n",
michael@0: program_name);
michael@0: synopsis (program_name);
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: long_usage (char *program_name)
michael@0: {
michael@0: PRFileDesc *pr_stderr;
michael@0:
michael@0: pr_stderr = PR_STDERR;
michael@0: synopsis (program_name);
michael@0: PR_fprintf (pr_stderr, "\nCommands (must specify exactly one):\n");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Pretty-print a binary request read from stdin\n",
michael@0: "-p");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Pretty-print a binary response read from stdin\n",
michael@0: "-P");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Create a request for cert \"nickname\" on stdout\n",
michael@0: "-r nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Get response for cert \"nickname\", dump to stdout\n",
michael@0: "-R nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Get status for cert \"nickname\"\n",
michael@0: "-S nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Fully verify cert \"nickname\", w/ status check\n",
michael@0: "-V nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: "\n %-10s also can be the name of the file with DER or\n"
michael@0: " %-13s PEM(use -a option) cert encoding\n", "nickname", "");
michael@0: PR_fprintf (pr_stderr, "Options:\n");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Decode input cert from PEM format. DER is default\n",
michael@0: "-a");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Add the service locator extension to the request\n",
michael@0: "-L");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Find security databases in \"dbdir\" (default %s)\n",
michael@0: "-d dbdir", DEFAULT_DB_DIR);
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Use \"location\" as URL of responder\n",
michael@0: "-l location");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Trust cert \"nickname\" as response signer\n",
michael@0: "-t nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Sign requests with cert \"nickname\"\n",
michael@0: "-s nickname");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Type of certificate usage for verification:\n",
michael@0: "-u usage");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s c SSL Client\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s s SSL Server\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s e Email Recipient\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s E Email Signer\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s S Object Signer\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s C CA\n", "");
michael@0: PR_fprintf (pr_stderr,
michael@0: " %-13s Validity time (default current time), one of:\n",
michael@0: "-w time");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s %-25s (GMT)\n", "", "YYMMDDhhmm[ss]Z");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s %-25s (later than GMT)\n", "", "YYMMDDhhmm[ss]+hhmm");
michael@0: PR_fprintf (pr_stderr,
michael@0: "%-17s %-25s (earlier than GMT)\n", "", "YYMMDDhhmm[ss]-hhmm");
michael@0: }
michael@0:
michael@0: #if defined(WIN32)
michael@0: /* We're going to write binary data to stdout, or read binary from stdin.
michael@0: * We must put stdout or stdin into O_BINARY mode or else
michael@0: outgoing \n's will become \r\n's, and incoming \r\n's will become \n's.
michael@0: */
michael@0: static SECStatus
michael@0: make_file_binary(FILE * binfile)
michael@0: {
michael@0: int smrv = _setmode(_fileno(binfile), _O_BINARY);
michael@0: if (smrv == -1) {
michael@0: fprintf(stderr, "%s: Cannot change stdout to binary mode.\n",
michael@0: program_name);
michael@0: }
michael@0: return smrv;
michael@0: }
michael@0: #define MAKE_FILE_BINARY make_file_binary
michael@0: #else
michael@0: #define MAKE_FILE_BINARY(file)
michael@0: #endif
michael@0:
michael@0: /*
michael@0: * XXX This is a generic function that would probably make a good
michael@0: * replacement for SECU_DER_Read (which is not at all specific to DER,
michael@0: * despite its name), but that requires fixing all of the tools...
michael@0: * Still, it should be done, whenenver I/somebody has the time.
michael@0: * (Also, consider whether this actually belongs in the security
michael@0: * library itself, not just in the command library.)
michael@0: *
michael@0: * This function takes an open file (a PRFileDesc *) and reads the
michael@0: * entire file into a SECItem. (Obviously, the file is intended to
michael@0: * be small enough that such a thing is advisable.) Both the SECItem
michael@0: * and the buffer it points to are allocated from the heap; the caller
michael@0: * is expected to free them. ("SECITEM_FreeItem(item, PR_TRUE)")
michael@0: */
michael@0: static SECItem *
michael@0: read_file_into_item (PRFileDesc *in_file, SECItemType si_type)
michael@0: {
michael@0: PRStatus prv;
michael@0: SECItem *item;
michael@0: PRFileInfo file_info;
michael@0: PRInt32 bytes_read;
michael@0:
michael@0: prv = PR_GetOpenFileInfo (in_file, &file_info);
michael@0: if (prv != PR_SUCCESS)
michael@0: return NULL;
michael@0:
michael@0: if (file_info.size == 0) {
michael@0: /* XXX Need a better error; just grabbed this one for expediency. */
michael@0: PORT_SetError (SEC_ERROR_INPUT_LEN);
michael@0: return NULL;
michael@0: }
michael@0:
michael@0: if (file_info.size > 0xffff) { /* I think this is too big. */
michael@0: PORT_SetError (SEC_ERROR_NO_MEMORY);
michael@0: return NULL;
michael@0: }
michael@0:
michael@0: item = PORT_Alloc (sizeof (SECItem));
michael@0: if (item == NULL)
michael@0: return NULL;
michael@0:
michael@0: item->type = si_type;
michael@0: item->len = (unsigned int) file_info.size;
michael@0: item->data = PORT_Alloc ((size_t)item->len);
michael@0: if (item->data == NULL)
michael@0: goto loser;
michael@0:
michael@0: bytes_read = PR_Read (in_file, item->data, (PRInt32) item->len);
michael@0: if (bytes_read < 0) {
michael@0: /* Something went wrong; error is already set for us. */
michael@0: goto loser;
michael@0: } else if (bytes_read == 0) {
michael@0: /* Something went wrong; we read nothing. But no system/nspr error. */
michael@0: /* XXX Need to set an error here. */
michael@0: goto loser;
michael@0: } else if (item->len != (unsigned int)bytes_read) {
michael@0: /* Something went wrong; we read less (or more!?) than we expected. */
michael@0: /* XXX Need to set an error here. */
michael@0: goto loser;
michael@0: }
michael@0:
michael@0: return item;
michael@0:
michael@0: loser:
michael@0: SECITEM_FreeItem (item, PR_TRUE);
michael@0: return NULL;
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Create a DER-encoded OCSP request (for the certificate whose nickname
michael@0: * is "name") and dump it out.
michael@0: */
michael@0: static SECStatus
michael@0: create_request (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
michael@0: PRBool add_service_locator, PRBool add_acceptable_responses)
michael@0: {
michael@0: CERTCertList *certs = NULL;
michael@0: CERTCertificate *myCert = NULL;
michael@0: CERTOCSPRequest *request = NULL;
michael@0: PRTime now = PR_Now();
michael@0: SECItem *encoding = NULL;
michael@0: SECStatus rv = SECFailure;
michael@0:
michael@0: if (handle == NULL || cert == NULL)
michael@0: return rv;
michael@0:
michael@0: myCert = CERT_DupCertificate(cert);
michael@0: if (myCert == NULL)
michael@0: goto loser;
michael@0:
michael@0: /*
michael@0: * We need to create a list of one.
michael@0: */
michael@0: certs = CERT_NewCertList();
michael@0: if (certs == NULL)
michael@0: goto loser;
michael@0:
michael@0: if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
michael@0: goto loser;
michael@0:
michael@0: /*
michael@0: * Now that cert is included in the list, we need to be careful
michael@0: * that we do not try to destroy it twice. This will prevent that.
michael@0: */
michael@0: myCert = NULL;
michael@0:
michael@0: request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL);
michael@0: if (request == NULL)
michael@0: goto loser;
michael@0:
michael@0: if (add_acceptable_responses) {
michael@0: rv = CERT_AddOCSPAcceptableResponses(request,
michael@0: SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
michael@0: if (rv != SECSuccess)
michael@0: goto loser;
michael@0: }
michael@0:
michael@0: encoding = CERT_EncodeOCSPRequest (NULL, request, NULL);
michael@0: if (encoding == NULL)
michael@0: goto loser;
michael@0:
michael@0: MAKE_FILE_BINARY(out_file);
michael@0: if (fwrite (encoding->data, encoding->len, 1, out_file) != 1)
michael@0: goto loser;
michael@0:
michael@0: rv = SECSuccess;
michael@0:
michael@0: loser:
michael@0: if (encoding != NULL)
michael@0: SECITEM_FreeItem(encoding, PR_TRUE);
michael@0: if (request != NULL)
michael@0: CERT_DestroyOCSPRequest(request);
michael@0: if (certs != NULL)
michael@0: CERT_DestroyCertList (certs);
michael@0: if (myCert != NULL)
michael@0: CERT_DestroyCertificate(myCert);
michael@0:
michael@0: return rv;
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Create a DER-encoded OCSP request (for the certificate whose nickname is
michael@0: * "cert_name"), then get and dump a corresponding response. The responder
michael@0: * location is either specified explicitly (as "responder_url") or found
michael@0: * via the AuthorityInfoAccess URL in the cert.
michael@0: */
michael@0: static SECStatus
michael@0: dump_response (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
michael@0: const char *responder_url)
michael@0: {
michael@0: CERTCertList *certs = NULL;
michael@0: CERTCertificate *myCert = NULL;
michael@0: char *loc = NULL;
michael@0: PRTime now = PR_Now();
michael@0: SECItem *response = NULL;
michael@0: SECStatus rv = SECFailure;
michael@0: PRBool includeServiceLocator;
michael@0:
michael@0: if (handle == NULL || cert == NULL)
michael@0: return rv;
michael@0:
michael@0: myCert = CERT_DupCertificate(cert);
michael@0: if (myCert == NULL)
michael@0: goto loser;
michael@0:
michael@0: if (responder_url != NULL) {
michael@0: loc = (char *) responder_url;
michael@0: includeServiceLocator = PR_TRUE;
michael@0: } else {
michael@0: loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert);
michael@0: if (loc == NULL)
michael@0: goto loser;
michael@0: includeServiceLocator = PR_FALSE;
michael@0: }
michael@0:
michael@0: /*
michael@0: * We need to create a list of one.
michael@0: */
michael@0: certs = CERT_NewCertList();
michael@0: if (certs == NULL)
michael@0: goto loser;
michael@0:
michael@0: if (CERT_AddCertToListTail (certs, myCert) != SECSuccess)
michael@0: goto loser;
michael@0:
michael@0: /*
michael@0: * Now that cert is included in the list, we need to be careful
michael@0: * that we do not try to destroy it twice. This will prevent that.
michael@0: */
michael@0: myCert = NULL;
michael@0:
michael@0: response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now,
michael@0: includeServiceLocator,
michael@0: NULL, NULL, NULL);
michael@0: if (response == NULL)
michael@0: goto loser;
michael@0:
michael@0: MAKE_FILE_BINARY(out_file);
michael@0: if (fwrite (response->data, response->len, 1, out_file) != 1)
michael@0: goto loser;
michael@0:
michael@0: rv = SECSuccess;
michael@0:
michael@0: loser:
michael@0: if (response != NULL)
michael@0: SECITEM_FreeItem (response, PR_TRUE);
michael@0: if (certs != NULL)
michael@0: CERT_DestroyCertList (certs);
michael@0: if (myCert != NULL)
michael@0: CERT_DestroyCertificate(myCert);
michael@0: if (loc != NULL && loc != responder_url)
michael@0: PORT_Free (loc);
michael@0:
michael@0: return rv;
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Get the status for the specified certificate (whose nickname is "cert_name").
michael@0: * Directly use the OCSP function rather than doing a full verification.
michael@0: */
michael@0: static SECStatus
michael@0: get_cert_status (FILE *out_file, CERTCertDBHandle *handle,
michael@0: CERTCertificate *cert, const char *cert_name,
michael@0: PRTime verify_time)
michael@0: {
michael@0: SECStatus rv = SECFailure;
michael@0:
michael@0: if (handle == NULL || cert == NULL)
michael@0: goto loser;
michael@0:
michael@0: rv = CERT_CheckOCSPStatus (handle, cert, verify_time, NULL);
michael@0:
michael@0: fprintf (out_file, "Check of certificate \"%s\" ", cert_name);
michael@0: if (rv == SECSuccess) {
michael@0: fprintf (out_file, "succeeded.\n");
michael@0: } else {
michael@0: const char *error_string = SECU_Strerror(PORT_GetError());
michael@0: fprintf (out_file, "failed. Reason:\n");
michael@0: if (error_string != NULL && PORT_Strlen(error_string) > 0)
michael@0: fprintf (out_file, "%s\n", error_string);
michael@0: else
michael@0: fprintf (out_file, "Unknown\n");
michael@0: }
michael@0:
michael@0: rv = SECSuccess;
michael@0:
michael@0: loser:
michael@0:
michael@0: return rv;
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Verify the specified certificate (whose nickname is "cert_name").
michael@0: * OCSP is already turned on, so we just need to call the standard
michael@0: * certificate verification API and let it do all the work.
michael@0: */
michael@0: static SECStatus
michael@0: verify_cert (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert,
michael@0: const char *cert_name, SECCertUsage cert_usage, PRTime verify_time)
michael@0: {
michael@0: SECStatus rv = SECFailure;
michael@0:
michael@0: if (handle == NULL || cert == NULL)
michael@0: return rv;
michael@0:
michael@0: rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time,
michael@0: NULL, NULL);
michael@0:
michael@0: fprintf (out_file, "Verification of certificate \"%s\" ", cert_name);
michael@0: if (rv == SECSuccess) {
michael@0: fprintf (out_file, "succeeded.\n");
michael@0: } else {
michael@0: const char *error_string = SECU_Strerror(PORT_GetError());
michael@0: fprintf (out_file, "failed. Reason:\n");
michael@0: if (error_string != NULL && PORT_Strlen(error_string) > 0)
michael@0: fprintf (out_file, "%s\n", error_string);
michael@0: else
michael@0: fprintf (out_file, "Unknown\n");
michael@0: }
michael@0:
michael@0: rv = SECSuccess;
michael@0:
michael@0: return rv;
michael@0: }
michael@0:
michael@0: CERTCertificate*
michael@0: find_certificate(CERTCertDBHandle *handle, const char *name, PRBool ascii)
michael@0: {
michael@0: CERTCertificate *cert = NULL;
michael@0: SECItem der;
michael@0: PRFileDesc *certFile;
michael@0:
michael@0: if (handle == NULL || name == NULL)
michael@0: return NULL;
michael@0:
michael@0: if (ascii == PR_FALSE) {
michael@0: /* by default need to check if there is cert nick is given */
michael@0: cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) name);
michael@0: if (cert != NULL)
michael@0: return cert;
michael@0: }
michael@0:
michael@0: certFile = PR_Open(name, PR_RDONLY, 0);
michael@0: if (certFile == NULL) {
michael@0: return NULL;
michael@0: }
michael@0:
michael@0: if (SECU_ReadDERFromFile(&der, certFile, ascii, PR_FALSE) == SECSuccess) {
michael@0: cert = CERT_DecodeCertFromPackage((char*)der.data, der.len);
michael@0: SECITEM_FreeItem(&der, PR_FALSE);
michael@0: }
michael@0: PR_Close(certFile);
michael@0:
michael@0: return cert;
michael@0: }
michael@0:
michael@0:
michael@0: #ifdef NO_PP
michael@0:
michael@0: static SECStatus
michael@0: print_request (FILE *out_file, SECItem *data)
michael@0: {
michael@0: fprintf (out_file, "Cannot pretty-print request compiled with NO_PP.\n");
michael@0: return SECSuccess;
michael@0: }
michael@0:
michael@0: static SECStatus
michael@0: print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
michael@0: {
michael@0: fprintf (out_file, "Cannot pretty-print response compiled with NO_PP.\n");
michael@0: return SECSuccess;
michael@0: }
michael@0:
michael@0: #else /* NO_PP */
michael@0:
michael@0: static void
michael@0: print_ocsp_version (FILE *out_file, SECItem *version, int level)
michael@0: {
michael@0: if (version->len > 0) {
michael@0: SECU_PrintInteger (out_file, version, "Version", level);
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Version: DEFAULT\n");
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_ocsp_cert_id (FILE *out_file, CERTOCSPCertID *cert_id, int level)
michael@0: {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Cert ID:\n");
michael@0: level++;
michael@0:
michael@0: SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm),
michael@0: "Hash Algorithm", level);
michael@0: SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash),
michael@0: "Issuer Name Hash", level);
michael@0: SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash),
michael@0: "Issuer Key Hash", level);
michael@0: SECU_PrintInteger (out_file, &(cert_id->serialNumber),
michael@0: "Serial Number", level);
michael@0: /* XXX lookup the cert; if found, print something nice (nickname?) */
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_raw_certificates (FILE *out_file, SECItem **raw_certs, int level)
michael@0: {
michael@0: SECItem *raw_cert;
michael@0: int i = 0;
michael@0: char cert_label[50];
michael@0:
michael@0: SECU_Indent (out_file, level);
michael@0:
michael@0: if (raw_certs == NULL) {
michael@0: fprintf (out_file, "No Certificates.\n");
michael@0: return;
michael@0: }
michael@0:
michael@0: fprintf (out_file, "Certificate List:\n");
michael@0: while ((raw_cert = raw_certs[i++]) != NULL) {
michael@0: sprintf (cert_label, "Certificate (%d)", i);
michael@0: (void) SECU_PrintSignedData (out_file, raw_cert, cert_label, level + 1,
michael@0: SECU_PrintCertificate);
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_ocsp_extensions (FILE *out_file, CERTCertExtension **extensions,
michael@0: char *msg, int level)
michael@0: {
michael@0: if (extensions) {
michael@0: SECU_PrintExtensions (out_file, extensions, msg, level);
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "No %s\n", msg);
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_single_request (FILE *out_file, ocspSingleRequest *single, int level)
michael@0: {
michael@0: print_ocsp_cert_id (out_file, single->reqCert, level);
michael@0: print_ocsp_extensions (out_file, single->singleRequestExtensions,
michael@0: "Single Request Extensions", level);
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Decode the DER/BER-encoded item "data" as an OCSP request
michael@0: * and pretty-print the subfields.
michael@0: */
michael@0: static SECStatus
michael@0: print_request (FILE *out_file, SECItem *data)
michael@0: {
michael@0: CERTOCSPRequest *request;
michael@0: ocspTBSRequest *tbsRequest;
michael@0: int level = 0;
michael@0:
michael@0: PORT_Assert (out_file != NULL);
michael@0: PORT_Assert (data != NULL);
michael@0: if (out_file == NULL || data == NULL) {
michael@0: PORT_SetError (SEC_ERROR_INVALID_ARGS);
michael@0: return SECFailure;
michael@0: }
michael@0:
michael@0: request = CERT_DecodeOCSPRequest (data);
michael@0: if (request == NULL || request->tbsRequest == NULL)
michael@0: return SECFailure;
michael@0:
michael@0: tbsRequest = request->tbsRequest;
michael@0:
michael@0: fprintf (out_file, "TBS Request:\n");
michael@0: level++;
michael@0:
michael@0: print_ocsp_version (out_file, &(tbsRequest->version), level);
michael@0:
michael@0: /*
michael@0: * XXX Probably should be an interface to get the signer name
michael@0: * without looking inside the tbsRequest at all.
michael@0: */
michael@0: if (tbsRequest->requestorName != NULL) {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "XXX print the requestorName\n");
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "No Requestor Name.\n");
michael@0: }
michael@0:
michael@0: if (tbsRequest->requestList != NULL) {
michael@0: int i;
michael@0:
michael@0: for (i = 0; tbsRequest->requestList[i] != NULL; i++) {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Request %d:\n", i);
michael@0: print_single_request (out_file, tbsRequest->requestList[i],
michael@0: level + 1);
michael@0: }
michael@0: } else {
michael@0: fprintf (out_file, "Request list is empty.\n");
michael@0: }
michael@0:
michael@0: print_ocsp_extensions (out_file, tbsRequest->requestExtensions,
michael@0: "Request Extensions", level);
michael@0:
michael@0: if (request->optionalSignature != NULL) {
michael@0: ocspSignature *whole_sig;
michael@0: SECItem rawsig;
michael@0:
michael@0: fprintf (out_file, "Signature:\n");
michael@0:
michael@0: whole_sig = request->optionalSignature;
michael@0: SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm),
michael@0: "Signature Algorithm", level);
michael@0:
michael@0: rawsig = whole_sig->signature;
michael@0: DER_ConvertBitString (&rawsig);
michael@0: SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
michael@0:
michael@0: print_raw_certificates (out_file, whole_sig->derCerts, level);
michael@0:
michael@0: fprintf (out_file, "XXX verify the sig and print result\n");
michael@0: } else {
michael@0: fprintf (out_file, "No Signature\n");
michael@0: }
michael@0:
michael@0: CERT_DestroyOCSPRequest (request);
michael@0: return SECSuccess;
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_revoked_info (FILE *out_file, ocspRevokedInfo *revoked_info, int level)
michael@0: {
michael@0: SECU_PrintGeneralizedTime (out_file, &(revoked_info->revocationTime),
michael@0: "Revocation Time", level);
michael@0:
michael@0: if (revoked_info->revocationReason != NULL) {
michael@0: SECU_PrintAsHex (out_file, revoked_info->revocationReason,
michael@0: "Revocation Reason", level);
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "No Revocation Reason.\n");
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_cert_status (FILE *out_file, ocspCertStatus *status, int level)
michael@0: {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Status: ");
michael@0:
michael@0: switch (status->certStatusType) {
michael@0: case ocspCertStatus_good:
michael@0: fprintf (out_file, "Cert is good.\n");
michael@0: break;
michael@0: case ocspCertStatus_revoked:
michael@0: fprintf (out_file, "Cert has been revoked.\n");
michael@0: print_revoked_info (out_file, status->certStatusInfo.revokedInfo,
michael@0: level + 1);
michael@0: break;
michael@0: case ocspCertStatus_unknown:
michael@0: fprintf (out_file, "Cert is unknown to responder.\n");
michael@0: break;
michael@0: default:
michael@0: fprintf (out_file, "Unrecognized status.\n");
michael@0: break;
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_single_response (FILE *out_file, CERTOCSPSingleResponse *single,
michael@0: int level)
michael@0: {
michael@0: print_ocsp_cert_id (out_file, single->certID, level);
michael@0:
michael@0: print_cert_status (out_file, single->certStatus, level);
michael@0:
michael@0: SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate),
michael@0: "This Update", level);
michael@0:
michael@0: if (single->nextUpdate != NULL) {
michael@0: SECU_PrintGeneralizedTime (out_file, single->nextUpdate,
michael@0: "Next Update", level);
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "No Next Update\n");
michael@0: }
michael@0:
michael@0: print_ocsp_extensions (out_file, single->singleExtensions,
michael@0: "Single Response Extensions", level);
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_responder_id (FILE *out_file, ocspResponderID *responderID, int level)
michael@0: {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Responder ID ");
michael@0:
michael@0: switch (responderID->responderIDType) {
michael@0: case ocspResponderID_byName:
michael@0: fprintf (out_file, "(byName):\n");
michael@0: SECU_PrintName (out_file, &(responderID->responderIDValue.name),
michael@0: "Name", level + 1);
michael@0: break;
michael@0: case ocspResponderID_byKey:
michael@0: fprintf (out_file, "(byKey):\n");
michael@0: SECU_PrintAsHex (out_file, &(responderID->responderIDValue.keyHash),
michael@0: "Key Hash", level + 1);
michael@0: break;
michael@0: default:
michael@0: fprintf (out_file, "Unrecognized Responder ID Type\n");
michael@0: break;
michael@0: }
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_response_data (FILE *out_file, ocspResponseData *responseData, int level)
michael@0: {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Response Data:\n");
michael@0: level++;
michael@0:
michael@0: print_ocsp_version (out_file, &(responseData->version), level);
michael@0:
michael@0: print_responder_id (out_file, responseData->responderID, level);
michael@0:
michael@0: SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt),
michael@0: "Produced At", level);
michael@0:
michael@0: if (responseData->responses != NULL) {
michael@0: int i;
michael@0:
michael@0: for (i = 0; responseData->responses[i] != NULL; i++) {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Response %d:\n", i);
michael@0: print_single_response (out_file, responseData->responses[i],
michael@0: level + 1);
michael@0: }
michael@0: } else {
michael@0: fprintf (out_file, "Response list is empty.\n");
michael@0: }
michael@0:
michael@0: print_ocsp_extensions (out_file, responseData->responseExtensions,
michael@0: "Response Extensions", level);
michael@0: }
michael@0:
michael@0:
michael@0: static void
michael@0: print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level)
michael@0: {
michael@0: SECItem rawsig;
michael@0:
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Basic OCSP Response:\n");
michael@0: level++;
michael@0:
michael@0: print_response_data (out_file, basic->tbsResponseData, level);
michael@0:
michael@0: SECU_PrintAlgorithmID (out_file,
michael@0: &(basic->responseSignature.signatureAlgorithm),
michael@0: "Signature Algorithm", level);
michael@0:
michael@0: rawsig = basic->responseSignature.signature;
michael@0: DER_ConvertBitString (&rawsig);
michael@0: SECU_PrintAsHex (out_file, &rawsig, "Signature", level);
michael@0:
michael@0: print_raw_certificates (out_file, basic->responseSignature.derCerts, level);
michael@0: }
michael@0:
michael@0:
michael@0: /*
michael@0: * Note this must match (exactly) the enumeration ocspResponseStatus.
michael@0: */
michael@0: static char *responseStatusNames[] = {
michael@0: "successful (Response has valid confirmations)",
michael@0: "malformedRequest (Illegal confirmation request)",
michael@0: "internalError (Internal error in issuer)",
michael@0: "tryLater (Try again later)",
michael@0: "unused ((4) is not used)",
michael@0: "sigRequired (Must sign the request)",
michael@0: "unauthorized (Request unauthorized)"
michael@0: };
michael@0:
michael@0: /*
michael@0: * Decode the DER/BER-encoded item "data" as an OCSP response
michael@0: * and pretty-print the subfields.
michael@0: */
michael@0: static SECStatus
michael@0: print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle)
michael@0: {
michael@0: CERTOCSPResponse *response;
michael@0: int level = 0;
michael@0:
michael@0: PORT_Assert (out_file != NULL);
michael@0: PORT_Assert (data != NULL);
michael@0: if (out_file == NULL || data == NULL) {
michael@0: PORT_SetError (SEC_ERROR_INVALID_ARGS);
michael@0: return SECFailure;
michael@0: }
michael@0:
michael@0: response = CERT_DecodeOCSPResponse (data);
michael@0: if (response == NULL)
michael@0: return SECFailure;
michael@0:
michael@0: if (response->statusValue >= ocspResponse_min &&
michael@0: response->statusValue <= ocspResponse_max) {
michael@0: fprintf (out_file, "Response Status: %s\n",
michael@0: responseStatusNames[response->statusValue]);
michael@0: } else {
michael@0: fprintf (out_file,
michael@0: "Response Status: other (Status value %d out of defined range)\n",
michael@0: (int)response->statusValue);
michael@0: }
michael@0:
michael@0: if (response->statusValue == ocspResponse_successful) {
michael@0: ocspResponseBytes *responseBytes = response->responseBytes;
michael@0: SECStatus sigStatus;
michael@0: CERTCertificate *signerCert = NULL;
michael@0:
michael@0: PORT_Assert (responseBytes != NULL);
michael@0:
michael@0: level++;
michael@0: fprintf (out_file, "Response Bytes:\n");
michael@0: SECU_PrintObjectID (out_file, &(responseBytes->responseType),
michael@0: "Response Type", level);
michael@0: switch (response->responseBytes->responseTypeTag) {
michael@0: case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
michael@0: print_basic_response (out_file,
michael@0: responseBytes->decodedResponse.basic,
michael@0: level);
michael@0: break;
michael@0: default:
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Unknown response syntax\n");
michael@0: break;
michael@0: }
michael@0:
michael@0: sigStatus = CERT_VerifyOCSPResponseSignature (response, handle,
michael@0: NULL, &signerCert, NULL);
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Signature verification ");
michael@0: if (sigStatus != SECSuccess) {
michael@0: fprintf (out_file, "failed: %s\n", SECU_Strerror (PORT_GetError()));
michael@0: } else {
michael@0: fprintf (out_file, "succeeded.\n");
michael@0: if (signerCert != NULL) {
michael@0: SECU_PrintName (out_file, &signerCert->subject, "Signer",
michael@0: level);
michael@0: CERT_DestroyCertificate (signerCert);
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "No signer cert returned?\n");
michael@0: }
michael@0: }
michael@0: } else {
michael@0: SECU_Indent (out_file, level);
michael@0: fprintf (out_file, "Unsuccessful response, no more information.\n");
michael@0: }
michael@0:
michael@0: CERT_DestroyOCSPResponse (response);
michael@0: return SECSuccess;
michael@0: }
michael@0:
michael@0: #endif /* NO_PP */
michael@0:
michael@0:
michael@0: static SECStatus
michael@0: cert_usage_from_char (const char *cert_usage_str, SECCertUsage *cert_usage)
michael@0: {
michael@0: PORT_Assert (cert_usage_str != NULL);
michael@0: PORT_Assert (cert_usage != NULL);
michael@0:
michael@0: if (PORT_Strlen (cert_usage_str) != 1)
michael@0: return SECFailure;
michael@0:
michael@0: switch (*cert_usage_str) {
michael@0: case 'c':
michael@0: *cert_usage = certUsageSSLClient;
michael@0: break;
michael@0: case 's':
michael@0: *cert_usage = certUsageSSLServer;
michael@0: break;
michael@0: case 'e':
michael@0: *cert_usage = certUsageEmailRecipient;
michael@0: break;
michael@0: case 'E':
michael@0: *cert_usage = certUsageEmailSigner;
michael@0: break;
michael@0: case 'S':
michael@0: *cert_usage = certUsageObjectSigner;
michael@0: break;
michael@0: case 'C':
michael@0: *cert_usage = certUsageVerifyCA;
michael@0: break;
michael@0: default:
michael@0: return SECFailure;
michael@0: }
michael@0:
michael@0: return SECSuccess;
michael@0: }
michael@0:
michael@0:
michael@0: int
michael@0: main (int argc, char **argv)
michael@0: {
michael@0: int retval;
michael@0: PRFileDesc *in_file;
michael@0: FILE *out_file; /* not PRFileDesc until SECU accepts it */
michael@0: int crequest, dresponse;
michael@0: int prequest, presponse;
michael@0: int ccert, vcert;
michael@0: const char *db_dir, *date_str, *cert_usage_str, *name;
michael@0: const char *responder_name, *responder_url, *signer_name;
michael@0: PRBool add_acceptable_responses, add_service_locator;
michael@0: SECItem *data = NULL;
michael@0: PLOptState *optstate;
michael@0: SECStatus rv;
michael@0: CERTCertDBHandle *handle = NULL;
michael@0: SECCertUsage cert_usage;
michael@0: PRTime verify_time;
michael@0: CERTCertificate *cert = NULL;
michael@0: PRBool ascii = PR_FALSE;
michael@0:
michael@0: retval = -1; /* what we return/exit with on error */
michael@0:
michael@0: program_name = PL_strrchr(argv[0], '/');
michael@0: program_name = program_name ? (program_name + 1) : argv[0];
michael@0:
michael@0: in_file = PR_STDIN;
michael@0: out_file = stdout;
michael@0:
michael@0: crequest = 0;
michael@0: dresponse = 0;
michael@0: prequest = 0;
michael@0: presponse = 0;
michael@0: ccert = 0;
michael@0: vcert = 0;
michael@0:
michael@0: db_dir = NULL;
michael@0: date_str = NULL;
michael@0: cert_usage_str = NULL;
michael@0: name = NULL;
michael@0: responder_name = NULL;
michael@0: responder_url = NULL;
michael@0: signer_name = NULL;
michael@0:
michael@0: add_acceptable_responses = PR_FALSE;
michael@0: add_service_locator = PR_FALSE;
michael@0:
michael@0: optstate = PL_CreateOptState (argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:");
michael@0: if (optstate == NULL) {
michael@0: SECU_PrintError (program_name, "PL_CreateOptState failed");
michael@0: return retval;
michael@0: }
michael@0:
michael@0: while (PL_GetNextOpt (optstate) == PL_OPT_OK) {
michael@0: switch (optstate->option) {
michael@0: case '?':
michael@0: short_usage (program_name);
michael@0: return retval;
michael@0:
michael@0: case 'A':
michael@0: add_acceptable_responses = PR_TRUE;
michael@0: break;
michael@0:
michael@0: case 'H':
michael@0: long_usage (program_name);
michael@0: return retval;
michael@0:
michael@0: case 'L':
michael@0: add_service_locator = PR_TRUE;
michael@0: break;
michael@0:
michael@0: case 'P':
michael@0: presponse = 1;
michael@0: break;
michael@0:
michael@0: case 'R':
michael@0: dresponse = 1;
michael@0: name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'S':
michael@0: ccert = 1;
michael@0: name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'V':
michael@0: vcert = 1;
michael@0: name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'a':
michael@0: ascii = PR_TRUE;
michael@0: break;
michael@0:
michael@0: case 'd':
michael@0: db_dir = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'l':
michael@0: responder_url = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'p':
michael@0: prequest = 1;
michael@0: break;
michael@0:
michael@0: case 'r':
michael@0: crequest = 1;
michael@0: name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 's':
michael@0: signer_name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 't':
michael@0: responder_name = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'u':
michael@0: cert_usage_str = optstate->value;
michael@0: break;
michael@0:
michael@0: case 'w':
michael@0: date_str = optstate->value;
michael@0: break;
michael@0: }
michael@0: }
michael@0:
michael@0: PL_DestroyOptState(optstate);
michael@0:
michael@0: if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) {
michael@0: PR_fprintf (PR_STDERR, "%s: must specify exactly one command\n\n",
michael@0: program_name);
michael@0: short_usage (program_name);
michael@0: return retval;
michael@0: }
michael@0:
michael@0: if (vcert) {
michael@0: if (cert_usage_str == NULL) {
michael@0: PR_fprintf (PR_STDERR, "%s: verification requires cert usage\n\n",
michael@0: program_name);
michael@0: short_usage (program_name);
michael@0: return retval;
michael@0: }
michael@0:
michael@0: rv = cert_usage_from_char (cert_usage_str, &cert_usage);
michael@0: if (rv != SECSuccess) {
michael@0: PR_fprintf (PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n",
michael@0: program_name, cert_usage_str);
michael@0: long_usage (program_name);
michael@0: return retval;
michael@0: }
michael@0: }
michael@0:
michael@0: if (ccert + vcert) {
michael@0: if (responder_url != NULL || responder_name != NULL) {
michael@0: /*
michael@0: * To do a full status check, both the URL and the cert name
michael@0: * of the responder must be specified if either one is.
michael@0: */
michael@0: if (responder_url == NULL || responder_name == NULL) {
michael@0: if (responder_url == NULL)
michael@0: PR_fprintf (PR_STDERR,
michael@0: "%s: must also specify responder location\n\n",
michael@0: program_name);
michael@0: else
michael@0: PR_fprintf (PR_STDERR,
michael@0: "%s: must also specify responder name\n\n",
michael@0: program_name);
michael@0: short_usage (program_name);
michael@0: return retval;
michael@0: }
michael@0: }
michael@0:
michael@0: if (date_str != NULL) {
michael@0: rv = DER_AsciiToTime (&verify_time, (char *) date_str);
michael@0: if (rv != SECSuccess) {
michael@0: SECU_PrintError (program_name, "error converting time string");
michael@0: PR_fprintf (PR_STDERR, "\n");
michael@0: long_usage (program_name);
michael@0: return retval;
michael@0: }
michael@0: } else {
michael@0: verify_time = PR_Now();
michael@0: }
michael@0: }
michael@0:
michael@0: retval = -2; /* errors change from usage to runtime */
michael@0:
michael@0: /*
michael@0: * Initialize the NSPR and Security libraries.
michael@0: */
michael@0: PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
michael@0: db_dir = SECU_ConfigDirectory (db_dir);
michael@0: rv = NSS_Init (db_dir);
michael@0: if (rv != SECSuccess) {
michael@0: SECU_PrintError (program_name, "NSS_Init failed");
michael@0: goto prdone;
michael@0: }
michael@0: SECU_RegisterDynamicOids();
michael@0:
michael@0: if (prequest + presponse) {
michael@0: MAKE_FILE_BINARY(stdin);
michael@0: data = read_file_into_item (in_file, siBuffer);
michael@0: if (data == NULL) {
michael@0: SECU_PrintError (program_name, "problem reading input");
michael@0: goto nssdone;
michael@0: }
michael@0: }
michael@0:
michael@0: if (crequest + dresponse + presponse + ccert + vcert) {
michael@0: handle = CERT_GetDefaultCertDB();
michael@0: if (handle == NULL) {
michael@0: SECU_PrintError (program_name, "problem getting certdb handle");
michael@0: goto nssdone;
michael@0: }
michael@0:
michael@0: /*
michael@0: * It would be fine to do the enable for all of these commands,
michael@0: * but this way we check that everything but an overall verify
michael@0: * can be done without it. That is, that the individual pieces
michael@0: * work on their own.
michael@0: */
michael@0: if (vcert) {
michael@0: rv = CERT_EnableOCSPChecking (handle);
michael@0: if (rv != SECSuccess) {
michael@0: SECU_PrintError (program_name, "error enabling OCSP checking");
michael@0: goto nssdone;
michael@0: }
michael@0: }
michael@0:
michael@0: if ((ccert + vcert) && (responder_name != NULL)) {
michael@0: rv = CERT_SetOCSPDefaultResponder (handle, responder_url,
michael@0: responder_name);
michael@0: if (rv != SECSuccess) {
michael@0: SECU_PrintError (program_name,
michael@0: "error setting default responder");
michael@0: goto nssdone;
michael@0: }
michael@0:
michael@0: rv = CERT_EnableOCSPDefaultResponder (handle);
michael@0: if (rv != SECSuccess) {
michael@0: SECU_PrintError (program_name,
michael@0: "error enabling default responder");
michael@0: goto nssdone;
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: #define NOTYET(opt) \
michael@0: { \
michael@0: PR_fprintf (PR_STDERR, "%s not yet working\n", opt); \
michael@0: exit (-1); \
michael@0: }
michael@0:
michael@0: if (name) {
michael@0: cert = find_certificate(handle, name, ascii);
michael@0: }
michael@0:
michael@0: if (crequest) {
michael@0: if (signer_name != NULL) {
michael@0: NOTYET("-s");
michael@0: }
michael@0: rv = create_request (out_file, handle, cert, add_service_locator,
michael@0: add_acceptable_responses);
michael@0: } else if (dresponse) {
michael@0: if (signer_name != NULL) {
michael@0: NOTYET("-s");
michael@0: }
michael@0: rv = dump_response (out_file, handle, cert, responder_url);
michael@0: } else if (prequest) {
michael@0: rv = print_request (out_file, data);
michael@0: } else if (presponse) {
michael@0: rv = print_response (out_file, data, handle);
michael@0: } else if (ccert) {
michael@0: if (signer_name != NULL) {
michael@0: NOTYET("-s");
michael@0: }
michael@0: rv = get_cert_status (out_file, handle, cert, name, verify_time);
michael@0: } else if (vcert) {
michael@0: if (signer_name != NULL) {
michael@0: NOTYET("-s");
michael@0: }
michael@0: rv = verify_cert (out_file, handle, cert, name, cert_usage, verify_time);
michael@0: }
michael@0:
michael@0: if (rv != SECSuccess)
michael@0: SECU_PrintError (program_name, "error performing requested operation");
michael@0: else
michael@0: retval = 0;
michael@0:
michael@0: nssdone:
michael@0: if (cert) {
michael@0: CERT_DestroyCertificate(cert);
michael@0: }
michael@0:
michael@0: if (data != NULL) {
michael@0: SECITEM_FreeItem (data, PR_TRUE);
michael@0: }
michael@0:
michael@0: if (handle != NULL) {
michael@0: CERT_DisableOCSPDefaultResponder(handle);
michael@0: CERT_DisableOCSPChecking (handle);
michael@0: }
michael@0:
michael@0: if (NSS_Shutdown () != SECSuccess) {
michael@0: retval = 1;
michael@0: }
michael@0:
michael@0: prdone:
michael@0: PR_Cleanup ();
michael@0: return retval;
michael@0: }