18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/net/sunrpc/auth_gss/auth_gss.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * RPCSEC_GSS client authentication.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (c) 2000 The Regents of the University of Michigan.
88c2ecf20Sopenharmony_ci *  All rights reserved.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Dug Song       <dugsong@monkey.org>
118c2ecf20Sopenharmony_ci *  Andy Adamson   <andros@umich.edu>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
208c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h>
218c2ecf20Sopenharmony_ci#include <linux/sunrpc/auth.h>
228c2ecf20Sopenharmony_ci#include <linux/sunrpc/auth_gss.h>
238c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h>
248c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h>
258c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_err.h>
268c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
278c2ecf20Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h>
288c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_api.h>
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci#include <linux/hashtable.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "auth_gss_internal.h"
338c2ecf20Sopenharmony_ci#include "../netns.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <trace/events/rpcgss.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const struct rpc_authops authgss_ops;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_credops;
408c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_nullops;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define GSS_RETRY_EXPIRED 5
438c2ecf20Sopenharmony_cistatic unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define GSS_KEY_EXPIRE_TIMEO 240
468c2ecf20Sopenharmony_cistatic unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
498c2ecf20Sopenharmony_ci# define RPCDBG_FACILITY	RPCDBG_AUTH
508c2ecf20Sopenharmony_ci#endif
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define GSS_CRED_SLACK		(RPC_MAX_AUTH_SIZE * 2)
538c2ecf20Sopenharmony_ci/* length of a krb5 verifier (48), plus data added before arguments when
548c2ecf20Sopenharmony_ci * using integrity (two 4-byte integers): */
558c2ecf20Sopenharmony_ci#define GSS_VERF_SLACK		100
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic DEFINE_HASHTABLE(gss_auth_hash_table, 4);
588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(gss_auth_hash_lock);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct gss_pipe {
618c2ecf20Sopenharmony_ci	struct rpc_pipe_dir_object pdo;
628c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe;
638c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt;
648c2ecf20Sopenharmony_ci	const char *name;
658c2ecf20Sopenharmony_ci	struct kref kref;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct gss_auth {
698c2ecf20Sopenharmony_ci	struct kref kref;
708c2ecf20Sopenharmony_ci	struct hlist_node hash;
718c2ecf20Sopenharmony_ci	struct rpc_auth rpc_auth;
728c2ecf20Sopenharmony_ci	struct gss_api_mech *mech;
738c2ecf20Sopenharmony_ci	enum rpc_gss_svc service;
748c2ecf20Sopenharmony_ci	struct rpc_clnt *client;
758c2ecf20Sopenharmony_ci	struct net *net;
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * There are two upcall pipes; dentry[1], named "gssd", is used
788c2ecf20Sopenharmony_ci	 * for the new text-based upcall; dentry[0] is named after the
798c2ecf20Sopenharmony_ci	 * mechanism (for example, "krb5") and exists for
808c2ecf20Sopenharmony_ci	 * backwards-compatibility with older gssd's.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	struct gss_pipe *gss_pipe[2];
838c2ecf20Sopenharmony_ci	const char *target_name;
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* pipe_version >= 0 if and only if someone has a pipe open. */
878c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pipe_version_lock);
888c2ecf20Sopenharmony_cistatic struct rpc_wait_queue pipe_version_rpc_waitqueue;
898c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
908c2ecf20Sopenharmony_cistatic void gss_put_auth(struct gss_auth *gss_auth);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void gss_free_ctx(struct gss_cl_ctx *);
938c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0;
948c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic inline struct gss_cl_ctx *
978c2ecf20Sopenharmony_cigss_get_ctx(struct gss_cl_ctx *ctx)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	refcount_inc(&ctx->count);
1008c2ecf20Sopenharmony_ci	return ctx;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic inline void
1048c2ecf20Sopenharmony_cigss_put_ctx(struct gss_cl_ctx *ctx)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&ctx->count))
1078c2ecf20Sopenharmony_ci		gss_free_ctx(ctx);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* gss_cred_set_ctx:
1118c2ecf20Sopenharmony_ci * called by gss_upcall_callback and gss_create_upcall in order
1128c2ecf20Sopenharmony_ci * to set the gss context. The actual exchange of an old context
1138c2ecf20Sopenharmony_ci * and a new one is protected by the pipe->lock.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void
1168c2ecf20Sopenharmony_cigss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
1218c2ecf20Sopenharmony_ci		return;
1228c2ecf20Sopenharmony_ci	gss_get_ctx(ctx);
1238c2ecf20Sopenharmony_ci	rcu_assign_pointer(gss_cred->gc_ctx, ctx);
1248c2ecf20Sopenharmony_ci	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
1258c2ecf20Sopenharmony_ci	smp_mb__before_atomic();
1268c2ecf20Sopenharmony_ci	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic struct gss_cl_ctx *
1308c2ecf20Sopenharmony_cigss_cred_get_ctx(struct rpc_cred *cred)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
1338c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = NULL;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	rcu_read_lock();
1368c2ecf20Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
1378c2ecf20Sopenharmony_ci	if (ctx)
1388c2ecf20Sopenharmony_ci		gss_get_ctx(ctx);
1398c2ecf20Sopenharmony_ci	rcu_read_unlock();
1408c2ecf20Sopenharmony_ci	return ctx;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct gss_cl_ctx *
1448c2ecf20Sopenharmony_cigss_alloc_context(void)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_NOFS);
1498c2ecf20Sopenharmony_ci	if (ctx != NULL) {
1508c2ecf20Sopenharmony_ci		ctx->gc_proc = RPC_GSS_PROC_DATA;
1518c2ecf20Sopenharmony_ci		ctx->gc_seq = 1;	/* NetApp 6.4R1 doesn't accept seq. no. 0 */
1528c2ecf20Sopenharmony_ci		spin_lock_init(&ctx->gc_seq_lock);
1538c2ecf20Sopenharmony_ci		refcount_set(&ctx->count,1);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	return ctx;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci#define GSSD_MIN_TIMEOUT (60 * 60)
1598c2ecf20Sopenharmony_cistatic const void *
1608c2ecf20Sopenharmony_cigss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	const void *q;
1638c2ecf20Sopenharmony_ci	unsigned int seclen;
1648c2ecf20Sopenharmony_ci	unsigned int timeout;
1658c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
1668c2ecf20Sopenharmony_ci	u32 window_size;
1678c2ecf20Sopenharmony_ci	int ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* First unsigned int gives the remaining lifetime in seconds of the
1708c2ecf20Sopenharmony_ci	 * credential - e.g. the remaining TGT lifetime for Kerberos or
1718c2ecf20Sopenharmony_ci	 * the -t value passed to GSSD.
1728c2ecf20Sopenharmony_ci	 */
1738c2ecf20Sopenharmony_ci	p = simple_get_bytes(p, end, &timeout, sizeof(timeout));
1748c2ecf20Sopenharmony_ci	if (IS_ERR(p))
1758c2ecf20Sopenharmony_ci		goto err;
1768c2ecf20Sopenharmony_ci	if (timeout == 0)
1778c2ecf20Sopenharmony_ci		timeout = GSSD_MIN_TIMEOUT;
1788c2ecf20Sopenharmony_ci	ctx->gc_expiry = now + ((unsigned long)timeout * HZ);
1798c2ecf20Sopenharmony_ci	/* Sequence number window. Determines the maximum number of
1808c2ecf20Sopenharmony_ci	 * simultaneous requests
1818c2ecf20Sopenharmony_ci	 */
1828c2ecf20Sopenharmony_ci	p = simple_get_bytes(p, end, &window_size, sizeof(window_size));
1838c2ecf20Sopenharmony_ci	if (IS_ERR(p))
1848c2ecf20Sopenharmony_ci		goto err;
1858c2ecf20Sopenharmony_ci	ctx->gc_win = window_size;
1868c2ecf20Sopenharmony_ci	/* gssd signals an error by passing ctx->gc_win = 0: */
1878c2ecf20Sopenharmony_ci	if (ctx->gc_win == 0) {
1888c2ecf20Sopenharmony_ci		/*
1898c2ecf20Sopenharmony_ci		 * in which case, p points to an error code. Anything other
1908c2ecf20Sopenharmony_ci		 * than -EKEYEXPIRED gets converted to -EACCES.
1918c2ecf20Sopenharmony_ci		 */
1928c2ecf20Sopenharmony_ci		p = simple_get_bytes(p, end, &ret, sizeof(ret));
1938c2ecf20Sopenharmony_ci		if (!IS_ERR(p))
1948c2ecf20Sopenharmony_ci			p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) :
1958c2ecf20Sopenharmony_ci						    ERR_PTR(-EACCES);
1968c2ecf20Sopenharmony_ci		goto err;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	/* copy the opaque wire context */
1998c2ecf20Sopenharmony_ci	p = simple_get_netobj(p, end, &ctx->gc_wire_ctx);
2008c2ecf20Sopenharmony_ci	if (IS_ERR(p))
2018c2ecf20Sopenharmony_ci		goto err;
2028c2ecf20Sopenharmony_ci	/* import the opaque security context */
2038c2ecf20Sopenharmony_ci	p  = simple_get_bytes(p, end, &seclen, sizeof(seclen));
2048c2ecf20Sopenharmony_ci	if (IS_ERR(p))
2058c2ecf20Sopenharmony_ci		goto err;
2068c2ecf20Sopenharmony_ci	q = (const void *)((const char *)p + seclen);
2078c2ecf20Sopenharmony_ci	if (unlikely(q > end || q < p)) {
2088c2ecf20Sopenharmony_ci		p = ERR_PTR(-EFAULT);
2098c2ecf20Sopenharmony_ci		goto err;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS);
2128c2ecf20Sopenharmony_ci	if (ret < 0) {
2138c2ecf20Sopenharmony_ci		trace_rpcgss_import_ctx(ret);
2148c2ecf20Sopenharmony_ci		p = ERR_PTR(ret);
2158c2ecf20Sopenharmony_ci		goto err;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* is there any trailing data? */
2198c2ecf20Sopenharmony_ci	if (q == end) {
2208c2ecf20Sopenharmony_ci		p = q;
2218c2ecf20Sopenharmony_ci		goto done;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* pull in acceptor name (if there is one) */
2258c2ecf20Sopenharmony_ci	p = simple_get_netobj(q, end, &ctx->gc_acceptor);
2268c2ecf20Sopenharmony_ci	if (IS_ERR(p))
2278c2ecf20Sopenharmony_ci		goto err;
2288c2ecf20Sopenharmony_cidone:
2298c2ecf20Sopenharmony_ci	trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout,
2308c2ecf20Sopenharmony_ci			     ctx->gc_acceptor.len, ctx->gc_acceptor.data);
2318c2ecf20Sopenharmony_cierr:
2328c2ecf20Sopenharmony_ci	return p;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/* XXX: Need some documentation about why UPCALL_BUF_LEN is so small.
2368c2ecf20Sopenharmony_ci *	Is user space expecting no more than UPCALL_BUF_LEN bytes?
2378c2ecf20Sopenharmony_ci *	Note that there are now _two_ NI_MAXHOST sized data items
2388c2ecf20Sopenharmony_ci *	being passed in this string.
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_ci#define UPCALL_BUF_LEN	256
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistruct gss_upcall_msg {
2438c2ecf20Sopenharmony_ci	refcount_t count;
2448c2ecf20Sopenharmony_ci	kuid_t	uid;
2458c2ecf20Sopenharmony_ci	const char *service_name;
2468c2ecf20Sopenharmony_ci	struct rpc_pipe_msg msg;
2478c2ecf20Sopenharmony_ci	struct list_head list;
2488c2ecf20Sopenharmony_ci	struct gss_auth *auth;
2498c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe;
2508c2ecf20Sopenharmony_ci	struct rpc_wait_queue rpc_waitqueue;
2518c2ecf20Sopenharmony_ci	wait_queue_head_t waitqueue;
2528c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
2538c2ecf20Sopenharmony_ci	char databuf[UPCALL_BUF_LEN];
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int get_pipe_version(struct net *net)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
2598c2ecf20Sopenharmony_ci	int ret;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	spin_lock(&pipe_version_lock);
2628c2ecf20Sopenharmony_ci	if (sn->pipe_version >= 0) {
2638c2ecf20Sopenharmony_ci		atomic_inc(&sn->pipe_users);
2648c2ecf20Sopenharmony_ci		ret = sn->pipe_version;
2658c2ecf20Sopenharmony_ci	} else
2668c2ecf20Sopenharmony_ci		ret = -EAGAIN;
2678c2ecf20Sopenharmony_ci	spin_unlock(&pipe_version_lock);
2688c2ecf20Sopenharmony_ci	return ret;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void put_pipe_version(struct net *net)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) {
2768c2ecf20Sopenharmony_ci		sn->pipe_version = -1;
2778c2ecf20Sopenharmony_ci		spin_unlock(&pipe_version_lock);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic void
2828c2ecf20Sopenharmony_cigss_release_msg(struct gss_upcall_msg *gss_msg)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct net *net = gss_msg->auth->net;
2858c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&gss_msg->count))
2868c2ecf20Sopenharmony_ci		return;
2878c2ecf20Sopenharmony_ci	put_pipe_version(net);
2888c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&gss_msg->list));
2898c2ecf20Sopenharmony_ci	if (gss_msg->ctx != NULL)
2908c2ecf20Sopenharmony_ci		gss_put_ctx(gss_msg->ctx);
2918c2ecf20Sopenharmony_ci	rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
2928c2ecf20Sopenharmony_ci	gss_put_auth(gss_msg->auth);
2938c2ecf20Sopenharmony_ci	kfree_const(gss_msg->service_name);
2948c2ecf20Sopenharmony_ci	kfree(gss_msg);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic struct gss_upcall_msg *
2988c2ecf20Sopenharmony_ci__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct gss_upcall_msg *pos;
3018c2ecf20Sopenharmony_ci	list_for_each_entry(pos, &pipe->in_downcall, list) {
3028c2ecf20Sopenharmony_ci		if (!uid_eq(pos->uid, uid))
3038c2ecf20Sopenharmony_ci			continue;
3048c2ecf20Sopenharmony_ci		if (pos->auth->service != auth->service)
3058c2ecf20Sopenharmony_ci			continue;
3068c2ecf20Sopenharmony_ci		refcount_inc(&pos->count);
3078c2ecf20Sopenharmony_ci		return pos;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	return NULL;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/* Try to add an upcall to the pipefs queue.
3138c2ecf20Sopenharmony_ci * If an upcall owned by our uid already exists, then we return a reference
3148c2ecf20Sopenharmony_ci * to that upcall instead of adding the new upcall.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_cistatic inline struct gss_upcall_msg *
3178c2ecf20Sopenharmony_cigss_add_msg(struct gss_upcall_msg *gss_msg)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
3208c2ecf20Sopenharmony_ci	struct gss_upcall_msg *old;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
3238c2ecf20Sopenharmony_ci	old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth);
3248c2ecf20Sopenharmony_ci	if (old == NULL) {
3258c2ecf20Sopenharmony_ci		refcount_inc(&gss_msg->count);
3268c2ecf20Sopenharmony_ci		list_add(&gss_msg->list, &pipe->in_downcall);
3278c2ecf20Sopenharmony_ci	} else
3288c2ecf20Sopenharmony_ci		gss_msg = old;
3298c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
3308c2ecf20Sopenharmony_ci	return gss_msg;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic void
3348c2ecf20Sopenharmony_ci__gss_unhash_msg(struct gss_upcall_msg *gss_msg)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	list_del_init(&gss_msg->list);
3378c2ecf20Sopenharmony_ci	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
3388c2ecf20Sopenharmony_ci	wake_up_all(&gss_msg->waitqueue);
3398c2ecf20Sopenharmony_ci	refcount_dec(&gss_msg->count);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic void
3438c2ecf20Sopenharmony_cigss_unhash_msg(struct gss_upcall_msg *gss_msg)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (list_empty(&gss_msg->list))
3488c2ecf20Sopenharmony_ci		return;
3498c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
3508c2ecf20Sopenharmony_ci	if (!list_empty(&gss_msg->list))
3518c2ecf20Sopenharmony_ci		__gss_unhash_msg(gss_msg);
3528c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic void
3568c2ecf20Sopenharmony_cigss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	switch (gss_msg->msg.errno) {
3598c2ecf20Sopenharmony_ci	case 0:
3608c2ecf20Sopenharmony_ci		if (gss_msg->ctx == NULL)
3618c2ecf20Sopenharmony_ci			break;
3628c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
3638c2ecf20Sopenharmony_ci		gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case -EKEYEXPIRED:
3668c2ecf20Sopenharmony_ci		set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	gss_cred->gc_upcall_timestamp = jiffies;
3698c2ecf20Sopenharmony_ci	gss_cred->gc_upcall = NULL;
3708c2ecf20Sopenharmony_ci	rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void
3748c2ecf20Sopenharmony_cigss_upcall_callback(struct rpc_task *task)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred,
3778c2ecf20Sopenharmony_ci			struct gss_cred, gc_base);
3788c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
3798c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = gss_msg->pipe;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
3828c2ecf20Sopenharmony_ci	gss_handle_downcall_result(gss_cred, gss_msg);
3838c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
3848c2ecf20Sopenharmony_ci	task->tk_status = gss_msg->msg.errno;
3858c2ecf20Sopenharmony_ci	gss_release_msg(gss_msg);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg,
3898c2ecf20Sopenharmony_ci			      const struct cred *cred)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct user_namespace *userns = cred->user_ns;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	uid_t uid = from_kuid_munged(userns, gss_msg->uid);
3948c2ecf20Sopenharmony_ci	memcpy(gss_msg->databuf, &uid, sizeof(uid));
3958c2ecf20Sopenharmony_ci	gss_msg->msg.data = gss_msg->databuf;
3968c2ecf20Sopenharmony_ci	gss_msg->msg.len = sizeof(uid);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf));
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic ssize_t
4028c2ecf20Sopenharmony_cigss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg,
4038c2ecf20Sopenharmony_ci		char __user *buf, size_t buflen)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg,
4068c2ecf20Sopenharmony_ci						      struct gss_upcall_msg,
4078c2ecf20Sopenharmony_ci						      msg);
4088c2ecf20Sopenharmony_ci	if (msg->copied == 0)
4098c2ecf20Sopenharmony_ci		gss_encode_v0_msg(gss_msg, file->f_cred);
4108c2ecf20Sopenharmony_ci	return rpc_pipe_generic_upcall(file, msg, buf, buflen);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
4148c2ecf20Sopenharmony_ci				const char *service_name,
4158c2ecf20Sopenharmony_ci				const char *target_name,
4168c2ecf20Sopenharmony_ci				const struct cred *cred)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct user_namespace *userns = cred->user_ns;
4198c2ecf20Sopenharmony_ci	struct gss_api_mech *mech = gss_msg->auth->mech;
4208c2ecf20Sopenharmony_ci	char *p = gss_msg->databuf;
4218c2ecf20Sopenharmony_ci	size_t buflen = sizeof(gss_msg->databuf);
4228c2ecf20Sopenharmony_ci	int len;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name,
4258c2ecf20Sopenharmony_ci			from_kuid_munged(userns, gss_msg->uid));
4268c2ecf20Sopenharmony_ci	buflen -= len;
4278c2ecf20Sopenharmony_ci	p += len;
4288c2ecf20Sopenharmony_ci	gss_msg->msg.len = len;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/*
4318c2ecf20Sopenharmony_ci	 * target= is a full service principal that names the remote
4328c2ecf20Sopenharmony_ci	 * identity that we are authenticating to.
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	if (target_name) {
4358c2ecf20Sopenharmony_ci		len = scnprintf(p, buflen, " target=%s", target_name);
4368c2ecf20Sopenharmony_ci		buflen -= len;
4378c2ecf20Sopenharmony_ci		p += len;
4388c2ecf20Sopenharmony_ci		gss_msg->msg.len += len;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/*
4428c2ecf20Sopenharmony_ci	 * gssd uses service= and srchost= to select a matching key from
4438c2ecf20Sopenharmony_ci	 * the system's keytab to use as the source principal.
4448c2ecf20Sopenharmony_ci	 *
4458c2ecf20Sopenharmony_ci	 * service= is the service name part of the source principal,
4468c2ecf20Sopenharmony_ci	 * or "*" (meaning choose any).
4478c2ecf20Sopenharmony_ci	 *
4488c2ecf20Sopenharmony_ci	 * srchost= is the hostname part of the source principal. When
4498c2ecf20Sopenharmony_ci	 * not provided, gssd uses the local hostname.
4508c2ecf20Sopenharmony_ci	 */
4518c2ecf20Sopenharmony_ci	if (service_name) {
4528c2ecf20Sopenharmony_ci		char *c = strchr(service_name, '@');
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		if (!c)
4558c2ecf20Sopenharmony_ci			len = scnprintf(p, buflen, " service=%s",
4568c2ecf20Sopenharmony_ci					service_name);
4578c2ecf20Sopenharmony_ci		else
4588c2ecf20Sopenharmony_ci			len = scnprintf(p, buflen,
4598c2ecf20Sopenharmony_ci					" service=%.*s srchost=%s",
4608c2ecf20Sopenharmony_ci					(int)(c - service_name),
4618c2ecf20Sopenharmony_ci					service_name, c + 1);
4628c2ecf20Sopenharmony_ci		buflen -= len;
4638c2ecf20Sopenharmony_ci		p += len;
4648c2ecf20Sopenharmony_ci		gss_msg->msg.len += len;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (mech->gm_upcall_enctypes) {
4688c2ecf20Sopenharmony_ci		len = scnprintf(p, buflen, " enctypes=%s",
4698c2ecf20Sopenharmony_ci				mech->gm_upcall_enctypes);
4708c2ecf20Sopenharmony_ci		buflen -= len;
4718c2ecf20Sopenharmony_ci		p += len;
4728c2ecf20Sopenharmony_ci		gss_msg->msg.len += len;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	trace_rpcgss_upcall_msg(gss_msg->databuf);
4758c2ecf20Sopenharmony_ci	len = scnprintf(p, buflen, "\n");
4768c2ecf20Sopenharmony_ci	if (len == 0)
4778c2ecf20Sopenharmony_ci		goto out_overflow;
4788c2ecf20Sopenharmony_ci	gss_msg->msg.len += len;
4798c2ecf20Sopenharmony_ci	gss_msg->msg.data = gss_msg->databuf;
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ciout_overflow:
4828c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
4838c2ecf20Sopenharmony_ci	return -ENOMEM;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic ssize_t
4878c2ecf20Sopenharmony_cigss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg,
4888c2ecf20Sopenharmony_ci		char __user *buf, size_t buflen)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg,
4918c2ecf20Sopenharmony_ci						      struct gss_upcall_msg,
4928c2ecf20Sopenharmony_ci						      msg);
4938c2ecf20Sopenharmony_ci	int err;
4948c2ecf20Sopenharmony_ci	if (msg->copied == 0) {
4958c2ecf20Sopenharmony_ci		err = gss_encode_v1_msg(gss_msg,
4968c2ecf20Sopenharmony_ci					gss_msg->service_name,
4978c2ecf20Sopenharmony_ci					gss_msg->auth->target_name,
4988c2ecf20Sopenharmony_ci					file->f_cred);
4998c2ecf20Sopenharmony_ci		if (err)
5008c2ecf20Sopenharmony_ci			return err;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	return rpc_pipe_generic_upcall(file, msg, buf, buflen);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic struct gss_upcall_msg *
5068c2ecf20Sopenharmony_cigss_alloc_msg(struct gss_auth *gss_auth,
5078c2ecf20Sopenharmony_ci		kuid_t uid, const char *service_name)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
5108c2ecf20Sopenharmony_ci	int vers;
5118c2ecf20Sopenharmony_ci	int err = -ENOMEM;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
5148c2ecf20Sopenharmony_ci	if (gss_msg == NULL)
5158c2ecf20Sopenharmony_ci		goto err;
5168c2ecf20Sopenharmony_ci	vers = get_pipe_version(gss_auth->net);
5178c2ecf20Sopenharmony_ci	err = vers;
5188c2ecf20Sopenharmony_ci	if (err < 0)
5198c2ecf20Sopenharmony_ci		goto err_free_msg;
5208c2ecf20Sopenharmony_ci	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
5218c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gss_msg->list);
5228c2ecf20Sopenharmony_ci	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
5238c2ecf20Sopenharmony_ci	init_waitqueue_head(&gss_msg->waitqueue);
5248c2ecf20Sopenharmony_ci	refcount_set(&gss_msg->count, 1);
5258c2ecf20Sopenharmony_ci	gss_msg->uid = uid;
5268c2ecf20Sopenharmony_ci	gss_msg->auth = gss_auth;
5278c2ecf20Sopenharmony_ci	kref_get(&gss_auth->kref);
5288c2ecf20Sopenharmony_ci	if (service_name) {
5298c2ecf20Sopenharmony_ci		gss_msg->service_name = kstrdup_const(service_name, GFP_NOFS);
5308c2ecf20Sopenharmony_ci		if (!gss_msg->service_name) {
5318c2ecf20Sopenharmony_ci			err = -ENOMEM;
5328c2ecf20Sopenharmony_ci			goto err_put_pipe_version;
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci	return gss_msg;
5368c2ecf20Sopenharmony_cierr_put_pipe_version:
5378c2ecf20Sopenharmony_ci	put_pipe_version(gss_auth->net);
5388c2ecf20Sopenharmony_cierr_free_msg:
5398c2ecf20Sopenharmony_ci	kfree(gss_msg);
5408c2ecf20Sopenharmony_cierr:
5418c2ecf20Sopenharmony_ci	return ERR_PTR(err);
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic struct gss_upcall_msg *
5458c2ecf20Sopenharmony_cigss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,
5488c2ecf20Sopenharmony_ci			struct gss_cred, gc_base);
5498c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_new, *gss_msg;
5508c2ecf20Sopenharmony_ci	kuid_t uid = cred->cr_cred->fsuid;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);
5538c2ecf20Sopenharmony_ci	if (IS_ERR(gss_new))
5548c2ecf20Sopenharmony_ci		return gss_new;
5558c2ecf20Sopenharmony_ci	gss_msg = gss_add_msg(gss_new);
5568c2ecf20Sopenharmony_ci	if (gss_msg == gss_new) {
5578c2ecf20Sopenharmony_ci		int res;
5588c2ecf20Sopenharmony_ci		refcount_inc(&gss_msg->count);
5598c2ecf20Sopenharmony_ci		res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
5608c2ecf20Sopenharmony_ci		if (res) {
5618c2ecf20Sopenharmony_ci			gss_unhash_msg(gss_new);
5628c2ecf20Sopenharmony_ci			refcount_dec(&gss_msg->count);
5638c2ecf20Sopenharmony_ci			gss_release_msg(gss_new);
5648c2ecf20Sopenharmony_ci			gss_msg = ERR_PTR(res);
5658c2ecf20Sopenharmony_ci		}
5668c2ecf20Sopenharmony_ci	} else
5678c2ecf20Sopenharmony_ci		gss_release_msg(gss_new);
5688c2ecf20Sopenharmony_ci	return gss_msg;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void warn_gssd(void)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n");
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic inline int
5778c2ecf20Sopenharmony_cigss_refresh_upcall(struct rpc_task *task)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
5808c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth,
5818c2ecf20Sopenharmony_ci			struct gss_auth, rpc_auth);
5828c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,
5838c2ecf20Sopenharmony_ci			struct gss_cred, gc_base);
5848c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
5858c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe;
5868c2ecf20Sopenharmony_ci	int err = 0;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	gss_msg = gss_setup_upcall(gss_auth, cred);
5898c2ecf20Sopenharmony_ci	if (PTR_ERR(gss_msg) == -EAGAIN) {
5908c2ecf20Sopenharmony_ci		/* XXX: warning on the first, under the assumption we
5918c2ecf20Sopenharmony_ci		 * shouldn't normally hit this case on a refresh. */
5928c2ecf20Sopenharmony_ci		warn_gssd();
5938c2ecf20Sopenharmony_ci		rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue,
5948c2ecf20Sopenharmony_ci				task, NULL, jiffies + (15 * HZ));
5958c2ecf20Sopenharmony_ci		err = -EAGAIN;
5968c2ecf20Sopenharmony_ci		goto out;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci	if (IS_ERR(gss_msg)) {
5998c2ecf20Sopenharmony_ci		err = PTR_ERR(gss_msg);
6008c2ecf20Sopenharmony_ci		goto out;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	pipe = gss_msg->pipe;
6038c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
6048c2ecf20Sopenharmony_ci	if (gss_cred->gc_upcall != NULL)
6058c2ecf20Sopenharmony_ci		rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
6068c2ecf20Sopenharmony_ci	else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
6078c2ecf20Sopenharmony_ci		gss_cred->gc_upcall = gss_msg;
6088c2ecf20Sopenharmony_ci		/* gss_upcall_callback will release the reference to gss_upcall_msg */
6098c2ecf20Sopenharmony_ci		refcount_inc(&gss_msg->count);
6108c2ecf20Sopenharmony_ci		rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
6118c2ecf20Sopenharmony_ci	} else {
6128c2ecf20Sopenharmony_ci		gss_handle_downcall_result(gss_cred, gss_msg);
6138c2ecf20Sopenharmony_ci		err = gss_msg->msg.errno;
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
6168c2ecf20Sopenharmony_ci	gss_release_msg(gss_msg);
6178c2ecf20Sopenharmony_ciout:
6188c2ecf20Sopenharmony_ci	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
6198c2ecf20Sopenharmony_ci					     cred->cr_cred->fsuid), err);
6208c2ecf20Sopenharmony_ci	return err;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic inline int
6248c2ecf20Sopenharmony_cigss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct net *net = gss_auth->net;
6278c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
6288c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe;
6298c2ecf20Sopenharmony_ci	struct rpc_cred *cred = &gss_cred->gc_base;
6308c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
6318c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
6328c2ecf20Sopenharmony_ci	int err;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ciretry:
6358c2ecf20Sopenharmony_ci	err = 0;
6368c2ecf20Sopenharmony_ci	/* if gssd is down, just skip upcalling altogether */
6378c2ecf20Sopenharmony_ci	if (!gssd_running(net)) {
6388c2ecf20Sopenharmony_ci		warn_gssd();
6398c2ecf20Sopenharmony_ci		err = -EACCES;
6408c2ecf20Sopenharmony_ci		goto out;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci	gss_msg = gss_setup_upcall(gss_auth, cred);
6438c2ecf20Sopenharmony_ci	if (PTR_ERR(gss_msg) == -EAGAIN) {
6448c2ecf20Sopenharmony_ci		err = wait_event_interruptible_timeout(pipe_version_waitqueue,
6458c2ecf20Sopenharmony_ci				sn->pipe_version >= 0, 15 * HZ);
6468c2ecf20Sopenharmony_ci		if (sn->pipe_version < 0) {
6478c2ecf20Sopenharmony_ci			warn_gssd();
6488c2ecf20Sopenharmony_ci			err = -EACCES;
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci		if (err < 0)
6518c2ecf20Sopenharmony_ci			goto out;
6528c2ecf20Sopenharmony_ci		goto retry;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci	if (IS_ERR(gss_msg)) {
6558c2ecf20Sopenharmony_ci		err = PTR_ERR(gss_msg);
6568c2ecf20Sopenharmony_ci		goto out;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci	pipe = gss_msg->pipe;
6598c2ecf20Sopenharmony_ci	for (;;) {
6608c2ecf20Sopenharmony_ci		prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE);
6618c2ecf20Sopenharmony_ci		spin_lock(&pipe->lock);
6628c2ecf20Sopenharmony_ci		if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {
6638c2ecf20Sopenharmony_ci			break;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci		spin_unlock(&pipe->lock);
6668c2ecf20Sopenharmony_ci		if (fatal_signal_pending(current)) {
6678c2ecf20Sopenharmony_ci			err = -ERESTARTSYS;
6688c2ecf20Sopenharmony_ci			goto out_intr;
6698c2ecf20Sopenharmony_ci		}
6708c2ecf20Sopenharmony_ci		schedule();
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci	if (gss_msg->ctx) {
6738c2ecf20Sopenharmony_ci		trace_rpcgss_ctx_init(gss_cred);
6748c2ecf20Sopenharmony_ci		gss_cred_set_ctx(cred, gss_msg->ctx);
6758c2ecf20Sopenharmony_ci	} else {
6768c2ecf20Sopenharmony_ci		err = gss_msg->msg.errno;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
6798c2ecf20Sopenharmony_ciout_intr:
6808c2ecf20Sopenharmony_ci	finish_wait(&gss_msg->waitqueue, &wait);
6818c2ecf20Sopenharmony_ci	gss_release_msg(gss_msg);
6828c2ecf20Sopenharmony_ciout:
6838c2ecf20Sopenharmony_ci	trace_rpcgss_upcall_result(from_kuid(&init_user_ns,
6848c2ecf20Sopenharmony_ci					     cred->cr_cred->fsuid), err);
6858c2ecf20Sopenharmony_ci	return err;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic struct gss_upcall_msg *
6898c2ecf20Sopenharmony_cigss_find_downcall(struct rpc_pipe *pipe, kuid_t uid)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	struct gss_upcall_msg *pos;
6928c2ecf20Sopenharmony_ci	list_for_each_entry(pos, &pipe->in_downcall, list) {
6938c2ecf20Sopenharmony_ci		if (!uid_eq(pos->uid, uid))
6948c2ecf20Sopenharmony_ci			continue;
6958c2ecf20Sopenharmony_ci		if (!rpc_msg_is_inflight(&pos->msg))
6968c2ecf20Sopenharmony_ci			continue;
6978c2ecf20Sopenharmony_ci		refcount_inc(&pos->count);
6988c2ecf20Sopenharmony_ci		return pos;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	return NULL;
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci#define MSG_BUF_MAXSIZE 1024
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic ssize_t
7068c2ecf20Sopenharmony_cigss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	const void *p, *end;
7098c2ecf20Sopenharmony_ci	void *buf;
7108c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
7118c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe;
7128c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
7138c2ecf20Sopenharmony_ci	uid_t id;
7148c2ecf20Sopenharmony_ci	kuid_t uid;
7158c2ecf20Sopenharmony_ci	ssize_t err = -EFBIG;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (mlen > MSG_BUF_MAXSIZE)
7188c2ecf20Sopenharmony_ci		goto out;
7198c2ecf20Sopenharmony_ci	err = -ENOMEM;
7208c2ecf20Sopenharmony_ci	buf = kmalloc(mlen, GFP_NOFS);
7218c2ecf20Sopenharmony_ci	if (!buf)
7228c2ecf20Sopenharmony_ci		goto out;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	err = -EFAULT;
7258c2ecf20Sopenharmony_ci	if (copy_from_user(buf, src, mlen))
7268c2ecf20Sopenharmony_ci		goto err;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	end = (const void *)((char *)buf + mlen);
7298c2ecf20Sopenharmony_ci	p = simple_get_bytes(buf, end, &id, sizeof(id));
7308c2ecf20Sopenharmony_ci	if (IS_ERR(p)) {
7318c2ecf20Sopenharmony_ci		err = PTR_ERR(p);
7328c2ecf20Sopenharmony_ci		goto err;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	uid = make_kuid(current_user_ns(), id);
7368c2ecf20Sopenharmony_ci	if (!uid_valid(uid)) {
7378c2ecf20Sopenharmony_ci		err = -EINVAL;
7388c2ecf20Sopenharmony_ci		goto err;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	err = -ENOMEM;
7428c2ecf20Sopenharmony_ci	ctx = gss_alloc_context();
7438c2ecf20Sopenharmony_ci	if (ctx == NULL)
7448c2ecf20Sopenharmony_ci		goto err;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	err = -ENOENT;
7478c2ecf20Sopenharmony_ci	/* Find a matching upcall */
7488c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
7498c2ecf20Sopenharmony_ci	gss_msg = gss_find_downcall(pipe, uid);
7508c2ecf20Sopenharmony_ci	if (gss_msg == NULL) {
7518c2ecf20Sopenharmony_ci		spin_unlock(&pipe->lock);
7528c2ecf20Sopenharmony_ci		goto err_put_ctx;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	list_del_init(&gss_msg->list);
7558c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);
7588c2ecf20Sopenharmony_ci	if (IS_ERR(p)) {
7598c2ecf20Sopenharmony_ci		err = PTR_ERR(p);
7608c2ecf20Sopenharmony_ci		switch (err) {
7618c2ecf20Sopenharmony_ci		case -EACCES:
7628c2ecf20Sopenharmony_ci		case -EKEYEXPIRED:
7638c2ecf20Sopenharmony_ci			gss_msg->msg.errno = err;
7648c2ecf20Sopenharmony_ci			err = mlen;
7658c2ecf20Sopenharmony_ci			break;
7668c2ecf20Sopenharmony_ci		case -EFAULT:
7678c2ecf20Sopenharmony_ci		case -ENOMEM:
7688c2ecf20Sopenharmony_ci		case -EINVAL:
7698c2ecf20Sopenharmony_ci		case -ENOSYS:
7708c2ecf20Sopenharmony_ci			gss_msg->msg.errno = -EAGAIN;
7718c2ecf20Sopenharmony_ci			break;
7728c2ecf20Sopenharmony_ci		default:
7738c2ecf20Sopenharmony_ci			printk(KERN_CRIT "%s: bad return from "
7748c2ecf20Sopenharmony_ci				"gss_fill_context: %zd\n", __func__, err);
7758c2ecf20Sopenharmony_ci			gss_msg->msg.errno = -EIO;
7768c2ecf20Sopenharmony_ci		}
7778c2ecf20Sopenharmony_ci		goto err_release_msg;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci	gss_msg->ctx = gss_get_ctx(ctx);
7808c2ecf20Sopenharmony_ci	err = mlen;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cierr_release_msg:
7838c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
7848c2ecf20Sopenharmony_ci	__gss_unhash_msg(gss_msg);
7858c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
7868c2ecf20Sopenharmony_ci	gss_release_msg(gss_msg);
7878c2ecf20Sopenharmony_cierr_put_ctx:
7888c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
7898c2ecf20Sopenharmony_cierr:
7908c2ecf20Sopenharmony_ci	kfree(buf);
7918c2ecf20Sopenharmony_ciout:
7928c2ecf20Sopenharmony_ci	return err;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic int gss_pipe_open(struct inode *inode, int new_version)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct net *net = inode->i_sb->s_fs_info;
7988c2ecf20Sopenharmony_ci	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
7998c2ecf20Sopenharmony_ci	int ret = 0;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	spin_lock(&pipe_version_lock);
8028c2ecf20Sopenharmony_ci	if (sn->pipe_version < 0) {
8038c2ecf20Sopenharmony_ci		/* First open of any gss pipe determines the version: */
8048c2ecf20Sopenharmony_ci		sn->pipe_version = new_version;
8058c2ecf20Sopenharmony_ci		rpc_wake_up(&pipe_version_rpc_waitqueue);
8068c2ecf20Sopenharmony_ci		wake_up(&pipe_version_waitqueue);
8078c2ecf20Sopenharmony_ci	} else if (sn->pipe_version != new_version) {
8088c2ecf20Sopenharmony_ci		/* Trying to open a pipe of a different version */
8098c2ecf20Sopenharmony_ci		ret = -EBUSY;
8108c2ecf20Sopenharmony_ci		goto out;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci	atomic_inc(&sn->pipe_users);
8138c2ecf20Sopenharmony_ciout:
8148c2ecf20Sopenharmony_ci	spin_unlock(&pipe_version_lock);
8158c2ecf20Sopenharmony_ci	return ret;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int gss_pipe_open_v0(struct inode *inode)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	return gss_pipe_open(inode, 0);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int gss_pipe_open_v1(struct inode *inode)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	return gss_pipe_open(inode, 1);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic void
8308c2ecf20Sopenharmony_cigss_pipe_release(struct inode *inode)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	struct net *net = inode->i_sb->s_fs_info;
8338c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = RPC_I(inode)->pipe;
8348c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cirestart:
8378c2ecf20Sopenharmony_ci	spin_lock(&pipe->lock);
8388c2ecf20Sopenharmony_ci	list_for_each_entry(gss_msg, &pipe->in_downcall, list) {
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		if (!list_empty(&gss_msg->msg.list))
8418c2ecf20Sopenharmony_ci			continue;
8428c2ecf20Sopenharmony_ci		gss_msg->msg.errno = -EPIPE;
8438c2ecf20Sopenharmony_ci		refcount_inc(&gss_msg->count);
8448c2ecf20Sopenharmony_ci		__gss_unhash_msg(gss_msg);
8458c2ecf20Sopenharmony_ci		spin_unlock(&pipe->lock);
8468c2ecf20Sopenharmony_ci		gss_release_msg(gss_msg);
8478c2ecf20Sopenharmony_ci		goto restart;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci	spin_unlock(&pipe->lock);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	put_pipe_version(net);
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic void
8558c2ecf20Sopenharmony_cigss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (msg->errno < 0) {
8608c2ecf20Sopenharmony_ci		refcount_inc(&gss_msg->count);
8618c2ecf20Sopenharmony_ci		gss_unhash_msg(gss_msg);
8628c2ecf20Sopenharmony_ci		if (msg->errno == -ETIMEDOUT)
8638c2ecf20Sopenharmony_ci			warn_gssd();
8648c2ecf20Sopenharmony_ci		gss_release_msg(gss_msg);
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci	gss_release_msg(gss_msg);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic void gss_pipe_dentry_destroy(struct dentry *dir,
8708c2ecf20Sopenharmony_ci		struct rpc_pipe_dir_object *pdo)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct gss_pipe *gss_pipe = pdo->pdo_data;
8738c2ecf20Sopenharmony_ci	struct rpc_pipe *pipe = gss_pipe->pipe;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	if (pipe->dentry != NULL) {
8768c2ecf20Sopenharmony_ci		rpc_unlink(pipe->dentry);
8778c2ecf20Sopenharmony_ci		pipe->dentry = NULL;
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int gss_pipe_dentry_create(struct dentry *dir,
8828c2ecf20Sopenharmony_ci		struct rpc_pipe_dir_object *pdo)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct gss_pipe *p = pdo->pdo_data;
8858c2ecf20Sopenharmony_ci	struct dentry *dentry;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
8888c2ecf20Sopenharmony_ci	if (IS_ERR(dentry))
8898c2ecf20Sopenharmony_ci		return PTR_ERR(dentry);
8908c2ecf20Sopenharmony_ci	p->pipe->dentry = dentry;
8918c2ecf20Sopenharmony_ci	return 0;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
8958c2ecf20Sopenharmony_ci	.create = gss_pipe_dentry_create,
8968c2ecf20Sopenharmony_ci	.destroy = gss_pipe_dentry_destroy,
8978c2ecf20Sopenharmony_ci};
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt,
9008c2ecf20Sopenharmony_ci		const char *name,
9018c2ecf20Sopenharmony_ci		const struct rpc_pipe_ops *upcall_ops)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct gss_pipe *p;
9048c2ecf20Sopenharmony_ci	int err = -ENOMEM;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	p = kmalloc(sizeof(*p), GFP_KERNEL);
9078c2ecf20Sopenharmony_ci	if (p == NULL)
9088c2ecf20Sopenharmony_ci		goto err;
9098c2ecf20Sopenharmony_ci	p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
9108c2ecf20Sopenharmony_ci	if (IS_ERR(p->pipe)) {
9118c2ecf20Sopenharmony_ci		err = PTR_ERR(p->pipe);
9128c2ecf20Sopenharmony_ci		goto err_free_gss_pipe;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci	p->name = name;
9158c2ecf20Sopenharmony_ci	p->clnt = clnt;
9168c2ecf20Sopenharmony_ci	kref_init(&p->kref);
9178c2ecf20Sopenharmony_ci	rpc_init_pipe_dir_object(&p->pdo,
9188c2ecf20Sopenharmony_ci			&gss_pipe_dir_object_ops,
9198c2ecf20Sopenharmony_ci			p);
9208c2ecf20Sopenharmony_ci	return p;
9218c2ecf20Sopenharmony_cierr_free_gss_pipe:
9228c2ecf20Sopenharmony_ci	kfree(p);
9238c2ecf20Sopenharmony_cierr:
9248c2ecf20Sopenharmony_ci	return ERR_PTR(err);
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistruct gss_alloc_pdo {
9288c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt;
9298c2ecf20Sopenharmony_ci	const char *name;
9308c2ecf20Sopenharmony_ci	const struct rpc_pipe_ops *upcall_ops;
9318c2ecf20Sopenharmony_ci};
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct gss_pipe *gss_pipe;
9368c2ecf20Sopenharmony_ci	struct gss_alloc_pdo *args = data;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (pdo->pdo_ops != &gss_pipe_dir_object_ops)
9398c2ecf20Sopenharmony_ci		return 0;
9408c2ecf20Sopenharmony_ci	gss_pipe = container_of(pdo, struct gss_pipe, pdo);
9418c2ecf20Sopenharmony_ci	if (strcmp(gss_pipe->name, args->name) != 0)
9428c2ecf20Sopenharmony_ci		return 0;
9438c2ecf20Sopenharmony_ci	if (!kref_get_unless_zero(&gss_pipe->kref))
9448c2ecf20Sopenharmony_ci		return 0;
9458c2ecf20Sopenharmony_ci	return 1;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct gss_pipe *gss_pipe;
9518c2ecf20Sopenharmony_ci	struct gss_alloc_pdo *args = data;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops);
9548c2ecf20Sopenharmony_ci	if (!IS_ERR(gss_pipe))
9558c2ecf20Sopenharmony_ci		return &gss_pipe->pdo;
9568c2ecf20Sopenharmony_ci	return NULL;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt,
9608c2ecf20Sopenharmony_ci		const char *name,
9618c2ecf20Sopenharmony_ci		const struct rpc_pipe_ops *upcall_ops)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	struct net *net = rpc_net_ns(clnt);
9648c2ecf20Sopenharmony_ci	struct rpc_pipe_dir_object *pdo;
9658c2ecf20Sopenharmony_ci	struct gss_alloc_pdo args = {
9668c2ecf20Sopenharmony_ci		.clnt = clnt,
9678c2ecf20Sopenharmony_ci		.name = name,
9688c2ecf20Sopenharmony_ci		.upcall_ops = upcall_ops,
9698c2ecf20Sopenharmony_ci	};
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	pdo = rpc_find_or_alloc_pipe_dir_object(net,
9728c2ecf20Sopenharmony_ci			&clnt->cl_pipedir_objects,
9738c2ecf20Sopenharmony_ci			gss_pipe_match_pdo,
9748c2ecf20Sopenharmony_ci			gss_pipe_alloc_pdo,
9758c2ecf20Sopenharmony_ci			&args);
9768c2ecf20Sopenharmony_ci	if (pdo != NULL)
9778c2ecf20Sopenharmony_ci		return container_of(pdo, struct gss_pipe, pdo);
9788c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
9798c2ecf20Sopenharmony_ci}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cistatic void __gss_pipe_free(struct gss_pipe *p)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = p->clnt;
9848c2ecf20Sopenharmony_ci	struct net *net = rpc_net_ns(clnt);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	rpc_remove_pipe_dir_object(net,
9878c2ecf20Sopenharmony_ci			&clnt->cl_pipedir_objects,
9888c2ecf20Sopenharmony_ci			&p->pdo);
9898c2ecf20Sopenharmony_ci	rpc_destroy_pipe_data(p->pipe);
9908c2ecf20Sopenharmony_ci	kfree(p);
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cistatic void __gss_pipe_release(struct kref *kref)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	struct gss_pipe *p = container_of(kref, struct gss_pipe, kref);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	__gss_pipe_free(p);
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic void gss_pipe_free(struct gss_pipe *p)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	if (p != NULL)
10038c2ecf20Sopenharmony_ci		kref_put(&p->kref, __gss_pipe_release);
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci/*
10078c2ecf20Sopenharmony_ci * NOTE: we have the opportunity to use different
10088c2ecf20Sopenharmony_ci * parameters based on the input flavor (which must be a pseudoflavor)
10098c2ecf20Sopenharmony_ci */
10108c2ecf20Sopenharmony_cistatic struct gss_auth *
10118c2ecf20Sopenharmony_cigss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	rpc_authflavor_t flavor = args->pseudoflavor;
10148c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth;
10158c2ecf20Sopenharmony_ci	struct gss_pipe *gss_pipe;
10168c2ecf20Sopenharmony_ci	struct rpc_auth * auth;
10178c2ecf20Sopenharmony_ci	int err = -ENOMEM; /* XXX? */
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
10208c2ecf20Sopenharmony_ci		return ERR_PTR(err);
10218c2ecf20Sopenharmony_ci	if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
10228c2ecf20Sopenharmony_ci		goto out_dec;
10238c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&gss_auth->hash);
10248c2ecf20Sopenharmony_ci	gss_auth->target_name = NULL;
10258c2ecf20Sopenharmony_ci	if (args->target_name) {
10268c2ecf20Sopenharmony_ci		gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
10278c2ecf20Sopenharmony_ci		if (gss_auth->target_name == NULL)
10288c2ecf20Sopenharmony_ci			goto err_free;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci	gss_auth->client = clnt;
10318c2ecf20Sopenharmony_ci	gss_auth->net = get_net(rpc_net_ns(clnt));
10328c2ecf20Sopenharmony_ci	err = -EINVAL;
10338c2ecf20Sopenharmony_ci	gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
10348c2ecf20Sopenharmony_ci	if (!gss_auth->mech)
10358c2ecf20Sopenharmony_ci		goto err_put_net;
10368c2ecf20Sopenharmony_ci	gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
10378c2ecf20Sopenharmony_ci	if (gss_auth->service == 0)
10388c2ecf20Sopenharmony_ci		goto err_put_mech;
10398c2ecf20Sopenharmony_ci	if (!gssd_running(gss_auth->net))
10408c2ecf20Sopenharmony_ci		goto err_put_mech;
10418c2ecf20Sopenharmony_ci	auth = &gss_auth->rpc_auth;
10428c2ecf20Sopenharmony_ci	auth->au_cslack = GSS_CRED_SLACK >> 2;
10438c2ecf20Sopenharmony_ci	auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2;
10448c2ecf20Sopenharmony_ci	auth->au_verfsize = GSS_VERF_SLACK >> 2;
10458c2ecf20Sopenharmony_ci	auth->au_ralign = GSS_VERF_SLACK >> 2;
10468c2ecf20Sopenharmony_ci	__set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags);
10478c2ecf20Sopenharmony_ci	auth->au_ops = &authgss_ops;
10488c2ecf20Sopenharmony_ci	auth->au_flavor = flavor;
10498c2ecf20Sopenharmony_ci	if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor))
10508c2ecf20Sopenharmony_ci		__set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags);
10518c2ecf20Sopenharmony_ci	refcount_set(&auth->au_count, 1);
10528c2ecf20Sopenharmony_ci	kref_init(&gss_auth->kref);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	err = rpcauth_init_credcache(auth);
10558c2ecf20Sopenharmony_ci	if (err)
10568c2ecf20Sopenharmony_ci		goto err_put_mech;
10578c2ecf20Sopenharmony_ci	/*
10588c2ecf20Sopenharmony_ci	 * Note: if we created the old pipe first, then someone who
10598c2ecf20Sopenharmony_ci	 * examined the directory at the right moment might conclude
10608c2ecf20Sopenharmony_ci	 * that we supported only the old pipe.  So we instead create
10618c2ecf20Sopenharmony_ci	 * the new pipe first.
10628c2ecf20Sopenharmony_ci	 */
10638c2ecf20Sopenharmony_ci	gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
10648c2ecf20Sopenharmony_ci	if (IS_ERR(gss_pipe)) {
10658c2ecf20Sopenharmony_ci		err = PTR_ERR(gss_pipe);
10668c2ecf20Sopenharmony_ci		goto err_destroy_credcache;
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci	gss_auth->gss_pipe[1] = gss_pipe;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
10718c2ecf20Sopenharmony_ci			&gss_upcall_ops_v0);
10728c2ecf20Sopenharmony_ci	if (IS_ERR(gss_pipe)) {
10738c2ecf20Sopenharmony_ci		err = PTR_ERR(gss_pipe);
10748c2ecf20Sopenharmony_ci		goto err_destroy_pipe_1;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci	gss_auth->gss_pipe[0] = gss_pipe;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	return gss_auth;
10798c2ecf20Sopenharmony_cierr_destroy_pipe_1:
10808c2ecf20Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
10818c2ecf20Sopenharmony_cierr_destroy_credcache:
10828c2ecf20Sopenharmony_ci	rpcauth_destroy_credcache(auth);
10838c2ecf20Sopenharmony_cierr_put_mech:
10848c2ecf20Sopenharmony_ci	gss_mech_put(gss_auth->mech);
10858c2ecf20Sopenharmony_cierr_put_net:
10868c2ecf20Sopenharmony_ci	put_net(gss_auth->net);
10878c2ecf20Sopenharmony_cierr_free:
10888c2ecf20Sopenharmony_ci	kfree(gss_auth->target_name);
10898c2ecf20Sopenharmony_ci	kfree(gss_auth);
10908c2ecf20Sopenharmony_ciout_dec:
10918c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
10928c2ecf20Sopenharmony_ci	trace_rpcgss_createauth(flavor, err);
10938c2ecf20Sopenharmony_ci	return ERR_PTR(err);
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic void
10978c2ecf20Sopenharmony_cigss_free(struct gss_auth *gss_auth)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[0]);
11008c2ecf20Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
11018c2ecf20Sopenharmony_ci	gss_mech_put(gss_auth->mech);
11028c2ecf20Sopenharmony_ci	put_net(gss_auth->net);
11038c2ecf20Sopenharmony_ci	kfree(gss_auth->target_name);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	kfree(gss_auth);
11068c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic void
11108c2ecf20Sopenharmony_cigss_free_callback(struct kref *kref)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	gss_free(gss_auth);
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_cistatic void
11188c2ecf20Sopenharmony_cigss_put_auth(struct gss_auth *gss_auth)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	kref_put(&gss_auth->kref, gss_free_callback);
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic void
11248c2ecf20Sopenharmony_cigss_destroy(struct rpc_auth *auth)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth,
11278c2ecf20Sopenharmony_ci			struct gss_auth, rpc_auth);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if (hash_hashed(&gss_auth->hash)) {
11308c2ecf20Sopenharmony_ci		spin_lock(&gss_auth_hash_lock);
11318c2ecf20Sopenharmony_ci		hash_del(&gss_auth->hash);
11328c2ecf20Sopenharmony_ci		spin_unlock(&gss_auth_hash_lock);
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[0]);
11368c2ecf20Sopenharmony_ci	gss_auth->gss_pipe[0] = NULL;
11378c2ecf20Sopenharmony_ci	gss_pipe_free(gss_auth->gss_pipe[1]);
11388c2ecf20Sopenharmony_ci	gss_auth->gss_pipe[1] = NULL;
11398c2ecf20Sopenharmony_ci	rpcauth_destroy_credcache(auth);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	gss_put_auth(gss_auth);
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci/*
11458c2ecf20Sopenharmony_ci * Auths may be shared between rpc clients that were cloned from a
11468c2ecf20Sopenharmony_ci * common client with the same xprt, if they also share the flavor and
11478c2ecf20Sopenharmony_ci * target_name.
11488c2ecf20Sopenharmony_ci *
11498c2ecf20Sopenharmony_ci * The auth is looked up from the oldest parent sharing the same
11508c2ecf20Sopenharmony_ci * cl_xprt, and the auth itself references only that common parent
11518c2ecf20Sopenharmony_ci * (which is guaranteed to last as long as any of its descendants).
11528c2ecf20Sopenharmony_ci */
11538c2ecf20Sopenharmony_cistatic struct gss_auth *
11548c2ecf20Sopenharmony_cigss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args,
11558c2ecf20Sopenharmony_ci		struct rpc_clnt *clnt,
11568c2ecf20Sopenharmony_ci		struct gss_auth *new)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth;
11598c2ecf20Sopenharmony_ci	unsigned long hashval = (unsigned long)clnt;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	spin_lock(&gss_auth_hash_lock);
11628c2ecf20Sopenharmony_ci	hash_for_each_possible(gss_auth_hash_table,
11638c2ecf20Sopenharmony_ci			gss_auth,
11648c2ecf20Sopenharmony_ci			hash,
11658c2ecf20Sopenharmony_ci			hashval) {
11668c2ecf20Sopenharmony_ci		if (gss_auth->client != clnt)
11678c2ecf20Sopenharmony_ci			continue;
11688c2ecf20Sopenharmony_ci		if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
11698c2ecf20Sopenharmony_ci			continue;
11708c2ecf20Sopenharmony_ci		if (gss_auth->target_name != args->target_name) {
11718c2ecf20Sopenharmony_ci			if (gss_auth->target_name == NULL)
11728c2ecf20Sopenharmony_ci				continue;
11738c2ecf20Sopenharmony_ci			if (args->target_name == NULL)
11748c2ecf20Sopenharmony_ci				continue;
11758c2ecf20Sopenharmony_ci			if (strcmp(gss_auth->target_name, args->target_name))
11768c2ecf20Sopenharmony_ci				continue;
11778c2ecf20Sopenharmony_ci		}
11788c2ecf20Sopenharmony_ci		if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count))
11798c2ecf20Sopenharmony_ci			continue;
11808c2ecf20Sopenharmony_ci		goto out;
11818c2ecf20Sopenharmony_ci	}
11828c2ecf20Sopenharmony_ci	if (new)
11838c2ecf20Sopenharmony_ci		hash_add(gss_auth_hash_table, &new->hash, hashval);
11848c2ecf20Sopenharmony_ci	gss_auth = new;
11858c2ecf20Sopenharmony_ciout:
11868c2ecf20Sopenharmony_ci	spin_unlock(&gss_auth_hash_lock);
11878c2ecf20Sopenharmony_ci	return gss_auth;
11888c2ecf20Sopenharmony_ci}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_cistatic struct gss_auth *
11918c2ecf20Sopenharmony_cigss_create_hashed(const struct rpc_auth_create_args *args,
11928c2ecf20Sopenharmony_ci		  struct rpc_clnt *clnt)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth;
11958c2ecf20Sopenharmony_ci	struct gss_auth *new;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
11988c2ecf20Sopenharmony_ci	if (gss_auth != NULL)
11998c2ecf20Sopenharmony_ci		goto out;
12008c2ecf20Sopenharmony_ci	new = gss_create_new(args, clnt);
12018c2ecf20Sopenharmony_ci	if (IS_ERR(new))
12028c2ecf20Sopenharmony_ci		return new;
12038c2ecf20Sopenharmony_ci	gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
12048c2ecf20Sopenharmony_ci	if (gss_auth != new)
12058c2ecf20Sopenharmony_ci		gss_destroy(&new->rpc_auth);
12068c2ecf20Sopenharmony_ciout:
12078c2ecf20Sopenharmony_ci	return gss_auth;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_cistatic struct rpc_auth *
12118c2ecf20Sopenharmony_cigss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth;
12148c2ecf20Sopenharmony_ci	struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	while (clnt != clnt->cl_parent) {
12178c2ecf20Sopenharmony_ci		struct rpc_clnt *parent = clnt->cl_parent;
12188c2ecf20Sopenharmony_ci		/* Find the original parent for this transport */
12198c2ecf20Sopenharmony_ci		if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps)
12208c2ecf20Sopenharmony_ci			break;
12218c2ecf20Sopenharmony_ci		clnt = parent;
12228c2ecf20Sopenharmony_ci	}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	gss_auth = gss_create_hashed(args, clnt);
12258c2ecf20Sopenharmony_ci	if (IS_ERR(gss_auth))
12268c2ecf20Sopenharmony_ci		return ERR_CAST(gss_auth);
12278c2ecf20Sopenharmony_ci	return &gss_auth->rpc_auth;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic struct gss_cred *
12318c2ecf20Sopenharmony_cigss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	struct gss_cred *new;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* Make a copy of the cred so that we can reference count it */
12368c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(*gss_cred), GFP_NOFS);
12378c2ecf20Sopenharmony_ci	if (new) {
12388c2ecf20Sopenharmony_ci		struct auth_cred acred = {
12398c2ecf20Sopenharmony_ci			.cred = gss_cred->gc_base.cr_cred,
12408c2ecf20Sopenharmony_ci		};
12418c2ecf20Sopenharmony_ci		struct gss_cl_ctx *ctx =
12428c2ecf20Sopenharmony_ci			rcu_dereference_protected(gss_cred->gc_ctx, 1);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci		rpcauth_init_cred(&new->gc_base, &acred,
12458c2ecf20Sopenharmony_ci				&gss_auth->rpc_auth,
12468c2ecf20Sopenharmony_ci				&gss_nullops);
12478c2ecf20Sopenharmony_ci		new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
12488c2ecf20Sopenharmony_ci		new->gc_service = gss_cred->gc_service;
12498c2ecf20Sopenharmony_ci		new->gc_principal = gss_cred->gc_principal;
12508c2ecf20Sopenharmony_ci		kref_get(&gss_auth->kref);
12518c2ecf20Sopenharmony_ci		rcu_assign_pointer(new->gc_ctx, ctx);
12528c2ecf20Sopenharmony_ci		gss_get_ctx(ctx);
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci	return new;
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci/*
12588c2ecf20Sopenharmony_ci * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call
12598c2ecf20Sopenharmony_ci * to the server with the GSS control procedure field set to
12608c2ecf20Sopenharmony_ci * RPC_GSS_PROC_DESTROY. This should normally cause the server to release
12618c2ecf20Sopenharmony_ci * all RPCSEC_GSS state associated with that context.
12628c2ecf20Sopenharmony_ci */
12638c2ecf20Sopenharmony_cistatic void
12648c2ecf20Sopenharmony_cigss_send_destroy_context(struct rpc_cred *cred)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
12678c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
12688c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
12698c2ecf20Sopenharmony_ci	struct gss_cred *new;
12708c2ecf20Sopenharmony_ci	struct rpc_task *task;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	new = gss_dup_cred(gss_auth, gss_cred);
12738c2ecf20Sopenharmony_ci	if (new) {
12748c2ecf20Sopenharmony_ci		ctx->gc_proc = RPC_GSS_PROC_DESTROY;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci		trace_rpcgss_ctx_destroy(gss_cred);
12778c2ecf20Sopenharmony_ci		task = rpc_call_null(gss_auth->client, &new->gc_base,
12788c2ecf20Sopenharmony_ci				     RPC_TASK_ASYNC);
12798c2ecf20Sopenharmony_ci		if (!IS_ERR(task))
12808c2ecf20Sopenharmony_ci			rpc_put_task(task);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		put_rpccred(&new->gc_base);
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
12878c2ecf20Sopenharmony_ci * to create a new cred or context, so they check that things have been
12888c2ecf20Sopenharmony_ci * allocated before freeing them. */
12898c2ecf20Sopenharmony_cistatic void
12908c2ecf20Sopenharmony_cigss_do_free_ctx(struct gss_cl_ctx *ctx)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	gss_delete_sec_context(&ctx->gc_gss_ctx);
12938c2ecf20Sopenharmony_ci	kfree(ctx->gc_wire_ctx.data);
12948c2ecf20Sopenharmony_ci	kfree(ctx->gc_acceptor.data);
12958c2ecf20Sopenharmony_ci	kfree(ctx);
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic void
12998c2ecf20Sopenharmony_cigss_free_ctx_callback(struct rcu_head *head)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu);
13028c2ecf20Sopenharmony_ci	gss_do_free_ctx(ctx);
13038c2ecf20Sopenharmony_ci}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_cistatic void
13068c2ecf20Sopenharmony_cigss_free_ctx(struct gss_cl_ctx *ctx)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	call_rcu(&ctx->gc_rcu, gss_free_ctx_callback);
13098c2ecf20Sopenharmony_ci}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_cistatic void
13128c2ecf20Sopenharmony_cigss_free_cred(struct gss_cred *gss_cred)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	kfree(gss_cred);
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic void
13188c2ecf20Sopenharmony_cigss_free_cred_callback(struct rcu_head *head)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu);
13218c2ecf20Sopenharmony_ci	gss_free_cred(gss_cred);
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic void
13258c2ecf20Sopenharmony_cigss_destroy_nullcred(struct rpc_cred *cred)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
13288c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
13298c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(gss_cred->gc_ctx, NULL);
13328c2ecf20Sopenharmony_ci	put_cred(cred->cr_cred);
13338c2ecf20Sopenharmony_ci	call_rcu(&cred->cr_rcu, gss_free_cred_callback);
13348c2ecf20Sopenharmony_ci	if (ctx)
13358c2ecf20Sopenharmony_ci		gss_put_ctx(ctx);
13368c2ecf20Sopenharmony_ci	gss_put_auth(gss_auth);
13378c2ecf20Sopenharmony_ci}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_cistatic void
13408c2ecf20Sopenharmony_cigss_destroy_cred(struct rpc_cred *cred)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0)
13438c2ecf20Sopenharmony_ci		gss_send_destroy_context(cred);
13448c2ecf20Sopenharmony_ci	gss_destroy_nullcred(cred);
13458c2ecf20Sopenharmony_ci}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_cistatic int
13488c2ecf20Sopenharmony_cigss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits);
13518c2ecf20Sopenharmony_ci}
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci/*
13548c2ecf20Sopenharmony_ci * Lookup RPCSEC_GSS cred for the current process
13558c2ecf20Sopenharmony_ci */
13568c2ecf20Sopenharmony_cistatic struct rpc_cred *
13578c2ecf20Sopenharmony_cigss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	return rpcauth_lookup_credcache(auth, acred, flags, GFP_NOFS);
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic struct rpc_cred *
13638c2ecf20Sopenharmony_cigss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
13668c2ecf20Sopenharmony_ci	struct gss_cred	*cred = NULL;
13678c2ecf20Sopenharmony_ci	int err = -ENOMEM;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (!(cred = kzalloc(sizeof(*cred), gfp)))
13708c2ecf20Sopenharmony_ci		goto out_err;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
13738c2ecf20Sopenharmony_ci	/*
13748c2ecf20Sopenharmony_ci	 * Note: in order to force a call to call_refresh(), we deliberately
13758c2ecf20Sopenharmony_ci	 * fail to flag the credential as RPCAUTH_CRED_UPTODATE.
13768c2ecf20Sopenharmony_ci	 */
13778c2ecf20Sopenharmony_ci	cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;
13788c2ecf20Sopenharmony_ci	cred->gc_service = gss_auth->service;
13798c2ecf20Sopenharmony_ci	cred->gc_principal = acred->principal;
13808c2ecf20Sopenharmony_ci	kref_get(&gss_auth->kref);
13818c2ecf20Sopenharmony_ci	return &cred->gc_base;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ciout_err:
13848c2ecf20Sopenharmony_ci	return ERR_PTR(err);
13858c2ecf20Sopenharmony_ci}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cistatic int
13888c2ecf20Sopenharmony_cigss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
13918c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base);
13928c2ecf20Sopenharmony_ci	int err;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	do {
13958c2ecf20Sopenharmony_ci		err = gss_create_upcall(gss_auth, gss_cred);
13968c2ecf20Sopenharmony_ci	} while (err == -EAGAIN);
13978c2ecf20Sopenharmony_ci	return err;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cistatic char *
14018c2ecf20Sopenharmony_cigss_stringify_acceptor(struct rpc_cred *cred)
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	char *string = NULL;
14048c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
14058c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
14068c2ecf20Sopenharmony_ci	unsigned int len;
14078c2ecf20Sopenharmony_ci	struct xdr_netobj *acceptor;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	rcu_read_lock();
14108c2ecf20Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
14118c2ecf20Sopenharmony_ci	if (!ctx)
14128c2ecf20Sopenharmony_ci		goto out;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	len = ctx->gc_acceptor.len;
14158c2ecf20Sopenharmony_ci	rcu_read_unlock();
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* no point if there's no string */
14188c2ecf20Sopenharmony_ci	if (!len)
14198c2ecf20Sopenharmony_ci		return NULL;
14208c2ecf20Sopenharmony_cirealloc:
14218c2ecf20Sopenharmony_ci	string = kmalloc(len + 1, GFP_KERNEL);
14228c2ecf20Sopenharmony_ci	if (!string)
14238c2ecf20Sopenharmony_ci		return NULL;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	rcu_read_lock();
14268c2ecf20Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	/* did the ctx disappear or was it replaced by one with no acceptor? */
14298c2ecf20Sopenharmony_ci	if (!ctx || !ctx->gc_acceptor.len) {
14308c2ecf20Sopenharmony_ci		kfree(string);
14318c2ecf20Sopenharmony_ci		string = NULL;
14328c2ecf20Sopenharmony_ci		goto out;
14338c2ecf20Sopenharmony_ci	}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	acceptor = &ctx->gc_acceptor;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	/*
14388c2ecf20Sopenharmony_ci	 * Did we find a new acceptor that's longer than the original? Allocate
14398c2ecf20Sopenharmony_ci	 * a longer buffer and try again.
14408c2ecf20Sopenharmony_ci	 */
14418c2ecf20Sopenharmony_ci	if (len < acceptor->len) {
14428c2ecf20Sopenharmony_ci		len = acceptor->len;
14438c2ecf20Sopenharmony_ci		rcu_read_unlock();
14448c2ecf20Sopenharmony_ci		kfree(string);
14458c2ecf20Sopenharmony_ci		goto realloc;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	memcpy(string, acceptor->data, acceptor->len);
14498c2ecf20Sopenharmony_ci	string[acceptor->len] = '\0';
14508c2ecf20Sopenharmony_ciout:
14518c2ecf20Sopenharmony_ci	rcu_read_unlock();
14528c2ecf20Sopenharmony_ci	return string;
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci/*
14568c2ecf20Sopenharmony_ci * Returns -EACCES if GSS context is NULL or will expire within the
14578c2ecf20Sopenharmony_ci * timeout (miliseconds)
14588c2ecf20Sopenharmony_ci */
14598c2ecf20Sopenharmony_cistatic int
14608c2ecf20Sopenharmony_cigss_key_timeout(struct rpc_cred *rc)
14618c2ecf20Sopenharmony_ci{
14628c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
14638c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
14648c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ);
14658c2ecf20Sopenharmony_ci	int ret = 0;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	rcu_read_lock();
14688c2ecf20Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
14698c2ecf20Sopenharmony_ci	if (!ctx || time_after(timeout, ctx->gc_expiry))
14708c2ecf20Sopenharmony_ci		ret = -EACCES;
14718c2ecf20Sopenharmony_ci	rcu_read_unlock();
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	return ret;
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cistatic int
14778c2ecf20Sopenharmony_cigss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
14808c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx;
14818c2ecf20Sopenharmony_ci	int ret;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
14848c2ecf20Sopenharmony_ci		goto out;
14858c2ecf20Sopenharmony_ci	/* Don't match with creds that have expired. */
14868c2ecf20Sopenharmony_ci	rcu_read_lock();
14878c2ecf20Sopenharmony_ci	ctx = rcu_dereference(gss_cred->gc_ctx);
14888c2ecf20Sopenharmony_ci	if (!ctx || time_after(jiffies, ctx->gc_expiry)) {
14898c2ecf20Sopenharmony_ci		rcu_read_unlock();
14908c2ecf20Sopenharmony_ci		return 0;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci	rcu_read_unlock();
14938c2ecf20Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags))
14948c2ecf20Sopenharmony_ci		return 0;
14958c2ecf20Sopenharmony_ciout:
14968c2ecf20Sopenharmony_ci	if (acred->principal != NULL) {
14978c2ecf20Sopenharmony_ci		if (gss_cred->gc_principal == NULL)
14988c2ecf20Sopenharmony_ci			return 0;
14998c2ecf20Sopenharmony_ci		ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
15008c2ecf20Sopenharmony_ci	} else {
15018c2ecf20Sopenharmony_ci		if (gss_cred->gc_principal != NULL)
15028c2ecf20Sopenharmony_ci			return 0;
15038c2ecf20Sopenharmony_ci		ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid);
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci	return ret;
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci/*
15098c2ecf20Sopenharmony_ci * Marshal credentials.
15108c2ecf20Sopenharmony_ci *
15118c2ecf20Sopenharmony_ci * The expensive part is computing the verifier. We can't cache a
15128c2ecf20Sopenharmony_ci * pre-computed version of the verifier because the seqno, which
15138c2ecf20Sopenharmony_ci * is different every time, is included in the MIC.
15148c2ecf20Sopenharmony_ci */
15158c2ecf20Sopenharmony_cistatic int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
15168c2ecf20Sopenharmony_ci{
15178c2ecf20Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
15188c2ecf20Sopenharmony_ci	struct rpc_cred *cred = req->rq_cred;
15198c2ecf20Sopenharmony_ci	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
15208c2ecf20Sopenharmony_ci						 gc_base);
15218c2ecf20Sopenharmony_ci	struct gss_cl_ctx	*ctx = gss_cred_get_ctx(cred);
15228c2ecf20Sopenharmony_ci	__be32		*p, *cred_len;
15238c2ecf20Sopenharmony_ci	u32             maj_stat = 0;
15248c2ecf20Sopenharmony_ci	struct xdr_netobj mic;
15258c2ecf20Sopenharmony_ci	struct kvec	iov;
15268c2ecf20Sopenharmony_ci	struct xdr_buf	verf_buf;
15278c2ecf20Sopenharmony_ci	int status;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	/* Credential */
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	p = xdr_reserve_space(xdr, 7 * sizeof(*p) +
15328c2ecf20Sopenharmony_ci			      ctx->gc_wire_ctx.len);
15338c2ecf20Sopenharmony_ci	if (!p)
15348c2ecf20Sopenharmony_ci		goto marshal_failed;
15358c2ecf20Sopenharmony_ci	*p++ = rpc_auth_gss;
15368c2ecf20Sopenharmony_ci	cred_len = p++;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	spin_lock(&ctx->gc_seq_lock);
15398c2ecf20Sopenharmony_ci	req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
15408c2ecf20Sopenharmony_ci	spin_unlock(&ctx->gc_seq_lock);
15418c2ecf20Sopenharmony_ci	if (req->rq_seqno == MAXSEQ)
15428c2ecf20Sopenharmony_ci		goto expired;
15438c2ecf20Sopenharmony_ci	trace_rpcgss_seqno(task);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(RPC_GSS_VERSION);
15468c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(ctx->gc_proc);
15478c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(req->rq_seqno);
15488c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(gss_cred->gc_service);
15498c2ecf20Sopenharmony_ci	p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
15508c2ecf20Sopenharmony_ci	*cred_len = cpu_to_be32((p - (cred_len + 1)) << 2);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	/* Verifier */
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* We compute the checksum for the verifier over the xdr-encoded bytes
15558c2ecf20Sopenharmony_ci	 * starting with the xid and ending at the end of the credential: */
15568c2ecf20Sopenharmony_ci	iov.iov_base = req->rq_snd_buf.head[0].iov_base;
15578c2ecf20Sopenharmony_ci	iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;
15588c2ecf20Sopenharmony_ci	xdr_buf_from_iov(&iov, &verf_buf);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	p = xdr_reserve_space(xdr, sizeof(*p));
15618c2ecf20Sopenharmony_ci	if (!p)
15628c2ecf20Sopenharmony_ci		goto marshal_failed;
15638c2ecf20Sopenharmony_ci	*p++ = rpc_auth_gss;
15648c2ecf20Sopenharmony_ci	mic.data = (u8 *)(p + 1);
15658c2ecf20Sopenharmony_ci	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
15668c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
15678c2ecf20Sopenharmony_ci		goto expired;
15688c2ecf20Sopenharmony_ci	else if (maj_stat != 0)
15698c2ecf20Sopenharmony_ci		goto bad_mic;
15708c2ecf20Sopenharmony_ci	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
15718c2ecf20Sopenharmony_ci		goto marshal_failed;
15728c2ecf20Sopenharmony_ci	status = 0;
15738c2ecf20Sopenharmony_ciout:
15748c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
15758c2ecf20Sopenharmony_ci	return status;
15768c2ecf20Sopenharmony_ciexpired:
15778c2ecf20Sopenharmony_ci	clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
15788c2ecf20Sopenharmony_ci	status = -EKEYEXPIRED;
15798c2ecf20Sopenharmony_ci	goto out;
15808c2ecf20Sopenharmony_cimarshal_failed:
15818c2ecf20Sopenharmony_ci	status = -EMSGSIZE;
15828c2ecf20Sopenharmony_ci	goto out;
15838c2ecf20Sopenharmony_cibad_mic:
15848c2ecf20Sopenharmony_ci	trace_rpcgss_get_mic(task, maj_stat);
15858c2ecf20Sopenharmony_ci	status = -EIO;
15868c2ecf20Sopenharmony_ci	goto out;
15878c2ecf20Sopenharmony_ci}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_cistatic int gss_renew_cred(struct rpc_task *task)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	struct rpc_cred *oldcred = task->tk_rqstp->rq_cred;
15928c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(oldcred,
15938c2ecf20Sopenharmony_ci						 struct gss_cred,
15948c2ecf20Sopenharmony_ci						 gc_base);
15958c2ecf20Sopenharmony_ci	struct rpc_auth *auth = oldcred->cr_auth;
15968c2ecf20Sopenharmony_ci	struct auth_cred acred = {
15978c2ecf20Sopenharmony_ci		.cred = oldcred->cr_cred,
15988c2ecf20Sopenharmony_ci		.principal = gss_cred->gc_principal,
15998c2ecf20Sopenharmony_ci	};
16008c2ecf20Sopenharmony_ci	struct rpc_cred *new;
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW);
16038c2ecf20Sopenharmony_ci	if (IS_ERR(new))
16048c2ecf20Sopenharmony_ci		return PTR_ERR(new);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	task->tk_rqstp->rq_cred = new;
16078c2ecf20Sopenharmony_ci	put_rpccred(oldcred);
16088c2ecf20Sopenharmony_ci	return 0;
16098c2ecf20Sopenharmony_ci}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic int gss_cred_is_negative_entry(struct rpc_cred *cred)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) {
16148c2ecf20Sopenharmony_ci		unsigned long now = jiffies;
16158c2ecf20Sopenharmony_ci		unsigned long begin, expire;
16168c2ecf20Sopenharmony_ci		struct gss_cred *gss_cred;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		gss_cred = container_of(cred, struct gss_cred, gc_base);
16198c2ecf20Sopenharmony_ci		begin = gss_cred->gc_upcall_timestamp;
16208c2ecf20Sopenharmony_ci		expire = begin + gss_expired_cred_retry_delay * HZ;
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci		if (time_in_range_open(now, begin, expire))
16238c2ecf20Sopenharmony_ci			return 1;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci	return 0;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci/*
16298c2ecf20Sopenharmony_ci* Refresh credentials. XXX - finish
16308c2ecf20Sopenharmony_ci*/
16318c2ecf20Sopenharmony_cistatic int
16328c2ecf20Sopenharmony_cigss_refresh(struct rpc_task *task)
16338c2ecf20Sopenharmony_ci{
16348c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
16358c2ecf20Sopenharmony_ci	int ret = 0;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (gss_cred_is_negative_entry(cred))
16388c2ecf20Sopenharmony_ci		return -EKEYEXPIRED;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
16418c2ecf20Sopenharmony_ci			!test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
16428c2ecf20Sopenharmony_ci		ret = gss_renew_cred(task);
16438c2ecf20Sopenharmony_ci		if (ret < 0)
16448c2ecf20Sopenharmony_ci			goto out;
16458c2ecf20Sopenharmony_ci		cred = task->tk_rqstp->rq_cred;
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
16498c2ecf20Sopenharmony_ci		ret = gss_refresh_upcall(task);
16508c2ecf20Sopenharmony_ciout:
16518c2ecf20Sopenharmony_ci	return ret;
16528c2ecf20Sopenharmony_ci}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci/* Dummy refresh routine: used only when destroying the context */
16558c2ecf20Sopenharmony_cistatic int
16568c2ecf20Sopenharmony_cigss_refresh_null(struct rpc_task *task)
16578c2ecf20Sopenharmony_ci{
16588c2ecf20Sopenharmony_ci	return 0;
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic int
16628c2ecf20Sopenharmony_cigss_validate(struct rpc_task *task, struct xdr_stream *xdr)
16638c2ecf20Sopenharmony_ci{
16648c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
16658c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
16668c2ecf20Sopenharmony_ci	__be32		*p, *seq = NULL;
16678c2ecf20Sopenharmony_ci	struct kvec	iov;
16688c2ecf20Sopenharmony_ci	struct xdr_buf	verf_buf;
16698c2ecf20Sopenharmony_ci	struct xdr_netobj mic;
16708c2ecf20Sopenharmony_ci	u32		len, maj_stat;
16718c2ecf20Sopenharmony_ci	int		status;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
16748c2ecf20Sopenharmony_ci	if (!p)
16758c2ecf20Sopenharmony_ci		goto validate_failed;
16768c2ecf20Sopenharmony_ci	if (*p++ != rpc_auth_gss)
16778c2ecf20Sopenharmony_ci		goto validate_failed;
16788c2ecf20Sopenharmony_ci	len = be32_to_cpup(p);
16798c2ecf20Sopenharmony_ci	if (len > RPC_MAX_AUTH_SIZE)
16808c2ecf20Sopenharmony_ci		goto validate_failed;
16818c2ecf20Sopenharmony_ci	p = xdr_inline_decode(xdr, len);
16828c2ecf20Sopenharmony_ci	if (!p)
16838c2ecf20Sopenharmony_ci		goto validate_failed;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	seq = kmalloc(4, GFP_NOFS);
16868c2ecf20Sopenharmony_ci	if (!seq)
16878c2ecf20Sopenharmony_ci		goto validate_failed;
16888c2ecf20Sopenharmony_ci	*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
16898c2ecf20Sopenharmony_ci	iov.iov_base = seq;
16908c2ecf20Sopenharmony_ci	iov.iov_len = 4;
16918c2ecf20Sopenharmony_ci	xdr_buf_from_iov(&iov, &verf_buf);
16928c2ecf20Sopenharmony_ci	mic.data = (u8 *)p;
16938c2ecf20Sopenharmony_ci	mic.len = len;
16948c2ecf20Sopenharmony_ci	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
16958c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
16968c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
16978c2ecf20Sopenharmony_ci	if (maj_stat)
16988c2ecf20Sopenharmony_ci		goto bad_mic;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/* We leave it to unwrap to calculate au_rslack. For now we just
17018c2ecf20Sopenharmony_ci	 * calculate the length of the verifier: */
17028c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags))
17038c2ecf20Sopenharmony_ci		cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
17048c2ecf20Sopenharmony_ci	status = 0;
17058c2ecf20Sopenharmony_ciout:
17068c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
17078c2ecf20Sopenharmony_ci	kfree(seq);
17088c2ecf20Sopenharmony_ci	return status;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_civalidate_failed:
17118c2ecf20Sopenharmony_ci	status = -EIO;
17128c2ecf20Sopenharmony_ci	goto out;
17138c2ecf20Sopenharmony_cibad_mic:
17148c2ecf20Sopenharmony_ci	trace_rpcgss_verify_mic(task, maj_stat);
17158c2ecf20Sopenharmony_ci	status = -EACCES;
17168c2ecf20Sopenharmony_ci	goto out;
17178c2ecf20Sopenharmony_ci}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_cistatic noinline_for_stack int
17208c2ecf20Sopenharmony_cigss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
17218c2ecf20Sopenharmony_ci		   struct rpc_task *task, struct xdr_stream *xdr)
17228c2ecf20Sopenharmony_ci{
17238c2ecf20Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
17248c2ecf20Sopenharmony_ci	struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf;
17258c2ecf20Sopenharmony_ci	struct xdr_netobj mic;
17268c2ecf20Sopenharmony_ci	__be32 *p, *integ_len;
17278c2ecf20Sopenharmony_ci	u32 offset, maj_stat;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
17308c2ecf20Sopenharmony_ci	if (!p)
17318c2ecf20Sopenharmony_ci		goto wrap_failed;
17328c2ecf20Sopenharmony_ci	integ_len = p++;
17338c2ecf20Sopenharmony_ci	*p = cpu_to_be32(rqstp->rq_seqno);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	if (rpcauth_wrap_req_encode(task, xdr))
17368c2ecf20Sopenharmony_ci		goto wrap_failed;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
17398c2ecf20Sopenharmony_ci	if (xdr_buf_subsegment(snd_buf, &integ_buf,
17408c2ecf20Sopenharmony_ci				offset, snd_buf->len - offset))
17418c2ecf20Sopenharmony_ci		goto wrap_failed;
17428c2ecf20Sopenharmony_ci	*integ_len = cpu_to_be32(integ_buf.len);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	p = xdr_reserve_space(xdr, 0);
17458c2ecf20Sopenharmony_ci	if (!p)
17468c2ecf20Sopenharmony_ci		goto wrap_failed;
17478c2ecf20Sopenharmony_ci	mic.data = (u8 *)(p + 1);
17488c2ecf20Sopenharmony_ci	maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
17498c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
17508c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
17518c2ecf20Sopenharmony_ci	else if (maj_stat)
17528c2ecf20Sopenharmony_ci		goto bad_mic;
17538c2ecf20Sopenharmony_ci	/* Check that the trailing MIC fit in the buffer, after the fact */
17548c2ecf20Sopenharmony_ci	if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0)
17558c2ecf20Sopenharmony_ci		goto wrap_failed;
17568c2ecf20Sopenharmony_ci	return 0;
17578c2ecf20Sopenharmony_ciwrap_failed:
17588c2ecf20Sopenharmony_ci	return -EMSGSIZE;
17598c2ecf20Sopenharmony_cibad_mic:
17608c2ecf20Sopenharmony_ci	trace_rpcgss_get_mic(task, maj_stat);
17618c2ecf20Sopenharmony_ci	return -EIO;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_cistatic void
17658c2ecf20Sopenharmony_cipriv_release_snd_buf(struct rpc_rqst *rqstp)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	int i;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	for (i=0; i < rqstp->rq_enc_pages_num; i++)
17708c2ecf20Sopenharmony_ci		__free_page(rqstp->rq_enc_pages[i]);
17718c2ecf20Sopenharmony_ci	kfree(rqstp->rq_enc_pages);
17728c2ecf20Sopenharmony_ci	rqstp->rq_release_snd_buf = NULL;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic int
17768c2ecf20Sopenharmony_cialloc_enc_pages(struct rpc_rqst *rqstp)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
17798c2ecf20Sopenharmony_ci	int first, last, i;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	if (rqstp->rq_release_snd_buf)
17828c2ecf20Sopenharmony_ci		rqstp->rq_release_snd_buf(rqstp);
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	if (snd_buf->page_len == 0) {
17858c2ecf20Sopenharmony_ci		rqstp->rq_enc_pages_num = 0;
17868c2ecf20Sopenharmony_ci		return 0;
17878c2ecf20Sopenharmony_ci	}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	first = snd_buf->page_base >> PAGE_SHIFT;
17908c2ecf20Sopenharmony_ci	last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT;
17918c2ecf20Sopenharmony_ci	rqstp->rq_enc_pages_num = last - first + 1 + 1;
17928c2ecf20Sopenharmony_ci	rqstp->rq_enc_pages
17938c2ecf20Sopenharmony_ci		= kmalloc_array(rqstp->rq_enc_pages_num,
17948c2ecf20Sopenharmony_ci				sizeof(struct page *),
17958c2ecf20Sopenharmony_ci				GFP_NOFS);
17968c2ecf20Sopenharmony_ci	if (!rqstp->rq_enc_pages)
17978c2ecf20Sopenharmony_ci		goto out;
17988c2ecf20Sopenharmony_ci	for (i=0; i < rqstp->rq_enc_pages_num; i++) {
17998c2ecf20Sopenharmony_ci		rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS);
18008c2ecf20Sopenharmony_ci		if (rqstp->rq_enc_pages[i] == NULL)
18018c2ecf20Sopenharmony_ci			goto out_free;
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci	rqstp->rq_release_snd_buf = priv_release_snd_buf;
18048c2ecf20Sopenharmony_ci	return 0;
18058c2ecf20Sopenharmony_ciout_free:
18068c2ecf20Sopenharmony_ci	rqstp->rq_enc_pages_num = i;
18078c2ecf20Sopenharmony_ci	priv_release_snd_buf(rqstp);
18088c2ecf20Sopenharmony_ciout:
18098c2ecf20Sopenharmony_ci	return -EAGAIN;
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic noinline_for_stack int
18138c2ecf20Sopenharmony_cigss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
18148c2ecf20Sopenharmony_ci		  struct rpc_task *task, struct xdr_stream *xdr)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
18178c2ecf20Sopenharmony_ci	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;
18188c2ecf20Sopenharmony_ci	u32		pad, offset, maj_stat;
18198c2ecf20Sopenharmony_ci	int		status;
18208c2ecf20Sopenharmony_ci	__be32		*p, *opaque_len;
18218c2ecf20Sopenharmony_ci	struct page	**inpages;
18228c2ecf20Sopenharmony_ci	int		first;
18238c2ecf20Sopenharmony_ci	struct kvec	*iov;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	status = -EIO;
18268c2ecf20Sopenharmony_ci	p = xdr_reserve_space(xdr, 2 * sizeof(*p));
18278c2ecf20Sopenharmony_ci	if (!p)
18288c2ecf20Sopenharmony_ci		goto wrap_failed;
18298c2ecf20Sopenharmony_ci	opaque_len = p++;
18308c2ecf20Sopenharmony_ci	*p = cpu_to_be32(rqstp->rq_seqno);
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	if (rpcauth_wrap_req_encode(task, xdr))
18338c2ecf20Sopenharmony_ci		goto wrap_failed;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	status = alloc_enc_pages(rqstp);
18368c2ecf20Sopenharmony_ci	if (unlikely(status))
18378c2ecf20Sopenharmony_ci		goto wrap_failed;
18388c2ecf20Sopenharmony_ci	first = snd_buf->page_base >> PAGE_SHIFT;
18398c2ecf20Sopenharmony_ci	inpages = snd_buf->pages + first;
18408c2ecf20Sopenharmony_ci	snd_buf->pages = rqstp->rq_enc_pages;
18418c2ecf20Sopenharmony_ci	snd_buf->page_base -= first << PAGE_SHIFT;
18428c2ecf20Sopenharmony_ci	/*
18438c2ecf20Sopenharmony_ci	 * Move the tail into its own page, in case gss_wrap needs
18448c2ecf20Sopenharmony_ci	 * more space in the head when wrapping.
18458c2ecf20Sopenharmony_ci	 *
18468c2ecf20Sopenharmony_ci	 * Still... Why can't gss_wrap just slide the tail down?
18478c2ecf20Sopenharmony_ci	 */
18488c2ecf20Sopenharmony_ci	if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
18498c2ecf20Sopenharmony_ci		char *tmp;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci		tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
18528c2ecf20Sopenharmony_ci		memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
18538c2ecf20Sopenharmony_ci		snd_buf->tail[0].iov_base = tmp;
18548c2ecf20Sopenharmony_ci	}
18558c2ecf20Sopenharmony_ci	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
18568c2ecf20Sopenharmony_ci	maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages);
18578c2ecf20Sopenharmony_ci	/* slack space should prevent this ever happening: */
18588c2ecf20Sopenharmony_ci	if (unlikely(snd_buf->len > snd_buf->buflen))
18598c2ecf20Sopenharmony_ci		goto wrap_failed;
18608c2ecf20Sopenharmony_ci	/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
18618c2ecf20Sopenharmony_ci	 * done anyway, so it's safe to put the request on the wire: */
18628c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
18638c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
18648c2ecf20Sopenharmony_ci	else if (maj_stat)
18658c2ecf20Sopenharmony_ci		goto bad_wrap;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	*opaque_len = cpu_to_be32(snd_buf->len - offset);
18688c2ecf20Sopenharmony_ci	/* guess whether the pad goes into the head or the tail: */
18698c2ecf20Sopenharmony_ci	if (snd_buf->page_len || snd_buf->tail[0].iov_len)
18708c2ecf20Sopenharmony_ci		iov = snd_buf->tail;
18718c2ecf20Sopenharmony_ci	else
18728c2ecf20Sopenharmony_ci		iov = snd_buf->head;
18738c2ecf20Sopenharmony_ci	p = iov->iov_base + iov->iov_len;
18748c2ecf20Sopenharmony_ci	pad = xdr_pad_size(snd_buf->len - offset);
18758c2ecf20Sopenharmony_ci	memset(p, 0, pad);
18768c2ecf20Sopenharmony_ci	iov->iov_len += pad;
18778c2ecf20Sopenharmony_ci	snd_buf->len += pad;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	return 0;
18808c2ecf20Sopenharmony_ciwrap_failed:
18818c2ecf20Sopenharmony_ci	return status;
18828c2ecf20Sopenharmony_cibad_wrap:
18838c2ecf20Sopenharmony_ci	trace_rpcgss_wrap(task, maj_stat);
18848c2ecf20Sopenharmony_ci	return -EIO;
18858c2ecf20Sopenharmony_ci}
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_cistatic int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
18908c2ecf20Sopenharmony_ci	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
18918c2ecf20Sopenharmony_ci			gc_base);
18928c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
18938c2ecf20Sopenharmony_ci	int status;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	status = -EIO;
18968c2ecf20Sopenharmony_ci	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
18978c2ecf20Sopenharmony_ci		/* The spec seems a little ambiguous here, but I think that not
18988c2ecf20Sopenharmony_ci		 * wrapping context destruction requests makes the most sense.
18998c2ecf20Sopenharmony_ci		 */
19008c2ecf20Sopenharmony_ci		status = rpcauth_wrap_req_encode(task, xdr);
19018c2ecf20Sopenharmony_ci		goto out;
19028c2ecf20Sopenharmony_ci	}
19038c2ecf20Sopenharmony_ci	switch (gss_cred->gc_service) {
19048c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_NONE:
19058c2ecf20Sopenharmony_ci		status = rpcauth_wrap_req_encode(task, xdr);
19068c2ecf20Sopenharmony_ci		break;
19078c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_INTEGRITY:
19088c2ecf20Sopenharmony_ci		status = gss_wrap_req_integ(cred, ctx, task, xdr);
19098c2ecf20Sopenharmony_ci		break;
19108c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_PRIVACY:
19118c2ecf20Sopenharmony_ci		status = gss_wrap_req_priv(cred, ctx, task, xdr);
19128c2ecf20Sopenharmony_ci		break;
19138c2ecf20Sopenharmony_ci	default:
19148c2ecf20Sopenharmony_ci		status = -EIO;
19158c2ecf20Sopenharmony_ci	}
19168c2ecf20Sopenharmony_ciout:
19178c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
19188c2ecf20Sopenharmony_ci	return status;
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci/**
19228c2ecf20Sopenharmony_ci * gss_update_rslack - Possibly update RPC receive buffer size estimates
19238c2ecf20Sopenharmony_ci * @task: rpc_task for incoming RPC Reply being unwrapped
19248c2ecf20Sopenharmony_ci * @cred: controlling rpc_cred for @task
19258c2ecf20Sopenharmony_ci * @before: XDR words needed before each RPC Reply message
19268c2ecf20Sopenharmony_ci * @after: XDR words needed following each RPC Reply message
19278c2ecf20Sopenharmony_ci *
19288c2ecf20Sopenharmony_ci */
19298c2ecf20Sopenharmony_cistatic void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred,
19308c2ecf20Sopenharmony_ci			      unsigned int before, unsigned int after)
19318c2ecf20Sopenharmony_ci{
19328c2ecf20Sopenharmony_ci	struct rpc_auth *auth = cred->cr_auth;
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) {
19358c2ecf20Sopenharmony_ci		auth->au_ralign = auth->au_verfsize + before;
19368c2ecf20Sopenharmony_ci		auth->au_rslack = auth->au_verfsize + after;
19378c2ecf20Sopenharmony_ci		trace_rpcgss_update_slack(task, auth);
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_cistatic int
19428c2ecf20Sopenharmony_cigss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred)
19438c2ecf20Sopenharmony_ci{
19448c2ecf20Sopenharmony_ci	gss_update_rslack(task, cred, 0, 0);
19458c2ecf20Sopenharmony_ci	return 0;
19468c2ecf20Sopenharmony_ci}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci/*
19498c2ecf20Sopenharmony_ci * RFC 2203, Section 5.3.2.2
19508c2ecf20Sopenharmony_ci *
19518c2ecf20Sopenharmony_ci *	struct rpc_gss_integ_data {
19528c2ecf20Sopenharmony_ci *		opaque databody_integ<>;
19538c2ecf20Sopenharmony_ci *		opaque checksum<>;
19548c2ecf20Sopenharmony_ci *	};
19558c2ecf20Sopenharmony_ci *
19568c2ecf20Sopenharmony_ci *	struct rpc_gss_data_t {
19578c2ecf20Sopenharmony_ci *		unsigned int seq_num;
19588c2ecf20Sopenharmony_ci *		proc_req_arg_t arg;
19598c2ecf20Sopenharmony_ci *	};
19608c2ecf20Sopenharmony_ci */
19618c2ecf20Sopenharmony_cistatic noinline_for_stack int
19628c2ecf20Sopenharmony_cigss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
19638c2ecf20Sopenharmony_ci		      struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
19648c2ecf20Sopenharmony_ci		      struct xdr_stream *xdr)
19658c2ecf20Sopenharmony_ci{
19668c2ecf20Sopenharmony_ci	struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf;
19678c2ecf20Sopenharmony_ci	u32 len, offset, seqno, maj_stat;
19688c2ecf20Sopenharmony_ci	struct xdr_netobj mic;
19698c2ecf20Sopenharmony_ci	int ret;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	ret = -EIO;
19728c2ecf20Sopenharmony_ci	mic.data = NULL;
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	/* opaque databody_integ<>; */
19758c2ecf20Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &len))
19768c2ecf20Sopenharmony_ci		goto unwrap_failed;
19778c2ecf20Sopenharmony_ci	if (len & 3)
19788c2ecf20Sopenharmony_ci		goto unwrap_failed;
19798c2ecf20Sopenharmony_ci	offset = rcv_buf->len - xdr_stream_remaining(xdr);
19808c2ecf20Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &seqno))
19818c2ecf20Sopenharmony_ci		goto unwrap_failed;
19828c2ecf20Sopenharmony_ci	if (seqno != rqstp->rq_seqno)
19838c2ecf20Sopenharmony_ci		goto bad_seqno;
19848c2ecf20Sopenharmony_ci	if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len))
19858c2ecf20Sopenharmony_ci		goto unwrap_failed;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	/*
19888c2ecf20Sopenharmony_ci	 * The xdr_stream now points to the beginning of the
19898c2ecf20Sopenharmony_ci	 * upper layer payload, to be passed below to
19908c2ecf20Sopenharmony_ci	 * rpcauth_unwrap_resp_decode(). The checksum, which
19918c2ecf20Sopenharmony_ci	 * follows the upper layer payload in @rcv_buf, is
19928c2ecf20Sopenharmony_ci	 * located and parsed without updating the xdr_stream.
19938c2ecf20Sopenharmony_ci	 */
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	/* opaque checksum<>; */
19968c2ecf20Sopenharmony_ci	offset += len;
19978c2ecf20Sopenharmony_ci	if (xdr_decode_word(rcv_buf, offset, &len))
19988c2ecf20Sopenharmony_ci		goto unwrap_failed;
19998c2ecf20Sopenharmony_ci	offset += sizeof(__be32);
20008c2ecf20Sopenharmony_ci	if (offset + len > rcv_buf->len)
20018c2ecf20Sopenharmony_ci		goto unwrap_failed;
20028c2ecf20Sopenharmony_ci	mic.len = len;
20038c2ecf20Sopenharmony_ci	mic.data = kmalloc(len, GFP_NOFS);
20048c2ecf20Sopenharmony_ci	if (!mic.data)
20058c2ecf20Sopenharmony_ci		goto unwrap_failed;
20068c2ecf20Sopenharmony_ci	if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len))
20078c2ecf20Sopenharmony_ci		goto unwrap_failed;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic);
20108c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
20118c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
20128c2ecf20Sopenharmony_ci	if (maj_stat != GSS_S_COMPLETE)
20138c2ecf20Sopenharmony_ci		goto bad_mic;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len));
20168c2ecf20Sopenharmony_ci	ret = 0;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ciout:
20198c2ecf20Sopenharmony_ci	kfree(mic.data);
20208c2ecf20Sopenharmony_ci	return ret;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ciunwrap_failed:
20238c2ecf20Sopenharmony_ci	trace_rpcgss_unwrap_failed(task);
20248c2ecf20Sopenharmony_ci	goto out;
20258c2ecf20Sopenharmony_cibad_seqno:
20268c2ecf20Sopenharmony_ci	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno);
20278c2ecf20Sopenharmony_ci	goto out;
20288c2ecf20Sopenharmony_cibad_mic:
20298c2ecf20Sopenharmony_ci	trace_rpcgss_verify_mic(task, maj_stat);
20308c2ecf20Sopenharmony_ci	goto out;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_cistatic noinline_for_stack int
20348c2ecf20Sopenharmony_cigss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
20358c2ecf20Sopenharmony_ci		     struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp,
20368c2ecf20Sopenharmony_ci		     struct xdr_stream *xdr)
20378c2ecf20Sopenharmony_ci{
20388c2ecf20Sopenharmony_ci	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
20398c2ecf20Sopenharmony_ci	struct kvec *head = rqstp->rq_rcv_buf.head;
20408c2ecf20Sopenharmony_ci	u32 offset, opaque_len, maj_stat;
20418c2ecf20Sopenharmony_ci	__be32 *p;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	p = xdr_inline_decode(xdr, 2 * sizeof(*p));
20448c2ecf20Sopenharmony_ci	if (unlikely(!p))
20458c2ecf20Sopenharmony_ci		goto unwrap_failed;
20468c2ecf20Sopenharmony_ci	opaque_len = be32_to_cpup(p++);
20478c2ecf20Sopenharmony_ci	offset = (u8 *)(p) - (u8 *)head->iov_base;
20488c2ecf20Sopenharmony_ci	if (offset + opaque_len > rcv_buf->len)
20498c2ecf20Sopenharmony_ci		goto unwrap_failed;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
20528c2ecf20Sopenharmony_ci			      offset + opaque_len, rcv_buf);
20538c2ecf20Sopenharmony_ci	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
20548c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
20558c2ecf20Sopenharmony_ci	if (maj_stat != GSS_S_COMPLETE)
20568c2ecf20Sopenharmony_ci		goto bad_unwrap;
20578c2ecf20Sopenharmony_ci	/* gss_unwrap decrypted the sequence number */
20588c2ecf20Sopenharmony_ci	if (be32_to_cpup(p++) != rqstp->rq_seqno)
20598c2ecf20Sopenharmony_ci		goto bad_seqno;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	/* gss_unwrap redacts the opaque blob from the head iovec.
20628c2ecf20Sopenharmony_ci	 * rcv_buf has changed, thus the stream needs to be reset.
20638c2ecf20Sopenharmony_ci	 */
20648c2ecf20Sopenharmony_ci	xdr_init_decode(xdr, rcv_buf, p, rqstp);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align,
20678c2ecf20Sopenharmony_ci			  2 + ctx->gc_gss_ctx->slack);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	return 0;
20708c2ecf20Sopenharmony_ciunwrap_failed:
20718c2ecf20Sopenharmony_ci	trace_rpcgss_unwrap_failed(task);
20728c2ecf20Sopenharmony_ci	return -EIO;
20738c2ecf20Sopenharmony_cibad_seqno:
20748c2ecf20Sopenharmony_ci	trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p));
20758c2ecf20Sopenharmony_ci	return -EIO;
20768c2ecf20Sopenharmony_cibad_unwrap:
20778c2ecf20Sopenharmony_ci	trace_rpcgss_unwrap(task, maj_stat);
20788c2ecf20Sopenharmony_ci	return -EIO;
20798c2ecf20Sopenharmony_ci}
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_cistatic bool
20828c2ecf20Sopenharmony_cigss_seq_is_newer(u32 new, u32 old)
20838c2ecf20Sopenharmony_ci{
20848c2ecf20Sopenharmony_ci	return (s32)(new - old) > 0;
20858c2ecf20Sopenharmony_ci}
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_cistatic bool
20888c2ecf20Sopenharmony_cigss_xmit_need_reencode(struct rpc_task *task)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
20918c2ecf20Sopenharmony_ci	struct rpc_cred *cred = req->rq_cred;
20928c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
20938c2ecf20Sopenharmony_ci	u32 win, seq_xmit = 0;
20948c2ecf20Sopenharmony_ci	bool ret = true;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	if (!ctx)
20978c2ecf20Sopenharmony_ci		goto out;
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq)))
21008c2ecf20Sopenharmony_ci		goto out_ctx;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	seq_xmit = READ_ONCE(ctx->gc_seq_xmit);
21038c2ecf20Sopenharmony_ci	while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) {
21048c2ecf20Sopenharmony_ci		u32 tmp = seq_xmit;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci		seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno);
21078c2ecf20Sopenharmony_ci		if (seq_xmit == tmp) {
21088c2ecf20Sopenharmony_ci			ret = false;
21098c2ecf20Sopenharmony_ci			goto out_ctx;
21108c2ecf20Sopenharmony_ci		}
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	win = ctx->gc_win;
21148c2ecf20Sopenharmony_ci	if (win > 0)
21158c2ecf20Sopenharmony_ci		ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win);
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ciout_ctx:
21188c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
21198c2ecf20Sopenharmony_ciout:
21208c2ecf20Sopenharmony_ci	trace_rpcgss_need_reencode(task, seq_xmit, ret);
21218c2ecf20Sopenharmony_ci	return ret;
21228c2ecf20Sopenharmony_ci}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cistatic int
21258c2ecf20Sopenharmony_cigss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
21268c2ecf20Sopenharmony_ci{
21278c2ecf20Sopenharmony_ci	struct rpc_rqst *rqstp = task->tk_rqstp;
21288c2ecf20Sopenharmony_ci	struct rpc_cred *cred = rqstp->rq_cred;
21298c2ecf20Sopenharmony_ci	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
21308c2ecf20Sopenharmony_ci			gc_base);
21318c2ecf20Sopenharmony_ci	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
21328c2ecf20Sopenharmony_ci	int status = -EIO;
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
21358c2ecf20Sopenharmony_ci		goto out_decode;
21368c2ecf20Sopenharmony_ci	switch (gss_cred->gc_service) {
21378c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_NONE:
21388c2ecf20Sopenharmony_ci		status = gss_unwrap_resp_auth(task, cred);
21398c2ecf20Sopenharmony_ci		break;
21408c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_INTEGRITY:
21418c2ecf20Sopenharmony_ci		status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr);
21428c2ecf20Sopenharmony_ci		break;
21438c2ecf20Sopenharmony_ci	case RPC_GSS_SVC_PRIVACY:
21448c2ecf20Sopenharmony_ci		status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr);
21458c2ecf20Sopenharmony_ci		break;
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci	if (status)
21488c2ecf20Sopenharmony_ci		goto out;
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ciout_decode:
21518c2ecf20Sopenharmony_ci	status = rpcauth_unwrap_resp_decode(task, xdr);
21528c2ecf20Sopenharmony_ciout:
21538c2ecf20Sopenharmony_ci	gss_put_ctx(ctx);
21548c2ecf20Sopenharmony_ci	return status;
21558c2ecf20Sopenharmony_ci}
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_cistatic const struct rpc_authops authgss_ops = {
21588c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
21598c2ecf20Sopenharmony_ci	.au_flavor	= RPC_AUTH_GSS,
21608c2ecf20Sopenharmony_ci	.au_name	= "RPCSEC_GSS",
21618c2ecf20Sopenharmony_ci	.create		= gss_create,
21628c2ecf20Sopenharmony_ci	.destroy	= gss_destroy,
21638c2ecf20Sopenharmony_ci	.hash_cred	= gss_hash_cred,
21648c2ecf20Sopenharmony_ci	.lookup_cred	= gss_lookup_cred,
21658c2ecf20Sopenharmony_ci	.crcreate	= gss_create_cred,
21668c2ecf20Sopenharmony_ci	.info2flavor	= gss_mech_info2flavor,
21678c2ecf20Sopenharmony_ci	.flavor2info	= gss_mech_flavor2info,
21688c2ecf20Sopenharmony_ci};
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_credops = {
21718c2ecf20Sopenharmony_ci	.cr_name		= "AUTH_GSS",
21728c2ecf20Sopenharmony_ci	.crdestroy		= gss_destroy_cred,
21738c2ecf20Sopenharmony_ci	.cr_init		= gss_cred_init,
21748c2ecf20Sopenharmony_ci	.crmatch		= gss_match,
21758c2ecf20Sopenharmony_ci	.crmarshal		= gss_marshal,
21768c2ecf20Sopenharmony_ci	.crrefresh		= gss_refresh,
21778c2ecf20Sopenharmony_ci	.crvalidate		= gss_validate,
21788c2ecf20Sopenharmony_ci	.crwrap_req		= gss_wrap_req,
21798c2ecf20Sopenharmony_ci	.crunwrap_resp		= gss_unwrap_resp,
21808c2ecf20Sopenharmony_ci	.crkey_timeout		= gss_key_timeout,
21818c2ecf20Sopenharmony_ci	.crstringify_acceptor	= gss_stringify_acceptor,
21828c2ecf20Sopenharmony_ci	.crneed_reencode	= gss_xmit_need_reencode,
21838c2ecf20Sopenharmony_ci};
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_nullops = {
21868c2ecf20Sopenharmony_ci	.cr_name		= "AUTH_GSS",
21878c2ecf20Sopenharmony_ci	.crdestroy		= gss_destroy_nullcred,
21888c2ecf20Sopenharmony_ci	.crmatch		= gss_match,
21898c2ecf20Sopenharmony_ci	.crmarshal		= gss_marshal,
21908c2ecf20Sopenharmony_ci	.crrefresh		= gss_refresh_null,
21918c2ecf20Sopenharmony_ci	.crvalidate		= gss_validate,
21928c2ecf20Sopenharmony_ci	.crwrap_req		= gss_wrap_req,
21938c2ecf20Sopenharmony_ci	.crunwrap_resp		= gss_unwrap_resp,
21948c2ecf20Sopenharmony_ci	.crstringify_acceptor	= gss_stringify_acceptor,
21958c2ecf20Sopenharmony_ci};
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0 = {
21988c2ecf20Sopenharmony_ci	.upcall		= gss_v0_upcall,
21998c2ecf20Sopenharmony_ci	.downcall	= gss_pipe_downcall,
22008c2ecf20Sopenharmony_ci	.destroy_msg	= gss_pipe_destroy_msg,
22018c2ecf20Sopenharmony_ci	.open_pipe	= gss_pipe_open_v0,
22028c2ecf20Sopenharmony_ci	.release_pipe	= gss_pipe_release,
22038c2ecf20Sopenharmony_ci};
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1 = {
22068c2ecf20Sopenharmony_ci	.upcall		= gss_v1_upcall,
22078c2ecf20Sopenharmony_ci	.downcall	= gss_pipe_downcall,
22088c2ecf20Sopenharmony_ci	.destroy_msg	= gss_pipe_destroy_msg,
22098c2ecf20Sopenharmony_ci	.open_pipe	= gss_pipe_open_v1,
22108c2ecf20Sopenharmony_ci	.release_pipe	= gss_pipe_release,
22118c2ecf20Sopenharmony_ci};
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_cistatic __net_init int rpcsec_gss_init_net(struct net *net)
22148c2ecf20Sopenharmony_ci{
22158c2ecf20Sopenharmony_ci	return gss_svc_init_net(net);
22168c2ecf20Sopenharmony_ci}
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_cistatic __net_exit void rpcsec_gss_exit_net(struct net *net)
22198c2ecf20Sopenharmony_ci{
22208c2ecf20Sopenharmony_ci	gss_svc_shutdown_net(net);
22218c2ecf20Sopenharmony_ci}
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_cistatic struct pernet_operations rpcsec_gss_net_ops = {
22248c2ecf20Sopenharmony_ci	.init = rpcsec_gss_init_net,
22258c2ecf20Sopenharmony_ci	.exit = rpcsec_gss_exit_net,
22268c2ecf20Sopenharmony_ci};
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci/*
22298c2ecf20Sopenharmony_ci * Initialize RPCSEC_GSS module
22308c2ecf20Sopenharmony_ci */
22318c2ecf20Sopenharmony_cistatic int __init init_rpcsec_gss(void)
22328c2ecf20Sopenharmony_ci{
22338c2ecf20Sopenharmony_ci	int err = 0;
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	err = rpcauth_register(&authgss_ops);
22368c2ecf20Sopenharmony_ci	if (err)
22378c2ecf20Sopenharmony_ci		goto out;
22388c2ecf20Sopenharmony_ci	err = gss_svc_init();
22398c2ecf20Sopenharmony_ci	if (err)
22408c2ecf20Sopenharmony_ci		goto out_unregister;
22418c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&rpcsec_gss_net_ops);
22428c2ecf20Sopenharmony_ci	if (err)
22438c2ecf20Sopenharmony_ci		goto out_svc_exit;
22448c2ecf20Sopenharmony_ci	rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
22458c2ecf20Sopenharmony_ci	return 0;
22468c2ecf20Sopenharmony_ciout_svc_exit:
22478c2ecf20Sopenharmony_ci	gss_svc_shutdown();
22488c2ecf20Sopenharmony_ciout_unregister:
22498c2ecf20Sopenharmony_ci	rpcauth_unregister(&authgss_ops);
22508c2ecf20Sopenharmony_ciout:
22518c2ecf20Sopenharmony_ci	return err;
22528c2ecf20Sopenharmony_ci}
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_cistatic void __exit exit_rpcsec_gss(void)
22558c2ecf20Sopenharmony_ci{
22568c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&rpcsec_gss_net_ops);
22578c2ecf20Sopenharmony_ci	gss_svc_shutdown();
22588c2ecf20Sopenharmony_ci	rpcauth_unregister(&authgss_ops);
22598c2ecf20Sopenharmony_ci	rcu_barrier(); /* Wait for completion of call_rcu()'s */
22608c2ecf20Sopenharmony_ci}
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ciMODULE_ALIAS("rpc-auth-6");
22638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
22648c2ecf20Sopenharmony_cimodule_param_named(expired_cred_retry_delay,
22658c2ecf20Sopenharmony_ci		   gss_expired_cred_retry_delay,
22668c2ecf20Sopenharmony_ci		   uint, 0644);
22678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
22688c2ecf20Sopenharmony_ci		"the RPC engine retries an expired credential");
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_cimodule_param_named(key_expire_timeo,
22718c2ecf20Sopenharmony_ci		   gss_key_expire_timeo,
22728c2ecf20Sopenharmony_ci		   uint, 0644);
22738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
22748c2ecf20Sopenharmony_ci		"credential keys lifetime where the NFS layer cleans up "
22758c2ecf20Sopenharmony_ci		"prior to key expiration");
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_cimodule_init(init_rpcsec_gss)
22788c2ecf20Sopenharmony_cimodule_exit(exit_rpcsec_gss)
2279