xref: /freebsd/sys/kern/vfs_export.c (revision 8859960436f5727f163a7b3468e08710c5e6d874)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * (c) UNIX System Laboratories, Inc.
7  * All or some portions of this file are derived from material licensed
8  * to the University of California by American Telephone and Telegraph
9  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10  * the permission of UNIX System Laboratories, Inc.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	@(#)vfs_subr.c	8.31 (Berkeley) 5/26/95
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 #include "opt_inet.h"
43 #include "opt_inet6.h"
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/dirent.h>
48 #include <sys/jail.h>
49 #include <sys/kernel.h>
50 #include <sys/lock.h>
51 #include <sys/malloc.h>
52 #include <sys/mbuf.h>
53 #include <sys/mount.h>
54 #include <sys/mutex.h>
55 #include <sys/proc.h>
56 #include <sys/rmlock.h>
57 #include <sys/refcount.h>
58 #include <sys/signalvar.h>
59 #include <sys/socket.h>
60 #include <sys/vnode.h>
61 
62 #include <netinet/in.h>
63 #include <net/radix.h>
64 
65 #include <rpc/types.h>
66 #include <rpc/auth.h>
67 
68 static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
69 
70 #if defined(INET) || defined(INET6)
71 static struct radix_node_head *vfs_create_addrlist_af(
72 		    struct radix_node_head **prnh, int off);
73 #endif
74 static int	vfs_free_netcred(struct radix_node *rn, void *w);
75 static void	vfs_free_addrlist_af(struct radix_node_head **prnh);
76 static int	vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
77 		    struct export_args *argp);
78 static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
79 
80 /*
81  * Network address lookup element
82  */
83 struct netcred {
84 	struct	radix_node netc_rnodes[2];
85 	uint64_t netc_exflags;
86 	struct	ucred *netc_anon;
87 	int	netc_numsecflavors;
88 	int	netc_secflavors[MAXSECFLAVORS];
89 };
90 
91 /*
92  * Network export information
93  */
94 struct netexport {
95 	struct	netcred ne_defexported;		      /* Default export */
96 	struct 	radix_node_head	*ne4;
97 	struct 	radix_node_head	*ne6;
98 };
99 
100 /*
101  * Build hash lists of net addresses and hang them off the mount point.
102  * Called by vfs_export() to set up the lists of export addresses.
103  */
104 static int
105 vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
106     struct export_args *argp)
107 {
108 	struct netcred *np;
109 	struct radix_node_head *rnh;
110 	int i;
111 	struct radix_node *rn;
112 	struct sockaddr *saddr, *smask = NULL;
113 #if defined(INET6) || defined(INET)
114 	int off;
115 #endif
116 	int error;
117 
118 	KASSERT(argp->ex_numsecflavors > 0,
119 	    ("%s: numsecflavors <= 0", __func__));
120 	KASSERT(argp->ex_numsecflavors < MAXSECFLAVORS,
121 	    ("%s: numsecflavors >= MAXSECFLAVORS", __func__));
122 
123 	/*
124 	 * XXX: This routine converts from a uid plus gid list
125 	 * to a `struct ucred' (np->netc_anon).  This
126 	 * operation is questionable; for example, what should be done
127 	 * with fields like cr_uidinfo and cr_prison?  Currently, this
128 	 * routine does not touch them (leaves them as NULL).
129 	 */
130 	if (argp->ex_addrlen == 0) {
131 		if (mp->mnt_flag & MNT_DEFEXPORTED) {
132 			vfs_mount_error(mp,
133 			    "MNT_DEFEXPORTED already set for mount %p", mp);
134 			return (EPERM);
135 		}
136 		np = &nep->ne_defexported;
137 		np->netc_exflags = argp->ex_flags;
138 		np->netc_anon = crget();
139 		np->netc_anon->cr_uid = argp->ex_uid;
140 		crsetgroups(np->netc_anon, argp->ex_ngroups,
141 		    argp->ex_groups);
142 		np->netc_anon->cr_prison = &prison0;
143 		prison_hold(np->netc_anon->cr_prison);
144 		np->netc_numsecflavors = argp->ex_numsecflavors;
145 		bcopy(argp->ex_secflavors, np->netc_secflavors,
146 		    sizeof(np->netc_secflavors));
147 		MNT_ILOCK(mp);
148 		mp->mnt_flag |= MNT_DEFEXPORTED;
149 		MNT_IUNLOCK(mp);
150 		return (0);
151 	}
152 
153 #if MSIZE <= 256
154 	if (argp->ex_addrlen > MLEN) {
155 		vfs_mount_error(mp, "ex_addrlen %d is greater than %d",
156 		    argp->ex_addrlen, MLEN);
157 		return (EINVAL);
158 	}
159 #endif
160 
161 	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
162 	np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
163 	saddr = (struct sockaddr *) (np + 1);
164 	if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen)))
165 		goto out;
166 	if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) {
167 		error = EINVAL;
168 		vfs_mount_error(mp, "Invalid saddr->sa_family: %d");
169 		goto out;
170 	}
171 	if (saddr->sa_len > argp->ex_addrlen)
172 		saddr->sa_len = argp->ex_addrlen;
173 	if (argp->ex_masklen) {
174 		smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
175 		error = copyin(argp->ex_mask, smask, argp->ex_masklen);
176 		if (error)
177 			goto out;
178 		if (smask->sa_len > argp->ex_masklen)
179 			smask->sa_len = argp->ex_masklen;
180 	}
181 	rnh = NULL;
182 	switch (saddr->sa_family) {
183 #ifdef INET
184 	case AF_INET:
185 		if ((rnh = nep->ne4) == NULL) {
186 			off = offsetof(struct sockaddr_in, sin_addr) << 3;
187 			rnh = vfs_create_addrlist_af(&nep->ne4, off);
188 		}
189 		break;
190 #endif
191 #ifdef INET6
192 	case AF_INET6:
193 		if ((rnh = nep->ne6) == NULL) {
194 			off = offsetof(struct sockaddr_in6, sin6_addr) << 3;
195 			rnh = vfs_create_addrlist_af(&nep->ne6, off);
196 		}
197 		break;
198 #endif
199 	}
200 	if (rnh == NULL) {
201 		error = ENOBUFS;
202 		vfs_mount_error(mp, "%s %s %d",
203 		    "Unable to initialize radix node head ",
204 		    "for address family", saddr->sa_family);
205 		goto out;
206 	}
207 	RADIX_NODE_HEAD_LOCK(rnh);
208 	rn = (*rnh->rnh_addaddr)(saddr, smask, &rnh->rh, np->netc_rnodes);
209 	RADIX_NODE_HEAD_UNLOCK(rnh);
210 	if (rn == NULL || np != (struct netcred *)rn) {	/* already exists */
211 		error = EPERM;
212 		vfs_mount_error(mp,
213 		    "netcred already exists for given addr/mask");
214 		goto out;
215 	}
216 	np->netc_exflags = argp->ex_flags;
217 	np->netc_anon = crget();
218 	np->netc_anon->cr_uid = argp->ex_uid;
219 	crsetgroups(np->netc_anon, argp->ex_ngroups,
220 	    argp->ex_groups);
221 	np->netc_anon->cr_prison = &prison0;
222 	prison_hold(np->netc_anon->cr_prison);
223 	np->netc_numsecflavors = argp->ex_numsecflavors;
224 	bcopy(argp->ex_secflavors, np->netc_secflavors,
225 	    sizeof(np->netc_secflavors));
226 	return (0);
227 out:
228 	free(np, M_NETADDR);
229 	return (error);
230 }
231 
232 /* Helper for vfs_free_addrlist. */
233 /* ARGSUSED */
234 static int
235 vfs_free_netcred(struct radix_node *rn, void *w)
236 {
237 	struct radix_node_head *rnh = (struct radix_node_head *) w;
238 	struct ucred *cred;
239 
240 	(*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, &rnh->rh);
241 	cred = ((struct netcred *)rn)->netc_anon;
242 	if (cred != NULL)
243 		crfree(cred);
244 	free(rn, M_NETADDR);
245 	return (0);
246 }
247 
248 #if defined(INET) || defined(INET6)
249 static struct radix_node_head *
250 vfs_create_addrlist_af(struct radix_node_head **prnh, int off)
251 {
252 
253 	if (rn_inithead((void **)prnh, off) == 0)
254 		return (NULL);
255 	RADIX_NODE_HEAD_LOCK_INIT(*prnh);
256 	return (*prnh);
257 }
258 #endif
259 
260 static void
261 vfs_free_addrlist_af(struct radix_node_head **prnh)
262 {
263 	struct radix_node_head *rnh;
264 
265 	rnh = *prnh;
266 	RADIX_NODE_HEAD_LOCK(rnh);
267 	(*rnh->rnh_walktree)(&rnh->rh, vfs_free_netcred, rnh);
268 	RADIX_NODE_HEAD_UNLOCK(rnh);
269 	RADIX_NODE_HEAD_DESTROY(rnh);
270 	rn_detachhead((void **)prnh);
271 	prnh = NULL;
272 }
273 
274 /*
275  * Free the net address hash lists that are hanging off the mount points.
276  */
277 void
278 vfs_free_addrlist(struct netexport *nep)
279 {
280 	struct ucred *cred;
281 
282 	if (nep->ne4 != NULL)
283 		vfs_free_addrlist_af(&nep->ne4);
284 	if (nep->ne6 != NULL)
285 		vfs_free_addrlist_af(&nep->ne6);
286 
287 	cred = nep->ne_defexported.netc_anon;
288 	if (cred != NULL) {
289 		crfree(cred);
290 		nep->ne_defexported.netc_anon = NULL;
291 	}
292 
293 }
294 
295 /*
296  * High level function to manipulate export options on a mount point
297  * and the passed in netexport.
298  * Struct export_args *argp is the variable used to twiddle options,
299  * the structure is described in sys/mount.h
300  */
301 int
302 vfs_export(struct mount *mp, struct export_args *argp)
303 {
304 	struct netexport *nep;
305 	int error;
306 	uint64_t jail_permid;
307 	bool new_nep, prison_alive;
308 
309 	if ((argp->ex_flags & (MNT_DELEXPORT | MNT_EXPORTED)) == 0)
310 		return (EINVAL);
311 
312 	if ((argp->ex_flags & MNT_EXPORTED) != 0 &&
313 	    (argp->ex_numsecflavors < 0
314 	    || argp->ex_numsecflavors >= MAXSECFLAVORS))
315 		return (EINVAL);
316 
317 	error = 0;
318 	jail_permid = curthread->td_ucred->cr_prison->pr_permid;
319 	lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL);
320 	nep = mp->mnt_export;
321 	prison_alive = prison_isalive_permid(mp->mnt_exjail);
322 	if (argp->ex_flags & MNT_DELEXPORT) {
323 		if (nep == NULL) {
324 			KASSERT(mp->mnt_exjail == 0,
325 			    ("vfs_export: mnt_exjail delexport not 0"));
326 			error = ENOENT;
327 			goto out;
328 		}
329 		KASSERT(mp->mnt_exjail != 0,
330 		    ("vfs_export: mnt_exjail delexport 0"));
331 		if (jail_permid == 1 && mp->mnt_exjail != jail_permid &&
332 		    prison_alive) {
333 			/* EXDEV will not get logged by mountd(8). */
334 			error = EXDEV;
335 			goto out;
336 		} else if (mp->mnt_exjail != jail_permid && prison_alive) {
337 			/* EPERM will get logged by mountd(8). */
338 			error = EPERM;
339 			goto out;
340 		}
341 		if (mp->mnt_flag & MNT_EXPUBLIC) {
342 			vfs_setpublicfs(NULL, NULL, NULL);
343 			MNT_ILOCK(mp);
344 			mp->mnt_flag &= ~MNT_EXPUBLIC;
345 			MNT_IUNLOCK(mp);
346 		}
347 		vfs_free_addrlist(nep);
348 		mp->mnt_export = NULL;
349 		mp->mnt_exjail = 0;
350 		free(nep, M_MOUNT);
351 		nep = NULL;
352 		MNT_ILOCK(mp);
353 		mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
354 		MNT_IUNLOCK(mp);
355 	}
356 	new_nep = false;
357 	if (argp->ex_flags & MNT_EXPORTED) {
358 		if (nep == NULL) {
359 			KASSERT(mp->mnt_exjail == 0,
360 			    ("vfs_export: mnt_exjail not 0"));
361 			nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
362 			mp->mnt_export = nep;
363 			new_nep = true;
364 		} else if (mp->mnt_exjail != jail_permid && prison_alive) {
365 			KASSERT(mp->mnt_exjail != 0,
366 			    ("vfs_export: mnt_exjail 0"));
367 			error = EPERM;
368 			goto out;
369 		}
370 		if (argp->ex_flags & MNT_EXPUBLIC) {
371 			if ((error = vfs_setpublicfs(mp, nep, argp)) != 0) {
372 				if (new_nep) {
373 					mp->mnt_export = NULL;
374 					free(nep, M_MOUNT);
375 				}
376 				goto out;
377 			}
378 			new_nep = false;
379 			mp->mnt_exjail = jail_permid;
380 			MNT_ILOCK(mp);
381 			mp->mnt_flag |= MNT_EXPUBLIC;
382 			MNT_IUNLOCK(mp);
383 		}
384 		if (argp->ex_numsecflavors == 0) {
385 			argp->ex_numsecflavors = 1;
386 			argp->ex_secflavors[0] = AUTH_SYS;
387 		}
388 		if ((error = vfs_hang_addrlist(mp, nep, argp))) {
389 			if (new_nep) {
390 				mp->mnt_export = NULL;
391 				free(nep, M_MOUNT);
392 			}
393 			goto out;
394 		}
395 		mp->mnt_exjail = jail_permid;
396 		MNT_ILOCK(mp);
397 		mp->mnt_flag |= MNT_EXPORTED;
398 		MNT_IUNLOCK(mp);
399 	}
400 
401 out:
402 	lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
403 	/*
404 	 * Once we have executed the vfs_export() command, we do
405 	 * not want to keep the "export" option around in the
406 	 * options list, since that will cause subsequent MNT_UPDATE
407 	 * calls to fail.  The export information is saved in
408 	 * mp->mnt_export, so we can safely delete the "export" mount option
409 	 * here.
410 	 */
411 	vfs_deleteopt(mp->mnt_optnew, "export");
412 	vfs_deleteopt(mp->mnt_opt, "export");
413 	return (error);
414 }
415 
416 /*
417  * Set the publicly exported filesystem (WebNFS). Currently, only
418  * one public filesystem is possible in the spec (RFC 2054 and 2055)
419  */
420 int
421 vfs_setpublicfs(struct mount *mp, struct netexport *nep,
422     struct export_args *argp)
423 {
424 	int error;
425 	struct vnode *rvp;
426 	char *cp;
427 
428 	/*
429 	 * mp == NULL -> invalidate the current info, the FS is
430 	 * no longer exported. May be called from either vfs_export
431 	 * or unmount, so check if it hasn't already been done.
432 	 */
433 	if (mp == NULL) {
434 		if (nfs_pub.np_valid) {
435 			nfs_pub.np_valid = 0;
436 			if (nfs_pub.np_index != NULL) {
437 				free(nfs_pub.np_index, M_TEMP);
438 				nfs_pub.np_index = NULL;
439 			}
440 		}
441 		return (0);
442 	}
443 
444 	/*
445 	 * Only one allowed at a time.
446 	 */
447 	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
448 		return (EBUSY);
449 
450 	/*
451 	 * Get real filehandle for root of exported FS.
452 	 */
453 	bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
454 	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
455 
456 	if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp)))
457 		return (error);
458 
459 	if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
460 		return (error);
461 
462 	vput(rvp);
463 
464 	/*
465 	 * If an indexfile was specified, pull it in.
466 	 */
467 	if (argp->ex_indexfile != NULL) {
468 		if (nfs_pub.np_index == NULL)
469 			nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP,
470 			    M_WAITOK);
471 		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
472 		    MAXNAMLEN, (size_t *)0);
473 		if (!error) {
474 			/*
475 			 * Check for illegal filenames.
476 			 */
477 			for (cp = nfs_pub.np_index; *cp; cp++) {
478 				if (*cp == '/') {
479 					error = EINVAL;
480 					break;
481 				}
482 			}
483 		}
484 		if (error) {
485 			free(nfs_pub.np_index, M_TEMP);
486 			nfs_pub.np_index = NULL;
487 			return (error);
488 		}
489 	}
490 
491 	nfs_pub.np_mount = mp;
492 	nfs_pub.np_valid = 1;
493 	return (0);
494 }
495 
496 /*
497  * Used by the filesystems to determine if a given network address
498  * (passed in 'nam') is present in their exports list, returns a pointer
499  * to struct netcred so that the filesystem can examine it for
500  * access rights (read/write/etc).
501  */
502 static struct netcred *
503 vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
504 {
505 	RADIX_NODE_HEAD_RLOCK_TRACKER;
506 	struct netexport *nep;
507 	struct netcred *np = NULL;
508 	struct radix_node_head *rnh;
509 	struct sockaddr *saddr;
510 
511 	nep = mp->mnt_export;
512 	if (nep == NULL)
513 		return (NULL);
514 	if ((mp->mnt_flag & MNT_EXPORTED) == 0)
515 		return (NULL);
516 
517 	/*
518 	 * Lookup in the export list
519 	 */
520 	if (nam != NULL) {
521 		saddr = nam;
522 		rnh = NULL;
523 		switch (saddr->sa_family) {
524 		case AF_INET:
525 			rnh = nep->ne4;
526 			break;
527 		case AF_INET6:
528 			rnh = nep->ne6;
529 			break;
530 		}
531 		if (rnh != NULL) {
532 			RADIX_NODE_HEAD_RLOCK(rnh);
533 			np = (struct netcred *) (*rnh->rnh_matchaddr)(saddr, &rnh->rh);
534 			RADIX_NODE_HEAD_RUNLOCK(rnh);
535 			if (np != NULL && (np->netc_rnodes->rn_flags & RNF_ROOT) != 0)
536 				return (NULL);
537 		}
538 	}
539 
540 	/*
541 	 * If no address match, use the default if it exists.
542 	 */
543 	if (np == NULL && (mp->mnt_flag & MNT_DEFEXPORTED) != 0)
544 		return (&nep->ne_defexported);
545 
546 	return (np);
547 }
548 
549 /*
550  * XXX: This comment comes from the deprecated ufs_check_export()
551  * XXX: and may not entirely apply, but lacking something better:
552  * This is the generic part of fhtovp called after the underlying
553  * filesystem has validated the file handle.
554  *
555  * Verify that a host should have access to a filesystem.
556  */
557 
558 int
559 vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, uint64_t *extflagsp,
560     struct ucred **credanonp, int *numsecflavors, int *secflavors)
561 {
562 	struct netcred *np;
563 
564 	lockmgr(&mp->mnt_explock, LK_SHARED, NULL);
565 	np = vfs_export_lookup(mp, nam);
566 	if (np == NULL) {
567 		lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
568 		*credanonp = NULL;
569 		return (EACCES);
570 	}
571 	*extflagsp = np->netc_exflags;
572 	if ((*credanonp = np->netc_anon) != NULL)
573 		crhold(*credanonp);
574 	if (numsecflavors) {
575 		*numsecflavors = np->netc_numsecflavors;
576 		KASSERT(*numsecflavors > 0,
577 		    ("%s: numsecflavors <= 0", __func__));
578 		KASSERT(*numsecflavors < MAXSECFLAVORS,
579 		    ("%s: numsecflavors >= MAXSECFLAVORS", __func__));
580 	}
581 	if (secflavors && np->netc_numsecflavors > 0)
582 		memcpy(secflavors, np->netc_secflavors, np->netc_numsecflavors *
583 		    sizeof(int));
584 	lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
585 	return (0);
586 }
587