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