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