Whamcloud - gitweb
LU-15896 gss: support OpenSSLv3 84/48184/2
authorSebastien Buisson <sbuisson@ddn.com>
Mon, 13 Jun 2022 12:41:11 +0000 (14:41 +0200)
committerOleg Drokin <green@whamcloud.com>
Sat, 20 Aug 2022 16:14:29 +0000 (16:14 +0000)
Lustre GSS code makes use of some OpenSSL API that has been
deprecated in v3, namely all the functions in the DH_* family.
So replace them with their EVP_PKEY_* counterparts if Lustre is
built on a system with OpenSSLv3.

Lustre-change: https://review.whamcloud.com/47717
Lustre-commit: 615691a531a80b75c4dd054dbb86d0bdbf4cf808

Fixes: ee60c14360 ("LU-15896 gss: ignore OpenSSLv3 deprecated API")
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I78a4ca18b25aca3c34fe84e41413a33caddc01b6
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-on: https://review.whamcloud.com/48184
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/autoconf/lustre-core.m4
lustre/utils/gss/lgss_sk.c
lustre/utils/gss/lgss_sk_utils.c
lustre/utils/gss/sk_utils.c
lustre/utils/gss/sk_utils.h
lustre/utils/gss/svcgssd_main_loop.c
lustre/utils/gss/svcgssd_proc.c

index 9dee2dd..76112bb 100644 (file)
@@ -69,7 +69,9 @@ AC_CHECK_FUNCS([copy_file_range],
 # LC_FID2PATH_UNION
 #
 AC_DEFUN([LC_FID2PATH_ANON_UNION], [
-AC_MSG_CHECKING([if 'struct getinfo_fid2path' has anony•mous union])
+saved_flags="$CFLAGS"
+CFLAGS="$CFLAGS -Werror"
+AC_MSG_CHECKING([if 'struct getinfo_fid2path' has anonymous union])
 AC_COMPILE_IFELSE([AC_LANG_SOURCE([
        #include <linux/lustre/lustre_idl.h>
 
@@ -82,14 +84,19 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
        }
 ])],[
        AC_DEFINE(HAVE_FID2PATH_ANON_UNIONS, 1, [union is unnamed])
-       AC_MSG_RESULT("yes")
+       AC_MSG_RESULT([yes])
+],[
+       AC_MSG_RESULT([no])
 ])
+CFLAGS="$saved_flags"
 ]) # LC_FID2PATH_ANON_UNION
 
 #
 # LC_IOC_REMOVE_ENTRY
 #
 AC_DEFUN([LC_IOC_REMOVE_ENTRY], [
+saved_flags="$CFLAGS"
+CFLAGS="$CFLAGS -Werror"
 AC_MSG_CHECKING([if ioctl IOC_REMOVE_ENTRY' is supported])
 AC_COMPILE_IFELSE([AC_LANG_SOURCE([
        #include <sys/ioctl.h>
@@ -101,7 +108,11 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
 ])],[
        AC_DEFINE(HAVE_IOC_REMOVE_ENTRY, 1,
                [IOC_REMOVE_ENTRY ioctl exists])
+       AC_MSG_RESULT([yes])
+],[
+       AC_MSG_RESULT([no])
 ])
+CFLAGS="$saved_flags"
 ]) # LC_IOC_REMOVE_ENTRY
 
 #
@@ -393,11 +404,14 @@ AS_IF([test "x$enable_gss" != xno], [
 ])
 ]) # LC_CONFIG_GSS
 
-# LC_OPENSSL_SSK
+# LC_OPENSSL_HMAC
 #
 # OpenSSL 1.0+ return int for HMAC functions but older SLES11 versions do not
-AC_DEFUN([LC_OPENSSL_SSK], [
-AC_MSG_CHECKING([whether OpenSSL has functions needed for SSK])
+AC_DEFUN([LC_OPENSSL_HMAC], [
+has_hmac_functions="no"
+saved_flags="$CFLAGS"
+CFLAGS="$CFLAGS -Werror"
+AC_MSG_CHECKING([whether OpenSSL has HMAC_Init_ex])
 AS_IF([test "x$enable_ssk" != xno], [
 AC_COMPILE_IFELSE([AC_LANG_SOURCE([
        #include <openssl/hmac.h>
@@ -407,9 +421,57 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
                int rc;
                rc = HMAC_Init_ex(NULL, "test", 4, EVP_md_null(), NULL);
        }
-])],[AC_DEFINE(HAVE_OPENSSL_SSK, 1,
-              [OpenSSL HMAC functions needed for SSK])],
-       [enable_ssk="no"])
+])],[
+       has_hmac_functions="yes"
+])
+])
+AC_MSG_RESULT([$has_hmac_functions])
+CFLAGS="$saved_flags"
+]) # LC_OPENSSL_HMAC
+
+# LC_OPENSSL_EVP_PKEY
+#
+# OpenSSL 3.0 introduces EVP_PKEY_get_params
+AC_DEFUN([LC_OPENSSL_EVP_PKEY], [
+has_evp_pkey="no"
+saved_flags="$CFLAGS"
+CFLAGS="$CFLAGS -Werror"
+AC_MSG_CHECKING([whether OpenSSL has EVP_PKEY_get_params])
+AS_IF([test "x$enable_ssk" != xno], [
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+       #include <openssl/evp.h>
+
+       int main(void) {
+               OSSL_PARAM *params;
+
+               int rc = EVP_PKEY_get_params(NULL, params);
+       }
+])],[
+       AC_DEFINE(HAVE_OPENSSL_EVP_PKEY, 1, [OpenSSL EVP_PKEY_get_params])
+       has_evp_pkey="yes"
+])
+])
+CFLAGS="$saved_flags"
+AC_MSG_RESULT([$has_evp_pkey])
+]) # LC_OPENSSL_EVP_PKEY
+
+#
+# LC_OPENSSL_SSK
+#
+# Check whether to enable Lustre client crypto
+#
+AC_DEFUN([LC_OPENSSL_SSK], [
+AC_MSG_CHECKING([whether OpenSSL has functions needed for SSK])
+AS_IF([test "x$enable_ssk" != xno], [
+       AC_MSG_RESULT(
+       )
+       LC_OPENSSL_HMAC
+       LC_OPENSSL_EVP_PKEY
+])
+AS_IF([test "x$has_hmac_functions" = xyes -o "x$has_evp_pkey" = xyes], [
+       AC_DEFINE(HAVE_OPENSSL_SSK, 1, [OpenSSL HMAC functions needed for SSK])
+], [
+       enable_ssk="no"
 ])
 AC_MSG_RESULT([$enable_ssk])
 ]) # LC_OPENSSL_SSK
