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