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