SUNRPC: Hoist KDF into struct gss_krb5_enctype
authorChuck Lever <chuck.lever@oracle.com>
Sun, 15 Jan 2023 17:22:11 +0000 (12:22 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 20 Feb 2023 14:20:42 +0000 (09:20 -0500)
Each Kerberos enctype can have a different KDF. Refactor the key
derivation path to support different KDFs for the enctypes
introduced in subsequent patches.

In particular, expose the key derivation function in struct
gss_krb5_enctype instead of the enctype's preferred random-to-key
function. The latter is usually the identity function and is only
ever called during key derivation, so have each KDF call it
directly.

A couple of extra clean-ups:
- Deduplicate the set_cdata() helper
- Have ->derive_key return negative errnos, in accordance with usual
  kernel coding conventions

This patch is a little bigger than I'd like, but these are all
mechanical changes and they are all to the same areas of code. No
behavior change is intended.

Tested-by: Scott Mayhew <smayhew@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
include/linux/sunrpc/gss_krb5.h
net/sunrpc/auth_gss/gss_krb5_internal.h
net/sunrpc/auth_gss/gss_krb5_keys.c
net/sunrpc/auth_gss/gss_krb5_mech.c

index 85fce36c242dc6ae298e163b9dde64f4c051f47c..04addef79177f0f46dd36c7bc8082142f9cc7b7a 100644 (file)
@@ -70,9 +70,11 @@ struct gss_krb5_enctype {
        const u32               keybytes;       /* raw key len, in bytes */
        const u32               keylength;      /* final key len, in bytes */
        int (*import_ctx)(struct krb5_ctx *ctx, gfp_t gfp_mask);
-       u32 (*mk_key) (const struct gss_krb5_enctype *gk5e,
-                      struct xdr_netobj *in,
-                      struct xdr_netobj *out); /* complete key generation */
+       int (*derive_key)(const struct gss_krb5_enctype *gk5e,
+                         const struct xdr_netobj *in,
+                         struct xdr_netobj *out,
+                         const struct xdr_netobj *label,
+                         gfp_t gfp_mask);
        u32 (*encrypt)(struct krb5_ctx *kctx, u32 offset,
                        struct xdr_buf *buf, struct page **pages);
        u32 (*decrypt)(struct krb5_ctx *kctx, u32 offset, u32 len,
@@ -257,21 +259,4 @@ krb5_get_seq_num(struct krb5_ctx *kctx,
 int
 xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen);
 
-u32
-krb5_derive_key(const struct gss_krb5_enctype *gk5e,
-               const struct xdr_netobj *inkey,
-               struct xdr_netobj *outkey,
-               const struct xdr_netobj *in_constant,
-               gfp_t gfp_mask);
-
-u32
-gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
-                      struct xdr_netobj *randombits,
-                      struct xdr_netobj *key);
-
-u32
-gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
-                     struct xdr_netobj *randombits,
-                     struct xdr_netobj *key);
-
 #endif /* _LINUX_SUNRPC_GSS_KRB5_H */
index 837bcfa3742f69ded94538ca2c004bc6ce4bb926..c7580026adc7c00c437a750409ef7301d3771a57 100644 (file)
@@ -38,6 +38,52 @@ u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
  * Implementation internal functions
  */
 
+/* Key Derivation Functions */
+
+int krb5_derive_key_v1(const struct gss_krb5_enctype *gk5e,
+                      const struct xdr_netobj *inkey,
+                      struct xdr_netobj *outkey,
+                      const struct xdr_netobj *label,
+                      gfp_t gfp_mask);
+
+int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
+                      const struct xdr_netobj *inkey,
+                      struct xdr_netobj *outkey,
+                      const struct xdr_netobj *label,
+                      gfp_t gfp_mask);
+
+/**
+ * krb5_derive_key - Derive a subkey from a protocol key
+ * @kctx: Kerberos 5 context
+ * @inkey: base protocol key
+ * @outkey: OUT: derived key
+ * @usage: key usage value
+ * @seed: key usage seed (one octet)
+ * @gfp_mask: memory allocation control flags
+ *
+ * Caller sets @outkey->len to the desired length of the derived key.
+ *
+ * On success, returns 0 and fills in @outkey. A negative errno value
+ * is returned on failure.
+ */
+static inline int krb5_derive_key(struct krb5_ctx *kctx,
+                                 const struct xdr_netobj *inkey,
+                                 struct xdr_netobj *outkey,
+                                 u32 usage, u8 seed, gfp_t gfp_mask)
+{
+       const struct gss_krb5_enctype *gk5e = kctx->gk5e;
+       u8 label_data[GSS_KRB5_K5CLENGTH];
+       struct xdr_netobj label = {
+               .len    = sizeof(label_data),
+               .data   = label_data,
+       };
+       __be32 *p = (__be32 *)label_data;
+
+       *p = cpu_to_be32(usage);
+       label_data[4] = seed;
+       return gk5e->derive_key(gk5e, inkey, outkey, &label, gfp_mask);
+}
+
 void krb5_make_confounder(u8 *p, int conflen);
 
 u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
