From 80289d80435f0e8dfd6a9d7b025ccef3ddbe2f18 Mon Sep 17 00:00:00 2001 From: "kaie%kuix.de" Date: Fri, 15 Feb 2013 17:54:57 +0000 Subject: [PATCH] Bug 700701 (as part of Bug 360420), Enhance tstclnt and ssltap to support OCSP stapling, r=rrelyea git-svn-id: svn://10.0.0.236/trunk@264734 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/security/nss/cmd/selfserv/selfserv.c | 10 +- mozilla/security/nss/cmd/ssltap/ssltap.c | 271 ++++++++++++++++- mozilla/security/nss/cmd/tstclnt/tstclnt.c | 294 ++++++++++++++++++- mozilla/security/nss/tests/ocsp/ocsp.sh | 64 ++++ 4 files changed, 618 insertions(+), 21 deletions(-) diff --git a/mozilla/security/nss/cmd/selfserv/selfserv.c b/mozilla/security/nss/cmd/selfserv/selfserv.c index cc17d0d53cc..501215bfca7 100644 --- a/mozilla/security/nss/cmd/selfserv/selfserv.c +++ b/mozilla/security/nss/cmd/selfserv/selfserv.c @@ -327,9 +327,12 @@ mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, CERTCertificate * peerCert; peerCert = SSL_PeerCertificate(fd); - - PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n", - peerCert->subjectName, peerCert->issuerName); + + if (peerCert) { + PRINTF("selfserv: Subject: %s\nselfserv: Issuer : %s\n", + peerCert->subjectName, peerCert->issuerName); + CERT_DestroyCertificate(peerCert); + } rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); @@ -340,7 +343,6 @@ mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, FPRINTF(stderr, "selfserv: -- SSL3: Certificate Invalid, err %d.\n%s\n", err, SECU_Strerror(err)); } - CERT_DestroyCertificate(peerCert); FLUSH; return rv; } diff --git a/mozilla/security/nss/cmd/ssltap/ssltap.c b/mozilla/security/nss/cmd/ssltap/ssltap.c index dc59ef6b51d..81f0e588e70 100644 --- a/mozilla/security/nss/cmd/ssltap/ssltap.c +++ b/mozilla/security/nss/cmd/ssltap/ssltap.c @@ -33,8 +33,10 @@ #include "nss.h" #include "cert.h" #include "sslproto.h" +#include "ocsp.h" +#include "ocspti.h" /* internals for pretty-printing routines *only* */ -#define VERSIONSTRING "$Revision: 1.23 $ ($Date: 2013-01-23 20:53:58 $) $Author: wtc%google.com $" +#define VERSIONSTRING "$Revision: 1.24 $ ($Date: 2013-02-15 17:54:56 $) $Author: kaie%kuix.de $" struct _DataBufferList; @@ -733,6 +735,236 @@ unsigned int print_hello_extension(unsigned char * hsdata, return pos; } +/* + * 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)", +}; + +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_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_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_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_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_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) +{ + SECU_Indent (out_file, level); + fprintf (out_file, "Basic OCSP Response:\n"); + level++; + + print_response_data (out_file, basic->tbsResponseData, level); +} + +static void +print_status_response(SECItem *data) +{ + int level = 2; + CERTOCSPResponse *response; + response = CERT_DecodeOCSPResponse (data); + if (!response) { + SECU_Indent (stdout, level); + fprintf(stdout,"unable to decode certificate_status\n"); + return; + } + + SECU_Indent (stdout, level); + if (response->statusValue >= ocspResponse_min && + response->statusValue <= ocspResponse_max) { + fprintf (stdout, "Response Status: %s\n", + responseStatusNames[response->statusValue]); + } else { + fprintf (stdout, + "Response Status: other (Status value %d out of defined range)\n", + (int)response->statusValue); + } + + if (response->statusValue == ocspResponse_successful) { + ocspResponseBytes *responseBytes = response->responseBytes; + PORT_Assert (responseBytes != NULL); + + level++; + SECU_PrintObjectID (stdout, &(responseBytes->responseType), + "Response Type", level); + switch (response->responseBytes->responseTypeTag) { + case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: + print_basic_response (stdout, + responseBytes->decodedResponse.basic, + level); + break; + default: + SECU_Indent (stdout, level); + fprintf (stdout, "Unknown response syntax\n"); + break; + } + } else { + SECU_Indent (stdout, level); + fprintf (stdout, "Unsuccessful response, no more information.\n"); + } + + CERT_DestroyOCSPResponse (response); +} + /* In the case of renegotiation, handshakes that occur in an already MAC'ed * channel, by the time of this call, the caller has already removed the MAC * from input recordLen. The only MAC'ed record that will get here with its @@ -791,6 +1023,7 @@ void print_ssl3_handshake(unsigned char *recordBuf, case 15: PR_FPUTS("certificate_verify)\n" ); break; case 16: PR_FPUTS("client_key_exchange)\n" ); break; case 20: PR_FPUTS("finished)\n" ); break; + case 22: PR_FPUTS("certificate_status_request)\n" ); break; default: PR_FPUTS("unknown)\n" ); break; } @@ -1088,6 +1321,37 @@ void print_ssl3_handshake(unsigned char *recordBuf, } break; + case 22: /*certificate_status_request*/ + { + SECItem data; + PRFileDesc *ofd; + static int ocspFileNumber; + char ocspFileName[20]; + + /* skip 4 bytes with handshake numbers, as in ssl3_HandleCertificateStatus */ + data.type = siBuffer; + data.data = hsdata + 4; + data.len = sslh.length - 4; + print_status_response(&data); + + PR_snprintf(ocspFileName, sizeof ocspFileName, "ocsp.%03d", + ++ocspFileNumber); + ofd = PR_Open(ocspFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, + 0664); + if (!ofd) { + PR_fprintf(PR_STDOUT, + " data = { couldn't save file '%s' }\n", + ocspFileName); + } else { + PR_Write(ofd, data.data, data.len); + PR_fprintf(PR_STDOUT, + " data = { saved in file '%s' }\n", + ocspFileName); + PR_Close(ofd); + } + } + break; + default: { PR_fprintf(PR_STDOUT," UNKNOWN MESSAGE TYPE %d [%d] {\n", @@ -1137,7 +1401,6 @@ void print_ssl(DataBufferList *s, int length, unsigned char *buffer) /* first, create a new buffer object for this piece of data. */ DataBuffer *db; - int i,l; if (s->size == 0 && length > 0 && buffer[0] >= 32 && buffer[0] < 128) { /* Not an SSL record, treat entire buffer as plaintext */ @@ -1145,12 +1408,8 @@ void print_ssl(DataBufferList *s, int length, unsigned char *buffer) return; } - check_integrity(s); - i = 0; - l = length; - db = PR_NEW(struct _DataBuffer); db->buffer = (unsigned char*)PORT_Alloc(length); diff --git a/mozilla/security/nss/cmd/tstclnt/tstclnt.c b/mozilla/security/nss/cmd/tstclnt/tstclnt.c index 56df75233d3..43346698152 100644 --- a/mozilla/security/nss/cmd/tstclnt/tstclnt.c +++ b/mozilla/security/nss/cmd/tstclnt/tstclnt.c @@ -28,6 +28,7 @@ #include "prio.h" #include "prnetdb.h" #include "nss.h" +#include "ocsp.h" #include "ssl.h" #include "sslproto.h" #include "pk11func.h" @@ -45,6 +46,13 @@ #define MAX_WAIT_FOR_SERVER 600 #define WAIT_INTERVAL 100 +#define EXIT_CODE_HANDSHAKE_FAILED 254 + +#define EXIT_CODE_SIDECHANNELTEST_GOOD 0 +#define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 +#define EXIT_CODE_SIDECHANNELTEST_NODATA 2 +#define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 + PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; int ssl2CipherSuites[] = { @@ -99,6 +107,7 @@ secuPWData pwdata = { PW_NONE, 0 }; void printSecurityInfo(PRFileDesc *fd) { CERTCertificate * cert; + const SECItem *cs; SSL3Statistics * ssl3stats = SSL_GetStatistics(); SECStatus result; SSLChannelInfo channel; @@ -144,6 +153,11 @@ void printSecurityInfo(PRFileDesc *fd) "%ld stateless resumes\n", ssl3stats->hsh_sid_cache_hits, ssl3stats->hsh_sid_cache_misses, ssl3stats->hsh_sid_cache_not_ok, ssl3stats->hsh_sid_stateless_resumes); + + cs = SSL_PeerStapledOCSPResponse(fd); + if (cs) { + fprintf(stderr, "Received Cert Status item (OCSP stapled data)\n"); + } } void @@ -165,7 +179,7 @@ static void PrintUsageHeader(const char *progName) fprintf(stderr, "Usage: %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n" "[-d certdir] [-n nickname] [-Bafosvx] [-c ciphers] [-Y]\n" - "[-V [min-version]:[max-version]]\n" + "[-V [min-version]:[max-version]] [-T]\n" "[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n", progName); } @@ -205,6 +219,19 @@ static void PrintParameterUsage(void) fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u"); fprintf(stderr, "%-20s Enable compression.\n", "-z"); fprintf(stderr, "%-20s Enable false start.\n", "-g"); + fprintf(stderr, "%-20s Enable the cert_status extension (OCSP stapling).\n", "-T"); + fprintf(stderr, "%-20s Require fresh revocation info from side channel.\n" + "%-20s -F once means: require for server cert only\n" + "%-20s -F twice means: require for intermediates, too\n" + "%-20s (Connect, handshake with server, disable dynamic download\n" + "%-20s of OCSP/CRL, verify cert using CERT_PKIXVerifyCert.)\n" + "%-20s Exit code:\n" + "%-20s 0: have fresh and valid revocation data, status good\n" + "%-20s 1: cert failed to verify, prior to revocation checking\n" + "%-20s 2: missing, old or invalid revocation data\n" + "%-20s 3: have fresh and valid revocation data, status revoked\n", + "-F", "", "", "", "", "", "", "", "", ""); + fprintf(stderr, "%-20s Test -F allows 0=any (default), 1=only OCSP, 2=only CRL\n", "-M"); fprintf(stderr, "%-20s Restrict ciphers\n", "-c ciphers"); fprintf(stderr, "%-20s Print cipher values allowed for parameter -c and exit\n", "-Y"); } @@ -294,8 +321,14 @@ typedef struct * peer's certificate and restart the handshake. */ void * dbHandle; /* Certificate database handle to use while * authenticating the peer's certificate. */ + PRBool testFreshStatusFromSideChannel; + PRErrorCode sideChannelRevocationTestResultCode; + PRBool requireDataForIntermediates; + PRBool allowOCSPSideChannelData; + PRBool allowCRLSideChannelData; } ServerCertAuth; + /* * Callback is called when incoming certificate is not valid. * Returns SECSuccess to accept the cert anyway, SECFailure to reject. @@ -310,16 +343,205 @@ ownBadCertHandler(void * arg, PRFileDesc * socket) return SECSuccess; /* override, say it's OK. */ } + + +#define EXIT_CODE_SIDECHANNELTEST_GOOD 0 +#define EXIT_CODE_SIDECHANNELTEST_BADCERT 1 +#define EXIT_CODE_SIDECHANNELTEST_NODATA 2 +#define EXIT_CODE_SIDECHANNELTEST_REVOKED 3 + +static void +verifyFromSideChannel(CERTCertificate *cert, ServerCertAuth *sca) +{ + PRUint64 revDoNotUse = + CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD; + + PRUint64 revUseLocalOnlyAndSoftFail = + CERT_REV_M_TEST_USING_THIS_METHOD + | CERT_REV_M_FORBID_NETWORK_FETCHING + | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE + | CERT_REV_M_IGNORE_MISSING_FRESH_INFO + | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; + + PRUint64 revUseLocalOnlyAndHardFail = + CERT_REV_M_TEST_USING_THIS_METHOD + | CERT_REV_M_FORBID_NETWORK_FETCHING + | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE + | CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO + | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; + + PRUint64 methodFlagsDoNotUse[2]; + PRUint64 methodFlagsCheckSoftFail[2]; + PRUint64 methodFlagsCheckHardFail[2]; + CERTRevocationTests revTestsDoNotCheck; + CERTRevocationTests revTestsOverallSoftFail; + CERTRevocationTests revTestsOverallHardFail; + CERTRevocationFlags rev; + CERTValInParam cvin[2]; + CERTValOutParam cvout[1]; + SECStatus rv; + + methodFlagsDoNotUse[cert_revocation_method_crl] = revDoNotUse; + methodFlagsDoNotUse[cert_revocation_method_ocsp] = revDoNotUse; + + methodFlagsCheckSoftFail[cert_revocation_method_crl] = + sca->allowCRLSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; + methodFlagsCheckSoftFail[cert_revocation_method_ocsp] = + sca->allowOCSPSideChannelData ? revUseLocalOnlyAndSoftFail : revDoNotUse; + + methodFlagsCheckHardFail[cert_revocation_method_crl] = + sca->allowCRLSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; + methodFlagsCheckHardFail[cert_revocation_method_ocsp] = + sca->allowOCSPSideChannelData ? revUseLocalOnlyAndHardFail : revDoNotUse; + + revTestsDoNotCheck.cert_rev_flags_per_method = methodFlagsDoNotUse; + revTestsDoNotCheck.number_of_defined_methods = 2; + revTestsDoNotCheck.number_of_preferred_methods = 0; + revTestsDoNotCheck.cert_rev_method_independent_flags = + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST + | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; + + revTestsOverallSoftFail.cert_rev_flags_per_method = 0; /* must define later */ + revTestsOverallSoftFail.number_of_defined_methods = 2; + revTestsOverallSoftFail.number_of_preferred_methods = 0; + revTestsOverallSoftFail.cert_rev_method_independent_flags = + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST + | CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; + + revTestsOverallHardFail.cert_rev_flags_per_method = 0; /* must define later */ + revTestsOverallHardFail.number_of_defined_methods = 2; + revTestsOverallHardFail.number_of_preferred_methods = 0; + revTestsOverallHardFail.cert_rev_method_independent_flags = + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST + | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; + + rev.chainTests = revTestsDoNotCheck; + rev.leafTests = revTestsDoNotCheck; + + cvin[0].type = cert_pi_revocationFlags; + cvin[0].value.pointer.revocation = &rev; + cvin[1].type = cert_pi_end; + + cvout[0].type = cert_po_end; + + /* Strategy: + * + * Verify with revocation checking disabled. + * On failure return 1. + * + * if result if "good", then continue testing. + * + * Verify with CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO. + * If result is good, return 0. + * + * On failure continue testing, find out why it failed. + * + * Verify with CERT_REV_M_IGNORE_MISSING_FRESH_INFO + * + * If result is "good", then our previous test failed, + * because we don't have fresh revocation info, return 2. + * + * If result is still bad, we do have revocation info, + * and it says "revoked" or something equivalent, return 3. + */ + + /* revocation checking disabled */ + rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, + cvin, cvout, NULL); + if (rv != SECSuccess) { + sca->sideChannelRevocationTestResultCode = + EXIT_CODE_SIDECHANNELTEST_BADCERT; + return; + } + + /* revocation checking, hard fail */ + if (sca->allowOCSPSideChannelData && sca->allowCRLSideChannelData) { + /* any method is allowed. use soft fail on individual checks, + * but use hard fail on the overall check + */ + revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; + } + else { + /* only one method is allowed. use hard fail on the individual checks. + * hard/soft fail is irrelevant on overall flags. + */ + revTestsOverallHardFail.cert_rev_flags_per_method = methodFlagsCheckHardFail; + } + rev.leafTests = revTestsOverallHardFail; + rev.chainTests = + sca->requireDataForIntermediates ? revTestsOverallHardFail : revTestsDoNotCheck; + rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, + cvin, cvout, NULL); + if (rv == SECSuccess) { + sca->sideChannelRevocationTestResultCode = + EXIT_CODE_SIDECHANNELTEST_GOOD; + return; + } + + /* revocation checking, soft fail */ + revTestsOverallSoftFail.cert_rev_flags_per_method = methodFlagsCheckSoftFail; + rev.leafTests = revTestsOverallSoftFail; + rev.chainTests = + sca->requireDataForIntermediates ? revTestsOverallSoftFail : revTestsDoNotCheck; + rv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer, + cvin, cvout, NULL); + if (rv == SECSuccess) { + sca->sideChannelRevocationTestResultCode = + EXIT_CODE_SIDECHANNELTEST_NODATA; + return; + } + + sca->sideChannelRevocationTestResultCode = + EXIT_CODE_SIDECHANNELTEST_REVOKED; +} + static SECStatus ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg; + if (!serverCertAuth->shouldPause) { + CERTCertificate *cert; + const SECItem *cs; + + if (!serverCertAuth->testFreshStatusFromSideChannel) { + return SSL_AuthCertificate(serverCertAuth->dbHandle, + fd, checkSig, isServer); + } + + /* No verification attempt must have happened before now, + * to ensure revocation data has been actively retrieved yet, + * or our test will produce incorrect results. + */ + + cert = SSL_RevealCert(fd); + if (!cert) { + exit(254); + } + + cs = SSL_PeerStapledOCSPResponse(fd); + if (cs) { + CERT_CacheOCSPResponseFromSideChannel( + serverCertAuth->dbHandle, + cert, + PR_Now(), + cs, + arg); + } + + verifyFromSideChannel(cert, serverCertAuth); + CERT_DestroyCertificate(cert); + /* return success to ensure our caller will continue and we will + * reach the code that handles + * serverCertAuth->sideChannelRevocationTestResultCode + */ + return SECSuccess; + } + FPRINTF(stderr, "%s: using asynchronous certificate validation\n", progName); - PORT_Assert(serverCertAuth->shouldPause); PORT_Assert(!serverCertAuth->isPaused); serverCertAuth->isPaused = PR_TRUE; return SECWouldBlock; @@ -576,6 +798,7 @@ int main(int argc, char **argv) int enableSessionTickets = 0; int enableCompression = 0; int enableFalseStart = 0; + int enableCertStatus = 0; PRSocketOptionData opt; PRNetAddr addr; PRPollDesc pollset[2]; @@ -597,6 +820,11 @@ int main(int argc, char **argv) serverCertAuth.shouldPause = PR_TRUE; serverCertAuth.isPaused = PR_FALSE; serverCertAuth.dbHandle = NULL; + serverCertAuth.testFreshStatusFromSideChannel = PR_FALSE; + serverCertAuth.sideChannelRevocationTestResultCode = EXIT_CODE_HANDSHAKE_FAILED; + serverCertAuth.requireDataForIntermediates = PR_FALSE; + serverCertAuth.allowOCSPSideChannelData = PR_TRUE; + serverCertAuth.allowCRLSideChannelData = PR_TRUE; progName = strrchr(argv[0], '/'); if (!progName) @@ -614,7 +842,7 @@ int main(int argc, char **argv) SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); optstate = PL_CreateOptState(argc, argv, - "BOSV:W:Ya:c:d:fgh:m:n:op:qr:st:uvw:xz"); + "BFM:OSTV:W:Ya:c:d:fgh:m:n:op:qr:st:uvw:xz"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -622,10 +850,36 @@ int main(int argc, char **argv) case 'B': bypassPKCS11 = 1; break; + case 'F': if (serverCertAuth.testFreshStatusFromSideChannel) { + /* parameter given twice or more */ + serverCertAuth.requireDataForIntermediates = PR_TRUE; + } + serverCertAuth.testFreshStatusFromSideChannel = PR_TRUE; + break; + case 'O': serverCertAuth.shouldPause = PR_FALSE; break; + case 'M': switch (atoi(optstate->value)) { + case 1: + serverCertAuth.allowOCSPSideChannelData = PR_TRUE; + serverCertAuth.allowCRLSideChannelData = PR_FALSE; + break; + case 2: + serverCertAuth.allowOCSPSideChannelData = PR_FALSE; + serverCertAuth.allowCRLSideChannelData = PR_TRUE; + break; + case 0: + default: + serverCertAuth.allowOCSPSideChannelData = PR_TRUE; + serverCertAuth.allowCRLSideChannelData = PR_TRUE; + break; + }; + break; + case 'S': skipProtoHeader = PR_TRUE; break; + case 'T': enableCertStatus = 1; break; + case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, enabledVersions, enableSSL2, &enabledVersions, &enableSSL2) != SECSuccess) { @@ -702,6 +956,12 @@ int main(int argc, char **argv) if (!host || !portno) Usage(progName); + if (serverCertAuth.testFreshStatusFromSideChannel + && serverCertAuth.shouldPause) { + fprintf(stderr, "%s: -F requires the use of -O\n", progName); + exit(1); + } + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); PK11_SetPasswordFunc(SECU_GetModulePassword); @@ -816,7 +1076,10 @@ int main(int argc, char **argv) } opt.option = PR_SockOpt_Nonblocking; - opt.value.non_blocking = PR_TRUE; + opt.value.non_blocking = PR_TRUE; /* default */ + if (serverCertAuth.testFreshStatusFromSideChannel) { + opt.value.non_blocking = PR_FALSE; + } PR_SetSocketOption(s, &opt); /*PR_SetSocketOption(PR_GetSpecialFD(PR_StandardInput), &opt);*/ @@ -936,15 +1199,18 @@ int main(int argc, char **argv) return 1; } + /* enable cert status (OCSP stapling). */ + rv = SSL_OptionSet(s, SSL_ENABLE_OCSP_STAPLING, enableCertStatus); + if (rv != SECSuccess) { + SECU_PrintError(progName, "error enabling cert status (OCSP stapling)"); + return 1; + } + SSL_SetPKCS11PinArg(s, &pwdata); serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); - if (serverCertAuth.shouldPause) { - SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); - } else { - SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle); - } + SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); if (override) { SSL_BadCertHook(s, ownBadCertHandler, NULL); } @@ -1042,6 +1308,12 @@ int main(int argc, char **argv) } #endif + if (serverCertAuth.testFreshStatusFromSideChannel) { + SSL_ForceHandshake(s); + error = serverCertAuth.sideChannelRevocationTestResultCode; + goto done; + } + /* ** Select on stdin and on the socket. Write data from stdin to ** socket, read data from socket and write to stdout. @@ -1055,7 +1327,7 @@ int main(int argc, char **argv) rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth, override); if (rv != SECSuccess) { - error = 254; /* 254 (usually) means "handshake failed" */ + error = EXIT_CODE_HANDSHAKE_FAILED; SECU_PrintError(progName, "authentication of server cert failed"); goto done; } @@ -1122,7 +1394,7 @@ int main(int argc, char **argv) rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth, override); if (rv != SECSuccess) { - error = 254; /* 254 (usually) means "handshake failed" */ + error = EXIT_CODE_HANDSHAKE_FAILED; SECU_PrintError(progName, "authentication of server cert failed"); goto done; } diff --git a/mozilla/security/nss/tests/ocsp/ocsp.sh b/mozilla/security/nss/tests/ocsp/ocsp.sh index eb4c96f0626..7fedfafccbb 100644 --- a/mozilla/security/nss/tests/ocsp/ocsp.sh +++ b/mozilla/security/nss/tests/ocsp/ocsp.sh @@ -47,6 +47,70 @@ ocsp_init() cd ${CLIENTDIR} } +ocsp_stapling() +{ + TESTNAME="startssl valid, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5143 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5143 -d . + html_msg $? 0 "$TESTNAME" + + TESTNAME="startssl revoked, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5144 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5144 -d . + html_msg $? 3 "$TESTNAME" + + TESTNAME="comodo trial test expired revoked, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5145 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5145 -d . + html_msg $? 1 "$TESTNAME" + + TESTNAME="thawte (expired) valid, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5146 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5146 -d . + html_msg $? 1 "$TESTNAME" + + TESTNAME="thawte (expired) revoked, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5147 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5147 -d . + html_msg $? 1 "$TESTNAME" + + TESTNAME="digicert valid, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5148 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5148 -d . + html_msg $? 0 "$TESTNAME" + + TESTNAME="digicert revoked, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5149 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 5149 -d . + html_msg $? 3 "$TESTNAME" + + TESTNAME="live valid, supports OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h login.live.com -p 443 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h login.live.com -p 443 -d . + html_msg $? 0 "$TESTNAME" + + TESTNAME="startssl valid, don't support OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 443 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h kuix.de -p 443 -d . + html_msg $? 2 "$TESTNAME" + + TESTNAME="cacert untrusted, don't support OCSP stapling" + echo "$SCRIPTNAME: $TESTNAME" + echo "tstclnt -V tls1.0: -T -v -F -M 1 -O -h www.cacert.org -p 443 -d ." + ${BINDIR}/tstclnt -V tls1.0: -T -v -F -M 1 -O -h www.cacert.org -p 443 -d . + html_msg $? 1 "$TESTNAME" +} + ################## main ################################################# ocsp_init ocsp_iopr_run +ocsp_stapling