@@ -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/**
2941029496Set 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