/ components / esp-tls / esp_tls.c
esp_tls.c
  1  // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  #include <stdio.h>
 15  #include <string.h>
 16  #include <stdlib.h>
 17  #include <unistd.h>
 18  
 19  #include <sys/types.h>
 20  #include <sys/socket.h>
 21  #include <netdb.h>
 22  
 23  #include <http_parser.h>
 24  #include "esp_tls.h"
 25  #include "esp_tls_error_capture_internal.h"
 26  #include <errno.h>
 27  static const char *TAG = "esp-tls";
 28  
 29  #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
 30  #include "esp_tls_mbedtls.h"
 31  #elif CONFIG_ESP_TLS_USING_WOLFSSL
 32  #include "esp_tls_wolfssl.h"
 33  #endif
 34  
 35  #ifdef ESP_PLATFORM
 36  #include <esp_log.h>
 37  #else
 38  #define ESP_LOGD(TAG, ...) //printf(__VA_ARGS__);
 39  #define ESP_LOGE(TAG, ...) printf(__VA_ARGS__);
 40  #endif
 41  
 42  #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
 43  #define _esp_create_ssl_handle              esp_create_mbedtls_handle
 44  #define _esp_tls_handshake                  esp_mbedtls_handshake
 45  #define _esp_tls_read                       esp_mbedtls_read
 46  #define _esp_tls_write                      esp_mbedtls_write
 47  #define _esp_tls_conn_delete                esp_mbedtls_conn_delete
 48  #ifdef CONFIG_ESP_TLS_SERVER
 49  #define _esp_tls_server_session_create      esp_mbedtls_server_session_create
 50  #define _esp_tls_server_session_delete      esp_mbedtls_server_session_delete
 51  #endif  /* CONFIG_ESP_TLS_SERVER */
 52  #define _esp_tls_get_bytes_avail            esp_mbedtls_get_bytes_avail
 53  #define _esp_tls_init_global_ca_store       esp_mbedtls_init_global_ca_store
 54  #define _esp_tls_set_global_ca_store        esp_mbedtls_set_global_ca_store                 /*!< Callback function for setting global CA store data for TLS/SSL */
 55  #define _esp_tls_get_global_ca_store        esp_mbedtls_get_global_ca_store
 56  #define _esp_tls_free_global_ca_store       esp_mbedtls_free_global_ca_store                /*!< Callback function for freeing global ca store for TLS/SSL */
 57  #elif CONFIG_ESP_TLS_USING_WOLFSSL /* CONFIG_ESP_TLS_USING_MBEDTLS */
 58  #define _esp_create_ssl_handle              esp_create_wolfssl_handle
 59  #define _esp_tls_handshake                  esp_wolfssl_handshake
 60  #define _esp_tls_read                       esp_wolfssl_read
 61  #define _esp_tls_write                      esp_wolfssl_write
 62  #define _esp_tls_conn_delete                esp_wolfssl_conn_delete
 63  #ifdef CONFIG_ESP_TLS_SERVER
 64  #define _esp_tls_server_session_create      esp_wolfssl_server_session_create
 65  #define _esp_tls_server_session_delete      esp_wolfssl_server_session_delete
 66  #endif  /* CONFIG_ESP_TLS_SERVER */
 67  #define _esp_tls_get_bytes_avail            esp_wolfssl_get_bytes_avail
 68  #define _esp_tls_init_global_ca_store       esp_wolfssl_init_global_ca_store
 69  #define _esp_tls_set_global_ca_store        esp_wolfssl_set_global_ca_store                 /*!< Callback function for setting global CA store data for TLS/SSL */
 70  #define _esp_tls_free_global_ca_store       esp_wolfssl_free_global_ca_store                /*!< Callback function for freeing global ca store for TLS/SSL */
 71  #else   /* ESP_TLS_USING_WOLFSSL */
 72  #error "No TLS stack configured"
 73  #endif
 74  
 75  static esp_err_t create_ssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
 76  {
 77      return _esp_create_ssl_handle(hostname, hostlen, cfg, tls);
 78  }
 79  
 80  static esp_err_t esp_tls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
 81  {
 82      return _esp_tls_handshake(tls, cfg);
 83  }
 84  
 85  static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
 86  {
 87      return recv(tls->sockfd, data, datalen, 0);
 88  }
 89  
 90  static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
 91  {
 92      return send(tls->sockfd, data, datalen, 0);
 93  }
 94  
 95  /**
 96   * @brief      Close the TLS connection and free any allocated resources.
 97   */
 98  void esp_tls_conn_delete(esp_tls_t *tls)
 99  {
100      esp_tls_conn_destroy(tls);
101  }
102  
103  int esp_tls_conn_destroy(esp_tls_t *tls)
104  {
105      if (tls != NULL) {
106          int ret = 0;
107          _esp_tls_conn_delete(tls);
108          if (tls->sockfd >= 0) {
109              ret = close(tls->sockfd);
110          }
111          free(tls->error_handle);
112          free(tls);
113          return ret;
114      }
115      return -1; // invalid argument
116  }
117  
118  esp_tls_t *esp_tls_init(void)
119  {
120      esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
121      if (!tls) {
122          return NULL;
123      }
124      tls->error_handle = calloc(1, sizeof(esp_tls_last_error_t));
125      if (!tls->error_handle) {
126          free(tls);
127          return NULL;
128      }
129  #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
130      tls->server_fd.fd = -1;
131  #endif
132      tls->sockfd = -1;
133      return tls;
134  }
135  
136  static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addrinfo **address_info)
137  {
138      struct addrinfo hints;
139      memset(&hints, 0, sizeof(hints));
140      hints.ai_family = AF_UNSPEC;
141      hints.ai_socktype = SOCK_STREAM;
142  
143      char *use_host = strndup(host, hostlen);
144      if (!use_host) {
145          return ESP_ERR_NO_MEM;
146      }
147  
148      ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
149      if (getaddrinfo(use_host, NULL, &hints, address_info)) {
150          ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host);
151          free(use_host);
152          return ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME;
153      }
154      free(use_host);
155      return ESP_OK;
156  }
157  
158  static void ms_to_timeval(int timeout_ms, struct timeval *tv)
159  {
160      tv->tv_sec = timeout_ms / 1000;
161      tv->tv_usec = (timeout_ms % 1000) * 1000;
162  }
163  
164  static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *sockfd, const esp_tls_t *tls, const esp_tls_cfg_t *cfg)
165  {
166      esp_err_t ret;
167      struct addrinfo *addrinfo;
168      if ((ret = resolve_host_name(host, hostlen, &addrinfo)) != ESP_OK) {
169          return ret;
170      }
171  
172      int fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
173      if (fd < 0) {
174          ESP_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
175          ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
176          ret = ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET;
177          goto err_freeaddr;
178      }
179  
180      void *addr_ptr;
181      if (addrinfo->ai_family == AF_INET) {
182          struct sockaddr_in *p = (struct sockaddr_in *)addrinfo->ai_addr;
183          p->sin_port = htons(port);
184          addr_ptr = p;
185      } else if (addrinfo->ai_family == AF_INET6) {
186          struct sockaddr_in6 *p = (struct sockaddr_in6 *)addrinfo->ai_addr;
187          p->sin6_port = htons(port);
188          p->sin6_family = AF_INET6;
189          addr_ptr = p;
190      } else {
191          ESP_LOGE(TAG, "Unsupported protocol family %d", addrinfo->ai_family);
192          ret = ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY;
193          goto err_freesocket;
194      }
195  
196      if (cfg) {
197          if (cfg->timeout_ms >= 0) {
198              struct timeval tv;
199              ms_to_timeval(cfg->timeout_ms, &tv);
200              setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
201              setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
202          }
203          if (cfg->non_block) {
204              int flags = fcntl(fd, F_GETFL, 0);
205              ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
206              if (ret < 0) {
207                  ESP_LOGE(TAG, "Failed to configure the socket as non-blocking (errno %d)", errno);
208                  goto err_freesocket;
209              }
210          }
211      }
212  
213      ret = connect(fd, addr_ptr, addrinfo->ai_addrlen);
214      if (ret < 0 && !(errno == EINPROGRESS && cfg && cfg->non_block)) {
215  
216          ESP_LOGE(TAG, "Failed to connnect to host (errno %d)", errno);
217          ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
218          ret = ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST;
219          goto err_freesocket;
220      }
221  
222      *sockfd = fd;
223      freeaddrinfo(addrinfo);
224      return ESP_OK;
225  
226  err_freesocket:
227      close(fd);
228  err_freeaddr:
229      freeaddrinfo(addrinfo);
230      return ret;
231  }
232  
233  static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
234  {
235      if (!tls) {
236          ESP_LOGE(TAG, "empty esp_tls parameter");
237          return -1;
238      }
239      esp_err_t esp_ret;
240      /* These states are used to keep a tab on connection progress in case of non-blocking connect,
241      and in case of blocking connect these cases will get executed one after the other */
242      switch (tls->conn_state) {
243      case ESP_TLS_INIT:
244          tls->sockfd = -1;
245          if (cfg != NULL) {
246  #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
247              mbedtls_net_init(&tls->server_fd);
248  #endif
249              tls->is_tls = true;
250          }
251          if ((esp_ret = esp_tcp_connect(hostname, hostlen, port, &tls->sockfd, tls, cfg)) != ESP_OK) {
252              ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
253              return -1;
254          }
255          if (!cfg) {
256              tls->read = tcp_read;
257              tls->write = tcp_write;
258              ESP_LOGD(TAG, "non-tls connection established");
259              return 1;
260          }
261          if (cfg->non_block) {
262              FD_ZERO(&tls->rset);
263              FD_SET(tls->sockfd, &tls->rset);
264              tls->wset = tls->rset;
265          }
266          tls->conn_state = ESP_TLS_CONNECTING;
267      /* falls through */
268      case ESP_TLS_CONNECTING:
269          if (cfg->non_block) {
270              ESP_LOGD(TAG, "connecting...");
271              struct timeval tv;
272              ms_to_timeval(cfg->timeout_ms, &tv);
273  
274              /* In case of non-blocking I/O, we use the select() API to check whether
275                 connection has been established or not*/
276              if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
277                         cfg->timeout_ms>0 ? &tv : NULL) == 0) {
278                  ESP_LOGD(TAG, "select() timed out");
279                  return 0;
280              }
281              if (FD_ISSET(tls->sockfd, &tls->rset) || FD_ISSET(tls->sockfd, &tls->wset)) {
282                  int error;
283                  unsigned int len = sizeof(error);
284                  /* pending error check */
285                  if (getsockopt(tls->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
286                      ESP_LOGD(TAG, "Non blocking connect failed");
287                      ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
288                      ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED);
289                      tls->conn_state = ESP_TLS_FAIL;
290                      return -1;
291                  }
292              }
293          }
294          /* By now, the connection has been established */
295          esp_ret = create_ssl_handle(hostname, hostlen, cfg, tls);
296          if (esp_ret != ESP_OK) {
297              ESP_LOGE(TAG, "create_ssl_handle failed");
298              ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
299              tls->conn_state = ESP_TLS_FAIL;
300              return -1;
301          }
302          tls->read = _esp_tls_read;
303          tls->write = _esp_tls_write;
304          tls->conn_state = ESP_TLS_HANDSHAKE;
305      /* falls through */
306      case ESP_TLS_HANDSHAKE:
307          ESP_LOGD(TAG, "handshake in progress...");
308          return esp_tls_handshake(tls, cfg);
309          break;
310      case ESP_TLS_FAIL:
311          ESP_LOGE(TAG, "failed to open a new connection");;
312          break;
313      default:
314          ESP_LOGE(TAG, "invalid esp-tls state");
315          break;
316      }
317      return -1;
318  }
319  
320  /**
321   * @brief      Create a new TLS/SSL connection
322   */
323  esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg)
324  {
325      esp_tls_t *tls = esp_tls_init();
326      if (!tls) {
327          return NULL;
328      }
329      /* esp_tls_conn_new() API establishes connection in a blocking manner thus this loop ensures that esp_tls_conn_new()
330         API returns only after connection is established unless there is an error*/
331      size_t start = xTaskGetTickCount();
332      while (1) {
333          int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
334          if (ret == 1) {
335              return tls;
336          } else if (ret == -1) {
337              esp_tls_conn_delete(tls);
338              ESP_LOGE(TAG, "Failed to open new connection");
339              return NULL;
340          } else if (ret == 0 && cfg->timeout_ms >= 0) {
341              size_t timeout_ticks = pdMS_TO_TICKS(cfg->timeout_ms);
342              uint32_t expired = xTaskGetTickCount() - start;
343              if (expired >= timeout_ticks) {
344                  esp_tls_conn_delete(tls);
345                  ESP_LOGE(TAG, "Failed to open new connection in specified timeout");
346                  return NULL;
347              }
348          }
349      }
350      return NULL;
351  }
352  
353  int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
354  {
355      /* esp_tls_conn_new_sync() is a sync alternative to esp_tls_conn_new_async() with symmetric function prototype
356      it is an alternative to esp_tls_conn_new() which is left for compatibility reasons */
357      size_t start = xTaskGetTickCount();
358      while (1) {
359          int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
360          if (ret == 1) {
361              return ret;
362          } else if (ret == -1) {
363              ESP_LOGE(TAG, "Failed to open new connection");
364              return -1;
365          } else if (ret == 0 && cfg->timeout_ms >= 0) {
366              size_t timeout_ticks = pdMS_TO_TICKS(cfg->timeout_ms);
367              uint32_t expired = xTaskGetTickCount() - start;
368              if (expired >= timeout_ticks) {
369                  ESP_LOGW(TAG, "Failed to open new connection in specified timeout");
370                  ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT);
371                  return 0;
372              }
373          }
374      }
375      return 0;
376  }
377  
378  /*
379   * @brief      Create a new TLS/SSL non-blocking connection
380   */
381  int esp_tls_conn_new_async(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
382  {
383      return esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
384  }
385  
386  static int get_port(const char *url, struct http_parser_url *u)
387  {
388      if (u->field_data[UF_PORT].len) {
389          return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
390      } else {
391          if (strncasecmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
392              return 80;
393          } else if (strncasecmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
394              return 443;
395          }
396      }
397      return 0;
398  }
399  
400  /**
401   * @brief      Create a new TLS/SSL connection with a given "HTTP" url
402   */
403  esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
404  {
405      /* Parse URI */
406      struct http_parser_url u;
407      http_parser_url_init(&u);
408      http_parser_parse_url(url, strlen(url), 0, &u);
409      esp_tls_t *tls = esp_tls_init();
410      if (!tls) {
411          return NULL;
412      }
413      /* Connect to host */
414      if (esp_tls_conn_new_sync(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
415                                get_port(url, &u), cfg, tls) == 1) {
416          return tls;
417      }
418      esp_tls_conn_delete(tls);
419      return NULL;
420  }
421  
422  /**
423   * @brief      Create a new non-blocking TLS/SSL connection with a given "HTTP" url
424   */
425  int esp_tls_conn_http_new_async(const char *url, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
426  {
427      /* Parse URI */
428      struct http_parser_url u;
429      http_parser_url_init(&u);
430      http_parser_parse_url(url, strlen(url), 0, &u);
431  
432      /* Connect to host */
433      return esp_tls_conn_new_async(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
434                                    get_port(url, &u), cfg, tls);
435  }
436  
437  #ifdef CONFIG_ESP_TLS_USING_MBEDTLS
438  
439  mbedtls_x509_crt *esp_tls_get_global_ca_store(void)
440  {
441      return _esp_tls_get_global_ca_store();
442  }
443  
444  #endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
445  #ifdef CONFIG_ESP_TLS_SERVER
446  /**
447   * @brief      Create a server side TLS/SSL connection
448   */
449  int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls)
450  {
451      return _esp_tls_server_session_create(cfg, sockfd, tls);
452  }
453  /**
454   * @brief      Close the server side TLS/SSL connection and free any allocated resources.
455   */
456  void esp_tls_server_session_delete(esp_tls_t *tls)
457  {
458      return _esp_tls_server_session_delete(tls);
459  }
460  #endif /* CONFIG_ESP_TLS_SERVER */
461  
462  ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls)
463  {
464      return _esp_tls_get_bytes_avail(tls);
465  }
466  
467  esp_err_t esp_tls_get_conn_sockfd(esp_tls_t *tls, int *sockfd)
468  {
469      if (!tls || !sockfd) {
470          ESP_LOGE(TAG, "Invalid arguments passed");
471          return ESP_ERR_INVALID_ARG;
472      }
473      *sockfd = tls->sockfd;
474      return ESP_OK;
475  }
476  
477  esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
478  {
479      if (!h) {
480          return ESP_ERR_INVALID_STATE;
481      }
482      esp_err_t last_err = h->last_error;
483      if (esp_tls_code) {
484          *esp_tls_code = h->esp_tls_error_code;
485      }
486      if (esp_tls_flags) {
487          *esp_tls_flags = h->esp_tls_flags;
488      }
489      memset(h, 0, sizeof(esp_tls_last_error_t));
490      return last_err;
491  }
492  
493  esp_err_t esp_tls_init_global_ca_store(void)
494  {
495      return _esp_tls_init_global_ca_store();
496  }
497  
498  esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
499  {
500      return _esp_tls_set_global_ca_store(cacert_pem_buf, cacert_pem_bytes);
501  }
502  
503  void esp_tls_free_global_ca_store(void)
504  {
505      return _esp_tls_free_global_ca_store();
506  }