mirror of
https://github.com/openssl/openssl.git
synced 2025-04-12 20:30:52 +08:00
TLS client: allow cert verify callback return -1 for SSL_ERROR_WANT_RETRY_VERIFY
The client-side cert verification callback function may not only return as usual for success or 0 for failure, but also -1, typically on failure verifying the server certificate. This makes the handshake suspend and return control to the calling application with SSL_ERROR_WANT_RETRY_VERIFY. The app can for instance fetch further certificates or cert status information needed for the verification. Calling SSL_connect() again resumes the connection attempt by retrying the server certificate verification step. This process may even be repeated if need be. The core implementation of the feature is in ssl/statem/statem_clnt.c, splitting tls_process_server_certificate() into a preparation step that just copies the certificates received from the server to s->session->peer_chain (rather than having them in a local variable at first) and returns to the state machine, and a post-processing step in tls_post_process_server_certificate() that can be repeated: Try verifying the current contents of s->session->peer_chain basically as before, but give the verification callback function the chance to pause connecting and make the TLS state machine later call tls_post_process_server_certificate() again. Otherwise processing continues as usual. The documentation of the new feature is added to SSL_CTX_set_cert_verify_callback.pod and SSL_want.pod. This adds two tests: * A generic test in test/helpers/handshake.c on the usability of the new server cert verification retry feature. It is triggered via test/ssl-tests/03-custom_verify.cnf.in (while the bulky auto- generated changes to test/ssl-tests/03-custom_verify.cnf can be basically ignored). * A test in test/sslapitest.c that demonstrates the effectiveness of the approach for augmenting the cert chain provided by the server in between SSL_connect() calls. Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/13906)
This commit is contained in:
parent
1395a84e48
commit
0c3eb2793b
@ -33,7 +33,19 @@ argument I<arg> is specified by the application when setting I<callback>.
|
||||
|
||||
I<callback> should return 1 to indicate verification success and 0 to
|
||||
indicate verification failure. If SSL_VERIFY_PEER is set and I<callback>
|
||||
returns 0, the handshake will fail. As the verification procedure may
|
||||
returns 0, the handshake will fail.
|
||||
|
||||
In client mode I<callback> may also return -1,
|
||||
typically on failure verifying the server certificate.
|
||||
This makes the handshake suspend and return control to the calling application
|
||||
with B<SSL_ERROR_WANT_RETRY_VERIFY>.
|
||||
The app can for instance fetch further certificates or cert status information
|
||||
needed for the verification.
|
||||
Calling L<SSL_connect(3)> again resumes the connection attempt
|
||||
by retrying the server certificate verification step.
|
||||
This process may even be repeated if need be.
|
||||
|
||||
As the verification procedure may
|
||||
allow the connection to continue in the case of failure (by always
|
||||
returning 1) the verification result must be set in any case using the
|
||||
B<error> member of I<x509_store_ctx> so that the calling application
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
=head1 NAME
|
||||
|
||||
SSL_want, SSL_want_nothing, SSL_want_read, SSL_want_write, SSL_want_x509_lookup,
|
||||
SSL_want_async, SSL_want_async_job, SSL_want_client_hello_cb - obtain state
|
||||
information TLS/SSL I/O operation
|
||||
SSL_want, SSL_want_nothing, SSL_want_read, SSL_want_write,
|
||||
SSL_want_x509_lookup, SSL_want_retry_verify, SSL_want_async, SSL_want_async_job,
|
||||
SSL_want_client_hello_cb - obtain state information TLS/SSL I/O operation
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
@ -15,6 +15,7 @@ information TLS/SSL I/O operation
|
||||
int SSL_want_read(const SSL *ssl);
|
||||
int SSL_want_write(const SSL *ssl);
|
||||
int SSL_want_x509_lookup(const SSL *ssl);
|
||||
int SSL_want_retry_verify(const SSL *ssl);
|
||||
int SSL_want_async(const SSL *ssl);
|
||||
int SSL_want_async_job(const SSL *ssl);
|
||||
int SSL_want_client_hello_cb(const SSL *ssl);
|
||||
@ -53,47 +54,50 @@ There is no data to be written or to be read.
|
||||
|
||||
There are data in the SSL buffer that must be written to the underlying
|
||||
B<BIO> layer in order to complete the actual SSL_*() operation.
|
||||
A call to L<SSL_get_error(3)> should return
|
||||
SSL_ERROR_WANT_WRITE.
|
||||
A call to L<SSL_get_error(3)> should return B<SSL_ERROR_WANT_WRITE>.
|
||||
|
||||
=item SSL_READING
|
||||
|
||||
More data must be read from the underlying B<BIO> layer in order to
|
||||
complete the actual SSL_*() operation.
|
||||
A call to L<SSL_get_error(3)> should return
|
||||
SSL_ERROR_WANT_READ.
|
||||
A call to L<SSL_get_error(3)> should return B<SSL_ERROR_WANT_READ>.
|
||||
|
||||
=item SSL_X509_LOOKUP
|
||||
|
||||
The operation did not complete because an application callback set by
|
||||
SSL_CTX_set_client_cert_cb() has asked to be called again.
|
||||
A call to L<SSL_get_error(3)> should return
|
||||
SSL_ERROR_WANT_X509_LOOKUP.
|
||||
A call to L<SSL_get_error(3)> should return B<SSL_ERROR_WANT_X509_LOOKUP>.
|
||||
|
||||
=item SSL_RETRY_VERIFY
|
||||
|
||||
The operation did not complete because an application callback set by
|
||||
SSL_CTX_set_cert_verify_callback() has asked to be called again.
|
||||
A call to L<SSL_get_error(3)> should return B<SSL_ERROR_WANT_RETRY_VERIFY>.
|
||||
|
||||
=item SSL_ASYNC_PAUSED
|
||||
|
||||
An asynchronous operation partially completed and was then paused. See
|
||||
L<SSL_get_all_async_fds(3)>. A call to L<SSL_get_error(3)> should return
|
||||
SSL_ERROR_WANT_ASYNC.
|
||||
B<SSL_ERROR_WANT_ASYNC>.
|
||||
|
||||
=item SSL_ASYNC_NO_JOBS
|
||||
|
||||
The asynchronous job could not be started because there were no async jobs
|
||||
available in the pool (see ASYNC_init_thread(3)). A call to L<SSL_get_error(3)>
|
||||
should return SSL_ERROR_WANT_ASYNC_JOB.
|
||||
should return B<SSL_ERROR_WANT_ASYNC_JOB>.
|
||||
|
||||
=item SSL_CLIENT_HELLO_CB
|
||||
|
||||
The operation did not complete because an application callback set by
|
||||
SSL_CTX_set_client_hello_cb() has asked to be called again.
|
||||
A call to L<SSL_get_error(3)> should return
|
||||
SSL_ERROR_WANT_CLIENT_HELLO_CB.
|
||||
A call to L<SSL_get_error(3)> should return B<SSL_ERROR_WANT_CLIENT_HELLO_CB>.
|
||||
|
||||
=back
|
||||
|
||||
SSL_want_nothing(), SSL_want_read(), SSL_want_write(), SSL_want_x509_lookup(),
|
||||
SSL_want_async(), SSL_want_async_job(), and SSL_want_client_hello_cb() return
|
||||
1, when the corresponding condition is true or 0 otherwise.
|
||||
SSL_want_nothing(), SSL_want_read(), SSL_want_write(),
|
||||
SSL_want_x509_lookup(), SSL_want_retry_verify(),
|
||||
SSL_want_async(), SSL_want_async_job(), and SSL_want_client_hello_cb()
|
||||
return 1 when the corresponding condition is true or 0 otherwise.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
|
@ -927,12 +927,14 @@ __owur int SSL_extension_supported(unsigned int ext_type);
|
||||
# define SSL_ASYNC_PAUSED 5
|
||||
# define SSL_ASYNC_NO_JOBS 6
|
||||
# define SSL_CLIENT_HELLO_CB 7
|
||||
# define SSL_RETRY_VERIFY 8
|
||||
|
||||
/* These will only be used when doing non-blocking IO */
|
||||
# define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING)
|
||||
# define SSL_want_read(s) (SSL_want(s) == SSL_READING)
|
||||
# define SSL_want_write(s) (SSL_want(s) == SSL_WRITING)
|
||||
# define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP)
|
||||
# define SSL_want_retry_verify(s) (SSL_want(s) == SSL_RETRY_VERIFY)
|
||||
# define SSL_want_async(s) (SSL_want(s) == SSL_ASYNC_PAUSED)
|
||||
# define SSL_want_async_job(s) (SSL_want(s) == SSL_ASYNC_NO_JOBS)
|
||||
# define SSL_want_client_hello_cb(s) (SSL_want(s) == SSL_CLIENT_HELLO_CB)
|
||||
@ -1227,6 +1229,7 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
|
||||
# define SSL_ERROR_WANT_ASYNC 9
|
||||
# define SSL_ERROR_WANT_ASYNC_JOB 10
|
||||
# define SSL_ERROR_WANT_CLIENT_HELLO_CB 11
|
||||
# define SSL_ERROR_WANT_RETRY_VERIFY 12
|
||||
|
||||
# ifndef OPENSSL_NO_DEPRECATED_3_0
|
||||
# define SSL_CTRL_SET_TMP_DH 3
|
||||
|
@ -3808,6 +3808,8 @@ int SSL_get_error(const SSL *s, int i)
|
||||
}
|
||||
if (SSL_want_x509_lookup(s))
|
||||
return SSL_ERROR_WANT_X509_LOOKUP;
|
||||
if (SSL_want_retry_verify(s))
|
||||
return SSL_ERROR_WANT_RETRY_VERIFY;
|
||||
if (SSL_want_async(s))
|
||||
return SSL_ERROR_WANT_ASYNC;
|
||||
if (SSL_want_async_job(s))
|
||||
|
@ -1009,7 +1009,7 @@ size_t ossl_statem_client_max_message_size(SSL *s)
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a message that the client has been received from the server.
|
||||
* Process a message that the client has received from the server.
|
||||
*/
|
||||
MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL *s, PACKET *pkt)
|
||||
{
|
||||
@ -1079,6 +1079,9 @@ WORK_STATE ossl_statem_client_post_process_message(SSL *s, WORK_STATE wst)
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
return WORK_ERROR;
|
||||
|
||||
case TLS_ST_CR_CERT:
|
||||
return tls_post_process_server_certificate(s, wst);
|
||||
|
||||
case TLS_ST_CR_CERT_VRFY:
|
||||
case TLS_ST_CR_CERT_REQ:
|
||||
return tls_prepare_client_certificate(s, wst);
|
||||
@ -1762,20 +1765,16 @@ static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL *s,
|
||||
return MSG_PROCESS_ERROR;
|
||||
}
|
||||
|
||||
/* prepare server cert verificaton by setting s->session->peer_chain from pkt */
|
||||
MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
{
|
||||
int i;
|
||||
MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
|
||||
unsigned long cert_list_len, cert_len;
|
||||
X509 *x = NULL;
|
||||
const unsigned char *certstart, *certbytes;
|
||||
STACK_OF(X509) *sk = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
size_t chainidx, certidx;
|
||||
size_t chainidx;
|
||||
unsigned int context = 0;
|
||||
const SSL_CERT_LOOKUP *clu;
|
||||
|
||||
if ((sk = sk_X509_new_null()) == NULL) {
|
||||
if ((s->session->peer_chain = sk_X509_new_null()) == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
@ -1834,14 +1833,39 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
OPENSSL_free(rawexts);
|
||||
}
|
||||
|
||||
if (!sk_X509_push(sk, x)) {
|
||||
if (!sk_X509_push(s->session->peer_chain, x)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
x = NULL;
|
||||
}
|
||||
return MSG_PROCESS_CONTINUE_PROCESSING;
|
||||
|
||||
i = ssl_verify_cert_chain(s, sk);
|
||||
err:
|
||||
X509_free(x);
|
||||
sk_X509_pop_free(s->session->peer_chain, X509_free);
|
||||
s->session->peer_chain = NULL;
|
||||
return MSG_PROCESS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the s->session->peer_chain and check server cert type.
|
||||
* On success set s->session->peer and s->session->verify_result.
|
||||
* Else the peer certificate verification callback may request retry.
|
||||
*/
|
||||
WORK_STATE tls_post_process_server_certificate(SSL *s, WORK_STATE wst)
|
||||
{
|
||||
X509 *x;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
const SSL_CERT_LOOKUP *clu;
|
||||
size_t certidx;
|
||||
int i;
|
||||
|
||||
i = ssl_verify_cert_chain(s, s->session->peer_chain);
|
||||
if (i == -1) {
|
||||
s->rwstate = SSL_RETRY_VERIFY;
|
||||
return WORK_MORE_A;
|
||||
}
|
||||
/*
|
||||
* The documented interface is that SSL_VERIFY_PEER should be set in order
|
||||
* for client side verification of the server certificate to take place.
|
||||
@ -1859,35 +1883,31 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
if (s->verify_mode != SSL_VERIFY_NONE && i <= 0) {
|
||||
SSLfatal(s, ssl_x509err2alert(s->verify_result),
|
||||
SSL_R_CERTIFICATE_VERIFY_FAILED);
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
ERR_clear_error(); /* but we keep s->verify_result */
|
||||
if (i > 1) {
|
||||
SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, i);
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
|
||||
s->session->peer_chain = sk;
|
||||
/*
|
||||
* Inconsistency alert: cert_chain does include the peer's certificate,
|
||||
* which we don't include in statem_srvr.c
|
||||
*/
|
||||
x = sk_X509_value(sk, 0);
|
||||
sk = NULL;
|
||||
x = sk_X509_value(s->session->peer_chain, 0);
|
||||
|
||||
pkey = X509_get0_pubkey(x);
|
||||
|
||||
if (pkey == NULL || EVP_PKEY_missing_parameters(pkey)) {
|
||||
x = NULL;
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS);
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
|
||||
if ((clu = ssl_cert_lookup_by_pkey(pkey, &certidx)) == NULL) {
|
||||
x = NULL;
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
/*
|
||||
* Check certificate type is consistent with ciphersuite. For TLS 1.3
|
||||
@ -1896,9 +1916,8 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
*/
|
||||
if (!SSL_IS_TLS13(s)) {
|
||||
if ((clu->amask & s->s3.tmp.new_cipher->algorithm_auth) == 0) {
|
||||
x = NULL;
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_WRONG_CERTIFICATE_TYPE);
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
}
|
||||
s->session->peer_type = certidx;
|
||||
@ -1907,7 +1926,6 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
X509_up_ref(x);
|
||||
s->session->peer = x;
|
||||
s->session->verify_result = s->verify_result;
|
||||
x = NULL;
|
||||
|
||||
/* Save the current hash state for when we receive the CertificateVerify */
|
||||
if (SSL_IS_TLS13(s)
|
||||
@ -1915,15 +1933,9 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
|
||||
sizeof(s->cert_verify_hash),
|
||||
&s->cert_verify_hash_len)) {
|
||||
/* SSLfatal() already called */;
|
||||
goto err;
|
||||
return WORK_ERROR;
|
||||
}
|
||||
|
||||
ret = MSG_PROCESS_CONTINUE_READING;
|
||||
|
||||
err:
|
||||
X509_free(x);
|
||||
sk_X509_pop_free(sk, X509_free);
|
||||
return ret;
|
||||
return WORK_FINISHED_CONTINUE;
|
||||
}
|
||||
|
||||
static int tls_process_ske_psk_preamble(SSL *s, PACKET *pkt)
|
||||
|
@ -129,6 +129,7 @@ __owur int tls_construct_cert_status_body(SSL *s, WPACKET *pkt);
|
||||
__owur int tls_construct_cert_status(SSL *s, WPACKET *pkt);
|
||||
__owur MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, PACKET *pkt);
|
||||
__owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt);
|
||||
__owur WORK_STATE tls_post_process_server_certificate(SSL *s, WORK_STATE wst);
|
||||
__owur int ssl3_check_cert_and_algorithm(SSL *s);
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
__owur int tls_construct_next_proto(SSL *s, WPACKET *pkt);
|
||||
|
@ -314,6 +314,14 @@ static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int n_retries = 0;
|
||||
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
if (--n_retries < 0)
|
||||
return 1;
|
||||
X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int verify_accept_cb(X509_STORE_CTX *ctx, void *arg) {
|
||||
return 1;
|
||||
}
|
||||
@ -527,6 +535,10 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
|
||||
case SSL_TEST_VERIFY_ACCEPT_ALL:
|
||||
SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb, NULL);
|
||||
break;
|
||||
case SSL_TEST_VERIFY_RETRY_ONCE:
|
||||
n_retries = 1;
|
||||
SSL_CTX_set_cert_verify_callback(client_ctx, &verify_retry_cb, NULL);
|
||||
break;
|
||||
case SSL_TEST_VERIFY_REJECT_ALL:
|
||||
SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_cb, NULL);
|
||||
break;
|
||||
@ -807,8 +819,10 @@ static void do_handshake_step(PEER *peer)
|
||||
peer->status = PEER_ERROR;
|
||||
} else {
|
||||
int error = SSL_get_error(peer->ssl, ret);
|
||||
|
||||
/* Memory bios should never block with SSL_ERROR_WANT_WRITE. */
|
||||
if (error != SSL_ERROR_WANT_READ)
|
||||
if (error != SSL_ERROR_WANT_READ
|
||||
&& error != SSL_ERROR_WANT_RETRY_VERIFY)
|
||||
peer->status = PEER_ERROR;
|
||||
}
|
||||
}
|
||||
@ -1674,6 +1688,10 @@ static HANDSHAKE_RESULT *do_handshake_internal(
|
||||
ret->session_id = SSL_TEST_SESSION_ID_YES;
|
||||
ret->session_ticket_do_not_call = server_ex_data.session_ticket_do_not_call;
|
||||
|
||||
if (extra->client.verify_callback == SSL_TEST_VERIFY_RETRY_ONCE
|
||||
&& n_retries != -1)
|
||||
ret->result = SSL_TEST_SERVER_FAIL;
|
||||
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
SSL_get0_next_proto_negotiated(client.ssl, &proto, &proto_len);
|
||||
ret->client_npn_negotiated = dup_str(proto, proto_len);
|
||||
|
@ -177,6 +177,7 @@ const char *ssl_protocol_name(int protocol)
|
||||
static const test_enum ssl_verify_callbacks[] = {
|
||||
{"None", SSL_TEST_VERIFY_NONE},
|
||||
{"AcceptAll", SSL_TEST_VERIFY_ACCEPT_ALL},
|
||||
{"RetryOnce", SSL_TEST_VERIFY_RETRY_ONCE},
|
||||
{"RejectAll", SSL_TEST_VERIFY_REJECT_ALL},
|
||||
};
|
||||
|
||||
@ -184,6 +185,7 @@ __owur static int parse_client_verify_callback(SSL_TEST_CLIENT_CONF *client_conf
|
||||
const char *value)
|
||||
{
|
||||
int ret_value;
|
||||
|
||||
if (!parse_enum(ssl_verify_callbacks, OSSL_NELEM(ssl_verify_callbacks),
|
||||
&ret_value, value)) {
|
||||
return 0;
|
||||
|
@ -25,6 +25,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
SSL_TEST_VERIFY_NONE = 0, /* Default */
|
||||
SSL_TEST_VERIFY_ACCEPT_ALL,
|
||||
SSL_TEST_VERIFY_RETRY_ONCE,
|
||||
SSL_TEST_VERIFY_REJECT_ALL
|
||||
} ssl_verify_callback_t;
|
||||
|
||||
|
@ -157,12 +157,12 @@ sub test_conf {
|
||||
"Getting output from generate_ssl_tests.pl.");
|
||||
|
||||
SKIP: {
|
||||
# Test 2. Compare against existing output in test/ssl_tests.cnf.
|
||||
# Test 2. Compare against existing output in test/ssl-tests/
|
||||
skip "Skipping generated source test for $conf", 1
|
||||
if !$check_source;
|
||||
|
||||
$run_test = is(cmp_text($output_file, $conf_file), 0,
|
||||
"Comparing generated sources.");
|
||||
"Comparing generated $output_file with $conf_file.");
|
||||
}
|
||||
|
||||
# Test 3. Run the test.
|
||||
|
@ -1,16 +1,17 @@
|
||||
# Generated with generate_ssl_tests.pl
|
||||
|
||||
num_tests = 9
|
||||
num_tests = 10
|
||||
|
||||
test-0 = 0-verify-success
|
||||
test-1 = 1-verify-custom-reject
|
||||
test-2 = 2-verify-custom-allow
|
||||
test-3 = 3-noverify-success
|
||||
test-4 = 4-noverify-ignore-custom-reject
|
||||
test-5 = 5-noverify-accept-custom-allow
|
||||
test-6 = 6-verify-fail-no-root
|
||||
test-7 = 7-verify-custom-success-no-root
|
||||
test-8 = 8-verify-custom-fail-no-root
|
||||
test-3 = 3-verify-custom-retry
|
||||
test-4 = 4-noverify-success
|
||||
test-5 = 5-noverify-ignore-custom-reject
|
||||
test-6 = 6-noverify-accept-custom-allow
|
||||
test-7 = 7-verify-fail-no-root
|
||||
test-8 = 8-verify-custom-success-no-root
|
||||
test-9 = 9-verify-custom-fail-no-root
|
||||
# ===========================================================
|
||||
|
||||
[0-verify-success]
|
||||
@ -91,148 +92,175 @@ VerifyCallback = AcceptAll
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[3-noverify-success]
|
||||
ssl_conf = 3-noverify-success-ssl
|
||||
[3-verify-custom-retry]
|
||||
ssl_conf = 3-verify-custom-retry-ssl
|
||||
|
||||
[3-noverify-success-ssl]
|
||||
server = 3-noverify-success-server
|
||||
client = 3-noverify-success-client
|
||||
[3-verify-custom-retry-ssl]
|
||||
server = 3-verify-custom-retry-server
|
||||
client = 3-verify-custom-retry-client
|
||||
|
||||
[3-noverify-success-server]
|
||||
[3-verify-custom-retry-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[3-noverify-success-client]
|
||||
[3-verify-custom-retry-client]
|
||||
CipherString = DEFAULT
|
||||
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
|
||||
VerifyMode = Peer
|
||||
|
||||
[test-3]
|
||||
ExpectedResult = Success
|
||||
client = 3-verify-custom-retry-client-extra
|
||||
|
||||
[3-verify-custom-retry-client-extra]
|
||||
VerifyCallback = RetryOnce
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[4-noverify-success]
|
||||
ssl_conf = 4-noverify-success-ssl
|
||||
|
||||
[4-noverify-success-ssl]
|
||||
server = 4-noverify-success-server
|
||||
client = 4-noverify-success-client
|
||||
|
||||
[4-noverify-success-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[4-noverify-success-client]
|
||||
CipherString = DEFAULT
|
||||
|
||||
[test-4]
|
||||
ExpectedResult = Success
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[4-noverify-ignore-custom-reject]
|
||||
ssl_conf = 4-noverify-ignore-custom-reject-ssl
|
||||
[5-noverify-ignore-custom-reject]
|
||||
ssl_conf = 5-noverify-ignore-custom-reject-ssl
|
||||
|
||||
[4-noverify-ignore-custom-reject-ssl]
|
||||
server = 4-noverify-ignore-custom-reject-server
|
||||
client = 4-noverify-ignore-custom-reject-client
|
||||
[5-noverify-ignore-custom-reject-ssl]
|
||||
server = 5-noverify-ignore-custom-reject-server
|
||||
client = 5-noverify-ignore-custom-reject-client
|
||||
|
||||
[4-noverify-ignore-custom-reject-server]
|
||||
[5-noverify-ignore-custom-reject-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[4-noverify-ignore-custom-reject-client]
|
||||
[5-noverify-ignore-custom-reject-client]
|
||||
CipherString = DEFAULT
|
||||
|
||||
[test-4]
|
||||
[test-5]
|
||||
ExpectedResult = Success
|
||||
client = 4-noverify-ignore-custom-reject-client-extra
|
||||
client = 5-noverify-ignore-custom-reject-client-extra
|
||||
|
||||
[4-noverify-ignore-custom-reject-client-extra]
|
||||
[5-noverify-ignore-custom-reject-client-extra]
|
||||
VerifyCallback = RejectAll
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[5-noverify-accept-custom-allow]
|
||||
ssl_conf = 5-noverify-accept-custom-allow-ssl
|
||||
[6-noverify-accept-custom-allow]
|
||||
ssl_conf = 6-noverify-accept-custom-allow-ssl
|
||||
|
||||
[5-noverify-accept-custom-allow-ssl]
|
||||
server = 5-noverify-accept-custom-allow-server
|
||||
client = 5-noverify-accept-custom-allow-client
|
||||
[6-noverify-accept-custom-allow-ssl]
|
||||
server = 6-noverify-accept-custom-allow-server
|
||||
client = 6-noverify-accept-custom-allow-client
|
||||
|
||||
[5-noverify-accept-custom-allow-server]
|
||||
[6-noverify-accept-custom-allow-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[5-noverify-accept-custom-allow-client]
|
||||
[6-noverify-accept-custom-allow-client]
|
||||
CipherString = DEFAULT
|
||||
|
||||
[test-5]
|
||||
[test-6]
|
||||
ExpectedResult = Success
|
||||
client = 5-noverify-accept-custom-allow-client-extra
|
||||
client = 6-noverify-accept-custom-allow-client-extra
|
||||
|
||||
[5-noverify-accept-custom-allow-client-extra]
|
||||
[6-noverify-accept-custom-allow-client-extra]
|
||||
VerifyCallback = AcceptAll
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[6-verify-fail-no-root]
|
||||
ssl_conf = 6-verify-fail-no-root-ssl
|
||||
[7-verify-fail-no-root]
|
||||
ssl_conf = 7-verify-fail-no-root-ssl
|
||||
|
||||
[6-verify-fail-no-root-ssl]
|
||||
server = 6-verify-fail-no-root-server
|
||||
client = 6-verify-fail-no-root-client
|
||||
[7-verify-fail-no-root-ssl]
|
||||
server = 7-verify-fail-no-root-server
|
||||
client = 7-verify-fail-no-root-client
|
||||
|
||||
[6-verify-fail-no-root-server]
|
||||
[7-verify-fail-no-root-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[6-verify-fail-no-root-client]
|
||||
[7-verify-fail-no-root-client]
|
||||
CipherString = DEFAULT
|
||||
VerifyMode = Peer
|
||||
|
||||
[test-6]
|
||||
[test-7]
|
||||
ExpectedClientAlert = UnknownCA
|
||||
ExpectedResult = ClientFail
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[7-verify-custom-success-no-root]
|
||||
ssl_conf = 7-verify-custom-success-no-root-ssl
|
||||
[8-verify-custom-success-no-root]
|
||||
ssl_conf = 8-verify-custom-success-no-root-ssl
|
||||
|
||||
[7-verify-custom-success-no-root-ssl]
|
||||
server = 7-verify-custom-success-no-root-server
|
||||
client = 7-verify-custom-success-no-root-client
|
||||
[8-verify-custom-success-no-root-ssl]
|
||||
server = 8-verify-custom-success-no-root-server
|
||||
client = 8-verify-custom-success-no-root-client
|
||||
|
||||
[7-verify-custom-success-no-root-server]
|
||||
[8-verify-custom-success-no-root-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[7-verify-custom-success-no-root-client]
|
||||
[8-verify-custom-success-no-root-client]
|
||||
CipherString = DEFAULT
|
||||
VerifyMode = Peer
|
||||
|
||||
[test-7]
|
||||
[test-8]
|
||||
ExpectedResult = Success
|
||||
client = 7-verify-custom-success-no-root-client-extra
|
||||
client = 8-verify-custom-success-no-root-client-extra
|
||||
|
||||
[7-verify-custom-success-no-root-client-extra]
|
||||
[8-verify-custom-success-no-root-client-extra]
|
||||
VerifyCallback = AcceptAll
|
||||
|
||||
|
||||
# ===========================================================
|
||||
|
||||
[8-verify-custom-fail-no-root]
|
||||
ssl_conf = 8-verify-custom-fail-no-root-ssl
|
||||
[9-verify-custom-fail-no-root]
|
||||
ssl_conf = 9-verify-custom-fail-no-root-ssl
|
||||
|
||||
[8-verify-custom-fail-no-root-ssl]
|
||||
server = 8-verify-custom-fail-no-root-server
|
||||
client = 8-verify-custom-fail-no-root-client
|
||||
[9-verify-custom-fail-no-root-ssl]
|
||||
server = 9-verify-custom-fail-no-root-server
|
||||
client = 9-verify-custom-fail-no-root-client
|
||||
|
||||
[8-verify-custom-fail-no-root-server]
|
||||
[9-verify-custom-fail-no-root-server]
|
||||
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
|
||||
CipherString = DEFAULT
|
||||
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
|
||||
|
||||
[8-verify-custom-fail-no-root-client]
|
||||
[9-verify-custom-fail-no-root-client]
|
||||
CipherString = DEFAULT
|
||||
VerifyMode = Peer
|
||||
|
||||
[test-8]
|
||||
[test-9]
|
||||
ExpectedClientAlert = HandshakeFailure
|
||||
ExpectedResult = ClientFail
|
||||
client = 8-verify-custom-fail-no-root-client-extra
|
||||
client = 9-verify-custom-fail-no-root-client-extra
|
||||
|
||||
[8-verify-custom-fail-no-root-client-extra]
|
||||
[9-verify-custom-fail-no-root-client-extra]
|
||||
VerifyCallback = RejectAll
|
||||
|
||||
|
||||
|
@ -51,6 +51,20 @@ our @tests = (
|
||||
},
|
||||
},
|
||||
|
||||
# Same test as above but with a custom callback that requests retry once.
|
||||
{
|
||||
name => "verify-custom-retry",
|
||||
server => { },
|
||||
client => {
|
||||
extra => {
|
||||
"VerifyCallback" => "RetryOnce",
|
||||
},
|
||||
},
|
||||
test => {
|
||||
"ExpectedResult" => "Success",
|
||||
},
|
||||
},
|
||||
|
||||
# Sanity-check that verification indeed succeeds if peer verification
|
||||
# is not requested.
|
||||
{
|
||||
|
@ -549,6 +549,91 @@ end:
|
||||
}
|
||||
#endif
|
||||
|
||||
static int verify_retry_cb(X509_STORE_CTX *ctx, void *arg)
|
||||
{
|
||||
int res = X509_verify_cert(ctx);
|
||||
|
||||
if (res == 0 && X509_STORE_CTX_get_error(ctx) ==
|
||||
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
|
||||
return -1; /* indicate SSL_ERROR_WANT_RETRY_VERIFY */
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_client_cert_verify_cb(void)
|
||||
{
|
||||
/* server key, cert, chain, and root */
|
||||
char *skey = test_mk_file_path(certsdir, "leaf.key");
|
||||
char *leaf = test_mk_file_path(certsdir, "leaf.pem");
|
||||
char *int2 = test_mk_file_path(certsdir, "subinterCA.pem");
|
||||
char *int1 = test_mk_file_path(certsdir, "interCA.pem");
|
||||
char *root = test_mk_file_path(certsdir, "rootCA.pem");
|
||||
X509 *crt1 = NULL, *crt2 = NULL;
|
||||
STACK_OF(X509) *server_chain;
|
||||
SSL_CTX *cctx = NULL, *sctx = NULL;
|
||||
SSL *clientssl = NULL, *serverssl = NULL;
|
||||
int testresult = 0;
|
||||
|
||||
if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
|
||||
TLS_client_method(), TLS1_VERSION, 0,
|
||||
&sctx, &cctx, NULL, NULL)))
|
||||
goto end;
|
||||
if (!TEST_int_eq(SSL_CTX_use_certificate_chain_file(sctx, leaf), 1)
|
||||
|| !TEST_int_eq(SSL_CTX_use_PrivateKey_file(sctx, skey,
|
||||
SSL_FILETYPE_PEM), 1)
|
||||
|| !TEST_int_eq(SSL_CTX_check_private_key(sctx), 1))
|
||||
goto end;
|
||||
if (!TEST_true(SSL_CTX_load_verify_locations(cctx, root, NULL)))
|
||||
goto end;
|
||||
SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, NULL);
|
||||
SSL_CTX_set_cert_verify_callback(cctx, verify_retry_cb, NULL);
|
||||
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
|
||||
&clientssl, NULL, NULL)))
|
||||
goto end;
|
||||
|
||||
/* attempt SSL_connect() with incomplete server chain */
|
||||
if (!TEST_false(create_ssl_connection(serverssl, clientssl,
|
||||
SSL_ERROR_WANT_RETRY_VERIFY)))
|
||||
goto end;
|
||||
|
||||
/* application provides intermediate certs needed to verify server cert */
|
||||
if (!TEST_ptr((crt1 = load_cert_pem(int1, libctx)))
|
||||
|| !TEST_ptr((crt2 = load_cert_pem(int2, libctx)))
|
||||
|| !TEST_ptr((server_chain = SSL_get_peer_cert_chain(clientssl))))
|
||||
goto end;
|
||||
/* add certs in reverse order to demonstrate real chain building */
|
||||
if (!TEST_true(sk_X509_push(server_chain, crt1)))
|
||||
goto end;
|
||||
crt1 = NULL;
|
||||
if (!TEST_true(sk_X509_push(server_chain, crt2)))
|
||||
goto end;
|
||||
crt2 = NULL;
|
||||
|
||||
/* continue SSL_connect(), must now succeed with completed server chain */
|
||||
if (!TEST_true(create_ssl_connection(serverssl, clientssl,
|
||||
SSL_ERROR_NONE)))
|
||||
goto end;
|
||||
|
||||
testresult = 1;
|
||||
|
||||
end:
|
||||
X509_free(crt1);
|
||||
X509_free(crt2);
|
||||
SSL_shutdown(clientssl);
|
||||
SSL_shutdown(serverssl);
|
||||
SSL_free(serverssl);
|
||||
SSL_free(clientssl);
|
||||
SSL_CTX_free(sctx);
|
||||
SSL_CTX_free(cctx);
|
||||
|
||||
OPENSSL_free(skey);
|
||||
OPENSSL_free(leaf);
|
||||
OPENSSL_free(int2);
|
||||
OPENSSL_free(int1);
|
||||
OPENSSL_free(root);
|
||||
|
||||
return testresult;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_TLS1_2
|
||||
static int full_client_hello_callback(SSL *s, int *al, void *arg)
|
||||
{
|
||||
@ -8618,6 +8703,7 @@ int setup_tests(void)
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
ADD_TEST(test_keylog_no_master_key);
|
||||
#endif
|
||||
ADD_TEST(test_client_cert_verify_cb);
|
||||
#ifndef OPENSSL_NO_TLS1_2
|
||||
ADD_TEST(test_client_hello_cb);
|
||||
ADD_TEST(test_no_ems);
|
||||
|
@ -579,6 +579,7 @@ SSL_want_async_job define
|
||||
SSL_want_client_hello_cb define
|
||||
SSL_want_nothing define
|
||||
SSL_want_read define
|
||||
SSL_want_retry_verify define
|
||||
SSL_want_write define
|
||||
SSL_want_x509_lookup define
|
||||
SSLv23_client_method define
|
||||
|
Loading…
x
Reference in New Issue
Block a user