diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index fb6e10366a..f0764520ae 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -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) { diff --git a/components/esp_https_server/include/esp_https_server.h b/components/esp_https_server/include/esp_https_server.h index 00565a963a..efe726e9e7 100644 --- a/components/esp_https_server/include/esp_https_server.h +++ b/components/esp_https_server/include/esp_https_server.h @@ -10,6 +10,7 @@ #include #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, \ } /** diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c index 9864ead3b2..163ecb36a9 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -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); diff --git a/examples/protocols/https_server/simple/example_test.py b/examples/protocols/https_server/simple/example_test.py index bf9a348ece..b159042033 100644 --- a/examples/protocols/https_server/simple/example_test.py +++ b/examples/protocols/https_server/simple/example_test.py @@ -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 = '

Hello Secure World!

' @@ -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() diff --git a/examples/protocols/https_server/simple/main/Kconfig.projbuild b/examples/protocols/https_server/simple/main/Kconfig.projbuild new file mode 100644 index 0000000000..ee04640636 --- /dev/null +++ b/examples/protocols/https_server/simple/main/Kconfig.projbuild @@ -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 diff --git a/examples/protocols/https_server/simple/main/main.c b/examples/protocols/https_server/simple/main/main.c index 1a0f70ffef..658400e2f8 100644 --- a/examples/protocols/https_server/simple/main/main.c +++ b/examples/protocols/https_server/simple/main/main.c @@ -18,6 +18,7 @@ #include "protocol_examples_common.h" #include +#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!"); diff --git a/examples/protocols/https_server/simple/sdkconfig.ci b/examples/protocols/https_server/simple/sdkconfig.ci index a9595bf0c1..084692470b 100644 --- a/examples/protocols/https_server/simple/sdkconfig.ci +++ b/examples/protocols/https_server/simple/sdkconfig.ci @@ -1 +1,2 @@ CONFIG_ESP_HTTPS_SERVER_ENABLE=y +CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK=y diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 3d4252ed99..f163089f26 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -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