1 | /* $NetBSD: nfs_srvsubs.c,v 1.14 2012/11/05 19:06:27 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.14 2012/11/05 19:06:27 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) > NFS_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_atdir = NULL; |
245 | ndp->ni_pathbuf = pathbuf_assimilate(path); |
246 | if (ndp->ni_pathbuf == NULL) { |
247 | error = ENOMEM; |
248 | goto out; |
249 | } |
250 | |
251 | if (pubflag) { |
252 | if (path[0] == '/') |
253 | dp = rootvnode; |
254 | } else { |
255 | cnp->cn_flags |= NOCROSSMOUNT; |
256 | } |
257 | |
258 | neverfollow = !pubflag; |
259 | |
260 | /* |
261 | * And call lookup() to do the real work |
262 | * |
263 | * Note: ndp->ni_pathbuf is left undestroyed on success; |
264 | * caller must clean it up. |
265 | */ |
266 | error = lookup_for_nfsd(ndp, dp, neverfollow); |
267 | if (error) { |
268 | goto out; |
269 | } |
270 | return 0; |
271 | |
272 | out: |
273 | if (ndp->ni_pathbuf != NULL) { |
274 | pathbuf_destroy(ndp->ni_pathbuf); |
275 | ndp->ni_pathbuf = NULL; |
276 | } else { |
277 | PNBUF_PUT(path); |
278 | } |
279 | return (error); |
280 | } |
281 | |
282 | /* |
283 | * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked) |
284 | * - look up fsid in mount list (if not found ret error) |
285 | * - get vp and export rights by calling VFS_FHTOVP() |
286 | * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon |
287 | * - if not lockflag unlock it with VOP_UNLOCK() |
288 | */ |
289 | int |
290 | nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp, |
291 | kauth_cred_t cred, struct nfssvc_sock *slp, struct mbuf *nam, int *rdonlyp, |
292 | int kerbflag, int pubflag) |
293 | { |
294 | struct mount *mp; |
295 | kauth_cred_t credanon; |
296 | int error, exflags; |
297 | struct sockaddr_in *saddr; |
298 | fhandle_t *fhp; |
299 | |
300 | fhp = NFSRVFH_FHANDLE(nsfh); |
301 | *vpp = (struct vnode *)0; |
302 | |
303 | if (nfs_ispublicfh(nsfh)) { |
304 | if (!pubflag || !nfs_pub.np_valid) |
305 | return (ESTALE); |
306 | fhp = nfs_pub.np_handle; |
307 | } |
308 | |
309 | error = netexport_check(&fhp->fh_fsid, nam, &mp, &exflags, &credanon); |
310 | if (error) { |
311 | return error; |
312 | } |
313 | |
314 | error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp); |
315 | if (error) |
316 | return (error); |
317 | |
318 | if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) { |
319 | saddr = mtod(nam, struct sockaddr_in *); |
320 | if ((saddr->sin_family == AF_INET) && |
321 | ntohs(saddr->sin_port) >= IPPORT_RESERVED) { |
322 | vput(*vpp); |
323 | return (NFSERR_AUTHERR | AUTH_TOOWEAK); |
324 | } |
325 | if ((saddr->sin_family == AF_INET6) && |
326 | ntohs(saddr->sin_port) >= IPV6PORT_RESERVED) { |
327 | vput(*vpp); |
328 | return (NFSERR_AUTHERR | AUTH_TOOWEAK); |
329 | } |
330 | } |
331 | /* |
332 | * Check/setup credentials. |
333 | */ |
334 | if (exflags & MNT_EXKERB) { |
335 | if (!kerbflag) { |
336 | vput(*vpp); |
337 | return (NFSERR_AUTHERR | AUTH_TOOWEAK); |
338 | } |
339 | } else if (kerbflag) { |
340 | vput(*vpp); |
341 | return (NFSERR_AUTHERR | AUTH_TOOWEAK); |
342 | } else if (kauth_cred_geteuid(cred) == 0 || /* NFS maproot, see below */ |
343 | (exflags & MNT_EXPORTANON)) { |
344 | /* |
345 | * This is used by the NFS maproot option. While we can change |
346 | * the secmodel on our own host, we can't change it on the |
347 | * clients. As means of least surprise, we're doing the |
348 | * traditional thing here. |
349 | * Should look into adding a "mapprivileged" or similar where |
350 | * the users can be explicitly specified... |
351 | * [elad, yamt 2008-03-05] |
352 | */ |
353 | kauth_cred_clone(credanon, cred); |
354 | } |
355 | if (exflags & MNT_EXRDONLY) |
356 | *rdonlyp = 1; |
357 | else |
358 | *rdonlyp = 0; |
359 | if (!lockflag) |
360 | VOP_UNLOCK(*vpp); |
361 | return (0); |
362 | } |
363 | |
364 | /* |
365 | * WebNFS: check if a filehandle is a public filehandle. For v3, this |
366 | * means a length of 0, for v2 it means all zeroes. |
367 | */ |
368 | int |
369 | nfs_ispublicfh(const nfsrvfh_t *nsfh) |
370 | { |
371 | const char *cp = (const void *)(NFSRVFH_DATA(nsfh)); |
372 | int i; |
373 | |
374 | if (NFSRVFH_SIZE(nsfh) == 0) { |
375 | return true; |
376 | } |
377 | if (NFSRVFH_SIZE(nsfh) != NFSX_V2FH) { |
378 | return false; |
379 | } |
380 | for (i = 0; i < NFSX_V2FH; i++) |
381 | if (*cp++ != 0) |
382 | return false; |
383 | return true; |
384 | } |
385 | |
386 | int |
387 | nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh, bool v3) |
388 | { |
389 | int error; |
390 | size_t fhsize; |
391 | |
392 | fhsize = NFSD_MAXFHSIZE; |
393 | error = vfs_composefh(vp, (void *)NFSRVFH_DATA(nsfh), &fhsize); |
394 | if (NFSX_FHTOOBIG_P(fhsize, v3)) { |
395 | error = EOPNOTSUPP; |
396 | } |
397 | if (error != 0) { |
398 | return error; |
399 | } |
400 | if (!v3 && fhsize < NFSX_V2FH) { |
401 | memset((char *)NFSRVFH_DATA(nsfh) + fhsize, 0, |
402 | NFSX_V2FH - fhsize); |
403 | fhsize = NFSX_V2FH; |
404 | } |
405 | if ((fhsize % NFSX_UNSIGNED) != 0) { |
406 | return EOPNOTSUPP; |
407 | } |
408 | nsfh->nsfh_size = fhsize; |
409 | return 0; |
410 | } |
411 | |
412 | int |
413 | nfsrv_comparefh(const nfsrvfh_t *fh1, const nfsrvfh_t *fh2) |
414 | { |
415 | |
416 | if (NFSRVFH_SIZE(fh1) != NFSRVFH_SIZE(fh2)) { |
417 | return NFSRVFH_SIZE(fh2) - NFSRVFH_SIZE(fh1); |
418 | } |
419 | return memcmp(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), NFSRVFH_SIZE(fh1)); |
420 | } |
421 | |
422 | void |
423 | nfsrv_copyfh(nfsrvfh_t *fh1, const nfsrvfh_t *fh2) |
424 | { |
425 | size_t size; |
426 | |
427 | fh1->nsfh_size = size = NFSRVFH_SIZE(fh2); |
428 | memcpy(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), size); |
429 | } |
430 | |