xref: /openbsd/sys/nfs/nfs_srvsubs.c (revision 8d80561b)
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