From 615691a531a80b75c4dd054dbb86d0bdbf4cf808 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Mon, 13 Jun 2022 14:41:11 +0200 Subject: [PATCH 1/1] LU-15896 gss: support OpenSSLv3 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. Fixes: ee60c14360 ("LU-15896 gss: ignore OpenSSLv3 deprecated API") Signed-off-by: Sebastien Buisson Change-Id: I78a4ca18b25aca3c34fe84e41413a33caddc01b6 Reviewed-on: https://review.whamcloud.com/47717 Reviewed-by: Andreas Dilger Tested-by: jenkins Reviewed-by: James Simmons Tested-by: Maloo Reviewed-by: Oleg Drokin --- lustre/autoconf/lustre-core.m4 | 81 ++++++- lustre/utils/gss/lgss_sk.c | 95 +++++--- lustre/utils/gss/lgss_sk_utils.c | 12 +- lustre/utils/gss/sk_utils.c | 423 ++++++++++++++++++++++++++--------- lustre/utils/gss/sk_utils.h | 54 ++++- lustre/utils/gss/svcgssd_main_loop.c | 7 +- lustre/utils/gss/svcgssd_proc.c | 7 +- 7 files changed, 520 insertions(+), 159 deletions(-) diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index b5df23f..bcd0294 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -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 @@ -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 @@ -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 @@ -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 + + 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 @@ -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 diff --git a/lustre/utils/gss/lgss_sk.c b/lustre/utils/gss/lgss_sk.c index f5782cb..afc1181 100644 --- a/lustre/utils/gss/lgss_sk.c +++ b/lustre/utils/gss/lgss_sk.c @@ -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; } diff --git a/lustre/utils/gss/lgss_sk_utils.c b/lustre/utils/gss/lgss_sk_utils.c index 5974c33..4ee8a48 100644 --- a/lustre/utils/gss/lgss_sk_utils.c +++ b/lustre/utils/gss/lgss_sk_utils.c @@ -27,8 +27,6 @@ #include #include -/* We need to use some deprecated APIs */ -#define OPENSSL_SUPPRESS_DEPRECATED #include #include #include @@ -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; } diff --git a/lustre/utils/gss/sk_utils.c b/lustre/utils/gss/sk_utils.c index 3061b22..e627199 100755 --- a/lustre/utils/gss/sk_utils.c +++ b/lustre/utils/gss/sk_utils.c @@ -33,12 +33,13 @@ #include #include #include -/* We need to use some deprecated APIs */ -#define OPENSSL_SUPPRESS_DEPRECATED #include #include #include #include +#ifdef HAVE_OPENSSL_EVP_PKEY +#include +#endif #include #include #include @@ -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; } diff --git a/lustre/utils/gss/sk_utils.h b/lustre/utils/gss/sk_utils.h index 0601a46..43d04c6 100644 --- a/lustre/utils/gss/sk_utils.h +++ b/lustre/utils/gss/sk_utils.h @@ -34,11 +34,13 @@ #include #endif #include -/* We need to use some deprecated APIs */ -#define OPENSSL_SUPPRESS_DEPRECATED #include #include #include +#ifdef HAVE_OPENSSL_EVP_PKEY +#include +#endif +#include #include #include @@ -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); diff --git a/lustre/utils/gss/svcgssd_main_loop.c b/lustre/utils/gss/svcgssd_main_loop.c index 30b91c8..ea28bcb 100644 --- a/lustre/utils/gss/svcgssd_main_loop.c +++ b/lustre/utils/gss/svcgssd_main_loop.c @@ -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) { diff --git a/lustre/utils/gss/svcgssd_proc.c b/lustre/utils/gss/svcgssd_proc.c index eb9bfe7..87a0ad0 100644 --- a/lustre/utils/gss/svcgssd_proc.c +++ b/lustre/utils/gss/svcgssd_proc.c @@ -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)) { -- 1.8.3.1