From: William Lallemand Date: Tue, 30 Jun 2026 13:14:53 +0000 (+0000) Subject: MEDIUM: ssl: add FIPS TLS 1.3 ciphersuite check for AWS-LC X-Git-Url: http://git.kaiwu.me/%7Bsubstring-before(@doc,%20'.xml')%7D.html?a=commitdiff_plain;h=29f5126317ebc3f48575f98b69143060baa2cf05;p=haproxy.git MEDIUM: ssl: add FIPS TLS 1.3 ciphersuite check for AWS-LC 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. --- diff --git a/include/haproxy/fips.h b/include/haproxy/fips.h index 031adb380..02198fede 100644 --- a/include/haproxy/fips.h +++ b/include/haproxy/fips.h @@ -9,6 +9,7 @@ #if defined(OPENSSL_IS_AWSLC) int ssl_fips_check_ciphers(SSL_CTX *ctx, const enum obj_type *obj, char **err); +int ssl_fips_check_ciphersuites(const char *ciphersuites, const enum obj_type *obj, char **err); int ssl_fips_check_version(int min_ver, const enum obj_type *obj); #endif diff --git a/src/fips.c b/src/fips.c index 5c4d6599c..fc7c994d1 100644 --- a/src/fips.c +++ b/src/fips.c @@ -20,6 +20,15 @@ static const int fips_approved_cipher_nids[] = { NID_undef }; +/* FIPS-approved TLS 1.3 ciphersuite names. NULL terminates the list. + * TLS_AES_128_CCM_SHA256 is FIPS-approved but not implemented by AWS-LC. */ +static const char *fips_approved_ciphersuites[] = { + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_CCM_SHA256", + NULL +}; + /* Fill display fields from for use in error messages. */ static void fips_obj_info(const enum obj_type *obj, const char **proxy_name, const char **type_str, @@ -52,6 +61,48 @@ static void fips_obj_info(const enum obj_type *obj, } } +/* Check that the TLS 1.3 ciphersuite list is FIPS-compliant. */ +int ssl_fips_check_ciphersuites(const char *ciphersuites, const enum obj_type *obj, char **err) +{ + 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) + memprintf(err, "%s[%s:%d] %s '%s/%s': FIPS mode active but non-FIPS ciphersuite(s) configured: %s.\n", + (err && *err) ? *err : "", file, line, type_str, proxy_name, obj_name, list); + else + memprintf(err, "%s%s '%s/%s': FIPS mode active but non-FIPS ciphersuite(s) configured: %s.\n", + (err && *err) ? *err : "", 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 is FIPS-compliant. */ int ssl_fips_check_ciphers(SSL_CTX *ctx, const enum obj_type *obj, char **err) { diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 0fb4b6951..dddf8c164 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -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, err); +#endif #endif #if defined(OPENSSL_IS_AWSLC) @@ -5282,6 +5286,13 @@ 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) + if (ssl_fips_check_ciphersuites(srv->ssl_ctx.ciphersuites, &srv->obj_type, &err)) { + ha_alert("%s", err); + ha_free(&err); + cfgerr++; + } +#endif #endif #if defined(OPENSSL_IS_AWSLC)