From: Matt Caswell Date: Thu, 28 Apr 2022 15:57:07 +0000 (+0100) Subject: Convert SSLv3 code to use the new read side record layer X-Git-Tag: openssl-3.2.0-alpha1~2250 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=10560aed15dd71601b89c8f0308f30b70744c914;p=thirdparty%2Fopenssl.git Convert SSLv3 code to use the new read side record layer Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/18132) --- diff --git a/ssl/record/methods/tlsrecord.c b/ssl/record/methods/tlsrecord.c index 691dd12ca57..1646ba5ba18 100644 --- a/ssl/record/methods/tlsrecord.c +++ b/ssl/record/methods/tlsrecord.c @@ -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); diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index 348b8747801..8e6db3f0c16 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -90,128 +90,55 @@ 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 - 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) { - 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; - } - } -#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; - } else if ((s->enc_write_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_write_ctx); - } - dd = s->enc_write_ctx; - if (ssl_replace_hash(&s->write_hash, m) == NULL) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE); - goto err; - } -#ifndef OPENSSL_NO_COMP - /* COMPRESS */ - COMP_CTX_free(s->compress); - s->compress = NULL; - if (comp != NULL) { - s->compress = COMP_CTX_new(comp); - if (s->compress == NULL) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_R_COMPRESSION_LIBRARY_ERROR); - goto err; - } - } -#endif - RECORD_LAYER_reset_write_sequence(&s->rlayer); - mac_secret = &(s->s3.write_mac_secret[0]); - } - - if (reuse_dd) - EVP_CIPHER_CTX_reset(dd); - p = s->s3.tmp.key_block; - mdi = EVP_MD_get_size(m); + mdi = EVP_MD_get_size(md); 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); + 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)) { - ms = &(p[0]); - n = i + i; + mac_secret = &(p[0]); + n = md_len + md_len; key = &(p[n]); - n += j + j; + n += key_len + key_len; iv = &(p[n]); - n += k + k; + n += iv_len + iv_len; } else { - n = i; - ms = &(p[n]); - n += i + j; + n = md_len; + mac_secret = &(p[n]); + n += md_len + key_len; key = &(p[n]); - n += j + k; + n += key_len + iv_len; iv = &(p[n]); - n += k; + n += iv_len; } if (n > s->s3.tmp.key_block_length) { @@ -219,15 +146,67 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which) goto err; } - memcpy(mac_secret, ms, i); + if (which & SSL3_CC_READ) { + 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; + } + + s->statem.enc_write_state = ENC_WRITE_STATE_VALID; + return 1; + } + + s->statem.enc_write_state = ENC_WRITE_STATE_INVALID; + if (s->enc_write_ctx != NULL) { + reuse_dd = 1; + } else if ((s->enc_write_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_write_ctx); + } + dd = s->enc_write_ctx; + if (ssl_replace_hash(&s->write_hash, md) == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE); + goto err; + } +#ifndef OPENSSL_NO_COMP + /* COMPRESS */ + COMP_CTX_free(s->compress); + s->compress = NULL; + if (comp != NULL) { + s->compress = COMP_CTX_new(comp->method); + if (s->compress == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_R_COMPRESSION_LIBRARY_ERROR); + goto err; + } + } +#endif + RECORD_LAYER_reset_write_sequence(&s->rlayer); + memcpy(&(s->s3.write_mac_secret[0]), mac_secret, md_len); + + if (reuse_dd) + EVP_CIPHER_CTX_reset(dd); - if (!EVP_CipherInit_ex(dd, c, NULL, key, iv, (which & SSL3_CC_WRITE))) { + 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; } - 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; }