Add support for TLS 1.3 OCSP multi-stapling for server certs

Co-authored-by: Michael Krueger

Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20945)
This commit is contained in:
martin 2025-02-07 14:22:41 +01:00 committed by Tomas Mraz
parent c108ead284
commit b1b4b154fd
35 changed files with 1930 additions and 245 deletions

View File

@ -133,6 +133,18 @@ OpenSSL 3.6
*Adrian Stanciu*
* Added support for TLS 1.3 OCSP multi-stapling for server certs.
* new `s_client` options:
* `-ocsp_check_leaf`: Checks the status of the leaf (server) certificate.
* `-ocsp_check_all`: Checks the status of all certificates in the server chain.
* new `s_server` option:
* `-status_all` Provides OCSP status information for the entire server certificate chain (multi-stapling) for TLS 1.3 and later.
* Improved `-status_file` option can now be given multiple times to provide
multiple files containing OCSP responses.
*Michael Krueger, Martin Rauch*
OpenSSL 3.5
-----------

View File

@ -106,6 +106,10 @@ int verify_callback(int ok, X509_STORE_CTX *ctx)
if (!verify_args.quiet)
policies_print(ctx);
break;
case X509_V_ERR_OCSP_NO_RESPONSE:
if (!verify_args.quiet)
BIO_printf(bio_err, "no OCSP response(s) for certificate(s) found.\n");
break;
}
if (err == X509_V_OK && ok == 2 && !verify_args.quiet)
policies_print(ctx);

View File

