2  * Copyright (c) 2007-2015 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> 
  87 #include <sys/ucred.h> 
  89 #include <kern/host.h> 
  90 #include <kern/task.h> 
  91 #include <libkern/libkern.h> 
  93 #include <mach/task.h> 
  94 #include <mach/host_special_ports.h> 
  95 #include <mach/host_priv.h> 
  96 #include <mach/thread_act.h> 
  97 #include <mach/mig_errors.h> 
  98 #include <mach/vm_map.h> 
  99 #include <vm/vm_map.h> 
 100 #include <vm/vm_kern.h> 
 101 #include <gssd/gssd_mach.h> 
 103 #include <nfs/rpcv2.h> 
 104 #include <nfs/nfsproto.h> 
 106 #include <nfs/nfsnode.h> 
 107 #include <nfs/nfs_gss.h> 
 108 #include <nfs/nfsmount.h> 
 109 #include <nfs/xdr_subs.h> 
 110 #include <nfs/nfsm_subs.h> 
 111 #include <nfs/nfs_gss.h> 
 112 #include "nfs_gss_crypto.h" 
 113 #include <mach_assert.h> 
 114 #include <kern/assert.h> 
 116 #define ASSERT(EX) assert(EX) 
 118 #define NFS_GSS_MACH_MAX_RETRIES 3 
 120 #define NFS_GSS_DBG(...) NFS_DBG(NFS_FAC_GSS, 7, ## __VA_ARGS__) 
 121 #define NFS_GSS_ISDBG  (NFS_DEBUG_FACILITY &  NFS_FAC_GSS) 
 126                 MD5_DESCBC_CTX m_ctx
; 
 127                 HMAC_SHA1_DES3KD_CTX h_ctx
; 
 131 #define MAX_DIGEST SHA_DIGEST_LENGTH 
 132 #ifdef NFS_KERNEL_DEBUG 
 133 #define HASHLEN(ki)  (((ki)->hash_len > MAX_DIGEST) ? \ 
 134                 (panic("nfs_gss.c:%d ki->hash_len is invalid = %d\n", __LINE__, (ki)->hash_len), MAX_DIGEST) : (ki)->hash_len) 
 136 #define HASHLEN(ki)  (((ki)->hash_len > MAX_DIGEST) ? \ 
 137                 (printf("nfs_gss.c:%d ki->hash_len is invalid = %d\n", __LINE__, (ki)->hash_len), MAX_DIGEST) : (ki)->hash_len) 
 141 u_long nfs_gss_svc_ctx_hash
; 
 142 struct nfs_gss_svc_ctx_hashhead 
*nfs_gss_svc_ctx_hashtbl
; 
 143 lck_mtx_t 
*nfs_gss_svc_ctx_mutex
; 
 144 lck_grp_t 
*nfs_gss_svc_grp
; 
 145 uint32_t nfsrv_gss_context_ttl 
= GSS_CTX_EXPIRE
; 
 146 #define GSS_SVC_CTX_TTL ((uint64_t)max(2*GSS_CTX_PEND, nfsrv_gss_context_ttl) * NSEC_PER_SEC) 
 147 #endif /* NFSSERVER */ 
 150 lck_grp_t 
*nfs_gss_clnt_grp
; 
 152 #endif /* NFSCLIENT */ 
 155  * These octet strings are used to encode/decode ASN.1 tokens 
 156  * in the RPCSEC_GSS verifiers. 
 158 static u_char krb5_tokhead
[] __attribute__((unused
)) = { 0x60, 0x23 }; 
 159        u_char krb5_mech
[11] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 }; 
 160 static u_char krb5_mic
[]  = { 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; 
 161 static u_char krb5_mic3
[]  = { 0x01, 0x01, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff }; 
 162 static u_char krb5_wrap
[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }; 
 163 static u_char krb5_wrap3
[] = { 0x02, 0x01, 0x04, 0x00, 0x02, 0x00, 0xff, 0xff }; 
 164 static u_char iv0
[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // DES MAC Initialization Vector 
 166 #define ALG_MIC(ki) (((ki)->type == NFS_GSS_1DES) ? krb5_mic : krb5_mic3) 
 167 #define ALG_WRAP(ki) (((ki)->type == NFS_GSS_1DES) ? krb5_wrap : krb5_wrap3) 
 170  * The size of the Kerberos v5 ASN.1 token 
 173  * Note that the second octet of the krb5_tokhead (0x23) is a 
 174  * DER-encoded size field that has variable length.  If the size 
 175  * is 128 bytes or greater, then it uses two bytes, three bytes 
 176  * if 65536 or greater, and so on.  Since the MIC tokens are 
 177  * separate from the data, the size is always the same: 35 bytes (0x23). 
 178  * However, the wrap token is different. Its size field includes the 
 179  * size of the token + the encrypted data that follows. So the size 
 180  * field may be two, three or four bytes. 
 182 #define KRB5_SZ_TOKHEAD sizeof(krb5_tokhead) 
 183 #define KRB5_SZ_MECH    sizeof(krb5_mech) 
 184 #define KRB5_SZ_ALG     sizeof(krb5_mic) // 8 - same as krb5_wrap 
 185 #define KRB5_SZ_SEQ     8 
 186 #define KRB5_SZ_EXTRA   3  // a wrap token may be longer by up to this many octets 
 187 #define KRB5_SZ_TOKEN_NOSUM     (KRB5_SZ_TOKHEAD + KRB5_SZ_MECH + KRB5_SZ_ALG + KRB5_SZ_SEQ) 
 188 #define KRB5_SZ_TOKEN(cksumlen)         ((cksumlen) + KRB5_SZ_TOKEN_NOSUM) 
 189 #define KRB5_SZ_TOKMAX(cksumlen)        (KRB5_SZ_TOKEN(cksumlen) + KRB5_SZ_EXTRA) 
 192 static int      nfs_gss_clnt_ctx_find(struct nfsreq 
*); 
 193 static int      nfs_gss_clnt_ctx_init(struct nfsreq 
*, struct nfs_gss_clnt_ctx 
*); 
 194 static int      nfs_gss_clnt_ctx_init_retry(struct nfsreq 
*, struct nfs_gss_clnt_ctx 
*); 
 195 static int      nfs_gss_clnt_ctx_callserver(struct nfsreq 
*, struct nfs_gss_clnt_ctx 
*); 
 196 static uint8_t  *nfs_gss_clnt_svcname(struct nfsmount 
*, gssd_nametype 
*, uint32_t *); 
 197 static int      nfs_gss_clnt_gssd_upcall(struct nfsreq 
*, struct nfs_gss_clnt_ctx 
*); 
 198 void            nfs_gss_clnt_ctx_neg_cache_reap(struct nfsmount 
*); 
 199 static void     nfs_gss_clnt_ctx_clean(struct nfs_gss_clnt_ctx 
*); 
 200 static int      nfs_gss_clnt_ctx_copy(struct nfs_gss_clnt_ctx 
*, struct nfs_gss_clnt_ctx 
**, gss_key_info 
*); 
 201 static void     nfs_gss_clnt_ctx_destroy(struct nfs_gss_clnt_ctx 
*); 
 202 static void     nfs_gss_clnt_log_error(struct nfsreq 
*, struct nfs_gss_clnt_ctx 
*, uint32_t, uint32_t); 
 203 #endif /* NFSCLIENT */ 
 206 static struct nfs_gss_svc_ctx 
*nfs_gss_svc_ctx_find(uint32_t); 
 207 static void     nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx 
*); 
 208 static void     nfs_gss_svc_ctx_timer(void *, void *); 
 209 static int      nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx 
*); 
 210 static int      nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx 
*, uint32_t); 
 211 #endif /* NFSSERVER */ 
 213 static void     host_release_special_port(mach_port_t
); 
 214 static mach_port_t 
host_copy_special_port(mach_port_t
); 
 215 static void     nfs_gss_mach_alloc_buffer(u_char 
*, uint32_t, vm_map_copy_t 
*); 
 216 static int      nfs_gss_mach_vmcopyout(vm_map_copy_t
, uint32_t, u_char 
*); 
 217 static int      nfs_gss_token_get(gss_key_info 
*ki
, u_char 
*, u_char 
*, int, uint32_t *, u_char 
*); 
 218 static int      nfs_gss_token_put(gss_key_info 
*ki
, u_char 
*, u_char 
*, int, int, u_char 
*); 
 219 static int      nfs_gss_der_length_size(int); 
 220 static void     nfs_gss_der_length_put(u_char 
**, int); 
 221 static int      nfs_gss_der_length_get(u_char 
**); 
 222 static int      nfs_gss_mchain_length(mbuf_t
); 
 223 static int      nfs_gss_append_chain(struct nfsm_chain 
*, mbuf_t
); 
 224 static void     nfs_gss_nfsm_chain(struct nfsm_chain 
*, mbuf_t
); 
 225 static void     nfs_gss_cksum_mchain(gss_key_info 
*, mbuf_t
, u_char 
*, int, int, u_char 
*); 
 226 static void     nfs_gss_cksum_chain(gss_key_info 
*, struct nfsm_chain 
*, u_char 
*, int, int, u_char 
*); 
 227 static void     nfs_gss_cksum_rep(gss_key_info 
*, uint32_t, u_char 
*); 
 228 static void     nfs_gss_encrypt_mchain(gss_key_info 
*, mbuf_t
, int, int, int); 
 229 static void     nfs_gss_encrypt_chain(gss_key_info 
*, struct nfsm_chain 
*, int, int, int); 
 231 static void     gss_digest_Init(GSS_DIGEST_CTX 
*, gss_key_info 
*); 
 232 static void     gss_digest_Update(GSS_DIGEST_CTX 
*, void *, size_t); 
 233 static void     gss_digest_Final(GSS_DIGEST_CTX 
*, void *); 
 234 static void     gss_des_crypt(gss_key_info 
*, des_cblock 
*, des_cblock 
*, 
 235                                 int32_t, des_cblock 
*, des_cblock 
*, int, int); 
 236 static int      gss_key_init(gss_key_info 
*, uint32_t); 
 239 thread_call_t nfs_gss_svc_ctx_timer_call
; 
 240 int nfs_gss_timer_on 
= 0; 
 241 uint32_t nfs_gss_ctx_count 
= 0; 
 242 const uint32_t nfs_gss_ctx_max 
= GSS_SVC_MAXCONTEXTS
; 
 243 #endif /* NFSSERVER */ 
 246  * Initialization when NFS starts 
 252         nfs_gss_clnt_grp 
= lck_grp_alloc_init("rpcsec_gss_clnt", LCK_GRP_ATTR_NULL
); 
 253 #endif /* NFSCLIENT */ 
 256         nfs_gss_svc_grp  
= lck_grp_alloc_init("rpcsec_gss_svc",  LCK_GRP_ATTR_NULL
); 
 258         nfs_gss_svc_ctx_hashtbl 
= hashinit(SVC_CTX_HASHSZ
, M_TEMP
, &nfs_gss_svc_ctx_hash
); 
 259         nfs_gss_svc_ctx_mutex 
= lck_mtx_alloc_init(nfs_gss_svc_grp
, LCK_ATTR_NULL
); 
 261         nfs_gss_svc_ctx_timer_call 
= thread_call_allocate(nfs_gss_svc_ctx_timer
, NULL
); 
 262 #endif /* NFSSERVER */ 
 268  * Find the context for a particular user. 
 270  * If the context doesn't already exist 
 271  * then create a new context for this user. 
 273  * Note that the code allows superuser (uid == 0) 
 274  * to adopt the context of another user. 
 276  * We'll match on the audit session ids, since those 
 277  * processes will have acccess to the same credential cache. 
 280 #define kauth_cred_getasid(cred) ((cred)->cr_audit.as_aia_p->ai_asid) 
 281 #define kauth_cred_getauid(cred) ((cred)->cr_audit.as_aia_p->ai_auid) 
 283 #define SAFE_CAST_INTTYPE( type, intval ) \ 
 284         ( (type)(intval)/(sizeof(type) < sizeof(intval) ? 0 : 1) ) 
 287 nfs_cred_getasid2uid(kauth_cred_t cred
) 
 289         uid_t result 
= SAFE_CAST_INTTYPE(uid_t
, kauth_cred_getasid(cred
)); 
 297 nfs_gss_clnt_ctx_dump(struct nfsmount 
*nmp
) 
 299         struct nfs_gss_clnt_ctx 
*cp
; 
 301         lck_mtx_lock(&nmp
->nm_lock
); 
 302         NFS_GSS_DBG("Enter\n"); 
 303         TAILQ_FOREACH(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
) { 
 304                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
 305                 printf("context %d/%d: refcnt = %d, flags = %x\n", 
 306                        kauth_cred_getasid(cp
->gss_clnt_cred
), 
 307                        kauth_cred_getauid(cp
->gss_clnt_cred
), 
 308                        cp
->gss_clnt_refcnt
, cp
->gss_clnt_flags
); 
 309                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 311         NFS_GSS_DBG("Exit\n"); 
 312         lck_mtx_unlock(&nmp
->nm_lock
); 
 316 nfs_gss_clnt_ctx_name(struct nfsmount 
*nmp
, struct nfs_gss_clnt_ctx 
*cp
, char *buf
, int len
) 
 320         const char *server 
= ""; 
 322         if (nmp 
&& nmp
->nm_mountp
) 
 323                 server 
= vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
; 
 326                 snprintf(buf
, len
, "[%s] NULL context", server
); 
 330         if (cp
->gss_clnt_principal 
&& !cp
->gss_clnt_display
) { 
 331                 np 
= (char *)cp
->gss_clnt_principal
; 
 332                 nlen 
= cp
->gss_clnt_prinlen
; 
 334                 np 
= cp
->gss_clnt_display
; 
 335                 nlen 
= np 
? strlen(cp
->gss_clnt_display
) : 0; 
 338                 snprintf(buf
, len
, "[%s] %.*s %d/%d %s", server
, nlen
, np
, 
 339                          kauth_cred_getasid(cp
->gss_clnt_cred
), 
 340                          kauth_cred_getuid(cp
->gss_clnt_cred
), 
 341                          cp
->gss_clnt_principal 
? "" : "[from default cred] "); 
 343                 snprintf(buf
, len
, "[%s] using default %d/%d ", server
, 
 344                          kauth_cred_getasid(cp
->gss_clnt_cred
), 
 345                          kauth_cred_getuid(cp
->gss_clnt_cred
)); 
 349 #define NFS_CTXBUFSZ 80 
 350 #define NFS_GSS_CTX(req, cp) nfs_gss_clnt_ctx_name((req)->r_nmp, cp ? cp : (req)->r_gss_ctx, CTXBUF, sizeof(CTXBUF)) 
 352 #define NFS_GSS_CLNT_CTX_DUMP(nmp)              \ 
 354                 if (NFS_GSS_ISDBG && (NFS_DEBUG_FLAGS & 0x2))   \ 
 355                         nfs_gss_clnt_ctx_dump((nmp));   \ 
 359 nfs_gss_clnt_ctx_cred_match(kauth_cred_t cred1
, kauth_cred_t cred2
) 
 361         if (kauth_cred_getasid(cred1
) == kauth_cred_getasid(cred2
)) 
 367  * Busy the mount for each principal set on the mount 
 368  * so that the automounter will not unmount the file 
 369  * system underneath us. With out this, if an unmount 
 370  * occurs the principal that is set for an audit session 
 371  * will be lost and we may end up with a different identity. 
 373  * Note setting principals on the mount is a bad idea. This 
 374  * really should be handle by KIM (Kerberos Identity Management) 
 375  * so that defaults can be set by service identities. 
 379 nfs_gss_clnt_mnt_ref(struct nfsmount 
*nmp
) 
 385             !(vfs_flags(nmp
->nm_mountp
) & MNT_AUTOMOUNTED
)) 
 388         error 
= VFS_ROOT(nmp
->nm_mountp
, &rvp
, NULL
); 
 396  * Unbusy the mout. See above comment, 
 400 nfs_gss_clnt_mnt_rele(struct nfsmount 
*nmp
) 
 406             !(vfs_flags(nmp
->nm_mountp
) & MNT_AUTOMOUNTED
)) 
 409         error 
= VFS_ROOT(nmp
->nm_mountp
, &rvp
, NULL
); 
 416 int nfs_root_steals_ctx 
= 1; 
 419 nfs_gss_clnt_ctx_find_principal(struct nfsreq 
*req
, uint8_t *principal
, uint32_t plen
, uint32_t nt
) 
 421         struct nfsmount 
*nmp 
= req
->r_nmp
; 
 422         struct nfs_gss_clnt_ctx 
*cp
; 
 427         char CTXBUF
