From b7273855acd7ec2d1e7a4ba626ed538808fc7517 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 3 Aug 2016 17:06:39 +0100 Subject: [PATCH] First pass at writing a writeable packets API Reviewed-by: Rich Salz --- ssl/build.info | 2 +- ssl/packet.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++ ssl/packet_locl.h | 76 +++++++++++ 3 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 ssl/packet.c diff --git a/ssl/build.info b/ssl/build.info index 69772465d9..c7d4574884 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -1,6 +1,6 @@ LIBS=../libssl SOURCE[../libssl]=\ - pqueue.c \ + pqueue.c packet.c \ statem/statem_srvr.c statem/statem_clnt.c s3_lib.c s3_enc.c record/rec_layer_s3.c \ statem/statem_lib.c s3_cbc.c s3_msg.c \ methods.c t1_lib.c t1_enc.c t1_ext.c \ diff --git a/ssl/packet.c b/ssl/packet.c new file mode 100644 index 0000000000..8528d8be08 --- /dev/null +++ b/ssl/packet.c @@ -0,0 +1,335 @@ +/* + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "packet_locl.h" + +/* + * Allocate bytes in the PACKETW_BUF for the output. This reserves the bytes + * and count them as "written", but doesn't actually do the writing. + */ +static unsigned char *PACKETW_BUF_allocate(PACKETW_BUF *wbuf, size_t len) +{ + unsigned char *ret = wbuf->curr; + + if (SIZE_MAX - wbuf->written < len) + return 0; + + if (wbuf->maxsize > 0 && wbuf->written + len > wbuf->maxsize) + return 0; + + if (wbuf->buf->length - wbuf->written < len) { + size_t newlen; + + if (wbuf->buf->length > SIZE_MAX / 2) + newlen = SIZE_MAX; + else + newlen = wbuf->buf->length * 2; + if (BUF_MEM_grow(wbuf->buf, newlen) == 0) + return NULL; + } + wbuf->written += len; + wbuf->curr += len; + + return ret; +} + +/* + * Initialise a PACKETW with the buffer in |buf|. The buffer must exist + * for the whole time that the PACKETW is being used. Additionally |lenbytes| of + * data is preallocated at the start of the buffer to store the length of the + * PACKETW once we know it. + */ +int PACKETW_init_len(PACKETW *pkt, BUF_MEM *buf, size_t lenbytes) +{ + PACKETW_BUF *wbuf; + /* Sanity check */ + if (buf == NULL) + return 0; + + wbuf = OPENSSL_zalloc(sizeof(PACKETW_BUF)); + if (wbuf == NULL) { + pkt->isclosed = 1; + return 0; + } + + wbuf->buf = buf; + wbuf->curr = (unsigned char *)buf->data; + wbuf->written = 0; + wbuf->maxsize = 0; + + pkt->parent = NULL; + pkt->wbuf = wbuf; + pkt->pwritten = lenbytes; + pkt->lenbytes = lenbytes; + pkt->haschild = 0; + pkt->isclosed = 0; + + if (lenbytes == 0) { + pkt->packet_len = NULL; + return 1; + } + + pkt->packet_len = PACKETW_BUF_allocate(wbuf, lenbytes); + if (pkt->packet_len == NULL) { + OPENSSL_free(wbuf); + pkt->wbuf = NULL; + pkt->isclosed = 1; + return 0; + } + + return 1; +} + +/* + * Same as PACKETW_init_len except there is no preallocation of the PACKETW + * length. + */ +int PACKETW_init(PACKETW *pkt, BUF_MEM *buf) +{ + return PACKETW_init_len(pkt, buf, 0); +} + +/* + * Set the PACKETW length, and the location for where we should write that + * length. Normally this will be at the start of the PACKETW, and therefore + * the PACKETW would have been initialised via PACKETW_init_len(). However there + * is the possibility that the length needs to be written to some other location + * other than the start of the PACKETW. In that case init via PACKETW_init() and + * then set the location for the length using this function. + */ +int PACKETW_set_packet_len(PACKETW *pkt, unsigned char *packet_len, + size_t lenbytes) +{ + /* We only allow this to be set once */ + if (pkt->isclosed || pkt->packet_len != NULL) + return 0; + + pkt->lenbytes = lenbytes; + pkt->packet_len = packet_len; + + return 1; +} + +int PACKETW_set_flags(PACKETW *pkt, unsigned int flags) +{ + pkt->flags = flags; + + return 1; +} + +/* + * Closes the PACKETW and marks it as invalid for future writes. It also writes + * out the length of the packet to the required location (normally the start + * of the PACKETW) if appropriate. A PACKETW cannot be closed if it has an + * active sub-packet. + */ +int PACKETW_close(PACKETW *pkt) +{ + size_t packlen; + + if (pkt->isclosed || pkt->haschild) + return 0; + + packlen = pkt->wbuf->written - pkt->pwritten; + if (packlen == 0 && pkt->flags & OPENSSL_PACKETW_FLAGS_NON_ZERO_LENGTH) + return 0; + + if (packlen == 0 + && pkt->flags & OPENSSL_PACKETW_FLAGS_ABANDON_ON_ZERO_LENGTH) { + /* Deallocate any bytes allocated for the length of the PACKETW */ + if ((pkt->wbuf->curr - pkt->lenbytes) == pkt->packet_len) { + pkt->wbuf->written -= pkt->lenbytes; + pkt->wbuf->curr -= pkt->lenbytes; + } + + /* Don't write out the packet length */ + pkt->packet_len = NULL; + } + + /* Write out the PACKETW length if needed */ + if (pkt->packet_len != NULL) { + size_t lenbytes; + + lenbytes = pkt->lenbytes; + + for (; lenbytes > 0; lenbytes--) { + pkt->packet_len[lenbytes - 1] = (unsigned char)(packlen & 0xff); + packlen >>= 8; + } + if (packlen > 0) { + /* + * We've extended beyond the max allowed for the number of len bytes + */ + return 0; + } + } + + if (pkt->parent != NULL) { + if (pkt->parent->haschild != 1) { + /* Should not happen! */ + return 0; + } + pkt->parent->haschild = 0; + pkt->parent = NULL; + } + + pkt->isclosed = 1; + + return 1; +} + +/* + * Initialise a new sub-packet (|subpkt|), based on a parent (|pkt|). + * Additionally |lenbytes| of data is preallocated at the start of the + * sub-packet to store its length once we know it. + */ +int PACKETW_get_sub_packet_len(PACKETW *pkt, PACKETW *subpkt, size_t lenbytes) +{ + if (pkt->isclosed || pkt->haschild || subpkt == NULL) + return 0; + + subpkt->parent = pkt; + subpkt->wbuf = pkt->wbuf; + subpkt->pwritten = pkt->wbuf->written + lenbytes; + subpkt->lenbytes = lenbytes; + subpkt->haschild = 0; + subpkt->isclosed = 0; + + if (lenbytes == 0) { + subpkt->packet_len = NULL; + pkt->haschild = 1; + return 1; + } + + subpkt->packet_len = PACKETW_BUF_allocate(pkt->wbuf, lenbytes); + if (subpkt->packet_len == NULL) { + subpkt->isclosed = 1; + return 0; + } + + pkt->haschild = 1; + + return 1; +} + +/* + * Same as PACKETW_get_sub_packet_len() except no bytes are pre-allocated for + * the sub-packet length. + */ +int PACKETW_get_sub_packet(PACKETW *pkt, PACKETW *subpkt) +{ + return PACKETW_get_sub_packet_len(pkt, subpkt, 0); +} + +/* + * Allocate some bytes in the PACKETW for writing. That number of bytes is + * marked as having been written, and a pointer to their location is stored in + * |*allocbytes|. + */ +int PACKETW_allocate_bytes(PACKETW *pkt, size_t bytes, + unsigned char **allocbytes) +{ + unsigned char *data; + + if (pkt->isclosed || pkt->haschild || bytes == 0) + return 0; + + data = PACKETW_BUF_allocate(pkt->wbuf, bytes); + if (data == NULL) + return 0; + + *allocbytes = data; + + return 1; +} + +/* + * Write the value stored in |val| into the PACKETW. The value will consome + * |bytes| amount of storage. An error will occur if |val| cannot be accommdated + * in |bytes| storage, e.g. attempting to write the value 256 into 1 byte will + * fail. + */ +int PACKETW_put_bytes(PACKETW *pkt, unsigned int val, size_t bytes) +{ + unsigned char *data; + + if (bytes > sizeof(unsigned int) + || !PACKETW_allocate_bytes(pkt, bytes, &data)) + return 0; + + data += bytes - 1; + for (; bytes > 0; bytes--) { + *data = (unsigned char)(val & 0xff); + data--; + val >>= 8; + } + + /* Check whether we could fit the value in the assigned number of bytes */ + if (val > 0) + return 0; + + return 1; +} + +/* + * Set a maximum size that we will not allow the PACKETW to grow beyond. If not + * set then there is no maximum. + */ +int PACKETW_set_max_size(PACKETW *pkt, size_t maxsize) +{ + pkt->wbuf->maxsize = maxsize; + + return 1; +} + +/* + * Copy |len| bytes of data from |*src| into the PACKETW. + */ +int PACKETW_memcpy(PACKETW *pkt, const void *src, size_t len) +{ + unsigned char *dest; + + if (len == 0) + return 1; + + if (!PACKETW_allocate_bytes(pkt, len, &dest)) + return 0; + + memcpy(dest, src, len); + + return 1; +} + +/* + * Return the total number of bytes written so far to the underlying buffer. + * This might includes bytes written by a parent PACKETW. + */ +int PACKETW_get_total_written(PACKETW *pkt, size_t *written) +{ + if (pkt->isclosed || written == NULL) + return 0; + + *written = pkt->wbuf->curr - (unsigned char *)pkt->wbuf->buf->data; + + return 1; +} + +/* + * Returns the length of this PACKETW so far. This excludes any bytes allocated + * for the length itself. + */ +int PACKETW_get_length(PACKETW *pkt, size_t *len) +{ + if (pkt->isclosed || len == NULL) + return 0; + + *len = pkt->wbuf->written - pkt->pwritten; + + return 1; +} diff --git a/ssl/packet_locl.h b/ssl/packet_locl.h index d34034dedb..e783047507 100644 --- a/ssl/packet_locl.h +++ b/ssl/packet_locl.h @@ -548,6 +548,82 @@ __owur static ossl_inline int PACKET_get_length_prefixed_3(PACKET *pkt, return 1; } + +/* Writeable packets */ + +typedef struct packetw_buf { + /* The buffer where we store the output data */ + BUF_MEM *buf; + + /* Pointer to where we are currently writing */ + unsigned char *curr; + + /* Number of bytes written so far */ + size_t written; + + /* + * Maximum number of bytes we will allow to be written to this PACKETW. Zero + * if no maximum + */ + size_t maxsize; +} PACKETW_BUF; + +typedef struct packetw_st PACKETW; +struct packetw_st { + /* The parent PACKETW if we have one or NULL otherwise */ + PACKETW *parent; + + /* The actual buffer - shared with sub-packets */ + PACKETW_BUF *wbuf; + + /* Flags for this PACKETW */ + unsigned int flags; + + /* + * Pointer to where the length of this PACKETW goes (or NULL if we don't + * write the length) + */ + unsigned char *packet_len; + + /* Number of bytes in the packet_len */ + size_t lenbytes; + + /* Number of bytes written to the buf prior to this packet starting */ + size_t pwritten; + + /* True if we have an active sub-packet or false otherwise */ + int haschild; + + /* True if PACKETW_close() has been called on this PACKETW */ + int isclosed; +}; + +/* Flags */ +#define OPENSSL_PACKETW_FLAGS_NONE 0 +/* Error on PACKETW_close() if no data written to the PACKETW */ +#define OPENSSL_PACKETW_FLAGS_NON_ZERO_LENGTH 1 +/* + * Abandon all changes on PACKETW_close() if no data written to the PACKETW, + * i.e. this does not write out a zero packet length + */ +#define OPENSSL_PACKETW_FLAGS_ABANDON_ON_ZERO_LENGTH 2 + +int PACKETW_init_len(PACKETW *pkt, BUF_MEM *buf, size_t lenbytes); +int PACKETW_init(PACKETW *pkt, BUF_MEM *buf); +int PACKETW_set_flags(PACKETW *pkt, unsigned int flags); +int PACKETW_set_packet_len(PACKETW *pkt, unsigned char *packet_len, + size_t lenbytes); +int PACKETW_close(PACKETW *pkt); +int PACKETW_get_sub_packet_len(PACKETW *pkt, PACKETW *subpkt, size_t lenbytes); +int PACKETW_get_sub_packet(PACKETW *pkt, PACKETW *subpkt); +int PACKETW_allocate_bytes(PACKETW *pkt, size_t bytes, + unsigned char **allocbytes); +int PACKETW_put_bytes(PACKETW *pkt, unsigned int val, size_t bytes); +int PACKETW_set_max_size(PACKETW *pkt, size_t maxsize); +int PACKETW_memcpy(PACKETW *pkt, const void *src, size_t len); +int PACKETW_get_total_written(PACKETW *pkt, size_t *written); +int PACKETW_get_length(PACKETW *pkt, size_t *len); + # ifdef __cplusplus } # endif -- 2.34.1