[git commit] ssl_server: new applet, not functional yet
Denys Vlasenko
vda.linux at googlemail.com
Sun Feb 15 14:16:26 UTC 2026
commit: https://git.busybox.net/busybox/commit/?id=61055ef909268eedaf5573e652269545440453f8
branch: https://git.busybox.net/busybox/log/?h=master
function old new delta
ssl_server_main - 285 +285
ssl_client_main 137 363 +226
packed_usage 36034 36072 +38
.rodata 107074 107103 +29
applet_names 2870 2881 +11
tls_handshake_as_server - 7 +7
applet_main 1652 1656 +4
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 5/0 up/down: 600/0) Total: 600 bytes
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
include/libbb.h | 10 ++++-
networking/ssl_client.c | 82 +++++++++++++++++++++++++++++++++--------
networking/ssl_server.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++
networking/tls.c | 19 ++++++++++
4 files changed, 191 insertions(+), 17 deletions(-)
diff --git a/include/libbb.h b/include/libbb.h
index baf0f29e5..ecdbd9234 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -911,6 +911,11 @@ typedef struct tls_state {
struct tls_aes aes_encrypt;
struct tls_aes aes_decrypt;
uint8_t H[16]; //used by AES_GCM
+
+#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
+ const char *privkey_in_der_format;
+ const char *cert_in_der_format;
+#endif
} tls_state_t;
static inline tls_state_t *new_tls_state(void)
@@ -918,7 +923,10 @@ static inline tls_state_t *new_tls_state(void)
tls_state_t *tls = xzalloc(sizeof(*tls));
return tls;
}
-void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC;
+void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni);
+void FAST_FUNC tls_handshake_as_server(tls_state_t *tls,
+ const char *privkey_der_filename,
+ const char *cert_der_filename);
#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0)
void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC;
diff --git a/networking/ssl_client.c b/networking/ssl_client.c
index 582fb0e71..2d923ef06 100644
--- a/networking/ssl_client.c
+++ b/networking/ssl_client.c
@@ -15,7 +15,7 @@
//kbuild:lib-$(CONFIG_SSL_CLIENT) += ssl_client.o
//usage:#define ssl_client_trivial_usage
-//usage: "[-e] -s FD [-r FD] [-n SNI]"
+//usage: "[-n SNI] { -s FD [-r FD] | HOST | -e PROG ARGS }"
//usage:#define ssl_client_full_usage ""
#include "libbb.h"
@@ -23,35 +23,85 @@
int ssl_client_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ssl_client_main(int argc UNUSED_PARAM, char **argv)
{
+ int exit_if_stdin_closed;
tls_state_t *tls;
const char *sni = NULL;
int opt;
+ enum {
+ OPT_s = (1 << 0),
+ OPT_r = (1 << 1),
+ OPT_n = (1 << 2),
+ OPT_e = (1 << 3),
+ };
// INIT_G();
-
tls = new_tls_state();
- opt = getopt32(argv, "es:+r:+n:", &tls->ofd, &tls->ifd, &sni);
- if (!(opt & (1<<2))) {
- /* -r N defaults to -s N */
- tls->ifd = tls->ofd;
- }
- if (!(opt & (3<<1))) {
- if (!argv[1])
+ /* "+": stop on first non-option */
+ opt = getopt32(argv, "^+" "s:+r:+n:e" "\0"
+ "e--s:e--r:s--e:r--e", &tls->ofd, &tls->ifd, &sni
+ );
+ argv += optind;
+
+ if (opt & OPT_e) {
+ /* -e PROG: run PROG and talk TLS to its stdin/stdout */
+ // Talk to local HTTP server behind local TLS server:
+ // printf "GET / HTTP/1.1\r\n\r\n" | ssl_client -e ssl_server -d PRIVKEY.der -e httpd -i
+ struct fd_pair to_prog;
+ struct fd_pair from_prog;
+
+ pid_t pid;
+
+ if (!argv[0])
bb_show_usage();
- /* Undocumented debug feature: without -s and -r, takes HOST arg and connects to it */
- //
+
+ xpiped_pair(to_prog);
+ xpiped_pair(from_prog);
+
+ pid = xvfork();
+ if (pid == 0) {
+ /* Child: run the program */
+
+ /* NB: close _first_, then move fds! */
+ close(to_prog.wr);
+ close(from_prog.rd);
+ xmove_fd(to_prog.rd, STDIN_FILENO);
+ xmove_fd(from_prog.wr, STDOUT_FILENO);
+
+ BB_EXECVP_or_die(argv);
+ }
+
+ /* Parent: close child ends of pipes */
+ close(to_prog.rd);
+ close(from_prog.wr);
+
+ tls->ofd = to_prog.wr; /* write to program's stdin */
+ tls->ifd = from_prog.rd; /* read from program's stdout */
+
+ } else if (!(opt & (OPT_s|OPT_r))) {
+ /* Not -e/-s/-r: connect to HOST */
// Talk to kernel.org:
- // printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | busybox ssl_client kernel.org
+ // printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ssl_client kernel.org
+ if (!argv[0] || argv[1])
+ bb_show_usage();
if (!sni)
- sni = argv[1];
- tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443);
+ sni = argv[0];
+ tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[0], 443);
+
+ } else {
+ /* -s FD [-r FD] */
+ if (!(opt & OPT_s) || argv[0])
+ bb_show_usage();
+ if (!(opt & OPT_r)) {
+ /* -r FD defaults to -s FD */
+ tls->ifd = tls->ofd;
+ }
}
tls_handshake(tls, sni);
- BUILD_BUG_ON(TLSLOOP_EXIT_ON_LOCAL_EOF != 1);
- tls_run_copy_loop(tls, /*flags*/ opt & 1);
+ exit_if_stdin_closed = (opt & OPT_s) ? TLSLOOP_EXIT_ON_LOCAL_EOF : 0;
+ tls_run_copy_loop(tls, /*flags*/ exit_if_stdin_closed);
return EXIT_SUCCESS;
}
diff --git a/networking/ssl_server.c b/networking/ssl_server.c
new file mode 100644
index 000000000..a8d6ba1c2
--- /dev/null
+++ b/networking/ssl_server.c
@@ -0,0 +1,97 @@
+/*
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//config:config SSL_SERVER
+//config: bool "ssl_server (test TLS server)"
+//config: default y
+//config: select TLS
+//config: help
+//config: inetd-style TLS server. Stdin/stdout are already connected
+//config: to an accepted TCP socket.
+
+//applet:IF_SSL_SERVER(APPLET(ssl_server, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SSL_SERVER) += ssl_server.o
+
+//usage:#define ssl_server_trivial_usage
+//usage: "[-vv] -p PRIVKEY.der -c CERT.der PROG ARGS"
+//usage:#define ssl_server_full_usage ""
+
+#include "libbb.h"
+
+/* TLS server applet.
+ *
+ * To generate a test RSA certificate and key:
+ * openssl req -x509 -newkey rsa:2048 -days 9999 -nodes \
+ * -subj '/CN=localhost' \
+ * -out cert.pem -keyout privkey.pem
+ * Convert to DER format:
+ * openssl x509 -in cert.pem -outform DER -out cert.der
+ * openssl rsa -in privkey.pem -outform DER -out privkey.der
+ *
+ * Run the server:
+ * tcpsvd 127.0.0.1 4433 ssl_server -p privkey.der -c cert.der -e echo 'Hello world'
+ *
+ * Test with:
+ * openssl s_client -connect localhost:4433
+ */
+int ssl_server_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ssl_server_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct fd_pair to_prog;
+ struct fd_pair from_prog;
+ pid_t pid;
+ tls_state_t *tls;
+ const char *privkey_in_der_format;
+ const char *cert_in_der_format;
+ unsigned opt;
+
+ tls = new_tls_state();
+
+ /* "+": stop on first non-option */
+ opt = getopt32(argv, "+""vp:c:",
+ &privkey_in_der_format, &cert_in_der_format
+ );
+ argv += optind;
+ if (!argv[0] || (opt >> 1) == 0)
+ bb_show_usage();
+
+ /* In inetd mode, stdin/stdout are the socket.
+ * But tls_run_copy_loop() needs *non-TLS* fds on STDIN and STDOUT.
+ * Shuffle them.
+ */
+ xdup2(STDIN_FILENO, 3);
+ xdup2(STDOUT_FILENO, 4);
+ tls->ifd = 3;
+ tls->ofd = 4;
+
+ /* This can abort on errors */
+ tls_handshake_as_server(tls, privkey_in_der_format, cert_in_der_format);
+
+ /* Run PROG, wrap its data in TLS and I/O to socket */
+ xpiped_pair(to_prog);
+ xpiped_pair(from_prog);
+ pid = xvfork();
+ if (pid == 0) {
+ /* Child: run the program */
+
+ /* NB: close _first_, then move fds! */
+ close(to_prog.wr);
+ close(from_prog.rd);
+ xmove_fd(to_prog.rd, STDIN_FILENO);
+ xmove_fd(from_prog.wr, STDOUT_FILENO);
+
+ BB_EXECVP_or_die(argv);
+ }
+ /* Parent: close child ends of pipes */
+ close(to_prog.rd);
+ close(from_prog.wr);
+
+ /* tls_run_copy_loop() needs non-TLS fds on STDIN and STDOUT */
+ xmove_fd(from_prog.rd, STDIN_FILENO);
+ xmove_fd(to_prog.wr, STDOUT_FILENO);
+ tls_run_copy_loop(tls, /*flags*/ 0
+//flags????
+);
+ return EXIT_SUCCESS;
+}
diff --git a/networking/tls.c b/networking/tls.c
index ec8ce20e6..852fd8002 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -2323,6 +2323,7 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
tls_free_outbuf(tls); /* mem usage optimization */
if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF)
break;
+ //TODO: if (pfds[1].revents) network has data, do a last read from it before exiting.
} else {
if (nread == inbuf_size) {
/* TLS has per record overhead, if input comes fast,
@@ -2332,6 +2333,11 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
if (inbuf_size > TLS_MAX_OUTBUF)
inbuf_size = TLS_MAX_OUTBUF;
}
+//BUG: in this example: printf '\n' | ssl_client -e ssl_server -OPTS echo 'Hi'
+//if ssl_client arrives here (if it sees stdin before it sees the server's "Hi" message),
+//it can be killed by SIGPIPE because server has already exited.
+//(If we disable SIGPIPE, it would die on write error).
+//Which means it won't get and won't print to stdout the server's response!
tls_xwrite(tls, nread);
}
}
@@ -2360,3 +2366,16 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
}
}
}
+
+#if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL
+
+/* =============== SERVER-SIDE CODE =============== */
+
+void FAST_FUNC tls_handshake_as_server(tls_state_t *tls,
+ const char *privkey_der_filename,
+ const char *cert_der_filename)
+{
+//TODO
+ exit(1 | !tls | !privkey_der_filename | !cert_der_filename);
+}
+#endif
More information about the busybox-cvs
mailing list