xref: /xnu-8796.121.2/bsd/nfs/nfs_socket.c (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
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 #include <IOKit/IOLib.h>
80 
81 #include <netinet/in.h>
82 
83 #include <nfs/rpcv2.h>
84 #include <nfs/nfsproto.h>
85 #include <nfs/nfs.h>
86 #include <nfs/xdr_subs.h>
87 #include <nfs/nfsm_subs.h>
88 #include <nfs/nfs_gss.h>
89 
90 ZONE_DEFINE(nfsrv_descript_zone, "NFSV3 srvdesc",
91     sizeof(struct nfsrv_descript), ZC_NONE);
92 
93 int nfsrv_sock_max_rec_queue_length = 128; /* max # RPC records queued on (UDP) socket */
94 
95 int nfsrv_getstream(struct nfsrv_sock *, int);
96 int nfsrv_getreq(struct nfsrv_descript *);
97 extern int nfsv3_procid[NFS_NPROCS];
98 
99 #define NFS_TRYLOCK_MSEC_SLEEP 1
100 
101 /*
102  * compare two sockaddr structures
103  */
104 int
nfs_sockaddr_cmp(struct sockaddr * sa1,struct sockaddr * sa2)105 nfs_sockaddr_cmp(struct sockaddr *sa1, struct sockaddr *sa2)
106 {
107 	if (!sa1) {
108 		return -1;
109 	}
110 	if (!sa2) {
111 		return 1;
112 	}
113 	if (sa1->sa_family != sa2->sa_family) {
114 		return (sa1->sa_family < sa2->sa_family) ? -1 : 1;
115 	}
116 	if (sa1->sa_len != sa2->sa_len) {
117 		return (sa1->sa_len < sa2->sa_len) ? -1 : 1;
118 	}
119 	if (sa1->sa_family == AF_INET) {
120 		return bcmp(&((struct sockaddr_in*)sa1)->sin_addr,
121 		           &((struct sockaddr_in*)sa2)->sin_addr, sizeof(((struct sockaddr_in*)sa1)->sin_addr));
122 	}
123 	if (sa1->sa_family == AF_INET6) {
124 		return bcmp(&((struct sockaddr_in6*)sa1)->sin6_addr,
125 		           &((struct sockaddr_in6*)sa2)->sin6_addr, sizeof(((struct sockaddr_in6*)sa1)->sin6_addr));
126 	}
127 	return -1;
128 }
129 
130 /*
131  * Generate the rpc reply header
132  * siz arg. is used to decide if adding a cluster is worthwhile
133  */
134 int
nfsrv_rephead(struct nfsrv_descript * nd,__unused struct nfsrv_sock * slp,struct nfsm_chain * nmrepp,size_t siz)135 nfsrv_rephead(
136 	struct nfsrv_descript *nd,
137 	__unused struct nfsrv_sock *slp,
138 	struct nfsm_chain *nmrepp,
139 	size_t siz)
140 {
141 	mbuf_t mrep;
142 	u_int32_t *tl;
143 	struct nfsm_chain nmrep;
144 	int err, error;
145 
146 	err = nd->nd_repstat;
147 	if (err && (nd->nd_vers == NFS_VER2)) {
148 		siz = 0;
149 	}
150 
151 	/*
152 	 * If this is a big reply, use a cluster else
153 	 * try and leave leading space for the lower level headers.
154 	 */
155 	siz += RPC_REPLYSIZ;
156 	if (siz >= nfs_mbuf_minclsize) {
157 		error = mbuf_getpacket(MBUF_WAITOK, &mrep);
158 	} else {
159 		error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &mrep);
160 	}
161 	if (error) {
162 		/* unable to allocate packet */
163 		/* XXX should we keep statistics for these errors? */
164 		return error;
165 	}
166 	if (siz < nfs_mbuf_minclsize) {
167 		/* leave space for lower level headers */
168 		tl = mbuf_data(mrep);
169 		tl += 80 / sizeof(*tl);  /* XXX max_hdr? XXX */
170 		mbuf_setdata(mrep, tl, 6 * NFSX_UNSIGNED);
171 	}
172 	nfsm_chain_init(&nmrep, mrep);
173 	nfsm_chain_add_32(error, &nmrep, nd->nd_retxid);
174 	nfsm_chain_add_32(error, &nmrep, RPC_REPLY);
175 	if (err == ERPCMISMATCH || (err & NFSERR_AUTHERR)) {
176 		nfsm_chain_add_32(error, &nmrep, RPC_MSGDENIED);
177 		if (err & NFSERR_AUTHERR) {
178 			nfsm_chain_add_32(error, &nmrep, RPC_AUTHERR);
179 			nfsm_chain_add_32(error, &nmrep, (err & ~NFSERR_AUTHERR));
180 		} else {
181 			nfsm_chain_add_32(error, &nmrep, RPC_MISMATCH);
182 			nfsm_chain_add_32(error, &nmrep, RPC_VER2);
183 			nfsm_chain_add_32(error, &nmrep, RPC_VER2);
184 		}
185 	} else {
186 		/* reply status */
187 		nfsm_chain_add_32(error, &nmrep, RPC_MSGACCEPTED);
188 		if (nd->nd_gss_context != NULL) {
189 			/* RPCSEC_GSS verifier */
190 			error = nfs_gss_svc_verf_put(nd, &nmrep);
191 			if (error) {
192 				nfsm_chain_add_32(error, &nmrep, RPC_SYSTEM_ERR);
193 				goto done;
194 			}
195 		} else {
196 			/* RPCAUTH_NULL verifier */
197 			nfsm_chain_add_32(error, &nmrep, RPCAUTH_NULL);
198 			nfsm_chain_add_32(error, &nmrep, 0);
199 		}
200 		/* accepted status */
201 		switch (err) {
202 		case EPROGUNAVAIL:
203 			nfsm_chain_add_32(error, &nmrep, RPC_PROGUNAVAIL);
204 			break;
205 		case EPROGMISMATCH:
206 			nfsm_chain_add_32(error, &nmrep, RPC_PROGMISMATCH);
207 			/* XXX hard coded versions? */
208 			nfsm_chain_add_32(error, &nmrep, NFS_VER2);
209 			nfsm_chain_add_32(error, &nmrep, NFS_VER3);
210 			break;
211 		case EPROCUNAVAIL:
212 			nfsm_chain_add_32(error, &nmrep, RPC_PROCUNAVAIL);
213 			break;
214 		case EBADRPC:
215 			nfsm_chain_add_32(error, &nmrep, RPC_GARBAGE);
216 			break;
217 		default:
218 			nfsm_chain_add_32(error, &nmrep, RPC_SUCCESS);
219 			if (nd->nd_gss_context != NULL) {
220 				error = nfs_gss_svc_prepare_reply(nd, &nmrep);
221 			}
222 			if (err != NFSERR_RETVOID) {
223 				nfsm_chain_add_32(error, &nmrep,
224 				    (err ? nfsrv_errmap(nd, err) : 0));
225 			}
226 			break;
227 		}
228 	}
229 
230 done:
231 	nfsm_chain_build_done(error, &nmrep);
232 	if (error) {
233 		/* error composing reply header */
234 		/* XXX should we keep statistics for these errors? */
235 		mbuf_freem(mrep);
236 		return error;
237 	}
238 
239 	*nmrepp = nmrep;
240 	if ((err != 0) && (err != NFSERR_RETVOID)) {
241 		OSAddAtomic64(1, &nfsrvstats.srvrpc_errs);
242 	}
243 	return 0;
244 }
245 
246 /*
247  * The nfs server send routine.
248  *
249  * - return EINTR or ERESTART if interrupted by a signal
250  * - return EPIPE if a connection is lost for connection based sockets (TCP...)
251  * - do any cleanup required by recoverable socket errors (???)
252  */
253 int
nfsrv_send(struct nfsrv_sock * slp,mbuf_t nam,mbuf_t top)254 nfsrv_send(struct nfsrv_sock *slp, mbuf_t nam, mbuf_t top)
255 {
256 	int error;
257 	socket_t so = slp->ns_so;
258 	struct sockaddr *sendnam;
259 	struct msghdr msg;
260 
261 	bzero(&msg, sizeof(msg));
262 	if (nam && !sock_isconnected(so) && (slp->ns_sotype != SOCK_STREAM)) {
263 		if ((sendnam = mbuf_data(nam))) {
264 			msg.msg_name = (caddr_t)sendnam;
265 			msg.msg_namelen = sendnam->sa_len;
266 		}
267 	}
268 	if (NFSRV_IS_DBG(NFSRV_FAC_SRV, 15)) {
269 		nfs_dump_mbuf(__func__, __LINE__, "nfsrv_send\n", top);
270 	}
271 	error = sock_sendmbuf(so, &msg, top, 0, NULL);
272 	if (!error) {
273 		return 0;
274 	}
275 	log(LOG_INFO, "nfsd send error %d\n", error);
276 
277 	if ((error == EWOULDBLOCK) && (slp->ns_sotype == SOCK_STREAM)) {
278 		error = EPIPE;  /* zap TCP sockets if they time out on send */
279 	}
280 	/* Handle any recoverable (soft) socket errors here. (???) */
281 	if (error != EINTR && error != ERESTART && error != EIO &&
282 	    error != EWOULDBLOCK && error != EPIPE) {
283 		error = 0;
284 	}
285 
286 	return error;
287 }
288 
289 /*
290  * Socket upcall routine for the nfsd sockets.
291  * The caddr_t arg is a pointer to the "struct nfsrv_sock".
292  * Essentially do as much as possible non-blocking, else punt and it will
293  * be called with MBUF_WAITOK from an nfsd.
294  */
295 void
nfsrv_rcv(socket_t so,void * arg,int waitflag)296 nfsrv_rcv(socket_t so, void *arg, int waitflag)
297 {
298 	struct nfsrv_sock *slp = arg;
299 
300 	while (1) {
301 		if (!nfsd_thread_count || !(slp->ns_flag & SLP_VALID)) {
302 			return;
303 		}
304 		if (lck_rw_try_lock_exclusive(&slp->ns_rwlock)) {
305 			/* Exclusive lock acquired */
306 			break;
307 		}
308 		IOSleep(NFS_TRYLOCK_MSEC_SLEEP);
309 	}
310 
311 	nfsrv_rcv_locked(so, slp, waitflag);
312 	/* Note: ns_rwlock gets dropped when called with MBUF_DONTWAIT */
313 }
314 void
nfsrv_rcv_locked(socket_t so,struct nfsrv_sock * slp,int waitflag)315 nfsrv_rcv_locked(socket_t so, struct nfsrv_sock *slp, int waitflag)
316 {
317 	mbuf_t m, mp, mhck, m2;
318 	int ns_flag = 0, error;
319 	struct msghdr   msg;
320 	size_t bytes_read;
321 
322 	if ((slp->ns_flag & SLP_VALID) == 0) {
323 		if (waitflag == MBUF_DONTWAIT) {
324 			lck_rw_done(&slp->ns_rwlock);
325 		}
326 		return;
327 	}
328 
329 #ifdef notdef
330 	/*
331 	 * Define this to test for nfsds handling this under heavy load.
332 	 */
333 	if (waitflag == MBUF_DONTWAIT) {
334 		ns_flag = SLP_NEEDQ;
335 		goto dorecs;
336 	}
337 #endif
338 	if (slp->ns_sotype == SOCK_STREAM) {
339 		/*
340 		 * If there are already records on the queue, defer soreceive()
341 		 * to an(other) nfsd so that there is feedback to the TCP layer that
342 		 * the nfs servers are heavily loaded.
343 		 */
344 		if (slp->ns_rec) {
345 			ns_flag = SLP_NEEDQ;
346 			goto dorecs;
347 		}
348 
349 		/*
350 		 * Do soreceive().
351 		 */
352 		bytes_read = 1000000000;
353 		error = sock_receivembuf(so, NULL, &mp, MSG_DONTWAIT, &bytes_read);
354 		if (error || mp == NULL) {
355 			if (error == EWOULDBLOCK) {
356 				ns_flag = (waitflag == MBUF_DONTWAIT) ? SLP_NEEDQ : 0;
357 			} else {
358 				ns_flag = SLP_DISCONN;
359 			}
360 			goto dorecs;
361 		}
362 		m = mp;
363 		if (slp->ns_rawend) {
364 			if ((error = mbuf_setnext(slp->ns_rawend, m))) {
365 				panic("nfsrv_rcv: mbuf_setnext failed %d", error);
366 			}
367 			slp->ns_cc += bytes_read;
368 		} else {
369 			slp->ns_raw = m;
370 			slp->ns_cc = bytes_read;
371 		}
372 		while ((m2 = mbuf_next(m))) {
373 			m = m2;
374 		}
375 		slp->ns_rawend = m;
376 
377 		/*
378 		 * Now try and parse record(s) out of the raw stream data.
379 		 */
380 		error = nfsrv_getstream(slp, waitflag);
381 		if (error) {
382 			if (error == EPERM) {
383 				ns_flag = SLP_DISCONN;
384 			} else {
385 				ns_flag = SLP_NEEDQ;
386 			}
387 		}
388 	} else {
389 		struct sockaddr_storage nam;
390 
391 		if (slp->ns_reccnt >= nfsrv_sock_max_rec_queue_length) {
392 			/* already have max # RPC records queued on this socket */
393 			ns_flag = SLP_NEEDQ;
394 			goto dorecs;
395 		}
396 
397 		bzero(&msg, sizeof(msg));
398 		msg.msg_name = (caddr_t)&nam;
399 		msg.msg_namelen = sizeof(nam);
400 
401 		do {
402 			bytes_read = 1000000000;
403 			error = sock_receivembuf(so, &msg, &mp, MSG_DONTWAIT | MSG_NEEDSA, &bytes_read);
404 			if (mp) {
405 				if (msg.msg_name && (mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &mhck) == 0)) {
406 					mbuf_setlen(mhck, nam.ss_len);
407 					bcopy(&nam, mbuf_data(mhck), nam.ss_len);
408 					m = mhck;
409 					if (mbuf_setnext(m, mp)) {
410 						/* trouble... just drop it */
411 						printf("nfsrv_rcv: mbuf_setnext failed\n");
412 						mbuf_free(mhck);
413 						m = mp;
414 					}
415 				} else {
416 					m = mp;
417 				}
418 				if (slp->ns_recend) {
419 					mbuf_setnextpkt(slp->ns_recend, m);
420 				} else {
421 					slp->ns_rec = m;
422 					slp->ns_flag |= SLP_DOREC;
423 				}
424 				slp->ns_recend = m;
425 				mbuf_setnextpkt(m, NULL);
426 				slp->ns_reccnt++;
427 			}
428 		} while (mp);
429 	}
430 
431 	/*
432 	 * Now try and process the request records, non-blocking.
433 	 */
434 dorecs:
435 	if (ns_flag) {
436 		slp->ns_flag |= ns_flag;
437 	}
438 	if (waitflag == MBUF_DONTWAIT) {
439 		int wake = (slp->ns_flag & SLP_WORKTODO);
440 		lck_rw_done(&slp->ns_rwlock);
441 		if (wake && nfsd_thread_count) {
442 			while (1) {
443 				if ((slp->ns_flag & SLP_VALID) == 0) {
444 					break;
445 				}
446 				if (lck_mtx_try_lock(&nfsd_mutex)) {
447 					/* Mutex acquired */
448 					nfsrv_wakenfsd(slp);
449 					lck_mtx_unlock(&nfsd_mutex);
450 					break;
451 				}
452 				IOSleep(NFS_TRYLOCK_MSEC_SLEEP);
453 			}
454 		}
455 	}
456 }
457 
458 /*
459  * Try and extract an RPC request from the mbuf data list received on a
460  * stream socket. The "waitflag" argument indicates whether or not it
461  * can sleep.
462  */
463 int
nfsrv_getstream(struct nfsrv_sock * slp,int waitflag)464 nfsrv_getstream(struct nfsrv_sock *slp, int waitflag)
465 {
466 	mbuf_t m;
467 	char *cp1, *cp2, *mdata;
468 	int error;
469 	size_t len, mlen;
470 	mbuf_t om, m2, recm;
471 	u_int32_t recmark;
472 
473 	if (slp->ns_flag & SLP_GETSTREAM) {
474 		panic("nfs getstream");
475 	}
476 	slp->ns_flag |= SLP_GETSTREAM;
477 	for (;;) {
478 		if (slp->ns_reclen == 0) {
479 			if (slp->ns_cc < NFSX_UNSIGNED) {
480 				slp->ns_flag &= ~SLP_GETSTREAM;
481 				return 0;
482 			}
483 			m = slp->ns_raw;
484 			mdata = mbuf_data(m);
485 			mlen = mbuf_len(m);
486 			if (mlen >= NFSX_UNSIGNED) {
487 				bcopy(mdata, (caddr_t)&recmark, NFSX_UNSIGNED);
488 				mdata += NFSX_UNSIGNED;
489 				mlen -= NFSX_UNSIGNED;
490 				mbuf_setdata(m, mdata, mlen);
491 			} else {
492 				cp1 = (caddr_t)&recmark;
493 				cp2 = mdata;
494 				while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
495 					while (mlen == 0) {
496 						m = mbuf_next(m);
497 						cp2 = mbuf_data(m);
498 						mlen = mbuf_len(m);
499 					}
500 					*cp1++ = *cp2++;
501 					mlen--;
502 					mbuf_setdata(m, cp2, mlen);
503 				}
504 			}
505 			slp->ns_cc -= NFSX_UNSIGNED;
506 			recmark = ntohl(recmark);
507 			slp->ns_reclen = recmark & ~0x80000000;
508 			if (recmark & 0x80000000) {
509 				slp->ns_flag |= SLP_LASTFRAG;
510 			} else {
511 				slp->ns_flag &= ~SLP_LASTFRAG;
512 			}
513 			if (slp->ns_reclen <= 0 || slp->ns_reclen > NFS_MAXPACKET) {
514 				slp->ns_flag &= ~SLP_GETSTREAM;
515 				return EPERM;
516 			}
517 		}
518 
519 		/*
520 		 * Now get the record part.
521 		 *
522 		 * Note that slp->ns_reclen may be 0.  Linux sometimes
523 		 * generates 0-length RPCs
524 		 */
525 		recm = NULL;
526 		if (slp->ns_cc == slp->ns_reclen) {
527 			recm = slp->ns_raw;
528 			slp->ns_raw = slp->ns_rawend = NULL;
529 			slp->ns_cc = slp->ns_reclen = 0;
530 		} else if (slp->ns_cc > slp->ns_reclen) {
531 			len = 0;
532 			m = slp->ns_raw;
533 			mlen = mbuf_len(m);
534 			mdata = mbuf_data(m);
535 			om = NULL;
536 			while (len < slp->ns_reclen) {
537 				if ((len + mlen) > slp->ns_reclen) {
538 					if (mbuf_copym(m, 0, slp->ns_reclen - len, waitflag, &m2)) {
539 						slp->ns_flag &= ~SLP_GETSTREAM;
540 						return EWOULDBLOCK;
541 					}
542 					if (om) {
543 						if (mbuf_setnext(om, m2)) {
544 							/* trouble... just drop it */
545 							printf("nfsrv_getstream: mbuf_setnext failed\n");
546 							mbuf_freem(m2);
547 							slp->ns_flag &= ~SLP_GETSTREAM;
548 							return EWOULDBLOCK;
549 						}
550 						recm = slp->ns_raw;
551 					} else {
552 						recm = m2;
553 					}
554 					mdata += slp->ns_reclen - len;
555 					mlen -= slp->ns_reclen - len;
556 					mbuf_setdata(m, mdata, mlen);
557 					len = slp->ns_reclen;
558 				} else if ((len + mlen) == slp->ns_reclen) {
559 					om = m;
560 					len += mlen;
561 					m = mbuf_next(m);
562 					recm = slp->ns_raw;
563 					if (mbuf_setnext(om, NULL)) {
564 						printf("nfsrv_getstream: mbuf_setnext failed 2\n");
565 						slp->ns_flag &= ~SLP_GETSTREAM;
566 						return EWOULDBLOCK;
567 					}
568 					mlen = mbuf_len(m);
569 					mdata = mbuf_data(m);
570 				} else {
571 					om = m;
572 					len += mlen;
573 					m = mbuf_next(m);
574 					mlen = mbuf_len(m);
575 					mdata = mbuf_data(m);
576 				}
577 			}
578 			slp->ns_raw = m;
579 			slp->ns_cc -= len;
580 			slp->ns_reclen = 0;
581 		} else {
582 			slp->ns_flag &= ~SLP_GETSTREAM;
583 			return 0;
584 		}
585 
586 		/*
587 		 * Accumulate the fragments into a record.
588 		 */
589 		if (slp->ns_frag == NULL) {
590 			slp->ns_frag = recm;
591 		} else {
592 			m = slp->ns_frag;
593 			while ((m2 = mbuf_next(m))) {
594 				m = m2;
595 			}
596 			if ((error = mbuf_setnext(m, recm))) {
597 				panic("nfsrv_getstream: mbuf_setnext failed 3, %d", error);
598 			}
599 		}
600 		if (slp->ns_flag & SLP_LASTFRAG) {
601 			if (slp->ns_recend) {
602 				mbuf_setnextpkt(slp->ns_recend, slp->ns_frag);
603 			} else {
604 				slp->ns_rec = slp->ns_frag;
605 				slp->ns_flag |= SLP_DOREC;
606 			}
607 			slp->ns_recend = slp->ns_frag;
608 			slp->ns_frag = NULL;
609 		}
610 	}
611 }
612 
613 /*
614  * Parse an RPC header.
615  */
616 int
nfsrv_dorec(struct nfsrv_sock * slp,struct nfsd * nfsd,struct nfsrv_descript ** ndp)617 nfsrv_dorec(
618 	struct nfsrv_sock *slp,
619 	struct nfsd *nfsd,
620 	struct nfsrv_descript **ndp)
621 {
622 	mbuf_t m;
623 	mbuf_t nam;
624 	struct nfsrv_descript *nd;
625 	int error = 0;
626 
627 	*ndp = NULL;
628 	if (!(slp->ns_flag & (SLP_VALID | SLP_DOREC)) || (slp->ns_rec == NULL)) {
629 		return ENOBUFS;
630 	}
631 	nd = zalloc(nfsrv_descript_zone);
632 	m = slp->ns_rec;
633 	slp->ns_rec = mbuf_nextpkt(m);
634 	if (slp->ns_rec) {
635 		mbuf_setnextpkt(m, NULL);
636 	} else {
637 		slp->ns_flag &= ~SLP_DOREC;
638 		slp->ns_recend = NULL;
639 	}
640 	slp->ns_reccnt--;
641 	if (mbuf_type(m) == MBUF_TYPE_SONAME) {
642 		nam = m;
643 		m = mbuf_next(m);
644 		if ((error = mbuf_setnext(nam, NULL))) {
645 			panic("nfsrv_dorec: mbuf_setnext failed %d", error);
646 		}
647 	} else {
648 		nam = NULL;
649 	}
650 	nd->nd_nam2 = nam;
651 	nfsm_chain_dissect_init(error, &nd->nd_nmreq, m);
652 	if (!error) {
653 		error = nfsrv_getreq(nd);
654 	}
655 	if (error) {
656 		if (nam) {
657 			mbuf_freem(nam);
658 		}
659 		if (nd->nd_gss_context) {
660 			nfs_gss_svc_ctx_deref(nd->nd_gss_context);
661 		}
662 		NFS_ZFREE(nfsrv_descript_zone, nd);
663 		return error;
664 	}
665 	nd->nd_mrep = NULL;
666 	*ndp = nd;
667 	nfsd->nfsd_nd = nd;
668 	return 0;
669 }
670 
671 /*
672  * Parse an RPC request
673  * - verify it
674  * - fill in the cred struct.
675  */
676 int
nfsrv_getreq(struct nfsrv_descript * nd)677 nfsrv_getreq(struct nfsrv_descript *nd)
678 {
679 	struct nfsm_chain *nmreq;
680 	int len, i;
681 	u_int32_t nfsvers, auth_type;
682 	int error = 0;
683 	uid_t user_id;
684 	gid_t group_id;
685 	short ngroups;
686 	uint32_t val;
687 
688 	nd->nd_cr = NULL;
689 	nd->nd_gss_context = NULL;
690 	nd->nd_gss_seqnum = 0;
691 	nd->nd_gss_mb = NULL;
692 
693 	user_id = group_id = -2;
694 	val = auth_type = len = 0;
695 
696 	nmreq = &nd->nd_nmreq;
697 	nfsm_chain_get_32(error, nmreq, nd->nd_retxid); // XID
698 	nfsm_chain_get_32(error, nmreq, val);           // RPC Call
699 	if (!error && (val != RPC_CALL)) {
700 		error = EBADRPC;
701 	}
702 	nfsmout_if(error);
703 	nd->nd_repstat = 0;
704 	nfsm_chain_get_32(error, nmreq, val);   // RPC Version
705 	nfsmout_if(error);
706 	if (val != RPC_VER2) {
707 		nd->nd_repstat = ERPCMISMATCH;
708 		nd->nd_procnum = NFSPROC_NOOP;
709 		return 0;
710 	}
711 	nfsm_chain_get_32(error, nmreq, val);   // RPC Program Number
712 	nfsmout_if(error);
713 	if (val != NFS_PROG) {
714 		nd->nd_repstat = EPROGUNAVAIL;
715 		nd->nd_procnum = NFSPROC_NOOP;
716 		return 0;
717 	}
718 	nfsm_chain_get_32(error, nmreq, nfsvers);// NFS Version Number
719 	nfsmout_if(error);
720 	if ((nfsvers < NFS_VER2) || (nfsvers > NFS_VER3)) {
721 		nd->nd_repstat = EPROGMISMATCH;
722 		nd->nd_procnum = NFSPROC_NOOP;
723 		return 0;
724 	}
725 	nd->nd_vers = nfsvers;
726 	nfsm_chain_get_32(error, nmreq, nd->nd_procnum);// NFS Procedure Number
727 	nfsmout_if(error);
728 	if ((nd->nd_procnum >= NFS_NPROCS) ||
729 	    ((nd->nd_vers == NFS_VER2) && (nd->nd_procnum > NFSV2PROC_STATFS))) {
730 		nd->nd_repstat = EPROCUNAVAIL;
731 		nd->nd_procnum = NFSPROC_NOOP;
732 		return 0;
733 	}
734 	if (nfsvers != NFS_VER3) {
735 		nd->nd_procnum = nfsv3_procid[nd->nd_procnum];
736 	}
737 	nfsm_chain_get_32(error, nmreq, auth_type);     // Auth Flavor
738 	nfsm_chain_get_32(error, nmreq, len);           // Auth Length
739 	if (!error && (len < 0 || len > RPCAUTH_MAXSIZ)) {
740 		error = EBADRPC;
741 	}
742 	nfsmout_if(error);
743 
744 	/* Handle authentication */
745 	if (auth_type == RPCAUTH_SYS) {
746 		struct posix_cred temp_pcred;
747 		if (nd->nd_procnum == NFSPROC_NULL) {
748 			return 0;
749 		}
750 		nd->nd_sec = RPCAUTH_SYS;
751 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);    // skip stamp
752 		nfsm_chain_get_32(error, nmreq, len);           // hostname length
753 		if (len < 0 || len > NFS_MAXNAMLEN) {
754 			error = EBADRPC;
755 		}
756 		nfsm_chain_adv(error, nmreq, nfsm_rndup(len));  // skip hostname
757 		nfsmout_if(error);
758 
759 		/* create a temporary credential using the bits from the wire */
760 		bzero(&temp_pcred, sizeof(temp_pcred));
761 		nfsm_chain_get_32(error, nmreq, user_id);
762 		nfsm_chain_get_32(error, nmreq, group_id);
763 		temp_pcred.cr_groups[0] = group_id;
764 		nfsm_chain_get_32(error, nmreq, len);           // extra GID count
765 		if ((len < 0) || (len > RPCAUTH_UNIXGIDS)) {
766 			error = EBADRPC;
767 		}
768 		nfsmout_if(error);
769 		for (i = 1; i <= len; i++) {
770 			if (i < NGROUPS) {
771 				nfsm_chain_get_32(error, nmreq, temp_pcred.cr_groups[i]);
772 			} else {
773 				nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
774 			}
775 		}
776 		nfsmout_if(error);
777 		ngroups = (len >= NGROUPS) ? NGROUPS : (short)(len + 1);
778 		if (ngroups > 1) {
779 			nfsrv_group_sort(&temp_pcred.cr_groups[0], ngroups);
780 		}
781 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);    // verifier flavor (should be AUTH_NONE)
782 		nfsm_chain_get_32(error, nmreq, len);           // verifier length
783 		if (len < 0 || len > RPCAUTH_MAXSIZ) {
784 			error = EBADRPC;
785 		}
786 		if (len > 0) {
787 			nfsm_chain_adv(error, nmreq, nfsm_rndup(len));
788 		}
789 
790 		/* request creation of a real credential */
791 		temp_pcred.cr_uid = user_id;
792 		temp_pcred.cr_ngroups = ngroups;
793 		nd->nd_cr = posix_cred_create(&temp_pcred);
794 		if (nd->nd_cr == NULL) {
795 			nd->nd_repstat = ENOMEM;
796 			nd->nd_procnum = NFSPROC_NOOP;
797 			return 0;
798 		}
799 	} else if (auth_type == RPCSEC_GSS) {
800 		error = nfs_gss_svc_cred_get(nd, nmreq);
801 		if (error) {
802 			if (error == EINVAL) {
803 				goto nfsmout;   // drop the request
804 			}
805 			nd->nd_repstat = error;
806 			nd->nd_procnum = NFSPROC_NOOP;
807 			return 0;
808 		}
809 	} else {
810 		if (nd->nd_procnum == NFSPROC_NULL) {   // assume it's AUTH_NONE
811 			return 0;
812 		}
813 		nd->nd_repstat = (NFSERR_AUTHERR | AUTH_REJECTCRED);
814 		nd->nd_procnum = NFSPROC_NOOP;
815 		return 0;
816 	}
817 	return 0;
818 nfsmout:
819 	if (IS_VALID_CRED(nd->nd_cr)) {
820 		kauth_cred_unref(&nd->nd_cr);
821 	}
822 	nfsm_chain_cleanup(nmreq);
823 	return error;
824 }
825 
826 /*
827  * Search for a sleeping nfsd and wake it up.
828  * SIDE EFFECT: If none found, make sure the socket is queued up so that one
829  * of the running nfsds will go look for the work in the nfsrv_sockwait list.
830  * Note: Must be called with nfsd_mutex held.
831  */
832 void
nfsrv_wakenfsd(struct nfsrv_sock * slp)833 nfsrv_wakenfsd(struct nfsrv_sock *slp)
834 {
835 	struct nfsd *nd;
836 
837 	while (1) {
838 		if ((slp->ns_flag & SLP_VALID) == 0) {
839 			return;
840 		}
841 		if (lck_rw_try_lock_exclusive(&slp->ns_rwlock)) {
842 			/* Exclusive lock acquired */
843 			break;
844 		}
845 		IOSleep(NFS_TRYLOCK_MSEC_SLEEP);
846 	}
847 
848 	/* if there's work to do on this socket, make sure it's queued up */
849 	if ((slp->ns_flag & SLP_WORKTODO) && !(slp->ns_flag & SLP_QUEUED)) {
850 		TAILQ_INSERT_TAIL(&nfsrv_sockwait, slp, ns_svcq);
851 		slp->ns_flag |= SLP_WAITQ;
852 	}
853 	lck_rw_done(&slp->ns_rwlock);
854 
855 	/* wake up a waiting nfsd, if possible */
856 	nd = TAILQ_FIRST(&nfsd_queue);
857 	if (!nd) {
858 		return;
859 	}
860 
861 	TAILQ_REMOVE(&nfsd_queue, nd, nfsd_queue);
862 	nd->nfsd_flag &= ~NFSD_WAITING;
863 	wakeup(nd);
864 }
865 
866 #endif /* CONFIG_NFS_SERVER */
867