ssl_server: new applet, not functional yet
No binary (build failed)
gcc -Wp,-MD,applets/.usage.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iinclude -Iinclude -o applets/usage applets/usage.c
(log yok)
| @@ -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; | |
| @@ -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; | |
| } | |
| @@ -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; |
| + | } |
| @@ -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 |