chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
set(src "src/https_server.c")
|
||||
set(inc "include")
|
||||
|
||||
idf_component_register(SRCS ${src}
|
||||
INCLUDE_DIRS ${inc}
|
||||
REQUIRES esp_http_server esp-tls esp_event
|
||||
PRIV_REQUIRES lwip)
|
||||
@@ -0,0 +1,35 @@
|
||||
menu "ESP HTTPS server"
|
||||
|
||||
config ESP_HTTPS_SERVER_ENABLE
|
||||
bool "Enable ESP_HTTPS_SERVER component"
|
||||
depends on (ESP_TLS_USING_MBEDTLS && MBEDTLS_TLS_SERVER)
|
||||
help
|
||||
Enable ESP HTTPS server component
|
||||
|
||||
config ESP_HTTPS_SERVER_EVENTS
|
||||
bool "Enable ESP_HTTPS_SERVER_EVENT event type"
|
||||
depends on ESP_HTTPS_SERVER_ENABLE
|
||||
default y
|
||||
help
|
||||
This enables the ESP_HTTPS_SERVER_EVENT event type. Generating these eventes adds some overhead.
|
||||
If you are not using this event type, you can disable it to save some memory.
|
||||
|
||||
|
||||
config ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT
|
||||
int "Time in millisecond to wait for posting event"
|
||||
depends on (ESP_HTTPS_SERVER_ENABLE && ESP_HTTPS_SERVER_EVENTS)
|
||||
default 2000
|
||||
help
|
||||
This config option helps in setting the time in millisecond to wait for event to be posted to the
|
||||
system default event loop. Set it to -1 if you need to set timeout to portMAX_DELAY.
|
||||
|
||||
config ESP_HTTPS_SERVER_CERT_SELECT_HOOK
|
||||
select ESP_TLS_SERVER_CERT_SELECT_HOOK
|
||||
bool "Enable certificate selection hook"
|
||||
default n
|
||||
help
|
||||
Enable certificate selection hook for ESP HTTPS Server. When enabled, this allows the server to
|
||||
dynamically select the appropriate certificate based on the client's Server Name Indication (SNI).
|
||||
This is useful for hosting multiple domains on a single server with different SSL certificates.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _ESP_HTTPS_SERVER_H_
|
||||
#define _ESP_HTTPS_SERVER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_tls.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP_HTTPS_SERVER_EVENTS || __DOXYGEN__
|
||||
ESP_EVENT_DECLARE_BASE(ESP_HTTPS_SERVER_EVENT);
|
||||
#endif // CONFIG_ESP_HTTPS_SERVER_EVENTS || __DOXYGEN__
|
||||
|
||||
typedef enum {
|
||||
HTTPS_SERVER_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */
|
||||
HTTPS_SERVER_EVENT_START, /*!< This event occurs when HTTPS Server is started */
|
||||
HTTPS_SERVER_EVENT_ON_CONNECTED, /*!< Once the HTTPS Server has been connected to the client */
|
||||
HTTPS_SERVER_EVENT_ON_DATA, /*!< Occurs when receiving data from the client */
|
||||
HTTPS_SERVER_EVENT_SENT_DATA, /*!< Occurs when an ESP HTTPS server sends data to the client */
|
||||
HTTPS_SERVER_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
|
||||
HTTPS_SERVER_EVENT_STOP, /*!< This event occurs when HTTPS Server is stopped */
|
||||
} esp_https_server_event_id_t;
|
||||
|
||||
typedef enum {
|
||||
HTTPD_SSL_TRANSPORT_SECURE, // SSL Enabled
|
||||
HTTPD_SSL_TRANSPORT_INSECURE // SSL disabled
|
||||
} httpd_ssl_transport_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Indicates the state at which the user callback is executed,
|
||||
* i.e at session creation, session close, or session error
|
||||
*/
|
||||
typedef enum {
|
||||
HTTPD_SSL_USER_CB_SESS_CREATE,
|
||||
HTTPD_SSL_USER_CB_SESS_CLOSE,
|
||||
HTTPD_SSL_USER_CB_SESS_ERROR
|
||||
} httpd_ssl_user_cb_state_t;
|
||||
|
||||
typedef esp_tls_handshake_callback esp_https_server_cert_select_cb;
|
||||
|
||||
/**
|
||||
* @brief Callback data struct, contains the ESP-TLS connection handle
|
||||
* and the connection state at which the callback is executed
|
||||
*/
|
||||
typedef struct esp_https_server_user_cb_arg {
|
||||
httpd_ssl_user_cb_state_t user_cb_state; /*!< State of user callback */
|
||||
esp_tls_t *tls; /*!< ESP-TLS connection handle */
|
||||
} esp_https_server_user_cb_arg_t;
|
||||
|
||||
typedef esp_tls_last_error_t esp_https_server_last_error_t;
|
||||
|
||||
/**
|
||||
* @brief Callback function prototype
|
||||
* Can be used to get connection or client information (SSL context)
|
||||
* E.g. Client certificate, Socket FD, Connection state, etc.
|
||||
*
|
||||
* @param user_cb Callback data struct
|
||||
*/
|
||||
typedef void esp_https_server_user_cb(esp_https_server_user_cb_arg_t *user_cb);
|
||||
|
||||
/**
|
||||
* HTTPS server config struct
|
||||
*
|
||||
* Please use HTTPD_SSL_CONFIG_DEFAULT() to initialize it.
|
||||
*/
|
||||
struct httpd_ssl_config {
|
||||
/**
|
||||
* Underlying HTTPD server config
|
||||
*
|
||||
* Parameters like task stack size and priority can be adjusted here.
|
||||
*/
|
||||
httpd_config_t httpd;
|
||||
|
||||
/** Server certificate */
|
||||
const uint8_t *servercert;
|
||||
|
||||
/** Server certificate byte length */
|
||||
size_t servercert_len;
|
||||
|
||||
/** CA certificate ((CA used to sign clients, or client cert itself) */
|
||||
const uint8_t *cacert_pem;
|
||||
|
||||
/** CA certificate byte length */
|
||||
size_t cacert_len;
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
|
||||
/** Client certificate authentication mode */
|
||||
bool client_cert_authmode_optional;
|
||||
#endif // CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
|
||||
|
||||
/** Private key */
|
||||
const uint8_t *prvtkey_pem;
|
||||
|
||||
/** Private key byte length */
|
||||
size_t prvtkey_len;
|
||||
|
||||
/** Unified key config. Takes precedence over prvtkey_pem when set */
|
||||
const esp_key_config_t *server_key;
|
||||
|
||||
/** Use ECDSA peripheral to use private key */
|
||||
bool use_ecdsa_peripheral;
|
||||
|
||||
/** The efuse block where ECDSA key is stored. For SECP384R1 curve, if two blocks are used, set this to the low block and use ecdsa_key_efuse_blk_high for the high block. */
|
||||
uint8_t ecdsa_key_efuse_blk;
|
||||
|
||||
/** The high efuse block for ECDSA key (used only for SECP384R1 curve). If not set (0), only ecdsa_key_efuse_blk is used. */
|
||||
uint8_t ecdsa_key_efuse_blk_high;
|
||||
|
||||
/** ECDSA curve to use (SECP256R1 or SECP384R1) */
|
||||
esp_tls_ecdsa_curve_t ecdsa_curve;
|
||||
|
||||
/** Transport Mode (default secure) */
|
||||
httpd_ssl_transport_mode_t transport_mode;
|
||||
|
||||
/** Port used when transport mode is secure (default 443) */
|
||||
uint16_t port_secure;
|
||||
|
||||
/** Port used when transport mode is insecure (default 80) */
|
||||
uint16_t port_insecure;
|
||||
|
||||
/** Enable tls session tickets */
|
||||
bool session_tickets;
|
||||
|
||||
/** User callback for esp_https_server */
|
||||
esp_https_server_user_cb *user_cb;
|
||||
|
||||
/** User data to add to the ssl context */
|
||||
void *ssl_userdata;
|
||||
|
||||
/** Certificate selection callback to use.
|
||||
* The callback is only applicable when CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK is enabled in menuconfig */
|
||||
esp_https_server_cert_select_cb cert_select_cb;
|
||||
|
||||
/** Application protocols the server supports in order of preference.
|
||||
* Used for negotiating during the TLS handshake, first one the client supports is selected.
|
||||
* The data structure must live as long as the https server itself */
|
||||
const char** alpn_protos;
|
||||
|
||||
/** TLS handshake timeout in milliseconds, default timeout is 10 seconds if not set */
|
||||
uint32_t tls_handshake_timeout_ms;
|
||||
|
||||
/** TLS protocol version for this server, e.g., TLS 1.2, TLS 1.3
|
||||
* (default - no preference). Enables per-server TLS version control. */
|
||||
esp_tls_proto_ver_t tls_version;
|
||||
|
||||
/** Pointer to a zero-terminated array of IANA identifiers of TLS ciphersuites.
|
||||
* Please check the list validity by esp_tls_get_ciphersuites_list() API.
|
||||
* This allows per-server cipher suite configuration. */
|
||||
const int *ciphersuites_list;
|
||||
};
|
||||
|
||||
typedef struct httpd_ssl_config httpd_ssl_config_t;
|
||||
|
||||
/**
|
||||
* Helper macro for optional client certificate authentication field
|
||||
*/
|
||||
#ifdef CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
|
||||
#define HTTPD_SSL_CONFIG_CLIENT_AUTH_OPTIONAL_INIT \
|
||||
.client_cert_authmode_optional = false,
|
||||
#else
|
||||
#define HTTPD_SSL_CONFIG_CLIENT_AUTH_OPTIONAL_INIT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default config struct init
|
||||
* Notes:
|
||||
* - port is set when starting the server, according to 'transport_mode'
|
||||
* - one socket uses ~ 40kB RAM with SSL, we reduce the default socket count to 4
|
||||
* - SSL sockets are usually long-lived, closing LRU prevents pool exhaustion DOS
|
||||
* - Stack size may need adjustments depending on the user application
|
||||
*/
|
||||
#define HTTPD_SSL_CONFIG_DEFAULT() { \
|
||||
.httpd = { \
|
||||
.task_priority = tskIDLE_PRIORITY+5, \
|
||||
.stack_size = 10240, \
|
||||
.core_id = tskNO_AFFINITY, \
|
||||
.task_caps = (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), \
|
||||
.max_req_hdr_len = CONFIG_HTTPD_MAX_REQ_HDR_LEN, \
|
||||
.max_uri_len = CONFIG_HTTPD_MAX_URI_LEN, \
|
||||
.server_port = 0, \
|
||||
.ctrl_port = ESP_HTTPD_DEF_CTRL_PORT+1, \
|
||||
.max_open_sockets = 4, \
|
||||
.max_uri_handlers = 8, \
|
||||
.max_resp_headers = 8, \
|
||||
.backlog_conn = 5, \
|
||||
.lru_purge_enable = true, \
|
||||
.recv_wait_timeout = 5, \
|
||||
.send_wait_timeout = 5, \
|
||||
.global_user_ctx = NULL, \
|
||||
.global_user_ctx_free_fn = NULL, \
|
||||
.global_transport_ctx = NULL, \
|
||||
.global_transport_ctx_free_fn = NULL, \
|
||||
.enable_so_linger = false, \
|
||||
.linger_timeout = 0, \
|
||||
.keep_alive_enable = false, \
|
||||
.keep_alive_idle = 0, \
|
||||
.keep_alive_interval = 0, \
|
||||
.keep_alive_count = 0, \
|
||||
.if_name = NULL, \
|
||||
.open_fn = NULL, \
|
||||
.close_fn = NULL, \
|
||||
.uri_match_fn = NULL \
|
||||
}, \
|
||||
.servercert = NULL, \
|
||||
.servercert_len = 0, \
|
||||
.cacert_pem = NULL, \
|
||||
.cacert_len = 0, \
|
||||
HTTPD_SSL_CONFIG_CLIENT_AUTH_OPTIONAL_INIT \
|
||||
.prvtkey_pem = NULL, \
|
||||
.prvtkey_len = 0, \
|
||||
.server_key = NULL, \
|
||||
.use_ecdsa_peripheral = false, \
|
||||
.ecdsa_key_efuse_blk = 0, \
|
||||
.ecdsa_key_efuse_blk_high = 0, \
|
||||
.ecdsa_curve = ESP_TLS_ECDSA_CURVE_SECP256R1, \
|
||||
.transport_mode = HTTPD_SSL_TRANSPORT_SECURE, \
|
||||
.port_secure = 443, \
|
||||
.port_insecure = 80, \
|
||||
.session_tickets = false, \
|
||||
.user_cb = NULL, \
|
||||
.ssl_userdata = NULL, \
|
||||
.cert_select_cb = NULL, \
|
||||
.alpn_protos = NULL, \
|
||||
.tls_handshake_timeout_ms = 0, \
|
||||
.tls_version = ESP_TLS_VER_ANY, \
|
||||
.ciphersuites_list = NULL, \
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SSL capable HTTP server (secure mode may be disabled in config)
|
||||
*
|
||||
* @param[in,out] config - server config, must not be const. Does not have to stay valid after
|
||||
* calling this function.
|
||||
* @param[out] handle - storage for the server handle, must be a valid pointer
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t httpd_ssl_start(httpd_handle_t *handle, httpd_ssl_config_t *config);
|
||||
|
||||
/**
|
||||
* Stop the server. Blocks until the server is shut down.
|
||||
*
|
||||
* @param[in] handle
|
||||
* @return
|
||||
* - ESP_OK: Server stopped successfully
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_FAIL: Failure to shut down server
|
||||
*/
|
||||
esp_err_t httpd_ssl_stop(httpd_handle_t handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ESP_HTTPS_SERVER_H_
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_https_server.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_tls.h"
|
||||
|
||||
const static char *TAG = "esp_https_server";
|
||||
|
||||
typedef struct httpd_ssl_ctx {
|
||||
esp_tls_cfg_server_t *tls_cfg;
|
||||
httpd_open_func_t open_fn;
|
||||
esp_https_server_user_cb *user_cb;
|
||||
} httpd_ssl_ctx_t;
|
||||
|
||||
typedef struct httpd_ssl_transport_ctx {
|
||||
esp_tls_t *tls;
|
||||
httpd_ssl_ctx_t *global_ctx;
|
||||
} httpd_ssl_transport_ctx_t;
|
||||
|
||||
#ifdef CONFIG_ESP_HTTPS_SERVER_EVENTS
|
||||
ESP_EVENT_DEFINE_BASE(ESP_HTTPS_SERVER_EVENT);
|
||||
|
||||
#if CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT == -1
|
||||
#define ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT portMAX_DELAY
|
||||
#else
|
||||
#define ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT pdMS_TO_TICKS(CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT)
|
||||
#endif
|
||||
|
||||
static void http_dispatch_event_to_event_loop(int32_t event_id, const void* event_data, size_t event_data_size)
|
||||
{
|
||||
esp_err_t err = esp_event_post(ESP_HTTPS_SERVER_EVENT, event_id, event_data, event_data_size, ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to post http_client event: %"PRId32", error: %s", event_id, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
#else // CONFIG_ESP_HTTPS_SERVER_EVENTS
|
||||
static void http_dispatch_event_to_event_loop(int32_t event_id, const void* event_data, size_t event_data_size)
|
||||
{
|
||||
// Events disabled, do nothing
|
||||
(void) event_id;
|
||||
(void) event_data;
|
||||
(void) event_data_size;
|
||||
}
|
||||
#endif // CONFIG_ESP_HTTPS_SERVER_EVENTS
|
||||
|
||||
/**
|
||||
* SSL socket close handler
|
||||
*
|
||||
* @param[in] ctx - session transport context (SSL context we stored there)
|
||||
*/
|
||||
static void httpd_ssl_close(void *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
httpd_ssl_transport_ctx_t *transport_ctx = (httpd_ssl_transport_ctx_t *)ctx;
|
||||
httpd_ssl_ctx_t *global_ctx = transport_ctx->global_ctx;
|
||||
esp_tls_t *tls = transport_ctx->tls;
|
||||
|
||||
if (global_ctx->user_cb) {
|
||||
esp_https_server_user_cb_arg_t user_cb_data = {0};
|
||||
user_cb_data.user_cb_state = HTTPD_SSL_USER_CB_SESS_CLOSE;
|
||||
user_cb_data.tls = tls;
|
||||
(global_ctx->user_cb)((void *)&user_cb_data);
|
||||
}
|
||||
|
||||
esp_tls_server_session_delete(tls);
|
||||
free(ctx);
|
||||
ESP_LOGD(TAG, "Secure socket closed");
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_DISCONNECTED, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL socket pending-check function
|
||||
*
|
||||
* @param server
|
||||
* @param sockfd
|
||||
* @return number of pending bytes, negative on error
|
||||
*/
|
||||
static int httpd_ssl_pending(httpd_handle_t server, int sockfd)
|
||||
{
|
||||
httpd_ssl_transport_ctx_t *transport_ctx = httpd_sess_get_transport_ctx(server, sockfd);
|
||||
assert(transport_ctx != NULL);
|
||||
esp_tls_t *tls = transport_ctx->tls;
|
||||
assert(tls != NULL);
|
||||
int ret = esp_tls_get_bytes_avail(tls);
|
||||
if (ret < 0) {
|
||||
esp_tls_error_handle_t error_handle;
|
||||
if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags);
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive from a SSL socket
|
||||
*
|
||||
* @param server
|
||||
* @param sockfd
|
||||
* @param buf
|
||||
* @param buf_len
|
||||
* @param flags
|
||||
* @return bytes read, negative on error
|
||||
*/
|
||||
static int httpd_ssl_recv(httpd_handle_t server, int sockfd, char *buf, size_t buf_len, int flags)
|
||||
{
|
||||
httpd_ssl_transport_ctx_t *transport_ctx = httpd_sess_get_transport_ctx(server, sockfd);
|
||||
assert(transport_ctx != NULL);
|
||||
esp_tls_t *tls = transport_ctx->tls;
|
||||
assert(tls != NULL);
|
||||
int ret = esp_tls_conn_read(tls, buf, buf_len);
|
||||
if (ret < 0) {
|
||||
esp_tls_error_handle_t error_handle;
|
||||
if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags);
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
}
|
||||
} else {
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ON_DATA, &ret, sizeof(int));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send to a SSL socket
|
||||
*
|
||||
* @param server
|
||||
* @param sockfd
|
||||
* @param buf
|
||||
* @param buf_len
|
||||
* @param flags
|
||||
* @return bytes sent, negative on error
|
||||
*/
|
||||
static int httpd_ssl_send(httpd_handle_t server, int sockfd, const char *buf, size_t buf_len, int flags)
|
||||
{
|
||||
httpd_ssl_transport_ctx_t *transport_ctx = httpd_sess_get_transport_ctx(server, sockfd);
|
||||
assert(transport_ctx != NULL);
|
||||
esp_tls_t *tls = transport_ctx->tls;
|
||||
assert(tls != NULL);
|
||||
int ret = esp_tls_conn_write(tls, buf, buf_len);
|
||||
if (ret < 0) {
|
||||
esp_tls_error_handle_t error_handle;
|
||||
if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags);
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
}
|
||||
} else {
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_SENT_DATA, NULL, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a SSL socket for the server.
|
||||
* The fd is already open and ready to read / write raw data.
|
||||
*
|
||||
* @param server
|
||||
* @param sockfd - raw socket fd
|
||||
* @return success
|
||||
*/
|
||||
static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd)
|
||||
{
|
||||
assert(server != NULL);
|
||||
|
||||
// Retrieve the SSL context from the global context field (set in config)
|
||||
httpd_ssl_ctx_t *global_ctx = httpd_get_global_transport_ctx(server);
|
||||
assert(global_ctx != NULL);
|
||||
|
||||
esp_tls_t *tls = esp_tls_init();
|
||||
if (!tls) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = ESP_ERR_NO_MEM;
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGI(TAG, "performing session handshake");
|
||||
int ret = esp_tls_server_session_create(global_ctx->tls_cfg, sockfd, tls);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "esp_tls_create_server_session failed, 0x%04x", -ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Pass a new structure containing the global context and the tls pointer to httpd_ssl_close
|
||||
// Store it in the context field of the HTTPD session object
|
||||
// NOTE: allocated memory will be freed by httpd_ssl_close
|
||||
httpd_ssl_transport_ctx_t *transport_ctx = (httpd_ssl_transport_ctx_t *)calloc(1, sizeof(httpd_ssl_transport_ctx_t));
|
||||
if (!transport_ctx) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = ESP_ERR_NO_MEM;
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
/* The TLS session (and its underlying socket fd) is already established; free it
|
||||
* before returning so a failed connection under memory pressure does not leak the
|
||||
* SSL context and the socket (CWE-401 / CWE-772). */
|
||||
esp_tls_server_session_delete(tls);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
transport_ctx->tls = tls;
|
||||
transport_ctx->global_ctx = global_ctx;
|
||||
|
||||
// Store the SSL session into the context field of the HTTPD session object
|
||||
httpd_sess_set_transport_ctx(server, sockfd, transport_ctx, httpd_ssl_close);
|
||||
|
||||
// Set rx/tx/pending override functions
|
||||
httpd_sess_set_send_override(server, sockfd, httpd_ssl_send);
|
||||
httpd_sess_set_recv_override(server, sockfd, httpd_ssl_recv);
|
||||
httpd_sess_set_pending_override(server, sockfd, httpd_ssl_pending);
|
||||
|
||||
// all access should now go through SSL
|
||||
ESP_LOGD(TAG, "Secure socket open");
|
||||
|
||||
if (global_ctx->open_fn) {
|
||||
(global_ctx->open_fn)(server, sockfd);
|
||||
}
|
||||
|
||||
if (global_ctx->user_cb) {
|
||||
esp_https_server_user_cb_arg_t user_cb_data = {0};
|
||||
user_cb_data.user_cb_state = HTTPD_SSL_USER_CB_SESS_CREATE;
|
||||
user_cb_data.tls = tls;
|
||||
(global_ctx->user_cb)((void *)&user_cb_data);
|
||||
}
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ON_CONNECTED, NULL, 0);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
{
|
||||
esp_tls_error_handle_t error_handle;
|
||||
if (esp_tls_get_error_handle(tls, &error_handle) == ESP_OK) {
|
||||
esp_https_server_last_error_t last_error = {0};
|
||||
last_error.last_error = esp_tls_get_and_clear_last_error(error_handle, &last_error.esp_tls_error_code, &last_error.esp_tls_flags);
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error));
|
||||
}
|
||||
// Call user callback if configured, allowing user to log failures
|
||||
if (global_ctx->user_cb) {
|
||||
esp_https_server_user_cb_arg_t user_cb_data = {0};
|
||||
user_cb_data.user_cb_state = HTTPD_SSL_USER_CB_SESS_ERROR;
|
||||
user_cb_data.tls = tls;
|
||||
(global_ctx->user_cb)((void *)&user_cb_data);
|
||||
}
|
||||
esp_tls_server_session_delete(tls);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the HTTPD global transport context
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
static void free_secure_context(void *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
httpd_ssl_ctx_t *ssl_ctx = ctx;
|
||||
esp_tls_cfg_server_t *cfg = ssl_ctx->tls_cfg;
|
||||
ESP_LOGI(TAG, "Server shuts down, releasing SSL context");
|
||||
if (cfg->cacert_buf) {
|
||||
free((void *)cfg->cacert_buf);
|
||||
}
|
||||
if (cfg->servercert_buf) {
|
||||
free((void *)cfg->servercert_buf);
|
||||
}
|
||||
if (cfg->serverkey_buf) {
|
||||
free((void *)cfg->serverkey_buf);
|
||||
}
|
||||
esp_tls_cfg_server_session_tickets_free(cfg);
|
||||
free(cfg);
|
||||
free(ssl_ctx);
|
||||
}
|
||||
|
||||
static esp_err_t create_secure_context(const struct httpd_ssl_config *config, httpd_ssl_ctx_t **ssl_ctx)
|
||||
{
|
||||
if (!ssl_ctx || !*ssl_ctx) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_tls_cfg_server_t *cfg = (esp_tls_cfg_server_t *)calloc(1, sizeof(esp_tls_cfg_server_t));
|
||||
if (!cfg) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (config->session_tickets) {
|
||||
ret = esp_tls_cfg_server_session_tickets_init(cfg);
|
||||
if ( ret != ESP_OK ) {
|
||||
ESP_LOGE(TAG, "Failed to init session ticket support. error: %s", esp_err_to_name(ret));
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
cfg->userdata = config->ssl_userdata;
|
||||
cfg->alpn_protos = config->alpn_protos;
|
||||
cfg->tls_handshake_timeout_ms = config->tls_handshake_timeout_ms;
|
||||
#ifdef CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
|
||||
cfg->client_cert_authmode_optional = config->client_cert_authmode_optional;
|
||||
#endif // CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
|
||||
|
||||
cfg->tls_version = config->tls_version;
|
||||
cfg->ciphersuites_list = config->ciphersuites_list;
|
||||
|
||||
#if defined(CONFIG_ESP_HTTPS_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 && config->cacert_len > 0) {
|
||||
cfg->cacert_buf = (unsigned char *)malloc(config->cacert_len);
|
||||
|
||||
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");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* servercert = cert of server itself */
|
||||
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");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
#if defined(CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK)
|
||||
if (config->cert_select_cb == NULL) {
|
||||
#endif
|
||||
ESP_LOGE(TAG, "No Server certificate supplied");
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto exit;
|
||||
#if defined(CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK)
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Server certificate not supplied, make sure to supply it in the certificate selection hook!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (config->use_ecdsa_peripheral) {
|
||||
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
|
||||
(*ssl_ctx)->tls_cfg->use_ecdsa_peripheral = config->use_ecdsa_peripheral;
|
||||
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk = config->ecdsa_key_efuse_blk;
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk_high = config->ecdsa_key_efuse_blk_high;
|
||||
#endif
|
||||
(*ssl_ctx)->tls_cfg->ecdsa_curve = config->ecdsa_curve;
|
||||
#else
|
||||
ESP_LOGE(TAG, "Please enable the support for signing using ECDSA peripheral in menuconfig.");
|
||||
ret = ESP_ERR_NOT_SUPPORTED;
|
||||
goto exit;
|
||||
#endif
|
||||
} else if (config->server_key != NULL) {
|
||||
/* Unified key config - pass directly to esp_tls */
|
||||
cfg->server_key = config->server_key;
|
||||
} else 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");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
#if defined(CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK)
|
||||
if (config->cert_select_cb == NULL) {
|
||||
ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present");
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
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");
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto exit;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
if (cfg) {
|
||||
free((void *) cfg->servercert_buf);
|
||||
free((void *) cfg->cacert_buf);
|
||||
}
|
||||
free(cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Start the server */
|
||||
esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(pHandle != NULL);
|
||||
|
||||
ESP_LOGI(TAG, "Starting server");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
httpd_ssl_ctx_t *ssl_ctx = NULL;
|
||||
|
||||
if (HTTPD_SSL_TRANSPORT_SECURE == config->transport_mode) {
|
||||
ssl_ctx = calloc(1, sizeof(httpd_ssl_ctx_t));
|
||||
if (!ssl_ctx) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
ret = create_secure_context(config, &ssl_ctx);
|
||||
if (ret != ESP_OK) {
|
||||
free(ssl_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "SSL context ready");
|
||||
|
||||
// set SSL specific config
|
||||
config->httpd.global_transport_ctx = ssl_ctx;
|
||||
config->httpd.global_transport_ctx_free_fn = free_secure_context;
|
||||
if (config->httpd.open_fn) {
|
||||
// since the httpd's open_fn is used for opening the SSL session, we save the configured
|
||||
// user pointer and call it upon opening the ssl socket
|
||||
ssl_ctx->open_fn = config->httpd.open_fn;
|
||||
}
|
||||
config->httpd.open_fn = httpd_ssl_open; // the open function configures the created SSL sessions
|
||||
|
||||
config->httpd.server_port = config->port_secure;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "SSL disabled, using plain HTTP");
|
||||
config->httpd.server_port = config->port_insecure;
|
||||
}
|
||||
|
||||
httpd_handle_t handle = NULL;
|
||||
|
||||
ret = httpd_start(&handle, &config->httpd);
|
||||
if (ret != ESP_OK) {
|
||||
if (ssl_ctx) {
|
||||
free_secure_context(ssl_ctx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
*pHandle = handle;
|
||||
|
||||
ESP_LOGI(TAG, "Server listening on port %d", config->httpd.server_port);
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_START, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/** Stop the server */
|
||||
esp_err_t httpd_ssl_stop(httpd_handle_t handle)
|
||||
{
|
||||
esp_err_t ret = httpd_stop(handle);
|
||||
if (ret == ESP_OK) {
|
||||
http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_STOP, NULL, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user