From e9e3dc790443429c7ccb74f967b5f6d908ae41af Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 26 Sep 2022 16:14:09 +0200 Subject: [PATCH 1/4] esp-tls: Add support for the CERTIFICATE SELECTION HOOK. The hook has access to required information so that the application can make a more informed decision on which certificate to serve (such as alpn value, server certificate type, etc.) Closes https://github.com/espressif/esp-idf/pull/9833 Signed-off-by: Aditya Patwardhan --- components/esp-tls/Kconfig | 8 ++ components/esp-tls/esp_tls.h | 22 ++++ components/esp-tls/esp_tls_mbedtls.c | 19 ++++ .../include/esp_https_server.h | 5 + .../esp_https_server/src/https_server.c | 102 ++++++++++++------ 5 files changed, 123 insertions(+), 33 deletions(-) diff --git a/components/esp-tls/Kconfig b/components/esp-tls/Kconfig index afd38d22a9..6c0d7586bc 100644 --- a/components/esp-tls/Kconfig +++ b/components/esp-tls/Kconfig @@ -57,6 +57,14 @@ menu "ESP-TLS" help Sets the session ticket timeout used in the tls server. + config ESP_TLS_SERVER_CERT_SELECT_HOOK + bool "Certificate selection hook" + depends on ESP_TLS_USING_MBEDTLS + help + Ability to configure and use a certificate selection callback during server handshake, + to select a certificate to present to the client based on the TLS extensions supplied in + the client hello (alpn, sni, etc). + config ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL bool "ESP-TLS Server: Set minimum Certificate Verification mode to Optional" depends on ESP_TLS_SERVER && ESP_TLS_USING_MBEDTLS diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index 24680b48d4..d018824f13 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -197,6 +197,20 @@ typedef struct esp_tls_server_session_ticket_ctx { } esp_tls_server_session_ticket_ctx_t; #endif + +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) +/** + * @brief tls handshake callback + * Can be used to configure per-handshake attributes for the TLS connection. + * E.g. Client certificate / Key, Authmode, Client CA verification, etc. + * + * @param ssl mbedtls_ssl_context that can be used for changing settings + * @return The reutn value of the callback must be 0 if successful, + * or a specific MBEDTLS_ERR_XXX code, which will cause the handhsake to abort + */ +typedef mbedtls_ssl_hs_cb_t esp_tls_handshake_callback; +#endif + typedef struct esp_tls_cfg_server { const char **alpn_protos; /*!< Application protocols required for HTTP2. If HTTP2/ALPN support is required, a list @@ -259,6 +273,14 @@ typedef struct esp_tls_cfg_server { Call esp_tls_cfg_server_session_tickets_free to free the data associated with this context. */ #endif + +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + esp_tls_handshake_callback cert_select_cb; /*!< Certificate selection callback that gets called after ClientHello is processed. + Can be used as an SNI callback, but also has access to other + TLS extensions, such as ALPN and server_certificate_type . */ +#endif + + void *userdata; /*!< User data to be add to the ssl context. Can be retrieved by callbacks */ } esp_tls_cfg_server_t; /** diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index 53ff9e6539..4bd0e5852d 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -512,12 +512,21 @@ esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls) return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED; } + mbedtls_ssl_conf_set_user_data_p(&tls->conf, cfg->userdata); + #ifdef CONFIG_MBEDTLS_SSL_ALPN if (cfg->alpn_protos) { mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos); } #endif +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + if (cfg->cert_select_cb != NULL) { + ESP_LOGI(TAG, "Initializing server side certificate selection callback"); + mbedtls_ssl_conf_cert_cb(&tls->conf, cfg->cert_select_cb); + } +#endif + if (cfg->cacert_buf != NULL) { esp_ret = set_ca_cert(tls, cfg->cacert_buf, cfg->cacert_bytes); if (esp_ret != ESP_OK) { @@ -569,7 +578,16 @@ esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls) return esp_ret; } } else { +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + if (cfg->cert_select_cb == NULL) { + ESP_LOGE(TAG, "Missing server certificate and/or key and no certificate selection callback is defined"); + } else { + ESP_LOGD(TAG, "Missing server certificate and/or key, but certificate selection callback is defined. Callback MUST ALWAYS call mbedtls_ssl_set_hs_own_cert, or the handshake will abort!"); + return ESP_OK; + } +#else ESP_LOGE(TAG, "Missing server certificate and/or key"); +#endif return ESP_ERR_INVALID_STATE; } @@ -790,6 +808,7 @@ int esp_mbedtls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp tls->conn_state = ESP_TLS_FAIL; return -1; } + tls->read = esp_mbedtls_read; tls->write = esp_mbedtls_write; int ret; diff --git a/components/esp_https_server/include/esp_https_server.h b/components/esp_https_server/include/esp_https_server.h index 9fbfe4c250..d224a8fa05 100644 --- a/components/esp_https_server/include/esp_https_server.h +++ b/components/esp_https_server/include/esp_https_server.h @@ -96,6 +96,11 @@ struct httpd_ssl_config { /** User callback for esp_https_server */ esp_https_server_user_cb *user_cb; + +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + esp_tls_handshake_callback cert_select_cb; /*!< Certificate selection callback to use */ +#endif + void *ssl_userdata; /*!< user data to add to the ssl context */ }; typedef struct httpd_ssl_config httpd_ssl_config_t; diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c index 50f581cae1..bfb4ed4467 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -200,65 +200,101 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con } esp_tls_cfg_server_t *cfg = (esp_tls_cfg_server_t *)calloc(1, sizeof(esp_tls_cfg_server_t)); if (!cfg) { - free(ssl_ctx); - return NULL; + goto free_ssl_ctx; } if (config->session_tickets) { if ( esp_tls_cfg_server_session_tickets_init(cfg) != ESP_OK ) { ESP_LOGE(TAG, "Failed to init session ticket support"); - free(ssl_ctx); - free(cfg); - return NULL; + goto free_cfg; } } + cfg->userdata = config->ssl_userdata; + +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + cfg->cert_select_cb = config->cert_select_cb; +#endif + ssl_ctx->tls_cfg = cfg; ssl_ctx->user_cb = config->user_cb; /* cacert = CA which signs client cert, or client cert itself */ - if(config->cacert_pem != NULL) { + if (config->cacert_pem != NULL && config->cacert_len > 0) { cfg->cacert_buf = (unsigned char *)malloc(config->cacert_len); - if (!cfg->cacert_buf) { - ESP_LOGE(TAG, "Could not allocate memory"); - free(cfg); - free(ssl_ctx); - return NULL; + + if (cfg->cacert_buf) { + memcpy((char *) cfg->cacert_buf, config->cacert_pem, config->cacert_len); + cfg->cacert_bytes = config->cacert_len; + } else { + ESP_LOGE(TAG, "Could not allocate memory for client certificate authority"); + goto free_cfg; } - memcpy((char *)cfg->cacert_buf, config->cacert_pem, config->cacert_len); - cfg->cacert_bytes = config->cacert_len; } /* servercert = cert of server itself */ - cfg->servercert_buf = (unsigned char *)malloc(config->servercert_len); - if (!cfg->servercert_buf) { - ESP_LOGE(TAG, "Could not allocate memory"); - free((void *)cfg->cacert_buf); - free(cfg); - free(ssl_ctx); - return NULL; + if (config->servercert != NULL && config->servercert_len > 0) { + cfg->servercert_buf = (unsigned char *)malloc(config->servercert_len); + + if (cfg->servercert_buf) { + memcpy((char *) cfg->servercert_buf, config->servercert, config->servercert_len); + cfg->servercert_bytes = config->servercert_len; + } else { + ESP_LOGE(TAG, "Could not allocate memory for server certificate"); + goto free_cacert; + } + } else { +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + if (config->cert_select_cb == NULL) { +#endif + ESP_LOGE(TAG, "No Server certificate supplied"); + goto free_cacert; +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + } else { + ESP_LOGW(TAG, "Server certificate not supplied, make sure to supply it in the certificate selection hook!"); + } +#endif } - memcpy((char *)cfg->servercert_buf, config->servercert, config->servercert_len); - cfg->servercert_bytes = config->servercert_len; /* Pass on secure element boolean */ cfg->use_secure_element = config->use_secure_element; if (!cfg->use_secure_element) { - cfg->serverkey_buf = (unsigned char *)malloc(config->prvtkey_len); - if (!cfg->serverkey_buf) { - ESP_LOGE(TAG, "Could not allocate memory"); - free((void *)cfg->servercert_buf); - free((void *)cfg->cacert_buf); - free(cfg); - free(ssl_ctx); - return NULL; + if (config->prvtkey_pem != NULL && config->prvtkey_len > 0) { + cfg->serverkey_buf = (unsigned char *) malloc(config->prvtkey_len); + + if (cfg->serverkey_buf) { + memcpy((char *) cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len); + cfg->serverkey_bytes = config->prvtkey_len; + } else { + ESP_LOGE(TAG, "Could not allocate memory for server key"); + goto free_servercert; + } + } else { +#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) + if (config->cert_select_cb == NULL) { + ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present"); + goto free_servercert; + } else { + ESP_LOGW(TAG, "Server key not supplied, make sure to supply it in the certificate selection hook"); + } +#else + ESP_LOGE(TAG, "No Server key supplied"); + goto free_servercert; +#endif } } - memcpy((char *)cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len); - cfg->serverkey_bytes = config->prvtkey_len; - return ssl_ctx; + +free_servercert: + free((void *) cfg->servercert_buf); +free_cacert: + free((void *) cfg->cacert_buf); +free_cfg: + free(cfg); +free_ssl_ctx: + free(ssl_ctx); + return NULL; } /** Start the server */ From 8ad4de7991dc2d61284828224d6c9db06bd90579 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 21 Oct 2022 12:51:31 +0530 Subject: [PATCH 2/4] esp-tls: Add changes to the Cert selection callback PR. --- components/esp-tls/Kconfig | 2 +- components/esp-tls/esp_tls.h | 5 ++-- components/esp-tls/esp_tls_mbedtls.c | 7 +++--- .../include/esp_https_server.h | 6 ++--- .../esp_https_server/src/https_server.c | 23 ++++++++----------- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/components/esp-tls/Kconfig b/components/esp-tls/Kconfig index 6c0d7586bc..ea73d0f979 100644 --- a/components/esp-tls/Kconfig +++ b/components/esp-tls/Kconfig @@ -59,7 +59,7 @@ menu "ESP-TLS" config ESP_TLS_SERVER_CERT_SELECT_HOOK bool "Certificate selection hook" - depends on ESP_TLS_USING_MBEDTLS + depends on ESP_TLS_USING_MBEDTLS && ESP_TLS_SERVER help Ability to configure and use a certificate selection callback during server handshake, to select a certificate to present to the client based on the TLS extensions supplied in diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index d018824f13..07c1c8089d 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -198,7 +198,6 @@ typedef struct esp_tls_server_session_ticket_ctx { #endif -#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) /** * @brief tls handshake callback * Can be used to configure per-handshake attributes for the TLS connection. @@ -209,7 +208,6 @@ typedef struct esp_tls_server_session_ticket_ctx { * or a specific MBEDTLS_ERR_XXX code, which will cause the handhsake to abort */ typedef mbedtls_ssl_hs_cb_t esp_tls_handshake_callback; -#endif typedef struct esp_tls_cfg_server { const char **alpn_protos; /*!< Application protocols required for HTTP2. @@ -274,13 +272,14 @@ typedef struct esp_tls_cfg_server { to free the data associated with this context. */ #endif + void *userdata; /*!< User data to be added to the ssl context. + Can be retrieved by callbacks */ #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) esp_tls_handshake_callback cert_select_cb; /*!< Certificate selection callback that gets called after ClientHello is processed. Can be used as an SNI callback, but also has access to other TLS extensions, such as ALPN and server_certificate_type . */ #endif - void *userdata; /*!< User data to be add to the ssl context. Can be retrieved by callbacks */ } esp_tls_cfg_server_t; /** diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index 4bd0e5852d..d8aaa991a1 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -522,7 +522,7 @@ esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls) #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) if (cfg->cert_select_cb != NULL) { - ESP_LOGI(TAG, "Initializing server side certificate selection callback"); + ESP_LOGI(TAG, "Initializing server side cert selection cb"); mbedtls_ssl_conf_cert_cb(&tls->conf, cfg->cert_select_cb); } #endif @@ -580,9 +580,10 @@ esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls) } else { #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) if (cfg->cert_select_cb == NULL) { - ESP_LOGE(TAG, "Missing server certificate and/or key and no certificate selection callback is defined"); + ESP_LOGE(TAG, "No cert select cb is defined"); } else { - ESP_LOGD(TAG, "Missing server certificate and/or key, but certificate selection callback is defined. Callback MUST ALWAYS call mbedtls_ssl_set_hs_own_cert, or the handshake will abort!"); + /* At this point Callback MUST ALWAYS call mbedtls_ssl_set_hs_own_cert, or the handshake will abort! */ + ESP_LOGD(TAG, "Missing server cert and/or key, but cert selection cb is defined."); return ESP_OK; } #else diff --git a/components/esp_https_server/include/esp_https_server.h b/components/esp_https_server/include/esp_https_server.h index d224a8fa05..071d5b5ff4 100644 --- a/components/esp_https_server/include/esp_https_server.h +++ b/components/esp_https_server/include/esp_https_server.h @@ -97,10 +97,8 @@ struct httpd_ssl_config { /** User callback for esp_https_server */ esp_https_server_user_cb *user_cb; -#if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) - esp_tls_handshake_callback cert_select_cb; /*!< Certificate selection callback to use */ -#endif void *ssl_userdata; /*!< user data to add to the ssl context */ + esp_tls_handshake_callback cert_select_cb; /*!< Certificate selection callback to use */ }; typedef struct httpd_ssl_config httpd_ssl_config_t; @@ -150,6 +148,8 @@ typedef struct httpd_ssl_config httpd_ssl_config_t; .session_tickets = false, \ .use_secure_element = false, \ .user_cb = NULL, \ + .ssl_userdata = NULL, \ + .cert_select_cb = NULL \ } /** diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c index bfb4ed4467..f77c775ea3 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -200,13 +200,13 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con } esp_tls_cfg_server_t *cfg = (esp_tls_cfg_server_t *)calloc(1, sizeof(esp_tls_cfg_server_t)); if (!cfg) { - goto free_ssl_ctx; + goto exit; } if (config->session_tickets) { if ( esp_tls_cfg_server_session_tickets_init(cfg) != ESP_OK ) { ESP_LOGE(TAG, "Failed to init session ticket support"); - goto free_cfg; + goto exit; } } @@ -228,7 +228,7 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->cacert_bytes = config->cacert_len; } else { ESP_LOGE(TAG, "Could not allocate memory for client certificate authority"); - goto free_cfg; + goto exit; } } @@ -241,14 +241,14 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->servercert_bytes = config->servercert_len; } else { ESP_LOGE(TAG, "Could not allocate memory for server certificate"); - goto free_cacert; + goto exit; } } else { #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) if (config->cert_select_cb == NULL) { #endif ESP_LOGE(TAG, "No Server certificate supplied"); - goto free_cacert; + goto exit; #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) } else { ESP_LOGW(TAG, "Server certificate not supplied, make sure to supply it in the certificate selection hook!"); @@ -260,39 +260,36 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con cfg->use_secure_element = config->use_secure_element; if (!cfg->use_secure_element) { if (config->prvtkey_pem != NULL && config->prvtkey_len > 0) { - cfg->serverkey_buf = (unsigned char *) malloc(config->prvtkey_len); + cfg->serverkey_buf = malloc(config->prvtkey_len); if (cfg->serverkey_buf) { memcpy((char *) cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len); cfg->serverkey_bytes = config->prvtkey_len; } else { ESP_LOGE(TAG, "Could not allocate memory for server key"); - goto free_servercert; + goto exit; } } else { #if defined(CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK) if (config->cert_select_cb == NULL) { ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present"); - goto free_servercert; + goto exit; } else { ESP_LOGW(TAG, "Server key not supplied, make sure to supply it in the certificate selection hook"); } #else ESP_LOGE(TAG, "No Server key supplied"); - goto free_servercert; + goto exit; #endif } } return ssl_ctx; -free_servercert: +exit: free((void *) cfg->servercert_buf); -free_cacert: free((void *) cfg->cacert_buf); -free_cfg: free(cfg); -free_ssl_ctx: free(ssl_ctx); return NULL; } From 1f6d66b152f86cde3a3280f550a71d9ba53f2c73 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 21 Oct 2022 16:01:22 +0530 Subject: [PATCH 3/4] esp_tls: Update documentation for cert callback --- docs/en/api-reference/protocols/esp_tls.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/en/api-reference/protocols/esp_tls.rst b/docs/en/api-reference/protocols/esp_tls.rst index 0627034629..a05f3da1b4 100644 --- a/docs/en/api-reference/protocols/esp_tls.rst +++ b/docs/en/api-reference/protocols/esp_tls.rst @@ -57,6 +57,23 @@ The ESP-TLS provides multiple options for TLS server verification on the client * **skip server verification**: This is an insecure option provided in the ESP-TLS for testing purpose. The option can be set by enabling :ref:`CONFIG_ESP_TLS_INSECURE` and :ref:`CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY` in the ESP-TLS menuconfig. When this option is enabled the ESP-TLS will skip server verification by default when no other options for server verification are selected in the :cpp:type:`esp_tls_cfg_t` structure. *WARNING:Enabling this option comes with a potential risk of establishing a TLS connection with a server which has a fake identity, provided that the server certificate is not provided either through API or other mechanism like ca_store etc.* +ESP-TLS Server cert selection hook +---------------------------------- +The ESP-TLS component provides an option to set the server cert selection hook when using the mbedTLS stack. This provides an ability to configure and use a certificate selection callback during server handshake, to select a certificate to present to the client based on the TLS extensions supplied in the client hello (alpn, sni, etc). To enable this feature, please enable :ref:`CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK` in the ESP-TLS menuconfig. +The certificate selection callback can be configured in the :cpp:type:`esp_tls_cfg_t` structure as follows: + +.. code-block:: c + + int cert_selection_callback(mbedtls_ssl_context *ssl) + { + /* Code that the callback should execute */ + return 0; + } + + esp_tls_cfg_t cfg = { + cert_select_cb = cert_section_callback, + }; + .. _esp_tls_wolfssl: Underlying SSL/TLS Library Options From 14e64783e7ef65967326ad7d7b63de45542a97ea Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Wed, 2 Nov 2022 09:58:32 +0530 Subject: [PATCH 4/4] esp-tls/Kconfig: Fix dependency for ESP-TLS Server menuconfig option --- components/esp-tls/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp-tls/Kconfig b/components/esp-tls/Kconfig index ea73d0f979..ac28442ef3 100644 --- a/components/esp-tls/Kconfig +++ b/components/esp-tls/Kconfig @@ -40,6 +40,7 @@ menu "ESP-TLS" config ESP_TLS_SERVER bool "Enable ESP-TLS Server" + depends on (ESP_TLS_USING_MBEDTLS && MBEDTLS_TLS_SERVER) || ESP_TLS_USING_WOLFSSL help Enable support for creating server side SSL/TLS session, available for mbedTLS as well as wolfSSL TLS library.