1 /* $OpenBSD: nfs_srvsubs.c,v 1.2 2024/09/18 05:21:19 jsg Exp $ */
2 /* $NetBSD: nfs_subs.c,v 1.27.4.3 1996/07/08 20:34:24 jtc Exp $ */
3
4 /*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Rick Macklem at The University of Guelph.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95
36 */
37
38
39 /*
40 * These functions support the nfsm_subs.h inline functions and help fiddle
41 * mbuf chains for the nfs op functions. They do things such as creating the
42 * rpc header and copying data between mbuf chains and uio lists.
43 */
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/mount.h>
47 #include <sys/vnode.h>
48 #include <sys/namei.h>
49 #include <sys/mbuf.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52 #include <sys/pool.h>
53
54 #include <nfs/rpcv2.h>
55 #include <nfs/nfsproto.h>
56 #include <nfs/nfs.h>
57 #include <nfs/xdr_subs.h>
58 #include <nfs/nfs_var.h>
59 #include <nfs/nfsm_subs.h>
60
61 #include <netinet/in.h>
62
63 /* Global vars */
64 extern u_int32_t nfs_false, nfs_true;
65 extern const nfstype nfsv2_type[9];
66 extern const nfstype nfsv3_type[9];
67
68 /*
69 * Set up nameidata for a lookup() call and do it
70 */
71 int
nfs_namei(struct nameidata * ndp,fhandle_t * fhp,int len,struct nfssvc_sock * slp,struct mbuf * nam,struct mbuf ** mdp,caddr_t * dposp,struct vnode ** retdirp,struct proc * p)72 nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
73 struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp,
74 caddr_t *dposp, struct vnode **retdirp, struct proc *p)
75 {
76 int i, rem;
77 struct mbuf *md;
78 char *fromcp, *tocp;
79 struct vnode *dp;
80 int error, rdonly;
81 struct componentname *cnp = &ndp->ni_cnd;
82
83 *retdirp = NULL;
84 cnp->cn_pnbuf = pool_get(&namei_pool, PR_WAITOK);
85 /*
86 * Copy the name from the mbuf list to ndp->ni_pnbuf
87 * and set the various ndp fields appropriately.
88 */
89 fromcp = *dposp;
90 tocp = cnp->cn_pnbuf;
91 md = *mdp;
92 rem = mtod(md, caddr_t) + md->m_len - fromcp;
93 for (i = 0; i < len; i++) {
94 while (rem == 0) {
95 md = md->m_next;
96 if (md == NULL) {
97 error = EBADRPC;
98 goto out;
99 }
100 fromcp = mtod(md, caddr_t);
101 rem = md->m_len;
102 }
103 if (*fromcp == '\0' || *fromcp == '/') {
104 error = EACCES;
105 goto out;
106 }
107 *tocp++ = *fromcp++;
108 rem--;
109 }
110 *tocp = '\0';
111 *mdp = md;
112 *dposp = fromcp;
113 len = nfsm_padlen(len);
114 if (len > 0) {
115 if (rem >= len)
116 *dposp += len;
117 else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
118 goto out;
119 }
120 ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
121 cnp->cn_nameptr = cnp->cn_pnbuf;
122 /*
123 * Extract and set starting directory.
124 */
125 error = nfsrv_fhtovp(fhp, 0, &dp, ndp->ni_cnd.cn_cred, slp,
126 nam, &rdonly);
127 if (error)
128 goto out;
129 if (dp->v_type != VDIR) {
130 vrele(dp);
131 error = ENOTDIR;
132 goto out;
133 }
134 vref(dp);
135 *retdirp = dp;
136 ndp->ni_startdir = dp;
137 if (rdonly)
138 cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
139 else
140 cnp->cn_flags |= NOCROSSMOUNT;
141
142 /*
143 * And call lookup() to do the real work
144 */
145 cnp->cn_proc = p;
146 error = vfs_lookup(ndp);
147 if (error)
148 goto out;
149 /*
150 * Check for encountering a symbolic link
151 */
152 if (cnp->cn_flags & ISSYMLINK) {
153 if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
154 vput(ndp->ni_dvp);
155 else
156 vrele(ndp->ni_dvp);
157 vput(ndp->ni_vp);
158 ndp->ni_vp = NULL;
159 error = EINVAL;
160 goto out;
161 }
162 /*
163 * Check for saved name request
164 */
165 if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
166 cnp->cn_flags |= HASBUF;
167 return (0);
168 }
169 out:
170 pool_put(&namei_pool, cnp->cn_pnbuf);
171 return (error);
172 }
173
174 /*
175 * A fiddled version of m_adj() that ensures null fill to a long
176 * boundary and only trims off the back end
177 */
178 void
nfsm_adj(struct mbuf * mp,int len,int nul)179 nfsm_adj(struct mbuf *mp, int len, int nul)
180 {
181 struct mbuf *m;
182 int count, i;
183 char *cp;
184
185 /*
186 * Trim from tail. Scan the mbuf chain,
187 * calculating its length and finding the last mbuf.
188 * If the adjustment only affects this mbuf, then just
189 * adjust and return. Otherwise, rescan and truncate
190 * after the remaining size.
191 */
192 count = 0;
193 m = mp;
194 for (;;) {
195 count += m->m_len;
196 if (m->m_next == NULL)
197 break;
198 m = m->m_next;
199 }
200 if (m->m_len > len) {
201 m->m_len -= len;
202 if (nul > 0) {
203 cp = mtod(m, caddr_t)+m->m_len-nul;
204 for (i = 0; i < nul; i++)
205 *cp++ = '\0';
206 }
207 return;
208 }
209 count -= len;
210 if (count < 0)
211 count = 0;
212 /*
213 * Correct length for chain is "count".
214 * Find the mbuf with last data, adjust its length,
215 * and toss data from remaining mbufs on chain.
216 */
217 for (m = mp; m; m = m->m_next) {
218 if (m->m_len >= count) {
219 m->m_len = count;
220 if (nul > 0) {
221 cp = mtod(m, caddr_t)+m->m_len-nul;
222 for (i = 0; i < nul; i++)
223 *cp++ = '\0';
224 }
225 break;
226 }
227 count -= m->m_len;
228 }
229 for (m = m->m_next;m;m = m->m_next)
230 m->m_len = 0;
231 }
232
233 /*
234 * Make these non-inline functions, so that the kernel text size
235 * doesn't get too big...
236 */
237 void
nfsm_srvwcc(struct nfsrv_descript * nfsd,int before_ret,struct vattr * before_vap,int after_ret,struct vattr * after_vap,struct nfsm_info * info)238 nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
239 struct vattr *before_vap, int after_ret, struct vattr *after_vap,
240 struct nfsm_info *info)
241 {
242 u_int32_t *tl;
243
244 if (before_ret) {
245 tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED);
246 *tl = nfs_false;
247 } else {
248 tl = nfsm_build(&info->nmi_mb, 7 * NFSX_UNSIGNED);
249 *tl++ = nfs_true;
250 txdr_hyper(before_vap->va_size, tl);
251 tl += 2;
252 txdr_nfsv3time(&(before_vap->va_mtime), tl);
253 tl += 2;
254 txdr_nfsv3time(&(before_vap->va_ctime), tl);
255 }
256 nfsm_srvpostop_attr(nfsd, after_ret, after_vap, info);
257 }
258
259 void
nfsm_srvpostop_attr(struct nfsrv_descript * nfsd,int after_ret,struct vattr * after_vap,struct nfsm_info * info)260 nfsm_srvpostop_attr(struct nfsrv_descript *nfsd, int after_ret,
261 struct vattr *after_vap, struct nfsm_info *info)
262 {
263 u_int32_t *tl;
264 struct nfs_fattr *fp;
265
266 if (after_ret) {
267 tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED);
268 *tl = nfs_false;
269 } else {
270 tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED + NFSX_V3FATTR);
271 *tl++ = nfs_true;
272 fp = (struct nfs_fattr *)tl;
273 nfsm_srvfattr(nfsd, after_vap, fp);
274 }
275 }
276
277 void
nfsm_srvfattr(struct nfsrv_descript * nfsd,struct vattr * vap,struct nfs_fattr * fp)278 nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
279 struct nfs_fattr *fp)
280 {
281
282 fp->fa_nlink = txdr_unsigned(vap->va_nlink);
283 fp->fa_uid = txdr_unsigned(vap->va_uid);
284 fp->fa_gid = txdr_unsigned(vap->va_gid);
285 if (nfsd->nd_flag & ND_NFSV3) {
286 fp->fa_type = vtonfsv3_type(vap->va_type);
287 fp->fa_mode = vtonfsv3_mode(vap->va_mode);
288 txdr_hyper(vap->va_size, &fp->fa3_size);
289 txdr_hyper(vap->va_bytes, &fp->fa3_used);
290 fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
291 fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
292 fp->fa3_fsid.nfsuquad[0] = 0;
293 fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
294 txdr_hyper(vap->va_fileid, &fp->fa3_fileid);
295 txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
296 txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
297 txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
298 } else {
299 fp->fa_type = vtonfsv2_type(vap->va_type);
300 fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
301 fp->fa2_size = txdr_unsigned(vap->va_size);
302 fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
303 if (vap->va_type == VFIFO)
304 fp->fa2_rdev = 0xffffffff;
305 else
306 fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
307 fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
308 fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
309 fp->fa2_fileid = txdr_unsigned((u_int32_t)vap->va_fileid);
310 txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
311 txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
312 txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
313 }
314 }
315
316 /*
317 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
318 * - look up fsid in mount list (if not found ret error)
319 * - get vp and export rights by calling VFS_FHTOVP() and VFS_CHECKEXP()
320 * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
321 * - if not lockflag unlock it with VOP_UNLOCK()
322 */
323 int
nfsrv_fhtovp(fhandle_t * fhp,int lockflag,struct vnode ** vpp,struct ucred * cred,struct nfssvc_sock * slp,struct mbuf * nam,int * rdonlyp)324 nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
325 struct ucred *cred, struct nfssvc_sock *slp, struct mbuf *nam,
326 int *rdonlyp)
327 {
328 struct mount *mp;
329 int i;
330 struct ucred *credanon;
331 int error, exflags;
332 struct sockaddr_in *saddr;
333
334 *vpp = NULL;
335 mp = vfs_getvfs(&fhp->fh_fsid);
336
337 if (!mp)
338 return (ESTALE);
339 error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
340 if (error)
341 return (error);
342 error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
343 if (error)
344 return (error);
345
346 saddr = mtod(nam, struct sockaddr_in *);
347 if (saddr->sin_family == AF_INET &&
348 (ntohs(saddr->sin_port) >= IPPORT_RESERVED ||
349 (slp->ns_so->so_type == SOCK_STREAM && ntohs(saddr->sin_port) == 20))) {
350 vput(*vpp);
351 return (NFSERR_AUTHERR | AUTH_TOOWEAK);
352 }
353
354 /* Check/setup credentials. */
355 if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
356 cred->cr_uid = credanon->cr_uid;
357 cred->cr_gid = credanon->cr_gid;
358 for (i = 0; i < credanon->cr_ngroups && i < NGROUPS_MAX; i++)
359 cred->cr_groups[i] = credanon->cr_groups[i];
360 cred->cr_ngroups = i;
361 }
362 if (exflags & MNT_EXRDONLY)
363 *rdonlyp = 1;
364 else
365 *rdonlyp = 0;
366 if (!lockflag)
367 VOP_UNLOCK(*vpp);
368
369 return (0);
370 }
371
372 /*
373 * This function compares two net addresses by family and returns non zero
374 * if they are the same host, or if there is any doubt it returns 0.
375 * The AF_INET family is handled as a special case so that address mbufs
376 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
377 */
378 int
netaddr_match(int family,union nethostaddr * haddr,struct mbuf * nam)379 netaddr_match(int family, union nethostaddr *haddr, struct mbuf *nam)
380 {
381 struct sockaddr_in *inetaddr;
382
383 switch (family) {
384 case AF_INET:
385 inetaddr = mtod(nam, struct sockaddr_in *);
386 if (inetaddr->sin_family == AF_INET &&
387 inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
388 return (1);
389 break;
390 default:
391 break;
392 }
393 return (0);
394 }
395
396 int
nfsm_srvsattr(struct mbuf ** mp,struct vattr * va,struct mbuf * mrep,caddr_t * dposp)397 nfsm_srvsattr(struct mbuf **mp, struct vattr *va, struct mbuf *mrep,
398 caddr_t *dposp)
399 {
400 struct nfsm_info info;
401 int error = 0;
402 uint32_t *tl;
403
404 info.nmi_md = *mp;
405 info.nmi_dpos = *dposp;
406 info.nmi_mrep = mrep;
407 info.nmi_errorp = &error;
408
409 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
410 if (tl == NULL)
411 return error;
412 if (*tl == nfs_true) {
413 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
414 if (tl == NULL)
415 return error;
416 va->va_mode = nfstov_mode(*tl);
417 }
418
419 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
420 if (tl == NULL)
421 return error;
422 if (*tl == nfs_true) {
423 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
424 if (tl == NULL)
425 return error;
426 va->va_uid = fxdr_unsigned(uid_t, *tl);
427 }
428
429 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
430 if (tl == NULL)
431 return error;
432 if (*tl == nfs_true) {
433 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
434 if (tl == NULL)
435 return error;
436 va->va_gid = fxdr_unsigned(gid_t, *tl);
437 }
438
439 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
440 if (tl == NULL)
441 return error;
442 if (*tl == nfs_true) {
443 tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
444 if (tl == NULL)
445 return error;
446 va->va_size = fxdr_hyper(tl);
447 }
448
449 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
450 if (tl == NULL)
451 return error;
452 switch (fxdr_unsigned(int, *tl)) {
453 case NFSV3SATTRTIME_TOCLIENT:
454 va->va_vaflags |= VA_UTIMES_CHANGE;
455 va->va_vaflags &= ~VA_UTIMES_NULL;
456 tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
457 if (tl == NULL)
458 return error;
459 fxdr_nfsv3time(tl, &va->va_atime);
460 break;
461 case NFSV3SATTRTIME_TOSERVER:
462 va->va_vaflags |= VA_UTIMES_CHANGE;
463 getnanotime(&va->va_atime);
464 break;
465 }
466
467 tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
468 if (tl == NULL)
469 return error;
470 switch (fxdr_unsigned(int, *tl)) {
471 case NFSV3SATTRTIME_TOCLIENT:
472 va->va_vaflags |= VA_UTIMES_CHANGE;
473 va->va_vaflags &= ~VA_UTIMES_NULL;
474 tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
475 if (tl == NULL)
476 return error;
477 fxdr_nfsv3time(tl, &va->va_mtime);
478 break;
479 case NFSV3SATTRTIME_TOSERVER:
480 va->va_vaflags |= VA_UTIMES_CHANGE;
481 getnanotime(&va->va_mtime);
482 break;
483 }
484
485 *dposp = info.nmi_dpos;
486 *mp = info.nmi_md;
487 return 0;
488 }
489