12573a464SChuck Lever // SPDX-License-Identifier: BSD-3-Clause
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/net/sunrpc/gss_krb5_mech.c
41da177e4SLinus Torvalds  *
581d4a433SKevin Coffman  *  Copyright (c) 2001-2008 The Regents of the University of Michigan.
61da177e4SLinus Torvalds  *  All rights reserved.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *  Andy Adamson <andros@umich.edu>
91da177e4SLinus Torvalds  *  J. Bruce Fields <bfields@umich.edu>
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
123b5cf20cSHerbert Xu #include <crypto/hash.h>
133b5cf20cSHerbert Xu #include <crypto/skcipher.h>
14378c6697SHerbert Xu #include <linux/err.h>
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/init.h>
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/sunrpc/auth.h>
201da177e4SLinus Torvalds #include <linux/sunrpc/gss_krb5.h>
211da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
22b084f598SJ. Bruce Fields #include <linux/sunrpc/gss_krb5_enctypes.h>
231da177e4SLinus Torvalds 
24ba6dfce4SDave Wysochanski #include "auth_gss_internal.h"
25*e01b2c79SChuck Lever #include "gss_krb5_internal.h"
26ba6dfce4SDave Wysochanski 
27f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
281da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
291da177e4SLinus Torvalds #endif
301da177e4SLinus Torvalds 
3147d84807SKevin Coffman static struct gss_api_mech gss_kerberos_mech;	/* forward declaration */
3247d84807SKevin Coffman 
3381d4a433SKevin Coffman static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
34fe9a2705SChuck Lever #ifndef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES
3581d4a433SKevin Coffman 	/*
3681d4a433SKevin Coffman 	 * DES (All DES enctypes are mapped to the same gss functionality)
3781d4a433SKevin Coffman 	 */
3881d4a433SKevin Coffman 	{
3981d4a433SKevin Coffman 	  .etype = ENCTYPE_DES_CBC_RAW,
4081d4a433SKevin Coffman 	  .ctype = CKSUMTYPE_RSA_MD5,
4181d4a433SKevin Coffman 	  .name = "des-cbc-crc",
4281d4a433SKevin Coffman 	  .encrypt_name = "cbc(des)",
4381d4a433SKevin Coffman 	  .cksum_name = "md5",
4481d4a433SKevin Coffman 	  .encrypt = krb5_encrypt,
4581d4a433SKevin Coffman 	  .decrypt = krb5_decrypt,
464891f2d0SKevin Coffman 	  .mk_key = NULL,
47*e01b2c79SChuck Lever 	  .get_mic = gss_krb5_get_mic_v1,
48*e01b2c79SChuck Lever 	  .verify_mic = gss_krb5_verify_mic_v1,
49*e01b2c79SChuck Lever 	  .wrap = gss_krb5_wrap_v1,
50*e01b2c79SChuck Lever 	  .unwrap = gss_krb5_unwrap_v1,
5181d4a433SKevin Coffman 	  .signalg = SGN_ALG_DES_MAC_MD5,
5281d4a433SKevin Coffman 	  .sealalg = SEAL_ALG_DES,
5381d4a433SKevin Coffman 	  .keybytes = 7,
5481d4a433SKevin Coffman 	  .keylength = 8,
5581d4a433SKevin Coffman 	  .cksumlength = 8,
56e1f6c07bSKevin Coffman 	  .keyed_cksum = 0,
5781d4a433SKevin Coffman 	},
58fe9a2705SChuck Lever #endif	/* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */
59958142e9SKevin Coffman 	/*
60958142e9SKevin Coffman 	 * 3DES
61958142e9SKevin Coffman 	 */
62958142e9SKevin Coffman 	{
63958142e9SKevin Coffman 	  .etype = ENCTYPE_DES3_CBC_RAW,
64958142e9SKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_DES3,
65958142e9SKevin Coffman 	  .name = "des3-hmac-sha1",
66958142e9SKevin Coffman 	  .encrypt_name = "cbc(des3_ede)",
67958142e9SKevin Coffman 	  .cksum_name = "hmac(sha1)",
68958142e9SKevin Coffman 	  .encrypt = krb5_encrypt,
69958142e9SKevin Coffman 	  .decrypt = krb5_decrypt,
70958142e9SKevin Coffman 	  .mk_key = gss_krb5_des3_make_key,
71*e01b2c79SChuck Lever 	  .get_mic = gss_krb5_get_mic_v1,
72*e01b2c79SChuck Lever 	  .verify_mic = gss_krb5_verify_mic_v1,
73*e01b2c79SChuck Lever 	  .wrap = gss_krb5_wrap_v1,
74*e01b2c79SChuck Lever 	  .unwrap = gss_krb5_unwrap_v1,
75958142e9SKevin Coffman 	  .signalg = SGN_ALG_HMAC_SHA1_DES3_KD,
76958142e9SKevin Coffman 	  .sealalg = SEAL_ALG_DES3KD,
77958142e9SKevin Coffman 	  .keybytes = 21,
78958142e9SKevin Coffman 	  .keylength = 24,
79958142e9SKevin Coffman 	  .cksumlength = 20,
80958142e9SKevin Coffman 	  .keyed_cksum = 1,
81958142e9SKevin Coffman 	},
82934a95aaSKevin Coffman 	/*
83934a95aaSKevin Coffman 	 * AES128
84934a95aaSKevin Coffman 	 */
85934a95aaSKevin Coffman 	{
86934a95aaSKevin Coffman 	  .etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
87934a95aaSKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_96_AES128,
88934a95aaSKevin Coffman 	  .name = "aes128-cts",
89934a95aaSKevin Coffman 	  .encrypt_name = "cts(cbc(aes))",
907989a4f4SChuck Lever 	  .aux_cipher = "cbc(aes)",
91934a95aaSKevin Coffman 	  .cksum_name = "hmac(sha1)",
92934a95aaSKevin Coffman 	  .encrypt = krb5_encrypt,
93934a95aaSKevin Coffman 	  .decrypt = krb5_decrypt,
94934a95aaSKevin Coffman 	  .mk_key = gss_krb5_aes_make_key,
95934a95aaSKevin Coffman 	  .encrypt_v2 = gss_krb5_aes_encrypt,
96934a95aaSKevin Coffman 	  .decrypt_v2 = gss_krb5_aes_decrypt,
97*e01b2c79SChuck Lever 
98*e01b2c79SChuck Lever 	  .get_mic = gss_krb5_get_mic_v2,
99*e01b2c79SChuck Lever 	  .verify_mic = gss_krb5_verify_mic_v2,
100*e01b2c79SChuck Lever 	  .wrap = gss_krb5_wrap_v2,
101*e01b2c79SChuck Lever 	  .unwrap = gss_krb5_unwrap_v2,
102*e01b2c79SChuck Lever 
103934a95aaSKevin Coffman 	  .signalg = -1,
104934a95aaSKevin Coffman 	  .sealalg = -1,
105934a95aaSKevin Coffman 	  .keybytes = 16,
106934a95aaSKevin Coffman 	  .keylength = 16,
107934a95aaSKevin Coffman 	  .cksumlength = 12,
108934a95aaSKevin Coffman 	  .keyed_cksum = 1,
109934a95aaSKevin Coffman 	},
110934a95aaSKevin Coffman 	/*
111934a95aaSKevin Coffman 	 * AES256
112934a95aaSKevin Coffman 	 */
113934a95aaSKevin Coffman 	{
114934a95aaSKevin Coffman 	  .etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
115934a95aaSKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_96_AES256,
116934a95aaSKevin Coffman 	  .name = "aes256-cts",
117934a95aaSKevin Coffman 	  .encrypt_name = "cts(cbc(aes))",
1187989a4f4SChuck Lever 	  .aux_cipher = "cbc(aes)",
119934a95aaSKevin Coffman 	  .cksum_name = "hmac(sha1)",
120934a95aaSKevin Coffman 	  .encrypt = krb5_encrypt,
121934a95aaSKevin Coffman 	  .decrypt = krb5_decrypt,
122934a95aaSKevin Coffman 	  .mk_key = gss_krb5_aes_make_key,
123934a95aaSKevin Coffman 	  .encrypt_v2 = gss_krb5_aes_encrypt,
124934a95aaSKevin Coffman 	  .decrypt_v2 = gss_krb5_aes_decrypt,
125*e01b2c79SChuck Lever 
126*e01b2c79SChuck Lever 	  .get_mic = gss_krb5_get_mic_v2,
127*e01b2c79SChuck Lever 	  .verify_mic = gss_krb5_verify_mic_v2,
128*e01b2c79SChuck Lever 	  .wrap = gss_krb5_wrap_v2,
129*e01b2c79SChuck Lever 	  .unwrap = gss_krb5_unwrap_v2,
130*e01b2c79SChuck Lever 
131934a95aaSKevin Coffman 	  .signalg = -1,
132934a95aaSKevin Coffman 	  .sealalg = -1,
133934a95aaSKevin Coffman 	  .keybytes = 32,
134934a95aaSKevin Coffman 	  .keylength = 32,
135934a95aaSKevin Coffman 	  .cksumlength = 12,
136934a95aaSKevin Coffman 	  .keyed_cksum = 1,
137934a95aaSKevin Coffman 	},
13881d4a433SKevin Coffman };
13981d4a433SKevin Coffman 
14081d4a433SKevin Coffman static const int num_supported_enctypes =
14181d4a433SKevin Coffman 	ARRAY_SIZE(supported_gss_krb5_enctypes);
14281d4a433SKevin Coffman 
14381d4a433SKevin Coffman static int
14481d4a433SKevin Coffman supported_gss_krb5_enctype(int etype)
14581d4a433SKevin Coffman {
14681d4a433SKevin Coffman 	int i;
14781d4a433SKevin Coffman 	for (i = 0; i < num_supported_enctypes; i++)
14881d4a433SKevin Coffman 		if (supported_gss_krb5_enctypes[i].etype == etype)
14981d4a433SKevin Coffman 			return 1;
15081d4a433SKevin Coffman 	return 0;
15181d4a433SKevin Coffman }
15281d4a433SKevin Coffman 
15381d4a433SKevin Coffman static const struct gss_krb5_enctype *
15481d4a433SKevin Coffman get_gss_krb5_enctype(int etype)
15581d4a433SKevin Coffman {
15681d4a433SKevin Coffman 	int i;
15781d4a433SKevin Coffman 	for (i = 0; i < num_supported_enctypes; i++)
15881d4a433SKevin Coffman 		if (supported_gss_krb5_enctypes[i].etype == etype)
15981d4a433SKevin Coffman 			return &supported_gss_krb5_enctypes[i];
16081d4a433SKevin Coffman 	return NULL;
16181d4a433SKevin Coffman }
16281d4a433SKevin Coffman 
1631da177e4SLinus Torvalds static inline const void *
16481d4a433SKevin Coffman get_key(const void *p, const void *end,
165e9e575b8SKees Cook 	struct krb5_ctx *ctx, struct crypto_sync_skcipher **res)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds 	struct xdr_netobj	key;
168378c6697SHerbert Xu 	int			alg;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &alg, sizeof(alg));
1711da177e4SLinus Torvalds 	if (IS_ERR(p))
1721da177e4SLinus Torvalds 		goto out_err;
17381d4a433SKevin Coffman 
17481d4a433SKevin Coffman 	switch (alg) {
17581d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_CRC:
17681d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_MD4:
17781d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_MD5:
17881d4a433SKevin Coffman 		/* Map all these key types to ENCTYPE_DES_CBC_RAW */
17981d4a433SKevin Coffman 		alg = ENCTYPE_DES_CBC_RAW;
18081d4a433SKevin Coffman 		break;
18181d4a433SKevin Coffman 	}
18281d4a433SKevin Coffman 
18381d4a433SKevin Coffman 	if (!supported_gss_krb5_enctype(alg)) {
18481d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: unsupported "
18581d4a433SKevin Coffman 			"encryption key algorithm %d\n", alg);
186ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
18781d4a433SKevin Coffman 		goto out_err;
18881d4a433SKevin Coffman 	}
1891da177e4SLinus Torvalds 	p = simple_get_netobj(p, end, &key);
1901da177e4SLinus Torvalds 	if (IS_ERR(p))
1911da177e4SLinus Torvalds 		goto out_err;
1921da177e4SLinus Torvalds 
193e9e575b8SKees Cook 	*res = crypto_alloc_sync_skcipher(ctx->gk5e->encrypt_name, 0, 0);
194378c6697SHerbert Xu 	if (IS_ERR(*res)) {
19581d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: unable to initialize "
19681d4a433SKevin Coffman 			"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
197378c6697SHerbert Xu 		*res = NULL;
1981da177e4SLinus Torvalds 		goto out_err_free_key;
1999e56904eSJ. Bruce Fields 	}
200e9e575b8SKees Cook 	if (crypto_sync_skcipher_setkey(*res, key.data, key.len)) {
20181d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: error setting key for "
20281d4a433SKevin Coffman 			"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
2031da177e4SLinus Torvalds 		goto out_err_free_tfm;
2049e56904eSJ. Bruce Fields 	}
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	kfree(key.data);
2071da177e4SLinus Torvalds 	return p;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds out_err_free_tfm:
210e9e575b8SKees Cook 	crypto_free_sync_skcipher(*res);
2111da177e4SLinus Torvalds out_err_free_key:
2121da177e4SLinus Torvalds 	kfree(key.data);
2131da177e4SLinus Torvalds 	p = ERR_PTR(-EINVAL);
2141da177e4SLinus Torvalds out_err:
2151da177e4SLinus Torvalds 	return p;
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds static int
219a8cc1cb7SKevin Coffman gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx)
2201da177e4SLinus Torvalds {
221c3be6577SPaul Burton 	u32 seq_send;
222e678e06bSJ. Bruce Fields 	int tmp;
223294ec5b8SArnd Bergmann 	u32 time32;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate));
2261da177e4SLinus Torvalds 	if (IS_ERR(p))
227a8cc1cb7SKevin Coffman 		goto out_err;
228a8cc1cb7SKevin Coffman 
229a8cc1cb7SKevin Coffman 	/* Old format supports only DES!  Any other enctype uses new format */
2301ac3719aSKevin Coffman 	ctx->enctype = ENCTYPE_DES_CBC_RAW;
231a8cc1cb7SKevin Coffman 
23281d4a433SKevin Coffman 	ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
233ce8477e1SBian Naimeng 	if (ctx->gk5e == NULL) {
234ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
23581d4a433SKevin Coffman 		goto out_err;
236ce8477e1SBian Naimeng 	}
23781d4a433SKevin Coffman 
238717757adSJ. Bruce Fields 	/* The downcall format was designed before we completely understood
239717757adSJ. Bruce Fields 	 * the uses of the context fields; so it includes some stuff we
240717757adSJ. Bruce Fields 	 * just give some minimal sanity-checking, and some we ignore
241717757adSJ. Bruce Fields 	 * completely (like the next twenty bytes): */
242ce8477e1SBian Naimeng 	if (unlikely(p + 20 > end || p + 20 < p)) {
243ce8477e1SBian Naimeng 		p = ERR_PTR(-EFAULT);
244a8cc1cb7SKevin Coffman 		goto out_err;
245ce8477e1SBian Naimeng 	}
246717757adSJ. Bruce Fields 	p += 20;
247e678e06bSJ. Bruce Fields 	p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
2481da177e4SLinus Torvalds 	if (IS_ERR(p))
249a8cc1cb7SKevin Coffman 		goto out_err;
250ef338beeSKevin Coffman 	if (tmp != SGN_ALG_DES_MAC_MD5) {
251ef338beeSKevin Coffman 		p = ERR_PTR(-ENOSYS);
252a8cc1cb7SKevin Coffman 		goto out_err;
253ef338beeSKevin Coffman 	}
254d922a84aSJ. Bruce Fields 	p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
2551da177e4SLinus Torvalds 	if (IS_ERR(p))
256a8cc1cb7SKevin Coffman 		goto out_err;
257ef338beeSKevin Coffman 	if (tmp != SEAL_ALG_DES) {
258ef338beeSKevin Coffman 		p = ERR_PTR(-ENOSYS);
259a8cc1cb7SKevin Coffman 		goto out_err;
260ef338beeSKevin Coffman 	}
261294ec5b8SArnd Bergmann 	p = simple_get_bytes(p, end, &time32, sizeof(time32));
2621da177e4SLinus Torvalds 	if (IS_ERR(p))
263a8cc1cb7SKevin Coffman 		goto out_err;
264294ec5b8SArnd Bergmann 	/* unsigned 32-bit time overflows in year 2106 */
265294ec5b8SArnd Bergmann 	ctx->endtime = (time64_t)time32;
266c3be6577SPaul Burton 	p = simple_get_bytes(p, end, &seq_send, sizeof(seq_send));
2671da177e4SLinus Torvalds 	if (IS_ERR(p))
268a8cc1cb7SKevin Coffman 		goto out_err;
269c3be6577SPaul Burton 	atomic_set(&ctx->seq_send, seq_send);
2701da177e4SLinus Torvalds 	p = simple_get_netobj(p, end, &ctx->mech_used);
2711da177e4SLinus Torvalds 	if (IS_ERR(p))
272a8cc1cb7SKevin Coffman 		goto out_err;
27381d4a433SKevin Coffman 	p = get_key(p, end, ctx, &ctx->enc);
2741da177e4SLinus Torvalds 	if (IS_ERR(p))
2751da177e4SLinus Torvalds 		goto out_err_free_mech;
27681d4a433SKevin Coffman 	p = get_key(p, end, ctx, &ctx->seq);
2771da177e4SLinus Torvalds 	if (IS_ERR(p))
2781da177e4SLinus Torvalds 		goto out_err_free_key1;
2791da177e4SLinus Torvalds 	if (p != end) {
2801da177e4SLinus Torvalds 		p = ERR_PTR(-EFAULT);
2811da177e4SLinus Torvalds 		goto out_err_free_key2;
2821da177e4SLinus Torvalds 	}
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	return 0;
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds out_err_free_key2:
287e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->seq);
2881da177e4SLinus Torvalds out_err_free_key1:
289e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->enc);
2901da177e4SLinus Torvalds out_err_free_mech:
2911da177e4SLinus Torvalds 	kfree(ctx->mech_used.data);
2921da177e4SLinus Torvalds out_err:
2931da177e4SLinus Torvalds 	return PTR_ERR(p);
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
296e9e575b8SKees Cook static struct crypto_sync_skcipher *
297934a95aaSKevin Coffman context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key)
29847d84807SKevin Coffman {
299e9e575b8SKees Cook 	struct crypto_sync_skcipher *cp;
30047d84807SKevin Coffman 
301e9e575b8SKees Cook 	cp = crypto_alloc_sync_skcipher(cname, 0, 0);
30247d84807SKevin Coffman 	if (IS_ERR(cp)) {
30347d84807SKevin Coffman 		dprintk("gss_kerberos_mech: unable to initialize "
304934a95aaSKevin Coffman 			"crypto algorithm %s\n", cname);
30547d84807SKevin Coffman 		return NULL;
30647d84807SKevin Coffman 	}
307e9e575b8SKees Cook 	if (crypto_sync_skcipher_setkey(cp, key, ctx->gk5e->keylength)) {
30847d84807SKevin Coffman 		dprintk("gss_kerberos_mech: error setting key for "
309934a95aaSKevin Coffman 			"crypto algorithm %s\n", cname);
310e9e575b8SKees Cook 		crypto_free_sync_skcipher(cp);
31147d84807SKevin Coffman 		return NULL;
31247d84807SKevin Coffman 	}
31347d84807SKevin Coffman 	return cp;
31447d84807SKevin Coffman }
31547d84807SKevin Coffman 
31647d84807SKevin Coffman static inline void
31747d84807SKevin Coffman set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed)
31847d84807SKevin Coffman {
31947d84807SKevin Coffman 	cdata[0] = (usage>>24)&0xff;
32047d84807SKevin Coffman 	cdata[1] = (usage>>16)&0xff;
32147d84807SKevin Coffman 	cdata[2] = (usage>>8)&0xff;
32247d84807SKevin Coffman 	cdata[3] = usage&0xff;
32347d84807SKevin Coffman 	cdata[4] = seed;
32447d84807SKevin Coffman }
32547d84807SKevin Coffman 
32647d84807SKevin Coffman static int
3271f4c86c0STrond Myklebust context_derive_keys_des3(struct krb5_ctx *ctx, gfp_t gfp_mask)
32847d84807SKevin Coffman {
32947d84807SKevin Coffman 	struct xdr_netobj c, keyin, keyout;
33047d84807SKevin Coffman 	u8 cdata[GSS_KRB5_K5CLENGTH];
33147d84807SKevin Coffman 	u32 err;
33247d84807SKevin Coffman 
33347d84807SKevin Coffman 	c.len = GSS_KRB5_K5CLENGTH;
33447d84807SKevin Coffman 	c.data = cdata;
33547d84807SKevin Coffman 
336fc263a91SKevin Coffman 	keyin.data = ctx->Ksess;
337fc263a91SKevin Coffman 	keyin.len = ctx->gk5e->keylength;
338fc263a91SKevin Coffman 	keyout.len = ctx->gk5e->keylength;
33947d84807SKevin Coffman 
34047d84807SKevin Coffman 	/* seq uses the raw key */
341934a95aaSKevin Coffman 	ctx->seq = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
342fc263a91SKevin Coffman 					   ctx->Ksess);
34347d84807SKevin Coffman 	if (ctx->seq == NULL)
34447d84807SKevin Coffman 		goto out_err;
34547d84807SKevin Coffman 
346934a95aaSKevin Coffman 	ctx->enc = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
347fc263a91SKevin Coffman 					   ctx->Ksess);
34847d84807SKevin Coffman 	if (ctx->enc == NULL)
34947d84807SKevin Coffman 		goto out_free_seq;
35047d84807SKevin Coffman 
35147d84807SKevin Coffman 	/* derive cksum */
35247d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM);
35347d84807SKevin Coffman 	keyout.data = ctx->cksum;
3541f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
35547d84807SKevin Coffman 	if (err) {
35647d84807SKevin Coffman 		dprintk("%s: Error %d deriving cksum key\n",
35747d84807SKevin Coffman 			__func__, err);
35847d84807SKevin Coffman 		goto out_free_enc;
35947d84807SKevin Coffman 	}
36047d84807SKevin Coffman 
36147d84807SKevin Coffman 	return 0;
36247d84807SKevin Coffman 
36347d84807SKevin Coffman out_free_enc:
364e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->enc);
36547d84807SKevin Coffman out_free_seq:
366e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->seq);
36747d84807SKevin Coffman out_err:
36847d84807SKevin Coffman 	return -EINVAL;
36947d84807SKevin Coffman }
37047d84807SKevin Coffman 
3712dbe0cacSChuck Lever static struct crypto_ahash *
3722dbe0cacSChuck Lever gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
3732dbe0cacSChuck Lever {
3742dbe0cacSChuck Lever 	struct crypto_ahash *tfm;
3752dbe0cacSChuck Lever 
3762dbe0cacSChuck Lever 	tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
3772dbe0cacSChuck Lever 	if (IS_ERR(tfm))
3782dbe0cacSChuck Lever 		return NULL;
3792dbe0cacSChuck Lever 	if (crypto_ahash_setkey(tfm, key->data, key->len)) {
3802dbe0cacSChuck Lever 		crypto_free_ahash(tfm);
3812dbe0cacSChuck Lever 		return NULL;
3822dbe0cacSChuck Lever 	}
3832dbe0cacSChuck Lever 	return tfm;
3842dbe0cacSChuck Lever }
3852dbe0cacSChuck Lever 
38647d84807SKevin Coffman static int
3871f4c86c0STrond Myklebust context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
38847d84807SKevin Coffman {
38947d84807SKevin Coffman 	u8 cdata[GSS_KRB5_K5CLENGTH];
3909f0b49f9SChuck Lever 	struct xdr_netobj c = {
3919f0b49f9SChuck Lever 		.len	= sizeof(cdata),
3929f0b49f9SChuck Lever 		.data	= cdata,
3939f0b49f9SChuck Lever 	};
3949f0b49f9SChuck Lever 	struct xdr_netobj keyin = {
3959f0b49f9SChuck Lever 		.len	= ctx->gk5e->keylength,
3969f0b49f9SChuck Lever 		.data	= ctx->Ksess,
3979f0b49f9SChuck Lever 	};
3989f0b49f9SChuck Lever 	struct xdr_netobj keyout;
3999f0b49f9SChuck Lever 	int ret = -EINVAL;
4009f0b49f9SChuck Lever 	void *subkey;
40147d84807SKevin Coffman 	u32 err;
40247d84807SKevin Coffman 
4039f0b49f9SChuck Lever 	subkey = kmalloc(ctx->gk5e->keylength, gfp_mask);
4049f0b49f9SChuck Lever 	if (!subkey)
4059f0b49f9SChuck Lever 		return -ENOMEM;
406fc263a91SKevin Coffman 	keyout.len = ctx->gk5e->keylength;
4079f0b49f9SChuck Lever 	keyout.data = subkey;
40847d84807SKevin Coffman 
40947d84807SKevin Coffman 	/* initiator seal encryption */
41047d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
4111f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
41247d84807SKevin Coffman 	if (err) {
41347d84807SKevin Coffman 		dprintk("%s: Error %d deriving initiator_seal key\n",
41447d84807SKevin Coffman 			__func__, err);
4159f0b49f9SChuck Lever 		goto out;
41647d84807SKevin Coffman 	}
417934a95aaSKevin Coffman 	ctx->initiator_enc = context_v2_alloc_cipher(ctx,
418934a95aaSKevin Coffman 						     ctx->gk5e->encrypt_name,
4199f0b49f9SChuck Lever 						     subkey);
42047d84807SKevin Coffman 	if (ctx->initiator_enc == NULL)
4219f0b49f9SChuck Lever 		goto out;
4227989a4f4SChuck Lever 	if (ctx->gk5e->aux_cipher) {
4237989a4f4SChuck Lever 		ctx->initiator_enc_aux =
4247989a4f4SChuck Lever 			context_v2_alloc_cipher(ctx, ctx->gk5e->aux_cipher,
4259f0b49f9SChuck Lever 						subkey);
4267989a4f4SChuck Lever 		if (ctx->initiator_enc_aux == NULL)
4277989a4f4SChuck Lever 			goto out_free;
4287989a4f4SChuck Lever 	}
42947d84807SKevin Coffman 
43047d84807SKevin Coffman 	/* acceptor seal encryption */
43147d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
4321f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
43347d84807SKevin Coffman 	if (err) {
43447d84807SKevin Coffman 		dprintk("%s: Error %d deriving acceptor_seal key\n",
43547d84807SKevin Coffman 			__func__, err);
4367989a4f4SChuck Lever 		goto out_free;
43747d84807SKevin Coffman 	}
438934a95aaSKevin Coffman 	ctx->acceptor_enc = context_v2_alloc_cipher(ctx,
439934a95aaSKevin Coffman 						    ctx->gk5e->encrypt_name,
4409f0b49f9SChuck Lever 						    subkey);
44147d84807SKevin Coffman 	if (ctx->acceptor_enc == NULL)
4427989a4f4SChuck Lever 		goto out_free;
4437989a4f4SChuck Lever 	if (ctx->gk5e->aux_cipher) {
4447989a4f4SChuck Lever 		ctx->acceptor_enc_aux =
4457989a4f4SChuck Lever 			context_v2_alloc_cipher(ctx, ctx->gk5e->aux_cipher,
4469f0b49f9SChuck Lever 						subkey);
4477989a4f4SChuck Lever 		if (ctx->acceptor_enc_aux == NULL)
4487989a4f4SChuck Lever 			goto out_free;
4497989a4f4SChuck Lever 	}
45047d84807SKevin Coffman 
45147d84807SKevin Coffman 	/* initiator sign checksum */
45247d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
4531f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4542dbe0cacSChuck Lever 	if (err)
4557989a4f4SChuck Lever 		goto out_free;
4562dbe0cacSChuck Lever 	ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
4572dbe0cacSChuck Lever 	if (ctx->initiator_sign == NULL)
4582dbe0cacSChuck Lever 		goto out_free;
45947d84807SKevin Coffman 
46047d84807SKevin Coffman 	/* acceptor sign checksum */
46147d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
4621f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4632dbe0cacSChuck Lever 	if (err)
4647989a4f4SChuck Lever 		goto out_free;
4652dbe0cacSChuck Lever 	ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
4662dbe0cacSChuck Lever 	if (ctx->acceptor_sign == NULL)
4672dbe0cacSChuck Lever 		goto out_free;
46847d84807SKevin Coffman 
46947d84807SKevin Coffman 	/* initiator seal integrity */
47047d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
4711f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4728270dbfcSChuck Lever 	if (err)
4737989a4f4SChuck Lever 		goto out_free;
4748270dbfcSChuck Lever 	ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
4758270dbfcSChuck Lever 	if (ctx->initiator_integ == NULL)
4768270dbfcSChuck Lever 		goto out_free;
47747d84807SKevin Coffman 
47847d84807SKevin Coffman 	/* acceptor seal integrity */
47947d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
4801f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4818270dbfcSChuck Lever 	if (err)
4827989a4f4SChuck Lever 		goto out_free;
4838270dbfcSChuck Lever 	ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
4848270dbfcSChuck Lever 	if (ctx->acceptor_integ == NULL)
4858270dbfcSChuck Lever 		goto out_free;
486934a95aaSKevin Coffman 
4879f0b49f9SChuck Lever 	ret = 0;
4889f0b49f9SChuck Lever out:
4899f0b49f9SChuck Lever 	kfree_sensitive(subkey);
4909f0b49f9SChuck Lever 	return ret;
49147d84807SKevin Coffman 
4927989a4f4SChuck Lever out_free:
4938270dbfcSChuck Lever 	crypto_free_ahash(ctx->acceptor_integ);
4948270dbfcSChuck Lever 	crypto_free_ahash(ctx->initiator_integ);
4952dbe0cacSChuck Lever 	crypto_free_ahash(ctx->acceptor_sign);
4962dbe0cacSChuck Lever 	crypto_free_ahash(ctx->initiator_sign);
4977989a4f4SChuck Lever 	crypto_free_sync_skcipher(ctx->acceptor_enc_aux);
498e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->acceptor_enc);
4997989a4f4SChuck Lever 	crypto_free_sync_skcipher(ctx->initiator_enc_aux);
500e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->initiator_enc);
5019f0b49f9SChuck Lever 	goto out;
50247d84807SKevin Coffman }
50347d84807SKevin Coffman 
50447d84807SKevin Coffman static int
5051f4c86c0STrond Myklebust gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
5061f4c86c0STrond Myklebust 		gfp_t gfp_mask)
50747d84807SKevin Coffman {
508c3be6577SPaul Burton 	u64 seq_send64;
50947d84807SKevin Coffman 	int keylen;
510294ec5b8SArnd Bergmann 	u32 time32;
51147d84807SKevin Coffman 
51247d84807SKevin Coffman 	p = simple_get_bytes(p, end, &ctx->flags, sizeof(ctx->flags));
51347d84807SKevin Coffman 	if (IS_ERR(p))
51447d84807SKevin Coffman 		goto out_err;
51547d84807SKevin Coffman 	ctx->initiate = ctx->flags & KRB5_CTX_FLAG_INITIATOR;
51647d84807SKevin Coffman 
517294ec5b8SArnd Bergmann 	p = simple_get_bytes(p, end, &time32, sizeof(time32));
51847d84807SKevin Coffman 	if (IS_ERR(p))
51947d84807SKevin Coffman 		goto out_err;
520294ec5b8SArnd Bergmann 	/* unsigned 32-bit time overflows in year 2106 */
521294ec5b8SArnd Bergmann 	ctx->endtime = (time64_t)time32;
522c3be6577SPaul Burton 	p = simple_get_bytes(p, end, &seq_send64, sizeof(seq_send64));
52347d84807SKevin Coffman 	if (IS_ERR(p))
52447d84807SKevin Coffman 		goto out_err;
525c3be6577SPaul Burton 	atomic64_set(&ctx->seq_send64, seq_send64);
52647d84807SKevin Coffman 	/* set seq_send for use by "older" enctypes */
527c3be6577SPaul Burton 	atomic_set(&ctx->seq_send, seq_send64);
528c3be6577SPaul Burton 	if (seq_send64 != atomic_read(&ctx->seq_send)) {
529c3be6577SPaul Burton 		dprintk("%s: seq_send64 %llx, seq_send %x overflow?\n", __func__,
530c3be6577SPaul Burton 			seq_send64, atomic_read(&ctx->seq_send));
531ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
53247d84807SKevin Coffman 		goto out_err;
53347d84807SKevin Coffman 	}
53447d84807SKevin Coffman 	p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
53547d84807SKevin Coffman 	if (IS_ERR(p))
53647d84807SKevin Coffman 		goto out_err;
537958142e9SKevin Coffman 	/* Map ENCTYPE_DES3_CBC_SHA1 to ENCTYPE_DES3_CBC_RAW */
538958142e9SKevin Coffman 	if (ctx->enctype == ENCTYPE_DES3_CBC_SHA1)
539958142e9SKevin Coffman 		ctx->enctype = ENCTYPE_DES3_CBC_RAW;
54047d84807SKevin Coffman 	ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
54147d84807SKevin Coffman 	if (ctx->gk5e == NULL) {
54247d84807SKevin Coffman 		dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
54347d84807SKevin Coffman 			ctx->enctype);
54447d84807SKevin Coffman 		p = ERR_PTR(-EINVAL);
54547d84807SKevin Coffman 		goto out_err;
54647d84807SKevin Coffman 	}
54747d84807SKevin Coffman 	keylen = ctx->gk5e->keylength;
54847d84807SKevin Coffman 
549fc263a91SKevin Coffman 	p = simple_get_bytes(p, end, ctx->Ksess, keylen);
55047d84807SKevin Coffman 	if (IS_ERR(p))
55147d84807SKevin Coffman 		goto out_err;
55247d84807SKevin Coffman 
55347d84807SKevin Coffman 	if (p != end) {
55447d84807SKevin Coffman 		p = ERR_PTR(-EINVAL);
55547d84807SKevin Coffman 		goto out_err;
55647d84807SKevin Coffman 	}
55747d84807SKevin Coffman 
55847d84807SKevin Coffman 	ctx->mech_used.data = kmemdup(gss_kerberos_mech.gm_oid.data,
5591f4c86c0STrond Myklebust 				      gss_kerberos_mech.gm_oid.len, gfp_mask);
56047d84807SKevin Coffman 	if (unlikely(ctx->mech_used.data == NULL)) {
56147d84807SKevin Coffman 		p = ERR_PTR(-ENOMEM);
56247d84807SKevin Coffman 		goto out_err;
56347d84807SKevin Coffman 	}
56447d84807SKevin Coffman 	ctx->mech_used.len = gss_kerberos_mech.gm_oid.len;
56547d84807SKevin Coffman 
56647d84807SKevin Coffman 	switch (ctx->enctype) {
56747d84807SKevin Coffman 	case ENCTYPE_DES3_CBC_RAW:
5681f4c86c0STrond Myklebust 		return context_derive_keys_des3(ctx, gfp_mask);
56947d84807SKevin Coffman 	case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
57047d84807SKevin Coffman 	case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
5711f4c86c0STrond Myklebust 		return context_derive_keys_new(ctx, gfp_mask);
57247d84807SKevin Coffman 	default:
57347d84807SKevin Coffman 		return -EINVAL;
57447d84807SKevin Coffman 	}
57547d84807SKevin Coffman 
57647d84807SKevin Coffman out_err:
57747d84807SKevin Coffman 	return PTR_ERR(p);
57847d84807SKevin Coffman }
57947d84807SKevin Coffman 
580a8cc1cb7SKevin Coffman static int
581*e01b2c79SChuck Lever gss_krb5_import_sec_context(const void *p, size_t len, struct gss_ctx *ctx_id,
582*e01b2c79SChuck Lever 			    time64_t *endtime, gfp_t gfp_mask)
583a8cc1cb7SKevin Coffman {
584a8cc1cb7SKevin Coffman 	const void *end = (const void *)((const char *)p + len);
585a8cc1cb7SKevin Coffman 	struct  krb5_ctx *ctx;
586a8cc1cb7SKevin Coffman 	int ret;
587a8cc1cb7SKevin Coffman 
5881f4c86c0STrond Myklebust 	ctx = kzalloc(sizeof(*ctx), gfp_mask);
589a8cc1cb7SKevin Coffman 	if (ctx == NULL)
590a8cc1cb7SKevin Coffman 		return -ENOMEM;
591a8cc1cb7SKevin Coffman 
592a8cc1cb7SKevin Coffman 	if (len == 85)
593a8cc1cb7SKevin Coffman 		ret = gss_import_v1_context(p, end, ctx);
594a8cc1cb7SKevin Coffman 	else
5951f4c86c0STrond Myklebust 		ret = gss_import_v2_context(p, end, ctx, gfp_mask);
59601c4e326SChuck Lever 	memzero_explicit(&ctx->Ksess, sizeof(ctx->Ksess));
5977f675ca7SChuck Lever 	if (ret) {
5987f675ca7SChuck Lever 		kfree(ctx);
5997f675ca7SChuck Lever 		return ret;
6007f675ca7SChuck Lever 	}
601a8cc1cb7SKevin Coffman 
602a8cc1cb7SKevin Coffman 	ctx_id->internal_ctx_id = ctx;
603400f26b5SSimo Sorce 	if (endtime)
604400f26b5SSimo Sorce 		*endtime = ctx->endtime;
6057f675ca7SChuck Lever 	return 0;
606a8cc1cb7SKevin Coffman }
607a8cc1cb7SKevin Coffman 
6081da177e4SLinus Torvalds static void
609*e01b2c79SChuck Lever gss_krb5_delete_sec_context(void *internal_ctx)
610*e01b2c79SChuck Lever {
6111da177e4SLinus Torvalds 	struct krb5_ctx *kctx = internal_ctx;
6121da177e4SLinus Torvalds 
613e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->seq);
614e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->enc);
615e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->acceptor_enc);
616e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->initiator_enc);
617e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
618e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->initiator_enc_aux);
6192dbe0cacSChuck Lever 	crypto_free_ahash(kctx->acceptor_sign);
6202dbe0cacSChuck Lever 	crypto_free_ahash(kctx->initiator_sign);
6218270dbfcSChuck Lever 	crypto_free_ahash(kctx->acceptor_integ);
6228270dbfcSChuck Lever 	crypto_free_ahash(kctx->initiator_integ);
6231da177e4SLinus Torvalds 	kfree(kctx->mech_used.data);
6241da177e4SLinus Torvalds 	kfree(kctx);
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds 
627*e01b2c79SChuck Lever /**
628*e01b2c79SChuck Lever  * gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism
629*e01b2c79SChuck Lever  * @gctx: GSS context
630*e01b2c79SChuck Lever  * @text: plaintext to checksum
631*e01b2c79SChuck Lever  * @token: buffer into which to write the computed checksum
632*e01b2c79SChuck Lever  *
633*e01b2c79SChuck Lever  * Return values:
634*e01b2c79SChuck Lever  *    %GSS_S_COMPLETE - success, and @token is filled in
635*e01b2c79SChuck Lever  *    %GSS_S_FAILURE - checksum could not be generated
636*e01b2c79SChuck Lever  *    %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
637*e01b2c79SChuck Lever  */
638*e01b2c79SChuck Lever static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text,
639*e01b2c79SChuck Lever 			    struct xdr_netobj *token)
640*e01b2c79SChuck Lever {
641*e01b2c79SChuck Lever 	struct krb5_ctx *kctx = gctx->internal_ctx_id;
642*e01b2c79SChuck Lever 
643*e01b2c79SChuck Lever 	return kctx->gk5e->get_mic(kctx, text, token);
644*e01b2c79SChuck Lever }
645*e01b2c79SChuck Lever 
646*e01b2c79SChuck Lever /**
647*e01b2c79SChuck Lever  * gss_krb5_verify_mic - verify_mic for the Kerberos GSS mechanism
648*e01b2c79SChuck Lever  * @gctx: GSS context
649*e01b2c79SChuck Lever  * @message_buffer: plaintext to check
650*e01b2c79SChuck Lever  * @read_token: received checksum to check
651*e01b2c79SChuck Lever  *
652*e01b2c79SChuck Lever  * Return values:
653*e01b2c79SChuck Lever  *    %GSS_S_COMPLETE - computed and received checksums match
654*e01b2c79SChuck Lever  *    %GSS_S_DEFECTIVE_TOKEN - received checksum is not valid
655*e01b2c79SChuck Lever  *    %GSS_S_BAD_SIG - computed and received checksums do not match
656*e01b2c79SChuck Lever  *    %GSS_S_FAILURE - received checksum could not be checked
657*e01b2c79SChuck Lever  *    %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
658*e01b2c79SChuck Lever  */
659*e01b2c79SChuck Lever static u32 gss_krb5_verify_mic(struct gss_ctx *gctx,
660*e01b2c79SChuck Lever 			       struct xdr_buf *message_buffer,
661*e01b2c79SChuck Lever 			       struct xdr_netobj *read_token)
662*e01b2c79SChuck Lever {
663*e01b2c79SChuck Lever 	struct krb5_ctx *kctx = gctx->internal_ctx_id;
664*e01b2c79SChuck Lever 
665*e01b2c79SChuck Lever 	return kctx->gk5e->verify_mic(kctx, message_buffer, read_token);
666*e01b2c79SChuck Lever }
667*e01b2c79SChuck Lever 
668*e01b2c79SChuck Lever /**
669*e01b2c79SChuck Lever  * gss_krb5_wrap - gss_wrap for the Kerberos GSS mechanism
670*e01b2c79SChuck Lever  * @gctx: initialized GSS context
671*e01b2c79SChuck Lever  * @offset: byte offset in @buf to start writing the cipher text
672*e01b2c79SChuck Lever  * @buf: OUT: send buffer
673*e01b2c79SChuck Lever  * @pages: plaintext to wrap
674*e01b2c79SChuck Lever  *
675*e01b2c79SChuck Lever  * Return values:
676*e01b2c79SChuck Lever  *    %GSS_S_COMPLETE - success, @buf has been updated
677*e01b2c79SChuck Lever  *    %GSS_S_FAILURE - @buf could not be wrapped
678*e01b2c79SChuck Lever  *    %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
679*e01b2c79SChuck Lever  */
680*e01b2c79SChuck Lever static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset,
681*e01b2c79SChuck Lever 			 struct xdr_buf *buf, struct page **pages)
682*e01b2c79SChuck Lever {
683*e01b2c79SChuck Lever 	struct krb5_ctx	*kctx = gctx->internal_ctx_id;
684*e01b2c79SChuck Lever 
685*e01b2c79SChuck Lever 	return kctx->gk5e->wrap(kctx, offset, buf, pages);
686*e01b2c79SChuck Lever }
687*e01b2c79SChuck Lever 
688*e01b2c79SChuck Lever /**
689*e01b2c79SChuck Lever  * gss_krb5_unwrap - gss_unwrap for the Kerberos GSS mechanism
690*e01b2c79SChuck Lever  * @gctx: initialized GSS context
691*e01b2c79SChuck Lever  * @offset: starting byte offset into @buf
692*e01b2c79SChuck Lever  * @len: size of ciphertext to unwrap
693*e01b2c79SChuck Lever  * @buf: ciphertext to unwrap
694*e01b2c79SChuck Lever  *
695*e01b2c79SChuck Lever  * Return values:
696*e01b2c79SChuck Lever  *    %GSS_S_COMPLETE - success, @buf has been updated
697*e01b2c79SChuck Lever  *    %GSS_S_DEFECTIVE_TOKEN - received blob is not valid
698*e01b2c79SChuck Lever  *    %GSS_S_BAD_SIG - computed and received checksums do not match
699*e01b2c79SChuck Lever  *    %GSS_S_FAILURE - @buf could not be unwrapped
700*e01b2c79SChuck Lever  *    %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
701*e01b2c79SChuck Lever  */
702*e01b2c79SChuck Lever static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset,
703*e01b2c79SChuck Lever 			   int len, struct xdr_buf *buf)
704*e01b2c79SChuck Lever {
705*e01b2c79SChuck Lever 	struct krb5_ctx	*kctx = gctx->internal_ctx_id;
706*e01b2c79SChuck Lever 
707*e01b2c79SChuck Lever 	return kctx->gk5e->unwrap(kctx, offset, len, buf,
708*e01b2c79SChuck Lever 				  &gctx->slack, &gctx->align);
709*e01b2c79SChuck Lever }
710*e01b2c79SChuck Lever 
711f1c0a861STrond Myklebust static const struct gss_api_ops gss_kerberos_ops = {
712*e01b2c79SChuck Lever 	.gss_import_sec_context	= gss_krb5_import_sec_context,
713*e01b2c79SChuck Lever 	.gss_get_mic		= gss_krb5_get_mic,
714*e01b2c79SChuck Lever 	.gss_verify_mic		= gss_krb5_verify_mic,
715*e01b2c79SChuck Lever 	.gss_wrap		= gss_krb5_wrap,
716*e01b2c79SChuck Lever 	.gss_unwrap		= gss_krb5_unwrap,
717*e01b2c79SChuck Lever 	.gss_delete_sec_context	= gss_krb5_delete_sec_context,
7181da177e4SLinus Torvalds };
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds static struct pf_desc gss_kerberos_pfs[] = {
7211da177e4SLinus Torvalds 	[0] = {
7221da177e4SLinus Torvalds 		.pseudoflavor = RPC_AUTH_GSS_KRB5,
72383523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
7241da177e4SLinus Torvalds 		.service = RPC_GSS_SVC_NONE,
7251da177e4SLinus Torvalds 		.name = "krb5",
7261da177e4SLinus Torvalds 	},
7271da177e4SLinus Torvalds 	[1] = {
7281da177e4SLinus Torvalds 		.pseudoflavor = RPC_AUTH_GSS_KRB5I,
72983523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
7301da177e4SLinus Torvalds 		.service = RPC_GSS_SVC_INTEGRITY,
7311da177e4SLinus Torvalds 		.name = "krb5i",
73265b80179SChuck Lever 		.datatouch = true,
7331da177e4SLinus Torvalds 	},
73414ae162cSJ. Bruce Fields 	[2] = {
73514ae162cSJ. Bruce Fields 		.pseudoflavor = RPC_AUTH_GSS_KRB5P,
73683523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
73714ae162cSJ. Bruce Fields 		.service = RPC_GSS_SVC_PRIVACY,
73814ae162cSJ. Bruce Fields 		.name = "krb5p",
73965b80179SChuck Lever 		.datatouch = true,
74014ae162cSJ. Bruce Fields 	},
7411da177e4SLinus Torvalds };
7421da177e4SLinus Torvalds 
743058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5");
744058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5i");
745058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5p");
746058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390003");
747058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390004");
748058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390005");
749f783288fSChuck Lever MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
750058c5c99SJ. Bruce Fields 
7511da177e4SLinus Torvalds static struct gss_api_mech gss_kerberos_mech = {
7521da177e4SLinus Torvalds 	.gm_name	= "krb5",
7531da177e4SLinus Torvalds 	.gm_owner	= THIS_MODULE,
754fb15b26fSChuck Lever 	.gm_oid		= { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
7551da177e4SLinus Torvalds 	.gm_ops		= &gss_kerberos_ops,
7561da177e4SLinus Torvalds 	.gm_pf_num	= ARRAY_SIZE(gss_kerberos_pfs),
7571da177e4SLinus Torvalds 	.gm_pfs		= gss_kerberos_pfs,
758b084f598SJ. Bruce Fields 	.gm_upcall_enctypes = KRB5_SUPPORTED_ENCTYPES,
7591da177e4SLinus Torvalds };
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds static int __init init_kerberos_module(void)
7621da177e4SLinus Torvalds {
7631da177e4SLinus Torvalds 	int status;
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	status = gss_mech_register(&gss_kerberos_mech);
7661da177e4SLinus Torvalds 	if (status)
7671da177e4SLinus Torvalds 		printk("Failed to register kerberos gss mechanism!\n");
7681da177e4SLinus Torvalds 	return status;
7691da177e4SLinus Torvalds }
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds static void __exit cleanup_kerberos_module(void)
7721da177e4SLinus Torvalds {
7731da177e4SLinus Torvalds 	gss_mech_unregister(&gss_kerberos_mech);
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds MODULE_LICENSE("GPL");
7771da177e4SLinus Torvalds module_init(init_kerberos_module);
7781da177e4SLinus Torvalds module_exit(cleanup_kerberos_module);
779