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 }