vfs_export.c revision 151897
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: head/sys/kern/vfs_export.c 151897 2005-10-31 15:41:29Z rwatson $");
39
40#include <sys/param.h>
41#include <sys/dirent.h>
42#include <sys/domain.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/mbuf.h>
47#include <sys/mount.h>
48#include <sys/mutex.h>
49#include <sys/refcount.h>
50#include <sys/socket.h>
51#include <sys/systm.h>
52#include <sys/vnode.h>
53
54#include <net/radix.h>
55
56static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
57
58static void	vfs_free_addrlist(struct netexport *nep);
59static int	vfs_free_netcred(struct radix_node *rn, void *w);
60static int	vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
61		    struct export_args *argp);
62static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
63
64/*
65 * Network address lookup element
66 */
67struct netcred {
68	struct	radix_node netc_rnodes[2];
69	int	netc_exflags;
70	struct	ucred netc_anon;
71};
72
73/*
74 * Network export information
75 */
76struct netexport {
77	struct	netcred ne_defexported;		      /* Default export */
78	struct	radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
79};
80
81/*
82 * Build hash lists of net addresses and hang them off the mount point.
83 * Called by ufs_mount() to set up the lists of export addresses.
84 */
85static int
86vfs_hang_addrlist(mp, nep, argp)
87	struct mount *mp;
88	struct netexport *nep;
89	struct export_args *argp;
90{
91	register struct netcred *np;
92	register struct radix_node_head *rnh;
93	register int i;
94	struct radix_node *rn;
95	struct sockaddr *saddr, *smask = 0;
96	struct domain *dom;
97	int error;
98
99	/*
100	 * XXX: This routine converts from a `struct xucred'
101	 * (argp->ex_anon) to a `struct ucred' (np->netc_anon).  This
102	 * operation is questionable; for example, what should be done
103	 * with fields like cr_uidinfo and cr_prison?  Currently, this
104	 * routine does not touch them (leaves them as NULL).
105	 */
106	if (argp->ex_anon.cr_version != XUCRED_VERSION)
107		return (EINVAL);
108
109	if (argp->ex_addrlen == 0) {
110		if (mp->mnt_flag & MNT_DEFEXPORTED)
111			return (EPERM);
112		np = &nep->ne_defexported;
113		np->netc_exflags = argp->ex_flags;
114		bzero(&np->netc_anon, sizeof(np->netc_anon));
115		np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
116		np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
117		bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
118		    sizeof(np->netc_anon.cr_groups));
119		refcount_init(&np->netc_anon.cr_ref, 1);
120		mp->mnt_flag |= MNT_DEFEXPORTED;
121		return (0);
122	}
123
124#if MSIZE <= 256
125	if (argp->ex_addrlen > MLEN)
126		return (EINVAL);
127#endif
128
129	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
130	np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
131	saddr = (struct sockaddr *) (np + 1);
132	if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen)))
133		goto out;
134	if (saddr->sa_family > AF_MAX) {
135		error = EINVAL;
136		goto out;
137	}
138	if (saddr->sa_len > argp->ex_addrlen)
139		saddr->sa_len = argp->ex_addrlen;
140	if (argp->ex_masklen) {
141		smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
142		error = copyin(argp->ex_mask, smask, argp->ex_masklen);
143		if (error)
144			goto out;
145		if (smask->sa_len > argp->ex_masklen)
146			smask->sa_len = argp->ex_masklen;
147	}
148	i = saddr->sa_family;
149	if ((rnh = nep->ne_rtable[i]) == NULL) {
150		/*
151		 * Seems silly to initialize every AF when most are not used,
152		 * do so on demand here
153		 */
154		for (dom = domains; dom; dom = dom->dom_next)
155			if (dom->dom_family == i && dom->dom_rtattach) {
156				dom->dom_rtattach((void **) &nep->ne_rtable[i],
157				    dom->dom_rtoffset);
158				break;
159			}
160		if ((rnh = nep->ne_rtable[i]) == NULL) {
161			error = ENOBUFS;
162			goto out;
163		}
164	}
165	RADIX_NODE_HEAD_LOCK(rnh);
166	rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes);
167	RADIX_NODE_HEAD_UNLOCK(rnh);
168	if (rn == NULL || np != (struct netcred *)rn) {	/* already exists */
169		error = EPERM;
170		goto out;
171	}
172	np->netc_exflags = argp->ex_flags;
173	bzero(&np->netc_anon, sizeof(np->netc_anon));
174	np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
175	np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
176	bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
177	    sizeof(np->netc_anon.cr_groups));
178	refcount_init(&np->netc_anon.cr_ref, 1);
179	return (0);
180out:
181	free(np, M_NETADDR);
182	return (error);
183}
184
185/* Helper for vfs_free_addrlist. */
186/* ARGSUSED */
187static int
188vfs_free_netcred(rn, w)
189	struct radix_node *rn;
190	void *w;
191{
192	register struct radix_node_head *rnh = (struct radix_node_head *) w;
193
194	(*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh);
195	free(rn, M_NETADDR);
196	return (0);
197}
198
199/*
200 * Free the net address hash lists that are hanging off the mount points.
201 */
202static void
203vfs_free_addrlist(nep)
204	struct netexport *nep;
205{
206	register int i;
207	register struct radix_node_head *rnh;
208
209	for (i = 0; i <= AF_MAX; i++)
210		if ((rnh = nep->ne_rtable[i])) {
211			RADIX_NODE_HEAD_LOCK(rnh);
212			(*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh);
213			RADIX_NODE_HEAD_DESTROY(rnh);
214			free(rnh, M_RTABLE);
215			nep->ne_rtable[i] = NULL;	/* not SMP safe XXX */
216		}
217}
218
219/*
220 * High level function to manipulate export options on a mount point
221 * and the passed in netexport.
222 * Struct export_args *argp is the variable used to twiddle options,
223 * the structure is described in sys/mount.h
224 */
225int
226vfs_export(mp, argp)
227	struct mount *mp;
228	struct export_args *argp;
229{
230	struct netexport *nep;
231	int error;
232
233	nep = mp->mnt_export;
234	if (argp->ex_flags & MNT_DELEXPORT) {
235		if (nep == NULL)
236			return (ENOENT);
237		if (mp->mnt_flag & MNT_EXPUBLIC) {
238			vfs_setpublicfs(NULL, NULL, NULL);
239			mp->mnt_flag &= ~MNT_EXPUBLIC;
240		}
241		vfs_free_addrlist(nep);
242		mp->mnt_export = NULL;
243		free(nep, M_MOUNT);
244		nep = NULL;
245		mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
246	}
247	if (argp->ex_flags & MNT_EXPORTED) {
248		if (nep == NULL) {
249			nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
250			mp->mnt_export = nep;
251		}
252		if (argp->ex_flags & MNT_EXPUBLIC) {
253			if ((error = vfs_setpublicfs(mp, nep, argp)) != 0)
254				return (error);
255			mp->mnt_flag |= MNT_EXPUBLIC;
256		}
257		if ((error = vfs_hang_addrlist(mp, nep, argp)))
258			return (error);
259		mp->mnt_flag |= MNT_EXPORTED;
260	}
261	return (0);
262}
263
264/*
265 * Set the publicly exported filesystem (WebNFS). Currently, only
266 * one public filesystem is possible in the spec (RFC 2054 and 2055)
267 */
268int
269vfs_setpublicfs(mp, nep, argp)
270	struct mount *mp;
271	struct netexport *nep;
272	struct export_args *argp;
273{
274	int error;
275	struct vnode *rvp;
276	char *cp;
277
278	/*
279	 * mp == NULL -> invalidate the current info, the FS is
280	 * no longer exported. May be called from either vfs_export
281	 * or unmount, so check if it hasn't already been done.
282	 */
283	if (mp == NULL) {
284		if (nfs_pub.np_valid) {
285			nfs_pub.np_valid = 0;
286			if (nfs_pub.np_index != NULL) {
287				FREE(nfs_pub.np_index, M_TEMP);
288				nfs_pub.np_index = NULL;
289			}
290		}
291		return (0);
292	}
293
294	/*
295	 * Only one allowed at a time.
296	 */
297	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
298		return (EBUSY);
299
300	/*
301	 * Get real filehandle for root of exported FS.
302	 */
303	bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
304	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
305
306	if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, curthread /* XXX */)))
307		return (error);
308
309	if ((error = VFS_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
310		return (error);
311
312	vput(rvp);
313
314	/*
315	 * If an indexfile was specified, pull it in.
316	 */
317	if (argp->ex_indexfile != NULL) {
318		MALLOC(nfs_pub.np_index, char *, MAXNAMLEN + 1, M_TEMP,
319		    M_WAITOK);
320		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
321		    MAXNAMLEN, (size_t *)0);
322		if (!error) {
323			/*
324			 * Check for illegal filenames.
325			 */
326			for (cp = nfs_pub.np_index; *cp; cp++) {
327				if (*cp == '/') {
328					error = EINVAL;
329					break;
330				}
331			}
332		}
333		if (error) {
334			FREE(nfs_pub.np_index, M_TEMP);
335			return (error);
336		}
337	}
338
339	nfs_pub.np_mount = mp;
340	nfs_pub.np_valid = 1;
341	return (0);
342}
343
344/*
345 * Used by the filesystems to determine if a given network address
346 * (passed in 'nam') is present in thier exports list, returns a pointer
347 * to struct netcred so that the filesystem can examine it for
348 * access rights (read/write/etc).
349 */
350static struct netcred *
351vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
352{
353	struct netexport *nep;
354	register struct netcred *np;
355	register struct radix_node_head *rnh;
356	struct sockaddr *saddr;
357
358	nep = mp->mnt_export;
359	if (nep == NULL)
360		return (NULL);
361	np = NULL;
362	if (mp->mnt_flag & MNT_EXPORTED) {
363		/*
364		 * Lookup in the export list first.
365		 */
366		if (nam != NULL) {
367			saddr = nam;
368			rnh = nep->ne_rtable[saddr->sa_family];
369			if (rnh != NULL) {
370				RADIX_NODE_HEAD_LOCK(rnh);
371				np = (struct netcred *)
372				    (*rnh->rnh_matchaddr)(saddr, rnh);
373				RADIX_NODE_HEAD_UNLOCK(rnh);
374				if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
375					np = NULL;
376			}
377		}
378		/*
379		 * If no address match, use the default if it exists.
380		 */
381		if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
382			np = &nep->ne_defexported;
383	}
384	return (np);
385}
386
387/*
388 * XXX: This comment comes from the deprecated ufs_check_export()
389 * XXX: and may not entirely apply, but lacking something better:
390 * This is the generic part of fhtovp called after the underlying
391 * filesystem has validated the file handle.
392 *
393 * Verify that a host should have access to a filesystem.
394 */
395
396int
397vfs_stdcheckexp(mp, nam, extflagsp, credanonp)
398	struct mount *mp;
399	struct sockaddr *nam;
400	int *extflagsp;
401	struct ucred **credanonp;
402{
403	struct netcred *np;
404
405	np = vfs_export_lookup(mp, nam);
406	if (np == NULL)
407		return (EACCES);
408	*extflagsp = np->netc_exflags;
409	*credanonp = &np->netc_anon;
410	return (0);
411}
412
413