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