@ -110,9 +110,10 @@ static char *sess_out = NULL;
static SSL_SESSION *psksess = NULL;
static void print_stuff(BIO *berr, SSL *con, int full);
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
static int ocsp_resp_cb(SSL *s, void *arg);
#endif
static void print_ocsp_response(BIO *bp, OCSP_RESPONSE *rsp);
# endif
static int ldap_ExtendedResponse_parse(const char *buf, long rem);
static int is_dNS_name(const char *host);
@ -483,7 +484,10 @@ typedef enum OPTION_choice {
OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
OPT_BRIEF, OPT_PREXIT, OPT_NO_INTERACTIVE, OPT_CRLF, OPT_QUIET, OPT_NBIO,
OPT_SSL_CLIENT_ENGINE, OPT_IGN_EOF, OPT_NO_IGN_EOF,
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG,
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_WDEBUG,
# ifndef OPENSSL_NO_OCSP
OPT_STATUS, OPT_STATUS_OCSP_CHECK_LEAF, OPT_STATUS_OCSP_CHECK_ALL,
# endif
OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG,
OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE,
OPT_PSK_IDENTITY, OPT_PSK, OPT_PSK_SESS,
@ -625,6 +629,17 @@ const OPTIONS s_client_options[] = {
{"no-interactive", OPT_NO_INTERACTIVE, '-',
"Don't run the client in the interactive mode"},
# ifndef OPENSSL_NO_OCSP
OPT_SECTION("OCSP stapling"),
{"status", OPT_STATUS, '-',
"Sends a certificate status request to the server (OCSP stapling) " \
"The server response (if any) will be printed out."},
{"ocsp_check_leaf", OPT_STATUS_OCSP_CHECK_LEAF, '-',
"Require checking leaf certificate status, attempting to use OCSP stapling first"},
{"ocsp_check_all", OPT_STATUS_OCSP_CHECK_ALL, '-',
"Require checking status of full chain, attempting to use OCSP stapling first"},
# endif
OPT_SECTION("Debug"),
{"showcerts", OPT_SHOWCERTS, '-',
"Show all certificates sent by the server"},
@ -659,9 +674,6 @@ const OPTIONS s_client_options[] = {
"Hex dump of all TLS extensions received"},
{"ignore_unexpected_eof", OPT_IGNORE_UNEXPECTED_EOF, '-',
"Do not treat lack of close_notify from a peer as an error"},
#ifndef OPENSSL_NO_OCSP
{"status", OPT_STATUS, '-', "Request certificate status from server"},
#endif
{"serverinfo", OPT_SERVERINFO, 's',
"types Send empty ClientHello extensions (comma-separated numbers)"},
{"alpn", OPT_ALPN, 's',
@ -1195,11 +1207,23 @@ int s_client_main(int argc, char **argv)
case OPT_TLSEXTDEBUG:
c_tlsextdebug = 1;
break;
# ifndef OPENSSL_NO_OCSP
case OPT_STATUS:
#ifndef OPENSSL_NO_OCSP
c_status_req = 1;
#endif
break;
case OPT_STATUS_OCSP_CHECK_LEAF:
c_status_req = 1;
X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_OCSP_RESP_CHECK);
vpmtouched++;
break;
case OPT_STATUS_OCSP_CHECK_ALL:
c_status_req = 1;
X509_VERIFY_PARAM_set_flags(vpm,
X509_V_FLAG_OCSP_RESP_CHECK |
X509_V_FLAG_OCSP_RESP_CHECK_ALL);
vpmtouched++;
break;
# endif
case OPT_WDEBUG:
#ifdef WATT32
dbug_init();
@ -3626,27 +3650,58 @@ static void print_stuff(BIO *bio, SSL *s, int full)
# ifndef OPENSSL_NO_OCSP
static int ocsp_resp_cb(SSL *s, void *arg)
{
const unsigned char *p;
int len;
int num, i;
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
OCSP_RESPONSE *rsp;
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
BIO_puts(arg, "OCSP response: ");
if (p == NULL) {
BIO_puts(arg, "no response sent\n");
return 1;
if (SSL_version(s) >= TLS1_3_VERSION) {
(void)SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
BIO_puts(arg, "OCSP responses: ");
if (sk_resp == NULL) {
BIO_puts(arg, "no responses sent\n");
return 1;
}
num = sk_OCSP_RESPONSE_num(sk_resp);
BIO_printf(arg, "number of responses: %d", num);
for (i = 0; i < num; i++)
print_ocsp_response(arg, sk_OCSP_RESPONSE_value(sk_resp, i));
} else {
const unsigned char *p;
int len = SSL_get_tlsext_status_ocsp_resp(s, &p);
BIO_puts(arg, "OCSP response: ");
if (p == NULL) {
BIO_puts(arg, "no OCSP response received\n");
return 1;
}
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if (rsp == NULL) {
BIO_puts(arg, "OCSP response parse error\n");
BIO_dump_indent(arg, (char *)p, len, 4);
return 0;
}
print_ocsp_response(arg, rsp);
OCSP_RESPONSE_free(rsp);
}
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if (rsp == NULL) {
BIO_puts(arg, "response parse error\n");
BIO_dump_indent(arg, (char *)p, len, 4);
return 0;
}
BIO_puts(arg, "\n======================================\n");
OCSP_RESPONSE_print(arg, rsp, 0);
BIO_puts(arg, "======================================\n");
OCSP_RESPONSE_free(rsp);
return 1;
}
static void print_ocsp_response(BIO *bp, OCSP_RESPONSE *rsp)
{
if (rsp == NULL) {
BIO_puts(bp, "no OCSP response to print\n");
return;
}
BIO_puts(bp, "\n======================================\n");
OCSP_RESPONSE_print(bp, rsp, 0);
BIO_puts(bp, "\n======================================\n");
}
# endif
static int ldap_ExtendedResponse_parse(const char *buf, long rem)

View File

@ -54,9 +54,10 @@ typedef unsigned int u_int;
#include "s_apps.h"
#include "timeouts.h"
#ifdef CHARSET_EBCDIC
#include <openssl/ebcdic.h>
# include <openssl/ebcdic.h>
#endif
#include "internal/sockets.h"
#include "internal/statem.h"
static int not_resumable_sess_cb(SSL *s, int is_forward_secure);
static int sv_body(int s, int stype, int prot, unsigned char *context);
@ -454,16 +455,20 @@ static int ssl_servername_cb(SSL *s, int *ad, void *arg)
/* Structure passed to cert status callback */
typedef struct tlsextstatusctx_st {
int timeout;
/* File to load OCSP Response from (or NULL if no file) */
char *respin;
/*
* List of filenames, from which we are loading each OCSP Response to
* staple during handshake (or NULL if no file)
*/
STACK_OF(OPENSSL_STRING) *sk_resp_in;
/* Default responder to use */
char *host, *path, *port;
char *proxy, *no_proxy;
int use_ssl;
int verbose;
int status_all;
} tlsextstatusctx;
static tlsextstatusctx tlscstatp = { -1 };
static tlsextstatusctx tlscstatp = { -1, NULL };
#ifndef OPENSSL_NO_OCSP
@ -474,14 +479,15 @@ static tlsextstatusctx tlscstatp = { -1 };
* the OCSP certificate IDs and minimise the number of OCSP responses by caching
* them until they were considered "expired".
*/
static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
OCSP_RESPONSE **resp)
static int get_ocsp_resp_from_responder_single(SSL *s, X509 *x,
tlsextstatusctx *srctx,
OCSP_RESPONSE **resp)
{
char *host = NULL, *port = NULL, *path = NULL;
char *proxy = NULL, *no_proxy = NULL;
int use_ssl;
STACK_OF(OPENSSL_STRING) *aia = NULL;
X509 *x = NULL, *cert;
X509 *cert;
X509_NAME *iname;
STACK_OF(X509) *chain = NULL;
SSL_CTX *ssl_ctx;
@ -494,7 +500,6 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
int i;
/* Build up OCSP query from server certificate */
x = SSL_get_certificate(s);
iname = X509_get_issuer_name(x);
aia = X509_get1_ocsp(x);
if (aia != NULL) {
@ -559,6 +564,7 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
SSL_get_tlsext_status_exts(s, &exts);
for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
if (!OCSP_REQUEST_add_ext(req, ext, -1))
goto err;
}
@ -591,6 +597,212 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
return ret;
}
static int bring_ocsp_resp_in_correct_order(SSL *s, tlsextstatusctx *srctx,
STACK_OF(OCSP_RESPONSE) *sk_resp_unordered,
STACK_OF(OCSP_RESPONSE) **sk_resp)
{
STACK_OF(X509) *server_certs = NULL;
X509 *ssl_cert = NULL;
X509 *issuer = NULL;
OCSP_RESPONSE *resp = NULL;
OCSP_BASICRESP *bs = NULL;
OCSP_CERTID *cert_id = NULL;
int found = -1;
int i, j, num = 1;
if (*sk_resp != NULL)
sk_OCSP_RESPONSE_pop_free(*sk_resp, OCSP_RESPONSE_free);
SSL_get0_chain_certs(s, &server_certs);
/*
* TODO(DTLS-1.3): in future DTLS should also be considered
*/
if (server_certs != NULL && srctx->status_all &&
!SSL_is_dtls(s) && SSL_version(s) >= TLS1_3_VERSION) {
/* certificate chain is available */
num = sk_X509_num(server_certs) + 1;
}
/* get OCSP response for server certificate first */
ssl_cert = SSL_get_certificate(s);
/*
* OpenSSL servers with TLS < 1.3 can be configured with no certificate
*/
if (ssl_cert == NULL)
return SSL_TLSEXT_ERR_OK;
/* reserve enough space so the pushes to the stack would not fail */
*sk_resp = sk_OCSP_RESPONSE_new_reserve(NULL, num);
if (sk_resp == NULL)
return SSL_TLSEXT_ERR_ALERT_FATAL;
for (i = 0; i < num; i++) {
if (i != 0) /* for each certificate in chain (except root) get the OCSP response */
ssl_cert = sk_X509_value(server_certs, i - 1);
/* issuer certificate is next in chain */
issuer = sk_X509_value(server_certs, i);
if (issuer == NULL
|| (cert_id = OCSP_cert_to_id(NULL, ssl_cert, issuer)) == NULL) {
sk_OCSP_RESPONSE_push(*sk_resp, NULL);
continue;
}
/* find the correct OCSP response for the requested certificate */
found = -1;
for (j = 0; j < sk_OCSP_RESPONSE_num(sk_resp_unordered); j++) {
if ((resp = sk_OCSP_RESPONSE_value(sk_resp_unordered, j)) == NULL)
continue;
if ((bs = OCSP_response_get1_basic(resp)) == NULL)
continue;
found = OCSP_resp_find(bs, cert_id, -1);
OCSP_BASICRESP_free(bs);
if (found > -1) {
/* remove the found OCSP response to prevent freeing it with the remaining list */
sk_OCSP_RESPONSE_delete(sk_resp_unordered, j);
break;
}
}
if (found < 0)
resp = NULL;
OCSP_CERTID_free(cert_id);
/* add response to stack; also insert null response */
(void)sk_OCSP_RESPONSE_push(*sk_resp, resp);
}
return SSL_TLSEXT_ERR_OK;
}
/*
* Helper function to get a list OCSP_RESPONSE from the files specified using
* -status_file options for the server certificate and the chain certificates,
* in the same order as the list of certs returned by SSL_get0_chain_certs().
* In case of a missing entry, the respective list element will be NULL.
*/
static int get_ocsp_resp_from_files(SSL *s, tlsextstatusctx *srctx,
STACK_OF(OCSP_RESPONSE) **sk_resp)
{
STACK_OF(OCSP_RESPONSE) *sk_resp_unordered = NULL;
char *respfile = NULL;
OCSP_RESPONSE *resp = NULL;
BIO *derbio;
int i;
int num = sk_OPENSSL_STRING_num(srctx->sk_resp_in);
int ret = SSL_TLSEXT_ERR_OK;
sk_resp_unordered = sk_OCSP_RESPONSE_new_reserve(NULL, num);
if (sk_resp_unordered == NULL) {
BIO_puts(bio_err, "cert_status: Cannot reserve memory for OCSP responses\n");
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
/* reading as many responses as files given */
for (i = 0; i < num; i++) {
respfile = sk_OPENSSL_STRING_value(srctx->sk_resp_in, i);
derbio = bio_open_default(respfile, 'r', FORMAT_ASN1);
if (derbio == NULL) {
BIO_printf(bio_err, "cert_status: Cannot open OCSP response file %s\n", respfile);
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
goto err;
}
resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
BIO_free(derbio);
if (resp == NULL) {
BIO_printf(bio_err, "cert_status: Error reading OCSP response from file %s\n",
respfile);
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
goto err;
}
sk_OCSP_RESPONSE_push(sk_resp_unordered, resp);
}
ret = bring_ocsp_resp_in_correct_order(s, srctx, sk_resp_unordered, sk_resp);
err:
/* free the unordered list, including all remaining OCSP responses */
sk_OCSP_RESPONSE_pop_free(sk_resp_unordered, OCSP_RESPONSE_free);
return ret;
}
/*
* Helper function to get a list of OCSP_RESPONSE from a responder
* for the server certificate and the chain certificates
* in the same order as the list of certs returned by SSL_get0_chain_certs().
* The function get_ocsp_resp_from_responder_single is called for each
* certificate.
* In case of a missing response, the respective list element will be NULL.
* This is a simplified version. It examines certificates each time and
* makes one OCSP responder query for each request. A full version would
* store details such as the OCSP certificate IDs and minimise the number of
* OCSP queries by caching responses until they were considered "expired".
*/
static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
STACK_OF(OCSP_RESPONSE) **sk_resp)
{
X509 *ssl_cert = NULL;
int i, num = 0;
STACK_OF(X509) *server_certs = NULL;
OCSP_RESPONSE *resp = NULL;
if (*sk_resp != NULL)
sk_OCSP_RESPONSE_pop_free(*sk_resp, OCSP_RESPONSE_free);
SSL_get0_chain_certs(s, &server_certs);
/*
* TODO(DTLS-1.3): in future DTLS should also be considered
*/
if (server_certs != NULL && srctx->status_all &&
!SSL_is_dtls(s) && SSL_version(s) >= TLS1_3_VERSION) {
/* certificate chain is available */
num = sk_X509_num(server_certs) + 1;
} else {
/*
* certificate chain is not available,
* set num to 1 for server certificate
*/
num = 1;
}
ssl_cert = SSL_get_certificate(s);
/*
* OpenSSL servers with TLS < 1.3 can be configured with no certificate
*/
if (ssl_cert == NULL)
return SSL_TLSEXT_ERR_OK;
*sk_resp = sk_OCSP_RESPONSE_new_reserve(NULL, num);
if (sk_resp == NULL)
return SSL_TLSEXT_ERR_ALERT_FATAL;
/* for each certificate in chain (except root) get the OCSP response */
for (i = 0; i < num; i++) {
if (i != 0) /* get OCSP response for server certificate first */
ssl_cert = sk_X509_value(server_certs, i - 1);
resp = NULL;
if (get_ocsp_resp_from_responder_single(s, ssl_cert, srctx, &resp) != SSL_TLSEXT_ERR_OK)
resp = NULL;
/* add response to stack; also insert null response */
sk_OCSP_RESPONSE_push(*sk_resp, resp);
}
return SSL_TLSEXT_ERR_OK;
}
/*
* Certificate Status callback. This is called when a client includes a
* certificate status request extension. The response is either obtained from a
@ -600,48 +812,48 @@ static int cert_status_cb(SSL *s, void *arg)
{
tlsextstatusctx *srctx = arg;
OCSP_RESPONSE *resp = NULL;
unsigned char *rspder = NULL;
int rspderlen;
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
int i;
if (srctx->verbose)
BIO_puts(bio_err, "cert_status: callback called\n");
if (srctx->respin != NULL) {
BIO *derbio = bio_open_default(srctx->respin, 'r', FORMAT_ASN1);
if (derbio == NULL) {
BIO_puts(bio_err, "cert_status: Cannot open OCSP response file\n");
goto err;
SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
if (sk_resp == NULL || sk_OCSP_RESPONSE_num(sk_resp) <= 0) {
if (srctx->sk_resp_in != NULL) {
get_ocsp_resp_from_files(s, srctx, &sk_resp);
} else {
ret = get_ocsp_resp_from_responder(s, srctx, &sk_resp);
if (ret != SSL_TLSEXT_ERR_OK)
goto err;
}
resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
BIO_free(derbio);
if (resp == NULL) {
BIO_puts(bio_err, "cert_status: Error reading OCSP response\n");
goto err;
}
} else {
ret = get_ocsp_resp_from_responder(s, srctx, &resp);
if (ret != SSL_TLSEXT_ERR_OK)
goto err;
(void)SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp);
}
rspderlen = i2d_OCSP_RESPONSE(resp, &rspder);
if (rspderlen <= 0)
goto err;
SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen);
if (srctx->verbose) {
BIO_puts(bio_err, "cert_status: ocsp response sent:\n");
OCSP_RESPONSE_print(bio_err, resp, 2);
BIO_printf(bio_err, "cert_status: number of responses: %d\n",
sk_OCSP_RESPONSE_num(sk_resp));
for (i = 0; i < sk_OCSP_RESPONSE_num(sk_resp); i++) {
resp = sk_OCSP_RESPONSE_value(sk_resp, i);
if (resp != NULL)
OCSP_RESPONSE_print(bio_err, resp, 2);
else
BIO_printf(bio_err,
"cert_status: no ocsp response for certificate with index %d\n", i);
}
}
ret = SSL_TLSEXT_ERR_OK;
err:
if (ret != SSL_TLSEXT_ERR_OK)
if (ret != SSL_TLSEXT_ERR_OK) {
ERR_print_errors(bio_err);
OCSP_RESPONSE_free(resp);
sk_OCSP_RESPONSE_pop_free(sk_resp, OCSP_RESPONSE_free);
}
return ret;
}
@ -680,6 +892,7 @@ static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
if (!s_quiet) {
/* We can assume that |in| is syntactically valid. */
unsigned int i;
BIO_printf(bio_s_out, "ALPN protocols advertised by the client: ");
for (i = 0; i < inlen;) {
if (i)
@ -692,9 +905,8 @@ static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
if (SSL_select_next_proto
((unsigned char **)out, outlen, alpn_ctx->data,
(unsigned int)alpn_ctx->len, in, inlen) != OPENSSL_NPN_NEGOTIATED) {
(unsigned int)alpn_ctx->len, in, inlen) != OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (!s_quiet) {
BIO_printf(bio_s_out, "ALPN protocols selected: ");
@ -725,9 +937,9 @@ typedef enum OPTION_choice {
OPT_VERIFYCAFILE,
OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE,
OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF,
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_VERBOSE,
OPT_STATUS_TIMEOUT, OPT_PROXY, OPT_NO_PROXY, OPT_STATUS_URL,
OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_ALL,
OPT_STATUS_VERBOSE, OPT_STATUS_TIMEOUT, OPT_PROXY, OPT_NO_PROXY,
OPT_STATUS_URL, OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE,
OPT_CRLF, OPT_QUIET, OPT_BRIEF, OPT_NO_DHE,
OPT_NO_RESUME_EPHEMERAL, OPT_PSK_IDENTITY, OPT_PSK_HINT, OPT_PSK,
@ -876,9 +1088,12 @@ const OPTIONS s_server_options[] = {
{"cert_comp", OPT_CERT_COMP, '-', "Pre-compress server certificates"},
#endif
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
OPT_SECTION("OCSP"),
{"status", OPT_STATUS, '-', "Request certificate status from server"},
{"status", OPT_STATUS, '-',
"Provide certificate status response if requested, for server cert only"},
{"status_all", OPT_STATUS_ALL, '-',
"Provide certificate status response(s) if requested, for the whole chain"},
{"status_verbose", OPT_STATUS_VERBOSE, '-',
"Print more output in certificate status callback"},
{"status_timeout", OPT_STATUS_TIMEOUT, 'n',
@ -891,8 +1106,8 @@ const OPTIONS s_server_options[] = {
{OPT_MORE_STR, 0, 0,
"Default from environment variable 'no_proxy', else 'NO_PROXY', else none"},
{"status_file", OPT_STATUS_FILE, '<',
"File containing DER encoded OCSP Response"},
#endif
"File containing DER encoded OCSP Response (can be specified multiple times)"},
# endif
OPT_SECTION("Debug"),
{"security_debug", OPT_SECURITY_DEBUG, '-',
@ -1076,9 +1291,9 @@ int s_server_main(int argc, char *argv[])
const char *s_cert_file = TEST_CERT, *s_key_file = NULL, *s_chain_file = NULL;
const char *s_cert_file2 = TEST_CERT2, *s_key_file2 = NULL;
char *s_dcert_file = NULL, *s_dkey_file = NULL, *s_dchain_file = NULL;
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
int s_tlsextstatus = 0;
#endif
# endif
int no_resume_ephemeral = 0;
unsigned int max_send_fragment = 0;
unsigned int split_send_fragment = 0, max_pipelines = 0;
@ -1390,33 +1605,40 @@ int s_server_main(int argc, char *argv[])
s_tlsextdebug = 1;
break;
case OPT_STATUS:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = 1;
#endif
tlscstatp.status_all = 0;
# endif
break;
case OPT_STATUS_ALL:
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = tlscstatp.status_all = 1;
# endif
break;
case OPT_STATUS_VERBOSE:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = tlscstatp.verbose = 1;
#endif
# endif
break;
case OPT_STATUS_TIMEOUT:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = 1;
tlscstatp.timeout = atoi(opt_arg());
#endif
# endif
break;
case OPT_PROXY:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
tlscstatp.proxy = opt_arg();
#endif
# endif
break;
case OPT_NO_PROXY:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
tlscstatp.no_proxy = opt_arg();
#endif
# endif
break;
case OPT_STATUS_URL:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = 1;
if (!OSSL_HTTP_parse_url(opt_arg(), &tlscstatp.use_ssl, NULL,
&tlscstatp.host, &tlscstatp.port, NULL,
@ -1424,13 +1646,16 @@ int s_server_main(int argc, char *argv[])
BIO_printf(bio_err, "Error parsing -status_url argument\n");
goto end;
}
#endif
# endif
break;
case OPT_STATUS_FILE:
#ifndef OPENSSL_NO_OCSP
# ifndef OPENSSL_NO_OCSP
s_tlsextstatus = 1;
tlscstatp.respin = opt_arg();
#endif
if (tlscstatp.sk_resp_in == NULL
&& (tlscstatp.sk_resp_in = sk_OPENSSL_STRING_new_null()) == NULL)
goto end;
sk_OPENSSL_STRING_push(tlscstatp.sk_resp_in, opt_arg());
# endif
break;
case OPT_MSG:
s_msg = 1;
@ -2358,6 +2583,7 @@ int s_server_main(int argc, char *argv[])
OPENSSL_free(port);
X509_VERIFY_PARAM_free(vpm);
free_sessions();
sk_OPENSSL_STRING_free(tlscstatp.sk_resp_in);
OPENSSL_free(tlscstatp.host);
OPENSSL_free(tlscstatp.port);
OPENSSL_free(tlscstatp.path);

View File

@ -174,6 +174,16 @@ const char *X509_verify_cert_error_string(long n)
return "OCSP verification failed";
case X509_V_ERR_OCSP_CERT_UNKNOWN:
return "OCSP unknown cert";
case X509_V_ERR_OCSP_RESP_INVALID:
return "OCSP response(s) invalid";
case X509_V_ERR_OCSP_SIGNATURE_FAILURE:
return "OCSP response signature verification failure";
case X509_V_ERR_OCSP_NOT_YET_VALID:
return "OCSP response not yet valid (contains a date in the future)";
case X509_V_ERR_OCSP_HAS_EXPIRED:
return "OCSP response has expired";
case X509_V_ERR_OCSP_NO_RESPONSE:
return "no OCSP response available for certificate";
case X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM:
return "Cannot find certificate signature algorithm";
case X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH:

View File

@ -22,6 +22,7 @@
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/ocsp.h>
#include <openssl/objects.h>
#include <openssl/core_names.h>
#include "internal/dane.h"
@ -55,7 +56,10 @@ static int check_name_constraints(X509_STORE_CTX *ctx);
static int check_id(X509_STORE_CTX *ctx);
static int check_trust(X509_STORE_CTX *ctx, int num_untrusted);
static int check_revocation(X509_STORE_CTX *ctx);
static int check_cert(X509_STORE_CTX *ctx);
#ifndef OPENSSL_NO_OCSP
static int check_cert_ocsp_resp(X509_STORE_CTX *ctx);
#endif
static int check_cert_crl(X509_STORE_CTX *ctx);
static int check_policy(X509_STORE_CTX *ctx);
static int check_dane_issuer(X509_STORE_CTX *ctx, int depth);
static int check_cert_key_level(X509_STORE_CTX *ctx, X509 *cert);
@ -184,6 +188,24 @@ static int verify_cb_crl(X509_STORE_CTX *ctx, int err)
return ctx->verify_cb(0, ctx);
}
#ifndef OPENSSL_NO_OCSP
/*
* Inform the verify callback of an error, OCSP-specific variant.
* It is called also on OCSP response errors, if the
* X509_V_FLAG_OCSP_RESP_CHECK or X509_V_FLAG_OCSP_RESP_CHECK_ALL flag
* is set.
* Here, the error depth and certificate are already set, we just specify
* the error number.
*
* Returns 0 to abort verification with an error, non-zero to continue.
*/
static int verify_cb_ocsp(X509_STORE_CTX *ctx, int err)
{
ctx->error = err;
return ctx->verify_cb(0, ctx);
}
#endif
/* Sadly, returns 0 also on internal error in ctx->verify_cb(). */
static int check_auth_level(X509_STORE_CTX *ctx)
{
@ -225,7 +247,6 @@ static int verify_rpk(X509_STORE_CTX *ctx)
return !!ctx->verify_cb(ctx->error == X509_V_OK, ctx);
}
/*-
* Returns -1 on internal error.
* Sadly, returns 0 also on internal error in ctx->verify_cb().
@ -1037,28 +1058,209 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted)
static int check_revocation(X509_STORE_CTX *ctx)
{
int i = 0, last = 0, ok = 0;
int crl_check_enabled =
(ctx->param->flags &
(X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL)) != 0;
int crl_check_all_enabled =
(ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) != 0;
int ocsp_check_enabled =
(ctx->param->flags &
(X509_V_FLAG_OCSP_RESP_CHECK | X509_V_FLAG_OCSP_RESP_CHECK_ALL)) != 0;
int ocsp_check_all_enabled =
(ctx->param->flags & X509_V_FLAG_OCSP_RESP_CHECK_ALL) != 0;
if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK) == 0)
if (!crl_check_enabled && !ocsp_check_enabled)
return 1;
if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) != 0) {
last = sk_X509_num(ctx->chain) - 1;
} else {
/* If checking CRL paths this isn't the EE certificate */
if (ctx->parent != NULL)
return 1;
last = 0;
if (ocsp_check_enabled) {
#ifndef OPENSSL_NO_OCSP
/*
* certificate status checking with OCSP
*/
if (ocsp_check_all_enabled)
last = sk_X509_num(ctx->chain) - 1;
else if (!crl_check_all_enabled && ctx->parent != NULL)
return 1; /* If checking CRL paths this isn't the EE certificate */
for (i = 0; i <= last; i++) {
ctx->error_depth = i;
ctx->current_cert = sk_X509_value(ctx->chain, i);
/* skip if cert is apparently self-signed */
if (ctx->current_cert->ex_flags & EXFLAG_SS)
continue;
/* the issuer certificate is the next in the chain */
ctx->current_issuer = sk_X509_value(ctx->chain, i + 1);
ok = check_cert_ocsp_resp(ctx);
/*
* In the case the certificate status is REVOKED, the verification
* can stop here.
*/
if (ok == V_OCSP_CERTSTATUS_REVOKED) {
return verify_cb_ocsp(ctx, ctx->error != 0
? ctx->error
: X509_V_ERR_OCSP_VERIFY_FAILED);
}
/*
* In the case the certificate status is GOOD, continue with the next
* certificate.
*/
if (ok == V_OCSP_CERTSTATUS_GOOD)
continue;
/*
* As stated in RFC 6961 section 2.2:
* If OCSP is not enabled or the client receives a "ocsp_response_list"
* that does not contain a response for one or more of the certificates
* in the completed certificate chain, the client SHOULD attempt to
* validate the certificate using an alternative retrieval method,
* such as downloading the relevant CRL;
*/
if (crl_check_all_enabled || (crl_check_enabled && i == 0)) {
ok = check_cert_crl(ctx);
if (!ok)
return ok;
} else {
ok = verify_cb_ocsp(ctx, X509_V_ERR_OCSP_VERIFY_FAILED);
if (!ok)
return ok;
}
}
#endif
}
for (i = 0; i <= last; i++) {
ctx->error_depth = i;
ok = check_cert(ctx);
if (!ok)
return ok;
if (crl_check_enabled && !ocsp_check_all_enabled) {
/* certificate status check with CRLs */
if (crl_check_all_enabled) {
last = sk_X509_num(ctx->chain) - 1;
} else {
/* If checking CRL paths this isn't the EE certificate */
if (ctx->parent != NULL)
return 1;
last = 0;
}
/*
* in the case that OCSP is only enabled for the server certificate
* and CRL for the complete chain, the rest of the chain has to be
* checked here
*/
if (ocsp_check_enabled && crl_check_all_enabled)
i = 1;
else
i = 0;
for (; i <= last; i++) {
ctx->error_depth = i;
ok = check_cert_crl(ctx);
if (!ok)
return ok;
}
}
return 1;
}
#ifndef OPENSSL_NO_OCSP
static int check_cert_ocsp_resp(X509_STORE_CTX *ctx)
{
int cert_status, crl_reason;
int i;
OCSP_RESPONSE *resp = NULL;
OCSP_BASICRESP *bs = NULL;
OCSP_SINGLERESP *sr = NULL;
OCSP_CERTID *sr_cert_id = NULL;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
ASN1_OBJECT *cert_id_md_oid;
EVP_MD *cert_id_md;
OCSP_CERTID *cert_id = NULL;
int ret = V_OCSP_CERTSTATUS_UNKNOWN;
int num;
num = sk_OCSP_RESPONSE_num(ctx->ocsp_resp);
if (num < 0 || num <= ctx->error_depth)
return X509_V_ERR_OCSP_NO_RESPONSE;
if ((resp = sk_OCSP_RESPONSE_value(ctx->ocsp_resp, ctx->error_depth)) == NULL
|| (bs = OCSP_response_get1_basic(resp)) == NULL
|| (num = OCSP_resp_count(bs)) < 1)
return X509_V_ERR_OCSP_NO_RESPONSE;
if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
OCSP_BASICRESP_free(bs);
ret = X509_V_ERR_OCSP_RESP_INVALID;
goto end;
}
if (OCSP_basic_verify(bs, ctx->chain, ctx->store, OCSP_TRUSTOTHER) <= 0) {
ret = X509_V_ERR_OCSP_SIGNATURE_FAILURE;
goto end;
}
/* find the right single response in the OCSP response */
for (i = 0; i < num; i++) {
sr = OCSP_resp_get0(bs, i);
/* determine the md algorithm which was used to create cert id */
sr_cert_id = (OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sr);
OCSP_id_get0_info(NULL, &cert_id_md_oid, NULL, NULL, sr_cert_id);
if (cert_id_md_oid != NULL)
cert_id_md = (EVP_MD *)EVP_get_digestbyobj(cert_id_md_oid);
else
cert_id_md = NULL;
/* search the stack for the requested OCSP response */
cert_id = OCSP_cert_to_id(cert_id_md, ctx->current_cert, ctx->current_issuer);
if (cert_id == NULL) {
ret = X509_V_ERR_OCSP_RESP_INVALID;
goto end;
}
if (!OCSP_id_cmp(cert_id, sr_cert_id))
break;
OCSP_CERTID_free(cert_id);
cert_id = NULL;
}
if (cert_id == NULL) {
ret = X509_V_ERR_OCSP_NO_RESPONSE;
goto end;
}
if (OCSP_resp_find_status(bs, cert_id, &cert_status, &crl_reason, &rev,
&thisupd, &nextupd) <= 0) {
ret = X509_V_ERR_OCSP_RESP_INVALID;
goto end;
}
if (cert_status == V_OCSP_CERTSTATUS_GOOD) {
/*
* Note:
* A OCSP stapling result will be accepted up to 5 minutes
* after it expired!
*/
if (!OCSP_check_validity(thisupd, nextupd, 300L, -1L))
ret = X509_V_ERR_OCSP_HAS_EXPIRED;
else
ret = V_OCSP_CERTSTATUS_GOOD;
} else {
ret = cert_status;
}
end:
OCSP_CERTID_free(cert_id);
OCSP_BASICRESP_free(bs);
return ret;
}
#endif
/* Sadly, returns 0 also on internal error. */
static int check_cert(X509_STORE_CTX *ctx)
static int check_cert_crl(X509_STORE_CTX *ctx)
{
X509_CRL *crl = NULL, *dcrl = NULL;
int ok = 0;
@ -1070,6 +1272,9 @@ static int check_cert(X509_STORE_CTX *ctx)
ctx->current_crl_score = 0;
ctx->current_reasons = 0;
/* skip if cert is apparently self-signed */
if (ctx->current_cert->ex_flags & EXFLAG_SS)
return 1;
if ((x->ex_flags & EXFLAG_PROXY) != 0)
return 1;
@ -1645,7 +1850,7 @@ static int get_crl_delta(X509_STORE_CTX *ctx,
sk_X509_CRL_pop_free(skcrl, X509_CRL_free);
done:
done:
/* If we got any kind of CRL use it and return success */
if (crl != NULL) {
ctx->current_issuer = issuer;
@ -2374,6 +2579,13 @@ void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk)
ctx->crls = sk;
}
#ifndef OPENSSL_NO_OCSP
void X509_STORE_CTX_set_ocsp_resp(X509_STORE_CTX *ctx, STACK_OF(OCSP_RESPONSE) *sk)
{
ctx->ocsp_resp = sk;
}
#endif
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose)
{
/*
@ -2490,7 +2702,6 @@ void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
OPENSSL_free(ctx);
}
int X509_STORE_CTX_init_rpk(X509_STORE_CTX *ctx, X509_STORE *store, EVP_PKEY *rpk)
{
if (!X509_STORE_CTX_init(ctx, store, NULL, NULL))
@ -2531,6 +2742,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
ctx->rpk = NULL;
/* Zero ex_data to make sure we're cleanup-safe */
memset(&ctx->ex_data, 0, sizeof(ctx->ex_data));
ctx->ocsp_resp = NULL;
/* store->cleanup is always 0 in OpenSSL, if set must be idempotent */
if (store != NULL)
@ -2690,7 +2902,7 @@ void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags,
void X509_STORE_CTX_set_current_reasons(X509_STORE_CTX *ctx,
unsigned int current_reasons)
{
ctx->current_reasons = current_reasons;
ctx->current_reasons = current_reasons;
}
X509 *X509_STORE_CTX_get0_cert(const X509_STORE_CTX *ctx)
@ -2767,7 +2979,7 @@ X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(const X509_STORE_CTX *ctx)
void X509_STORE_CTX_set_get_crl(X509_STORE_CTX *ctx,
X509_STORE_CTX_get_crl_fn get_crl)
{
ctx->get_crl = get_crl;
ctx->get_crl = get_crl;
}
X509_STORE_CTX_check_crl_fn

View File

@ -94,6 +94,8 @@ B<openssl> B<s_client>
[B<-sess_in> I<filename>]
[B<-serverinfo> I<types>]
[B<-status>]
[B<-ocsp_check_leaf>]
[B<-ocsp_check_all>]
[B<-alpn> I<protocols>]
[B<-nextprotoneg> I<protocols>]
[B<-ct>]
@ -671,6 +673,24 @@ file.
Sends a certificate status request to the server (OCSP stapling). The server
response (if any) is printed out.
=item B<-ocsp_check_leaf>
Require performing server (end-entity) certificate status checking, where any
OCSP response provided in the TLS handshake (by so-called "OCSP stapling") is tried
first.
If no valid and conclusive OCSP response can be found, CRL-based checking
is attempted as fallback if enabled, otherwise the status check fails.
This implies B<-status>.
=item B<-ocsp_check_all>
As the option before, but require performing certificate status checking also
for the issuer chain of the server certificate (i.e., intermediate CA certificates,
excluding the trust anchor).
This implies the B<-status> and B<-ocsp_check_leaf>.
=item B<-alpn> I<protocols>, B<-nextprotoneg> I<protocols>
These flags enable the Enable the Application-Layer Protocol Negotiation
@ -1023,6 +1043,11 @@ B<-no_tx_cert_comp>,
and B<-tfo>
options were added in OpenSSL 3.2.
The
<-ocsp_check_leaf>
and B<-ocsp_check_all>
options were added in OpenSSL 3.6.
=head1 COPYRIGHT
Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.

View File

@ -74,6 +74,7 @@ B<openssl> B<s_server>
[B<-no_ign_eof>]
[B<-no_ems>]
[B<-status>]
[B<-status_all>]
[B<-status_verbose>]
[B<-status_timeout> I<int>]
[B<-proxy> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>]
@ -487,7 +488,15 @@ Disable Extended master secret negotiation.
=item B<-status>
Enables certificate status request support (aka OCSP stapling).
Enables certificate status request support (aka OCSP stapling):
an OCSP response is provided for the leaf (server) certificate
if requested by the client side.
=item B<-status_all>
Like before, but for TLS v1.3 and beyond, status responses for all
certificates in the chain (except the trust anchor) are provided
if requested by the client side.
=item B<-status_verbose>
@ -530,6 +539,8 @@ Any given query component is handled as part of the path component.
Overrides any OCSP responder URLs from the certificate and always provides the
OCSP Response stored in the file. The file must be in DER format.
This option may be used multiple times to specify OCSP responses for all
certificates in the server certificate chain.
=item B<-ssl_config> I<val>
@ -925,6 +936,8 @@ B<-no_tx_cert_comp>,
and B<-tfo>
options were added in OpenSSL 3.2.
The B<-status_all> option was added in OpenSSL 3.6.
=head1 COPYRIGHT
Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.

View File

@ -11,7 +11,9 @@ SSL_CTX_get_tlsext_status_type,
SSL_set_tlsext_status_type,
SSL_get_tlsext_status_type,
SSL_get_tlsext_status_ocsp_resp,
SSL_set_tlsext_status_ocsp_resp
SSL_set_tlsext_status_ocsp_resp,
SSL_get0_tlsext_status_ocsp_resp_ex,
SSL_set0_tlsext_status_ocsp_resp_ex
- OCSP Certificate Status Request functions
=head1 SYNOPSIS
@ -33,9 +35,12 @@ SSL_set_tlsext_status_ocsp_resp
long SSL_get_tlsext_status_ocsp_resp(ssl, unsigned char **resp);
long SSL_set_tlsext_status_ocsp_resp(ssl, unsigned char *resp, int len);
long SSL_get0_tlsext_status_ocsp_resp_ex(ssl, STACK_OF(OCSP_RESPONSE) **resp);
long SSL_set0_tlsext_status_ocsp_resp_ex(ssl, STACK_OF(OCSP_RESPONSE) *resp);
=head1 DESCRIPTION
A client application may request that a server send back an OCSP status response
A client application may request that a server send back OCSP status response(s)
(also known as OCSP stapling). To do so the client should call the
SSL_CTX_set_tlsext_status_type() function prior to the creation of any SSL
objects. Alternatively an application can call the SSL_set_tlsext_status_type()
@ -45,9 +50,13 @@ should be passed in the B<type> argument. Calling
SSL_CTX_get_tlsext_status_type() will return the type B<TLSEXT_STATUSTYPE_ocsp>
previously set via SSL_CTX_set_tlsext_status_type() or -1 if not set.
For TLS versions before 1.3 only a single OCSP status response is sent back
by the server. TLS 1.3 specifies that the server can send OCSP status responses
for the whole chain (OCSP multi-stapling).
The client should additionally provide a callback function to decide what to do
with the returned OCSP response by calling SSL_CTX_set_tlsext_status_cb(). The
callback function should determine whether the returned OCSP response is
callback function should determine whether the returned OCSP response(s) are
acceptable or not. The callback will be passed as an argument the value
previously set via a call to SSL_CTX_set_tlsext_status_arg(). Note that the
callback will not be called in the event of a handshake where session resumption
@ -63,22 +72,48 @@ side SSL_get_tlsext_status_type() can be used to determine whether the client
requested OCSP stapling. If the client requested it then this function will
return B<TLSEXT_STATUSTYPE_ocsp>, or -1 otherwise.
The response returned by the server can be obtained via a call to
SSL_get_tlsext_status_ocsp_resp(). The value B<*resp> will be updated to point
to the OCSP response data and the return value will be the length of that data.
Typically a callback would obtain an OCSP_RESPONSE object from this data via a
call to the d2i_OCSP_RESPONSE() function. If the server has not provided any
response data then B<*resp> will be NULL and the return value from
A single response returned by the server (TLS < 1.3) can be obtained via a call
to SSL_get_tlsext_status_ocsp_resp(). The value B<*resp> will be updated to
point to the OCSP response data and the return value will be the length of that
data. Typically a callback would obtain an OCSP_RESPONSE object from this data
via a call to the d2i_OCSP_RESPONSE() function. If the server has not provided
any response data then B<*resp> will be NULL and the return value from
SSL_get_tlsext_status_ocsp_resp() will be -1.
A server application must also call the SSL_CTX_set_tlsext_status_cb() function
if it wants to be able to provide clients with (single) OCSP response for the
server certificate. Typically the server callback would obtain the server
certificate that is being sent back to the client via a call to
SSL_get_certificate(); retrieve the related OCSP response to be sent back; and
then set that response data by calling SSL_set_tlsext_status_ocsp_resp(). A
pointer to the response data should be provided in the B<resp> argument, and
the length of that data should be in the B<len> argument.
In the case of multi-stapling the responses to be returned by the server can be
obtained via a call to SSL_get0_tlsext_status_ocsp_resp_ex(). The value B<*resp>
will be updated to point to the OCSP response stack and the return value will
be the number of responses on the stack.
The OCSP responses on the stack are expected to be in the same order as the
certificates in the chain. If no OCSP response is available for a certificate
in the chain, a NULL element in the stack will represent this.
Typically a callback would obtain an OCSP_RESPONSE object from the stack via a
call to sk_OCSP_RESPONSE_pop. If the server has not provided any response data
then B<*resp> will be NULL and the return value from
SSL_get0_tlsext_status_ocsp_resp_ex() will be -1.
A server application must also call the SSL_CTX_set_tlsext_status_cb() function
if it wants to be able to provide clients with OCSP Certificate Status
responses. Typically the server callback would obtain the server certificate
that is being sent back to the client via a call to SSL_get_certificate();
obtain the OCSP response to be sent back; and then set that response data by
calling SSL_set_tlsext_status_ocsp_resp(). A pointer to the response data should
be provided in the B<resp> argument, and the length of that data should be in
the B<len> argument.
responses, where TLS 1.3 allows for multi-stapling, i.e., providing responses
for all certificates in the chain of the server certificate (excluding the root
CA certificate).
The certificates sent back to the client and for which OCSP response(s)
should be acquired could be obtained via call to SSL_get_certificate() resp.
SSL_get0_chain_certs(). OCSP response(s) then set by calling
SSL_set0_tlsext_status_ocsp_resp_ex(). A stack of OCSP responses should be
provided in the B<resp> argument.
The OCSP responses on the stack are expected to be in the same order as the
certificate in the chain. If no OCSP response is available for a certificate in
the chain, a NULL element in the stack will represent this.
=head1 RETURN VALUES
@ -93,8 +128,9 @@ returned) or SSL_TLSEXT_ERR_ALERT_FATAL (meaning that a fatal error has
occurred).
SSL_CTX_set_tlsext_status_cb(), SSL_CTX_set_tlsext_status_arg(),
SSL_CTX_set_tlsext_status_type(), SSL_set_tlsext_status_type() and
SSL_CTX_set_tlsext_status_type(), SSL_set_tlsext_status_type(),
SSL_set_tlsext_status_ocsp_resp() return 0 on error or 1 on success.
SSL_set0_tlsext_status_ocsp_resp_ex() will return always 1.
SSL_CTX_get_tlsext_status_type() returns the value previously set by
SSL_CTX_set_tlsext_status_type(), or -1 if not set.
@ -102,6 +138,9 @@ SSL_CTX_set_tlsext_status_type(), or -1 if not set.
SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP response data
or -1 if there is no OCSP response data.
SSL_get0_tlsext_status_ocsp_resp_ex() returns the number of the OCSP responses
on the stack or -1 if there is no OCSP response data.
SSL_get_tlsext_status_type() returns B<TLSEXT_STATUSTYPE_ocsp> on the client
side if SSL_set_tlsext_status_type() was previously called, or on the server
side if the client requested OCSP stapling. Otherwise -1 is returned.
@ -115,6 +154,9 @@ L<ssl(7)>
The SSL_get_tlsext_status_type(), SSL_CTX_get_tlsext_status_type()
and SSL_CTX_set_tlsext_status_type() functions were added in OpenSSL 1.1.0.
The SSL_get0_tlsext_status_ocsp_resp_ex() and SSL_set0_tlsext_status_ocsp_resp_ex()
macros were added in OpenSSL 3.6.
=head1 COPYRIGHT
Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.

View File

@ -440,6 +440,32 @@ Returned by the verify callback to indicate OCSP verification failed.
Returned by the verify callback to indicate that the certificate is not
recognized by the OCSP responder.
=item B<X509_V_ERR_OCSP_RESP_INVALID: OCSP response(s) invalid>
Returned by the verify callback to indicate that one or more OCSP
responses are invalid.
=item B<X509_V_ERR_OCSP_SIGNATURE_FAILURE: OCSP response signature failure>
Returned by the verify callback to indicate OCSP response signature
verification failed.
=item B<X509_V_ERR_OCSP_NOT_YET_VALID: OCSP response not yet valid>
OCSP response not yet valid (contains a date in the future)>
Returned by the verify callback to indicate that OCSP response has a
I<thisUpdate> date in the future.
=item B<X509_V_ERR_OCSP_HAS_EXPIRED: OCSP response has expired>
Returned by the verify callback to indicate that the OCSP response has expired.
=item B<X509_V_ERR_OCSP_NO_RESPONSE:
no OCSP response available for certificate>
Returned by the verify callback to indicate that no OCSP response is available
for the certificate.
=item B<X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM:
unsupported signature algorithm>

View File

@ -16,6 +16,7 @@ X509_STORE_CTX_get0_rpk,
X509_STORE_CTX_set_default,
X509_STORE_CTX_set_verify,
X509_STORE_CTX_verify_fn,
X509_STORE_CTX_set_ocsp_resp,
X509_STORE_CTX_set_purpose,
X509_STORE_CTX_set_trust,
X509_STORE_CTX_purpose_inherit
@ -56,6 +57,7 @@ X509_STORE_CTX_purpose_inherit
typedef int (*X509_STORE_CTX_verify_fn)(X509_STORE_CTX *);
void X509_STORE_CTX_set_verify(X509_STORE_CTX *ctx, X509_STORE_CTX_verify_fn verify);
void X509_STORE_CTX_set_ocsp_resp(X509_STORE_CTX *ctx, STACK_OF(OCSP_RESPONSE) *sk);
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust);
int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
@ -217,6 +219,12 @@ validate extended key usage information in certificates will need to define a
custom "purpose" (see below) or supply a nondefault verification callback
(L<X509_STORE_set_verify_cb_func(3)>).
X509_STORE_CTX_set_ocsp_resp() sets the OCSP response(s) for the verification
of a certificate chain or for including in the TLS handshake, when the client
requests OCSP stapling. The stack of OCSP responses I<sk> is not copied but
just stored to the context.
I<ctx> holds a pointer to the stack, so the stack must outlive the I<ctx>.
X509_STORE_CTX_set_purpose() sets the purpose for the target certificate being
verified in the I<ctx>. Built-in available values for the I<purpose> argument
are B<X509_PURPOSE_SSL_CLIENT>, B<X509_PURPOSE_SSL_SERVER>,
@ -314,6 +322,7 @@ The X509_STORE_CTX_get_num_untrusted() function was added in OpenSSL 1.1.0.
The X509_STORE_CTX_new_ex() function was added in OpenSSL 3.0.
The X509_STORE_CTX_init_rpk(), X509_STORE_CTX_get0_rpk(), and
X509_STORE_CTX_set0_rpk() functions were added in OpenSSL 3.2.
X509_STORE_CTX_set_ocsp_resp() function was added in OpenSSL 3.6.
There is no need to call X509_STORE_CTX_cleanup() explicitly since OpenSSL 3.0.

View File

@ -221,6 +221,7 @@ struct x509_store_ctx_st { /* X509_STORE_CTX */
STACK_OF(X509) *untrusted;
/* set of CRLs passed in */
STACK_OF(X509_CRL) *crls;
STACK_OF(OCSP_RESPONSE) *ocsp_resp;
X509_VERIFY_PARAM *param;
/* Other info for use with get_issuer() */
void *other_ctx;

View File

@ -1342,6 +1342,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
# define SSL_CTRL_GET0_IMPLEMENTED_GROUPS 139
# define SSL_CTRL_GET_SIGNATURE_NAME 140
# define SSL_CTRL_GET_PEER_SIGNATURE_NAME 141
# define SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP_EX 142
# define SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP_EX 143
# define SSL_CERT_SET_FIRST 1
# define SSL_CERT_SET_NEXT 2
# define SSL_CERT_SET_SERVER 3

View File

@ -325,6 +325,12 @@ __owur int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain)
# define SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP,arglen,arg)
# define SSL_get0_tlsext_status_ocsp_resp_ex(ssl, arg) \
SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP_EX, 0, arg)
# define SSL_set0_tlsext_status_ocsp_resp_ex(ssl, arg) \
SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP_EX, 0, arg)
# define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,\
(void (*)(void))cb)

View File

@ -39,6 +39,8 @@ use OpenSSL::stackhash qw(generate_stack_macros);
extern "C" {
#endif
DEFINE_STACK_OF(OCSP_RESPONSE)
/*-
SSL_CTX -> X509_STORE
-> X509_LOOKUP
@ -316,6 +318,14 @@ X509_LOOKUP_ctrl_ex((x), X509_L_ADD_STORE, (name), 0, NULL, \
# define X509_V_ERR_EC_KEY_EXPLICIT_PARAMS 94
# define X509_V_ERR_RPK_UNTRUSTED 95
/* additional OCSP status errors */
# define X509_V_ERR_OCSP_RESP_INVALID 96
# define X509_V_ERR_OCSP_SIGNATURE_FAILURE 97
# define X509_V_ERR_OCSP_NOT_YET_VALID 98
# define X509_V_ERR_OCSP_HAS_EXPIRED 99
# define X509_V_ERR_OCSP_NO_RESPONSE 100
# define X509_V_ERR_CRL_VERIFY_FAILED 101
/* Certificate verify flags */
# ifndef OPENSSL_NO_DEPRECATED_1_1_0
# define X509_V_FLAG_CB_ISSUER_CHECK 0x0 /* Deprecated */
@ -367,6 +377,11 @@ X509_LOOKUP_ctrl_ex((x), X509_L_ADD_STORE, (name), 0, NULL, \
/* Do not check certificate/CRL validity against current time */
# define X509_V_FLAG_NO_CHECK_TIME 0x200000
/* Verify OCSP stapling response for server certificate */
# define X509_V_FLAG_OCSP_RESP_CHECK 0x400000
/* Verify OCSP stapling responses for whole chain */
# define X509_V_FLAG_OCSP_RESP_CHECK_ALL 0x800000
# define X509_VP_FLAG_DEFAULT 0x1
# define X509_VP_FLAG_OVERWRITE 0x2
# define X509_VP_FLAG_RESET_FLAGS 0x4
@ -675,6 +690,9 @@ void X509_STORE_CTX_set_cert(X509_STORE_CTX *ctx, X509 *target);
void X509_STORE_CTX_set0_rpk(X509_STORE_CTX *ctx, EVP_PKEY *target);
void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *c, STACK_OF(X509) *sk);
void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk);
# ifndef OPENSSL_NO_OCSP
void X509_STORE_CTX_set_ocsp_resp(X509_STORE_CTX *ctx, STACK_OF(OCSP_RESPONSE) *sk);
# endif
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust);
int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,

View File

@ -22,6 +22,7 @@
#include <openssl/core_names.h>
#include "internal/cryptlib.h"
#include "internal/ssl_unwrap.h"
#include <openssl/ocsp.h>
#define TLS13_NUM_CIPHERS OSSL_NELEM(tls13_ciphers)
#define SSL3_NUM_CIPHERS OSSL_NELEM(ssl3_ciphers)
@ -3534,6 +3535,10 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
{
int ret = 0;
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
#ifndef OPENSSL_NO_OCSP
unsigned char *p = NULL;
OCSP_RESPONSE *resp = NULL;
#endif
if (sc == NULL)
return ret;
@ -3666,16 +3671,79 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
break;
case SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP:
*(unsigned char **)parg = sc->ext.ocsp.resp;
if (sc->ext.ocsp.resp_len == 0
|| sc->ext.ocsp.resp_len > LONG_MAX)
return -1;
return (long)sc->ext.ocsp.resp_len;
*(unsigned char **)parg = NULL;
ret = -1;
#ifndef OPENSSL_NO_OCSP
resp = sk_OCSP_RESPONSE_value(sc->ext.ocsp.resp_ex, 0);
if (resp != NULL) {
int resp_len = i2d_OCSP_RESPONSE(resp, &p);
if (resp_len > 0) {
OPENSSL_free(sc->ext.ocsp.resp);
*(unsigned char **)parg = sc->ext.ocsp.resp = p;
sc->ext.ocsp.resp_len = (size_t)resp_len;
ret = resp_len;
}
}
#endif
break;
case SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP:
OPENSSL_free(sc->ext.ocsp.resp);
sc->ext.ocsp.resp = parg;
sc->ext.ocsp.resp_len = larg;
ret = 1;
#ifndef OPENSSL_NO_OCSP
/*
* cleanup single values, which might be set somewhere else
* we only use the extended values
*/
if (sc->ext.ocsp.resp != NULL) {
OPENSSL_free(sc->ext.ocsp.resp);
sc->ext.ocsp.resp = NULL;
sc->ext.ocsp.resp_len = 0;
}
sk_OCSP_RESPONSE_pop_free(sc->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
sc->ext.ocsp.resp_ex = NULL;
if (parg != NULL) {
sc->ext.ocsp.resp_ex = sk_OCSP_RESPONSE_new_reserve(NULL, 1);
if (sc->ext.ocsp.resp_ex == NULL)
return 0;
p = parg;
resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p, larg);
if (resp != NULL)
sk_OCSP_RESPONSE_push(sc->ext.ocsp.resp_ex, resp);
}
#endif
break;
case SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP_EX:
#ifndef OPENSSL_NO_OCSP
*(STACK_OF(OCSP_RESPONSE) **)parg = sc->ext.ocsp.resp_ex;
ret = sk_OCSP_RESPONSE_num(sc->ext.ocsp.resp_ex);
#else
*(unsigned char **)parg = NULL;
ret = -1;
#endif
break;
case SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP_EX:
#ifndef OPENSSL_NO_OCSP
/*
* cleanup single values, which might be set somewhere else
* we only use the extended values
*/
if (sc->ext.ocsp.resp != NULL) {
OPENSSL_free(sc->ext.ocsp.resp);
sc->ext.ocsp.resp = NULL;
sc->ext.ocsp.resp_len = 0;
}
sk_OCSP_RESPONSE_pop_free(sc->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
sc->ext.ocsp.resp_ex = (STACK_OF(OCSP_RESPONSE) *)parg;
#endif
ret = 1;
break;

View File

@ -433,6 +433,9 @@ static int ssl_verify_internal(SSL_CONNECTION *s, STACK_OF(X509) *sk, EVP_PKEY *
X509_STORE_CTX *ctx = NULL;
X509_VERIFY_PARAM *param;
SSL_CTX *sctx;
#ifndef OPENSSL_NO_OCSP
SSL *ssl;
#endif
/* Something must be passed in */
if ((sk == NULL || sk_X509_num(sk) == 0) && rpk == NULL)
@ -486,6 +489,26 @@ static int ssl_verify_internal(SSL_CONNECTION *s, STACK_OF(X509) *sk, EVP_PKEY *
if (DANETLS_ENABLED(&s->dane))
X509_STORE_CTX_set0_dane(ctx, &s->dane);
/*
* Set OCSP Responses for verification:
* This function is called in the SERVER_CERTIFICATE message, in TLS 1.2
* the OCSP responses are sent in the CERT_STATUS message after that.
* Therefore the verification code currently only works in TLS 1.3.
*/
#ifndef OPENSSL_NO_OCSP
ssl = SSL_CONNECTION_GET_SSL(s);
/*
* TODO(DTLS-1.3): in future DTLS should also be considered
*/
if (!SSL_is_dtls(ssl) && SSL_version(ssl) >= TLS1_3_VERSION) {
/* ignore status_request_v2 if TLS version < 1.3 */
int status = SSL_get_tlsext_status_type(ssl);
if (status == TLSEXT_STATUSTYPE_ocsp)
X509_STORE_CTX_set_ocsp_resp(ctx, s->ext.ocsp.resp_ex);
}
#endif
/*
* We need to inherit the verify parameters. These can be determined by
* the context: if its a server it will verify SSL client certificates or
@ -555,7 +578,7 @@ int ssl_verify_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk)
}
static void set0_CA_list(STACK_OF(X509_NAME) **ca_list,
STACK_OF(X509_NAME) *name_list)
STACK_OF(X509_NAME) *name_list)
{
sk_X509_NAME_pop_free(*ca_list, X509_NAME_free);
*ca_list = name_list;

View File

@ -831,6 +831,7 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl,
s->ext.ocsp.exts = NULL;
s->ext.ocsp.resp = NULL;
s->ext.ocsp.resp_len = 0;
s->ext.ocsp.resp_ex = NULL;
if (!SSL_CTX_up_ref(ctx))
goto err;
@ -1497,14 +1498,20 @@ void ossl_ssl_connection_free(SSL *ssl)
OPENSSL_free(s->ext.tuples);
OPENSSL_free(s->ext.peer_supportedgroups);
sk_X509_EXTENSION_pop_free(s->ext.ocsp.exts, X509_EXTENSION_free);
#ifndef OPENSSL_NO_OCSP
OPENSSL_free(s->ext.ocsp.resp);
s->ext.ocsp.resp = NULL;
s->ext.ocsp.resp_len = 0;
sk_OCSP_RESPID_pop_free(s->ext.ocsp.ids, OCSP_RESPID_free);
sk_OCSP_RESPONSE_pop_free(s->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
s->ext.ocsp.resp_ex = NULL;
#endif
#ifndef OPENSSL_NO_CT
SCT_LIST_free(s->scts);
OPENSSL_free(s->ext.scts);
#endif
OPENSSL_free(s->ext.ocsp.resp);
OPENSSL_free(s->ext.alpn);
OPENSSL_free(s->ext.tls13_cookie);
if (s->clienthello != NULL)
@ -6428,41 +6435,59 @@ static int ct_extract_ocsp_response_scts(SSL_CONNECTION *s)
{
# ifndef OPENSSL_NO_OCSP
int scts_extracted = 0;
const unsigned char *p;
OCSP_BASICRESP *br = NULL;
OCSP_RESPONSE *rsp = NULL;
STACK_OF(SCT) *scts = NULL;
int i;
int ret;
int i, j;
if (s->ext.ocsp.resp == NULL || s->ext.ocsp.resp_len == 0)
if (s->ext.ocsp.resp_ex == NULL)
goto err;
p = s->ext.ocsp.resp;
rsp = d2i_OCSP_RESPONSE(NULL, &p, (int)s->ext.ocsp.resp_len);
if (rsp == NULL)
goto err;
br = OCSP_response_get1_basic(rsp);
if (br == NULL)
goto err;
for (i = 0; i < OCSP_resp_count(br); ++i) {
OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
if (single == NULL)
continue;
scts =
OCSP_SINGLERESP_get1_ext_d2i(single, NID_ct_cert_scts, NULL, NULL);
scts_extracted =
ct_move_scts(&s->scts, scts, SCT_SOURCE_OCSP_STAPLED_RESPONSE);
if (scts_extracted < 0)
for (j = 0; j < sk_OCSP_RESPONSE_num(s->ext.ocsp.resp_ex); j++) {
rsp = sk_OCSP_RESPONSE_value(s->ext.ocsp.resp_ex, j);
if (rsp == NULL)
goto err;
br = OCSP_response_get1_basic(rsp);
if (br == NULL)
goto err;
for (i = 0; i < OCSP_resp_count(br); ++i) {
OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
if (single == NULL)
continue;
scts = OCSP_SINGLERESP_get1_ext_d2i(single,
NID_ct_cert_scts, NULL, NULL);
OCSP_SINGLERESP_free(single);
if (scts == NULL) {
scts_extracted = -1;
goto err;
}
ret = ct_move_scts(&s->scts, scts,
SCT_SOURCE_OCSP_STAPLED_RESPONSE);
SCT_LIST_free(scts);
if (ret < 0) {
scts_extracted = -1;
goto err;
}
scts_extracted += ret;
}
OCSP_BASICRESP_free(br);
/* to assure that is not freed twice */
br = NULL;
}
err:
SCT_LIST_free(scts);
OCSP_BASICRESP_free(br);
OCSP_RESPONSE_free(rsp);
return scts_extracted;
# else
/* Behave as if no OCSP response exists */

View File

@ -1639,6 +1639,7 @@ struct ssl_connection_st {
/* OCSP response received or to be sent */
unsigned char *resp;
size_t resp_len;
STACK_OF(OCSP_RESPONSE) *resp_ex;
} ocsp;
/* RFC4507 session ticket expected to be received or sent */
@ -2594,6 +2595,7 @@ void ssl_cert_set_cert_cb(CERT *c, int (*cb) (SSL *ssl, void *arg), void *arg);
__owur int ssl_verify_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk);
__owur int ssl_verify_rpk(SSL_CONNECTION *s, EVP_PKEY *rpk);
__owur int ssl_verify_ocsp(SSL *s, STACK_OF(X509) *sk);
__owur int ssl_build_cert_chain(SSL_CONNECTION *s, SSL_CTX *ctx, int flags);
__owur int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain,
int ref);

View File

@ -18,6 +18,7 @@
#include "internal/ssl_unwrap.h"
#include "../ssl_local.h"
#include "statem_local.h"
#include <openssl/ocsp.h>
static int final_renegotiate(SSL_CONNECTION *s, unsigned int context, int sent);
static int init_server_name(SSL_CONNECTION *s, unsigned int context);
@ -1148,6 +1149,9 @@ static int init_status_request(SSL_CONNECTION *s, unsigned int context)
OPENSSL_free(s->ext.ocsp.resp);
s->ext.ocsp.resp = NULL;
s->ext.ocsp.resp_len = 0;
sk_OCSP_RESPONSE_pop_free(s->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
s->ext.ocsp.resp_ex = NULL;
}
return 1;

View File

@ -1510,14 +1510,8 @@ int tls_parse_stoc_status_request(SSL_CONNECTION *s, PACKET *pkt,
}
if (SSL_CONNECTION_IS_TLS13(s)) {
/* We only know how to handle this if it's for the first Certificate in
* the chain. We ignore any other responses.
*/
if (chainidx != 0)
return 1;
/* SSLfatal() already called */
return tls_process_cert_status_body(s, pkt);
return tls_process_cert_status_body(s, chainidx, pkt);
}
/* Set flag to expect CertificateStatus message */

View File

@ -1751,9 +1751,6 @@ EXT_RETURN tls_construct_stoc_status_request(SSL_CONNECTION *s, WPACKET *pkt,
if (!s->ext.status_expected)
return EXT_RETURN_NOT_SENT;
if (SSL_CONNECTION_IS_TLS13(s) && chainidx != 0)
return EXT_RETURN_NOT_SENT;
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_status_request)
|| !WPACKET_start_sub_packet_u16(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
@ -1765,9 +1762,10 @@ EXT_RETURN tls_construct_stoc_status_request(SSL_CONNECTION *s, WPACKET *pkt,
* send back an empty extension, with the certificate status appearing as a
* separate message
*/
if (SSL_CONNECTION_IS_TLS13(s) && !tls_construct_cert_status_body(s, pkt)) {
/* SSLfatal() already called */
return EXT_RETURN_FAIL;
if (SSL_CONNECTION_IS_TLS13(s)
&& !tls_construct_cert_status_body(s, chainidx, pkt)) {
/* SSLfatal() already called */
return EXT_RETURN_FAIL;
}
if (!WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);

View File

@ -29,6 +29,7 @@
#include "internal/cryptlib.h"
#include "internal/comp.h"
#include "internal/ssl_unwrap.h"
#include <openssl/ocsp.h>
static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
PACKET *pkt);
@ -2900,40 +2901,77 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL_CONNECTION *s,
* In TLSv1.3 this is called from the extensions code, otherwise it is used to
* parse a separate message. Returns 1 on success or 0 on failure
*/
int tls_process_cert_status_body(SSL_CONNECTION *s, PACKET *pkt)
int tls_process_cert_status_body(SSL_CONNECTION *s, size_t chainidx, PACKET *pkt)
{
size_t resplen;
unsigned int type;
#ifndef OPENSSL_NO_OCSP
size_t resplen;
unsigned char *respder;
OCSP_RESPONSE *resp = NULL;
const unsigned char *p;
#endif
if (!PACKET_get_1(pkt, &type)
|| type != TLSEXT_STATUSTYPE_ocsp) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_UNSUPPORTED_STATUS_TYPE);
return 0;
}
if (!PACKET_get_net_3_len(pkt, &resplen)
|| PACKET_remaining(pkt) != resplen) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
return 0;
}
s->ext.ocsp.resp = OPENSSL_malloc(resplen);
if (s->ext.ocsp.resp == NULL) {
s->ext.ocsp.resp_len = 0;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
return 0;
}
s->ext.ocsp.resp_len = resplen;
if (!PACKET_copy_bytes(pkt, s->ext.ocsp.resp, resplen)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
return 0;
#ifndef OPENSSL_NO_OCSP
OPENSSL_free(s->ext.ocsp.resp);
s->ext.ocsp.resp = NULL;
s->ext.ocsp.resp_len = 0;
if (s->ext.ocsp.resp_ex == NULL)
s->ext.ocsp.resp_ex = sk_OCSP_RESPONSE_new_null();
/*
* TODO(DTLS-1.3): in future DTLS should also be considered
*/
if (!SSL_CONNECTION_IS_TLS13(s) && type == TLSEXT_STATUSTYPE_ocsp) {
sk_OCSP_RESPONSE_pop_free(s->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
s->ext.ocsp.resp_ex = sk_OCSP_RESPONSE_new_null();
}
if (PACKET_remaining(pkt) > 0) {
if (!PACKET_get_net_3_len(pkt, &resplen)
|| PACKET_remaining(pkt) != resplen) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
return 0;
}
if (resplen > 0) {
respder = OPENSSL_malloc(resplen);
if (respder == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
return 0;
}
if (!PACKET_copy_bytes(pkt, respder, resplen)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
OPENSSL_free(respder);
return 0;
}
p = respder;
resp = d2i_OCSP_RESPONSE(NULL, &p, (long)resplen);
OPENSSL_free(respder);
if (resp == NULL) {
SSLfatal(s, TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE,
SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE);
return 0;
}
sk_OCSP_RESPONSE_insert(s->ext.ocsp.resp_ex, resp, (int)chainidx);
}
}
#endif
return 1;
}
MSG_PROCESS_RETURN tls_process_cert_status(SSL_CONNECTION *s, PACKET *pkt)
{
if (!tls_process_cert_status_body(s, pkt)) {
if (!tls_process_cert_status_body(s, 0, pkt)) {
/* SSLfatal() already called */
return MSG_PROCESS_ERROR;
}

View File

@ -148,7 +148,7 @@ __owur MSG_PROCESS_RETURN tls_process_certificate_request(SSL_CONNECTION *s,
PACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL_CONNECTION *s,
PACKET *pkt);
__owur int tls_process_cert_status_body(SSL_CONNECTION *s, PACKET *pkt);
__owur int tls_process_cert_status_body(SSL_CONNECTION *s, size_t chainidx, PACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_cert_status(SSL_CONNECTION *s,
PACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_server_done(SSL_CONNECTION *s,
@ -168,7 +168,7 @@ __owur int ssl_do_client_cert_cb(SSL_CONNECTION *s, X509 **px509,
__owur CON_FUNC_RETURN tls_construct_client_key_exchange(SSL_CONNECTION *s,
WPACKET *pkt);
__owur int tls_client_key_exchange_post_work(SSL_CONNECTION *s);
__owur int tls_construct_cert_status_body(SSL_CONNECTION *s, WPACKET *pkt);
__owur int tls_construct_cert_status_body(SSL_CONNECTION *s, size_t chainidx, WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_cert_status(SSL_CONNECTION *s,
WPACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_key_exchange(SSL_CONNECTION *s,

View File

@ -31,6 +31,7 @@
#include <openssl/asn1t.h>
#include <openssl/comp.h>
#include "internal/comp.h"
#include <openssl/ocsp.h>
#define TICKET_NONCE_SIZE 8
@ -2193,8 +2194,11 @@ static int tls_handle_status_request(SSL_CONNECTION *s)
break;
/* status request response should be sent */
case SSL_TLSEXT_ERR_OK:
if (s->ext.ocsp.resp)
#ifndef OPENSSL_NO_OCSP
if (s->ext.ocsp.resp_ex != NULL
&& sk_OCSP_RESPONSE_num(s->ext.ocsp.resp_ex) > 0)
s->ext.status_expected = 1;
#endif
break;
/* something bad happened */
case SSL_TLSEXT_ERR_ALERT_FATAL:
@ -2298,6 +2302,7 @@ WORK_STATE tls_post_process_client_hello(SSL_CONNECTION *s, WORK_STATE wst)
if (wst == WORK_MORE_A) {
int rv = tls_early_post_process_client_hello(s);
if (rv == 0) {
/* SSLfatal() was already called */
goto err;
@ -4327,21 +4332,149 @@ CON_FUNC_RETURN tls_construct_new_session_ticket(SSL_CONNECTION *s, WPACKET *pkt
* In TLSv1.3 this is called from the extensions code, otherwise it is used to
* create a separate message. Returns 1 on success or 0 on failure.
*/
int tls_construct_cert_status_body(SSL_CONNECTION *s, WPACKET *pkt)
int tls_construct_cert_status_body(SSL_CONNECTION *s, size_t chainidx, WPACKET *pkt)
{
if (!WPACKET_put_bytes_u8(pkt, s->ext.status_type)
|| !WPACKET_sub_memcpy_u24(pkt, s->ext.ocsp.resp,
s->ext.ocsp.resp_len)) {
unsigned char *respder = NULL;
int resplen = 0;
#ifndef OPENSSL_NO_OCSP
int i = 0, num = 0;
unsigned int len;
X509 *x = NULL;
STACK_OF(X509) *chain_certs = NULL;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
OCSP_RESPONSE *resp = NULL;
OCSP_BASICRESP *bs = NULL;
OCSP_SINGLERESP *sr = NULL;
OCSP_CERTID *cid = NULL;
OCSP_CERTID *sr_cert_id = NULL;
ASN1_OBJECT *cert_id_md_oid;
const EVP_MD *cert_id_md;
ASN1_INTEGER *respSerial;
ASN1_OCTET_STRING *respIssuerNameHash;
ASN1_OCTET_STRING *certIssuerNameHash;
const X509_NAME *certIssuerName;
unsigned char md[EVP_MAX_MD_SIZE];
const ASN1_INTEGER *certSerial;
#endif
if (!WPACKET_put_bytes_u8(pkt, s->ext.status_type)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
#ifndef OPENSSL_NO_OCSP
/*
* In TLSv1.3 the caller gives the index of the certificate for which the
* status message should be created.
* Prior to TLSv1.3 the chain index is 0 and the body should contain only
* the status of the server certificate itself.
*/
SSL_get0_chain_certs(ssl, &chain_certs);
/*
* if the certificate chain was built, get the status message for the
* requested certificate specified by chainidx SSL_get0_chain_certs
* contains certificate chain except the server cert
*
* if chainidx = 0 the server certificate is requested
* if chainidx > 0 an intermediate certificate is requested
*/
if (chain_certs != NULL && (int)chainidx <= sk_X509_num(chain_certs) && chainidx > 0)
x = sk_X509_value(chain_certs, (int)chainidx - 1);
else
x = SSL_get_certificate(ssl);
if (x == NULL)
return 0;
/* for a selfsigned certificate there will be no OCSP response */
if (X509_self_signed(x, 0))
return 1;
if ((resp = sk_OCSP_RESPONSE_value(s->ext.ocsp.resp_ex, (int)chainidx)) != NULL) {
/*
* check if its the right response in the case it is a successful response
* as not every time the issuer certificate is available the check just
* uses the issuer name and the serial number from the current certificate
*/
if (OCSP_response_status(resp) == OCSP_RESPONSE_STATUS_SUCCESSFUL) {
/*
* set a mark for the error queue her to be able to ignore errors
* happening because of test cases
*/
ERR_set_mark();
if (((bs = OCSP_response_get1_basic(resp)) != NULL)
&& ((sr = OCSP_resp_get0(bs, 0)) != NULL)) {
/* use the first single response to get the algorithm used */
cid = (OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sr);
OCSP_id_get0_info(&respIssuerNameHash, &cert_id_md_oid, NULL, &respSerial, cid);
if (cert_id_md_oid != NULL)
cert_id_md = EVP_get_digestbyobj(cert_id_md_oid);
else
cert_id_md = EVP_sha1();
/* get serial number and issuer name hash of the certificate from the chain */
certSerial = X509_get0_serialNumber(x);
certIssuerName = X509_get_issuer_name(x);
certIssuerNameHash = ASN1_OCTET_STRING_new();
if (!X509_NAME_digest(certIssuerName, cert_id_md, md, &len) ||
!(ASN1_OCTET_STRING_set(certIssuerNameHash, md, len))) {
ASN1_OCTET_STRING_free(certIssuerNameHash);
OCSP_BASICRESP_free(bs);
ERR_clear_last_mark();
return 0;
}
num = OCSP_resp_count(bs);
for (i = 0; i < num; i++) {
sr = OCSP_resp_get0(bs, i);
/* determine the md algorithm which was used to create cert id */
sr_cert_id = (OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sr);
OCSP_id_get0_info(&respIssuerNameHash, NULL, NULL, &respSerial, sr_cert_id);
if (!ASN1_INTEGER_cmp(certSerial, respSerial) &&
!ASN1_OCTET_STRING_cmp(certIssuerNameHash, respIssuerNameHash))
break;
}
ASN1_OCTET_STRING_free(certIssuerNameHash);
OCSP_BASICRESP_free(bs);
/*
* if we did not find the right single response in the OCSP response we
* construct an empty message
*/
if (i == num)
resp = NULL;
}
/*
* in a test case a response without a basic response is used the error set
* could be ignored here
*/
ERR_pop_to_mark();
}
}
if (resp != NULL)
resplen = i2d_OCSP_RESPONSE(resp, &respder);
#endif
if (!WPACKET_sub_memcpy_u24(pkt, respder, resplen)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(respder);
return 0;
}
OPENSSL_free(respder);
return 1;
}
CON_FUNC_RETURN tls_construct_cert_status(SSL_CONNECTION *s, WPACKET *pkt)
{
if (!tls_construct_cert_status_body(s, pkt)) {
if (!tls_construct_cert_status_body(s, 0, pkt)) {
/* SSLfatal() already called */
return CON_FUNC_ERROR;
}

View File

@ -13,6 +13,7 @@
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
#include <openssl/core_names.h>
#include <openssl/ocsp.h>
#include "../../ssl/ssl_local.h"
#include "internal/ssl_unwrap.h"
@ -265,47 +266,100 @@ static int client_hello_nov12_cb(SSL *s, int *al, void *arg)
return SSL_CLIENT_HELLO_SUCCESS;
}
static unsigned char dummy_ocsp_resp_good_val = 0xff;
static unsigned char dummy_ocsp_resp_bad_val = 0xfe;
#ifndef OPENSSL_NO_OCSP
static OCSP_RESPONSE *dummy_ocsp_resp = NULL;
static STACK_OF(OCSP_RESPONSE) *dummy_sk_resp = NULL;
static int server_ocsp_cb(SSL *s, void *arg)
{
unsigned char *resp;
unsigned char *respder = NULL;
int resplen = 0;
resplen = i2d_OCSP_RESPONSE(arg, &respder);
resp = OPENSSL_malloc(1);
if (resp == NULL)
return SSL_TLSEXT_ERR_ALERT_FATAL;
/*
* For the purposes of testing we just send back a dummy OCSP response
*/
*resp = *(unsigned char *)arg;
if (!SSL_set_tlsext_status_ocsp_resp(s, resp, 1)) {
OPENSSL_free(resp);
if (!SSL_set_tlsext_status_ocsp_resp(s, respder, resplen)) {
OPENSSL_free(respder);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
OPENSSL_free(respder);
return SSL_TLSEXT_ERR_OK;
}
static int server_ocsp_cb_ext(SSL *s, void *arg)
{
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
/*
* For the purposes of testing we just send back a dummy OCSP response
*/
sk_resp = (STACK_OF(OCSP_RESPONSE) *)arg;
if (!SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp))
return SSL_TLSEXT_ERR_ALERT_FATAL;
return SSL_TLSEXT_ERR_OK;
}
static int client_ocsp_cb(SSL *s, void *arg)
{
const unsigned char *resp;
int len;
const unsigned char *resp, *p;
OCSP_RESPONSE *rsp;
int len, status;
len = SSL_get_tlsext_status_ocsp_resp(s, &resp);
if (len != 1 || *resp != dummy_ocsp_resp_good_val)
return 0;
return 1;
p = resp;
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
status = OCSP_response_status(rsp);
OCSP_RESPONSE_free(rsp);
SSL_set_tlsext_status_ocsp_resp(s, NULL, 0);
OCSP_RESPONSE_free(dummy_ocsp_resp);
return status == OCSP_RESPONSE_STATUS_SUCCESSFUL;
}
static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) {
static int client_ocsp_cb_ext(SSL *s, void *arg)
{
int len, status;
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
OCSP_RESPONSE *rsp;
SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
if (sk_resp == NULL)
return 0;
len = sk_OCSP_RESPONSE_num(sk_resp);
if (len != 1)
return 0;
rsp = sk_OCSP_RESPONSE_value(sk_resp, 0);
status = OCSP_response_status(rsp);
SSL_set0_tlsext_status_ocsp_resp_ex(s, NULL);
return status == OCSP_RESPONSE_STATUS_SUCCESSFUL;
}
#endif
static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg)
{
X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
return 0;
}
static int n_retries = 0;
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg) {
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg)
{
int idx = SSL_get_ex_data_X509_STORE_CTX_idx();
SSL *ssl;
@ -320,7 +374,8 @@ static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg) {
return SSL_set_retry_verify(ssl);
}
static int verify_accept_cb(X509_STORE_CTX *ctx, void *arg) {
static int verify_accept_cb(X509_STORE_CTX *ctx, void *arg)
{
return 1;
}
@ -566,13 +621,67 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
}
if (extra->server.cert_status != SSL_TEST_CERT_STATUS_NONE) {
SSL_CTX_set_tlsext_status_type(client_ctx, TLSEXT_STATUSTYPE_ocsp);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
SSL_CTX_set_tlsext_status_arg(client_ctx, NULL);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
SSL_CTX_set_tlsext_status_arg(server_ctx,
((extra->server.cert_status == SSL_TEST_CERT_STATUS_GOOD_RESPONSE)
? &dummy_ocsp_resp_good_val : &dummy_ocsp_resp_bad_val));
#ifndef OPENSSL_NO_OCSP
switch (extra->server.cert_status) {
case SSL_TEST_CERT_STATUS_GOOD_RESPONSE:
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_ocsp_resp);
break;
case SSL_TEST_CERT_STATUS_BAD_RESPONSE:
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_INTERNALERROR, NULL);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_ocsp_resp);
break;
case SSL_TEST_CERT_STATUS_GOOD_RESPONSE_EXT:
dummy_sk_resp = sk_OCSP_RESPONSE_new_null();
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
sk_OCSP_RESPONSE_push(dummy_sk_resp, dummy_ocsp_resp);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb_ext);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb_ext);
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_sk_resp);
break;
case SSL_TEST_CERT_STATUS_BAD_RESPONSE_EXT:
dummy_sk_resp = sk_OCSP_RESPONSE_new_null();
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_INTERNALERROR, NULL);
sk_OCSP_RESPONSE_push(dummy_sk_resp, dummy_ocsp_resp);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb_ext);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb_ext);
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_sk_resp);
break;
default:
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
SSL_CTX_set_tlsext_status_arg(server_ctx, &dummy_ocsp_resp);
break;
}
#endif
}
/*

View File

@ -460,7 +460,9 @@ IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, enable_server_sctp_label_bug)
static const test_enum ssl_certstatus[] = {
{"None", SSL_TEST_CERT_STATUS_NONE},
{"GoodResponse", SSL_TEST_CERT_STATUS_GOOD_RESPONSE},
{"BadResponse", SSL_TEST_CERT_STATUS_BAD_RESPONSE}
{"BadResponse", SSL_TEST_CERT_STATUS_BAD_RESPONSE},
{"GoodResponseExt", SSL_TEST_CERT_STATUS_GOOD_RESPONSE_EXT},
{"BadResponseExt", SSL_TEST_CERT_STATUS_BAD_RESPONSE_EXT}
};
__owur static int parse_certstatus(SSL_TEST_SERVER_CONF *server_conf,

View File

@ -88,7 +88,9 @@ typedef enum {
typedef enum {
SSL_TEST_CERT_STATUS_NONE = 0, /* Default */
SSL_TEST_CERT_STATUS_GOOD_RESPONSE,
SSL_TEST_CERT_STATUS_BAD_RESPONSE
SSL_TEST_CERT_STATUS_BAD_RESPONSE,
SSL_TEST_CERT_STATUS_GOOD_RESPONSE_EXT,
SSL_TEST_CERT_STATUS_BAD_RESPONSE_EXT
} ssl_cert_status_t;
/*

View File

@ -1,9 +1,11 @@
# Generated with generate_ssl_tests.pl
num_tests = 2
num_tests = 4
test-0 = 0-certstatus-good
test-1 = 1-certstatus-bad
test-2 = 2-certstatus-good-ext
test-3 = 3-certstatus-bad-ext
# ===========================================================
[0-certstatus-good]
@ -60,3 +62,59 @@ server = 1-certstatus-bad-server-extra
CertStatus = BadResponse
# ===========================================================
[2-certstatus-good-ext]
ssl_conf = 2-certstatus-good-ext-ssl
[2-certstatus-good-ext-ssl]
server = 2-certstatus-good-ext-server
client = 2-certstatus-good-ext-client
[2-certstatus-good-ext-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[2-certstatus-good-ext-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-2]
ExpectedResult = Success
Method = TLS
server = 2-certstatus-good-ext-server-extra
[2-certstatus-good-ext-server-extra]
CertStatus = GoodResponseExt
# ===========================================================
[3-certstatus-bad-ext]
ssl_conf = 3-certstatus-bad-ext-ssl
[3-certstatus-bad-ext-ssl]
server = 3-certstatus-bad-ext-server
client = 3-certstatus-bad-ext-client
[3-certstatus-bad-ext-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[3-certstatus-bad-ext-client]
CipherString = DEFAULT
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-3]
ExpectedResult = ClientFail
Method = TLS
server = 3-certstatus-bad-ext-server-extra
[3-certstatus-bad-ext-server-extra]
CertStatus = BadResponseExt

View File

@ -42,4 +42,30 @@ our @tests = (
"ExpectedResult" => "ClientFail"
}
},
{
name => "certstatus-good-ext",
server => {
extra => {
"CertStatus" => "GoodResponseExt",
},
},
client => {},
test => {
"Method" => "TLS",
"ExpectedResult" => "Success"
}
},
{
name => "certstatus-bad-ext",
server => {
extra => {
"CertStatus" => "BadResponseExt",
},
},
client => {},
test => {
"Method" => "TLS",
"ExpectedResult" => "ClientFail"
}
},
);

View File

@ -1,9 +1,11 @@
# Generated with generate_ssl_tests.pl
num_tests = 2
num_tests = 4
test-0 = 0-certstatus-good
test-1 = 1-certstatus-bad
test-2 = 2-certstatus-good-ext
test-3 = 3-certstatus-bad-ext
# ===========================================================
[0-certstatus-good]
@ -60,3 +62,59 @@ server = 1-certstatus-bad-server-extra
CertStatus = BadResponse
# ===========================================================
[2-certstatus-good-ext]
ssl_conf = 2-certstatus-good-ext-ssl
[2-certstatus-good-ext-ssl]
server = 2-certstatus-good-ext-server
client = 2-certstatus-good-ext-client
[2-certstatus-good-ext-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT:@SECLEVEL=0
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[2-certstatus-good-ext-client]
CipherString = DEFAULT:@SECLEVEL=0
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-2]
ExpectedResult = Success
Method = DTLS
server = 2-certstatus-good-ext-server-extra
[2-certstatus-good-ext-server-extra]
CertStatus = GoodResponseExt
# ===========================================================
[3-certstatus-bad-ext]
ssl_conf = 3-certstatus-bad-ext-ssl
[3-certstatus-bad-ext-ssl]
server = 3-certstatus-bad-ext-server
client = 3-certstatus-bad-ext-client
[3-certstatus-bad-ext-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT:@SECLEVEL=0
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[3-certstatus-bad-ext-client]
CipherString = DEFAULT:@SECLEVEL=0
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-3]
ExpectedResult = ClientFail
Method = DTLS
server = 3-certstatus-bad-ext-server-extra
[3-certstatus-bad-ext-server-extra]
CertStatus = BadResponseExt

View File

@ -51,6 +51,38 @@ our @tests_standard = (
"Method" => "DTLS",
"ExpectedResult" => "ClientFail"
}
},
{
name => "certstatus-good-ext",
server => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
extra => {
"CertStatus" => "GoodResponseExt"
},
},
client => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
},
test => {
"Method" => "DTLS",
"ExpectedResult" => "Success"
}
},
{
name => "certstatus-bad-ext",
server => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
extra => {
"CertStatus" => "BadResponseExt",
},
},
client => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
},
test => {
"Method" => "DTLS",
"ExpectedResult" => "ClientFail"
}
}
);
@ -89,6 +121,40 @@ our @tests_sctp = (
"ExpectedResult" => "ClientFail"
}
},
{
name => "certstatus-good-ext",
server => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
extra => {
"CertStatus" => "GoodResponseExt",
},
},
client => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
},
test => {
"Method" => "DTLS",
"UseSCTP" => "Yes",
"ExpectedResult" => "Success"
}
},
{
name => "certstatus-bad-ext",
server => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
extra => {
"CertStatus" => "BadResponseExt",
},
},
client => {
"CipherString" => "DEFAULT:\@SECLEVEL=0",
},
test => {
"Method" => "DTLS",
"UseSCTP" => "Yes",
"ExpectedResult" => "ClientFail"
}
},
);
if (!$fips_mode || !disabled("dtls1_2")) {

View File

@ -82,6 +82,13 @@ static int find_session_cb_cnt = 0;
static int end_of_early_data = 0;
#endif
#ifndef OPENSSL_NO_OCSP
static int test_tlsext_status_type(void);
# ifndef OSSL_NO_USABLE_TLS1_3
static int test_tlsext_status_type_multi(void);
# endif
#endif
static char *certsdir = NULL;
static char *cert = NULL;
static char *privkey = NULL;
@ -110,10 +117,11 @@ static size_t client_log_buffer_index = 0;
static int error_writing_log = 0;
#ifndef OPENSSL_NO_OCSP
static const unsigned char orespder[] = "Dummy OCSP Response";
static int ocsp_server_called = 0;
static int ocsp_client_called = 0;
# ifndef OSSL_NO_USABLE_TLS1_3
static int ocsp_verify_cb_called = 0;
# endif
static int cdummyarg = 1;
static X509 *ocspcert = NULL;
#endif
@ -1734,7 +1742,7 @@ static int execute_cleanse_plaintext(const SSL_METHOD *smeth,
if (is_fips) {
testresult = 1;
goto end;
};
}
/*
* Default sigalgs are SHA1 based in <DTLS1.2 which is in security
* level 0
@ -1847,12 +1855,62 @@ static int test_cleanse_plaintext(void)
}
#ifndef OPENSSL_NO_OCSP
static int ocsp_server_cb(SSL *s, void *arg)
static OCSP_RESPONSE *create_ocsp_resp(X509 *ssl_cert, X509 *issuer, int status,
char *signer_key_files, char *signer_cert_files)
{
ASN1_TIME *thisupd = X509_gmtime_adj(NULL, 0);
ASN1_TIME *nextupd = X509_time_adj_ex(NULL, 1, 0, NULL);
OCSP_CERTID *cert_id = NULL;
char *signer_key_file = NULL;
char *signer_cert_file = NULL;
EVP_PKEY *signer_key = NULL;
X509 *signer_cert = NULL;
OCSP_RESPONSE *ocsp_resp = NULL;
EVP_MD *md = EVP_MD_fetch(libctx, "SHA-256", NULL);
OCSP_BASICRESP *bs = OCSP_BASICRESP_new();
if (signer_key_files != NULL && signer_cert_files != NULL) {
cert_id = OCSP_cert_to_id(md, ssl_cert, issuer);
OCSP_basic_add1_status(bs, cert_id, status, 0, thisupd, thisupd, nextupd);
signer_key_file = test_mk_file_path(certsdir, signer_key_files);
if (!TEST_ptr(signer_key = load_pkey_pem(signer_key_file, libctx)))
goto end;
signer_cert_file = test_mk_file_path(certsdir, signer_cert_files);
if (!TEST_ptr(signer_cert = load_cert_pem(signer_cert_file, libctx))
|| !TEST_true(OCSP_basic_sign(bs, signer_cert, signer_key, EVP_sha256(),
NULL, OCSP_NOCERTS)))
goto end;
ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs);
} else {
ocsp_resp = OCSP_response_create(status, NULL);
}
end:
OPENSSL_free(signer_key_file);
OPENSSL_free(signer_cert_file);
X509_free(signer_cert);
EVP_PKEY_free(signer_key);
ASN1_UTCTIME_free(thisupd);
ASN1_TIME_free(nextupd);
OCSP_BASICRESP_free(bs);
OCSP_CERTID_free(cert_id);
EVP_MD_free(md);
return ocsp_resp;
}
static int ocsp_server_cb_single(SSL *s, void *arg)
{
int *argi = (int *)arg;
unsigned char *copy = NULL;
STACK_OF(X509) *server_certs = NULL;
X509 *ssl_cert = NULL;
X509 *issuer = NULL;
OCSP_RESPONSE *ocsp_resp;
STACK_OF(OCSP_RESPID) *ids = NULL;
OCSP_RESPID *id = NULL;
unsigned char *ocsp_resp_der = NULL;
int resplen = 0;
if (*argi == 2) {
/* In this test we are expecting exactly 1 OCSP_RESPID */
@ -1867,29 +1925,43 @@ static int ocsp_server_cb(SSL *s, void *arg)
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (!TEST_ptr(copy = OPENSSL_memdup(orespder, sizeof(orespder))))
ssl_cert = SSL_get_certificate(s);
SSL_get0_chain_certs(s, &server_certs);
issuer = sk_X509_value(server_certs, 0);
ocsp_resp = create_ocsp_resp(ssl_cert, issuer, V_OCSP_CERTSTATUS_GOOD, "subinterCA.key", "subinterCA.pem");
if (!TEST_ptr(ocsp_resp))
return SSL_TLSEXT_ERR_ALERT_FATAL;
if (!TEST_true(SSL_set_tlsext_status_ocsp_resp(s, copy,
sizeof(orespder)))) {
OPENSSL_free(copy);
resplen = i2d_OCSP_RESPONSE(ocsp_resp, &ocsp_resp_der);
OCSP_RESPONSE_free(ocsp_resp);
if (!TEST_true(SSL_set_tlsext_status_ocsp_resp(s, ocsp_resp_der, resplen))) {
OPENSSL_free(ocsp_resp_der);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
OPENSSL_free(ocsp_resp_der);
ocsp_server_called = 1;
return SSL_TLSEXT_ERR_OK;
}
static int ocsp_client_cb(SSL *s, void *arg)
static int ocsp_client_cb_single(SSL *s, void *arg)
{
int *argi = (int *)arg;
const unsigned char *respderin;
size_t len;
const unsigned char *resp, *p;
OCSP_RESPONSE *rsp;
int len, resp_status;
if (*argi != 1 && *argi != 2)
return 0;
len = SSL_get_tlsext_status_ocsp_resp(s, &respderin);
if (!TEST_mem_eq(orespder, len, respderin, len))
len = SSL_get_tlsext_status_ocsp_resp(s, &resp);
p = resp;
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
resp_status = OCSP_response_status(rsp);
OCSP_RESPONSE_free(rsp);
if (resp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
return 0;
ocsp_client_called = 1;
@ -1899,17 +1971,22 @@ static int ocsp_client_cb(SSL *s, void *arg)
static int test_tlsext_status_type(void)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
char *leaf_chain = test_mk_file_path(certsdir, "leaf-chain.pem");
char *skey = test_mk_file_path(certsdir, "leaf.key");
char *leaf = test_mk_file_path(certsdir, "leaf.pem");
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
STACK_OF(OCSP_RESPID) *ids = NULL;
OCSP_RESPID *id = NULL;
BIO *certbio = NULL;
OSSL_LIB_CTX *tmpctx = OSSL_LIB_CTX_set0_default(libctx);
if (!create_ssl_ctx_pair(libctx, TLS_server_method(), TLS_client_method(),
TLS1_VERSION, 0,
&sctx, &cctx, cert, privkey))
&sctx, &cctx, leaf, skey))
return 0;
if (SSL_CTX_use_certificate_chain_file(sctx, leaf_chain) <= 0)
goto end;
if (SSL_CTX_get_tlsext_status_type(cctx) != -1)
goto end;
@ -1920,7 +1997,7 @@ static int test_tlsext_status_type(void)
goto end;
if (!TEST_int_eq(SSL_get_tlsext_status_type(clientssl), -1)
|| !TEST_true(SSL_set_tlsext_status_type(clientssl,
TLSEXT_STATUSTYPE_ocsp))
TLSEXT_STATUSTYPE_ocsp))
|| !TEST_int_eq(SSL_get_tlsext_status_type(clientssl),
TLSEXT_STATUSTYPE_ocsp))
goto end;
@ -1929,7 +2006,7 @@ static int test_tlsext_status_type(void)
clientssl = NULL;
if (!SSL_CTX_set_tlsext_status_type(cctx, TLSEXT_STATUSTYPE_ocsp)
|| SSL_CTX_get_tlsext_status_type(cctx) != TLSEXT_STATUSTYPE_ocsp)
|| SSL_CTX_get_tlsext_status_type(cctx) != TLSEXT_STATUSTYPE_ocsp)
goto end;
clientssl = SSL_new(cctx);
@ -1944,10 +2021,13 @@ static int test_tlsext_status_type(void)
* Now actually do a handshake and check OCSP information is exchanged and
* the callbacks get called
*/
SSL_CTX_set_tlsext_status_cb(cctx, ocsp_client_cb);
SSL_CTX_set_tlsext_status_cb(cctx, ocsp_client_cb_single);
SSL_CTX_set_tlsext_status_arg(cctx, &cdummyarg);
SSL_CTX_set_tlsext_status_cb(sctx, ocsp_server_cb);
SSL_CTX_set_tlsext_status_cb(sctx, ocsp_server_cb_single);
SSL_CTX_set_tlsext_status_arg(sctx, &cdummyarg);
ocsp_client_called = 0;
ocsp_server_called = 0;
cdummyarg = 1;
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL))
|| !TEST_true(create_ssl_connection(serverssl, clientssl,
@ -1966,7 +2046,7 @@ static int test_tlsext_status_type(void)
cdummyarg = 0;
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL))
/* This should fail because the callback will fail */
/* This should fail because the callback will fail */
|| !TEST_false(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE))
|| !TEST_false(ocsp_client_called)
@ -2016,19 +2096,351 @@ static int test_tlsext_status_type(void)
testresult = 1;
end:
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
OPENSSL_free(leaf_chain);
OPENSSL_free(skey);
OPENSSL_free(leaf);
sk_OCSP_RESPID_pop_free(ids, OCSP_RESPID_free);
OCSP_RESPID_free(id);
BIO_free(certbio);
X509_free(ocspcert);
OSSL_LIB_CTX_set0_default(tmpctx);
ocspcert = NULL;
return testresult;
}
# ifndef OSSL_NO_USABLE_TLS1_3
static int ocsp_server_cb_multi(SSL *s, void *arg)
{
int *argi = (int *)arg;
const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
X509 *ssl_cert = NULL;
X509 *issuer = NULL;
int i, num = 0;
STACK_OF(X509) *server_certs = NULL;
OCSP_RESPONSE *ocsp_resp;
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
char *signer_key_files[] = { "subinterCA.key", "interCA.key", "rootCA.key" };
char *signer_cert_files[] = { "subinterCA.pem", "interCA.pem", "rootCA.pem" };
int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
if (*argi != 1 && *argi != 3)
goto end;
ocsp_server_called += 1;
SSL_get0_chain_certs(s, &server_certs);
if (server_certs != NULL && sc != NULL) {
/* certificate chain is available */
num = sk_X509_num(server_certs);
} else if (sc != NULL) {
/*
* certificate chain is not available,
* set num to 1 for server certificate
*/
num = 1;
}
/* in the test case with arg = 1 we only send the EE certificate response */
if (*argi == 1)
num = *argi;
sk_resp = sk_OCSP_RESPONSE_new_reserve(NULL, num);
if (sk_resp == NULL)
goto end;
for (i = 0; i < num; i++) {
if (i == 0) {
/* get OCSP response for server certificate first */
ssl_cert = SSL_get_certificate(s);
} else {
/*
* for each certificate in chain (except root) get
* the OCSP response
*/
ssl_cert = sk_X509_value(server_certs, i - 1);
}
issuer = sk_X509_value(server_certs, i);
if (ocsp_server_called == 3 && i == 0)
ocsp_resp = create_ocsp_resp(ssl_cert, issuer, OCSP_RESPONSE_STATUS_MALFORMEDREQUEST,
NULL, NULL);
else if (ocsp_server_called == 4 && i == 0)
ocsp_resp = create_ocsp_resp(ssl_cert, issuer, V_OCSP_CERTSTATUS_REVOKED,
signer_key_files[i], signer_cert_files[i]);
else
ocsp_resp = create_ocsp_resp(ssl_cert, issuer, V_OCSP_CERTSTATUS_GOOD,
signer_key_files[i], signer_cert_files[i]);
sk_OCSP_RESPONSE_push(sk_resp, ocsp_resp);
}
ret = SSL_TLSEXT_ERR_OK;
(void)SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp);
end:
return ret;
}
static int ocsp_client_cb_multi(SSL *s, void *arg)
{
int *argi = (int *)arg;
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
OCSP_RESPONSE *resp = NULL;
STACK_OF(X509) *server_certs = NULL;
X509 *ssl_cert = NULL, *issuer_cert = NULL;
OCSP_BASICRESP *bs = NULL;
OCSP_CERTID *cert_id = NULL;
OCSP_SINGLERESP *sr = NULL;
OCSP_CERTID *cid = NULL;
ASN1_OBJECT *cert_id_md_oid;
const EVP_MD *cert_id_md;
int i, num = 0;
int testresult = 0;
SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
num = sk_OCSP_RESPONSE_num(sk_resp);
/* check if we get as many OCSP responses as expected */
if (*argi < 1 || *argi > 3 || num != *argi)
return 0;
ocsp_client_called += 1;
server_certs = SSL_get_peer_cert_chain(s);
/*
* check if OCSP responses for all certificates in the chain are received
* and they are in the correct order
*/
for (i = 0; i < num; i++) {
if ((ssl_cert = sk_X509_value(server_certs, i)) == NULL)
return 0;
/* for a selfsigned certificate we expect no OCSP response */
if (X509_self_signed(ssl_cert, 0))
continue;
if ((resp = sk_OCSP_RESPONSE_value(sk_resp, i)) == NULL)
break;
if (ocsp_client_called != 3) {
if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
resp = NULL;
break;
}
if ((bs = OCSP_response_get1_basic(resp)) == NULL)
break;
/* we send a OCSP response with one single response so we check only the first */
if ((sr = OCSP_resp_get0(bs, 0)) == NULL) {
resp = NULL;
break;
}
/* determine the md algorithm which was used to create cert id */
cid = (OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sr);
OCSP_id_get0_info(NULL, &cert_id_md_oid, NULL, NULL, cid);
if (cert_id_md_oid != NULL)
cert_id_md = EVP_get_digestbyobj(cert_id_md_oid);
else
cert_id_md = NULL;
issuer_cert = sk_X509_value(server_certs, i + 1);
if (issuer_cert == NULL) {
resp = NULL;
break;
}
/* search the stack for the requested OCSP response */
cert_id = OCSP_cert_to_id(cert_id_md, ssl_cert, issuer_cert);
if (cert_id == NULL) {
resp = NULL;
break;
}
if (OCSP_resp_find(bs, cert_id, -1) < 0)
resp = NULL;
OCSP_BASICRESP_free(bs);
bs = NULL;
OCSP_CERTID_free(cert_id);
cert_id = NULL;
} else {
/* in that test case we expect a OCSP response with an error status */
if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_MALFORMEDREQUEST) {
resp = NULL;
break;
}
}
if (resp == NULL)
break;
}
testresult = resp != NULL;
OCSP_BASICRESP_free(bs);
OCSP_CERTID_free(cert_id);
return testresult;
}
static int verify_cb_multi_stapling(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
int res = preverify_ok;
/* in that test cases the verify of the first response should be not ok */
if (ocsp_server_called == 3 || ocsp_server_called == 4)
if (ocsp_verify_cb_called == 0 && preverify_ok == 0)
res = 1;
ocsp_verify_cb_called += 1;
return res;
}
static int test_tlsext_status_type_multi(void)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
char *leaf_chain = test_mk_file_path(certsdir, "leaf-chain.pem");
char *skey = test_mk_file_path(certsdir, "leaf.key");
char *leaf = test_mk_file_path(certsdir, "leaf.pem");
char *root = test_mk_file_path(certsdir, "rootCA.pem");
OSSL_LIB_CTX *tmpctx = OSSL_LIB_CTX_set0_default(libctx);
X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new(), *out_vpm = NULL;
if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), TLS_client_method(),
TLS1_VERSION, 0, &sctx, &cctx, leaf, skey)))
goto end;
if (TEST_int_lt(SSL_CTX_use_certificate_chain_file(sctx, leaf_chain), 0))
goto end;
if (!TEST_true(SSL_CTX_load_verify_locations(cctx, root, NULL)))
goto end;
if (TEST_int_ne(SSL_CTX_get_tlsext_status_type(cctx), -1))
goto end;
/* set verify callback function */
SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, verify_cb_multi_stapling);
/* First just do various checks getting and setting tlsext_status_type */
clientssl = SSL_new(cctx);
if (!TEST_ptr(clientssl))
goto end;
if (!TEST_int_eq(SSL_get_tlsext_status_type(clientssl), -1)
|| !TEST_true(SSL_set_tlsext_status_type(clientssl,
TLSEXT_STATUSTYPE_ocsp))
|| !TEST_int_eq(SSL_get_tlsext_status_type(clientssl),
TLSEXT_STATUSTYPE_ocsp))
goto end;
SSL_free(clientssl);
clientssl = NULL;
if (!TEST_true(SSL_CTX_set_tlsext_status_type(cctx, TLSEXT_STATUSTYPE_ocsp))
|| TEST_int_ne(SSL_CTX_get_tlsext_status_type(cctx), TLSEXT_STATUSTYPE_ocsp))
goto end;
/*
* Now actually do a handshake and check OCSP information is exchanged and
* the callbacks get called
*/
SSL_CTX_set_tlsext_status_cb(cctx, ocsp_client_cb_multi);
SSL_CTX_set_tlsext_status_arg(cctx, &cdummyarg);
SSL_CTX_set_tlsext_status_cb(sctx, ocsp_server_cb_multi);
SSL_CTX_set_tlsext_status_arg(sctx, &cdummyarg);
ocsp_client_called = 0;
ocsp_server_called = 0;
ocsp_verify_cb_called = 0;
cdummyarg = 3; /* expect three OCSP responses */
X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_OCSP_RESP_CHECK | X509_V_FLAG_OCSP_RESP_CHECK_ALL);
if (!TEST_true(SSL_CTX_set1_param(cctx, vpm)))
goto end;
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))
|| !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
|| !TEST_int_eq(ocsp_client_called, 1) || !TEST_int_eq(ocsp_server_called, 1)
|| !TEST_true(ocsp_verify_cb_called))
goto end;
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = NULL;
clientssl = NULL;
/*
* This time we only transfer the OCSP information for the server certificate
*/
ocsp_verify_cb_called = 0;
cdummyarg = 1; /* expect one OCSP response */
out_vpm = SSL_CTX_get0_param(cctx);
X509_VERIFY_PARAM_clear_flags(out_vpm, X509_V_FLAG_OCSP_RESP_CHECK_ALL);
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))
|| !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
|| !TEST_int_eq(ocsp_client_called, 2) || !TEST_int_eq(ocsp_server_called, 2)
|| !TEST_true(ocsp_verify_cb_called))
goto end;
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = NULL;
clientssl = NULL;
/*
* tbd
*/
ocsp_verify_cb_called = 0;
cdummyarg = 1; /* expect one OCSP response */
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))
|| !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
|| !TEST_int_eq(ocsp_client_called, 3) || !TEST_int_eq(ocsp_server_called, 3)
|| !TEST_true(ocsp_verify_cb_called))
goto end;
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = NULL;
clientssl = NULL;
/*
* In the third test case we set the status of the server certificate to REVOKED.
* The SSL connection should fail and the ocsp_client_cb_multi should not be called.
*/
ocsp_verify_cb_called = 0;
cdummyarg = 3; /* expect three OCSP responses */
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))
|| !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
|| !TEST_int_eq(ocsp_client_called, 4) || !TEST_int_eq(ocsp_server_called, 4)
|| !TEST_true(ocsp_verify_cb_called))
goto end;
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = NULL;
clientssl = NULL;
testresult = 1;
end:
OPENSSL_free(leaf_chain);
OPENSSL_free(skey);
OPENSSL_free(leaf);
OPENSSL_free(root);
X509_VERIFY_PARAM_free(vpm);
OSSL_LIB_CTX_set0_default(tmpctx);
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
return testresult;
}
# endif
#endif
#if !defined(OSSL_NO_USABLE_TLS1_3) || !defined(OPENSSL_NO_TLS1_2)
@ -13476,6 +13888,9 @@ int setup_tests(void)
ADD_TEST(test_cleanse_plaintext);
#ifndef OPENSSL_NO_OCSP
ADD_TEST(test_tlsext_status_type);
# ifndef OSSL_NO_USABLE_TLS1_3
ADD_TEST(test_tlsext_status_type_multi);
# endif
#endif
ADD_TEST(test_session_with_only_int_cache);
ADD_TEST(test_session_with_only_ext_cache);

