11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Neil Brown <neilb@cse.unsw.edu.au>
31da177e4SLinus Torvalds  * J. Bruce Fields <bfields@umich.edu>
41da177e4SLinus Torvalds  * Andy Adamson <andros@umich.edu>
51da177e4SLinus Torvalds  * Dug Song <dugsong@monkey.org>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * RPCSEC_GSS server authentication.
81da177e4SLinus Torvalds  * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
91da177e4SLinus Torvalds  * (gssapi)
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * The RPCSEC_GSS involves three stages:
121da177e4SLinus Torvalds  *  1/ context creation
131da177e4SLinus Torvalds  *  2/ data exchange
141da177e4SLinus Torvalds  *  3/ context destruction
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * Context creation is handled largely by upcalls to user-space.
171da177e4SLinus Torvalds  *  In particular, GSS_Accept_sec_context is handled by an upcall
181da177e4SLinus Torvalds  * Data exchange is handled entirely within the kernel
191da177e4SLinus Torvalds  *  In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
201da177e4SLinus Torvalds  * Context destruction is handled in-kernel
211da177e4SLinus Torvalds  *  GSS_Delete_sec_context is in-kernel
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
241da177e4SLinus Torvalds  * The context handle and gss_token are used as a key into the rpcsec_init cache.
251da177e4SLinus Torvalds  * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
261da177e4SLinus Torvalds  * being major_status, minor_status, context_handle, reply_token.
271da177e4SLinus Torvalds  * These are sent back to the client.
281da177e4SLinus Torvalds  * Sequence window management is handled by the kernel.  The window size if currently
291da177e4SLinus Torvalds  * a compile time constant.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * When user-space is happy that a context is established, it places an entry
321da177e4SLinus Torvalds  * in the rpcsec_context cache. The key for this cache is the context_handle.
331da177e4SLinus Torvalds  * The content includes:
341da177e4SLinus Torvalds  *   uid/gidlist - for determining access rights
351da177e4SLinus Torvalds  *   mechanism type
361da177e4SLinus Torvalds  *   mechanism specific information, such as a key
371da177e4SLinus Torvalds  *
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #include <linux/types.h>
411da177e4SLinus Torvalds #include <linux/module.h>
421da177e4SLinus Torvalds #include <linux/pagemap.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h>
451da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h>
461da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h>
471da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h>
481da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h>
491da177e4SLinus Torvalds #include <linux/sunrpc/cache.h>
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #ifdef RPC_DEBUG
521da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
561da177e4SLinus Torvalds  * into replies.
571da177e4SLinus Torvalds  *
581da177e4SLinus Torvalds  * Key is context handle (\x if empty) and gss_token.
591da177e4SLinus Torvalds  * Content is major_status minor_status (integers) context_handle, reply_token.
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	return a->len == b->len && 0 == memcmp(a->data, b->data, a->len);
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #define	RSI_HASHBITS	6
691da177e4SLinus Torvalds #define	RSI_HASHMAX	(1<<RSI_HASHBITS)
701da177e4SLinus Torvalds #define	RSI_HASHMASK	(RSI_HASHMAX-1)
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds struct rsi {
731da177e4SLinus Torvalds 	struct cache_head	h;
741da177e4SLinus Torvalds 	struct xdr_netobj	in_handle, in_token;
751da177e4SLinus Torvalds 	struct xdr_netobj	out_handle, out_token;
761da177e4SLinus Torvalds 	int			major_status, minor_status;
771da177e4SLinus Torvalds };
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds static struct cache_head *rsi_table[RSI_HASHMAX];
801da177e4SLinus Torvalds static struct cache_detail rsi_cache;
81d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old);
82d4d11ea9SNeilBrown static struct rsi *rsi_lookup(struct rsi *item);
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	kfree(rsii->in_handle.data);
871da177e4SLinus Torvalds 	kfree(rsii->in_token.data);
881da177e4SLinus Torvalds 	kfree(rsii->out_handle.data);
891da177e4SLinus Torvalds 	kfree(rsii->out_token.data);
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
92baab935fSNeilBrown static void rsi_put(struct kref *ref)
931da177e4SLinus Torvalds {
94baab935fSNeilBrown 	struct rsi *rsii = container_of(ref, struct rsi, h.ref);
951da177e4SLinus Torvalds 	rsi_free(rsii);
961da177e4SLinus Torvalds 	kfree(rsii);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS)
1021da177e4SLinus Torvalds 	     ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
105d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b)
1061da177e4SLinus Torvalds {
107d4d11ea9SNeilBrown 	struct rsi *item = container_of(a, struct rsi, h);
108d4d11ea9SNeilBrown 	struct rsi *tmp = container_of(b, struct rsi, h);
1091da177e4SLinus Torvalds 	return netobj_equal(&item->in_handle, &tmp->in_handle)
1101da177e4SLinus Torvalds 		&& netobj_equal(&item->in_token, &tmp->in_token);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds 	dst->len = len;
1161da177e4SLinus Torvalds 	dst->data = (len ? kmalloc(len, GFP_KERNEL) : NULL);
1171da177e4SLinus Torvalds 	if (dst->data)
1181da177e4SLinus Torvalds 		memcpy(dst->data, src, len);
1191da177e4SLinus Torvalds 	if (len && !dst->data)
1201da177e4SLinus Torvalds 		return -ENOMEM;
1211da177e4SLinus Torvalds 	return 0;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	return dup_to_netobj(dst, src->data, src->len);
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
129d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem)
1301da177e4SLinus Torvalds {
131d4d11ea9SNeilBrown 	struct rsi *new = container_of(cnew, struct rsi, h);
132d4d11ea9SNeilBrown 	struct rsi *item = container_of(citem, struct rsi, h);
133d4d11ea9SNeilBrown 
1341da177e4SLinus Torvalds 	new->out_handle.data = NULL;
1351da177e4SLinus Torvalds 	new->out_handle.len = 0;
1361da177e4SLinus Torvalds 	new->out_token.data = NULL;
1371da177e4SLinus Torvalds 	new->out_token.len = 0;
1381da177e4SLinus Torvalds 	new->in_handle.len = item->in_handle.len;
1391da177e4SLinus Torvalds 	item->in_handle.len = 0;
1401da177e4SLinus Torvalds 	new->in_token.len = item->in_token.len;
1411da177e4SLinus Torvalds 	item->in_token.len = 0;
1421da177e4SLinus Torvalds 	new->in_handle.data = item->in_handle.data;
1431da177e4SLinus Torvalds 	item->in_handle.data = NULL;
1441da177e4SLinus Torvalds 	new->in_token.data = item->in_token.data;
1451da177e4SLinus Torvalds 	item->in_token.data = NULL;
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds 
148d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem)
1491da177e4SLinus Torvalds {
150d4d11ea9SNeilBrown 	struct rsi *new = container_of(cnew, struct rsi, h);
151d4d11ea9SNeilBrown 	struct rsi *item = container_of(citem, struct rsi, h);
152d4d11ea9SNeilBrown 
1531da177e4SLinus Torvalds 	BUG_ON(new->out_handle.data || new->out_token.data);
1541da177e4SLinus Torvalds 	new->out_handle.len = item->out_handle.len;
1551da177e4SLinus Torvalds 	item->out_handle.len = 0;
1561da177e4SLinus Torvalds 	new->out_token.len = item->out_token.len;
1571da177e4SLinus Torvalds 	item->out_token.len = 0;
1581da177e4SLinus Torvalds 	new->out_handle.data = item->out_handle.data;
1591da177e4SLinus Torvalds 	item->out_handle.data = NULL;
1601da177e4SLinus Torvalds 	new->out_token.data = item->out_token.data;
1611da177e4SLinus Torvalds 	item->out_token.data = NULL;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	new->major_status = item->major_status;
1641da177e4SLinus Torvalds 	new->minor_status = item->minor_status;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
167d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void)
168d4d11ea9SNeilBrown {
169d4d11ea9SNeilBrown 	struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL);
170d4d11ea9SNeilBrown 	if (rsii)
171d4d11ea9SNeilBrown 		return &rsii->h;
172d4d11ea9SNeilBrown 	else
173d4d11ea9SNeilBrown 		return NULL;
174d4d11ea9SNeilBrown }
175d4d11ea9SNeilBrown 
1761da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd,
1771da177e4SLinus Torvalds                        struct cache_head *h,
1781da177e4SLinus Torvalds                        char **bpp, int *blen)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	struct rsi *rsii = container_of(h, struct rsi, h);
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len);
1831da177e4SLinus Torvalds 	qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len);
1841da177e4SLinus Torvalds 	(*bpp)[-1] = '\n';
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd,
1891da177e4SLinus Torvalds                     char *mesg, int mlen)
1901da177e4SLinus Torvalds {
1911da177e4SLinus Torvalds 	/* context token expiry major minor context token */
1921da177e4SLinus Torvalds 	char *buf = mesg;
1931da177e4SLinus Torvalds 	char *ep;
1941da177e4SLinus Torvalds 	int len;
1951da177e4SLinus Torvalds 	struct rsi rsii, *rsip = NULL;
1961da177e4SLinus Torvalds 	time_t expiry;
1971da177e4SLinus Torvalds 	int status = -EINVAL;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	memset(&rsii, 0, sizeof(rsii));
2001da177e4SLinus Torvalds 	/* handle */
2011da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2021da177e4SLinus Torvalds 	if (len < 0)
2031da177e4SLinus Torvalds 		goto out;
2041da177e4SLinus Torvalds 	status = -ENOMEM;
2051da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.in_handle, buf, len))
2061da177e4SLinus Torvalds 		goto out;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	/* token */
2091da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2101da177e4SLinus Torvalds 	status = -EINVAL;
2111da177e4SLinus Torvalds 	if (len < 0)
2121da177e4SLinus Torvalds 		goto out;
2131da177e4SLinus Torvalds 	status = -ENOMEM;
2141da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.in_token, buf, len))
2151da177e4SLinus Torvalds 		goto out;
2161da177e4SLinus Torvalds 
217d4d11ea9SNeilBrown 	rsip = rsi_lookup(&rsii);
218d4d11ea9SNeilBrown 	if (!rsip)
219d4d11ea9SNeilBrown 		goto out;
220d4d11ea9SNeilBrown 
2211da177e4SLinus Torvalds 	rsii.h.flags = 0;
2221da177e4SLinus Torvalds 	/* expiry */
2231da177e4SLinus Torvalds 	expiry = get_expiry(&mesg);
2241da177e4SLinus Torvalds 	status = -EINVAL;
2251da177e4SLinus Torvalds 	if (expiry == 0)
2261da177e4SLinus Torvalds 		goto out;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	/* major/minor */
2291da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2301da177e4SLinus Torvalds 	if (len < 0)
2311da177e4SLinus Torvalds 		goto out;
2321da177e4SLinus Torvalds 	if (len == 0) {
2331da177e4SLinus Torvalds 		goto out;
2341da177e4SLinus Torvalds 	} else {
2351da177e4SLinus Torvalds 		rsii.major_status = simple_strtoul(buf, &ep, 10);
2361da177e4SLinus Torvalds 		if (*ep)
2371da177e4SLinus Torvalds 			goto out;
2381da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
2391da177e4SLinus Torvalds 		if (len <= 0)
2401da177e4SLinus Torvalds 			goto out;
2411da177e4SLinus Torvalds 		rsii.minor_status = simple_strtoul(buf, &ep, 10);
2421da177e4SLinus Torvalds 		if (*ep)
2431da177e4SLinus Torvalds 			goto out;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 		/* out_handle */
2461da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
2471da177e4SLinus Torvalds 		if (len < 0)
2481da177e4SLinus Torvalds 			goto out;
2491da177e4SLinus Torvalds 		status = -ENOMEM;
2501da177e4SLinus Torvalds 		if (dup_to_netobj(&rsii.out_handle, buf, len))
2511da177e4SLinus Torvalds 			goto out;
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 		/* out_token */
2541da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
2551da177e4SLinus Torvalds 		status = -EINVAL;
2561da177e4SLinus Torvalds 		if (len < 0)
2571da177e4SLinus Torvalds 			goto out;
2581da177e4SLinus Torvalds 		status = -ENOMEM;
2591da177e4SLinus Torvalds 		if (dup_to_netobj(&rsii.out_token, buf, len))
2601da177e4SLinus Torvalds 			goto out;
2611da177e4SLinus Torvalds 	}
2621da177e4SLinus Torvalds 	rsii.h.expiry_time = expiry;
263d4d11ea9SNeilBrown 	rsip = rsi_update(&rsii, rsip);
2641da177e4SLinus Torvalds 	status = 0;
2651da177e4SLinus Torvalds out:
2661da177e4SLinus Torvalds 	rsi_free(&rsii);
2671da177e4SLinus Torvalds 	if (rsip)
268baab935fSNeilBrown 		cache_put(&rsip->h, &rsi_cache);
269d4d11ea9SNeilBrown 	else
270d4d11ea9SNeilBrown 		status = -ENOMEM;
2711da177e4SLinus Torvalds 	return status;
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds static struct cache_detail rsi_cache = {
275f35279d3SBruce Allan 	.owner		= THIS_MODULE,
2761da177e4SLinus Torvalds 	.hash_size	= RSI_HASHMAX,
2771da177e4SLinus Torvalds 	.hash_table     = rsi_table,
2781da177e4SLinus Torvalds 	.name           = "auth.rpcsec.init",
2791da177e4SLinus Torvalds 	.cache_put      = rsi_put,
2801da177e4SLinus Torvalds 	.cache_request  = rsi_request,
2811da177e4SLinus Torvalds 	.cache_parse    = rsi_parse,
282d4d11ea9SNeilBrown 	.match		= rsi_match,
283d4d11ea9SNeilBrown 	.init		= rsi_init,
284d4d11ea9SNeilBrown 	.update		= update_rsi,
285d4d11ea9SNeilBrown 	.alloc		= rsi_alloc,
2861da177e4SLinus Torvalds };
2871da177e4SLinus Torvalds 
288d4d11ea9SNeilBrown static struct rsi *rsi_lookup(struct rsi *item)
289d4d11ea9SNeilBrown {
290d4d11ea9SNeilBrown 	struct cache_head *ch;
291d4d11ea9SNeilBrown 	int hash = rsi_hash(item);
292d4d11ea9SNeilBrown 
293d4d11ea9SNeilBrown 	ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash);
294d4d11ea9SNeilBrown 	if (ch)
295d4d11ea9SNeilBrown 		return container_of(ch, struct rsi, h);
296d4d11ea9SNeilBrown 	else
297d4d11ea9SNeilBrown 		return NULL;
298d4d11ea9SNeilBrown }
299d4d11ea9SNeilBrown 
300d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old)
301d4d11ea9SNeilBrown {
302d4d11ea9SNeilBrown 	struct cache_head *ch;
303d4d11ea9SNeilBrown 	int hash = rsi_hash(new);
304d4d11ea9SNeilBrown 
305d4d11ea9SNeilBrown 	ch = sunrpc_cache_update(&rsi_cache, &new->h,
306d4d11ea9SNeilBrown 				 &old->h, hash);
307d4d11ea9SNeilBrown 	if (ch)
308d4d11ea9SNeilBrown 		return container_of(ch, struct rsi, h);
309d4d11ea9SNeilBrown 	else
310d4d11ea9SNeilBrown 		return NULL;
311d4d11ea9SNeilBrown }
312d4d11ea9SNeilBrown 
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds /*
3151da177e4SLinus Torvalds  * The rpcsec_context cache is used to store a context that is
3161da177e4SLinus Torvalds  * used in data exchange.
3171da177e4SLinus Torvalds  * The key is a context handle. The content is:
3181da177e4SLinus Torvalds  *  uid, gidlist, mechanism, service-set, mech-specific-data
3191da177e4SLinus Torvalds  */
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds #define	RSC_HASHBITS	10
3221da177e4SLinus Torvalds #define	RSC_HASHMAX	(1<<RSC_HASHBITS)
3231da177e4SLinus Torvalds #define	RSC_HASHMASK	(RSC_HASHMAX-1)
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds #define GSS_SEQ_WIN	128
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds struct gss_svc_seq_data {
3281da177e4SLinus Torvalds 	/* highest seq number seen so far: */
3291da177e4SLinus Torvalds 	int			sd_max;
3301da177e4SLinus Torvalds 	/* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of
3311da177e4SLinus Torvalds 	 * sd_win is nonzero iff sequence number i has been seen already: */
3321da177e4SLinus Torvalds 	unsigned long		sd_win[GSS_SEQ_WIN/BITS_PER_LONG];
3331da177e4SLinus Torvalds 	spinlock_t		sd_lock;
3341da177e4SLinus Torvalds };
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds struct rsc {
3371da177e4SLinus Torvalds 	struct cache_head	h;
3381da177e4SLinus Torvalds 	struct xdr_netobj	handle;
3391da177e4SLinus Torvalds 	struct svc_cred		cred;
3401da177e4SLinus Torvalds 	struct gss_svc_seq_data	seqdata;
3411da177e4SLinus Torvalds 	struct gss_ctx		*mechctx;
3421da177e4SLinus Torvalds };
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds static struct cache_head *rsc_table[RSC_HASHMAX];
3451da177e4SLinus Torvalds static struct cache_detail rsc_cache;
34617f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old);
34717f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item);
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci)
3501da177e4SLinus Torvalds {
3511da177e4SLinus Torvalds 	kfree(rsci->handle.data);
3521da177e4SLinus Torvalds 	if (rsci->mechctx)
3531da177e4SLinus Torvalds 		gss_delete_sec_context(&rsci->mechctx);
3541da177e4SLinus Torvalds 	if (rsci->cred.cr_group_info)
3551da177e4SLinus Torvalds 		put_group_info(rsci->cred.cr_group_info);
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
358baab935fSNeilBrown static void rsc_put(struct kref *ref)
3591da177e4SLinus Torvalds {
360baab935fSNeilBrown 	struct rsc *rsci = container_of(ref, struct rsc, h.ref);
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	rsc_free(rsci);
3631da177e4SLinus Torvalds 	kfree(rsci);
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds static inline int
3671da177e4SLinus Torvalds rsc_hash(struct rsc *rsci)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds 	return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
37217f834b6SNeilBrown static int
37317f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b)
3741da177e4SLinus Torvalds {
37517f834b6SNeilBrown 	struct rsc *new = container_of(a, struct rsc, h);
37617f834b6SNeilBrown 	struct rsc *tmp = container_of(b, struct rsc, h);
37717f834b6SNeilBrown 
3781da177e4SLinus Torvalds 	return netobj_equal(&new->handle, &tmp->handle);
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds 
38117f834b6SNeilBrown static void
38217f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
3831da177e4SLinus Torvalds {
38417f834b6SNeilBrown 	struct rsc *new = container_of(cnew, struct rsc, h);
38517f834b6SNeilBrown 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
38617f834b6SNeilBrown 
3871da177e4SLinus Torvalds 	new->handle.len = tmp->handle.len;
3881da177e4SLinus Torvalds 	tmp->handle.len = 0;
3891da177e4SLinus Torvalds 	new->handle.data = tmp->handle.data;
3901da177e4SLinus Torvalds 	tmp->handle.data = NULL;
3911da177e4SLinus Torvalds 	new->mechctx = NULL;
3921da177e4SLinus Torvalds 	new->cred.cr_group_info = NULL;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
39517f834b6SNeilBrown static void
39617f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
3971da177e4SLinus Torvalds {
39817f834b6SNeilBrown 	struct rsc *new = container_of(cnew, struct rsc, h);
39917f834b6SNeilBrown 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
40017f834b6SNeilBrown 
4011da177e4SLinus Torvalds 	new->mechctx = tmp->mechctx;
4021da177e4SLinus Torvalds 	tmp->mechctx = NULL;
4031da177e4SLinus Torvalds 	memset(&new->seqdata, 0, sizeof(new->seqdata));
4041da177e4SLinus Torvalds 	spin_lock_init(&new->seqdata.sd_lock);
4051da177e4SLinus Torvalds 	new->cred = tmp->cred;
4061da177e4SLinus Torvalds 	tmp->cred.cr_group_info = NULL;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
40917f834b6SNeilBrown static struct cache_head *
41017f834b6SNeilBrown rsc_alloc(void)
41117f834b6SNeilBrown {
41217f834b6SNeilBrown 	struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL);
41317f834b6SNeilBrown 	if (rsci)
41417f834b6SNeilBrown 		return &rsci->h;
41517f834b6SNeilBrown 	else
41617f834b6SNeilBrown 		return NULL;
41717f834b6SNeilBrown }
41817f834b6SNeilBrown 
4191da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd,
4201da177e4SLinus Torvalds 		     char *mesg, int mlen)
4211da177e4SLinus Torvalds {
4221da177e4SLinus Torvalds 	/* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */
4231da177e4SLinus Torvalds 	char *buf = mesg;
4241da177e4SLinus Torvalds 	int len, rv;
4251da177e4SLinus Torvalds 	struct rsc rsci, *rscp = NULL;
4261da177e4SLinus Torvalds 	time_t expiry;
4271da177e4SLinus Torvalds 	int status = -EINVAL;
4281df0cadaSJ. Bruce Fields 	struct gss_api_mech *gm = NULL;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	memset(&rsci, 0, sizeof(rsci));
4311da177e4SLinus Torvalds 	/* context handle */
4321da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
4331da177e4SLinus Torvalds 	if (len < 0) goto out;
4341da177e4SLinus Torvalds 	status = -ENOMEM;
4351da177e4SLinus Torvalds 	if (dup_to_netobj(&rsci.handle, buf, len))
4361da177e4SLinus Torvalds 		goto out;
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	rsci.h.flags = 0;
4391da177e4SLinus Torvalds 	/* expiry */
4401da177e4SLinus Torvalds 	expiry = get_expiry(&mesg);
4411da177e4SLinus Torvalds 	status = -EINVAL;
4421da177e4SLinus Torvalds 	if (expiry == 0)
4431da177e4SLinus Torvalds 		goto out;
4441da177e4SLinus Torvalds 
44517f834b6SNeilBrown 	rscp = rsc_lookup(&rsci);
44617f834b6SNeilBrown 	if (!rscp)
44717f834b6SNeilBrown 		goto out;
44817f834b6SNeilBrown 
4491da177e4SLinus Torvalds 	/* uid, or NEGATIVE */
4501da177e4SLinus Torvalds 	rv = get_int(&mesg, &rsci.cred.cr_uid);
4511da177e4SLinus Torvalds 	if (rv == -EINVAL)
4521da177e4SLinus Torvalds 		goto out;
4531da177e4SLinus Torvalds 	if (rv == -ENOENT)
4541da177e4SLinus Torvalds 		set_bit(CACHE_NEGATIVE, &rsci.h.flags);
4551da177e4SLinus Torvalds 	else {
4561da177e4SLinus Torvalds 		int N, i;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 		/* gid */
4591da177e4SLinus Torvalds 		if (get_int(&mesg, &rsci.cred.cr_gid))
4601da177e4SLinus Torvalds 			goto out;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 		/* number of additional gid's */
4631da177e4SLinus Torvalds 		if (get_int(&mesg, &N))
4641da177e4SLinus Torvalds 			goto out;
4651da177e4SLinus Torvalds 		status = -ENOMEM;
4661da177e4SLinus Torvalds 		rsci.cred.cr_group_info = groups_alloc(N);
4671da177e4SLinus Torvalds 		if (rsci.cred.cr_group_info == NULL)
4681da177e4SLinus Torvalds 			goto out;
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 		/* gid's */
4711da177e4SLinus Torvalds 		status = -EINVAL;
4721da177e4SLinus Torvalds 		for (i=0; i<N; i++) {
4731da177e4SLinus Torvalds 			gid_t gid;
4741da177e4SLinus Torvalds 			if (get_int(&mesg, &gid))
4751da177e4SLinus Torvalds 				goto out;
4761da177e4SLinus Torvalds 			GROUP_AT(rsci.cred.cr_group_info, i) = gid;
4771da177e4SLinus Torvalds 		}
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 		/* mech name */
4801da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4811da177e4SLinus Torvalds 		if (len < 0)
4821da177e4SLinus Torvalds 			goto out;
4831da177e4SLinus Torvalds 		gm = gss_mech_get_by_name(buf);
4841da177e4SLinus Torvalds 		status = -EOPNOTSUPP;
4851da177e4SLinus Torvalds 		if (!gm)
4861da177e4SLinus Torvalds 			goto out;
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 		status = -EINVAL;
4891da177e4SLinus Torvalds 		/* mech-specific data: */
4901da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4911df0cadaSJ. Bruce Fields 		if (len < 0)
4921da177e4SLinus Torvalds 			goto out;
4935fb8b49eSJ. Bruce Fields 		status = gss_import_sec_context(buf, len, gm, &rsci.mechctx);
4941df0cadaSJ. Bruce Fields 		if (status)
4951da177e4SLinus Torvalds 			goto out;
4961da177e4SLinus Torvalds 	}
4971da177e4SLinus Torvalds 	rsci.h.expiry_time = expiry;
49817f834b6SNeilBrown 	rscp = rsc_update(&rsci, rscp);
4991da177e4SLinus Torvalds 	status = 0;
5001da177e4SLinus Torvalds out:
5011df0cadaSJ. Bruce Fields 	gss_mech_put(gm);
5021da177e4SLinus Torvalds 	rsc_free(&rsci);
5031da177e4SLinus Torvalds 	if (rscp)
504baab935fSNeilBrown 		cache_put(&rscp->h, &rsc_cache);
50517f834b6SNeilBrown 	else
50617f834b6SNeilBrown 		status = -ENOMEM;
5071da177e4SLinus Torvalds 	return status;
5081da177e4SLinus Torvalds }
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds static struct cache_detail rsc_cache = {
511f35279d3SBruce Allan 	.owner		= THIS_MODULE,
5121da177e4SLinus Torvalds 	.hash_size	= RSC_HASHMAX,
5131da177e4SLinus Torvalds 	.hash_table	= rsc_table,
5141da177e4SLinus Torvalds 	.name		= "auth.rpcsec.context",
5151da177e4SLinus Torvalds 	.cache_put	= rsc_put,
5161da177e4SLinus Torvalds 	.cache_parse	= rsc_parse,
51717f834b6SNeilBrown 	.match		= rsc_match,
51817f834b6SNeilBrown 	.init		= rsc_init,
51917f834b6SNeilBrown 	.update		= update_rsc,
52017f834b6SNeilBrown 	.alloc		= rsc_alloc,
5211da177e4SLinus Torvalds };
5221da177e4SLinus Torvalds 
52317f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item)
52417f834b6SNeilBrown {
52517f834b6SNeilBrown 	struct cache_head *ch;
52617f834b6SNeilBrown 	int hash = rsc_hash(item);
52717f834b6SNeilBrown 
52817f834b6SNeilBrown 	ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash);
52917f834b6SNeilBrown 	if (ch)
53017f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
53117f834b6SNeilBrown 	else
53217f834b6SNeilBrown 		return NULL;
53317f834b6SNeilBrown }
53417f834b6SNeilBrown 
53517f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old)
53617f834b6SNeilBrown {
53717f834b6SNeilBrown 	struct cache_head *ch;
53817f834b6SNeilBrown 	int hash = rsc_hash(new);
53917f834b6SNeilBrown 
54017f834b6SNeilBrown 	ch = sunrpc_cache_update(&rsc_cache, &new->h,
54117f834b6SNeilBrown 				 &old->h, hash);
54217f834b6SNeilBrown 	if (ch)
54317f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
54417f834b6SNeilBrown 	else
54517f834b6SNeilBrown 		return NULL;
54617f834b6SNeilBrown }
54717f834b6SNeilBrown 
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds static struct rsc *
5501da177e4SLinus Torvalds gss_svc_searchbyctx(struct xdr_netobj *handle)
5511da177e4SLinus Torvalds {
5521da177e4SLinus Torvalds 	struct rsc rsci;
5531da177e4SLinus Torvalds 	struct rsc *found;
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	memset(&rsci, 0, sizeof(rsci));
5561da177e4SLinus Torvalds 	if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
5571da177e4SLinus Torvalds 		return NULL;
55817f834b6SNeilBrown 	found = rsc_lookup(&rsci);
5591da177e4SLinus Torvalds 	rsc_free(&rsci);
5601da177e4SLinus Torvalds 	if (!found)
5611da177e4SLinus Torvalds 		return NULL;
5621da177e4SLinus Torvalds 	if (cache_check(&rsc_cache, &found->h, NULL))
5631da177e4SLinus Torvalds 		return NULL;
5641da177e4SLinus Torvalds 	return found;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */
5681da177e4SLinus Torvalds static int
5691da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num)
5701da177e4SLinus Torvalds {
5711da177e4SLinus Torvalds 	struct gss_svc_seq_data *sd = &rsci->seqdata;
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	spin_lock(&sd->sd_lock);
5741da177e4SLinus Torvalds 	if (seq_num > sd->sd_max) {
5751da177e4SLinus Torvalds 		if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
5761da177e4SLinus Torvalds 			memset(sd->sd_win,0,sizeof(sd->sd_win));
5771da177e4SLinus Torvalds 			sd->sd_max = seq_num;
5781da177e4SLinus Torvalds 		} else while (sd->sd_max < seq_num) {
5791da177e4SLinus Torvalds 			sd->sd_max++;
5801da177e4SLinus Torvalds 			__clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win);
5811da177e4SLinus Torvalds 		}
5821da177e4SLinus Torvalds 		__set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
5831da177e4SLinus Torvalds 		goto ok;
5841da177e4SLinus Torvalds 	} else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) {
5851da177e4SLinus Torvalds 		goto drop;
5861da177e4SLinus Torvalds 	}
5871da177e4SLinus Torvalds 	/* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */
5881da177e4SLinus Torvalds 	if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
5891da177e4SLinus Torvalds 		goto drop;
5901da177e4SLinus Torvalds ok:
5911da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
5921da177e4SLinus Torvalds 	return 1;
5931da177e4SLinus Torvalds drop:
5941da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
5951da177e4SLinus Torvalds 	return 0;
5961da177e4SLinus Torvalds }
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i)
5991da177e4SLinus Torvalds {
6001da177e4SLinus Torvalds 	return (i + 3 ) & ~3;
6011da177e4SLinus Torvalds }
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds static inline int
6041da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
6051da177e4SLinus Torvalds {
6061da177e4SLinus Torvalds 	int l;
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6091da177e4SLinus Torvalds 		return -1;
6101da177e4SLinus Torvalds 	o->len = ntohl(svc_getu32(argv));
6111da177e4SLinus Torvalds 	l = round_up_to_quad(o->len);
6121da177e4SLinus Torvalds 	if (argv->iov_len < l)
6131da177e4SLinus Torvalds 		return -1;
6141da177e4SLinus Torvalds 	o->data = argv->iov_base;
6151da177e4SLinus Torvalds 	argv->iov_base += l;
6161da177e4SLinus Torvalds 	argv->iov_len -= l;
6171da177e4SLinus Torvalds 	return 0;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds static inline int
6211da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	u32 *p;
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 	if (resv->iov_len + 4 > PAGE_SIZE)
6261da177e4SLinus Torvalds 		return -1;
6271da177e4SLinus Torvalds 	svc_putu32(resv, htonl(o->len));
6281da177e4SLinus Torvalds 	p = resv->iov_base + resv->iov_len;
6291da177e4SLinus Torvalds 	resv->iov_len += round_up_to_quad(o->len);
6301da177e4SLinus Torvalds 	if (resv->iov_len > PAGE_SIZE)
6311da177e4SLinus Torvalds 		return -1;
6321da177e4SLinus Torvalds 	memcpy(p, o->data, o->len);
6331da177e4SLinus Torvalds 	memset((u8 *)p + o->len, 0, round_up_to_quad(o->len) - o->len);
6341da177e4SLinus Torvalds 	return 0;
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds /* Verify the checksum on the header and return SVC_OK on success.
6381da177e4SLinus Torvalds  * Otherwise, return SVC_DROP (in the case of a bad sequence number)
6391da177e4SLinus Torvalds  * or return SVC_DENIED and indicate error in authp.
6401da177e4SLinus Torvalds  */
6411da177e4SLinus Torvalds static int
6421da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
6431da177e4SLinus Torvalds 		  u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp)
6441da177e4SLinus Torvalds {
6451da177e4SLinus Torvalds 	struct gss_ctx		*ctx_id = rsci->mechctx;
6461da177e4SLinus Torvalds 	struct xdr_buf		rpchdr;
6471da177e4SLinus Torvalds 	struct xdr_netobj	checksum;
6481da177e4SLinus Torvalds 	u32			flavor = 0;
6491da177e4SLinus Torvalds 	struct kvec		*argv = &rqstp->rq_arg.head[0];
6501da177e4SLinus Torvalds 	struct kvec		iov;
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds 	/* data to compute the checksum over: */
6531da177e4SLinus Torvalds 	iov.iov_base = rpcstart;
6541da177e4SLinus Torvalds 	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
6551da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &rpchdr);
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
6581da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6591da177e4SLinus Torvalds 		return SVC_DENIED;
6601da177e4SLinus Torvalds 	flavor = ntohl(svc_getu32(argv));
6611da177e4SLinus Torvalds 	if (flavor != RPC_AUTH_GSS)
6621da177e4SLinus Torvalds 		return SVC_DENIED;
6631da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &checksum))
6641da177e4SLinus Torvalds 		return SVC_DENIED;
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds 	if (rqstp->rq_deferred) /* skip verification of revisited request */
6671da177e4SLinus Torvalds 		return SVC_OK;
66800fd6e14SJ. Bruce Fields 	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
6691da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
6701da177e4SLinus Torvalds 		return SVC_DENIED;
6711da177e4SLinus Torvalds 	}
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	if (gc->gc_seq > MAXSEQ) {
6741da177e4SLinus Torvalds 		dprintk("RPC:      svcauth_gss: discarding request with large sequence number %d\n",
6751da177e4SLinus Torvalds 				gc->gc_seq);
6761da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
6771da177e4SLinus Torvalds 		return SVC_DENIED;
6781da177e4SLinus Torvalds 	}
6791da177e4SLinus Torvalds 	if (!gss_check_seq_num(rsci, gc->gc_seq)) {
6801da177e4SLinus Torvalds 		dprintk("RPC:      svcauth_gss: discarding request with old sequence number %d\n",
6811da177e4SLinus Torvalds 				gc->gc_seq);
6821da177e4SLinus Torvalds 		return SVC_DROP;
6831da177e4SLinus Torvalds 	}
6841da177e4SLinus Torvalds 	return SVC_OK;
6851da177e4SLinus Torvalds }
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds static int
688822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp)
689822f1005SAndy Adamson {
690822f1005SAndy Adamson 	u32     *p;
691822f1005SAndy Adamson 
692822f1005SAndy Adamson 	svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_NULL));
693822f1005SAndy Adamson 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
694822f1005SAndy Adamson 	/* don't really need to check if head->iov_len > PAGE_SIZE ... */
695822f1005SAndy Adamson 	*p++ = 0;
696822f1005SAndy Adamson 	if (!xdr_ressize_check(rqstp, p))
697822f1005SAndy Adamson 		return -1;
698822f1005SAndy Adamson 	return 0;
699822f1005SAndy Adamson }
700822f1005SAndy Adamson 
701822f1005SAndy Adamson static int
7021da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
7031da177e4SLinus Torvalds {
7041da177e4SLinus Torvalds 	u32			xdr_seq;
7051da177e4SLinus Torvalds 	u32			maj_stat;
7061da177e4SLinus Torvalds 	struct xdr_buf		verf_data;
7071da177e4SLinus Torvalds 	struct xdr_netobj	mic;
7081da177e4SLinus Torvalds 	u32			*p;
7091da177e4SLinus Torvalds 	struct kvec		iov;
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS));
7121da177e4SLinus Torvalds 	xdr_seq = htonl(seq);
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds 	iov.iov_base = &xdr_seq;
7151da177e4SLinus Torvalds 	iov.iov_len = sizeof(xdr_seq);
7161da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &verf_data);
7171da177e4SLinus Torvalds 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
7181da177e4SLinus Torvalds 	mic.data = (u8 *)(p + 1);
71900fd6e14SJ. Bruce Fields 	maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
7201da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
7211da177e4SLinus Torvalds 		return -1;
7221da177e4SLinus Torvalds 	*p++ = htonl(mic.len);
7231da177e4SLinus Torvalds 	memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
7241da177e4SLinus Torvalds 	p += XDR_QUADLEN(mic.len);
7251da177e4SLinus Torvalds 	if (!xdr_ressize_check(rqstp, p))
7261da177e4SLinus Torvalds 		return -1;
7271da177e4SLinus Torvalds 	return 0;
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds struct gss_domain {
7311da177e4SLinus Torvalds 	struct auth_domain	h;
7321da177e4SLinus Torvalds 	u32			pseudoflavor;
7331da177e4SLinus Torvalds };
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds static struct auth_domain *
7361da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
7371da177e4SLinus Torvalds {
7381da177e4SLinus Torvalds 	char *name;
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	name = gss_service_to_auth_domain_name(ctx->mech_type, svc);
7411da177e4SLinus Torvalds 	if (!name)
7421da177e4SLinus Torvalds 		return NULL;
7431da177e4SLinus Torvalds 	return auth_domain_find(name);
7441da177e4SLinus Torvalds }
7451da177e4SLinus Torvalds 
746efc36aa5SNeilBrown static struct auth_ops svcauthops_gss;
747efc36aa5SNeilBrown 
7481da177e4SLinus Torvalds int
7491da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
7501da177e4SLinus Torvalds {
7511da177e4SLinus Torvalds 	struct gss_domain	*new;
7521da177e4SLinus Torvalds 	struct auth_domain	*test;
7531da177e4SLinus Torvalds 	int			stat = -ENOMEM;
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 	new = kmalloc(sizeof(*new), GFP_KERNEL);
7561da177e4SLinus Torvalds 	if (!new)
7571da177e4SLinus Torvalds 		goto out;
758efc36aa5SNeilBrown 	kref_init(&new->h.ref);
7591da177e4SLinus Torvalds 	new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL);
7601da177e4SLinus Torvalds 	if (!new->h.name)
7611da177e4SLinus Torvalds 		goto out_free_dom;
7621da177e4SLinus Torvalds 	strcpy(new->h.name, name);
763efc36aa5SNeilBrown 	new->h.flavour = &svcauthops_gss;
7641da177e4SLinus Torvalds 	new->pseudoflavor = pseudoflavor;
7651da177e4SLinus Torvalds 
766efc36aa5SNeilBrown 	test = auth_domain_lookup(name, &new->h);
767efc36aa5SNeilBrown 	if (test != &new->h) { /* XXX Duplicate registration? */
7681da177e4SLinus Torvalds 		auth_domain_put(&new->h);
769efc36aa5SNeilBrown 		/* dangling ref-count... */
7701da177e4SLinus Torvalds 		goto out;
7711da177e4SLinus Torvalds 	}
7721da177e4SLinus Torvalds 	return 0;
7731da177e4SLinus Torvalds 
7741da177e4SLinus Torvalds out_free_dom:
7751da177e4SLinus Torvalds 	kfree(new);
7761da177e4SLinus Torvalds out:
7771da177e4SLinus Torvalds 	return stat;
7781da177e4SLinus Torvalds }
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds static inline int
7831da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
7841da177e4SLinus Torvalds {
7851da177e4SLinus Torvalds 	u32     raw;
7861da177e4SLinus Torvalds 	int     status;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
7891da177e4SLinus Torvalds 	if (status)
7901da177e4SLinus Torvalds 		return status;
7911da177e4SLinus Torvalds 	*obj = ntohl(raw);
7921da177e4SLinus Torvalds 	return 0;
7931da177e4SLinus Torvalds }
7941da177e4SLinus Torvalds 
7951da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client.
7961da177e4SLinus Torvalds  * Obstacles:
7971da177e4SLinus Torvalds  *	The client shouldn't malloc(), would have to pass in own memory.
7981da177e4SLinus Torvalds  *	The server uses base of head iovec as read pointer, while the
7991da177e4SLinus Torvalds  *	client uses separate pointer. */
8001da177e4SLinus Torvalds static int
8011da177e4SLinus Torvalds unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
8021da177e4SLinus Torvalds {
8031da177e4SLinus Torvalds 	int stat = -EINVAL;
8041da177e4SLinus Torvalds 	u32 integ_len, maj_stat;
8051da177e4SLinus Torvalds 	struct xdr_netobj mic;
8061da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds 	integ_len = ntohl(svc_getu32(&buf->head[0]));
8091da177e4SLinus Torvalds 	if (integ_len & 3)
8101da177e4SLinus Torvalds 		goto out;
8111da177e4SLinus Torvalds 	if (integ_len > buf->len)
8121da177e4SLinus Torvalds 		goto out;
8131da177e4SLinus Torvalds 	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
8141da177e4SLinus Torvalds 		BUG();
8151da177e4SLinus Torvalds 	/* copy out mic... */
8161da177e4SLinus Torvalds 	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
8171da177e4SLinus Torvalds 		BUG();
8181da177e4SLinus Torvalds 	if (mic.len > RPC_MAX_AUTH_SIZE)
8191da177e4SLinus Torvalds 		goto out;
8201da177e4SLinus Torvalds 	mic.data = kmalloc(mic.len, GFP_KERNEL);
8211da177e4SLinus Torvalds 	if (!mic.data)
8221da177e4SLinus Torvalds 		goto out;
8231da177e4SLinus Torvalds 	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
8241da177e4SLinus Torvalds 		goto out;
82500fd6e14SJ. Bruce Fields 	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
8261da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
8271da177e4SLinus Torvalds 		goto out;
8281da177e4SLinus Torvalds 	if (ntohl(svc_getu32(&buf->head[0])) != seq)
8291da177e4SLinus Torvalds 		goto out;
8301da177e4SLinus Torvalds 	stat = 0;
8311da177e4SLinus Torvalds out:
8321da177e4SLinus Torvalds 	return stat;
8331da177e4SLinus Torvalds }
8341da177e4SLinus Torvalds 
8357c9fdcfbSJ. Bruce Fields static inline int
8367c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf)
8377c9fdcfbSJ. Bruce Fields {
8387c9fdcfbSJ. Bruce Fields 	return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
8397c9fdcfbSJ. Bruce Fields }
8407c9fdcfbSJ. Bruce Fields 
8417c9fdcfbSJ. Bruce Fields static void
8427c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad)
8437c9fdcfbSJ. Bruce Fields {
8447c9fdcfbSJ. Bruce Fields 	if (buf->page_len == 0) {
8457c9fdcfbSJ. Bruce Fields 		/* We need to adjust head and buf->len in tandem in this
8467c9fdcfbSJ. Bruce Fields 		 * case to make svc_defer() work--it finds the original
8477c9fdcfbSJ. Bruce Fields 		 * buffer start using buf->len - buf->head[0].iov_len. */
8487c9fdcfbSJ. Bruce Fields 		buf->head[0].iov_len -= pad;
8497c9fdcfbSJ. Bruce Fields 	}
8507c9fdcfbSJ. Bruce Fields }
8517c9fdcfbSJ. Bruce Fields 
8527c9fdcfbSJ. Bruce Fields static int
8537c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
8547c9fdcfbSJ. Bruce Fields {
8557c9fdcfbSJ. Bruce Fields 	u32 priv_len, maj_stat;
8567c9fdcfbSJ. Bruce Fields 	int pad, saved_len, remaining_len, offset;
8577c9fdcfbSJ. Bruce Fields 
8587c9fdcfbSJ. Bruce Fields 	rqstp->rq_sendfile_ok = 0;
8597c9fdcfbSJ. Bruce Fields 
8607c9fdcfbSJ. Bruce Fields 	priv_len = ntohl(svc_getu32(&buf->head[0]));
8617c9fdcfbSJ. Bruce Fields 	if (rqstp->rq_deferred) {
8627c9fdcfbSJ. Bruce Fields 		/* Already decrypted last time through! The sequence number
8637c9fdcfbSJ. Bruce Fields 		 * check at out_seq is unnecessary but harmless: */
8647c9fdcfbSJ. Bruce Fields 		goto out_seq;
8657c9fdcfbSJ. Bruce Fields 	}
8667c9fdcfbSJ. Bruce Fields 	/* buf->len is the number of bytes from the original start of the
8677c9fdcfbSJ. Bruce Fields 	 * request to the end, where head[0].iov_len is just the bytes
8687c9fdcfbSJ. Bruce Fields 	 * not yet read from the head, so these two values are different: */
8697c9fdcfbSJ. Bruce Fields 	remaining_len = total_buf_len(buf);
8707c9fdcfbSJ. Bruce Fields 	if (priv_len > remaining_len)
8717c9fdcfbSJ. Bruce Fields 		return -EINVAL;
8727c9fdcfbSJ. Bruce Fields 	pad = remaining_len - priv_len;
8737c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
8747c9fdcfbSJ. Bruce Fields 	fix_priv_head(buf, pad);
8757c9fdcfbSJ. Bruce Fields 
8767c9fdcfbSJ. Bruce Fields 	/* Maybe it would be better to give gss_unwrap a length parameter: */
8777c9fdcfbSJ. Bruce Fields 	saved_len = buf->len;
8787c9fdcfbSJ. Bruce Fields 	buf->len = priv_len;
8797c9fdcfbSJ. Bruce Fields 	maj_stat = gss_unwrap(ctx, 0, buf);
8807c9fdcfbSJ. Bruce Fields 	pad = priv_len - buf->len;
8817c9fdcfbSJ. Bruce Fields 	buf->len = saved_len;
8827c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
8837c9fdcfbSJ. Bruce Fields 	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
8847c9fdcfbSJ. Bruce Fields 	 * In the krb5p case, at least, the data ends up offset, so we need to
8857c9fdcfbSJ. Bruce Fields 	 * move it around. */
8867c9fdcfbSJ. Bruce Fields 	/* XXX: This is very inefficient.  It would be better to either do
8877c9fdcfbSJ. Bruce Fields 	 * this while we encrypt, or maybe in the receive code, if we can peak
8887c9fdcfbSJ. Bruce Fields 	 * ahead and work out the service and mechanism there. */
8897c9fdcfbSJ. Bruce Fields 	offset = buf->head[0].iov_len % 4;
8907c9fdcfbSJ. Bruce Fields 	if (offset) {
8917c9fdcfbSJ. Bruce Fields 		buf->buflen = RPCSVC_MAXPAYLOAD;
8927c9fdcfbSJ. Bruce Fields 		xdr_shift_buf(buf, offset);
8937c9fdcfbSJ. Bruce Fields 		fix_priv_head(buf, pad);
8947c9fdcfbSJ. Bruce Fields 	}
8957c9fdcfbSJ. Bruce Fields 	if (maj_stat != GSS_S_COMPLETE)
8967c9fdcfbSJ. Bruce Fields 		return -EINVAL;
8977c9fdcfbSJ. Bruce Fields out_seq:
8987c9fdcfbSJ. Bruce Fields 	if (ntohl(svc_getu32(&buf->head[0])) != seq)
8997c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9007c9fdcfbSJ. Bruce Fields 	return 0;
9017c9fdcfbSJ. Bruce Fields }
9027c9fdcfbSJ. Bruce Fields 
9031da177e4SLinus Torvalds struct gss_svc_data {
9041da177e4SLinus Torvalds 	/* decoded gss client cred: */
9051da177e4SLinus Torvalds 	struct rpc_gss_wire_cred	clcred;
9061da177e4SLinus Torvalds 	/* pointer to the beginning of the procedure-specific results,
9071da177e4SLinus Torvalds 	 * which may be encrypted/checksummed in svcauth_gss_release: */
9081da177e4SLinus Torvalds 	u32				*body_start;
9091da177e4SLinus Torvalds 	struct rsc			*rsci;
9101da177e4SLinus Torvalds };
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds static int
9131da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp)
9141da177e4SLinus Torvalds {
9151da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
9161da177e4SLinus Torvalds 	struct rsc *rsci = svcdata->rsci;
9171da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &svcdata->clcred;
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds 	rqstp->rq_client = find_gss_auth_domain(rsci->mechctx, gc->gc_svc);
9201da177e4SLinus Torvalds 	if (rqstp->rq_client == NULL)
9211da177e4SLinus Torvalds 		return SVC_DENIED;
9221da177e4SLinus Torvalds 	return SVC_OK;
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds 
92591a4762eSKevin Coffman static inline int
92691a4762eSKevin Coffman gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip)
92791a4762eSKevin Coffman {
92891a4762eSKevin Coffman 	struct rsc *rsci;
92991a4762eSKevin Coffman 
93091a4762eSKevin Coffman 	if (rsip->major_status != GSS_S_COMPLETE)
93191a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
93291a4762eSKevin Coffman 	rsci = gss_svc_searchbyctx(&rsip->out_handle);
93391a4762eSKevin Coffman 	if (rsci == NULL) {
93491a4762eSKevin Coffman 		rsip->major_status = GSS_S_NO_CONTEXT;
93591a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
93691a4762eSKevin Coffman 	}
93791a4762eSKevin Coffman 	return gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
93891a4762eSKevin Coffman }
93991a4762eSKevin Coffman 
9401da177e4SLinus Torvalds /*
9411da177e4SLinus Torvalds  * Accept an rpcsec packet.
9421da177e4SLinus Torvalds  * If context establishment, punt to user space
9431da177e4SLinus Torvalds  * If data exchange, verify/decrypt
9441da177e4SLinus Torvalds  * If context destruction, handle here
9451da177e4SLinus Torvalds  * In the context establishment and destruction case we encode
9461da177e4SLinus Torvalds  * response here and return SVC_COMPLETE.
9471da177e4SLinus Torvalds  */
9481da177e4SLinus Torvalds static int
9491da177e4SLinus Torvalds svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	struct kvec	*argv = &rqstp->rq_arg.head[0];
9521da177e4SLinus Torvalds 	struct kvec	*resv = &rqstp->rq_res.head[0];
9531da177e4SLinus Torvalds 	u32		crlen;
9541da177e4SLinus Torvalds 	struct xdr_netobj tmpobj;
9551da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
9561da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc;
9571da177e4SLinus Torvalds 	struct rsc	*rsci = NULL;
9581da177e4SLinus Torvalds 	struct rsi	*rsip, rsikey;
9591da177e4SLinus Torvalds 	u32		*rpcstart;
9601da177e4SLinus Torvalds 	u32		*reject_stat = resv->iov_base + resv->iov_len;
9611da177e4SLinus Torvalds 	int		ret;
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 	dprintk("RPC:      svcauth_gss: argv->iov_len = %zd\n",argv->iov_len);
9641da177e4SLinus Torvalds 
9651da177e4SLinus Torvalds 	*authp = rpc_autherr_badcred;
9661da177e4SLinus Torvalds 	if (!svcdata)
9671da177e4SLinus Torvalds 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
9681da177e4SLinus Torvalds 	if (!svcdata)
9691da177e4SLinus Torvalds 		goto auth_err;
9701da177e4SLinus Torvalds 	rqstp->rq_auth_data = svcdata;
9711da177e4SLinus Torvalds 	svcdata->body_start = NULL;
9721da177e4SLinus Torvalds 	svcdata->rsci = NULL;
9731da177e4SLinus Torvalds 	gc = &svcdata->clcred;
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	/* start of rpc packet is 7 u32's back from here:
9761da177e4SLinus Torvalds 	 * xid direction rpcversion prog vers proc flavour
9771da177e4SLinus Torvalds 	 */
9781da177e4SLinus Torvalds 	rpcstart = argv->iov_base;
9791da177e4SLinus Torvalds 	rpcstart -= 7;
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds 	/* credential is:
9821da177e4SLinus Torvalds 	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
9831da177e4SLinus Torvalds 	 * at least 5 u32s, and is preceeded by length, so that makes 6.
9841da177e4SLinus Torvalds 	 */
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	if (argv->iov_len < 5 * 4)
9871da177e4SLinus Torvalds 		goto auth_err;
9881da177e4SLinus Torvalds 	crlen = ntohl(svc_getu32(argv));
9891da177e4SLinus Torvalds 	if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION)
9901da177e4SLinus Torvalds 		goto auth_err;
9911da177e4SLinus Torvalds 	gc->gc_proc = ntohl(svc_getu32(argv));
9921da177e4SLinus Torvalds 	gc->gc_seq = ntohl(svc_getu32(argv));
9931da177e4SLinus Torvalds 	gc->gc_svc = ntohl(svc_getu32(argv));
9941da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &gc->gc_ctx))
9951da177e4SLinus Torvalds 		goto auth_err;
9961da177e4SLinus Torvalds 	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
9971da177e4SLinus Torvalds 		goto auth_err;
9981da177e4SLinus Torvalds 
9991da177e4SLinus Torvalds 	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
10001da177e4SLinus Torvalds 		goto auth_err;
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds 	/*
10031da177e4SLinus Torvalds 	 * We've successfully parsed the credential. Let's check out the
10041da177e4SLinus Torvalds 	 * verifier.  An AUTH_NULL verifier is allowed (and required) for
10051da177e4SLinus Torvalds 	 * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for
10061da177e4SLinus Torvalds 	 * PROC_DATA and PROC_DESTROY.
10071da177e4SLinus Torvalds 	 *
10081da177e4SLinus Torvalds 	 * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length).
10091da177e4SLinus Torvalds 	 * AUTH_RPCSEC_GSS verifier is:
10101da177e4SLinus Torvalds 	 *   6 (AUTH_RPCSEC_GSS), length, checksum.
10111da177e4SLinus Torvalds 	 * checksum is calculated over rpcheader from xid up to here.
10121da177e4SLinus Torvalds 	 */
10131da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
10141da177e4SLinus Torvalds 	switch (gc->gc_proc) {
10151da177e4SLinus Torvalds 	case RPC_GSS_PROC_INIT:
10161da177e4SLinus Torvalds 	case RPC_GSS_PROC_CONTINUE_INIT:
10171da177e4SLinus Torvalds 		if (argv->iov_len < 2 * 4)
10181da177e4SLinus Torvalds 			goto auth_err;
10191da177e4SLinus Torvalds 		if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL)
10201da177e4SLinus Torvalds 			goto auth_err;
10211da177e4SLinus Torvalds 		if (ntohl(svc_getu32(argv)) != 0)
10221da177e4SLinus Torvalds 			goto auth_err;
10231da177e4SLinus Torvalds 		break;
10241da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
10251da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
10261da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
10271da177e4SLinus Torvalds 		rsci = gss_svc_searchbyctx(&gc->gc_ctx);
10281da177e4SLinus Torvalds 		if (!rsci)
10291da177e4SLinus Torvalds 			goto auth_err;
10301da177e4SLinus Torvalds 		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
10311da177e4SLinus Torvalds 		case SVC_OK:
10321da177e4SLinus Torvalds 			break;
10331da177e4SLinus Torvalds 		case SVC_DENIED:
10341da177e4SLinus Torvalds 			goto auth_err;
10351da177e4SLinus Torvalds 		case SVC_DROP:
10361da177e4SLinus Torvalds 			goto drop;
10371da177e4SLinus Torvalds 		}
10381da177e4SLinus Torvalds 		break;
10391da177e4SLinus Torvalds 	default:
10401da177e4SLinus Torvalds 		*authp = rpc_autherr_rejectedcred;
10411da177e4SLinus Torvalds 		goto auth_err;
10421da177e4SLinus Torvalds 	}
10431da177e4SLinus Torvalds 
10441da177e4SLinus Torvalds 	/* now act upon the command: */
10451da177e4SLinus Torvalds 	switch (gc->gc_proc) {
10461da177e4SLinus Torvalds 	case RPC_GSS_PROC_INIT:
10471da177e4SLinus Torvalds 	case RPC_GSS_PROC_CONTINUE_INIT:
10481da177e4SLinus Torvalds 		*authp = rpc_autherr_badcred;
10491da177e4SLinus Torvalds 		if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
10501da177e4SLinus Torvalds 			goto auth_err;
10511da177e4SLinus Torvalds 		memset(&rsikey, 0, sizeof(rsikey));
10521da177e4SLinus Torvalds 		if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
10531da177e4SLinus Torvalds 			goto drop;
10541da177e4SLinus Torvalds 		*authp = rpc_autherr_badverf;
10551da177e4SLinus Torvalds 		if (svc_safe_getnetobj(argv, &tmpobj)) {
10561da177e4SLinus Torvalds 			kfree(rsikey.in_handle.data);
10571da177e4SLinus Torvalds 			goto auth_err;
10581da177e4SLinus Torvalds 		}
10591da177e4SLinus Torvalds 		if (dup_netobj(&rsikey.in_token, &tmpobj)) {
10601da177e4SLinus Torvalds 			kfree(rsikey.in_handle.data);
10611da177e4SLinus Torvalds 			goto drop;
10621da177e4SLinus Torvalds 		}
10631da177e4SLinus Torvalds 
1064d4d11ea9SNeilBrown 		rsip = rsi_lookup(&rsikey);
10651da177e4SLinus Torvalds 		rsi_free(&rsikey);
10661da177e4SLinus Torvalds 		if (!rsip) {
10671da177e4SLinus Torvalds 			goto drop;
10681da177e4SLinus Torvalds 		}
10691da177e4SLinus Torvalds 		switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
10701da177e4SLinus Torvalds 		case -EAGAIN:
10711da177e4SLinus Torvalds 			goto drop;
10721da177e4SLinus Torvalds 		case -ENOENT:
10731da177e4SLinus Torvalds 			goto drop;
10741da177e4SLinus Torvalds 		case 0:
107591a4762eSKevin Coffman 			if (gss_write_init_verf(rqstp, rsip))
10761da177e4SLinus Torvalds 				goto drop;
10771da177e4SLinus Torvalds 			if (resv->iov_len + 4 > PAGE_SIZE)
10781da177e4SLinus Torvalds 				goto drop;
10791da177e4SLinus Torvalds 			svc_putu32(resv, rpc_success);
10801da177e4SLinus Torvalds 			if (svc_safe_putnetobj(resv, &rsip->out_handle))
10811da177e4SLinus Torvalds 				goto drop;
10821da177e4SLinus Torvalds 			if (resv->iov_len + 3 * 4 > PAGE_SIZE)
10831da177e4SLinus Torvalds 				goto drop;
10841da177e4SLinus Torvalds 			svc_putu32(resv, htonl(rsip->major_status));
10851da177e4SLinus Torvalds 			svc_putu32(resv, htonl(rsip->minor_status));
10861da177e4SLinus Torvalds 			svc_putu32(resv, htonl(GSS_SEQ_WIN));
10871da177e4SLinus Torvalds 			if (svc_safe_putnetobj(resv, &rsip->out_token))
10881da177e4SLinus Torvalds 				goto drop;
10891da177e4SLinus Torvalds 			rqstp->rq_client = NULL;
10901da177e4SLinus Torvalds 		}
10911da177e4SLinus Torvalds 		goto complete;
10921da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
10931da177e4SLinus Torvalds 		set_bit(CACHE_NEGATIVE, &rsci->h.flags);
10941da177e4SLinus Torvalds 		if (resv->iov_len + 4 > PAGE_SIZE)
10951da177e4SLinus Torvalds 			goto drop;
10961da177e4SLinus Torvalds 		svc_putu32(resv, rpc_success);
10971da177e4SLinus Torvalds 		goto complete;
10981da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
10991da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
11001da177e4SLinus Torvalds 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
11011da177e4SLinus Torvalds 			goto auth_err;
11021da177e4SLinus Torvalds 		rqstp->rq_cred = rsci->cred;
11031da177e4SLinus Torvalds 		get_group_info(rsci->cred.cr_group_info);
11041da177e4SLinus Torvalds 		*authp = rpc_autherr_badcred;
11051da177e4SLinus Torvalds 		switch (gc->gc_svc) {
11061da177e4SLinus Torvalds 		case RPC_GSS_SVC_NONE:
11071da177e4SLinus Torvalds 			break;
11081da177e4SLinus Torvalds 		case RPC_GSS_SVC_INTEGRITY:
11091da177e4SLinus Torvalds 			if (unwrap_integ_data(&rqstp->rq_arg,
11101da177e4SLinus Torvalds 					gc->gc_seq, rsci->mechctx))
11111da177e4SLinus Torvalds 				goto auth_err;
11121da177e4SLinus Torvalds 			/* placeholders for length and seq. number: */
11131da177e4SLinus Torvalds 			svcdata->body_start = resv->iov_base + resv->iov_len;
11141da177e4SLinus Torvalds 			svc_putu32(resv, 0);
11151da177e4SLinus Torvalds 			svc_putu32(resv, 0);
11161da177e4SLinus Torvalds 			break;
11171da177e4SLinus Torvalds 		case RPC_GSS_SVC_PRIVACY:
11187c9fdcfbSJ. Bruce Fields 			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
11197c9fdcfbSJ. Bruce Fields 					gc->gc_seq, rsci->mechctx))
11207c9fdcfbSJ. Bruce Fields 				goto auth_err;
11217c9fdcfbSJ. Bruce Fields 			/* placeholders for length and seq. number: */
11227c9fdcfbSJ. Bruce Fields 			svcdata->body_start = resv->iov_base + resv->iov_len;
11237c9fdcfbSJ. Bruce Fields 			svc_putu32(resv, 0);
11247c9fdcfbSJ. Bruce Fields 			svc_putu32(resv, 0);
11257c9fdcfbSJ. Bruce Fields 			break;
11261da177e4SLinus Torvalds 		default:
11271da177e4SLinus Torvalds 			goto auth_err;
11281da177e4SLinus Torvalds 		}
11291da177e4SLinus Torvalds 		svcdata->rsci = rsci;
11301da177e4SLinus Torvalds 		cache_get(&rsci->h);
11311da177e4SLinus Torvalds 		ret = SVC_OK;
11321da177e4SLinus Torvalds 		goto out;
11331da177e4SLinus Torvalds 	}
11341da177e4SLinus Torvalds auth_err:
11351da177e4SLinus Torvalds 	/* Restore write pointer to original value: */
11361da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, reject_stat);
11371da177e4SLinus Torvalds 	ret = SVC_DENIED;
11381da177e4SLinus Torvalds 	goto out;
11391da177e4SLinus Torvalds complete:
11401da177e4SLinus Torvalds 	ret = SVC_COMPLETE;
11411da177e4SLinus Torvalds 	goto out;
11421da177e4SLinus Torvalds drop:
11431da177e4SLinus Torvalds 	ret = SVC_DROP;
11441da177e4SLinus Torvalds out:
11451da177e4SLinus Torvalds 	if (rsci)
1146baab935fSNeilBrown 		cache_put(&rsci->h, &rsc_cache);
11471da177e4SLinus Torvalds 	return ret;
11481da177e4SLinus Torvalds }
11491da177e4SLinus Torvalds 
1150e142ede8SJ. Bruce Fields static inline int
1151e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
11521da177e4SLinus Torvalds {
11531da177e4SLinus Torvalds 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
11541da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
11551da177e4SLinus Torvalds 	struct xdr_buf *resbuf = &rqstp->rq_res;
11561da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
11571da177e4SLinus Torvalds 	struct xdr_netobj mic;
11581da177e4SLinus Torvalds 	struct kvec *resv;
11591da177e4SLinus Torvalds 	u32 *p;
11601da177e4SLinus Torvalds 	int integ_offset, integ_len;
11611da177e4SLinus Torvalds 	int stat = -EINVAL;
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds 	p = gsd->body_start;
11641da177e4SLinus Torvalds 	gsd->body_start = NULL;
11651da177e4SLinus Torvalds 	/* move accept_stat to right place: */
11661da177e4SLinus Torvalds 	memcpy(p, p + 2, 4);
11677c9fdcfbSJ. Bruce Fields 	/* Don't wrap in failure case: */
11687c9fdcfbSJ. Bruce Fields 	/* Counting on not getting here if call was not even accepted! */
11691da177e4SLinus Torvalds 	if (*p != rpc_success) {
11701da177e4SLinus Torvalds 		resbuf->head[0].iov_len -= 2 * 4;
11711da177e4SLinus Torvalds 		goto out;
11721da177e4SLinus Torvalds 	}
11731da177e4SLinus Torvalds 	p++;
11741da177e4SLinus Torvalds 	integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
11751da177e4SLinus Torvalds 	integ_len = resbuf->len - integ_offset;
11761da177e4SLinus Torvalds 	BUG_ON(integ_len % 4);
11771da177e4SLinus Torvalds 	*p++ = htonl(integ_len);
11781da177e4SLinus Torvalds 	*p++ = htonl(gc->gc_seq);
11791da177e4SLinus Torvalds 	if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset,
11801da177e4SLinus Torvalds 				integ_len))
11811da177e4SLinus Torvalds 		BUG();
11821da177e4SLinus Torvalds 	if (resbuf->page_len == 0
1183dfee55f0SNeilBrown 			&& resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE
11841da177e4SLinus Torvalds 			< PAGE_SIZE) {
11851da177e4SLinus Torvalds 		BUG_ON(resbuf->tail[0].iov_len);
11861da177e4SLinus Torvalds 		/* Use head for everything */
11871da177e4SLinus Torvalds 		resv = &resbuf->head[0];
11881da177e4SLinus Torvalds 	} else if (resbuf->tail[0].iov_base == NULL) {
1189e142ede8SJ. Bruce Fields 		if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1190dfee55f0SNeilBrown 			goto out_err;
1191e142ede8SJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
1192dfee55f0SNeilBrown 						+ resbuf->head[0].iov_len;
11931da177e4SLinus Torvalds 		resbuf->tail[0].iov_len = 0;
1194dfee55f0SNeilBrown 		rqstp->rq_restailpage = 0;
11951da177e4SLinus Torvalds 		resv = &resbuf->tail[0];
11961da177e4SLinus Torvalds 	} else {
11971da177e4SLinus Torvalds 		resv = &resbuf->tail[0];
11981da177e4SLinus Torvalds 	}
11991da177e4SLinus Torvalds 	mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
120000fd6e14SJ. Bruce Fields 	if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))
12011da177e4SLinus Torvalds 		goto out_err;
12021da177e4SLinus Torvalds 	svc_putu32(resv, htonl(mic.len));
12031da177e4SLinus Torvalds 	memset(mic.data + mic.len, 0,
12041da177e4SLinus Torvalds 			round_up_to_quad(mic.len) - mic.len);
12051da177e4SLinus Torvalds 	resv->iov_len += XDR_QUADLEN(mic.len) << 2;
12061da177e4SLinus Torvalds 	/* not strictly required: */
12071da177e4SLinus Torvalds 	resbuf->len += XDR_QUADLEN(mic.len) << 2;
12081da177e4SLinus Torvalds 	BUG_ON(resv->iov_len > PAGE_SIZE);
1209e142ede8SJ. Bruce Fields out:
1210e142ede8SJ. Bruce Fields 	stat = 0;
1211e142ede8SJ. Bruce Fields out_err:
1212e142ede8SJ. Bruce Fields 	return stat;
1213e142ede8SJ. Bruce Fields }
1214e142ede8SJ. Bruce Fields 
12157c9fdcfbSJ. Bruce Fields static inline int
12167c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
12177c9fdcfbSJ. Bruce Fields {
12187c9fdcfbSJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
12197c9fdcfbSJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
12207c9fdcfbSJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
12217c9fdcfbSJ. Bruce Fields 	struct page **inpages = NULL;
12227c9fdcfbSJ. Bruce Fields 	u32 *p;
12237c9fdcfbSJ. Bruce Fields 	int offset, *len;
12247c9fdcfbSJ. Bruce Fields 	int pad;
12257c9fdcfbSJ. Bruce Fields 
12267c9fdcfbSJ. Bruce Fields 	p = gsd->body_start;
12277c9fdcfbSJ. Bruce Fields 	gsd->body_start = NULL;
12287c9fdcfbSJ. Bruce Fields 	/* move accept_stat to right place: */
12297c9fdcfbSJ. Bruce Fields 	memcpy(p, p + 2, 4);
12307c9fdcfbSJ. Bruce Fields 	/* Don't wrap in failure case: */
12317c9fdcfbSJ. Bruce Fields 	/* Counting on not getting here if call was not even accepted! */
12327c9fdcfbSJ. Bruce Fields 	if (*p != rpc_success) {
12337c9fdcfbSJ. Bruce Fields 		resbuf->head[0].iov_len -= 2 * 4;
12347c9fdcfbSJ. Bruce Fields 		return 0;
12357c9fdcfbSJ. Bruce Fields 	}
12367c9fdcfbSJ. Bruce Fields 	p++;
12377c9fdcfbSJ. Bruce Fields 	len = p++;
12387c9fdcfbSJ. Bruce Fields 	offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
12397c9fdcfbSJ. Bruce Fields 	*p++ = htonl(gc->gc_seq);
12407c9fdcfbSJ. Bruce Fields 	inpages = resbuf->pages;
12417c9fdcfbSJ. Bruce Fields 	/* XXX: Would be better to write some xdr helper functions for
12427c9fdcfbSJ. Bruce Fields 	 * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
12437c9fdcfbSJ. Bruce Fields 	if (resbuf->tail[0].iov_base && rqstp->rq_restailpage == 0) {
12447c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base
12457c9fdcfbSJ. Bruce Fields 							+ PAGE_SIZE);
12467c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base);
12477c9fdcfbSJ. Bruce Fields 		if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len
12487c9fdcfbSJ. Bruce Fields 				+ 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
12497c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
12507c9fdcfbSJ. Bruce Fields 		memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE,
12517c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_base,
12527c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_len);
12537c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
12547c9fdcfbSJ. Bruce Fields 	}
12557c9fdcfbSJ. Bruce Fields 	if (resbuf->tail[0].iov_base == NULL) {
12567c9fdcfbSJ. Bruce Fields 		if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
12577c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
12587c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
12597c9fdcfbSJ. Bruce Fields 			+ resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
12607c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_len = 0;
12617c9fdcfbSJ. Bruce Fields 		rqstp->rq_restailpage = 0;
12627c9fdcfbSJ. Bruce Fields 	}
12637c9fdcfbSJ. Bruce Fields 	if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
12647c9fdcfbSJ. Bruce Fields 		return -ENOMEM;
12657c9fdcfbSJ. Bruce Fields 	*len = htonl(resbuf->len - offset);
12667c9fdcfbSJ. Bruce Fields 	pad = 3 - ((resbuf->len - offset - 1)&3);
12677c9fdcfbSJ. Bruce Fields 	p = (u32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len);
12687c9fdcfbSJ. Bruce Fields 	memset(p, 0, pad);
12697c9fdcfbSJ. Bruce Fields 	resbuf->tail[0].iov_len += pad;
12707c9fdcfbSJ. Bruce Fields 	resbuf->len += pad;
12717c9fdcfbSJ. Bruce Fields 	return 0;
12727c9fdcfbSJ. Bruce Fields }
12737c9fdcfbSJ. Bruce Fields 
1274e142ede8SJ. Bruce Fields static int
1275e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp)
1276e142ede8SJ. Bruce Fields {
1277e142ede8SJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1278e142ede8SJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
1279e142ede8SJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
1280e142ede8SJ. Bruce Fields 	int stat = -EINVAL;
1281e142ede8SJ. Bruce Fields 
1282e142ede8SJ. Bruce Fields 	if (gc->gc_proc != RPC_GSS_PROC_DATA)
1283e142ede8SJ. Bruce Fields 		goto out;
1284e142ede8SJ. Bruce Fields 	/* Release can be called twice, but we only wrap once. */
1285e142ede8SJ. Bruce Fields 	if (gsd->body_start == NULL)
1286e142ede8SJ. Bruce Fields 		goto out;
1287e142ede8SJ. Bruce Fields 	/* normally not set till svc_send, but we need it here: */
12887c9fdcfbSJ. Bruce Fields 	/* XXX: what for?  Do we mess it up the moment we call svc_putu32
12897c9fdcfbSJ. Bruce Fields 	 * or whatever? */
12907c9fdcfbSJ. Bruce Fields 	resbuf->len = total_buf_len(resbuf);
1291e142ede8SJ. Bruce Fields 	switch (gc->gc_svc) {
1292e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_NONE:
1293e142ede8SJ. Bruce Fields 		break;
1294e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_INTEGRITY:
12957c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_integ(rqstp);
12967c9fdcfbSJ. Bruce Fields 		if (stat)
12977c9fdcfbSJ. Bruce Fields 			goto out_err;
12981da177e4SLinus Torvalds 		break;
12991da177e4SLinus Torvalds 	case RPC_GSS_SVC_PRIVACY:
13007c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_priv(rqstp);
13017c9fdcfbSJ. Bruce Fields 		if (stat)
13027c9fdcfbSJ. Bruce Fields 			goto out_err;
13037c9fdcfbSJ. Bruce Fields 		break;
13041da177e4SLinus Torvalds 	default:
13051da177e4SLinus Torvalds 		goto out_err;
13061da177e4SLinus Torvalds 	}
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds out:
13091da177e4SLinus Torvalds 	stat = 0;
13101da177e4SLinus Torvalds out_err:
13111da177e4SLinus Torvalds 	if (rqstp->rq_client)
13121da177e4SLinus Torvalds 		auth_domain_put(rqstp->rq_client);
13131da177e4SLinus Torvalds 	rqstp->rq_client = NULL;
13141da177e4SLinus Torvalds 	if (rqstp->rq_cred.cr_group_info)
13151da177e4SLinus Torvalds 		put_group_info(rqstp->rq_cred.cr_group_info);
13161da177e4SLinus Torvalds 	rqstp->rq_cred.cr_group_info = NULL;
13171da177e4SLinus Torvalds 	if (gsd->rsci)
1318baab935fSNeilBrown 		cache_put(&gsd->rsci->h, &rsc_cache);
13191da177e4SLinus Torvalds 	gsd->rsci = NULL;
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	return stat;
13221da177e4SLinus Torvalds }
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds static void
13251da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom)
13261da177e4SLinus Torvalds {
13271da177e4SLinus Torvalds 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 	kfree(dom->name);
13301da177e4SLinus Torvalds 	kfree(gd);
13311da177e4SLinus Torvalds }
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = {
13341da177e4SLinus Torvalds 	.name		= "rpcsec_gss",
13351da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
13361da177e4SLinus Torvalds 	.flavour	= RPC_AUTH_GSS,
13371da177e4SLinus Torvalds 	.accept		= svcauth_gss_accept,
13381da177e4SLinus Torvalds 	.release	= svcauth_gss_release,
13391da177e4SLinus Torvalds 	.domain_release = svcauth_gss_domain_release,
13401da177e4SLinus Torvalds 	.set_client	= svcauth_gss_set_client,
13411da177e4SLinus Torvalds };
13421da177e4SLinus Torvalds 
13431da177e4SLinus Torvalds int
13441da177e4SLinus Torvalds gss_svc_init(void)
13451da177e4SLinus Torvalds {
13461da177e4SLinus Torvalds 	int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
13471da177e4SLinus Torvalds 	if (rv == 0) {
13481da177e4SLinus Torvalds 		cache_register(&rsc_cache);
13491da177e4SLinus Torvalds 		cache_register(&rsi_cache);
13501da177e4SLinus Torvalds 	}
13511da177e4SLinus Torvalds 	return rv;
13521da177e4SLinus Torvalds }
13531da177e4SLinus Torvalds 
13541da177e4SLinus Torvalds void
13551da177e4SLinus Torvalds gss_svc_shutdown(void)
13561da177e4SLinus Torvalds {
1357f35279d3SBruce Allan 	if (cache_unregister(&rsc_cache))
1358f35279d3SBruce Allan 		printk(KERN_ERR "auth_rpcgss: failed to unregister rsc cache\n");
1359f35279d3SBruce Allan 	if (cache_unregister(&rsi_cache))
1360f35279d3SBruce Allan 		printk(KERN_ERR "auth_rpcgss: failed to unregister rsi cache\n");
13611da177e4SLinus Torvalds 	svc_auth_unregister(RPC_AUTH_GSS);
13621da177e4SLinus Torvalds }
1363