Whamcloud - gitweb
LU-17624 ssk: support FIPS mode on client 14/54314/8
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 6 Mar 2024 15:33:25 +0000 (15:33 +0000)
committerOleg Drokin <green@whamcloud.com>
Mon, 15 Apr 2024 16:52:41 +0000 (16:52 +0000)
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 <sbuisson@ddn.com>
Change-Id: I52b1926393e51fba6a9e92a837f86a38516ef6ad
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54314
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
lustre/autoconf/lustre-core.m4
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/utils/gss/lgss_sk.c
lustre/utils/gss/sk_utils.h

index f139924..6b6c0fb 100644 (file)
@@ -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 <openssl/dh.h>
+       #include <openssl/dsa.h>
+       #include <openssl/evp.h>
+       #include <openssl/hmac.h>
+       #include <openssl/fips.h>
+
+       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], [
index 376c622..682a5bf 100644 (file)
@@ -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,
index afc1181..c43722c 100644 (file)
 
 /* 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) {
index 4de88e9..e54d6c4 100644 (file)
 #include <linux/lustre/lustre_idl.h>
 #include <linux/lustre/lustre_disk.h>
 #include <openssl/dh.h>
+#include <openssl/dsa.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#ifdef HAVE_OPENSSL_FIPS
+#include <openssl/fips.h>
+#endif
 #ifdef HAVE_OPENSSL_EVP_PKEY
 #include <openssl/core_names.h>
 #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);