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/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 56 static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure"); 57 58 static void vfs_free_addrlist(struct netexport *nep); 59 static int vfs_free_netcred(struct radix_node *rn, void *w); 60 static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, 61 struct export_args *argp); 62 static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *); 63 64 /* 65 * Network address lookup element 66 */ 67 struct 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 */ 76 struct 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 */ 85 static int 86 vfs_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); 180 out: 181 free(np, M_NETADDR); 182 return (error); 183 } 184 185 /* Helper for vfs_free_addrlist. */ 186 /* ARGSUSED */ 187 static int 188 vfs_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 */ 202 static void 203 vfs_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 */ 225 int 226 vfs_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 */ 268 int 269 vfs_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 */ 350 static struct netcred * 351 vfs_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 396 int 397 vfs_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