162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/net/sunrpc/auth_gss/auth_gss.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * RPCSEC_GSS client authentication.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (c) 2000 The Regents of the University of Michigan.
862306a36Sopenharmony_ci *  All rights reserved.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  Dug Song       <dugsong@monkey.org>
1162306a36Sopenharmony_ci *  Andy Adamson   <andros@umich.edu>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/sched.h>
1962306a36Sopenharmony_ci#include <linux/pagemap.h>
2062306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
2162306a36Sopenharmony_ci#include <linux/sunrpc/auth.h>
2262306a36Sopenharmony_ci#include <linux/sunrpc/auth_gss.h>
2362306a36Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h>
2462306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h>
2562306a36Sopenharmony_ci#include <linux/sunrpc/gss_err.h>
2662306a36Sopenharmony_ci#include <linux/workqueue.h>
2762306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h>
2862306a36Sopenharmony_ci#include <linux/sunrpc/gss_api.h>
2962306a36Sopenharmony_ci#include <linux/uaccess.h>
3062306a36Sopenharmony_ci#include <linux/hashtable.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "auth_gss_internal.h"
3362306a36Sopenharmony_ci#include "../netns.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <trace/events/rpcgss.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const struct rpc_authops authgss_ops;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct rpc_credops gss_credops;
4062306a36Sopenharmony_cistatic const struct rpc_credops gss_nullops;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define GSS_RETRY_EXPIRED 5
4362306a36Sopenharmony_cistatic unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define GSS_KEY_EXPIRE_TIMEO 240
4662306a36Sopenharmony_cistatic unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
4962306a36Sopenharmony_ci# define RPCDBG_FACILITY	RPCDBG_AUTH
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * This compile-time check verifies that we will not exceed the
5462306a36Sopenharmony_ci * slack space allotted by the client and server auth_gss code
5562306a36Sopenharmony_ci * before they call gss_wrap().
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci#define GSS_KRB5_MAX_SLACK_NEEDED					\
5862306a36Sopenharmony_ci	(GSS_KRB5_TOK_HDR_LEN		/* gss token header */		\
5962306a36Sopenharmony_ci	+ GSS_KRB5_MAX_CKSUM_LEN	/* gss token checksum */	\
6062306a36Sopenharmony_ci	+ GSS_KRB5_MAX_BLOCKSIZE	/* confounder */		\
6162306a36Sopenharmony_ci	+ GSS_KRB5_MAX_BLOCKSIZE	/* possible padding */		\
6262306a36Sopenharmony_ci	+ GSS_KRB5_TOK_HDR_LEN		/* encrypted hdr in v2 token */	\
6362306a36Sopenharmony_ci	+ GSS_KRB5_MAX_CKSUM_LEN	/* encryption hmac */		\
6462306a36Sopenharmony_ci	+ XDR_UNIT * 2			/* RPC verifier */		\
6562306a36Sopenharmony_ci	+ GSS_KRB5_TOK_HDR_LEN						\
6662306a36Sopenharmony_ci	+ GSS_KRB5_MAX_CKSUM_LEN)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define GSS_CRED_SLACK		(RPC_MAX_AUTH_SIZE * 2)
6962306a36Sopenharmony_ci/* length of a krb5 verifier (48), plus data added before arguments when
7062306a36Sopenharmony_ci * using integrity (two 4-byte integers): */
7162306a36Sopenharmony_ci#define GSS_VERF_SLACK		100
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic DEFINE_HASHTABLE(gss_auth_hash_table, 4);
7462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(gss_auth_hash_lock);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct gss_pipe {
7762306a36Sopenharmony_ci	struct rpc_pipe_dir_object pdo;
7862306a36Sopenharmony_ci	struct rpc_pipe *pipe;
7962306a36Sopenharmony_ci	struct rpc_clnt *clnt;
8062306a36Sopenharmony_ci	const char *name;
8162306a36Sopenharmony_ci	struct kref kref;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct gss_auth {
8562306a36Sopenharmony_ci	struct kref kref;
8662306a36Sopenharmony_ci	struct hlist_node hash;
8762306a36Sopenharmony_ci	struct rpc_auth rpc_auth;
8862306a36Sopenharmony_ci	struct gss_api_mech *mech;
8962306a36Sopenharmony_ci	enum rpc_gss_svc service;
9062306a36Sopenharmony_ci	struct rpc_clnt *client;
9162306a36Sopenharmony_ci	struct net	*net;
9262306a36Sopenharmony_ci	netns_tracker	ns_tracker;
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * There are two upcall pipes; dentry[1], named "gssd", is used
9562306a36Sopenharmony_ci	 * for the new text-based upcall; dentry[0] is named after the
9662306a36Sopenharmony_ci	 * mechanism (for example, "krb5") and exists for
9762306a36Sopenharmony_ci	 * backwards-compatibility with older gssd's.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	struct gss_pipe *gss_pipe[2];
10062306a36Sopenharmony_ci	const char *target_name;
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* pipe_version >= 0 if and only if someone has a pipe open. */
10462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pipe_version_lock);
10562306a36Sopenharmony_cistatic struct rpc_wait_queue pipe_version_rpc_waitqueue;
10662306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
10762306a36Sopenharmony_cistatic void gss_put_auth(struct gss_auth *gss_auth);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void gss_free_ctx(struct gss_cl_ctx *);
11062306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0;
11162306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline struct gss_cl_ctx *
11462306a36Sopenharmony_cigss_get_ctx(struct gss_cl_ctx *ctx)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	refcount_inc(&ctx->count);
11762306a36Sopenharmony_ci	return ctx;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline void
12162306a36Sopenharmony_cigss_put_ctx(struct gss_cl_ctx *ctx)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	if (refcount_dec_and_test(&ctx->count))
12462306a36Sopenharmony_ci		gss_free_ctx(ctx);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* gss_cred_set_ctx:
12862306a36Sopenharmony_ci * called by gss_upcall_callback and gss_create_upcall in order
12962306a36Sopenharmony_ci * to set the gss context. The actual exchange of an old context
13062306a36Sopenharmony_ci * and a new one is protected by the pipe->lock.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic void
13362306a36Sopenharmony_cigss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
13862306a36Sopenharmony_ci		return;
13962306a36Sopenharmony_ci	gss_get_ctx(ctx);
14062306a36Sopenharmony_ci	rcu_assign_pointer(gss_cred->gc_ctx, ctx);
14162306a36Sopenharmony_ci	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
14262306a36Sopenharmony_ci	smp_mb__before_atomic();
14362306a36Sopenharmony_ci	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct gss_cl_ctx *
14762306a36Sopenharmony_cigss_cred_get_ctx(struct rpc_cred *cred)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
15062306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = NULL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	rcu_read_lock();
15362306a36Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
15462306a36Sopenharmony_ci	if (ctx)
15562306a36Sopenharmony_ci		gss_get_ctx(ctx);
15662306a36Sopenharmony_ci	rcu_read_unlock();
15762306a36Sopenharmony_ci	return ctx;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic struct gss_cl_ctx *
16162306a36Sopenharmony_cigss_alloc_context(void)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
16662306a36Sopenharmony_ci	if (ctx != NULL) {
16762306a36Sopenharmony_ci		ctx->gc_proc = RPC_GSS_PROC_DATA;
16862306a36Sopenharmony_ci		ctx->gc_seq = 1;	/* NetApp 6.4R1 doesn't accept seq. no. 0 */
16962306a36Sopenharmony_ci		spin_lock_init(&ctx->gc_seq_lock);
17062306a36Sopenharmony_ci		refcount_set(&ctx->count,1);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	return ctx;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define GSSD_MIN_TIMEOUT (60 * 60)
17662306a36Sopenharmony_cistatic const void *
17762306a36Sopenharmony_cigss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	const void *q;
18062306a36Sopenharmony_ci	unsigned int seclen;
18162306a36Sopenharmony_ci	unsigned int timeout;
18262306a36Sopenharmony_ci	unsigned long now = jiffies;
18362306a36Sopenharmony_ci	u32 window_size;
18462306a36Sopenharmony_ci	int ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* First unsigned int gives the remaining lifetime in seconds of the
18762306a36Sopenharmony_ci	 * credential - e.g. the remaining TGT lifetime for Kerberos or
18862306a36Sopenharmony_ci	 * the -t value passed to GSSD.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	p = simple_get_bytes(p, end, &timeout, sizeof(timeout));
19162306a36Sopenharmony_ci	if (IS_ERR(p))
19262306a36Sopenharmony_ci		goto err;
19362306a36Sopenharmony_ci	if (timeout == 0)
19462306a36Sopenharmony_ci		timeout = GSSD_MIN_TIMEOUT;
19562306a36Sopenharmony_ci	ctx->gc_expiry = now + ((unsigned long)timeout * HZ);
19662306a36Sopenharmony_ci	/* Sequence number window. Determines the maximum number of
19762306a36Sopenharmony_ci	 * simultaneous requests
19862306a36Sopenharmony_ci	 */
19962306a36Sopenharmony_ci	p = simple_get_bytes(p, end, &window_size, sizeof(window_size));
20062306a36Sopenharmony_ci	if (IS_ERR(p))
20162306a36Sopenharmony_ci		goto err;
20262306a36Sopenharmony_ci	ctx->gc_win = window_size;
20362306a36Sopenharmony_ci	/* gssd signals an error by passing ctx->gc_win = 0: */
20462306a36Sopenharmony_ci	if (ctx->gc_win == 0) {
20562306a36Sopenharmony_ci		/*
20662306a36Sopenharmony_ci		 * in which case, p points to an error code. Anything other
20762306a36Sopenharmony_ci		 * than -EKEYEXPIRED gets converted to -EACCES.
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		p = simple_get_bytes(p, end, &ret, sizeof(ret));
21062306a36Sopenharmony_ci		if (!IS_ERR(p))
21162306a36Sopenharmony_ci			p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) :
21262306a36Sopenharmony_ci						    ERR_PTR(-EACCES);
21362306a36Sopenharmony_ci		goto err;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	/* copy the opaque wire context */
21662306a36Sopenharmony_ci	p = simple_get_netobj(p, end, &ctx->gc_wire_ctx);
21762306a36Sopenharmony_ci	if (IS_ERR(p))
21862306a36Sopenharmony_ci		goto err;
21962306a36Sopenharmony_ci	/* import the opaque security context */
22062306a36Sopenharmony_ci	p  = simple_get_bytes(p, end, &seclen, sizeof(seclen));
22162306a36Sopenharmony_ci	if (IS_ERR(p))
22262306a36Sopenharmony_ci		goto err;
22362306a36Sopenharmony_ci	q = (const void *)((const char *)p + seclen);
22462306a36Sopenharmony_ci	if (unlikely(q > end || q < p)) {
22562306a36Sopenharmony_ci		p = ERR_PTR(-EFAULT);
22662306a36Sopenharmony_ci		goto err;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_KERNEL);
22962306a36Sopenharmony_ci	if (ret < 0) {
23062306a36Sopenharmony_ci		trace_rpcgss_import_ctx(ret);
23162306a36Sopenharmony_ci		p = ERR_PTR(ret);
23262306a36Sopenharmony_ci		goto err;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* is there any trailing data? */
23662306a36Sopenharmony_ci	if (q == end) {
23762306a36Sopenharmony_ci		p = q;
23862306a36Sopenharmony_ci		goto done;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* pull in acceptor name (if there is one) */
24262306a36Sopenharmony_ci	p = simple_get_netobj(q, end, &ctx->gc_acceptor);
24362306a36Sopenharmony_ci	if (IS_ERR(p))
24462306a36Sopenharmony_ci		goto err;
24562306a36Sopenharmony_cidone:
24662306a36Sopenharmony_ci	trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout,
24762306a36Sopenharmony_ci			     ctx->gc_acceptor.len, ctx->gc_acceptor.data);
24862306a36Sopenharmony_cierr:
24962306a36Sopenharmony_ci	return p;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/* XXX: Need some documentation about why UPCALL_BUF_LEN is so small.
25362306a36Sopenharmony_ci *	Is user space expecting no more than UPCALL_BUF_LEN bytes?
25462306a36Sopenharmony_ci *	Note that there are now _two_ NI_MAXHOST sized data items
25562306a36Sopenharmony_ci *	being passed in this string.
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_ci#define UPCALL_BUF_LEN	256
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistruct gss_upcall_msg {
26062306a36Sopenharmony_ci	refcount_t count;
26162306a36Sopenharmony_ci	kuid_t	uid;
26262306a36Sopenharmony_ci	const char *service_name;
26362306a36Sopenharmony_ci	struct rpc_pipe_msg msg;
26462306a36Sopenharmony_ci	struct list_head list;
26562306a36Sopenharmony_ci	struct gss_auth *auth;
26662306a36Sopenharmony_ci	struct rpc_pipe *pipe;
26762306a36Sopenharmony_ci	struct rpc_wait_queue rpc_waitqueue;
26862306a36Sopenharmony_ci	wait_queue_head_t waitqueue;
26962306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
27062306a36Sopenharmony_ci	char databuf[UPCALL_BUF_LEN];
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int get_pipe_version(struct net *net)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
27662306a36Sopenharmony_ci	int ret;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_lock(&pipe_version_lock);
27962306a36Sopenharmony_ci	if (sn->pipe_version >= 0) {
28062306a36Sopenharmony_ci		atomic_inc(&sn->pipe_users);
28162306a36Sopenharmony_ci		ret = sn->pipe_version;
28262306a36Sopenharmony_ci	} else
28362306a36Sopenharmony_ci		ret = -EAGAIN;
28462306a36Sopenharmony_ci	spin_unlock(&pipe_version_lock);
28562306a36Sopenharmony_ci	return ret;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void put_pipe_version(struct net *net)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) {
29362306a36Sopenharmony_ci		sn->pipe_version = -1;
29462306a36Sopenharmony_ci		spin_unlock(&pipe_version_lock);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void
29962306a36Sopenharmony_cigss_release_msg(struct gss_upcall_msg *gss_msg)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct net *net = gss_msg->auth->net;
30262306a36Sopenharmony_ci	if (!refcount_dec_and_test(&gss_msg->count))
30362306a36Sopenharmony_ci		return;
30462306a36Sopenharmony_ci	put_pipe_version(net);
30562306a36Sopenharmony_ci	BUG_ON(!list_empty(&gss_msg->list));
30662306a36Sopenharmony_ci	if (gss_msg->ctx != NULL)
30762306a36Sopenharmony_ci		gss_put_ctx(gss_msg->ctx);
30862306a36Sopenharmony_ci	rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
30962306a36Sopenharmony_ci	gss_put_auth(gss_msg->auth);
31062306a36Sopenharmony_ci	kfree_const(gss_msg->service_name);
31162306a36Sopenharmony_ci	kfree(gss_msg);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic struct gss_upcall_msg *
31562306a36Sopenharmony_ci__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct gss_upcall_msg *pos;
31862306a36Sopenharmony_ci	list_for_each_entry(pos, &pipe->in_downcall, list) {
31962306a36Sopenharmony_ci		if (!uid_eq(pos->uid, uid))
32062306a36Sopenharmony_ci			continue;
32162306a36Sopenharmony_ci		if (pos->auth->service != auth->service)
32262306a36Sopenharmony_ci			continue;
32362306a36Sopenharmony_ci		refcount_inc(&pos->count);
32462306a36Sopenharmony_ci		return pos;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	return NULL;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/* Try to add an upcall to the pipefs queue.
33062306a36Sopenharmony_ci * If an upcall owned by our uid already exists, then we return a reference
33162306a36Sopenharmony_ci * to that upcall instead of adding the new upcall.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic inline struct gss_upcall_msg *
33462306a36Sopenharmony_cigss_add_msg(struct gss_upcall_msg *gss_msg)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
33762306a36Sopenharmony_ci	struct gss_upcall_msg *old;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	spin_lock(&pipe->lock);
34062306a36Sopenharmony_ci	old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth);
34162306a36Sopenharmony_ci	if (old == NULL) {
34262306a36Sopenharmony_ci		refcount_inc(&gss_msg->count);
34362306a36Sopenharmony_ci		list_add(&gss_msg->list, &pipe->in_downcall);
34462306a36Sopenharmony_ci	} else
34562306a36Sopenharmony_ci		gss_msg = old;
34662306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
34762306a36Sopenharmony_ci	return gss_msg;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void
35162306a36Sopenharmony_ci__gss_unhash_msg(struct gss_upcall_msg *gss_msg)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	list_del_init(&gss_msg->list);
35462306a36Sopenharmony_ci	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
35562306a36Sopenharmony_ci	wake_up_all(&gss_msg->waitqueue);
35662306a36Sopenharmony_ci	refcount_dec(&gss_msg->count);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void
36062306a36Sopenharmony_cigss_unhash_msg(struct gss_upcall_msg *gss_msg)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (list_empty(&gss_msg->list))
36562306a36Sopenharmony_ci		return;
36662306a36Sopenharmony_ci	spin_lock(&pipe->lock);
36762306a36Sopenharmony_ci	if (!list_empty(&gss_msg->list))
36862306a36Sopenharmony_ci		__gss_unhash_msg(gss_msg);
36962306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void
37362306a36Sopenharmony_cigss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	switch (gss_msg->msg.errno) {
37662306a36Sopenharmony_ci	case 0:
37762306a36Sopenharmony_ci		if (gss_msg->ctx == NULL)
37862306a36Sopenharmony_ci			break;
37962306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
38062306a36Sopenharmony_ci		gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
38162306a36Sopenharmony_ci		break;
38262306a36Sopenharmony_ci	case -EKEYEXPIRED:
38362306a36Sopenharmony_ci		set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	gss_cred->gc_upcall_timestamp = jiffies;
38662306a36Sopenharmony_ci	gss_cred->gc_upcall = NULL;
38762306a36Sopenharmony_ci	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void
39162306a36Sopenharmony_cigss_upcall_callback(struct rpc_task *task)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred,
39462306a36Sopenharmony_ci			struct gss_cred, gc_base);
39562306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
39662306a36Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	spin_lock(&pipe->lock);
39962306a36Sopenharmony_ci	gss_handle_downcall_result(gss_cred, gss_msg);
40062306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
40162306a36Sopenharmony_ci	task->tk_status = gss_msg->msg.errno;
40262306a36Sopenharmony_ci	gss_release_msg(gss_msg);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg,
40662306a36Sopenharmony_ci			      const struct cred *cred)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct user_namespace *userns = cred->user_ns;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	uid_t uid = from_kuid_munged(userns, gss_msg->uid);
41162306a36Sopenharmony_ci	memcpy(gss_msg->databuf, &uid, sizeof(uid));
41262306a36Sopenharmony_ci	gss_msg->msg.data = gss_msg->databuf;
41362306a36Sopenharmony_ci	gss_msg->msg.len = sizeof(uid);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf));
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic ssize_t
41962306a36Sopenharmony_cigss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg,
42062306a36Sopenharmony_ci		char __user *buf, size_t buflen)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg,
42362306a36Sopenharmony_ci						      struct gss_upcall_msg,
42462306a36Sopenharmony_ci						      msg);
42562306a36Sopenharmony_ci	if (msg->copied == 0)
42662306a36Sopenharmony_ci		gss_encode_v0_msg(gss_msg, file->f_cred);
42762306a36Sopenharmony_ci	return rpc_pipe_generic_upcall(file, msg, buf, buflen);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
43162306a36Sopenharmony_ci				const char *service_name,
43262306a36Sopenharmony_ci				const char *target_name,
43362306a36Sopenharmony_ci				const struct cred *cred)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct user_namespace *userns = cred->user_ns;
43662306a36Sopenharmony_ci	struct gss_api_mech *mech = gss_msg->auth->mech;
43762306a36Sopenharmony_ci	char *p = gss_msg->databuf;
43862306a36Sopenharmony_ci	size_t buflen = sizeof(gss_msg->databuf);
43962306a36Sopenharmony_ci	int len;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name,
44262306a36Sopenharmony_ci			from_kuid_munged(userns, gss_msg->uid));
44362306a36Sopenharmony_ci	buflen -= len;
44462306a36Sopenharmony_ci	p += len;
44562306a36Sopenharmony_ci	gss_msg->msg.len = len;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/*
44862306a36Sopenharmony_ci	 * target= is a full service principal that names the remote
44962306a36Sopenharmony_ci	 * identity that we are authenticating to.
45062306a36Sopenharmony_ci	 */
45162306a36Sopenharmony_ci	if (target_name) {
45262306a36Sopenharmony_ci		len = scnprintf(p, buflen, " target=%s", target_name);
45362306a36Sopenharmony_ci		buflen -= len;
45462306a36Sopenharmony_ci		p += len;
45562306a36Sopenharmony_ci		gss_msg->msg.len += len;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/*
45962306a36Sopenharmony_ci	 * gssd uses service= and srchost= to select a matching key from
46062306a36Sopenharmony_ci	 * the system's keytab to use as the source principal.
46162306a36Sopenharmony_ci	 *
46262306a36Sopenharmony_ci	 * service= is the service name part of the source principal,
46362306a36Sopenharmony_ci	 * or "*" (meaning choose any).
46462306a36Sopenharmony_ci	 *
46562306a36Sopenharmony_ci	 * srchost= is the hostname part of the source principal. When
46662306a36Sopenharmony_ci	 * not provided, gssd uses the local hostname.
46762306a36Sopenharmony_ci	 */
46862306a36Sopenharmony_ci	if (service_name) {
46962306a36Sopenharmony_ci		char *c = strchr(service_name, '@');
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (!c)
47262306a36Sopenharmony_ci			len = scnprintf(p, buflen, " service=%s",
47362306a36Sopenharmony_ci					service_name);
47462306a36Sopenharmony_ci		else
47562306a36Sopenharmony_ci			len = scnprintf(p, buflen,
47662306a36Sopenharmony_ci					" service=%.*s srchost=%s",
47762306a36Sopenharmony_ci					(int)(c - service_name),
47862306a36Sopenharmony_ci					service_name, c + 1);
47962306a36Sopenharmony_ci		buflen -= len;
48062306a36Sopenharmony_ci		p += len;
48162306a36Sopenharmony_ci		gss_msg->msg.len += len;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (mech->gm_upcall_enctypes) {
48562306a36Sopenharmony_ci		len = scnprintf(p, buflen, " enctypes=%s",
48662306a36Sopenharmony_ci				mech->gm_upcall_enctypes);
48762306a36Sopenharmony_ci		buflen -= len;
48862306a36Sopenharmony_ci		p += len;
48962306a36Sopenharmony_ci		gss_msg->msg.len += len;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	trace_rpcgss_upcall_msg(gss_msg->databuf);
49262306a36Sopenharmony_ci	len = scnprintf(p, buflen, "\n");
49362306a36Sopenharmony_ci	if (len == 0)
49462306a36Sopenharmony_ci		goto out_overflow;
49562306a36Sopenharmony_ci	gss_msg->msg.len += len;
49662306a36Sopenharmony_ci	gss_msg->msg.data = gss_msg->databuf;
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ciout_overflow:
49962306a36Sopenharmony_ci	WARN_ON_ONCE(1);
50062306a36Sopenharmony_ci	return -ENOMEM;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic ssize_t
50462306a36Sopenharmony_cigss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg,
50562306a36Sopenharmony_ci		char __user *buf, size_t buflen)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg,
50862306a36Sopenharmony_ci						      struct gss_upcall_msg,
50962306a36Sopenharmony_ci						      msg);
51062306a36Sopenharmony_ci	int err;
51162306a36Sopenharmony_ci	if (msg->copied == 0) {
51262306a36Sopenharmony_ci		err = gss_encode_v1_msg(gss_msg,
51362306a36Sopenharmony_ci					gss_msg->service_name,
51462306a36Sopenharmony_ci					gss_msg->auth->target_name,
51562306a36Sopenharmony_ci					file->f_cred);
51662306a36Sopenharmony_ci		if (err)
51762306a36Sopenharmony_ci			return err;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	return rpc_pipe_generic_upcall(file, msg, buf, buflen);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct gss_upcall_msg *
52362306a36Sopenharmony_cigss_alloc_msg(struct gss_auth *gss_auth,
52462306a36Sopenharmony_ci		kuid_t uid, const char *service_name)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
52762306a36Sopenharmony_ci	int vers;
52862306a36Sopenharmony_ci	int err = -ENOMEM;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL);
53162306a36Sopenharmony_ci	if (gss_msg == NULL)
53262306a36Sopenharmony_ci		goto err;
53362306a36Sopenharmony_ci	vers = get_pipe_version(gss_auth->net);
53462306a36Sopenharmony_ci	err = vers;
53562306a36Sopenharmony_ci	if (err < 0)
53662306a36Sopenharmony_ci		goto err_free_msg;
53762306a36Sopenharmony_ci	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
53862306a36Sopenharmony_ci	INIT_LIST_HEAD(&gss_msg->list);
53962306a36Sopenharmony_ci	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
54062306a36Sopenharmony_ci	init_waitqueue_head(&gss_msg->waitqueue);
54162306a36Sopenharmony_ci	refcount_set(&gss_msg->count, 1);
54262306a36Sopenharmony_ci	gss_msg->uid = uid;
54362306a36Sopenharmony_ci	gss_msg->auth = gss_auth;
54462306a36Sopenharmony_ci	kref_get(&gss_auth->kref);
54562306a36Sopenharmony_ci	if (service_name) {
54662306a36Sopenharmony_ci		gss_msg->service_name = kstrdup_const(service_name, GFP_KERNEL);
54762306a36Sopenharmony_ci		if (!gss_msg->service_name) {
54862306a36Sopenharmony_ci			err = -ENOMEM;
54962306a36Sopenharmony_ci			goto err_put_pipe_version;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci	return gss_msg;
55362306a36Sopenharmony_cierr_put_pipe_version:
55462306a36Sopenharmony_ci	put_pipe_version(gss_auth->net);
55562306a36Sopenharmony_cierr_free_msg:
55662306a36Sopenharmony_ci	kfree(gss_msg);
55762306a36Sopenharmony_cierr:
55862306a36Sopenharmony_ci	return ERR_PTR(err);
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic struct gss_upcall_msg *
56262306a36Sopenharmony_cigss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,
56562306a36Sopenharmony_ci			struct gss_cred, gc_base);
56662306a36Sopenharmony_ci	struct gss_upcall_msg *gss_new, *gss_msg;
56762306a36Sopenharmony_ci	kuid_t uid = cred->cr_cred->fsuid;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);
57062306a36Sopenharmony_ci	if (IS_ERR(gss_new))
57162306a36Sopenharmony_ci		return gss_new;
57262306a36Sopenharmony_ci	gss_msg = gss_add_msg(gss_new);
57362306a36Sopenharmony_ci	if (gss_msg == gss_new) {
57462306a36Sopenharmony_ci		int res;
57562306a36Sopenharmony_ci		refcount_inc(&gss_msg->count);
57662306a36Sopenharmony_ci		res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
57762306a36Sopenharmony_ci		if (res) {
57862306a36Sopenharmony_ci			gss_unhash_msg(gss_new);
57962306a36Sopenharmony_ci			refcount_dec(&gss_msg->count);
58062306a36Sopenharmony_ci			gss_release_msg(gss_new);
58162306a36Sopenharmony_ci			gss_msg = ERR_PTR(res);
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci	} else
58462306a36Sopenharmony_ci		gss_release_msg(gss_new);
58562306a36Sopenharmony_ci	return gss_msg;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic void warn_gssd(void)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n");
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic inline int
59462306a36Sopenharmony_cigss_refresh_upcall(struct rpc_task *task)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
59762306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth,
59862306a36Sopenharmony_ci			struct gss_auth, rpc_auth);
59962306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,
60062306a36Sopenharmony_ci			struct gss_cred, gc_base);
60162306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
60262306a36Sopenharmony_ci	struct rpc_pipe *pipe;
60362306a36Sopenharmony_ci	int err = 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	gss_msg = gss_setup_upcall(gss_auth, cred);
60662306a36Sopenharmony_ci	if (PTR_ERR(gss_msg) == -EAGAIN) {
60762306a36Sopenharmony_ci		/* XXX: warning on the first, under the assumption we
60862306a36Sopenharmony_ci		 * shouldn't normally hit this case on a refresh. */
60962306a36Sopenharmony_ci		warn_gssd();
61062306a36Sopenharmony_ci		rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue,
61162306a36Sopenharmony_ci				task, NULL, jiffies + (15 * HZ));
61262306a36Sopenharmony_ci		err = -EAGAIN;
61362306a36Sopenharmony_ci		goto out;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci	if (IS_ERR(gss_msg)) {
61662306a36Sopenharmony_ci		err = PTR_ERR(gss_msg);
61762306a36Sopenharmony_ci		goto out;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	pipe = gss_msg->pipe;
62062306a36Sopenharmony_ci	spin_lock(&pipe->lock);
62162306a36Sopenharmony_ci	if (gss_cred->gc_upcall != NULL)
62262306a36Sopenharmony_ci		rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
62362306a36Sopenharmony_ci	else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
62462306a36Sopenharmony_ci		gss_cred->gc_upcall = gss_msg;
62562306a36Sopenharmony_ci		/* gss_upcall_callback will release the reference to gss_upcall_msg */
62662306a36Sopenharmony_ci		refcount_inc(&gss_msg->count);
62762306a36Sopenharmony_ci		rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
62862306a36Sopenharmony_ci	} else {
62962306a36Sopenharmony_ci		gss_handle_downcall_result(gss_cred, gss_msg);
63062306a36Sopenharmony_ci		err = gss_msg->msg.errno;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
63362306a36Sopenharmony_ci	gss_release_msg(gss_msg);
63462306a36Sopenharmony_ciout:
63562306a36Sopenharmony_ci	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
63662306a36Sopenharmony_ci					     cred->cr_cred->fsuid), err);
63762306a36Sopenharmony_ci	return err;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic inline int
64162306a36Sopenharmony_cigss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct net *net = gss_auth->net;
64462306a36Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
64562306a36Sopenharmony_ci	struct rpc_pipe *pipe;
64662306a36Sopenharmony_ci	struct rpc_cred *cred = &gss_cred->gc_base;
64762306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
64862306a36Sopenharmony_ci	DEFINE_WAIT(wait);
64962306a36Sopenharmony_ci	int err;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ciretry:
65262306a36Sopenharmony_ci	err = 0;
65362306a36Sopenharmony_ci	/* if gssd is down, just skip upcalling altogether */
65462306a36Sopenharmony_ci	if (!gssd_running(net)) {
65562306a36Sopenharmony_ci		warn_gssd();
65662306a36Sopenharmony_ci		err = -EACCES;
65762306a36Sopenharmony_ci		goto out;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	gss_msg = gss_setup_upcall(gss_auth, cred);
66062306a36Sopenharmony_ci	if (PTR_ERR(gss_msg) == -EAGAIN) {
66162306a36Sopenharmony_ci		err = wait_event_interruptible_timeout(pipe_version_waitqueue,
66262306a36Sopenharmony_ci				sn->pipe_version >= 0, 15 * HZ);
66362306a36Sopenharmony_ci		if (sn->pipe_version < 0) {
66462306a36Sopenharmony_ci			warn_gssd();
66562306a36Sopenharmony_ci			err = -EACCES;
66662306a36Sopenharmony_ci		}
66762306a36Sopenharmony_ci		if (err < 0)
66862306a36Sopenharmony_ci			goto out;
66962306a36Sopenharmony_ci		goto retry;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	if (IS_ERR(gss_msg)) {
67262306a36Sopenharmony_ci		err = PTR_ERR(gss_msg);
67362306a36Sopenharmony_ci		goto out;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci	pipe = gss_msg->pipe;
67662306a36Sopenharmony_ci	for (;;) {
67762306a36Sopenharmony_ci		prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE);
67862306a36Sopenharmony_ci		spin_lock(&pipe->lock);
67962306a36Sopenharmony_ci		if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {
68062306a36Sopenharmony_ci			break;
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci		spin_unlock(&pipe->lock);
68362306a36Sopenharmony_ci		if (fatal_signal_pending(current)) {
68462306a36Sopenharmony_ci			err = -ERESTARTSYS;
68562306a36Sopenharmony_ci			goto out_intr;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci		schedule();
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	if (gss_msg->ctx) {
69062306a36Sopenharmony_ci		trace_rpcgss_ctx_init(gss_cred);
69162306a36Sopenharmony_ci		gss_cred_set_ctx(cred, gss_msg->ctx);
69262306a36Sopenharmony_ci	} else {
69362306a36Sopenharmony_ci		err = gss_msg->msg.errno;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
69662306a36Sopenharmony_ciout_intr:
69762306a36Sopenharmony_ci	finish_wait(&gss_msg->waitqueue, &wait);
69862306a36Sopenharmony_ci	gss_release_msg(gss_msg);
69962306a36Sopenharmony_ciout:
70062306a36Sopenharmony_ci	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
70162306a36Sopenharmony_ci					     cred->cr_cred->fsuid), err);
70262306a36Sopenharmony_ci	return err;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic struct gss_upcall_msg *
70662306a36Sopenharmony_cigss_find_downcall(struct rpc_pipe *pipe, kuid_t uid)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct gss_upcall_msg *pos;
70962306a36Sopenharmony_ci	list_for_each_entry(pos, &pipe->in_downcall, list) {
71062306a36Sopenharmony_ci		if (!uid_eq(pos->uid, uid))
71162306a36Sopenharmony_ci			continue;
71262306a36Sopenharmony_ci		if (!rpc_msg_is_inflight(&pos->msg))
71362306a36Sopenharmony_ci			continue;
71462306a36Sopenharmony_ci		refcount_inc(&pos->count);
71562306a36Sopenharmony_ci		return pos;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	return NULL;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci#define MSG_BUF_MAXSIZE 1024
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic ssize_t
72362306a36Sopenharmony_cigss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	const void *p, *end;
72662306a36Sopenharmony_ci	void *buf;
72762306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
72862306a36Sopenharmony_ci	struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe;
72962306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
73062306a36Sopenharmony_ci	uid_t id;
73162306a36Sopenharmony_ci	kuid_t uid;
73262306a36Sopenharmony_ci	ssize_t err = -EFBIG;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (mlen > MSG_BUF_MAXSIZE)
73562306a36Sopenharmony_ci		goto out;
73662306a36Sopenharmony_ci	err = -ENOMEM;
73762306a36Sopenharmony_ci	buf = kmalloc(mlen, GFP_KERNEL);
73862306a36Sopenharmony_ci	if (!buf)
73962306a36Sopenharmony_ci		goto out;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	err = -EFAULT;
74262306a36Sopenharmony_ci	if (copy_from_user(buf, src, mlen))
74362306a36Sopenharmony_ci		goto err;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	end = (const void *)((char *)buf + mlen);
74662306a36Sopenharmony_ci	p = simple_get_bytes(buf, end, &id, sizeof(id));
74762306a36Sopenharmony_ci	if (IS_ERR(p)) {
74862306a36Sopenharmony_ci		err = PTR_ERR(p);
74962306a36Sopenharmony_ci		goto err;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	uid = make_kuid(current_user_ns(), id);
75362306a36Sopenharmony_ci	if (!uid_valid(uid)) {
75462306a36Sopenharmony_ci		err = -EINVAL;
75562306a36Sopenharmony_ci		goto err;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	err = -ENOMEM;
75962306a36Sopenharmony_ci	ctx = gss_alloc_context();
76062306a36Sopenharmony_ci	if (ctx == NULL)
76162306a36Sopenharmony_ci		goto err;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	err = -ENOENT;
76462306a36Sopenharmony_ci	/* Find a matching upcall */
76562306a36Sopenharmony_ci	spin_lock(&pipe->lock);
76662306a36Sopenharmony_ci	gss_msg = gss_find_downcall(pipe, uid);
76762306a36Sopenharmony_ci	if (gss_msg == NULL) {
76862306a36Sopenharmony_ci		spin_unlock(&pipe->lock);
76962306a36Sopenharmony_ci		goto err_put_ctx;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci	list_del_init(&gss_msg->list);
77262306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);
77562306a36Sopenharmony_ci	if (IS_ERR(p)) {
77662306a36Sopenharmony_ci		err = PTR_ERR(p);
77762306a36Sopenharmony_ci		switch (err) {
77862306a36Sopenharmony_ci		case -EACCES:
77962306a36Sopenharmony_ci		case -EKEYEXPIRED:
78062306a36Sopenharmony_ci			gss_msg->msg.errno = err;
78162306a36Sopenharmony_ci			err = mlen;
78262306a36Sopenharmony_ci			break;
78362306a36Sopenharmony_ci		case -EFAULT:
78462306a36Sopenharmony_ci		case -ENOMEM:
78562306a36Sopenharmony_ci		case -EINVAL:
78662306a36Sopenharmony_ci		case -ENOSYS:
78762306a36Sopenharmony_ci			gss_msg->msg.errno = -EAGAIN;
78862306a36Sopenharmony_ci			break;
78962306a36Sopenharmony_ci		default:
79062306a36Sopenharmony_ci			printk(KERN_CRIT "%s: bad return from "
79162306a36Sopenharmony_ci				"gss_fill_context: %zd\n", __func__, err);
79262306a36Sopenharmony_ci			gss_msg->msg.errno = -EIO;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci		goto err_release_msg;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci	gss_msg->ctx = gss_get_ctx(ctx);
79762306a36Sopenharmony_ci	err = mlen;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cierr_release_msg:
80062306a36Sopenharmony_ci	spin_lock(&pipe->lock);
80162306a36Sopenharmony_ci	__gss_unhash_msg(gss_msg);
80262306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
80362306a36Sopenharmony_ci	gss_release_msg(gss_msg);
80462306a36Sopenharmony_cierr_put_ctx:
80562306a36Sopenharmony_ci	gss_put_ctx(ctx);
80662306a36Sopenharmony_cierr:
80762306a36Sopenharmony_ci	kfree(buf);
80862306a36Sopenharmony_ciout:
80962306a36Sopenharmony_ci	return err;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int gss_pipe_open(struct inode *inode, int new_version)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct net *net = inode->i_sb->s_fs_info;
81562306a36Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
81662306a36Sopenharmony_ci	int ret = 0;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	spin_lock(&pipe_version_lock);
81962306a36Sopenharmony_ci	if (sn->pipe_version < 0) {
82062306a36Sopenharmony_ci		/* First open of any gss pipe determines the version: */
82162306a36Sopenharmony_ci		sn->pipe_version = new_version;
82262306a36Sopenharmony_ci		rpc_wake_up(&pipe_version_rpc_waitqueue);
82362306a36Sopenharmony_ci		wake_up(&pipe_version_waitqueue);
82462306a36Sopenharmony_ci	} else if (sn->pipe_version != new_version) {
82562306a36Sopenharmony_ci		/* Trying to open a pipe of a different version */
82662306a36Sopenharmony_ci		ret = -EBUSY;
82762306a36Sopenharmony_ci		goto out;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci	atomic_inc(&sn->pipe_users);
83062306a36Sopenharmony_ciout:
83162306a36Sopenharmony_ci	spin_unlock(&pipe_version_lock);
83262306a36Sopenharmony_ci	return ret;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int gss_pipe_open_v0(struct inode *inode)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	return gss_pipe_open(inode, 0);
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int gss_pipe_open_v1(struct inode *inode)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	return gss_pipe_open(inode, 1);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void
84762306a36Sopenharmony_cigss_pipe_release(struct inode *inode)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct net *net = inode->i_sb->s_fs_info;
85062306a36Sopenharmony_ci	struct rpc_pipe *pipe = RPC_I(inode)->pipe;
85162306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cirestart:
85462306a36Sopenharmony_ci	spin_lock(&pipe->lock);
85562306a36Sopenharmony_ci	list_for_each_entry(gss_msg, &pipe->in_downcall, list) {
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		if (!list_empty(&gss_msg->msg.list))
85862306a36Sopenharmony_ci			continue;
85962306a36Sopenharmony_ci		gss_msg->msg.errno = -EPIPE;
86062306a36Sopenharmony_ci		refcount_inc(&gss_msg->count);
86162306a36Sopenharmony_ci		__gss_unhash_msg(gss_msg);
86262306a36Sopenharmony_ci		spin_unlock(&pipe->lock);
86362306a36Sopenharmony_ci		gss_release_msg(gss_msg);
86462306a36Sopenharmony_ci		goto restart;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci	spin_unlock(&pipe->lock);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	put_pipe_version(net);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic void
87262306a36Sopenharmony_cigss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (msg->errno < 0) {
87762306a36Sopenharmony_ci		refcount_inc(&gss_msg->count);
87862306a36Sopenharmony_ci		gss_unhash_msg(gss_msg);
87962306a36Sopenharmony_ci		if (msg->errno == -ETIMEDOUT)
88062306a36Sopenharmony_ci			warn_gssd();
88162306a36Sopenharmony_ci		gss_release_msg(gss_msg);
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci	gss_release_msg(gss_msg);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic void gss_pipe_dentry_destroy(struct dentry *dir,
88762306a36Sopenharmony_ci		struct rpc_pipe_dir_object *pdo)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct gss_pipe *gss_pipe = pdo->pdo_data;
89062306a36Sopenharmony_ci	struct rpc_pipe *pipe = gss_pipe->pipe;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (pipe->dentry != NULL) {
89362306a36Sopenharmony_ci		rpc_unlink(pipe->dentry);
89462306a36Sopenharmony_ci		pipe->dentry = NULL;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int gss_pipe_dentry_create(struct dentry *dir,
89962306a36Sopenharmony_ci		struct rpc_pipe_dir_object *pdo)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct gss_pipe *p = pdo->pdo_data;
90262306a36Sopenharmony_ci	struct dentry *dentry;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
90562306a36Sopenharmony_ci	if (IS_ERR(dentry))
90662306a36Sopenharmony_ci		return PTR_ERR(dentry);
90762306a36Sopenharmony_ci	p->pipe->dentry = dentry;
90862306a36Sopenharmony_ci	return 0;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
91262306a36Sopenharmony_ci	.create = gss_pipe_dentry_create,
91362306a36Sopenharmony_ci	.destroy = gss_pipe_dentry_destroy,
91462306a36Sopenharmony_ci};
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt,
91762306a36Sopenharmony_ci		const char *name,
91862306a36Sopenharmony_ci		const struct rpc_pipe_ops *upcall_ops)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct gss_pipe *p;
92162306a36Sopenharmony_ci	int err = -ENOMEM;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	p = kmalloc(sizeof(*p), GFP_KERNEL);
92462306a36Sopenharmony_ci	if (p == NULL)
92562306a36Sopenharmony_ci		goto err;
92662306a36Sopenharmony_ci	p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
92762306a36Sopenharmony_ci	if (IS_ERR(p->pipe)) {
92862306a36Sopenharmony_ci		err = PTR_ERR(p->pipe);
92962306a36Sopenharmony_ci		goto err_free_gss_pipe;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci	p->name = name;
93262306a36Sopenharmony_ci	p->clnt = clnt;
93362306a36Sopenharmony_ci	kref_init(&p->kref);
93462306a36Sopenharmony_ci	rpc_init_pipe_dir_object(&p->pdo,
93562306a36Sopenharmony_ci			&gss_pipe_dir_object_ops,
93662306a36Sopenharmony_ci			p);
93762306a36Sopenharmony_ci	return p;
93862306a36Sopenharmony_cierr_free_gss_pipe:
93962306a36Sopenharmony_ci	kfree(p);
94062306a36Sopenharmony_cierr:
94162306a36Sopenharmony_ci	return ERR_PTR(err);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistruct gss_alloc_pdo {
94562306a36Sopenharmony_ci	struct rpc_clnt *clnt;
94662306a36Sopenharmony_ci	const char *name;
94762306a36Sopenharmony_ci	const struct rpc_pipe_ops *upcall_ops;
94862306a36Sopenharmony_ci};
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	struct gss_pipe *gss_pipe;
95362306a36Sopenharmony_ci	struct gss_alloc_pdo *args = data;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (pdo->pdo_ops != &gss_pipe_dir_object_ops)
95662306a36Sopenharmony_ci		return 0;
95762306a36Sopenharmony_ci	gss_pipe = container_of(pdo, struct gss_pipe, pdo);
95862306a36Sopenharmony_ci	if (strcmp(gss_pipe->name, args->name) != 0)
95962306a36Sopenharmony_ci		return 0;
96062306a36Sopenharmony_ci	if (!kref_get_unless_zero(&gss_pipe->kref))
96162306a36Sopenharmony_ci		return 0;
96262306a36Sopenharmony_ci	return 1;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct gss_pipe *gss_pipe;
96862306a36Sopenharmony_ci	struct gss_alloc_pdo *args = data;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops);
97162306a36Sopenharmony_ci	if (!IS_ERR(gss_pipe))
97262306a36Sopenharmony_ci		return &gss_pipe->pdo;
97362306a36Sopenharmony_ci	return NULL;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt,
97762306a36Sopenharmony_ci		const char *name,
97862306a36Sopenharmony_ci		const struct rpc_pipe_ops *upcall_ops)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct net *net = rpc_net_ns(clnt);
98162306a36Sopenharmony_ci	struct rpc_pipe_dir_object *pdo;
98262306a36Sopenharmony_ci	struct gss_alloc_pdo args = {
98362306a36Sopenharmony_ci		.clnt = clnt,
98462306a36Sopenharmony_ci		.name = name,
98562306a36Sopenharmony_ci		.upcall_ops = upcall_ops,
98662306a36Sopenharmony_ci	};
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	pdo = rpc_find_or_alloc_pipe_dir_object(net,
98962306a36Sopenharmony_ci			&clnt->cl_pipedir_objects,
99062306a36Sopenharmony_ci			gss_pipe_match_pdo,
99162306a36Sopenharmony_ci			gss_pipe_alloc_pdo,
99262306a36Sopenharmony_ci			&args);
99362306a36Sopenharmony_ci	if (pdo != NULL)
99462306a36Sopenharmony_ci		return container_of(pdo, struct gss_pipe, pdo);
99562306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic void __gss_pipe_free(struct gss_pipe *p)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct rpc_clnt *clnt = p->clnt;
100162306a36Sopenharmony_ci	struct net *net = rpc_net_ns(clnt);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	rpc_remove_pipe_dir_object(net,
100462306a36Sopenharmony_ci			&clnt->cl_pipedir_objects,
100562306a36Sopenharmony_ci			&p->pdo);
100662306a36Sopenharmony_ci	rpc_destroy_pipe_data(p->pipe);
100762306a36Sopenharmony_ci	kfree(p);
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic void __gss_pipe_release(struct kref *kref)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct gss_pipe *p = container_of(kref, struct gss_pipe, kref);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	__gss_pipe_free(p);
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic void gss_pipe_free(struct gss_pipe *p)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	if (p != NULL)
102062306a36Sopenharmony_ci		kref_put(&p->kref, __gss_pipe_release);
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci/*
102462306a36Sopenharmony_ci * NOTE: we have the opportunity to use different
102562306a36Sopenharmony_ci * parameters based on the input flavor (which must be a pseudoflavor)
102662306a36Sopenharmony_ci */
102762306a36Sopenharmony_cistatic struct gss_auth *
102862306a36Sopenharmony_cigss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	rpc_authflavor_t flavor = args->pseudoflavor;
103162306a36Sopenharmony_ci	struct gss_auth *gss_auth;
103262306a36Sopenharmony_ci	struct gss_pipe *gss_pipe;
103362306a36Sopenharmony_ci	struct rpc_auth * auth;
103462306a36Sopenharmony_ci	int err = -ENOMEM; /* XXX? */
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
103762306a36Sopenharmony_ci		return ERR_PTR(err);
103862306a36Sopenharmony_ci	if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
103962306a36Sopenharmony_ci		goto out_dec;
104062306a36Sopenharmony_ci	INIT_HLIST_NODE(&gss_auth->hash);
104162306a36Sopenharmony_ci	gss_auth->target_name = NULL;
104262306a36Sopenharmony_ci	if (args->target_name) {
104362306a36Sopenharmony_ci		gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
104462306a36Sopenharmony_ci		if (gss_auth->target_name == NULL)
104562306a36Sopenharmony_ci			goto err_free;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci	gss_auth->client = clnt;
104862306a36Sopenharmony_ci	gss_auth->net = get_net_track(rpc_net_ns(clnt), &gss_auth->ns_tracker,
104962306a36Sopenharmony_ci				      GFP_KERNEL);
105062306a36Sopenharmony_ci	err = -EINVAL;
105162306a36Sopenharmony_ci	gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
105262306a36Sopenharmony_ci	if (!gss_auth->mech)
105362306a36Sopenharmony_ci		goto err_put_net;
105462306a36Sopenharmony_ci	gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
105562306a36Sopenharmony_ci	if (gss_auth->service == 0)
105662306a36Sopenharmony_ci		goto err_put_mech;
105762306a36Sopenharmony_ci	if (!gssd_running(gss_auth->net))
105862306a36Sopenharmony_ci		goto err_put_mech;
105962306a36Sopenharmony_ci	auth = &gss_auth->rpc_auth;
106062306a36Sopenharmony_ci	auth->au_cslack = GSS_CRED_SLACK >> 2;
106162306a36Sopenharmony_ci	BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE);
106262306a36Sopenharmony_ci	auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2;
106362306a36Sopenharmony_ci	auth->au_verfsize = GSS_VERF_SLACK >> 2;
106462306a36Sopenharmony_ci	auth->au_ralign = GSS_VERF_SLACK >> 2;
106562306a36Sopenharmony_ci	__set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags);
106662306a36Sopenharmony_ci	auth->au_ops = &authgss_ops;
106762306a36Sopenharmony_ci	auth->au_flavor = flavor;
106862306a36Sopenharmony_ci	if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor))
106962306a36Sopenharmony_ci		__set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags);
107062306a36Sopenharmony_ci	refcount_set(&auth->au_count, 1);
107162306a36Sopenharmony_ci	kref_init(&gss_auth->kref);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	err = rpcauth_init_credcache(auth);
107462306a36Sopenharmony_ci	if (err)
107562306a36Sopenharmony_ci		goto err_put_mech;
107662306a36Sopenharmony_ci	/*
107762306a36Sopenharmony_ci	 * Note: if we created the old pipe first, then someone who
107862306a36Sopenharmony_ci	 * examined the directory at the right moment might conclude
107962306a36Sopenharmony_ci	 * that we supported only the old pipe.  So we instead create
108062306a36Sopenharmony_ci	 * the new pipe first.
108162306a36Sopenharmony_ci	 */
108262306a36Sopenharmony_ci	gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
108362306a36Sopenharmony_ci	if (IS_ERR(gss_pipe)) {
108462306a36Sopenharmony_ci		err = PTR_ERR(gss_pipe);
108562306a36Sopenharmony_ci		goto err_destroy_credcache;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci	gss_auth->gss_pipe[1] = gss_pipe;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
109062306a36Sopenharmony_ci			&gss_upcall_ops_v0);
109162306a36Sopenharmony_ci	if (IS_ERR(gss_pipe)) {
109262306a36Sopenharmony_ci		err = PTR_ERR(gss_pipe);
109362306a36Sopenharmony_ci		goto err_destroy_pipe_1;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci	gss_auth->gss_pipe[0] = gss_pipe;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return gss_auth;
109862306a36Sopenharmony_cierr_destroy_pipe_1:
109962306a36Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
110062306a36Sopenharmony_cierr_destroy_credcache:
110162306a36Sopenharmony_ci	rpcauth_destroy_credcache(auth);
110262306a36Sopenharmony_cierr_put_mech:
110362306a36Sopenharmony_ci	gss_mech_put(gss_auth->mech);
110462306a36Sopenharmony_cierr_put_net:
110562306a36Sopenharmony_ci	put_net_track(gss_auth->net, &gss_auth->ns_tracker);
110662306a36Sopenharmony_cierr_free:
110762306a36Sopenharmony_ci	kfree(gss_auth->target_name);
110862306a36Sopenharmony_ci	kfree(gss_auth);
110962306a36Sopenharmony_ciout_dec:
111062306a36Sopenharmony_ci	module_put(THIS_MODULE);
111162306a36Sopenharmony_ci	trace_rpcgss_createauth(flavor, err);
111262306a36Sopenharmony_ci	return ERR_PTR(err);
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic void
111662306a36Sopenharmony_cigss_free(struct gss_auth *gss_auth)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[0]);
111962306a36Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
112062306a36Sopenharmony_ci	gss_mech_put(gss_auth->mech);
112162306a36Sopenharmony_ci	put_net_track(gss_auth->net, &gss_auth->ns_tracker);
112262306a36Sopenharmony_ci	kfree(gss_auth->target_name);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	kfree(gss_auth);
112562306a36Sopenharmony_ci	module_put(THIS_MODULE);
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic void
112962306a36Sopenharmony_cigss_free_callback(struct kref *kref)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	gss_free(gss_auth);
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic void
113762306a36Sopenharmony_cigss_put_auth(struct gss_auth *gss_auth)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	kref_put(&gss_auth->kref, gss_free_callback);
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic void
114362306a36Sopenharmony_cigss_destroy(struct rpc_auth *auth)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth,
114662306a36Sopenharmony_ci			struct gss_auth, rpc_auth);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (hash_hashed(&gss_auth->hash)) {
114962306a36Sopenharmony_ci		spin_lock(&gss_auth_hash_lock);
115062306a36Sopenharmony_ci		hash_del(&gss_auth->hash);
115162306a36Sopenharmony_ci		spin_unlock(&gss_auth_hash_lock);
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[0]);
115562306a36Sopenharmony_ci	gss_auth->gss_pipe[0] = NULL;
115662306a36Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
115762306a36Sopenharmony_ci	gss_auth->gss_pipe[1] = NULL;
115862306a36Sopenharmony_ci	rpcauth_destroy_credcache(auth);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	gss_put_auth(gss_auth);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci * Auths may be shared between rpc clients that were cloned from a
116562306a36Sopenharmony_ci * common client with the same xprt, if they also share the flavor and
116662306a36Sopenharmony_ci * target_name.
116762306a36Sopenharmony_ci *
116862306a36Sopenharmony_ci * The auth is looked up from the oldest parent sharing the same
116962306a36Sopenharmony_ci * cl_xprt, and the auth itself references only that common parent
117062306a36Sopenharmony_ci * (which is guaranteed to last as long as any of its descendants).
117162306a36Sopenharmony_ci */
117262306a36Sopenharmony_cistatic struct gss_auth *
117362306a36Sopenharmony_cigss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args,
117462306a36Sopenharmony_ci		struct rpc_clnt *clnt,
117562306a36Sopenharmony_ci		struct gss_auth *new)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct gss_auth *gss_auth;
117862306a36Sopenharmony_ci	unsigned long hashval = (unsigned long)clnt;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	spin_lock(&gss_auth_hash_lock);
118162306a36Sopenharmony_ci	hash_for_each_possible(gss_auth_hash_table,
118262306a36Sopenharmony_ci			gss_auth,
118362306a36Sopenharmony_ci			hash,
118462306a36Sopenharmony_ci			hashval) {
118562306a36Sopenharmony_ci		if (gss_auth->client != clnt)
118662306a36Sopenharmony_ci			continue;
118762306a36Sopenharmony_ci		if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
118862306a36Sopenharmony_ci			continue;
118962306a36Sopenharmony_ci		if (gss_auth->target_name != args->target_name) {
119062306a36Sopenharmony_ci			if (gss_auth->target_name == NULL)
119162306a36Sopenharmony_ci				continue;
119262306a36Sopenharmony_ci			if (args->target_name == NULL)
119362306a36Sopenharmony_ci				continue;
119462306a36Sopenharmony_ci			if (strcmp(gss_auth->target_name, args->target_name))
119562306a36Sopenharmony_ci				continue;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci		if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count))
119862306a36Sopenharmony_ci			continue;
119962306a36Sopenharmony_ci		goto out;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci	if (new)
120262306a36Sopenharmony_ci		hash_add(gss_auth_hash_table, &new->hash, hashval);
120362306a36Sopenharmony_ci	gss_auth = new;
120462306a36Sopenharmony_ciout:
120562306a36Sopenharmony_ci	spin_unlock(&gss_auth_hash_lock);
120662306a36Sopenharmony_ci	return gss_auth;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic struct gss_auth *
121062306a36Sopenharmony_cigss_create_hashed(const struct rpc_auth_create_args *args,
121162306a36Sopenharmony_ci		  struct rpc_clnt *clnt)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	struct gss_auth *gss_auth;
121462306a36Sopenharmony_ci	struct gss_auth *new;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
121762306a36Sopenharmony_ci	if (gss_auth != NULL)
121862306a36Sopenharmony_ci		goto out;
121962306a36Sopenharmony_ci	new = gss_create_new(args, clnt);
122062306a36Sopenharmony_ci	if (IS_ERR(new))
122162306a36Sopenharmony_ci		return new;
122262306a36Sopenharmony_ci	gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
122362306a36Sopenharmony_ci	if (gss_auth != new)
122462306a36Sopenharmony_ci		gss_destroy(&new->rpc_auth);
122562306a36Sopenharmony_ciout:
122662306a36Sopenharmony_ci	return gss_auth;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic struct rpc_auth *
123062306a36Sopenharmony_cigss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct gss_auth *gss_auth;
123362306a36Sopenharmony_ci	struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	while (clnt != clnt->cl_parent) {
123662306a36Sopenharmony_ci		struct rpc_clnt *parent = clnt->cl_parent;
123762306a36Sopenharmony_ci		/* Find the original parent for this transport */
123862306a36Sopenharmony_ci		if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps)
123962306a36Sopenharmony_ci			break;
124062306a36Sopenharmony_ci		clnt = parent;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	gss_auth = gss_create_hashed(args, clnt);
124462306a36Sopenharmony_ci	if (IS_ERR(gss_auth))
124562306a36Sopenharmony_ci		return ERR_CAST(gss_auth);
124662306a36Sopenharmony_ci	return &gss_auth->rpc_auth;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cistatic struct gss_cred *
125062306a36Sopenharmony_cigss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	struct gss_cred *new;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Make a copy of the cred so that we can reference count it */
125562306a36Sopenharmony_ci	new = kzalloc(sizeof(*gss_cred), GFP_KERNEL);
125662306a36Sopenharmony_ci	if (new) {
125762306a36Sopenharmony_ci		struct auth_cred acred = {
125862306a36Sopenharmony_ci			.cred = gss_cred->gc_base.cr_cred,
125962306a36Sopenharmony_ci		};
126062306a36Sopenharmony_ci		struct gss_cl_ctx *ctx =
126162306a36Sopenharmony_ci			rcu_dereference_protected(gss_cred->gc_ctx, 1);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		rpcauth_init_cred(&new->gc_base, &acred,
126462306a36Sopenharmony_ci				&gss_auth->rpc_auth,
126562306a36Sopenharmony_ci				&gss_nullops);
126662306a36Sopenharmony_ci		new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
126762306a36Sopenharmony_ci		new->gc_service = gss_cred->gc_service;
126862306a36Sopenharmony_ci		new->gc_principal = gss_cred->gc_principal;
126962306a36Sopenharmony_ci		kref_get(&gss_auth->kref);
127062306a36Sopenharmony_ci		rcu_assign_pointer(new->gc_ctx, ctx);
127162306a36Sopenharmony_ci		gss_get_ctx(ctx);
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci	return new;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci/*
127762306a36Sopenharmony_ci * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call
127862306a36Sopenharmony_ci * to the server with the GSS control procedure field set to
127962306a36Sopenharmony_ci * RPC_GSS_PROC_DESTROY. This should normally cause the server to release
128062306a36Sopenharmony_ci * all RPCSEC_GSS state associated with that context.
128162306a36Sopenharmony_ci */
128262306a36Sopenharmony_cistatic void
128362306a36Sopenharmony_cigss_send_destroy_context(struct rpc_cred *cred)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
128662306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
128762306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
128862306a36Sopenharmony_ci	struct gss_cred *new;
128962306a36Sopenharmony_ci	struct rpc_task *task;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	new = gss_dup_cred(gss_auth, gss_cred);
129262306a36Sopenharmony_ci	if (new) {
129362306a36Sopenharmony_ci		ctx->gc_proc = RPC_GSS_PROC_DESTROY;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci		trace_rpcgss_ctx_destroy(gss_cred);
129662306a36Sopenharmony_ci		task = rpc_call_null(gss_auth->client, &new->gc_base,
129762306a36Sopenharmony_ci				     RPC_TASK_ASYNC);
129862306a36Sopenharmony_ci		if (!IS_ERR(task))
129962306a36Sopenharmony_ci			rpc_put_task(task);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		put_rpccred(&new->gc_base);
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
130662306a36Sopenharmony_ci * to create a new cred or context, so they check that things have been
130762306a36Sopenharmony_ci * allocated before freeing them. */
130862306a36Sopenharmony_cistatic void
130962306a36Sopenharmony_cigss_do_free_ctx(struct gss_cl_ctx *ctx)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	gss_delete_sec_context(&ctx->gc_gss_ctx);
131262306a36Sopenharmony_ci	kfree(ctx->gc_wire_ctx.data);
131362306a36Sopenharmony_ci	kfree(ctx->gc_acceptor.data);
131462306a36Sopenharmony_ci	kfree(ctx);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic void
131862306a36Sopenharmony_cigss_free_ctx_callback(struct rcu_head *head)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu);
132162306a36Sopenharmony_ci	gss_do_free_ctx(ctx);
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic void
132562306a36Sopenharmony_cigss_free_ctx(struct gss_cl_ctx *ctx)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	call_rcu(&ctx->gc_rcu, gss_free_ctx_callback);
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic void
133162306a36Sopenharmony_cigss_free_cred(struct gss_cred *gss_cred)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	kfree(gss_cred);
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic void
133762306a36Sopenharmony_cigss_free_cred_callback(struct rcu_head *head)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu);
134062306a36Sopenharmony_ci	gss_free_cred(gss_cred);
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic void
134462306a36Sopenharmony_cigss_destroy_nullcred(struct rpc_cred *cred)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
134762306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
134862306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	RCU_INIT_POINTER(gss_cred->gc_ctx, NULL);
135162306a36Sopenharmony_ci	put_cred(cred->cr_cred);
135262306a36Sopenharmony_ci	call_rcu(&cred->cr_rcu, gss_free_cred_callback);
135362306a36Sopenharmony_ci	if (ctx)
135462306a36Sopenharmony_ci		gss_put_ctx(ctx);
135562306a36Sopenharmony_ci	gss_put_auth(gss_auth);
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic void
135962306a36Sopenharmony_cigss_destroy_cred(struct rpc_cred *cred)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0)
136262306a36Sopenharmony_ci		gss_send_destroy_context(cred);
136362306a36Sopenharmony_ci	gss_destroy_nullcred(cred);
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic int
136762306a36Sopenharmony_cigss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits);
137062306a36Sopenharmony_ci}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci/*
137362306a36Sopenharmony_ci * Lookup RPCSEC_GSS cred for the current process
137462306a36Sopenharmony_ci */
137562306a36Sopenharmony_cistatic struct rpc_cred *gss_lookup_cred(struct rpc_auth *auth,
137662306a36Sopenharmony_ci					struct auth_cred *acred, int flags)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	return rpcauth_lookup_credcache(auth, acred, flags,
137962306a36Sopenharmony_ci					rpc_task_gfp_mask());
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic struct rpc_cred *
138362306a36Sopenharmony_cigss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
138662306a36Sopenharmony_ci	struct gss_cred	*cred = NULL;
138762306a36Sopenharmony_ci	int err = -ENOMEM;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (!(cred = kzalloc(sizeof(*cred), gfp)))
139062306a36Sopenharmony_ci		goto out_err;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
139362306a36Sopenharmony_ci	/*
139462306a36Sopenharmony_ci	 * Note: in order to force a call to call_refresh(), we deliberately
139562306a36Sopenharmony_ci	 * fail to flag the credential as RPCAUTH_CRED_UPTODATE.
139662306a36Sopenharmony_ci	 */
139762306a36Sopenharmony_ci	cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;
139862306a36Sopenharmony_ci	cred->gc_service = gss_auth->service;
139962306a36Sopenharmony_ci	cred->gc_principal = acred->principal;
140062306a36Sopenharmony_ci	kref_get(&gss_auth->kref);
140162306a36Sopenharmony_ci	return &cred->gc_base;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ciout_err:
140462306a36Sopenharmony_ci	return ERR_PTR(err);
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic int
140862306a36Sopenharmony_cigss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
141162306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base);
141262306a36Sopenharmony_ci	int err;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	do {
141562306a36Sopenharmony_ci		err = gss_create_upcall(gss_auth, gss_cred);
141662306a36Sopenharmony_ci	} while (err == -EAGAIN);
141762306a36Sopenharmony_ci	return err;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_cistatic char *
142162306a36Sopenharmony_cigss_stringify_acceptor(struct rpc_cred *cred)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	char *string = NULL;
142462306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
142562306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
142662306a36Sopenharmony_ci	unsigned int len;
142762306a36Sopenharmony_ci	struct xdr_netobj *acceptor;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	rcu_read_lock();
143062306a36Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
143162306a36Sopenharmony_ci	if (!ctx)
143262306a36Sopenharmony_ci		goto out;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	len = ctx->gc_acceptor.len;
143562306a36Sopenharmony_ci	rcu_read_unlock();
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	/* no point if there's no string */
143862306a36Sopenharmony_ci	if (!len)
143962306a36Sopenharmony_ci		return NULL;
144062306a36Sopenharmony_cirealloc:
144162306a36Sopenharmony_ci	string = kmalloc(len + 1, GFP_KERNEL);
144262306a36Sopenharmony_ci	if (!string)
144362306a36Sopenharmony_ci		return NULL;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	rcu_read_lock();
144662306a36Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	/* did the ctx disappear or was it replaced by one with no acceptor? */
144962306a36Sopenharmony_ci	if (!ctx || !ctx->gc_acceptor.len) {
145062306a36Sopenharmony_ci		kfree(string);
145162306a36Sopenharmony_ci		string = NULL;
145262306a36Sopenharmony_ci		goto out;
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	acceptor = &ctx->gc_acceptor;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	/*
145862306a36Sopenharmony_ci	 * Did we find a new acceptor that's longer than the original? Allocate
145962306a36Sopenharmony_ci	 * a longer buffer and try again.
146062306a36Sopenharmony_ci	 */
146162306a36Sopenharmony_ci	if (len < acceptor->len) {
146262306a36Sopenharmony_ci		len = acceptor->len;
146362306a36Sopenharmony_ci		rcu_read_unlock();
146462306a36Sopenharmony_ci		kfree(string);
146562306a36Sopenharmony_ci		goto realloc;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	memcpy(string, acceptor->data, acceptor->len);
146962306a36Sopenharmony_ci	string[acceptor->len] = '\0';
147062306a36Sopenharmony_ciout:
147162306a36Sopenharmony_ci	rcu_read_unlock();
147262306a36Sopenharmony_ci	return string;
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci/*
147662306a36Sopenharmony_ci * Returns -EACCES if GSS context is NULL or will expire within the
147762306a36Sopenharmony_ci * timeout (miliseconds)
147862306a36Sopenharmony_ci */
147962306a36Sopenharmony_cistatic int
148062306a36Sopenharmony_cigss_key_timeout(struct rpc_cred *rc)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
148362306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
148462306a36Sopenharmony_ci	unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ);
148562306a36Sopenharmony_ci	int ret = 0;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	rcu_read_lock();
148862306a36Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
148962306a36Sopenharmony_ci	if (!ctx || time_after(timeout, ctx->gc_expiry))
149062306a36Sopenharmony_ci		ret = -EACCES;
149162306a36Sopenharmony_ci	rcu_read_unlock();
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return ret;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int
149762306a36Sopenharmony_cigss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
150062306a36Sopenharmony_ci	struct gss_cl_ctx *ctx;
150162306a36Sopenharmony_ci	int ret;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
150462306a36Sopenharmony_ci		goto out;
150562306a36Sopenharmony_ci	/* Don't match with creds that have expired. */
150662306a36Sopenharmony_ci	rcu_read_lock();
150762306a36Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
150862306a36Sopenharmony_ci	if (!ctx || time_after(jiffies, ctx->gc_expiry)) {
150962306a36Sopenharmony_ci		rcu_read_unlock();
151062306a36Sopenharmony_ci		return 0;
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci	rcu_read_unlock();
151362306a36Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags))
151462306a36Sopenharmony_ci		return 0;
151562306a36Sopenharmony_ciout:
151662306a36Sopenharmony_ci	if (acred->principal != NULL) {
151762306a36Sopenharmony_ci		if (gss_cred->gc_principal == NULL)
151862306a36Sopenharmony_ci			return 0;
151962306a36Sopenharmony_ci		ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
152062306a36Sopenharmony_ci	} else {
152162306a36Sopenharmony_ci		if (gss_cred->gc_principal != NULL)
152262306a36Sopenharmony_ci			return 0;
152362306a36Sopenharmony_ci		ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid);
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci	return ret;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci/*
152962306a36Sopenharmony_ci * Marshal credentials.
153062306a36Sopenharmony_ci *
153162306a36Sopenharmony_ci * The expensive part is computing the verifier. We can't cache a
153262306a36Sopenharmony_ci * pre-computed version of the verifier because the seqno, which
153362306a36Sopenharmony_ci * is different every time, is included in the MIC.
153462306a36Sopenharmony_ci */
153562306a36Sopenharmony_cistatic int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
153862306a36Sopenharmony_ci	struct rpc_cred *cred = req->rq_cred;
153962306a36Sopenharmony_ci	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
154062306a36Sopenharmony_ci						 gc_base);
154162306a36Sopenharmony_ci	struct gss_cl_ctx	*ctx = gss_cred_get_ctx(cred);
154262306a36Sopenharmony_ci	__be32		*p, *cred_len;
154362306a36Sopenharmony_ci	u32             maj_stat = 0;
154462306a36Sopenharmony_ci	struct xdr_netobj mic;
154562306a36Sopenharmony_ci	struct kvec	iov;
154662306a36Sopenharmony_ci	struct xdr_buf	verf_buf;
154762306a36Sopenharmony_ci	int status;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* Credential */
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 7 * sizeof(*p) +
155262306a36Sopenharmony_ci			      ctx->gc_wire_ctx.len);
155362306a36Sopenharmony_ci	if (!p)
155462306a36Sopenharmony_ci		goto marshal_failed;
155562306a36Sopenharmony_ci	*p++ = rpc_auth_gss;
155662306a36Sopenharmony_ci	cred_len = p++;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	spin_lock(&ctx->gc_seq_lock);
155962306a36Sopenharmony_ci	req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
156062306a36Sopenharmony_ci	spin_unlock(&ctx->gc_seq_lock);
156162306a36Sopenharmony_ci	if (req->rq_seqno == MAXSEQ)
156262306a36Sopenharmony_ci		goto expired;
156362306a36Sopenharmony_ci	trace_rpcgss_seqno(task);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	*p++ = cpu_to_be32(RPC_GSS_VERSION);
156662306a36Sopenharmony_ci	*p++ = cpu_to_be32(ctx->gc_proc);
156762306a36Sopenharmony_ci	*p++ = cpu_to_be32(req->rq_seqno);
156862306a36Sopenharmony_ci	*p++ = cpu_to_be32(gss_cred->gc_service);
156962306a36Sopenharmony_ci	p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
157062306a36Sopenharmony_ci	*cred_len = cpu_to_be32((p - (cred_len + 1)) << 2);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	/* Verifier */
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	/* We compute the checksum for the verifier over the xdr-encoded bytes
157562306a36Sopenharmony_ci	 * starting with the xid and ending at the end of the credential: */
157662306a36Sopenharmony_ci	iov.iov_base = req->rq_snd_buf.head[0].iov_base;
157762306a36Sopenharmony_ci	iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;
157862306a36Sopenharmony_ci	xdr_buf_from_iov(&iov, &verf_buf);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, sizeof(*p));
158162306a36Sopenharmony_ci	if (!p)
158262306a36Sopenharmony_ci		goto marshal_failed;
158362306a36Sopenharmony_ci	*p++ = rpc_auth_gss;
158462306a36Sopenharmony_ci	mic.data = (u8 *)(p + 1);
158562306a36Sopenharmony_ci	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
158662306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
158762306a36Sopenharmony_ci		goto expired;
158862306a36Sopenharmony_ci	else if (maj_stat != 0)
158962306a36Sopenharmony_ci		goto bad_mic;
159062306a36Sopenharmony_ci	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
159162306a36Sopenharmony_ci		goto marshal_failed;
159262306a36Sopenharmony_ci	status = 0;
159362306a36Sopenharmony_ciout:
159462306a36Sopenharmony_ci	gss_put_ctx(ctx);
159562306a36Sopenharmony_ci	return status;
159662306a36Sopenharmony_ciexpired:
159762306a36Sopenharmony_ci	clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
159862306a36Sopenharmony_ci	status = -EKEYEXPIRED;
159962306a36Sopenharmony_ci	goto out;
160062306a36Sopenharmony_cimarshal_failed:
160162306a36Sopenharmony_ci	status = -EMSGSIZE;
160262306a36Sopenharmony_ci	goto out;
160362306a36Sopenharmony_cibad_mic:
160462306a36Sopenharmony_ci	trace_rpcgss_get_mic(task, maj_stat);
160562306a36Sopenharmony_ci	status = -EIO;
160662306a36Sopenharmony_ci	goto out;
160762306a36Sopenharmony_ci}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_cistatic int gss_renew_cred(struct rpc_task *task)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct rpc_cred *oldcred = task->tk_rqstp->rq_cred;
161262306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(oldcred,
161362306a36Sopenharmony_ci						 struct gss_cred,
161462306a36Sopenharmony_ci						 gc_base);
161562306a36Sopenharmony_ci	struct rpc_auth *auth = oldcred->cr_auth;
161662306a36Sopenharmony_ci	struct auth_cred acred = {
161762306a36Sopenharmony_ci		.cred = oldcred->cr_cred,
161862306a36Sopenharmony_ci		.principal = gss_cred->gc_principal,
161962306a36Sopenharmony_ci	};
162062306a36Sopenharmony_ci	struct rpc_cred *new;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW);
162362306a36Sopenharmony_ci	if (IS_ERR(new))
162462306a36Sopenharmony_ci		return PTR_ERR(new);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	task->tk_rqstp->rq_cred = new;
162762306a36Sopenharmony_ci	put_rpccred(oldcred);
162862306a36Sopenharmony_ci	return 0;
162962306a36Sopenharmony_ci}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_cistatic int gss_cred_is_negative_entry(struct rpc_cred *cred)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) {
163462306a36Sopenharmony_ci		unsigned long now = jiffies;
163562306a36Sopenharmony_ci		unsigned long begin, expire;
163662306a36Sopenharmony_ci		struct gss_cred *gss_cred;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci		gss_cred = container_of(cred, struct gss_cred, gc_base);
163962306a36Sopenharmony_ci		begin = gss_cred->gc_upcall_timestamp;
164062306a36Sopenharmony_ci		expire = begin + gss_expired_cred_retry_delay * HZ;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		if (time_in_range_open(now, begin, expire))
164362306a36Sopenharmony_ci			return 1;
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci	return 0;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci/*
164962306a36Sopenharmony_ci* Refresh credentials. XXX - finish
165062306a36Sopenharmony_ci*/
165162306a36Sopenharmony_cistatic int
165262306a36Sopenharmony_cigss_refresh(struct rpc_task *task)
165362306a36Sopenharmony_ci{
165462306a36Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
165562306a36Sopenharmony_ci	int ret = 0;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (gss_cred_is_negative_entry(cred))
165862306a36Sopenharmony_ci		return -EKEYEXPIRED;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
166162306a36Sopenharmony_ci			!test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
166262306a36Sopenharmony_ci		ret = gss_renew_cred(task);
166362306a36Sopenharmony_ci		if (ret < 0)
166462306a36Sopenharmony_ci			goto out;
166562306a36Sopenharmony_ci		cred = task->tk_rqstp->rq_cred;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
166962306a36Sopenharmony_ci		ret = gss_refresh_upcall(task);
167062306a36Sopenharmony_ciout:
167162306a36Sopenharmony_ci	return ret;
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci/* Dummy refresh routine: used only when destroying the context */
167562306a36Sopenharmony_cistatic int
167662306a36Sopenharmony_cigss_refresh_null(struct rpc_task *task)
167762306a36Sopenharmony_ci{
167862306a36Sopenharmony_ci	return 0;
167962306a36Sopenharmony_ci}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_cistatic int
168262306a36Sopenharmony_cigss_validate(struct rpc_task *task, struct xdr_stream *xdr)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
168562306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
168662306a36Sopenharmony_ci	__be32		*p, *seq = NULL;
168762306a36Sopenharmony_ci	struct kvec	iov;
168862306a36Sopenharmony_ci	struct xdr_buf	verf_buf;
168962306a36Sopenharmony_ci	struct xdr_netobj mic;
169062306a36Sopenharmony_ci	u32		len, maj_stat;
169162306a36Sopenharmony_ci	int		status;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
169462306a36Sopenharmony_ci	if (!p)
169562306a36Sopenharmony_ci		goto validate_failed;
169662306a36Sopenharmony_ci	if (*p++ != rpc_auth_gss)
169762306a36Sopenharmony_ci		goto validate_failed;
169862306a36Sopenharmony_ci	len = be32_to_cpup(p);
169962306a36Sopenharmony_ci	if (len > RPC_MAX_AUTH_SIZE)
170062306a36Sopenharmony_ci		goto validate_failed;
170162306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, len);
170262306a36Sopenharmony_ci	if (!p)
170362306a36Sopenharmony_ci		goto validate_failed;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	seq = kmalloc(4, GFP_KERNEL);
170662306a36Sopenharmony_ci	if (!seq)
170762306a36Sopenharmony_ci		goto validate_failed;
170862306a36Sopenharmony_ci	*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
170962306a36Sopenharmony_ci	iov.iov_base = seq;
171062306a36Sopenharmony_ci	iov.iov_len = 4;
171162306a36Sopenharmony_ci	xdr_buf_from_iov(&iov, &verf_buf);
171262306a36Sopenharmony_ci	mic.data = (u8 *)p;
171362306a36Sopenharmony_ci	mic.len = len;
171462306a36Sopenharmony_ci	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
171562306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
171662306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
171762306a36Sopenharmony_ci	if (maj_stat)
171862306a36Sopenharmony_ci		goto bad_mic;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	/* We leave it to unwrap to calculate au_rslack. For now we just
172162306a36Sopenharmony_ci	 * calculate the length of the verifier: */
172262306a36Sopenharmony_ci	if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags))
172362306a36Sopenharmony_ci		cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
172462306a36Sopenharmony_ci	status = 0;
172562306a36Sopenharmony_ciout:
172662306a36Sopenharmony_ci	gss_put_ctx(ctx);
172762306a36Sopenharmony_ci	kfree(seq);
172862306a36Sopenharmony_ci	return status;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_civalidate_failed:
173162306a36Sopenharmony_ci	status = -EIO;
173262306a36Sopenharmony_ci	goto out;
173362306a36Sopenharmony_cibad_mic:
173462306a36Sopenharmony_ci	trace_rpcgss_verify_mic(task, maj_stat);
173562306a36Sopenharmony_ci	status = -EACCES;
173662306a36Sopenharmony_ci	goto out;
173762306a36Sopenharmony_ci}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_cistatic noinline_for_stack int
174062306a36Sopenharmony_cigss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
174162306a36Sopenharmony_ci		   struct rpc_task *task, struct xdr_stream *xdr)
174262306a36Sopenharmony_ci{
174362306a36Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
174462306a36Sopenharmony_ci	struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf;
174562306a36Sopenharmony_ci	struct xdr_netobj mic;
174662306a36Sopenharmony_ci	__be32 *p, *integ_len;
174762306a36Sopenharmony_ci	u32 offset, maj_stat;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
175062306a36Sopenharmony_ci	if (!p)
175162306a36Sopenharmony_ci		goto wrap_failed;
175262306a36Sopenharmony_ci	integ_len = p++;
175362306a36Sopenharmony_ci	*p = cpu_to_be32(rqstp->rq_seqno);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (rpcauth_wrap_req_encode(task, xdr))
175662306a36Sopenharmony_ci		goto wrap_failed;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
175962306a36Sopenharmony_ci	if (xdr_buf_subsegment(snd_buf, &integ_buf,
176062306a36Sopenharmony_ci				offset, snd_buf->len - offset))
176162306a36Sopenharmony_ci		goto wrap_failed;
176262306a36Sopenharmony_ci	*integ_len = cpu_to_be32(integ_buf.len);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 0);
176562306a36Sopenharmony_ci	if (!p)
176662306a36Sopenharmony_ci		goto wrap_failed;
176762306a36Sopenharmony_ci	mic.data = (u8 *)(p + 1);
176862306a36Sopenharmony_ci	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
176962306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
177062306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
177162306a36Sopenharmony_ci	else if (maj_stat)
177262306a36Sopenharmony_ci		goto bad_mic;
177362306a36Sopenharmony_ci	/* Check that the trailing MIC fit in the buffer, after the fact */
177462306a36Sopenharmony_ci	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
177562306a36Sopenharmony_ci		goto wrap_failed;
177662306a36Sopenharmony_ci	return 0;
177762306a36Sopenharmony_ciwrap_failed:
177862306a36Sopenharmony_ci	return -EMSGSIZE;
177962306a36Sopenharmony_cibad_mic:
178062306a36Sopenharmony_ci	trace_rpcgss_get_mic(task, maj_stat);
178162306a36Sopenharmony_ci	return -EIO;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic void
178562306a36Sopenharmony_cipriv_release_snd_buf(struct rpc_rqst *rqstp)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	int i;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	for (i=0; i < rqstp->rq_enc_pages_num; i++)
179062306a36Sopenharmony_ci		__free_page(rqstp->rq_enc_pages[i]);
179162306a36Sopenharmony_ci	kfree(rqstp->rq_enc_pages);
179262306a36Sopenharmony_ci	rqstp->rq_release_snd_buf = NULL;
179362306a36Sopenharmony_ci}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_cistatic int
179662306a36Sopenharmony_cialloc_enc_pages(struct rpc_rqst *rqstp)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
179962306a36Sopenharmony_ci	int first, last, i;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (rqstp->rq_release_snd_buf)
180262306a36Sopenharmony_ci		rqstp->rq_release_snd_buf(rqstp);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (snd_buf->page_len == 0) {
180562306a36Sopenharmony_ci		rqstp->rq_enc_pages_num = 0;
180662306a36Sopenharmony_ci		return 0;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	first = snd_buf->page_base >> PAGE_SHIFT;
181062306a36Sopenharmony_ci	last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT;
181162306a36Sopenharmony_ci	rqstp->rq_enc_pages_num = last - first + 1 + 1;
181262306a36Sopenharmony_ci	rqstp->rq_enc_pages
181362306a36Sopenharmony_ci		= kmalloc_array(rqstp->rq_enc_pages_num,
181462306a36Sopenharmony_ci				sizeof(struct page *),
181562306a36Sopenharmony_ci				GFP_KERNEL);
181662306a36Sopenharmony_ci	if (!rqstp->rq_enc_pages)
181762306a36Sopenharmony_ci		goto out;
181862306a36Sopenharmony_ci	for (i=0; i < rqstp->rq_enc_pages_num; i++) {
181962306a36Sopenharmony_ci		rqstp->rq_enc_pages[i] = alloc_page(GFP_KERNEL);
182062306a36Sopenharmony_ci		if (rqstp->rq_enc_pages[i] == NULL)
182162306a36Sopenharmony_ci			goto out_free;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci	rqstp->rq_release_snd_buf = priv_release_snd_buf;
182462306a36Sopenharmony_ci	return 0;
182562306a36Sopenharmony_ciout_free:
182662306a36Sopenharmony_ci	rqstp->rq_enc_pages_num = i;
182762306a36Sopenharmony_ci	priv_release_snd_buf(rqstp);
182862306a36Sopenharmony_ciout:
182962306a36Sopenharmony_ci	return -EAGAIN;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic noinline_for_stack int
183362306a36Sopenharmony_cigss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
183462306a36Sopenharmony_ci		  struct rpc_task *task, struct xdr_stream *xdr)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
183762306a36Sopenharmony_ci	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;
183862306a36Sopenharmony_ci	u32		pad, offset, maj_stat;
183962306a36Sopenharmony_ci	int		status;
184062306a36Sopenharmony_ci	__be32		*p, *opaque_len;
184162306a36Sopenharmony_ci	struct page	**inpages;
184262306a36Sopenharmony_ci	int		first;
184362306a36Sopenharmony_ci	struct kvec	*iov;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	status = -EIO;
184662306a36Sopenharmony_ci	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
184762306a36Sopenharmony_ci	if (!p)
184862306a36Sopenharmony_ci		goto wrap_failed;
184962306a36Sopenharmony_ci	opaque_len = p++;
185062306a36Sopenharmony_ci	*p = cpu_to_be32(rqstp->rq_seqno);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (rpcauth_wrap_req_encode(task, xdr))
185362306a36Sopenharmony_ci		goto wrap_failed;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	status = alloc_enc_pages(rqstp);
185662306a36Sopenharmony_ci	if (unlikely(status))
185762306a36Sopenharmony_ci		goto wrap_failed;
185862306a36Sopenharmony_ci	first = snd_buf->page_base >> PAGE_SHIFT;
185962306a36Sopenharmony_ci	inpages = snd_buf->pages + first;
186062306a36Sopenharmony_ci	snd_buf->pages = rqstp->rq_enc_pages;
186162306a36Sopenharmony_ci	snd_buf->page_base -= first << PAGE_SHIFT;
186262306a36Sopenharmony_ci	/*
186362306a36Sopenharmony_ci	 * Move the tail into its own page, in case gss_wrap needs
186462306a36Sopenharmony_ci	 * more space in the head when wrapping.
186562306a36Sopenharmony_ci	 *
186662306a36Sopenharmony_ci	 * Still... Why can't gss_wrap just slide the tail down?
186762306a36Sopenharmony_ci	 */
186862306a36Sopenharmony_ci	if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
186962306a36Sopenharmony_ci		char *tmp;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci		tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
187262306a36Sopenharmony_ci		memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
187362306a36Sopenharmony_ci		snd_buf->tail[0].iov_base = tmp;
187462306a36Sopenharmony_ci	}
187562306a36Sopenharmony_ci	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
187662306a36Sopenharmony_ci	maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages);
187762306a36Sopenharmony_ci	/* slack space should prevent this ever happening: */
187862306a36Sopenharmony_ci	if (unlikely(snd_buf->len > snd_buf->buflen))
187962306a36Sopenharmony_ci		goto wrap_failed;
188062306a36Sopenharmony_ci	/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
188162306a36Sopenharmony_ci	 * done anyway, so it's safe to put the request on the wire: */
188262306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
188362306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
188462306a36Sopenharmony_ci	else if (maj_stat)
188562306a36Sopenharmony_ci		goto bad_wrap;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	*opaque_len = cpu_to_be32(snd_buf->len - offset);
188862306a36Sopenharmony_ci	/* guess whether the pad goes into the head or the tail: */
188962306a36Sopenharmony_ci	if (snd_buf->page_len || snd_buf->tail[0].iov_len)
189062306a36Sopenharmony_ci		iov = snd_buf->tail;
189162306a36Sopenharmony_ci	else
189262306a36Sopenharmony_ci		iov = snd_buf->head;
189362306a36Sopenharmony_ci	p = iov->iov_base + iov->iov_len;
189462306a36Sopenharmony_ci	pad = xdr_pad_size(snd_buf->len - offset);
189562306a36Sopenharmony_ci	memset(p, 0, pad);
189662306a36Sopenharmony_ci	iov->iov_len += pad;
189762306a36Sopenharmony_ci	snd_buf->len += pad;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	return 0;
190062306a36Sopenharmony_ciwrap_failed:
190162306a36Sopenharmony_ci	return status;
190262306a36Sopenharmony_cibad_wrap:
190362306a36Sopenharmony_ci	trace_rpcgss_wrap(task, maj_stat);
190462306a36Sopenharmony_ci	return -EIO;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
191062306a36Sopenharmony_ci	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
191162306a36Sopenharmony_ci			gc_base);
191262306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
191362306a36Sopenharmony_ci	int status;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	status = -EIO;
191662306a36Sopenharmony_ci	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
191762306a36Sopenharmony_ci		/* The spec seems a little ambiguous here, but I think that not
191862306a36Sopenharmony_ci		 * wrapping context destruction requests makes the most sense.
191962306a36Sopenharmony_ci		 */
192062306a36Sopenharmony_ci		status = rpcauth_wrap_req_encode(task, xdr);
192162306a36Sopenharmony_ci		goto out;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci	switch (gss_cred->gc_service) {
192462306a36Sopenharmony_ci	case RPC_GSS_SVC_NONE:
192562306a36Sopenharmony_ci		status = rpcauth_wrap_req_encode(task, xdr);
192662306a36Sopenharmony_ci		break;
192762306a36Sopenharmony_ci	case RPC_GSS_SVC_INTEGRITY:
192862306a36Sopenharmony_ci		status = gss_wrap_req_integ(cred, ctx, task, xdr);
192962306a36Sopenharmony_ci		break;
193062306a36Sopenharmony_ci	case RPC_GSS_SVC_PRIVACY:
193162306a36Sopenharmony_ci		status = gss_wrap_req_priv(cred, ctx, task, xdr);
193262306a36Sopenharmony_ci		break;
193362306a36Sopenharmony_ci	default:
193462306a36Sopenharmony_ci		status = -EIO;
193562306a36Sopenharmony_ci	}
193662306a36Sopenharmony_ciout:
193762306a36Sopenharmony_ci	gss_put_ctx(ctx);
193862306a36Sopenharmony_ci	return status;
193962306a36Sopenharmony_ci}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci/**
194262306a36Sopenharmony_ci * gss_update_rslack - Possibly update RPC receive buffer size estimates
194362306a36Sopenharmony_ci * @task: rpc_task for incoming RPC Reply being unwrapped
194462306a36Sopenharmony_ci * @cred: controlling rpc_cred for @task
194562306a36Sopenharmony_ci * @before: XDR words needed before each RPC Reply message
194662306a36Sopenharmony_ci * @after: XDR words needed following each RPC Reply message
194762306a36Sopenharmony_ci *
194862306a36Sopenharmony_ci */
194962306a36Sopenharmony_cistatic void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred,
195062306a36Sopenharmony_ci			      unsigned int before, unsigned int after)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	struct rpc_auth *auth = cred->cr_auth;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) {
195562306a36Sopenharmony_ci		auth->au_ralign = auth->au_verfsize + before;
195662306a36Sopenharmony_ci		auth->au_rslack = auth->au_verfsize + after;
195762306a36Sopenharmony_ci		trace_rpcgss_update_slack(task, auth);
195862306a36Sopenharmony_ci	}
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic int
196262306a36Sopenharmony_cigss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred)
196362306a36Sopenharmony_ci{
196462306a36Sopenharmony_ci	gss_update_rslack(task, cred, 0, 0);
196562306a36Sopenharmony_ci	return 0;
196662306a36Sopenharmony_ci}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci/*
196962306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.2
197062306a36Sopenharmony_ci *
197162306a36Sopenharmony_ci *	struct rpc_gss_integ_data {
197262306a36Sopenharmony_ci *		opaque databody_integ<>;
197362306a36Sopenharmony_ci *		opaque checksum<>;
197462306a36Sopenharmony_ci *	};
197562306a36Sopenharmony_ci *
197662306a36Sopenharmony_ci *	struct rpc_gss_data_t {
197762306a36Sopenharmony_ci *		unsigned int seq_num;
197862306a36Sopenharmony_ci *		proc_req_arg_t arg;
197962306a36Sopenharmony_ci *	};
198062306a36Sopenharmony_ci */
198162306a36Sopenharmony_cistatic noinline_for_stack int
198262306a36Sopenharmony_cigss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
198362306a36Sopenharmony_ci		      struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
198462306a36Sopenharmony_ci		      struct xdr_stream *xdr)
198562306a36Sopenharmony_ci{
198662306a36Sopenharmony_ci	struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf;
198762306a36Sopenharmony_ci	u32 len, offset, seqno, maj_stat;
198862306a36Sopenharmony_ci	struct xdr_netobj mic;
198962306a36Sopenharmony_ci	int ret;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	ret = -EIO;
199262306a36Sopenharmony_ci	mic.data = NULL;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	/* opaque databody_integ<>; */
199562306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &len))
199662306a36Sopenharmony_ci		goto unwrap_failed;
199762306a36Sopenharmony_ci	if (len & 3)
199862306a36Sopenharmony_ci		goto unwrap_failed;
199962306a36Sopenharmony_ci	offset = rcv_buf->len - xdr_stream_remaining(xdr);
200062306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &seqno))
200162306a36Sopenharmony_ci		goto unwrap_failed;
200262306a36Sopenharmony_ci	if (seqno != rqstp->rq_seqno)
200362306a36Sopenharmony_ci		goto bad_seqno;
200462306a36Sopenharmony_ci	if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len))
200562306a36Sopenharmony_ci		goto unwrap_failed;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	/*
200862306a36Sopenharmony_ci	 * The xdr_stream now points to the beginning of the
200962306a36Sopenharmony_ci	 * upper layer payload, to be passed below to
201062306a36Sopenharmony_ci	 * rpcauth_unwrap_resp_decode(). The checksum, which
201162306a36Sopenharmony_ci	 * follows the upper layer payload in @rcv_buf, is
201262306a36Sopenharmony_ci	 * located and parsed without updating the xdr_stream.
201362306a36Sopenharmony_ci	 */
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	/* opaque checksum<>; */
201662306a36Sopenharmony_ci	offset += len;
201762306a36Sopenharmony_ci	if (xdr_decode_word(rcv_buf, offset, &len))
201862306a36Sopenharmony_ci		goto unwrap_failed;
201962306a36Sopenharmony_ci	offset += sizeof(__be32);
202062306a36Sopenharmony_ci	if (offset + len > rcv_buf->len)
202162306a36Sopenharmony_ci		goto unwrap_failed;
202262306a36Sopenharmony_ci	mic.len = len;
202362306a36Sopenharmony_ci	mic.data = kmalloc(len, GFP_KERNEL);
202462306a36Sopenharmony_ci	if (ZERO_OR_NULL_PTR(mic.data))
202562306a36Sopenharmony_ci		goto unwrap_failed;
202662306a36Sopenharmony_ci	if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len))
202762306a36Sopenharmony_ci		goto unwrap_failed;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic);
203062306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
203162306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
203262306a36Sopenharmony_ci	if (maj_stat != GSS_S_COMPLETE)
203362306a36Sopenharmony_ci		goto bad_mic;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len));
203662306a36Sopenharmony_ci	ret = 0;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ciout:
203962306a36Sopenharmony_ci	kfree(mic.data);
204062306a36Sopenharmony_ci	return ret;
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ciunwrap_failed:
204362306a36Sopenharmony_ci	trace_rpcgss_unwrap_failed(task);
204462306a36Sopenharmony_ci	goto out;
204562306a36Sopenharmony_cibad_seqno:
204662306a36Sopenharmony_ci	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno);
204762306a36Sopenharmony_ci	goto out;
204862306a36Sopenharmony_cibad_mic:
204962306a36Sopenharmony_ci	trace_rpcgss_verify_mic(task, maj_stat);
205062306a36Sopenharmony_ci	goto out;
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_cistatic noinline_for_stack int
205462306a36Sopenharmony_cigss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
205562306a36Sopenharmony_ci		     struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
205662306a36Sopenharmony_ci		     struct xdr_stream *xdr)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
205962306a36Sopenharmony_ci	struct kvec *head = rqstp->rq_rcv_buf.head;
206062306a36Sopenharmony_ci	u32 offset, opaque_len, maj_stat;
206162306a36Sopenharmony_ci	__be32 *p;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
206462306a36Sopenharmony_ci	if (unlikely(!p))
206562306a36Sopenharmony_ci		goto unwrap_failed;
206662306a36Sopenharmony_ci	opaque_len = be32_to_cpup(p++);
206762306a36Sopenharmony_ci	offset = (u8 *)(p) - (u8 *)head->iov_base;
206862306a36Sopenharmony_ci	if (offset + opaque_len > rcv_buf->len)
206962306a36Sopenharmony_ci		goto unwrap_failed;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
207262306a36Sopenharmony_ci			      offset + opaque_len, rcv_buf);
207362306a36Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
207462306a36Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
207562306a36Sopenharmony_ci	if (maj_stat != GSS_S_COMPLETE)
207662306a36Sopenharmony_ci		goto bad_unwrap;
207762306a36Sopenharmony_ci	/* gss_unwrap decrypted the sequence number */
207862306a36Sopenharmony_ci	if (be32_to_cpup(p++) != rqstp->rq_seqno)
207962306a36Sopenharmony_ci		goto bad_seqno;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	/* gss_unwrap redacts the opaque blob from the head iovec.
208262306a36Sopenharmony_ci	 * rcv_buf has changed, thus the stream needs to be reset.
208362306a36Sopenharmony_ci	 */
208462306a36Sopenharmony_ci	xdr_init_decode(xdr, rcv_buf, p, rqstp);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align,
208762306a36Sopenharmony_ci			  2 + ctx->gc_gss_ctx->slack);
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	return 0;
209062306a36Sopenharmony_ciunwrap_failed:
209162306a36Sopenharmony_ci	trace_rpcgss_unwrap_failed(task);
209262306a36Sopenharmony_ci	return -EIO;
209362306a36Sopenharmony_cibad_seqno:
209462306a36Sopenharmony_ci	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p));
209562306a36Sopenharmony_ci	return -EIO;
209662306a36Sopenharmony_cibad_unwrap:
209762306a36Sopenharmony_ci	trace_rpcgss_unwrap(task, maj_stat);
209862306a36Sopenharmony_ci	return -EIO;
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cistatic bool
210262306a36Sopenharmony_cigss_seq_is_newer(u32 new, u32 old)
210362306a36Sopenharmony_ci{
210462306a36Sopenharmony_ci	return (s32)(new - old) > 0;
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic bool
210862306a36Sopenharmony_cigss_xmit_need_reencode(struct rpc_task *task)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
211162306a36Sopenharmony_ci	struct rpc_cred *cred = req->rq_cred;
211262306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
211362306a36Sopenharmony_ci	u32 win, seq_xmit = 0;
211462306a36Sopenharmony_ci	bool ret = true;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (!ctx)
211762306a36Sopenharmony_ci		goto out;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq)))
212062306a36Sopenharmony_ci		goto out_ctx;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	seq_xmit = READ_ONCE(ctx->gc_seq_xmit);
212362306a36Sopenharmony_ci	while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) {
212462306a36Sopenharmony_ci		u32 tmp = seq_xmit;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci		seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno);
212762306a36Sopenharmony_ci		if (seq_xmit == tmp) {
212862306a36Sopenharmony_ci			ret = false;
212962306a36Sopenharmony_ci			goto out_ctx;
213062306a36Sopenharmony_ci		}
213162306a36Sopenharmony_ci	}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	win = ctx->gc_win;
213462306a36Sopenharmony_ci	if (win > 0)
213562306a36Sopenharmony_ci		ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ciout_ctx:
213862306a36Sopenharmony_ci	gss_put_ctx(ctx);
213962306a36Sopenharmony_ciout:
214062306a36Sopenharmony_ci	trace_rpcgss_need_reencode(task, seq_xmit, ret);
214162306a36Sopenharmony_ci	return ret;
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic int
214562306a36Sopenharmony_cigss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
214862306a36Sopenharmony_ci	struct rpc_cred *cred = rqstp->rq_cred;
214962306a36Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
215062306a36Sopenharmony_ci			gc_base);
215162306a36Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
215262306a36Sopenharmony_ci	int status = -EIO;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
215562306a36Sopenharmony_ci		goto out_decode;
215662306a36Sopenharmony_ci	switch (gss_cred->gc_service) {
215762306a36Sopenharmony_ci	case RPC_GSS_SVC_NONE:
215862306a36Sopenharmony_ci		status = gss_unwrap_resp_auth(task, cred);
215962306a36Sopenharmony_ci		break;
216062306a36Sopenharmony_ci	case RPC_GSS_SVC_INTEGRITY:
216162306a36Sopenharmony_ci		status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr);
216262306a36Sopenharmony_ci		break;
216362306a36Sopenharmony_ci	case RPC_GSS_SVC_PRIVACY:
216462306a36Sopenharmony_ci		status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr);
216562306a36Sopenharmony_ci		break;
216662306a36Sopenharmony_ci	}
216762306a36Sopenharmony_ci	if (status)
216862306a36Sopenharmony_ci		goto out;
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ciout_decode:
217162306a36Sopenharmony_ci	status = rpcauth_unwrap_resp_decode(task, xdr);
217262306a36Sopenharmony_ciout:
217362306a36Sopenharmony_ci	gss_put_ctx(ctx);
217462306a36Sopenharmony_ci	return status;
217562306a36Sopenharmony_ci}
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_cistatic const struct rpc_authops authgss_ops = {
217862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
217962306a36Sopenharmony_ci	.au_flavor	= RPC_AUTH_GSS,
218062306a36Sopenharmony_ci	.au_name	= "RPCSEC_GSS",
218162306a36Sopenharmony_ci	.create		= gss_create,
218262306a36Sopenharmony_ci	.destroy	= gss_destroy,
218362306a36Sopenharmony_ci	.hash_cred	= gss_hash_cred,
218462306a36Sopenharmony_ci	.lookup_cred	= gss_lookup_cred,
218562306a36Sopenharmony_ci	.crcreate	= gss_create_cred,
218662306a36Sopenharmony_ci	.info2flavor	= gss_mech_info2flavor,
218762306a36Sopenharmony_ci	.flavor2info	= gss_mech_flavor2info,
218862306a36Sopenharmony_ci};
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_cistatic const struct rpc_credops gss_credops = {
219162306a36Sopenharmony_ci	.cr_name		= "AUTH_GSS",
219262306a36Sopenharmony_ci	.crdestroy		= gss_destroy_cred,
219362306a36Sopenharmony_ci	.cr_init		= gss_cred_init,
219462306a36Sopenharmony_ci	.crmatch		= gss_match,
219562306a36Sopenharmony_ci	.crmarshal		= gss_marshal,
219662306a36Sopenharmony_ci	.crrefresh		= gss_refresh,
219762306a36Sopenharmony_ci	.crvalidate		= gss_validate,
219862306a36Sopenharmony_ci	.crwrap_req		= gss_wrap_req,
219962306a36Sopenharmony_ci	.crunwrap_resp		= gss_unwrap_resp,
220062306a36Sopenharmony_ci	.crkey_timeout		= gss_key_timeout,
220162306a36Sopenharmony_ci	.crstringify_acceptor	= gss_stringify_acceptor,
220262306a36Sopenharmony_ci	.crneed_reencode	= gss_xmit_need_reencode,
220362306a36Sopenharmony_ci};
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_cistatic const struct rpc_credops gss_nullops = {
220662306a36Sopenharmony_ci	.cr_name		= "AUTH_GSS",
220762306a36Sopenharmony_ci	.crdestroy		= gss_destroy_nullcred,
220862306a36Sopenharmony_ci	.crmatch		= gss_match,
220962306a36Sopenharmony_ci	.crmarshal		= gss_marshal,
221062306a36Sopenharmony_ci	.crrefresh		= gss_refresh_null,
221162306a36Sopenharmony_ci	.crvalidate		= gss_validate,
221262306a36Sopenharmony_ci	.crwrap_req		= gss_wrap_req,
221362306a36Sopenharmony_ci	.crunwrap_resp		= gss_unwrap_resp,
221462306a36Sopenharmony_ci	.crstringify_acceptor	= gss_stringify_acceptor,
221562306a36Sopenharmony_ci};
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0 = {
221862306a36Sopenharmony_ci	.upcall		= gss_v0_upcall,
221962306a36Sopenharmony_ci	.downcall	= gss_pipe_downcall,
222062306a36Sopenharmony_ci	.destroy_msg	= gss_pipe_destroy_msg,
222162306a36Sopenharmony_ci	.open_pipe	= gss_pipe_open_v0,
222262306a36Sopenharmony_ci	.release_pipe	= gss_pipe_release,
222362306a36Sopenharmony_ci};
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1 = {
222662306a36Sopenharmony_ci	.upcall		= gss_v1_upcall,
222762306a36Sopenharmony_ci	.downcall	= gss_pipe_downcall,
222862306a36Sopenharmony_ci	.destroy_msg	= gss_pipe_destroy_msg,
222962306a36Sopenharmony_ci	.open_pipe	= gss_pipe_open_v1,
223062306a36Sopenharmony_ci	.release_pipe	= gss_pipe_release,
223162306a36Sopenharmony_ci};
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_cistatic __net_init int rpcsec_gss_init_net(struct net *net)
223462306a36Sopenharmony_ci{
223562306a36Sopenharmony_ci	return gss_svc_init_net(net);
223662306a36Sopenharmony_ci}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_cistatic __net_exit void rpcsec_gss_exit_net(struct net *net)
223962306a36Sopenharmony_ci{
224062306a36Sopenharmony_ci	gss_svc_shutdown_net(net);
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_cistatic struct pernet_operations rpcsec_gss_net_ops = {
224462306a36Sopenharmony_ci	.init = rpcsec_gss_init_net,
224562306a36Sopenharmony_ci	.exit = rpcsec_gss_exit_net,
224662306a36Sopenharmony_ci};
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci/*
224962306a36Sopenharmony_ci * Initialize RPCSEC_GSS module
225062306a36Sopenharmony_ci */
225162306a36Sopenharmony_cistatic int __init init_rpcsec_gss(void)
225262306a36Sopenharmony_ci{
225362306a36Sopenharmony_ci	int err = 0;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	err = rpcauth_register(&authgss_ops);
225662306a36Sopenharmony_ci	if (err)
225762306a36Sopenharmony_ci		goto out;
225862306a36Sopenharmony_ci	err = gss_svc_init();
225962306a36Sopenharmony_ci	if (err)
226062306a36Sopenharmony_ci		goto out_unregister;
226162306a36Sopenharmony_ci	err = register_pernet_subsys(&rpcsec_gss_net_ops);
226262306a36Sopenharmony_ci	if (err)
226362306a36Sopenharmony_ci		goto out_svc_exit;
226462306a36Sopenharmony_ci	rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
226562306a36Sopenharmony_ci	return 0;
226662306a36Sopenharmony_ciout_svc_exit:
226762306a36Sopenharmony_ci	gss_svc_shutdown();
226862306a36Sopenharmony_ciout_unregister:
226962306a36Sopenharmony_ci	rpcauth_unregister(&authgss_ops);
227062306a36Sopenharmony_ciout:
227162306a36Sopenharmony_ci	return err;
227262306a36Sopenharmony_ci}
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_cistatic void __exit exit_rpcsec_gss(void)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	unregister_pernet_subsys(&rpcsec_gss_net_ops);
227762306a36Sopenharmony_ci	gss_svc_shutdown();
227862306a36Sopenharmony_ci	rpcauth_unregister(&authgss_ops);
227962306a36Sopenharmony_ci	rcu_barrier(); /* Wait for completion of call_rcu()'s */
228062306a36Sopenharmony_ci}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ciMODULE_ALIAS("rpc-auth-6");
228362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
228462306a36Sopenharmony_cimodule_param_named(expired_cred_retry_delay,
228562306a36Sopenharmony_ci		   gss_expired_cred_retry_delay,
228662306a36Sopenharmony_ci		   uint, 0644);
228762306a36Sopenharmony_ciMODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
228862306a36Sopenharmony_ci		"the RPC engine retries an expired credential");
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_cimodule_param_named(key_expire_timeo,
229162306a36Sopenharmony_ci		   gss_key_expire_timeo,
229262306a36Sopenharmony_ci		   uint, 0644);
229362306a36Sopenharmony_ciMODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
229462306a36Sopenharmony_ci		"credential keys lifetime where the NFS layer cleans up "
229562306a36Sopenharmony_ci		"prior to key expiration");
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_cimodule_init(init_rpcsec_gss)
229862306a36Sopenharmony_cimodule_exit(exit_rpcsec_gss)
2299