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 <xpc/xpc.h>
37#import <xpc/private.h>
38#import <syslog.h>
39#import <sandbox.h>
40
41#import "HeimCredCoder.h"
42#import "heimcred.h"
43#import "common.h"
44
45/*
46 *
47 */
48struct HeimMech_s {
49    CFRuntimeBase runtime;
50    CFStringRef name;
51    CFSetRef publicAttributes;
52    HeimCredAuthCallback callback;
53};
54
55static NSString *archivePath = NULL;
56
57static void
58FlattenCredential(const void *key, const void *value, void *context)
59{
60    HeimCredRef cred = (HeimCredRef)value;
61    id nskey = [HeimCredDecoder copyCF2NS:key];
62    id attrs = [HeimCredDecoder copyCF2NS:cred->attributes];
63    NSMutableDictionary *flatten = context;
64    [flatten setObject:attrs forKey:nskey];
65    [nskey release];
66    [attrs release];
67}
68
69static void
70storeCredCache(void)
71{
72    id flatten = NULL;
73    @try {
74	flatten = [[NSMutableDictionary alloc] init];
75	CFDictionaryApplyFunction(HeimCredCTX.items, FlattenCredential, flatten);
76	[HeimCredDecoder archiveRootObject:flatten toFile:archivePath];
77    } @catch(NSException *e) {
78    } @finally {
79	[flatten release];
80    }
81}
82
83static void
84readCredCache(void)
85{
86    @autoreleasepool {
87	NSDictionary *flatten = NULL;
88	@try {
89	    flatten = [HeimCredDecoder copyUnarchiveObjectWithFileSecureEncoding:archivePath];
90
91	    if (!flatten || ![flatten isKindOfClass:[NSDictionary class]])
92		return;
93
94	    [flatten enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
95		CFUUIDRef cfkey = [HeimCredDecoder copyNS2CF:key];
96		CFDictionaryRef cfvalue = [HeimCredDecoder copyNS2CF:value];
97		if (cfkey && cfvalue) {
98		    HeimCredRef cred = HeimCredCreateItem(cfkey);
99		    cred->attributes = cfvalue;
100		    CFRetain(cfvalue);
101		    CFDictionarySetValue(HeimCredCTX.items, cred->uuid, cred);
102		}
103		CFRELEASE_NULL(cfkey);
104		CFRELEASE_NULL(cfvalue);
105	     }];
106	} @catch(NSException *e) {
107	    syslog(LOG_ERR, "readCredCache failed with: %s:%s", [[e name] UTF8String], [[e reason] UTF8String]);
108	}
109    }
110}
111
112struct peer {
113    xpc_connection_t peer;
114    CFStringRef bundleID;
115};
116
117static bool
118checkACLInCredentialChain(struct peer *peer, CFUUIDRef uuid, bool *hasACL)
119{
120    int max_depth = 10;
121    bool res = false;
122
123    if (hasACL)
124	*hasACL = false;
125
126    while (1) {
127	HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, uuid);
128	CFUUIDRef parent;
129
130	if (cred == NULL)
131	    goto pass;
132
133	if (max_depth-- < 0)
134	    goto out;
135
136	CFArrayRef array = CFDictionaryGetValue(cred->attributes,  kHEIMAttrBundleIdentifierACL);
137	if (array) {
138	    CFIndex n, count;
139
140	    if (hasACL)
141		*hasACL = true;
142
143	    if (CFGetTypeID(array) != CFArrayGetTypeID())
144		goto out;
145
146	    count = CFArrayGetCount(array);
147	    for (n = 0; n < count; n++) {
148		CFStringRef prefix = CFArrayGetValueAtIndex(array, n);
149
150		if (CFGetTypeID(prefix) != CFStringGetTypeID())
151		    goto out;
152
153		if (CFStringHasPrefix(peer->bundleID, prefix))
154		    goto pass;
155
156		if (CFStringCompare(CFSTR("*"), prefix, 0) == kCFCompareEqualTo)
157		    goto pass;
158	    }
159	    if (n >= count)
160		goto out;
161	}
162
163	parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
164	if (parent == NULL)
165	    goto out;
166
167	if (CFEqual(parent, uuid))
168	    break;
169
170	uuid = parent;
171    }
172
173 pass:
174    res = true;
175
176  out:
177    return res;
178}
179
180static bool
181addPeerToACL(struct peer *peer, CFMutableDictionaryRef attrs)
182{
183    CFArrayRef acl = CFDictionaryGetValue(attrs, kHEIMAttrBundleIdentifierACL);
184    if (acl == NULL || CFGetTypeID(acl) != CFArrayGetTypeID())
185	return false;
186
187    if (!CFArrayContainsValue(acl, CFRangeMake(0, CFArrayGetCount(acl)), peer->bundleID)) {
188	CFMutableArrayRef a = CFArrayCreateMutableCopy(NULL, 0, acl);
189	if (a == NULL)
190	    return false;
191	CFArrayAppendValue(a, peer->bundleID);
192	CFDictionarySetValue(attrs, kHEIMAttrBundleIdentifierACL, a);
193	acl = a;
194	CFRELEASE_NULL(a);
195    }
196
197    return true;
198}
199
200static void
201do_CreateCred(struct peer *peer, xpc_object_t request, xpc_object_t reply)
202{
203    CFMutableDictionaryRef attrs = NULL;
204    HeimCredRef cred = NULL;
205    CFUUIDRef uuid = NULL;
206    bool hasACL = false;
207
208    CFDictionaryRef attributes = HeimCredMessageCopyAttributes(request, "attributes");
209    if (attributes == NULL)
210	return;
211
212    /* check if we are ok to link into this cred-tree */
213    uuid = CFDictionaryGetValue(attributes, kHEIMAttrParentCredential);
214    if (uuid != NULL && !checkACLInCredentialChain(peer, uuid, &hasACL))
215	return;
216
217    uuid = CFDictionaryGetValue(attributes, kHEIMAttrUUID);
218    if (uuid) {
219	CFRetain(uuid);
220
221	if (CFDictionaryGetValue(HeimCredCTX.items, uuid) != NULL)
222	    goto out;
223
224    } else {
225	uuid = CFUUIDCreate(NULL);
226	if (uuid == NULL)
227	    goto out;
228    }
229    cred = HeimCredCreateItem(uuid);
230    if (cred == NULL)
231	goto out;
232
233    /* XXX filter attributes */
234
235    attrs= CFDictionaryCreateMutableCopy(NULL, 0, attributes);
236    if (attrs == NULL)
237	goto out;
238
239    bool hasACLInAttributes = addPeerToACL(peer, attrs);
240
241    /* make sure this cred-tree as an ACL */
242    if (!hasACL && !hasACLInAttributes) {
243	const void *values[1] = { (void *)peer->bundleID };
244	CFArrayRef array = CFArrayCreate(NULL, values, sizeof(values)/sizeof(values[0]), &kCFTypeArrayCallBacks);
245	if (array == NULL) abort();
246	CFDictionarySetValue(attrs, kHEIMAttrBundleIdentifierACL, array);
247	CFRELEASE_NULL(array);
248    }
249
250
251
252    CFDictionarySetValue(attrs, kHEIMAttrUUID, uuid);
253
254    /* make sure credential always are have a parent and group */
255    if (CFDictionaryGetValue(attrs, kHEIMAttrParentCredential) == NULL)
256	CFDictionarySetValue(attrs, kHEIMAttrParentCredential, uuid);
257    if (CFDictionaryGetValue(attrs, kHEIMAttrCredentialGroup) == NULL)
258	CFDictionarySetValue(attrs, kHEIMAttrCredentialGroup, uuid);
259
260    cred->attributes = attrs;
261
262    CFDictionarySetValue(HeimCredCTX.items, cred->uuid, cred);
263
264    HeimCredMessageSetAttributes(reply, "attributes", cred->attributes);
265out:
266    CFRELEASE_NULL(attributes);
267    CFRELEASE_NULL(cred);
268    CFRELEASE_NULL(uuid);
269}
270
271struct MatchCTX {
272    struct peer *peer;
273    CFMutableArrayRef results;
274    CFDictionaryRef query;
275    CFIndex numQueryItems;
276    HeimCredRef cred;
277    CFIndex count;
278};
279
280static void
281MatchQueryItem(const void *key, const void *value, void *context)
282{
283    struct MatchCTX * mc = (struct MatchCTX *)context;
284    if (mc->cred->attributes == NULL)
285	return;
286    CFTypeRef val = CFDictionaryGetValue(mc->cred->attributes, key);
287    if (val == NULL)
288	return;
289    if (!CFEqual(val, value))
290	return;
291    mc->count++;
292}
293
294static void
295MatchQuery(const void *key, const void *value, void *context)
296{
297    struct MatchCTX *mc = (struct MatchCTX *)context;
298    CFUUIDRef uuid = (CFUUIDRef)key;
299
300    if (!checkACLInCredentialChain(mc->peer, uuid, NULL))
301	return;
302
303    HeimCredRef cred = (HeimCredRef)value;
304    if (cred->attributes == NULL)
305	return;
306    mc->cred = cred;
307    mc->count = 0;
308    CFDictionaryApplyFunction(mc->query, MatchQueryItem, mc);
309    if (mc->numQueryItems < mc->count) abort();
310    if (mc->numQueryItems == mc->count)
311	CFArrayAppendValue(mc->results, cred);
312}
313
314
315static CFArrayRef
316QueryCopy(struct peer *peer, xpc_object_t request, const char *key)
317{
318    struct MatchCTX mc = {
319	.peer = peer,
320	.results = NULL,
321	.query = NULL,
322	.cred = NULL,
323    };
324    CFUUIDRef uuidref = NULL;
325
326
327    mc.query = HeimCredMessageCopyAttributes(request, key);
328    if (mc.query == NULL)
329	goto out;
330
331    mc.numQueryItems = CFDictionaryGetCount(mc.query);
332
333    if (mc.numQueryItems == 1) {
334	uuidref = CFDictionaryGetValue(mc.query, kHEIMAttrUUID);
335	if (uuidref && CFGetTypeID(uuidref) == CFUUIDGetTypeID()) {
336
337	    if (!checkACLInCredentialChain(peer, uuidref, NULL))
338		goto out;
339
340	    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, uuidref);
341
342	    const void *values[1] = { (void *)cred } ;
343
344	    mc.results = (CFMutableArrayRef)CFArrayCreate(NULL, (const void **)&values, cred ? 1 : 0, &kCFTypeArrayCallBacks);
345	    goto out;
346	}
347    }
348
349    mc.results = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
350
351    CFDictionaryApplyFunction(HeimCredCTX.items, MatchQuery, &mc);
352
353out:
354    CFRELEASE_NULL(mc.query);
355    return mc.results;
356}
357
358struct child {
359    CFUUIDRef parent;
360    CFMutableArrayRef array;
361};
362
363static void
364DeleteChild(const void *key, const void *value, void *context)
365{
366    struct child *child = (struct child  *)context;
367
368    if (CFEqual(key, child->parent))
369	return;
370
371    HeimCredRef cred = (HeimCredRef)value;
372    CFUUIDRef parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
373    if (parent && CFEqual(child->parent, parent))
374	CFArrayAppendValue(child->array, cred->uuid);
375}
376
377static void
378DeleteChildrenApplier(const void *value, void *context)
379{
380    if (CFGetTypeID(value) != CFUUIDGetTypeID()) abort();
381    CFDictionaryRemoveValue(HeimCredCTX.items, value);
382}
383
384
385static void
386DeleteChildren(CFUUIDRef parent)
387{
388    /*
389     * delete all child entries for to UUID
390     */
391    struct child child = {
392	.parent = parent,
393	.array = NULL,
394    };
395    child.array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
396    CFDictionaryApplyFunction(HeimCredCTX.items, DeleteChild, &child);
397    CFArrayApplyFunction(child.array, CFRangeMake(0, CFArrayGetCount(child.array)), DeleteChildrenApplier, NULL);
398    CFRelease(child.array);
399}
400
401struct fromto {
402    CFUUIDRef from;
403    CFUUIDRef to;
404};
405
406static void
407UpdateParent(const void *key, const void *value, void *context)
408{
409    struct fromto *fromto = (struct fromto *)context;
410    HeimCredRef cred = (HeimCredRef)value;
411    CFUUIDRef parent = CFDictionaryGetValue(cred->attributes, kHEIMAttrParentCredential);
412    if (parent && CFEqual(parent, fromto->from)) {
413	CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, cred->attributes);
414	CFRelease(cred->attributes);
415	CFDictionarySetValue(attrs, kHEIMAttrParentCredential, fromto->to);
416	CFDictionarySetValue(attrs, kHEIMAttrCredentialGroup, fromto->to);
417	cred->attributes = attrs;
418    }
419}
420
421
422
423
424static void
425do_Delete(struct peer *peer, xpc_object_t request, xpc_object_t reply)
426{
427    CFArrayRef items = QueryCopy(peer, request, "query");
428    if (items == NULL)
429	return;
430
431    CFIndex n, count = CFArrayGetCount(items);
432    for (n = 0; n < count; n++) {
433	HeimCredRef cred = (HeimCredRef)CFArrayGetValueAtIndex(items, n);
434	CFDictionaryRemoveValue(HeimCredCTX.items, cred->uuid);
435	DeleteChildren(cred->uuid);
436    }
437    CFRelease(items);
438}
439
440static void
441updateCred(const void *key, const void *value, void *context)
442{
443    CFMutableDictionaryRef attrs = (CFMutableDictionaryRef)context;
444    /* XXX filter keys */
445    CFDictionarySetValue(attrs, key, value);
446}
447
448
449static void
450do_SetAttrs(struct peer *peer, xpc_object_t request, xpc_object_t reply)
451{
452    CFUUIDRef uuid = HeimCredCopyUUID(request, "uuid");
453    CFMutableDictionaryRef attrs;
454
455    if (uuid == NULL)
456	return;
457
458    if (!checkACLInCredentialChain(peer, uuid, NULL))
459	return;
460
461    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, uuid);
462    CFRelease(uuid);
463    if (cred == NULL)
464	return;
465
466    if (cred->attributes) {
467	attrs = CFDictionaryCreateMutableCopy(NULL, 0, cred->attributes);
468	if (attrs == NULL)
469	    return;
470    } else {
471	attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
472    }
473
474    CFDictionaryRef replacementAttrs = HeimCredMessageCopyAttributes(request, "attributes");
475    if (replacementAttrs == NULL) {
476	CFRelease(attrs);
477	return;
478    }
479    CFDictionaryApplyFunction(replacementAttrs, updateCred, attrs);
480    CFRELEASE_NULL(replacementAttrs);
481
482    /* make sure the current caller is on the ACL list */
483    addPeerToACL(peer, attrs);
484
485    CFRELEASE_NULL(cred->attributes);
486    cred->attributes = attrs;
487}
488
489static void
490do_Fetch(struct peer *peer, xpc_object_t request, xpc_object_t reply)
491{
492    CFUUIDRef uuid = HeimCredCopyUUID(request, "uuid");
493    if (uuid == NULL)
494	return;
495
496    if (!checkACLInCredentialChain(peer, uuid, NULL))
497	return;
498
499    HeimCredRef cred = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, uuid);
500    CFRelease(uuid);
501    if (cred == NULL)
502	return;
503
504    /* XXX filter the attributes */
505
506    HeimCredMessageSetAttributes(reply, "attributes", cred->attributes);
507}
508
509static void
510do_Query(struct peer *peer, xpc_object_t request, xpc_object_t reply)
511{
512    CFArrayRef items = QueryCopy(peer, request, "query");
513    if (items == NULL)
514	return;
515
516    CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
517    if (array == NULL) {
518	CFRELEASE_NULL(items);
519	return;
520    }
521
522    CFIndex n, count = CFArrayGetCount(items);
523    for (n = 0; n < count; n++) {
524	HeimCredRef cred = (HeimCredRef)CFArrayGetValueAtIndex(items, n);
525	CFArrayAppendValue(array, cred->uuid);
526    }
527    CFRELEASE_NULL(items);
528
529    HeimCredMessageSetAttributes(reply, "items", array);
530    CFRELEASE_NULL(array);
531}
532
533static void
534do_Move(struct peer *peer, xpc_object_t request, xpc_object_t reply)
535{
536    CFUUIDRef from = (CFUUIDRef)HeimCredMessageCopyAttributes(request, "from");
537    CFUUIDRef to = (CFUUIDRef)HeimCredMessageCopyAttributes(request, "to");
538
539    if (!checkACLInCredentialChain(peer, from, NULL) || !checkACLInCredentialChain(peer, to, NULL))
540	return;
541
542    HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, from);
543    HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(HeimCredCTX.items, to);
544
545    CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
546    CFDictionaryRemoveValue(HeimCredCTX.items, from);
547    credfrom = NULL;
548
549    CFDictionarySetValue(newattrs, kHEIMAttrUUID, to);
550    CFDictionarySetValue(newattrs, kHEIMAttrCredentialGroup, to);
551
552    if (credto == NULL) {
553	credto = HeimCredCreateItem(to);
554	if (credto == NULL) abort();
555	credto->attributes = newattrs;
556	CFDictionarySetValue(HeimCredCTX.items, credto->uuid, credto);
557	CFRelease(credto);
558
559    } else {
560	CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
561	if (parentUUID)
562	    CFDictionarySetValue(newattrs, kHEIMAttrParentCredential, parentUUID);
563	CFRELEASE_NULL(credto->attributes);
564	credto->attributes = newattrs;
565    }
566
567    /*
568     * delete all child entries for to UUID
569     */
570    DeleteChildren(to);
571
572    /*
573     * update all child entries for from UUID
574     */
575    struct fromto fromto = {
576	.from = from,
577	.to = to,
578    };
579    CFDictionaryApplyFunction(HeimCredCTX.items, UpdateParent, &fromto);
580}
581
582
583static void GSSCred_peer_event_handler(struct peer *peer, xpc_object_t event)
584{
585    xpc_object_t reply  = NULL;
586    xpc_type_t type = xpc_get_type(event);
587    if (type == XPC_TYPE_ERROR)
588	return;
589
590    assert(type == XPC_TYPE_DICTIONARY);
591
592    const char *cmd = xpc_dictionary_get_string(event, "command");
593    if (cmd == NULL) {
594	xpc_connection_cancel(peer->peer);
595    } else if (strcmp(cmd, "wakeup") == 0) {
596
597    } else if (strcmp(cmd, "create") == 0) {
598	reply = xpc_dictionary_create_reply(event);
599	do_CreateCred(peer, event, reply);
600    } else if (strcmp(cmd, "delete") == 0) {
601	reply = xpc_dictionary_create_reply(event);
602	do_Delete(peer, event, reply);
603    } else if (strcmp(cmd, "setattributes") == 0) {
604	reply = xpc_dictionary_create_reply(event);
605	do_SetAttrs(peer, event, reply);
606    } else if (strcmp(cmd, "fetch") == 0) {
607	reply = xpc_dictionary_create_reply(event);
608	do_Fetch(peer, event, reply);
609    } else if (strcmp(cmd, "move") == 0) {
610	reply = xpc_dictionary_create_reply(event);
611	do_Move(peer, event, reply);
612    } else if (strcmp(cmd, "query") == 0) {
613	reply = xpc_dictionary_create_reply(event);
614	do_Query(peer, event, reply);
615    } else if (strcmp(cmd, "retain-transient") == 0) {
616	reply = xpc_dictionary_create_reply(event);
617    } else if (strcmp(cmd, "release-transient") == 0) {
618	reply = xpc_dictionary_create_reply(event);
619    } else {
620	syslog(LOG_ERR, "peer sent invalid command %s", cmd);
621	xpc_connection_cancel(peer->peer);
622    }
623
624    if (reply) {
625	xpc_connection_send_message(peer->peer, reply);
626	xpc_release(reply);
627    }
628
629    storeCredCache();
630}
631
632static void
633peer_final(void *ptr)
634{
635    struct peer *peer = ptr;
636    CFRELEASE_NULL(peer->bundleID);
637}
638
639static CFStringRef
640CopySigningIdentitier(xpc_connection_t conn)
641{
642	uint8_t header[8] = { 0 };
643	uint32_t len;
644	audit_token_t audit_token;
645	pid_t pid;
646
647	pid = xpc_connection_get_pid(conn);
648	xpc_connection_get_audit_token(conn, &audit_token);
649
650	int rcent = csops_audittoken(pid, CS_OPS_IDENTITY, header, sizeof(header), &audit_token);
651	if (rcent != -1 || errno != ERANGE)
652	    return NULL;
653
654
655	memcpy(&len, &header[4], 4);
656	len = ntohl(len);
657	if (len > 1024 * 1024)
658	    return NULL;
659	else if (len == 0)
660	    return NULL;
661
662	uint8_t buffer[len];
663
664	rcent = csops_audittoken(pid, CS_OPS_IDENTITY, buffer, len, &audit_token);
665	if (rcent != 0)
666	    return NULL;
667
668	char *p = (char *)buffer;
669	if (len > 8) {
670	    p += 8;
671	    len -= 8;
672	} else
673	    return NULL;
674
675	return CFStringCreateWithBytes(NULL, (UInt8 *)p, len, kCFStringEncodingUTF8, false);
676}
677
678static void GSSCred_event_handler(xpc_connection_t peerconn)
679{
680    struct peer *peer;
681
682    peer = malloc(sizeof(*peer));
683    if (peer == NULL) abort();
684
685    peer->peer = peerconn;
686    peer->bundleID = CopySigningIdentitier(peerconn);
687    if (peer->bundleID == NULL) {
688	free(peer);
689	xpc_connection_cancel(peerconn);
690	return;
691    }
692
693    xpc_connection_set_context(peerconn, peer);
694    xpc_connection_set_finalizer_f(peerconn, peer_final);
695
696    xpc_connection_set_event_handler(peerconn, ^(xpc_object_t event) {
697	GSSCred_peer_event_handler(peer, event);
698    });
699    xpc_connection_resume(peerconn);
700}
701
702int main(int argc, const char *argv[])
703{
704#if TARGET_OS_EMBEDDED
705    char *error = NULL;
706
707    if (sandbox_init("com.apple.GSSCred", SANDBOX_NAMED, &error)) {
708	syslog(LOG_ERR, "failed to enter sandbox: %s", error);
709	exit(EXIT_FAILURE);
710    }
711#endif
712
713#if TARGET_IPHONE_SIMULATOR
714    archivePath = [[NSString alloc] initWithFormat:@"%@/Library/Caches/com.apple.GSSCred.simulator-archive", NSHomeDirectory()];
715#elif TARGET_OS_EMBEDDED
716    archivePath = @"/var/db/heim-credential-store.archive";
717#else
718    archivePath = [[NSString alloc] initWithFormat:@"%@/Library/Caches/com.apple.GSSCred.archive", NSHomeDirectory()];
719#endif
720
721    _HeimCredInitCommon();
722    readCredCache();
723    xpc_main(GSSCred_event_handler);
724    return 0;
725}
726