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);