1 /* $NetBSD: nfs_srvsubs.c,v 1.8 2010/11/30 10:30:03 dholland 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.8 2010/11/30 10:30:03 dholland 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(struct nameidata *ndp, nfsrvfh_t *nsfh, uint32_t len, struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp, char **dposp, struct vnode **retdirp, struct lwp *l, int kerbflag, int pubflag) 120 { 121 int i, rem; 122 struct mbuf *md; 123 char *fromcp, *tocp, *cp, *path; 124 struct vnode *dp; 125 int error, rdonly; 126 int neverfollow; 127 struct componentname *cnp = &ndp->ni_cnd; 128 129 *retdirp = NULL; 130 ndp->ni_pathbuf = NULL; 131 132 if ((len + 1) > MAXPATHLEN) 133 return (ENAMETOOLONG); 134 if (len == 0) 135 return (EACCES); 136 137 /* 138 * Copy the name from the mbuf list to ndp->ni_pathbuf 139 * and set the various ndp fields appropriately. 140 */ 141 path = PNBUF_GET(); 142 fromcp = *dposp; 143 tocp = path; 144 md = *mdp; 145 rem = mtod(md, char *) + md->m_len - fromcp; 146 for (i = 0; i < len; i++) { 147 while (rem == 0) { 148 md = md->m_next; 149 if (md == NULL) { 150 error = EBADRPC; 151 goto out; 152 } 153 fromcp = mtod(md, void *); 154 rem = md->m_len; 155 } 156 if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) { 157 error = EACCES; 158 goto out; 159 } 160 *tocp++ = *fromcp++; 161 rem--; 162 } 163 *tocp = '\0'; 164 *mdp = md; 165 *dposp = fromcp; 166 len = nfsm_rndup(len)-len; 167 if (len > 0) { 168 if (rem >= len) 169 *dposp += len; 170 else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0) 171 goto out; 172 } 173 174 /* 175 * Extract and set starting directory. 176 */ 177 error = nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp, 178 nam, &rdonly, kerbflag, pubflag); 179 if (error) 180 goto out; 181 if (dp->v_type != VDIR) { 182 vrele(dp); 183 error = ENOTDIR; 184 goto out; 185 } 186 187 if (rdonly) 188 cnp->cn_flags |= RDONLY; 189 190 *retdirp = dp; 191 192 if (pubflag) { 193 /* 194 * Oh joy. For WebNFS, handle those pesky '%' escapes, 195 * and the 'native path' indicator. 196 */ 197 cp = PNBUF_GET(); 198 fromcp = path; 199 tocp = cp; 200 if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) { 201 switch ((unsigned char)*fromcp) { 202 case WEBNFS_NATIVE_CHAR: 203 /* 204 * 'Native' path for us is the same 205 * as a path according to the NFS spec, 206 * just skip the escape char. 207 */ 208 fromcp++; 209 break; 210 /* 211 * More may be added in the future, range 0x80-0xff 212 */ 213 default: 214 error = EIO; 215 vrele(dp); 216 PNBUF_PUT(cp); 217 goto out; 218 } 219 } 220 /* 221 * Translate the '%' escapes, URL-style. 222 */ 223 while (*fromcp != '\0') { 224 if (*fromcp == WEBNFS_ESC_CHAR) { 225 if (fromcp[1] != '\0' && fromcp[2] != '\0') { 226 fromcp++; 227 *tocp++ = HEXSTRTOI(fromcp); 228 fromcp += 2; 229 continue; 230 } else { 231 error = ENOENT; 232 vrele(dp); 233 PNBUF_PUT(cp); 234 goto out; 235 } 236 } else 237 *tocp++ = *fromcp++; 238 } 239 *tocp = '\0'; 240 PNBUF_PUT(path); 241 path = cp; 242 } 243 244 ndp->ni_pathbuf = pathbuf_assimilate(path); 245 if (ndp->ni_pathbuf == NULL) { 246 error = ENOMEM; 247 goto out; 248 } 249 ndp->ni_pathlen = (tocp - path) + 1; 250 /*ndp->ni_segflg = UIO_SYSSPACE; - obsolete */ 251 ndp->ni_rootdir = rootvnode; 252 ndp->ni_erootdir = NULL; 253 254 if (pubflag) { 255 ndp->ni_loopcnt = 0; 256 if (path[0] == '/') 257 dp = rootvnode; 258 } else { 259 cnp->cn_flags |= NOCROSSMOUNT; 260 } 261 262 neverfollow = !pubflag; 263 264 /* 265 * And call lookup() to do the real work 266 * 267 * Note: ndp->ni_pathbuf is left undestroyed; caller must 268 * clean it up. 269 */ 270 error = lookup_for_nfsd(ndp, dp, neverfollow); 271 if (error) { 272 return (error); 273 } 274 return 0; 275 276 out: 277 if (ndp->ni_pathbuf != NULL) { 278 pathbuf_destroy(ndp->ni_pathbuf); 279 } else { 280 PNBUF_PUT(path); 281 } 282 return (error); 283 } 284 285 /* 286 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked) 287 * - look up fsid in mount list (if not found ret error) 288 * - get vp and export rights by calling VFS_FHTOVP() 289 * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon 290 * - if not lockflag unlock it with VOP_UNLOCK() 291 */ 292 int 293 nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp, 294 kauth_cred_t cred, struct nfssvc_sock *slp, struct mbuf *nam, int *rdonlyp, 295 int kerbflag, int pubflag) 296 { 297 struct mount *mp; 298 kauth_cred_t credanon; 299 int error, exflags; 300 struct sockaddr_in *saddr; 301 fhandle_t *fhp; 302 303 fhp = NFSRVFH_FHANDLE(nsfh); 304 *vpp = (struct vnode *)0; 305 306 if (nfs_ispublicfh(nsfh)) { 307 if (!pubflag || !nfs_pub.np_valid) 308 return (ESTALE); 309 fhp = nfs_pub.np_handle; 310 } 311 312 error = netexport_check(&fhp->fh_fsid, nam, &mp, &exflags, &credanon); 313 if (error) { 314 return error; 315 } 316 317 error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp); 318 if (error) 319 return (error); 320 321 if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) { 322 saddr = mtod(nam, struct sockaddr_in *); 323 if ((saddr->sin_family == AF_INET) && 324 ntohs(saddr->sin_port) >= IPPORT_RESERVED) { 325 vput(*vpp); 326 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 327 } 328 if ((saddr->sin_family == AF_INET6) && 329 ntohs(saddr->sin_port) >= IPV6PORT_RESERVED) { 330 vput(*vpp); 331 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 332 } 333 } 334 /* 335 * Check/setup credentials. 336 */ 337 if (exflags & MNT_EXKERB) { 338 if (!kerbflag) { 339 vput(*vpp); 340 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 341 } 342 } else if (kerbflag) { 343 vput(*vpp); 344 return (NFSERR_AUTHERR | AUTH_TOOWEAK); 345 } else if (kauth_cred_geteuid(cred) == 0 || /* NFS maproot, see below */ 346 (exflags & MNT_EXPORTANON)) { 347 /* 348 * This is used by the NFS maproot option. While we can change 349 * the secmodel on our own host, we can't change it on the 350 * clients. As means of least surprise, we're doing the 351 * traditional thing here. 352 * Should look into adding a "mapprivileged" or similar where 353 * the users can be explicitly specified... 354 * [elad, yamt 2008-03-05] 355 */ 356 kauth_cred_clone(credanon, cred); 357 } 358 if (exflags & MNT_EXRDONLY) 359 *rdonlyp = 1; 360 else 361 *rdonlyp = 0; 362 if (!lockflag) 363 VOP_UNLOCK(*vpp); 364 return (0); 365 } 366 367 /* 368 * WebNFS: check if a filehandle is a public filehandle. For v3, this 369 * means a length of 0, for v2 it means all zeroes. 370 */ 371 int 372 nfs_ispublicfh(const nfsrvfh_t *nsfh) 373 { 374 const char *cp = (const void *)(NFSRVFH_DATA(nsfh)); 375 int i; 376 377 if (NFSRVFH_SIZE(nsfh) == 0) { 378 return true; 379 } 380 if (NFSRVFH_SIZE(nsfh) != NFSX_V2FH) { 381 return false; 382 } 383 for (i = 0; i < NFSX_V2FH; i++) 384 if (*cp++ != 0) 385 return false; 386 return true; 387 } 388 389 int 390 nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh, bool v3) 391 { 392 int error; 393 size_t fhsize; 394 395 fhsize = NFSD_MAXFHSIZE; 396 error = vfs_composefh(vp, (void *)NFSRVFH_DATA(nsfh), &fhsize); 397 if (NFSX_FHTOOBIG_P(fhsize, v3)) { 398 error = EOPNOTSUPP; 399 } 400 if (error != 0) { 401 return error; 402 } 403 if (!v3 && fhsize < NFSX_V2FH) { 404 memset((char *)NFSRVFH_DATA(nsfh) + fhsize, 0, 405 NFSX_V2FH - fhsize); 406 fhsize = NFSX_V2FH; 407 } 408 if ((fhsize % NFSX_UNSIGNED) != 0) { 409 return EOPNOTSUPP; 410 } 411 nsfh->nsfh_size = fhsize; 412 return 0; 413 } 414 415 int 416 nfsrv_comparefh(const nfsrvfh_t *fh1, const nfsrvfh_t *fh2) 417 { 418 419 if (NFSRVFH_SIZE(fh1) != NFSRVFH_SIZE(fh2)) { 420 return NFSRVFH_SIZE(fh2) - NFSRVFH_SIZE(fh1); 421 } 422 return memcmp(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), NFSRVFH_SIZE(fh1)); 423 } 424 425 void 426 nfsrv_copyfh(nfsrvfh_t *fh1, const nfsrvfh_t *fh2) 427 { 428 size_t size; 429 430 fh1->nsfh_size = size = NFSRVFH_SIZE(fh2); 431 memcpy(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), size); 432 } 433