Add SSL_get0_verified_chain() to return verified chain of peer
authorDr. Stephen Henson <steve@openssl.org>
Sat, 6 Feb 2016 03:17:23 +0000 (03:17 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Sat, 6 Feb 2016 18:18:28 +0000 (18:18 +0000)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
doc/ssl/SSL_get_peer_cert_chain.pod
include/openssl/ssl.h
ssl/ssl_cert.c
ssl/ssl_lib.c
ssl/ssl_locl.h

index 4d3e6d5..649de14 100644 (file)
@@ -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 <openssl/ssl.h>
 
  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<SSL_get_peer_certificate(3)>.
 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<not> a verified chain.
+
+SSL_get0_verified_chain() returns the B<verified> 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<ssl(3)>, L<SSL_get_peer_certificate(3)>
+L<ssl(3)>, L<SSL_get_peer_certificate(3)>, L<X509_up_ref(3)>,
+L<X509_chain_up_ref(3)>
 
 =cut
index 8c80c91..659ab96 100644 (file)
@@ -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);
index 2aaf99c..68c8924 100644 (file)
@@ -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);
index 1154b71..197a37c 100644 (file)
@@ -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);
index 9a30598..b505309 100644 (file)
@@ -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;