diff --git a/Makefile b/Makefile index 86659d74..c0621e5f 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,8 @@ export UNAME_S LIBS = -lrt -lm -ldl -LIBS += -lbfd -lcrypt -lssl -lcrypto -lcurl -lreadline -luuid -rdynamic +# -lcrypto needed for SHA1_Init in hash.c +LIBS += -lbfd -lcrypt -lcrypto -lcurl -lreadline -luuid -rdynamic ifeq ($(UNAME_S),Linux) LIBS += -lbsd -lcap @@ -40,7 +41,7 @@ INSTALL = install # Uncomment this to see all build commands instead of 'quiet' output #NOISY_BUILD=yes -MOD_SUBDIR:=doors modules nets +MOD_SUBDIR:=doors io modules nets SUBDIRS:=$(MOD_SUBDIR) SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install) SUBDIRS_CLEAN:=$(SUBDIRS:%=%-clean) diff --git a/bbs/bbs.c b/bbs/bbs.c index dc0ea094..8ab09c4f 100644 --- a/bbs/bbs.c +++ b/bbs/bbs.c @@ -66,7 +66,6 @@ #include "include/group.h" #include "include/variables.h" #include "include/startup.h" -#include "include/tls.h" #include "include/event.h" #include "include/test.h" #include "include/transfer.h" @@ -584,7 +583,6 @@ static void bbs_shutdown(void) bbs_history_shutdown(); /* Free history. Must be done in the core, not by mod_sysop, since this may only be called once. */ bbs_curl_shutdown(); /* Clean up cURL */ - ssl_server_shutdown(); /* Shut down SSL/TLS */ login_cache_cleanup(); /* Clean up any remaining cached logins */ username_cache_flush(); /* Clean up any cached username mappings */ bbs_free_menus(); /* Clean up menus */ @@ -1015,8 +1013,6 @@ int main(int argc, char *argv[]) CHECK_INIT(bbs_init_doors()); CHECK_INIT(bbs_init_tests()); - ssl_server_init(); /* If this fails for some reason, that's okay. Other failures will ensue, but this is not fatal. */ - CHECK_INIT(bbs_curl_init()); CHECK_INIT(bbs_history_init()); diff --git a/bbs/hash.c b/bbs/hash.c index 5748cc9d..aa804893 100644 --- a/bbs/hash.c +++ b/bbs/hash.c @@ -22,10 +22,12 @@ #include "include/hash.h" /* For hashing: */ -#ifdef HAVE_OPENSSL #include #include -#endif + +#include +#include +#include #undef sprintf @@ -34,7 +36,6 @@ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* SHA256_Init, SHA256_Update, SHA256_Final deprecated in OpenSSL 3.0 */ int hash_sha256(const char *s, char buf[SHA256_BUFSIZE]) { -#ifdef HAVE_OPENSSL int i; unsigned char hash[SHA256_DIGEST_LENGTH]; @@ -49,17 +50,10 @@ int hash_sha256(const char *s, char buf[SHA256_BUFSIZE]) } buf[SHA256_BUFSIZE - 1] = '\0'; return 0; -#else - UNUSED(s); - UNUSED(buf); - UNUSED(len); - return -1; -#endif } int hash_sha1(const char *s, char buf[SHA1_BUFSIZE]) { -#ifdef HAVE_OPENSSL int i; unsigned char hash[SHA_DIGEST_LENGTH]; @@ -74,17 +68,10 @@ int hash_sha1(const char *s, char buf[SHA1_BUFSIZE]) } buf[SHA1_BUFSIZE - 1] = '\0'; return 0; -#else - UNUSED(s); - UNUSED(buf); - UNUSED(len); - return -1; -#endif } int hash_sha1_bytes(const char *s, char buf[SHA1_LEN]) { -#ifdef HAVE_OPENSSL unsigned char hash[SHA_DIGEST_LENGTH]; /* We already use OpenSSL, just use that */ @@ -95,11 +82,5 @@ int hash_sha1_bytes(const char *s, char buf[SHA1_LEN]) memcpy(buf, hash, SHA1_LEN); return 0; -#else - UNUSED(s); - UNUSED(buf); - UNUSED(len); - return -1; -#endif } #pragma GCC diagnostic pop /* -Wdeprecated-declarations */ diff --git a/bbs/io.c b/bbs/io.c new file mode 100644 index 00000000..f638255a --- /dev/null +++ b/bbs/io.c @@ -0,0 +1,273 @@ +/* + * LBBS -- The Lightweight Bulletin Board System + * + * Copyright (C) 2024, Naveen Albert + * + * Naveen Albert + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Abstract I/O transformations interface + * + * \author Naveen Albert + */ + +#include "include/bbs.h" + +#include "include/linkedlists.h" +#include "include/module.h" +#include "include/io.h" + +/* Unlike most I/O stream abstractions, such as OpenSSL's BIO, + * Dovecot's read/write streams, and libetpan's "low" interface, + * this is not a truly abstract I/O interface. + * It is an interface that is highly coupled to file descriptors, + * since much of the I/O in the BBS is currently written to depend on that. + * While it would be more performant to be able to call I/O callback functions + * that could, for example, call SSL_write directly under the hood, + * rather than first writing to a pipe which is then drained in another + * thread and passed to SSL_write, at this point, it would require + * substantial work to refactor everything not to use file descriptors directly, + * since initially it was only needed for TLS and nothing else. + * + * This abstraction is still useful, since instead of keeping track + * of multiple read/write file descriptors, we can continue to only + * use one and I/O modules will be responsible for setting up their + * own intermediate layer. This also allows for modularity since + * dependencies for particular kinds of I/O transformations (e.g. TLS, compression) + * need not be embedded in the core, but can be implemented in their own modules. + */ + +struct bbs_io_transformer { + const char *name; + enum bbs_io_transform_type type; + enum bbs_io_transform_dir dir; + int (*setup)(int *rfd, int *wfd, enum bbs_io_transform_dir dir, void **restrict data, const void *arg); + int (*query)(struct bbs_io_transformation *tran, int query, void *data); + void (*cleanup)(struct bbs_io_transformation *tran); + void *module; + RWLIST_ENTRY(bbs_io_transformer) entry; + char data[]; +}; + +static RWLIST_HEAD_STATIC(transformers, bbs_io_transformer); + +int __bbs_io_transformer_register(const char *name, int (*setup)(int *rfd, int *wfd, enum bbs_io_transform_dir dir, void **restrict data, const void *arg), + int (*query)(struct bbs_io_transformation *tran, int query, void *data), + void (*cleanup)(struct bbs_io_transformation *tran), enum bbs_io_transform_type type, enum bbs_io_transform_dir dir, void *module) +{ + struct bbs_io_transformer *t; + + RWLIST_WRLOCK(&transformers); + RWLIST_TRAVERSE(&transformers, t, entry) { + if (!strcasecmp(name, t->name)) { + RWLIST_UNLOCK(&transformers); + bbs_error("I/O transformer '%s' already registered\n", name); + return -1; + } + } + t = calloc(1, sizeof(*t) + strlen(name) + 1); + if (ALLOC_FAILURE(t)) { + RWLIST_UNLOCK(&transformers); + return -1; + } + strcpy(t->data, name); /* Safe */ + t->name = t->data; + t->module = module; + t->setup = setup; + t->query = query; + t->cleanup = cleanup; + t->type = type; + t->dir = dir; + RWLIST_INSERT_TAIL(&transformers, t, entry); + RWLIST_UNLOCK(&transformers); + + return 0; +} + +int bbs_io_transformer_unregister(const char *name) +{ + struct bbs_io_transformer *t; + + RWLIST_WRLOCK(&transformers); + RWLIST_TRAVERSE_SAFE_BEGIN(&transformers, t, entry) { + if (!strcasecmp(name, t->name)) { + RWLIST_REMOVE_CURRENT(entry); + free(t); + break; + } + } + RWLIST_TRAVERSE_SAFE_END; + RWLIST_UNLOCK(&transformers); + + return t ? 0 : -1; +} + +int bbs_io_named_transformer_available(const char *name) +{ + struct bbs_io_transformer *t; + + RWLIST_RDLOCK(&transformers); + RWLIST_TRAVERSE(&transformers, t, entry) { + if (!strcmp(name, t->name)) { + break; + } + } + RWLIST_UNLOCK(&transformers); + + if (!t) { + bbs_debug(3, "No such transformer named '%s'\n", name); + } + + return t ? 1 : 0; +} + +int bbs_io_transformer_available(enum bbs_io_transform_type transform_type) +{ + struct bbs_io_transformer *t; + + RWLIST_RDLOCK(&transformers); + RWLIST_TRAVERSE(&transformers, t, entry) { + if (t->type == transform_type) { + break; + } + } + RWLIST_UNLOCK(&transformers); + + if (!t) { + bbs_debug(3, "No such transformer of type %d\n", transform_type); + } + + return t ? 1 : 0; +} + +static int io_transform_slots_free(struct bbs_io_transformations *trans) +{ + int i; + + for (i = 0; i < MAX_IO_TRANSFORMS; i++) { + if (!trans->transformations[i].transformer) { + /* Not in use */ + return 1; + } + } + return 0; +} + +static int io_transform_store(struct bbs_io_transformations *trans, struct bbs_io_transformer *t, void *data) +{ + int i; + + for (i = 0; i < MAX_IO_TRANSFORMS; i++) { + if (!trans->transformations[i].transformer) { + trans->transformations[i].data = data; + trans->transformations[i].transformer = t; + bbs_debug(7, "Set up node I/O transformer at index %d\n", i); + return 0; + } + } + /* Shouldn't happen since only one thread is really handling a node's I/O at a time */ + bbs_error("Failed to store transformation\n"); + return -1; +} + +int bbs_io_transform_setup(struct bbs_io_transformations *trans, enum bbs_io_transform_type type, enum bbs_io_transform_dir direction, int *rfd, int *wfd, const void *arg) +{ + int res; + void *data = NULL; + struct bbs_io_transformer *t; + + RWLIST_RDLOCK(&transformers); + if (!io_transform_slots_free(trans)) { + RWLIST_UNLOCK(&transformers); + bbs_error("Already at max transformations (%d)\n", MAX_IO_TRANSFORMS); + return -1; + } + RWLIST_TRAVERSE(&transformers, t, entry) { + if (!(t->dir & direction)) { + continue; + } + if (t->type == type) { + break; + } + } + + if (!t) { + /* Should use bbs_io_transformer_available before to check. + * Yes, that is TOCTOU, but this should happen infrequently, + * although it is possible, hence a warning, not an error: */ + RWLIST_UNLOCK(&transformers); + bbs_warning("No suitable transformer found (type %d)\n", type); + return -1; + } + + res = t->setup(rfd, wfd, direction, &data, arg); + + /* Store transform private data on node */ + if (!res) { + if (io_transform_store(trans, t, data)) { + struct bbs_io_transformation tran; + tran.transformer = t; + tran.data = data; + t->cleanup(&tran); + res = 1; + } else { + bbs_module_ref(t->module, 1); + } + } + RWLIST_UNLOCK(&transformers); + + return res; +} + +int bbs_io_transform_query(struct bbs_io_transformations *trans, enum bbs_io_transform_type type, int query, void *data) +{ + int i; + int res = -1; + + RWLIST_RDLOCK(&transformers); + for (i = 0; i < MAX_IO_TRANSFORMS; i++) { + if (trans->transformations[i].data) { + struct bbs_io_transformer *t = trans->transformations[i].transformer; + if (t->type == type) { + if (t->query) { + res = t->query(&trans->transformations[i], query, data); + } else { + res = 1; + } + break; + } + } + } + RWLIST_UNLOCK(&transformers); + + return res; +} + +static void teardown_transformation(struct bbs_io_transformation *tran) +{ + struct bbs_io_transformer *t = tran->transformer; + t->cleanup(tran); + tran->data = NULL; + tran->transformer = NULL; + bbs_module_unref(t->module, 1); +} + +void bbs_io_teardown_all_transformers(struct bbs_io_transformations *trans) +{ + int i; + + RWLIST_RDLOCK(&transformers); + for (i = 0; i < MAX_IO_TRANSFORMS; i++) { + if (trans->transformations[i].data) { + bbs_debug(7, "Removing I/O transformer at index %d\n", i); + teardown_transformation(&trans->transformations[i]); + } + } + RWLIST_UNLOCK(&transformers); +} diff --git a/bbs/module.c b/bbs/module.c index 057c0dcc..b2768daf 100644 --- a/bbs/module.c +++ b/bbs/module.c @@ -500,7 +500,18 @@ static void check_dependencies(const char *restrict resource_in, unsigned int su return; } - if (!strlen_zero(mod->info->dependencies)) { + if (mod->info->flags & MODFLAG_ALWAYS_PRELOAD) { + /* The module wants to be loaded as early as possible during startup, + * so add it to the preload list. + * Do this first, since then we can avoid checking if it has any dependents. + * XXX This is not foolproof; ideally, we would have store a "load priority" + * for all modules, and ensure that the priority of this module + * is before anything that might try to use it (or behave differently if not loaded). */ + if (!stringlist_contains(&modules_preload, resource_in)) { + bbs_debug(2, "Module %s requested to be preloaded\n", resource_in); + stringlist_push(&modules_preload, resource_in); + } + } else if (!strlen_zero(mod->info->dependencies)) { char dependencies_buf[256]; char *dependencies, *dependency; safe_strncpy(dependencies_buf, mod->info->dependencies, sizeof(dependencies_buf)); diff --git a/bbs/node.c b/bbs/node.c index cb159af2..4e00442d 100644 --- a/bbs/node.c +++ b/bbs/node.c @@ -520,6 +520,8 @@ static void node_shutdown(struct bbs_node *node, int unique) time_t now; int wasloggedin = 0; + bbs_io_teardown_all_transformers(&node->trans); + /* Prevent node from being freed until we release the lock. */ bbs_node_lock(node); if (!node->active) { @@ -2079,6 +2081,31 @@ void bbs_node_net_begin(struct bbs_node *node) bbs_node_begin(node); } +int bbs_node_starttls(struct bbs_node *node) +{ + int res; + + if (unlikely(node->secure)) { + bbs_error("Can't start TLS, connection already encrypted\n"); + return 1; + } + + if (!bbs_io_transformer_available(TRANSFORM_TLS_ENCRYPTION)) { + return 1; + } + + res = bbs_io_transform_setup(&node->trans, TRANSFORM_TLS_ENCRYPTION, TRANSFORM_SERVER, &node->rfd, &node->wfd, NULL); + if (res) { + /* If TLS setup fails, it's probably garbage traffic and safe to penalize: */ + if (node) { + bbs_event_dispatch(node, EVENT_NODE_ENCRYPTION_FAILED); + } + } else { + node->secure = 1; + } + return res; +} + void bbs_node_exit(struct bbs_node *node) { bbs_soft_assert(node->id > 0); diff --git a/bbs/socket.c b/bbs/socket.c index 6687ff6a..40c75960 100644 --- a/bbs/socket.c +++ b/bbs/socket.c @@ -62,7 +62,6 @@ #include "include/node.h" #include "include/term.h" /* use bbs_unbuffer */ -#include "include/tls.h" #include "include/alertpipe.h" #include "include/net.h" #include "include/linkedlists.h" diff --git a/bbs/tcp.c b/bbs/tcp.c index e35f3c36..242dfd4c 100644 --- a/bbs/tcp.c +++ b/bbs/tcp.c @@ -19,19 +19,26 @@ #include "include/bbs.h" +#include + #include "include/utils.h" -#include "include/tls.h" #include "include/node.h" void bbs_tcp_client_cleanup(struct bbs_tcp_client *client) { - if (client->ssl) { - ssl_close(client->ssl); - client->ssl = NULL; - } + bbs_io_teardown_all_transformers(&client->trans); close_if(client->fd); } +static int starttls(struct bbs_tcp_client *client, const char *snihostname) +{ + if (!bbs_io_transformer_available(TRANSFORM_TLS_ENCRYPTION)) { + return 1; + } + + return bbs_io_transform_setup(&client->trans, TRANSFORM_TLS_ENCRYPTION, TRANSFORM_CLIENT, &client->rfd, &client->wfd, snihostname); +} + int bbs_tcp_client_connect(struct bbs_tcp_client *client, struct bbs_url *url, int secure, char *buf, size_t len) { client->fd = bbs_tcp_connect(url->host, url->port); @@ -43,8 +50,7 @@ int bbs_tcp_client_connect(struct bbs_tcp_client *client, struct bbs_url *url, i client->buf = buf; client->len = len; if (client->secure) { - client->ssl = ssl_client_new(client->fd, &client->rfd, &client->wfd, url->host); - if (!client->ssl) { + if (starttls(client, url->host)) { bbs_debug(3, "Failed to set up TLS\n"); close_if(client->fd); return -1; @@ -58,12 +64,11 @@ int bbs_tcp_client_connect(struct bbs_tcp_client *client, struct bbs_url *url, i int bbs_tcp_client_starttls(struct bbs_tcp_client *client, const char *hostname) { bbs_assert(!client->secure); - client->ssl = ssl_client_new(client->fd, &client->rfd, &client->wfd, hostname); - if (!client->ssl) { + + if (starttls(client, hostname)) { bbs_warning("Failed to do STARTTLS\n"); return -1; } - client->secure = 1; bbs_readline_flush(&client->rldata); /* Prevent STARTTLS response injection by resetting the buffer after TLS upgrade */ return 0; } diff --git a/bbs/utils.c b/bbs/utils.c index 6acfb44d..d3e9510d 100644 --- a/bbs/utils.c +++ b/bbs/utils.c @@ -21,6 +21,7 @@ #include #include +#include #include #include /* use isprint, isspace */ #include diff --git a/external/modman.c b/external/modman.c index 8f7c059b..bdd3cfad 100644 --- a/external/modman.c +++ b/external/modman.c @@ -743,6 +743,7 @@ static int check_unmet_dependencies(int autodisable) res |= check_module_subdir("bbs", 0); /* Check the core, too, but never autodisable, the build should fail if we the core dependencies aren't met */ #endif res |= check_module_subdir("doors", autodisable); + res |= check_module_subdir("io", autodisable); res |= check_module_subdir("modules", autodisable); res |= check_module_subdir("nets", autodisable); diff --git a/include/hash.h b/include/hash.h index 689a2289..b3108c57 100644 --- a/include/hash.h +++ b/include/hash.h @@ -13,15 +13,6 @@ * */ -/* I know we link with -lssl statically in the main binary, this is just for semantics, mainly */ -#define HAVE_OPENSSL - -#ifdef HAVE_OPENSSL -#include -#include -#include -#endif - #define SHA256_BUFSIZE 65 #define SHA1_LEN 20 #define SHA1_BUFSIZE 41 /* 160 bits = 20 bytes = 40 hex digits + NUL */ diff --git a/include/io.h b/include/io.h new file mode 100644 index 00000000..54874335 --- /dev/null +++ b/include/io.h @@ -0,0 +1,109 @@ +/* + * LBBS -- The Lightweight Bulletin Board System + * + * Copyright (C) 2024, Naveen Albert + * + * Naveen Albert + * + */ + +/*! \file + * + * \brief Abstract I/O transformations interface + * + */ + +#ifndef _BBS_IO_TRANSFORM +#define _BBS_IO_TRANSFORM + +enum bbs_io_transform_type { + TRANSFORM_TLS_ENCRYPTION = 0, /*!< TLS encryption/decryption */ + TRANSFORM_ZLIB_COMPRESSION = 1, /*!< zlib compression/decompression */ +}; + +/* Number of transform types in above enum + a few more if we allow other kinds */ +#define MAX_IO_TRANSFORMS 2 + +enum bbs_io_transform_dir { + TRANSFORM_SERVER_TX = (1 << 0), + TRANSFORM_SERVER_RX = (1 << 1), + TRANSFORM_CLIENT_TX = (1 << 2), + TRANSFORM_CLIENT_RX = (1 << 3), +}; + +#define TRANSFORM_SERVER (TRANSFORM_SERVER_TX | TRANSFORM_SERVER_RX) +#define TRANSFORM_CLIENT (TRANSFORM_CLIENT_TX | TRANSFORM_CLIENT_RX) +#define TRANSFORM_SERVER_CLIENT_TX_RX (TRANSFORM_SERVER_TX | TRANSFORM_SERVER_RX | TRANSFORM_CLIENT_TX | TRANSFORM_CLIENT_RX) + +#define TRANSFORM_QUERY_TLS_REUSE 0 + +struct bbs_io_transformer; + +struct bbs_io_transformation { + struct bbs_io_transformer *transformer; /* Transformer */ + void *data; /* Transformer's private data */ +}; + +struct bbs_io_transformations { + struct bbs_io_transformation transformations[MAX_IO_TRANSFORMS]; +}; + +int __bbs_io_transformer_register(const char *name, int (*setup)(int *rfd, int *wfd, enum bbs_io_transform_dir dir, void **restrict data, const void *arg), + int (*query)(struct bbs_io_transformation *tran, int query, void *data), + void (*cleanup)(struct bbs_io_transformation *tran), enum bbs_io_transform_type type, enum bbs_io_transform_dir dir, void *module); + +/*! + * \brief Register I/O transformer callbacks. + * \param name + * \param setup + * \param query Optional + * \param cleanup + * \param type + * \param dir + * \retval 0 on success, -1 on failure + */ +#define bbs_io_transformer_register(name, setup, query, cleanup, type, dir) __bbs_io_transformer_register(name, setup, query, cleanup, type, dir, BBS_MODULE_SELF) + +/*! \brief Unergister I/O transformer callback */ +int bbs_io_transformer_unregister(const char *name); + +/*! \brief Check if a transformer is registered by name */ +int bbs_io_named_transformer_available(const char *name); + +/*! \brief Check if a suitable transformer is available */ +int bbs_io_transformer_available(enum bbs_io_transform_type transform_type); + +/*! + * \brief Begin using a transformer. This action is permanent (only bbs_io_teardown_all_transformers will end transformation) + * \param trans + * \param type + * \param direction + * \param rfd Must be initialized, but may be modified + * \param wfd Must be initialized, but may be modified + * \param arg Optional data. For TRANSFORM_TLS_ENCRYPTION and TRANSFORM_CLIENT, the Server Name Indication hostname + * \retval 0 on success, -1 on failure + */ +int bbs_io_transform_setup(struct bbs_io_transformations *trans, enum bbs_io_transform_type type, enum bbs_io_transform_dir direction, int *rfd, int *wfd, const void *arg); + +/*! + * \brief Read and/or write a setting while a transformation is active + * \param trans + * \param type + * \param query A TRANSFORM_QUERY argument + * \param Either an input and/or output parameter depending on the query. For TRANSFORM_QUERY_TLS_REUSE, will be set to whether the connection was reused + * \retval 0 on success + * \retval 1 no query callback + * \retval -1 on failure or no such type + */ +int bbs_io_transform_query(struct bbs_io_transformations *trans, enum bbs_io_transform_type type, int query, void *data); + +/*! + * \brief Terminate all transformations, typically immediately before closing the surrounding file descriptors + * \param trans + * \note This function is idempotent (it may be called repeatedly without side effects) + */ +void bbs_io_teardown_all_transformers(struct bbs_io_transformations *trans); + +#define ssl_available() (bbs_io_transformer_available(TRANSFORM_TLS_ENCRYPTION)) + +#endif /* _BBS_IO_TRANSFORM */ diff --git a/include/module.h b/include/module.h index aef20a91..5ce25e8b 100644 --- a/include/module.h +++ b/include/module.h @@ -16,6 +16,7 @@ enum bbs_module_flags { MODFLAG_DEFAULT = 0, MODFLAG_GLOBAL_SYMBOLS = (1 << 0), /* Module exports global symbols */ + MODFLAG_ALWAYS_PRELOAD = (1 << 1), /* Module should be loaded as early as possible */ }; struct bbs_module_info { diff --git a/include/node.h b/include/node.h index c6fed16d..a3bc348e 100644 --- a/include/node.h +++ b/include/node.h @@ -15,6 +15,7 @@ #include "linkedlists.h" /* for RWLIST_ENTRY */ #include "keys.h" /* key definitions */ +#include "io.h" /* for I/O transformations */ struct bbs_module; struct bbs_user; @@ -42,6 +43,7 @@ struct bbs_node { char slavename[84]; /*!< PTY slave name */ int spyfd; /*!< Sysop's STDOUT file descriptor */ int spyfdin; /*!< Sysop's STDIN file descriptor */ + struct bbs_io_transformations trans; /*!< I/O transformations */ unsigned int rows; /*!< Screen size: number of rows */ unsigned int cols; /*!< Screen size: number of columns */ pthread_t thread; /*!< Thread handling socket I/O */ @@ -80,6 +82,7 @@ struct bbs_node { unsigned int nonagle:1; /*!< Nagle's algorithm disabled */ unsigned int dimensions:1; /*!< Aware of actual terminal dimensions */ unsigned int ansi:1; /*!< Terminal supports ANSI escape sequences */ + unsigned int secure:1; /*!< Connection encrypted using TLS */ int ans; /*!< Detailed ANSI support flags */ /* TDD stuff */ char ioreplace[10][2]; /*!< Character replacement for TDDs and other keyboard input-limited endpoints. 2D list with 10 slots. */ @@ -791,6 +794,13 @@ void bbs_node_begin(struct bbs_node *node); */ void bbs_node_net_begin(struct bbs_node *node); +/*! + * \brief Start TLS on a node, splitting node->fd into node->rfd and node->wfd for TLS I/O + * \param node + * \retval 0 on success, -1 on failure, 1 if TLS not available + */ +int bbs_node_starttls(struct bbs_node *node); + /*! \brief Stop handling a node * \note Not needed if you use bbs_node_handler * \note After calling this function, node will no longer be a valid reference diff --git a/include/tcp.h b/include/tcp.h index 95f82a3f..46031782 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -14,6 +14,8 @@ * \author Naveen Albert */ +#include "include/io.h" + struct bbs_url; struct bbs_tcp_client { @@ -23,7 +25,7 @@ struct bbs_tcp_client { int fd; int rfd; int wfd; - SSL *ssl; + struct bbs_io_transformations trans; /*!< I/O transformations */ unsigned int secure:1; }; diff --git a/include/tls.h b/include/tls.h deleted file mode 100644 index 10dd3b6d..00000000 --- a/include/tls.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * LBBS -- The Lightweight Bulletin Board System - * - * Copyright (C) 2023, Naveen Albert - * - * Naveen Albert - * - */ - -/*! \file - * - * \brief Transport Layer Security (TLS) - * - */ - -struct bbs_node; - -/* I know we link with -lssl statically in the main binary, this is just for semantics, mainly */ -#define HAVE_OPENSSL - -#ifdef HAVE_OPENSSL -#include -#include -#include -#endif - -/*! \brief Get the string version of an OpenSSL error */ -const char *ssl_strerror(int err); - -/*! - * \brief Create a new SSL structure for a file descriptor for a server TLS session - * \param node BBS node - * \param fd Server file descriptor returned previously by accept() - * \param[out] rfd File descriptor for reading from connection (data has been decrypted) - * \param[out] wfd File descriptor for writing to connection (data will be encrypted) - * \retval ssl on success, NULL on failure - * \note This may be used immediately after accept or later in the session (e.g. STARTTLS) - * \note In most cases, ssl_node_new_accept should be used, instead of using this function directly. - */ -SSL *ssl_new_accept(struct bbs_node *node, int fd, int *rfd, int *wfd); - -/*! - * \brief Set up TLS for a node's socket file descriptor - * \param node BBS node. NULL if not associated with a node. - * \param fd Server file descriptor returned previously by accept() - * \param[out] rfd File descriptor for reading from connection (data has been decrypted) - * \param[out] wfd File descriptor for writing to connection (data will be encrypted) - * \retval ssl on success, NULL on failure - * \note This may be used immediately after accept or later in the session (e.g. STARTTLS) - */ -SSL *ssl_node_new_accept(struct bbs_node *node, int *rfd, int *wfd) __attribute__((nonnull (1))); - -/*! - * \brief Create a new SSL structure for a file descriptor for a client TLS session - * \param fd Client file descriptor - * \param[out] rfd File descriptor for reading from connection (data has been decrypted) - * \param[out] wfd File descriptor for writing to connection (data will be encrypted) - * \param snihostname Hostname to use for Server Name Indication - * \retval ssl on success, NULL on failure - */ -SSL *ssl_client_new(int fd, int *rfd, int *wfd, const char *snihostname); - -/*! - * \brief Close and free an OpenSSL connection - * \retval 0 on success, -1 on failure - */ -int ssl_close(SSL *ssl); - -/*! - * \brief Whether SSL is available. Modules requiring SSL/TLS functionality should ensure this returns true - * \retval 1 if available, 0 if not - */ -int ssl_available(void); - -/*! \brief Load SSL config on startup */ -int ssl_server_init(void); - -/*! \brief Clean up SSL on shutdown */ -void ssl_server_shutdown(void); diff --git a/include/utils.h b/include/utils.h index 5ebb78fb..c69f4533 100644 --- a/include/utils.h +++ b/include/utils.h @@ -24,13 +24,6 @@ struct sockaddr_in; #include "include/string.h" #include "include/thread.h" -#define HAVE_OPENSSL - -/* Forward declaration of SSL* needed */ -#ifdef HAVE_OPENSSL -#include -#endif - /* Note: Most actual I/O functions are in socket.c but declared in node.h */ #include "include/socket.h" #include "include/tcp.h" diff --git a/include/version.h b/include/version.h index c25f296c..19e9afaa 100644 --- a/include/version.h +++ b/include/version.h @@ -15,4 +15,4 @@ #define BBS_MAJOR_VERSION 0 #define BBS_MINOR_VERSION 6 -#define BBS_PATCH_VERSION 1 +#define BBS_PATCH_VERSION 2 diff --git a/io/Makefile b/io/Makefile new file mode 100644 index 00000000..eddb7c7e --- /dev/null +++ b/io/Makefile @@ -0,0 +1,30 @@ + +MOD_SRC := $(wildcard *.c) +MOD_SO := $(MOD_SRC:.c=.so) +DEPENDS := $(patsubst %.c,%.d,$(MOD_SRC)) + +# the include directory is in the parent +INC = -I.. + +all: $(MOD_SO) + +-include $(DEPENDS) + +$(DEPENDS): + +%.o : %.c %.d + @echo " [CC] $< -> $@" + $(CC) $(CFLAGS) -fPIC -DBBS_MODULE=\"$(basename $<)\" -DBBS_MODULE_SELF_SYM=__internal_$(basename $<)_self -MMD -MP $(INC) -c $< + +%.so : %.o + @echo " [LD] $^ -> $@" + $(CC) -shared -fPIC -o $(basename $^).so $^ + +io_tls.so : io_tls.o + @echo " [LD] $^ -> $@" + $(CC) -shared -fPIC -o $(basename $^).so $^ -lssl -lcrypto + +# Don't automatically remove intermediate .o files, to prevent unnecessary recompilations +.SECONDARY: $(patsubst %.c,%.o,$(MOD_SRC)) + +.PHONY: all diff --git a/bbs/tls.c b/io/io_tls.c similarity index 92% rename from bbs/tls.c rename to io/io_tls.c index 2aa3ba1a..077dcb07 100644 --- a/bbs/tls.c +++ b/io/io_tls.c @@ -1,7 +1,7 @@ /* * LBBS -- The Lightweight Bulletin Board System * - * Copyright (C) 2023, Naveen Albert + * Copyright (C) 2023-2024, Naveen Albert * * Naveen Albert * @@ -22,12 +22,13 @@ #include #include -#include "include/tls.h" - -#ifdef HAVE_OPENSSL #include -#endif +#include +#include +#include + +#include "include/module.h" #include "include/node.h" #include "include/linkedlists.h" #include "include/config.h" @@ -37,18 +38,15 @@ #include "include/cli.h" #include "include/reload.h" -#ifdef HAVE_OPENSSL static char root_certs[84] = "/etc/ssl/certs/ca-certificates.crt"; static char ssl_cert[256] = ""; static char ssl_key[256] = ""; -SSL_CTX *ssl_ctx = NULL; -#endif +static SSL_CTX *ssl_ctx = NULL; static int ssl_is_available = 0; static int ssl_shutting_down = 0; -#ifdef HAVE_OPENSSL static bbs_mutex_t *lock_cs = NULL; static long *lock_count = NULL; @@ -137,12 +135,10 @@ static void sni_free(struct sni *sni) SSL_CTX_free(sni->ctx); free(sni); } -#endif /* HAVE_OPENSSL */ /*! \todo is there an OpenSSL function for this? */ -const char *ssl_strerror(int err) +static const char *ssl_strerror(int err) { -#ifdef HAVE_OPENSSL switch (err) { case SSL_ERROR_NONE: return "SSL_ERROR_NONE"; @@ -165,13 +161,9 @@ const char *ssl_strerror(int err) default: break; } -#else - UNUSED(err); -#endif /* HAVE_OPENSSL */ return "Undefined"; } -#ifdef HAVE_OPENSSL struct ssl_fd { SSL *ssl; int fd; @@ -657,26 +649,11 @@ static int ssl_servername_cb(SSL *s, int *ad, void *arg) } return SSL_TLSEXT_ERR_OK; } -#endif /* HAVE_OPENSSL */ - -SSL *ssl_node_new_accept(struct bbs_node *node, int *rfd, int *wfd) -{ - int my_rfd = -1, my_wfd = -1; - SSL *ssl = ssl_new_accept(node, node->fd, &my_rfd, &my_wfd); - if (my_rfd >= 0) { - *rfd = my_rfd; - } - if (my_wfd >= 0) { - *wfd = my_wfd; - } - return ssl; -} static bbs_rwlock_t ssl_cert_lock = BBS_RWLOCK_INITIALIZER; -SSL *ssl_new_accept(struct bbs_node *node, int fd, int *rfd, int *wfd) +static SSL *ssl_new_accept(int fd, int *rfd, int *wfd) { -#ifdef HAVE_OPENSSL int res; int readfd, writefd; int attempts = 0; @@ -720,10 +697,6 @@ SSL *ssl_new_accept(struct bbs_node *node, int fd, int *rfd, int *wfd) bbs_rwlock_unlock(&ssl_cert_lock); bbs_debug(1, "SSL error %d: %d (%s = %s)\n", res, sslerr, ssl_strerror(sslerr), ERR_error_string(ERR_get_error(), NULL)); SSL_free(ssl); - /* If TLS setup fails, it's probably garbage traffic and safe to penalize: */ - if (node) { - bbs_event_dispatch(node, EVENT_NODE_ENCRYPTION_FAILED); - } return NULL; } bbs_rwlock_unlock(&ssl_cert_lock); @@ -745,19 +718,12 @@ SSL *ssl_new_accept(struct bbs_node *node, int fd, int *rfd, int *wfd) bbs_debug(5, "SSL session was reused for this connection\n"); } - bbs_debug(3, "TLS handshake completed (%s)\n", SSL_get_version(ssl)); + bbs_debug(3, "TLS handshake completed %p (%s)\n", ssl, SSL_get_version(ssl)); return ssl; -#else - UNUSED(fd); - UNUSED(rfd); - UNUSED(wfd); - return NULL; -#endif /* HAVE_OPENSSL */ } -SSL *ssl_client_new(int fd, int *rfd, int *wfd, const char *snihostname) +static SSL *ssl_client_new(int fd, int *rfd, int *wfd, const char *snihostname) { -#ifdef HAVE_OPENSSL SSL *ssl; SSL_CTX *ctx; X509 *server_cert; @@ -850,7 +816,7 @@ SSL *ssl_client_new(int fd, int *rfd, int *wfd, const char *snihostname) bbs_warning("SSL verify failed: %ld (%s)\n", verify_result, X509_verify_cert_error_string(verify_result)); goto sslcleanup; } else { - bbs_debug(4, "TLS verification successful\n"); + bbs_debug(4, "TLS verification successful %p\n", ssl); } SSL_CTX_free(ctx); @@ -867,23 +833,16 @@ SSL *ssl_client_new(int fd, int *rfd, int *wfd, const char *snihostname) SSL_free(ssl); ctx = NULL; ssl = NULL; -#else - UNUSED(fd); - UNUSED(rfd); - UNUSED(wfd); - UNUSED(snihostname); -#endif /* HAVE_OPENSSL */ return NULL; } -int ssl_close(SSL *ssl) +static int ssl_close(SSL *ssl) { -#ifdef HAVE_OPENSSL int sres, res = ssl_unregister_fd(ssl); sres = SSL_shutdown(ssl); if (sres < 0) { int err = SSL_get_error(ssl, sres); - bbs_debug(1, "SSL shutdown failed: %s\n", ssl_strerror(err)); + bbs_debug(1, "SSL shutdown failed %p: %s\n", ssl, ssl_strerror(err)); } else if (!sres) { /* We sent a close notify, but haven't received one from the peer. * To be properly conformant, a client should send us a close notify, @@ -894,18 +853,8 @@ int ssl_close(SSL *ssl) } /* else, if sres == 1, shutdown completed successfully. */ SSL_free(ssl); return res; -#else - UNUSED(ssl); - return -1; -#endif /* HAVE_OPENSSL */ -} - -int ssl_available(void) -{ - return ssl_is_available; } -#ifdef HAVE_OPENSSL static int validate_cert(SSL_CTX *ctx, const char *cert) { const ASN1_TIME *created, *expires; @@ -1064,13 +1013,11 @@ static int ssl_load_config(int reload) static int tls_cleanup(void) { -#ifdef HAVE_OPENSSL if (ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } RWLIST_WRLOCK_REMOVE_ALL(&sni_certs, entry, sni_free); -#endif /* HAVE_OPENSSL */ return 0; } @@ -1134,8 +1081,14 @@ static int tlsreload(int fd) return 0; } +static int cli_tlsreload(struct bbs_cli_args *a) +{ + return tlsreload(a->fdout); +} + static struct bbs_cli_entry cli_commands_tls[] = { BBS_CLI_COMMAND(cli_tls, "tls", 1, "List all TLS sessions", NULL), + BBS_CLI_COMMAND(cli_tlsreload, "tlsreload", 1, "Reload TLS certificates and configuration", NULL), }; static int setup_ssl_io(void) @@ -1149,13 +1102,10 @@ static int setup_ssl_io(void) thread_launched = 1; return 0; } -#endif /* HAVE_OPENSSL */ -int ssl_server_init(void) +static int ssl_server_init(void) { - bbs_register_reload_handler("tls", "Reload TLS certificates and configuration", tlsreload); bbs_cli_register_multiple(cli_commands_tls); -#ifdef HAVE_OPENSSL setup_ssl_io(); /* Even if we can't be a TLS server, we can still be a TLS client. */ if (ssl_load_config(0)) { @@ -1170,21 +1120,17 @@ int ssl_server_init(void) locks_initialized = 1; ssl_is_available = 1; return 0; -#else - bbs_error("BBS compiled without OpenSSL support?\n"); - return -1; /* Won't happen */ -#endif } -void ssl_server_shutdown(void) +static void ssl_server_shutdown(void) { ssl_is_available = 0; ssl_shutting_down = 1; -#ifdef HAVE_OPENSSL tls_cleanup(); bbs_cli_unregister_multiple(cli_commands_tls); + /* Do not use pthread_cancel, let the thread clean up */ if (thread_launched) { bbs_alertpipe_write(ssl_alert_pipe); /* Tell thread to exit */ @@ -1195,5 +1141,95 @@ void ssl_server_shutdown(void) if (locks_initialized) { lock_cleanup(); } -#endif } + +/* I/O transformation callback functions */ + +static int setup(int *rfd, int *wfd, enum bbs_io_transform_dir dir, void **restrict data, const void *arg) +{ + SSL *ssl = NULL; + int fd = *rfd; + + /* For TLS, these must match since OpenSSL takes a file descriptor for setup (which is expected to be a socket, or something like that, same fd for read/write) */ + if (*rfd != *wfd) { + bbs_error("rfd != wfd (%d != %d)\n", *rfd, *wfd); + return -1; + } + + if (!ssl_is_available) { + bbs_warning("Declining TLS setup\n"); + return -1; + } + + if (dir & TRANSFORM_SERVER) { + ssl = ssl_new_accept(fd, rfd, wfd); + } else if (dir & TRANSFORM_CLIENT) { + const char *snihostname = arg; + ssl = ssl_client_new(fd, rfd, wfd, snihostname); + } + + if (!ssl) { + return -1; + } + + *data = ssl; /* Store as transform callback data */ + return 0; +} + +static void cleanup(struct bbs_io_transformation *tran) +{ + SSL *ssl = tran->data; + bbs_assert_exists(ssl); + ssl_close(ssl); +} + +static int query(struct bbs_io_transformation *tran, int query, void *data) +{ + SSL *ssl = tran->data; + int *result = data; + + switch (query) { + case TRANSFORM_QUERY_TLS_REUSE: + *result = SSL_session_reused(ssl); + break; + default: + bbs_warning("Unknown query type: %d\n", query); + return -1; + } + return 0; +} + +static int load_module(void) +{ + if (ssl_server_init()) { + ssl_server_shutdown(); + return -1; + } + if (bbs_io_transformer_register("TLS", setup, query, cleanup, TRANSFORM_TLS_ENCRYPTION, TRANSFORM_SERVER_CLIENT_TX_RX)) { + ssl_server_shutdown(); + return -1; + } + return 0; +} + +static int unload_module(void) +{ + bbs_io_transformer_unregister("TLS"); + + /* This module should be reffed for each I/O transformation using it, + * but also double check to be sure. */ + RWLIST_WRLOCK(&sslfds); + if (!RWLIST_EMPTY(&sslfds)) { + RWLIST_UNLOCK(&sslfds); + bbs_warning("TLS connections are still active, declining to unload\n"); + return -1; + } + RWLIST_UNLOCK(&sslfds); + + ssl_server_shutdown(); + return 0; +} + +/* Since most network modules will use ssl_available() to check for TLS availability + * when they load, we should be loaded before any of them are: */ +BBS_MODULE_INFO_FLAGS("TLS (Transport Layer Security)", MODFLAG_ALWAYS_PRELOAD); diff --git a/modules/mod_http.c b/modules/mod_http.c index cab2a222..971379d3 100644 --- a/modules/mod_http.c +++ b/modules/mod_http.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -36,7 +37,10 @@ #include #include -#include "include/tls.h" +#ifdef __linux__ +#include /* use PATH_MAX */ +#endif + #include "include/module.h" #include "include/node.h" #include "include/auth.h" @@ -1871,9 +1875,6 @@ static int http_handle_request(struct http_session *http, char *buf) /*! \brief Thread to handle a single HTTP/HTTPS client */ static void http_handler(struct bbs_node *node, int secure) { -#ifdef HAVE_OPENSSL - SSL *ssl = NULL; -#endif int res; char buf[MAX_HTTP_REQUEST_SIZE]; struct readline_data rldata; @@ -1887,11 +1888,8 @@ static void http_handler(struct bbs_node *node, int secure) SET_BITFIELD(http.secure, secure); /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &http.node->rfd, &http.node->wfd); - if (!ssl) { - return; /* Disconnect. */ - } + if (secure && bbs_node_starttls(node)) { + return; /* Disconnect. */ } bbs_readline_init(&rldata, buf, sizeof(buf)); @@ -1938,13 +1936,6 @@ static void http_handler(struct bbs_node *node, int secure) } http_session_cleanup(&http); } while (res >= 0 && http.req->keepalive); - -#ifdef HAVE_OPENSSL - if (ssl) { - ssl_close(ssl); - ssl = NULL; - } -#endif } /*! \brief 80 columns of spaces */ diff --git a/nets/net_ftp.c b/nets/net_ftp.c index 853f471f..40ee835a 100644 --- a/nets/net_ftp.c +++ b/nets/net_ftp.c @@ -35,7 +35,6 @@ #include "include/auth.h" #include "include/system.h" #include "include/transfer.h" -#include "include/tls.h" #include "include/event.h" static int minport, maxport; @@ -81,6 +80,7 @@ struct ftp_session { int wfd2; int protbufsize; struct bbs_node *node; + struct bbs_io_transformations data_transformers; /* For data channel encryption */ unsigned int securedata:1; unsigned int gotpbsz:1; }; @@ -122,14 +122,13 @@ static int ftp_pasv_new(int *sockfd) int __data_res = 0; \ ftp->rfd2 = ftp->wfd2 = pasv_fd; \ if (ftp->securedata) { \ + int reused; \ bbs_debug(3, "Setting up TLS on data channel\n"); \ - ssl2 = ssl_new_accept(ftp->node, pasv_fd, &ftp->rfd2, &ftp->wfd2); \ - if (!ssl2) { \ + if (bbs_io_transform_setup(&ftp->data_transformers, TRANSFORM_TLS_ENCRYPTION, TRANSFORM_SERVER, &ftp->rfd2, &ftp->wfd2, NULL)) { \ __data_res = -1; \ - } else if (require_reuse && !SSL_session_reused(ssl2)) { \ + } else if (require_reuse && (bbs_io_transform_query(&ftp->data_transformers, TRANSFORM_TLS_ENCRYPTION, TRANSFORM_QUERY_TLS_REUSE, &reused) || !reused)) { \ bbs_warning("TLS session was not reused\n"); \ - ssl_close(ssl2); \ - ssl2 = NULL; \ + bbs_io_teardown_all_transformers(&ftp->data_transformers); \ ftp->rfd2 = ftp->wfd2 = -1; \ __data_res = -1; \ } \ @@ -141,19 +140,13 @@ static int ftp_pasv_new(int *sockfd) if (fp) { \ fclose(fp); \ } \ - if (ssl2) { \ - ssl_close(ssl2); \ - ssl2 = NULL; \ - } \ + bbs_io_teardown_all_transformers(&ftp->data_transformers); \ close_if(fd); /* Close connection when done. This is the EOF that signals the client that the file transfer has completed. */ \ ftp->rfd2 = ftp->wfd2 = -1; #define DATA_DONE_FD(fp, datafd) \ close_if(datafd); \ - if (ssl2) { \ - ssl_close(ssl2); \ - ssl2 = NULL; \ - } \ + bbs_io_teardown_all_transformers(&ftp->data_transformers); \ close_if(fd); /* Close connection when done. This is the EOF that signals the client that the file transfer has completed. */ \ ftp->rfd2 = ftp->wfd2 = -1; @@ -166,7 +159,6 @@ static ssize_t ftp_put(struct ftp_session *ftp, int *pasvfdptr, const char *full struct bbs_file_transfer_event event; FILE *fp; size_t x = 0, bytes = 0; - SSL *ssl2 = NULL; int pasv_fd = *pasvfdptr; size_t maxuploadsize = bbs_transfer_max_upload_size(); @@ -286,7 +278,6 @@ static void *ftp_handler(void *varg) char type = 'A'; /* Default is ASCII */ struct readline_data rldata; struct ftp_session ftpstack, *ftp; - SSL *ssl = NULL, *ssl2 = NULL; bbs_node_net_begin(node); @@ -299,11 +290,8 @@ static void *ftp_handler(void *varg) ftp->node = node; /* Start TLS if we need to */ - if (!strcmp(node->protname, "FTPS")) { - ssl = ssl_node_new_accept(node, &ftp->node->rfd, &ftp->node->wfd); - if (!ssl) { - goto cleanup; - } + if (!strcmp(node->protname, "FTPS") && bbs_node_starttls(node)) { + goto cleanup; } /* FTP uses CR LF line endings (but that's what you expected, right?) */ @@ -379,7 +367,7 @@ static void *ftp_handler(void *varg) } else if (!strcasecmp(command, "ACCT")) { res = ftp_write(ftp, 202, "Command Not Implemented, Superflous\r\n"); /* Not needed */ } else if (!strcasecmp(command, "REIN")) { /* Reinitialize */ - if (ssl) { + if (node->secure) { break; /* Can't go back to unencrypted, just disconnect. */ } if (node->user) { @@ -402,10 +390,9 @@ static void *ftp_handler(void *varg) res = ftp_write(ftp, 211, "END\r\n"); } else if (!strcasecmp(command, "AUTH") && !strlen_zero(rest) && !strcasecmp(rest, "TLS")) { /* AUTH TLS / AUTH SSL = RFC2228 opportunistic encryption */ - if (!ssl && ssl_available()) { + if (ssl_available()) { res = ftp_write(ftp, 234, "Begin TLS negotiation\r\n"); - ssl = ssl_node_new_accept(node, &ftp->node->rfd, &ftp->node->wfd); - if (!ssl) { + if (bbs_node_starttls(node)) { break; /* Just abort */ } /* Must reauthorize */ @@ -416,7 +403,7 @@ static void *ftp_handler(void *varg) res = ftp_write(ftp, 502, "Command Not Implemented\r\n"); } } else if (!strcasecmp(command, "CCC")) { /* Clear Control Channel */ - if (ssl) { + if (node->secure) { /* We don't support reverting back from encrypted to unencrypted */ res = ftp_write(ftp, 534, "Connection may not be downgraded\r\n"); } else { @@ -456,7 +443,7 @@ static void *ftp_handler(void *varg) res = ftp_write(ftp, 501, "Argument not valid\r\n"); continue; } - if (!ssl) { + if (!node->secure) { res = ftp_write(ftp, 503, "Security data exchange not completed\r\n"); continue; } @@ -478,6 +465,10 @@ static void *ftp_handler(void *varg) res = ftp_write(ftp, 200, "Data will not be encrypted\r\n"); break; case 'P': /* Private */ + if (!ssl_available()) { + res = ftp_write(ftp, 503, "Secure data channel unavailable\r\n"); + break; + } ftp->securedata = 1; res = ftp_write(ftp, 200, "Data will be encrypted\r\n"); break; @@ -496,10 +487,7 @@ static void *ftp_handler(void *varg) } else if (!strcasecmp(command, "PASV") || !strcasecmp(command, "EPSV")) { /* Passive Mode */ int tmpfd; close_if(pasv_fd); /* In case there was an existing data channel open (but there shouldn't be...) */ - if (ssl2) { - ssl_close(ssl2); - ssl2 = NULL; - } + bbs_io_teardown_all_transformers(&ftp->data_transformers); pasv_port = ftp_pasv_new(&pasv_fd); if (pasv_port < 0) { res = ftp_write(ftp, 425, "Failed to enter passive mode, closing control connection\r\n"); @@ -858,14 +846,7 @@ static void *ftp_handler(void *varg) cleanup: close_if(pasv_fd); - if (ssl2) { - ssl_close(ssl2); - ssl2 = NULL; - } - if (ssl) { - ssl_close(ssl); - ssl = NULL; - } + bbs_io_teardown_all_transformers(&ftp->data_transformers); bbs_node_exit(node); return NULL; } diff --git a/nets/net_http.c b/nets/net_http.c index b93d794d..27fc2120 100644 --- a/nets/net_http.c +++ b/nets/net_http.c @@ -24,12 +24,15 @@ #include "include/bbs.h" +#ifdef __linux__ +#include /* use PATH_MAX */ +#endif + #include "include/module.h" #include "include/config.h" #include "include/node.h" #include "include/user.h" #include "include/utils.h" -#include "include/tls.h" #include "include/transfer.h" /* Needed for mod_http.h */ diff --git a/nets/net_imap.c b/nets/net_imap.c index dd140307..22dee150 100644 --- a/nets/net_imap.c +++ b/nets/net_imap.c @@ -127,8 +127,6 @@ #include #include -#include "include/tls.h" - #include "include/module.h" #include "include/config.h" #include "include/node.h" @@ -4650,17 +4648,11 @@ static void handle_client(struct imap_session *imap) /*! \brief Thread to handle a single IMAP/IMAPS client */ static void imap_handler(struct bbs_node *node, int secure) { -#ifdef HAVE_OPENSSL - SSL *ssl; -#endif struct imap_session imap, *s; /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { - return; - } + if (secure && bbs_node_starttls(node)) { + return; } memset(&imap, 0, sizeof(imap)); @@ -4695,12 +4687,6 @@ static void imap_handler(struct bbs_node *node, int secure) /* imap is stack allocated, don't free it */ cleanup: -#ifdef HAVE_OPENSSL - if (secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif imap_destroy(&imap); bbs_mutex_destroy(&imap.lock); } diff --git a/nets/net_imap/imap_client.c b/nets/net_imap/imap_client.c index 9382581c..cfee9c9e 100644 --- a/nets/net_imap/imap_client.c +++ b/nets/net_imap/imap_client.c @@ -19,6 +19,7 @@ #include "include/bbs.h" +#include #include #include "include/node.h" diff --git a/nets/net_imap/imap_server_list.c b/nets/net_imap/imap_server_list.c index 09e10537..67848dcd 100644 --- a/nets/net_imap/imap_server_list.c +++ b/nets/net_imap/imap_server_list.c @@ -475,6 +475,8 @@ int list_scandir(struct imap_session *imap, struct list_command *lcmd, int level if (level == 0 && lcmd->ns == NAMESPACE_OTHER && isdigit(*entry->d_name)) { unsigned int userid = (unsigned int) atoi(entry->d_name); + bbs_assert_exists(imap->node); + bbs_assert_exists(imap->node->user); if (userid == imap->node->user->id) { goto cleanup; /* Skip ourself */ } diff --git a/nets/net_imap/imap_server_search.c b/nets/net_imap/imap_server_search.c index c48fe9d4..c6b7b095 100644 --- a/nets/net_imap/imap_server_search.c +++ b/nets/net_imap/imap_server_search.c @@ -21,6 +21,7 @@ #include #include +#include #include "include/node.h" #include "include/test.h" diff --git a/nets/net_irc.c b/nets/net_irc.c index 475d1769..09c4adfa 100644 --- a/nets/net_irc.c +++ b/nets/net_irc.c @@ -21,11 +21,14 @@ #include #include +#include #include #include #include -#include "include/tls.h" +#ifdef __linux__ +#include /* use PATH_MAX */ +#endif #include "include/module.h" #include "include/config.h" @@ -3811,9 +3814,6 @@ static struct bbs_cli_entry cli_commands_irc[] = { /*! \brief Thread to handle a single IRC/IRCS client */ static void irc_handler(struct bbs_node *node, int secure) { -#ifdef HAVE_OPENSSL - SSL *ssl; -#endif struct irc_user *user; if (need_restart) { @@ -3832,12 +3832,9 @@ static void irc_handler(struct bbs_node *node, int secure) bbs_mutex_init(&user->lock, NULL); /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { - free(user); - return; - } + if (secure && bbs_node_starttls(node)) { + free(user); + return; } user->node = node; @@ -3850,13 +3847,6 @@ static void irc_handler(struct bbs_node *node, int secure) } handle_client(user); - -#ifdef HAVE_OPENSSL - if (secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif user_free(user); } diff --git a/nets/net_nntp.c b/nets/net_nntp.c index c637033c..3da2e27f 100644 --- a/nets/net_nntp.c +++ b/nets/net_nntp.c @@ -28,8 +28,7 @@ #include #include #include - -#include "include/tls.h" +#include #include "include/stringlist.h" #include "include/module.h" @@ -107,7 +106,6 @@ struct nntp_session { unsigned int inpost:1; unsigned int inpostheaders:1; unsigned int postfail:1; - unsigned int secure:1; unsigned int dostarttls:1; }; @@ -837,7 +835,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) nntp_send(nntp, 101, "Capability list:"); _nntp_send(nntp, "VERSION 2\r\n"); /* Must be first */ /* Don't advertise MODE-READER, just READER */ - if (!nntp->secure) { + if (!nntp->node->secure) { _nntp_send(nntp, "STARTTLS\r\n"); } if (nntp->mode == NNTP_MODE_READER) { @@ -849,7 +847,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) _nntp_send(nntp, "MODE-READER\r\n"); } _nntp_send(nntp, "XSECRET\r\n"); - if ((nntp->secure || !require_secure_login) && !bbs_user_is_registered(nntp->node->user)) { + if ((nntp->node->secure || !require_secure_login) && !bbs_user_is_registered(nntp->node->user)) { _nntp_send(nntp, "AUTHINFO USER\r\n"); _nntp_send(nntp, "SASL PLAIN\r\n"); } @@ -858,7 +856,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) } else if (!strcasecmp(command, "STARTTLS")) { if (!ssl_available()) { nntp_send(nntp, 580, "STARTTLS may not be used"); - } else if (!nntp->secure) { + } else if (!nntp->node->secure) { nntp_send(nntp, 382, "Ready to start TLS"); nntp->dostarttls = 1; } else { @@ -928,7 +926,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) return 0; } nntp_send(nntp, 290, "Password for %s accepted", user); /*! \todo Is this really the right response code? */ - } else if ((nntp->secure || !require_secure_login) && !strcasecmp(command, "AUTHINFO")) { + } else if ((nntp->node->secure || !require_secure_login) && !strcasecmp(command, "AUTHINFO")) { /* RFC 4643 AUTHINFO */ /* If this command is not implemented and we send a 480, * Thunderbirds will just go into a loop sending AUTH INFO commands, forever, @@ -1166,7 +1164,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) } } else if (nntp->mode == NNTP_MODE_TRANSIT && !strcasecmp(command, "IHAVE")) { /* Check if client is authorized to relay us articles. */ - if (requirerelaytls && !nntp->secure) { + if (requirerelaytls && !nntp->node->secure) { nntp_send(nntp, 483, "Secure connection required"); return 0; } @@ -1214,7 +1212,7 @@ static int nntp_process(struct nntp_session *nntp, char *s, size_t len) return 0; } -static void handle_client(struct nntp_session *nntp, SSL **sslptr) +static void handle_client(struct nntp_session *nntp) { char buf[1001]; struct readline_data rldata; @@ -1246,11 +1244,9 @@ static void handle_client(struct nntp_session *nntp, SSL **sslptr) /* RFC 4642 */ bbs_debug(3, "Starting TLS\n"); nntp->dostarttls = 0; - *sslptr = ssl_node_new_accept(nntp->node, &nntp->node->rfd, &nntp->node->wfd); - if (!*sslptr) { + if (bbs_node_starttls(nntp->node)) { break; /* Just abort */ } - nntp->secure = 1; free_if(nntp->currentgroup); nntp->currentarticle = 0; bbs_readline_flush(&rldata); /* Prevent STARTTLS command injection by resetting the buffer after TLS upgrade */ @@ -1261,32 +1257,19 @@ static void handle_client(struct nntp_session *nntp, SSL **sslptr) /*! \brief Thread to handle a single NNTP/NNTPS client */ static void nntp_handler(struct bbs_node *node, int secure, int reader) { -#ifdef HAVE_OPENSSL - SSL *ssl = NULL; -#endif struct nntp_session nntp; /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { - return; - } + if (secure && bbs_node_starttls(node)) { + return; } memset(&nntp, 0, sizeof(nntp)); nntp.node = node; - SET_BITFIELD(nntp.secure, secure); SET_BITFIELD(nntp.mode, reader); - handle_client(&nntp, &ssl); + handle_client(&nntp); -#ifdef HAVE_OPENSSL - if (nntp.secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif nntp_destroy(&nntp); } diff --git a/nets/net_pop3.c b/nets/net_pop3.c index 6957f03a..2b16c67b 100644 --- a/nets/net_pop3.c +++ b/nets/net_pop3.c @@ -29,8 +29,6 @@ #include #include -#include "include/tls.h" - #include "include/module.h" #include "include/config.h" #include "include/utils.h" @@ -744,17 +742,11 @@ static void handle_client(struct pop3_session *pop3) /*! \brief Thread to handle a single POP3/POP3S client */ static void pop3_handler(struct bbs_node *node, int secure) { -#ifdef HAVE_OPENSSL - SSL *ssl = NULL; -#endif struct pop3_session pop3; /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { - return; - } + if (secure && bbs_node_starttls(node)) { + return; } memset(&pop3, 0, sizeof(pop3)); @@ -763,12 +755,6 @@ static void pop3_handler(struct bbs_node *node, int secure) handle_client(&pop3); mailbox_dispatch_event_basic(EVENT_LOGOUT, node, NULL, NULL); -#ifdef HAVE_OPENSSL - if (secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif pop3_destroy(&pop3); } diff --git a/nets/net_sieve.c b/nets/net_sieve.c index 1b771bbd..1b2bd40a 100644 --- a/nets/net_sieve.c +++ b/nets/net_sieve.c @@ -24,8 +24,6 @@ #include #include -#include "include/tls.h" - #include "include/module.h" #include "include/config.h" #include "include/utils.h" @@ -52,7 +50,6 @@ struct sieve_session { long unsigned int quotaleft; unsigned int uploadsofar; unsigned int uploadexpected; - unsigned int secure:1; unsigned int dostarttls:1; }; @@ -79,7 +76,7 @@ static int handle_capability(struct sieve_session *sieve) sieve_send(sieve, "\"SIEVE\" \"%s\"", caps); free(caps); sieve_send(sieve, "\"VERSION\" \"1.0\""); - if (!sieve->secure) { + if (!sieve->node->secure) { sieve_send(sieve, "\"STARTTLS\""); } sieve_send(sieve, "\"SASL\" \"PLAIN\""); @@ -434,7 +431,7 @@ static int sieve_process(struct sieve_session *sieve, char *s) return 0; } -static void handle_client(struct sieve_session *sieve, SSL **sslptr) +static void handle_client(struct sieve_session *sieve) { char buf[1001]; /* Maximum length, including CR LF, is 1000 */ struct readline_data rldata; @@ -475,12 +472,10 @@ static void handle_client(struct sieve_session *sieve, SSL **sslptr) if (sieve->dostarttls) { /* RFC3207 STARTTLS */ bbs_debug(3, "Starting TLS\n"); sieve->dostarttls = 0; - *sslptr = ssl_node_new_accept(sieve->node, &sieve->rfd, &sieve->wfd); - if (!*sslptr) { + if (bbs_node_starttls(sieve->node)) { break; /* Just abort */ } bbs_readline_flush(&rldata); /* Prevent STARTTLS command injection by resetting the buffer after TLS upgrade */ - sieve->secure = 1; if (handle_capability(sieve)) { /* Must reissue after STARTTLS */ return; } @@ -491,9 +486,6 @@ static void handle_client(struct sieve_session *sieve, SSL **sslptr) static void *__sieve_handler(void *varg) { struct bbs_node *node = varg; -#ifdef HAVE_OPENSSL - SSL *ssl = NULL; -#endif struct sieve_session sieve; bbs_node_net_begin(node); @@ -501,17 +493,9 @@ static void *__sieve_handler(void *varg) memset(&sieve, 0, sizeof(sieve)); sieve.rfd = sieve.wfd = node->fd; sieve.node = node; - sieve.secure = 0; - handle_client(&sieve, &ssl); + handle_client(&sieve); -#ifdef HAVE_OPENSSL - /* Note that due to STARTTLS, sieve.secure might not always equal secure at this point (session could start off insecure and end up secure) */ - if (sieve.secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif sieve_cleanup(&sieve); bbs_node_exit(node); diff --git a/nets/net_smtp.c b/nets/net_smtp.c index 714088f8..32810dd9 100644 --- a/nets/net_smtp.c +++ b/nets/net_smtp.c @@ -40,6 +40,7 @@ #include "include/bbs.h" #include +#include #include #include #include @@ -48,8 +49,6 @@ #include /* for msg_to_filename */ -#include "include/tls.h" - #include "include/module.h" #include "include/config.h" #include "include/utils.h" @@ -204,7 +203,6 @@ struct smtp_session { unsigned int ehlo:1; /* Client supports ESMTP (EHLO) */ unsigned int fromlocal:1; /* Sender is local */ unsigned int msa:1; /* Whether connection was to the Message Submission Agent port (as opposed to the Mail Transfer Agent port) */ - unsigned int secure:1; /* Whether session is secure (TLS, STARTTLS) */ }; static void smtp_reset(struct smtp_session *smtp) @@ -595,7 +593,7 @@ static int handle_helo(struct smtp_session *smtp, char *s, int ehlo) smtp_reply0_nostatus(smtp, 250, "%s at your service [%s]", bbs_hostname(), smtp->node->ip); /* The RFC says that login should only be allowed on secure connections, * but if we don't allow login on plaintext connections, then they're functionally useless. */ - if (smtp->secure || !require_starttls || exempt_from_starttls(smtp)) { + if (smtp->node->secure || !require_starttls || exempt_from_starttls(smtp)) { smtp_reply0_nostatus(smtp, 250, "AUTH LOGIN PLAIN"); /* RFC-complaint way */ smtp_reply0_nostatus(smtp, 250, "AUTH=LOGIN PLAIN"); /* For non-compliant user agents, e.g. Outlook 2003 and older */ } @@ -603,7 +601,7 @@ static int handle_helo(struct smtp_session *smtp, char *s, int ehlo) smtp_reply0_nostatus(smtp, 250, "SIZE %u", max_message_size); /* RFC 1870 */ smtp_reply0_nostatus(smtp, 250, "8BITMIME"); /* RFC 6152 */ smtp_reply0_nostatus(smtp, 250, "ETRN"); /* RFC 1985 */ - if (!smtp->secure && ssl_available()) { + if (!smtp->node->secure && ssl_available()) { smtp_reply0_nostatus(smtp, 250, "STARTTLS"); } if (bbs_user_is_registered(smtp->node->user)) { @@ -1198,7 +1196,7 @@ const char *smtp_protname(struct smtp_session *smtp) { /* RFC 2822, RFC 3848, RFC 2033 */ if (smtp->ehlo) { - if (smtp->secure) { + if (smtp->node->secure) { if (bbs_user_is_registered(smtp->node->user)) { return "ESMTPSA"; } else { @@ -2765,14 +2763,14 @@ static int smtp_process(struct smtp_session *smtp, char *s, struct readline_data } else if (!strcasecmp(command, "EHLO")) { return handle_helo(smtp, s, 1); } else if (!strcasecmp(command, "STARTTLS")) { - if (!smtp->secure) { + if (!smtp->node->secure) { smtp_reply_nostatus(smtp, 220, "Ready to start TLS"); smtp->tflags.dostarttls = 1; smtp->gothelo = 0; /* Client will need to start over. */ } else { smtp_reply(smtp, 454, 5.5.1, "STARTTLS may not be repeated"); } - } else if (smtp->msa && !smtp->secure && require_starttls && !exempt_from_starttls(smtp)) { + } else if (smtp->msa && !smtp->node->secure && require_starttls && !exempt_from_starttls(smtp)) { smtp_reply(smtp, 504, 5.5.4, "Must issue a STARTTLS command first"); } else if (!strcasecmp(command, "AUTH")) { /* https://www.samlogic.net/articles/smtp-commands-reference-auth.htm */ @@ -2780,7 +2778,7 @@ static int smtp_process(struct smtp_session *smtp, char *s, struct readline_data smtp_reply(smtp, 503, 5.5.1, "Bad sequence of commands."); } else if (bbs_user_is_registered(smtp->node->user)) { /* Already authed */ smtp_reply(smtp, 503, 5.7.0, "Already authenticated, no identity changes permitted"); - } else if (!smtp->secure && require_starttls && !exempt_from_starttls(smtp)) { + } else if (!smtp->node->secure && require_starttls && !exempt_from_starttls(smtp)) { /* Must not offer PLAIN or LOGIN on insecure connections. */ smtp_reply(smtp, 504, 5.5.4, "Must issue a STARTTLS command first"); } else { @@ -2835,7 +2833,7 @@ static int smtp_process(struct smtp_session *smtp, char *s, struct readline_data return 0; } -static void handle_client(struct smtp_session *smtp, SSL **sslptr) +static void handle_client(struct smtp_session *smtp) { char buf[1001]; /* Maximum length, including CR LF, is 1000 */ struct readline_data rldata; @@ -2875,11 +2873,10 @@ static void handle_client(struct smtp_session *smtp, SSL **sslptr) /* You might think this would be more complicated, but nope, this is literally all there is to it. */ bbs_debug(3, "Starting TLS\n"); smtp->tflags.dostarttls = 0; - *sslptr = ssl_node_new_accept(smtp->node, &smtp->node->rfd, &smtp->node->wfd); - if (!*sslptr) { + if (bbs_node_starttls(smtp->node)) { break; /* Just abort */ } - smtp->secure = 1; + smtp->node->secure = 1; /* Prevent STARTTLS command injection by resetting the buffer after TLS upgrade: * http://www.postfix.org/CVE-2011-0411.html * https://blog.apnic.net/2021/11/18/vulnerabilities-show-why-starttls-should-be-avoided-if-possible/ @@ -2892,37 +2889,23 @@ static void handle_client(struct smtp_session *smtp, SSL **sslptr) /*! \brief Thread to handle a single SMTP/SMTPS client */ static void smtp_handler(struct bbs_node *node, int msa, int secure) { -#ifdef HAVE_OPENSSL - SSL *ssl = NULL; -#endif struct smtp_session smtp; /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { - return; - } + if (secure && bbs_node_starttls(node)) { + return; } memset(&smtp, 0, sizeof(smtp)); smtp.node = node; - SET_BITFIELD(smtp.secure, secure); SET_BITFIELD(smtp.msa, msa); stringlist_init(&smtp.recipients); stringlist_init(&smtp.sentrecipients); - handle_client(&smtp, &ssl); + handle_client(&smtp); mailbox_dispatch_event_basic(EVENT_LOGOUT, node, NULL, NULL); -#ifdef HAVE_OPENSSL - /* Note that due to STARTTLS, smtp.secure might not always equal secure at this point (session could start off insecure and end up secure) */ - if (smtp.secure) { /* implies ssl */ - ssl_close(ssl); - ssl = NULL; - } -#endif smtp_destroy(&smtp); } diff --git a/nets/net_telnet.c b/nets/net_telnet.c index be981b5c..de0cdfc2 100644 --- a/nets/net_telnet.c +++ b/nets/net_telnet.c @@ -45,7 +45,6 @@ #include "include/utils.h" #include "include/config.h" #include "include/net.h" -#include "include/tls.h" static int telnet_socket = -1, telnets_socket = -1, tty_socket = -1, ttys_socket = -1; /*!< TCP Socket for allowing incoming network connections */ static pthread_t telnet_thread, telnets_thread, tty_thread; @@ -381,7 +380,6 @@ static void *telnet_listener(void *unused) static void *telnets_handler(void *varg) { struct bbs_node *node = varg; - SSL *ssl = NULL; /* Yikes... a TELNETS client needs 3 threads: * the network thread, the PTY thread, @@ -390,8 +388,7 @@ static void *telnets_handler(void *varg) * as another intermediary layer that looks for 255 bytes, * and add yet a fourth thread! */ /* Set up TLS, then do the handshake, then proceed as normal. */ - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { + if (bbs_node_starttls(node)) { bbs_node_exit(node); /* Since we're not calling bbs_node_handler, we're responsible for manually cleaning the node up */ return NULL; } @@ -402,21 +399,18 @@ static void *telnets_handler(void *varg) bbs_node_exit(node); /* Manual cleanup */ } - ssl_close(ssl); return NULL; } static void *tty_handler(void *varg) { struct bbs_node *node = varg; - SSL *ssl = NULL; if (!strcmp(node->protname, "TDDS")) { /* Use TDD for both secure and plaintext TDD. Used in NODE_IS_TDD macro. */ node->protname = "TDD"; bbs_debug(5, "Connection accepted on secure TTY port\n"); - ssl = ssl_node_new_accept(node, &node->rfd, &node->wfd); - if (!ssl) { + if (bbs_node_starttls(node)) { bbs_node_exit(node); return NULL; } @@ -425,9 +419,6 @@ static void *tty_handler(void *varg) tty_handshake(node); bbs_node_handler(node); /* Run the normal node handler */ - if (ssl) { - ssl_close(ssl); - } return NULL; } diff --git a/nets/net_ws.c b/nets/net_ws.c index 48e0e075..3483b48f 100644 --- a/nets/net_ws.c +++ b/nets/net_ws.c @@ -23,12 +23,15 @@ #include /* use isdigit */ #include +#ifdef __linux__ +#include /* use PATH_MAX */ +#endif + #include "include/module.h" #include "include/config.h" #include "include/node.h" #include "include/user.h" #include "include/utils.h" -#include "include/tls.h" #include "include/test.h" #include "include/cli.h" @@ -1120,7 +1123,6 @@ static void ws_handler(struct bbs_node *node, struct http_session *http, int pro static void ws_direct_handler(struct bbs_node *node, int secure) { - SSL *ssl = NULL; int res; /* needed for HTTP structure */ @@ -1136,11 +1138,8 @@ static void ws_direct_handler(struct bbs_node *node, int secure) SET_BITFIELD(http.secure, secure); /* Start TLS if we need to */ - if (secure) { - ssl = ssl_node_new_accept(node, &http.node->rfd, &http.node->wfd); - if (!ssl) { - return; /* Disconnect. */ - } + if (secure && bbs_node_starttls(node)) { + return; /* Disconnect. */ } /* If this is a direct connection, either the client connected directly to us, @@ -1153,26 +1152,20 @@ static void ws_direct_handler(struct bbs_node *node, int secure) bbs_readline_init(&rldata, buf, sizeof(buf)); res = http_parse_request(&http, buf); /* This will, among other things, load any cookies in the request, so we can identify the client. */ if (res) { - goto cleanup; /* Just disconnect, don't even bother sending a response */ + return; /* Just disconnect, don't even bother sending a response */ } bbs_debug(4, "Ready to begin WebSocket handshake\n"); if (!http_websocket_upgrade_requested(&http)) { bbs_debug(3, "Not a WebSocket client?\n"); /* Probably just rando TCP traffic hitting this port. Drop it. */ - goto cleanup; + return; } else if (http_websocket_handshake(&http)) { - goto cleanup; /* WebSocket handshake failed */ + return; /* WebSocket handshake failed */ } /* Handshake succeeded! Okay, we're done with the HTTP stuff now. It's just websockets from here on out. */ ws_handler(node, &http, 1); /* Seems backwards, but this is a reverse proxied connection, most likely */ http_session_cleanup(&http); - -cleanup: - if (ssl) { - ssl_close(ssl); - ssl = NULL; - } } static enum http_response_code ws_proxy_handler(struct http_session *http) diff --git a/scripts/install_prereq.sh b/scripts/install_prereq.sh index 55bd2940..1f33b5b2 100755 --- a/scripts/install_prereq.sh +++ b/scripts/install_prereq.sh @@ -13,10 +13,6 @@ PACKAGES_FREEBSD="git gcc gmake" # used by libopenarc, libetpan PACKAGES_DEBIAN="$PACKAGES_DEBIAN make automake pkg-config libtool m4" -# OpenSSL -PACKAGES_DEBIAN="$PACKAGES_DEBIAN libssl-dev" -PACKAGES_FEDORA="$PACKAGES_FEDORA openssl-devel" - PACKAGES_DEBIAN="$PACKAGES_DEBIAN libncurses-dev" # ncurses PACKAGES_DEBIAN="$PACKAGES_DEBIAN ncurses-base ncurses-term" # full/extended terminal definitions @@ -48,6 +44,10 @@ PACKAGES_FEDORA="$PACKAGES_FEDORA lrzsz" PACKAGES_DEBIAN="$PACKAGES_DEBIAN libedit-dev libreadline-dev" PACKAGES_FEDORA="$PACKAGES_FEDORA libedit-devel readline-devel" +# hash.c, io_tls: OpenSSL +PACKAGES_DEBIAN="$PACKAGES_DEBIAN libssl-dev" +PACKAGES_FEDORA="$PACKAGES_FEDORA openssl-devel" + # libssh (net_ssh) PACKAGES_DEBIAN="$PACKAGES_DEBIAN libssh-dev" # net_ssh, which requires objdump to test for symbol existence... thanks a lot, libssh