1 | /* $NetBSD: nfs_export.c,v 1.59 2016/11/20 09:28:43 maxv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1997, 1998, 2004, 2005, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. |
10 | * This code is derived from software contributed to The NetBSD Foundation |
11 | * by Charles M. Hannum. |
12 | * This code is derived from software contributed to The NetBSD Foundation |
13 | * by Julio M. Merino Vidal. |
14 | * |
15 | * Redistribution and use in source and binary forms, with or without |
16 | * modification, are permitted provided that the following conditions |
17 | * are met: |
18 | * 1. Redistributions of source code must retain the above copyright |
19 | * notice, this list of conditions and the following disclaimer. |
20 | * 2. Redistributions in binary form must reproduce the above copyright |
21 | * notice, this list of conditions and the following disclaimer in the |
22 | * documentation and/or other materials provided with the distribution. |
23 | * |
24 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
25 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
26 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
27 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
34 | * POSSIBILITY OF SUCH DAMAGE. |
35 | */ |
36 | |
37 | /* |
38 | * Copyright (c) 1989, 1993 |
39 | * The Regents of the University of California. All rights reserved. |
40 | * (c) UNIX System Laboratories, Inc. |
41 | * All or some portions of this file are derived from material licensed |
42 | * to the University of California by American Telephone and Telegraph |
43 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
44 | * the permission of UNIX System Laboratories, Inc. |
45 | * |
46 | * Redistribution and use in source and binary forms, with or without |
47 | * modification, are permitted provided that the following conditions |
48 | * are met: |
49 | * 1. Redistributions of source code must retain the above copyright |
50 | * notice, this list of conditions and the following disclaimer. |
51 | * 2. Redistributions in binary form must reproduce the above copyright |
52 | * notice, this list of conditions and the following disclaimer in the |
53 | * documentation and/or other materials provided with the distribution. |
54 | * 3. Neither the name of the University nor the names of its contributors |
55 | * may be used to endorse or promote products derived from this software |
56 | * without specific prior written permission. |
57 | * |
58 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
59 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
61 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
62 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
63 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
64 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
65 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
66 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
67 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
68 | * SUCH DAMAGE. |
69 | * |
70 | * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94 |
71 | */ |
72 | |
73 | /* |
74 | * VFS exports list management. |
75 | * |
76 | * Lock order: vfs_busy -> mnt_updating -> netexport_lock. |
77 | */ |
78 | |
79 | #include <sys/cdefs.h> |
80 | __KERNEL_RCSID(0, "$NetBSD: nfs_export.c,v 1.59 2016/11/20 09:28:43 maxv Exp $" ); |
81 | |
82 | #include <sys/param.h> |
83 | #include <sys/systm.h> |
84 | #include <sys/kernel.h> |
85 | #include <sys/queue.h> |
86 | #include <sys/proc.h> |
87 | #include <sys/mount.h> |
88 | #include <sys/vnode.h> |
89 | #include <sys/namei.h> |
90 | #include <sys/errno.h> |
91 | #include <sys/malloc.h> |
92 | #include <sys/domain.h> |
93 | #include <sys/mbuf.h> |
94 | #include <sys/dirent.h> |
95 | #include <sys/socket.h> /* XXX for AF_MAX */ |
96 | #include <sys/kauth.h> |
97 | |
98 | #include <net/radix.h> |
99 | |
100 | #include <netinet/in.h> |
101 | |
102 | #include <nfs/rpcv2.h> |
103 | #include <nfs/nfsproto.h> |
104 | #include <nfs/nfs.h> |
105 | #include <nfs/nfs_var.h> |
106 | |
107 | /* |
108 | * Network address lookup element. |
109 | */ |
110 | struct netcred { |
111 | struct radix_node netc_rnodes[2]; |
112 | int netc_refcnt; |
113 | int netc_exflags; |
114 | kauth_cred_t netc_anon; |
115 | }; |
116 | |
117 | /* |
118 | * Network export information. |
119 | */ |
120 | struct netexport { |
121 | TAILQ_ENTRY(netexport) ne_list; |
122 | struct mount *ne_mount; |
123 | struct netcred ne_defexported; /* Default export */ |
124 | struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ |
125 | }; |
126 | TAILQ_HEAD(, netexport) netexport_list = |
127 | TAILQ_HEAD_INITIALIZER(netexport_list); |
128 | |
129 | /* Publicly exported file system. */ |
130 | struct nfs_public nfs_pub; |
131 | |
132 | /* |
133 | * Local prototypes. |
134 | */ |
135 | static int init_exports(struct mount *, struct netexport **); |
136 | static int hang_addrlist(struct mount *, struct netexport *, |
137 | const struct export_args *); |
138 | static int sacheck(struct sockaddr *); |
139 | static int free_netcred(struct radix_node *, void *); |
140 | static int export(struct netexport *, const struct export_args *); |
141 | static int setpublicfs(struct mount *, struct netexport *, |
142 | const struct export_args *); |
143 | static struct netcred *netcred_lookup(struct netexport *, struct mbuf *); |
144 | static struct netexport *netexport_lookup(const struct mount *); |
145 | static struct netexport *netexport_lookup_byfsid(const fsid_t *); |
146 | static void netexport_clear(struct netexport *); |
147 | static void netexport_insert(struct netexport *); |
148 | static void netexport_remove(struct netexport *); |
149 | static void netexport_wrlock(void); |
150 | static void netexport_wrunlock(void); |
151 | static int nfs_export_update_30(struct mount *mp, const char *path, void *); |
152 | |
153 | static krwlock_t netexport_lock; |
154 | |
155 | /* |
156 | * PUBLIC INTERFACE |
157 | */ |
158 | |
159 | /* |
160 | * Declare and initialize the file system export hooks. |
161 | */ |
162 | static void netexport_unmount(struct mount *); |
163 | |
164 | struct vfs_hooks nfs_export_hooks = { |
165 | { NULL, NULL }, |
166 | .vh_unmount = netexport_unmount, |
167 | .vh_reexport = nfs_export_update_30, |
168 | }; |
169 | |
170 | /* |
171 | * VFS unmount hook for NFS exports. |
172 | * |
173 | * Releases NFS exports list resources if the given mount point has some. |
174 | * As allocation happens lazily, it may be that it doesn't have this |
175 | * information, although it theoretically should. |
176 | */ |
177 | static void |
178 | netexport_unmount(struct mount *mp) |
179 | { |
180 | struct netexport *ne; |
181 | |
182 | KASSERT(mp != NULL); |
183 | |
184 | netexport_wrlock(); |
185 | ne = netexport_lookup(mp); |
186 | if (ne == NULL) { |
187 | netexport_wrunlock(); |
188 | return; |
189 | } |
190 | netexport_clear(ne); |
191 | netexport_remove(ne); |
192 | netexport_wrunlock(); |
193 | kmem_free(ne, sizeof(*ne)); |
194 | } |
195 | |
196 | void |
197 | netexport_init(void) |
198 | { |
199 | |
200 | rw_init(&netexport_lock); |
201 | } |
202 | |
203 | void |
204 | netexport_fini(void) |
205 | { |
206 | struct netexport *ne; |
207 | struct mount *mp; |
208 | int error; |
209 | |
210 | while (!TAILQ_EMPTY(&netexport_list)) { |
211 | netexport_wrlock(); |
212 | ne = TAILQ_FIRST(&netexport_list); |
213 | mp = ne->ne_mount; |
214 | error = vfs_busy(mp, NULL); |
215 | netexport_wrunlock(); |
216 | if (error != 0) { |
217 | kpause("nfsfini" , false, hz, NULL); |
218 | continue; |
219 | } |
220 | mutex_enter(&mp->mnt_updating); /* mnt_flag */ |
221 | netexport_unmount(mp); |
222 | mutex_exit(&mp->mnt_updating); /* mnt_flag */ |
223 | vfs_unbusy(mp, false, NULL); |
224 | } |
225 | rw_destroy(&netexport_lock); |
226 | } |
227 | |
228 | |
229 | /* |
230 | * Atomically set the NFS exports list of the given file system, replacing |
231 | * it with a new list of entries. |
232 | * |
233 | * Returns zero on success or an appropriate error code otherwise. |
234 | * |
235 | * Helper function for the nfssvc(2) system call (NFSSVC_SETEXPORTSLIST |
236 | * command). |
237 | */ |
238 | int |
239 | mountd_set_exports_list(const struct mountd_exports_list *mel, struct lwp *l, |
240 | struct mount *nmp) |
241 | { |
242 | int error; |
243 | #ifdef notyet |
244 | /* XXX: See below to see the reason why this is disabled. */ |
245 | size_t i; |
246 | #endif |
247 | struct mount *mp; |
248 | struct netexport *ne; |
249 | struct pathbuf *pb; |
250 | struct nameidata nd; |
251 | struct vnode *vp; |
252 | size_t fid_size; |
253 | |
254 | if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_NFS, |
255 | KAUTH_REQ_NETWORK_NFS_EXPORT, NULL, NULL, NULL) != 0) |
256 | return EPERM; |
257 | |
258 | /* Look up the file system path. */ |
259 | error = pathbuf_copyin(mel->mel_path, &pb); |
260 | if (error) { |
261 | return error; |
262 | } |
263 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pb); |
264 | error = namei(&nd); |
265 | if (error != 0) { |
266 | pathbuf_destroy(pb); |
267 | return error; |
268 | } |
269 | vp = nd.ni_vp; |
270 | mp = vp->v_mount; |
271 | KASSERT(nmp == NULL || nmp == mp); |
272 | pathbuf_destroy(pb); |
273 | |
274 | /* |
275 | * Make sure the file system can do vptofh. If the file system |
276 | * knows the handle's size, just trust it's able to do the |
277 | * actual translation also (otherwise we should check fhtovp |
278 | * also, and that's getting a wee bit ridiculous). |
279 | */ |
280 | fid_size = 0; |
281 | if ((error = VFS_VPTOFH(vp, NULL, &fid_size)) != E2BIG) { |
282 | vput(vp); |
283 | return EOPNOTSUPP; |
284 | } |
285 | |
286 | /* Mark the file system busy. */ |
287 | error = vfs_busy(mp, NULL); |
288 | vput(vp); |
289 | if (error != 0) |
290 | return error; |
291 | if (nmp == NULL) |
292 | mutex_enter(&mp->mnt_updating); /* mnt_flag */ |
293 | netexport_wrlock(); |
294 | ne = netexport_lookup(mp); |
295 | if (ne == NULL) { |
296 | error = init_exports(mp, &ne); |
297 | if (error != 0) { |
298 | goto out; |
299 | } |
300 | } |
301 | |
302 | KASSERT(ne != NULL); |
303 | KASSERT(ne->ne_mount == mp); |
304 | |
305 | /* |
306 | * XXX: The part marked as 'notyet' works fine from the kernel's |
307 | * point of view, in the sense that it is able to atomically update |
308 | * the complete exports list for a file system. However, supporting |
309 | * this in mountd(8) requires a lot of work; so, for now, keep the |
310 | * old behavior of updating a single entry per call. |
311 | * |
312 | * When mountd(8) is fixed, just remove the second branch of this |
313 | * preprocessor conditional and enable the first one. |
314 | */ |
315 | #ifdef notyet |
316 | netexport_clear(ne); |
317 | for (i = 0; error == 0 && i < mel->mel_nexports; i++) |
318 | error = export(ne, &mel->mel_exports[i]); |
319 | #else |
320 | if (mel->mel_nexports == 0) |
321 | netexport_clear(ne); |
322 | else if (mel->mel_nexports == 1) |
323 | error = export(ne, &mel->mel_exports[0]); |
324 | else { |
325 | printf("%s: Cannot set more than one " |
326 | "entry at once (unimplemented)\n" , __func__); |
327 | error = EOPNOTSUPP; |
328 | } |
329 | #endif |
330 | |
331 | out: |
332 | netexport_wrunlock(); |
333 | if (nmp == NULL) |
334 | mutex_exit(&mp->mnt_updating); /* mnt_flag */ |
335 | vfs_unbusy(mp, false, NULL); |
336 | return error; |
337 | } |
338 | |
339 | static void |
340 | netexport_insert(struct netexport *ne) |
341 | { |
342 | |
343 | TAILQ_INSERT_HEAD(&netexport_list, ne, ne_list); |
344 | } |
345 | |
346 | static void |
347 | netexport_remove(struct netexport *ne) |
348 | { |
349 | |
350 | TAILQ_REMOVE(&netexport_list, ne, ne_list); |
351 | } |
352 | |
353 | static struct netexport * |
354 | netexport_lookup(const struct mount *mp) |
355 | { |
356 | struct netexport *ne; |
357 | |
358 | TAILQ_FOREACH(ne, &netexport_list, ne_list) { |
359 | if (ne->ne_mount == mp) { |
360 | goto done; |
361 | } |
362 | } |
363 | ne = NULL; |
364 | done: |
365 | return ne; |
366 | } |
367 | |
368 | static struct netexport * |
369 | netexport_lookup_byfsid(const fsid_t *fsid) |
370 | { |
371 | struct netexport *ne; |
372 | |
373 | TAILQ_FOREACH(ne, &netexport_list, ne_list) { |
374 | const struct mount *mp = ne->ne_mount; |
375 | |
376 | if (mp->mnt_stat.f_fsidx.__fsid_val[0] == fsid->__fsid_val[0] && |
377 | mp->mnt_stat.f_fsidx.__fsid_val[1] == fsid->__fsid_val[1]) { |
378 | goto done; |
379 | } |
380 | } |
381 | ne = NULL; |
382 | done: |
383 | |
384 | return ne; |
385 | } |
386 | |
387 | /* |
388 | * Check if the file system specified by the 'mp' mount structure is |
389 | * exported to a client with 'anon' anonymous credentials. The 'mb' |
390 | * argument is an mbuf containing the network address of the client. |
391 | * The return parameters for the export flags for the client are returned |
392 | * in the address specified by 'wh'. |
393 | * |
394 | * This function is used exclusively by the NFS server. It is generally |
395 | * invoked before VFS_FHTOVP to validate that a client has access to the |
396 | * file system. |
397 | */ |
398 | |
399 | int |
400 | netexport_check(const fsid_t *fsid, struct mbuf *mb, struct mount **mpp, |
401 | int *wh, kauth_cred_t *anon) |
402 | { |
403 | struct netexport *ne; |
404 | struct netcred *np; |
405 | |
406 | ne = netexport_lookup_byfsid(fsid); |
407 | if (ne == NULL) { |
408 | return EACCES; |
409 | } |
410 | np = netcred_lookup(ne, mb); |
411 | if (np == NULL) { |
412 | return EACCES; |
413 | } |
414 | |
415 | *mpp = ne->ne_mount; |
416 | *wh = np->netc_exflags; |
417 | *anon = np->netc_anon; |
418 | |
419 | return 0; |
420 | } |
421 | |
422 | /* |
423 | * Handles legacy export requests. In this case, the export information |
424 | * is hardcoded in a specific place of the mount arguments structure (given |
425 | * in data); the request for an update is given through the fspec field |
426 | * (also in a known location), which must be a null pointer. |
427 | * |
428 | * Returns EJUSTRETURN if the given command was not a export request. |
429 | * Otherwise, returns 0 on success or an appropriate error code otherwise. |
430 | */ |
431 | static int |
432 | nfs_export_update_30(struct mount *mp, const char *path, void *data) |
433 | { |
434 | struct mountd_exports_list mel; |
435 | struct mnt_export_args30 *args; |
436 | |
437 | args = data; |
438 | mel.mel_path = path; |
439 | |
440 | if (args->fspec != NULL) |
441 | return EJUSTRETURN; |
442 | |
443 | if (args->eargs.ex_flags & 0x00020000) { |
444 | /* Request to delete exports. The mask above holds the |
445 | * value that used to be in MNT_DELEXPORT. */ |
446 | mel.mel_nexports = 0; |
447 | } else { |
448 | /* |
449 | * The following code assumes export_args has not |
450 | * changed since export_args30, so check that. |
451 | */ |
452 | __CTASSERT(sizeof(args->eargs) == sizeof(*mel.mel_exports)); |
453 | |
454 | mel.mel_nexports = 1; |
455 | mel.mel_exports = (void *)&args->eargs; |
456 | } |
457 | |
458 | return mountd_set_exports_list(&mel, curlwp, mp); |
459 | } |
460 | |
461 | /* |
462 | * INTERNAL FUNCTIONS |
463 | */ |
464 | |
465 | /* |
466 | * Initializes NFS exports for the mountpoint given in 'mp'. |
467 | * If successful, returns 0 and sets *nep to the address of the new |
468 | * netexport item; otherwise returns an appropriate error code |
469 | * and *nep remains unmodified. |
470 | */ |
471 | static int |
472 | init_exports(struct mount *mp, struct netexport **nep) |
473 | { |
474 | int error; |
475 | struct export_args ea; |
476 | struct netexport *ne; |
477 | |
478 | KASSERT(mp != NULL); |
479 | |
480 | /* Ensure that we do not already have this mount point. */ |
481 | KASSERT(netexport_lookup(mp) == NULL); |
482 | |
483 | ne = kmem_zalloc(sizeof(*ne), KM_SLEEP); |
484 | ne->ne_mount = mp; |
485 | |
486 | /* Set the default export entry. Handled internally by export upon |
487 | * first call. */ |
488 | memset(&ea, 0, sizeof(ea)); |
489 | ea.ex_root = -2; |
490 | if (mp->mnt_flag & MNT_RDONLY) |
491 | ea.ex_flags |= MNT_EXRDONLY; |
492 | error = export(ne, &ea); |
493 | if (error != 0) { |
494 | kmem_free(ne, sizeof(*ne)); |
495 | } else { |
496 | netexport_insert(ne); |
497 | *nep = ne; |
498 | } |
499 | |
500 | return error; |
501 | } |
502 | |
503 | /* |
504 | * Build hash lists of net addresses and hang them off the mount point. |
505 | * Called by export() to set up a new entry in the lists of export |
506 | * addresses. |
507 | */ |
508 | static int |
509 | hang_addrlist(struct mount *mp, struct netexport *nep, |
510 | const struct export_args *argp) |
511 | { |
512 | int error, i; |
513 | struct netcred *np, *enp; |
514 | struct radix_node_head *rnh; |
515 | struct sockaddr *saddr, *smask; |
516 | struct domain *dom; |
517 | |
518 | smask = NULL; |
519 | |
520 | if (argp->ex_addrlen == 0) { |
521 | if (mp->mnt_flag & MNT_DEFEXPORTED) |
522 | return EPERM; |
523 | np = &nep->ne_defexported; |
524 | KASSERT(np->netc_anon == NULL); |
525 | np->netc_anon = kauth_cred_alloc(); |
526 | np->netc_exflags = argp->ex_flags; |
527 | kauth_uucred_to_cred(np->netc_anon, &argp->ex_anon); |
528 | mp->mnt_flag |= MNT_DEFEXPORTED; |
529 | return 0; |
530 | } |
531 | |
532 | if (argp->ex_addrlen > MLEN || argp->ex_masklen > MLEN) |
533 | return EINVAL; |
534 | |
535 | i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; |
536 | np = malloc(i, M_NETADDR, M_WAITOK | M_ZERO); |
537 | np->netc_anon = kauth_cred_alloc(); |
538 | saddr = (struct sockaddr *)(np + 1); |
539 | error = copyin(argp->ex_addr, saddr, argp->ex_addrlen); |
540 | if (error) |
541 | goto out; |
542 | if (saddr->sa_len > argp->ex_addrlen) |
543 | saddr->sa_len = argp->ex_addrlen; |
544 | if (sacheck(saddr) == -1) { |
545 | error = EINVAL; |
546 | goto out; |
547 | } |
548 | if (argp->ex_masklen) { |
549 | smask = (struct sockaddr *)((char *)saddr + argp->ex_addrlen); |
550 | error = copyin(argp->ex_mask, smask, argp->ex_masklen); |
551 | if (error) |
552 | goto out; |
553 | if (smask->sa_len > argp->ex_masklen) |
554 | smask->sa_len = argp->ex_masklen; |
555 | if (smask->sa_family != saddr->sa_family) { |
556 | error = EINVAL; |
557 | goto out; |
558 | } |
559 | if (sacheck(smask) == -1) { |
560 | error = EINVAL; |
561 | goto out; |
562 | } |
563 | } |
564 | i = saddr->sa_family; |
565 | if ((rnh = nep->ne_rtable[i]) == 0) { |
566 | /* |
567 | * Seems silly to initialize every AF when most are not |
568 | * used, do so on demand here. |
569 | */ |
570 | DOMAIN_FOREACH(dom) { |
571 | if (dom->dom_family == i && dom->dom_rtattach) { |
572 | rn_inithead((void **)&nep->ne_rtable[i], |
573 | dom->dom_rtoffset); |
574 | break; |
575 | } |
576 | } |
577 | if ((rnh = nep->ne_rtable[i]) == 0) { |
578 | error = ENOBUFS; |
579 | goto out; |
580 | } |
581 | } |
582 | |
583 | enp = (struct netcred *)(*rnh->rnh_addaddr)(saddr, smask, rnh, |
584 | np->netc_rnodes); |
585 | if (enp != np) { |
586 | if (enp == NULL) { |
587 | enp = (struct netcred *)(*rnh->rnh_lookup)(saddr, |
588 | smask, rnh); |
589 | if (enp == NULL) { |
590 | error = EPERM; |
591 | goto out; |
592 | } |
593 | } else |
594 | enp->netc_refcnt++; |
595 | |
596 | goto check; |
597 | } else |
598 | enp->netc_refcnt = 1; |
599 | |
600 | np->netc_exflags = argp->ex_flags; |
601 | kauth_uucred_to_cred(np->netc_anon, &argp->ex_anon); |
602 | return 0; |
603 | check: |
604 | if (enp->netc_exflags != argp->ex_flags || |
605 | kauth_cred_uucmp(enp->netc_anon, &argp->ex_anon) != 0) |
606 | error = EPERM; |
607 | else |
608 | error = 0; |
609 | out: |
610 | KASSERT(np->netc_anon != NULL); |
611 | kauth_cred_free(np->netc_anon); |
612 | free(np, M_NETADDR); |
613 | return error; |
614 | } |
615 | |
616 | /* |
617 | * Ensure that the address stored in 'sa' is valid. |
618 | * Returns zero on success, otherwise -1. |
619 | */ |
620 | static int |
621 | sacheck(struct sockaddr *sa) |
622 | { |
623 | |
624 | switch (sa->sa_family) { |
625 | case AF_INET: { |
626 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
627 | char *p = (char *)sin->sin_zero; |
628 | size_t i; |
629 | |
630 | if (sin->sin_len != sizeof(*sin)) |
631 | return -1; |
632 | if (sin->sin_port != 0) |
633 | return -1; |
634 | for (i = 0; i < sizeof(sin->sin_zero); i++) |
635 | if (*p++ != '\0') |
636 | return -1; |
637 | return 0; |
638 | } |
639 | case AF_INET6: { |
640 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
641 | |
642 | if (sin6->sin6_len != sizeof(*sin6)) |
643 | return -1; |
644 | if (sin6->sin6_port != 0) |
645 | return -1; |
646 | return 0; |
647 | } |
648 | default: |
649 | return -1; |
650 | } |
651 | } |
652 | |
653 | /* |
654 | * Free the netcred object pointed to by the 'rn' radix node. |
655 | * 'w' holds a pointer to the radix tree head. |
656 | */ |
657 | static int |
658 | free_netcred(struct radix_node *rn, void *w) |
659 | { |
660 | struct radix_node_head *rnh = (struct radix_node_head *)w; |
661 | struct netcred *np = (struct netcred *)(void *)rn; |
662 | |
663 | (*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh); |
664 | if (--(np->netc_refcnt) <= 0) { |
665 | KASSERT(np->netc_anon != NULL); |
666 | kauth_cred_free(np->netc_anon); |
667 | free(np, M_NETADDR); |
668 | } |
669 | return 0; |
670 | } |
671 | |
672 | /* |
673 | * Clears the exports list for a given file system. |
674 | */ |
675 | static void |
676 | netexport_clear(struct netexport *ne) |
677 | { |
678 | struct radix_node_head *rnh; |
679 | struct mount *mp = ne->ne_mount; |
680 | int i; |
681 | |
682 | if (mp->mnt_flag & MNT_EXPUBLIC) { |
683 | setpublicfs(NULL, NULL, NULL); |
684 | mp->mnt_flag &= ~MNT_EXPUBLIC; |
685 | } |
686 | |
687 | for (i = 0; i <= AF_MAX; i++) { |
688 | if ((rnh = ne->ne_rtable[i]) != NULL) { |
689 | rn_walktree(rnh, free_netcred, rnh); |
690 | free(rnh, M_RTABLE); |
691 | ne->ne_rtable[i] = NULL; |
692 | } |
693 | } |
694 | |
695 | if ((mp->mnt_flag & MNT_DEFEXPORTED) != 0) { |
696 | struct netcred *np = &ne->ne_defexported; |
697 | |
698 | KASSERT(np->netc_anon != NULL); |
699 | kauth_cred_free(np->netc_anon); |
700 | np->netc_anon = NULL; |
701 | } else { |
702 | KASSERT(ne->ne_defexported.netc_anon == NULL); |
703 | } |
704 | |
705 | mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); |
706 | } |
707 | |
708 | /* |
709 | * Add a new export entry (described by an export_args structure) to the |
710 | * given file system. |
711 | */ |
712 | static int |
713 | export(struct netexport *nep, const struct export_args *argp) |
714 | { |
715 | struct mount *mp = nep->ne_mount; |
716 | int error; |
717 | |
718 | if (argp->ex_flags & MNT_EXPORTED) { |
719 | if (argp->ex_flags & MNT_EXPUBLIC) { |
720 | if ((error = setpublicfs(mp, nep, argp)) != 0) |
721 | return error; |
722 | mp->mnt_flag |= MNT_EXPUBLIC; |
723 | } |
724 | if ((error = hang_addrlist(mp, nep, argp)) != 0) |
725 | return error; |
726 | mp->mnt_flag |= MNT_EXPORTED; |
727 | } |
728 | return 0; |
729 | } |
730 | |
731 | /* |
732 | * Set the publicly exported filesystem (WebNFS). Currently, only |
733 | * one public filesystem is possible in the spec (RFC 2054 and 2055) |
734 | */ |
735 | static int |
736 | setpublicfs(struct mount *mp, struct netexport *nep, |
737 | const struct export_args *argp) |
738 | { |
739 | char *cp; |
740 | int error; |
741 | struct vnode *rvp; |
742 | size_t fhsize; |
743 | |
744 | /* |
745 | * mp == NULL --> invalidate the current info; the FS is |
746 | * no longer exported. May be called from either export |
747 | * or unmount, so check if it hasn't already been done. |
748 | */ |
749 | if (mp == NULL) { |
750 | if (nfs_pub.np_valid) { |
751 | nfs_pub.np_valid = 0; |
752 | if (nfs_pub.np_handle != NULL) { |
753 | free(nfs_pub.np_handle, M_TEMP); |
754 | nfs_pub.np_handle = NULL; |
755 | } |
756 | if (nfs_pub.np_index != NULL) { |
757 | free(nfs_pub.np_index, M_TEMP); |
758 | nfs_pub.np_index = NULL; |
759 | } |
760 | } |
761 | return 0; |
762 | } |
763 | |
764 | /* |
765 | * Only one allowed at a time. |
766 | */ |
767 | if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount) |
768 | return EBUSY; |
769 | |
770 | /* |
771 | * Get real filehandle for root of exported FS. |
772 | */ |
773 | if ((error = VFS_ROOT(mp, &rvp))) |
774 | return error; |
775 | |
776 | fhsize = 0; |
777 | error = vfs_composefh(rvp, NULL, &fhsize); |
778 | if (error != E2BIG) |
779 | return error; |
780 | nfs_pub.np_handle = malloc(fhsize, M_TEMP, M_NOWAIT); |
781 | if (nfs_pub.np_handle == NULL) |
782 | error = ENOMEM; |
783 | else |
784 | error = vfs_composefh(rvp, nfs_pub.np_handle, &fhsize); |
785 | if (error) |
786 | return error; |
787 | |
788 | vput(rvp); |
789 | |
790 | /* |
791 | * If an indexfile was specified, pull it in. |
792 | */ |
793 | if (argp->ex_indexfile != NULL) { |
794 | nfs_pub.np_index = malloc(NFS_MAXNAMLEN + 1, M_TEMP, M_WAITOK); |
795 | error = copyinstr(argp->ex_indexfile, nfs_pub.np_index, |
796 | NFS_MAXNAMLEN, (size_t *)0); |
797 | if (!error) { |
798 | /* |
799 | * Check for illegal filenames. |
800 | */ |
801 | for (cp = nfs_pub.np_index; *cp; cp++) { |
802 | if (*cp == '/') { |
803 | error = EINVAL; |
804 | break; |
805 | } |
806 | } |
807 | } |
808 | if (error) { |
809 | free(nfs_pub.np_index, M_TEMP); |
810 | return error; |
811 | } |
812 | } |
813 | |
814 | nfs_pub.np_mount = mp; |
815 | nfs_pub.np_valid = 1; |
816 | return 0; |
817 | } |
818 | |
819 | /* |
820 | * Look up an export entry in the exports list that matches the address |
821 | * stored in 'nam'. If no entry is found, the default one is used instead |
822 | * (if available). |
823 | */ |
824 | static struct netcred * |
825 | netcred_lookup(struct netexport *ne, struct mbuf *nam) |
826 | { |
827 | struct netcred *np; |
828 | struct radix_node_head *rnh; |
829 | struct sockaddr *saddr; |
830 | |
831 | if ((ne->ne_mount->mnt_flag & MNT_EXPORTED) == 0) { |
832 | return NULL; |
833 | } |
834 | |
835 | /* |
836 | * Look in the export list first. |
837 | */ |
838 | np = NULL; |
839 | if (nam != NULL) { |
840 | saddr = mtod(nam, struct sockaddr *); |
841 | rnh = ne->ne_rtable[saddr->sa_family]; |
842 | if (rnh != NULL) { |
843 | np = (struct netcred *) |
844 | (*rnh->rnh_matchaddr)((void *)saddr, |
845 | rnh); |
846 | if (np && np->netc_rnodes->rn_flags & RNF_ROOT) |
847 | np = NULL; |
848 | } |
849 | } |
850 | /* |
851 | * If no address match, use the default if it exists. |
852 | */ |
853 | if (np == NULL && ne->ne_mount->mnt_flag & MNT_DEFEXPORTED) |
854 | np = &ne->ne_defexported; |
855 | |
856 | return np; |
857 | } |
858 | |
859 | void |
860 | netexport_rdlock(void) |
861 | { |
862 | |
863 | rw_enter(&netexport_lock, RW_READER); |
864 | } |
865 | |
866 | void |
867 | netexport_rdunlock(void) |
868 | { |
869 | |
870 | rw_exit(&netexport_lock); |
871 | } |
872 | |
873 | static void |
874 | netexport_wrlock(void) |
875 | { |
876 | |
877 | rw_enter(&netexport_lock, RW_WRITER); |
878 | } |
879 | |
880 | static void |
881 | netexport_wrunlock(void) |
882 | { |
883 | |
884 | rw_exit(&netexport_lock); |
885 | } |
886 | |
887 | bool |
888 | netexport_hasexports(void) |
889 | { |
890 | |
891 | return nfs_pub.np_valid || !TAILQ_EMPTY(&netexport_list); |
892 | } |
893 | |