Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CVE-2024-4741 for branch 8.3 #632

Merged
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

Changes between 8.3.3 and 8.3.4 [xxxx年xx月xx日]

*) 修复CVE-2024-4741

*) 修复CVE-2024-2511

*) 修复CVE-2024-0727
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.en
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

Changes between 8.3.3 and 8.3.4 [xx XXX xxxx]

*) Fix CVE-2024-4741

*) Fix CVE-2024-2511

*) Fix CVE-2024-0727
Expand Down
52 changes: 28 additions & 24 deletions ssl/record/rec_layer_s3.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <openssl/rand.h>
#include "record_local.h"
#include "../packet_local.h"
#include "internal/cryptlib.h"

#if defined(OPENSSL_SMALL_FOOTPRINT) || \
!( defined(AESNI_ASM) && ( \
Expand Down Expand Up @@ -80,6 +81,15 @@ int RECORD_LAYER_read_pending(const RECORD_LAYER *rl)
return SSL3_BUFFER_get_left(&rl->rbuf) != 0;
}

int RECORD_LAYER_data_present(const RECORD_LAYER *rl)
{
if (rl->rstate == SSL_ST_READ_BODY)
return 1;
if (RECORD_LAYER_processed_read_pending(rl))
return 1;
return 0;
}

/* Checks if we have decrypted unread record data pending */
int RECORD_LAYER_processed_read_pending(const RECORD_LAYER *rl)
{
Expand Down Expand Up @@ -202,30 +212,19 @@ int ssl3_read_n(SSL *s, size_t n, size_t max, int extend, int clearold,
/* start with empty packet ... */
if (left == 0)
rb->offset = align;
else if (align != 0 && left >= SSL3_RT_HEADER_LENGTH) {
/*
* check if next packet length is large enough to justify payload
* alignment...
*/
pkt = rb->buf + rb->offset;
if (pkt[0] == SSL3_RT_APPLICATION_DATA
&& (pkt[3] << 8 | pkt[4]) >= 128) {
/*
* Note that even if packet is corrupted and its length field
* is insane, we can only be led to wrong decision about
* whether memmove will occur or not. Header values has no
* effect on memmove arguments and therefore no buffer
* overrun can be triggered.
*/
memmove(rb->buf + align, pkt, left);
rb->offset = align;
}
}

s->rlayer.packet = rb->buf + rb->offset;
s->rlayer.packet_length = 0;
/* ... now we can act as if 'extend' was set */
}

if (!ossl_assert(s->rlayer.packet != NULL)) {
/* does not happen */
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_N,
ERR_R_INTERNAL_ERROR);
return -1;
}

len = s->rlayer.packet_length;
pkt = rb->buf + align;
/*
Expand Down Expand Up @@ -598,14 +597,13 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
if (numpipes > maxpipes)
numpipes = maxpipes;

if (n / numpipes >= max_send_fragment) {
if (n / numpipes >= split_send_fragment) {
/*
* We have enough data to completely fill all available
* pipelines
*/
for (j = 0; j < numpipes; j++) {
pipelens[j] = max_send_fragment;
}
for (j = 0; j < numpipes; j++)
pipelens[j] = split_send_fragment;
} else {
/* We can partially fill all available pipelines */
tmppipelen = n / numpipes;
Expand Down Expand Up @@ -1162,7 +1160,13 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
SSL_R_BIO_NOT_SET);
i = -1;
}
if (i > 0 && tmpwrit == SSL3_BUFFER_get_left(&wb[currbuf])) {

/*
* If zero byte write succeeds, i will be 0 rather than a non-zero
* value. Treat i == 0 as success rather than an error for zero byte
* writes to permit this case.
*/
if (i >= 0 && tmpwrit == SSL3_BUFFER_get_left(&wb[currbuf])) {
SSL3_BUFFER_set_left(&wb[currbuf], 0);
SSL3_BUFFER_add_offset(&wb[currbuf], tmpwrit);
if (currbuf + 1 < s->rlayer.numwpipes)
Expand Down
1 change: 1 addition & 0 deletions ssl/record/record.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void RECORD_LAYER_release(RECORD_LAYER *rl);
int RECORD_LAYER_read_pending(const RECORD_LAYER *rl);
int RECORD_LAYER_processed_read_pending(const RECORD_LAYER *rl);
int RECORD_LAYER_write_pending(const RECORD_LAYER *rl);
int RECORD_LAYER_data_present(const RECORD_LAYER *rl);
void RECORD_LAYER_reset_read_sequence(RECORD_LAYER *rl);
void RECORD_LAYER_reset_write_sequence(RECORD_LAYER *rl);
int RECORD_LAYER_is_sslv2_record(RECORD_LAYER *rl);
Expand Down
7 changes: 7 additions & 0 deletions ssl/record/ssl3_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ int ssl3_setup_read_buffer(SSL *s)
if (ssl_allow_compression(s))
len += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
#endif

/* Ensure our buffer is large enough to support all our pipelines */
if (s->max_pipelines > 1)
len *= s->max_pipelines;

if (b->default_len > len)
len = b->default_len;
if ((p = OPENSSL_malloc(len)) == NULL) {
Expand Down Expand Up @@ -175,5 +180,7 @@ int ssl3_release_read_buffer(SSL *s)
b = RECORD_LAYER_get_rbuf(&s->rlayer);
OPENSSL_free(b->buf);
b->buf = NULL;
s->rlayer.packet = NULL;
s->rlayer.packet_length = 0;
return 1;
}
3 changes: 1 addition & 2 deletions ssl/record/ssl3_record.c
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
EVP_CIPHER_CTX *ds;
size_t reclen[SSL_MAX_PIPELINES];
unsigned char buf[SSL_MAX_PIPELINES][EVP_AEAD_TLS1_AAD_LEN];
unsigned char *data[SSL_MAX_PIPELINES];
int i, pad = 0, ret, tmpr;
size_t bs, mac_size = 0, ctr, padnum, loop;
unsigned char padval;
Expand Down Expand Up @@ -1113,8 +1114,6 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
}
}
if (n_recs > 1) {
unsigned char *data[SSL_MAX_PIPELINES];

/* Set the output buffers */
for (ctr = 0; ctr < n_recs; ctr++) {
data[ctr] = recs[ctr].data;
Expand Down
3 changes: 3 additions & 0 deletions ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -5822,6 +5822,9 @@ int SSL_free_buffers(SSL *ssl)
if (RECORD_LAYER_read_pending(rl) || RECORD_LAYER_write_pending(rl))
return 0;

if (RECORD_LAYER_data_present(rl))
return 0;

RECORD_LAYER_release(rl);
return 1;
}
Expand Down
204 changes: 204 additions & 0 deletions test/sslapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <openssl/srp.h>
#include <openssl/txt_db.h>
#include <openssl/aes.h>
#include <openssl/engine.h>
#include <openssl/rand.h>

#include "ssltestlib.h"
#include "testutil.h"
Expand Down Expand Up @@ -7283,6 +7285,205 @@ static int test_session_cache_overflow(int idx)
}
#endif /* !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) */

