1/*-
2 * Copyright (c) 2013 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2013 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 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#import <TargetConditionals.h>
31
32#import <Foundation/Foundation.h>
33#import <CoreFoundation/CoreFoundation.h>
34#import <CoreFoundation/CFRuntime.h>
35#import <System/sys/codesign.h>
36#import <servers/bootstrap.h>
37#import <bsm/audit.h>
38#import <bsm/audit_session.h>
39#import <bsm/libbsm.h>
40#import <xpc/xpc.h>
41#import <xpc/private.h>
42#import <libproc.h>
43#import <syslog.h>
44#import <sandbox.h>
45#import <launch.h>
46#import <launch_priv.h>
47
48#import <dispatch/dispatch.h>
49#import <notify.h>
50#import <mach/mach.h>
51#import <mach/mach_time.h>
52
53#import <roken.h>
54
55#import "HeimCredCoder.h"
56#import "heimcred.h"
57#import "common.h"
58#import "heimbase.h"
59#import "hc_err.h"
60
61/*
62 *
63 */
64
65static bool validateObject(CFDictionaryRef, CFErrorRef *);
66static void addErrorToReply(xpc_object_t, CFErrorRef);
67static CFTypeRef GetValidatedValue(CFDictionaryRef, CFStringRef, CFTypeID, CFErrorRef *);
68static void HCMakeError(CFErrorRef *, CFIndex,  const void *const *, const void *const *, CFIndex);
69static CFTypeID getSessionTypeID(void);
70static struct HeimSession *HeimCredCopySession(int);
71static void handleDefaultCredentialUpdate(struct HeimSession *, HeimCredRef, CFDictionaryRef);
72
73
74/*
75 *
76 */
77struct HeimSession {
78    CFRuntimeBase runtime;
79    uid_t session;
80    CFMutableDictionaryRef items;
81    CFMutableDictionaryRef defaultCredentials;
82    int updateDefaultCredential;
83};
84
85/*
86 *
87 */
88struct HeimMech {
89    CFRuntimeBase runtime;
90    CFStringRef name;
91    HeimCredStatusCallback statusCallback;
92    HeimCredAuthCallback authCallback;
93};
94
95
96
97static NSString *archivePath = NULL;
98static dispatch_queue_t runQueue;
99
100static void
101FlattenCredential(const void *key, const void *value, void *context)
102{
103    NSMutableDictionary *flatten = (NSMutableDictionary *)context;
104    HeimCredRef cred = (HeimCredRef)value;
105    id nskey = [HeimCredDecoder copyCF2NS:key];
106    id attrs = [HeimCredDecoder copyCF2NS:cred->attributes];
107    [flatten setObject:attrs forKey:nskey];
108    [nskey release];
109    [attrs release];
110}
111
112static void
113FlattenSession(const void *key, const void *value, void *context)
114{
115    NSMutableDictionary *sf = (NSMutableDictionary *)context;
116    id cf = NULL;
117    @try {
118	struct HeimSession *session = (struct HeimSession *)value;
119	cf = [[NSMutableDictionary alloc] init];
120	CFDictionaryApplyFunction(session->items, FlattenCredential, cf);
121	[sf setObject:cf forKey:[NSNumber numberWithInt:(int)session->session]];
122    } @catch(NSException * __unused e) {
123    } @finally {
124	[cf release];
125    }
126}
127
128
129static void
130storeCredCache(void)
131{
132    id sf = NULL;
133    @try {
134	sf = [[NSMutableDictionary alloc] init];
135	CFDictionaryApplyFunction(HeimCredCTX.sessions, FlattenSession, sf);
136	[HeimCredDecoder archiveRootObject:sf toFile:archivePath];
137    } @catch(NSException * __unused e) {
138    } @finally {
139	[sf release];
140    }
141}
142
143static uint64_t
144relative_nano_time(void)
145{
146     static uint64_t factor;
147     uint64_t now;
148
149    now = mach_absolute_time();
150
151    if (factor == 0) {
152	mach_timebase_info_data_t base;
153	(void)mach_timebase_info(&base);
154	factor = base.numer / base.denom;
155    }
156
157    return now * factor;
158}
159
160#define KRB5_KCM_NOTIFY_CACHE_CHANGED "com.apple.Kerberos.cache.changed"
161
162static void
163notifyChangedCaches(void)
164{
165    static uint64_t last_change;
166    static int notify_pending;
167
168#define NOTIFY_TIME_LIMIT (NSEC_PER_SEC / 2)
169
170    dispatch_async(dispatch_get_main_queue(), ^{
171	    uint64_t now, diff;
172
173	    if (notify_pending)
174		return;
175
176	    now = relative_nano_time();
177	    if (now < last_change)
178		diff = NOTIFY_TIME_LIMIT;
179	    else
180		diff = now - last_change;
181
182	    if (diff >= NOTIFY_TIME_LIMIT) {
183		notify_post(KRB5_KCM_NOTIFY_CACHE_CHANGED);
184		last_change = now;
185	    } else {
186		notify_pending = 1;
187		/* wait up to deliver the event */
188		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NOTIFY_TIME_LIMIT - diff), dispatch_get_main_queue(), ^{
189			notify_pending = 0;
190			last_change = relative_nano_time();
191			notify_post(KRB5_KCM_NOTIFY_CACHE_CHANGED);
192		    });
193	    }
194	});
195}
196
197static bool
198HeimCredAssignMech(HeimCredRef cred, CFDictionaryRef attributes)
199{
200    heim_assert(cred->mech == NULL, "HeimCredAssignMech already have a mech");
201
202    CFErrorRef error = NULL;
203    CFStringRef mechName = GetValidatedValue(attributes, kHEIMAttrType, CFStringGetTypeID(), &error);
204
205    if (mechName) {
206	struct HeimMech *mech = (struct HeimMech *)CFDictionaryGetValue(HeimCredCTX.mechanisms, mechName);
207	if (mech) {
208	    cred->mech = mech;
209	    return true;
210	}
211    }
212    CFRELEASE_NULL(error);
213    return false;
214}
215
216static bool
217sessionExists(pid_t asid)
218{
219    auditinfo_addr_t aia;
220    aia.ai_asid = asid;
221
222    if (audit_get_sinfo_addr(&aia, sizeof(aia)) == 0)
223	return true;
224    return false;
225}
226
227static void
228readCredCache(void)
229{
230    @autoreleasepool {
231	NSDictionary *sessions = NULL;
232	@try {
233	    sessions = [HeimCredDecoder copyUnarchiveObjectWithFileSecureEncoding:archivePath];
234
235	    if (!sessions || ![sessions isKindOfClass:[NSDictionary class]])
236		return;
237
238	    [sessions enumerateKeysAndObjectsUsingBlock:^(id skey, id svalue, BOOL *sstop) {
239		    int sessionID = [(NSNumber *)skey intValue];
240
241		    if (!sessionExists(sessionID))
242			return;
243
244		    struct HeimSession *session = HeimCredCopySession(sessionID);
245		    if (session == NULL)
246			return;
247
248		    NSDictionary *creds = (NSDictionary *)svalue;
249
250		    if (!creds || ![creds isKindOfClass:[NSDictionary class]]) {
251			CFRelease(session);
252			return;
253		    }
254
255		    [creds enumerateKeysAndObjectsUsingBlock:^(id ckey, id cvalue, BOOL *cstop) {
256			    CFUUIDRef cfkey = [HeimCredDecoder copyNS2CF:ckey];
257			    CFDictionaryRef cfvalue = [HeimCredDecoder copyNS2CF:cvalue];
258			    if (cfkey && cfvalue) {
259				HeimCredRef cred = HeimCredCreateItem(cfkey);
260
261				if (HeimCredAssignMech(cred, cfvalue)) {
262				    cred->attributes = CFRetain(cfvalue);
263				    CFDictionarySetValue(session->items, cred->uuid, cred);
264
265				    handleDefaultCredentialUpdate(session, cred, cred->attributes);
266				} else {
267				    /* dropping cred if mech assignment failed */
268				    CFRelease(cred);
269				}
270			    }
271			    CFRELEASE_NULL(cfkey);
272			    CFRELEASE_NULL(cfvalue);
273			}];
274
275		    CFRelease(session);
276		}];
277	} @catch(NSException *e) {
278	    syslog(LOG_ERR, "readCredCache failed with: %s:%s", [[e name] UTF8String], [[e reason] UTF8String]);
279	}
280    }
281}
282
283struct peer {
284    xpc_connection_t peer;
285    CFStringRef bundleID;
286    struct HeimSession *session;
287};
288
289/*
290 * Check if the credential (uuid) have an ACL and while not having an
291 * ACL, walk up the parent chain and check if any other parents have
292 * an ACL.
293 */
294
295static bool
296checkACLInCredentialChain(struct peer *peer, CFUUIDRef uuid, bool *hasACL)
297{
298    int max_depth = 10;
299    bool res = false;
300
301    if (hasACL)
302	*hasACL = false;
303
304    while (1) {
305	HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(peer->session->items, uuid);
306	CFUUIDRef parent;
307
308	if (cred == NULL)
309		goto pass;
310
311	heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
312
313	if (max_depth-- < 0)
314	    goto out;
315
316	CFArrayRef array = CFDictionaryGetValue(cred->attributes,  kHEIMAttrBundleIdentifierACL);
317	if (array) {
318	    CFIndex n, count;
319
320	    if (hasACL)
321		*hasACL = true;
322
323	    if (CFGetTypeID(array) != CFArrayGetTypeID())
324		goto out;
325
326	    count = CFArrayGetCount(array);
327	    for (n = 0; n < count; n++) {
328		CFStringRef prefix = CFArrayGetValueAtIndex(array, n);
329
330		if (CFGetTypeID(prefix) != CFStringGetTypeID())
331		    goto out;
332
333#if !TARGET_OS_EMBEDDED
334		if (CFEqual(prefix, CFSTR("*")))
335		    goto pass;
336#endif
337		if (CFEqual(peer->bundleID, prefix))
338		    goto pass;
339
340		NSPredicate *wildCardMatch = [NSPredicate predicateWithFormat:@"self like %@", prefix];
341		BOOL matchFound = [wildCardMatch evaluateWithObject:(NSString *)peer->bundleID];
342
343		if (matchFound)
344		    goto pass;
345	    }
346	    if (n >= count)
347		goto out;
348	}
349
350	parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
351	if (parent == NULL)
352	    goto out;
353
354	if (CFEqual(parent, uuid))
355	    break;
356
357	uuid = parent;
358    }
359
360 pass:
361    res = true;
362
363  out:
364    return res;
365}
366
367static bool
368addPeerToACL(struct peer *peer, CFMutableDictionaryRef attrs)
369{
370    CFArrayRef acl = CFDictionaryGetValue(attrs, kHEIMAttrBundleIdentifierACL);
371    if (acl == NULL || CFGetTypeID(acl) != CFArrayGetTypeID())
372	return false;
373
374    if (!CFArrayContainsValue(acl, CFRangeMake(0, CFArrayGetCount(acl)), peer->bundleID)) {
375	CFMutableArrayRef a = CFArrayCreateMutableCopy(NULL, 0, acl);
376	if (a == NULL)
377	    return false;
378	CFArrayAppendValue(a, peer->bundleID);
379	CFDictionarySetValue(attrs, kHEIMAttrBundleIdentifierACL, a);
380	CFRELEASE_NULL(a);
381    }
382
383    return true;
384}
385
386static void
387updateStoreTime(HeimCredRef cred, CFMutableDictionaryRef attrs)
388{
389    CFDateRef date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
390    if (date == NULL)
391	return;
392    CFDictionarySetValue(attrs, kHEIMAttrStoreTime, date);
393    CFRelease(date);
394}
395
396static void
397handleDefaultCredentialUpdate(struct HeimSession *session, HeimCredRef cred, CFDictionaryRef attrs)
398{
399
400    heim_assert(cred->mech != NULL, "mech is NULL, schame validation doesn't work ?");
401
402    CFUUIDRef oldDefault = CFDictionaryGetValue(session->defaultCredentials, cred->mech->name);
403
404    CFBooleanRef defaultCredential = CFDictionaryGetValue(attrs, kHEIMAttrDefaultCredential);
405    if (defaultCredential == NULL || !CFBooleanGetValue(defaultCredential)) {
406	if (oldDefault)
407	    return;
408    } else {
409	if (oldDefault) {
410	    /*
411	     * Drop marker on old credential
412	     */
413	    HeimCredRef oldCred = (HeimCredRef)CFDictionaryGetValue(session->items, oldDefault);
414	    if (oldCred) {
415		CFMutableDictionaryRef oldCredAttrs = CFDictionaryCreateMutableCopy(NULL, 0, oldCred->attributes);
416		CFDictionaryRemoveValue(oldCredAttrs, kHEIMAttrDefaultCredential);
417		CFRELEASE_NULL(oldCred->attributes);
418		oldCred->attributes = oldCredAttrs;
419	    }
420	}
421    }
422
423    CFDictionarySetValue(session->defaultCredentials, cred->mech->name, cred->uuid);
424
425    notifyChangedCaches();
426}
427
428static void
429handleDefaultCredentialDeletion(struct HeimSession *session, HeimCredRef cred)
430{
431    CFUUIDRef defaultCredential = CFDictionaryGetValue(session->defaultCredentials, cred->mech->name);
432
433    if (defaultCredential && CFEqual(defaultCredential, cred->uuid))
434	CFDictionaryRemoveValue(session->defaultCredentials, cred->mech->name);
435    session->updateDefaultCredential = 1;
436}
437
438static void
439reElectCredential(const void *key, const void *value, void *context)
440{
441    HeimCredRef cred = (HeimCredRef)value;
442    struct HeimSession *session = (struct HeimSession *)context;
443
444    if (CFDictionaryGetValue(session->defaultCredentials, cred->mech->name))
445	return;
446    if (CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential) != NULL)
447	return;
448    CFDictionarySetValue(session->defaultCredentials, cred->mech->name, cred->uuid);
449}
450
451static void
452reElectMechCredential(const void *key, const void *value, void *context)
453{
454    struct HeimMech *mech = (struct HeimMech *)value;
455    struct HeimSession *session = (struct HeimSession *)context;
456
457    if (CFDictionaryGetValue(session->defaultCredentials, mech->name))
458	return;
459
460    CFDictionaryApplyFunction(session->items, reElectCredential, session);
461
462    if (CFDictionaryGetValue(session->defaultCredentials, mech->name)) {
463	HeimCredCTX.needFlush = 1;
464    } else {
465	/*
466	 * If there is no default credential, make one up
467	 */
468	CFUUIDRef defcred = CFUUIDCreate(NULL);
469	CFDictionarySetValue(session->defaultCredentials, mech->name, defcred);
470	CFRelease(defcred);
471    }
472
473    notifyChangedCaches();
474}
475
476static void
477reElectDefaultCredential(struct peer *peer)
478{
479    if (!peer->session->updateDefaultCredential)
480	return;
481    peer->session->updateDefaultCredential = 0;
482
483    CFDictionaryApplyFunction(HeimCredCTX.mechanisms, reElectMechCredential, peer->session);
484}
485
486#if TARGET_OS_EMBEDDED
487static bool
488canSetAnyBundleIDACL(struct peer *peer)
489{
490    return CFStringCompare(CFSTR("com.apple.accountsd"), peer->bundleID, 0) == kCFCompareEqualTo;
491}
492#endif
493
494static void
495do_CreateCred(struct peer *peer, xpc_object_t request, xpc_object_t reply)
496{
497    CFMutableDictionaryRef attrs = NULL;
498    HeimCredRef cred = NULL;
499    CFUUIDRef uuid = NULL;
500    bool hasACL = false;
501    CFErrorRef error = NULL;
502    CFBooleanRef lead;
503
504    CFDictionaryRef attributes = HeimCredMessageCopyAttributes(request, "attributes", CFDictionaryGetTypeID());
505    if (attributes == NULL)
506	goto out;
507
508    if (!validateObject(attributes, &error)) {
509	addErrorToReply(reply, error);
510	goto out;
511    }
512
513    /* check if we are ok to link into this cred-tree */
514    uuid = CFDictionaryGetValue(attributes, kHEIMAttrParentCredential);
515    if (uuid != NULL && !checkACLInCredentialChain(peer, uuid, &hasACL))
516	goto out;
517
518    uuid = CFDictionaryGetValue(attributes, kHEIMAttrUUID);
519    if (uuid) {
520	CFRetain(uuid);
521
522	if (CFGetTypeID(uuid) != CFUUIDGetTypeID())
523	    goto out;
524
525	if (CFDictionaryGetValue(peer->session->items, uuid) != NULL)
526	    goto out;
527
528    } else {
529	uuid = CFUUIDCreate(NULL);
530	if (uuid == NULL)
531	    goto out;
532    }
533    cred = HeimCredCreateItem(uuid);
534    if (cred == NULL)
535	goto out;
536
537    attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
538    if (attrs == NULL)
539	goto out;
540
541    bool hasACLInAttributes = addPeerToACL(peer, attrs);
542
543#if TARGET_OS_EMBEDDED
544    if (hasACLInAttributes && !canSetAnyBundleIDACL(peer)) {
545       CFArrayRef array = CFDictionaryGetValue(attrs, kHEIMAttrBundleIdentifierACL);
546       if (array == NULL || CFGetTypeID(array) != CFArrayGetTypeID() || CFArrayGetCount(array) != 1) {
547           syslog(LOG_ERR, "peer sent more then one bundle id and is not accountsd");
548           goto out;
549       }
550    }
551#endif
552
553    /*
554     * make sure this cred-tree as an ACL Set the ACL to be the peer
555     * on ios, and on osx, its "*" since that is the default policy.
556     *
557     * XXX should use the default keychain rule for appsandboxed peers
558     * on osx.
559     */
560    if (!hasACL && !hasACLInAttributes) {
561#if TARGET_OS_IPHONE
562	const void *values[1] = { (void *)peer->bundleID };
563#else
564	const void *values[1] = { CFSTR("*") };
565#endif
566	CFArrayRef array = CFArrayCreate(NULL, values, sizeof(values)/sizeof(values[0]), &kCFTypeArrayCallBacks);
567	heim_assert(array != NULL, "out of memory");
568	CFDictionarySetValue(attrs, kHEIMAttrBundleIdentifierACL, array);
569	CFRELEASE_NULL(array);
570    }
571
572    CFDictionarySetValue(attrs, kHEIMAttrUUID, uuid);
573
574    if (!validateObject(attrs, &error)) {
575	addErrorToReply(reply, error);
576	goto out;
577    }
578
579    if (!HeimCredAssignMech(cred, attrs)) {
580	goto out;
581    }
582
583    updateStoreTime(cred, attrs);
584    handleDefaultCredentialUpdate(peer->session, cred, attrs);
585
586    cred->attributes = CFRetain(attrs);
587
588    CFDictionarySetValue(peer->session->items, cred->uuid, cred);
589    notifyChangedCaches();
590    HeimCredCTX.needFlush = 1;
591
592    /*
593     * If the default credential is unset or doesn't exists, switch to
594     * the now just created lead credential.
595     */
596
597    heim_assert(cred->mech != NULL, "mech is NULL, schame validation doesn't work ?");
598
599    CFUUIDRef defcred = CFDictionaryGetValue(peer->session->defaultCredentials, cred->mech->name);
600    if ((defcred == NULL || CFDictionaryGetValue(peer->session->items, defcred) == NULL)
601	&& (lead = CFDictionaryGetValue(cred->attributes, kHEIMAttrLeadCredential))
602	&& CFBooleanGetValue(lead))
603    {
604	CFDictionarySetValue(peer->session->defaultCredentials, cred->mech->name, cred->uuid);
605    }
606
607    HeimCredMessageSetAttributes(reply, "attributes", cred->attributes);
608out:
609    CFRELEASE_NULL(attrs);
610    CFRELEASE_NULL(attributes);
611    CFRELEASE_NULL(cred);
612    CFRELEASE_NULL(uuid);
613    CFRELEASE_NULL(error);
614}
615
616struct MatchCTX {
617    struct peer *peer;
618    CFMutableArrayRef results;
619    CFDictionaryRef query;
620    CFIndex numQueryItems;
621    CFDictionaryRef attributes;
622    CFIndex count;
623};
624
625static void
626MatchQueryItem(const void *key, const void *value, void *context)
627{
628    struct MatchCTX * mc = (struct MatchCTX *)context;
629    heim_assert(mc->attributes != NULL, "attributes NULL in MatchQueryItem");
630
631    /*
632     * Exact matching for each rule, kCFNull matches when key is not set.
633     */
634
635    CFTypeRef val = CFDictionaryGetValue(mc->attributes, key);
636    if (val == NULL) {
637	if (!CFEqual(kCFNull, value))
638	    return;
639    } else if (!CFEqual(val, value))
640	return;
641    mc->count++;
642}
643
644static void
645MatchQueryCred(const void *key, const void *value, void *context)
646{
647    struct MatchCTX *mc = (struct MatchCTX *)context;
648    CFUUIDRef uuid = (CFUUIDRef)key;
649
650    if (!checkACLInCredentialChain(mc->peer, uuid, NULL))
651	return;
652
653    HeimCredRef cred = (HeimCredRef)value;
654    heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
655
656    if (cred->attributes == NULL)
657	return;
658    mc->attributes = cred->attributes;
659    mc->count = 0;
660    CFDictionaryApplyFunction(mc->query, MatchQueryItem, mc);
661    heim_assert(mc->numQueryItems >= mc->count, "cant have matched more then number of queries");
662
663    /* found a match */
664    if (mc->numQueryItems == mc->count)
665	CFArrayAppendValue(mc->results, cred);
666}
667
668static void
669MatchQuerySchema(const void *key, const void *value, void *context)
670{
671    struct MatchCTX *mc = (struct MatchCTX *)context;
672
673    CFDictionaryRef schema = (CFDictionaryRef)value;
674    heim_assert(CFGetTypeID(schema) == CFDictionaryGetTypeID(), "schema wrong type");
675
676    mc->attributes = schema;
677    mc->count = 0;
678    CFDictionaryApplyFunction(mc->query, MatchQueryItem, mc);
679    heim_assert(mc->numQueryItems >= mc->count, "cant have matched more then number of queries");
680
681    /* found a match */
682    if (mc->numQueryItems == mc->count)
683	CFArrayAppendValue(mc->results, schema);
684}
685
686
687static CFMutableArrayRef
688QueryCopy(struct peer *peer, xpc_object_t request, const char *key)
689{
690    struct MatchCTX mc = {
691	.peer = peer,
692	.results = NULL,
693	.query = NULL,
694	.attributes = NULL,
695    };
696    CFUUIDRef uuidref = NULL;
697    CFErrorRef error = NULL;
698
699    mc.query = HeimCredMessageCopyAttributes(request, key, CFDictionaryGetTypeID());
700    if (mc.query == NULL)
701	goto out;
702
703    mc.numQueryItems = CFDictionaryGetCount(mc.query);
704
705    if (mc.numQueryItems == 1) {
706	uuidref = CFDictionaryGetValue(mc.query, kHEIMAttrUUID);
707	if (uuidref && CFGetTypeID(uuidref) == CFUUIDGetTypeID()) {
708
709	    if (!checkACLInCredentialChain(peer, uuidref, NULL))
710		goto out;
711
712	    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(peer->session->items, uuidref);
713	    if (cred == NULL)
714		goto out;
715
716	    heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
717
718	    mc.results = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
719	    CFArrayAppendValue(mc.results, cred);
720
721	    goto out;
722	}
723    }
724
725    mc.results = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
726
727    CFStringRef type = GetValidatedValue(mc.query, kHEIMAttrType, CFStringGetTypeID(), &error);
728    if (CFEqual(type, kHEIMTypeSchema))
729	CFDictionaryApplyFunction(HeimCredCTX.schemas, MatchQuerySchema, &mc);
730    else
731	CFDictionaryApplyFunction(peer->session->items, MatchQueryCred, &mc);
732
733out:
734    CFRELEASE_NULL(error);
735    CFRELEASE_NULL(mc.query);
736    return mc.results;
737}
738
739struct child {
740    CFUUIDRef parent;
741    CFMutableArrayRef array;
742    struct HeimSession *session;
743};
744
745static void
746DeleteChild(const void *key, const void *value, void *context)
747{
748    struct child *child = (struct child  *)context;
749
750    if (CFEqual(key, child->parent))
751	return;
752
753    HeimCredRef cred = (HeimCredRef)value;
754    heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
755
756    CFUUIDRef parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
757    if (parent && CFEqual(child->parent, parent)) {
758	CFArrayAppendValue(child->array, cred->uuid);
759
760	handleDefaultCredentialDeletion(child->session, cred);
761
762	/*
763	 * Recurse over grandchildren too
764	 */
765	struct child grandchildren = {
766	    .parent = key,
767	    .array = child->array,
768	    .session = child->session,
769	};
770	CFDictionaryApplyFunction(child->session->items, DeleteChild, &grandchildren);
771    }
772}
773
774static void
775DeleteChildrenApplier(const void *value, void *context)
776{
777    struct HeimSession *session = (struct HeimSession *)context;
778    heim_assert(CFGetTypeID(value) == CFUUIDGetTypeID(), "Value not an CFUUIDRef");
779    CFDictionaryRemoveValue(session->items, value);
780}
781
782
783static void
784DeleteChildren(struct HeimSession *session, CFUUIDRef parent)
785{
786    /*
787     * delete all child entries for to UUID
788     */
789    struct child child = {
790	.parent = parent,
791	.array = NULL,
792	.session = session,
793    };
794    child.array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
795    CFDictionaryApplyFunction(session->items, DeleteChild, &child);
796    CFArrayApplyFunction(child.array, CFRangeMake(0, CFArrayGetCount(child.array)), DeleteChildrenApplier, session);
797    CFRelease(child.array);
798}
799
800struct fromto {
801    CFUUIDRef from;
802    CFUUIDRef to;
803};
804
805static void
806UpdateParent(const void *key, const void *value, void *context)
807{
808    struct fromto *fromto = (struct fromto *)context;
809    HeimCredRef cred = (HeimCredRef)value;
810    heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
811
812    CFUUIDRef parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
813    if (parent && CFEqual(parent, fromto->from)) {
814	CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, cred->attributes);
815	CFRelease(cred->attributes);
816	CFDictionarySetValue(attrs, kHEIMAttrParentCredential, fromto->to);
817	cred->attributes = attrs;
818    }
819}
820
821
822
823
824static void
825do_Delete(struct peer *peer, xpc_object_t request, xpc_object_t reply)
826{
827    CFErrorRef error = NULL;
828
829    CFArrayRef items = QueryCopy(peer, request, "query");
830    if (items == NULL || CFArrayGetCount(items) == 0) {
831	const void *const keys[] = { CFSTR("CommonErrorCode") };
832	const void *const values[] = { kCFBooleanTrue };
833	HCMakeError(&error, kHeimCredErrorNoItemsMatchesQuery, keys, values, 1);
834	goto out;
835    }
836
837    CFIndex n, count = CFArrayGetCount(items);
838    for (n = 0; n < count; n++) {
839	HeimCredRef cred = (HeimCredRef)CFArrayGetValueAtIndex(items, n);
840	heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
841
842	CFDictionaryRemoveValue(peer->session->items, cred->uuid);
843	DeleteChildren(peer->session, cred->uuid);
844    }
845
846    notifyChangedCaches();
847    HeimCredCTX.needFlush = 1;
848
849 out:
850    if (error) {
851	addErrorToReply(reply, error);
852	CFRelease(error);
853    }
854    CFRELEASE_NULL(items);
855}
856
857static void
858updateCred(const void *key, const void *value, void *context)
859{
860    CFMutableDictionaryRef attrs = (CFMutableDictionaryRef)context;
861    CFDictionarySetValue(attrs, key, value);
862}
863
864
865static void
866do_SetAttrs(struct peer *peer, xpc_object_t request, xpc_object_t reply)
867{
868    CFUUIDRef uuid = HeimCredCopyUUID(request, "uuid");
869    CFMutableDictionaryRef attrs;
870    CFErrorRef error = NULL;
871
872    if (uuid == NULL)
873	return;
874
875    if (!checkACLInCredentialChain(peer, uuid, NULL)) {
876	CFRelease(uuid);
877	return;
878    }
879
880    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(peer->session->items, uuid);
881    CFRelease(uuid);
882    if (cred == NULL)
883	return;
884
885    heim_assert(CFGetTypeID(cred) == HeimCredGetTypeID(), "cred wrong type");
886
887    if (cred->attributes) {
888	attrs = CFDictionaryCreateMutableCopy(NULL, 0, cred->attributes);
889	if (attrs == NULL)
890	    return;
891    } else {
892	attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
893    }
894
895    CFDictionaryRef replacementAttrs = HeimCredMessageCopyAttributes(request, "attributes", CFDictionaryGetTypeID());
896    if (replacementAttrs == NULL) {
897	CFRelease(attrs);
898	goto out;
899    }
900
901    CFDictionaryApplyFunction(replacementAttrs, updateCred, attrs);
902    CFRELEASE_NULL(replacementAttrs);
903
904    if (!validateObject(attrs, &error)) {
905	addErrorToReply(reply, error);
906	goto out;
907    }
908
909    handleDefaultCredentialUpdate(peer->session, cred, attrs);
910
911    /* make sure the current caller is on the ACL list */
912    addPeerToACL(peer, attrs);
913
914    CFRELEASE_NULL(cred->attributes);
915    cred->attributes = attrs;
916  out:
917    CFRELEASE_NULL(error);
918}
919
920static void
921do_Fetch(struct peer *peer, xpc_object_t request, xpc_object_t reply)
922{
923    CFUUIDRef uuid = HeimCredCopyUUID(request, "uuid");
924    if (uuid == NULL)
925	return;
926
927    if (!checkACLInCredentialChain(peer, uuid, NULL)) {
928	CFRelease(uuid);
929	return;
930    }
931
932    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(peer->session->items, uuid);
933    CFRelease(uuid);
934    if (cred == NULL)
935	return;
936
937    /* XXX filter the attributes */
938
939    HeimCredMessageSetAttributes(reply, "attributes", cred->attributes);
940}
941
942static CFComparisonResult
943orderLeadFirst(const void *val1, const void *val2, void *context)
944{
945    HeimCredRef cred1 = (HeimCredRef)val1;
946    HeimCredRef cred2 = (HeimCredRef)val2;
947
948    CFTypeRef uuid1 = CFDictionaryGetValue(cred1->attributes, kHEIMAttrParentCredential);
949    CFTypeRef uuid2 = CFDictionaryGetValue(cred2->attributes, kHEIMAttrParentCredential);
950
951    if (uuid1 && uuid2 && CFEqual(uuid1, uuid2)) {
952	CFBooleanRef lead1 = CFDictionaryGetValue(cred1->attributes, kHEIMAttrLeadCredential);
953	CFBooleanRef lead2 = CFDictionaryGetValue(cred2->attributes, kHEIMAttrLeadCredential);
954
955	if (lead1 && CFBooleanGetValue(lead1))
956	    return kCFCompareLessThan;
957	if (lead2 && CFBooleanGetValue(lead2))
958	    return kCFCompareGreaterThan;
959    }
960
961    CFErrorRef error = NULL;
962
963    CFDateRef authTime1 = GetValidatedValue(cred1->attributes, kHEIMAttrStoreTime, CFDateGetTypeID(), &error);
964    CFRELEASE_NULL(error);
965    CFDateRef authTime2 = GetValidatedValue(cred2->attributes, kHEIMAttrStoreTime, CFDateGetTypeID(), &error);
966    CFRELEASE_NULL(error);
967
968    if (authTime1 && authTime2)
969	return CFDateCompare(authTime1, authTime2, NULL);
970
971    CFIndex hash1 = CFHash(cred1->uuid);
972    CFIndex hash2 = CFHash(cred2->uuid);
973
974    if (hash1 < hash2)
975	return kCFCompareLessThan;
976
977    return kCFCompareGreaterThan;
978}
979
980
981static void
982do_Query(struct peer *peer, xpc_object_t request, xpc_object_t reply)
983{
984    CFMutableArrayRef array = NULL;
985    CFErrorRef error = NULL;
986
987    reElectDefaultCredential(peer);
988
989    CFMutableArrayRef items = QueryCopy(peer, request, "query");
990    if (items == NULL) {
991	const void *const keys[] = { CFSTR("CommonErrorCode") };
992	const void *const values[] = { kCFBooleanTrue };
993	HCMakeError(&error, kHeimCredErrorNoItemsMatchesQuery, keys, values, 1);
994	goto out;
995    }
996
997    array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
998    if (array == NULL) {
999	goto out;
1000    }
1001
1002    CFIndex n, count = CFArrayGetCount(items);
1003
1004    if (count > 1)
1005	CFArraySortValues(items, CFRangeMake(0, count), orderLeadFirst, NULL);
1006
1007    for (n = 0; n < count; n++) {
1008	HeimCredRef cred = (HeimCredRef)CFArrayGetValueAtIndex(items, n);
1009	CFArrayAppendValue(array, cred->uuid);
1010    }
1011    CFRELEASE_NULL(items);
1012
1013    HeimCredMessageSetAttributes(reply, "items", array);
1014
1015    out:
1016    if (error) {
1017	addErrorToReply(reply, error);
1018	CFRelease(error);
1019    }
1020    CFRELEASE_NULL(array);
1021    CFRELEASE_NULL(items);
1022}
1023
1024static void
1025do_GetDefault(struct peer *peer, xpc_object_t request, xpc_object_t reply)
1026{
1027    CFErrorRef error = NULL;
1028
1029    reElectDefaultCredential(peer);
1030
1031    CFStringRef mechName = HeimCredMessageCopyAttributes(request, "mech", CFStringGetTypeID());
1032    if (mechName == NULL) {
1033	HCMakeError(&error, kHeimCredErrorNoItemsMatchesQuery, NULL, NULL, 0);
1034	goto out;
1035    }
1036
1037    CFUUIDRef defCred = CFDictionaryGetValue(peer->session->defaultCredentials, mechName);
1038    if (defCred == NULL) {
1039	/*
1040	 * If there is is no credential, try to elect one
1041	 */
1042	peer->session->updateDefaultCredential = 1;
1043	reElectDefaultCredential(peer);
1044
1045	defCred = CFDictionaryGetValue(peer->session->defaultCredentials, mechName);
1046	if (defCred == NULL) {
1047	    HCMakeError(&error, kHeimCredErrorNoItemsMatchesQuery, NULL, NULL, 0);
1048	    goto out;
1049	}
1050    }
1051
1052    HeimCredMessageSetAttributes(reply, "default", defCred);
1053 out:
1054     if (error) {
1055	 addErrorToReply(reply, error);
1056	 CFRelease(error);
1057     }
1058}
1059
1060static void
1061do_Move(struct peer *peer, xpc_object_t request, xpc_object_t reply)
1062{
1063    CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
1064    CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
1065
1066    if (from == NULL || to == NULL) {
1067	CFRELEASE_NULL(from);
1068	CFRELEASE_NULL(to);
1069	return;
1070    }
1071
1072    if (!checkACLInCredentialChain(peer, from, NULL) || !checkACLInCredentialChain(peer, to, NULL)) {
1073	CFRelease(from);
1074	CFRelease(to);
1075	return;
1076    }
1077
1078    HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
1079    HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
1080
1081    if (credfrom == NULL) {
1082	CFRelease(from);
1083	CFRelease(to);
1084	return;
1085    }
1086
1087
1088    CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
1089    CFDictionaryRemoveValue(peer->session->items, from);
1090    credfrom = NULL;
1091
1092    CFDictionarySetValue(newattrs, kHEIMAttrUUID, to);
1093
1094    if (credto == NULL) {
1095	credto = HeimCredCreateItem(to);
1096	heim_assert(credto != NULL, "out of memory");
1097
1098	HeimCredAssignMech(credto, newattrs);
1099
1100	credto->attributes = newattrs;
1101	CFDictionarySetValue(peer->session->items, credto->uuid, credto);
1102	CFRelease(credto);
1103
1104    } else {
1105	CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
1106	if (parentUUID)
1107	    CFDictionarySetValue(newattrs, kHEIMAttrParentCredential, parentUUID);
1108	CFRELEASE_NULL(credto->attributes);
1109	credto->attributes = newattrs;
1110    }
1111
1112    /*
1113     * delete all child entries for to UUID
1114     */
1115    DeleteChildren(peer->session, to);
1116
1117    /*
1118     * update all child entries for from UUID
1119     */
1120    struct fromto fromto = {
1121	.from = from,
1122	.to = to,
1123    };
1124    CFDictionaryApplyFunction(peer->session->items, UpdateParent, &fromto);
1125
1126    notifyChangedCaches();
1127    HeimCredCTX.needFlush = 1;
1128}
1129
1130static void
1131StatusCredential(const void *key, const void *value, void *context)
1132{
1133    HeimCredRef cred = (HeimCredRef)value;
1134    xpc_object_t sc = (xpc_object_t)context;
1135    char *s;
1136
1137    if (CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential) != NULL)
1138	return;
1139
1140    xpc_object_t xc = xpc_dictionary_create(NULL, NULL, 0);
1141
1142    CFStringRef us = CFUUIDCreateString(NULL, cred->uuid);
1143    s = rk_cfstring2cstring(us);
1144    CFRelease(us);
1145
1146    xpc_dictionary_set_value(sc, s, xc);
1147    xpc_release(xc);
1148    free(s);
1149
1150    CFStringRef name = CFDictionaryGetValue(cred->attributes, kHEIMAttrClientName);
1151    if (name) {
1152	s = rk_cfstring2cstring(name);
1153	xpc_dictionary_set_string(xc, "name", s);
1154	free(s);
1155    }
1156
1157    s = rk_cfstring2cstring(cred->mech->name);
1158    xpc_dictionary_set_string(xc, "mech", s);
1159    free(s);
1160}
1161
1162static void
1163StatusSession(const void *key, const void *value, void *context)
1164{
1165    struct HeimSession *session = (struct HeimSession *)value;
1166    xpc_object_t ss = (xpc_object_t)context;
1167    xpc_object_t sc = xpc_dictionary_create(NULL, NULL, 0);
1168    CFDictionaryApplyFunction(session->items, StatusCredential, sc);
1169    CFStringRef sessionID = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), key);
1170    char *s = rk_cfstring2cstring(sessionID);
1171    xpc_dictionary_set_value(ss, s, sc);
1172    free(s);
1173    xpc_release(sc);
1174    CFRelease(sessionID);
1175}
1176
1177static void
1178do_Status(struct peer *peer, xpc_object_t request, xpc_object_t reply)
1179{
1180    uid_t uid = xpc_connection_get_euid(peer->peer);
1181    struct stat sb;
1182
1183    if (uid == 0) {
1184	xpc_object_t ss = xpc_dictionary_create(NULL, NULL, 0);
1185
1186	CFDictionaryApplyFunction(HeimCredCTX.sessions, StatusSession, ss);
1187	xpc_dictionary_set_value(reply, "items", ss);
1188	xpc_release(ss);
1189    }
1190
1191    if (stat([archivePath UTF8String], &sb) == 0) {
1192	xpc_dictionary_set_int64(reply, "cache-size", (int64_t)sb.st_size);
1193    } else {
1194	xpc_dictionary_set_int64(reply, "cache-size", 0);
1195    }
1196}
1197
1198static void GSSCred_peer_event_handler(struct peer *peer, xpc_object_t event)
1199{
1200    xpc_object_t reply  = NULL;
1201    xpc_type_t type = xpc_get_type(event);
1202    if (type == XPC_TYPE_ERROR)
1203	return;
1204
1205    assert(type == XPC_TYPE_DICTIONARY);
1206
1207    const char *cmd = xpc_dictionary_get_string(event, "command");
1208    if (cmd == NULL) {
1209	syslog(LOG_ERR, "peer sent invalid no command");
1210	xpc_connection_cancel(peer->peer);
1211    } else if (strcmp(cmd, "wakeup") == 0) {
1212
1213    } else if (strcmp(cmd, "create") == 0) {
1214	reply = xpc_dictionary_create_reply(event);
1215	do_CreateCred(peer, event, reply);
1216    } else if (strcmp(cmd, "delete") == 0) {
1217	reply = xpc_dictionary_create_reply(event);
1218	do_Delete(peer, event, reply);
1219    } else if (strcmp(cmd, "setattributes") == 0) {
1220	reply = xpc_dictionary_create_reply(event);
1221	do_SetAttrs(peer, event, reply);
1222    } else if (strcmp(cmd, "fetch") == 0) {
1223	reply = xpc_dictionary_create_reply(event);
1224	do_Fetch(peer, event, reply);
1225    } else if (strcmp(cmd, "move") == 0) {
1226	reply = xpc_dictionary_create_reply(event);
1227	do_Move(peer, event, reply);
1228    } else if (strcmp(cmd, "query") == 0) {
1229	reply = xpc_dictionary_create_reply(event);
1230	do_Query(peer, event, reply);
1231    } else if (strcmp(cmd, "default") == 0) {
1232	reply = xpc_dictionary_create_reply(event);
1233	do_GetDefault(peer, event, reply);
1234    } else if (strcmp(cmd, "retain-transient") == 0) {
1235	reply = xpc_dictionary_create_reply(event);
1236    } else if (strcmp(cmd, "release-transient") == 0) {
1237	reply = xpc_dictionary_create_reply(event);
1238    } else if (strcmp(cmd, "status") == 0) {
1239	reply = xpc_dictionary_create_reply(event);
1240	do_Status(peer, event, reply);
1241    } else {
1242	syslog(LOG_ERR, "peer sent invalid command %s", cmd);
1243	xpc_connection_cancel(peer->peer);
1244    }
1245
1246    if (reply) {
1247	xpc_connection_send_message(peer->peer, reply);
1248	xpc_release(reply);
1249    }
1250
1251    if (HeimCredCTX.needFlush) {
1252	if (!HeimCredCTX.flushPending) {
1253	    HeimCredCTX.needFlush = false;
1254	} else {
1255	    HeimCredCTX.flushPending = true;
1256
1257	    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC), runQueue, ^{
1258	        @autoreleasepool {
1259		    HeimCredCTX.flushPending = false;
1260		    storeCredCache();
1261		}
1262	    });
1263	}
1264    }
1265}
1266
1267static void
1268peer_final(void *ptr)
1269{
1270    struct peer *peer = ptr;
1271    CFRELEASE_NULL(peer->bundleID);
1272    CFRELEASE_NULL(peer->session);
1273}
1274
1275static CFStringRef
1276CopySigningIdentitier(xpc_connection_t conn)
1277{
1278#if TARGET_IPHONE_SIMULATOR
1279	char path[MAXPATHLEN];
1280	CFStringRef ident = NULL;
1281	const char *str = NULL;
1282
1283	/* simulator binaries are not codesigned, fake it */
1284	if (proc_pidpath(getpid(), path, sizeof(path)) > 0) {
1285		xpc_bundle_t bundle = xpc_bundle_create(path, XPC_BUNDLE_FROM_PATH);
1286		if (bundle) {
1287			xpc_object_t xdict = xpc_bundle_get_info_dictionary(bundle);
1288			if (xdict) {
1289				str = xpc_dictionary_get_string(xdict, "CFBundleIdentifier");
1290				if (str)
1291					ident = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), str);
1292			}
1293			xpc_release(bundle);
1294		}
1295		/*
1296		 * If not a bundle, its a command line tool, lets use com.apple.$(basename)
1297		 */
1298		if (ident == NULL) {
1299			str = strrchr(path, '/');
1300			if (str) {
1301				str++;
1302				ident = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.%s"), str);
1303			}
1304		}
1305	}
1306	if (ident == NULL)
1307		ident = CFSTR("iphonesimulator");
1308	return ident;
1309#else
1310	CFStringRef res;
1311	uint8_t header[8] = { 0 };
1312	uint32_t len;
1313	audit_token_t audit_token;
1314	pid_t pid;
1315
1316	pid = xpc_connection_get_pid(conn);
1317	xpc_connection_get_audit_token(conn, &audit_token);
1318
1319	int rcent = csops_audittoken(pid, CS_OPS_IDENTITY, header, sizeof(header), &audit_token);
1320	if (rcent != -1 || errno != ERANGE)
1321	    return NULL;
1322
1323	memcpy(&len, &header[4], 4);
1324	len = ntohl(len);
1325	if (len > 1024 * 1024)
1326	    return NULL;
1327	else if (len == 0)
1328	    return NULL;
1329
1330	uint8_t *buffer = malloc(len);
1331	if (buffer == NULL)
1332	    return NULL;
1333
1334	rcent = csops_audittoken(pid, CS_OPS_IDENTITY, buffer, len, &audit_token);
1335	if (rcent != 0) {
1336	    free(buffer);
1337	    return NULL;
1338	}
1339
1340	char *p = (char *)buffer;
1341	if (len > 8) {
1342	    p += 8;
1343	    len -= 8;
1344	} else
1345	    return NULL;
1346
1347	if (p[len - 1] != '\0') {
1348	    free(buffer);
1349	    return NULL;
1350	}
1351
1352	res = CFStringCreateWithBytes(NULL, (UInt8 *)p, len - 1, kCFStringEncodingUTF8, false);
1353	free(buffer);
1354	return res;
1355#endif
1356}
1357
1358/*
1359 *
1360 */
1361
1362static struct HeimSession *
1363HeimCredCopySession(int sessionID)
1364{
1365    struct HeimSession *session;
1366    CFNumberRef sid;
1367
1368    sid = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
1369    heim_assert(sid != NULL, "out of memory");
1370
1371
1372    session = (struct HeimSession *)CFDictionaryGetValue(HeimCredCTX.sessions, sid);
1373    if (session) {
1374	CFRelease(sid);
1375	CFRetain(session);
1376	return session;
1377    }
1378
1379    CFTypeID sessionTID = getSessionTypeID();
1380
1381    heim_assert(sessionTID != _kCFRuntimeNotATypeID, "could not register cftype");
1382
1383    session = (struct HeimSession *)_CFRuntimeCreateInstance(NULL, sessionTID, sizeof(struct HeimSession) - sizeof(CFRuntimeBase), NULL);
1384    heim_assert(session != NULL, "out of memory while registering HeimMech instance");
1385
1386    session->session = sessionID;
1387    session->items = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1388    session->defaultCredentials = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1389    session->updateDefaultCredential = 0;
1390
1391    CFDictionarySetValue(HeimCredCTX.sessions, sid, session);
1392    CFRelease(sid);
1393
1394    return session;
1395}
1396
1397/*
1398 *
1399 */
1400
1401static void GSSCred_event_handler(xpc_connection_t peerconn)
1402{
1403    struct peer *peer;
1404
1405    peer = malloc(sizeof(*peer));
1406    heim_assert(peer != NULL, "out of memory");
1407
1408    peer->peer = peerconn;
1409    peer->bundleID = CopySigningIdentitier(peerconn);
1410    if (peer->bundleID == NULL) {
1411	char path[MAXPATHLEN];
1412
1413	if (proc_pidpath(getpid(), path, sizeof(path)) <= 0)
1414	    path[0] = '\0';
1415
1416	syslog(LOG_ERR, "client[pid-%d] \"%s\" is not signed", (int)xpc_connection_get_pid(peerconn), path);
1417#if TARGET_OS_EMBEDDED
1418	free(peer);
1419	xpc_connection_cancel(peerconn);
1420	return;
1421#else
1422	peer->bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("unsigned-binary-path:%s-end"), path);
1423	heim_assert(peer->bundleID != NULL, "out of memory");
1424#endif
1425    }
1426    peer->session = HeimCredCopySession(xpc_connection_get_asid(peerconn));
1427    heim_assert(peer->session != NULL, "out of memory");
1428
1429    xpc_connection_set_context(peerconn, peer);
1430    xpc_connection_set_finalizer_f(peerconn, peer_final);
1431
1432    xpc_connection_set_event_handler(peerconn, ^(xpc_object_t event) {
1433	GSSCred_peer_event_handler(peer, event);
1434    });
1435    xpc_connection_resume(peerconn);
1436}
1437
1438static void
1439addErrorToReply(xpc_object_t reply, CFErrorRef error)
1440{
1441    if (error == NULL)
1442	return;
1443
1444    xpc_object_t xe = xpc_dictionary_create(NULL, NULL, 0);
1445    xpc_dictionary_set_int64(xe, "error-code", CFErrorGetCode(error));
1446    xpc_dictionary_set_value(reply, "error", xe);
1447    xpc_release(xe);
1448}
1449
1450/*
1451 *
1452 */
1453
1454static CFStringRef
1455debug_session(CFTypeRef cf)
1456{
1457    return CFSTR("debugsession");
1458}
1459
1460static void
1461release_session(CFTypeRef cf)
1462{
1463    struct HeimSession *session = (struct HeimSession *)cf;
1464    CFRELEASE_NULL(session->items);
1465    CFRELEASE_NULL(session->defaultCredentials);
1466}
1467
1468static CFTypeID
1469getSessionTypeID(void)
1470{
1471    static CFTypeID haid = _kCFRuntimeNotATypeID;
1472    static dispatch_once_t inited;
1473
1474    dispatch_once(&inited, ^{
1475	    static const CFRuntimeClass HeimCredSessionClass = {
1476		0,
1477		"HeimCredSession",
1478		NULL,
1479		NULL,
1480		release_session,
1481		NULL,
1482		NULL,
1483		NULL,
1484		debug_session
1485	    };
1486	    haid = _CFRuntimeRegisterClass(&HeimCredSessionClass);
1487	});
1488    return haid;
1489}
1490
1491/*
1492 *
1493 */
1494
1495static CFStringRef
1496debug_mech(CFTypeRef cf)
1497{
1498    return CFSTR("debugmech");
1499}
1500
1501static void
1502release_mech(CFTypeRef cf)
1503{
1504    struct HeimMech *mech = (struct HeimMech *)cf;
1505    CFRELEASE_NULL(mech->name);
1506}
1507
1508static CFTypeID
1509getMechTypeID(void)
1510{
1511    static CFTypeID haid = _kCFRuntimeNotATypeID;
1512    static dispatch_once_t inited;
1513
1514    dispatch_once(&inited, ^{
1515	    static const CFRuntimeClass HeimCredMechanismClass = {
1516		0,
1517		"HeimCredMechanism",
1518		NULL,
1519		NULL,
1520		release_mech,
1521		NULL,
1522		NULL,
1523		NULL,
1524		debug_mech
1525	    };
1526	    haid = _CFRuntimeRegisterClass(&HeimCredMechanismClass);
1527	});
1528    return haid;
1529}
1530
1531/*
1532 *
1533 */
1534
1535static CFTypeRef
1536GetValidatedValue(CFDictionaryRef object, CFStringRef key, CFTypeID requiredTypeID, CFErrorRef *error)
1537{
1538    heim_assert(error != NULL, "error ptr required");
1539
1540    CFTypeRef value = CFDictionaryGetValue(object, key);
1541    if (value == NULL)
1542	return NULL;
1543    if (CFGetTypeID(value) != requiredTypeID)
1544	return NULL;
1545    return value;
1546}
1547
1548struct validate {
1549    CFDictionaryRef schema;
1550    CFDictionaryRef object;
1551    CFTypeID subTypeID;
1552    CFErrorRef *error;
1553    bool valid;
1554};
1555
1556#define kHeimCredErrorDomain CFSTR("com.apple.GSS.credential-store")
1557
1558static void
1559HCMakeError(CFErrorRef *error, CFIndex code,
1560	    const void *const *keys,
1561	    const void *const *values,
1562	    CFIndex num)
1563{
1564    if (*error != NULL)
1565	return;
1566    *error = CFErrorCreateWithUserInfoKeysAndValues(NULL, kHeimCredErrorDomain, code, keys, values, num);
1567}
1568
1569static void
1570ValidateKey(const void *key, const void *value, void *context)
1571{
1572    struct validate *ctx = (struct validate *)context;
1573    CFStringRef rule;
1574
1575    rule = GetValidatedValue(ctx->schema, key, CFStringGetTypeID(), ctx->error);
1576    if (rule == NULL) {
1577	const void *const keys[] = { CFSTR("Key"), CFSTR("CommonErrorCode") };
1578	const void *const values[] = { key, kCFBooleanTrue };
1579	HCMakeError(ctx->error, kHeimCredErrorUnknownKey, keys, values, 2);
1580	ctx->valid = false;
1581	syslog(LOG_ERR, "unknown key");
1582	return;
1583    }
1584
1585    return;
1586}
1587
1588static bool
1589StringContains(CFStringRef haystack, CFStringRef needle)
1590{
1591    if (CFStringFind(haystack, needle, 0).location == kCFNotFound)
1592	return false;
1593    return true;
1594}
1595
1596static CFTypeID
1597GetTypeFromSchemaRule(CFStringRef key, CFStringRef rule, bool typeLevelType)
1598{
1599    if (typeLevelType && StringContains(rule, CFSTR("a")))
1600	return CFArrayGetTypeID();
1601    else if (StringContains(rule, CFSTR("s")))
1602	return CFStringGetTypeID();
1603    else if (StringContains(rule, CFSTR("u")))
1604	return CFUUIDGetTypeID();
1605    else if (StringContains(rule, CFSTR("d")))
1606	return CFDataGetTypeID();
1607    else if (StringContains(rule, CFSTR("b")))
1608	return CFBooleanGetTypeID();
1609    else if (StringContains(rule, CFSTR("t")))
1610	return CFDateGetTypeID();
1611    else if (StringContains(rule, CFSTR("n")))
1612	return CFNumberGetTypeID();
1613
1614    heim_abort("key %s have a broken rule %s", rk_cfstring2cstring(rule), rk_cfstring2cstring(key));
1615}
1616
1617static void
1618ValidateSubtype(const void *value, void *context)
1619{
1620    struct validate *ctx = (struct validate *)context;
1621    if (CFGetTypeID(value) != ctx->subTypeID) {
1622	const void *const keys[] = { CFSTR("CommonErrorCode") };
1623	const void *const values[] = { kCFBooleanTrue };
1624	HCMakeError(ctx->error, kHeimCredErrorUnknownKey, keys, values, 1);
1625	ctx->valid = false;
1626    }
1627}
1628
1629static void
1630ValidateSchema(const void *key, const void *value, void *context)
1631{
1632    struct validate *ctx = (struct validate *)context;
1633    CFStringRef rule = value;
1634    CFTypeRef ov;
1635
1636    if (CFEqual(kHEIMObjectType, key))
1637	return;
1638
1639    ov = CFDictionaryGetValue(ctx->object, key);
1640    if (ov == NULL) {
1641	if (StringContains(rule, CFSTR("R"))) {
1642	    const void *const keys[] = { CFSTR("Key"), CFSTR("Rule"), CFSTR("CommonErrorCode") };
1643	    const void *const values[] = { key, rule, kCFBooleanTrue };
1644	    HCMakeError(ctx->error, kHeimCredErrorMissingSchemaKey, keys, values, 3);
1645	    ctx->valid = false;
1646	    syslog(LOG_ERR, "key missing key");
1647	}
1648    } else {
1649	CFTypeID expectedType = GetTypeFromSchemaRule(key, rule, true);
1650
1651	if (expectedType != CFGetTypeID(ov)) {
1652	    const void *const keys[] = { CFSTR("Key"), CFSTR("Rule"), CFSTR("CommonErrorCode") };
1653	    const void *const values[] = { key, rule, kCFBooleanTrue };
1654	    HCMakeError(ctx->error, kHeimCredErrorMissingSchemaKey, keys, values, 3);
1655	    ctx->valid = false;
1656	    syslog(LOG_ERR, "key have wrong type key");
1657	}
1658	if (expectedType == CFArrayGetTypeID()) {
1659	    ctx->subTypeID = GetTypeFromSchemaRule(key, rule, false);
1660	    CFArrayApplyFunction(ov, CFRangeMake(0, CFArrayGetCount(ov)), ValidateSubtype, ctx);
1661	}
1662    }
1663}
1664
1665static bool
1666validateObject(CFDictionaryRef object, CFErrorRef *error)
1667{
1668    heim_assert(error != NULL, "why you bother validating if you wont report the error to the user");
1669
1670    struct validate ctx = {
1671	.object = object,
1672	.valid = true,
1673	.error = error
1674    };
1675
1676    CFStringRef type = GetValidatedValue(object, kHEIMObjectType, CFStringGetTypeID(), error);
1677    if (type == NULL) {
1678	const void *const keys[] = { CFSTR("CommonErrorCode") };
1679	const void *const values[] = { kCFBooleanTrue };
1680	HCMakeError(error, kHeimCredErrorMissingSchemaKey, keys, values, 1);
1681	return false;
1682    }
1683
1684    ctx.schema = GetValidatedValue(HeimCredCTX.schemas, type, CFDictionaryGetTypeID(), error);
1685    if (ctx.schema == NULL) {
1686	const void *const keys[] = { CFSTR("CommonErrorCode") };
1687	const void *const values[] = { kCFBooleanTrue };
1688	HCMakeError(error, kHeimCredErrorNoSuchSchema, keys, values, 1);
1689	return false;
1690    }
1691
1692    /* XXX validate with schema to see that all required attributes are applied */
1693
1694    CFDictionaryApplyFunction(object, ValidateKey, &ctx);
1695    CFDictionaryApplyFunction(ctx.schema, ValidateSchema, &ctx);
1696
1697    return ctx.valid;
1698}
1699
1700static void
1701ValidateSchemaAtRegistration(const void *key, const void *value, void *context)
1702{
1703    if (CFEqual(kHEIMObjectType, key))
1704	return;
1705
1706    CFTypeID schemaID = GetTypeFromSchemaRule(key, value, true); /* will abort if rule is broken */
1707
1708    CFStringRef globalValue = CFDictionaryGetValue(HeimCredCTX.globalSchema, key);
1709    if (globalValue == NULL) {
1710	CFDictionarySetValue(HeimCredCTX.globalSchema, key, value);
1711    } else {
1712	CFTypeID globalID = GetTypeFromSchemaRule(key, globalValue, true);
1713	if (globalID != schemaID) {
1714	    heim_abort("two schemas have different type for the same key %d != %d (%s)", (int)globalID, (int)schemaID, rk_cfstring2cstring(key));
1715	}
1716    }
1717}
1718
1719static void
1720registerSchema(const void *value, void *context)
1721{
1722    CFDictionaryRef schema = (CFDictionaryRef)value;
1723    CFStringRef typeName = CFDictionaryGetValue(schema, kHEIMObjectType);
1724    CFTypeRef other;
1725
1726    heim_assert(typeName != NULL, "schema w/o kHEIMObjectType ?");
1727
1728    other = CFDictionaryGetValue(HeimCredCTX.schemas, typeName);
1729    heim_assert(other == NULL, "schema already registered");
1730
1731    CFDictionaryApplyFunction(schema, ValidateSchemaAtRegistration, NULL);
1732
1733    CFDictionarySetValue(HeimCredCTX.schemas, typeName, schema);
1734}
1735
1736void
1737_HeimCredRegisterMech(CFStringRef name,
1738		      CFSetRef schemas,
1739		      HeimCredStatusCallback statusCallback,
1740		      HeimCredAuthCallback authCallback)
1741{
1742    struct HeimMech *mech;
1743
1744    mech = (struct HeimMech *)CFDictionaryGetValue(HeimCredCTX.mechanisms, name);
1745    heim_assert(mech == NULL, "mech already registered");
1746
1747    CFTypeID mechID = getMechTypeID();
1748
1749    heim_assert(mechID != _kCFRuntimeNotATypeID, "could not register cftype");
1750
1751    mech = (struct HeimMech *)_CFRuntimeCreateInstance(NULL, mechID, sizeof(struct HeimMech) - sizeof(CFRuntimeBase), NULL);
1752    heim_assert(mech != NULL, "out of memory while registering HeimMech instance");
1753
1754    mech->name = CFRetain(name);
1755    mech->statusCallback = statusCallback;
1756    mech->authCallback = authCallback;
1757
1758    CFDictionarySetValue(HeimCredCTX.mechanisms, name, mech);
1759
1760    CFSetApplyFunction(schemas, registerSchema, NULL);
1761}
1762
1763/*
1764 * schema rules:
1765 * R  - required
1766 * G  - generate
1767 * s  - string
1768 * ax - array of x
1769 * u  - uuid
1770 * d  - data
1771 * b  - boolean
1772 * t  - time/date
1773 *
1774 */
1775
1776
1777CFMutableDictionaryRef
1778_HeimCredCreateBaseSchema(CFStringRef objectType)
1779{
1780    CFMutableDictionaryRef schema = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1781
1782    CFDictionarySetValue(schema, kHEIMAttrType, kHEIMTypeSchema);
1783    CFDictionarySetValue(schema, kHEIMObjectType, objectType);
1784    CFDictionarySetValue(schema, kHEIMAttrType, CFSTR("Rs"));
1785    CFDictionarySetValue(schema, kHEIMAttrClientName, CFSTR("s"));
1786    CFDictionarySetValue(schema, kHEIMAttrServerName, CFSTR("s"));
1787    CFDictionarySetValue(schema, kHEIMAttrUUID, CFSTR("Gu"));
1788    CFDictionarySetValue(schema, kHEIMAttrDisplayName, CFSTR("s"));
1789    CFDictionarySetValue(schema, kHEIMAttrCredential, CFSTR("b"));
1790    CFDictionarySetValue(schema, kHEIMAttrLeadCredential, CFSTR("b"));
1791    CFDictionarySetValue(schema, kHEIMAttrParentCredential, CFSTR("u"));
1792    CFDictionarySetValue(schema, kHEIMAttrBundleIdentifierACL, CFSTR("as"));
1793    CFDictionarySetValue(schema, kHEIMAttrDefaultCredential, CFSTR("b"));
1794    CFDictionarySetValue(schema, kHEIMAttrAuthTime, CFSTR("t"));
1795    CFDictionarySetValue(schema, kHEIMAttrStoreTime, CFSTR("Gt"));
1796    CFDictionarySetValue(schema, kHEIMAttrData, CFSTR("d"));
1797    CFDictionarySetValue(schema, kHEIMAttrRetainStatus, CFSTR("n"));
1798
1799#if 0
1800    CFDictionarySetValue(schema, kHEIMAttrTransient, CFSTR("b"));
1801    CFDictionarySetValue(schema, kHEIMAttrAllowedDomain, CFSTR("as"));
1802    CFDictionarySetValue(schema, kHEIMAttrStatus, CFSTR(""));
1803    CFDictionarySetValue(schema, kHEIMAttrExpire, CFSTR("t"));
1804    CFDictionarySetValue(schema, kHEIMAttrRenewTill, CFSTR("t"));
1805#endif
1806
1807    return schema;
1808}
1809
1810static void
1811_HeimCredRegisterGeneric(void)
1812{
1813    CFMutableSetRef set;
1814    CFMutableDictionaryRef schema;
1815
1816    set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1817    schema = _HeimCredCreateBaseSchema(kHEIMObjectGeneric);
1818
1819    CFSetAddValue(set, schema);
1820    CFRelease(schema);
1821    _HeimCredRegisterMech(kHEIMTypeGeneric, set, NULL, NULL);
1822    CFRelease(set);
1823}
1824
1825static void
1826_HeimCredRegisterConfiguration(void)
1827{
1828    CFMutableSetRef set;
1829    CFMutableDictionaryRef schema;
1830
1831    set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1832    schema = _HeimCredCreateBaseSchema(kHEIMObjectConfiguration);
1833
1834    CFSetAddValue(set, schema);
1835    CFRelease(schema);
1836    _HeimCredRegisterMech(kHEIMTypeConfiguration, set, NULL, NULL);
1837    CFRelease(set);
1838}
1839
1840#if !TARGET_OS_IPHONE
1841/*
1842 *
1843 */
1844
1845static void GSSCred_session_handler(xpc_connection_t peerconn)
1846{
1847    audit_token_t token;
1848    uid_t uid;
1849
1850    xpc_connection_get_audit_token(peerconn, &token);
1851    uid = xpc_connection_get_euid(peerconn);
1852
1853    if (HeimCredCTX.session != xpc_connection_get_asid(peerconn) && uid != 0) {
1854	syslog(LOG_ERR, "client[pid-%d] is not in same session or root", (int)xpc_connection_get_pid(peerconn));
1855	xpc_connection_cancel(peerconn);
1856	return;
1857    }
1858
1859    /* acquire credential here */
1860
1861    xpc_connection_cancel(peerconn);
1862}
1863#endif
1864
1865/*
1866 * We don't need to hold a xpc_transaction over this session, since if
1867 * we get killed in the middle of deleting credentials, we'll catch
1868 * that when we start up again.
1869 */
1870
1871static void
1872SessionMonitor(void)
1873{
1874    au_sdev_handle_t *h;
1875    dispatch_queue_t bgq;
1876
1877    h = au_sdev_open(AU_SDEVF_ALLSESSIONS);
1878    if (h == NULL)
1879	return;
1880
1881    bgq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
1882
1883    dispatch_async(bgq, ^{
1884        for (;;) {
1885	    auditinfo_addr_t aio;
1886	    int event;
1887
1888	    if (au_sdev_read_aia(h, &event, &aio) != 0)
1889		continue;
1890
1891	    /*
1892	     * Ignore everything but END. This should relly be
1893	     * CLOSE but since that is delayed until the credential
1894	     * is reused, we can't do that
1895	     * */
1896	    if (event != AUE_SESSION_END)
1897		continue;
1898
1899	    dispatch_async(dispatch_get_main_queue(), ^{
1900		    int sessionID = aio.ai_asid;
1901		    CFNumberRef sid = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
1902		    heim_assert(sid != NULL, "out of memory");
1903		    CFDictionaryRemoveValue(HeimCredCTX.sessions, sid);
1904		    CFRelease(sid);
1905		});
1906	}
1907    });
1908}
1909
1910
1911/*
1912 *
1913 */
1914
1915int main(int argc, const char *argv[])
1916{
1917    xpc_connection_t conn;
1918
1919#if TARGET_OS_EMBEDDED
1920    char *error = NULL;
1921
1922    if (sandbox_init("com.apple.GSSCred", SANDBOX_NAMED, &error)) {
1923	syslog(LOG_ERR, "failed to enter sandbox: %s", error);
1924	exit(EXIT_FAILURE);
1925    }
1926#endif
1927
1928    HeimCredCTX.mechanisms = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1929    heim_assert(HeimCredCTX.mechanisms != NULL, "out of memory");
1930
1931    HeimCredCTX.schemas = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1932    heim_assert(HeimCredCTX.schemas != NULL, "out of memory");
1933
1934    HeimCredCTX.globalSchema = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1935    heim_assert(HeimCredCTX.globalSchema != NULL, "out of memory");
1936
1937    _HeimCredRegisterGeneric();
1938    _HeimCredRegisterConfiguration();
1939    _HeimCredRegisterKerberos();
1940    _HeimCredRegisterNTLM();
1941
1942    CFRELEASE_NULL(HeimCredCTX.globalSchema);
1943
1944#if TARGET_IPHONE_SIMULATOR
1945    archivePath = [[NSString alloc] initWithFormat:@"%@/Library/Caches/com.apple.GSSCred.simulator-archive", NSHomeDirectory()];
1946#else
1947    archivePath = @"/var/db/heim-credential-store.archive";
1948#endif
1949
1950#if !TARGET_OS_IPHONE
1951    char *instanceId = getenv(LAUNCH_ENV_INSTANCEID);
1952    if (instanceId) {
1953	/*
1954	 * Pull out uuid and session stored in the sessionUUID
1955	 */
1956
1957	uuid_t sessionUUID;
1958	if (uuid_parse(instanceId, (void *)&sessionUUID) != 0) {
1959	    syslog(LOG_ERR, "can't parse LAUNCH_ENV_INSTANCEID as a uuid");
1960	    return 2;
1961	}
1962
1963	uid_t auid;
1964	memcpy(&auid, &sessionUUID, sizeof(auid));
1965
1966	HeimCredCTX.session = ntohl(auid);
1967
1968	if (HeimCredCTX.session == 0) {
1969	    syslog(LOG_ERR, "0 is not a valid session");
1970	    return 3;
1971	}
1972
1973	/*
1974	 * Join that session
1975	 */
1976	mach_port_name_t session_port;
1977	int ret = audit_session_port(HeimCredCTX.session, &session_port);
1978	if (ret) {
1979	    syslog(LOG_ERR, "could not get audit session port for %d: %s", HeimCredCTX.session, strerror(errno));
1980	    return 4;
1981	}
1982	audit_session_join(session_port);
1983	mach_port_deallocate(current_task(), session_port);
1984
1985
1986	conn = xpc_connection_create_mach_service("com.apple.GSSCred",
1987						  dispatch_get_main_queue(),
1988						  XPC_CONNECTION_MACH_SERVICE_LISTENER);
1989
1990	xpc_connection_set_event_handler(conn, ^(xpc_object_t object){
1991		GSSCred_session_handler(object);
1992	    });
1993
1994    } else
1995#endif
1996    {
1997
1998	_HeimCredInitCommon();
1999	SessionMonitor();
2000	readCredCache();
2001
2002	runQueue = dispatch_queue_create("com.apple.GSSCred", DISPATCH_QUEUE_SERIAL);
2003	heim_assert(runQueue != NULL, "dispatch_queue_create failed");
2004
2005	conn = xpc_connection_create_mach_service("com.apple.GSSCred",
2006					      runQueue,
2007					      XPC_CONNECTION_MACH_SERVICE_LISTENER);
2008
2009	xpc_connection_set_event_handler(conn, ^(xpc_object_t object){
2010		GSSCred_event_handler(object);
2011	    });
2012
2013    }
2014
2015    xpc_connection_resume(conn);
2016
2017    dispatch_main();
2018}
2019