Skip to content

Commit fedd3ad

Browse files
committed
Fix !aNULL/!eNULL to drop explicitly-listed anonymous/NULL cipher suites
ParseCipherList() only cleared the InitSuites mask for "!aNULL"/"!eNULL", which governs generated defaults, so an explicitly listed ADH or NULL-cipher suite survived (e.g. "ADH-AES128-SHA:!aNULL" still offered an unauthenticated suite). Scrub the explicit suites after parsing; exclusions are order- independent and sticky (a later "ALL" cannot re-enable them). Add test_wolfSSL_set_cipher_list_exclusions.
1 parent 160b317 commit fedd3ad

2 files changed

Lines changed: 344 additions & 2 deletions

File tree

src/internal.c

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29406,6 +29406,92 @@ int GetCipherSuiteFromName(const char* name, byte* cipherSuite0,
2940629406
return ret;
2940729407
}
2940829408

29409+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
29410+
/* Classify a cipher suite for OpenSSL-style "!aNULL"/"!eNULL" exclusion.
29411+
* anon != 0: match suites that perform NO peer authentication.
29412+
* enull != 0: match suites that perform NO encryption.
29413+
* Classification is by the RFC/IANA suite name: anonymous suites contain
29414+
* "_anon_" and named NULL-cipher suites contain "_NULL" (e.g. TLS_DH_anon_*,
29415+
* TLS_ECDH_anon_*, TLS_*_WITH_NULL_*), with an OpenSSL short-name fallback
29416+
* (ADH/AECDH/...NULL...) when error strings are compiled out. The TLS 1.3
29417+
* integrity-only suites (RFC 9150) are NULL-encryption too but carry no "NULL"
29418+
* token in their names, so they are matched by suite id below. */
29419+
static int CipherSuiteExcluded(byte first, byte second, int anon, int enull)
29420+
{
29421+
int i;
29422+
const int sz = GetCipherNamesSize();
29423+
29424+
#ifdef HAVE_NULL_CIPHER
29425+
/* TLS 1.3 integrity-only suites perform no encryption; wolfSSL keys them
29426+
* off haveNull in InitSuites(), so honor "!eNULL" for them here too. */
29427+
if (enull && (first == ECC_BYTE) &&
29428+
((second == TLS_SHA256_SHA256) || (second == TLS_SHA384_SHA384))) {
29429+
return 1;
29430+
}
29431+
#endif
29432+
29433+
for (i = 0; i < sz; i++) {
29434+
if ((cipher_names[i].cipherSuite0 != first) ||
29435+
(cipher_names[i].cipherSuite != second)) {
29436+
continue;
29437+
}
29438+
#ifndef NO_CIPHER_SUITE_ALIASES
29439+
if (cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS) {
29440+
continue;
29441+
}
29442+
#endif
29443+
#ifndef NO_ERROR_STRINGS
29444+
if (cipher_names[i].name_iana != NULL) {
29445+
if (anon &&
29446+
(XSTRSTR(cipher_names[i].name_iana, "_anon_") != NULL)) {
29447+
return 1;
29448+
}
29449+
if (enull &&
29450+
(XSTRSTR(cipher_names[i].name_iana, "_NULL") != NULL)) {
29451+
return 1;
29452+
}
29453+
}
29454+
#endif
29455+
if (cipher_names[i].name != NULL) {
29456+
if (anon && ((XSTRSTR(cipher_names[i].name, "ADH") != NULL) ||
29457+
(XSTRSTR(cipher_names[i].name, "AECDH") != NULL))) {
29458+
return 1;
29459+
}
29460+
if (enull && (XSTRSTR(cipher_names[i].name, "NULL") != NULL)) {
29461+
return 1;
29462+
}
29463+
}
29464+
break; /* found the suite; not excluded */
29465+
}
29466+
return 0;
29467+
}
29468+
29469+
/* Drop suites matching an exclusion from the explicit user-suite array
29470+
* suites[0..*idx). Used to honor an OpenSSL-style exclusion ("!aNULL",
29471+
* "!eNULL"/"!NULL", and the "!aNULL:!eNULL" embedded by DEFAULT): the keyword
29472+
* only adjusts the InitSuites mask, so without this an explicitly listed
29473+
* anonymous or NULL-cipher suite would survive (anonymous = no peer
29474+
* authentication; NULL = no confidentiality). Compacts in place; *idx is a
29475+
* byte count of 2-byte suite pairs. */
29476+
static void RemoveExcludedSuites(byte* suites, int* idx, int anon, int enull)
29477+
{
29478+
int i;
29479+
int out = 0;
29480+
29481+
for (i = 0; (i + 1) < *idx; i += 2) {
29482+
if (CipherSuiteExcluded(suites[i], suites[i + 1], anon, enull)) {
29483+
continue;
29484+
}
29485+
if (out != i) {
29486+
suites[out + 0] = suites[i + 0];
29487+
suites[out + 1] = suites[i + 1];
29488+
}
29489+
out += 2;
29490+
}
29491+
*idx = out;
29492+
}
29493+
#endif /* OPENSSL_EXTRA || OPENSSL_ALL */
29494+
2940929495
/**
2941029496
Set the enabled cipher suites.
2941129497

@@ -29438,6 +29524,8 @@ static int ParseCipherList(Suites* suites,
2943829524
word16 haveAES128 = 1; /* allowed by default if compiled in */
2943929525
word16 haveSHA1 = 1; /* allowed by default if compiled in */
2944029526
word16 haveRC4 = 1; /* allowed by default if compiled in */
29527+
int excludeAnon = 0; /* OpenSSL "!aNULL" seen (see end of parse) */
29528+
int excludeNull = 0; /* OpenSSL "!eNULL"/"!NULL" seen */
2944129529
#endif
2944229530
int tls1_3 = 0;
2944329531
const int suiteSz = GetCipherNamesSize();
@@ -29542,10 +29630,19 @@ static int ParseCipherList(Suites* suites,
2954229630
}
2954329631

