*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
-----------
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);
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);
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,
{"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"},
"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',
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();
# 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);
+
+ return 1;
+}
+
+static void print_ocsp_response(BIO *bp, OCSP_RESPONSE *rsp)
+{
if (rsp == NULL) {
- BIO_puts(arg, "response parse error\n");
- BIO_dump_indent(arg, (char *)p, len, 4);
- return 0;
+ BIO_puts(bp, "no OCSP response to print\n");
+ return;
}
- BIO_puts(arg, "\n======================================\n");
- OCSP_RESPONSE_print(arg, rsp, 0);
- BIO_puts(arg, "======================================\n");
- OCSP_RESPONSE_free(rsp);
- return 1;
+
+ BIO_puts(bp, "\n======================================\n");
+ OCSP_RESPONSE_print(bp, rsp, 0);
+ BIO_puts(bp, "\n======================================\n");
}
# endif
#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);
/* 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
* 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;
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) {
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;
}
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;
+}
+
/*
- * Certificate Status callback. This is called when a client includes a
- * certificate status request extension. The response is either obtained from a
- * file, or from an OCSP responder.
+ * 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 cert_status_cb(SSL *s, void *arg)
+static int get_ocsp_resp_from_files(SSL *s, tlsextstatusctx *srctx,
+ STACK_OF(OCSP_RESPONSE) **sk_resp)
{
- tlsextstatusctx *srctx = arg;
+ STACK_OF(OCSP_RESPONSE) *sk_resp_unordered = NULL;
+ char *respfile = NULL;
OCSP_RESPONSE *resp = NULL;
- unsigned char *rspder = NULL;
- int rspderlen;
- int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+ BIO *derbio;
+ int i;
+ int num = sk_OPENSSL_STRING_num(srctx->sk_resp_in);
+ int ret = SSL_TLSEXT_ERR_OK;
- if (srctx->verbose)
- BIO_puts(bio_err, "cert_status: callback called\n");
+ 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 (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");
+ 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_puts(bio_err, "cert_status: Error reading OCSP response\n");
+ 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 {
- ret = get_ocsp_resp_from_responder(s, srctx, &resp);
- if (ret != SSL_TLSEXT_ERR_OK)
- goto err;
+ /*
+ * certificate chain is not available,
+ * set num to 1 for server certificate
+ */
+ num = 1;
}
- rspderlen = i2d_OCSP_RESPONSE(resp, &rspder);
- if (rspderlen <= 0)
- goto err;
+ 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
+ * file, or from an OCSP responder.
+ */
+static int cert_status_cb(SSL *s, void *arg)
+{
+ tlsextstatusctx *srctx = arg;
+ OCSP_RESPONSE *resp = NULL;
+ 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");
+
+ 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;
+ }
+
+ (void)SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp);
+ }
- 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;
}
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)
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: ");
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,
{"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',
{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, '-',
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;
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,
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;
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);
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:
#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"
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);
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)
{
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().
static int check_revocation(X509_STORE_CTX *ctx)
{
int i = 0, last = 0, ok = 0;
-
- if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK) == 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 (!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;
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;
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;
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)
{
/*
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))
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)
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)
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
[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>]
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
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.
[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]>]
=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>
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>
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.
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
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()
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
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
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.
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.
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.
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>
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
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,
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>,
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.
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;
# 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
# 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)
extern "C" {
#endif
+DEFINE_STACK_OF(OCSP_RESPONSE)
+
/*-
SSL_CTX -> X509_STORE
-> X509_LOOKUP
# 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 */
/* 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
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,
#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)
{
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;
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;
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)
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
}
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;
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;
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)
{
# 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;
+ 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;
+ 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);
+ for (i = 0; i < OCSP_resp_count(br); ++i) {
+ OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
- if (single == NULL)
- continue;
+ 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)
- goto err;
+ 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 */
/* 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 */
__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);
#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);
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;
}
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 */
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);
* 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);
#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);
* 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;
+
+#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();
}
- 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;
+
+ 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;
}
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,
__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,
#include <openssl/asn1t.h>
#include <openssl/comp.h>
#include "internal/comp.h"
+#include <openssl/ocsp.h>
#define TICKET_NONCE_SIZE 8
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:
if (wst == WORK_MORE_A) {
int rv = tls_early_post_process_client_hello(s);
+
if (rv == 0) {
/* SSLfatal() was already called */
goto err;
* 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;
}
#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"
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)
+
+ 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 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;
- return 1;
+ 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) {
+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;
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;
}
}
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
}
/*
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,
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;
/*
# 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]
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
+
+
"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"
+ }
+ },
);
# 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]
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
+
+
"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"
+ }
}
);
"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")) {
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;
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
if (is_fips) {
testresult = 1;
goto end;
- };
+ }
/*
* Default sigalgs are SHA1 based in <DTLS1.2 which is in security
* level 0
}
#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 */
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;
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;
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;
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);
* 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,
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)
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)
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);
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:
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
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