1 /* $NetBSD: nfs_srvsubs.c,v 1.1 2008/11/19 18:36:09 ad Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Rick Macklem at The University of Guelph. 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. 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 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95 35 */ 36 37 /* 38 * Copyright 2000 Wasabi Systems, Inc. 39 * All rights reserved. 40 * 41 * Written by Frank van der Linden for Wasabi Systems, Inc. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed for the NetBSD Project by 54 * Wasabi Systems, Inc. 55 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 56 * or promote products derived from this software without specific prior 57 * written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 69 * POSSIBILITY OF SUCH DAMAGE. 70 */ 71 72 #include <sys/cdefs.h> 73 __KERNEL_RCSID(0, "$NetBSD: nfs_srvsubs.c,v 1.1 2008/11/19 18:36:09 ad Exp $"); 74 75 #include <sys/param.h> 76 #include <sys/proc.h> 77 #include <sys/systm.h> 78 #include <sys/kernel.h> 79 #include <sys/kmem.h> 80 #include <sys/mount.h> 81 #include <sys/vnode.h> 82 #include <sys/namei.h> 83 #include <sys/mbuf.h> 84 #include <sys/socket.h> 85 #include <sys/stat.h> 86 #include <sys/filedesc.h> 87 #include <sys/time.h> 88 #include <sys/dirent.h> 89 #include <sys/once.h> 90 #include <sys/kauth.h> 91 #include <sys/atomic.h> 92 93 #include <uvm/uvm_extern.h> 94 95 #include <nfs/rpcv2.h> 96 #include <nfs/nfsproto.h> 97 #include <nfs/nfsnode.h> 98 #include <nfs/nfs.h> 99 #include <nfs/xdr_subs.h> 100 #include <nfs/nfsm_subs.h> 101 #include <nfs/nfsmount.h> 102 #include <nfs/nfsrtt.h> 103 #include <nfs/nfs_var.h> 104 105 #include <miscfs/specfs/specdev.h> 106 107 #include <netinet/in.h> 108 109 /* 110 * Set up nameidata for a lookup() call and do it. 111 * 112 * If pubflag is set, this call is done for a lookup operation on the 113 * public filehandle. In that case we allow crossing mountpoints and 114 * absolute pathnames. However, the caller is expected to check that 115 * the lookup result is within the public fs, and deny access if 116 * it is not. 117 */ 118 int 119 nfs_namei(ndp, nsfh, len, slp, nam, mdp, dposp, retdirp, l, kerbflag, pubflag) 120 struct nameidata *ndp; 121 nfsrvfh_t *nsfh; 122 uint32_t len; 123 struct nfssvc_sock *slp; 124 struct mbuf *nam; 125 struct mbuf **mdp; 126 char **dposp; 127 struct vnode **retdirp; 128 struct lwp *l; 129 int kerbflag, pubflag; 130 { 131 int i, rem; 132 struct mbuf *md; 133 char *fromcp, *tocp, *cp; 134 struct iovec aiov; 135 struct uio auio; 136 struct vnode *dp; 137 int error, rdonly, linklen; 138 struct componentname *cnp = &ndp->ni_cnd; 139 140 *retdirp = NULL; 141 142 if ((len + 1) > MAXPATHLEN) 143 return (ENAMETOOLONG); 144 if (len == 0) 145 return (EACCES); 146 cnp->cn_pnbuf = PNBUF_GET(); 147 148 /* 149 * Copy the name from the mbuf list to ndp->ni_pnbuf 150 * and set the various ndp fields appropriately. 151 */ 152 fromcp = *dposp; 153 tocp = cnp->cn_pnbuf; 154 md = *mdp; 155 rem = mtod(md, char *) + md->m_len - fromcp; 156 for (i = 0; i < len; i++) { 157 while (rem == 0) { 158 md = md->m_next; 159 if (md == NULL) { 160 error = EBADRPC; 161 goto out; 162 } 163 fromcp = mtod(md, void *); 164 rem = md->m_len; 165 } 166 if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) { 167 error = EACCES; 168 goto out; 169 } 170 *tocp++ = *fromcp++; 171 rem--; 172 } 173 *tocp = '\0'; 174 *mdp = md; 175 *dposp = fromcp; 176 len = nfsm_rndup(len)-len; 177 if (len > 0) { 178 if (rem >= len) 179 *dposp += len; 180 else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0) 181 goto out; 182 } 183 184 /* 185 * Extract and set starting directory. 186 */ 187 error = nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp, 188 nam, &rdonly, kerbflag, pubflag); 189 if (error) 190 goto out; 191 if (dp->v_type != VDIR) { 192 vrele(dp); 193 error = ENOTDIR; 194 goto out; 195 } 196 197 if (rdonly) 198 cnp->cn_flags |= RDONLY; 199 200 *retdirp = dp; 201 202 if (pubflag) { 203 /* 204 * Oh joy. For WebNFS, handle those pesky '%' escapes, 205 * and the 'native path' indicator. 206 */ 207 cp = PNBUF_GET(); 208 fromcp = cnp->cn_pnbuf; 209 tocp = cp; 210 if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) { 211 switch ((unsigned char)*fromcp) { 212 case WEBNFS_NATIVE_CHAR: 213 /* 214 * 'Native' path for us is the same 215 * as a path according to the NFS spec, 216 * just skip the escape char. 217 */ 218 fromcp++; 219 break; 220 /* 221 * More may be added in the future, range 0x80-0xff 222 */ 223 default: 224 error = EIO; 225 vrele(dp); 226 PNBUF_PUT(cp); 227 goto out; 228 } 229 } 230 /* 231 * Translate the '%' escapes, URL-style. 232 */ 233 while (*fromcp != '\0') { 234 if (*fromcp == WEBNFS_ESC_CHAR) { 235 if (fromcp[1] != '\0' && fromcp[2] != '\0') { 236 fromcp++; 237 *tocp++ = HEXSTRTOI(fromcp); 238 fromcp += 2; 239 continue; 240 } else { 241 error = ENOENT; 242 vrele(dp); 243 PNBUF_PUT(cp); 244 goto out; 245 } 246 } else 247 *tocp++ = *fromcp++; 248 } 249 *tocp = '\0'; 250 PNBUF_PUT(cnp->cn_pnbuf); 251 cnp->cn_pnbuf = cp; 252 } 253 254 ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1; 255 ndp->ni_segflg = UIO_SYSSPACE; 256 ndp->ni_rootdir = rootvnode; 257 ndp->ni_erootdir = NULL; 258 259 if (pubflag) { 260 ndp->ni_loopcnt = 0; 261 if (cnp->cn_pnbuf[0] == '/') 262 dp = rootvnode; 263 } else { 264 cnp->cn_flags |= NOCROSSMOUNT; 265 } 266 267 VREF(dp); 268 vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); 269 270 for (;;) { 271 cnp->cn_nameptr = cnp->cn_pnbuf; 272 ndp->ni_startdir = dp; 273 274 /* 275 * And call lookup() to do the real work 276 */ 277 error = lookup(ndp); 278 if (error) { 279 if (ndp->ni_dvp) { 280 vput(ndp->ni_dvp); 281 } 282 PNBUF_PUT(cnp->cn_pnbuf); 283 return (error); 284 } 285 286 /* 287 * Check for encountering a symbolic link 288 */ 289 if ((cnp->cn_flags & ISSYMLINK) == 0) { 290 if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp) { 291 if (ndp->ni_dvp == ndp->ni_vp) { 292 vrele(ndp->ni_dvp); 293 } else { 294 vput(ndp->ni_dvp); 295 } 296 } 297 if (cnp->cn_flags & (SAVENAME | SAVESTART)) 298 cnp->cn_flags |= HASBUF; 299 else 300 PNBUF_PUT(cnp->cn_pnbuf); 301 return (0); 302 } else { 303 if (!pubflag) { 304 error = EINVAL; 305 break; 306 } 307 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { 308 error = ELOOP; 309 break; 310 } 311 if (ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) { 312 error = VOP_ACCESS(ndp->ni_vp, VEXEC, cnp->cn_cred); 313 if (error != 0) 314 break; 315 } 316 if (ndp->ni_pathlen > 1) 317 cp = PNBUF_GET(); 318 else 319 cp = cnp->cn_pnbuf; 320 aiov.iov_base = cp; 321 aiov.iov_len = MAXPATHLEN; 322 auio.uio_iov = &aiov; 323 auio.uio_iovcnt = 1; 324 auio.uio_offset = 0; 325 auio.uio_rw = UIO_READ; 326 auio.uio_resid = MAXPATHLEN; 327 UIO_SETUP_SYSSPACE(&auio); 328 error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); 329 if (error) { 330 badlink: 331 if (ndp->ni_pathlen > 1) 332 PNBUF_PUT(cp); 333 break; 334 } 335 linklen = MAXPATHLEN - auio.uio_resid; 336 if (linklen == 0) { 337 error = ENOENT; 338 goto badlink; 339 } 340 if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { 341 error = ENAMETOOLONG; 342 goto badlink; 343 } 344 if (ndp->ni_pathlen > 1) { 345 memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen); 346 PNBUF_PUT(cnp->cn_pnbuf); 347 cnp->cn_pnbuf = cp; 348 } else 349 cnp->cn_pnbuf[linklen] = '\0'; 350 ndp->ni_pathlen += linklen; 351 vput(ndp->ni_vp); 352 dp = ndp->ni_dvp; 353 354 /* 355 * Check if root directory should replace current directory. 356 */ 357 if (cnp->cn_pnbuf[0] == '/') { 358 vput(dp); 359 dp = ndp->ni_rootdir; 360 VREF(dp); 361 vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); 362 } 363 } 364 } 365 vput(ndp->ni_dvp); 366 vput(ndp->ni_vp); 367 ndp->ni_vp = NULL; 368 out: 369 PNBUF_PUT(cnp->cn_pnbuf); 370 return (error); 371 } 372 373 /* 374 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked) 375 * - look up fsid in mount list (if not found ret error) 376 * - get vp and export rights by calling VFS_FHTOVP() 377 * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon 378 * - if not lockflag unlock it with VOP_UNLOCK() 379 */ 380 int 381 nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp, 382 kauth_cred_t cred, struct nfssvc_sock *slp, struct mbuf *nam, int *rdonlyp, 383 int kerbflag, int pubflag) 384 { 385 struct mount *mp; 386 kauth_cred_t credanon; 387 int error, exflags; 388 struct sockaddr_in *saddr; 389 fhandle_t *fhp; 390 391 fhp = NFSRVFH_FHANDLE(nsfh); 392 *vpp = (struct vnode *)0; 393 394 if (nfs_ispublicfh(nsfh)) { 395 if (!pubflag || !nfs_pub.np_valid) 396 return (ESTALE); 397 fhp = nfs_pub.np_handle; 398 } 399 400 error = netexport_check(&fhp->fh_fsid, nam, &mp, &exflags, &credanon); 401 if (error) { 402 return error; 403 } 404 405 error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp); 406 if (error) 407 return (error); 408 409 if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) { 410 saddr = mtod(nam, struct sockaddr_in *); 411 if ((saddr->sin_family == AF_INET) && 412 ntohs(saddr->sin_port) >= IPPORT_RESERVED) { 413 vput(*vpp); 414 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 415 } 416 if ((saddr->sin_family == AF_INET6) && 417 ntohs(saddr->sin_port) >= IPV6PORT_RESERVED) { 418 vput(*vpp); 419 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 420 } 421 } 422 /* 423 * Check/setup credentials. 424 */ 425 if (exflags & MNT_EXKERB) { 426 if (!kerbflag) { 427 vput(*vpp); 428 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 429 } 430 } else if (kerbflag) { 431 vput(*vpp); 432 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 433 } else if (kauth_cred_geteuid(cred) == 0 || /* NFS maproot, see below */ 434 (exflags & MNT_EXPORTANON)) { 435 /* 436 * This is used by the NFS maproot option. While we can change 437 * the secmodel on our own host, we can't change it on the 438 * clients. As means of least surprise, we're doing the 439 * traditional thing here. 440 * Should look into adding a "mapprivileged" or similar where 441 * the users can be explicitly specified... 442 * [elad, yamt 2008-03-05] 443 */ 444 kauth_cred_clone(credanon, cred); 445 } 446 if (exflags & MNT_EXRDONLY) 447 *rdonlyp = 1; 448 else 449 *rdonlyp = 0; 450 if (!lockflag) 451 VOP_UNLOCK(*vpp, 0); 452 return (0); 453 } 454 455 /* 456 * WebNFS: check if a filehandle is a public filehandle. For v3, this 457 * means a length of 0, for v2 it means all zeroes. 458 */ 459 int 460 nfs_ispublicfh(const nfsrvfh_t *nsfh) 461 { 462 const char *cp = (const void *)(NFSRVFH_DATA(nsfh)); 463 int i; 464 465 if (NFSRVFH_SIZE(nsfh) == 0) { 466 return true; 467 } 468 if (NFSRVFH_SIZE(nsfh) != NFSX_V2FH) { 469 return false; 470 } 471 for (i = 0; i < NFSX_V2FH; i++) 472 if (*cp++ != 0) 473 return false; 474 return true; 475 } 476 477 int 478 nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh, bool v3) 479 { 480 int error; 481 size_t fhsize; 482 483 fhsize = NFSD_MAXFHSIZE; 484 error = vfs_composefh(vp, (void *)NFSRVFH_DATA(nsfh), &fhsize); 485 if (NFSX_FHTOOBIG_P(fhsize, v3)) { 486 error = EOPNOTSUPP; 487 } 488 if (error != 0) { 489 return error; 490 } 491 if (!v3 && fhsize < NFSX_V2FH) { 492 memset((char *)NFSRVFH_DATA(nsfh) + fhsize, 0, 493 NFSX_V2FH - fhsize); 494 fhsize = NFSX_V2FH; 495 } 496 if ((fhsize % NFSX_UNSIGNED) != 0) { 497 return EOPNOTSUPP; 498 } 499 nsfh->nsfh_size = fhsize; 500 return 0; 501 } 502 503 int 504 nfsrv_comparefh(const nfsrvfh_t *fh1, const nfsrvfh_t *fh2) 505 { 506 507 if (NFSRVFH_SIZE(fh1) != NFSRVFH_SIZE(fh2)) { 508 return NFSRVFH_SIZE(fh2) - NFSRVFH_SIZE(fh1); 509 } 510 return memcmp(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), NFSRVFH_SIZE(fh1)); 511 } 512 513 void 514 nfsrv_copyfh(nfsrvfh_t *fh1, const nfsrvfh_t *fh2) 515 { 516 size_t size; 517 518 fh1->nsfh_size = size = NFSRVFH_SIZE(fh2); 519 memcpy(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), size); 520 } 521