Whamcloud - gitweb
LU-8602 gss: Properly port gss to newer crypto api.
[fs/lustre-release.git] / lustre / utils / gss / sk_utils.c
index f38510f..4b4a911 100644 (file)
@@ -22,6 +22,8 @@
 /*
  * Copyright (C) 2015, Trustees of Indiana University
  *
+ * Copyright (c) 2016, 2017, Intel Corporation.
+ *
  * Author: Jeremy Filizetti <jfilizet@iu.edu>
  */
 
 #include <openssl/hmac.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <lnet/nidstr.h>
 
 #include "sk_utils.h"
 #include "write_bytes.h"
 
 #define SK_PBKDF2_ITERATIONS 10000
 
-static struct sk_crypt_type sk_crypt_types[] = {
-       [SK_CRYPT_AES256_CTR] = {
-               .sct_name = "ctr(aes)",
-               .sct_bytes = 32,
-       },
-};
-
-static struct sk_hmac_type sk_hmac_types[] = {
-       [SK_HMAC_SHA256] = {
-               .sht_name = "hmac(sha256)",
-               .sht_bytes = 32,
-       },
-       [SK_HMAC_SHA512] = {
-               .sht_name = "hmac(sha512)",
-               .sht_bytes = 64,
-       },
-};
-
 #ifdef _NEW_BUILD_
 # include "lgss_utils.h"
 #else
@@ -109,14 +92,22 @@ struct sk_keyfile_config *sk_read_file(char *filename)
 
        /* allow standard input override */
        if (strcmp(filename, "-") == 0)
-               fd = dup(STDIN_FILENO);
+               fd = STDIN_FILENO;
        else
                fd = open(filename, O_RDONLY);
 
        if (fd == -1) {
-               printerr(0, "Error opening file %s: %s\n", filename,
+               printerr(0, "Error opening key file '%s': %s\n", filename,
                         strerror(errno));
                goto out_free;
+       } else if (fd != STDIN_FILENO) {
+               struct stat st;
+
+               rc = fstat(fd, &st);
+               if (rc == 0 && (st.st_mode & ~(S_IFREG | 0600)))
+                       fprintf(stderr, "warning: "
+                               "secret key '%s' has insecure file mode %#o\n",
+                               filename, st.st_mode);
        }
 
        ptr = (char *)config;
@@ -138,7 +129,8 @@ struct sk_keyfile_config *sk_read_file(char *filename)
                remain -= rc;
        }
 
-       close(fd);
+       if (fd != STDIN_FILENO)
+               close(fd);
        sk_config_disk_to_cpu(config);
        return config;
 
