xref: /kernel/linux/linux-6.6/fs/nfs/callback.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/nfs/callback.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004 Trond Myklebust
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * NFSv4 callback handling
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/completion.h>
1162306a36Sopenharmony_ci#include <linux/ip.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/sched/signal.h>
1462306a36Sopenharmony_ci#include <linux/sunrpc/svc.h>
1562306a36Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
1662306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/freezer.h>
2062306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h>
2162306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <net/inet_sock.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "nfs4_fs.h"
2662306a36Sopenharmony_ci#include "callback.h"
2762306a36Sopenharmony_ci#include "internal.h"
2862306a36Sopenharmony_ci#include "netns.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_CALLBACK
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct nfs_callback_data {
3362306a36Sopenharmony_ci	unsigned int users;
3462306a36Sopenharmony_ci	struct svc_serv *serv;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
3862306a36Sopenharmony_cistatic DEFINE_MUTEX(nfs_callback_mutex);
3962306a36Sopenharmony_cistatic struct svc_program nfs4_callback_program;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	const struct cred *cred = current_cred();
4462306a36Sopenharmony_ci	int ret;
4562306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	ret = svc_xprt_create(serv, "tcp", net, PF_INET,
4862306a36Sopenharmony_ci			      nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
4962306a36Sopenharmony_ci			      cred);
5062306a36Sopenharmony_ci	if (ret <= 0)
5162306a36Sopenharmony_ci		goto out_err;
5262306a36Sopenharmony_ci	nn->nfs_callback_tcpport = ret;
5362306a36Sopenharmony_ci	dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
5462306a36Sopenharmony_ci		nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ret = svc_xprt_create(serv, "tcp", net, PF_INET6,
5762306a36Sopenharmony_ci			      nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
5862306a36Sopenharmony_ci			      cred);
5962306a36Sopenharmony_ci	if (ret > 0) {
6062306a36Sopenharmony_ci		nn->nfs_callback_tcpport6 = ret;
6162306a36Sopenharmony_ci		dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
6262306a36Sopenharmony_ci			nn->nfs_callback_tcpport6, PF_INET6, net->ns.inum);
6362306a36Sopenharmony_ci	} else if (ret != -EAFNOSUPPORT)
6462306a36Sopenharmony_ci		goto out_err;
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciout_err:
6862306a36Sopenharmony_ci	return (ret) ? ret : -ENOMEM;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * This is the NFSv4 callback kernel thread.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic int
7562306a36Sopenharmony_cinfs4_callback_svc(void *vrqstp)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct svc_rqst *rqstp = vrqstp;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	set_freezable();
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	while (!kthread_freezable_should_stop(NULL))
8262306a36Sopenharmony_ci		svc_recv(rqstp);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	svc_exit_thread(rqstp);
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * The callback service for NFSv4.1 callbacks
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic int
9362306a36Sopenharmony_cinfs41_callback_svc(void *vrqstp)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct svc_rqst *rqstp = vrqstp;
9662306a36Sopenharmony_ci	struct svc_serv *serv = rqstp->rq_server;
9762306a36Sopenharmony_ci	struct rpc_rqst *req;
9862306a36Sopenharmony_ci	int error;
9962306a36Sopenharmony_ci	DEFINE_WAIT(wq);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	set_freezable();
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	while (!kthread_freezable_should_stop(NULL)) {
10462306a36Sopenharmony_ci		prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_IDLE);
10562306a36Sopenharmony_ci		spin_lock_bh(&serv->sv_cb_lock);
10662306a36Sopenharmony_ci		if (!list_empty(&serv->sv_cb_list)) {
10762306a36Sopenharmony_ci			req = list_first_entry(&serv->sv_cb_list,
10862306a36Sopenharmony_ci					struct rpc_rqst, rq_bc_list);
10962306a36Sopenharmony_ci			list_del(&req->rq_bc_list);
11062306a36Sopenharmony_ci			spin_unlock_bh(&serv->sv_cb_lock);
11162306a36Sopenharmony_ci			finish_wait(&serv->sv_cb_waitq, &wq);
11262306a36Sopenharmony_ci			dprintk("Invoking bc_svc_process()\n");
11362306a36Sopenharmony_ci			error = bc_svc_process(serv, req, rqstp);
11462306a36Sopenharmony_ci			dprintk("bc_svc_process() returned w/ error code= %d\n",
11562306a36Sopenharmony_ci				error);
11662306a36Sopenharmony_ci		} else {
11762306a36Sopenharmony_ci			spin_unlock_bh(&serv->sv_cb_lock);
11862306a36Sopenharmony_ci			if (!kthread_should_stop())
11962306a36Sopenharmony_ci				schedule();
12062306a36Sopenharmony_ci			finish_wait(&serv->sv_cb_waitq, &wq);
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	svc_exit_thread(rqstp);
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
12962306a36Sopenharmony_ci		struct svc_serv *serv)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (minorversion)
13262306a36Sopenharmony_ci		/*
13362306a36Sopenharmony_ci		 * Save the svc_serv in the transport so that it can
13462306a36Sopenharmony_ci		 * be referenced when the session backchannel is initialized
13562306a36Sopenharmony_ci		 */
13662306a36Sopenharmony_ci		xprt->bc_serv = serv;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci#else
13962306a36Sopenharmony_cistatic inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
14062306a36Sopenharmony_ci		struct svc_serv *serv)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
14662306a36Sopenharmony_ci				  struct svc_serv *serv)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	int nrservs = nfs_callback_nr_threads;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	nfs_callback_bc_serv(minorversion, xprt, serv);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
15462306a36Sopenharmony_ci		nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (serv->sv_nrthreads == nrservs)
15762306a36Sopenharmony_ci		return 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = svc_set_num_threads(serv, NULL, nrservs);
16062306a36Sopenharmony_ci	if (ret) {
16162306a36Sopenharmony_ci		svc_set_num_threads(serv, NULL, 0);
16262306a36Sopenharmony_ci		return ret;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	dprintk("nfs_callback_up: service started\n");
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (--nn->cb_users[minorversion])
17362306a36Sopenharmony_ci		return;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
17662306a36Sopenharmony_ci	svc_xprt_destroy_all(serv, net);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
18062306a36Sopenharmony_ci			       struct net *net, struct rpc_xprt *xprt)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
18362306a36Sopenharmony_ci	int ret;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (nn->cb_users[minorversion]++)
18662306a36Sopenharmony_ci		return 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	dprintk("NFS: create per-net callback data; net=%x\n", net->ns.inum);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	ret = svc_bind(serv, net);
19162306a36Sopenharmony_ci	if (ret < 0) {
19262306a36Sopenharmony_ci		printk(KERN_WARNING "NFS: bind callback service failed\n");
19362306a36Sopenharmony_ci		goto err_bind;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	ret = 0;
19762306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0)
19862306a36Sopenharmony_ci		ret = nfs4_callback_up_net(serv, net);
19962306a36Sopenharmony_ci	else if (xprt->ops->bc_setup)
20062306a36Sopenharmony_ci		set_bc_enabled(serv);
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		ret = -EPROTONOSUPPORT;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (ret < 0) {
20562306a36Sopenharmony_ci		printk(KERN_ERR "NFS: callback service start failed\n");
20662306a36Sopenharmony_ci		goto err_socks;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cierr_socks:
21162306a36Sopenharmony_ci	svc_rpcb_cleanup(serv, net);
21262306a36Sopenharmony_cierr_bind:
21362306a36Sopenharmony_ci	nn->cb_users[minorversion]--;
21462306a36Sopenharmony_ci	dprintk("NFS: Couldn't create callback socket: err = %d; "
21562306a36Sopenharmony_ci			"net = %x\n", ret, net->ns.inum);
21662306a36Sopenharmony_ci	return ret;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic struct svc_serv *nfs_callback_create_svc(int minorversion)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
22262306a36Sopenharmony_ci	int (*threadfn)(void *data);
22362306a36Sopenharmony_ci	struct svc_serv *serv;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/*
22662306a36Sopenharmony_ci	 * Check whether we're already up and running.
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	if (cb_info->serv)
22962306a36Sopenharmony_ci		return svc_get(cb_info->serv);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * Sanity check: if there's no task,
23362306a36Sopenharmony_ci	 * we should be the first user ...
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	if (cb_info->users)
23662306a36Sopenharmony_ci		printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
23762306a36Sopenharmony_ci			cb_info->users);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	threadfn = nfs4_callback_svc;
24062306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
24162306a36Sopenharmony_ci	if (minorversion)
24262306a36Sopenharmony_ci		threadfn = nfs41_callback_svc;
24362306a36Sopenharmony_ci#else
24462306a36Sopenharmony_ci	if (minorversion)
24562306a36Sopenharmony_ci		return ERR_PTR(-ENOTSUPP);
24662306a36Sopenharmony_ci#endif
24762306a36Sopenharmony_ci	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
24862306a36Sopenharmony_ci			  threadfn);
24962306a36Sopenharmony_ci	if (!serv) {
25062306a36Sopenharmony_ci		printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
25162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	cb_info->serv = serv;
25462306a36Sopenharmony_ci	/* As there is only one thread we need to over-ride the
25562306a36Sopenharmony_ci	 * default maximum of 80 connections
25662306a36Sopenharmony_ci	 */
25762306a36Sopenharmony_ci	serv->sv_maxconn = 1024;
25862306a36Sopenharmony_ci	dprintk("nfs_callback_create_svc: service created\n");
25962306a36Sopenharmony_ci	return serv;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * Bring up the callback thread if it is not already up.
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ciint nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct svc_serv *serv;
26862306a36Sopenharmony_ci	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
26962306a36Sopenharmony_ci	int ret;
27062306a36Sopenharmony_ci	struct net *net = xprt->xprt_net;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	mutex_lock(&nfs_callback_mutex);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	serv = nfs_callback_create_svc(minorversion);
27562306a36Sopenharmony_ci	if (IS_ERR(serv)) {
27662306a36Sopenharmony_ci		ret = PTR_ERR(serv);
27762306a36Sopenharmony_ci		goto err_create;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ret = nfs_callback_up_net(minorversion, serv, net, xprt);
28162306a36Sopenharmony_ci	if (ret < 0)
28262306a36Sopenharmony_ci		goto err_net;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	ret = nfs_callback_start_svc(minorversion, xprt, serv);
28562306a36Sopenharmony_ci	if (ret < 0)
28662306a36Sopenharmony_ci		goto err_start;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	cb_info->users++;
28962306a36Sopenharmony_cierr_net:
29062306a36Sopenharmony_ci	if (!cb_info->users)
29162306a36Sopenharmony_ci		cb_info->serv = NULL;
29262306a36Sopenharmony_ci	svc_put(serv);
29362306a36Sopenharmony_cierr_create:
29462306a36Sopenharmony_ci	mutex_unlock(&nfs_callback_mutex);
29562306a36Sopenharmony_ci	return ret;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cierr_start:
29862306a36Sopenharmony_ci	nfs_callback_down_net(minorversion, serv, net);
29962306a36Sopenharmony_ci	dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
30062306a36Sopenharmony_ci	goto err_net;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * Kill the callback thread if it's no longer being used.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_civoid nfs_callback_down(int minorversion, struct net *net)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
30962306a36Sopenharmony_ci	struct svc_serv *serv;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	mutex_lock(&nfs_callback_mutex);
31262306a36Sopenharmony_ci	serv = cb_info->serv;
31362306a36Sopenharmony_ci	nfs_callback_down_net(minorversion, serv, net);
31462306a36Sopenharmony_ci	cb_info->users--;
31562306a36Sopenharmony_ci	if (cb_info->users == 0) {
31662306a36Sopenharmony_ci		svc_get(serv);
31762306a36Sopenharmony_ci		svc_set_num_threads(serv, NULL, 0);
31862306a36Sopenharmony_ci		svc_put(serv);
31962306a36Sopenharmony_ci		dprintk("nfs_callback_down: service destroyed\n");
32062306a36Sopenharmony_ci		cb_info->serv = NULL;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	mutex_unlock(&nfs_callback_mutex);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/* Boolean check of RPC_AUTH_GSS principal */
32662306a36Sopenharmony_ciint
32762306a36Sopenharmony_cicheck_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	char *p = rqstp->rq_cred.cr_principal;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
33262306a36Sopenharmony_ci		return 1;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
33562306a36Sopenharmony_ci	if (clp->cl_minorversion != 0)
33662306a36Sopenharmony_ci		return 0;
33762306a36Sopenharmony_ci	/*
33862306a36Sopenharmony_ci	 * It might just be a normal user principal, in which case
33962306a36Sopenharmony_ci	 * userspace won't bother to tell us the name at all.
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	if (p == NULL)
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/*
34562306a36Sopenharmony_ci	 * Did we get the acceptor from userland during the SETCLIENID
34662306a36Sopenharmony_ci	 * negotiation?
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	if (clp->cl_acceptor)
34962306a36Sopenharmony_ci		return !strcmp(p, clp->cl_acceptor);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * Otherwise try to verify it using the cl_hostname. Note that this
35362306a36Sopenharmony_ci	 * doesn't work if a non-canonical hostname was used in the devname.
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (memcmp(p, "nfs@", 4) != 0)
35962306a36Sopenharmony_ci		return 0;
36062306a36Sopenharmony_ci	p += 4;
36162306a36Sopenharmony_ci	if (strcmp(p, clp->cl_hostname) != 0)
36262306a36Sopenharmony_ci		return 0;
36362306a36Sopenharmony_ci	return 1;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/*
36762306a36Sopenharmony_ci * pg_authenticate method for nfsv4 callback threads.
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * The authflavor has been negotiated, so an incorrect flavor is a server
37062306a36Sopenharmony_ci * bug. Deny packets with incorrect authflavor.
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * All other checking done after NFS decoding where the nfs_client can be
37362306a36Sopenharmony_ci * found in nfs4_callback_compound
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_cistatic enum svc_auth_status nfs_callback_authenticate(struct svc_rqst *rqstp)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	rqstp->rq_auth_stat = rpc_autherr_badcred;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	switch (rqstp->rq_authop->flavour) {
38062306a36Sopenharmony_ci	case RPC_AUTH_NULL:
38162306a36Sopenharmony_ci		if (rqstp->rq_proc != CB_NULL)
38262306a36Sopenharmony_ci			return SVC_DENIED;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	case RPC_AUTH_GSS:
38562306a36Sopenharmony_ci		/* No RPC_AUTH_GSS support yet in NFSv4.1 */
38662306a36Sopenharmony_ci		 if (svc_is_backchannel(rqstp))
38762306a36Sopenharmony_ci			return SVC_DENIED;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	rqstp->rq_auth_stat = rpc_auth_ok;
39162306a36Sopenharmony_ci	return SVC_OK;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/*
39562306a36Sopenharmony_ci * Define NFS4 callback program
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_cistatic const struct svc_version *nfs4_callback_version[] = {
39862306a36Sopenharmony_ci	[1] = &nfs4_callback_version1,
39962306a36Sopenharmony_ci	[4] = &nfs4_callback_version4,
40062306a36Sopenharmony_ci};
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic struct svc_stat nfs4_callback_stats;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic struct svc_program nfs4_callback_program = {
40562306a36Sopenharmony_ci	.pg_prog = NFS4_CALLBACK,			/* RPC service number */
40662306a36Sopenharmony_ci	.pg_nvers = ARRAY_SIZE(nfs4_callback_version),	/* Number of entries */
40762306a36Sopenharmony_ci	.pg_vers = nfs4_callback_version,		/* version table */
40862306a36Sopenharmony_ci	.pg_name = "NFSv4 callback",			/* service name */
40962306a36Sopenharmony_ci	.pg_class = "nfs",				/* authentication class */
41062306a36Sopenharmony_ci	.pg_stats = &nfs4_callback_stats,
41162306a36Sopenharmony_ci	.pg_authenticate = nfs_callback_authenticate,
41262306a36Sopenharmony_ci	.pg_init_request = svc_generic_init_request,
41362306a36Sopenharmony_ci	.pg_rpcbind_set	= svc_generic_rpcbind_set,
41462306a36Sopenharmony_ci};
415