Convert SSLv3 code to use the new read side record layer

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18132)
This commit is contained in:
Matt Caswell 2022-04-28 16:57:07 +01:00
parent 4840c2a5e6
commit 10560aed15
2 changed files with 388 additions and 105 deletions

View File

@ -101,6 +101,9 @@ struct ossl_record_layer_st
/* uncompress */
COMP_CTX *expand;
/* Only used by SSLv3 */
unsigned char mac_secret[EVP_MAX_MD_SIZE];
/* Function pointers for version specific functions */
/* Function pointers for version specific functions */
struct record_functions_st *funcs;
@ -206,6 +209,69 @@ static int tls_any_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
return 1;
}
/* TODO(RECLAYER): Handle OPENSSL_NO_COMP */
static int ssl3_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
unsigned char *key, size_t keylen,
unsigned char *iv, size_t ivlen,
unsigned char *mackey, size_t mackeylen,
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,
/* TODO(RECLAYER): Remove me */
SSL_CONNECTION *s)
{
EVP_CIPHER_CTX *ciph_ctx;
if (md == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if ((rl->enc_read_ctx = EVP_CIPHER_CTX_new()) == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
return 0;
}
ciph_ctx = rl->enc_read_ctx;
rl->read_hash = EVP_MD_CTX_new();
if (rl->read_hash == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
#ifndef OPENSSL_NO_COMP
if (comp != NULL) {
rl->expand = COMP_CTX_new(comp->method);
if (rl->expand == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR,
SSL_R_COMPRESSION_LIBRARY_ERROR);
return 0;
}
}
#endif
if (!EVP_DecryptInit_ex(ciph_ctx, ciph, NULL, key, iv)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (EVP_CIPHER_get0_provider(ciph) != NULL
&& !tls_provider_set_tls_parameters(rl, ciph_ctx, ciph, md, s)) {
/* RLAYERfatal already called */
return 0;
}
if (mackeylen > sizeof(rl->mac_secret)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
memcpy(rl->mac_secret, mackey, mackeylen);
return 1;
}
/* TODO(RECLAYER): Handle OPENSSL_NO_COMP */
static int tls1_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
unsigned char *key, size_t keylen,
@ -337,7 +403,7 @@ static int tls1_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
}
if (EVP_CIPHER_get0_provider(ciph) != NULL
&& !tls_provider_set_tls_parameters(rl, ciph_ctx, ciph, md, s)) {
/* SSLfatal already called */
/* RLAYERfatal already called */
return 0;
}
@ -358,6 +424,125 @@ static int tls_any_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *recs, size_t n_rec
return 1;
}
/*-
* ssl3_enc encrypts/decrypts |n_recs| records in |inrecs|. Calls SSLfatal on
* internal error, but not otherwise. It is the responsibility of the caller to
* report a bad_record_mac
*
* Returns:
* 0: if the record is publicly invalid, or an internal error
* 1: Success or Mac-then-encrypt decryption failed (MAC will be randomised)
*/
static int ssl3_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *inrecs, size_t n_recs,
int sending, SSL_MAC_BUF *mac, size_t macsize,
/* TODO(RECLAYER): Remove me */ SSL_CONNECTION *s)
{
SSL3_RECORD *rec;
EVP_CIPHER_CTX *ds;
size_t l, i;
size_t bs;
const EVP_CIPHER *enc;
int provided;
rec = inrecs;
/*
* We shouldn't ever be called with more than one record in the SSLv3 case
*/
if (n_recs != 1)
return 0;
if (sending) {
ds = s->enc_write_ctx;
if (s->enc_write_ctx == NULL)
enc = NULL;
else
enc = EVP_CIPHER_CTX_get0_cipher(s->enc_write_ctx);
} else {
ds = rl->enc_read_ctx;
if (rl->enc_read_ctx == NULL)
enc = NULL;
else
enc = EVP_CIPHER_CTX_get0_cipher(rl->enc_read_ctx);
}
provided = (EVP_CIPHER_get0_provider(enc) != NULL);
l = rec->length;
bs = EVP_CIPHER_CTX_get_block_size(ds);
/* COMPRESS */
if ((bs != 1) && sending && !provided) {
/*
* We only do this for legacy ciphers. Provided ciphers add the
* padding on the provider side.
*/
i = bs - (l % bs);
/* we need to add 'i-1' padding bytes */
l += i;
/*
* the last of these zero bytes will be overwritten with the
* padding length.
*/
memset(&rec->input[rec->length], 0, i);
rec->length += i;
rec->input[l - 1] = (unsigned char)(i - 1);
}
if (!sending) {
if (l == 0 || l % bs != 0) {
/* Publicly invalid */
return 0;
}
/* otherwise, rec->length >= bs */
}
if (provided) {
int outlen;
if (!EVP_CipherUpdate(ds, rec->data, &outlen, rec->input,
(unsigned int)l))
return 0;
rec->length = outlen;
if (!sending && mac != NULL) {
/* Now get a pointer to the MAC */
OSSL_PARAM params[2], *p = params;
/* Get the MAC */
mac->alloced = 0;
*p++ = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC,
(void **)&mac->mac,
macsize);
*p = OSSL_PARAM_construct_end();
if (!EVP_CIPHER_CTX_get_params(ds, params)) {
/* Shouldn't normally happen */
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
} else {
if (EVP_Cipher(ds, rec->data, rec->input, (unsigned int)l) < 1) {
/* Shouldn't happen */
RLAYERfatal(rl, SSL_AD_BAD_RECORD_MAC, ERR_R_INTERNAL_ERROR);
return 0;
}
if (!sending)
return ssl3_cbc_remove_padding_and_mac(&rec->length,
rec->orig_len,
rec->data,
(mac != NULL) ? &mac->mac : NULL,
(mac != NULL) ? &mac->alloced : NULL,
bs,
macsize,
rl->libctx);
}
return 1;
}
#define MAX_PADDING 256
/*-
@ -690,6 +875,125 @@ static int tls1_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *recs, size_t n_recs,
return 1;
}
static const unsigned char ssl3_pad_1[48] = {
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
};
static const unsigned char ssl3_pad_2[48] = {
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c
};
static int ssl3_mac(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec, unsigned char *md,
int sending, SSL_CONNECTION *ssl)
{
unsigned char *mac_sec, *seq;
const EVP_MD_CTX *hash;
unsigned char *p, rec_char;
size_t md_size;
size_t npad;
int t;
if (sending) {
mac_sec = &(ssl->s3.write_mac_secret[0]);
seq = RECORD_LAYER_get_write_sequence(&ssl->rlayer);
hash = ssl->write_hash;
} else {
mac_sec = &(rl->mac_secret[0]);
seq = RECORD_LAYER_get_read_sequence(&ssl->rlayer);
hash = rl->read_hash;
}
t = EVP_MD_CTX_get_size(hash);
if (t < 0)
return 0;
md_size = t;
npad = (48 / md_size) * md_size;
if (!sending
&& EVP_CIPHER_CTX_get_mode(rl->enc_read_ctx) == EVP_CIPH_CBC_MODE
&& ssl3_cbc_record_digest_supported(hash)) {
#ifdef OPENSSL_NO_DEPRECATED_3_0
return 0;
#else
/*
* This is a CBC-encrypted record. We must avoid leaking any
* timing-side channel information about how many blocks of data we
* are hashing because that gives an attacker a timing-oracle.
*/
/*-
* npad is, at most, 48 bytes and that's with MD5:
* 16 + 48 + 8 (sequence bytes) + 1 + 2 = 75.
*
* With SHA-1 (the largest hash speced for SSLv3) the hash size
* goes up 4, but npad goes down by 8, resulting in a smaller
* total size.
*/
unsigned char header[75];
size_t j = 0;
memcpy(header + j, mac_sec, md_size);
j += md_size;
memcpy(header + j, ssl3_pad_1, npad);
j += npad;
memcpy(header + j, seq, 8);
j += 8;
header[j++] = rec->type;
header[j++] = (unsigned char)(rec->length >> 8);
header[j++] = (unsigned char)(rec->length & 0xff);
/* Final param == is SSLv3 */
if (ssl3_cbc_digest_record(EVP_MD_CTX_get0_md(hash),
md, &md_size,
header, rec->input,
rec->length, rec->orig_len,
mac_sec, md_size, 1) <= 0)
return 0;
#endif
} else {
unsigned int md_size_u;
/* Chop the digest off the end :-) */
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL)
return 0;
rec_char = rec->type;
p = md;
s2n(rec->length, p);
if (EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
|| EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
|| EVP_DigestUpdate(md_ctx, ssl3_pad_1, npad) <= 0
|| EVP_DigestUpdate(md_ctx, seq, 8) <= 0
|| EVP_DigestUpdate(md_ctx, &rec_char, 1) <= 0
|| EVP_DigestUpdate(md_ctx, md, 2) <= 0
|| EVP_DigestUpdate(md_ctx, rec->input, rec->length) <= 0
|| EVP_DigestFinal_ex(md_ctx, md, NULL) <= 0
|| EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
|| EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
|| EVP_DigestUpdate(md_ctx, ssl3_pad_2, npad) <= 0
|| EVP_DigestUpdate(md_ctx, md, md_size) <= 0
|| EVP_DigestFinal_ex(md_ctx, md, &md_size_u) <= 0) {
EVP_MD_CTX_free(md_ctx);
return 0;
}
EVP_MD_CTX_free(md_ctx);
}
ssl3_record_sequence_update(seq);
return 1;
}
static int tls1_mac(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec, unsigned char *md,
int sending, SSL_CONNECTION *ssl)
{
@ -830,9 +1134,9 @@ struct record_functions_st tls_1_0_funcs = {
};
struct record_functions_st ssl_3_0_funcs = {
tls_fail_set_crypto_state,
NULL,
NULL
ssl3_set_crypto_state,
ssl3_cipher,
ssl3_mac
};
static int tls_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio);

