diff --git a/ChangeLog.d/mbedtls_ssl_dn_hint.txt b/ChangeLog.d/mbedtls_ssl_dn_hint.txt new file mode 100644 index 000000000..f569a36ca --- /dev/null +++ b/ChangeLog.d/mbedtls_ssl_dn_hint.txt @@ -0,0 +1,3 @@ +Features + * Add accessors to configure DN hints for certificate request: + mbedtls_ssl_conf_dn_hints() and mbedtls_ssl_set_hs_dn_hints() diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index ad7b8f04d..b3b5d47f8 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1496,6 +1496,10 @@ struct mbedtls_ssl_config #if defined(MBEDTLS_SSL_SRV_C) mbedtls_ssl_hs_cb_t MBEDTLS_PRIVATE(f_cert_cb); /*!< certificate selection callback */ #endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + const mbedtls_x509_crt *MBEDTLS_PRIVATE(dn_hints);/*!< acceptable client cert issuers */ +#endif }; struct mbedtls_ssl_context @@ -3128,6 +3132,26 @@ void mbedtls_ssl_conf_ca_chain( mbedtls_ssl_config *conf, mbedtls_x509_crt *ca_chain, mbedtls_x509_crl *ca_crl ); +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +/** + * \brief Set DN hints sent to client in CertificateRequest message + * + * \note If not set, subject distinguished names (DNs) are taken + * from \c mbedtls_ssl_conf_ca_chain() + * or \c mbedtls_ssl_set_hs_ca_chain()) + * + * \param conf SSL configuration + * \param crt crt chain whose subject DNs are issuer DNs of client certs + * from which the client should select client peer certificate. + */ +static inline +void mbedtls_ssl_conf_dn_hints( mbedtls_ssl_config *conf, + const mbedtls_x509_crt *crt ) +{ + conf->MBEDTLS_PRIVATE(dn_hints) = crt; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + #if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) /** * \brief Set the trusted certificate callback. @@ -3652,6 +3676,21 @@ void mbedtls_ssl_set_hs_ca_chain( mbedtls_ssl_context *ssl, mbedtls_x509_crt *ca_chain, mbedtls_x509_crl *ca_crl ); +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +/** + * \brief Set DN hints sent to client in CertificateRequest message + * + * \note Same as \c mbedtls_ssl_conf_dn_hints() but for use within + * the SNI callback or the certificate selection callback. + * + * \param ssl SSL context + * \param crt crt chain whose subject DNs are issuer DNs of client certs + * from which the client should select client peer certificate. + */ +void mbedtls_ssl_set_hs_dn_hints( mbedtls_ssl_context *ssl, + const mbedtls_x509_crt *crt ); +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + /** * \brief Set authmode for the current handshake. * diff --git a/library/ssl_misc.h b/library/ssl_misc.h index 119826f72..8230163a1 100644 --- a/library/ssl_misc.h +++ b/library/ssl_misc.h @@ -850,6 +850,9 @@ struct mbedtls_ssl_handshake_params #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) const unsigned char *sni_name; /*!< raw SNI */ size_t sni_name_len; /*!< raw SNI len */ +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + const mbedtls_x509_crt *dn_hints; /*!< acceptable client cert issuers */ +#endif #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ }; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 5fa02d26f..196973857 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1472,6 +1472,14 @@ void mbedtls_ssl_set_hs_ca_chain( mbedtls_ssl_context *ssl, ssl->handshake->sni_ca_crl = ca_crl; } +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +void mbedtls_ssl_set_hs_dn_hints( mbedtls_ssl_context *ssl, + const mbedtls_x509_crt *crt) +{ + ssl->handshake->dn_hints = crt; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + void mbedtls_ssl_set_hs_authmode( mbedtls_ssl_context *ssl, int authmode ) { diff --git a/library/ssl_tls12_client.c b/library/ssl_tls12_client.c index f516efab1..35f11eae0 100644 --- a/library/ssl_tls12_client.c +++ b/library/ssl_tls12_client.c @@ -2534,6 +2534,7 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl ) size_t sig_alg_len; #if defined(MBEDTLS_DEBUG_C) unsigned char *sig_alg; + unsigned char *dn; #endif MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate request" ) ); @@ -2681,6 +2682,43 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_DECODE_ERROR ); } +#if defined(MBEDTLS_DEBUG_C) + dn = buf + mbedtls_ssl_hs_hdr_len( ssl ) + 3 + n - dn_len; + for( size_t i = 0, dni_len = 0; i < dn_len; i += 2 + dni_len ) + { + unsigned char *p = dn + i + 2; + mbedtls_x509_name name; + mbedtls_x509_name *name_cur, *name_prv; + size_t asn1_len; + char s[MBEDTLS_X509_MAX_DN_NAME_SIZE]; + memset( &name, 0, sizeof( name ) ); + dni_len = MBEDTLS_GET_UINT16_BE( dn + i, 0 ); + if( dni_len > dn_len - i - 2 || + mbedtls_asn1_get_tag( &p, p + dni_len, &asn1_len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) != 0 || + mbedtls_x509_get_name( &p, p + asn1_len, &name ) != 0 ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_DECODE_ERROR ); + } + MBEDTLS_SSL_DEBUG_MSG( 3, + ( "DN hint: %.*s", + mbedtls_x509_dn_gets( s, sizeof(s), &name ), s ) ); + name_cur = name.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) ); + mbedtls_free( name_prv ); + } + } +#endif + exit: MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= parse certificate request" ) ); diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 21e5cda97..0952872cc 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -2489,6 +2489,16 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl ) * `mbedtls_ssl_conf_ca_cb()`, then the * CertificateRequest is currently left empty. */ +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if( ssl->handshake->dn_hints != NULL ) + crt = ssl->handshake->dn_hints; + else +#endif + if( ssl->conf->dn_hints != NULL ) + crt = ssl->conf->dn_hints; + else +#endif #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) if( ssl->handshake->sni_ca_chain != NULL ) crt = ssl->handshake->sni_ca_chain; diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 425181752..5231fe4d3 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -116,6 +116,7 @@ int main( void ) #define DFL_CID_VALUE_RENEGO NULL #define DFL_AUTH_MODE -1 #define DFL_CERT_REQ_CA_LIST MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED +#define DFL_CERT_REQ_DN_HINT 0 #define DFL_MFL_CODE MBEDTLS_SSL_MAX_FRAG_LEN_NONE #define DFL_TRUNC_HMAC -1 #define DFL_TICKETS MBEDTLS_SSL_SESSION_TICKETS_ENABLED @@ -506,6 +507,7 @@ int main( void ) " options: none, optional, required\n" \ " cert_req_ca_list=%%d default: 1 (send ca list)\n" \ " options: 1 (send ca list), 0 (don't send)\n" \ + " 2 (send conf dn hint), 3 (send hs dn hint)\n" \ USAGE_IO \ USAGE_KEY_OPAQUE \ "\n" \ @@ -629,6 +631,7 @@ struct options int allow_sha1; /* flag for SHA-1 support */ int auth_mode; /* verify mode for connection */ int cert_req_ca_list; /* should we send the CA list? */ + int cert_req_dn_hint; /* mode to set DN hints for CA list to send */ unsigned char mfl_code; /* code for maximum fragment length */ int trunc_hmac; /* accept truncated hmac? */ int tickets; /* enable / disable session tickets */ @@ -1597,6 +1600,7 @@ int main( int argc, char *argv[] ) opt.allow_sha1 = DFL_SHA1; opt.auth_mode = DFL_AUTH_MODE; opt.cert_req_ca_list = DFL_CERT_REQ_CA_LIST; + opt.cert_req_dn_hint = DFL_CERT_REQ_DN_HINT; opt.mfl_code = DFL_MFL_CODE; opt.trunc_hmac = DFL_TRUNC_HMAC; opt.tickets = DFL_TICKETS; @@ -1923,8 +1927,13 @@ int main( int argc, char *argv[] ) else if( strcmp( p, "cert_req_ca_list" ) == 0 ) { opt.cert_req_ca_list = atoi( q ); - if( opt.cert_req_ca_list < 0 || opt.cert_req_ca_list > 1 ) + if( opt.cert_req_ca_list < 0 || opt.cert_req_ca_list > 3 ) goto usage; + if( opt.cert_req_ca_list > 1 ) + { + opt.cert_req_dn_hint = opt.cert_req_ca_list; + opt.cert_req_ca_list = MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED; + } } else if( strcmp( p, "max_frag_len" ) == 0 ) { @@ -2732,6 +2741,16 @@ int main( int argc, char *argv[] ) if( opt.cert_req_ca_list != DFL_CERT_REQ_CA_LIST ) mbedtls_ssl_conf_cert_req_ca_list( &conf, opt.cert_req_ca_list ); +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + /* exercise setting DN hints for server certificate request + * (Intended for use where the client cert expected has been signed by + * a specific CA which is an intermediate in a CA chain, not the root) */ + if( opt.cert_req_dn_hint == 2 && key_cert_init2 ) + mbedtls_ssl_conf_dn_hints( &conf, &srvcert2 ); +#endif +#endif + #if defined(MBEDTLS_SSL_PROTO_DTLS) if( opt.hs_to_min != DFL_HS_TO_MIN || opt.hs_to_max != DFL_HS_TO_MAX ) mbedtls_ssl_conf_handshake_timeout( &conf, opt.hs_to_min, opt.hs_to_max ); @@ -3332,6 +3351,20 @@ reset: } #endif +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + /* exercise setting DN hints for server certificate request + * (Intended for use where the client cert expected has been signed by + * a specific CA which is an intermediate in a CA chain, not the root) + * (Additionally, the CA choice would typically be influenced by SNI + * if being set per-handshake using mbedtls_ssl_set_hs_dn_hints()) */ + if( opt.cert_req_dn_hint == 3 && key_cert_init2 ) + mbedtls_ssl_set_hs_dn_hints( &ssl, &srvcert2 ); +#endif +#endif +#endif + mbedtls_printf( " ok\n" ); /* diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index afabb6452..af327911b 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -5116,6 +5116,39 @@ run_test "Authentication: send CA list in CertificateRequest, client self sig -c "! mbedtls_ssl_handshake returned" \ -s "X509 - Certificate verification failed" +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "Authentication: send alt conf DN hints in CertificateRequest" \ + "$P_SRV debug_level=3 auth_mode=optional cert_req_ca_list=2 \ + crt_file2=data_files/server1.crt \ + key_file2=data_files/server1.key" \ + "$P_CLI debug_level=3 auth_mode=optional \ + crt_file=data_files/server6.crt \ + key_file=data_files/server6.key" \ + 0 \ + -c "DN hint: C=NL, O=PolarSSL, CN=PolarSSL Server 1" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "Authentication: send alt conf DN hints in CertificateRequest (2)" \ + "$P_SRV debug_level=3 auth_mode=optional cert_req_ca_list=2 \ + crt_file2=data_files/server2.crt \ + key_file2=data_files/server2.key" \ + "$P_CLI debug_level=3 auth_mode=optional \ + crt_file=data_files/server6.crt \ + key_file=data_files/server6.key" \ + 0 \ + -c "DN hint: C=NL, O=PolarSSL, CN=localhost" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "Authentication: send alt hs DN hints in CertificateRequest" \ + "$P_SRV debug_level=3 auth_mode=optional cert_req_ca_list=3 \ + crt_file2=data_files/server1.crt \ + key_file2=data_files/server1.key" \ + "$P_CLI debug_level=3 auth_mode=optional \ + crt_file=data_files/server6.crt \ + key_file=data_files/server6.key" \ + 0 \ + -c "DN hint: C=NL, O=PolarSSL, CN=PolarSSL Server 1" + # Tests for auth_mode, using CA callback, these are duplicated from the authentication tests # When updating these tests, modify the matching authentication tests accordingly