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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user