From 696178edff89f8df0382af0edbd0f723790a86cc Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Sat, 6 Feb 2016 03:17:23 +0000 Subject: [PATCH] Add SSL_get0_verified_chain() to return verified chain of peer Reviewed-by: Viktor Dukhovni --- doc/ssl/SSL_get_peer_cert_chain.pod | 27 +++++++++++++++++++++------ include/openssl/ssl.h | 1 + ssl/ssl_cert.c | 9 +++++++++ ssl/ssl_lib.c | 8 ++++++++ ssl/ssl_locl.h | 6 ++++-- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/doc/ssl/SSL_get_peer_cert_chain.pod b/doc/ssl/SSL_get_peer_cert_chain.pod index 4d3e6d5b09..649de145ba 100644 --- a/doc/ssl/SSL_get_peer_cert_chain.pod +++ b/doc/ssl/SSL_get_peer_cert_chain.pod @@ -2,31 +2,45 @@ =head1 NAME -SSL_get_peer_cert_chain - get the X509 certificate chain of the peer +SSL_get_peer_cert_chain, SSL_get0_verified_chain - get the X509 certificate +chain of the peer =head1 SYNOPSIS #include STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl); + STACK_OF(X509) *SSL_get0_verified_chain(const SSL *ssl); =head1 DESCRIPTION SSL_get_peer_cert_chain() returns a pointer to STACK_OF(X509) certificates -forming the certificate chain of the peer. If called on the client side, +forming the certificate chain sent by the peer. If called on the client side, the stack also contains the peer's certificate; if called on the server side, the peer's certificate must be obtained separately using L. If the peer did not present a certificate, NULL is returned. +NB: SSL_get_peer_chain() returns the peer chain as sent by the peer: it +only consists of certificates the peer has sent (in the order the peer +has sent them) it is B a verified chain. + +SSL_get0_verified_chain() returns the B certificate chain +of the peer including the peer's end entity certificate. It must be called +after a session has been successfully established. If peer verification was +not successful (as indicated by SSL_get_verify_result() not returning +X509_V_OK) the chain may be incomplete or invalid. + =head1 NOTES The peer certificate chain is not necessarily available after reusing a session, in which case a NULL pointer is returned. -The reference count of the STACK_OF(X509) object is not incremented. -If the corresponding session is freed, the pointer must not be used -any longer. +The reference count of each certificate in the returned STACK_OF(X509) object +is not incremented and the returned stack may be invalidated by renegotiation. +If applications wish to use any certificates in the returned chain +indefinitely they must increase the reference counts using X509_up_ref() or +obtain a copy of the whole chain with X509_chain_up_ref(). =head1 RETURN VALUES @@ -47,6 +61,7 @@ The return value points to the certificate chain presented by the peer. =head1 SEE ALSO -L, L +L, L, L, +L =cut diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 8c80c91f71..659ab9631a 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1716,6 +1716,7 @@ __owur OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); void SSL_set_verify_result(SSL *ssl, long v); __owur long SSL_get_verify_result(const SSL *ssl); +__owur STACK_OF(X509) *SSL_get0_verified_chain(const SSL *s); __owur size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen); diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 2aaf99cc23..68c8924425 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -541,6 +541,15 @@ int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) } s->verify_result = ctx.error; + sk_X509_pop_free(s->verified_chain, X509_free); + s->verified_chain = NULL; + if (X509_STORE_CTX_get_chain(&ctx) != NULL) { + s->verified_chain = X509_STORE_CTX_get1_chain(&ctx); + if (s->verified_chain == NULL) { + SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN, ERR_R_MALLOC_FAILURE); + i = 0; + } + } /* Move peername from the store context params to the SSL handle's */ X509_VERIFY_PARAM_move_peername(s->param, param); diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 1154b71ab6..197a37cd0d 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -715,6 +715,7 @@ SSL *SSL_new(SSL_CTX *ctx) s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len; } + s->verified_chain = NULL; s->verify_result = X509_V_OK; s->default_passwd_callback = ctx->default_passwd_callback; @@ -1052,6 +1053,8 @@ void SSL_free(SSL *s) sk_X509_NAME_pop_free(s->client_CA, X509_NAME_free); + sk_X509_pop_free(s->verified_chain, X509_free); + if (s->method != NULL) s->method->ssl_free(s); @@ -3822,4 +3825,9 @@ unsigned long SSL_clear_options(SSL *s, unsigned long op) return s->options &= ~op; } +STACK_OF(X509) *SSL_get0_verified_chain(const SSL *s) +{ + return s->verified_chain; +} + IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id); diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 9a30598104..b505309375 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -614,7 +614,7 @@ struct ssl_session_st { /* This is the cert and type for the other end. */ X509 *peer; int peer_type; - /* Certificate chain of peer */ + /* Certificate chain peer sent */ STACK_OF(X509) *peer_chain; /* * when app_verify_callback accepts a session where the peer's @@ -1058,8 +1058,10 @@ struct ssl_st { unsigned int max_psk_len); # endif SSL_CTX *ctx; - /* extra application data */ + /* Verified chain of peer */ + STACK_OF(X509) *verified_chain; long verify_result; + /* extra application data */ CRYPTO_EX_DATA ex_data; /* for server side, keep the list of CA_dn we can use */ STACK_OF(X509_NAME) *client_CA; -- 2.34.1