Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * linux/fs/nfs/callback.c
4 : : *
5 : : * Copyright (C) 2004 Trond Myklebust
6 : : *
7 : : * NFSv4 callback handling
8 : : */
9 : :
10 : : #include <linux/completion.h>
11 : : #include <linux/ip.h>
12 : : #include <linux/module.h>
13 : : #include <linux/sched/signal.h>
14 : : #include <linux/sunrpc/svc.h>
15 : : #include <linux/sunrpc/svcsock.h>
16 : : #include <linux/nfs_fs.h>
17 : : #include <linux/errno.h>
18 : : #include <linux/mutex.h>
19 : : #include <linux/freezer.h>
20 : : #include <linux/kthread.h>
21 : : #include <linux/sunrpc/svcauth_gss.h>
22 : : #include <linux/sunrpc/bc_xprt.h>
23 : :
24 : : #include <net/inet_sock.h>
25 : :
26 : : #include "nfs4_fs.h"
27 : : #include "callback.h"
28 : : #include "internal.h"
29 : : #include "netns.h"
30 : :
31 : : #define NFSDBG_FACILITY NFSDBG_CALLBACK
32 : :
33 : : struct nfs_callback_data {
34 : : unsigned int users;
35 : : struct svc_serv *serv;
36 : : };
37 : :
38 : : static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
39 : : static DEFINE_MUTEX(nfs_callback_mutex);
40 : : static struct svc_program nfs4_callback_program;
41 : :
42 : 0 : static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
43 : : {
44 : 0 : const struct cred *cred = current_cred();
45 : : int ret;
46 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
47 : :
48 : 0 : ret = svc_create_xprt(serv, "tcp", net, PF_INET,
49 : : nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
50 : : cred);
51 [ # # ]: 0 : if (ret <= 0)
52 : : goto out_err;
53 : 0 : nn->nfs_callback_tcpport = ret;
54 : : dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
55 : : nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
56 : :
57 : 0 : ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
58 : : nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
59 : : cred);
60 [ # # ]: 0 : if (ret > 0) {
61 : 0 : nn->nfs_callback_tcpport6 = ret;
62 : : dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
63 : : nn->nfs_callback_tcpport6, PF_INET6, net->ns.inum);
64 [ # # ]: 0 : } else if (ret != -EAFNOSUPPORT)
65 : : goto out_err;
66 : : return 0;
67 : :
68 : : out_err:
69 [ # # ]: 0 : return (ret) ? ret : -ENOMEM;
70 : : }
71 : :
72 : : /*
73 : : * This is the NFSv4 callback kernel thread.
74 : : */
75 : : static int
76 : 0 : nfs4_callback_svc(void *vrqstp)
77 : : {
78 : : int err;
79 : : struct svc_rqst *rqstp = vrqstp;
80 : :
81 : 0 : set_freezable();
82 : :
83 [ # # ]: 0 : while (!kthread_freezable_should_stop(NULL)) {
84 : :
85 [ # # ]: 0 : if (signal_pending(current))
86 : 0 : flush_signals(current);
87 : : /*
88 : : * Listen for a request on the socket
89 : : */
90 : 0 : err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
91 [ # # ]: 0 : if (err == -EAGAIN || err == -EINTR)
92 : 0 : continue;
93 : 0 : svc_process(rqstp);
94 : : }
95 : 0 : svc_exit_thread(rqstp);
96 : 0 : module_put_and_exit(0);
97 : : return 0;
98 : : }
99 : :
100 : : #if defined(CONFIG_NFS_V4_1)
101 : : /*
102 : : * The callback service for NFSv4.1 callbacks
103 : : */
104 : : static int
105 : 0 : nfs41_callback_svc(void *vrqstp)
106 : : {
107 : : struct svc_rqst *rqstp = vrqstp;
108 : 0 : struct svc_serv *serv = rqstp->rq_server;
109 : : struct rpc_rqst *req;
110 : : int error;
111 : 0 : DEFINE_WAIT(wq);
112 : :
113 : 0 : set_freezable();
114 : :
115 [ # # ]: 0 : while (!kthread_freezable_should_stop(NULL)) {
116 : :
117 [ # # ]: 0 : if (signal_pending(current))
118 : 0 : flush_signals(current);
119 : :
120 : 0 : prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
121 : : spin_lock_bh(&serv->sv_cb_lock);
122 [ # # ]: 0 : if (!list_empty(&serv->sv_cb_list)) {
123 : 0 : req = list_first_entry(&serv->sv_cb_list,
124 : : struct rpc_rqst, rq_bc_list);
125 : : list_del(&req->rq_bc_list);
126 : : spin_unlock_bh(&serv->sv_cb_lock);
127 : 0 : finish_wait(&serv->sv_cb_waitq, &wq);
128 : : dprintk("Invoking bc_svc_process()\n");
129 : 0 : error = bc_svc_process(serv, req, rqstp);
130 : : dprintk("bc_svc_process() returned w/ error code= %d\n",
131 : : error);
132 : : } else {
133 : : spin_unlock_bh(&serv->sv_cb_lock);
134 [ # # ]: 0 : if (!kthread_should_stop())
135 : 0 : schedule();
136 : 0 : finish_wait(&serv->sv_cb_waitq, &wq);
137 : : }
138 : : }
139 : 0 : svc_exit_thread(rqstp);
140 : 0 : module_put_and_exit(0);
141 : : return 0;
142 : : }
143 : :
144 : : static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
145 : : struct svc_serv *serv)
146 : : {
147 [ # # ]: 0 : if (minorversion)
148 : : /*
149 : : * Save the svc_serv in the transport so that it can
150 : : * be referenced when the session backchannel is initialized
151 : : */
152 : 0 : xprt->bc_serv = serv;
153 : : }
154 : : #else
155 : : static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
156 : : struct svc_serv *serv)
157 : : {
158 : : }
159 : : #endif /* CONFIG_NFS_V4_1 */
160 : :
161 : 0 : static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
162 : : struct svc_serv *serv)
163 : : {
164 : 0 : int nrservs = nfs_callback_nr_threads;
165 : : int ret;
166 : :
167 : : nfs_callback_bc_serv(minorversion, xprt, serv);
168 : :
169 [ # # ]: 0 : if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
170 : : nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
171 : :
172 [ # # ]: 0 : if (serv->sv_nrthreads-1 == nrservs)
173 : : return 0;
174 : :
175 : 0 : ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
176 [ # # ]: 0 : if (ret) {
177 : 0 : serv->sv_ops->svo_setup(serv, NULL, 0);
178 : 0 : return ret;
179 : : }
180 : : dprintk("nfs_callback_up: service started\n");
181 : : return 0;
182 : : }
183 : :
184 : 0 : static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
185 : : {
186 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
187 : :
188 [ # # ]: 0 : if (--nn->cb_users[minorversion])
189 : 0 : return;
190 : :
191 : : dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
192 : 0 : svc_shutdown_net(serv, net);
193 : : }
194 : :
195 : 0 : static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
196 : : struct net *net, struct rpc_xprt *xprt)
197 : : {
198 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
199 : : int ret;
200 : :
201 [ # # ]: 0 : if (nn->cb_users[minorversion]++)
202 : : return 0;
203 : :
204 : : dprintk("NFS: create per-net callback data; net=%x\n", net->ns.inum);
205 : :
206 : 0 : ret = svc_bind(serv, net);
207 [ # # ]: 0 : if (ret < 0) {
208 : 0 : printk(KERN_WARNING "NFS: bind callback service failed\n");
209 : 0 : goto err_bind;
210 : : }
211 : :
212 : : ret = 0;
213 [ # # ]: 0 : if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0)
214 : 0 : ret = nfs4_callback_up_net(serv, net);
215 [ # # ]: 0 : else if (xprt->ops->bc_setup)
216 : : set_bc_enabled(serv);
217 : : else
218 : : ret = -EPROTONOSUPPORT;
219 : :
220 [ # # ]: 0 : if (ret < 0) {
221 : 0 : printk(KERN_ERR "NFS: callback service start failed\n");
222 : : goto err_socks;
223 : : }
224 : : return 0;
225 : :
226 : : err_socks:
227 : 0 : svc_rpcb_cleanup(serv, net);
228 : : err_bind:
229 : 0 : nn->cb_users[minorversion]--;
230 : : dprintk("NFS: Couldn't create callback socket: err = %d; "
231 : : "net = %x\n", ret, net->ns.inum);
232 : 0 : return ret;
233 : : }
234 : :
235 : : static const struct svc_serv_ops nfs40_cb_sv_ops = {
236 : : .svo_function = nfs4_callback_svc,
237 : : .svo_enqueue_xprt = svc_xprt_do_enqueue,
238 : : .svo_setup = svc_set_num_threads_sync,
239 : : .svo_module = THIS_MODULE,
240 : : };
241 : : #if defined(CONFIG_NFS_V4_1)
242 : : static const struct svc_serv_ops nfs41_cb_sv_ops = {
243 : : .svo_function = nfs41_callback_svc,
244 : : .svo_enqueue_xprt = svc_xprt_do_enqueue,
245 : : .svo_setup = svc_set_num_threads_sync,
246 : : .svo_module = THIS_MODULE,
247 : : };
248 : :
249 : : static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
250 : : [0] = &nfs40_cb_sv_ops,
251 : : [1] = &nfs41_cb_sv_ops,
252 : : };
253 : : #else
254 : : static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
255 : : [0] = &nfs40_cb_sv_ops,
256 : : [1] = NULL,
257 : : };
258 : : #endif
259 : :
260 : 0 : static struct svc_serv *nfs_callback_create_svc(int minorversion)
261 : : {
262 : : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
263 : : const struct svc_serv_ops *sv_ops;
264 : : struct svc_serv *serv;
265 : :
266 : : /*
267 : : * Check whether we're already up and running.
268 : : */
269 [ # # ]: 0 : if (cb_info->serv) {
270 : : /*
271 : : * Note: increase service usage, because later in case of error
272 : : * svc_destroy() will be called.
273 : : */
274 : : svc_get(cb_info->serv);
275 : 0 : return cb_info->serv;
276 : : }
277 : :
278 [ # # ]: 0 : switch (minorversion) {
279 : : case 0:
280 : 0 : sv_ops = nfs4_cb_sv_ops[0];
281 : 0 : break;
282 : : default:
283 : 0 : sv_ops = nfs4_cb_sv_ops[1];
284 : : }
285 : :
286 [ # # ]: 0 : if (sv_ops == NULL)
287 : : return ERR_PTR(-ENOTSUPP);
288 : :
289 : : /*
290 : : * Sanity check: if there's no task,
291 : : * we should be the first user ...
292 : : */
293 [ # # ]: 0 : if (cb_info->users)
294 : 0 : printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
295 : : cb_info->users);
296 : :
297 : 0 : serv = svc_create_pooled(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
298 [ # # ]: 0 : if (!serv) {
299 : 0 : printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
300 : 0 : return ERR_PTR(-ENOMEM);
301 : : }
302 : 0 : cb_info->serv = serv;
303 : : /* As there is only one thread we need to over-ride the
304 : : * default maximum of 80 connections
305 : : */
306 : 0 : serv->sv_maxconn = 1024;
307 : : dprintk("nfs_callback_create_svc: service created\n");
308 : 0 : return serv;
309 : : }
310 : :
311 : : /*
312 : : * Bring up the callback thread if it is not already up.
313 : : */
314 : 0 : int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
315 : : {
316 : : struct svc_serv *serv;
317 : : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
318 : : int ret;
319 : 0 : struct net *net = xprt->xprt_net;
320 : :
321 : 0 : mutex_lock(&nfs_callback_mutex);
322 : :
323 : 0 : serv = nfs_callback_create_svc(minorversion);
324 [ # # ]: 0 : if (IS_ERR(serv)) {
325 : : ret = PTR_ERR(serv);
326 : 0 : goto err_create;
327 : : }
328 : :
329 : 0 : ret = nfs_callback_up_net(minorversion, serv, net, xprt);
330 [ # # ]: 0 : if (ret < 0)
331 : : goto err_net;
332 : :
333 : 0 : ret = nfs_callback_start_svc(minorversion, xprt, serv);
334 [ # # ]: 0 : if (ret < 0)
335 : : goto err_start;
336 : :
337 : 0 : cb_info->users++;
338 : : /*
339 : : * svc_create creates the svc_serv with sv_nrthreads == 1, and then
340 : : * svc_prepare_thread increments that. So we need to call svc_destroy
341 : : * on both success and failure so that the refcount is 1 when the
342 : : * thread exits.
343 : : */
344 : : err_net:
345 [ # # ]: 0 : if (!cb_info->users)
346 : 0 : cb_info->serv = NULL;
347 : 0 : svc_destroy(serv);
348 : : err_create:
349 : 0 : mutex_unlock(&nfs_callback_mutex);
350 : 0 : return ret;
351 : :
352 : : err_start:
353 : 0 : nfs_callback_down_net(minorversion, serv, net);
354 : : dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
355 : 0 : goto err_net;
356 : : }
357 : :
358 : : /*
359 : : * Kill the callback thread if it's no longer being used.
360 : : */
361 : 0 : void nfs_callback_down(int minorversion, struct net *net)
362 : : {
363 : : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
364 : : struct svc_serv *serv;
365 : :
366 : 0 : mutex_lock(&nfs_callback_mutex);
367 : 0 : serv = cb_info->serv;
368 : 0 : nfs_callback_down_net(minorversion, serv, net);
369 : 0 : cb_info->users--;
370 [ # # ]: 0 : if (cb_info->users == 0) {
371 : : svc_get(serv);
372 : 0 : serv->sv_ops->svo_setup(serv, NULL, 0);
373 : 0 : svc_destroy(serv);
374 : : dprintk("nfs_callback_down: service destroyed\n");
375 : 0 : cb_info->serv = NULL;
376 : : }
377 : 0 : mutex_unlock(&nfs_callback_mutex);
378 : 0 : }
379 : :
380 : : /* Boolean check of RPC_AUTH_GSS principal */
381 : : int
382 : 0 : check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
383 : : {
384 : 0 : char *p = rqstp->rq_cred.cr_principal;
385 : :
386 [ # # ]: 0 : if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
387 : : return 1;
388 : :
389 : : /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
390 [ # # ]: 0 : if (clp->cl_minorversion != 0)
391 : : return 0;
392 : : /*
393 : : * It might just be a normal user principal, in which case
394 : : * userspace won't bother to tell us the name at all.
395 : : */
396 [ # # ]: 0 : if (p == NULL)
397 : : return 0;
398 : :
399 : : /*
400 : : * Did we get the acceptor from userland during the SETCLIENID
401 : : * negotiation?
402 : : */
403 [ # # ]: 0 : if (clp->cl_acceptor)
404 : 0 : return !strcmp(p, clp->cl_acceptor);
405 : :
406 : : /*
407 : : * Otherwise try to verify it using the cl_hostname. Note that this
408 : : * doesn't work if a non-canonical hostname was used in the devname.
409 : : */
410 : :
411 : : /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
412 : :
413 [ # # ]: 0 : if (memcmp(p, "nfs@", 4) != 0)
414 : : return 0;
415 : 0 : p += 4;
416 [ # # ]: 0 : if (strcmp(p, clp->cl_hostname) != 0)
417 : : return 0;
418 : 0 : return 1;
419 : : }
420 : :
421 : : /*
422 : : * pg_authenticate method for nfsv4 callback threads.
423 : : *
424 : : * The authflavor has been negotiated, so an incorrect flavor is a server
425 : : * bug. Deny packets with incorrect authflavor.
426 : : *
427 : : * All other checking done after NFS decoding where the nfs_client can be
428 : : * found in nfs4_callback_compound
429 : : */
430 : 0 : static int nfs_callback_authenticate(struct svc_rqst *rqstp)
431 : : {
432 [ # # # ]: 0 : switch (rqstp->rq_authop->flavour) {
433 : : case RPC_AUTH_NULL:
434 [ # # ]: 0 : if (rqstp->rq_proc != CB_NULL)
435 : : return SVC_DENIED;
436 : : break;
437 : : case RPC_AUTH_GSS:
438 : : /* No RPC_AUTH_GSS support yet in NFSv4.1 */
439 [ # # ]: 0 : if (svc_is_backchannel(rqstp))
440 : : return SVC_DENIED;
441 : : }
442 : 0 : return SVC_OK;
443 : : }
444 : :
445 : : /*
446 : : * Define NFS4 callback program
447 : : */
448 : : static const struct svc_version *nfs4_callback_version[] = {
449 : : [1] = &nfs4_callback_version1,
450 : : [4] = &nfs4_callback_version4,
451 : : };
452 : :
453 : : static struct svc_stat nfs4_callback_stats;
454 : :
455 : : static struct svc_program nfs4_callback_program = {
456 : : .pg_prog = NFS4_CALLBACK, /* RPC service number */
457 : : .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */
458 : : .pg_vers = nfs4_callback_version, /* version table */
459 : : .pg_name = "NFSv4 callback", /* service name */
460 : : .pg_class = "nfs", /* authentication class */
461 : : .pg_stats = &nfs4_callback_stats,
462 : : .pg_authenticate = nfs_callback_authenticate,
463 : : .pg_init_request = svc_generic_init_request,
464 : : .pg_rpcbind_set = svc_generic_rpcbind_set,
465 : : };
|