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