xref: /xnu-8792.61.2/bsd/nfs/nfs_socket.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c) !
1 /*
2  * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30  * Copyright (c) 1989, 1991, 1993, 1995
31  *	The Regents of the University of California.  All rights reserved.
32  *
33  * This code is derived from software contributed to Berkeley by
34  * Rick Macklem at The University of Guelph.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed by the University of
47  *	California, Berkeley and its contributors.
48  * 4. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  *	@(#)nfs_socket.c	8.5 (Berkeley) 3/30/95
65  * FreeBSD-Id: nfs_socket.c,v 1.30 1997/10/28 15:59:07 bde Exp $
66  */
67 
68 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70 
71 /*
72  * Socket operations for use by nfs
73  */
74 
75 #include <sys/systm.h>
76 #include <sys/kauth.h>
77 #include <sys/mount_internal.h>
78 #include <sys/kpi_mbuf.h>
79 
80 #include <netinet/in.h>
81 
82 #include <nfs/rpcv2.h>
83 #include <nfs/nfsproto.h>
84 #include <nfs/nfs.h>
85 #include <nfs/xdr_subs.h>
86 #include <nfs/nfsm_subs.h>
87 #include <nfs/nfs_gss.h>
88 
89 ZONE_DEFINE(nfsrv_descript_zone, "NFSV3 srvdesc",
90     sizeof(struct nfsrv_descript), ZC_NONE);
91 
92 int nfsrv_sock_max_rec_queue_length = 128; /* max # RPC records queued on (UDP) socket */
93 
94 int nfsrv_getstream(struct nfsrv_sock *, int);
95 int nfsrv_getreq(struct nfsrv_descript *);
96 extern int nfsv3_procid[NFS_NPROCS];
97 
98 /*
99  * compare two sockaddr structures
100  */
101 int
nfs_sockaddr_cmp(struct sockaddr * sa1,struct sockaddr * sa2)102 nfs_sockaddr_cmp(struct sockaddr *sa1, struct sockaddr *sa2)
103 {
104 	if (!sa1) {
105 		return -1;
106 	}
107 	if (!sa2) {
108 		return 1;
109 	}
110 	if (sa1->sa_family != sa2->sa_family) {
111 		return (sa1->sa_family < sa2->sa_family) ? -1 : 1;
112 	}
113 	if (sa1->sa_len != sa2->sa_len) {
114 		return (sa1->sa_len < sa2->sa_len) ? -1 : 1;
115 	}
116 	if (sa1->sa_family == AF_INET) {
117 		return bcmp(&((struct sockaddr_in*)sa1)->sin_addr,
118 		           &((struct sockaddr_in*)sa2)->sin_addr, sizeof(((struct sockaddr_in*)sa1)->sin_addr));
119 	}
120 	if (sa1->sa_family == AF_INET6) {
121 		return bcmp(&((struct sockaddr_in6*)sa1)->sin6_addr,
122 		           &((struct sockaddr_in6*)sa2)->sin6_addr, sizeof(((struct sockaddr_in6*)sa1)->sin6_addr));
123 	}
124 	return -1;
125 }
126 
127 /*
128  * Generate the rpc reply header
129  * siz arg. is used to decide if adding a cluster is worthwhile
130  */
131 int
nfsrv_rephead(struct nfsrv_descript * nd,__unused struct nfsrv_sock * slp,struct nfsm_chain * nmrepp,size_t siz)132 nfsrv_rephead(
133 	struct nfsrv_descript *nd,
134 	__unused struct nfsrv_sock *slp,
135 	struct nfsm_chain *nmrepp,
136 	size_t siz)
137 {
138 	mbuf_t mrep;
139 	u_int32_t *tl;
140 	struct nfsm_chain nmrep;
141 	int err, error;
142 
143 	err = nd->nd_repstat;
144 	if (err && (nd->nd_vers == NFS_VER2)) {
145 		siz = 0;
146 	}
147 
148 	/*
149 	 * If this is a big reply, use a cluster else
150 	 * try and leave leading space for the lower level headers.
151 	 */
152 	siz += RPC_REPLYSIZ;
153 	if (siz >= nfs_mbuf_minclsize) {
154 		error = mbuf_getpacket(MBUF_WAITOK, &mrep);
155 	} else {
156 		error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &mrep);
157 	}
158 	if (error) {
159 		/* unable to allocate packet */
160 		/* XXX should we keep statistics for these errors? */
161 		return error;
162 	}
163 	if (siz < nfs_mbuf_minclsize) {
164 		/* leave space for lower level headers */
165 		tl = mbuf_data(mrep);
166 		tl += 80 / sizeof(*tl);  /* XXX max_hdr? XXX */
167 		mbuf_setdata(mrep, tl, 6 * NFSX_UNSIGNED);
168 	}
169 	nfsm_chain_init(&nmrep, mrep);
170 	nfsm_chain_add_32(error, &nmrep, nd->nd_retxid);
171 	nfsm_chain_add_32(error, &nmrep, RPC_REPLY);
172 	if (err == ERPCMISMATCH || (err & NFSERR_AUTHERR)) {
173 		nfsm_chain_add_32(error, &nmrep, RPC_MSGDENIED);
174 		if (err & NFSERR_AUTHERR) {
175 			nfsm_chain_add_32(error, &nmrep, RPC_AUTHERR);
176 			nfsm_chain_add_32(error, &nmrep, (err & ~NFSERR_AUTHERR));
177 		} else {
178 			nfsm_chain_add_32(error, &nmrep, RPC_MISMATCH);
179 			nfsm_chain_add_32(error, &nmrep, RPC_VER2);
180 			nfsm_chain_add_32(error, &nmrep, RPC_VER2);
181 		}
182 	} else {
183 		/* reply status */
184 		nfsm_chain_add_32(error, &nmrep, RPC_MSGACCEPTED);
185 		if (nd->nd_gss_context != NULL) {
186 			/* RPCSEC_GSS verifier */
187 			error = nfs_gss_svc_verf_put(nd, &nmrep);
188 			if (error) {
189 				nfsm_chain_add_32(error, &nmrep, RPC_SYSTEM_ERR);
190 				goto done;
191 			}
192 		} else {
193 			/* RPCAUTH_NULL verifier */
194 			nfsm_chain_add_32(error, &nmrep, RPCAUTH_NULL);
195 			nfsm_chain_add_32(error, &nmrep, 0);
196 		}
197 		/* accepted status */
198 		switch (err) {
199 		case EPROGUNAVAIL:
200 			nfsm_chain_add_32(error, &nmrep, RPC_PROGUNAVAIL);
201 			break;
202 		case EPROGMISMATCH:
203 			nfsm_chain_add_32(error, &nmrep, RPC_PROGMISMATCH);
204 			/* XXX hard coded versions? */
205 			nfsm_chain_add_32(error, &nmrep, NFS_VER2);
206 			nfsm_chain_add_32(error, &nmrep, NFS_VER3);
207 			break;
208 		case EPROCUNAVAIL:
209 			nfsm_chain_add_32(error, &nmrep, RPC_PROCUNAVAIL);
210 			break;
211 		case EBADRPC:
212 			nfsm_chain_add_32(error, &nmrep, RPC_GARBAGE);
213 			break;
214 		default:
215 			nfsm_chain_add_32(error, &nmrep, RPC_SUCCESS);
216 			if (nd->nd_gss_context != NULL) {
217 				error = nfs_gss_svc_prepare_reply(nd, &nmrep);
218 			}
219 			if (err != NFSERR_RETVOID) {
220 				nfsm_chain_add_32(error, &nmrep,
221 				    (err ? nfsrv_errmap(nd, err) : 0));
222 			}
223 			break;
224 		}
225 	}
226 
227 done:
228 	nfsm_chain_build_done(error, &nmrep);
229 	if (error) {
230 		/* error composing reply header */
231 		/* XXX should we keep statistics for these errors? */
232 		mbuf_freem(mrep);
233 		return error;
234 	}
235 
236 	*nmrepp = nmrep;
237 	if ((err != 0) && (err != NFSERR_RETVOID)) {
238 		OSAddAtomic64(1, &nfsrvstats.srvrpc_errs);
239 	}
240 	return 0;
241 }
242 
243 /*
244  * The nfs server send routine.
245  *
246  * - return EINTR or ERESTART if interrupted by a signal
247  * - return EPIPE if a connection is lost for connection based sockets (TCP...)
248  * - do any cleanup required by recoverable socket errors (???)
249  */
250 int
nfsrv_send(struct nfsrv_sock * slp,mbuf_t nam,mbuf_t top)251 nfsrv_send(struct nfsrv_sock *slp, mbuf_t nam, mbuf_t top)
252 {
253 	int error;
254 	socket_t so = slp->ns_so;
255 	struct sockaddr *sendnam;
256 	struct msghdr msg;
257 
258 	bzero(&msg, sizeof(msg));
259 	if (nam && !sock_isconnected(so) && (slp->ns_sotype != SOCK_STREAM)) {
260 		if ((sendnam = mbuf_data(nam))) {
261 			msg.msg_name = (caddr_t)sendnam;
262 			msg.msg_namelen = sendnam->sa_len;
263 		}
264 	}
265 	if (NFSRV_IS_DBG(NFSRV_FAC_SRV, 15)) {
266 		nfs_dump_mbuf(__func__, __LINE__, "nfsrv_send\n", top);
267 	}
268 	error = sock_sendmbuf(so, &msg, top, 0, NULL);
269 	if (!error) {
270 		return 0;
271 	}
272 	log(LOG_INFO, "nfsd send error %d\n", error);
273 
274 	if ((error == EWOULDBLOCK) && (slp->ns_sotype == SOCK_STREAM)) {
275 		error = EPIPE;  /* zap TCP sockets if they time out on send */
276 	}
277 	/* Handle any recoverable (soft) socket errors here. (???) */
278 	if (error != EINTR && error != ERESTART && error != EIO &&
279 	    error != EWOULDBLOCK && error != EPIPE) {
280 		error = 0;
281 	}
282 
283 	return error;
284 }
285 
286 /*
287  * Socket upcall routine for the nfsd sockets.
288  * The caddr_t arg is a pointer to the "struct nfsrv_sock".
289  * Essentially do as much as possible non-blocking, else punt and it will
290  * be called with MBUF_WAITOK from an nfsd.
291  */
292 void
nfsrv_rcv(socket_t so,void * arg,int waitflag)293 nfsrv_rcv(socket_t so, void *arg, int waitflag)
294 {
295 	struct nfsrv_sock *slp = arg;
296 
297 	if (!nfsd_thread_count || !(slp->ns_flag & SLP_VALID)) {
298 		return;
299 	}
300 
301 	lck_rw_lock_exclusive(&slp->ns_rwlock);
302 	nfsrv_rcv_locked(so, slp, waitflag);
303 	/* Note: ns_rwlock gets dropped when called with MBUF_DONTWAIT */
304 }
305 void
nfsrv_rcv_locked(socket_t so,struct nfsrv_sock * slp,int waitflag)306 nfsrv_rcv_locked(socket_t so, struct nfsrv_sock *slp, int waitflag)
307 {
308 	mbuf_t m, mp, mhck, m2;
309 	int ns_flag = 0, error;
310 	struct msghdr   msg;
311 	size_t bytes_read;
312 
313 	if ((slp->ns_flag & SLP_VALID) == 0) {
314 		if (waitflag == MBUF_DONTWAIT) {
315 			lck_rw_done(&slp->ns_rwlock);
316 		}
317 		return;
318 	}
319 
320 #ifdef notdef
321 	/*
322 	 * Define this to test for nfsds handling this under heavy load.
323 	 */
324 	if (waitflag == MBUF_DONTWAIT) {
325 		ns_flag = SLP_NEEDQ;
326 		goto dorecs;
327 	}
328 #endif
329 	if (slp->ns_sotype == SOCK_STREAM) {
330 		/*
331 		 * If there are already records on the queue, defer soreceive()
332 		 * to an(other) nfsd so that there is feedback to the TCP layer that
333 		 * the nfs servers are heavily loaded.
334 		 */
335 		if (slp->ns_rec) {
336 			ns_flag = SLP_NEEDQ;
337 			goto dorecs;
338 		}
339 
340 		/*
341 		 * Do soreceive().
342 		 */
343 		bytes_read = 1000000000;
344 		error = sock_receivembuf(so, NULL, &mp, MSG_DONTWAIT, &bytes_read);
345 		if (error || mp == NULL) {
346 			if (error == EWOULDBLOCK) {
347 				ns_flag = (waitflag == MBUF_DONTWAIT) ? SLP_NEEDQ : 0;
348 			} else {
349 				ns_flag = SLP_DISCONN;
350 			}
351 			goto dorecs;
352 		}
353 		m = mp;
354 		if (slp->ns_rawend) {
355 			if ((error = mbuf_setnext(slp->ns_rawend, m))) {
356 				panic("nfsrv_rcv: mbuf_setnext failed %d", error);
357 			}
358 			slp->ns_cc += bytes_read;
359 		} else {
360 			slp->ns_raw = m;
361 			slp->ns_cc = bytes_read;
362 		}
363 		while ((m2 = mbuf_next(m))) {
364 			m = m2;
365 		}
366 		slp->ns_rawend = m;
367 
368 		/*
369 		 * Now try and parse record(s) out of the raw stream data.
370 		 */
371 		error = nfsrv_getstream(slp, waitflag);
372 		if (error) {
373 			if (error == EPERM) {
374 				ns_flag = SLP_DISCONN;
375 			} else {
376 				ns_flag = SLP_NEEDQ;
377 			}
378 		}
379 	} else {
380 		struct sockaddr_storage nam;
381 
382 		if (slp->ns_reccnt >= nfsrv_sock_max_rec_queue_length) {
383 			/* already have max # RPC records queued on this socket */
384 			ns_flag = SLP_NEEDQ;
385 			goto dorecs;
386 		}
387 
388 		bzero(&msg, sizeof(msg));
389 		msg.msg_name = (caddr_t)&nam;
390 		msg.msg_namelen = sizeof(nam);
391 
392 		do {
393 			bytes_read = 1000000000;
394 			error = sock_receivembuf(so, &msg, &mp, MSG_DONTWAIT | MSG_NEEDSA, &bytes_read);
395 			if (mp) {
396 				if (msg.msg_name && (mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &mhck) == 0)) {
397 					mbuf_setlen(mhck, nam.ss_len);
398 					bcopy(&nam, mbuf_data(mhck), nam.ss_len);
399 					m = mhck;
400 					if (mbuf_setnext(m, mp)) {
401 						/* trouble... just drop it */
402 						printf("nfsrv_rcv: mbuf_setnext failed\n");
403 						mbuf_free(mhck);
404 						m = mp;
405 					}
406 				} else {
407 					m = mp;
408 				}
409 				if (slp->ns_recend) {
410 					mbuf_setnextpkt(slp->ns_recend, m);
411 				} else {
412 					slp->ns_rec = m;
413 					slp->ns_flag |= SLP_DOREC;
414 				}
415 				slp->ns_recend = m;
416 				mbuf_setnextpkt(m, NULL);
417 				slp->ns_reccnt++;
418 			}
419 		} while (mp);
420 	}
421 
422 	/*
423 	 * Now try and process the request records, non-blocking.
424 	 */
425 dorecs:
426 	if (ns_flag) {
427 		slp->ns_flag |= ns_flag;
428 	}
429 	if (waitflag == MBUF_DONTWAIT) {
430 		int wake = (slp->ns_flag & SLP_WORKTODO);
431 		lck_rw_done(&slp->ns_rwlock);
432 		if (wake && nfsd_thread_count) {
433 			lck_mtx_lock(&nfsd_mutex);
434 			nfsrv_wakenfsd(slp);
435 			lck_mtx_unlock(&nfsd_mutex);
436 		}
437 	}
438 }
439 
440 /*
441  * Try and extract an RPC request from the mbuf data list received on a
442  * stream socket. The "waitflag" argument indicates whether or not it
443  * can sleep.
444  */
445 int
nfsrv_getstream(struct nfsrv_sock * slp,int waitflag)446 nfsrv_getstream(struct nfsrv_sock *slp, int waitflag)
447 {
448 	mbuf_t m;
449 	char *cp1, *cp2, *mdata;
450 	int error;
451 	size_t len, mlen;
452 	mbuf_t om, m2, recm;
453 	u_int32_t recmark;
454 
455 	if (slp->ns_flag & SLP_GETSTREAM) {
456 		panic("nfs getstream");
457 	}
458 	slp->ns_flag |= SLP_GETSTREAM;
459 	for (;;) {
460 		if (slp->ns_reclen == 0) {
461 			if (slp->ns_cc < NFSX_UNSIGNED) {
462 				slp->ns_flag &= ~SLP_GETSTREAM;
463 				return 0;
464 			}
465 			m = slp->ns_raw;
466 			mdata = mbuf_data(m);
467 			mlen = mbuf_len(m);
468 			if (mlen >= NFSX_UNSIGNED) {
469 				bcopy(mdata, (caddr_t)&recmark, NFSX_UNSIGNED);
470 				mdata += NFSX_UNSIGNED;
471 				mlen -= NFSX_UNSIGNED;
472 				mbuf_setdata(m, mdata, mlen);
473 			} else {
474 				cp1 = (caddr_t)&recmark;
475 				cp2 = mdata;
476 				while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
477 					while (mlen == 0) {
478 						m = mbuf_next(m);
479 						cp2 = mbuf_data(m);
480 						mlen = mbuf_len(m);
481 					}
482 					*cp1++ = *cp2++;
483 					mlen--;
484 					mbuf_setdata(m, cp2, mlen);
485 				}
486 			}
487 			slp->ns_cc -= NFSX_UNSIGNED;
488 			recmark = ntohl(recmark);
489 			slp->ns_reclen = recmark & ~0x80000000;
490 			if (recmark & 0x80000000) {
491 				slp->ns_flag |= SLP_LASTFRAG;
492 			} else {
493 				slp->ns_flag &= ~SLP_LASTFRAG;
494 			}
495 			if (slp->ns_reclen <= 0 || slp->ns_reclen > NFS_MAXPACKET) {
496 				slp->ns_flag &= ~SLP_GETSTREAM;
497 				return EPERM;
498 			}
499 		}
500 
501 		/*
502 		 * Now get the record part.
503 		 *
504 		 * Note that slp->ns_reclen may be 0.  Linux sometimes
505 		 * generates 0-length RPCs
506 		 */
507 		recm = NULL;
508 		if (slp->ns_cc == slp->ns_reclen) {
509 			recm = slp->ns_raw;
510 			slp->ns_raw = slp->ns_rawend = NULL;
511 			slp->ns_cc = slp->ns_reclen = 0;
512 		} else if (slp->ns_cc > slp->ns_reclen) {
513 			len = 0;
514 			m = slp->ns_raw;
515 			mlen = mbuf_len(m);
516 			mdata = mbuf_data(m);
517 			om = NULL;
518 			while (len < slp->ns_reclen) {
519 				if ((len + mlen) > slp->ns_reclen) {
520 					if (mbuf_copym(m, 0, slp->ns_reclen - len, waitflag, &m2)) {
521 						slp->ns_flag &= ~SLP_GETSTREAM;
522 						return EWOULDBLOCK;
523 					}
524 					if (om) {
525 						if (mbuf_setnext(om, m2)) {
526 							/* trouble... just drop it */
527 							printf("nfsrv_getstream: mbuf_setnext failed\n");
528 							mbuf_freem(m2);
529 							slp->ns_flag &= ~SLP_GETSTREAM;
530 							return EWOULDBLOCK;
531 						}
532 						recm = slp->ns_raw;
533 					} else {
534 						recm = m2;
535 					}
536 					mdata += slp->ns_reclen - len;
537 					mlen -= slp->ns_reclen - len;
538 					mbuf_setdata(m, mdata, mlen);
539 					len = slp->ns_reclen;
540 				} else if ((len + mlen) == slp->ns_reclen) {
541 					om = m;
542 					len += mlen;
543 					m = mbuf_next(m);
544 					recm = slp->ns_raw;
545 					if (mbuf_setnext(om, NULL)) {
546 						printf("nfsrv_getstream: mbuf_setnext failed 2\n");
547 						slp->ns_flag &= ~SLP_GETSTREAM;
548 						return EWOULDBLOCK;
549 					}
550 					mlen = mbuf_len(m);
551 					mdata = mbuf_data(m);
552 				} else {
553 					om = m;
554 					len += mlen;
555 					m = mbuf_next(m);
556 					mlen = mbuf_len(m);
557 					mdata = mbuf_data(m);
558 				}
559 			}
560 			slp->ns_raw = m;
561 			slp->ns_cc -= len;
562 			slp->ns_reclen = 0;
563 		} else {
564 			slp->ns_flag &= ~SLP_GETSTREAM;
565 			return 0;
566 		}
567 
568 		/*
569 		 * Accumulate the fragments into a record.
570 		 */
571 		if (slp->ns_frag == NULL) {
572 			slp->ns_frag = recm;
573 		} else {
574 			m = slp->ns_frag;
575 			while ((m2 = mbuf_next(m))) {
576 				m = m2;
577 			}
578 			if ((error = mbuf_setnext(m, recm))) {
579 				panic("nfsrv_getstream: mbuf_setnext failed 3, %d", error);
580 			}
581 		}
582 		if (slp->ns_flag & SLP_LASTFRAG) {
583 			if (slp->ns_recend) {
584 				mbuf_setnextpkt(slp->ns_recend, slp->ns_frag);
585 			} else {
586 				slp->ns_rec = slp->ns_frag;
587 				slp->ns_flag |= SLP_DOREC;
588 			}
589 			slp->ns_recend = slp->ns_frag;
590 			slp->ns_frag = NULL;
591 		}
592 	}
593 }
594 
595 /*
596  * Parse an RPC header.
597  */
598 int
nfsrv_dorec(struct nfsrv_sock * slp,struct nfsd * nfsd,struct nfsrv_descript ** ndp)599 nfsrv_dorec(
600 	struct nfsrv_sock *slp,
601 	struct nfsd *nfsd,
602 	struct nfsrv_descript **ndp)
603 {
604 	mbuf_t m;
605 	mbuf_t nam;
606 	struct nfsrv_descript *nd;
607 	int error = 0;
608 
609 	*ndp = NULL;
610 	if (!(slp->ns_flag & (SLP_VALID | SLP_DOREC)) || (slp->ns_rec == NULL)) {
611 		return ENOBUFS;
612 	}
613 	nd = zalloc(nfsrv_descript_zone);
614 	m = slp->ns_rec;
615 	slp->ns_rec = mbuf_nextpkt(m);
616 	if (slp->ns_rec) {
617 		mbuf_setnextpkt(m, NULL);
618 	} else {
619 		slp->ns_flag &= ~SLP_DOREC;
620 		slp->ns_recend = NULL;
621 	}
622 	slp->ns_reccnt--;
623 	if (mbuf_type(m) == MBUF_TYPE_SONAME) {
624 		nam = m;
625 		m = mbuf_next(m);
626 		if ((error = mbuf_setnext(nam, NULL))) {
627 			panic("nfsrv_dorec: mbuf_setnext failed %d", error);
628 		}
629 	} else {
630 		nam = NULL;
631 	}
632 	nd->nd_nam2 = nam;
633 	nfsm_chain_dissect_init(error, &nd->nd_nmreq, m);
634 	if (!error) {
635 		error = nfsrv_getreq(nd);
636 	}
637 	if (error) {
638 		if (nam) {
639 			mbuf_freem(nam);
640 		}
641 		if (nd->nd_gss_context) {
642 			nfs_gss_svc_ctx_deref(nd->nd_gss_context);
643 		}
644 		NFS_ZFREE(nfsrv_descript_zone, nd);
645 		return error;
646 	}
647 	nd->nd_mrep = NULL;
648 	*ndp = nd;
649 	nfsd->nfsd_nd = nd;
650 	return 0;
651 }
652 
653 /*
654  * Parse an RPC request
655  * - verify it
656  * - fill in the cred struct.
657  */
658 int
nfsrv_getreq(struct nfsrv_descript * nd)659 nfsrv_getreq(struct nfsrv_descript *nd)
660 {
661 	struct nfsm_chain *nmreq;
662 	int len, i;
663 	u_int32_t nfsvers, auth_type;
664 	int error = 0;
665 	uid_t user_id;
666 	gid_t group_id;
667 	short ngroups;
668 	uint32_t val;
669 
670 	nd->nd_cr = NULL;
671 	nd->nd_gss_context = NULL;
672 	nd->nd_gss_seqnum = 0;
673 	nd->nd_gss_mb = NULL;
674 
675 	user_id = group_id = -2;
676 	val = auth_type = len = 0;
677 
678 	nmreq = &nd->nd_nmreq;
679 	nfsm_chain_get_32(error, nmreq, nd->nd_retxid); // XID
680 	nfsm_chain_get_32(error, nmreq, val);           // RPC Call
681 	if (!error && (val != RPC_CALL)) {
682 		error = EBADRPC;
683 	}
684 	nfsmout_if(error);
685 	nd->nd_repstat = 0;
686 	nfsm_chain_get_32(error, nmreq, val);   // RPC Version
687 	nfsmout_if(error);
688 	if (val != RPC_VER2) {
689 		nd->nd_repstat = ERPCMISMATCH;
690 		nd->nd_procnum = NFSPROC_NOOP;
691 		return 0;
692 	}
693 	nfsm_chain_get_32(error, nmreq, val);   // RPC Program Number
694 	nfsmout_if(error);
695 	if (val != NFS_PROG) {
696 		nd->nd_repstat = EPROGUNAVAIL;
697 		nd->nd_procnum = NFSPROC_NOOP;
698 		return 0;
699 	}
700 	nfsm_chain_get_32(error, nmreq, nfsvers);// NFS Version Number
701 	nfsmout_if(error);
702 	if ((nfsvers < NFS_VER2) || (nfsvers > NFS_VER3)) {
703 		nd->nd_repstat = EPROGMISMATCH;
704 		nd->nd_procnum = NFSPROC_NOOP;
705 		return 0;
706 	}
707 	nd->nd_vers = nfsvers;
708 	nfsm_chain_get_32(error, nmreq, nd->nd_procnum);// NFS Procedure Number
709 	nfsmout_if(error);
710 	if ((nd->nd_procnum >= NFS_NPROCS) ||
711 	    ((nd->nd_vers == NFS_VER2) && (nd->nd_procnum > NFSV2PROC_STATFS))) {
712 		nd->nd_repstat = EPROCUNAVAIL;
713 		nd->nd_procnum = NFSPROC_NOOP;
714 		return 0;
715 	}
716 	if (nfsvers != NFS_VER3) {
717 		nd->nd_procnum = nfsv3_procid[nd->nd_procnum];
718 	}
719 	nfsm_chain_get_32(error, nmreq, auth_type);     // Auth Flavor
720 	nfsm_chain_get_32(error, nmreq, len);           // Auth Length
721 	if (!error && (len < 0 || len > RPCAUTH_MAXSIZ)) {
722 		error = EBADRPC;
723 	}
724 	nfsmout_if(error);
725 
726 	/* Handle authentication */
727 	if (auth_type == RPCAUTH_SYS) {
728 		struct posix_cred temp_pcred;
729 		if (nd->nd_procnum == NFSPROC_NULL) {
730 			return 0;
731 		}
732 		nd->nd_sec = RPCAUTH_SYS;
733 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);    // skip stamp
734 		nfsm_chain_get_32(error, nmreq, len);           // hostname length
735 		if (len < 0 || len > NFS_MAXNAMLEN) {
736 			error = EBADRPC;
737 		}
738 		nfsm_chain_adv(error, nmreq, nfsm_rndup(len));  // skip hostname
739 		nfsmout_if(error);
740 
741 		/* create a temporary credential using the bits from the wire */
742 		bzero(&temp_pcred, sizeof(temp_pcred));
743 		nfsm_chain_get_32(error, nmreq, user_id);
744 		nfsm_chain_get_32(error, nmreq, group_id);
745 		temp_pcred.cr_groups[0] = group_id;
746 		nfsm_chain_get_32(error, nmreq, len);           // extra GID count
747 		if ((len < 0) || (len > RPCAUTH_UNIXGIDS)) {
748 			error = EBADRPC;
749 		}
750 		nfsmout_if(error);
751 		for (i = 1; i <= len; i++) {
752 			if (i < NGROUPS) {
753 				nfsm_chain_get_32(error, nmreq, temp_pcred.cr_groups[i]);
754 			} else {
755 				nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
756 			}
757 		}
758 		nfsmout_if(error);
759 		ngroups = (len >= NGROUPS) ? NGROUPS : (short)(len + 1);
760 		if (ngroups > 1) {
761 			nfsrv_group_sort(&temp_pcred.cr_groups[0], ngroups);
762 		}
763 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);    // verifier flavor (should be AUTH_NONE)
764 		nfsm_chain_get_32(error, nmreq, len);           // verifier length
765 		if (len < 0 || len > RPCAUTH_MAXSIZ) {
766 			error = EBADRPC;
767 		}
768 		if (len > 0) {
769 			nfsm_chain_adv(error, nmreq, nfsm_rndup(len));
770 		}
771 
772 		/* request creation of a real credential */
773 		temp_pcred.cr_uid = user_id;
774 		temp_pcred.cr_ngroups = ngroups;
775 		nd->nd_cr = posix_cred_create(&temp_pcred);
776 		if (nd->nd_cr == NULL) {
777 			nd->nd_repstat = ENOMEM;
778 			nd->nd_procnum = NFSPROC_NOOP;
779 			return 0;
780 		}
781 	} else if (auth_type == RPCSEC_GSS) {
782 		error = nfs_gss_svc_cred_get(nd, nmreq);
783 		if (error) {
784 			if (error == EINVAL) {
785 				goto nfsmout;   // drop the request
786 			}
787 			nd->nd_repstat = error;
788 			nd->nd_procnum = NFSPROC_NOOP;
789 			return 0;
790 		}
791 	} else {
792 		if (nd->nd_procnum == NFSPROC_NULL) {   // assume it's AUTH_NONE
793 			return 0;
794 		}
795 		nd->nd_repstat = (NFSERR_AUTHERR | AUTH_REJECTCRED);
796 		nd->nd_procnum = NFSPROC_NOOP;
797 		return 0;
798 	}
799 	return 0;
800 nfsmout:
801 	if (IS_VALID_CRED(nd->nd_cr)) {
802 		kauth_cred_unref(&nd->nd_cr);
803 	}
804 	nfsm_chain_cleanup(nmreq);
805 	return error;
806 }
807 
808 /*
809  * Search for a sleeping nfsd and wake it up.
810  * SIDE EFFECT: If none found, make sure the socket is queued up so that one
811  * of the running nfsds will go look for the work in the nfsrv_sockwait list.
812  * Note: Must be called with nfsd_mutex held.
813  */
814 void
nfsrv_wakenfsd(struct nfsrv_sock * slp)815 nfsrv_wakenfsd(struct nfsrv_sock *slp)
816 {
817 	struct nfsd *nd;
818 
819 	if ((slp->ns_flag & SLP_VALID) == 0) {
820 		return;
821 	}
822 
823 	lck_rw_lock_exclusive(&slp->ns_rwlock);
824 	/* if there's work to do on this socket, make sure it's queued up */
825 	if ((slp->ns_flag & SLP_WORKTODO) && !(slp->ns_flag & SLP_QUEUED)) {
826 		TAILQ_INSERT_TAIL(&nfsrv_sockwait, slp, ns_svcq);
827 		slp->ns_flag |= SLP_WAITQ;
828 	}
829 	lck_rw_done(&slp->ns_rwlock);
830 
831 	/* wake up a waiting nfsd, if possible */
832 	nd = TAILQ_FIRST(&nfsd_queue);
833 	if (!nd) {
834 		return;
835 	}
836 
837 	TAILQ_REMOVE(&nfsd_queue, nd, nfsd_queue);
838 	nd->nfsd_flag &= ~NFSD_WAITING;
839 	wakeup(nd);
840 }
841 
842 #endif /* CONFIG_NFS_SERVER */
843