1/*
2 * Copyright (c) 2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2011 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of KTH nor the names of its contributors may be
20 *    used to endorse or promote products derived from this software without
21 *    specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36
37#define HEIMDAL_PRINTF_ATTRIBUTE(x)
38
39#include <GSSItem.h>
40#include <gssapi.h>
41#include <gssapi_spi.h>
42#include <gssapi_mech.h>
43#include <heimbase.h>
44#include <krb5.h>
45#include <roken.h>
46#include <kcm.h>
47
48
49
50#include <notify.h>
51
52#include <Security/Security.h>
53
54#define GSS_CONST_TYPE(t,k) const t k = (t)(CFSTR(#k));
55
56GSS_CONST_TYPE(CFTypeRef,   kGSSAttrClass);
57GSS_CONST_TYPE(CFStringRef, kGSSAttrClassKerberos);
58GSS_CONST_TYPE(CFStringRef, kGSSAttrClassNTLM);
59GSS_CONST_TYPE(CFStringRef, kGSSAttrClassIAKerb);
60
61GSS_CONST_TYPE(CFTypeRef,   kGSSAttrSupportGSSCredential);
62GSS_CONST_TYPE(CFTypeRef,   kGSSAttrNameType);
63GSS_CONST_TYPE(CFTypeRef,   kGSSAttrNameTypeGSSExportedName);
64GSS_CONST_TYPE(CFTypeRef,   kGSSAttrNameTypeGSSUsername);
65GSS_CONST_TYPE(CFTypeRef,   kGSSAttrNameTypeGSSHostBasedService);
66GSS_CONST_TYPE(CFTypeRef,   kGSSAttrName);
67GSS_CONST_TYPE(CFTypeRef,   kGSSAttrNameDisplay);
68GSS_CONST_TYPE(CFTypeRef,   kGSSAttrUUID);
69GSS_CONST_TYPE(CFTypeRef,   kGSSAttrTransientExpire);
70GSS_CONST_TYPE(CFTypeRef,   kGSSAttrTransientDefaultInClass);
71GSS_CONST_TYPE(CFTypeRef,   kGSSAttrCredentialPassword);
72GSS_CONST_TYPE(CFTypeRef,   kGSSAttrCredentialStore);
73GSS_CONST_TYPE(CFTypeRef,   kGSSAttrCredentialSecIdentity);
74GSS_CONST_TYPE(CFTypeRef,   kGSSAttrCredentialExists);
75GSS_CONST_TYPE(CFTypeRef,   kGSSAttrStatusPersistant);
76GSS_CONST_TYPE(CFTypeRef,   kGSSAttrStatusAutoAcquire);
77GSS_CONST_TYPE(CFTypeRef,   kGSSAttrStatusTransient);
78
79GSS_CONST_TYPE(CFTypeRef,   kGSSAttrStatusAutoAcquireStatus);
80
81
82GSS_CONST_TYPE(CFTypeRef,   kGSSOperationChangePasswordOldPassword);
83GSS_CONST_TYPE(CFTypeRef,   kGSSOperationChangePasswordNewPassword);
84
85#undef GSS_CONST_TYPE
86
87static CFNumberRef kGSSSecPasswordType = NULL;
88
89static gss_cred_id_t itemToGSSCred(GSSItemRef, OM_uint32 *, CFErrorRef *);
90static void updateTransientValues(GSSItemRef);
91
92
93static CFStringRef kGSSConfKeys = CFSTR("kGSSConfKeys");
94
95
96struct GSSItem {
97    CFRuntimeBase base;
98    CFMutableDictionaryRef keys;
99    CFUUIDRef gssCredential;
100};
101
102static void
103_gssitem_release(struct GSSItem *item)
104{
105    if (item->keys) {
106	CFRelease(item->keys);
107	item->keys = NULL;
108    }
109    if (item->gssCredential) {
110	CFRelease(item->gssCredential);
111	item->gssCredential = NULL;
112    }
113}
114
115static CFDictionaryRef valid_set_types;
116static CFDictionaryRef transient_types;
117static int notify_token;
118static dispatch_queue_t bgq;
119static CFTypeID gssitemid = _kCFRuntimeNotATypeID;
120
121
122static void
123create_tables(void * ctx __attribute__((__unused__)))
124{
125    CFMutableDictionaryRef top, cl;
126
127    bgq = dispatch_queue_create("org.h5l.gss.item", DISPATCH_QUEUE_CONCURRENT);
128    heim_assert(bgq != NULL, "no breakground queue");
129
130    top = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
131    heim_assert(top != NULL, "out of memory");
132    cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
133    heim_assert(top != NULL, "out of memory");
134
135    CFDictionarySetValue(cl, kGSSAttrClass, kCFBooleanTrue);
136    CFDictionarySetValue(cl, kGSSAttrNameType, kCFBooleanTrue);
137    CFDictionarySetValue(cl, kGSSAttrName, kCFBooleanTrue);
138    CFDictionarySetValue(cl, kGSSAttrUUID, kCFBooleanTrue);
139    CFDictionarySetValue(cl, kGSSAttrCredentialPassword, kCFBooleanFalse);
140    CFDictionarySetValue(cl, kGSSAttrCredentialSecIdentity, kCFBooleanFalse);
141    CFDictionarySetValue(cl, kGSSAttrStatusPersistant, kCFBooleanFalse);
142    CFDictionarySetValue(cl, kGSSAttrStatusAutoAcquire, kCFBooleanFalse);
143    CFDictionarySetValue(cl, kGSSAttrStatusTransient, kCFBooleanFalse);
144
145    CFDictionarySetValue(top, kGSSAttrClassKerberos, cl);
146    CFRelease(cl);
147
148    cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
149
150    CFDictionarySetValue(cl, kGSSAttrClass, kCFBooleanTrue);
151    CFDictionarySetValue(cl, kGSSAttrNameType, kCFBooleanTrue);
152    CFDictionarySetValue(cl, kGSSAttrName, kCFBooleanTrue);
153    CFDictionarySetValue(cl, kGSSAttrUUID, kCFBooleanTrue);
154    CFDictionarySetValue(cl, kGSSAttrCredentialPassword, kCFBooleanFalse);
155    CFDictionarySetValue(cl, kGSSAttrStatusPersistant, kCFBooleanFalse);
156    CFDictionarySetValue(cl, kGSSAttrStatusAutoAcquire, kCFBooleanFalse);
157    CFDictionarySetValue(cl, kGSSAttrStatusTransient, kCFBooleanFalse);
158
159    CFDictionarySetValue(top, kGSSAttrClassNTLM, cl);
160    CFRelease(cl);
161
162    valid_set_types = top;
163
164    cl = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
165
166    CFDictionarySetValue(cl, kGSSAttrTransientExpire, kCFBooleanTrue);
167    transient_types = cl;
168
169    const int32_t gssnum = 'GSSP';
170    kGSSSecPasswordType = CFNumberCreate(NULL, kCFNumberSInt32Type, &gssnum);
171
172    (void)notify_register_check(KRB5_KCM_NOTIFY_CACHE_CHANGED, &notify_token);
173
174    static const CFRuntimeClass gssItemClass = {
175		0,
176		"GSSItem",
177		NULL,
178		NULL,
179		(void(*)(CFTypeRef))_gssitem_release,
180		NULL,
181		NULL,
182		NULL,
183		NULL
184	    };
185    gssitemid = _CFRuntimeRegisterClass(&gssItemClass);
186}
187
188static void
189gss_init(void)
190{
191    static dispatch_once_t once;
192    dispatch_once_f(&once, NULL, create_tables);
193}
194
195#pragma mark Item
196
197CFTypeID
198GSSItemGetTypeID(void)
199{
200    gss_init();
201    return gssitemid;
202}
203
204static GSSItemRef
205GSSCreateItem(CFAllocatorRef alloc, CFDictionaryRef keys)
206{
207    GSSItemRef item;
208
209    item = (GSSItemRef)_CFRuntimeCreateInstance(alloc, gssitemid, sizeof(struct GSSItem) - sizeof(CFRuntimeBase), NULL);
210    if (item == NULL)
211	return NULL;
212
213    if (keys)
214	item->keys = CFDictionaryCreateMutableCopy(alloc, 0, keys);
215    else
216	item->keys = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
217    item->gssCredential = NULL;
218
219    return item;
220}
221
222#pragma mark Configuration
223
224
225static CFStringRef
226CreateNewUUID(GSSItemRef item)
227{
228    CFUUIDRef uuid = CFUUIDCreate(NULL);
229    if (uuid == NULL)
230	return NULL;
231
232    /* uuid is not a property list type, make it a string */
233    CFStringRef uuidstr = CFUUIDCreateString(NULL, uuid);
234    CFRelease(uuid);
235    if (uuidstr == NULL)
236	return NULL;
237
238    CFDictionarySetValue(item->keys, kGSSAttrUUID, uuidstr);
239    return uuidstr;
240}
241
242static CFURLRef
243copyConfigurationURL(void)
244{
245    CFURLRef file, home = CFCopyHomeDirectoryURLForUser(NULL);
246    if (home == NULL)
247	return NULL;
248
249    file = CFURLCreateCopyAppendingPathComponent(NULL, home, CFSTR("Library/Preferences/com.apple.GSS.items.plist"), false);
250    CFRelease(home);
251
252    return file;
253}
254
255static CFStringRef
256CopyTransientUUID(gss_cred_id_t cred)
257{
258    CFUUIDRef uuid = GSSCredentialCopyUUID(cred);
259    CFStringRef uuidstr = NULL;
260
261    if (uuid) {
262	uuidstr = CFUUIDCreateString(NULL, uuid);
263	CFRelease(uuid);
264    }
265
266    return uuidstr;
267}
268
269struct CreateContext {
270    CFMutableDictionaryRef c;
271    CFMutableDictionaryRef transitentUUIDs;
272};
273
274static void
275createItem(const void *key, const void *value, void *contextValue)
276{
277    struct CreateContext *context = contextValue;
278
279    GSSItemRef item = GSSCreateItem(NULL, value);
280    if (item) {
281	gss_cred_id_t cred = itemToGSSCred(item, NULL, NULL);
282
283	if (cred) {
284	    CFStringRef uuid = CopyTransientUUID(cred);
285	    if (uuid) {
286		CFDictionarySetValue(context->transitentUUIDs, uuid, kCFBooleanTrue);
287		CFRelease(uuid);
288	    }
289	}
290
291	CFDictionarySetValue(context->c, key, item);
292	CFRelease(item);
293    }
294}
295
296static void
297addTransientKeys(struct CreateContext *createContext)
298{
299    OM_uint32 min_stat;
300
301    gss_iter_creds (&min_stat, 0, GSS_KRB5_MECHANISM, ^(gss_iter_OID oid, gss_cred_id_t cred) {
302	    OM_uint32 maj_stat, junk;
303	    gss_name_t name;
304
305	    CFStringRef uuid = CopyTransientUUID(cred);
306	    if (uuid == NULL)
307		return;
308
309	    if (CFDictionaryGetValue(createContext->transitentUUIDs, uuid)) {
310		CFRelease(uuid);
311		return;
312	    }
313
314	    GSSItemRef item = GSSCreateItem(NULL, NULL);
315	    if (item == NULL) {
316		CFRelease(uuid);
317		return;
318	    }
319
320	    CFDictionarySetValue(item->keys, kGSSAttrUUID, uuid);
321
322	    CFDictionarySetValue(item->keys, kGSSAttrClass, kGSSAttrClassKerberos);
323	    CFDictionarySetValue(item->keys, kGSSAttrNameType, kGSSAttrNameTypeGSSExportedName);
324
325	    name = _gss_cred_copy_name(&junk, cred, NULL);
326	    if (name == NULL) {
327		CFRelease(uuid);
328		CFRelease(item);
329		return;
330	    }
331
332	    gss_buffer_desc buffer = { 0, NULL };
333	    maj_stat = gss_export_name(&junk, name, &buffer);
334	    gss_release_name(&junk, &name);
335	    if (maj_stat) {
336		CFRelease(uuid);
337		CFRelease(item);
338		return;
339	    }
340
341	    CFDataRef data = CFDataCreate(NULL, buffer.value, buffer.length);
342	    CFDictionarySetValue(item->keys, kGSSAttrName, data);
343	    CFRelease(data);
344
345	    updateTransientValues(item);
346	    CFDictionarySetValue(item->keys, kGSSAttrStatusTransient, kCFBooleanTrue);
347
348	    CFDictionarySetValue(createContext->c, uuid, item);
349
350	    item->gssCredential = GSSCredentialCopyUUID(cred);
351
352	    CFRelease(item);
353	    CFRelease(uuid);
354	});
355}
356
357static void
358initCreateContext(struct CreateContext *createContext)
359{
360    heim_assert(createContext->c == NULL, "init more then once");
361
362    createContext->c = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
363    heim_assert(createContext->c != NULL, "out of memory");
364
365    createContext->transitentUUIDs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
366    heim_assert(createContext->transitentUUIDs != NULL, "out of memory");
367}
368
369static CFMutableDictionaryRef
370copyConfiguration(bool create, CFErrorRef *error)
371{
372    struct CreateContext createContext = { NULL, NULL };
373    CFReadStreamRef s;
374    CFDictionaryRef d = NULL, keys;
375    CFURLRef url;
376
377    url = copyConfigurationURL();
378    if (url == NULL)
379	return NULL;
380
381    s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
382    CFRelease(url);
383    if (s == NULL)
384	goto out;
385
386    if (!CFReadStreamOpen(s)) {
387	CFRelease(s);
388	goto out;
389    }
390
391    d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, error);
392    CFRelease(s);
393    if (d == NULL)
394	goto out;
395
396    if (CFGetTypeID(d) != CFDictionaryGetTypeID())
397	goto out;
398
399    initCreateContext(&createContext);
400
401    keys = CFDictionaryGetValue(d, kGSSConfKeys);
402    if (keys == NULL) {
403	CFRelease(createContext.c);
404	createContext.c = NULL;
405	goto out;
406    }
407
408    CFDictionaryApplyFunction(keys, createItem, &createContext);
409
410 out:
411    if (create && createContext.c == NULL)
412	initCreateContext(&createContext);
413
414    if (createContext.c)
415	addTransientKeys(&createContext);
416
417    if (createContext.transitentUUIDs)
418	CFRelease(createContext.transitentUUIDs);
419    if (d)
420	CFRelease(d);
421
422    return createContext.c;
423}
424
425static void
426storeItem(const void *key, const void *value, void *context)
427{
428    GSSItemRef item = (GSSItemRef)value;
429
430    heim_assert(CFGetTypeID(item) == GSSItemGetTypeID(), "trying to store a non GSSItem");
431
432    /* if the credential is a transit credential, dont store in store */
433    if (CFDictionaryGetValue(item->keys, kGSSAttrStatusTransient))
434	return;
435    CFDictionarySetValue(context, key, item->keys);
436}
437
438static void
439storeConfiguration(CFDictionaryRef conf)
440{
441    CFMutableDictionaryRef c = NULL, keys = NULL;
442    CFWriteStreamRef s;
443    CFURLRef url;
444
445    url = copyConfigurationURL();
446    if (url == NULL)
447	return;
448
449    s = CFWriteStreamCreateWithFile(NULL, url);
450    CFRelease(url);
451    if (s == NULL)
452	return;
453
454    keys = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
455    if (keys == NULL) {
456	CFRelease(s);
457	return;
458    }
459    CFDictionaryApplyFunction(conf, storeItem, keys);
460
461    c = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
462    if (c == NULL) {
463	CFRelease(keys);
464	CFRelease(s);
465	return;
466    }
467
468    CFDictionarySetValue(c, kGSSConfKeys, keys);
469    CFRelease(keys);
470
471    if (CFWriteStreamOpen(s)) {
472	CFPropertyListWrite(c, s, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
473	CFWriteStreamClose(s);
474    }
475
476    CFRelease(s);
477    CFRelease(c);
478}
479
480#pragma mark ToGSS conversion
481
482static gss_name_t
483itemCopyGSSName(GSSItemRef item, CFErrorRef *error)
484{
485    gss_name_t gssname = GSS_C_NO_NAME;
486    CFTypeRef name, type;
487    CFTypeID nametype;
488    gss_buffer_desc buffer;
489    gss_const_OID nameoid;
490    OM_uint32 junk;
491
492    type = CFDictionaryGetValue(item->keys, kGSSAttrNameType);
493    if (type == NULL)
494	return GSS_C_NO_NAME;
495
496    if (CFEqual(type, kGSSAttrNameTypeGSSUsername))
497	nameoid = GSS_C_NT_USER_NAME;
498    else if (CFEqual(type, kGSSAttrNameTypeGSSHostBasedService))
499	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
500    else if (CFEqual(type, kGSSAttrNameTypeGSSExportedName))
501	nameoid = GSS_C_NT_EXPORT_NAME;
502    else
503	return GSS_C_NO_NAME;
504
505    name = CFDictionaryGetValue(item->keys, kGSSAttrName);
506    if (name == NULL)
507	return GSS_C_NO_NAME;
508
509    nametype = CFGetTypeID(name);
510    if (nametype == CFStringGetTypeID()) {
511	buffer.value = rk_cfstring2cstring(name);
512	if (buffer.value == NULL)
513	    return GSS_C_NO_NAME;
514	buffer.length = strlen((char *)buffer.value);
515    } else if (nametype == CFDataGetTypeID()) {
516	buffer.value = malloc(CFDataGetLength(name));
517	if (buffer.value == NULL)
518	    return GSS_C_NO_NAME;
519	memcpy(buffer.value, CFDataGetBytePtr(name), CFDataGetLength(name));
520	buffer.length = CFDataGetLength(name);
521    } else
522	return GSS_C_NO_NAME;
523
524    (void)gss_import_name(&junk, &buffer, nameoid, &gssname);
525
526    return gssname;
527}
528
529static gss_const_OID
530itemToMechOID(GSSItemRef item, CFErrorRef *error)
531{
532    CFTypeRef type;
533
534    type = CFDictionaryGetValue(item->keys, kGSSAttrClass);
535    if (type == NULL)
536	return GSS_C_NO_OID;
537
538    if (CFEqual(type, kGSSAttrClassKerberos))
539	return GSS_KRB5_MECHANISM;
540    else if (CFEqual(type, kGSSAttrClassNTLM))
541	return GSS_NTLM_MECHANISM;
542    else if (CFEqual(type, kGSSAttrClassIAKerb))
543	return GSS_IAKERB_MECHANISM;
544
545    return GSS_C_NO_OID;
546}
547
548static gss_cred_id_t
549itemToGSSCred(GSSItemRef item, OM_uint32 *lifetime, CFErrorRef *error)
550{
551    gss_OID_set mechs = GSS_C_NO_OID_SET;
552    OM_uint32 maj_stat, min_stat;
553    gss_cred_id_t gsscred;
554    gss_const_OID mechoid;
555    gss_name_t name;
556
557    if (item->gssCredential) {
558	gsscred = GSSCreateCredentialFromUUID(item->gssCredential);
559	if (gsscred && lifetime)
560	    (void)gss_inquire_cred(&min_stat, gsscred, NULL, lifetime, NULL, NULL);
561	return gsscred;
562    }
563
564    mechoid = itemToMechOID(item, error);
565    if (mechoid == NULL)
566	return GSS_C_NO_CREDENTIAL;
567
568    name = itemCopyGSSName(item, error);
569    if (name == NULL)
570	return GSS_C_NO_CREDENTIAL;
571
572    maj_stat = gss_create_empty_oid_set(&min_stat, &mechs);
573    if (maj_stat != GSS_S_COMPLETE) {
574	if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, NULL);
575	gss_release_name(&min_stat, &name);
576	return GSS_C_NO_CREDENTIAL;
577    }
578
579    maj_stat = gss_add_oid_set_member(&min_stat, mechoid, &mechs);
580    if (maj_stat != GSS_S_COMPLETE) {
581	if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, NULL);
582	gss_release_oid_set(&min_stat, &mechs);
583	gss_release_name(&min_stat, &name);
584	return GSS_C_NO_CREDENTIAL;
585    }
586
587    maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE, mechs,
588				GSS_C_INITIATE, &gsscred, NULL, lifetime);
589    gss_release_oid_set(&min_stat, &mechs);
590    gss_release_name(&min_stat, &name);
591    if (maj_stat) {
592	if (error) *error = _gss_mg_create_cferror(maj_stat, min_stat, mechoid);
593	return GSS_C_NO_CREDENTIAL;
594    }
595
596    item->gssCredential = GSSCredentialCopyUUID(gsscred);
597
598    return gsscred;
599}
600
601#pragma mark Keychain
602
603static CFTypeRef
604extractCopyPassword(GSSItemRef item, bool getAttributes)
605{
606#ifndef __APPLE_TARGET_EMBEDDED__
607    OM_uint32 maj_stat, junk;
608    gss_buffer_desc buffer;
609    CFDataRef password = NULL;
610    char *name, *realm;
611    gss_name_t gssname;
612    CFStringRef val;
613    OSStatus osret;
614    UInt32 length;
615    void *buf;
616    void *ptr;
617
618    /* First look for UUID version of the credential */
619
620    val = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID);
621    if (val) {
622	CFTypeRef result = NULL;
623
624	name = rk_cfstring2cstring(val);
625	if (name == NULL)
626	    return NULL;
627
628	CFDataRef dataoid = CFDataCreate(NULL, (void *)name, strlen(name));
629	free(name);
630
631	CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
632	if (query == NULL) {
633	    CFRelease(dataoid);
634	    return NULL;
635	}
636
637	CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
638	CFDictionaryAddValue(query, kSecAttrType, kGSSSecPasswordType);
639	CFDictionaryAddValue(query, kSecAttrGeneric, dataoid);
640	CFDictionaryAddValue(query, kSecAttrService, CFSTR("GSS"));
641	CFRelease(dataoid);
642	if (getAttributes)
643	    CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
644	else
645	    CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
646
647	osret = SecItemCopyMatching(query, &result);
648	CFRelease(query);
649	if (osret == noErr) {
650	    CFTypeID expectedType = getAttributes ? CFDictionaryGetTypeID() : CFDataGetTypeID();
651	    if (CFGetTypeID(result) != expectedType) {
652		CFRelease(result);
653		return NULL;
654	    }
655	    return result;
656	}
657    }
658
659    if (getAttributes)
660	return NULL;
661
662    /* check for legacy kerberos version */
663
664    gssname = itemCopyGSSName(item, NULL);
665    if (gssname == NULL)
666	return NULL;
667
668    maj_stat = gss_display_name(&junk, gssname, &buffer, NULL);
669    gss_release_name(&junk, &gssname);
670    if (maj_stat)
671	return NULL;
672
673    ptr = memchr(buffer.value, '@', buffer.length);
674    if (ptr == NULL) {
675	asprintf(&name, "%.*s", (int)buffer.length, buffer.value);
676	realm = strdup("");
677    } else {
678	size_t len = (int)((char *)ptr - (char *)buffer.value);
679	asprintf(&name, "%.*s", (int)len, (char *)buffer.value);
680	asprintf(&realm, "%.*s", (int)(buffer.length - len - 1), (char *)buffer.value + len + 1);
681    }
682    gss_release_buffer(&junk, &buffer);
683
684    osret = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(realm), realm,
685					   (UInt32)strlen(name), name,
686					   &length, &buf, NULL);
687    free(name);
688    free(realm);
689    if (osret != noErr)
690	return NULL;
691
692    password = CFDataCreate(NULL, buf, length);
693    SecKeychainItemFreeContent(NULL, buf);
694
695    return password;
696#else /* __APPLE_TARGET_EMBEDDED__ */
697    return NULL;
698#endif /* !__APPLE_TARGET_EMBEDDED__ */
699}
700
701static Boolean
702storePassword(GSSItemRef item, CFDictionaryRef attributes, CFStringRef password, CFErrorRef *error)
703{
704#ifndef __APPLE_TARGET_EMBEDDED__
705    CFTypeRef itemRef = NULL;
706    OSStatus osret;
707    CFStringRef uuidname;
708    char *name, *pw;
709
710    uuidname = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID);
711    if (uuidname == NULL && CFGetTypeID(uuidname) != CFStringGetTypeID())
712	return false;
713
714    name = rk_cfstring2cstring(uuidname);
715    if (name == NULL)
716	return false;
717
718    CFDataRef dataoid = CFDataCreate(NULL, (void *)name, strlen(name));
719    free(name);
720    if (dataoid == NULL)
721	return false;
722
723    CFMutableDictionaryRef itemAttr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
724    if (itemAttr == NULL) {
725	CFRelease(dataoid);
726	return false;
727    }
728
729    CFDictionaryAddValue(itemAttr, kSecAttrGeneric, dataoid);
730    CFRelease(dataoid);
731
732    CFStringRef gssclass = CFDictionaryGetValue(item->keys, kGSSAttrClass);
733    if (gssclass == NULL)
734	gssclass = CFSTR("unknown mech");
735    CFStringRef displayname = CFDictionaryGetValue(item->keys, kGSSAttrNameDisplay);
736    if (displayname == NULL)
737	displayname = uuidname;
738
739    CFStringRef description = CFStringCreateWithFormat(NULL, NULL, CFSTR("GSS %@ password for %@"), gssclass, displayname);
740    if (description == NULL) {
741	CFRelease(itemAttr);
742	return false;
743    }
744    CFDictionaryAddValue(itemAttr, kSecAttrDescription, description);
745    CFDictionaryAddValue(itemAttr, kSecAttrLabel, description);
746    CFRelease(description);
747
748    CFDictionaryAddValue(itemAttr, kSecClass, kSecClassGenericPassword);
749    CFDictionaryAddValue(itemAttr, kSecAttrType, kGSSSecPasswordType);
750    CFDictionaryAddValue(itemAttr, kSecAttrAccount, uuidname);
751    CFDictionaryAddValue(itemAttr, kSecAttrService, CFSTR("GSS"));
752
753    pw = rk_cfstring2cstring(password);
754    if (pw == NULL) {
755	CFRelease(itemAttr);
756	return false;
757    }
758    CFDataRef datapw = CFDataCreate(NULL, (void *)pw, strlen(pw));
759    memset(pw, 0, strlen(pw));
760    free(pw);
761    if (datapw == NULL) {
762	CFRelease(itemAttr);
763	return false;
764    }
765    CFDictionaryAddValue(itemAttr, kSecValueData, datapw);
766    CFRelease(datapw);
767
768    CFTypeRef access = (void *)CFDictionaryGetValue(attributes, kSecAttrAccessGroup);
769    if (access)
770	CFDictionaryAddValue(itemAttr, kSecAttrAccessGroup, access);
771
772    osret = SecItemAdd(itemAttr, &itemRef);
773    CFRelease(itemAttr);
774    if (osret)
775	return false;
776
777    CFRelease(itemRef);
778
779    return true;
780#else
781    return false;
782#endif /* !__APPLE_TARGET_EMBEDDED__ */
783}
784
785#pragma mark Matching
786
787struct iterateAttr {
788    GSSItemRef item;
789    CFDictionaryRef attrs;
790    CFErrorRef error;
791    bool match;
792};
793
794static bool
795applyClassItems(GSSItemRef item, CFDictionaryRef attributes,
796		CFDictionaryApplierFunction applier, CFErrorRef *error)
797{
798    struct iterateAttr mattrs;
799    CFDictionaryRef classattrs;
800    CFTypeRef itemclass;
801
802    if (error)
803	*error = NULL;
804
805    itemclass = CFDictionaryGetValue(attributes, kGSSAttrClass);
806    if (itemclass == NULL) {
807	itemclass = CFDictionaryGetValue(item->keys, kGSSAttrClass);
808	if (itemclass == NULL)
809	    return false;
810    }
811
812    classattrs = CFDictionaryGetValue(valid_set_types, itemclass);
813    if (classattrs == NULL)
814	return false;
815
816    mattrs.attrs = attributes;
817    mattrs.item = item;
818    mattrs.error = NULL;
819    mattrs.match = true;
820
821    CFDictionaryApplyFunction(classattrs, applier, &mattrs);
822
823    if (mattrs.error) {
824	if (error)
825	    *error = mattrs.error;
826	else
827	    CFRelease(mattrs.error);
828	return false;
829    }
830    return mattrs.match;
831}
832
833static void
834matchAttr(const void *key, const void *value, void *context)
835{
836    struct iterateAttr *mattrs = context;
837
838    if (!mattrs->match)
839	return;
840
841    CFTypeRef attrvalue = CFDictionaryGetValue(mattrs->attrs, key);
842    CFTypeRef itemvalue = CFDictionaryGetValue(mattrs->item->keys, key);
843
844    if (attrvalue && itemvalue && !CFEqual(attrvalue, itemvalue))
845	mattrs->match = false;
846}
847
848static bool
849matchAttrs(GSSItemRef item, CFDictionaryRef attrs)
850{
851    return applyClassItems(item, attrs, matchAttr, NULL);
852}
853
854struct search {
855    CFDictionaryRef attrs;
856    CFMutableArrayRef result;
857};
858
859static void
860searchFunction(const void *key, const void *value, void *context)
861{
862    struct search *search = context;
863    GSSItemRef item = (GSSItemRef)value;
864
865    if (matchAttrs(item, search->attrs))
866	CFArrayAppendValue(search->result, item);
867}
868
869
870static CFMutableArrayRef
871searchCopyResult(CFMutableDictionaryRef conf, CFDictionaryRef attrs)
872{
873    CFMutableArrayRef result;
874    struct search search;
875
876    result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
877    if (result == NULL)
878	return NULL;
879
880    search.attrs = attrs;
881    search.result = result;
882
883    CFDictionaryApplyFunction(conf, searchFunction, &search);
884
885    if (CFArrayGetCount(result) == 0) {
886	CFRelease(result);
887	return NULL;
888    }
889
890    return result;
891}
892
893static void
894modifyAttributes(const void *key, const void *value, void *context)
895{
896    struct iterateAttr *mattrs = context;
897    CFTypeRef newValue = CFDictionaryGetValue(mattrs->attrs, key);
898
899    /* check if this valid should be stored in the permanent store */
900    if (!CFBooleanGetValue(value))
901	return;
902
903    if (newValue)
904	CFDictionarySetValue(mattrs->item->keys, key, newValue);
905
906}
907
908static bool
909modifyItem(GSSItemRef item, CFDictionaryRef attributes, CFErrorRef *error)
910{
911    bool res = applyClassItems(item, attributes, modifyAttributes, error);
912    if (!res)
913	return res;
914
915    CFTypeRef cred = CFDictionaryGetValue(attributes, kGSSAttrCredentialPassword);
916
917    if (cred && CFGetTypeID(cred) == CFStringGetTypeID()) {
918	bool shouldStore = true, mustStore = false;
919	CFBooleanRef store;
920
921	store = CFDictionaryGetValue(attributes, kGSSAttrCredentialStore);
922	if (store)
923	    shouldStore = mustStore = (CFGetTypeID(store) == CFBooleanGetTypeID() && CFBooleanGetValue(store));
924
925	if (shouldStore) {
926	    res = storePassword(item, attributes, cred, error);
927	    if (res && mustStore)
928		return res;
929	}
930    }
931    return true;
932}
933
934
935static void
936validateAttributes(const void *key, const void *value, void *context)
937{
938    struct iterateAttr *mattrs = context;
939
940    if (mattrs->error != NULL)
941	return;
942
943    Boolean b = CFBooleanGetValue(value);
944    if (b) {
945	CFTypeRef status = CFDictionaryGetValue(mattrs->attrs, key);
946	if (status == NULL)
947	    mattrs->error = CFErrorCreate(NULL, CFSTR("com.apple.GSS"), EINVAL, NULL);
948    }
949}
950
951
952static bool
953validateItem(GSSItemRef item, CFErrorRef *error)
954{
955    return applyClassItems(item, item->keys, validateAttributes, error);
956}
957
958/*
959 *
960 */
961
962static void
963updateTransientValues(GSSItemRef item)
964{
965    OM_uint32 maj_stat, junk, lifetime = 0;
966    gss_cred_id_t gsscred = itemToGSSCred(item, &lifetime, NULL);
967    gss_buffer_desc buffer;
968
969    CFDictionaryRef attributes = extractCopyPassword(item, 1);
970    if (attributes) {
971	CFDictionarySetValue(item->keys, kGSSAttrCredentialPassword, kCFBooleanTrue);
972	CFRelease(attributes);
973    }
974
975    if (gsscred) {
976
977	CFDictionarySetValue(item->keys, kGSSAttrCredentialExists, kCFBooleanTrue);
978
979	maj_stat = gss_cred_label_get(&junk, gsscred, "kcm-status", &buffer);
980	if (maj_stat == GSS_S_COMPLETE) {
981	    CFDataRef data = CFDataCreate(NULL, buffer.value, buffer.length);
982
983	    if (data) {
984		CFDictionarySetValue(item->keys, kGSSAttrStatusAutoAcquireStatus, data);
985		CFRelease(data);
986	    }
987	    gss_release_buffer(&junk, &buffer);
988	}
989
990#define TimeIntervalSince1970  978307200.0
991	CFDateRef date;
992	if (lifetime)
993	    date = CFDateCreate(NULL, time(NULL) - TimeIntervalSince1970 + lifetime);
994	else
995	    date = CFDateCreate(NULL, 0.0); /* We don't know when expired, so make it expired */
996
997	CFDictionarySetValue(item->keys, kGSSAttrTransientExpire, date);
998	CFRelease(date);
999
1000	{
1001	    gss_buffer_set_t buffers = NULL;
1002
1003	    maj_stat = gss_inquire_cred_by_oid(&junk, gsscred, GSS_C_CRED_GET_DEFAULT, &buffers);
1004	    gss_release_buffer_set(&junk, &buffers);
1005	    if (maj_stat == GSS_S_COMPLETE)
1006		CFDictionarySetValue(item->keys, kGSSAttrTransientDefaultInClass, kCFBooleanTrue);
1007	}
1008
1009	CFRelease(gsscred);
1010
1011    } else {
1012	CFDictionaryRemoveValue(item->keys, kGSSAttrTransientExpire);
1013    }
1014
1015    gss_name_t gssname = itemCopyGSSName(item, NULL);
1016    if (gssname) {
1017
1018	maj_stat = gss_display_name(&junk, gssname, &buffer, NULL);
1019	if (maj_stat == GSS_S_COMPLETE) {
1020	    CFStringRef name;
1021	    name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%.*s"), (int)buffer.length, buffer.value);
1022	    gss_release_buffer(&junk, &buffer);
1023	    if (name) {
1024		CFDictionarySetValue(item->keys, kGSSAttrNameDisplay, name);
1025		CFRelease(name);
1026	    }
1027	}
1028	gss_release_name(&junk, &gssname);
1029    }
1030}
1031
1032#pragma mark API
1033
1034GSSItemRef
1035GSSItemAdd(CFDictionaryRef attributes, CFErrorRef *error)
1036{
1037    CFMutableDictionaryRef conf;
1038    GSSItemRef item;
1039    CFStringRef uuidstr = NULL;
1040    CFMutableArrayRef items;
1041
1042    gss_init();
1043
1044    heim_assert(attributes != NULL, "no attributes passed to GSSItemAdd");
1045
1046    if (error)
1047	*error = NULL;
1048
1049    conf = copyConfiguration(true, error);
1050    if (conf == NULL)
1051	return NULL;
1052
1053    /* refuse to add dups */
1054    items = searchCopyResult(conf, attributes);
1055    if (items) {
1056	CFRelease(items);
1057	CFRelease(conf);
1058	return NULL;
1059    }
1060
1061    item = GSSCreateItem(NULL, NULL);
1062    if (item == NULL)
1063	goto out;
1064
1065    uuidstr = CreateNewUUID(item);
1066    if (uuidstr == NULL) {
1067	CFRelease(item);
1068	item = NULL;
1069	goto out;
1070    }
1071
1072    if (!modifyItem(item, attributes, error)) {
1073	CFRelease(item);
1074	item = NULL;
1075	goto out;
1076    }
1077
1078    if (!validateItem(item, error)) {
1079	CFRelease(item);
1080	item = NULL;
1081	goto out;
1082    }
1083
1084    updateTransientValues(item);
1085
1086    CFDictionarySetValue(conf, uuidstr, item);
1087
1088    storeConfiguration(conf);
1089
1090out:
1091    if (uuidstr)
1092	CFRelease(uuidstr);
1093    if (conf)
1094	CFRelease(conf);
1095
1096    return item;
1097}
1098
1099Boolean
1100GSSItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error)
1101{
1102    CFMutableDictionaryRef conf;
1103    CFMutableArrayRef items;
1104
1105    gss_init();
1106
1107    heim_assert(query != NULL, "no attributes passed to GSSItemAdd");
1108
1109    if (error)
1110	*error = NULL;
1111
1112    conf = copyConfiguration(true, error);
1113    if (conf == NULL)
1114	return false;
1115
1116    items = searchCopyResult(conf, query);
1117    if (items == NULL) {
1118	CFRelease(conf);
1119	return false;
1120    }
1121
1122    CFIndex n, count = CFArrayGetCount(items);
1123    Boolean res = true;
1124    for (n = 0; n < count; n++) {
1125	GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n);
1126
1127	res = modifyItem(item, attributesToUpdate, error);
1128	if (res)
1129	    break;
1130    }
1131    CFRelease(items);
1132
1133    if (res)
1134	storeConfiguration(conf);
1135
1136    CFRelease(conf);
1137
1138    return res;
1139}
1140
1141static Boolean
1142ItemDeleteItem(CFMutableDictionaryRef conf, GSSItemRef item, CFErrorRef *error)
1143{
1144    CFTypeRef uuidstr = CFDictionaryGetValue(item->keys, kGSSAttrUUID);
1145
1146    if (uuidstr == NULL)
1147	return false;
1148
1149    gss_cred_id_t cred = itemToGSSCred(item, NULL, NULL);
1150    if (cred) {
1151	OM_uint32 junk;
1152	gss_destroy_cred(&junk, &cred);
1153    }
1154    CFDictionaryRemoveValue(conf, uuidstr);
1155
1156    return true;
1157}
1158
1159Boolean
1160GSSItemDeleteItem(GSSItemRef item, CFErrorRef *error)
1161{
1162    CFMutableDictionaryRef conf;
1163    Boolean res = false;
1164
1165    conf = copyConfiguration(false, error);
1166    if (conf == NULL)
1167	return false;
1168
1169    res = ItemDeleteItem(conf, item, error);
1170    if (res)
1171	storeConfiguration(conf);
1172
1173    CFRelease(conf);
1174
1175    return res;
1176}
1177
1178Boolean
1179GSSItemDelete(CFDictionaryRef query, CFErrorRef *error)
1180{
1181    CFMutableDictionaryRef conf;
1182    CFMutableArrayRef items;
1183
1184    gss_init();
1185
1186    heim_assert(query != NULL, "no attributes passed to GSSItemDelete");
1187
1188    if (error)
1189	*error = NULL;
1190
1191    conf = copyConfiguration(false, error);
1192    if (conf == NULL)
1193	return false;
1194
1195    items = searchCopyResult(conf, query);
1196    if (items == NULL) {
1197	CFRelease(conf);
1198	return false;
1199    }
1200
1201    CFIndex n, count = CFArrayGetCount(items);
1202    Boolean res = false;
1203
1204    for (n = 0; n < count; n++) {
1205	GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n);
1206
1207	if (ItemDeleteItem(conf, item, error))
1208	    res = true;
1209    }
1210    CFRelease(items);
1211
1212    if (res)
1213	storeConfiguration(conf);
1214
1215    CFRelease(conf);
1216
1217    return res;
1218}
1219
1220CFArrayRef
1221GSSItemCopyMatching(CFDictionaryRef query, CFErrorRef *error)
1222{
1223    CFMutableDictionaryRef conf;
1224    CFMutableArrayRef items;
1225
1226    gss_init();
1227
1228    if (error)
1229	*error = NULL;
1230
1231    conf = copyConfiguration(true, error);
1232    if (conf == NULL)
1233	return NULL;
1234
1235    items = searchCopyResult(conf, query);
1236    CFRelease(conf);
1237    if (items == NULL)
1238	return NULL;
1239
1240    /*
1241     * Update the transient info
1242     */
1243
1244    CFIndex n, count = CFArrayGetCount(items);
1245    for (n = 0; n < count; n++) {
1246	GSSItemRef item = (GSSItemRef)CFArrayGetValueAtIndex(items, n);
1247
1248	updateTransientValues(item);
1249    }
1250
1251    return items;
1252}
1253
1254/*
1255 *
1256 */
1257
1258struct __GSSOperationType {
1259    void (*func)(GSSItemRef, CFDictionaryRef, dispatch_queue_t, GSSItemOperationCallbackBlock);
1260};
1261
1262static void
1263itemAcquire(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1264{
1265    gss_cred_id_t gsscred = GSS_C_NO_CREDENTIAL;
1266    CFMutableDictionaryRef gssattrs = NULL;
1267    gss_name_t name = GSS_C_NO_NAME;
1268    CFErrorRef error = NULL;
1269    gss_const_OID mech;
1270    CFTypeRef cred = NULL;
1271    OM_uint32 junk;
1272
1273    name = itemCopyGSSName(item, &error);
1274    if (name == NULL)
1275	goto out;
1276
1277    mech = itemToMechOID(item, &error);
1278    if (mech == GSS_C_NO_OID)
1279	goto out;
1280
1281    gssattrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1282    if (gssattrs == NULL)
1283	goto out;
1284
1285    if (options) {
1286	cred = CFDictionaryGetValue(options, kGSSAttrCredentialPassword);
1287	if (cred)
1288	    CFDictionarySetValue(gssattrs, kGSSICPassword, cred);
1289	else {
1290	    cred = CFDictionaryGetValue(options, kGSSAttrCredentialSecIdentity);
1291	    if (cred)
1292		CFDictionarySetValue(gssattrs, kGSSICCertificate, cred);
1293	}
1294    }
1295
1296    /*
1297     * Force Kerberos/kcm/gsscred layer to use the the same UUID
1298     */
1299    CFTypeRef uuidstr = CFDictionaryGetValue(item->keys, kGSSAttrUUID);
1300    if (uuidstr) {
1301	CFStringRef cacheName = CFStringCreateWithFormat(NULL, NULL, CFSTR("API:%@"), uuidstr);
1302	if (cacheName == NULL)
1303	    goto out;
1304
1305	CFDictionarySetValue(gssattrs, kGSSICKerberosCacheName, cacheName);
1306	CFRelease(cacheName);
1307    }
1308
1309    if (cred == NULL) {
1310
1311	if ((cred = extractCopyPassword(item, 0)) != NULL) {
1312	    CFDictionarySetValue(gssattrs, kGSSICPassword, cred);
1313	    CFRelease(cred);
1314	    cred = NULL;
1315	} else if ((cred = NULL /* XXX extract certfigcate */) != NULL) {
1316	    /* CFDictionarySetValue(gssattrs, kGSSAttrCredentialSecIdentity, cred); */
1317	}
1318    }
1319
1320    (void)gss_aapl_initial_cred(name, mech, gssattrs, &gsscred, &error);
1321
1322    if (item->gssCredential) {
1323	CFRelease(item->gssCredential);
1324	item->gssCredential = NULL;
1325    }
1326
1327    if (gsscred)
1328	item->gssCredential = GSSCredentialCopyUUID(gsscred);
1329
1330    updateTransientValues(item);
1331
1332 out:
1333    if (gssattrs)
1334	CFRelease(gssattrs);
1335    if (name)
1336	gss_release_name(&junk, &name);
1337
1338    dispatch_async(q, ^{
1339	    callback(gsscred, error);
1340	    if (error)
1341		CFRelease(error);
1342	    if (gsscred)
1343		CFRelease(gsscred);
1344    });
1345}
1346
1347const struct __GSSOperationType __kGSSOperationAcquire = {
1348    itemAcquire
1349};
1350
1351static void
1352itemDestroyTransient(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1353{
1354    CFErrorRef error = NULL;
1355    gss_cred_id_t gsscred;
1356    OM_uint32 junk;
1357    CFBooleanRef result = kCFBooleanFalse;
1358
1359    gsscred = itemToGSSCred(item, NULL, &error);
1360    if (gsscred) {
1361	(void)gss_destroy_cred(&junk, &gsscred);
1362	result = kCFBooleanTrue;
1363    }
1364
1365    dispatch_async(q, ^{
1366	callback(result, error);
1367	if (error)
1368	    CFRelease(error);
1369    });
1370}
1371
1372const struct __GSSOperationType __kGSSOperationDestoryTransient = {
1373    itemDestroyTransient
1374};
1375const struct __GSSOperationType __kGSSOperationDestroyTransient = {
1376    itemDestroyTransient
1377};
1378
1379
1380static void
1381itemGetGSSCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1382{
1383    gss_cred_id_t gsscred;
1384    CFErrorRef error = NULL;
1385
1386    gsscred = itemToGSSCred(item, NULL, &error);
1387
1388    dispatch_async(q, ^{
1389	callback(gsscred, error);
1390	if (error)
1391	    CFRelease(error);
1392    });
1393}
1394
1395const struct __GSSOperationType __kGSSOperationGetGSSCredential = {
1396    itemGetGSSCredential
1397};
1398
1399static void
1400itemCredentialDiagnostics(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1401{
1402    gss_cred_id_t gsscred;
1403    CFErrorRef error = NULL;
1404    CFMutableArrayRef array = NULL;
1405    OM_uint32 junk;
1406
1407    gsscred = itemToGSSCred(item, NULL, &error);
1408    if (gsscred) {
1409	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
1410
1411	if (gss_inquire_cred_by_oid(&junk, gsscred, GSS_C_CRED_DIAG, &data_set) != GSS_S_COMPLETE || data_set->count < 1)
1412	    goto out;
1413
1414	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1415	if (array == NULL)
1416	    goto out;
1417
1418	OM_uint32 n;
1419
1420	for (n = 0; n < data_set->count; n++) {
1421	    CFDataRef data = NULL;
1422
1423	    data = CFDataCreate(NULL, data_set->elements[n].value, data_set->elements[n].length);
1424	    if (data) {
1425		CFArrayAppendValue(array, data);
1426		CFRelease(data);
1427	    }
1428	}
1429	gss_release_buffer_set(&junk, &data_set);
1430	CFRelease(gsscred);
1431    }
1432
1433 out:
1434
1435    dispatch_async(q, ^{
1436	callback(array, error);
1437	if (array)
1438	    CFRelease(array);
1439	if (error)
1440	    CFRelease(error);
1441    });
1442}
1443
1444const struct __GSSOperationType __kGSSOperationCredentialDiagnostics = {
1445    itemCredentialDiagnostics
1446};
1447
1448static void
1449itemChangePassword(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1450{
1451    CFMutableDictionaryRef chpwdopts = NULL;
1452    gss_name_t gssname = NULL;
1453    CFErrorRef error = NULL;
1454    gss_const_OID mech;
1455
1456    CFStringRef oldpw = CFDictionaryGetValue(options, kGSSOperationChangePasswordOldPassword);
1457    CFStringRef newpw = CFDictionaryGetValue(options, kGSSOperationChangePasswordNewPassword);
1458
1459    if (oldpw == NULL || newpw == NULL)
1460	goto out;
1461
1462    mech = itemToMechOID(item, &error);
1463    if (mech == GSS_C_NO_OID)
1464	goto out;
1465
1466    chpwdopts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1467    if (chpwdopts == NULL)
1468	goto out;
1469
1470    gssname = itemCopyGSSName(item, &error);
1471    if (gssname) {
1472
1473	CFDictionaryAddValue(chpwdopts, kGSSChangePasswordOldPassword, oldpw);
1474	CFDictionaryAddValue(chpwdopts, kGSSChangePasswordNewPassword, newpw);
1475
1476	(void)gss_aapl_change_password(gssname, mech, chpwdopts, &error);
1477    }
1478  out:
1479    if (gssname)
1480	CFRelease(gssname);
1481
1482    dispatch_async(q, ^{
1483	callback(NULL, error);
1484	if (error)
1485	    CFRelease(error);
1486    });
1487}
1488
1489const struct __GSSOperationType __kGSSOperationChangePassword = {
1490    itemChangePassword
1491};
1492
1493/*
1494 *
1495 */
1496
1497static void
1498itemSetDefault(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1499{
1500    CFErrorRef error = NULL;
1501    gss_cred_id_t gsscred;
1502
1503    gsscred = itemToGSSCred(item, NULL, &error);
1504    if (gsscred) {
1505	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
1506	OM_uint32 maj_stat, min_stat, junk;
1507
1508	maj_stat = gss_inquire_cred_by_oid(&min_stat, gsscred, GSS_C_CRED_SET_DEFAULT, &data_set);
1509	gss_release_buffer_set(&junk, &data_set);
1510	CFRelease(gsscred);
1511	if (maj_stat != GSS_S_COMPLETE)
1512	    error = _gss_mg_create_cferror(maj_stat, min_stat, NULL);
1513    }
1514
1515    dispatch_async(q, ^{
1516	    callback(NULL, error);
1517	    if (error)
1518		CFRelease(error);
1519	});
1520}
1521
1522const struct __GSSOperationType __kGSSOperationSetDefault = {
1523    itemSetDefault
1524};
1525
1526
1527/*
1528 *
1529 */
1530
1531static void
1532itemRenewCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1533{
1534    CFErrorRef error = NULL;
1535    gss_cred_id_t gsscred;
1536    OM_uint32 maj_stat, min_stat;
1537
1538    gsscred = itemToGSSCred(item, NULL, &error);
1539    if (gsscred) {
1540	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
1541
1542	maj_stat = gss_inquire_cred_by_oid(&min_stat, gsscred, GSS_C_CRED_RENEW, &data_set);
1543	gss_release_buffer_set(&min_stat, &data_set);
1544	CFRelease(gsscred);
1545	if (maj_stat != GSS_S_COMPLETE)
1546	    error = _gss_mg_create_cferror(maj_stat, min_stat, NULL);
1547    }
1548
1549    dispatch_async(q, ^{
1550	    callback(NULL, error);
1551	    if (error)
1552		CFRelease(error);
1553	});
1554}
1555
1556const struct __GSSOperationType __kGSSOperationRenewCredential = {
1557    itemRenewCredential
1558};
1559
1560/*
1561 *
1562 */
1563
1564static void
1565itemRemoveBackingCredential(GSSItemRef item, CFDictionaryRef options, dispatch_queue_t q, GSSItemOperationCallbackBlock callback)
1566{
1567    CFErrorRef error = NULL;
1568    CFStringRef uuidname;
1569
1570    uuidname = CFDictionaryGetValue((CFDictionaryRef)item->keys, kGSSAttrUUID);
1571    if (uuidname == NULL || CFGetTypeID(uuidname) != CFStringGetTypeID())
1572	goto out;
1573
1574    CFMutableDictionaryRef itemAttr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1575    if (itemAttr == NULL) {
1576	goto out;
1577    }
1578
1579    CFDictionaryAddValue(itemAttr, kSecClass, kSecClassGenericPassword);
1580    CFDictionaryAddValue(itemAttr, kSecAttrType, kGSSSecPasswordType);
1581    CFDictionaryAddValue(itemAttr, kSecAttrAccount, uuidname);
1582    CFDictionaryAddValue(itemAttr, kSecAttrService, CFSTR("GSS"));
1583
1584    (void)SecItemDelete(itemAttr);
1585    CFRelease(itemAttr);
1586
1587 out:
1588    dispatch_async(q, ^{
1589	    callback(NULL, error);
1590	    if (error)
1591		CFRelease(error);
1592	});
1593}
1594
1595
1596const struct __GSSOperationType __kGSSOperationRemoveBackingCredential = {
1597    itemRemoveBackingCredential
1598};
1599
1600
1601/*
1602 *
1603 */
1604
1605Boolean
1606GSSItemOperation(GSSItemRef item, GSSOperation op, CFDictionaryRef options,
1607		 dispatch_queue_t q, GSSItemOperationCallbackBlock func)
1608{
1609    GSSItemOperationCallbackBlock callback;
1610
1611    gss_init();
1612
1613    callback = (GSSItemOperationCallbackBlock)Block_copy(func);
1614
1615    CFRetain(item);
1616    if (options)
1617	CFRetain(options);
1618
1619    dispatch_async(bgq, ^{
1620	    op->func(item, options, q, callback);
1621	    Block_release(callback);
1622	    CFRelease(item);
1623	    if (options)
1624		CFRelease(options);
1625	});
1626
1627    return true;
1628}
1629
1630/*
1631 *
1632 */
1633
1634CFTypeRef
1635GSSItemGetValue(GSSItemRef item, CFStringRef key)
1636{
1637    int checkNeeded = 0;
1638
1639    if (CFDictionaryGetValue(transient_types, key)
1640	&& notify_check(notify_token, &checkNeeded) == NOTIFY_STATUS_OK
1641	&& checkNeeded)
1642	updateTransientValues(item);
1643
1644    return CFDictionaryGetValue(item->keys, key);
1645}
1646