Просмотр исходного кода

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
Mahavir Jain 4 лет назад
Родитель
Сommit
b94bbdbd9c

+ 1 - 1
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) {

+ 21 - 0
components/esp_https_server/include/esp_https_server.h

@@ -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,                              \
 }
 
 /**

+ 9 - 0
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);

+ 80 - 13
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 = '<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()

+ 10 - 0
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

+ 35 - 2
examples/protocols/https_server/simple/main/main.c

@@ -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!");

+ 1 - 0
examples/protocols/https_server/simple/sdkconfig.ci

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

+ 0 - 1
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