diff --git a/CHANGELOG.md b/CHANGELOG.md index a646e92..e9c846e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ Cytoplasm. It is intended to be updated with every commit that makes a user-faci change worth reporting in the change log. As such, it changes frequently between releases. Final change log entries are published as [Releases](releases). +## v0.5.0 + +**TO BE WRITTEN** + +### Breaking Changes +- `ShaToHex` now requires what hash was used. + +### New Features +- LMDB support is available with the `--with-lmdb` flag. +- MbedTLS summort is available with the `--with-mbed` flag. Please note that Cytoplasm +programs using it must be used with the `CYTO_TLS_CA` environment defined to map to a +valid CA file, and that the `CYTO_TLS_SEED` flag can be set to use a seedfile, increasing +entropy on systems where it is unavailable. + ## v0.4.1 Cytoplasm is now a C99 library! Upgrading from C89 to C99 makes Cytoplasm more portable diff --git a/README.md b/README.md index 08a8b5d..c2a9b58 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Cytoplasm aims to have zero software dependencies beyond what is mandated by POS - OpenSSL - LibreSSL +- MbedTLS (requires the `CYTO_TLS_CA` environment variables for programs built with it) If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested. @@ -50,7 +51,7 @@ You can also run `make install` as `root` to install Cytoplasm to the system. Th The `configure` script has a number of optional flags, which are as follows: -- `--with-(openssl|libressl)`: Select the TLS implementation to use. OpenSSL is selected by default. +- `--with-(openssl|libressl|mbed)`: Select the TLS implementation to use. OpenSSL is selected by default. - `--disable-tls`: Disable TLS altogether. - `--prefix=`: Set the install prefix to set by default in the `Makefile`. This defaults to `/usr/local`, which should be appropriate for most Unix-like systems. - `--(enable|disable)-debug`: Control whether or not to enable debug mode. This sets the optimization level to 0 and builds with debug symbols. Useful for running with a debugger. @@ -113,4 +114,4 @@ Note that both arguments to Main may be treated like any other Cytoplasm array o All of the code and documentation for Cytoplasm is licensed under the same license as Telodendria itself. Please refer to [Telodendria → License](/Telodendria/Telodendria#license) for details. -The Cytoplasm logo was designed by [Tobskep](https://tobskep.com) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license. \ No newline at end of file +The Cytoplasm logo was designed by [Tobskep](https://tobskep.com) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license. diff --git a/configure b/configure index deb186e..38e2145 100755 --- a/configure +++ b/configure @@ -76,6 +76,10 @@ for arg in $SCRIPT_ARGS; do TLS_IMPL="TLS_LIBRESSL" TLS_LIBS="-ltls -lcrypto -lssl" ;; + --with-mbed) + TLS_IMPL="TLS_MBEDTLS" + TLS_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto" + ;; --disable-tls) TLS_IMPL="" TLS_LIBS="" diff --git a/include/Cytoplasm/Tls.h b/include/Cytoplasm/Tls.h index 047ffbd..dc829e8 100644 --- a/include/Cytoplasm/Tls.h +++ b/include/Cytoplasm/Tls.h @@ -50,6 +50,7 @@ #define TLS_LIBRESSL 2 #define TLS_OPENSSL 3 +#define TLS_MBEDTLS 4 /** * Create a new TLS client stream using the given file descriptor and diff --git a/src/Sha/Sha1.c b/src/Sha/Sha1.c index c4f9db8..43a1742 100644 --- a/src/Sha/Sha1.c +++ b/src/Sha/Sha1.c @@ -29,6 +29,7 @@ #include /* TODO: Verify LibreSSL support later */ +#include #if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL) #include diff --git a/src/Sha/Sha256.c b/src/Sha/Sha256.c index 0b44b7e..80317cd 100644 --- a/src/Sha/Sha256.c +++ b/src/Sha/Sha256.c @@ -31,6 +31,7 @@ /* TODO: Verify LibreSSL support later */ +#include #if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL) #include diff --git a/src/Tls/TlsMbedTLS.c b/src/Tls/TlsMbedTLS.c new file mode 100644 index 0000000..a4249d1 --- /dev/null +++ b/src/Tls/TlsMbedTLS.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with + * other valuable contributors. See CONTRIBUTORS.txt for the full list. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +#if TLS_IMPL == TLS_MBEDTLS + +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" + +#include +#include +#include + +#include +#include + +/* + * #include statements and any implementation structures + * needed should go here. + */ +typedef struct MbedCookie { + int fd; + bool serverside; + + mbedtls_net_context serverFD; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctrDrbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cert; + + mbedtls_pk_context serverkey; +} MbedCookie; + +static bool +AddPEM(mbedtls_x509_crt *certs, char *path) +{ + size_t len; + if (!certs || !path) + { + return false; + } + + len = strlen(path); + if (len >= 4 && StrEquals(&path[len - 1 - 4], ".pem")) + { + /* Parse it as a file */ + if (mbedtls_x509_crt_parse_file(certs, path) == 0) + { + return true; + } + } + + /* Parse it as a directory if it is not a .PEM + * Note that this is non-recursive. */ + return mbedtls_x509_crt_parse_path(certs, path) == 0; +} +static bool +RegisterPEMs(mbedtls_x509_crt *certs) +{ + char *cafile; + if (!certs) + { + return false; + } + + /* Step 0: Load from CYTO_TLS_CA if present to overwrite */ + cafile = getenv("CYTO_TLS_CA"); + if (AddPEM(certs, cafile)) + { + return true; + } + + /* Step 1: Try /etc/ssl/certs */ + if (AddPEM(certs, "/etc/ssl/certs")) + { + return true; + } + /* Step 2: Try loading off Mozilla's certificates */ + if (AddPEM(certs, "/usr/share/ca-certificates/mozilla")) + { + return true; + } + + /* Step 3: Try loading from its root directly*/ + if (AddPEM(certs, "/usr/share/ca-certificates")) + { + return true; + } + + /* Step 4: Give up. */ + return false; +} + +void * +TlsInitClient(int fd, const char *serverName) +{ + MbedCookie *cookie; + int err; + if (!serverName) + { + return NULL; + } + + cookie = Malloc(sizeof(MbedCookie)); + memset(cookie, 0, sizeof(MbedCookie)); + cookie->fd = fd; + cookie->serverside = false; + + /* Initialise MbedTLS */ + mbedtls_net_init(&cookie->serverFD); + mbedtls_ssl_init(&cookie->ssl); + mbedtls_ssl_config_init(&cookie->conf); + mbedtls_x509_crt_init(&cookie->cert); + mbedtls_ctr_drbg_init(&cookie->ctrDrbg); + mbedtls_pk_init(&cookie->serverkey); + + mbedtls_entropy_init(&cookie->entropy); + err = mbedtls_ctr_drbg_seed( + &cookie->ctrDrbg, + mbedtls_entropy_func, + &cookie->entropy, + (const unsigned char *) serverName, strlen(serverName) + ); + if (err != 0) + { + Log(LOG_ERR, "MbedTLS failure on client init: %d", err); + goto error; + } + + /* TODO: Reconsider a source of additional entropy. */ + + cookie->serverFD.fd = fd; + + err = mbedtls_ssl_config_defaults( + &cookie->conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT + ); + if (err != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on client certs: %s", message); + goto error; + } + + /* Setup key verification */ + if (!RegisterPEMs(&cookie->cert)) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on client certs: %s", message); + goto error; + } + mbedtls_ssl_conf_ca_chain(&cookie->conf, &cookie->cert, NULL); + + /* Setup some callbacks */ + mbedtls_ssl_conf_rng( + &cookie->conf, + mbedtls_ctr_drbg_random, + &cookie->ctrDrbg + ); + if ((err = mbedtls_ssl_setup(&cookie->ssl, &cookie->conf)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on SSL setup: %s", message); + goto error; + } + + /* Setup the servername */ + if ((err = mbedtls_ssl_set_hostname(&cookie->ssl, serverName)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on client hostname: %s", message); + goto error; + } + + /* Setup some functions */ + mbedtls_ssl_set_bio( + &cookie->ssl, &cookie->serverFD, + mbedtls_net_send, mbedtls_net_recv, NULL + ); + return cookie; +error: + + mbedtls_net_free(&cookie->serverFD); + mbedtls_ssl_free(&cookie->ssl); + mbedtls_ssl_config_free(&cookie->conf); + mbedtls_ctr_drbg_free(&cookie->ctrDrbg); + mbedtls_entropy_free(&cookie->entropy); + Free(cookie); + return NULL; +} + +void * +TlsInitServer(int fd, const char *crt, const char *key) +{ + MbedCookie *cookie; + char *seed; + int err; + if (!crt || !key) + { + return NULL; + } + + cookie = Malloc(sizeof(MbedCookie)); + memset(cookie, 0, sizeof(MbedCookie)); + cookie->fd = fd; + cookie->serverside = true; + + /* Initialise MbedTLS */ + mbedtls_net_init(&cookie->serverFD); + mbedtls_ssl_init(&cookie->ssl); + mbedtls_ssl_config_init(&cookie->conf); + mbedtls_x509_crt_init(&cookie->cert); + mbedtls_ctr_drbg_init(&cookie->ctrDrbg); + mbedtls_pk_init(&cookie->serverkey); + + mbedtls_entropy_init(&cookie->entropy); + err = mbedtls_ctr_drbg_seed( + &cookie->ctrDrbg, + mbedtls_entropy_func, + &cookie->entropy, + (const unsigned char *) key, strlen(key) + ); + if (err != 0) + { + Log(LOG_ERR, "MbedTLS failure on server init: %d", err); + goto error; + } + /* Add a source of entropy if possible(using the CYTO_TLS_SEED env). + * Note that we ignore the error code. */ + seed = getenv("CYTO_TLS_SEED"); + mbedtls_entropy_update_seed_file(&cookie->entropy, seed); + + /* Setup key verification */ + if ((err = mbedtls_x509_crt_parse_file(&cookie->cert, crt)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on server certs: %s", message); + goto error; + } + + + + if ((err = mbedtls_pk_parse_keyfile(&cookie->serverkey, key, NULL, mbedtls_entropy_func, &cookie->ctrDrbg)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on server certs: %s", message); + goto error; + } + mbedtls_ssl_conf_ca_chain(&cookie->conf, cookie->cert.next, NULL); + if ((err = mbedtls_ssl_conf_own_cert(&cookie->conf, &cookie->cert, &cookie->serverkey)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on server certs: %s", message); + goto error; + } + + /* Setup SSL */ + cookie->serverFD.fd = fd; + err = mbedtls_ssl_config_defaults( + &cookie->conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT + ); + if (err != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on server SSL: %s", message); + goto error; + } + + /* Setup some callbacks */ + mbedtls_ssl_conf_rng( + &cookie->conf, + mbedtls_ctr_drbg_random, + &cookie->ctrDrbg + ); + mbedtls_ssl_conf_ca_chain(&cookie->conf, cookie->cert.next, NULL); + if ((err = mbedtls_ssl_setup(&cookie->ssl, &cookie->conf)) != 0) + { + char message[256]; + mbedtls_strerror(err, message, 255); + Log(LOG_ERR, "MbedTLS failure on SSL setup: %s", message); + goto error; + } + + /* Client connexion */ + /* Setup some functions */ + mbedtls_ssl_set_bio( + &cookie->ssl, &cookie->serverFD, + mbedtls_net_send, mbedtls_net_recv, NULL + ); + + /* Handshake the client */ + while ((err = mbedtls_ssl_handshake(&cookie->ssl)) != 0) + { + switch (err) + { + case MBEDTLS_ERR_SSL_WANT_WRITE: + case MBEDTLS_ERR_SSL_WANT_READ: + break; + default: + goto error; + } + } + return cookie; +error: + + mbedtls_net_free(&cookie->serverFD); + mbedtls_ssl_free(&cookie->ssl); + mbedtls_ssl_config_free(&cookie->conf); + mbedtls_ctr_drbg_free(&cookie->ctrDrbg); + mbedtls_entropy_free(&cookie->entropy); + Free(cookie); + return NULL; +} + +ssize_t +TlsRead(void *cookie, void *buf, size_t nBytes) +{ + MbedCookie *cooked = cookie; + int ret; + if (!cookie || !buf) + { + return -1; + } + ret = mbedtls_ssl_read(&cooked->ssl, buf, nBytes); + if (ret <= 0) + { + ret = -1; + } + return ret; +} + +ssize_t +TlsWrite(void *cookie, void *buf, size_t nBytes) +{ + MbedCookie *cooked = cookie; + int ret; + if (!cookie || !buf) + { + return -1; + } + ret = mbedtls_ssl_write(&cooked->ssl, buf, nBytes); + if (ret <= 0) + { + ret = -1; + } + + return ret; +} + +int +TlsClose(void *cookie) +{ + MbedCookie *cooked = cookie; + if (!cookie) + { + return -1; + } + + mbedtls_net_free(&cooked->serverFD); + mbedtls_ssl_free(&cooked->ssl); + mbedtls_ssl_config_free(&cooked->conf); + mbedtls_ctr_drbg_free(&cooked->ctrDrbg); + mbedtls_entropy_free(&cooked->entropy); + mbedtls_x509_crt_free(&cooked->cert); + + if (cooked->serverside) + { + mbedtls_pk_free(&cooked->serverkey); + } + Free(cooked); + return 0; +} + +#endif