1/* 2 * Copyright (c) 2010 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 "NetworkAuthenticationHelper.h" 25#include "NetworkAuthenticationHelperGSS.h" 26#include "KerberosHelper.h" 27#include "KerberosHelperContext.h" 28 29#include <Heimdal/krb5.h> 30#include <Heimdal/hx509.h> 31 32#include <GSS/gssapi.h> 33#include <GSS/gssapi_ntlm.h> 34#include <GSS/gssapi_krb5.h> 35#include <GSS/gssapi_spi.h> 36 37#include <Kernel/gssd/gssd_mach_types.h> 38 39#include <CoreFoundation/CFRuntime.h> 40#include <Security/Security.h> 41#include <Security/SecCertificatePriv.h> 42#include <CommonCrypto/CommonDigest.h> 43 44#include <CoreServices/CoreServices.h> 45#include <CoreServices/CoreServicesPriv.h> 46 47#include "LKDCHelper.h" 48#include "DeconstructServiceName.h" 49#include "utils.h" 50 51static void add_user_selections(NAHRef na); 52 53 54const CFStringRef kNAHServiceAFPServer = CFSTR("afpserver"); 55const CFStringRef kNAHServiceCIFSServer = CFSTR("cifs"); 56const CFStringRef kNAHServiceHostServer = CFSTR("host"); 57const CFStringRef kNAHServiceVNCServer = CFSTR("vnc"); 58 59static const char *nah_created = "nah-created"; 60 61static bool nah_use_gss_uam = true; 62static bool nah_vnc_support_iakerb = true; 63static dispatch_once_t init_globals; 64 65enum NAHMechType { 66 NO_MECH = 0, 67 GSS_KERBEROS, 68 GSS_KERBEROS_U2U, 69 GSS_KERBEROS_IAKERB, 70 GSS_KERBEROS_PKU2U, 71 GSS_NTLM, 72 GSS_SPNEGO 73}; 74 75struct NAHSelectionData { 76 CFRuntimeBase base; 77 NAHRef na; 78 dispatch_semaphore_t wait; 79 int waiting; 80 int have_cred; 81 int canceled; 82 int done; 83 84 enum NAHMechType mech; 85 CFStringRef client; 86 CFStringRef clienttype; 87 CFStringRef server; 88 CFStringRef servertype; 89 90 SecIdentityRef certificate; 91 bool spnego; 92 93 CFStringRef inferredLabel; 94 95 krb5_ccache ccache; 96}; 97 98struct NAHData { 99 CFRuntimeBase base; 100 CFAllocatorRef alloc; 101 102 /* Input */ 103 104 CFMutableStringRef hostname; 105 CFMutableStringRef lchostname; 106 CFStringRef service; 107 CFStringRef username; 108 CFStringRef specificname; 109 CFDictionaryRef servermechs; 110 111 CFStringRef spnegoServerName; 112 113 /* credentials */ 114 CFArrayRef x509identities; 115 CFStringRef password; 116 117 CFArrayRef mechs; 118 119 /* intermediate results */ 120 dispatch_queue_t q; 121 dispatch_queue_t bgq; 122 krb5_context context; 123 hx509_context hxctx; 124 125 /* final result */ 126 127 CFMutableArrayRef selections; 128}; 129 130static void signal_result(NAHSelectionRef selection); 131static Boolean wait_result(NAHSelectionRef selection); 132static void nalog(int level, CFStringRef fmt, ...) 133 __attribute__((format(CFString, 2, 3))); 134 135#define CFRELEASE(x) do { if ((x)) { CFRelease((x)); (x) = NULL; } } while(0) 136 137/* 138 * 139 */ 140 141CFStringRef 142NAHCopyMMeUserNameFromCertificate(SecCertificateRef cert) 143{ 144 unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 145 char str[CC_SHA1_DIGEST_LENGTH * 2 + 1]; 146 CFDataRef certData; 147 CC_SHA1_CTX ctx; 148 char *cpOut; 149 unsigned dex; 150 151 certData = SecCertificateCopyData(cert); 152 CFRelease(cert); 153 if (NULL == certData) 154 return NULL; 155 156 CC_SHA1_Init(&ctx); 157 CC_SHA1_Update(&ctx, CFDataGetBytePtr(certData), CFDataGetLength(certData)); 158 CC_SHA1_Final(digest, &ctx); 159 160 CFRelease(certData); 161 162 cpOut = str; 163 164 for(dex = 0; dex < CC_SHA1_DIGEST_LENGTH; dex++) { 165 snprintf(cpOut, 3, "%02X", (unsigned)(digest[dex])); 166 cpOut += 2; 167 } 168 169 return CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); 170} 171 172/* 173 * 174 */ 175 176static char * 177cf2cstring(CFStringRef inString) 178{ 179 char *string = NULL; 180 CFIndex length; 181 182 string = (char *) CFStringGetCStringPtr(inString, kCFStringEncodingUTF8); 183 if (string) 184 return strdup(string); 185 186 length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(inString), 187 kCFStringEncodingUTF8) + 1; 188 string = malloc (length); 189 if (string == NULL) 190 return NULL; 191 if (!CFStringGetCString(inString, string, length, kCFStringEncodingUTF8)) { 192 free (string); 193 return NULL; 194 } 195 return string; 196} 197 198/* 199 * 200 */ 201 202static void 203nalog(int level, CFStringRef fmt, ...) 204{ 205 CFStringRef str; 206 va_list ap; 207 char *s; 208 209 va_start(ap, fmt); 210 str = CFStringCreateWithFormatAndArguments(NULL, 0, fmt, ap); 211 va_end(ap); 212 213 if (str == NULL) 214 return; 215 216 if (__KRBCreateUTF8StringFromCFString (str, &s) == 0) { 217 asl_log(NULL, NULL, level, "%s", s); 218 __KRBReleaseUTF8String(s); 219 } 220 CFRelease(str); 221} 222 223static void 224naselrelease(NAHSelectionRef nasel) 225{ 226 if (nasel->waiting != 0) 227 abort(); 228 if (nasel->wait) 229 dispatch_release(nasel->wait); 230 CFRELEASE(nasel->client); 231 CFRELEASE(nasel->clienttype); 232 CFRELEASE(nasel->server); 233 CFRELEASE(nasel->servertype); 234 if (nasel->ccache) 235 krb5_cc_close(nasel->na->context, nasel->ccache); 236 CFRELEASE(nasel->certificate); 237 CFRELEASE(nasel->inferredLabel); 238} 239 240static CFStringRef naseldebug(NAHSelectionRef nasel) CF_RETURNS_RETAINED; 241static CFStringRef naselformatting(CFTypeRef cf, CFDictionaryRef formatOptions) CF_RETURNS_RETAINED; 242 243 244 245 246static CFStringRef 247naseldebug(NAHSelectionRef nasel) 248{ 249 if (!wait_result(nasel)) 250 return CFSTR("selection canceled"); 251 252 CFStringRef mech = NAHSelectionGetInfoForKey(nasel, kNAHMechanism); 253 CFStringRef innermech = NAHSelectionGetInfoForKey(nasel, kNAHInnerMechanism); 254 255 return CFStringCreateWithFormat(NULL, NULL, 256 CFSTR("<NetworkAuthenticationSelection: " 257 "%@<%@>, %@ %@ spnego: %s>"), 258 mech, innermech, 259 nasel->client, 260 nasel->server, 261 nasel->spnego ? "yes" : "no"); 262} 263 264static CFStringRef 265naselformatting(CFTypeRef cf, CFDictionaryRef formatOptions) 266{ 267 return naseldebug((NAHSelectionRef)cf); 268} 269 270const CFStringRef kNAHErrorDomain = CFSTR("com.apple.NetworkAuthenticationHelper"); 271 272static bool 273updateError(CFAllocatorRef alloc, CFErrorRef *error, CFIndex errorcode, CFStringRef fmt, ...) 274{ 275 const void *keys[1] = { kCFErrorDescriptionKey }; 276 void *values[1]; 277 va_list va; 278 279 if (error == NULL) 280 return false; 281 282 va_start(va, fmt); 283 values[0] = (void *)CFStringCreateWithFormatAndArguments(alloc, NULL, fmt, va); 284 va_end(va); 285 if (values[0] == NULL) { 286 *error = NULL; 287 return false; 288 } 289 290 nalog(ASL_LEVEL_DEBUG, CFSTR("NAH: error: %@"), values[0]); 291 292 *error = CFErrorCreateWithUserInfoKeysAndValues(alloc, kNAHErrorDomain, errorcode, 293 (const void * const *)keys, 294 (const void * const *)values, 1); 295 CFRelease(values[0]); 296 297 return true; 298} 299 300static struct { 301 CFStringRef name; 302 enum NAHMechType mech; 303 gss_OID oid; 304} mechs[] = { 305 { CFSTR("Kerberos"), GSS_KERBEROS, GSS_KRB5_MECHANISM }, 306 { CFSTR("KerberosUser2User"), GSS_KERBEROS_U2U, NULL }, 307 { CFSTR("PKU2U"), GSS_KERBEROS_PKU2U, GSS_PKU2U_MECHANISM }, 308 { CFSTR("IAKerb"), GSS_KERBEROS_IAKERB, GSS_IAKERB_MECHANISM }, 309 { CFSTR("NTLM"), GSS_NTLM, GSS_NTLM_MECHANISM }, 310 { CFSTR("SPNEGO"), GSS_SPNEGO, GSS_SPNEGO_MECHANISM }, 311 { CFSTR("SPENGO"), GSS_SPNEGO, GSS_SPNEGO_MECHANISM } 312}; 313 314static enum NAHMechType 315name2mech(CFStringRef name) 316{ 317 size_t n; 318 319 if (name == NULL) 320 return NO_MECH; 321 322 for (n = 0; n < sizeof(mechs) / sizeof(mechs[0]); n++) { 323 if (CFStringCompare(mechs[n].name, name, kCFCompareCaseInsensitive) == kCFCompareEqualTo) 324 return mechs[n].mech; 325 } 326 return NO_MECH; 327} 328 329static gss_OID 330name2oid(CFStringRef name) 331{ 332 size_t n; 333 334 if (name == NULL) 335 return NO_MECH; 336 337 for (n = 0; n < sizeof(mechs) / sizeof(mechs[0]); n++) { 338 if (CFStringCompare(mechs[n].name, name, kCFCompareCaseInsensitive) == kCFCompareEqualTo) 339 return mechs[n].oid; 340 } 341 return NULL; 342} 343 344static CFStringRef 345mech2name(enum NAHMechType mech) 346{ 347 size_t n; 348 for (n = 0; n < sizeof(mechs) / sizeof(mechs[0]); n++) { 349 if (mechs[n].mech == mech) 350 return mechs[n].name; 351 } 352 return NULL; 353} 354 355 356static gss_OID 357mech2oid(enum NAHMechType mech) 358{ 359 size_t n; 360 for (n = 0; n < sizeof(mechs) / sizeof(mechs[0]); n++) { 361 if (mechs[n].mech == mech) 362 return mechs[n].oid; 363 } 364 return GSS_C_NO_OID; 365} 366 367 368static CFTypeID 369NAHSelectionGetTypeID(void) 370{ 371 static CFTypeID naselid = _kCFRuntimeNotATypeID; 372 static dispatch_once_t inited; 373 374 dispatch_once(&inited, ^{ 375 static const CFRuntimeClass naselclass = { 376 0, 377 "NetworkAuthenticationSelection", 378 NULL, 379 NULL, 380 (void(*)(CFTypeRef))naselrelease, 381 NULL, 382 NULL, 383 naselformatting, 384 (CFStringRef (*)(CFTypeRef))naseldebug 385 }; 386 naselid = _CFRuntimeRegisterClass(&naselclass); 387 }); 388 389 return naselid; 390} 391 392static NAHSelectionRef 393NAHSelectionAlloc(NAHRef na) 394{ 395 CFTypeID id = NAHSelectionGetTypeID(); 396 NAHSelectionRef nasel; 397 398 if (id == _kCFRuntimeNotATypeID) 399 return NULL; 400 401 nasel = (NAHSelectionRef)_CFRuntimeCreateInstance(na->alloc, id, sizeof(struct NAHSelectionData) - sizeof(CFRuntimeBase), NULL); 402 if (nasel == NULL) 403 return NULL; 404 405 nasel->na = na; 406 nasel->spnego = true; 407 408 return nasel; 409 410} 411 412static void 413nahrelease(NAHRef na) 414{ 415 CFRELEASE(na->hostname); 416 CFRELEASE(na->lchostname); 417 CFRELEASE(na->service); 418 CFRELEASE(na->username); 419 CFRELEASE(na->specificname); 420 CFRELEASE(na->servermechs); 421 CFRELEASE(na->spnegoServerName); 422 423 CFRELEASE(na->x509identities); 424 CFRELEASE(na->password); 425 426 CFRELEASE(na->mechs); 427 428 CFRELEASE(na->selections); 429 430 if (na->q) 431 dispatch_release(na->q); 432 if (na->context) 433 krb5_free_context(na->context); 434 if (na->hxctx) 435 hx509_context_free(&na->hxctx); 436} 437 438static CFTypeID 439NAGetTypeID(void) 440{ 441 static CFTypeID naid = _kCFRuntimeNotATypeID; 442 static dispatch_once_t inited; 443 444 dispatch_once(&inited, ^{ 445 static const CFRuntimeClass naclass = { 446 0, 447 "NetworkAuthentication", 448 NULL, 449 NULL, 450 (void(*)(CFTypeRef))nahrelease, 451 NULL, 452 NULL, 453 NULL, 454 NULL 455 }; 456 naid = _CFRuntimeRegisterClass(&naclass); 457 }); 458 459 return naid; 460} 461 462/* 463 * 464 */ 465 466static NAHRef 467NAAlloc(CFAllocatorRef alloc) 468{ 469 CFTypeID id = NAGetTypeID(); 470 NAHRef na; 471 472 if (id == _kCFRuntimeNotATypeID) 473 return NULL; 474 475 na = (NAHRef)_CFRuntimeCreateInstance(alloc, id, sizeof(struct NAHData) - sizeof(CFRuntimeBase), NULL); 476 if (na == NULL) 477 return NULL; 478 479 na->q = dispatch_queue_create("network-authentication", NULL); 480 na->bgq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 481 482 na->alloc = alloc; 483 484 return na; 485} 486 487static bool 488haveMech(NAHRef na, CFStringRef mech) 489{ 490 if (na->servermechs == NULL) 491 return false; 492 if (CFDictionaryGetValue(na->servermechs, mech) != NULL) 493 return true; 494 return false; 495} 496 497/* 498 * Status of selection 499 */ 500 501const CFStringRef kNAHSelectionHaveCredential = CFSTR("kNAHSelectionHaveCredential"); 502const CFStringRef kNAHSelectionUserPrintable = CFSTR("kNAHSelectionUserPrintable"); 503const CFStringRef kNAHClientPrincipal = CFSTR("kNAHClientPrincipal"); 504const CFStringRef kNAHServerPrincipal = CFSTR("kNAHServerPrincipal"); 505const CFStringRef kNAHMechanism = CFSTR("kNAHMechanism"); 506const CFStringRef kNAHInnerMechanism = CFSTR("kNAHInnerMechanism"); 507const CFStringRef kNAHCredentialType = CFSTR("kNAHCredentialType"); 508const CFStringRef kNAHUseSPNEGO = CFSTR("kNAHUseSPNEGO"); 509 510const CFStringRef kNAHClientNameType = CFSTR("kNAHClientNameType"); 511const CFStringRef kNAHClientNameTypeGSSD = CFSTR("kNAHClientNameTypeGSSD"); 512 513const CFStringRef kNAHServerNameType = CFSTR("kNAHServerNameType"); 514const CFStringRef kNAHServerNameTypeGSSD = CFSTR("kNAHServerNameTypeGSSD"); 515 516const CFStringRef kNAHNTUsername = CFSTR("kNAHNTUsername"); 517const CFStringRef kNAHNTServiceBasedName = CFSTR("kNAHNTServiceBasedName"); 518const CFStringRef kNAHNTKRB5PrincipalReferral = CFSTR("kNAHNTKRB5PrincipalReferral"); 519const CFStringRef kNAHNTKRB5Principal = CFSTR("kNAHNTKRB5Principal"); 520const CFStringRef kNAHNTUUID = CFSTR("kNAHNTUUID"); 521 522 523const CFStringRef kNAHInferredLabel = CFSTR("kNAHInferredLabel"); 524 525 526/* 527 * Add selection 528 */ 529 530enum { 531 USE_SPNEGO = 1, 532 FORCE_ADD = 2 533}; 534 535static NAHSelectionRef 536addSelection(NAHRef na, 537 CFStringRef client, 538 CFStringRef clienttype, 539 CFStringRef server, 540 CFStringRef servertype, 541 enum NAHMechType mech, 542 int *duplicate, 543 unsigned long flags) 544{ 545 NAHSelectionRef nasel; 546 int matching; 547 CFIndex n; 548 549 if (clienttype == NULL) 550 clienttype = kNAHNTUsername; 551 552 if (servertype == NULL) 553 servertype = kNAHNTServiceBasedName; 554 555 matching = (flags & FORCE_ADD) || (na->specificname == NULL) || CFStringHasPrefix(client, na->specificname); 556 557 nalog(ASL_LEVEL_DEBUG, CFSTR("addSelection: %@ (%d) %@ %@ %s %s"), 558 mech2name(mech), (int)mech, client, server, (flags & USE_SPNEGO) ? "SPNEGO" : "raw", 559 matching ? "matching" : "no-matching"); 560 561 /* If no matching, skip this credential */ 562 if (matching == 0) 563 return NULL; 564 565 /* check for dups */ 566 for (n = 0; n < CFArrayGetCount(na->selections); n++) { 567 nasel = (NAHSelectionRef)CFArrayGetValueAtIndex(na->selections, n); 568 if (nasel->mech != mech) 569 continue; 570 if (CFStringCompare(nasel->client, client, 0) != kCFCompareEqualTo) 571 continue; 572 if (nasel->server && server && CFStringCompare(nasel->server, server, 0) != kCFCompareEqualTo) 573 continue; 574 if (CFStringCompare(nasel->servertype, servertype, 0) != kCFCompareEqualTo) 575 continue; 576 if (duplicate) 577 *duplicate = 1; 578 return nasel; 579 } 580 if (duplicate) 581 *duplicate = 0; 582 583 nasel = NAHSelectionAlloc(na); 584 if (nasel == NULL) 585 return NULL; 586 587 nasel->client = CFRetain(client); 588 if (server) { 589 nasel->server = CFRetain(server); 590 nasel->waiting = 0; 591 nasel->wait = NULL; 592 } else { 593 nasel->server = NULL; 594 nasel->waiting = 0; 595 nasel->wait = dispatch_semaphore_create(0); 596 } 597 nasel->done = 0; 598 nasel->clienttype = clienttype; 599 CFRetain(clienttype); 600 nasel->servertype = servertype; 601 CFRetain(servertype); 602 603 nasel->mech = mech; 604 nasel->spnego = (flags & USE_SPNEGO) ? true : false; 605 606 CFArrayAppendValue(na->selections, nasel); 607 608 CFRelease(nasel); /* referenced by array */ 609 610 return nasel; 611} 612 613static bool 614findUsername(CFAllocatorRef alloc, NAHRef na, CFDictionaryRef info) 615{ 616 char *name; 617 618 if (info) { 619 na->username = CFDictionaryGetValue(info, kNAHUserName); 620 if (na->username) { 621 CFRange range, ur; 622 623 CFRetain(na->username); 624 625 if (CFStringFindWithOptions(na->username, CFSTR("@"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 626 ur.location = 0; 627 ur.length = range.location; 628 na->specificname = CFStringCreateWithSubstring(na->alloc, na->username, ur); 629 } else if (CFStringFindWithOptions(na->username, CFSTR("\\"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 630 ur.location = range.location + 1; 631 ur.length = CFStringGetLength(na->username) - ur.location; 632 na->specificname = CFStringCreateWithSubstring(na->alloc, na->username, ur); 633 } else { 634 na->specificname = na->username; 635 CFRetain(na->specificname); 636 } 637 638 nalog(ASL_LEVEL_DEBUG, CFSTR("NAH: specific name is: %@ foo"), na->specificname); 639 640 return true; 641 } 642 } 643 644 name = getlogin(); 645 if (name == NULL) 646 return false; 647 648 na->username = CFStringCreateWithCString(alloc, name, kCFStringEncodingUTF8); 649 if (na->username == NULL) 650 return false; 651 652 return true; 653} 654 655static void 656classic_lkdc_background(NAHSelectionRef nasel) 657{ 658 LKDCHelperErrorType ret; 659 char *hostname = NULL, *realm = NULL; 660 CFStringRef u; 661 662 ret = __KRBCreateUTF8StringFromCFString(nasel->na->hostname, &hostname); 663 if (ret) 664 goto out; 665 666 ret = LKDCDiscoverRealm(hostname, &realm); 667 if (ret) 668 goto out; 669 670 nasel->server = CFStringCreateWithFormat(nasel->na->alloc, NULL, 671 CFSTR("%@/%s@%s"), 672 nasel->na->service, realm, realm); 673 674 u = nasel->client; 675 676 nasel->client = 677 CFStringCreateWithFormat(nasel->na->alloc, 0, CFSTR("%@@%s"), u, realm); 678 CFRELEASE(u); 679 680 out: 681 __KRBReleaseUTF8String(hostname); 682 free(realm); 683 signal_result(nasel); 684} 685 686/* 687 * Returns true for those hostname that looks like those hostname 688 * where we should use LKDC hostnames instead. This check is only used 689 * for protocol where we don't know wether we should use LKDC or 690 * classic Kerberos. 691 */ 692 693static bool 694have_lkdcish_hostname(NAHRef na, bool localIsLKDC) 695{ 696 CFMutableStringRef btmmDomain = NULL; 697 CFStringRef btmmDomainData; 698 bool ret = false; 699 700 btmmDomainData = _CSBackToMyMacCopyDomain(); 701 if (btmmDomainData) { 702 btmmDomain = CFStringCreateMutableCopy(na->alloc, 0, btmmDomainData); 703 CFRELEASE(btmmDomainData); 704 if (btmmDomain) { 705 CFStringTrim(btmmDomain, CFSTR(".")); 706 nalog(ASL_LEVEL_DEBUG, CFSTR("using BTMM domain %@"), btmmDomain); 707 } 708 } 709 710 711 if (na->lchostname == NULL) { 712 na->lchostname = CFStringCreateMutableCopy(NULL, 0, na->hostname); 713 if (na->lchostname == NULL) { 714 CFRELEASE(btmmDomain); 715 return false; 716 } 717 } 718 719 CFStringLowercase(na->lchostname, CFLocaleGetSystem()); 720 721 if (localIsLKDC && CFStringHasSuffix(na->lchostname, CFSTR(".local"))) 722 ret = true; 723 else if (btmmDomain && CFStringHasSuffix(na->lchostname, btmmDomain)) 724 ret = true; 725 726 CFRELEASE(btmmDomain); 727 728 return ret; 729} 730 731static void 732classic_lkdc(NAHRef na, unsigned long flags) 733{ 734 NAHSelectionRef nasel; 735 int duplicate; 736 737 if (!have_lkdcish_hostname(na, true)) 738 return; 739 740 if (na->password) { 741 nasel = addSelection(na, 742 na->username, kNAHNTKRB5Principal, 743 NULL, kNAHNTKRB5PrincipalReferral, 744 GSS_KERBEROS, &duplicate, flags); 745 if (nasel && !duplicate) { 746 CFRetain(na); 747 dispatch_async(na->bgq, ^{ 748 classic_lkdc_background(nasel); 749 CFRelease(na); 750 }); 751 } 752 } 753} 754 755static void 756add_realms(NAHRef na, char **realms, unsigned long flags) 757{ 758 CFStringRef u, s; 759 size_t n; 760 761 for (n = 0; realms[n] != NULL; n++) { 762 u = CFStringCreateWithFormat(na->alloc, 0, CFSTR("%@@%s"), na->username, realms[n]); 763 s = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%@@%s"), na->service, na->hostname, realms[n]); 764 765 if (u && s) 766 addSelection(na, u, kNAHNTKRB5Principal, 767 s, kNAHNTKRB5PrincipalReferral, GSS_KERBEROS, NULL, flags); 768 CFRELEASE(u); 769 CFRELEASE(s); 770 } 771} 772 773 774static void 775use_classic_kerberos(NAHRef na, unsigned long flags) 776{ 777 CFRange range, dr, ur; 778 char **realms, *str; 779 int ret; 780 781 if (have_lkdcish_hostname(na, false)) 782 return; 783 784 ret = __KRBCreateUTF8StringFromCFString(na->hostname, &str); 785 if (ret) 786 return; 787 788 /* 789 * If user have @REALM, lets try that out 790 */ 791 792 if (CFStringFindWithOptions(na->username, CFSTR("@"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 793 CFStringRef domain = NULL, s = NULL; 794 CFMutableStringRef domainm = NULL; 795 796 dr.location = range.location + 1; 797 dr.length = CFStringGetLength(na->username) - dr.location; 798 799 domain = CFStringCreateWithSubstring(na->alloc, na->username, dr); 800 if (domain) { 801 domainm = CFStringCreateMutableCopy(na->alloc, 0, domain); 802 803 if (domainm) { 804 CFStringUppercase(domainm, NULL); 805 806 s = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%@@%@"), 807 na->service, na->hostname, domainm); 808 809 if (s) 810 addSelection(na, na->username, kNAHNTKRB5Principal, 811 s, kNAHNTKRB5PrincipalReferral, GSS_KERBEROS, NULL, flags); 812 } 813 } 814 CFRELEASE(domainm); 815 CFRELEASE(domain); 816 CFRELEASE(s); 817 } 818 819 if (CFStringFindWithOptions(na->username, CFSTR("\\"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 820 CFStringRef domain = NULL, user = NULL, user2 = NULL, s = NULL; 821 CFMutableStringRef domainm = NULL; 822 823 dr.location = 0; 824 dr.length = range.location; 825 826 ur.location = range.location + 1; 827 ur.length = CFStringGetLength(na->username) - ur.location; 828 829 user = CFStringCreateWithSubstring(na->alloc, na->username, ur); 830 domain = CFStringCreateWithSubstring(na->alloc, na->username, dr); 831 if (domain && user) { 832 domainm = CFStringCreateMutableCopy(na->alloc, 0, domain); 833 user2 = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@@%@"), user, domain); 834 835 if (domainm && user2) { 836 CFStringUppercase(domainm, NULL); 837 838 s = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%@@%@"), 839 na->service, na->hostname, domainm); 840 841 if (s) 842 addSelection(na, user2, kNAHNTKRB5Principal, 843 s, kNAHNTKRB5PrincipalReferral, GSS_KERBEROS, NULL, flags|FORCE_ADD); 844 } 845 } 846 CFRELEASE(domainm); 847 CFRELEASE(domain); 848 CFRELEASE(user2); 849 CFRELEASE(user); 850 CFRELEASE(s); 851 } 852 853 /* 854 * Try the host realm 855 */ 856 857 ret = krb5_get_host_realm(na->context, str, &realms); 858 __KRBReleaseUTF8String(str); 859 if (ret == 0) { 860 add_realms(na, realms, flags); 861 krb5_free_host_realm(na->context, realms); 862 } 863 864 /* 865 * Also, just for the heck of it, check default realms 866 */ 867 868 ret = krb5_get_default_realms(na->context, &realms); 869 if (ret == 0) { 870 add_realms(na, realms, flags); 871 krb5_free_host_realm(na->context, realms); 872 } 873} 874 875/* 876 * 877 */ 878 879static void 880use_existing_principals(NAHRef na, int only_lkdc, unsigned long flags) 881{ 882 krb5_cccol_cursor cursor; 883 krb5_principal client; 884 krb5_error_code ret; 885 CFStringRef server; 886 krb5_ccache id; 887 CFStringRef u; 888 char *c; 889 890 ret = krb5_cccol_cursor_new(na->context, &cursor); 891 if (ret) 892 return; 893 894 while ((ret = krb5_cccol_cursor_next(na->context, cursor, &id)) == 0 && id != NULL) { 895 NAHSelectionRef nasel; 896 int is_lkdc; 897 time_t t; 898 899 ret = krb5_cc_get_principal(na->context, id, &client); 900 if (ret) { 901 krb5_cc_close(na->context, id); 902 continue; 903 } 904 905 ret = krb5_cc_get_lifetime(na->context, id, &t); 906 if (ret || t <= 0) { 907 krb5_cc_close(na->context, id); 908 continue; 909 } 910 911 is_lkdc = krb5_principal_is_lkdc(na->context, client); 912 913 if ((only_lkdc && !is_lkdc) || (!only_lkdc && is_lkdc)) { 914 krb5_free_principal(na->context, client); 915 krb5_cc_close(na->context, id); 916 continue; 917 } 918 919 ret = krb5_unparse_name(na->context, client, &c); 920 if (ret) { 921 krb5_free_principal(na->context, client); 922 krb5_cc_close(na->context, id); 923 continue; 924 } 925 926 u = CFStringCreateWithCString(na->alloc, c, kCFStringEncodingUTF8); 927 free(c); 928 if (u == NULL) { 929 krb5_free_principal(na->context, client); 930 krb5_cc_close(na->context, id); 931 continue; 932 } 933 934 if (is_lkdc) { 935 CFStringRef cr = NULL; 936 krb5_data data; 937 938 ret = krb5_cc_get_config(na->context, id, NULL, "lkdc-hostname", &data); 939 if (ret == 0) { 940 cr = CFStringCreateWithBytes(na->alloc, data.data, data.length, kCFStringEncodingUTF8, false); 941 krb5_data_free(&data); 942 } 943 944 if (cr == NULL || CFStringCompare(na->hostname, cr, 0) != kCFCompareEqualTo) { 945 krb5_free_principal(na->context, client); 946 krb5_cc_close(na->context, id); 947 continue; 948 } 949 950 /* Create server principal */ 951 server = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%s@%s"), 952 na->service, client->realm, client->realm); 953 954 nalog(ASL_LEVEL_DEBUG, CFSTR("Adding existing LKDC cache: %@ -> %@"), u, server); 955 956 } else { 957 server = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%@@%s"), na->service, na->hostname, 958 krb5_principal_get_realm(na->context, client)); 959 nalog(ASL_LEVEL_DEBUG, CFSTR("Adding existing cache: %@ -> %@"), u, server); 960 } 961 krb5_free_principal(na->context, client); 962 963 nasel = addSelection(na, u, kNAHNTKRB5Principal, 964 server, kNAHNTKRB5PrincipalReferral, GSS_KERBEROS, NULL, flags); 965 CFRELEASE(u); 966 CFRELEASE(server); 967 if (nasel != NULL && nasel->ccache == NULL) { 968 krb5_data data; 969 nasel->ccache = id; 970 nasel->have_cred = 1; 971 972 if (nasel->inferredLabel == NULL) { 973 ret = krb5_cc_get_config(na->context, id, NULL, "FriendlyName", &data); 974 if (ret == 0) { 975 nasel->inferredLabel = CFStringCreateWithBytes(na->alloc, data.data, data.length, kCFStringEncodingUTF8, false); 976 krb5_data_free(&data); 977 } 978 } 979 980 } else 981 krb5_cc_close(na->context, id); 982 983 } 984 985 krb5_cccol_cursor_free(na->context, &cursor); 986} 987 988static CFStringRef kWELLKNOWN_LKDC = CFSTR("WELLKNOWN:COM.APPLE.LKDC"); 989 990static void 991wellknown_lkdc(NAHRef na, enum NAHMechType mechtype, unsigned long flags) 992{ 993 CFStringRef s, u; 994 CFIndex n; 995 996 u = CFStringCreateWithFormat(na->alloc, NULL, 997 CFSTR("%@@%@"), 998 na->username, kWELLKNOWN_LKDC); 999 if (u == NULL) 1000 return; 1001 1002 s = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/localhost@%@"), 1003 na->service, kWELLKNOWN_LKDC); 1004 if (s == NULL) { 1005 CFRELEASE(u); 1006 return; 1007 } 1008 1009 if (na->password) 1010 addSelection(na, u, kNAHNTKRB5Principal, 1011 s, kNAHNTKRB5Principal, mechtype, NULL, flags); 1012 CFRELEASE(u); 1013 1014 /* if we have certs, lets push those names too */ 1015 for (n = 0; na->x509identities && n < CFArrayGetCount(na->x509identities); n++) { 1016 SecIdentityRef identity = (void *)CFArrayGetValueAtIndex(na->x509identities, n); 1017 CFStringRef csstr; 1018 SecCertificateRef cert = NULL; 1019 1020 if (SecIdentityCopyCertificate(identity, &cert)) 1021 continue; 1022 1023 csstr = _CSCopyKerberosPrincipalForCertificate(cert); 1024 CFRelease(cert); 1025 if (csstr == NULL) { 1026 hx509_cert hxcert; 1027 char *str; 1028 int ret; 1029 1030 ret = hx509_cert_init_SecFramework(na->hxctx, identity, &hxcert); 1031 if (ret) 1032 continue; 1033 1034 ret = hx509_cert_get_appleid(na->hxctx, hxcert, &str); 1035 hx509_cert_free(hxcert); 1036 if (ret) 1037 continue; 1038 1039 u = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%s@%@"), 1040 str, kWELLKNOWN_LKDC); 1041 krb5_xfree(str); 1042 if (u == NULL) 1043 continue; 1044 } else { 1045 u = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@@%@"), 1046 csstr, kWELLKNOWN_LKDC); 1047 CFRelease(csstr); 1048 if (u == NULL) 1049 continue; 1050 } 1051 1052 NAHSelectionRef nasel = addSelection(na, u, kNAHNTKRB5Principal, s, kNAHNTKRB5PrincipalReferral, mechtype, NULL, flags); 1053 CFRELEASE(u); 1054 if (nasel) { 1055 CFRetain(identity); 1056 nasel->certificate = identity; 1057 } 1058 } 1059 1060 CFRELEASE(s); 1061} 1062 1063static bool 1064is_smb(NAHRef na) 1065{ 1066 if (CFStringCompare(na->service, kNAHServiceHostServer, 0) == kCFCompareEqualTo || 1067 CFStringCompare(na->service, kNAHServiceCIFSServer, 0) == kCFCompareEqualTo) 1068 return true; 1069 return false; 1070} 1071 1072static void 1073guess_kerberos(NAHRef na) 1074{ 1075 bool try_lkdc_classic = true; 1076 bool try_wlkdc = false; 1077 bool try_iakerb_with_lkdc = false; 1078 bool have_kerberos = false; 1079 krb5_error_code ret; 1080 unsigned long flags = USE_SPNEGO; 1081 1082 if (nah_use_gss_uam 1083 && (na->password || na->x509identities) 1084 && haveMech(na, kGSSAPIMechIAKERB) 1085 && haveMech(na, kGSSAPIMechSupportsAppleLKDC)) 1086 { 1087 /* if we support IAKERB and AppleLDKC and is not SMB (client can't handle it, rdar://problem/8437184), let go for iakerb with */ 1088 try_iakerb_with_lkdc = true; 1089 } else if (haveMech(na, kGSSAPIMechPKU2UOID) || haveMech(na, kGSSAPIMechSupportsAppleLKDC)) { 1090 try_wlkdc = true; 1091 } else if (CFStringCompare(na->service, kNAHServiceVNCServer, 0) == kCFCompareEqualTo) { 1092 try_wlkdc = true; 1093 if (nah_vnc_support_iakerb && (na->password || na->x509identities)) 1094 try_iakerb_with_lkdc = true; 1095 } 1096 1097 /* 1098 * let check if we should disable LKDC classic mode 1099 * 1100 * There is two cases where we know that we don't want to do LKDC 1101 * - Server supports PKU2U or announce support for AppleLKDC 1102 * - Server didn't do above and didn't have LKDC in the announced name 1103 * This later is true for windows servers and 10.7 SMB servers, 1104 * 10.7 afp server does announce LKDC names though (tracked by 9002742). 1105 */ 1106 1107 if (haveMech(na, kGSSAPIMechPKU2UOID) || haveMech(na, kGSSAPIMechSupportsAppleLKDC)) { 1108 try_lkdc_classic = false; 1109 nalog(ASL_LEVEL_DEBUG, CFSTR("Turing off LKDC classic since server announces support for wellknown name: %@"), na->servermechs); 1110 } else if (na->spnegoServerName) { 1111 CFRange res = CFStringFind(na->spnegoServerName, CFSTR("@LKDC"), 0); 1112 if (res.location == kCFNotFound) { 1113 nalog(ASL_LEVEL_DEBUG, CFSTR("Turing off LKDC classic since spnegoServerName didn't contain LKDC: %@"), 1114 na->spnegoServerName); 1115 try_lkdc_classic = false; 1116 } 1117 } 1118 1119 /* 1120 * If we are using an old AFP server, disable SPNEGO 1121 */ 1122 if (CFStringCompare(na->service, kNAHServiceAFPServer, 0) == kCFCompareEqualTo && 1123 !haveMech(na, kGSSAPIMechSupportsAppleLKDC)) 1124 { 1125 flags &= (~USE_SPNEGO); 1126 } 1127 1128 1129 have_kerberos = (na->servermechs == NULL) || 1130 haveMech(na, kGSSAPIMechIAKERB) || 1131 haveMech(na, kGSSAPIMechKerberosOID) || 1132 haveMech(na, kGSSAPIMechKerberosMicrosoftOID) || 1133 haveMech(na, kGSSAPIMechPKU2UOID); 1134 1135 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate-krb: have_kerberos=%s try_iakerb_with_lkdc=%s try-wkdc=%s try-lkdc-classic=%s use-spnego=%s"), 1136 have_kerberos ? "yes" : "no", 1137 try_iakerb_with_lkdc ? "yes" : "no", 1138 try_wlkdc ? "yes" : "no", 1139 try_lkdc_classic ? "yes" : "no", 1140 (flags & USE_SPNEGO) ? "yes" : "no"); 1141 1142 if (!have_kerberos) 1143 return; 1144 1145 /* 1146 * 1147 */ 1148 1149 ret = krb5_init_context(&na->context); 1150 if (ret) 1151 return; 1152 1153 ret = hx509_context_init(&na->hxctx); 1154 if (ret) 1155 return; 1156 1157 /* 1158 * We'll use matching LKDC credentials to this host since they are 1159 * faster then public key operations. 1160 */ 1161 1162 use_existing_principals(na, 1, flags); 1163 1164 /* 1165 * IAKERB with LKDC 1166 */ 1167 1168 if (try_iakerb_with_lkdc) { 1169 wellknown_lkdc(na, GSS_KERBEROS_IAKERB, flags); 1170 } 1171 1172 /* 1173 * Wellknown:LKDC 1174 */ 1175 1176 if (try_wlkdc) 1177 wellknown_lkdc(na, GSS_KERBEROS, flags); 1178 1179 /* 1180 * Do classic Kerberos too 1181 */ 1182 1183 if (na->password) 1184 use_classic_kerberos(na, flags); 1185 1186 1187 /* 1188 * Classic LKDC style, causes mDNS lookups, so avoid if possible 1189 */ 1190 1191 if (try_lkdc_classic) { 1192 /* classic name */ 1193 classic_lkdc(na, flags); 1194 } 1195 1196 /* 1197 * We'll use existing credentials if we have them 1198 */ 1199 1200 use_existing_principals(na, 0, flags); 1201} 1202 1203static void 1204guess_ntlm(NAHRef na) 1205{ 1206 CFStringRef s; 1207 unsigned long flags = USE_SPNEGO; 1208 1209 if (!haveMech(na, kGSSAPIMechNTLMOID)) 1210 return; 1211 1212 if (na->servermechs) { 1213 CFDataRef data = CFDictionaryGetValue(na->servermechs, kGSSAPIMechNTLMOID); 1214 if (data && CFDataGetLength(data) == 3 && memcmp(CFDataGetBytePtr(data), "raw", 3) == 0) 1215 flags &= (~USE_SPNEGO); 1216 } 1217 1218 s = CFStringCreateWithFormat(na->alloc, 0, CFSTR("%@@%@"), na->service, na->hostname); 1219 if (s == NULL) 1220 return; 1221 1222 if (na->password) { 1223 CFRange range, ur, dr; 1224 CFStringRef u = NULL; 1225 unsigned long flags2 = 0; 1226 1227 if (CFStringFindWithOptions(na->username, CFSTR("@"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 1228 u = na->username; 1229 CFRetain(u); 1230 1231 flags2 = FORCE_ADD; 1232 } else if (CFStringFindWithOptions(na->username, CFSTR("\\"), CFRangeMake(0, CFStringGetLength(na->username)), 0, &range)) { 1233 CFStringRef ustr, dstr; 1234 1235 dr.location = 0; 1236 dr.length = range.location; 1237 1238 ur.location = range.location + 1; 1239 ur.length = CFStringGetLength(na->username) - ur.location; 1240 1241 dstr = CFStringCreateWithSubstring(NULL, na->username, dr); 1242 ustr = CFStringCreateWithSubstring(NULL, na->username, ur); 1243 1244 if (dstr && ustr) 1245 u = CFStringCreateWithFormat(na->alloc, 0, CFSTR("%@@%@"), ustr, dstr); 1246 CFRELEASE(ustr); 1247 CFRELEASE(dstr); 1248 1249 flags2 = FORCE_ADD; 1250 } else { 1251 u = CFStringCreateWithFormat(na->alloc, 0, CFSTR("%@@\\%@"), na->username, na->hostname); 1252 } 1253 1254 if (u) { 1255 addSelection(na, u, kNAHNTUsername, s, NULL, GSS_NTLM, NULL, flags | flags2); 1256 CFRELEASE(u); 1257 } 1258 1259 if (na->specificname) { 1260 u = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@@\\%@"), na->specificname, na->hostname); 1261 addSelection(na, u, kNAHNTUsername, s, NULL, GSS_NTLM, NULL, flags); 1262 CFRELEASE(u); 1263 } 1264 } 1265 1266 /* pick up ntlm credentials in caches */ 1267 1268 dispatch_semaphore_t sema = dispatch_semaphore_create(0); 1269 if (sema == NULL) 1270 goto out; 1271 1272 (void)gss_iter_creds(NULL, 0, GSS_NTLM_MECHANISM, ^(gss_OID oid, gss_cred_id_t cred) { 1273 OM_uint32 min_stat; 1274 gss_name_t name = GSS_C_NO_NAME; 1275 gss_buffer_desc buffer = { 0, NULL }; 1276 1277 if (cred == NULL) { 1278 dispatch_semaphore_signal(sema); 1279 return; 1280 } 1281 1282 gss_inquire_cred(&min_stat, cred, &name, NULL, NULL, NULL); 1283 gss_display_name(&min_stat, name, &buffer, NULL); 1284 gss_release_name(&min_stat, &name); 1285 1286 CFStringRef u = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%.*s"), 1287 (int)buffer.length, buffer.value); 1288 1289 gss_release_buffer(&min_stat, &buffer); 1290 1291 if (u == NULL) 1292 return; 1293 1294 /* if we where given a name, and it matches, add it */ 1295 1296 NAHSelectionRef nasel = addSelection(na, u, kNAHNTUsername, s, NULL, GSS_NTLM, NULL, flags); 1297 CFRELEASE(u); 1298 if (nasel) { 1299 nasel->have_cred = 1; 1300 } 1301 }); 1302 1303 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 1304 dispatch_release(sema); 1305 1306 out: 1307 CFRELEASE(s); 1308} 1309 1310/* 1311 * Flattern arrays and try 1312 */ 1313 1314static void 1315addSecItem(CFMutableArrayRef certs, CFTypeRef item) 1316{ 1317 CFTypeID type = CFGetTypeID(item); 1318 1319 if (CFArrayGetTypeID() == type) { 1320 CFIndex n, count = CFArrayGetCount(item); 1321 for (n = 0; n < count; n++) { 1322 CFTypeRef arrayItem = CFArrayGetValueAtIndex(item, n); 1323 addSecItem(certs, arrayItem); 1324 } 1325 } else if (SecCertificateGetTypeID() == type) { 1326 SecIdentityRef identity = NULL; 1327 SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)item, &identity); 1328 if (identity) { 1329 CFArrayAppendValue(certs, identity); 1330 CFRelease(identity); 1331 } 1332 } else if (SecIdentityGetTypeID() == type) { 1333 CFArrayAppendValue(certs, item); 1334 } else { 1335 CFStringRef desc = CFCopyDescription(item); 1336 nalog(ASL_LEVEL_DEBUG, CFSTR("unknown type of certificates: %@"), desc); 1337 CFRELEASE(desc); 1338 } 1339} 1340 1341/* 1342 * 1343 */ 1344 1345const CFStringRef kNAHNegTokenInit = CFSTR("kNAHNegTokenInit"); 1346const CFStringRef kNAHUserName = CFSTR("kNAHUserName"); 1347const CFStringRef kNAHCertificates = CFSTR("kNAHCertificates"); 1348const CFStringRef kNAHPassword = CFSTR("kNAHPassword"); 1349 1350NAHRef 1351NAHCreate(CFAllocatorRef alloc, 1352 CFStringRef hostname, 1353 CFStringRef service, 1354 CFDictionaryRef info) 1355{ 1356 NAHRef na = NAAlloc(alloc); 1357 CFStringRef canonname; 1358 char *hostnamestr = NULL; 1359 1360 dispatch_once(&init_globals, ^{ 1361 Boolean have_key = false; 1362 nah_use_gss_uam = CFPreferencesGetAppBooleanValue(CFSTR("GSSEnable"), CFSTR("com.apple.NetworkAuthenticationHelper"), &have_key); 1363 if (!have_key) 1364 nah_use_gss_uam = true; 1365 nah_vnc_support_iakerb = CFPreferencesGetAppBooleanValue(CFSTR("VNCSupportIAKerb"), CFSTR("com.apple.NetworkAuthenticationHelper"), &have_key); 1366 }); 1367 1368 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: hostname=%@ service=%@"), hostname, service); 1369 1370 /* first undo the damage BrowserServices have done to the hostname */ 1371 1372 if (_CFNetServiceDeconstructServiceName(hostname, &hostnamestr)) { 1373 canonname = CFStringCreateWithCString(na->alloc, hostnamestr, kCFStringEncodingUTF8); 1374 free(hostnamestr); 1375 if (canonname == NULL) 1376 return NULL; 1377 } else { 1378 canonname = hostname; 1379 CFRetain(canonname); 1380 } 1381 1382 na->hostname = CFStringCreateMutableCopy(alloc, 0, canonname); 1383 CFRelease(canonname); 1384 if (na->hostname == NULL) { 1385 CFRELEASE(na); 1386 return NULL; 1387 } 1388 CFStringTrim(na->hostname, CFSTR(".")); 1389 1390 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: will use hostname=%@"), na->hostname); 1391 1392 na->service = CFRetain(service); 1393 1394 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: will use service=%@"), na->service); 1395 1396 1397 if (!findUsername(alloc, na, info)) { 1398 CFRELEASE(na); 1399 return NULL; 1400 } 1401 1402 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: username=%@ username %s"), na->username, na->specificname ? 1403 "given" : "generated"); 1404 1405 1406 if (info) { 1407 na->password = CFDictionaryGetValue(info, kNAHPassword); 1408 if (na->password) { 1409 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: password")); 1410 CFRetain(na->password); 1411 } 1412 } 1413 1414 na->selections = CFArrayCreateMutable(na->alloc, 0, &kCFTypeArrayCallBacks); 1415 if (na->selections == NULL) { 1416 CFRELEASE(na); 1417 return NULL; 1418 } 1419 1420 if (info) { 1421 CFDictionaryRef nti; 1422 CFArrayRef certs; 1423 1424 nti = CFDictionaryGetValue(info, kNAHNegTokenInit); 1425 if (nti) { 1426 na->servermechs = CFDictionaryGetValue(nti, kSPNEGONegTokenInitMechs); 1427 if (na->servermechs) 1428 CFRetain(na->servermechs); 1429 na->spnegoServerName = CFDictionaryGetValue(nti, kSPNEGONegTokenInitHintsHostname); 1430 if (na->spnegoServerName) { 1431 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: SPNEGO hints name %@"), na->spnegoServerName); 1432 CFRetain(na->spnegoServerName); 1433 } 1434 } 1435 1436 certs = CFDictionaryGetValue(info, kNAHCertificates); 1437 if (certs) { 1438 CFMutableArrayRef a = CFArrayCreateMutable(na->alloc, 0, &kCFTypeArrayCallBacks); 1439 1440 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: certs %@"), certs); 1441 1442 addSecItem(a, certs); 1443 1444 if (CFArrayGetCount(a)) { 1445 na->x509identities = a; 1446 } else { 1447 CFRelease(a); 1448 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCreate: we got no certs")); 1449 } 1450 } 1451 } 1452 1453 /* here starts the guessing game */ 1454 1455 add_user_selections(na); 1456 1457 guess_kerberos(na); 1458 1459 /* only do NTLM for SMB */ 1460 if (na->x509identities == NULL && is_smb(na)) 1461 guess_ntlm(na); 1462 1463 return na; 1464} 1465 1466CFArrayRef 1467NAHGetSelections(NAHRef na) 1468{ 1469 return na->selections; 1470} 1471 1472static CFStringRef 1473copyInferedNameFromIdentity(SecIdentityRef identity) 1474{ 1475 CFStringRef inferredLabel = NULL; 1476 SecCertificateRef cert = NULL; 1477 1478 SecIdentityCopyCertificate(identity, &cert); 1479 if (cert == NULL) 1480 return NULL; 1481 1482 inferredLabel = _CSCopyAppleIDAccountForAppleIDCertificate(cert, NULL); 1483 if (inferredLabel == NULL) 1484 SecCertificateInferLabel(cert, &inferredLabel); 1485 1486 CFRELEASE(cert); 1487 1488 return inferredLabel; 1489} 1490 1491 1492static void 1493setFriendlyName(NAHRef na, 1494 NAHSelectionRef selection, 1495 SecIdentityRef cert, 1496 krb5_ccache id, 1497 int is_lkdc) 1498{ 1499 CFStringRef inferredLabel = NULL; 1500 1501 if (cert) { 1502 inferredLabel = copyInferedNameFromIdentity(cert); 1503 1504 } else if (na->specificname || is_lkdc) { 1505 inferredLabel = na->username; 1506 CFRetain(inferredLabel); 1507 } else { 1508 inferredLabel = selection->client; 1509 CFRetain(inferredLabel); 1510 } 1511 1512 if (inferredLabel) { 1513 char *label; 1514 1515 if (__KRBCreateUTF8StringFromCFString(inferredLabel, &label) == noErr) { 1516 krb5_data data; 1517 1518 data.data = label; 1519 data.length = strlen(label) + 1; 1520 1521 krb5_cc_set_config(na->context, id, NULL, "FriendlyName", &data); 1522 free(label); 1523 } 1524 selection->inferredLabel = inferredLabel; 1525 } 1526} 1527 1528static int 1529acquire_kerberos(NAHRef na, 1530 NAHSelectionRef selection, 1531 CFStringRef password, 1532 SecIdentityRef cert, 1533 CFErrorRef *error) 1534{ 1535 krb5_init_creds_context icc = NULL; 1536 krb5_get_init_creds_opt *opt = NULL; 1537 krb5_principal client = NULL; 1538 int destroy_cache = 0; 1539 krb5_ccache id = NULL; 1540 krb5_error_code ret; 1541 krb5_creds cred; 1542 char *str = NULL; 1543 int parseflags = 0; 1544 int is_lkdc = 0; 1545 1546 memset(&cred, 0, sizeof(cred)); 1547 1548 nalog(ASL_LEVEL_DEBUG, CFSTR("acquire_kerberos: %@ with pw:%s cert:%s"), 1549 selection->client, 1550 password ? "yes" : "no", 1551 cert ? "yes" : "no"); 1552 1553 ret = __KRBCreateUTF8StringFromCFString(selection->client, &str); 1554 if (ret) 1555 goto out; 1556 1557 /* 1558 * Check if this is an enterprise name 1559 * XXX horrible, caller should tell us 1560 */ 1561 { 1562 char *p = strchr(str, '@'); 1563 if (p && (p = strchr(p + 1, '@')) != NULL) 1564 parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE; 1565 } 1566 1567 ret = krb5_parse_name_flags(na->context, str, parseflags, &client); 1568 __KRBReleaseUTF8String(str); 1569 if (ret) 1570 goto out; 1571 1572 ret = krb5_unparse_name(na->context, client, &str); 1573 if (ret == 0) { 1574 nalog(ASL_LEVEL_DEBUG, CFSTR("acquire_kerberos: trying with %s as client principal"), str); 1575 free(str); 1576 } 1577 1578 ret = krb5_get_init_creds_opt_alloc(na->context, &opt); 1579 if (ret) 1580 goto out; 1581 1582 if (cert) { 1583 ret = krb5_get_init_creds_opt_set_pkinit(na->context, opt, client, 1584 NULL, "KEYCHAIN:", 1585 NULL, NULL, 0, 1586 NULL, NULL, NULL); 1587 if (ret) 1588 goto out; 1589 } 1590 1591 krb5_get_init_creds_opt_set_canonicalize(na->context, opt, TRUE); 1592 krb5_get_init_creds_opt_set_win2k(na->context, opt, TRUE); 1593 1594 ret = krb5_init_creds_init(na->context, client, NULL, NULL, 1595 0, opt, &icc); 1596 if (ret) 1597 goto out; 1598 1599 if (krb5_principal_is_lkdc(na->context, client)) { 1600 char *tcphostname = NULL; 1601 1602 ret = __KRBCreateUTF8StringFromCFString(na->hostname, &str); 1603 if (ret) 1604 goto out; 1605 asprintf(&tcphostname, "tcp/%s", str); 1606 __KRBReleaseUTF8String(str); 1607 if (tcphostname == NULL) { 1608 ret = ENOMEM; 1609 goto out; 1610 } 1611 krb5_init_creds_set_kdc_hostname(na->context, icc, tcphostname); 1612 free(tcphostname); 1613 } 1614 1615 if (cert) { 1616 hx509_cert hxcert; 1617 1618 ret = hx509_cert_init_SecFramework(na->hxctx, cert, &hxcert); 1619 if (ret) 1620 goto out; 1621 1622 ret = krb5_init_creds_set_pkinit_client_cert(na->context, icc, hxcert); 1623 hx509_cert_free(hxcert); 1624 if (ret) 1625 goto out; 1626 1627 } else if (password) { 1628 ret = __KRBCreateUTF8StringFromCFString(password, &str); 1629 if (ret) 1630 goto out; 1631 ret = krb5_init_creds_set_password(na->context, icc, str); 1632 __KRBReleaseUTF8String(str); 1633 if (ret) 1634 goto out; 1635 } else { 1636 abort(); 1637 } 1638 1639 ret = krb5_init_creds_get(na->context, icc); 1640 if (ret) 1641 goto out; 1642 1643 ret = krb5_init_creds_get_creds(na->context, icc, &cred); 1644 if (ret) 1645 goto out; 1646 1647 ret = krb5_cc_cache_match(na->context, cred.client, &id); 1648 if (ret) { 1649 ret = krb5_cc_new_unique(na->context, NULL, NULL, &id); 1650 if (ret) 1651 goto out; 1652 destroy_cache = 1; 1653 } 1654 1655 ret = krb5_cc_initialize(na->context, id, cred.client); 1656 if (ret) 1657 goto out; 1658 1659 ret = krb5_cc_store_cred(na->context, id, &cred); 1660 if (ret) 1661 goto out; 1662 1663 ret = krb5_init_creds_store_config(na->context, icc, id); 1664 if (ret) 1665 goto out; 1666 1667 /* the KDC might have done referrals games, let update the principals */ 1668 1669 { 1670 CFStringRef newclient, newserver; 1671 const char *realm = krb5_principal_get_realm(na->context, cred.client); 1672 1673 is_lkdc = krb5_realm_is_lkdc(realm); 1674 1675 ret = krb5_unparse_name(na->context, cred.client, &str); 1676 if (ret) 1677 goto out; 1678 1679 newclient = CFStringCreateWithCString(na->alloc, str, kCFStringEncodingUTF8); 1680 free(str); 1681 if (newclient == NULL) { 1682 ret = ENOMEM; 1683 goto out; 1684 } 1685 1686 nalog(ASL_LEVEL_DEBUG, CFSTR("acquire_kerberos: got %@ as client principal"), newclient); 1687 1688 if (CFStringCompare(newclient, selection->client, 0) != kCFCompareEqualTo) { 1689 1690 CFRELEASE(selection->client); 1691 selection->client = newclient; 1692 1693 if (is_lkdc) { 1694 newserver = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%s@%s"), 1695 na->service, realm, realm); 1696 } else { 1697 newserver = CFStringCreateWithFormat(na->alloc, NULL, CFSTR("%@/%@@%s"), 1698 na->service, na->hostname, realm); 1699 } 1700 if (newserver) { 1701 CFRELEASE(selection->server); 1702 selection->server = newserver; 1703 } 1704 } else { 1705 CFRELEASE(newclient); 1706 } 1707 } 1708 1709 setFriendlyName(na, selection, cert, id, is_lkdc); 1710 { 1711 krb5_data data; 1712 data.data = "1"; 1713 data.length = 1; 1714 krb5_cc_set_config(na->context, id, NULL, nah_created, &data); 1715 } 1716 1717 1718 out: 1719 if (ret) { 1720 const char *e = krb5_get_error_message(na->context, ret); 1721 updateError(NULL, error, ret, CFSTR("acquire_kerberos failed %@: %d - %s"), 1722 selection->client, ret, e); 1723 krb5_free_error_message(na->context, e); 1724 } else { 1725 nalog(ASL_LEVEL_DEBUG, CFSTR("acquire_kerberos successful")); 1726 } 1727 1728 if (opt) 1729 krb5_get_init_creds_opt_free(na->context, opt); 1730 1731 if (icc) 1732 krb5_init_creds_free(na->context, icc); 1733 1734 if (id) { 1735 if (ret != 0 && destroy_cache) 1736 krb5_cc_destroy(na->context, id); 1737 else 1738 krb5_cc_close(na->context, id); 1739 } 1740 krb5_free_cred_contents(na->context, &cred); 1741 1742 if (client) 1743 krb5_free_principal(na->context, client); 1744 1745 return ret; 1746} 1747 1748/* 1749 * 1750 */ 1751 1752Boolean 1753NAHSelectionAcquireCredentialAsync(NAHSelectionRef selection, 1754 CFDictionaryRef info, 1755 dispatch_queue_t q, 1756 void (^result)(CFErrorRef error)) 1757{ 1758 void (^r)(CFErrorRef error) = (void (^)(CFErrorRef))Block_copy(result); 1759 1760 CFRetain(selection->na); 1761 1762 dispatch_async(selection->na->bgq, ^{ 1763 CFErrorRef e = NULL; 1764 Boolean res; 1765 1766 res = NAHSelectionAcquireCredential(selection, info, &e); 1767 if (!res) { 1768 r(NULL); 1769 CFRelease(selection->na); 1770 Block_release(r); 1771 return; 1772 } 1773 1774 dispatch_async(q, ^{ 1775 r(e); 1776 if (e) 1777 CFRelease(e); 1778 CFRelease(selection->na); 1779 Block_release(r); 1780 }); 1781 }); 1782 1783 return true; 1784} 1785 1786static void 1787setGSSLabel(gss_cred_id_t cred, const char *label, CFStringRef value) 1788{ 1789 gss_buffer_desc buf; 1790 OM_uint32 junk; 1791 1792 buf.value = cf2cstring(value); 1793 if (buf.value == NULL) 1794 return; 1795 buf.length = strlen((char *)buf.value); 1796 1797 gss_cred_label_set(&junk, cred, label, &buf); 1798 free(buf.value); 1799} 1800 1801 1802const CFStringRef kNAHForceRefreshCredential = CFSTR("kNAHForceRefreshCredential"); 1803 1804Boolean 1805NAHSelectionAcquireCredential(NAHSelectionRef selection, 1806 CFDictionaryRef info, 1807 CFErrorRef *error) 1808{ 1809 if (error) 1810 *error = NULL; 1811 1812 CFRetain(selection->na); 1813 1814 if (!wait_result(selection)) { 1815 CFRelease(selection->na); 1816 return false; 1817 } 1818 1819 if (selection->mech == GSS_KERBEROS) { 1820 1821 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: kerberos client: %@ (server %@)"), 1822 selection->client, selection->server); 1823 1824 /* if we already have a cache, skip acquire unless force */ 1825 if (selection->ccache) { 1826 nalog(ASL_LEVEL_DEBUG, CFSTR("have ccache")); 1827 KRBCredChangeReferenceCount(selection->client, 1, 1); 1828 return true; 1829 } 1830 1831 if (selection->na->password == NULL && selection->certificate == NULL) { 1832 nalog(ASL_LEVEL_DEBUG, CFSTR("krb5: no password or cert, punting")); 1833 CFRelease(selection->na); 1834 return false; 1835 } 1836 1837 int ret; 1838 1839 ret = acquire_kerberos(selection->na, 1840 selection, 1841 selection->na->password, 1842 selection->certificate, 1843 error); 1844 1845 CFRelease(selection->na); 1846 if (ret && error && *error) 1847 nalog(ASL_LEVEL_NOTICE, CFSTR("NAHSelectionAcquireCredential %@"), *error); 1848 1849 return (ret == 0) ? true : false; 1850 1851 } else if (selection->mech == GSS_NTLM) { 1852 gss_auth_identity_desc identity; 1853 gss_name_t name = GSS_C_NO_NAME; 1854 gss_buffer_desc gbuf; 1855 char *password, *user; 1856 OM_uint32 major, minor, junk; 1857 dispatch_semaphore_t s; 1858 char *str; 1859 1860 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: ntlm")); 1861 1862 if (selection->have_cred) { 1863 CFRelease(selection->na); 1864 return true; 1865 } 1866 1867 if (selection->na->password == NULL) { 1868 CFRelease(selection->na); 1869 return false; 1870 } 1871 1872 __KRBCreateUTF8StringFromCFString(selection->client, &user); 1873 1874 gbuf.value = user; 1875 gbuf.length = strlen(user); 1876 1877 major = gss_import_name(&minor, &gbuf, GSS_C_NT_USER_NAME, &name); 1878 __KRBReleaseUTF8String(user); 1879 if (major) { 1880 CFRelease(selection->na); 1881 return false; 1882 } 1883 1884 s = dispatch_semaphore_create(0); 1885 __KRBCreateUTF8StringFromCFString(selection->client, &user); 1886 __KRBCreateUTF8StringFromCFString(selection->na->password, &password); 1887 1888 selection->inferredLabel = selection->client; 1889 CFRetain(selection->inferredLabel); 1890 1891 /* drop when name is completly supported */ 1892 str = strchr(user, '@'); 1893 if (str) 1894 *str++ = '\0'; 1895 1896 identity.username = user; 1897 if (str) 1898 identity.realm = str; 1899 else 1900 identity.realm = ""; 1901 identity.password = password; 1902 1903 major = gss_acquire_cred_ex(name, 1904 0, 1905 GSS_C_INDEFINITE, 1906 GSS_NTLM_MECHANISM, 1907 GSS_C_INITIATE, 1908 &identity, 1909 ^(gss_status_id_t status, gss_cred_id_t cred, 1910 gss_OID_set set, OM_uint32 flags) { 1911 1912 if (cred) { 1913 gss_buffer_desc buffer; 1914 OM_uint32 min_stat; 1915 1916 buffer.value = user; 1917 buffer.length = strlen(user); 1918 1919 gss_cred_label_set(&min_stat, cred, "FriendlyName", &buffer); 1920 1921 buffer.value = "1"; 1922 buffer.length = 1; 1923 gss_cred_label_set(&min_stat, cred, nah_created, &buffer); 1924 } else { 1925 updateError(NULL, error, 1, CFSTR("failed to create ntlm cred")); 1926 } 1927 1928 dispatch_semaphore_signal(s); 1929 CFRelease(selection->na); 1930 }); 1931 gss_release_name(&junk, &name); 1932 if (major == GSS_S_COMPLETE) { 1933 dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER); 1934 1935 if (error && *error) 1936 nalog(ASL_LEVEL_NOTICE, CFSTR("NAHSelectionAcquireCredential ntlm %@"), *error); 1937 1938 } else { 1939 updateError(NULL, error, major, CFSTR("Failed to acquire NTLM credentials")); 1940 CFRelease(selection->na); 1941 } 1942 1943 __KRBReleaseUTF8String(user); 1944 __KRBReleaseUTF8String(password); 1945 1946 dispatch_release(s); 1947 return true; 1948 } else if (selection->mech == GSS_KERBEROS_IAKERB) { 1949 gss_name_t name = GSS_C_NO_NAME; 1950 gss_buffer_desc gbuf; 1951 char *user; 1952 OM_uint32 major, minor, junk; 1953 gss_cred_id_t cred; 1954 1955 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: iakerb %@"), selection->client); 1956 1957 if (selection->have_cred) { 1958 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: already have cred, why iakerb then ?")); 1959 CFRelease(selection->na); 1960 return false; 1961 } 1962 1963 1964 if (selection->na->password == NULL && selection->certificate == NULL) { 1965 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: no password nor cert")); 1966 CFRelease(selection->na); 1967 return false; 1968 } 1969 1970 __KRBCreateUTF8StringFromCFString(selection->client, &user); 1971 1972 gbuf.value = user; 1973 gbuf.length = strlen(user); 1974 1975 major = gss_import_name(&minor, &gbuf, GSS_C_NT_USER_NAME, &name); 1976 __KRBReleaseUTF8String(user); 1977 if (major) { 1978 CFRelease(selection->na); 1979 return false; 1980 } 1981 1982 if (selection->certificate) 1983 selection->inferredLabel = copyInferedNameFromIdentity(selection->certificate); 1984 1985 if (selection->inferredLabel == NULL) { 1986 CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 0, selection->client); 1987 CFRange range = CFStringFind(str, CFSTR("@"), 0); 1988 if (range.location != kCFNotFound) 1989 CFStringPad(str, NULL, range.location, 0); 1990 selection->inferredLabel = str; 1991 } 1992 1993 CFMutableDictionaryRef dict = NULL; 1994 1995 dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1996 1997 if (selection->na->password) 1998 CFDictionaryAddValue(dict, kGSSICPassword, selection->na->password); 1999 if (selection->certificate) 2000 CFDictionaryAddValue(dict, kGSSICCertificate, selection->certificate); 2001 2002 major = gss_aapl_initial_cred(name, GSS_IAKERB_MECHANISM, dict, &cred, error); 2003 CFRelease(dict); 2004 gss_release_name(&junk, &name); 2005 if (major) { 2006 if (error && *error) 2007 nalog(ASL_LEVEL_NOTICE, CFSTR("NAHSelectionAcquireCredential iakerb %@"), *error); 2008 CFRelease(selection->na); 2009 return false; 2010 } 2011 2012 setGSSLabel(cred, "FriendlyName", selection->inferredLabel); 2013 setGSSLabel(cred, "lkdc-hostname", selection->na->hostname); 2014 2015 { 2016 gss_buffer_set_t dataset = GSS_C_NO_BUFFER_SET; 2017 2018 major = gss_inquire_cred_by_oid(&minor, cred, GSS_C_NT_UUID, &dataset); 2019 if (major || dataset->count != 1) { 2020 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: failed with no uuid")); 2021 gss_release_buffer_set(&junk, &dataset); 2022 CFRelease(selection->na); 2023 return false; 2024 } 2025 2026 CFStringRef newclient = CFStringCreateWithBytes(NULL, dataset->elements[0].value, dataset->elements[0].length, kCFStringEncodingUTF8, false); 2027 if (newclient) { 2028 CFRELEASE(selection->client); 2029 selection->client = newclient; 2030 selection->clienttype = kNAHNTUUID; 2031 } 2032 gss_release_buffer_set(&junk, &dataset); 2033 } 2034 nalog(ASL_LEVEL_NOTICE, CFSTR("NAHSelectionAcquireCredential complete: iakerb %@ - %@: %@"), selection->client, selection->inferredLabel, cred); 2035 2036 gss_release_cred(&junk, &cred); 2037 2038 CFRelease(selection->na); 2039 2040 return true; 2041 } else { 2042 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHSelectionAcquireCredential: unknown")); 2043 } 2044 2045 return false; 2046} 2047 2048 2049 2050static void 2051signal_result(NAHSelectionRef s) 2052{ 2053 dispatch_sync(s->na->q, ^{ 2054 if (s->done) 2055 abort(); 2056 s->done = 1; 2057 while (s->waiting > 0) { 2058 dispatch_semaphore_signal(s->wait); 2059 s->waiting--; 2060 } 2061 }); 2062} 2063 2064static Boolean 2065wait_result(NAHSelectionRef s) 2066{ 2067 __block dispatch_semaphore_t sema; 2068 2069 dispatch_sync(s->na->q, ^{ 2070 if (s->canceled || s->done) { 2071 sema = NULL; 2072 } else { 2073 sema = s->wait; 2074 if (sema) 2075 s->waiting++; 2076 } 2077 }); 2078 if (sema) 2079 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 2080 2081 if (s->canceled) { 2082 CFRelease(s); 2083 return false; 2084 } 2085 return true; 2086} 2087 2088/* 2089 * 2090 */ 2091 2092CFTypeRef 2093NAHSelectionGetInfoForKey(NAHSelectionRef selection, CFStringRef key) 2094{ 2095 if (!wait_result(selection)) 2096 return NULL; 2097 2098 if (CFStringCompare(kNAHSelectionHaveCredential, key, 0) == kCFCompareEqualTo) { 2099 if (selection->ccache) 2100 return kCFBooleanTrue; 2101 return kCFBooleanFalse; 2102 } else if (CFStringCompare(kNAHSelectionUserPrintable, key, 0) == kCFCompareEqualTo) { 2103 return selection->client; /* XXX make prettier ? */ 2104 } else if (CFStringCompare(kNAHServerPrincipal, key, 0) == kCFCompareEqualTo) { 2105 return selection->server; 2106 } else if (CFStringCompare(kNAHClientPrincipal, key, 0) == kCFCompareEqualTo) { 2107 return selection->client; 2108 } else if (CFStringCompare(kNAHMechanism, key, 0) == kCFCompareEqualTo) { 2109 /* if not told otherwise, wrap everything in SPNEGO wrappings */ 2110 if (!selection->spnego) 2111 return mech2name(selection->mech); 2112 return kGSSAPIMechSPNEGO; 2113 } else if (CFStringCompare(kNAHInnerMechanism, key, 0) == kCFCompareEqualTo) { 2114 return mech2name(selection->mech); 2115 } else if (CFStringCompare(kNAHUseSPNEGO, key, 0) == kCFCompareEqualTo) { 2116 return selection->spnego ? kCFBooleanTrue : kCFBooleanFalse; 2117 } else if (CFStringCompare(kNAHCredentialType, key, 0) == kCFCompareEqualTo) { 2118 return mech2name(selection->mech); 2119 } else if (CFStringCompare(kNAHInferredLabel, key, 0) == kCFCompareEqualTo) { 2120 return selection->inferredLabel; 2121 } 2122 2123 return NULL; 2124} 2125 2126/* 2127 * Returns the data for kNetFSAuthenticationInfoKey 2128 */ 2129 2130CFDictionaryRef 2131NAHSelectionCopyAuthInfo(NAHSelectionRef selection) 2132{ 2133 CFMutableDictionaryRef dict; 2134 CFStringRef string; 2135 int gssdclient, gssdserver; 2136 2137 if (!wait_result(selection)) 2138 return NULL; 2139 2140 if (selection->server == NULL) 2141 return NULL; 2142 2143 dict = CFDictionaryCreateMutable (kCFAllocatorDefault, 5, 2144 &kCFCopyStringDictionaryKeyCallBacks, 2145 &kCFTypeDictionaryValueCallBacks); 2146 if (dict == NULL) 2147 return NULL; 2148 2149 CFDictionaryAddValue(dict, kNAHMechanism, 2150 NAHSelectionGetInfoForKey(selection, kNAHMechanism)); 2151 2152 CFDictionaryAddValue(dict, kNAHCredentialType, 2153 NAHSelectionGetInfoForKey(selection, kNAHCredentialType)); 2154 2155 CFDictionaryAddValue(dict, kNAHClientNameType, selection->clienttype); 2156 2157 if (CFStringCompare(selection->clienttype, kNAHNTUUID, 0) == 0) { 2158 gssdclient = GSSD_UUID; 2159 } else if (CFStringCompare(selection->clienttype, kNAHNTKRB5Principal, 0) == 0) { 2160 gssdclient = GSSD_KRB5_PRINCIPAL; 2161 } else if (CFStringCompare(selection->clienttype, kNAHNTUsername, 0) == 0) { 2162 gssdclient = GSSD_NTLM_PRINCIPAL; 2163 } else { 2164 gssdclient = GSSD_USER; 2165 } 2166 2167 CFDictionaryAddValue(dict, kNAHServerNameType, selection->servertype); 2168 2169 if (CFStringCompare(selection->servertype, kNAHNTServiceBasedName, 0) == 0) 2170 gssdserver = GSSD_HOSTBASED; 2171 else if (CFStringCompare(selection->servertype, kNAHNTKRB5PrincipalReferral, 0) == 0) 2172 gssdserver = GSSD_KRB5_REFERRAL; 2173 else if (CFStringCompare(selection->servertype, kNAHNTKRB5Principal, 0) == 0) 2174 gssdserver = GSSD_KRB5_PRINCIPAL; 2175 else 2176 gssdserver = GSSD_HOSTBASED; 2177 2178 2179 CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &gssdclient); 2180 if (num) { 2181 CFDictionaryAddValue(dict, kNAHClientNameTypeGSSD, num); 2182 CFRelease(num); 2183 } 2184 2185 num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &gssdserver); 2186 if (num) { 2187 CFDictionaryAddValue(dict, kNAHServerNameTypeGSSD, num); 2188 CFRelease(num); 2189 } 2190 2191 CFDictionaryAddValue(dict, kNAHClientPrincipal, 2192 NAHSelectionGetInfoForKey(selection, kNAHClientPrincipal)); 2193 CFDictionaryAddValue(dict, kNAHServerPrincipal, 2194 NAHSelectionGetInfoForKey(selection, kNAHServerPrincipal)); 2195 2196 /* add label if we have one */ 2197 if ((string = NAHSelectionGetInfoForKey(selection, kNAHInferredLabel)) != NULL) 2198 CFDictionaryAddValue(dict, kNAHInferredLabel, string); 2199 2200 CFDictionaryAddValue(dict, kNAHUseSPNEGO, NAHSelectionGetInfoForKey(selection, kNAHUseSPNEGO)); 2201 2202 return dict; 2203} 2204 2205/* 2206 * 2207 */ 2208 2209void 2210NAHCancel(NAHRef na) 2211{ 2212 dispatch_sync(na->q, ^{ 2213 NAHSelectionRef s; 2214 CFIndex n, num; 2215 2216 num = CFArrayGetCount(na->selections); 2217 for (n = 0; n < num; n++) { 2218 s = (NAHSelectionRef)CFArrayGetValueAtIndex(na->selections, n); 2219 2220 s->canceled = 1; 2221 2222 while (s->waiting > 0) { 2223 CFRetain(s); 2224 dispatch_semaphore_signal(s->wait); 2225 s->waiting--; 2226 } 2227 } 2228 }); 2229} 2230 2231/* 2232 * Reference counting 2233 */ 2234 2235 2236static Boolean 2237CredChange(CFStringRef referenceKey, int count, const char *label) 2238{ 2239 const char *mechname; 2240 gss_OID nametype; 2241 gss_OID oid; 2242 2243 if (referenceKey == NULL) 2244 return false; 2245 2246 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHCredChange: %@ count: %d label: %s"), 2247 referenceKey, count, label ? label : "<nolabel>"); 2248 2249 if (CFStringHasPrefix(referenceKey, CFSTR("krb5:"))) { 2250 oid = GSS_KRB5_MECHANISM; 2251 nametype = GSS_C_NT_USER_NAME; 2252 mechname = "kerberos"; 2253 } else if (CFStringHasPrefix(referenceKey, CFSTR("uuid:"))) { 2254 oid = NULL; 2255 nametype = GSS_C_NT_UUID; 2256 mechname = "uuid"; 2257 } else if (CFStringHasPrefix(referenceKey, CFSTR("ntlm:"))) { 2258 oid = GSS_NTLM_MECHANISM; 2259 nametype = GSS_C_NT_USER_NAME; 2260 mechname = "ntlm"; 2261 } else 2262 return false; 2263 2264 { 2265 gss_cred_id_t cred; 2266 gss_buffer_desc gbuf; 2267 OM_uint32 min_stat, maj_stat; 2268 CFStringRef name; 2269 gss_name_t gname; 2270 OSStatus ret; 2271 gss_OID_set_desc mechset; 2272 char *n; 2273 2274 if (oid) { 2275 mechset.elements = oid; 2276 mechset.count = 1; 2277 } 2278 2279 name = CFStringCreateWithSubstring(NULL, referenceKey, CFRangeMake(5, CFStringGetLength(referenceKey) - 5)); 2280 if (name == NULL) 2281 return false; 2282 2283 ret = __KRBCreateUTF8StringFromCFString(name, &n); 2284 CFRelease(name); 2285 if (ret) 2286 return false; 2287 2288 gbuf.value = n; 2289 gbuf.length = strlen(n); 2290 2291 maj_stat = gss_import_name(&min_stat, &gbuf, nametype, &gname); 2292 if (maj_stat != GSS_S_COMPLETE) { 2293 nalog(ASL_LEVEL_DEBUG, CFSTR("ChangeCred: name not importable %s/%s"), n, mechname); 2294 free(n); 2295 return false; 2296 } 2297 2298 maj_stat = gss_acquire_cred(&min_stat, gname, GSS_C_INDEFINITE, oid ? &mechset : NULL, GSS_C_INITIATE, &cred, NULL, NULL); 2299 gss_release_name(&min_stat, &gname); 2300 2301 if (maj_stat != GSS_S_COMPLETE) { 2302 nalog(ASL_LEVEL_DEBUG, CFSTR("ChangeCred: cred name %s/%s not found"), n, mechname); 2303 free(n); 2304 return false; 2305 } 2306 free(n); 2307 2308 /* check that the credential is refcounted */ 2309 { 2310 gss_buffer_desc buffer; 2311 maj_stat = gss_cred_label_get(&min_stat, cred, nah_created, &buffer); 2312 if (maj_stat) { 2313 gss_release_cred(&min_stat, &cred); 2314 return false; 2315 } 2316 gss_release_buffer(&min_stat, &buffer); 2317 } 2318 2319 if (count == 0) { 2320 /* do nothing */ 2321 } else if (count > 0) { 2322 gss_cred_hold(&min_stat, cred); 2323 } else { 2324 gss_cred_unhold(&min_stat, cred); 2325 } 2326 2327 if (label) { 2328 gss_buffer_desc buffer = { 2329 .value = "1", 2330 .length = 1 2331 }; 2332 2333 gss_cred_label_set(&min_stat, cred, label, &buffer); 2334 } 2335 2336 gss_release_cred(&min_stat, &cred); 2337 return true; 2338 } 2339} 2340 2341char * 2342NAHCreateRefLabelFromIdentifier(CFStringRef identifier) 2343{ 2344 CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("reference-label:%@"), identifier); 2345 OSStatus ret; 2346 char *label; 2347 2348 if (str == NULL) 2349 return NULL; 2350 2351 ret = __KRBCreateUTF8StringFromCFString(str, &label); 2352 CFRelease(str); 2353 if (ret != noErr) 2354 return NULL; 2355 return label; 2356} 2357 2358 2359Boolean 2360NAHAddReferenceAndLabel(NAHSelectionRef selection, 2361 CFStringRef identifier) 2362{ 2363 CFStringRef ref; 2364 Boolean res; 2365 char *ident; 2366 2367 if (!wait_result(selection)) 2368 return false; 2369 2370 ref = NAHCopyReferenceKey(selection); 2371 if (ref == NULL) 2372 return false; 2373 2374 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHAddReferenceAndLabel: %@ label: %@"), ref, identifier); 2375 2376 ident = NAHCreateRefLabelFromIdentifier(identifier); 2377 if (ident == NULL) { 2378 CFRelease(ref); 2379 return false; 2380 } 2381 2382 res = CredChange(ref, 1, ident); 2383 CFRelease(ref); 2384 __KRBReleaseUTF8String(ident); 2385 2386 return res; 2387} 2388 2389CFStringRef 2390NAHCopyReferenceKey(NAHSelectionRef selection) 2391{ 2392 CFStringRef type; 2393 if (selection->client == NULL) 2394 return NULL; 2395 2396 switch (selection->mech) { 2397 case GSS_KERBEROS: 2398 type = CFSTR("krb5"); 2399 break; 2400 case GSS_KERBEROS_PKU2U: 2401 case GSS_KERBEROS_IAKERB: 2402 type = CFSTR("uuid"); 2403 break; 2404 case GSS_NTLM: 2405 type = CFSTR("ntlm"); 2406 break; 2407 default: 2408 return NULL; 2409 } 2410 2411 /* if we are using UUID name types, prefer that over mech type */ 2412 if (selection->clienttype && CFStringCompare(selection->clienttype, kNAHNTUUID, 0) == 0) 2413 type = CFSTR("uuid"); 2414 2415 return CFStringCreateWithFormat(NULL, 0, CFSTR("%@:%@"), 2416 type, selection->client); 2417} 2418 2419void 2420NAHFindByLabelAndRelease(CFStringRef identifier) 2421{ 2422 char *str; 2423 2424 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHFindByLabelAndRelease: looking for label %@"), identifier); 2425 2426 str = NAHCreateRefLabelFromIdentifier(identifier); 2427 if (str == NULL) 2428 return; 2429 2430 gss_iter_creds(NULL, 0, GSS_C_NO_OID, ^(gss_OID mech, gss_cred_id_t cred) { 2431 OM_uint32 min_stat, maj_stat; 2432 gss_buffer_desc buffer; 2433 2434 if (cred == NULL) 2435 return; 2436 2437 maj_stat = gss_cred_label_get(&min_stat, cred, nah_created, &buffer); 2438 if (maj_stat) { 2439 gss_release_cred(&min_stat, &cred); 2440 return; 2441 } 2442 gss_release_buffer(&min_stat, &buffer); 2443 2444 buffer.value = NULL; 2445 buffer.length = 0; 2446 2447 /* if there is a label, unhold */ 2448 maj_stat = gss_cred_label_get(&min_stat, cred, str, &buffer); 2449 gss_release_buffer(&min_stat, &buffer); 2450 if (maj_stat == GSS_S_COMPLETE) { 2451 nalog(ASL_LEVEL_DEBUG, CFSTR("NAHFindByLabelAndRelease: found credential unholding")); 2452 gss_cred_label_set(&min_stat, cred, str, NULL); 2453 gss_cred_unhold(&min_stat, cred); 2454 } 2455 gss_release_cred(&min_stat, &cred); 2456 }); 2457 2458 __KRBReleaseUTF8String(str); 2459} 2460 2461Boolean 2462NAHCredAddReference(CFStringRef referenceKey) 2463{ 2464 return CredChange(referenceKey, 1, NULL); 2465} 2466 2467Boolean 2468NAHCredRemoveReference(CFStringRef referenceKey) 2469{ 2470 return CredChange(referenceKey, -1, NULL); 2471} 2472 2473/* 2474 * 2475 */ 2476 2477static CFStringRef kDomainKey = CFSTR("domain"); 2478static CFStringRef kUsername = CFSTR("user"); 2479 2480static CFStringRef kMech = CFSTR("mech"); 2481static CFStringRef kClient = CFSTR("client"); 2482 2483 2484static void 2485add_user_selections(NAHRef na) 2486{ 2487 CFArrayRef array; 2488 enum NAHMechType mech = GSS_KERBEROS; 2489 CFIndex n; 2490 2491 array = CFPreferencesCopyAppValue(CFSTR("UserSelections"), 2492 CFSTR("com.apple.NetworkAuthenticationHelper")); 2493 if (array == NULL || CFGetTypeID(array) != CFArrayGetTypeID()) { 2494 CFRELEASE(array); 2495 return; 2496 } 2497 2498 2499 for (n = 0; n < CFArrayGetCount(array); n++) { 2500 CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n); 2501 CFStringRef server = NULL; 2502 CFStringRef d, u, m, c; 2503 2504 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) 2505 continue; 2506 2507 m = CFDictionaryGetValue(dict, kMech); 2508 d = CFDictionaryGetValue(dict, kDomainKey); 2509 u = CFDictionaryGetValue(dict, kUsername); 2510 c = CFDictionaryGetValue(dict, kClient); 2511 2512 if (c == NULL || CFGetTypeID(c) != CFStringGetTypeID()) 2513 continue; 2514 if (m == NULL || CFGetTypeID(m) != CFStringGetTypeID()) 2515 continue; 2516 if (d == NULL || CFGetTypeID(d) != CFStringGetTypeID()) 2517 continue; 2518 if (u == NULL && CFGetTypeID(u) != CFStringGetTypeID()) 2519 continue; 2520 2521 /* find if matching */ 2522 /* exact matching for now, should really be domain matching */ 2523 if (CFStringCompare(d, na->hostname, kCFCompareCaseInsensitive) != kCFCompareEqualTo) 2524 continue; 2525 2526 if (u == NULL) { 2527 if (CFStringCompare(d, na->username, 0) != kCFCompareEqualTo) 2528 continue; 2529 } 2530 2531 mech = name2mech(m); 2532 if (mech == NO_MECH) 2533 continue; 2534 2535 server = CFStringCreateWithFormat(na->alloc, 0, CFSTR("%@@%@"), 2536 na->service, na->hostname); 2537 2538 /* add selection */ 2539 if (server && c) 2540 addSelection(na, c, NULL, server, NULL, mech, NULL, true); 2541 CFRELEASE(server); 2542 } 2543 CFRelease(array); 2544} 2545 2546 2547 2548 2549/* 2550 * GSS-API Support 2551 */ 2552 2553static gss_OID 2554ntstring2oid(CFStringRef name) 2555{ 2556 if (CFStringCompare(name, kNAHNTServiceBasedName, 0) == 0) 2557 return GSS_C_NT_HOSTBASED_SERVICE; 2558 else if (CFStringCompare(name, kNAHNTKRB5PrincipalReferral, 0) == 0) 2559 return GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL; 2560 else if (CFStringCompare(name, kNAHNTKRB5Principal, 0) == 0) 2561 return GSS_KRB5_NT_PRINCIPAL_NAME; 2562 else if (CFStringCompare(name, kNAHNTUUID, 0) == 0) 2563 return GSS_C_NT_UUID; 2564 2565 return NULL; 2566} 2567 2568 2569 2570gss_cred_id_t 2571NAHSelectionGetGSSCredential(NAHSelectionRef selection, CFErrorRef *error) 2572{ 2573 gss_buffer_desc buffer; 2574 OM_uint32 minor_status, major_status, junk; 2575 gss_name_t name; 2576 gss_cred_id_t cred = NULL; 2577 gss_OID nt; 2578 2579 if (error) 2580 *error = NULL; 2581 2582 if (!wait_result(selection)) 2583 return NULL; 2584 2585 if (selection->client == NULL) 2586 return NULL; 2587 2588 nt = ntstring2oid(selection->clienttype); 2589 if (nt == NULL) 2590 nt = GSS_C_NT_USER_NAME; 2591 2592 buffer.value = cf2cstring(selection->client); 2593 if (buffer.value == NULL) 2594 return NULL; 2595 buffer.length = strlen((char *)buffer.value); 2596 2597 major_status = gss_import_name(&minor_status, &buffer, nt, &name); 2598 free(buffer.value); 2599 if (major_status) { 2600 updateError(NULL, error, major_status, CFSTR("Failed create name for %@"), selection->server); 2601 return NULL; 2602 } 2603 2604 major_status = gss_acquire_cred(&minor_status, name, GSS_C_INITIATE, NULL, GSS_C_INITIATE, &cred, NULL, NULL); 2605 gss_release_name(&junk, &name); 2606 if (major_status) { 2607 updateError(NULL, error, major_status, CFSTR("Failed create credential for %@"), selection->server); 2608 return NULL; 2609 } 2610 2611 return cred; 2612} 2613 2614gss_name_t 2615NAHSelectionGetGSSAcceptorName(NAHSelectionRef selection, CFErrorRef *error) 2616{ 2617 gss_buffer_desc buffer; 2618 OM_uint32 minor_status, major_status; 2619 gss_name_t name; 2620 gss_OID nt; 2621 2622 if (error) 2623 *error = NULL; 2624 2625 if (!wait_result(selection)) 2626 return GSS_C_NO_NAME; 2627 2628 if (selection->server == NULL) 2629 return GSS_C_NO_NAME; 2630 2631 buffer.value = cf2cstring(selection->server); 2632 if (buffer.value == NULL) 2633 return NULL; 2634 buffer.length = strlen((char *)buffer.value); 2635 2636 nt = ntstring2oid(selection->servertype); 2637 if (nt == NULL) 2638 nt = GSS_C_NT_HOSTBASED_SERVICE; 2639 2640 major_status = gss_import_name(&minor_status, &buffer, nt, &name); 2641 free(buffer.value); 2642 if (major_status) 2643 updateError(NULL, error, major_status, CFSTR("Failed create name for %@"), selection->server); 2644 2645 return name; 2646} 2647 2648gss_OID 2649NAHSelectionGetGSSMech(NAHSelectionRef selection) 2650{ 2651 if (!wait_result(selection)) 2652 return NULL; 2653 2654 return mech2oid(selection->mech); 2655} 2656 2657/* 2658 * Same again, but for AuthenticationInfo dictionary 2659 */ 2660 2661gss_cred_id_t 2662NAHAuthenticationInfoCopyClientCredential(CFDictionaryRef authInfo, CFErrorRef *error) 2663{ 2664 gss_buffer_desc buffer; 2665 OM_uint32 minor_status, major_status, junk; 2666 gss_name_t name; 2667 gss_cred_id_t cred = NULL; 2668 gss_OID nt, mech; 2669 2670 if (error) 2671 *error = NULL; 2672 2673 CFStringRef mechanism = CFDictionaryGetValue(authInfo, kNAHCredentialType); 2674 CFStringRef clientName = CFDictionaryGetValue(authInfo, kNAHClientPrincipal); 2675 CFStringRef clientNameType = CFDictionaryGetValue(authInfo, kNAHClientNameType); 2676 2677 if (mechanism == NULL || clientName == NULL || clientNameType == NULL) { 2678 updateError(NULL, error, EINVAL, CFSTR("key missing from AuthenticationInfo")); 2679 return NULL; 2680 } 2681 2682 mech = name2oid(mechanism); 2683 if (mech == NULL) { 2684 updateError(NULL, error, EINVAL, CFSTR("unknown mech")); 2685 return NULL; 2686 } 2687 2688 nt = ntstring2oid(clientNameType); 2689 if (nt == NULL) 2690 nt = GSS_C_NT_USER_NAME; 2691 2692 buffer.value = cf2cstring(clientName); 2693 if (buffer.value == NULL) 2694 return NULL; 2695 buffer.length = strlen((char *)buffer.value); 2696 2697 major_status = gss_import_name(&minor_status, &buffer, nt, &name); 2698 free(buffer.value); 2699 if (major_status) { 2700 updateError(NULL, error, major_status, CFSTR("Failed create name for %@"), clientName); 2701 return NULL; 2702 } 2703 2704 major_status = gss_acquire_cred(&minor_status, name, GSS_C_INITIATE, NULL, GSS_C_INITIATE, &cred, NULL, NULL); 2705 gss_release_name(&junk, &name); 2706 if (major_status) { 2707 updateError(NULL, error, major_status, CFSTR("Failed create credential for %@"), clientName); 2708 return NULL; 2709 } 2710 2711 return cred; 2712} 2713 2714gss_name_t 2715NAHAuthenticationInfoCopyServerName(CFDictionaryRef authInfo, CFErrorRef *error) 2716{ 2717 gss_buffer_desc buffer; 2718 OM_uint32 minor_status, major_status; 2719 gss_name_t name; 2720 gss_OID nt; 2721 2722 if (error) 2723 *error = NULL; 2724 2725 CFStringRef serverName = CFDictionaryGetValue(authInfo, kNAHServerPrincipal); 2726 CFStringRef serverNameType = CFDictionaryGetValue(authInfo, kNAHServerNameType); 2727 2728 if (serverName == NULL || serverNameType == NULL) { 2729 updateError(NULL, error, EINVAL, CFSTR("key missing from AuthenticationInfo")); 2730 return NULL; 2731 } 2732 2733 nt = ntstring2oid(serverNameType); 2734 if (nt == NULL) 2735 nt = GSS_C_NT_HOSTBASED_SERVICE; 2736 2737 buffer.value = cf2cstring(serverName); 2738 if (buffer.value == NULL) 2739 return NULL; 2740 buffer.length = strlen((char *)buffer.value); 2741 2742 major_status = gss_import_name(&minor_status, &buffer, nt, &name); 2743 free(buffer.value); 2744 if (major_status) { 2745 updateError(NULL, error, major_status, CFSTR("Failed create name for %@"), serverName); 2746 return NULL; 2747 } 2748 2749 return name; 2750} 2751 2752gss_OID 2753NAHAuthenticationInfoGetGSSMechanism(CFDictionaryRef authInfo, CFErrorRef *error) 2754{ 2755 CFStringRef mechanism = CFDictionaryGetValue(authInfo, kNAHMechanism); 2756 2757 if (mechanism == NULL) { 2758 updateError(NULL, error, EINVAL, CFSTR("key missing from AuthenticationInfo")); 2759 return NULL; 2760 } 2761 2762 return name2oid(mechanism); 2763} 2764 2765/* 2766 * These are the maching the OID version kGSSAPIMech<name>OID 2767 */ 2768 2769CFStringRef kGSSAPIMechNTLM = CFSTR("NTLM"); 2770CFStringRef kGSSAPIMechKerberos = CFSTR("Kerberos"); 2771CFStringRef kGSSAPIMechKerberosU2U = CFSTR("KerberosUser2User"); 2772CFStringRef kGSSAPIMechKerberosMicrosoft = CFSTR("KerberosMicrosoft"); 2773CFStringRef kGSSAPIMechIAKerb = CFSTR("IAKerb"); 2774CFStringRef kGSSAPIMechPKU2U = CFSTR("PKU2U"); 2775CFStringRef kGSSAPIMechSPNEGO = CFSTR("SPNEGO"); 2776