Merge branch 'feature/esp_https_server_callback' into 'master'

feature: Added user callback for esp_https_server

Closes IDFGH-5771

See merge request espressif/esp-idf!15405
This commit is contained in:
Mahavir Jain 2021-10-12 04:37:50 +00:00
commit b94bbdbd9c
8 changed files with 157 additions and 17 deletions

View File

@ -500,7 +500,7 @@ esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls)
return esp_ret;
}
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
}
if (cfg->servercert_buf != NULL && cfg->serverkey_buf != NULL) {

View File

@ -10,6 +10,7 @@
#include <stdbool.h>
#include "esp_err.h"
#include "esp_http_server.h"
#include "esp_tls.h"
#ifdef __cplusplus
extern "C" {
@ -20,6 +21,22 @@ typedef enum {
HTTPD_SSL_TRANSPORT_INSECURE // SSL disabled
} httpd_ssl_transport_mode_t;
/**
* @brief Callback data struct, contains the ESP-TLS connection handle
*/
typedef struct esp_https_server_user_cb_arg {
const esp_tls_t *tls;
} esp_https_server_user_cb_arg_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
*
@ -66,6 +83,9 @@ struct httpd_ssl_config {
/** Enable tls session tickets */
bool session_tickets;
/** User callback for esp_https_server */
esp_https_server_user_cb *user_cb;
};
typedef struct httpd_ssl_config httpd_ssl_config_t;
@ -113,6 +133,7 @@ typedef struct httpd_ssl_config httpd_ssl_config_t;
.port_secure = 443, \
.port_insecure = 80, \
.session_tickets = false, \
.user_cb = NULL, \
}
/**

View File

@ -15,6 +15,7 @@ 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;
/**
@ -119,6 +120,13 @@ static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd)
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.tls = tls;
(global_ctx->user_cb)((void *)&user_cb_data);
}
return ESP_OK;
fail:
esp_tls_server_session_delete(tls);
@ -172,6 +180,7 @@ static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *con
}
ssl_ctx->tls_cfg = cfg;
ssl_ctx->user_cb = config->user_cb;
/* cacert = CA which signs client cert, or client cert itself , which is mapped to client_verify_cert_pem */
if(config->client_verify_cert_pem != NULL) {
cfg->cacert_buf = (unsigned char *)malloc(config->client_verify_cert_len);

View File

@ -1,18 +1,7 @@
#!/usr/bin/env python
#
# Copyright 2021 Espressif Systems (Shanghai) CO LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import http.client
import os
@ -43,6 +32,61 @@ server_cert_pem = '-----BEGIN CERTIFICATE-----\n'\
'hb6pnMh3jRq4h0+5CZielA4/a+TdrNPv/qok67ot/XJdY3qHCCd8O2b14OVq9jo=\n'\
'-----END CERTIFICATE-----\n'
client_cert_pem = '-----BEGIN CERTIFICATE-----\n' \
'MIID7TCCAtWgAwIBAgIUBdm7RStsshnl3CCpknSJhXQK4GcwDQYJKoZIhvcNAQEL\n' \
'BQAwgYUxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdKaWFuZ3N1MQ8wDQYDVQQHDAZT\n' \
'dXpob3UxEjAQBgNVBAoMCUVzcHJlc3NpZjEMMAoGA1UECwwDY29tMRIwEAYDVQQD\n' \
'DAkxMjcuMC4wLjExHTAbBgkqhkiG9w0BCQEWDmVzcDMyeEBlc3AuY29tMB4XDTIx\n' \
'MTAwNTExMTMxMFoXDTMxMTAwMzExMTMxMFowgYUxCzAJBgNVBAYTAkNOMRAwDgYD\n' \
'VQQIDAdKaWFuZ3N1MQ8wDQYDVQQHDAZTdXpob3UxEjAQBgNVBAoMCUVzcHJlc3Np\n' \
'ZjEMMAoGA1UECwwDY29tMRIwEAYDVQQDDAkxMjcuMC4wLjExHTAbBgkqhkiG9w0B\n' \
'CQEWDmVzcDMyeEBlc3AuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n' \
'AQEAu2nP0HPtgKvRUwFuOs72caf4oyeK33OVfa6fGGttr/QYyw9PrwtdFDyEWEiI\n' \
'4P4hnxNC+bvNSYtJUzF9EmkqrUtKxhBsRVTKWOqumcgtiMWOxpdVKl0936ne2Pqh\n' \
'SweddrQwvPDFuB3hRikRX11+d5vkjFBV9FoZobKHWemDkXSc2R99xRie5PJoEfoz\n' \
'rmu5zjCaPHxzkyZsmH4MILfTuhUGc/Eye9Nl+lpY5KLjM14ZMQLK1CHRuI/oqCN6\n' \
'1WQrgUY5EyXGe0jXHTVhlL2RN8njxJ/4r3JnK/BQkcXTIMPOP8jIv9Sy1HhxfXKy\n' \
'HzLqOBn0Ft+mOADrpAWX8WnwUQIDAQABo1MwUTAdBgNVHQ4EFgQUpu4d8d+IywjB\n' \
'HMiKX84L+1ri8BIwHwYDVR0jBBgwFoAUpu4d8d+IywjBHMiKX84L+1ri8BIwDwYD\n' \
'VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAXm5Hn/aKKO3RnHqqfxok\n' \
'Hbw5yA2L2T6VPj2puI0Sh5GW62INjM0Kszy3L5mQqLUSsjcEcFAZmpeo14ytPRLG\n' \
'o6+WG/4er3hBA7D8oDni7hp8Qs+/EtNuEuoU+qQiKsT2DvA5rafT7laNfvjgqaoJ\n' \
'YMTCvzKLnMBaglB+qC9grgvJwMN0RTzHyY6UySdNZmcf5QXWLWjsX8E8/u4iSq8l\n' \
'eZlddTjh7HGGEOim7AkvKR9VYAvKGOV+FvUzCxPpoTr6kS2NGwnR7QnvKADECtLj\n' \
'gf+hW1FalMn0yTVspg4+BNbIThh0thbsvPDUTekMNfaRKKHZpJP2Ty3LkCbANLBR\n' \
'tQ==\n' \
'-----END CERTIFICATE-----\n'
client_key_pem = '-----BEGIN PRIVATE KEY-----\n' \
'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7ac/Qc+2Aq9FT\n' \
'AW46zvZxp/ijJ4rfc5V9rp8Ya22v9BjLD0+vC10UPIRYSIjg/iGfE0L5u81Ji0lT\n' \
'MX0SaSqtS0rGEGxFVMpY6q6ZyC2IxY7Gl1UqXT3fqd7Y+qFLB512tDC88MW4HeFG\n' \
'KRFfXX53m+SMUFX0WhmhsodZ6YORdJzZH33FGJ7k8mgR+jOua7nOMJo8fHOTJmyY\n' \
'fgwgt9O6FQZz8TJ702X6WljkouMzXhkxAsrUIdG4j+ioI3rVZCuBRjkTJcZ7SNcd\n' \
'NWGUvZE3yePEn/ivcmcr8FCRxdMgw84/yMi/1LLUeHF9crIfMuo4GfQW36Y4AOuk\n' \
'BZfxafBRAgMBAAECggEBAJuJZ1UCwRtGfUS8LTVVSiZtVuZhDNoB3REfeR4VGkUq\n' \
'+eCcZm9JqQgAaX2zRRYlEtYocC8+c1MT69jFe51p9mc302ipfJHVmtFMg3dRMKkP\n' \
'/DxIn/+2voD/Q9kjt/TC7yXyyXglApKZCbrmnmpc93ZgxL7GdW+Dzz3pIne2WuC9\n' \
'T6ie71R8X60sau6ApMgkUq6On0f21v/VLkNU67tQJGBF6Q1HE8PK7Ptun3WSBVNm\n' \
'FNNJKRBwiqfWXe9hPlqqCWayYBrojSqJJXn5Xd6n5XzLDPzAXuPlkPF3VwWeXGam\n' \
'3RBZA26gwv50E1PeiUQOipkR57J+O9j/oA07AnhsxPkCgYEA8RMvE3ImZTkPVqdX\n' \
'72E2A5ScJswVvZelnRS/mG8U+8UlvevAu5MYr717DHKHy3yOw/u7wbkqk6KEIcyz\n' \
'ctNPBPqTweaZ28eEY/+lXSdQaWLD2UgZC8JIcMOSeFugghEHeBaxLzUYBNDToE3q\n' \
'1El2HJ7W14QuTA+CEtCEb+tc7ssCgYEAxwQkBTT8A7mOEE0phfUACqaBuAXld+zu\n' \
'I3PNJDIhg1ZABEJ9vo9+3duFDoEHVsJOetijrBBxf/XAvi3bTJ+gAjcA54cGpkxz\n' \
'6ssbFWZeC9exyo0ILKn33o716GrCvQn1kmuF2gasmAcrOVsMygawR7P02oasDP/X\n' \
'UckbZdqofdMCgYEAom0GfteePv0e9Idzm/mnZuot+4Xt7/vIvflIze+p96hxMXEy\n' \
'Pi9xppbH3S8dh2C44Bsv+epEYYxR8mP1VBxDVVtvSmmQqJ/Y93c7d3QRna/JvQ/y\n' \
'sBWKsU9T1HwHvRq0KZlAcEoZkMUSkSNuYPHN/qKWpkaM2vpn7T1Ivg+aYdkCgYA/\n' \
'CGO0NnzfXSTOqvHM2LVDqksJkuyD2Enwdpvxq+MLawTplHmpIl/HOuDgoCNH6lDa\n' \
'/cSRGcApDBgY5ANCOIiASxWBPzXu8+X+5odUdtCwpYdNJPAC3W6BUfw2uaGmKAJc\n' \
'dqu1S0nc+OBK0Tiyv/2TKD8T+3WAxINZBv4je2bEOwKBgEavm5zTN9NILJsJCf9k\n' \
'te7+uDFuyoNWkL1vmMPuJYVC1QMVq1yr3DSaxA19BG9P4ZyOMOwVlPVWA+LofD4D\n' \
'S+w4Jjl2KDI4tSLUr6bsAJWdDfmrmGmRN3Kpds4RXaymV3rjj7qRk1J+ivtwo89s\n' \
'Vj+VslYzxw7FKKmnBgh/qGbJ\n' \
'-----END PRIVATE KEY-----\n'
success_response = '<h1>Hello Secure World!</h1>'
@ -74,10 +118,23 @@ def test_examples_protocol_https_server_simple(env, extra_data): # type: (tiny_
Utility.console_log('Performing GET request over an SSL connection with the server')
CLIENT_CERT_FILE = 'client_cert.pem'
CLIENT_KEY_FILE = 'client_key.pem'
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cadata=server_cert_pem)
with open(CLIENT_CERT_FILE, 'w') as cert, open(CLIENT_KEY_FILE, 'w') as key:
cert.write(client_cert_pem)
key.write(client_key_pem)
ssl_context.load_cert_chain(certfile=CLIENT_CERT_FILE, keyfile=CLIENT_KEY_FILE)
os.remove(CLIENT_CERT_FILE)
os.remove(CLIENT_KEY_FILE)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
Utility.console_log('Performing SSL handshake with the server')
conn.request('GET','/')
@ -89,6 +146,16 @@ def test_examples_protocol_https_server_simple(env, extra_data): # type: (tiny_
Utility.console_log('Response obtained does not match with correct response')
raise RuntimeError('Failed to test SSL connection')
Utility.console_log('Checking user callback: Obtaining client certificate...')
serial_number = dut1.expect(re.compile(r'serial number(.*)'), timeout=5)[0]
issuer_name = dut1.expect(re.compile(r'issuer name(.*)'), timeout=5)[0]
expiry = dut1.expect(re.compile(r'expires on(.*)'), timeout=5)[0]
Utility.console_log('Serial No.' + serial_number)
Utility.console_log('Issuer Name' + issuer_name)
Utility.console_log('Expires on' + expiry)
Utility.console_log('Correct response obtained')
Utility.console_log('SSL connection test successful\nClosing the connection')
conn.close()

View File

@ -0,0 +1,10 @@
menu "Example Configuration"
config EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
bool "Enable user callback with HTTPS Server"
default false
help
Enable user callback for esp_https_server which can be used to get SSL context (connection information)
E.g. Certificate of the connected client
endmenu

View File

@ -18,6 +18,7 @@
#include "protocol_examples_common.h"
#include <esp_https_server.h>
#include "esp_tls.h"
/* A simple example that demonstrates how to create GET and POST
* handlers and start an HTTPS server.
@ -25,7 +26,6 @@
static const char *TAG = "example";
/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
@ -35,13 +35,43 @@ static esp_err_t root_get_handler(httpd_req_t *req)
return ESP_OK;
}
#if CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
/**
* Example callback function to get the certificate of connected clients,
* whenever a new SSL connection is created
*
* Can also be used to other information like Socket FD, Connection state, etc.
*/
void https_server_user_callback(esp_https_server_user_cb_arg_t *user_cb)
{
ESP_LOGI(TAG, "Session Created!");
const mbedtls_x509_crt *cert;
const size_t buf_size = 1024;
char *buf = calloc(buf_size, sizeof(char));
if (buf == NULL) {
ESP_LOGE(TAG, "Out of memory - Callback execution failed!");
return;
}
cert = mbedtls_ssl_get_peer_cert(&user_cb->tls->ssl);
if (cert != NULL) {
mbedtls_x509_crt_info((char *) buf, buf_size - 1, " ", cert);
ESP_LOGI(TAG, "Peer certificate info:\n%s", buf);
} else {
ESP_LOGW(TAG, "Could not obtain the peer certificate!");
}
free(buf);
}
#endif
static const httpd_uri_t root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler
};
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
@ -61,6 +91,9 @@ static httpd_handle_t start_webserver(void)
conf.prvtkey_pem = prvtkey_pem_start;
conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;
#if CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
conf.user_cb = https_server_user_callback;
#endif
esp_err_t ret = httpd_ssl_start(&server, &conf);
if (ESP_OK != ret) {
ESP_LOGI(TAG, "Error starting server!");

View File

@ -1 +1,2 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK=y

View File

@ -3738,7 +3738,6 @@ examples/protocols/http_server/ws_echo_server/ws_server_example_test.py
examples/protocols/https_mbedtls/main/https_mbedtls_example_main.c
examples/protocols/https_request/example_test.py
examples/protocols/https_request/main/https_request_example_main.c
examples/protocols/https_server/simple/example_test.py
examples/protocols/https_server/simple/main/main.c
examples/protocols/https_server/wss_server/main/keep_alive.c
examples/protocols/https_server/wss_server/main/keep_alive.h