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