diff --git a/networking/tls.c b/networking/tls.c index 000d2aedc..55ad2d83f 100644 --- a/networking/tls.c +++ b/networking/tls.c @@ -181,6 +181,42 @@ enum { OUTBUF_PFX = 8 + AES_BLOCKSIZE, /* header + IV */ OUTBUF_SFX = SHA256_OUTSIZE + AES_BLOCKSIZE, /* MAC + padding */ MAX_OUTBUF = MAX_TLS_RECORD - OUTBUF_PFX - OUTBUF_SFX, + + // RFC 5246 + // | 6.2.1. Fragmentation + // | The record layer fragments information blocks into TLSPlaintext + // | records carrying data in chunks of 2^14 bytes or less. Client + // | message boundaries are not preserved in the record layer (i.e., + // | multiple client messages of the same ContentType MAY be coalesced + // | into a single TLSPlaintext record, or a single message MAY be + // | fragmented across several records) + // |... + // | length + // | The length (in bytes) of the following TLSPlaintext.fragment. + // | The length MUST NOT exceed 2^14. + // |... + // | 6.2.2. Record Compression and Decompression + // |... + // | Compression must be lossless and may not increase the content length + // | by more than 1024 bytes. If the decompression function encounters a + // | TLSCompressed.fragment that would decompress to a length in excess of + // | 2^14 bytes, it MUST report a fatal decompression failure error. + // |... + // | length + // | The length (in bytes) of the following TLSCompressed.fragment. + // | The length MUST NOT exceed 2^14 + 1024. + // |... + // | 6.2.3. Record Payload Protection + // | The encryption and MAC functions translate a TLSCompressed + // | structure into a TLSCiphertext. The decryption functions reverse + // | the process. The MAC of the record also includes a sequence + // | number so that missing, extra, or repeated messages are + // | detectable. + // |... + // | length + // | The length (in bytes) of the following TLSCiphertext.fragment. + // | The length MUST NOT exceed 2^14 + 2048. + MAX_INBUF = (1 << 14) + 2048, }; struct record_hdr { @@ -218,36 +254,10 @@ typedef struct tls_state { int outbuf_size; uint8_t *outbuf; - // RFC 5246 - // | 6.2.1. Fragmentation - // | The record layer fragments information blocks into TLSPlaintext - // | records carrying data in chunks of 2^14 bytes or less. Client - // | message boundaries are not preserved in the record layer (i.e., - // | multiple client messages of the same ContentType MAY be coalesced - // | into a single TLSPlaintext record, or a single message MAY be - // | fragmented across several records) - // |... - // | length - // | The length (in bytes) of the following TLSPlaintext.fragment. - // | The length MUST NOT exceed 2^14. - // |... - // | 6.2.2. Record Compression and Decompression - // |... - // | Compression must be lossless and may not increase the content length - // | by more than 1024 bytes. If the decompression function encounters a - // | TLSCompressed.fragment that would decompress to a length in excess of - // | 2^14 bytes, it MUST report a fatal decompression failure error. - // |... - // | length - // | The length (in bytes) of the following TLSCompressed.fragment. - // | The length MUST NOT exceed 2^14 + 1024. - // - // Since our buffer also contains 5-byte headers, make it a bit bigger: - int insize; - int tail; -//needed? - uint64_t align____; - uint8_t inbuf[20*1024]; + int inbuf_size; + int ofs_to_buffered; + int buffered_size; + uint8_t *inbuf; } tls_state_t; @@ -483,10 +493,21 @@ static tls_state_t *new_tls_state(void) static void tls_error_die(tls_state_t *tls) { - dump_tls_record(tls->inbuf, tls->insize + tls->tail); + dump_tls_record(tls->inbuf, tls->ofs_to_buffered + tls->buffered_size); bb_error_msg_and_die("TODO: useful diagnostic about %p", tls); } +#if 0 //UNUSED +static void tls_free_inbuf(tls_state_t *tls) +{ + if (tls->buffered_size == 0) { + free(tls->inbuf); + tls->inbuf_size = 0; + tls->inbuf = NULL; + } +} +#endif + static void tls_free_outbuf(tls_state_t *tls) { free(tls->outbuf); @@ -683,13 +704,13 @@ static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size) static int tls_has_buffered_record(tls_state_t *tls) { - int buffered = tls->tail; + int buffered = tls->buffered_size; struct record_hdr *xhdr; int rec_size; if (buffered < RECHDR_LEN) return 0; - xhdr = (void*)(tls->inbuf + tls->insize); + xhdr = (void*)(tls->inbuf + tls->ofs_to_buffered); rec_size = RECHDR_LEN + (0x100 * xhdr->len16_hi + xhdr->len16_lo); if (buffered < rec_size) return 0; @@ -704,23 +725,25 @@ static int tls_xread_record(tls_state_t *tls) int target; again: - dbg("insize:%u tail:%u\n", tls->insize, tls->tail); - total = tls->tail; + dbg("ofs_to_buffered:%u buffered_size:%u\n", tls->ofs_to_buffered, tls->buffered_size); + total = tls->buffered_size; if (total != 0) { - memmove(tls->inbuf, tls->inbuf + tls->insize, total); - //dbg("<< remaining at %d [%d] ", tls->insize, total); + memmove(tls->inbuf, tls->inbuf + tls->ofs_to_buffered, total); + //dbg("<< remaining at %d [%d] ", tls->ofs_to_buffered, total); //dump_raw_in("<< %s\n", tls->inbuf, total); } errno = 0; - target = sizeof(tls->inbuf); + target = MAX_INBUF; for (;;) { - if (total >= RECHDR_LEN && target == sizeof(tls->inbuf)) { + int rem; + + if (total >= RECHDR_LEN && target == MAX_INBUF) { xhdr = (void*)tls->inbuf; target = RECHDR_LEN + (0x100 * xhdr->len16_hi + xhdr->len16_lo); - if (target >= sizeof(tls->inbuf)) { + if (target > MAX_INBUF) { /* malformed input (too long): yell and die */ - tls->tail = 0; - tls->insize = total; + tls->buffered_size = 0; + tls->ofs_to_buffered = total; tls_error_die(tls); } /* can also check type/proto_maj/proto_min here */ @@ -732,12 +755,22 @@ static int tls_xread_record(tls_state_t *tls) /* if total >= target, we have a full packet (and possibly more)... */ if (total - target >= 0) break; - sz = safe_read(tls->fd, tls->inbuf + total, sizeof(tls->inbuf) - total); + /* input buffer is grown only as needed */ + rem = tls->inbuf_size - total; + if (rem == 0) { + tls->inbuf_size += MAX_INBUF / 8; + if (tls->inbuf_size > MAX_INBUF) + tls->inbuf_size = MAX_INBUF; + dbg("inbuf_size:%d\n", tls->inbuf_size); + rem = tls->inbuf_size - total; + tls->inbuf = xrealloc(tls->inbuf, tls->inbuf_size); + } + sz = safe_read(tls->fd, tls->inbuf + total, rem); if (sz <= 0) { if (sz == 0 && total == 0) { /* "Abrupt" EOF, no TLS shutdown (seen from kernel.org) */ dbg("EOF (without TLS shutdown) from peer\n"); - tls->tail = 0; + tls->buffered_size = 0; goto end; } bb_perror_msg_and_die("short read, have only %d", total); @@ -745,10 +778,10 @@ static int tls_xread_record(tls_state_t *tls) dump_raw_in("<< %s\n", tls->inbuf + total, sz); total += sz; } - tls->tail = total - target; - tls->insize = target; - //dbg("<< stashing at %d [%d] ", tls->insize, tls->tail); - //dump_hex("<< %s\n", tls->inbuf + tls->insize, tls->tail); + tls->buffered_size = total - target; + tls->ofs_to_buffered = target; + //dbg("<< stashing at %d [%d] ", tls->ofs_to_buffered, tls->buffered_size); + //dump_hex("<< %s\n", tls->inbuf + tls->ofs_to_buffered, tls->buffered_size); sz = target - RECHDR_LEN; @@ -1547,7 +1580,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) * doubt it's ok to do it "raw" */ FD_CLR(STDIN_FILENO, &readfds); - tls_free_outbuf(tls); + tls_free_outbuf(tls); /* mem usage optimization */ } else { if (nread == inbuf_size) { /* TLS has per record overhead, if input comes fast, @@ -1570,6 +1603,7 @@ int tls_main(int argc UNUSED_PARAM, char **argv) */ //FD_CLR(cfd, &readfds); //close(STDOUT_FILENO); + //tls_free_inbuf(tls); /* mem usage optimization */ //continue; break; }