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 
405a0e3ad6STejun Heo #include <linux/slab.h>
411da177e4SLinus Torvalds #include <linux/types.h>
421da177e4SLinus Torvalds #include <linux/module.h>
431da177e4SLinus Torvalds #include <linux/pagemap.h>
44ae2975bcSEric W. Biederman #include <linux/user_namespace.h>
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h>
471da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h>
481da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h>
491da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h>
501da177e4SLinus Torvalds #include <linux/sunrpc/cache.h>
51030d794bSSimo Sorce #include "gss_rpc_upcall.h"
521da177e4SLinus Torvalds 
53a1db410dSStanislav Kinsbursky 
54f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
551da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
561da177e4SLinus Torvalds #endif
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
591da177e4SLinus Torvalds  * into replies.
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  * Key is context handle (\x if empty) and gss_token.
621da177e4SLinus Torvalds  * Content is major_status minor_status (integers) context_handle, reply_token.
631da177e4SLinus Torvalds  *
641da177e4SLinus Torvalds  */
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds 	return a->len == b->len && 0 == memcmp(a->data, b->data, a->len);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds #define	RSI_HASHBITS	6
721da177e4SLinus Torvalds #define	RSI_HASHMAX	(1<<RSI_HASHBITS)
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds struct rsi {
751da177e4SLinus Torvalds 	struct cache_head	h;
761da177e4SLinus Torvalds 	struct xdr_netobj	in_handle, in_token;
771da177e4SLinus Torvalds 	struct xdr_netobj	out_handle, out_token;
781da177e4SLinus Torvalds 	int			major_status, minor_status;
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
81a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old);
82a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, 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);
109f64f9e71SJoe Perches 	return netobj_equal(&item->in_handle, &tmp->in_handle) &&
110f64f9e71SJoe Perches 	       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;
116e69062b4SArnaldo Carvalho de Melo 	dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL);
1171da177e4SLinus Torvalds 	if (len && !dst->data)
1181da177e4SLinus Torvalds 		return -ENOMEM;
1191da177e4SLinus Torvalds 	return 0;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	return dup_to_netobj(dst, src->data, src->len);
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
127d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem)
1281da177e4SLinus Torvalds {
129d4d11ea9SNeilBrown 	struct rsi *new = container_of(cnew, struct rsi, h);
130d4d11ea9SNeilBrown 	struct rsi *item = container_of(citem, struct rsi, h);
131d4d11ea9SNeilBrown 
1321da177e4SLinus Torvalds 	new->out_handle.data = NULL;
1331da177e4SLinus Torvalds 	new->out_handle.len = 0;
1341da177e4SLinus Torvalds 	new->out_token.data = NULL;
1351da177e4SLinus Torvalds 	new->out_token.len = 0;
1361da177e4SLinus Torvalds 	new->in_handle.len = item->in_handle.len;
1371da177e4SLinus Torvalds 	item->in_handle.len = 0;
1381da177e4SLinus Torvalds 	new->in_token.len = item->in_token.len;
1391da177e4SLinus Torvalds 	item->in_token.len = 0;
1401da177e4SLinus Torvalds 	new->in_handle.data = item->in_handle.data;
1411da177e4SLinus Torvalds 	item->in_handle.data = NULL;
1421da177e4SLinus Torvalds 	new->in_token.data = item->in_token.data;
1431da177e4SLinus Torvalds 	item->in_token.data = NULL;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
146d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem)
1471da177e4SLinus Torvalds {
148d4d11ea9SNeilBrown 	struct rsi *new = container_of(cnew, struct rsi, h);
149d4d11ea9SNeilBrown 	struct rsi *item = container_of(citem, struct rsi, h);
150d4d11ea9SNeilBrown 
1511da177e4SLinus Torvalds 	BUG_ON(new->out_handle.data || new->out_token.data);
1521da177e4SLinus Torvalds 	new->out_handle.len = item->out_handle.len;
1531da177e4SLinus Torvalds 	item->out_handle.len = 0;
1541da177e4SLinus Torvalds 	new->out_token.len = item->out_token.len;
1551da177e4SLinus Torvalds 	item->out_token.len = 0;
1561da177e4SLinus Torvalds 	new->out_handle.data = item->out_handle.data;
1571da177e4SLinus Torvalds 	item->out_handle.data = NULL;
1581da177e4SLinus Torvalds 	new->out_token.data = item->out_token.data;
1591da177e4SLinus Torvalds 	item->out_token.data = NULL;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	new->major_status = item->major_status;
1621da177e4SLinus Torvalds 	new->minor_status = item->minor_status;
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
165d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void)
166d4d11ea9SNeilBrown {
167d4d11ea9SNeilBrown 	struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL);
168d4d11ea9SNeilBrown 	if (rsii)
169d4d11ea9SNeilBrown 		return &rsii->h;
170d4d11ea9SNeilBrown 	else
171d4d11ea9SNeilBrown 		return NULL;
172d4d11ea9SNeilBrown }
173d4d11ea9SNeilBrown 
1741da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd,
1751da177e4SLinus Torvalds 		       struct cache_head *h,
1761da177e4SLinus Torvalds 		       char **bpp, int *blen)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	struct rsi *rsii = container_of(h, struct rsi, h);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len);
1811da177e4SLinus Torvalds 	qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len);
1821da177e4SLinus Torvalds 	(*bpp)[-1] = '\n';
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd,
1861da177e4SLinus Torvalds 		    char *mesg, int mlen)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	/* context token expiry major minor context token */
1891da177e4SLinus Torvalds 	char *buf = mesg;
1901da177e4SLinus Torvalds 	char *ep;
1911da177e4SLinus Torvalds 	int len;
1921da177e4SLinus Torvalds 	struct rsi rsii, *rsip = NULL;
1931da177e4SLinus Torvalds 	time_t expiry;
1941da177e4SLinus Torvalds 	int status = -EINVAL;
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	memset(&rsii, 0, sizeof(rsii));
1971da177e4SLinus Torvalds 	/* handle */
1981da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
1991da177e4SLinus Torvalds 	if (len < 0)
2001da177e4SLinus Torvalds 		goto out;
2011da177e4SLinus Torvalds 	status = -ENOMEM;
2021da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.in_handle, buf, len))
2031da177e4SLinus Torvalds 		goto out;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	/* token */
2061da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2071da177e4SLinus Torvalds 	status = -EINVAL;
2081da177e4SLinus Torvalds 	if (len < 0)
2091da177e4SLinus Torvalds 		goto out;
2101da177e4SLinus Torvalds 	status = -ENOMEM;
2111da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.in_token, buf, len))
2121da177e4SLinus Torvalds 		goto out;
2131da177e4SLinus Torvalds 
214a1db410dSStanislav Kinsbursky 	rsip = rsi_lookup(cd, &rsii);
215d4d11ea9SNeilBrown 	if (!rsip)
216d4d11ea9SNeilBrown 		goto out;
217d4d11ea9SNeilBrown 
2181da177e4SLinus Torvalds 	rsii.h.flags = 0;
2191da177e4SLinus Torvalds 	/* expiry */
2201da177e4SLinus Torvalds 	expiry = get_expiry(&mesg);
2211da177e4SLinus Torvalds 	status = -EINVAL;
2221da177e4SLinus Torvalds 	if (expiry == 0)
2231da177e4SLinus Torvalds 		goto out;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	/* major/minor */
2261da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
227b39c18fcSJ. Bruce Fields 	if (len <= 0)
2281da177e4SLinus Torvalds 		goto out;
2291da177e4SLinus Torvalds 	rsii.major_status = simple_strtoul(buf, &ep, 10);
2301da177e4SLinus Torvalds 	if (*ep)
2311da177e4SLinus Torvalds 		goto out;
2321da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2331da177e4SLinus Torvalds 	if (len <= 0)
2341da177e4SLinus Torvalds 		goto out;
2351da177e4SLinus Torvalds 	rsii.minor_status = simple_strtoul(buf, &ep, 10);
2361da177e4SLinus Torvalds 	if (*ep)
2371da177e4SLinus Torvalds 		goto out;
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	/* out_handle */
2401da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2411da177e4SLinus Torvalds 	if (len < 0)
2421da177e4SLinus Torvalds 		goto out;
2431da177e4SLinus Torvalds 	status = -ENOMEM;
2441da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.out_handle, buf, len))
2451da177e4SLinus Torvalds 		goto out;
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 	/* out_token */
2481da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
2491da177e4SLinus Torvalds 	status = -EINVAL;
2501da177e4SLinus Torvalds 	if (len < 0)
2511da177e4SLinus Torvalds 		goto out;
2521da177e4SLinus Torvalds 	status = -ENOMEM;
2531da177e4SLinus Torvalds 	if (dup_to_netobj(&rsii.out_token, buf, len))
2541da177e4SLinus Torvalds 		goto out;
2551da177e4SLinus Torvalds 	rsii.h.expiry_time = expiry;
256a1db410dSStanislav Kinsbursky 	rsip = rsi_update(cd, &rsii, rsip);
2571da177e4SLinus Torvalds 	status = 0;
2581da177e4SLinus Torvalds out:
2591da177e4SLinus Torvalds 	rsi_free(&rsii);
2601da177e4SLinus Torvalds 	if (rsip)
261a1db410dSStanislav Kinsbursky 		cache_put(&rsip->h, cd);
262d4d11ea9SNeilBrown 	else
263d4d11ea9SNeilBrown 		status = -ENOMEM;
2641da177e4SLinus Torvalds 	return status;
2651da177e4SLinus Torvalds }
2661da177e4SLinus Torvalds 
267a1db410dSStanislav Kinsbursky static struct cache_detail rsi_cache_template = {
268f35279d3SBruce Allan 	.owner		= THIS_MODULE,
2691da177e4SLinus Torvalds 	.hash_size	= RSI_HASHMAX,
2701da177e4SLinus Torvalds 	.name           = "auth.rpcsec.init",
2711da177e4SLinus Torvalds 	.cache_put      = rsi_put,
27273fb847aSStanislav Kinsbursky 	.cache_request  = rsi_request,
2731da177e4SLinus Torvalds 	.cache_parse    = rsi_parse,
274d4d11ea9SNeilBrown 	.match		= rsi_match,
275d4d11ea9SNeilBrown 	.init		= rsi_init,
276d4d11ea9SNeilBrown 	.update		= update_rsi,
277d4d11ea9SNeilBrown 	.alloc		= rsi_alloc,
2781da177e4SLinus Torvalds };
2791da177e4SLinus Torvalds 
280a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item)
281d4d11ea9SNeilBrown {
282d4d11ea9SNeilBrown 	struct cache_head *ch;
283d4d11ea9SNeilBrown 	int hash = rsi_hash(item);
284d4d11ea9SNeilBrown 
285a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_lookup(cd, &item->h, hash);
286d4d11ea9SNeilBrown 	if (ch)
287d4d11ea9SNeilBrown 		return container_of(ch, struct rsi, h);
288d4d11ea9SNeilBrown 	else
289d4d11ea9SNeilBrown 		return NULL;
290d4d11ea9SNeilBrown }
291d4d11ea9SNeilBrown 
292a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old)
293d4d11ea9SNeilBrown {
294d4d11ea9SNeilBrown 	struct cache_head *ch;
295d4d11ea9SNeilBrown 	int hash = rsi_hash(new);
296d4d11ea9SNeilBrown 
297a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_update(cd, &new->h,
298d4d11ea9SNeilBrown 				 &old->h, hash);
299d4d11ea9SNeilBrown 	if (ch)
300d4d11ea9SNeilBrown 		return container_of(ch, struct rsi, h);
301d4d11ea9SNeilBrown 	else
302d4d11ea9SNeilBrown 		return NULL;
303d4d11ea9SNeilBrown }
304d4d11ea9SNeilBrown 
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds /*
3071da177e4SLinus Torvalds  * The rpcsec_context cache is used to store a context that is
3081da177e4SLinus Torvalds  * used in data exchange.
3091da177e4SLinus Torvalds  * The key is a context handle. The content is:
3101da177e4SLinus Torvalds  *  uid, gidlist, mechanism, service-set, mech-specific-data
3111da177e4SLinus Torvalds  */
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds #define	RSC_HASHBITS	10
3141da177e4SLinus Torvalds #define	RSC_HASHMAX	(1<<RSC_HASHBITS)
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds #define GSS_SEQ_WIN	128
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds struct gss_svc_seq_data {
3191da177e4SLinus Torvalds 	/* highest seq number seen so far: */
3201da177e4SLinus Torvalds 	int			sd_max;
3211da177e4SLinus Torvalds 	/* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of
3221da177e4SLinus Torvalds 	 * sd_win is nonzero iff sequence number i has been seen already: */
3231da177e4SLinus Torvalds 	unsigned long		sd_win[GSS_SEQ_WIN/BITS_PER_LONG];
3241da177e4SLinus Torvalds 	spinlock_t		sd_lock;
3251da177e4SLinus Torvalds };
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds struct rsc {
3281da177e4SLinus Torvalds 	struct cache_head	h;
3291da177e4SLinus Torvalds 	struct xdr_netobj	handle;
3301da177e4SLinus Torvalds 	struct svc_cred		cred;
3311da177e4SLinus Torvalds 	struct gss_svc_seq_data	seqdata;
3321da177e4SLinus Torvalds 	struct gss_ctx		*mechctx;
3331da177e4SLinus Torvalds };
3341da177e4SLinus Torvalds 
335a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
336a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item);
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	kfree(rsci->handle.data);
3411da177e4SLinus Torvalds 	if (rsci->mechctx)
3421da177e4SLinus Torvalds 		gss_delete_sec_context(&rsci->mechctx);
34303a4e1f6SJ. Bruce Fields 	free_svc_cred(&rsci->cred);
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
346baab935fSNeilBrown static void rsc_put(struct kref *ref)
3471da177e4SLinus Torvalds {
348baab935fSNeilBrown 	struct rsc *rsci = container_of(ref, struct rsc, h.ref);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	rsc_free(rsci);
3511da177e4SLinus Torvalds 	kfree(rsci);
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds static inline int
3551da177e4SLinus Torvalds rsc_hash(struct rsc *rsci)
3561da177e4SLinus Torvalds {
3571da177e4SLinus Torvalds 	return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS);
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds 
36017f834b6SNeilBrown static int
36117f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b)
3621da177e4SLinus Torvalds {
36317f834b6SNeilBrown 	struct rsc *new = container_of(a, struct rsc, h);
36417f834b6SNeilBrown 	struct rsc *tmp = container_of(b, struct rsc, h);
36517f834b6SNeilBrown 
3661da177e4SLinus Torvalds 	return netobj_equal(&new->handle, &tmp->handle);
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
36917f834b6SNeilBrown static void
37017f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
3711da177e4SLinus Torvalds {
37217f834b6SNeilBrown 	struct rsc *new = container_of(cnew, struct rsc, h);
37317f834b6SNeilBrown 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
37417f834b6SNeilBrown 
3751da177e4SLinus Torvalds 	new->handle.len = tmp->handle.len;
3761da177e4SLinus Torvalds 	tmp->handle.len = 0;
3771da177e4SLinus Torvalds 	new->handle.data = tmp->handle.data;
3781da177e4SLinus Torvalds 	tmp->handle.data = NULL;
3791da177e4SLinus Torvalds 	new->mechctx = NULL;
38044234063SJ. Bruce Fields 	init_svc_cred(&new->cred);
3811da177e4SLinus Torvalds }
3821da177e4SLinus Torvalds 
38317f834b6SNeilBrown static void
38417f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
3851da177e4SLinus Torvalds {
38617f834b6SNeilBrown 	struct rsc *new = container_of(cnew, struct rsc, h);
38717f834b6SNeilBrown 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
38817f834b6SNeilBrown 
3891da177e4SLinus Torvalds 	new->mechctx = tmp->mechctx;
3901da177e4SLinus Torvalds 	tmp->mechctx = NULL;
3911da177e4SLinus Torvalds 	memset(&new->seqdata, 0, sizeof(new->seqdata));
3921da177e4SLinus Torvalds 	spin_lock_init(&new->seqdata.sd_lock);
3931da177e4SLinus Torvalds 	new->cred = tmp->cred;
39444234063SJ. Bruce Fields 	init_svc_cred(&tmp->cred);
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
39717f834b6SNeilBrown static struct cache_head *
39817f834b6SNeilBrown rsc_alloc(void)
39917f834b6SNeilBrown {
40017f834b6SNeilBrown 	struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL);
40117f834b6SNeilBrown 	if (rsci)
40217f834b6SNeilBrown 		return &rsci->h;
40317f834b6SNeilBrown 	else
40417f834b6SNeilBrown 		return NULL;
40517f834b6SNeilBrown }
40617f834b6SNeilBrown 
4071da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd,
4081da177e4SLinus Torvalds 		     char *mesg, int mlen)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds 	/* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */
4111da177e4SLinus Torvalds 	char *buf = mesg;
412683428faSEric W. Biederman 	int id;
4131da177e4SLinus Torvalds 	int len, rv;
4141da177e4SLinus Torvalds 	struct rsc rsci, *rscp = NULL;
4151da177e4SLinus Torvalds 	time_t expiry;
4161da177e4SLinus Torvalds 	int status = -EINVAL;
4171df0cadaSJ. Bruce Fields 	struct gss_api_mech *gm = NULL;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	memset(&rsci, 0, sizeof(rsci));
4201da177e4SLinus Torvalds 	/* context handle */
4211da177e4SLinus Torvalds 	len = qword_get(&mesg, buf, mlen);
4221da177e4SLinus Torvalds 	if (len < 0) goto out;
4231da177e4SLinus Torvalds 	status = -ENOMEM;
4241da177e4SLinus Torvalds 	if (dup_to_netobj(&rsci.handle, buf, len))
4251da177e4SLinus Torvalds 		goto out;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	rsci.h.flags = 0;
4281da177e4SLinus Torvalds 	/* expiry */
4291da177e4SLinus Torvalds 	expiry = get_expiry(&mesg);
4301da177e4SLinus Torvalds 	status = -EINVAL;
4311da177e4SLinus Torvalds 	if (expiry == 0)
4321da177e4SLinus Torvalds 		goto out;
4331da177e4SLinus Torvalds 
434a1db410dSStanislav Kinsbursky 	rscp = rsc_lookup(cd, &rsci);
43517f834b6SNeilBrown 	if (!rscp)
43617f834b6SNeilBrown 		goto out;
43717f834b6SNeilBrown 
4381da177e4SLinus Torvalds 	/* uid, or NEGATIVE */
439683428faSEric W. Biederman 	rv = get_int(&mesg, &id);
4401da177e4SLinus Torvalds 	if (rv == -EINVAL)
4411da177e4SLinus Torvalds 		goto out;
4421da177e4SLinus Torvalds 	if (rv == -ENOENT)
4431da177e4SLinus Torvalds 		set_bit(CACHE_NEGATIVE, &rsci.h.flags);
4441da177e4SLinus Torvalds 	else {
4451da177e4SLinus Torvalds 		int N, i;
4461da177e4SLinus Torvalds 
4473c34ae11SJ. Bruce Fields 		/*
4483c34ae11SJ. Bruce Fields 		 * NOTE: we skip uid_valid()/gid_valid() checks here:
4493c34ae11SJ. Bruce Fields 		 * instead, * -1 id's are later mapped to the
4503c34ae11SJ. Bruce Fields 		 * (export-specific) anonymous id by nfsd_setuser.
4513c34ae11SJ. Bruce Fields 		 *
4523c34ae11SJ. Bruce Fields 		 * (But supplementary gid's get no such special
4533c34ae11SJ. Bruce Fields 		 * treatment so are checked for validity here.)
4543c34ae11SJ. Bruce Fields 		 */
455683428faSEric W. Biederman 		/* uid */
456683428faSEric W. Biederman 		rsci.cred.cr_uid = make_kuid(&init_user_ns, id);
457683428faSEric W. Biederman 
4581da177e4SLinus Torvalds 		/* gid */
459683428faSEric W. Biederman 		if (get_int(&mesg, &id))
460683428faSEric W. Biederman 			goto out;
461683428faSEric W. Biederman 		rsci.cred.cr_gid = make_kgid(&init_user_ns, id);
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 		/* number of additional gid's */
4641da177e4SLinus Torvalds 		if (get_int(&mesg, &N))
4651da177e4SLinus Torvalds 			goto out;
46676cb4be9SDan Carpenter 		if (N < 0 || N > NGROUPS_MAX)
46776cb4be9SDan Carpenter 			goto out;
4681da177e4SLinus Torvalds 		status = -ENOMEM;
4691da177e4SLinus Torvalds 		rsci.cred.cr_group_info = groups_alloc(N);
4701da177e4SLinus Torvalds 		if (rsci.cred.cr_group_info == NULL)
4711da177e4SLinus Torvalds 			goto out;
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 		/* gid's */
4741da177e4SLinus Torvalds 		status = -EINVAL;
4751da177e4SLinus Torvalds 		for (i=0; i<N; i++) {
476ae2975bcSEric W. Biederman 			kgid_t kgid;
477683428faSEric W. Biederman 			if (get_int(&mesg, &id))
4781da177e4SLinus Torvalds 				goto out;
479683428faSEric W. Biederman 			kgid = make_kgid(&init_user_ns, id);
480ae2975bcSEric W. Biederman 			if (!gid_valid(kgid))
481ae2975bcSEric W. Biederman 				goto out;
48281243eacSAlexey Dobriyan 			rsci.cred.cr_group_info->gid[i] = kgid;
4831da177e4SLinus Torvalds 		}
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 		/* mech name */
4861da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4871da177e4SLinus Torvalds 		if (len < 0)
4881da177e4SLinus Torvalds 			goto out;
4890dc1531aSJ. Bruce Fields 		gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
4901da177e4SLinus Torvalds 		status = -EOPNOTSUPP;
4911da177e4SLinus Torvalds 		if (!gm)
4921da177e4SLinus Torvalds 			goto out;
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds 		status = -EINVAL;
4951da177e4SLinus Torvalds 		/* mech-specific data: */
4961da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4971df0cadaSJ. Bruce Fields 		if (len < 0)
4981da177e4SLinus Torvalds 			goto out;
499400f26b5SSimo Sorce 		status = gss_import_sec_context(buf, len, gm, &rsci.mechctx,
500400f26b5SSimo Sorce 						NULL, GFP_KERNEL);
5011df0cadaSJ. Bruce Fields 		if (status)
5021da177e4SLinus Torvalds 			goto out;
50368e76ad0SOlga Kornievskaia 
50468e76ad0SOlga Kornievskaia 		/* get client name */
50568e76ad0SOlga Kornievskaia 		len = qword_get(&mesg, buf, mlen);
50668e76ad0SOlga Kornievskaia 		if (len > 0) {
50703a4e1f6SJ. Bruce Fields 			rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL);
5081eb6d622SWei Yongjun 			if (!rsci.cred.cr_principal) {
5091eb6d622SWei Yongjun 				status = -ENOMEM;
51068e76ad0SOlga Kornievskaia 				goto out;
51168e76ad0SOlga Kornievskaia 			}
5121eb6d622SWei Yongjun 		}
51368e76ad0SOlga Kornievskaia 
5141da177e4SLinus Torvalds 	}
5151da177e4SLinus Torvalds 	rsci.h.expiry_time = expiry;
516a1db410dSStanislav Kinsbursky 	rscp = rsc_update(cd, &rsci, rscp);
5171da177e4SLinus Torvalds 	status = 0;
5181da177e4SLinus Torvalds out:
5191da177e4SLinus Torvalds 	rsc_free(&rsci);
5201da177e4SLinus Torvalds 	if (rscp)
521a1db410dSStanislav Kinsbursky 		cache_put(&rscp->h, cd);
52217f834b6SNeilBrown 	else
52317f834b6SNeilBrown 		status = -ENOMEM;
5241da177e4SLinus Torvalds 	return status;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
527a1db410dSStanislav Kinsbursky static struct cache_detail rsc_cache_template = {
528f35279d3SBruce Allan 	.owner		= THIS_MODULE,
5291da177e4SLinus Torvalds 	.hash_size	= RSC_HASHMAX,
5301da177e4SLinus Torvalds 	.name		= "auth.rpcsec.context",
5311da177e4SLinus Torvalds 	.cache_put	= rsc_put,
5321da177e4SLinus Torvalds 	.cache_parse	= rsc_parse,
53317f834b6SNeilBrown 	.match		= rsc_match,
53417f834b6SNeilBrown 	.init		= rsc_init,
53517f834b6SNeilBrown 	.update		= update_rsc,
53617f834b6SNeilBrown 	.alloc		= rsc_alloc,
5371da177e4SLinus Torvalds };
5381da177e4SLinus Torvalds 
539a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item)
54017f834b6SNeilBrown {
54117f834b6SNeilBrown 	struct cache_head *ch;
54217f834b6SNeilBrown 	int hash = rsc_hash(item);
54317f834b6SNeilBrown 
544a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_lookup(cd, &item->h, hash);
54517f834b6SNeilBrown 	if (ch)
54617f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
54717f834b6SNeilBrown 	else
54817f834b6SNeilBrown 		return NULL;
54917f834b6SNeilBrown }
55017f834b6SNeilBrown 
551a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old)
55217f834b6SNeilBrown {
55317f834b6SNeilBrown 	struct cache_head *ch;
55417f834b6SNeilBrown 	int hash = rsc_hash(new);
55517f834b6SNeilBrown 
556a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_update(cd, &new->h,
55717f834b6SNeilBrown 				 &old->h, hash);
55817f834b6SNeilBrown 	if (ch)
55917f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
56017f834b6SNeilBrown 	else
56117f834b6SNeilBrown 		return NULL;
56217f834b6SNeilBrown }
56317f834b6SNeilBrown 
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds static struct rsc *
566a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds 	struct rsc rsci;
5691da177e4SLinus Torvalds 	struct rsc *found;
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	memset(&rsci, 0, sizeof(rsci));
572bf2c4b6fSChuck Lever 	if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
573bf2c4b6fSChuck Lever 		return NULL;
574a1db410dSStanislav Kinsbursky 	found = rsc_lookup(cd, &rsci);
575bf2c4b6fSChuck Lever 	rsc_free(&rsci);
5761da177e4SLinus Torvalds 	if (!found)
5771da177e4SLinus Torvalds 		return NULL;
578a1db410dSStanislav Kinsbursky 	if (cache_check(cd, &found->h, NULL))
5791da177e4SLinus Torvalds 		return NULL;
5801da177e4SLinus Torvalds 	return found;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */
5841da177e4SLinus Torvalds static int
5851da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num)
5861da177e4SLinus Torvalds {
5871da177e4SLinus Torvalds 	struct gss_svc_seq_data *sd = &rsci->seqdata;
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	spin_lock(&sd->sd_lock);
5901da177e4SLinus Torvalds 	if (seq_num > sd->sd_max) {
5911da177e4SLinus Torvalds 		if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
5921da177e4SLinus Torvalds 			memset(sd->sd_win,0,sizeof(sd->sd_win));
5931da177e4SLinus Torvalds 			sd->sd_max = seq_num;
5941da177e4SLinus Torvalds 		} else while (sd->sd_max < seq_num) {
5951da177e4SLinus Torvalds 			sd->sd_max++;
5961da177e4SLinus Torvalds 			__clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win);
5971da177e4SLinus Torvalds 		}
5981da177e4SLinus Torvalds 		__set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
5991da177e4SLinus Torvalds 		goto ok;
6001da177e4SLinus Torvalds 	} else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) {
6011da177e4SLinus Torvalds 		goto drop;
6021da177e4SLinus Torvalds 	}
6031da177e4SLinus Torvalds 	/* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */
6041da177e4SLinus Torvalds 	if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
6051da177e4SLinus Torvalds 		goto drop;
6061da177e4SLinus Torvalds ok:
6071da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
6081da177e4SLinus Torvalds 	return 1;
6091da177e4SLinus Torvalds drop:
6101da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
6111da177e4SLinus Torvalds 	return 0;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i)
6151da177e4SLinus Torvalds {
6161da177e4SLinus Torvalds 	return (i + 3 ) & ~3;
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds static inline int
6201da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
6211da177e4SLinus Torvalds {
6221da177e4SLinus Torvalds 	int l;
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6251da177e4SLinus Torvalds 		return -1;
62676994313SAlexey Dobriyan 	o->len = svc_getnl(argv);
6271da177e4SLinus Torvalds 	l = round_up_to_quad(o->len);
6281da177e4SLinus Torvalds 	if (argv->iov_len < l)
6291da177e4SLinus Torvalds 		return -1;
6301da177e4SLinus Torvalds 	o->data = argv->iov_base;
6311da177e4SLinus Torvalds 	argv->iov_base += l;
6321da177e4SLinus Torvalds 	argv->iov_len -= l;
6331da177e4SLinus Torvalds 	return 0;
6341da177e4SLinus Torvalds }
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds static inline int
6371da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
6381da177e4SLinus Torvalds {
639753ed90dSAl Viro 	u8 *p;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	if (resv->iov_len + 4 > PAGE_SIZE)
6421da177e4SLinus Torvalds 		return -1;
64376994313SAlexey Dobriyan 	svc_putnl(resv, o->len);
6441da177e4SLinus Torvalds 	p = resv->iov_base + resv->iov_len;
6451da177e4SLinus Torvalds 	resv->iov_len += round_up_to_quad(o->len);
6461da177e4SLinus Torvalds 	if (resv->iov_len > PAGE_SIZE)
6471da177e4SLinus Torvalds 		return -1;
6481da177e4SLinus Torvalds 	memcpy(p, o->data, o->len);
649753ed90dSAl Viro 	memset(p + o->len, 0, round_up_to_quad(o->len) - o->len);
6501da177e4SLinus Torvalds 	return 0;
6511da177e4SLinus Torvalds }
6521da177e4SLinus Torvalds 
65321fcd02bSJ. Bruce Fields /*
65421fcd02bSJ. Bruce Fields  * Verify the checksum on the header and return SVC_OK on success.
6551da177e4SLinus Torvalds  * Otherwise, return SVC_DROP (in the case of a bad sequence number)
6561da177e4SLinus Torvalds  * or return SVC_DENIED and indicate error in authp.
6571da177e4SLinus Torvalds  */
6581da177e4SLinus Torvalds static int
6591da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
660d8ed029dSAlexey Dobriyan 		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp)
6611da177e4SLinus Torvalds {
6621da177e4SLinus Torvalds 	struct gss_ctx		*ctx_id = rsci->mechctx;
6631da177e4SLinus Torvalds 	struct xdr_buf		rpchdr;
6641da177e4SLinus Torvalds 	struct xdr_netobj	checksum;
6651da177e4SLinus Torvalds 	u32			flavor = 0;
6661da177e4SLinus Torvalds 	struct kvec		*argv = &rqstp->rq_arg.head[0];
6671da177e4SLinus Torvalds 	struct kvec		iov;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	/* data to compute the checksum over: */
6701da177e4SLinus Torvalds 	iov.iov_base = rpcstart;
6711da177e4SLinus Torvalds 	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
6721da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &rpchdr);
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
6751da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6761da177e4SLinus Torvalds 		return SVC_DENIED;
67776994313SAlexey Dobriyan 	flavor = svc_getnl(argv);
6781da177e4SLinus Torvalds 	if (flavor != RPC_AUTH_GSS)
6791da177e4SLinus Torvalds 		return SVC_DENIED;
6801da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &checksum))
6811da177e4SLinus Torvalds 		return SVC_DENIED;
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 	if (rqstp->rq_deferred) /* skip verification of revisited request */
6841da177e4SLinus Torvalds 		return SVC_OK;
68500fd6e14SJ. Bruce Fields 	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
6861da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
6871da177e4SLinus Torvalds 		return SVC_DENIED;
6881da177e4SLinus Torvalds 	}
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	if (gc->gc_seq > MAXSEQ) {
6918885cb36SChuck Lever 		dprintk("RPC:       svcauth_gss: discarding request with "
6928885cb36SChuck Lever 				"large sequence number %d\n", gc->gc_seq);
6931da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
6941da177e4SLinus Torvalds 		return SVC_DENIED;
6951da177e4SLinus Torvalds 	}
6961da177e4SLinus Torvalds 	if (!gss_check_seq_num(rsci, gc->gc_seq)) {
6978885cb36SChuck Lever 		dprintk("RPC:       svcauth_gss: discarding request with "
6988885cb36SChuck Lever 				"old sequence number %d\n", gc->gc_seq);
6991da177e4SLinus Torvalds 		return SVC_DROP;
7001da177e4SLinus Torvalds 	}
7011da177e4SLinus Torvalds 	return SVC_OK;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds static int
705822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp)
706822f1005SAndy Adamson {
707d8ed029dSAlexey Dobriyan 	__be32     *p;
708822f1005SAndy Adamson 
70976994313SAlexey Dobriyan 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL);
710822f1005SAndy Adamson 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
711822f1005SAndy Adamson 	/* don't really need to check if head->iov_len > PAGE_SIZE ... */
712822f1005SAndy Adamson 	*p++ = 0;
713822f1005SAndy Adamson 	if (!xdr_ressize_check(rqstp, p))
714822f1005SAndy Adamson 		return -1;
715822f1005SAndy Adamson 	return 0;
716822f1005SAndy Adamson }
717822f1005SAndy Adamson 
718822f1005SAndy Adamson static int
7191da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
7201da177e4SLinus Torvalds {
7212876a344SJ. Bruce Fields 	__be32			*xdr_seq;
7221da177e4SLinus Torvalds 	u32			maj_stat;
7231da177e4SLinus Torvalds 	struct xdr_buf		verf_data;
7241da177e4SLinus Torvalds 	struct xdr_netobj	mic;
725d8ed029dSAlexey Dobriyan 	__be32			*p;
7261da177e4SLinus Torvalds 	struct kvec		iov;
7272876a344SJ. Bruce Fields 	int err = -1;
7281da177e4SLinus Torvalds 
72976994313SAlexey Dobriyan 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
7302876a344SJ. Bruce Fields 	xdr_seq = kmalloc(4, GFP_KERNEL);
7312876a344SJ. Bruce Fields 	if (!xdr_seq)
7322876a344SJ. Bruce Fields 		return -1;
7332876a344SJ. Bruce Fields 	*xdr_seq = htonl(seq);
7341da177e4SLinus Torvalds 
7352876a344SJ. Bruce Fields 	iov.iov_base = xdr_seq;
7362876a344SJ. Bruce Fields 	iov.iov_len = 4;
7371da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &verf_data);
7381da177e4SLinus Torvalds 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
7391da177e4SLinus Torvalds 	mic.data = (u8 *)(p + 1);
74000fd6e14SJ. Bruce Fields 	maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
7411da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
7422876a344SJ. Bruce Fields 		goto out;
7431da177e4SLinus Torvalds 	*p++ = htonl(mic.len);
7441da177e4SLinus Torvalds 	memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
7451da177e4SLinus Torvalds 	p += XDR_QUADLEN(mic.len);
7461da177e4SLinus Torvalds 	if (!xdr_ressize_check(rqstp, p))
7472876a344SJ. Bruce Fields 		goto out;
7482876a344SJ. Bruce Fields 	err = 0;
7492876a344SJ. Bruce Fields out:
7502876a344SJ. Bruce Fields 	kfree(xdr_seq);
7512876a344SJ. Bruce Fields 	return err;
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds struct gss_domain {
7551da177e4SLinus Torvalds 	struct auth_domain	h;
7561da177e4SLinus Torvalds 	u32			pseudoflavor;
7571da177e4SLinus Torvalds };
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds static struct auth_domain *
7601da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
7611da177e4SLinus Torvalds {
7621da177e4SLinus Torvalds 	char *name;
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	name = gss_service_to_auth_domain_name(ctx->mech_type, svc);
7651da177e4SLinus Torvalds 	if (!name)
7661da177e4SLinus Torvalds 		return NULL;
7671da177e4SLinus Torvalds 	return auth_domain_find(name);
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds 
770efc36aa5SNeilBrown static struct auth_ops svcauthops_gss;
771efc36aa5SNeilBrown 
7724796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom)
7734796f457SJ. Bruce Fields {
7744796f457SJ. Bruce Fields 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
7754796f457SJ. Bruce Fields 
7764796f457SJ. Bruce Fields 	return gd->pseudoflavor;
7774796f457SJ. Bruce Fields }
7784796f457SJ. Bruce Fields 
7797bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
7804796f457SJ. Bruce Fields 
7811da177e4SLinus Torvalds int
7821da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
7831da177e4SLinus Torvalds {
7841da177e4SLinus Torvalds 	struct gss_domain	*new;
7851da177e4SLinus Torvalds 	struct auth_domain	*test;
7861da177e4SLinus Torvalds 	int			stat = -ENOMEM;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	new = kmalloc(sizeof(*new), GFP_KERNEL);
7891da177e4SLinus Torvalds 	if (!new)
7901da177e4SLinus Torvalds 		goto out;
791efc36aa5SNeilBrown 	kref_init(&new->h.ref);
792e69062b4SArnaldo Carvalho de Melo 	new->h.name = kstrdup(name, GFP_KERNEL);
7931da177e4SLinus Torvalds 	if (!new->h.name)
7941da177e4SLinus Torvalds 		goto out_free_dom;
795efc36aa5SNeilBrown 	new->h.flavour = &svcauthops_gss;
7961da177e4SLinus Torvalds 	new->pseudoflavor = pseudoflavor;
7971da177e4SLinus Torvalds 
798cb276805SJ. Bruce Fields 	stat = 0;
799efc36aa5SNeilBrown 	test = auth_domain_lookup(name, &new->h);
800cb276805SJ. Bruce Fields 	if (test != &new->h) { /* Duplicate registration */
801cb276805SJ. Bruce Fields 		auth_domain_put(test);
802cb276805SJ. Bruce Fields 		kfree(new->h.name);
803cb276805SJ. Bruce Fields 		goto out_free_dom;
8041da177e4SLinus Torvalds 	}
8051da177e4SLinus Torvalds 	return 0;
8061da177e4SLinus Torvalds 
8071da177e4SLinus Torvalds out_free_dom:
8081da177e4SLinus Torvalds 	kfree(new);
8091da177e4SLinus Torvalds out:
8101da177e4SLinus Torvalds 	return stat;
8111da177e4SLinus Torvalds }
8121da177e4SLinus Torvalds 
8137bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds static inline int
8161da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
8171da177e4SLinus Torvalds {
818d8ed029dSAlexey Dobriyan 	__be32  raw;
8191da177e4SLinus Torvalds 	int     status;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
8221da177e4SLinus Torvalds 	if (status)
8231da177e4SLinus Torvalds 		return status;
8241da177e4SLinus Torvalds 	*obj = ntohl(raw);
8251da177e4SLinus Torvalds 	return 0;
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client.
8291da177e4SLinus Torvalds  * Obstacles:
8301da177e4SLinus Torvalds  *	The client shouldn't malloc(), would have to pass in own memory.
8311da177e4SLinus Torvalds  *	The server uses base of head iovec as read pointer, while the
8321da177e4SLinus Torvalds  *	client uses separate pointer. */
8331da177e4SLinus Torvalds static int
8344c190e2fSJeff Layton unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
8351da177e4SLinus Torvalds {
8361da177e4SLinus Torvalds 	int stat = -EINVAL;
8371da177e4SLinus Torvalds 	u32 integ_len, maj_stat;
8381da177e4SLinus Torvalds 	struct xdr_netobj mic;
8391da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
8401da177e4SLinus Torvalds 
84106eb8a56SChuck Lever 	/* NFS READ normally uses splice to send data in-place. However
84206eb8a56SChuck Lever 	 * the data in cache can change after the reply's MIC is computed
84306eb8a56SChuck Lever 	 * but before the RPC reply is sent. To prevent the client from
84406eb8a56SChuck Lever 	 * rejecting the server-computed MIC in this somewhat rare case,
84506eb8a56SChuck Lever 	 * do not use splice with the GSS integrity service.
84606eb8a56SChuck Lever 	 */
84706eb8a56SChuck Lever 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
84806eb8a56SChuck Lever 
8494c190e2fSJeff Layton 	/* Did we already verify the signature on the original pass through? */
8504c190e2fSJeff Layton 	if (rqstp->rq_deferred)
8514c190e2fSJeff Layton 		return 0;
8524c190e2fSJeff Layton 
85376994313SAlexey Dobriyan 	integ_len = svc_getnl(&buf->head[0]);
8541da177e4SLinus Torvalds 	if (integ_len & 3)
855b797b5beSJ.Bruce Fields 		return stat;
8561da177e4SLinus Torvalds 	if (integ_len > buf->len)
857b797b5beSJ.Bruce Fields 		return stat;
8581da177e4SLinus Torvalds 	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
8591da177e4SLinus Torvalds 		BUG();
8601da177e4SLinus Torvalds 	/* copy out mic... */
8611da177e4SLinus Torvalds 	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
8621da177e4SLinus Torvalds 		BUG();
8631da177e4SLinus Torvalds 	if (mic.len > RPC_MAX_AUTH_SIZE)
864b797b5beSJ.Bruce Fields 		return stat;
8651da177e4SLinus Torvalds 	mic.data = kmalloc(mic.len, GFP_KERNEL);
8661da177e4SLinus Torvalds 	if (!mic.data)
867b797b5beSJ.Bruce Fields 		return stat;
8681da177e4SLinus Torvalds 	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
8691da177e4SLinus Torvalds 		goto out;
87000fd6e14SJ. Bruce Fields 	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
8711da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
8721da177e4SLinus Torvalds 		goto out;
87376994313SAlexey Dobriyan 	if (svc_getnl(&buf->head[0]) != seq)
8741da177e4SLinus Torvalds 		goto out;
875c0cb8bf3STomáš Trnka 	/* trim off the mic and padding at the end before returning */
876c0cb8bf3STomáš Trnka 	xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
8771da177e4SLinus Torvalds 	stat = 0;
8781da177e4SLinus Torvalds out:
879b797b5beSJ.Bruce Fields 	kfree(mic.data);
8801da177e4SLinus Torvalds 	return stat;
8811da177e4SLinus Torvalds }
8821da177e4SLinus Torvalds 
8837c9fdcfbSJ. Bruce Fields static inline int
8847c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf)
8857c9fdcfbSJ. Bruce Fields {
8867c9fdcfbSJ. Bruce Fields 	return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
8877c9fdcfbSJ. Bruce Fields }
8887c9fdcfbSJ. Bruce Fields 
8897c9fdcfbSJ. Bruce Fields static void
8907c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad)
8917c9fdcfbSJ. Bruce Fields {
8927c9fdcfbSJ. Bruce Fields 	if (buf->page_len == 0) {
8937c9fdcfbSJ. Bruce Fields 		/* We need to adjust head and buf->len in tandem in this
8947c9fdcfbSJ. Bruce Fields 		 * case to make svc_defer() work--it finds the original
8957c9fdcfbSJ. Bruce Fields 		 * buffer start using buf->len - buf->head[0].iov_len. */
8967c9fdcfbSJ. Bruce Fields 		buf->head[0].iov_len -= pad;
8977c9fdcfbSJ. Bruce Fields 	}
8987c9fdcfbSJ. Bruce Fields }
8997c9fdcfbSJ. Bruce Fields 
9007c9fdcfbSJ. Bruce Fields static int
9017c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
9027c9fdcfbSJ. Bruce Fields {
9037c9fdcfbSJ. Bruce Fields 	u32 priv_len, maj_stat;
9047c9fdcfbSJ. Bruce Fields 	int pad, saved_len, remaining_len, offset;
9057c9fdcfbSJ. Bruce Fields 
906779fb0f3SJeff Layton 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
9077c9fdcfbSJ. Bruce Fields 
90876994313SAlexey Dobriyan 	priv_len = svc_getnl(&buf->head[0]);
9097c9fdcfbSJ. Bruce Fields 	if (rqstp->rq_deferred) {
9107c9fdcfbSJ. Bruce Fields 		/* Already decrypted last time through! The sequence number
9117c9fdcfbSJ. Bruce Fields 		 * check at out_seq is unnecessary but harmless: */
9127c9fdcfbSJ. Bruce Fields 		goto out_seq;
9137c9fdcfbSJ. Bruce Fields 	}
9147c9fdcfbSJ. Bruce Fields 	/* buf->len is the number of bytes from the original start of the
9157c9fdcfbSJ. Bruce Fields 	 * request to the end, where head[0].iov_len is just the bytes
9167c9fdcfbSJ. Bruce Fields 	 * not yet read from the head, so these two values are different: */
9177c9fdcfbSJ. Bruce Fields 	remaining_len = total_buf_len(buf);
9187c9fdcfbSJ. Bruce Fields 	if (priv_len > remaining_len)
9197c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9207c9fdcfbSJ. Bruce Fields 	pad = remaining_len - priv_len;
9217c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
9227c9fdcfbSJ. Bruce Fields 	fix_priv_head(buf, pad);
9237c9fdcfbSJ. Bruce Fields 
9247c9fdcfbSJ. Bruce Fields 	/* Maybe it would be better to give gss_unwrap a length parameter: */
9257c9fdcfbSJ. Bruce Fields 	saved_len = buf->len;
9267c9fdcfbSJ. Bruce Fields 	buf->len = priv_len;
9277c9fdcfbSJ. Bruce Fields 	maj_stat = gss_unwrap(ctx, 0, buf);
9287c9fdcfbSJ. Bruce Fields 	pad = priv_len - buf->len;
9297c9fdcfbSJ. Bruce Fields 	buf->len = saved_len;
9307c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
9317c9fdcfbSJ. Bruce Fields 	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
9327c9fdcfbSJ. Bruce Fields 	 * In the krb5p case, at least, the data ends up offset, so we need to
9337c9fdcfbSJ. Bruce Fields 	 * move it around. */
9347c9fdcfbSJ. Bruce Fields 	/* XXX: This is very inefficient.  It would be better to either do
9357c9fdcfbSJ. Bruce Fields 	 * this while we encrypt, or maybe in the receive code, if we can peak
9367c9fdcfbSJ. Bruce Fields 	 * ahead and work out the service and mechanism there. */
9377c9fdcfbSJ. Bruce Fields 	offset = buf->head[0].iov_len % 4;
9387c9fdcfbSJ. Bruce Fields 	if (offset) {
9397c9fdcfbSJ. Bruce Fields 		buf->buflen = RPCSVC_MAXPAYLOAD;
9407c9fdcfbSJ. Bruce Fields 		xdr_shift_buf(buf, offset);
9417c9fdcfbSJ. Bruce Fields 		fix_priv_head(buf, pad);
9427c9fdcfbSJ. Bruce Fields 	}
9437c9fdcfbSJ. Bruce Fields 	if (maj_stat != GSS_S_COMPLETE)
9447c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9457c9fdcfbSJ. Bruce Fields out_seq:
94676994313SAlexey Dobriyan 	if (svc_getnl(&buf->head[0]) != seq)
9477c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9487c9fdcfbSJ. Bruce Fields 	return 0;
9497c9fdcfbSJ. Bruce Fields }
9507c9fdcfbSJ. Bruce Fields 
9511da177e4SLinus Torvalds struct gss_svc_data {
9521da177e4SLinus Torvalds 	/* decoded gss client cred: */
9531da177e4SLinus Torvalds 	struct rpc_gss_wire_cred	clcred;
9545b304bc5SJ.Bruce Fields 	/* save a pointer to the beginning of the encoded verifier,
9555b304bc5SJ.Bruce Fields 	 * for use in encryption/checksumming in svcauth_gss_release: */
9565b304bc5SJ.Bruce Fields 	__be32				*verf_start;
9571da177e4SLinus Torvalds 	struct rsc			*rsci;
9581da177e4SLinus Torvalds };
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds static int
9611da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp)
9621da177e4SLinus Torvalds {
9631da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
9641da177e4SLinus Torvalds 	struct rsc *rsci = svcdata->rsci;
9651da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &svcdata->clcred;
9663ab4d8b1SJ. Bruce Fields 	int stat;
9671da177e4SLinus Torvalds 
9683ab4d8b1SJ. Bruce Fields 	/*
9693ab4d8b1SJ. Bruce Fields 	 * A gss export can be specified either by:
9703ab4d8b1SJ. Bruce Fields 	 * 	export	*(sec=krb5,rw)
9713ab4d8b1SJ. Bruce Fields 	 * or by
9723ab4d8b1SJ. Bruce Fields 	 * 	export gss/krb5(rw)
9733ab4d8b1SJ. Bruce Fields 	 * The latter is deprecated; but for backwards compatibility reasons
9743ab4d8b1SJ. Bruce Fields 	 * the nfsd code will still fall back on trying it if the former
9753ab4d8b1SJ. Bruce Fields 	 * doesn't work; so we try to make both available to nfsd, below.
9763ab4d8b1SJ. Bruce Fields 	 */
9773ab4d8b1SJ. Bruce Fields 	rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc);
9783ab4d8b1SJ. Bruce Fields 	if (rqstp->rq_gssclient == NULL)
9791da177e4SLinus Torvalds 		return SVC_DENIED;
9803ab4d8b1SJ. Bruce Fields 	stat = svcauth_unix_set_client(rqstp);
9811ebede86SNeilBrown 	if (stat == SVC_DROP || stat == SVC_CLOSE)
9823ab4d8b1SJ. Bruce Fields 		return stat;
9831da177e4SLinus Torvalds 	return SVC_OK;
9841da177e4SLinus Torvalds }
9851da177e4SLinus Torvalds 
98691a4762eSKevin Coffman static inline int
987fc2952a2SSimo Sorce gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
988fc2952a2SSimo Sorce 		struct xdr_netobj *out_handle, int *major_status)
98991a4762eSKevin Coffman {
99091a4762eSKevin Coffman 	struct rsc *rsci;
99154f9247bSFrank Filz 	int        rc;
99291a4762eSKevin Coffman 
993fc2952a2SSimo Sorce 	if (*major_status != GSS_S_COMPLETE)
99491a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
995fc2952a2SSimo Sorce 	rsci = gss_svc_searchbyctx(cd, out_handle);
99691a4762eSKevin Coffman 	if (rsci == NULL) {
997fc2952a2SSimo Sorce 		*major_status = GSS_S_NO_CONTEXT;
99891a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
99991a4762eSKevin Coffman 	}
100054f9247bSFrank Filz 	rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
1001a1db410dSStanislav Kinsbursky 	cache_put(&rsci->h, cd);
100254f9247bSFrank Filz 	return rc;
100391a4762eSKevin Coffman }
100491a4762eSKevin Coffman 
1005fc2952a2SSimo Sorce static inline int
1006030d794bSSimo Sorce gss_read_common_verf(struct rpc_gss_wire_cred *gc,
1007fc2952a2SSimo Sorce 		     struct kvec *argv, __be32 *authp,
1008030d794bSSimo Sorce 		     struct xdr_netobj *in_handle)
1009fc2952a2SSimo Sorce {
1010fc2952a2SSimo Sorce 	/* Read the verifier; should be NULL: */
1011fc2952a2SSimo Sorce 	*authp = rpc_autherr_badverf;
1012fc2952a2SSimo Sorce 	if (argv->iov_len < 2 * 4)
1013fc2952a2SSimo Sorce 		return SVC_DENIED;
1014fc2952a2SSimo Sorce 	if (svc_getnl(argv) != RPC_AUTH_NULL)
1015fc2952a2SSimo Sorce 		return SVC_DENIED;
1016fc2952a2SSimo Sorce 	if (svc_getnl(argv) != 0)
1017fc2952a2SSimo Sorce 		return SVC_DENIED;
1018fc2952a2SSimo Sorce 	/* Martial context handle and token for upcall: */
1019fc2952a2SSimo Sorce 	*authp = rpc_autherr_badcred;
1020fc2952a2SSimo Sorce 	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
1021fc2952a2SSimo Sorce 		return SVC_DENIED;
1022fc2952a2SSimo Sorce 	if (dup_netobj(in_handle, &gc->gc_ctx))
1023fc2952a2SSimo Sorce 		return SVC_CLOSE;
1024fc2952a2SSimo Sorce 	*authp = rpc_autherr_badverf;
1025030d794bSSimo Sorce 
1026030d794bSSimo Sorce 	return 0;
1027030d794bSSimo Sorce }
1028030d794bSSimo Sorce 
1029030d794bSSimo Sorce static inline int
1030030d794bSSimo Sorce gss_read_verf(struct rpc_gss_wire_cred *gc,
1031030d794bSSimo Sorce 	      struct kvec *argv, __be32 *authp,
1032030d794bSSimo Sorce 	      struct xdr_netobj *in_handle,
1033030d794bSSimo Sorce 	      struct xdr_netobj *in_token)
1034030d794bSSimo Sorce {
1035030d794bSSimo Sorce 	struct xdr_netobj tmpobj;
1036030d794bSSimo Sorce 	int res;
1037030d794bSSimo Sorce 
1038030d794bSSimo Sorce 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1039030d794bSSimo Sorce 	if (res)
1040030d794bSSimo Sorce 		return res;
1041030d794bSSimo Sorce 
1042fc2952a2SSimo Sorce 	if (svc_safe_getnetobj(argv, &tmpobj)) {
1043fc2952a2SSimo Sorce 		kfree(in_handle->data);
1044fc2952a2SSimo Sorce 		return SVC_DENIED;
1045fc2952a2SSimo Sorce 	}
1046fc2952a2SSimo Sorce 	if (dup_netobj(in_token, &tmpobj)) {
1047fc2952a2SSimo Sorce 		kfree(in_handle->data);
1048fc2952a2SSimo Sorce 		return SVC_CLOSE;
1049fc2952a2SSimo Sorce 	}
1050fc2952a2SSimo Sorce 
1051fc2952a2SSimo Sorce 	return 0;
1052fc2952a2SSimo Sorce }
1053fc2952a2SSimo Sorce 
1054030d794bSSimo Sorce /* Ok this is really heavily depending on a set of semantics in
1055030d794bSSimo Sorce  * how rqstp is set up by svc_recv and pages laid down by the
1056030d794bSSimo Sorce  * server when reading a request. We are basically guaranteed that
1057030d794bSSimo Sorce  * the token lays all down linearly across a set of pages, starting
1058030d794bSSimo Sorce  * at iov_base in rq_arg.head[0] which happens to be the first of a
1059030d794bSSimo Sorce  * set of pages stored in rq_pages[].
1060030d794bSSimo Sorce  * rq_arg.head[0].iov_base will provide us the page_base to pass
1061030d794bSSimo Sorce  * to the upcall.
1062030d794bSSimo Sorce  */
1063030d794bSSimo Sorce static inline int
1064030d794bSSimo Sorce gss_read_proxy_verf(struct svc_rqst *rqstp,
1065030d794bSSimo Sorce 		    struct rpc_gss_wire_cred *gc, __be32 *authp,
1066030d794bSSimo Sorce 		    struct xdr_netobj *in_handle,
1067030d794bSSimo Sorce 		    struct gssp_in_token *in_token)
1068030d794bSSimo Sorce {
1069030d794bSSimo Sorce 	struct kvec *argv = &rqstp->rq_arg.head[0];
1070030d794bSSimo Sorce 	u32 inlen;
1071030d794bSSimo Sorce 	int res;
1072030d794bSSimo Sorce 
1073030d794bSSimo Sorce 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1074030d794bSSimo Sorce 	if (res)
1075030d794bSSimo Sorce 		return res;
1076030d794bSSimo Sorce 
1077030d794bSSimo Sorce 	inlen = svc_getnl(argv);
1078030d794bSSimo Sorce 	if (inlen > (argv->iov_len + rqstp->rq_arg.page_len))
1079030d794bSSimo Sorce 		return SVC_DENIED;
1080030d794bSSimo Sorce 
1081030d794bSSimo Sorce 	in_token->pages = rqstp->rq_pages;
1082030d794bSSimo Sorce 	in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK;
1083030d794bSSimo Sorce 	in_token->page_len = inlen;
1084030d794bSSimo Sorce 
1085030d794bSSimo Sorce 	return 0;
1086030d794bSSimo Sorce }
1087030d794bSSimo Sorce 
1088fc2952a2SSimo Sorce static inline int
1089fc2952a2SSimo Sorce gss_write_resv(struct kvec *resv, size_t size_limit,
1090fc2952a2SSimo Sorce 	       struct xdr_netobj *out_handle, struct xdr_netobj *out_token,
1091fc2952a2SSimo Sorce 	       int major_status, int minor_status)
1092fc2952a2SSimo Sorce {
1093fc2952a2SSimo Sorce 	if (resv->iov_len + 4 > size_limit)
1094fc2952a2SSimo Sorce 		return -1;
1095fc2952a2SSimo Sorce 	svc_putnl(resv, RPC_SUCCESS);
1096fc2952a2SSimo Sorce 	if (svc_safe_putnetobj(resv, out_handle))
1097fc2952a2SSimo Sorce 		return -1;
1098fc2952a2SSimo Sorce 	if (resv->iov_len + 3 * 4 > size_limit)
1099fc2952a2SSimo Sorce 		return -1;
1100fc2952a2SSimo Sorce 	svc_putnl(resv, major_status);
1101fc2952a2SSimo Sorce 	svc_putnl(resv, minor_status);
1102fc2952a2SSimo Sorce 	svc_putnl(resv, GSS_SEQ_WIN);
1103fc2952a2SSimo Sorce 	if (svc_safe_putnetobj(resv, out_token))
1104fc2952a2SSimo Sorce 		return -1;
1105fc2952a2SSimo Sorce 	return 0;
1106fc2952a2SSimo Sorce }
1107fc2952a2SSimo Sorce 
11081da177e4SLinus Torvalds /*
110921fcd02bSJ. Bruce Fields  * Having read the cred already and found we're in the context
111021fcd02bSJ. Bruce Fields  * initiation case, read the verifier and initiate (or check the results
111121fcd02bSJ. Bruce Fields  * of) upcalls to userspace for help with context initiation.  If
111221fcd02bSJ. Bruce Fields  * the upcall results are available, write the verifier and result.
111321fcd02bSJ. Bruce Fields  * Otherwise, drop the request pending an answer to the upcall.
111421fcd02bSJ. Bruce Fields  */
1115030d794bSSimo Sorce static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
111621fcd02bSJ. Bruce Fields 			struct rpc_gss_wire_cred *gc, __be32 *authp)
111721fcd02bSJ. Bruce Fields {
111821fcd02bSJ. Bruce Fields 	struct kvec *argv = &rqstp->rq_arg.head[0];
111921fcd02bSJ. Bruce Fields 	struct kvec *resv = &rqstp->rq_res.head[0];
112021fcd02bSJ. Bruce Fields 	struct rsi *rsip, rsikey;
1121980e5a40SJ. Bruce Fields 	int ret;
1122a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
112321fcd02bSJ. Bruce Fields 
112421fcd02bSJ. Bruce Fields 	memset(&rsikey, 0, sizeof(rsikey));
1125fc2952a2SSimo Sorce 	ret = gss_read_verf(gc, argv, authp,
1126fc2952a2SSimo Sorce 			    &rsikey.in_handle, &rsikey.in_token);
1127fc2952a2SSimo Sorce 	if (ret)
1128fc2952a2SSimo Sorce 		return ret;
112921fcd02bSJ. Bruce Fields 
113021fcd02bSJ. Bruce Fields 	/* Perform upcall, or find upcall result: */
1131a1db410dSStanislav Kinsbursky 	rsip = rsi_lookup(sn->rsi_cache, &rsikey);
113221fcd02bSJ. Bruce Fields 	rsi_free(&rsikey);
113321fcd02bSJ. Bruce Fields 	if (!rsip)
11341ebede86SNeilBrown 		return SVC_CLOSE;
1135a1db410dSStanislav Kinsbursky 	if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
113621fcd02bSJ. Bruce Fields 		/* No upcall result: */
11371ebede86SNeilBrown 		return SVC_CLOSE;
11382ed5282cSNeilBrown 
11391ebede86SNeilBrown 	ret = SVC_CLOSE;
114021fcd02bSJ. Bruce Fields 	/* Got an answer to the upcall; use it: */
1141fc2952a2SSimo Sorce 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1142fc2952a2SSimo Sorce 				&rsip->out_handle, &rsip->major_status))
1143980e5a40SJ. Bruce Fields 		goto out;
1144fc2952a2SSimo Sorce 	if (gss_write_resv(resv, PAGE_SIZE,
1145fc2952a2SSimo Sorce 			   &rsip->out_handle, &rsip->out_token,
1146fc2952a2SSimo Sorce 			   rsip->major_status, rsip->minor_status))
1147980e5a40SJ. Bruce Fields 		goto out;
11482ed5282cSNeilBrown 
1149980e5a40SJ. Bruce Fields 	ret = SVC_COMPLETE;
1150980e5a40SJ. Bruce Fields out:
1151a1db410dSStanislav Kinsbursky 	cache_put(&rsip->h, sn->rsi_cache);
1152980e5a40SJ. Bruce Fields 	return ret;
115321fcd02bSJ. Bruce Fields }
115421fcd02bSJ. Bruce Fields 
1155030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd,
1156030d794bSSimo Sorce 				struct gssp_upcall_data *ud,
1157030d794bSSimo Sorce 				uint64_t *handle)
1158030d794bSSimo Sorce {
1159030d794bSSimo Sorce 	struct rsc rsci, *rscp = NULL;
1160030d794bSSimo Sorce 	static atomic64_t ctxhctr;
1161030d794bSSimo Sorce 	long long ctxh;
1162030d794bSSimo Sorce 	struct gss_api_mech *gm = NULL;
1163030d794bSSimo Sorce 	time_t expiry;
1164030d794bSSimo Sorce 	int status = -EINVAL;
1165030d794bSSimo Sorce 
1166030d794bSSimo Sorce 	memset(&rsci, 0, sizeof(rsci));
1167030d794bSSimo Sorce 	/* context handle */
1168030d794bSSimo Sorce 	status = -ENOMEM;
1169030d794bSSimo Sorce 	/* the handle needs to be just a unique id,
1170030d794bSSimo Sorce 	 * use a static counter */
1171030d794bSSimo Sorce 	ctxh = atomic64_inc_return(&ctxhctr);
1172030d794bSSimo Sorce 
1173030d794bSSimo Sorce 	/* make a copy for the caller */
1174030d794bSSimo Sorce 	*handle = ctxh;
1175030d794bSSimo Sorce 
1176030d794bSSimo Sorce 	/* make a copy for the rsc cache */
1177030d794bSSimo Sorce 	if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t)))
1178030d794bSSimo Sorce 		goto out;
1179030d794bSSimo Sorce 	rscp = rsc_lookup(cd, &rsci);
1180030d794bSSimo Sorce 	if (!rscp)
1181030d794bSSimo Sorce 		goto out;
1182030d794bSSimo Sorce 
1183030d794bSSimo Sorce 	/* creds */
1184030d794bSSimo Sorce 	if (!ud->found_creds) {
1185030d794bSSimo Sorce 		/* userspace seem buggy, we should always get at least a
1186030d794bSSimo Sorce 		 * mapping to nobody */
11873be34555SJ. Bruce Fields 		dprintk("RPC:       No creds found!\n");
11883be34555SJ. Bruce Fields 		goto out;
1189030d794bSSimo Sorce 	} else {
1190030d794bSSimo Sorce 
1191030d794bSSimo Sorce 		/* steal creds */
1192030d794bSSimo Sorce 		rsci.cred = ud->creds;
1193030d794bSSimo Sorce 		memset(&ud->creds, 0, sizeof(struct svc_cred));
1194030d794bSSimo Sorce 
1195030d794bSSimo Sorce 		status = -EOPNOTSUPP;
1196030d794bSSimo Sorce 		/* get mech handle from OID */
1197030d794bSSimo Sorce 		gm = gss_mech_get_by_OID(&ud->mech_oid);
1198030d794bSSimo Sorce 		if (!gm)
1199030d794bSSimo Sorce 			goto out;
12007193bd17SJ. Bruce Fields 		rsci.cred.cr_gss_mech = gm;
1201030d794bSSimo Sorce 
1202030d794bSSimo Sorce 		status = -EINVAL;
1203030d794bSSimo Sorce 		/* mech-specific data: */
1204030d794bSSimo Sorce 		status = gss_import_sec_context(ud->out_handle.data,
1205030d794bSSimo Sorce 						ud->out_handle.len,
1206030d794bSSimo Sorce 						gm, &rsci.mechctx,
1207030d794bSSimo Sorce 						&expiry, GFP_KERNEL);
1208030d794bSSimo Sorce 		if (status)
1209030d794bSSimo Sorce 			goto out;
1210030d794bSSimo Sorce 	}
1211030d794bSSimo Sorce 
1212030d794bSSimo Sorce 	rsci.h.expiry_time = expiry;
1213030d794bSSimo Sorce 	rscp = rsc_update(cd, &rsci, rscp);
1214030d794bSSimo Sorce 	status = 0;
1215030d794bSSimo Sorce out:
1216030d794bSSimo Sorce 	rsc_free(&rsci);
1217030d794bSSimo Sorce 	if (rscp)
1218030d794bSSimo Sorce 		cache_put(&rscp->h, cd);
1219030d794bSSimo Sorce 	else
1220030d794bSSimo Sorce 		status = -ENOMEM;
1221030d794bSSimo Sorce 	return status;
1222030d794bSSimo Sorce }
1223030d794bSSimo Sorce 
1224030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
1225030d794bSSimo Sorce 			struct rpc_gss_wire_cred *gc, __be32 *authp)
1226030d794bSSimo Sorce {
1227030d794bSSimo Sorce 	struct kvec *resv = &rqstp->rq_res.head[0];
1228030d794bSSimo Sorce 	struct xdr_netobj cli_handle;
1229030d794bSSimo Sorce 	struct gssp_upcall_data ud;
1230030d794bSSimo Sorce 	uint64_t handle;
1231030d794bSSimo Sorce 	int status;
1232030d794bSSimo Sorce 	int ret;
1233030d794bSSimo Sorce 	struct net *net = rqstp->rq_xprt->xpt_net;
1234030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1235030d794bSSimo Sorce 
1236030d794bSSimo Sorce 	memset(&ud, 0, sizeof(ud));
1237030d794bSSimo Sorce 	ret = gss_read_proxy_verf(rqstp, gc, authp,
1238030d794bSSimo Sorce 				  &ud.in_handle, &ud.in_token);
1239030d794bSSimo Sorce 	if (ret)
1240030d794bSSimo Sorce 		return ret;
1241030d794bSSimo Sorce 
1242030d794bSSimo Sorce 	ret = SVC_CLOSE;
1243030d794bSSimo Sorce 
1244030d794bSSimo Sorce 	/* Perform synchronous upcall to gss-proxy */
1245030d794bSSimo Sorce 	status = gssp_accept_sec_context_upcall(net, &ud);
1246030d794bSSimo Sorce 	if (status)
1247030d794bSSimo Sorce 		goto out;
1248030d794bSSimo Sorce 
124904d70edaSScott Mayhew 	dprintk("RPC:       svcauth_gss: gss major status = %d "
125004d70edaSScott Mayhew 			"minor status = %d\n",
125104d70edaSScott Mayhew 			ud.major_status, ud.minor_status);
1252030d794bSSimo Sorce 
1253030d794bSSimo Sorce 	switch (ud.major_status) {
1254030d794bSSimo Sorce 	case GSS_S_CONTINUE_NEEDED:
1255030d794bSSimo Sorce 		cli_handle = ud.out_handle;
1256030d794bSSimo Sorce 		break;
1257030d794bSSimo Sorce 	case GSS_S_COMPLETE:
1258030d794bSSimo Sorce 		status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle);
1259030d794bSSimo Sorce 		if (status)
1260030d794bSSimo Sorce 			goto out;
1261030d794bSSimo Sorce 		cli_handle.data = (u8 *)&handle;
1262030d794bSSimo Sorce 		cli_handle.len = sizeof(handle);
1263030d794bSSimo Sorce 		break;
1264030d794bSSimo Sorce 	default:
1265030d794bSSimo Sorce 		ret = SVC_CLOSE;
1266030d794bSSimo Sorce 		goto out;
1267030d794bSSimo Sorce 	}
1268030d794bSSimo Sorce 
1269030d794bSSimo Sorce 	/* Got an answer to the upcall; use it: */
1270030d794bSSimo Sorce 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1271030d794bSSimo Sorce 				&cli_handle, &ud.major_status))
1272030d794bSSimo Sorce 		goto out;
1273030d794bSSimo Sorce 	if (gss_write_resv(resv, PAGE_SIZE,
1274030d794bSSimo Sorce 			   &cli_handle, &ud.out_token,
1275030d794bSSimo Sorce 			   ud.major_status, ud.minor_status))
1276030d794bSSimo Sorce 		goto out;
1277030d794bSSimo Sorce 
1278030d794bSSimo Sorce 	ret = SVC_COMPLETE;
1279030d794bSSimo Sorce out:
1280030d794bSSimo Sorce 	gssp_free_upcall_data(&ud);
1281030d794bSSimo Sorce 	return ret;
1282030d794bSSimo Sorce }
1283030d794bSSimo Sorce 
12840fdc2678SJeff Layton /*
12850fdc2678SJeff Layton  * Try to set the sn->use_gss_proxy variable to a new value. We only allow
12860fdc2678SJeff Layton  * it to be changed if it's currently undefined (-1). If it's any other value
12870fdc2678SJeff Layton  * then return -EBUSY unless the type wouldn't have changed anyway.
12880fdc2678SJeff Layton  */
12890fdc2678SJeff Layton static int set_gss_proxy(struct net *net, int type)
12900fdc2678SJeff Layton {
12910fdc2678SJeff Layton 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
12920fdc2678SJeff Layton 	int ret;
12930fdc2678SJeff Layton 
12940fdc2678SJeff Layton 	WARN_ON_ONCE(type != 0 && type != 1);
12950fdc2678SJeff Layton 	ret = cmpxchg(&sn->use_gss_proxy, -1, type);
12960fdc2678SJeff Layton 	if (ret != -1 && ret != type)
12970fdc2678SJeff Layton 		return -EBUSY;
12980fdc2678SJeff Layton 	return 0;
12990fdc2678SJeff Layton }
1300030d794bSSimo Sorce 
1301030d794bSSimo Sorce static bool use_gss_proxy(struct net *net)
1302030d794bSSimo Sorce {
1303030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1304030d794bSSimo Sorce 
13050fdc2678SJeff Layton 	/* If use_gss_proxy is still undefined, then try to disable it */
13060fdc2678SJeff Layton 	if (sn->use_gss_proxy == -1)
13070fdc2678SJeff Layton 		set_gss_proxy(net, 0);
1308030d794bSSimo Sorce 	return sn->use_gss_proxy;
1309030d794bSSimo Sorce }
1310030d794bSSimo Sorce 
13110ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS
13120ff3bab5SJ. Bruce Fields 
1313030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf,
1314030d794bSSimo Sorce 			 size_t count, loff_t *ppos)
1315030d794bSSimo Sorce {
1316e77e4300SAl Viro 	struct net *net = PDE_DATA(file_inode(file));
1317030d794bSSimo Sorce 	char tbuf[20];
1318030d794bSSimo Sorce 	unsigned long i;
1319030d794bSSimo Sorce 	int res;
1320030d794bSSimo Sorce 
1321030d794bSSimo Sorce 	if (*ppos || count > sizeof(tbuf)-1)
1322030d794bSSimo Sorce 		return -EINVAL;
1323030d794bSSimo Sorce 	if (copy_from_user(tbuf, buf, count))
1324030d794bSSimo Sorce 		return -EFAULT;
1325030d794bSSimo Sorce 
1326030d794bSSimo Sorce 	tbuf[count] = 0;
1327030d794bSSimo Sorce 	res = kstrtoul(tbuf, 0, &i);
1328030d794bSSimo Sorce 	if (res)
1329030d794bSSimo Sorce 		return res;
1330030d794bSSimo Sorce 	if (i != 1)
1331030d794bSSimo Sorce 		return -EINVAL;
1332a92e5eb1SJeff Layton 	res = set_gssp_clnt(net);
1333030d794bSSimo Sorce 	if (res)
1334030d794bSSimo Sorce 		return res;
1335a92e5eb1SJeff Layton 	res = set_gss_proxy(net, 1);
1336030d794bSSimo Sorce 	if (res)
1337030d794bSSimo Sorce 		return res;
1338030d794bSSimo Sorce 	return count;
1339030d794bSSimo Sorce }
1340030d794bSSimo Sorce 
1341030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf,
1342030d794bSSimo Sorce 			 size_t count, loff_t *ppos)
1343030d794bSSimo Sorce {
1344e77e4300SAl Viro 	struct net *net = PDE_DATA(file_inode(file));
13451654a04cSJeff Layton 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1346030d794bSSimo Sorce 	unsigned long p = *ppos;
1347030d794bSSimo Sorce 	char tbuf[10];
1348030d794bSSimo Sorce 	size_t len;
1349030d794bSSimo Sorce 
13501654a04cSJeff Layton 	snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
1351030d794bSSimo Sorce 	len = strlen(tbuf);
1352030d794bSSimo Sorce 	if (p >= len)
1353030d794bSSimo Sorce 		return 0;
1354030d794bSSimo Sorce 	len -= p;
1355030d794bSSimo Sorce 	if (len > count)
1356030d794bSSimo Sorce 		len = count;
1357030d794bSSimo Sorce 	if (copy_to_user(buf, (void *)(tbuf+p), len))
1358030d794bSSimo Sorce 		return -EFAULT;
1359030d794bSSimo Sorce 	*ppos += len;
1360030d794bSSimo Sorce 	return len;
1361030d794bSSimo Sorce }
1362030d794bSSimo Sorce 
1363030d794bSSimo Sorce static const struct file_operations use_gss_proxy_ops = {
1364030d794bSSimo Sorce 	.open = nonseekable_open,
1365030d794bSSimo Sorce 	.write = write_gssp,
1366030d794bSSimo Sorce 	.read = read_gssp,
1367030d794bSSimo Sorce };
1368030d794bSSimo Sorce 
1369030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net)
1370030d794bSSimo Sorce {
1371030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1372030d794bSSimo Sorce 	struct proc_dir_entry **p = &sn->use_gssp_proc;
1373030d794bSSimo Sorce 
1374030d794bSSimo Sorce 	sn->use_gss_proxy = -1;
1375030d794bSSimo Sorce 	*p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR,
1376030d794bSSimo Sorce 			      sn->proc_net_rpc,
1377030d794bSSimo Sorce 			      &use_gss_proxy_ops, net);
1378030d794bSSimo Sorce 	if (!*p)
1379030d794bSSimo Sorce 		return -ENOMEM;
1380030d794bSSimo Sorce 	init_gssp_clnt(sn);
1381030d794bSSimo Sorce 	return 0;
1382030d794bSSimo Sorce }
1383030d794bSSimo Sorce 
1384030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net)
1385030d794bSSimo Sorce {
1386030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1387030d794bSSimo Sorce 
1388030d794bSSimo Sorce 	if (sn->use_gssp_proc) {
1389030d794bSSimo Sorce 		remove_proc_entry("use-gss-proxy", sn->proc_net_rpc);
1390030d794bSSimo Sorce 		clear_gssp_clnt(sn);
1391030d794bSSimo Sorce 	}
1392030d794bSSimo Sorce }
13930ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */
13940ff3bab5SJ. Bruce Fields 
13950ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net)
13960ff3bab5SJ. Bruce Fields {
13970ff3bab5SJ. Bruce Fields 	return 0;
13980ff3bab5SJ. Bruce Fields }
13990ff3bab5SJ. Bruce Fields 
14000ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
1401030d794bSSimo Sorce 
1402030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */
1403030d794bSSimo Sorce 
140421fcd02bSJ. Bruce Fields /*
14051da177e4SLinus Torvalds  * Accept an rpcsec packet.
14061da177e4SLinus Torvalds  * If context establishment, punt to user space
14071da177e4SLinus Torvalds  * If data exchange, verify/decrypt
14081da177e4SLinus Torvalds  * If context destruction, handle here
14091da177e4SLinus Torvalds  * In the context establishment and destruction case we encode
14101da177e4SLinus Torvalds  * response here and return SVC_COMPLETE.
14111da177e4SLinus Torvalds  */
14121da177e4SLinus Torvalds static int
1413d8ed029dSAlexey Dobriyan svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
14141da177e4SLinus Torvalds {
14151da177e4SLinus Torvalds 	struct kvec	*argv = &rqstp->rq_arg.head[0];
14161da177e4SLinus Torvalds 	struct kvec	*resv = &rqstp->rq_res.head[0];
14171da177e4SLinus Torvalds 	u32		crlen;
14181da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
14191da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc;
14201da177e4SLinus Torvalds 	struct rsc	*rsci = NULL;
1421d8ed029dSAlexey Dobriyan 	__be32		*rpcstart;
1422d8ed029dSAlexey Dobriyan 	__be32		*reject_stat = resv->iov_base + resv->iov_len;
14231da177e4SLinus Torvalds 	int		ret;
1424a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
14251da177e4SLinus Torvalds 
14268885cb36SChuck Lever 	dprintk("RPC:       svcauth_gss: argv->iov_len = %zd\n",
14278885cb36SChuck Lever 			argv->iov_len);
14281da177e4SLinus Torvalds 
14291da177e4SLinus Torvalds 	*authp = rpc_autherr_badcred;
14301da177e4SLinus Torvalds 	if (!svcdata)
14311da177e4SLinus Torvalds 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
14321da177e4SLinus Torvalds 	if (!svcdata)
14331da177e4SLinus Torvalds 		goto auth_err;
14341da177e4SLinus Torvalds 	rqstp->rq_auth_data = svcdata;
14355b304bc5SJ.Bruce Fields 	svcdata->verf_start = NULL;
14361da177e4SLinus Torvalds 	svcdata->rsci = NULL;
14371da177e4SLinus Torvalds 	gc = &svcdata->clcred;
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds 	/* start of rpc packet is 7 u32's back from here:
14401da177e4SLinus Torvalds 	 * xid direction rpcversion prog vers proc flavour
14411da177e4SLinus Torvalds 	 */
14421da177e4SLinus Torvalds 	rpcstart = argv->iov_base;
14431da177e4SLinus Torvalds 	rpcstart -= 7;
14441da177e4SLinus Torvalds 
14451da177e4SLinus Torvalds 	/* credential is:
14461da177e4SLinus Torvalds 	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
144725985edcSLucas De Marchi 	 * at least 5 u32s, and is preceded by length, so that makes 6.
14481da177e4SLinus Torvalds 	 */
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds 	if (argv->iov_len < 5 * 4)
14511da177e4SLinus Torvalds 		goto auth_err;
145276994313SAlexey Dobriyan 	crlen = svc_getnl(argv);
145376994313SAlexey Dobriyan 	if (svc_getnl(argv) != RPC_GSS_VERSION)
14541da177e4SLinus Torvalds 		goto auth_err;
145576994313SAlexey Dobriyan 	gc->gc_proc = svc_getnl(argv);
145676994313SAlexey Dobriyan 	gc->gc_seq = svc_getnl(argv);
145776994313SAlexey Dobriyan 	gc->gc_svc = svc_getnl(argv);
14581da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &gc->gc_ctx))
14591da177e4SLinus Torvalds 		goto auth_err;
14601da177e4SLinus Torvalds 	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
14611da177e4SLinus Torvalds 		goto auth_err;
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds 	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
14641da177e4SLinus Torvalds 		goto auth_err;
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
14671da177e4SLinus Torvalds 	switch (gc->gc_proc) {
14681da177e4SLinus Torvalds 	case RPC_GSS_PROC_INIT:
14691da177e4SLinus Torvalds 	case RPC_GSS_PROC_CONTINUE_INIT:
1470030d794bSSimo Sorce 		if (use_gss_proxy(SVC_NET(rqstp)))
1471030d794bSSimo Sorce 			return svcauth_gss_proxy_init(rqstp, gc, authp);
1472030d794bSSimo Sorce 		else
1473030d794bSSimo Sorce 			return svcauth_gss_legacy_init(rqstp, gc, authp);
14741da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
14751da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
147621fcd02bSJ. Bruce Fields 		/* Look up the context, and check the verifier: */
14771da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
1478a1db410dSStanislav Kinsbursky 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
14791da177e4SLinus Torvalds 		if (!rsci)
14801da177e4SLinus Torvalds 			goto auth_err;
14811da177e4SLinus Torvalds 		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
14821da177e4SLinus Torvalds 		case SVC_OK:
14831da177e4SLinus Torvalds 			break;
14841da177e4SLinus Torvalds 		case SVC_DENIED:
14851da177e4SLinus Torvalds 			goto auth_err;
14861da177e4SLinus Torvalds 		case SVC_DROP:
14871da177e4SLinus Torvalds 			goto drop;
14881da177e4SLinus Torvalds 		}
14891da177e4SLinus Torvalds 		break;
14901da177e4SLinus Torvalds 	default:
14911da177e4SLinus Torvalds 		*authp = rpc_autherr_rejectedcred;
14921da177e4SLinus Torvalds 		goto auth_err;
14931da177e4SLinus Torvalds 	}
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 	/* now act upon the command: */
14961da177e4SLinus Torvalds 	switch (gc->gc_proc) {
14971da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
1498c5e434c9SWei Yongjun 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
1499c5e434c9SWei Yongjun 			goto auth_err;
15002b477c00SNeil Brown 		/* Delete the entry from the cache_list and call cache_put */
15012b477c00SNeil Brown 		sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
15021da177e4SLinus Torvalds 		if (resv->iov_len + 4 > PAGE_SIZE)
15031da177e4SLinus Torvalds 			goto drop;
150476994313SAlexey Dobriyan 		svc_putnl(resv, RPC_SUCCESS);
15051da177e4SLinus Torvalds 		goto complete;
15061da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
15071da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
15085b304bc5SJ.Bruce Fields 		svcdata->verf_start = resv->iov_base + resv->iov_len;
15091da177e4SLinus Torvalds 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
15101da177e4SLinus Torvalds 			goto auth_err;
15111da177e4SLinus Torvalds 		rqstp->rq_cred = rsci->cred;
15121da177e4SLinus Torvalds 		get_group_info(rsci->cred.cr_group_info);
15131da177e4SLinus Torvalds 		*authp = rpc_autherr_badcred;
15141da177e4SLinus Torvalds 		switch (gc->gc_svc) {
15151da177e4SLinus Torvalds 		case RPC_GSS_SVC_NONE:
15161da177e4SLinus Torvalds 			break;
15171da177e4SLinus Torvalds 		case RPC_GSS_SVC_INTEGRITY:
1518b620754bSJ. Bruce Fields 			/* placeholders for length and seq. number: */
1519b620754bSJ. Bruce Fields 			svc_putnl(resv, 0);
1520b620754bSJ. Bruce Fields 			svc_putnl(resv, 0);
15214c190e2fSJeff Layton 			if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
15221da177e4SLinus Torvalds 					gc->gc_seq, rsci->mechctx))
1523dd35210eSHarshula Jayasuriya 				goto garbage_args;
1524a5cddc88SJ. Bruce Fields 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
1525b620754bSJ. Bruce Fields 			break;
1526b620754bSJ. Bruce Fields 		case RPC_GSS_SVC_PRIVACY:
15271da177e4SLinus Torvalds 			/* placeholders for length and seq. number: */
152876994313SAlexey Dobriyan 			svc_putnl(resv, 0);
152976994313SAlexey Dobriyan 			svc_putnl(resv, 0);
15307c9fdcfbSJ. Bruce Fields 			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
15317c9fdcfbSJ. Bruce Fields 					gc->gc_seq, rsci->mechctx))
1532dd35210eSHarshula Jayasuriya 				goto garbage_args;
1533a5cddc88SJ. Bruce Fields 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
15347c9fdcfbSJ. Bruce Fields 			break;
15351da177e4SLinus Torvalds 		default:
15361da177e4SLinus Torvalds 			goto auth_err;
15371da177e4SLinus Torvalds 		}
15381da177e4SLinus Torvalds 		svcdata->rsci = rsci;
15391da177e4SLinus Torvalds 		cache_get(&rsci->h);
1540d5497fc6SJ. Bruce Fields 		rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
154183523d08SChuck Lever 					rsci->mechctx->mech_type,
154283523d08SChuck Lever 					GSS_C_QOP_DEFAULT,
154383523d08SChuck Lever 					gc->gc_svc);
15441da177e4SLinus Torvalds 		ret = SVC_OK;
15451da177e4SLinus Torvalds 		goto out;
15461da177e4SLinus Torvalds 	}
1547dd35210eSHarshula Jayasuriya garbage_args:
1548dd35210eSHarshula Jayasuriya 	ret = SVC_GARBAGE;
1549dd35210eSHarshula Jayasuriya 	goto out;
15501da177e4SLinus Torvalds auth_err:
155121fcd02bSJ. Bruce Fields 	/* Restore write pointer to its original value: */
15521da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, reject_stat);
15531da177e4SLinus Torvalds 	ret = SVC_DENIED;
15541da177e4SLinus Torvalds 	goto out;
15551da177e4SLinus Torvalds complete:
15561da177e4SLinus Torvalds 	ret = SVC_COMPLETE;
15571da177e4SLinus Torvalds 	goto out;
15581da177e4SLinus Torvalds drop:
15594d712ef1SChuck Lever 	ret = SVC_CLOSE;
15601da177e4SLinus Torvalds out:
15611da177e4SLinus Torvalds 	if (rsci)
1562a1db410dSStanislav Kinsbursky 		cache_put(&rsci->h, sn->rsc_cache);
15631da177e4SLinus Torvalds 	return ret;
15641da177e4SLinus Torvalds }
15651da177e4SLinus Torvalds 
1566cfbdbab0SAl Viro static __be32 *
15673c15a486SJ.Bruce Fields svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd)
15683c15a486SJ.Bruce Fields {
1569cfbdbab0SAl Viro 	__be32 *p;
1570cfbdbab0SAl Viro 	u32 verf_len;
15713c15a486SJ.Bruce Fields 
15725b304bc5SJ.Bruce Fields 	p = gsd->verf_start;
15735b304bc5SJ.Bruce Fields 	gsd->verf_start = NULL;
15745b304bc5SJ.Bruce Fields 
15755b304bc5SJ.Bruce Fields 	/* If the reply stat is nonzero, don't wrap: */
15765b304bc5SJ.Bruce Fields 	if (*(p-1) != rpc_success)
15775b304bc5SJ.Bruce Fields 		return NULL;
15785b304bc5SJ.Bruce Fields 	/* Skip the verifier: */
15795b304bc5SJ.Bruce Fields 	p += 1;
15805b304bc5SJ.Bruce Fields 	verf_len = ntohl(*p++);
15815b304bc5SJ.Bruce Fields 	p += XDR_QUADLEN(verf_len);
15823c15a486SJ.Bruce Fields 	/* move accept_stat to right place: */
15833c15a486SJ.Bruce Fields 	memcpy(p, p + 2, 4);
15845b304bc5SJ.Bruce Fields 	/* Also don't wrap if the accept stat is nonzero: */
15853c15a486SJ.Bruce Fields 	if (*p != rpc_success) {
15863c15a486SJ.Bruce Fields 		resbuf->head[0].iov_len -= 2 * 4;
15873c15a486SJ.Bruce Fields 		return NULL;
15883c15a486SJ.Bruce Fields 	}
15893c15a486SJ.Bruce Fields 	p++;
15903c15a486SJ.Bruce Fields 	return p;
15913c15a486SJ.Bruce Fields }
15923c15a486SJ.Bruce Fields 
1593e142ede8SJ. Bruce Fields static inline int
1594e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
15951da177e4SLinus Torvalds {
15961da177e4SLinus Torvalds 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
15971da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
15981da177e4SLinus Torvalds 	struct xdr_buf *resbuf = &rqstp->rq_res;
15991da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
16001da177e4SLinus Torvalds 	struct xdr_netobj mic;
16011da177e4SLinus Torvalds 	struct kvec *resv;
1602d8ed029dSAlexey Dobriyan 	__be32 *p;
16031da177e4SLinus Torvalds 	int integ_offset, integ_len;
16041da177e4SLinus Torvalds 	int stat = -EINVAL;
16051da177e4SLinus Torvalds 
16063c15a486SJ.Bruce Fields 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
16073c15a486SJ.Bruce Fields 	if (p == NULL)
16081da177e4SLinus Torvalds 		goto out;
16091da177e4SLinus Torvalds 	integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
16101da177e4SLinus Torvalds 	integ_len = resbuf->len - integ_offset;
16111da177e4SLinus Torvalds 	BUG_ON(integ_len % 4);
16121da177e4SLinus Torvalds 	*p++ = htonl(integ_len);
16131da177e4SLinus Torvalds 	*p++ = htonl(gc->gc_seq);
1614bba0f88bSJ. Bruce Fields 	if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len))
16151da177e4SLinus Torvalds 		BUG();
1616153e44d2SNeilBrown 	if (resbuf->tail[0].iov_base == NULL) {
1617e142ede8SJ. Bruce Fields 		if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1618dfee55f0SNeilBrown 			goto out_err;
1619e142ede8SJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
1620dfee55f0SNeilBrown 						+ resbuf->head[0].iov_len;
16211da177e4SLinus Torvalds 		resbuf->tail[0].iov_len = 0;
16221da177e4SLinus Torvalds 	}
1623bba0f88bSJ. Bruce Fields 	resv = &resbuf->tail[0];
16241da177e4SLinus Torvalds 	mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
162500fd6e14SJ. Bruce Fields 	if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))
16261da177e4SLinus Torvalds 		goto out_err;
162776994313SAlexey Dobriyan 	svc_putnl(resv, mic.len);
16281da177e4SLinus Torvalds 	memset(mic.data + mic.len, 0,
16291da177e4SLinus Torvalds 			round_up_to_quad(mic.len) - mic.len);
16301da177e4SLinus Torvalds 	resv->iov_len += XDR_QUADLEN(mic.len) << 2;
16311da177e4SLinus Torvalds 	/* not strictly required: */
16321da177e4SLinus Torvalds 	resbuf->len += XDR_QUADLEN(mic.len) << 2;
16331da177e4SLinus Torvalds 	BUG_ON(resv->iov_len > PAGE_SIZE);
1634e142ede8SJ. Bruce Fields out:
1635e142ede8SJ. Bruce Fields 	stat = 0;
1636e142ede8SJ. Bruce Fields out_err:
1637e142ede8SJ. Bruce Fields 	return stat;
1638e142ede8SJ. Bruce Fields }
1639e142ede8SJ. Bruce Fields 
16407c9fdcfbSJ. Bruce Fields static inline int
16417c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
16427c9fdcfbSJ. Bruce Fields {
16437c9fdcfbSJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
16447c9fdcfbSJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
16457c9fdcfbSJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
16467c9fdcfbSJ. Bruce Fields 	struct page **inpages = NULL;
1647753ed90dSAl Viro 	__be32 *p, *len;
1648753ed90dSAl Viro 	int offset;
16497c9fdcfbSJ. Bruce Fields 	int pad;
16507c9fdcfbSJ. Bruce Fields 
16513c15a486SJ.Bruce Fields 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
16523c15a486SJ.Bruce Fields 	if (p == NULL)
16537c9fdcfbSJ. Bruce Fields 		return 0;
16547c9fdcfbSJ. Bruce Fields 	len = p++;
16557c9fdcfbSJ. Bruce Fields 	offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
16567c9fdcfbSJ. Bruce Fields 	*p++ = htonl(gc->gc_seq);
16577c9fdcfbSJ. Bruce Fields 	inpages = resbuf->pages;
16587c9fdcfbSJ. Bruce Fields 	/* XXX: Would be better to write some xdr helper functions for
16597c9fdcfbSJ. Bruce Fields 	 * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
16607561042fSKevin Coffman 
16617561042fSKevin Coffman 	/*
16627561042fSKevin Coffman 	 * If there is currently tail data, make sure there is
16637561042fSKevin Coffman 	 * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in
16647561042fSKevin Coffman 	 * the page, and move the current tail data such that
16657561042fSKevin Coffman 	 * there is RPC_MAX_AUTH_SIZE slack space available in
16667561042fSKevin Coffman 	 * both the head and tail.
16677561042fSKevin Coffman 	 */
166844524359SNeilBrown 	if (resbuf->tail[0].iov_base) {
16697c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base
16707c9fdcfbSJ. Bruce Fields 							+ PAGE_SIZE);
16717c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base);
16727c9fdcfbSJ. Bruce Fields 		if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len
16737c9fdcfbSJ. Bruce Fields 				+ 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
16747c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
16757c9fdcfbSJ. Bruce Fields 		memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE,
16767c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_base,
16777c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_len);
16787c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
16797c9fdcfbSJ. Bruce Fields 	}
16807561042fSKevin Coffman 	/*
16817561042fSKevin Coffman 	 * If there is no current tail data, make sure there is
16827561042fSKevin Coffman 	 * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the
16837561042fSKevin Coffman 	 * allotted page, and set up tail information such that there
16847561042fSKevin Coffman 	 * is RPC_MAX_AUTH_SIZE slack space available in both the
16857561042fSKevin Coffman 	 * head and tail.
16867561042fSKevin Coffman 	 */
16877c9fdcfbSJ. Bruce Fields 	if (resbuf->tail[0].iov_base == NULL) {
16887c9fdcfbSJ. Bruce Fields 		if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
16897c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
16907c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
16917c9fdcfbSJ. Bruce Fields 			+ resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
16927c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_len = 0;
16937c9fdcfbSJ. Bruce Fields 	}
16947c9fdcfbSJ. Bruce Fields 	if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
16957c9fdcfbSJ. Bruce Fields 		return -ENOMEM;
16967c9fdcfbSJ. Bruce Fields 	*len = htonl(resbuf->len - offset);
16977c9fdcfbSJ. Bruce Fields 	pad = 3 - ((resbuf->len - offset - 1)&3);
1698d8ed029dSAlexey Dobriyan 	p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len);
16997c9fdcfbSJ. Bruce Fields 	memset(p, 0, pad);
17007c9fdcfbSJ. Bruce Fields 	resbuf->tail[0].iov_len += pad;
17017c9fdcfbSJ. Bruce Fields 	resbuf->len += pad;
17027c9fdcfbSJ. Bruce Fields 	return 0;
17037c9fdcfbSJ. Bruce Fields }
17047c9fdcfbSJ. Bruce Fields 
1705e142ede8SJ. Bruce Fields static int
1706e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp)
1707e142ede8SJ. Bruce Fields {
1708e142ede8SJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1709e142ede8SJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
1710e142ede8SJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
1711e142ede8SJ. Bruce Fields 	int stat = -EINVAL;
1712a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
1713e142ede8SJ. Bruce Fields 
1714e142ede8SJ. Bruce Fields 	if (gc->gc_proc != RPC_GSS_PROC_DATA)
1715e142ede8SJ. Bruce Fields 		goto out;
1716e142ede8SJ. Bruce Fields 	/* Release can be called twice, but we only wrap once. */
17175b304bc5SJ.Bruce Fields 	if (gsd->verf_start == NULL)
1718e142ede8SJ. Bruce Fields 		goto out;
1719e142ede8SJ. Bruce Fields 	/* normally not set till svc_send, but we need it here: */
17207c9fdcfbSJ. Bruce Fields 	/* XXX: what for?  Do we mess it up the moment we call svc_putu32
17217c9fdcfbSJ. Bruce Fields 	 * or whatever? */
17227c9fdcfbSJ. Bruce Fields 	resbuf->len = total_buf_len(resbuf);
1723e142ede8SJ. Bruce Fields 	switch (gc->gc_svc) {
1724e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_NONE:
1725e142ede8SJ. Bruce Fields 		break;
1726e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_INTEGRITY:
17277c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_integ(rqstp);
17287c9fdcfbSJ. Bruce Fields 		if (stat)
17297c9fdcfbSJ. Bruce Fields 			goto out_err;
17301da177e4SLinus Torvalds 		break;
17311da177e4SLinus Torvalds 	case RPC_GSS_SVC_PRIVACY:
17327c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_priv(rqstp);
17337c9fdcfbSJ. Bruce Fields 		if (stat)
17347c9fdcfbSJ. Bruce Fields 			goto out_err;
17357c9fdcfbSJ. Bruce Fields 		break;
1736eac81736SWei Yongjun 	/*
1737eac81736SWei Yongjun 	 * For any other gc_svc value, svcauth_gss_accept() already set
1738eac81736SWei Yongjun 	 * the auth_error appropriately; just fall through:
1739eac81736SWei Yongjun 	 */
17401da177e4SLinus Torvalds 	}
17411da177e4SLinus Torvalds 
17421da177e4SLinus Torvalds out:
17431da177e4SLinus Torvalds 	stat = 0;
17441da177e4SLinus Torvalds out_err:
17451da177e4SLinus Torvalds 	if (rqstp->rq_client)
17461da177e4SLinus Torvalds 		auth_domain_put(rqstp->rq_client);
17471da177e4SLinus Torvalds 	rqstp->rq_client = NULL;
17483ab4d8b1SJ. Bruce Fields 	if (rqstp->rq_gssclient)
17493ab4d8b1SJ. Bruce Fields 		auth_domain_put(rqstp->rq_gssclient);
17503ab4d8b1SJ. Bruce Fields 	rqstp->rq_gssclient = NULL;
17511da177e4SLinus Torvalds 	if (rqstp->rq_cred.cr_group_info)
17521da177e4SLinus Torvalds 		put_group_info(rqstp->rq_cred.cr_group_info);
17531da177e4SLinus Torvalds 	rqstp->rq_cred.cr_group_info = NULL;
17541da177e4SLinus Torvalds 	if (gsd->rsci)
1755a1db410dSStanislav Kinsbursky 		cache_put(&gsd->rsci->h, sn->rsc_cache);
17561da177e4SLinus Torvalds 	gsd->rsci = NULL;
17571da177e4SLinus Torvalds 
17581da177e4SLinus Torvalds 	return stat;
17591da177e4SLinus Torvalds }
17601da177e4SLinus Torvalds 
17611da177e4SLinus Torvalds static void
17621da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom)
17631da177e4SLinus Torvalds {
17641da177e4SLinus Torvalds 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
17651da177e4SLinus Torvalds 
17661da177e4SLinus Torvalds 	kfree(dom->name);
17671da177e4SLinus Torvalds 	kfree(gd);
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds 
17701da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = {
17711da177e4SLinus Torvalds 	.name		= "rpcsec_gss",
17721da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
17731da177e4SLinus Torvalds 	.flavour	= RPC_AUTH_GSS,
17741da177e4SLinus Torvalds 	.accept		= svcauth_gss_accept,
17751da177e4SLinus Torvalds 	.release	= svcauth_gss_release,
17761da177e4SLinus Torvalds 	.domain_release = svcauth_gss_domain_release,
17771da177e4SLinus Torvalds 	.set_client	= svcauth_gss_set_client,
17781da177e4SLinus Torvalds };
17791da177e4SLinus Torvalds 
1780a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net)
1781a1db410dSStanislav Kinsbursky {
1782a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1783a1db410dSStanislav Kinsbursky 	struct cache_detail *cd;
1784a1db410dSStanislav Kinsbursky 	int err;
1785a1db410dSStanislav Kinsbursky 
1786a1db410dSStanislav Kinsbursky 	cd = cache_create_net(&rsi_cache_template, net);
1787a1db410dSStanislav Kinsbursky 	if (IS_ERR(cd))
1788a1db410dSStanislav Kinsbursky 		return PTR_ERR(cd);
1789a1db410dSStanislav Kinsbursky 	err = cache_register_net(cd, net);
1790a1db410dSStanislav Kinsbursky 	if (err) {
1791a1db410dSStanislav Kinsbursky 		cache_destroy_net(cd, net);
1792a1db410dSStanislav Kinsbursky 		return err;
1793a1db410dSStanislav Kinsbursky 	}
1794a1db410dSStanislav Kinsbursky 	sn->rsi_cache = cd;
1795a1db410dSStanislav Kinsbursky 	return 0;
1796a1db410dSStanislav Kinsbursky }
1797a1db410dSStanislav Kinsbursky 
1798a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net)
1799a1db410dSStanislav Kinsbursky {
1800a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1801a1db410dSStanislav Kinsbursky 	struct cache_detail *cd = sn->rsi_cache;
1802a1db410dSStanislav Kinsbursky 
1803a1db410dSStanislav Kinsbursky 	sn->rsi_cache = NULL;
1804a1db410dSStanislav Kinsbursky 	cache_purge(cd);
1805a1db410dSStanislav Kinsbursky 	cache_unregister_net(cd, net);
1806a1db410dSStanislav Kinsbursky 	cache_destroy_net(cd, net);
1807a1db410dSStanislav Kinsbursky }
1808a1db410dSStanislav Kinsbursky 
1809a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net)
1810a1db410dSStanislav Kinsbursky {
1811a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1812a1db410dSStanislav Kinsbursky 	struct cache_detail *cd;
1813a1db410dSStanislav Kinsbursky 	int err;
1814a1db410dSStanislav Kinsbursky 
1815a1db410dSStanislav Kinsbursky 	cd = cache_create_net(&rsc_cache_template, net);
1816a1db410dSStanislav Kinsbursky 	if (IS_ERR(cd))
1817a1db410dSStanislav Kinsbursky 		return PTR_ERR(cd);
1818a1db410dSStanislav Kinsbursky 	err = cache_register_net(cd, net);
1819a1db410dSStanislav Kinsbursky 	if (err) {
1820a1db410dSStanislav Kinsbursky 		cache_destroy_net(cd, net);
1821a1db410dSStanislav Kinsbursky 		return err;
1822a1db410dSStanislav Kinsbursky 	}
1823a1db410dSStanislav Kinsbursky 	sn->rsc_cache = cd;
1824a1db410dSStanislav Kinsbursky 	return 0;
1825a1db410dSStanislav Kinsbursky }
1826a1db410dSStanislav Kinsbursky 
1827a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net)
1828a1db410dSStanislav Kinsbursky {
1829a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1830a1db410dSStanislav Kinsbursky 	struct cache_detail *cd = sn->rsc_cache;
1831a1db410dSStanislav Kinsbursky 
1832a1db410dSStanislav Kinsbursky 	sn->rsc_cache = NULL;
1833a1db410dSStanislav Kinsbursky 	cache_purge(cd);
1834a1db410dSStanislav Kinsbursky 	cache_unregister_net(cd, net);
1835a1db410dSStanislav Kinsbursky 	cache_destroy_net(cd, net);
1836a1db410dSStanislav Kinsbursky }
1837a1db410dSStanislav Kinsbursky 
1838a1db410dSStanislav Kinsbursky int
1839a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net)
1840a1db410dSStanislav Kinsbursky {
1841a1db410dSStanislav Kinsbursky 	int rv;
1842a1db410dSStanislav Kinsbursky 
1843a1db410dSStanislav Kinsbursky 	rv = rsc_cache_create_net(net);
1844a1db410dSStanislav Kinsbursky 	if (rv)
1845a1db410dSStanislav Kinsbursky 		return rv;
1846a1db410dSStanislav Kinsbursky 	rv = rsi_cache_create_net(net);
1847a1db410dSStanislav Kinsbursky 	if (rv)
1848a1db410dSStanislav Kinsbursky 		goto out1;
1849030d794bSSimo Sorce 	rv = create_use_gss_proxy_proc_entry(net);
1850030d794bSSimo Sorce 	if (rv)
1851030d794bSSimo Sorce 		goto out2;
1852a1db410dSStanislav Kinsbursky 	return 0;
1853030d794bSSimo Sorce out2:
1854030d794bSSimo Sorce 	destroy_use_gss_proxy_proc_entry(net);
1855a1db410dSStanislav Kinsbursky out1:
1856a1db410dSStanislav Kinsbursky 	rsc_cache_destroy_net(net);
1857a1db410dSStanislav Kinsbursky 	return rv;
1858a1db410dSStanislav Kinsbursky }
1859a1db410dSStanislav Kinsbursky 
1860a1db410dSStanislav Kinsbursky void
1861a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net)
1862a1db410dSStanislav Kinsbursky {
1863030d794bSSimo Sorce 	destroy_use_gss_proxy_proc_entry(net);
1864a1db410dSStanislav Kinsbursky 	rsi_cache_destroy_net(net);
1865a1db410dSStanislav Kinsbursky 	rsc_cache_destroy_net(net);
1866a1db410dSStanislav Kinsbursky }
1867a1db410dSStanislav Kinsbursky 
18681da177e4SLinus Torvalds int
18691da177e4SLinus Torvalds gss_svc_init(void)
18701da177e4SLinus Torvalds {
1871a1db410dSStanislav Kinsbursky 	return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
18721da177e4SLinus Torvalds }
18731da177e4SLinus Torvalds 
18741da177e4SLinus Torvalds void
18751da177e4SLinus Torvalds gss_svc_shutdown(void)
18761da177e4SLinus Torvalds {
18771da177e4SLinus Torvalds 	svc_auth_unregister(RPC_AUTH_GSS);
18781da177e4SLinus Torvalds }
1879