diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 06860e22c6..6295b1816c 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -492,6 +492,7 @@ #define MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ #define MBEDTLS_SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ #define MBEDTLS_SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */ +#define MBEDTLS_SSL_ALERT_MSG_CERT_REQUIRED 116 /* 0x74 */ #define MBEDTLS_SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */ #define MBEDTLS_SSL_HS_HELLO_REQUEST 0 diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c index e69fd7b622..a260717e13 100644 --- a/library/ssl_tls13_generic.c +++ b/library/ssl_tls13_generic.c @@ -153,6 +153,30 @@ static void ssl_tls13_create_verify_structure( const unsigned char *transcript_h *verify_buffer_len = idx; } +/* Coordinate: Check whether a certificate verify message is expected. + * Returns a negative value on failure, and otherwise + * - SSL_CERTIFICATE_VERIFY_SKIP + * - SSL_CERTIFICATE_VERIFY_READ + * to indicate if the CertificateVerify message should be present or not. + */ +#define SSL_CERTIFICATE_VERIFY_SKIP 0 +#define SSL_CERTIFICATE_VERIFY_READ 1 +static int ssl_tls13_read_certificate_verify_coordinate( mbedtls_ssl_context *ssl ) +{ + if( mbedtls_ssl_tls13_some_psk_enabled( ssl ) ) + return( SSL_CERTIFICATE_VERIFY_SKIP ); + +#if !defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); +#else + if( ssl->session_negotiate->peer_cert == NULL ) + return( SSL_CERTIFICATE_VERIFY_SKIP ); + + return( SSL_CERTIFICATE_VERIFY_READ ); +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ +} + static int ssl_tls13_parse_certificate_verify( mbedtls_ssl_context *ssl, const unsigned char *buf, const unsigned char *end, @@ -315,6 +339,20 @@ int mbedtls_ssl_tls13_process_certificate_verify( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) ); + MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_read_certificate_verify_coordinate( ssl ) ); + if( ret == SSL_CERTIFICATE_VERIFY_SKIP ) + { + MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) ); + ret = 0; + goto cleanup; + } + else if( ret != SSL_CERTIFICATE_VERIFY_READ ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg( ssl, MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, &buf, &buf_len ) ); @@ -367,13 +405,66 @@ cleanup: /* * - * STATE HANDLING: Incoming Certificate, client-side only currently. + * STATE HANDLING: Incoming Certificate. * */ -/* - * Implementation +/* Coordination: Check if a certificate is expected. + * Returns a negative error code on failure, and otherwise + * SSL_CERTIFICATE_EXPECTED or + * SSL_CERTIFICATE_SKIP + * indicating whether a Certificate message is expected or not. */ +#define SSL_CERTIFICATE_EXPECTED 0 +#define SSL_CERTIFICATE_SKIP 1 + +static int ssl_tls13_read_certificate_coordinate( mbedtls_ssl_context *ssl ) +{ +#if defined(MBEDTLS_SSL_SRV_C) + int authmode = ssl->conf->authmode; +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_SRV_C) + if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "Switch to handshake keys for inbound traffic" ) ); + + mbedtls_ssl_set_inbound_transform( ssl, ssl->handshake->transform_handshake ); + } +#endif /* MBEDTLS_SSL_SRV_C */ + + if( mbedtls_ssl_tls13_some_psk_enabled( ssl ) ) + return( SSL_CERTIFICATE_SKIP ); + +#if !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + ( ( void )authmode ); + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); +#else +#if defined(MBEDTLS_SSL_SRV_C) + if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER ) + { + /* If SNI was used, overwrite authentication mode + * from the configuration. */ +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if( ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET ) + authmode = ssl->handshake->sni_authmode; +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + + if( authmode == MBEDTLS_SSL_VERIFY_NONE ) + { + /* NOTE: Is it intentional that we set verify_result + * to SKIP_VERIFY on server-side only? */ + ssl->session_negotiate->verify_result = + MBEDTLS_X509_BADCERT_SKIP_VERIFY; + return( SSL_CERTIFICATE_SKIP ); + } + } +#endif /* MBEDTLS_SSL_SRV_C */ + + return( SSL_CERTIFICATE_EXPECTED ); +#endif /* !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +} #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) @@ -415,10 +506,39 @@ static int ssl_tls13_parse_certificate( mbedtls_ssl_context *ssl, const unsigned char *p = buf; const unsigned char *certificate_list_end; - MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 4 ); + MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 1 ); certificate_request_context_len = p[0]; - certificate_list_len = MBEDTLS_GET_UINT24_BE( p, 1 ); - p += 4; + p++; + +#if defined(MBEDTLS_SSL_SRV_C) + if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER ) + { + MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, + certificate_request_context_len + 3 ); + + /* check whether we got an empty certificate message */ + if( memcmp( p + certificate_request_context_len , "\0\0\0", 3 ) == 0 ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, + ( "client has no certificate - empty certificate message received" ) ); + + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING; + if( ssl->conf->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL ) + return( 0 ); + else + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "client certificate required" ) ); + MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_CERT_REQUIRED, + MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE ); + return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE ); + } + } + } +#endif /* MBEDTLS_SSL_SRV_C */ + + MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 3 ); + certificate_list_len = MBEDTLS_GET_UINT24_BE( p, 0 ); + p += 3; /* In theory, the certificate list can be up to 2^24 Bytes, but we don't * support anything beyond 2^16 = 64K. @@ -547,10 +667,56 @@ static int ssl_tls13_parse_certificate( mbedtls_ssl_context *ssl, static int ssl_tls13_validate_certificate( mbedtls_ssl_context *ssl ) { int ret = 0; + int authmode = ssl->conf->authmode; mbedtls_x509_crt *ca_chain; mbedtls_x509_crl *ca_crl; uint32_t verify_result = 0; + /* If SNI was used, overwrite authentication mode + * from the configuration. */ +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if( ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET ) + authmode = ssl->handshake->sni_authmode; +#endif + + /* + * If the client hasn't sent a certificate ( i.e. it sent + * an empty certificate chain ), this is reflected in the peer CRT + * structure being unset. + * Check for that and handle it depending on the + * server's authentication mode. + */ +#if defined(MBEDTLS_SSL_SRV_C) + if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->session_negotiate->peer_cert == NULL ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "client has no certificate" ) ); + + /* The client was asked for a certificate but didn't send + one. The client should know what's going on, so we + don't send an alert. */ + + /* Note that for authmode == VERIFY_NONE we don't end up in this + * routine in the first place, because ssl_tls13_read_certificate_coordinate + * will return CERTIFICATE_SKIP. */ + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING; + if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL ) + return( 0 ); + else + return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE ); + } +#endif /* MBEDTLS_SSL_SRV_C */ + + if( authmode == MBEDTLS_SSL_VERIFY_NONE ) + { + /* NOTE: This happens on client-side only, with the + * server-side case of VERIFY_NONE being handled earlier + * and leading to `ssl->verify_result` being set to + * MBEDTLS_X509_BADCERT_SKIP_VERIFY -- + * is this difference intentional? */ + return( 0 ); + } + #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) if( ssl->handshake->sni_ca_chain != NULL ) { @@ -593,8 +759,21 @@ static int ssl_tls13_validate_certificate( mbedtls_ssl_context *ssl ) ret = MBEDTLS_ERR_SSL_BAD_CERTIFICATE; } + /* mbedtls_x509_crt_verify_with_profile is supposed to report a + * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED, + * with details encoded in the verification flags. All other kinds + * of error codes, including those from the user provided f_vrfy + * functions, are treated as fatal and lead to a failure of + * ssl_tls13_parse_certificate even if verification was optional. */ + if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL && + ( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED || + ret == MBEDTLS_ERR_SSL_BAD_CERTIFICATE ) ) + { + ret = 0; + } - if( ca_chain == NULL ) + + if( ca_chain == NULL && authmode == MBEDTLS_SSL_VERIFY_REQUIRED ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no CA chain" ) ); ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED; @@ -654,29 +833,47 @@ int mbedtls_ssl_tls13_process_certificate( mbedtls_ssl_context *ssl ) int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) ); + /* Coordination: + * Check if we expect a certificate, and if yes, + * check if a non-empty certificate has been sent. + */ + MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_read_certificate_coordinate( ssl ) ); #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) - unsigned char *buf; - size_t buf_len; + if( ret == SSL_CERTIFICATE_EXPECTED ) + { + unsigned char *buf; + size_t buf_len; - MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg( - ssl, MBEDTLS_SSL_HS_CERTIFICATE, - &buf, &buf_len ) ); + MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg( + ssl, MBEDTLS_SSL_HS_CERTIFICATE, + &buf, &buf_len ) ); - /* Parse the certificate chain sent by the peer. */ - MBEDTLS_SSL_PROC_CHK( ssl_tls13_parse_certificate( ssl, buf, buf + buf_len ) ); - /* Validate the certificate chain and set the verification results. */ - MBEDTLS_SSL_PROC_CHK( ssl_tls13_validate_certificate( ssl ) ); + /* Parse the certificate chain sent by the peer. */ + MBEDTLS_SSL_PROC_CHK( ssl_tls13_parse_certificate( ssl, buf, + buf + buf_len ) ); + /* Validate the certificate chain and set the verification results. */ + MBEDTLS_SSL_PROC_CHK( ssl_tls13_validate_certificate( ssl ) ); + + mbedtls_ssl_add_hs_msg_to_checksum( ssl, MBEDTLS_SSL_HS_CERTIFICATE, + buf, buf_len ); + } + else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + if( ret == SSL_CERTIFICATE_SKIP ) + { + MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) ); + ret = 0; + } + else + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } - mbedtls_ssl_add_hs_msg_to_checksum( ssl, MBEDTLS_SSL_HS_CERTIFICATE, - buf, buf_len ); cleanup: MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) ); -#else - MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); - ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; -#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ return( ret ); } #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index 8c8eb2ab6a..1288e49444 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -1613,6 +1613,24 @@ int mbedtls_ssl_tls13_handshake_server_step( mbedtls_ssl_context *ssl ) ret = ssl_tls13_handshake_wrapup( ssl ); break; + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + ret = mbedtls_ssl_tls13_process_certificate( ssl ); + if( ret == 0 ) + { + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY ); + } + break; + + case MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY: + ret = mbedtls_ssl_tls13_process_certificate_verify( ssl ); + if( ret == 0 ) + { + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_FINISHED ); + } + break; + default: MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );