From 9ccc5e6d844b2f5cb0b11e75157d74ea784b54b2 Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Thu, 26 Mar 2020 10:53:50 +0800 Subject: [PATCH] cert bundle: Fix memory leak during cert verification Also refactors the unit tests and fixes the test case, as it was giving false positives. Closes IDFGH-2950 Closes https://github.com/espressif/esp-idf/issues/4983 --- components/mbedtls/CMakeLists.txt | 1 + .../mbedtls/esp_crt_bundle/esp_crt_bundle.c | 31 +-- components/mbedtls/test/prvtkey.pem | 50 ++--- components/mbedtls/test/server_cert_bundle | Bin 31227 -> 400 bytes components/mbedtls/test/server_cert_chain.pem | 79 ++------ components/mbedtls/test/test_esp_crt_bundle.c | 178 ++++++++++-------- 6 files changed, 159 insertions(+), 180 deletions(-) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 1f6f391411..37abd64355 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -1,4 +1,5 @@ idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(python PYTHON) idf_component_register(SRCS "esp_crt_bundle/esp_crt_bundle.c" INCLUDE_DIRS "port/include" "mbedtls/include" "esp_crt_bundle/include" diff --git a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c index 6d99f781ce..c03da8d9a8 100644 --- a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c +++ b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c @@ -19,7 +19,6 @@ #include "esp_log.h" #include "esp_err.h" - #define BUNDLE_HEADER_OFFSET 2 #define CRT_HEADER_OFFSET 4 @@ -43,45 +42,48 @@ typedef struct crt_bundle_t { static crt_bundle_t s_crt_bundle; static int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int data, uint32_t *flags); -static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len); +static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len); -static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len) +static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len) { - int ret = ESP_FAIL; + int ret = 0; mbedtls_x509_crt parent; const mbedtls_md_info_t *md_info; unsigned char hash[MBEDTLS_MD_MAX_SIZE]; mbedtls_x509_crt_init(&parent); - if ( (ret = mbedtls_pk_parse_public_key(&parent.pk , pub_key_buf, pub_key_len) ) != 0) { + if ( (ret = mbedtls_pk_parse_public_key(&parent.pk, pub_key_buf, pub_key_len) ) != 0) { ESP_LOGE(TAG, "PK parse failed with error %X", ret); - return ESP_FAIL; + goto cleanup; } // Fast check to avoid expensive computations when not necessary if (!mbedtls_pk_can_do(&parent.pk, child->sig_pk)) { ESP_LOGE(TAG, "Simple compare failed"); - return ESP_FAIL; + ret = -1; + goto cleanup; } md_info = mbedtls_md_info_from_type(child->sig_md); if ( (ret = mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash )) != 0 ) { ESP_LOGE(TAG, "Internal mbedTLS error %X", ret); - return ESP_FAIL; + goto cleanup; } - ret = mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent.pk, - child->sig_md, hash, mbedtls_md_get_size( md_info ), - child->sig.p, child->sig.len ) ; + if ( (ret = mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent.pk, + child->sig_md, hash, mbedtls_md_get_size( md_info ), + child->sig.p, child->sig.len )) != 0 ) { - if (ret != 0) { ESP_LOGE(TAG, "PK verify failed with error %X", ret); - return ESP_FAIL; + goto cleanup; } - return ESP_OK; +cleanup: + mbedtls_x509_crt_free(&parent); + + return ret; } @@ -205,6 +207,7 @@ esp_err_t esp_crt_bundle_attach(void *conf) void esp_crt_bundle_detach(mbedtls_ssl_config *conf) { free(s_crt_bundle.crts); + s_crt_bundle.crts = NULL; if (conf) { mbedtls_ssl_conf_verify(conf, NULL, NULL); } diff --git a/components/mbedtls/test/prvtkey.pem b/components/mbedtls/test/prvtkey.pem index ac8ee3b17b..bb0a510a7c 100644 --- a/components/mbedtls/test/prvtkey.pem +++ b/components/mbedtls/test/prvtkey.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/components/mbedtls/test/server_cert_bundle b/components/mbedtls/test/server_cert_bundle index 19123f6a93aac4adad599cb3f4d9344e3f0f52c0..0b7f09aa06da459957cb5c4df81df1876b9597b8 100644 GIT binary patch delta 349 zcmV-j0iyo<^8t_pGXMbqWC12HVlfLa2?hgI1O^iVLryUhFcAg=RRjnO3R7=wWi3;5 zVRU6N5ik!1162eElYJN;6BjTQ1_M;3`!A{8v?j?n8#xP&kH?KLj0Xp*+dFiz8yfkH0zT9-0>%%v3x8KMSm7~z zj;WH5vZxHBmXWxo(Crrt1pE#85Z`K93$W({5BKkdW}EhAH~fU-GgOS$<2!5~vj;PA zCrbx%6S1$3s^XX{f5LV>?qFsg?F&crBiSt7N@Z}ognJ(oL71-qtvU*G~?F2RIV~0|5X54>pE0 literal 31227 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 ); @@ -87,52 +93,49 @@ esp_err_t server_setup(mbedtls_endpoint_t * server) ret = mbedtls_x509_crt_parse( &server->cert, server_cert_chain_pem_start, server_cert_chain_pem_end - server_cert_chain_pem_start); - if( ret != 0 ) { + if ( ret != 0 ) { ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned %d", ret ); return ESP_FAIL; } - ret = mbedtls_pk_parse_key( &server->pkey, (const unsigned char *)server_pk_start , + ret = mbedtls_pk_parse_key( &server->pkey, (const unsigned char *)server_pk_start, server_pk_end - server_pk_start, NULL, 0 ); - if( ret != 0 ) { + if ( ret != 0 ) { ESP_LOGE(TAG, "mbedtls_pk_parse_key returned %d", ret ); return ESP_FAIL; } ESP_LOGI(TAG, "Bind on https://%s:%s/", SERVER_ADDRESS, SERVER_PORT ); - if( ( ret = mbedtls_net_bind( &server->listen_fd, NULL, SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) { + if ( ( ret = mbedtls_net_bind( &server->listen_fd, NULL, SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_net_bind returned %d", ret ); return ESP_FAIL; } + mbedtls_net_set_nonblock(&server->listen_fd); ESP_LOGI(TAG, "Seeding the random number generator"); - if( ( ret = mbedtls_ctr_drbg_seed( &server->ctr_drbg, mbedtls_entropy_func, &server->entropy, - NULL, 0) ) != 0 ) { + if ( ( ret = mbedtls_ctr_drbg_seed( &server->ctr_drbg, mbedtls_entropy_func, &server->entropy, + NULL, 0) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret ); return ESP_FAIL; } ESP_LOGI(TAG, "Setting up the SSL data"); - if( ( ret = mbedtls_ssl_config_defaults( &server->conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) - { + if ( ( ret = mbedtls_ssl_config_defaults( &server->conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret ); return ESP_FAIL; } mbedtls_ssl_conf_rng( &server->conf, mbedtls_ctr_drbg_random, &server->ctr_drbg ); - mbedtls_ssl_conf_ca_chain( &server->conf, server->cert.next, NULL ); - if (( ret = mbedtls_ssl_conf_own_cert( &server->conf, &server->cert, &server->pkey ) ) != 0 ) - { + if (( ret = mbedtls_ssl_conf_own_cert( &server->conf, &server->cert, &server->pkey ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned %d", ret ); return ESP_FAIL; } - if (( ret = mbedtls_ssl_setup( &server->ssl, &server->conf ) ) != 0 ) - { + if (( ret = mbedtls_ssl_setup( &server->ssl, &server->conf ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_setup returned %d", ret ); return ESP_FAIL; } @@ -151,27 +154,33 @@ void server_task(void *pvParameters) goto exit; } - ESP_LOGI(TAG, "Waiting for a remote connection" ); - if( ( ret = mbedtls_net_accept( &server.listen_fd, &server.client_fd, - NULL, 0, NULL ) ) != 0 ) { - ESP_LOGE(TAG, "mbedtls_net_accept returned %d", ret ); - goto exit; - } + bool connected = false; + while (!exit_flag) { - mbedtls_ssl_set_bio( &server.ssl, &server.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ret = mbedtls_net_accept( &server.listen_fd, &server.client_fd, NULL, 0, NULL ); - while(exit_flag == false) { - mbedtls_ssl_handshake( &server.ssl ); + if (ret == 0) { + connected = true; + } + + if (connected) { + mbedtls_ssl_set_bio( &server.ssl, &server.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ret = mbedtls_ssl_handshake( &server.ssl ); + mbedtls_ssl_session_reset(&server.ssl); + connected = false; + } + + vTaskDelay(20 / portTICK_PERIOD_MS); } ESP_LOGE(TAG, "Server shutdown"); - exit: - endpoint_teardown(&server); - xSemaphoreGive(*sema); - vTaskDelete(NULL); +exit: + endpoint_teardown(&server); + xSemaphoreGive(*sema); + vTaskDelete(NULL); } -esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint) +esp_err_t endpoint_teardown(mbedtls_endpoint_t *endpoint) { mbedtls_net_free( &endpoint->client_fd ); mbedtls_net_free( &endpoint->listen_fd ); @@ -187,7 +196,7 @@ esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint) return ESP_OK; } -esp_err_t client_setup(mbedtls_endpoint_t* client) +esp_err_t client_setup(mbedtls_endpoint_t *client) { int ret; mbedtls_ssl_config_init( &client->conf ); @@ -199,24 +208,24 @@ esp_err_t client_setup(mbedtls_endpoint_t* client) mbedtls_ctr_drbg_init( &client->ctr_drbg ); ESP_LOGI(TAG, "Seeding the random number generator"); - if((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func, &client->entropy, - NULL, 0)) != 0) { + if ((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func, &client->entropy, + NULL, 0)) != 0) { ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret); return ESP_FAIL; } ESP_LOGI(TAG, "Setting hostname for TLS session..."); - /* Hostname set here should match CN in server certificate */ - if((ret = mbedtls_ssl_set_hostname(&client->ssl, SERVER_ADDRESS)) != 0) { + /* Hostname set here should match CN in server certificate */ + if ((ret = mbedtls_ssl_set_hostname(&client->ssl, SERVER_ADDRESS)) != 0) { ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret); return ESP_FAIL; } ESP_LOGI(TAG, "Setting up the SSL/TLS structure..."); - if((ret = mbedtls_ssl_config_defaults(&client->conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + if ((ret = mbedtls_ssl_config_defaults(&client->conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret); return ESP_FAIL; } @@ -230,21 +239,26 @@ esp_err_t client_setup(mbedtls_endpoint_t* client) return ESP_OK; } -void client_task(void *pvParameters) +int client_task(const uint8_t *bundle, esp_crt_validate_res_t *res) { - int ret; - - xSemaphoreHandle *sema = (xSemaphoreHandle *) pvParameters; + int ret = ESP_FAIL; mbedtls_endpoint_t client; - if(client_setup(&client) != ESP_OK) { + *res = ESP_CRT_VALIDATE_UNKNOWN; + + if (client_setup(&client) != ESP_OK) { ESP_LOGE(TAG, "SSL client setup failed"); goto exit; } - // Set the custom bundle which DOESN'T includes the server's root certificate (default bundle) esp_crt_bundle_attach(&client.conf); + if (bundle) { + /* Set a bundle different from the menuconfig bundle */ + esp_crt_bundle_set(bundle); + } + + ESP_LOGI(TAG, "Connecting to %s:%s...", SERVER_ADDRESS, SERVER_PORT); if ((ret = mbedtls_net_connect(&client.client_fd, SERVER_ADDRESS, SERVER_PORT, MBEDTLS_NET_PROTO_TCP)) != 0) { @@ -256,57 +270,61 @@ void client_task(void *pvParameters) mbedtls_ssl_set_bio(&client.ssl, &client.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle that is missing the server root certificate"); - ret = mbedtls_ssl_handshake(&client.ssl); + while ( ( ret = mbedtls_ssl_handshake( &client.ssl ) ) != 0 ) { + if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { + printf( "mbedtls_ssl_handshake failed with -0x%x\n", -ret ); + break; + } + } ESP_LOGI(TAG, "Verifying peer X.509 certificate for bundle ..."); - TEST_ASSERT(mbedtls_ssl_get_verify_result(&client.ssl) != 0); + ret = mbedtls_ssl_get_verify_result(&client.ssl); + + *res = (ret == 0) ? ESP_CRT_VALIDATE_OK : ESP_CRT_VALIDATE_FAIL; + // Reset session before new connection mbedtls_ssl_close_notify(&client.ssl); mbedtls_ssl_session_reset(&client.ssl); - - // Set the custom bundle which includes the server's root certificate - esp_crt_bundle_set(server_cert_bundle_start); - - ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle containing the server root certificate"); - ret = mbedtls_ssl_handshake(&client.ssl); - - ESP_LOGI(TAG, "Verifying peer X.509 certificate ..."); - ret = mbedtls_ssl_get_verify_result(&client.ssl); - TEST_ASSERT(ret == 0); - - if(ret == 0) { - ESP_LOGI(TAG, "Certificate validated"); - } + mbedtls_net_free( &client.client_fd); - exit_flag = true; +exit: + mbedtls_ssl_close_notify(&client.ssl); + mbedtls_ssl_session_reset(&client.ssl); + esp_crt_bundle_detach(&client.conf); + endpoint_teardown(&client); - exit: - mbedtls_ssl_close_notify(&client.ssl); - mbedtls_ssl_session_reset(&client.ssl); - esp_crt_bundle_detach(&client.conf); - endpoint_teardown(&client); - xSemaphoreGive(*sema); - vTaskDelete(NULL); + return ret; } TEST_CASE("custom certificate bundle", "[mbedtls]") { + esp_crt_validate_res_t validate_res; + test_case_uses_tcpip(); - xSemaphoreHandle exit_sema = xSemaphoreCreateCounting(2, 0); + xSemaphoreHandle exit_sema = xSemaphoreCreateBinary(); - xTaskCreate(server_task, "server task", 8192, &exit_sema, 5, NULL); + exit_flag = false; + xTaskCreate(server_task, "server task", 8192, &exit_sema, 10, NULL); // Wait for the server to start up vTaskDelay(100 / portTICK_PERIOD_MS); - xTaskCreate(client_task, "https_get_task", 8192, &exit_sema, 5, NULL); - for(int i = 0; i < 2; i++) { - if(!xSemaphoreTake(exit_sema, 10000/portTICK_PERIOD_MS)) { - TEST_FAIL_MESSAGE("exit_sem not released by test task"); - } + /* Test with default crt bundle that doesnt contain the ca crt */ + client_task(NULL, &validate_res); + TEST_ASSERT(validate_res == ESP_CRT_VALIDATE_FAIL); + + /* Test with bundle that does contain the CA crt */ + client_task(server_cert_bundle_start, &validate_res); + TEST_ASSERT(validate_res == ESP_CRT_VALIDATE_OK); + + exit_flag = true; + + if (!xSemaphoreTake(exit_sema, 10000 / portTICK_PERIOD_MS)) { + TEST_FAIL_MESSAGE("exit_sem not released by server task"); } + vSemaphoreDelete(exit_sema); -} \ No newline at end of file +}