xref: /freebsd/sys/kern/vfs_export.c (revision 81ad6265)
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/rmlock.h>
56 #include <sys/refcount.h>
57 #include <sys/signalvar.h>
58 #include <sys/socket.h>
59 #include <sys/vnode.h>
60 
61 #include <netinet/in.h>
62 #include <net/radix.h>
63 
64 #include <rpc/types.h>
65 #include <rpc/auth.h>
66 
67 static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
68 
69 #if defined(INET) || defined(INET6)
70 static struct radix_node_head *vfs_create_addrlist_af(
71 		    struct radix_node_head **prnh, int off);
72 #endif
73 static int	vfs_free_netcred(struct radix_node *rn, void *w);
74 static void	vfs_free_addrlist_af(struct radix_node_head **prnh);
75 static int	vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
76 		    struct export_args *argp);
77 static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
78 
79 /*
80  * Network address lookup element
81  */
82 struct netcred {
83 	struct	radix_node netc_rnodes[2];
84 	uint64_t netc_exflags;
85 	struct	ucred *netc_anon;
86 	int	netc_numsecflavors;
87 	int	netc_secflavors[MAXSECFLAVORS];
88 };
89 
90 /*
91  * Network export information
92  */
93 struct netexport {
94 	struct	netcred ne_defexported;		      /* Default export */
95 	struct 	radix_node_head	*ne4;
96 	struct 	radix_node_head	*ne6;
97 };
98 
99 /*
100  * Build hash lists of net addresses and hang them off the mount point.
101  * Called by vfs_export() to set up the lists of export addresses.
102  */
103 static int
104 vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
105     struct export_args *argp)
106 {
107 	struct netcred *np;
108 	struct radix_node_head *rnh;
109 	int i;
110 	struct radix_node *rn;
111 	struct sockaddr *saddr, *smask = NULL;
112 #if defined(INET6) || defined(INET)
113 	int off;
114 #endif
115 	int error;
116 
117 	KASSERT(argp->ex_numsecflavors > 0,
118 	    ("%s: numsecflavors <= 0", __func__));
119 	KASSERT(argp->ex_numsecflavors < MAXSECFLAVORS,
120 	    ("%s: numsecflavors >= MAXSECFLAVORS", __func__));
121 
122 	/*
123 	 * XXX: This routine converts from a uid plus gid list
124 	 * to a `struct ucred' (np->netc_anon).  This
125 	 * operation is questionable; for example, what should be done
126 	 * with fields like cr_uidinfo and cr_prison?  Currently, this
127 	 * routine does not touch them (leaves them as NULL).
128 	 */
129 	if (argp->ex_addrlen == 0) {
130 		if (mp->mnt_flag & MNT_DEFEXPORTED) {
131 			vfs_mount_error(mp,
132 			    "MNT_DEFEXPORTED already set for mount %p", mp);
133 			return (EPERM);
134 		}
135 		np = &nep->ne_defexported;
136 		np->netc_exflags = argp->ex_flags;
137 		np->netc_anon = crget();
138 		np->netc_anon->cr_uid = argp->ex_uid;
139 		crsetgroups(np->netc_anon, argp->ex_ngroups,
140 		    argp->ex_groups);
141 		np->netc_anon->cr_prison = &prison0;
142 		prison_hold(np->netc_anon->cr_prison);
143 		np->netc_numsecflavors = argp->ex_numsecflavors;
144 		bcopy(argp->ex_secflavors, np->netc_secflavors,
145 		    sizeof(np->netc_secflavors));
146 		MNT_ILOCK(mp);
147 		mp->mnt_flag |= MNT_DEFEXPORTED;
148 		MNT_IUNLOCK(mp);
149 		return (0);
150 	}
151 
152 #if MSIZE <= 256
153 	if (argp->ex_addrlen > MLEN) {
154 		vfs_mount_error(mp, "ex_addrlen %d is greater than %d",
155 		    argp->ex_addrlen, MLEN);
156 		return (EINVAL);
157 	}
158 #endif
159 
160 	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
161 	np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
162 	saddr = (struct sockaddr *) (np + 1);
163 	if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen)))
164 		goto out;
165 	if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) {
166 		error = EINVAL;
167 		vfs_mount_error(mp, "Invalid saddr->sa_family: %d");
168 		goto out;
169 	}
170 	if (saddr->sa_len > argp->ex_addrlen)
171 		saddr->sa_len = argp->ex_addrlen;
172 	if (argp->ex_masklen) {
173 		smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
174 		error = copyin(argp->ex_mask, smask, argp->ex_masklen);
175 		if (error)
176 			goto out;
177 		if (smask->sa_len > argp->ex_masklen)
178 			smask->sa_len = argp->ex_masklen;
179 	}
180 	rnh = NULL;
181 	switch (saddr->sa_family) {
182 #ifdef INET
183 	case AF_INET:
184 		if ((rnh = nep->ne4) == NULL) {
185 			off = offsetof(struct sockaddr_in, sin_addr) << 3;
186 			rnh = vfs_create_addrlist_af(&nep->ne4, off);
187 		}
188 		break;
189 #endif
190 #ifdef INET6
191 	case AF_INET6:
192 		if ((rnh = nep->ne6) == NULL) {
193 			off = offsetof(struct sockaddr_in6, sin6_addr) << 3;
194 			rnh = vfs_create_addrlist_af(&nep->ne6, off);
195 		}
196 		break;
197 #endif
198 	}
199 	if (rnh == NULL) {
200 		error = ENOBUFS;
201 		vfs_mount_error(mp, "%s %s %d",
202 		    "Unable to initialize radix node head ",
203 		    "for address family", saddr->sa_family);
204 		goto out;
205 	}
206 	RADIX_NODE_HEAD_LOCK(rnh);
207 	rn = (*rnh->rnh_addaddr)(saddr, smask, &rnh->rh, np->netc_rnodes);
208 	RADIX_NODE_HEAD_UNLOCK(rnh);
209 	if (rn == NULL || np != (struct netcred *)rn) {	/* already exists */
210 		error = EPERM;
211 		vfs_mount_error(mp,
212 		    "netcred already exists for given addr/mask");
213 		goto out;
214 	}
215 	np->netc_exflags = argp->ex_flags;
216 	np->netc_anon = crget();
217 	np->netc_anon->cr_uid = argp->ex_uid;
218 	crsetgroups(np->netc_anon, argp->ex_ngroups,
219 	    argp->ex_groups);
220 	np->netc_anon->cr_prison = &prison0;
221 	prison_hold(np->netc_anon->cr_prison);
222 	np->netc_numsecflavors = argp->ex_numsecflavors;
223 	bcopy(argp->ex_secflavors, np->netc_secflavors,
224 	    sizeof(np->netc_secflavors));
225 	return (0);
226 out:
227 	free(np, M_NETADDR);
228 	return (error);
229 }
230 
231 /* Helper for vfs_free_addrlist. */
232 /* ARGSUSED */
233 static int
234 vfs_free_netcred(struct radix_node *rn, void *w)
235 {
236 	struct radix_node_head *rnh = (struct radix_node_head *) w;
237 	struct ucred *cred;
238 
239 	(*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, &rnh->rh);
240 	cred = ((struct netcred *)rn)->netc_anon;
241 	if (cred != NULL)
242 		crfree(cred);
243 	free(rn, M_NETADDR);
244 	return (0);
245 }
246 
247 #if defined(INET) || defined(INET6)
248 static struct radix_node_head *
249 vfs_create_addrlist_af(struct radix_node_head **prnh, int off)
250 {
251 
252 	if (rn_inithead((void **)prnh, off) == 0)
253 		return (NULL);
254 	RADIX_NODE_HEAD_LOCK_INIT(*prnh);
255 	return (*prnh);
256 }
257 #endif
258 
259 static void
260 vfs_free_addrlist_af(struct radix_node_head **prnh)
261 {
262 	struct radix_node_head *rnh;
263 
264 	rnh = *prnh;
265 	RADIX_NODE_HEAD_LOCK(rnh);
266 	(*rnh->rnh_walktree)(&rnh->rh, vfs_free_netcred, rnh);
267 	RADIX_NODE_HEAD_UNLOCK(rnh);
268 	RADIX_NODE_HEAD_DESTROY(rnh);
269 	rn_detachhead((void **)prnh);
270 	prnh = NULL;
271 }
272 
273 /*
274  * Free the net address hash lists that are hanging off the mount points.
275  */
276 void
277 vfs_free_addrlist(struct netexport *nep)
278 {
279 	struct ucred *cred;
280 
281 	if (nep->ne4 != NULL)
282 		vfs_free_addrlist_af(&nep->ne4);
283 	if (nep->ne6 != NULL)
284 		vfs_free_addrlist_af(&nep->ne6);
285 
286 	cred = nep->ne_defexported.netc_anon;
287 	if (cred != NULL) {
288 		crfree(cred);
289 		nep->ne_defexported.netc_anon = NULL;
290 	}
291 
292 }
293 
294 /*
295  * High level function to manipulate export options on a mount point
296  * and the passed in netexport.
297  * Struct export_args *argp is the variable used to twiddle options,
298  * the structure is described in sys/mount.h
299  */
300 int
301 vfs_export(struct mount *mp, struct export_args *argp)
302 {
303 	struct netexport *nep;
304 	int error;
305 
306 	if ((argp->ex_flags & (MNT_DELEXPORT | MNT_EXPORTED)) == 0)
307 		return (EINVAL);
308 
309 	if ((argp->ex_flags & MNT_EXPORTED) != 0 &&
310 	    (argp->ex_numsecflavors < 0
311 	    || argp->ex_numsecflavors >= MAXSECFLAVORS))
312 		return (EINVAL);
313 
314 	error = 0;
315 	lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL);
316 	nep = mp->mnt_export;
317 	if (argp->ex_flags & MNT_DELEXPORT) {
318 		if (nep == NULL) {
319 			error = ENOENT;
320 			goto out;
321 		}
322 		if (mp->mnt_flag & MNT_EXPUBLIC) {
323 			vfs_setpublicfs(NULL, NULL, NULL);
324 			MNT_ILOCK(mp);
325 			mp->mnt_flag &= ~MNT_EXPUBLIC;
326 			MNT_IUNLOCK(mp);
327 		}
328 		vfs_free_addrlist(nep);
329 		mp->mnt_export = NULL;
330 		free(nep, M_MOUNT);
331 		nep = NULL;
332 		MNT_ILOCK(mp);
333 		mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
334 		MNT_IUNLOCK(mp);
335 	}
336 	if (argp->ex_flags & MNT_EXPORTED) {
337 		if (nep == NULL) {
338 			nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
339 			mp->mnt_export = nep;
340 		}
341 		if (argp->ex_flags & MNT_EXPUBLIC) {
342 			if ((error = vfs_setpublicfs(mp, nep, argp)) != 0)
343 				goto out;
344 			MNT_ILOCK(mp);
345 			mp->mnt_flag |= MNT_EXPUBLIC;
346 			MNT_IUNLOCK(mp);
347 		}
348 		if (argp->ex_numsecflavors == 0) {
349 			argp->ex_numsecflavors = 1;
350 			argp->ex_secflavors[0] = AUTH_SYS;
351 		}
352 		if ((error = vfs_hang_addrlist(mp, nep, argp)))
353 			goto out;
354 		MNT_ILOCK(mp);
355 		mp->mnt_flag |= MNT_EXPORTED;
356 		MNT_IUNLOCK(mp);
357 	}
358 
359 out:
360 	lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
361 	/*
362 	 * Once we have executed the vfs_export() command, we do
363 	 * not want to keep the "export" option around in the
364 	 * options list, since that will cause subsequent MNT_UPDATE
365 	 * calls to fail.  The export information is saved in
366 	 * mp->mnt_export, so we can safely delete the "export" mount option
367 	 * here.
368 	 */
369 	vfs_deleteopt(mp->mnt_optnew, "export");
370 	vfs_deleteopt(mp->mnt_opt, "export");
371 	return (error);
372 }
373 
374 /*
375  * Set the publicly exported filesystem (WebNFS). Currently, only
376  * one public filesystem is possible in the spec (RFC 2054 and 2055)
377  */
378 int
379 vfs_setpublicfs(struct mount *mp, struct netexport *nep,
380     struct export_args *argp)
381 {
382 	int error;
383 	struct vnode *rvp;
384 	char *cp;
385 
386 	/*
387 	 * mp == NULL -> invalidate the current info, the FS is
388 	 * no longer exported. May be called from either vfs_export
389 	 * or unmount, so check if it hasn't already been done.
390 	 */
391 	if (mp == NULL) {
392 		if (nfs_pub.np_valid) {
393 			nfs_pub.np_valid = 0;
394 			if (nfs_pub.np_index != NULL) {
395 				free(nfs_pub.np_index, M_TEMP);
396 				nfs_pub.np_index = NULL;
397 			}
398 		}
399 		return (0);
400 	}
401 
402 	/*
403 	 * Only one allowed at a time.
404 	 */
405 	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
406 		return (EBUSY);
407 
408 	/*
409 	 * Get real filehandle for root of exported FS.
410 	 */
411 	bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
412 	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
413 
414 	if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp)))
415 		return (error);
416 
417 	if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
418 		return (error);
419 
420 	vput(rvp);
421 
422 	/*
423 	 * If an indexfile was specified, pull it in.
424 	 */
425 	if (argp->ex_indexfile != NULL) {
426 		if (nfs_pub.np_index == NULL)
427 			nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP,
428 			    M_WAITOK);
429 		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
430 		    MAXNAMLEN, (size_t *)0);
431 		if (!error) {
432 			/*
433 			 * Check for illegal filenames.
434 			 */
435 			for (cp = nfs_pub.np_index; *cp; cp++) {
436 				if (*cp == '/') {
437 					error = EINVAL;
438 					break;
439 				}
440 			}
441 		}
442 		if (error) {
443 			free(nfs_pub.np_index, M_TEMP);
444 			nfs_pub.np_index = NULL;
445 			return (error);
446 		}
447 	}
448 
449 	nfs_pub.np_mount = mp;
450 	nfs_pub.np_valid = 1;
451 	return (0);
452 }
453 
454 /*
455  * Used by the filesystems to determine if a given network address
456  * (passed in 'nam') is present in their exports list, returns a pointer
457  * to struct netcred so that the filesystem can examine it for
458  * access rights (read/write/etc).
459  */
460 static struct netcred *
461 vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
462 {
463 	RADIX_NODE_HEAD_RLOCK_TRACKER;
464 	struct netexport *nep;
465 	struct netcred *np = NULL;
466 	struct radix_node_head *rnh;
467 	struct sockaddr *saddr;
468 
469 	nep = mp->mnt_export;
470 	if (nep == NULL)
471 		return (NULL);
472 	if ((mp->mnt_flag & MNT_EXPORTED) == 0)
473 		return (NULL);
474 
475 	/*
476 	 * Lookup in the export list
477 	 */
478 	if (nam != NULL) {
479 		saddr = nam;
480 		rnh = NULL;
481 		switch (saddr->sa_family) {
482 		case AF_INET:
483 			rnh = nep->ne4;
484 			break;
485 		case AF_INET6:
486 			rnh = nep->ne6;
487 			break;
488 		}
489 		if (rnh != NULL) {
490 			RADIX_NODE_HEAD_RLOCK(rnh);
491 			np = (struct netcred *) (*rnh->rnh_matchaddr)(saddr, &rnh->rh);
492 			RADIX_NODE_HEAD_RUNLOCK(rnh);
493 			if (np != NULL && (np->netc_rnodes->rn_flags & RNF_ROOT) != 0)
494 				return (NULL);
495 		}
496 	}
497 
498 	/*
499 	 * If no address match, use the default if it exists.
500 	 */
501 	if (np == NULL && (mp->mnt_flag & MNT_DEFEXPORTED) != 0)
502 		return (&nep->ne_defexported);
503 
504 	return (np);
505 }
506 
507 /*
508  * XXX: This comment comes from the deprecated ufs_check_export()
509  * XXX: and may not entirely apply, but lacking something better:
510  * This is the generic part of fhtovp called after the underlying
511  * filesystem has validated the file handle.
512  *
513  * Verify that a host should have access to a filesystem.
514  */
515 
516 int
517 vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, uint64_t *extflagsp,
518     struct ucred **credanonp, int *numsecflavors, int *secflavors)
519 {
520 	struct netcred *np;
521 
522 	lockmgr(&mp->mnt_explock, LK_SHARED, NULL);
523 	np = vfs_export_lookup(mp, nam);
524 	if (np == NULL) {
525 		lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
526 		*credanonp = NULL;
527 		return (EACCES);
528 	}
529 	*extflagsp = np->netc_exflags;
530 	if ((*credanonp = np->netc_anon) != NULL)
531 		crhold(*credanonp);
532 	if (numsecflavors) {
533 		*numsecflavors = np->netc_numsecflavors;
534 		KASSERT(*numsecflavors > 0,
535 		    ("%s: numsecflavors <= 0", __func__));
536 		KASSERT(*numsecflavors < MAXSECFLAVORS,
537 		    ("%s: numsecflavors >= MAXSECFLAVORS", __func__));
538 	}
539 	if (secflavors && np->netc_numsecflavors > 0)
540 		memcpy(secflavors, np->netc_secflavors, np->netc_numsecflavors *
541 		    sizeof(int));
542 	lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
543 	return (0);
544 }
545