1 /*
2  * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include "esp_https_server.h"
9 #include "esp_log.h"
10 #include "sdkconfig.h"
11 #include "esp_tls.h"
12 
13 const static char *TAG = "esp_https_server";
14 
15 typedef struct httpd_ssl_ctx {
16     esp_tls_cfg_server_t *tls_cfg;
17     httpd_open_func_t open_fn;
18     esp_https_server_user_cb *user_cb;
19 } httpd_ssl_ctx_t;
20 
21 /**
22  * SSL socket close handler
23  *
24  * @param[in] ctx - session transport context (SSL context we stored there)
25  */
httpd_ssl_close(void * ctx)26 static void httpd_ssl_close(void *ctx)
27 {
28     assert(ctx != NULL);
29     esp_tls_server_session_delete(ctx);
30     ESP_LOGD(TAG, "Secure socket closed");
31 }
32 
33 /**
34  * SSL socket pending-check function
35  *
36  * @param server
37  * @param sockfd
38  * @return number of pending bytes, negative on error
39  */
httpd_ssl_pending(httpd_handle_t server,int sockfd)40 static int httpd_ssl_pending(httpd_handle_t server, int sockfd)
41 {
42     esp_tls_t *tls = httpd_sess_get_transport_ctx(server, sockfd);
43     assert(tls != NULL);
44     return esp_tls_get_bytes_avail(tls);
45 }
46 
47 /**
48  * Receive from a SSL socket
49  *
50  * @param server
51  * @param sockfd
52  * @param buf
53  * @param buf_len
54  * @param flags
55  * @return bytes read, negative on error
56  */
httpd_ssl_recv(httpd_handle_t server,int sockfd,char * buf,size_t buf_len,int flags)57 static int httpd_ssl_recv(httpd_handle_t server, int sockfd, char *buf, size_t buf_len, int flags)
58 {
59     esp_tls_t *tls = httpd_sess_get_transport_ctx(server, sockfd);
60     assert(tls != NULL);
61     return esp_tls_conn_read(tls, buf, buf_len);
62 }
63 
64 /**
65  * Send to a SSL socket
66  *
67  * @param server
68  * @param sockfd
69  * @param buf
70  * @param buf_len
71  * @param flags
72  * @return bytes sent, negative on error
73  */
httpd_ssl_send(httpd_handle_t server,int sockfd,const char * buf,size_t buf_len,int flags)74 static int httpd_ssl_send(httpd_handle_t server, int sockfd, const char *buf, size_t buf_len, int flags)
75 {
76     esp_tls_t *tls = httpd_sess_get_transport_ctx(server, sockfd);
77     assert(tls != NULL);
78     return esp_tls_conn_write(tls, buf, buf_len);
79 }
80 
81 /**
82  * Open a SSL socket for the server.
83  * The fd is already open and ready to read / write raw data.
84  *
85  * @param server
86  * @param sockfd - raw socket fd
87  * @return success
88  */
httpd_ssl_open(httpd_handle_t server,int sockfd)89 static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd)
90 {
91     assert(server != NULL);
92 
93     // Retrieve the SSL context from the global context field (set in config)
94     httpd_ssl_ctx_t *global_ctx = httpd_get_global_transport_ctx(server);
95     assert(global_ctx != NULL);
96 
97     esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
98     if (!tls) {
99         return ESP_ERR_NO_MEM;
100     }
101     ESP_LOGI(TAG, "performing session handshake");
102     int ret = esp_tls_server_session_create(global_ctx->tls_cfg, sockfd, tls);
103     if (ret != 0) {
104         ESP_LOGE(TAG, "esp_tls_create_server_session failed");
105         goto fail;
106     }
107 
108     // Store the SSL session into the context field of the HTTPD session object
109     httpd_sess_set_transport_ctx(server, sockfd, tls, httpd_ssl_close);
110 
111     // Set rx/tx/pending override functions
112     httpd_sess_set_send_override(server, sockfd, httpd_ssl_send);
113     httpd_sess_set_recv_override(server, sockfd, httpd_ssl_recv);
114     httpd_sess_set_pending_override(server, sockfd, httpd_ssl_pending);
115 
116     // all access should now go through SSL
117 
118     ESP_LOGD(TAG, "Secure socket open");
119 
120     if (global_ctx->open_fn) {
121         (global_ctx->open_fn)(server, sockfd);
122     }
123 
124     if (global_ctx->user_cb) {
125         esp_https_server_user_cb_arg_t user_cb_data = {0};
126         user_cb_data.tls = tls;
127         (global_ctx->user_cb)((void *)&user_cb_data);
128     }
129 
130     return ESP_OK;
131 fail:
132     esp_tls_server_session_delete(tls);
133     return ESP_FAIL;
134 }
135 
136 /**
137  * Tear down the HTTPD global transport context
138  *
139  * @param ctx
140  */
free_secure_context(void * ctx)141 static void free_secure_context(void *ctx)
142 {
143     assert(ctx != NULL);
144     httpd_ssl_ctx_t *ssl_ctx = ctx;
145     esp_tls_cfg_server_t *cfg = ssl_ctx->tls_cfg;
146     ESP_LOGI(TAG, "Server shuts down, releasing SSL context");
147     if (cfg->cacert_buf) {
148         free((void *)cfg->cacert_buf);
149     }
150     if (cfg->servercert_buf) {
151         free((void *)cfg->servercert_buf);
152     }
153     if (cfg->serverkey_buf) {
154         free((void *)cfg->serverkey_buf);
155     }
156     esp_tls_cfg_server_session_tickets_free(cfg);
157     free(cfg);
158     free(ssl_ctx);
159 }
160 
create_secure_context(const struct httpd_ssl_config * config)161 static httpd_ssl_ctx_t *create_secure_context(const struct httpd_ssl_config *config)
162 {
163     httpd_ssl_ctx_t *ssl_ctx = calloc(1, sizeof(httpd_ssl_ctx_t));
164     if (!ssl_ctx) {
165         return NULL;
166     }
167     esp_tls_cfg_server_t *cfg = (esp_tls_cfg_server_t *)calloc(1, sizeof(esp_tls_cfg_server_t));
168     if (!cfg) {
169         free(ssl_ctx);
170         return NULL;
171     }
172 
173     if (config->session_tickets) {
174         if ( esp_tls_cfg_server_session_tickets_init(cfg) != ESP_OK ) {
175             ESP_LOGE(TAG, "Failed to init session ticket support");
176             free(ssl_ctx);
177             free(cfg);
178             return NULL;
179         }
180     }
181 
182     ssl_ctx->tls_cfg = cfg;
183     ssl_ctx->user_cb = config->user_cb;
184 /* cacert = CA which signs client cert, or client cert itself , which is mapped to client_verify_cert_pem */
185     if(config->client_verify_cert_pem != NULL) {
186         cfg->cacert_buf = (unsigned char *)malloc(config->client_verify_cert_len);
187         if (!cfg->cacert_buf) {
188             ESP_LOGE(TAG, "Could not allocate memory");
189             free(cfg);
190             free(ssl_ctx);
191             return NULL;
192         }
193         memcpy((char *)cfg->cacert_buf, config->client_verify_cert_pem, config->client_verify_cert_len);
194         cfg->cacert_bytes = config->client_verify_cert_len;
195     }
196 /* servercert = cert of server itself ( in our case it is mapped to cacert in https_server example) */
197     cfg->servercert_buf = (unsigned char *)malloc(config->cacert_len);
198     if (!cfg->servercert_buf) {
199         ESP_LOGE(TAG, "Could not allocate memory");
200         free((void *)cfg->cacert_buf);
201         free(cfg);
202         free(ssl_ctx);
203         return NULL;
204     }
205     memcpy((char *)cfg->servercert_buf, config->cacert_pem, config->cacert_len);
206     cfg->servercert_bytes = config->cacert_len;
207 
208     cfg->serverkey_buf = (unsigned char *)malloc(config->prvtkey_len);
209     if (!cfg->serverkey_buf) {
210         ESP_LOGE(TAG, "Could not allocate memory");
211         free((void *)cfg->servercert_buf);
212         free((void *)cfg->cacert_buf);
213         free(cfg);
214         free(ssl_ctx);
215         return NULL;
216     }
217     memcpy((char *)cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len);
218     cfg->serverkey_bytes = config->prvtkey_len;
219 
220     return ssl_ctx;
221 }
222 
223 /** Start the server */
httpd_ssl_start(httpd_handle_t * pHandle,struct httpd_ssl_config * config)224 esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *config)
225 {
226     assert(config != NULL);
227     assert(pHandle != NULL);
228 
229     ESP_LOGI(TAG, "Starting server");
230 
231     if (HTTPD_SSL_TRANSPORT_SECURE == config->transport_mode) {
232 
233         httpd_ssl_ctx_t *ssl_ctx = create_secure_context(config);
234         if (!ssl_ctx) {
235             return -1;
236         }
237 
238         ESP_LOGD(TAG, "SSL context ready");
239 
240         // set SSL specific config
241         config->httpd.global_transport_ctx = ssl_ctx;
242         config->httpd.global_transport_ctx_free_fn = free_secure_context;
243         if (config->httpd.open_fn) {
244             // since the httpd's open_fn is used for opening the SSL session, we save the configured
245             // user pointer and call it upon opening the ssl socket
246             ssl_ctx->open_fn = config->httpd.open_fn;
247         }
248         config->httpd.open_fn = httpd_ssl_open; // the open function configures the created SSL sessions
249 
250         config->httpd.server_port = config->port_secure;
251     } else {
252         ESP_LOGD(TAG, "SSL disabled, using plain HTTP");
253         config->httpd.server_port = config->port_insecure;
254     }
255 
256     httpd_handle_t handle = NULL;
257 
258     esp_err_t ret = httpd_start(&handle, &config->httpd);
259     if (ret != ESP_OK) return ret;
260 
261     *pHandle = handle;
262 
263     ESP_LOGI(TAG, "Server listening on port %d", config->httpd.server_port);
264     return ESP_OK;
265 }
266 
267 /** Stop the server */
httpd_ssl_stop(httpd_handle_t handle)268 void httpd_ssl_stop(httpd_handle_t handle)
269 {
270     httpd_stop(handle);
271 }
272