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 form 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	/* lha says we should set this bit when doing signing */
309	if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
310		flags |= GSSD_INTEG_FLAG; /* Doing signing */
311	}
312	/* See if its ok to touch the home directory */
313	if (vcp->vc_hflags2 & SMBV_HOME_ACCESS_OK) {
314		gssd_flags |= GSSD_HOME_ACCESS_OK;
315	}
316	/* The server doesn't support NTLMSSP, send RAW NTLM */
317	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
318		mechtype = GSSD_NTLM_MECH;
319	} else {
320		mechtype = GSSD_SPNEGO_MECH;
321	}
322
323retry:
324	*display_name = '\0';
325	kr = mach_gss_init_sec_context_v2(
326					  cp->gss_mp,
327					  mechtype,
328					  (gssd_byte_buffer) itoken, (mach_msg_type_number_t) cp->gss_tokenlen,
329					  uid,
330					  cp->gss_client_nt,
331					  (gssd_byte_buffer) cpn,
332					  (mach_msg_type_number_t) cp->gss_cpn_len,
333					  cp->gss_target_nt,
334					  (gssd_byte_buffer) spn,
335					  (mach_msg_type_number_t) cp->gss_spn_len,
336					  flags,
337					  &gssd_flags,
338					  &cp->gss_ctx,
339					  &cp->gss_cred,
340					  &cp->gss_rflags,
341					  &okey,  &keylen,
342					  &otoken, (mach_msg_type_number_t *) &otokenlen,
343					  display_name,
344					  &cp->gss_major,
345					  &cp->gss_minor);
346
347	if (kr != 0) {
348		SMBERROR("smb_gss_init: mach_gss_init_sec_context failed: %x %d\n", kr, kr);
349		if (kr == MIG_SERVER_DIED && cp->gss_cred == 0 &&
350			retry_cnt++ < GSS_MACH_MAX_RETRIES) {
351			if (cp->gss_tokenlen > 0)
352				gss_mach_alloc_buffer(cp->gss_token, cp->gss_tokenlen, &itoken);
353			if (cp->gss_cpn_len > 0)
354				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
355			if (cp->gss_spn_len > 0)
356				gss_mach_alloc_buffer(cp->gss_spn, cp->gss_spn_len, &spn);
357			goto retry;
358		}
359		goto out;
360	}
361
362	if (keylen > 0) {
363		/* Free any old key and reset the sequence number */
364		smb_reset_sig(vcp);
365		vcp->vc_mackeylen = keylen;
366		SMB_MALLOC(vcp->vc_mackey, uint8_t *, vcp->vc_mackeylen, M_SMBTEMP, M_WAITOK);
367		error = gss_mach_vmcopyout((vm_map_copy_t) okey, vcp->vc_mackeylen, vcp->vc_mackey);
368		if (error) {
369			gss_mach_vm_map_copy_discard((vm_map_copy_t)otoken, otokenlen);
370			goto out;
371		}
372
373        /*
374         * MS-SMB2 3.2.1.3 Per Session
375         * "Session.SessionKey: the first 16 bytes of the cryptographic key
376         * for this authenticated context...."
377         *
378         * So we must truncate the signing key if required.
379         * See <rdar://problem/13591834>.
380         */
381        if (vcp->vc_flags & SMBV_SMB2) {
382            if (vcp->vc_mackeylen > SMB2_KRB5_SESSION_KEYLEN) {
383                vcp->vc_mackeylen = SMB2_KRB5_SESSION_KEYLEN;
384            }
385        }
386
387		SMBDEBUG("%s keylen = %d seqno = %d\n", vcp->vc_srvname, keylen, vcp->vc_seqno);
388		smb_hexdump(__FUNCTION__, "setting vc_mackey = ", vcp->vc_mackey, vcp->vc_mackeylen);
389		/*
390		 * Windows expects the sequence number to restart once we get a signing
391		 * key. They expect this to happen once the client creates a authorization
392		 * token blob to send to the server. This was we can validate the servers
393		 * response. When doing Kerberos and now NTLMSSP we don't get the signing
394		 * key until after the gss mech has completed. Not sure how to really
395		 * fix this issue, but for now we just reset the sequence number as if
396		 * we had the key when the last round went out.
397		 */
398		vcp->vc_seqno = 2;
399	}
400
401	/* If we're done, see if the server is mapping everybody to guest */
402	if (SMB_GSS_COMPLETE(cp) && (gssd_flags & GSSD_GUEST_ONLY)) {
403		vcp->vc_flags |= SMBV_SFS_ACCESS;
404		SMBDEBUG("NTLMSSP simple file sharing\n");
405	}
406
407	/* Free context token used as input */
408    if (cp->gss_token != NULL) {
409        SMB_FREE(cp->gss_token, M_SMBTEMP);
410    }
411	cp->gss_tokenlen = 0;
412
413	if (otokenlen > 0) {
414		SMB_MALLOC(cp->gss_token, uint8_t *, otokenlen, M_SMBTEMP, M_WAITOK);
415		error = gss_mach_vmcopyout((vm_map_copy_t) otoken, otokenlen, cp->gss_token);
416		if (error)
417			goto out;
418		cp->gss_tokenlen = otokenlen;
419	}
420
421	if (cp->gss_cpn_display == NULL && display_name[0]) {
422		size_t len = strnlen(display_name, MAX_DISPLAY_STR);
423		SMB_MALLOC(cp->gss_cpn_display, char *, len, M_SMBTEMP, M_WAITOK);
424		if (cp->gss_cpn_display)
425			strlcpy(cp->gss_cpn_display, display_name, len);
426		SMBDEBUG("Received display name %s\n", display_name);
427	}
428
429	/*
430	 * We have a gss error, could be the Kerberos creditials doesn't exist yet or
431	 * has expired. If we are in reconnect then we may need to wait for the user
432	 * to correct the problem. So lets return EAGAIN so the reconnect code can
433	 * try again later.
434	 */
435	if (SMB_GSS_ERROR(cp) && (vcp->vc_iod->iod_flags & SMBIOD_RECONNECT))
436		return EAGAIN;
437
438	return (0);
439
440out:
441	SMB_FREE(cp->gss_token, M_SMBTEMP);
442	cp->gss_tokenlen = 0;
443
444	return (EAUTH);
445}
446
447/*
448 * smb_gss_vc_caps:
449 *
450 * Given a virtual circut, determine our capabilities to send to the server
451 * as part of "ssandx" message. We now call the general routine smb_vc_caps
452 * handle the basic items and add that we are doing extended security.
453 */
454static uint32_t smb_gss_vc_caps(struct smb_vc *vcp)
455{
456	return (smb_vc_caps(vcp) | SMB_CAP_EXT_SECURITY);
457}
458
459/*
460 * smb_gss_ssandx:
461 *
462 * Send a session setup and x message on vc with cred and caps
463 */
464int
465smb1_gss_ssandx(struct smb_vc *vcp, uint32_t caps, uint16_t *action,
466                vfs_context_t context)
467{
468	struct smb_rq *rqp = NULL;
469	struct smb_gss *gp = &vcp->vc_gss;
470	struct mbchain *mbp;
471	struct mdchain *mdp;
472	uint8_t		wc;
473	uint16_t	bc, toklen;
474	int			error;
475	uint32_t	tokenlen;
476	uint32_t	maxtokenlen;
477	uint8_t *	tokenptr;
478
479#ifdef SMB_DEBUG
480	/* For testing use a smaller max size */
481	maxtokenlen = 2048;
482#else // SMB_DEBUG
483	/* Get the max blob size we can send in SetupAndX message */
484	maxtokenlen = vcp->vc_txmax - (SMB_HDRLEN + SMB_SETUPXRLEN);
485#endif // SMB_DEBUG
486
487	tokenptr = gp->gss_token;	/* Get the start of the Kerberos blob */
488	do {
489		uint16_t maxtx = vcp->vc_txmax;
490		if (rqp)	/* If we are looping then release it, before getting it again */
491			smb_rq_done(rqp);
492
493		/* Allocate the request form a session setup and x */
494		error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, 0, context, &rqp);
495		if (error)
496			break;
497		/* Fill the request with the required parameters */
498		smb_rq_wstart(rqp);
499        smb_rq_getrequest(rqp, &mbp);
500		mb_put_uint8(mbp, 0xff);
501		mb_put_uint8(mbp, 0);
502		mb_put_uint16le(mbp, 0);
503		mb_put_uint16le(mbp, maxtx);
504		mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
505		mb_put_uint16le(mbp, vcp->vc_number);
506		mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
507		/* Get the max size we can send in one SetupAndX message */
508		tokenlen = (gp->gss_tokenlen > maxtokenlen) ? maxtokenlen : gp->gss_tokenlen;
509		mb_put_uint16le(mbp, tokenlen);
510		mb_put_uint32le(mbp, 0);		/* reserved */
511		mb_put_uint32le(mbp, caps);		/* our caps */
512		smb_rq_wend(rqp);
513		smb_rq_bstart(rqp);
514		/* SPNEGO blob */
515		mb_put_mem(mbp, (caddr_t) tokenptr, tokenlen, MB_MSYSTEM);
516		smb_put_dstring(mbp, SMB_UNICODE_STRINGS(vcp), SMBFS_NATIVEOS, sizeof(SMBFS_NATIVEOS), NO_SFM_CONVERSIONS);	/* Native OS */
517		smb_put_dstring(mbp, SMB_UNICODE_STRINGS(vcp), SMBFS_LANMAN, sizeof(SMBFS_LANMAN), NO_SFM_CONVERSIONS);	/* LAN Mgr */
518		smb_rq_bend(rqp);
519		/* Send the request and check for reply */
520		error = smb_rq_simple_timed(rqp, SMBSSNSETUPTIMO);
521		/* Move the pointer to the next offset in the blob */
522		tokenptr += tokenlen;
523		/* Subtract the size we have already sent */
524		gp->gss_tokenlen -= tokenlen;
525		/* Save the servers vc identifier if not already set. */
526		if ((error == EAGAIN) && (vcp->vc_smbuid == 0))
527			vcp->vc_smbuid = rqp->sr_rpuid;
528
529	} while (gp->gss_tokenlen && (error == EAGAIN));
530
531	/* Free the gss spnego token that we sent */
532	SMB_FREE(gp->gss_token, M_SMBTEMP);
533	gp->gss_tokenlen = 0;
534	gp->gss_smb_error = error;	/* Hold on to the last smb error returned */
535	/* EAGAIN is not  really an  error, reset it to no error */
536	if (error == EAGAIN) {
537		error = 0;
538	}
539	/* At this point error should have the correct error, we only support NTStatus code with extended security */
540	if (error) {
541		SMB_LOG_AUTH("Extended security authorization failed! %d\n", error);
542		goto bad;
543	}
544
545	/*
546	 * Save the servers vc identifier. Seems Samba will give us a new one for
547	 * every loop of a SetUpAndX NTLMSSP response. Windows server just return
548	 * the same one every time. We assume here the last one is the one we
549	 * should always use. Seems to make Samba work correctly.
550	 */
551	vcp->vc_smbuid = rqp->sr_rpuid;
552
553	/* Get the reply  and decode the result */
554	smb_rq_getreply(rqp, &mdp);
555	error = md_get_uint8(mdp, &wc);
556	if (error)
557		goto bad;
558	if (wc != 4) {
559		error = EBADRPC;
560		goto bad;
561	}
562	md_get_uint8(mdp, NULL);	/* secondary cmd */
563	md_get_uint8(mdp, NULL);	/* mbz */
564	md_get_uint16le(mdp, NULL);	/* andxoffset */
565	md_get_uint16le(mdp, action);	/* action */
566	md_get_uint16le(mdp, &toklen);	/* Spnego token length */
567	gp->gss_tokenlen = toklen;
568	md_get_uint16le(mdp, &bc); /* remaining bytes */
569	/*
570	 * Set the gss token from the server
571	 */
572    SMB_MALLOC(gp->gss_token, uint8_t *, gp->gss_tokenlen, M_SMBTEMP, M_WAITOK);
573    if (gp->gss_token == NULL)
574        goto bad;
575	error = md_get_mem(mdp, (caddr_t) gp->gss_token, gp->gss_tokenlen, MB_MSYSTEM);
576	if (error)
577		goto bad;
578	/* Determine the amount of data left in the buffer */
579	bc = (toklen > bc) ? 0 : bc - toklen;
580	/*
581	 * The rest of the message should contain the NativeOS and NativeLANManager
582	 * strings. If the message is in UNICODE then the byte count field is always
583	 * on an odd boundry, so we need to check to see if the security blob token
584	 * is odd or even. If the security blob toklen is even then we need to skip
585	 * the padd byte.
586	 */
587	if (SMB_UNICODE_STRINGS(vcp) && (bc > 0) && (!(toklen & 1))) {
588		md_get_uint8(mdp, NULL);	/* Skip Padd Byte */
589		bc -= 1;
590	}
591	/*
592	 * Now see if we can get the NativeOS and NativeLANManager strings. We
593	 * use these strings to tell if the server is a Win2k or XP system,
594	 * also Shared Computers wants this info.
595	 */
596	parse_server_os_lanman_strings(vcp, mdp, bc);
597
598bad:
599	smb_rq_done(rqp);
600	return (error);
601}
602
603/*
604 * smb_gss_ssnsetup:
605 *
606 * If we'er using gss, then we should be called from smb_smb_ssnsetup
607 * to do an extended security session setup and x to the server
608 */
609
610int
611smb_gss_ssnsetup(struct smb_vc *vcp, vfs_context_t context)
612{
613	int error = 0;
614	uint32_t caps;
615	uint16_t action = 0;
616
617	/* We should always have a gssd port! */
618	if (!SMB_USE_GSS(vcp)) {
619		SMBERROR("Doing Extended Security, but we don't have a gssd port!\n");
620		return (EINVAL);
621	}
622
623	/*
624	 * set the smbuid to zero so we will pick up the first
625	 * value returned from the server in smb_gss_ssandx
626	 */
627	vcp->vc_smbuid = 0;
628	vcp->vc_session_id = 0;
629
630	/* Get our caps from the vc. N.B. Seems only Samba uses this */
631	caps = smb_gss_vc_caps(vcp);
632
633	do {
634		/* Call gss to create a security blob */
635        error = smb_gss_init(vcp, vcp->vc_uid);
636
637		/* No token to send or error so just break out */
638		if (error || (SMB_GSS_ERROR(&vcp->vc_gss))) {
639			SMB_LOG_AUTH("GSSD extended security error = %d gss_major = %d gss_minor = %d\n",
640					   error, vcp->vc_gss.gss_major, vcp->vc_gss.gss_minor);
641
642            if ((vcp->vc_session_id != 0) || (vcp->vc_smbuid != 0)) {
643                /*
644                 * <13687368> If the GSS Auth fails on the client side in the
645                 * middle of SessionSetup exchanges (vc_session_id != 0 and
646                 * vc_smbuid != 0), then just logout which will tell the server
647                 * that the auth has failed
648                 */
649                (void)smb_smb_ssnclose(vcp, context);
650            }
651
652			/* Always return EAUTH unless we want the reconnect code to try again */
653			if (error != EAGAIN)
654				error = EAUTH;
655			break;
656		}
657		if ((vcp->vc_gss.gss_tokenlen) && ((error = smb_gss_ssandx(vcp, caps,
658                                                                   &action,
659                                                                   context))))
660			break;
661	} while (SMB_GSS_CONTINUE_NEEDED(&vcp->vc_gss));
662
663	if ((error == 0) && !SMBV_HAS_GUEST_ACCESS(vcp)
664        && !SMBV_HAS_ANONYMOUS_ACCESS(vcp) &&
665        (action & SMB_ACT_GUEST)) {
666		/*
667		 * We wanted to only login the users as guest if they ask to be login as
668		 * guest. Window system will login any bad user name as guest if guest is
669		 * turn on.
670		 * The first problem here is with XPHome. XPHome doesn't care if the
671		 * user is real or made up, it always logs them is as guest. We now have
672		 * a way to tell if Simple File Sharing (XPHome) is being used, so this
673		 * is no longer an issue.
674		 *
675		 * The second problem here is with Vista. Vista will log you in as guest
676		 * if your account has no password. The problem here is we don't always
677		 * know if the user provided a password or not. We could have the Network
678		 * Authorization Helper tell us, but this would not work in the cache
679		 * case.
680		 *
681		 * The user wanted authenticated access but got guest access, log them
682		 * out and return EAUTH.
683		 */
684        SMBWARNING("Got guest access, but wanted real access, logging off.\n");
685        (void)smb_smb_ssnclose(vcp, context);
686        error = EAUTH;
687	}
688	if ((error == 0) && (action & SMB_ACT_GUEST)) {
689		/* And the lying dogs might tells us to do signing when they don't */
690		vcp->vc_hflags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE;
691		smb_reset_sig(vcp);
692	}
693	if (error)	/* Reset the signature info */
694		smb_reset_sig(vcp);
695
696	smb_gss_reset(&vcp->vc_gss);
697	return error;
698}
699
700/*
701 * The VC needs to hold a reference on the credentials until its destroyed.
702 *
703 */
704void smb_gss_ref_cred(struct smb_vc *vcp)
705{
706	struct smb_gss *cp = &vcp->vc_gss;
707	vm_map_copy_t cpn = NULL;
708	gssd_mechtype mechtype;
709	kern_return_t kr;
710	int retry_cnt = 0;
711
712	SMBDEBUG("%s\n", vcp->vc_srvname);
713	if (!(VC_CAPS(vcp) & SMB_CAP_EXT_SECURITY)) {
714		/* Not using gssd then no way to take a reference */
715		return;
716	}
717	if ((cp->gss_cpn_len == 0) || (cp->gss_cpn == NULL)) {
718		/* No cred then no way to take a reference */
719		return;
720	}
721
722	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
723		mechtype = GSSD_NTLM_MECH;
724	} else {
725		mechtype = GSSD_SPNEGO_MECH;
726	}
727
728	gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
729retry:
730	kr = mach_gss_hold_cred(cp->gss_mp, mechtype, cp->gss_client_nt,
731							(gssd_byte_buffer)cpn,
732							(mach_msg_type_number_t)cp->gss_cpn_len,
733							&cp->gss_major, &cp->gss_minor);
734	if (kr != KERN_SUCCESS) {
735		SMB_LOG_AUTH("mach_gss_hold_cred failed: kr = 0x%x kr = %d\n", kr, kr);
736		if (kr == MIG_SERVER_DIED && retry_cnt++ < GSS_MACH_MAX_RETRIES) {
737			if (cp->gss_cpn_len > 0)
738				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
739			goto retry;
740		}
741	} else if (SMB_GSS_ERROR(cp)) {
742		SMB_LOG_AUTH("FAILED: gss_major = %d gss_minor = %d\n",
743				   cp->gss_major, cp->gss_minor);
744	}
745}
746
747/*
748 * The VC holds a reference on the credtials release it.
749 */
750void smb_gss_rel_cred(struct smb_vc *vcp)
751{
752	struct smb_gss *cp = &vcp->vc_gss;
753	vm_map_copy_t cpn = NULL;
754	gssd_mechtype mechtype;
755	kern_return_t kr;
756	int retry_cnt = 0;
757
758	SMBDEBUG("%s\n", vcp->vc_srvname);
759	/* Not using gssd then no way to take a reference */
760	if (!(VC_CAPS(vcp) & SMB_CAP_EXT_SECURITY)) {
761		return;
762	}
763	if ((cp->gss_cpn_len == 0) || (cp->gss_cpn == NULL)) {
764		/* No cred then no way to take a reference */
765		return;
766	}
767
768	if (vcp->vc_flags & SMBV_RAW_NTLMSSP) {
769		mechtype = GSSD_NTLM_MECH;
770	} else {
771		mechtype = GSSD_SPNEGO_MECH;
772	}
773
774	gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
775retry:
776	kr = mach_gss_unhold_cred(cp->gss_mp, mechtype, cp->gss_client_nt,
777							  (gssd_byte_buffer)cpn,
778							  (mach_msg_type_number_t)cp->gss_cpn_len,
779							  &cp->gss_major, &cp->gss_minor);
780	if (kr != KERN_SUCCESS) {
781		SMB_LOG_AUTH("mach_gss_unhold_cred failed: kr = 0x%x kr = %d\n", kr, kr);
782		if (kr == MIG_SERVER_DIED && retry_cnt++ < GSS_MACH_MAX_RETRIES) {
783			if (cp->gss_cpn_len > 0)
784				gss_mach_alloc_buffer(cp->gss_cpn, cp->gss_cpn_len, &cpn);
785			goto retry;
786		}
787	} else if (SMB_GSS_ERROR(cp)) {
788		SMB_LOG_AUTH("FAILED: gss_major = %d gss_minor = %d\n",
789				   cp->gss_major, cp->gss_minor);
790	}
791}
792