index a7c6866dad96222a2205578fe14600c3cd7da4af..f6de4fdd63ae476599cda73c8eb212b720196deb 100644 (file)
@@ -139,23 +139,20 @@ static void krb5_nfold(u32 inbits, const u8 *in,
  * This is the DK (derive_key) function as described in rfc3961, sec 5.1
  * Taken from MIT Kerberos and modified.
  */
-
-u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
-                   const struct xdr_netobj *inkey,
-                   struct xdr_netobj *outkey,
-                   const struct xdr_netobj *in_constant,
-                   gfp_t gfp_mask)
+static int krb5_DK(const struct gss_krb5_enctype *gk5e,
+                  const struct xdr_netobj *inkey, u8 *rawkey,
+                  const struct xdr_netobj *in_constant, gfp_t gfp_mask)
 {
        size_t blocksize, keybytes, keylength, n;
-       unsigned char *inblockdata, *outblockdata, *rawkey;
+       unsigned char *inblockdata, *outblockdata;
        struct xdr_netobj inblock, outblock;
        struct crypto_sync_skcipher *cipher;
-       u32 ret = EINVAL;
+       int ret = -EINVAL;
 
        keybytes = gk5e->keybytes;
        keylength = gk5e->keylength;
 
-       if ((inkey->len != keylength) || (outkey->len != keylength))
+       if (inkey->len != keylength)
                goto err_return;
 
        cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
@@ -165,7 +162,7 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
        if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len))
                goto err_return;
 
-       ret = ENOMEM;
+       ret = -ENOMEM;
        inblockdata = kmalloc(blocksize, gfp_mask);
        if (inblockdata == NULL)
                goto err_free_cipher;
@@ -174,10 +171,6 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
        if (outblockdata == NULL)
                goto err_free_in;
 
-       rawkey = kmalloc(keybytes, gfp_mask);
-       if (rawkey == NULL)
-               goto err_free_out;
-
        inblock.data = (char *) inblockdata;
        inblock.len = blocksize;
 
@@ -210,26 +203,8 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
                n += outblock.len;
        }
 
-       /* postprocess the key */
-
-       inblock.data = (char *) rawkey;
-       inblock.len = keybytes;
-
-       BUG_ON(gk5e->mk_key == NULL);
-       ret = (*(gk5e->mk_key))(gk5e, &inblock, outkey);
-       if (ret) {
-               dprintk("%s: got %d from mk_key function for '%s'\n",
-                       __func__, ret, gk5e->encrypt_name);
-               goto err_free_raw;
-       }
-
-       /* clean memory, free resources and exit */
-
        ret = 0;
 
-err_free_raw:
-       kfree_sensitive(rawkey);
-err_free_out:
        kfree_sensitive(outblockdata);
 err_free_in:
        kfree_sensitive(inblockdata);
@@ -252,15 +227,11 @@ static void mit_des_fixup_key_parity(u8 key[8])
        }
 }
 