#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
/*
* Test TLSv1.2 with a pipeline capable cipher. TLSv1.3 and DTLS do not
* support this yet. The only pipeline capable cipher that we have is in the
* dasync engine (providers don't support this yet), so we have to use
* deprecated APIs for this test.
*
* Test 0: Client has pipelining enabled, server does not
* Test 1: Server has pipelining enabled, client does not
* Test 2: Client has pipelining enabled, server does not: not enough data to
* fill all the pipelines
* Test 3: Client has pipelining enabled, server does not: not enough data to
* fill all the pipelines by more than a full pipeline's worth
* Test 4: Client has pipelining enabled, server does not: more data than all
* the available pipelines can take
* Test 5: Client has pipelining enabled, server does not: Maximum size pipeline
* Test 6: Repeat of test 0, but the engine is loaded late (after the SSL_CTX
* is created)
*/
static int test_pipelining(int idx)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL, *peera, *peerb;
int testresult = 0, numreads;
/* A 55 byte message */
unsigned char *msg = (unsigned char *)
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123";
size_t written, readbytes, offset, msglen, fragsize = 10, numpipes = 5;
size_t expectedreads;
unsigned char *buf = NULL;
ENGINE *e = NULL;

if (idx != 6) {
e = load_dasync();
if (e == NULL)
return 0;
}

if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(),
TLS_client_method(), 0,
TLS1_2_VERSION, &sctx, &cctx, cert,
privkey)))
goto end;

if (idx == 6) {
e = load_dasync();
if (e == NULL)
goto end;
/* Now act like test 0 */
idx = 0;
}

if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL)))
goto end;

if (!TEST_true(SSL_set_cipher_list(clientssl, "AES128-SHA")))
goto end;

/* peera is always configured for pipelining, while peerb is not. */
if (idx == 1) {
peera = serverssl;
peerb = clientssl;

} else {
peera = clientssl;
peerb = serverssl;
}

