1/*
2 * Copyright (c) 2006 - 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/param.h>
25#include <sys/systm.h>
26#include <sys/kernel.h>
27#include <sys/malloc.h>
28
29#include <mach/task.h>
30#include <mach/host_special_ports.h>
31#include <mach/mig_errors.h>
32#include <mach/vm_map.h>
33#include <vm/vm_map.h>
34#include <vm/vm_kern.h>
35
36#include <sys/smb_apple.h>
37
38#include <netsmb/smb.h>
39#include <netsmb/smb_2.h>
40#include <netsmb/smb_subr.h>
41#include <netsmb/smb_conn.h>
42#include <netsmb/smb_rq.h>
43#include <netsmb/smb_gss.h>
44#include <netsmb/smb_gss_2.h>
45
46#include <sys/kauth.h>
47#include <smbfs/smbfs.h>
48#include <netsmb/smb_converter.h>
49
50#include <gssd/gssd_mach_types.h>
51#include <gssd/gssd_mach.h>
52#include <sys/random.h>
53
54extern host_priv_t host_priv_self(void);
55extern kern_return_t host_get_special_port(host_priv_t, int, int, ipc_port_t *);
56
57#define kauth_cred_getasid(cred) ((cred)->cr_audit.as_aia_p->ai_asid)
58#define kauth_cred_getauid(cred) ((cred)->cr_audit.as_aia_p->ai_auid)
59
60#define SMB2_KRB5_SESSION_KEYLEN 16
61
62/*
63 * smb_gss_negotiate:
64 * This routine is called from smb_smb_negotiate to initialize the vc_gss
65 * structure in a vc for doing extened security. We asn.1 decode the security
66 * blob passed in, and get the task special port for gssd.
67 */
68int
69smb_gss_negotiate(struct smb_vc *vcp, vfs_context_t context)
70{
71	struct smb_gss *gp = &vcp->vc_gss;
72	mach_port_t gssd_host_port;
73	uid_t uid;
74	kauth_cred_t cred;
75	kern_return_t kr;
76
77	if (IPC_PORT_VALID(gp->gss_mp))
78		return 0;
79
80	DBG_ASSERT(context);
81	/* Should never happen, but just in case */
82	if (context == NULL)
83		return EPIPE;
84
85    if (gp->gss_spn != NULL) {
86        SMB_FREE(gp->gss_spn, M_SMBTEMP);
87    }
88
89	/*
90	 * Get a mach port to talk to gssd.
91	 * gssd lives in the root bootstrap, so we call gssd's lookup routine
92	 * to get a send right to talk to a new gssd instance that launchd has launched
93	 * based on the cred's uid and audit session id.
94	 */
95	kr = host_get_gssd_port(host_priv_self(), &gssd_host_port);
96	if (kr != KERN_SUCCESS) {
97		SMBERROR("Can't get gssd port, status %x (%d)\n", kr, kr);
98		return EPIPE;
99	}
100	if (!IPC_PORT_VALID(gssd_host_port)) {
101		SMBERROR("gssd port not valid\n");
102		return EPIPE;
103	}
104
105	cred = vfs_context_ucred(context);
106	if (gp->gss_asid == AU_ASSIGN_ASID)
107		gp->gss_asid = kauth_cred_getasid(cred);
108
109	uid = kauth_cred_getauid(cred);
110	if (uid == AU_DEFAUDITID)
111		uid = kauth_cred_getuid(cred);
112
113	kr = mach_gss_lookup(gssd_host_port, uid, gp->gss_asid, &gp->gss_mp);
114	if (kr != KERN_SUCCESS || !IPC_PORT_VALID(gp->gss_mp)) {
115		if (kr != KERN_SUCCESS)
116			SMBERROR("mach_gss_lookup failed: status %x (%d)\n", kr, kr);
117		else
118			SMBERROR("Port is %s\n", gp->gss_mp == IPC_PORT_DEAD ? "dead" : "null");
119
120		return EPIPE;
121	}
122	return 0;
123}
124
125/*
126 * smb_gss_reset:
127 *
128 * Reset in case we need to reconnect or reauth after an error
129 */
130static void
131smb_gss_reset(struct smb_gss *gp)
132{
133	if (gp->gss_token != NULL) {
134        SMB_FREE(gp->gss_token, M_SMBTEMP);
135    }
136	gp->gss_tokenlen = 0;
137	gp->gss_ctx = 0;
138	gp->gss_cred = 0;
139	gp->gss_major = 0;
140	gp->gss_minor = 0;
141}
142
143/*
144 * smb_gss_destroy:
145 *
146 * Cleanup a struct smb_gss
147 */
148void
149smb_gss_destroy(struct smb_gss *gp)
150{
151	extern void ipc_port_release_send(ipc_port_t);
152
153	/* Release the mach port by  calling the kernel routine needed to release task special port */
154	if (IPC_PORT_VALID(gp->gss_mp))
155	    ipc_port_release_send(gp->gss_mp);
156    if (gp->gss_cpn) {
157        SMB_FREE(gp->gss_cpn, M_SMBTEMP);
158    }
159    if (gp->gss_cpn_display) {
160        SMB_FREE(gp->gss_cpn_display, M_SMBTEMP);
161    }
162    if (gp->gss_spn) {
163        SMB_FREE(gp->gss_spn, M_SMBTEMP);
164    }
165    if (gp->gss_token) {
166        SMB_FREE(gp->gss_token, M_SMBTEMP);
167    }
168	bzero(gp, sizeof(struct smb_gss));
169}
170
171/*
172 * The token that is sent and received in the gssd upcall
173 * has unbounded variable length.  Mach RPC does not pass
174 * the token in-line.  Instead it uses page mapping to handle
175 * these parameters.  This function allocates a VM buffer
176 * to hold the token for an upcall and copies the token
177 * (received from the client) into it.  The VM buffer is
178 * marked with a src_destroy flag so that the upcall will
179 * automatically de-allocate the buffer when the upcall is
180 * complete.
181 */
182static void
183gss_mach_alloc_buffer(u_char *buf, uint32_t buflen, vm_map_copy_t *addr)
184{
185	kern_return_t kr;
186	vm_offset_t kmem_buf;
187	vm_size_t tbuflen;
188
189	*addr = NULL;
190	if (buf == NULL || buflen == 0)
191		return;
192
193	tbuflen = round_page(buflen);
194	kr = vm_allocate(ipc_kernel_map, &kmem_buf, tbuflen, VM_FLAGS_ANYWHERE);
195	if (kr != 0) {
196		SMBERROR("gss_mach_alloc_buffer: vm_allocate failed\n");
197		return;
198	}
199
200	kr = vm_map_wire(ipc_kernel_map, vm_map_trunc_page(kmem_buf,
201                    vm_map_page_mask(ipc_kernel_map)),
202                    vm_map_round_page(kmem_buf + tbuflen,
203                    vm_map_page_mask(ipc_kernel_map)),
204                    VM_PROT_READ|VM_PROT_WRITE, FALSE);
205
206	if (kr != 0) {
207		SMBERROR("gss_mach_alloc_buffer: vm_map_wire failed kr = %d\n", kr);
208		return;
209	}
210
211	bcopy(buf, (void *) kmem_buf, buflen);
212
213	kr = vm_map_unwire(ipc_kernel_map,
214        vm_map_trunc_page(kmem_buf, vm_map_page_mask(ipc_kernel_map)),
215        vm_map_round_page(kmem_buf + tbuflen, vm_map_page_mask(ipc_kernel_map)),
216        FALSE);
217
218	if (kr != 0) {
219		SMBERROR("gss_mach_alloc_buffer: vm_map_unwire failed kr = %d\n", kr);
220		return;
221	}
222
223	kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t) kmem_buf,
224					   (vm_map_size_t) buflen, TRUE, addr);
225	if (kr != 0) {
226		SMBERROR("gss_mach_alloc_buffer: vm_map_copyin failed kr = %d\n", kr);
227		return;
228	}
229}
230
231/*
232 * Here we handle a token received from the gssd via an upcall.
233 * The received token resides in an allocate VM buffer.
234 * We copy the token out of this buffer to a chunk of malloc'ed
235 * memory of the right size, then de-allocate the VM buffer.
236 */
237static int
238gss_mach_vmcopyout(vm_map_copy_t in, uint32_t len, u_char *out)
239{
240	vm_map_offset_t map_data;
241	vm_offset_t data;
242	int error;
243
244	error = vm_map_copyout(ipc_kernel_map, &map_data, in);
245	if (error)
246		return (error);
247
248	data = CAST_DOWN(vm_offset_t, map_data);
249	bcopy((void *) data, out, len);
250	vm_deallocate(ipc_kernel_map, data, len);
251
252	return (0);
253}
254
255/*
256 * This is a tempory hack until vm_map_copy_discard is available to kexts
257 */
258static void
259gss_mach_vm_map_copy_discard(vm_map_copy_t copy, mach_msg_type_number_t len)
260{
261	vm_map_offset_t map_data;
262
263	if (vm_map_copyout(ipc_kernel_map, &map_data, copy))
264		return;
265	vm_deallocate(ipc_kernel_map, CAST_DOWN(vm_offset_t, map_data), (vm_size_t)len);
266}
267
268/*
269 * Wrapper to make mach up call, using the parameters in the smb_gss structure
270 * and the supplied uid. (Should be the vc_uid field of the enclosing vc).
271 */
272
273static kern_return_t
274smb_gss_init(struct smb_vc *vcp, uid_t uid)
275{
276	struct smb_gss *cp = &vcp->vc_gss;
277	uint32_t gssd_flags = GSSD_NO_DEFAULT;
278	uint32_t flags = GSSD_MUTUAL_FLAG | GSSD_DELEG_POLICY_FLAG;
279	kern_return_t kr;
280	gssd_byte_buffer okey = NULL;
281	int retry_cnt = 0;
282	vm_map_copy_t itoken = NULL;
283	vm_map_copy_t cpn = NULL;
284	vm_map_copy_t spn = NULL;
285	gssd_byte_buffer otoken = NULL;
286	mach_msg_type_number_t otokenlen;
287	mach_msg_type_number_t keylen;
288	int error = 0;
289	char display_name[MAX_DISPLAY_STR];
290	gssd_mechtype mechtype;
291
292	if (!IPC_PORT_VALID(cp->gss_mp)) {
293		SMBWARNING("smb_gss_init: gssd port not valid\n");
294		goto out;
295	}
296
297	if (cp->gss_tokenlen > 0)
298		gss_mach_alloc_buffer(cp->gss_token, cp->gss_tokenlen, &itoken);
299	if (cp->gss_cpn_len > 0)
300		gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
301	if (cp->gss_spn_len > 0)
302		gss_mach_alloc_buffer(cp->gss_spn, cp->gss_spn_len, &spn);
303
304	/* lha says we should set this bit when doing anonymous */
305	if (vcp->vc_flags & SMBV_ANONYMOUS_ACCESS) {
306		flags |= GSSD_ANON_FLAG;
307	}
308
309    /*
310     * lha says we should set this bit when doing signing.
311     */
312    if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
313        flags |= GSSD_INTEG_FLAG; /* Doing signing */
314    }
315
316    /*
317     * If SMB 2/3, then we will need signing to do the validate negotiate
318     * but only if its not Anonymous and not Guest
319     */
320    if ((vcp->vc_flags & SMBV_SMB2) &&
321        !(vcp->vc_flags & SMBV_ANONYMOUS_ACCESS) &&
322        !(vcp->vc_flags & SMBV_GUEST_ACCESS)) {
323        flags |= GSSD_INTEG_FLAG; /* Doing signing */
324    }
325
326	/* The server doesn't support NTLMSSP, send RAW NTLM */
327	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
328		mechtype = GSSD_NTLM_MECH;
329	}
330    else {
331		mechtype = GSSD_SPNEGO_MECH;
332	}
333
334retry:
335	*display_name = '\0';
336	kr = mach_gss_init_sec_context_v2(
337					  cp->gss_mp,
338					  mechtype,
339					  (gssd_byte_buffer) itoken, (mach_msg_type_number_t) cp->gss_tokenlen,
340					  uid,
341					  cp->gss_client_nt,
342					  (gssd_byte_buffer) cpn,
343					  (mach_msg_type_number_t) cp->gss_cpn_len,
344					  cp->gss_target_nt,
345					  (gssd_byte_buffer) spn,
346					  (mach_msg_type_number_t) cp->gss_spn_len,
347					  flags,
348					  &gssd_flags,
349					  &cp->gss_ctx,
350					  &cp->gss_cred,
351					  &cp->gss_rflags,
352					  &okey,  &keylen,
353					  &otoken, (mach_msg_type_number_t *) &otokenlen,
354					  display_name,
355					  &cp->gss_major,
356					  &cp->gss_minor);
357
358	if (kr != 0) {
359		SMBERROR("smb_gss_init: mach_gss_init_sec_context failed: %x %d\n", kr, kr);
360		if (kr == MIG_SERVER_DIED && cp->gss_cred == 0 &&
361			retry_cnt++ < GSS_MACH_MAX_RETRIES) {
362			if (cp->gss_tokenlen > 0)
363				gss_mach_alloc_buffer(cp->gss_token, cp->gss_tokenlen, &itoken);
364			if (cp->gss_cpn_len > 0)
365				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
366			if (cp->gss_spn_len > 0)
367				gss_mach_alloc_buffer(cp->gss_spn, cp->gss_spn_len, &spn);
368			goto retry;
369		}
370		goto out;
371	}
372
373	if (keylen > 0) {
374		/* Free any old key and reset the sequence number */
375		smb_reset_sig(vcp);
376		vcp->vc_mackeylen = keylen;
377		SMB_MALLOC(vcp->vc_mackey, uint8_t *, vcp->vc_mackeylen, M_SMBTEMP, M_WAITOK);
378		error = gss_mach_vmcopyout((vm_map_copy_t) okey, vcp->vc_mackeylen, vcp->vc_mackey);
379		if (error) {
380			gss_mach_vm_map_copy_discard((vm_map_copy_t)otoken, otokenlen);
381			goto out;
382		}
383
384        /*
385         * MS-SMB2 3.2.1.3 Per Session
386         * "Session.SessionKey: the first 16 bytes of the cryptographic key
387         * for this authenticated context...."
388         *
389         * So we must truncate the signing key if required.
390         * See <rdar://problem/13591834>.
391         */
392        if (vcp->vc_flags & SMBV_SMB2) {
393            if (vcp->vc_mackeylen > SMB2_KRB5_SESSION_KEYLEN) {
394                vcp->vc_mackeylen = SMB2_KRB5_SESSION_KEYLEN;
395            }
396        }
397
398        /* Derive SMB 3 keys from the session key from gssd */
399        if (vcp->vc_flags & (SMBV_SMB30 | SMBV_SMB302)) {
400            smb3_derive_keys(vcp);
401        }
402
403		SMBDEBUG("%s keylen = %d seqno = %d\n", vcp->vc_srvname, keylen, vcp->vc_seqno);
404		smb_hexdump(__FUNCTION__, "setting vc_mackey = ", vcp->vc_mackey, vcp->vc_mackeylen);
405		/*
406		 * Windows expects the sequence number to restart once we get a signing
407		 * key. They expect this to happen once the client creates a authorization
408		 * token blob to send to the server. This was we can validate the servers
409		 * response. When doing Kerberos and now NTLMSSP we don't get the signing
410		 * key until after the gss mech has completed. Not sure how to really
411		 * fix this issue, but for now we just reset the sequence number as if
412		 * we had the key when the last round went out.
413		 */
414		vcp->vc_seqno = 2;
415	}
416
417	/* If we're done, see if the server is mapping everybody to guest */
418	if (SMB_GSS_COMPLETE(cp) && (gssd_flags & GSSD_GUEST_ONLY)) {
419		vcp->vc_flags |= SMBV_SFS_ACCESS;
420		SMBDEBUG("NTLMSSP simple file sharing\n");
421	}
422
423	/* Free context token used as input */
424    if (cp->gss_token != NULL) {
425        SMB_FREE(cp->gss_token, M_SMBTEMP);
426    }
427	cp->gss_tokenlen = 0;
428
429	if (otokenlen > 0) {
430		SMB_MALLOC(cp->gss_token, uint8_t *, otokenlen, M_SMBTEMP, M_WAITOK);
431		error = gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_token);
432		if (error)
433			goto out;
434		cp->gss_tokenlen = otokenlen;
435	}
436
437	if (cp->gss_cpn_display == NULL && display_name[0]) {
438		size_t len = strnlen(display_name, MAX_DISPLAY_STR);
439		SMB_MALLOC(cp->gss_cpn_display, char *, len, M_SMBTEMP, M_WAITOK);
440		if (cp->gss_cpn_display)
441			strlcpy(cp->gss_cpn_display, display_name, len);
442		SMBDEBUG("Received display name %s\n", display_name);
443	}
444
445	/*
446	 * We have a gss error, could be the Kerberos creditials doesn't exist yet or
447	 * has expired. If we are in reconnect then we may need to wait for the user
448	 * to correct the problem. So lets return EAGAIN so the reconnect code can
449	 * try again later.
450	 */
451	if (SMB_GSS_ERROR(cp) && (vcp->vc_iod->iod_flags & SMBIOD_RECONNECT))
452		return EAGAIN;
453
454	return (0);
455
456out:
457	SMB_FREE(cp->gss_token, M_SMBTEMP);
458	cp->gss_tokenlen = 0;
459
460	return (EAUTH);
461}
462
463/*
464 * smb_gss_vc_caps:
465 *
466 * Given a virtual circut, determine our capabilities to send to the server
467 * as part of "ssandx" message. We now call the general routine smb_vc_caps
468 * handle the basic items and add that we are doing extended security.
469 */
470static uint32_t smb_gss_vc_caps(struct smb_vc *vcp)
471{
472	return (smb_vc_caps(vcp) | SMB_CAP_EXT_SECURITY);
473}
474
475/*
476 * smb_gss_ssandx:
477 *
478 * Send a session setup and x message on vc with cred and caps
479 */
480int
481smb1_gss_ssandx(struct smb_vc *vcp, uint32_t caps, uint16_t *action,
482                vfs_context_t context)
483{
484	struct smb_rq *rqp = NULL;
485	struct smb_gss *gp = &vcp->vc_gss;
486	struct mbchain *mbp;
487	struct mdchain *mdp;
488	uint8_t		wc;
489	uint16_t	bc, toklen;
490	int			error;
491	uint32_t	tokenlen;
492	uint32_t	maxtokenlen;
493	uint8_t *	tokenptr;
494
495#ifdef SMB_DEBUG
496	/* For testing use a smaller max size */
497	maxtokenlen = 2048;
498#else // SMB_DEBUG
499	/* Get the max blob size we can send in SetupAndX message */
500	maxtokenlen = vcp->vc_txmax - (SMB_HDRLEN + SMB_SETUPXRLEN);
501#endif // SMB_DEBUG
502
503	tokenptr = gp->gss_token;	/* Get the start of the Kerberos blob */
504	do {
505		uint16_t maxtx = vcp->vc_txmax;
506		if (rqp)	/* If we are looping then release it, before getting it again */
507			smb_rq_done(rqp);
508
509		/* Allocate the request form a session setup and x */
510		error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, 0, context, &rqp);
511		if (error)
512			break;
513		/* Fill the request with the required parameters */
514		smb_rq_wstart(rqp);
515        smb_rq_getrequest(rqp, &mbp);
516		mb_put_uint8(mbp, 0xff);
517		mb_put_uint8(mbp, 0);
518		mb_put_uint16le(mbp, 0);
519		mb_put_uint16le(mbp, maxtx);
520		mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
521		mb_put_uint16le(mbp, vcp->vc_number);
522		mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
523		/* Get the max size we can send in one SetupAndX message */
524		tokenlen = (gp->gss_tokenlen > maxtokenlen) ? maxtokenlen : gp->gss_tokenlen;
525		mb_put_uint16le(mbp, tokenlen);
526		mb_put_uint32le(mbp, 0);		/* reserved */
527		mb_put_uint32le(mbp, caps);		/* our caps */
528		smb_rq_wend(rqp);
529		smb_rq_bstart(rqp);
530		/* SPNEGO blob */
531		mb_put_mem(mbp, (caddr_t) tokenptr, tokenlen, MB_MSYSTEM);
532		smb_put_dstring(mbp, SMB_UNICODE_STRINGS(vcp), SMBFS_NATIVEOS, sizeof(SMBFS_NATIVEOS), NO_SFM_CONVERSIONS);	/* Native OS */
533		smb_put_dstring(mbp, SMB_UNICODE_STRINGS(vcp), SMBFS_LANMAN, sizeof(SMBFS_LANMAN), NO_SFM_CONVERSIONS);	/* LAN Mgr */
534		smb_rq_bend(rqp);
535		/* Send the request and check for reply */
536		error = smb_rq_simple_timed(rqp, SMBSSNSETUPTIMO);
537		/* Move the pointer to the next offset in the blob */
538		tokenptr += tokenlen;
539		/* Subtract the size we have already sent */
540		gp->gss_tokenlen -= tokenlen;
541		/* Save the servers vc identifier if not already set. */
542		if ((error == EAGAIN) && (vcp->vc_smbuid == 0))
543			vcp->vc_smbuid = rqp->sr_rpuid;
544
545	} while (gp->gss_tokenlen && (error == EAGAIN));
546
547	/* Free the gss spnego token that we sent */
548	SMB_FREE(gp->gss_token, M_SMBTEMP);
549	gp->gss_tokenlen = 0;
550	gp->gss_smb_error = error;	/* Hold on to the last smb error returned */
551	/* EAGAIN is not  really an  error, reset it to no error */
552	if (error == EAGAIN) {
553		error = 0;
554	}
555	/* At this point error should have the correct error, we only support NTStatus code with extended security */
556	if (error) {
557		SMB_LOG_AUTH("Extended security authorization failed! %d\n", error);
558		goto bad;
559	}
560
561	/*
562	 * Save the servers vc identifier. Seems Samba will give us a new one for
563	 * every loop of a SetUpAndX NTLMSSP response. Windows server just return
564	 * the same one every time. We assume here the last one is the one we
565	 * should always use. Seems to make Samba work correctly.
566	 */
567	vcp->vc_smbuid = rqp->sr_rpuid;
568
569	/* Get the reply  and decode the result */
570	smb_rq_getreply(rqp, &mdp);
571	error = md_get_uint8(mdp, &wc);
572	if (error)
573		goto bad;
574	if (wc != 4) {
575		error = EBADRPC;
576		goto bad;
577	}
578	md_get_uint8(mdp, NULL);	/* secondary cmd */
579	md_get_uint8(mdp, NULL);	/* mbz */
580	md_get_uint16le(mdp, NULL);	/* andxoffset */
581	md_get_uint16le(mdp, action);	/* action */
582	md_get_uint16le(mdp, &toklen);	/* Spnego token length */
583	gp->gss_tokenlen = toklen;
584	md_get_uint16le(mdp, &bc); /* remaining bytes */
585	/*
586	 * Set the gss token from the server
587	 */
588    SMB_MALLOC(gp->gss_token, uint8_t *, gp->gss_tokenlen, M_SMBTEMP, M_WAITOK);
589    if (gp->gss_token == NULL)
590        goto bad;
591	error = md_get_mem(mdp, (caddr_t) gp->gss_token, gp->gss_tokenlen, MB_MSYSTEM);
592	if (error)
593		goto bad;
594	/* Determine the amount of data left in the buffer */
595	bc = (toklen > bc) ? 0 : bc - toklen;
596	/*
597	 * The rest of the message should contain the NativeOS and NativeLANManager
598	 * strings. If the message is in UNICODE then the byte count field is always
599	 * on an odd boundry, so we need to check to see if the security blob token
600	 * is odd or even. If the security blob toklen is even then we need to skip
601	 * the padd byte.
602	 */
603	if (SMB_UNICODE_STRINGS(vcp) && (bc > 0) && (!(toklen & 1))) {
604		md_get_uint8(mdp, NULL);	/* Skip Padd Byte */
605		bc -= 1;
606	}
607	/*
608	 * Now see if we can get the NativeOS and NativeLANManager strings. We
609	 * use these strings to tell if the server is a Win2k or XP system,
610	 * also Shared Computers wants this info.
611	 */
612	parse_server_os_lanman_strings(vcp, mdp, bc);
613
614bad:
615	smb_rq_done(rqp);
616	return (error);
617}
618
619/*
620 * smb_gss_ssnsetup:
621 *
622 * If we'er using gss, then we should be called from smb_smb_ssnsetup
623 * to do an extended security session setup and x to the server
624 */
625
626int
627smb_gss_ssnsetup(struct smb_vc *vcp, vfs_context_t context)
628{
629	int error = 0;
630	uint32_t caps;
631	uint16_t action = 0;
632
633	/* We should always have a gssd port! */
634	if (!SMB_USE_GSS(vcp)) {
635		SMBERROR("Doing Extended Security, but we don't have a gssd port!\n");
636		return (EINVAL);
637	}
638
639	/*
640	 * set the smbuid to zero so we will pick up the first
641	 * value returned from the server in smb_gss_ssandx
642	 */
643	vcp->vc_smbuid = 0;
644	vcp->vc_session_id = 0;
645
646	/* Get our caps from the vc. N.B. Seems only Samba uses this */
647	caps = smb_gss_vc_caps(vcp);
648
649	do {
650		/* Call gss to create a security blob */
651        error = smb_gss_init(vcp, vcp->vc_uid);
652
653		/* No token to send or error so just break out */
654		if (error || (SMB_GSS_ERROR(&vcp->vc_gss))) {
655			SMB_LOG_AUTH("GSSD extended security error = %d gss_major = %d gss_minor = %d\n",
656					   error, vcp->vc_gss.gss_major, vcp->vc_gss.gss_minor);
657
658            if ((vcp->vc_session_id != 0) || (vcp->vc_smbuid != 0)) {
659                /*
660                 * <13687368> If the GSS Auth fails on the client side in the
661                 * middle of SessionSetup exchanges (vc_session_id != 0 and
662                 * vc_smbuid != 0), then just logout which will tell the server
663                 * that the auth has failed
664                 */
665                (void)smb_smb_ssnclose(vcp, context);
666            }
667
668			/* Always return EAUTH unless we want the reconnect code to try again */
669			if (error != EAGAIN)
670				error = EAUTH;
671			break;
672		}
673		if ((vcp->vc_gss.gss_tokenlen) && ((error = smb_gss_ssandx(vcp, caps,
674                                                                   &action,
675                                                                   context))))
676			break;
677	} while (SMB_GSS_CONTINUE_NEEDED(&vcp->vc_gss));
678
679	if ((error == 0) && !SMBV_HAS_GUEST_ACCESS(vcp)
680        && !SMBV_HAS_ANONYMOUS_ACCESS(vcp) &&
681        (action & SMB_ACT_GUEST)) {
682		/*
683		 * We wanted to only login the users as guest if they ask to be login as
684		 * guest. Window system will login any bad user name as guest if guest is
685		 * turn on.
686		 * The first problem here is with XPHome. XPHome doesn't care if the
687		 * user is real or made up, it always logs them is as guest. We now have
688		 * a way to tell if Simple File Sharing (XPHome) is being used, so this
689		 * is no longer an issue.
690		 *
691		 * The second problem here is with Vista. Vista will log you in as guest
692		 * if your account has no password. The problem here is we don't always
693		 * know if the user provided a password or not. We could have the Network
694		 * Authorization Helper tell us, but this would not work in the cache
695		 * case.
696		 *
697		 * The user wanted authenticated access but got guest access, log them
698		 * out and return EAUTH.
699		 */
700        SMBWARNING("Got guest access, but wanted real access, logging off.\n");
701        (void)smb_smb_ssnclose(vcp, context);
702        error = EAUTH;
703	}
704	if ((error == 0) && (action & SMB_ACT_GUEST)) {
705		/* And the lying dogs might tells us to do signing when they don't */
706		vcp->vc_hflags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE;
707		smb_reset_sig(vcp);
708	}
709	if (error)	/* Reset the signature info */
710		smb_reset_sig(vcp);
711
712	smb_gss_reset(&vcp->vc_gss);
713	return error;
714}
715
716/*
717 * The VC needs to hold a reference on the credentials until its destroyed.
718 *
719 */
720void smb_gss_ref_cred(struct smb_vc *vcp)
721{
722	struct smb_gss *cp = &vcp->vc_gss;
723	vm_map_copy_t cpn = NULL;
724	gssd_mechtype mechtype;
725	kern_return_t kr;
726	int retry_cnt = 0;
727
728	SMBDEBUG("%s\n", vcp->vc_srvname);
729	if (!(VC_CAPS(vcp) & SMB_CAP_EXT_SECURITY)) {
730		/* Not using gssd then no way to take a reference */
731		return;
732	}
733	if ((cp->gss_cpn_len == 0) || (cp->gss_cpn == NULL)) {
734		/* No cred then no way to take a reference */
735		return;
736	}
737
738	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
739		mechtype = GSSD_NTLM_MECH;
740	} else {
741		mechtype = GSSD_SPNEGO_MECH;
742	}
743
744	gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
745retry:
746	kr = mach_gss_hold_cred(cp->gss_mp, mechtype, cp->gss_client_nt,
747							(gssd_byte_buffer)cpn,
748							(mach_msg_type_number_t)cp->gss_cpn_len,
749							&cp->gss_major, &cp->gss_minor);
750	if (kr != KERN_SUCCESS) {
751		SMB_LOG_AUTH("mach_gss_hold_cred failed: kr = 0x%x kr = %d\n", kr, kr);
752		if (kr == MIG_SERVER_DIED && retry_cnt++ < GSS_MACH_MAX_RETRIES) {
753			if (cp->gss_cpn_len > 0)
754				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
755			goto retry;
756		}
757	} else if (SMB_GSS_ERROR(cp)) {
758		SMB_LOG_AUTH("FAILED: gss_major = %d gss_minor = %d\n",
759				   cp->gss_major, cp->gss_minor);
760	}
761}
762
763/*
764 * The VC holds a reference on the credtials release it.
765 */
766void smb_gss_rel_cred(struct smb_vc *vcp)
767{
768	struct smb_gss *cp = &vcp->vc_gss;
769	vm_map_copy_t cpn = NULL;
770	gssd_mechtype mechtype;
771	kern_return_t kr;
772	int retry_cnt = 0;
773
774	SMBDEBUG("%s\n", vcp->vc_srvname);
775	/* Not using gssd then no way to take a reference */
776	if (!(VC_CAPS(vcp) & SMB_CAP_EXT_SECURITY)) {
777		return;
778	}
779	if ((cp->gss_cpn_len == 0) || (cp->gss_cpn == NULL)) {
780		/* No cred then no way to take a reference */
781		return;
782	}
783
784	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
785		mechtype = GSSD_NTLM_MECH;
786	} else {
787		mechtype = GSSD_SPNEGO_MECH;
788	}
789
790	gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
791retry:
792	kr = mach_gss_unhold_cred(cp->gss_mp, mechtype, cp->gss_client_nt,
793							  (gssd_byte_buffer)cpn,
794							  (mach_msg_type_number_t)cp->gss_cpn_len,
795							  &cp->gss_major, &cp->gss_minor);
796	if (kr != KERN_SUCCESS) {
797		SMB_LOG_AUTH("mach_gss_unhold_cred failed: kr = 0x%x kr = %d\n", kr, kr);
798		if (kr == MIG_SERVER_DIED && retry_cnt++ < GSS_MACH_MAX_RETRIES) {
799			if (cp->gss_cpn_len > 0)
800				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
801			goto retry;
802		}
803	} else if (SMB_GSS_ERROR(cp)) {
804		SMB_LOG_AUTH("FAILED: gss_major = %d gss_minor = %d\n",
805				   cp->gss_major, cp->gss_minor);
806	}
807}
808