Commit 2691a27d authored by Chuck Lever's avatar Chuck Lever
Browse files

SUNRPC: Hoist KDF into struct gss_krb5_enctype



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: default avatarScott Mayhew <smayhew@redhat.com>
Reviewed-by: default avatarSimo Sorce <simo@redhat.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent ae6ad5d0
Loading
Loading
Loading
Loading
+5 −20
Original line number Diff line number Diff line
@@ -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 */
+46 −0
Original line number Diff line number Diff line
@@ -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,
+86 −45
Original line number Diff line number Diff line
@@ -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,
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 @@ u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
	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,
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;
}
+19 −57
Original line number Diff line number Diff line
@@ -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)