#include "tls.h" #if MG_ENABLE_MBEDTLS ///////////////////////////////////////// MBEDTLS #include "log.h" #include "url.h" #include "util.h" #include #include #ifndef EXTERN_C #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C #endif #endif #if MBEDTLS_VERSION_NUMBER >= 0x03000000 #define RNG , rng_get, NULL #else #define RNG #endif // Different versions have those in different files, so declare here EXTERN_C int mbedtls_net_recv(void *, unsigned char *, size_t); EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t); struct mg_tls { char *cafile; // CA certificate path mbedtls_x509_crt ca; // Parsed CA certificate mbedtls_x509_crt cert; // Parsed certificate mbedtls_ssl_context ssl; // SSL/TLS context mbedtls_ssl_config conf; // SSL-TLS config mbedtls_pk_context pk; // Private key context }; void mg_tls_handshake(struct mg_connection *c) { struct mg_tls *tls = (struct mg_tls *) c->tls; int rc; mbedtls_ssl_set_bio(&tls->ssl, &c->fd, mbedtls_net_send, mbedtls_net_recv, 0); rc = mbedtls_ssl_handshake(&tls->ssl); if (rc == 0) { // Success LOG(LL_DEBUG, ("%lu success", c->id)); c->is_tls_hs = 0; } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending LOG(LL_VERBOSE_DEBUG, ("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting, c->is_tls_hs, rc, -rc)); } else { mg_error(c, "TLS handshake: -%#x", -rc); // Error } } static int mbed_rng(void *ctx, unsigned char *buf, size_t len) { mg_random(buf, len); (void) ctx; return 0; } static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) { n = (int) strlen(s2) - 1; LOG(LL_VERBOSE_DEBUG, ("%p %.*s", ((struct mg_connection *) c)->fd, n, s2)); (void) s; (void) c; (void) lev; } #if MBEDTLS_VERSION_NUMBER >= 0x03000000 static int rng_get(void *p_rng, unsigned char *buf, size_t len) { (void) p_rng; mg_random(buf, len); return 0; } #endif void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) { struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls)); int rc = 0; const char *ca = opts->ca == NULL ? "-" : opts->ca[0] == '-' ? "(emb)" : opts->ca; const char *cert = opts->cert == NULL ? "-" : opts->cert[0] == '-' ? "(emb)" : opts->cert; const char *certkey = opts->certkey == NULL ? "-" : opts->certkey[0] == '-' ? "(emb)" : opts->certkey; if (tls == NULL) { mg_error(c, "TLS OOM"); goto fail; } LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, ca, cert, certkey)); mbedtls_ssl_init(&tls->ssl); mbedtls_ssl_config_init(&tls->conf); mbedtls_x509_crt_init(&tls->ca); mbedtls_x509_crt_init(&tls->cert); mbedtls_pk_init(&tls->pk); mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c); //#if !defined(ESP_PLATFORM) // mbedtls_debug_set_threshold(5); //#endif if ((rc = mbedtls_ssl_config_defaults( &tls->conf, c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { mg_error(c, "tls defaults %#x", -rc); goto fail; } mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c); if (opts->ca == NULL || strcmp(opts->ca, "*") == 0) { mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE); } if (opts->ca != NULL && opts->ca[0] != '\0') { #if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK) tls->cafile = strdup(opts->ca); rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL); if (rc != 0) { mg_error(c, "parse on-disk chain(%s) err %#x", ca, -rc); goto fail; } #else rc = opts->ca[0] == '-' ? mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) opts->ca, strlen(opts->ca) + 1) : mbedtls_x509_crt_parse_file(&tls->ca, opts->ca); if (rc != 0) { mg_error(c, "parse(%s) err %#x", ca, -rc); goto fail; } mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL); #endif if (opts->srvname.len > 0) { char mem[128], *buf = mem; mg_asprintf(&buf, sizeof(mem), "%.*s", (int) opts->srvname.len, opts->srvname.ptr); mbedtls_ssl_set_hostname(&tls->ssl, buf); if (buf != mem) free(buf); } mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED); } if (opts->cert != NULL && opts->cert[0] != '\0') { const char *key = opts->certkey; if (key == NULL) { key = opts->cert; certkey = cert; } rc = opts->cert[0] == '-' ? mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) opts->cert, strlen(opts->cert) + 1) : mbedtls_x509_crt_parse_file(&tls->cert, opts->cert); if (rc != 0) { mg_error(c, "parse(%s) err %#x", cert, -rc); goto fail; } rc = key[0] == '-' ? mbedtls_pk_parse_key(&tls->pk, (uint8_t *) key, strlen(key) + 1, NULL, 0 RNG) : mbedtls_pk_parse_keyfile(&tls->pk, key, NULL RNG); if (rc != 0) { mg_error(c, "tls key(%s) %#x", certkey, -rc); goto fail; } rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk); if (rc != 0) { mg_error(c, "own cert %#x", -rc); goto fail; } } if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) { mg_error(c, "setup err %#x", -rc); goto fail; } c->tls = tls; c->is_tls = 1; c->is_tls_hs = 1; if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { mg_tls_handshake(c); } return; fail: c->is_closing = 1; free(tls); } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_READ ? 0 : n; } long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_WRITE ? 0 : n; } void mg_tls_free(struct mg_connection *c) { struct mg_tls *tls = (struct mg_tls *) c->tls; if (tls == NULL) return; free(tls->cafile); mbedtls_ssl_free(&tls->ssl); mbedtls_pk_free(&tls->pk); mbedtls_x509_crt_free(&tls->ca); mbedtls_x509_crt_free(&tls->cert); mbedtls_ssl_config_free(&tls->conf); free(tls); c->tls = NULL; } #elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL #include #include struct mg_tls { SSL_CTX *ctx; SSL *ssl; }; static int mg_tls_err(struct mg_tls *tls, int res) { int err = SSL_get_error(tls->ssl, res); // We've just fetched the last error from the queue. // Now we need to clear the error queue. If we do not, then the following // can happen (actually reported): // - A new connection is accept()-ed with cert error (e.g. self-signed cert) // - Since all accept()-ed connections share listener's context, // - *ALL* SSL accepted connection report read error on the next poll cycle. // Thus a single errored connection can close all the rest, unrelated ones. // Clearing the error keeps the shared SSL_CTX in an OK state. if (err != 0) ERR_print_errors_fp(stderr); ERR_clear_error(); if (err == SSL_ERROR_WANT_READ) return 0; if (err == SSL_ERROR_WANT_WRITE) return 0; return err; } void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) { struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls)); const char *id = "mongoose"; static unsigned char s_initialised = 0; int rc; if (tls == NULL) { mg_error(c, "TLS OOM"); goto fail; } if (!s_initialised) { SSL_library_init(); s_initialised++; } LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, opts->ca == NULL ? "null" : opts->ca, opts->cert == NULL ? "null" : opts->cert, opts->certkey == NULL ? "null" : opts->certkey)); tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method()) : SSL_CTX_new(SSLv23_server_method()); if ((tls->ssl = SSL_new(tls->ctx)) == NULL) { mg_error(c, "SSL_new"); goto fail; } SSL_set_session_id_context(tls->ssl, (const uint8_t *) id, (unsigned) strlen(id)); // Disable deprecated protocols SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2); SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3); SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1); SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1); #ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION); #endif #ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); #endif if (opts->ca != NULL && opts->ca[0] != '\0') { SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) { mg_error(c, "parse(%s): err %d", opts->ca, mg_tls_err(tls, rc)); goto fail; } } if (opts->cert != NULL && opts->cert[0] != '\0') { const char *key = opts->certkey; if (key == NULL) key = opts->cert; if ((rc = SSL_use_certificate_file(tls->ssl, opts->cert, 1)) != 1) { mg_error(c, "Invalid SSL cert, err %d", mg_tls_err(tls, rc)); goto fail; } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) { mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc)); goto fail; #if OPENSSL_VERSION_NUMBER > 0x10100000L } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) != 1) { mg_error(c, "Invalid CA, err %d", mg_tls_err(tls, rc)); goto fail; #endif } else { SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #if OPENSSL_VERSION_NUMBER > 0x10002000L SSL_set_ecdh_auto(tls->ssl, 1); #endif } } if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers); if (opts->srvname.len > 0) { char mem[128], *buf = mem; mg_asprintf(&buf, sizeof(mem), "%.*s", (int) opts->srvname.len, opts->srvname.ptr); SSL_set_tlsext_host_name(tls->ssl, buf); if (buf != mem) free(buf); } c->tls = tls; c->is_tls = 1; c->is_tls_hs = 1; if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { mg_tls_handshake(c); } c->is_hexdumping = 1; LOG(LL_DEBUG, ("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client")); return; fail: c->is_closing = 1; free(tls); } void mg_tls_handshake(struct mg_connection *c) { struct mg_tls *tls = (struct mg_tls *) c->tls; int rc; SSL_set_fd(tls->ssl, (int) (size_t) c->fd); rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl); if (rc == 1) { LOG(LL_DEBUG, ("%lu success", c->id)); c->is_tls_hs = 0; } else { int code = mg_tls_err(tls, rc); if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code); } } void mg_tls_free(struct mg_connection *c) { struct mg_tls *tls = (struct mg_tls *) c->tls; if (tls == NULL) return; SSL_free(tls->ssl); SSL_CTX_free(tls->ctx); free(tls); c->tls = NULL; } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; int n = SSL_read(tls->ssl, buf, (int) len); return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; } long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; int n = SSL_write(tls->ssl, buf, (int) len); return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; } #else ////////////////////////////////////////// NO TLS void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) { (void) opts; mg_error(c, "TLS is not enabled"); } void mg_tls_handshake(struct mg_connection *c) { (void) c; } void mg_tls_free(struct mg_connection *c) { (void) c; } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { return c == NULL || buf == NULL || len == 0 ? 0 : -1; } long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { return c == NULL || buf == NULL || len == 0 ? 0 : -1; } #endif