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, 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); 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, vfs_free_netcred, rnh); 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); 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