-/*
- * This is the des3 key derivation postprocess function
- */
-u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
-                          struct xdr_netobj *randombits,
-                          struct xdr_netobj *key)
+static int krb5_random_to_key_v1(const struct gss_krb5_enctype *gk5e,
+                                struct xdr_netobj *randombits,
+                                struct xdr_netobj *key)
 {
-       int i;
-       u32 ret = EINVAL;
+       int i, ret = -EINVAL;
 
        if (key->len != 24) {
                dprintk("%s: key->len is %d\n", __func__, key->len);
@@ -292,14 +263,49 @@ err_out:
        return ret;
 }
 
+/**
+ * krb5_derive_key_v1 - Derive a subkey for an RFC 3961 enctype
+ * @gk5e: Kerberos 5 enctype profile
+ * @inkey: base protocol key
+ * @outkey: OUT: derived key
+ * @label: subkey usage label
+ * @gfp_mask: memory allocation control flags
+ *
+ * Caller sets @outkey->len to the desired length of the derived key.
+ *
+ * On success, returns 0 and fills in @outkey. A negative errno value
+ * is returned on failure.
+ */
+int krb5_derive_key_v1(const struct gss_krb5_enctype *gk5e,
+                      const struct xdr_netobj *inkey,
+                      struct xdr_netobj *outkey,
+                      const struct xdr_netobj *label,
+                      gfp_t gfp_mask)
+{
+       struct xdr_netobj inblock;
+       int ret;
+
+       inblock.len = gk5e->keybytes;
+       inblock.data = kmalloc(inblock.len, gfp_mask);
+       if (!inblock.data)
+               return -ENOMEM;
+
+       ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask);
+       if (!ret)
+               ret = krb5_random_to_key_v1(gk5e, &inblock, outkey);
+
+       kfree_sensitive(inblock.data);
+       return ret;
+}
+
 /*
- * This is the aes key derivation postprocess function
+ * This is the identity function, with some sanity checking.
  */
-u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
-                         struct xdr_netobj *randombits,
-                         struct xdr_netobj *key)
+static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e,
+                                struct xdr_netobj *randombits,
+                                struct xdr_netobj *key)
 {
-       u32 ret = EINVAL;
+       int ret = -EINVAL;
 
        if (key->len != 16 && key->len != 32) {
                dprintk("%s: key->len is %d\n", __func__, key->len);
@@ -320,3 +326,38 @@ u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
 err_out:
        return ret;
 }
+
+/**
+ * krb5_derive_key_v2 - Derive a subkey for an RFC 3962 enctype
+ * @gk5e: Kerberos 5 enctype profile
+ * @inkey: base protocol key
+ * @outkey: OUT: derived key
+ * @label: subkey usage label
+ * @gfp_mask: memory allocation control flags
+ *
+ * Caller sets @outkey->len to the desired length of the derived key.
+ *
+ * On success, returns 0 and fills in @outkey. A negative errno value
+ * is returned on failure.
+ */
+int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
+                      const struct xdr_netobj *inkey,
+                      struct xdr_netobj *outkey,
+                      const struct xdr_netobj *label,
+                      gfp_t gfp_mask)
+{
+       struct xdr_netobj inblock;
+       int ret;
+
+       inblock.len = gk5e->keybytes;
+       inblock.data = kmalloc(inblock.len, gfp_mask);
+       if (!inblock.data)
+               return -ENOMEM;
+
+       ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask);
+       if (!ret)
+               ret = krb5_random_to_key_v2(gk5e, &inblock, outkey);
+
+       kfree_sensitive(inblock.data);
+       return ret;
+}
index 9f393175a276a630752d8043436a7700c2a57e2a..9e9551b224f6c4bc530f6783ab4e00218ee68fb9 100644 (file)
@@ -49,7 +49,6 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
          .encrypt_name = "cbc(des)",
          .cksum_name = "md5",
          .import_ctx = gss_krb5_import_ctx_des,
-         .mk_key = NULL,
          .get_mic = gss_krb5_get_mic_v1,
          .verify_mic = gss_krb5_verify_mic_v1,
          .wrap = gss_krb5_wrap_v1,
@@ -71,7 +70,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
          .encrypt_name = "cbc(des3_ede)",
          .cksum_name = "hmac(sha1)",
          .import_ctx = gss_krb5_import_ctx_v1,
-         .mk_key = gss_krb5_des3_make_key,
+         .derive_key = krb5_derive_key_v1,
          .get_mic = gss_krb5_get_mic_v1,
          .verify_mic = gss_krb5_verify_mic_v1,
          .wrap = gss_krb5_wrap_v1,
@@ -97,7 +96,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
          .aux_cipher = "cbc(aes)",
          .cksum_name = "hmac(sha1)",
          .import_ctx = gss_krb5_import_ctx_v2,
-         .mk_key = gss_krb5_aes_make_key,
+         .derive_key = krb5_derive_key_v2,
          .encrypt = gss_krb5_aes_encrypt,
          .decrypt = gss_krb5_aes_decrypt,
 
@@ -124,7 +123,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
          .aux_cipher = "cbc(aes)",
          .cksum_name = "hmac(sha1)",
          .import_ctx = gss_krb5_import_ctx_v2,
-         .mk_key = gss_krb5_aes_make_key,
+         .derive_key = krb5_derive_key_v2,
          .encrypt = gss_krb5_aes_encrypt,
          .decrypt = gss_krb5_aes_decrypt,
 
