]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_gss.c
xnu-3789.41.3.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_gss.c
CommitLineData
2d21ac55 1/*
3e170ce0 2 * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
2d21ac55
A
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/*************
30 * These functions implement RPCSEC_GSS security for the NFS client and server.
31 * The code is specific to the use of Kerberos v5 and the use of DES MAC MD5
32 * protection as described in Internet RFC 2203 and 2623.
33 *
34 * In contrast to the original AUTH_SYS authentication, RPCSEC_GSS is stateful.
35 * It requires the client and server negotiate a secure connection as part of a
36 * security context. The context state is maintained in client and server structures.
37 * On the client side, each user of an NFS mount is assigned their own context,
38 * identified by UID, on their first use of the mount, and it persists until the
39 * unmount or until the context is renewed. Each user context has a corresponding
40 * server context which the server maintains until the client destroys it, or
41 * until the context expires.
42 *
43 * The client and server contexts are set up dynamically. When a user attempts
44 * to send an NFS request, if there is no context for the user, then one is
45 * set up via an exchange of NFS null procedure calls as described in RFC 2203.
46 * During this exchange, the client and server pass a security token that is
47 * forwarded via Mach upcall to the gssd, which invokes the GSS-API to authenticate
48 * the user to the server (and vice-versa). The client and server also receive
49 * a unique session key that can be used to digitally sign the credentials and
50 * verifier or optionally to provide data integrity and/or privacy.
51 *
52 * Once the context is complete, the client and server enter a normal data
53 * exchange phase - beginning with the NFS request that prompted the context
54 * creation. During this phase, the client's RPC header contains an RPCSEC_GSS
55 * credential and verifier, and the server returns a verifier as well.
56 * For simple authentication, the verifier contains a signed checksum of the
57 * RPC header, including the credential. The server's verifier has a signed
58 * checksum of the current sequence number.
59 *
60 * Each client call contains a sequence number that nominally increases by one
61 * on each request. The sequence number is intended to prevent replay attacks.
62 * Since the protocol can be used over UDP, there is some allowance for
63 * out-of-sequence requests, so the server checks whether the sequence numbers
64 * are within a sequence "window". If a sequence number is outside the lower
65 * bound of the window, the server silently drops the request. This has some
66 * implications for retransmission. If a request needs to be retransmitted, the
67 * client must bump the sequence number even if the request XID is unchanged.
68 *
69 * When the NFS mount is unmounted, the client sends a "destroy" credential
70 * to delete the server's context for each user of the mount. Since it's
71 * possible for the client to crash or disconnect without sending the destroy
72 * message, the server has a thread that reaps contexts that have been idle
73 * too long.
74 */
75
76#include <stdint.h>
77#include <sys/param.h>
78#include <sys/systm.h>
79#include <sys/proc.h>
80#include <sys/kauth.h>
81#include <sys/kernel.h>
82#include <sys/mount_internal.h>
83#include <sys/vnode.h>
84#include <sys/ubc.h>
85#include <sys/malloc.h>
86#include <sys/kpi_mbuf.h>
39236c6e 87#include <sys/ucred.h>
2d21ac55
A
88
89#include <kern/host.h>
fe8ab488 90#include <kern/task.h>
2d21ac55
A
91#include <libkern/libkern.h>
92
93#include <mach/task.h>
316670eb 94#include <mach/host_special_ports.h>
2d21ac55
A
95#include <mach/host_priv.h>
96#include <mach/thread_act.h>
97#include <mach/mig_errors.h>
98#include <mach/vm_map.h>
99#include <vm/vm_map.h>
100#include <vm/vm_kern.h>
101#include <gssd/gssd_mach.h>
102
103#include <nfs/rpcv2.h>
104#include <nfs/nfsproto.h>
105#include <nfs/nfs.h>
106#include <nfs/nfsnode.h>
107#include <nfs/nfs_gss.h>
108#include <nfs/nfsmount.h>
109#include <nfs/xdr_subs.h>
110#include <nfs/nfsm_subs.h>
111#include <nfs/nfs_gss.h>
3e170ce0
A
112#include <mach_assert.h>
113#include <kern/assert.h>
114
115#define ASSERT(EX) assert(EX)
b0d623f7 116
2d21ac55
A
117#define NFS_GSS_MACH_MAX_RETRIES 3
118
39236c6e
A
119#define NFS_GSS_DBG(...) NFS_DBG(NFS_FAC_GSS, 7, ## __VA_ARGS__)
120#define NFS_GSS_ISDBG (NFS_DEBUG_FACILITY & NFS_FAC_GSS)
121
b0d623f7 122
2d21ac55
A
123#if NFSSERVER
124u_long nfs_gss_svc_ctx_hash;
125struct nfs_gss_svc_ctx_hashhead *nfs_gss_svc_ctx_hashtbl;
126lck_mtx_t *nfs_gss_svc_ctx_mutex;
127lck_grp_t *nfs_gss_svc_grp;
b0d623f7
A
128uint32_t nfsrv_gss_context_ttl = GSS_CTX_EXPIRE;
129#define GSS_SVC_CTX_TTL ((uint64_t)max(2*GSS_CTX_PEND, nfsrv_gss_context_ttl) * NSEC_PER_SEC)
2d21ac55
A
130#endif /* NFSSERVER */
131
132#if NFSCLIENT
133lck_grp_t *nfs_gss_clnt_grp;
134#endif /* NFSCLIENT */
135
39037602
A
136#define KRB5_MAX_MIC_SIZE 128
137uint8_t krb5_mech_oid[11] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 };
138static uint8_t xdrpad[] = { 0x00, 0x00, 0x00, 0x00};
2d21ac55
A
139
140#if NFSCLIENT
141static int nfs_gss_clnt_ctx_find(struct nfsreq *);
2d21ac55 142static int nfs_gss_clnt_ctx_init(struct nfsreq *, struct nfs_gss_clnt_ctx *);
6d2010ae 143static int nfs_gss_clnt_ctx_init_retry(struct nfsreq *, struct nfs_gss_clnt_ctx *);
2d21ac55 144static int nfs_gss_clnt_ctx_callserver(struct nfsreq *, struct nfs_gss_clnt_ctx *);
39236c6e 145static uint8_t *nfs_gss_clnt_svcname(struct nfsmount *, gssd_nametype *, uint32_t *);
39037602 146static int nfs_gss_clnt_gssd_upcall(struct nfsreq *, struct nfs_gss_clnt_ctx *, uint32_t);
3e170ce0 147void nfs_gss_clnt_ctx_neg_cache_reap(struct nfsmount *);
fe8ab488 148static void nfs_gss_clnt_ctx_clean(struct nfs_gss_clnt_ctx *);
39037602 149static int nfs_gss_clnt_ctx_copy(struct nfs_gss_clnt_ctx *, struct nfs_gss_clnt_ctx **);
fe8ab488
A
150static void nfs_gss_clnt_ctx_destroy(struct nfs_gss_clnt_ctx *);
151static void nfs_gss_clnt_log_error(struct nfsreq *, struct nfs_gss_clnt_ctx *, uint32_t, uint32_t);
2d21ac55
A
152#endif /* NFSCLIENT */
153
154#if NFSSERVER
155static struct nfs_gss_svc_ctx *nfs_gss_svc_ctx_find(uint32_t);
156static void nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx *);
157static void nfs_gss_svc_ctx_timer(void *, void *);
158static int nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx *);
159static int nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *, uint32_t);
160#endif /* NFSSERVER */
161
316670eb
A
162static void host_release_special_port(mach_port_t);
163static mach_port_t host_copy_special_port(mach_port_t);
2d21ac55
A
164static void nfs_gss_mach_alloc_buffer(u_char *, uint32_t, vm_map_copy_t *);
165static int nfs_gss_mach_vmcopyout(vm_map_copy_t, uint32_t, u_char *);
39037602 166
2d21ac55
A
167static int nfs_gss_mchain_length(mbuf_t);
168static int nfs_gss_append_chain(struct nfsm_chain *, mbuf_t);
169static void nfs_gss_nfsm_chain(struct nfsm_chain *, mbuf_t);
2d21ac55
A
170
171#if NFSSERVER
172thread_call_t nfs_gss_svc_ctx_timer_call;
173int nfs_gss_timer_on = 0;
174uint32_t nfs_gss_ctx_count = 0;
175const uint32_t nfs_gss_ctx_max = GSS_SVC_MAXCONTEXTS;
176#endif /* NFSSERVER */
177
178/*
179 * Initialization when NFS starts
180 */
181void
182nfs_gss_init(void)
183{
184#if NFSCLIENT
185 nfs_gss_clnt_grp = lck_grp_alloc_init("rpcsec_gss_clnt", LCK_GRP_ATTR_NULL);
186#endif /* NFSCLIENT */
187
188#if NFSSERVER
189 nfs_gss_svc_grp = lck_grp_alloc_init("rpcsec_gss_svc", LCK_GRP_ATTR_NULL);
190
191 nfs_gss_svc_ctx_hashtbl = hashinit(SVC_CTX_HASHSZ, M_TEMP, &nfs_gss_svc_ctx_hash);
192 nfs_gss_svc_ctx_mutex = lck_mtx_alloc_init(nfs_gss_svc_grp, LCK_ATTR_NULL);
193
194 nfs_gss_svc_ctx_timer_call = thread_call_allocate(nfs_gss_svc_ctx_timer, NULL);
195#endif /* NFSSERVER */
196}
197
39037602
A
198/*
199 * Common RPCSEC_GSS support routines
200 */
201
202static errno_t
203rpc_gss_prepend_32(mbuf_t *mb, uint32_t value)
204{
205 int error;
206 uint32_t *data;
207
208#if 0
209 data = mbuf_data(*mb);
210 /*
211 * If a wap token comes back and is not aligned
212 * get a new buffer (which should be aligned) to put the
213 * length in.
214 */
215 if ((uintptr_t)data & 0x3) {
216 mbuf_t nmb;
217
218 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &nmb);
219 if (error)
220 return (error);
221 mbuf_setnext(nmb, *mb);
222 *mb = nmb;
223 }
224#endif
225 error = mbuf_prepend(mb, sizeof(uint32_t), MBUF_WAITOK);
226 if (error)
227 return (error);
228
229 data = mbuf_data(*mb);
230 *data = txdr_unsigned(value);
231
232 return (0);
233}
234
235/*
236 * Prepend the sequence number to the xdr encode argumen or result
237 * Sequence number is prepended in its own mbuf.
238 *
239 * On successful return mbp_head will point to the old mbuf chain
240 * prepended with a new mbuf that has the sequence number.
241 */
242
243static errno_t
244rpc_gss_data_create(mbuf_t *mbp_head, uint32_t seqnum)
245{
246 int error;
247 mbuf_t mb;
248 struct nfsm_chain nmc;
249 struct nfsm_chain *nmcp = &nmc;
250 uint8_t *data;
251
252 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
253 if (error)
254 return (error);
255 data = mbuf_data(mb);
256#if 0
257 /* Reserve space for prepending */
258 len = mbuf_maxlen(mb);
259 len = (len & ~0x3) - NFSX_UNSIGNED;
260 printf("%s: data = %p, len = %d\n", __func__, data, (int)len);
261 error = mbuf_setdata(mb, data + len, 0);
262 if (error || mbuf_trailingspace(mb))
263 printf("%s: data = %p trailingspace = %d error = %d\n", __func__, mbuf_data(mb), (int)mbuf_trailingspace(mb), error);
264#endif
265 /* Reserve 16 words for prepending */
266 error = mbuf_setdata(mb, data + 16*sizeof(uint32_t), 0);
267 nfsm_chain_init(nmcp, mb);
268 nfsm_chain_add_32(error, nmcp, seqnum);
269 nfsm_chain_build_done(error, nmcp);
270 if (error)
271 return (EINVAL);
272 mbuf_setnext(nmcp->nmc_mcur, *mbp_head);
273 *mbp_head = nmcp->nmc_mhead;
274
275 return (0);
276}
277
278/*
279 * Create an rpc_gss_integ_data_t given an argument or result in mb_head.
280 * On successful return mb_head will point to the rpc_gss_integ_data_t of length len.
281 * Note mb_head will now point to a 4 byte sequence number. len does not include
282 * any extra xdr padding.
283 * Returns 0 on success, else an errno_t
284 */
285
286static errno_t
287rpc_gss_integ_data_create(gss_ctx_id_t ctx, mbuf_t *mb_head, uint32_t seqnum, uint32_t *len)
288{
289 uint32_t error;
290 uint32_t major;
291 uint32_t length;
292 gss_buffer_desc mic;
293 struct nfsm_chain nmc;
294
295 /* Length of the argument or result */
296 length = nfs_gss_mchain_length(*mb_head);
297 if (len)
298 *len = length;
299 error = rpc_gss_data_create(mb_head, seqnum);
300 if (error)
301 return (error);
302
303 /*
304 * length is the length of the rpc_gss_data
305 */
306 length += NFSX_UNSIGNED; /* Add the sequence number to the length */
307 major = gss_krb5_get_mic_mbuf(&error, ctx, 0, *mb_head, 0, length, &mic);
308 if (major != GSS_S_COMPLETE) {
309 printf("gss_krb5_get_mic_mbuf failed %d\n", error);
310 return (error);
311 }
312
313 error = rpc_gss_prepend_32(mb_head, length);
314 if (error)
315 return (error);
316
317 nfsm_chain_dissect_init(error, &nmc, *mb_head);
318 /* Append GSS mic token by advancing rpc_gss_data_t length + NFSX_UNSIGNED (size of the length field) */
319 nfsm_chain_adv(error, &nmc, length + NFSX_UNSIGNED);
320 nfsm_chain_finish_mbuf(error, &nmc); // Force the mic into its own sub chain.
321 nfsm_chain_add_32(error, &nmc, mic.length);
322 nfsm_chain_add_opaque(error, &nmc, mic.value, mic.length);
323 nfsm_chain_build_done(error, &nmc);
324 gss_release_buffer(NULL, &mic);
325
326// printmbuf("rpc_gss_integ_data_create done", *mb_head, 0, 0);
327 assert(nmc.nmc_mhead == *mb_head);
328
329 return (error);
330}
331
332/*
333 * Create an rpc_gss_priv_data_t out of the supplied raw arguments or results in mb_head.
334 * On successful return mb_head will point to a wrap token of lenght len.
335 * Note len does not include any xdr padding
336 * Returns 0 on success, else an errno_t
337 */
338static errno_t
339rpc_gss_priv_data_create(gss_ctx_id_t ctx, mbuf_t *mb_head, uint32_t seqnum, uint32_t *len)
340{
341 uint32_t error;
342 uint32_t major;
343 struct nfsm_chain nmc;
344 uint32_t pad;
345 uint32_t length;
346
347 error = rpc_gss_data_create(mb_head, seqnum);
348 if (error)
349 return (error);
350
351 length = nfs_gss_mchain_length(*mb_head);
352 major = gss_krb5_wrap_mbuf(&error, ctx, 1, 0, mb_head, 0, length, NULL);
353 if (major != GSS_S_COMPLETE)
354 return (error);
355
356 length = nfs_gss_mchain_length(*mb_head);
357 if (len)
358 *len = length;
359 pad = nfsm_pad(length);
360
361 /* Prepend the opaque length of rep rpc_gss_priv_data */
362 error = rpc_gss_prepend_32(mb_head, length);
363
364 if (error)
365 return (error);
366 if (pad) {
367 nfsm_chain_dissect_init(error, &nmc, *mb_head);
368 /* Advance the opauque size of length and length data */
369 nfsm_chain_adv(error, &nmc, NFSX_UNSIGNED + length);
370 nfsm_chain_finish_mbuf(error, &nmc);
371 nfsm_chain_add_opaque_nopad(error, &nmc, xdrpad, pad);
372 nfsm_chain_build_done(error, &nmc);
373 }
374
375 return (error);
376}
377
2d21ac55
A
378#if NFSCLIENT
379
39037602
A
380/*
381 * Restore the argument or result from an rpc_gss_integ_data mbuf chain
382 * We have a four byte seqence number, len arguments, and an opaque
383 * encoded mic, possibly followed by some pad bytes. The mic and possible
384 * pad bytes are on their own sub mbuf chains.
385 *
386 * On successful return mb_head is the chain of the xdr args or results sans
387 * the sequence number and mic and return 0. Otherwise return an errno.
388 *
389 */
390static errno_t
391rpc_gss_integ_data_restore(gss_ctx_id_t ctx __unused, mbuf_t *mb_head, size_t len)
392{
393 mbuf_t mb = *mb_head;
394 mbuf_t tail = NULL, next;
395
396 /* Chop of the opaque length and seq number */
397 mbuf_adj(mb, 2 * NFSX_UNSIGNED);
398
399 /* should only be one, ... but */
400 for (; mb; mb = next) {
401 next = mbuf_next(mb);
402 if (mbuf_len(mb) == 0)
403 mbuf_free(mb);
404 else
405 break;
406 }
407 *mb_head = mb;
408
409 for (; mb && len; mb = mbuf_next(mb)) {
410 tail = mb;
411 if (mbuf_len(mb) <= len)
412 len -= mbuf_len(mb);
413 else
414 return (EBADRPC);
415 }
416 /* drop the mic */
417 if (tail) {
418 mbuf_setnext(tail, NULL);
419 mbuf_freem(mb);
420 }
421
422 return (0);
423}
424
425/*
426 * Restore the argument or result rfom an rpc_gss_priv_data mbuf chain
427 * mb_head points to the wrap token of length len.
428 *
429 * On successful return mb_head is our original xdr arg or result an
430 * the return value is 0. Otherise return an errno
431 */
432static errno_t
433rpc_gss_priv_data_restore(gss_ctx_id_t ctx, mbuf_t *mb_head, size_t len)
434{
435 uint32_t major, error;
436 mbuf_t mb = *mb_head, next;
437 uint32_t plen;
438 size_t length;
439 gss_qop_t qop = GSS_C_QOP_REVERSE;
440
441 /* Chop of the opaque length */
442 mbuf_adj(mb, NFSX_UNSIGNED);
443 /* If we have padding, drop it */
444 plen = nfsm_pad(len);
445 if (plen) {
446 mbuf_t tail = NULL;
447
448 for(length = 0; length < len && mb; mb = mbuf_next(mb)) {
449 tail = mb;
450 length += mbuf_len(mb);
451 }
452 if ((length != len) || (mb == NULL) || (tail == NULL))
453 return (EBADRPC);
454
455 mbuf_freem(mb);
456 mbuf_setnext(tail, NULL);
457 }
458
459 major = gss_krb5_unwrap_mbuf(&error, ctx, mb_head, 0, len, NULL, &qop);
460 if (major != GSS_S_COMPLETE) {
461 printf("gss_krb5_unwrap_mbuf failed. major = %d minor = %d\n", (int)major, error);
462 return (error);
463 }
464 mb = *mb_head;
465
466 /* Drop the seqence number */
467 mbuf_adj(mb, NFSX_UNSIGNED);
468 assert(mbuf_len(mb) == 0);
469
470 /* Chop of any empty mbufs */
471 for (mb = *mb_head; mb; mb = next) {
472 next = mbuf_next(mb);
473 if (mbuf_len(mb) == 0)
474 mbuf_free(mb);
475 else
476 break;
477 }
478 *mb_head = mb;
479
480 return (0);
481}
482
2d21ac55
A
483/*
484 * Find the context for a particular user.
485 *
486 * If the context doesn't already exist
487 * then create a new context for this user.
488 *
489 * Note that the code allows superuser (uid == 0)
490 * to adopt the context of another user.
39236c6e
A
491 *
492 * We'll match on the audit session ids, since those
493 * processes will have acccess to the same credential cache.
2d21ac55 494 */
39236c6e
A
495
496#define kauth_cred_getasid(cred) ((cred)->cr_audit.as_aia_p->ai_asid)
497#define kauth_cred_getauid(cred) ((cred)->cr_audit.as_aia_p->ai_auid)
498
3e170ce0
A
499#define SAFE_CAST_INTTYPE( type, intval ) \
500 ( (type)(intval)/(sizeof(type) < sizeof(intval) ? 0 : 1) )
501
502uid_t
503nfs_cred_getasid2uid(kauth_cred_t cred)
504{
505 uid_t result = SAFE_CAST_INTTYPE(uid_t, kauth_cred_getasid(cred));
506 return (result);
507}
508
fe8ab488
A
509/*
510 * Debugging
511 */
512static void
513nfs_gss_clnt_ctx_dump(struct nfsmount *nmp)
514{
515 struct nfs_gss_clnt_ctx *cp;
516
517 lck_mtx_lock(&nmp->nm_lock);
3e170ce0 518 NFS_GSS_DBG("Enter\n");
fe8ab488
A
519 TAILQ_FOREACH(cp, &nmp->nm_gsscl, gss_clnt_entries) {
520 lck_mtx_lock(cp->gss_clnt_mtx);
521 printf("context %d/%d: refcnt = %d, flags = %x\n",
522 kauth_cred_getasid(cp->gss_clnt_cred),
523 kauth_cred_getauid(cp->gss_clnt_cred),
524 cp->gss_clnt_refcnt, cp->gss_clnt_flags);
525 lck_mtx_unlock(cp->gss_clnt_mtx);
526 }
3e170ce0 527 NFS_GSS_DBG("Exit\n");
fe8ab488
A
528 lck_mtx_unlock(&nmp->nm_lock);
529}
530
3e170ce0
A
531static char *
532nfs_gss_clnt_ctx_name(struct nfsmount *nmp, struct nfs_gss_clnt_ctx *cp, char *buf, int len)
533{
534 char *np;
535 int nlen;
536 const char *server = "";
537
538 if (nmp && nmp->nm_mountp)
539 server = vfs_statfs(nmp->nm_mountp)->f_mntfromname;
540
541 if (cp == NULL) {
542 snprintf(buf, len, "[%s] NULL context", server);
543 return (buf);
544 }
545
546 if (cp->gss_clnt_principal && !cp->gss_clnt_display) {
547 np = (char *)cp->gss_clnt_principal;
548 nlen = cp->gss_clnt_prinlen;
549 } else {
550 np = cp->gss_clnt_display;
551 nlen = np ? strlen(cp->gss_clnt_display) : 0;
552 }
553 if (nlen)
554 snprintf(buf, len, "[%s] %.*s %d/%d %s", server, nlen, np,
555 kauth_cred_getasid(cp->gss_clnt_cred),
556 kauth_cred_getuid(cp->gss_clnt_cred),
557 cp->gss_clnt_principal ? "" : "[from default cred] ");
558 else
559 snprintf(buf, len, "[%s] using default %d/%d ", server,
560 kauth_cred_getasid(cp->gss_clnt_cred),
561 kauth_cred_getuid(cp->gss_clnt_cred));
562 return (buf);
563}
564
565#define NFS_CTXBUFSZ 80
566#define NFS_GSS_CTX(req, cp) nfs_gss_clnt_ctx_name((req)->r_nmp, cp ? cp : (req)->r_gss_ctx, CTXBUF, sizeof(CTXBUF))
567
fe8ab488
A
568#define NFS_GSS_CLNT_CTX_DUMP(nmp) \
569 do { \
570 if (NFS_GSS_ISDBG && (NFS_DEBUG_FLAGS & 0x2)) \
571 nfs_gss_clnt_ctx_dump((nmp)); \
572 } while (0)
573
39236c6e
A
574static int
575nfs_gss_clnt_ctx_cred_match(kauth_cred_t cred1, kauth_cred_t cred2)
576{
577 if (kauth_cred_getasid(cred1) == kauth_cred_getasid(cred2))
578 return (1);
579 return (0);
580}
581
3e170ce0
A
582/*
583 * Busy the mount for each principal set on the mount
584 * so that the automounter will not unmount the file
585 * system underneath us. With out this, if an unmount
586 * occurs the principal that is set for an audit session
587 * will be lost and we may end up with a different identity.
588 *
589 * Note setting principals on the mount is a bad idea. This
590 * really should be handle by KIM (Kerberos Identity Management)
591 * so that defaults can be set by service identities.
592 */
593
594static void
595nfs_gss_clnt_mnt_ref(struct nfsmount *nmp)
596{
597 int error;
598 vnode_t rvp;
599
600 if (nmp == NULL ||
601 !(vfs_flags(nmp->nm_mountp) & MNT_AUTOMOUNTED))
602 return;
603
604 error = VFS_ROOT(nmp->nm_mountp, &rvp, NULL);
605 if (!error) {
606 vnode_ref(rvp);
607 vnode_put(rvp);
608 }
609}
610
611/*
612 * Unbusy the mout. See above comment,
613 */
614
615static void
616nfs_gss_clnt_mnt_rele(struct nfsmount *nmp)
617{
618 int error;
619 vnode_t rvp;
620
621 if (nmp == NULL ||
622 !(vfs_flags(nmp->nm_mountp) & MNT_AUTOMOUNTED))
623 return;
624
625 error = VFS_ROOT(nmp->nm_mountp, &rvp, NULL);
626 if (!error) {
627 vnode_rele(rvp);
628 vnode_put(rvp);
629 }
630}
631
632int nfs_root_steals_ctx = 1;
633
2d21ac55 634static int
3e170ce0 635nfs_gss_clnt_ctx_find_principal(struct nfsreq *req, uint8_t *principal, uint32_t plen, uint32_t nt)
2d21ac55
A
636{
637 struct nfsmount *nmp = req->r_nmp;
638 struct nfs_gss_clnt_ctx *cp;
3e170ce0 639 struct nfsreq treq;
2d21ac55 640 int error = 0;
fe8ab488 641 struct timeval now;
3e170ce0
A
642 char CTXBUF[NFS_CTXBUFSZ];
643
644 bzero(&treq, sizeof (struct nfsreq));
645 treq.r_nmp = nmp;
646
fe8ab488 647 microuptime(&now);
2d21ac55
A
648 lck_mtx_lock(&nmp->nm_lock);
649 TAILQ_FOREACH(cp, &nmp->nm_gsscl, gss_clnt_entries) {
fe8ab488
A
650 lck_mtx_lock(cp->gss_clnt_mtx);
651 if (cp->gss_clnt_flags & GSS_CTX_DESTROY) {
3e170ce0
A
652 NFS_GSS_DBG("Found destroyed context %s refcnt = %d continuing\n",
653 NFS_GSS_CTX(req, cp),
fe8ab488
A
654 cp->gss_clnt_refcnt);
655 lck_mtx_unlock(cp->gss_clnt_mtx);
656 continue;
657 }
39236c6e 658 if (nfs_gss_clnt_ctx_cred_match(cp->gss_clnt_cred, req->r_cred)) {
fe8ab488
A
659 if (nmp->nm_gsscl.tqh_first != cp) {
660 TAILQ_REMOVE(&nmp->nm_gsscl, cp, gss_clnt_entries);
661 TAILQ_INSERT_HEAD(&nmp->nm_gsscl, cp, gss_clnt_entries);
662 }
3e170ce0
A
663 if (principal) {
664 /*
665 * If we have a principal, but it does not match the current cred
666 * mark it for removal
667 */
668 if (cp->gss_clnt_prinlen != plen || cp->gss_clnt_prinnt != nt ||
669 bcmp(cp->gss_clnt_principal, principal, plen) != 0) {
670 cp->gss_clnt_flags |= (GSS_CTX_INVAL | GSS_CTX_DESTROY);
671 cp->gss_clnt_refcnt++;
672 lck_mtx_unlock(cp->gss_clnt_mtx);
673 NFS_GSS_DBG("Marking %s for deletion because %s does not match\n",
674 NFS_GSS_CTX(req, cp), principal);
675 NFS_GSS_DBG("len = (%d,%d), nt = (%d,%d)\n", cp->gss_clnt_prinlen, plen,
676 cp->gss_clnt_prinnt, nt);
677 treq.r_gss_ctx = cp;
678 cp = NULL;
679 break;
680 }
681 }
fe8ab488 682 if (cp->gss_clnt_flags & GSS_CTX_INVAL) {
3e170ce0
A
683 /*
684 * If we're still being used and we're not expired
685 * just return and don't bother gssd again. Note if
686 * gss_clnt_nctime is zero it is about to be set to now.
687 */
688 if (cp->gss_clnt_nctime + GSS_NEG_CACHE_TO >= now.tv_sec || cp->gss_clnt_nctime == 0) {
689 NFS_GSS_DBG("Context %s (refcnt = %d) not expired returning EAUTH nctime = %ld now = %ld\n",
690 NFS_GSS_CTX(req, cp), cp->gss_clnt_refcnt, cp->gss_clnt_nctime, now.tv_sec);
691 lck_mtx_unlock(cp->gss_clnt_mtx);
692 lck_mtx_unlock(&nmp->nm_lock);
693 return (NFSERR_EAUTH);
694 }
695 if (cp->gss_clnt_refcnt) {
696 struct nfs_gss_clnt_ctx *ncp;
697 /*
698 * If this context has references, we can't use it so we mark if for
699 * destruction and create a new context based on this one in the
700 * same manner as renewing one.
701 */
702 cp->gss_clnt_flags |= GSS_CTX_DESTROY;
703 NFS_GSS_DBG("Context %s has expired but we still have %d references\n",
704 NFS_GSS_CTX(req, cp), cp->gss_clnt_refcnt);
39037602 705 error = nfs_gss_clnt_ctx_copy(cp, &ncp);
3e170ce0
A
706 lck_mtx_unlock(cp->gss_clnt_mtx);
707 if (error) {
708 lck_mtx_unlock(&nmp->nm_lock);
709 return (error);
710 }
711 cp = ncp;
712 break;
713 } else {
3e170ce0
A
714 if (cp->gss_clnt_nctime)
715 nmp->nm_ncentries--;
716 lck_mtx_unlock(cp->gss_clnt_mtx);
717 TAILQ_REMOVE(&nmp->nm_gsscl, cp, gss_clnt_entries);
718 break;
719 }
fe8ab488 720 }
3e170ce0
A
721 /* Found a valid context to return */
722 cp->gss_clnt_refcnt++;
723 req->r_gss_ctx = cp;
fe8ab488 724 lck_mtx_unlock(cp->gss_clnt_mtx);
6d2010ae 725 lck_mtx_unlock(&nmp->nm_lock);
2d21ac55
A
726 return (0);
727 }
fe8ab488 728 lck_mtx_unlock(cp->gss_clnt_mtx);
2d21ac55
A
729 }
730
4bd07ac2 731 if (!cp && nfs_root_steals_ctx && principal == NULL && kauth_cred_getuid(req->r_cred) == 0) {
2d21ac55
A
732 /*
733 * If superuser is trying to get access, then co-opt
734 * the first valid context in the list.
735 * XXX Ultimately, we need to allow superuser to
736 * go ahead and attempt to set up its own context
737 * in case one is set up for it.
738 */
739 TAILQ_FOREACH(cp, &nmp->nm_gsscl, gss_clnt_entries) {
fe8ab488 740 if (!(cp->gss_clnt_flags & (GSS_CTX_INVAL|GSS_CTX_DESTROY))) {
2d21ac55 741 nfs_gss_clnt_ctx_ref(req, cp);
6d2010ae 742 lck_mtx_unlock(&nmp->nm_lock);
3e170ce0 743 NFS_GSS_DBG("Root stole context %s\n", NFS_GSS_CTX(req, NULL));
2d21ac55
A
744 return (0);
745 }
746 }
747 }
748
3e170ce0
A
749 NFS_GSS_DBG("Context %s%sfound in Neg Cache @ %ld\n",
750 NFS_GSS_CTX(req, cp),
751 cp == NULL ? " not " : "",
fe8ab488 752 cp == NULL ? 0L : cp->gss_clnt_nctime);
3e170ce0 753
2d21ac55 754 /*
fe8ab488 755 * Not found - create a new context
2d21ac55 756 */
2d21ac55 757
2d21ac55 758 if (cp == NULL) {
fe8ab488
A
759 MALLOC(cp, struct nfs_gss_clnt_ctx *, sizeof(*cp), M_TEMP, M_WAITOK|M_ZERO);
760 if (cp == NULL) {
761 lck_mtx_unlock(&nmp->nm_lock);
762 return (ENOMEM);
763 }
764 cp->gss_clnt_cred = req->r_cred;
765 kauth_cred_ref(cp->gss_clnt_cred);
766 cp->gss_clnt_mtx = lck_mtx_alloc_init(nfs_gss_clnt_grp, LCK_ATTR_NULL);
767 cp->gss_clnt_ptime = now.tv_sec - GSS_PRINT_DELAY;
3e170ce0
A
768 if (principal) {
769 MALLOC(cp->gss_clnt_principal, uint8_t *, plen+1, M_TEMP, M_WAITOK|M_ZERO);
770 memcpy(cp->gss_clnt_principal, principal, plen);
771 cp->gss_clnt_prinlen = plen;
772 cp->gss_clnt_prinnt = nt;
773 cp->gss_clnt_flags |= GSS_CTX_STICKY;
774 nfs_gss_clnt_mnt_ref(nmp);
775 }
fe8ab488
A
776 } else {
777 nfs_gss_clnt_ctx_clean(cp);
3e170ce0
A
778 if (principal) {
779 /*
780 * If we have a principal and we found a matching audit
781 * session, then to get here, the principal had to match.
782 * In walking the context list if it has a principal
783 * or the principal is not set then we mark the context
784 * for destruction and set cp to NULL and we fall to the
785 * if clause above. If the context still has references
786 * again we copy the context which will preserve the principal
787 * and we end up here with the correct principal set.
788 * If we don't have references the the principal must have
789 * match and we will fall through here.
790 */
791 cp->gss_clnt_flags |= GSS_CTX_STICKY;
792 }
2d21ac55 793 }
3e170ce0 794
2d21ac55
A
795 cp->gss_clnt_thread = current_thread();
796 nfs_gss_clnt_ctx_ref(req, cp);
fe8ab488 797 TAILQ_INSERT_HEAD(&nmp->nm_gsscl, cp, gss_clnt_entries);
2d21ac55
A
798 lck_mtx_unlock(&nmp->nm_lock);
799
6d2010ae 800 error = nfs_gss_clnt_ctx_init_retry(req, cp); // Initialize new context
3e170ce0
A
801 if (error) {
802 NFS_GSS_DBG("nfs_gss_clnt_ctx_init_retry returned %d for %s\n", error, NFS_GSS_CTX(req, cp));
b0d623f7 803 nfs_gss_clnt_ctx_unref(req);
3e170ce0
A
804 }
805
806 /* Remove any old matching contex that had a different principal */
807 nfs_gss_clnt_ctx_unref(&treq);
b0d623f7 808
2d21ac55
A
809 return (error);
810}
811
3e170ce0
A
812static int
813nfs_gss_clnt_ctx_find(struct nfsreq *req)
814{
815 return (nfs_gss_clnt_ctx_find_principal(req, NULL, 0, 0));
816}
817
2d21ac55
A
818/*
819 * Inserts an RPCSEC_GSS credential into an RPC header.
820 * After the credential is inserted, the code continues
821 * to build the verifier which contains a signed checksum
822 * of the RPC header.
823 */
39037602 824
2d21ac55
A
825int
826nfs_gss_clnt_cred_put(struct nfsreq *req, struct nfsm_chain *nmc, mbuf_t args)
827{
2d21ac55
A
828 struct nfs_gss_clnt_ctx *cp;
829 uint32_t seqnum = 0;
39037602
A
830 uint32_t major;
831 uint32_t error = 0;
832 int slpflag, recordmark = 0, offset;
2d21ac55 833 struct gss_seq *gsp;
39037602 834 gss_buffer_desc mic;
3e170ce0 835
b0d623f7
A
836 slpflag = (PZERO-1);
837 if (req->r_nmp) {
6d2010ae 838 slpflag |= (NMFLAG(req->r_nmp, INTR) && req->r_thread && !(req->r_flags & R_NOINTR)) ? PCATCH : 0;
b0d623f7
A
839 recordmark = (req->r_nmp->nm_sotype == SOCK_STREAM);
840 }
3e170ce0 841
2d21ac55
A
842retry:
843 if (req->r_gss_ctx == NULL) {
844 /*
845 * Find the context for this user.
846 * If no context is found, one will
847 * be created.
848 */
849 error = nfs_gss_clnt_ctx_find(req);
850 if (error)
851 return (error);
852 }
853 cp = req->r_gss_ctx;
854
2d21ac55
A
855 /*
856 * If the context thread isn't null, then the context isn't
857 * yet complete and is for the exclusive use of the thread
858 * doing the context setup. Wait until the context thread
859 * is null.
860 */
861 lck_mtx_lock(cp->gss_clnt_mtx);
862 if (cp->gss_clnt_thread && cp->gss_clnt_thread != current_thread()) {
863 cp->gss_clnt_flags |= GSS_NEEDCTX;
b0d623f7 864 msleep(cp, cp->gss_clnt_mtx, slpflag | PDROP, "ctxwait", NULL);
6d2010ae 865 slpflag &= ~PCATCH;
b0d623f7 866 if ((error = nfs_sigintr(req->r_nmp, req, req->r_thread, 0)))
2d21ac55
A
867 return (error);
868 nfs_gss_clnt_ctx_unref(req);
869 goto retry;
870 }
871 lck_mtx_unlock(cp->gss_clnt_mtx);
872
873 if (cp->gss_clnt_flags & GSS_CTX_COMPLETE) {
874 /*
875 * Get a sequence number for this request.
876 * Check whether the oldest request in the window is complete.
877 * If it's still pending, then wait until it's done before
878 * we allocate a new sequence number and allow this request
879 * to proceed.
880 */
881 lck_mtx_lock(cp->gss_clnt_mtx);
882 while (win_getbit(cp->gss_clnt_seqbits,
883 ((cp->gss_clnt_seqnum - cp->gss_clnt_seqwin) + 1) % cp->gss_clnt_seqwin)) {
884 cp->gss_clnt_flags |= GSS_NEEDSEQ;
ebb1b9f4 885 msleep(cp, cp->gss_clnt_mtx, slpflag | PDROP, "seqwin", NULL);
6d2010ae 886 slpflag &= ~PCATCH;
b0d623f7 887 if ((error = nfs_sigintr(req->r_nmp, req, req->r_thread, 0))) {
2d21ac55
A
888 return (error);
889 }
ebb1b9f4 890 lck_mtx_lock(cp->gss_clnt_mtx);
2d21ac55
A
891 if (cp->gss_clnt_flags & GSS_CTX_INVAL) {
892 /* Renewed while while we were waiting */
893 lck_mtx_unlock(cp->gss_clnt_mtx);
894 nfs_gss_clnt_ctx_unref(req);
895 goto retry;
896 }
897 }
898 seqnum = ++cp->gss_clnt_seqnum;
899 win_setbit(cp->gss_clnt_seqbits, seqnum % cp->gss_clnt_seqwin);
900 lck_mtx_unlock(cp->gss_clnt_mtx);
901
902 MALLOC(gsp, struct gss_seq *, sizeof(*gsp), M_TEMP, M_WAITOK|M_ZERO);
903 if (gsp == NULL)
904 return (ENOMEM);
905 gsp->gss_seqnum = seqnum;
906 SLIST_INSERT_HEAD(&req->r_gss_seqlist, gsp, gss_seqnext);
907 }
908
909 /* Insert the credential */
910 nfsm_chain_add_32(error, nmc, RPCSEC_GSS);
911 nfsm_chain_add_32(error, nmc, 5 * NFSX_UNSIGNED + cp->gss_clnt_handle_len);
912 nfsm_chain_add_32(error, nmc, RPCSEC_GSS_VERS_1);
913 nfsm_chain_add_32(error, nmc, cp->gss_clnt_proc);
914 nfsm_chain_add_32(error, nmc, seqnum);
915 nfsm_chain_add_32(error, nmc, cp->gss_clnt_service);
916 nfsm_chain_add_32(error, nmc, cp->gss_clnt_handle_len);
b0d623f7
A
917 if (cp->gss_clnt_handle_len > 0) {
918 if (cp->gss_clnt_handle == NULL)
919 return (EBADRPC);
920 nfsm_chain_add_opaque(error, nmc, cp->gss_clnt_handle, cp->gss_clnt_handle_len);
921 }
922 if (error)
923 return(error);
2d21ac55
A
924 /*
925 * Now add the verifier
926 */
927 if (cp->gss_clnt_proc == RPCSEC_GSS_INIT ||
928 cp->gss_clnt_proc == RPCSEC_GSS_CONTINUE_INIT) {
929 /*
930 * If the context is still being created
931 * then use a null verifier.
932 */
933 nfsm_chain_add_32(error, nmc, RPCAUTH_NULL); // flavor
934 nfsm_chain_add_32(error, nmc, 0); // length
935 nfsm_chain_build_done(error, nmc);
936 if (!error)
937 nfs_gss_append_chain(nmc, args);
938 return (error);
939 }
940
b0d623f7 941 offset = recordmark ? NFSX_UNSIGNED : 0; // record mark
2d21ac55 942 nfsm_chain_build_done(error, nmc);
2d21ac55 943
39037602
A
944 major = gss_krb5_get_mic_mbuf((uint32_t *)&error, cp->gss_clnt_ctx_id, 0, nmc->nmc_mhead, offset, 0, &mic);
945 if (major != GSS_S_COMPLETE) {
946 printf ("gss_krb5_get_mic_buf failed %d\n", error);
947 return (error);
948 }
949
2d21ac55 950 nfsm_chain_add_32(error, nmc, RPCSEC_GSS); // flavor
39037602
A
951 nfsm_chain_add_32(error, nmc, mic.length); // length
952 nfsm_chain_add_opaque(error, nmc, mic.value, mic.length);
953 (void)gss_release_buffer(NULL, &mic);
2d21ac55
A
954 nfsm_chain_build_done(error, nmc);
955 if (error)
956 return (error);
957
958 /*
959 * Now we may have to compute integrity or encrypt the call args
960 * per RFC 2203 Section 5.3.2
961 */
962 switch (cp->gss_clnt_service) {
963 case RPCSEC_GSS_SVC_NONE:
39037602
A
964 if (args)
965 nfs_gss_append_chain(nmc, args);
2d21ac55
A
966 break;
967 case RPCSEC_GSS_SVC_INTEGRITY:
39037602
A
968 /*
969 * r_gss_arglen is the length of args mbuf going into the routine.
970 * Its used to find the mic if we need to restore the args.
971 */
972 /* Note the mbufs that were used in r_mrest are being encapsulated in the rpc_gss_integ_data_t */
973 assert(req->r_mrest == args);
974 nfsm_chain_finish_mbuf(error, nmc);
2d21ac55
A
975 if (error)
976 return (error);
39037602
A
977 error = rpc_gss_integ_data_create(cp->gss_clnt_ctx_id, &args, seqnum, &req->r_gss_arglen);
978 if (error)
979 break;
980 req->r_mrest = args;
981 req->r_gss_argoff = nfsm_chain_offset(nmc);
982 nfs_gss_append_chain(nmc, args);
2d21ac55
A
983 break;
984 case RPCSEC_GSS_SVC_PRIVACY:
2d21ac55 985 /*
39037602
A
986 * r_gss_arglen is the length of the wrap token sans any padding length.
987 * Its used to find any XDR padding of the wrap token.
2d21ac55 988 */
39037602
A
989 /* Note the mbufs that were used in r_mrest are being encapsulated in the rpc_gss_priv_data_t */
990 assert(req->r_mrest == args);
991 nfsm_chain_finish_mbuf(error, nmc);
2d21ac55
A
992 if (error)
993 return (error);
39037602
A
994 error = rpc_gss_priv_data_create(cp->gss_clnt_ctx_id, &args, seqnum, &req->r_gss_arglen);
995 if (error)
996 break;
997 req->r_mrest = args;
998 req->r_gss_argoff = nfsm_chain_offset(nmc);
999 nfs_gss_append_chain(nmc, args);
2d21ac55 1000 break;
39037602
A
1001 default:
1002 return (EINVAL);
2d21ac55
A
1003 }
1004
1005 return (error);
1006}
1007
1008/*
1009 * When receiving a reply, the client checks the verifier
1010 * returned by the server. Check that the verifier is the
1011 * correct type, then extract the sequence number checksum
1012 * from the token in the credential and compare it with a
1013 * computed checksum of the sequence number in the request
1014 * that was sent.
1015 */
1016int
1017nfs_gss_clnt_verf_get(
1018 struct nfsreq *req,
1019 struct nfsm_chain *nmc,
1020 uint32_t verftype,
1021 uint32_t verflen,
1022 uint32_t *accepted_statusp)
1023{
39037602 1024 gss_buffer_desc cksum;
2d21ac55 1025 uint32_t seqnum = 0;
39037602 1026 uint32_t major;
2d21ac55
A
1027 struct nfs_gss_clnt_ctx *cp = req->r_gss_ctx;
1028 struct nfsm_chain nmc_tmp;
1029 struct gss_seq *gsp;
39037602 1030 uint32_t reslen, offset;
2d21ac55 1031 int error = 0;
39037602
A
1032 mbuf_t results_mbuf, prev_mbuf, pad_mbuf;
1033 size_t ressize;
2d21ac55 1034
39037602 1035 reslen = 0;
2d21ac55
A
1036 *accepted_statusp = 0;
1037
1038 if (cp == NULL)
b0d623f7 1039 return (NFSERR_EAUTH);
2d21ac55
A
1040 /*
1041 * If it's not an RPCSEC_GSS verifier, then it has to
1042 * be a null verifier that resulted from either
1043 * a CONTINUE_NEEDED reply during context setup or
1044 * from the reply to an AUTH_UNIX call from a dummy
1045 * context that resulted from a fallback to sec=sys.
1046 */
1047 if (verftype != RPCSEC_GSS) {
1048 if (verftype != RPCAUTH_NULL)
b0d623f7 1049 return (NFSERR_EAUTH);
fe8ab488 1050 if (cp->gss_clnt_flags & GSS_CTX_COMPLETE)
b0d623f7 1051 return (NFSERR_EAUTH);
2d21ac55
A
1052 if (verflen > 0)
1053 nfsm_chain_adv(error, nmc, nfsm_rndup(verflen));
1054 nfsm_chain_get_32(error, nmc, *accepted_statusp);
1055 return (error);
1056 }
1057
2d21ac55
A
1058 /*
1059 * If we received an RPCSEC_GSS verifier but the
1060 * context isn't yet complete, then it must be
1061 * the context complete message from the server.
1062 * The verifier will contain an encrypted checksum
1063 * of the window but we don't have the session key
1064 * yet so we can't decrypt it. Stash the verifier
1065 * and check it later in nfs_gss_clnt_ctx_init() when
1066 * the context is complete.
1067 */
1068 if (!(cp->gss_clnt_flags & GSS_CTX_COMPLETE)) {
1069 MALLOC(cp->gss_clnt_verf, u_char *, verflen, M_TEMP, M_WAITOK|M_ZERO);
1070 if (cp->gss_clnt_verf == NULL)
1071 return (ENOMEM);
39037602 1072 cp->gss_clnt_verflen = verflen;
2d21ac55
A
1073 nfsm_chain_get_opaque(error, nmc, verflen, cp->gss_clnt_verf);
1074 nfsm_chain_get_32(error, nmc, *accepted_statusp);
1075 return (error);
1076 }
1077
39037602
A
1078 if (verflen > KRB5_MAX_MIC_SIZE)
1079 return (EBADRPC);
1080 cksum.length = verflen;
1081 MALLOC(cksum.value, void *, verflen, M_TEMP, M_WAITOK);
b0d623f7 1082
2d21ac55 1083 /*
39037602 1084 * Get the gss mic
2d21ac55 1085 */
39037602
A
1086 nfsm_chain_get_opaque(error, nmc, verflen, cksum.value);
1087 if (error) {
1088 FREE(cksum.value, M_TEMP);
2d21ac55 1089 goto nfsmout;
39037602 1090 }
2d21ac55
A
1091
1092 /*
1093 * Search the request sequence numbers for this reply, starting
1094 * with the most recent, looking for a checksum that matches
1095 * the one in the verifier returned by the server.
1096 */
1097 SLIST_FOREACH(gsp, &req->r_gss_seqlist, gss_seqnext) {
39037602
A
1098 gss_buffer_desc seqnum_buf;
1099 uint32_t network_seqnum = htonl(gsp->gss_seqnum);
1100
1101 seqnum_buf.length = sizeof(network_seqnum);
1102 seqnum_buf.value = &network_seqnum;
1103 major = gss_krb5_verify_mic(NULL, cp->gss_clnt_ctx_id, &seqnum_buf, &cksum, NULL);
1104 if (major == GSS_S_COMPLETE)
2d21ac55
A
1105 break;
1106 }
39037602 1107 FREE(cksum.value, M_TEMP);
2d21ac55 1108 if (gsp == NULL)
b0d623f7 1109 return (NFSERR_EAUTH);
2d21ac55
A
1110
1111 /*
1112 * Get the RPC accepted status
1113 */
1114 nfsm_chain_get_32(error, nmc, *accepted_statusp);
1115 if (*accepted_statusp != RPC_SUCCESS)
1116 return (0);
1117
1118 /*
1119 * Now we may have to check integrity or decrypt the results
1120 * per RFC 2203 Section 5.3.2
1121 */
1122 switch (cp->gss_clnt_service) {
1123 case RPCSEC_GSS_SVC_NONE:
1124 /* nothing to do */
1125 break;
1126 case RPCSEC_GSS_SVC_INTEGRITY:
1127 /*
39037602 1128 * Here's what we expect in the integrity results from RFC 2203:
2d21ac55
A
1129 *
1130 * - length of seq num + results (4 bytes)
1131 * - sequence number (4 bytes)
1132 * - results (variable bytes)
39037602
A
1133 * - length of checksum token
1134 * - checksum of seqnum + results
2d21ac55 1135 */
39037602 1136
2d21ac55
A
1137 nfsm_chain_get_32(error, nmc, reslen); // length of results
1138 if (reslen > NFS_MAXPACKET) {
1139 error = EBADRPC;
1140 goto nfsmout;
1141 }
1142
39037602
A
1143 /* Advance and fetch the mic */
1144 nmc_tmp = *nmc;
1145 nfsm_chain_adv(error, &nmc_tmp, reslen); // skip over the results
1146 nfsm_chain_get_32(error, &nmc_tmp, cksum.length);
1147 MALLOC(cksum.value, void *, cksum.length, M_TEMP, M_WAITOK);
1148 nfsm_chain_get_opaque(error, &nmc_tmp, cksum.length, cksum.value);
1149 //XXX chop offf the cksum?
1150
1151 /* Call verify mic */
1152 offset = nfsm_chain_offset(nmc);
1153 major = gss_krb5_verify_mic_mbuf((uint32_t *)&error, cp->gss_clnt_ctx_id, nmc->nmc_mhead, offset, reslen, &cksum, NULL);
1154 FREE(cksum.value, M_TEMP);
1155 if (major != GSS_S_COMPLETE) {
1156 printf("client results: gss_krb5_verify_mic_mbuf failed %d\n", error);
1157 error = EBADRPC;
1158 goto nfsmout;
1159 }
2d21ac55
A
1160
1161 /*
1162 * Get the sequence number prepended to the results
39037602 1163 * and compare it against the header.
2d21ac55
A
1164 */
1165 nfsm_chain_get_32(error, nmc, seqnum);
39037602
A
1166 if (gsp->gss_seqnum != seqnum) {
1167 error = EBADRPC;
1168 goto nfsmout;
1169 }
1170#if 0
2d21ac55
A
1171 SLIST_FOREACH(gsp, &req->r_gss_seqlist, gss_seqnext) {
1172 if (seqnum == gsp->gss_seqnum)
1173 break;
1174 }
1175 if (gsp == NULL) {
1176 error = EBADRPC;
1177 goto nfsmout;
1178 }
39037602 1179#endif
2d21ac55
A
1180 break;
1181 case RPCSEC_GSS_SVC_PRIVACY:
1182 /*
1183 * Here's what we expect in the privacy results:
1184 *
39037602
A
1185 * opaque encodeing of the wrap token
1186 * - length of wrap token
1187 * - wrap token
2d21ac55 1188 */
39037602 1189 prev_mbuf = nmc->nmc_mcur;
2d21ac55 1190 nfsm_chain_get_32(error, nmc, reslen); // length of results
39037602 1191 if (reslen == 0 || reslen > NFS_MAXPACKET) {
2d21ac55
A
1192 error = EBADRPC;
1193 goto nfsmout;
1194 }
1195
39037602
A
1196 /* Get the wrap token (current mbuf in the chain starting at the current offset) */
1197 offset = nmc->nmc_ptr - (caddr_t)mbuf_data(nmc->nmc_mcur);
1198
1199 /* split out the wrap token */
1200 ressize = reslen;
1201 error = gss_normalize_mbuf(nmc->nmc_mcur, offset, &ressize, &results_mbuf, &pad_mbuf, 0);
2d21ac55
A
1202 if (error)
1203 goto nfsmout;
2d21ac55 1204
39037602
A
1205 if (pad_mbuf) {
1206 assert(nfsm_pad(reslen) == mbuf_len(pad_mbuf));
1207 mbuf_free(pad_mbuf);
1208 }
2d21ac55 1209
39037602
A
1210 major = gss_krb5_unwrap_mbuf((uint32_t *)&error, cp->gss_clnt_ctx_id, &results_mbuf, 0, ressize, NULL, NULL);
1211 if (major) {
1212 printf("%s unwraped failed %d\n", __func__, error);
2d21ac55
A
1213 goto nfsmout;
1214 }
1215
39037602
A
1216 /* Now replace the wrapped arguments with the unwrapped ones */
1217 mbuf_setnext(prev_mbuf, results_mbuf);
1218 nmc->nmc_mcur = results_mbuf;
1219 nmc->nmc_ptr = mbuf_data(results_mbuf);
1220 nmc->nmc_left = mbuf_len(results_mbuf);
2d21ac55
A
1221
1222 /*
1223 * Get the sequence number prepended to the results
39037602 1224 * and compare it against the header
2d21ac55
A
1225 */
1226 nfsm_chain_get_32(error, nmc, seqnum);
39037602
A
1227 if (gsp->gss_seqnum != seqnum) {
1228 printf("%s bad seqnum\n", __func__);
1229 error = EBADRPC;
1230 goto nfsmout;
1231 }
1232#if 0
2d21ac55
A
1233 SLIST_FOREACH(gsp, &req->r_gss_seqlist, gss_seqnext) {
1234 if (seqnum == gsp->gss_seqnum)
1235 break;
1236 }
1237 if (gsp == NULL) {
1238 error = EBADRPC;
1239 goto nfsmout;
1240 }
39037602 1241#endif
2d21ac55
A
1242 break;
1243 }
1244nfsmout:
1245 return (error);
1246}
1247
1248/*
1249 * An RPCSEC_GSS request with no integrity or privacy consists
1250 * of just the header mbufs followed by the arg mbufs.
1251 *
39037602
A
1252 * However, integrity or privacy the original mbufs have mbufs
1253 * prepended and appended to, which means we have to do some work to
1254 * restore the arg mbuf chain to its previous state in case we need to
1255 * retransmit.
2d21ac55
A
1256 *
1257 * The location and length of the args is marked by two fields
1258 * in the request structure: r_gss_argoff and r_gss_arglen,
1259 * which are stashed when the NFS request is built.
3e170ce0 1260 */
2d21ac55
A
1261int
1262nfs_gss_clnt_args_restore(struct nfsreq *req)
1263{
1264 struct nfs_gss_clnt_ctx *cp = req->r_gss_ctx;
1265 struct nfsm_chain mchain, *nmc = &mchain;
39037602 1266 int error = 0, merr;
2d21ac55 1267
3e170ce0 1268 if (cp == NULL)
b0d623f7 1269 return (NFSERR_EAUTH);
2d21ac55
A
1270
1271 if ((cp->gss_clnt_flags & GSS_CTX_COMPLETE) == 0)
1272 return (ENEEDAUTH);
1273
39037602
A
1274 /* Nothing to restore for SVC_NONE */
1275 if (cp->gss_clnt_service == RPCSEC_GSS_SVC_NONE)
1276 return (0);
1277
2d21ac55
A
1278 nfsm_chain_dissect_init(error, nmc, req->r_mhead); // start at RPC header
1279 nfsm_chain_adv(error, nmc, req->r_gss_argoff); // advance to args
1280 if (error)
1281 return (error);
1282
39037602
A
1283 if (cp->gss_clnt_service == RPCSEC_GSS_SVC_INTEGRITY)
1284 error = rpc_gss_integ_data_restore(cp->gss_clnt_ctx_id, &req->r_mrest, req->r_gss_arglen);
1285 else
1286 error = rpc_gss_priv_data_restore(cp->gss_clnt_ctx_id, &req->r_mrest, req->r_gss_arglen);
2d21ac55 1287
39037602
A
1288 merr = mbuf_setnext(nmc->nmc_mcur, req->r_mrest); /* Should always succeed */
1289 assert (merr == 0);
2d21ac55 1290
39037602 1291 return (error ? error : merr);
2d21ac55
A
1292}
1293
1294/*
1295 * This function sets up a new context on the client.
1296 * Context setup alternates upcalls to the gssd with NFS nullproc calls
1297 * to the server. Each of these calls exchanges an opaque token, obtained
1298 * via the gssd's calls into the GSS-API on either the client or the server.
1299 * This cycle of calls ends when the client's upcall to the gssd and the
1300 * server's response both return GSS_S_COMPLETE. At this point, the client
1301 * should have its session key and a handle that it can use to refer to its
1302 * new context on the server.
1303 */
1304static int
1305nfs_gss_clnt_ctx_init(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp)
1306{
1307 struct nfsmount *nmp = req->r_nmp;
39037602
A
1308 gss_buffer_desc cksum, window;
1309 uint32_t network_seqnum;
2d21ac55
A
1310 int client_complete = 0;
1311 int server_complete = 0;
2d21ac55 1312 int error = 0;
39037602
A
1313 int retrycnt = 0;
1314 uint32_t major;
2d21ac55
A
1315
1316 /* Initialize a new client context */
1317
2d21ac55 1318 if (cp->gss_clnt_svcname == NULL) {
fe8ab488
A
1319 cp->gss_clnt_svcname = nfs_gss_clnt_svcname(nmp, &cp->gss_clnt_svcnt, &cp->gss_clnt_svcnamlen);
1320 if (cp->gss_clnt_svcname == NULL) {
1321 error = NFSERR_EAUTH;
1322 goto nfsmout;
1323 }
2d21ac55 1324 }
b0d623f7 1325
2d21ac55
A
1326 cp->gss_clnt_proc = RPCSEC_GSS_INIT;
1327
1328 cp->gss_clnt_service =
6d2010ae
A
1329 req->r_auth == RPCAUTH_KRB5 ? RPCSEC_GSS_SVC_NONE :
1330 req->r_auth == RPCAUTH_KRB5I ? RPCSEC_GSS_SVC_INTEGRITY :
1331 req->r_auth == RPCAUTH_KRB5P ? RPCSEC_GSS_SVC_PRIVACY : 0;
2d21ac55
A
1332
1333 /*
1334 * Now loop around alternating gss_init_sec_context and
1335 * gss_accept_sec_context upcalls to the gssd on the client
1336 * and server side until the context is complete - or fails.
1337 */
1338 for (;;) {
b0d623f7 1339retry:
2d21ac55 1340 /* Upcall to the gss_init_sec_context in the gssd */
39037602 1341 error = nfs_gss_clnt_gssd_upcall(req, cp, retrycnt);
2d21ac55
A
1342 if (error)
1343 goto nfsmout;
1344
1345 if (cp->gss_clnt_major == GSS_S_COMPLETE) {
1346 client_complete = 1;
39037602 1347 NFS_GSS_DBG("Client complete\n");
2d21ac55
A
1348 if (server_complete)
1349 break;
1350 } else if (cp->gss_clnt_major != GSS_S_CONTINUE_NEEDED) {
39037602
A
1351 /*
1352 * We may have gotten here because the accept sec context
1353 * from the server failed and sent back a GSS token that
1354 * encapsulates a kerberos error token per RFC 1964/4121
1355 * with a status of GSS_S_CONTINUE_NEEDED. That caused us
1356 * to loop to the above up call and received the now
1357 * decoded errors.
1358 */
1359 retrycnt++;
1360 cp->gss_clnt_gssd_flags |= GSSD_RESTART;
1361 NFS_GSS_DBG("Retrying major = %x minor = %d\n", cp->gss_clnt_major, (int)cp->gss_clnt_minor);
1362 goto retry;
2d21ac55
A
1363 }
1364
1365 /*
1366 * Pass the token to the server.
1367 */
1368 error = nfs_gss_clnt_ctx_callserver(req, cp);
b0d623f7 1369 if (error) {
39037602
A
1370 if (error == ENEEDAUTH &&
1371 (cp->gss_clnt_proc == RPCSEC_GSS_INIT ||
1372 cp->gss_clnt_proc == RPCSEC_GSS_CONTINUE_INIT)) {
1373 /*
1374 * We got here because the server had a problem
1375 * trying to establish a context and sent that there
1376 * was a context problem at the rpc sec layer. Perhaps
1377 * gss_accept_sec_context succeeded in user space,
1378 * but the kernel could not handle the etype
1379 * to generate the mic for the verifier of the rpc_sec
1380 * window size.
1381 */
1382 retrycnt++;
1383 cp->gss_clnt_gssd_flags |= GSSD_RESTART;
1384 NFS_GSS_DBG("Retrying major = %x minor = %d\n", cp->gss_clnt_major, (int)cp->gss_clnt_minor);
b0d623f7
A
1385 goto retry;
1386 }
2d21ac55 1387 goto nfsmout;
b0d623f7 1388 }
2d21ac55 1389 if (cp->gss_clnt_major == GSS_S_COMPLETE) {
39037602 1390 NFS_GSS_DBG("Server complete\n");
2d21ac55
A
1391 server_complete = 1;
1392 if (client_complete)
1393 break;
39037602
A
1394 } else if (cp->gss_clnt_major == GSS_S_CONTINUE_NEEDED) {
1395 cp->gss_clnt_proc = RPCSEC_GSS_CONTINUE_INIT;
1396 } else {
1397 /* Server didn't like us. Try something else */
1398 retrycnt++;
1399 cp->gss_clnt_gssd_flags |= GSSD_RESTART;
1400 NFS_GSS_DBG("Retrying major = %x minor = %d\n", cp->gss_clnt_major, (int)cp->gss_clnt_minor);
2d21ac55 1401 }
2d21ac55
A
1402 }
1403
1404 /*
1405 * The context is apparently established successfully
1406 */
6d2010ae 1407 lck_mtx_lock(cp->gss_clnt_mtx);
2d21ac55 1408 cp->gss_clnt_flags |= GSS_CTX_COMPLETE;
6d2010ae 1409 lck_mtx_unlock(cp->gss_clnt_mtx);
2d21ac55 1410 cp->gss_clnt_proc = RPCSEC_GSS_DATA;
2d21ac55 1411
39037602
A
1412 network_seqnum = htonl(cp->gss_clnt_seqwin);
1413 window.length = sizeof (cp->gss_clnt_seqwin);
1414 window.value = &network_seqnum;
1415 cksum.value = cp->gss_clnt_verf;
1416 cksum.length = cp->gss_clnt_verflen;
1417 major = gss_krb5_verify_mic((uint32_t *)&error, cp->gss_clnt_ctx_id, &window, &cksum, NULL);
1418 cp->gss_clnt_verflen = 0;
2d21ac55
A
1419 FREE(cp->gss_clnt_verf, M_TEMP);
1420 cp->gss_clnt_verf = NULL;
39037602
A
1421 if (major != GSS_S_COMPLETE) {
1422 printf("%s: could not verify window\n", __func__);
b0d623f7 1423 error = NFSERR_EAUTH;
2d21ac55
A
1424 goto nfsmout;
1425 }
1426
1427 /*
1428 * Set an initial sequence number somewhat randomized.
1429 * Start small so we don't overflow GSS_MAXSEQ too quickly.
1430 * Add the size of the sequence window so seqbits arithmetic
1431 * doesn't go negative.
1432 */
1433 cp->gss_clnt_seqnum = (random() & 0xffff) + cp->gss_clnt_seqwin;
1434
1435 /*
1436 * Allocate a bitmap to keep track of which requests
1437 * are pending within the sequence number window.
1438 */
1439 MALLOC(cp->gss_clnt_seqbits, uint32_t *,
1440 nfsm_rndup((cp->gss_clnt_seqwin + 7) / 8), M_TEMP, M_WAITOK|M_ZERO);
1441 if (cp->gss_clnt_seqbits == NULL)
b0d623f7 1442 error = NFSERR_EAUTH;
39037602 1443
2d21ac55 1444nfsmout:
3e170ce0 1445 /*
b0d623f7
A
1446 * If the error is ENEEDAUTH we're not done, so no need
1447 * to wake up other threads again. This thread will retry in
1448 * the find or renew routines.
1449 */
39037602
A
1450 if (error == ENEEDAUTH) {
1451 NFS_GSS_DBG("Returning ENEEDAUTH\n");
b0d623f7 1452 return (error);
39037602 1453 }
b0d623f7 1454
2d21ac55
A
1455 /*
1456 * If there's an error, just mark it as invalid.
1457 * It will be removed when the reference count
1458 * drops to zero.
1459 */
6d2010ae 1460 lck_mtx_lock(cp->gss_clnt_mtx);
2d21ac55
A
1461 if (error)
1462 cp->gss_clnt_flags |= GSS_CTX_INVAL;
1463
1464 /*
1465 * Wake any threads waiting to use the context
1466 */
2d21ac55
A
1467 cp->gss_clnt_thread = NULL;
1468 if (cp->gss_clnt_flags & GSS_NEEDCTX) {
1469 cp->gss_clnt_flags &= ~GSS_NEEDCTX;
1470 wakeup(cp);
1471 }
1472 lck_mtx_unlock(cp->gss_clnt_mtx);
1473
39037602 1474 NFS_GSS_DBG("Returning error = %d\n", error);
2d21ac55
A
1475 return (error);
1476}
1477
6d2010ae
A
1478/*
1479 * This function calls nfs_gss_clnt_ctx_init() to set up a new context.
1480 * But if there's a failure in trying to establish the context it keeps
1481 * retrying at progressively longer intervals in case the failure is
1482 * due to some transient condition. For instance, the server might be
1483 * failing the context setup because directory services is not coming
1484 * up in a timely fashion.
1485 */
1486static int
1487nfs_gss_clnt_ctx_init_retry(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp)
1488{
1489 struct nfsmount *nmp = req->r_nmp;
1490 struct timeval now;
1491 time_t waituntil;
1492 int error, slpflag;
1493 int retries = 0;
1494 int timeo = NFS_TRYLATERDEL;
1495
fe8ab488 1496 if (nfs_mount_gone(nmp)) {
6d2010ae
A
1497 error = ENXIO;
1498 goto bad;
1499 }
1500
1501 /* For an "intr" mount allow a signal to interrupt the retries */
1502 slpflag = (NMFLAG(nmp, INTR) && !(req->r_flags & R_NOINTR)) ? PCATCH : 0;
1503
1504 while ((error = nfs_gss_clnt_ctx_init(req, cp)) == ENEEDAUTH) {
1505 microuptime(&now);
1506 waituntil = now.tv_sec + timeo;
1507 while (now.tv_sec < waituntil) {
39236c6e 1508 tsleep(NULL, PSOCK | slpflag, "nfs_gss_clnt_ctx_init_retry", hz);
6d2010ae
A
1509 slpflag = 0;
1510 error = nfs_sigintr(req->r_nmp, req, current_thread(), 0);
1511 if (error)
1512 goto bad;
1513 microuptime(&now);
1514 }
1515
1516 retries++;
1517 /* If it's a soft mount just give up after a while */
fe8ab488 1518 if ((NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT)) && (retries > nmp->nm_retry)) {
6d2010ae
A
1519 error = ETIMEDOUT;
1520 goto bad;
1521 }
1522 timeo *= 2;
1523 if (timeo > 60)
1524 timeo = 60;
1525 }
1526
1527 if (error == 0)
1528 return 0; // success
1529bad:
1530 /*
1531 * Give up on this context
1532 */
1533 lck_mtx_lock(cp->gss_clnt_mtx);
1534 cp->gss_clnt_flags |= GSS_CTX_INVAL;
1535
1536 /*
1537 * Wake any threads waiting to use the context
1538 */
1539 cp->gss_clnt_thread = NULL;
1540 if (cp->gss_clnt_flags & GSS_NEEDCTX) {
1541 cp->gss_clnt_flags &= ~GSS_NEEDCTX;
1542 wakeup(cp);
1543 }
1544 lck_mtx_unlock(cp->gss_clnt_mtx);
1545
1546 return error;
1547}
1548
2d21ac55
A
1549/*
1550 * Call the NFS server using a null procedure for context setup.
1551 * Even though it's a null procedure and nominally has no arguments
1552 * RFC 2203 requires that the GSS-API token be passed as an argument
1553 * and received as a reply.
1554 */
1555static int
1556nfs_gss_clnt_ctx_callserver(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp)
1557{
2d21ac55
A
1558 struct nfsm_chain nmreq, nmrep;
1559 int error = 0, status;
fe8ab488 1560 uint32_t major = cp->gss_clnt_major, minor = cp->gss_clnt_minor;
2d21ac55
A
1561 int sz;
1562
fe8ab488 1563 if (nfs_mount_gone(req->r_nmp))
b0d623f7 1564 return (ENXIO);
2d21ac55
A
1565 nfsm_chain_null(&nmreq);
1566 nfsm_chain_null(&nmrep);
1567 sz = NFSX_UNSIGNED + nfsm_rndup(cp->gss_clnt_tokenlen);
1568 nfsm_chain_build_alloc_init(error, &nmreq, sz);
1569 nfsm_chain_add_32(error, &nmreq, cp->gss_clnt_tokenlen);
b0d623f7
A
1570 if (cp->gss_clnt_tokenlen > 0)
1571 nfsm_chain_add_opaque(error, &nmreq, cp->gss_clnt_token, cp->gss_clnt_tokenlen);
2d21ac55
A
1572 nfsm_chain_build_done(error, &nmreq);
1573 if (error)
1574 goto nfsmout;
1575
1576 /* Call the server */
b0d623f7
A
1577 error = nfs_request_gss(req->r_nmp->nm_mountp, &nmreq, req->r_thread, req->r_cred,
1578 (req->r_flags & R_OPTMASK), cp, &nmrep, &status);
2d21ac55
A
1579 if (cp->gss_clnt_token != NULL) {
1580 FREE(cp->gss_clnt_token, M_TEMP);
1581 cp->gss_clnt_token = NULL;
1582 }
1583 if (!error)
1584 error = status;
1585 if (error)
1586 goto nfsmout;
1587
1588 /* Get the server's reply */
1589
1590 nfsm_chain_get_32(error, &nmrep, cp->gss_clnt_handle_len);
b0d623f7 1591 if (cp->gss_clnt_handle != NULL) {
2d21ac55 1592 FREE(cp->gss_clnt_handle, M_TEMP);
b0d623f7
A
1593 cp->gss_clnt_handle = NULL;
1594 }
2d21ac55
A
1595 if (cp->gss_clnt_handle_len > 0) {
1596 MALLOC(cp->gss_clnt_handle, u_char *, cp->gss_clnt_handle_len, M_TEMP, M_WAITOK);
1597 if (cp->gss_clnt_handle == NULL) {
1598 error = ENOMEM;
1599 goto nfsmout;
1600 }
1601 nfsm_chain_get_opaque(error, &nmrep, cp->gss_clnt_handle_len, cp->gss_clnt_handle);
1602 }
1603 nfsm_chain_get_32(error, &nmrep, cp->gss_clnt_major);
1604 nfsm_chain_get_32(error, &nmrep, cp->gss_clnt_minor);
1605 nfsm_chain_get_32(error, &nmrep, cp->gss_clnt_seqwin);
1606 nfsm_chain_get_32(error, &nmrep, cp->gss_clnt_tokenlen);
1607 if (error)
1608 goto nfsmout;
1609 if (cp->gss_clnt_tokenlen > 0) {
1610 MALLOC(cp->gss_clnt_token, u_char *, cp->gss_clnt_tokenlen, M_TEMP, M_WAITOK);
1611 if (cp->gss_clnt_token == NULL) {
1612 error = ENOMEM;
1613 goto nfsmout;
1614 }
1615 nfsm_chain_get_opaque(error, &nmrep, cp->gss_clnt_tokenlen, cp->gss_clnt_token);
1616 }
1617
1618 /*
1619 * Make sure any unusual errors are expanded and logged by gssd
1620 */
1621 if (cp->gss_clnt_major != GSS_S_COMPLETE &&
1622 cp->gss_clnt_major != GSS_S_CONTINUE_NEEDED) {
2d21ac55 1623
fe8ab488
A
1624 printf("nfs_gss_clnt_ctx_callserver: gss_clnt_major = %d\n", cp->gss_clnt_major);
1625 nfs_gss_clnt_log_error(req, cp, major, minor);
1626
2d21ac55
A
1627 }
1628
1629nfsmout:
1630 nfsm_chain_cleanup(&nmreq);
1631 nfsm_chain_cleanup(&nmrep);
1632
1633 return (error);
1634}
1635
1636/*
39236c6e
A
1637 * We construct the service principal as a gss hostbased service principal of
1638 * the form nfs@<server>, unless the servers principal was passed down in the
1639 * mount arguments. If the arguments don't specify the service principal, the
1640 * server name is extracted the location passed in the mount argument if
1641 * available. Otherwise assume a format of <server>:<path> in the
1642 * mntfromname. We don't currently support url's or other bizarre formats like
1643 * path@server. Mount_url will convert the nfs url into <server>:<path> when
1644 * calling mount, so this works out well in practice.
1645 *
2d21ac55 1646 */
39236c6e
A
1647
1648static uint8_t *
1649nfs_gss_clnt_svcname(struct nfsmount *nmp, gssd_nametype *nt, uint32_t *len)
2d21ac55 1650{
39236c6e
A
1651 char *svcname, *d, *server;
1652 int lindx, sindx;
2d21ac55 1653
fe8ab488 1654 if (nfs_mount_gone(nmp))
b0d623f7 1655 return (NULL);
2d21ac55 1656
39236c6e
A
1657 if (nmp->nm_sprinc) {
1658 *len = strlen(nmp->nm_sprinc) + 1;
1659 MALLOC(svcname, char *, *len, M_TEMP, M_WAITOK);
1660 *nt = GSSD_HOSTBASED;
1661 if (svcname == NULL)
1662 return (NULL);
1663 strlcpy(svcname, nmp->nm_sprinc, *len);
1664
1665 return ((uint8_t *)svcname);
1666 }
1667
1668 *nt = GSSD_HOSTBASED;
1669 if (nmp->nm_locations.nl_numlocs && !(NFS_GSS_ISDBG && (NFS_DEBUG_FLAGS & 0x1))) {
1670 lindx = nmp->nm_locations.nl_current.nli_loc;
1671 sindx = nmp->nm_locations.nl_current.nli_serv;
1672 server = nmp->nm_locations.nl_locations[lindx]->nl_servers[sindx]->ns_name;
1673 *len = (uint32_t)strlen(server);
1674 } else {
1675 /* Older binaries using older mount args end up here */
1676 server = vfs_statfs(nmp->nm_mountp)->f_mntfromname;
1677 NFS_GSS_DBG("nfs getting gss svcname from %s\n", server);
1678 d = strchr(server, ':');
1679 *len = (uint32_t)(d ? (d - server) : strlen(server));
1680 }
1681
1682 *len += 5; /* "nfs@" plus null */
1683 MALLOC(svcname, char *, *len, M_TEMP, M_WAITOK);
1684 strlcpy(svcname, "nfs", *len);
1685 strlcat(svcname, "@", *len);
1686 strlcat(svcname, server, *len);
1687 NFS_GSS_DBG("nfs svcname = %s\n", svcname);
1688
1689 return ((uint8_t *)svcname);
2d21ac55
A
1690}
1691
316670eb
A
1692/*
1693 * Get a mach port to talk to gssd.
1694 * gssd lives in the root bootstrap, so we call gssd's lookup routine
1695 * to get a send right to talk to a new gssd instance that launchd has launched
1696 * based on the cred's uid and audit session id.
1697 */
316670eb
A
1698
1699static mach_port_t
1700nfs_gss_clnt_get_upcall_port(kauth_cred_t credp)
1701{
1702 mach_port_t gssd_host_port, uc_port = IPC_PORT_NULL;
1703 kern_return_t kr;
1704 au_asid_t asid;
1705 uid_t uid;
1706
1707 kr = host_get_gssd_port(host_priv_self(), &gssd_host_port);
1708 if (kr != KERN_SUCCESS) {
1709 printf("nfs_gss_get_upcall_port: can't get gssd port, status %x (%d)\n", kr, kr);
1710 return (IPC_PORT_NULL);
1711 }
1712 if (!IPC_PORT_VALID(gssd_host_port)) {
1713 printf("nfs_gss_get_upcall_port: gssd port not valid\n");
1714 return (IPC_PORT_NULL);
1715 }
1716
1717 asid = kauth_cred_getasid(credp);
1718 uid = kauth_cred_getauid(credp);
1719 if (uid == AU_DEFAUDITID)
1720 uid = kauth_cred_getuid(credp);
1721 kr = mach_gss_lookup(gssd_host_port, uid, asid, &uc_port);
1722 if (kr != KERN_SUCCESS)
1723 printf("nfs_gss_clnt_get_upcall_port: mach_gssd_lookup failed: status %x (%d)\n", kr, kr);
3e170ce0 1724 host_release_special_port(gssd_host_port);
316670eb
A
1725
1726 return (uc_port);
1727}
1728
fe8ab488
A
1729
1730static void
1731nfs_gss_clnt_log_error(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp, uint32_t major, uint32_t minor)
1732{
1733#define GETMAJERROR(x) (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)
1734 struct nfsmount *nmp = req->r_nmp;
1735 char who[] = "client";
1736 uint32_t gss_error = GETMAJERROR(cp->gss_clnt_major);
1737 const char *procn = "unkown";
1738 proc_t proc;
1739 pid_t pid = -1;
1740 struct timeval now;
1741
1742 if (req->r_thread) {
1743 proc = (proc_t)get_bsdthreadtask_info(req->r_thread);
1744 if (proc != NULL && (proc->p_fd == NULL || (proc->p_lflag & P_LVFORK)))
1745 proc = NULL;
1746 if (proc) {
1747 if (*proc->p_comm)
1748 procn = proc->p_comm;
1749 pid = proc->p_pid;
1750 }
1751 } else {
1752 procn = "kernproc";
1753 pid = 0;
1754 }
1755
1756 microuptime(&now);
1757 if ((cp->gss_clnt_major != major || cp->gss_clnt_minor != minor ||
1758 cp->gss_clnt_ptime + GSS_PRINT_DELAY < now.tv_sec) &&
1759 (nmp->nm_state & NFSSTA_MOUNTED)) {
1760 /*
1761 * Will let gssd do some logging in hopes that it can translate
1762 * the minor code.
1763 */
1764 if (cp->gss_clnt_minor && cp->gss_clnt_minor != minor) {
1765 (void) mach_gss_log_error(
1766 cp->gss_clnt_mport,
1767 vfs_statfs(nmp->nm_mountp)->f_mntfromname,
1768 kauth_cred_getuid(cp->gss_clnt_cred),
1769 who,
1770 cp->gss_clnt_major,
1771 cp->gss_clnt_minor);
1772 }
1773 gss_error = gss_error ? gss_error : cp->gss_clnt_major;
1774
1775 /*
1776 *%%% It would be really nice to get the terminal from the proc or auditinfo_addr struct and print that here.
1777 */
1778 printf("NFS: gssd auth failure by %s on audit session %d uid %d proc %s/%d for mount %s. Error: major = %d minor = %d\n",
1779 cp->gss_clnt_display ? cp->gss_clnt_display : who, kauth_cred_getasid(req->r_cred), kauth_cred_getuid(req->r_cred),
1780 procn, pid, vfs_statfs(nmp->nm_mountp)->f_mntfromname, gss_error, (int32_t)cp->gss_clnt_minor);
1781 cp->gss_clnt_ptime = now.tv_sec;
1782 switch (gss_error) {
1783 case 7: printf("NFS: gssd does not have credentials for session %d/%d, (kinit)?\n",
1784 kauth_cred_getasid(req->r_cred), kauth_cred_getauid(req->r_cred));
1785 break;
1786 case 11: printf("NFS: gssd has expired credentals for session %d/%d, (kinit)?\n",
1787 kauth_cred_getasid(req->r_cred), kauth_cred_getauid(req->r_cred));
1788 break;
1789 }
1790 } else {
1791 NFS_GSS_DBG("NFS: gssd auth failure by %s on audit session %d uid %d proc %s/%d for mount %s. Error: major = %d minor = %d\n",
1792 cp->gss_clnt_display ? cp->gss_clnt_display : who, kauth_cred_getasid(req->r_cred), kauth_cred_getuid(req->r_cred),
1793 procn, pid, vfs_statfs(nmp->nm_mountp)->f_mntfromname, gss_error, (int32_t)cp->gss_clnt_minor);
1794 }
1795}
1796
2d21ac55
A
1797/*
1798 * Make an upcall to the gssd using Mach RPC
316670eb 1799 * The upcall is made using a host special port.
2d21ac55
A
1800 * This allows launchd to fire up the gssd in the
1801 * user's session. This is important, since gssd
1802 * must have access to the user's credential cache.
1803 */
1804static int
39037602 1805nfs_gss_clnt_gssd_upcall(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp, uint32_t retrycnt)
2d21ac55
A
1806{
1807 kern_return_t kr;
39037602
A
1808 gssd_byte_buffer octx = NULL;
1809 uint32_t lucidlen = 0;
1810 void *lucid_ctx_buffer;
2d21ac55
A
1811 int retry_cnt = 0;
1812 vm_map_copy_t itoken = NULL;
6d2010ae 1813 gssd_byte_buffer otoken = NULL;
b0d623f7 1814 mach_msg_type_number_t otokenlen;
2d21ac55 1815 int error = 0;
39236c6e
A
1816 uint8_t *principal = NULL;
1817 uint32_t plen = 0;
1818 int32_t nt = GSSD_STRING_NAME;
1819 vm_map_copy_t pname = NULL;
1820 vm_map_copy_t svcname = NULL;
1821 char display_name[MAX_DISPLAY_STR] = "";
b0d623f7 1822 uint32_t ret_flags;
39037602 1823 struct nfsmount *nmp = req->r_nmp;
fe8ab488 1824 uint32_t major = cp->gss_clnt_major, minor = cp->gss_clnt_minor;
39037602
A
1825 uint32_t selected = (uint32_t)-1;
1826 struct nfs_etype etype;
1827
1828 if (nmp == NULL || vfs_isforce(nmp->nm_mountp) || (nmp->nm_state & (NFSSTA_FORCE | NFSSTA_DEAD)))
1829 return (ENXIO);
1830
1831 if (cp->gss_clnt_gssd_flags & GSSD_RESTART) {
1832 if (cp->gss_clnt_token)
1833 FREE(cp->gss_clnt_token, M_TEMP);
1834 cp->gss_clnt_token = NULL;
1835 cp->gss_clnt_tokenlen = 0;
1836 cp->gss_clnt_proc = RPCSEC_GSS_INIT;
1837 }
1838
1839 NFS_GSS_DBG("Retrycnt = %d nm_etype.count = %d\n", retrycnt, nmp->nm_etype.count);
1840 if (retrycnt >= nmp->nm_etype.count)
1841 return (EACCES);
1842
1843 /* Copy the mount etypes to an order set of etypes to try */
1844 etype = nmp->nm_etype;
1845
1846 /*
1847 * If we've already selected an etype, lets put that first in our
1848 * array of etypes to try, since overwhelmingly, that is likely
1849 * to be the etype we want.
1850 */
1851 if (etype.selected < etype.count) {
1852 etype.etypes[0] = nmp->nm_etype.etypes[etype.selected];
1853 for (uint32_t i = 0; i < etype.selected; i++)
1854 etype.etypes[i+1] = nmp->nm_etype.etypes[i];
1855 for (uint32_t i = etype.selected + 1; i < etype.count; i++)
1856 etype.etypes[i] = nmp->nm_etype.etypes[i];
1857 }
1858
1859 /* Remove the ones we've already have tried */
1860 for (uint32_t i = retrycnt; i < etype.count; i++)
1861 etype.etypes[i - retrycnt] = etype.etypes[i];
1862 etype.count = etype.count - retrycnt;
1863
1864 NFS_GSS_DBG("etype count = %d preferred etype = %d\n", etype.count, etype.etypes[0]);
1865
1866 /*
1867 * NFS currently only supports default principals or
1868 * principals based on the uid of the caller, unless
1869 * the principal to use for the mounting cred was specified
1870 * in the mount argmuments. If the realm to use was specified
1871 * then will send that up as the principal since the realm is
39236c6e
A
1872 * preceed by an "@" gssd that will try and select the default
1873 * principal for that realm.
2d21ac55 1874 */
39236c6e 1875
39236c6e
A
1876 if (cp->gss_clnt_principal && cp->gss_clnt_prinlen) {
1877 principal = cp->gss_clnt_principal;
1878 plen = cp->gss_clnt_prinlen;
1879 nt = cp->gss_clnt_prinnt;
1880 } else if (nmp->nm_principal && IS_VALID_CRED(nmp->nm_mcred) && req->r_cred == nmp->nm_mcred) {
1881 plen = (uint32_t)strlen(nmp->nm_principal);
1882 MALLOC(principal, uint8_t *, plen, M_TEMP, M_WAITOK | M_ZERO);
1883 if (principal == NULL)
1884 return (ENOMEM);
1885 bcopy(nmp->nm_principal, principal, plen);
1886 cp->gss_clnt_prinnt = nt = GSSD_USER;
1887 }
1888 else if (nmp->nm_realm) {
1889 plen = (uint32_t)strlen(nmp->nm_realm);
1890 principal = (uint8_t *)nmp->nm_realm;
1891 nt = GSSD_USER;
1892 }
1893
6d2010ae 1894 if (!IPC_PORT_VALID(cp->gss_clnt_mport)) {
316670eb
A
1895 cp->gss_clnt_mport = nfs_gss_clnt_get_upcall_port(req->r_cred);
1896 if (cp->gss_clnt_mport == IPC_PORT_NULL)
b0d623f7 1897 goto out;
2d21ac55
A
1898 }
1899
39236c6e
A
1900 if (plen)
1901 nfs_gss_mach_alloc_buffer(principal, plen, &pname);
1902 if (cp->gss_clnt_svcnamlen)
1903 nfs_gss_mach_alloc_buffer(cp->gss_clnt_svcname, cp->gss_clnt_svcnamlen, &svcname);
1904 if (cp->gss_clnt_tokenlen)
2d21ac55
A
1905 nfs_gss_mach_alloc_buffer(cp->gss_clnt_token, cp->gss_clnt_tokenlen, &itoken);
1906
39037602
A
1907 /* Always want to export the lucid context */
1908 cp->gss_clnt_gssd_flags |= GSSD_LUCID_CONTEXT;
1909
2d21ac55 1910retry:
39037602 1911 kr = mach_gss_init_sec_context_v3(
2d21ac55 1912 cp->gss_clnt_mport,
6d2010ae
A
1913 GSSD_KRB5_MECH,
1914 (gssd_byte_buffer) itoken, (mach_msg_type_number_t) cp->gss_clnt_tokenlen,
39236c6e
A
1915 kauth_cred_getuid(cp->gss_clnt_cred),
1916 nt,
1917 (gssd_byte_buffer)pname, (mach_msg_type_number_t) plen,
1918 cp->gss_clnt_svcnt,
1919 (gssd_byte_buffer)svcname, (mach_msg_type_number_t) cp->gss_clnt_svcnamlen,
b0d623f7 1920 GSSD_MUTUAL_FLAG,
39037602 1921 (gssd_etype_list)etype.etypes, (mach_msg_type_number_t)etype.count,
39236c6e 1922 &cp->gss_clnt_gssd_flags,
2d21ac55
A
1923 &cp->gss_clnt_context,
1924 &cp->gss_clnt_cred_handle,
b0d623f7 1925 &ret_flags,
39037602 1926 &octx, (mach_msg_type_number_t *) &lucidlen,
b0d623f7 1927 &otoken, &otokenlen,
39236c6e 1928 cp->gss_clnt_display ? NULL : display_name,
2d21ac55
A
1929 &cp->gss_clnt_major,
1930 &cp->gss_clnt_minor);
1931
39037602 1932 /* Clear the RESTART flag */
b0d623f7 1933 cp->gss_clnt_gssd_flags &= ~GSSD_RESTART;
39037602
A
1934 if (cp->gss_clnt_major != GSS_S_CONTINUE_NEEDED) {
1935 /* We're done with the gssd handles */
1936 cp->gss_clnt_context = 0;
1937 cp->gss_clnt_cred_handle = 0;
1938 }
39236c6e 1939
b0d623f7
A
1940 if (kr != KERN_SUCCESS) {
1941 printf("nfs_gss_clnt_gssd_upcall: mach_gss_init_sec_context failed: %x (%d)\n", kr, kr);
2d21ac55 1942 if (kr == MIG_SERVER_DIED && cp->gss_clnt_cred_handle == 0 &&
39236c6e
A
1943 retry_cnt++ < NFS_GSS_MACH_MAX_RETRIES &&
1944 !vfs_isforce(nmp->nm_mountp) && (nmp->nm_state & (NFSSTA_FORCE | NFSSTA_DEAD)) == 0) {
1945 if (plen)
1946 nfs_gss_mach_alloc_buffer(principal, plen, &pname);
1947 if (cp->gss_clnt_svcnamlen)
1948 nfs_gss_mach_alloc_buffer(cp->gss_clnt_svcname, cp->gss_clnt_svcnamlen, &svcname);
b0d623f7
A
1949 if (cp->gss_clnt_tokenlen > 0)
1950 nfs_gss_mach_alloc_buffer(cp->gss_clnt_token, cp->gss_clnt_tokenlen, &itoken);
2d21ac55 1951 goto retry;
b0d623f7 1952 }
316670eb
A
1953
1954 host_release_special_port(cp->gss_clnt_mport);
1955 cp->gss_clnt_mport = IPC_PORT_NULL;
b0d623f7 1956 goto out;
2d21ac55
A
1957 }
1958
39236c6e
A
1959 if (cp->gss_clnt_display == NULL && *display_name != '\0') {
1960 int dlen = strnlen(display_name, MAX_DISPLAY_STR) + 1; /* Add extra byte to include '\0' */
39037602 1961
39236c6e
A
1962 if (dlen < MAX_DISPLAY_STR) {
1963 MALLOC(cp->gss_clnt_display, char *, dlen, M_TEMP, M_WAITOK);
1964 if (cp->gss_clnt_display == NULL)
1965 goto skip;
1966 bcopy(display_name, cp->gss_clnt_display, dlen);
1967 } else {
1968 goto skip;
1969 }
1970 }
1971skip:
2d21ac55
A
1972 /*
1973 * Make sure any unusual errors are expanded and logged by gssd
39236c6e
A
1974 *
1975 * XXXX, we need to rethink this and just have gssd return a string for the major and minor codes.
2d21ac55
A
1976 */
1977 if (cp->gss_clnt_major != GSS_S_COMPLETE &&
1978 cp->gss_clnt_major != GSS_S_CONTINUE_NEEDED) {
39037602 1979 NFS_GSS_DBG("Up call returned error\n");
fe8ab488 1980 nfs_gss_clnt_log_error(req, cp, major, minor);
2d21ac55
A
1981 }
1982
39037602
A
1983 if (lucidlen > 0) {
1984 if (lucidlen > MAX_LUCIDLEN) {
1985 printf("nfs_gss_clnt_gssd_upcall: bad context length (%d)\n", lucidlen);
1986 vm_map_copy_discard((vm_map_copy_t) octx);
b0d623f7
A
1987 vm_map_copy_discard((vm_map_copy_t) otoken);
1988 goto out;
1989 }
39037602
A
1990 MALLOC(lucid_ctx_buffer, void *, lucidlen, M_TEMP, M_WAITOK | M_ZERO);
1991 error = nfs_gss_mach_vmcopyout((vm_map_copy_t) octx, lucidlen, lucid_ctx_buffer);
b0d623f7
A
1992 if (error) {
1993 vm_map_copy_discard((vm_map_copy_t) otoken);
1994 goto out;
2d21ac55 1995 }
39037602
A
1996
1997 if (cp->gss_clnt_ctx_id)
1998 gss_krb5_destroy_context(cp->gss_clnt_ctx_id);
1999 cp->gss_clnt_ctx_id = gss_krb5_make_context(lucid_ctx_buffer, lucidlen);
2000 if (cp->gss_clnt_ctx_id == NULL) {
2001 printf("Failed to make context from lucid_ctx_buffer\n");
b0d623f7 2002 goto out;
39037602
A
2003 }
2004 for (uint32_t i = 0; i < nmp->nm_etype.count; i++) {
2005 if (nmp->nm_etype.etypes[i] == cp->gss_clnt_ctx_id->gss_cryptor.etype) {
2006 selected = i;
2007 break;
2008 }
2009 }
2d21ac55
A
2010 }
2011
b0d623f7
A
2012 /* Free context token used as input */
2013 if (cp->gss_clnt_token)
2014 FREE(cp->gss_clnt_token, M_TEMP);
2015 cp->gss_clnt_token = NULL;
2016 cp->gss_clnt_tokenlen = 0;
2017
2018 if (otokenlen > 0) {
2019 /* Set context token to gss output token */
2020 MALLOC(cp->gss_clnt_token, u_char *, otokenlen, M_TEMP, M_WAITOK);
2021 if (cp->gss_clnt_token == NULL) {
2022 printf("nfs_gss_clnt_gssd_upcall: could not allocate %d bytes\n", otokenlen);
2023 vm_map_copy_discard((vm_map_copy_t) otoken);
2d21ac55 2024 return (ENOMEM);
b0d623f7
A
2025 }
2026 error = nfs_gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_clnt_token);
2027 if (error) {
39037602 2028 printf("Could not copyout gss token\n");
b0d623f7
A
2029 FREE(cp->gss_clnt_token, M_TEMP);
2030 cp->gss_clnt_token = NULL;
2031 return (NFSERR_EAUTH);
2032 }
2033 cp->gss_clnt_tokenlen = otokenlen;
2d21ac55
A
2034 }
2035
39037602
A
2036 if (selected != (uint32_t)-1) {
2037 nmp->nm_etype.selected = selected;
2038 NFS_GSS_DBG("etype selected = %d\n", nmp->nm_etype.etypes[selected]);
2039 }
2040 NFS_GSS_DBG("Up call succeeded major = %d\n", cp->gss_clnt_major);
2d21ac55 2041 return (0);
b0d623f7
A
2042
2043out:
2044 if (cp->gss_clnt_token)
2045 FREE(cp->gss_clnt_token, M_TEMP);
2046 cp->gss_clnt_token = NULL;
2047 cp->gss_clnt_tokenlen = 0;
2048
39037602 2049 NFS_GSS_DBG("Up call returned NFSERR_EAUTH");
b0d623f7 2050 return (NFSERR_EAUTH);
2d21ac55
A
2051}
2052
2053/*
2054 * Invoked at the completion of an RPC call that uses an RPCSEC_GSS
2055 * credential. The sequence number window that the server returns
2056 * at context setup indicates the maximum number of client calls that
2057 * can be outstanding on a context. The client maintains a bitmap that
2058 * represents the server's window. Each pending request has a bit set
2059 * in the window bitmap. When a reply comes in or times out, we reset
2060 * the bit in the bitmap and if there are any other threads waiting for
2061 * a context slot we notify the waiting thread(s).
2062 *
2063 * Note that if a request is retransmitted, it will have a single XID
2064 * but it may be associated with multiple sequence numbers. So we
2065 * may have to reset multiple sequence number bits in the window bitmap.
2066 */
2067void
2068nfs_gss_clnt_rpcdone(struct nfsreq *req)
2069{
2070 struct nfs_gss_clnt_ctx *cp = req->r_gss_ctx;
2071 struct gss_seq *gsp, *ngsp;
2072 int i = 0;
2073
2074 if (cp == NULL || !(cp->gss_clnt_flags & GSS_CTX_COMPLETE))
2075 return; // no context - don't bother
2076 /*
2077 * Reset the bit for this request in the
2078 * sequence number window to indicate it's done.
2079 * We do this even if the request timed out.
2080 */
2081 lck_mtx_lock(cp->gss_clnt_mtx);
2082 gsp = SLIST_FIRST(&req->r_gss_seqlist);
2083 if (gsp && gsp->gss_seqnum > (cp->gss_clnt_seqnum - cp->gss_clnt_seqwin))
2084 win_resetbit(cp->gss_clnt_seqbits,
2085 gsp->gss_seqnum % cp->gss_clnt_seqwin);
2086
2087 /*
2088 * Limit the seqnum list to GSS_CLNT_SEQLISTMAX entries
2089 */
2090 SLIST_FOREACH_SAFE(gsp, &req->r_gss_seqlist, gss_seqnext, ngsp) {
2091 if (++i > GSS_CLNT_SEQLISTMAX) {
2092 SLIST_REMOVE(&req->r_gss_seqlist, gsp, gss_seq, gss_seqnext);
2093 FREE(gsp, M_TEMP);
2094 }
2095 }
2096
2097 /*
2098 * If there's a thread waiting for
2099 * the window to advance, wake it up.
2100 */
2101 if (cp->gss_clnt_flags & GSS_NEEDSEQ) {
2102 cp->gss_clnt_flags &= ~GSS_NEEDSEQ;
2103 wakeup(cp);
2104 }
2105 lck_mtx_unlock(cp->gss_clnt_mtx);
2106}
2107
2108/*
2109 * Create a reference to a context from a request
2110 * and bump the reference count
2111 */
2112void
2113nfs_gss_clnt_ctx_ref(struct nfsreq *req, struct nfs_gss_clnt_ctx *cp)
2114{
2115 req->r_gss_ctx = cp;
2116
2117 lck_mtx_lock(cp->gss_clnt_mtx);
2118 cp->gss_clnt_refcnt++;
2119 lck_mtx_unlock(cp->gss_clnt_mtx);
2120}
2121
2122/*
2123 * Remove a context reference from a request
2124 * If the reference count drops to zero, and the
2125 * context is invalid, destroy the context
2126 */
2127void
2128nfs_gss_clnt_ctx_unref(struct nfsreq *req)
2129{
2130 struct nfsmount *nmp = req->r_nmp;
2131 struct nfs_gss_clnt_ctx *cp = req->r_gss_ctx;
fe8ab488 2132 int on_neg_cache = 0;
3e170ce0 2133 int neg_cache = 0;
fe8ab488 2134 int destroy = 0;
3e170ce0
A
2135 struct timeval now;
2136 char CTXBUF[NFS_CTXBUFSZ];
2d21ac55
A
2137
2138 if (cp == NULL)
2139 return;
2140
2141 req->r_gss_ctx = NULL;
2142
2143 lck_mtx_lock(cp->gss_clnt_mtx);
fe8ab488
A
2144 if (--cp->gss_clnt_refcnt < 0)
2145 panic("Over release of gss context!\n");
2146
3e170ce0
A
2147 if (cp->gss_clnt_refcnt == 0) {
2148 if ((cp->gss_clnt_flags & GSS_CTX_INVAL) &&
39037602
A
2149 cp->gss_clnt_ctx_id) {
2150 gss_krb5_destroy_context(cp->gss_clnt_ctx_id);
2151 cp->gss_clnt_ctx_id = NULL;
3e170ce0
A
2152 }
2153 if (cp->gss_clnt_flags & GSS_CTX_DESTROY) {
2154 destroy = 1;
2155 if (cp->gss_clnt_flags & GSS_CTX_STICKY)
2156 nfs_gss_clnt_mnt_rele(nmp);
2157 if (cp->gss_clnt_nctime)
2158 on_neg_cache = 1;
2159 }
2160 }
2161 if (!destroy && cp->gss_clnt_nctime == 0 &&
2162 (cp->gss_clnt_flags & GSS_CTX_INVAL)) {
2163 microuptime(&now);
2164 cp->gss_clnt_nctime = now.tv_sec;
fe8ab488
A
2165 neg_cache = 1;
2166 }
2167 lck_mtx_unlock(cp->gss_clnt_mtx);
2168 if (destroy) {
3e170ce0 2169 NFS_GSS_DBG("Destroying context %s\n", NFS_GSS_CTX(req, cp));
fe8ab488 2170 if (nmp) {
2d21ac55 2171 lck_mtx_lock(&nmp->nm_lock);
fe8ab488 2172 if (cp->gss_clnt_entries.tqe_next != NFSNOLIST) {
3e170ce0
A
2173 TAILQ_REMOVE(&nmp->nm_gsscl, cp, gss_clnt_entries);
2174 }
2175 if (on_neg_cache) {
2176 nmp->nm_ncentries--;
fe8ab488 2177 }
2d21ac55 2178 lck_mtx_unlock(&nmp->nm_lock);
fe8ab488
A
2179 }
2180 nfs_gss_clnt_ctx_destroy(cp);
3e170ce0
A
2181 } else if (neg_cache) {
2182 NFS_GSS_DBG("Entering context %s into negative cache\n", NFS_GSS_CTX(req, cp));
2183 if (nmp) {
2184 lck_mtx_lock(&nmp->nm_lock);
2185 nmp->nm_ncentries++;
2186 nfs_gss_clnt_ctx_neg_cache_reap(nmp);
2187 lck_mtx_unlock(&nmp->nm_lock);
2188 }
2189 }
fe8ab488
A
2190 NFS_GSS_CLNT_CTX_DUMP(nmp);
2191}
2192
2193/*
3e170ce0 2194 * Try and reap any old negative cache entries.
fe8ab488
A
2195 * cache queue.
2196 */
2197void
3e170ce0 2198nfs_gss_clnt_ctx_neg_cache_reap(struct nfsmount *nmp)
fe8ab488 2199{
3e170ce0 2200 struct nfs_gss_clnt_ctx *cp, *tcp;
fe8ab488
A
2201 struct timeval now;
2202 int reaped = 0;
2d21ac55 2203
fe8ab488 2204 /* Try and reap old, unreferenced, expired contexts */
39037602
A
2205 microuptime(&now);
2206
2207 NFS_GSS_DBG("Reaping contexts ncentries = %d\n", nmp->nm_ncentries);
3e170ce0
A
2208
2209 TAILQ_FOREACH_SAFE(cp, &nmp->nm_gsscl, gss_clnt_entries, tcp) {
fe8ab488
A
2210 int destroy = 0;
2211
3e170ce0
A
2212 /* Don't reap STICKY contexts */
2213 if ((cp->gss_clnt_flags & GSS_CTX_STICKY) ||
2214 !(cp->gss_clnt_flags & GSS_CTX_INVAL))
2215 continue;
fe8ab488
A
2216 /* Keep up to GSS_MAX_NEG_CACHE_ENTRIES */
2217 if (nmp->nm_ncentries <= GSS_MAX_NEG_CACHE_ENTRIES)
2218 break;
3e170ce0
A
2219 /* Contexts too young */
2220 if (cp->gss_clnt_nctime + GSS_NEG_CACHE_TO >= now.tv_sec)
2221 continue;
fe8ab488 2222 /* Not referenced, remove it. */
3e170ce0
A
2223 lck_mtx_lock(cp->gss_clnt_mtx);
2224 if (cp->gss_clnt_refcnt == 0) {
2225 cp->gss_clnt_flags |= GSS_CTX_DESTROY;
fe8ab488
A
2226 destroy = 1;
2227 }
3e170ce0
A
2228 lck_mtx_unlock(cp->gss_clnt_mtx);
2229 if (destroy) {
2230 TAILQ_REMOVE(&nmp->nm_gsscl, cp, gss_clnt_entries);
2231 nmp->nm_ncentries++;
2232 reaped++;
2233 nfs_gss_clnt_ctx_destroy(cp);
2234 }
fe8ab488
A
2235 }
2236 NFS_GSS_DBG("Reaped %d contexts ncentries = %d\n", reaped, nmp->nm_ncentries);
fe8ab488
A
2237}
2238
2239/*
2240 * Clean a context to be cached
2241 */
2242static void
2243nfs_gss_clnt_ctx_clean(struct nfs_gss_clnt_ctx *cp)
2244{
3e170ce0
A
2245 /* Preserve gss_clnt_mtx */
2246 assert(cp->gss_clnt_thread == NULL); /* Will be set to this thread */
2247 /* gss_clnt_entries we should not be on any list at this point */
fe8ab488 2248 cp->gss_clnt_flags = 0;
3e170ce0
A
2249 /* gss_clnt_refcnt should be zero */
2250 assert(cp->gss_clnt_refcnt == 0);
2251 /*
2252 * We are who we are preserve:
2253 * gss_clnt_cred
2254 * gss_clnt_principal
2255 * gss_clnt_prinlen
2256 * gss_clnt_prinnt
2257 * gss_clnt_desplay
2258 */
2259 /* gss_clnt_proc will be set in nfs_gss_clnt_ctx_init */
2260 cp->gss_clnt_seqnum = 0;
2261 /* Preserve gss_clnt_service, we're not changing flavors */
fe8ab488
A
2262 if (cp->gss_clnt_handle) {
2263 FREE(cp->gss_clnt_handle, M_TEMP);
2264 cp->gss_clnt_handle = NULL;
2265 }
3e170ce0
A
2266 cp->gss_clnt_handle_len = 0;
2267 cp->gss_clnt_nctime = 0;
2268 cp->gss_clnt_seqwin = 0;
fe8ab488
A
2269 if (cp->gss_clnt_seqbits) {
2270 FREE(cp->gss_clnt_seqbits, M_TEMP);
2271 cp->gss_clnt_seqbits = NULL;
2272 }
3e170ce0
A
2273 /* Preserve gss_clnt_mport. Still talking to the same gssd */
2274 if (cp->gss_clnt_verf) {
2275 FREE(cp->gss_clnt_verf, M_TEMP);
2276 cp->gss_clnt_verf = NULL;
fe8ab488 2277 }
3e170ce0 2278 /* Service name might change on failover, so reset it */
fe8ab488
A
2279 if (cp->gss_clnt_svcname) {
2280 FREE(cp->gss_clnt_svcname, M_TEMP);
2281 cp->gss_clnt_svcname = NULL;
3e170ce0 2282 cp->gss_clnt_svcnt = 0;
fe8ab488 2283 }
3e170ce0
A
2284 cp->gss_clnt_svcnamlen = 0;
2285 cp->gss_clnt_cred_handle = 0;
2286 cp->gss_clnt_context = 0;
2287 if (cp->gss_clnt_token) {
2288 FREE(cp->gss_clnt_token, M_TEMP);
2289 cp->gss_clnt_token = NULL;
2290 }
2291 cp->gss_clnt_tokenlen = 0;
39037602 2292 /* XXX gss_clnt_ctx_id ??? */
3e170ce0
A
2293 /*
2294 * Preserve:
2295 * gss_clnt_gssd_flags
2296 * gss_clnt_major
2297 * gss_clnt_minor
2298 * gss_clnt_ptime
2299 */
2300}
2301
2302/*
2303 * Copy a source context to a new context. This is used to create a new context
2304 * with the identity of the old context for renewal. The old context is invalid
2305 * at this point but may have reference still to it, so it is not safe to use that
2306 * context.
2307 */
2308static int
39037602 2309nfs_gss_clnt_ctx_copy(struct nfs_gss_clnt_ctx *scp, struct nfs_gss_clnt_ctx **dcpp)
3e170ce0
A
2310{
2311 struct nfs_gss_clnt_ctx *dcp;
2312
2313 *dcpp = (struct nfs_gss_clnt_ctx *)NULL;
2314 MALLOC(dcp, struct nfs_gss_clnt_ctx *, sizeof (struct nfs_gss_clnt_ctx), M_TEMP, M_WAITOK);
2315 if (dcp == NULL)
2316 return (ENOMEM);
2317 bzero(dcp, sizeof (struct nfs_gss_clnt_ctx));
3e170ce0
A
2318 dcp->gss_clnt_mtx = lck_mtx_alloc_init(nfs_gss_clnt_grp, LCK_ATTR_NULL);
2319 dcp->gss_clnt_cred = scp->gss_clnt_cred;
2320 kauth_cred_ref(dcp->gss_clnt_cred);
2321 dcp->gss_clnt_prinlen = scp->gss_clnt_prinlen;
2322 dcp->gss_clnt_prinnt = scp->gss_clnt_prinnt;
2323 if (scp->gss_clnt_principal) {
2324 MALLOC(dcp->gss_clnt_principal, uint8_t *, dcp->gss_clnt_prinlen, M_TEMP, M_WAITOK | M_ZERO);
2325 if (dcp->gss_clnt_principal == NULL) {
3e170ce0
A
2326 FREE(dcp, M_TEMP);
2327 return (ENOMEM);
2328 }
2329 bcopy(scp->gss_clnt_principal, dcp->gss_clnt_principal, dcp->gss_clnt_prinlen);
2330 }
2331 /* Note we don't preserve the display name, that will be set by a successful up call */
2332 dcp->gss_clnt_service = scp->gss_clnt_service;
2333 dcp->gss_clnt_mport = host_copy_special_port(scp->gss_clnt_mport);
39037602 2334 dcp->gss_clnt_ctx_id = NULL; /* Will be set from successful upcall */
3e170ce0
A
2335 dcp->gss_clnt_gssd_flags = scp->gss_clnt_gssd_flags;
2336 dcp->gss_clnt_major = scp->gss_clnt_major;
2337 dcp->gss_clnt_minor = scp->gss_clnt_minor;
2338 dcp->gss_clnt_ptime = scp->gss_clnt_ptime;
2339
2340 *dcpp = dcp;
2341
2342 return (0);
2d21ac55
A
2343}
2344
2345/*
2346 * Remove a context
2347 */
2348static void
fe8ab488 2349nfs_gss_clnt_ctx_destroy(struct nfs_gss_clnt_ctx *cp)
2d21ac55 2350{
fe8ab488
A
2351 NFS_GSS_DBG("Destroying context %d/%d\n",
2352 kauth_cred_getasid(cp->gss_clnt_cred),
2353 kauth_cred_getauid(cp->gss_clnt_cred));
2d21ac55 2354
316670eb 2355 host_release_special_port(cp->gss_clnt_mport);
fe8ab488 2356 cp->gss_clnt_mport = IPC_PORT_NULL;
3e170ce0 2357
fe8ab488 2358 if (cp->gss_clnt_mtx) {
2d21ac55 2359 lck_mtx_destroy(cp->gss_clnt_mtx, nfs_gss_clnt_grp);
fe8ab488
A
2360 cp->gss_clnt_mtx = (lck_mtx_t *)NULL;
2361 }
39236c6e
A
2362 if (IS_VALID_CRED(cp->gss_clnt_cred))
2363 kauth_cred_unref(&cp->gss_clnt_cred);
fe8ab488
A
2364 cp->gss_clnt_entries.tqe_next = NFSNOLIST;
2365 cp->gss_clnt_entries.tqe_prev = NFSNOLIST;
2366 if (cp->gss_clnt_principal) {
39236c6e 2367 FREE(cp->gss_clnt_principal, M_TEMP);
fe8ab488
A
2368 cp->gss_clnt_principal = NULL;
2369 }
2370 if (cp->gss_clnt_display) {
39236c6e 2371 FREE(cp->gss_clnt_display, M_TEMP);
fe8ab488
A
2372 cp->gss_clnt_display = NULL;
2373 }
39037602
A
2374 if (cp->gss_clnt_ctx_id) {
2375 gss_krb5_destroy_context(cp->gss_clnt_ctx_id);
2376 cp->gss_clnt_ctx_id = NULL;
3e170ce0
A
2377 }
2378
fe8ab488 2379 nfs_gss_clnt_ctx_clean(cp);
3e170ce0 2380
2d21ac55
A
2381 FREE(cp, M_TEMP);
2382}
2383
2384/*
2385 * The context for a user is invalid.
2386 * Mark the context as invalid, then
2387 * create a new context.
2388 */
2389int
2390nfs_gss_clnt_ctx_renew(struct nfsreq *req)
2391{
2392 struct nfs_gss_clnt_ctx *cp = req->r_gss_ctx;
2d21ac55 2393 struct nfs_gss_clnt_ctx *ncp;
3e170ce0 2394 struct nfsmount *nmp;
2d21ac55 2395 int error = 0;
3e170ce0 2396 char CTXBUF[NFS_CTXBUFSZ];
2d21ac55 2397
b0d623f7 2398 if (cp == NULL)
2d21ac55
A
2399 return (0);
2400
3e170ce0
A
2401 if (req->r_nmp == NULL)
2402 return (ENXIO);
2403 nmp = req->r_nmp;
2404
2d21ac55
A
2405 lck_mtx_lock(cp->gss_clnt_mtx);
2406 if (cp->gss_clnt_flags & GSS_CTX_INVAL) {
2407 lck_mtx_unlock(cp->gss_clnt_mtx);
2408 nfs_gss_clnt_ctx_unref(req);
2409 return (0); // already being renewed
2410 }
2d21ac55 2411
fe8ab488 2412 cp->gss_clnt_flags |= (GSS_CTX_INVAL | GSS_CTX_DESTROY);
2d21ac55 2413
2d21ac55
A
2414 if (cp->gss_clnt_flags & (GSS_NEEDCTX | GSS_NEEDSEQ)) {
2415 cp->gss_clnt_flags &= ~GSS_NEEDSEQ;
2416 wakeup(cp);
2417 }
2418 lck_mtx_unlock(cp->gss_clnt_mtx);
2419
39037602
A
2420 if (cp->gss_clnt_proc == RPCSEC_GSS_DESTROY)
2421 return (EACCES); /* Destroying a context is best effort. Don't renew. */
2422 /*
2423 * If we're setting up a context let nfs_gss_clnt_ctx_init know this is not working
2424 * and to try some other etype.
2425 */
2426 if (cp->gss_clnt_proc != RPCSEC_GSS_DATA)
2427 return (ENEEDAUTH);
2428 error = nfs_gss_clnt_ctx_copy(cp, &ncp);
3e170ce0
A
2429 NFS_GSS_DBG("Renewing context %s\n", NFS_GSS_CTX(req, ncp));
2430 nfs_gss_clnt_ctx_unref(req);
2431 if (error)
2432 return (error);
2433
2434 lck_mtx_lock(&nmp->nm_lock);
2d21ac55 2435 /*
3e170ce0
A
2436 * Note we don't bother taking the new context mutex as we're
2437 * not findable at the moment.
2d21ac55 2438 */
2d21ac55 2439 ncp->gss_clnt_thread = current_thread();
2d21ac55 2440 nfs_gss_clnt_ctx_ref(req, ncp);
3e170ce0
A
2441 TAILQ_INSERT_HEAD(&nmp->nm_gsscl, ncp, gss_clnt_entries);
2442 lck_mtx_unlock(&nmp->nm_lock);
2d21ac55 2443
3e170ce0 2444 error = nfs_gss_clnt_ctx_init_retry(req, ncp); // Initialize new context
2d21ac55
A
2445 if (error)
2446 nfs_gss_clnt_ctx_unref(req);
3e170ce0 2447
2d21ac55
A
2448 return (error);
2449}
2450
fe8ab488 2451
2d21ac55
A
2452/*
2453 * Destroy all the contexts associated with a mount.
2454 * The contexts are also destroyed by the server.
2455 */
2456void
6d2010ae 2457nfs_gss_clnt_ctx_unmount(struct nfsmount *nmp)
2d21ac55
A
2458{
2459 struct nfs_gss_clnt_ctx *cp;
2d21ac55 2460 struct nfsm_chain nmreq, nmrep;
2d21ac55
A
2461 int error, status;
2462 struct nfsreq req;
2d21ac55
A
2463 req.r_nmp = nmp;
2464
fe8ab488
A
2465 if (!nmp)
2466 return;
2467
3e170ce0
A
2468
2469 lck_mtx_lock(&nmp->nm_lock);
2470 while((cp = TAILQ_FIRST(&nmp->nm_gsscl))) {
2471 TAILQ_REMOVE(&nmp->nm_gsscl, cp, gss_clnt_entries);
2472 cp->gss_clnt_entries.tqe_next = NFSNOLIST;
fe8ab488 2473 lck_mtx_lock(cp->gss_clnt_mtx);
3e170ce0
A
2474 if (cp->gss_clnt_flags & GSS_CTX_DESTROY) {
2475 lck_mtx_unlock(cp->gss_clnt_mtx);
2476 continue;
2477 }
fe8ab488
A
2478 cp->gss_clnt_refcnt++;
2479 lck_mtx_unlock(cp->gss_clnt_mtx);
2480 req.r_gss_ctx = cp;
2481
2d21ac55 2482 lck_mtx_unlock(&nmp->nm_lock);
2d21ac55
A
2483 /*
2484 * Tell the server to destroy its context.
fe8ab488 2485 * But don't bother if it's a forced unmount.
2d21ac55 2486 */
3e170ce0
A
2487 if (!nfs_mount_gone(nmp) &&
2488 (cp->gss_clnt_flags & (GSS_CTX_INVAL | GSS_CTX_DESTROY | GSS_CTX_COMPLETE)) == GSS_CTX_COMPLETE) {
2d21ac55
A
2489 cp->gss_clnt_proc = RPCSEC_GSS_DESTROY;
2490
2491 error = 0;
2492 nfsm_chain_null(&nmreq);
2493 nfsm_chain_null(&nmrep);
2494 nfsm_chain_build_alloc_init(error, &nmreq, 0);
2495 nfsm_chain_build_done(error, &nmreq);
2496 if (!error)
b0d623f7 2497 nfs_request_gss(nmp->nm_mountp, &nmreq,
39236c6e 2498 current_thread(), cp->gss_clnt_cred, 0, cp, &nmrep, &status);
2d21ac55
A
2499 nfsm_chain_cleanup(&nmreq);
2500 nfsm_chain_cleanup(&nmrep);
2d21ac55
A
2501 }
2502
2503 /*
2504 * Mark the context invalid then drop
2505 * the reference to remove it if its
2506 * refcount is zero.
2507 */
6d2010ae 2508 lck_mtx_lock(cp->gss_clnt_mtx);
fe8ab488 2509 cp->gss_clnt_flags |= (GSS_CTX_INVAL | GSS_CTX_DESTROY);
6d2010ae 2510 lck_mtx_unlock(cp->gss_clnt_mtx);
2d21ac55 2511 nfs_gss_clnt_ctx_unref(&req);
fe8ab488 2512 lck_mtx_lock(&nmp->nm_lock);
fe8ab488 2513 }
3e170ce0
A
2514 lck_mtx_unlock(&nmp->nm_lock);
2515 assert(TAILQ_EMPTY(&nmp->nm_gsscl));
2d21ac55
A
2516}
2517
3e170ce0 2518
39236c6e 2519/*
fe8ab488 2520 * Removes a mounts context for a credential
39236c6e
A
2521 */
2522int
fe8ab488 2523nfs_gss_clnt_ctx_remove(struct nfsmount *nmp, kauth_cred_t cred)
39236c6e
A
2524{
2525 struct nfs_gss_clnt_ctx *cp;
2526 struct nfsreq req;
2527
2528 req.r_nmp = nmp;
2529
fe8ab488
A
2530 NFS_GSS_DBG("Enter\n");
2531 NFS_GSS_CLNT_CTX_DUMP(nmp);
39236c6e
A
2532 lck_mtx_lock(&nmp->nm_lock);
2533 TAILQ_FOREACH(cp, &nmp->nm_gsscl, gss_clnt_entries) {
fe8ab488 2534 lck_mtx_lock(cp->gss_clnt_mtx);
39236c6e 2535 if (nfs_gss_clnt_ctx_cred_match(cp->gss_clnt_cred, cred)) {
fe8ab488
A
2536 if (cp->gss_clnt_flags & GSS_CTX_DESTROY) {
2537 NFS_GSS_DBG("Found destroyed context %d/%d. refcnt = %d continuing\n",
2538 kauth_cred_getasid(cp->gss_clnt_cred),
2539 kauth_cred_getauid(cp->gss_clnt_cred),
2540 cp->gss_clnt_refcnt);
2541 lck_mtx_unlock(cp->gss_clnt_mtx);
39236c6e 2542 continue;
fe8ab488 2543 }
39236c6e 2544 cp->gss_clnt_refcnt++;
fe8ab488 2545 cp->gss_clnt_flags |= (GSS_CTX_INVAL | GSS_CTX_DESTROY);
39236c6e
A
2546 lck_mtx_unlock(cp->gss_clnt_mtx);
2547 req.r_gss_ctx = cp;
fe8ab488
A
2548 lck_mtx_unlock(&nmp->nm_lock);
2549 /*
2550 * Drop the reference to remove it if its
2551 * refcount is zero.
2552 */
2553 NFS_GSS_DBG("Removed context %d/%d refcnt = %d\n",
2554 kauth_cred_getasid(cp->gss_clnt_cred),
2555 kauth_cred_getuid(cp->gss_clnt_cred),
2556 cp->gss_clnt_refcnt);
2557 nfs_gss_clnt_ctx_unref(&req);
2558 return (0);
39236c6e 2559 }
fe8ab488 2560 lck_mtx_unlock(cp->gss_clnt_mtx);
39236c6e 2561 }
39236c6e 2562
3e170ce0
A
2563 lck_mtx_unlock(&nmp->nm_lock);
2564
2565 NFS_GSS_DBG("Returning ENOENT\n");
2566 return (ENOENT);
2567}
2568
2569/*
2570 * Sets a mounts principal for a session associated with cred.
2571 */
2572int
2573nfs_gss_clnt_ctx_set_principal(struct nfsmount *nmp, vfs_context_t ctx,
2574 uint8_t *principal, uint32_t princlen, uint32_t nametype)
2575
2576{
2577 struct nfsreq req;
2578 int error;
2579
2580 NFS_GSS_DBG("Enter:\n");
2581
2582 bzero(&req, sizeof(struct nfsreq));
2583 req.r_nmp = nmp;
2584 req.r_gss_ctx = NULL;
2585 req.r_auth = nmp->nm_auth;
2586 req.r_thread = vfs_context_thread(ctx);
2587 req.r_cred = vfs_context_ucred(ctx);
2588
2589 error = nfs_gss_clnt_ctx_find_principal(&req, principal, princlen, nametype);
2590 NFS_GSS_DBG("nfs_gss_clnt_ctx_find_principal returned %d\n", error);
2591 /*
2592 * We don't care about auth errors. Those would indicate that the context is in the
2593 * neagative cache and if and when the user has credentials for the principal
2594 * we should be good to go in that we will select those credentials for this principal.
2595 */
2596 if (error == EACCES || error == EAUTH || error == ENEEDAUTH)
2597 error = 0;
2598
2599 /* We're done with this request */
2600 nfs_gss_clnt_ctx_unref(&req);
2601
2602 return (error);
2603}
2604
2605/*
2606 * Gets a mounts principal from a session associated with cred
2607 */
2608int
2609nfs_gss_clnt_ctx_get_principal(struct nfsmount *nmp, vfs_context_t ctx,
2610 struct user_nfs_gss_principal *p)
2611{
2612 struct nfsreq req;
2613 int error = 0;
2614 struct nfs_gss_clnt_ctx *cp;
2615 kauth_cred_t cred = vfs_context_ucred(ctx);
39037602 2616 const char *princ = NULL;
3e170ce0
A
2617 char CTXBUF[NFS_CTXBUFSZ];
2618
39037602
A
2619 /* Make sure the the members of the struct user_nfs_gss_principal are initialized */
2620 p->nametype = GSSD_STRING_NAME;
2621 p->principal = USER_ADDR_NULL;
2622 p->princlen = 0;
2623 p->flags = 0;
2624
3e170ce0
A
2625 req.r_nmp = nmp;
2626 lck_mtx_lock(&nmp->nm_lock);
2627 TAILQ_FOREACH(cp, &nmp->nm_gsscl, gss_clnt_entries) {
fe8ab488 2628 lck_mtx_lock(cp->gss_clnt_mtx);
3e170ce0
A
2629 if (cp->gss_clnt_flags & GSS_CTX_DESTROY) {
2630 NFS_GSS_DBG("Found destroyed context %s refcnt = %d continuing\n",
2631 NFS_GSS_CTX(&req, cp),
2632 cp->gss_clnt_refcnt);
2633 lck_mtx_unlock(cp->gss_clnt_mtx);
2634 continue;
2635 }
fe8ab488 2636 if (nfs_gss_clnt_ctx_cred_match(cp->gss_clnt_cred, cred)) {
fe8ab488 2637 cp->gss_clnt_refcnt++;
fe8ab488 2638 lck_mtx_unlock(cp->gss_clnt_mtx);
3e170ce0 2639 goto out;
fe8ab488
A
2640 }
2641 lck_mtx_unlock(cp->gss_clnt_mtx);
2642 }
39236c6e 2643
3e170ce0
A
2644out:
2645 if (cp == NULL) {
2646 lck_mtx_unlock(&nmp->nm_lock);
39037602 2647 p->flags |= NFS_IOC_NO_CRED_FLAG; /* No credentials, valid or invalid on this mount */
3e170ce0
A
2648 NFS_GSS_DBG("No context found for session %d by uid %d\n",
2649 kauth_cred_getasid(cred), kauth_cred_getuid(cred));
2650 return (0);
2651 }
2652
39037602
A
2653 /* Indicate if the cred is INVALID */
2654 if (cp->gss_clnt_flags & GSS_CTX_INVAL)
2655 p->flags |= NFS_IOC_INVALID_CRED_FLAG;
2656
2657 /* We have set a principal on the mount */
2658 if (cp->gss_clnt_principal) {
2659 princ = (char *)cp->gss_clnt_principal;
2660 p->princlen = cp->gss_clnt_prinlen;
2661 p->nametype = cp->gss_clnt_prinnt;
2662 } else if (cp->gss_clnt_display) {
2663 /* We have a successful use the the default credential */
2664 princ = cp->gss_clnt_display;
2665 p->princlen = strlen(cp->gss_clnt_display);
2666 }
2667
2668 /*
2669 * If neither of the above is true we have an invalid default credential
2670 * So from above p->principal is USER_ADDR_NULL and princ is NULL
2671 */
2672
3e170ce0
A
2673 if (princ) {
2674 char *pp;
39236c6e 2675
3e170ce0 2676 MALLOC(pp, char *, p->princlen, M_TEMP, M_WAITOK);
39037602
A
2677 bcopy(princ, pp, p->princlen);
2678 p->principal = CAST_USER_ADDR_T(pp);
3e170ce0 2679 }
39037602 2680
3e170ce0 2681 lck_mtx_unlock(&nmp->nm_lock);
39236c6e 2682
3e170ce0
A
2683 req.r_gss_ctx = cp;
2684 NFS_GSS_DBG("Found context %s\n", NFS_GSS_CTX(&req, NULL));
2685 nfs_gss_clnt_ctx_unref(&req);
2686 return (error);
2687}
2d21ac55
A
2688#endif /* NFSCLIENT */
2689
2690/*************
2691 *
2692 * Server functions
2693 */
2694
2695#if NFSSERVER
2696
2697/*
2698 * Find a server context based on a handle value received
2699 * in an RPCSEC_GSS credential.
2700 */
2701static struct nfs_gss_svc_ctx *
2702nfs_gss_svc_ctx_find(uint32_t handle)
2703{
2704 struct nfs_gss_svc_ctx_hashhead *head;
2705 struct nfs_gss_svc_ctx *cp;
b0d623f7
A
2706 uint64_t timenow;
2707
2708 if (handle == 0)
2709 return (NULL);
2710
2d21ac55 2711 head = &nfs_gss_svc_ctx_hashtbl[SVC_CTX_HASH(handle)];
b0d623f7
A
2712 /*
2713 * Don't return a context that is going to expire in GSS_CTX_PEND seconds
2714 */
2715 clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC, &timenow);
2d21ac55
A
2716
2717 lck_mtx_lock(nfs_gss_svc_ctx_mutex);
b0d623f7 2718
6d2010ae 2719 LIST_FOREACH(cp, head, gss_svc_entries) {
b0d623f7
A
2720 if (cp->gss_svc_handle == handle) {
2721 if (timenow > cp->gss_svc_incarnation + GSS_SVC_CTX_TTL) {
2722 /*
2723 * Context has or is about to expire. Don't use.
2724 * We'll return null and the client will have to create
2725 * a new context.
2726 */
2727 cp->gss_svc_handle = 0;
2728 /*
6d2010ae 2729 * Make sure though that we stay around for GSS_CTX_PEND seconds
b0d623f7
A
2730 * for other threads that might be using the context.
2731 */
2732 cp->gss_svc_incarnation = timenow;
6d2010ae 2733
b0d623f7 2734 cp = NULL;
6d2010ae 2735 break;
b0d623f7 2736 }
6d2010ae
A
2737 lck_mtx_lock(cp->gss_svc_mtx);
2738 cp->gss_svc_refcnt++;
2739 lck_mtx_unlock(cp->gss_svc_mtx);
2d21ac55 2740 break;
b0d623f7 2741 }
6d2010ae 2742 }
b0d623f7 2743
2d21ac55
A
2744 lck_mtx_unlock(nfs_gss_svc_ctx_mutex);
2745
2746 return (cp);
2747}
2748
2749/*
2750 * Insert a new server context into the hash table
2751 * and start the context reap thread if necessary.
2752 */
2753static void
2754nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx *cp)
2755{
2756 struct nfs_gss_svc_ctx_hashhead *head;
6d2010ae 2757 struct nfs_gss_svc_ctx *p;
2d21ac55 2758
6d2010ae
A
2759 lck_mtx_lock(nfs_gss_svc_ctx_mutex);
2760
2761 /*
2762 * Give the client a random handle so that if we reboot
2763 * it's unlikely the client will get a bad context match.
2764 * Make sure it's not zero or already assigned.
2765 */
2766retry:
2767 cp->gss_svc_handle = random();
2768 if (cp->gss_svc_handle == 0)
2769 goto retry;
2d21ac55 2770 head = &nfs_gss_svc_ctx_hashtbl[SVC_CTX_HASH(cp->gss_svc_handle)];
6d2010ae
A
2771 LIST_FOREACH(p, head, gss_svc_entries)
2772 if (p->gss_svc_handle == cp->gss_svc_handle)
2773 goto retry;
2d21ac55 2774
6d2010ae
A
2775 clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC,
2776 &cp->gss_svc_incarnation);
2d21ac55
A
2777 LIST_INSERT_HEAD(head, cp, gss_svc_entries);
2778 nfs_gss_ctx_count++;
2779
2780 if (!nfs_gss_timer_on) {
2781 nfs_gss_timer_on = 1;
b0d623f7 2782
2d21ac55 2783 nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call,
6d2010ae 2784 min(GSS_TIMER_PERIOD, max(GSS_CTX_TTL_MIN, nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
2d21ac55 2785 }
b0d623f7 2786
2d21ac55
A
2787 lck_mtx_unlock(nfs_gss_svc_ctx_mutex);
2788}
2789
2790/*
2791 * This function is called via the kernel's callout
2792 * mechanism. It runs only when there are
2793 * cached RPCSEC_GSS contexts.
2794 */
2795void
2796nfs_gss_svc_ctx_timer(__unused void *param1, __unused void *param2)
2797{
2d21ac55
A
2798 struct nfs_gss_svc_ctx *cp, *next;
2799 uint64_t timenow;
2800 int contexts = 0;
2801 int i;
2802
2803 lck_mtx_lock(nfs_gss_svc_ctx_mutex);
2804 clock_get_uptime(&timenow);
2805
fe8ab488
A
2806 NFS_GSS_DBG("is running\n");
2807
2d21ac55
A
2808 /*
2809 * Scan all the hash chains
2d21ac55
A
2810 */
2811 for (i = 0; i < SVC_CTX_HASHSZ; i++) {
2812 /*
2813 * For each hash chain, look for entries
2814 * that haven't been used in a while.
2815 */
6d2010ae 2816 LIST_FOREACH_SAFE(cp, &nfs_gss_svc_ctx_hashtbl[i], gss_svc_entries, next) {
2d21ac55 2817 contexts++;
6d2010ae
A
2818 if (timenow > cp->gss_svc_incarnation +
2819 (cp->gss_svc_handle ? GSS_SVC_CTX_TTL : 0)
2820 && cp->gss_svc_refcnt == 0) {
2d21ac55
A
2821 /*
2822 * A stale context - remove it
2823 */
2824 LIST_REMOVE(cp, gss_svc_entries);
fe8ab488 2825 NFS_GSS_DBG("Removing contex for %d\n", cp->gss_svc_uid);
2d21ac55
A
2826 if (cp->gss_svc_seqbits)
2827 FREE(cp->gss_svc_seqbits, M_TEMP);
2828 lck_mtx_destroy(cp->gss_svc_mtx, nfs_gss_svc_grp);
2829 FREE(cp, M_TEMP);
2830 contexts--;
2831 }
2832 }
2833 }
2834
2835 nfs_gss_ctx_count = contexts;
2836
2837 /*
2838 * If there are still some cached contexts left,
2839 * set up another callout to check on them later.
2840 */
2841 nfs_gss_timer_on = nfs_gss_ctx_count > 0;
2842 if (nfs_gss_timer_on)
2843 nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call,
6d2010ae 2844 min(GSS_TIMER_PERIOD, max(GSS_CTX_TTL_MIN, nfsrv_gss_context_ttl)) * MSECS_PER_SEC);
2d21ac55
A
2845
2846 lck_mtx_unlock(nfs_gss_svc_ctx_mutex);
2847}
2848
2849/*
2850 * Here the server receives an RPCSEC_GSS credential in an
2851 * RPC call header. First there's some checking to make sure
2852 * the credential is appropriate - whether the context is still
2853 * being set up, or is complete. Then we use the handle to find
2854 * the server's context and validate the verifier, which contains
2855 * a signed checksum of the RPC header. If the verifier checks
2856 * out, we extract the user's UID and groups from the context
2857 * and use it to set up a UNIX credential for the user's request.
2858 */
2859int
2860nfs_gss_svc_cred_get(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
2861{
2862 uint32_t vers, proc, seqnum, service;
2863 uint32_t handle, handle_len;
39037602 2864 uint32_t major;
2d21ac55 2865 struct nfs_gss_svc_ctx *cp = NULL;
39037602 2866 uint32_t flavor = 0, header_len;
2d21ac55 2867 int error = 0;
39037602
A
2868 uint32_t arglen, start;
2869 size_t argsize;
2870 gss_buffer_desc cksum;
2d21ac55 2871 struct nfsm_chain nmc_tmp;
39037602
A
2872 mbuf_t reply_mbuf, prev_mbuf, pad_mbuf;
2873
2d21ac55 2874 vers = proc = seqnum = service = handle_len = 0;
39037602 2875 arglen = 0;
2d21ac55
A
2876
2877 nfsm_chain_get_32(error, nmc, vers);
2878 if (vers != RPCSEC_GSS_VERS_1) {
2879 error = NFSERR_AUTHERR | AUTH_REJECTCRED;
2880 goto nfsmout;
2881 }
2882
2883 nfsm_chain_get_32(error, nmc, proc);
2884 nfsm_chain_get_32(error, nmc, seqnum);
2885 nfsm_chain_get_32(error, nmc, service);
2886 nfsm_chain_get_32(error, nmc, handle_len);
2887 if (error)
2888 goto nfsmout;
2889
2890 /*
2891 * Make sure context setup/destroy is being done with a nullproc
2892 */
2893 if (proc != RPCSEC_GSS_DATA && nd->nd_procnum != NFSPROC_NULL) {
2894 error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
2895 goto nfsmout;
2896 }
2897
2898 /*
2899 * If the sequence number is greater than the max
2900 * allowable, reject and have the client init a
2901 * new context.
2902 */
2903 if (seqnum > GSS_MAXSEQ) {
2904 error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
2905 goto nfsmout;
2906 }
2907
2908 nd->nd_sec =
2909 service == RPCSEC_GSS_SVC_NONE ? RPCAUTH_KRB5 :
2910 service == RPCSEC_GSS_SVC_INTEGRITY ? RPCAUTH_KRB5I :
2911 service == RPCSEC_GSS_SVC_PRIVACY ? RPCAUTH_KRB5P : 0;
2912
2913 if (proc == RPCSEC_GSS_INIT) {
2914 /*
2915 * Limit the total number of contexts
2916 */
2917 if (nfs_gss_ctx_count > nfs_gss_ctx_max) {
2918 error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
2919 goto nfsmout;
2920 }
2921
2922 /*
2923 * Set up a new context
2924 */
2925 MALLOC(cp, struct nfs_gss_svc_ctx *, sizeof(*cp), M_TEMP, M_WAITOK|M_ZERO);
2926 if (cp == NULL) {
2927 error = ENOMEM;
2928 goto nfsmout;
2929 }
6d2010ae
A
2930 cp->gss_svc_mtx = lck_mtx_alloc_init(nfs_gss_svc_grp, LCK_ATTR_NULL);
2931 cp->gss_svc_refcnt = 1;
2d21ac55 2932 } else {
2d21ac55
A
2933 /*
2934 * Use the handle to find the context
2935 */
2936 if (handle_len != sizeof(handle)) {
2937 error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
2938 goto nfsmout;
2939 }
2940 nfsm_chain_get_32(error, nmc, handle);
2941 if (error)
2942 goto nfsmout;
2943 cp = nfs_gss_svc_ctx_find(handle);
2944 if (cp == NULL) {
2945 error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
2946 goto nfsmout;
2947 }
2948 }
2949
2950 cp->gss_svc_proc = proc;
2951
2952 if (proc == RPCSEC_GSS_DATA || proc == RPCSEC_GSS_DESTROY) {
6d2010ae 2953 struct posix_cred temp_pcred;
2d21ac55
A
2954
2955 if (cp->gss_svc_seqwin == 0) {
2956 /*
2957 * Context isn't complete
2958 */
2959 error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
2960 goto nfsmout;
2961 }
2962
2963 if (!nfs_gss_svc_seqnum_valid(cp, seqnum)) {
2964 /*
2965 * Sequence number is bad
2966 */
2967 error = EINVAL; // drop the request
2968 goto nfsmout;
2969 }
2970
2d21ac55
A
2971 /*
2972 * Validate the verifier.
2973 * The verifier contains an encrypted checksum
2974 * of the call header from the XID up to and
2975 * including the credential. We compute the
2976 * checksum and compare it with what came in
2977 * the verifier.
2978 */
39037602 2979 header_len = nfsm_chain_offset(nmc);
2d21ac55 2980 nfsm_chain_get_32(error, nmc, flavor);
39037602 2981 nfsm_chain_get_32(error, nmc, cksum.length);
6d2010ae
A
2982 if (error)
2983 goto nfsmout;
39037602 2984 if (flavor != RPCSEC_GSS || cksum.length > KRB5_MAX_MIC_SIZE)
2d21ac55 2985 error = NFSERR_AUTHERR | AUTH_BADVERF;
39037602
A
2986 MALLOC(cksum.value, void *, cksum.length, M_TEMP, M_WAITOK);
2987 nfsm_chain_get_opaque(error, nmc, cksum.length, cksum.value);
2d21ac55
A
2988 if (error)
2989 goto nfsmout;
2990
39037602
A
2991 /* Now verify the client's call header checksum */
2992 major = gss_krb5_verify_mic_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id, nmc->nmc_mhead, 0, header_len, &cksum, NULL);
2993 (void)gss_release_buffer(NULL, &cksum);
2994 if (major != GSS_S_COMPLETE) {
2995 printf("Server header: gss_krb5_verify_mic_mbuf failed %d\n", error);
2d21ac55
A
2996 error = NFSERR_AUTHERR | RPCSEC_GSS_CTXPROBLEM;
2997 goto nfsmout;
2998 }
2999
3000 nd->nd_gss_seqnum = seqnum;
3001
3002 /*
3003 * Set up the user's cred
3004 */
6d2010ae
A
3005 bzero(&temp_pcred, sizeof(temp_pcred));
3006 temp_pcred.cr_uid = cp->gss_svc_uid;
3007 bcopy(cp->gss_svc_gids, temp_pcred.cr_groups,
2d21ac55 3008 sizeof(gid_t) * cp->gss_svc_ngroups);
6d2010ae 3009 temp_pcred.cr_ngroups = cp->gss_svc_ngroups;
2d21ac55 3010
6d2010ae 3011 nd->nd_cr = posix_cred_create(&temp_pcred);
2d21ac55
A
3012 if (nd->nd_cr == NULL) {
3013 error = ENOMEM;
3014 goto nfsmout;
3015 }
b0d623f7 3016 clock_get_uptime(&cp->gss_svc_incarnation);
2d21ac55
A
3017
3018 /*
3019 * If the call arguments are integrity or privacy protected
3020 * then we need to check them here.
3021 */
3022 switch (service) {
3023 case RPCSEC_GSS_SVC_NONE:
3024 /* nothing to do */
3025 break;
3026 case RPCSEC_GSS_SVC_INTEGRITY:
3027 /*
3028 * Here's what we expect in the integrity call args:
3029 *
3030 * - length of seq num + call args (4 bytes)
3031 * - sequence number (4 bytes)
3032 * - call args (variable bytes)
39037602
A
3033 * - length of checksum token
3034 * - checksum of seqnum + call args
2d21ac55
A
3035 */
3036 nfsm_chain_get_32(error, nmc, arglen); // length of args
3037 if (arglen > NFS_MAXPACKET) {
3038 error = EBADRPC;
3039 goto nfsmout;
3040 }
3041
39037602
A
3042 nmc_tmp = *nmc;
3043 nfsm_chain_adv(error, &nmc_tmp, arglen);
3044 nfsm_chain_get_32(error, &nmc_tmp, cksum.length);
3045 MALLOC(cksum.value, void *, cksum.length, M_TEMP, M_WAITOK);
3046
3047 if (cksum.value == NULL) {
3048 error = EBADRPC;
3049 goto nfsmout;
3050 }
3051 nfsm_chain_get_opaque(error, &nmc_tmp, cksum.length, cksum.value);
3052
3053 /* Verify the checksum over the call args */
2d21ac55 3054 start = nfsm_chain_offset(nmc);
39037602
A
3055
3056 major = gss_krb5_verify_mic_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id,
3057 nmc->nmc_mhead, start, arglen, &cksum, NULL);
3058 FREE(cksum.value, M_TEMP);
3059 if (major != GSS_S_COMPLETE) {
3060 printf("Server args: gss_krb5_verify_mic_mbuf failed %d\n", error);
3061 error = EBADRPC;
3062 goto nfsmout;
3063 }
3064
2d21ac55
A
3065 /*
3066 * Get the sequence number prepended to the args
3067 * and compare it against the one sent in the
3068 * call credential.
3069 */
3070 nfsm_chain_get_32(error, nmc, seqnum);
3071 if (seqnum != nd->nd_gss_seqnum) {
3072 error = EBADRPC; // returns as GARBAGEARGS
3073 goto nfsmout;
3074 }
2d21ac55
A
3075 break;
3076 case RPCSEC_GSS_SVC_PRIVACY:
3077 /*
3078 * Here's what we expect in the privacy call args:
3079 *
39037602 3080 * - length of wrap token
2d21ac55 3081 * - wrap token (37-40 bytes)
2d21ac55 3082 */
39037602 3083 prev_mbuf = nmc->nmc_mcur;
2d21ac55
A
3084 nfsm_chain_get_32(error, nmc, arglen); // length of args
3085 if (arglen > NFS_MAXPACKET) {
3086 error = EBADRPC;
3087 goto nfsmout;
3088 }
39037602
A
3089
3090 /* Get the wrap token (current mbuf in the chain starting at the current offset) */
3091 start = nmc->nmc_ptr - (caddr_t)mbuf_data(nmc->nmc_mcur);
3092
3093 /* split out the wrap token */
3094 argsize = arglen;
3095 error = gss_normalize_mbuf(nmc->nmc_mcur, start, &argsize, &reply_mbuf, &pad_mbuf, 0);
2d21ac55
A
3096 if (error)
3097 goto nfsmout;
39037602
A
3098
3099 assert(argsize == arglen);
3100 if (pad_mbuf) {
3101 assert(nfsm_pad(arglen) == mbuf_len(pad_mbuf));
3102 mbuf_free(pad_mbuf);
3103 } else {
3104 assert(nfsm_pad(arglen) == 0);
3105 }
3106
3107 major = gss_krb5_unwrap_mbuf((uint32_t *)&error, cp->gss_svc_ctx_id, &reply_mbuf, 0, arglen, NULL, NULL);
3108 if (major != GSS_S_COMPLETE) {
3109 printf("%s: gss_krb5_unwrap_mbuf failes %d\n", __func__, error);
2d21ac55
A
3110 goto nfsmout;
3111 }
3112
39037602
A
3113 /* Now replace the wrapped arguments with the unwrapped ones */
3114 mbuf_setnext(prev_mbuf, reply_mbuf);
3115 nmc->nmc_mcur = reply_mbuf;
3116 nmc->nmc_ptr = mbuf_data(reply_mbuf);
3117 nmc->nmc_left = mbuf_len(reply_mbuf);
3118
3119 /*
3120 * - sequence number (4 bytes)
3121 * - call args
3122 */
3123
3124 // nfsm_chain_reverse(nmc, nfsm_pad(toklen));
3125
2d21ac55
A
3126 /*
3127 * Get the sequence number prepended to the args
3128 * and compare it against the one sent in the
3129 * call credential.
3130 */
2d21ac55
A
3131 nfsm_chain_get_32(error, nmc, seqnum);
3132 if (seqnum != nd->nd_gss_seqnum) {
39037602
A
3133 printf("%s: Sequence number mismatch seqnum = %d nd->nd_gss_seqnum = %d\n",
3134 __func__, seqnum, nd->nd_gss_seqnum);
3135 printmbuf("reply_mbuf", nmc->nmc_mhead, 0, 0);
3136 printf("reply_mbuf %p nmc_head %p\n", reply_mbuf, nmc->nmc_mhead);
2d21ac55
A
3137 error = EBADRPC; // returns as GARBAGEARGS
3138 goto nfsmout;
3139 }
3140 break;
3141 }
3142 } else {
39037602 3143 uint32_t verflen;
2d21ac55
A
3144 /*
3145 * If the proc is RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT
3146 * then we expect a null verifier.
3147 */
3148 nfsm_chain_get_32(error, nmc, flavor);
3149 nfsm_chain_get_32(error, nmc, verflen);
3150 if (error || flavor != RPCAUTH_NULL || verflen > 0)
3151 error = NFSERR_AUTHERR | RPCSEC_GSS_CREDPROBLEM;
6d2010ae
A
3152 if (error) {
3153 if (proc == RPCSEC_GSS_INIT) {
3154 lck_mtx_destroy(cp->gss_svc_mtx, nfs_gss_svc_grp);
3155 FREE(cp, M_TEMP);
3156 cp = NULL;
3157 }
2d21ac55 3158 goto nfsmout;
6d2010ae 3159 }
2d21ac55
A
3160 }
3161
3162 nd->nd_gss_context = cp;
6d2010ae 3163 return 0;
2d21ac55 3164nfsmout:
6d2010ae
A
3165 if (cp)
3166 nfs_gss_svc_ctx_deref(cp);
2d21ac55
A
3167 return (error);
3168}
3169
3170/*
3171 * Insert the server's verifier into the RPC reply header.
3172 * It contains a signed checksum of the sequence number that
3173 * was received in the RPC call.
3174 * Then go on to add integrity or privacy if necessary.
3175 */
3176int
3177nfs_gss_svc_verf_put(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
3178{
3179 struct nfs_gss_svc_ctx *cp;
3180 int error = 0;
39037602
A
3181 gss_buffer_desc cksum, seqbuf;
3182 uint32_t network_seqnum;
2d21ac55 3183 cp = nd->nd_gss_context;
39037602
A
3184 uint32_t major;
3185
2d21ac55
A
3186 if (cp->gss_svc_major != GSS_S_COMPLETE) {
3187 /*
3188 * If the context isn't yet complete
3189 * then return a null verifier.
3190 */
3191 nfsm_chain_add_32(error, nmc, RPCAUTH_NULL);
3192 nfsm_chain_add_32(error, nmc, 0);
3193 return (error);
3194 }
3195
3196 /*
3197 * Compute checksum of the request seq number
3198 * If it's the final reply of context setup
3199 * then return the checksum of the context
3200 * window size.
3201 */
39037602 3202 seqbuf.length = NFSX_UNSIGNED;
2d21ac55
A
3203 if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
3204 cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT)
39037602 3205 network_seqnum = htonl(cp->gss_svc_seqwin);
2d21ac55 3206 else
39037602
A
3207 network_seqnum = htonl(nd->nd_gss_seqnum);
3208 seqbuf.value = &network_seqnum;
3209
3210 major = gss_krb5_get_mic((uint32_t *)&error, cp->gss_svc_ctx_id, 0, &seqbuf, &cksum);
3211 if (major != GSS_S_COMPLETE)
3212 return (error);
3213
2d21ac55
A
3214 /*
3215 * Now wrap it in a token and add
3216 * the verifier to the reply.
3217 */
2d21ac55 3218 nfsm_chain_add_32(error, nmc, RPCSEC_GSS);
39037602
A
3219 nfsm_chain_add_32(error, nmc, cksum.length);
3220 nfsm_chain_add_opaque(error, nmc, cksum.value, cksum.length);
3221 gss_release_buffer(NULL, &cksum);
2d21ac55
A
3222
3223 return (error);
3224}
3225
3226/*
3227 * The results aren't available yet, but if they need to be
3228 * checksummed for integrity protection or encrypted, then
3229 * we can record the start offset here, insert a place-holder
3230 * for the results length, as well as the sequence number.
3231 * The rest of the work is done later by nfs_gss_svc_protect_reply()
3232 * when the results are available.
3233 */
3234int
3235nfs_gss_svc_prepare_reply(struct nfsrv_descript *nd, struct nfsm_chain *nmc)
3236{
3237 struct nfs_gss_svc_ctx *cp = nd->nd_gss_context;
3238 int error = 0;
3239
3240 if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
3241 cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT)
3242 return (0);
3243
3244 switch (nd->nd_sec) {
3245 case RPCAUTH_KRB5:
3246 /* Nothing to do */
3247 break;
3248 case RPCAUTH_KRB5I:
2d21ac55
A
3249 case RPCAUTH_KRB5P:
3250 nd->nd_gss_mb = nmc->nmc_mcur; // record current mbuf
3251 nfsm_chain_finish_mbuf(error, nmc); // split the chain here
2d21ac55
A
3252 break;
3253 }
3254
3255 return (error);
3256}
3257
3258/*
3259 * The results are checksummed or encrypted for return to the client
3260 */
3261int
39037602 3262nfs_gss_svc_protect_reply(struct nfsrv_descript *nd, mbuf_t mrep __unused)
2d21ac55
A
3263{
3264 struct nfs_gss_svc_ctx *cp = nd->nd_gss_context;
3265 struct nfsm_chain nmrep_res, *nmc_res = &nmrep_res;
2d21ac55
A
3266 mbuf_t mb, results;
3267 uint32_t reslen;
2d21ac55
A
3268 int error = 0;
3269
39037602 3270 /* XXX
2d21ac55
A
3271 * Using a reference to the mbuf where we previously split the reply
3272 * mbuf chain, we split the mbuf chain argument into two mbuf chains,
3273 * one that allows us to prepend a length field or token, (nmc_pre)
3274 * and the second which holds just the results that we're going to
3275 * checksum and/or encrypt. When we're done, we join the chains back
3276 * together.
3277 */
39037602 3278
2d21ac55
A
3279 mb = nd->nd_gss_mb; // the mbuf where we split
3280 results = mbuf_next(mb); // first mbuf in the results
2d21ac55
A
3281 error = mbuf_setnext(mb, NULL); // disconnect the chains
3282 if (error)
3283 return (error);
39037602
A
3284 nfs_gss_nfsm_chain(nmc_res, mb); // set up the prepend chain
3285 nfsm_chain_build_done(error, nmc_res);
3286 if (error)
3287 return (error);
2d21ac55
A
3288
3289 if (nd->nd_sec == RPCAUTH_KRB5I) {
39037602 3290 error = rpc_gss_integ_data_create(cp->gss_svc_ctx_id, &results, nd->nd_gss_seqnum, &reslen);
2d21ac55
A
3291 } else {
3292 /* RPCAUTH_KRB5P */
39037602 3293 error = rpc_gss_priv_data_create(cp->gss_svc_ctx_id, &results, nd->nd_gss_seqnum, &reslen);
2d21ac55 3294 }
39037602
A
3295 nfs_gss_append_chain(nmc_res, results); // Append the results mbufs
3296 nfsm_chain_build_done(error, nmc_res);
2d21ac55
A
3297
3298 return (error);
3299}
3300
3301/*
3302 * This function handles the context setup calls from the client.
3303 * Essentially, it implements the NFS null procedure calls when
3304 * an RPCSEC_GSS credential is used.
3305 * This is the context maintenance function. It creates and
3306 * destroys server contexts at the whim of the client.
3307 * During context creation, it receives GSS-API tokens from the
3308 * client, passes them up to gssd, and returns a received token
3309 * back to the client in the null procedure reply.
3310 */
3311int
3312nfs_gss_svc_ctx_init(struct nfsrv_descript *nd, struct nfsrv_sock *slp, mbuf_t *mrepp)
3313{
3314 struct nfs_gss_svc_ctx *cp = NULL;
2d21ac55
A
3315 int error = 0;
3316 int autherr = 0;
3317 struct nfsm_chain *nmreq, nmrep;
3318 int sz;
3319
3320 nmreq = &nd->nd_nmreq;
3321 nfsm_chain_null(&nmrep);
3322 *mrepp = NULL;
3323 cp = nd->nd_gss_context;
3324 nd->nd_repstat = 0;
3325
3326 switch (cp->gss_svc_proc) {
3327 case RPCSEC_GSS_INIT:
2d21ac55 3328 nfs_gss_svc_ctx_insert(cp);
2d21ac55
A
3329 /* FALLTHRU */
3330
3331 case RPCSEC_GSS_CONTINUE_INIT:
3332 /* Get the token from the request */
3333 nfsm_chain_get_32(error, nmreq, cp->gss_svc_tokenlen);
3334 if (cp->gss_svc_tokenlen == 0) {
3335 autherr = RPCSEC_GSS_CREDPROBLEM;
3336 break;
3337 }
3338 MALLOC(cp->gss_svc_token, u_char *, cp->gss_svc_tokenlen, M_TEMP, M_WAITOK);
3339 if (cp->gss_svc_token == NULL) {
3340 autherr = RPCSEC_GSS_CREDPROBLEM;
3341 break;
3342 }
3343 nfsm_chain_get_opaque(error, nmreq, cp->gss_svc_tokenlen, cp->gss_svc_token);
3344
3345 /* Use the token in a gss_accept_sec_context upcall */
3346 error = nfs_gss_svc_gssd_upcall(cp);
3347 if (error) {
3348 autherr = RPCSEC_GSS_CREDPROBLEM;
b0d623f7 3349 if (error == NFSERR_EAUTH)
2d21ac55
A
3350 error = 0;
3351 break;
3352 }
3353
3354 /*
3355 * If the context isn't complete, pass the new token
3356 * back to the client for another round.
3357 */
3358 if (cp->gss_svc_major != GSS_S_COMPLETE)
3359 break;
3360
3361 /*
3362 * Now the server context is complete.
3363 * Finish setup.
3364 */
b0d623f7
A
3365 clock_get_uptime(&cp->gss_svc_incarnation);
3366
2d21ac55
A
3367 cp->gss_svc_seqwin = GSS_SVC_SEQWINDOW;
3368 MALLOC(cp->gss_svc_seqbits, uint32_t *,
3369 nfsm_rndup((cp->gss_svc_seqwin + 7) / 8), M_TEMP, M_WAITOK|M_ZERO);
3370 if (cp->gss_svc_seqbits == NULL) {
3371 autherr = RPCSEC_GSS_CREDPROBLEM;
3372 break;
3373 }
2d21ac55
A
3374 break;
3375
3376 case RPCSEC_GSS_DATA:
3377 /* Just a nullproc ping - do nothing */
3378 break;
3379
3380 case RPCSEC_GSS_DESTROY:
3381 /*
3382 * Don't destroy the context immediately because
3383 * other active requests might still be using it.
3384 * Instead, schedule it for destruction after
3385 * GSS_CTX_PEND time has elapsed.
3386 */
3387 cp = nfs_gss_svc_ctx_find(cp->gss_svc_handle);
3388 if (cp != NULL) {
3389 cp->gss_svc_handle = 0; // so it can't be found
3390 lck_mtx_lock(cp->gss_svc_mtx);
3391 clock_interval_to_deadline(GSS_CTX_PEND, NSEC_PER_SEC,
b0d623f7 3392 &cp->gss_svc_incarnation);
2d21ac55
A
3393 lck_mtx_unlock(cp->gss_svc_mtx);
3394 }
3395 break;
3396 default:
3397 autherr = RPCSEC_GSS_CREDPROBLEM;
3398 break;
3399 }
3400
3401 /* Now build the reply */
3402
3403 if (nd->nd_repstat == 0)
3404 nd->nd_repstat = autherr ? (NFSERR_AUTHERR | autherr) : NFSERR_RETVOID;
3405 sz = 7 * NFSX_UNSIGNED + nfsm_rndup(cp->gss_svc_tokenlen); // size of results
3406 error = nfsrv_rephead(nd, slp, &nmrep, sz);
3407 *mrepp = nmrep.nmc_mhead;
3408 if (error || autherr)
3409 goto nfsmout;
3410
3411 if (cp->gss_svc_proc == RPCSEC_GSS_INIT ||
3412 cp->gss_svc_proc == RPCSEC_GSS_CONTINUE_INIT) {
3413 nfsm_chain_add_32(error, &nmrep, sizeof(cp->gss_svc_handle));
3414 nfsm_chain_add_32(error, &nmrep, cp->gss_svc_handle);
3415
3416 nfsm_chain_add_32(error, &nmrep, cp->gss_svc_major);
3417 nfsm_chain_add_32(error, &nmrep, cp->gss_svc_minor);
3418 nfsm_chain_add_32(error, &nmrep, cp->gss_svc_seqwin);
3419
3420 nfsm_chain_add_32(error, &nmrep, cp->gss_svc_tokenlen);
2d21ac55 3421 if (cp->gss_svc_token != NULL) {
b0d623f7 3422 nfsm_chain_add_opaque(error, &nmrep, cp->gss_svc_token, cp->gss_svc_tokenlen);
2d21ac55
A
3423 FREE(cp->gss_svc_token, M_TEMP);
3424 cp->gss_svc_token = NULL;
3425 }
3426 }
3427
3428nfsmout:
3429 if (autherr != 0) {
b0d623f7 3430 nd->nd_gss_context = NULL;
2d21ac55
A
3431 LIST_REMOVE(cp, gss_svc_entries);
3432 if (cp->gss_svc_seqbits != NULL)
3433 FREE(cp->gss_svc_seqbits, M_TEMP);
3434 if (cp->gss_svc_token != NULL)
3435 FREE(cp->gss_svc_token, M_TEMP);
3436 lck_mtx_destroy(cp->gss_svc_mtx, nfs_gss_svc_grp);
3437 FREE(cp, M_TEMP);
3438 }
3439
3440 nfsm_chain_build_done(error, &nmrep);
3441 if (error) {
3442 nfsm_chain_cleanup(&nmrep);
3443 *mrepp = NULL;
3444 }
3445 return (error);
3446}
3447
3448/*
3449 * This is almost a mirror-image of the client side upcall.
3450 * It passes and receives a token, but invokes gss_accept_sec_context.
3451 * If it's the final call of the context setup, then gssd also returns
3452 * the session key and the user's UID.
3453 */
3454static int
3455nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx *cp)
3456{
3457 kern_return_t kr;
3458 mach_port_t mp;
3459 int retry_cnt = 0;
39037602
A
3460 gssd_byte_buffer octx = NULL;
3461 uint32_t lucidlen = 0;
3462 void *lucid_ctx_buffer;
b0d623f7 3463 uint32_t ret_flags;
2d21ac55 3464 vm_map_copy_t itoken = NULL;
6d2010ae 3465 gssd_byte_buffer otoken = NULL;
b0d623f7 3466 mach_msg_type_number_t otokenlen;
2d21ac55
A
3467 int error = 0;
3468 char svcname[] = "nfs";
3469
316670eb 3470 kr = host_get_gssd_port(host_priv_self(), &mp);
2d21ac55 3471 if (kr != KERN_SUCCESS) {
b0d623f7
A
3472 printf("nfs_gss_svc_gssd_upcall: can't get gssd port, status %x (%d)\n", kr, kr);
3473 goto out;
2d21ac55
A
3474 }
3475 if (!IPC_PORT_VALID(mp)) {
3476 printf("nfs_gss_svc_gssd_upcall: gssd port not valid\n");
b0d623f7 3477 goto out;
2d21ac55
A
3478 }
3479
3480 if (cp->gss_svc_tokenlen > 0)
3481 nfs_gss_mach_alloc_buffer(cp->gss_svc_token, cp->gss_svc_tokenlen, &itoken);
3482
3483retry:
39037602 3484 printf("Calling mach_gss_accept_sec_context\n");
2d21ac55
A
3485 kr = mach_gss_accept_sec_context(
3486 mp,
6d2010ae 3487 (gssd_byte_buffer) itoken, (mach_msg_type_number_t) cp->gss_svc_tokenlen,
2d21ac55
A
3488 svcname,
3489 0,
2d21ac55
A
3490 &cp->gss_svc_context,
3491 &cp->gss_svc_cred_handle,
b0d623f7 3492 &ret_flags,
2d21ac55
A
3493 &cp->gss_svc_uid,
3494 cp->gss_svc_gids,
3495 &cp->gss_svc_ngroups,
39037602 3496 &octx, (mach_msg_type_number_t *) &lucidlen,
b0d623f7 3497 &otoken, &otokenlen,
2d21ac55
A
3498 &cp->gss_svc_major,
3499 &cp->gss_svc_minor);
3500
39037602 3501 printf("mach_gss_accept_sec_context returned %d\n", kr);
2d21ac55 3502 if (kr != KERN_SUCCESS) {
b0d623f7 3503 printf("nfs_gss_svc_gssd_upcall failed: %x (%d)\n", kr, kr);
2d21ac55 3504 if (kr == MIG_SERVER_DIED && cp->gss_svc_context == 0 &&
b0d623f7
A
3505 retry_cnt++ < NFS_GSS_MACH_MAX_RETRIES) {
3506 if (cp->gss_svc_tokenlen > 0)
3507 nfs_gss_mach_alloc_buffer(cp->gss_svc_token, cp->gss_svc_tokenlen, &itoken);
2d21ac55 3508 goto retry;
b0d623f7 3509 }
316670eb 3510 host_release_special_port(mp);
b0d623f7 3511 goto out;
2d21ac55
A
3512 }
3513
316670eb 3514 host_release_special_port(mp);
b0d623f7 3515
39037602
A
3516 if (lucidlen > 0) {
3517 if (lucidlen > MAX_LUCIDLEN) {
3518 printf("nfs_gss_svc_gssd_upcall: bad context length (%d)\n", lucidlen);
3519 vm_map_copy_discard((vm_map_copy_t) octx);
b0d623f7
A
3520 vm_map_copy_discard((vm_map_copy_t) otoken);
3521 goto out;
2d21ac55 3522 }
39037602
A
3523 MALLOC(lucid_ctx_buffer, void *, lucidlen, M_TEMP, M_WAITOK | M_ZERO);
3524 error = nfs_gss_mach_vmcopyout((vm_map_copy_t) octx, lucidlen, lucid_ctx_buffer);
b0d623f7
A
3525 if (error) {
3526 vm_map_copy_discard((vm_map_copy_t) otoken);
39037602 3527 FREE(lucid_ctx_buffer, M_TEMP);
b0d623f7
A
3528 goto out;
3529 }
39037602
A
3530 if (cp->gss_svc_ctx_id)
3531 gss_krb5_destroy_context(cp->gss_svc_ctx_id);
3532 cp->gss_svc_ctx_id = gss_krb5_make_context(lucid_ctx_buffer, lucidlen);
3533 if (cp->gss_svc_ctx_id == NULL) {
3534 printf("Failed to make context from lucid_ctx_buffer\n");
b0d623f7 3535 goto out;
39037602 3536 }
2d21ac55
A
3537 }
3538
b0d623f7
A
3539 /* Free context token used as input */
3540 if (cp->gss_svc_token)
3541 FREE(cp->gss_svc_token, M_TEMP);
3542 cp->gss_svc_token = NULL;
3543 cp->gss_svc_tokenlen = 0;
3544
3545 if (otokenlen > 0) {
3546 /* Set context token to gss output token */
3547 MALLOC(cp->gss_svc_token, u_char *, otokenlen, M_TEMP, M_WAITOK);
3548 if (cp->gss_svc_token == NULL) {
3549 printf("nfs_gss_svc_gssd_upcall: could not allocate %d bytes\n", otokenlen);
3550 vm_map_copy_discard((vm_map_copy_t) otoken);
2d21ac55 3551 return (ENOMEM);
b0d623f7
A
3552 }
3553 error = nfs_gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_svc_token);
3554 if (error) {
3555 FREE(cp->gss_svc_token, M_TEMP);
3556 cp->gss_svc_token = NULL;
3557 return (NFSERR_EAUTH);
3558 }
3559 cp->gss_svc_tokenlen = otokenlen;
2d21ac55
A
3560 }
3561
b0d623f7
A
3562 return (0);
3563
3564out:
3565 FREE(cp->gss_svc_token, M_TEMP);
3566 cp->gss_svc_tokenlen = 0;
3567 cp->gss_svc_token = NULL;
3568
3569 return (NFSERR_EAUTH);
2d21ac55
A
3570}
3571
3572/*
3573 * Validate the sequence number in the credential as described
3574 * in RFC 2203 Section 5.3.3.1
3575 *
3576 * Here the window of valid sequence numbers is represented by
3577 * a bitmap. As each sequence number is received, its bit is
3578 * set in the bitmap. An invalid sequence number lies below
3579 * the lower bound of the window, or is within the window but
3580 * has its bit already set.
3581 */
3582static int
3583nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx *cp, uint32_t seq)
3584{
3585 uint32_t *bits = cp->gss_svc_seqbits;
3586 uint32_t win = cp->gss_svc_seqwin;
3587 uint32_t i;
3588
3589 lck_mtx_lock(cp->gss_svc_mtx);
3590
3591 /*
3592 * If greater than the window upper bound,
3593 * move the window up, and set the bit.
3594 */
3595 if (seq > cp->gss_svc_seqmax) {
3596 if (seq - cp->gss_svc_seqmax > win)
3597 bzero(bits, nfsm_rndup((win + 7) / 8));
3598 else
3599 for (i = cp->gss_svc_seqmax + 1; i < seq; i++)
3600 win_resetbit(bits, i % win);
3601 win_setbit(bits, seq % win);
3602 cp->gss_svc_seqmax = seq;
3603 lck_mtx_unlock(cp->gss_svc_mtx);
3604 return (1);
3605 }
3606
3607 /*
3608 * Invalid if below the lower bound of the window
3609 */
3610 if (seq <= cp->gss_svc_seqmax - win) {
3611 lck_mtx_unlock(cp->gss_svc_mtx);
3612 return (0);
3613 }
3614
3615 /*
3616 * In the window, invalid if the bit is already set
3617 */
3618 if (win_getbit(bits, seq % win)) {
3619 lck_mtx_unlock(cp->gss_svc_mtx);
3620 return (0);
3621 }
3622 win_setbit(bits, seq % win);
3623 lck_mtx_unlock(cp->gss_svc_mtx);
3624 return (1);
3625}
3626
6d2010ae
A
3627/*
3628 * Drop a reference to a context
3629 *
3630 * Note that it's OK for the context to exist
3631 * with a refcount of zero. The refcount isn't
3632 * checked until we're about to reap an expired one.
3633 */
3634void
3635nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx *cp)
3636{
3637 lck_mtx_lock(cp->gss_svc_mtx);
3638 if (cp->gss_svc_refcnt > 0)
3639 cp->gss_svc_refcnt--;
3640 else
3641 printf("nfs_gss_ctx_deref: zero refcount\n");
3642 lck_mtx_unlock(cp->gss_svc_mtx);
3643}
3644
2d21ac55
A
3645/*
3646 * Called at NFS server shutdown - destroy all contexts
3647 */
3648void
3649nfs_gss_svc_cleanup(void)
3650{
3651 struct nfs_gss_svc_ctx_hashhead *head;
3652 struct nfs_gss_svc_ctx *cp, *ncp;
3653 int i;
3654
3655 lck_mtx_lock(nfs_gss_svc_ctx_mutex);
3656
3657 /*
3658 * Run through all the buckets
3659 */
3660 for (i = 0; i < SVC_CTX_HASHSZ; i++) {
3661 /*
3662 * Remove and free all entries in the bucket
3663 */
3664 head = &nfs_gss_svc_ctx_hashtbl[i];
3665 LIST_FOREACH_SAFE(cp, head, gss_svc_entries, ncp) {
3666 LIST_REMOVE(cp, gss_svc_entries);
3667 if (cp->gss_svc_seqbits)
3668 FREE(cp->gss_svc_seqbits, M_TEMP);
3669 lck_mtx_destroy(cp->gss_svc_mtx, nfs_gss_svc_grp);
3670 FREE(cp, M_TEMP);
3671 }
3672 }
3673
3674 lck_mtx_unlock(nfs_gss_svc_ctx_mutex);
3675}
3676
3677#endif /* NFSSERVER */
3678
3679
3680/*************
3681 * The following functions are used by both client and server.
3682 */
3683
3684/*
316670eb
A
3685 * Release a host special port that was obtained by host_get_special_port
3686 * or one of its macros (host_get_gssd_port in this case).
2d21ac55
A
3687 * This really should be in a public kpi.
3688 */
3689
3690/* This should be in a public header if this routine is not */
3691extern void ipc_port_release_send(ipc_port_t);
3692extern ipc_port_t ipc_port_copy_send(ipc_port_t);
3693
3694static void
316670eb 3695host_release_special_port(mach_port_t mp)
2d21ac55 3696{
6d2010ae
A
3697 if (IPC_PORT_VALID(mp))
3698 ipc_port_release_send(mp);
2d21ac55
A
3699}
3700
3701static mach_port_t
316670eb 3702host_copy_special_port(mach_port_t mp)
2d21ac55 3703{
316670eb 3704 return (ipc_port_copy_send(mp));
2d21ac55
A
3705}
3706
3707/*
3708 * The token that is sent and received in the gssd upcall
3709 * has unbounded variable length. Mach RPC does not pass
3710 * the token in-line. Instead it uses page mapping to handle
3711 * these parameters. This function allocates a VM buffer
3712 * to hold the token for an upcall and copies the token
3713 * (received from the client) into it. The VM buffer is
3714 * marked with a src_destroy flag so that the upcall will
3715 * automatically de-allocate the buffer when the upcall is
3716 * complete.
3717 */
3718static void
3719nfs_gss_mach_alloc_buffer(u_char *buf, uint32_t buflen, vm_map_copy_t *addr)
3720{
3721 kern_return_t kr;
3722 vm_offset_t kmem_buf;
3723 vm_size_t tbuflen;
3724
3725 *addr = NULL;
3726 if (buf == NULL || buflen == 0)
3727 return;
3728
39236c6e
A
3729 tbuflen = vm_map_round_page(buflen,
3730 vm_map_page_mask(ipc_kernel_map));
3e170ce0 3731 kr = vm_allocate(ipc_kernel_map, &kmem_buf, tbuflen, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_KERN_MEMORY_FILE));
2d21ac55
A
3732 if (kr != 0) {
3733 printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n");
3734 return;
3735 }
3736
39236c6e
A
3737 kr = vm_map_wire(ipc_kernel_map,
3738 vm_map_trunc_page(kmem_buf,
3739 vm_map_page_mask(ipc_kernel_map)),
3740 vm_map_round_page(kmem_buf + tbuflen,
3741 vm_map_page_mask(ipc_kernel_map)),
3e170ce0 3742 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_MEMORY_TAG_MAKE(VM_KERN_MEMORY_FILE), FALSE);
b0d623f7
A
3743 if (kr != 0) {
3744 printf("nfs_gss_mach_alloc_buffer: vm_map_wire failed\n");
3745 return;
3746 }
3747
2d21ac55 3748 bcopy(buf, (void *) kmem_buf, buflen);
b0d623f7
A
3749 // Shouldn't need to bzero below since vm_allocate returns zeroed pages
3750 // bzero(kmem_buf + buflen, tbuflen - buflen);
3751
39236c6e
A
3752 kr = vm_map_unwire(ipc_kernel_map,
3753 vm_map_trunc_page(kmem_buf,
3754 vm_map_page_mask(ipc_kernel_map)),
3755 vm_map_round_page(kmem_buf + tbuflen,
3756 vm_map_page_mask(ipc_kernel_map)),
3757 FALSE);
2d21ac55
A
3758 if (kr != 0) {
3759 printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n");
3760 return;
3761 }
3762
3763 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t) kmem_buf,
3764 (vm_map_size_t) buflen, TRUE, addr);
3765 if (kr != 0) {
3766 printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n");
3767 return;
3768 }
2d21ac55
A
3769}
3770
3771/*
3772 * Here we handle a token received from the gssd via an upcall.
3773 * The received token resides in an allocate VM buffer.
3774 * We copy the token out of this buffer to a chunk of malloc'ed
3775 * memory of the right size, then de-allocate the VM buffer.
3776 */
3777static int
3778nfs_gss_mach_vmcopyout(vm_map_copy_t in, uint32_t len, u_char *out)
3779{
3780 vm_map_offset_t map_data;
3781 vm_offset_t data;
3782 int error;
3783
3784 error = vm_map_copyout(ipc_kernel_map, &map_data, in);
3785 if (error)
3786 return (error);
3787
3788 data = CAST_DOWN(vm_offset_t, map_data);
3789 bcopy((void *) data, out, len);
3790 vm_deallocate(ipc_kernel_map, data, len);
3791
3792 return (0);
3793}
3794
2d21ac55
A
3795/*
3796 * Return the number of bytes in an mbuf chain.
3797 */
3798static int
3799nfs_gss_mchain_length(mbuf_t mhead)
3800{
3801 mbuf_t mb;
3802 int len = 0;
3803
3804 for (mb = mhead; mb; mb = mbuf_next(mb))
3805 len += mbuf_len(mb);
3806
3807 return (len);
3808}
3809
3810/*
3811 * Append an args or results mbuf chain to the header chain
3812 */
3813static int
3814nfs_gss_append_chain(struct nfsm_chain *nmc, mbuf_t mc)
3815{
3816 int error = 0;
3817 mbuf_t mb, tail;
3818
3819 /* Connect the mbuf chains */
3820 error = mbuf_setnext(nmc->nmc_mcur, mc);
3821 if (error)
3822 return (error);
3823
3824 /* Find the last mbuf in the chain */
3825 tail = NULL;
3826 for (mb = mc; mb; mb = mbuf_next(mb))
3827 tail = mb;
3828
3829 nmc->nmc_mcur = tail;
3830 nmc->nmc_ptr = (caddr_t) mbuf_data(tail) + mbuf_len(tail);
3831 nmc->nmc_left = mbuf_trailingspace(tail);
3832
3833 return (0);
3834}
3835
3836/*
3837 * Convert an mbuf chain to an NFS mbuf chain
3838 */
3839static void
3840nfs_gss_nfsm_chain(struct nfsm_chain *nmc, mbuf_t mc)
3841{
3842 mbuf_t mb, tail;
3843
3844 /* Find the last mbuf in the chain */
3845 tail = NULL;
3846 for (mb = mc; mb; mb = mbuf_next(mb))
3847 tail = mb;
3848
3849 nmc->nmc_mhead = mc;
3850 nmc->nmc_mcur = tail;
3851 nmc->nmc_ptr = (caddr_t) mbuf_data(tail) + mbuf_len(tail);
3852 nmc->nmc_left = mbuf_trailingspace(tail);
3853 nmc->nmc_flags = 0;
3854}
3855
3856
b0d623f7
A
3857
3858#if 0
3859#define DISPLAYLEN 16
3860#define MAXDISPLAYLEN 256
3861
3862static void
3863hexdump(const char *msg, void *data, size_t len)
3864{
3865 size_t i, j;
3866 u_char *d = data;
3867 char *p, disbuf[3*DISPLAYLEN+1];
3868
3869 printf("NFS DEBUG %s len=%d:\n", msg, (uint32_t)len);
3870 if (len > MAXDISPLAYLEN)
3871 len = MAXDISPLAYLEN;
3872
3873 for (i = 0; i < len; i += DISPLAYLEN) {
3874 for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3)
3875 snprintf(p, 4, "%02x ", d[i + j]);
3876 printf("\t%s\n", disbuf);
2d21ac55 3877 }
2d21ac55 3878}
b0d623f7 3879#endif