[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