if (idx == 5) {
numpipes = 2;
/* Maximum allowed fragment size */
fragsize = SSL3_RT_MAX_PLAIN_LENGTH;
msglen = fragsize * numpipes;
msg = OPENSSL_malloc(msglen);
if (!TEST_ptr(msg))
goto end;
if (!TEST_int_gt(RAND_bytes(msg, msglen), 0))
goto end;
} else if (idx == 4) {
msglen = 55;
} else {
msglen = 50;
}
if (idx == 2)
msglen -= 2; /* Send 2 less bytes */
else if (idx == 3)
msglen -= 12; /* Send 12 less bytes */

buf = OPENSSL_malloc(msglen);
if (!TEST_ptr(buf))
goto end;

if (idx == 5) {
/*
* Test that setting a split send fragment longer than the maximum
* allowed fails
*/
if (!TEST_false(SSL_set_split_send_fragment(peera, fragsize + 1)))
goto end;
}

/*
* In the normal case. We have 5 pipelines with 10 bytes per pipeline
* (50 bytes in total). This is a ridiculously small number of bytes -
* but sufficient for our purposes
*/
if (!TEST_true(SSL_set_max_pipelines(peera, numpipes))
|| !TEST_true(SSL_set_split_send_fragment(peera, fragsize)))
goto end;

if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
goto end;

/* Write some data from peera to peerb */
if (!TEST_true(SSL_write_ex(peera, msg, msglen, &written))
|| !TEST_size_t_eq(written, msglen))
goto end;

/*
* If the pipelining code worked, then we expect all |numpipes| pipelines to
* have been used - except in test 3 where only |numpipes - 1| pipelines
* will be used. This will result in |numpipes| records (|numpipes - 1| for
* test 3) having been sent to peerb. Since peerb is not using read_ahead we
* expect this to be read in |numpipes| or |numpipes - 1| separate
* SSL_read_ex calls. In the case of test 4, there is then one additional
* read for left over data that couldn't fit in the previous pipelines
*/
for (offset = 0, numreads = 0;
offset < msglen;
offset += readbytes, numreads++) {
if (!TEST_true(SSL_read_ex(peerb, buf + offset,
msglen - offset, &readbytes)))
goto end;
}

expectedreads = idx == 4 ? numpipes + 1
: (idx == 3 ? numpipes - 1 : numpipes);
if (!TEST_mem_eq(msg, msglen, buf, offset)
|| !TEST_int_eq(numreads, expectedreads))
goto end;

/*
* Write some data from peerb to peera. We do this in up to |numpipes + 1|
* chunks to exercise the read pipelining code on peera.
*/
for (offset = 0; offset < msglen; offset += fragsize) {
size_t sendlen = msglen - offset;

if (sendlen > fragsize)
sendlen = fragsize;
if (!TEST_true(SSL_write_ex(peerb, msg + offset, sendlen, &written))
|| !TEST_size_t_eq(written, sendlen))
goto end;
}

/*
* The data was written in |numpipes|, |numpipes - 1| or |numpipes + 1|
* separate chunks (depending on which test we are running). If the
* pipelining is working then we expect peera to read up to numpipes chunks
* and process them in parallel, giving back the complete result in a single
* call to SSL_read_ex
*/
if (!TEST_true(SSL_read_ex(peera, buf, msglen, &readbytes))
|| !TEST_size_t_le(readbytes, msglen))
goto end;

if (idx == 4) {
size_t readbytes2;

if (!TEST_true(SSL_read_ex(peera, buf + readbytes,
msglen - readbytes, &readbytes2)))
goto end;
readbytes += readbytes2;
if (!TEST_size_t_le(readbytes, msglen))
goto end;
}

if (!TEST_mem_eq(msg, msglen, buf, readbytes))
goto end;

testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
if (e != NULL) {
ENGINE_unregister_ciphers(e);
ENGINE_finish(e);
ENGINE_free(e);
}
OPENSSL_free(buf);
if (fragsize == SSL3_RT_MAX_PLAIN_LENGTH)
OPENSSL_free(msg);
return testresult;
}
#endif /* !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE) */

struct resume_servername_cb_data {
int i;
SSL_CTX *cctx;
Expand Down Expand Up @@ -7582,6 +7783,9 @@ int setup_tests(void)
#endif
#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2)
ADD_ALL_TESTS(test_session_cache_overflow, 4);
#endif
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
ADD_ALL_TESTS(test_pipelining, 7);
#endif
ADD_ALL_TESTS(test_multi_resume, 5);

Expand Down
Loading
Loading