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