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
=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
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);
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
# 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)
# 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
}
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))
}
/*
- * 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)
{
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);
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;
}
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;
+
+ 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, sk);
+ 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.
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
*/
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;
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)
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)
__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);
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;
}
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;
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;
}
}
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);
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},
};
const char *value)
{
int ret_value;
+
if (!parse_enum(ssl_verify_callbacks, OSSL_NELEM(ssl_verify_callbacks),
&ret_value, value)) {
return 0;
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;
"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.
# 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]
# ===========================================================
-[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-ignore-custom-reject]
-ssl_conf = 4-noverify-ignore-custom-reject-ssl
+[4-noverify-success]
+ssl_conf = 4-noverify-success-ssl
-[4-noverify-ignore-custom-reject-ssl]
-server = 4-noverify-ignore-custom-reject-server
-client = 4-noverify-ignore-custom-reject-client
+[4-noverify-success-ssl]
+server = 4-noverify-success-server
+client = 4-noverify-success-client
-[4-noverify-ignore-custom-reject-server]
+[4-noverify-success-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
-[4-noverify-ignore-custom-reject-client]
+[4-noverify-success-client]
CipherString = DEFAULT
[test-4]
ExpectedResult = Success
-client = 4-noverify-ignore-custom-reject-client-extra
-[4-noverify-ignore-custom-reject-client-extra]
+
+# ===========================================================
+
+[5-noverify-ignore-custom-reject]
+ssl_conf = 5-noverify-ignore-custom-reject-ssl
+
+[5-noverify-ignore-custom-reject-ssl]
+server = 5-noverify-ignore-custom-reject-server
+client = 5-noverify-ignore-custom-reject-client
+
+[5-noverify-ignore-custom-reject-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[5-noverify-ignore-custom-reject-client]
+CipherString = DEFAULT
+
+[test-5]
+ExpectedResult = Success
+client = 5-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
},
},
+ # 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.
{
}
#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)
{
#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);
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