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 
267ee24eac3SBhumika Goyal static const 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 		}
484bdcf0a42SThiago Rafael Becker 		groups_sort(rsci.cred.cr_group_info);
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 		/* mech name */
4871da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4881da177e4SLinus Torvalds 		if (len < 0)
4891da177e4SLinus Torvalds 			goto out;
4900dc1531aSJ. Bruce Fields 		gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
4911da177e4SLinus Torvalds 		status = -EOPNOTSUPP;
4921da177e4SLinus Torvalds 		if (!gm)
4931da177e4SLinus Torvalds 			goto out;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 		status = -EINVAL;
4961da177e4SLinus Torvalds 		/* mech-specific data: */
4971da177e4SLinus Torvalds 		len = qword_get(&mesg, buf, mlen);
4981df0cadaSJ. Bruce Fields 		if (len < 0)
4991da177e4SLinus Torvalds 			goto out;
500400f26b5SSimo Sorce 		status = gss_import_sec_context(buf, len, gm, &rsci.mechctx,
501400f26b5SSimo Sorce 						NULL, GFP_KERNEL);
5021df0cadaSJ. Bruce Fields 		if (status)
5031da177e4SLinus Torvalds 			goto out;
50468e76ad0SOlga Kornievskaia 
50568e76ad0SOlga Kornievskaia 		/* get client name */
50668e76ad0SOlga Kornievskaia 		len = qword_get(&mesg, buf, mlen);
50768e76ad0SOlga Kornievskaia 		if (len > 0) {
50803a4e1f6SJ. Bruce Fields 			rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL);
5091eb6d622SWei Yongjun 			if (!rsci.cred.cr_principal) {
5101eb6d622SWei Yongjun 				status = -ENOMEM;
51168e76ad0SOlga Kornievskaia 				goto out;
51268e76ad0SOlga Kornievskaia 			}
5131eb6d622SWei Yongjun 		}
51468e76ad0SOlga Kornievskaia 
5151da177e4SLinus Torvalds 	}
5161da177e4SLinus Torvalds 	rsci.h.expiry_time = expiry;
517a1db410dSStanislav Kinsbursky 	rscp = rsc_update(cd, &rsci, rscp);
5181da177e4SLinus Torvalds 	status = 0;
5191da177e4SLinus Torvalds out:
5201da177e4SLinus Torvalds 	rsc_free(&rsci);
5211da177e4SLinus Torvalds 	if (rscp)
522a1db410dSStanislav Kinsbursky 		cache_put(&rscp->h, cd);
52317f834b6SNeilBrown 	else
52417f834b6SNeilBrown 		status = -ENOMEM;
5251da177e4SLinus Torvalds 	return status;
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds 
528ee24eac3SBhumika Goyal static const struct cache_detail rsc_cache_template = {
529f35279d3SBruce Allan 	.owner		= THIS_MODULE,
5301da177e4SLinus Torvalds 	.hash_size	= RSC_HASHMAX,
5311da177e4SLinus Torvalds 	.name		= "auth.rpcsec.context",
5321da177e4SLinus Torvalds 	.cache_put	= rsc_put,
5331da177e4SLinus Torvalds 	.cache_parse	= rsc_parse,
53417f834b6SNeilBrown 	.match		= rsc_match,
53517f834b6SNeilBrown 	.init		= rsc_init,
53617f834b6SNeilBrown 	.update		= update_rsc,
53717f834b6SNeilBrown 	.alloc		= rsc_alloc,
5381da177e4SLinus Torvalds };
5391da177e4SLinus Torvalds 
540a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item)
54117f834b6SNeilBrown {
54217f834b6SNeilBrown 	struct cache_head *ch;
54317f834b6SNeilBrown 	int hash = rsc_hash(item);
54417f834b6SNeilBrown 
545a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_lookup(cd, &item->h, hash);
54617f834b6SNeilBrown 	if (ch)
54717f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
54817f834b6SNeilBrown 	else
54917f834b6SNeilBrown 		return NULL;
55017f834b6SNeilBrown }
55117f834b6SNeilBrown 
552a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old)
55317f834b6SNeilBrown {
55417f834b6SNeilBrown 	struct cache_head *ch;
55517f834b6SNeilBrown 	int hash = rsc_hash(new);
55617f834b6SNeilBrown 
557a1db410dSStanislav Kinsbursky 	ch = sunrpc_cache_update(cd, &new->h,
55817f834b6SNeilBrown 				 &old->h, hash);
55917f834b6SNeilBrown 	if (ch)
56017f834b6SNeilBrown 		return container_of(ch, struct rsc, h);
56117f834b6SNeilBrown 	else
56217f834b6SNeilBrown 		return NULL;
56317f834b6SNeilBrown }
56417f834b6SNeilBrown 
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds static struct rsc *
567a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle)
5681da177e4SLinus Torvalds {
5691da177e4SLinus Torvalds 	struct rsc rsci;
5701da177e4SLinus Torvalds 	struct rsc *found;
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 	memset(&rsci, 0, sizeof(rsci));
573bf2c4b6fSChuck Lever 	if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
574bf2c4b6fSChuck Lever 		return NULL;
575a1db410dSStanislav Kinsbursky 	found = rsc_lookup(cd, &rsci);
576bf2c4b6fSChuck Lever 	rsc_free(&rsci);
5771da177e4SLinus Torvalds 	if (!found)
5781da177e4SLinus Torvalds 		return NULL;
579a1db410dSStanislav Kinsbursky 	if (cache_check(cd, &found->h, NULL))
5801da177e4SLinus Torvalds 		return NULL;
5811da177e4SLinus Torvalds 	return found;
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */
5851da177e4SLinus Torvalds static int
5861da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num)
5871da177e4SLinus Torvalds {
5881da177e4SLinus Torvalds 	struct gss_svc_seq_data *sd = &rsci->seqdata;
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	spin_lock(&sd->sd_lock);
5911da177e4SLinus Torvalds 	if (seq_num > sd->sd_max) {
5921da177e4SLinus Torvalds 		if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
5931da177e4SLinus Torvalds 			memset(sd->sd_win,0,sizeof(sd->sd_win));
5941da177e4SLinus Torvalds 			sd->sd_max = seq_num;
5951da177e4SLinus Torvalds 		} else while (sd->sd_max < seq_num) {
5961da177e4SLinus Torvalds 			sd->sd_max++;
5971da177e4SLinus Torvalds 			__clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win);
5981da177e4SLinus Torvalds 		}
5991da177e4SLinus Torvalds 		__set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
6001da177e4SLinus Torvalds 		goto ok;
6011da177e4SLinus Torvalds 	} else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) {
6021da177e4SLinus Torvalds 		goto drop;
6031da177e4SLinus Torvalds 	}
6041da177e4SLinus Torvalds 	/* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */
6051da177e4SLinus Torvalds 	if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
6061da177e4SLinus Torvalds 		goto drop;
6071da177e4SLinus Torvalds ok:
6081da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
6091da177e4SLinus Torvalds 	return 1;
6101da177e4SLinus Torvalds drop:
6111da177e4SLinus Torvalds 	spin_unlock(&sd->sd_lock);
6121da177e4SLinus Torvalds 	return 0;
6131da177e4SLinus Torvalds }
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i)
6161da177e4SLinus Torvalds {
6171da177e4SLinus Torvalds 	return (i + 3 ) & ~3;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds static inline int
6211da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	int l;
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6261da177e4SLinus Torvalds 		return -1;
62776994313SAlexey Dobriyan 	o->len = svc_getnl(argv);
6281da177e4SLinus Torvalds 	l = round_up_to_quad(o->len);
6291da177e4SLinus Torvalds 	if (argv->iov_len < l)
6301da177e4SLinus Torvalds 		return -1;
6311da177e4SLinus Torvalds 	o->data = argv->iov_base;
6321da177e4SLinus Torvalds 	argv->iov_base += l;
6331da177e4SLinus Torvalds 	argv->iov_len -= l;
6341da177e4SLinus Torvalds 	return 0;
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds static inline int
6381da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
6391da177e4SLinus Torvalds {
640753ed90dSAl Viro 	u8 *p;
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 	if (resv->iov_len + 4 > PAGE_SIZE)
6431da177e4SLinus Torvalds 		return -1;
64476994313SAlexey Dobriyan 	svc_putnl(resv, o->len);
6451da177e4SLinus Torvalds 	p = resv->iov_base + resv->iov_len;
6461da177e4SLinus Torvalds 	resv->iov_len += round_up_to_quad(o->len);
6471da177e4SLinus Torvalds 	if (resv->iov_len > PAGE_SIZE)
6481da177e4SLinus Torvalds 		return -1;
6491da177e4SLinus Torvalds 	memcpy(p, o->data, o->len);
650753ed90dSAl Viro 	memset(p + o->len, 0, round_up_to_quad(o->len) - o->len);
6511da177e4SLinus Torvalds 	return 0;
6521da177e4SLinus Torvalds }
6531da177e4SLinus Torvalds 
65421fcd02bSJ. Bruce Fields /*
65521fcd02bSJ. Bruce Fields  * Verify the checksum on the header and return SVC_OK on success.
6561da177e4SLinus Torvalds  * Otherwise, return SVC_DROP (in the case of a bad sequence number)
6571da177e4SLinus Torvalds  * or return SVC_DENIED and indicate error in authp.
6581da177e4SLinus Torvalds  */
6591da177e4SLinus Torvalds static int
6601da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
661d8ed029dSAlexey Dobriyan 		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp)
6621da177e4SLinus Torvalds {
6631da177e4SLinus Torvalds 	struct gss_ctx		*ctx_id = rsci->mechctx;
6641da177e4SLinus Torvalds 	struct xdr_buf		rpchdr;
6651da177e4SLinus Torvalds 	struct xdr_netobj	checksum;
6661da177e4SLinus Torvalds 	u32			flavor = 0;
6671da177e4SLinus Torvalds 	struct kvec		*argv = &rqstp->rq_arg.head[0];
6681da177e4SLinus Torvalds 	struct kvec		iov;
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 	/* data to compute the checksum over: */
6711da177e4SLinus Torvalds 	iov.iov_base = rpcstart;
6721da177e4SLinus Torvalds 	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
6731da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &rpchdr);
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
6761da177e4SLinus Torvalds 	if (argv->iov_len < 4)
6771da177e4SLinus Torvalds 		return SVC_DENIED;
67876994313SAlexey Dobriyan 	flavor = svc_getnl(argv);
6791da177e4SLinus Torvalds 	if (flavor != RPC_AUTH_GSS)
6801da177e4SLinus Torvalds 		return SVC_DENIED;
6811da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &checksum))
6821da177e4SLinus Torvalds 		return SVC_DENIED;
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	if (rqstp->rq_deferred) /* skip verification of revisited request */
6851da177e4SLinus Torvalds 		return SVC_OK;
68600fd6e14SJ. Bruce Fields 	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
6871da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
6881da177e4SLinus Torvalds 		return SVC_DENIED;
6891da177e4SLinus Torvalds 	}
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	if (gc->gc_seq > MAXSEQ) {
6928885cb36SChuck Lever 		dprintk("RPC:       svcauth_gss: discarding request with "
6938885cb36SChuck Lever 				"large sequence number %d\n", gc->gc_seq);
6941da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
6951da177e4SLinus Torvalds 		return SVC_DENIED;
6961da177e4SLinus Torvalds 	}
6971da177e4SLinus Torvalds 	if (!gss_check_seq_num(rsci, gc->gc_seq)) {
6988885cb36SChuck Lever 		dprintk("RPC:       svcauth_gss: discarding request with "
6998885cb36SChuck Lever 				"old sequence number %d\n", gc->gc_seq);
7001da177e4SLinus Torvalds 		return SVC_DROP;
7011da177e4SLinus Torvalds 	}
7021da177e4SLinus Torvalds 	return SVC_OK;
7031da177e4SLinus Torvalds }
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds static int
706822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp)
707822f1005SAndy Adamson {
708d8ed029dSAlexey Dobriyan 	__be32     *p;
709822f1005SAndy Adamson 
71076994313SAlexey Dobriyan 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL);
711822f1005SAndy Adamson 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
712822f1005SAndy Adamson 	/* don't really need to check if head->iov_len > PAGE_SIZE ... */
713822f1005SAndy Adamson 	*p++ = 0;
714822f1005SAndy Adamson 	if (!xdr_ressize_check(rqstp, p))
715822f1005SAndy Adamson 		return -1;
716822f1005SAndy Adamson 	return 0;
717822f1005SAndy Adamson }
718822f1005SAndy Adamson 
719822f1005SAndy Adamson static int
7201da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
7211da177e4SLinus Torvalds {
7222876a344SJ. Bruce Fields 	__be32			*xdr_seq;
7231da177e4SLinus Torvalds 	u32			maj_stat;
7241da177e4SLinus Torvalds 	struct xdr_buf		verf_data;
7251da177e4SLinus Torvalds 	struct xdr_netobj	mic;
726d8ed029dSAlexey Dobriyan 	__be32			*p;
7271da177e4SLinus Torvalds 	struct kvec		iov;
7282876a344SJ. Bruce Fields 	int err = -1;
7291da177e4SLinus Torvalds 
73076994313SAlexey Dobriyan 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
7312876a344SJ. Bruce Fields 	xdr_seq = kmalloc(4, GFP_KERNEL);
7322876a344SJ. Bruce Fields 	if (!xdr_seq)
7332876a344SJ. Bruce Fields 		return -1;
7342876a344SJ. Bruce Fields 	*xdr_seq = htonl(seq);
7351da177e4SLinus Torvalds 
7362876a344SJ. Bruce Fields 	iov.iov_base = xdr_seq;
7372876a344SJ. Bruce Fields 	iov.iov_len = 4;
7381da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &verf_data);
7391da177e4SLinus Torvalds 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
7401da177e4SLinus Torvalds 	mic.data = (u8 *)(p + 1);
74100fd6e14SJ. Bruce Fields 	maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
7421da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
7432876a344SJ. Bruce Fields 		goto out;
7441da177e4SLinus Torvalds 	*p++ = htonl(mic.len);
7451da177e4SLinus Torvalds 	memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
7461da177e4SLinus Torvalds 	p += XDR_QUADLEN(mic.len);
7471da177e4SLinus Torvalds 	if (!xdr_ressize_check(rqstp, p))
7482876a344SJ. Bruce Fields 		goto out;
7492876a344SJ. Bruce Fields 	err = 0;
7502876a344SJ. Bruce Fields out:
7512876a344SJ. Bruce Fields 	kfree(xdr_seq);
7522876a344SJ. Bruce Fields 	return err;
7531da177e4SLinus Torvalds }
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds struct gss_domain {
7561da177e4SLinus Torvalds 	struct auth_domain	h;
7571da177e4SLinus Torvalds 	u32			pseudoflavor;
7581da177e4SLinus Torvalds };
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds static struct auth_domain *
7611da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
7621da177e4SLinus Torvalds {
7631da177e4SLinus Torvalds 	char *name;
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	name = gss_service_to_auth_domain_name(ctx->mech_type, svc);
7661da177e4SLinus Torvalds 	if (!name)
7671da177e4SLinus Torvalds 		return NULL;
7681da177e4SLinus Torvalds 	return auth_domain_find(name);
7691da177e4SLinus Torvalds }
7701da177e4SLinus Torvalds 
771efc36aa5SNeilBrown static struct auth_ops svcauthops_gss;
772efc36aa5SNeilBrown 
7734796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom)
7744796f457SJ. Bruce Fields {
7754796f457SJ. Bruce Fields 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
7764796f457SJ. Bruce Fields 
7774796f457SJ. Bruce Fields 	return gd->pseudoflavor;
7784796f457SJ. Bruce Fields }
7794796f457SJ. Bruce Fields 
7807bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
7814796f457SJ. Bruce Fields 
7821da177e4SLinus Torvalds int
7831da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
7841da177e4SLinus Torvalds {
7851da177e4SLinus Torvalds 	struct gss_domain	*new;
7861da177e4SLinus Torvalds 	struct auth_domain	*test;
7871da177e4SLinus Torvalds 	int			stat = -ENOMEM;
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds 	new = kmalloc(sizeof(*new), GFP_KERNEL);
7901da177e4SLinus Torvalds 	if (!new)
7911da177e4SLinus Torvalds 		goto out;
792efc36aa5SNeilBrown 	kref_init(&new->h.ref);
793e69062b4SArnaldo Carvalho de Melo 	new->h.name = kstrdup(name, GFP_KERNEL);
7941da177e4SLinus Torvalds 	if (!new->h.name)
7951da177e4SLinus Torvalds 		goto out_free_dom;
796efc36aa5SNeilBrown 	new->h.flavour = &svcauthops_gss;
7971da177e4SLinus Torvalds 	new->pseudoflavor = pseudoflavor;
7981da177e4SLinus Torvalds 
799cb276805SJ. Bruce Fields 	stat = 0;
800efc36aa5SNeilBrown 	test = auth_domain_lookup(name, &new->h);
801cb276805SJ. Bruce Fields 	if (test != &new->h) { /* Duplicate registration */
802cb276805SJ. Bruce Fields 		auth_domain_put(test);
803cb276805SJ. Bruce Fields 		kfree(new->h.name);
804cb276805SJ. Bruce Fields 		goto out_free_dom;
8051da177e4SLinus Torvalds 	}
8061da177e4SLinus Torvalds 	return 0;
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds out_free_dom:
8091da177e4SLinus Torvalds 	kfree(new);
8101da177e4SLinus Torvalds out:
8111da177e4SLinus Torvalds 	return stat;
8121da177e4SLinus Torvalds }
8131da177e4SLinus Torvalds 
8147bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds static inline int
8171da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
8181da177e4SLinus Torvalds {
819d8ed029dSAlexey Dobriyan 	__be32  raw;
8201da177e4SLinus Torvalds 	int     status;
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
8231da177e4SLinus Torvalds 	if (status)
8241da177e4SLinus Torvalds 		return status;
8251da177e4SLinus Torvalds 	*obj = ntohl(raw);
8261da177e4SLinus Torvalds 	return 0;
8271da177e4SLinus Torvalds }
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client.
8301da177e4SLinus Torvalds  * Obstacles:
8311da177e4SLinus Torvalds  *	The client shouldn't malloc(), would have to pass in own memory.
8321da177e4SLinus Torvalds  *	The server uses base of head iovec as read pointer, while the
8331da177e4SLinus Torvalds  *	client uses separate pointer. */
8341da177e4SLinus Torvalds static int
8354c190e2fSJeff Layton unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
8361da177e4SLinus Torvalds {
8371da177e4SLinus Torvalds 	int stat = -EINVAL;
8381da177e4SLinus Torvalds 	u32 integ_len, maj_stat;
8391da177e4SLinus Torvalds 	struct xdr_netobj mic;
8401da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
8411da177e4SLinus Torvalds 
84206eb8a56SChuck Lever 	/* NFS READ normally uses splice to send data in-place. However
84306eb8a56SChuck Lever 	 * the data in cache can change after the reply's MIC is computed
84406eb8a56SChuck Lever 	 * but before the RPC reply is sent. To prevent the client from
84506eb8a56SChuck Lever 	 * rejecting the server-computed MIC in this somewhat rare case,
84606eb8a56SChuck Lever 	 * do not use splice with the GSS integrity service.
84706eb8a56SChuck Lever 	 */
84806eb8a56SChuck Lever 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
84906eb8a56SChuck Lever 
8504c190e2fSJeff Layton 	/* Did we already verify the signature on the original pass through? */
8514c190e2fSJeff Layton 	if (rqstp->rq_deferred)
8524c190e2fSJeff Layton 		return 0;
8534c190e2fSJeff Layton 
85476994313SAlexey Dobriyan 	integ_len = svc_getnl(&buf->head[0]);
8551da177e4SLinus Torvalds 	if (integ_len & 3)
856b797b5beSJ.Bruce Fields 		return stat;
8571da177e4SLinus Torvalds 	if (integ_len > buf->len)
858b797b5beSJ.Bruce Fields 		return stat;
8591754eb2bSJ. Bruce Fields 	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) {
8601754eb2bSJ. Bruce Fields 		WARN_ON_ONCE(1);
8611754eb2bSJ. Bruce Fields 		return stat;
8621754eb2bSJ. Bruce Fields 	}
8631da177e4SLinus Torvalds 	/* copy out mic... */
8641da177e4SLinus Torvalds 	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
8651754eb2bSJ. Bruce Fields 		return stat;
8661da177e4SLinus Torvalds 	if (mic.len > RPC_MAX_AUTH_SIZE)
867b797b5beSJ.Bruce Fields 		return stat;
8681da177e4SLinus Torvalds 	mic.data = kmalloc(mic.len, GFP_KERNEL);
8691da177e4SLinus Torvalds 	if (!mic.data)
870b797b5beSJ.Bruce Fields 		return stat;
8711da177e4SLinus Torvalds 	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
8721da177e4SLinus Torvalds 		goto out;
87300fd6e14SJ. Bruce Fields 	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
8741da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
8751da177e4SLinus Torvalds 		goto out;
87676994313SAlexey Dobriyan 	if (svc_getnl(&buf->head[0]) != seq)
8771da177e4SLinus Torvalds 		goto out;
878c0cb8bf3STomáš Trnka 	/* trim off the mic and padding at the end before returning */
879c0cb8bf3STomáš Trnka 	xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
8801da177e4SLinus Torvalds 	stat = 0;
8811da177e4SLinus Torvalds out:
882b797b5beSJ.Bruce Fields 	kfree(mic.data);
8831da177e4SLinus Torvalds 	return stat;
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds 
8867c9fdcfbSJ. Bruce Fields static inline int
8877c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf)
8887c9fdcfbSJ. Bruce Fields {
8897c9fdcfbSJ. Bruce Fields 	return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
8907c9fdcfbSJ. Bruce Fields }
8917c9fdcfbSJ. Bruce Fields 
8927c9fdcfbSJ. Bruce Fields static void
8937c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad)
8947c9fdcfbSJ. Bruce Fields {
8957c9fdcfbSJ. Bruce Fields 	if (buf->page_len == 0) {
8967c9fdcfbSJ. Bruce Fields 		/* We need to adjust head and buf->len in tandem in this
8977c9fdcfbSJ. Bruce Fields 		 * case to make svc_defer() work--it finds the original
8987c9fdcfbSJ. Bruce Fields 		 * buffer start using buf->len - buf->head[0].iov_len. */
8997c9fdcfbSJ. Bruce Fields 		buf->head[0].iov_len -= pad;
9007c9fdcfbSJ. Bruce Fields 	}
9017c9fdcfbSJ. Bruce Fields }
9027c9fdcfbSJ. Bruce Fields 
9037c9fdcfbSJ. Bruce Fields static int
9047c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
9057c9fdcfbSJ. Bruce Fields {
9067c9fdcfbSJ. Bruce Fields 	u32 priv_len, maj_stat;
9077c9fdcfbSJ. Bruce Fields 	int pad, saved_len, remaining_len, offset;
9087c9fdcfbSJ. Bruce Fields 
909779fb0f3SJeff Layton 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
9107c9fdcfbSJ. Bruce Fields 
91176994313SAlexey Dobriyan 	priv_len = svc_getnl(&buf->head[0]);
9127c9fdcfbSJ. Bruce Fields 	if (rqstp->rq_deferred) {
9137c9fdcfbSJ. Bruce Fields 		/* Already decrypted last time through! The sequence number
9147c9fdcfbSJ. Bruce Fields 		 * check at out_seq is unnecessary but harmless: */
9157c9fdcfbSJ. Bruce Fields 		goto out_seq;
9167c9fdcfbSJ. Bruce Fields 	}
9177c9fdcfbSJ. Bruce Fields 	/* buf->len is the number of bytes from the original start of the
9187c9fdcfbSJ. Bruce Fields 	 * request to the end, where head[0].iov_len is just the bytes
9197c9fdcfbSJ. Bruce Fields 	 * not yet read from the head, so these two values are different: */
9207c9fdcfbSJ. Bruce Fields 	remaining_len = total_buf_len(buf);
9217c9fdcfbSJ. Bruce Fields 	if (priv_len > remaining_len)
9227c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9237c9fdcfbSJ. Bruce Fields 	pad = remaining_len - priv_len;
9247c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
9257c9fdcfbSJ. Bruce Fields 	fix_priv_head(buf, pad);
9267c9fdcfbSJ. Bruce Fields 
9277c9fdcfbSJ. Bruce Fields 	/* Maybe it would be better to give gss_unwrap a length parameter: */
9287c9fdcfbSJ. Bruce Fields 	saved_len = buf->len;
9297c9fdcfbSJ. Bruce Fields 	buf->len = priv_len;
9307c9fdcfbSJ. Bruce Fields 	maj_stat = gss_unwrap(ctx, 0, buf);
9317c9fdcfbSJ. Bruce Fields 	pad = priv_len - buf->len;
9327c9fdcfbSJ. Bruce Fields 	buf->len = saved_len;
9337c9fdcfbSJ. Bruce Fields 	buf->len -= pad;
9347c9fdcfbSJ. Bruce Fields 	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
9357c9fdcfbSJ. Bruce Fields 	 * In the krb5p case, at least, the data ends up offset, so we need to
9367c9fdcfbSJ. Bruce Fields 	 * move it around. */
9377c9fdcfbSJ. Bruce Fields 	/* XXX: This is very inefficient.  It would be better to either do
9387c9fdcfbSJ. Bruce Fields 	 * this while we encrypt, or maybe in the receive code, if we can peak
9397c9fdcfbSJ. Bruce Fields 	 * ahead and work out the service and mechanism there. */
9407c9fdcfbSJ. Bruce Fields 	offset = buf->head[0].iov_len % 4;
9417c9fdcfbSJ. Bruce Fields 	if (offset) {
9427c9fdcfbSJ. Bruce Fields 		buf->buflen = RPCSVC_MAXPAYLOAD;
9437c9fdcfbSJ. Bruce Fields 		xdr_shift_buf(buf, offset);
9447c9fdcfbSJ. Bruce Fields 		fix_priv_head(buf, pad);
9457c9fdcfbSJ. Bruce Fields 	}
9467c9fdcfbSJ. Bruce Fields 	if (maj_stat != GSS_S_COMPLETE)
9477c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9487c9fdcfbSJ. Bruce Fields out_seq:
94976994313SAlexey Dobriyan 	if (svc_getnl(&buf->head[0]) != seq)
9507c9fdcfbSJ. Bruce Fields 		return -EINVAL;
9517c9fdcfbSJ. Bruce Fields 	return 0;
9527c9fdcfbSJ. Bruce Fields }
9537c9fdcfbSJ. Bruce Fields 
9541da177e4SLinus Torvalds struct gss_svc_data {
9551da177e4SLinus Torvalds 	/* decoded gss client cred: */
9561da177e4SLinus Torvalds 	struct rpc_gss_wire_cred	clcred;
9575b304bc5SJ.Bruce Fields 	/* save a pointer to the beginning of the encoded verifier,
9585b304bc5SJ.Bruce Fields 	 * for use in encryption/checksumming in svcauth_gss_release: */
9595b304bc5SJ.Bruce Fields 	__be32				*verf_start;
9601da177e4SLinus Torvalds 	struct rsc			*rsci;
9611da177e4SLinus Torvalds };
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds static int
9641da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp)
9651da177e4SLinus Torvalds {
9661da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
9671da177e4SLinus Torvalds 	struct rsc *rsci = svcdata->rsci;
9681da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &svcdata->clcred;
9693ab4d8b1SJ. Bruce Fields 	int stat;
9701da177e4SLinus Torvalds 
9713ab4d8b1SJ. Bruce Fields 	/*
9723ab4d8b1SJ. Bruce Fields 	 * A gss export can be specified either by:
9733ab4d8b1SJ. Bruce Fields 	 * 	export	*(sec=krb5,rw)
9743ab4d8b1SJ. Bruce Fields 	 * or by
9753ab4d8b1SJ. Bruce Fields 	 * 	export gss/krb5(rw)
9763ab4d8b1SJ. Bruce Fields 	 * The latter is deprecated; but for backwards compatibility reasons
9773ab4d8b1SJ. Bruce Fields 	 * the nfsd code will still fall back on trying it if the former
9783ab4d8b1SJ. Bruce Fields 	 * doesn't work; so we try to make both available to nfsd, below.
9793ab4d8b1SJ. Bruce Fields 	 */
9803ab4d8b1SJ. Bruce Fields 	rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc);
9813ab4d8b1SJ. Bruce Fields 	if (rqstp->rq_gssclient == NULL)
9821da177e4SLinus Torvalds 		return SVC_DENIED;
9833ab4d8b1SJ. Bruce Fields 	stat = svcauth_unix_set_client(rqstp);
9841ebede86SNeilBrown 	if (stat == SVC_DROP || stat == SVC_CLOSE)
9853ab4d8b1SJ. Bruce Fields 		return stat;
9861da177e4SLinus Torvalds 	return SVC_OK;
9871da177e4SLinus Torvalds }
9881da177e4SLinus Torvalds 
98991a4762eSKevin Coffman static inline int
990fc2952a2SSimo Sorce gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
991fc2952a2SSimo Sorce 		struct xdr_netobj *out_handle, int *major_status)
99291a4762eSKevin Coffman {
99391a4762eSKevin Coffman 	struct rsc *rsci;
99454f9247bSFrank Filz 	int        rc;
99591a4762eSKevin Coffman 
996fc2952a2SSimo Sorce 	if (*major_status != GSS_S_COMPLETE)
99791a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
998fc2952a2SSimo Sorce 	rsci = gss_svc_searchbyctx(cd, out_handle);
99991a4762eSKevin Coffman 	if (rsci == NULL) {
1000fc2952a2SSimo Sorce 		*major_status = GSS_S_NO_CONTEXT;
100191a4762eSKevin Coffman 		return gss_write_null_verf(rqstp);
100291a4762eSKevin Coffman 	}
100354f9247bSFrank Filz 	rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
1004a1db410dSStanislav Kinsbursky 	cache_put(&rsci->h, cd);
100554f9247bSFrank Filz 	return rc;
100691a4762eSKevin Coffman }
100791a4762eSKevin Coffman 
1008fc2952a2SSimo Sorce static inline int
1009030d794bSSimo Sorce gss_read_common_verf(struct rpc_gss_wire_cred *gc,
1010fc2952a2SSimo Sorce 		     struct kvec *argv, __be32 *authp,
1011030d794bSSimo Sorce 		     struct xdr_netobj *in_handle)
1012fc2952a2SSimo Sorce {
1013fc2952a2SSimo Sorce 	/* Read the verifier; should be NULL: */
1014fc2952a2SSimo Sorce 	*authp = rpc_autherr_badverf;
1015fc2952a2SSimo Sorce 	if (argv->iov_len < 2 * 4)
1016fc2952a2SSimo Sorce 		return SVC_DENIED;
1017fc2952a2SSimo Sorce 	if (svc_getnl(argv) != RPC_AUTH_NULL)
1018fc2952a2SSimo Sorce 		return SVC_DENIED;
1019fc2952a2SSimo Sorce 	if (svc_getnl(argv) != 0)
1020fc2952a2SSimo Sorce 		return SVC_DENIED;
1021fc2952a2SSimo Sorce 	/* Martial context handle and token for upcall: */
1022fc2952a2SSimo Sorce 	*authp = rpc_autherr_badcred;
1023fc2952a2SSimo Sorce 	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
1024fc2952a2SSimo Sorce 		return SVC_DENIED;
1025fc2952a2SSimo Sorce 	if (dup_netobj(in_handle, &gc->gc_ctx))
1026fc2952a2SSimo Sorce 		return SVC_CLOSE;
1027fc2952a2SSimo Sorce 	*authp = rpc_autherr_badverf;
1028030d794bSSimo Sorce 
1029030d794bSSimo Sorce 	return 0;
1030030d794bSSimo Sorce }
1031030d794bSSimo Sorce 
1032030d794bSSimo Sorce static inline int
1033030d794bSSimo Sorce gss_read_verf(struct rpc_gss_wire_cred *gc,
1034030d794bSSimo Sorce 	      struct kvec *argv, __be32 *authp,
1035030d794bSSimo Sorce 	      struct xdr_netobj *in_handle,
1036030d794bSSimo Sorce 	      struct xdr_netobj *in_token)
1037030d794bSSimo Sorce {
1038030d794bSSimo Sorce 	struct xdr_netobj tmpobj;
1039030d794bSSimo Sorce 	int res;
1040030d794bSSimo Sorce 
1041030d794bSSimo Sorce 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1042030d794bSSimo Sorce 	if (res)
1043030d794bSSimo Sorce 		return res;
1044030d794bSSimo Sorce 
1045fc2952a2SSimo Sorce 	if (svc_safe_getnetobj(argv, &tmpobj)) {
1046fc2952a2SSimo Sorce 		kfree(in_handle->data);
1047fc2952a2SSimo Sorce 		return SVC_DENIED;
1048fc2952a2SSimo Sorce 	}
1049fc2952a2SSimo Sorce 	if (dup_netobj(in_token, &tmpobj)) {
1050fc2952a2SSimo Sorce 		kfree(in_handle->data);
1051fc2952a2SSimo Sorce 		return SVC_CLOSE;
1052fc2952a2SSimo Sorce 	}
1053fc2952a2SSimo Sorce 
1054fc2952a2SSimo Sorce 	return 0;
1055fc2952a2SSimo Sorce }
1056fc2952a2SSimo Sorce 
1057030d794bSSimo Sorce /* Ok this is really heavily depending on a set of semantics in
1058030d794bSSimo Sorce  * how rqstp is set up by svc_recv and pages laid down by the
1059030d794bSSimo Sorce  * server when reading a request. We are basically guaranteed that
1060030d794bSSimo Sorce  * the token lays all down linearly across a set of pages, starting
1061030d794bSSimo Sorce  * at iov_base in rq_arg.head[0] which happens to be the first of a
1062030d794bSSimo Sorce  * set of pages stored in rq_pages[].
1063030d794bSSimo Sorce  * rq_arg.head[0].iov_base will provide us the page_base to pass
1064030d794bSSimo Sorce  * to the upcall.
1065030d794bSSimo Sorce  */
1066030d794bSSimo Sorce static inline int
1067030d794bSSimo Sorce gss_read_proxy_verf(struct svc_rqst *rqstp,
1068030d794bSSimo Sorce 		    struct rpc_gss_wire_cred *gc, __be32 *authp,
1069030d794bSSimo Sorce 		    struct xdr_netobj *in_handle,
1070030d794bSSimo Sorce 		    struct gssp_in_token *in_token)
1071030d794bSSimo Sorce {
1072030d794bSSimo Sorce 	struct kvec *argv = &rqstp->rq_arg.head[0];
1073030d794bSSimo Sorce 	u32 inlen;
1074030d794bSSimo Sorce 	int res;
1075030d794bSSimo Sorce 
1076030d794bSSimo Sorce 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1077030d794bSSimo Sorce 	if (res)
1078030d794bSSimo Sorce 		return res;
1079030d794bSSimo Sorce 
1080030d794bSSimo Sorce 	inlen = svc_getnl(argv);
1081030d794bSSimo Sorce 	if (inlen > (argv->iov_len + rqstp->rq_arg.page_len))
1082030d794bSSimo Sorce 		return SVC_DENIED;
1083030d794bSSimo Sorce 
1084030d794bSSimo Sorce 	in_token->pages = rqstp->rq_pages;
1085030d794bSSimo Sorce 	in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK;
1086030d794bSSimo Sorce 	in_token->page_len = inlen;
1087030d794bSSimo Sorce 
1088030d794bSSimo Sorce 	return 0;
1089030d794bSSimo Sorce }
1090030d794bSSimo Sorce 
1091fc2952a2SSimo Sorce static inline int
1092fc2952a2SSimo Sorce gss_write_resv(struct kvec *resv, size_t size_limit,
1093fc2952a2SSimo Sorce 	       struct xdr_netobj *out_handle, struct xdr_netobj *out_token,
1094fc2952a2SSimo Sorce 	       int major_status, int minor_status)
1095fc2952a2SSimo Sorce {
1096fc2952a2SSimo Sorce 	if (resv->iov_len + 4 > size_limit)
1097fc2952a2SSimo Sorce 		return -1;
1098fc2952a2SSimo Sorce 	svc_putnl(resv, RPC_SUCCESS);
1099fc2952a2SSimo Sorce 	if (svc_safe_putnetobj(resv, out_handle))
1100fc2952a2SSimo Sorce 		return -1;
1101fc2952a2SSimo Sorce 	if (resv->iov_len + 3 * 4 > size_limit)
1102fc2952a2SSimo Sorce 		return -1;
1103fc2952a2SSimo Sorce 	svc_putnl(resv, major_status);
1104fc2952a2SSimo Sorce 	svc_putnl(resv, minor_status);
1105fc2952a2SSimo Sorce 	svc_putnl(resv, GSS_SEQ_WIN);
1106fc2952a2SSimo Sorce 	if (svc_safe_putnetobj(resv, out_token))
1107fc2952a2SSimo Sorce 		return -1;
1108fc2952a2SSimo Sorce 	return 0;
1109fc2952a2SSimo Sorce }
1110fc2952a2SSimo Sorce 
11111da177e4SLinus Torvalds /*
111221fcd02bSJ. Bruce Fields  * Having read the cred already and found we're in the context
111321fcd02bSJ. Bruce Fields  * initiation case, read the verifier and initiate (or check the results
111421fcd02bSJ. Bruce Fields  * of) upcalls to userspace for help with context initiation.  If
111521fcd02bSJ. Bruce Fields  * the upcall results are available, write the verifier and result.
111621fcd02bSJ. Bruce Fields  * Otherwise, drop the request pending an answer to the upcall.
111721fcd02bSJ. Bruce Fields  */
1118030d794bSSimo Sorce static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
111921fcd02bSJ. Bruce Fields 			struct rpc_gss_wire_cred *gc, __be32 *authp)
112021fcd02bSJ. Bruce Fields {
112121fcd02bSJ. Bruce Fields 	struct kvec *argv = &rqstp->rq_arg.head[0];
112221fcd02bSJ. Bruce Fields 	struct kvec *resv = &rqstp->rq_res.head[0];
112321fcd02bSJ. Bruce Fields 	struct rsi *rsip, rsikey;
1124980e5a40SJ. Bruce Fields 	int ret;
1125a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
112621fcd02bSJ. Bruce Fields 
112721fcd02bSJ. Bruce Fields 	memset(&rsikey, 0, sizeof(rsikey));
1128fc2952a2SSimo Sorce 	ret = gss_read_verf(gc, argv, authp,
1129fc2952a2SSimo Sorce 			    &rsikey.in_handle, &rsikey.in_token);
1130fc2952a2SSimo Sorce 	if (ret)
1131fc2952a2SSimo Sorce 		return ret;
113221fcd02bSJ. Bruce Fields 
113321fcd02bSJ. Bruce Fields 	/* Perform upcall, or find upcall result: */
1134a1db410dSStanislav Kinsbursky 	rsip = rsi_lookup(sn->rsi_cache, &rsikey);
113521fcd02bSJ. Bruce Fields 	rsi_free(&rsikey);
113621fcd02bSJ. Bruce Fields 	if (!rsip)
11371ebede86SNeilBrown 		return SVC_CLOSE;
1138a1db410dSStanislav Kinsbursky 	if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
113921fcd02bSJ. Bruce Fields 		/* No upcall result: */
11401ebede86SNeilBrown 		return SVC_CLOSE;
11412ed5282cSNeilBrown 
11421ebede86SNeilBrown 	ret = SVC_CLOSE;
114321fcd02bSJ. Bruce Fields 	/* Got an answer to the upcall; use it: */
1144fc2952a2SSimo Sorce 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1145fc2952a2SSimo Sorce 				&rsip->out_handle, &rsip->major_status))
1146980e5a40SJ. Bruce Fields 		goto out;
1147fc2952a2SSimo Sorce 	if (gss_write_resv(resv, PAGE_SIZE,
1148fc2952a2SSimo Sorce 			   &rsip->out_handle, &rsip->out_token,
1149fc2952a2SSimo Sorce 			   rsip->major_status, rsip->minor_status))
1150980e5a40SJ. Bruce Fields 		goto out;
11512ed5282cSNeilBrown 
1152980e5a40SJ. Bruce Fields 	ret = SVC_COMPLETE;
1153980e5a40SJ. Bruce Fields out:
1154a1db410dSStanislav Kinsbursky 	cache_put(&rsip->h, sn->rsi_cache);
1155980e5a40SJ. Bruce Fields 	return ret;
115621fcd02bSJ. Bruce Fields }
115721fcd02bSJ. Bruce Fields 
1158030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd,
1159030d794bSSimo Sorce 				struct gssp_upcall_data *ud,
1160030d794bSSimo Sorce 				uint64_t *handle)
1161030d794bSSimo Sorce {
1162030d794bSSimo Sorce 	struct rsc rsci, *rscp = NULL;
1163030d794bSSimo Sorce 	static atomic64_t ctxhctr;
1164030d794bSSimo Sorce 	long long ctxh;
1165030d794bSSimo Sorce 	struct gss_api_mech *gm = NULL;
1166030d794bSSimo Sorce 	time_t expiry;
1167030d794bSSimo Sorce 	int status = -EINVAL;
1168030d794bSSimo Sorce 
1169030d794bSSimo Sorce 	memset(&rsci, 0, sizeof(rsci));
1170030d794bSSimo Sorce 	/* context handle */
1171030d794bSSimo Sorce 	status = -ENOMEM;
1172030d794bSSimo Sorce 	/* the handle needs to be just a unique id,
1173030d794bSSimo Sorce 	 * use a static counter */
1174030d794bSSimo Sorce 	ctxh = atomic64_inc_return(&ctxhctr);
1175030d794bSSimo Sorce 
1176030d794bSSimo Sorce 	/* make a copy for the caller */
1177030d794bSSimo Sorce 	*handle = ctxh;
1178030d794bSSimo Sorce 
1179030d794bSSimo Sorce 	/* make a copy for the rsc cache */
1180030d794bSSimo Sorce 	if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t)))
1181030d794bSSimo Sorce 		goto out;
1182030d794bSSimo Sorce 	rscp = rsc_lookup(cd, &rsci);
1183030d794bSSimo Sorce 	if (!rscp)
1184030d794bSSimo Sorce 		goto out;
1185030d794bSSimo Sorce 
1186030d794bSSimo Sorce 	/* creds */
1187030d794bSSimo Sorce 	if (!ud->found_creds) {
1188030d794bSSimo Sorce 		/* userspace seem buggy, we should always get at least a
1189030d794bSSimo Sorce 		 * mapping to nobody */
11903be34555SJ. Bruce Fields 		dprintk("RPC:       No creds found!\n");
11913be34555SJ. Bruce Fields 		goto out;
1192030d794bSSimo Sorce 	} else {
1193030d794bSSimo Sorce 
1194030d794bSSimo Sorce 		/* steal creds */
1195030d794bSSimo Sorce 		rsci.cred = ud->creds;
1196030d794bSSimo Sorce 		memset(&ud->creds, 0, sizeof(struct svc_cred));
1197030d794bSSimo Sorce 
1198030d794bSSimo Sorce 		status = -EOPNOTSUPP;
1199030d794bSSimo Sorce 		/* get mech handle from OID */
1200030d794bSSimo Sorce 		gm = gss_mech_get_by_OID(&ud->mech_oid);
1201030d794bSSimo Sorce 		if (!gm)
1202030d794bSSimo Sorce 			goto out;
12037193bd17SJ. Bruce Fields 		rsci.cred.cr_gss_mech = gm;
1204030d794bSSimo Sorce 
1205030d794bSSimo Sorce 		status = -EINVAL;
1206030d794bSSimo Sorce 		/* mech-specific data: */
1207030d794bSSimo Sorce 		status = gss_import_sec_context(ud->out_handle.data,
1208030d794bSSimo Sorce 						ud->out_handle.len,
1209030d794bSSimo Sorce 						gm, &rsci.mechctx,
1210030d794bSSimo Sorce 						&expiry, GFP_KERNEL);
1211030d794bSSimo Sorce 		if (status)
1212030d794bSSimo Sorce 			goto out;
1213030d794bSSimo Sorce 	}
1214030d794bSSimo Sorce 
1215030d794bSSimo Sorce 	rsci.h.expiry_time = expiry;
1216030d794bSSimo Sorce 	rscp = rsc_update(cd, &rsci, rscp);
1217030d794bSSimo Sorce 	status = 0;
1218030d794bSSimo Sorce out:
1219030d794bSSimo Sorce 	rsc_free(&rsci);
1220030d794bSSimo Sorce 	if (rscp)
1221030d794bSSimo Sorce 		cache_put(&rscp->h, cd);
1222030d794bSSimo Sorce 	else
1223030d794bSSimo Sorce 		status = -ENOMEM;
1224030d794bSSimo Sorce 	return status;
1225030d794bSSimo Sorce }
1226030d794bSSimo Sorce 
1227030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
1228030d794bSSimo Sorce 			struct rpc_gss_wire_cred *gc, __be32 *authp)
1229030d794bSSimo Sorce {
1230030d794bSSimo Sorce 	struct kvec *resv = &rqstp->rq_res.head[0];
1231030d794bSSimo Sorce 	struct xdr_netobj cli_handle;
1232030d794bSSimo Sorce 	struct gssp_upcall_data ud;
1233030d794bSSimo Sorce 	uint64_t handle;
1234030d794bSSimo Sorce 	int status;
1235030d794bSSimo Sorce 	int ret;
1236030d794bSSimo Sorce 	struct net *net = rqstp->rq_xprt->xpt_net;
1237030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1238030d794bSSimo Sorce 
1239030d794bSSimo Sorce 	memset(&ud, 0, sizeof(ud));
1240030d794bSSimo Sorce 	ret = gss_read_proxy_verf(rqstp, gc, authp,
1241030d794bSSimo Sorce 				  &ud.in_handle, &ud.in_token);
1242030d794bSSimo Sorce 	if (ret)
1243030d794bSSimo Sorce 		return ret;
1244030d794bSSimo Sorce 
1245030d794bSSimo Sorce 	ret = SVC_CLOSE;
1246030d794bSSimo Sorce 
1247030d794bSSimo Sorce 	/* Perform synchronous upcall to gss-proxy */
1248030d794bSSimo Sorce 	status = gssp_accept_sec_context_upcall(net, &ud);
1249030d794bSSimo Sorce 	if (status)
1250030d794bSSimo Sorce 		goto out;
1251030d794bSSimo Sorce 
125204d70edaSScott Mayhew 	dprintk("RPC:       svcauth_gss: gss major status = %d "
125304d70edaSScott Mayhew 			"minor status = %d\n",
125404d70edaSScott Mayhew 			ud.major_status, ud.minor_status);
1255030d794bSSimo Sorce 
1256030d794bSSimo Sorce 	switch (ud.major_status) {
1257030d794bSSimo Sorce 	case GSS_S_CONTINUE_NEEDED:
1258030d794bSSimo Sorce 		cli_handle = ud.out_handle;
1259030d794bSSimo Sorce 		break;
1260030d794bSSimo Sorce 	case GSS_S_COMPLETE:
1261030d794bSSimo Sorce 		status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle);
1262030d794bSSimo Sorce 		if (status)
1263030d794bSSimo Sorce 			goto out;
1264030d794bSSimo Sorce 		cli_handle.data = (u8 *)&handle;
1265030d794bSSimo Sorce 		cli_handle.len = sizeof(handle);
1266030d794bSSimo Sorce 		break;
1267030d794bSSimo Sorce 	default:
1268030d794bSSimo Sorce 		ret = SVC_CLOSE;
1269030d794bSSimo Sorce 		goto out;
1270030d794bSSimo Sorce 	}
1271030d794bSSimo Sorce 
1272030d794bSSimo Sorce 	/* Got an answer to the upcall; use it: */
1273030d794bSSimo Sorce 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1274030d794bSSimo Sorce 				&cli_handle, &ud.major_status))
1275030d794bSSimo Sorce 		goto out;
1276030d794bSSimo Sorce 	if (gss_write_resv(resv, PAGE_SIZE,
1277030d794bSSimo Sorce 			   &cli_handle, &ud.out_token,
1278030d794bSSimo Sorce 			   ud.major_status, ud.minor_status))
1279030d794bSSimo Sorce 		goto out;
1280030d794bSSimo Sorce 
1281030d794bSSimo Sorce 	ret = SVC_COMPLETE;
1282030d794bSSimo Sorce out:
1283030d794bSSimo Sorce 	gssp_free_upcall_data(&ud);
1284030d794bSSimo Sorce 	return ret;
1285030d794bSSimo Sorce }
1286030d794bSSimo Sorce 
12870fdc2678SJeff Layton /*
12880fdc2678SJeff Layton  * Try to set the sn->use_gss_proxy variable to a new value. We only allow
12890fdc2678SJeff Layton  * it to be changed if it's currently undefined (-1). If it's any other value
12900fdc2678SJeff Layton  * then return -EBUSY unless the type wouldn't have changed anyway.
12910fdc2678SJeff Layton  */
12920fdc2678SJeff Layton static int set_gss_proxy(struct net *net, int type)
12930fdc2678SJeff Layton {
12940fdc2678SJeff Layton 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
12950fdc2678SJeff Layton 	int ret;
12960fdc2678SJeff Layton 
12970fdc2678SJeff Layton 	WARN_ON_ONCE(type != 0 && type != 1);
12980fdc2678SJeff Layton 	ret = cmpxchg(&sn->use_gss_proxy, -1, type);
12990fdc2678SJeff Layton 	if (ret != -1 && ret != type)
13000fdc2678SJeff Layton 		return -EBUSY;
13010fdc2678SJeff Layton 	return 0;
13020fdc2678SJeff Layton }
1303030d794bSSimo Sorce 
1304030d794bSSimo Sorce static bool use_gss_proxy(struct net *net)
1305030d794bSSimo Sorce {
1306030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1307030d794bSSimo Sorce 
13080fdc2678SJeff Layton 	/* If use_gss_proxy is still undefined, then try to disable it */
13090fdc2678SJeff Layton 	if (sn->use_gss_proxy == -1)
13100fdc2678SJeff Layton 		set_gss_proxy(net, 0);
1311030d794bSSimo Sorce 	return sn->use_gss_proxy;
1312030d794bSSimo Sorce }
1313030d794bSSimo Sorce 
13140ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS
13150ff3bab5SJ. Bruce Fields 
1316030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf,
1317030d794bSSimo Sorce 			 size_t count, loff_t *ppos)
1318030d794bSSimo Sorce {
1319e77e4300SAl Viro 	struct net *net = PDE_DATA(file_inode(file));
1320030d794bSSimo Sorce 	char tbuf[20];
1321030d794bSSimo Sorce 	unsigned long i;
1322030d794bSSimo Sorce 	int res;
1323030d794bSSimo Sorce 
1324030d794bSSimo Sorce 	if (*ppos || count > sizeof(tbuf)-1)
1325030d794bSSimo Sorce 		return -EINVAL;
1326030d794bSSimo Sorce 	if (copy_from_user(tbuf, buf, count))
1327030d794bSSimo Sorce 		return -EFAULT;
1328030d794bSSimo Sorce 
1329030d794bSSimo Sorce 	tbuf[count] = 0;
1330030d794bSSimo Sorce 	res = kstrtoul(tbuf, 0, &i);
1331030d794bSSimo Sorce 	if (res)
1332030d794bSSimo Sorce 		return res;
1333030d794bSSimo Sorce 	if (i != 1)
1334030d794bSSimo Sorce 		return -EINVAL;
1335a92e5eb1SJeff Layton 	res = set_gssp_clnt(net);
1336030d794bSSimo Sorce 	if (res)
1337030d794bSSimo Sorce 		return res;
1338a92e5eb1SJeff Layton 	res = set_gss_proxy(net, 1);
1339030d794bSSimo Sorce 	if (res)
1340030d794bSSimo Sorce 		return res;
1341030d794bSSimo Sorce 	return count;
1342030d794bSSimo Sorce }
1343030d794bSSimo Sorce 
1344030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf,
1345030d794bSSimo Sorce 			 size_t count, loff_t *ppos)
1346030d794bSSimo Sorce {
1347e77e4300SAl Viro 	struct net *net = PDE_DATA(file_inode(file));
13481654a04cSJeff Layton 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1349030d794bSSimo Sorce 	unsigned long p = *ppos;
1350030d794bSSimo Sorce 	char tbuf[10];
1351030d794bSSimo Sorce 	size_t len;
1352030d794bSSimo Sorce 
13531654a04cSJeff Layton 	snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
1354030d794bSSimo Sorce 	len = strlen(tbuf);
1355030d794bSSimo Sorce 	if (p >= len)
1356030d794bSSimo Sorce 		return 0;
1357030d794bSSimo Sorce 	len -= p;
1358030d794bSSimo Sorce 	if (len > count)
1359030d794bSSimo Sorce 		len = count;
1360030d794bSSimo Sorce 	if (copy_to_user(buf, (void *)(tbuf+p), len))
1361030d794bSSimo Sorce 		return -EFAULT;
1362030d794bSSimo Sorce 	*ppos += len;
1363030d794bSSimo Sorce 	return len;
1364030d794bSSimo Sorce }
1365030d794bSSimo Sorce 
1366030d794bSSimo Sorce static const struct file_operations use_gss_proxy_ops = {
1367030d794bSSimo Sorce 	.open = nonseekable_open,
1368030d794bSSimo Sorce 	.write = write_gssp,
1369030d794bSSimo Sorce 	.read = read_gssp,
1370030d794bSSimo Sorce };
1371030d794bSSimo Sorce 
1372030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net)
1373030d794bSSimo Sorce {
1374030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1375030d794bSSimo Sorce 	struct proc_dir_entry **p = &sn->use_gssp_proc;
1376030d794bSSimo Sorce 
1377030d794bSSimo Sorce 	sn->use_gss_proxy = -1;
1378d6444062SJoe Perches 	*p = proc_create_data("use-gss-proxy", S_IFREG | 0600,
1379030d794bSSimo Sorce 			      sn->proc_net_rpc,
1380030d794bSSimo Sorce 			      &use_gss_proxy_ops, net);
1381030d794bSSimo Sorce 	if (!*p)
1382030d794bSSimo Sorce 		return -ENOMEM;
1383030d794bSSimo Sorce 	init_gssp_clnt(sn);
1384030d794bSSimo Sorce 	return 0;
1385030d794bSSimo Sorce }
1386030d794bSSimo Sorce 
1387030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net)
1388030d794bSSimo Sorce {
1389030d794bSSimo Sorce 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1390030d794bSSimo Sorce 
1391030d794bSSimo Sorce 	if (sn->use_gssp_proc) {
1392030d794bSSimo Sorce 		remove_proc_entry("use-gss-proxy", sn->proc_net_rpc);
1393030d794bSSimo Sorce 		clear_gssp_clnt(sn);
1394030d794bSSimo Sorce 	}
1395030d794bSSimo Sorce }
13960ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */
13970ff3bab5SJ. Bruce Fields 
13980ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net)
13990ff3bab5SJ. Bruce Fields {
14000ff3bab5SJ. Bruce Fields 	return 0;
14010ff3bab5SJ. Bruce Fields }
14020ff3bab5SJ. Bruce Fields 
14030ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
1404030d794bSSimo Sorce 
1405030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */
1406030d794bSSimo Sorce 
140721fcd02bSJ. Bruce Fields /*
14081da177e4SLinus Torvalds  * Accept an rpcsec packet.
14091da177e4SLinus Torvalds  * If context establishment, punt to user space
14101da177e4SLinus Torvalds  * If data exchange, verify/decrypt
14111da177e4SLinus Torvalds  * If context destruction, handle here
14121da177e4SLinus Torvalds  * In the context establishment and destruction case we encode
14131da177e4SLinus Torvalds  * response here and return SVC_COMPLETE.
14141da177e4SLinus Torvalds  */
14151da177e4SLinus Torvalds static int
1416d8ed029dSAlexey Dobriyan svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
14171da177e4SLinus Torvalds {
14181da177e4SLinus Torvalds 	struct kvec	*argv = &rqstp->rq_arg.head[0];
14191da177e4SLinus Torvalds 	struct kvec	*resv = &rqstp->rq_res.head[0];
14201da177e4SLinus Torvalds 	u32		crlen;
14211da177e4SLinus Torvalds 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
14221da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc;
14231da177e4SLinus Torvalds 	struct rsc	*rsci = NULL;
1424d8ed029dSAlexey Dobriyan 	__be32		*rpcstart;
1425d8ed029dSAlexey Dobriyan 	__be32		*reject_stat = resv->iov_base + resv->iov_len;
14261da177e4SLinus Torvalds 	int		ret;
1427a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
14281da177e4SLinus Torvalds 
14298885cb36SChuck Lever 	dprintk("RPC:       svcauth_gss: argv->iov_len = %zd\n",
14308885cb36SChuck Lever 			argv->iov_len);
14311da177e4SLinus Torvalds 
14321da177e4SLinus Torvalds 	*authp = rpc_autherr_badcred;
14331da177e4SLinus Torvalds 	if (!svcdata)
14341da177e4SLinus Torvalds 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
14351da177e4SLinus Torvalds 	if (!svcdata)
14361da177e4SLinus Torvalds 		goto auth_err;
14371da177e4SLinus Torvalds 	rqstp->rq_auth_data = svcdata;
14385b304bc5SJ.Bruce Fields 	svcdata->verf_start = NULL;
14391da177e4SLinus Torvalds 	svcdata->rsci = NULL;
14401da177e4SLinus Torvalds 	gc = &svcdata->clcred;
14411da177e4SLinus Torvalds 
14421da177e4SLinus Torvalds 	/* start of rpc packet is 7 u32's back from here:
14431da177e4SLinus Torvalds 	 * xid direction rpcversion prog vers proc flavour
14441da177e4SLinus Torvalds 	 */
14451da177e4SLinus Torvalds 	rpcstart = argv->iov_base;
14461da177e4SLinus Torvalds 	rpcstart -= 7;
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds 	/* credential is:
14491da177e4SLinus Torvalds 	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
145025985edcSLucas De Marchi 	 * at least 5 u32s, and is preceded by length, so that makes 6.
14511da177e4SLinus Torvalds 	 */
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds 	if (argv->iov_len < 5 * 4)
14541da177e4SLinus Torvalds 		goto auth_err;
145576994313SAlexey Dobriyan 	crlen = svc_getnl(argv);
145676994313SAlexey Dobriyan 	if (svc_getnl(argv) != RPC_GSS_VERSION)
14571da177e4SLinus Torvalds 		goto auth_err;
145876994313SAlexey Dobriyan 	gc->gc_proc = svc_getnl(argv);
145976994313SAlexey Dobriyan 	gc->gc_seq = svc_getnl(argv);
146076994313SAlexey Dobriyan 	gc->gc_svc = svc_getnl(argv);
14611da177e4SLinus Torvalds 	if (svc_safe_getnetobj(argv, &gc->gc_ctx))
14621da177e4SLinus Torvalds 		goto auth_err;
14631da177e4SLinus Torvalds 	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
14641da177e4SLinus Torvalds 		goto auth_err;
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
14671da177e4SLinus Torvalds 		goto auth_err;
14681da177e4SLinus Torvalds 
14691da177e4SLinus Torvalds 	*authp = rpc_autherr_badverf;
14701da177e4SLinus Torvalds 	switch (gc->gc_proc) {
14711da177e4SLinus Torvalds 	case RPC_GSS_PROC_INIT:
14721da177e4SLinus Torvalds 	case RPC_GSS_PROC_CONTINUE_INIT:
1473030d794bSSimo Sorce 		if (use_gss_proxy(SVC_NET(rqstp)))
1474030d794bSSimo Sorce 			return svcauth_gss_proxy_init(rqstp, gc, authp);
1475030d794bSSimo Sorce 		else
1476030d794bSSimo Sorce 			return svcauth_gss_legacy_init(rqstp, gc, authp);
14771da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
14781da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
147921fcd02bSJ. Bruce Fields 		/* Look up the context, and check the verifier: */
14801da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_credproblem;
1481a1db410dSStanislav Kinsbursky 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
14821da177e4SLinus Torvalds 		if (!rsci)
14831da177e4SLinus Torvalds 			goto auth_err;
14841da177e4SLinus Torvalds 		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
14851da177e4SLinus Torvalds 		case SVC_OK:
14861da177e4SLinus Torvalds 			break;
14871da177e4SLinus Torvalds 		case SVC_DENIED:
14881da177e4SLinus Torvalds 			goto auth_err;
14891da177e4SLinus Torvalds 		case SVC_DROP:
14901da177e4SLinus Torvalds 			goto drop;
14911da177e4SLinus Torvalds 		}
14921da177e4SLinus Torvalds 		break;
14931da177e4SLinus Torvalds 	default:
14941da177e4SLinus Torvalds 		*authp = rpc_autherr_rejectedcred;
14951da177e4SLinus Torvalds 		goto auth_err;
14961da177e4SLinus Torvalds 	}
14971da177e4SLinus Torvalds 
14981da177e4SLinus Torvalds 	/* now act upon the command: */
14991da177e4SLinus Torvalds 	switch (gc->gc_proc) {
15001da177e4SLinus Torvalds 	case RPC_GSS_PROC_DESTROY:
1501c5e434c9SWei Yongjun 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
1502c5e434c9SWei Yongjun 			goto auth_err;
15032b477c00SNeil Brown 		/* Delete the entry from the cache_list and call cache_put */
15042b477c00SNeil Brown 		sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
15051da177e4SLinus Torvalds 		if (resv->iov_len + 4 > PAGE_SIZE)
15061da177e4SLinus Torvalds 			goto drop;
150776994313SAlexey Dobriyan 		svc_putnl(resv, RPC_SUCCESS);
15081da177e4SLinus Torvalds 		goto complete;
15091da177e4SLinus Torvalds 	case RPC_GSS_PROC_DATA:
15101da177e4SLinus Torvalds 		*authp = rpcsec_gsserr_ctxproblem;
15115b304bc5SJ.Bruce Fields 		svcdata->verf_start = resv->iov_base + resv->iov_len;
15121da177e4SLinus Torvalds 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
15131da177e4SLinus Torvalds 			goto auth_err;
15141da177e4SLinus Torvalds 		rqstp->rq_cred = rsci->cred;
15151da177e4SLinus Torvalds 		get_group_info(rsci->cred.cr_group_info);
15161da177e4SLinus Torvalds 		*authp = rpc_autherr_badcred;
15171da177e4SLinus Torvalds 		switch (gc->gc_svc) {
15181da177e4SLinus Torvalds 		case RPC_GSS_SVC_NONE:
15191da177e4SLinus Torvalds 			break;
15201da177e4SLinus Torvalds 		case RPC_GSS_SVC_INTEGRITY:
1521b620754bSJ. Bruce Fields 			/* placeholders for length and seq. number: */
1522b620754bSJ. Bruce Fields 			svc_putnl(resv, 0);
1523b620754bSJ. Bruce Fields 			svc_putnl(resv, 0);
15244c190e2fSJeff Layton 			if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
15251da177e4SLinus Torvalds 					gc->gc_seq, rsci->mechctx))
1526dd35210eSHarshula Jayasuriya 				goto garbage_args;
1527a5cddc88SJ. Bruce Fields 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
1528b620754bSJ. Bruce Fields 			break;
1529b620754bSJ. Bruce Fields 		case RPC_GSS_SVC_PRIVACY:
15301da177e4SLinus Torvalds 			/* placeholders for length and seq. number: */
153176994313SAlexey Dobriyan 			svc_putnl(resv, 0);
153276994313SAlexey Dobriyan 			svc_putnl(resv, 0);
15337c9fdcfbSJ. Bruce Fields 			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
15347c9fdcfbSJ. Bruce Fields 					gc->gc_seq, rsci->mechctx))
1535dd35210eSHarshula Jayasuriya 				goto garbage_args;
1536a5cddc88SJ. Bruce Fields 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
15377c9fdcfbSJ. Bruce Fields 			break;
15381da177e4SLinus Torvalds 		default:
15391da177e4SLinus Torvalds 			goto auth_err;
15401da177e4SLinus Torvalds 		}
15411da177e4SLinus Torvalds 		svcdata->rsci = rsci;
15421da177e4SLinus Torvalds 		cache_get(&rsci->h);
1543d5497fc6SJ. Bruce Fields 		rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
154483523d08SChuck Lever 					rsci->mechctx->mech_type,
154583523d08SChuck Lever 					GSS_C_QOP_DEFAULT,
154683523d08SChuck Lever 					gc->gc_svc);
15471da177e4SLinus Torvalds 		ret = SVC_OK;
15481da177e4SLinus Torvalds 		goto out;
15491da177e4SLinus Torvalds 	}
1550dd35210eSHarshula Jayasuriya garbage_args:
1551dd35210eSHarshula Jayasuriya 	ret = SVC_GARBAGE;
1552dd35210eSHarshula Jayasuriya 	goto out;
15531da177e4SLinus Torvalds auth_err:
155421fcd02bSJ. Bruce Fields 	/* Restore write pointer to its original value: */
15551da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, reject_stat);
15561da177e4SLinus Torvalds 	ret = SVC_DENIED;
15571da177e4SLinus Torvalds 	goto out;
15581da177e4SLinus Torvalds complete:
15591da177e4SLinus Torvalds 	ret = SVC_COMPLETE;
15601da177e4SLinus Torvalds 	goto out;
15611da177e4SLinus Torvalds drop:
15624d712ef1SChuck Lever 	ret = SVC_CLOSE;
15631da177e4SLinus Torvalds out:
15641da177e4SLinus Torvalds 	if (rsci)
1565a1db410dSStanislav Kinsbursky 		cache_put(&rsci->h, sn->rsc_cache);
15661da177e4SLinus Torvalds 	return ret;
15671da177e4SLinus Torvalds }
15681da177e4SLinus Torvalds 
1569cfbdbab0SAl Viro static __be32 *
15703c15a486SJ.Bruce Fields svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd)
15713c15a486SJ.Bruce Fields {
1572cfbdbab0SAl Viro 	__be32 *p;
1573cfbdbab0SAl Viro 	u32 verf_len;
15743c15a486SJ.Bruce Fields 
15755b304bc5SJ.Bruce Fields 	p = gsd->verf_start;
15765b304bc5SJ.Bruce Fields 	gsd->verf_start = NULL;
15775b304bc5SJ.Bruce Fields 
15785b304bc5SJ.Bruce Fields 	/* If the reply stat is nonzero, don't wrap: */
15795b304bc5SJ.Bruce Fields 	if (*(p-1) != rpc_success)
15805b304bc5SJ.Bruce Fields 		return NULL;
15815b304bc5SJ.Bruce Fields 	/* Skip the verifier: */
15825b304bc5SJ.Bruce Fields 	p += 1;
15835b304bc5SJ.Bruce Fields 	verf_len = ntohl(*p++);
15845b304bc5SJ.Bruce Fields 	p += XDR_QUADLEN(verf_len);
15853c15a486SJ.Bruce Fields 	/* move accept_stat to right place: */
15863c15a486SJ.Bruce Fields 	memcpy(p, p + 2, 4);
15875b304bc5SJ.Bruce Fields 	/* Also don't wrap if the accept stat is nonzero: */
15883c15a486SJ.Bruce Fields 	if (*p != rpc_success) {
15893c15a486SJ.Bruce Fields 		resbuf->head[0].iov_len -= 2 * 4;
15903c15a486SJ.Bruce Fields 		return NULL;
15913c15a486SJ.Bruce Fields 	}
15923c15a486SJ.Bruce Fields 	p++;
15933c15a486SJ.Bruce Fields 	return p;
15943c15a486SJ.Bruce Fields }
15953c15a486SJ.Bruce Fields 
1596e142ede8SJ. Bruce Fields static inline int
1597e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
15981da177e4SLinus Torvalds {
15991da177e4SLinus Torvalds 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
16001da177e4SLinus Torvalds 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
16011da177e4SLinus Torvalds 	struct xdr_buf *resbuf = &rqstp->rq_res;
16021da177e4SLinus Torvalds 	struct xdr_buf integ_buf;
16031da177e4SLinus Torvalds 	struct xdr_netobj mic;
16041da177e4SLinus Torvalds 	struct kvec *resv;
1605d8ed029dSAlexey Dobriyan 	__be32 *p;
16061da177e4SLinus Torvalds 	int integ_offset, integ_len;
16071da177e4SLinus Torvalds 	int stat = -EINVAL;
16081da177e4SLinus Torvalds 
16093c15a486SJ.Bruce Fields 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
16103c15a486SJ.Bruce Fields 	if (p == NULL)
16111da177e4SLinus Torvalds 		goto out;
16121da177e4SLinus Torvalds 	integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
16131da177e4SLinus Torvalds 	integ_len = resbuf->len - integ_offset;
16141da177e4SLinus Torvalds 	BUG_ON(integ_len % 4);
16151da177e4SLinus Torvalds 	*p++ = htonl(integ_len);
16161da177e4SLinus Torvalds 	*p++ = htonl(gc->gc_seq);
16171754eb2bSJ. Bruce Fields 	if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) {
16181754eb2bSJ. Bruce Fields 		WARN_ON_ONCE(1);
16191754eb2bSJ. Bruce Fields 		goto out_err;
16201754eb2bSJ. Bruce Fields 	}
1621153e44d2SNeilBrown 	if (resbuf->tail[0].iov_base == NULL) {
1622e142ede8SJ. Bruce Fields 		if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1623dfee55f0SNeilBrown 			goto out_err;
1624e142ede8SJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
1625dfee55f0SNeilBrown 						+ resbuf->head[0].iov_len;
16261da177e4SLinus Torvalds 		resbuf->tail[0].iov_len = 0;
16271da177e4SLinus Torvalds 	}
1628bba0f88bSJ. Bruce Fields 	resv = &resbuf->tail[0];
16291da177e4SLinus Torvalds 	mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
163000fd6e14SJ. Bruce Fields 	if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))
16311da177e4SLinus Torvalds 		goto out_err;
163276994313SAlexey Dobriyan 	svc_putnl(resv, mic.len);
16331da177e4SLinus Torvalds 	memset(mic.data + mic.len, 0,
16341da177e4SLinus Torvalds 			round_up_to_quad(mic.len) - mic.len);
16351da177e4SLinus Torvalds 	resv->iov_len += XDR_QUADLEN(mic.len) << 2;
16361da177e4SLinus Torvalds 	/* not strictly required: */
16371da177e4SLinus Torvalds 	resbuf->len += XDR_QUADLEN(mic.len) << 2;
16381da177e4SLinus Torvalds 	BUG_ON(resv->iov_len > PAGE_SIZE);
1639e142ede8SJ. Bruce Fields out:
1640e142ede8SJ. Bruce Fields 	stat = 0;
1641e142ede8SJ. Bruce Fields out_err:
1642e142ede8SJ. Bruce Fields 	return stat;
1643e142ede8SJ. Bruce Fields }
1644e142ede8SJ. Bruce Fields 
16457c9fdcfbSJ. Bruce Fields static inline int
16467c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
16477c9fdcfbSJ. Bruce Fields {
16487c9fdcfbSJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
16497c9fdcfbSJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
16507c9fdcfbSJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
16517c9fdcfbSJ. Bruce Fields 	struct page **inpages = NULL;
1652753ed90dSAl Viro 	__be32 *p, *len;
1653753ed90dSAl Viro 	int offset;
16547c9fdcfbSJ. Bruce Fields 	int pad;
16557c9fdcfbSJ. Bruce Fields 
16563c15a486SJ.Bruce Fields 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
16573c15a486SJ.Bruce Fields 	if (p == NULL)
16587c9fdcfbSJ. Bruce Fields 		return 0;
16597c9fdcfbSJ. Bruce Fields 	len = p++;
16607c9fdcfbSJ. Bruce Fields 	offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
16617c9fdcfbSJ. Bruce Fields 	*p++ = htonl(gc->gc_seq);
16627c9fdcfbSJ. Bruce Fields 	inpages = resbuf->pages;
16637c9fdcfbSJ. Bruce Fields 	/* XXX: Would be better to write some xdr helper functions for
16647c9fdcfbSJ. Bruce Fields 	 * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
16657561042fSKevin Coffman 
16667561042fSKevin Coffman 	/*
16677561042fSKevin Coffman 	 * If there is currently tail data, make sure there is
16687561042fSKevin Coffman 	 * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in
16697561042fSKevin Coffman 	 * the page, and move the current tail data such that
16707561042fSKevin Coffman 	 * there is RPC_MAX_AUTH_SIZE slack space available in
16717561042fSKevin Coffman 	 * both the head and tail.
16727561042fSKevin Coffman 	 */
167344524359SNeilBrown 	if (resbuf->tail[0].iov_base) {
16747c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base
16757c9fdcfbSJ. Bruce Fields 							+ PAGE_SIZE);
16767c9fdcfbSJ. Bruce Fields 		BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base);
16777c9fdcfbSJ. Bruce Fields 		if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len
16787c9fdcfbSJ. Bruce Fields 				+ 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
16797c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
16807c9fdcfbSJ. Bruce Fields 		memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE,
16817c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_base,
16827c9fdcfbSJ. Bruce Fields 			resbuf->tail[0].iov_len);
16837c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
16847c9fdcfbSJ. Bruce Fields 	}
16857561042fSKevin Coffman 	/*
16867561042fSKevin Coffman 	 * If there is no current tail data, make sure there is
16877561042fSKevin Coffman 	 * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the
16887561042fSKevin Coffman 	 * allotted page, and set up tail information such that there
16897561042fSKevin Coffman 	 * is RPC_MAX_AUTH_SIZE slack space available in both the
16907561042fSKevin Coffman 	 * head and tail.
16917561042fSKevin Coffman 	 */
16927c9fdcfbSJ. Bruce Fields 	if (resbuf->tail[0].iov_base == NULL) {
16937c9fdcfbSJ. Bruce Fields 		if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
16947c9fdcfbSJ. Bruce Fields 			return -ENOMEM;
16957c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
16967c9fdcfbSJ. Bruce Fields 			+ resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
16977c9fdcfbSJ. Bruce Fields 		resbuf->tail[0].iov_len = 0;
16987c9fdcfbSJ. Bruce Fields 	}
16997c9fdcfbSJ. Bruce Fields 	if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
17007c9fdcfbSJ. Bruce Fields 		return -ENOMEM;
17017c9fdcfbSJ. Bruce Fields 	*len = htonl(resbuf->len - offset);
17027c9fdcfbSJ. Bruce Fields 	pad = 3 - ((resbuf->len - offset - 1)&3);
1703d8ed029dSAlexey Dobriyan 	p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len);
17047c9fdcfbSJ. Bruce Fields 	memset(p, 0, pad);
17057c9fdcfbSJ. Bruce Fields 	resbuf->tail[0].iov_len += pad;
17067c9fdcfbSJ. Bruce Fields 	resbuf->len += pad;
17077c9fdcfbSJ. Bruce Fields 	return 0;
17087c9fdcfbSJ. Bruce Fields }
17097c9fdcfbSJ. Bruce Fields 
1710e142ede8SJ. Bruce Fields static int
1711e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp)
1712e142ede8SJ. Bruce Fields {
1713e142ede8SJ. Bruce Fields 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1714e142ede8SJ. Bruce Fields 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
1715e142ede8SJ. Bruce Fields 	struct xdr_buf *resbuf = &rqstp->rq_res;
1716e142ede8SJ. Bruce Fields 	int stat = -EINVAL;
1717a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
1718e142ede8SJ. Bruce Fields 
1719e142ede8SJ. Bruce Fields 	if (gc->gc_proc != RPC_GSS_PROC_DATA)
1720e142ede8SJ. Bruce Fields 		goto out;
1721e142ede8SJ. Bruce Fields 	/* Release can be called twice, but we only wrap once. */
17225b304bc5SJ.Bruce Fields 	if (gsd->verf_start == NULL)
1723e142ede8SJ. Bruce Fields 		goto out;
1724e142ede8SJ. Bruce Fields 	/* normally not set till svc_send, but we need it here: */
17257c9fdcfbSJ. Bruce Fields 	/* XXX: what for?  Do we mess it up the moment we call svc_putu32
17267c9fdcfbSJ. Bruce Fields 	 * or whatever? */
17277c9fdcfbSJ. Bruce Fields 	resbuf->len = total_buf_len(resbuf);
1728e142ede8SJ. Bruce Fields 	switch (gc->gc_svc) {
1729e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_NONE:
1730e142ede8SJ. Bruce Fields 		break;
1731e142ede8SJ. Bruce Fields 	case RPC_GSS_SVC_INTEGRITY:
17327c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_integ(rqstp);
17337c9fdcfbSJ. Bruce Fields 		if (stat)
17347c9fdcfbSJ. Bruce Fields 			goto out_err;
17351da177e4SLinus Torvalds 		break;
17361da177e4SLinus Torvalds 	case RPC_GSS_SVC_PRIVACY:
17377c9fdcfbSJ. Bruce Fields 		stat = svcauth_gss_wrap_resp_priv(rqstp);
17387c9fdcfbSJ. Bruce Fields 		if (stat)
17397c9fdcfbSJ. Bruce Fields 			goto out_err;
17407c9fdcfbSJ. Bruce Fields 		break;
1741eac81736SWei Yongjun 	/*
1742eac81736SWei Yongjun 	 * For any other gc_svc value, svcauth_gss_accept() already set
1743eac81736SWei Yongjun 	 * the auth_error appropriately; just fall through:
1744eac81736SWei Yongjun 	 */
17451da177e4SLinus Torvalds 	}
17461da177e4SLinus Torvalds 
17471da177e4SLinus Torvalds out:
17481da177e4SLinus Torvalds 	stat = 0;
17491da177e4SLinus Torvalds out_err:
17501da177e4SLinus Torvalds 	if (rqstp->rq_client)
17511da177e4SLinus Torvalds 		auth_domain_put(rqstp->rq_client);
17521da177e4SLinus Torvalds 	rqstp->rq_client = NULL;
17533ab4d8b1SJ. Bruce Fields 	if (rqstp->rq_gssclient)
17543ab4d8b1SJ. Bruce Fields 		auth_domain_put(rqstp->rq_gssclient);
17553ab4d8b1SJ. Bruce Fields 	rqstp->rq_gssclient = NULL;
17561da177e4SLinus Torvalds 	if (rqstp->rq_cred.cr_group_info)
17571da177e4SLinus Torvalds 		put_group_info(rqstp->rq_cred.cr_group_info);
17581da177e4SLinus Torvalds 	rqstp->rq_cred.cr_group_info = NULL;
17591da177e4SLinus Torvalds 	if (gsd->rsci)
1760a1db410dSStanislav Kinsbursky 		cache_put(&gsd->rsci->h, sn->rsc_cache);
17611da177e4SLinus Torvalds 	gsd->rsci = NULL;
17621da177e4SLinus Torvalds 
17631da177e4SLinus Torvalds 	return stat;
17641da177e4SLinus Torvalds }
17651da177e4SLinus Torvalds 
17661da177e4SLinus Torvalds static void
17671da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom)
17681da177e4SLinus Torvalds {
17691da177e4SLinus Torvalds 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
17701da177e4SLinus Torvalds 
17711da177e4SLinus Torvalds 	kfree(dom->name);
17721da177e4SLinus Torvalds 	kfree(gd);
17731da177e4SLinus Torvalds }
17741da177e4SLinus Torvalds 
17751da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = {
17761da177e4SLinus Torvalds 	.name		= "rpcsec_gss",
17771da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
17781da177e4SLinus Torvalds 	.flavour	= RPC_AUTH_GSS,
17791da177e4SLinus Torvalds 	.accept		= svcauth_gss_accept,
17801da177e4SLinus Torvalds 	.release	= svcauth_gss_release,
17811da177e4SLinus Torvalds 	.domain_release = svcauth_gss_domain_release,
17821da177e4SLinus Torvalds 	.set_client	= svcauth_gss_set_client,
17831da177e4SLinus Torvalds };
17841da177e4SLinus Torvalds 
1785a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net)
1786a1db410dSStanislav Kinsbursky {
1787a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1788a1db410dSStanislav Kinsbursky 	struct cache_detail *cd;
1789a1db410dSStanislav Kinsbursky 	int err;
1790a1db410dSStanislav Kinsbursky 
1791a1db410dSStanislav Kinsbursky 	cd = cache_create_net(&rsi_cache_template, net);
1792a1db410dSStanislav Kinsbursky 	if (IS_ERR(cd))
1793a1db410dSStanislav Kinsbursky 		return PTR_ERR(cd);
1794a1db410dSStanislav Kinsbursky 	err = cache_register_net(cd, net);
1795a1db410dSStanislav Kinsbursky 	if (err) {
1796a1db410dSStanislav Kinsbursky 		cache_destroy_net(cd, net);
1797a1db410dSStanislav Kinsbursky 		return err;
1798a1db410dSStanislav Kinsbursky 	}
1799a1db410dSStanislav Kinsbursky 	sn->rsi_cache = cd;
1800a1db410dSStanislav Kinsbursky 	return 0;
1801a1db410dSStanislav Kinsbursky }
1802a1db410dSStanislav Kinsbursky 
1803a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net)
1804a1db410dSStanislav Kinsbursky {
1805a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1806a1db410dSStanislav Kinsbursky 	struct cache_detail *cd = sn->rsi_cache;
1807a1db410dSStanislav Kinsbursky 
1808a1db410dSStanislav Kinsbursky 	sn->rsi_cache = NULL;
1809a1db410dSStanislav Kinsbursky 	cache_purge(cd);
1810a1db410dSStanislav Kinsbursky 	cache_unregister_net(cd, net);
1811a1db410dSStanislav Kinsbursky 	cache_destroy_net(cd, net);
1812a1db410dSStanislav Kinsbursky }
1813a1db410dSStanislav Kinsbursky 
1814a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net)
1815a1db410dSStanislav Kinsbursky {
1816a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1817a1db410dSStanislav Kinsbursky 	struct cache_detail *cd;
1818a1db410dSStanislav Kinsbursky 	int err;
1819a1db410dSStanislav Kinsbursky 
1820a1db410dSStanislav Kinsbursky 	cd = cache_create_net(&rsc_cache_template, net);
1821a1db410dSStanislav Kinsbursky 	if (IS_ERR(cd))
1822a1db410dSStanislav Kinsbursky 		return PTR_ERR(cd);
1823a1db410dSStanislav Kinsbursky 	err = cache_register_net(cd, net);
1824a1db410dSStanislav Kinsbursky 	if (err) {
1825a1db410dSStanislav Kinsbursky 		cache_destroy_net(cd, net);
1826a1db410dSStanislav Kinsbursky 		return err;
1827a1db410dSStanislav Kinsbursky 	}
1828a1db410dSStanislav Kinsbursky 	sn->rsc_cache = cd;
1829a1db410dSStanislav Kinsbursky 	return 0;
1830a1db410dSStanislav Kinsbursky }
1831a1db410dSStanislav Kinsbursky 
1832a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net)
1833a1db410dSStanislav Kinsbursky {
1834a1db410dSStanislav Kinsbursky 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1835a1db410dSStanislav Kinsbursky 	struct cache_detail *cd = sn->rsc_cache;
1836a1db410dSStanislav Kinsbursky 
1837a1db410dSStanislav Kinsbursky 	sn->rsc_cache = NULL;
1838a1db410dSStanislav Kinsbursky 	cache_purge(cd);
1839a1db410dSStanislav Kinsbursky 	cache_unregister_net(cd, net);
1840a1db410dSStanislav Kinsbursky 	cache_destroy_net(cd, net);
1841a1db410dSStanislav Kinsbursky }
1842a1db410dSStanislav Kinsbursky 
1843a1db410dSStanislav Kinsbursky int
1844a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net)
1845a1db410dSStanislav Kinsbursky {
1846a1db410dSStanislav Kinsbursky 	int rv;
1847a1db410dSStanislav Kinsbursky 
1848a1db410dSStanislav Kinsbursky 	rv = rsc_cache_create_net(net);
1849a1db410dSStanislav Kinsbursky 	if (rv)
1850a1db410dSStanislav Kinsbursky 		return rv;
1851a1db410dSStanislav Kinsbursky 	rv = rsi_cache_create_net(net);
1852a1db410dSStanislav Kinsbursky 	if (rv)
1853a1db410dSStanislav Kinsbursky 		goto out1;
1854030d794bSSimo Sorce 	rv = create_use_gss_proxy_proc_entry(net);
1855030d794bSSimo Sorce 	if (rv)
1856030d794bSSimo Sorce 		goto out2;
1857a1db410dSStanislav Kinsbursky 	return 0;
1858030d794bSSimo Sorce out2:
1859030d794bSSimo Sorce 	destroy_use_gss_proxy_proc_entry(net);
1860a1db410dSStanislav Kinsbursky out1:
1861a1db410dSStanislav Kinsbursky 	rsc_cache_destroy_net(net);
1862a1db410dSStanislav Kinsbursky 	return rv;
1863a1db410dSStanislav Kinsbursky }
1864a1db410dSStanislav Kinsbursky 
1865a1db410dSStanislav Kinsbursky void
1866a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net)
1867a1db410dSStanislav Kinsbursky {
1868030d794bSSimo Sorce 	destroy_use_gss_proxy_proc_entry(net);
1869a1db410dSStanislav Kinsbursky 	rsi_cache_destroy_net(net);
1870a1db410dSStanislav Kinsbursky 	rsc_cache_destroy_net(net);
1871a1db410dSStanislav Kinsbursky }
1872a1db410dSStanislav Kinsbursky 
18731da177e4SLinus Torvalds int
18741da177e4SLinus Torvalds gss_svc_init(void)
18751da177e4SLinus Torvalds {
1876a1db410dSStanislav Kinsbursky 	return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
18771da177e4SLinus Torvalds }
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds void
18801da177e4SLinus Torvalds gss_svc_shutdown(void)
18811da177e4SLinus Torvalds {
18821da177e4SLinus Torvalds 	svc_auth_unregister(RPC_AUTH_GSS);
18831da177e4SLinus Torvalds }
1884