mirror of https://github.com/openssl/openssl.git
Add support for TLS 1.3 OCSP multi-stapling for server certs
Co-authored-by: Michael Krueger Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20945)
This commit is contained in:
parent
c108ead284
commit
b1b4b154fd
12
CHANGES.md
12
CHANGES.md
|
|
@ -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
|
||||
-----------
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
105
apps/s_client.c
105
apps/s_client.c
|
|
@ -110,9 +110,10 @@ static char *sess_out = NULL;
|
|||
static SSL_SESSION *psksess = NULL;
|
||||
|
||||
static void print_stuff(BIO *berr, SSL *con, int full);
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
static int ocsp_resp_cb(SSL *s, void *arg);
|
||||
#endif
|
||||
static void print_ocsp_response(BIO *bp, OCSP_RESPONSE *rsp);
|
||||
# endif
|
||||
static int ldap_ExtendedResponse_parse(const char *buf, long rem);
|
||||
static int is_dNS_name(const char *host);
|
||||
|
||||
|
|
@ -483,7 +484,10 @@ typedef enum OPTION_choice {
|
|||
OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
|
||||
OPT_BRIEF, OPT_PREXIT, OPT_NO_INTERACTIVE, OPT_CRLF, OPT_QUIET, OPT_NBIO,
|
||||
OPT_SSL_CLIENT_ENGINE, OPT_IGN_EOF, OPT_NO_IGN_EOF,
|
||||
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG,
|
||||
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_WDEBUG,
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
OPT_STATUS, OPT_STATUS_OCSP_CHECK_LEAF, OPT_STATUS_OCSP_CHECK_ALL,
|
||||
# endif
|
||||
OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG,
|
||||
OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE,
|
||||
OPT_PSK_IDENTITY, OPT_PSK, OPT_PSK_SESS,
|
||||
|
|
@ -625,6 +629,17 @@ const OPTIONS s_client_options[] = {
|
|||
{"no-interactive", OPT_NO_INTERACTIVE, '-',
|
||||
"Don't run the client in the interactive mode"},
|
||||
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
OPT_SECTION("OCSP stapling"),
|
||||
{"status", OPT_STATUS, '-',
|
||||
"Sends a certificate status request to the server (OCSP stapling) " \
|
||||
"The server response (if any) will be printed out."},
|
||||
{"ocsp_check_leaf", OPT_STATUS_OCSP_CHECK_LEAF, '-',
|
||||
"Require checking leaf certificate status, attempting to use OCSP stapling first"},
|
||||
{"ocsp_check_all", OPT_STATUS_OCSP_CHECK_ALL, '-',
|
||||
"Require checking status of full chain, attempting to use OCSP stapling first"},
|
||||
# endif
|
||||
|
||||
OPT_SECTION("Debug"),
|
||||
{"showcerts", OPT_SHOWCERTS, '-',
|
||||
"Show all certificates sent by the server"},
|
||||
|
|
@ -659,9 +674,6 @@ const OPTIONS s_client_options[] = {
|
|||
"Hex dump of all TLS extensions received"},
|
||||
{"ignore_unexpected_eof", OPT_IGNORE_UNEXPECTED_EOF, '-',
|
||||
"Do not treat lack of close_notify from a peer as an error"},
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
{"status", OPT_STATUS, '-', "Request certificate status from server"},
|
||||
#endif
|
||||
{"serverinfo", OPT_SERVERINFO, 's',
|
||||
"types Send empty ClientHello extensions (comma-separated numbers)"},
|
||||
{"alpn", OPT_ALPN, 's',
|
||||
|
|
@ -1195,11 +1207,23 @@ int s_client_main(int argc, char **argv)
|
|||
case OPT_TLSEXTDEBUG:
|
||||
c_tlsextdebug = 1;
|
||||
break;
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
case OPT_STATUS:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
c_status_req = 1;
|
||||
#endif
|
||||
break;
|
||||
case OPT_STATUS_OCSP_CHECK_LEAF:
|
||||
c_status_req = 1;
|
||||
X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_OCSP_RESP_CHECK);
|
||||
vpmtouched++;
|
||||
break;
|
||||
case OPT_STATUS_OCSP_CHECK_ALL:
|
||||
c_status_req = 1;
|
||||
X509_VERIFY_PARAM_set_flags(vpm,
|
||||
X509_V_FLAG_OCSP_RESP_CHECK |
|
||||
X509_V_FLAG_OCSP_RESP_CHECK_ALL);
|
||||
vpmtouched++;
|
||||
break;
|
||||
# endif
|
||||
case OPT_WDEBUG:
|
||||
#ifdef WATT32
|
||||
dbug_init();
|
||||
|
|
@ -3626,27 +3650,58 @@ static void print_stuff(BIO *bio, SSL *s, int full)
|
|||
# ifndef OPENSSL_NO_OCSP
|
||||
static int ocsp_resp_cb(SSL *s, void *arg)
|
||||
{
|
||||
const unsigned char *p;
|
||||
int len;
|
||||
int num, i;
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
|
||||
OCSP_RESPONSE *rsp;
|
||||
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
|
||||
BIO_puts(arg, "OCSP response: ");
|
||||
if (p == NULL) {
|
||||
BIO_puts(arg, "no response sent\n");
|
||||
return 1;
|
||||
|
||||
if (SSL_version(s) >= TLS1_3_VERSION) {
|
||||
(void)SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
|
||||
|
||||
BIO_puts(arg, "OCSP responses: ");
|
||||
|
||||
if (sk_resp == NULL) {
|
||||
BIO_puts(arg, "no responses sent\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
num = sk_OCSP_RESPONSE_num(sk_resp);
|
||||
|
||||
BIO_printf(arg, "number of responses: %d", num);
|
||||
for (i = 0; i < num; i++)
|
||||
print_ocsp_response(arg, sk_OCSP_RESPONSE_value(sk_resp, i));
|
||||
} else {
|
||||
const unsigned char *p;
|
||||
int len = SSL_get_tlsext_status_ocsp_resp(s, &p);
|
||||
|
||||
BIO_puts(arg, "OCSP response: ");
|
||||
if (p == NULL) {
|
||||
BIO_puts(arg, "no OCSP response received\n");
|
||||
return 1;
|
||||
}
|
||||
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
||||
if (rsp == NULL) {
|
||||
BIO_puts(arg, "OCSP response parse error\n");
|
||||
BIO_dump_indent(arg, (char *)p, len, 4);
|
||||
return 0;
|
||||
}
|
||||
print_ocsp_response(arg, rsp);
|
||||
OCSP_RESPONSE_free(rsp);
|
||||
}
|
||||
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
||||
if (rsp == NULL) {
|
||||
BIO_puts(arg, "response parse error\n");
|
||||
BIO_dump_indent(arg, (char *)p, len, 4);
|
||||
return 0;
|
||||
}
|
||||
BIO_puts(arg, "\n======================================\n");
|
||||
OCSP_RESPONSE_print(arg, rsp, 0);
|
||||
BIO_puts(arg, "======================================\n");
|
||||
OCSP_RESPONSE_free(rsp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_ocsp_response(BIO *bp, OCSP_RESPONSE *rsp)
|
||||
{
|
||||
if (rsp == NULL) {
|
||||
BIO_puts(bp, "no OCSP response to print\n");
|
||||
return;
|
||||
}
|
||||
|
||||
BIO_puts(bp, "\n======================================\n");
|
||||
OCSP_RESPONSE_print(bp, rsp, 0);
|
||||
BIO_puts(bp, "\n======================================\n");
|
||||
}
|
||||
# endif
|
||||
|
||||
static int ldap_ExtendedResponse_parse(const char *buf, long rem)
|
||||
|
|
|
|||
346
apps/s_server.c
346
apps/s_server.c
|
|
@ -54,9 +54,10 @@ typedef unsigned int u_int;
|
|||
#include "s_apps.h"
|
||||
#include "timeouts.h"
|
||||
#ifdef CHARSET_EBCDIC
|
||||
#include <openssl/ebcdic.h>
|
||||
# include <openssl/ebcdic.h>
|
||||
#endif
|
||||
#include "internal/sockets.h"
|
||||
#include "internal/statem.h"
|
||||
|
||||
static int not_resumable_sess_cb(SSL *s, int is_forward_secure);
|
||||
static int sv_body(int s, int stype, int prot, unsigned char *context);
|
||||
|
|
@ -454,16 +455,20 @@ static int ssl_servername_cb(SSL *s, int *ad, void *arg)
|
|||
/* Structure passed to cert status callback */
|
||||
typedef struct tlsextstatusctx_st {
|
||||
int timeout;
|
||||
/* File to load OCSP Response from (or NULL if no file) */
|
||||
char *respin;
|
||||
/*
|
||||
* List of filenames, from which we are loading each OCSP Response to
|
||||
* staple during handshake (or NULL if no file)
|
||||
*/
|
||||
STACK_OF(OPENSSL_STRING) *sk_resp_in;
|
||||
/* Default responder to use */
|
||||
char *host, *path, *port;
|
||||
char *proxy, *no_proxy;
|
||||
int use_ssl;
|
||||
int verbose;
|
||||
int status_all;
|
||||
} tlsextstatusctx;
|
||||
|
||||
static tlsextstatusctx tlscstatp = { -1 };
|
||||
static tlsextstatusctx tlscstatp = { -1, NULL };
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
|
||||
|
|
@ -474,14 +479,15 @@ static tlsextstatusctx tlscstatp = { -1 };
|
|||
* the OCSP certificate IDs and minimise the number of OCSP responses by caching
|
||||
* them until they were considered "expired".
|
||||
*/
|
||||
static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
|
||||
OCSP_RESPONSE **resp)
|
||||
static int get_ocsp_resp_from_responder_single(SSL *s, X509 *x,
|
||||
tlsextstatusctx *srctx,
|
||||
OCSP_RESPONSE **resp)
|
||||
{
|
||||
char *host = NULL, *port = NULL, *path = NULL;
|
||||
char *proxy = NULL, *no_proxy = NULL;
|
||||
int use_ssl;
|
||||
STACK_OF(OPENSSL_STRING) *aia = NULL;
|
||||
X509 *x = NULL, *cert;
|
||||
X509 *cert;
|
||||
X509_NAME *iname;
|
||||
STACK_OF(X509) *chain = NULL;
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
|
@ -494,7 +500,6 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
|
|||
int i;
|
||||
|
||||
/* Build up OCSP query from server certificate */
|
||||
x = SSL_get_certificate(s);
|
||||
iname = X509_get_issuer_name(x);
|
||||
aia = X509_get1_ocsp(x);
|
||||
if (aia != NULL) {
|
||||
|
|
@ -559,6 +564,7 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
|
|||
SSL_get_tlsext_status_exts(s, &exts);
|
||||
for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
|
||||
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
|
||||
|
||||
if (!OCSP_REQUEST_add_ext(req, ext, -1))
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -591,6 +597,212 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bring_ocsp_resp_in_correct_order(SSL *s, tlsextstatusctx *srctx,
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp_unordered,
|
||||
STACK_OF(OCSP_RESPONSE) **sk_resp)
|
||||
{
|
||||
STACK_OF(X509) *server_certs = NULL;
|
||||
X509 *ssl_cert = NULL;
|
||||
X509 *issuer = NULL;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
OCSP_BASICRESP *bs = NULL;
|
||||
OCSP_CERTID *cert_id = NULL;
|
||||
int found = -1;
|
||||
int i, j, num = 1;
|
||||
|
||||
if (*sk_resp != NULL)
|
||||
sk_OCSP_RESPONSE_pop_free(*sk_resp, OCSP_RESPONSE_free);
|
||||
|
||||
SSL_get0_chain_certs(s, &server_certs);
|
||||
/*
|
||||
* TODO(DTLS-1.3): in future DTLS should also be considered
|
||||
*/
|
||||
if (server_certs != NULL && srctx->status_all &&
|
||||
!SSL_is_dtls(s) && SSL_version(s) >= TLS1_3_VERSION) {
|
||||
/* certificate chain is available */
|
||||
num = sk_X509_num(server_certs) + 1;
|
||||
}
|
||||
|
||||
/* get OCSP response for server certificate first */
|
||||
ssl_cert = SSL_get_certificate(s);
|
||||
|
||||
/*
|
||||
* OpenSSL servers with TLS < 1.3 can be configured with no certificate
|
||||
*/
|
||||
if (ssl_cert == NULL)
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
|
||||
/* reserve enough space so the pushes to the stack would not fail */
|
||||
*sk_resp = sk_OCSP_RESPONSE_new_reserve(NULL, num);
|
||||
|
||||
if (sk_resp == NULL)
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (i != 0) /* for each certificate in chain (except root) get the OCSP response */
|
||||
ssl_cert = sk_X509_value(server_certs, i - 1);
|
||||
|
||||
/* issuer certificate is next in chain */
|
||||
issuer = sk_X509_value(server_certs, i);
|
||||
|
||||
if (issuer == NULL
|
||||
|| (cert_id = OCSP_cert_to_id(NULL, ssl_cert, issuer)) == NULL) {
|
||||
sk_OCSP_RESPONSE_push(*sk_resp, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* find the correct OCSP response for the requested certificate */
|
||||
found = -1;
|
||||
for (j = 0; j < sk_OCSP_RESPONSE_num(sk_resp_unordered); j++) {
|
||||
if ((resp = sk_OCSP_RESPONSE_value(sk_resp_unordered, j)) == NULL)
|
||||
continue;
|
||||
if ((bs = OCSP_response_get1_basic(resp)) == NULL)
|
||||
continue;
|
||||
found = OCSP_resp_find(bs, cert_id, -1);
|
||||
OCSP_BASICRESP_free(bs);
|
||||
if (found > -1) {
|
||||
/* remove the found OCSP response to prevent freeing it with the remaining list */
|
||||
sk_OCSP_RESPONSE_delete(sk_resp_unordered, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found < 0)
|
||||
resp = NULL;
|
||||
|
||||
OCSP_CERTID_free(cert_id);
|
||||
|
||||
/* add response to stack; also insert null response */
|
||||
(void)sk_OCSP_RESPONSE_push(*sk_resp, resp);
|
||||
}
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to get a list OCSP_RESPONSE from the files specified using
|
||||
* -status_file options for the server certificate and the chain certificates,
|
||||
* in the same order as the list of certs returned by SSL_get0_chain_certs().
|
||||
* In case of a missing entry, the respective list element will be NULL.
|
||||
*/
|
||||
static int get_ocsp_resp_from_files(SSL *s, tlsextstatusctx *srctx,
|
||||
STACK_OF(OCSP_RESPONSE) **sk_resp)
|
||||
{
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp_unordered = NULL;
|
||||
char *respfile = NULL;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
BIO *derbio;
|
||||
int i;
|
||||
int num = sk_OPENSSL_STRING_num(srctx->sk_resp_in);
|
||||
int ret = SSL_TLSEXT_ERR_OK;
|
||||
|
||||
sk_resp_unordered = sk_OCSP_RESPONSE_new_reserve(NULL, num);
|
||||
|
||||
if (sk_resp_unordered == NULL) {
|
||||
BIO_puts(bio_err, "cert_status: Cannot reserve memory for OCSP responses\n");
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* reading as many responses as files given */
|
||||
for (i = 0; i < num; i++) {
|
||||
respfile = sk_OPENSSL_STRING_value(srctx->sk_resp_in, i);
|
||||
|
||||
derbio = bio_open_default(respfile, 'r', FORMAT_ASN1);
|
||||
|
||||
if (derbio == NULL) {
|
||||
BIO_printf(bio_err, "cert_status: Cannot open OCSP response file %s\n", respfile);
|
||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
|
||||
BIO_free(derbio);
|
||||
if (resp == NULL) {
|
||||
BIO_printf(bio_err, "cert_status: Error reading OCSP response from file %s\n",
|
||||
respfile);
|
||||
ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
sk_OCSP_RESPONSE_push(sk_resp_unordered, resp);
|
||||
}
|
||||
|
||||
ret = bring_ocsp_resp_in_correct_order(s, srctx, sk_resp_unordered, sk_resp);
|
||||
|
||||
err:
|
||||
/* free the unordered list, including all remaining OCSP responses */
|
||||
sk_OCSP_RESPONSE_pop_free(sk_resp_unordered, OCSP_RESPONSE_free);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to get a list of OCSP_RESPONSE from a responder
|
||||
* for the server certificate and the chain certificates
|
||||
* in the same order as the list of certs returned by SSL_get0_chain_certs().
|
||||
* The function get_ocsp_resp_from_responder_single is called for each
|
||||
* certificate.
|
||||
* In case of a missing response, the respective list element will be NULL.
|
||||
* This is a simplified version. It examines certificates each time and
|
||||
* makes one OCSP responder query for each request. A full version would
|
||||
* store details such as the OCSP certificate IDs and minimise the number of
|
||||
* OCSP queries by caching responses until they were considered "expired".
|
||||
*/
|
||||
static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
|
||||
STACK_OF(OCSP_RESPONSE) **sk_resp)
|
||||
{
|
||||
X509 *ssl_cert = NULL;
|
||||
int i, num = 0;
|
||||
STACK_OF(X509) *server_certs = NULL;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
|
||||
if (*sk_resp != NULL)
|
||||
sk_OCSP_RESPONSE_pop_free(*sk_resp, OCSP_RESPONSE_free);
|
||||
|
||||
SSL_get0_chain_certs(s, &server_certs);
|
||||
/*
|
||||
* TODO(DTLS-1.3): in future DTLS should also be considered
|
||||
*/
|
||||
if (server_certs != NULL && srctx->status_all &&
|
||||
!SSL_is_dtls(s) && SSL_version(s) >= TLS1_3_VERSION) {
|
||||
/* certificate chain is available */
|
||||
num = sk_X509_num(server_certs) + 1;
|
||||
} else {
|
||||
/*
|
||||
* certificate chain is not available,
|
||||
* set num to 1 for server certificate
|
||||
*/
|
||||
num = 1;
|
||||
}
|
||||
|
||||
ssl_cert = SSL_get_certificate(s);
|
||||
|
||||
/*
|
||||
* OpenSSL servers with TLS < 1.3 can be configured with no certificate
|
||||
*/
|
||||
if (ssl_cert == NULL)
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
|
||||
*sk_resp = sk_OCSP_RESPONSE_new_reserve(NULL, num);
|
||||
|
||||
if (sk_resp == NULL)
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
|
||||
/* for each certificate in chain (except root) get the OCSP response */
|
||||
for (i = 0; i < num; i++) {
|
||||
if (i != 0) /* get OCSP response for server certificate first */
|
||||
ssl_cert = sk_X509_value(server_certs, i - 1);
|
||||
|
||||
resp = NULL;
|
||||
if (get_ocsp_resp_from_responder_single(s, ssl_cert, srctx, &resp) != SSL_TLSEXT_ERR_OK)
|
||||
resp = NULL;
|
||||
|
||||
/* add response to stack; also insert null response */
|
||||
sk_OCSP_RESPONSE_push(*sk_resp, resp);
|
||||
}
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Certificate Status callback. This is called when a client includes a
|
||||
* certificate status request extension. The response is either obtained from a
|
||||
|
|
@ -600,48 +812,48 @@ static int cert_status_cb(SSL *s, void *arg)
|
|||
{
|
||||
tlsextstatusctx *srctx = arg;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
unsigned char *rspder = NULL;
|
||||
int rspderlen;
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
|
||||
int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
int i;
|
||||
|
||||
if (srctx->verbose)
|
||||
BIO_puts(bio_err, "cert_status: callback called\n");
|
||||
|
||||
if (srctx->respin != NULL) {
|
||||
BIO *derbio = bio_open_default(srctx->respin, 'r', FORMAT_ASN1);
|
||||
if (derbio == NULL) {
|
||||
BIO_puts(bio_err, "cert_status: Cannot open OCSP response file\n");
|
||||
goto err;
|
||||
SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
|
||||
|
||||
if (sk_resp == NULL || sk_OCSP_RESPONSE_num(sk_resp) <= 0) {
|
||||
if (srctx->sk_resp_in != NULL) {
|
||||
get_ocsp_resp_from_files(s, srctx, &sk_resp);
|
||||
} else {
|
||||
ret = get_ocsp_resp_from_responder(s, srctx, &sk_resp);
|
||||
if (ret != SSL_TLSEXT_ERR_OK)
|
||||
goto err;
|
||||
}
|
||||
resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
|
||||
BIO_free(derbio);
|
||||
if (resp == NULL) {
|
||||
BIO_puts(bio_err, "cert_status: Error reading OCSP response\n");
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
ret = get_ocsp_resp_from_responder(s, srctx, &resp);
|
||||
if (ret != SSL_TLSEXT_ERR_OK)
|
||||
goto err;
|
||||
|
||||
(void)SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp);
|
||||
}
|
||||
|
||||
rspderlen = i2d_OCSP_RESPONSE(resp, &rspder);
|
||||
if (rspderlen <= 0)
|
||||
goto err;
|
||||
|
||||
SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen);
|
||||
if (srctx->verbose) {
|
||||
BIO_puts(bio_err, "cert_status: ocsp response sent:\n");
|
||||
OCSP_RESPONSE_print(bio_err, resp, 2);
|
||||
BIO_printf(bio_err, "cert_status: number of responses: %d\n",
|
||||
sk_OCSP_RESPONSE_num(sk_resp));
|
||||
for (i = 0; i < sk_OCSP_RESPONSE_num(sk_resp); i++) {
|
||||
resp = sk_OCSP_RESPONSE_value(sk_resp, i);
|
||||
if (resp != NULL)
|
||||
OCSP_RESPONSE_print(bio_err, resp, 2);
|
||||
else
|
||||
BIO_printf(bio_err,
|
||||
"cert_status: no ocsp response for certificate with index %d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
ret = SSL_TLSEXT_ERR_OK;
|
||||
|
||||
err:
|
||||
if (ret != SSL_TLSEXT_ERR_OK)
|
||||
if (ret != SSL_TLSEXT_ERR_OK) {
|
||||
ERR_print_errors(bio_err);
|
||||
|
||||
OCSP_RESPONSE_free(resp);
|
||||
sk_OCSP_RESPONSE_pop_free(sk_resp, OCSP_RESPONSE_free);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -680,6 +892,7 @@ static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
|
|||
if (!s_quiet) {
|
||||
/* We can assume that |in| is syntactically valid. */
|
||||
unsigned int i;
|
||||
|
||||
BIO_printf(bio_s_out, "ALPN protocols advertised by the client: ");
|
||||
for (i = 0; i < inlen;) {
|
||||
if (i)
|
||||
|
|
@ -692,9 +905,8 @@ static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
|
|||
|
||||
if (SSL_select_next_proto
|
||||
((unsigned char **)out, outlen, alpn_ctx->data,
|
||||
(unsigned int)alpn_ctx->len, in, inlen) != OPENSSL_NPN_NEGOTIATED) {
|
||||
(unsigned int)alpn_ctx->len, in, inlen) != OPENSSL_NPN_NEGOTIATED)
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (!s_quiet) {
|
||||
BIO_printf(bio_s_out, "ALPN protocols selected: ");
|
||||
|
|
@ -725,9 +937,9 @@ typedef enum OPTION_choice {
|
|||
OPT_VERIFYCAFILE,
|
||||
OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE,
|
||||
OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF,
|
||||
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_VERBOSE,
|
||||
OPT_STATUS_TIMEOUT, OPT_PROXY, OPT_NO_PROXY, OPT_STATUS_URL,
|
||||
OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
|
||||
OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_ALL,
|
||||
OPT_STATUS_VERBOSE, OPT_STATUS_TIMEOUT, OPT_PROXY, OPT_NO_PROXY,
|
||||
OPT_STATUS_URL, OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
|
||||
OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE,
|
||||
OPT_CRLF, OPT_QUIET, OPT_BRIEF, OPT_NO_DHE,
|
||||
OPT_NO_RESUME_EPHEMERAL, OPT_PSK_IDENTITY, OPT_PSK_HINT, OPT_PSK,
|
||||
|
|
@ -876,9 +1088,12 @@ const OPTIONS s_server_options[] = {
|
|||
{"cert_comp", OPT_CERT_COMP, '-', "Pre-compress server certificates"},
|
||||
#endif
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
OPT_SECTION("OCSP"),
|
||||
{"status", OPT_STATUS, '-', "Request certificate status from server"},
|
||||
{"status", OPT_STATUS, '-',
|
||||
"Provide certificate status response if requested, for server cert only"},
|
||||
{"status_all", OPT_STATUS_ALL, '-',
|
||||
"Provide certificate status response(s) if requested, for the whole chain"},
|
||||
{"status_verbose", OPT_STATUS_VERBOSE, '-',
|
||||
"Print more output in certificate status callback"},
|
||||
{"status_timeout", OPT_STATUS_TIMEOUT, 'n',
|
||||
|
|
@ -891,8 +1106,8 @@ const OPTIONS s_server_options[] = {
|
|||
{OPT_MORE_STR, 0, 0,
|
||||
"Default from environment variable 'no_proxy', else 'NO_PROXY', else none"},
|
||||
{"status_file", OPT_STATUS_FILE, '<',
|
||||
"File containing DER encoded OCSP Response"},
|
||||
#endif
|
||||
"File containing DER encoded OCSP Response (can be specified multiple times)"},
|
||||
# endif
|
||||
|
||||
OPT_SECTION("Debug"),
|
||||
{"security_debug", OPT_SECURITY_DEBUG, '-',
|
||||
|
|
@ -1076,9 +1291,9 @@ int s_server_main(int argc, char *argv[])
|
|||
const char *s_cert_file = TEST_CERT, *s_key_file = NULL, *s_chain_file = NULL;
|
||||
const char *s_cert_file2 = TEST_CERT2, *s_key_file2 = NULL;
|
||||
char *s_dcert_file = NULL, *s_dkey_file = NULL, *s_dchain_file = NULL;
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
int s_tlsextstatus = 0;
|
||||
#endif
|
||||
# endif
|
||||
int no_resume_ephemeral = 0;
|
||||
unsigned int max_send_fragment = 0;
|
||||
unsigned int split_send_fragment = 0, max_pipelines = 0;
|
||||
|
|
@ -1390,33 +1605,40 @@ int s_server_main(int argc, char *argv[])
|
|||
s_tlsextdebug = 1;
|
||||
break;
|
||||
case OPT_STATUS:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = 1;
|
||||
#endif
|
||||
tlscstatp.status_all = 0;
|
||||
# endif
|
||||
break;
|
||||
case OPT_STATUS_ALL:
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = tlscstatp.status_all = 1;
|
||||
# endif
|
||||
break;
|
||||
|
||||
case OPT_STATUS_VERBOSE:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = tlscstatp.verbose = 1;
|
||||
#endif
|
||||
# endif
|
||||
break;
|
||||
case OPT_STATUS_TIMEOUT:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = 1;
|
||||
tlscstatp.timeout = atoi(opt_arg());
|
||||
#endif
|
||||
# endif
|
||||
break;
|
||||
case OPT_PROXY:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
tlscstatp.proxy = opt_arg();
|
||||
#endif
|
||||
# endif
|
||||
break;
|
||||
case OPT_NO_PROXY:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
tlscstatp.no_proxy = opt_arg();
|
||||
#endif
|
||||
# endif
|
||||
break;
|
||||
case OPT_STATUS_URL:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = 1;
|
||||
if (!OSSL_HTTP_parse_url(opt_arg(), &tlscstatp.use_ssl, NULL,
|
||||
&tlscstatp.host, &tlscstatp.port, NULL,
|
||||
|
|
@ -1424,13 +1646,16 @@ int s_server_main(int argc, char *argv[])
|
|||
BIO_printf(bio_err, "Error parsing -status_url argument\n");
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
break;
|
||||
case OPT_STATUS_FILE:
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
s_tlsextstatus = 1;
|
||||
tlscstatp.respin = opt_arg();
|
||||
#endif
|
||||
if (tlscstatp.sk_resp_in == NULL
|
||||
&& (tlscstatp.sk_resp_in = sk_OPENSSL_STRING_new_null()) == NULL)
|
||||
goto end;
|
||||
sk_OPENSSL_STRING_push(tlscstatp.sk_resp_in, opt_arg());
|
||||
# endif
|
||||
break;
|
||||
case OPT_MSG:
|
||||
s_msg = 1;
|
||||
|
|
@ -2358,6 +2583,7 @@ int s_server_main(int argc, char *argv[])
|
|||
OPENSSL_free(port);
|
||||
X509_VERIFY_PARAM_free(vpm);
|
||||
free_sessions();
|
||||
sk_OPENSSL_STRING_free(tlscstatp.sk_resp_in);
|
||||
OPENSSL_free(tlscstatp.host);
|
||||
OPENSSL_free(tlscstatp.port);
|
||||
OPENSSL_free(tlscstatp.path);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <openssl/asn1.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/ocsp.h>
|
||||
#include <openssl/objects.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include "internal/dane.h"
|
||||
|
|
@ -55,7 +56,10 @@ static int check_name_constraints(X509_STORE_CTX *ctx);
|
|||
static int check_id(X509_STORE_CTX *ctx);
|
||||
static int check_trust(X509_STORE_CTX *ctx, int num_untrusted);
|
||||
static int check_revocation(X509_STORE_CTX *ctx);
|
||||
static int check_cert(X509_STORE_CTX *ctx);
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
static int check_cert_ocsp_resp(X509_STORE_CTX *ctx);
|
||||
#endif
|
||||
static int check_cert_crl(X509_STORE_CTX *ctx);
|
||||
static int check_policy(X509_STORE_CTX *ctx);
|
||||
static int check_dane_issuer(X509_STORE_CTX *ctx, int depth);
|
||||
static int check_cert_key_level(X509_STORE_CTX *ctx, X509 *cert);
|
||||
|
|
@ -184,6 +188,24 @@ static int verify_cb_crl(X509_STORE_CTX *ctx, int err)
|
|||
return ctx->verify_cb(0, ctx);
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
/*
|
||||
* Inform the verify callback of an error, OCSP-specific variant.
|
||||
* It is called also on OCSP response errors, if the
|
||||
* X509_V_FLAG_OCSP_RESP_CHECK or X509_V_FLAG_OCSP_RESP_CHECK_ALL flag
|
||||
* is set.
|
||||
* Here, the error depth and certificate are already set, we just specify
|
||||
* the error number.
|
||||
*
|
||||
* Returns 0 to abort verification with an error, non-zero to continue.
|
||||
*/
|
||||
static int verify_cb_ocsp(X509_STORE_CTX *ctx, int err)
|
||||
{
|
||||
ctx->error = err;
|
||||
return ctx->verify_cb(0, ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sadly, returns 0 also on internal error in ctx->verify_cb(). */
|
||||
static int check_auth_level(X509_STORE_CTX *ctx)
|
||||
{
|
||||
|
|
@ -225,7 +247,6 @@ static int verify_rpk(X509_STORE_CTX *ctx)
|
|||
return !!ctx->verify_cb(ctx->error == X509_V_OK, ctx);
|
||||
}
|
||||
|
||||
|
||||
/*-
|
||||
* Returns -1 on internal error.
|
||||
* Sadly, returns 0 also on internal error in ctx->verify_cb().
|
||||
|
|
@ -1037,28 +1058,209 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted)
|
|||
static int check_revocation(X509_STORE_CTX *ctx)
|
||||
{
|
||||
int i = 0, last = 0, ok = 0;
|
||||
int crl_check_enabled =
|
||||
(ctx->param->flags &
|
||||
(X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL)) != 0;
|
||||
int crl_check_all_enabled =
|
||||
(ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) != 0;
|
||||
int ocsp_check_enabled =
|
||||
(ctx->param->flags &
|
||||
(X509_V_FLAG_OCSP_RESP_CHECK | X509_V_FLAG_OCSP_RESP_CHECK_ALL)) != 0;
|
||||
int ocsp_check_all_enabled =
|
||||
(ctx->param->flags & X509_V_FLAG_OCSP_RESP_CHECK_ALL) != 0;
|
||||
|
||||
if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK) == 0)
|
||||
if (!crl_check_enabled && !ocsp_check_enabled)
|
||||
return 1;
|
||||
if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) != 0) {
|
||||
last = sk_X509_num(ctx->chain) - 1;
|
||||
} else {
|
||||
/* If checking CRL paths this isn't the EE certificate */
|
||||
if (ctx->parent != NULL)
|
||||
return 1;
|
||||
last = 0;
|
||||
|
||||
if (ocsp_check_enabled) {
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
/*
|
||||
* certificate status checking with OCSP
|
||||
*/
|
||||
if (ocsp_check_all_enabled)
|
||||
last = sk_X509_num(ctx->chain) - 1;
|
||||
else if (!crl_check_all_enabled && ctx->parent != NULL)
|
||||
return 1; /* If checking CRL paths this isn't the EE certificate */
|
||||
|
||||
for (i = 0; i <= last; i++) {
|
||||
ctx->error_depth = i;
|
||||
ctx->current_cert = sk_X509_value(ctx->chain, i);
|
||||
|
||||
/* skip if cert is apparently self-signed */
|
||||
if (ctx->current_cert->ex_flags & EXFLAG_SS)
|
||||
continue;
|
||||
|
||||
/* the issuer certificate is the next in the chain */
|
||||
ctx->current_issuer = sk_X509_value(ctx->chain, i + 1);
|
||||
|
||||
ok = check_cert_ocsp_resp(ctx);
|
||||
|
||||
/*
|
||||
* In the case the certificate status is REVOKED, the verification
|
||||
* can stop here.
|
||||
*/
|
||||
if (ok == V_OCSP_CERTSTATUS_REVOKED) {
|
||||
return verify_cb_ocsp(ctx, ctx->error != 0
|
||||
? ctx->error
|
||||
: X509_V_ERR_OCSP_VERIFY_FAILED);
|
||||
}
|
||||
|
||||
/*
|
||||
* In the case the certificate status is GOOD, continue with the next
|
||||
* certificate.
|
||||
*/
|
||||
if (ok == V_OCSP_CERTSTATUS_GOOD)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* As stated in RFC 6961 section 2.2:
|
||||
* If OCSP is not enabled or the client receives a "ocsp_response_list"
|
||||
* that does not contain a response for one or more of the certificates
|
||||
* in the completed certificate chain, the client SHOULD attempt to
|
||||
* validate the certificate using an alternative retrieval method,
|
||||
* such as downloading the relevant CRL;
|
||||
*/
|
||||
if (crl_check_all_enabled || (crl_check_enabled && i == 0)) {
|
||||
ok = check_cert_crl(ctx);
|
||||
if (!ok)
|
||||
return ok;
|
||||
} else {
|
||||
ok = verify_cb_ocsp(ctx, X509_V_ERR_OCSP_VERIFY_FAILED);
|
||||
if (!ok)
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
for (i = 0; i <= last; i++) {
|
||||
ctx->error_depth = i;
|
||||
ok = check_cert(ctx);
|
||||
if (!ok)
|
||||
return ok;
|
||||
|
||||
if (crl_check_enabled && !ocsp_check_all_enabled) {
|
||||
/* certificate status check with CRLs */
|
||||
if (crl_check_all_enabled) {
|
||||
last = sk_X509_num(ctx->chain) - 1;
|
||||
} else {
|
||||
/* If checking CRL paths this isn't the EE certificate */
|
||||
if (ctx->parent != NULL)
|
||||
return 1;
|
||||
last = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* in the case that OCSP is only enabled for the server certificate
|
||||
* and CRL for the complete chain, the rest of the chain has to be
|
||||
* checked here
|
||||
*/
|
||||
if (ocsp_check_enabled && crl_check_all_enabled)
|
||||
i = 1;
|
||||
else
|
||||
i = 0;
|
||||
for (; i <= last; i++) {
|
||||
ctx->error_depth = i;
|
||||
ok = check_cert_crl(ctx);
|
||||
if (!ok)
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
static int check_cert_ocsp_resp(X509_STORE_CTX *ctx)
|
||||
{
|
||||
int cert_status, crl_reason;
|
||||
int i;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
OCSP_BASICRESP *bs = NULL;
|
||||
OCSP_SINGLERESP *sr = NULL;
|
||||
OCSP_CERTID *sr_cert_id = NULL;
|
||||
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
|
||||
ASN1_OBJECT *cert_id_md_oid;
|
||||
EVP_MD *cert_id_md;
|
||||
OCSP_CERTID *cert_id = NULL;
|
||||
int ret = V_OCSP_CERTSTATUS_UNKNOWN;
|
||||
int num;
|
||||
|
||||
num = sk_OCSP_RESPONSE_num(ctx->ocsp_resp);
|
||||
|
||||
if (num < 0 || num <= ctx->error_depth)
|
||||
return X509_V_ERR_OCSP_NO_RESPONSE;
|
||||
|
||||
if ((resp = sk_OCSP_RESPONSE_value(ctx->ocsp_resp, ctx->error_depth)) == NULL
|
||||
|| (bs = OCSP_response_get1_basic(resp)) == NULL
|
||||
|| (num = OCSP_resp_count(bs)) < 1)
|
||||
return X509_V_ERR_OCSP_NO_RESPONSE;
|
||||
|
||||
if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
OCSP_BASICRESP_free(bs);
|
||||
ret = X509_V_ERR_OCSP_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (OCSP_basic_verify(bs, ctx->chain, ctx->store, OCSP_TRUSTOTHER) <= 0) {
|
||||
ret = X509_V_ERR_OCSP_SIGNATURE_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* find the right single response in the OCSP response */
|
||||
for (i = 0; i < num; i++) {
|
||||
sr = OCSP_resp_get0(bs, i);
|
||||
|
||||
/* determine the md algorithm which was used to create cert id */
|
||||
sr_cert_id = (OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sr);
|
||||
OCSP_id_get0_info(NULL, &cert_id_md_oid, NULL, NULL, sr_cert_id);
|
||||
if (cert_id_md_oid != NULL)
|
||||
cert_id_md = (EVP_MD *)EVP_get_digestbyobj(cert_id_md_oid);
|
||||
else
|
||||
cert_id_md = NULL;
|
||||
|
||||
/* search the stack for the requested OCSP response */
|
||||
cert_id = OCSP_cert_to_id(cert_id_md, ctx->current_cert, ctx->current_issuer);
|
||||
if (cert_id == NULL) {
|
||||
ret = X509_V_ERR_OCSP_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!OCSP_id_cmp(cert_id, sr_cert_id))
|
||||
break;
|
||||
|
||||
OCSP_CERTID_free(cert_id);
|
||||
cert_id = NULL;
|
||||
}
|
||||
|
||||
if (cert_id == NULL) {
|
||||
ret = X509_V_ERR_OCSP_NO_RESPONSE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (OCSP_resp_find_status(bs, cert_id, &cert_status, &crl_reason, &rev,
|
||||
&thisupd, &nextupd) <= 0) {
|
||||
ret = X509_V_ERR_OCSP_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (cert_status == V_OCSP_CERTSTATUS_GOOD) {
|
||||
/*
|
||||
* Note:
|
||||
* A OCSP stapling result will be accepted up to 5 minutes
|
||||
* after it expired!
|
||||
*/
|
||||
if (!OCSP_check_validity(thisupd, nextupd, 300L, -1L))
|
||||
ret = X509_V_ERR_OCSP_HAS_EXPIRED;
|
||||
else
|
||||
ret = V_OCSP_CERTSTATUS_GOOD;
|
||||
} else {
|
||||
ret = cert_status;
|
||||
}
|
||||
|
||||
end:
|
||||
OCSP_CERTID_free(cert_id);
|
||||
OCSP_BASICRESP_free(bs);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sadly, returns 0 also on internal error. */
|
||||
static int check_cert(X509_STORE_CTX *ctx)
|
||||
static int check_cert_crl(X509_STORE_CTX *ctx)
|
||||
{
|
||||
X509_CRL *crl = NULL, *dcrl = NULL;
|
||||
int ok = 0;
|
||||
|
|
@ -1070,6 +1272,9 @@ static int check_cert(X509_STORE_CTX *ctx)
|
|||
ctx->current_crl_score = 0;
|
||||
ctx->current_reasons = 0;
|
||||
|
||||
/* skip if cert is apparently self-signed */
|
||||
if (ctx->current_cert->ex_flags & EXFLAG_SS)
|
||||
return 1;
|
||||
if ((x->ex_flags & EXFLAG_PROXY) != 0)
|
||||
return 1;
|
||||
|
||||
|
|
@ -1645,7 +1850,7 @@ static int get_crl_delta(X509_STORE_CTX *ctx,
|
|||
|
||||
sk_X509_CRL_pop_free(skcrl, X509_CRL_free);
|
||||
|
||||
done:
|
||||
done:
|
||||
/* If we got any kind of CRL use it and return success */
|
||||
if (crl != NULL) {
|
||||
ctx->current_issuer = issuer;
|
||||
|
|
@ -2374,6 +2579,13 @@ void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk)
|
|||
ctx->crls = sk;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
void X509_STORE_CTX_set_ocsp_resp(X509_STORE_CTX *ctx, STACK_OF(OCSP_RESPONSE) *sk)
|
||||
{
|
||||
ctx->ocsp_resp = sk;
|
||||
}
|
||||
#endif
|
||||
|
||||
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose)
|
||||
{
|
||||
/*
|
||||
|
|
@ -2490,7 +2702,6 @@ void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
|
|||
OPENSSL_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
int X509_STORE_CTX_init_rpk(X509_STORE_CTX *ctx, X509_STORE *store, EVP_PKEY *rpk)
|
||||
{
|
||||
if (!X509_STORE_CTX_init(ctx, store, NULL, NULL))
|
||||
|
|
@ -2531,6 +2742,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
|
|||
ctx->rpk = NULL;
|
||||
/* Zero ex_data to make sure we're cleanup-safe */
|
||||
memset(&ctx->ex_data, 0, sizeof(ctx->ex_data));
|
||||
ctx->ocsp_resp = NULL;
|
||||
|
||||
/* store->cleanup is always 0 in OpenSSL, if set must be idempotent */
|
||||
if (store != NULL)
|
||||
|
|
@ -2690,7 +2902,7 @@ void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags,
|
|||
void X509_STORE_CTX_set_current_reasons(X509_STORE_CTX *ctx,
|
||||
unsigned int current_reasons)
|
||||
{
|
||||
ctx->current_reasons = current_reasons;
|
||||
ctx->current_reasons = current_reasons;
|
||||
}
|
||||
|
||||
X509 *X509_STORE_CTX_get0_cert(const X509_STORE_CTX *ctx)
|
||||
|
|
@ -2767,7 +2979,7 @@ X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(const X509_STORE_CTX *ctx)
|
|||
void X509_STORE_CTX_set_get_crl(X509_STORE_CTX *ctx,
|
||||
X509_STORE_CTX_get_crl_fn get_crl)
|
||||
{
|
||||
ctx->get_crl = get_crl;
|
||||
ctx->get_crl = get_crl;
|
||||
}
|
||||
|
||||
X509_STORE_CTX_check_crl_fn
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
84
ssl/s3_lib.c
84
ssl/s3_lib.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -831,6 +831,7 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl,
|
|||
s->ext.ocsp.exts = NULL;
|
||||
s->ext.ocsp.resp = NULL;
|
||||
s->ext.ocsp.resp_len = 0;
|
||||
s->ext.ocsp.resp_ex = NULL;
|
||||
|
||||
if (!SSL_CTX_up_ref(ctx))
|
||||
goto err;
|
||||
|
|
@ -1497,14 +1498,20 @@ void ossl_ssl_connection_free(SSL *ssl)
|
|||
OPENSSL_free(s->ext.tuples);
|
||||
OPENSSL_free(s->ext.peer_supportedgroups);
|
||||
sk_X509_EXTENSION_pop_free(s->ext.ocsp.exts, X509_EXTENSION_free);
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
OPENSSL_free(s->ext.ocsp.resp);
|
||||
s->ext.ocsp.resp = NULL;
|
||||
s->ext.ocsp.resp_len = 0;
|
||||
|
||||
sk_OCSP_RESPID_pop_free(s->ext.ocsp.ids, OCSP_RESPID_free);
|
||||
sk_OCSP_RESPONSE_pop_free(s->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
|
||||
s->ext.ocsp.resp_ex = NULL;
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_CT
|
||||
SCT_LIST_free(s->scts);
|
||||
OPENSSL_free(s->ext.scts);
|
||||
#endif
|
||||
OPENSSL_free(s->ext.ocsp.resp);
|
||||
OPENSSL_free(s->ext.alpn);
|
||||
OPENSSL_free(s->ext.tls13_cookie);
|
||||
if (s->clienthello != NULL)
|
||||
|
|
@ -6428,41 +6435,59 @@ static int ct_extract_ocsp_response_scts(SSL_CONNECTION *s)
|
|||
{
|
||||
# ifndef OPENSSL_NO_OCSP
|
||||
int scts_extracted = 0;
|
||||
const unsigned char *p;
|
||||
OCSP_BASICRESP *br = NULL;
|
||||
OCSP_RESPONSE *rsp = NULL;
|
||||
STACK_OF(SCT) *scts = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
if (s->ext.ocsp.resp == NULL || s->ext.ocsp.resp_len == 0)
|
||||
if (s->ext.ocsp.resp_ex == NULL)
|
||||
goto err;
|
||||
|
||||
p = s->ext.ocsp.resp;
|
||||
rsp = d2i_OCSP_RESPONSE(NULL, &p, (int)s->ext.ocsp.resp_len);
|
||||
if (rsp == NULL)
|
||||
goto err;
|
||||
|
||||
br = OCSP_response_get1_basic(rsp);
|
||||
if (br == NULL)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < OCSP_resp_count(br); ++i) {
|
||||
OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
|
||||
|
||||
if (single == NULL)
|
||||
continue;
|
||||
|
||||
scts =
|
||||
OCSP_SINGLERESP_get1_ext_d2i(single, NID_ct_cert_scts, NULL, NULL);
|
||||
scts_extracted =
|
||||
ct_move_scts(&s->scts, scts, SCT_SOURCE_OCSP_STAPLED_RESPONSE);
|
||||
if (scts_extracted < 0)
|
||||
for (j = 0; j < sk_OCSP_RESPONSE_num(s->ext.ocsp.resp_ex); j++) {
|
||||
rsp = sk_OCSP_RESPONSE_value(s->ext.ocsp.resp_ex, j);
|
||||
if (rsp == NULL)
|
||||
goto err;
|
||||
|
||||
br = OCSP_response_get1_basic(rsp);
|
||||
if (br == NULL)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < OCSP_resp_count(br); ++i) {
|
||||
OCSP_SINGLERESP *single = OCSP_resp_get0(br, i);
|
||||
|
||||
if (single == NULL)
|
||||
continue;
|
||||
|
||||
scts = OCSP_SINGLERESP_get1_ext_d2i(single,
|
||||
NID_ct_cert_scts, NULL, NULL);
|
||||
|
||||
OCSP_SINGLERESP_free(single);
|
||||
|
||||
if (scts == NULL) {
|
||||
scts_extracted = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = ct_move_scts(&s->scts, scts,
|
||||
SCT_SOURCE_OCSP_STAPLED_RESPONSE);
|
||||
|
||||
SCT_LIST_free(scts);
|
||||
|
||||
if (ret < 0) {
|
||||
scts_extracted = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
scts_extracted += ret;
|
||||
}
|
||||
|
||||
OCSP_BASICRESP_free(br);
|
||||
/* to assure that is not freed twice */
|
||||
br = NULL;
|
||||
}
|
||||
err:
|
||||
SCT_LIST_free(scts);
|
||||
OCSP_BASICRESP_free(br);
|
||||
OCSP_RESPONSE_free(rsp);
|
||||
return scts_extracted;
|
||||
# else
|
||||
/* Behave as if no OCSP response exists */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "internal/cryptlib.h"
|
||||
#include "internal/comp.h"
|
||||
#include "internal/ssl_unwrap.h"
|
||||
#include <openssl/ocsp.h>
|
||||
|
||||
static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
|
||||
PACKET *pkt);
|
||||
|
|
@ -2900,40 +2901,77 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL_CONNECTION *s,
|
|||
* In TLSv1.3 this is called from the extensions code, otherwise it is used to
|
||||
* parse a separate message. Returns 1 on success or 0 on failure
|
||||
*/
|
||||
int tls_process_cert_status_body(SSL_CONNECTION *s, PACKET *pkt)
|
||||
int tls_process_cert_status_body(SSL_CONNECTION *s, size_t chainidx, PACKET *pkt)
|
||||
{
|
||||
size_t resplen;
|
||||
unsigned int type;
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
size_t resplen;
|
||||
unsigned char *respder;
|
||||
OCSP_RESPONSE *resp = NULL;
|
||||
const unsigned char *p;
|
||||
#endif
|
||||
|
||||
if (!PACKET_get_1(pkt, &type)
|
||||
|| type != TLSEXT_STATUSTYPE_ocsp) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_UNSUPPORTED_STATUS_TYPE);
|
||||
return 0;
|
||||
}
|
||||
if (!PACKET_get_net_3_len(pkt, &resplen)
|
||||
|| PACKET_remaining(pkt) != resplen) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
return 0;
|
||||
}
|
||||
s->ext.ocsp.resp = OPENSSL_malloc(resplen);
|
||||
if (s->ext.ocsp.resp == NULL) {
|
||||
s->ext.ocsp.resp_len = 0;
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
|
||||
return 0;
|
||||
}
|
||||
s->ext.ocsp.resp_len = resplen;
|
||||
if (!PACKET_copy_bytes(pkt, s->ext.ocsp.resp, resplen)) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
return 0;
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
OPENSSL_free(s->ext.ocsp.resp);
|
||||
s->ext.ocsp.resp = NULL;
|
||||
s->ext.ocsp.resp_len = 0;
|
||||
|
||||
if (s->ext.ocsp.resp_ex == NULL)
|
||||
s->ext.ocsp.resp_ex = sk_OCSP_RESPONSE_new_null();
|
||||
|
||||
/*
|
||||
* TODO(DTLS-1.3): in future DTLS should also be considered
|
||||
*/
|
||||
if (!SSL_CONNECTION_IS_TLS13(s) && type == TLSEXT_STATUSTYPE_ocsp) {
|
||||
sk_OCSP_RESPONSE_pop_free(s->ext.ocsp.resp_ex, OCSP_RESPONSE_free);
|
||||
s->ext.ocsp.resp_ex = sk_OCSP_RESPONSE_new_null();
|
||||
}
|
||||
|
||||
if (PACKET_remaining(pkt) > 0) {
|
||||
if (!PACKET_get_net_3_len(pkt, &resplen)
|
||||
|| PACKET_remaining(pkt) != resplen) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (resplen > 0) {
|
||||
respder = OPENSSL_malloc(resplen);
|
||||
|
||||
if (respder == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!PACKET_copy_bytes(pkt, respder, resplen)) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
OPENSSL_free(respder);
|
||||
return 0;
|
||||
}
|
||||
p = respder;
|
||||
resp = d2i_OCSP_RESPONSE(NULL, &p, (long)resplen);
|
||||
OPENSSL_free(respder);
|
||||
if (resp == NULL) {
|
||||
SSLfatal(s, TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE,
|
||||
SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE);
|
||||
return 0;
|
||||
}
|
||||
sk_OCSP_RESPONSE_insert(s->ext.ocsp.resp_ex, resp, (int)chainidx);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
MSG_PROCESS_RETURN tls_process_cert_status(SSL_CONNECTION *s, PACKET *pkt)
|
||||
{
|
||||
if (!tls_process_cert_status_body(s, pkt)) {
|
||||
if (!tls_process_cert_status_body(s, 0, pkt)) {
|
||||
/* SSLfatal() already called */
|
||||
return MSG_PROCESS_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <openssl/x509_vfy.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/ocsp.h>
|
||||
|
||||
#include "../../ssl/ssl_local.h"
|
||||
#include "internal/ssl_unwrap.h"
|
||||
|
|
@ -265,47 +266,100 @@ static int client_hello_nov12_cb(SSL *s, int *al, void *arg)
|
|||
return SSL_CLIENT_HELLO_SUCCESS;
|
||||
}
|
||||
|
||||
static unsigned char dummy_ocsp_resp_good_val = 0xff;
|
||||
static unsigned char dummy_ocsp_resp_bad_val = 0xfe;
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
static OCSP_RESPONSE *dummy_ocsp_resp = NULL;
|
||||
static STACK_OF(OCSP_RESPONSE) *dummy_sk_resp = NULL;
|
||||
|
||||
static int server_ocsp_cb(SSL *s, void *arg)
|
||||
{
|
||||
unsigned char *resp;
|
||||
unsigned char *respder = NULL;
|
||||
int resplen = 0;
|
||||
|
||||
resplen = i2d_OCSP_RESPONSE(arg, &respder);
|
||||
|
||||
resp = OPENSSL_malloc(1);
|
||||
if (resp == NULL)
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
/*
|
||||
* For the purposes of testing we just send back a dummy OCSP response
|
||||
*/
|
||||
*resp = *(unsigned char *)arg;
|
||||
if (!SSL_set_tlsext_status_ocsp_resp(s, resp, 1)) {
|
||||
OPENSSL_free(resp);
|
||||
if (!SSL_set_tlsext_status_ocsp_resp(s, respder, resplen)) {
|
||||
OPENSSL_free(respder);
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
OPENSSL_free(respder);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int server_ocsp_cb_ext(SSL *s, void *arg)
|
||||
{
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
|
||||
|
||||
/*
|
||||
* For the purposes of testing we just send back a dummy OCSP response
|
||||
*/
|
||||
sk_resp = (STACK_OF(OCSP_RESPONSE) *)arg;
|
||||
if (!SSL_set0_tlsext_status_ocsp_resp_ex(s, sk_resp))
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int client_ocsp_cb(SSL *s, void *arg)
|
||||
{
|
||||
const unsigned char *resp;
|
||||
int len;
|
||||
const unsigned char *resp, *p;
|
||||
OCSP_RESPONSE *rsp;
|
||||
int len, status;
|
||||
|
||||
len = SSL_get_tlsext_status_ocsp_resp(s, &resp);
|
||||
if (len != 1 || *resp != dummy_ocsp_resp_good_val)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
p = resp;
|
||||
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
|
||||
|
||||
status = OCSP_response_status(rsp);
|
||||
|
||||
OCSP_RESPONSE_free(rsp);
|
||||
SSL_set_tlsext_status_ocsp_resp(s, NULL, 0);
|
||||
|
||||
OCSP_RESPONSE_free(dummy_ocsp_resp);
|
||||
|
||||
return status == OCSP_RESPONSE_STATUS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
static int client_ocsp_cb_ext(SSL *s, void *arg)
|
||||
{
|
||||
int len, status;
|
||||
STACK_OF(OCSP_RESPONSE) *sk_resp = NULL;
|
||||
OCSP_RESPONSE *rsp;
|
||||
|
||||
SSL_get0_tlsext_status_ocsp_resp_ex(s, &sk_resp);
|
||||
|
||||
if (sk_resp == NULL)
|
||||
return 0;
|
||||
|
||||
len = sk_OCSP_RESPONSE_num(sk_resp);
|
||||
|
||||
if (len != 1)
|
||||
return 0;
|
||||
|
||||
rsp = sk_OCSP_RESPONSE_value(sk_resp, 0);
|
||||
|
||||
status = OCSP_response_status(rsp);
|
||||
|
||||
SSL_set0_tlsext_status_ocsp_resp_ex(s, NULL);
|
||||
|
||||
return status == OCSP_RESPONSE_STATUS_SUCCESSFUL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg)
|
||||
{
|
||||
X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int n_retries = 0;
|
||||
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg)
|
||||
{
|
||||
int idx = SSL_get_ex_data_X509_STORE_CTX_idx();
|
||||
SSL *ssl;
|
||||
|
||||
|
|
@ -320,7 +374,8 @@ static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg) {
|
|||
return SSL_set_retry_verify(ssl);
|
||||
}
|
||||
|
||||
static int verify_accept_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
static int verify_accept_cb(X509_STORE_CTX *ctx, void *arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -566,13 +621,67 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
|
|||
}
|
||||
|
||||
if (extra->server.cert_status != SSL_TEST_CERT_STATUS_NONE) {
|
||||
|
||||
SSL_CTX_set_tlsext_status_type(client_ctx, TLSEXT_STATUSTYPE_ocsp);
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_arg(client_ctx, NULL);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx,
|
||||
((extra->server.cert_status == SSL_TEST_CERT_STATUS_GOOD_RESPONSE)
|
||||
? &dummy_ocsp_resp_good_val : &dummy_ocsp_resp_bad_val));
|
||||
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
switch (extra->server.cert_status) {
|
||||
case SSL_TEST_CERT_STATUS_GOOD_RESPONSE:
|
||||
|
||||
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
|
||||
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_ocsp_resp);
|
||||
|
||||
break;
|
||||
|
||||
case SSL_TEST_CERT_STATUS_BAD_RESPONSE:
|
||||
|
||||
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_INTERNALERROR, NULL);
|
||||
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_ocsp_resp);
|
||||
|
||||
break;
|
||||
|
||||
case SSL_TEST_CERT_STATUS_GOOD_RESPONSE_EXT:
|
||||
|
||||
dummy_sk_resp = sk_OCSP_RESPONSE_new_null();
|
||||
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
|
||||
sk_OCSP_RESPONSE_push(dummy_sk_resp, dummy_ocsp_resp);
|
||||
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb_ext);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb_ext);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_sk_resp);
|
||||
|
||||
break;
|
||||
|
||||
case SSL_TEST_CERT_STATUS_BAD_RESPONSE_EXT:
|
||||
|
||||
dummy_sk_resp = sk_OCSP_RESPONSE_new_null();
|
||||
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_INTERNALERROR, NULL);
|
||||
sk_OCSP_RESPONSE_push(dummy_sk_resp, dummy_ocsp_resp);
|
||||
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb_ext);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb_ext);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx, dummy_sk_resp);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
dummy_ocsp_resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, NULL);
|
||||
|
||||
SSL_CTX_set_tlsext_status_cb(client_ctx, client_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_cb(server_ctx, server_ocsp_cb);
|
||||
SSL_CTX_set_tlsext_status_arg(server_ctx, &dummy_ocsp_resp);
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue