X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=ssl%2Fpacket.c;h=7a4414ae6a8012ffa7bbe39c2c54afbeffa7ef82;hp=8528d8be089f79b638579db05a645eaf0d49a056;hb=67dc995eaf538ea309c6292a1a5073465201f55b;hpb=b7273855acd7ec2d1e7a4ba626ed538808fc7517 diff --git a/ssl/packet.c b/ssl/packet.c index 8528d8be08..7a4414ae6a 100644 --- a/ssl/packet.c +++ b/ssl/packet.c @@ -7,298 +7,363 @@ * https://www.openssl.org/source/license.html */ +#include "internal/cryptlib.h" #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) +#define DEFAULT_BUF_SIZE 256 + +int WPACKET_allocate_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes) +{ + if (!WPACKET_reserve_bytes(pkt, len, allocbytes)) + return 0; + + pkt->written += len; + pkt->curr += len; + return 1; +} + +int WPACKET_sub_allocate_bytes__(WPACKET *pkt, size_t len, + unsigned char **allocbytes, size_t lenbytes) { - unsigned char *ret = wbuf->curr; + if (!WPACKET_start_sub_packet_len__(pkt, lenbytes) + || !WPACKET_allocate_bytes(pkt, len, allocbytes) + || !WPACKET_close(pkt)) + return 0; + + return 1; +} + +#define GETBUF(p) (((p)->staticbuf != NULL) \ + ? (p)->staticbuf : (unsigned char *)(p)->buf->data) - if (SIZE_MAX - wbuf->written < len) +int WPACKET_reserve_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes) +{ + /* Internal API, so should not fail */ + if (!ossl_assert(pkt->subs != NULL && len != 0)) return 0; - if (wbuf->maxsize > 0 && wbuf->written + len > wbuf->maxsize) + if (pkt->maxsize - pkt->written < len) return 0; - if (wbuf->buf->length - wbuf->written < len) { + if (pkt->staticbuf == NULL && (pkt->buf->length - pkt->written < len)) { size_t newlen; + size_t reflen; + + reflen = (len > pkt->buf->length) ? len : pkt->buf->length; - if (wbuf->buf->length > SIZE_MAX / 2) + if (reflen > SIZE_MAX / 2) { newlen = SIZE_MAX; - else - newlen = wbuf->buf->length * 2; - if (BUF_MEM_grow(wbuf->buf, newlen) == 0) - return NULL; + } else { + newlen = reflen * 2; + if (newlen < DEFAULT_BUF_SIZE) + newlen = DEFAULT_BUF_SIZE; + } + if (BUF_MEM_grow(pkt->buf, newlen) == 0) + return 0; } - wbuf->written += len; - wbuf->curr += len; + if (allocbytes != NULL) + *allocbytes = WPACKET_get_curr(pkt); - return ret; + return 1; } -/* - * 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) +int WPACKET_sub_reserve_bytes__(WPACKET *pkt, size_t len, + unsigned char **allocbytes, size_t lenbytes) { - PACKETW_BUF *wbuf; - /* Sanity check */ - if (buf == NULL) + if (!WPACKET_reserve_bytes(pkt, lenbytes + len, allocbytes)) return 0; - wbuf = OPENSSL_zalloc(sizeof(PACKETW_BUF)); - if (wbuf == NULL) { - pkt->isclosed = 1; - return 0; - } + *allocbytes += lenbytes; + + return 1; +} - wbuf->buf = buf; - wbuf->curr = (unsigned char *)buf->data; - wbuf->written = 0; - wbuf->maxsize = 0; +static size_t maxmaxsize(size_t lenbytes) +{ + if (lenbytes >= sizeof(size_t) || lenbytes == 0) + return SIZE_MAX; - pkt->parent = NULL; - pkt->wbuf = wbuf; - pkt->pwritten = lenbytes; - pkt->lenbytes = lenbytes; - pkt->haschild = 0; - pkt->isclosed = 0; + return ((size_t)1 << (lenbytes * 8)) - 1 + lenbytes; +} - if (lenbytes == 0) { - pkt->packet_len = NULL; +static int wpacket_intern_init_len(WPACKET *pkt, size_t lenbytes) +{ + unsigned char *lenchars; + + pkt->curr = 0; + pkt->written = 0; + + pkt->subs = OPENSSL_zalloc(sizeof(*pkt->subs)); + if (pkt->subs == NULL) + return 0; + + if (lenbytes == 0) return 1; - } - pkt->packet_len = PACKETW_BUF_allocate(wbuf, lenbytes); - if (pkt->packet_len == NULL) { - OPENSSL_free(wbuf); - pkt->wbuf = NULL; - pkt->isclosed = 1; + pkt->subs->pwritten = lenbytes; + pkt->subs->lenbytes = lenbytes; + + if (!WPACKET_allocate_bytes(pkt, lenbytes, &lenchars)) { + OPENSSL_free(pkt->subs); + pkt->subs = NULL; return 0; } + pkt->subs->packet_len = lenchars - GETBUF(pkt); return 1; } -/* - * Same as PACKETW_init_len except there is no preallocation of the PACKETW - * length. - */ -int PACKETW_init(PACKETW *pkt, BUF_MEM *buf) +int WPACKET_init_static_len(WPACKET *pkt, unsigned char *buf, size_t len, + size_t lenbytes) { - return PACKETW_init_len(pkt, buf, 0); + size_t max = maxmaxsize(lenbytes); + + /* Internal API, so should not fail */ + if (!ossl_assert(buf != NULL && len > 0)) + return 0; + + pkt->staticbuf = buf; + pkt->buf = NULL; + pkt->maxsize = (max < len) ? max : len; + + return wpacket_intern_init_len(pkt, lenbytes); } -/* - * 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) +int WPACKET_init_len(WPACKET *pkt, BUF_MEM *buf, size_t lenbytes) +{ + /* Internal API, so should not fail */ + if (!ossl_assert(buf != NULL)) + return 0; + + pkt->staticbuf = NULL; + pkt->buf = buf; + pkt->maxsize = maxmaxsize(lenbytes); + + return wpacket_intern_init_len(pkt, lenbytes); +} + +int WPACKET_init(WPACKET *pkt, BUF_MEM *buf) +{ + return WPACKET_init_len(pkt, buf, 0); +} + +int WPACKET_set_flags(WPACKET *pkt, unsigned int flags) { - /* We only allow this to be set once */ - if (pkt->isclosed || pkt->packet_len != NULL) + /* Internal API, so should not fail */ + if (!ossl_assert(pkt->subs != NULL)) return 0; - pkt->lenbytes = lenbytes; - pkt->packet_len = packet_len; + pkt->subs->flags = flags; return 1; } -int PACKETW_set_flags(PACKETW *pkt, unsigned int flags) +/* Store the |value| of length |len| at location |data| */ +static int put_value(unsigned char *data, size_t value, size_t len) { - pkt->flags = flags; + for (data += len - 1; len > 0; len--) { + *data = (unsigned char)(value & 0xff); + data--; + value >>= 8; + } + + /* Check whether we could fit the value in the assigned number of bytes */ + if (value > 0) + return 0; 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. + * Internal helper function used by WPACKET_close(), WPACKET_finish() and + * WPACKET_fill_lengths() to close a sub-packet and write out its length if + * necessary. If |doclose| is 0 then it goes through the motions of closing + * (i.e. it fills in all the lengths), but doesn't actually close anything. */ -int PACKETW_close(PACKETW *pkt) +static int wpacket_intern_close(WPACKET *pkt, WPACKET_SUB *sub, int doclose) { - size_t packlen; - - if (pkt->isclosed || pkt->haschild) - return 0; + size_t packlen = pkt->written - sub->pwritten; - packlen = pkt->wbuf->written - pkt->pwritten; - if (packlen == 0 && pkt->flags & OPENSSL_PACKETW_FLAGS_NON_ZERO_LENGTH) + if (packlen == 0 + && (sub->flags & WPACKET_FLAGS_NON_ZERO_LENGTH) != 0) 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; + && sub->flags & WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH) { + /* We can't handle this case. Return an error */ + if (!doclose) + return 0; + + /* Deallocate any bytes allocated for the length of the WPACKET */ + if ((pkt->curr - sub->lenbytes) == sub->packet_len) { + pkt->written -= sub->lenbytes; + pkt->curr -= sub->lenbytes; } /* Don't write out the packet length */ - pkt->packet_len = NULL; + sub->packet_len = 0; + sub->lenbytes = 0; } - /* 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 - */ + /* Write out the WPACKET length if needed */ + if (sub->lenbytes > 0 + && !put_value(&GETBUF(pkt)[sub->packet_len], packlen, + sub->lenbytes)) return 0; - } + + if (doclose) { + pkt->subs = sub->parent; + OPENSSL_free(sub); } - if (pkt->parent != NULL) { - if (pkt->parent->haschild != 1) { - /* Should not happen! */ + return 1; +} + +int WPACKET_fill_lengths(WPACKET *pkt) +{ + WPACKET_SUB *sub; + + if (!ossl_assert(pkt->subs != NULL)) + return 0; + + for (sub = pkt->subs; sub != NULL; sub = sub->parent) { + if (!wpacket_intern_close(pkt, sub, 0)) 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) +int WPACKET_close(WPACKET *pkt) { - if (pkt->isclosed || pkt->haschild || subpkt == NULL) + /* + * Internal API, so should not fail - but we do negative testing of this + * so no assert (otherwise the tests fail) + */ + if (pkt->subs == NULL || pkt->subs->parent == 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; + return wpacket_intern_close(pkt, pkt->subs, 1); +} - if (lenbytes == 0) { - subpkt->packet_len = NULL; - pkt->haschild = 1; - return 1; +int WPACKET_finish(WPACKET *pkt) +{ + int ret; + + /* + * Internal API, so should not fail - but we do negative testing of this + * so no assert (otherwise the tests fail) + */ + if (pkt->subs == NULL || pkt->subs->parent != NULL) + return 0; + + ret = wpacket_intern_close(pkt, pkt->subs, 1); + if (ret) { + OPENSSL_free(pkt->subs); + pkt->subs = NULL; } - subpkt->packet_len = PACKETW_BUF_allocate(pkt->wbuf, lenbytes); - if (subpkt->packet_len == NULL) { - subpkt->isclosed = 1; + return ret; +} + +int WPACKET_start_sub_packet_len__(WPACKET *pkt, size_t lenbytes) +{ + WPACKET_SUB *sub; + unsigned char *lenchars; + + /* Internal API, so should not fail */ + if (!ossl_assert(pkt->subs != NULL)) + return 0; + + sub = OPENSSL_zalloc(sizeof(*sub)); + if (sub == NULL) return 0; + + sub->parent = pkt->subs; + pkt->subs = sub; + sub->pwritten = pkt->written + lenbytes; + sub->lenbytes = lenbytes; + + if (lenbytes == 0) { + sub->packet_len = 0; + return 1; } - pkt->haschild = 1; + if (!WPACKET_allocate_bytes(pkt, lenbytes, &lenchars)) + return 0; + /* Convert to an offset in case the underlying BUF_MEM gets realloc'd */ + sub->packet_len = lenchars - GETBUF(pkt); 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) +int WPACKET_start_sub_packet(WPACKET *pkt) { - return PACKETW_get_sub_packet_len(pkt, subpkt, 0); + return WPACKET_start_sub_packet_len__(pkt, 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) +int WPACKET_put_bytes__(WPACKET *pkt, unsigned int val, size_t size) { unsigned char *data; - if (pkt->isclosed || pkt->haschild || bytes == 0) + /* Internal API, so should not fail */ + if (!ossl_assert(size <= sizeof(unsigned int)) + || !WPACKET_allocate_bytes(pkt, size, &data) + || !put_value(data, val, size)) 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) +int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize) { - unsigned char *data; + WPACKET_SUB *sub; + size_t lenbytes; - if (bytes > sizeof(unsigned int) - || !PACKETW_allocate_bytes(pkt, bytes, &data)) + /* Internal API, so should not fail */ + if (!ossl_assert(pkt->subs != NULL)) return 0; - data += bytes - 1; - for (; bytes > 0; bytes--) { - *data = (unsigned char)(val & 0xff); - data--; - val >>= 8; - } + /* Find the WPACKET_SUB for the top level */ + for (sub = pkt->subs; sub->parent != NULL; sub = sub->parent) + continue; - /* Check whether we could fit the value in the assigned number of bytes */ - if (val > 0) + lenbytes = sub->lenbytes; + if (lenbytes == 0) + lenbytes = sizeof(pkt->maxsize); + + if (maxmaxsize(lenbytes) < maxsize || maxsize < pkt->written) return 0; + pkt->maxsize = maxsize; + 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) +int WPACKET_memset(WPACKET *pkt, int ch, size_t len) { - pkt->wbuf->maxsize = maxsize; + unsigned char *dest; + + if (len == 0) + return 1; + + if (!WPACKET_allocate_bytes(pkt, len, &dest)) + return 0; + + memset(dest, ch, len); return 1; } -/* - * Copy |len| bytes of data from |*src| into the PACKETW. - */ -int PACKETW_memcpy(PACKETW *pkt, const void *src, size_t len) +int WPACKET_memcpy(WPACKET *pkt, const void *src, size_t len) { unsigned char *dest; if (len == 0) return 1; - if (!PACKETW_allocate_bytes(pkt, len, &dest)) + if (!WPACKET_allocate_bytes(pkt, len, &dest)) return 0; memcpy(dest, src, len); @@ -306,30 +371,51 @@ int PACKETW_memcpy(PACKETW *pkt, const void *src, size_t 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) +int WPACKET_sub_memcpy__(WPACKET *pkt, const void *src, size_t len, + size_t lenbytes) { - if (pkt->isclosed || written == NULL) + if (!WPACKET_start_sub_packet_len__(pkt, lenbytes) + || !WPACKET_memcpy(pkt, src, len) + || !WPACKET_close(pkt)) return 0; - *written = pkt->wbuf->curr - (unsigned char *)pkt->wbuf->buf->data; + return 1; +} + +int WPACKET_get_total_written(WPACKET *pkt, size_t *written) +{ + /* Internal API, so should not fail */ + if (!ossl_assert(written != NULL)) + return 0; + + *written = pkt->written; 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) +int WPACKET_get_length(WPACKET *pkt, size_t *len) { - if (pkt->isclosed || len == NULL) + /* Internal API, so should not fail */ + if (!ossl_assert(pkt->subs != NULL && len != NULL)) return 0; - *len = pkt->wbuf->written - pkt->pwritten; + *len = pkt->written - pkt->subs->pwritten; return 1; } + +unsigned char *WPACKET_get_curr(WPACKET *pkt) +{ + return GETBUF(pkt) + pkt->curr; +} + +void WPACKET_cleanup(WPACKET *pkt) +{ + WPACKET_SUB *sub, *parent; + + for (sub = pkt->subs; sub != NULL; sub = parent) { + parent = sub->parent; + OPENSSL_free(sub); + } + pkt->subs = NULL; +}