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