2954429632
if (XSTRCMP(name, "DEFAULT") == 0 || XSTRCMP(name, "ALL") == 0) {
29545-
if (XSTRCMP(name, "ALL") == 0)
29633+
if (XSTRCMP(name, "ALL") == 0) {
29634+
/* OpenSSL "ALL" includes anonymous suites and does not generate
29635+
* eNULL (haveNull=0 below), but it does NOT delete an
29636+
* explicitly-listed NULL suite and does not clear a prior
29637+
* "!aNULL"/"!eNULL" - so it sets neither exclude flag. */
2954629638
haveSig |= SIG_ANON;
29547-
else
29639+
}
29640+
else {
29641+
/* OpenSSL "DEFAULT" embeds "!aNULL:!eNULL" (delete both). */
2954829642
haveSig &= ~SIG_ANON;
29643+
excludeAnon = 1;
29644+
excludeNull = 1;
29645+
}
2954929646
haveRSA = 1;
2955029647
haveDH = 1;
2955129648
haveECC = 1;
@@ -29587,6 +29684,11 @@ static int ParseCipherList(Suites* suites,
2958729684
haveSig |= SIG_ANON;
2958829685
else
2958929686
haveSig &= ~SIG_ANON;
29687+
/* Track exclusion (sticky) so an explicit ADH suite is dropped at
29688+
* the end regardless of where "!aNULL" sits in the list; a later
29689+
* allowing "aNULL" does not undo it (OpenSSL "!" is permanent). */
29690+
if (!allowing)
29691+
excludeAnon = 1;
2959029692
if (allowing) {
2959129693
/* Allow RSA by default. */
2959229694
if (!haveECC)
@@ -29601,6 +29703,11 @@ static int ParseCipherList(Suites* suites,
2960129703

2960229704
if (XSTRCMP(name, "eNULL") == 0 || XSTRCMP(name, "NULL") == 0) {
2960329705
haveNull = allowing;
29706+
/* Track exclusion (sticky) so an explicit NULL-cipher suite is
29707+
* dropped at the end regardless of "!eNULL"/"!NULL" position; a
29708+
* later allowing "eNULL" does not undo it. */
29709+
if (!allowing)
29710+
excludeNull = 1;
2960429711
if (allowing) {
2960529712
/* Allow RSA by default. */
2960629713
if (!haveECC)
@@ -29843,6 +29950,38 @@ static int ParseCipherList(Suites* suites,
2984329950
}
2984429951
} while (next);
2984529952

29953+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
29954+
/* Apply OpenSSL-style category exclusions to the explicitly-listed suites.
29955+
* Done here, after the whole list is parsed, so list order does not matter:
29956+
* the "!aNULL"/"!eNULL" keywords above only adjust the InitSuites mask
29957+
* (which governs generated defaults), never the suites the user named
29958+
* explicitly. The excludeAnon/excludeNull flags are set-only / sticky: like
29959+
* OpenSSL's permanent "!", once a category is excluded a later allowing form
29960+
* ("aNULL", "ALL", ...) does NOT re-enable it - fail closed. */
29961+
if (excludeAnon) {
29962+
haveSig &= ~SIG_ANON;
29963+
RemoveExcludedSuites(suites->suites, &idx, 1, 0);
29964+
}
29965+
if (excludeNull) {
29966+
haveNull = 0;
29967+
RemoveExcludedSuites(suites->suites, &idx, 0, 1);
29968+
}
29969+
/* If the user named suites but every one was excluded, fail like OpenSSL
29970+
* (empty list) rather than silently returning an unusable suite set.
29971+
* callInitSuites means defaults were also requested, so an empty explicit
29972+
* part is fine there. */
29973+
if (ret && !callInitSuites && idx == 0) {
29974+
/* Commit a fully-consistent empty state: suiteSz 0 with setSuites set
29975+
* makes InitSuites() leave it empty (it early-outs on setSuites), so
29976+
* this fails closed - no suites offered, no default regeneration. */
29977+
suites->suiteSz = 0;
29978+
suites->hashSigAlgoSz = 0;
29979+
suites->setSuites = 1;
29980+
WOLFSSL_MSG("Cipher list empty after !aNULL/!eNULL exclusions");
29981+
return 0;
29982+
}
29983+
#endif
29984+
2984629985
if (ret) {
2984729986
int keySz = 0;
2984829987
#ifndef NO_CERTS

0 commit comments

Comments
 (0)