From 0acc52519de732f4f010e1029e1308cee825eaed Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 30 Jan 2013 16:58:17 +0000 Subject: [PATCH] [tls] Concatenate received non-data records before processing Allow non-data records to be split across multiple received I/O buffers, to accommodate large certificate chains. Reported-by: Nicola Volpini Tested-by: Nicola Volpini Signed-off-by: Michael Brown --- src/core/iobuf.c | 42 ++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/iobuf.h | 1 + src/net/tls.c | 22 ++++++++++----------- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/core/iobuf.c b/src/core/iobuf.c index 3f67d2f5e..afc91d150 100644 --- a/src/core/iobuf.c +++ b/src/core/iobuf.c @@ -158,3 +158,45 @@ int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) { return -ENOBUFS; } +/** + * Concatenate I/O buffers into a single buffer + * + * @v list List of I/O buffers + * @ret iobuf Concatenated I/O buffer, or NULL on allocation failure + * + * After a successful concatenation, the list will be empty. + */ +struct io_buffer * iob_concatenate ( struct list_head *list ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + struct io_buffer *concatenated; + size_t len = 0; + + /* If the list contains only a single entry, avoid an + * unnecessary additional allocation. + */ + if ( list_is_singular ( list ) ) { + iobuf = list_first_entry ( list, struct io_buffer, list ); + INIT_LIST_HEAD ( list ); + return iobuf; + } + + /* Calculate total length */ + list_for_each_entry ( iobuf, list, list ) + len += iob_len ( iobuf ); + + /* Allocate new I/O buffer */ + concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 ); + if ( ! concatenated ) + return NULL; + + /* Move data to new I/O buffer */ + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ), + iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + } + + return concatenated; +} diff --git a/src/include/ipxe/iobuf.h b/src/include/ipxe/iobuf.h index 65a8e80da..b2b0cb440 100644 --- a/src/include/ipxe/iobuf.h +++ b/src/include/ipxe/iobuf.h @@ -216,5 +216,6 @@ extern struct io_buffer * __malloc alloc_iob ( size_t len ); extern void free_iob ( struct io_buffer *iobuf ); extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ); +extern struct io_buffer * iob_concatenate ( struct list_head *list ); #endif /* _IPXE_IOBUF_H */ diff --git a/src/net/tls.c b/src/net/tls.c index 4ad131c87..5e18f7266 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -105,10 +105,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EINVAL_MAC \ __einfo_uniqify ( EINFO_EINVAL, 0x0d, \ "Invalid MAC" ) -#define EINVAL_NON_DATA __einfo_error ( EINFO_EINVAL_NON_DATA ) -#define EINFO_EINVAL_NON_DATA \ - __einfo_uniqify ( EINFO_EINVAL, 0x0e, \ - "Overlength non-data record" ) #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ __einfo_uniqify ( EINFO_EINVAL, 0x01, \ @@ -137,6 +133,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_ENOMEM_RX_DATA \ __einfo_uniqify ( EINFO_ENOMEM, 0x07, \ "Not enough space for received data" ) +#define ENOMEM_RX_CONCAT __einfo_error ( EINFO_ENOMEM_RX_CONCAT ) +#define EINFO_ENOMEM_RX_CONCAT \ + __einfo_uniqify ( EINFO_ENOMEM, 0x08, \ + "Not enough space to concatenate received data" ) #define ENOTSUP_CIPHER __einfo_error ( EINFO_ENOTSUP_CIPHER ) #define EINFO_ENOTSUP_CIPHER \ __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ @@ -1743,14 +1743,12 @@ static int tls_new_record ( struct tls_session *tls, unsigned int type, return 0; } - /* For all other records, fail unless we have exactly one I/O buffer */ - iobuf = list_first_entry ( rx_data, struct io_buffer, list ); - assert ( iobuf != NULL ); - list_del ( &iobuf->list ); - if ( ! list_empty ( rx_data ) ) { - DBGC ( tls, "TLS %p overlength non-data record\n", tls ); - free_iob ( iobuf ); - return -EINVAL_NON_DATA; + /* For all other records, merge into a single I/O buffer */ + iobuf = iob_concatenate ( rx_data ); + if ( ! iobuf ) { + DBGC ( tls, "TLS %p could not concatenate non-data record " + "type %d\n", tls, type ); + return -ENOMEM_RX_CONCAT; } /* Determine handler */