openssl/ssl/quic/quic_sf_list.c
Tomas Mraz a02571a024 Support SSL_OP_CLEANSE_PLAINTEXT on QUIC streams
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21182)
2023-06-23 14:31:45 +02:00

335 lines
8.5 KiB
C

/*
* Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (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 "internal/uint_set.h"
#include "internal/common.h"
#include "internal/quic_sf_list.h"
struct stream_frame_st {
struct stream_frame_st *prev, *next;
UINT_RANGE range;
OSSL_QRX_PKT *pkt;
const unsigned char *data;
};
static void stream_frame_free(SFRAME_LIST *fl, STREAM_FRAME *sf)
{
if (fl->cleanse && sf->data != NULL)
OPENSSL_cleanse((unsigned char *)sf->data,
(size_t)(sf->range.end - sf->range.start));
ossl_qrx_pkt_release(sf->pkt);
OPENSSL_free(sf);
}
static STREAM_FRAME *stream_frame_new(UINT_RANGE *range, OSSL_QRX_PKT *pkt,
const unsigned char *data)
{
STREAM_FRAME *sf = OPENSSL_zalloc(sizeof(*sf));
if (sf == NULL)
return NULL;
if (pkt != NULL)
ossl_qrx_pkt_up_ref(pkt);
sf->range = *range;
sf->pkt = pkt;
sf->data = data;
return sf;
}
void ossl_sframe_list_init(SFRAME_LIST *fl)
{
memset(fl, 0, sizeof(*fl));
}
void ossl_sframe_list_destroy(SFRAME_LIST *fl)
{
STREAM_FRAME *sf, *next_frame;
for (sf = fl->head; sf != NULL; sf = next_frame) {
next_frame = sf->next;
stream_frame_free(fl, sf);
}
}
static int append_frame(SFRAME_LIST *fl, UINT_RANGE *range,
OSSL_QRX_PKT *pkt,
const unsigned char *data)
{
STREAM_FRAME *new_frame;
if ((new_frame = stream_frame_new(range, pkt, data)) == NULL)
return 0;
new_frame->prev = fl->tail;
if (fl->tail != NULL)
fl->tail->next = new_frame;
fl->tail = new_frame;
++fl->num_frames;
return 1;
}
int ossl_sframe_list_insert(SFRAME_LIST *fl, UINT_RANGE *range,
OSSL_QRX_PKT *pkt,
const unsigned char *data, int fin)
{
STREAM_FRAME *sf, *new_frame, *prev_frame, *next_frame;
#ifndef NDEBUG
uint64_t curr_end = fl->tail != NULL ? fl->tail->range.end
: fl->offset;
/* This check for FINAL_SIZE_ERROR is handled by QUIC FC already */
assert((!fin || curr_end <= range->end)
&& (!fl->fin || curr_end >= range->end));
#endif
if (fl->offset >= range->end)
goto end;
/* nothing there yet */
if (fl->tail == NULL) {
fl->tail = fl->head = stream_frame_new(range, pkt, data);
if (fl->tail == NULL)
return 0;
++fl->num_frames;
goto end;
}
/* optimize insertion at the end */
if (fl->tail->range.start < range->start) {
if (fl->tail->range.end >= range->end)
goto end;
if (!append_frame(fl, range, pkt, data))
return 0;
goto end;
}
prev_frame = NULL;
for (sf = fl->head; sf != NULL && sf->range.start < range->start;
sf = sf->next)
prev_frame = sf;
if (!ossl_assert(sf != NULL))
/* frame list invariant broken */
return 0;
if (prev_frame != NULL && prev_frame->range.end >= range->end)
goto end;
/*
* Now we must create a new frame although in the end we might drop it,
* because we will be potentially dropping existing overlapping frames.
*/
new_frame = stream_frame_new(range, pkt, data);
if (new_frame == NULL)
return 0;
for (next_frame = sf;
next_frame != NULL && next_frame->range.end <= range->end;) {
STREAM_FRAME *drop_frame = next_frame;
next_frame = next_frame->next;
if (next_frame != NULL)
next_frame->prev = drop_frame->prev;
if (prev_frame != NULL)
prev_frame->next = drop_frame->next;
if (fl->head == drop_frame)
fl->head = next_frame;
if (fl->tail == drop_frame)
fl->tail = prev_frame;
--fl->num_frames;
stream_frame_free(fl, drop_frame);
}
if (next_frame != NULL) {
/* check whether the new_frame is redundant because there is no gap */
if (prev_frame != NULL
&& next_frame->range.start <= prev_frame->range.end) {
stream_frame_free(fl, new_frame);
goto end;
}
next_frame->prev = new_frame;
} else {
fl->tail = new_frame;
}
new_frame->next = next_frame;
new_frame->prev = prev_frame;
if (prev_frame != NULL)
prev_frame->next = new_frame;
else
fl->head = new_frame;
++fl->num_frames;
end:
fl->fin = fin || fl->fin;
return 1;
}
int ossl_sframe_list_peek(const SFRAME_LIST *fl, void **iter,
UINT_RANGE *range, const unsigned char **data,
int *fin)
{
STREAM_FRAME *sf = *iter;
uint64_t start;
if (sf == NULL) {
start = fl->offset;
sf = fl->head;
} else {
start = sf->range.end;
sf = sf->next;
}
range->start = start;
if (sf == NULL || sf->range.start > start
|| !ossl_assert(start < sf->range.end)) {
range->end = start;
*data = NULL;
*iter = NULL;
/* set fin only if we are at the end */
*fin = sf == NULL ? fl->fin : 0;
return 0;
}
range->end = sf->range.end;
if (sf->data != NULL)
*data = sf->data + (start - sf->range.start);
else
*data = NULL;
*fin = sf->next == NULL ? fl->fin : 0;
*iter = sf;
return 1;
}
int ossl_sframe_list_drop_frames(SFRAME_LIST *fl, uint64_t limit)
{
STREAM_FRAME *sf;
/* offset cannot move back or past the data received */
if (!ossl_assert(limit >= fl->offset)
|| !ossl_assert(fl->tail == NULL
|| limit <= fl->tail->range.end)
|| !ossl_assert(fl->tail != NULL
|| limit == fl->offset))
return 0;
fl->offset = limit;
for (sf = fl->head; sf != NULL && sf->range.end <= limit;) {
STREAM_FRAME *drop_frame = sf;
sf = sf->next;
--fl->num_frames;
stream_frame_free(fl, drop_frame);
}
fl->head = sf;
if (sf != NULL)
sf->prev = NULL;
else
fl->tail = NULL;
fl->head_locked = 0;
return 1;
}
int ossl_sframe_list_lock_head(SFRAME_LIST *fl, UINT_RANGE *range,
const unsigned char **data,
int *fin)
{
int ret;
void *iter = NULL;
if (fl->head_locked)
return 0;
ret = ossl_sframe_list_peek(fl, &iter, range, data, fin);
if (ret)
fl->head_locked = 1;
return ret;
}
int ossl_sframe_list_is_head_locked(SFRAME_LIST *fl)
{
return fl->head_locked;
}
int ossl_sframe_list_move_data(SFRAME_LIST *fl,
sframe_list_write_at_cb *write_at_cb,
void *cb_arg)
{
STREAM_FRAME *sf = fl->head, *prev_frame = NULL;
uint64_t limit = fl->offset;
if (sf == NULL)
return 1;
if (fl->head_locked)
sf = sf->next;
for (; sf != NULL; sf = sf->next) {
size_t len;
const unsigned char *data = sf->data;
if (limit < sf->range.start)
limit = sf->range.start;
if (data != NULL) {
if (limit > sf->range.start)
data += (size_t)(limit - sf->range.start);
len = (size_t)(sf->range.end - limit);
if (!write_at_cb(limit, data, len, cb_arg))
/* data did not fit */
return 0;
if (fl->cleanse)
OPENSSL_cleanse((unsigned char *)sf->data,
(size_t)(sf->range.end - sf->range.start));
/* release the packet */
sf->data = NULL;
ossl_qrx_pkt_release(sf->pkt);
sf->pkt = NULL;
}
limit = sf->range.end;
/* merge contiguous frames */
if (prev_frame != NULL
&& prev_frame->range.end >= sf->range.start) {
prev_frame->range.end = sf->range.end;
prev_frame->next = sf->next;
if (sf->next != NULL)
sf->next->prev = prev_frame;
else
fl->tail = prev_frame;
--fl->num_frames;
stream_frame_free(fl, sf);
sf = prev_frame;
continue;
}
prev_frame = sf;
}
return 1;
}