diff options
author | Denys Vlasenko | 2017-01-23 16:12:17 +0100 |
---|---|---|
committer | Denys Vlasenko | 2017-01-23 16:12:17 +0100 |
commit | b5bf1913d31512d1c5f4c9656dc96e6b8dcd92ba (patch) | |
tree | 2739d19dc6c99f73d84b25a616928c5cb36281ce | |
parent | 9492da7e63deae898d7bf924be5790c7fab1a4fb (diff) | |
download | busybox-b5bf1913d31512d1c5f4c9656dc96e6b8dcd92ba.zip busybox-b5bf1913d31512d1c5f4c9656dc96e6b8dcd92ba.tar.gz |
tls: send EMPTY_RENEGOTIATION_INFO_SCSV in our client hello
Hoped this can make cdn.kernel.org to like us more. Nope.
While at it, made error reporting more useful.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | networking/tls.c | 112 |
1 files changed, 86 insertions, 26 deletions
diff --git a/networking/tls.c b/networking/tls.c index 89f2ec6..4456c7d 100644 --- a/networking/tls.c +++ b/networking/tls.c @@ -22,6 +22,16 @@ #define TLS_DEBUG_HASH 0 #define TLS_DEBUG_DER 0 #define TLS_DEBUG_FIXED_SECRETS 0 +#if 0 +# define dump_raw_out(...) dump_hex(__VA_ARGS__) +#else +# define dump_raw_out(...) ((void)0) +#endif +#if 0 +# define dump_raw_in(...) dump_hex(__VA_ARGS__) +#else +# define dump_raw_in(...) ((void)0) +#endif #if TLS_DEBUG # define dbg(...) fprintf(stderr, __VA_ARGS__) @@ -35,18 +45,6 @@ # define dbg_der(...) ((void)0) #endif -#if 0 -# define dump_raw_out(...) dump_hex(__VA_ARGS__) -#else -# define dump_raw_out(...) ((void)0) -#endif - -#if 0 -# define dump_raw_in(...) dump_hex(__VA_ARGS__) -#else -# define dump_raw_in(...) ((void)0) -#endif - #define RECORD_TYPE_CHANGE_CIPHER_SPEC 20 #define RECORD_TYPE_ALERT 21 #define RECORD_TYPE_HANDSHAKE 22 @@ -154,8 +152,18 @@ // works against "openssl s_server -cipher NULL" // and against wolfssl-3.9.10-stable/examples/server/server.c: //#define CIPHER_ID TLS_RSA_WITH_NULL_SHA256 // for testing (does everything except encrypting) + // works against wolfssl-3.9.10-stable/examples/server/server.c -#define CIPHER_ID TLS_RSA_WITH_AES_256_CBC_SHA256 // ok, no SERVER_KEY_EXCHANGE +// works for kernel.org +// does not work for cdn.kernel.org (e.g. downloading an actual tarball, not a web page) +// getting alert 40 "handshake failure" at once +// with GNU Wget 1.18, they agree on TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xC02F) cipher +// fail: openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES256-SHA256 +// fail: openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES256-GCM-SHA384 +// fail: openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES128-SHA256 +// ok: openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES128-GCM-SHA256 +// ok: openssl s_client -connect cdn.kernel.org:443 -debug -tls1_2 -no_tls1 -no_tls1_1 -cipher AES128-SHA +#define CIPHER_ID TLS_RSA_WITH_AES_256_CBC_SHA256 // no SERVER_KEY_EXCHANGE from peer enum { RSA_PREMASTER_SIZE = 48, @@ -445,6 +453,18 @@ static void prf_hmac_sha256( #undef SEED } +static void bad_record_die(tls_state_t *tls, const char *expected, int len) +{ + bb_error_msg_and_die("got bad TLS record (len:%d) while expecting %s", len, expected); + if (len > 0) { + uint8_t *p = tls->inbuf; + while (len > 0) + fprintf(stderr, " %02x", *p++); + fputc('\n', stderr); + } + xfunc_die(); +} + static void tls_error_die(tls_state_t *tls) { dump_tls_record(tls->inbuf, tls->ofs_to_buffered + tls->buffered_size); @@ -671,6 +691,18 @@ static int tls_has_buffered_record(tls_state_t *tls) return rec_size; } +static const char *alert_text(int code) +{ + switch (code) { + case 20: return "bad MAC"; + case 50: return "decode error"; + case 51: return "decrypt error"; + case 40: return "handshake failure"; + case 112: return "unrecognized name"; + } + return itoa(code); +} + static int tls_xread_record(tls_state_t *tls) { struct record_hdr *xhdr; @@ -780,16 +812,28 @@ static int tls_xread_record(tls_state_t *tls) if (xhdr->type == RECORD_TYPE_ALERT && sz >= 2) { uint8_t *p = tls->inbuf + RECHDR_LEN; dbg("ALERT size:%d level:%d description:%d\n", sz, p[0], p[1]); + if (p[0] == 2) { /* fatal */ + bb_error_msg_and_die("TLS %s from peer (alert code %d): %s", + "error", + p[1], alert_text(p[1]) + ); + } if (p[0] == 1) { /* warning */ if (p[1] == 0) { /* "close_notify" warning: it's EOF */ dbg("EOF (TLS encoded) from peer\n"); sz = 0; goto end; } +//This possibly needs to be cached and shown only if +//a fatal alert follows +// bb_error_msg("TLS %s from peer (alert code %d): %s", +// "warning", +// p[1], alert_text(p[1]) +// ); /* discard it, get next record */ goto again; } - /* p[0] == 1: fatal error, others: not defined in protocol */ + /* p[0] not 1 or 2: not defined in protocol */ sz = 0; goto end; } @@ -1031,7 +1075,7 @@ static int tls_xread_handshake_block(tls_state_t *tls, int min_len) || xhdr->proto_maj != TLS_MAJ || xhdr->proto_min != TLS_MIN ) { - tls_error_die(tls); + bad_record_die(tls, "handshake record", len); } dbg("got HANDSHAKE\n"); return len; @@ -1051,7 +1095,6 @@ static ALWAYS_INLINE void fill_handshake_record_hdr(void *buf, unsigned type, un h->len24_lo = len & 0xff; } -//TODO: implement RFC 5746 (Renegotiation Indication Extension) - some servers will refuse to work with us otherwise static void send_client_hello(tls_state_t *tls, const char *sni) { struct client_hello { @@ -1062,18 +1105,27 @@ static void send_client_hello(tls_state_t *tls, const char *sni) uint8_t session_id_len; /* uint8_t session_id[]; */ uint8_t cipherid_len16_hi, cipherid_len16_lo; - uint8_t cipherid[2 * 1]; /* actually variable */ + uint8_t cipherid[2 * 2]; /* actually variable */ uint8_t comprtypes_len; uint8_t comprtypes[1]; /* actually variable */ /* Extensions (SNI shown): * hi,lo // len of all extensions - * 0x00,0x00 // extension_type: "Server Name" - * 0x00,0x0e // list len (there can be more than one SNI) - * 0x00,0x0c // len of 1st Server Name Indication - * 0x00 // name type: host_name - * 0x00,0x09 // name len + * 00,00 // extension_type: "Server Name" + * 00,0e // list len (there can be more than one SNI) + * 00,0c // len of 1st Server Name Indication + * 00 // name type: host_name + * 00,09 // name len * "localhost" // name */ +// GNU Wget 1.18 to cdn.kernel.org sends these extensions: +// 0055 +// 0005 0005 0100000000 - status_request +// 0000 0013 0011 00 000e 63646e 2e 6b65726e656c 2e 6f7267 - server_name +// ff01 0001 00 - renegotiation_info +// 0023 0000 - session_ticket +// 000a 0008 0006001700180019 - supported_groups +// 000b 0002 0100 - ec_point_formats +// 000d 0016 00140401040305010503060106030301030302010203 - signature_algorithms }; struct client_hello *record; int len; @@ -1084,6 +1136,7 @@ static void send_client_hello(tls_state_t *tls, const char *sni) len += 11 + strlen(sni); record = tls_get_outbuf(tls, len); memset(record, 0, len); + fill_handshake_record_hdr(record, HANDSHAKE_CLIENT_HELLO, len); record->proto_maj = TLS_MAJ; /* the "requested" version of the protocol, */ record->proto_min = TLS_MIN; /* can be higher than one in record headers */ @@ -1092,10 +1145,16 @@ static void send_client_hello(tls_state_t *tls, const char *sni) memset(record->rand32, 0x11, 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; - record->cipherid[0] = CIPHER_ID >> 8; + record->cipherid_len16_lo = 2 * 2; + if ((CIPHER_ID >> 8) != 0) + record->cipherid[0] = CIPHER_ID >> 8; record->cipherid[1] = CIPHER_ID & 0xff; + /* RFC 5746 Renegotiation Indication Extension - some servers will refuse to work with us otherwise */ + /*record->cipherid[2] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV >> 8; - zero */ + record->cipherid[3] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV & 0xff; + record->comprtypes_len = 1; /* record->comprtypes[0] = 0; */ @@ -1136,8 +1195,9 @@ static void get_server_hello(tls_state_t *tls) struct server_hello *hp; uint8_t *cipherid; + int len; - tls_xread_handshake_block(tls, 74); + len = tls_xread_handshake_block(tls, 74); hp = (void*)tls->inbuf; // 74 bytes: @@ -1150,7 +1210,7 @@ static void get_server_hello(tls_state_t *tls) || hp->proto_maj != TLS_MAJ || hp->proto_min != TLS_MIN ) { - tls_error_die(tls); + bad_record_die(tls, "'server hello'", len); } cipherid = &hp->cipherid_hi; |