separate TLS code into a library, use in in wget
No binary (build failed)
—
(log yok)
| @@ -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, | |
| @@ -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; |
| + | } |
| @@ -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; |
| } | |
| @@ -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); | |