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/sunrpc/svcauth_gss.h> |
21 | #include <linux/sunrpc/bc_xprt.h> |
22 | |
23 | #include <net/inet_sock.h> |
24 | |
25 | #include "nfs4_fs.h" |
26 | #include "callback.h" |
27 | #include "internal.h" |
28 | #include "netns.h" |
29 | |
30 | #define NFSDBG_FACILITY NFSDBG_CALLBACK |
31 | |
32 | struct nfs_callback_data { |
33 | unsigned int users; |
34 | struct svc_serv *serv; |
35 | }; |
36 | |
37 | static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; |
38 | static DEFINE_MUTEX(nfs_callback_mutex); |
39 | static struct svc_program nfs4_callback_program; |
40 | |
41 | static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net) |
42 | { |
43 | const struct cred *cred = current_cred(); |
44 | int ret; |
45 | struct nfs_net *nn = net_generic(net, id: nfs_net_id); |
46 | |
47 | ret = svc_xprt_create(serv, xprt_name: "tcp" , net, PF_INET, |
48 | port: nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, |
49 | cred); |
50 | if (ret <= 0) |
51 | goto out_err; |
52 | nn->nfs_callback_tcpport = ret; |
53 | dprintk("NFS: Callback listener port = %u (af %u, net %x)\n" , |
54 | nn->nfs_callback_tcpport, PF_INET, net->ns.inum); |
55 | |
56 | ret = svc_xprt_create(serv, xprt_name: "tcp" , net, PF_INET6, |
57 | port: nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, |
58 | cred); |
59 | if (ret > 0) { |
60 | nn->nfs_callback_tcpport6 = ret; |
61 | dprintk("NFS: Callback listener port = %u (af %u, net %x)\n" , |
62 | nn->nfs_callback_tcpport6, PF_INET6, net->ns.inum); |
63 | } else if (ret != -EAFNOSUPPORT) |
64 | goto out_err; |
65 | return 0; |
66 | |
67 | out_err: |
68 | return (ret) ? ret : -ENOMEM; |
69 | } |
70 | |
71 | /* |
72 | * This is the NFSv4 callback kernel thread. |
73 | */ |
74 | static int |
75 | nfs4_callback_svc(void *vrqstp) |
76 | { |
77 | struct svc_rqst *rqstp = vrqstp; |
78 | |
79 | set_freezable(); |
80 | |
81 | while (!svc_thread_should_stop(rqstp)) |
82 | svc_recv(rqstp); |
83 | |
84 | svc_exit_thread(rqstp); |
85 | return 0; |
86 | } |
87 | |
88 | #if defined(CONFIG_NFS_V4_1) |
89 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, |
90 | struct svc_serv *serv) |
91 | { |
92 | if (minorversion) |
93 | /* |
94 | * Save the svc_serv in the transport so that it can |
95 | * be referenced when the session backchannel is initialized |
96 | */ |
97 | xprt->bc_serv = serv; |
98 | } |
99 | #else |
100 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, |
101 | struct svc_serv *serv) |
102 | { |
103 | } |
104 | #endif /* CONFIG_NFS_V4_1 */ |
105 | |
106 | static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, |
107 | struct svc_serv *serv) |
108 | { |
109 | int nrservs = nfs_callback_nr_threads; |
110 | int ret; |
111 | |
112 | nfs_callback_bc_serv(minorversion, xprt, serv); |
113 | |
114 | if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS) |
115 | nrservs = NFS4_MIN_NR_CALLBACK_THREADS; |
116 | |
117 | if (serv->sv_nrthreads == nrservs) |
118 | return 0; |
119 | |
120 | ret = svc_set_num_threads(serv, NULL, nrservs); |
121 | if (ret) { |
122 | svc_set_num_threads(serv, NULL, 0); |
123 | return ret; |
124 | } |
125 | dprintk("nfs_callback_up: service started\n" ); |
126 | return 0; |
127 | } |
128 | |
129 | static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net) |
130 | { |
131 | struct nfs_net *nn = net_generic(net, id: nfs_net_id); |
132 | |
133 | if (--nn->cb_users[minorversion]) |
134 | return; |
135 | |
136 | dprintk("NFS: destroy per-net callback data; net=%x\n" , net->ns.inum); |
137 | svc_xprt_destroy_all(serv, net); |
138 | } |
139 | |
140 | static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, |
141 | struct net *net, struct rpc_xprt *xprt) |
142 | { |
143 | struct nfs_net *nn = net_generic(net, id: nfs_net_id); |
144 | int ret; |
145 | |
146 | if (nn->cb_users[minorversion]++) |
147 | return 0; |
148 | |
149 | dprintk("NFS: create per-net callback data; net=%x\n" , net->ns.inum); |
150 | |
151 | ret = svc_bind(serv, net); |
152 | if (ret < 0) { |
153 | printk(KERN_WARNING "NFS: bind callback service failed\n" ); |
154 | goto err_bind; |
155 | } |
156 | |
157 | ret = 0; |
158 | if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0) |
159 | ret = nfs4_callback_up_net(serv, net); |
160 | else if (xprt->ops->bc_setup) |
161 | set_bc_enabled(serv); |
162 | else |
163 | ret = -EPROTONOSUPPORT; |
164 | |
165 | if (ret < 0) { |
166 | printk(KERN_ERR "NFS: callback service start failed\n" ); |
167 | goto err_socks; |
168 | } |
169 | return 0; |
170 | |
171 | err_socks: |
172 | svc_rpcb_cleanup(serv, net); |
173 | err_bind: |
174 | nn->cb_users[minorversion]--; |
175 | dprintk("NFS: Couldn't create callback socket: err = %d; " |
176 | "net = %x\n" , ret, net->ns.inum); |
177 | return ret; |
178 | } |
179 | |
180 | static struct svc_serv *nfs_callback_create_svc(int minorversion) |
181 | { |
182 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
183 | int (*threadfn)(void *data); |
184 | struct svc_serv *serv; |
185 | |
186 | /* |
187 | * Check whether we're already up and running. |
188 | */ |
189 | if (cb_info->serv) |
190 | return svc_get(serv: cb_info->serv); |
191 | |
192 | /* |
193 | * Sanity check: if there's no task, |
194 | * we should be the first user ... |
195 | */ |
196 | if (cb_info->users) |
197 | printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n" , |
198 | cb_info->users); |
199 | |
200 | threadfn = nfs4_callback_svc; |
201 | #if !defined(CONFIG_NFS_V4_1) |
202 | if (minorversion) |
203 | return ERR_PTR(-ENOTSUPP); |
204 | #endif |
205 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, |
206 | threadfn); |
207 | if (!serv) { |
208 | printk(KERN_ERR "nfs_callback_create_svc: create service failed\n" ); |
209 | return ERR_PTR(error: -ENOMEM); |
210 | } |
211 | cb_info->serv = serv; |
212 | /* As there is only one thread we need to over-ride the |
213 | * default maximum of 80 connections |
214 | */ |
215 | serv->sv_maxconn = 1024; |
216 | dprintk("nfs_callback_create_svc: service created\n" ); |
217 | return serv; |
218 | } |
219 | |
220 | /* |
221 | * Bring up the callback thread if it is not already up. |
222 | */ |
223 | int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) |
224 | { |
225 | struct svc_serv *serv; |
226 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
227 | int ret; |
228 | struct net *net = xprt->xprt_net; |
229 | |
230 | mutex_lock(&nfs_callback_mutex); |
231 | |
232 | serv = nfs_callback_create_svc(minorversion); |
233 | if (IS_ERR(ptr: serv)) { |
234 | ret = PTR_ERR(ptr: serv); |
235 | goto err_create; |
236 | } |
237 | |
238 | ret = nfs_callback_up_net(minorversion, serv, net, xprt); |
239 | if (ret < 0) |
240 | goto err_net; |
241 | |
242 | ret = nfs_callback_start_svc(minorversion, xprt, serv); |
243 | if (ret < 0) |
244 | goto err_start; |
245 | |
246 | cb_info->users++; |
247 | err_net: |
248 | if (!cb_info->users) |
249 | cb_info->serv = NULL; |
250 | svc_put(serv); |
251 | err_create: |
252 | mutex_unlock(lock: &nfs_callback_mutex); |
253 | return ret; |
254 | |
255 | err_start: |
256 | nfs_callback_down_net(minorversion, serv, net); |
257 | dprintk("NFS: Couldn't create server thread; err = %d\n" , ret); |
258 | goto err_net; |
259 | } |
260 | |
261 | /* |
262 | * Kill the callback thread if it's no longer being used. |
263 | */ |
264 | void nfs_callback_down(int minorversion, struct net *net) |
265 | { |
266 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
267 | struct svc_serv *serv; |
268 | |
269 | mutex_lock(&nfs_callback_mutex); |
270 | serv = cb_info->serv; |
271 | nfs_callback_down_net(minorversion, serv, net); |
272 | cb_info->users--; |
273 | if (cb_info->users == 0) { |
274 | svc_get(serv); |
275 | svc_set_num_threads(serv, NULL, 0); |
276 | svc_put(serv); |
277 | dprintk("nfs_callback_down: service destroyed\n" ); |
278 | cb_info->serv = NULL; |
279 | } |
280 | mutex_unlock(lock: &nfs_callback_mutex); |
281 | } |
282 | |
283 | /* Boolean check of RPC_AUTH_GSS principal */ |
284 | int |
285 | check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) |
286 | { |
287 | char *p = rqstp->rq_cred.cr_principal; |
288 | |
289 | if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) |
290 | return 1; |
291 | |
292 | /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ |
293 | if (clp->cl_minorversion != 0) |
294 | return 0; |
295 | /* |
296 | * It might just be a normal user principal, in which case |
297 | * userspace won't bother to tell us the name at all. |
298 | */ |
299 | if (p == NULL) |
300 | return 0; |
301 | |
302 | /* |
303 | * Did we get the acceptor from userland during the SETCLIENID |
304 | * negotiation? |
305 | */ |
306 | if (clp->cl_acceptor) |
307 | return !strcmp(p, clp->cl_acceptor); |
308 | |
309 | /* |
310 | * Otherwise try to verify it using the cl_hostname. Note that this |
311 | * doesn't work if a non-canonical hostname was used in the devname. |
312 | */ |
313 | |
314 | /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ |
315 | |
316 | if (memcmp(p, q: "nfs@" , size: 4) != 0) |
317 | return 0; |
318 | p += 4; |
319 | if (strcmp(p, clp->cl_hostname) != 0) |
320 | return 0; |
321 | return 1; |
322 | } |
323 | |
324 | /* |
325 | * pg_authenticate method for nfsv4 callback threads. |
326 | * |
327 | * The authflavor has been negotiated, so an incorrect flavor is a server |
328 | * bug. Deny packets with incorrect authflavor. |
329 | * |
330 | * All other checking done after NFS decoding where the nfs_client can be |
331 | * found in nfs4_callback_compound |
332 | */ |
333 | static enum svc_auth_status nfs_callback_authenticate(struct svc_rqst *rqstp) |
334 | { |
335 | rqstp->rq_auth_stat = rpc_autherr_badcred; |
336 | |
337 | switch (rqstp->rq_authop->flavour) { |
338 | case RPC_AUTH_NULL: |
339 | if (rqstp->rq_proc != CB_NULL) |
340 | return SVC_DENIED; |
341 | break; |
342 | case RPC_AUTH_GSS: |
343 | /* No RPC_AUTH_GSS support yet in NFSv4.1 */ |
344 | if (svc_is_backchannel(rqstp)) |
345 | return SVC_DENIED; |
346 | } |
347 | |
348 | rqstp->rq_auth_stat = rpc_auth_ok; |
349 | return SVC_OK; |
350 | } |
351 | |
352 | /* |
353 | * Define NFS4 callback program |
354 | */ |
355 | static const struct svc_version *nfs4_callback_version[] = { |
356 | [1] = &nfs4_callback_version1, |
357 | [4] = &nfs4_callback_version4, |
358 | }; |
359 | |
360 | static struct svc_stat nfs4_callback_stats; |
361 | |
362 | static struct svc_program nfs4_callback_program = { |
363 | .pg_prog = NFS4_CALLBACK, /* RPC service number */ |
364 | .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ |
365 | .pg_vers = nfs4_callback_version, /* version table */ |
366 | .pg_name = "NFSv4 callback" , /* service name */ |
367 | .pg_class = "nfs" , /* authentication class */ |
368 | .pg_stats = &nfs4_callback_stats, |
369 | .pg_authenticate = nfs_callback_authenticate, |
370 | .pg_init_request = svc_generic_init_request, |
371 | .pg_rpcbind_set = svc_generic_rpcbind_set, |
372 | }; |
373 | |