https_server.c 7.6 KB

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