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