Sometimes data read by a record layer in one epoch is actually intended for
the next epoch. For example in a TLS with read_ahead, the read_ahead data
could contain a KeyUpdate message followed by application data encrypted
with new keys. Therefore we implement a mechanism for passing this data
across the epochs.
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18132)
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
- const EVP_MD *md, const SSL_COMP *comp, BIO *transport,
- BIO_ADDR *local, BIO_ADDR *peer,
+ const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
+ BIO *transport, BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
- ciph, taglen, mactype, md, comp, transport,
- local, peer, settings, options, retrl, s);
+ ciph, taglen, mactype, md, comp, prev,
+ transport, next, local, peer, settings,
+ options, retrl, s);
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
int version;
int role;
int direction;
int version;
int role;
int direction;
+
+ /*
+ * A BIO containing any data read in the previous epoch that was destined
+ * for this epoch
+ */
+ BIO *prev;
+
+ /* The transport BIO */
+
+ /*
+ * A BIO where we will send any data read by us that is destined for the
+ * next epoch.
+ */
+ BIO *next;
+
/* Types match the equivalent structures in the SSL object */
uint64_t options;
/*
/* Types match the equivalent structures in the SSL object */
uint64_t options;
/*
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
- const EVP_MD *md, const SSL_COMP *comp, BIO *transport,
+ const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
+ BIO *transport, BIO *next,
BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s);
BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s);
-void tls_free(OSSL_RECORD_LAYER *rl);
+int tls_free(OSSL_RECORD_LAYER *rl);
int tls_reset(OSSL_RECORD_LAYER *rl);
int tls_unprocessed_read_pending(OSSL_RECORD_LAYER *rl);
int tls_processed_read_pending(OSSL_RECORD_LAYER *rl);
int tls_reset(OSSL_RECORD_LAYER *rl);
int tls_unprocessed_read_pending(OSSL_RECORD_LAYER *rl);
int tls_processed_read_pending(OSSL_RECORD_LAYER *rl);
# define SSL_AD_NO_ALERT -1
# define SSL_AD_NO_ALERT -1
+static void tls_int_free(OSSL_RECORD_LAYER *rl);
+
void ossl_rlayer_fatal(OSSL_RECORD_LAYER *rl, int al, int reason,
const char *fmt, ...)
{
void ossl_rlayer_fatal(OSSL_RECORD_LAYER *rl, int al, int reason,
const char *fmt, ...)
{
while (left < n) {
size_t bioread = 0;
int ret;
while (left < n) {
size_t bioread = 0;
int ret;
+ BIO *bio = rl->prev != NULL ? rl->prev : rl->bio;
/*
* Now we have len+left bytes at the front of s->s3.rbuf.buf and
/*
* Now we have len+left bytes at the front of s->s3.rbuf.buf and
- if (rl->bio != NULL) {
- ret = BIO_read(rl->bio, pkt + len + left, max - left);
+ if (bio != NULL) {
+ ret = BIO_read(bio, pkt + len + left, max - left);
if (ret > 0) {
bioread = ret;
ret = OSSL_RECORD_RETURN_SUCCESS;
if (ret > 0) {
bioread = ret;
ret = OSSL_RECORD_RETURN_SUCCESS;
- } else if (BIO_should_retry(rl->bio)) {
+ } else if (BIO_should_retry(bio)) {
+ if (rl->prev != NULL) {
+ /*
+ * We were reading from the previous epoch. Now there is no
+ * more data, so swap to the actual transport BIO
+ */
+ BIO_free(rl->prev);
+ rl->prev = NULL;
+ continue;
+ }
ret = OSSL_RECORD_RETURN_RETRY;
ret = OSSL_RECORD_RETURN_RETRY;
- } else if (BIO_eof(rl->bio)) {
+ } else if (BIO_eof(bio)) {
ret = OSSL_RECORD_RETURN_EOF;
} else {
ret = OSSL_RECORD_RETURN_FATAL;
ret = OSSL_RECORD_RETURN_EOF;
} else {
ret = OSSL_RECORD_RETURN_FATAL;
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
- const EVP_MD *md, const SSL_COMP *comp, BIO *transport,
- BIO_ADDR *local, BIO_ADDR *peer,
- const OSSL_PARAM *settings, const OSSL_PARAM *options,
- OSSL_RECORD_LAYER **retrl,
+ const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
+ BIO *transport, BIO *next, BIO_ADDR *local,
+ BIO_ADDR *peer, const OSSL_PARAM *settings,
+ const OSSL_PARAM *options, OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s)
{
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s)
{
return OSSL_RECORD_RETURN_FATAL;
}
return OSSL_RECORD_RETURN_FATAL;
}
- if (transport != NULL && !BIO_up_ref(transport)) {
- RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
/*
* TODO(RECLAYER): Need to handle the case where the params are updated
* after the record layer has been created.
/*
* TODO(RECLAYER): Need to handle the case where the params are updated
* after the record layer has been created.
if (!tls_set1_bio(rl, transport))
goto err;
if (!tls_set1_bio(rl, transport))
goto err;
+ if (prev != NULL && !BIO_up_ref(prev))
+ goto err;
+ rl->prev = prev;
+
+ if (next != NULL && !BIO_up_ref(next))
+ goto err;
+ rl->next = next;
+
*retrl = rl;
return OSSL_RECORD_RETURN_SUCCESS;
err:
*retrl = rl;
return OSSL_RECORD_RETURN_SUCCESS;
err:
return OSSL_RECORD_RETURN_FATAL;
}
return OSSL_RECORD_RETURN_FATAL;
}
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
- const EVP_MD *md, const SSL_COMP *comp, BIO *transport,
- BIO_ADDR *local, BIO_ADDR *peer,
+ const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
+ BIO *transport, BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
- ciph, taglen, mactype, md, comp, transport,
- local, peer, settings, options, retrl, s);
+ ciph, taglen, mactype, md, comp, prev,
+ transport, next, local, peer, settings,
+ options, retrl, s);
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
const EVP_CIPHER *ciph, size_t taglen,
/* TODO(RECLAYER): This probably should not be an int */
int mactype,
- const EVP_MD *md, const SSL_COMP *comp, BIO *transport,
- BIO_ADDR *local, BIO_ADDR *peer,
+ const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
+ BIO *transport, BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
const OSSL_PARAM *settings, const OSSL_PARAM *options,
OSSL_RECORD_LAYER **retrl,
/* TODO(RECLAYER): Remove me */
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
key, keylen, iv, ivlen, mackey, mackeylen,
- ciph, taglen, mactype, md, comp, transport,
- local, peer, settings, options, retrl, s);
+ ciph, taglen, mactype, md, comp, prev,
+ transport, next, local, peer, settings,
+ options, retrl, s);
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
if (ret != OSSL_RECORD_RETURN_SUCCESS)
return ret;
return OSSL_RECORD_RETURN_SUCCESS;
}
return OSSL_RECORD_RETURN_SUCCESS;
}
-void tls_free(OSSL_RECORD_LAYER *rl)
+static void tls_int_free(OSSL_RECORD_LAYER *rl)
{
/* TODO(RECLAYER): Cleanse sensitive fields */
{
/* TODO(RECLAYER): Cleanse sensitive fields */
+ BIO_free(rl->next);
+ SSL3_BUFFER_release(&rl->rbuf);
+
+ EVP_CIPHER_CTX_free(rl->enc_read_ctx);
+ EVP_MD_CTX_free(rl->read_hash);
+ COMP_CTX_free(rl->expand);
+
+int tls_free(OSSL_RECORD_LAYER *rl)
+{
+ SSL3_BUFFER *rbuf;
+ size_t left, written;
+ int ret = 1;
+
+ rbuf = &rl->rbuf;
+
+ left = SSL3_BUFFER_get_left(rbuf);
+ if (left > 0) {
+ /*
+ * This record layer is closing but we still have data left in our
+ * buffer. It must be destined for the next epoch - so push it there.
+ */
+ ret = BIO_write_ex(rl->next, rbuf->buf + rbuf->offset, left, &written);
+ }
+ tls_int_free(rl);
+
+ return ret;
+}
+
+
int tls_reset(OSSL_RECORD_LAYER *rl)
{
memset(rl, 0, sizeof(*rl));
int tls_reset(OSSL_RECORD_LAYER *rl)
{
memset(rl, 0, sizeof(*rl));
meth = ssl_select_next_record_layer(s, level);
meth = ssl_select_next_record_layer(s, level);
- if (s->rrlmethod != NULL)
- s->rrlmethod->free(s->rrl);
+ if (s->rrlmethod != NULL && !s->rrlmethod->free(s->rrl)) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
if (meth != NULL)
s->rrlmethod = meth;
if (meth != NULL)
s->rrlmethod = meth;
+ BIO *prev = s->rrlnext;
+
+ s->rrlnext = BIO_new(BIO_s_mem());
+
+ if (s->rrlnext == NULL) {
+ BIO_free(prev);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
rlret = s->rrlmethod->new_record_layer(sctx->libctx, sctx->propq,
version, s->server, direction,
level, key, keylen, iv, ivlen,
mackey, mackeylen, ciph, taglen,
rlret = s->rrlmethod->new_record_layer(sctx->libctx, sctx->propq,
version, s->server, direction,
level, key, keylen, iv, ivlen,
mackey, mackeylen, ciph, taglen,
- mactype, md, comp, s->rbio,
- NULL, NULL, NULL, options,
- &s->rrl, s);
+ mactype, md, comp, prev, s->rbio,
+ s->rrlnext, NULL, NULL, NULL,
+ options, &s->rrl, s);
+ BIO_free(prev);
switch (rlret) {
case OSSL_RECORD_RETURN_FATAL:
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_RECORD_LAYER_FAILURE);
switch (rlret) {
case OSSL_RECORD_RETURN_FATAL:
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_RECORD_LAYER_FAILURE);
int mactype,
const EVP_MD *md,
const SSL_COMP *comp,
int mactype,
const EVP_MD *md,
const SSL_COMP *comp,
- BIO *transport, BIO_ADDR *local,
+ BIO *prev,
+ BIO *transport,
+ BIO *next,
+ BIO_ADDR *local,
BIO_ADDR *peer,
const OSSL_PARAM *settings,
const OSSL_PARAM *options,
OSSL_RECORD_LAYER **ret,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s);
BIO_ADDR *peer,
const OSSL_PARAM *settings,
const OSSL_PARAM *options,
OSSL_RECORD_LAYER **ret,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s);
- void (*free)(OSSL_RECORD_LAYER *rl);
+ int (*free)(OSSL_RECORD_LAYER *rl);
int (*reset)(OSSL_RECORD_LAYER *rl); /* Is this needed? */
int (*reset)(OSSL_RECORD_LAYER *rl); /* Is this needed? */
}
RECORD_LAYER_clear(&sc->rlayer);
}
RECORD_LAYER_clear(&sc->rlayer);
+ BIO_free(sc->rrlnext);
+ sc->rrlnext = NULL;
/*
* TODO(RECLAYER): The record method should probably initialy come from the
/*
* TODO(RECLAYER): The record method should probably initialy come from the
+ if (s->rrlmethod != NULL)
+ s->rrlmethod->free(s->rrl); /* Ignore return value */
+ BIO_free(s->rrlnext);
+
X509_VERIFY_PARAM_free(s->param);
dane_final(&s->dane);
X509_VERIFY_PARAM_free(s->param);
dane_final(&s->dane);
const OSSL_RECORD_METHOD *rrlmethod;
/* The read direction record layer */
OSSL_RECORD_LAYER *rrl;
const OSSL_RECORD_METHOD *rrlmethod;
/* The read direction record layer */
OSSL_RECORD_LAYER *rrl;
+ /* BIO to store data destined for the next record layer epoch */
+ BIO *rrlnext;
/* Default password callback. */
pem_password_cb *default_passwd_callback;
/* Default password callback. */
pem_password_cb *default_passwd_callback;