tls: make input buffer grow as needed

As it turns out, it goes only up to "inbuf_size:4608"
for kernel.org - fixed 18kb buffer was x4 larger than necessary.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-01-20 20:27:06 +01:00
parent 9731ca7611
commit 3916139ac4

View File

@ -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;
}