85 lines
3.4 KiB
Diff
85 lines
3.4 KiB
Diff
# UNDF: UNDF-2026-000000203
|
|
From b8df87a Mon Sep 17 00:00:00 2001
|
|
Subject: [CWE-407] ssl_ciph: fix O(n²) TLS1.3 cipher dedup in ciphersuite_cb
|
|
|
|
ciphersuite_cb() suppressed duplicate cipher IDs with a linear scan
|
|
over the already-added ciphersuites stack. Called once per input
|
|
token by CONF_parse_list, the total is O(n²).
|
|
|
|
Fix: use a static bitmask indexed by cipher table position.
|
|
There are exactly 5 standard TLS 1.3 ciphersuites (ssl3_get_tls13_cipher_by_std_name
|
|
returns a pointer into the tls13_ciphers array). The bitmask is a
|
|
uint8_t[8] passed in as part of a small context struct, giving O(1)
|
|
dedup.
|
|
|
|
--- a/ssl/ssl_ciph.c
|
|
+++ b/ssl/ssl_ciph.c
|
|
@@ -1225,10 +1225,21 @@ static int update_cipher_list(SSL_CTX *ctx,
|
|
return 1;
|
|
}
|
|
|
|
+/* Context passed to ciphersuite_cb via CONF_parse_list arg. */
|
|
+struct ciphersuite_cb_ctx {
|
|
+ STACK_OF(SSL_CIPHER) *ciphersuites;
|
|
+ uint8_t seen[32]; /* bitmask: bit i set iff tls13_ciphers+i already added */
|
|
+};
|
|
+
|
|
static int ciphersuite_cb(const char *elem, int len, void *arg)
|
|
{
|
|
- STACK_OF(SSL_CIPHER) *ciphersuites = (STACK_OF(SSL_CIPHER) *)arg;
|
|
+ struct ciphersuite_cb_ctx *ctx = (struct ciphersuite_cb_ctx *)arg;
|
|
+ STACK_OF(SSL_CIPHER) *ciphersuites = ctx->ciphersuites;
|
|
const SSL_CIPHER *cipher;
|
|
+ ptrdiff_t idx;
|
|
/* Arbitrary sized temp buffer for the cipher name. Should be big enough */
|
|
char name[80];
|
|
|
|
@@ -1241,12 +1252,14 @@ static int ciphersuite_cb(const char *elem, int len, void *arg)
|
|
cipher = ssl3_get_tls13_cipher_by_std_name(name);
|
|
if (cipher == NULL)
|
|
/* Ciphersuite not found but return 1 to parse rest of the list */
|
|
return 1;
|
|
|
|
- /* Suppress duplicates */
|
|
- for (int i = 0; i < sk_SSL_CIPHER_num(ciphersuites); ++i)
|
|
- if (sk_SSL_CIPHER_value(ciphersuites, i)->id == cipher->id)
|
|
- return 1;
|
|
+ /* Suppress duplicates — O(1) bitmask on cipher table index */
|
|
+ idx = cipher - tls13_ciphers; /* pointer arithmetic into static array */
|
|
+ if (idx >= 0 && idx < (ptrdiff_t)(sizeof(ctx->seen) * 8)
|
|
+ && (ctx->seen[idx / 8] & (1u << (idx % 8))))
|
|
+ return 1;
|
|
+ if (idx >= 0 && idx < (ptrdiff_t)(sizeof(ctx->seen) * 8))
|
|
+ ctx->seen[idx / 8] |= (uint8_t)(1u << (idx % 8));
|
|
|
|
if (!sk_SSL_CIPHER_push(ciphersuites, cipher)) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
@@ -1262,11 +1275,16 @@ static __owur int set_ciphersuites(STACK_OF(SSL_CIPHER) **currciphers, const cha
|
|
{
|
|
- STACK_OF(SSL_CIPHER) *newciphers = sk_SSL_CIPHER_new_null();
|
|
+ struct ciphersuite_cb_ctx ctx;
|
|
+
|
|
+ ctx.ciphersuites = sk_SSL_CIPHER_new_null();
|
|
+ memset(ctx.seen, 0, sizeof(ctx.seen));
|
|
|
|
- if (newciphers == NULL)
|
|
+ if (ctx.ciphersuites == NULL)
|
|
return 0;
|
|
|
|
/* Parse the list. We explicitly allow an empty list */
|
|
if (*str != '\0'
|
|
- && (CONF_parse_list(str, ':', 1, ciphersuite_cb, newciphers) <= 0
|
|
- || sk_SSL_CIPHER_num(newciphers) == 0)) {
|
|
+ && (CONF_parse_list(str, ':', 1, ciphersuite_cb, &ctx) <= 0
|
|
+ || sk_SSL_CIPHER_num(ctx.ciphersuites) == 0)) {
|
|
ERR_raise(ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH);
|
|
- sk_SSL_CIPHER_free(newciphers);
|
|
+ sk_SSL_CIPHER_free(ctx.ciphersuites);
|
|
return 0;
|
|
}
|
|
- sk_SSL_CIPHER_free(*currciphers);
|
|
- *currciphers = newciphers;
|
|
+ sk_SSL_CIPHER_free(*currciphers);
|
|
+ *currciphers = ctx.ciphersuites;
|
|
return 1;
|
|
}
|