diff --git a/components/esp-tls/Kconfig b/components/esp-tls/Kconfig index afd38d22a9..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. @@ -57,6 +58,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 && 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 + 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..07c1c8089d 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -197,6 +197,18 @@ typedef struct esp_tls_server_session_ticket_ctx { } esp_tls_server_session_ticket_ctx_t; #endif + +/** + * @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; + 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 +271,15 @@ typedef struct esp_tls_cfg_server { Call esp_tls_cfg_server_session_tickets_free 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 + } 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..d8aaa991a1 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 cert selection cb"); + 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,17 @@ 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, "No cert select cb is defined"); + } else { + /* 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 ESP_LOGE(TAG, "Missing server certificate and/or key"); +#endif return ESP_ERR_INVALID_STATE; } @@ -790,6 +809,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..071d5b5ff4 100644 --- a/components/esp_https_server/include/esp_https_server.h +++ b/components/esp_https_server/include/esp_https_server.h @@ -96,6 +96,9 @@ struct httpd_ssl_config { /** User callback for esp_https_server */ esp_https_server_user_cb *user_cb; + + 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; @@ -145,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 50f581cae1..6525643dd5 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -200,65 +200,100 @@ 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 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"); - free(ssl_ctx); - free(cfg); - return NULL; + goto exit; } } + 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 exit; } - 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 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 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!"); + } +#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 = 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 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 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 exit; +#endif } } - memcpy((char *)cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len); - cfg->serverkey_bytes = config->prvtkey_len; - return ssl_ctx; + +exit: + if (cfg) { + free((void *) cfg->servercert_buf); + free((void *) cfg->cacert_buf); + } + free(cfg); + free(ssl_ctx); + return NULL; } /** Start the server */ 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