xref: /freebsd/sys/kern/vfs_export.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)vfs_subr.c	8.31 (Berkeley) 5/26/95
39  * $FreeBSD$
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/socket.h>
46 #include <sys/malloc.h>
47 #include <sys/mbuf.h>
48 #include <sys/mount.h>
49 #include <net/radix.h>
50 #include <sys/domain.h>
51 #include <sys/dirent.h>
52 #include <sys/vnode.h>
53 
54 static MALLOC_DEFINE(M_NETADDR, "Export Host", "Export host address structure");
55 
56 static void	vfs_free_addrlist __P((struct netexport *nep));
57 static int	vfs_free_netcred __P((struct radix_node *rn, void *w));
58 static int	vfs_hang_addrlist __P((struct mount *mp, struct netexport *nep,
59 				       struct export_args *argp));
60 
61 /*
62  * Network address lookup element
63  */
64 struct netcred {
65 	struct	radix_node netc_rnodes[2];
66 	int	netc_exflags;
67 	struct	ucred netc_anon;
68 };
69 
70 /*
71  * Network export information
72  */
73 struct netexport {
74 	struct	netcred ne_defexported;		      /* Default export */
75 	struct	radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
76 };
77 
78 /*
79  * Build hash lists of net addresses and hang them off the mount point.
80  * Called by ufs_mount() to set up the lists of export addresses.
81  */
82 static int
83 vfs_hang_addrlist(mp, nep, argp)
84 	struct mount *mp;
85 	struct netexport *nep;
86 	struct export_args *argp;
87 {
88 	register struct netcred *np;
89 	register struct radix_node_head *rnh;
90 	register int i;
91 	struct radix_node *rn;
92 	struct sockaddr *saddr, *smask = 0;
93 	struct domain *dom;
94 	int error;
95 
96 	if (argp->ex_addrlen == 0) {
97 		if (mp->mnt_flag & MNT_DEFEXPORTED)
98 			return (EPERM);
99 		np = &nep->ne_defexported;
100 		np->netc_exflags = argp->ex_flags;
101 		bzero(&np->netc_anon, sizeof(np->netc_anon));
102 		np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
103 		np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
104 		bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
105 		    sizeof(np->netc_anon.cr_groups));
106 		np->netc_anon.cr_ref = 1;
107 		mp->mnt_flag |= MNT_DEFEXPORTED;
108 		return (0);
109 	}
110 
111 	if (argp->ex_addrlen > MLEN)
112 		return (EINVAL);
113 
114 	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
115 	np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
116 	saddr = (struct sockaddr *) (np + 1);
117 	if ((error = copyin(argp->ex_addr, (caddr_t) saddr, argp->ex_addrlen)))
118 		goto out;
119 	if (saddr->sa_len > argp->ex_addrlen)
120 		saddr->sa_len = argp->ex_addrlen;
121 	if (argp->ex_masklen) {
122 		smask = (struct sockaddr *) ((caddr_t) saddr + argp->ex_addrlen);
123 		error = copyin(argp->ex_mask, (caddr_t) smask, argp->ex_masklen);
124 		if (error)
125 			goto out;
126 		if (smask->sa_len > argp->ex_masklen)
127 			smask->sa_len = argp->ex_masklen;
128 	}
129 	i = saddr->sa_family;
130 	if ((rnh = nep->ne_rtable[i]) == 0) {
131 		/*
132 		 * Seems silly to initialize every AF when most are not used,
133 		 * do so on demand here
134 		 */
135 		for (dom = domains; dom; dom = dom->dom_next)
136 			if (dom->dom_family == i && dom->dom_rtattach) {
137 				dom->dom_rtattach((void **) &nep->ne_rtable[i],
138 				    dom->dom_rtoffset);
139 				break;
140 			}
141 		if ((rnh = nep->ne_rtable[i]) == 0) {
142 			error = ENOBUFS;
143 			goto out;
144 		}
145 	}
146 	rn = (*rnh->rnh_addaddr) ((caddr_t) saddr, (caddr_t) smask, rnh,
147 	    np->netc_rnodes);
148 	if (rn == 0 || np != (struct netcred *) rn) {	/* already exists */
149 		error = EPERM;
150 		goto out;
151 	}
152 	np->netc_exflags = argp->ex_flags;
153 	bzero(&np->netc_anon, sizeof(np->netc_anon));
154 	np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
155 	np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
156 	bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
157 	    sizeof(np->netc_anon.cr_groups));
158 	np->netc_anon.cr_ref = 1;
159 	return (0);
160 out:
161 	free(np, M_NETADDR);
162 	return (error);
163 }
164 
165 /* Helper for vfs_free_addrlist. */
166 /* ARGSUSED */
167 static int
168 vfs_free_netcred(rn, w)
169 	struct radix_node *rn;
170 	void *w;
171 {
172 	register struct radix_node_head *rnh = (struct radix_node_head *) w;
173 
174 	(*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh);
175 	free((caddr_t) rn, M_NETADDR);
176 	return (0);
177 }
178 
179 /*
180  * Free the net address hash lists that are hanging off the mount points.
181  */
182 static void
183 vfs_free_addrlist(nep)
184 	struct netexport *nep;
185 {
186 	register int i;
187 	register struct radix_node_head *rnh;
188 
189 	for (i = 0; i <= AF_MAX; i++)
190 		if ((rnh = nep->ne_rtable[i])) {
191 			(*rnh->rnh_walktree) (rnh, vfs_free_netcred,
192 			    (caddr_t) rnh);
193 			free((caddr_t) rnh, M_RTABLE);
194 			nep->ne_rtable[i] = 0;
195 		}
196 }
197 
198 /*
199  * High level function to manipulate export options on a mount point
200  * and the passed in netexport.
201  * Struct export_args *argp is the variable used to twiddle options,
202  * the structure is described in sys/mount.h
203  */
204 int
205 vfs_export(mp, argp)
206 	struct mount *mp;
207 	struct export_args *argp;
208 {
209 	struct netexport *nep;
210 	int error;
211 
212 	nep = mp->mnt_export;
213 	if (argp->ex_flags & MNT_DELEXPORT) {
214 		if (nep == NULL)
215 			return (ENOENT);
216 		if (mp->mnt_flag & MNT_EXPUBLIC) {
217 			vfs_setpublicfs(NULL, NULL, NULL);
218 			mp->mnt_flag &= ~MNT_EXPUBLIC;
219 		}
220 		vfs_free_addrlist(nep);
221 		mp->mnt_export = NULL;
222 		free(nep, M_MOUNT);
223 		mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
224 	}
225 	if (argp->ex_flags & MNT_EXPORTED) {
226 		if (nep == NULL) {
227 			nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
228 			mp->mnt_export = nep;
229 		}
230 		if (argp->ex_flags & MNT_EXPUBLIC) {
231 			if ((error = vfs_setpublicfs(mp, nep, argp)) != 0)
232 				return (error);
233 			mp->mnt_flag |= MNT_EXPUBLIC;
234 		}
235 		if ((error = vfs_hang_addrlist(mp, nep, argp)))
236 			return (error);
237 		mp->mnt_flag |= MNT_EXPORTED;
238 	}
239 	return (0);
240 }
241 
242 /*
243  * Set the publicly exported filesystem (WebNFS). Currently, only
244  * one public filesystem is possible in the spec (RFC 2054 and 2055)
245  */
246 int
247 vfs_setpublicfs(mp, nep, argp)
248 	struct mount *mp;
249 	struct netexport *nep;
250 	struct export_args *argp;
251 {
252 	int error;
253 	struct vnode *rvp;
254 	char *cp;
255 
256 	/*
257 	 * mp == NULL -> invalidate the current info, the FS is
258 	 * no longer exported. May be called from either vfs_export
259 	 * or unmount, so check if it hasn't already been done.
260 	 */
261 	if (mp == NULL) {
262 		if (nfs_pub.np_valid) {
263 			nfs_pub.np_valid = 0;
264 			if (nfs_pub.np_index != NULL) {
265 				FREE(nfs_pub.np_index, M_TEMP);
266 				nfs_pub.np_index = NULL;
267 			}
268 		}
269 		return (0);
270 	}
271 
272 	/*
273 	 * Only one allowed at a time.
274 	 */
275 	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
276 		return (EBUSY);
277 
278 	/*
279 	 * Get real filehandle for root of exported FS.
280 	 */
281 	bzero((caddr_t)&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
282 	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
283 
284 	if ((error = VFS_ROOT(mp, &rvp)))
285 		return (error);
286 
287 	if ((error = VFS_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
288 		return (error);
289 
290 	vput(rvp);
291 
292 	/*
293 	 * If an indexfile was specified, pull it in.
294 	 */
295 	if (argp->ex_indexfile != NULL) {
296 		MALLOC(nfs_pub.np_index, char *, MAXNAMLEN + 1, M_TEMP,
297 		    M_WAITOK);
298 		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
299 		    MAXNAMLEN, (size_t *)0);
300 		if (!error) {
301 			/*
302 			 * Check for illegal filenames.
303 			 */
304 			for (cp = nfs_pub.np_index; *cp; cp++) {
305 				if (*cp == '/') {
306 					error = EINVAL;
307 					break;
308 				}
309 			}
310 		}
311 		if (error) {
312 			FREE(nfs_pub.np_index, M_TEMP);
313 			return (error);
314 		}
315 	}
316 
317 	nfs_pub.np_mount = mp;
318 	nfs_pub.np_valid = 1;
319 	return (0);
320 }
321 
322 /*
323  * Used by the filesystems to determine if a given network address
324  * (passed in 'nam') is present in thier exports list, returns a pointer
325  * to struct netcred so that the filesystem can examine it for
326  * access rights (read/write/etc).
327  */
328 struct netcred *
329 vfs_export_lookup(mp, nam)
330 	register struct mount *mp;
331 	struct sockaddr *nam;
332 {
333 	struct netexport *nep;
334 	register struct netcred *np;
335 	register struct radix_node_head *rnh;
336 	struct sockaddr *saddr;
337 
338 	nep = mp->mnt_export;
339 	if (nep == NULL)
340 		return (NULL);
341 	np = NULL;
342 	if (mp->mnt_flag & MNT_EXPORTED) {
343 		/*
344 		 * Lookup in the export list first.
345 		 */
346 		if (nam != NULL) {
347 			saddr = nam;
348 			rnh = nep->ne_rtable[saddr->sa_family];
349 			if (rnh != NULL) {
350 				np = (struct netcred *)
351 					(*rnh->rnh_matchaddr)((caddr_t)saddr,
352 							      rnh);
353 				if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
354 					np = NULL;
355 			}
356 		}
357 		/*
358 		 * If no address match, use the default if it exists.
359 		 */
360 		if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
361 			np = &nep->ne_defexported;
362 	}
363 	return (np);
364 }
365 
366 /*
367  * XXX: This comment comes from the deprecated ufs_check_export()
368  * XXX: and may not entirely apply, but lacking something better:
369  * This is the generic part of fhtovp called after the underlying
370  * filesystem has validated the file handle.
371  *
372  * Verify that a host should have access to a filesystem.
373  */
374 
375 int
376 vfs_stdcheckexp(mp, nam, extflagsp, credanonp)
377 	struct mount *mp;
378 	struct sockaddr *nam;
379 	int *extflagsp;
380 	struct ucred **credanonp;
381 {
382 	struct netcred *np;
383 
384 	np = vfs_export_lookup(mp, nam);
385 	if (np == NULL)
386 		return (EACCES);
387 	*extflagsp = np->netc_exflags;
388 	*credanonp = &np->netc_anon;
389 	return (0);
390 }
391 
392