From 9a647c326a41e8160d53e6cb5470161a44c0e8cf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 23 Jan 2017 01:08:16 +0100 Subject: [PATCH] separate TLS code into a library, use in in wget A new applet, ssl_client, is the TLS debug thing now. It doubles as wget's NOMMU helper. In MMU mode, wget still forks, but then directly calls TLS code, without execing. This can also be applied to sendmail/popmail (SMTPS / SMTP+starttls support) and nc --ssl (ncat, nmap's nc clone, has such option). function old new delta tls_handshake - 1691 +1691 tls_run_copy_loop - 443 +443 ssl_client_main - 128 +128 packed_usage 30978 31007 +29 wget_main 2508 2535 +27 applet_names 2553 2560 +7 ... xwrite_encrypted 360 342 -18 tls_main 2127 - -2127 ------------------------------------------------------------------------------ (add/remove: 4/1 grow/shrink: 13/8 up/down: 2351/-2195) Total: 156 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 88 +++++++++++++++++------ networking/ssl_client.c | 55 +++++++++++++++ networking/tls.c | 151 ++++++++++++---------------------------- networking/wget.c | 102 ++++++++++++++------------- 4 files changed, 221 insertions(+), 175 deletions(-) create mode 100644 networking/ssl_client.c diff --git a/include/libbb.h b/include/libbb.h index 87f89c76d..ba3b1479e 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -355,6 +355,27 @@ extern char *skip_dev_pfx(const char *tty_name) FAST_FUNC; extern char *strrstr(const char *haystack, const char *needle) FAST_FUNC; +/* dmalloc will redefine these to it's own implementation. It is safe + * to have the prototypes here unconditionally. */ +void *malloc_or_warn(size_t size) FAST_FUNC RETURNS_MALLOC; +void *xmalloc(size_t size) FAST_FUNC RETURNS_MALLOC; +void *xzalloc(size_t size) FAST_FUNC RETURNS_MALLOC; +void *xrealloc(void *old, size_t size) FAST_FUNC; +/* After v = xrealloc_vector(v, SHIFT, idx) it's ok to use + * at least v[idx] and v[idx+1], for all idx values. + * SHIFT specifies how many new elements are added (1:2, 2:4, ..., 8:256...) + * when all elements are used up. New elements are zeroed out. + * xrealloc_vector(v, SHIFT, idx) *MUST* be called with consecutive IDXs - + * skipping an index is a bad bug - it may miss a realloc! + */ +#define xrealloc_vector(vector, shift, idx) \ + xrealloc_vector_helper((vector), (sizeof((vector)[0]) << 8) + (shift), (idx)) +void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) FAST_FUNC; +char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC; +char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC; +void *xmemdup(const void *s, int n) FAST_FUNC RETURNS_MALLOC; + + //TODO: supply a pointer to char[11] buffer (avoid statics)? extern const char *bb_mode_string(mode_t mode) FAST_FUNC; extern int is_directory(const char *name, int followLinks) FAST_FUNC; @@ -692,6 +713,52 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC; // Also mount.c and inetd.c are using gethostbyname(), // + inet_common.c has additional IPv4-only stuff +#define SHA256_INSIZE 64 +#define SHA256_OUTSIZE 32 +#define AES_BLOCKSIZE 16 +#define AES128_KEYSIZE 16 +#define AES256_KEYSIZE 32 +struct tls_handshake_data; /* opaque */ +typedef struct tls_state { + int ofd; + int ifd; + + int min_encrypted_len_on_read; + uint8_t encrypt_on_write; + + uint8_t *outbuf; + int outbuf_size; + + int inbuf_size; + int ofs_to_buffered; + int buffered_size; + uint8_t *inbuf; + + struct tls_handshake_data *hsd; + + // RFC 5246 + // sequence number + // Each connection state contains a sequence number, which is + // maintained separately for read and write states. The sequence + // number MUST be set to zero whenever a connection state is made the + // active state. Sequence numbers are of type uint64 and may not + // exceed 2^64-1. + /*uint64_t read_seq64_be;*/ + uint64_t write_seq64_be; + + uint8_t client_write_MAC_key[SHA256_OUTSIZE]; + uint8_t server_write_MAC_key[SHA256_OUTSIZE]; + uint8_t client_write_key[AES256_KEYSIZE]; + uint8_t server_write_key[AES256_KEYSIZE]; +} tls_state_t; + +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 tls_run_copy_loop(tls_state_t *tls) FAST_FUNC; void socket_want_pktinfo(int fd) FAST_FUNC; ssize_t send_to_from(int fd, void *buf, size_t len, int flags, @@ -705,9 +772,6 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags, uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; -char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC; -char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC; -void *xmemdup(const void *s, int n) FAST_FUNC RETURNS_MALLOC; void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; @@ -753,24 +817,6 @@ enum { }; void visible(unsigned ch, char *buf, int flags) FAST_FUNC; -/* dmalloc will redefine these to it's own implementation. It is safe - * to have the prototypes here unconditionally. */ -void *malloc_or_warn(size_t size) FAST_FUNC RETURNS_MALLOC; -void *xmalloc(size_t size) FAST_FUNC RETURNS_MALLOC; -void *xzalloc(size_t size) FAST_FUNC RETURNS_MALLOC; -void *xrealloc(void *old, size_t size) FAST_FUNC; -/* After v = xrealloc_vector(v, SHIFT, idx) it's ok to use - * at least v[idx] and v[idx+1], for all idx values. - * SHIFT specifies how many new elements are added (1:2, 2:4, ..., 8:256...) - * when all elements are used up. New elements are zeroed out. - * xrealloc_vector(v, SHIFT, idx) *MUST* be called with consecutive IDXs - - * skipping an index is a bad bug - it may miss a realloc! - */ -#define xrealloc_vector(vector, shift, idx) \ - xrealloc_vector_helper((vector), (sizeof((vector)[0]) << 8) + (shift), (idx)) -void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) FAST_FUNC; - - extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC; // NB: will return short read on error, not -1, diff --git a/networking/ssl_client.c b/networking/ssl_client.c new file mode 100644 index 000000000..cfeae1587 --- /dev/null +++ b/networking/ssl_client.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config SSL_CLIENT +//config: bool "ssl_client" +//config: default y +//config: select TLS +//config: help +//config: This tool pipes data to/from a socket, TLS-encrypting it. + +//applet:IF_SSL_CLIENT(APPLET(ssl_client, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SSL_CLIENT) += ssl_client.o + +//usage:#define ssl_client_trivial_usage +//usage: "-s FD [-r FD] [-n SNI]" +//usage:#define ssl_client_full_usage "" + +#include "libbb.h" + +int ssl_client_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ssl_client_main(int argc UNUSED_PARAM, char **argv) +{ + tls_state_t *tls; + const char *sni = NULL; + int opt; + + // INIT_G(); + + tls = new_tls_state(); + opt = getopt32(argv, "s:#r:#n:", &tls->ofd, &tls->ifd, &sni); + if (!(opt & 2)) { + /* -r N defaults to -s N */ + tls->ifd = tls->ofd; + } + + if (!(opt & 3)) { + if (!argv[1]) + bb_show_usage(); + /* Undocumented debug feature: without -s and -r, takes HOST arg and connects to it */ + // + // Talk to kernel.org: + // printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ./busybox ssl_client kernel.org + if (!sni) + sni = argv[1]; + tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443); + } + + tls_handshake(tls, sni); + tls_run_copy_loop(tls); + + return EXIT_SUCCESS; +} diff --git a/networking/tls.c b/networking/tls.c index b111e4bb4..29cc5b9f3 100644 --- a/networking/tls.c +++ b/networking/tls.c @@ -4,11 +4,9 @@ * Licensed under GPLv2, see file LICENSE in this source tree. */ //config:config TLS -//config: bool "tls (debugging)" +//config: bool #No description makes it a hidden option //config: default n -//applet:IF_TLS(APPLET(tls, BB_DIR_USR_BIN, BB_SUID_DROP)) - //kbuild:lib-$(CONFIG_TLS) += tls.o //kbuild:lib-$(CONFIG_TLS) += tls_pstm.o //kbuild:lib-$(CONFIG_TLS) += tls_pstm_montgomery_reduce.o @@ -18,12 +16,7 @@ //kbuild:lib-$(CONFIG_TLS) += tls_aes.o ////kbuild:lib-$(CONFIG_TLS) += tls_aes_gcm.o -//usage:#define tls_trivial_usage -//usage: "HOST[:PORT]" -//usage:#define tls_full_usage "\n\n" - #include "tls.h" -//#include "common_bufsiz.h" #define TLS_DEBUG 1 #define TLS_DEBUG_HASH 0 @@ -165,13 +158,6 @@ #define CIPHER_ID TLS_RSA_WITH_AES_256_CBC_SHA256 // ok, no SERVER_KEY_EXCHANGE enum { - SHA256_INSIZE = 64, - SHA256_OUTSIZE = 32, - - AES_BLOCKSIZE = 16, - AES128_KEYSIZE = 16, - AES256_KEYSIZE = 32, - RSA_PREMASTER_SIZE = 48, RECHDR_LEN = 5, @@ -225,20 +211,7 @@ struct record_hdr { uint8_t len16_hi, len16_lo; }; -typedef struct tls_state { - int fd; - - int min_encrypted_len_on_read; - uint8_t encrypt_on_write; - - uint8_t *outbuf; - int outbuf_size; - - int inbuf_size; - int ofs_to_buffered; - int buffered_size; - uint8_t *inbuf; - +struct tls_handshake_data { //TODO: store just the DER key here, parse/use/delete it when sending client key //this way it will stay key type agnostic here. psRsaKey_t server_rsa_pub_key; @@ -247,22 +220,7 @@ typedef struct tls_state { // these two are unused after finished messages are exchanged: sha256_ctx_t handshake_sha256_ctx; uint8_t master_secret[48]; - - // RFC 5246 - // sequence number - // Each connection state contains a sequence number, which is - // maintained separately for read and write states. The sequence - // number MUST be set to zero whenever a connection state is made the - // active state. Sequence numbers are of type uint64 and may not - // exceed 2^64-1. - /*uint64_t read_seq64_be;*/ - uint64_t write_seq64_be; - - uint8_t client_write_MAC_key[SHA256_OUTSIZE]; - uint8_t server_write_MAC_key[SHA256_OUTSIZE]; - uint8_t client_write_key[AES256_KEYSIZE]; - uint8_t server_write_key[AES256_KEYSIZE]; -} tls_state_t; +}; static unsigned get24be(const uint8_t *p) @@ -487,14 +445,6 @@ static void prf_hmac_sha256( #undef SEED } -static tls_state_t *new_tls_state(void) -{ - tls_state_t *tls = xzalloc(sizeof(*tls)); - tls->fd = -1; - sha256_begin(&tls->handshake_sha256_ctx); - return tls; -} - static void tls_error_die(tls_state_t *tls) { dump_tls_record(tls->inbuf, tls->ofs_to_buffered + tls->buffered_size); @@ -597,7 +547,7 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type) xhdr->len16_hi = size >> 8; xhdr->len16_lo = size & 0xff; dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); - xwrite(tls->fd, xhdr, RECHDR_LEN + size); + xwrite(tls->ofd, xhdr, RECHDR_LEN + size); dbg("wrote %u bytes (NULL crypt, SHA256 hash)\n", size); return; } @@ -681,7 +631,7 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type) xhdr->len16_hi = size >> 8; xhdr->len16_lo = size & 0xff; dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); - xwrite(tls->fd, xhdr, RECHDR_LEN + size); + xwrite(tls->ofd, xhdr, RECHDR_LEN + size); dbg("wrote %u bytes\n", (int)RECHDR_LEN + size); } @@ -697,10 +647,10 @@ static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size) xhdr->len16_hi = size >> 8; xhdr->len16_lo = size & 0xff; dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size); - xwrite(tls->fd, xhdr, RECHDR_LEN + size); + xwrite(tls->ofd, xhdr, RECHDR_LEN + size); dbg("wrote %u bytes\n", (int)RECHDR_LEN + size); /* Handshake hash does not include record headers */ - sha256_hash_dbg(">> sha256:%s", &tls->handshake_sha256_ctx, buf, size); + sha256_hash_dbg(">> sha256:%s", &tls->hsd->handshake_sha256_ctx, buf, size); return; } xwrite_encrypted(tls, size, RECORD_TYPE_HANDSHAKE); @@ -769,7 +719,7 @@ static int tls_xread_record(tls_state_t *tls) rem = tls->inbuf_size - total; tls->inbuf = xrealloc(tls->inbuf, tls->inbuf_size); } - sz = safe_read(tls->fd, tls->inbuf + total, rem); + sz = safe_read(tls->ifd, tls->inbuf + total, rem); if (sz <= 0) { if (sz == 0 && total == 0) { /* "Abrupt" EOF, no TLS shutdown (seen from kernel.org) */ @@ -848,7 +798,7 @@ static int tls_xread_record(tls_state_t *tls) * in our FINISHED record must include data of incoming packets too! */ if (tls->inbuf[0] == RECORD_TYPE_HANDSHAKE) { - sha256_hash_dbg("<< sha256:%s", &tls->handshake_sha256_ctx, tls->inbuf + RECHDR_LEN, sz); + sha256_hash_dbg("<< sha256:%s", &tls->hsd->handshake_sha256_ctx, tls->inbuf + RECHDR_LEN, sz); } end: dbg("got block len:%u\n", sz); @@ -1059,12 +1009,12 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len) xfunc_die(); der++; der = enter_der_item(der, &end); /* enter SEQ */ - /* memset(tls->server_rsa_pub_key, 0, sizeof(tls->server_rsa_pub_key)); - already is */ - der_binary_to_pstm(&tls->server_rsa_pub_key.N, der, end); /* modulus */ + /* memset(tls->hsd->server_rsa_pub_key, 0, sizeof(tls->hsd->server_rsa_pub_key)); - already is */ + der_binary_to_pstm(&tls->hsd->server_rsa_pub_key.N, der, end); /* modulus */ der = skip_der_item(der, end); - der_binary_to_pstm(&tls->server_rsa_pub_key.e, der, end); /* exponent */ - tls->server_rsa_pub_key.size = pstm_unsigned_bin_size(&tls->server_rsa_pub_key.N); - dbg("server_rsa_pub_key.size:%d\n", tls->server_rsa_pub_key.size); + der_binary_to_pstm(&tls->hsd->server_rsa_pub_key.e, der, end); /* exponent */ + tls->hsd->server_rsa_pub_key.size = pstm_unsigned_bin_size(&tls->hsd->server_rsa_pub_key.N); + dbg("server_rsa_pub_key.size:%d\n", tls->hsd->server_rsa_pub_key.size); } /* @@ -1140,7 +1090,7 @@ static void send_client_hello(tls_state_t *tls, const char *sni) tls_get_random(record->rand32, sizeof(record->rand32)); if (TLS_DEBUG_FIXED_SECRETS) memset(record->rand32, 0x11, sizeof(record->rand32)); - memcpy(tls->client_and_server_rand32, record->rand32, sizeof(record->rand32)); + memcpy(tls->hsd->client_and_server_rand32, record->rand32, sizeof(record->rand32)); /* record->session_id_len = 0; - already is */ /* record->cipherid_len16_hi = 0; */ record->cipherid_len16_lo = 2 * 1; @@ -1225,7 +1175,7 @@ static void get_server_hello(tls_state_t *tls) } dbg("<< SERVER_HELLO\n"); - memcpy(tls->client_and_server_rand32 + 32, hp->rand32, sizeof(hp->rand32)); + memcpy(tls->hsd->client_and_server_rand32 + 32, hp->rand32, sizeof(hp->rand32)); } static void get_server_cert(tls_state_t *tls) @@ -1282,7 +1232,7 @@ static void send_client_key_exchange(tls_state_t *tls) rsa_premaster[0] = TLS_MAJ; rsa_premaster[1] = TLS_MIN; len = psRsaEncryptPub(/*pool:*/ NULL, - /* psRsaKey_t* */ &tls->server_rsa_pub_key, + /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key, rsa_premaster, /*inlen:*/ sizeof(rsa_premaster), record->key, sizeof(record->key), data_param_ignored @@ -1310,12 +1260,12 @@ static void send_client_key_exchange(tls_state_t *tls) // The master secret is always exactly 48 bytes in length. The length // of the premaster secret will vary depending on key exchange method. prf_hmac_sha256( - tls->master_secret, sizeof(tls->master_secret), + tls->hsd->master_secret, sizeof(tls->hsd->master_secret), rsa_premaster, sizeof(rsa_premaster), "master secret", - tls->client_and_server_rand32, sizeof(tls->client_and_server_rand32) + tls->hsd->client_and_server_rand32, sizeof(tls->hsd->client_and_server_rand32) ); - dump_hex("master secret:%s\n", tls->master_secret, sizeof(tls->master_secret)); + dump_hex("master secret:%s\n", tls->hsd->master_secret, sizeof(tls->hsd->master_secret)); // RFC 5246 // 6.3. Key Calculation @@ -1354,8 +1304,8 @@ static void send_client_key_exchange(tls_state_t *tls) uint8_t tmp64[64]; /* make "server_rand32 + client_rand32" */ - memcpy(&tmp64[0] , &tls->client_and_server_rand32[32], 32); - memcpy(&tmp64[32], &tls->client_and_server_rand32[0] , 32); + memcpy(&tmp64[0] , &tls->hsd->client_and_server_rand32[32], 32); + memcpy(&tmp64[32], &tls->hsd->client_and_server_rand32[0] , 32); prf_hmac_sha256( tls->client_write_MAC_key, 2 * (SHA256_OUTSIZE + AES256_KEYSIZE), @@ -1363,7 +1313,7 @@ static void send_client_key_exchange(tls_state_t *tls) // server_write_MAC_key[SHA256_OUTSIZE] // client_write_key[AES256_KEYSIZE] // server_write_key[AES256_KEYSIZE] - tls->master_secret, sizeof(tls->master_secret), + tls->hsd->master_secret, sizeof(tls->hsd->master_secret), "key expansion", tmp64, 64 ); @@ -1384,7 +1334,7 @@ static const uint8_t rec_CHANGE_CIPHER_SPEC[] = { static void send_change_cipher_spec(tls_state_t *tls) { dbg(">> CHANGE_CIPHER_SPEC\n"); - xwrite(tls->fd, rec_CHANGE_CIPHER_SPEC, sizeof(rec_CHANGE_CIPHER_SPEC)); + xwrite(tls->ofd, rec_CHANGE_CIPHER_SPEC, sizeof(rec_CHANGE_CIPHER_SPEC)); } // 7.4.9. Finished @@ -1436,13 +1386,13 @@ static void send_client_finished(tls_state_t *tls) fill_handshake_record_hdr(record, HANDSHAKE_FINISHED, sizeof(*record)); - sha256_peek(&tls->handshake_sha256_ctx, handshake_hash); + sha256_peek(&tls->hsd->handshake_sha256_ctx, handshake_hash); prf_hmac_sha256(record->prf_result, sizeof(record->prf_result), - tls->master_secret, sizeof(tls->master_secret), + tls->hsd->master_secret, sizeof(tls->hsd->master_secret), "client finished", handshake_hash, sizeof(handshake_hash) ); - dump_hex("from secret: %s\n", tls->master_secret, sizeof(tls->master_secret)); + dump_hex("from secret: %s\n", tls->hsd->master_secret, sizeof(tls->hsd->master_secret)); dump_hex("from labelSeed: %s", "client finished", sizeof("client finished")-1); dump_hex("%s\n", handshake_hash, sizeof(handshake_hash)); dump_hex("=> digest: %s\n", record->prf_result, sizeof(record->prf_result)); @@ -1451,7 +1401,7 @@ static void send_client_finished(tls_state_t *tls) xwrite_encrypted(tls, sizeof(*record), RECORD_TYPE_HANDSHAKE); } -static void tls_handshake(tls_state_t *tls, const char *sni) +void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni) { // Client RFC 5246 Server // (*) - optional messages, not always sent @@ -1472,6 +1422,9 @@ static void tls_handshake(tls_state_t *tls, const char *sni) // Application Data <------> Application Data int len; + tls->hsd = xzalloc(sizeof(*tls->hsd)); + sha256_begin(&tls->hsd->handshake_sha256_ctx); + send_client_hello(tls, sni); get_server_hello(tls); @@ -1541,6 +1494,12 @@ static void tls_handshake(tls_state_t *tls, const char *sni) tls_error_die(tls); dbg("<< FINISHED\n"); /* application data can be sent/received */ + + /* free handshake data */ +// if (PARANOIA) +// memset(tls->hsd, 0, sizeof(*tls->hsd)); + free(tls->hsd); + tls->hsd = NULL; } static void tls_xwrite(tls_state_t *tls, int len) @@ -1557,35 +1516,17 @@ static void tls_xwrite(tls_state_t *tls, int len) // openssl req -x509 -newkey rsa:$((4096/4*3)) -keyout key.pem -out server.pem -nodes -days 99999 -subj '/CN=localhost' // openssl s_server -key key.pem -cert server.pem -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher NULL // openssl s_client -connect 127.0.0.1:4433 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher NULL-SHA256 -// -// Talk to kernel.org: -// printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ./busybox tls kernel.org -int tls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int tls_main(int argc UNUSED_PARAM, char **argv) +void FAST_FUNC tls_run_copy_loop(tls_state_t *tls) { - tls_state_t *tls; fd_set readfds; int inbuf_size; const int INBUF_STEP = 4 * 1024; - int cfd; - - // INIT_G(); - // getopt32(argv, "myopts") - - if (!argv[1]) - bb_show_usage(); - - cfd = create_and_connect_stream_or_die(argv[1], 443); - - tls = new_tls_state(); - tls->fd = cfd; - tls_handshake(tls, argv[1]); - - /* Select loop copying stdin to cfd, and cfd to stdout */ +//TODO: convert to poll + /* Select loop copying stdin to ofd, and ifd to stdout */ FD_ZERO(&readfds); - FD_SET(cfd, &readfds); + FD_SET(tls->ifd, &readfds); FD_SET(STDIN_FILENO, &readfds); inbuf_size = INBUF_STEP; @@ -1594,7 +1535,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) int nread; testfds = readfds; - if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0) + if (select(tls->ifd + 1, &testfds, NULL, NULL, NULL) < 0) bb_perror_msg_and_die("select"); if (FD_ISSET(STDIN_FILENO, &testfds)) { @@ -1608,7 +1549,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) /* Close outgoing half-connection so they get EOF, * but leave incoming alone so we can see response */ - //shutdown(cfd, SHUT_WR); + //shutdown(tls->ofd, SHUT_WR); /* But TLS has no way to encode this, * doubt it's ok to do it "raw" */ @@ -1626,7 +1567,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) tls_xwrite(tls, nread); } } - if (FD_ISSET(cfd, &testfds)) { + if (FD_ISSET(tls->ifd, &testfds)) { dbg("NETWORK HAS DATA\n"); read_record: nread = tls_xread_record(tls); @@ -1634,7 +1575,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) /* TLS protocol has no real concept of one-sided shutdowns: * if we get "TLS EOF" from the peer, writes will fail too */ - //FD_CLR(cfd, &readfds); + //FD_CLR(tls->ifd, &readfds); //close(STDOUT_FILENO); //tls_free_inbuf(tls); /* mem usage optimization */ //continue; @@ -1650,6 +1591,4 @@ int tls_main(int argc UNUSED_PARAM, char **argv) goto read_record; } } - - return EXIT_SUCCESS; } diff --git a/networking/wget.c b/networking/wget.c index 58ead4c96..a448acdae 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -47,18 +47,26 @@ //config: FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option //config: will work in addition to -T. //config: +//config:config FEATURE_WGET_HTTPS +//config: bool "Support HTTPS using internal TLS code" +//config: default y +//config: depends on WGET +//config: select TLS +//config: help +//config: wget will use internal TLS code to connect to https:// URLs. +//config: Note: +//config: On NOMMU machines, ssl_helper applet should be available +//config: in the $PATH for this to work. Make sure to select that applet. +//config: //config:config FEATURE_WGET_OPENSSL //config: bool "Try to connect to HTTPS using openssl" //config: default y //config: depends on WGET //config: help -//config: Choose how wget establishes SSL connection for https:// URLs. -//config: -//config: Busybox itself contains no SSL code. wget will spawn -//config: a helper program to talk over HTTPS. +//config: Try to use openssl to handle HTTPS. //config: //config: OpenSSL has a simple SSL client for debug purposes. -//config: If you select "openssl" helper, wget will effectively run: +//config: If you select this option, wget will effectively run: //config: "openssl s_client -quiet -connect hostname:443 //config: -servername hostname 2>/dev/null" and pipe its data //config: through it. -servername is not used if hostname is numeric. @@ -71,24 +79,9 @@ //config: openssl is also a big binary, often dynamically linked //config: against ~15 libraries. //config: -//config:config FEATURE_WGET_SSL_HELPER -//config: bool "Try to connect to HTTPS using ssl_helper" -//config: default y -//config: depends on WGET -//config: help -//config: Choose how wget establishes SSL connection for https:// URLs. -//config: -//config: Busybox itself contains no SSL code. wget will spawn -//config: a helper program to talk over HTTPS. -//config: -//config: ssl_helper is a tool which can be built statically -//config: from busybox sources against a small embedded SSL library. -//config: Please see networking/ssl_helper/README. -//config: It does not require double host resolution and emits -//config: error messages to stderr. -//config: -//config: Precompiled static binary may be available at -//config: http://busybox.net/downloads/binaries/ +//config: If openssl can't be executed, internal TLS code will be used +//config: (if you enabled it); if openssl can be executed but fails later, +//config: wget can't detect this, and download will fail. //applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP)) @@ -137,7 +130,7 @@ #endif -#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_SSL_HELPER) +#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_HTTPS) struct host_info { char *allocated; @@ -657,7 +650,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) char *servername; int sp[2]; int pid; - IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;) + IF_FEATURE_WGET_HTTPS(volatile int child_failed = 0;) if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0) /* Kernel can have AF_UNIX support disabled */ @@ -702,7 +695,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) BB_EXECVP(argv[0], argv); xmove_fd(3, 2); -# if ENABLE_FEATURE_WGET_SSL_HELPER +# if ENABLE_FEATURE_WGET_HTTPS child_failed = 1; xfunc_die(); # else @@ -715,7 +708,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) free(servername); free(allocated); close(sp[1]); -# if ENABLE_FEATURE_WGET_SSL_HELPER +# if ENABLE_FEATURE_WGET_HTTPS if (child_failed) { close(sp[0]); return -1; @@ -725,38 +718,51 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) } #endif -/* See networking/ssl_helper/README how to build one */ -#if ENABLE_FEATURE_WGET_SSL_HELPER -static void spawn_https_helper_small(int network_fd) +#if ENABLE_FEATURE_WGET_HTTPS +static void spawn_ssl_client(const char *host, int network_fd) { int sp[2]; int pid; + char *servername, *p; + + servername = xstrdup(host); + p = strrchr(servername, ':'); + if (p) *p = '\0'; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0) /* Kernel can have AF_UNIX support disabled */ bb_perror_msg_and_die("socketpair"); + fflush_all(); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* Child */ - char *argv[3]; - close(sp[0]); xmove_fd(sp[1], 0); xdup2(0, 1); - xmove_fd(network_fd, 3); - /* - * A simple ssl/tls helper - */ - argv[0] = (char*)"ssl_helper"; - argv[1] = (char*)"-d3"; - argv[2] = NULL; - BB_EXECVP(argv[0], argv); - bb_perror_msg_and_die("can't execute '%s'", argv[0]); + if (BB_MMU) { + tls_state_t *tls = new_tls_state(); + tls->ifd = tls->ofd = network_fd; + tls_handshake(tls, servername); + tls_run_copy_loop(tls); + exit(0); + } else { + char *argv[5]; + xmove_fd(network_fd, 3); + argv[0] = (char*)"ssl_client"; + argv[1] = (char*)"-s3"; + //TODO: if (!is_ip_address(servername))... + argv[2] = (char*)"-n"; + argv[3] = servername; + argv[4] = NULL; + BB_EXECVP(argv[0], argv); + bb_perror_msg_and_die("can't execute '%s'", argv[0]); + } /* notreached */ } /* Parent */ + free(servername); close(sp[1]); xmove_fd(sp[0], network_fd); } @@ -1005,16 +1011,16 @@ static void download_one_url(const char *url) /* Open socket to http(s) server */ #if ENABLE_FEATURE_WGET_OPENSSL - /* openssl (and maybe ssl_helper) support is configured */ + /* openssl (and maybe internal TLS) support is configured */ if (target.protocol == P_HTTPS) { /* openssl-based helper * Inconvenient API since we can't give it an open fd */ int fd = spawn_https_helper_openssl(server.host, server.port); -# if ENABLE_FEATURE_WGET_SSL_HELPER - if (fd < 0) { /* no openssl? try ssl_helper */ +# if ENABLE_FEATURE_WGET_HTTPS + if (fd < 0) { /* no openssl? try internal */ sfp = open_socket(lsa); - spawn_https_helper_small(fileno(sfp)); + spawn_ssl_client(server.host, fileno(sfp)); goto socket_opened; } # else @@ -1027,11 +1033,11 @@ static void download_one_url(const char *url) } sfp = open_socket(lsa); socket_opened: -#elif ENABLE_FEATURE_WGET_SSL_HELPER - /* Only ssl_helper support is configured */ +#elif ENABLE_FEATURE_WGET_HTTPS + /* Only internal TLS support is configured */ sfp = open_socket(lsa); if (target.protocol == P_HTTPS) - spawn_https_helper_small(fileno(sfp)); + spawn_ssl_client(server.host, fileno(sfp)); #else /* ssl (https) support is not configured */ sfp = open_socket(lsa); -- 2.25.1