@@ -358,16 +357,6 @@ context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key)
        return cp;
 }
 
-static inline void
-set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed)
-{
-       cdata[0] = (usage>>24)&0xff;
-       cdata[1] = (usage>>16)&0xff;
-       cdata[2] = (usage>>8)&0xff;
-       cdata[3] = usage&0xff;
-       cdata[4] = seed;
-}
-
 #if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
 static int
 gss_krb5_import_ctx_des(struct krb5_ctx *ctx, gfp_t gfp_mask)
@@ -378,16 +367,10 @@ gss_krb5_import_ctx_des(struct krb5_ctx *ctx, gfp_t gfp_mask)
 static int
 gss_krb5_import_ctx_v1(struct krb5_ctx *ctx, gfp_t gfp_mask)
 {
-       struct xdr_netobj c, keyin, keyout;
-       u8 cdata[GSS_KRB5_K5CLENGTH];
-       u32 err;
-
-       c.len = GSS_KRB5_K5CLENGTH;
-       c.data = cdata;
+       struct xdr_netobj keyin, keyout;
 
        keyin.data = ctx->Ksess;
        keyin.len = ctx->gk5e->keylength;
-       keyout.len = ctx->gk5e->keylength;
 
        /* seq uses the raw key */
        ctx->seq = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
@@ -401,14 +384,11 @@ gss_krb5_import_ctx_v1(struct krb5_ctx *ctx, gfp_t gfp_mask)
                goto out_free_seq;
 
        /* derive cksum */
-       set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM);
        keyout.data = ctx->cksum;
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err) {
-               dprintk("%s: Error %d deriving cksum key\n",
-                       __func__, err);
+       keyout.len = ctx->gk5e->keylength;
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_SIGN,
+                           KEY_USAGE_SEED_CHECKSUM, gfp_mask))
                goto out_free_enc;
-       }
 
        return 0;
 
@@ -441,11 +421,6 @@ gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
 static int
 gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
 {
-       u8 cdata[GSS_KRB5_K5CLENGTH];
-       struct xdr_netobj c = {
-               .len    = sizeof(cdata),
-               .data   = cdata,
-       };
        struct xdr_netobj keyin = {
                .len    = ctx->gk5e->keylength,
                .data   = ctx->Ksess,
@@ -453,7 +428,6 @@ gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
        struct xdr_netobj keyout;
        int ret = -EINVAL;
        void *subkey;
-       u32 err;
 
        subkey = kmalloc(ctx->gk5e->keylength, gfp_mask);
        if (!subkey)
@@ -462,13 +436,9 @@ gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
        keyout.data = subkey;
 
        /* initiator seal encryption */
-       set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err) {
-               dprintk("%s: Error %d deriving initiator_seal key\n",
-                       __func__, err);
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
+                           KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
                goto out;
-       }
        ctx->initiator_enc = context_v2_alloc_cipher(ctx,
                                                     ctx->gk5e->encrypt_name,
                                                     subkey);
@@ -483,13 +453,9 @@ gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
        }
 
        /* acceptor seal encryption */
-       set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err) {
-               dprintk("%s: Error %d deriving acceptor_seal key\n",
-                       __func__, err);
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
+                           KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
                goto out_free;
-       }
        ctx->acceptor_enc = context_v2_alloc_cipher(ctx,
                                                    ctx->gk5e->encrypt_name,
                                                    subkey);
@@ -504,36 +470,32 @@ gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
        }
 
        /* initiator sign checksum */
-       set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err)
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SIGN,
+                           KEY_USAGE_SEED_CHECKSUM, gfp_mask))
                goto out_free;
        ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
        if (ctx->initiator_sign == NULL)
                goto out_free;
 
        /* acceptor sign checksum */
-       set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err)
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SIGN,
+                           KEY_USAGE_SEED_CHECKSUM, gfp_mask))
                goto out_free;
        ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
        if (ctx->acceptor_sign == NULL)
                goto out_free;
 
        /* initiator seal integrity */
-       set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err)
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
+                           KEY_USAGE_SEED_INTEGRITY, gfp_mask))
                goto out_free;
        ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
        if (ctx->initiator_integ == NULL)
                goto out_free;
 
        /* acceptor seal integrity */
-       set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
-       err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
-       if (err)
+       if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
+                           KEY_USAGE_SEED_INTEGRITY, gfp_mask))
                goto out_free;
        ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
        if (ctx->acceptor_integ == NULL)