From 5dc91df283fb5a7030b384f224085d73268dcca5 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Wed, 6 Mar 2024 15:33:25 +0000 Subject: [PATCH] LU-17624 ssk: support FIPS mode on client In FIPS mode, only certain crypto methods are allowed. This has an impact on the DHKE mechanism implemented for SSK, as this relies on a prime number generated for the client key. More specifically, FIPS mode imposes that only certain safe, well-known primes be used. OpenSSL prior to v1.1 just imposes a requirement on the prime length. OpenSSL v1.1 requires the use of a specific primitive when FIPS mode is on, to fetch a well-known prime based on a prime NID. OpenSSL v3 is capable of detecting FIPS mode is enforced, and picks up a well-known prime instead of generating one. Because of this, primes used for the DHKE are identical on all clients in FIPS mode. So urge admins to use a short expiration time on SSK keys, one day instead of one week, so that security contexts are re-negotiated more often. The NIST recommended primes are from see Table 26 in Appendix D of: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf Test-Parameters: trivial Test-Parameters: testgroup=review-dne-selinux-ssk-part-1 Test-Parameters: testgroup=review-dne-selinux-ssk-part-2 Test-Parameters: testgroup=review-dne-selinux-ssk-part-1 clientdistro=el9.2 Test-Parameters: testgroup=review-dne-selinux-ssk-part-2 clientdistro=el9.2 Signed-off-by: Sebastien Buisson Change-Id: I52b1926393e51fba6a9e92a837f86a38516ef6ad Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54314 Reviewed-by: Oleg Drokin Reviewed-by: James Simmons Reviewed-by: Andreas Dilger Tested-by: jenkins Tested-by: Maloo --- lustre/autoconf/lustre-core.m4 | 31 ++++++++++ lustre/include/uapi/linux/lustre/lustre_user.h | 6 ++ lustre/utils/gss/lgss_sk.c | 80 +++++++++++++++++++++++++- lustre/utils/gss/sk_utils.h | 73 +++++++++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index f139924..6b6c0fb 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -405,6 +405,36 @@ AC_MSG_RESULT([$has_hmac_functions]) CFLAGS="$saved_flags" ]) # LC_OPENSSL_HMAC +# LC_OPENSSL_FIPS +# +# OpenSSL 1.0+ can be built with or without FIPS support +AC_DEFUN([LC_OPENSSL_FIPS], [ +has_fips_support="no" +saved_flags="$CFLAGS" +CFLAGS="-Werror" +AC_MSG_CHECKING([whether OpenSSL has FIPS_mode]) +AS_IF([test "x$enable_ssk" != xno], [ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + #include + #include + #include + #include + #include + + int main(void) { + int rc; + rc = FIPS_mode(); + return rc; + } +])],[ + AC_DEFINE(HAVE_OPENSSL_FIPS, 1, [OpenSSL FIPS_mode]) + has_fips_support="yes" +]) +]) +AC_MSG_RESULT([$has_fips_support]) +CFLAGS="$saved_flags" +]) # LC_OPENSSL_FIPS + # LC_OPENSSL_EVP_PKEY # # OpenSSL 3.0 introduces EVP_PKEY_get_params @@ -440,6 +470,7 @@ AC_MSG_RESULT([$has_evp_pkey]) AC_DEFUN([LC_OPENSSL_SSK], [ AS_IF([test "x$enable_ssk" != xno], [ LC_OPENSSL_HMAC + LC_OPENSSL_FIPS LC_OPENSSL_EVP_PKEY ]) AS_IF([test "x$has_hmac_functions" = xyes -o "x$has_evp_pkey" = xyes], [ diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index 376c622..682a5bf 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -2877,6 +2877,12 @@ struct sk_hmac_type { int sht_type; }; +struct sk_prime_type { + const char *spt_name; + int spt_type; + int spt_primebits; +}; + enum lock_mode_user { MODE_READ_USER = 1, MODE_WRITE_USER, diff --git a/lustre/utils/gss/lgss_sk.c b/lustre/utils/gss/lgss_sk.c index afc1181..c43722c 100644 --- a/lustre/utils/gss/lgss_sk.c +++ b/lustre/utils/gss/lgss_sk.c @@ -51,10 +51,14 @@ /* One week default expiration */ #define SK_DEFAULT_EXPIRE 604800 +/* But only one day in FIPS mode */ +#define SK_DEFAULT_EXPIRE_FIPS 86400 #define SK_DEFAULT_SK_KEYLEN 256 #define SK_DEFAULT_PRIME_BITS 2048 #define SK_DEFAULT_NODEMAP "default" +static int fips_mode; + static void usage(FILE *fp, char *program) { int i; @@ -275,13 +279,76 @@ static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config) return rc; } +#if !defined(HAVE_OPENSSL_EVP_PKEY) && OPENSSL_VERSION_NUMBER >= 0x10100000L +static inline int __fetch_ssk_prime(struct sk_keyfile_config *config) +{ + const BIGNUM *p; + DH *dh = NULL; + int primenid; + int rc = -1; + + primenid = sk_primebits2primenid(config->skc_prime_bits); + dh = DH_new_by_nid(primenid); + if (!dh) { + fprintf(stderr, "error: dh cannot be init\n"); + goto prime_end; + } + + p = DH_get0_p(dh); + if (!p) { + fprintf(stderr, "error: cannot get p from dh\n"); + goto prime_end; + } + + if (BN_num_bytes(p) > SK_MAX_P_BYTES) { + fprintf(stderr, + "error: requested length %d exceeds maximum %d\n", + BN_num_bytes(p), SK_MAX_P_BYTES * 8); + goto prime_end; + } + + if (BN_bn2bin(p, config->skc_p) != BN_num_bytes(p)) { + fprintf(stderr, "error: convert BIGNUM p to binary failed\n"); + goto prime_end; + } + + rc = 0; + +prime_end: + if (rc) + fprintf(stderr, + "error: fetching SSK prime failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return rc; +} +#endif + static inline int __gen_ssk_prime(struct sk_keyfile_config *config) { int rc = -1; + const char *primename; EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *dh = NULL; BIGNUM *p; + if (fips_mode) { + primename = sk_primebits2name(config->skc_prime_bits); + if (!primename) { + fprintf(stderr, + "error: prime len %d not supported in FIPS mode\n", + config->skc_prime_bits); + return rc; + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + fprintf(stdout, + "FIPS mode, using well-known prime %s\n", primename); +#ifndef HAVE_OPENSSL_EVP_PKEY + return __fetch_ssk_prime(config); +#endif +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + } + ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL); if (!ctx || EVP_PKEY_paramgen_init(ctx) != 1) { fprintf(stderr, "error: ctx cannot be init\n"); @@ -320,6 +387,10 @@ static inline int __gen_ssk_prime(struct sk_keyfile_config *config) rc = 0; prime_end: + if (rc) + fprintf(stderr, + "error: generating SSK prime failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); EVP_PKEY_free(dh); EVP_PKEY_CTX_free(ctx); return rc; @@ -489,6 +560,8 @@ int main(int argc, char **argv) /* init gss logger for foreground (no syslog) which prints to stderr */ initerr(NULL, verbose, 1); + fips_mode = FIPS_mode(); + if (input) return print_config(input); @@ -548,7 +621,8 @@ int main(int argc, char **argv) /* Set the defaults for new key */ config->skc_version = SK_CONF_VERSION; - config->skc_expire = SK_DEFAULT_EXPIRE; + config->skc_expire = fips_mode ? + SK_DEFAULT_EXPIRE_FIPS : SK_DEFAULT_EXPIRE; config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN; config->skc_prime_bits = SK_DEFAULT_PRIME_BITS; config->skc_crypt_alg = SK_CRYPT_AES256_CTR; @@ -576,6 +650,10 @@ int main(int argc, char **argv) config->skc_hmac_alg = hmac; if (expire != -1) config->skc_expire = expire; + if (fips_mode && config->skc_expire > SK_DEFAULT_EXPIRE_FIPS) + fprintf(stderr, + "warning: using a %us key expiration greater than %us is not recommended in FIPS mode\n", + config->skc_expire, SK_DEFAULT_EXPIRE_FIPS); if (shared_keylen != -1) config->skc_shared_keylen = shared_keylen; if (prime_bits != -1) { diff --git a/lustre/utils/gss/sk_utils.h b/lustre/utils/gss/sk_utils.h index 4de88e9..e54d6c4 100644 --- a/lustre/utils/gss/sk_utils.h +++ b/lustre/utils/gss/sk_utils.h @@ -36,8 +36,12 @@ #include #include #include +#include #include #include +#ifdef HAVE_OPENSSL_FIPS +#include +#endif #ifdef HAVE_OPENSSL_EVP_PKEY #include #endif @@ -133,6 +137,10 @@ static inline const BIGNUM *DH_get0_p(const DH *dh) } #endif +#ifndef HAVE_OPENSSL_FIPS +#define FIPS_mode() 0 +#endif + /* Some limits and defaults */ #define SK_CONF_VERSION 1 #define SK_MSG_VERSION 1 @@ -369,6 +377,71 @@ static inline const char *sk_hmac2name(enum sk_hmac_alg type) return NULL; } +#ifndef NID_ffdhe2048 +#define NID_ffdhe2048 1126 +#define NID_ffdhe3072 1127 +#define NID_ffdhe4096 1128 +#define NID_ffdhe6144 1129 +#define NID_ffdhe8192 1130 +#endif + +static const struct sk_prime_type sk_prime_nids[] = { + { + .spt_name = "null", + .spt_type = 0, + .spt_primebits = 0 + }, + { + .spt_name = "ffdhe2048", + .spt_type = NID_ffdhe2048, + .spt_primebits = 2048 + }, + { + .spt_name = "ffdhe3072", + .spt_type = NID_ffdhe3072, + .spt_primebits = 3072 + }, + { + .spt_name = "ffdhe4096", + .spt_type = NID_ffdhe4096, + .spt_primebits = 4096 + }, + { + .spt_name = "ffdhe6144", + .spt_type = NID_ffdhe6144, + .spt_primebits = 6144 + }, + { + .spt_name = "ffdhe8192", + .spt_type = NID_ffdhe8192, + .spt_primebits = 8192 + }, +}; + +static inline int sk_primebits2primenid(int primebits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sk_prime_nids); i++) { + if (primebits == sk_prime_nids[i].spt_primebits) + return sk_prime_nids[i].spt_type; + } + + return -1; +} + +static inline const char *sk_primebits2name(int primebits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sk_prime_nids); i++) { + if (primebits == sk_prime_nids[i].spt_primebits) + return sk_prime_nids[i].spt_name; + } + + return NULL; +} + void sk_init_logging(char *program, int verbose, int fg); struct sk_keyfile_config *sk_read_file(char *filename); int sk_load_keyfile(char *path); -- 1.8.3.1