]> git.kaiwu.me - haproxy.git/commitdiff
MEDIUM: ssl: add FIPS TLS 1.3 ciphersuite check for AWS-LC
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 30 Jun 2026 13:14:53 +0000 (13:14 +0000)
committerWilliam Lallemand <wlallemand@haproxy.com>
Tue, 30 Jun 2026 13:57:11 +0000 (13:57 +0000)
AWS-LC does not expose TLS 1.3 ciphersuites set via SSL_CTX_set_ciphersuites()
through SSL_CTX_get_ciphers(), so the existing NID-based cipher check in
ssl_fips_check_ciphers() cannot catch non-FIPS TLS 1.3 suites.  This is
further compounded by a defect in the AWS-LC-FIPS 3.x branch where TLS 1.3
ciphers are missing from SSL_get_ciphers() entirely (fixed in
https://github.com/aws/aws-lc/pull/2092), making any SSL_CTX-based
inspection unreliable across versions.

Add ssl_fips_check_ciphersuites() which validates the ciphersuite string
directly against a FIPS-approved allowlist (TLS_AES_128_GCM_SHA256 and
TLS_AES_256_GCM_SHA384).  A NULL string is silently accepted since the
global defaults were already overwritten with FIPS values at init time.

The new check is called right after SSL_CTX_set_ciphersuites() in both
the bind (ssl_sock_prepare_ctx) and server (ssl_sock_prepare_srv_ssl_ctx)
configuration paths.

include/haproxy/fips.h
src/fips.c
src/ssl_sock.c

index 7cb70dcf68eea03f0708a38e481d1d2976205130..d4c39a99f2bbee9eab9bdbf675d72a13d8c838ff 100644 (file)
@@ -9,6 +9,7 @@
 
 #if defined(OPENSSL_IS_AWSLC)
 int ssl_fips_check_ciphers(SSL_CTX *ctx, const enum obj_type *obj);
+int ssl_fips_check_ciphersuites(const char *ciphersuites, const enum obj_type *obj);
 int ssl_fips_check_version(int min_ver, const enum obj_type *obj);
 #endif
 
index 4fafe972ace1fa6c12fd71629d0b9311d0a8ad42..731abd4f8aa68f52b3f02c21f96719c77ac0650b 100644 (file)
@@ -17,6 +17,13 @@ static const int fips_approved_cipher_nids[] = {
        NID_undef
 };
 
+/* FIPS-approved TLS 1.3 ciphersuite names.  NULL terminates the list. */
+static const char *fips_approved_ciphersuites[] = {
+       "TLS_AES_128_GCM_SHA256",
+       "TLS_AES_256_GCM_SHA384",
+       NULL
+};
+
 /* Fill display fields from <obj> for use in error messages. */
 static void fips_obj_info(const enum obj_type *obj,
                           const char **proxy_name, const char **type_str,
@@ -49,6 +56,48 @@ static void fips_obj_info(const enum obj_type *obj,
        }
 }
 
+/* Check that the TLS 1.3 ciphersuite list <ciphersuites> is FIPS-compliant. */
+int ssl_fips_check_ciphersuites(const char *ciphersuites, const enum obj_type *obj)
+{
+       const char *proxy_name, *type_str, *obj_name, *file;
+       const char *p, *end;
+       char *list = NULL;
+       int i, line;
+       size_t len;
+
+       if (!FIPS_mode() || !ciphersuites)
+               return 0;
+
+       p = ciphersuites;
+       while (p && *p) {
+               end = strchr(p, ':');
+               len = end ? (size_t)(end - p) : strlen(p);
+
+               for (i = 0; fips_approved_ciphersuites[i]; i++) {
+                       if (strlen(fips_approved_ciphersuites[i]) == len &&
+                           strncmp(p, fips_approved_ciphersuites[i], len) == 0)
+                               goto next;
+               }
+               memprintf(&list, "%s%s'%.*s'", list ? list : "",
+                         list ? ", " : "", (int)len, p);
+       next:
+               p = end ? end + 1 : NULL;
+       }
+
+       if (list) {
+               fips_obj_info(obj, &proxy_name, &type_str, &obj_name, &file, &line);
+               if (file)
+                       ha_alert("[%s:%d] %s '%s/%s': FIPS mode active but non-FIPS ciphersuite(s) configured: %s.\n",
+                                file, line, type_str, proxy_name, obj_name, list);
+               else
+                       ha_alert("%s '%s/%s': FIPS mode active but non-FIPS ciphersuite(s) configured: %s.\n",
+                                type_str, proxy_name, obj_name, list);
+               free(list);
+               return ERR_ALERT | ERR_ABORT | ERR_FATAL;
+       }
+       return 0;
+}
+
 /* Check that the TLS 1.2 cipher list configured on <ctx> is FIPS-compliant. */
 int ssl_fips_check_ciphers(SSL_CTX *ctx, const enum obj_type *obj)
 {
index b3907cc6f155716b53d665637a9c8339915d1b0b..f77eb01628165b97613f525fa13ff463c36837bc 100644 (file)
@@ -4730,6 +4730,10 @@ static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_con
                          err && *err ? *err : "", curproxy->id, conf_ciphersuites, bind_conf->arg, bind_conf->file, bind_conf->line);
                cfgerr |= ERR_ALERT | ERR_FATAL;
        }
+#if defined(OPENSSL_IS_AWSLC)
+       cfgerr |= ssl_fips_check_ciphersuites(conf_ciphersuites,
+                                             &LIST_ELEM(bind_conf->listeners.n, struct listener *, by_bind)->obj_type);
+#endif
 #endif
 
 #if defined(OPENSSL_IS_AWSLC)
@@ -5279,6 +5283,9 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx)
                         srv->ssl_ctx.ciphersuites);
                cfgerr++;
        }
+#if defined(OPENSSL_IS_AWSLC)
+       cfgerr += !!ssl_fips_check_ciphersuites(srv->ssl_ctx.ciphersuites, &srv->obj_type);
+#endif
 #endif
 
 #if defined(OPENSSL_IS_AWSLC)