Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * linux/fs/nfs/callback.c | |
4 | * | |
5 | * Copyright (C) 2004 Trond Myklebust | |
6 | * | |
7 | * NFSv4 callback handling | |
8 | */ | |
9 | ||
1da177e4 LT |
10 | #include <linux/completion.h> |
11 | #include <linux/ip.h> | |
12 | #include <linux/module.h> | |
3f07c014 | 13 | #include <linux/sched/signal.h> |
1da177e4 LT |
14 | #include <linux/sunrpc/svc.h> |
15 | #include <linux/sunrpc/svcsock.h> | |
16 | #include <linux/nfs_fs.h> | |
758201e2 | 17 | #include <linux/errno.h> |
353ab6e9 | 18 | #include <linux/mutex.h> |
83144186 | 19 | #include <linux/freezer.h> |
945b34a7 | 20 | #include <linux/sunrpc/svcauth_gss.h> |
a43cde94 | 21 | #include <linux/sunrpc/bc_xprt.h> |
14c85021 ACM |
22 | |
23 | #include <net/inet_sock.h> | |
24 | ||
4ce79717 | 25 | #include "nfs4_fs.h" |
1da177e4 | 26 | #include "callback.h" |
24c8dbbb | 27 | #include "internal.h" |
bbe0a3aa | 28 | #include "netns.h" |
1da177e4 LT |
29 | |
30 | #define NFSDBG_FACILITY NFSDBG_CALLBACK | |
31 | ||
32 | struct nfs_callback_data { | |
33 | unsigned int users; | |
a43cde94 | 34 | struct svc_serv *serv; |
1da177e4 LT |
35 | }; |
36 | ||
e82dc22d | 37 | static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; |
353ab6e9 | 38 | static DEFINE_MUTEX(nfs_callback_mutex); |
1da177e4 LT |
39 | static struct svc_program nfs4_callback_program; |
40 | ||
c946556b SK |
41 | static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net) |
42 | { | |
4df493a2 | 43 | const struct cred *cred = current_cred(); |
c946556b | 44 | int ret; |
bbe0a3aa | 45 | struct nfs_net *nn = net_generic(net, nfs_net_id); |
c946556b | 46 | |
352ad314 CL |
47 | ret = svc_xprt_create(serv, "tcp", net, PF_INET, |
48 | nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, | |
49 | cred); | |
c946556b SK |
50 | if (ret <= 0) |
51 | goto out_err; | |
bbe0a3aa | 52 | nn->nfs_callback_tcpport = ret; |
e4949e4b VA |
53 | dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", |
54 | nn->nfs_callback_tcpport, PF_INET, net->ns.inum); | |
c946556b | 55 | |
352ad314 CL |
56 | ret = svc_xprt_create(serv, "tcp", net, PF_INET6, |
57 | nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, | |
58 | cred); | |
c946556b | 59 | if (ret > 0) { |
29dcc16a | 60 | nn->nfs_callback_tcpport6 = ret; |
91bd2ffa | 61 | dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", |
e4949e4b | 62 | nn->nfs_callback_tcpport6, PF_INET6, net->ns.inum); |
c946556b SK |
63 | } else if (ret != -EAFNOSUPPORT) |
64 | goto out_err; | |
65 | return 0; | |
66 | ||
67 | out_err: | |
68 | return (ret) ? ret : -ENOMEM; | |
69 | } | |
70 | ||
1da177e4 | 71 | /* |
e82dc22d | 72 | * This is the NFSv4 callback kernel thread. |
1da177e4 | 73 | */ |
a277e33c | 74 | static int |
71468513 | 75 | nfs4_callback_svc(void *vrqstp) |
1da177e4 | 76 | { |
a277e33c | 77 | struct svc_rqst *rqstp = vrqstp; |
1da177e4 | 78 | |
83144186 | 79 | set_freezable(); |
1da177e4 | 80 | |
fa341560 | 81 | while (!svc_thread_should_stop(rqstp)) |
c743b425 | 82 | svc_recv(rqstp); |
f49169c9 | 83 | |
ed6473dd | 84 | svc_exit_thread(rqstp); |
a277e33c | 85 | return 0; |
1da177e4 LT |
86 | } |
87 | ||
a43cde94 | 88 | #if defined(CONFIG_NFS_V4_1) |
a43cde94 | 89 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, |
691c457a | 90 | struct svc_serv *serv) |
a43cde94 RL |
91 | { |
92 | if (minorversion) | |
691c457a SK |
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; | |
a43cde94 RL |
98 | } |
99 | #else | |
a43cde94 | 100 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, |
691c457a | 101 | struct svc_serv *serv) |
a43cde94 RL |
102 | { |
103 | } | |
104 | #endif /* CONFIG_NFS_V4_1 */ | |
105 | ||
8e246144 SK |
106 | static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, |
107 | struct svc_serv *serv) | |
108 | { | |
5405fc44 | 109 | int nrservs = nfs_callback_nr_threads; |
8e246144 | 110 | int ret; |
8e246144 SK |
111 | |
112 | nfs_callback_bc_serv(minorversion, xprt, serv); | |
113 | ||
5405fc44 TM |
114 | if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS) |
115 | nrservs = NFS4_MIN_NR_CALLBACK_THREADS; | |
116 | ||
ec52361d | 117 | if (serv->sv_nrthreads == nrservs) |
23c20ecd SK |
118 | return 0; |
119 | ||
3ebdbe52 | 120 | ret = svc_set_num_threads(serv, NULL, nrservs); |
bb6aeba7 | 121 | if (ret) { |
3ebdbe52 | 122 | svc_set_num_threads(serv, NULL, 0); |
e9b7e917 | 123 | return ret; |
8e246144 SK |
124 | } |
125 | dprintk("nfs_callback_up: service started\n"); | |
126 | return 0; | |
127 | } | |
128 | ||
b3d19c51 SK |
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, nfs_net_id); | |
132 | ||
133 | if (--nn->cb_users[minorversion]) | |
134 | return; | |
135 | ||
e4949e4b | 136 | dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum); |
c7d7ec8f | 137 | svc_xprt_destroy_all(serv, net); |
b3d19c51 SK |
138 | } |
139 | ||
76566773 CL |
140 | static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, |
141 | struct net *net, struct rpc_xprt *xprt) | |
c946556b | 142 | { |
b3d19c51 | 143 | struct nfs_net *nn = net_generic(net, nfs_net_id); |
c946556b SK |
144 | int ret; |
145 | ||
b3d19c51 SK |
146 | if (nn->cb_users[minorversion]++) |
147 | return 0; | |
148 | ||
e4949e4b | 149 | dprintk("NFS: create per-net callback data; net=%x\n", net->ns.inum); |
c946556b SK |
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 | ||
a289ce53 | 157 | ret = 0; |
d55b352b | 158 | if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0) |
76566773 | 159 | ret = nfs4_callback_up_net(serv, net); |
a289ce53 | 160 | else if (xprt->ops->bc_setup) |
0ad30ff6 | 161 | set_bc_enabled(serv); |
a289ce53 VA |
162 | else |
163 | ret = -EPROTONOSUPPORT; | |
c946556b SK |
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: | |
98b0f80c | 174 | nn->cb_users[minorversion]--; |
23c20ecd | 175 | dprintk("NFS: Couldn't create callback socket: err = %d; " |
e4949e4b | 176 | "net = %x\n", ret, net->ns.inum); |
c946556b SK |
177 | return ret; |
178 | } | |
179 | ||
dd018428 SK |
180 | static struct svc_serv *nfs_callback_create_svc(int minorversion) |
181 | { | |
182 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; | |
37902c63 | 183 | int (*threadfn)(void *data); |
dd018428 SK |
184 | struct svc_serv *serv; |
185 | ||
186 | /* | |
187 | * Check whether we're already up and running. | |
188 | */ | |
df5e49c8 | 189 | if (cb_info->serv) |
1e3577a4 | 190 | return cb_info->serv; |
dd018428 SK |
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 | ||
37902c63 | 200 | threadfn = nfs4_callback_svc; |
063ab935 | 201 | #if !defined(CONFIG_NFS_V4_1) |
37902c63 CL |
202 | if (minorversion) |
203 | return ERR_PTR(-ENOTSUPP); | |
204 | #endif | |
205 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, | |
206 | threadfn); | |
dd018428 SK |
207 | if (!serv) { |
208 | printk(KERN_ERR "nfs_callback_create_svc: create service failed\n"); | |
209 | return ERR_PTR(-ENOMEM); | |
210 | } | |
3b01c11e | 211 | cb_info->serv = serv; |
dd018428 SK |
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 | ||
71468513 BH |
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 | { | |
dd018428 | 225 | struct svc_serv *serv; |
e82dc22d | 226 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
23c20ecd | 227 | int ret; |
c946556b | 228 | struct net *net = xprt->xprt_net; |
71468513 BH |
229 | |
230 | mutex_lock(&nfs_callback_mutex); | |
dd018428 SK |
231 | |
232 | serv = nfs_callback_create_svc(minorversion); | |
233 | if (IS_ERR(serv)) { | |
234 | ret = PTR_ERR(serv); | |
235 | goto err_create; | |
236 | } | |
237 | ||
76566773 | 238 | ret = nfs_callback_up_net(minorversion, serv, net, xprt); |
c946556b SK |
239 | if (ret < 0) |
240 | goto err_net; | |
9793f7c8 | 241 | |
8e246144 SK |
242 | ret = nfs_callback_start_svc(minorversion, xprt, serv); |
243 | if (ret < 0) | |
244 | goto err_start; | |
a277e33c | 245 | |
23c20ecd | 246 | cb_info->users++; |
23c20ecd | 247 | err_net: |
1e3577a4 N |
248 | if (!cb_info->users) { |
249 | svc_set_num_threads(cb_info->serv, NULL, 0); | |
250 | svc_destroy(&cb_info->serv); | |
251 | } | |
dd018428 | 252 | err_create: |
353ab6e9 | 253 | mutex_unlock(&nfs_callback_mutex); |
1da177e4 | 254 | return ret; |
8e246144 SK |
255 | |
256 | err_start: | |
b3d19c51 | 257 | nfs_callback_down_net(minorversion, serv, net); |
23c20ecd SK |
258 | dprintk("NFS: Couldn't create server thread; err = %d\n", ret); |
259 | goto err_net; | |
1da177e4 LT |
260 | } |
261 | ||
262 | /* | |
5afc597c | 263 | * Kill the callback thread if it's no longer being used. |
1da177e4 | 264 | */ |
c8ceb412 | 265 | void nfs_callback_down(int minorversion, struct net *net) |
1da177e4 | 266 | { |
e82dc22d | 267 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
bb6aeba7 | 268 | struct svc_serv *serv; |
e82dc22d | 269 | |
353ab6e9 | 270 | mutex_lock(&nfs_callback_mutex); |
bb6aeba7 TM |
271 | serv = cb_info->serv; |
272 | nfs_callback_down_net(minorversion, serv, net); | |
e82dc22d | 273 | cb_info->users--; |
3b01c11e | 274 | if (cb_info->users == 0) { |
3ebdbe52 | 275 | svc_set_num_threads(serv, NULL, 0); |
1dc42e04 | 276 | dprintk("nfs_callback_down: service destroyed\n"); |
1e3577a4 | 277 | svc_destroy(&cb_info->serv); |
5afc597c | 278 | } |
353ab6e9 | 279 | mutex_unlock(&nfs_callback_mutex); |
1da177e4 LT |
280 | } |
281 | ||
778be232 AA |
282 | /* Boolean check of RPC_AUTH_GSS principal */ |
283 | int | |
284 | check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) | |
945b34a7 | 285 | { |
03a4e1f6 | 286 | char *p = rqstp->rq_cred.cr_principal; |
945b34a7 | 287 | |
778be232 AA |
288 | if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) |
289 | return 1; | |
290 | ||
ece0de63 AA |
291 | /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ |
292 | if (clp->cl_minorversion != 0) | |
778be232 | 293 | return 0; |
945b34a7 OK |
294 | /* |
295 | * It might just be a normal user principal, in which case | |
296 | * userspace won't bother to tell us the name at all. | |
297 | */ | |
298 | if (p == NULL) | |
778be232 | 299 | return 0; |
945b34a7 | 300 | |
f11b2a1c JL |
301 | /* |
302 | * Did we get the acceptor from userland during the SETCLIENID | |
303 | * negotiation? | |
304 | */ | |
305 | if (clp->cl_acceptor) | |
306 | return !strcmp(p, clp->cl_acceptor); | |
307 | ||
308 | /* | |
309 | * Otherwise try to verify it using the cl_hostname. Note that this | |
310 | * doesn't work if a non-canonical hostname was used in the devname. | |
311 | */ | |
312 | ||
945b34a7 OK |
313 | /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ |
314 | ||
315 | if (memcmp(p, "nfs@", 4) != 0) | |
778be232 | 316 | return 0; |
945b34a7 | 317 | p += 4; |
4e0038b6 | 318 | if (strcmp(p, clp->cl_hostname) != 0) |
778be232 AA |
319 | return 0; |
320 | return 1; | |
945b34a7 OK |
321 | } |
322 | ||
778be232 AA |
323 | /* |
324 | * pg_authenticate method for nfsv4 callback threads. | |
325 | * | |
326 | * The authflavor has been negotiated, so an incorrect flavor is a server | |
6f02dc88 | 327 | * bug. Deny packets with incorrect authflavor. |
778be232 AA |
328 | * |
329 | * All other checking done after NFS decoding where the nfs_client can be | |
330 | * found in nfs4_callback_compound | |
331 | */ | |
78c542f9 | 332 | static enum svc_auth_status nfs_callback_authenticate(struct svc_rqst *rqstp) |
1da177e4 | 333 | { |
5c2465df CL |
334 | rqstp->rq_auth_stat = rpc_autherr_badcred; |
335 | ||
1da177e4 | 336 | switch (rqstp->rq_authop->flavour) { |
778be232 AA |
337 | case RPC_AUTH_NULL: |
338 | if (rqstp->rq_proc != CB_NULL) | |
6f02dc88 | 339 | return SVC_DENIED; |
778be232 AA |
340 | break; |
341 | case RPC_AUTH_GSS: | |
342 | /* No RPC_AUTH_GSS support yet in NFSv4.1 */ | |
343 | if (svc_is_backchannel(rqstp)) | |
6f02dc88 | 344 | return SVC_DENIED; |
1da177e4 | 345 | } |
5c2465df CL |
346 | |
347 | rqstp->rq_auth_stat = rpc_auth_ok; | |
778be232 | 348 | return SVC_OK; |
1da177e4 LT |
349 | } |
350 | ||
351 | /* | |
352 | * Define NFS4 callback program | |
353 | */ | |
e9679189 | 354 | static const struct svc_version *nfs4_callback_version[] = { |
1da177e4 | 355 | [1] = &nfs4_callback_version1, |
07bccc2d | 356 | [4] = &nfs4_callback_version4, |
1da177e4 LT |
357 | }; |
358 | ||
359 | static struct svc_stat nfs4_callback_stats; | |
360 | ||
361 | static struct svc_program nfs4_callback_program = { | |
362 | .pg_prog = NFS4_CALLBACK, /* RPC service number */ | |
363 | .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ | |
364 | .pg_vers = nfs4_callback_version, /* version table */ | |
365 | .pg_name = "NFSv4 callback", /* service name */ | |
366 | .pg_class = "nfs", /* authentication class */ | |
367 | .pg_stats = &nfs4_callback_stats, | |
368 | .pg_authenticate = nfs_callback_authenticate, | |
8e5b6773 | 369 | .pg_init_request = svc_generic_init_request, |
642ee6b2 | 370 | .pg_rpcbind_set = svc_generic_rpcbind_set, |
1da177e4 | 371 | }; |