Skip to content

Commit

Permalink
Merge pull request #634 from dongbeiouba/fix/CVE-2024-5535
Browse files Browse the repository at this point in the history
  • Loading branch information
InfoHunter authored Aug 19, 2024
2 parents 0153a79 + 9573eb9 commit eddba6c
Show file tree
Hide file tree
Showing 16 changed files with 1,050 additions and 271 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Changes between 8.4.0 and 8.5.0 [xx XXX xxxx]

*) 修复CVE-2024-5535

*) 修复CVE-2024-4741

*) 修复CVE-2024-4603
Expand Down
63 changes: 40 additions & 23 deletions ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -3010,37 +3010,54 @@ int SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
unsigned int server_len,
const unsigned char *client, unsigned int client_len)
{
unsigned int i, j;
const unsigned char *result;
int status = OPENSSL_NPN_UNSUPPORTED;
PACKET cpkt, csubpkt, spkt, ssubpkt;

if (!PACKET_buf_init(&cpkt, client, client_len)
|| !PACKET_get_length_prefixed_1(&cpkt, &csubpkt)
|| PACKET_remaining(&csubpkt) == 0) {
*out = NULL;
*outlen = 0;
return OPENSSL_NPN_NO_OVERLAP;
}

/*
* Set the default opportunistic protocol. Will be overwritten if we find
* a match.
*/
*out = (unsigned char *)PACKET_data(&csubpkt);
*outlen = (unsigned char)PACKET_remaining(&csubpkt);

/*
* For each protocol in server preference order, see if we support it.
*/
for (i = 0; i < server_len;) {
for (j = 0; j < client_len;) {
if (server[i] == client[j] &&
memcmp(&server[i + 1], &client[j + 1], server[i]) == 0) {
/* We found a match */
result = &server[i];
status = OPENSSL_NPN_NEGOTIATED;
goto found;
if (PACKET_buf_init(&spkt, server, server_len)) {
while (PACKET_get_length_prefixed_1(&spkt, &ssubpkt)) {
if (PACKET_remaining(&ssubpkt) == 0)
continue; /* Invalid - ignore it */
if (PACKET_buf_init(&cpkt, client, client_len)) {
while (PACKET_get_length_prefixed_1(&cpkt, &csubpkt)) {
if (PACKET_equal(&csubpkt, PACKET_data(&ssubpkt),
PACKET_remaining(&ssubpkt))) {
/* We found a match */
*out = (unsigned char *)PACKET_data(&ssubpkt);
*outlen = (unsigned char)PACKET_remaining(&ssubpkt);
return OPENSSL_NPN_NEGOTIATED;
}
}
/* Ignore spurious trailing bytes in the client list */
} else {
/* This should never happen */
return OPENSSL_NPN_NO_OVERLAP;
}
j += client[j];
j++;
}
i += server[i];
i++;
/* Ignore spurious trailing bytes in the server list */
}

/* There's no overlap between our protocols and the server's list. */
result = client;
status = OPENSSL_NPN_NO_OVERLAP;

found:
*out = (unsigned char *)result + 1;
*outlen = result[0];
return status;
/*
* There's no overlap between our protocols and the server's list. We use
* the default opportunistic protocol selected earlier
*/
return OPENSSL_NPN_NO_OVERLAP;
}

#ifndef OPENSSL_NO_NEXTPROTONEG
Expand Down
27 changes: 26 additions & 1 deletion ssl/statem/extensions_clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,8 @@ int tls_parse_stoc_npn(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
PACKET_data(pkt),
PACKET_remaining(pkt),
s->ctx->ext.npn_select_cb_arg) !=
SSL_TLSEXT_ERR_OK) {
SSL_TLSEXT_ERR_OK
|| selected_len == 0) {
SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_EXTENSION);
return 0;
}
Expand Down Expand Up @@ -1679,6 +1680,8 @@ int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
size_t chainidx)
{
size_t len;
PACKET confpkt, protpkt;
int valid = 0;

/* We must have requested it. */
if (!s->s3.alpn_sent) {
Expand All @@ -1697,6 +1700,28 @@ int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
return 0;
}

/* It must be a protocol that we sent */
if (!PACKET_buf_init(&confpkt, s->ext.alpn, s->ext.alpn_len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
while (PACKET_get_length_prefixed_1(&confpkt, &protpkt)) {
if (PACKET_remaining(&protpkt) != len)
continue;
if (memcmp(PACKET_data(pkt), PACKET_data(&protpkt), len) == 0) {
/* Valid protocol found */
valid = 1;
break;
}
}

if (!valid) {
/* The protocol sent from the server does not match one we advertised */
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
return 0;
}

OPENSSL_free(s->s3.alpn_selected);
s->s3.alpn_selected = OPENSSL_malloc(len);
if (s->s3.alpn_selected == NULL) {
Expand Down
3 changes: 2 additions & 1 deletion ssl/statem/extensions_srvr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1566,9 +1566,10 @@ EXT_RETURN tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt,
return EXT_RETURN_FAIL;
}
s->s3.npn_seen = 1;
return EXT_RETURN_SENT;
}

return EXT_RETURN_SENT;
return EXT_RETURN_NOT_SENT;
}
#endif

Expand Down
27 changes: 26 additions & 1 deletion ssl/statem_ntls/ntls_extensions_clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,8 @@ int tls_parse_stoc_npn_ntls(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
PACKET_data(pkt),
PACKET_remaining(pkt),
s->ctx->ext.npn_select_cb_arg) !=
SSL_TLSEXT_ERR_OK) {
SSL_TLSEXT_ERR_OK
|| selected_len == 0) {
SSLfatal_ntls(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_EXTENSION);
return 0;
}
Expand Down Expand Up @@ -673,6 +674,8 @@ int tls_parse_stoc_alpn_ntls(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
size_t chainidx)
{
size_t len;
PACKET confpkt, protpkt;
int valid = 0;

/* We must have requested it. */
if (!s->s3.alpn_sent) {
Expand All @@ -691,6 +694,28 @@ int tls_parse_stoc_alpn_ntls(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
SSLfatal_ntls(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
return 0;
}

/* It must be a protocol that we sent */
if (!PACKET_buf_init(&confpkt, s->ext.alpn, s->ext.alpn_len)) {
SSLfatal_ntls(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
while (PACKET_get_length_prefixed_1(&confpkt, &protpkt)) {
if (PACKET_remaining(&protpkt) != len)
continue;
if (memcmp(PACKET_data(pkt), PACKET_data(&protpkt), len) == 0) {
/* Valid protocol found */
valid = 1;
break;
}
}

if (!valid) {
/* The protocol sent from the server does not match one we advertised */
SSLfatal_ntls(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
return 0;
}

OPENSSL_free(s->s3.alpn_selected);
s->s3.alpn_selected = OPENSSL_malloc(len);
if (s->s3.alpn_selected == NULL) {
Expand Down
3 changes: 2 additions & 1 deletion ssl/statem_ntls/ntls_extensions_srvr.c
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,10 @@ EXT_RETURN tls_construct_stoc_next_proto_neg_ntls(SSL *s, WPACKET *pkt,
return EXT_RETURN_FAIL;
}
s->s3.npn_seen = 1;
return EXT_RETURN_SENT;
}

return EXT_RETURN_SENT;
return EXT_RETURN_NOT_SENT;
}
#endif

Expand Down
6 changes: 6 additions & 0 deletions test/helpers/handshake.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ static int parse_protos(const char *protos, unsigned char **out, size_t *outlen)

len = strlen(protos);

if (len == 0) {
*out = NULL;
*outlen = 0;
return 1;
}

/* Should never have reuse. */
if (!TEST_ptr_null(*out)
/* Test values are small, so we omit length limit checks. */
Expand Down
73 changes: 73 additions & 0 deletions test/recipes/70-test_npn.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#! /usr/bin/env perl
# Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

use strict;
use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file/;
use OpenSSL::Test::Utils;

use TLSProxy::Proxy;

my $test_name = "test_npn";
setup($test_name);

plan skip_all => "TLSProxy isn't usable on $^O"
if $^O =~ /^(VMS)$/;

plan skip_all => "$test_name needs the dynamic engine feature enabled"
if disabled("engine") || disabled("dynamic-engine");

plan skip_all => "$test_name needs the sock feature enabled"
if disabled("sock");

plan skip_all => "$test_name needs NPN enabled"
if disabled("nextprotoneg");

plan skip_all => "$test_name needs TLSv1.2 enabled"
if disabled("tls1_2");

my $proxy = TLSProxy::Proxy->new(
undef,
cmdstr(app(["openssl"]), display => 1),
srctop_file("apps", "server.pem"),
(!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
);

$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 1;

my $npnseen = 0;

# Test 1: Check sending an empty NextProto message from the client works. This is
# valid as per the spec, but OpenSSL does not allow you to send it.
# Therefore we must be prepared to receive such a message but we cannot
# generate it except via TLSProxy
$proxy->clear();
$proxy->filter(\&npn_filter);
$proxy->clientflags("-nextprotoneg foo -no_tls1_3");
$proxy->serverflags("-nextprotoneg foo");
$proxy->start();
ok($npnseen && TLSProxy::Message->success(), "Empty NPN message");

sub npn_filter
{
my $proxy = shift;
my $message;

# The NextProto message always appears in flight 2
return if $proxy->flight != 2;

foreach my $message (@{$proxy->message_list}) {
if ($message->mt == TLSProxy::Message::MT_NEXT_PROTO) {
# Our TLSproxy NextProto message support doesn't support parsing of
# the message. If we repack it just creates an empty NextProto
# message - which is exactly the scenario we want to test here.
$message->repack();
$npnseen = 1;
}
}
}
Loading

0 comments on commit eddba6c

Please sign in to comment.