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