diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 6480e78c9..da47f12ef 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -680,6 +680,8 @@ struct mbedtls_ssl_handshake_params mbedtls_ssl_key_cert *key_cert; /*!< chosen key/cert pair (server) */ #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) mbedtls_ssl_key_cert *sni_key_cert; /*!< key/cert list from SNI */ + mbedtls_x509_crt *sni_ca_chain; /*!< trusted CAs from SNI callback */ + mbedtls_x509_crl *sni_ca_crl; /*!< trusted CAs CRLs from SNI */ #endif #endif /* MBEDTLS_X509_CRT_PARSE_C */ #if defined(MBEDTLS_SSL_PROTO_DTLS) @@ -1562,10 +1564,15 @@ void mbedtls_ssl_set_ca_chain( mbedtls_ssl_config *conf, * certificate chain. The top certificate (self-signed) * can be omitted. * - * \note This function may be called more than once if you want to - * support multiple certificates (eg, one using RSA and one - * using ECDSA). However, on client, currently only the first - * certificate is used (subsequent calls have no effect). + * \note On server, this function can be called multiple times to + * provision more than one cert/key pair (eg one ECDSA, one + * RSA with SHA-256, one RSA with SHA-1). An adequate + * certificate will be selected according to the client's + * advertised capabilities. In case mutliple certificates are + * adequate, preference is given to the one set by the first + * call to this function, then second, etc. + * + * \note On client, only the first call has any effect. * * \param conf SSL configuration * \param own_cert own public certificate chain @@ -1722,6 +1729,21 @@ int mbedtls_ssl_set_hs_own_cert( mbedtls_ssl_context *ssl, mbedtls_x509_crt *own_cert, mbedtls_pk_context *pk_key ); +/** + * \brief Set the data required to verify peer certificate for the + * current handshake + * + * \note Same as \c mbedtls_ssl_set_ca_chain() but for use within + * the SNI callback. + * + * \param ssl SSL context + * \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs) + * \param ca_crl trusted CA CRLs + */ +void mbedtls_ssl_set_hs_ca_chain( mbedtls_ssl_context *ssl, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl ); + /** * \brief Set server side ServerName TLS extension callback * (optional, server-side only). @@ -1732,10 +1754,11 @@ int mbedtls_ssl_set_hs_own_cert( mbedtls_ssl_context *ssl, * following parameters: (void *parameter, mbedtls_ssl_context *ssl, * const unsigned char *hostname, size_t len). If a suitable * certificate is found, the callback should set the - * certificate and key to use with mbedtls_ssl_set_hs_own_cert() (and - * possibly adjust the CA chain as well TODO: broken) and return 0. The - * callback should return -1 to abort the handshake at this - * point. + * certificate(s) and key(s) to use with \c + * mbedtls_ssl_set_hs_own_cert() (can be called repeatedly), + * and optionally adjust the CA with \c + * mbedtls_ssl_set_hs_ca_chain() and return 0. The callback + * should return -1 to abort the handshake at this point. * * \param conf SSL configuration * \param f_sni verification function diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ea015ae70..e83466ec6 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -2725,7 +2725,9 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl ) * opaque DistinguishedName<1..2^16-1>; */ p += 2; - crt = ssl->conf->ca_chain; + crt = ssl->handshake->sni_ca_chain != NULL ? + ssl->handshake->sni_ca_chain : + ssl->conf->ca_chain; total_dn_size = 0; while( crt != NULL && crt->version != 0 ) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 5fb2b6221..6618ebe54 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -4029,7 +4029,21 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) if( ssl->conf->authmode != MBEDTLS_SSL_VERIFY_NONE ) { - if( ssl->conf->ca_chain == NULL ) + mbedtls_x509_crt *ca_chain; + mbedtls_x509_crl *ca_crl; + + if( ssl->handshake->sni_ca_chain != NULL ) + { + ca_chain = ssl->handshake->sni_ca_chain; + ca_crl = ssl->handshake->sni_ca_crl; + } + else + { + ca_chain = ssl->conf->ca_chain; + ca_crl = ssl->conf->ca_crl; + } + + if( ca_chain == NULL ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no CA chain" ) ); return( MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED ); @@ -4039,7 +4053,7 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) * Main check: verify certificate */ ret = mbedtls_x509_crt_verify( ssl->session_negotiate->peer_cert, - ssl->conf->ca_chain, ssl->conf->ca_crl, ssl->hostname, + ca_chain, ca_crl, ssl->hostname, &ssl->session_negotiate->verify_result, ssl->conf->f_vrfy, ssl->conf->p_vrfy ); @@ -5360,6 +5374,14 @@ int mbedtls_ssl_set_hs_own_cert( mbedtls_ssl_context *ssl, return( ssl_append_key_cert( &ssl->handshake->sni_key_cert, own_cert, pk_key ) ); } + +void mbedtls_ssl_set_hs_ca_chain( mbedtls_ssl_context *ssl, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl ) +{ + ssl->handshake->sni_ca_chain = ca_chain; + ssl->handshake->sni_ca_crl = ca_crl; +} #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)