diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-01-20 19:11:14 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-01-20 19:11:14 +0100 |
commit | 38972a8df1df970a5878bbd7fc5ef6259f1168ab (patch) | |
tree | d205e830156c71b04058e8e7d28c2bda8f6a67df /networking/tls.c | |
parent | e7863f394e6a963d1e2c6af77ea064442d3ef594 (diff) | |
download | busybox-w32-38972a8df1df970a5878bbd7fc5ef6259f1168ab.tar.gz busybox-w32-38972a8df1df970a5878bbd7fc5ef6259f1168ab.tar.bz2 busybox-w32-38972a8df1df970a5878bbd7fc5ef6259f1168ab.zip |
tls: improve i/o loop
With tls_has_buffered_record(), entire kernel.org response
is printed at once, without 6 second pause to see its delayed EOF.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'networking/tls.c')
-rw-r--r-- | networking/tls.c | 130 |
1 files changed, 92 insertions, 38 deletions
diff --git a/networking/tls.c b/networking/tls.c index cdce1004e..000d2aedc 100644 --- a/networking/tls.c +++ b/networking/tls.c | |||
@@ -48,7 +48,7 @@ | |||
48 | # define dump_raw_out(...) ((void)0) | 48 | # define dump_raw_out(...) ((void)0) |
49 | #endif | 49 | #endif |
50 | 50 | ||
51 | #if 1 | 51 | #if 0 |
52 | # define dump_raw_in(...) dump_hex(__VA_ARGS__) | 52 | # define dump_raw_in(...) dump_hex(__VA_ARGS__) |
53 | #else | 53 | #else |
54 | # define dump_raw_in(...) ((void)0) | 54 | # define dump_raw_in(...) ((void)0) |
@@ -72,9 +72,6 @@ | |||
72 | #define HANDSHAKE_CLIENT_KEY_EXCHANGE 16 | 72 | #define HANDSHAKE_CLIENT_KEY_EXCHANGE 16 |
73 | #define HANDSHAKE_FINISHED 20 | 73 | #define HANDSHAKE_FINISHED 20 |
74 | 74 | ||
75 | #define SSL_HS_RANDOM_SIZE 32 | ||
76 | #define SSL_HS_RSA_PREMASTER_SIZE 48 | ||
77 | |||
78 | #define SSL_NULL_WITH_NULL_NULL 0x0000 | 75 | #define SSL_NULL_WITH_NULL_NULL 0x0000 |
79 | #define SSL_RSA_WITH_NULL_MD5 0x0001 | 76 | #define SSL_RSA_WITH_NULL_MD5 0x0001 |
80 | #define SSL_RSA_WITH_NULL_SHA 0x0002 | 77 | #define SSL_RSA_WITH_NULL_SHA 0x0002 |
@@ -175,12 +172,15 @@ enum { | |||
175 | AES128_KEYSIZE = 16, | 172 | AES128_KEYSIZE = 16, |
176 | AES256_KEYSIZE = 32, | 173 | AES256_KEYSIZE = 32, |
177 | 174 | ||
175 | RSA_PREMASTER_SIZE = 48, | ||
176 | |||
178 | RECHDR_LEN = 5, | 177 | RECHDR_LEN = 5, |
179 | 178 | ||
180 | MAX_TLS_RECORD = (1 << 14), | 179 | MAX_TLS_RECORD = (1 << 14), |
180 | /* 8 = 3+5. 3 extra bytes result in record data being 32-bit aligned */ | ||
181 | OUTBUF_PFX = 8 + AES_BLOCKSIZE, /* header + IV */ | 181 | OUTBUF_PFX = 8 + AES_BLOCKSIZE, /* header + IV */ |
182 | OUTBUF_SFX = SHA256_OUTSIZE + AES_BLOCKSIZE, /* MAC + padding */ | 182 | OUTBUF_SFX = SHA256_OUTSIZE + AES_BLOCKSIZE, /* MAC + padding */ |
183 | MAX_OTBUF = MAX_TLS_RECORD - OUTBUF_PFX - OUTBUF_SFX, | 183 | MAX_OUTBUF = MAX_TLS_RECORD - OUTBUF_PFX - OUTBUF_SFX, |
184 | }; | 184 | }; |
185 | 185 | ||
186 | struct record_hdr { | 186 | struct record_hdr { |
@@ -205,20 +205,21 @@ typedef struct tls_state { | |||
205 | uint8_t server_write_MAC_key[SHA256_OUTSIZE]; | 205 | uint8_t server_write_MAC_key[SHA256_OUTSIZE]; |
206 | uint8_t client_write_key[AES256_KEYSIZE]; | 206 | uint8_t client_write_key[AES256_KEYSIZE]; |
207 | uint8_t server_write_key[AES256_KEYSIZE]; | 207 | uint8_t server_write_key[AES256_KEYSIZE]; |
208 | // RFC 5246 | 208 | |
209 | // sequence number | 209 | // RFC 5246 |
210 | // Each connection state contains a sequence number, which is | 210 | // sequence number |
211 | // maintained separately for read and write states. The sequence | 211 | // Each connection state contains a sequence number, which is |
212 | // number MUST be set to zero whenever a connection state is made the | 212 | // maintained separately for read and write states. The sequence |
213 | // active state. Sequence numbers are of type uint64 and may not | 213 | // number MUST be set to zero whenever a connection state is made the |
214 | // exceed 2^64-1. | 214 | // active state. Sequence numbers are of type uint64 and may not |
215 | // exceed 2^64-1. | ||
215 | uint64_t write_seq64_be; | 216 | uint64_t write_seq64_be; |
216 | 217 | ||
217 | int outbuf_size; | 218 | int outbuf_size; |
218 | uint8_t *outbuf; | 219 | uint8_t *outbuf; |
219 | 220 | ||
220 | // RFC 5246 | 221 | // RFC 5246 |
221 | // |6.2.1. Fragmentation | 222 | // | 6.2.1. Fragmentation |
222 | // | The record layer fragments information blocks into TLSPlaintext | 223 | // | The record layer fragments information blocks into TLSPlaintext |
223 | // | records carrying data in chunks of 2^14 bytes or less. Client | 224 | // | records carrying data in chunks of 2^14 bytes or less. Client |
224 | // | message boundaries are not preserved in the record layer (i.e., | 225 | // | message boundaries are not preserved in the record layer (i.e., |
@@ -290,6 +291,9 @@ static void dump_tls_record(const void *vp, int len) | |||
290 | len -= xhdr_len; | 291 | len -= xhdr_len; |
291 | } | 292 | } |
292 | } | 293 | } |
294 | #else | ||
295 | # define dump_hex(...) ((void)0) | ||
296 | # define dump_tls_record(...) ((void)0) | ||
293 | #endif | 297 | #endif |
294 | 298 | ||
295 | void tls_get_random(void *buf, unsigned len) | 299 | void tls_get_random(void *buf, unsigned len) |
@@ -480,12 +484,19 @@ static tls_state_t *new_tls_state(void) | |||
480 | static void tls_error_die(tls_state_t *tls) | 484 | static void tls_error_die(tls_state_t *tls) |
481 | { | 485 | { |
482 | dump_tls_record(tls->inbuf, tls->insize + tls->tail); | 486 | dump_tls_record(tls->inbuf, tls->insize + tls->tail); |
483 | xfunc_die(); | 487 | bb_error_msg_and_die("TODO: useful diagnostic about %p", tls); |
488 | } | ||
489 | |||
490 | static void tls_free_outbuf(tls_state_t *tls) | ||
491 | { | ||
492 | free(tls->outbuf); | ||
493 | tls->outbuf_size = 0; | ||
494 | tls->outbuf = NULL; | ||
484 | } | 495 | } |
485 | 496 | ||
486 | static void *tls_get_outbuf(tls_state_t *tls, int len) | 497 | static void *tls_get_outbuf(tls_state_t *tls, int len) |
487 | { | 498 | { |
488 | if (len > MAX_OTBUF) | 499 | if (len > MAX_OUTBUF) |
489 | xfunc_die(); | 500 | xfunc_die(); |
490 | if (tls->outbuf_size < len + OUTBUF_PFX + OUTBUF_SFX) { | 501 | if (tls->outbuf_size < len + OUTBUF_PFX + OUTBUF_SFX) { |
491 | tls->outbuf_size = len + OUTBUF_PFX + OUTBUF_SFX; | 502 | tls->outbuf_size = len + OUTBUF_PFX + OUTBUF_SFX; |
@@ -670,7 +681,22 @@ static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size) | |||
670 | xwrite_encrypted(tls, size, RECORD_TYPE_HANDSHAKE); | 681 | xwrite_encrypted(tls, size, RECORD_TYPE_HANDSHAKE); |
671 | } | 682 | } |
672 | 683 | ||
673 | static int xread_tls_block(tls_state_t *tls) | 684 | static int tls_has_buffered_record(tls_state_t *tls) |
685 | { | ||
686 | int buffered = tls->tail; | ||
687 | struct record_hdr *xhdr; | ||
688 | int rec_size; | ||
689 | |||
690 | if (buffered < RECHDR_LEN) | ||
691 | return 0; | ||
692 | xhdr = (void*)(tls->inbuf + tls->insize); | ||
693 | rec_size = RECHDR_LEN + (0x100 * xhdr->len16_hi + xhdr->len16_lo); | ||
694 | if (buffered < rec_size) | ||
695 | return 0; | ||
696 | return rec_size; | ||
697 | } | ||
698 | |||
699 | static int tls_xread_record(tls_state_t *tls) | ||
674 | { | 700 | { |
675 | struct record_hdr *xhdr; | 701 | struct record_hdr *xhdr; |
676 | int sz; | 702 | int sz; |
@@ -1012,7 +1038,7 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len) | |||
1012 | static int xread_tls_handshake_block(tls_state_t *tls, int min_len) | 1038 | static int xread_tls_handshake_block(tls_state_t *tls, int min_len) |
1013 | { | 1039 | { |
1014 | struct record_hdr *xhdr; | 1040 | struct record_hdr *xhdr; |
1015 | int len = xread_tls_block(tls); | 1041 | int len = tls_xread_record(tls); |
1016 | 1042 | ||
1017 | xhdr = (void*)tls->inbuf; | 1043 | xhdr = (void*)tls->inbuf; |
1018 | if (len < min_len | 1044 | if (len < min_len |
@@ -1177,7 +1203,7 @@ static void send_client_key_exchange(tls_state_t *tls) | |||
1177 | }; | 1203 | }; |
1178 | //FIXME: better size estimate | 1204 | //FIXME: better size estimate |
1179 | struct client_key_exchange *record = tls_get_outbuf(tls, sizeof(*record)); | 1205 | struct client_key_exchange *record = tls_get_outbuf(tls, sizeof(*record)); |
1180 | uint8_t rsa_premaster[SSL_HS_RSA_PREMASTER_SIZE]; | 1206 | uint8_t rsa_premaster[RSA_PREMASTER_SIZE]; |
1181 | int len; | 1207 | int len; |
1182 | 1208 | ||
1183 | tls_get_random(rsa_premaster, sizeof(rsa_premaster)); | 1209 | tls_get_random(rsa_premaster, sizeof(rsa_premaster)); |
@@ -1383,7 +1409,7 @@ static void tls_handshake(tls_state_t *tls) | |||
1383 | send_client_hello(tls); | 1409 | send_client_hello(tls); |
1384 | get_server_hello(tls); | 1410 | get_server_hello(tls); |
1385 | 1411 | ||
1386 | //RFC 5246 | 1412 | // RFC 5246 |
1387 | // The server MUST send a Certificate message whenever the agreed- | 1413 | // The server MUST send a Certificate message whenever the agreed- |
1388 | // upon key exchange method uses certificates for authentication | 1414 | // upon key exchange method uses certificates for authentication |
1389 | // (this includes all key exchange methods defined in this document | 1415 | // (this includes all key exchange methods defined in this document |
@@ -1408,7 +1434,7 @@ static void tls_handshake(tls_state_t *tls) | |||
1408 | 1434 | ||
1409 | // if (tls->inbuf[RECHDR_LEN] == HANDSHAKE_CERTIFICATE_REQUEST) { | 1435 | // if (tls->inbuf[RECHDR_LEN] == HANDSHAKE_CERTIFICATE_REQUEST) { |
1410 | // dbg("<< CERTIFICATE_REQUEST\n"); | 1436 | // dbg("<< CERTIFICATE_REQUEST\n"); |
1411 | //RFC 5246: (in response to this,) "If no suitable certificate is available, | 1437 | // RFC 5246: (in response to this,) "If no suitable certificate is available, |
1412 | // the client MUST send a certificate message containing no | 1438 | // the client MUST send a certificate message containing no |
1413 | // certificates. That is, the certificate_list structure has a | 1439 | // certificates. That is, the certificate_list structure has a |
1414 | // length of zero. ... | 1440 | // length of zero. ... |
@@ -1433,7 +1459,7 @@ static void tls_handshake(tls_state_t *tls) | |||
1433 | send_client_finished(tls); | 1459 | send_client_finished(tls); |
1434 | 1460 | ||
1435 | /* Get CHANGE_CIPHER_SPEC */ | 1461 | /* Get CHANGE_CIPHER_SPEC */ |
1436 | len = xread_tls_block(tls); | 1462 | len = tls_xread_record(tls); |
1437 | if (len != 1 || memcmp(tls->inbuf, rec_CHANGE_CIPHER_SPEC, 6) != 0) | 1463 | if (len != 1 || memcmp(tls->inbuf, rec_CHANGE_CIPHER_SPEC, 6) != 0) |
1438 | tls_error_die(tls); | 1464 | tls_error_die(tls); |
1439 | dbg("<< CHANGE_CIPHER_SPEC\n"); | 1465 | dbg("<< CHANGE_CIPHER_SPEC\n"); |
@@ -1444,7 +1470,7 @@ static void tls_handshake(tls_state_t *tls) | |||
1444 | tls->min_encrypted_len_on_read = AES_BLOCKSIZE + SHA256_OUTSIZE + AES_BLOCKSIZE; | 1470 | tls->min_encrypted_len_on_read = AES_BLOCKSIZE + SHA256_OUTSIZE + AES_BLOCKSIZE; |
1445 | 1471 | ||
1446 | /* Get (encrypted) FINISHED from the server */ | 1472 | /* Get (encrypted) FINISHED from the server */ |
1447 | len = xread_tls_block(tls); | 1473 | len = tls_xread_record(tls); |
1448 | if (len < 4 || tls->inbuf[RECHDR_LEN] != HANDSHAKE_FINISHED) | 1474 | if (len < 4 || tls->inbuf[RECHDR_LEN] != HANDSHAKE_FINISHED) |
1449 | tls_error_die(tls); | 1475 | tls_error_die(tls); |
1450 | dbg("<< FINISHED\n"); | 1476 | dbg("<< FINISHED\n"); |
@@ -1473,9 +1499,12 @@ int tls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |||
1473 | int tls_main(int argc UNUSED_PARAM, char **argv) | 1499 | int tls_main(int argc UNUSED_PARAM, char **argv) |
1474 | { | 1500 | { |
1475 | tls_state_t *tls; | 1501 | tls_state_t *tls; |
1476 | fd_set readfds, testfds; | 1502 | fd_set readfds; |
1503 | int inbuf_size; | ||
1504 | const int INBUF_STEP = 4 * 1024; | ||
1477 | int cfd; | 1505 | int cfd; |
1478 | 1506 | ||
1507 | |||
1479 | // INIT_G(); | 1508 | // INIT_G(); |
1480 | // getopt32(argv, "myopts") | 1509 | // getopt32(argv, "myopts") |
1481 | 1510 | ||
@@ -1493,13 +1522,12 @@ int tls_main(int argc UNUSED_PARAM, char **argv) | |||
1493 | FD_SET(cfd, &readfds); | 1522 | FD_SET(cfd, &readfds); |
1494 | FD_SET(STDIN_FILENO, &readfds); | 1523 | FD_SET(STDIN_FILENO, &readfds); |
1495 | 1524 | ||
1496 | //#define iobuf bb_common_bufsiz1 | 1525 | inbuf_size = INBUF_STEP; |
1497 | // setup_common_bufsiz(); | ||
1498 | for (;;) { | 1526 | for (;;) { |
1527 | fd_set testfds; | ||
1499 | int nread; | 1528 | int nread; |
1500 | 1529 | ||
1501 | testfds = readfds; | 1530 | testfds = readfds; |
1502 | |||
1503 | if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0) | 1531 | if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0) |
1504 | bb_perror_msg_and_die("select"); | 1532 | bb_perror_msg_and_die("select"); |
1505 | 1533 | ||
@@ -1507,26 +1535,52 @@ int tls_main(int argc UNUSED_PARAM, char **argv) | |||
1507 | void *buf; | 1535 | void *buf; |
1508 | 1536 | ||
1509 | dbg("STDIN HAS DATA\n"); | 1537 | dbg("STDIN HAS DATA\n"); |
1510 | //TODO: growable buffer | 1538 | buf = tls_get_outbuf(tls, inbuf_size); |
1511 | buf = tls_get_outbuf(tls, 4 * 1024); | 1539 | nread = safe_read(STDIN_FILENO, buf, inbuf_size); |
1512 | nread = safe_read(STDIN_FILENO, buf, 4 * 1024); | ||
1513 | if (nread < 1) { | 1540 | if (nread < 1) { |
1514 | //&& errno != EAGAIN | 1541 | /* We'd want to do this: */ |
1515 | /* Close outgoing half-connection so they get EOF, | 1542 | /* Close outgoing half-connection so they get EOF, |
1516 | * but leave incoming alone so we can see response */ | 1543 | * but leave incoming alone so we can see response |
1517 | //TLS has no way to encode this, doubt it's ok to do it "raw" | 1544 | */ |
1518 | // shutdown(cfd, SHUT_WR); | 1545 | //shutdown(cfd, SHUT_WR); |
1546 | /* But TLS has no way to encode this, | ||
1547 | * doubt it's ok to do it "raw" | ||
1548 | */ | ||
1519 | FD_CLR(STDIN_FILENO, &readfds); | 1549 | FD_CLR(STDIN_FILENO, &readfds); |
1550 | tls_free_outbuf(tls); | ||
1551 | } else { | ||
1552 | if (nread == inbuf_size) { | ||
1553 | /* TLS has per record overhead, if input comes fast, | ||
1554 | * read, encrypt and send bigger chunks | ||
1555 | */ | ||
1556 | inbuf_size += INBUF_STEP; | ||
1557 | if (inbuf_size > MAX_OUTBUF) | ||
1558 | inbuf_size = MAX_OUTBUF; | ||
1559 | } | ||
1560 | tls_xwrite(tls, nread); | ||
1520 | } | 1561 | } |
1521 | tls_xwrite(tls, nread); | ||
1522 | } | 1562 | } |
1523 | if (FD_ISSET(cfd, &testfds)) { | 1563 | if (FD_ISSET(cfd, &testfds)) { |
1524 | dbg("NETWORK HAS DATA\n"); | 1564 | dbg("NETWORK HAS DATA\n"); |
1525 | nread = xread_tls_block(tls); | 1565 | read_record: |
1526 | if (nread < 1) | 1566 | nread = tls_xread_record(tls); |
1527 | //TODO: if eof, just close stdout, but not exit! | 1567 | if (nread < 1) { |
1528 | return EXIT_SUCCESS; | 1568 | /* TLS protocol has no real concept of one-sided shutdowns: |
1569 | * if we get "TLS EOF" from the peer, writes will fail too | ||
1570 | */ | ||
1571 | //FD_CLR(cfd, &readfds); | ||
1572 | //close(STDOUT_FILENO); | ||
1573 | //continue; | ||
1574 | break; | ||
1575 | } | ||
1576 | if (tls->inbuf[0] != RECORD_TYPE_APPLICATION_DATA) | ||
1577 | bb_error_msg_and_die("unexpected record type %d", tls->inbuf[0]); | ||
1529 | xwrite(STDOUT_FILENO, tls->inbuf + RECHDR_LEN, nread); | 1578 | xwrite(STDOUT_FILENO, tls->inbuf + RECHDR_LEN, nread); |
1579 | /* We may already have a complete next record buffered, | ||
1580 | * can process it without network reads (and possible blocking) | ||
1581 | */ | ||
1582 | if (tls_has_buffered_record(tls)) | ||
1583 | goto read_record; | ||
1530 | } | 1584 | } |
1531 | } | 1585 | } |
1532 | 1586 | ||