xref: /OK3568_Linux_fs/kernel/net/sunrpc/auth_gss/svcauth_gss.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Neil Brown <neilb@cse.unsw.edu.au>
4*4882a593Smuzhiyun  * J. Bruce Fields <bfields@umich.edu>
5*4882a593Smuzhiyun  * Andy Adamson <andros@umich.edu>
6*4882a593Smuzhiyun  * Dug Song <dugsong@monkey.org>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * RPCSEC_GSS server authentication.
9*4882a593Smuzhiyun  * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
10*4882a593Smuzhiyun  * (gssapi)
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * The RPCSEC_GSS involves three stages:
13*4882a593Smuzhiyun  *  1/ context creation
14*4882a593Smuzhiyun  *  2/ data exchange
15*4882a593Smuzhiyun  *  3/ context destruction
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * Context creation is handled largely by upcalls to user-space.
18*4882a593Smuzhiyun  *  In particular, GSS_Accept_sec_context is handled by an upcall
19*4882a593Smuzhiyun  * Data exchange is handled entirely within the kernel
20*4882a593Smuzhiyun  *  In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
21*4882a593Smuzhiyun  * Context destruction is handled in-kernel
22*4882a593Smuzhiyun  *  GSS_Delete_sec_context is in-kernel
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
25*4882a593Smuzhiyun  * The context handle and gss_token are used as a key into the rpcsec_init cache.
26*4882a593Smuzhiyun  * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
27*4882a593Smuzhiyun  * being major_status, minor_status, context_handle, reply_token.
28*4882a593Smuzhiyun  * These are sent back to the client.
29*4882a593Smuzhiyun  * Sequence window management is handled by the kernel.  The window size if currently
30*4882a593Smuzhiyun  * a compile time constant.
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  * When user-space is happy that a context is established, it places an entry
33*4882a593Smuzhiyun  * in the rpcsec_context cache. The key for this cache is the context_handle.
34*4882a593Smuzhiyun  * The content includes:
35*4882a593Smuzhiyun  *   uid/gidlist - for determining access rights
36*4882a593Smuzhiyun  *   mechanism type
37*4882a593Smuzhiyun  *   mechanism specific information, such as a key
38*4882a593Smuzhiyun  *
39*4882a593Smuzhiyun  */
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #include <linux/slab.h>
42*4882a593Smuzhiyun #include <linux/types.h>
43*4882a593Smuzhiyun #include <linux/module.h>
44*4882a593Smuzhiyun #include <linux/pagemap.h>
45*4882a593Smuzhiyun #include <linux/user_namespace.h>
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #include <linux/sunrpc/auth_gss.h>
48*4882a593Smuzhiyun #include <linux/sunrpc/gss_err.h>
49*4882a593Smuzhiyun #include <linux/sunrpc/svcauth.h>
50*4882a593Smuzhiyun #include <linux/sunrpc/svcauth_gss.h>
51*4882a593Smuzhiyun #include <linux/sunrpc/cache.h>
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #include <trace/events/rpcgss.h>
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #include "gss_rpc_upcall.h"
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
59*4882a593Smuzhiyun  * into replies.
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  * Key is context handle (\x if empty) and gss_token.
62*4882a593Smuzhiyun  * Content is major_status minor_status (integers) context_handle, reply_token.
63*4882a593Smuzhiyun  *
64*4882a593Smuzhiyun  */
65*4882a593Smuzhiyun 
netobj_equal(struct xdr_netobj * a,struct xdr_netobj * b)66*4882a593Smuzhiyun static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	return a->len == b->len && 0 == memcmp(a->data, b->data, a->len);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun #define	RSI_HASHBITS	6
72*4882a593Smuzhiyun #define	RSI_HASHMAX	(1<<RSI_HASHBITS)
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun struct rsi {
75*4882a593Smuzhiyun 	struct cache_head	h;
76*4882a593Smuzhiyun 	struct xdr_netobj	in_handle, in_token;
77*4882a593Smuzhiyun 	struct xdr_netobj	out_handle, out_token;
78*4882a593Smuzhiyun 	int			major_status, minor_status;
79*4882a593Smuzhiyun 	struct rcu_head		rcu_head;
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old);
83*4882a593Smuzhiyun static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item);
84*4882a593Smuzhiyun 
rsi_free(struct rsi * rsii)85*4882a593Smuzhiyun static void rsi_free(struct rsi *rsii)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	kfree(rsii->in_handle.data);
88*4882a593Smuzhiyun 	kfree(rsii->in_token.data);
89*4882a593Smuzhiyun 	kfree(rsii->out_handle.data);
90*4882a593Smuzhiyun 	kfree(rsii->out_token.data);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
rsi_free_rcu(struct rcu_head * head)93*4882a593Smuzhiyun static void rsi_free_rcu(struct rcu_head *head)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	struct rsi *rsii = container_of(head, struct rsi, rcu_head);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	rsi_free(rsii);
98*4882a593Smuzhiyun 	kfree(rsii);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
rsi_put(struct kref * ref)101*4882a593Smuzhiyun static void rsi_put(struct kref *ref)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	struct rsi *rsii = container_of(ref, struct rsi, h.ref);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	call_rcu(&rsii->rcu_head, rsi_free_rcu);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
rsi_hash(struct rsi * item)108*4882a593Smuzhiyun static inline int rsi_hash(struct rsi *item)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS)
111*4882a593Smuzhiyun 	     ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
rsi_match(struct cache_head * a,struct cache_head * b)114*4882a593Smuzhiyun static int rsi_match(struct cache_head *a, struct cache_head *b)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	struct rsi *item = container_of(a, struct rsi, h);
117*4882a593Smuzhiyun 	struct rsi *tmp = container_of(b, struct rsi, h);
118*4882a593Smuzhiyun 	return netobj_equal(&item->in_handle, &tmp->in_handle) &&
119*4882a593Smuzhiyun 	       netobj_equal(&item->in_token, &tmp->in_token);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
dup_to_netobj(struct xdr_netobj * dst,char * src,int len)122*4882a593Smuzhiyun static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	dst->len = len;
125*4882a593Smuzhiyun 	dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL);
126*4882a593Smuzhiyun 	if (len && !dst->data)
127*4882a593Smuzhiyun 		return -ENOMEM;
128*4882a593Smuzhiyun 	return 0;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
dup_netobj(struct xdr_netobj * dst,struct xdr_netobj * src)131*4882a593Smuzhiyun static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	return dup_to_netobj(dst, src->data, src->len);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun 
rsi_init(struct cache_head * cnew,struct cache_head * citem)136*4882a593Smuzhiyun static void rsi_init(struct cache_head *cnew, struct cache_head *citem)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	struct rsi *new = container_of(cnew, struct rsi, h);
139*4882a593Smuzhiyun 	struct rsi *item = container_of(citem, struct rsi, h);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	new->out_handle.data = NULL;
142*4882a593Smuzhiyun 	new->out_handle.len = 0;
143*4882a593Smuzhiyun 	new->out_token.data = NULL;
144*4882a593Smuzhiyun 	new->out_token.len = 0;
145*4882a593Smuzhiyun 	new->in_handle.len = item->in_handle.len;
146*4882a593Smuzhiyun 	item->in_handle.len = 0;
147*4882a593Smuzhiyun 	new->in_token.len = item->in_token.len;
148*4882a593Smuzhiyun 	item->in_token.len = 0;
149*4882a593Smuzhiyun 	new->in_handle.data = item->in_handle.data;
150*4882a593Smuzhiyun 	item->in_handle.data = NULL;
151*4882a593Smuzhiyun 	new->in_token.data = item->in_token.data;
152*4882a593Smuzhiyun 	item->in_token.data = NULL;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
update_rsi(struct cache_head * cnew,struct cache_head * citem)155*4882a593Smuzhiyun static void update_rsi(struct cache_head *cnew, struct cache_head *citem)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	struct rsi *new = container_of(cnew, struct rsi, h);
158*4882a593Smuzhiyun 	struct rsi *item = container_of(citem, struct rsi, h);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	BUG_ON(new->out_handle.data || new->out_token.data);
161*4882a593Smuzhiyun 	new->out_handle.len = item->out_handle.len;
162*4882a593Smuzhiyun 	item->out_handle.len = 0;
163*4882a593Smuzhiyun 	new->out_token.len = item->out_token.len;
164*4882a593Smuzhiyun 	item->out_token.len = 0;
165*4882a593Smuzhiyun 	new->out_handle.data = item->out_handle.data;
166*4882a593Smuzhiyun 	item->out_handle.data = NULL;
167*4882a593Smuzhiyun 	new->out_token.data = item->out_token.data;
168*4882a593Smuzhiyun 	item->out_token.data = NULL;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	new->major_status = item->major_status;
171*4882a593Smuzhiyun 	new->minor_status = item->minor_status;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
rsi_alloc(void)174*4882a593Smuzhiyun static struct cache_head *rsi_alloc(void)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL);
177*4882a593Smuzhiyun 	if (rsii)
178*4882a593Smuzhiyun 		return &rsii->h;
179*4882a593Smuzhiyun 	else
180*4882a593Smuzhiyun 		return NULL;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun 
rsi_upcall(struct cache_detail * cd,struct cache_head * h)183*4882a593Smuzhiyun static int rsi_upcall(struct cache_detail *cd, struct cache_head *h)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	return sunrpc_cache_pipe_upcall_timeout(cd, h);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
rsi_request(struct cache_detail * cd,struct cache_head * h,char ** bpp,int * blen)188*4882a593Smuzhiyun static void rsi_request(struct cache_detail *cd,
189*4882a593Smuzhiyun 		       struct cache_head *h,
190*4882a593Smuzhiyun 		       char **bpp, int *blen)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun 	struct rsi *rsii = container_of(h, struct rsi, h);
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len);
195*4882a593Smuzhiyun 	qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len);
196*4882a593Smuzhiyun 	(*bpp)[-1] = '\n';
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
rsi_parse(struct cache_detail * cd,char * mesg,int mlen)199*4882a593Smuzhiyun static int rsi_parse(struct cache_detail *cd,
200*4882a593Smuzhiyun 		    char *mesg, int mlen)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun 	/* context token expiry major minor context token */
203*4882a593Smuzhiyun 	char *buf = mesg;
204*4882a593Smuzhiyun 	char *ep;
205*4882a593Smuzhiyun 	int len;
206*4882a593Smuzhiyun 	struct rsi rsii, *rsip = NULL;
207*4882a593Smuzhiyun 	time64_t expiry;
208*4882a593Smuzhiyun 	int status = -EINVAL;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	memset(&rsii, 0, sizeof(rsii));
211*4882a593Smuzhiyun 	/* handle */
212*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
213*4882a593Smuzhiyun 	if (len < 0)
214*4882a593Smuzhiyun 		goto out;
215*4882a593Smuzhiyun 	status = -ENOMEM;
216*4882a593Smuzhiyun 	if (dup_to_netobj(&rsii.in_handle, buf, len))
217*4882a593Smuzhiyun 		goto out;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* token */
220*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
221*4882a593Smuzhiyun 	status = -EINVAL;
222*4882a593Smuzhiyun 	if (len < 0)
223*4882a593Smuzhiyun 		goto out;
224*4882a593Smuzhiyun 	status = -ENOMEM;
225*4882a593Smuzhiyun 	if (dup_to_netobj(&rsii.in_token, buf, len))
226*4882a593Smuzhiyun 		goto out;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	rsip = rsi_lookup(cd, &rsii);
229*4882a593Smuzhiyun 	if (!rsip)
230*4882a593Smuzhiyun 		goto out;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	rsii.h.flags = 0;
233*4882a593Smuzhiyun 	/* expiry */
234*4882a593Smuzhiyun 	expiry = get_expiry(&mesg);
235*4882a593Smuzhiyun 	status = -EINVAL;
236*4882a593Smuzhiyun 	if (expiry == 0)
237*4882a593Smuzhiyun 		goto out;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	/* major/minor */
240*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
241*4882a593Smuzhiyun 	if (len <= 0)
242*4882a593Smuzhiyun 		goto out;
243*4882a593Smuzhiyun 	rsii.major_status = simple_strtoul(buf, &ep, 10);
244*4882a593Smuzhiyun 	if (*ep)
245*4882a593Smuzhiyun 		goto out;
246*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
247*4882a593Smuzhiyun 	if (len <= 0)
248*4882a593Smuzhiyun 		goto out;
249*4882a593Smuzhiyun 	rsii.minor_status = simple_strtoul(buf, &ep, 10);
250*4882a593Smuzhiyun 	if (*ep)
251*4882a593Smuzhiyun 		goto out;
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	/* out_handle */
254*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
255*4882a593Smuzhiyun 	if (len < 0)
256*4882a593Smuzhiyun 		goto out;
257*4882a593Smuzhiyun 	status = -ENOMEM;
258*4882a593Smuzhiyun 	if (dup_to_netobj(&rsii.out_handle, buf, len))
259*4882a593Smuzhiyun 		goto out;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	/* out_token */
262*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
263*4882a593Smuzhiyun 	status = -EINVAL;
264*4882a593Smuzhiyun 	if (len < 0)
265*4882a593Smuzhiyun 		goto out;
266*4882a593Smuzhiyun 	status = -ENOMEM;
267*4882a593Smuzhiyun 	if (dup_to_netobj(&rsii.out_token, buf, len))
268*4882a593Smuzhiyun 		goto out;
269*4882a593Smuzhiyun 	rsii.h.expiry_time = expiry;
270*4882a593Smuzhiyun 	rsip = rsi_update(cd, &rsii, rsip);
271*4882a593Smuzhiyun 	status = 0;
272*4882a593Smuzhiyun out:
273*4882a593Smuzhiyun 	rsi_free(&rsii);
274*4882a593Smuzhiyun 	if (rsip)
275*4882a593Smuzhiyun 		cache_put(&rsip->h, cd);
276*4882a593Smuzhiyun 	else
277*4882a593Smuzhiyun 		status = -ENOMEM;
278*4882a593Smuzhiyun 	return status;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun static const struct cache_detail rsi_cache_template = {
282*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
283*4882a593Smuzhiyun 	.hash_size	= RSI_HASHMAX,
284*4882a593Smuzhiyun 	.name           = "auth.rpcsec.init",
285*4882a593Smuzhiyun 	.cache_put      = rsi_put,
286*4882a593Smuzhiyun 	.cache_upcall	= rsi_upcall,
287*4882a593Smuzhiyun 	.cache_request  = rsi_request,
288*4882a593Smuzhiyun 	.cache_parse    = rsi_parse,
289*4882a593Smuzhiyun 	.match		= rsi_match,
290*4882a593Smuzhiyun 	.init		= rsi_init,
291*4882a593Smuzhiyun 	.update		= update_rsi,
292*4882a593Smuzhiyun 	.alloc		= rsi_alloc,
293*4882a593Smuzhiyun };
294*4882a593Smuzhiyun 
rsi_lookup(struct cache_detail * cd,struct rsi * item)295*4882a593Smuzhiyun static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	struct cache_head *ch;
298*4882a593Smuzhiyun 	int hash = rsi_hash(item);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash);
301*4882a593Smuzhiyun 	if (ch)
302*4882a593Smuzhiyun 		return container_of(ch, struct rsi, h);
303*4882a593Smuzhiyun 	else
304*4882a593Smuzhiyun 		return NULL;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
rsi_update(struct cache_detail * cd,struct rsi * new,struct rsi * old)307*4882a593Smuzhiyun static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	struct cache_head *ch;
310*4882a593Smuzhiyun 	int hash = rsi_hash(new);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	ch = sunrpc_cache_update(cd, &new->h,
313*4882a593Smuzhiyun 				 &old->h, hash);
314*4882a593Smuzhiyun 	if (ch)
315*4882a593Smuzhiyun 		return container_of(ch, struct rsi, h);
316*4882a593Smuzhiyun 	else
317*4882a593Smuzhiyun 		return NULL;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun /*
322*4882a593Smuzhiyun  * The rpcsec_context cache is used to store a context that is
323*4882a593Smuzhiyun  * used in data exchange.
324*4882a593Smuzhiyun  * The key is a context handle. The content is:
325*4882a593Smuzhiyun  *  uid, gidlist, mechanism, service-set, mech-specific-data
326*4882a593Smuzhiyun  */
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun #define	RSC_HASHBITS	10
329*4882a593Smuzhiyun #define	RSC_HASHMAX	(1<<RSC_HASHBITS)
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun #define GSS_SEQ_WIN	128
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun struct gss_svc_seq_data {
334*4882a593Smuzhiyun 	/* highest seq number seen so far: */
335*4882a593Smuzhiyun 	u32			sd_max;
336*4882a593Smuzhiyun 	/* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of
337*4882a593Smuzhiyun 	 * sd_win is nonzero iff sequence number i has been seen already: */
338*4882a593Smuzhiyun 	unsigned long		sd_win[GSS_SEQ_WIN/BITS_PER_LONG];
339*4882a593Smuzhiyun 	spinlock_t		sd_lock;
340*4882a593Smuzhiyun };
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun struct rsc {
343*4882a593Smuzhiyun 	struct cache_head	h;
344*4882a593Smuzhiyun 	struct xdr_netobj	handle;
345*4882a593Smuzhiyun 	struct svc_cred		cred;
346*4882a593Smuzhiyun 	struct gss_svc_seq_data	seqdata;
347*4882a593Smuzhiyun 	struct gss_ctx		*mechctx;
348*4882a593Smuzhiyun 	struct rcu_head		rcu_head;
349*4882a593Smuzhiyun };
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
352*4882a593Smuzhiyun static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item);
353*4882a593Smuzhiyun 
rsc_free(struct rsc * rsci)354*4882a593Smuzhiyun static void rsc_free(struct rsc *rsci)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	kfree(rsci->handle.data);
357*4882a593Smuzhiyun 	if (rsci->mechctx)
358*4882a593Smuzhiyun 		gss_delete_sec_context(&rsci->mechctx);
359*4882a593Smuzhiyun 	free_svc_cred(&rsci->cred);
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
rsc_free_rcu(struct rcu_head * head)362*4882a593Smuzhiyun static void rsc_free_rcu(struct rcu_head *head)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	struct rsc *rsci = container_of(head, struct rsc, rcu_head);
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	kfree(rsci->handle.data);
367*4882a593Smuzhiyun 	kfree(rsci);
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
rsc_put(struct kref * ref)370*4882a593Smuzhiyun static void rsc_put(struct kref *ref)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct rsc *rsci = container_of(ref, struct rsc, h.ref);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	if (rsci->mechctx)
375*4882a593Smuzhiyun 		gss_delete_sec_context(&rsci->mechctx);
376*4882a593Smuzhiyun 	free_svc_cred(&rsci->cred);
377*4882a593Smuzhiyun 	call_rcu(&rsci->rcu_head, rsc_free_rcu);
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun static inline int
rsc_hash(struct rsc * rsci)381*4882a593Smuzhiyun rsc_hash(struct rsc *rsci)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun 	return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun static int
rsc_match(struct cache_head * a,struct cache_head * b)387*4882a593Smuzhiyun rsc_match(struct cache_head *a, struct cache_head *b)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	struct rsc *new = container_of(a, struct rsc, h);
390*4882a593Smuzhiyun 	struct rsc *tmp = container_of(b, struct rsc, h);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	return netobj_equal(&new->handle, &tmp->handle);
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun static void
rsc_init(struct cache_head * cnew,struct cache_head * ctmp)396*4882a593Smuzhiyun rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	struct rsc *new = container_of(cnew, struct rsc, h);
399*4882a593Smuzhiyun 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	new->handle.len = tmp->handle.len;
402*4882a593Smuzhiyun 	tmp->handle.len = 0;
403*4882a593Smuzhiyun 	new->handle.data = tmp->handle.data;
404*4882a593Smuzhiyun 	tmp->handle.data = NULL;
405*4882a593Smuzhiyun 	new->mechctx = NULL;
406*4882a593Smuzhiyun 	init_svc_cred(&new->cred);
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun static void
update_rsc(struct cache_head * cnew,struct cache_head * ctmp)410*4882a593Smuzhiyun update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun 	struct rsc *new = container_of(cnew, struct rsc, h);
413*4882a593Smuzhiyun 	struct rsc *tmp = container_of(ctmp, struct rsc, h);
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	new->mechctx = tmp->mechctx;
416*4882a593Smuzhiyun 	tmp->mechctx = NULL;
417*4882a593Smuzhiyun 	memset(&new->seqdata, 0, sizeof(new->seqdata));
418*4882a593Smuzhiyun 	spin_lock_init(&new->seqdata.sd_lock);
419*4882a593Smuzhiyun 	new->cred = tmp->cred;
420*4882a593Smuzhiyun 	init_svc_cred(&tmp->cred);
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun static struct cache_head *
rsc_alloc(void)424*4882a593Smuzhiyun rsc_alloc(void)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL);
427*4882a593Smuzhiyun 	if (rsci)
428*4882a593Smuzhiyun 		return &rsci->h;
429*4882a593Smuzhiyun 	else
430*4882a593Smuzhiyun 		return NULL;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun 
rsc_upcall(struct cache_detail * cd,struct cache_head * h)433*4882a593Smuzhiyun static int rsc_upcall(struct cache_detail *cd, struct cache_head *h)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	return -EINVAL;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun 
rsc_parse(struct cache_detail * cd,char * mesg,int mlen)438*4882a593Smuzhiyun static int rsc_parse(struct cache_detail *cd,
439*4882a593Smuzhiyun 		     char *mesg, int mlen)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun 	/* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */
442*4882a593Smuzhiyun 	char *buf = mesg;
443*4882a593Smuzhiyun 	int id;
444*4882a593Smuzhiyun 	int len, rv;
445*4882a593Smuzhiyun 	struct rsc rsci, *rscp = NULL;
446*4882a593Smuzhiyun 	time64_t expiry;
447*4882a593Smuzhiyun 	int status = -EINVAL;
448*4882a593Smuzhiyun 	struct gss_api_mech *gm = NULL;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	memset(&rsci, 0, sizeof(rsci));
451*4882a593Smuzhiyun 	/* context handle */
452*4882a593Smuzhiyun 	len = qword_get(&mesg, buf, mlen);
453*4882a593Smuzhiyun 	if (len < 0) goto out;
454*4882a593Smuzhiyun 	status = -ENOMEM;
455*4882a593Smuzhiyun 	if (dup_to_netobj(&rsci.handle, buf, len))
456*4882a593Smuzhiyun 		goto out;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	rsci.h.flags = 0;
459*4882a593Smuzhiyun 	/* expiry */
460*4882a593Smuzhiyun 	expiry = get_expiry(&mesg);
461*4882a593Smuzhiyun 	status = -EINVAL;
462*4882a593Smuzhiyun 	if (expiry == 0)
463*4882a593Smuzhiyun 		goto out;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	rscp = rsc_lookup(cd, &rsci);
466*4882a593Smuzhiyun 	if (!rscp)
467*4882a593Smuzhiyun 		goto out;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	/* uid, or NEGATIVE */
470*4882a593Smuzhiyun 	rv = get_int(&mesg, &id);
471*4882a593Smuzhiyun 	if (rv == -EINVAL)
472*4882a593Smuzhiyun 		goto out;
473*4882a593Smuzhiyun 	if (rv == -ENOENT)
474*4882a593Smuzhiyun 		set_bit(CACHE_NEGATIVE, &rsci.h.flags);
475*4882a593Smuzhiyun 	else {
476*4882a593Smuzhiyun 		int N, i;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 		/*
479*4882a593Smuzhiyun 		 * NOTE: we skip uid_valid()/gid_valid() checks here:
480*4882a593Smuzhiyun 		 * instead, * -1 id's are later mapped to the
481*4882a593Smuzhiyun 		 * (export-specific) anonymous id by nfsd_setuser.
482*4882a593Smuzhiyun 		 *
483*4882a593Smuzhiyun 		 * (But supplementary gid's get no such special
484*4882a593Smuzhiyun 		 * treatment so are checked for validity here.)
485*4882a593Smuzhiyun 		 */
486*4882a593Smuzhiyun 		/* uid */
487*4882a593Smuzhiyun 		rsci.cred.cr_uid = make_kuid(current_user_ns(), id);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 		/* gid */
490*4882a593Smuzhiyun 		if (get_int(&mesg, &id))
491*4882a593Smuzhiyun 			goto out;
492*4882a593Smuzhiyun 		rsci.cred.cr_gid = make_kgid(current_user_ns(), id);
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 		/* number of additional gid's */
495*4882a593Smuzhiyun 		if (get_int(&mesg, &N))
496*4882a593Smuzhiyun 			goto out;
497*4882a593Smuzhiyun 		if (N < 0 || N > NGROUPS_MAX)
498*4882a593Smuzhiyun 			goto out;
499*4882a593Smuzhiyun 		status = -ENOMEM;
500*4882a593Smuzhiyun 		rsci.cred.cr_group_info = groups_alloc(N);
501*4882a593Smuzhiyun 		if (rsci.cred.cr_group_info == NULL)
502*4882a593Smuzhiyun 			goto out;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 		/* gid's */
505*4882a593Smuzhiyun 		status = -EINVAL;
506*4882a593Smuzhiyun 		for (i=0; i<N; i++) {
507*4882a593Smuzhiyun 			kgid_t kgid;
508*4882a593Smuzhiyun 			if (get_int(&mesg, &id))
509*4882a593Smuzhiyun 				goto out;
510*4882a593Smuzhiyun 			kgid = make_kgid(current_user_ns(), id);
511*4882a593Smuzhiyun 			if (!gid_valid(kgid))
512*4882a593Smuzhiyun 				goto out;
513*4882a593Smuzhiyun 			rsci.cred.cr_group_info->gid[i] = kgid;
514*4882a593Smuzhiyun 		}
515*4882a593Smuzhiyun 		groups_sort(rsci.cred.cr_group_info);
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 		/* mech name */
518*4882a593Smuzhiyun 		len = qword_get(&mesg, buf, mlen);
519*4882a593Smuzhiyun 		if (len < 0)
520*4882a593Smuzhiyun 			goto out;
521*4882a593Smuzhiyun 		gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
522*4882a593Smuzhiyun 		status = -EOPNOTSUPP;
523*4882a593Smuzhiyun 		if (!gm)
524*4882a593Smuzhiyun 			goto out;
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 		status = -EINVAL;
527*4882a593Smuzhiyun 		/* mech-specific data: */
528*4882a593Smuzhiyun 		len = qword_get(&mesg, buf, mlen);
529*4882a593Smuzhiyun 		if (len < 0)
530*4882a593Smuzhiyun 			goto out;
531*4882a593Smuzhiyun 		status = gss_import_sec_context(buf, len, gm, &rsci.mechctx,
532*4882a593Smuzhiyun 						NULL, GFP_KERNEL);
533*4882a593Smuzhiyun 		if (status)
534*4882a593Smuzhiyun 			goto out;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 		/* get client name */
537*4882a593Smuzhiyun 		len = qword_get(&mesg, buf, mlen);
538*4882a593Smuzhiyun 		if (len > 0) {
539*4882a593Smuzhiyun 			rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL);
540*4882a593Smuzhiyun 			if (!rsci.cred.cr_principal) {
541*4882a593Smuzhiyun 				status = -ENOMEM;
542*4882a593Smuzhiyun 				goto out;
543*4882a593Smuzhiyun 			}
544*4882a593Smuzhiyun 		}
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 	rsci.h.expiry_time = expiry;
548*4882a593Smuzhiyun 	rscp = rsc_update(cd, &rsci, rscp);
549*4882a593Smuzhiyun 	status = 0;
550*4882a593Smuzhiyun out:
551*4882a593Smuzhiyun 	rsc_free(&rsci);
552*4882a593Smuzhiyun 	if (rscp)
553*4882a593Smuzhiyun 		cache_put(&rscp->h, cd);
554*4882a593Smuzhiyun 	else
555*4882a593Smuzhiyun 		status = -ENOMEM;
556*4882a593Smuzhiyun 	return status;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun static const struct cache_detail rsc_cache_template = {
560*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
561*4882a593Smuzhiyun 	.hash_size	= RSC_HASHMAX,
562*4882a593Smuzhiyun 	.name		= "auth.rpcsec.context",
563*4882a593Smuzhiyun 	.cache_put	= rsc_put,
564*4882a593Smuzhiyun 	.cache_upcall	= rsc_upcall,
565*4882a593Smuzhiyun 	.cache_parse	= rsc_parse,
566*4882a593Smuzhiyun 	.match		= rsc_match,
567*4882a593Smuzhiyun 	.init		= rsc_init,
568*4882a593Smuzhiyun 	.update		= update_rsc,
569*4882a593Smuzhiyun 	.alloc		= rsc_alloc,
570*4882a593Smuzhiyun };
571*4882a593Smuzhiyun 
rsc_lookup(struct cache_detail * cd,struct rsc * item)572*4882a593Smuzhiyun static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item)
573*4882a593Smuzhiyun {
574*4882a593Smuzhiyun 	struct cache_head *ch;
575*4882a593Smuzhiyun 	int hash = rsc_hash(item);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash);
578*4882a593Smuzhiyun 	if (ch)
579*4882a593Smuzhiyun 		return container_of(ch, struct rsc, h);
580*4882a593Smuzhiyun 	else
581*4882a593Smuzhiyun 		return NULL;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun 
rsc_update(struct cache_detail * cd,struct rsc * new,struct rsc * old)584*4882a593Smuzhiyun static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun 	struct cache_head *ch;
587*4882a593Smuzhiyun 	int hash = rsc_hash(new);
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	ch = sunrpc_cache_update(cd, &new->h,
590*4882a593Smuzhiyun 				 &old->h, hash);
591*4882a593Smuzhiyun 	if (ch)
592*4882a593Smuzhiyun 		return container_of(ch, struct rsc, h);
593*4882a593Smuzhiyun 	else
594*4882a593Smuzhiyun 		return NULL;
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun static struct rsc *
gss_svc_searchbyctx(struct cache_detail * cd,struct xdr_netobj * handle)599*4882a593Smuzhiyun gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	struct rsc rsci;
602*4882a593Smuzhiyun 	struct rsc *found;
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	memset(&rsci, 0, sizeof(rsci));
605*4882a593Smuzhiyun 	if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
606*4882a593Smuzhiyun 		return NULL;
607*4882a593Smuzhiyun 	found = rsc_lookup(cd, &rsci);
608*4882a593Smuzhiyun 	rsc_free(&rsci);
609*4882a593Smuzhiyun 	if (!found)
610*4882a593Smuzhiyun 		return NULL;
611*4882a593Smuzhiyun 	if (cache_check(cd, &found->h, NULL))
612*4882a593Smuzhiyun 		return NULL;
613*4882a593Smuzhiyun 	return found;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun /**
617*4882a593Smuzhiyun  * gss_check_seq_num - GSS sequence number window check
618*4882a593Smuzhiyun  * @rqstp: RPC Call to use when reporting errors
619*4882a593Smuzhiyun  * @rsci: cached GSS context state (updated on return)
620*4882a593Smuzhiyun  * @seq_num: sequence number to check
621*4882a593Smuzhiyun  *
622*4882a593Smuzhiyun  * Implements sequence number algorithm as specified in
623*4882a593Smuzhiyun  * RFC 2203, Section 5.3.3.1. "Context Management".
624*4882a593Smuzhiyun  *
625*4882a593Smuzhiyun  * Return values:
626*4882a593Smuzhiyun  *   %true: @rqstp's GSS sequence number is inside the window
627*4882a593Smuzhiyun  *   %false: @rqstp's GSS sequence number is outside the window
628*4882a593Smuzhiyun  */
gss_check_seq_num(const struct svc_rqst * rqstp,struct rsc * rsci,u32 seq_num)629*4882a593Smuzhiyun static bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci,
630*4882a593Smuzhiyun 			      u32 seq_num)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	struct gss_svc_seq_data *sd = &rsci->seqdata;
633*4882a593Smuzhiyun 	bool result = false;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	spin_lock(&sd->sd_lock);
636*4882a593Smuzhiyun 	if (seq_num > sd->sd_max) {
637*4882a593Smuzhiyun 		if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
638*4882a593Smuzhiyun 			memset(sd->sd_win, 0, sizeof(sd->sd_win));
639*4882a593Smuzhiyun 			sd->sd_max = seq_num;
640*4882a593Smuzhiyun 		} else while (sd->sd_max < seq_num) {
641*4882a593Smuzhiyun 			sd->sd_max++;
642*4882a593Smuzhiyun 			__clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win);
643*4882a593Smuzhiyun 		}
644*4882a593Smuzhiyun 		__set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
645*4882a593Smuzhiyun 		goto ok;
646*4882a593Smuzhiyun 	} else if (seq_num + GSS_SEQ_WIN <= sd->sd_max) {
647*4882a593Smuzhiyun 		goto toolow;
648*4882a593Smuzhiyun 	}
649*4882a593Smuzhiyun 	if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
650*4882a593Smuzhiyun 		goto alreadyseen;
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun ok:
653*4882a593Smuzhiyun 	result = true;
654*4882a593Smuzhiyun out:
655*4882a593Smuzhiyun 	spin_unlock(&sd->sd_lock);
656*4882a593Smuzhiyun 	return result;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun toolow:
659*4882a593Smuzhiyun 	trace_rpcgss_svc_seqno_low(rqstp, seq_num,
660*4882a593Smuzhiyun 				   sd->sd_max - GSS_SEQ_WIN,
661*4882a593Smuzhiyun 				   sd->sd_max);
662*4882a593Smuzhiyun 	goto out;
663*4882a593Smuzhiyun alreadyseen:
664*4882a593Smuzhiyun 	trace_rpcgss_svc_seqno_seen(rqstp, seq_num);
665*4882a593Smuzhiyun 	goto out;
666*4882a593Smuzhiyun }
667*4882a593Smuzhiyun 
round_up_to_quad(u32 i)668*4882a593Smuzhiyun static inline u32 round_up_to_quad(u32 i)
669*4882a593Smuzhiyun {
670*4882a593Smuzhiyun 	return (i + 3 ) & ~3;
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun static inline int
svc_safe_getnetobj(struct kvec * argv,struct xdr_netobj * o)674*4882a593Smuzhiyun svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun 	int l;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	if (argv->iov_len < 4)
679*4882a593Smuzhiyun 		return -1;
680*4882a593Smuzhiyun 	o->len = svc_getnl(argv);
681*4882a593Smuzhiyun 	l = round_up_to_quad(o->len);
682*4882a593Smuzhiyun 	if (argv->iov_len < l)
683*4882a593Smuzhiyun 		return -1;
684*4882a593Smuzhiyun 	o->data = argv->iov_base;
685*4882a593Smuzhiyun 	argv->iov_base += l;
686*4882a593Smuzhiyun 	argv->iov_len -= l;
687*4882a593Smuzhiyun 	return 0;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun static inline int
svc_safe_putnetobj(struct kvec * resv,struct xdr_netobj * o)691*4882a593Smuzhiyun svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
692*4882a593Smuzhiyun {
693*4882a593Smuzhiyun 	u8 *p;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	if (resv->iov_len + 4 > PAGE_SIZE)
696*4882a593Smuzhiyun 		return -1;
697*4882a593Smuzhiyun 	svc_putnl(resv, o->len);
698*4882a593Smuzhiyun 	p = resv->iov_base + resv->iov_len;
699*4882a593Smuzhiyun 	resv->iov_len += round_up_to_quad(o->len);
700*4882a593Smuzhiyun 	if (resv->iov_len > PAGE_SIZE)
701*4882a593Smuzhiyun 		return -1;
702*4882a593Smuzhiyun 	memcpy(p, o->data, o->len);
703*4882a593Smuzhiyun 	memset(p + o->len, 0, round_up_to_quad(o->len) - o->len);
704*4882a593Smuzhiyun 	return 0;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun /*
708*4882a593Smuzhiyun  * Verify the checksum on the header and return SVC_OK on success.
709*4882a593Smuzhiyun  * Otherwise, return SVC_DROP (in the case of a bad sequence number)
710*4882a593Smuzhiyun  * or return SVC_DENIED and indicate error in authp.
711*4882a593Smuzhiyun  */
712*4882a593Smuzhiyun static int
gss_verify_header(struct svc_rqst * rqstp,struct rsc * rsci,__be32 * rpcstart,struct rpc_gss_wire_cred * gc,__be32 * authp)713*4882a593Smuzhiyun gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
714*4882a593Smuzhiyun 		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp)
715*4882a593Smuzhiyun {
716*4882a593Smuzhiyun 	struct gss_ctx		*ctx_id = rsci->mechctx;
717*4882a593Smuzhiyun 	struct xdr_buf		rpchdr;
718*4882a593Smuzhiyun 	struct xdr_netobj	checksum;
719*4882a593Smuzhiyun 	u32			flavor = 0;
720*4882a593Smuzhiyun 	struct kvec		*argv = &rqstp->rq_arg.head[0];
721*4882a593Smuzhiyun 	struct kvec		iov;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	/* data to compute the checksum over: */
724*4882a593Smuzhiyun 	iov.iov_base = rpcstart;
725*4882a593Smuzhiyun 	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
726*4882a593Smuzhiyun 	xdr_buf_from_iov(&iov, &rpchdr);
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	*authp = rpc_autherr_badverf;
729*4882a593Smuzhiyun 	if (argv->iov_len < 4)
730*4882a593Smuzhiyun 		return SVC_DENIED;
731*4882a593Smuzhiyun 	flavor = svc_getnl(argv);
732*4882a593Smuzhiyun 	if (flavor != RPC_AUTH_GSS)
733*4882a593Smuzhiyun 		return SVC_DENIED;
734*4882a593Smuzhiyun 	if (svc_safe_getnetobj(argv, &checksum))
735*4882a593Smuzhiyun 		return SVC_DENIED;
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 	if (rqstp->rq_deferred) /* skip verification of revisited request */
738*4882a593Smuzhiyun 		return SVC_OK;
739*4882a593Smuzhiyun 	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
740*4882a593Smuzhiyun 		*authp = rpcsec_gsserr_credproblem;
741*4882a593Smuzhiyun 		return SVC_DENIED;
742*4882a593Smuzhiyun 	}
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	if (gc->gc_seq > MAXSEQ) {
745*4882a593Smuzhiyun 		trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq);
746*4882a593Smuzhiyun 		*authp = rpcsec_gsserr_ctxproblem;
747*4882a593Smuzhiyun 		return SVC_DENIED;
748*4882a593Smuzhiyun 	}
749*4882a593Smuzhiyun 	if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq))
750*4882a593Smuzhiyun 		return SVC_DROP;
751*4882a593Smuzhiyun 	return SVC_OK;
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun static int
gss_write_null_verf(struct svc_rqst * rqstp)755*4882a593Smuzhiyun gss_write_null_verf(struct svc_rqst *rqstp)
756*4882a593Smuzhiyun {
757*4882a593Smuzhiyun 	__be32     *p;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL);
760*4882a593Smuzhiyun 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
761*4882a593Smuzhiyun 	/* don't really need to check if head->iov_len > PAGE_SIZE ... */
762*4882a593Smuzhiyun 	*p++ = 0;
763*4882a593Smuzhiyun 	if (!xdr_ressize_check(rqstp, p))
764*4882a593Smuzhiyun 		return -1;
765*4882a593Smuzhiyun 	return 0;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun static int
gss_write_verf(struct svc_rqst * rqstp,struct gss_ctx * ctx_id,u32 seq)769*4882a593Smuzhiyun gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun 	__be32			*xdr_seq;
772*4882a593Smuzhiyun 	u32			maj_stat;
773*4882a593Smuzhiyun 	struct xdr_buf		verf_data;
774*4882a593Smuzhiyun 	struct xdr_netobj	mic;
775*4882a593Smuzhiyun 	__be32			*p;
776*4882a593Smuzhiyun 	struct kvec		iov;
777*4882a593Smuzhiyun 	int err = -1;
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
780*4882a593Smuzhiyun 	xdr_seq = kmalloc(4, GFP_KERNEL);
781*4882a593Smuzhiyun 	if (!xdr_seq)
782*4882a593Smuzhiyun 		return -1;
783*4882a593Smuzhiyun 	*xdr_seq = htonl(seq);
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	iov.iov_base = xdr_seq;
786*4882a593Smuzhiyun 	iov.iov_len = 4;
787*4882a593Smuzhiyun 	xdr_buf_from_iov(&iov, &verf_data);
788*4882a593Smuzhiyun 	p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
789*4882a593Smuzhiyun 	mic.data = (u8 *)(p + 1);
790*4882a593Smuzhiyun 	maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
791*4882a593Smuzhiyun 	if (maj_stat != GSS_S_COMPLETE)
792*4882a593Smuzhiyun 		goto out;
793*4882a593Smuzhiyun 	*p++ = htonl(mic.len);
794*4882a593Smuzhiyun 	memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
795*4882a593Smuzhiyun 	p += XDR_QUADLEN(mic.len);
796*4882a593Smuzhiyun 	if (!xdr_ressize_check(rqstp, p))
797*4882a593Smuzhiyun 		goto out;
798*4882a593Smuzhiyun 	err = 0;
799*4882a593Smuzhiyun out:
800*4882a593Smuzhiyun 	kfree(xdr_seq);
801*4882a593Smuzhiyun 	return err;
802*4882a593Smuzhiyun }
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun struct gss_domain {
805*4882a593Smuzhiyun 	struct auth_domain	h;
806*4882a593Smuzhiyun 	u32			pseudoflavor;
807*4882a593Smuzhiyun };
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun static struct auth_domain *
find_gss_auth_domain(struct gss_ctx * ctx,u32 svc)810*4882a593Smuzhiyun find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
811*4882a593Smuzhiyun {
812*4882a593Smuzhiyun 	char *name;
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	name = gss_service_to_auth_domain_name(ctx->mech_type, svc);
815*4882a593Smuzhiyun 	if (!name)
816*4882a593Smuzhiyun 		return NULL;
817*4882a593Smuzhiyun 	return auth_domain_find(name);
818*4882a593Smuzhiyun }
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun static struct auth_ops svcauthops_gss;
821*4882a593Smuzhiyun 
svcauth_gss_flavor(struct auth_domain * dom)822*4882a593Smuzhiyun u32 svcauth_gss_flavor(struct auth_domain *dom)
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	return gd->pseudoflavor;
827*4882a593Smuzhiyun }
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun struct auth_domain *
svcauth_gss_register_pseudoflavor(u32 pseudoflavor,char * name)832*4882a593Smuzhiyun svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
833*4882a593Smuzhiyun {
834*4882a593Smuzhiyun 	struct gss_domain	*new;
835*4882a593Smuzhiyun 	struct auth_domain	*test;
836*4882a593Smuzhiyun 	int			stat = -ENOMEM;
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	new = kmalloc(sizeof(*new), GFP_KERNEL);
839*4882a593Smuzhiyun 	if (!new)
840*4882a593Smuzhiyun 		goto out;
841*4882a593Smuzhiyun 	kref_init(&new->h.ref);
842*4882a593Smuzhiyun 	new->h.name = kstrdup(name, GFP_KERNEL);
843*4882a593Smuzhiyun 	if (!new->h.name)
844*4882a593Smuzhiyun 		goto out_free_dom;
845*4882a593Smuzhiyun 	new->h.flavour = &svcauthops_gss;
846*4882a593Smuzhiyun 	new->pseudoflavor = pseudoflavor;
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	test = auth_domain_lookup(name, &new->h);
849*4882a593Smuzhiyun 	if (test != &new->h) {
850*4882a593Smuzhiyun 		pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n",
851*4882a593Smuzhiyun 			name);
852*4882a593Smuzhiyun 		stat = -EADDRINUSE;
853*4882a593Smuzhiyun 		auth_domain_put(test);
854*4882a593Smuzhiyun 		goto out_free_name;
855*4882a593Smuzhiyun 	}
856*4882a593Smuzhiyun 	return test;
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun out_free_name:
859*4882a593Smuzhiyun 	kfree(new->h.name);
860*4882a593Smuzhiyun out_free_dom:
861*4882a593Smuzhiyun 	kfree(new);
862*4882a593Smuzhiyun out:
863*4882a593Smuzhiyun 	return ERR_PTR(stat);
864*4882a593Smuzhiyun }
865*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun static inline int
read_u32_from_xdr_buf(struct xdr_buf * buf,int base,u32 * obj)868*4882a593Smuzhiyun read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
869*4882a593Smuzhiyun {
870*4882a593Smuzhiyun 	__be32  raw;
871*4882a593Smuzhiyun 	int     status;
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun 	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
874*4882a593Smuzhiyun 	if (status)
875*4882a593Smuzhiyun 		return status;
876*4882a593Smuzhiyun 	*obj = ntohl(raw);
877*4882a593Smuzhiyun 	return 0;
878*4882a593Smuzhiyun }
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun /* It would be nice if this bit of code could be shared with the client.
881*4882a593Smuzhiyun  * Obstacles:
882*4882a593Smuzhiyun  *	The client shouldn't malloc(), would have to pass in own memory.
883*4882a593Smuzhiyun  *	The server uses base of head iovec as read pointer, while the
884*4882a593Smuzhiyun  *	client uses separate pointer. */
885*4882a593Smuzhiyun static int
unwrap_integ_data(struct svc_rqst * rqstp,struct xdr_buf * buf,u32 seq,struct gss_ctx * ctx)886*4882a593Smuzhiyun unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
887*4882a593Smuzhiyun {
888*4882a593Smuzhiyun 	u32 integ_len, rseqno, maj_stat;
889*4882a593Smuzhiyun 	int stat = -EINVAL;
890*4882a593Smuzhiyun 	struct xdr_netobj mic;
891*4882a593Smuzhiyun 	struct xdr_buf integ_buf;
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	mic.data = NULL;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	/* NFS READ normally uses splice to send data in-place. However
896*4882a593Smuzhiyun 	 * the data in cache can change after the reply's MIC is computed
897*4882a593Smuzhiyun 	 * but before the RPC reply is sent. To prevent the client from
898*4882a593Smuzhiyun 	 * rejecting the server-computed MIC in this somewhat rare case,
899*4882a593Smuzhiyun 	 * do not use splice with the GSS integrity service.
900*4882a593Smuzhiyun 	 */
901*4882a593Smuzhiyun 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	/* Did we already verify the signature on the original pass through? */
904*4882a593Smuzhiyun 	if (rqstp->rq_deferred)
905*4882a593Smuzhiyun 		return 0;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 	integ_len = svc_getnl(&buf->head[0]);
908*4882a593Smuzhiyun 	if (integ_len & 3)
909*4882a593Smuzhiyun 		goto unwrap_failed;
910*4882a593Smuzhiyun 	if (integ_len > buf->len)
911*4882a593Smuzhiyun 		goto unwrap_failed;
912*4882a593Smuzhiyun 	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
913*4882a593Smuzhiyun 		goto unwrap_failed;
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 	/* copy out mic... */
916*4882a593Smuzhiyun 	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
917*4882a593Smuzhiyun 		goto unwrap_failed;
918*4882a593Smuzhiyun 	if (mic.len > RPC_MAX_AUTH_SIZE)
919*4882a593Smuzhiyun 		goto unwrap_failed;
920*4882a593Smuzhiyun 	mic.data = kmalloc(mic.len, GFP_KERNEL);
921*4882a593Smuzhiyun 	if (!mic.data)
922*4882a593Smuzhiyun 		goto unwrap_failed;
923*4882a593Smuzhiyun 	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
924*4882a593Smuzhiyun 		goto unwrap_failed;
925*4882a593Smuzhiyun 	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
926*4882a593Smuzhiyun 	if (maj_stat != GSS_S_COMPLETE)
927*4882a593Smuzhiyun 		goto bad_mic;
928*4882a593Smuzhiyun 	rseqno = svc_getnl(&buf->head[0]);
929*4882a593Smuzhiyun 	if (rseqno != seq)
930*4882a593Smuzhiyun 		goto bad_seqno;
931*4882a593Smuzhiyun 	/* trim off the mic and padding at the end before returning */
932*4882a593Smuzhiyun 	xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
933*4882a593Smuzhiyun 	stat = 0;
934*4882a593Smuzhiyun out:
935*4882a593Smuzhiyun 	kfree(mic.data);
936*4882a593Smuzhiyun 	return stat;
937*4882a593Smuzhiyun 
938*4882a593Smuzhiyun unwrap_failed:
939*4882a593Smuzhiyun 	trace_rpcgss_svc_unwrap_failed(rqstp);
940*4882a593Smuzhiyun 	goto out;
941*4882a593Smuzhiyun bad_seqno:
942*4882a593Smuzhiyun 	trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
943*4882a593Smuzhiyun 	goto out;
944*4882a593Smuzhiyun bad_mic:
945*4882a593Smuzhiyun 	trace_rpcgss_svc_mic(rqstp, maj_stat);
946*4882a593Smuzhiyun 	goto out;
947*4882a593Smuzhiyun }
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun static inline int
total_buf_len(struct xdr_buf * buf)950*4882a593Smuzhiyun total_buf_len(struct xdr_buf *buf)
951*4882a593Smuzhiyun {
952*4882a593Smuzhiyun 	return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
953*4882a593Smuzhiyun }
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun static void
fix_priv_head(struct xdr_buf * buf,int pad)956*4882a593Smuzhiyun fix_priv_head(struct xdr_buf *buf, int pad)
957*4882a593Smuzhiyun {
958*4882a593Smuzhiyun 	if (buf->page_len == 0) {
959*4882a593Smuzhiyun 		/* We need to adjust head and buf->len in tandem in this
960*4882a593Smuzhiyun 		 * case to make svc_defer() work--it finds the original
961*4882a593Smuzhiyun 		 * buffer start using buf->len - buf->head[0].iov_len. */
962*4882a593Smuzhiyun 		buf->head[0].iov_len -= pad;
963*4882a593Smuzhiyun 	}
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun static int
unwrap_priv_data(struct svc_rqst * rqstp,struct xdr_buf * buf,u32 seq,struct gss_ctx * ctx)967*4882a593Smuzhiyun unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun 	u32 priv_len, maj_stat;
970*4882a593Smuzhiyun 	int pad, remaining_len, offset;
971*4882a593Smuzhiyun 	u32 rseqno;
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	priv_len = svc_getnl(&buf->head[0]);
976*4882a593Smuzhiyun 	if (rqstp->rq_deferred) {
977*4882a593Smuzhiyun 		/* Already decrypted last time through! The sequence number
978*4882a593Smuzhiyun 		 * check at out_seq is unnecessary but harmless: */
979*4882a593Smuzhiyun 		goto out_seq;
980*4882a593Smuzhiyun 	}
981*4882a593Smuzhiyun 	/* buf->len is the number of bytes from the original start of the
982*4882a593Smuzhiyun 	 * request to the end, where head[0].iov_len is just the bytes
983*4882a593Smuzhiyun 	 * not yet read from the head, so these two values are different: */
984*4882a593Smuzhiyun 	remaining_len = total_buf_len(buf);
985*4882a593Smuzhiyun 	if (priv_len > remaining_len)
986*4882a593Smuzhiyun 		goto unwrap_failed;
987*4882a593Smuzhiyun 	pad = remaining_len - priv_len;
988*4882a593Smuzhiyun 	buf->len -= pad;
989*4882a593Smuzhiyun 	fix_priv_head(buf, pad);
990*4882a593Smuzhiyun 
991*4882a593Smuzhiyun 	maj_stat = gss_unwrap(ctx, 0, priv_len, buf);
992*4882a593Smuzhiyun 	pad = priv_len - buf->len;
993*4882a593Smuzhiyun 	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
994*4882a593Smuzhiyun 	 * In the krb5p case, at least, the data ends up offset, so we need to
995*4882a593Smuzhiyun 	 * move it around. */
996*4882a593Smuzhiyun 	/* XXX: This is very inefficient.  It would be better to either do
997*4882a593Smuzhiyun 	 * this while we encrypt, or maybe in the receive code, if we can peak
998*4882a593Smuzhiyun 	 * ahead and work out the service and mechanism there. */
999*4882a593Smuzhiyun 	offset = xdr_pad_size(buf->head[0].iov_len);
1000*4882a593Smuzhiyun 	if (offset) {
1001*4882a593Smuzhiyun 		buf->buflen = RPCSVC_MAXPAYLOAD;
1002*4882a593Smuzhiyun 		xdr_shift_buf(buf, offset);
1003*4882a593Smuzhiyun 		fix_priv_head(buf, pad);
1004*4882a593Smuzhiyun 	}
1005*4882a593Smuzhiyun 	if (maj_stat != GSS_S_COMPLETE)
1006*4882a593Smuzhiyun 		goto bad_unwrap;
1007*4882a593Smuzhiyun out_seq:
1008*4882a593Smuzhiyun 	rseqno = svc_getnl(&buf->head[0]);
1009*4882a593Smuzhiyun 	if (rseqno != seq)
1010*4882a593Smuzhiyun 		goto bad_seqno;
1011*4882a593Smuzhiyun 	return 0;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun unwrap_failed:
1014*4882a593Smuzhiyun 	trace_rpcgss_svc_unwrap_failed(rqstp);
1015*4882a593Smuzhiyun 	return -EINVAL;
1016*4882a593Smuzhiyun bad_seqno:
1017*4882a593Smuzhiyun 	trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
1018*4882a593Smuzhiyun 	return -EINVAL;
1019*4882a593Smuzhiyun bad_unwrap:
1020*4882a593Smuzhiyun 	trace_rpcgss_svc_unwrap(rqstp, maj_stat);
1021*4882a593Smuzhiyun 	return -EINVAL;
1022*4882a593Smuzhiyun }
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun struct gss_svc_data {
1025*4882a593Smuzhiyun 	/* decoded gss client cred: */
1026*4882a593Smuzhiyun 	struct rpc_gss_wire_cred	clcred;
1027*4882a593Smuzhiyun 	/* save a pointer to the beginning of the encoded verifier,
1028*4882a593Smuzhiyun 	 * for use in encryption/checksumming in svcauth_gss_release: */
1029*4882a593Smuzhiyun 	__be32				*verf_start;
1030*4882a593Smuzhiyun 	struct rsc			*rsci;
1031*4882a593Smuzhiyun };
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun static int
svcauth_gss_set_client(struct svc_rqst * rqstp)1034*4882a593Smuzhiyun svcauth_gss_set_client(struct svc_rqst *rqstp)
1035*4882a593Smuzhiyun {
1036*4882a593Smuzhiyun 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
1037*4882a593Smuzhiyun 	struct rsc *rsci = svcdata->rsci;
1038*4882a593Smuzhiyun 	struct rpc_gss_wire_cred *gc = &svcdata->clcred;
1039*4882a593Smuzhiyun 	int stat;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	/*
1042*4882a593Smuzhiyun 	 * A gss export can be specified either by:
1043*4882a593Smuzhiyun 	 * 	export	*(sec=krb5,rw)
1044*4882a593Smuzhiyun 	 * or by
1045*4882a593Smuzhiyun 	 * 	export gss/krb5(rw)
1046*4882a593Smuzhiyun 	 * The latter is deprecated; but for backwards compatibility reasons
1047*4882a593Smuzhiyun 	 * the nfsd code will still fall back on trying it if the former
1048*4882a593Smuzhiyun 	 * doesn't work; so we try to make both available to nfsd, below.
1049*4882a593Smuzhiyun 	 */
1050*4882a593Smuzhiyun 	rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc);
1051*4882a593Smuzhiyun 	if (rqstp->rq_gssclient == NULL)
1052*4882a593Smuzhiyun 		return SVC_DENIED;
1053*4882a593Smuzhiyun 	stat = svcauth_unix_set_client(rqstp);
1054*4882a593Smuzhiyun 	if (stat == SVC_DROP || stat == SVC_CLOSE)
1055*4882a593Smuzhiyun 		return stat;
1056*4882a593Smuzhiyun 	return SVC_OK;
1057*4882a593Smuzhiyun }
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun static inline int
gss_write_init_verf(struct cache_detail * cd,struct svc_rqst * rqstp,struct xdr_netobj * out_handle,int * major_status)1060*4882a593Smuzhiyun gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
1061*4882a593Smuzhiyun 		struct xdr_netobj *out_handle, int *major_status)
1062*4882a593Smuzhiyun {
1063*4882a593Smuzhiyun 	struct rsc *rsci;
1064*4882a593Smuzhiyun 	int        rc;
1065*4882a593Smuzhiyun 
1066*4882a593Smuzhiyun 	if (*major_status != GSS_S_COMPLETE)
1067*4882a593Smuzhiyun 		return gss_write_null_verf(rqstp);
1068*4882a593Smuzhiyun 	rsci = gss_svc_searchbyctx(cd, out_handle);
1069*4882a593Smuzhiyun 	if (rsci == NULL) {
1070*4882a593Smuzhiyun 		*major_status = GSS_S_NO_CONTEXT;
1071*4882a593Smuzhiyun 		return gss_write_null_verf(rqstp);
1072*4882a593Smuzhiyun 	}
1073*4882a593Smuzhiyun 	rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
1074*4882a593Smuzhiyun 	cache_put(&rsci->h, cd);
1075*4882a593Smuzhiyun 	return rc;
1076*4882a593Smuzhiyun }
1077*4882a593Smuzhiyun 
1078*4882a593Smuzhiyun static inline int
gss_read_common_verf(struct rpc_gss_wire_cred * gc,struct kvec * argv,__be32 * authp,struct xdr_netobj * in_handle)1079*4882a593Smuzhiyun gss_read_common_verf(struct rpc_gss_wire_cred *gc,
1080*4882a593Smuzhiyun 		     struct kvec *argv, __be32 *authp,
1081*4882a593Smuzhiyun 		     struct xdr_netobj *in_handle)
1082*4882a593Smuzhiyun {
1083*4882a593Smuzhiyun 	/* Read the verifier; should be NULL: */
1084*4882a593Smuzhiyun 	*authp = rpc_autherr_badverf;
1085*4882a593Smuzhiyun 	if (argv->iov_len < 2 * 4)
1086*4882a593Smuzhiyun 		return SVC_DENIED;
1087*4882a593Smuzhiyun 	if (svc_getnl(argv) != RPC_AUTH_NULL)
1088*4882a593Smuzhiyun 		return SVC_DENIED;
1089*4882a593Smuzhiyun 	if (svc_getnl(argv) != 0)
1090*4882a593Smuzhiyun 		return SVC_DENIED;
1091*4882a593Smuzhiyun 	/* Martial context handle and token for upcall: */
1092*4882a593Smuzhiyun 	*authp = rpc_autherr_badcred;
1093*4882a593Smuzhiyun 	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
1094*4882a593Smuzhiyun 		return SVC_DENIED;
1095*4882a593Smuzhiyun 	if (dup_netobj(in_handle, &gc->gc_ctx))
1096*4882a593Smuzhiyun 		return SVC_CLOSE;
1097*4882a593Smuzhiyun 	*authp = rpc_autherr_badverf;
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	return 0;
1100*4882a593Smuzhiyun }
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun static inline int
gss_read_verf(struct rpc_gss_wire_cred * gc,struct kvec * argv,__be32 * authp,struct xdr_netobj * in_handle,struct xdr_netobj * in_token)1103*4882a593Smuzhiyun gss_read_verf(struct rpc_gss_wire_cred *gc,
1104*4882a593Smuzhiyun 	      struct kvec *argv, __be32 *authp,
1105*4882a593Smuzhiyun 	      struct xdr_netobj *in_handle,
1106*4882a593Smuzhiyun 	      struct xdr_netobj *in_token)
1107*4882a593Smuzhiyun {
1108*4882a593Smuzhiyun 	struct xdr_netobj tmpobj;
1109*4882a593Smuzhiyun 	int res;
1110*4882a593Smuzhiyun 
1111*4882a593Smuzhiyun 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1112*4882a593Smuzhiyun 	if (res)
1113*4882a593Smuzhiyun 		return res;
1114*4882a593Smuzhiyun 
1115*4882a593Smuzhiyun 	if (svc_safe_getnetobj(argv, &tmpobj)) {
1116*4882a593Smuzhiyun 		kfree(in_handle->data);
1117*4882a593Smuzhiyun 		return SVC_DENIED;
1118*4882a593Smuzhiyun 	}
1119*4882a593Smuzhiyun 	if (dup_netobj(in_token, &tmpobj)) {
1120*4882a593Smuzhiyun 		kfree(in_handle->data);
1121*4882a593Smuzhiyun 		return SVC_CLOSE;
1122*4882a593Smuzhiyun 	}
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	return 0;
1125*4882a593Smuzhiyun }
1126*4882a593Smuzhiyun 
gss_free_in_token_pages(struct gssp_in_token * in_token)1127*4882a593Smuzhiyun static void gss_free_in_token_pages(struct gssp_in_token *in_token)
1128*4882a593Smuzhiyun {
1129*4882a593Smuzhiyun 	u32 inlen;
1130*4882a593Smuzhiyun 	int i;
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun 	i = 0;
1133*4882a593Smuzhiyun 	inlen = in_token->page_len;
1134*4882a593Smuzhiyun 	while (inlen) {
1135*4882a593Smuzhiyun 		if (in_token->pages[i])
1136*4882a593Smuzhiyun 			put_page(in_token->pages[i]);
1137*4882a593Smuzhiyun 		inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen;
1138*4882a593Smuzhiyun 	}
1139*4882a593Smuzhiyun 
1140*4882a593Smuzhiyun 	kfree(in_token->pages);
1141*4882a593Smuzhiyun 	in_token->pages = NULL;
1142*4882a593Smuzhiyun }
1143*4882a593Smuzhiyun 
gss_read_proxy_verf(struct svc_rqst * rqstp,struct rpc_gss_wire_cred * gc,__be32 * authp,struct xdr_netobj * in_handle,struct gssp_in_token * in_token)1144*4882a593Smuzhiyun static int gss_read_proxy_verf(struct svc_rqst *rqstp,
1145*4882a593Smuzhiyun 			       struct rpc_gss_wire_cred *gc, __be32 *authp,
1146*4882a593Smuzhiyun 			       struct xdr_netobj *in_handle,
1147*4882a593Smuzhiyun 			       struct gssp_in_token *in_token)
1148*4882a593Smuzhiyun {
1149*4882a593Smuzhiyun 	struct kvec *argv = &rqstp->rq_arg.head[0];
1150*4882a593Smuzhiyun 	unsigned int length, pgto_offs, pgfrom_offs;
1151*4882a593Smuzhiyun 	int pages, i, res, pgto, pgfrom;
1152*4882a593Smuzhiyun 	size_t inlen, to_offs, from_offs;
1153*4882a593Smuzhiyun 
1154*4882a593Smuzhiyun 	res = gss_read_common_verf(gc, argv, authp, in_handle);
1155*4882a593Smuzhiyun 	if (res)
1156*4882a593Smuzhiyun 		return res;
1157*4882a593Smuzhiyun 
1158*4882a593Smuzhiyun 	inlen = svc_getnl(argv);
1159*4882a593Smuzhiyun 	if (inlen > (argv->iov_len + rqstp->rq_arg.page_len))
1160*4882a593Smuzhiyun 		return SVC_DENIED;
1161*4882a593Smuzhiyun 
1162*4882a593Smuzhiyun 	pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
1163*4882a593Smuzhiyun 	in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL);
1164*4882a593Smuzhiyun 	if (!in_token->pages)
1165*4882a593Smuzhiyun 		return SVC_DENIED;
1166*4882a593Smuzhiyun 	in_token->page_base = 0;
1167*4882a593Smuzhiyun 	in_token->page_len = inlen;
1168*4882a593Smuzhiyun 	for (i = 0; i < pages; i++) {
1169*4882a593Smuzhiyun 		in_token->pages[i] = alloc_page(GFP_KERNEL);
1170*4882a593Smuzhiyun 		if (!in_token->pages[i]) {
1171*4882a593Smuzhiyun 			gss_free_in_token_pages(in_token);
1172*4882a593Smuzhiyun 			return SVC_DENIED;
1173*4882a593Smuzhiyun 		}
1174*4882a593Smuzhiyun 	}
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 	length = min_t(unsigned int, inlen, argv->iov_len);
1177*4882a593Smuzhiyun 	memcpy(page_address(in_token->pages[0]), argv->iov_base, length);
1178*4882a593Smuzhiyun 	inlen -= length;
1179*4882a593Smuzhiyun 
1180*4882a593Smuzhiyun 	to_offs = length;
1181*4882a593Smuzhiyun 	from_offs = rqstp->rq_arg.page_base;
1182*4882a593Smuzhiyun 	while (inlen) {
1183*4882a593Smuzhiyun 		pgto = to_offs >> PAGE_SHIFT;
1184*4882a593Smuzhiyun 		pgfrom = from_offs >> PAGE_SHIFT;
1185*4882a593Smuzhiyun 		pgto_offs = to_offs & ~PAGE_MASK;
1186*4882a593Smuzhiyun 		pgfrom_offs = from_offs & ~PAGE_MASK;
1187*4882a593Smuzhiyun 
1188*4882a593Smuzhiyun 		length = min_t(unsigned int, inlen,
1189*4882a593Smuzhiyun 			 min_t(unsigned int, PAGE_SIZE - pgto_offs,
1190*4882a593Smuzhiyun 			       PAGE_SIZE - pgfrom_offs));
1191*4882a593Smuzhiyun 		memcpy(page_address(in_token->pages[pgto]) + pgto_offs,
1192*4882a593Smuzhiyun 		       page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs,
1193*4882a593Smuzhiyun 		       length);
1194*4882a593Smuzhiyun 
1195*4882a593Smuzhiyun 		to_offs += length;
1196*4882a593Smuzhiyun 		from_offs += length;
1197*4882a593Smuzhiyun 		inlen -= length;
1198*4882a593Smuzhiyun 	}
1199*4882a593Smuzhiyun 	return 0;
1200*4882a593Smuzhiyun }
1201*4882a593Smuzhiyun 
1202*4882a593Smuzhiyun static inline int
gss_write_resv(struct kvec * resv,size_t size_limit,struct xdr_netobj * out_handle,struct xdr_netobj * out_token,int major_status,int minor_status)1203*4882a593Smuzhiyun gss_write_resv(struct kvec *resv, size_t size_limit,
1204*4882a593Smuzhiyun 	       struct xdr_netobj *out_handle, struct xdr_netobj *out_token,
1205*4882a593Smuzhiyun 	       int major_status, int minor_status)
1206*4882a593Smuzhiyun {
1207*4882a593Smuzhiyun 	if (resv->iov_len + 4 > size_limit)
1208*4882a593Smuzhiyun 		return -1;
1209*4882a593Smuzhiyun 	svc_putnl(resv, RPC_SUCCESS);
1210*4882a593Smuzhiyun 	if (svc_safe_putnetobj(resv, out_handle))
1211*4882a593Smuzhiyun 		return -1;
1212*4882a593Smuzhiyun 	if (resv->iov_len + 3 * 4 > size_limit)
1213*4882a593Smuzhiyun 		return -1;
1214*4882a593Smuzhiyun 	svc_putnl(resv, major_status);
1215*4882a593Smuzhiyun 	svc_putnl(resv, minor_status);
1216*4882a593Smuzhiyun 	svc_putnl(resv, GSS_SEQ_WIN);
1217*4882a593Smuzhiyun 	if (svc_safe_putnetobj(resv, out_token))
1218*4882a593Smuzhiyun 		return -1;
1219*4882a593Smuzhiyun 	return 0;
1220*4882a593Smuzhiyun }
1221*4882a593Smuzhiyun 
1222*4882a593Smuzhiyun /*
1223*4882a593Smuzhiyun  * Having read the cred already and found we're in the context
1224*4882a593Smuzhiyun  * initiation case, read the verifier and initiate (or check the results
1225*4882a593Smuzhiyun  * of) upcalls to userspace for help with context initiation.  If
1226*4882a593Smuzhiyun  * the upcall results are available, write the verifier and result.
1227*4882a593Smuzhiyun  * Otherwise, drop the request pending an answer to the upcall.
1228*4882a593Smuzhiyun  */
svcauth_gss_legacy_init(struct svc_rqst * rqstp,struct rpc_gss_wire_cred * gc,__be32 * authp)1229*4882a593Smuzhiyun static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
1230*4882a593Smuzhiyun 			struct rpc_gss_wire_cred *gc, __be32 *authp)
1231*4882a593Smuzhiyun {
1232*4882a593Smuzhiyun 	struct kvec *argv = &rqstp->rq_arg.head[0];
1233*4882a593Smuzhiyun 	struct kvec *resv = &rqstp->rq_res.head[0];
1234*4882a593Smuzhiyun 	struct rsi *rsip, rsikey;
1235*4882a593Smuzhiyun 	int ret;
1236*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 	memset(&rsikey, 0, sizeof(rsikey));
1239*4882a593Smuzhiyun 	ret = gss_read_verf(gc, argv, authp,
1240*4882a593Smuzhiyun 			    &rsikey.in_handle, &rsikey.in_token);
1241*4882a593Smuzhiyun 	if (ret)
1242*4882a593Smuzhiyun 		return ret;
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun 	/* Perform upcall, or find upcall result: */
1245*4882a593Smuzhiyun 	rsip = rsi_lookup(sn->rsi_cache, &rsikey);
1246*4882a593Smuzhiyun 	rsi_free(&rsikey);
1247*4882a593Smuzhiyun 	if (!rsip)
1248*4882a593Smuzhiyun 		return SVC_CLOSE;
1249*4882a593Smuzhiyun 	if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
1250*4882a593Smuzhiyun 		/* No upcall result: */
1251*4882a593Smuzhiyun 		return SVC_CLOSE;
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	ret = SVC_CLOSE;
1254*4882a593Smuzhiyun 	/* Got an answer to the upcall; use it: */
1255*4882a593Smuzhiyun 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1256*4882a593Smuzhiyun 				&rsip->out_handle, &rsip->major_status))
1257*4882a593Smuzhiyun 		goto out;
1258*4882a593Smuzhiyun 	if (gss_write_resv(resv, PAGE_SIZE,
1259*4882a593Smuzhiyun 			   &rsip->out_handle, &rsip->out_token,
1260*4882a593Smuzhiyun 			   rsip->major_status, rsip->minor_status))
1261*4882a593Smuzhiyun 		goto out;
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun 	ret = SVC_COMPLETE;
1264*4882a593Smuzhiyun out:
1265*4882a593Smuzhiyun 	cache_put(&rsip->h, sn->rsi_cache);
1266*4882a593Smuzhiyun 	return ret;
1267*4882a593Smuzhiyun }
1268*4882a593Smuzhiyun 
gss_proxy_save_rsc(struct cache_detail * cd,struct gssp_upcall_data * ud,uint64_t * handle)1269*4882a593Smuzhiyun static int gss_proxy_save_rsc(struct cache_detail *cd,
1270*4882a593Smuzhiyun 				struct gssp_upcall_data *ud,
1271*4882a593Smuzhiyun 				uint64_t *handle)
1272*4882a593Smuzhiyun {
1273*4882a593Smuzhiyun 	struct rsc rsci, *rscp = NULL;
1274*4882a593Smuzhiyun 	static atomic64_t ctxhctr;
1275*4882a593Smuzhiyun 	long long ctxh;
1276*4882a593Smuzhiyun 	struct gss_api_mech *gm = NULL;
1277*4882a593Smuzhiyun 	time64_t expiry;
1278*4882a593Smuzhiyun 	int status = -EINVAL;
1279*4882a593Smuzhiyun 
1280*4882a593Smuzhiyun 	memset(&rsci, 0, sizeof(rsci));
1281*4882a593Smuzhiyun 	/* context handle */
1282*4882a593Smuzhiyun 	status = -ENOMEM;
1283*4882a593Smuzhiyun 	/* the handle needs to be just a unique id,
1284*4882a593Smuzhiyun 	 * use a static counter */
1285*4882a593Smuzhiyun 	ctxh = atomic64_inc_return(&ctxhctr);
1286*4882a593Smuzhiyun 
1287*4882a593Smuzhiyun 	/* make a copy for the caller */
1288*4882a593Smuzhiyun 	*handle = ctxh;
1289*4882a593Smuzhiyun 
1290*4882a593Smuzhiyun 	/* make a copy for the rsc cache */
1291*4882a593Smuzhiyun 	if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t)))
1292*4882a593Smuzhiyun 		goto out;
1293*4882a593Smuzhiyun 	rscp = rsc_lookup(cd, &rsci);
1294*4882a593Smuzhiyun 	if (!rscp)
1295*4882a593Smuzhiyun 		goto out;
1296*4882a593Smuzhiyun 
1297*4882a593Smuzhiyun 	/* creds */
1298*4882a593Smuzhiyun 	if (!ud->found_creds) {
1299*4882a593Smuzhiyun 		/* userspace seem buggy, we should always get at least a
1300*4882a593Smuzhiyun 		 * mapping to nobody */
1301*4882a593Smuzhiyun 		goto out;
1302*4882a593Smuzhiyun 	} else {
1303*4882a593Smuzhiyun 		struct timespec64 boot;
1304*4882a593Smuzhiyun 
1305*4882a593Smuzhiyun 		/* steal creds */
1306*4882a593Smuzhiyun 		rsci.cred = ud->creds;
1307*4882a593Smuzhiyun 		memset(&ud->creds, 0, sizeof(struct svc_cred));
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun 		status = -EOPNOTSUPP;
1310*4882a593Smuzhiyun 		/* get mech handle from OID */
1311*4882a593Smuzhiyun 		gm = gss_mech_get_by_OID(&ud->mech_oid);
1312*4882a593Smuzhiyun 		if (!gm)
1313*4882a593Smuzhiyun 			goto out;
1314*4882a593Smuzhiyun 		rsci.cred.cr_gss_mech = gm;
1315*4882a593Smuzhiyun 
1316*4882a593Smuzhiyun 		status = -EINVAL;
1317*4882a593Smuzhiyun 		/* mech-specific data: */
1318*4882a593Smuzhiyun 		status = gss_import_sec_context(ud->out_handle.data,
1319*4882a593Smuzhiyun 						ud->out_handle.len,
1320*4882a593Smuzhiyun 						gm, &rsci.mechctx,
1321*4882a593Smuzhiyun 						&expiry, GFP_KERNEL);
1322*4882a593Smuzhiyun 		if (status)
1323*4882a593Smuzhiyun 			goto out;
1324*4882a593Smuzhiyun 
1325*4882a593Smuzhiyun 		getboottime64(&boot);
1326*4882a593Smuzhiyun 		expiry -= boot.tv_sec;
1327*4882a593Smuzhiyun 	}
1328*4882a593Smuzhiyun 
1329*4882a593Smuzhiyun 	rsci.h.expiry_time = expiry;
1330*4882a593Smuzhiyun 	rscp = rsc_update(cd, &rsci, rscp);
1331*4882a593Smuzhiyun 	status = 0;
1332*4882a593Smuzhiyun out:
1333*4882a593Smuzhiyun 	rsc_free(&rsci);
1334*4882a593Smuzhiyun 	if (rscp)
1335*4882a593Smuzhiyun 		cache_put(&rscp->h, cd);
1336*4882a593Smuzhiyun 	else
1337*4882a593Smuzhiyun 		status = -ENOMEM;
1338*4882a593Smuzhiyun 	return status;
1339*4882a593Smuzhiyun }
1340*4882a593Smuzhiyun 
svcauth_gss_proxy_init(struct svc_rqst * rqstp,struct rpc_gss_wire_cred * gc,__be32 * authp)1341*4882a593Smuzhiyun static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
1342*4882a593Smuzhiyun 			struct rpc_gss_wire_cred *gc, __be32 *authp)
1343*4882a593Smuzhiyun {
1344*4882a593Smuzhiyun 	struct kvec *resv = &rqstp->rq_res.head[0];
1345*4882a593Smuzhiyun 	struct xdr_netobj cli_handle;
1346*4882a593Smuzhiyun 	struct gssp_upcall_data ud;
1347*4882a593Smuzhiyun 	uint64_t handle;
1348*4882a593Smuzhiyun 	int status;
1349*4882a593Smuzhiyun 	int ret;
1350*4882a593Smuzhiyun 	struct net *net = SVC_NET(rqstp);
1351*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun 	memset(&ud, 0, sizeof(ud));
1354*4882a593Smuzhiyun 	ret = gss_read_proxy_verf(rqstp, gc, authp,
1355*4882a593Smuzhiyun 				  &ud.in_handle, &ud.in_token);
1356*4882a593Smuzhiyun 	if (ret)
1357*4882a593Smuzhiyun 		return ret;
1358*4882a593Smuzhiyun 
1359*4882a593Smuzhiyun 	ret = SVC_CLOSE;
1360*4882a593Smuzhiyun 
1361*4882a593Smuzhiyun 	/* Perform synchronous upcall to gss-proxy */
1362*4882a593Smuzhiyun 	status = gssp_accept_sec_context_upcall(net, &ud);
1363*4882a593Smuzhiyun 	if (status)
1364*4882a593Smuzhiyun 		goto out;
1365*4882a593Smuzhiyun 
1366*4882a593Smuzhiyun 	trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status);
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun 	switch (ud.major_status) {
1369*4882a593Smuzhiyun 	case GSS_S_CONTINUE_NEEDED:
1370*4882a593Smuzhiyun 		cli_handle = ud.out_handle;
1371*4882a593Smuzhiyun 		break;
1372*4882a593Smuzhiyun 	case GSS_S_COMPLETE:
1373*4882a593Smuzhiyun 		status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle);
1374*4882a593Smuzhiyun 		if (status)
1375*4882a593Smuzhiyun 			goto out;
1376*4882a593Smuzhiyun 		cli_handle.data = (u8 *)&handle;
1377*4882a593Smuzhiyun 		cli_handle.len = sizeof(handle);
1378*4882a593Smuzhiyun 		break;
1379*4882a593Smuzhiyun 	default:
1380*4882a593Smuzhiyun 		goto out;
1381*4882a593Smuzhiyun 	}
1382*4882a593Smuzhiyun 
1383*4882a593Smuzhiyun 	/* Got an answer to the upcall; use it: */
1384*4882a593Smuzhiyun 	if (gss_write_init_verf(sn->rsc_cache, rqstp,
1385*4882a593Smuzhiyun 				&cli_handle, &ud.major_status))
1386*4882a593Smuzhiyun 		goto out;
1387*4882a593Smuzhiyun 	if (gss_write_resv(resv, PAGE_SIZE,
1388*4882a593Smuzhiyun 			   &cli_handle, &ud.out_token,
1389*4882a593Smuzhiyun 			   ud.major_status, ud.minor_status))
1390*4882a593Smuzhiyun 		goto out;
1391*4882a593Smuzhiyun 
1392*4882a593Smuzhiyun 	ret = SVC_COMPLETE;
1393*4882a593Smuzhiyun out:
1394*4882a593Smuzhiyun 	gss_free_in_token_pages(&ud.in_token);
1395*4882a593Smuzhiyun 	gssp_free_upcall_data(&ud);
1396*4882a593Smuzhiyun 	return ret;
1397*4882a593Smuzhiyun }
1398*4882a593Smuzhiyun 
1399*4882a593Smuzhiyun /*
1400*4882a593Smuzhiyun  * Try to set the sn->use_gss_proxy variable to a new value. We only allow
1401*4882a593Smuzhiyun  * it to be changed if it's currently undefined (-1). If it's any other value
1402*4882a593Smuzhiyun  * then return -EBUSY unless the type wouldn't have changed anyway.
1403*4882a593Smuzhiyun  */
set_gss_proxy(struct net * net,int type)1404*4882a593Smuzhiyun static int set_gss_proxy(struct net *net, int type)
1405*4882a593Smuzhiyun {
1406*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1407*4882a593Smuzhiyun 	int ret;
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 	WARN_ON_ONCE(type != 0 && type != 1);
1410*4882a593Smuzhiyun 	ret = cmpxchg(&sn->use_gss_proxy, -1, type);
1411*4882a593Smuzhiyun 	if (ret != -1 && ret != type)
1412*4882a593Smuzhiyun 		return -EBUSY;
1413*4882a593Smuzhiyun 	return 0;
1414*4882a593Smuzhiyun }
1415*4882a593Smuzhiyun 
use_gss_proxy(struct net * net)1416*4882a593Smuzhiyun static bool use_gss_proxy(struct net *net)
1417*4882a593Smuzhiyun {
1418*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1419*4882a593Smuzhiyun 
1420*4882a593Smuzhiyun 	/* If use_gss_proxy is still undefined, then try to disable it */
1421*4882a593Smuzhiyun 	if (sn->use_gss_proxy == -1)
1422*4882a593Smuzhiyun 		set_gss_proxy(net, 0);
1423*4882a593Smuzhiyun 	return sn->use_gss_proxy;
1424*4882a593Smuzhiyun }
1425*4882a593Smuzhiyun 
1426*4882a593Smuzhiyun #ifdef CONFIG_PROC_FS
1427*4882a593Smuzhiyun 
write_gssp(struct file * file,const char __user * buf,size_t count,loff_t * ppos)1428*4882a593Smuzhiyun static ssize_t write_gssp(struct file *file, const char __user *buf,
1429*4882a593Smuzhiyun 			 size_t count, loff_t *ppos)
1430*4882a593Smuzhiyun {
1431*4882a593Smuzhiyun 	struct net *net = PDE_DATA(file_inode(file));
1432*4882a593Smuzhiyun 	char tbuf[20];
1433*4882a593Smuzhiyun 	unsigned long i;
1434*4882a593Smuzhiyun 	int res;
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun 	if (*ppos || count > sizeof(tbuf)-1)
1437*4882a593Smuzhiyun 		return -EINVAL;
1438*4882a593Smuzhiyun 	if (copy_from_user(tbuf, buf, count))
1439*4882a593Smuzhiyun 		return -EFAULT;
1440*4882a593Smuzhiyun 
1441*4882a593Smuzhiyun 	tbuf[count] = 0;
1442*4882a593Smuzhiyun 	res = kstrtoul(tbuf, 0, &i);
1443*4882a593Smuzhiyun 	if (res)
1444*4882a593Smuzhiyun 		return res;
1445*4882a593Smuzhiyun 	if (i != 1)
1446*4882a593Smuzhiyun 		return -EINVAL;
1447*4882a593Smuzhiyun 	res = set_gssp_clnt(net);
1448*4882a593Smuzhiyun 	if (res)
1449*4882a593Smuzhiyun 		return res;
1450*4882a593Smuzhiyun 	res = set_gss_proxy(net, 1);
1451*4882a593Smuzhiyun 	if (res)
1452*4882a593Smuzhiyun 		return res;
1453*4882a593Smuzhiyun 	return count;
1454*4882a593Smuzhiyun }
1455*4882a593Smuzhiyun 
read_gssp(struct file * file,char __user * buf,size_t count,loff_t * ppos)1456*4882a593Smuzhiyun static ssize_t read_gssp(struct file *file, char __user *buf,
1457*4882a593Smuzhiyun 			 size_t count, loff_t *ppos)
1458*4882a593Smuzhiyun {
1459*4882a593Smuzhiyun 	struct net *net = PDE_DATA(file_inode(file));
1460*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1461*4882a593Smuzhiyun 	unsigned long p = *ppos;
1462*4882a593Smuzhiyun 	char tbuf[10];
1463*4882a593Smuzhiyun 	size_t len;
1464*4882a593Smuzhiyun 
1465*4882a593Smuzhiyun 	snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
1466*4882a593Smuzhiyun 	len = strlen(tbuf);
1467*4882a593Smuzhiyun 	if (p >= len)
1468*4882a593Smuzhiyun 		return 0;
1469*4882a593Smuzhiyun 	len -= p;
1470*4882a593Smuzhiyun 	if (len > count)
1471*4882a593Smuzhiyun 		len = count;
1472*4882a593Smuzhiyun 	if (copy_to_user(buf, (void *)(tbuf+p), len))
1473*4882a593Smuzhiyun 		return -EFAULT;
1474*4882a593Smuzhiyun 	*ppos += len;
1475*4882a593Smuzhiyun 	return len;
1476*4882a593Smuzhiyun }
1477*4882a593Smuzhiyun 
1478*4882a593Smuzhiyun static const struct proc_ops use_gss_proxy_proc_ops = {
1479*4882a593Smuzhiyun 	.proc_open	= nonseekable_open,
1480*4882a593Smuzhiyun 	.proc_write	= write_gssp,
1481*4882a593Smuzhiyun 	.proc_read	= read_gssp,
1482*4882a593Smuzhiyun };
1483*4882a593Smuzhiyun 
create_use_gss_proxy_proc_entry(struct net * net)1484*4882a593Smuzhiyun static int create_use_gss_proxy_proc_entry(struct net *net)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1487*4882a593Smuzhiyun 	struct proc_dir_entry **p = &sn->use_gssp_proc;
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun 	sn->use_gss_proxy = -1;
1490*4882a593Smuzhiyun 	*p = proc_create_data("use-gss-proxy", S_IFREG | 0600,
1491*4882a593Smuzhiyun 			      sn->proc_net_rpc,
1492*4882a593Smuzhiyun 			      &use_gss_proxy_proc_ops, net);
1493*4882a593Smuzhiyun 	if (!*p)
1494*4882a593Smuzhiyun 		return -ENOMEM;
1495*4882a593Smuzhiyun 	init_gssp_clnt(sn);
1496*4882a593Smuzhiyun 	return 0;
1497*4882a593Smuzhiyun }
1498*4882a593Smuzhiyun 
destroy_use_gss_proxy_proc_entry(struct net * net)1499*4882a593Smuzhiyun static void destroy_use_gss_proxy_proc_entry(struct net *net)
1500*4882a593Smuzhiyun {
1501*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1502*4882a593Smuzhiyun 
1503*4882a593Smuzhiyun 	if (sn->use_gssp_proc) {
1504*4882a593Smuzhiyun 		remove_proc_entry("use-gss-proxy", sn->proc_net_rpc);
1505*4882a593Smuzhiyun 		clear_gssp_clnt(sn);
1506*4882a593Smuzhiyun 	}
1507*4882a593Smuzhiyun }
1508*4882a593Smuzhiyun #else /* CONFIG_PROC_FS */
1509*4882a593Smuzhiyun 
create_use_gss_proxy_proc_entry(struct net * net)1510*4882a593Smuzhiyun static int create_use_gss_proxy_proc_entry(struct net *net)
1511*4882a593Smuzhiyun {
1512*4882a593Smuzhiyun 	return 0;
1513*4882a593Smuzhiyun }
1514*4882a593Smuzhiyun 
destroy_use_gss_proxy_proc_entry(struct net * net)1515*4882a593Smuzhiyun static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
1516*4882a593Smuzhiyun 
1517*4882a593Smuzhiyun #endif /* CONFIG_PROC_FS */
1518*4882a593Smuzhiyun 
1519*4882a593Smuzhiyun /*
1520*4882a593Smuzhiyun  * Accept an rpcsec packet.
1521*4882a593Smuzhiyun  * If context establishment, punt to user space
1522*4882a593Smuzhiyun  * If data exchange, verify/decrypt
1523*4882a593Smuzhiyun  * If context destruction, handle here
1524*4882a593Smuzhiyun  * In the context establishment and destruction case we encode
1525*4882a593Smuzhiyun  * response here and return SVC_COMPLETE.
1526*4882a593Smuzhiyun  */
1527*4882a593Smuzhiyun static int
svcauth_gss_accept(struct svc_rqst * rqstp,__be32 * authp)1528*4882a593Smuzhiyun svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
1529*4882a593Smuzhiyun {
1530*4882a593Smuzhiyun 	struct kvec	*argv = &rqstp->rq_arg.head[0];
1531*4882a593Smuzhiyun 	struct kvec	*resv = &rqstp->rq_res.head[0];
1532*4882a593Smuzhiyun 	u32		crlen;
1533*4882a593Smuzhiyun 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
1534*4882a593Smuzhiyun 	struct rpc_gss_wire_cred *gc;
1535*4882a593Smuzhiyun 	struct rsc	*rsci = NULL;
1536*4882a593Smuzhiyun 	__be32		*rpcstart;
1537*4882a593Smuzhiyun 	__be32		*reject_stat = resv->iov_base + resv->iov_len;
1538*4882a593Smuzhiyun 	int		ret;
1539*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
1540*4882a593Smuzhiyun 
1541*4882a593Smuzhiyun 	*authp = rpc_autherr_badcred;
1542*4882a593Smuzhiyun 	if (!svcdata)
1543*4882a593Smuzhiyun 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
1544*4882a593Smuzhiyun 	if (!svcdata)
1545*4882a593Smuzhiyun 		goto auth_err;
1546*4882a593Smuzhiyun 	rqstp->rq_auth_data = svcdata;
1547*4882a593Smuzhiyun 	svcdata->verf_start = NULL;
1548*4882a593Smuzhiyun 	svcdata->rsci = NULL;
1549*4882a593Smuzhiyun 	gc = &svcdata->clcred;
1550*4882a593Smuzhiyun 
1551*4882a593Smuzhiyun 	/* start of rpc packet is 7 u32's back from here:
1552*4882a593Smuzhiyun 	 * xid direction rpcversion prog vers proc flavour
1553*4882a593Smuzhiyun 	 */
1554*4882a593Smuzhiyun 	rpcstart = argv->iov_base;
1555*4882a593Smuzhiyun 	rpcstart -= 7;
1556*4882a593Smuzhiyun 
1557*4882a593Smuzhiyun 	/* credential is:
1558*4882a593Smuzhiyun 	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
1559*4882a593Smuzhiyun 	 * at least 5 u32s, and is preceded by length, so that makes 6.
1560*4882a593Smuzhiyun 	 */
1561*4882a593Smuzhiyun 
1562*4882a593Smuzhiyun 	if (argv->iov_len < 5 * 4)
1563*4882a593Smuzhiyun 		goto auth_err;
1564*4882a593Smuzhiyun 	crlen = svc_getnl(argv);
1565*4882a593Smuzhiyun 	if (svc_getnl(argv) != RPC_GSS_VERSION)
1566*4882a593Smuzhiyun 		goto auth_err;
1567*4882a593Smuzhiyun 	gc->gc_proc = svc_getnl(argv);
1568*4882a593Smuzhiyun 	gc->gc_seq = svc_getnl(argv);
1569*4882a593Smuzhiyun 	gc->gc_svc = svc_getnl(argv);
1570*4882a593Smuzhiyun 	if (svc_safe_getnetobj(argv, &gc->gc_ctx))
1571*4882a593Smuzhiyun 		goto auth_err;
1572*4882a593Smuzhiyun 	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
1573*4882a593Smuzhiyun 		goto auth_err;
1574*4882a593Smuzhiyun 
1575*4882a593Smuzhiyun 	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
1576*4882a593Smuzhiyun 		goto auth_err;
1577*4882a593Smuzhiyun 
1578*4882a593Smuzhiyun 	*authp = rpc_autherr_badverf;
1579*4882a593Smuzhiyun 	switch (gc->gc_proc) {
1580*4882a593Smuzhiyun 	case RPC_GSS_PROC_INIT:
1581*4882a593Smuzhiyun 	case RPC_GSS_PROC_CONTINUE_INIT:
1582*4882a593Smuzhiyun 		if (use_gss_proxy(SVC_NET(rqstp)))
1583*4882a593Smuzhiyun 			return svcauth_gss_proxy_init(rqstp, gc, authp);
1584*4882a593Smuzhiyun 		else
1585*4882a593Smuzhiyun 			return svcauth_gss_legacy_init(rqstp, gc, authp);
1586*4882a593Smuzhiyun 	case RPC_GSS_PROC_DATA:
1587*4882a593Smuzhiyun 	case RPC_GSS_PROC_DESTROY:
1588*4882a593Smuzhiyun 		/* Look up the context, and check the verifier: */
1589*4882a593Smuzhiyun 		*authp = rpcsec_gsserr_credproblem;
1590*4882a593Smuzhiyun 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
1591*4882a593Smuzhiyun 		if (!rsci)
1592*4882a593Smuzhiyun 			goto auth_err;
1593*4882a593Smuzhiyun 		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
1594*4882a593Smuzhiyun 		case SVC_OK:
1595*4882a593Smuzhiyun 			break;
1596*4882a593Smuzhiyun 		case SVC_DENIED:
1597*4882a593Smuzhiyun 			goto auth_err;
1598*4882a593Smuzhiyun 		case SVC_DROP:
1599*4882a593Smuzhiyun 			goto drop;
1600*4882a593Smuzhiyun 		}
1601*4882a593Smuzhiyun 		break;
1602*4882a593Smuzhiyun 	default:
1603*4882a593Smuzhiyun 		*authp = rpc_autherr_rejectedcred;
1604*4882a593Smuzhiyun 		goto auth_err;
1605*4882a593Smuzhiyun 	}
1606*4882a593Smuzhiyun 
1607*4882a593Smuzhiyun 	/* now act upon the command: */
1608*4882a593Smuzhiyun 	switch (gc->gc_proc) {
1609*4882a593Smuzhiyun 	case RPC_GSS_PROC_DESTROY:
1610*4882a593Smuzhiyun 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
1611*4882a593Smuzhiyun 			goto auth_err;
1612*4882a593Smuzhiyun 		/* Delete the entry from the cache_list and call cache_put */
1613*4882a593Smuzhiyun 		sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
1614*4882a593Smuzhiyun 		if (resv->iov_len + 4 > PAGE_SIZE)
1615*4882a593Smuzhiyun 			goto drop;
1616*4882a593Smuzhiyun 		svc_putnl(resv, RPC_SUCCESS);
1617*4882a593Smuzhiyun 		goto complete;
1618*4882a593Smuzhiyun 	case RPC_GSS_PROC_DATA:
1619*4882a593Smuzhiyun 		*authp = rpcsec_gsserr_ctxproblem;
1620*4882a593Smuzhiyun 		svcdata->verf_start = resv->iov_base + resv->iov_len;
1621*4882a593Smuzhiyun 		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
1622*4882a593Smuzhiyun 			goto auth_err;
1623*4882a593Smuzhiyun 		rqstp->rq_cred = rsci->cred;
1624*4882a593Smuzhiyun 		get_group_info(rsci->cred.cr_group_info);
1625*4882a593Smuzhiyun 		*authp = rpc_autherr_badcred;
1626*4882a593Smuzhiyun 		switch (gc->gc_svc) {
1627*4882a593Smuzhiyun 		case RPC_GSS_SVC_NONE:
1628*4882a593Smuzhiyun 			break;
1629*4882a593Smuzhiyun 		case RPC_GSS_SVC_INTEGRITY:
1630*4882a593Smuzhiyun 			/* placeholders for length and seq. number: */
1631*4882a593Smuzhiyun 			svc_putnl(resv, 0);
1632*4882a593Smuzhiyun 			svc_putnl(resv, 0);
1633*4882a593Smuzhiyun 			if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
1634*4882a593Smuzhiyun 					gc->gc_seq, rsci->mechctx))
1635*4882a593Smuzhiyun 				goto garbage_args;
1636*4882a593Smuzhiyun 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
1637*4882a593Smuzhiyun 			break;
1638*4882a593Smuzhiyun 		case RPC_GSS_SVC_PRIVACY:
1639*4882a593Smuzhiyun 			/* placeholders for length and seq. number: */
1640*4882a593Smuzhiyun 			svc_putnl(resv, 0);
1641*4882a593Smuzhiyun 			svc_putnl(resv, 0);
1642*4882a593Smuzhiyun 			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
1643*4882a593Smuzhiyun 					gc->gc_seq, rsci->mechctx))
1644*4882a593Smuzhiyun 				goto garbage_args;
1645*4882a593Smuzhiyun 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
1646*4882a593Smuzhiyun 			break;
1647*4882a593Smuzhiyun 		default:
1648*4882a593Smuzhiyun 			goto auth_err;
1649*4882a593Smuzhiyun 		}
1650*4882a593Smuzhiyun 		svcdata->rsci = rsci;
1651*4882a593Smuzhiyun 		cache_get(&rsci->h);
1652*4882a593Smuzhiyun 		rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
1653*4882a593Smuzhiyun 					rsci->mechctx->mech_type,
1654*4882a593Smuzhiyun 					GSS_C_QOP_DEFAULT,
1655*4882a593Smuzhiyun 					gc->gc_svc);
1656*4882a593Smuzhiyun 		ret = SVC_OK;
1657*4882a593Smuzhiyun 		trace_rpcgss_svc_authenticate(rqstp, gc);
1658*4882a593Smuzhiyun 		goto out;
1659*4882a593Smuzhiyun 	}
1660*4882a593Smuzhiyun garbage_args:
1661*4882a593Smuzhiyun 	ret = SVC_GARBAGE;
1662*4882a593Smuzhiyun 	goto out;
1663*4882a593Smuzhiyun auth_err:
1664*4882a593Smuzhiyun 	/* Restore write pointer to its original value: */
1665*4882a593Smuzhiyun 	xdr_ressize_check(rqstp, reject_stat);
1666*4882a593Smuzhiyun 	ret = SVC_DENIED;
1667*4882a593Smuzhiyun 	goto out;
1668*4882a593Smuzhiyun complete:
1669*4882a593Smuzhiyun 	ret = SVC_COMPLETE;
1670*4882a593Smuzhiyun 	goto out;
1671*4882a593Smuzhiyun drop:
1672*4882a593Smuzhiyun 	ret = SVC_CLOSE;
1673*4882a593Smuzhiyun out:
1674*4882a593Smuzhiyun 	if (rsci)
1675*4882a593Smuzhiyun 		cache_put(&rsci->h, sn->rsc_cache);
1676*4882a593Smuzhiyun 	return ret;
1677*4882a593Smuzhiyun }
1678*4882a593Smuzhiyun 
1679*4882a593Smuzhiyun static __be32 *
svcauth_gss_prepare_to_wrap(struct xdr_buf * resbuf,struct gss_svc_data * gsd)1680*4882a593Smuzhiyun svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd)
1681*4882a593Smuzhiyun {
1682*4882a593Smuzhiyun 	__be32 *p;
1683*4882a593Smuzhiyun 	u32 verf_len;
1684*4882a593Smuzhiyun 
1685*4882a593Smuzhiyun 	p = gsd->verf_start;
1686*4882a593Smuzhiyun 	gsd->verf_start = NULL;
1687*4882a593Smuzhiyun 
1688*4882a593Smuzhiyun 	/* If the reply stat is nonzero, don't wrap: */
1689*4882a593Smuzhiyun 	if (*(p-1) != rpc_success)
1690*4882a593Smuzhiyun 		return NULL;
1691*4882a593Smuzhiyun 	/* Skip the verifier: */
1692*4882a593Smuzhiyun 	p += 1;
1693*4882a593Smuzhiyun 	verf_len = ntohl(*p++);
1694*4882a593Smuzhiyun 	p += XDR_QUADLEN(verf_len);
1695*4882a593Smuzhiyun 	/* move accept_stat to right place: */
1696*4882a593Smuzhiyun 	memcpy(p, p + 2, 4);
1697*4882a593Smuzhiyun 	/* Also don't wrap if the accept stat is nonzero: */
1698*4882a593Smuzhiyun 	if (*p != rpc_success) {
1699*4882a593Smuzhiyun 		resbuf->head[0].iov_len -= 2 * 4;
1700*4882a593Smuzhiyun 		return NULL;
1701*4882a593Smuzhiyun 	}
1702*4882a593Smuzhiyun 	p++;
1703*4882a593Smuzhiyun 	return p;
1704*4882a593Smuzhiyun }
1705*4882a593Smuzhiyun 
1706*4882a593Smuzhiyun static inline int
svcauth_gss_wrap_resp_integ(struct svc_rqst * rqstp)1707*4882a593Smuzhiyun svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
1708*4882a593Smuzhiyun {
1709*4882a593Smuzhiyun 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1710*4882a593Smuzhiyun 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
1711*4882a593Smuzhiyun 	struct xdr_buf *resbuf = &rqstp->rq_res;
1712*4882a593Smuzhiyun 	struct xdr_buf integ_buf;
1713*4882a593Smuzhiyun 	struct xdr_netobj mic;
1714*4882a593Smuzhiyun 	struct kvec *resv;
1715*4882a593Smuzhiyun 	__be32 *p;
1716*4882a593Smuzhiyun 	int integ_offset, integ_len;
1717*4882a593Smuzhiyun 	int stat = -EINVAL;
1718*4882a593Smuzhiyun 
1719*4882a593Smuzhiyun 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
1720*4882a593Smuzhiyun 	if (p == NULL)
1721*4882a593Smuzhiyun 		goto out;
1722*4882a593Smuzhiyun 	integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
1723*4882a593Smuzhiyun 	integ_len = resbuf->len - integ_offset;
1724*4882a593Smuzhiyun 	if (integ_len & 3)
1725*4882a593Smuzhiyun 		goto out;
1726*4882a593Smuzhiyun 	*p++ = htonl(integ_len);
1727*4882a593Smuzhiyun 	*p++ = htonl(gc->gc_seq);
1728*4882a593Smuzhiyun 	if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) {
1729*4882a593Smuzhiyun 		WARN_ON_ONCE(1);
1730*4882a593Smuzhiyun 		goto out_err;
1731*4882a593Smuzhiyun 	}
1732*4882a593Smuzhiyun 	if (resbuf->tail[0].iov_base == NULL) {
1733*4882a593Smuzhiyun 		if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1734*4882a593Smuzhiyun 			goto out_err;
1735*4882a593Smuzhiyun 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
1736*4882a593Smuzhiyun 						+ resbuf->head[0].iov_len;
1737*4882a593Smuzhiyun 		resbuf->tail[0].iov_len = 0;
1738*4882a593Smuzhiyun 	}
1739*4882a593Smuzhiyun 	resv = &resbuf->tail[0];
1740*4882a593Smuzhiyun 	mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
1741*4882a593Smuzhiyun 	if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))
1742*4882a593Smuzhiyun 		goto out_err;
1743*4882a593Smuzhiyun 	svc_putnl(resv, mic.len);
1744*4882a593Smuzhiyun 	memset(mic.data + mic.len, 0,
1745*4882a593Smuzhiyun 			round_up_to_quad(mic.len) - mic.len);
1746*4882a593Smuzhiyun 	resv->iov_len += XDR_QUADLEN(mic.len) << 2;
1747*4882a593Smuzhiyun 	/* not strictly required: */
1748*4882a593Smuzhiyun 	resbuf->len += XDR_QUADLEN(mic.len) << 2;
1749*4882a593Smuzhiyun 	if (resv->iov_len > PAGE_SIZE)
1750*4882a593Smuzhiyun 		goto out_err;
1751*4882a593Smuzhiyun out:
1752*4882a593Smuzhiyun 	stat = 0;
1753*4882a593Smuzhiyun out_err:
1754*4882a593Smuzhiyun 	return stat;
1755*4882a593Smuzhiyun }
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun static inline int
svcauth_gss_wrap_resp_priv(struct svc_rqst * rqstp)1758*4882a593Smuzhiyun svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
1759*4882a593Smuzhiyun {
1760*4882a593Smuzhiyun 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1761*4882a593Smuzhiyun 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
1762*4882a593Smuzhiyun 	struct xdr_buf *resbuf = &rqstp->rq_res;
1763*4882a593Smuzhiyun 	struct page **inpages = NULL;
1764*4882a593Smuzhiyun 	__be32 *p, *len;
1765*4882a593Smuzhiyun 	int offset;
1766*4882a593Smuzhiyun 	int pad;
1767*4882a593Smuzhiyun 
1768*4882a593Smuzhiyun 	p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
1769*4882a593Smuzhiyun 	if (p == NULL)
1770*4882a593Smuzhiyun 		return 0;
1771*4882a593Smuzhiyun 	len = p++;
1772*4882a593Smuzhiyun 	offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
1773*4882a593Smuzhiyun 	*p++ = htonl(gc->gc_seq);
1774*4882a593Smuzhiyun 	inpages = resbuf->pages;
1775*4882a593Smuzhiyun 	/* XXX: Would be better to write some xdr helper functions for
1776*4882a593Smuzhiyun 	 * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun 	/*
1779*4882a593Smuzhiyun 	 * If there is currently tail data, make sure there is
1780*4882a593Smuzhiyun 	 * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in
1781*4882a593Smuzhiyun 	 * the page, and move the current tail data such that
1782*4882a593Smuzhiyun 	 * there is RPC_MAX_AUTH_SIZE slack space available in
1783*4882a593Smuzhiyun 	 * both the head and tail.
1784*4882a593Smuzhiyun 	 */
1785*4882a593Smuzhiyun 	if (resbuf->tail[0].iov_base) {
1786*4882a593Smuzhiyun 		if (resbuf->tail[0].iov_base >=
1787*4882a593Smuzhiyun 			resbuf->head[0].iov_base + PAGE_SIZE)
1788*4882a593Smuzhiyun 			return -EINVAL;
1789*4882a593Smuzhiyun 		if (resbuf->tail[0].iov_base < resbuf->head[0].iov_base)
1790*4882a593Smuzhiyun 			return -EINVAL;
1791*4882a593Smuzhiyun 		if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len
1792*4882a593Smuzhiyun 				+ 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1793*4882a593Smuzhiyun 			return -ENOMEM;
1794*4882a593Smuzhiyun 		memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE,
1795*4882a593Smuzhiyun 			resbuf->tail[0].iov_base,
1796*4882a593Smuzhiyun 			resbuf->tail[0].iov_len);
1797*4882a593Smuzhiyun 		resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
1798*4882a593Smuzhiyun 	}
1799*4882a593Smuzhiyun 	/*
1800*4882a593Smuzhiyun 	 * If there is no current tail data, make sure there is
1801*4882a593Smuzhiyun 	 * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the
1802*4882a593Smuzhiyun 	 * allotted page, and set up tail information such that there
1803*4882a593Smuzhiyun 	 * is RPC_MAX_AUTH_SIZE slack space available in both the
1804*4882a593Smuzhiyun 	 * head and tail.
1805*4882a593Smuzhiyun 	 */
1806*4882a593Smuzhiyun 	if (resbuf->tail[0].iov_base == NULL) {
1807*4882a593Smuzhiyun 		if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
1808*4882a593Smuzhiyun 			return -ENOMEM;
1809*4882a593Smuzhiyun 		resbuf->tail[0].iov_base = resbuf->head[0].iov_base
1810*4882a593Smuzhiyun 			+ resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
1811*4882a593Smuzhiyun 		resbuf->tail[0].iov_len = 0;
1812*4882a593Smuzhiyun 	}
1813*4882a593Smuzhiyun 	if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
1814*4882a593Smuzhiyun 		return -ENOMEM;
1815*4882a593Smuzhiyun 	*len = htonl(resbuf->len - offset);
1816*4882a593Smuzhiyun 	pad = 3 - ((resbuf->len - offset - 1)&3);
1817*4882a593Smuzhiyun 	p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len);
1818*4882a593Smuzhiyun 	memset(p, 0, pad);
1819*4882a593Smuzhiyun 	resbuf->tail[0].iov_len += pad;
1820*4882a593Smuzhiyun 	resbuf->len += pad;
1821*4882a593Smuzhiyun 	return 0;
1822*4882a593Smuzhiyun }
1823*4882a593Smuzhiyun 
1824*4882a593Smuzhiyun static int
svcauth_gss_release(struct svc_rqst * rqstp)1825*4882a593Smuzhiyun svcauth_gss_release(struct svc_rqst *rqstp)
1826*4882a593Smuzhiyun {
1827*4882a593Smuzhiyun 	struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
1828*4882a593Smuzhiyun 	struct rpc_gss_wire_cred *gc;
1829*4882a593Smuzhiyun 	struct xdr_buf *resbuf = &rqstp->rq_res;
1830*4882a593Smuzhiyun 	int stat = -EINVAL;
1831*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
1832*4882a593Smuzhiyun 
1833*4882a593Smuzhiyun 	if (!gsd)
1834*4882a593Smuzhiyun 		goto out;
1835*4882a593Smuzhiyun 	gc = &gsd->clcred;
1836*4882a593Smuzhiyun 	if (gc->gc_proc != RPC_GSS_PROC_DATA)
1837*4882a593Smuzhiyun 		goto out;
1838*4882a593Smuzhiyun 	/* Release can be called twice, but we only wrap once. */
1839*4882a593Smuzhiyun 	if (gsd->verf_start == NULL)
1840*4882a593Smuzhiyun 		goto out;
1841*4882a593Smuzhiyun 	/* normally not set till svc_send, but we need it here: */
1842*4882a593Smuzhiyun 	/* XXX: what for?  Do we mess it up the moment we call svc_putu32
1843*4882a593Smuzhiyun 	 * or whatever? */
1844*4882a593Smuzhiyun 	resbuf->len = total_buf_len(resbuf);
1845*4882a593Smuzhiyun 	switch (gc->gc_svc) {
1846*4882a593Smuzhiyun 	case RPC_GSS_SVC_NONE:
1847*4882a593Smuzhiyun 		break;
1848*4882a593Smuzhiyun 	case RPC_GSS_SVC_INTEGRITY:
1849*4882a593Smuzhiyun 		stat = svcauth_gss_wrap_resp_integ(rqstp);
1850*4882a593Smuzhiyun 		if (stat)
1851*4882a593Smuzhiyun 			goto out_err;
1852*4882a593Smuzhiyun 		break;
1853*4882a593Smuzhiyun 	case RPC_GSS_SVC_PRIVACY:
1854*4882a593Smuzhiyun 		stat = svcauth_gss_wrap_resp_priv(rqstp);
1855*4882a593Smuzhiyun 		if (stat)
1856*4882a593Smuzhiyun 			goto out_err;
1857*4882a593Smuzhiyun 		break;
1858*4882a593Smuzhiyun 	/*
1859*4882a593Smuzhiyun 	 * For any other gc_svc value, svcauth_gss_accept() already set
1860*4882a593Smuzhiyun 	 * the auth_error appropriately; just fall through:
1861*4882a593Smuzhiyun 	 */
1862*4882a593Smuzhiyun 	}
1863*4882a593Smuzhiyun 
1864*4882a593Smuzhiyun out:
1865*4882a593Smuzhiyun 	stat = 0;
1866*4882a593Smuzhiyun out_err:
1867*4882a593Smuzhiyun 	if (rqstp->rq_client)
1868*4882a593Smuzhiyun 		auth_domain_put(rqstp->rq_client);
1869*4882a593Smuzhiyun 	rqstp->rq_client = NULL;
1870*4882a593Smuzhiyun 	if (rqstp->rq_gssclient)
1871*4882a593Smuzhiyun 		auth_domain_put(rqstp->rq_gssclient);
1872*4882a593Smuzhiyun 	rqstp->rq_gssclient = NULL;
1873*4882a593Smuzhiyun 	if (rqstp->rq_cred.cr_group_info)
1874*4882a593Smuzhiyun 		put_group_info(rqstp->rq_cred.cr_group_info);
1875*4882a593Smuzhiyun 	rqstp->rq_cred.cr_group_info = NULL;
1876*4882a593Smuzhiyun 	if (gsd && gsd->rsci) {
1877*4882a593Smuzhiyun 		cache_put(&gsd->rsci->h, sn->rsc_cache);
1878*4882a593Smuzhiyun 		gsd->rsci = NULL;
1879*4882a593Smuzhiyun 	}
1880*4882a593Smuzhiyun 	return stat;
1881*4882a593Smuzhiyun }
1882*4882a593Smuzhiyun 
1883*4882a593Smuzhiyun static void
svcauth_gss_domain_release_rcu(struct rcu_head * head)1884*4882a593Smuzhiyun svcauth_gss_domain_release_rcu(struct rcu_head *head)
1885*4882a593Smuzhiyun {
1886*4882a593Smuzhiyun 	struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head);
1887*4882a593Smuzhiyun 	struct gss_domain *gd = container_of(dom, struct gss_domain, h);
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun 	kfree(dom->name);
1890*4882a593Smuzhiyun 	kfree(gd);
1891*4882a593Smuzhiyun }
1892*4882a593Smuzhiyun 
1893*4882a593Smuzhiyun static void
svcauth_gss_domain_release(struct auth_domain * dom)1894*4882a593Smuzhiyun svcauth_gss_domain_release(struct auth_domain *dom)
1895*4882a593Smuzhiyun {
1896*4882a593Smuzhiyun 	call_rcu(&dom->rcu_head, svcauth_gss_domain_release_rcu);
1897*4882a593Smuzhiyun }
1898*4882a593Smuzhiyun 
1899*4882a593Smuzhiyun static struct auth_ops svcauthops_gss = {
1900*4882a593Smuzhiyun 	.name		= "rpcsec_gss",
1901*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
1902*4882a593Smuzhiyun 	.flavour	= RPC_AUTH_GSS,
1903*4882a593Smuzhiyun 	.accept		= svcauth_gss_accept,
1904*4882a593Smuzhiyun 	.release	= svcauth_gss_release,
1905*4882a593Smuzhiyun 	.domain_release = svcauth_gss_domain_release,
1906*4882a593Smuzhiyun 	.set_client	= svcauth_gss_set_client,
1907*4882a593Smuzhiyun };
1908*4882a593Smuzhiyun 
rsi_cache_create_net(struct net * net)1909*4882a593Smuzhiyun static int rsi_cache_create_net(struct net *net)
1910*4882a593Smuzhiyun {
1911*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1912*4882a593Smuzhiyun 	struct cache_detail *cd;
1913*4882a593Smuzhiyun 	int err;
1914*4882a593Smuzhiyun 
1915*4882a593Smuzhiyun 	cd = cache_create_net(&rsi_cache_template, net);
1916*4882a593Smuzhiyun 	if (IS_ERR(cd))
1917*4882a593Smuzhiyun 		return PTR_ERR(cd);
1918*4882a593Smuzhiyun 	err = cache_register_net(cd, net);
1919*4882a593Smuzhiyun 	if (err) {
1920*4882a593Smuzhiyun 		cache_destroy_net(cd, net);
1921*4882a593Smuzhiyun 		return err;
1922*4882a593Smuzhiyun 	}
1923*4882a593Smuzhiyun 	sn->rsi_cache = cd;
1924*4882a593Smuzhiyun 	return 0;
1925*4882a593Smuzhiyun }
1926*4882a593Smuzhiyun 
rsi_cache_destroy_net(struct net * net)1927*4882a593Smuzhiyun static void rsi_cache_destroy_net(struct net *net)
1928*4882a593Smuzhiyun {
1929*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1930*4882a593Smuzhiyun 	struct cache_detail *cd = sn->rsi_cache;
1931*4882a593Smuzhiyun 
1932*4882a593Smuzhiyun 	sn->rsi_cache = NULL;
1933*4882a593Smuzhiyun 	cache_purge(cd);
1934*4882a593Smuzhiyun 	cache_unregister_net(cd, net);
1935*4882a593Smuzhiyun 	cache_destroy_net(cd, net);
1936*4882a593Smuzhiyun }
1937*4882a593Smuzhiyun 
rsc_cache_create_net(struct net * net)1938*4882a593Smuzhiyun static int rsc_cache_create_net(struct net *net)
1939*4882a593Smuzhiyun {
1940*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1941*4882a593Smuzhiyun 	struct cache_detail *cd;
1942*4882a593Smuzhiyun 	int err;
1943*4882a593Smuzhiyun 
1944*4882a593Smuzhiyun 	cd = cache_create_net(&rsc_cache_template, net);
1945*4882a593Smuzhiyun 	if (IS_ERR(cd))
1946*4882a593Smuzhiyun 		return PTR_ERR(cd);
1947*4882a593Smuzhiyun 	err = cache_register_net(cd, net);
1948*4882a593Smuzhiyun 	if (err) {
1949*4882a593Smuzhiyun 		cache_destroy_net(cd, net);
1950*4882a593Smuzhiyun 		return err;
1951*4882a593Smuzhiyun 	}
1952*4882a593Smuzhiyun 	sn->rsc_cache = cd;
1953*4882a593Smuzhiyun 	return 0;
1954*4882a593Smuzhiyun }
1955*4882a593Smuzhiyun 
rsc_cache_destroy_net(struct net * net)1956*4882a593Smuzhiyun static void rsc_cache_destroy_net(struct net *net)
1957*4882a593Smuzhiyun {
1958*4882a593Smuzhiyun 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
1959*4882a593Smuzhiyun 	struct cache_detail *cd = sn->rsc_cache;
1960*4882a593Smuzhiyun 
1961*4882a593Smuzhiyun 	sn->rsc_cache = NULL;
1962*4882a593Smuzhiyun 	cache_purge(cd);
1963*4882a593Smuzhiyun 	cache_unregister_net(cd, net);
1964*4882a593Smuzhiyun 	cache_destroy_net(cd, net);
1965*4882a593Smuzhiyun }
1966*4882a593Smuzhiyun 
1967*4882a593Smuzhiyun int
gss_svc_init_net(struct net * net)1968*4882a593Smuzhiyun gss_svc_init_net(struct net *net)
1969*4882a593Smuzhiyun {
1970*4882a593Smuzhiyun 	int rv;
1971*4882a593Smuzhiyun 
1972*4882a593Smuzhiyun 	rv = rsc_cache_create_net(net);
1973*4882a593Smuzhiyun 	if (rv)
1974*4882a593Smuzhiyun 		return rv;
1975*4882a593Smuzhiyun 	rv = rsi_cache_create_net(net);
1976*4882a593Smuzhiyun 	if (rv)
1977*4882a593Smuzhiyun 		goto out1;
1978*4882a593Smuzhiyun 	rv = create_use_gss_proxy_proc_entry(net);
1979*4882a593Smuzhiyun 	if (rv)
1980*4882a593Smuzhiyun 		goto out2;
1981*4882a593Smuzhiyun 	return 0;
1982*4882a593Smuzhiyun out2:
1983*4882a593Smuzhiyun 	rsi_cache_destroy_net(net);
1984*4882a593Smuzhiyun out1:
1985*4882a593Smuzhiyun 	rsc_cache_destroy_net(net);
1986*4882a593Smuzhiyun 	return rv;
1987*4882a593Smuzhiyun }
1988*4882a593Smuzhiyun 
1989*4882a593Smuzhiyun void
gss_svc_shutdown_net(struct net * net)1990*4882a593Smuzhiyun gss_svc_shutdown_net(struct net *net)
1991*4882a593Smuzhiyun {
1992*4882a593Smuzhiyun 	destroy_use_gss_proxy_proc_entry(net);
1993*4882a593Smuzhiyun 	rsi_cache_destroy_net(net);
1994*4882a593Smuzhiyun 	rsc_cache_destroy_net(net);
1995*4882a593Smuzhiyun }
1996*4882a593Smuzhiyun 
1997*4882a593Smuzhiyun int
gss_svc_init(void)1998*4882a593Smuzhiyun gss_svc_init(void)
1999*4882a593Smuzhiyun {
2000*4882a593Smuzhiyun 	return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
2001*4882a593Smuzhiyun }
2002*4882a593Smuzhiyun 
2003*4882a593Smuzhiyun void
gss_svc_shutdown(void)2004*4882a593Smuzhiyun gss_svc_shutdown(void)
2005*4882a593Smuzhiyun {
2006*4882a593Smuzhiyun 	svc_auth_unregister(RPC_AUTH_GSS);
2007*4882a593Smuzhiyun }
2008