2 * Copyright (c) 2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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.
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.
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.
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.
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.
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
77 #include <sys/param.h>
78 #include <sys/systm.h>
80 #include <sys/kauth.h>
81 #include <sys/kernel.h>
82 #include <sys/mount_internal.h>
83 #include <sys/vnode.h>
85 #include <sys/malloc.h>
86 #include <sys/kpi_mbuf.h>
88 #include <kern/host.h>
89 #include <libkern/libkern.h>
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>
101 #include <nfs/rpcv2.h>
102 #include <nfs/nfsproto.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>
111 #define NFS_GSS_MACH_MAX_RETRIES 3
114 u_long nfs_gss_svc_ctx_hash
;
115 struct nfs_gss_svc_ctx_hashhead
*nfs_gss_svc_ctx_hashtbl
;
116 lck_mtx_t
*nfs_gss_svc_ctx_mutex
;
117 lck_grp_t
*nfs_gss_svc_grp
;
118 #endif /* NFSSERVER */
121 lck_grp_t
*nfs_gss_clnt_grp
;
122 #endif /* NFSCLIENT */
125 * These octet strings are used to encode/decode ASN.1 tokens
126 * in the RPCSEC_GSS verifiers.
128 static u_char krb5_tokhead
[] = { 0x60, 0x23 };
129 static u_char krb5_mech
[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 };
130 static u_char krb5_mic
[] = { 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff };
131 static u_char krb5_wrap
[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
132 static u_char iv0
[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // DES MAC Initialization Vector
135 * The size of the Kerberos v5 ASN.1 token
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.
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)
157 static int nfs_gss_clnt_ctx_find(struct nfsreq
*);
158 static int nfs_gss_clnt_ctx_failover(struct nfsreq
*);
159 static int nfs_gss_clnt_ctx_init(struct nfsreq
*, struct nfs_gss_clnt_ctx
*);
160 static int nfs_gss_clnt_ctx_callserver(struct nfsreq
*, struct nfs_gss_clnt_ctx
*);
161 static char *nfs_gss_clnt_svcname(struct nfsmount
*);
162 static int nfs_gss_clnt_gssd_upcall(struct nfsreq
*, struct nfs_gss_clnt_ctx
*);
163 static void nfs_gss_clnt_ctx_remove(struct nfsmount
*, struct nfs_gss_clnt_ctx
*);
164 static int nfs_gss_clnt_ctx_delay(struct nfsreq
*, int *);
165 #endif /* NFSCLIENT */
168 static struct nfs_gss_svc_ctx
*nfs_gss_svc_ctx_find(uint32_t);
169 static void nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx
*);
170 static void nfs_gss_svc_ctx_timer(void *, void *);
171 static int nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx
*);
172 static int nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx
*, uint32_t);
173 #endif /* NFSSERVER */
175 static void task_release_special_port(mach_port_t
);
176 static mach_port_t
task_copy_special_port(mach_port_t
);
177 static void nfs_gss_mach_alloc_buffer(u_char
*, uint32_t, vm_map_copy_t
*);
178 static int nfs_gss_mach_vmcopyout(vm_map_copy_t
, uint32_t, u_char
*);
179 static int nfs_gss_token_get(des_key_schedule
, u_char
*, u_char
*, int, uint32_t *, u_char
*);
180 static int nfs_gss_token_put(des_key_schedule
, u_char
*, u_char
*, int, int, u_char
*);
181 static int nfs_gss_der_length_size(int);
182 static void nfs_gss_der_length_put(u_char
**, int);
183 static int nfs_gss_der_length_get(u_char
**);
184 static int nfs_gss_mchain_length(mbuf_t
);
185 static int nfs_gss_append_chain(struct nfsm_chain
*, mbuf_t
);
186 static void nfs_gss_nfsm_chain(struct nfsm_chain
*, mbuf_t
);
187 static void nfs_gss_cksum_mchain(des_key_schedule
, mbuf_t
, u_char
*, int, int, u_char
*);
188 static void nfs_gss_cksum_chain(des_key_schedule
, struct nfsm_chain
*, u_char
*, int, int, u_char
*);
189 static void nfs_gss_cksum_rep(des_key_schedule
, uint32_t, u_char
*);
190 static void nfs_gss_encrypt_mchain(u_char
*, mbuf_t
, int, int, int);
191 static void nfs_gss_encrypt_chain(u_char
*, struct nfsm_chain
*, int, int, int);
192 static DES_LONG
des_cbc_cksum(des_cblock
*, des_cblock
*, long, des_key_schedule
, des_cblock
*);
193 static void des_cbc_encrypt(des_cblock
*, des_cblock
*, long, des_key_schedule
,
194 des_cblock
*, des_cblock
*, int);
197 thread_call_t nfs_gss_svc_ctx_timer_call
;
198 int nfs_gss_timer_on
= 0;
199 uint32_t nfs_gss_ctx_count
= 0;
200 const uint32_t nfs_gss_ctx_max
= GSS_SVC_MAXCONTEXTS
;
201 #endif /* NFSSERVER */
204 * Initialization when NFS starts
210 nfs_gss_clnt_grp
= lck_grp_alloc_init("rpcsec_gss_clnt", LCK_GRP_ATTR_NULL
);
211 #endif /* NFSCLIENT */
214 nfs_gss_svc_grp
= lck_grp_alloc_init("rpcsec_gss_svc", LCK_GRP_ATTR_NULL
);
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
);
219 nfs_gss_svc_ctx_timer_call
= thread_call_allocate(nfs_gss_svc_ctx_timer
, NULL
);
220 #endif /* NFSSERVER */
226 * Find the context for a particular user.
228 * If the context doesn't already exist
229 * then create a new context for this user.
231 * Note that the code allows superuser (uid == 0)
232 * to adopt the context of another user.
235 nfs_gss_clnt_ctx_find(struct nfsreq
*req
)
237 struct nfsmount
*nmp
= req
->r_nmp
;
238 struct nfs_gss_clnt_ctx
*cp
;
239 uid_t uid
= kauth_cred_getuid(req
->r_cred
);
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
)
249 lck_mtx_unlock(&nmp
->nm_lock
);
250 nfs_gss_clnt_ctx_ref(req
, cp
);
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.
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
);
273 * Not found - create a new context
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.
282 if (req
->r_thread
== NULL
) {
283 if ((nmp
->nm_flag
& NFSMNT_SECGIVEN
) == 0) {
284 error
= nfs_gss_clnt_ctx_failover(req
);
286 printf("nfs_gss_clnt_ctx_find: no context for async\n");
290 lck_mtx_unlock(&nmp
->nm_lock
);
295 MALLOC(cp
, struct nfs_gss_clnt_ctx
*, sizeof(*cp
), M_TEMP
, M_WAITOK
|M_ZERO
);
297 lck_mtx_unlock(&nmp
->nm_lock
);
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
);
308 error
= nfs_gss_clnt_ctx_init(req
, cp
);
310 nfs_gss_clnt_ctx_unref(req
);
312 if (error
== ENEEDAUTH
) {
313 error
= nfs_gss_clnt_ctx_delay(req
, &retrycnt
);
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
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
);
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.
340 * Assumes context list (nm_lock) is locked
343 nfs_gss_clnt_ctx_failover(struct nfsreq
*req
)
345 struct nfsmount
*nmp
= req
->r_nmp
;
346 struct nfs_gss_clnt_ctx
*cp
;
347 uid_t uid
= kauth_cred_getuid(req
->r_cred
);
350 MALLOC(cp
, struct nfs_gss_clnt_ctx
*, sizeof(*cp
), M_TEMP
, M_WAITOK
|M_ZERO
);
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
);
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
);
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
372 nfs_gss_clnt_cred_put(struct nfsreq
*req
, struct nfsm_chain
*nmc
, mbuf_t args
)
374 struct nfsmount
*nmp
= req
->r_nmp
;
375 struct nfs_gss_clnt_ctx
*cp
;
379 int start
, len
, offset
= 0;
381 struct nfsm_chain nmc_tmp
;
383 u_char tokbuf
[KRB5_SZ_TOKMAX
];
388 if (req
->r_gss_ctx
== NULL
) {
390 * Find the context for this user.
391 * If no context is found, one will
394 error
= nfs_gss_clnt_ctx_find(req
);
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.
405 if (cp
->gss_clnt_service
== RPCSEC_GSS_SVC_SYS
) {
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.
412 lck_mtx_lock(cp
->gss_clnt_mtx
);
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
);
420 lck_mtx_unlock(cp
->gss_clnt_mtx
);
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
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)))
437 nfs_gss_clnt_ctx_unref(req
);
440 lck_mtx_unlock(cp
->gss_clnt_mtx
);
442 if (cp
->gss_clnt_flags
& GSS_CTX_COMPLETE
) {
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
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
);
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
);
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
);
471 MALLOC(gsp
, struct gss_seq
*, sizeof(*gsp
), M_TEMP
, M_WAITOK
|M_ZERO
);
474 gsp
->gss_seqnum
= seqnum
;
475 SLIST_INSERT_HEAD(&req
->r_gss_seqlist
, gsp
, gss_seqnext
);
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
);
489 * Now add the verifier
491 if (cp
->gss_clnt_proc
== RPCSEC_GSS_INIT
||
492 cp
->gss_clnt_proc
== RPCSEC_GSS_CONTINUE_INIT
) {
494 * If the context is still being created
495 * then use a null verifier.
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
);
501 nfs_gss_append_chain(nmc
, args
);
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
);
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
);
518 * Now we may have to compute integrity or encrypt the call args
519 * per RFC 2203 Section 5.3.2
521 switch (cp
->gss_clnt_service
) {
522 case RPCSEC_GSS_SVC_NONE
:
523 nfs_gss_append_chain(nmc
, args
);
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
);
536 nfs_gss_append_chain(nmc
, args
); // Append the args mbufs
538 /* Now compute a checksum over the seqnum + args */
539 nfs_gss_cksum_chain(cp
->gss_clnt_sched
, nmc
, krb5_mic
, start
, len
, cksum
);
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
);
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
);
557 nfs_gss_append_chain(&nmc_tmp
, args
); // Append the args mbufs
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
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.
568 nfsm_chain_finish_mbuf(error
, &nmc_tmp
); // force padding into new mbuf
570 nfsm_chain_add_32(error
, &nmc_tmp
, 0x04040404);
571 len
+= NFSX_UNSIGNED
;
573 nfsm_chain_add_32(error
, &nmc_tmp
, 0x08080808);
574 nfsm_chain_add_32(error
, &nmc_tmp
, 0x08080808);
575 len
+= 2 * NFSX_UNSIGNED
;
577 nfsm_chain_build_done(error
, &nmc_tmp
);
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
);
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
);
590 nfs_gss_append_chain(nmc
, nmc_tmp
.nmc_mhead
); // Append the args mbufs
592 /* Finally, encrypt the args */
593 nfs_gss_encrypt_chain(cp
->gss_clnt_skey
, &nmc_tmp
, 0, len
, DES_ENCRYPT
);
595 /* Add null XDR pad if the ASN.1 token misaligned the data */
596 pad
= nfsm_pad(toklen
+ len
);
598 nfsm_chain_add_opaque_nopad(error
, nmc
, iv0
, pad
);
599 nfsm_chain_build_done(error
, nmc
);
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
616 nfs_gss_clnt_verf_get(
618 struct nfsm_chain
*nmc
,
621 uint32_t *accepted_statusp
)
623 u_char tokbuf
[KRB5_SZ_TOKMAX
];
624 u_char cksum1
[8], cksum2
[8];
626 struct nfs_gss_clnt_ctx
*cp
= req
->r_gss_ctx
;
627 struct nfsm_chain nmc_tmp
;
629 uint32_t reslen
, start
, cksumlen
, toklen
;
632 reslen
= cksumlen
= 0;
633 *accepted_statusp
= 0;
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.
644 if (verftype
!= RPCSEC_GSS
) {
645 if (verftype
!= RPCAUTH_NULL
)
647 if (cp
->gss_clnt_flags
& GSS_CTX_COMPLETE
&&
648 cp
->gss_clnt_service
!= RPCSEC_GSS_SVC_SYS
)
651 nfsm_chain_adv(error
, nmc
, nfsm_rndup(verflen
));
652 nfsm_chain_get_32(error
, nmc
, *accepted_statusp
);
656 if (verflen
!= KRB5_SZ_TOKEN
)
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.
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
)
673 nfsm_chain_get_opaque(error
, nmc
, verflen
, cp
->gss_clnt_verf
);
674 nfsm_chain_get_32(error
, nmc
, *accepted_statusp
);
679 * Get the 8 octet sequence number
680 * checksum out of the verifier token.
682 nfsm_chain_get_opaque(error
, nmc
, verflen
, tokbuf
);
685 error
= nfs_gss_token_get(cp
->gss_clnt_sched
, krb5_mic
, tokbuf
, 0, NULL
, cksum1
);
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.
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)
703 * Get the RPC accepted status
705 nfsm_chain_get_32(error
, nmc
, *accepted_statusp
);
706 if (*accepted_statusp
!= RPC_SUCCESS
)
710 * Now we may have to check integrity or decrypt the results
711 * per RFC 2203 Section 5.3.2
713 switch (cp
->gss_clnt_service
) {
714 case RPCSEC_GSS_SVC_NONE
:
717 case RPCSEC_GSS_SVC_INTEGRITY
:
719 * Here's what we expect in the integrity results:
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)
727 nfsm_chain_get_32(error
, nmc
, reslen
); // length of results
728 if (reslen
> NFS_MAXPACKET
) {
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
);
738 * Get the sequence number prepended to the results
739 * and compare it against the list in the request.
741 nfsm_chain_get_32(error
, nmc
, seqnum
);
742 SLIST_FOREACH(gsp
, &req
->r_gss_seqlist
, gss_seqnext
) {
743 if (seqnum
== gsp
->gss_seqnum
)
752 * Advance to the end of the results and
753 * fetch the checksum computed by the server.
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
) {
763 nfsm_chain_get_opaque(error
, &nmc_tmp
, cksumlen
, tokbuf
);
766 error
= nfs_gss_token_get(cp
->gss_clnt_sched
, krb5_mic
, tokbuf
, 0,
771 /* Verify that the checksums are the same */
772 if (bcmp(cksum1
, cksum2
, 8) != 0) {
777 case RPCSEC_GSS_SVC_PRIVACY
:
779 * Here's what we expect in the privacy results:
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)
787 nfsm_chain_get_32(error
, nmc
, reslen
); // length of results
788 if (reslen
> NFS_MAXPACKET
) {
793 /* Get the token that prepends the encrypted results */
794 nfsm_chain_get_opaque(error
, nmc
, KRB5_SZ_TOKMAX
, tokbuf
);
797 error
= nfs_gss_token_get(cp
->gss_clnt_sched
, krb5_wrap
, tokbuf
, 0,
801 nfsm_chain_reverse(nmc
, nfsm_pad(toklen
));
802 reslen
-= toklen
; // size of confounder + seqnum + results
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
);
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
);
811 /* Verify that the checksums are the same */
812 if (bcmp(cksum1
, cksum2
, 8) != 0) {
817 nfsm_chain_adv(error
, nmc
, 8); // skip over the confounder
820 * Get the sequence number prepended to the results
821 * and compare it against the list in the request.
823 nfsm_chain_get_32(error
, nmc
, seqnum
);
824 SLIST_FOREACH(gsp
, &req
->r_gss_seqlist
, gss_seqnext
) {
825 if (seqnum
== gsp
->gss_seqnum
)
840 * An RPCSEC_GSS request with no integrity or privacy consists
841 * of just the header mbufs followed by the arg mbufs.
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.
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.
852 nfs_gss_clnt_args_restore(struct nfsreq
*req
)
854 struct nfs_gss_clnt_ctx
*cp
= req
->r_gss_ctx
;
855 struct nfsm_chain mchain
, *nmc
= &mchain
;
861 if ((cp
->gss_clnt_flags
& GSS_CTX_COMPLETE
) == 0)
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
869 switch (cp
->gss_clnt_service
) {
870 case RPCSEC_GSS_SVC_NONE
:
873 case RPCSEC_GSS_SVC_INTEGRITY
:
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
879 nfsm_chain_adv(error
, nmc
, req
->r_gss_arglen
); // adv to last args mbuf
883 mbuf_freem(mbuf_next(nmc
->nmc_mcur
)); // free the cksum mbuf
884 error
= mbuf_setnext(nmc
->nmc_mcur
, NULL
);
886 case RPCSEC_GSS_SVC_PRIVACY
:
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.
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
);
902 mbuf_freem(mbuf_next(nmc
->nmc_mcur
)); // free the pad mbuf
903 error
= mbuf_setnext(nmc
->nmc_mcur
, NULL
);
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.
921 nfs_gss_clnt_ctx_init(struct nfsreq
*req
, struct nfs_gss_clnt_ctx
*cp
)
923 struct nfsmount
*nmp
= req
->r_nmp
;
924 int client_complete
= 0;
925 int server_complete
= 0;
926 u_char cksum1
[8], cksum2
[8];
930 /* Initialize a new client context */
932 cp
->gss_clnt_svcname
= nfs_gss_clnt_svcname(nmp
);
933 if (cp
->gss_clnt_svcname
== NULL
) {
937 cp
->gss_clnt_proc
= RPCSEC_GSS_INIT
;
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;
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.
951 /* Upcall to the gss_init_sec_context in the gssd */
952 error
= nfs_gss_clnt_gssd_upcall(req
, cp
);
956 if (cp
->gss_clnt_major
== GSS_S_COMPLETE
) {
960 } else if (cp
->gss_clnt_major
!= GSS_S_CONTINUE_NEEDED
) {
966 * Pass the token to the server.
968 error
= nfs_gss_clnt_ctx_callserver(req
, cp
);
972 if (cp
->gss_clnt_major
== GSS_S_COMPLETE
) {
976 } else if (cp
->gss_clnt_major
!= GSS_S_CONTINUE_NEEDED
) {
981 cp
->gss_clnt_proc
= RPCSEC_GSS_CONTINUE_INIT
;
985 * The context is apparently established successfully
987 cp
->gss_clnt_flags
|= GSS_CTX_COMPLETE
;
988 cp
->gss_clnt_proc
= RPCSEC_GSS_DATA
;
990 cp
->gss_clnt_ctime
= now
.tv_sec
; // time stamp
993 * Construct a key schedule from our shiny new session key
995 error
= des_key_sched((des_cblock
*) cp
->gss_clnt_skey
, cp
->gss_clnt_sched
);
1002 * Compute checksum of the server's window
1004 nfs_gss_cksum_rep(cp
->gss_clnt_sched
, cp
->gss_clnt_seqwin
, cksum1
);
1007 * and see if it matches the one in the
1008 * verifier the server returned.
1010 error
= nfs_gss_token_get(cp
->gss_clnt_sched
, krb5_mic
, cp
->gss_clnt_verf
, 0,
1012 FREE(cp
->gss_clnt_verf
, M_TEMP
);
1013 cp
->gss_clnt_verf
= NULL
;
1015 if (error
|| bcmp(cksum1
, cksum2
, 8) != 0) {
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.
1026 cp
->gss_clnt_seqnum
= (random() & 0xffff) + cp
->gss_clnt_seqwin
;
1029 * Allocate a bitmap to keep track of which requests
1030 * are pending within the sequence number window.
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
)
1038 * If there's an error, just mark it as invalid.
1039 * It will be removed when the reference count
1043 cp
->gss_clnt_flags
|= GSS_CTX_INVAL
;
1046 * Wake any threads waiting to use the context
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
;
1054 lck_mtx_unlock(cp
->gss_clnt_mtx
);
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.
1066 nfs_gss_clnt_ctx_callserver(struct nfsreq
*req
, struct nfs_gss_clnt_ctx
*cp
)
1068 struct nfsmount
*nmp
= req
->r_nmp
;
1069 struct nfsm_chain nmreq
, nmrep
;
1070 int error
= 0, status
;
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
);
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
;
1096 /* Get the server's reply */
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
) {
1107 nfsm_chain_get_opaque(error
, &nmrep
, cp
->gss_clnt_handle_len
, cp
->gss_clnt_handle
);
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
);
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
) {
1121 nfsm_chain_get_opaque(error
, &nmrep
, cp
->gss_clnt_tokenlen
, cp
->gss_clnt_token
);
1125 * Make sure any unusual errors are expanded and logged by gssd
1127 if (cp
->gss_clnt_major
!= GSS_S_COMPLETE
&&
1128 cp
->gss_clnt_major
!= GSS_S_CONTINUE_NEEDED
) {
1129 char who
[] = "server";
1131 (void) mach_gss_log_error(
1133 vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
,
1137 cp
->gss_clnt_minor
);
1141 nfsm_chain_cleanup(&nmreq
);
1142 nfsm_chain_cleanup(&nmrep
);
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.
1155 nfs_gss_clnt_svcname(struct nfsmount
*nmp
)
1158 char* mntfromhere
= &vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
[0];
1161 len
= strlen(mntfromhere
) + 5; /* "nfs/" plus null */
1162 MALLOC(svcname
, char *, len
, M_TEMP
, M_NOWAIT
);
1163 if (svcname
== NULL
)
1165 strlcpy(svcname
, "nfs/", len
);
1166 strlcat(svcname
, mntfromhere
, len
);
1167 d
= strchr(svcname
, ':');
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.
1182 nfs_gss_clnt_gssd_upcall(struct nfsreq
*req
, struct nfs_gss_clnt_ctx
*cp
)
1185 byte_buffer okey
= NULL
;
1186 uint32_t skeylen
= 0;
1188 vm_map_copy_t itoken
= NULL
;
1189 byte_buffer otoken
= NULL
;
1194 * NFS currently only supports default principals or
1195 * principals based on the uid of the caller.
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.
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
);
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
;
1215 if (cp
->gss_clnt_tokenlen
> 0)
1216 nfs_gss_mach_alloc_buffer(cp
->gss_clnt_token
, cp
->gss_clnt_tokenlen
, &itoken
);
1219 kr
= mach_gss_init_sec_context(
1222 (byte_buffer
) itoken
, (mach_msg_type_number_t
) cp
->gss_clnt_tokenlen
,
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
);
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
)
1240 task_release_special_port(cp
->gss_clnt_mport
);
1241 cp
->gss_clnt_mport
= NULL
;
1246 * Make sure any unusual errors are expanded and logged by gssd
1248 if (cp
->gss_clnt_major
!= GSS_S_COMPLETE
&&
1249 cp
->gss_clnt_major
!= GSS_S_CONTINUE_NEEDED
) {
1250 char who
[] = "client";
1252 (void) mach_gss_log_error(
1254 vfs_statfs(req
->r_nmp
->nm_mountp
)->f_mntfromname
,
1258 cp
->gss_clnt_minor
);
1262 if (skeylen
!= SKEYLEN
) {
1263 printf("nfs_gss_clnt_gssd_upcall: bad key length (%d)\n", skeylen
);
1266 error
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) okey
, skeylen
, cp
->gss_clnt_skey
);
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
)
1275 error
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) otoken
, cp
->gss_clnt_tokenlen
,
1276 cp
->gss_clnt_token
);
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).
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.
1299 nfs_gss_clnt_rpcdone(struct nfsreq
*req
)
1301 struct nfs_gss_clnt_ctx
*cp
= req
->r_gss_ctx
;
1302 struct gss_seq
*gsp
, *ngsp
;
1305 if (cp
== NULL
|| !(cp
->gss_clnt_flags
& GSS_CTX_COMPLETE
))
1306 return; // no context - don't bother
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.
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
);
1319 * Limit the seqnum list to GSS_CLNT_SEQLISTMAX entries
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
);
1329 * If there's a thread waiting for
1330 * the window to advance, wake it up.
1332 if (cp
->gss_clnt_flags
& GSS_NEEDSEQ
) {
1333 cp
->gss_clnt_flags
&= ~GSS_NEEDSEQ
;
1336 lck_mtx_unlock(cp
->gss_clnt_mtx
);
1340 * Create a reference to a context from a request
1341 * and bump the reference count
1344 nfs_gss_clnt_ctx_ref(struct nfsreq
*req
, struct nfs_gss_clnt_ctx
*cp
)
1346 req
->r_gss_ctx
= cp
;
1348 lck_mtx_lock(cp
->gss_clnt_mtx
);
1349 cp
->gss_clnt_refcnt
++;
1350 lck_mtx_unlock(cp
->gss_clnt_mtx
);
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
1359 nfs_gss_clnt_ctx_unref(struct nfsreq
*req
)
1361 struct nfsmount
*nmp
= req
->r_nmp
;
1362 struct nfs_gss_clnt_ctx
*cp
= req
->r_gss_ctx
;
1367 req
->r_gss_ctx
= NULL
;
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
);
1375 lck_mtx_lock(&nmp
->nm_lock
);
1376 nfs_gss_clnt_ctx_remove(nmp
, cp
);
1378 lck_mtx_unlock(&nmp
->nm_lock
);
1382 lck_mtx_unlock(cp
->gss_clnt_mtx
);
1389 nfs_gss_clnt_ctx_remove(struct nfsmount
*nmp
, struct nfs_gss_clnt_ctx
*cp
)
1392 * If dequeueing, assume nmp->nm_lock is held
1395 TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
);
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
);
1413 * The context for a user is invalid.
1414 * Mark the context as invalid, then
1415 * create a new context.
1418 nfs_gss_clnt_ctx_renew(struct nfsreq
*req
)
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
;
1425 mach_port_t saved_mport
;
1428 if (cp
== NULL
|| !(cp
->gss_clnt_flags
& GSS_CTX_COMPLETE
))
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
1437 saved_uid
= cp
->gss_clnt_uid
;
1438 saved_mport
= task_copy_special_port(cp
->gss_clnt_mport
);
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
);
1446 * If there's a thread waiting
1447 * in the old context, wake it up.
1449 if (cp
->gss_clnt_flags
& (GSS_NEEDCTX
| GSS_NEEDSEQ
)) {
1450 cp
->gss_clnt_flags
&= ~GSS_NEEDSEQ
;
1453 lck_mtx_unlock(cp
->gss_clnt_mtx
);
1457 * Create a new context
1459 MALLOC(ncp
, struct nfs_gss_clnt_ctx
*, sizeof(*ncp
),
1460 M_TEMP
, M_WAITOK
|M_ZERO
);
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
);
1473 /* Adjust reference counts to new and old context */
1474 nfs_gss_clnt_ctx_unref(req
);
1475 nfs_gss_clnt_ctx_ref(req
, ncp
);
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
);
1484 task_release_special_port(saved_mport
);
1486 nfs_gss_clnt_ctx_unref(req
);
1492 * Destroy all the contexts associated with a mount.
1493 * The contexts are also destroyed by the server.
1496 nfs_gss_clnt_ctx_unmount(struct nfsmount
*nmp
, int mntflags
)
1498 struct nfs_gss_clnt_ctx
*cp
;
1499 struct ucred temp_cred
;
1501 struct nfsm_chain nmreq
, nmrep
;
1506 bzero((caddr_t
) &temp_cred
, sizeof(temp_cred
));
1507 temp_cred
.cr_ngroups
= 1;
1511 lck_mtx_lock(&nmp
->nm_lock
);
1512 cp
= TAILQ_FIRST(&nmp
->nm_gsscl
);
1513 lck_mtx_unlock(&nmp
->nm_lock
);
1517 nfs_gss_clnt_ctx_ref(&req
, cp
);
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.
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
;
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
);
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
);
1543 * Mark the context invalid then drop
1544 * the reference to remove it if its
1547 cp
->gss_clnt_flags
|= GSS_CTX_INVAL
;
1548 nfs_gss_clnt_ctx_unref(&req
);
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.
1559 nfs_gss_clnt_ctx_delay(struct nfsreq
*req
, int *retry
)
1561 int timeo
= (1 << *retry
) * NFS_TRYLATERDEL
;
1563 struct nfsmount
*nmp
= req
->r_nmp
;
1567 if ((nmp
->nm_flag
& NFSMNT_SOFT
) && *retry
> nmp
->nm_retry
)
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);
1587 #endif /* NFSCLIENT */
1597 * Find a server context based on a handle value received
1598 * in an RPCSEC_GSS credential.
1600 static struct nfs_gss_svc_ctx
*
1601 nfs_gss_svc_ctx_find(uint32_t handle
)
1603 struct nfs_gss_svc_ctx_hashhead
*head
;
1604 struct nfs_gss_svc_ctx
*cp
;
1606 head
= &nfs_gss_svc_ctx_hashtbl
[SVC_CTX_HASH(handle
)];
1608 lck_mtx_lock(nfs_gss_svc_ctx_mutex
);
1609 LIST_FOREACH(cp
, head
, gss_svc_entries
)
1610 if (cp
->gss_svc_handle
== handle
)
1612 lck_mtx_unlock(nfs_gss_svc_ctx_mutex
);
1618 * Insert a new server context into the hash table
1619 * and start the context reap thread if necessary.
1622 nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx
*cp
)
1624 struct nfs_gss_svc_ctx_hashhead
*head
;
1626 head
= &nfs_gss_svc_ctx_hashtbl
[SVC_CTX_HASH(cp
->gss_svc_handle
)];
1628 lck_mtx_lock(nfs_gss_svc_ctx_mutex
);
1629 LIST_INSERT_HEAD(head
, cp
, gss_svc_entries
);
1630 nfs_gss_ctx_count
++;
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
);
1637 lck_mtx_unlock(nfs_gss_svc_ctx_mutex
);
1641 * This function is called via the kernel's callout
1642 * mechanism. It runs only when there are
1643 * cached RPCSEC_GSS contexts.
1646 nfs_gss_svc_ctx_timer(__unused
void *param1
, __unused
void *param2
)
1648 struct nfs_gss_svc_ctx_hashhead
*head
;
1649 struct nfs_gss_svc_ctx
*cp
, *next
;
1654 lck_mtx_lock(nfs_gss_svc_ctx_mutex
);
1655 clock_get_uptime(&timenow
);
1658 * Scan all the hash chains
1659 * Assume nfs_gss_svc_ctx_mutex is held
1661 for (i
= 0; i
< SVC_CTX_HASHSZ
; i
++) {
1663 * For each hash chain, look for entries
1664 * that haven't been used in a while.
1666 head
= &nfs_gss_svc_ctx_hashtbl
[i
];
1667 for (cp
= LIST_FIRST(head
); cp
; cp
= next
) {
1669 next
= LIST_NEXT(cp
, gss_svc_entries
);
1670 if (timenow
> cp
->gss_svc_expiretime
) {
1672 * A stale context - remove it
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
);
1684 nfs_gss_ctx_count
= contexts
;
1687 * If there are still some cached contexts left,
1688 * set up another callout to check on them later.
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
);
1695 lck_mtx_unlock(nfs_gss_svc_ctx_mutex
);
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.
1709 nfs_gss_svc_cred_get(struct nfsrv_descript
*nd
, struct nfsm_chain
*nmc
)
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;
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
;
1721 vers
= proc
= seqnum
= service
= handle_len
= 0;
1722 arglen
= cksumlen
= 0;
1724 nfsm_chain_get_32(error
, nmc
, vers
);
1725 if (vers
!= RPCSEC_GSS_VERS_1
) {
1726 error
= NFSERR_AUTHERR
| AUTH_REJECTCRED
;
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
);
1738 * Make sure context setup/destroy is being done with a nullproc
1740 if (proc
!= RPCSEC_GSS_DATA
&& nd
->nd_procnum
!= NFSPROC_NULL
) {
1741 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CREDPROBLEM
;
1746 * If the sequence number is greater than the max
1747 * allowable, reject and have the client init a
1750 if (seqnum
> GSS_MAXSEQ
) {
1751 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CTXPROBLEM
;
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;
1760 if (proc
== RPCSEC_GSS_INIT
) {
1762 * Limit the total number of contexts
1764 if (nfs_gss_ctx_count
> nfs_gss_ctx_max
) {
1765 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CTXPROBLEM
;
1770 * Set up a new context
1772 MALLOC(cp
, struct nfs_gss_svc_ctx
*, sizeof(*cp
), M_TEMP
, M_WAITOK
|M_ZERO
);
1780 * Use the handle to find the context
1782 if (handle_len
!= sizeof(handle
)) {
1783 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CREDPROBLEM
;
1786 nfsm_chain_get_32(error
, nmc
, handle
);
1789 cp
= nfs_gss_svc_ctx_find(handle
);
1791 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CTXPROBLEM
;
1796 cp
->gss_svc_proc
= proc
;
1798 if (proc
== RPCSEC_GSS_DATA
|| proc
== RPCSEC_GSS_DESTROY
) {
1799 struct ucred temp_cred
;
1801 if (cp
->gss_svc_seqwin
== 0) {
1803 * Context isn't complete
1805 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CTXPROBLEM
;
1809 if (!nfs_gss_svc_seqnum_valid(cp
, seqnum
)) {
1811 * Sequence number is bad
1813 error
= EINVAL
; // drop the request
1817 /* Now compute the client's call header checksum */
1818 nfs_gss_cksum_chain(cp
->gss_svc_sched
, nmc
, krb5_mic
, 0, 0, cksum1
);
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
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
);
1836 /* Get the checksum from the token inside the verifier */
1837 error
= nfs_gss_token_get(cp
->gss_svc_sched
, krb5_mic
, tokbuf
, 1,
1842 if (bcmp(cksum1
, cksum2
, 8) != 0) {
1843 error
= NFSERR_AUTHERR
| RPCSEC_GSS_CTXPROBLEM
;
1847 nd
->nd_gss_seqnum
= seqnum
;
1850 * Set up the user's cred
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
;
1858 nd
->nd_cr
= kauth_cred_create(&temp_cred
);
1859 if (nd
->nd_cr
== NULL
) {
1863 clock_interval_to_deadline(GSS_CTX_EXPIRE
, NSEC_PER_SEC
,
1864 &cp
->gss_svc_expiretime
);
1867 * If the call arguments are integrity or privacy protected
1868 * then we need to check them here.
1871 case RPCSEC_GSS_SVC_NONE
:
1874 case RPCSEC_GSS_SVC_INTEGRITY
:
1876 * Here's what we expect in the integrity call args:
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)
1884 nfsm_chain_get_32(error
, nmc
, arglen
); // length of args
1885 if (arglen
> NFS_MAXPACKET
) {
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
);
1895 * Get the sequence number prepended to the args
1896 * and compare it against the one sent in the
1899 nfsm_chain_get_32(error
, nmc
, seqnum
);
1900 if (seqnum
!= nd
->nd_gss_seqnum
) {
1901 error
= EBADRPC
; // returns as GARBAGEARGS
1906 * Advance to the end of the args and
1907 * fetch the checksum computed by the client.
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
) {
1917 nfsm_chain_get_opaque(error
, &nmc_tmp
, cksumlen
, tokbuf
);
1920 error
= nfs_gss_token_get(cp
->gss_svc_sched
, krb5_mic
, tokbuf
, 1,
1923 /* Verify that the checksums are the same */
1924 if (error
|| bcmp(cksum1
, cksum2
, 8) != 0) {
1929 case RPCSEC_GSS_SVC_PRIVACY
:
1931 * Here's what we expect in the privacy call args:
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)
1939 nfsm_chain_get_32(error
, nmc
, arglen
); // length of args
1940 if (arglen
> NFS_MAXPACKET
) {
1945 /* Get the token that prepends the encrypted args */
1946 nfsm_chain_get_opaque(error
, nmc
, KRB5_SZ_TOKMAX
, tokbuf
);
1949 error
= nfs_gss_token_get(cp
->gss_svc_sched
, krb5_wrap
, tokbuf
, 1,
1953 nfsm_chain_reverse(nmc
, nfsm_pad(toklen
));
1955 /* decrypt the 8 byte confounder + seqnum + args */
1956 start
= nfsm_chain_offset(nmc
);
1958 nfs_gss_encrypt_chain(cp
->gss_svc_skey
, nmc
, start
, arglen
, DES_DECRYPT
);
1960 /* Compute a checksum over the sequence number + results */
1961 nfs_gss_cksum_chain(cp
->gss_svc_sched
, nmc
, krb5_wrap
, start
, arglen
, cksum2
);
1963 /* Verify that the checksums are the same */
1964 if (bcmp(cksum1
, cksum2
, 8) != 0) {
1970 * Get the sequence number prepended to the args
1971 * and compare it against the one sent in the
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
1984 * If the proc is RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT
1985 * then we expect a null verifier.
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
;
1995 nd
->nd_gss_context
= cp
;
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.
2007 nfs_gss_svc_verf_put(struct nfsrv_descript
*nd
, struct nfsm_chain
*nmc
)
2009 struct nfs_gss_svc_ctx
*cp
;
2011 u_char tokbuf
[KRB5_SZ_TOKEN
];
2015 cp
= nd
->nd_gss_context
;
2017 if (cp
->gss_svc_major
!= GSS_S_COMPLETE
) {
2019 * If the context isn't yet complete
2020 * then return a null verifier.
2022 nfsm_chain_add_32(error
, nmc
, RPCAUTH_NULL
);
2023 nfsm_chain_add_32(error
, nmc
, 0);
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
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
);
2037 nfs_gss_cksum_rep(cp
->gss_svc_sched
, nd
->nd_gss_seqnum
, cksum
);
2039 * Now wrap it in a token and add
2040 * the verifier to the reply.
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
);
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.
2059 nfs_gss_svc_prepare_reply(struct nfsrv_descript
*nd
, struct nfsm_chain
*nmc
)
2061 struct nfs_gss_svc_ctx
*cp
= nd
->nd_gss_context
;
2064 if (cp
->gss_svc_proc
== RPCSEC_GSS_INIT
||
2065 cp
->gss_svc_proc
== RPCSEC_GSS_CONTINUE_INIT
)
2068 switch (nd
->nd_sec
) {
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
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
2090 * The results are checksummed or encrypted for return to the client
2093 nfs_gss_svc_protect_reply(struct nfsrv_descript
*nd
, mbuf_t mrep
)
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
;
2100 u_char tokbuf
[KRB5_SZ_TOKMAX
];
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
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
2120 nfs_gss_nfsm_chain(nmc_pre
, mb
); // set up the prepend chain
2122 if (nd
->nd_sec
== RPCAUTH_KRB5I
) {
2123 nfsm_chain_add_32(error
, nmc_pre
, reslen
);
2124 nfsm_chain_build_done(error
, nmc_pre
);
2127 nfs_gss_append_chain(nmc_pre
, results
); // Append the results mbufs
2129 /* Now compute the checksum over the results data */
2130 nfs_gss_cksum_mchain(cp
->gss_svc_sched
, results
, krb5_mic
, 0, reslen
, cksum
);
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
);
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.
2144 if (reslen
% 8 > 0) {
2145 nfsm_chain_add_32(error
, nmc_res
, 0x04040404);
2146 reslen
+= NFSX_UNSIGNED
;
2148 nfsm_chain_add_32(error
, nmc_res
, 0x08080808);
2149 nfsm_chain_add_32(error
, nmc_res
, 0x08080808);
2150 reslen
+= 2 * NFSX_UNSIGNED
;
2152 nfsm_chain_build_done(error
, nmc_res
);
2154 /* Now compute the checksum over the results data */
2155 nfs_gss_cksum_mchain(cp
->gss_svc_sched
, results
, krb5_wrap
, 0, reslen
, cksum
);
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
);
2164 nfs_gss_append_chain(nmc_pre
, results
); // Append the results mbufs
2166 /* Encrypt the confounder + seqnum + results */
2167 nfs_gss_encrypt_mchain(cp
->gss_svc_skey
, results
, 0, reslen
, DES_ENCRYPT
);
2169 /* Add null XDR pad if the ASN.1 token misaligned the data */
2170 pad
= nfsm_pad(toklen
+ reslen
);
2172 nfsm_chain_add_opaque_nopad(error
, nmc_pre
, iv0
, pad
);
2173 nfsm_chain_build_done(error
, nmc_pre
);
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.
2191 nfs_gss_svc_ctx_init(struct nfsrv_descript
*nd
, struct nfsrv_sock
*slp
, mbuf_t
*mrepp
)
2193 struct nfs_gss_svc_ctx
*cp
= NULL
;
2194 uint32_t handle
= 0;
2197 struct nfsm_chain
*nmreq
, nmrep
;
2200 nmreq
= &nd
->nd_nmreq
;
2201 nfsm_chain_null(&nmrep
);
2203 cp
= nd
->nd_gss_context
;
2206 switch (cp
->gss_svc_proc
) {
2207 case RPCSEC_GSS_INIT
:
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.
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
);
2222 nfs_gss_svc_ctx_insert(cp
);
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
;
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
;
2238 nfsm_chain_get_opaque(error
, nmreq
, cp
->gss_svc_tokenlen
, cp
->gss_svc_token
);
2240 /* Use the token in a gss_accept_sec_context upcall */
2241 error
= nfs_gss_svc_gssd_upcall(cp
);
2243 autherr
= RPCSEC_GSS_CREDPROBLEM
;
2250 * If the context isn't complete, pass the new token
2251 * back to the client for another round.
2253 if (cp
->gss_svc_major
!= GSS_S_COMPLETE
)
2257 * Now the server context is complete.
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
;
2271 * Generate a key schedule from our shiny new DES key
2273 error
= des_key_sched((des_cblock
*) cp
->gss_svc_skey
, cp
->gss_svc_sched
);
2275 autherr
= RPCSEC_GSS_CREDPROBLEM
;
2281 case RPCSEC_GSS_DATA
:
2282 /* Just a nullproc ping - do nothing */
2285 case RPCSEC_GSS_DESTROY
:
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.
2292 cp
= nfs_gss_svc_ctx_find(cp
->gss_svc_handle
);
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
);
2302 autherr
= RPCSEC_GSS_CREDPROBLEM
;
2306 /* Now build the reply */
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
)
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
);
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
);
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
;
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
);
2344 nfsm_chain_build_done(error
, &nmrep
);
2346 nfsm_chain_cleanup(&nmrep
);
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.
2359 nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx
*cp
)
2364 byte_buffer okey
= NULL
;
2365 uint32_t skeylen
= 0;
2366 vm_map_copy_t itoken
= NULL
;
2367 byte_buffer otoken
= NULL
;
2369 char svcname
[] = "nfs";
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
);
2376 if (!IPC_PORT_VALID(mp
)) {
2377 printf("nfs_gss_svc_gssd_upcall: gssd port not valid\n");
2381 if (cp
->gss_svc_tokenlen
> 0)
2382 nfs_gss_mach_alloc_buffer(cp
->gss_svc_token
, cp
->gss_svc_tokenlen
, &itoken
);
2385 kr
= mach_gss_accept_sec_context(
2387 (byte_buffer
) itoken
, (mach_msg_type_number_t
) cp
->gss_svc_tokenlen
,
2390 &cp
->gss_svc_gssd_verf
,
2391 &cp
->gss_svc_context
,
2392 &cp
->gss_svc_cred_handle
,
2395 &cp
->gss_svc_ngroups
,
2396 &okey
, (mach_msg_type_number_t
*) &skeylen
,
2397 &otoken
, (mach_msg_type_number_t
*) &cp
->gss_svc_tokenlen
,
2399 &cp
->gss_svc_minor
);
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
)
2406 task_release_special_port(mp
);
2410 task_release_special_port(mp
);
2412 if (skeylen
!= SKEYLEN
) {
2413 printf("nfs_gss_svc_gssd_upcall: bad key length (%d)\n", skeylen
);
2416 error
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) okey
, skeylen
, cp
->gss_svc_skey
);
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
)
2425 error
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) otoken
, cp
->gss_svc_tokenlen
,
2435 * Validate the sequence number in the credential as described
2436 * in RFC 2203 Section 5.3.3.1
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.
2445 nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx
*cp
, uint32_t seq
)
2447 uint32_t *bits
= cp
->gss_svc_seqbits
;
2448 uint32_t win
= cp
->gss_svc_seqwin
;
2451 lck_mtx_lock(cp
->gss_svc_mtx
);
2454 * If greater than the window upper bound,
2455 * move the window up, and set the bit.
2457 if (seq
> cp
->gss_svc_seqmax
) {
2458 if (seq
- cp
->gss_svc_seqmax
> win
)
2459 bzero(bits
, nfsm_rndup((win
+ 7) / 8));
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
);
2470 * Invalid if below the lower bound of the window
2472 if (seq
<= cp
->gss_svc_seqmax
- win
) {
2473 lck_mtx_unlock(cp
->gss_svc_mtx
);
2478 * In the window, invalid if the bit is already set
2480 if (win_getbit(bits
, seq
% win
)) {
2481 lck_mtx_unlock(cp
->gss_svc_mtx
);
2484 win_setbit(bits
, seq
% win
);
2485 lck_mtx_unlock(cp
->gss_svc_mtx
);
2490 * Called at NFS server shutdown - destroy all contexts
2493 nfs_gss_svc_cleanup(void)
2495 struct nfs_gss_svc_ctx_hashhead
*head
;
2496 struct nfs_gss_svc_ctx
*cp
, *ncp
;
2499 lck_mtx_lock(nfs_gss_svc_ctx_mutex
);
2502 * Run through all the buckets
2504 for (i
= 0; i
< SVC_CTX_HASHSZ
; i
++) {
2506 * Remove and free all entries in the bucket
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
);
2518 lck_mtx_unlock(nfs_gss_svc_ctx_mutex
);
2521 #endif /* NFSSERVER */
2525 * The following functions are used by both client and server.
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.
2534 /* This should be in a public header if this routine is not */
2535 extern void ipc_port_release_send(ipc_port_t
);
2536 extern ipc_port_t
ipc_port_copy_send(ipc_port_t
);
2539 task_release_special_port(mach_port_t mp
)
2542 ipc_port_release_send(mp
);
2546 task_copy_special_port(mach_port_t mp
)
2548 return ipc_port_copy_send(mp
);
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
2563 nfs_gss_mach_alloc_buffer(u_char
*buf
, uint32_t buflen
, vm_map_copy_t
*addr
)
2566 vm_offset_t kmem_buf
;
2570 if (buf
== NULL
|| buflen
== 0)
2573 tbuflen
= round_page(buflen
);
2574 kr
= vm_allocate(ipc_kernel_map
, &kmem_buf
, tbuflen
, VM_FLAGS_ANYWHERE
);
2576 printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n");
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
);
2584 bcopy(buf
, (void *) kmem_buf
, buflen
);
2586 kr
= vm_map_unwire(ipc_kernel_map
, vm_map_trunc_page(kmem_buf
),
2587 vm_map_round_page(kmem_buf
+ tbuflen
), FALSE
);
2589 printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n");
2593 kr
= vm_map_copyin(ipc_kernel_map
, (vm_map_address_t
) kmem_buf
,
2594 (vm_map_size_t
) buflen
, TRUE
, addr
);
2596 printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n");
2600 if (buflen
!= tbuflen
)
2601 kmem_free(ipc_kernel_map
, kmem_buf
+ buflen
, tbuflen
- buflen
);
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.
2611 nfs_gss_mach_vmcopyout(vm_map_copy_t in
, uint32_t len
, u_char
*out
)
2613 vm_map_offset_t map_data
;
2617 error
= vm_map_copyout(ipc_kernel_map
, &map_data
, in
);
2621 data
= CAST_DOWN(vm_offset_t
, map_data
);
2622 bcopy((void *) data
, out
, len
);
2623 vm_deallocate(ipc_kernel_map
, data
, len
);
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.
2635 des_key_schedule sched
,
2642 static uint32_t seqnum
= 0;
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;
2654 toklen
= KRB5_SZ_MECH
+ KRB5_SZ_ALG
+ KRB5_SZ_SEQ
+ KRB5_SZ_CKSUM
;
2655 nfs_gss_der_length_put(&p
, toklen
+ datalen
);
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
2662 bcopy(krb5_mech
, p
, sizeof(krb5_mech
));
2663 p
+= sizeof(krb5_mech
);
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.
2672 bcopy(alg
, p
, KRB5_SZ_ALG
);
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
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.
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
);
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.
2713 * Determine size of ASN.1 DER length
2716 nfs_gss_der_length_size(int len
)
2719 len
< (1 << 7) ? 1 :
2720 len
< (1 << 8) ? 2 :
2721 len
< (1 << 16) ? 3 :
2722 len
< (1 << 24) ? 4 : 5;
2726 * Encode an ASN.1 DER length field
2729 nfs_gss_der_length_put(u_char
**pp
, int len
)
2731 int sz
= nfs_gss_der_length_size(len
);
2735 *p
++ = (u_char
) len
;
2737 *p
++ = (u_char
) ((sz
-1) | 0x80);
2740 *p
++ = (u_char
) ((len
>> (sz
* 8)) & 0xff);
2747 * Decode an ASN.1 DER length field
2750 nfs_gss_der_length_get(u_char
**pp
)
2753 uint32_t flen
, len
= 0;
2757 if ((*p
++ & 0x80) == 0)
2760 if (flen
> sizeof(uint32_t))
2763 len
= (len
<< 8) + *p
++;
2770 * Decode an ASN.1 token from an RPCSEC_GSS verifier.
2774 des_key_schedule sched
,
2786 * Check that we have a valid token header
2789 return (AUTH_BADCRED
);
2790 (void) nfs_gss_der_length_get(&p
); // ignore the size
2793 * Check that we have the DER encoded Kerberos v5 mech OID
2795 if (bcmp(p
, krb5_mech
, sizeof(krb5_mech
) != 0))
2796 return (AUTH_BADCRED
);
2797 p
+= sizeof(krb5_mech
);
2800 * Now check the token ID, DES MAC MD5 algorithm
2801 * indicator, and filler octets.
2803 if (bcmp(p
, alg
, KRB5_SZ_ALG
) != 0)
2804 return (AUTH_BADCRED
);
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
2816 des_cbc_encrypt((des_cblock
*) p
, (des_cblock
*) plain
, 8,
2817 sched
, (des_cblock
*) (p
+ 8), NULL
, DES_DECRYPT
);
2819 for (i
= 0; i
< 4; i
++)
2820 seqnum
|= plain
[i
] << (i
* 8);
2823 * Make sure the direction
2824 * indicator octets are correct.
2826 d
= initiator
? 0x00 : 0xff;
2827 for (i
= 4; i
< 8; i
++)
2829 return (AUTH_BADCRED
);
2832 * Finally, get the checksum
2844 * Return the number of bytes in an mbuf chain.
2847 nfs_gss_mchain_length(mbuf_t mhead
)
2852 for (mb
= mhead
; mb
; mb
= mbuf_next(mb
))
2853 len
+= mbuf_len(mb
);
2859 * Append an args or results mbuf chain to the header chain
2862 nfs_gss_append_chain(struct nfsm_chain
*nmc
, mbuf_t mc
)
2867 /* Connect the mbuf chains */
2868 error
= mbuf_setnext(nmc
->nmc_mcur
, mc
);
2872 /* Find the last mbuf in the chain */
2874 for (mb
= mc
; mb
; mb
= mbuf_next(mb
))
2877 nmc
->nmc_mcur
= tail
;
2878 nmc
->nmc_ptr
= (caddr_t
) mbuf_data(tail
) + mbuf_len(tail
);
2879 nmc
->nmc_left
= mbuf_trailingspace(tail
);
2885 * Convert an mbuf chain to an NFS mbuf chain
2888 nfs_gss_nfsm_chain(struct nfsm_chain
*nmc
, mbuf_t mc
)
2892 /* Find the last mbuf in the chain */
2894 for (mb
= mc
; mb
; mb
= mbuf_next(mb
))
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
);
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
2913 nfs_gss_cksum_mchain(
2914 des_key_schedule sched
,
2930 * Logically prepend the first 8 bytes of the algorithm
2931 * field as required by RFC 1964, section 1.2.1.1
2933 MD5Update(&context
, alg
, KRB5_SZ_ALG
);
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.
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 */
2949 /* At or beyond offset - checksum data */
2954 bytes
= left
< len
? left
: len
;
2956 MD5Update(&context
, ptr
, bytes
);
2960 MD5Final(digest
, &context
);
2963 * Now get the DES CBC checksum for the digest.
2965 (void) des_cbc_cksum((des_cblock
*) digest
, (des_cblock
*) cksum
,
2966 sizeof(digest
), sched
, (des_cblock
*) iv0
);
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
2977 nfs_gss_cksum_chain(
2978 des_key_schedule sched
,
2979 struct nfsm_chain
*nmc
,
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.
2991 len
= nfsm_chain_offset(nmc
) - offset
;
2993 return (nfs_gss_cksum_mchain(sched
, nmc
->nmc_mhead
, alg
, offset
, len
, cksum
));
2997 * Compute a checksum of the sequence number (or sequence window)
2998 * of an RPCSEC_GSS reply.
3001 nfs_gss_cksum_rep(des_key_schedule sched
, uint32_t seqnum
, u_char
*cksum
)
3005 uint32_t val
= htonl(seqnum
);
3010 * Logically prepend the first 8 bytes of the MIC
3011 * token as required by RFC 1964, section 1.2.1.1
3013 MD5Update(&context
, krb5_mic
, KRB5_SZ_ALG
);
3016 * Compute the digest of the seqnum in network order
3018 MD5Update(&context
, (u_char
*) &val
, 4);
3019 MD5Final(digest
, &context
);
3022 * Now get the DES CBC checksum for the digest.
3024 (void) des_cbc_cksum((des_cblock
*) digest
, (des_cblock
*) cksum
,
3025 sizeof(digest
), sched
, (des_cblock
*) iv0
);
3029 * Encrypt or decrypt data in an mbuf chain with des-cbc.
3032 nfs_gss_encrypt_mchain(
3039 des_key_schedule sched
;
3042 u_char tmp
[8], ivec
[8];
3043 int i
, left
, left8
, remain
;
3046 * Make the key schedule per RFC 1964 section 1.2.2.3
3048 for (i
= 0; i
< 8; i
++)
3049 tmp
[i
] = key
[i
] ^ 0xf0;
3052 (void) des_key_sched((des_cblock
*) tmp
, sched
);
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.
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 */
3069 /* At or beyond offset - encrypt data */
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.
3084 left8
= left
- remain
;
3085 left
= left8
< len
? left8
: len
;
3087 des_cbc_encrypt((des_cblock
*) ptr
, (des_cblock
*) ptr
, left
, sched
,
3088 (des_cblock
*) ivec
, (des_cblock
*) ivec
, encrypt
);
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
3107 * Encrypt or decrypt data in an NFS mbuf chain with des-cbc.
3110 nfs_gss_encrypt_chain(
3112 struct nfsm_chain
*nmc
,
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.
3123 len
= nfsm_chain_offset(nmc
) - offset
;
3125 return (nfs_gss_encrypt_mchain(key
, nmc
->nmc_mhead
, offset
, len
, encrypt
));
3129 * XXX This function borrowed from OpenBSD.
3130 * It will likely be moved into kernel crypto.
3133 des_cbc_cksum(input
, output
, length
, schedule
, ivec
)
3134 des_cblock (*input
);
3135 des_cblock (*output
);
3137 des_key_schedule schedule
;
3140 register unsigned long tout0
,tout1
,tin0
,tin1
;
3141 register long l
=length
;
3142 unsigned long tin
[2];
3143 unsigned char *in
,*out
,*iv
;
3145 in
=(unsigned char *)input
;
3146 out
=(unsigned char *)output
;
3147 iv
=(unsigned char *)ivec
;
3156 c2ln(in
,tin0
,tin1
,l
);
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 */
3169 tout0
=tin0
=tin1
=tin
[0]=tin
[1]=0;
3174 * XXX This function borrowed from OpenBSD.
3175 * It will likely be moved into kernel crypto.
3178 des_cbc_encrypt(input
, output
, length
, schedule
, ivec
, retvec
, encrypt
)
3179 des_cblock (*input
);
3180 des_cblock (*output
);
3182 des_key_schedule schedule
;
3184 des_cblock (*retvec
);
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];
3195 in
=(unsigned char *)input
;
3196 out
=(unsigned char *)output
;
3197 retval
=(unsigned char *)retvec
;
3198 iv
=(unsigned char *)ivec
;
3203 for (l
-=8; l
>=0; l
-=8) {
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
);
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
);
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
);
3239 c2l(in
,tin0
); tin
[0]=tin0
;
3240 c2l(in
,tin1
); tin
[1]=tin1
;
3241 des_encrypt1((DES_LONG
*)tin
,schedule
,DES_DECRYPT
);
3244 l2cn(tout0
,tout1
,out
,l
+8);
3253 tin0
=tin1
=tout0
=tout1
=xor0
=xor1
=0;