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"
25ba6dfce4SDave Wysochanski 
26f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
271da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
281da177e4SLinus Torvalds #endif
291da177e4SLinus Torvalds 
3047d84807SKevin Coffman static struct gss_api_mech gss_kerberos_mech;	/* forward declaration */
3147d84807SKevin Coffman 
3281d4a433SKevin Coffman static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
33fe9a2705SChuck Lever #ifndef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES
3481d4a433SKevin Coffman 	/*
3581d4a433SKevin Coffman 	 * DES (All DES enctypes are mapped to the same gss functionality)
3681d4a433SKevin Coffman 	 */
3781d4a433SKevin Coffman 	{
3881d4a433SKevin Coffman 	  .etype = ENCTYPE_DES_CBC_RAW,
3981d4a433SKevin Coffman 	  .ctype = CKSUMTYPE_RSA_MD5,
4081d4a433SKevin Coffman 	  .name = "des-cbc-crc",
4181d4a433SKevin Coffman 	  .encrypt_name = "cbc(des)",
4281d4a433SKevin Coffman 	  .cksum_name = "md5",
4381d4a433SKevin Coffman 	  .encrypt = krb5_encrypt,
4481d4a433SKevin Coffman 	  .decrypt = krb5_decrypt,
454891f2d0SKevin Coffman 	  .mk_key = NULL,
4681d4a433SKevin Coffman 	  .signalg = SGN_ALG_DES_MAC_MD5,
4781d4a433SKevin Coffman 	  .sealalg = SEAL_ALG_DES,
4881d4a433SKevin Coffman 	  .keybytes = 7,
4981d4a433SKevin Coffman 	  .keylength = 8,
5081d4a433SKevin Coffman 	  .cksumlength = 8,
51e1f6c07bSKevin Coffman 	  .keyed_cksum = 0,
5281d4a433SKevin Coffman 	},
53fe9a2705SChuck Lever #endif	/* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */
54958142e9SKevin Coffman 	/*
55958142e9SKevin Coffman 	 * 3DES
56958142e9SKevin Coffman 	 */
57958142e9SKevin Coffman 	{
58958142e9SKevin Coffman 	  .etype = ENCTYPE_DES3_CBC_RAW,
59958142e9SKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_DES3,
60958142e9SKevin Coffman 	  .name = "des3-hmac-sha1",
61958142e9SKevin Coffman 	  .encrypt_name = "cbc(des3_ede)",
62958142e9SKevin Coffman 	  .cksum_name = "hmac(sha1)",
63958142e9SKevin Coffman 	  .encrypt = krb5_encrypt,
64958142e9SKevin Coffman 	  .decrypt = krb5_decrypt,
65958142e9SKevin Coffman 	  .mk_key = gss_krb5_des3_make_key,
66958142e9SKevin Coffman 	  .signalg = SGN_ALG_HMAC_SHA1_DES3_KD,
67958142e9SKevin Coffman 	  .sealalg = SEAL_ALG_DES3KD,
68958142e9SKevin Coffman 	  .keybytes = 21,
69958142e9SKevin Coffman 	  .keylength = 24,
70958142e9SKevin Coffman 	  .cksumlength = 20,
71958142e9SKevin Coffman 	  .keyed_cksum = 1,
72958142e9SKevin Coffman 	},
73934a95aaSKevin Coffman 	/*
74934a95aaSKevin Coffman 	 * AES128
75934a95aaSKevin Coffman 	 */
76934a95aaSKevin Coffman 	{
77934a95aaSKevin Coffman 	  .etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
78934a95aaSKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_96_AES128,
79934a95aaSKevin Coffman 	  .name = "aes128-cts",
80934a95aaSKevin Coffman 	  .encrypt_name = "cts(cbc(aes))",
817989a4f4SChuck Lever 	  .aux_cipher = "cbc(aes)",
82934a95aaSKevin Coffman 	  .cksum_name = "hmac(sha1)",
83934a95aaSKevin Coffman 	  .encrypt = krb5_encrypt,
84934a95aaSKevin Coffman 	  .decrypt = krb5_decrypt,
85934a95aaSKevin Coffman 	  .mk_key = gss_krb5_aes_make_key,
86934a95aaSKevin Coffman 	  .encrypt_v2 = gss_krb5_aes_encrypt,
87934a95aaSKevin Coffman 	  .decrypt_v2 = gss_krb5_aes_decrypt,
88934a95aaSKevin Coffman 	  .signalg = -1,
89934a95aaSKevin Coffman 	  .sealalg = -1,
90934a95aaSKevin Coffman 	  .keybytes = 16,
91934a95aaSKevin Coffman 	  .keylength = 16,
92934a95aaSKevin Coffman 	  .cksumlength = 12,
93934a95aaSKevin Coffman 	  .keyed_cksum = 1,
94934a95aaSKevin Coffman 	},
95934a95aaSKevin Coffman 	/*
96934a95aaSKevin Coffman 	 * AES256
97934a95aaSKevin Coffman 	 */
98934a95aaSKevin Coffman 	{
99934a95aaSKevin Coffman 	  .etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
100934a95aaSKevin Coffman 	  .ctype = CKSUMTYPE_HMAC_SHA1_96_AES256,
101934a95aaSKevin Coffman 	  .name = "aes256-cts",
102934a95aaSKevin Coffman 	  .encrypt_name = "cts(cbc(aes))",
1037989a4f4SChuck Lever 	  .aux_cipher = "cbc(aes)",
104934a95aaSKevin Coffman 	  .cksum_name = "hmac(sha1)",
105934a95aaSKevin Coffman 	  .encrypt = krb5_encrypt,
106934a95aaSKevin Coffman 	  .decrypt = krb5_decrypt,
107934a95aaSKevin Coffman 	  .mk_key = gss_krb5_aes_make_key,
108934a95aaSKevin Coffman 	  .encrypt_v2 = gss_krb5_aes_encrypt,
109934a95aaSKevin Coffman 	  .decrypt_v2 = gss_krb5_aes_decrypt,
110934a95aaSKevin Coffman 	  .signalg = -1,
111934a95aaSKevin Coffman 	  .sealalg = -1,
112934a95aaSKevin Coffman 	  .keybytes = 32,
113934a95aaSKevin Coffman 	  .keylength = 32,
114934a95aaSKevin Coffman 	  .cksumlength = 12,
115934a95aaSKevin Coffman 	  .keyed_cksum = 1,
116934a95aaSKevin Coffman 	},
11781d4a433SKevin Coffman };
11881d4a433SKevin Coffman 
11981d4a433SKevin Coffman static const int num_supported_enctypes =
12081d4a433SKevin Coffman 	ARRAY_SIZE(supported_gss_krb5_enctypes);
12181d4a433SKevin Coffman 
12281d4a433SKevin Coffman static int
12381d4a433SKevin Coffman supported_gss_krb5_enctype(int etype)
12481d4a433SKevin Coffman {
12581d4a433SKevin Coffman 	int i;
12681d4a433SKevin Coffman 	for (i = 0; i < num_supported_enctypes; i++)
12781d4a433SKevin Coffman 		if (supported_gss_krb5_enctypes[i].etype == etype)
12881d4a433SKevin Coffman 			return 1;
12981d4a433SKevin Coffman 	return 0;
13081d4a433SKevin Coffman }
13181d4a433SKevin Coffman 
13281d4a433SKevin Coffman static const struct gss_krb5_enctype *
13381d4a433SKevin Coffman get_gss_krb5_enctype(int etype)
13481d4a433SKevin Coffman {
13581d4a433SKevin Coffman 	int i;
13681d4a433SKevin Coffman 	for (i = 0; i < num_supported_enctypes; i++)
13781d4a433SKevin Coffman 		if (supported_gss_krb5_enctypes[i].etype == etype)
13881d4a433SKevin Coffman 			return &supported_gss_krb5_enctypes[i];
13981d4a433SKevin Coffman 	return NULL;
14081d4a433SKevin Coffman }
14181d4a433SKevin Coffman 
1421da177e4SLinus Torvalds static inline const void *
14381d4a433SKevin Coffman get_key(const void *p, const void *end,
144e9e575b8SKees Cook 	struct krb5_ctx *ctx, struct crypto_sync_skcipher **res)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	struct xdr_netobj	key;
147378c6697SHerbert Xu 	int			alg;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &alg, sizeof(alg));
1501da177e4SLinus Torvalds 	if (IS_ERR(p))
1511da177e4SLinus Torvalds 		goto out_err;
15281d4a433SKevin Coffman 
15381d4a433SKevin Coffman 	switch (alg) {
15481d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_CRC:
15581d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_MD4:
15681d4a433SKevin Coffman 	case ENCTYPE_DES_CBC_MD5:
15781d4a433SKevin Coffman 		/* Map all these key types to ENCTYPE_DES_CBC_RAW */
15881d4a433SKevin Coffman 		alg = ENCTYPE_DES_CBC_RAW;
15981d4a433SKevin Coffman 		break;
16081d4a433SKevin Coffman 	}
16181d4a433SKevin Coffman 
16281d4a433SKevin Coffman 	if (!supported_gss_krb5_enctype(alg)) {
16381d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: unsupported "
16481d4a433SKevin Coffman 			"encryption key algorithm %d\n", alg);
165ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
16681d4a433SKevin Coffman 		goto out_err;
16781d4a433SKevin Coffman 	}
1681da177e4SLinus Torvalds 	p = simple_get_netobj(p, end, &key);
1691da177e4SLinus Torvalds 	if (IS_ERR(p))
1701da177e4SLinus Torvalds 		goto out_err;
1711da177e4SLinus Torvalds 
172e9e575b8SKees Cook 	*res = crypto_alloc_sync_skcipher(ctx->gk5e->encrypt_name, 0, 0);
173378c6697SHerbert Xu 	if (IS_ERR(*res)) {
17481d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: unable to initialize "
17581d4a433SKevin Coffman 			"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
176378c6697SHerbert Xu 		*res = NULL;
1771da177e4SLinus Torvalds 		goto out_err_free_key;
1789e56904eSJ. Bruce Fields 	}
179e9e575b8SKees Cook 	if (crypto_sync_skcipher_setkey(*res, key.data, key.len)) {
18081d4a433SKevin Coffman 		printk(KERN_WARNING "gss_kerberos_mech: error setting key for "
18181d4a433SKevin Coffman 			"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
1821da177e4SLinus Torvalds 		goto out_err_free_tfm;
1839e56904eSJ. Bruce Fields 	}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	kfree(key.data);
1861da177e4SLinus Torvalds 	return p;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds out_err_free_tfm:
189e9e575b8SKees Cook 	crypto_free_sync_skcipher(*res);
1901da177e4SLinus Torvalds out_err_free_key:
1911da177e4SLinus Torvalds 	kfree(key.data);
1921da177e4SLinus Torvalds 	p = ERR_PTR(-EINVAL);
1931da177e4SLinus Torvalds out_err:
1941da177e4SLinus Torvalds 	return p;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds static int
198a8cc1cb7SKevin Coffman gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx)
1991da177e4SLinus Torvalds {
200c3be6577SPaul Burton 	u32 seq_send;
201e678e06bSJ. Bruce Fields 	int tmp;
202294ec5b8SArnd Bergmann 	u32 time32;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate));
2051da177e4SLinus Torvalds 	if (IS_ERR(p))
206a8cc1cb7SKevin Coffman 		goto out_err;
207a8cc1cb7SKevin Coffman 
208a8cc1cb7SKevin Coffman 	/* Old format supports only DES!  Any other enctype uses new format */
2091ac3719aSKevin Coffman 	ctx->enctype = ENCTYPE_DES_CBC_RAW;
210a8cc1cb7SKevin Coffman 
21181d4a433SKevin Coffman 	ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
212ce8477e1SBian Naimeng 	if (ctx->gk5e == NULL) {
213ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
21481d4a433SKevin Coffman 		goto out_err;
215ce8477e1SBian Naimeng 	}
21681d4a433SKevin Coffman 
217717757adSJ. Bruce Fields 	/* The downcall format was designed before we completely understood
218717757adSJ. Bruce Fields 	 * the uses of the context fields; so it includes some stuff we
219717757adSJ. Bruce Fields 	 * just give some minimal sanity-checking, and some we ignore
220717757adSJ. Bruce Fields 	 * completely (like the next twenty bytes): */
221ce8477e1SBian Naimeng 	if (unlikely(p + 20 > end || p + 20 < p)) {
222ce8477e1SBian Naimeng 		p = ERR_PTR(-EFAULT);
223a8cc1cb7SKevin Coffman 		goto out_err;
224ce8477e1SBian Naimeng 	}
225717757adSJ. Bruce Fields 	p += 20;
226e678e06bSJ. Bruce Fields 	p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
2271da177e4SLinus Torvalds 	if (IS_ERR(p))
228a8cc1cb7SKevin Coffman 		goto out_err;
229ef338beeSKevin Coffman 	if (tmp != SGN_ALG_DES_MAC_MD5) {
230ef338beeSKevin Coffman 		p = ERR_PTR(-ENOSYS);
231a8cc1cb7SKevin Coffman 		goto out_err;
232ef338beeSKevin Coffman 	}
233d922a84aSJ. Bruce Fields 	p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
2341da177e4SLinus Torvalds 	if (IS_ERR(p))
235a8cc1cb7SKevin Coffman 		goto out_err;
236ef338beeSKevin Coffman 	if (tmp != SEAL_ALG_DES) {
237ef338beeSKevin Coffman 		p = ERR_PTR(-ENOSYS);
238a8cc1cb7SKevin Coffman 		goto out_err;
239ef338beeSKevin Coffman 	}
240294ec5b8SArnd Bergmann 	p = simple_get_bytes(p, end, &time32, sizeof(time32));
2411da177e4SLinus Torvalds 	if (IS_ERR(p))
242a8cc1cb7SKevin Coffman 		goto out_err;
243294ec5b8SArnd Bergmann 	/* unsigned 32-bit time overflows in year 2106 */
244294ec5b8SArnd Bergmann 	ctx->endtime = (time64_t)time32;
245c3be6577SPaul Burton 	p = simple_get_bytes(p, end, &seq_send, sizeof(seq_send));
2461da177e4SLinus Torvalds 	if (IS_ERR(p))
247a8cc1cb7SKevin Coffman 		goto out_err;
248c3be6577SPaul Burton 	atomic_set(&ctx->seq_send, seq_send);
2491da177e4SLinus Torvalds 	p = simple_get_netobj(p, end, &ctx->mech_used);
2501da177e4SLinus Torvalds 	if (IS_ERR(p))
251a8cc1cb7SKevin Coffman 		goto out_err;
25281d4a433SKevin Coffman 	p = get_key(p, end, ctx, &ctx->enc);
2531da177e4SLinus Torvalds 	if (IS_ERR(p))
2541da177e4SLinus Torvalds 		goto out_err_free_mech;
25581d4a433SKevin Coffman 	p = get_key(p, end, ctx, &ctx->seq);
2561da177e4SLinus Torvalds 	if (IS_ERR(p))
2571da177e4SLinus Torvalds 		goto out_err_free_key1;
2581da177e4SLinus Torvalds 	if (p != end) {
2591da177e4SLinus Torvalds 		p = ERR_PTR(-EFAULT);
2601da177e4SLinus Torvalds 		goto out_err_free_key2;
2611da177e4SLinus Torvalds 	}
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	return 0;
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds out_err_free_key2:
266e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->seq);
2671da177e4SLinus Torvalds out_err_free_key1:
268e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->enc);
2691da177e4SLinus Torvalds out_err_free_mech:
2701da177e4SLinus Torvalds 	kfree(ctx->mech_used.data);
2711da177e4SLinus Torvalds out_err:
2721da177e4SLinus Torvalds 	return PTR_ERR(p);
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds 
275e9e575b8SKees Cook static struct crypto_sync_skcipher *
276934a95aaSKevin Coffman context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key)
27747d84807SKevin Coffman {
278e9e575b8SKees Cook 	struct crypto_sync_skcipher *cp;
27947d84807SKevin Coffman 
280e9e575b8SKees Cook 	cp = crypto_alloc_sync_skcipher(cname, 0, 0);
28147d84807SKevin Coffman 	if (IS_ERR(cp)) {
28247d84807SKevin Coffman 		dprintk("gss_kerberos_mech: unable to initialize "
283934a95aaSKevin Coffman 			"crypto algorithm %s\n", cname);
28447d84807SKevin Coffman 		return NULL;
28547d84807SKevin Coffman 	}
286e9e575b8SKees Cook 	if (crypto_sync_skcipher_setkey(cp, key, ctx->gk5e->keylength)) {
28747d84807SKevin Coffman 		dprintk("gss_kerberos_mech: error setting key for "
288934a95aaSKevin Coffman 			"crypto algorithm %s\n", cname);
289e9e575b8SKees Cook 		crypto_free_sync_skcipher(cp);
29047d84807SKevin Coffman 		return NULL;
29147d84807SKevin Coffman 	}
29247d84807SKevin Coffman 	return cp;
29347d84807SKevin Coffman }
29447d84807SKevin Coffman 
29547d84807SKevin Coffman static inline void
29647d84807SKevin Coffman set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed)
29747d84807SKevin Coffman {
29847d84807SKevin Coffman 	cdata[0] = (usage>>24)&0xff;
29947d84807SKevin Coffman 	cdata[1] = (usage>>16)&0xff;
30047d84807SKevin Coffman 	cdata[2] = (usage>>8)&0xff;
30147d84807SKevin Coffman 	cdata[3] = usage&0xff;
30247d84807SKevin Coffman 	cdata[4] = seed;
30347d84807SKevin Coffman }
30447d84807SKevin Coffman 
30547d84807SKevin Coffman static int
3061f4c86c0STrond Myklebust context_derive_keys_des3(struct krb5_ctx *ctx, gfp_t gfp_mask)
30747d84807SKevin Coffman {
30847d84807SKevin Coffman 	struct xdr_netobj c, keyin, keyout;
30947d84807SKevin Coffman 	u8 cdata[GSS_KRB5_K5CLENGTH];
31047d84807SKevin Coffman 	u32 err;
31147d84807SKevin Coffman 
31247d84807SKevin Coffman 	c.len = GSS_KRB5_K5CLENGTH;
31347d84807SKevin Coffman 	c.data = cdata;
31447d84807SKevin Coffman 
315fc263a91SKevin Coffman 	keyin.data = ctx->Ksess;
316fc263a91SKevin Coffman 	keyin.len = ctx->gk5e->keylength;
317fc263a91SKevin Coffman 	keyout.len = ctx->gk5e->keylength;
31847d84807SKevin Coffman 
31947d84807SKevin Coffman 	/* seq uses the raw key */
320934a95aaSKevin Coffman 	ctx->seq = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
321fc263a91SKevin Coffman 					   ctx->Ksess);
32247d84807SKevin Coffman 	if (ctx->seq == NULL)
32347d84807SKevin Coffman 		goto out_err;
32447d84807SKevin Coffman 
325934a95aaSKevin Coffman 	ctx->enc = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
326fc263a91SKevin Coffman 					   ctx->Ksess);
32747d84807SKevin Coffman 	if (ctx->enc == NULL)
32847d84807SKevin Coffman 		goto out_free_seq;
32947d84807SKevin Coffman 
33047d84807SKevin Coffman 	/* derive cksum */
33147d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM);
33247d84807SKevin Coffman 	keyout.data = ctx->cksum;
3331f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
33447d84807SKevin Coffman 	if (err) {
33547d84807SKevin Coffman 		dprintk("%s: Error %d deriving cksum key\n",
33647d84807SKevin Coffman 			__func__, err);
33747d84807SKevin Coffman 		goto out_free_enc;
33847d84807SKevin Coffman 	}
33947d84807SKevin Coffman 
34047d84807SKevin Coffman 	return 0;
34147d84807SKevin Coffman 
34247d84807SKevin Coffman out_free_enc:
343e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->enc);
34447d84807SKevin Coffman out_free_seq:
345e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->seq);
34647d84807SKevin Coffman out_err:
34747d84807SKevin Coffman 	return -EINVAL;
34847d84807SKevin Coffman }
34947d84807SKevin Coffman 
3502dbe0cacSChuck Lever static struct crypto_ahash *
3512dbe0cacSChuck Lever gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
3522dbe0cacSChuck Lever {
3532dbe0cacSChuck Lever 	struct crypto_ahash *tfm;
3542dbe0cacSChuck Lever 
3552dbe0cacSChuck Lever 	tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
3562dbe0cacSChuck Lever 	if (IS_ERR(tfm))
3572dbe0cacSChuck Lever 		return NULL;
3582dbe0cacSChuck Lever 	if (crypto_ahash_setkey(tfm, key->data, key->len)) {
3592dbe0cacSChuck Lever 		crypto_free_ahash(tfm);
3602dbe0cacSChuck Lever 		return NULL;
3612dbe0cacSChuck Lever 	}
3622dbe0cacSChuck Lever 	return tfm;
3632dbe0cacSChuck Lever }
3642dbe0cacSChuck Lever 
36547d84807SKevin Coffman static int
3661f4c86c0STrond Myklebust context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
36747d84807SKevin Coffman {
36847d84807SKevin Coffman 	u8 cdata[GSS_KRB5_K5CLENGTH];
3699f0b49f9SChuck Lever 	struct xdr_netobj c = {
3709f0b49f9SChuck Lever 		.len	= sizeof(cdata),
3719f0b49f9SChuck Lever 		.data	= cdata,
3729f0b49f9SChuck Lever 	};
3739f0b49f9SChuck Lever 	struct xdr_netobj keyin = {
3749f0b49f9SChuck Lever 		.len	= ctx->gk5e->keylength,
3759f0b49f9SChuck Lever 		.data	= ctx->Ksess,
3769f0b49f9SChuck Lever 	};
3779f0b49f9SChuck Lever 	struct xdr_netobj keyout;
3789f0b49f9SChuck Lever 	int ret = -EINVAL;
3799f0b49f9SChuck Lever 	void *subkey;
38047d84807SKevin Coffman 	u32 err;
38147d84807SKevin Coffman 
3829f0b49f9SChuck Lever 	subkey = kmalloc(ctx->gk5e->keylength, gfp_mask);
3839f0b49f9SChuck Lever 	if (!subkey)
3849f0b49f9SChuck Lever 		return -ENOMEM;
385fc263a91SKevin Coffman 	keyout.len = ctx->gk5e->keylength;
3869f0b49f9SChuck Lever 	keyout.data = subkey;
38747d84807SKevin Coffman 
38847d84807SKevin Coffman 	/* initiator seal encryption */
38947d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
3901f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
39147d84807SKevin Coffman 	if (err) {
39247d84807SKevin Coffman 		dprintk("%s: Error %d deriving initiator_seal key\n",
39347d84807SKevin Coffman 			__func__, err);
3949f0b49f9SChuck Lever 		goto out;
39547d84807SKevin Coffman 	}
396934a95aaSKevin Coffman 	ctx->initiator_enc = context_v2_alloc_cipher(ctx,
397934a95aaSKevin Coffman 						     ctx->gk5e->encrypt_name,
3989f0b49f9SChuck Lever 						     subkey);
39947d84807SKevin Coffman 	if (ctx->initiator_enc == NULL)
4009f0b49f9SChuck Lever 		goto out;
4017989a4f4SChuck Lever 	if (ctx->gk5e->aux_cipher) {
4027989a4f4SChuck Lever 		ctx->initiator_enc_aux =
4037989a4f4SChuck Lever 			context_v2_alloc_cipher(ctx, ctx->gk5e->aux_cipher,
4049f0b49f9SChuck Lever 						subkey);
4057989a4f4SChuck Lever 		if (ctx->initiator_enc_aux == NULL)
4067989a4f4SChuck Lever 			goto out_free;
4077989a4f4SChuck Lever 	}
40847d84807SKevin Coffman 
40947d84807SKevin Coffman 	/* acceptor seal encryption */
41047d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_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 acceptor_seal key\n",
41447d84807SKevin Coffman 			__func__, err);
4157989a4f4SChuck Lever 		goto out_free;
41647d84807SKevin Coffman 	}
417934a95aaSKevin Coffman 	ctx->acceptor_enc = context_v2_alloc_cipher(ctx,
418934a95aaSKevin Coffman 						    ctx->gk5e->encrypt_name,
4199f0b49f9SChuck Lever 						    subkey);
42047d84807SKevin Coffman 	if (ctx->acceptor_enc == NULL)
4217989a4f4SChuck Lever 		goto out_free;
4227989a4f4SChuck Lever 	if (ctx->gk5e->aux_cipher) {
4237989a4f4SChuck Lever 		ctx->acceptor_enc_aux =
4247989a4f4SChuck Lever 			context_v2_alloc_cipher(ctx, ctx->gk5e->aux_cipher,
4259f0b49f9SChuck Lever 						subkey);
4267989a4f4SChuck Lever 		if (ctx->acceptor_enc_aux == NULL)
4277989a4f4SChuck Lever 			goto out_free;
4287989a4f4SChuck Lever 	}
42947d84807SKevin Coffman 
43047d84807SKevin Coffman 	/* initiator sign checksum */
43147d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
4321f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4332dbe0cacSChuck Lever 	if (err)
4347989a4f4SChuck Lever 		goto out_free;
4352dbe0cacSChuck Lever 	ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
4362dbe0cacSChuck Lever 	if (ctx->initiator_sign == NULL)
4372dbe0cacSChuck Lever 		goto out_free;
43847d84807SKevin Coffman 
43947d84807SKevin Coffman 	/* acceptor sign checksum */
44047d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
4411f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
4422dbe0cacSChuck Lever 	if (err)
4437989a4f4SChuck Lever 		goto out_free;
4442dbe0cacSChuck Lever 	ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
4452dbe0cacSChuck Lever 	if (ctx->acceptor_sign == NULL)
4462dbe0cacSChuck Lever 		goto out_free;
44747d84807SKevin Coffman 
44847d84807SKevin Coffman 	/* initiator seal integrity */
44947d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
4501f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
451*8270dbfcSChuck Lever 	if (err)
4527989a4f4SChuck Lever 		goto out_free;
453*8270dbfcSChuck Lever 	ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
454*8270dbfcSChuck Lever 	if (ctx->initiator_integ == NULL)
455*8270dbfcSChuck Lever 		goto out_free;
45647d84807SKevin Coffman 
45747d84807SKevin Coffman 	/* acceptor seal integrity */
45847d84807SKevin Coffman 	set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
4591f4c86c0STrond Myklebust 	err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
460*8270dbfcSChuck Lever 	if (err)
4617989a4f4SChuck Lever 		goto out_free;
462*8270dbfcSChuck Lever 	ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
463*8270dbfcSChuck Lever 	if (ctx->acceptor_integ == NULL)
464*8270dbfcSChuck Lever 		goto out_free;
465934a95aaSKevin Coffman 
4669f0b49f9SChuck Lever 	ret = 0;
4679f0b49f9SChuck Lever out:
4689f0b49f9SChuck Lever 	kfree_sensitive(subkey);
4699f0b49f9SChuck Lever 	return ret;
47047d84807SKevin Coffman 
4717989a4f4SChuck Lever out_free:
472*8270dbfcSChuck Lever 	crypto_free_ahash(ctx->acceptor_integ);
473*8270dbfcSChuck Lever 	crypto_free_ahash(ctx->initiator_integ);
4742dbe0cacSChuck Lever 	crypto_free_ahash(ctx->acceptor_sign);
4752dbe0cacSChuck Lever 	crypto_free_ahash(ctx->initiator_sign);
4767989a4f4SChuck Lever 	crypto_free_sync_skcipher(ctx->acceptor_enc_aux);
477e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->acceptor_enc);
4787989a4f4SChuck Lever 	crypto_free_sync_skcipher(ctx->initiator_enc_aux);
479e9e575b8SKees Cook 	crypto_free_sync_skcipher(ctx->initiator_enc);
4809f0b49f9SChuck Lever 	goto out;
48147d84807SKevin Coffman }
48247d84807SKevin Coffman 
48347d84807SKevin Coffman static int
4841f4c86c0STrond Myklebust gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
4851f4c86c0STrond Myklebust 		gfp_t gfp_mask)
48647d84807SKevin Coffman {
487c3be6577SPaul Burton 	u64 seq_send64;
48847d84807SKevin Coffman 	int keylen;
489294ec5b8SArnd Bergmann 	u32 time32;
49047d84807SKevin Coffman 
49147d84807SKevin Coffman 	p = simple_get_bytes(p, end, &ctx->flags, sizeof(ctx->flags));
49247d84807SKevin Coffman 	if (IS_ERR(p))
49347d84807SKevin Coffman 		goto out_err;
49447d84807SKevin Coffman 	ctx->initiate = ctx->flags & KRB5_CTX_FLAG_INITIATOR;
49547d84807SKevin Coffman 
496294ec5b8SArnd Bergmann 	p = simple_get_bytes(p, end, &time32, sizeof(time32));
49747d84807SKevin Coffman 	if (IS_ERR(p))
49847d84807SKevin Coffman 		goto out_err;
499294ec5b8SArnd Bergmann 	/* unsigned 32-bit time overflows in year 2106 */
500294ec5b8SArnd Bergmann 	ctx->endtime = (time64_t)time32;
501c3be6577SPaul Burton 	p = simple_get_bytes(p, end, &seq_send64, sizeof(seq_send64));
50247d84807SKevin Coffman 	if (IS_ERR(p))
50347d84807SKevin Coffman 		goto out_err;
504c3be6577SPaul Burton 	atomic64_set(&ctx->seq_send64, seq_send64);
50547d84807SKevin Coffman 	/* set seq_send for use by "older" enctypes */
506c3be6577SPaul Burton 	atomic_set(&ctx->seq_send, seq_send64);
507c3be6577SPaul Burton 	if (seq_send64 != atomic_read(&ctx->seq_send)) {
508c3be6577SPaul Burton 		dprintk("%s: seq_send64 %llx, seq_send %x overflow?\n", __func__,
509c3be6577SPaul Burton 			seq_send64, atomic_read(&ctx->seq_send));
510ce8477e1SBian Naimeng 		p = ERR_PTR(-EINVAL);
51147d84807SKevin Coffman 		goto out_err;
51247d84807SKevin Coffman 	}
51347d84807SKevin Coffman 	p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
51447d84807SKevin Coffman 	if (IS_ERR(p))
51547d84807SKevin Coffman 		goto out_err;
516958142e9SKevin Coffman 	/* Map ENCTYPE_DES3_CBC_SHA1 to ENCTYPE_DES3_CBC_RAW */
517958142e9SKevin Coffman 	if (ctx->enctype == ENCTYPE_DES3_CBC_SHA1)
518958142e9SKevin Coffman 		ctx->enctype = ENCTYPE_DES3_CBC_RAW;
51947d84807SKevin Coffman 	ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
52047d84807SKevin Coffman 	if (ctx->gk5e == NULL) {
52147d84807SKevin Coffman 		dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
52247d84807SKevin Coffman 			ctx->enctype);
52347d84807SKevin Coffman 		p = ERR_PTR(-EINVAL);
52447d84807SKevin Coffman 		goto out_err;
52547d84807SKevin Coffman 	}
52647d84807SKevin Coffman 	keylen = ctx->gk5e->keylength;
52747d84807SKevin Coffman 
528fc263a91SKevin Coffman 	p = simple_get_bytes(p, end, ctx->Ksess, keylen);
52947d84807SKevin Coffman 	if (IS_ERR(p))
53047d84807SKevin Coffman 		goto out_err;
53147d84807SKevin Coffman 
53247d84807SKevin Coffman 	if (p != end) {
53347d84807SKevin Coffman 		p = ERR_PTR(-EINVAL);
53447d84807SKevin Coffman 		goto out_err;
53547d84807SKevin Coffman 	}
53647d84807SKevin Coffman 
53747d84807SKevin Coffman 	ctx->mech_used.data = kmemdup(gss_kerberos_mech.gm_oid.data,
5381f4c86c0STrond Myklebust 				      gss_kerberos_mech.gm_oid.len, gfp_mask);
53947d84807SKevin Coffman 	if (unlikely(ctx->mech_used.data == NULL)) {
54047d84807SKevin Coffman 		p = ERR_PTR(-ENOMEM);
54147d84807SKevin Coffman 		goto out_err;
54247d84807SKevin Coffman 	}
54347d84807SKevin Coffman 	ctx->mech_used.len = gss_kerberos_mech.gm_oid.len;
54447d84807SKevin Coffman 
54547d84807SKevin Coffman 	switch (ctx->enctype) {
54647d84807SKevin Coffman 	case ENCTYPE_DES3_CBC_RAW:
5471f4c86c0STrond Myklebust 		return context_derive_keys_des3(ctx, gfp_mask);
54847d84807SKevin Coffman 	case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
54947d84807SKevin Coffman 	case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
5501f4c86c0STrond Myklebust 		return context_derive_keys_new(ctx, gfp_mask);
55147d84807SKevin Coffman 	default:
55247d84807SKevin Coffman 		return -EINVAL;
55347d84807SKevin Coffman 	}
55447d84807SKevin Coffman 
55547d84807SKevin Coffman out_err:
55647d84807SKevin Coffman 	return PTR_ERR(p);
55747d84807SKevin Coffman }
55847d84807SKevin Coffman 
559a8cc1cb7SKevin Coffman static int
560a8cc1cb7SKevin Coffman gss_import_sec_context_kerberos(const void *p, size_t len,
5611f4c86c0STrond Myklebust 				struct gss_ctx *ctx_id,
562294ec5b8SArnd Bergmann 				time64_t *endtime,
5631f4c86c0STrond Myklebust 				gfp_t gfp_mask)
564a8cc1cb7SKevin Coffman {
565a8cc1cb7SKevin Coffman 	const void *end = (const void *)((const char *)p + len);
566a8cc1cb7SKevin Coffman 	struct  krb5_ctx *ctx;
567a8cc1cb7SKevin Coffman 	int ret;
568a8cc1cb7SKevin Coffman 
5691f4c86c0STrond Myklebust 	ctx = kzalloc(sizeof(*ctx), gfp_mask);
570a8cc1cb7SKevin Coffman 	if (ctx == NULL)
571a8cc1cb7SKevin Coffman 		return -ENOMEM;
572a8cc1cb7SKevin Coffman 
573a8cc1cb7SKevin Coffman 	if (len == 85)
574a8cc1cb7SKevin Coffman 		ret = gss_import_v1_context(p, end, ctx);
575a8cc1cb7SKevin Coffman 	else
5761f4c86c0STrond Myklebust 		ret = gss_import_v2_context(p, end, ctx, gfp_mask);
57701c4e326SChuck Lever 	memzero_explicit(&ctx->Ksess, sizeof(ctx->Ksess));
5787f675ca7SChuck Lever 	if (ret) {
5797f675ca7SChuck Lever 		kfree(ctx);
5807f675ca7SChuck Lever 		return ret;
5817f675ca7SChuck Lever 	}
582a8cc1cb7SKevin Coffman 
583a8cc1cb7SKevin Coffman 	ctx_id->internal_ctx_id = ctx;
584400f26b5SSimo Sorce 	if (endtime)
585400f26b5SSimo Sorce 		*endtime = ctx->endtime;
5867f675ca7SChuck Lever 	return 0;
587a8cc1cb7SKevin Coffman }
588a8cc1cb7SKevin Coffman 
5891da177e4SLinus Torvalds static void
5901da177e4SLinus Torvalds gss_delete_sec_context_kerberos(void *internal_ctx) {
5911da177e4SLinus Torvalds 	struct krb5_ctx *kctx = internal_ctx;
5921da177e4SLinus Torvalds 
593e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->seq);
594e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->enc);
595e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->acceptor_enc);
596e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->initiator_enc);
597e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
598e9e575b8SKees Cook 	crypto_free_sync_skcipher(kctx->initiator_enc_aux);
5992dbe0cacSChuck Lever 	crypto_free_ahash(kctx->acceptor_sign);
6002dbe0cacSChuck Lever 	crypto_free_ahash(kctx->initiator_sign);
601*8270dbfcSChuck Lever 	crypto_free_ahash(kctx->acceptor_integ);
602*8270dbfcSChuck Lever 	crypto_free_ahash(kctx->initiator_integ);
6031da177e4SLinus Torvalds 	kfree(kctx->mech_used.data);
6041da177e4SLinus Torvalds 	kfree(kctx);
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds 
607f1c0a861STrond Myklebust static const struct gss_api_ops gss_kerberos_ops = {
6081da177e4SLinus Torvalds 	.gss_import_sec_context	= gss_import_sec_context_kerberos,
6091da177e4SLinus Torvalds 	.gss_get_mic		= gss_get_mic_kerberos,
6101da177e4SLinus Torvalds 	.gss_verify_mic		= gss_verify_mic_kerberos,
61114ae162cSJ. Bruce Fields 	.gss_wrap		= gss_wrap_kerberos,
61214ae162cSJ. Bruce Fields 	.gss_unwrap		= gss_unwrap_kerberos,
6131da177e4SLinus Torvalds 	.gss_delete_sec_context	= gss_delete_sec_context_kerberos,
6141da177e4SLinus Torvalds };
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds static struct pf_desc gss_kerberos_pfs[] = {
6171da177e4SLinus Torvalds 	[0] = {
6181da177e4SLinus Torvalds 		.pseudoflavor = RPC_AUTH_GSS_KRB5,
61983523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
6201da177e4SLinus Torvalds 		.service = RPC_GSS_SVC_NONE,
6211da177e4SLinus Torvalds 		.name = "krb5",
6221da177e4SLinus Torvalds 	},
6231da177e4SLinus Torvalds 	[1] = {
6241da177e4SLinus Torvalds 		.pseudoflavor = RPC_AUTH_GSS_KRB5I,
62583523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
6261da177e4SLinus Torvalds 		.service = RPC_GSS_SVC_INTEGRITY,
6271da177e4SLinus Torvalds 		.name = "krb5i",
62865b80179SChuck Lever 		.datatouch = true,
6291da177e4SLinus Torvalds 	},
63014ae162cSJ. Bruce Fields 	[2] = {
63114ae162cSJ. Bruce Fields 		.pseudoflavor = RPC_AUTH_GSS_KRB5P,
63283523d08SChuck Lever 		.qop = GSS_C_QOP_DEFAULT,
63314ae162cSJ. Bruce Fields 		.service = RPC_GSS_SVC_PRIVACY,
63414ae162cSJ. Bruce Fields 		.name = "krb5p",
63565b80179SChuck Lever 		.datatouch = true,
63614ae162cSJ. Bruce Fields 	},
6371da177e4SLinus Torvalds };
6381da177e4SLinus Torvalds 
639058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5");
640058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5i");
641058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-krb5p");
642058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390003");
643058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390004");
644058c5c99SJ. Bruce Fields MODULE_ALIAS("rpc-auth-gss-390005");
645f783288fSChuck Lever MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
646058c5c99SJ. Bruce Fields 
6471da177e4SLinus Torvalds static struct gss_api_mech gss_kerberos_mech = {
6481da177e4SLinus Torvalds 	.gm_name	= "krb5",
6491da177e4SLinus Torvalds 	.gm_owner	= THIS_MODULE,
650fb15b26fSChuck Lever 	.gm_oid		= { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
6511da177e4SLinus Torvalds 	.gm_ops		= &gss_kerberos_ops,
6521da177e4SLinus Torvalds 	.gm_pf_num	= ARRAY_SIZE(gss_kerberos_pfs),
6531da177e4SLinus Torvalds 	.gm_pfs		= gss_kerberos_pfs,
654b084f598SJ. Bruce Fields 	.gm_upcall_enctypes = KRB5_SUPPORTED_ENCTYPES,
6551da177e4SLinus Torvalds };
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds static int __init init_kerberos_module(void)
6581da177e4SLinus Torvalds {
6591da177e4SLinus Torvalds 	int status;
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 	status = gss_mech_register(&gss_kerberos_mech);
6621da177e4SLinus Torvalds 	if (status)
6631da177e4SLinus Torvalds 		printk("Failed to register kerberos gss mechanism!\n");
6641da177e4SLinus Torvalds 	return status;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds static void __exit cleanup_kerberos_module(void)
6681da177e4SLinus Torvalds {
6691da177e4SLinus Torvalds 	gss_mech_unregister(&gss_kerberos_mech);
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds MODULE_LICENSE("GPL");
6731da177e4SLinus Torvalds module_init(init_kerberos_module);
6741da177e4SLinus Torvalds module_exit(cleanup_kerberos_module);
675