View File

@ -5925,6 +5925,7 @@ OSSL_AA_DIST_POINT_new ? 3_5_0 EXIST::FUNCTION:
OSSL_AA_DIST_POINT_it ? 3_5_0 EXIST::FUNCTION:
PEM_ASN1_write_bio_ctx ? 3_5_0 EXIST::FUNCTION:
EVP_PKEY_get_security_category ? 3_6_0 EXIST::FUNCTION:
X509_STORE_CTX_set_ocsp_resp ? 3_6_0 EXIST::FUNCTION:OCSP
OPENSSL_sk_set_thunks ? 3_6_0 EXIST::FUNCTION:
i2d_PKCS8PrivateKey ? 3_6_0 EXIST::FUNCTION:
OSSL_PARAM_set_octet_string_or_ptr ? 3_6_0 EXIST::FUNCTION:

View File

@ -653,6 +653,7 @@ SSL_get_signature_nid define
SSL_get_time define
SSL_get_timeout define
SSL_get_tlsext_status_ocsp_resp define
SSL_get0_tlsext_status_ocsp_resp_ex define
SSL_get_tlsext_status_type define
SSL_get_tmp_key define
SSL_in_accept_init define
@ -690,6 +691,7 @@ SSL_set_time define
SSL_set_timeout define
SSL_set_tlsext_host_name define
SSL_set_tlsext_status_ocsp_resp define
SSL_set0_tlsext_status_ocsp_resp_ex define
SSL_set_tlsext_status_type define
SSL_set_tmp_dh define
SSL_set_tmp_ecdh define