Skip to content

Commit

Permalink
Merge pull request #632 from dongbeiouba/fix83/CVE-2024-4741
Browse files Browse the repository at this point in the history
Fix CVE-2024-4741 for branch 8.3
  • Loading branch information
InfoHunter authored Jul 1, 2024
2 parents 97bbaea + 905e7a3 commit 4cabfa7
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 28 deletions.
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 @@ -5824,6 +5824,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

0 comments on commit 4cabfa7

Please sign in to comment.