[NFS_CTXBUFSZ
]; 
 429         bzero(&treq
, sizeof (struct nfsreq
)); 
 433         lck_mtx_lock(&nmp
->nm_lock
); 
 434         TAILQ_FOREACH(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
) { 
 435                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
 436                 if (cp
->gss_clnt_flags 
& GSS_CTX_DESTROY
) { 
 437                         NFS_GSS_DBG("Found destroyed context %s refcnt = %d continuing\n", 
 438                                     NFS_GSS_CTX(req
, cp
), 
 439                                     cp
->gss_clnt_refcnt
); 
 440                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 443                 if (nfs_gss_clnt_ctx_cred_match(cp
->gss_clnt_cred
, req
->r_cred
)) { 
 444                         if (nmp
->nm_gsscl
.tqh_first 
!= cp
) { 
 445                                 TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
 446                                 TAILQ_INSERT_HEAD(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
 450                                  * If we have a principal, but it does not match the current cred 
 451                                  * mark it for removal 
 453                                 if (cp
->gss_clnt_prinlen 
!= plen 
|| cp
->gss_clnt_prinnt 
!= nt 
|| 
 454                                     bcmp(cp
->gss_clnt_principal
, principal
, plen
) != 0) { 
 455                                         cp
->gss_clnt_flags 
|= (GSS_CTX_INVAL 
| GSS_CTX_DESTROY
); 
 456                                         cp
->gss_clnt_refcnt
++; 
 457                                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 458                                         NFS_GSS_DBG("Marking %s for deletion because %s does not match\n", 
 459                                                     NFS_GSS_CTX(req
, cp
), principal
); 
 460                                         NFS_GSS_DBG("len = (%d,%d), nt = (%d,%d)\n", cp
->gss_clnt_prinlen
, plen
, 
 461                                                     cp
->gss_clnt_prinnt
, nt
); 
 467                         if (cp
->gss_clnt_flags 
& GSS_CTX_INVAL
) { 
 469                                  * If we're still being used and we're not expired 
 470                                  * just return and don't bother gssd again. Note if 
 471                                  * gss_clnt_nctime is zero it is about to be set to now. 
 473                                 if (cp
->gss_clnt_nctime 
+ GSS_NEG_CACHE_TO 
>= now
.tv_sec 
|| cp
->gss_clnt_nctime 
== 0) { 
 474                                         NFS_GSS_DBG("Context %s (refcnt = %d) not expired returning EAUTH nctime = %ld now = %ld\n", 
 475                                                     NFS_GSS_CTX(req
, cp
), cp
->gss_clnt_refcnt
, cp
->gss_clnt_nctime
, now
.tv_sec
); 
 476                                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 477                                         lck_mtx_unlock(&nmp
->nm_lock
); 
 478                                         return (NFSERR_EAUTH
); 
 480                                 if (cp
->gss_clnt_refcnt
) { 
 481                                         struct nfs_gss_clnt_ctx 
*ncp
; 
 483                                          * If this context has references, we can't use it so we mark if for 
 484                                          * destruction and create a new context based on this one in the 
 485                                          * same manner as renewing one. 
 487                                         cp
->gss_clnt_flags 
|= GSS_CTX_DESTROY
; 
 488                                         NFS_GSS_DBG("Context %s has expired but we still have %d references\n", 
 489                                                     NFS_GSS_CTX(req
, cp
), cp
->gss_clnt_refcnt
); 
 490                                         error 
= nfs_gss_clnt_ctx_copy(cp
, &ncp
, NULL
); 
 491                                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 493                                                 lck_mtx_unlock(&nmp
->nm_lock
); 
 499                                         /* cp->gss_clnt_kinfo should be NULL here */ 
 500                                         if (cp
->gss_clnt_kinfo
) { 
 501                                                 FREE(cp
->gss_clnt_kinfo
, M_TEMP
); 
 502                                                 cp
->gss_clnt_kinfo 
= NULL
; 
 504                                         if (cp
->gss_clnt_nctime
) 
 506                                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 507                                         TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
 511                         /* Found a valid context to return */ 
 512                         cp
->gss_clnt_refcnt
++; 
 514                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 515                         lck_mtx_unlock(&nmp
->nm_lock
); 
 518                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 521         MALLOC(ki
, gss_key_info 
*, sizeof (gss_key_info
), M_TEMP
, M_WAITOK
|M_ZERO
); 
 523                 lck_mtx_unlock(&nmp
->nm_lock
); 
 528                 cp
->gss_clnt_kinfo 
= ki
; 
 529         } else if (nfs_root_steals_ctx 
&& principal 
== NULL 
&& kauth_cred_getuid(req
->r_cred
) == 0) { 
 531                  * If superuser is trying to get access, then co-opt 
 532                  * the first valid context in the list. 
 533                  * XXX Ultimately, we need to allow superuser to 
 534                  * go ahead and attempt to set up its own context 
 535                  * in case one is set up for it. 
 537                 TAILQ_FOREACH(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
) { 
 538                         if (!(cp
->gss_clnt_flags 
& (GSS_CTX_INVAL
|GSS_CTX_DESTROY
))) { 
 539                                 nfs_gss_clnt_ctx_ref(req
, cp
); 
 540                                 lck_mtx_unlock(&nmp
->nm_lock
); 
 541                                 NFS_GSS_DBG("Root stole context %s\n", NFS_GSS_CTX(req
, NULL
)); 
 547         NFS_GSS_DBG("Context %s%sfound in Neg Cache @  %ld\n", 
 548                     NFS_GSS_CTX(req
, cp
), 
 549                     cp 
== NULL 
? " not " : "", 
 550                     cp 
== NULL 
? 0L : cp
->gss_clnt_nctime
); 
 553          * Not found - create a new context 
 557                 MALLOC(cp
, struct nfs_gss_clnt_ctx 
*, sizeof(*cp
), M_TEMP
, M_WAITOK
|M_ZERO
); 
 559                         lck_mtx_unlock(&nmp
->nm_lock
); 
 562                 cp
->gss_clnt_kinfo 
= ki
; 
 563                 cp
->gss_clnt_cred 
= req
->r_cred
; 
 564                 kauth_cred_ref(cp
->gss_clnt_cred
); 
 565                 cp
->gss_clnt_mtx 
= lck_mtx_alloc_init(nfs_gss_clnt_grp
, LCK_ATTR_NULL
); 
 566                 cp
->gss_clnt_ptime 
= now
.tv_sec 
- GSS_PRINT_DELAY
; 
 568                         MALLOC(cp
->gss_clnt_principal
, uint8_t *, plen
+1, M_TEMP
, M_WAITOK
|M_ZERO
); 
 569                         memcpy(cp
->gss_clnt_principal
, principal
, plen
); 
 570                         cp
->gss_clnt_prinlen 
= plen
; 
 571                         cp
->gss_clnt_prinnt 
= nt
; 
 572                         cp
->gss_clnt_flags 
|= GSS_CTX_STICKY
; 
 573                         nfs_gss_clnt_mnt_ref(nmp
); 
 576                 nfs_gss_clnt_ctx_clean(cp
); 
 579                          * If we have a principal and we found a matching audit 
 580                          * session, then to get here, the principal had to match. 
 581                          * In walking the context list if it has a principal 
 582                          * or the principal is not set then we mark the context 
 583                          * for destruction and set cp to NULL and we fall to the 
 584                          * if clause above. If the context still has references 
 585                          * again we copy the context which will preserve the principal 
 586                          * and we end up here with the correct principal set. 
 587                          * If we don't have references the the principal must have 
 588                          * match and we will fall through here. 
 590                         cp
->gss_clnt_flags 
|= GSS_CTX_STICKY
; 
 594         cp
->gss_clnt_thread 
= current_thread(); 
 595         nfs_gss_clnt_ctx_ref(req
, cp
); 
 596         TAILQ_INSERT_HEAD(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
 597         lck_mtx_unlock(&nmp
->nm_lock
); 
 599         error 
= nfs_gss_clnt_ctx_init_retry(req
, cp
); // Initialize new context 
 601                 NFS_GSS_DBG("nfs_gss_clnt_ctx_init_retry returned %d for %s\n", error
, NFS_GSS_CTX(req
, cp
)); 
 602                 nfs_gss_clnt_ctx_unref(req
); 
 605         /* Remove any old matching contex that had a different principal */ 
 606         nfs_gss_clnt_ctx_unref(&treq
); 
 612 nfs_gss_clnt_ctx_find(struct nfsreq 
*req
) 
 614         return (nfs_gss_clnt_ctx_find_principal(req
, NULL
, 0, 0)); 
 618  * Inserts an RPCSEC_GSS credential into an RPC header. 
 619  * After the credential is inserted, the code continues 
 620  * to build the verifier which contains a signed checksum 
 624 nfs_gss_clnt_cred_put(struct nfsreq 
*req
, struct nfsm_chain 
*nmc
, mbuf_t args
) 
 626         struct nfs_gss_clnt_ctx 
*cp
; 
 629         int slpflag
, recordmark 
= 0; 
 630         int start
, len
, offset 
= 0; 
 632         struct nfsm_chain nmc_tmp
; 
 634         u_char tokbuf
[KRB5_SZ_TOKMAX(MAX_DIGEST
)]; 
 635         u_char cksum
[MAX_DIGEST
]; 
 640                 slpflag 
|= (NMFLAG(req
->r_nmp
, INTR
) && req
->r_thread 
&& !(req
->r_flags 
& R_NOINTR
)) ? PCATCH 
: 0; 
 641                 recordmark 
= (req
->r_nmp
->nm_sotype 
== SOCK_STREAM
); 
 645         if (req
->r_gss_ctx 
== NULL
) { 
 647                  * Find the context for this user. 
 648                  * If no context is found, one will 
 651                 error 
= nfs_gss_clnt_ctx_find(req
); 
 658          * If the context thread isn't null, then the context isn't 
 659          * yet complete and is for the exclusive use of the thread 
 660          * doing the context setup. Wait until the context thread 
 663         lck_mtx_lock(cp
->gss_clnt_mtx
); 
 664         if (cp
->gss_clnt_thread 
&& cp
->gss_clnt_thread 
!= current_thread()) { 
 665                 cp
->gss_clnt_flags 
|= GSS_NEEDCTX
; 
 666                 msleep(cp
, cp
->gss_clnt_mtx
, slpflag 
| PDROP
, "ctxwait", NULL
); 
 668                 if ((error 
= nfs_sigintr(req
->r_nmp
, req
, req
->r_thread
, 0))) 
 670                 nfs_gss_clnt_ctx_unref(req
); 
 673         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 675         ki 
= cp
->gss_clnt_kinfo
; 
 676         if (cp
->gss_clnt_flags 
& GSS_CTX_COMPLETE
) { 
 678                  * Get a sequence number for this request. 
 679                  * Check whether the oldest request in the window is complete. 
 680                  * If it's still pending, then wait until it's done before 
 681                  * we allocate a new sequence number and allow this request 
 684                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
 685                 while (win_getbit(cp
->gss_clnt_seqbits
,  
 686                         ((cp
->gss_clnt_seqnum 
- cp
->gss_clnt_seqwin
) + 1) % cp
->gss_clnt_seqwin
)) { 
 687                         cp
->gss_clnt_flags 
|= GSS_NEEDSEQ
; 
 688                         msleep(cp
, cp
->gss_clnt_mtx
, slpflag 
| PDROP
, "seqwin", NULL
); 
 690                         if ((error 
= nfs_sigintr(req
->r_nmp
, req
, req
->r_thread
, 0))) { 
 693                         lck_mtx_lock(cp
->gss_clnt_mtx
); 
 694                         if (cp
->gss_clnt_flags 
& GSS_CTX_INVAL
) { 
 695                                 /* Renewed while while we were waiting */ 
 696                                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 697                                 nfs_gss_clnt_ctx_unref(req
); 
 701                 seqnum 
= ++cp
->gss_clnt_seqnum
; 
 702                 win_setbit(cp
->gss_clnt_seqbits
, seqnum 
% cp
->gss_clnt_seqwin
); 
 703                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
 705                 MALLOC(gsp
, struct gss_seq 
*, sizeof(*gsp
), M_TEMP
, M_WAITOK
|M_ZERO
); 
 708                 gsp
->gss_seqnum 
= seqnum
; 
 709                 SLIST_INSERT_HEAD(&req
->r_gss_seqlist
, gsp
, gss_seqnext
); 
 712         /* Insert the credential */ 
 713         nfsm_chain_add_32(error
, nmc
, RPCSEC_GSS
); 
 714         nfsm_chain_add_32(error
, nmc
, 5 * NFSX_UNSIGNED 
+ cp
->gss_clnt_handle_len
); 
 715         nfsm_chain_add_32(error
, nmc
, RPCSEC_GSS_VERS_1
); 
 716         nfsm_chain_add_32(error
, nmc
, cp
->gss_clnt_proc
); 
 717         nfsm_chain_add_32(error
, nmc
, seqnum
); 
 718         nfsm_chain_add_32(error
, nmc
, cp
->gss_clnt_service
); 
 719         nfsm_chain_add_32(error
, nmc
, cp
->gss_clnt_handle_len
); 
 720         if (cp
->gss_clnt_handle_len 
> 0) { 
 721                 if (cp
->gss_clnt_handle 
== NULL
) 
 723                 nfsm_chain_add_opaque(error
, nmc
, cp
->gss_clnt_handle
, cp
->gss_clnt_handle_len
); 
 728          * Now add the verifier 
 730         if (cp
->gss_clnt_proc 
== RPCSEC_GSS_INIT 
|| 
 731                 cp
->gss_clnt_proc 
== RPCSEC_GSS_CONTINUE_INIT
) { 
 733                  * If the context is still being created 
 734                  * then use a null verifier. 
 736                 nfsm_chain_add_32(error
, nmc
, RPCAUTH_NULL
);    // flavor 
 737                 nfsm_chain_add_32(error
, nmc
, 0);               // length 
 738                 nfsm_chain_build_done(error
, nmc
); 
 740                         nfs_gss_append_chain(nmc
, args
); 
 744         offset 
= recordmark 
? NFSX_UNSIGNED 
: 0; // record mark 
 745         nfsm_chain_build_done(error
, nmc
); 
 746         nfs_gss_cksum_chain(ki
, nmc
, ALG_MIC(ki
), offset
, 0, cksum
); 
 748         toklen 
= nfs_gss_token_put(ki
, ALG_MIC(ki
), tokbuf
, 1, 0, cksum
); 
 749         nfsm_chain_add_32(error
, nmc
, RPCSEC_GSS
);      // flavor 
 750         nfsm_chain_add_32(error
, nmc
, toklen
);          // length 
 751         nfsm_chain_add_opaque(error
, nmc
, tokbuf
, toklen
); 
 752         nfsm_chain_build_done(error
, nmc
); 
 757          * Now we may have to compute integrity or encrypt the call args 
 758          * per RFC 2203 Section 5.3.2 
 760         switch (cp
->gss_clnt_service
) { 
 761         case RPCSEC_GSS_SVC_NONE
: 
 762                 nfs_gss_append_chain(nmc
, args
); 
 764         case RPCSEC_GSS_SVC_INTEGRITY
: 
 765                 len 
= nfs_gss_mchain_length(args
);      // Find args length 
 766                 req
->r_gss_arglen 
= len
;                // Stash the args len 
 767                 len 
+= NFSX_UNSIGNED
;                   // Add seqnum length 
 768                 nfsm_chain_add_32(error
, nmc
, len
);     // and insert it 
 769                 start 
= nfsm_chain_offset(nmc
); 
 770                 nfsm_chain_add_32(error
, nmc
, seqnum
);  // Insert seqnum 
 771                 req
->r_gss_argoff 
= nfsm_chain_offset(nmc
); // Offset to args 
 772                 nfsm_chain_build_done(error
, nmc
); 
 775                 nfs_gss_append_chain(nmc
, args
);        // Append the args mbufs 
 777                 /* Now compute a checksum over the seqnum + args */ 
 778                 nfs_gss_cksum_chain(ki
, nmc
, ALG_MIC(ki
), start
, len
, cksum
); 
 780                 /* Insert it into a token and append to the request */ 
 781                 toklen 
= nfs_gss_token_put(ki
, ALG_MIC(ki
), tokbuf
, 1, 0, cksum
); 
 782                 nfsm_chain_finish_mbuf(error
, nmc
);     // force checksum into new mbuf 
 783                 nfsm_chain_add_32(error
, nmc
, toklen
); 
 784                 nfsm_chain_add_opaque(error
, nmc
, tokbuf
, toklen
); 
 785                 nfsm_chain_build_done(error
, nmc
); 
 787         case RPCSEC_GSS_SVC_PRIVACY
: 
 788                 /* Prepend a new mbuf with the confounder & sequence number */ 
 789                 nfsm_chain_build_alloc_init(error
, &nmc_tmp
, 3 * NFSX_UNSIGNED
); 
 790                 nfsm_chain_add_32(error
, &nmc_tmp
, random());   // confounder bytes 1-4 
 791                 nfsm_chain_add_32(error
, &nmc_tmp
, random());   // confounder bytes 4-8 
 792                 nfsm_chain_add_32(error
, &nmc_tmp
, seqnum
); 
 793                 nfsm_chain_build_done(error
, &nmc_tmp
); 
 796                 nfs_gss_append_chain(&nmc_tmp
, args
);           // Append the args mbufs 
 798                 len 
= nfs_gss_mchain_length(args
);              // Find args length 
 799                 len 
+= 3 * NFSX_UNSIGNED
;                       // add confounder & seqnum 
 800                 req
->r_gss_arglen 
= len
;                        // Stash length 
 803                  * Append a pad trailer - per RFC 1964 section 1.2.2.3 
 804                  * Since XDR data is always 32-bit aligned, it 
 805                  * needs to be padded either by 4 bytes or 8 bytes. 
 807                 nfsm_chain_finish_mbuf(error
, &nmc_tmp
);        // force padding into new mbuf 
 809                         nfsm_chain_add_32(error
, &nmc_tmp
, 0x04040404); 
 810                         len 
+= NFSX_UNSIGNED
; 
 812                         nfsm_chain_add_32(error
, &nmc_tmp
, 0x08080808); 
 813                         nfsm_chain_add_32(error
, &nmc_tmp
, 0x08080808); 
 814                         len 
+=  2 * NFSX_UNSIGNED
; 
 816                 nfsm_chain_build_done(error
, &nmc_tmp
); 
 818                 /* Now compute a checksum over the confounder + seqnum + args */ 
 819                 nfs_gss_cksum_chain(ki
, &nmc_tmp
, ALG_WRAP(ki
), 0, len
, cksum
); 
 821                 /* Insert it into a token */ 
 822                 toklen 
= nfs_gss_token_put(ki
, ALG_WRAP(ki
), tokbuf
, 1, len
, cksum
); 
 823                 nfsm_chain_add_32(error
, nmc
, toklen 
+ len
);    // token + args length 
 824                 nfsm_chain_add_opaque_nopad(error
, nmc
, tokbuf
, toklen
); 
 825                 req
->r_gss_argoff 
= nfsm_chain_offset(nmc
);     // Stash offset 
 826                 nfsm_chain_build_done(error
, nmc
); 
 829                 nfs_gss_append_chain(nmc
, nmc_tmp
.nmc_mhead
);   // Append the args mbufs 
 831                 /* Finally, encrypt the args */ 
 832                 nfs_gss_encrypt_chain(ki
, &nmc_tmp
, 0, len
, DES_ENCRYPT
); 
 834                 /* Add null XDR pad if the ASN.1 token misaligned the data */ 
 835                 pad 
= nfsm_pad(toklen 
+ len
); 
 837                         nfsm_chain_add_opaque_nopad(error
, nmc
, iv0
, pad
); 
 838                         nfsm_chain_build_done(error
, nmc
); 
 847  * When receiving a reply, the client checks the verifier 
 848  * returned by the server. Check that the verifier is the 
 849  * correct type, then extract the sequence number checksum 
 850  * from the token in the credential and compare it with a 
 851  * computed checksum of the sequence number in the request 
 855 nfs_gss_clnt_verf_get( 
 857         struct nfsm_chain 
*nmc
, 
 860         uint32_t *accepted_statusp
) 
 862         u_char tokbuf
[KRB5_SZ_TOKMAX(MAX_DIGEST
)]; 
 863         u_char cksum1
[MAX_DIGEST
], cksum2
[MAX_DIGEST
]; 
 865         struct nfs_gss_clnt_ctx 
*cp 
= req
->r_gss_ctx
; 
 866         struct nfsm_chain nmc_tmp
; 
 868         uint32_t reslen
, start
, cksumlen
, toklen
; 
 870         gss_key_info 
*ki 
= cp
->gss_clnt_kinfo
; 
 872         reslen 
= cksumlen 
= 0; 
 873         *accepted_statusp 
= 0; 
 876                 return (NFSERR_EAUTH
); 
 878          * If it's not an RPCSEC_GSS verifier, then it has to 
 879          * be a null verifier that resulted from either 
 880          * a CONTINUE_NEEDED reply during context setup or 
 881          * from the reply to an AUTH_UNIX call from a dummy 
 882          * context that resulted from a fallback to sec=sys. 
 884         if (verftype 
!= RPCSEC_GSS
) { 
 885                 if (verftype 
!= RPCAUTH_NULL
) 
 886                         return (NFSERR_EAUTH
); 
 887                 if (cp
->gss_clnt_flags 
& GSS_CTX_COMPLETE
) 
 888                         return (NFSERR_EAUTH
); 
 890                         nfsm_chain_adv(error
, nmc
, nfsm_rndup(verflen
)); 
 891                 nfsm_chain_get_32(error
, nmc
, *accepted_statusp
); 
 896          * If we received an RPCSEC_GSS verifier but the 
 897          * context isn't yet complete, then it must be 
 898          * the context complete message from the server. 
 899          * The verifier will contain an encrypted checksum 
 900          * of the window but we don't have the session key 
 901          * yet so we can't decrypt it. Stash the verifier 
 902          * and check it later in nfs_gss_clnt_ctx_init() when 
 903          * the context is complete. 
 905         if (!(cp
->gss_clnt_flags 
& GSS_CTX_COMPLETE
)) { 
 906                 MALLOC(cp
->gss_clnt_verf
, u_char 
*, verflen
, M_TEMP
, M_WAITOK
|M_ZERO
); 
 907                 if (cp
->gss_clnt_verf 
== NULL
) 
 909                 nfsm_chain_get_opaque(error
, nmc
, verflen
, cp
->gss_clnt_verf
); 
 910                 nfsm_chain_get_32(error
, nmc
, *accepted_statusp
); 
 914         if (verflen 
!= KRB5_SZ_TOKEN(ki
->hash_len
)) 
 915                 return (NFSERR_EAUTH
); 
 918          * Get the 8 octet sequence number 
 919          * checksum out of the verifier token. 
 921         nfsm_chain_get_opaque(error
, nmc
, verflen
, tokbuf
); 
 924         error 
= nfs_gss_token_get(ki
, ALG_MIC(ki
), tokbuf
, 0, NULL
, cksum1
); 
 929          * Search the request sequence numbers for this reply, starting 
 930          * with the most recent, looking for a checksum that matches 
 931          * the one in the verifier returned by the server. 
 933         SLIST_FOREACH(gsp
, &req
->r_gss_seqlist
, gss_seqnext
) { 
 934                 nfs_gss_cksum_rep(ki
, gsp
->gss_seqnum
, cksum2
); 
 935                 if (bcmp(cksum1
, cksum2
, HASHLEN(ki
)) == 0) 
 939                 return (NFSERR_EAUTH
); 
 942          * Get the RPC accepted status 
 944         nfsm_chain_get_32(error
, nmc
, *accepted_statusp
); 
 945         if (*accepted_statusp 
!= RPC_SUCCESS
) 
 949          * Now we may have to check integrity or decrypt the results 
 950          * per RFC 2203 Section 5.3.2 
 952         switch (cp
->gss_clnt_service
) { 
 953         case RPCSEC_GSS_SVC_NONE
: 
 956         case RPCSEC_GSS_SVC_INTEGRITY
: 
 958                  * Here's what we expect in the integrity results: 
 960                  * - length of seq num + results (4 bytes) 
 961                  * - sequence number (4 bytes) 
 962                  * - results (variable bytes) 
 963                  * - length of checksum token (37) 
 964                  * - checksum of seqnum + results (37 bytes) 
 966                 nfsm_chain_get_32(error
, nmc
, reslen
);          // length of results 
 967                 if (reslen 
> NFS_MAXPACKET
) { 
 972                 /* Compute a checksum over the sequence number + results */ 
 973                 start 
= nfsm_chain_offset(nmc
); 
 974                 nfs_gss_cksum_chain(ki
, nmc
, ALG_MIC(ki
), start
, reslen
, cksum1
); 
 977                  * Get the sequence number prepended to the results 
 978                  * and compare it against the list in the request. 
 980                 nfsm_chain_get_32(error
, nmc
, seqnum
); 
 981                 SLIST_FOREACH(gsp
, &req
->r_gss_seqlist
, gss_seqnext
) { 
 982                         if (seqnum 
== gsp
->gss_seqnum
) 
 991                  * Advance to the end of the results and 
 992                  * fetch the checksum computed by the server. 
 995                 reslen 
-= NFSX_UNSIGNED
;                        // already skipped seqnum 
 996                 nfsm_chain_adv(error
, &nmc_tmp
, reslen
);        // skip over the results 
 997                 nfsm_chain_get_32(error
, &nmc_tmp
, cksumlen
);   // length of checksum 
 998                 if (cksumlen 
!= KRB5_SZ_TOKEN(ki
->hash_len
)) { 
1002                 nfsm_chain_get_opaque(error
, &nmc_tmp
, cksumlen
, tokbuf
); 
1005                 error 
= nfs_gss_token_get(ki
, ALG_MIC(ki
), tokbuf
, 0, NULL
, cksum2
); 
1009                 /* Verify that the checksums are the same */ 
1010                 if (bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
1015         case RPCSEC_GSS_SVC_PRIVACY
: 
1017                  * Here's what we expect in the privacy results: 
1019                  * - length of confounder + seq num + token + results 
1020                  * - wrap token (37-40 bytes) 
1021                  * - confounder (8 bytes) 
1022                  * - sequence number (4 bytes) 
1023                  * - results (encrypted) 
1025                 nfsm_chain_get_32(error
, nmc
, reslen
);          // length of results 
1026                 if (reslen 
> NFS_MAXPACKET
) { 
1031                 /* Get the token that prepends the encrypted results */ 
1032                 nfsm_chain_get_opaque(error
, nmc
, KRB5_SZ_TOKMAX(ki
->hash_len
), tokbuf
); 
1035                 error 
= nfs_gss_token_get(ki
, ALG_WRAP(ki
), tokbuf
, 0, 
1039                 nfsm_chain_reverse(nmc
, nfsm_pad(toklen
)); 
1040                 reslen 
-= toklen
;                               // size of confounder + seqnum + results 
1042                 /* decrypt the confounder + sequence number + results */ 
1043                 start 
= nfsm_chain_offset(nmc
); 
1044                 nfs_gss_encrypt_chain(ki
, nmc
, start
, reslen
, DES_DECRYPT
); 
1046                 /* Compute a checksum over the confounder + sequence number + results */ 
1047                 nfs_gss_cksum_chain(ki
, nmc
, ALG_WRAP(ki
), start
, reslen
, cksum2
); 
1049                 /* Verify that the checksums are the same */ 
1050                 if (bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
1055                 nfsm_chain_adv(error
, nmc
, 8);  // skip over the confounder 
1058                  * Get the sequence number prepended to the results 
1059                  * and compare it against the list in the request. 
1061                 nfsm_chain_get_32(error
, nmc
, seqnum
); 
1062                 SLIST_FOREACH(gsp
, &req
->r_gss_seqlist
, gss_seqnext
) { 
1063                         if (seqnum 
== gsp
->gss_seqnum
) 
1078  * An RPCSEC_GSS request with no integrity or privacy consists 
1079  * of just the header mbufs followed by the arg mbufs. 
1081  * However, integrity or privacy both trailer mbufs to the args, 
1082  * which means we have to do some work to restore the arg mbuf 
1083  * chain to its previous state in case we need to retransmit. 
1085  * The location and length of the args is marked by two fields 
1086  * in the request structure: r_gss_argoff and r_gss_arglen, 
1087  * which are stashed when the NFS request is built. 
1090 nfs_gss_clnt_args_restore(struct nfsreq 
*req
) 
1092         struct nfs_gss_clnt_ctx 
*cp 
= req
->r_gss_ctx
; 
1093         struct nfsm_chain mchain
, *nmc 
= &mchain
; 
1097                 return (NFSERR_EAUTH
); 
1099         if ((cp
->gss_clnt_flags 
& GSS_CTX_COMPLETE
) == 0) 
1102         nfsm_chain_dissect_init(error
, nmc
, req
->r_mhead
);      // start at RPC header 
1103         nfsm_chain_adv(error
, nmc
, req
->r_gss_argoff
);          // advance to args 
1107         switch (cp
->gss_clnt_service
) { 
1108         case RPCSEC_GSS_SVC_NONE
: 
1111         case RPCSEC_GSS_SVC_INTEGRITY
: 
1113                  * All we have to do here is remove the appended checksum mbufs. 
1114                  * We know that the checksum starts in a new mbuf beyond the end 
1117                 nfsm_chain_adv(error
, nmc
, req
->r_gss_arglen
);  // adv to last args mbuf 
1121                 mbuf_freem(mbuf_next(nmc
->nmc_mcur
));           // free the cksum mbuf 
1122                 error 
= mbuf_setnext(nmc
->nmc_mcur
, NULL
); 
1124         case RPCSEC_GSS_SVC_PRIVACY
: 
1126                  * The args are encrypted along with prepended confounders and seqnum. 
1127                  * First we decrypt, the confounder, seqnum and args then skip to the 
1128                  * final mbuf of the args. 
1129                  * The arglen includes 8 bytes of confounder and 4 bytes of seqnum. 
1130                  * Finally, we remove between 4 and 8 bytes of encryption padding 
1131                  * as well as any alignment padding in the trailing mbuf. 
1133                 len 
= req
->r_gss_arglen
; 
1134                 len 
+= len 
% 8 > 0 ? 4 : 8;                     // add DES padding length 
1135                 nfs_gss_encrypt_chain(cp
->gss_clnt_kinfo
, nmc
, 
1136                                         req
->r_gss_argoff
, len
, DES_DECRYPT
); 
1137                 nfsm_chain_adv(error
, nmc
, req
->r_gss_arglen
); 
1140                 mbuf_freem(mbuf_next(nmc
->nmc_mcur
));           // free the pad mbuf 
1141                 error 
= mbuf_setnext(nmc
->nmc_mcur
, NULL
); 
1149  * This function sets up  a new context on the client. 
1150  * Context setup alternates upcalls to the gssd with NFS nullproc calls 
1151  * to the server.  Each of these calls exchanges an opaque token, obtained 
1152  * via the gssd's calls into the GSS-API on either the client or the server. 
1153  * This cycle of calls ends when the client's upcall to the gssd and the 
1154  * server's response both return GSS_S_COMPLETE.  At this point, the client 
1155  * should have its session key and a handle that it can use to refer to its 
1156  * new context on the server. 
1159 nfs_gss_clnt_ctx_init(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
) 
1161         struct nfsmount 
*nmp 
= req
->r_nmp
; 
1162         int client_complete 
= 0; 
1163         int server_complete 
= 0; 
1164         u_char cksum1
[MAX_DIGEST
], cksum2
[MAX_DIGEST
]; 
1166         gss_key_info 
*ki 
= cp
->gss_clnt_kinfo
; 
1168         /* Initialize a new client context */ 
1170         if (cp
->gss_clnt_svcname 
== NULL
) { 
1171                 cp
->gss_clnt_svcname 
= nfs_gss_clnt_svcname(nmp
, &cp
->gss_clnt_svcnt
, &cp
->gss_clnt_svcnamlen
); 
1172                 if (cp
->gss_clnt_svcname 
== NULL
) { 
1173                         error 
= NFSERR_EAUTH
; 
1178         cp
->gss_clnt_proc 
= RPCSEC_GSS_INIT
; 
1180         cp
->gss_clnt_service 
= 
1181                 req
->r_auth 
== RPCAUTH_KRB5  
? RPCSEC_GSS_SVC_NONE 
: 
1182                 req
->r_auth 
== RPCAUTH_KRB5I 
? RPCSEC_GSS_SVC_INTEGRITY 
: 
1183                 req
->r_auth 
== RPCAUTH_KRB5P 
? RPCSEC_GSS_SVC_PRIVACY 
: 0; 
1185         cp
->gss_clnt_gssd_flags 
= (nfs_single_des 
? GSSD_NFS_1DES 
: 0); 
1187          * Now loop around alternating gss_init_sec_context and 
1188          * gss_accept_sec_context upcalls to the gssd on the client 
1189          * and server side until the context is complete - or fails. 
1194                 /* Upcall to the gss_init_sec_context in the gssd */ 
1195                 error 
= nfs_gss_clnt_gssd_upcall(req
, cp
); 
1199                 if (cp
->gss_clnt_major 
== GSS_S_COMPLETE
) { 
1200                         client_complete 
= 1; 
1201                         if (server_complete
) 
1203                 } else if (cp
->gss_clnt_major 
!= GSS_S_CONTINUE_NEEDED
) { 
1204                         error 
= NFSERR_EAUTH
; 
1209                  * Pass the token to the server. 
1211                 error 
= nfs_gss_clnt_ctx_callserver(req
, cp
); 
1213                         if (error 
== ENEEDAUTH 
&& cp
->gss_clnt_proc 
== RPCSEC_GSS_INIT 
&& 
1214                                 (cp
->gss_clnt_gssd_flags 
& (GSSD_RESTART 
| GSSD_NFS_1DES
)) == 0) { 
1215                                 NFS_GSS_DBG("Retrying with single DES for req %p\n", req
); 
1216                                 cp
->gss_clnt_gssd_flags 
= (GSSD_RESTART 
| GSSD_NFS_1DES
); 
1217                                 if (cp
->gss_clnt_token
) 
1218                                         FREE(cp
->gss_clnt_token
, M_TEMP
); 
1219                                 cp
->gss_clnt_token 
= NULL
; 
1220                                 cp
->gss_clnt_tokenlen 
= 0; 
1223                         // Reset flags, if error = ENEEDAUTH we will try 3des again 
1224                         cp
->gss_clnt_gssd_flags 
= 0;  
1227                 if (cp
->gss_clnt_major 
== GSS_S_COMPLETE
) { 
1228                         server_complete 
= 1; 
1229                         if (client_complete
) 
1232                 cp
->gss_clnt_proc 
= RPCSEC_GSS_CONTINUE_INIT
; 
1236          * The context is apparently established successfully 
1238         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1239         cp
->gss_clnt_flags 
|= GSS_CTX_COMPLETE
; 
1240         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1241         cp
->gss_clnt_proc 
= RPCSEC_GSS_DATA
; 
1244          * Compute checksum of the server's window 
1246         nfs_gss_cksum_rep(ki
, cp
->gss_clnt_seqwin
, cksum1
); 
1249          * and see if it matches the one in the 
1250          * verifier the server returned. 
1252         error 
= nfs_gss_token_get(ki
, ALG_MIC(ki
), cp
->gss_clnt_verf
, 0, 
1254         FREE(cp
->gss_clnt_verf
, M_TEMP
); 
1255         cp
->gss_clnt_verf 
= NULL
; 
1257         if (error 
|| bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
1258                 error 
= NFSERR_EAUTH
; 
1263          * Set an initial sequence number somewhat randomized. 
1264          * Start small so we don't overflow GSS_MAXSEQ too quickly. 
1265          * Add the size of the sequence window so seqbits arithmetic 
1266          * doesn't go negative. 
1268         cp
->gss_clnt_seqnum 
= (random() & 0xffff) + cp
->gss_clnt_seqwin
; 
1271          * Allocate a bitmap to keep track of which requests 
1272          * are pending within the sequence number window. 
1274         MALLOC(cp
->gss_clnt_seqbits
, uint32_t *, 
1275                 nfsm_rndup((cp
->gss_clnt_seqwin 
+ 7) / 8), M_TEMP
, M_WAITOK
|M_ZERO
); 
1276         if (cp
->gss_clnt_seqbits 
== NULL
) 
1277                 error 
= NFSERR_EAUTH
; 
1280          * If the error is ENEEDAUTH we're not done, so no need 
1281          * to wake up other threads again. This thread will retry in 
1282          * the find or renew routines. 
1284         if (error 
== ENEEDAUTH
) 
1288          * If there's an error, just mark it as invalid. 
1289          * It will be removed when the reference count 
1292         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1294                 cp
->gss_clnt_flags 
|= GSS_CTX_INVAL
; 
1297          * Wake any threads waiting to use the context 
1299         cp
->gss_clnt_thread 
= NULL
; 
1300         if (cp
->gss_clnt_flags 
& GSS_NEEDCTX
) { 
1301                 cp
->gss_clnt_flags 
&= ~GSS_NEEDCTX
; 
1304         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1310  * This function calls nfs_gss_clnt_ctx_init() to set up a new context. 
1311  * But if there's a failure in trying to establish the context it keeps 
1312  * retrying at progressively longer intervals in case the failure is 
1313  * due to some transient condition.  For instance, the server might be 
1314  * failing the context setup because directory services is not coming 
1315  * up in a timely fashion. 
1318 nfs_gss_clnt_ctx_init_retry(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
) 
1320         struct nfsmount 
*nmp 
= req
->r_nmp
; 
1325         int timeo 
= NFS_TRYLATERDEL
; 
1327         if (nfs_mount_gone(nmp
)) { 
1332         /* For an "intr" mount allow a signal to interrupt the retries */ 
1333         slpflag 
= (NMFLAG(nmp
, INTR
) && !(req
->r_flags 
& R_NOINTR
)) ? PCATCH 
: 0; 
1335         while ((error 
= nfs_gss_clnt_ctx_init(req
, cp
)) == ENEEDAUTH
) { 
1337                 waituntil 
= now
.tv_sec 
+ timeo
; 
1338                 while (now
.tv_sec 
< waituntil
) { 
1339                         tsleep(NULL
, PSOCK 
| slpflag
, "nfs_gss_clnt_ctx_init_retry", hz
); 
1341                         error 
= nfs_sigintr(req
->r_nmp
, req
, current_thread(), 0); 
1348                 /* If it's a soft mount just give up after a while */ 
1349                 if ((NMFLAG(nmp
, SOFT
) || (req
->r_flags 
& R_SOFT
)) && (retries 
> nmp
->nm_retry
)) { 
1359                 return 0;       // success 
1362          * Give up on this context 
1364         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1365         cp
->gss_clnt_flags 
|= GSS_CTX_INVAL
; 
1368          * Wake any threads waiting to use the context 
1370         cp
->gss_clnt_thread 
= NULL
; 
1371         if (cp
->gss_clnt_flags 
& GSS_NEEDCTX
) { 
1372                 cp
->gss_clnt_flags 
&= ~GSS_NEEDCTX
; 
1375         lck_mtx_unlock(cp
->gss_clnt_mtx
);                                
1381  * Call the NFS server using a null procedure for context setup. 
1382  * Even though it's a null procedure and nominally has no arguments 
1383  * RFC 2203 requires that the GSS-API token be passed as an argument 
1384  * and received as a reply. 
1387 nfs_gss_clnt_ctx_callserver(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
) 
1389         struct nfsm_chain nmreq
, nmrep
; 
1390         int error 
= 0, status
; 
1391         uint32_t major 
= cp
->gss_clnt_major
, minor 
= cp
->gss_clnt_minor
; 
1394         if (nfs_mount_gone(req
->r_nmp
)) 
1396         nfsm_chain_null(&nmreq
); 
1397         nfsm_chain_null(&nmrep
); 
1398         sz 
= NFSX_UNSIGNED 
+ nfsm_rndup(cp
->gss_clnt_tokenlen
); 
1399         nfsm_chain_build_alloc_init(error
, &nmreq
, sz
); 
1400         nfsm_chain_add_32(error
, &nmreq
, cp
->gss_clnt_tokenlen
); 
1401         if (cp
->gss_clnt_tokenlen 
> 0) 
1402                 nfsm_chain_add_opaque(error
, &nmreq
, cp
->gss_clnt_token
, cp
->gss_clnt_tokenlen
); 
1403         nfsm_chain_build_done(error
, &nmreq
); 
1407         /* Call the server */ 
1408         error 
= nfs_request_gss(req
->r_nmp
->nm_mountp
, &nmreq
, req
->r_thread
, req
->r_cred
,  
1409                                 (req
->r_flags 
& R_OPTMASK
), cp
, &nmrep
, &status
); 
1410         if (cp
->gss_clnt_token 
!= NULL
) { 
1411                 FREE(cp
->gss_clnt_token
, M_TEMP
); 
1412                 cp
->gss_clnt_token 
= NULL
; 
1419         /* Get the server's reply */ 
1421         nfsm_chain_get_32(error
, &nmrep
, cp
->gss_clnt_handle_len
); 
1422         if (cp
->gss_clnt_handle 
!= NULL
) { 
1423                 FREE(cp
->gss_clnt_handle
, M_TEMP
); 
1424                 cp
->gss_clnt_handle 
= NULL
; 
1426         if (cp
->gss_clnt_handle_len 
> 0) { 
1427                 MALLOC(cp
->gss_clnt_handle
, u_char 
*, cp
->gss_clnt_handle_len
, M_TEMP
, M_WAITOK
); 
1428                 if (cp
->gss_clnt_handle 
== NULL
) { 
1432                 nfsm_chain_get_opaque(error
, &nmrep
, cp
->gss_clnt_handle_len
, cp
->gss_clnt_handle
); 
1434         nfsm_chain_get_32(error
, &nmrep
, cp
->gss_clnt_major
); 
1435         nfsm_chain_get_32(error
, &nmrep
, cp
->gss_clnt_minor
); 
1436         nfsm_chain_get_32(error
, &nmrep
, cp
->gss_clnt_seqwin
); 
1437         nfsm_chain_get_32(error
, &nmrep
, cp
->gss_clnt_tokenlen
); 
1440         if (cp
->gss_clnt_tokenlen 
> 0) { 
1441                 MALLOC(cp
->gss_clnt_token
, u_char 
*, cp
->gss_clnt_tokenlen
, M_TEMP
, M_WAITOK
); 
1442                 if (cp
->gss_clnt_token 
== NULL
) { 
1446                 nfsm_chain_get_opaque(error
, &nmrep
, cp
->gss_clnt_tokenlen
, cp
->gss_clnt_token
); 
1450          * Make sure any unusual errors are expanded and logged by gssd 
1452         if (cp
->gss_clnt_major 
!= GSS_S_COMPLETE 
&& 
1453             cp
->gss_clnt_major 
!= GSS_S_CONTINUE_NEEDED
) { 
1455                 printf("nfs_gss_clnt_ctx_callserver: gss_clnt_major = %d\n", cp
->gss_clnt_major
); 
1456                 nfs_gss_clnt_log_error(req
, cp
, major
, minor
); 
1461         nfsm_chain_cleanup(&nmreq
); 
1462         nfsm_chain_cleanup(&nmrep
); 
1468  * We construct the service principal as a gss hostbased service principal of 
1469  * the form nfs@<server>, unless the servers principal was passed down in the 
1470  * mount arguments. If the arguments don't specify the service principal, the 
1471  * server name is extracted the location passed in the mount argument if 
1472  * available.  Otherwise assume a format of <server>:<path> in the 
1473  * mntfromname. We don't currently support url's or other bizarre formats like 
1474  * path@server. Mount_url will convert the nfs url into <server>:<path> when 
1475  * calling mount, so this works out well in practice. 
1480 nfs_gss_clnt_svcname(struct nfsmount 
*nmp
, gssd_nametype 
*nt
, uint32_t *len
) 
1482         char *svcname
, *d
, *server
; 
1485         if (nfs_mount_gone(nmp
)) 
1488         if (nmp
->nm_sprinc
) { 
1489                 *len 
= strlen(nmp
->nm_sprinc
) + 1; 
1490                 MALLOC(svcname
, char *, *len
, M_TEMP
, M_WAITOK
); 
1491                 *nt 
= GSSD_HOSTBASED
; 
1492                 if (svcname 
== NULL
) 
1494                 strlcpy(svcname
, nmp
->nm_sprinc
, *len
); 
1496                 return ((uint8_t *)svcname
); 
1499         *nt 
= GSSD_HOSTBASED
; 
1500         if (nmp
->nm_locations
.nl_numlocs 
&& !(NFS_GSS_ISDBG 
&& (NFS_DEBUG_FLAGS 
& 0x1))) { 
1501                 lindx 
= nmp
->nm_locations
.nl_current
.nli_loc
; 
1502                 sindx 
= nmp
->nm_locations
.nl_current
.nli_serv
; 
1503                 server 
= nmp
->nm_locations
.nl_locations
[lindx
]->nl_servers
[sindx
]->ns_name
; 
1504                 *len 
= (uint32_t)strlen(server
); 
1506                 /* Older binaries using older mount args end up here */ 
1507                 server 
= vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
; 
1508                 NFS_GSS_DBG("nfs getting gss svcname from %s\n", server
); 
1509                 d 
= strchr(server
, ':'); 
1510                 *len 
= (uint32_t)(d 
? (d 
- server
) : strlen(server
)); 
1513         *len 
+=  5; /* "nfs@" plus null */ 
1514         MALLOC(svcname
, char *, *len
, M_TEMP
, M_WAITOK
); 
1515         strlcpy(svcname
, "nfs", *len
); 
1516         strlcat(svcname
, "@", *len
); 
1517         strlcat(svcname
, server
, *len
); 
1518         NFS_GSS_DBG("nfs svcname = %s\n", svcname
); 
1520         return ((uint8_t *)svcname
); 
1524  * Get a mach port to talk to gssd. 
1525  * gssd lives in the root bootstrap, so we call gssd's lookup routine 
1526  * to get a send right to talk to a new gssd instance that launchd has launched 
1527  * based on the cred's uid and audit session id. 
1531 nfs_gss_clnt_get_upcall_port(kauth_cred_t credp
) 
1533         mach_port_t gssd_host_port
, uc_port 
= IPC_PORT_NULL
; 
1538         kr 
= host_get_gssd_port(host_priv_self(), &gssd_host_port
); 
1539         if (kr 
!= KERN_SUCCESS
) { 
1540                 printf("nfs_gss_get_upcall_port: can't get gssd port, status %x (%d)\n", kr
, kr
); 
1541                 return (IPC_PORT_NULL
); 
1543         if (!IPC_PORT_VALID(gssd_host_port
)) { 
1544                 printf("nfs_gss_get_upcall_port: gssd port not valid\n"); 
1545                 return (IPC_PORT_NULL
); 
1548         asid 
= kauth_cred_getasid(credp
); 
1549         uid 
= kauth_cred_getauid(credp
); 
1550         if (uid 
== AU_DEFAUDITID
) 
1551                 uid 
= kauth_cred_getuid(credp
); 
1552         kr 
= mach_gss_lookup(gssd_host_port
, uid
, asid
, &uc_port
); 
1553         if (kr 
!= KERN_SUCCESS
) 
1554                 printf("nfs_gss_clnt_get_upcall_port: mach_gssd_lookup failed: status %x (%d)\n", kr
, kr
); 
1555         host_release_special_port(gssd_host_port
); 
1562 nfs_gss_clnt_log_error(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
, uint32_t major
, uint32_t minor
) 
1564 #define GETMAJERROR(x) (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)  
1565         struct nfsmount 
*nmp 
= req
->r_nmp
; 
1566         char who
[] = "client"; 
1567         uint32_t gss_error 
= GETMAJERROR(cp
->gss_clnt_major
); 
1568         const char *procn 
= "unkown"; 
1573         if (req
->r_thread
) { 
1574                 proc 
= (proc_t
)get_bsdthreadtask_info(req
->r_thread
); 
1575                 if (proc 
!= NULL 
&& (proc
->p_fd 
== NULL 
|| (proc
->p_lflag 
& P_LVFORK
))) 
1579                                 procn 
= proc
->p_comm
; 
1588         if ((cp
->gss_clnt_major 
!= major 
|| cp
->gss_clnt_minor 
!= minor 
|| 
1589              cp
->gss_clnt_ptime 
+ GSS_PRINT_DELAY 
< now
.tv_sec
) && 
1590             (nmp
->nm_state 
& NFSSTA_MOUNTED
)) { 
1592                  * Will let gssd do some logging in hopes that it can translate 
1595                 if (cp
->gss_clnt_minor 
&& cp
->gss_clnt_minor 
!= minor
) { 
1596                         (void) mach_gss_log_error( 
1598                                 vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
, 
1599                                 kauth_cred_getuid(cp
->gss_clnt_cred
), 
1602                                 cp
->gss_clnt_minor
); 
1604                 gss_error 
= gss_error 
? gss_error 
: cp
->gss_clnt_major
; 
1607                  *%%% It would be really nice to get the terminal from the proc or auditinfo_addr struct and print that here. 
1609                 printf("NFS: gssd auth failure by %s on audit session %d uid %d proc %s/%d for mount %s. Error: major = %d minor = %d\n", 
1610                        cp
->gss_clnt_display 
? cp
->gss_clnt_display 
: who
, kauth_cred_getasid(req
->r_cred
), kauth_cred_getuid(req
->r_cred
), 
1611                        procn
, pid
, vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
, gss_error
, (int32_t)cp
->gss_clnt_minor
); 
1612                 cp
->gss_clnt_ptime 
= now
.tv_sec
; 
1613                 switch (gss_error
) { 
1614                 case 7: printf("NFS: gssd does not have credentials for session %d/%d, (kinit)?\n", 
1615                                kauth_cred_getasid(req
->r_cred
), kauth_cred_getauid(req
->r_cred
)); 
1617                 case 11: printf("NFS: gssd has expired credentals for session %d/%d, (kinit)?\n", 
1618                                kauth_cred_getasid(req
->r_cred
), kauth_cred_getauid(req
->r_cred
)); 
1622                 NFS_GSS_DBG("NFS: gssd auth failure by %s on audit session %d uid %d proc %s/%d for mount %s. Error: major = %d minor = %d\n", 
1623                             cp
->gss_clnt_display 
? cp
->gss_clnt_display 
: who
, kauth_cred_getasid(req
->r_cred
), kauth_cred_getuid(req
->r_cred
), 
1624                             procn
, pid
, vfs_statfs(nmp
->nm_mountp
)->f_mntfromname
, gss_error
, (int32_t)cp
->gss_clnt_minor
); 
1629  * Make an upcall to the gssd using Mach RPC 
1630  * The upcall is made using a host special port. 
1631  * This allows launchd to fire up the gssd in the 
1632  * user's session.  This is important, since gssd 
1633  * must have access to the user's credential cache. 
1636 nfs_gss_clnt_gssd_upcall(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
) 
1639         gssd_byte_buffer okey 
= NULL
; 
1640         uint32_t skeylen 
= 0; 
1642         vm_map_copy_t itoken 
= NULL
; 
1643         gssd_byte_buffer otoken 
= NULL
; 
1644         mach_msg_type_number_t otokenlen
; 
1646         uint8_t *principal 
= NULL
; 
1648         int32_t nt 
= GSSD_STRING_NAME
; 
1649         vm_map_copy_t pname 
= NULL
; 
1650         vm_map_copy_t svcname 
= NULL
; 
1651         char display_name
[MAX_DISPLAY_STR
] = ""; 
1653         uint32_t nfs_1des 
= (cp
->gss_clnt_gssd_flags 
& GSSD_NFS_1DES
); 
1654         struct nfsmount 
*nmp
; 
1655         uint32_t major 
= cp
->gss_clnt_major
, minor 
= cp
->gss_clnt_minor
; 
1658          * NFS currently only supports default principals or 
1659          * principals based on the uid of the caller, unless 
1660          * the principal to use for the mounting cred was specified 
1661          * in the mount argmuments. If the realm to use was specified 
1662          * then will send that up as the principal since the realm is 
1663          * preceed by an "@" gssd that will try and select the default 
1664          * principal for that realm. 
1668         if (nmp 
== NULL 
|| vfs_isforce(nmp
->nm_mountp
) || (nmp
->nm_state 
& (NFSSTA_FORCE 
| NFSSTA_DEAD
))) 
1671         if (cp
->gss_clnt_principal 
&& cp
->gss_clnt_prinlen
) { 
1672                 principal 
= cp
->gss_clnt_principal
; 
1673                 plen 
= cp
->gss_clnt_prinlen
; 
1674                 nt 
= cp
->gss_clnt_prinnt
; 
1675         } else if (nmp
->nm_principal 
&& IS_VALID_CRED(nmp
->nm_mcred
) && req
->r_cred 
== nmp
->nm_mcred
) { 
1676                 plen 
= (uint32_t)strlen(nmp
->nm_principal
); 
1677                 MALLOC(principal
, uint8_t *, plen
, M_TEMP
, M_WAITOK 
| M_ZERO
); 
1678                 if (principal 
== NULL
) 
1680                 bcopy(nmp
->nm_principal
, principal
, plen
); 
1681                 cp
->gss_clnt_prinnt 
= nt 
= GSSD_USER
; 
1683         else if (nmp
->nm_realm
) { 
1684                 plen 
= (uint32_t)strlen(nmp
->nm_realm
); 
1685                 principal 
= (uint8_t *)nmp
->nm_realm
; 
1689         if (!IPC_PORT_VALID(cp
->gss_clnt_mport
)) { 
1690                 cp
->gss_clnt_mport 
= nfs_gss_clnt_get_upcall_port(req
->r_cred
); 
1691                 if (cp
->gss_clnt_mport 
== IPC_PORT_NULL
) 
1696                 nfs_gss_mach_alloc_buffer(principal
, plen
, &pname
); 
1697         if (cp
->gss_clnt_svcnamlen
) 
1698                 nfs_gss_mach_alloc_buffer(cp
->gss_clnt_svcname
, cp
->gss_clnt_svcnamlen
, &svcname
); 
1699         if (cp
->gss_clnt_tokenlen
) 
1700                 nfs_gss_mach_alloc_buffer(cp
->gss_clnt_token
, cp
->gss_clnt_tokenlen
, &itoken
); 
1703         kr 
= mach_gss_init_sec_context_v2( 
1706                 (gssd_byte_buffer
) itoken
, (mach_msg_type_number_t
) cp
->gss_clnt_tokenlen
, 
1707                 kauth_cred_getuid(cp
->gss_clnt_cred
), 
1709                 (gssd_byte_buffer
)pname
, (mach_msg_type_number_t
) plen
, 
1711                 (gssd_byte_buffer
)svcname
, (mach_msg_type_number_t
) cp
->gss_clnt_svcnamlen
, 
1713                 &cp
->gss_clnt_gssd_flags
, 
1714                 &cp
->gss_clnt_context
, 
1715                 &cp
->gss_clnt_cred_handle
, 
1717                 &okey
,  (mach_msg_type_number_t 
*) &skeylen
, 
1718                 &otoken
, &otokenlen
, 
1719                 cp
->gss_clnt_display 
? NULL 
: display_name
, 
1720                 &cp
->gss_clnt_major
, 
1721                 &cp
->gss_clnt_minor
); 
1723         /* Should be cleared and set in gssd ? */ 
1724         cp
->gss_clnt_gssd_flags 
&= ~GSSD_RESTART
; 
1725         cp
->gss_clnt_gssd_flags 
|= nfs_1des
; 
1727         if (kr 
!= KERN_SUCCESS
) { 
1728                 printf("nfs_gss_clnt_gssd_upcall: mach_gss_init_sec_context failed: %x (%d)\n", kr
, kr
); 
1729                 if (kr 
== MIG_SERVER_DIED 
&& cp
->gss_clnt_cred_handle 
== 0 && 
1730                         retry_cnt
++ < NFS_GSS_MACH_MAX_RETRIES 
&& 
1731                         !vfs_isforce(nmp
->nm_mountp
) && (nmp
->nm_state 
& (NFSSTA_FORCE 
| NFSSTA_DEAD
)) == 0) { 
1733                                 nfs_gss_mach_alloc_buffer(principal
, plen
, &pname
); 
1734                         if (cp
->gss_clnt_svcnamlen
) 
1735                                 nfs_gss_mach_alloc_buffer(cp
->gss_clnt_svcname
, cp
->gss_clnt_svcnamlen
, &svcname
); 
1736                         if (cp
->gss_clnt_tokenlen 
> 0) 
1737                                 nfs_gss_mach_alloc_buffer(cp
->gss_clnt_token
, cp
->gss_clnt_tokenlen
, &itoken
); 
1741                 host_release_special_port(cp
->gss_clnt_mport
); 
1742                 cp
->gss_clnt_mport 
= IPC_PORT_NULL
; 
1746         if (cp
->gss_clnt_display 
== NULL 
&& *display_name 
!= '\0') { 
1747                 int dlen 
= strnlen(display_name
, MAX_DISPLAY_STR
) + 1;  /* Add extra byte to include '\0' */ 
1749                 if (dlen 
< MAX_DISPLAY_STR
) { 
1750                         MALLOC(cp
->gss_clnt_display
, char *, dlen
, M_TEMP
, M_WAITOK
); 
1751                         if (cp
->gss_clnt_display 
== NULL
) 
1753                         bcopy(display_name
, cp
->gss_clnt_display
, dlen
);  
1760          * Make sure any unusual errors are expanded and logged by gssd 
1762          * XXXX, we need to rethink this and just have gssd return a string for the major and minor codes. 
1764         if (cp
->gss_clnt_major 
!= GSS_S_COMPLETE 
&& 
1765             cp
->gss_clnt_major 
!= GSS_S_CONTINUE_NEEDED
) { 
1766                 nfs_gss_clnt_log_error(req
, cp
, major
, minor
); 
1770                 if (skeylen 
!= SKEYLEN 
&& skeylen 
!= SKEYLEN3
) { 
1771                         printf("nfs_gss_clnt_gssd_upcall: bad key length (%d)\n", skeylen
); 
1772                         vm_map_copy_discard((vm_map_copy_t
) okey
); 
1773                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
1776                 error 
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) okey
, skeylen
,  
1777                                 cp
->gss_clnt_kinfo
->skey
); 
1779                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
1783                 error 
= gss_key_init(cp
->gss_clnt_kinfo
, skeylen
); 
1788         /* Free context token used as input */ 
1789         if (cp
->gss_clnt_token
) 
1790                 FREE(cp
->gss_clnt_token
, M_TEMP
); 
1791         cp
->gss_clnt_token 
= NULL
; 
1792         cp
->gss_clnt_tokenlen 
= 0; 
1794         if (otokenlen 
> 0) { 
1795                 /* Set context token to gss output token */ 
1796                 MALLOC(cp
->gss_clnt_token
, u_char 
*, otokenlen
, M_TEMP
, M_WAITOK
); 
1797                 if (cp
->gss_clnt_token 
== NULL
) { 
1798                         printf("nfs_gss_clnt_gssd_upcall: could not allocate %d bytes\n", otokenlen
); 
1799                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
1802                 error 
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) otoken
, otokenlen
, cp
->gss_clnt_token
); 
1804                         FREE(cp
->gss_clnt_token
, M_TEMP
); 
1805                         cp
->gss_clnt_token 
= NULL
; 
1806                         return (NFSERR_EAUTH
); 
1808                 cp
->gss_clnt_tokenlen 
= otokenlen
; 
1814         if (cp
->gss_clnt_token
) 
1815                 FREE(cp
->gss_clnt_token
, M_TEMP
); 
1816         cp
->gss_clnt_token 
= NULL
; 
1817         cp
->gss_clnt_tokenlen 
= 0; 
1819         return (NFSERR_EAUTH
); 
1823  * Invoked at the completion of an RPC call that uses an RPCSEC_GSS 
1824  * credential. The sequence number window that the server returns 
1825  * at context setup indicates the maximum number of client calls that 
1826  * can be outstanding on a context. The client maintains a bitmap that 
1827  * represents the server's window.  Each pending request has a bit set 
1828  * in the window bitmap.  When a reply comes in or times out, we reset 
1829  * the bit in the bitmap and if there are any other threads waiting for 
1830  * a context slot we notify the waiting thread(s). 
1832  * Note that if a request is retransmitted, it will have a single XID 
1833  * but it may be associated with multiple sequence numbers.  So we 
1834  * may have to reset multiple sequence number bits in the window bitmap. 
1837 nfs_gss_clnt_rpcdone(struct nfsreq 
*req
) 
1839         struct nfs_gss_clnt_ctx 
*cp 
= req
->r_gss_ctx
; 
1840         struct gss_seq 
*gsp
, *ngsp
; 
1843         if (cp 
== NULL 
|| !(cp
->gss_clnt_flags 
& GSS_CTX_COMPLETE
)) 
1844                 return; // no context - don't bother 
1846          * Reset the bit for this request in the 
1847          * sequence number window to indicate it's done. 
1848          * We do this even if the request timed out. 
1850         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1851         gsp 
= SLIST_FIRST(&req
->r_gss_seqlist
); 
1852         if (gsp 
&& gsp
->gss_seqnum 
> (cp
->gss_clnt_seqnum 
- cp
->gss_clnt_seqwin
)) 
1853                 win_resetbit(cp
->gss_clnt_seqbits
, 
1854                         gsp
->gss_seqnum 
% cp
->gss_clnt_seqwin
); 
1857          * Limit the seqnum list to GSS_CLNT_SEQLISTMAX entries 
1859         SLIST_FOREACH_SAFE(gsp
, &req
->r_gss_seqlist
, gss_seqnext
, ngsp
) { 
1860                 if (++i 
> GSS_CLNT_SEQLISTMAX
) { 
1861                         SLIST_REMOVE(&req
->r_gss_seqlist
, gsp
, gss_seq
, gss_seqnext
); 
1867          * If there's a thread waiting for 
1868          * the window to advance, wake it up. 
1870         if (cp
->gss_clnt_flags 
& GSS_NEEDSEQ
) { 
1871                 cp
->gss_clnt_flags 
&= ~GSS_NEEDSEQ
; 
1874         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1878  * Create a reference to a context from a request 
1879  * and bump the reference count 
1882 nfs_gss_clnt_ctx_ref(struct nfsreq 
*req
, struct nfs_gss_clnt_ctx 
*cp
) 
1884         req
->r_gss_ctx 
= cp
; 
1886         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1887         cp
->gss_clnt_refcnt
++; 
1888         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1892  * Remove a context reference from a request 
1893  * If the reference count drops to zero, and the 
1894  * context is invalid, destroy the context 
1897 nfs_gss_clnt_ctx_unref(struct nfsreq 
*req
) 
1899         struct nfsmount 
*nmp 
= req
->r_nmp
; 
1900         struct nfs_gss_clnt_ctx 
*cp 
= req
->r_gss_ctx
; 
1901         int on_neg_cache 
= 0; 
1905         char CTXBUF
[NFS_CTXBUFSZ
]; 
1910         req
->r_gss_ctx 
= NULL
; 
1912         lck_mtx_lock(cp
->gss_clnt_mtx
); 
1913         if (--cp
->gss_clnt_refcnt 
< 0) 
1914                 panic("Over release of gss context!\n"); 
1916         if (cp
->gss_clnt_refcnt 
== 0) { 
1917                 if ((cp
->gss_clnt_flags 
& GSS_CTX_INVAL
) && 
1918                     cp
->gss_clnt_kinfo
) { 
1919                         FREE(cp
->gss_clnt_kinfo
, M_TEMP
); 
1920                         cp
->gss_clnt_kinfo 
= NULL
; 
1922                 if (cp
->gss_clnt_flags 
& GSS_CTX_DESTROY
) { 
1924                         if (cp
->gss_clnt_flags 
& GSS_CTX_STICKY
) 
1925                                 nfs_gss_clnt_mnt_rele(nmp
); 
1926                         if (cp
->gss_clnt_nctime
) 
1930         if (!destroy 
&& cp
->gss_clnt_nctime 
== 0 && 
1931             (cp
->gss_clnt_flags 
& GSS_CTX_INVAL
)) { 
1933                 cp
->gss_clnt_nctime 
= now
.tv_sec
; 
1936         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1938                 NFS_GSS_DBG("Destroying context %s\n", NFS_GSS_CTX(req
, cp
)); 
1940                         lck_mtx_lock(&nmp
->nm_lock
); 
1941                         if (cp
->gss_clnt_entries
.tqe_next 
!= NFSNOLIST
) { 
1942                                 TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
1945                                 nmp
->nm_ncentries
--; 
1947                         lck_mtx_unlock(&nmp
->nm_lock
); 
1949                 nfs_gss_clnt_ctx_destroy(cp
); 
1950         } else if (neg_cache
) { 
1951                 NFS_GSS_DBG("Entering context %s into negative cache\n", NFS_GSS_CTX(req
, cp
)); 
1953                         lck_mtx_lock(&nmp
->nm_lock
); 
1954                         nmp
->nm_ncentries
++; 
1955                         nfs_gss_clnt_ctx_neg_cache_reap(nmp
); 
1956                         lck_mtx_unlock(&nmp
->nm_lock
); 
1959         NFS_GSS_CLNT_CTX_DUMP(nmp
); 
1963  * Try and reap any old negative cache entries. 
1967 nfs_gss_clnt_ctx_neg_cache_reap(struct nfsmount 
*nmp
) 
1969         struct nfs_gss_clnt_ctx 
*cp
, *tcp
; 
1973         NFS_GSS_DBG("Reaping contexts ncentries = %d\n", nmp
->nm_ncentries
); 
1974         /* Try and reap old, unreferenced, expired contexts */ 
1976         TAILQ_FOREACH_SAFE(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
, tcp
) { 
1979                 /* Don't reap STICKY contexts */ 
1980                 if ((cp
->gss_clnt_flags 
& GSS_CTX_STICKY
) || 
1981                     !(cp
->gss_clnt_flags 
& GSS_CTX_INVAL
)) 
1983                 /* Keep up to GSS_MAX_NEG_CACHE_ENTRIES */ 
1984                 if (nmp
->nm_ncentries 
<= GSS_MAX_NEG_CACHE_ENTRIES
) 
1986                 /* Contexts too young */ 
1987                 if (cp
->gss_clnt_nctime 
+ GSS_NEG_CACHE_TO 
>= now
.tv_sec
) 
1989                 /* Not referenced, remove it. */ 
1990                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
1991                 if (cp
->gss_clnt_refcnt 
== 0) { 
1992                         cp
->gss_clnt_flags 
|= GSS_CTX_DESTROY
; 
1995                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
1997                         TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
1998                         nmp
->nm_ncentries
++; 
2000                         nfs_gss_clnt_ctx_destroy(cp
); 
2003         NFS_GSS_DBG("Reaped %d contexts ncentries = %d\n", reaped
, nmp
->nm_ncentries
); 
2007  * Clean a context to be cached 
2010 nfs_gss_clnt_ctx_clean(struct nfs_gss_clnt_ctx 
*cp
) 
2012         /* Preserve gss_clnt_mtx */ 
2013         assert(cp
->gss_clnt_thread 
== NULL
);  /* Will be set to this thread */ 
2014         /* gss_clnt_entries  we should not be on any list at this point */ 
2015         cp
->gss_clnt_flags 
= 0; 
2016         /* gss_clnt_refcnt should be zero */ 
2017         assert(cp
->gss_clnt_refcnt 
== 0); 
2019          * We are who we are preserve: 
2021          * gss_clnt_principal 
2026         /* gss_clnt_proc will be set in nfs_gss_clnt_ctx_init */ 
2027         cp
->gss_clnt_seqnum 
= 0; 
2028         /* Preserve gss_clnt_service, we're not changing flavors */ 
2029         if (cp
->gss_clnt_handle
) { 
2030                 FREE(cp
->gss_clnt_handle
, M_TEMP
); 
2031                 cp
->gss_clnt_handle 
= NULL
; 
2033         cp
->gss_clnt_handle_len 
= 0; 
2034         cp
->gss_clnt_nctime 
= 0; 
2035         cp
->gss_clnt_seqwin 
= 0; 
2036         if (cp
->gss_clnt_seqbits
) { 
2037                 FREE(cp
->gss_clnt_seqbits
, M_TEMP
); 
2038                 cp
->gss_clnt_seqbits 
= NULL
; 
2040         /* Preserve gss_clnt_mport. Still talking to the same gssd */ 
2041         if (cp
->gss_clnt_verf
) { 
2042                 FREE(cp
->gss_clnt_verf
, M_TEMP
); 
2043                 cp
->gss_clnt_verf 
= NULL
; 
2045         /* Service name might change on failover, so reset it */ 
2046         if (cp
->gss_clnt_svcname
) { 
2047                 FREE(cp
->gss_clnt_svcname
, M_TEMP
); 
2048                 cp
->gss_clnt_svcname 
= NULL
; 
2049                 cp
->gss_clnt_svcnt 
= 0; 
2051         cp
->gss_clnt_svcnamlen 
= 0; 
2052         cp
->gss_clnt_cred_handle 
= 0; 
2053         cp
->gss_clnt_context 
= 0; 
2054         if (cp
->gss_clnt_token
) { 
2055                 FREE(cp
->gss_clnt_token
, M_TEMP
); 
2056                 cp
->gss_clnt_token 
= NULL
; 
2058         cp
->gss_clnt_tokenlen 
= 0; 
2059         if (cp
->gss_clnt_kinfo
) 
2060                 bzero(cp
->gss_clnt_kinfo
, sizeof(gss_key_info
)); 
2063          * gss_clnt_gssd_flags 
2071  * Copy a source context to a new context. This is used to create a new context 
2072  * with the identity of the old context for renewal. The old context is invalid 
2073  * at this point but may have reference still to it, so it is not safe to use that 
2077 nfs_gss_clnt_ctx_copy(struct nfs_gss_clnt_ctx 
*scp
, struct nfs_gss_clnt_ctx 
**dcpp
, gss_key_info 
*ki
) 
2079         struct nfs_gss_clnt_ctx 
*dcp
; 
2081         *dcpp 
= (struct nfs_gss_clnt_ctx 
*)NULL
; 
2082         MALLOC(dcp
, struct nfs_gss_clnt_ctx 
*, sizeof (struct nfs_gss_clnt_ctx
), M_TEMP
, M_WAITOK
); 
2085         bzero(dcp
, sizeof (struct nfs_gss_clnt_ctx
)); 
2087                 MALLOC(dcp
->gss_clnt_kinfo
, gss_key_info 
*, sizeof (gss_key_info
), M_TEMP
, M_WAITOK
); 
2088                 if (dcp
->gss_clnt_kinfo 
== NULL
) { 
2093                 dcp
->gss_clnt_kinfo 
= ki
; 
2095         bzero(dcp
->gss_clnt_kinfo
, sizeof (gss_key_info
)); 
2096         dcp
->gss_clnt_mtx 
= lck_mtx_alloc_init(nfs_gss_clnt_grp
, LCK_ATTR_NULL
); 
2097         dcp
->gss_clnt_cred 
= scp
->gss_clnt_cred
; 
2098         kauth_cred_ref(dcp
->gss_clnt_cred
); 
2099         dcp
->gss_clnt_prinlen 
= scp
->gss_clnt_prinlen
; 
2100         dcp
->gss_clnt_prinnt 
= scp
->gss_clnt_prinnt
; 
2101         if (scp
->gss_clnt_principal
) { 
2102                 MALLOC(dcp
->gss_clnt_principal
, uint8_t *, dcp
->gss_clnt_prinlen
, M_TEMP
, M_WAITOK 
| M_ZERO
); 
2103                 if (dcp
->gss_clnt_principal 
== NULL
) { 
2104                         FREE(dcp
->gss_clnt_kinfo
, M_TEMP
); 
2108                 bcopy(scp
->gss_clnt_principal
, dcp
->gss_clnt_principal
, dcp
->gss_clnt_prinlen
); 
2110         /* Note we don't preserve the display name, that will be set by a successful up call */ 
2111         dcp
->gss_clnt_service 
= scp
->gss_clnt_service
; 
2112         dcp
->gss_clnt_mport 
= host_copy_special_port(scp
->gss_clnt_mport
); 
2113         /*  gss_clnt_kinfo allocated above */ 
2114         dcp
->gss_clnt_gssd_flags 
= scp
->gss_clnt_gssd_flags
; 
2115         dcp
->gss_clnt_major 
= scp
->gss_clnt_major
; 
2116         dcp
->gss_clnt_minor 
= scp
->gss_clnt_minor
; 
2117         dcp
->gss_clnt_ptime 
= scp
->gss_clnt_ptime
; 
2128 nfs_gss_clnt_ctx_destroy(struct nfs_gss_clnt_ctx 
*cp
) 
2130         NFS_GSS_DBG("Destroying context %d/%d\n", 
2131                     kauth_cred_getasid(cp
->gss_clnt_cred
), 
2132                     kauth_cred_getauid(cp
->gss_clnt_cred
)); 
2134         host_release_special_port(cp
->gss_clnt_mport
); 
2135         cp
->gss_clnt_mport 
= IPC_PORT_NULL
; 
2137         if (cp
->gss_clnt_mtx
) { 
2138                 lck_mtx_destroy(cp
->gss_clnt_mtx
, nfs_gss_clnt_grp
); 
2139                 cp
->gss_clnt_mtx 
= (lck_mtx_t 
*)NULL
; 
2141         if (IS_VALID_CRED(cp
->gss_clnt_cred
)) 
2142                 kauth_cred_unref(&cp
->gss_clnt_cred
); 
2143         cp
->gss_clnt_entries
.tqe_next 
= NFSNOLIST
; 
2144         cp
->gss_clnt_entries
.tqe_prev 
= NFSNOLIST
; 
2145         if (cp
->gss_clnt_principal
) { 
2146                 FREE(cp
->gss_clnt_principal
, M_TEMP
); 
2147                 cp
->gss_clnt_principal 
= NULL
; 
2149         if (cp
->gss_clnt_display
) { 
2150                 FREE(cp
->gss_clnt_display
, M_TEMP
); 
2151                 cp
->gss_clnt_display 
= NULL
; 
2153         if (cp
->gss_clnt_kinfo
) { 
2154                 FREE(cp
->gss_clnt_kinfo
, M_TEMP
); 
2155                 cp
->gss_clnt_kinfo 
= NULL
; 
2158         nfs_gss_clnt_ctx_clean(cp
); 
2164  * The context for a user is invalid. 
2165  * Mark the context as invalid, then 
2166  * create a new context. 
2169 nfs_gss_clnt_ctx_renew(struct nfsreq 
*req
) 
2171         struct nfs_gss_clnt_ctx 
*cp 
= req
->r_gss_ctx
; 
2172         struct nfs_gss_clnt_ctx 
*ncp
; 
2173         struct nfsmount 
*nmp
; 
2175         char CTXBUF
[NFS_CTXBUFSZ
]; 
2180         if (req
->r_nmp 
== NULL
) 
2184         lck_mtx_lock(cp
->gss_clnt_mtx
); 
2185         if (cp
->gss_clnt_flags 
& GSS_CTX_INVAL
) { 
2186                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2187                 nfs_gss_clnt_ctx_unref(req
); 
2188                 return (0);     // already being renewed 
2191         cp
->gss_clnt_flags 
|= (GSS_CTX_INVAL 
| GSS_CTX_DESTROY
); 
2193         if (cp
->gss_clnt_flags 
& (GSS_NEEDCTX 
| GSS_NEEDSEQ
)) { 
2194                 cp
->gss_clnt_flags 
&= ~GSS_NEEDSEQ
; 
2197         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2199         error 
=  nfs_gss_clnt_ctx_copy(cp
, &ncp
, NULL
); 
2200         NFS_GSS_DBG("Renewing context %s\n", NFS_GSS_CTX(req
, ncp
)); 
2201         nfs_gss_clnt_ctx_unref(req
); 
2205         lck_mtx_lock(&nmp
->nm_lock
); 
2207          * Note we don't bother taking the new context mutex as we're 
2208          * not findable at the moment. 
2210         ncp
->gss_clnt_thread 
= current_thread(); 
2211         nfs_gss_clnt_ctx_ref(req
, ncp
); 
2212         TAILQ_INSERT_HEAD(&nmp
->nm_gsscl
, ncp
, gss_clnt_entries
); 
2213         lck_mtx_unlock(&nmp
->nm_lock
); 
2215         error 
= nfs_gss_clnt_ctx_init_retry(req
, ncp
); // Initialize new context 
2217                 nfs_gss_clnt_ctx_unref(req
); 
2224  * Destroy all the contexts associated with a mount. 
2225  * The contexts are also destroyed by the server. 
2228 nfs_gss_clnt_ctx_unmount(struct nfsmount 
*nmp
) 
2230         struct nfs_gss_clnt_ctx 
*cp
; 
2231         struct nfsm_chain nmreq
, nmrep
; 
2240         lck_mtx_lock(&nmp
->nm_lock
); 
2241         while((cp 
= TAILQ_FIRST(&nmp
->nm_gsscl
))) { 
2242                 TAILQ_REMOVE(&nmp
->nm_gsscl
, cp
, gss_clnt_entries
); 
2243                 cp
->gss_clnt_entries
.tqe_next 
= NFSNOLIST
; 
2244                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
2245                 if (cp
->gss_clnt_flags 
& GSS_CTX_DESTROY
) { 
2246                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2249                 cp
->gss_clnt_refcnt
++; 
2250                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2253                 lck_mtx_unlock(&nmp
->nm_lock
); 
2255                  * Tell the server to destroy its context. 
2256                  * But don't bother if it's a forced unmount. 
2258                 if (!nfs_mount_gone(nmp
) && 
2259                     (cp
->gss_clnt_flags 
& (GSS_CTX_INVAL 
| GSS_CTX_DESTROY 
| GSS_CTX_COMPLETE
)) == GSS_CTX_COMPLETE
) { 
2260                         cp
->gss_clnt_proc 
= RPCSEC_GSS_DESTROY
; 
2263                         nfsm_chain_null(&nmreq
); 
2264                         nfsm_chain_null(&nmrep
); 
2265                         nfsm_chain_build_alloc_init(error
, &nmreq
, 0); 
2266                         nfsm_chain_build_done(error
, &nmreq
); 
2268                                 nfs_request_gss(nmp
->nm_mountp
, &nmreq
, 
2269                                         current_thread(), cp
->gss_clnt_cred
, 0, cp
, &nmrep
, &status
); 
2270                         nfsm_chain_cleanup(&nmreq
); 
2271                         nfsm_chain_cleanup(&nmrep
); 
2275                  * Mark the context invalid then drop 
2276                  * the reference to remove it if its 
2279                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
2280                 cp
->gss_clnt_flags 
|= (GSS_CTX_INVAL 
| GSS_CTX_DESTROY
); 
2281                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2282                 nfs_gss_clnt_ctx_unref(&req
); 
2283                 lck_mtx_lock(&nmp
->nm_lock
); 
2285         lck_mtx_unlock(&nmp
->nm_lock
); 
2286         assert(TAILQ_EMPTY(&nmp
->nm_gsscl
)); 
2291  * Removes a mounts context for a credential 
2294 nfs_gss_clnt_ctx_remove(struct nfsmount 
*nmp
, kauth_cred_t cred
) 
2296         struct nfs_gss_clnt_ctx 
*cp
; 
2301         NFS_GSS_DBG("Enter\n"); 
2302         NFS_GSS_CLNT_CTX_DUMP(nmp
); 
2303         lck_mtx_lock(&nmp
->nm_lock
); 
2304         TAILQ_FOREACH(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
) { 
2305                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
2306                 if (nfs_gss_clnt_ctx_cred_match(cp
->gss_clnt_cred
, cred
)) { 
2307                         if (cp
->gss_clnt_flags 
& GSS_CTX_DESTROY
) { 
2308                                 NFS_GSS_DBG("Found destroyed context %d/%d. refcnt = %d continuing\n", 
2309                                             kauth_cred_getasid(cp
->gss_clnt_cred
), 
2310                                             kauth_cred_getauid(cp
->gss_clnt_cred
), 
2311                                             cp
->gss_clnt_refcnt
); 
2312                                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2315                         cp
->gss_clnt_refcnt
++; 
2316                         cp
->gss_clnt_flags 
|= (GSS_CTX_INVAL 
| GSS_CTX_DESTROY
); 
2317                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2319                         lck_mtx_unlock(&nmp
->nm_lock
); 
2321                          * Drop the reference to remove it if its 
2324                         NFS_GSS_DBG("Removed context %d/%d refcnt = %d\n", 
2325                                     kauth_cred_getasid(cp
->gss_clnt_cred
), 
2326                                     kauth_cred_getuid(cp
->gss_clnt_cred
), 
2327                                     cp
->gss_clnt_refcnt
); 
2328                         nfs_gss_clnt_ctx_unref(&req
); 
2331                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2334         lck_mtx_unlock(&nmp
->nm_lock
); 
2336         NFS_GSS_DBG("Returning ENOENT\n"); 
2341  * Sets a mounts principal for a session associated with cred. 
2344 nfs_gss_clnt_ctx_set_principal(struct nfsmount 
*nmp
, vfs_context_t ctx
, 
2345                                uint8_t *principal
, uint32_t princlen
, uint32_t nametype
) 
2351         NFS_GSS_DBG("Enter:\n"); 
2353         bzero(&req
, sizeof(struct nfsreq
)); 
2355         req
.r_gss_ctx 
= NULL
; 
2356         req
.r_auth 
= nmp
->nm_auth
; 
2357         req
.r_thread 
= vfs_context_thread(ctx
); 
2358         req
.r_cred 
= vfs_context_ucred(ctx
); 
2360         error 
= nfs_gss_clnt_ctx_find_principal(&req
, principal
, princlen
, nametype
); 
2361         NFS_GSS_DBG("nfs_gss_clnt_ctx_find_principal returned %d\n", error
); 
2363          * We don't care about auth errors. Those would indicate that the context is in the 
2364          * neagative cache and if and when the user has credentials for the principal 
2365          * we should be good to go in that we will select those credentials for this principal. 
2367         if (error 
== EACCES 
|| error 
== EAUTH 
|| error 
== ENEEDAUTH
) 
2370         /* We're done with this request */ 
2371         nfs_gss_clnt_ctx_unref(&req
); 
2377  * Gets a mounts principal from a session associated with cred 
2380 nfs_gss_clnt_ctx_get_principal(struct nfsmount 
*nmp
, vfs_context_t ctx
, 
2381                                struct user_nfs_gss_principal 
*p
) 
2385         struct nfs_gss_clnt_ctx 
*cp
; 
2386         kauth_cred_t cred 
= vfs_context_ucred(ctx
); 
2388         char CTXBUF
[NFS_CTXBUFSZ
]; 
2391         lck_mtx_lock(&nmp
->nm_lock
); 
2392         TAILQ_FOREACH(cp
, &nmp
->nm_gsscl
, gss_clnt_entries
) { 
2393                 lck_mtx_lock(cp
->gss_clnt_mtx
); 
2394                 if (cp
->gss_clnt_flags 
& GSS_CTX_DESTROY
) { 
2395                         NFS_GSS_DBG("Found destroyed context %s refcnt = %d continuing\n", 
2396                                     NFS_GSS_CTX(&req
, cp
), 
2397                                     cp
->gss_clnt_refcnt
); 
2398                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2401                 if (nfs_gss_clnt_ctx_cred_match(cp
->gss_clnt_cred
, cred
)) { 
2402                         cp
->gss_clnt_refcnt
++; 
2403                         lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2406                 lck_mtx_unlock(cp
->gss_clnt_mtx
); 
2411                 lck_mtx_unlock(&nmp
->nm_lock
); 
2413                 p
->principal 
= USER_ADDR_NULL
; 
2414                 p
->nametype 
= GSSD_STRING_NAME
; 
2415                 p
->flags 
|= NFS_IOC_NO_CRED_FLAG
; 
2416                 NFS_GSS_DBG("No context found for session %d by uid %d\n", 
2417                             kauth_cred_getasid(cred
), kauth_cred_getuid(cred
)); 
2421         princ 
= cp
->gss_clnt_principal 
? (char *)cp
->gss_clnt_principal 
: cp
->gss_clnt_display
; 
2422         p
->princlen 
= cp
->gss_clnt_principal 
? cp
->gss_clnt_prinlen 
: 
2423                 (cp
->gss_clnt_display 
? strlen(cp
->gss_clnt_display
) : 0); 
2424         p
->nametype 
= cp
->gss_clnt_prinnt
; 
2428                 MALLOC(pp
, char *, p
->princlen
, M_TEMP
, M_WAITOK
); 
2430                         bcopy(princ
, pp
, p
->princlen
); 
2431                         p
->principal 
= CAST_USER_ADDR_T(pp
); 
2436         lck_mtx_unlock(&nmp
->nm_lock
); 
2439         NFS_GSS_DBG("Found context %s\n", NFS_GSS_CTX(&req
, NULL
)); 
2440         nfs_gss_clnt_ctx_unref(&req
); 
2443 #endif /* NFSCLIENT */ 
2453  * Find a server context based on a handle value received 
2454  * in an RPCSEC_GSS credential. 
2456 static struct nfs_gss_svc_ctx 
* 
2457 nfs_gss_svc_ctx_find(uint32_t handle
) 
2459         struct nfs_gss_svc_ctx_hashhead 
*head
; 
2460         struct nfs_gss_svc_ctx 
*cp
; 
2466         head 
= &nfs_gss_svc_ctx_hashtbl
[SVC_CTX_HASH(handle
)]; 
2468          * Don't return a context that is going to expire in GSS_CTX_PEND seconds 
2470         clock_interval_to_deadline(GSS_CTX_PEND
, NSEC_PER_SEC
, &timenow
); 
2472         lck_mtx_lock(nfs_gss_svc_ctx_mutex
); 
2474         LIST_FOREACH(cp
, head
, gss_svc_entries
) { 
2475                 if (cp
->gss_svc_handle 
== handle
) { 
2476                         if (timenow 
> cp
->gss_svc_incarnation 
+ GSS_SVC_CTX_TTL
) { 
2478                                  * Context has or is about to expire. Don't use. 
2479                                  * We'll return null and the client will have to create 
2482                                 cp
->gss_svc_handle 
= 0; 
2484                                  * Make sure though that we stay around for GSS_CTX_PEND seconds  
2485                                  * for other threads that might be using the context. 
2487                                 cp
->gss_svc_incarnation 
= timenow
; 
2492                         lck_mtx_lock(cp
->gss_svc_mtx
);                           
2493                         cp
->gss_svc_refcnt
++; 
2494                         lck_mtx_unlock(cp
->gss_svc_mtx
);                                 
2499         lck_mtx_unlock(nfs_gss_svc_ctx_mutex
); 
2505  * Insert a new server context into the hash table 
2506  * and start the context reap thread if necessary. 
2509 nfs_gss_svc_ctx_insert(struct nfs_gss_svc_ctx 
*cp
) 
2511         struct nfs_gss_svc_ctx_hashhead 
*head
; 
2512         struct nfs_gss_svc_ctx 
*p
; 
2514         lck_mtx_lock(nfs_gss_svc_ctx_mutex
); 
2517          * Give the client a random handle so that if we reboot 
2518          * it's unlikely the client will get a bad context match. 
2519          * Make sure it's not zero or already assigned. 
2522         cp
->gss_svc_handle 
= random(); 
2523         if (cp
->gss_svc_handle 
== 0) 
2525         head 
= &nfs_gss_svc_ctx_hashtbl
[SVC_CTX_HASH(cp
->gss_svc_handle
)]; 
2526         LIST_FOREACH(p
, head
, gss_svc_entries
) 
2527                 if (p
->gss_svc_handle 
== cp
->gss_svc_handle
) 
2530         clock_interval_to_deadline(GSS_CTX_PEND
, NSEC_PER_SEC
, 
2531                 &cp
->gss_svc_incarnation
); 
2532         LIST_INSERT_HEAD(head
, cp
, gss_svc_entries
); 
2533         nfs_gss_ctx_count
++; 
2535         if (!nfs_gss_timer_on
) { 
2536                 nfs_gss_timer_on 
= 1; 
2538                 nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call
, 
2539                         min(GSS_TIMER_PERIOD
, max(GSS_CTX_TTL_MIN
, nfsrv_gss_context_ttl
)) * MSECS_PER_SEC
); 
2542         lck_mtx_unlock(nfs_gss_svc_ctx_mutex
); 
2546  * This function is called via the kernel's callout 
2547  * mechanism.  It runs only when there are 
2548  * cached RPCSEC_GSS contexts. 
2551 nfs_gss_svc_ctx_timer(__unused 
void *param1
, __unused 
void *param2
) 
2553         struct nfs_gss_svc_ctx 
*cp
, *next
; 
2558         lck_mtx_lock(nfs_gss_svc_ctx_mutex
); 
2559         clock_get_uptime(&timenow
); 
2561         NFS_GSS_DBG("is running\n"); 
2564          * Scan all the hash chains 
2566         for (i 
= 0; i 
< SVC_CTX_HASHSZ
; i
++) { 
2568                  * For each hash chain, look for entries 
2569                  * that haven't been used in a while. 
2571                 LIST_FOREACH_SAFE(cp
, &nfs_gss_svc_ctx_hashtbl
[i
], gss_svc_entries
, next
) { 
2573                         if (timenow 
> cp
->gss_svc_incarnation 
+  
2574                                 (cp
->gss_svc_handle 
? GSS_SVC_CTX_TTL 
: 0) 
2575                                 && cp
->gss_svc_refcnt 
== 0) { 
2577                                  * A stale context - remove it 
2579                                 LIST_REMOVE(cp
, gss_svc_entries
); 
2580                                 NFS_GSS_DBG("Removing contex for %d\n", cp
->gss_svc_uid
); 
2581                                 if (cp
->gss_svc_seqbits
) 
2582                                         FREE(cp
->gss_svc_seqbits
, M_TEMP
); 
2583                                 lck_mtx_destroy(cp
->gss_svc_mtx
, nfs_gss_svc_grp
); 
2590         nfs_gss_ctx_count 
= contexts
; 
2593          * If there are still some cached contexts left, 
2594          * set up another callout to check on them later. 
2596         nfs_gss_timer_on 
= nfs_gss_ctx_count 
> 0; 
2597         if (nfs_gss_timer_on
) 
2598                 nfs_interval_timer_start(nfs_gss_svc_ctx_timer_call
, 
2599                         min(GSS_TIMER_PERIOD
, max(GSS_CTX_TTL_MIN
, nfsrv_gss_context_ttl
)) * MSECS_PER_SEC
); 
2601         lck_mtx_unlock(nfs_gss_svc_ctx_mutex
); 
2605  * Here the server receives an RPCSEC_GSS credential in an 
2606  * RPC call header.  First there's some checking to make sure 
2607  * the credential is appropriate - whether the context is still 
2608  * being set up, or is complete.  Then we use the handle to find 
2609  * the server's context and validate the verifier, which contains 
2610  * a signed checksum of the RPC header. If the verifier checks 
2611  * out, we extract the user's UID and groups from the context 
2612  * and use it to set up a UNIX credential for the user's request. 
2615 nfs_gss_svc_cred_get(struct nfsrv_descript 
*nd
, struct nfsm_chain 
*nmc
) 
2617         uint32_t vers
, proc
, seqnum
, service
; 
2618         uint32_t handle
, handle_len
; 
2619         struct nfs_gss_svc_ctx 
*cp 
= NULL
; 
2620         uint32_t flavor 
= 0, verflen 
= 0; 
2622         uint32_t arglen
, start
, toklen
, cksumlen
; 
2623         u_char tokbuf
[KRB5_SZ_TOKMAX(MAX_DIGEST
)]; 
2624         u_char cksum1
[MAX_DIGEST
], cksum2
[MAX_DIGEST
]; 
2625         struct nfsm_chain nmc_tmp
; 
2628         vers 
= proc 
= seqnum 
= service 
= handle_len 
= 0; 
2629         arglen 
= cksumlen 
= 0; 
2631         nfsm_chain_get_32(error
, nmc
, vers
); 
2632         if (vers 
!= RPCSEC_GSS_VERS_1
) { 
2633                 error 
= NFSERR_AUTHERR 
| AUTH_REJECTCRED
; 
2637         nfsm_chain_get_32(error
, nmc
, proc
); 
2638         nfsm_chain_get_32(error
, nmc
, seqnum
); 
2639         nfsm_chain_get_32(error
, nmc
, service
); 
2640         nfsm_chain_get_32(error
, nmc
, handle_len
); 
2645          * Make sure context setup/destroy is being done with a nullproc 
2647         if (proc 
!= RPCSEC_GSS_DATA 
&& nd
->nd_procnum 
!= NFSPROC_NULL
) { 
2648                 error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CREDPROBLEM
; 
2653          * If the sequence number is greater than the max 
2654          * allowable, reject and have the client init a 
2657         if (seqnum 
> GSS_MAXSEQ
) { 
2658                 error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CTXPROBLEM
; 
2663                 service 
== RPCSEC_GSS_SVC_NONE 
?      RPCAUTH_KRB5 
: 
2664                 service 
== RPCSEC_GSS_SVC_INTEGRITY 
? RPCAUTH_KRB5I 
: 
2665                 service 
== RPCSEC_GSS_SVC_PRIVACY 
?   RPCAUTH_KRB5P 
: 0; 
2667         if (proc 
== RPCSEC_GSS_INIT
) { 
2669                  * Limit the total number of contexts 
2671                 if (nfs_gss_ctx_count 
> nfs_gss_ctx_max
) { 
2672                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CTXPROBLEM
; 
2677                  * Set up a new context 
2679                 MALLOC(cp
, struct nfs_gss_svc_ctx 
*, sizeof(*cp
), M_TEMP
, M_WAITOK
|M_ZERO
); 
2684                 cp
->gss_svc_mtx 
= lck_mtx_alloc_init(nfs_gss_svc_grp
, LCK_ATTR_NULL
); 
2685                 cp
->gss_svc_refcnt 
= 1; 
2689                  * Use the handle to find the context 
2691                 if (handle_len 
!= sizeof(handle
)) { 
2692                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CREDPROBLEM
; 
2695                 nfsm_chain_get_32(error
, nmc
, handle
); 
2698                 cp 
= nfs_gss_svc_ctx_find(handle
); 
2700                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CTXPROBLEM
; 
2705         cp
->gss_svc_proc 
= proc
; 
2706         ki 
= &cp
->gss_svc_kinfo
; 
2708         if (proc 
== RPCSEC_GSS_DATA 
|| proc 
== RPCSEC_GSS_DESTROY
) { 
2709                 struct posix_cred temp_pcred
; 
2711                 if (cp
->gss_svc_seqwin 
== 0) { 
2713                          * Context isn't complete 
2715                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CTXPROBLEM
; 
2719                 if (!nfs_gss_svc_seqnum_valid(cp
, seqnum
)) { 
2721                          * Sequence number is bad 
2723                         error 
= EINVAL
; // drop the request 
2727                 /* Now compute the client's call header checksum */ 
2728                 nfs_gss_cksum_chain(ki
, nmc
, ALG_MIC(ki
), 0, 0, cksum1
); 
2731                  * Validate the verifier. 
2732                  * The verifier contains an encrypted checksum 
2733                  * of the call header from the XID up to and 
2734                  * including the credential.  We compute the 
2735                  * checksum and compare it with what came in 
2738                 nfsm_chain_get_32(error
, nmc
, flavor
); 
2739                 nfsm_chain_get_32(error
, nmc
, verflen
); 
2742                 if (flavor 
!= RPCSEC_GSS 
|| verflen 
!= KRB5_SZ_TOKEN(ki
->hash_len
)) 
2743                         error 
= NFSERR_AUTHERR 
| AUTH_BADVERF
; 
2744                 nfsm_chain_get_opaque(error
, nmc
, verflen
, tokbuf
); 
2748                 /* Get the checksum from the token inside the verifier */ 
2749                 error 
= nfs_gss_token_get(ki
, ALG_MIC(ki
), tokbuf
, 1, 
2754                 if (bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
2755                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CTXPROBLEM
; 
2759                 nd
->nd_gss_seqnum 
= seqnum
; 
2762                  * Set up the user's cred 
2764                 bzero(&temp_pcred
, sizeof(temp_pcred
)); 
2765                 temp_pcred
.cr_uid 
= cp
->gss_svc_uid
; 
2766                 bcopy(cp
->gss_svc_gids
, temp_pcred
.cr_groups
, 
2767                                 sizeof(gid_t
) * cp
->gss_svc_ngroups
); 
2768                 temp_pcred
.cr_ngroups 
= cp
->gss_svc_ngroups
; 
2770                 nd
->nd_cr 
= posix_cred_create(&temp_pcred
); 
2771                 if (nd
->nd_cr 
== NULL
) { 
2775                 clock_get_uptime(&cp
->gss_svc_incarnation
); 
2778                  * If the call arguments are integrity or privacy protected 
2779                  * then we need to check them here. 
2782                 case RPCSEC_GSS_SVC_NONE
: 
2785                 case RPCSEC_GSS_SVC_INTEGRITY
: 
2787                          * Here's what we expect in the integrity call args: 
2789                          * - length of seq num + call args (4 bytes) 
2790                          * - sequence number (4 bytes) 
2791                          * - call args (variable bytes) 
2792                          * - length of checksum token (37) 
2793                          * - checksum of seqnum + call args (37 bytes) 
2795                         nfsm_chain_get_32(error
, nmc
, arglen
);          // length of args 
2796                         if (arglen 
> NFS_MAXPACKET
) { 
2801                         /* Compute the checksum over the call args */ 
2802                         start 
= nfsm_chain_offset(nmc
); 
2803                         nfs_gss_cksum_chain(ki
, nmc
, ALG_MIC(ki
), start
, arglen
, cksum1
); 
2806                          * Get the sequence number prepended to the args 
2807                          * and compare it against the one sent in the 
2810                         nfsm_chain_get_32(error
, nmc
, seqnum
); 
2811                         if (seqnum 
!= nd
->nd_gss_seqnum
) { 
2812                                 error 
= EBADRPC
;                        // returns as GARBAGEARGS 
2817                          * Advance to the end of the args and 
2818                          * fetch the checksum computed by the client. 
2821                         arglen 
-= NFSX_UNSIGNED
;                        // skipped seqnum 
2822                         nfsm_chain_adv(error
, &nmc_tmp
, arglen
);        // skip args 
2823                         nfsm_chain_get_32(error
, &nmc_tmp
, cksumlen
);   // length of checksum 
2824                         if (cksumlen 
!= KRB5_SZ_TOKEN(ki
->hash_len
)) { 
2828                         nfsm_chain_get_opaque(error
, &nmc_tmp
, cksumlen
, tokbuf
); 
2831                         error 
= nfs_gss_token_get(ki
, ALG_MIC(ki
), tokbuf
, 1, 
2834                         /* Verify that the checksums are the same */ 
2835                         if (error 
|| bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
2840                 case RPCSEC_GSS_SVC_PRIVACY
: 
2842                          * Here's what we expect in the privacy call args: 
2844                          * - length of confounder + seq num + token + call args 
2845                          * - wrap token (37-40 bytes) 
2846                          * - confounder (8 bytes) 
2847                          * - sequence number (4 bytes) 
2848                          * - call args (encrypted) 
2850                         nfsm_chain_get_32(error
, nmc
, arglen
);          // length of args 
2851                         if (arglen 
> NFS_MAXPACKET
) { 
2856                         /* Get the token that prepends the encrypted args */ 
2857                         nfsm_chain_get_opaque(error
, nmc
, KRB5_SZ_TOKMAX(ki
->hash_len
), tokbuf
); 
2860                         error 
= nfs_gss_token_get(ki
, ALG_WRAP(ki
), tokbuf
, 1, 
2864                         nfsm_chain_reverse(nmc
, nfsm_pad(toklen
)); 
2866                         /* decrypt the 8 byte confounder + seqnum + args */ 
2867                         start 
= nfsm_chain_offset(nmc
); 
2869                         nfs_gss_encrypt_chain(ki
, nmc
, start
, arglen
, DES_DECRYPT
); 
2871                         /* Compute a checksum over the sequence number + results */ 
2872                         nfs_gss_cksum_chain(ki
, nmc
, ALG_WRAP(ki
), start
, arglen
, cksum2
); 
2874                         /* Verify that the checksums are the same */ 
2875                         if (bcmp(cksum1
, cksum2
, HASHLEN(ki
)) != 0) { 
2881                          * Get the sequence number prepended to the args 
2882                          * and compare it against the one sent in the 
2885                         nfsm_chain_adv(error
, nmc
, 8);                  // skip over the confounder 
2886                         nfsm_chain_get_32(error
, nmc
, seqnum
); 
2887                         if (seqnum 
!= nd
->nd_gss_seqnum
) { 
2888                                 error 
= EBADRPC
;                        // returns as GARBAGEARGS 
2895                  * If the proc is RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT 
2896                  * then we expect a null verifier. 
2898                 nfsm_chain_get_32(error
, nmc
, flavor
); 
2899                 nfsm_chain_get_32(error
, nmc
, verflen
); 
2900                 if (error 
|| flavor 
!= RPCAUTH_NULL 
|| verflen 
> 0) 
2901                         error 
= NFSERR_AUTHERR 
| RPCSEC_GSS_CREDPROBLEM
; 
2903                         if (proc 
== RPCSEC_GSS_INIT
) { 
2904                                 lck_mtx_destroy(cp
->gss_svc_mtx
, nfs_gss_svc_grp
); 
2912         nd
->nd_gss_context 
= cp
; 
2916                 nfs_gss_svc_ctx_deref(cp
); 
2921  * Insert the server's verifier into the RPC reply header. 
2922  * It contains a signed checksum of the sequence number that 
2923  * was received in the RPC call. 
2924  * Then go on to add integrity or privacy if necessary. 
2927 nfs_gss_svc_verf_put(struct nfsrv_descript 
*nd
, struct nfsm_chain 
*nmc
) 
2929         struct nfs_gss_svc_ctx 
*cp
; 
2931         u_char tokbuf
[KRB5_SZ_TOKEN(MAX_DIGEST
)]; 
2933         u_char cksum
[MAX_DIGEST
]; 
2936         cp 
= nd
->nd_gss_context
; 
2937         ki 
= &cp
->gss_svc_kinfo
; 
2939         if (cp
->gss_svc_major 
!= GSS_S_COMPLETE
) { 
2941                  * If the context isn't yet complete 
2942                  * then return a null verifier. 
2944                 nfsm_chain_add_32(error
, nmc
, RPCAUTH_NULL
); 
2945                 nfsm_chain_add_32(error
, nmc
, 0); 
2950          * Compute checksum of the request seq number 
2951          * If it's the final reply of context setup 
2952          * then return the checksum of the context 
2955         if (cp
->gss_svc_proc 
== RPCSEC_GSS_INIT 
|| 
2956             cp
->gss_svc_proc 
== RPCSEC_GSS_CONTINUE_INIT
) 
2957                 nfs_gss_cksum_rep(ki
, cp
->gss_svc_seqwin
, cksum
); 
2959                 nfs_gss_cksum_rep(ki
, nd
->nd_gss_seqnum
, cksum
); 
2961          * Now wrap it in a token and add 
2962          * the verifier to the reply. 
2964         toklen 
= nfs_gss_token_put(ki
, ALG_MIC(ki
), tokbuf
, 0, 0, cksum
); 
2965         nfsm_chain_add_32(error
, nmc
, RPCSEC_GSS
); 
2966         nfsm_chain_add_32(error
, nmc
, toklen
); 
2967         nfsm_chain_add_opaque(error
, nmc
, tokbuf
, toklen
); 
2973  * The results aren't available yet, but if they need to be 
2974  * checksummed for integrity protection or encrypted, then 
2975  * we can record the start offset here, insert a place-holder 
2976  * for the results length, as well as the sequence number. 
2977  * The rest of the work is done later by nfs_gss_svc_protect_reply() 
2978  * when the results are available. 
2981 nfs_gss_svc_prepare_reply(struct nfsrv_descript 
*nd
, struct nfsm_chain 
*nmc
) 
2983         struct nfs_gss_svc_ctx 
*cp 
= nd
->nd_gss_context
; 
2986         if (cp
->gss_svc_proc 
== RPCSEC_GSS_INIT 
|| 
2987             cp
->gss_svc_proc 
== RPCSEC_GSS_CONTINUE_INIT
) 
2990         switch (nd
->nd_sec
) { 
2995                 nd
->nd_gss_mb 
= nmc
->nmc_mcur
;                  // record current mbuf 
2996                 nfsm_chain_finish_mbuf(error
, nmc
);             // split the chain here 
2997                 nfsm_chain_add_32(error
, nmc
, nd
->nd_gss_seqnum
); // req sequence number 
3000                 nd
->nd_gss_mb 
= nmc
->nmc_mcur
;                  // record current mbuf 
3001                 nfsm_chain_finish_mbuf(error
, nmc
);             // split the chain here 
3002                 nfsm_chain_add_32(error
, nmc
, random());        // confounder bytes 1-4 
3003                 nfsm_chain_add_32(error
, nmc
, random());        // confounder bytes 5-8 
3004                 nfsm_chain_add_32(error
, nmc
, nd
->nd_gss_seqnum
); // req sequence number 
3012  * The results are checksummed or encrypted for return to the client 
3015 nfs_gss_svc_protect_reply(struct nfsrv_descript 
*nd
, mbuf_t mrep
) 
3017         struct nfs_gss_svc_ctx 
*cp 
= nd
->nd_gss_context
; 
3018         struct nfsm_chain nmrep_res
, *nmc_res 
= &nmrep_res
; 
3019         struct nfsm_chain nmrep_pre
, *nmc_pre 
= &nmrep_pre
; 
3022         u_char tokbuf
[KRB5_SZ_TOKMAX(MAX_DIGEST
)]; 
3024         u_char cksum
[MAX_DIGEST
]; 
3026         gss_key_info 
*ki 
= &cp
->gss_svc_kinfo
; 
3029          * Using a reference to the mbuf where we previously split the reply 
3030          * mbuf chain, we split the mbuf chain argument into two mbuf chains, 
3031          * one that allows us to prepend a length field or token, (nmc_pre) 
3032          * and the second which holds just the results that we're going to 
3033          * checksum and/or encrypt.  When we're done, we join the chains back 
3036         nfs_gss_nfsm_chain(nmc_res
, mrep
);              // set up the results chain 
3037         mb 
= nd
->nd_gss_mb
;                             // the mbuf where we split 
3038         results 
= mbuf_next(mb
);                        // first mbuf in the results 
3039         reslen 
= nfs_gss_mchain_length(results
);        // length of results 
3040         error 
= mbuf_setnext(mb
, NULL
);                 // disconnect the chains 
3043         nfs_gss_nfsm_chain(nmc_pre
, mb
);                // set up the prepend chain 
3045         if (nd
->nd_sec 
== RPCAUTH_KRB5I
) { 
3046                 nfsm_chain_add_32(error
, nmc_pre
, reslen
); 
3047                 nfsm_chain_build_done(error
, nmc_pre
); 
3050                 nfs_gss_append_chain(nmc_pre
, results
); // Append the results mbufs 
3052                 /* Now compute the checksum over the results data */ 
3053                 nfs_gss_cksum_mchain(ki
, results
, ALG_MIC(ki
), 0, reslen
, cksum
); 
3055                 /* Put it into a token and append to the request */ 
3056                 toklen 
= nfs_gss_token_put(ki
, ALG_MIC(ki
), tokbuf
, 0, 0, cksum
); 
3057                 nfsm_chain_add_32(error
, nmc_res
, toklen
); 
3058                 nfsm_chain_add_opaque(error
, nmc_res
, tokbuf
, toklen
); 
3059                 nfsm_chain_build_done(error
, nmc_res
); 
3063                  * Append a pad trailer - per RFC 1964 section 1.2.2.3 
3064                  * Since XDR data is always 32-bit aligned, it 
3065                  * needs to be padded either by 4 bytes or 8 bytes. 
3067                 if (reslen 
% 8 > 0) { 
3068                         nfsm_chain_add_32(error
, nmc_res
, 0x04040404); 
3069                         reslen 
+= NFSX_UNSIGNED
; 
3071                         nfsm_chain_add_32(error
, nmc_res
, 0x08080808); 
3072                         nfsm_chain_add_32(error
, nmc_res
, 0x08080808); 
3073                         reslen 
+=  2 * NFSX_UNSIGNED
; 
3075                 nfsm_chain_build_done(error
, nmc_res
); 
3077                 /* Now compute the checksum over the results data */ 
3078                 nfs_gss_cksum_mchain(ki
, results
, ALG_WRAP(ki
), 0, reslen
, cksum
); 
3080                 /* Put it into a token and insert in the reply */ 
3081                 toklen 
= nfs_gss_token_put(ki
, ALG_WRAP(ki
), tokbuf
, 0, reslen
, cksum
); 
3082                 nfsm_chain_add_32(error
, nmc_pre
, toklen 
+ reslen
); 
3083                 nfsm_chain_add_opaque_nopad(error
, nmc_pre
, tokbuf
, toklen
); 
3084                 nfsm_chain_build_done(error
, nmc_pre
); 
3087                 nfs_gss_append_chain(nmc_pre
, results
); // Append the results mbufs 
3089                 /* Encrypt the confounder + seqnum + results */ 
3090                 nfs_gss_encrypt_mchain(ki
, results
, 0, reslen
, DES_ENCRYPT
); 
3092                 /* Add null XDR pad if the ASN.1 token misaligned the data */ 
3093                 pad 
= nfsm_pad(toklen 
+ reslen
); 
3095                         nfsm_chain_add_opaque_nopad(error
, nmc_pre
, iv0
, pad
); 
3096                         nfsm_chain_build_done(error
, nmc_pre
); 
3104  * This function handles the context setup calls from the client. 
3105  * Essentially, it implements the NFS null procedure calls when 
3106  * an RPCSEC_GSS credential is used. 
3107  * This is the context maintenance function.  It creates and 
3108  * destroys server contexts at the whim of the client. 
3109  * During context creation, it receives GSS-API tokens from the 
3110  * client, passes them up to gssd, and returns a received token 
3111  * back to the client in the null procedure reply. 
3114 nfs_gss_svc_ctx_init(struct nfsrv_descript 
*nd
, struct nfsrv_sock 
*slp
, mbuf_t 
*mrepp
) 
3116         struct nfs_gss_svc_ctx 
*cp 
= NULL
; 
3119         struct nfsm_chain 
*nmreq
, nmrep
; 
3122         nmreq 
= &nd
->nd_nmreq
; 
3123         nfsm_chain_null(&nmrep
); 
3125         cp 
= nd
->nd_gss_context
; 
3128         switch (cp
->gss_svc_proc
) { 
3129         case RPCSEC_GSS_INIT
: 
3130                 nfs_gss_svc_ctx_insert(cp
); 
3133         case RPCSEC_GSS_CONTINUE_INIT
: 
3134                 /* Get the token from the request */ 
3135                 nfsm_chain_get_32(error
, nmreq
, cp
->gss_svc_tokenlen
); 
3136                 if (cp
->gss_svc_tokenlen 
== 0) { 
3137                         autherr 
= RPCSEC_GSS_CREDPROBLEM
; 
3140                 MALLOC(cp
->gss_svc_token
, u_char 
*, cp
->gss_svc_tokenlen
, M_TEMP
, M_WAITOK
); 
3141                 if (cp
->gss_svc_token 
== NULL
) { 
3142                         autherr 
= RPCSEC_GSS_CREDPROBLEM
; 
3145                 nfsm_chain_get_opaque(error
, nmreq
, cp
->gss_svc_tokenlen
, cp
->gss_svc_token
); 
3147                 /* Use the token in a gss_accept_sec_context upcall */ 
3148                 error 
= nfs_gss_svc_gssd_upcall(cp
); 
3150                         autherr 
= RPCSEC_GSS_CREDPROBLEM
; 
3151                         if (error 
== NFSERR_EAUTH
) 
3157                  * If the context isn't complete, pass the new token 
3158                  * back to the client for another round. 
3160                 if (cp
->gss_svc_major 
!= GSS_S_COMPLETE
) 
3164                  * Now the server context is complete. 
3167                 clock_get_uptime(&cp
->gss_svc_incarnation
); 
3169                 cp
->gss_svc_seqwin 
= GSS_SVC_SEQWINDOW
; 
3170                 MALLOC(cp
->gss_svc_seqbits
, uint32_t *, 
3171                         nfsm_rndup((cp
->gss_svc_seqwin 
+ 7) / 8), M_TEMP
, M_WAITOK
|M_ZERO
); 
3172                 if (cp
->gss_svc_seqbits 
== NULL
) { 
3173                         autherr 
= RPCSEC_GSS_CREDPROBLEM
; 
3178         case RPCSEC_GSS_DATA
: 
3179                 /* Just a nullproc ping - do nothing */ 
3182         case RPCSEC_GSS_DESTROY
: 
3184                  * Don't destroy the context immediately because 
3185                  * other active requests might still be using it. 
3186                  * Instead, schedule it for destruction after 
3187                  * GSS_CTX_PEND time has elapsed. 
3189                 cp 
= nfs_gss_svc_ctx_find(cp
->gss_svc_handle
); 
3191                         cp
->gss_svc_handle 
= 0; // so it can't be found 
3192                         lck_mtx_lock(cp
->gss_svc_mtx
); 
3193                         clock_interval_to_deadline(GSS_CTX_PEND
, NSEC_PER_SEC
, 
3194                                 &cp
->gss_svc_incarnation
); 
3195                         lck_mtx_unlock(cp
->gss_svc_mtx
); 
3199                 autherr 
= RPCSEC_GSS_CREDPROBLEM
; 
3203         /* Now build the reply  */ 
3205         if (nd
->nd_repstat 
== 0) 
3206                 nd
->nd_repstat 
= autherr 
? (NFSERR_AUTHERR 
| autherr
) : NFSERR_RETVOID
; 
3207         sz 
= 7 * NFSX_UNSIGNED 
+ nfsm_rndup(cp
->gss_svc_tokenlen
); // size of results 
3208         error 
= nfsrv_rephead(nd
, slp
, &nmrep
, sz
); 
3209         *mrepp 
= nmrep
.nmc_mhead
; 
3210         if (error 
|| autherr
) 
3213         if (cp
->gss_svc_proc 
== RPCSEC_GSS_INIT 
|| 
3214             cp
->gss_svc_proc 
== RPCSEC_GSS_CONTINUE_INIT
) { 
3215                 nfsm_chain_add_32(error
, &nmrep
, sizeof(cp
->gss_svc_handle
)); 
3216                 nfsm_chain_add_32(error
, &nmrep
, cp
->gss_svc_handle
); 
3218                 nfsm_chain_add_32(error
, &nmrep
, cp
->gss_svc_major
); 
3219                 nfsm_chain_add_32(error
, &nmrep
, cp
->gss_svc_minor
); 
3220                 nfsm_chain_add_32(error
, &nmrep
, cp
->gss_svc_seqwin
); 
3222                 nfsm_chain_add_32(error
, &nmrep
, cp
->gss_svc_tokenlen
); 
3223                 if (cp
->gss_svc_token 
!= NULL
) { 
3224                         nfsm_chain_add_opaque(error
, &nmrep
, cp
->gss_svc_token
, cp
->gss_svc_tokenlen
); 
3225                         FREE(cp
->gss_svc_token
, M_TEMP
); 
3226                         cp
->gss_svc_token 
= NULL
; 
3232                 nd
->nd_gss_context 
= NULL
; 
3233                 LIST_REMOVE(cp
, gss_svc_entries
); 
3234                 if (cp
->gss_svc_seqbits 
!= NULL
) 
3235                         FREE(cp
->gss_svc_seqbits
, M_TEMP
); 
3236                 if (cp
->gss_svc_token 
!= NULL
) 
3237                         FREE(cp
->gss_svc_token
, M_TEMP
); 
3238                 lck_mtx_destroy(cp
->gss_svc_mtx
, nfs_gss_svc_grp
); 
3242         nfsm_chain_build_done(error
, &nmrep
); 
3244                 nfsm_chain_cleanup(&nmrep
); 
3251  * This is almost a mirror-image of the client side upcall. 
3252  * It passes and receives a token, but invokes gss_accept_sec_context. 
3253  * If it's the final call of the context setup, then gssd also returns 
3254  * the session key and the user's UID. 
3257 nfs_gss_svc_gssd_upcall(struct nfs_gss_svc_ctx 
*cp
) 
3262         gssd_byte_buffer okey 
= NULL
; 
3263         uint32_t skeylen 
= 0; 
3265         vm_map_copy_t itoken 
= NULL
; 
3266         gssd_byte_buffer otoken 
= NULL
; 
3267         mach_msg_type_number_t otokenlen
; 
3269         char svcname
[] = "nfs"; 
3271         kr 
= host_get_gssd_port(host_priv_self(), &mp
); 
3272         if (kr 
!= KERN_SUCCESS
) { 
3273                 printf("nfs_gss_svc_gssd_upcall: can't get gssd port, status %x (%d)\n", kr
, kr
); 
3276         if (!IPC_PORT_VALID(mp
)) { 
3277                 printf("nfs_gss_svc_gssd_upcall: gssd port not valid\n"); 
3281         if (cp
->gss_svc_tokenlen 
> 0) 
3282                 nfs_gss_mach_alloc_buffer(cp
->gss_svc_token
, cp
->gss_svc_tokenlen
, &itoken
); 
3285         kr 
= mach_gss_accept_sec_context( 
3287                 (gssd_byte_buffer
) itoken
, (mach_msg_type_number_t
) cp
->gss_svc_tokenlen
, 
3290                 &cp
->gss_svc_context
, 
3291                 &cp
->gss_svc_cred_handle
, 
3295                 &cp
->gss_svc_ngroups
, 
3296                 &okey
, (mach_msg_type_number_t 
*) &skeylen
, 
3297                 &otoken
, &otokenlen
, 
3299                 &cp
->gss_svc_minor
); 
3301         if (kr 
!= KERN_SUCCESS
) {  
3302                 printf("nfs_gss_svc_gssd_upcall failed: %x (%d)\n", kr
, kr
); 
3303                 if (kr 
== MIG_SERVER_DIED 
&& cp
->gss_svc_context 
== 0 && 
3304                         retry_cnt
++ < NFS_GSS_MACH_MAX_RETRIES
) { 
3305                         if (cp
->gss_svc_tokenlen 
> 0) 
3306                                 nfs_gss_mach_alloc_buffer(cp
->gss_svc_token
, cp
->gss_svc_tokenlen
, &itoken
); 
3309                 host_release_special_port(mp
); 
3313         host_release_special_port(mp
); 
3316                 if (skeylen 
!= SKEYLEN 
&& skeylen 
!= SKEYLEN3
) { 
3317                         printf("nfs_gss_svc_gssd_upcall: bad key length (%d)\n", skeylen
); 
3318                         vm_map_copy_discard((vm_map_copy_t
) okey
); 
3319                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
3322                 error 
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) okey
, skeylen
, cp
->gss_svc_kinfo
.skey
); 
3324                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
3327                 error 
= gss_key_init(&cp
->gss_svc_kinfo
, skeylen
); 
3333         /* Free context token used as input */ 
3334         if (cp
->gss_svc_token
) 
3335                 FREE(cp
->gss_svc_token
, M_TEMP
); 
3336         cp
->gss_svc_token 
= NULL
; 
3337         cp
->gss_svc_tokenlen 
= 0; 
3339         if (otokenlen 
> 0) { 
3340                 /* Set context token to gss output token */ 
3341                 MALLOC(cp
->gss_svc_token
, u_char 
*, otokenlen
, M_TEMP
, M_WAITOK
); 
3342                 if (cp
->gss_svc_token 
== NULL
) { 
3343                         printf("nfs_gss_svc_gssd_upcall: could not allocate %d bytes\n", otokenlen
); 
3344                         vm_map_copy_discard((vm_map_copy_t
) otoken
); 
3347                 error 
= nfs_gss_mach_vmcopyout((vm_map_copy_t
) otoken
, otokenlen
, cp
->gss_svc_token
); 
3349                         FREE(cp
->gss_svc_token
, M_TEMP
); 
3350                         cp
->gss_svc_token 
= NULL
; 
3351                         return (NFSERR_EAUTH
); 
3353                 cp
->gss_svc_tokenlen 
= otokenlen
; 
3359         FREE(cp
->gss_svc_token
, M_TEMP
); 
3360         cp
->gss_svc_tokenlen 
= 0; 
3361         cp
->gss_svc_token 
= NULL
; 
3363         return (NFSERR_EAUTH
);   
3367  * Validate the sequence number in the credential as described 
3368  * in RFC 2203 Section 5.3.3.1 
3370  * Here the window of valid sequence numbers is represented by 
3371  * a bitmap.  As each sequence number is received, its bit is 
3372  * set in the bitmap.  An invalid sequence number lies below 
3373  * the lower bound of the window, or is within the window but 
3374  * has its bit already set. 
3377 nfs_gss_svc_seqnum_valid(struct nfs_gss_svc_ctx 
*cp
, uint32_t seq
) 
3379         uint32_t *bits 
= cp
->gss_svc_seqbits
; 
3380         uint32_t win 
= cp
->gss_svc_seqwin
; 
3383         lck_mtx_lock(cp
->gss_svc_mtx
); 
3386          * If greater than the window upper bound, 
3387          * move the window up, and set the bit. 
3389         if (seq 
> cp
->gss_svc_seqmax
) { 
3390                 if (seq 
- cp
->gss_svc_seqmax 
> win
) 
3391                         bzero(bits
, nfsm_rndup((win 
+ 7) / 8)); 
3393                         for (i 
= cp
->gss_svc_seqmax 
+ 1; i 
< seq
; i
++) 
3394                                 win_resetbit(bits
, i 
% win
); 
3395                 win_setbit(bits
, seq 
% win
); 
3396                 cp
->gss_svc_seqmax 
= seq
; 
3397                 lck_mtx_unlock(cp
->gss_svc_mtx
); 
3402          * Invalid if below the lower bound of the window 
3404         if (seq 
<= cp
->gss_svc_seqmax 
- win
) { 
3405                 lck_mtx_unlock(cp
->gss_svc_mtx
); 
3410          * In the window, invalid if the bit is already set 
3412         if (win_getbit(bits
, seq 
% win
)) { 
3413                 lck_mtx_unlock(cp
->gss_svc_mtx
); 
3416         win_setbit(bits
, seq 
% win
); 
3417         lck_mtx_unlock(cp
->gss_svc_mtx
); 
3422  * Drop a reference to a context 
3424  * Note that it's OK for the context to exist 
3425  * with a refcount of zero.  The refcount isn't 
3426  * checked until we're about to reap an expired one. 
3429 nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx 
*cp
) 
3431         lck_mtx_lock(cp
->gss_svc_mtx
);                           
3432         if (cp
->gss_svc_refcnt 
> 0) 
3433                 cp
->gss_svc_refcnt
--; 
3435                 printf("nfs_gss_ctx_deref: zero refcount\n"); 
3436         lck_mtx_unlock(cp
->gss_svc_mtx
);                                 
3440  * Called at NFS server shutdown - destroy all contexts 
3443 nfs_gss_svc_cleanup(void) 
3445         struct nfs_gss_svc_ctx_hashhead 
*head
; 
3446         struct nfs_gss_svc_ctx 
*cp
, *ncp
; 
3449         lck_mtx_lock(nfs_gss_svc_ctx_mutex
); 
3452          * Run through all the buckets 
3454         for (i 
= 0; i 
< SVC_CTX_HASHSZ
; i
++) { 
3456                  * Remove and free all entries in the bucket 
3458                 head 
= &nfs_gss_svc_ctx_hashtbl
[i
]; 
3459                 LIST_FOREACH_SAFE(cp
, head
, gss_svc_entries
, ncp
) { 
3460                         LIST_REMOVE(cp
, gss_svc_entries
); 
3461                         if (cp
->gss_svc_seqbits
) 
3462                                 FREE(cp
->gss_svc_seqbits
, M_TEMP
); 
3463                         lck_mtx_destroy(cp
->gss_svc_mtx
, nfs_gss_svc_grp
); 
3468         lck_mtx_unlock(nfs_gss_svc_ctx_mutex
); 
3471 #endif /* NFSSERVER */ 
3475  * The following functions are used by both client and server. 
3479  * Release a host special port that was obtained by host_get_special_port 
3480  * or one of its macros (host_get_gssd_port in this case). 
3481  * This really should be in a public kpi.  
3484 /* This should be in a public header if this routine is not */ 
3485 extern void ipc_port_release_send(ipc_port_t
); 
3486 extern ipc_port_t 
ipc_port_copy_send(ipc_port_t
); 
3489 host_release_special_port(mach_port_t mp
) 
3491         if (IPC_PORT_VALID(mp
)) 
3492                 ipc_port_release_send(mp
); 
3496 host_copy_special_port(mach_port_t mp
) 
3498         return (ipc_port_copy_send(mp
)); 
3502  * The token that is sent and received in the gssd upcall 
3503  * has unbounded variable length.  Mach RPC does not pass 
3504  * the token in-line.  Instead it uses page mapping to handle 
3505  * these parameters.  This function allocates a VM buffer 
3506  * to hold the token for an upcall and copies the token 
3507  * (received from the client) into it.  The VM buffer is 
3508  * marked with a src_destroy flag so that the upcall will 
3509  * automatically de-allocate the buffer when the upcall is 
3513 nfs_gss_mach_alloc_buffer(u_char 
*buf
, uint32_t buflen
, vm_map_copy_t 
*addr
) 
3516         vm_offset_t kmem_buf
; 
3520         if (buf 
== NULL 
|| buflen 
== 0) 
3523         tbuflen 
= vm_map_round_page(buflen
, 
3524                                     vm_map_page_mask(ipc_kernel_map
)); 
3525         kr 
= vm_allocate(ipc_kernel_map
, &kmem_buf
, tbuflen
, VM_FLAGS_ANYWHERE 
| VM_MAKE_TAG(VM_KERN_MEMORY_FILE
)); 
3527                 printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n"); 
3531         kr 
= vm_map_wire(ipc_kernel_map
, 
3532                          vm_map_trunc_page(kmem_buf
, 
3533                                            vm_map_page_mask(ipc_kernel_map
)), 
3534                          vm_map_round_page(kmem_buf 
+ tbuflen
, 
3535                                            vm_map_page_mask(ipc_kernel_map
)), 
3536                 VM_PROT_READ
|VM_PROT_WRITE
|VM_PROT_MEMORY_TAG_MAKE(VM_KERN_MEMORY_FILE
), FALSE
); 
3538                 printf("nfs_gss_mach_alloc_buffer: vm_map_wire failed\n"); 
3542         bcopy(buf
, (void *) kmem_buf
, buflen
); 
3543         // Shouldn't need to bzero below since vm_allocate returns zeroed pages 
3544         // bzero(kmem_buf + buflen, tbuflen - buflen); 
3546         kr 
= vm_map_unwire(ipc_kernel_map
, 
3547                            vm_map_trunc_page(kmem_buf
, 
3548                                              vm_map_page_mask(ipc_kernel_map
)), 
3549                            vm_map_round_page(kmem_buf 
+ tbuflen
, 
3550                                              vm_map_page_mask(ipc_kernel_map
)), 
3553                 printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n"); 
3557         kr 
= vm_map_copyin(ipc_kernel_map
, (vm_map_address_t
) kmem_buf
, 
3558                 (vm_map_size_t
) buflen
, TRUE
, addr
); 
3560                 printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n"); 
3566  * Here we handle a token received from the gssd via an upcall. 
3567  * The received token resides in an allocate VM buffer. 
3568  * We copy the token out of this buffer to a chunk of malloc'ed 
3569  * memory of the right size, then de-allocate the VM buffer. 
3572 nfs_gss_mach_vmcopyout(vm_map_copy_t in
, uint32_t len
, u_char 
*out
) 
3574         vm_map_offset_t map_data
; 
3578         error 
= vm_map_copyout(ipc_kernel_map
, &map_data
, in
); 
3582         data 
= CAST_DOWN(vm_offset_t
, map_data
); 
3583         bcopy((void *) data
, out
, len
); 
3584         vm_deallocate(ipc_kernel_map
, data
, len
); 
3590  * Encode an ASN.1 token to be wrapped in an RPCSEC_GSS verifier. 
3591  * Returns the size of the token, since it contains a variable 
3592  * length DER encoded size field. 
3603         static uint32_t seqnum 
= 0; 
3609          * Fill in the token header: 2 octets. 
3610          * This is 0x06 - an ASN.1 tag for APPLICATION, 0, SEQUENCE 
3611          * followed by the length of the token: 35 + 0 octets for a 
3612          * MIC token, or 35 + encrypted octets for a wrap token; 
3615         toklen 
= KRB5_SZ_MECH 
+ KRB5_SZ_ALG 
+ KRB5_SZ_SEQ 
+ HASHLEN(ki
); 
3616         nfs_gss_der_length_put(&p
, toklen 
+ datalen
); 
3619          * Fill in the DER encoded mech OID for Kerberos v5. 
3620          * This represents the Kerberos OID 1.2.840.113554.1.2.2 
3621          * described in RFC 2623, section 4.2 
3623         bcopy(krb5_mech
, p
, sizeof(krb5_mech
)); 
3624         p 
+= sizeof(krb5_mech
); 
3627          * Now at the token described in RFC 1964, section 1.2.1 
3628          * Fill in the token ID, integrity algorithm indicator, 
3629          * for DES MAC MD5, and four filler octets. 
3630          * The alg string encodes the bytes to represent either 
3631          * a MIC token or a WRAP token for Kerberos. 
3633         bcopy(alg
, p
, KRB5_SZ_ALG
); 
3637          * Now encode the sequence number according to 
3638          * RFC 1964, section 1.2.1.2 which dictates 4 octets 
3639          * of sequence number followed by 4 bytes of direction 
3640          * indicator: 0x00 for initiator or 0xff for acceptor. 
3641          * We DES CBC encrypt the sequence number using the first 
3642          * 8 octets of the checksum field as an initialization 
3644          * Note that this sequence number is not at all related 
3645          * to the RPCSEC_GSS protocol sequence number.  This 
3646          * number is private to the ASN.1 token.  The only 
3647          * requirement is that it not be repeated in case the 
3648          * server has replay detection on, which normally should 
3649          * not be the case, since RFC 2203 section 5.2.3 says that 
3650          * replay detection and sequence checking must be turned off. 
3653         for (i 
= 0; i 
< 4; i
++) 
3654                 plain
[i
] = (u_char
) ((seqnum 
>> (i 
* 8)) & 0xff); 
3655         for (i 
= 4; i 
< 8; i
++) 
3656                 plain
[i
] = initiator 
? 0x00 : 0xff; 
3657         gss_des_crypt(ki
, (des_cblock 
*) plain
, (des_cblock 
*) p
, 8, 
3658                         (des_cblock 
*) cksum
, NULL
, DES_ENCRYPT
, KG_USAGE_SEQ
); 
3662          * Finally, append the octets of the  
3663          * checksum of the alg + plaintext data. 
3664          * The plaintext could be an RPC call header, 
3665          * the window value, or a sequence number. 
3667         bcopy(cksum
, p
, HASHLEN(ki
)); 
3674  * Determine size of ASN.1 DER length 
3677 nfs_gss_der_length_size(int len
) 
3680                 len 
< (1 <<  7) ? 1 : 
3681                 len 
< (1 <<  8) ? 2 : 
3682                 len 
< (1 << 16) ? 3 : 
3683                 len 
< (1 << 24) ? 4 : 5; 
3687  * Encode an ASN.1 DER length field 
3690 nfs_gss_der_length_put(u_char 
**pp
, int len
) 
3692         int sz 
= nfs_gss_der_length_size(len
); 
3696                 *p
++ = (u_char
) len
; 
3698                 *p
++ = (u_char
) ((sz
-1) | 0x80); 
3701                         *p
++ = (u_char
) ((len 
>> (sz 
* 8)) & 0xff); 
3708  * Decode an ASN.1 DER length field 
3711 nfs_gss_der_length_get(u_char 
**pp
) 
3714         uint32_t flen
, len 
= 0; 
3718         if ((*p
++ & 0x80) == 0) 
3721                 if (flen 
> sizeof(uint32_t)) 
3724                         len 
= (len 
<< 8) + *p
++; 
3731  * Decode an ASN.1 token from an RPCSEC_GSS verifier. 
3747          * Check that we have a valid token header 
3750                 return (AUTH_BADCRED
); 
3751         (void) nfs_gss_der_length_get(&p
);      // ignore the size 
3754          * Check that we have the DER encoded Kerberos v5 mech OID 
3756         if (bcmp(p
, krb5_mech
, sizeof(krb5_mech
) != 0)) 
3757                 return (AUTH_BADCRED
); 
3758         p 
+= sizeof(krb5_mech
); 
3761          * Now check the token ID, DES MAC MD5 algorithm 
3762          * indicator, and filler octets. 
3764         if (bcmp(p
, alg
, KRB5_SZ_ALG
) != 0) 
3765                 return (AUTH_BADCRED
); 
3769          * Now decrypt the sequence number. 
3770          * Note that the gss decryption uses the first 8 octets 
3771          * of the checksum field as an initialization vector (p + 8). 
3772          * Per RFC 2203 section 5.2.2 we don't check the sequence number 
3773          * in the ASN.1 token because the RPCSEC_GSS protocol has its 
3774          * own sequence number described in section 5.3.3.1 
3777         gss_des_crypt(ki
, (des_cblock 
*)p
, (des_cblock 
*) plain
, 8, 
3778                         (des_cblock 
*) (p 
+ 8), NULL
, DES_DECRYPT
, KG_USAGE_SEQ
); 
3780         for (i 
= 0; i 
< 4; i
++) 
3781                 seqnum 
|= plain
[i
] << (i 
* 8); 
3784          * Make sure the direction 
3785          * indicator octets are correct. 
3787         d 
= initiator 
? 0x00 : 0xff; 
3788         for (i 
= 4; i 
< 8; i
++) 
3790                         return (AUTH_BADCRED
); 
3793          * Finally, get the checksum 
3795         bcopy(p
, cksum
, HASHLEN(ki
)); 
3805  * Return the number of bytes in an mbuf chain. 
3808 nfs_gss_mchain_length(mbuf_t mhead
) 
3813         for (mb 
= mhead
; mb
; mb 
= mbuf_next(mb
)) 
3814                 len 
+= mbuf_len(mb
); 
3820  * Append an args or results mbuf chain to the header chain 
3823 nfs_gss_append_chain(struct nfsm_chain 
*nmc
, mbuf_t mc
) 
3828         /* Connect the mbuf chains */ 
3829         error 
= mbuf_setnext(nmc
->nmc_mcur
, mc
); 
3833         /* Find the last mbuf in the chain */ 
3835         for (mb 
= mc
; mb
; mb 
= mbuf_next(mb
)) 
3838         nmc
->nmc_mcur 
= tail
; 
3839         nmc
->nmc_ptr 
= (caddr_t
) mbuf_data(tail
) + mbuf_len(tail
); 
3840         nmc
->nmc_left 
= mbuf_trailingspace(tail
); 
3846  * Convert an mbuf chain to an NFS mbuf chain 
3849 nfs_gss_nfsm_chain(struct nfsm_chain 
*nmc
, mbuf_t mc
) 
3853         /* Find the last mbuf in the chain */ 
3855         for (mb 
= mc
; mb
; mb 
= mbuf_next(mb
)) 
3858         nmc
->nmc_mhead 
= mc
; 
3859         nmc
->nmc_mcur 
= tail
; 
3860         nmc
->nmc_ptr 
= (caddr_t
) mbuf_data(tail
) + mbuf_len(tail
); 
3861         nmc
->nmc_left 
= mbuf_trailingspace(tail
); 
3867  * Compute a checksum over an mbuf chain. 
3868  * Start building an MD5 digest at the given offset and keep 
3869  * going until the end of data in the current mbuf is reached. 
3870  * Then convert the 16 byte MD5 digest to an 8 byte DES CBC 
3874 nfs_gss_cksum_mchain( 
3885         GSS_DIGEST_CTX context
; 
3887         gss_digest_Init(&context
, ki
); 
3890          * Logically prepend the first 8 bytes of the algorithm 
3891          * field as required by RFC 1964, section 1.2.1.1 
3893         gss_digest_Update(&context
, alg
, KRB5_SZ_ALG
); 
3896          * Move down the mbuf chain until we reach the given 
3897          * byte offset, then start MD5 on the mbuf data until 
3898          * we've done len bytes. 
3901         for (mb 
= mhead
; mb 
&& len 
> 0; mb 
= mbuf_next(mb
)) { 
3902                 ptr  
= mbuf_data(mb
); 
3903                 left 
= mbuf_len(mb
); 
3904                 if (offset 
>= left
) { 
3905                         /* Offset not yet reached */ 
3909                 /* At or beyond offset - checksum data */ 
3914                 bytes 
= left 
< len 
? left 
: len
; 
3916                         gss_digest_Update(&context
, ptr
, bytes
); 
3920         gss_digest_Final(&context
, digest
); 
3924  * Compute a checksum over an NFS mbuf chain. 
3925  * Start building an MD5 digest at the given offset and keep 
3926  * going until the end of data in the current mbuf is reached. 
3927  * Then convert the 16 byte MD5 digest to an 8 byte DES CBC 
3931 nfs_gss_cksum_chain( 
3933         struct nfsm_chain 
*nmc
, 
3940          * If the length parameter is zero, then we need 
3941          * to use the length from the offset to the current 
3942          * encode/decode offset. 
3945                 len 
= nfsm_chain_offset(nmc
) - offset
; 
3947         return (nfs_gss_cksum_mchain(ki
, nmc
->nmc_mhead
, alg
, offset
, len
, cksum
)); 
3951  * Compute a checksum of the sequence number (or sequence window) 
3952  * of an RPCSEC_GSS reply. 
3955 nfs_gss_cksum_rep(gss_key_info 
*ki
, uint32_t seqnum
, u_char 
*cksum
) 
3957         GSS_DIGEST_CTX context
; 
3958         uint32_t val 
= htonl(seqnum
); 
3960         gss_digest_Init(&context
, ki
); 
3963          * Logically prepend the first 8 bytes of the MIC 
3964          * token as required by RFC 1964, section 1.2.1.1 
3966         gss_digest_Update(&context
, ALG_MIC(ki
), KRB5_SZ_ALG
); 
3969          * Compute the digest of the seqnum in network order 
3971         gss_digest_Update(&context
, &val
, 4); 
3972         gss_digest_Final(&context
, cksum
); 
3976  * Encrypt or decrypt data in an mbuf chain with des-cbc. 
3979 nfs_gss_encrypt_mchain( 
3988         u_char tmp
[8], ivec
[8]; 
3989         int left
, left8
, remain
; 
3995          * Move down the mbuf chain until we reach the given 
3996          * byte offset, then start encrypting the mbuf data until 
3997          * we've done len bytes. 
4000         for (mb 
= mhead
; mb 
&& len 
> 0; mb 
= mbn
) { 
4001                 mbn  
= mbuf_next(mb
); 
4002                 ptr  
= mbuf_data(mb
); 
4003                 left 
= mbuf_len(mb
); 
4004                 if (offset 
>= left
) { 
4005                         /* Offset not yet reached */ 
4009                 /* At or beyond offset - encrypt data */ 
4015                  * DES or DES3 CBC has to encrypt 8 bytes at a time. 
4016                  * If the number of bytes to be encrypted in this 
4017                  * mbuf isn't some multiple of 8 bytes, encrypt all 
4018                  * the 8 byte blocks, then combine the remaining 
4019                  * bytes with enough from the next mbuf to make up 
4020                  * an 8 byte block and encrypt that block separately, 
4021                  * i.e. that block is split across two mbufs. 
4024                 left8 
= left 
- remain
; 
4025                 left 
= left8 
< len 
? left8 
: len
; 
4027                         gss_des_crypt(ki
, (des_cblock 
*) ptr
, (des_cblock 
*) ptr
, 
4028                                         left
, &ivec
, &ivec
, encrypt
, KG_USAGE_SEAL
); 
4032                 if (mbn 
&& remain 
> 0) { 
4033                         nptr 
= mbuf_data(mbn
); 
4034                         offset 
= 8 - remain
; 
4035                         bcopy(ptr 
+ left
, tmp
, remain
);         // grab from this mbuf 
4036                         bcopy(nptr
, tmp 
+ remain
, offset
);      // grab from next mbuf 
4037                         gss_des_crypt(ki
, (des_cblock 
*) tmp
, (des_cblock 
*) tmp
, 8, 
4038                                         &ivec
, &ivec
, encrypt
, KG_USAGE_SEAL
); 
4039                         bcopy(tmp
, ptr 
+ left
, remain
);         // return to this mbuf 
4040                         bcopy(tmp 
+ remain
, nptr
, offset
);      // return to next mbuf 
4047  * Encrypt or decrypt data in an NFS mbuf chain with des-cbc. 
4050 nfs_gss_encrypt_chain( 
4052         struct nfsm_chain 
*nmc
, 
4058          * If the length parameter is zero, then we need 
4059          * to use the length from the offset to the current 
4060          * encode/decode offset. 
4063                 len 
= nfsm_chain_offset(nmc
) - offset
; 
4065         return (nfs_gss_encrypt_mchain(ki
, nmc
->nmc_mhead
, offset
, len
, encrypt
)); 
4069  * The routines that follow provide abstractions for doing digests and crypto. 
4073 gss_digest_Init(GSS_DIGEST_CTX 
*ctx
, gss_key_info 
*ki
) 
4075         ctx
->type 
= ki
->type
; 
4077         case NFS_GSS_1DES
:      MD5_DESCBC_Init(&ctx
->m_ctx
, &ki
->ks_u
.des
.gss_sched
); 
4079         case NFS_GSS_3DES
:      HMAC_SHA1_DES3KD_Init(&ctx
->h_ctx
, ki
->ks_u
.des3
.ckey
, 0); 
4082                         printf("gss_digest_Init: Unknown key info type %d\n", ki
->type
); 
4087 gss_digest_Update(GSS_DIGEST_CTX 
*ctx
, void *data
, size_t len
) 
4089         switch (ctx
->type
) { 
4090         case NFS_GSS_1DES
:      MD5_DESCBC_Update(&ctx
->m_ctx
, data
, len
); 
4092         case NFS_GSS_3DES
:      HMAC_SHA1_DES3KD_Update(&ctx
->h_ctx
, data
, len
); 
4098 gss_digest_Final(GSS_DIGEST_CTX 
*ctx
, void *digest
) 
4100         switch (ctx
->type
) { 
4101         case NFS_GSS_1DES
:      MD5_DESCBC_Final(digest
, &ctx
->m_ctx
); 
4103         case NFS_GSS_3DES
:      HMAC_SHA1_DES3KD_Final(digest
, &ctx
->h_ctx
); 
4109 gss_des_crypt(gss_key_info 
*ki
, des_cblock 
*in
, des_cblock 
*out
, 
4110                 int32_t len
, des_cblock 
*iv
, des_cblock 
*retiv
, int encrypt
, int usage
) 
4115                                 des_cbc_key_schedule 
*sched 
= ((usage 
== KG_USAGE_SEAL
) ? 
4116                                                         &ki
->ks_u
.des
.gss_sched_Ke 
: 
4117                                                         &ki
->ks_u
.des
.gss_sched
); 
4118                                 des_cbc_encrypt(in
, out
, len
, sched
, iv
, retiv
, encrypt
); 
4123                         des3_cbc_encrypt(in
, out
, len
, &ki
->ks_u
.des3
.gss_sched
, iv
, retiv
, encrypt
); 
4129 gss_key_init(gss_key_info 
*ki
, uint32_t skeylen
) 
4135         ki
->keybytes 
= skeylen
; 
4137         case sizeof(des_cblock
): 
4138                                 ki
->type 
= NFS_GSS_1DES
; 
4139                                 ki
->hash_len 
= MD5_DESCBC_DIGEST_LENGTH
; 
4140                                 ki
->ks_u
.des
.key 
= (des_cblock 
*)ki
->skey
; 
4141                                 rc 
= des_cbc_key_sched(ki
->ks_u
.des
.key
, &ki
->ks_u
.des
.gss_sched
); 
4144                                 for (i 
= 0; i 
< ki
->keybytes
; i
++) 
4145                                         k
[0][i
] = 0xf0 ^ (*ki
->ks_u
.des
.key
)[i
]; 
4146                                 rc 
= des_cbc_key_sched(&k
[0], &ki
->ks_u
.des
.gss_sched_Ke
); 
4148         case 3*sizeof(des_cblock
):       
4149                                 ki
->type 
= NFS_GSS_3DES
; 
4150                                 ki
->hash_len 
= SHA_DIGEST_LENGTH
; 
4151                                 ki
->ks_u
.des3
.key 
= (des_cblock (*)[3])ki
->skey
; 
4152                                 des3_derive_key(*ki
->ks_u
.des3
.key
, ki
->ks_u
.des3
.ckey
, 
4153                                                 KEY_USAGE_DES3_SIGN
, KEY_USAGE_LEN
); 
4154                                 rc 
= des3_cbc_key_sched(*ki
->ks_u
.des3
.key
, &ki
->ks_u
.des3
.gss_sched
); 
4159                                 printf("gss_key_init: Invalid key length %d\n", skeylen
); 
4168 #define DISPLAYLEN 16 
4169 #define MAXDISPLAYLEN 256 
4172 hexdump(const char *msg
, void *data
, size_t len
) 
4176         char *p
, disbuf
[3*DISPLAYLEN
+1]; 
4178         printf("NFS DEBUG %s len=%d:\n", msg
, (uint32_t)len
); 
4179         if (len 
> MAXDISPLAYLEN
) 
4180                 len 
= MAXDISPLAYLEN
; 
4182         for (i 
= 0; i 
< len
; i 
+= DISPLAYLEN
) { 
4183                 for (p 
= disbuf
, j 
= 0; (j 
+ i
) < len 
&& j 
< DISPLAYLEN
; j
++, p 
+= 3) 
4184                         snprintf(p
, 4, "%02x ", d
[i 
+ j
]); 
4185                 printf("\t%s\n", disbuf
);