xref: /xnu-8792.81.2/bsd/nfs/nfs_gss.c (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
1 /*
2  * Copyright (c) 2007-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 
29 #include <nfs/nfs_conf.h>
30 #if CONFIG_NFS_SERVER
31 
32 /*************
33  * These functions implement RPCSEC_GSS security for the NFS client and server.
34  * The code is specific to the use of Kerberos v5 and the use of DES MAC MD5
35  * protection as described in Internet RFC 2203 and 2623.
36  *
37  * In contrast to the original AUTH_SYS authentication, RPCSEC_GSS is stateful.
38  * It requires the client and server negotiate a secure connection as part of a
39  * security context. The context state is maintained in client and server structures.
40  * On the client side, each user of an NFS mount is assigned their own context,
41  * identified by UID, on their first use of the mount, and it persists until the
42  * unmount or until the context is renewed.  Each user context has a corresponding
43  * server context which the server maintains until the client destroys it, or
44  * until the context expires.
45  *
46  * The client and server contexts are set up dynamically.  When a user attempts
47  * to send an NFS request, if there is no context for the user, then one is
48  * set up via an exchange of NFS null procedure calls as described in RFC 2203.
49  * During this exchange, the client and server pass a security token that is
50  * forwarded via Mach upcall to the gssd, which invokes the GSS-API to authenticate
51  * the user to the server (and vice-versa). The client and server also receive
52  * a unique session key that can be used to digitally sign the credentials and
53  * verifier or optionally to provide data integrity and/or privacy.
54  *
55  * Once the context is complete, the client and server enter a normal data
56  * exchange phase - beginning with the NFS request that prompted the context
57  * creation. During this phase, the client's RPC header contains an RPCSEC_GSS
58  * credential and verifier, and the server returns a verifier as well.
59  * For simple authentication, the verifier contains a signed checksum of the
60  * RPC header, including the credential.  The server's verifier has a signed
61  * checksum of the current sequence number.
62  *
63  * Each client call contains a sequence number that nominally increases by one
64  * on each request.  The sequence number is intended to prevent replay attacks.
65  * Since the protocol can be used over UDP, there is some allowance for
66  * out-of-sequence requests, so the server checks whether the sequence numbers
67  * are within a sequence "window". If a sequence number is outside the lower
68  * bound of the window, the server silently drops the request. This has some
69  * implications for retransmission. If a request needs to be retransmitted, the
70  * client must bump the sequence number even if the request XID is unchanged.
71  *
72  * When the NFS mount is unmounted, the client sends a "destroy" credential
73  * to delete the server's context for each user of the mount. Since it's
74  * possible for the client to crash or disconnect without sending the destroy
75  * message, the server has a thread that reaps contexts that have been idle
76  * too long.
77  */
78 
79 #include <sys/systm.h>
80 #include <sys/kauth.h>
81 #include <sys/mount_internal.h>
82 #include <sys/kpi_mbuf.h>
83 
84 #include <kern/host.h>
85 
86 #include <mach/host_priv.h>
87 #include <mach/vm_map.h>
88 #include <vm/vm_map.h>
89 #include <gssd/gssd_mach.h>
90 
91 #include <nfs/rpcv2.h>
92 #include <nfs/nfsproto.h>
93 #include <nfs/nfs.h>
94 #include <nfs/nfs_gss.h>
95 #include <nfs/xdr_subs.h>
96 #include <nfs/nfsm_subs.h>
97 #include <nfs/nfs_gss.h>
98 
99 #define NFS_GSS_MACH_MAX_RETRIES 3
100 
101 #define NFSRV_GSS_DBG(...) NFSRV_DBG(NFSRV_FAC_GSS, 7, ## __VA_ARGS__)
102 
103 u_long nfs_gss_svc_ctx_hash;
104 struct nfs_gss_svc_ctx_hashhead *nfs_gss_svc_ctx_hashtbl;
105 static LCK_GRP_DECLARE(nfs_gss_svc_grp, "rpcsec_gss_svc");
106 static LCK_MTX_DECLARE(nfs_gss_svc_ctx_mutex, &nfs_gss_svc_grp);
107 uint32_t nfsrv_gss_context_ttl = GSS_CTX_EXPIRE;
108 #define GSS_SVC_CTX_TTL ((uint64_t)max(2*GSS_CTX_PEND, nfsrv_gss_context_ttl) * NSEC_PER_SEC)
109 
110 #define KRB5_MAX_MIC_SIZE 128
111 static uint8_t xdrpad[] = { 0x00, 0x00, 0x00, 0x00};
112 
113 static struct nfs_gss_svc_ctx *nfs_gss_svc_ctx_find(uint32_t);
114 static void     nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx *);
115 static void     nfs_gss_svc_ctx_timer(void *, void *);
116 static int      nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx *);
117 static int      nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *, uint32_t);
118 
119 /* This is only used by server code */
120 static void     nfs_gss_nfsm_chain(struct nfsm_chain *, mbuf_t);
121 
122 static void     host_release_special_port(mach_port_t);
123 static void     nfs_gss_mach_alloc_buffer(u_char *, size_t, vm_map_copy_t *);
124 static int      nfs_gss_mach_vmcopyout(vm_map_copy_t, uint32_t, u_char *);
125 
126 static int      nfs_gss_mchain_length(mbuf_t);
127 static int      nfs_gss_append_chain(struct nfsm_chain *, mbuf_t);
128 static int      nfs_gss_seqbits_size(uint32_t);
129 
130 thread_call_t nfs_gss_svc_ctx_timer_call;
131 int nfs_gss_timer_on = 0;
132 uint32_t nfs_gss_ctx_count = 0;
133 const uint32_t nfs_gss_ctx_max = GSS_SVC_MAXCONTEXTS;
134 
135 /*
136  * Common RPCSEC_GSS support routines
137  */
138 
139 static errno_t
rpc_gss_prepend_32(mbuf_t * mb,uint32_t value)140 rpc_gss_prepend_32(mbuf_t *mb, uint32_t value)
141 {
142 	int error;
143 	uint32_t *data;
144 
145 #if 0
146 	data = mbuf_data(*mb);
147 	/*
148 	 * If a wap token comes back and is not aligned
149 	 * get a new buffer (which should be aligned) to put the
150 	 * length in.
151 	 */
152 	if ((uintptr_t)data & 0x3) {
153 		mbuf_t nmb;
154 
155 		error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &nmb);
156 		if (error) {
157 			return error;
158 		}
159 		mbuf_setnext(nmb, *mb);
160 		*mb = nmb;
161 	}
162 #endif
163 	error = mbuf_prepend(mb, sizeof(uint32_t), MBUF_WAITOK);
164 	if (error) {
165 		return error;
166 	}
167 
168 	data = mbuf_data(*mb);
169 	*data = txdr_unsigned(value);
170 
171 	return 0;
172 }
173 
174 /*
175  * Prepend the sequence number to the xdr encode argumen or result
176  * Sequence number is prepended in its own mbuf.
177  *
178  * On successful return mbp_head will point to the old mbuf chain
179  * prepended  with a new mbuf that has the sequence number.
180  */
181 
182 static errno_t
rpc_gss_data_create(mbuf_t * mbp_head,uint32_t seqnum)183 rpc_gss_data_create(mbuf_t *mbp_head, uint32_t seqnum)
184 {
185 	int error;
186 	mbuf_t mb;
187 	struct nfsm_chain nmc;
188 	struct nfsm_chain *nmcp = &nmc;
189 	uint8_t *data;
190 
191 	error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
192 	if (error) {
193 		return error;
194 	}
195 	data = mbuf_data(mb);
196 #if 0
197 	/* Reserve space for prepending */
198 	len = mbuf_maxlen(mb);
199 	len = (len & ~0x3) - NFSX_UNSIGNED;
200 	printf("%s: data = %p, len = %d\n", __func__, data, (int)len);
201 	error = mbuf_setdata(mb, data + len, 0);
202 	if (error || mbuf_trailingspace(mb)) {
203 		printf("%s: data = %p trailingspace = %d error = %d\n", __func__, mbuf_data(mb), (int)mbuf_trailingspace(mb), error);
204 	}
205 #endif
206 	/* Reserve 16 words for prepending */
207 	error = mbuf_setdata(mb, data + 16 * sizeof(uint32_t), 0);
208 	nfsm_chain_init(nmcp, mb);
209 	nfsm_chain_add_32(error, nmcp, seqnum);
210 	nfsm_chain_build_done(error, nmcp);
211 	if (error) {
212 		return EINVAL;
213 	}
214 	mbuf_setnext(nmcp->nmc_mcur, *mbp_head);
215 	*mbp_head = nmcp->nmc_mhead;
216 
217 	return 0;
218 }
219 
220 /*
221  * Create an rpc_gss_integ_data_t given an argument or result in mb_head.
222  * On successful return mb_head will point to the rpc_gss_integ_data_t of length len.
223  *      Note mb_head will now point to a 4 byte sequence number. len does not include
224  *	any extra xdr padding.
225  * Returns 0 on success, else an errno_t
226  */
227 
228 static errno_t
rpc_gss_integ_data_create(gss_ctx_id_t ctx,mbuf_t * mb_head,uint32_t seqnum,uint32_t * len)229 rpc_gss_integ_data_create(gss_ctx_id_t ctx, mbuf_t *mb_head, uint32_t seqnum, uint32_t *len)
230 {
231 	uint32_t error;
232 	uint32_t major;
233 	uint32_t length;
234 	gss_buffer_desc mic;
235 	struct nfsm_chain nmc = {};
236 
237 	/* Length of the argument or result */
238 	length = nfs_gss_mchain_length(*mb_head);
239 	if (len) {
240 		*len = length;
241 	}
242 	error = rpc_gss_data_create(mb_head, seqnum);
243 	if (error) {
244 		return error;
245 	}
246 
247 	/*
248 	 * length is the length of the rpc_gss_data
249 	 */
250 	length += NFSX_UNSIGNED;  /* Add the sequence number to the length */
251 	major = gss_krb5_get_mic_mbuf(&error, ctx, 0, *mb_head, 0, length, &mic);
252 	if (major != GSS_S_COMPLETE) {
253 		printf("gss_krb5_get_mic_mbuf failed %d\n", error);
254 		return error;
255 	}
256 
257 	error = rpc_gss_prepend_32(mb_head, length);
258 	if (error) {
259 		return error;
260 	}
261 
262 	nfsm_chain_dissect_init(error, &nmc, *mb_head);
263 	/* Append GSS mic token by advancing rpc_gss_data_t length + NFSX_UNSIGNED (size of the length field) */
264 	nfsm_chain_adv(error, &nmc, length + NFSX_UNSIGNED);
265 	nfsm_chain_finish_mbuf(error, &nmc); // Force the mic into its own sub chain.
266 	nfsm_chain_add_32(error, &nmc, mic.length);
267 	nfsm_chain_add_opaque(error, &nmc, mic.value, mic.length);
268 	nfsm_chain_build_done(error, &nmc);
269 	gss_release_buffer(NULL, &mic);
270 
271 //	printmbuf("rpc_gss_integ_data_create done", *mb_head, 0, 0);
272 	assert(nmc.nmc_mhead == *mb_head);
273 
274 	return error;
275 }
276 
277 /*
278  * Create an rpc_gss_priv_data_t out of the supplied raw arguments or results in mb_head.
279  * On successful return mb_head will point to a wrap token of lenght len.
280  *	Note len does not include any xdr padding
281  * Returns 0 on success, else an errno_t
282  */
283 static errno_t
rpc_gss_priv_data_create(gss_ctx_id_t ctx,mbuf_t * mb_head,uint32_t seqnum,uint32_t * len)284 rpc_gss_priv_data_create(gss_ctx_id_t ctx, mbuf_t *mb_head, uint32_t seqnum, uint32_t *len)
285 {
286 	uint32_t error;
287 	uint32_t major;
288 	struct nfsm_chain nmc;
289 	uint32_t pad;
290 	uint32_t length;
291 
292 	error = rpc_gss_data_create(mb_head, seqnum);
293 	if (error) {
294 		return error;
295 	}
296 
297 	length = nfs_gss_mchain_length(*mb_head);
298 	major = gss_krb5_wrap_mbuf(&error, ctx, 1, 0, mb_head, 0, length, NULL);
299 	if (major != GSS_S_COMPLETE) {
300 		return error;
301 	}
302 
303 	length = nfs_gss_mchain_length(*mb_head);
304 	if (len) {
305 		*len = length;
306 	}
307 	pad = nfsm_pad(length);
308 
309 	/* Prepend the opaque length of rep rpc_gss_priv_data */
310 	error = rpc_gss_prepend_32(mb_head, length);
311 
312 	if (error) {
313 		return error;
314 	}
315 	if (pad) {
316 		nfsm_chain_dissect_init(error, &nmc, *mb_head);
317 		/* Advance the opauque size of length and length data */
318 		nfsm_chain_adv(error, &nmc, NFSX_UNSIGNED + length);
319 		nfsm_chain_finish_mbuf(error, &nmc);
320 		nfsm_chain_add_opaque_nopad(error, &nmc, xdrpad, pad);
321 		nfsm_chain_build_done(error, &nmc);
322 	}
323 
324 	return error;
325 }
326 
327 /*************
328  *
329  * Server functions
330  */
331 
332 /*
333  * Initialization when NFS starts
334  */
335 void
nfs_gss_svc_init(void)336 nfs_gss_svc_init(void)
337 {
338 	nfs_gss_svc_ctx_hashtbl = hashinit(SVC_CTX_HASHSZ, M_TEMP, &nfs_gss_svc_ctx_hash);
339 
340 	nfs_gss_svc_ctx_timer_call = thread_call_allocate(nfs_gss_svc_ctx_timer, NULL);
341 }
342 
343 /*
344  * Find a server context based on a handle value received
345  * in an RPCSEC_GSS credential.
346  */
347 static struct nfs_gss_svc_ctx *
nfs_gss_svc_ctx_find(uint32_t handle)348 nfs_gss_svc_ctx_find(uint32_t handle)
349 {
350 	struct nfs_gss_svc_ctx_hashhead *head;
351 	struct nfs_gss_svc_ctx *cp;
352 	uint64_t timenow;
353 
354 	if (handle == 0) {
355 		return NULL;
356 	}
357 
358 	head = &nfs_gss_svc_ctx_hashtbl[SVC_CTX_HASH(handle)];
359 	/*
360 	 * Don't return a context that is going to expire in GSS_CTX_PEND seconds
361 	 */
362 	clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC, &timenow);
363 
364 	lck_mtx_lock(&nfs_gss_svc_ctx_mutex);
365 
366 	LIST_FOREACH(cp, head, gss_svc_entries) {
367 		if (cp->gss_svc_handle == handle) {
368 			if (timenow > cp->gss_svc_incarnation + GSS_SVC_CTX_TTL) {
369 				/*
370 				 * Context has or is about to expire. Don't use.
371 				 * We'll return null and the client will have to create
372 				 * a new context.
373 				 */
374 				cp->gss_svc_handle = 0;
375 				/*
376 				 * Make sure though that we stay around for GSS_CTX_PEND seconds
377 				 * for other threads that might be using the context.
378 				 */
379 				cp->gss_svc_incarnation = timenow;
380 
381 				cp = NULL;
382 				break;
383 			}
384 			lck_mtx_lock(&cp->gss_svc_mtx);
385 			cp->gss_svc_refcnt++;
386 			lck_mtx_unlock(&cp->gss_svc_mtx);
387 			break;
388 		}
389 	}
390 
391 	lck_mtx_unlock(&nfs_gss_svc_ctx_mutex);
392 
393 	return cp;
394 }
395 
396 /*
397  * Insert a new server context into the hash table
398  * and start the context reap thread if necessary.
399  */
400 static void
nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx * cp)401 nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx *cp)
402 {
403 	struct nfs_gss_svc_ctx_hashhead *head;
404 	struct nfs_gss_svc_ctx *p;
405 
406 	lck_mtx_lock(&nfs_gss_svc_ctx_mutex);
407 
408 	/*
409 	 * Give the client a random handle so that if we reboot
410 	 * it's unlikely the client will get a bad context match.
411 	 * Make sure it's not zero or already assigned.
412 	 */
413 retry:
414 	cp->gss_svc_handle = random();
415 	if (cp->gss_svc_handle == 0) {
416 		goto retry;
417 	}
418 	head = &nfs_gss_svc_ctx_hashtbl[SVC_CTX_HASH(cp->gss_svc_handle)];
419 	LIST_FOREACH(p, head, gss_svc_entries)
420 	if (p->gss_svc_handle == cp->gss_svc_handle) {
421 		goto retry;
422 	}
423 
424 	clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC,
425 	    &cp->gss_svc_incarnation);
426 	LIST_INSERT_HEAD(head, cp, gss_svc_entries);
427 	nfs_gss_ctx_count++;
428 
429 	if (!nfs_gss_timer_on) {
430 		nfs_gss_timer_on = 1;
431 
432 		nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call,
433 		    min(GSS_TIMER_PERIOD, max(GSS_CTX_TTL_MIN, nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
434 	}
435 
436 	lck_mtx_unlock(&nfs_gss_svc_ctx_mutex);
437 }
438 
439 /*
440  * This function is called via the kernel's callout
441  * mechanism.  It runs only when there are
442  * cached RPCSEC_GSS contexts.
443  */
444 void
nfs_gss_svc_ctx_timer(__unused void * param1,__unused void * param2)445 nfs_gss_svc_ctx_timer(__unused void *param1, __unused void *param2)
446 {
447 	struct nfs_gss_svc_ctx *cp, *next;
448 	uint64_t timenow;
449 	int contexts = 0;
450 	int i;
451 
452 	lck_mtx_lock(&nfs_gss_svc_ctx_mutex);
453 	clock_get_uptime(&timenow);
454 
455 	NFSRV_GSS_DBG("is running\n");
456 
457 	/*
458 	 * Scan all the hash chains
459 	 */
460 	for (i = 0; i < SVC_CTX_HASHSZ; i++) {
461 		/*
462 		 * For each hash chain, look for entries
463 		 * that haven't been used in a while.
464 		 */
465 		LIST_FOREACH_SAFE(cp, &nfs_gss_svc_ctx_hashtbl[i], gss_svc_entries, next) {
466 			contexts++;
467 			if (timenow > cp->gss_svc_incarnation +
468 			    (cp->gss_svc_handle ? GSS_SVC_CTX_TTL : 0)
469 			    && cp->gss_svc_refcnt == 0) {
470 				/*
471 				 * A stale context - remove it
472 				 */
473 				LIST_REMOVE(cp, gss_svc_entries);
474 				NFSRV_GSS_DBG("Removing contex for %d\n", cp->gss_svc_uid);
475 				if (cp->gss_svc_seqbits) {
476 					kfree_data(cp->gss_svc_seqbits, nfs_gss_seqbits_size(cp->gss_svc_seqwin));
477 				}
478 				lck_mtx_destroy(&cp->gss_svc_mtx, &nfs_gss_svc_grp);
479 				kfree_type(struct nfs_gss_svc_ctx, cp);
480 				contexts--;
481 			}
482 		}
483 	}
484 
485 	nfs_gss_ctx_count = contexts;
486 
487 	/*
488 	 * If there are still some cached contexts left,
489 	 * set up another callout to check on them later.
490 	 */
491 	nfs_gss_timer_on = nfs_gss_ctx_count > 0;
492 	if (nfs_gss_timer_on) {
493 		nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call,
494 		    min(GSS_TIMER_PERIOD, max(GSS_CTX_TTL_MIN, nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
495 	}
496 
497 	lck_mtx_unlock(&nfs_gss_svc_ctx_mutex);
498 }
499 
500 /*
501  * Here the server receives an RPCSEC_GSS credential in an
502  * RPC call header.  First there's some checking to make sure
503  * the credential is appropriate - whether the context is still
504  * being set up, or is complete.  Then we use the handle to find
505  * the server's context and validate the verifier, which contains
506  * a signed checksum of the RPC header. If the verifier checks
507  * out, we extract the user's UID and groups from the context
508  * and use it to set up a UNIX credential for the user's request.
509  */
510 int
nfs_gss_svc_cred_get(struct nfsrv_descript * nd,struct nfsm_chain * nmc)511 nfs_gss_svc_cred_get(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
512 {
513 	uint32_t vers, proc, seqnum, service;
514 	uint32_t handle, handle_len;
515 	uint32_t major;
516 	struct nfs_gss_svc_ctx *cp = NULL;
517 	uint32_t flavor = 0;
518 	int error = 0;
519 	uint32_t arglen;
520 	size_t argsize, start, header_len;
521 	gss_buffer_desc cksum;
522 	struct nfsm_chain nmc_tmp;
523 	mbuf_t reply_mbuf, prev_mbuf, pad_mbuf;
524 
525 	vers = proc = seqnum = service = handle_len = 0;
526 	arglen = 0;
527 
528 	nfsm_chain_get_32(error, nmc, vers);
529 	if (vers != RPCSEC_GSS_VERS_1) {
530 		error = NFSERR_AUTHERR | AUTH_REJECTCRED;
531 		goto nfsmout;
532 	}
533 
534 	nfsm_chain_get_32(error, nmc, proc);
535 	nfsm_chain_get_32(error, nmc, seqnum);
536 	nfsm_chain_get_32(error, nmc, service);
537 	nfsm_chain_get_32(error, nmc, handle_len);
538 	if (error) {
539 		goto nfsmout;
540 	}
541 
542 	/*
543 	 * Make sure context setup/destroy is being done with a nullproc
544 	 */
545 	if (proc != RPCSEC_GSS_DATA && nd->nd_procnum != NFSPROC_NULL) {
546 		error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
547 		goto nfsmout;
548 	}
549 
550 	/*
551 	 * If the sequence number is greater than the max
552 	 * allowable, reject and have the client init a
553 	 * new context.
554 	 */
555 	if (seqnum > GSS_MAXSEQ) {
556 		error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
557 		goto nfsmout;
558 	}
559 
560 	nd->nd_sec =
561 	    service == RPCSEC_GSS_SVC_NONE ?      RPCAUTH_KRB5 :
562 	    service == RPCSEC_GSS_SVC_INTEGRITY ? RPCAUTH_KRB5I :
563 	    service == RPCSEC_GSS_SVC_PRIVACY ?   RPCAUTH_KRB5P : 0;
564 
565 	if (proc == RPCSEC_GSS_INIT) {
566 		/*
567 		 * Limit the total number of contexts
568 		 */
569 		if (nfs_gss_ctx_count > nfs_gss_ctx_max) {
570 			error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
571 			goto nfsmout;
572 		}
573 
574 		/*
575 		 * Set up a new context
576 		 */
577 		cp = kalloc_type(struct nfs_gss_svc_ctx,
578 		    Z_WAITOK | Z_ZERO | Z_NOFAIL);
579 		lck_mtx_init(&cp->gss_svc_mtx, &nfs_gss_svc_grp, LCK_ATTR_NULL);
580 		cp->gss_svc_refcnt = 1;
581 	} else {
582 		/*
583 		 * Use the handle to find the context
584 		 */
585 		if (handle_len != sizeof(handle)) {
586 			error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
587 			goto nfsmout;
588 		}
589 		nfsm_chain_get_32(error, nmc, handle);
590 		if (error) {
591 			goto nfsmout;
592 		}
593 		cp = nfs_gss_svc_ctx_find(handle);
594 		if (cp == NULL) {
595 			error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
596 			goto nfsmout;
597 		}
598 	}
599 
600 	cp->gss_svc_proc = proc;
601 
602 	if (proc == RPCSEC_GSS_DATA || proc == RPCSEC_GSS_DESTROY) {
603 		struct posix_cred temp_pcred;
604 
605 		if (cp->gss_svc_seqwin == 0) {
606 			/*
607 			 * Context isn't complete
608 			 */
609 			error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
610 			goto nfsmout;
611 		}
612 
613 		if (!nfs_gss_svc_seqnum_valid(cp, seqnum)) {
614 			/*
615 			 * Sequence number is bad
616 			 */
617 			error = EINVAL; // drop the request
618 			goto nfsmout;
619 		}
620 
621 		/*
622 		 * Validate the verifier.
623 		 * The verifier contains an encrypted checksum
624 		 * of the call header from the XID up to and
625 		 * including the credential.  We compute the
626 		 * checksum and compare it with what came in
627 		 * the verifier.
628 		 */
629 		header_len = nfsm_chain_offset(nmc);
630 		nfsm_chain_get_32(error, nmc, flavor);
631 		nfsm_chain_get_32(error, nmc, cksum.length);
632 		if (error) {
633 			goto nfsmout;
634 		}
635 		if (flavor != RPCSEC_GSS || cksum.length > KRB5_MAX_MIC_SIZE) {
636 			error = NFSERR_AUTHERR | AUTH_BADVERF;
637 		} else {
638 			cksum.value = kalloc_data(cksum.length, Z_WAITOK | Z_NOFAIL);
639 			nfsm_chain_get_opaque(error, nmc, cksum.length, cksum.value);
640 		}
641 		if (error) {
642 			goto nfsmout;
643 		}
644 
645 		/* Now verify the client's call header checksum */
646 		major = gss_krb5_verify_mic_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id, nmc->nmc_mhead, 0, header_len, &cksum, NULL);
647 		(void)gss_release_buffer(NULL, &cksum);
648 		if (major != GSS_S_COMPLETE) {
649 			printf("Server header: gss_krb5_verify_mic_mbuf failed %d\n", error);
650 			error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
651 			goto nfsmout;
652 		}
653 
654 		nd->nd_gss_seqnum = seqnum;
655 
656 		/*
657 		 * Set up the user's cred
658 		 */
659 		bzero(&temp_pcred, sizeof(temp_pcred));
660 		temp_pcred.cr_uid = cp->gss_svc_uid;
661 		bcopy(cp->gss_svc_gids, temp_pcred.cr_groups,
662 		    sizeof(gid_t) * cp->gss_svc_ngroups);
663 		temp_pcred.cr_ngroups = (short)cp->gss_svc_ngroups;
664 
665 		nd->nd_cr = posix_cred_create(&temp_pcred);
666 		if (nd->nd_cr == NULL) {
667 			error = ENOMEM;
668 			goto nfsmout;
669 		}
670 		clock_get_uptime(&cp->gss_svc_incarnation);
671 
672 		/*
673 		 * If the call arguments are integrity or privacy protected
674 		 * then we need to check them here.
675 		 */
676 		switch (service) {
677 		case RPCSEC_GSS_SVC_NONE:
678 			/* nothing to do */
679 			break;
680 		case RPCSEC_GSS_SVC_INTEGRITY:
681 			/*
682 			 * Here's what we expect in the integrity call args:
683 			 *
684 			 * - length of seq num + call args (4 bytes)
685 			 * - sequence number (4 bytes)
686 			 * - call args (variable bytes)
687 			 * - length of checksum token
688 			 * - checksum of seqnum + call args
689 			 */
690 			nfsm_chain_get_32(error, nmc, arglen);          // length of args
691 			if (arglen > NFS_MAXPACKET) {
692 				error = EBADRPC;
693 				goto nfsmout;
694 			}
695 
696 			nmc_tmp = *nmc;
697 			nfsm_chain_adv(error, &nmc_tmp, arglen);
698 			nfsm_chain_get_32(error, &nmc_tmp, cksum.length);
699 			cksum.value = NULL;
700 			if (cksum.length > 0 && cksum.length < GSS_MAX_MIC_LEN) {
701 				cksum.value = kalloc_data(cksum.length, Z_WAITOK | Z_NOFAIL);
702 			} else {
703 				error = EBADRPC;
704 				goto nfsmout;
705 			}
706 			nfsm_chain_get_opaque(error, &nmc_tmp, cksum.length, cksum.value);
707 
708 			/* Verify the checksum over the call args */
709 			start = nfsm_chain_offset(nmc);
710 
711 			major = gss_krb5_verify_mic_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id,
712 			    nmc->nmc_mhead, start, arglen, &cksum, NULL);
713 			kfree_data(cksum.value, cksum.length);
714 			if (major != GSS_S_COMPLETE) {
715 				printf("Server args: gss_krb5_verify_mic_mbuf failed %d\n", error);
716 				error = EBADRPC;
717 				goto nfsmout;
718 			}
719 
720 			/*
721 			 * Get the sequence number prepended to the args
722 			 * and compare it against the one sent in the
723 			 * call credential.
724 			 */
725 			nfsm_chain_get_32(error, nmc, seqnum);
726 			if (seqnum != nd->nd_gss_seqnum) {
727 				error = EBADRPC;                        // returns as GARBAGEARGS
728 				goto nfsmout;
729 			}
730 			break;
731 		case RPCSEC_GSS_SVC_PRIVACY:
732 			/*
733 			 * Here's what we expect in the privacy call args:
734 			 *
735 			 * - length of wrap token
736 			 * - wrap token (37-40 bytes)
737 			 */
738 			prev_mbuf = nmc->nmc_mcur;
739 			nfsm_chain_get_32(error, nmc, arglen);          // length of args
740 			if (arglen > NFS_MAXPACKET) {
741 				error = EBADRPC;
742 				goto nfsmout;
743 			}
744 
745 			/* Get the wrap token (current mbuf in the chain starting at the current offset) */
746 			start = nmc->nmc_ptr - (caddr_t)mbuf_data(nmc->nmc_mcur);
747 
748 			/* split out the wrap token */
749 			argsize = arglen;
750 			error = gss_normalize_mbuf(nmc->nmc_mcur, start, &argsize, &reply_mbuf, &pad_mbuf, 0);
751 			if (error) {
752 				goto nfsmout;
753 			}
754 
755 			assert(argsize == arglen);
756 			if (pad_mbuf) {
757 				assert(nfsm_pad(arglen) == mbuf_len(pad_mbuf));
758 				mbuf_free(pad_mbuf);
759 			} else {
760 				assert(nfsm_pad(arglen) == 0);
761 			}
762 
763 			major = gss_krb5_unwrap_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id, &reply_mbuf, 0, arglen, NULL, NULL);
764 			if (major != GSS_S_COMPLETE) {
765 				printf("%s: gss_krb5_unwrap_mbuf failes %d\n", __func__, error);
766 				goto nfsmout;
767 			}
768 
769 			/* Now replace the wrapped arguments with the unwrapped ones */
770 			mbuf_setnext(prev_mbuf, reply_mbuf);
771 			nmc->nmc_mcur = reply_mbuf;
772 			nmc->nmc_ptr = mbuf_data(reply_mbuf);
773 			nmc->nmc_left = mbuf_len(reply_mbuf);
774 
775 			/*
776 			 * - sequence number (4 bytes)
777 			 * - call args
778 			 */
779 
780 			// nfsm_chain_reverse(nmc, nfsm_pad(toklen));
781 
782 			/*
783 			 * Get the sequence number prepended to the args
784 			 * and compare it against the one sent in the
785 			 * call credential.
786 			 */
787 			nfsm_chain_get_32(error, nmc, seqnum);
788 			if (seqnum != nd->nd_gss_seqnum) {
789 				printf("%s: Sequence number mismatch seqnum = %d nd->nd_gss_seqnum = %d\n",
790 				    __func__, seqnum, nd->nd_gss_seqnum);
791 				printmbuf("reply_mbuf", nmc->nmc_mhead, 0, 0);
792 				printf("reply_mbuf %p nmc_head %p\n", reply_mbuf, nmc->nmc_mhead);
793 				error = EBADRPC;                        // returns as GARBAGEARGS
794 				goto nfsmout;
795 			}
796 			break;
797 		}
798 	} else {
799 		uint32_t verflen;
800 		/*
801 		 * If the proc is RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT
802 		 * then we expect a null verifier.
803 		 */
804 		nfsm_chain_get_32(error, nmc, flavor);
805 		nfsm_chain_get_32(error, nmc, verflen);
806 		if (error || flavor != RPCAUTH_NULL || verflen > 0) {
807 			error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
808 		}
809 		if (error) {
810 			if (proc == RPCSEC_GSS_INIT) {
811 				lck_mtx_destroy(&cp->gss_svc_mtx, &nfs_gss_svc_grp);
812 				kfree_type(struct nfs_gss_svc_ctx, cp);
813 				cp = NULL;
814 			}
815 			goto nfsmout;
816 		}
817 	}
818 
819 	nd->nd_gss_context = cp;
820 	return 0;
821 nfsmout:
822 	if (cp) {
823 		nfs_gss_svc_ctx_deref(cp);
824 	}
825 	return error;
826 }
827 
828 /*
829  * Insert the server's verifier into the RPC reply header.
830  * It contains a signed checksum of the sequence number that
831  * was received in the RPC call.
832  * Then go on to add integrity or privacy if necessary.
833  */
834 int
nfs_gss_svc_verf_put(struct nfsrv_descript * nd,struct nfsm_chain * nmc)835 nfs_gss_svc_verf_put(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
836 {
837 	struct nfs_gss_svc_ctx *cp;
838 	int error = 0;
839 	gss_buffer_desc cksum, seqbuf;
840 	uint32_t network_seqnum;
841 	cp = nd->nd_gss_context;
842 	uint32_t major;
843 
844 	if (cp->gss_svc_major != GSS_S_COMPLETE) {
845 		/*
846 		 * If the context isn't yet complete
847 		 * then return a null verifier.
848 		 */
849 		nfsm_chain_add_32(error, nmc, RPCAUTH_NULL);
850 		nfsm_chain_add_32(error, nmc, 0);
851 		return error;
852 	}
853 
854 	/*
855 	 * Compute checksum of the request seq number
856 	 * If it's the final reply of context setup
857 	 * then return the checksum of the context
858 	 * window size.
859 	 */
860 	seqbuf.length = NFSX_UNSIGNED;
861 	if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
862 	    cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT) {
863 		network_seqnum = htonl(cp->gss_svc_seqwin);
864 	} else {
865 		network_seqnum = htonl(nd->nd_gss_seqnum);
866 	}
867 	seqbuf.value = &network_seqnum;
868 
869 	major = gss_krb5_get_mic((uint32_t *)&error, cp->gss_svc_ctx_id, 0, &seqbuf, &cksum);
870 	if (major != GSS_S_COMPLETE) {
871 		return error;
872 	}
873 
874 	/*
875 	 * Now wrap it in a token and add
876 	 * the verifier to the reply.
877 	 */
878 	nfsm_chain_add_32(error, nmc, RPCSEC_GSS);
879 	nfsm_chain_add_32(error, nmc, cksum.length);
880 	nfsm_chain_add_opaque(error, nmc, cksum.value, cksum.length);
881 	gss_release_buffer(NULL, &cksum);
882 
883 	return error;
884 }
885 
886 /*
887  * The results aren't available yet, but if they need to be
888  * checksummed for integrity protection or encrypted, then
889  * we can record the start offset here, insert a place-holder
890  * for the results length, as well as the sequence number.
891  * The rest of the work is done later by nfs_gss_svc_protect_reply()
892  * when the results are available.
893  */
894 int
nfs_gss_svc_prepare_reply(struct nfsrv_descript * nd,struct nfsm_chain * nmc)895 nfs_gss_svc_prepare_reply(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
896 {
897 	struct nfs_gss_svc_ctx *cp = nd->nd_gss_context;
898 	int error = 0;
899 
900 	if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
901 	    cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT) {
902 		return 0;
903 	}
904 
905 	switch (nd->nd_sec) {
906 	case RPCAUTH_KRB5:
907 		/* Nothing to do */
908 		break;
909 	case RPCAUTH_KRB5I:
910 	case RPCAUTH_KRB5P:
911 		nd->nd_gss_mb = nmc->nmc_mcur;                  // record current mbuf
912 		nfsm_chain_finish_mbuf(error, nmc);             // split the chain here
913 		break;
914 	}
915 
916 	return error;
917 }
918 
919 /*
920  * The results are checksummed or encrypted for return to the client
921  */
922 int
nfs_gss_svc_protect_reply(struct nfsrv_descript * nd,mbuf_t mrep __unused)923 nfs_gss_svc_protect_reply(struct nfsrv_descript *nd, mbuf_t mrep __unused)
924 {
925 	struct nfs_gss_svc_ctx *cp = nd->nd_gss_context;
926 	struct nfsm_chain nmrep_res, *nmc_res = &nmrep_res;
927 	mbuf_t mb, results;
928 	uint32_t reslen;
929 	int error = 0;
930 
931 	/* XXX
932 	 * Using a reference to the mbuf where we previously split the reply
933 	 * mbuf chain, we split the mbuf chain argument into two mbuf chains,
934 	 * one that allows us to prepend a length field or token, (nmc_pre)
935 	 * and the second which holds just the results that we're going to
936 	 * checksum and/or encrypt.  When we're done, we join the chains back
937 	 * together.
938 	 */
939 
940 	mb = nd->nd_gss_mb;                             // the mbuf where we split
941 	results = mbuf_next(mb);                        // first mbuf in the results
942 	error = mbuf_setnext(mb, NULL);                 // disconnect the chains
943 	if (error) {
944 		return error;
945 	}
946 	nfs_gss_nfsm_chain(nmc_res, mb);                // set up the prepend chain
947 	nfsm_chain_build_done(error, nmc_res);
948 	if (error) {
949 		return error;
950 	}
951 
952 	if (nd->nd_sec == RPCAUTH_KRB5I) {
953 		error = rpc_gss_integ_data_create(cp->gss_svc_ctx_id, &results, nd->nd_gss_seqnum, &reslen);
954 	} else {
955 		/* RPCAUTH_KRB5P */
956 		error = rpc_gss_priv_data_create(cp->gss_svc_ctx_id, &results, nd->nd_gss_seqnum, &reslen);
957 	}
958 	nfs_gss_append_chain(nmc_res, results); // Append the results mbufs
959 	nfsm_chain_build_done(error, nmc_res);
960 
961 	return error;
962 }
963 
964 /*
965  * This function handles the context setup calls from the client.
966  * Essentially, it implements the NFS null procedure calls when
967  * an RPCSEC_GSS credential is used.
968  * This is the context maintenance function.  It creates and
969  * destroys server contexts at the whim of the client.
970  * During context creation, it receives GSS-API tokens from the
971  * client, passes them up to gssd, and returns a received token
972  * back to the client in the null procedure reply.
973  */
974 int
nfs_gss_svc_ctx_init(struct nfsrv_descript * nd,struct nfsrv_sock * slp,mbuf_t * mrepp)975 nfs_gss_svc_ctx_init(struct nfsrv_descript *nd, struct nfsrv_sock *slp, mbuf_t *mrepp)
976 {
977 	struct nfs_gss_svc_ctx *cp = NULL;
978 	int error = 0;
979 	int autherr = 0;
980 	struct nfsm_chain *nmreq, nmrep;
981 	int sz;
982 
983 	nmreq = &nd->nd_nmreq;
984 	nfsm_chain_null(&nmrep);
985 	*mrepp = NULL;
986 	cp = nd->nd_gss_context;
987 	nd->nd_repstat = 0;
988 
989 	switch (cp->gss_svc_proc) {
990 	case RPCSEC_GSS_INIT:
991 		nfs_gss_svc_ctx_insert(cp);
992 		OS_FALLTHROUGH;
993 
994 	case RPCSEC_GSS_CONTINUE_INIT:
995 		/* Get the token from the request */
996 		nfsm_chain_get_32(error, nmreq, cp->gss_svc_tokenlen);
997 		cp->gss_svc_token = NULL;
998 		if (cp->gss_svc_tokenlen > 0 && cp->gss_svc_tokenlen < GSS_MAX_TOKEN_LEN) {
999 			cp->gss_svc_token = kalloc_data(cp->gss_svc_tokenlen, Z_WAITOK);
1000 		}
1001 		if (cp->gss_svc_token == NULL) {
1002 			autherr = RPCSEC_GSS_CREDPROBLEM;
1003 			break;
1004 		}
1005 		nfsm_chain_get_opaque(error, nmreq, cp->gss_svc_tokenlen, cp->gss_svc_token);
1006 
1007 		/* Use the token in a gss_accept_sec_context upcall */
1008 		error = nfs_gss_svc_gssd_upcall(cp);
1009 		if (error) {
1010 			autherr = RPCSEC_GSS_CREDPROBLEM;
1011 			if (error == NFSERR_EAUTH) {
1012 				error = 0;
1013 			}
1014 			break;
1015 		}
1016 
1017 		/*
1018 		 * If the context isn't complete, pass the new token
1019 		 * back to the client for another round.
1020 		 */
1021 		if (cp->gss_svc_major != GSS_S_COMPLETE) {
1022 			break;
1023 		}
1024 
1025 		/*
1026 		 * Now the server context is complete.
1027 		 * Finish setup.
1028 		 */
1029 		clock_get_uptime(&cp->gss_svc_incarnation);
1030 
1031 		cp->gss_svc_seqwin = GSS_SVC_SEQWINDOW;
1032 		cp->gss_svc_seqbits = kalloc_data(nfs_gss_seqbits_size(cp->gss_svc_seqwin), Z_WAITOK | Z_ZERO);
1033 		if (cp->gss_svc_seqbits == NULL) {
1034 			autherr = RPCSEC_GSS_CREDPROBLEM;
1035 			break;
1036 		}
1037 		break;
1038 
1039 	case RPCSEC_GSS_DATA:
1040 		/* Just a nullproc ping - do nothing */
1041 		break;
1042 
1043 	case RPCSEC_GSS_DESTROY:
1044 		/*
1045 		 * Don't destroy the context immediately because
1046 		 * other active requests might still be using it.
1047 		 * Instead, schedule it for destruction after
1048 		 * GSS_CTX_PEND time has elapsed.
1049 		 */
1050 		cp = nfs_gss_svc_ctx_find(cp->gss_svc_handle);
1051 		if (cp != NULL) {
1052 			cp->gss_svc_handle = 0; // so it can't be found
1053 			lck_mtx_lock(&cp->gss_svc_mtx);
1054 			clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC,
1055 			    &cp->gss_svc_incarnation);
1056 			lck_mtx_unlock(&cp->gss_svc_mtx);
1057 		}
1058 		break;
1059 	default:
1060 		autherr = RPCSEC_GSS_CREDPROBLEM;
1061 		break;
1062 	}
1063 
1064 	/* Now build the reply  */
1065 
1066 	if (nd->nd_repstat == 0) {
1067 		nd->nd_repstat = autherr ? (NFSERR_AUTHERR | autherr) : NFSERR_RETVOID;
1068 	}
1069 	sz = 7 * NFSX_UNSIGNED + nfsm_rndup(cp->gss_svc_tokenlen); // size of results
1070 	error = nfsrv_rephead(nd, slp, &nmrep, sz);
1071 	*mrepp = nmrep.nmc_mhead;
1072 	if (error || autherr) {
1073 		goto nfsmout;
1074 	}
1075 
1076 	if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
1077 	    cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT) {
1078 		nfsm_chain_add_32(error, &nmrep, sizeof(cp->gss_svc_handle));
1079 		nfsm_chain_add_32(error, &nmrep, cp->gss_svc_handle);
1080 
1081 		nfsm_chain_add_32(error, &nmrep, cp->gss_svc_major);
1082 		nfsm_chain_add_32(error, &nmrep, cp->gss_svc_minor);
1083 		nfsm_chain_add_32(error, &nmrep, cp->gss_svc_seqwin);
1084 
1085 		nfsm_chain_add_32(error, &nmrep, cp->gss_svc_tokenlen);
1086 		if (cp->gss_svc_token != NULL) {
1087 			nfsm_chain_add_opaque(error, &nmrep, cp->gss_svc_token, cp->gss_svc_tokenlen);
1088 			kfree_data_addr(cp->gss_svc_token);
1089 		}
1090 	}
1091 
1092 nfsmout:
1093 	if (autherr != 0) {
1094 		nd->nd_gss_context = NULL;
1095 		LIST_REMOVE(cp, gss_svc_entries);
1096 		if (cp->gss_svc_seqbits != NULL) {
1097 			kfree_data(cp->gss_svc_seqbits, nfs_gss_seqbits_size(cp->gss_svc_seqwin));
1098 		}
1099 		if (cp->gss_svc_token != NULL) {
1100 			kfree_data_addr(cp->gss_svc_token);
1101 		}
1102 		lck_mtx_destroy(&cp->gss_svc_mtx, &nfs_gss_svc_grp);
1103 		kfree_type(struct nfs_gss_svc_ctx, cp);
1104 	}
1105 
1106 	nfsm_chain_build_done(error, &nmrep);
1107 	if (error) {
1108 		nfsm_chain_cleanup(&nmrep);
1109 		*mrepp = NULL;
1110 	}
1111 	return error;
1112 }
1113 
1114 /*
1115  * This is almost a mirror-image of the client side upcall.
1116  * It passes and receives a token, but invokes gss_accept_sec_context.
1117  * If it's the final call of the context setup, then gssd also returns
1118  * the session key and the user's UID.
1119  */
1120 static int
nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx * cp)1121 nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx *cp)
1122 {
1123 	kern_return_t kr;
1124 	mach_port_t mp;
1125 	int retry_cnt = 0;
1126 	gssd_byte_buffer octx = NULL;
1127 	uint32_t lucidlen = 0;
1128 	void *lucid_ctx_buffer;
1129 	uint32_t ret_flags;
1130 	vm_map_copy_t itoken = NULL;
1131 	gssd_byte_buffer otoken = NULL;
1132 	mach_msg_type_number_t otokenlen;
1133 	int error = 0;
1134 	char svcname[] = "nfs";
1135 
1136 	kr = host_get_gssd_port(host_priv_self(), &mp);
1137 	if (kr != KERN_SUCCESS) {
1138 		printf("nfs_gss_svc_gssd_upcall: can't get gssd port, status %x (%d)\n", kr, kr);
1139 		goto out;
1140 	}
1141 	if (!IPC_PORT_VALID(mp)) {
1142 		printf("nfs_gss_svc_gssd_upcall: gssd port not valid\n");
1143 		goto out;
1144 	}
1145 
1146 	if (cp->gss_svc_tokenlen > 0) {
1147 		nfs_gss_mach_alloc_buffer(cp->gss_svc_token, cp->gss_svc_tokenlen, &itoken);
1148 	}
1149 
1150 retry:
1151 	printf("Calling mach_gss_accept_sec_context\n");
1152 	kr = mach_gss_accept_sec_context(
1153 		mp,
1154 		(gssd_byte_buffer) itoken, (mach_msg_type_number_t) cp->gss_svc_tokenlen,
1155 		svcname,
1156 		0,
1157 		&cp->gss_svc_context,
1158 		&cp->gss_svc_cred_handle,
1159 		&ret_flags,
1160 		&cp->gss_svc_uid,
1161 		cp->gss_svc_gids,
1162 		&cp->gss_svc_ngroups,
1163 		&octx, (mach_msg_type_number_t *) &lucidlen,
1164 		&otoken, &otokenlen,
1165 		&cp->gss_svc_major,
1166 		&cp->gss_svc_minor);
1167 
1168 	printf("mach_gss_accept_sec_context returned %d\n", kr);
1169 	if (kr != KERN_SUCCESS) {
1170 		printf("nfs_gss_svc_gssd_upcall failed: %x (%d)\n", kr, kr);
1171 		if (kr == MIG_SERVER_DIED && cp->gss_svc_context == 0 &&
1172 		    retry_cnt++ < NFS_GSS_MACH_MAX_RETRIES) {
1173 			if (cp->gss_svc_tokenlen > 0) {
1174 				nfs_gss_mach_alloc_buffer(cp->gss_svc_token, cp->gss_svc_tokenlen, &itoken);
1175 			}
1176 			goto retry;
1177 		}
1178 		host_release_special_port(mp);
1179 		goto out;
1180 	}
1181 
1182 	host_release_special_port(mp);
1183 
1184 	if (lucidlen > 0) {
1185 		if (lucidlen > MAX_LUCIDLEN) {
1186 			printf("nfs_gss_svc_gssd_upcall: bad context length (%d)\n", lucidlen);
1187 			vm_map_copy_discard((vm_map_copy_t) octx);
1188 			vm_map_copy_discard((vm_map_copy_t) otoken);
1189 			goto out;
1190 		}
1191 		lucid_ctx_buffer = kalloc_data(lucidlen, Z_WAITOK | Z_ZERO);
1192 		error = nfs_gss_mach_vmcopyout((vm_map_copy_t) octx, lucidlen, lucid_ctx_buffer);
1193 		if (error) {
1194 			vm_map_copy_discard((vm_map_copy_t) otoken);
1195 			kfree_data(lucid_ctx_buffer, lucidlen);
1196 			goto out;
1197 		}
1198 		if (cp->gss_svc_ctx_id) {
1199 			gss_krb5_destroy_context(cp->gss_svc_ctx_id);
1200 		}
1201 		cp->gss_svc_ctx_id = gss_krb5_make_context(lucid_ctx_buffer, lucidlen);
1202 		kfree_data(lucid_ctx_buffer, lucidlen);
1203 		if (cp->gss_svc_ctx_id == NULL) {
1204 			printf("Failed to make context from lucid_ctx_buffer\n");
1205 			goto out;
1206 		}
1207 	}
1208 
1209 	/* Free context token used as input */
1210 	if (cp->gss_svc_token) {
1211 		kfree_data(cp->gss_svc_token, cp->gss_svc_tokenlen);
1212 	}
1213 	cp->gss_svc_token = NULL;
1214 	cp->gss_svc_tokenlen = 0;
1215 
1216 	if (otokenlen > 0) {
1217 		/* Set context token to gss output token */
1218 		cp->gss_svc_token = kalloc_data(otokenlen, Z_WAITOK);
1219 		if (cp->gss_svc_token == NULL) {
1220 			printf("nfs_gss_svc_gssd_upcall: could not allocate %d bytes\n", otokenlen);
1221 			vm_map_copy_discard((vm_map_copy_t) otoken);
1222 			return ENOMEM;
1223 		}
1224 		error = nfs_gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_svc_token);
1225 		if (error) {
1226 			kfree_data(cp->gss_svc_token, otokenlen);
1227 			return NFSERR_EAUTH;
1228 		}
1229 		cp->gss_svc_tokenlen = otokenlen;
1230 	}
1231 
1232 	return 0;
1233 
1234 out:
1235 	kfree_data(cp->gss_svc_token, cp->gss_svc_tokenlen);
1236 	cp->gss_svc_tokenlen = 0;
1237 
1238 	return NFSERR_EAUTH;
1239 }
1240 
1241 /*
1242  * Validate the sequence number in the credential as described
1243  * in RFC 2203 Section 5.3.3.1
1244  *
1245  * Here the window of valid sequence numbers is represented by
1246  * a bitmap.  As each sequence number is received, its bit is
1247  * set in the bitmap.  An invalid sequence number lies below
1248  * the lower bound of the window, or is within the window but
1249  * has its bit already set.
1250  */
1251 static int
nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx * cp,uint32_t seq)1252 nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *cp, uint32_t seq)
1253 {
1254 	uint32_t *bits = cp->gss_svc_seqbits;
1255 	uint32_t win = cp->gss_svc_seqwin;
1256 	uint32_t i;
1257 
1258 	lck_mtx_lock(&cp->gss_svc_mtx);
1259 
1260 	/*
1261 	 * If greater than the window upper bound,
1262 	 * move the window up, and set the bit.
1263 	 */
1264 	if (seq > cp->gss_svc_seqmax) {
1265 		if (seq - cp->gss_svc_seqmax > win) {
1266 			bzero(bits, nfs_gss_seqbits_size(win));
1267 		} else {
1268 			for (i = cp->gss_svc_seqmax + 1; i < seq; i++) {
1269 				win_resetbit(bits, i % win);
1270 			}
1271 		}
1272 		win_setbit(bits, seq % win);
1273 		cp->gss_svc_seqmax = seq;
1274 		lck_mtx_unlock(&cp->gss_svc_mtx);
1275 		return 1;
1276 	}
1277 
1278 	/*
1279 	 * Invalid if below the lower bound of the window
1280 	 */
1281 	if (seq <= cp->gss_svc_seqmax - win) {
1282 		lck_mtx_unlock(&cp->gss_svc_mtx);
1283 		return 0;
1284 	}
1285 
1286 	/*
1287 	 * In the window, invalid if the bit is already set
1288 	 */
1289 	if (win_getbit(bits, seq % win)) {
1290 		lck_mtx_unlock(&cp->gss_svc_mtx);
1291 		return 0;
1292 	}
1293 	win_setbit(bits, seq % win);
1294 	lck_mtx_unlock(&cp->gss_svc_mtx);
1295 	return 1;
1296 }
1297 
1298 /*
1299  * Drop a reference to a context
1300  *
1301  * Note that it's OK for the context to exist
1302  * with a refcount of zero.  The refcount isn't
1303  * checked until we're about to reap an expired one.
1304  */
1305 void
nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx * cp)1306 nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx *cp)
1307 {
1308 	lck_mtx_lock(&cp->gss_svc_mtx);
1309 	if (cp->gss_svc_refcnt > 0) {
1310 		cp->gss_svc_refcnt--;
1311 	} else {
1312 		printf("nfs_gss_ctx_deref: zero refcount\n");
1313 	}
1314 	lck_mtx_unlock(&cp->gss_svc_mtx);
1315 }
1316 
1317 /*
1318  * Called at NFS server shutdown - destroy all contexts
1319  */
1320 void
nfs_gss_svc_cleanup(void)1321 nfs_gss_svc_cleanup(void)
1322 {
1323 	struct nfs_gss_svc_ctx_hashhead *head;
1324 	struct nfs_gss_svc_ctx *cp, *ncp;
1325 	int i;
1326 
1327 	lck_mtx_lock(&nfs_gss_svc_ctx_mutex);
1328 
1329 	/*
1330 	 * Run through all the buckets
1331 	 */
1332 	for (i = 0; i < SVC_CTX_HASHSZ; i++) {
1333 		/*
1334 		 * Remove and free all entries in the bucket
1335 		 */
1336 		head = &nfs_gss_svc_ctx_hashtbl[i];
1337 		LIST_FOREACH_SAFE(cp, head, gss_svc_entries, ncp) {
1338 			LIST_REMOVE(cp, gss_svc_entries);
1339 			if (cp->gss_svc_seqbits) {
1340 				kfree_data(cp->gss_svc_seqbits, nfs_gss_seqbits_size(cp->gss_svc_seqwin));
1341 			}
1342 			lck_mtx_destroy(&cp->gss_svc_mtx, &nfs_gss_svc_grp);
1343 			kfree_type(struct nfs_gss_svc_ctx, cp);
1344 		}
1345 	}
1346 
1347 	lck_mtx_unlock(&nfs_gss_svc_ctx_mutex);
1348 }
1349 
1350 /*************
1351  * The following functions are used by both client and server.
1352  */
1353 
1354 /*
1355  * Release a host special port that was obtained by host_get_special_port
1356  * or one of its macros (host_get_gssd_port in this case).
1357  * This really should be in a public kpi.
1358  */
1359 
1360 /* This should be in a public header if this routine is not */
1361 static void
host_release_special_port(mach_port_t mp)1362 host_release_special_port(mach_port_t mp)
1363 {
1364 	if (IPC_PORT_VALID(mp)) {
1365 		ipc_port_release_send(mp);
1366 	}
1367 }
1368 
1369 /*
1370  * The token that is sent and received in the gssd upcall
1371  * has unbounded variable length.  Mach RPC does not pass
1372  * the token in-line.  Instead it uses page mapping to handle
1373  * these parameters.  This function allocates a VM buffer
1374  * to hold the token for an upcall and copies the token
1375  * (received from the client) into it.  The VM buffer is
1376  * marked with a src_destroy flag so that the upcall will
1377  * automatically de-allocate the buffer when the upcall is
1378  * complete.
1379  */
1380 static void
nfs_gss_mach_alloc_buffer(u_char * buf,size_t buflen,vm_map_copy_t * addr)1381 nfs_gss_mach_alloc_buffer(u_char *buf, size_t buflen, vm_map_copy_t *addr)
1382 {
1383 	kern_return_t kr;
1384 	vm_offset_t kmem_buf;
1385 	vm_size_t tbuflen;
1386 
1387 	*addr = NULL;
1388 	if (buf == NULL || buflen == 0) {
1389 		return;
1390 	}
1391 
1392 	tbuflen = vm_map_round_page(buflen, vm_map_page_mask(ipc_kernel_map));
1393 
1394 	if (tbuflen < buflen) {
1395 		printf("nfs_gss_mach_alloc_buffer: vm_map_round_page failed\n");
1396 		return;
1397 	}
1398 
1399 	kr = kmem_alloc(ipc_kernel_map, &kmem_buf, tbuflen,
1400 	    KMA_DATA, VM_KERN_MEMORY_FILE);
1401 	if (kr != 0) {
1402 		printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n");
1403 		return;
1404 	}
1405 
1406 	bcopy(buf, (char *)kmem_buf, buflen);
1407 	bzero((char *)kmem_buf + buflen, tbuflen - buflen);
1408 
1409 	kr = vm_map_unwire(ipc_kernel_map, kmem_buf, kmem_buf + tbuflen, FALSE);
1410 	if (kr != 0) {
1411 		printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n");
1412 		return;
1413 	}
1414 
1415 	kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t) kmem_buf,
1416 	    (vm_map_size_t) buflen, TRUE, addr);
1417 	if (kr != 0) {
1418 		printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n");
1419 		return;
1420 	}
1421 }
1422 
1423 /*
1424  * Here we handle a token received from the gssd via an upcall.
1425  * The received token resides in an allocate VM buffer.
1426  * We copy the token out of this buffer to a chunk of malloc'ed
1427  * memory of the right size, then de-allocate the VM buffer.
1428  */
1429 static int
nfs_gss_mach_vmcopyout(vm_map_copy_t in,uint32_t len,u_char * out)1430 nfs_gss_mach_vmcopyout(vm_map_copy_t in, uint32_t len, u_char *out)
1431 {
1432 	vm_map_offset_t map_data;
1433 	vm_offset_t data;
1434 	int error;
1435 
1436 	error = vm_map_copyout(ipc_kernel_map, &map_data, in);
1437 	if (error) {
1438 		return error;
1439 	}
1440 
1441 	data = CAST_DOWN(vm_offset_t, map_data);
1442 	bcopy((void *) data, out, len);
1443 	vm_deallocate(ipc_kernel_map, data, len);
1444 
1445 	return 0;
1446 }
1447 
1448 /*
1449  * Return the number of bytes in an mbuf chain.
1450  */
1451 static int
nfs_gss_mchain_length(mbuf_t mhead)1452 nfs_gss_mchain_length(mbuf_t mhead)
1453 {
1454 	mbuf_t mb;
1455 	int len = 0;
1456 
1457 	for (mb = mhead; mb; mb = mbuf_next(mb)) {
1458 		len += mbuf_len(mb);
1459 	}
1460 
1461 	return len;
1462 }
1463 
1464 /*
1465  * Return the size for the sequence numbers bitmap.
1466  */
1467 static int
nfs_gss_seqbits_size(uint32_t win)1468 nfs_gss_seqbits_size(uint32_t win)
1469 {
1470 	return nfsm_rndup((win + 7) / 8);
1471 }
1472 
1473 /*
1474  * Append an args or results mbuf chain to the header chain
1475  */
1476 static int
nfs_gss_append_chain(struct nfsm_chain * nmc,mbuf_t mc)1477 nfs_gss_append_chain(struct nfsm_chain *nmc, mbuf_t mc)
1478 {
1479 	int error = 0;
1480 	mbuf_t mb, tail;
1481 
1482 	/* Connect the mbuf chains */
1483 	error = mbuf_setnext(nmc->nmc_mcur, mc);
1484 	if (error) {
1485 		return error;
1486 	}
1487 
1488 	/* Find the last mbuf in the chain */
1489 	tail = NULL;
1490 	for (mb = mc; mb; mb = mbuf_next(mb)) {
1491 		tail = mb;
1492 	}
1493 
1494 	nmc->nmc_mcur = tail;
1495 	nmc->nmc_ptr = (caddr_t) mbuf_data(tail) + mbuf_len(tail);
1496 	nmc->nmc_left = mbuf_trailingspace(tail);
1497 
1498 	return 0;
1499 }
1500 
1501 /*
1502  * Convert an mbuf chain to an NFS mbuf chain
1503  */
1504 static void
nfs_gss_nfsm_chain(struct nfsm_chain * nmc,mbuf_t mc)1505 nfs_gss_nfsm_chain(struct nfsm_chain *nmc, mbuf_t mc)
1506 {
1507 	mbuf_t mb, tail;
1508 
1509 	/* Find the last mbuf in the chain */
1510 	tail = NULL;
1511 	for (mb = mc; mb; mb = mbuf_next(mb)) {
1512 		tail = mb;
1513 	}
1514 
1515 	nmc->nmc_mhead = mc;
1516 	nmc->nmc_mcur = tail;
1517 	nmc->nmc_ptr = (caddr_t) mbuf_data(tail) + mbuf_len(tail);
1518 	nmc->nmc_left = mbuf_trailingspace(tail);
1519 	nmc->nmc_flags = 0;
1520 }
1521 
1522 #if 0
1523 #define DISPLAYLEN 16
1524 #define MAXDISPLAYLEN 256
1525 
1526 static void
1527 hexdump(const char *msg, void *data, size_t len)
1528 {
1529 	size_t i, j;
1530 	u_char *d = data;
1531 	char *p, disbuf[3 * DISPLAYLEN + 1];
1532 
1533 	printf("NFS DEBUG %s len=%d:\n", msg, (uint32_t)len);
1534 	if (len > MAXDISPLAYLEN) {
1535 		len = MAXDISPLAYLEN;
1536 	}
1537 
1538 	for (i = 0; i < len; i += DISPLAYLEN) {
1539 		for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3) {
1540 			snprintf(p, 4, "%02x ", d[i + j]);
1541 		}
1542 		printf("\t%s\n", disbuf);
1543 	}
1544 }
1545 #endif
1546 
1547 #endif /* CONFIG_NFS_SERVER */
1548