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