@@ -29404,6 +29404,93 @@ int GetCipherSuiteFromName(const char* name, byte* cipherSuite0,
2940429404 return ret;
2940529405}
2940629406
29407+ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
29408+ /* Classify a cipher suite for OpenSSL-style "!aNULL"/"!eNULL" exclusion.
29409+ * anon != 0: match suites that perform NO peer authentication.
29410+ * enull != 0: match suites that perform NO encryption.
29411+ * Classification is by suite name: the RFC/IANA name when compiled in
29412+ * (anonymous suites contain "_anon_", named NULL-cipher suites contain "_NULL"
29413+ * - e.g. TLS_DH_anon_*, TLS_ECDH_anon_*, TLS_*_WITH_NULL_*) and the OpenSSL
29414+ * short name (ADH/AECDH/...NULL...), which is the only name available under
29415+ * NO_ERROR_STRINGS. The TLS 1.3 integrity-only suites (RFC 9150) are
29416+ * NULL-encryption too but carry no "NULL" token in their names, so they are
29417+ * matched by suite id below. */
29418+ static int CipherSuiteExcluded(byte first, byte second, int anon, int enull)
29419+ {
29420+ int i;
29421+ const int sz = GetCipherNamesSize();
29422+
29423+ #ifdef HAVE_NULL_CIPHER
29424+ /* TLS 1.3 integrity-only suites perform no encryption; wolfSSL keys them
29425+ * off haveNull in InitSuites(), so honor "!eNULL" for them here too. */
29426+ if (enull && (first == ECC_BYTE) &&
29427+ ((second == TLS_SHA256_SHA256) || (second == TLS_SHA384_SHA384))) {
29428+ return 1;
29429+ }
29430+ #endif
29431+
29432+ for (i = 0; i < sz; i++) {
29433+ if ((cipher_names[i].cipherSuite0 != first) ||
29434+ (cipher_names[i].cipherSuite != second)) {
29435+ continue;
29436+ }
29437+ #ifndef NO_CIPHER_SUITE_ALIASES
29438+ if (cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS) {
29439+ continue;
29440+ }
29441+ #endif
29442+ #ifndef NO_ERROR_STRINGS
29443+ if (cipher_names[i].name_iana != NULL) {
29444+ if (anon &&
29445+ (XSTRSTR(cipher_names[i].name_iana, "_anon_") != NULL)) {
29446+ return 1;
29447+ }
29448+ if (enull &&
29449+ (XSTRSTR(cipher_names[i].name_iana, "_NULL") != NULL)) {
29450+ return 1;
29451+ }
29452+ }
29453+ #endif
29454+ if (cipher_names[i].name != NULL) {
29455+ if (anon && ((XSTRSTR(cipher_names[i].name, "ADH") != NULL) ||
29456+ (XSTRSTR(cipher_names[i].name, "AECDH") != NULL))) {
29457+ return 1;
29458+ }
29459+ if (enull && (XSTRSTR(cipher_names[i].name, "NULL") != NULL)) {
29460+ return 1;
29461+ }
29462+ }
29463+ break; /* found the suite; not excluded */
29464+ }
29465+ return 0;
29466+ }
29467+
29468+ /* Drop suites matching an exclusion from the explicit user-suite array
29469+ * suites[0..*idx). Used to honor an OpenSSL-style exclusion ("!aNULL",
29470+ * "!eNULL"/"!NULL", and the "!aNULL:!eNULL" embedded by DEFAULT): the keyword
29471+ * only adjusts the InitSuites mask, so without this an explicitly listed
29472+ * anonymous or NULL-cipher suite would survive (anonymous = no peer
29473+ * authentication; NULL = no confidentiality). Compacts in place; *idx is a
29474+ * byte count of 2-byte suite pairs. */
29475+ static void RemoveExcludedSuites(byte* suites, int* idx, int anon, int enull)
29476+ {
29477+ int i;
29478+ int out = 0;
29479+
29480+ for (i = 0; (i + 1) < *idx; i += 2) {
29481+ if (CipherSuiteExcluded(suites[i], suites[i + 1], anon, enull)) {
29482+ continue;
29483+ }
29484+ if (out != i) {
29485+ suites[out + 0] = suites[i + 0];
29486+ suites[out + 1] = suites[i + 1];
29487+ }
29488+ out += 2;
29489+ }
29490+ *idx = out;
29491+ }
29492+ #endif /* OPENSSL_EXTRA || OPENSSL_ALL */
29493+
2940729494/**
2940829495Set the enabled cipher suites.
2940929496
@@ -29436,6 +29523,8 @@ static int ParseCipherList(Suites* suites,
2943629523 word16 haveAES128 = 1; /* allowed by default if compiled in */
2943729524 word16 haveSHA1 = 1; /* allowed by default if compiled in */
2943829525 word16 haveRC4 = 1; /* allowed by default if compiled in */
29526+ int excludeAnon = 0; /* "!aNULL"/DEFAULT seen (applied at end) */
29527+ int excludeNull = 0; /* "!eNULL"/"!NULL"/DEFAULT seen */
2943929528#endif
2944029529 int tls1_3 = 0;
2944129530 const int suiteSz = GetCipherNamesSize();
@@ -29540,10 +29629,19 @@ static int ParseCipherList(Suites* suites,
2954029629 }
2954129630
2954229631 if (XSTRCMP(name, "DEFAULT") == 0 || XSTRCMP(name, "ALL") == 0) {
29543- if (XSTRCMP(name, "ALL") == 0)
29632+ if (XSTRCMP(name, "ALL") == 0) {
29633+ /* OpenSSL "ALL" includes anonymous suites and is "all but
29634+ * eNULL" (haveNull=0 below stops eNULL generation), but it is
29635+ * not a delete directive: unlike DEFAULT it must not set
29636+ * excludeAnon/excludeNull, nor clear a prior "!aNULL"/"!eNULL". */
2954429637 haveSig |= SIG_ANON;
29545- else
29638+ }
29639+ else {
29640+ /* OpenSSL "DEFAULT" embeds "!aNULL:!eNULL" (delete both). */
2954629641 haveSig &= ~SIG_ANON;
29642+ excludeAnon = 1;
29643+ excludeNull = 1;
29644+ }
2954729645 haveRSA = 1;
2954829646 haveDH = 1;
2954929647 haveECC = 1;
@@ -29585,6 +29683,11 @@ static int ParseCipherList(Suites* suites,
2958529683 haveSig |= SIG_ANON;
2958629684 else
2958729685 haveSig &= ~SIG_ANON;
29686+ /* Track exclusion (sticky) so an explicit ADH suite is dropped at
29687+ * the end regardless of where "!aNULL" sits in the list; a later
29688+ * allowing "aNULL" does not undo it (OpenSSL "!" is permanent). */
29689+ if (!allowing)
29690+ excludeAnon = 1;
2958829691 if (allowing) {
2958929692 /* Allow RSA by default. */
2959029693 if (!haveECC)
@@ -29599,6 +29702,11 @@ static int ParseCipherList(Suites* suites,
2959929702
2960029703 if (XSTRCMP(name, "eNULL") == 0 || XSTRCMP(name, "NULL") == 0) {
2960129704 haveNull = allowing;
29705+ /* Track exclusion (sticky) so an explicit NULL-cipher suite is
29706+ * dropped at the end regardless of "!eNULL"/"!NULL" position; a
29707+ * later allowing "eNULL" does not undo it. */
29708+ if (!allowing)
29709+ excludeNull = 1;
2960229710 if (allowing) {
2960329711 /* Allow RSA by default. */
2960429712 if (!haveECC)
@@ -29841,6 +29949,38 @@ static int ParseCipherList(Suites* suites,
2984129949 }
2984229950 } while (next);
2984329951
29952+ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
29953+ /* Apply OpenSSL-style category exclusions to the explicitly-listed suites.
29954+ * Done here, after the whole list is parsed, so list order does not matter:
29955+ * the "!aNULL"/"!eNULL" keywords above only adjust the InitSuites mask
29956+ * (which governs generated defaults), never the suites the user named
29957+ * explicitly. The excludeAnon/excludeNull flags are set-only / sticky: like
29958+ * OpenSSL's permanent "!", once "!aNULL"/"!eNULL" has excluded a category a
29959+ * later allowing keyword does not re-enable it - fail closed. */
29960+ if (excludeAnon) {
29961+ haveSig &= ~SIG_ANON;
29962+ RemoveExcludedSuites(suites->suites, &idx, 1, 0);
29963+ }
29964+ if (excludeNull) {
29965+ haveNull = 0;
29966+ RemoveExcludedSuites(suites->suites, &idx, 0, 1);
29967+ }
29968+ /* If the user named suites but every one was excluded, fail like OpenSSL
29969+ * (empty list) rather than silently returning an unusable suite set.
29970+ * callInitSuites means defaults were also requested, so an empty explicit
29971+ * part is fine there. */
29972+ if (ret && !callInitSuites && idx == 0) {
29973+ /* Commit a fully-consistent empty state: suiteSz 0 with setSuites set
29974+ * makes InitSuites() leave it empty (it early-outs on setSuites), so
29975+ * this fails closed - no suites offered, no default regeneration. */
29976+ suites->suiteSz = 0;
29977+ suites->hashSigAlgoSz = 0;
29978+ suites->setSuites = 1;
29979+ WOLFSSL_MSG("Cipher list empty after !aNULL/!eNULL exclusions");
29980+ return 0;
29981+ }
29982+ #endif
29983+
2984429984 if (ret) {
2984529985 int keySz = 0;
2984629986 #ifndef NO_CERTS
0 commit comments