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