@@ -200,7 +192,7 @@ static key_serial_t sk_load_key(const struct sk_keyfile_config *skc,
  * \return     0       sucess
  * \return     -1      failure
  */
-int sk_load_keyfile(char *path, int type)
+int sk_load_keyfile(char *path)
 {
        struct sk_keyfile_config *config;
        char description[SK_DESCRIPTION_SIZE + 1];
@@ -233,7 +225,7 @@ int sk_load_keyfile(char *path, int type)
        /* The server side can have multiple key files per file system so
         * the nodemap name is appended to the key description to uniquely
         * identify it */
-       if (type & SK_TYPE_MGS) {
+       if (config->skc_type & SK_TYPE_MGS) {
                /* Any key can be an MGS key as long as we are told to use it */
                rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:MGS:%s",
                              config->skc_nodemap);
@@ -242,7 +234,7 @@ int sk_load_keyfile(char *path, int type)
                if (sk_load_key(config, description) == -1)
                        goto out;
        }
-       if (type & SK_TYPE_SERVER) {
+       if (config->skc_type & SK_TYPE_SERVER) {
                /* Server keys need to have the file system name in the key */
                if (!config->skc_fsname) {
                        printerr(0, "Key configuration has no file system "
@@ -256,7 +248,7 @@ int sk_load_keyfile(char *path, int type)
                if (sk_load_key(config, description) == -1)
                        goto out;
        }
-       if (type & SK_TYPE_CLIENT) {
+       if (config->skc_type & SK_TYPE_CLIENT) {
                /* Load client file system key */
                if (config->skc_fsname) {
                        rc = snprintf(description, SK_DESCRIPTION_SIZE,
@@ -305,7 +297,7 @@ void sk_config_cpu_to_disk(struct sk_keyfile_config *config)
        config->skc_crypt_alg = htobe16(config->skc_crypt_alg);
        config->skc_expire = htobe32(config->skc_expire);
        config->skc_shared_keylen = htobe32(config->skc_shared_keylen);
-       config->skc_session_keylen = htobe32(config->skc_session_keylen);
+       config->skc_prime_bits = htobe32(config->skc_prime_bits);
 
        for (i = 0; i < MAX_MGSNIDS; i++)
                config->skc_mgsnids[i] = htobe64(config->skc_mgsnids[i]);
@@ -330,7 +322,7 @@ void sk_config_disk_to_cpu(struct sk_keyfile_config *config)
        config->skc_crypt_alg = be16toh(config->skc_crypt_alg);
        config->skc_expire = be32toh(config->skc_expire);
        config->skc_shared_keylen = be32toh(config->skc_shared_keylen);
-       config->skc_session_keylen = be32toh(config->skc_session_keylen);
+       config->skc_prime_bits = be32toh(config->skc_prime_bits);
 
        for (i = 0; i < MAX_MGSNIDS; i++)
                config->skc_mgsnids[i] = be64toh(config->skc_mgsnids[i]);
@@ -354,18 +346,22 @@ int sk_validate_config(const struct sk_keyfile_config *config)
                printerr(0, "Null configuration passed\n");
                return -1;
        }
+
        if (config->skc_version != SK_CONF_VERSION) {
                printerr(0, "Invalid version\n");
                return -1;
        }
-       if (config->skc_hmac_alg >= SK_HMAC_MAX) {
+
+       if (config->skc_hmac_alg == SK_HMAC_INVALID) {
                printerr(0, "Invalid HMAC algorithm\n");
                return -1;
        }
-       if (config->skc_crypt_alg >= SK_CRYPT_MAX) {
+
+       if (config->skc_crypt_alg == SK_CRYPT_INVALID) {
                printerr(0, "Invalid crypt algorithm\n");
                return -1;
        }
+
        if (config->skc_expire < 60 || config->skc_expire > INT_MAX) {
                /* Try to limit key expiration to some reasonable minimum and
                 * also prevent values over INT_MAX because there appears
@@ -374,11 +370,11 @@ int sk_validate_config(const struct sk_keyfile_config *config)
                         "and %d\n", 60, INT_MAX);
                return -1;
        }
-       if (config->skc_session_keylen % 8 != 0 ||
-           config->skc_session_keylen > SK_SESSION_MAX_KEYLEN_BYTES * 8) {
+       if (config->skc_prime_bits % 8 != 0 ||
+           config->skc_prime_bits > SK_MAX_P_BYTES * 8) {
                printerr(0, "Invalid session key length must be a multiple of 8"
                         " and less then %d bits\n",
-                        SK_SESSION_MAX_KEYLEN_BYTES * 8);
+                        SK_MAX_P_BYTES * 8);
                return -1;
        }
        if (config->skc_shared_keylen % 8 != 0 ||
@@ -406,6 +402,11 @@ int sk_validate_config(const struct sk_keyfile_config *config)
                return -1;
        }
 
+       if (config->skc_type == SK_TYPE_INVALID) {
+               printerr(0, "Invalid key type\n");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -645,12 +646,11 @@ struct sk_cred *sk_create_cred(const char *tgt, const char *nodemap,
 
        kctx = &skc->sc_kctx;
        kctx->skc_version = config->skc_version;
-       kctx->skc_hmac_alg = config->skc_hmac_alg;
-       kctx->skc_crypt_alg = config->skc_crypt_alg;
+       strcpy(kctx->skc_hmac_alg, sk_hmac2name(config->skc_hmac_alg));
+       strcpy(kctx->skc_crypt_alg, sk_crypt2name(config->skc_crypt_alg));
        kctx->skc_expire = config->skc_expire;
 
        /* key payload format is in bits, convert to bytes */
-       skc->sc_session_keylen = config->skc_session_keylen / 8;
        kctx->skc_shared_key.length = config->skc_shared_keylen / 8;
        kctx->skc_shared_key.value = malloc(kctx->skc_shared_key.length);
        if (!kctx->skc_shared_key.value) {
@@ -660,6 +660,14 @@ struct sk_cred *sk_create_cred(const char *tgt, const char *nodemap,
        memcpy(kctx->skc_shared_key.value, config->skc_shared_key,
               kctx->skc_shared_key.length);
 
+       skc->sc_p.length = config->skc_prime_bits / 8;
+       skc->sc_p.value = malloc(skc->sc_p.length);
+       if (!skc->sc_p.value) {
+               printerr(0, "Failed to allocate p\n");
+               goto out_err;
+       }
+       memcpy(skc->sc_p.value, config->skc_p, skc->sc_p.length);
+
        free(config);
 
        return skc;
@@ -671,54 +679,50 @@ out_err:
        return NULL;
 }
 
-static void sk_free_parameters(struct sk_cred *skc)
-{
-       if (skc->sc_params)
-               DH_free(skc->sc_params);
-       if (skc->sc_p.value)
-               free(skc->sc_p.value);
-       if (skc->sc_pub_key.value)
-               free(skc->sc_pub_key.value);
-
-       skc->sc_params = NULL;
-       skc->sc_p.value = NULL;
-       skc->sc_p.length = 0;
-       skc->sc_pub_key.value = NULL;
-       skc->sc_pub_key.length = 0;
-}
-
 /**
- * Generates a public key and computes the private key for the DH key exchange.
- * The parameters must be populated with the p and g from the peer.
+ * Populates the DH parameters for the DHKE
  *
- * \param[in,out]      skc     Shared key credentials structure to populate
- *                             with DH parameters
+ * \param[in,out]      skc             Shared key credentials structure to
+ *                                     populate with DH parameters
  *
  * \retval     GSS_S_COMPLETE  success
  * \retval     GSS_S_FAILURE   failure
  */
-static uint32_t sk_gen_responder_params(struct sk_cred *skc)
+uint32_t sk_gen_params(struct sk_cred *skc)
 {
+       uint32_t random;
        int rc;
 
-       /* No keys to generate without privacy mode */
-       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
-               return GSS_S_COMPLETE;
+       /* 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 */
+       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));
+               return GSS_S_FAILURE;
+       }
 
+       /* The random value will always be used in byte range operations
+        * 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;
        }
 
-       /* responder should already have sc_p populated */
        skc->sc_params->p = BN_bin2bn(skc->sc_p.value, skc->sc_p.length, NULL);
        if (!skc->sc_params->p) {
                printerr(0, "Failed to convert binary to BIGNUM\n");
                return GSS_S_FAILURE;
        }
 
-       /* and we use a static generator for shared key */
+       /* We use a static generator for shared key */
        skc->sc_params->g = BN_new();
        if (!skc->sc_params->g) {
                printerr(0, "Failed to allocate new BIGNUM\n");
@@ -729,7 +733,7 @@ static uint32_t sk_gen_responder_params(struct sk_cred *skc)
                return GSS_S_FAILURE;
        }
 
-       /* verify that we have a safe prime and valid generator */
+       /* Verify that we have a safe prime and valid generator */
        if (DH_check(skc->sc_params, &rc) != 1) {
                printerr(0, "DH_check() failed: %d\n", rc);
                return GSS_S_FAILURE;
@@ -757,114 +761,18 @@ static uint32_t sk_gen_responder_params(struct sk_cred *skc)
 }
 
 /**
- * Generates shared key Diffie Hellman parameters used for the DH key exchange
- * between host and peer if privacy mode is enabled
- *
- * \param[in,out]      skc     Shared key credentials structure to populate
- *                             with DH parameters
- *
- * \retval     GSS_S_COMPLETE  success
- * \retval     GSS_S_FAILURE   failure
- */
-static uint32_t sk_gen_initiator_params(struct sk_cred *skc)
-{
-       int rc;
-
-       /* The credential could be used so free existing parameters */
-       sk_free_parameters(skc);
-
-       /* Only privacy mode needs the rest of the parameter generation
-        * but we use IV in other modes as well so tokens should be
-        * unique */
-       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
-               return GSS_S_COMPLETE;
-
-       skc->sc_params = DH_generate_parameters(skc->sc_session_keylen * 8,
-                                               SK_GENERATOR, NULL, NULL);
-       if (skc->sc_params == NULL) {
-               printerr(0, "Failed to generate diffie-hellman parameters: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-               return GSS_S_FAILURE;
-       }
-
-       if (DH_check(skc->sc_params, &rc) != 1) {
-               printerr(0, "DH_check() failed: %d\n", rc);
-               return GSS_S_FAILURE;
-       } else if (rc) {
-               printerr(0, "DH_check() returned error codes: 0x%x\n", rc);
-               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;
-       }
-
-       skc->sc_p.length = BN_num_bytes(skc->sc_params->p);
-       skc->sc_pub_key.length = BN_num_bytes(skc->sc_params->pub_key);
-       skc->sc_p.value = malloc(skc->sc_p.length);
-       skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
-       if (!skc->sc_p.value || !skc->sc_pub_key.value) {
-               printerr(0, "Failed to allocate memory for params\n");
-               return GSS_S_FAILURE;
-       }
-
-       BN_bn2bin(skc->sc_params->pub_key, skc->sc_pub_key.value);
-       BN_bn2bin(skc->sc_params->p, skc->sc_p.value);
-
-       return GSS_S_COMPLETE;
-}
-
-/**
- * Generates or populates the DH parameters depending on whether the system is
- * the initiator or responder for the connection
- *
- * \param[in,out]      skc             Shared key credentials structure to
- *                                     populate with DH parameters
- * \param[in]          initiator       Boolean whether to initiate parameters
- *
- * \retval     GSS_S_COMPLETE  success
- * \retval     GSS_S_FAILURE   failure
- */
-uint32_t sk_gen_params(struct sk_cred *skc, const bool initiator)
-{
-       uint32_t random;
-
-       /* 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 */
-       if (RAND_bytes((unsigned char *)&random, sizeof(random)) != 1) {
-               printerr(0, "Failed to get data for random parameter\n");
-               return GSS_S_FAILURE;
-       }
-
-       /* The random value will always be used in byte range operations
-        * so we keep it as big endian from this point on */
-       skc->sc_kctx.skc_host_random = htobe32(random);
-
-       if (initiator)
-               return sk_gen_initiator_params(skc);
-
-       return sk_gen_responder_params(skc);
-}
-
-/**
  * Convert SK hash algorithm into openssl message digest
  *
  * \param[in,out]      alg             SK hash algorithm
  *
  * \retval             EVP_MD
  */
-static inline const EVP_MD *sk_hash_to_evp_md(enum sk_hmac_alg alg)
+static inline const EVP_MD *sk_hash_to_evp_md(enum cfs_crypto_hash_alg alg)
 {
        switch (alg) {
-       case SK_HMAC_SHA256:
+       case CFS_HASH_ALG_SHA256:
                return EVP_sha256();
-       case SK_HMAC_SHA512:
+       case CFS_HASH_ALG_SHA512:
                return EVP_sha512();
        default:
                return EVP_md_null();
@@ -1049,7 +957,8 @@ void sk_free_cred(struct sk_cred *skc)
  * If the size is smaller it will take copy the first N bytes necessary to
  * fill the derived key. */
 int sk_kdf(gss_buffer_desc *derived_key , gss_buffer_desc *origin_key,
-          gss_buffer_desc *key_binding_bufs, int numbufs, int hmac_alg)
+          gss_buffer_desc *key_binding_bufs, int numbufs,
+          enum cfs_crypto_hash_alg hmac_alg)
 {
        size_t remain;
        size_t bytes;
@@ -1081,8 +990,10 @@ int sk_kdf(gss_buffer_desc *derived_key , gss_buffer_desc *origin_key,
                        return rc;
                }
 
-               LASSERT(sk_hmac_types[hmac_alg].sht_bytes ==
-                       tmp_hash.length);
+               if (cfs_crypto_hash_digestsize(hmac_alg) != tmp_hash.length) {
+                       free(tmp_hash.value);
+                       return -EINVAL;
+               }
 
                bytes = (remain < tmp_hash.length) ? remain : tmp_hash.length;
                memcpy(keydata, tmp_hash.value, bytes);
@@ -1109,9 +1020,11 @@ int sk_session_kdf(struct sk_cred *skc, lnet_nid_t client_nid,
        struct sk_kernel_ctx *kctx = &skc->sc_kctx;
        gss_buffer_desc *session_key = &kctx->skc_session_key;
        gss_buffer_desc bufs[5];
+       enum cfs_crypto_crypt_alg crypt_alg;
        int rc = -1;
 
-       session_key->length = sk_crypt_types[kctx->skc_crypt_alg].sct_bytes;
+       crypt_alg = cfs_crypto_crypt_alg(kctx->skc_crypt_alg);
+       session_key->length = cfs_crypto_crypt_keysize(crypt_alg);
        session_key->value = malloc(session_key->length);
        if (!session_key->value) {
                printerr(0, "Failed to allocate memory for session key\n");
@@ -1133,7 +1046,7 @@ int sk_session_kdf(struct sk_cred *skc, lnet_nid_t client_nid,
        bufs[4] = *server_token;
 
        return sk_kdf(&kctx->skc_session_key, &kctx->skc_shared_key, bufs,
-                     5, kctx->skc_hmac_alg);
+                     5, cfs_crypto_hash_alg(kctx->skc_hmac_alg));
 }
 
 /* Uses the session key to create an HMAC key and encryption key.  In
@@ -1165,18 +1078,21 @@ int sk_compute_keys(struct sk_cred *skc)
        gss_buffer_desc *session_key = &kctx->skc_session_key;
        gss_buffer_desc *hmac_key = &kctx->skc_hmac_key;
        gss_buffer_desc *encrypt_key = &kctx->skc_encrypt_key;
+       enum cfs_crypto_hash_alg hmac_alg;
+       enum cfs_crypto_crypt_alg crypt_alg;
        char *encrypt = "Encrypt";
        char *integrity = "Integrity";
        int rc;
 
-       hmac_key->length = sk_hmac_types[kctx->skc_hmac_alg].sht_bytes;
+       hmac_alg = cfs_crypto_hash_alg(kctx->skc_hmac_alg);
+       hmac_key->length = cfs_crypto_hash_digestsize(hmac_alg);
        hmac_key->value = malloc(hmac_key->length);
        if (!hmac_key->value)
                return -ENOMEM;
 
        rc = PKCS5_PBKDF2_HMAC(integrity, -1, session_key->value,
                               session_key->length, SK_PBKDF2_ITERATIONS,
-                              sk_hash_to_evp_md(kctx->skc_hmac_alg),
+                              sk_hash_to_evp_md(hmac_alg),
                               hmac_key->length, hmac_key->value);
        if (rc == 0)
                return -EINVAL;
@@ -1185,14 +1101,15 @@ int sk_compute_keys(struct sk_cred *skc)
        if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
                return 0;
 
-       encrypt_key->length = sk_crypt_types[kctx->skc_crypt_alg].sct_bytes;
+       crypt_alg = cfs_crypto_crypt_alg(kctx->skc_crypt_alg);
+       encrypt_key->length = cfs_crypto_crypt_keysize(crypt_alg);
        encrypt_key->value = malloc(encrypt_key->length);
        if (!encrypt_key->value)
                return -ENOMEM;
 
        rc = PKCS5_PBKDF2_HMAC(encrypt, -1, session_key->value,
                               session_key->length, SK_PBKDF2_ITERATIONS,
-                              sk_hash_to_evp_md(kctx->skc_hmac_alg),
+                              sk_hash_to_evp_md(hmac_alg),
                               encrypt_key->length, encrypt_key->value);
        if (rc == 0)
                return -EINVAL;
@@ -1218,10 +1135,6 @@ uint32_t sk_compute_dh_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
        int status;
        uint32_t rc = GSS_S_FAILURE;
 
-       /* No keys computed unless privacy mode is in use */
-       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
-               return GSS_S_COMPLETE;
-
        remote_pub_key = BN_bin2bn(pub_key->value, pub_key->length, NULL);
        if (!remote_pub_key) {
                printerr(0, "Failed to convert binary to BIGNUM\n");