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:
kaie%kuix.de
2013-02-15 17:54:57 +00:00
parent a555bc1567
commit 80289d8043
4 changed files with 618 additions and 21 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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