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
103u_long nfs_gss_svc_ctx_hash;
104struct nfs_gss_svc_ctx_hashhead *nfs_gss_svc_ctx_hashtbl;
105static LCK_GRP_DECLARE(nfs_gss_svc_grp, "rpcsec_gss_svc");
106static LCK_MTX_DECLARE(nfs_gss_svc_ctx_mutex, &nfs_gss_svc_grp);
107uint32_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
111static uint8_t xdrpad[] = { 0x00, 0x00, 0x00, 0x00};
112
113static struct nfs_gss_svc_ctx *nfs_gss_svc_ctx_find(uint32_t);
114static void nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx *);
115static void nfs_gss_svc_ctx_timer(void *, void *);
116static int nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx *);
117static int nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *, uint32_t);
118
119/* This is only used by server code */
120static void nfs_gss_nfsm_chain(struct nfsm_chain *, mbuf_t);
121
122static void host_release_special_port(mach_port_t);
123static void nfs_gss_mach_alloc_buffer(u_char *, size_t, vm_map_copy_t *);
124static int nfs_gss_mach_vmcopyout(vm_map_copy_t, uint32_t, u_char *);
125
126static int nfs_gss_mchain_length(mbuf_t);
127static int nfs_gss_append_chain(struct nfsm_chain *, mbuf_t);
128static int nfs_gss_seqbits_size(uint32_t);
129
130thread_call_t nfs_gss_svc_ctx_timer_call;
131int nfs_gss_timer_on = 0;
132uint32_t nfs_gss_ctx_count = 0;
133const uint32_t nfs_gss_ctx_max = GSS_SVC_MAXCONTEXTS;
134
135/*
136 * Common RPCSEC_GSS support routines
137 */
138
139static errno_t
140rpc_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(mbuf: mb, len: sizeof(uint32_t), how: MBUF_WAITOK);
164 if (error) {
165 return error;
166 }
167
168 data = mbuf_data(mbuf: *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
182static errno_t
183rpc_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(how: MBUF_WAITOK, type: MBUF_TYPE_DATA, mbuf: &mb);
192 if (error) {
193 return error;
194 }
195 data = mbuf_data(mbuf: 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(mbuf: mb, data: data + 16 * sizeof(uint32_t), len: 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(mbuf: nmcp->nmc_mcur, next: *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
228static errno_t
229rpc_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(mbp_head: 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: mb_head, value: 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 */
283static errno_t
284rpc_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(mbp_head: 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: mb_head, value: 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 */
335void
336nfs_gss_svc_init(void)
337{
338 nfs_gss_svc_ctx_hashtbl = hashinit(SVC_CTX_HASHSZ, M_TEMP, hashmask: &nfs_gss_svc_ctx_hash);
339
340 nfs_gss_svc_ctx_timer_call = thread_call_allocate(func: 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 */
347static struct nfs_gss_svc_ctx *
348nfs_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, result: &timenow);
363
364 lck_mtx_lock(lck: &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(lck: &cp->gss_svc_mtx);
385 cp->gss_svc_refcnt++;
386 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
387 break;
388 }
389 }
390
391 lck_mtx_unlock(lck: &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 */
400static void
401nfs_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(lck: &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 */
413retry:
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 result: &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, b: max(GSS_CTX_TTL_MIN, b: nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
434 }
435
436 lck_mtx_unlock(lck: &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 */
444void
445nfs_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(lck: &nfs_gss_svc_ctx_mutex);
453 clock_get_uptime(result: &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(lck: &cp->gss_svc_mtx, grp: &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, b: max(GSS_CTX_TTL_MIN, b: nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
495 }
496
497 lck_mtx_unlock(lck: &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 */
510int
511nfs_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(lck: &cp->gss_svc_mtx, grp: &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(s: &temp_pcred, n: sizeof(temp_pcred));
660 temp_pcred.cr_uid = cp->gss_svc_uid;
661 bcopy(src: cp->gss_svc_gids, dst: temp_pcred.cr_groups,
662 n: 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(pcred: &temp_pcred);
666 if (nd->nd_cr == NULL) {
667 error = ENOMEM;
668 goto nfsmout;
669 }
670 clock_get_uptime(result: &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(mbuf: 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(mbuf: 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(mbuf: prev_mbuf, next: reply_mbuf);
771 nmc->nmc_mcur = reply_mbuf;
772 nmc->nmc_ptr = mbuf_data(mbuf: reply_mbuf);
773 nmc->nmc_left = mbuf_len(mbuf: 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(lck: &cp->gss_svc_mtx, grp: &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;
821nfsmout:
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 */
834int
835nfs_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 */
894int
895nfs_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 */
922int
923nfs_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(mbuf: mb); // first mbuf in the results
942 error = mbuf_setnext(mbuf: 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(ctx: cp->gss_svc_ctx_id, mb_head: &results, seqnum: nd->nd_gss_seqnum, len: &reslen);
954 } else {
955 /* RPCAUTH_KRB5P */
956 error = rpc_gss_priv_data_create(ctx: cp->gss_svc_ctx_id, mb_head: &results, seqnum: nd->nd_gss_seqnum, len: &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 */
974int
975nfs_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(result: &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(handle: cp->gss_svc_handle);
1051 if (cp != NULL) {
1052 cp->gss_svc_handle = 0; // so it can't be found
1053 lck_mtx_lock(lck: &cp->gss_svc_mtx);
1054 clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC,
1055 result: &cp->gss_svc_incarnation);
1056 lck_mtx_unlock(lck: &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
1092nfsmout:
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(lck: &cp->gss_svc_mtx, grp: &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 */
1120static int
1121nfs_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
1150retry:
1151 printf("Calling mach_gss_accept_sec_context\n");
1152 kr = mach_gss_accept_sec_context(
1153 server: mp,
1154 intoken: (gssd_byte_buffer) itoken, intokenCnt: (mach_msg_type_number_t) cp->gss_svc_tokenlen,
1155 svc_namestr: svcname,
1156 gssd_flags: 0,
1157 context: &cp->gss_svc_context,
1158 cred_handle: &cp->gss_svc_cred_handle,
1159 flags: &ret_flags,
1160 uid: &cp->gss_svc_uid,
1161 gids: cp->gss_svc_gids,
1162 gidsCnt: &cp->gss_svc_ngroups,
1163 key: &octx, keyCnt: (mach_msg_type_number_t *) &lucidlen,
1164 outtoken: &otoken, outtokenCnt: &otokenlen,
1165 major_stat: &cp->gss_svc_major,
1166 minor_stat: &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(copy: (vm_map_copy_t) octx);
1188 vm_map_copy_discard(copy: (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(copy: (vm_map_copy_t) octx);
1195 vm_map_copy_discard(copy: (vm_map_copy_t) otoken);
1196 kfree_data(lucid_ctx_buffer, lucidlen);
1197 goto out;
1198 }
1199 if (cp->gss_svc_ctx_id) {
1200 gss_krb5_destroy_context(cp->gss_svc_ctx_id);
1201 }
1202 cp->gss_svc_ctx_id = gss_krb5_make_context(lucid_ctx_buffer, lucidlen);
1203 kfree_data(lucid_ctx_buffer, lucidlen);
1204 if (cp->gss_svc_ctx_id == NULL) {
1205 printf("Failed to make context from lucid_ctx_buffer\n");
1206 goto out;
1207 }
1208 }
1209
1210 /* Free context token used as input */
1211 if (cp->gss_svc_token) {
1212 kfree_data(cp->gss_svc_token, cp->gss_svc_tokenlen);
1213 }
1214 cp->gss_svc_token = NULL;
1215 cp->gss_svc_tokenlen = 0;
1216
1217 if (otokenlen > 0) {
1218 /* Set context token to gss output token */
1219 cp->gss_svc_token = kalloc_data(otokenlen, Z_WAITOK);
1220 if (cp->gss_svc_token == NULL) {
1221 printf("nfs_gss_svc_gssd_upcall: could not allocate %d bytes\n", otokenlen);
1222 vm_map_copy_discard(copy: (vm_map_copy_t) otoken);
1223 return ENOMEM;
1224 }
1225 error = nfs_gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_svc_token);
1226 if (error) {
1227 vm_map_copy_discard(copy: (vm_map_copy_t) otoken);
1228 kfree_data(cp->gss_svc_token, otokenlen);
1229 return NFSERR_EAUTH;
1230 }
1231 cp->gss_svc_tokenlen = otokenlen;
1232 }
1233
1234 return 0;
1235
1236out:
1237 kfree_data(cp->gss_svc_token, cp->gss_svc_tokenlen);
1238 cp->gss_svc_tokenlen = 0;
1239
1240 return NFSERR_EAUTH;
1241}
1242
1243/*
1244 * Validate the sequence number in the credential as described
1245 * in RFC 2203 Section 5.3.3.1
1246 *
1247 * Here the window of valid sequence numbers is represented by
1248 * a bitmap. As each sequence number is received, its bit is
1249 * set in the bitmap. An invalid sequence number lies below
1250 * the lower bound of the window, or is within the window but
1251 * has its bit already set.
1252 */
1253static int
1254nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *cp, uint32_t seq)
1255{
1256 uint32_t *bits = cp->gss_svc_seqbits;
1257 uint32_t win = cp->gss_svc_seqwin;
1258 uint32_t i;
1259
1260 lck_mtx_lock(lck: &cp->gss_svc_mtx);
1261
1262 /*
1263 * If greater than the window upper bound,
1264 * move the window up, and set the bit.
1265 */
1266 if (seq > cp->gss_svc_seqmax) {
1267 if (seq - cp->gss_svc_seqmax > win) {
1268 bzero(s: bits, n: nfs_gss_seqbits_size(win));
1269 } else {
1270 for (i = cp->gss_svc_seqmax + 1; i < seq; i++) {
1271 win_resetbit(bits, i % win);
1272 }
1273 }
1274 win_setbit(bits, seq % win);
1275 cp->gss_svc_seqmax = seq;
1276 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
1277 return 1;
1278 }
1279
1280 /*
1281 * Invalid if below the lower bound of the window
1282 */
1283 if (seq <= cp->gss_svc_seqmax - win) {
1284 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
1285 return 0;
1286 }
1287
1288 /*
1289 * In the window, invalid if the bit is already set
1290 */
1291 if (win_getbit(bits, seq % win)) {
1292 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
1293 return 0;
1294 }
1295 win_setbit(bits, seq % win);
1296 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
1297 return 1;
1298}
1299
1300/*
1301 * Drop a reference to a context
1302 *
1303 * Note that it's OK for the context to exist
1304 * with a refcount of zero. The refcount isn't
1305 * checked until we're about to reap an expired one.
1306 */
1307void
1308nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx *cp)
1309{
1310 lck_mtx_lock(lck: &cp->gss_svc_mtx);
1311 if (cp->gss_svc_refcnt > 0) {
1312 cp->gss_svc_refcnt--;
1313 } else {
1314 printf("nfs_gss_ctx_deref: zero refcount\n");
1315 }
1316 lck_mtx_unlock(lck: &cp->gss_svc_mtx);
1317}
1318
1319/*
1320 * Called at NFS server shutdown - destroy all contexts
1321 */
1322void
1323nfs_gss_svc_cleanup(void)
1324{
1325 struct nfs_gss_svc_ctx_hashhead *head;
1326 struct nfs_gss_svc_ctx *cp, *ncp;
1327 int i;
1328
1329 lck_mtx_lock(lck: &nfs_gss_svc_ctx_mutex);
1330
1331 /*
1332 * Run through all the buckets
1333 */
1334 for (i = 0; i < SVC_CTX_HASHSZ; i++) {
1335 /*
1336 * Remove and free all entries in the bucket
1337 */
1338 head = &nfs_gss_svc_ctx_hashtbl[i];
1339 LIST_FOREACH_SAFE(cp, head, gss_svc_entries, ncp) {
1340 LIST_REMOVE(cp, gss_svc_entries);
1341 if (cp->gss_svc_seqbits) {
1342 kfree_data(cp->gss_svc_seqbits, nfs_gss_seqbits_size(cp->gss_svc_seqwin));
1343 }
1344 lck_mtx_destroy(lck: &cp->gss_svc_mtx, grp: &nfs_gss_svc_grp);
1345 kfree_type(struct nfs_gss_svc_ctx, cp);
1346 }
1347 }
1348
1349 lck_mtx_unlock(lck: &nfs_gss_svc_ctx_mutex);
1350}
1351
1352/*************
1353 * The following functions are used by both client and server.
1354 */
1355
1356/*
1357 * Release a host special port that was obtained by host_get_special_port
1358 * or one of its macros (host_get_gssd_port in this case).
1359 * This really should be in a public kpi.
1360 */
1361
1362/* This should be in a public header if this routine is not */
1363static void
1364host_release_special_port(mach_port_t mp)
1365{
1366 if (IPC_PORT_VALID(mp)) {
1367 ipc_port_release_send(port: mp);
1368 }
1369}
1370
1371/*
1372 * The token that is sent and received in the gssd upcall
1373 * has unbounded variable length. Mach RPC does not pass
1374 * the token in-line. Instead it uses page mapping to handle
1375 * these parameters. This function allocates a VM buffer
1376 * to hold the token for an upcall and copies the token
1377 * (received from the client) into it. The VM buffer is
1378 * marked with a src_destroy flag so that the upcall will
1379 * automatically de-allocate the buffer when the upcall is
1380 * complete.
1381 */
1382static void
1383nfs_gss_mach_alloc_buffer(u_char *buf, size_t buflen, vm_map_copy_t *addr)
1384{
1385 kern_return_t kr;
1386 vm_offset_t kmem_buf;
1387 vm_size_t tbuflen;
1388
1389 *addr = NULL;
1390 if (buf == NULL || buflen == 0) {
1391 return;
1392 }
1393
1394 tbuflen = vm_map_round_page(buflen, vm_map_page_mask(ipc_kernel_map));
1395
1396 if (tbuflen < buflen) {
1397 printf("nfs_gss_mach_alloc_buffer: vm_map_round_page failed\n");
1398 return;
1399 }
1400
1401 kr = kmem_alloc(map: ipc_kernel_map, addrp: &kmem_buf, size: tbuflen,
1402 flags: KMA_DATA, VM_KERN_MEMORY_FILE);
1403 if (kr != 0) {
1404 printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n");
1405 return;
1406 }
1407
1408 bcopy(src: buf, dst: (char *)kmem_buf, n: buflen);
1409 bzero(s: (char *)kmem_buf + buflen, n: tbuflen - buflen);
1410
1411 kr = vm_map_unwire(map: ipc_kernel_map, start: kmem_buf, end: kmem_buf + tbuflen, FALSE);
1412 if (kr != 0) {
1413 printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n");
1414 return;
1415 }
1416
1417 kr = vm_map_copyin(src_map: ipc_kernel_map, src_addr: (vm_map_address_t) kmem_buf,
1418 len: (vm_map_size_t) buflen, TRUE, copy_result: addr);
1419 if (kr != 0) {
1420 printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n");
1421 return;
1422 }
1423}
1424
1425/*
1426 * Here we handle a token received from the gssd via an upcall.
1427 * The received token resides in an allocate VM buffer.
1428 * We copy the token out of this buffer to a chunk of malloc'ed
1429 * memory of the right size, then de-allocate the VM buffer.
1430 */
1431static int
1432nfs_gss_mach_vmcopyout(vm_map_copy_t in, uint32_t len, u_char *out)
1433{
1434 vm_map_offset_t map_data;
1435 vm_offset_t data;
1436 int error;
1437
1438 error = vm_map_copyout(dst_map: ipc_kernel_map, dst_addr: &map_data, copy: in);
1439 if (error) {
1440 return error;
1441 }
1442
1443 data = CAST_DOWN(vm_offset_t, map_data);
1444 bcopy(src: (void *) data, dst: out, n: len);
1445 vm_deallocate(target_task: ipc_kernel_map, address: data, size: len);
1446
1447 return 0;
1448}
1449
1450/*
1451 * Return the number of bytes in an mbuf chain.
1452 */
1453static int
1454nfs_gss_mchain_length(mbuf_t mhead)
1455{
1456 mbuf_t mb;
1457 int len = 0;
1458
1459 for (mb = mhead; mb; mb = mbuf_next(mbuf: mb)) {
1460 len += mbuf_len(mbuf: mb);
1461 }
1462
1463 return len;
1464}
1465
1466/*
1467 * Return the size for the sequence numbers bitmap.
1468 */
1469static int
1470nfs_gss_seqbits_size(uint32_t win)
1471{
1472 return nfsm_rndup((win + 7) / 8);
1473}
1474
1475/*
1476 * Append an args or results mbuf chain to the header chain
1477 */
1478static int
1479nfs_gss_append_chain(struct nfsm_chain *nmc, mbuf_t mc)
1480{
1481 int error = 0;
1482 mbuf_t mb, tail;
1483
1484 /* Connect the mbuf chains */
1485 error = mbuf_setnext(mbuf: nmc->nmc_mcur, next: mc);
1486 if (error) {
1487 return error;
1488 }
1489
1490 /* Find the last mbuf in the chain */
1491 tail = NULL;
1492 for (mb = mc; mb; mb = mbuf_next(mbuf: mb)) {
1493 tail = mb;
1494 }
1495
1496 nmc->nmc_mcur = tail;
1497 nmc->nmc_ptr = (caddr_t) mbuf_data(mbuf: tail) + mbuf_len(mbuf: tail);
1498 nmc->nmc_left = mbuf_trailingspace(mbuf: tail);
1499
1500 return 0;
1501}
1502
1503/*
1504 * Convert an mbuf chain to an NFS mbuf chain
1505 */
1506static void
1507nfs_gss_nfsm_chain(struct nfsm_chain *nmc, mbuf_t mc)
1508{
1509 mbuf_t mb, tail;
1510
1511 /* Find the last mbuf in the chain */
1512 tail = NULL;
1513 for (mb = mc; mb; mb = mbuf_next(mbuf: mb)) {
1514 tail = mb;
1515 }
1516
1517 nmc->nmc_mhead = mc;
1518 nmc->nmc_mcur = tail;
1519 nmc->nmc_ptr = (caddr_t) mbuf_data(mbuf: tail) + mbuf_len(mbuf: tail);
1520 nmc->nmc_left = mbuf_trailingspace(mbuf: tail);
1521 nmc->nmc_flags = 0;
1522}
1523
1524#if 0
1525#define DISPLAYLEN 16
1526#define MAXDISPLAYLEN 256
1527
1528static void
1529hexdump(const char *msg, void *data, size_t len)
1530{
1531 size_t i, j;
1532 u_char *d = data;
1533 char *p, disbuf[3 * DISPLAYLEN + 1];
1534
1535 printf("NFS DEBUG %s len=%d:\n", msg, (uint32_t)len);
1536 if (len > MAXDISPLAYLEN) {
1537 len = MAXDISPLAYLEN;
1538 }
1539
1540 for (i = 0; i < len; i += DISPLAYLEN) {
1541 for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3) {
1542 snprintf(p, 4, "%02x ", d[i + j]);
1543 }
1544 printf("\t%s\n", disbuf);
1545 }
1546}
1547#endif
1548
1549#endif /* CONFIG_NFS_SERVER */
1550