diff options
author | relyea%netscape.com <devnull@localhost> | 2000-03-31 20:13:40 +0000 |
---|---|---|
committer | relyea%netscape.com <devnull@localhost> | 2000-03-31 20:13:40 +0000 |
commit | 9502869e82d4f3ce26b292263e1c626dca3a34f3 (patch) | |
tree | 4d0f8ab157505b57c13a5e2bdf979560ab751527 /security/nss/cmd/ocspclnt | |
parent | 222a52dab759085f56dcb6588b69a6a937d82aa2 (diff) | |
download | nss-hg-9502869e82d4f3ce26b292263e1c626dca3a34f3.tar.gz |
Initial NSS Open Source checkin
Diffstat (limited to 'security/nss/cmd/ocspclnt')
-rw-r--r-- | security/nss/cmd/ocspclnt/Makefile | 73 | ||||
-rw-r--r-- | security/nss/cmd/ocspclnt/manifest.mn | 52 | ||||
-rw-r--r-- | security/nss/cmd/ocspclnt/ocspclnt.c | 1220 |
3 files changed, 1345 insertions, 0 deletions
diff --git a/security/nss/cmd/ocspclnt/Makefile b/security/nss/cmd/ocspclnt/Makefile new file mode 100644 index 000000000..6895bd3cd --- /dev/null +++ b/security/nss/cmd/ocspclnt/Makefile @@ -0,0 +1,73 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include $(CORE_DEPTH)/security/cmd/platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + +include $(CORE_DEPTH)/security/cmd/platrules.mk diff --git a/security/nss/cmd/ocspclnt/manifest.mn b/security/nss/cmd/ocspclnt/manifest.mn new file mode 100644 index 000000000..f57a6d385 --- /dev/null +++ b/security/nss/cmd/ocspclnt/manifest.mn @@ -0,0 +1,52 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +CORE_DEPTH = ../../.. + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = security + +CSRCS = \ + ocspclnt.c \ + $(NULL) + +# headers for the MODULE (defined above) are implicitly required. +REQUIRES = dbm seccmd + +# WINNT uses EXTRA_LIBS as the list of libs to link in. +# Unix uses OS_LIBS for that purpose. +# We can solve this via conditional makefile code, but +# can't do this in manifest.mn because OS_ARCH isn't defined there. +# So, look in the local Makefile for the defines for the list of libs. + +PROGRAM = ocspclnt diff --git a/security/nss/cmd/ocspclnt/ocspclnt.c b/security/nss/cmd/ocspclnt/ocspclnt.c new file mode 100644 index 000000000..c26633fbe --- /dev/null +++ b/security/nss/cmd/ocspclnt/ocspclnt.c @@ -0,0 +1,1220 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * Test program for client-side OCSP. + * + * $Id$ + */ + +#include "secutil.h" +#include "nspr.h" +#include "plgetopt.h" +#include "nss.h" +#include "cert.h" +#include "ocsp.h" +#include "xconst.h" /* + * XXX internal header file; needed to get at + * cert_DecodeAuthInfoAccessExtension -- would be + * nice to not need this, but that would require + * better/different APIs. + */ + +#ifndef NO_PP /* + * Compile with this every once in a while to be + * sure that no dependencies on it get added + * outside of the pretty-printing routines. + */ +#include "ocspti.h" /* internals for pretty-printing routines *only* */ +#endif /* NO_PP */ + +#define DEFAULT_DB_DIR "~/.netscape" + + +static void +synopsis (char *program_name) +{ + PRFileDesc *pr_stderr; + + pr_stderr = PR_STDERR; + PR_fprintf (pr_stderr, "Usage:"); + PR_fprintf (pr_stderr, + "\t%s -p [-d <dir>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t%s -P [-d <dir>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t%s -r <name> [-L] [-s <name>] [-d <dir>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t%s -R <name> [-l <location>] [-s <name>] [-d <dir>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t%s -S <name> [-l <location> -t <name>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t\t [-s <name>] [-w <time>] [-d <dir>]\n"); + PR_fprintf (pr_stderr, + "\t%s -V <name> -u <usage> [-l <location> -t <name>]\n", + program_name); + PR_fprintf (pr_stderr, + "\t\t [-s <name>] [-w <time>] [-d <dir>]\n"); +} + + +static void +short_usage (char *program_name) +{ + PR_fprintf (PR_STDERR, + "Type %s -H for more detailed descriptions\n", + program_name); + synopsis (program_name); +} + + +static void +long_usage (char *program_name) +{ + PRFileDesc *pr_stderr; + + pr_stderr = PR_STDERR; + synopsis (program_name); + PR_fprintf (pr_stderr, "\nCommands (must specify exactly one):\n"); + PR_fprintf (pr_stderr, + " %-13s Pretty-print a binary request read from stdin\n", + "-p"); + PR_fprintf (pr_stderr, + " %-13s Pretty-print a binary response read from stdin\n", + "-P"); + PR_fprintf (pr_stderr, + " %-13s Create a request for cert \"nickname\" on stdout\n", + "-r nickname"); + PR_fprintf (pr_stderr, + " %-13s Get response for cert \"nickname\", dump to stdout\n", + "-R nickname"); + PR_fprintf (pr_stderr, + " %-13s Get status for cert \"nickname\"\n", + "-S nickname"); + PR_fprintf (pr_stderr, + " %-13s Fully verify cert \"nickname\", w/ status check\n", + "-V nickname"); + PR_fprintf (pr_stderr, "Options:\n"); + PR_fprintf (pr_stderr, + " %-13s Add the service locator extension to the request\n", + "-L"); + PR_fprintf (pr_stderr, + " %-13s Find security databases in \"dbdir\" (default %s)\n", + "-d dbdir", DEFAULT_DB_DIR); + PR_fprintf (pr_stderr, + " %-13s Use \"location\" as URL of responder\n", + "-l location"); + PR_fprintf (pr_stderr, + " %-13s Trust cert \"nickname\" as response signer\n", + "-t nickname"); + PR_fprintf (pr_stderr, + " %-13s Sign requests with cert \"nickname\"\n", + "-s nickname"); + PR_fprintf (pr_stderr, + " %-13s Type of certificate usage for verification:\n", + "-u usage"); + PR_fprintf (pr_stderr, + "%-17s c SSL Client\n", ""); + PR_fprintf (pr_stderr, + "%-17s s SSL Server\n", ""); + PR_fprintf (pr_stderr, + "%-17s e Email Recipient\n", ""); + PR_fprintf (pr_stderr, + "%-17s E Email Signer\n", ""); + PR_fprintf (pr_stderr, + "%-17s S Object Signer\n", ""); + PR_fprintf (pr_stderr, + "%-17s C CA\n", ""); + PR_fprintf (pr_stderr, + " %-13s Validity time (default current time), one of:\n", + "-w time"); + PR_fprintf (pr_stderr, + "%-17s %-25s (GMT)\n", "", "YYMMDDhhmm[ss]Z"); + PR_fprintf (pr_stderr, + "%-17s %-25s (later than GMT)\n", "", "YYMMDDhhmm[ss]+hhmm"); + PR_fprintf (pr_stderr, + "%-17s %-25s (earlier than GMT)\n", "", "YYMMDDhhmm[ss]-hhmm"); +} + + +/* + * XXX This is a generic function that would probably make a good + * replacement for SECU_DER_Read (which is not at all specific to DER, + * despite its name), but that requires fixing all of the tools... + * Still, it should be done, whenenver I/somebody has the time. + * (Also, consider whether this actually belongs in the security + * library itself, not just in the command library.) + * + * This function takes an open file (a PRFileDesc *) and reads the + * entire file into a SECItem. (Obviously, the file is intended to + * be small enough that such a thing is advisable.) Both the SECItem + * and the buffer it points to are allocated from the heap; the caller + * is expected to free them. ("SECITEM_FreeItem(item, PR_TRUE)") + */ +static SECItem * +read_file_into_item (PRFileDesc *in_file, SECItemType si_type) +{ + PRStatus prv; + SECItem *item; + PRFileInfo file_info; + PRInt32 bytes_read; + + prv = PR_GetOpenFileInfo (in_file, &file_info); + if (prv != PR_SUCCESS) + return NULL; + + if (file_info.size == 0) { + /* XXX Need a better error; just grabbed this one for expediency. */ + PORT_SetError (SEC_ERROR_INPUT_LEN); + return NULL; + } + + if (file_info.size > 0xffff) { /* I think this is too big. */ + PORT_SetError (SEC_ERROR_NO_MEMORY); + return NULL; + } + + item = PORT_Alloc (sizeof (SECItem)); + if (item == NULL) + return NULL; + + item->type = si_type; + item->len = (unsigned int) file_info.size; + item->data = PORT_Alloc ((size_t)item->len); + if (item->data == NULL) + goto loser; + + bytes_read = PR_Read (in_file, item->data, (PRInt32) item->len); + if (bytes_read < 0) { + /* Something went wrong; error is already set for us. */ + goto loser; + } else if (bytes_read == 0) { + /* Something went wrong; we read nothing. But no system/nspr error. */ + /* XXX Need to set an error here. */ + goto loser; + } else if (item->len != (unsigned int)bytes_read) { + /* Something went wrong; we read less (or more!?) than we expected. */ + /* XXX Need to set an error here. */ + goto loser; + } + + return item; + +loser: + SECITEM_FreeItem (item, PR_TRUE); + return NULL; +} + + +/* + * Create a DER-encoded OCSP request (for the certificate whose nickname + * is "name") and dump it out. + */ +static SECStatus +create_request (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name, + PRBool add_service_locator, PRBool add_acceptable_responses) +{ + CERTCertificate *cert = NULL; + CERTCertList *certs = NULL; + CERTOCSPRequest *request = NULL; + int64 now = PR_Now(); + SECItem *encoding = NULL; + SECStatus rv = SECFailure; + + if (handle == NULL || cert_name == NULL) + goto loser; + + cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name); + if (cert == NULL) + goto loser; + + /* + * We need to create a list of one. + */ + certs = CERT_NewCertList(); + if (certs == NULL) + goto loser; + + if (CERT_AddCertToListTail (certs, cert) != SECSuccess) + goto loser; + + /* + * Now that cert is included in the list, we need to be careful + * that we do not try to destroy it twice. This will prevent that. + */ + cert = NULL; + + request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL); + if (request == NULL) + goto loser; + + if (add_acceptable_responses) { + rv = CERT_AddOCSPAcceptableResponses(request, + SEC_OID_PKIX_OCSP_BASIC_RESPONSE); + if (rv != SECSuccess) + goto loser; + } + + encoding = CERT_EncodeOCSPRequest (NULL, request, NULL); + if (encoding == NULL) + goto loser; + + if (fwrite (encoding->data, encoding->len, 1, out_file) != 1) + goto loser; + + rv = SECSuccess; + +loser: + if (encoding != NULL) + SECITEM_FreeItem(encoding, PR_TRUE); + if (request != NULL) + CERT_DestroyOCSPRequest(request); + if (certs != NULL) + CERT_DestroyCertList (certs); + if (cert != NULL) + CERT_DestroyCertificate (cert); + + return rv; +} + + +/* + * Create a DER-encoded OCSP request (for the certificate whose nickname is + * "cert_name"), then get and dump a corresponding response. The responder + * location is either specified explicitly (as "responder_url") or found + * via the AuthorityInfoAccess URL in the cert. + */ +static SECStatus +dump_response (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name, + const char *responder_url) +{ + CERTCertificate *cert = NULL; + CERTCertList *certs = NULL; + char *loc = NULL; + int64 now = PR_Now(); + SECItem *response = NULL; + SECStatus rv = SECFailure; + PRBool includeServiceLocator; + + if (handle == NULL || cert_name == NULL) + goto loser; + + cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name); + if (cert == NULL) + goto loser; + + if (responder_url != NULL) { + loc = (char *) responder_url; + includeServiceLocator = PR_TRUE; + } else { + loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert); + if (loc == NULL) + goto loser; + includeServiceLocator = PR_FALSE; + } + + /* + * We need to create a list of one. + */ + certs = CERT_NewCertList(); + if (certs == NULL) + goto loser; + + if (CERT_AddCertToListTail (certs, cert) != SECSuccess) + goto loser; + + /* + * Now that cert is included in the list, we need to be careful + * that we do not try to destroy it twice. This will prevent that. + */ + cert = NULL; + + response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now, + includeServiceLocator, + NULL, NULL, NULL); + if (response == NULL) + goto loser; + + if (fwrite (response->data, response->len, 1, out_file) != 1) + goto loser; + + rv = SECSuccess; + +loser: + if (response != NULL) + SECITEM_FreeItem (response, PR_TRUE); + if (certs != NULL) + CERT_DestroyCertList (certs); + if (loc != NULL && loc != responder_url) + PORT_Free (loc); + if (cert != NULL) + CERT_DestroyCertificate (cert); + + return rv; +} + + +/* + * Get the status for the specified certificate (whose nickname is "cert_name"). + * Directly use the OCSP function rather than doing a full verification. + */ +static SECStatus +get_cert_status (FILE *out_file, CERTCertDBHandle *handle, + const char *cert_name, int64 verify_time) +{ + CERTCertificate *cert = NULL; + SECStatus rv = SECFailure; + + if (handle == NULL || cert_name == NULL) + goto loser; + + cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name); + if (cert == NULL) + goto loser; + + rv = CERT_CheckOCSPStatus (handle, cert, verify_time, NULL); + + fprintf (out_file, "Check of certificate \"%s\" ", cert_name); + if (rv == SECSuccess) { + fprintf (out_file, "succeeded.\n"); + } else { + const char *error_string = SECU_Strerror(PORT_GetError()); + fprintf (out_file, "failed. Reason:\n"); + if (error_string != NULL && PORT_Strlen(error_string) > 0) + fprintf (out_file, "%s\n", error_string); + else + fprintf (out_file, "Unknown\n"); + } + + rv = SECSuccess; + +loser: + if (cert != NULL) + CERT_DestroyCertificate (cert); + + return rv; +} + + +/* + * Verify the specified certificate (whose nickname is "cert_name"). + * OCSP is already turned on, so we just need to call the standard + * certificate verification API and let it do all the work. + */ +static SECStatus +verify_cert (FILE *out_file, CERTCertDBHandle *handle, const char *cert_name, + SECCertUsage cert_usage, int64 verify_time) +{ + CERTCertificate *cert = NULL; + SECStatus rv = SECFailure; + + if (handle == NULL || cert_name == NULL) + goto loser; + + cert = CERT_FindCertByNicknameOrEmailAddr (handle, (char *) cert_name); + if (cert == NULL) + goto loser; + + rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time, + NULL, NULL); + + fprintf (out_file, "Verification of certificate \"%s\" ", cert_name); + if (rv == SECSuccess) { + fprintf (out_file, "succeeded.\n"); + } else { + const char *error_string = SECU_Strerror(PORT_GetError()); + fprintf (out_file, "failed. Reason:\n"); + if (error_string != NULL && PORT_Strlen(error_string) > 0) + fprintf (out_file, "%s\n", error_string); + else + fprintf (out_file, "Unknown\n"); + } + + rv = SECSuccess; + +loser: + if (cert != NULL) + CERT_DestroyCertificate (cert); + + return rv; +} + + +#ifdef NO_PP + +static SECStatus +print_request (FILE *out_file, SECItem *data) +{ + fprintf (out_file, "Cannot pretty-print request compiled with NO_PP.\n"); + return SECSuccess; +} + +static SECStatus +print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle) +{ + fprintf (out_file, "Cannot pretty-print response compiled with NO_PP.\n"); + return SECSuccess; +} + +#else /* NO_PP */ + +static void +print_ocsp_version (FILE *out_file, SECItem *version, int level) +{ + if (version->len > 0) { + SECU_PrintInteger (out_file, version, "Version", level); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "Version: DEFAULT\n"); + } +} + + +static void +print_ocsp_cert_id (FILE *out_file, CERTOCSPCertID *cert_id, int level) +{ + SECU_Indent (out_file, level); + fprintf (out_file, "Cert ID:\n"); + level++; + + SECU_PrintAlgorithmID (out_file, &(cert_id->hashAlgorithm), + "Hash Algorithm", level); + SECU_PrintAsHex (out_file, &(cert_id->issuerNameHash), + "Issuer Name Hash", level); + SECU_PrintAsHex (out_file, &(cert_id->issuerKeyHash), + "Issuer Key Hash", level); + SECU_PrintInteger (out_file, &(cert_id->serialNumber), + "Serial Number", level); + /* XXX lookup the cert; if found, print something nice (nickname?) */ +} + + +static void +print_raw_certificates (FILE *out_file, SECItem **raw_certs, int level) +{ + SECItem *raw_cert; + int i = 0; + char cert_label[50]; + + SECU_Indent (out_file, level); + + if (raw_certs == NULL) { + fprintf (out_file, "No Certificates.\n"); + return; + } + + fprintf (out_file, "Certificate List:\n"); + while ((raw_cert = raw_certs[i++]) != NULL) { + sprintf (cert_label, "Certificate (%d)", i); + (void) SECU_PrintSignedData (out_file, raw_cert, cert_label, level + 1, + SECU_PrintCertificate); + } +} + + +static void +print_ocsp_extensions (FILE *out_file, CERTCertExtension **extensions, + char *msg, int level) +{ + if (extensions) { + SECU_PrintExtensions (out_file, extensions, msg, level); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "No %s\n", msg); + } +} + + +static void +print_single_request (FILE *out_file, ocspSingleRequest *single, int level) +{ + print_ocsp_cert_id (out_file, single->reqCert, level); + print_ocsp_extensions (out_file, single->singleRequestExtensions, + "Single Request Extensions", level); +} + + +/* + * Decode the DER/BER-encoded item "data" as an OCSP request + * and pretty-print the subfields. + */ +static SECStatus +print_request (FILE *out_file, SECItem *data) +{ + CERTOCSPRequest *request; + ocspTBSRequest *tbsRequest; + int level = 0; + + PORT_Assert (out_file != NULL); + PORT_Assert (data != NULL); + if (out_file == NULL || data == NULL) { + PORT_SetError (SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + request = CERT_DecodeOCSPRequest (data); + if (request == NULL || request->tbsRequest == NULL) + return SECFailure; + + tbsRequest = request->tbsRequest; + + fprintf (out_file, "TBS Request:\n"); + level++; + + print_ocsp_version (out_file, &(tbsRequest->version), level); + + /* + * XXX Probably should be an interface to get the signer name + * without looking inside the tbsRequest at all. + */ + if (tbsRequest->requestorName != NULL) { + SECU_Indent (out_file, level); + fprintf (out_file, "XXX print the requestorName\n"); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "No Requestor Name.\n"); + } + + if (tbsRequest->requestList != NULL) { + int i; + + for (i = 0; tbsRequest->requestList[i] != NULL; i++) { + SECU_Indent (out_file, level); + fprintf (out_file, "Request %d:\n", i); + print_single_request (out_file, tbsRequest->requestList[i], + level + 1); + } + } else { + fprintf (out_file, "Request list is empty.\n"); + } + + print_ocsp_extensions (out_file, tbsRequest->requestExtensions, + "Request Extensions", level); + + if (request->optionalSignature != NULL) { + ocspSignature *whole_sig; + SECItem rawsig; + + fprintf (out_file, "Signature:\n"); + + whole_sig = request->optionalSignature; + SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm), + "Signature Algorithm", level); + + rawsig = whole_sig->signature; + DER_ConvertBitString (&rawsig); + SECU_PrintAsHex (out_file, &rawsig, "Signature", level); + + print_raw_certificates (out_file, whole_sig->derCerts, level); + + fprintf (out_file, "XXX verify the sig and print result\n"); + } else { + fprintf (out_file, "No Signature\n"); + } + + CERT_DestroyOCSPRequest (request); + return SECSuccess; +} + + +static void +print_revoked_info (FILE *out_file, ocspRevokedInfo *revoked_info, int level) +{ + SECU_PrintGeneralizedTime (out_file, &(revoked_info->revocationTime), + "Revocation Time", level); + + if (revoked_info->revocationReason != NULL) { + SECU_PrintAsHex (out_file, revoked_info->revocationReason, + "Revocation Reason", level); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "No Revocation Reason.\n"); + } +} + + +static void +print_cert_status (FILE *out_file, ocspCertStatus *status, int level) +{ + SECU_Indent (out_file, level); + fprintf (out_file, "Status: "); + + switch (status->certStatusType) { + case ocspCertStatus_good: + fprintf (out_file, "Cert is good.\n"); + break; + case ocspCertStatus_revoked: + fprintf (out_file, "Cert has been revoked.\n"); + print_revoked_info (out_file, status->certStatusInfo.revokedInfo, + level + 1); + break; + case ocspCertStatus_unknown: + fprintf (out_file, "Cert is unknown to responder.\n"); + break; + default: + fprintf (out_file, "Unrecognized status.\n"); + break; + } +} + + +static void +print_single_response (FILE *out_file, CERTOCSPSingleResponse *single, + int level) +{ + print_ocsp_cert_id (out_file, single->certID, level); + + print_cert_status (out_file, single->certStatus, level); + + SECU_PrintGeneralizedTime (out_file, &(single->thisUpdate), + "This Update", level); + + if (single->nextUpdate != NULL) { + SECU_PrintGeneralizedTime (out_file, single->nextUpdate, + "Next Update", level); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "No Next Update\n"); + } + + print_ocsp_extensions (out_file, single->singleExtensions, + "Single Response Extensions", level); +} + + +static void +print_responder_id (FILE *out_file, ocspResponderID *responderID, int level) +{ + SECU_Indent (out_file, level); + fprintf (out_file, "Responder ID "); + + switch (responderID->responderIDType) { + case ocspResponderID_byName: + fprintf (out_file, "(byName):\n"); + SECU_PrintName (out_file, &(responderID->responderIDValue.name), + "Name", level + 1); + break; + case ocspResponderID_byKey: + fprintf (out_file, "(byKey):\n"); + SECU_PrintAsHex (out_file, &(responderID->responderIDValue.keyHash), + "Key Hash", level + 1); + break; + default: + fprintf (out_file, "Unrecognized Responder ID Type\n"); + break; + } +} + + +static void +print_response_data (FILE *out_file, ocspResponseData *responseData, int level) +{ + SECU_Indent (out_file, level); + fprintf (out_file, "Response Data:\n"); + level++; + + print_ocsp_version (out_file, &(responseData->version), level); + + print_responder_id (out_file, responseData->responderID, level); + + SECU_PrintGeneralizedTime (out_file, &(responseData->producedAt), + "Produced At", level); + + if (responseData->responses != NULL) { + int i; + + for (i = 0; responseData->responses[i] != NULL; i++) { + SECU_Indent (out_file, level); + fprintf (out_file, "Response %d:\n", i); + print_single_response (out_file, responseData->responses[i], + level + 1); + } + } else { + fprintf (out_file, "Response list is empty.\n"); + } + + print_ocsp_extensions (out_file, responseData->responseExtensions, + "Response Extensions", level); +} + + +static void +print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level) +{ + SECItem rawsig; + + SECU_Indent (out_file, level); + fprintf (out_file, "Basic OCSP Response:\n"); + level++; + + print_response_data (out_file, basic->tbsResponseData, level); + + SECU_PrintAlgorithmID (out_file, + &(basic->responseSignature.signatureAlgorithm), + "Signature Algorithm", level); + + rawsig = basic->responseSignature.signature; + DER_ConvertBitString (&rawsig); + SECU_PrintAsHex (out_file, &rawsig, "Signature", level); + + print_raw_certificates (out_file, basic->responseSignature.derCerts, level); +} + + +/* + * Note this must match (exactly) the enumeration ocspResponseStatus. + */ +static char *responseStatusNames[] = { + "successful (Response has valid confirmations)", + "malformedRequest (Illegal confirmation request)", + "internalError (Internal error in issuer)", + "tryLater (Try again later)", + "unused ((4) is not used)", + "sigRequired (Must sign the request)", + "unauthorized (Request unauthorized)", + "other (Status value out of defined range)" +}; + +/* + * Decode the DER/BER-encoded item "data" as an OCSP response + * and pretty-print the subfields. + */ +static SECStatus +print_response (FILE *out_file, SECItem *data, CERTCertDBHandle *handle) +{ + CERTOCSPResponse *response; + int level = 0; + + PORT_Assert (out_file != NULL); + PORT_Assert (data != NULL); + if (out_file == NULL || data == NULL) { + PORT_SetError (SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + response = CERT_DecodeOCSPResponse (data); + if (response == NULL) + return SECFailure; + + PORT_Assert (response->statusValue <= ocspResponse_other); + fprintf (out_file, "Response Status: %s\n", + responseStatusNames[response->statusValue]); + + if (response->statusValue == ocspResponse_successful) { + ocspResponseBytes *responseBytes = response->responseBytes; + SECStatus sigStatus; + CERTCertificate *signerCert = NULL; + + PORT_Assert (responseBytes != NULL); + + level++; + fprintf (out_file, "Response Bytes:\n"); + SECU_PrintObjectID (out_file, &(responseBytes->responseType), + "Response Type", level); + switch (response->responseBytes->responseTypeTag) { + case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: + print_basic_response (out_file, + responseBytes->decodedResponse.basic, + level); + break; + default: + SECU_Indent (out_file, level); + fprintf (out_file, "Unknown response syntax\n"); + break; + } + + sigStatus = CERT_VerifyOCSPResponseSignature (response, handle, + NULL, &signerCert); + SECU_Indent (out_file, level); + fprintf (out_file, "Signature verification "); + if (sigStatus != SECSuccess) { + fprintf (out_file, "failed: %s\n", SECU_Strerror (PORT_GetError())); + } else { + fprintf (out_file, "succeeded.\n"); + if (signerCert != NULL) { + SECU_PrintName (out_file, &signerCert->subject, "Signer", + level); + CERT_DestroyCertificate (signerCert); + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "No signer cert returned?\n"); + } + } + } else { + SECU_Indent (out_file, level); + fprintf (out_file, "Unsuccessful response, no more information.\n"); + } + + CERT_DestroyOCSPResponse (response); + return SECSuccess; +} + +#endif /* NO_PP */ + + +static SECStatus +cert_usage_from_char (const char *cert_usage_str, SECCertUsage *cert_usage) +{ + PORT_Assert (cert_usage_str != NULL); + PORT_Assert (cert_usage != NULL); + + if (PORT_Strlen (cert_usage_str) != 1) + return SECFailure; + + switch (*cert_usage_str) { + case 'c': + *cert_usage = certUsageSSLClient; + break; + case 's': + *cert_usage = certUsageSSLServer; + break; + case 'e': + *cert_usage = certUsageEmailRecipient; + break; + case 'E': + *cert_usage = certUsageEmailSigner; + break; + case 'S': + *cert_usage = certUsageObjectSigner; + break; + case 'C': + *cert_usage = certUsageVerifyCA; + break; + default: + return SECFailure; + } + + return SECSuccess; +} + + +int +main (int argc, char **argv) +{ + int retval; + char *program_name; + PRFileDesc *in_file; + FILE *out_file; /* not PRFileDesc until SECU accepts it */ + int crequest, dresponse; + int prequest, presponse; + int ccert, vcert; + const char *db_dir, *date_str, *cert_usage_str, *name; + const char *responder_name, *responder_url, *signer_name; + PRBool add_acceptable_responses, add_service_locator; + SECItem *data = NULL; + PLOptState *optstate; + SECStatus rv; + CERTCertDBHandle *handle = NULL; + SECCertUsage cert_usage; + int64 verify_time; + + retval = -1; /* what we return/exit with on error */ + + program_name = PL_strrchr(argv[0], '/'); + program_name = program_name ? (program_name + 1) : argv[0]; + + in_file = PR_STDIN; + out_file = stdout; + + crequest = 0; + dresponse = 0; + prequest = 0; + presponse = 0; + ccert = 0; + vcert = 0; + + db_dir = NULL; + date_str = NULL; + cert_usage_str = NULL; + name = NULL; + responder_name = NULL; + responder_url = NULL; + signer_name = NULL; + + add_acceptable_responses = PR_FALSE; + add_service_locator = PR_FALSE; + + optstate = PL_CreateOptState (argc, argv, "AHLPR:S:V:d:l:pr:s:t:u:w:"); + if (optstate == NULL) { + SECU_PrintError (program_name, "PL_CreateOptState failed"); + return retval; + } + + while (PL_GetNextOpt (optstate) == PL_OPT_OK) { + switch (optstate->option) { + case '?': + short_usage (program_name); + return retval; + + case 'A': + add_acceptable_responses = PR_TRUE; + break; + + case 'H': + long_usage (program_name); + return retval; + + case 'L': + add_service_locator = PR_TRUE; + break; + + case 'P': + presponse = 1; + break; + + case 'R': + dresponse = 1; + name = optstate->value; + break; + + case 'S': + ccert = 1; + name = optstate->value; + break; + + case 'V': + vcert = 1; + name = optstate->value; + break; + + case 'd': + db_dir = optstate->value; + break; + + case 'l': + responder_url = optstate->value; + break; + + case 'p': + prequest = 1; + break; + + case 'r': + crequest = 1; + name = optstate->value; + break; + + case 's': + signer_name = optstate->value; + break; + + case 't': + responder_name = optstate->value; + break; + + case 'u': + cert_usage_str = optstate->value; + break; + + case 'w': + date_str = optstate->value; + break; + } + } + + if ((crequest + dresponse + prequest + presponse + ccert + vcert) != 1) { + PR_fprintf (PR_STDERR, "%s: must specify exactly one command\n\n", + program_name); + short_usage (program_name); + return retval; + } + + if (vcert) { + if (cert_usage_str == NULL) { + PR_fprintf (PR_STDERR, "%s: verification requires cert usage\n\n", + program_name); + short_usage (program_name); + return retval; + } + + rv = cert_usage_from_char (cert_usage_str, &cert_usage); + if (rv != SECSuccess) { + PR_fprintf (PR_STDERR, "%s: invalid cert usage (\"%s\")\n\n", + program_name, cert_usage_str); + long_usage (program_name); + return retval; + } + } + + if (ccert + vcert) { + if (responder_url != NULL || responder_name != NULL) { + /* + * To do a full status check, both the URL and the cert name + * of the responder must be specified if either one is. + */ + if (responder_url == NULL || responder_name == NULL) { + if (responder_url == NULL) + PR_fprintf (PR_STDERR, + "%s: must also specify responder location\n\n", + program_name); + else + PR_fprintf (PR_STDERR, + "%s: must also specify responder name\n\n", + program_name); + short_usage (program_name); + return retval; + } + } + + if (date_str != NULL) { + rv = DER_AsciiToTime (&verify_time, (char *) date_str); + if (rv != SECSuccess) { + SECU_PrintError (program_name, "error converting time string"); + PR_fprintf (PR_STDERR, "\n"); + long_usage (program_name); + return retval; + } + } else { + verify_time = PR_Now(); + } + } + + retval = -2; /* errors change from usage to runtime */ + + /* + * Initialize the NSPR and Security libraries. + */ + PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + db_dir = SECU_ConfigDirectory (db_dir); + rv = NSS_Init (db_dir); + if (rv != SECSuccess) { + SECU_PrintError (program_name, "NSS_Init failed"); + goto prdone; + } + + if (prequest + presponse) { + data = read_file_into_item (in_file, siBuffer); + if (data == NULL) { + SECU_PrintError (program_name, "problem reading input"); + goto nssdone; + } + } + + if (crequest + dresponse + presponse + ccert + vcert) { + handle = CERT_GetDefaultCertDB(); + if (handle == NULL) { + SECU_PrintError (program_name, "problem getting certdb handle"); + goto nssdone; + } + + /* + * It would be fine to do the enable for all of these commands, + * but this way we check that everything but an overall verify + * can be done without it. That is, that the individual pieces + * work on their own. + */ + if (vcert) { + rv = CERT_EnableOCSPChecking (handle); + if (rv != SECSuccess) { + SECU_PrintError (program_name, "error enabling OCSP checking"); + goto nssdone; + } + } + + if ((ccert + vcert) && (responder_name != NULL)) { + rv = CERT_SetOCSPDefaultResponder (handle, responder_url, + responder_name); + if (rv != SECSuccess) { + SECU_PrintError (program_name, + "error setting default responder"); + goto nssdone; + } + + rv = CERT_EnableOCSPDefaultResponder (handle); + if (rv != SECSuccess) { + SECU_PrintError (program_name, + "error enabling default responder"); + goto nssdone; + } + } + } + +#define NOTYET(opt) \ + { \ + PR_fprintf (PR_STDERR, "%s not yet working\n", opt); \ + exit (-1); \ + } + + if (crequest) { + if (signer_name != NULL) { + NOTYET("-s"); + } + rv = create_request (out_file, handle, name, add_service_locator, + add_acceptable_responses); + } else if (dresponse) { + if (signer_name != NULL) { + NOTYET("-s"); + } + rv = dump_response (out_file, handle, name, responder_url); + } else if (prequest) { + rv = print_request (out_file, data); + } else if (presponse) { + rv = print_response (out_file, data, handle); + } else if (ccert) { + if (signer_name != NULL) { + NOTYET("-s"); + } + rv = get_cert_status (out_file, handle, name, verify_time); + } else if (vcert) { + if (signer_name != NULL) { + NOTYET("-s"); + } + rv = verify_cert (out_file, handle, name, cert_usage, verify_time); + } + + if (rv != SECSuccess) + SECU_PrintError (program_name, "error performing requested operation"); + else + retval = 0; + +nssdone: + if (data != NULL) { + SECITEM_FreeItem (data, PR_TRUE); + } + + if (handle != NULL) { + (void) CERT_DisableOCSPChecking (handle); + CERT_ClosePermCertDB (handle); + } + + NSS_Shutdown (); + +prdone: + PR_Cleanup (); + return retval; +} |