xref: /openbmc/linux/net/sunrpc/auth_gss/auth_gss.c (revision 6b2e6856)
12573a464SChuck Lever // SPDX-License-Identifier: BSD-3-Clause
21da177e4SLinus Torvalds /*
3f30c2269SUwe Zeisberger  * linux/net/sunrpc/auth_gss/auth_gss.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * RPCSEC_GSS client authentication.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Copyright (c) 2000 The Regents of the University of Michigan.
81da177e4SLinus Torvalds  *  All rights reserved.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Dug Song       <dugsong@monkey.org>
111da177e4SLinus Torvalds  *  Andy Adamson   <andros@umich.edu>
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/types.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
181da177e4SLinus Torvalds #include <linux/sched.h>
192d2da60cSJ. Bruce Fields #include <linux/pagemap.h>
201da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
211da177e4SLinus Torvalds #include <linux/sunrpc/auth.h>
221da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h>
231da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h>
241da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h>
251da177e4SLinus Torvalds #include <linux/workqueue.h>
261da177e4SLinus Torvalds #include <linux/sunrpc/rpc_pipe_fs.h>
271da177e4SLinus Torvalds #include <linux/sunrpc/gss_api.h>
287c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
29eb6dc19dSTrond Myklebust #include <linux/hashtable.h>
301da177e4SLinus Torvalds 
31abfdbd53STrond Myklebust #include "../netns.h"
32abfdbd53STrond Myklebust 
330c77668dSChuck Lever #include <trace/events/rpcgss.h>
340c77668dSChuck Lever 
35f1c0a861STrond Myklebust static const struct rpc_authops authgss_ops;
361da177e4SLinus Torvalds 
37f1c0a861STrond Myklebust static const struct rpc_credops gss_credops;
380df7fb74STrond Myklebust static const struct rpc_credops gss_nullops;
391da177e4SLinus Torvalds 
40126e216aSTrond Myklebust #define GSS_RETRY_EXPIRED 5
41126e216aSTrond Myklebust static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
42126e216aSTrond Myklebust 
434de6caa2SAndy Adamson #define GSS_KEY_EXPIRE_TIMEO 240
444de6caa2SAndy Adamson static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
454de6caa2SAndy Adamson 
46f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
471da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
481da177e4SLinus Torvalds #endif
491da177e4SLinus Torvalds 
50725f2865SKevin Coffman #define GSS_CRED_SLACK		(RPC_MAX_AUTH_SIZE * 2)
511da177e4SLinus Torvalds /* length of a krb5 verifier (48), plus data added before arguments when
521da177e4SLinus Torvalds  * using integrity (two 4-byte integers): */
53adeb8133SOlga Kornievskaia #define GSS_VERF_SLACK		100
541da177e4SLinus Torvalds 
5523c323afSTrond Myklebust static DEFINE_HASHTABLE(gss_auth_hash_table, 4);
56eb6dc19dSTrond Myklebust static DEFINE_SPINLOCK(gss_auth_hash_lock);
57eb6dc19dSTrond Myklebust 
5819172284STrond Myklebust struct gss_pipe {
5919172284STrond Myklebust 	struct rpc_pipe_dir_object pdo;
6019172284STrond Myklebust 	struct rpc_pipe *pipe;
6119172284STrond Myklebust 	struct rpc_clnt *clnt;
6219172284STrond Myklebust 	const char *name;
63414a6295STrond Myklebust 	struct kref kref;
6419172284STrond Myklebust };
6519172284STrond Myklebust 
661da177e4SLinus Torvalds struct gss_auth {
670285ed1fSTrond Myklebust 	struct kref kref;
68eb6dc19dSTrond Myklebust 	struct hlist_node hash;
691da177e4SLinus Torvalds 	struct rpc_auth rpc_auth;
701da177e4SLinus Torvalds 	struct gss_api_mech *mech;
711da177e4SLinus Torvalds 	enum rpc_gss_svc service;
721da177e4SLinus Torvalds 	struct rpc_clnt *client;
73e726340aSTrond Myklebust 	struct net *net;
7434769fc4S\"J. Bruce Fields\ 	/*
7534769fc4S\"J. Bruce Fields\ 	 * There are two upcall pipes; dentry[1], named "gssd", is used
7634769fc4S\"J. Bruce Fields\ 	 * for the new text-based upcall; dentry[0] is named after the
7734769fc4S\"J. Bruce Fields\ 	 * mechanism (for example, "krb5") and exists for
7834769fc4S\"J. Bruce Fields\ 	 * backwards-compatibility with older gssd's.
7934769fc4S\"J. Bruce Fields\ 	 */
8019172284STrond Myklebust 	struct gss_pipe *gss_pipe[2];
81bd4a3eb1STrond Myklebust 	const char *target_name;
821da177e4SLinus Torvalds };
831da177e4SLinus Torvalds 
8479a3f20bS\"J. Bruce Fields\ /* pipe_version >= 0 if and only if someone has a pipe open. */
8579a3f20bS\"J. Bruce Fields\ static DEFINE_SPINLOCK(pipe_version_lock);
8679a3f20bS\"J. Bruce Fields\ static struct rpc_wait_queue pipe_version_rpc_waitqueue;
8779a3f20bS\"J. Bruce Fields\ static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
889eb2ddb4STrond Myklebust static void gss_put_auth(struct gss_auth *gss_auth);
89cf81939dS\"J. Bruce Fields\ 
905d28dc82STrond Myklebust static void gss_free_ctx(struct gss_cl_ctx *);
91b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0;
92b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds static inline struct gss_cl_ctx *
951da177e4SLinus Torvalds gss_get_ctx(struct gss_cl_ctx *ctx)
961da177e4SLinus Torvalds {
970fa10472SReshetova, Elena 	refcount_inc(&ctx->count);
981da177e4SLinus Torvalds 	return ctx;
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds static inline void
1021da177e4SLinus Torvalds gss_put_ctx(struct gss_cl_ctx *ctx)
1031da177e4SLinus Torvalds {
1040fa10472SReshetova, Elena 	if (refcount_dec_and_test(&ctx->count))
1055d28dc82STrond Myklebust 		gss_free_ctx(ctx);
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1085d28dc82STrond Myklebust /* gss_cred_set_ctx:
1095d28dc82STrond Myklebust  * called by gss_upcall_callback and gss_create_upcall in order
1105d28dc82STrond Myklebust  * to set the gss context. The actual exchange of an old context
1119beae467SStanislav Kinsbursky  * and a new one is protected by the pipe->lock.
1125d28dc82STrond Myklebust  */
1131da177e4SLinus Torvalds static void
1141da177e4SLinus Torvalds gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
1151da177e4SLinus Torvalds {
1161da177e4SLinus Torvalds 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
1175d28dc82STrond Myklebust 
118cd019f75STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
119cd019f75STrond Myklebust 		return;
1207b6962b0STrond Myklebust 	gss_get_ctx(ctx);
121cf778b00SEric Dumazet 	rcu_assign_pointer(gss_cred->gc_ctx, ctx);
122fc432dd9STrond Myklebust 	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
1234e857c58SPeter Zijlstra 	smp_mb__before_atomic();
124fc432dd9STrond Myklebust 	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds static const void *
1281da177e4SLinus Torvalds simple_get_bytes(const void *p, const void *end, void *res, size_t len)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	const void *q = (const void *)((const char *)p + len);
1311da177e4SLinus Torvalds 	if (unlikely(q > end || q < p))
1321da177e4SLinus Torvalds 		return ERR_PTR(-EFAULT);
1331da177e4SLinus Torvalds 	memcpy(res, p, len);
1341da177e4SLinus Torvalds 	return q;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds static inline const void *
1381da177e4SLinus Torvalds simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	const void *q;
1411da177e4SLinus Torvalds 	unsigned int len;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &len, sizeof(len));
1441da177e4SLinus Torvalds 	if (IS_ERR(p))
1451da177e4SLinus Torvalds 		return p;
1461da177e4SLinus Torvalds 	q = (const void *)((const char *)p + len);
1471da177e4SLinus Torvalds 	if (unlikely(q > end || q < p))
1481da177e4SLinus Torvalds 		return ERR_PTR(-EFAULT);
1490f38b873STrond Myklebust 	dest->data = kmemdup(p, len, GFP_NOFS);
1501da177e4SLinus Torvalds 	if (unlikely(dest->data == NULL))
1511da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
1521da177e4SLinus Torvalds 	dest->len = len;
1531da177e4SLinus Torvalds 	return q;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds static struct gss_cl_ctx *
1571da177e4SLinus Torvalds gss_cred_get_ctx(struct rpc_cred *cred)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
1601da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx = NULL;
1611da177e4SLinus Torvalds 
1625d28dc82STrond Myklebust 	rcu_read_lock();
163c5e6aecdSJeff Layton 	ctx = rcu_dereference(gss_cred->gc_ctx);
164c5e6aecdSJeff Layton 	if (ctx)
165c5e6aecdSJeff Layton 		gss_get_ctx(ctx);
1665d28dc82STrond Myklebust 	rcu_read_unlock();
1671da177e4SLinus Torvalds 	return ctx;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds static struct gss_cl_ctx *
1711da177e4SLinus Torvalds gss_alloc_context(void)
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx;
1741da177e4SLinus Torvalds 
1750f38b873STrond Myklebust 	ctx = kzalloc(sizeof(*ctx), GFP_NOFS);
1761da177e4SLinus Torvalds 	if (ctx != NULL) {
1771da177e4SLinus Torvalds 		ctx->gc_proc = RPC_GSS_PROC_DATA;
1781da177e4SLinus Torvalds 		ctx->gc_seq = 1;	/* NetApp 6.4R1 doesn't accept seq. no. 0 */
1791da177e4SLinus Torvalds 		spin_lock_init(&ctx->gc_seq_lock);
1800fa10472SReshetova, Elena 		refcount_set(&ctx->count,1);
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds 	return ctx;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds #define GSSD_MIN_TIMEOUT (60 * 60)
1861da177e4SLinus Torvalds static const void *
1871da177e4SLinus Torvalds gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm)
1881da177e4SLinus Torvalds {
1891da177e4SLinus Torvalds 	const void *q;
1901da177e4SLinus Torvalds 	unsigned int seclen;
1911da177e4SLinus Torvalds 	unsigned int timeout;
192620038f6SAndy Adamson 	unsigned long now = jiffies;
1931da177e4SLinus Torvalds 	u32 window_size;
1941da177e4SLinus Torvalds 	int ret;
1951da177e4SLinus Torvalds 
196620038f6SAndy Adamson 	/* First unsigned int gives the remaining lifetime in seconds of the
197620038f6SAndy Adamson 	 * credential - e.g. the remaining TGT lifetime for Kerberos or
198620038f6SAndy Adamson 	 * the -t value passed to GSSD.
199620038f6SAndy Adamson 	 */
2001da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &timeout, sizeof(timeout));
2011da177e4SLinus Torvalds 	if (IS_ERR(p))
2021da177e4SLinus Torvalds 		goto err;
2031da177e4SLinus Torvalds 	if (timeout == 0)
2041da177e4SLinus Torvalds 		timeout = GSSD_MIN_TIMEOUT;
205620038f6SAndy Adamson 	ctx->gc_expiry = now + ((unsigned long)timeout * HZ);
206620038f6SAndy Adamson 	/* Sequence number window. Determines the maximum number of
207620038f6SAndy Adamson 	 * simultaneous requests
208620038f6SAndy Adamson 	 */
2091da177e4SLinus Torvalds 	p = simple_get_bytes(p, end, &window_size, sizeof(window_size));
2101da177e4SLinus Torvalds 	if (IS_ERR(p))
2111da177e4SLinus Torvalds 		goto err;
2121da177e4SLinus Torvalds 	ctx->gc_win = window_size;
2131da177e4SLinus Torvalds 	/* gssd signals an error by passing ctx->gc_win = 0: */
2141da177e4SLinus Torvalds 	if (ctx->gc_win == 0) {
215dc5ddce9SJeff Layton 		/*
216dc5ddce9SJeff Layton 		 * in which case, p points to an error code. Anything other
217dc5ddce9SJeff Layton 		 * than -EKEYEXPIRED gets converted to -EACCES.
218dc5ddce9SJeff Layton 		 */
219dc5ddce9SJeff Layton 		p = simple_get_bytes(p, end, &ret, sizeof(ret));
220dc5ddce9SJeff Layton 		if (!IS_ERR(p))
221dc5ddce9SJeff Layton 			p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) :
222dc5ddce9SJeff Layton 						    ERR_PTR(-EACCES);
2231da177e4SLinus Torvalds 		goto err;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 	/* copy the opaque wire context */
2261da177e4SLinus Torvalds 	p = simple_get_netobj(p, end, &ctx->gc_wire_ctx);
2271da177e4SLinus Torvalds 	if (IS_ERR(p))
2281da177e4SLinus Torvalds 		goto err;
2291da177e4SLinus Torvalds 	/* import the opaque security context */
2301da177e4SLinus Torvalds 	p  = simple_get_bytes(p, end, &seclen, sizeof(seclen));
2311da177e4SLinus Torvalds 	if (IS_ERR(p))
2321da177e4SLinus Torvalds 		goto err;
2331da177e4SLinus Torvalds 	q = (const void *)((const char *)p + seclen);
2341da177e4SLinus Torvalds 	if (unlikely(q > end || q < p)) {
2351da177e4SLinus Torvalds 		p = ERR_PTR(-EFAULT);
2361da177e4SLinus Torvalds 		goto err;
2371da177e4SLinus Torvalds 	}
238400f26b5SSimo Sorce 	ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS);
2391da177e4SLinus Torvalds 	if (ret < 0) {
2400c77668dSChuck Lever 		trace_rpcgss_import_ctx(ret);
2411da177e4SLinus Torvalds 		p = ERR_PTR(ret);
2421da177e4SLinus Torvalds 		goto err;
2431da177e4SLinus Torvalds 	}
2442004c726SJeff Layton 
2452004c726SJeff Layton 	/* is there any trailing data? */
2462004c726SJeff Layton 	if (q == end) {
2472004c726SJeff Layton 		p = q;
2482004c726SJeff Layton 		goto done;
2492004c726SJeff Layton 	}
2502004c726SJeff Layton 
2512004c726SJeff Layton 	/* pull in acceptor name (if there is one) */
2522004c726SJeff Layton 	p = simple_get_netobj(q, end, &ctx->gc_acceptor);
2532004c726SJeff Layton 	if (IS_ERR(p))
2542004c726SJeff Layton 		goto err;
2552004c726SJeff Layton done:
2560c77668dSChuck Lever 	trace_rpcgss_context(ctx->gc_expiry, now, timeout,
2570c77668dSChuck Lever 			     ctx->gc_acceptor.len, ctx->gc_acceptor.data);
2581da177e4SLinus Torvalds err:
2591da177e4SLinus Torvalds 	return p;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
262a1a23777SChuck Lever /* XXX: Need some documentation about why UPCALL_BUF_LEN is so small.
263a1a23777SChuck Lever  *	Is user space expecting no more than UPCALL_BUF_LEN bytes?
264a1a23777SChuck Lever  *	Note that there are now _two_ NI_MAXHOST sized data items
265a1a23777SChuck Lever  *	being passed in this string.
266a1a23777SChuck Lever  */
267a1a23777SChuck Lever #define UPCALL_BUF_LEN	256
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds struct gss_upcall_msg {
2707ff13969SReshetova, Elena 	refcount_t count;
2717eaf040bSEric W. Biederman 	kuid_t	uid;
2721da177e4SLinus Torvalds 	struct rpc_pipe_msg msg;
2731da177e4SLinus Torvalds 	struct list_head list;
2741da177e4SLinus Torvalds 	struct gss_auth *auth;
2759beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe;
2761da177e4SLinus Torvalds 	struct rpc_wait_queue rpc_waitqueue;
2771da177e4SLinus Torvalds 	wait_queue_head_t waitqueue;
2781da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx;
27934769fc4S\"J. Bruce Fields\ 	char databuf[UPCALL_BUF_LEN];
2801da177e4SLinus Torvalds };
2811da177e4SLinus Torvalds 
2822aed8b47STrond Myklebust static int get_pipe_version(struct net *net)
28379a3f20bS\"J. Bruce Fields\ {
2842aed8b47STrond Myklebust 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
28579a3f20bS\"J. Bruce Fields\ 	int ret;
28679a3f20bS\"J. Bruce Fields\ 
28779a3f20bS\"J. Bruce Fields\ 	spin_lock(&pipe_version_lock);
2882aed8b47STrond Myklebust 	if (sn->pipe_version >= 0) {
2892aed8b47STrond Myklebust 		atomic_inc(&sn->pipe_users);
2902aed8b47STrond Myklebust 		ret = sn->pipe_version;
29179a3f20bS\"J. Bruce Fields\ 	} else
29279a3f20bS\"J. Bruce Fields\ 		ret = -EAGAIN;
29379a3f20bS\"J. Bruce Fields\ 	spin_unlock(&pipe_version_lock);
29479a3f20bS\"J. Bruce Fields\ 	return ret;
29579a3f20bS\"J. Bruce Fields\ }
29679a3f20bS\"J. Bruce Fields\ 
2972aed8b47STrond Myklebust static void put_pipe_version(struct net *net)
29879a3f20bS\"J. Bruce Fields\ {
2992aed8b47STrond Myklebust 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
3002aed8b47STrond Myklebust 
3012aed8b47STrond Myklebust 	if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) {
3022aed8b47STrond Myklebust 		sn->pipe_version = -1;
30379a3f20bS\"J. Bruce Fields\ 		spin_unlock(&pipe_version_lock);
30479a3f20bS\"J. Bruce Fields\ 	}
30579a3f20bS\"J. Bruce Fields\ }
30679a3f20bS\"J. Bruce Fields\ 
3071da177e4SLinus Torvalds static void
3081da177e4SLinus Torvalds gss_release_msg(struct gss_upcall_msg *gss_msg)
3091da177e4SLinus Torvalds {
310e726340aSTrond Myklebust 	struct net *net = gss_msg->auth->net;
3117ff13969SReshetova, Elena 	if (!refcount_dec_and_test(&gss_msg->count))
3121da177e4SLinus Torvalds 		return;
3132aed8b47STrond Myklebust 	put_pipe_version(net);
3141da177e4SLinus Torvalds 	BUG_ON(!list_empty(&gss_msg->list));
3151da177e4SLinus Torvalds 	if (gss_msg->ctx != NULL)
3161da177e4SLinus Torvalds 		gss_put_ctx(gss_msg->ctx);
317f6a1cc89STrond Myklebust 	rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
3189eb2ddb4STrond Myklebust 	gss_put_auth(gss_msg->auth);
3191da177e4SLinus Torvalds 	kfree(gss_msg);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds static struct gss_upcall_msg *
3239130b8dbSOlga Kornievskaia __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth)
3241da177e4SLinus Torvalds {
3251da177e4SLinus Torvalds 	struct gss_upcall_msg *pos;
3269beae467SStanislav Kinsbursky 	list_for_each_entry(pos, &pipe->in_downcall, list) {
3270b4d51b0SEric W. Biederman 		if (!uid_eq(pos->uid, uid))
3281da177e4SLinus Torvalds 			continue;
3299130b8dbSOlga Kornievskaia 		if (auth && pos->auth->service != auth->service)
3309130b8dbSOlga Kornievskaia 			continue;
3317ff13969SReshetova, Elena 		refcount_inc(&pos->count);
3321da177e4SLinus Torvalds 		return pos;
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	return NULL;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
337720b8f2dS\\\"J. Bruce Fields\\\ /* Try to add an upcall to the pipefs queue.
3381da177e4SLinus Torvalds  * If an upcall owned by our uid already exists, then we return a reference
3391da177e4SLinus Torvalds  * to that upcall instead of adding the new upcall.
3401da177e4SLinus Torvalds  */
3411da177e4SLinus Torvalds static inline struct gss_upcall_msg *
342053e324fSSuresh Jayaraman gss_add_msg(struct gss_upcall_msg *gss_msg)
3431da177e4SLinus Torvalds {
3449beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe = gss_msg->pipe;
3451da177e4SLinus Torvalds 	struct gss_upcall_msg *old;
3461da177e4SLinus Torvalds 
3479beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
3489130b8dbSOlga Kornievskaia 	old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth);
3491da177e4SLinus Torvalds 	if (old == NULL) {
3507ff13969SReshetova, Elena 		refcount_inc(&gss_msg->count);
3519beae467SStanislav Kinsbursky 		list_add(&gss_msg->list, &pipe->in_downcall);
3521da177e4SLinus Torvalds 	} else
3531da177e4SLinus Torvalds 		gss_msg = old;
3549beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
3551da177e4SLinus Torvalds 	return gss_msg;
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds static void
3591da177e4SLinus Torvalds __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
3601da177e4SLinus Torvalds {
3611da177e4SLinus Torvalds 	list_del_init(&gss_msg->list);
3621da177e4SLinus Torvalds 	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
3631da177e4SLinus Torvalds 	wake_up_all(&gss_msg->waitqueue);
3647ff13969SReshetova, Elena 	refcount_dec(&gss_msg->count);
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds static void
3681da177e4SLinus Torvalds gss_unhash_msg(struct gss_upcall_msg *gss_msg)
3691da177e4SLinus Torvalds {
3709beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe = gss_msg->pipe;
3711da177e4SLinus Torvalds 
3723b68aaeaSTrond Myklebust 	if (list_empty(&gss_msg->list))
3733b68aaeaSTrond Myklebust 		return;
3749beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
3753b68aaeaSTrond Myklebust 	if (!list_empty(&gss_msg->list))
3761da177e4SLinus Torvalds 		__gss_unhash_msg(gss_msg);
3779beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds static void
381126e216aSTrond Myklebust gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg)
382126e216aSTrond Myklebust {
383126e216aSTrond Myklebust 	switch (gss_msg->msg.errno) {
384126e216aSTrond Myklebust 	case 0:
385126e216aSTrond Myklebust 		if (gss_msg->ctx == NULL)
386126e216aSTrond Myklebust 			break;
387126e216aSTrond Myklebust 		clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
388126e216aSTrond Myklebust 		gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
389126e216aSTrond Myklebust 		break;
390126e216aSTrond Myklebust 	case -EKEYEXPIRED:
391126e216aSTrond Myklebust 		set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
392126e216aSTrond Myklebust 	}
393126e216aSTrond Myklebust 	gss_cred->gc_upcall_timestamp = jiffies;
394126e216aSTrond Myklebust 	gss_cred->gc_upcall = NULL;
395126e216aSTrond Myklebust 	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
396126e216aSTrond Myklebust }
397126e216aSTrond Myklebust 
398126e216aSTrond Myklebust static void
3991da177e4SLinus Torvalds gss_upcall_callback(struct rpc_task *task)
4001da177e4SLinus Torvalds {
401a17c2153STrond Myklebust 	struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred,
4021da177e4SLinus Torvalds 			struct gss_cred, gc_base);
4031da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
4049beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe = gss_msg->pipe;
4051da177e4SLinus Torvalds 
4069beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
407126e216aSTrond Myklebust 	gss_handle_downcall_result(gss_cred, gss_msg);
4089beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
409126e216aSTrond Myklebust 	task->tk_status = gss_msg->msg.errno;
4101da177e4SLinus Torvalds 	gss_release_msg(gss_msg);
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
41334769fc4S\"J. Bruce Fields\ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
41434769fc4S\"J. Bruce Fields\ {
41590602c7bSEric W. Biederman 	uid_t uid = from_kuid(&init_user_ns, gss_msg->uid);
41690602c7bSEric W. Biederman 	memcpy(gss_msg->databuf, &uid, sizeof(uid));
41790602c7bSEric W. Biederman 	gss_msg->msg.data = gss_msg->databuf;
41890602c7bSEric W. Biederman 	gss_msg->msg.len = sizeof(uid);
4199d3a2260STrond Myklebust 
4209d3a2260STrond Myklebust 	BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf));
42134769fc4S\"J. Bruce Fields\ }
42234769fc4S\"J. Bruce Fields\ 
4239d3a2260STrond Myklebust static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
424bd4a3eb1STrond Myklebust 				const char *service_name,
425bd4a3eb1STrond Myklebust 				const char *target_name)
42634769fc4S\"J. Bruce Fields\ {
427683ac665STrond Myklebust 	struct gss_api_mech *mech = gss_msg->auth->mech;
4288b1c7bf5SOlga Kornievskaia 	char *p = gss_msg->databuf;
4299d3a2260STrond Myklebust 	size_t buflen = sizeof(gss_msg->databuf);
4309d3a2260STrond Myklebust 	int len;
4318b1c7bf5SOlga Kornievskaia 
4329d3a2260STrond Myklebust 	len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name,
43390602c7bSEric W. Biederman 			from_kuid(&init_user_ns, gss_msg->uid));
4349d3a2260STrond Myklebust 	buflen -= len;
4359d3a2260STrond Myklebust 	p += len;
4369d3a2260STrond Myklebust 	gss_msg->msg.len = len;
437108b833cSChuck Lever 
438108b833cSChuck Lever 	/*
439108b833cSChuck Lever 	 * target= is a full service principal that names the remote
440108b833cSChuck Lever 	 * identity that we are authenticating to.
441108b833cSChuck Lever 	 */
442bd4a3eb1STrond Myklebust 	if (target_name) {
4439d3a2260STrond Myklebust 		len = scnprintf(p, buflen, " target=%s", target_name);
4449d3a2260STrond Myklebust 		buflen -= len;
4458b1c7bf5SOlga Kornievskaia 		p += len;
4468b1c7bf5SOlga Kornievskaia 		gss_msg->msg.len += len;
4478b1c7bf5SOlga Kornievskaia 	}
448108b833cSChuck Lever 
449108b833cSChuck Lever 	/*
450108b833cSChuck Lever 	 * gssd uses service= and srchost= to select a matching key from
451108b833cSChuck Lever 	 * the system's keytab to use as the source principal.
452108b833cSChuck Lever 	 *
453108b833cSChuck Lever 	 * service= is the service name part of the source principal,
454108b833cSChuck Lever 	 * or "*" (meaning choose any).
455108b833cSChuck Lever 	 *
456108b833cSChuck Lever 	 * srchost= is the hostname part of the source principal. When
457108b833cSChuck Lever 	 * not provided, gssd uses the local hostname.
458108b833cSChuck Lever 	 */
459a1a23777SChuck Lever 	if (service_name) {
460a1a23777SChuck Lever 		char *c = strchr(service_name, '@');
461a1a23777SChuck Lever 
462a1a23777SChuck Lever 		if (!c)
463a1a23777SChuck Lever 			len = scnprintf(p, buflen, " service=%s",
464a1a23777SChuck Lever 					service_name);
465a1a23777SChuck Lever 		else
466a1a23777SChuck Lever 			len = scnprintf(p, buflen,
467a1a23777SChuck Lever 					" service=%.*s srchost=%s",
468a1a23777SChuck Lever 					(int)(c - service_name),
469a1a23777SChuck Lever 					service_name, c + 1);
4709d3a2260STrond Myklebust 		buflen -= len;
4712efef708SOlga Kornievskaia 		p += len;
4722efef708SOlga Kornievskaia 		gss_msg->msg.len += len;
4732efef708SOlga Kornievskaia 	}
474108b833cSChuck Lever 
475683ac665STrond Myklebust 	if (mech->gm_upcall_enctypes) {
4769d3a2260STrond Myklebust 		len = scnprintf(p, buflen, " enctypes=%s",
4779d3a2260STrond Myklebust 				mech->gm_upcall_enctypes);
4789d3a2260STrond Myklebust 		buflen -= len;
479683ac665STrond Myklebust 		p += len;
480683ac665STrond Myklebust 		gss_msg->msg.len += len;
481683ac665STrond Myklebust 	}
4820c77668dSChuck Lever 	trace_rpcgss_upcall_msg(gss_msg->databuf);
4839d3a2260STrond Myklebust 	len = scnprintf(p, buflen, "\n");
4849d3a2260STrond Myklebust 	if (len == 0)
4859d3a2260STrond Myklebust 		goto out_overflow;
4868b1c7bf5SOlga Kornievskaia 	gss_msg->msg.len += len;
48734769fc4S\"J. Bruce Fields\ 	gss_msg->msg.data = gss_msg->databuf;
4889d3a2260STrond Myklebust 	return 0;
4899d3a2260STrond Myklebust out_overflow:
4909d3a2260STrond Myklebust 	WARN_ON_ONCE(1);
4919d3a2260STrond Myklebust 	return -ENOMEM;
49234769fc4S\"J. Bruce Fields\ }
49334769fc4S\"J. Bruce Fields\ 
49468c97153STrond Myklebust static struct gss_upcall_msg *
495e726340aSTrond Myklebust gss_alloc_msg(struct gss_auth *gss_auth,
4967eaf040bSEric W. Biederman 		kuid_t uid, const char *service_name)
4971da177e4SLinus Torvalds {
4981da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg;
49979a3f20bS\"J. Bruce Fields\ 	int vers;
5009d3a2260STrond Myklebust 	int err = -ENOMEM;
5011da177e4SLinus Torvalds 
5020f38b873STrond Myklebust 	gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
503db75b3d6S\"J. Bruce Fields\ 	if (gss_msg == NULL)
5049d3a2260STrond Myklebust 		goto err;
505e726340aSTrond Myklebust 	vers = get_pipe_version(gss_auth->net);
5069d3a2260STrond Myklebust 	err = vers;
5079d3a2260STrond Myklebust 	if (err < 0)
5089d3a2260STrond Myklebust 		goto err_free_msg;
50919172284STrond Myklebust 	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
5101da177e4SLinus Torvalds 	INIT_LIST_HEAD(&gss_msg->list);
5111da177e4SLinus Torvalds 	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
5121da177e4SLinus Torvalds 	init_waitqueue_head(&gss_msg->waitqueue);
5137ff13969SReshetova, Elena 	refcount_set(&gss_msg->count, 1);
5141da177e4SLinus Torvalds 	gss_msg->uid = uid;
5151da177e4SLinus Torvalds 	gss_msg->auth = gss_auth;
516bd4a3eb1STrond Myklebust 	switch (vers) {
517bd4a3eb1STrond Myklebust 	case 0:
518bd4a3eb1STrond Myklebust 		gss_encode_v0_msg(gss_msg);
5195fccc5b5STrond Myklebust 		break;
520bd4a3eb1STrond Myklebust 	default:
5219d3a2260STrond Myklebust 		err = gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name);
5229d3a2260STrond Myklebust 		if (err)
523e9776d0fSTrond Myklebust 			goto err_put_pipe_version;
52407d53ae4Szhong jiang 	}
5259eb2ddb4STrond Myklebust 	kref_get(&gss_auth->kref);
5261da177e4SLinus Torvalds 	return gss_msg;
527e9776d0fSTrond Myklebust err_put_pipe_version:
528e9776d0fSTrond Myklebust 	put_pipe_version(gss_auth->net);
5299d3a2260STrond Myklebust err_free_msg:
5309d3a2260STrond Myklebust 	kfree(gss_msg);
5319d3a2260STrond Myklebust err:
5329d3a2260STrond Myklebust 	return ERR_PTR(err);
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds static struct gss_upcall_msg *
536e726340aSTrond Myklebust gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
5371da177e4SLinus Torvalds {
5387c67db3aSTrond Myklebust 	struct gss_cred *gss_cred = container_of(cred,
5397c67db3aSTrond Myklebust 			struct gss_cred, gc_base);
5401da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_new, *gss_msg;
54104d1532bSNeilBrown 	kuid_t uid = cred->cr_cred->fsuid;
5421da177e4SLinus Torvalds 
543e726340aSTrond Myklebust 	gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);
544db75b3d6S\"J. Bruce Fields\ 	if (IS_ERR(gss_new))
545db75b3d6S\"J. Bruce Fields\ 		return gss_new;
546053e324fSSuresh Jayaraman 	gss_msg = gss_add_msg(gss_new);
5471da177e4SLinus Torvalds 	if (gss_msg == gss_new) {
5481cded9d2SNeilBrown 		int res;
5497ff13969SReshetova, Elena 		refcount_inc(&gss_msg->count);
5501cded9d2SNeilBrown 		res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
5511da177e4SLinus Torvalds 		if (res) {
5521da177e4SLinus Torvalds 			gss_unhash_msg(gss_new);
5537ff13969SReshetova, Elena 			refcount_dec(&gss_msg->count);
5541cded9d2SNeilBrown 			gss_release_msg(gss_new);
5551da177e4SLinus Torvalds 			gss_msg = ERR_PTR(res);
5561da177e4SLinus Torvalds 		}
5571da177e4SLinus Torvalds 	} else
5581da177e4SLinus Torvalds 		gss_release_msg(gss_new);
5591da177e4SLinus Torvalds 	return gss_msg;
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
562b03568c3S\"J. Bruce Fields\ static void warn_gssd(void)
563b03568c3S\"J. Bruce Fields\ {
5640ea9de0eSJeff Layton 	dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n");
565b03568c3S\"J. Bruce Fields\ }
566b03568c3S\"J. Bruce Fields\ 
5671da177e4SLinus Torvalds static inline int
5681da177e4SLinus Torvalds gss_refresh_upcall(struct rpc_task *task)
5691da177e4SLinus Torvalds {
570a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
5714a8c1344STrond Myklebust 	struct gss_auth *gss_auth = container_of(cred->cr_auth,
5721da177e4SLinus Torvalds 			struct gss_auth, rpc_auth);
5731da177e4SLinus Torvalds 	struct gss_cred *gss_cred = container_of(cred,
5741da177e4SLinus Torvalds 			struct gss_cred, gc_base);
5751da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg;
5769beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe;
5771da177e4SLinus Torvalds 	int err = 0;
5781da177e4SLinus Torvalds 
579e726340aSTrond Myklebust 	gss_msg = gss_setup_upcall(gss_auth, cred);
580480e3243SRoel Kluin 	if (PTR_ERR(gss_msg) == -EAGAIN) {
58179a3f20bS\"J. Bruce Fields\ 		/* XXX: warning on the first, under the assumption we
58279a3f20bS\"J. Bruce Fields\ 		 * shouldn't normally hit this case on a refresh. */
58379a3f20bS\"J. Bruce Fields\ 		warn_gssd();
5846b2e6856STrond Myklebust 		rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue,
5856b2e6856STrond Myklebust 				task, NULL, jiffies + (15 * HZ));
5860c77668dSChuck Lever 		err = -EAGAIN;
5870c77668dSChuck Lever 		goto out;
58879a3f20bS\"J. Bruce Fields\ 	}
5891da177e4SLinus Torvalds 	if (IS_ERR(gss_msg)) {
5901da177e4SLinus Torvalds 		err = PTR_ERR(gss_msg);
5911da177e4SLinus Torvalds 		goto out;
5921da177e4SLinus Torvalds 	}
5939beae467SStanislav Kinsbursky 	pipe = gss_msg->pipe;
5949beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
5951da177e4SLinus Torvalds 	if (gss_cred->gc_upcall != NULL)
5965d00837bSTrond Myklebust 		rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
597126e216aSTrond Myklebust 	else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
5981da177e4SLinus Torvalds 		gss_cred->gc_upcall = gss_msg;
5991da177e4SLinus Torvalds 		/* gss_upcall_callback will release the reference to gss_upcall_msg */
6007ff13969SReshetova, Elena 		refcount_inc(&gss_msg->count);
6015d00837bSTrond Myklebust 		rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
602126e216aSTrond Myklebust 	} else {
603126e216aSTrond Myklebust 		gss_handle_downcall_result(gss_cred, gss_msg);
6041da177e4SLinus Torvalds 		err = gss_msg->msg.errno;
605126e216aSTrond Myklebust 	}
6069beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
6071da177e4SLinus Torvalds 	gss_release_msg(gss_msg);
6081da177e4SLinus Torvalds out:
6090c77668dSChuck Lever 	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
6100c77668dSChuck Lever 					     cred->cr_cred->fsuid), err);
6111da177e4SLinus Torvalds 	return err;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds static inline int
6151da177e4SLinus Torvalds gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
6161da177e4SLinus Torvalds {
617e726340aSTrond Myklebust 	struct net *net = gss_auth->net;
618abfdbd53STrond Myklebust 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6199beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe;
6201da177e4SLinus Torvalds 	struct rpc_cred *cred = &gss_cred->gc_base;
6211da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg;
6221da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
623d36ccb9cSTrond Myklebust 	int err;
6241da177e4SLinus Torvalds 
62579a3f20bS\"J. Bruce Fields\ retry:
626d36ccb9cSTrond Myklebust 	err = 0;
62789f84243SJeff Layton 	/* if gssd is down, just skip upcalling altogether */
62889f84243SJeff Layton 	if (!gssd_running(net)) {
62989f84243SJeff Layton 		warn_gssd();
6300c77668dSChuck Lever 		err = -EACCES;
6310c77668dSChuck Lever 		goto out;
63289f84243SJeff Layton 	}
633e726340aSTrond Myklebust 	gss_msg = gss_setup_upcall(gss_auth, cred);
63479a3f20bS\"J. Bruce Fields\ 	if (PTR_ERR(gss_msg) == -EAGAIN) {
63579a3f20bS\"J. Bruce Fields\ 		err = wait_event_interruptible_timeout(pipe_version_waitqueue,
63689f84243SJeff Layton 				sn->pipe_version >= 0, 15 * HZ);
6372aed8b47STrond Myklebust 		if (sn->pipe_version < 0) {
638d1a8016aSBryan Schumaker 			warn_gssd();
639d1a8016aSBryan Schumaker 			err = -EACCES;
640d1a8016aSBryan Schumaker 		}
641d36ccb9cSTrond Myklebust 		if (err < 0)
64279a3f20bS\"J. Bruce Fields\ 			goto out;
64379a3f20bS\"J. Bruce Fields\ 		goto retry;
64479a3f20bS\"J. Bruce Fields\ 	}
6451da177e4SLinus Torvalds 	if (IS_ERR(gss_msg)) {
6461da177e4SLinus Torvalds 		err = PTR_ERR(gss_msg);
6471da177e4SLinus Torvalds 		goto out;
6481da177e4SLinus Torvalds 	}
6499beae467SStanislav Kinsbursky 	pipe = gss_msg->pipe;
6501da177e4SLinus Torvalds 	for (;;) {
6515afa9133STrond Myklebust 		prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE);
6529beae467SStanislav Kinsbursky 		spin_lock(&pipe->lock);
6531da177e4SLinus Torvalds 		if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {
6541da177e4SLinus Torvalds 			break;
6551da177e4SLinus Torvalds 		}
6569beae467SStanislav Kinsbursky 		spin_unlock(&pipe->lock);
6575afa9133STrond Myklebust 		if (fatal_signal_pending(current)) {
6581da177e4SLinus Torvalds 			err = -ERESTARTSYS;
6591da177e4SLinus Torvalds 			goto out_intr;
6601da177e4SLinus Torvalds 		}
6611da177e4SLinus Torvalds 		schedule();
6621da177e4SLinus Torvalds 	}
6631da177e4SLinus Torvalds 	if (gss_msg->ctx)
6647b6962b0STrond Myklebust 		gss_cred_set_ctx(cred, gss_msg->ctx);
6651da177e4SLinus Torvalds 	else
6661da177e4SLinus Torvalds 		err = gss_msg->msg.errno;
6679beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
6681da177e4SLinus Torvalds out_intr:
6691da177e4SLinus Torvalds 	finish_wait(&gss_msg->waitqueue, &wait);
6701da177e4SLinus Torvalds 	gss_release_msg(gss_msg);
6711da177e4SLinus Torvalds out:
6720c77668dSChuck Lever 	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
6730c77668dSChuck Lever 					     cred->cr_cred->fsuid), err);
6741da177e4SLinus Torvalds 	return err;
6751da177e4SLinus Torvalds }
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds #define MSG_BUF_MAXSIZE 1024
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds static ssize_t
6801da177e4SLinus Torvalds gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
6811da177e4SLinus Torvalds {
6821da177e4SLinus Torvalds 	const void *p, *end;
6831da177e4SLinus Torvalds 	void *buf;
6841da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg;
685496ad9aaSAl Viro 	struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe;
6861da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx;
68790602c7bSEric W. Biederman 	uid_t id;
68890602c7bSEric W. Biederman 	kuid_t uid;
6893b68aaeaSTrond Myklebust 	ssize_t err = -EFBIG;
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	if (mlen > MSG_BUF_MAXSIZE)
6921da177e4SLinus Torvalds 		goto out;
6931da177e4SLinus Torvalds 	err = -ENOMEM;
6940f38b873STrond Myklebust 	buf = kmalloc(mlen, GFP_NOFS);
6951da177e4SLinus Torvalds 	if (!buf)
6961da177e4SLinus Torvalds 		goto out;
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	err = -EFAULT;
6991da177e4SLinus Torvalds 	if (copy_from_user(buf, src, mlen))
7001da177e4SLinus Torvalds 		goto err;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	end = (const void *)((char *)buf + mlen);
70390602c7bSEric W. Biederman 	p = simple_get_bytes(buf, end, &id, sizeof(id));
7041da177e4SLinus Torvalds 	if (IS_ERR(p)) {
7051da177e4SLinus Torvalds 		err = PTR_ERR(p);
7061da177e4SLinus Torvalds 		goto err;
7071da177e4SLinus Torvalds 	}
7081da177e4SLinus Torvalds 
70990602c7bSEric W. Biederman 	uid = make_kuid(&init_user_ns, id);
71090602c7bSEric W. Biederman 	if (!uid_valid(uid)) {
71190602c7bSEric W. Biederman 		err = -EINVAL;
71290602c7bSEric W. Biederman 		goto err;
71390602c7bSEric W. Biederman 	}
71490602c7bSEric W. Biederman 
7151da177e4SLinus Torvalds 	err = -ENOMEM;
7161da177e4SLinus Torvalds 	ctx = gss_alloc_context();
7171da177e4SLinus Torvalds 	if (ctx == NULL)
7181da177e4SLinus Torvalds 		goto err;
7193b68aaeaSTrond Myklebust 
7203b68aaeaSTrond Myklebust 	err = -ENOENT;
7213b68aaeaSTrond Myklebust 	/* Find a matching upcall */
7229beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
7239130b8dbSOlga Kornievskaia 	gss_msg = __gss_find_upcall(pipe, uid, NULL);
7243b68aaeaSTrond Myklebust 	if (gss_msg == NULL) {
7259beae467SStanislav Kinsbursky 		spin_unlock(&pipe->lock);
7263b68aaeaSTrond Myklebust 		goto err_put_ctx;
7273b68aaeaSTrond Myklebust 	}
7283b68aaeaSTrond Myklebust 	list_del_init(&gss_msg->list);
7299beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
7303b68aaeaSTrond Myklebust 
7316e84c7b6STrond Myklebust 	p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);
7321da177e4SLinus Torvalds 	if (IS_ERR(p)) {
7331da177e4SLinus Torvalds 		err = PTR_ERR(p);
734486bad2eSJeff Layton 		switch (err) {
735486bad2eSJeff Layton 		case -EACCES:
736dc5ddce9SJeff Layton 		case -EKEYEXPIRED:
737486bad2eSJeff Layton 			gss_msg->msg.errno = err;
738486bad2eSJeff Layton 			err = mlen;
739486bad2eSJeff Layton 			break;
740486bad2eSJeff Layton 		case -EFAULT:
741486bad2eSJeff Layton 		case -ENOMEM:
742486bad2eSJeff Layton 		case -EINVAL:
743486bad2eSJeff Layton 		case -ENOSYS:
744486bad2eSJeff Layton 			gss_msg->msg.errno = -EAGAIN;
745486bad2eSJeff Layton 			break;
746486bad2eSJeff Layton 		default:
747486bad2eSJeff Layton 			printk(KERN_CRIT "%s: bad return from "
7486c853099SRandy Dunlap 				"gss_fill_context: %zd\n", __func__, err);
749437b300cSScott Mayhew 			gss_msg->msg.errno = -EIO;
750486bad2eSJeff Layton 		}
7513b68aaeaSTrond Myklebust 		goto err_release_msg;
7521da177e4SLinus Torvalds 	}
7531da177e4SLinus Torvalds 	gss_msg->ctx = gss_get_ctx(ctx);
7543b68aaeaSTrond Myklebust 	err = mlen;
7553b68aaeaSTrond Myklebust 
7563b68aaeaSTrond Myklebust err_release_msg:
7579beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
7581da177e4SLinus Torvalds 	__gss_unhash_msg(gss_msg);
7599beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
7601da177e4SLinus Torvalds 	gss_release_msg(gss_msg);
7611da177e4SLinus Torvalds err_put_ctx:
7621da177e4SLinus Torvalds 	gss_put_ctx(ctx);
7631da177e4SLinus Torvalds err:
7641da177e4SLinus Torvalds 	kfree(buf);
7651da177e4SLinus Torvalds out:
7661da177e4SLinus Torvalds 	return err;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
76934769fc4S\"J. Bruce Fields\ static int gss_pipe_open(struct inode *inode, int new_version)
770cf81939dS\"J. Bruce Fields\ {
7712aed8b47STrond Myklebust 	struct net *net = inode->i_sb->s_fs_info;
7722aed8b47STrond Myklebust 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
77334769fc4S\"J. Bruce Fields\ 	int ret = 0;
77434769fc4S\"J. Bruce Fields\ 
77579a3f20bS\"J. Bruce Fields\ 	spin_lock(&pipe_version_lock);
7762aed8b47STrond Myklebust 	if (sn->pipe_version < 0) {
77734769fc4S\"J. Bruce Fields\ 		/* First open of any gss pipe determines the version: */
7782aed8b47STrond Myklebust 		sn->pipe_version = new_version;
77979a3f20bS\"J. Bruce Fields\ 		rpc_wake_up(&pipe_version_rpc_waitqueue);
78079a3f20bS\"J. Bruce Fields\ 		wake_up(&pipe_version_waitqueue);
7812aed8b47STrond Myklebust 	} else if (sn->pipe_version != new_version) {
78234769fc4S\"J. Bruce Fields\ 		/* Trying to open a pipe of a different version */
78334769fc4S\"J. Bruce Fields\ 		ret = -EBUSY;
78434769fc4S\"J. Bruce Fields\ 		goto out;
78579a3f20bS\"J. Bruce Fields\ 	}
7862aed8b47STrond Myklebust 	atomic_inc(&sn->pipe_users);
78734769fc4S\"J. Bruce Fields\ out:
78879a3f20bS\"J. Bruce Fields\ 	spin_unlock(&pipe_version_lock);
78934769fc4S\"J. Bruce Fields\ 	return ret;
79034769fc4S\"J. Bruce Fields\ 
79134769fc4S\"J. Bruce Fields\ }
79234769fc4S\"J. Bruce Fields\ 
79334769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v0(struct inode *inode)
79434769fc4S\"J. Bruce Fields\ {
79534769fc4S\"J. Bruce Fields\ 	return gss_pipe_open(inode, 0);
79634769fc4S\"J. Bruce Fields\ }
79734769fc4S\"J. Bruce Fields\ 
79834769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v1(struct inode *inode)
79934769fc4S\"J. Bruce Fields\ {
80034769fc4S\"J. Bruce Fields\ 	return gss_pipe_open(inode, 1);
801cf81939dS\"J. Bruce Fields\ }
802cf81939dS\"J. Bruce Fields\ 
8031da177e4SLinus Torvalds static void
8041da177e4SLinus Torvalds gss_pipe_release(struct inode *inode)
8051da177e4SLinus Torvalds {
8062aed8b47STrond Myklebust 	struct net *net = inode->i_sb->s_fs_info;
8079beae467SStanislav Kinsbursky 	struct rpc_pipe *pipe = RPC_I(inode)->pipe;
8081da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg;
8091da177e4SLinus Torvalds 
8105a67657aSTrond Myklebust restart:
8119beae467SStanislav Kinsbursky 	spin_lock(&pipe->lock);
8129beae467SStanislav Kinsbursky 	list_for_each_entry(gss_msg, &pipe->in_downcall, list) {
8136e84c7b6STrond Myklebust 
8145a67657aSTrond Myklebust 		if (!list_empty(&gss_msg->msg.list))
8155a67657aSTrond Myklebust 			continue;
8161da177e4SLinus Torvalds 		gss_msg->msg.errno = -EPIPE;
8177ff13969SReshetova, Elena 		refcount_inc(&gss_msg->count);
8181da177e4SLinus Torvalds 		__gss_unhash_msg(gss_msg);
8199beae467SStanislav Kinsbursky 		spin_unlock(&pipe->lock);
8201da177e4SLinus Torvalds 		gss_release_msg(gss_msg);
8215a67657aSTrond Myklebust 		goto restart;
8221da177e4SLinus Torvalds 	}
8239beae467SStanislav Kinsbursky 	spin_unlock(&pipe->lock);
824cf81939dS\"J. Bruce Fields\ 
8252aed8b47STrond Myklebust 	put_pipe_version(net);
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds static void
8291da177e4SLinus Torvalds gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
8301da177e4SLinus Torvalds {
8311da177e4SLinus Torvalds 	struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds 	if (msg->errno < 0) {
8347ff13969SReshetova, Elena 		refcount_inc(&gss_msg->count);
8351da177e4SLinus Torvalds 		gss_unhash_msg(gss_msg);
836b03568c3S\"J. Bruce Fields\ 		if (msg->errno == -ETIMEDOUT)
837b03568c3S\"J. Bruce Fields\ 			warn_gssd();
8381da177e4SLinus Torvalds 		gss_release_msg(gss_msg);
8391da177e4SLinus Torvalds 	}
8401cded9d2SNeilBrown 	gss_release_msg(gss_msg);
8411da177e4SLinus Torvalds }
8421da177e4SLinus Torvalds 
84319172284STrond Myklebust static void gss_pipe_dentry_destroy(struct dentry *dir,
84419172284STrond Myklebust 		struct rpc_pipe_dir_object *pdo)
845ccdc28f8SStanislav Kinsbursky {
84619172284STrond Myklebust 	struct gss_pipe *gss_pipe = pdo->pdo_data;
84719172284STrond Myklebust 	struct rpc_pipe *pipe = gss_pipe->pipe;
848ccdc28f8SStanislav Kinsbursky 
84919172284STrond Myklebust 	if (pipe->dentry != NULL) {
85019172284STrond Myklebust 		rpc_unlink(pipe->dentry);
85119172284STrond Myklebust 		pipe->dentry = NULL;
8526b2fddd3STrond Myklebust 	}
853ccdc28f8SStanislav Kinsbursky }
854ccdc28f8SStanislav Kinsbursky 
85519172284STrond Myklebust static int gss_pipe_dentry_create(struct dentry *dir,
85619172284STrond Myklebust 		struct rpc_pipe_dir_object *pdo)
857ccdc28f8SStanislav Kinsbursky {
85819172284STrond Myklebust 	struct gss_pipe *p = pdo->pdo_data;
8596b2fddd3STrond Myklebust 	struct dentry *dentry;
860ccdc28f8SStanislav Kinsbursky 
86119172284STrond Myklebust 	dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
86219172284STrond Myklebust 	if (IS_ERR(dentry))
86319172284STrond Myklebust 		return PTR_ERR(dentry);
86419172284STrond Myklebust 	p->pipe->dentry = dentry;
865ccdc28f8SStanislav Kinsbursky 	return 0;
86619172284STrond Myklebust }
867ccdc28f8SStanislav Kinsbursky 
86819172284STrond Myklebust static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
86919172284STrond Myklebust 	.create = gss_pipe_dentry_create,
87019172284STrond Myklebust 	.destroy = gss_pipe_dentry_destroy,
87119172284STrond Myklebust };
87219172284STrond Myklebust 
87319172284STrond Myklebust static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt,
87419172284STrond Myklebust 		const char *name,
87519172284STrond Myklebust 		const struct rpc_pipe_ops *upcall_ops)
87619172284STrond Myklebust {
87719172284STrond Myklebust 	struct gss_pipe *p;
87819172284STrond Myklebust 	int err = -ENOMEM;
87919172284STrond Myklebust 
88019172284STrond Myklebust 	p = kmalloc(sizeof(*p), GFP_KERNEL);
88119172284STrond Myklebust 	if (p == NULL)
88219172284STrond Myklebust 		goto err;
88319172284STrond Myklebust 	p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
88419172284STrond Myklebust 	if (IS_ERR(p->pipe)) {
88519172284STrond Myklebust 		err = PTR_ERR(p->pipe);
88619172284STrond Myklebust 		goto err_free_gss_pipe;
88719172284STrond Myklebust 	}
88819172284STrond Myklebust 	p->name = name;
88919172284STrond Myklebust 	p->clnt = clnt;
890414a6295STrond Myklebust 	kref_init(&p->kref);
89119172284STrond Myklebust 	rpc_init_pipe_dir_object(&p->pdo,
89219172284STrond Myklebust 			&gss_pipe_dir_object_ops,
89319172284STrond Myklebust 			p);
89419172284STrond Myklebust 	return p;
89519172284STrond Myklebust err_free_gss_pipe:
89619172284STrond Myklebust 	kfree(p);
8976b2fddd3STrond Myklebust err:
89819172284STrond Myklebust 	return ERR_PTR(err);
899ccdc28f8SStanislav Kinsbursky }
900ccdc28f8SStanislav Kinsbursky 
901414a6295STrond Myklebust struct gss_alloc_pdo {
902414a6295STrond Myklebust 	struct rpc_clnt *clnt;
903414a6295STrond Myklebust 	const char *name;
904414a6295STrond Myklebust 	const struct rpc_pipe_ops *upcall_ops;
905414a6295STrond Myklebust };
906414a6295STrond Myklebust 
907414a6295STrond Myklebust static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data)
908414a6295STrond Myklebust {
909414a6295STrond Myklebust 	struct gss_pipe *gss_pipe;
910414a6295STrond Myklebust 	struct gss_alloc_pdo *args = data;
911414a6295STrond Myklebust 
912414a6295STrond Myklebust 	if (pdo->pdo_ops != &gss_pipe_dir_object_ops)
913414a6295STrond Myklebust 		return 0;
914414a6295STrond Myklebust 	gss_pipe = container_of(pdo, struct gss_pipe, pdo);
915414a6295STrond Myklebust 	if (strcmp(gss_pipe->name, args->name) != 0)
916414a6295STrond Myklebust 		return 0;
917414a6295STrond Myklebust 	if (!kref_get_unless_zero(&gss_pipe->kref))
918414a6295STrond Myklebust 		return 0;
919414a6295STrond Myklebust 	return 1;
920414a6295STrond Myklebust }
921414a6295STrond Myklebust 
922414a6295STrond Myklebust static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data)
923414a6295STrond Myklebust {
924414a6295STrond Myklebust 	struct gss_pipe *gss_pipe;
925414a6295STrond Myklebust 	struct gss_alloc_pdo *args = data;
926414a6295STrond Myklebust 
927414a6295STrond Myklebust 	gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops);
928414a6295STrond Myklebust 	if (!IS_ERR(gss_pipe))
929414a6295STrond Myklebust 		return &gss_pipe->pdo;
930414a6295STrond Myklebust 	return NULL;
931414a6295STrond Myklebust }
932414a6295STrond Myklebust 
933414a6295STrond Myklebust static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt,
934414a6295STrond Myklebust 		const char *name,
935414a6295STrond Myklebust 		const struct rpc_pipe_ops *upcall_ops)
936414a6295STrond Myklebust {
937414a6295STrond Myklebust 	struct net *net = rpc_net_ns(clnt);
938414a6295STrond Myklebust 	struct rpc_pipe_dir_object *pdo;
939414a6295STrond Myklebust 	struct gss_alloc_pdo args = {
940414a6295STrond Myklebust 		.clnt = clnt,
941414a6295STrond Myklebust 		.name = name,
942414a6295STrond Myklebust 		.upcall_ops = upcall_ops,
943414a6295STrond Myklebust 	};
944414a6295STrond Myklebust 
945414a6295STrond Myklebust 	pdo = rpc_find_or_alloc_pipe_dir_object(net,
946414a6295STrond Myklebust 			&clnt->cl_pipedir_objects,
947414a6295STrond Myklebust 			gss_pipe_match_pdo,
948414a6295STrond Myklebust 			gss_pipe_alloc_pdo,
949414a6295STrond Myklebust 			&args);
950414a6295STrond Myklebust 	if (pdo != NULL)
951414a6295STrond Myklebust 		return container_of(pdo, struct gss_pipe, pdo);
952414a6295STrond Myklebust 	return ERR_PTR(-ENOMEM);
953414a6295STrond Myklebust }
954414a6295STrond Myklebust 
95519172284STrond Myklebust static void __gss_pipe_free(struct gss_pipe *p)
956ccdc28f8SStanislav Kinsbursky {
95719172284STrond Myklebust 	struct rpc_clnt *clnt = p->clnt;
95819172284STrond Myklebust 	struct net *net = rpc_net_ns(clnt);
959ccdc28f8SStanislav Kinsbursky 
96019172284STrond Myklebust 	rpc_remove_pipe_dir_object(net,
96119172284STrond Myklebust 			&clnt->cl_pipedir_objects,
96219172284STrond Myklebust 			&p->pdo);
96319172284STrond Myklebust 	rpc_destroy_pipe_data(p->pipe);
96419172284STrond Myklebust 	kfree(p);
965ccdc28f8SStanislav Kinsbursky }
966ccdc28f8SStanislav Kinsbursky 
967414a6295STrond Myklebust static void __gss_pipe_release(struct kref *kref)
968414a6295STrond Myklebust {
969414a6295STrond Myklebust 	struct gss_pipe *p = container_of(kref, struct gss_pipe, kref);
970414a6295STrond Myklebust 
971414a6295STrond Myklebust 	__gss_pipe_free(p);
972414a6295STrond Myklebust }
973414a6295STrond Myklebust 
97419172284STrond Myklebust static void gss_pipe_free(struct gss_pipe *p)
975ccdc28f8SStanislav Kinsbursky {
97619172284STrond Myklebust 	if (p != NULL)
977414a6295STrond Myklebust 		kref_put(&p->kref, __gss_pipe_release);
978ccdc28f8SStanislav Kinsbursky }
979ccdc28f8SStanislav Kinsbursky 
9801da177e4SLinus Torvalds /*
9811da177e4SLinus Torvalds  * NOTE: we have the opportunity to use different
9821da177e4SLinus Torvalds  * parameters based on the input flavor (which must be a pseudoflavor)
9831da177e4SLinus Torvalds  */
984eb6dc19dSTrond Myklebust static struct gss_auth *
98582b98ca5SSargun Dhillon gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
9861da177e4SLinus Torvalds {
987c2190661STrond Myklebust 	rpc_authflavor_t flavor = args->pseudoflavor;
9881da177e4SLinus Torvalds 	struct gss_auth *gss_auth;
98919172284STrond Myklebust 	struct gss_pipe *gss_pipe;
9901da177e4SLinus Torvalds 	struct rpc_auth * auth;
9916a19275aSJ. Bruce Fields 	int err = -ENOMEM; /* XXX? */
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	if (!try_module_get(THIS_MODULE))
9946a19275aSJ. Bruce Fields 		return ERR_PTR(err);
9951da177e4SLinus Torvalds 	if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
9961da177e4SLinus Torvalds 		goto out_dec;
997eb6dc19dSTrond Myklebust 	INIT_HLIST_NODE(&gss_auth->hash);
998bd4a3eb1STrond Myklebust 	gss_auth->target_name = NULL;
999c2190661STrond Myklebust 	if (args->target_name) {
1000c2190661STrond Myklebust 		gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
1001bd4a3eb1STrond Myklebust 		if (gss_auth->target_name == NULL)
1002bd4a3eb1STrond Myklebust 			goto err_free;
1003bd4a3eb1STrond Myklebust 	}
10041da177e4SLinus Torvalds 	gss_auth->client = clnt;
1005e726340aSTrond Myklebust 	gss_auth->net = get_net(rpc_net_ns(clnt));
10066a19275aSJ. Bruce Fields 	err = -EINVAL;
10071da177e4SLinus Torvalds 	gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
10080c77668dSChuck Lever 	if (!gss_auth->mech)
1009e726340aSTrond Myklebust 		goto err_put_net;
10101da177e4SLinus Torvalds 	gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
1011438b6fdeSJ. Bruce Fields 	if (gss_auth->service == 0)
1012438b6fdeSJ. Bruce Fields 		goto err_put_mech;
1013a699d65eSTrond Myklebust 	if (!gssd_running(gss_auth->net))
1014a699d65eSTrond Myklebust 		goto err_put_mech;
10151da177e4SLinus Torvalds 	auth = &gss_auth->rpc_auth;
10161da177e4SLinus Torvalds 	auth->au_cslack = GSS_CRED_SLACK >> 2;
10171da177e4SLinus Torvalds 	auth->au_rslack = GSS_VERF_SLACK >> 2;
1018a00275baSChuck Lever 	auth->au_verfsize = GSS_VERF_SLACK >> 2;
101935e77d21SChuck Lever 	auth->au_ralign = GSS_VERF_SLACK >> 2;
1020ce52914eSScott Mayhew 	auth->au_flags = 0;
10211da177e4SLinus Torvalds 	auth->au_ops = &authgss_ops;
10221da177e4SLinus Torvalds 	auth->au_flavor = flavor;
102365b80179SChuck Lever 	if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor))
102465b80179SChuck Lever 		auth->au_flags |= RPCAUTH_AUTH_DATATOUCH;
1025331bc71cSTrond Myklebust 	refcount_set(&auth->au_count, 1);
10260285ed1fSTrond Myklebust 	kref_init(&gss_auth->kref);
10271da177e4SLinus Torvalds 
102819172284STrond Myklebust 	err = rpcauth_init_credcache(auth);
102919172284STrond Myklebust 	if (err)
103019172284STrond Myklebust 		goto err_put_mech;
103134769fc4S\"J. Bruce Fields\ 	/*
103234769fc4S\"J. Bruce Fields\ 	 * Note: if we created the old pipe first, then someone who
103334769fc4S\"J. Bruce Fields\ 	 * examined the directory at the right moment might conclude
103434769fc4S\"J. Bruce Fields\ 	 * that we supported only the old pipe.  So we instead create
103534769fc4S\"J. Bruce Fields\ 	 * the new pipe first.
103634769fc4S\"J. Bruce Fields\ 	 */
1037414a6295STrond Myklebust 	gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
103819172284STrond Myklebust 	if (IS_ERR(gss_pipe)) {
103919172284STrond Myklebust 		err = PTR_ERR(gss_pipe);
104019172284STrond Myklebust 		goto err_destroy_credcache;
10416a19275aSJ. Bruce Fields 	}
104219172284STrond Myklebust 	gss_auth->gss_pipe[1] = gss_pipe;
10431da177e4SLinus Torvalds 
1044414a6295STrond Myklebust 	gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
104519172284STrond Myklebust 			&gss_upcall_ops_v0);
104619172284STrond Myklebust 	if (IS_ERR(gss_pipe)) {
104719172284STrond Myklebust 		err = PTR_ERR(gss_pipe);
1048c239d83bSStanislav Kinsbursky 		goto err_destroy_pipe_1;
1049c239d83bSStanislav Kinsbursky 	}
105019172284STrond Myklebust 	gss_auth->gss_pipe[0] = gss_pipe;
105107a2bf1dSTrond Myklebust 
1052eb6dc19dSTrond Myklebust 	return gss_auth;
1053c239d83bSStanislav Kinsbursky err_destroy_pipe_1:
1054414a6295STrond Myklebust 	gss_pipe_free(gss_auth->gss_pipe[1]);
105519172284STrond Myklebust err_destroy_credcache:
105619172284STrond Myklebust 	rpcauth_destroy_credcache(auth);
10571da177e4SLinus Torvalds err_put_mech:
10581da177e4SLinus Torvalds 	gss_mech_put(gss_auth->mech);
1059e726340aSTrond Myklebust err_put_net:
1060e726340aSTrond Myklebust 	put_net(gss_auth->net);
10611da177e4SLinus Torvalds err_free:
1062bd4a3eb1STrond Myklebust 	kfree(gss_auth->target_name);
10631da177e4SLinus Torvalds 	kfree(gss_auth);
10641da177e4SLinus Torvalds out_dec:
10651da177e4SLinus Torvalds 	module_put(THIS_MODULE);
10660c77668dSChuck Lever 	trace_rpcgss_createauth(flavor, err);
10676a19275aSJ. Bruce Fields 	return ERR_PTR(err);
10681da177e4SLinus Torvalds }
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds static void
10710285ed1fSTrond Myklebust gss_free(struct gss_auth *gss_auth)
10720285ed1fSTrond Myklebust {
107319172284STrond Myklebust 	gss_pipe_free(gss_auth->gss_pipe[0]);
107419172284STrond Myklebust 	gss_pipe_free(gss_auth->gss_pipe[1]);
10750285ed1fSTrond Myklebust 	gss_mech_put(gss_auth->mech);
1076e726340aSTrond Myklebust 	put_net(gss_auth->net);
1077bd4a3eb1STrond Myklebust 	kfree(gss_auth->target_name);
10780285ed1fSTrond Myklebust 
10790285ed1fSTrond Myklebust 	kfree(gss_auth);
10800285ed1fSTrond Myklebust 	module_put(THIS_MODULE);
10810285ed1fSTrond Myklebust }
10820285ed1fSTrond Myklebust 
10830285ed1fSTrond Myklebust static void
10840285ed1fSTrond Myklebust gss_free_callback(struct kref *kref)
10850285ed1fSTrond Myklebust {
10860285ed1fSTrond Myklebust 	struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref);
10870285ed1fSTrond Myklebust 
10880285ed1fSTrond Myklebust 	gss_free(gss_auth);
10890285ed1fSTrond Myklebust }
10900285ed1fSTrond Myklebust 
10910285ed1fSTrond Myklebust static void
10929eb2ddb4STrond Myklebust gss_put_auth(struct gss_auth *gss_auth)
10939eb2ddb4STrond Myklebust {
10949eb2ddb4STrond Myklebust 	kref_put(&gss_auth->kref, gss_free_callback);
10959eb2ddb4STrond Myklebust }
10969eb2ddb4STrond Myklebust 
10979eb2ddb4STrond Myklebust static void
10981da177e4SLinus Torvalds gss_destroy(struct rpc_auth *auth)
10991da177e4SLinus Torvalds {
110019172284STrond Myklebust 	struct gss_auth *gss_auth = container_of(auth,
110119172284STrond Myklebust 			struct gss_auth, rpc_auth);
11021da177e4SLinus Torvalds 
1103eb6dc19dSTrond Myklebust 	if (hash_hashed(&gss_auth->hash)) {
1104eb6dc19dSTrond Myklebust 		spin_lock(&gss_auth_hash_lock);
1105eb6dc19dSTrond Myklebust 		hash_del(&gss_auth->hash);
1106eb6dc19dSTrond Myklebust 		spin_unlock(&gss_auth_hash_lock);
1107eb6dc19dSTrond Myklebust 	}
1108eb6dc19dSTrond Myklebust 
110919172284STrond Myklebust 	gss_pipe_free(gss_auth->gss_pipe[0]);
111019172284STrond Myklebust 	gss_auth->gss_pipe[0] = NULL;
111119172284STrond Myklebust 	gss_pipe_free(gss_auth->gss_pipe[1]);
111219172284STrond Myklebust 	gss_auth->gss_pipe[1] = NULL;
11133ab9bb72STrond Myklebust 	rpcauth_destroy_credcache(auth);
11143ab9bb72STrond Myklebust 
11159eb2ddb4STrond Myklebust 	gss_put_auth(gss_auth);
11161da177e4SLinus Torvalds }
11171da177e4SLinus Torvalds 
1118a0f6ed8eSJ. Bruce Fields /*
1119a0f6ed8eSJ. Bruce Fields  * Auths may be shared between rpc clients that were cloned from a
1120a0f6ed8eSJ. Bruce Fields  * common client with the same xprt, if they also share the flavor and
1121a0f6ed8eSJ. Bruce Fields  * target_name.
1122a0f6ed8eSJ. Bruce Fields  *
1123a0f6ed8eSJ. Bruce Fields  * The auth is looked up from the oldest parent sharing the same
1124a0f6ed8eSJ. Bruce Fields  * cl_xprt, and the auth itself references only that common parent
1125a0f6ed8eSJ. Bruce Fields  * (which is guaranteed to last as long as any of its descendants).
1126a0f6ed8eSJ. Bruce Fields  */
1127eb6dc19dSTrond Myklebust static struct gss_auth *
112882b98ca5SSargun Dhillon gss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args,
1129eb6dc19dSTrond Myklebust 		struct rpc_clnt *clnt,
1130eb6dc19dSTrond Myklebust 		struct gss_auth *new)
1131eb6dc19dSTrond Myklebust {
1132eb6dc19dSTrond Myklebust 	struct gss_auth *gss_auth;
1133eb6dc19dSTrond Myklebust 	unsigned long hashval = (unsigned long)clnt;
1134eb6dc19dSTrond Myklebust 
1135eb6dc19dSTrond Myklebust 	spin_lock(&gss_auth_hash_lock);
1136eb6dc19dSTrond Myklebust 	hash_for_each_possible(gss_auth_hash_table,
1137eb6dc19dSTrond Myklebust 			gss_auth,
1138eb6dc19dSTrond Myklebust 			hash,
1139eb6dc19dSTrond Myklebust 			hashval) {
1140a0f6ed8eSJ. Bruce Fields 		if (gss_auth->client != clnt)
1141a0f6ed8eSJ. Bruce Fields 			continue;
1142eb6dc19dSTrond Myklebust 		if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
1143eb6dc19dSTrond Myklebust 			continue;
1144eb6dc19dSTrond Myklebust 		if (gss_auth->target_name != args->target_name) {
1145eb6dc19dSTrond Myklebust 			if (gss_auth->target_name == NULL)
1146eb6dc19dSTrond Myklebust 				continue;
1147eb6dc19dSTrond Myklebust 			if (args->target_name == NULL)
1148eb6dc19dSTrond Myklebust 				continue;
1149eb6dc19dSTrond Myklebust 			if (strcmp(gss_auth->target_name, args->target_name))
1150eb6dc19dSTrond Myklebust 				continue;
1151eb6dc19dSTrond Myklebust 		}
1152331bc71cSTrond Myklebust 		if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count))
1153eb6dc19dSTrond Myklebust 			continue;
1154eb6dc19dSTrond Myklebust 		goto out;
1155eb6dc19dSTrond Myklebust 	}
1156eb6dc19dSTrond Myklebust 	if (new)
1157eb6dc19dSTrond Myklebust 		hash_add(gss_auth_hash_table, &new->hash, hashval);
1158eb6dc19dSTrond Myklebust 	gss_auth = new;
1159eb6dc19dSTrond Myklebust out:
1160eb6dc19dSTrond Myklebust 	spin_unlock(&gss_auth_hash_lock);
1161eb6dc19dSTrond Myklebust 	return gss_auth;
1162eb6dc19dSTrond Myklebust }
1163eb6dc19dSTrond Myklebust 
1164eb6dc19dSTrond Myklebust static struct gss_auth *
116582b98ca5SSargun Dhillon gss_create_hashed(const struct rpc_auth_create_args *args,
116682b98ca5SSargun Dhillon 		  struct rpc_clnt *clnt)
1167eb6dc19dSTrond Myklebust {
1168eb6dc19dSTrond Myklebust 	struct gss_auth *gss_auth;
1169eb6dc19dSTrond Myklebust 	struct gss_auth *new;
1170eb6dc19dSTrond Myklebust 
1171eb6dc19dSTrond Myklebust 	gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
1172eb6dc19dSTrond Myklebust 	if (gss_auth != NULL)
1173eb6dc19dSTrond Myklebust 		goto out;
1174eb6dc19dSTrond Myklebust 	new = gss_create_new(args, clnt);
1175eb6dc19dSTrond Myklebust 	if (IS_ERR(new))
1176eb6dc19dSTrond Myklebust 		return new;
1177eb6dc19dSTrond Myklebust 	gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
1178eb6dc19dSTrond Myklebust 	if (gss_auth != new)
1179eb6dc19dSTrond Myklebust 		gss_destroy(&new->rpc_auth);
1180eb6dc19dSTrond Myklebust out:
1181eb6dc19dSTrond Myklebust 	return gss_auth;
1182eb6dc19dSTrond Myklebust }
1183eb6dc19dSTrond Myklebust 
1184eb6dc19dSTrond Myklebust static struct rpc_auth *
118582b98ca5SSargun Dhillon gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
1186eb6dc19dSTrond Myklebust {
1187eb6dc19dSTrond Myklebust 	struct gss_auth *gss_auth;
1188ad01b2c6STrond Myklebust 	struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch);
1189eb6dc19dSTrond Myklebust 
1190eb6dc19dSTrond Myklebust 	while (clnt != clnt->cl_parent) {
1191eb6dc19dSTrond Myklebust 		struct rpc_clnt *parent = clnt->cl_parent;
1192eb6dc19dSTrond Myklebust 		/* Find the original parent for this transport */
1193ad01b2c6STrond Myklebust 		if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps)
1194eb6dc19dSTrond Myklebust 			break;
1195eb6dc19dSTrond Myklebust 		clnt = parent;
1196eb6dc19dSTrond Myklebust 	}
1197eb6dc19dSTrond Myklebust 
1198eb6dc19dSTrond Myklebust 	gss_auth = gss_create_hashed(args, clnt);
1199eb6dc19dSTrond Myklebust 	if (IS_ERR(gss_auth))
1200eb6dc19dSTrond Myklebust 		return ERR_CAST(gss_auth);
1201eb6dc19dSTrond Myklebust 	return &gss_auth->rpc_auth;
1202eb6dc19dSTrond Myklebust }
1203eb6dc19dSTrond Myklebust 
1204a652a4bcSTrond Myklebust static struct gss_cred *
1205a652a4bcSTrond Myklebust gss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
1206a652a4bcSTrond Myklebust {
1207a652a4bcSTrond Myklebust 	struct gss_cred *new;
1208a652a4bcSTrond Myklebust 
1209a652a4bcSTrond Myklebust 	/* Make a copy of the cred so that we can reference count it */
121012a3ad61STrond Myklebust 	new = kzalloc(sizeof(*gss_cred), GFP_NOFS);
1211a652a4bcSTrond Myklebust 	if (new) {
1212a652a4bcSTrond Myklebust 		struct auth_cred acred = {
12138276c902SNeilBrown 			.cred = gss_cred->gc_base.cr_cred,
1214a652a4bcSTrond Myklebust 		};
1215a652a4bcSTrond Myklebust 		struct gss_cl_ctx *ctx =
1216a652a4bcSTrond Myklebust 			rcu_dereference_protected(gss_cred->gc_ctx, 1);
1217a652a4bcSTrond Myklebust 
1218a652a4bcSTrond Myklebust 		rpcauth_init_cred(&new->gc_base, &acred,
1219a652a4bcSTrond Myklebust 				&gss_auth->rpc_auth,
1220a652a4bcSTrond Myklebust 				&gss_nullops);
1221a652a4bcSTrond Myklebust 		new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
1222a652a4bcSTrond Myklebust 		new->gc_service = gss_cred->gc_service;
1223a652a4bcSTrond Myklebust 		new->gc_principal = gss_cred->gc_principal;
1224a652a4bcSTrond Myklebust 		kref_get(&gss_auth->kref);
1225a652a4bcSTrond Myklebust 		rcu_assign_pointer(new->gc_ctx, ctx);
1226a652a4bcSTrond Myklebust 		gss_get_ctx(ctx);
1227a652a4bcSTrond Myklebust 	}
1228a652a4bcSTrond Myklebust 	return new;
1229a652a4bcSTrond Myklebust }
1230a652a4bcSTrond Myklebust 
12310df7fb74STrond Myklebust /*
1232a652a4bcSTrond Myklebust  * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call
12330df7fb74STrond Myklebust  * to the server with the GSS control procedure field set to
12340df7fb74STrond Myklebust  * RPC_GSS_PROC_DESTROY. This should normally cause the server to release
12350df7fb74STrond Myklebust  * all RPCSEC_GSS state associated with that context.
12360df7fb74STrond Myklebust  */
1237a652a4bcSTrond Myklebust static void
1238a652a4bcSTrond Myklebust gss_send_destroy_context(struct rpc_cred *cred)
12390df7fb74STrond Myklebust {
12400df7fb74STrond Myklebust 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
12410df7fb74STrond Myklebust 	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
1242c5e6aecdSJeff Layton 	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
1243a652a4bcSTrond Myklebust 	struct gss_cred *new;
12440df7fb74STrond Myklebust 	struct rpc_task *task;
12450df7fb74STrond Myklebust 
1246a652a4bcSTrond Myklebust 	new = gss_dup_cred(gss_auth, gss_cred);
1247a652a4bcSTrond Myklebust 	if (new) {
1248c5e6aecdSJeff Layton 		ctx->gc_proc = RPC_GSS_PROC_DESTROY;
12490df7fb74STrond Myklebust 
1250a652a4bcSTrond Myklebust 		task = rpc_call_null(gss_auth->client, &new->gc_base,
1251a652a4bcSTrond Myklebust 				RPC_TASK_ASYNC|RPC_TASK_SOFT);
12520df7fb74STrond Myklebust 		if (!IS_ERR(task))
12530df7fb74STrond Myklebust 			rpc_put_task(task);
12540df7fb74STrond Myklebust 
1255a652a4bcSTrond Myklebust 		put_rpccred(&new->gc_base);
1256a652a4bcSTrond Myklebust 	}
12570df7fb74STrond Myklebust }
12580df7fb74STrond Myklebust 
12590df7fb74STrond Myklebust /* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
12601da177e4SLinus Torvalds  * to create a new cred or context, so they check that things have been
12611da177e4SLinus Torvalds  * allocated before freeing them. */
12621da177e4SLinus Torvalds static void
12635d28dc82STrond Myklebust gss_do_free_ctx(struct gss_cl_ctx *ctx)
12641da177e4SLinus Torvalds {
12650d8a3746STrond Myklebust 	gss_delete_sec_context(&ctx->gc_gss_ctx);
12661da177e4SLinus Torvalds 	kfree(ctx->gc_wire_ctx.data);
12672004c726SJeff Layton 	kfree(ctx->gc_acceptor.data);
12681da177e4SLinus Torvalds 	kfree(ctx);
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds static void
12725d28dc82STrond Myklebust gss_free_ctx_callback(struct rcu_head *head)
12735d28dc82STrond Myklebust {
12745d28dc82STrond Myklebust 	struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu);
12755d28dc82STrond Myklebust 	gss_do_free_ctx(ctx);
12765d28dc82STrond Myklebust }
12775d28dc82STrond Myklebust 
12785d28dc82STrond Myklebust static void
12795d28dc82STrond Myklebust gss_free_ctx(struct gss_cl_ctx *ctx)
12805d28dc82STrond Myklebust {
12815d28dc82STrond Myklebust 	call_rcu(&ctx->gc_rcu, gss_free_ctx_callback);
12825d28dc82STrond Myklebust }
12835d28dc82STrond Myklebust 
12845d28dc82STrond Myklebust static void
128531be5bf1STrond Myklebust gss_free_cred(struct gss_cred *gss_cred)
12861da177e4SLinus Torvalds {
128731be5bf1STrond Myklebust 	kfree(gss_cred);
128831be5bf1STrond Myklebust }
12891da177e4SLinus Torvalds 
129031be5bf1STrond Myklebust static void
129131be5bf1STrond Myklebust gss_free_cred_callback(struct rcu_head *head)
129231be5bf1STrond Myklebust {
129331be5bf1STrond Myklebust 	struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu);
129431be5bf1STrond Myklebust 	gss_free_cred(gss_cred);
129531be5bf1STrond Myklebust }
12961da177e4SLinus Torvalds 
129731be5bf1STrond Myklebust static void
12986dcd3926SJeff Layton gss_destroy_nullcred(struct rpc_cred *cred)
129931be5bf1STrond Myklebust {
13005d28dc82STrond Myklebust 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
13010285ed1fSTrond Myklebust 	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
1302c5e6aecdSJeff Layton 	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
13035d28dc82STrond Myklebust 
1304a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(gss_cred->gc_ctx, NULL);
130597f68c6bSNeilBrown 	put_cred(cred->cr_cred);
130631be5bf1STrond Myklebust 	call_rcu(&cred->cr_rcu, gss_free_cred_callback);
13075d28dc82STrond Myklebust 	if (ctx)
13085d28dc82STrond Myklebust 		gss_put_ctx(ctx);
13099eb2ddb4STrond Myklebust 	gss_put_auth(gss_auth);
13101da177e4SLinus Torvalds }
13111da177e4SLinus Torvalds 
13126dcd3926SJeff Layton static void
13136dcd3926SJeff Layton gss_destroy_cred(struct rpc_cred *cred)
13146dcd3926SJeff Layton {
13156dcd3926SJeff Layton 
1316a652a4bcSTrond Myklebust 	if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0)
1317a652a4bcSTrond Myklebust 		gss_send_destroy_context(cred);
13186dcd3926SJeff Layton 	gss_destroy_nullcred(cred);
13196dcd3926SJeff Layton }
13206dcd3926SJeff Layton 
1321a960f8d6SFrank Sorenson static int
1322a960f8d6SFrank Sorenson gss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
1323a960f8d6SFrank Sorenson {
13248276c902SNeilBrown 	return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits);
1325a960f8d6SFrank Sorenson }
1326a960f8d6SFrank Sorenson 
13271da177e4SLinus Torvalds /*
13281da177e4SLinus Torvalds  * Lookup RPCSEC_GSS cred for the current process
13291da177e4SLinus Torvalds  */
13301da177e4SLinus Torvalds static struct rpc_cred *
13318a317760STrond Myklebust gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
13321da177e4SLinus Torvalds {
13333c6e0bc8SJeff Layton 	return rpcauth_lookup_credcache(auth, acred, flags, GFP_NOFS);
13341da177e4SLinus Torvalds }
13351da177e4SLinus Torvalds 
13361da177e4SLinus Torvalds static struct rpc_cred *
13373c6e0bc8SJeff Layton gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
13381da177e4SLinus Torvalds {
13391da177e4SLinus Torvalds 	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
13401da177e4SLinus Torvalds 	struct gss_cred	*cred = NULL;
13411da177e4SLinus Torvalds 	int err = -ENOMEM;
13421da177e4SLinus Torvalds 
13433c6e0bc8SJeff Layton 	if (!(cred = kzalloc(sizeof(*cred), gfp)))
13441da177e4SLinus Torvalds 		goto out_err;
13451da177e4SLinus Torvalds 
13465fe4755eSTrond Myklebust 	rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
13471da177e4SLinus Torvalds 	/*
13481da177e4SLinus Torvalds 	 * Note: in order to force a call to call_refresh(), we deliberately
13491da177e4SLinus Torvalds 	 * fail to flag the credential as RPCAUTH_CRED_UPTODATE.
13501da177e4SLinus Torvalds 	 */
1351fc432dd9STrond Myklebust 	cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;
13521da177e4SLinus Torvalds 	cred->gc_service = gss_auth->service;
135368c97153STrond Myklebust 	cred->gc_principal = acred->principal;
13540285ed1fSTrond Myklebust 	kref_get(&gss_auth->kref);
13551da177e4SLinus Torvalds 	return &cred->gc_base;
13561da177e4SLinus Torvalds 
13571da177e4SLinus Torvalds out_err:
13581da177e4SLinus Torvalds 	return ERR_PTR(err);
13591da177e4SLinus Torvalds }
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds static int
1362fba3bad4STrond Myklebust gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
1363fba3bad4STrond Myklebust {
1364fba3bad4STrond Myklebust 	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
1365fba3bad4STrond Myklebust 	struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base);
1366fba3bad4STrond Myklebust 	int err;
1367fba3bad4STrond Myklebust 
1368fba3bad4STrond Myklebust 	do {
1369fba3bad4STrond Myklebust 		err = gss_create_upcall(gss_auth, gss_cred);
1370fba3bad4STrond Myklebust 	} while (err == -EAGAIN);
1371fba3bad4STrond Myklebust 	return err;
1372fba3bad4STrond Myklebust }
1373fba3bad4STrond Myklebust 
1374a0337d1dSJeff Layton static char *
1375a0337d1dSJeff Layton gss_stringify_acceptor(struct rpc_cred *cred)
1376a0337d1dSJeff Layton {
1377c5e6aecdSJeff Layton 	char *string = NULL;
1378a0337d1dSJeff Layton 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
1379c5e6aecdSJeff Layton 	struct gss_cl_ctx *ctx;
1380b3ecba09SJeff Layton 	unsigned int len;
1381c5e6aecdSJeff Layton 	struct xdr_netobj *acceptor;
1382c5e6aecdSJeff Layton 
1383c5e6aecdSJeff Layton 	rcu_read_lock();
1384c5e6aecdSJeff Layton 	ctx = rcu_dereference(gss_cred->gc_ctx);
1385c5e6aecdSJeff Layton 	if (!ctx)
1386c5e6aecdSJeff Layton 		goto out;
1387c5e6aecdSJeff Layton 
1388b3ecba09SJeff Layton 	len = ctx->gc_acceptor.len;
1389b3ecba09SJeff Layton 	rcu_read_unlock();
1390a0337d1dSJeff Layton 
1391a0337d1dSJeff Layton 	/* no point if there's no string */
1392b3ecba09SJeff Layton 	if (!len)
1393b3ecba09SJeff Layton 		return NULL;
1394b3ecba09SJeff Layton realloc:
1395b3ecba09SJeff Layton 	string = kmalloc(len + 1, GFP_KERNEL);
1396a0337d1dSJeff Layton 	if (!string)
1397b3ecba09SJeff Layton 		return NULL;
1398b3ecba09SJeff Layton 
1399b3ecba09SJeff Layton 	rcu_read_lock();
1400b3ecba09SJeff Layton 	ctx = rcu_dereference(gss_cred->gc_ctx);
1401b3ecba09SJeff Layton 
1402b3ecba09SJeff Layton 	/* did the ctx disappear or was it replaced by one with no acceptor? */
1403b3ecba09SJeff Layton 	if (!ctx || !ctx->gc_acceptor.len) {
1404b3ecba09SJeff Layton 		kfree(string);
1405b3ecba09SJeff Layton 		string = NULL;
1406c5e6aecdSJeff Layton 		goto out;
1407b3ecba09SJeff Layton 	}
1408b3ecba09SJeff Layton 
1409b3ecba09SJeff Layton 	acceptor = &ctx->gc_acceptor;
1410b3ecba09SJeff Layton 
1411b3ecba09SJeff Layton 	/*
1412b3ecba09SJeff Layton 	 * Did we find a new acceptor that's longer than the original? Allocate
1413b3ecba09SJeff Layton 	 * a longer buffer and try again.
1414b3ecba09SJeff Layton 	 */
1415b3ecba09SJeff Layton 	if (len < acceptor->len) {
1416b3ecba09SJeff Layton 		len = acceptor->len;
1417b3ecba09SJeff Layton 		rcu_read_unlock();
1418b3ecba09SJeff Layton 		kfree(string);
1419b3ecba09SJeff Layton 		goto realloc;
1420b3ecba09SJeff Layton 	}
1421a0337d1dSJeff Layton 
1422a0337d1dSJeff Layton 	memcpy(string, acceptor->data, acceptor->len);
1423a0337d1dSJeff Layton 	string[acceptor->len] = '\0';
1424c5e6aecdSJeff Layton out:
1425c5e6aecdSJeff Layton 	rcu_read_unlock();
1426a0337d1dSJeff Layton 	return string;
1427a0337d1dSJeff Layton }
1428a0337d1dSJeff Layton 
14294de6caa2SAndy Adamson /*
14304de6caa2SAndy Adamson  * Returns -EACCES if GSS context is NULL or will expire within the
14314de6caa2SAndy Adamson  * timeout (miliseconds)
14324de6caa2SAndy Adamson  */
14334de6caa2SAndy Adamson static int
14344de6caa2SAndy Adamson gss_key_timeout(struct rpc_cred *rc)
14354de6caa2SAndy Adamson {
14364de6caa2SAndy Adamson 	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
1437c5e6aecdSJeff Layton 	struct gss_cl_ctx *ctx;
1438cc6a7aabSArnd Bergmann 	unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ);
1439cc6a7aabSArnd Bergmann 	int ret = 0;
14404de6caa2SAndy Adamson 
1441c5e6aecdSJeff Layton 	rcu_read_lock();
1442c5e6aecdSJeff Layton 	ctx = rcu_dereference(gss_cred->gc_ctx);
1443cc6a7aabSArnd Bergmann 	if (!ctx || time_after(timeout, ctx->gc_expiry))
1444cc6a7aabSArnd Bergmann 		ret = -EACCES;
1445c5e6aecdSJeff Layton 	rcu_read_unlock();
1446cc6a7aabSArnd Bergmann 
1447cc6a7aabSArnd Bergmann 	return ret;
14484de6caa2SAndy Adamson }
14494de6caa2SAndy Adamson 
1450fba3bad4STrond Myklebust static int
14518a317760STrond Myklebust gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
14521da177e4SLinus Torvalds {
14531da177e4SLinus Torvalds 	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
1454c5e6aecdSJeff Layton 	struct gss_cl_ctx *ctx;
14554de6caa2SAndy Adamson 	int ret;
14561da177e4SLinus Torvalds 
1457cd019f75STrond Myklebust 	if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
14588a317760STrond Myklebust 		goto out;
14591da177e4SLinus Torvalds 	/* Don't match with creds that have expired. */
1460c5e6aecdSJeff Layton 	rcu_read_lock();
1461c5e6aecdSJeff Layton 	ctx = rcu_dereference(gss_cred->gc_ctx);
1462c5e6aecdSJeff Layton 	if (!ctx || time_after(jiffies, ctx->gc_expiry)) {
1463c5e6aecdSJeff Layton 		rcu_read_unlock();
1464cd019f75STrond Myklebust 		return 0;
1465c5e6aecdSJeff Layton 	}
1466c5e6aecdSJeff Layton 	rcu_read_unlock();
1467cd019f75STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags))
14681da177e4SLinus Torvalds 		return 0;
14698a317760STrond Myklebust out:
147068c97153STrond Myklebust 	if (acred->principal != NULL) {
147168c97153STrond Myklebust 		if (gss_cred->gc_principal == NULL)
147268c97153STrond Myklebust 			return 0;
14734de6caa2SAndy Adamson 		ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
1474ddf529eeSNeilBrown 	} else {
147568c97153STrond Myklebust 		if (gss_cred->gc_principal != NULL)
14767c67db3aSTrond Myklebust 			return 0;
147704d1532bSNeilBrown 		ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid);
14784de6caa2SAndy Adamson 	}
14794de6caa2SAndy Adamson 	return ret;
14801da177e4SLinus Torvalds }
14811da177e4SLinus Torvalds 
14821da177e4SLinus Torvalds /*
14831da177e4SLinus Torvalds  * Marshal credentials.
1484e8680a24SChuck Lever  *
1485e8680a24SChuck Lever  * The expensive part is computing the verifier. We can't cache a
1486e8680a24SChuck Lever  * pre-computed version of the verifier because the seqno, which
1487e8680a24SChuck Lever  * is different every time, is included in the MIC.
14881da177e4SLinus Torvalds  */
1489e8680a24SChuck Lever static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
14901da177e4SLinus Torvalds {
1491a17c2153STrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
1492a17c2153STrond Myklebust 	struct rpc_cred *cred = req->rq_cred;
14931da177e4SLinus Torvalds 	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
14941da177e4SLinus Torvalds 						 gc_base);
14951da177e4SLinus Torvalds 	struct gss_cl_ctx	*ctx = gss_cred_get_ctx(cred);
1496e8680a24SChuck Lever 	__be32		*p, *cred_len;
14971da177e4SLinus Torvalds 	u32             maj_stat = 0;
14981da177e4SLinus Torvalds 	struct xdr_netobj mic;
14991da177e4SLinus Torvalds 	struct kvec	iov;
15001da177e4SLinus Torvalds 	struct xdr_buf	verf_buf;
15010c77668dSChuck Lever 	int status;
15021da177e4SLinus Torvalds 
1503e8680a24SChuck Lever 	/* Credential */
1504e8680a24SChuck Lever 
1505e8680a24SChuck Lever 	p = xdr_reserve_space(xdr, 7 * sizeof(*p) +
1506e8680a24SChuck Lever 			      ctx->gc_wire_ctx.len);
1507e8680a24SChuck Lever 	if (!p)
15080c77668dSChuck Lever 		goto marshal_failed;
1509e8680a24SChuck Lever 	*p++ = rpc_auth_gss;
15101da177e4SLinus Torvalds 	cred_len = p++;
15111da177e4SLinus Torvalds 
15121da177e4SLinus Torvalds 	spin_lock(&ctx->gc_seq_lock);
151397b78ae9STrond Myklebust 	req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
15141da177e4SLinus Torvalds 	spin_unlock(&ctx->gc_seq_lock);
151597b78ae9STrond Myklebust 	if (req->rq_seqno == MAXSEQ)
15160c77668dSChuck Lever 		goto expired;
15170c77668dSChuck Lever 	trace_rpcgss_seqno(task);
15181da177e4SLinus Torvalds 
1519e8680a24SChuck Lever 	*p++ = cpu_to_be32(RPC_GSS_VERSION);
1520e8680a24SChuck Lever 	*p++ = cpu_to_be32(ctx->gc_proc);
1521e8680a24SChuck Lever 	*p++ = cpu_to_be32(req->rq_seqno);
1522e8680a24SChuck Lever 	*p++ = cpu_to_be32(gss_cred->gc_service);
15231da177e4SLinus Torvalds 	p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
1524e8680a24SChuck Lever 	*cred_len = cpu_to_be32((p - (cred_len + 1)) << 2);
1525e8680a24SChuck Lever 
1526e8680a24SChuck Lever 	/* Verifier */
15271da177e4SLinus Torvalds 
15281da177e4SLinus Torvalds 	/* We compute the checksum for the verifier over the xdr-encoded bytes
15291da177e4SLinus Torvalds 	 * starting with the xid and ending at the end of the credential: */
1530067fb11bSChuck Lever 	iov.iov_base = req->rq_snd_buf.head[0].iov_base;
15311da177e4SLinus Torvalds 	iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;
15321da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &verf_buf);
15331da177e4SLinus Torvalds 
1534e8680a24SChuck Lever 	p = xdr_reserve_space(xdr, sizeof(*p));
1535e8680a24SChuck Lever 	if (!p)
15360c77668dSChuck Lever 		goto marshal_failed;
1537e8680a24SChuck Lever 	*p++ = rpc_auth_gss;
15381da177e4SLinus Torvalds 	mic.data = (u8 *)(p + 1);
153900fd6e14SJ. Bruce Fields 	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
1540e8680a24SChuck Lever 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
15410c77668dSChuck Lever 		goto expired;
1542e8680a24SChuck Lever 	else if (maj_stat != 0)
15430c77668dSChuck Lever 		goto bad_mic;
1544e8680a24SChuck Lever 	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
15450c77668dSChuck Lever 		goto marshal_failed;
15460c77668dSChuck Lever 	status = 0;
15470c77668dSChuck Lever out:
15481da177e4SLinus Torvalds 	gss_put_ctx(ctx);
15490c77668dSChuck Lever 	return status;
15500c77668dSChuck Lever expired:
155197b78ae9STrond Myklebust 	clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
15520c77668dSChuck Lever 	status = -EKEYEXPIRED;
15530c77668dSChuck Lever 	goto out;
15540c77668dSChuck Lever marshal_failed:
15550c77668dSChuck Lever 	status = -EMSGSIZE;
15560c77668dSChuck Lever 	goto out;
15570c77668dSChuck Lever bad_mic:
15580c77668dSChuck Lever 	trace_rpcgss_get_mic(task, maj_stat);
15590c77668dSChuck Lever 	status = -EIO;
15600c77668dSChuck Lever 	goto out;
15611da177e4SLinus Torvalds }
15621da177e4SLinus Torvalds 
1563cd019f75STrond Myklebust static int gss_renew_cred(struct rpc_task *task)
1564cd019f75STrond Myklebust {
1565a17c2153STrond Myklebust 	struct rpc_cred *oldcred = task->tk_rqstp->rq_cred;
1566cd019f75STrond Myklebust 	struct gss_cred *gss_cred = container_of(oldcred,
1567cd019f75STrond Myklebust 						 struct gss_cred,
1568cd019f75STrond Myklebust 						 gc_base);
1569cd019f75STrond Myklebust 	struct rpc_auth *auth = oldcred->cr_auth;
1570cd019f75STrond Myklebust 	struct auth_cred acred = {
157197f68c6bSNeilBrown 		.cred = oldcred->cr_cred,
157268c97153STrond Myklebust 		.principal = gss_cred->gc_principal,
1573cd019f75STrond Myklebust 	};
1574cd019f75STrond Myklebust 	struct rpc_cred *new;
1575cd019f75STrond Myklebust 
1576cd019f75STrond Myklebust 	new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW);
1577cd019f75STrond Myklebust 	if (IS_ERR(new))
1578cd019f75STrond Myklebust 		return PTR_ERR(new);
1579a17c2153STrond Myklebust 	task->tk_rqstp->rq_cred = new;
1580cd019f75STrond Myklebust 	put_rpccred(oldcred);
1581cd019f75STrond Myklebust 	return 0;
1582cd019f75STrond Myklebust }
1583cd019f75STrond Myklebust 
1584126e216aSTrond Myklebust static int gss_cred_is_negative_entry(struct rpc_cred *cred)
1585126e216aSTrond Myklebust {
1586126e216aSTrond Myklebust 	if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) {
1587126e216aSTrond Myklebust 		unsigned long now = jiffies;
1588126e216aSTrond Myklebust 		unsigned long begin, expire;
1589126e216aSTrond Myklebust 		struct gss_cred *gss_cred;
1590126e216aSTrond Myklebust 
1591126e216aSTrond Myklebust 		gss_cred = container_of(cred, struct gss_cred, gc_base);
1592126e216aSTrond Myklebust 		begin = gss_cred->gc_upcall_timestamp;
1593126e216aSTrond Myklebust 		expire = begin + gss_expired_cred_retry_delay * HZ;
1594126e216aSTrond Myklebust 
1595126e216aSTrond Myklebust 		if (time_in_range_open(now, begin, expire))
1596126e216aSTrond Myklebust 			return 1;
1597126e216aSTrond Myklebust 	}
1598126e216aSTrond Myklebust 	return 0;
1599126e216aSTrond Myklebust }
1600126e216aSTrond Myklebust 
16011da177e4SLinus Torvalds /*
16021da177e4SLinus Torvalds * Refresh credentials. XXX - finish
16031da177e4SLinus Torvalds */
16041da177e4SLinus Torvalds static int
16051da177e4SLinus Torvalds gss_refresh(struct rpc_task *task)
16061da177e4SLinus Torvalds {
1607a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
1608cd019f75STrond Myklebust 	int ret = 0;
16091da177e4SLinus Torvalds 
1610126e216aSTrond Myklebust 	if (gss_cred_is_negative_entry(cred))
1611126e216aSTrond Myklebust 		return -EKEYEXPIRED;
1612126e216aSTrond Myklebust 
1613cd019f75STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
1614cd019f75STrond Myklebust 			!test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
1615cd019f75STrond Myklebust 		ret = gss_renew_cred(task);
1616cd019f75STrond Myklebust 		if (ret < 0)
1617cd019f75STrond Myklebust 			goto out;
1618a17c2153STrond Myklebust 		cred = task->tk_rqstp->rq_cred;
1619cd019f75STrond Myklebust 	}
1620cd019f75STrond Myklebust 
1621cd019f75STrond Myklebust 	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
1622cd019f75STrond Myklebust 		ret = gss_refresh_upcall(task);
1623cd019f75STrond Myklebust out:
1624cd019f75STrond Myklebust 	return ret;
16251da177e4SLinus Torvalds }
16261da177e4SLinus Torvalds 
16270df7fb74STrond Myklebust /* Dummy refresh routine: used only when destroying the context */
16280df7fb74STrond Myklebust static int
16290df7fb74STrond Myklebust gss_refresh_null(struct rpc_task *task)
16300df7fb74STrond Myklebust {
1631c297c8b9SAndy Adamson 	return 0;
16320df7fb74STrond Myklebust }
16330df7fb74STrond Myklebust 
1634a0584ee9SChuck Lever static int
1635a0584ee9SChuck Lever gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
16361da177e4SLinus Torvalds {
1637a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
16381da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
1639a0584ee9SChuck Lever 	__be32		*p, *seq = NULL;
16401da177e4SLinus Torvalds 	struct kvec	iov;
16411da177e4SLinus Torvalds 	struct xdr_buf	verf_buf;
16421da177e4SLinus Torvalds 	struct xdr_netobj mic;
1643a0584ee9SChuck Lever 	u32		len, maj_stat;
1644a0584ee9SChuck Lever 	int		status;
16451da177e4SLinus Torvalds 
1646a0584ee9SChuck Lever 	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
1647a0584ee9SChuck Lever 	if (!p)
1648a0584ee9SChuck Lever 		goto validate_failed;
1649a0584ee9SChuck Lever 	if (*p++ != rpc_auth_gss)
1650a0584ee9SChuck Lever 		goto validate_failed;
1651a0584ee9SChuck Lever 	len = be32_to_cpup(p);
1652a0584ee9SChuck Lever 	if (len > RPC_MAX_AUTH_SIZE)
1653a0584ee9SChuck Lever 		goto validate_failed;
1654a0584ee9SChuck Lever 	p = xdr_inline_decode(xdr, len);
1655a0584ee9SChuck Lever 	if (!p)
1656a0584ee9SChuck Lever 		goto validate_failed;
16571da177e4SLinus Torvalds 
16582876a344SJ. Bruce Fields 	seq = kmalloc(4, GFP_NOFS);
16592876a344SJ. Bruce Fields 	if (!seq)
1660a0584ee9SChuck Lever 		goto validate_failed;
1661a0584ee9SChuck Lever 	*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
16622876a344SJ. Bruce Fields 	iov.iov_base = seq;
16632876a344SJ. Bruce Fields 	iov.iov_len = 4;
16641da177e4SLinus Torvalds 	xdr_buf_from_iov(&iov, &verf_buf);
16651da177e4SLinus Torvalds 	mic.data = (u8 *)p;
16661da177e4SLinus Torvalds 	mic.len = len;
166700fd6e14SJ. Bruce Fields 	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
16681da177e4SLinus Torvalds 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
1669fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
1670a0584ee9SChuck Lever 	if (maj_stat)
1671a0584ee9SChuck Lever 		goto bad_mic;
1672a0584ee9SChuck Lever 
167324b2605bSJ. Bruce Fields 	/* We leave it to unwrap to calculate au_rslack. For now we just
167424b2605bSJ. Bruce Fields 	 * calculate the length of the verifier: */
16751be27f36STrond Myklebust 	cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
1676a0584ee9SChuck Lever 	status = 0;
1677a0584ee9SChuck Lever out:
16781da177e4SLinus Torvalds 	gss_put_ctx(ctx);
16792876a344SJ. Bruce Fields 	kfree(seq);
1680a0584ee9SChuck Lever 	return status;
1681a0584ee9SChuck Lever 
1682a0584ee9SChuck Lever validate_failed:
1683a0584ee9SChuck Lever 	status = -EIO;
1684a0584ee9SChuck Lever 	goto out;
1685a0584ee9SChuck Lever bad_mic:
16860c77668dSChuck Lever 	trace_rpcgss_verify_mic(task, maj_stat);
1687a0584ee9SChuck Lever 	status = -EACCES;
1688a0584ee9SChuck Lever 	goto out;
16891da177e4SLinus Torvalds }
16901da177e4SLinus Torvalds 
1691e8680a24SChuck Lever static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
1692e8680a24SChuck Lever 			      struct rpc_task *task, struct xdr_stream *xdr)
16939f06c719SChuck Lever {
1694e8680a24SChuck Lever 	struct rpc_rqst *rqstp = task->tk_rqstp;
1695e8680a24SChuck Lever 	struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf;
16961da177e4SLinus Torvalds 	struct xdr_netobj mic;
1697e8680a24SChuck Lever 	__be32 *p, *integ_len;
1698e8680a24SChuck Lever 	u32 offset, maj_stat;
16991da177e4SLinus Torvalds 
1700e8680a24SChuck Lever 	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
1701e8680a24SChuck Lever 	if (!p)
1702e8680a24SChuck Lever 		goto wrap_failed;
17031da177e4SLinus Torvalds 	integ_len = p++;
1704e8680a24SChuck Lever 	*p = cpu_to_be32(rqstp->rq_seqno);
1705e8680a24SChuck Lever 
1706e8680a24SChuck Lever 	if (rpcauth_wrap_req_encode(task, xdr))
1707e8680a24SChuck Lever 		goto wrap_failed;
1708e8680a24SChuck Lever 
17091da177e4SLinus Torvalds 	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
17101da177e4SLinus Torvalds 	if (xdr_buf_subsegment(snd_buf, &integ_buf,
17111da177e4SLinus Torvalds 				offset, snd_buf->len - offset))
1712e8680a24SChuck Lever 		goto wrap_failed;
1713e8680a24SChuck Lever 	*integ_len = cpu_to_be32(integ_buf.len);
17141da177e4SLinus Torvalds 
1715e8680a24SChuck Lever 	p = xdr_reserve_space(xdr, 0);
1716e8680a24SChuck Lever 	if (!p)
1717e8680a24SChuck Lever 		goto wrap_failed;
17181da177e4SLinus Torvalds 	mic.data = (u8 *)(p + 1);
171900fd6e14SJ. Bruce Fields 	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
17201da177e4SLinus Torvalds 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
1721fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
17221da177e4SLinus Torvalds 	else if (maj_stat)
17230c77668dSChuck Lever 		goto bad_mic;
1724e8680a24SChuck Lever 	/* Check that the trailing MIC fit in the buffer, after the fact */
1725e8680a24SChuck Lever 	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
1726e8680a24SChuck Lever 		goto wrap_failed;
17271da177e4SLinus Torvalds 	return 0;
1728e8680a24SChuck Lever wrap_failed:
1729e8680a24SChuck Lever 	return -EMSGSIZE;
17300c77668dSChuck Lever bad_mic:
17310c77668dSChuck Lever 	trace_rpcgss_get_mic(task, maj_stat);
17320c77668dSChuck Lever 	return -EIO;
17331da177e4SLinus Torvalds }
17341da177e4SLinus Torvalds 
17352d2da60cSJ. Bruce Fields static void
17362d2da60cSJ. Bruce Fields priv_release_snd_buf(struct rpc_rqst *rqstp)
17372d2da60cSJ. Bruce Fields {
17382d2da60cSJ. Bruce Fields 	int i;
17392d2da60cSJ. Bruce Fields 
17402d2da60cSJ. Bruce Fields 	for (i=0; i < rqstp->rq_enc_pages_num; i++)
17412d2da60cSJ. Bruce Fields 		__free_page(rqstp->rq_enc_pages[i]);
17422d2da60cSJ. Bruce Fields 	kfree(rqstp->rq_enc_pages);
17438dae5398SChuck Lever 	rqstp->rq_release_snd_buf = NULL;
17442d2da60cSJ. Bruce Fields }
17452d2da60cSJ. Bruce Fields 
17462d2da60cSJ. Bruce Fields static int
17472d2da60cSJ. Bruce Fields alloc_enc_pages(struct rpc_rqst *rqstp)
17482d2da60cSJ. Bruce Fields {
17492d2da60cSJ. Bruce Fields 	struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
17502d2da60cSJ. Bruce Fields 	int first, last, i;
17512d2da60cSJ. Bruce Fields 
17528dae5398SChuck Lever 	if (rqstp->rq_release_snd_buf)
17538dae5398SChuck Lever 		rqstp->rq_release_snd_buf(rqstp);
17548dae5398SChuck Lever 
17552d2da60cSJ. Bruce Fields 	if (snd_buf->page_len == 0) {
17562d2da60cSJ. Bruce Fields 		rqstp->rq_enc_pages_num = 0;
17572d2da60cSJ. Bruce Fields 		return 0;
17582d2da60cSJ. Bruce Fields 	}
17592d2da60cSJ. Bruce Fields 
176009cbfeafSKirill A. Shutemov 	first = snd_buf->page_base >> PAGE_SHIFT;
176109cbfeafSKirill A. Shutemov 	last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT;
17622d2da60cSJ. Bruce Fields 	rqstp->rq_enc_pages_num = last - first + 1 + 1;
17632d2da60cSJ. Bruce Fields 	rqstp->rq_enc_pages
17646da2ec56SKees Cook 		= kmalloc_array(rqstp->rq_enc_pages_num,
17656da2ec56SKees Cook 				sizeof(struct page *),
17662d2da60cSJ. Bruce Fields 				GFP_NOFS);
17672d2da60cSJ. Bruce Fields 	if (!rqstp->rq_enc_pages)
17682d2da60cSJ. Bruce Fields 		goto out;
17692d2da60cSJ. Bruce Fields 	for (i=0; i < rqstp->rq_enc_pages_num; i++) {
17702d2da60cSJ. Bruce Fields 		rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS);
17712d2da60cSJ. Bruce Fields 		if (rqstp->rq_enc_pages[i] == NULL)
17722d2da60cSJ. Bruce Fields 			goto out_free;
17732d2da60cSJ. Bruce Fields 	}
17742d2da60cSJ. Bruce Fields 	rqstp->rq_release_snd_buf = priv_release_snd_buf;
17752d2da60cSJ. Bruce Fields 	return 0;
17762d2da60cSJ. Bruce Fields out_free:
1777cdead7cfSTrond Myklebust 	rqstp->rq_enc_pages_num = i;
1778cdead7cfSTrond Myklebust 	priv_release_snd_buf(rqstp);
17792d2da60cSJ. Bruce Fields out:
17802d2da60cSJ. Bruce Fields 	return -EAGAIN;
17812d2da60cSJ. Bruce Fields }
17822d2da60cSJ. Bruce Fields 
1783e8680a24SChuck Lever static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
1784e8680a24SChuck Lever 			     struct rpc_task *task, struct xdr_stream *xdr)
17852d2da60cSJ. Bruce Fields {
1786e8680a24SChuck Lever 	struct rpc_rqst *rqstp = task->tk_rqstp;
17872d2da60cSJ. Bruce Fields 	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;
1788e8680a24SChuck Lever 	u32		pad, offset, maj_stat;
17892d2da60cSJ. Bruce Fields 	int		status;
1790e8680a24SChuck Lever 	__be32		*p, *opaque_len;
17912d2da60cSJ. Bruce Fields 	struct page	**inpages;
17922d2da60cSJ. Bruce Fields 	int		first;
17932d2da60cSJ. Bruce Fields 	struct kvec	*iov;
17942d2da60cSJ. Bruce Fields 
1795e8680a24SChuck Lever 	status = -EIO;
1796e8680a24SChuck Lever 	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
1797e8680a24SChuck Lever 	if (!p)
1798e8680a24SChuck Lever 		goto wrap_failed;
17992d2da60cSJ. Bruce Fields 	opaque_len = p++;
1800e8680a24SChuck Lever 	*p = cpu_to_be32(rqstp->rq_seqno);
18012d2da60cSJ. Bruce Fields 
1802e8680a24SChuck Lever 	if (rpcauth_wrap_req_encode(task, xdr))
1803e8680a24SChuck Lever 		goto wrap_failed;
18042d2da60cSJ. Bruce Fields 
18052d2da60cSJ. Bruce Fields 	status = alloc_enc_pages(rqstp);
1806e8680a24SChuck Lever 	if (unlikely(status))
1807e8680a24SChuck Lever 		goto wrap_failed;
180809cbfeafSKirill A. Shutemov 	first = snd_buf->page_base >> PAGE_SHIFT;
18092d2da60cSJ. Bruce Fields 	inpages = snd_buf->pages + first;
18102d2da60cSJ. Bruce Fields 	snd_buf->pages = rqstp->rq_enc_pages;
181109cbfeafSKirill A. Shutemov 	snd_buf->page_base -= first << PAGE_SHIFT;
18127561042fSKevin Coffman 	/*
1813e8680a24SChuck Lever 	 * Move the tail into its own page, in case gss_wrap needs
1814e8680a24SChuck Lever 	 * more space in the head when wrapping.
18157561042fSKevin Coffman 	 *
1816e8680a24SChuck Lever 	 * Still... Why can't gss_wrap just slide the tail down?
18177561042fSKevin Coffman 	 */
18182d2da60cSJ. Bruce Fields 	if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
1819e8680a24SChuck Lever 		char *tmp;
1820e8680a24SChuck Lever 
18212d2da60cSJ. Bruce Fields 		tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
18222d2da60cSJ. Bruce Fields 		memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
18232d2da60cSJ. Bruce Fields 		snd_buf->tail[0].iov_base = tmp;
18242d2da60cSJ. Bruce Fields 	}
1825e8680a24SChuck Lever 	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
182600fd6e14SJ. Bruce Fields 	maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages);
18277561042fSKevin Coffman 	/* slack space should prevent this ever happening: */
1828e8680a24SChuck Lever 	if (unlikely(snd_buf->len > snd_buf->buflen))
1829e8680a24SChuck Lever 		goto wrap_failed;
18302d2da60cSJ. Bruce Fields 	/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
18312d2da60cSJ. Bruce Fields 	 * done anyway, so it's safe to put the request on the wire: */
18322d2da60cSJ. Bruce Fields 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
1833fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
18342d2da60cSJ. Bruce Fields 	else if (maj_stat)
18350c77668dSChuck Lever 		goto bad_wrap;
18362d2da60cSJ. Bruce Fields 
1837e8680a24SChuck Lever 	*opaque_len = cpu_to_be32(snd_buf->len - offset);
1838e8680a24SChuck Lever 	/* guess whether the pad goes into the head or the tail: */
18392d2da60cSJ. Bruce Fields 	if (snd_buf->page_len || snd_buf->tail[0].iov_len)
18402d2da60cSJ. Bruce Fields 		iov = snd_buf->tail;
18412d2da60cSJ. Bruce Fields 	else
18422d2da60cSJ. Bruce Fields 		iov = snd_buf->head;
18432d2da60cSJ. Bruce Fields 	p = iov->iov_base + iov->iov_len;
18442d2da60cSJ. Bruce Fields 	pad = 3 - ((snd_buf->len - offset - 1) & 3);
18452d2da60cSJ. Bruce Fields 	memset(p, 0, pad);
18462d2da60cSJ. Bruce Fields 	iov->iov_len += pad;
18472d2da60cSJ. Bruce Fields 	snd_buf->len += pad;
18482d2da60cSJ. Bruce Fields 
18492d2da60cSJ. Bruce Fields 	return 0;
1850e8680a24SChuck Lever wrap_failed:
1851e8680a24SChuck Lever 	return status;
18520c77668dSChuck Lever bad_wrap:
18530c77668dSChuck Lever 	trace_rpcgss_wrap(task, maj_stat);
18540c77668dSChuck Lever 	return -EIO;
18552d2da60cSJ. Bruce Fields }
18562d2da60cSJ. Bruce Fields 
1857e8680a24SChuck Lever static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
18581da177e4SLinus Torvalds {
1859a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
18601da177e4SLinus Torvalds 	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
18611da177e4SLinus Torvalds 			gc_base);
18621da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
1863e8680a24SChuck Lever 	int status;
18641da177e4SLinus Torvalds 
1865e8680a24SChuck Lever 	status = -EIO;
18661da177e4SLinus Torvalds 	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
18671da177e4SLinus Torvalds 		/* The spec seems a little ambiguous here, but I think that not
18681da177e4SLinus Torvalds 		 * wrapping context destruction requests makes the most sense.
18691da177e4SLinus Torvalds 		 */
1870e8680a24SChuck Lever 		status = rpcauth_wrap_req_encode(task, xdr);
18711da177e4SLinus Torvalds 		goto out;
18721da177e4SLinus Torvalds 	}
18731da177e4SLinus Torvalds 	switch (gss_cred->gc_service) {
18741da177e4SLinus Torvalds 	case RPC_GSS_SVC_NONE:
1875e8680a24SChuck Lever 		status = rpcauth_wrap_req_encode(task, xdr);
18761da177e4SLinus Torvalds 		break;
18771da177e4SLinus Torvalds 	case RPC_GSS_SVC_INTEGRITY:
1878e8680a24SChuck Lever 		status = gss_wrap_req_integ(cred, ctx, task, xdr);
18791da177e4SLinus Torvalds 		break;
18801da177e4SLinus Torvalds 	case RPC_GSS_SVC_PRIVACY:
1881e8680a24SChuck Lever 		status = gss_wrap_req_priv(cred, ctx, task, xdr);
18821da177e4SLinus Torvalds 		break;
18830c77668dSChuck Lever 	default:
18840c77668dSChuck Lever 		status = -EIO;
18851da177e4SLinus Torvalds 	}
18861da177e4SLinus Torvalds out:
18871da177e4SLinus Torvalds 	gss_put_ctx(ctx);
18881da177e4SLinus Torvalds 	return status;
18891da177e4SLinus Torvalds }
18901da177e4SLinus Torvalds 
1891a0584ee9SChuck Lever static int
1892a0584ee9SChuck Lever gss_unwrap_resp_auth(struct rpc_cred *cred)
18931da177e4SLinus Torvalds {
189435e77d21SChuck Lever 	struct rpc_auth *auth = cred->cr_auth;
189535e77d21SChuck Lever 
189635e77d21SChuck Lever 	auth->au_rslack = auth->au_verfsize;
189735e77d21SChuck Lever 	auth->au_ralign = auth->au_verfsize;
1898a0584ee9SChuck Lever 	return 0;
1899a0584ee9SChuck Lever }
19001da177e4SLinus Torvalds 
1901a0584ee9SChuck Lever static int
19020c77668dSChuck Lever gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
19030c77668dSChuck Lever 		      struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
19040c77668dSChuck Lever 		      struct xdr_stream *xdr)
1905a0584ee9SChuck Lever {
1906a0584ee9SChuck Lever 	struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf;
1907a0584ee9SChuck Lever 	u32 data_offset, mic_offset, integ_len, maj_stat;
190835e77d21SChuck Lever 	struct rpc_auth *auth = cred->cr_auth;
1909a0584ee9SChuck Lever 	struct xdr_netobj mic;
1910a0584ee9SChuck Lever 	__be32 *p;
1911a0584ee9SChuck Lever 
1912a0584ee9SChuck Lever 	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
1913a0584ee9SChuck Lever 	if (unlikely(!p))
1914a0584ee9SChuck Lever 		goto unwrap_failed;
1915a0584ee9SChuck Lever 	integ_len = be32_to_cpup(p++);
19161da177e4SLinus Torvalds 	if (integ_len & 3)
1917a0584ee9SChuck Lever 		goto unwrap_failed;
1918a0584ee9SChuck Lever 	data_offset = (u8 *)(p) - (u8 *)rcv_buf->head[0].iov_base;
19191da177e4SLinus Torvalds 	mic_offset = integ_len + data_offset;
19201da177e4SLinus Torvalds 	if (mic_offset > rcv_buf->len)
1921a0584ee9SChuck Lever 		goto unwrap_failed;
1922a0584ee9SChuck Lever 	if (be32_to_cpup(p) != rqstp->rq_seqno)
19230c77668dSChuck Lever 		goto bad_seqno;
19241da177e4SLinus Torvalds 
1925a0584ee9SChuck Lever 	if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, integ_len))
1926a0584ee9SChuck Lever 		goto unwrap_failed;
19271da177e4SLinus Torvalds 	if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset))
1928a0584ee9SChuck Lever 		goto unwrap_failed;
192900fd6e14SJ. Bruce Fields 	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
19301da177e4SLinus Torvalds 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
1931fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
19321da177e4SLinus Torvalds 	if (maj_stat != GSS_S_COMPLETE)
1933a0584ee9SChuck Lever 		goto bad_mic;
1934a0584ee9SChuck Lever 
193535e77d21SChuck Lever 	auth->au_rslack = auth->au_verfsize + 2 + 1 + XDR_QUADLEN(mic.len);
193635e77d21SChuck Lever 	auth->au_ralign = auth->au_verfsize + 2;
19371da177e4SLinus Torvalds 	return 0;
1938a0584ee9SChuck Lever unwrap_failed:
19390c77668dSChuck Lever 	trace_rpcgss_unwrap_failed(task);
19400c77668dSChuck Lever 	return -EIO;
19410c77668dSChuck Lever bad_seqno:
19420c77668dSChuck Lever 	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(p));
1943a0584ee9SChuck Lever 	return -EIO;
1944a0584ee9SChuck Lever bad_mic:
19450c77668dSChuck Lever 	trace_rpcgss_verify_mic(task, maj_stat);
1946a0584ee9SChuck Lever 	return -EIO;
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
1949a0584ee9SChuck Lever static int
19500c77668dSChuck Lever gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
19510c77668dSChuck Lever 		     struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
19520c77668dSChuck Lever 		     struct xdr_stream *xdr)
19532d2da60cSJ. Bruce Fields {
19542d2da60cSJ. Bruce Fields 	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
1955a0584ee9SChuck Lever 	struct kvec *head = rqstp->rq_rcv_buf.head;
195635e77d21SChuck Lever 	struct rpc_auth *auth = cred->cr_auth;
1957a0584ee9SChuck Lever 	unsigned int savedlen = rcv_buf->len;
1958a0584ee9SChuck Lever 	u32 offset, opaque_len, maj_stat;
1959a0584ee9SChuck Lever 	__be32 *p;
19602d2da60cSJ. Bruce Fields 
1961a0584ee9SChuck Lever 	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
1962a0584ee9SChuck Lever 	if (unlikely(!p))
1963a0584ee9SChuck Lever 		goto unwrap_failed;
1964a0584ee9SChuck Lever 	opaque_len = be32_to_cpup(p++);
1965a0584ee9SChuck Lever 	offset = (u8 *)(p) - (u8 *)head->iov_base;
19662d2da60cSJ. Bruce Fields 	if (offset + opaque_len > rcv_buf->len)
1967a0584ee9SChuck Lever 		goto unwrap_failed;
19682d2da60cSJ. Bruce Fields 	rcv_buf->len = offset + opaque_len;
19692d2da60cSJ. Bruce Fields 
197000fd6e14SJ. Bruce Fields 	maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf);
19712d2da60cSJ. Bruce Fields 	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
1972fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
19732d2da60cSJ. Bruce Fields 	if (maj_stat != GSS_S_COMPLETE)
1974a0584ee9SChuck Lever 		goto bad_unwrap;
1975a0584ee9SChuck Lever 	/* gss_unwrap decrypted the sequence number */
1976a0584ee9SChuck Lever 	if (be32_to_cpup(p++) != rqstp->rq_seqno)
19770c77668dSChuck Lever 		goto bad_seqno;
19782d2da60cSJ. Bruce Fields 
1979a0584ee9SChuck Lever 	/* gss_unwrap redacts the opaque blob from the head iovec.
1980a0584ee9SChuck Lever 	 * rcv_buf has changed, thus the stream needs to be reset.
1981a0584ee9SChuck Lever 	 */
1982a0584ee9SChuck Lever 	xdr_init_decode(xdr, rcv_buf, p, rqstp);
1983a0584ee9SChuck Lever 
198435e77d21SChuck Lever 	auth->au_rslack = auth->au_verfsize + 2 +
198535e77d21SChuck Lever 			  XDR_QUADLEN(savedlen - rcv_buf->len);
198635e77d21SChuck Lever 	auth->au_ralign = auth->au_verfsize + 2 +
1987a0584ee9SChuck Lever 			  XDR_QUADLEN(savedlen - rcv_buf->len);
19882d2da60cSJ. Bruce Fields 	return 0;
1989a0584ee9SChuck Lever unwrap_failed:
19900c77668dSChuck Lever 	trace_rpcgss_unwrap_failed(task);
19910c77668dSChuck Lever 	return -EIO;
19920c77668dSChuck Lever bad_seqno:
19930c77668dSChuck Lever 	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p));
1994a0584ee9SChuck Lever 	return -EIO;
1995a0584ee9SChuck Lever bad_unwrap:
19960c77668dSChuck Lever 	trace_rpcgss_unwrap(task, maj_stat);
1997a0584ee9SChuck Lever 	return -EIO;
1998bf269551SChuck Lever }
19992d2da60cSJ. Bruce Fields 
20003021a5bbSTrond Myklebust static bool
20013021a5bbSTrond Myklebust gss_seq_is_newer(u32 new, u32 old)
20023021a5bbSTrond Myklebust {
20033021a5bbSTrond Myklebust 	return (s32)(new - old) > 0;
20043021a5bbSTrond Myklebust }
20053021a5bbSTrond Myklebust 
20063021a5bbSTrond Myklebust static bool
20073021a5bbSTrond Myklebust gss_xmit_need_reencode(struct rpc_task *task)
20083021a5bbSTrond Myklebust {
20093021a5bbSTrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
20103021a5bbSTrond Myklebust 	struct rpc_cred *cred = req->rq_cred;
20113021a5bbSTrond Myklebust 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
20120c77668dSChuck Lever 	u32 win, seq_xmit = 0;
20133021a5bbSTrond Myklebust 	bool ret = true;
20143021a5bbSTrond Myklebust 
20153021a5bbSTrond Myklebust 	if (!ctx)
20160c77668dSChuck Lever 		goto out;
20173021a5bbSTrond Myklebust 
20183021a5bbSTrond Myklebust 	if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq)))
20190c77668dSChuck Lever 		goto out_ctx;
20203021a5bbSTrond Myklebust 
20213021a5bbSTrond Myklebust 	seq_xmit = READ_ONCE(ctx->gc_seq_xmit);
20223021a5bbSTrond Myklebust 	while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) {
20233021a5bbSTrond Myklebust 		u32 tmp = seq_xmit;
20243021a5bbSTrond Myklebust 
20253021a5bbSTrond Myklebust 		seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno);
20263021a5bbSTrond Myklebust 		if (seq_xmit == tmp) {
20273021a5bbSTrond Myklebust 			ret = false;
20280c77668dSChuck Lever 			goto out_ctx;
20293021a5bbSTrond Myklebust 		}
20303021a5bbSTrond Myklebust 	}
20313021a5bbSTrond Myklebust 
20323021a5bbSTrond Myklebust 	win = ctx->gc_win;
20333021a5bbSTrond Myklebust 	if (win > 0)
20343021a5bbSTrond Myklebust 		ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win);
20350c77668dSChuck Lever 
20360c77668dSChuck Lever out_ctx:
20373021a5bbSTrond Myklebust 	gss_put_ctx(ctx);
20380c77668dSChuck Lever out:
20390c77668dSChuck Lever 	trace_rpcgss_need_reencode(task, seq_xmit, ret);
20403021a5bbSTrond Myklebust 	return ret;
20413021a5bbSTrond Myklebust }
20423021a5bbSTrond Myklebust 
20431da177e4SLinus Torvalds static int
2044a0584ee9SChuck Lever gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
20451da177e4SLinus Torvalds {
2046a0584ee9SChuck Lever 	struct rpc_rqst *rqstp = task->tk_rqstp;
2047a0584ee9SChuck Lever 	struct rpc_cred *cred = rqstp->rq_cred;
20481da177e4SLinus Torvalds 	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
20491da177e4SLinus Torvalds 			gc_base);
20501da177e4SLinus Torvalds 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
20511da177e4SLinus Torvalds 	int status = -EIO;
20521da177e4SLinus Torvalds 
20531da177e4SLinus Torvalds 	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
20541da177e4SLinus Torvalds 		goto out_decode;
20551da177e4SLinus Torvalds 	switch (gss_cred->gc_service) {
20561da177e4SLinus Torvalds 	case RPC_GSS_SVC_NONE:
2057a0584ee9SChuck Lever 		status = gss_unwrap_resp_auth(cred);
20581da177e4SLinus Torvalds 		break;
20591da177e4SLinus Torvalds 	case RPC_GSS_SVC_INTEGRITY:
20600c77668dSChuck Lever 		status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr);
20611da177e4SLinus Torvalds 		break;
20621da177e4SLinus Torvalds 	case RPC_GSS_SVC_PRIVACY:
20630c77668dSChuck Lever 		status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr);
20641da177e4SLinus Torvalds 		break;
20651da177e4SLinus Torvalds 	}
2066a0584ee9SChuck Lever 	if (status)
2067a0584ee9SChuck Lever 		goto out;
2068a0584ee9SChuck Lever 
20691da177e4SLinus Torvalds out_decode:
2070a0584ee9SChuck Lever 	status = rpcauth_unwrap_resp_decode(task, xdr);
20711da177e4SLinus Torvalds out:
20721da177e4SLinus Torvalds 	gss_put_ctx(ctx);
20731da177e4SLinus Torvalds 	return status;
20741da177e4SLinus Torvalds }
20751da177e4SLinus Torvalds 
2076f1c0a861STrond Myklebust static const struct rpc_authops authgss_ops = {
20771da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
20781da177e4SLinus Torvalds 	.au_flavor	= RPC_AUTH_GSS,
20791da177e4SLinus Torvalds 	.au_name	= "RPCSEC_GSS",
20801da177e4SLinus Torvalds 	.create		= gss_create,
20811da177e4SLinus Torvalds 	.destroy	= gss_destroy,
2082a960f8d6SFrank Sorenson 	.hash_cred	= gss_hash_cred,
20831da177e4SLinus Torvalds 	.lookup_cred	= gss_lookup_cred,
208480df9d20SStanislav Kinsbursky 	.crcreate	= gss_create_cred,
20856a1a1e34SChuck Lever 	.list_pseudoflavors = gss_mech_list_pseudoflavors,
20869568c5e9SChuck Lever 	.info2flavor	= gss_mech_info2flavor,
2087a77c806fSChuck Lever 	.flavor2info	= gss_mech_flavor2info,
20881da177e4SLinus Torvalds };
20891da177e4SLinus Torvalds 
2090f1c0a861STrond Myklebust static const struct rpc_credops gss_credops = {
20911da177e4SLinus Torvalds 	.cr_name		= "AUTH_GSS",
20921da177e4SLinus Torvalds 	.crdestroy		= gss_destroy_cred,
2093fba3bad4STrond Myklebust 	.cr_init		= gss_cred_init,
20941da177e4SLinus Torvalds 	.crmatch		= gss_match,
20951da177e4SLinus Torvalds 	.crmarshal		= gss_marshal,
20961da177e4SLinus Torvalds 	.crrefresh		= gss_refresh,
20971da177e4SLinus Torvalds 	.crvalidate		= gss_validate,
20981da177e4SLinus Torvalds 	.crwrap_req		= gss_wrap_req,
20991da177e4SLinus Torvalds 	.crunwrap_resp		= gss_unwrap_resp,
21004de6caa2SAndy Adamson 	.crkey_timeout		= gss_key_timeout,
2101a0337d1dSJeff Layton 	.crstringify_acceptor	= gss_stringify_acceptor,
21023021a5bbSTrond Myklebust 	.crneed_reencode	= gss_xmit_need_reencode,
21031da177e4SLinus Torvalds };
21041da177e4SLinus Torvalds 
21050df7fb74STrond Myklebust static const struct rpc_credops gss_nullops = {
21060df7fb74STrond Myklebust 	.cr_name		= "AUTH_GSS",
21076dcd3926SJeff Layton 	.crdestroy		= gss_destroy_nullcred,
21080df7fb74STrond Myklebust 	.crmatch		= gss_match,
21090df7fb74STrond Myklebust 	.crmarshal		= gss_marshal,
21100df7fb74STrond Myklebust 	.crrefresh		= gss_refresh_null,
21110df7fb74STrond Myklebust 	.crvalidate		= gss_validate,
21120df7fb74STrond Myklebust 	.crwrap_req		= gss_wrap_req,
21130df7fb74STrond Myklebust 	.crunwrap_resp		= gss_unwrap_resp,
2114a0337d1dSJeff Layton 	.crstringify_acceptor	= gss_stringify_acceptor,
21150df7fb74STrond Myklebust };
21160df7fb74STrond Myklebust 
2117b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0 = {
2118c1225158SPeng Tao 	.upcall		= rpc_pipe_generic_upcall,
21191da177e4SLinus Torvalds 	.downcall	= gss_pipe_downcall,
21201da177e4SLinus Torvalds 	.destroy_msg	= gss_pipe_destroy_msg,
212134769fc4S\"J. Bruce Fields\ 	.open_pipe	= gss_pipe_open_v0,
212234769fc4S\"J. Bruce Fields\ 	.release_pipe	= gss_pipe_release,
212334769fc4S\"J. Bruce Fields\ };
212434769fc4S\"J. Bruce Fields\ 
2125b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1 = {
2126c1225158SPeng Tao 	.upcall		= rpc_pipe_generic_upcall,
212734769fc4S\"J. Bruce Fields\ 	.downcall	= gss_pipe_downcall,
212834769fc4S\"J. Bruce Fields\ 	.destroy_msg	= gss_pipe_destroy_msg,
212934769fc4S\"J. Bruce Fields\ 	.open_pipe	= gss_pipe_open_v1,
21301da177e4SLinus Torvalds 	.release_pipe	= gss_pipe_release,
21311da177e4SLinus Torvalds };
21321da177e4SLinus Torvalds 
2133a1db410dSStanislav Kinsbursky static __net_init int rpcsec_gss_init_net(struct net *net)
2134a1db410dSStanislav Kinsbursky {
2135a1db410dSStanislav Kinsbursky 	return gss_svc_init_net(net);
2136a1db410dSStanislav Kinsbursky }
2137a1db410dSStanislav Kinsbursky 
2138a1db410dSStanislav Kinsbursky static __net_exit void rpcsec_gss_exit_net(struct net *net)
2139a1db410dSStanislav Kinsbursky {
2140a1db410dSStanislav Kinsbursky 	gss_svc_shutdown_net(net);
2141a1db410dSStanislav Kinsbursky }
2142a1db410dSStanislav Kinsbursky 
2143a1db410dSStanislav Kinsbursky static struct pernet_operations rpcsec_gss_net_ops = {
2144a1db410dSStanislav Kinsbursky 	.init = rpcsec_gss_init_net,
2145a1db410dSStanislav Kinsbursky 	.exit = rpcsec_gss_exit_net,
2146a1db410dSStanislav Kinsbursky };
2147a1db410dSStanislav Kinsbursky 
21481da177e4SLinus Torvalds /*
21491da177e4SLinus Torvalds  * Initialize RPCSEC_GSS module
21501da177e4SLinus Torvalds  */
21511da177e4SLinus Torvalds static int __init init_rpcsec_gss(void)
21521da177e4SLinus Torvalds {
21531da177e4SLinus Torvalds 	int err = 0;
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds 	err = rpcauth_register(&authgss_ops);
21561da177e4SLinus Torvalds 	if (err)
21571da177e4SLinus Torvalds 		goto out;
21581da177e4SLinus Torvalds 	err = gss_svc_init();
21591da177e4SLinus Torvalds 	if (err)
21601da177e4SLinus Torvalds 		goto out_unregister;
2161a1db410dSStanislav Kinsbursky 	err = register_pernet_subsys(&rpcsec_gss_net_ops);
2162a1db410dSStanislav Kinsbursky 	if (err)
2163a1db410dSStanislav Kinsbursky 		goto out_svc_exit;
216479a3f20bS\"J. Bruce Fields\ 	rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
21651da177e4SLinus Torvalds 	return 0;
2166a1db410dSStanislav Kinsbursky out_svc_exit:
2167a1db410dSStanislav Kinsbursky 	gss_svc_shutdown();
21681da177e4SLinus Torvalds out_unregister:
21691da177e4SLinus Torvalds 	rpcauth_unregister(&authgss_ops);
21701da177e4SLinus Torvalds out:
21711da177e4SLinus Torvalds 	return err;
21721da177e4SLinus Torvalds }
21731da177e4SLinus Torvalds 
21741da177e4SLinus Torvalds static void __exit exit_rpcsec_gss(void)
21751da177e4SLinus Torvalds {
2176a1db410dSStanislav Kinsbursky 	unregister_pernet_subsys(&rpcsec_gss_net_ops);
21771da177e4SLinus Torvalds 	gss_svc_shutdown();
21781da177e4SLinus Torvalds 	rpcauth_unregister(&authgss_ops);
2179bf12691dSJesper Dangaard Brouer 	rcu_barrier(); /* Wait for completion of call_rcu()'s */
21801da177e4SLinus Torvalds }
21811da177e4SLinus Torvalds 
218271afa85eSChuck Lever MODULE_ALIAS("rpc-auth-6");
21831da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2184126e216aSTrond Myklebust module_param_named(expired_cred_retry_delay,
2185126e216aSTrond Myklebust 		   gss_expired_cred_retry_delay,
2186126e216aSTrond Myklebust 		   uint, 0644);
2187126e216aSTrond Myklebust MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
2188126e216aSTrond Myklebust 		"the RPC engine retries an expired credential");
2189126e216aSTrond Myklebust 
21904de6caa2SAndy Adamson module_param_named(key_expire_timeo,
21914de6caa2SAndy Adamson 		   gss_key_expire_timeo,
21924de6caa2SAndy Adamson 		   uint, 0644);
21934de6caa2SAndy Adamson MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
21944de6caa2SAndy Adamson 		"credential keys lifetime where the NFS layer cleans up "
21954de6caa2SAndy Adamson 		"prior to key expiration");
21964de6caa2SAndy Adamson 
21971da177e4SLinus Torvalds module_init(init_rpcsec_gss)
21981da177e4SLinus Torvalds module_exit(exit_rpcsec_gss)
2199