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