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