@@ -418,6 +480,8 @@ AC_MSG_RESULT([$enable_ssk])
 #
 # OpenSSL is needed for l_getsepol
 AC_DEFUN([LC_OPENSSL_GETSEPOL], [
+saved_flags="$CFLAGS"
+CFLAGS="$CFLAGS -Werror"
 AC_MSG_CHECKING([whether openssl-devel is present])
 AC_COMPILE_IFELSE([AC_LANG_SOURCE([
        #include <openssl/evp.h>
@@ -437,6 +501,7 @@ No openssl-devel headers found, unable to build l_getsepol and SELinux status ch
 ])
 ])
 AC_MSG_RESULT([$enable_getsepol])
+CFLAGS="$saved_flags"
 ]) # LC_OPENSSL_GETSEPOL
 
 # LC_HAVE_LIBAIO
index f5782cb..afc1181 100644 (file)
@@ -275,6 +275,56 @@ static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
        return rc;
 }
 
+static inline int __gen_ssk_prime(struct sk_keyfile_config *config)
+{
+       int rc = -1;
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *dh = NULL;
+       BIGNUM *p;
+
+       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");
+               goto prime_end;
+       }
+
+       if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx,
+                                                config->skc_prime_bits) <= 0 ||
+           EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, SK_GENERATOR) <= 0) {
+               fprintf(stderr, "error: cannot set prime or generator\n");
+               goto prime_end;
+       }
+
+       if (EVP_PKEY_paramgen(ctx, &dh) != 1) {
+               fprintf(stderr, "error: cannot generate DH parameters\n");
+               goto prime_end;
+       }
+
+       if (!EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_P, &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: cannot generate DH parameters: requested length %d exceeds maximum %d\n",
+                       config->skc_prime_bits, 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:
+       EVP_PKEY_free(dh);
+       EVP_PKEY_CTX_free(ctx);
+       return rc;
+}
+
 int main(int argc, char **argv)
 {
        struct sk_keyfile_config *config;
@@ -298,7 +348,6 @@ int main(int argc, char **argv)
        int opt;
        enum sk_key_type type = SK_TYPE_INVALID;
        bool generate_prime = false;
-       DH *dh = NULL;
 
        static struct option long_opts[] = {
        { .name = "crypt",      .has_arg = required_argument, .val = 'c'},
@@ -529,8 +578,17 @@ int main(int argc, char **argv)
                config->skc_expire = expire;
        if (shared_keylen != -1)
                config->skc_shared_keylen = shared_keylen;
-       if (prime_bits != -1)
+       if (prime_bits != -1) {
+#ifdef HAVE_OPENSSL_EVP_PKEY
+               /* #define DH_MIN_MODULUS_BITS 512, not exported by OpenSSL */
+               if (prime_bits < 512) {
+                       fprintf(stderr,
+                               "error: prime length must be at least 512\n");
+                       return EXIT_FAILURE;
+               }
+#endif
                config->skc_prime_bits = prime_bits;
+       }
        if (fsname)
                /* fsname string length was checked when parsing
                 * command-line options
@@ -556,39 +614,9 @@ int main(int argc, char **argv)
        }
 
        if (generate_prime) {
-               const BIGNUM *p;
-               int rc;
-
                printf("Generating DH parameters, this can take a while...\n");
-               dh = DH_new();
-               if (!dh) {
-                       fprintf(stderr, "error: dh cannot be allocated\n");
-                       goto error;
-               }
-
-               rc = DH_generate_parameters_ex(dh, config->skc_prime_bits,
-                                              SK_GENERATOR, NULL);
-               if (rc != 1) {
-                       fprintf(stderr, "error generating DH parameters\n");
+               if (__gen_ssk_prime(config))
                        goto error;
-               }
-
-               DH_get0_pqg(dh, &p, NULL, NULL);
-
-               if (BN_num_bytes(p) > SK_MAX_P_BYTES) {
-                       fprintf(stderr, "error: cannot generate DH parameters: "
-                               "requested length %d exceeds maximum %d\n",
-                               config->skc_prime_bits, SK_MAX_P_BYTES * 8);
-                       goto error;
-               }
-               if (BN_bn2bin(p, config->skc_p) != BN_num_bytes(p)) {
-                       fprintf(stderr,
-                               "error: convert BIGNUM p to binary failed\n");
-                       goto error;
-               }
-
-               DH_free(dh);
-               dh = NULL;
        }
 
        if (write_config_file(modify ?: output, config, modify))
@@ -597,7 +625,6 @@ int main(int argc, char **argv)
        return EXIT_SUCCESS;
 
 error:
-       DH_free(dh);
        free(config);
        return EXIT_FAILURE;
 }
index 5974c33..4ee8a48 100644 (file)
@@ -27,8 +27,6 @@
 
 #include <limits.h>
 #include <string.h>
-/* We need to use some deprecated APIs */
-#define OPENSSL_SUPPRESS_DEPRECATED
 #include <openssl/dh.h>
 #include <openssl/engine.h>
 #include <openssl/err.h>
@@ -183,6 +181,14 @@ static int lgss_sk_validate_cred(struct lgss_cred *cred, gss_buffer_desc *token,
                 * because there is a chance that the parameters generated
                 * resulted in a key that is 1 byte short */
                printerr(0, "Short key computed, must retry\n");
+               if (skc->sc_dh_shared_key.value) {
+                       /* erase secret key before freeing memory */
+                       memset(skc->sc_dh_shared_key.value, 0,
+                              skc->sc_dh_shared_key.length);
+                       free(skc->sc_dh_shared_key.value);
+                       skc->sc_dh_shared_key.value = NULL;
+               }
+               skc->sc_dh_shared_key.length = 0;
                return -EAGAIN;
        } else if (rc != GSS_S_COMPLETE) {
                printerr(0, "Failed to compute session key: 0x%x\n", rc);
@@ -192,7 +198,7 @@ static int lgss_sk_validate_cred(struct lgss_cred *cred, gss_buffer_desc *token,
        rc = sk_session_kdf(skc, cred->lc_self_nid, &cred->lc_mech_token,
                            token);
        if (rc) {
-               printerr(0, "Failed to calulate derived key\n");
+               printerr(0, "Failed to calculate derived key\n");
                return -EINVAL;
        }
 
index 3061b22..e627199 100755 (executable)
 #include <string.h>
 #include <stdbool.h>
 #include <unistd.h>
-/* We need to use some deprecated APIs */
-#define OPENSSL_SUPPRESS_DEPRECATED
 #include <openssl/dh.h>
 #include <openssl/engine.h>
 #include <openssl/err.h>
 #include <openssl/hmac.h>
+#ifdef HAVE_OPENSSL_EVP_PKEY
+#include <openssl/param_build.h>
+#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <libcfs/util/string.h>
@@ -693,36 +694,39 @@ out_err:
  * of the prime provided as input parameter to DH_check(). This makes the check
  * roughly x10 longer, and causes request timeouts when an SSK flavor is being
  * used.
- *
  * Instead, use a dynamic number Miller-Rabin rounds based on the speed of the
  * check on the current system, evaluated when the lsvcgssd daemon starts, but
  * at least as many as OpenSSL 1.1.1b used for the same key size. If default
  * DH_check() duration is OK, use it directly instead of limiting the rounds.
- *
  * If \a num_rounds == 0, we just call original DH_check() directly.
+ *
+ * OpenSSL v3 internally forces a minimum of 64 rounds when checking prime, so
+ * it is no longer possible to test prime check speed with fewer rounds. In this
+ * case, do not bother and directly call EVP_PKEY_param_check.
  */
-static bool sk_is_dh_valid(const DH *dh, int num_rounds)
+#ifdef HAVE_OPENSSL_EVP_PKEY
+static bool sk_is_dh_valid(EVP_PKEY_CTX *ctx)
+{
+       if (EVP_PKEY_param_check(ctx) != 1) {
+               printerr(0, "EVP_PKEY_param_check failed\n");
+               ERR_print_errors_fp(stderr);
+               return false;
+       }
+       return true;
+}
+#else
+static inline bool sk_check_dh(const DH *dh, int num_rounds, bool fullcheck)
 {
-       const BIGNUM *p, *g;
+       const BIGNUM *p = NULL, *g = NULL;
        BN_ULONG word;
        BN_CTX *ctx;
        BIGNUM *r;
        bool valid = false;
        int rc;
 
-       if (num_rounds == 0) {
-               int codes = 0;
-
-               rc = DH_check(dh, &codes);
-               if (rc != 1 || codes) {
-                       printerr(0, "DH_check(0) failed: codes=%#x: rc=%d\n",
-                                codes, rc);
-                       return false;
-               }
-               return true;
-       }
-
        DH_get0_pqg(dh, &p, NULL, &g);
+       if (!p || !g)
+               return false;
 
        if (!BN_is_word(g, SK_GENERATOR)) {
                printerr(0, "%s: Diffie-Hellman generator is not %u\n",
@@ -731,12 +735,20 @@ static bool sk_is_dh_valid(const DH *dh, int num_rounds)
        }
 
        word = BN_mod_word(p, 24);
-       if (word != 11) {
+       /* OpenSSL v3 changed the way the prime is generated,
+        * using p mod 24 == 23.
+        * So we must accept word == 23 if the prime was generated
+        * by a client with OpenSSL v3.
+        */
+       if ((word != 11) && (word != 23)) {
                printerr(0, "%s: Diffie-Hellman prime modulo=%lu unsuitable\n",
                         program_invocation_short_name, word);
                return false;
        }
 
+       if (!fullcheck)
+               return true;
+
        ctx = BN_CTX_new();
        if (ctx == NULL) {
                printerr(0, "%s: Diffie-Hellman error allocating context\n",
@@ -774,6 +786,30 @@ out_free:
        return valid;
 }
 
+static bool sk_is_dh_valid(const DH *dh, int num_rounds)
+{
+       int rc;
+
+       if (num_rounds == 0) {
+               int codes = 0;
+
+               rc = DH_check(dh, &codes);
+               if (codes == DH_NOT_SUITABLE_GENERATOR &&
+                   sk_check_dh(dh, num_rounds, false))
+                       return true;
+               if (rc != 1 || codes) {
+                       printerr(0, "DH_check(0) failed: codes=%#x: rc=%d\n",
+                                codes, rc);
+                       return false;
+               }
+               return true;
+       }
+
+       return sk_check_dh(dh, num_rounds, true);
+}
+#endif
+
+#ifndef HAVE_OPENSSL_EVP_PKEY
 #define VALUE_LENGTH 256
 static unsigned char test_prime[VALUE_LENGTH] =
        "\xf7\xfa\x49\xd8\xec\xb1\x3b\xff\x26\x10\x3f\xc5\x3a\xc5\xcc\x40"
@@ -876,6 +912,120 @@ free_dh:
 
        return prev_rounds;
 }
+#endif /* !HAVE_OPENSSL_EVP_PKEY */
+
+#ifdef HAVE_OPENSSL_EVP_PKEY
+static uint32_t __sk_gen_params(struct sk_cred *skc, BIGNUM *p, BIGNUM *g,
+                               int num_rounds)
+{
+       EVP_PKEY_CTX *ctx = NULL, *ctx_from_key = NULL;
+       OSSL_PARAM_BLD *tmpl = NULL;
+       OSSL_PARAM *params = NULL;
+       EVP_PKEY *key = NULL;
+       uint32_t rc = GSS_S_FAILURE;
+
+       tmpl = OSSL_PARAM_BLD_new();
+       if (!tmpl ||
+           !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_P, p) ||
+           !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_G, g)) {
+               printerr(0, "error: params cannot be pushed\n");
+               goto err;
+       }
+       params = OSSL_PARAM_BLD_to_param(tmpl);
+       if (!params) {
+               printerr(0, "error: params cannot be allocated\n");
+               goto err;
+       }
+
+       ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+       if (!ctx ||
+           EVP_PKEY_fromdata_init(ctx) != 1 ||
+           EVP_PKEY_fromdata(ctx, &key,
+                             EVP_PKEY_KEY_PARAMETERS, params) != 1) {
+               printerr(0, "error: params cannot be set\n");
+               goto err;
+       }
+
+       ctx_from_key = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL);
+       if (!ctx_from_key) {
+               printerr(0, "error: ctx_from_key cannot be allocated\n");
+               goto err;
+       }
+
+       /* Verify that we have a safe prime and valid generator */
+       if (!sk_is_dh_valid(ctx_from_key))
+               goto err;
+
+       skc->sc_params = NULL;
+       if (EVP_PKEY_keygen_init(ctx_from_key) != 1 ||
+           EVP_PKEY_keygen(ctx_from_key, &skc->sc_params) != 1) {
+               printerr(0, "Failed to generate public DH key: %s\n",
+                        ERR_error_string(ERR_get_error(), NULL));
+               goto err;
+       }
+
+       /* skc->sc_pub_key.value is allocated by
+        * EVP_PKEY_get1_encoded_public_key
+        */
+       skc->sc_pub_key.length =
+         EVP_PKEY_get1_encoded_public_key(skc->sc_params,
+                                     (unsigned char **)&skc->sc_pub_key.value);
+       if (skc->sc_pub_key.length == 0) {
+               printerr(0, "error: cannot get pub key\n");
+               skc->sc_pub_key.value = NULL;
+               goto err;
+       }
+       rc = GSS_S_COMPLETE;
+
+err:
+       EVP_PKEY_CTX_free(ctx_from_key);
+       EVP_PKEY_free(key);
+       EVP_PKEY_CTX_free(ctx);
+       OSSL_PARAM_free(params);
+       OSSL_PARAM_BLD_free(tmpl);
+       BN_free(g);
+       BN_free(p);
+       return rc;
+}
+#else /* !HAVE_OPENSSL_EVP_PKEY */
+static uint32_t __sk_gen_params(struct sk_cred *skc, BIGNUM *p, BIGNUM *g,
+                               int num_rounds)
+{
+       const BIGNUM *pub_key;
+
+       /* Populate DH parameters */
+       /* "dh" takes over freeing of 'p' and 'g' if this succeeds */
+       skc->sc_params = DH_new();
+       if (!skc->sc_params || !DH_set0_pqg(skc->sc_params, p, NULL, g)) {
+               printerr(0, "Failed to set pqg\n");
+               BN_free(g);
+               BN_free(p);
+               return GSS_S_FAILURE;
+       }
+
+       /* Verify that we have a safe prime and valid generator */
+       if (!sk_is_dh_valid(skc->sc_params, num_rounds))
+               return GSS_S_FAILURE;
+
+       if (DH_generate_key(skc->sc_params) != 1) {
+               printerr(0, "Failed to generate public DH key: %s\n",
+                        ERR_error_string(ERR_get_error(), NULL));
+               return GSS_S_FAILURE;
+       }
+
+       DH_get0_key(skc->sc_params, &pub_key, NULL);
+       skc->sc_pub_key.length = BN_num_bytes(pub_key);
+       skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
+       if (!skc->sc_pub_key.value) {
+               printerr(0, "Failed to allocate memory for public key\n");
+               return GSS_S_FAILURE;
+       }
+
+       BN_bn2bin(pub_key, skc->sc_pub_key.value);
+
+       return GSS_S_COMPLETE;
+}
+#endif /* HAVE_OPENSSL_EVP_PKEY */
 
 /**
  * Populates the DH parameters for the DHKE
@@ -890,14 +1040,14 @@ uint32_t sk_gen_params(struct sk_cred *skc, int num_rounds)
 {
        uint32_t random;
        BIGNUM *p, *g;
-       const BIGNUM *pub_key;
 
        /* Random value used by both the request and response as part of the
         * key binding material.  This also should ensure we have unqiue
         * tokens that are sent to the remote server which is important because
         * the token is hashed for the sunrpc cache lookups and a failure there
         * would cause connection attempts to fail indefinitely due to the large
-        * timeout value on the server side */
+        * timeout value on the server side.
+        */
        if (RAND_bytes((unsigned char *)&random, sizeof(random)) != 1) {
                printerr(0, "Failed to get data for random parameter: %s\n",
                         ERR_error_string(ERR_get_error(), NULL));
@@ -905,16 +1055,10 @@ uint32_t sk_gen_params(struct sk_cred *skc, int num_rounds)
        }
 
        /* The random value will always be used in byte range operations
-        * so we keep it as big endian from this point on */
+        * so we keep it as big endian from this point on.
+        */
        skc->sc_kctx.skc_host_random = random;
 
-       /* Populate DH parameters */
-       skc->sc_params = DH_new();
-       if (!skc->sc_params) {
-               printerr(0, "Failed to allocate DH\n");
-               return GSS_S_FAILURE;
-       }
-
        p = BN_bin2bn(skc->sc_p.value, skc->sc_p.length, NULL);
        if (!p) {
                printerr(0, "Failed to convert binary to BIGNUM\n");
@@ -925,39 +1069,21 @@ uint32_t sk_gen_params(struct sk_cred *skc, int num_rounds)
        g = BN_new();
        if (!g) {
                printerr(0, "Failed to allocate new BIGNUM\n");
-               return GSS_S_FAILURE;
+               goto free_p;
        }
        if (BN_set_word(g, SK_GENERATOR) != 1) {
                printerr(0, "Failed to set g value for DH params\n");
-               return GSS_S_FAILURE;
-       }
-
-       if (!DH_set0_pqg(skc->sc_params, p, NULL, g)) {
-               printerr(0, "Failed to set pqg\n");
-               return GSS_S_FAILURE;
-       }
-
-       /* Verify that we have a safe prime and valid generator */
-       if (!sk_is_dh_valid(skc->sc_params, num_rounds))
-               return GSS_S_FAILURE;
-
-       if (DH_generate_key(skc->sc_params) != 1) {
-               printerr(0, "Failed to generate public DH key: %s\n",
-                        ERR_error_string(ERR_get_error(), NULL));
-               return GSS_S_FAILURE;
+               goto free_g;
        }
 
-       DH_get0_key(skc->sc_params, &pub_key, NULL);
-       skc->sc_pub_key.length = BN_num_bytes(pub_key);
-       skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
-       if (!skc->sc_pub_key.value) {
-               printerr(0, "Failed to allocate memory for public key\n");
-               return GSS_S_FAILURE;
-       }
+       return __sk_gen_params(skc, p, g, num_rounds);
 
-       BN_bn2bin(pub_key, skc->sc_pub_key.value);
+free_g:
+       BN_free(g);
+free_p:
+       BN_free(p);
 
-       return GSS_S_COMPLETE;
+       return GSS_S_FAILURE;
 }
 
 /**
@@ -997,18 +1123,18 @@ static inline const EVP_MD *sk_hash_to_evp_md(enum cfs_crypto_hash_alg alg)
 int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
                 const EVP_MD *hash_alg, gss_buffer_desc *hmac)
 {
-       HMAC_CTX *hctx;
        unsigned int hashlen = EVP_MD_size(hash_alg);
-       int i;
-       int rc = -1;
+       EVP_MAC_CTX *ctx = NULL;
+       EVP_MAC *mac = NULL;
+       size_t len = 0;
+       int i, rc = -1;
+       DECLARE_EVP_MD(subalg, hash_alg);
 
        if (hash_alg == EVP_md_null()) {
                printerr(0, "Invalid hash algorithm\n");
                return -1;
        }
 
-       hctx = HMAC_CTX_new();
-
        hmac->length = hashlen;
        hmac->value = malloc(hashlen);
        if (!hmac->value) {
@@ -1016,32 +1142,45 @@ int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
                goto out;
        }
 
-       if (HMAC_Init_ex(hctx, key->value, key->length, hash_alg, NULL) != 1) {
+       mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+       if (!mac) {
+               printerr(0, "Failed to fetch HMAC\n");
+               goto out;
+       }
+
+       ctx = EVP_MAC_CTX_new(mac);
+       if (!ctx) {
+               printerr(0, "Failed to init HMAC ctx\n");
+               goto out;
+       }
+
+       if (EVP_MAC_init(ctx, key->value, key->length, subalg) != 1) {
                printerr(0, "Failed to init HMAC\n");
                goto out;
        }
 
        for (i = 0; i < numbufs; i++) {
-               if (HMAC_Update(hctx, bufs[i].value, bufs[i].length) != 1) {
+               if (EVP_MAC_update(ctx, bufs[i].value, bufs[i].length) != 1) {
                        printerr(0, "Failed to update HMAC\n");
                        goto out;
                }
        }
 
        /* The result gets populated in hmac */
-       if (HMAC_Final(hctx, hmac->value, &hashlen) != 1) {
+       if (EVP_MAC_final(ctx, hmac->value, &len, hashlen) != 1) {
                printerr(0, "Failed to finalize HMAC\n");
                goto out;
        }
-
-       if (hmac->length != hashlen) {
-               printerr(0, "HMAC size does not match expected\n");
+       if (hmac->length != len) {
+               printerr(0, "HMAC size %zu does not match expected %zu\n",
+                        len, hmac->length);
                goto out;
        }
 
        rc = 0;
 out:
-       HMAC_CTX_free(hctx);
+       EVP_MAC_CTX_free(ctx);
+       EVP_MAC_free(mac);
        return rc;
 }
 
@@ -1136,8 +1275,10 @@ void sk_free_cred(struct sk_cred *skc)
                free(skc->sc_kctx.skc_session_key.value);
        }
 
-       if (skc->sc_params)
-               DH_free(skc->sc_params);
+       if (skc->sc_params) {
+               EVP_PKEY_free(skc->sc_params);
+               skc->sc_params = NULL;
+       }
 
        free(skc);
        skc = NULL;
@@ -1317,23 +1458,66 @@ int sk_compute_keys(struct sk_cred *skc)
        return 0;
 }
 
-/**
- * Computes a session key based on the DH parameters from the host and its peer
- *
- * \param[in,out]      skc             Shared key credentials structure with
- *                                     the session key populated with the
- *                                     compute key
- * \param[in]          pub_key         Public key returned from peer in
- *                                     gss_buffer_desc
- * \return     gss error               failure
- * \return     GSS_S_COMPLETE          success
- */
-uint32_t sk_compute_dh_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
+uint32_t __sk_compute_dh_key(struct sk_cred *skc,
+                            const gss_buffer_desc *pub_key,
+                            size_t *expected_len)
 {
        gss_buffer_desc *dh_shared = &skc->sc_dh_shared_key;
-       BIGNUM *remote_pub_key;
-       int status;
        uint32_t rc = GSS_S_FAILURE;
+#ifdef HAVE_OPENSSL_EVP_PKEY
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *peerkey = NULL;
+
+       peerkey = EVP_PKEY_new();
+       if (!peerkey ||
+           EVP_PKEY_copy_parameters(peerkey, skc->sc_params) != 1) {
+               printerr(0, "error: peerkey cannot be init\n");
+               goto out_err;
+       }
+
+       if (EVP_PKEY_set1_encoded_public_key(peerkey,
+                                            pub_key->value,
+                                            pub_key->length) != 1) {
+               printerr(0, "error: peerkey cannot be set\n");
+               goto out_err;
+       }
+
+       ctx = EVP_PKEY_CTX_new_from_pkey(NULL, skc->sc_params, NULL);
+       if (!ctx) {
+               printerr(0, "error: ctx cannot be allocated\n");
+               goto out_err;
+       }
+
+       if (EVP_PKEY_derive_init(ctx) != 1 ||
+           EVP_PKEY_derive_set_peer(ctx, peerkey) != 1) {
+               printerr(0, "error: ctx cannot be init\n");
+               goto out_err;
+       }
+
+       if (EVP_PKEY_derive(ctx, NULL, expected_len) != 1) {
+               printerr(0, "error: cannot get dh length\n");
+               goto out_err;
+       }
+
+       dh_shared->length = *expected_len;
+       dh_shared->value = malloc(*expected_len);
+       if (!dh_shared->value) {
+               printerr(0, "error: cannot allocate memory for shared key\n");
+               goto out_err;
+       }
+
+       if (EVP_PKEY_derive(ctx, dh_shared->value, &dh_shared->length) != 1) {
+               printerr(0, "error: cannot derive dh key\n");
+               ERR_print_errors_fp(stderr);
+               goto out_err;
+       }
+
+       rc = GSS_S_COMPLETE;
+out_err:
+       EVP_PKEY_CTX_free(ctx);
+       EVP_PKEY_free(peerkey);
+#else /* !HAVE_OPENSSL_EVP_PKEY */
+       BIGNUM *remote_pub_key;
 
        remote_pub_key = BN_bin2bn(pub_key->value, pub_key->length, NULL);
        if (!remote_pub_key) {
@@ -1341,48 +1525,75 @@ uint32_t sk_compute_dh_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
                return rc;
        }
 
-       dh_shared->length = DH_size(skc->sc_params);
-       dh_shared->value = malloc(dh_shared->length);
+       *expected_len = DH_size(skc->sc_params);
+       dh_shared->length = *expected_len;
+       dh_shared->value = malloc(*expected_len);
        if (!dh_shared->value) {
-               printerr(0, "Failed to allocate memory for computed shared "
-                        "secret key\n");
+               printerr(0,
+                        "Failed to allocate memory for computed shared secret key\n");
                goto out_err;
        }
 
-       /* This compute the shared key from the DHKE */
-       status = DH_compute_key(dh_shared->value, remote_pub_key,
-                               skc->sc_params);
-       if (status == -1) {
-               printerr(0, "DH_compute_key() failed: %s\n",
+       /* This computes the shared key from the DHKE */
+       dh_shared->length = DH_compute_key(dh_shared->value, remote_pub_key,
+                                          skc->sc_params);
+       if (dh_shared->length == -1) {
+               printerr(0, "DH key derivation failed: %s\n",
                         ERR_error_string(ERR_get_error(), NULL));
                goto out_err;
-       } else if (status < dh_shared->length) {
+       }
+
+       rc = GSS_S_COMPLETE;
+out_err:
+       BN_free(remote_pub_key);
+#endif /* HAVE_OPENSSL_EVP_PKEY */
+       return rc;
+}
+
+/**
+ * Computes a session key based on the DH parameters from the host and its peer
+ *
+ * \param[in,out]      skc             Shared key credentials structure with
+ *                                     the session key populated with the
+ *                                     compute key
+ * \param[in]          pub_key         Public key returned from peer in
+ *                                     gss_buffer_desc
+ * \return     gss error               failure
+ * \return     GSS_S_COMPLETE          success
+ */
+uint32_t sk_compute_dh_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
+{
+       size_t expected_len;
+       uint32_t rc;
+
+       rc = __sk_compute_dh_key(skc, pub_key, &expected_len);
+       if (rc != GSS_S_COMPLETE)
+               return rc;
+
+       if (skc->sc_dh_shared_key.length < expected_len) {
                /* there is around 1 chance out of 256 that the returned
                 * shared key is shorter than expected
                 */
-               if (status >= dh_shared->length - 2) {
-                       int shift = dh_shared->length - status;
+               if (skc->sc_dh_shared_key.length >= expected_len - 2) {
+                       int shift = expected_len - skc->sc_dh_shared_key.length;
+
                        /* if the key is short by only 1 or 2 bytes, just
                         * prepend it with 0s
                         */
-                       memmove((void *)(dh_shared->value + shift),
-                               dh_shared->value, status);
-                       memset(dh_shared->value, 0, shift);
+                       memmove((void *)(skc->sc_dh_shared_key.value + shift),
+                               skc->sc_dh_shared_key.value,
+                               skc->sc_dh_shared_key.length);
+                       memset(skc->sc_dh_shared_key.value, 0, shift);
                } else {
                        /* if the key is really too short, return GSS_S_BAD_QOP
                         * so that the caller can retry to generate
                         */
-                       printerr(0, "DH_compute_key() returned a short key of %d bytes, expected: %zu\n",
-                                status, dh_shared->length);
+                       printerr(0,
+                                "DH derivation returned a short key of %zu bytes, expected: %zu\n",
+                                skc->sc_dh_shared_key.length, expected_len);
                        rc = GSS_S_BAD_QOP;
-                       goto out_err;
                }
        }
-
-       rc = GSS_S_COMPLETE;
-
-out_err:
-       BN_free(remote_pub_key);
        return rc;
 }
 
index 0601a46..43d04c6 100644 (file)
 #include <keyutils.h>
 #endif
 #include <linux/lustre/lustre_idl.h>
-/* We need to use some deprecated APIs */
-#define OPENSSL_SUPPRESS_DEPRECATED
 #include <openssl/dh.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#ifdef HAVE_OPENSSL_EVP_PKEY
+#include <openssl/core_names.h>
+#endif
+#include <openssl/err.h>
 #include <sys/types.h>
 
 #include <libcfs/libcfs_crypto.h>
@@ -123,6 +125,11 @@ static inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key,
        if (priv_key != NULL)
                *priv_key = dh->priv_key;
 }
+
+static inline const BIGNUM *DH_get0_p(const DH *dh)
+{
+       return dh->p;
+}
 #endif
 
 /* Some limits and defaults */
@@ -212,6 +219,45 @@ struct sk_kernel_ctx {
        gss_buffer_desc skc_session_key;
 };
 
+
+#ifdef HAVE_OPENSSL_EVP_PKEY
+#define DECLARE_EVP_MD(name, hash)                                     \
+       OSSL_PARAM name[] = {                                           \
+               OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, \
+                                              (char *)EVP_MD_get0_name(hash), \
+                                              0),                      \
+               OSSL_PARAM_END                                          \
+       }
+#else /* !HAVE_OPENSSL_EVP_PKEY */
+#define EVP_PKEY DH
+#define EVP_PKEY_free(dh) DH_free(dh)
+struct dh_ssk_ctx { uint32_t bits; uint32_t gen; };
+#define EVP_PKEY_CTX struct dh_ssk_ctx
+#define EVP_PKEY_CTX_new_from_name(p1, name, p2) malloc(sizeof(EVP_PKEY_CTX))
+#define EVP_PKEY_paramgen_init(ctx) 1
+#undef EVP_PKEY_CTX_set_dh_paramgen_prime_len
+#define EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, len) ((ctx)->bits = len)
+#undef EVP_PKEY_CTX_set_dh_paramgen_generator
+#define EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, g) ((ctx)->gen = g)
+#define EVP_PKEY_paramgen(ctx, dhp)                                    \
+       ((*dhp = DH_new()) &&                                           \
+        DH_generate_parameters_ex(*(dhp), (ctx)->bits, (ctx)->gen, NULL))
+#define EVP_PKEY_get_bn_param(dh, param, bnp) (*bnp = (BIGNUM *)DH_get0_p(dh))
+#define EVP_PKEY_CTX_free(ctx) free(ctx)
+#define DECLARE_EVP_MD(name, hash)     \
+       const EVP_MD *name = hash
+#define EVP_MAC_CTX HMAC_CTX
+#define EVP_MAC_CTX_new(mac) HMAC_CTX_new()
+#define EVP_MAC_CTX_free HMAC_CTX_free
+#define EVP_MAC void
+#define EVP_MAC_fetch(a, b, c) (void *)1
+#define EVP_MAC_init(ctx, val, len, alg) HMAC_Init_ex(ctx, val, len, alg, NULL)
+#define EVP_MAC_update HMAC_Update
+#define EVP_MAC_final(ctx, val, lenp, hlen)            \
+       HMAC_Final(ctx, val, (unsigned int *)(lenp))
+#define EVP_MAC_free(mac) {}
+#endif
+
 /* Structure used in context initiation to hold all necessary data */
 struct sk_cred {
        uint32_t                 sc_flags;
@@ -222,7 +268,7 @@ struct sk_cred {
        gss_buffer_desc          sc_hmac;
        gss_buffer_desc          sc_dh_shared_key;
        struct sk_kernel_ctx     sc_kctx;
-       DH                      *sc_params;
+       EVP_PKEY *sc_params;
 };
 
 /* Names match up with openssl enc and dgst commands */
@@ -332,7 +378,9 @@ uint32_t sk_verify_hash(const char *string, const EVP_MD *hash_alg,
                        const gss_buffer_desc *current_hash);
 struct sk_cred *sk_create_cred(const char *fsname, const char *cluster,
                               const uint32_t flags);
+#ifndef HAVE_OPENSSL_EVP_PKEY
 int sk_speedtest_dh_valid(unsigned int usec_check_max);
+#endif
 uint32_t sk_gen_params(struct sk_cred *skc, int num_rounds);
 int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
                 const EVP_MD *hash_alg, gss_buffer_desc *hmac);
index 30b91c8..ea28bcb 100644 (file)
@@ -74,14 +74,15 @@ svcgssd_run()
        struct timespec         halfsec = { .tv_sec = 0, .tv_nsec = 500000000 };
 
        if (sk_enabled) {
-#if OPENSSL_VERSION_NUMBER >= 0x1010103fL
+#if !defined(HAVE_OPENSSL_EVP_PKEY) && OPENSSL_VERSION_NUMBER >= 0x1010103fL
                sk_dh_checks =
                        sk_speedtest_dh_valid(MAX_ALLOWED_TIME_FOR_PRIME);
+               if (sk_dh_checks)
+                       printerr(1, "will use %d rounds for prime testing\n",
+                                sk_dh_checks);
 #else
                sk_dh_checks = 0;
 #endif
-               printerr(1, "will use %d rounds for prime testing\n",
-                        sk_dh_checks);
        }
 
        while (1) {
index eb9bfe7..87a0ad0 100644 (file)
@@ -475,7 +475,7 @@ redo:
                 */
                attempts++;
                if (skc->sc_params) {
-                       DH_free(skc->sc_params);
+                       EVP_PKEY_free(skc->sc_params);
                        skc->sc_params = NULL;
                }
                if (skc->sc_pub_key.value) {
@@ -484,6 +484,9 @@ redo:
                }
                skc->sc_pub_key.length = 0;
                if (skc->sc_dh_shared_key.value) {
+                       /* erase secret key before freeing memory */
+                       memset(skc->sc_dh_shared_key.value, 0,
+                              skc->sc_dh_shared_key.length);
                        free(skc->sc_dh_shared_key.value);
                        skc->sc_dh_shared_key.value = NULL;
                }
@@ -521,7 +524,7 @@ redo:
        printerr(2, "Created netstring of %zd bytes\n", snd->out_tok.length);
 
        if (sk_session_kdf(skc, snd->nid, &snd->in_tok, &snd->out_tok)) {
-               printerr(0, "Failed to calulate derviced session key\n");
+               printerr(0, "Failed to calculate derived session key\n");
                goto out_err;
        }
        if (sk_compute_keys(skc)) {