]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add support for TLS 1.3 OCSP multi-stapling for server certs
authormartin <rauch.martin@gmail.com>
Fri, 7 Feb 2025 13:22:41 +0000 (14:22 +0100)
committerTomas Mraz <tomas@openssl.org>
Fri, 25 Jul 2025 15:24:37 +0000 (17:24 +0200)
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)

35 files changed:
CHANGES.md
apps/lib/s_cb.c
apps/s_client.c
apps/s_server.c
crypto/x509/x509_txt.c
crypto/x509/x509_vfy.c
doc/man1/openssl-s_client.pod.in
doc/man1/openssl-s_server.pod.in
doc/man3/SSL_CTX_set_tlsext_status_cb.pod
doc/man3/X509_STORE_CTX_get_error.pod
doc/man3/X509_STORE_CTX_new.pod
include/crypto/x509.h
include/openssl/ssl.h.in
include/openssl/tls1.h
include/openssl/x509_vfy.h.in
ssl/s3_lib.c
ssl/ssl_cert.c
ssl/ssl_lib.c
ssl/ssl_local.h
ssl/statem/extensions.c
ssl/statem/extensions_clnt.c
ssl/statem/extensions_srvr.c
ssl/statem/statem_clnt.c
ssl/statem/statem_local.h
ssl/statem/statem_srvr.c
test/helpers/handshake.c
test/helpers/ssl_test_ctx.c
test/helpers/ssl_test_ctx.h
test/ssl-tests/15-certstatus.cnf
test/ssl-tests/15-certstatus.cnf.in
test/ssl-tests/16-dtls-certstatus.cnf
test/ssl-tests/16-dtls-certstatus.cnf.in
test/sslapitest.c
util/libcrypto.num
util/other.syms

index cf0aaa1a0c25cf63dac1c44a5775845305ac77ce..2c0b9300e40a9cf0da50c16893c7d1eb85818379 100644 (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
 -----------
 
index fce944e44923849e679dc87f863730bb8b2e192c..8e6f50d537959af641d39152cb6d0c93478289b4 100644 (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);
index dbef77ec03a652a81e396cca6fafccc8d534e44a..b5c4ccffd3b9d2085734ded9c373b4882c851e85 100644 (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,26 +3650,57 @@ 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);
+
+    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
 
index 13809de819868207e901f7ebbbf9a1cb1593d230..d23e5defa518739ddeca4572ec7e2089257600b6 100644 (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,57 +597,263 @@ 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;
+}
+
 /*
- * 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;
 }
@@ -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);
index edbf5bdbb1fa4178e9164b461de03f3109085d6f..484a7b98c51098197b077926b98c1e4bdd703647 100644 (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:
index 5cdf929e4f1e5c50583c35e3e9bd87f72666333c..9af893610f7e0e4f300852d6180e4073ece2f417 100644 (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;
-
-    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;
@@ -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
index 82c5917f60817e6c3828f4bd513c068aca9d94ca..4c0759ab0f582e36883286b48d1962398f0529fb 100644 (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.
index 4f2a63d9b69bb260b953e17a16d4ccc75f5899f4..4c30c9c628330860de08e29af432f4b66d0c4053 100644 (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.
index c63b3be6cfa875b97ae796d35fbcb5d31beff945..d4f3dd257eab8cd741acf377d5b2c2dd4bf9afb1 100644 (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.
index f1f5ce1eb75026168d783f8a0368c06368e548f2..eee433a10dd451efc149198ab9ef79d6a3a7e6b3 100644 (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>
 
index 4810d48a01aaab43a28ea820f764159093e150f6..e4bce1311bb91e784b022db5ef6694ae3e5e0f77 100644 (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.
 
index b66eab72c63bba75b91d562c5a519796b05d1a33..65d1196c01cdebc007e83df4049e244534aed44d 100644 (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;
index 05f6a1f0512b4acb9c4b81ec3218317924122e22..1cc77189023c5acdd9c9039649f9e8261dd7935e 100644 (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
index 8e9b110bb303bad4242eac2c6cf61fc9141c3d95..80fc1b3f22a88666c3c0c2e5e942640f0824f7d7 100644 (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)
index 2a402f2161370a78be9af2ac5c6432ceebf82fcc..56f19ec8d61461ca74e239aecd013cf9b663d42f 100644 (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,
index e82d921ae6403e32236b2f4a67b79b51d091abea..226493a1615abc8e238db76f7eee1d0b947435eb 100644 (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;
 
index 1be6cd7dbc99569ad211ad0038eb30207f6cdd10..a3e2eae25244085701dff4cf9a1962552493e19c 100644 (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;
index 9ebd8d34f69122077db7f956b6249e664bdc2b58..31fb6d9dffb485ced12df14f60742c1768c0d46e 100644 (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;
+    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 */
index b4e7d0ef7b346ac9611a6559e1b54773651234c8..104379e990b1c670237f350d4f8cd0a98e1ac650 100644 (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);
index 8b55585663240ce6951d4864c178d47bd6e02c60..d786670855a3ae43e5e33f3086061f34bd58ac6a 100644 (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;
index 5055b67bfe800e961f710ed2b2f31ee383975500..3746554c7783c64869ceee368fcbbcd40ea02779 100644 (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 */
index 18fb5428176b24cc11a6055546689ddc35bfdb47..00c216600fb6e3c48ca0e9c70e8f002752b98ce8 100644 (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);
index 50d1541773ecf317e79051a9d4c4a5ee30a8625a..4278c4aec1c50f63c170605ad868380a7a3b69f2 100644 (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;
+
+#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;
     }
index 352f9b8cec7add4731f0a8829af8844c6c29ab5d..48870683c342609bd34f44ec3a8d2ec8b3b5b8c5 100644 (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,
index d38143caa08e4898d9e734815eac8d172edb2b5c..80d09d76e1c15c99aa38cf1d4904cd39f3b43568 100644 (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;
     }
index c312042c698ceb530d649879e63a63e03adc3ba9..9b0cbd9bd90314ad4794b554d73e06fc47053998 100644 (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)
+
+    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;
 
@@ -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
     }
 
     /*
index e5fc064ce5b428a6a98eeb0dd4c9ff51a761b423..704829c9e204675484d0cc58f3d3be9a3bfda0fe 100644 (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,
index 017d2d1121514419fdb027125b1de3eb22c75a64..ddcdd4fbcedbed25c1d5c8b95933232252fd6e41 100644 (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;
 
 /*
index bf6c41cda2f3c7ef68d040ef6c0c58742f4bfbca..9e63269c70ed87db85de993d013f76869cfa2ff8 100644 (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
+
+
index f2c5b63346a6d59a443bc1ea503b715ad0afd8b4..5c3dfa42152e3d14049c54b7895d72346ccde8ba 100644 (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"
+        }
+    },
 );
index ee46be4d3bef18feba1525b5f83909a7b556e310..4ccdb2648c5fa7eadafe93ed24adad904e12bf5b 100644 (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
+
+
index b5ae020e6a0c26d9177ac3119cee3e015a7895ff..4ee5d1a952178823135fb827bdc1eecd10d4942f 100644 (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")) {
index 0031325d3903f7b49b6a5a7132574d794bedbee6..216bb1b9ed0b780aab9bc9c9f8114340ec15cc6a 100644 (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);
index 206aedc07e14f55eb4dcf635c60f31bffc70ea28..2228aa3bf60416f42fd54c4a01103d8f310d0d24 100644 (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:
index 484f176c5f7cdc3be40c0c70451e8d2b57f45136..44c85f53be63be9ba3a053712726513c6ff14802 100644 (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