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