From: William Lallemand Date: Tue, 30 Jun 2026 13:39:14 +0000 (+0000) Subject: MEDIUM: ssl: add FIPS signature algorithm check for AWS-LC X-Git-Url: http://git.kaiwu.me/web/$%7BGITURL%7D/static/gitweb.js?a=commitdiff_plain;ds=sidebyside;p=haproxy.git MEDIUM: ssl: add FIPS signature algorithm check for AWS-LC Add ssl_fips_check_sigalgs() which validates the configured signature algorithm list against the FIPS-approved set: ECDSA on NIST P-curves with SHA-256/384/512, RSA-PSS (rsae and pss variants) with SHA-256/ 384/512, and RSA-PKCS1 with SHA-256/384/512. SHA-1 based algorithms and non-FIPS primitives (ed25519, ed448) are rejected. The check uses the same strchr-based string parsing as ssl_fips_check_ciphersuites(). A NULL list is silently accepted since the global defaults were already overwritten with FIPS values at init time. The check is called right after SSL_CTX_set1_sigalgs_list() and SSL_CTX_set1_client_sigalgs_list() 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 f92c7c644..574673359 100644 --- a/include/haproxy/fips.h +++ b/include/haproxy/fips.h @@ -11,6 +11,7 @@ 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_curves(const char *curves, const enum obj_type *obj, char **err); +int ssl_fips_check_sigalgs(const char *sigalgs, 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 589cc224d..3b4a31418 100644 --- a/src/fips.c +++ b/src/fips.c @@ -30,6 +30,23 @@ static const int fips_approved_curve_nids[] = { NID_undef }; +/* FIPS-approved signature algorithm names. NULL terminates the list. */ +static const char *fips_approved_sigalgs[] = { + "ecdsa_secp256r1_sha256", + "ecdsa_secp384r1_sha384", + "ecdsa_secp521r1_sha512", + "rsa_pss_rsae_sha256", + "rsa_pss_rsae_sha384", + "rsa_pss_rsae_sha512", + "rsa_pss_pss_sha256", + "rsa_pss_pss_sha384", + "rsa_pss_pss_sha512", + "rsa_pkcs1_sha256", + "rsa_pkcs1_sha384", + "rsa_pkcs1_sha512", + NULL +}; + /* 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[] = { @@ -71,6 +88,48 @@ static void fips_obj_info(const enum obj_type *obj, } } +/* Check that the signature algorithm list is FIPS-compliant. */ +int ssl_fips_check_sigalgs(const char *sigalgs, 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() || !sigalgs) + return 0; + + p = sigalgs; + while (p && *p) { + end = strchr(p, ':'); + len = end ? (size_t)(end - p) : strlen(p); + + for (i = 0; fips_approved_sigalgs[i]; i++) { + if (strlen(fips_approved_sigalgs[i]) == len && + strncmp(p, fips_approved_sigalgs[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 signature algorithm(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 signature algorithm(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.3 ciphersuite list is FIPS-compliant. */ int ssl_fips_check_ciphersuites(const char *ciphersuites, const enum obj_type *obj, char **err) { diff --git a/src/ssl_sock.c b/src/ssl_sock.c index ece8cc64f..36a2d1887 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -4839,6 +4839,10 @@ static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_con cfgerr |= ERR_ALERT | ERR_FATAL; } } +#if defined(OPENSSL_IS_AWSLC) + cfgerr |= ssl_fips_check_sigalgs(conf_sigalgs, + &LIST_ELEM(bind_conf->listeners.n, struct listener *, by_bind)->obj_type, err); +#endif #endif #if defined(SSL_CTX_set1_client_sigalgs_list) @@ -4850,6 +4854,10 @@ static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_con cfgerr |= ERR_ALERT | ERR_FATAL; } } +#if defined(OPENSSL_IS_AWSLC) + cfgerr |= ssl_fips_check_sigalgs(conf_client_sigalgs, + &LIST_ELEM(bind_conf->listeners.n, struct listener *, by_bind)->obj_type, err); +#endif #endif #ifdef USE_QUIC_OPENSSL_COMPAT @@ -5324,6 +5332,13 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) cfgerr++; } } +#if defined(OPENSSL_IS_AWSLC) + if (ssl_fips_check_sigalgs(conf_sigalgs, &srv->obj_type, &err)) { + ha_alert("%s", err); + ha_free(&err); + cfgerr++; + } +#endif #endif #if defined(SSL_CTX_set1_client_sigalgs_list) conf_client_sigalgs = srv->ssl_ctx.client_sigalgs; @@ -5334,6 +5349,13 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) cfgerr++; } } +#if defined(OPENSSL_IS_AWSLC) + if (ssl_fips_check_sigalgs(conf_client_sigalgs, &srv->obj_type, &err)) { + ha_alert("%s", err); + ha_free(&err); + cfgerr++; + } +#endif #endif #if defined(SSL_CTX_set1_curves_list)