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