View File

@ -90,65 +90,82 @@ static int ssl3_generate_key_block(SSL_CONNECTION *s, unsigned char *km, int num
int ssl3_change_cipher_state(SSL_CONNECTION *s, int which)
{
unsigned char *p, *mac_secret;
unsigned char *ms, *key, *iv;
size_t md_len;
unsigned char *key, *iv;
EVP_CIPHER_CTX *dd;
const EVP_CIPHER *c;
const EVP_CIPHER *ciph;
#ifndef OPENSSL_NO_COMP
COMP_METHOD *comp;
const SSL_COMP *comp;
#endif
const EVP_MD *m;
const EVP_MD *md;
int mdi;
size_t n, i, j, k, cl;
size_t n, iv_len, key_len;
int reuse_dd = 0;
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
c = s->s3.tmp.new_sym_enc;
m = s->s3.tmp.new_hash;
ciph = s->s3.tmp.new_sym_enc;
md = s->s3.tmp.new_hash;
/* m == NULL will lead to a crash later */
if (!ossl_assert(m != NULL)) {
if (!ossl_assert(md != NULL)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
#ifndef OPENSSL_NO_COMP
if (s->s3.tmp.new_compression == NULL)
comp = NULL;
else
comp = s->s3.tmp.new_compression->method;
comp = s->s3.tmp.new_compression;
#endif
p = s->s3.tmp.key_block;
mdi = EVP_MD_get_size(md);
if (mdi < 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
md_len = (size_t)mdi;
key_len = EVP_CIPHER_get_key_length(ciph);
iv_len = EVP_CIPHER_get_iv_length(ciph);
if ((which == SSL3_CHANGE_CIPHER_CLIENT_WRITE) ||
(which == SSL3_CHANGE_CIPHER_SERVER_READ)) {
mac_secret = &(p[0]);
n = md_len + md_len;
key = &(p[n]);
n += key_len + key_len;
iv = &(p[n]);
n += iv_len + iv_len;
} else {
n = md_len;
mac_secret = &(p[n]);
n += md_len + key_len;
key = &(p[n]);
n += key_len + iv_len;
iv = &(p[n]);
n += iv_len;
}
if (n > s->s3.tmp.key_block_length) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
if (which & SSL3_CC_READ) {
if (s->enc_read_ctx != NULL) {
reuse_dd = 1;
} else if ((s->enc_read_ctx = EVP_CIPHER_CTX_new()) == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
goto err;
} else {
/*
* make sure it's initialised in case we exit later with an error
*/
EVP_CIPHER_CTX_reset(s->enc_read_ctx);
}
dd = s->enc_read_ctx;
if (ssl_replace_hash(&s->read_hash, m) == NULL) {
s->rrlmethod->free(s->rrl);
s->rrl = s->rrlmethod->new_record_layer(sctx->libctx,
sctx->propq,
SSL3_VERSION, s->server,
OSSL_RECORD_DIRECTION_READ,
OSSL_RECORD_PROTECTION_LEVEL_APPLICATION,
key, key_len, iv, iv_len,
mac_secret, md_len, ciph, 0,
NID_undef, md, comp, s->rbio,
NULL, NULL, NULL, NULL, s);
if (s->rrl == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
#ifndef OPENSSL_NO_COMP
/* COMPRESS */
COMP_CTX_free(s->expand);
s->expand = NULL;
if (comp != NULL) {
s->expand = COMP_CTX_new(comp);
if (s->expand == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
SSL_R_COMPRESSION_LIBRARY_ERROR);
goto err;
s->statem.enc_write_state = ENC_WRITE_STATE_VALID;
return 1;
}
}
#endif
RECORD_LAYER_reset_read_sequence(&s->rlayer);
mac_secret = &(s->s3.read_mac_secret[0]);
} else {
s->statem.enc_write_state = ENC_WRITE_STATE_INVALID;
if (s->enc_write_ctx != NULL) {
reuse_dd = 1;
@ -156,13 +173,11 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which)
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
goto err;
} else {
/*
* make sure it's initialised in case we exit later with an error
*/
/* make sure it's initialised in case we exit later with an error */
EVP_CIPHER_CTX_reset(s->enc_write_ctx);
}
dd = s->enc_write_ctx;
if (ssl_replace_hash(&s->write_hash, m) == NULL) {
if (ssl_replace_hash(&s->write_hash, md) == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
goto err;
}
@ -171,7 +186,7 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which)
COMP_CTX_free(s->compress);
s->compress = NULL;
if (comp != NULL) {
s->compress = COMP_CTX_new(comp);
s->compress = COMP_CTX_new(comp->method);
if (s->compress == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
SSL_R_COMPRESSION_LIBRARY_ERROR);
@ -180,54 +195,18 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which)
}
#endif
RECORD_LAYER_reset_write_sequence(&s->rlayer);
mac_secret = &(s->s3.write_mac_secret[0]);
}
memcpy(&(s->s3.write_mac_secret[0]), mac_secret, md_len);
if (reuse_dd)
EVP_CIPHER_CTX_reset(dd);
p = s->s3.tmp.key_block;
mdi = EVP_MD_get_size(m);
if (mdi < 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
i = mdi;
cl = EVP_CIPHER_get_key_length(c);
j = cl;
k = EVP_CIPHER_get_iv_length(c);
if ((which == SSL3_CHANGE_CIPHER_CLIENT_WRITE) ||
(which == SSL3_CHANGE_CIPHER_SERVER_READ)) {
ms = &(p[0]);
n = i + i;
key = &(p[n]);
n += j + j;
iv = &(p[n]);
n += k + k;
} else {
n = i;
ms = &(p[n]);
n += i + j;
key = &(p[n]);
n += j + k;
iv = &(p[n]);
n += k;
}
if (n > s->s3.tmp.key_block_length) {
if (!EVP_CipherInit_ex(dd, ciph, NULL, key, iv, (which & SSL3_CC_WRITE))) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
memcpy(mac_secret, ms, i);
if (!EVP_CipherInit_ex(dd, c, NULL, key, iv, (which & SSL3_CC_WRITE))) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
if (EVP_CIPHER_get0_provider(c) != NULL
&& !tls_provider_set_tls_params(s, dd, c, m)) {
if (EVP_CIPHER_get0_provider(ciph) != NULL
&& !tls_provider_set_tls_params(s, dd, ciph, md)) {
/* SSLfatal already called */
goto err;
}