]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/SecureObjectSync/SOSAccount.c
8ac8431efb063057007aa527e226d1f5d57d2f05
[apple/security.git] / sec / SOSCircle / SecureObjectSync / SOSAccount.c
1 /*
2 * Created by Michael Brouwer on 6/22/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
4 */
5
6 /*
7 * SOSAccount.c - Implementation of the secure object syncing account.
8 * An account contains a SOSCircle for each protection domain synced.
9 */
10
11 #include <SecureObjectSync/SOSInternal.h>
12 #include <SecureObjectSync/SOSAccount.h>
13 #include <SecureObjectSync/SOSCircle.h>
14 #include <SecureObjectSync/SOSCloudCircle.h>
15 #include <SecureObjectSync/SOSEngine.h>
16 #include <SecureObjectSync/SOSPeer.h>
17 #include <SecureObjectSync/SOSFullPeerInfo.h>
18 #include <SecureObjectSync/SOSPeerInfo.h>
19 #include <SecureObjectSync/SOSPeerInfoInternal.h>
20 #include <SecureObjectSync/SOSUserKeygen.h>
21 #include <Security/SecKeyPriv.h>
22 #include <Security/SecItemPriv.h>
23 #include <CoreFoundation/CFArray.h>
24 #include <dispatch/dispatch.h>
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <AssertMacros.h>
28 #include <utilities/SecCFWrappers.h>
29
30 #include <utilities/der_plist.h>
31 #include <utilities/der_plist_internal.h>
32 #include <utilities/iOSforOSX.h>
33
34 #include <utilities/SecAKSWrappers.h>
35
36 #include <corecrypto/ccder.h>
37
38 #include <securityd/SOSCloudCircleServer.h>
39 #include <securityd/SecDbItem.h> // For SecError
40
41 #include <utilities/debugging.h>
42 #include <utilities/iCloudKeychainTrace.h>
43
44 #include <notify.h>
45
46 static CFStringRef kicloud_identity_name = CFSTR("Cloud Identity");
47
48 //
49 // Forward statics.
50 //
51
52 static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, bool writeUpdate, bool initialSync, CFErrorRef *error);
53
54 //
55 // DER Encoding utilities
56 //
57
58 //
59 // Encodes data or a zero length data
60 //
61 static size_t der_sizeof_data_or_null(CFDataRef data, CFErrorRef* error)
62 {
63 if (data) {
64 return der_sizeof_data(data, error);
65 } else {
66 return der_sizeof_null(kCFNull, error);
67 }
68 }
69
70 static uint8_t* der_encode_data_or_null(CFDataRef data, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
71 {
72 if (data) {
73 return der_encode_data(data, error, der, der_end);
74 } else {
75 return der_encode_null(kCFNull, error, der, der_end);
76 }
77 }
78
79
80 static const uint8_t* der_decode_data_or_null(CFAllocatorRef allocator, CFDataRef* data,
81 CFErrorRef* error,
82 const uint8_t* der, const uint8_t* der_end)
83 {
84 CFTypeRef value = NULL;
85 der = der_decode_plist(allocator, 0, &value, error, der, der_end);
86 if (value && CFGetTypeID(value) != CFDataGetTypeID()) {
87 CFReleaseNull(value);
88 }
89 if (data) {
90 *data = value;
91 }
92 return der;
93 }
94
95
96 //
97 // Mark: public_bytes encode/decode
98 //
99
100 static size_t der_sizeof_public_bytes(SecKeyRef publicKey, CFErrorRef* error)
101 {
102 CFDataRef publicData = NULL;
103
104 if (publicKey)
105 SecKeyCopyPublicBytes(publicKey, &publicData);
106
107 size_t size = der_sizeof_data_or_null(publicData, error);
108
109 CFReleaseNull(publicData);
110
111 return size;
112 }
113
114 static uint8_t* der_encode_public_bytes(SecKeyRef publicKey, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
115 {
116 CFDataRef publicData = NULL;
117
118 if (publicKey)
119 SecKeyCopyPublicBytes(publicKey, &publicData);
120
121 uint8_t *result = der_encode_data_or_null(publicData, error, der, der_end);
122
123 CFReleaseNull(publicData);
124
125 return result;
126 }
127
128 static const uint8_t* der_decode_public_bytes(CFAllocatorRef allocator, CFIndex algorithmID, SecKeyRef* publicKey, CFErrorRef* error, const uint8_t* der, const uint8_t* der_end)
129 {
130 CFDataRef dataFound = NULL;
131 der = der_decode_data_or_null(allocator, &dataFound, error, der, der_end);
132
133 if (der && dataFound && publicKey) {
134 *publicKey = SecKeyCreateFromPublicData(allocator, algorithmID, dataFound);
135 }
136 CFReleaseNull(dataFound);
137
138 return der;
139 }
140
141
142 //
143 // Cloud Paramters encode/decode
144 //
145
146 static size_t der_sizeof_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error)
147 {
148 size_t public_key_size = der_sizeof_public_bytes(publicKey, error);
149 size_t parameters_size = der_sizeof_data_or_null(paramters, error);
150
151 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, public_key_size + parameters_size);
152 }
153
154 static uint8_t* der_encode_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error,
155 const uint8_t* der, uint8_t* der_end)
156 {
157 uint8_t* original_der_end = der_end;
158
159 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, original_der_end, der,
160 der_encode_public_bytes(publicKey, error, der,
161 der_encode_data_or_null(paramters, error, der, der_end)));
162 }
163
164 static const uint8_t* der_decode_cloud_parameters(CFAllocatorRef allocator,
165 CFIndex algorithmID, SecKeyRef* publicKey,
166 CFDataRef *parameters,
167 CFErrorRef* error,
168 const uint8_t* der, const uint8_t* der_end)
169 {
170 const uint8_t *sequence_end;
171 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
172 der = der_decode_public_bytes(allocator, algorithmID, publicKey, error, der, sequence_end);
173 der = der_decode_data_or_null(allocator, parameters, error, der, sequence_end);
174
175 return der;
176 }
177
178
179 //
180 // bool encoding/decoding
181 //
182
183
184 static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end)
185 {
186 if (NULL == der)
187 return NULL;
188
189 size_t payload_size = 0;
190 const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end);
191
192 if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) {
193 return NULL;
194 }
195
196 if (boolean)
197 *boolean = (*payload != 0);
198
199 return payload + payload_size;
200 }
201
202
203 static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error)
204 {
205 return ccder_sizeof(CCDER_BOOLEAN, 1);
206 }
207
208
209 static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end)
210 {
211 uint8_t value_byte = value;
212
213 return ccder_encode_tl(CCDER_BOOLEAN, 1, der,
214 ccder_encode_body(1, &value_byte, der, der_end));
215 }
216
217 struct __OpaqueSOSAccount {
218 CFRuntimeBase _base;
219
220 dispatch_queue_t queue;
221
222 CFDictionaryRef gestalt;
223
224 CFMutableDictionaryRef circle_identities;
225 CFMutableDictionaryRef circles;
226 CFMutableDictionaryRef retired_peers;
227
228 bool user_public_trusted;
229 CFDataRef user_key_parameters;
230 SecKeyRef user_public;
231 SecKeyRef previous_public;
232 enum DepartureReason departure_code;
233
234 // Non-persistent data
235
236 SOSDataSourceFactoryRef factory;
237 SecKeyRef _user_private;
238 dispatch_source_t user_private_timer;
239 int lock_notification_token;
240
241 // Live Notification
242 CFMutableArrayRef change_blocks;
243
244 SOSAccountKeyInterestBlock update_interest_block;
245 SOSAccountDataUpdateBlock update_block;
246 SOSAccountMessageProcessedBlock processed_message_block;
247
248 CFMutableDictionaryRef deferred_updates;
249
250 CFMutableDictionaryRef pending_changes;
251 };
252
253 CFGiblisWithCompareFor(SOSAccount);
254
255 static inline bool SOSAccountHasLeft(SOSAccountRef account) {
256 switch(account->departure_code) {
257 case kSOSWithdrewMembership: /* Fallthrough */
258 case kSOSMembershipRevoked: /* Fallthrough */
259 case kSOSLeftUntrustedCircle:
260 return true;
261 case kSOSNeverAppliedToCircle: /* Fallthrough */
262 case kSOSNeverLeftCircle: /* Fallthrough */
263 default:
264 return false;
265 }
266 }
267
268 // Private static functions.
269
270 static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error);
271
272 static bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
273 {
274 bool result = false;
275 if (a)
276 {
277 require(a->factory, xit);
278 CFArrayRef circle_names = a->factory->copy_names(a->factory);
279 require(circle_names, xit);
280 CFArrayForEach(circle_names, ^(const void*name) {
281 if (isString(name))
282 SOSAccountEnsureCircle(a, (CFStringRef)name, NULL);
283 });
284
285 CFReleaseNull(circle_names);
286 result = true;
287 }
288 xit:
289 return result;
290 }
291
292 static SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
293 CFDictionaryRef gestalt,
294 SOSDataSourceFactoryRef factory,
295 SOSAccountKeyInterestBlock interest_block,
296 SOSAccountDataUpdateBlock update_block) {
297 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
298
299 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
300
301 a->gestalt = gestalt;
302 CFRetain(a->gestalt);
303
304 a->circles = CFDictionaryCreateMutableForCFTypes(allocator);
305 a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator);
306 a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator);
307
308 a->factory = factory; // We adopt the factory. kthanksbai.
309
310 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
311
312 a->update_interest_block = Block_copy(interest_block);
313 a->update_block = Block_copy(update_block);
314
315 a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator);
316 a->departure_code = kSOSNeverAppliedToCircle;
317
318 return a;
319 }
320
321
322 static SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamedIfPresent(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
323 if (CFDictionaryGetValue(account->circles, name) == NULL) {
324 SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
325 return NULL;
326 }
327
328 return (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
329 }
330
331
332 static void SOSAccountForEachKnownCircle(SOSAccountRef account,
333 void (^handle_incompatible)(CFStringRef name),
334 void (^handle_no_peer)(SOSCircleRef circle),
335 void (^handle_peer)(SOSCircleRef circle, SOSFullPeerInfoRef full_peer)) {
336 CFDictionaryForEach(account->circles, ^(const void *key, const void *value) {
337 if (isNull(value)) {
338 if (handle_incompatible)
339 handle_incompatible((CFStringRef)key);
340 } else {
341 SOSCircleRef circle = (SOSCircleRef) value;
342 CFRetainSafe(circle);
343 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL);
344 if (!fpi) {
345 if (handle_no_peer)
346 handle_no_peer(circle);
347 } else {
348 CFRetainSafe(fpi);
349 if (handle_peer)
350 handle_peer(circle, fpi);
351 CFReleaseSafe(fpi);
352 }
353 CFReleaseSafe(circle);
354 }
355 });
356 }
357
358
359 bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
360 {
361 if (CFEqual(new_gestalt, account->gestalt))
362 return false;
363
364 SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
365 if (SOSFullPeerInfoUpdateGestalt(full_peer, new_gestalt, NULL)) {
366 SOSAccountModifyCircle(account, SOSCircleGetName(circle),
367 NULL, ^(SOSCircleRef circle_to_change) {
368 (void) SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(full_peer));
369 });
370 };
371 });
372
373 CFReleaseNull(account->gestalt);
374 account->gestalt = new_gestalt;
375 CFRetain(account->gestalt);
376
377 return true;
378 }
379
380 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
381 CFDictionaryRef gestalt,
382 SOSDataSourceFactoryRef factory,
383 SOSAccountKeyInterestBlock interest_block,
384 SOSAccountDataUpdateBlock update_block) {
385 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory, interest_block, update_block);
386
387 SOSAccountEnsureFactoryCircles(a);
388
389 return a;
390 }
391
392 static void SOSAccountDestroy(CFTypeRef aObj) {
393 SOSAccountRef a = (SOSAccountRef) aObj;
394
395 if (a->factory)
396 a->factory->release(a->factory);
397
398 CFReleaseNull(a->gestalt);
399 CFReleaseNull(a->circle_identities);
400 CFReleaseNull(a->circles);
401 CFReleaseNull(a->retired_peers);
402
403 a->user_public_trusted = false;
404 CFReleaseNull(a->user_public);
405 CFReleaseNull(a->user_key_parameters);
406
407 SOSAccountPurgePrivateCredential(a);
408 CFReleaseNull(a->previous_public);
409
410 CFReleaseNull(a->change_blocks);
411 Block_release(a->update_interest_block);
412 Block_release(a->update_block);
413 CFReleaseNull(a->processed_message_block);
414 CFReleaseNull(a->pending_changes);
415 CFReleaseNull(a->deferred_updates);
416 a->departure_code = kSOSNeverAppliedToCircle;
417
418 dispatch_release(a->queue);
419 }
420
421 static void SOSAccountSetToNew(SOSAccountRef a) {
422 CFAllocatorRef allocator = CFGetAllocator(a);
423 CFReleaseNull(a->circle_identities);
424 CFReleaseNull(a->circles);
425 CFReleaseNull(a->retired_peers);
426
427 CFReleaseNull(a->user_key_parameters);
428 CFReleaseNull(a->user_public);
429 CFReleaseNull(a->previous_public);
430 CFReleaseNull(a->_user_private);
431
432 CFReleaseNull(a->pending_changes);
433 CFReleaseNull(a->deferred_updates);
434
435 a->user_public_trusted = false;
436 a->departure_code = kSOSNeverAppliedToCircle;
437 a->user_private_timer = 0;
438 a->lock_notification_token = 0;
439
440 // keeping gestalt;
441 // keeping factory;
442 // Live Notification
443 // change_blocks;
444 // update_interest_block;
445 // update_block;
446
447 a->circles = CFDictionaryCreateMutableForCFTypes(allocator);
448 a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator);
449 a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator);
450 a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator);
451
452 SOSAccountEnsureFactoryCircles(a);
453 }
454
455
456 static CFStringRef SOSAccountCopyDescription(CFTypeRef aObj) {
457 SOSAccountRef a = (SOSAccountRef) aObj;
458
459 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@\n Circles: %@ CircleIDs: %@>"), a, a->gestalt, a->circles, a->circle_identities);
460 }
461
462 static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
463 {
464 SOSAccountRef laccount = (SOSAccountRef) lhs;
465 SOSAccountRef raccount = (SOSAccountRef) rhs;
466
467 return CFEqual(laccount->gestalt, raccount->gestalt)
468 && CFEqual(laccount->circles, raccount->circles)
469 && CFEqual(laccount->circle_identities, raccount->circle_identities);
470 // ??? retired_peers
471 }
472
473 #if OLD_CODERS_SUPPORTED
474
475 //
476 // MARK: Persistent Encode decode
477 //
478 SOSAccountRef SOSAccountCreateFromDER_V1(CFAllocatorRef allocator,
479 SOSDataSourceFactoryRef factory,
480 SOSAccountKeyInterestBlock interest_block,
481 SOSAccountDataUpdateBlock update_block,
482 CFErrorRef* error,
483 const uint8_t** der_p, const uint8_t *der_end)
484 {
485 SOSAccountRef account = NULL;
486
487 const uint8_t *sequence_end;
488 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
489
490 {
491 CFDictionaryRef decoded_gestalt = NULL;
492 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
493 *der_p, der_end);
494
495 if (*der_p == 0)
496 return NULL;
497
498 account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
499 CFReleaseNull(decoded_gestalt);
500 }
501
502 CFArrayRef array = NULL;
503 *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
504
505 *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
506 *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
507 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
508 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
509 if (*der_p != sequence_end)
510 *der_p = NULL;
511
512 __block bool success = true;
513
514 require_quiet(array && *der_p, fail);
515
516 CFArrayForEach(array, ^(const void *value) {
517 if (success) {
518 if (isString(value)) {
519 CFDictionaryAddValue(account->circles, value, kCFNull);
520 } else {
521 CFDataRef circleData = NULL;
522 CFDataRef fullPeerInfoData = NULL;
523
524 if (isData(value)) {
525 circleData = (CFDataRef) value;
526 } else if (isArray(value)) {
527 CFArrayRef pair = (CFArrayRef) value;
528
529 CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
530 CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
531
532 if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
533 circleData = (CFDataRef) circleObject;
534 fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
535 }
536 }
537
538 if (circleData) {
539 SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
540 require_action_quiet(circle, fail, success = false);
541
542 CFStringRef circleName = SOSCircleGetName(circle);
543 CFDictionaryAddValue(account->circles, circleName, circle);
544
545 if (fullPeerInfoData) {
546 SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
547 require_action_quiet(full_peer, fail, success = false);
548
549 CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
550 CFReleaseNull(full_peer);
551 }
552 fail:
553 CFReleaseNull(circle);
554 }
555 }
556 }
557 });
558 CFReleaseNull(array);
559
560 require_quiet(success, fail);
561 require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
562 SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
563
564 return account;
565
566 fail:
567 // Create a default error if we don't have one:
568 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Account DER"), NULL, error);
569 CFReleaseNull(account);
570 return NULL;
571 }
572
573 SOSAccountRef SOSAccountCreateFromDER_V2(CFAllocatorRef allocator,
574 SOSDataSourceFactoryRef factory,
575 SOSAccountKeyInterestBlock interest_block,
576 SOSAccountDataUpdateBlock update_block,
577 CFErrorRef* error,
578 const uint8_t** der_p, const uint8_t *der_end)
579 {
580 SOSAccountRef account = NULL;
581 const uint8_t *dersave = *der_p;
582 const uint8_t *derend = der_end;
583
584 const uint8_t *sequence_end;
585 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
586
587 {
588 CFDictionaryRef decoded_gestalt = NULL;
589 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
590 *der_p, der_end);
591
592 if (*der_p == 0)
593 return NULL;
594
595 account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
596 CFReleaseNull(decoded_gestalt);
597 }
598
599 CFArrayRef array = NULL;
600 *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
601
602 uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
603 *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
604 *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
605 *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
606 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
607 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
608 if (*der_p != sequence_end)
609 *der_p = NULL;
610 account->departure_code = (enum DepartureReason) tmp_departure_code;
611
612 __block bool success = true;
613
614 require_quiet(array && *der_p, fail);
615
616 CFArrayForEach(array, ^(const void *value) {
617 if (success) {
618 if (isString(value)) {
619 CFDictionaryAddValue(account->circles, value, kCFNull);
620 } else {
621 CFDataRef circleData = NULL;
622 CFDataRef fullPeerInfoData = NULL;
623
624 if (isData(value)) {
625 circleData = (CFDataRef) value;
626 } else if (isArray(value)) {
627 CFArrayRef pair = (CFArrayRef) value;
628
629 CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
630 CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
631
632 if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
633 circleData = (CFDataRef) circleObject;
634 fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
635 }
636 }
637
638 if (circleData) {
639 SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
640 require_action_quiet(circle, fail, success = false);
641
642 CFStringRef circleName = SOSCircleGetName(circle);
643 CFDictionaryAddValue(account->circles, circleName, circle);
644
645 if (fullPeerInfoData) {
646 SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
647 require_action_quiet(full_peer, fail, success = false);
648
649 CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
650 }
651 fail:
652 CFReleaseNull(circle);
653 }
654 }
655 }
656 });
657 CFReleaseNull(array);
658
659 require_quiet(success, fail);
660 require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
661 SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
662
663 return account;
664
665 fail:
666 // Create a default error if we don't have one:
667 account->factory = NULL; // give the factory back.
668 CFReleaseNull(account);
669 // Try the der inflater from the previous release.
670 account = SOSAccountCreateFromDER_V1(allocator, factory, interest_block, update_block, error, &dersave, derend);
671 if(account) account->departure_code = kSOSNeverAppliedToCircle;
672 return account;
673 }
674
675 #endif /* OLD_CODERS_SUPPORTED */
676
677 #define CURRENT_ACCOUNT_PERSISTENT_VERSION 6
678
679 SOSAccountRef SOSAccountCreateFromDER(CFAllocatorRef allocator,
680 SOSDataSourceFactoryRef factory,
681 SOSAccountKeyInterestBlock interest_block,
682 SOSAccountDataUpdateBlock update_block,
683 CFErrorRef* error,
684 const uint8_t** der_p, const uint8_t *der_end)
685 {
686 SOSAccountRef account = NULL;
687 #if UPGRADE_FROM_PREVIOUS_VERSION
688 const uint8_t *dersave = *der_p;
689 const uint8_t *derend = der_end;
690 #endif
691 uint64_t version = 0;
692
693 const uint8_t *sequence_end;
694 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
695 *der_p = ccder_decode_uint64(&version, *der_p, sequence_end);
696 if(!(*der_p) || version < CURRENT_ACCOUNT_PERSISTENT_VERSION) {
697 #if UPGRADE_FROM_PREVIOUS_VERSION
698 return SOSAccountCreateFromDER_V3(allocator, factory, interest_block, update_block, error, &dersave, derend);
699 #else
700 return NULL;
701 #endif
702 }
703
704 {
705 CFDictionaryRef decoded_gestalt = NULL;
706 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
707 *der_p, der_end);
708
709 if (*der_p == 0)
710 return NULL;
711
712 account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
713 CFReleaseNull(decoded_gestalt);
714 }
715
716 CFArrayRef array = NULL;
717 *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
718
719 uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
720 *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
721 *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
722 *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
723 *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, sequence_end);
724 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
725 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
726 if (*der_p != sequence_end)
727 *der_p = NULL;
728 account->departure_code = (enum DepartureReason) tmp_departure_code;
729
730 __block bool success = true;
731
732 require_quiet(array && *der_p, fail);
733
734 CFArrayForEach(array, ^(const void *value) {
735 if (success) {
736 if (isString(value)) {
737 CFDictionaryAddValue(account->circles, value, kCFNull);
738 } else {
739 CFDataRef circleData = NULL;
740 CFDataRef fullPeerInfoData = NULL;
741
742 if (isData(value)) {
743 circleData = (CFDataRef) value;
744 } else if (isArray(value)) {
745 CFArrayRef pair = (CFArrayRef) value;
746
747 CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
748 CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
749
750 if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
751 circleData = (CFDataRef) circleObject;
752 fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
753 }
754 }
755
756 if (circleData) {
757 SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
758 require_action_quiet(circle, fail, success = false);
759
760 CFStringRef circleName = SOSCircleGetName(circle);
761 CFDictionaryAddValue(account->circles, circleName, circle);
762
763 if (fullPeerInfoData) {
764 SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
765 require_action_quiet(full_peer, fail, success = false);
766
767 CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
768 }
769 fail:
770 CFReleaseNull(circle);
771 }
772 }
773 }
774 });
775 CFReleaseNull(array);
776
777 require_quiet(success, fail);
778 require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
779 SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
780
781 return account;
782
783 fail:
784 account->factory = NULL; // give the factory back.
785 CFReleaseNull(account);
786 return NULL;
787 }
788
789
790 SOSAccountRef SOSAccountCreateFromDER_V3(CFAllocatorRef allocator,
791 SOSDataSourceFactoryRef factory,
792 SOSAccountKeyInterestBlock interest_block,
793 SOSAccountDataUpdateBlock update_block,
794 CFErrorRef* error,
795 const uint8_t** der_p, const uint8_t *der_end)
796 {
797 SOSAccountRef account = NULL;
798 uint64_t version = 0;
799
800 const uint8_t *sequence_end;
801 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
802 *der_p = ccder_decode_uint64(&version, *der_p, sequence_end);
803 if(!(*der_p) || version != 3) {
804 // In this case we want to silently fail so that an account gets newly created.
805 return NULL;
806 }
807
808 {
809 CFDictionaryRef decoded_gestalt = NULL;
810 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error,
811 *der_p, der_end);
812
813 if (*der_p == 0)
814 return NULL;
815
816 account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block);
817 CFReleaseNull(decoded_gestalt);
818 }
819
820 CFArrayRef array = NULL;
821 *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end);
822
823 uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
824 *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end);
825 *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end);
826 *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end);
827 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end);
828 *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end);
829 if (*der_p != sequence_end)
830 *der_p = NULL;
831 account->departure_code = (enum DepartureReason) tmp_departure_code;
832
833 __block bool success = true;
834
835 require_quiet(array && *der_p, fail);
836
837 CFArrayForEach(array, ^(const void *value) {
838 if (success) {
839 if (isString(value)) {
840 CFDictionaryAddValue(account->circles, value, kCFNull);
841 } else {
842 CFDataRef circleData = NULL;
843 CFDataRef fullPeerInfoData = NULL;
844
845 if (isData(value)) {
846 circleData = (CFDataRef) value;
847 } else if (isArray(value)) {
848 CFArrayRef pair = (CFArrayRef) value;
849
850 CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
851 CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);
852
853 if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
854 circleData = (CFDataRef) circleObject;
855 fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
856 }
857 }
858
859 if (circleData) {
860 SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
861 require_action_quiet(circle, fail, success = false);
862
863 CFStringRef circleName = SOSCircleGetName(circle);
864 CFDictionaryAddValue(account->circles, circleName, circle);
865
866 if (fullPeerInfoData) {
867 SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error);
868 require_action_quiet(full_peer, fail, success = false);
869
870 CFDictionaryAddValue(account->circle_identities, circleName, full_peer);
871 }
872 fail:
873 CFReleaseNull(circle);
874 }
875 }
876 }
877 });
878 CFReleaseNull(array);
879
880 require_quiet(success, fail);
881 require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail,
882 SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
883
884 return account;
885
886 fail:
887 // Create a default error if we don't have one:
888 account->factory = NULL; // give the factory back.
889 CFReleaseNull(account);
890 // Don't try the der inflater from the previous release.
891 // account = SOSAccountCreateFromDER_V2(allocator, factory, interest_block, update_block, error, &dersave, derend);
892 if(account) account->departure_code = kSOSNeverAppliedToCircle;
893 return account;
894 }
895
896 SOSAccountRef SOSAccountCreateFromData(CFAllocatorRef allocator, CFDataRef circleData,
897 SOSDataSourceFactoryRef factory,
898 SOSAccountKeyInterestBlock interest_block,
899 SOSAccountDataUpdateBlock update_block,
900 CFErrorRef* error)
901 {
902 size_t size = CFDataGetLength(circleData);
903 const uint8_t *der = CFDataGetBytePtr(circleData);
904 SOSAccountRef account = SOSAccountCreateFromDER(allocator, factory, interest_block, update_block,
905 error,
906 &der, der + size);
907 return account;
908 }
909
910 static CFMutableArrayRef SOSAccountCopyCircleArrayToEncode(SOSAccountRef account)
911 {
912 CFMutableArrayRef arrayToEncode = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
913
914 CFDictionaryForEach(account->circles, ^(const void *key, const void *value) {
915 if (isNull(value)) {
916 CFArrayAppendValue(arrayToEncode, key); // Encode the name of the circle that's out of date.
917 } else {
918 SOSCircleRef circle = (SOSCircleRef) value;
919 CFDataRef encodedCircle = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, NULL);
920 CFTypeRef arrayEntry = encodedCircle;
921 CFRetainSafe(arrayEntry);
922
923 SOSFullPeerInfoRef full_peer = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, key);
924
925 if (full_peer) {
926 CFDataRef encodedPeer = SOSFullPeerInfoCopyEncodedData(full_peer, kCFAllocatorDefault, NULL);
927 CFTypeRef originalArrayEntry = arrayEntry;
928 arrayEntry = CFArrayCreateForCFTypes(kCFAllocatorDefault, encodedCircle, encodedPeer, NULL);
929
930 CFReleaseSafe(originalArrayEntry);
931 CFReleaseNull(encodedPeer);
932 }
933
934 CFArrayAppendValue(arrayToEncode, arrayEntry);
935
936 CFReleaseSafe(arrayEntry);
937 CFReleaseNull(encodedCircle);
938 }
939
940 });
941
942 return arrayToEncode;
943 }
944
945 size_t SOSAccountGetDEREncodedSize(SOSAccountRef account, CFErrorRef *error)
946 {
947 size_t sequence_size = 0;
948 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
949 uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
950
951 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail);
952 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail);
953 require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail);
954 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail);
955 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail);
956 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail);
957 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->previous_public, error)), fail);
958 require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail);
959 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail);
960
961 CFReleaseNull(arrayToEncode);
962 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
963
964 fail:
965 CFReleaseNull(arrayToEncode);
966 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
967 return 0;
968 }
969
970 uint8_t* SOSAccountEncodeToDER(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
971 {
972 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
973 uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
974 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
975 ccder_encode_uint64(version, der,
976 der_encode_dictionary(account->gestalt, error, der,
977 der_encode_array(arrayToEncode, error, der,
978 ccder_encode_uint64(account->departure_code, der,
979 ccder_encode_bool(account->user_public_trusted, der,
980 der_encode_public_bytes(account->user_public, error, der,
981 der_encode_public_bytes(account->previous_public, error, der,
982 der_encode_data_or_null(account->user_key_parameters, error, der,
983 der_encode_dictionary(account->retired_peers, error, der, der_end))))))))));
984
985 CFReleaseNull(arrayToEncode);
986
987 return der_end;
988 }
989
990
991
992 size_t SOSAccountGetDEREncodedSize_V3(SOSAccountRef account, CFErrorRef *error)
993 {
994 size_t sequence_size = 0;
995 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
996 uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
997
998 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail);
999 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail);
1000 require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail);
1001 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail);
1002 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail);
1003 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail);
1004 require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail);
1005 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail);
1006
1007 CFReleaseNull(arrayToEncode);
1008 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1009
1010 fail:
1011 CFReleaseNull(arrayToEncode);
1012 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1013 return 0;
1014 }
1015
1016 uint8_t* SOSAccountEncodeToDER_V3(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1017 {
1018 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1019 uint64_t version = 3;
1020 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1021 ccder_encode_uint64(version, der,
1022 der_encode_dictionary(account->gestalt, error, der,
1023 der_encode_array(arrayToEncode, error, der,
1024 ccder_encode_uint64(account->departure_code, der,
1025 ccder_encode_bool(account->user_public_trusted, der,
1026 der_encode_public_bytes(account->user_public, error, der,
1027 der_encode_data_or_null(account->user_key_parameters, error, der,
1028 der_encode_dictionary(account->retired_peers, error, der, der_end)))))))));
1029
1030 CFReleaseNull(arrayToEncode);
1031
1032 return der_end;
1033 }
1034
1035 #if OLD_CODERS_SUPPORTED
1036
1037 /* Original V2 encoders */
1038
1039 size_t SOSAccountGetDEREncodedSize_V2(SOSAccountRef account, CFErrorRef *error)
1040 {
1041 size_t sequence_size = 0;
1042 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1043
1044 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail);
1045 require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail);
1046 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail);
1047 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail);
1048 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail);
1049 require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail);
1050 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail);
1051
1052 CFReleaseNull(arrayToEncode);
1053 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1054
1055 fail:
1056 CFReleaseNull(arrayToEncode);
1057 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1058 return 0;
1059 }
1060
1061 uint8_t* SOSAccountEncodeToDER_V2(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1062 {
1063 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1064
1065 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1066 der_encode_dictionary(account->gestalt, error, der,
1067 der_encode_array(arrayToEncode, error, der,
1068 ccder_encode_uint64(account->departure_code, der,
1069 ccder_encode_bool(account->user_public_trusted, der,
1070 der_encode_public_bytes(account->user_public, error, der,
1071 der_encode_data_or_null(account->user_key_parameters, error, der,
1072 der_encode_dictionary(account->retired_peers, error, der, der_end))))))));
1073
1074 CFReleaseNull(arrayToEncode);
1075
1076 return der_end;
1077 }
1078
1079
1080 /* Original V1 encoders */
1081
1082
1083 size_t SOSAccountGetDEREncodedSize_V1(SOSAccountRef account, CFErrorRef *error)
1084 {
1085 size_t sequence_size = 0;
1086 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1087
1088 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail);
1089 require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail);
1090 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail);
1091 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail);
1092 require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail);
1093 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail);
1094
1095 CFReleaseNull(arrayToEncode);
1096 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
1097
1098 fail:
1099 CFReleaseNull(arrayToEncode);
1100 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1101 return 0;
1102 }
1103
1104 uint8_t* SOSAccountEncodeToDER_V1(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
1105 {
1106 CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account);
1107
1108 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1109 der_encode_dictionary(account->gestalt, error, der,
1110 der_encode_array(arrayToEncode, error, der,
1111 ccder_encode_bool(account->user_public_trusted, der,
1112 der_encode_public_bytes(account->user_public, error, der,
1113 der_encode_data_or_null(account->user_key_parameters, error, der,
1114 der_encode_dictionary(account->retired_peers, error, der, der_end)))))));
1115
1116 CFReleaseNull(arrayToEncode);
1117
1118 return der_end;
1119 }
1120 #endif /* OLD_CODERS_SUPPORTED */
1121
1122 /************************/
1123
1124 CFDataRef SOSAccountCopyEncodedData(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef *error)
1125 {
1126 size_t size = SOSAccountGetDEREncodedSize(account, error);
1127 if (size == 0)
1128 return NULL;
1129 uint8_t buffer[size];
1130 uint8_t* start = SOSAccountEncodeToDER(account, error, buffer, buffer + sizeof(buffer));
1131 CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
1132 return result;
1133 }
1134
1135 dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
1136 return account->queue;
1137 }
1138
1139 //
1140 // MARK: User Credential management
1141 //
1142
1143 void SOSAccountPurgePrivateCredential(SOSAccountRef account)
1144 {
1145 CFReleaseNull(account->_user_private);
1146 if (account->user_private_timer) {
1147 dispatch_source_cancel(account->user_private_timer);
1148 dispatch_release(account->user_private_timer);
1149 account->user_private_timer = NULL;
1150 xpc_transaction_end();
1151 }
1152 if (account->lock_notification_token) {
1153 notify_cancel(account->lock_notification_token);
1154 account->lock_notification_token = 0;
1155 }
1156 }
1157
1158 static void SOSAccountSetPrivateCredential(SOSAccountRef account, SecKeyRef private) {
1159 if (!private)
1160 return SOSAccountPurgePrivateCredential(account);
1161
1162 CFRetain(private);
1163 CFReleaseSafe(account->_user_private);
1164 account->_user_private = private;
1165
1166 bool resume_timer = false;
1167 if (!account->user_private_timer) {
1168 xpc_transaction_begin();
1169 resume_timer = true;
1170 account->user_private_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, account->queue);
1171 dispatch_source_set_event_handler(account->user_private_timer, ^{
1172 SOSAccountPurgePrivateCredential(account);
1173 });
1174
1175 notify_register_dispatch(kUserKeybagStateChangeNotification, &account->lock_notification_token, account->queue, ^(int token) {
1176 bool locked = false;
1177 CFErrorRef lockCheckError = NULL;
1178
1179 if (!SecAKSGetIsLocked(&locked, &lockCheckError)) {
1180 secerror("Checking for locked after change failed: %@", lockCheckError);
1181 }
1182
1183 if (locked) {
1184 SOSAccountPurgePrivateCredential(account);
1185 }
1186 });
1187 }
1188
1189 // (Re)set the timer's fire time to now + 120 seconds with a 5 second fuzz factor.
1190 dispatch_time_t purgeTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * 60 * NSEC_PER_SEC));
1191 dispatch_source_set_timer(account->user_private_timer, purgeTime, DISPATCH_TIME_FOREVER, (int64_t)(5 * NSEC_PER_SEC));
1192 if (resume_timer)
1193 dispatch_resume(account->user_private_timer);
1194 }
1195
1196 SecKeyRef SOSAccountGetPrivateCredential(SOSAccountRef account, CFErrorRef* error)
1197 {
1198 if (account->_user_private == NULL) {
1199 SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("Private Key not available - failed to prompt user recently"), NULL, error);
1200 }
1201 return account->_user_private;
1202 }
1203
1204 static bool SOSAccountHasPublicKey(SOSAccountRef account, CFErrorRef* error)
1205 {
1206 if (account->user_public == NULL || account->user_public_trusted == false) {
1207 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key not available - failed to register before call"), NULL, error);
1208 return false;
1209 }
1210
1211 return true;
1212 }
1213
1214 static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error);
1215
1216 static void SOSAccountGenerationSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) {
1217 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1218 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1219 if(SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), NULL) &&
1220 !SOSCircleVerify(circle, account->user_public, NULL)) {
1221 SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) {
1222 SOSFullPeerInfoRef cloud_fpi = SOSCircleGetiCloudFullPeerInfoRef(circle);
1223 require_quiet(cloud_fpi != NULL, gen_sign);
1224 require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi, privKey, NULL), gen_sign);
1225 if(!SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(cloud_fpi))) {
1226 }
1227 gen_sign: // finally generation sign this.
1228 SOSCircleGenerationSign(circle, privKey, fpi, NULL);
1229 account->departure_code = kSOSNeverLeftCircle;
1230 });
1231 }
1232 });
1233 }
1234
1235 /* this one is meant to be local - not published over KVS. */
1236 static void SOSAccountPeerSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) {
1237 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1238 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1239 SOSFullPeerInfoUpgradeSignatures(fpi, privKey, NULL);
1240 });
1241 }
1242
1243 static void SOSAccountSetPreviousPublic(SOSAccountRef account) {
1244 CFReleaseNull(account->previous_public);
1245 account->previous_public = account->user_public;
1246 CFRetain(account->previous_public);
1247 }
1248
1249 static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account, bool public_was_trusted, SecKeyRef privKey)
1250 {
1251 if (!privKey) return;
1252 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey);
1253
1254 if (account->user_public && account->user_public_trusted && CFEqual(publicKey, account->user_public)) return;
1255
1256 if(public_was_trusted && account->user_public) {
1257 CFReleaseNull(account->previous_public);
1258 account->previous_public = account->user_public;
1259 CFRetain(account->previous_public);
1260 }
1261
1262 CFReleaseNull(account->user_public);
1263 account->user_public = publicKey;
1264 account->user_public_trusted = true;
1265
1266 if(!account->previous_public) {
1267 account->previous_public = account->user_public;
1268 CFRetain(account->previous_public);
1269 }
1270
1271 secnotice("trust", "trusting new public key: %@", account->user_public);
1272 }
1273
1274 static void SOSAccountProcessDeferredUpdates(SOSAccountRef account) {
1275 CFErrorRef error = NULL;
1276 if (account->deferred_updates && !SOSAccountHandleUpdates(account, account->deferred_updates, &error))
1277 secerror("Failed to handle updates when setting public key (%@)", error);
1278
1279 CFReleaseNull(account->deferred_updates);
1280 }
1281
1282
1283 bool SOSAccountTryUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error)
1284 {
1285 bool success = false;
1286
1287 if (!SOSAccountHasPublicKey(account, error))
1288 return false;
1289
1290 if (account->user_key_parameters) {
1291 SecKeyRef new_key = SOSUserKeygen(user_password, account->user_key_parameters, error);
1292 if (new_key) {
1293 SecKeyRef new_public_key = SecKeyCreatePublicFromPrivate(new_key);
1294
1295 if (CFEqualSafe(new_public_key, account->user_public)) {
1296 SOSAccountSetPrivateCredential(account, new_key);
1297 success = true;
1298 } else {
1299 SOSCreateError(kSOSErrorWrongPassword, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL, error);
1300 }
1301 CFReleaseSafe(new_public_key);
1302 CFReleaseSafe(new_key);
1303 }
1304 } else {
1305 SOSCreateError(kSOSErrorProcessingFailure, CFSTR("Have public key but no parameters??"), NULL, error);
1306 }
1307
1308 return success;
1309 }
1310
1311 static bool SOSAccountPublishCloudParameters(SOSAccountRef account, CFErrorRef* error)
1312 {
1313 bool success = false;
1314 CFMutableDataRef cloudParameters = CFDataCreateMutableWithScratch(kCFAllocatorDefault,
1315 der_sizeof_cloud_parameters(account->user_public,
1316 account->user_key_parameters,
1317 error));
1318 if (der_encode_cloud_parameters(account->user_public, account->user_key_parameters, error,
1319 CFDataGetMutableBytePtr(cloudParameters),
1320 CFDataGetMutablePastEndPtr(cloudParameters)) != NULL) {
1321
1322 CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1323 kSOSKVSKeyParametersKey, cloudParameters,
1324 NULL);
1325
1326 CFErrorRef changeError = NULL;
1327 if (account->update_block(changes, &changeError)) {
1328 success = true;
1329 } else {
1330 SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
1331 CFSTR("update parameters key failed [%@]"), changes);
1332 }
1333 CFReleaseSafe(changes);
1334 CFReleaseSafe(changeError);
1335 } else {
1336 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Encoding parameters failed"), NULL, error);
1337 }
1338
1339 CFReleaseNull(cloudParameters);
1340
1341 return success;
1342 }
1343
1344 bool SOSAccountAssertUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error)
1345 {
1346 bool public_was_trusted = account->user_public_trusted;
1347 account->user_public_trusted = false;
1348 SecKeyRef user_private = NULL;
1349
1350 if (account->user_public && account->user_key_parameters) {
1351 // We have an untrusted public key – see if our generation makes the same key:
1352 // if so we trust it and we have the private key.
1353 // if not we still don't trust it.
1354 require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit);
1355 SecKeyRef public_candidate = SecKeyCreatePublicFromPrivate(user_private);
1356 if (!CFEqualSafe(account->user_public, public_candidate)) {
1357 secnotice("trust", "Public keys don't match: calculated: %@, expected: %@",
1358 account->user_public, public_candidate);
1359 debugDumpUserParameters(CFSTR("params"), account->user_key_parameters);
1360 CFReleaseNull(user_private);
1361 } else {
1362 SOSAccountPeerSignatureUpdate(account, user_private);
1363 SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private);
1364 }
1365 CFReleaseSafe(public_candidate);
1366 }
1367
1368 if (!account->user_public_trusted) {
1369 // We may or may not have parameters here.
1370 // In any case we tried using them and they didn't match
1371 // So forget all that and start again, assume we're the first to push anything useful.
1372
1373 CFReleaseNull(account->user_key_parameters);
1374 account->user_key_parameters = SOSUserKeyCreateGenerateParameters(error);
1375 require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit);
1376
1377 SOSAccountPeerSignatureUpdate(account, user_private);
1378 SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private);
1379
1380 CFErrorRef publishError = NULL;
1381 if (!SOSAccountPublishCloudParameters(account, &publishError))
1382 secerror("Failed to publish new cloud parameters: %@", publishError);
1383 CFReleaseSafe(publishError);
1384 }
1385
1386 SOSAccountProcessDeferredUpdates(account);
1387 SOSAccountGenerationSignatureUpdate(account, user_private);
1388 SOSAccountSetPrivateCredential(account, user_private);
1389 exit:
1390 CFReleaseSafe(user_private);
1391
1392 return account->user_public_trusted;
1393 }
1394
1395 //
1396 // MARK: Circle management
1397 //
1398
1399 int SOSAccountCountCircles(SOSAccountRef a) {
1400 assert(a);
1401 assert(a->circle_identities);
1402 assert(a->circles);
1403 return (int)CFDictionaryGetCount(a->circles);
1404 }
1405
1406 static SecKeyRef GeneratePermanentFullECKey_internal(int keySize, CFStringRef name, CFTypeRef accessibility, CFBooleanRef sync, CFErrorRef* error)
1407 {
1408 SecKeyRef full_key = NULL;
1409
1410 CFNumberRef key_size_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
1411
1412 CFDictionaryRef priv_key_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1413 kSecAttrIsPermanent, kCFBooleanTrue,
1414 NULL);
1415
1416 CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1417 kSecAttrKeyType, kSecAttrKeyTypeEC,
1418 kSecAttrKeySizeInBits, key_size_num,
1419 kSecPrivateKeyAttrs, priv_key_attrs,
1420 kSecAttrAccessible, accessibility,
1421 kSecAttrAccessGroup, kSOSInternalAccessGroup,
1422 kSecAttrLabel, name,
1423 kSecAttrSynchronizable, sync,
1424 kSecUseTombstones, kCFBooleanTrue,
1425 NULL);
1426
1427 CFReleaseNull(priv_key_attrs);
1428
1429 CFReleaseNull(key_size_num);
1430 OSStatus status = SecKeyGeneratePair(keygen_parameters, NULL, &full_key);
1431 CFReleaseNull(keygen_parameters);
1432
1433 if (status)
1434 secerror("status: %ld", (long)status);
1435 if (status != errSecSuccess && error != NULL && *error == NULL) {
1436 *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
1437 }
1438
1439 return full_key;
1440 }
1441
1442 static SecKeyRef GeneratePermanentFullECKey(int keySize, CFStringRef name, CFErrorRef* error) {
1443 return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kCFBooleanFalse, error);
1444 }
1445
1446 static SecKeyRef GeneratePermanentFullECKeyForCloudIdentity(int keySize, CFStringRef name, CFErrorRef* error) {
1447 return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlocked, kCFBooleanTrue, error);
1448 }
1449
1450
1451 SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) {
1452 if (CFDictionaryGetValue(account->circles, name) == NULL) {
1453 SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
1454 return NULL;
1455 }
1456 SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1457
1458
1459 if (circle_full_peer_info == NULL) {
1460 CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(account->gestalt), name);
1461 SecKeyRef full_key = GeneratePermanentFullECKey(256, keyName, error);
1462 CFReleaseNull(keyName);
1463
1464 if (full_key) {
1465 circle_full_peer_info = SOSFullPeerInfoCreate(kCFAllocatorDefault, account->gestalt, full_key, error);
1466
1467 CFReleaseNull(full_key);
1468
1469 if (!circle_full_peer_info) {
1470 secerror("Can't make FullPeerInfo for %@-%@ (%@) - is AKS ok?", SOSPeerGestaltGetName(account->gestalt), name, error ? (void*)*error : (void*)CFSTR("-"));
1471 return circle_full_peer_info;
1472 }
1473
1474 CFDictionarySetValue(account->circle_identities, name, circle_full_peer_info);
1475 CFReleaseNull(circle_full_peer_info);
1476 circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1477 }
1478 else
1479 secerror("No full_key: %@:", error ? *error : NULL);
1480 }
1481
1482 return circle_full_peer_info;
1483 }
1484
1485 static bool SOSAccountDestroyCirclePeerInfoNamed(SOSAccountRef account, CFStringRef name, CFErrorRef* error) {
1486 if (CFDictionaryGetValue(account->circles, name) == NULL) {
1487 SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name);
1488 return false;
1489 }
1490
1491 SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name);
1492
1493 if (circle_full_peer_info) {
1494 SOSPeerPurgeAllFor(SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(circle_full_peer_info)));
1495
1496 SOSFullPeerInfoPurgePersistentKey(circle_full_peer_info, NULL);
1497 }
1498
1499 CFDictionaryRemoveValue(account->circle_identities, name);
1500
1501 return true;
1502 }
1503
1504 static bool SOSAccountDestroyCirclePeerInfo(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1505 return SOSAccountDestroyCirclePeerInfoNamed(account, SOSCircleGetName(circle), error);
1506 }
1507
1508 SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1509 return SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
1510 }
1511
1512 SOSPeerInfoRef SOSAccountGetMyPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1513 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error);
1514
1515 return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
1516 }
1517
1518 SOSPeerInfoRef SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error)
1519 {
1520 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, name, error);
1521
1522 return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL;
1523 }
1524
1525 CFArrayRef SOSAccountCopyAccountIdentityPeerInfos(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
1526 {
1527 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(allocator);
1528
1529 CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) {
1530 SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) value;
1531
1532 CFArrayAppendValue(result, SOSFullPeerInfoGetPeerInfo(fpi));
1533 });
1534
1535 return result;
1536 }
1537
1538 bool SOSAccountIsAccountIdentity(SOSAccountRef account, SOSPeerInfoRef peer_info, CFErrorRef *error)
1539 {
1540 __block bool matches = false;
1541 CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) {
1542 if (!matches) {
1543 matches = CFEqual(peer_info, SOSFullPeerInfoGetPeerInfo((SOSFullPeerInfoRef) value));
1544 }
1545 });
1546
1547 return matches;
1548 }
1549
1550 bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
1551 {
1552 __block bool result = true;
1553 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
1554 if (!SOSAccountSyncWithAllPeersInCircle(account, circle, error))
1555 result = false;
1556 });
1557
1558 return result;
1559 }
1560
1561 bool SOSAccountSyncWithAllPeersInCircle(SOSAccountRef account, SOSCircleRef circle,
1562 CFErrorRef *error)
1563 {
1564 SOSPeerInfoRef my_peer = SOSAccountGetMyPeerInCircle(account, circle, error);
1565 if (!my_peer)
1566 return false;
1567
1568 __block bool didSync = false;
1569 __block bool result = true;
1570
1571 if (SOSCircleHasPeer(circle, my_peer, NULL)) {
1572 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1573 if (!CFEqual(SOSPeerInfoGetPeerID(my_peer), SOSPeerInfoGetPeerID(peer)))
1574 {
1575 bool local_didSync = false;
1576 if (!SOSAccountSyncWithPeer(account, circle, peer, &local_didSync, error))
1577 result = false;
1578 if (!didSync && local_didSync)
1579 {
1580 didSync = true;
1581 }
1582 }
1583 });
1584
1585 if (didSync)
1586 {
1587 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
1588 }
1589 }
1590
1591 return result;
1592 }
1593
1594 bool SOSAccountSyncWithPeer(SOSAccountRef account, SOSCircleRef circle,
1595 SOSPeerInfoRef thisPeer, bool* didSendData, CFErrorRef* error)
1596 {
1597 CFStringRef peer_id = SOSPeerInfoGetPeerID(thisPeer);
1598 CFStringRef peer_write_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_id);
1599 SOSFullPeerInfoRef myRef = SOSAccountGetMyFullPeerInCircle(account, circle, error);
1600
1601 __block bool sentData = false;
1602
1603
1604 SOSPeerSendBlock writeToKVSKey = ^bool (CFDataRef data, CFErrorRef* error) {
1605 secnotice("account", "writing data of size %ld:", data?CFDataGetLength(data):0);
1606 sentData = (NULL != data);
1607 CFDictionaryRef writeToDo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peer_write_key, data, NULL);
1608 bool written = account->update_block(writeToDo, error);
1609 if (account->processed_message_block)
1610 account->processed_message_block(circle, NULL, data);
1611 CFRelease(writeToDo);
1612 return written;
1613 };
1614
1615 if (NULL != didSendData)
1616 {
1617 *didSendData = sentData;
1618 }
1619
1620 bool result = SOSCircleSyncWithPeer(myRef, circle, account->factory, writeToKVSKey, peer_id, error);
1621 CFReleaseNull(peer_write_key);
1622 return result;
1623 }
1624
1625 static bool SOSAccountIsActivePeerInCircleNamed(SOSAccountRef account, CFStringRef circle_name, CFStringRef peerid, CFErrorRef* error) {
1626 SOSCircleRef circle = SOSAccountFindCircle(account, circle_name, error);
1627 if(!circle) return false;
1628 return SOSCircleHasActivePeerWithID(circle, peerid, error);
1629 }
1630
1631 static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1632 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL);
1633 if(!fpi) return false;
1634 return SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), error);
1635 }
1636
1637 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
1638 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
1639 {
1640 if(!SOSAccountIsMyPeerActiveInCircle(account, circle, NULL)) return true;
1641
1642 CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1643
1644 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef circlePeer) {
1645 CFStringRef from_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, cleanupPeer, circlePeer);
1646 CFStringRef to_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, circlePeer, cleanupPeer);
1647
1648 CFDictionaryAddValue(keysToWrite, from_key, kCFNull);
1649 CFDictionaryAddValue(keysToWrite, to_key, kCFNull);
1650
1651 CFReleaseNull(from_key);
1652 CFReleaseNull(to_key);
1653 });
1654
1655 if(SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer)) {
1656 CFStringRef resignationKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(cleanupPeer));
1657 CFDictionarySetValue(keysToWrite, resignationKey, kCFNull);
1658 CFDictionaryRemoveValue(account->retired_peers, resignationKey);
1659 CFReleaseNull(resignationKey);
1660 }
1661
1662 bool success = account->update_block(keysToWrite, error);
1663
1664 CFReleaseNull(keysToWrite);
1665
1666 return success;
1667 }
1668
1669 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
1670 CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1671
1672 CFDictionaryRef copyToIterate = CFDictionaryCreateCopy(kCFAllocatorDefault, account->retired_peers);
1673
1674 CFDictionaryForEach(copyToIterate, ^(const void* resignationKey, const void* value) {
1675 CFStringRef circle_name = NULL;
1676 CFStringRef retiree_peerid = NULL;
1677 SOSPeerInfoRef pi = NULL;
1678 SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL);
1679 require_quiet(keytype == kRetirementKey && circle_name && retiree_peerid && isData(value), forget);
1680 pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
1681 require_quiet(pi && CFEqualSafe(retiree_peerid, SOSPeerInfoGetPeerID(pi)), forget);
1682
1683 require_quiet(!SOSAccountIsActivePeerInCircleNamed(account, circle_name, retiree_peerid, NULL), keep);
1684 require_quiet(SOSPeerInfoRetireRetirementTicket(seconds, pi), keep);
1685
1686 // Happy day, it's time and it's a ticket we should eradicate from KVS.
1687 CFDictionarySetValue(keysToWrite, resignationKey, kCFNull);
1688
1689 forget:
1690 CFDictionaryRemoveValue(account->retired_peers, resignationKey);
1691 keep:
1692 CFReleaseSafe(pi);
1693 CFReleaseSafe(circle_name);
1694 CFReleaseSafe(retiree_peerid);
1695 });
1696 CFReleaseNull(copyToIterate);
1697
1698 bool success = true;
1699 if(CFDictionaryGetCount(keysToWrite)) {
1700 success = account->update_block(keysToWrite, error);
1701 }
1702 CFReleaseNull(keysToWrite);
1703
1704 return success;
1705 }
1706
1707 bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
1708 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1709 CFStringRef key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(peer));
1710 if(key && !CFDictionaryGetValueIfPresent(account->retired_peers, key, NULL)) {
1711 CFDataRef value = SOSPeerInfoCopyEncodedData(peer, NULL, NULL);
1712 if(value) {
1713 CFDictionarySetValue(account->retired_peers, key, value);
1714 SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, error);
1715 }
1716 CFReleaseSafe(value);
1717 }
1718 CFReleaseSafe(key);
1719 });
1720 return true;
1721 }
1722
1723 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
1724 CFStringRef circle_to_mod = SOSCircleGetName(starting_circle);
1725 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1726 if(!new_circle) return NULL;
1727
1728 CFDictionaryForEach(account->retired_peers, ^(const void* resignationKey, const void* value) {
1729 CFStringRef circle_name = NULL;
1730 CFStringRef retiree_peerid = NULL;
1731
1732 SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL);
1733 if(keytype == kRetirementKey && CFEqualSafe(circle_name, circle_to_mod) && SOSCircleHasPeerWithID(new_circle, retiree_peerid, NULL)) {
1734 if(isData(value)) {
1735 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
1736 SOSCircleUpdatePeerInfo(new_circle, pi);
1737 CFReleaseSafe(pi);
1738 }
1739 }
1740 CFReleaseSafe(circle_name);
1741 CFReleaseSafe(retiree_peerid);
1742 });
1743
1744 if(SOSCircleCountPeers(new_circle) == 0) {
1745 SOSCircleResetToEmpty(new_circle, NULL);
1746 }
1747
1748 return new_circle;
1749 }
1750
1751
1752 //
1753 // Circle Finding
1754 //
1755 SOSCircleRef SOSAccountFindCompatibleCircle(SOSAccountRef a, CFStringRef name)
1756 {
1757 CFTypeRef entry = CFDictionaryGetValue(a->circles, name);
1758
1759 if (CFGetTypeID(entry) == SOSCircleGetTypeID())
1760 return (SOSCircleRef) entry;
1761
1762 return NULL;
1763 }
1764
1765 SOSCircleRef SOSAccountFindCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error)
1766 {
1767 CFTypeRef entry = CFDictionaryGetValue(a->circles, name);
1768
1769 require_action_quiet(!isNull(entry), fail,
1770 SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle in KVS"), NULL, error));
1771
1772 require_action_quiet(entry, fail,
1773 SOSCreateError(kSOSErrorNoCircle, CFSTR("No circle found"), NULL, error));
1774
1775
1776 return (SOSCircleRef) entry;
1777
1778 fail:
1779 return NULL;
1780 }
1781
1782 SOSCircleRef SOSAccountEnsureCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error)
1783 {
1784 CFErrorRef localError = NULL;
1785
1786 SOSCircleRef circle = SOSAccountFindCircle(a, name, &localError);
1787
1788 require_action_quiet(circle || !isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle), fail,
1789 if (error) { *error = localError; localError = NULL; });
1790
1791
1792 if (NULL == circle) {
1793 circle = SOSCircleCreate(NULL, name, NULL);
1794 if (circle){
1795 CFDictionaryAddValue(a->circles, name, circle);
1796 CFRelease(circle);
1797 circle = SOSAccountFindCircle(a, name, &localError);
1798 }
1799 SOSUpdateKeyInterest(a, false, NULL);
1800 }
1801
1802 fail:
1803 CFReleaseNull(localError);
1804 return circle;
1805 }
1806
1807
1808 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock)
1809 {
1810 CFArrayAppendValue(a->change_blocks, changeBlock);
1811 }
1812
1813 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock)
1814 {
1815 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
1816 }
1817
1818 static void DifferenceAndCall(CFArrayRef old_members, CFArrayRef new_members, void (^updatedCircle)(CFArrayRef additions, CFArrayRef removals))
1819 {
1820 CFMutableArrayRef additions = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, new_members);
1821 CFMutableArrayRef removals = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, old_members);
1822
1823
1824 CFArrayForEach(old_members, ^(const void * value) {
1825 CFArrayRemoveAllValue(additions, value);
1826 });
1827
1828 CFArrayForEach(new_members, ^(const void * value) {
1829 CFArrayRemoveAllValue(removals, value);
1830 });
1831
1832 updatedCircle(additions, removals);
1833
1834 CFReleaseSafe(additions);
1835 CFReleaseSafe(removals);
1836 }
1837
1838 static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle)
1839 {
1840 CFMutableArrayRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault);
1841 CFMutableArrayRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault);
1842
1843 CFMutableArrayRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault);
1844 CFMutableArrayRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault);
1845
1846 DifferenceAndCall(old_members, new_members, ^(CFArrayRef added_members, CFArrayRef removed_members) {
1847 DifferenceAndCall(old_applicants, new_applicants, ^(CFArrayRef added_applicants, CFArrayRef removed_applicants) {
1848 CFArrayForEach(account->change_blocks, ^(const void * notificationBlock) {
1849 ((SOSAccountCircleMembershipChangeBlock) notificationBlock)(newCircle, added_members, removed_members, added_applicants, removed_applicants);
1850 });
1851 });
1852 });
1853
1854 CFReleaseNull(old_applicants);
1855 CFReleaseNull(new_applicants);
1856
1857 CFReleaseNull(old_members);
1858 CFReleaseNull(new_members);
1859 }
1860
1861 void SOSAccountForEachCircle(SOSAccountRef account, void (^process)(SOSCircleRef circle))
1862 {
1863 CFDictionaryForEach(account->circles, ^(const void* key, const void* value) {
1864 assert(value);
1865 process((SOSCircleRef)value);
1866 });
1867 }
1868
1869 static void AppendCircleKeyName(CFMutableArrayRef array, CFStringRef name) {
1870 CFStringRef circle_key = SOSCircleKeyCreateWithName(name, NULL);
1871 CFArrayAppendValue(array, circle_key);
1872 CFReleaseNull(circle_key);
1873 }
1874
1875 static inline void AppendCircleInterests(CFMutableArrayRef circle_keys, CFMutableArrayRef retiree_keys, CFMutableArrayRef message_keys, SOSCircleRef circle, SOSFullPeerInfoRef me) {
1876 CFStringRef my_peer_id = NULL;
1877
1878 if (me) {
1879 SOSPeerInfoRef my_peer = me ? SOSFullPeerInfoGetPeerInfo(me) : NULL;
1880 my_peer_id = SOSPeerInfoGetPeerID(my_peer);
1881 }
1882
1883 if (circle_keys) {
1884 CFStringRef circleName = SOSCircleGetName(circle);
1885 AppendCircleKeyName(circle_keys, circleName);
1886 }
1887
1888 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1889 if (!CFEqualSafe(my_peer_id, SOSPeerInfoGetPeerID(peer))) {
1890 CFStringRef peer_name = SOSPeerInfoGetPeerID(peer);
1891 if (retiree_keys) {
1892 CFStringRef retirementKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, peer_name);
1893 CFArrayAppendValue(retiree_keys, retirementKey);
1894 CFReleaseNull(retirementKey);
1895 }
1896
1897 if (my_peer_id && message_keys) {
1898 CFStringRef messageKey = SOSMessageKeyCreateWithCircleAndPeerNames(circle, peer_name, my_peer_id);
1899 CFArrayAppendValue(message_keys, messageKey);
1900 CFRelease(messageKey);
1901 }
1902 }
1903 });
1904 }
1905
1906 static void SOSAccountCopyKeyInterests(SOSAccountRef account,
1907 CFMutableArrayRef alwaysKeys,
1908 CFMutableArrayRef afterFirstUnlockKeys,
1909 CFMutableArrayRef whenUnlockedKeys)
1910 {
1911 CFArrayAppendValue(afterFirstUnlockKeys, kSOSKVSKeyParametersKey);
1912
1913 SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
1914 AppendCircleKeyName(afterFirstUnlockKeys, name);
1915 }, ^(SOSCircleRef circle) {
1916 AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, whenUnlockedKeys, circle, NULL);
1917 }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
1918 bool inCircle = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(full_peer), NULL);
1919
1920 AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, inCircle ? whenUnlockedKeys : NULL, circle, full_peer);
1921 });
1922 }
1923
1924 static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error)
1925 {
1926 if (account->update_interest_block) {
1927
1928 CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1929 CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1930 CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1931
1932 SOSAccountCopyKeyInterests(account, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys);
1933
1934 account->update_interest_block(getNewKeysOnly, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys);
1935
1936 CFReleaseNull(alwaysKeys);
1937 CFReleaseNull(afterFirstUnlockKeys);
1938 CFReleaseNull(whenUnlockedKeys);
1939 }
1940
1941 return true;
1942 }
1943
1944 static bool SOSAccountSendPendingChanges(SOSAccountRef account, CFErrorRef *error) {
1945 CFErrorRef changeError = NULL;
1946
1947 if (CFDictionaryGetCount(account->pending_changes) == 0)
1948 return true;
1949
1950 bool success = account->update_block(account->pending_changes, &changeError);
1951 if (success) {
1952 CFDictionaryRemoveAllValues(account->pending_changes);
1953 } else {
1954 SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
1955 CFSTR("Send changes block failed [%@]"), account->pending_changes);
1956 }
1957
1958 return success;
1959 }
1960
1961 static bool SOSAccountAddCircleToPending(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error)
1962 {
1963 bool success = false;
1964 CFDataRef circle_data = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, error);
1965
1966 if (circle_data) {
1967 CFStringRef circle_key = SOSCircleKeyCreateWithCircle(circle, NULL);
1968
1969 CFDictionarySetValue(account->pending_changes, circle_key, circle_data);
1970 success = true;
1971
1972 CFReleaseNull(circle_data);
1973 CFReleaseNull(circle_key);
1974 }
1975
1976 return success;
1977 }
1978
1979
1980 static void SOSAccountRecordRetiredPeerInCircleNamed(SOSAccountRef account, CFStringRef circleName, SOSPeerInfoRef retiree)
1981 {
1982 // Replace Peer with RetiredPeer, if were a peer.
1983 SOSAccountModifyCircle(account, circleName, NULL, ^(SOSCircleRef circle) {
1984 if (SOSCircleUpdatePeerInfo(circle, retiree)) {
1985 CFErrorRef cleanupError = NULL;
1986 SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError);
1987 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1988 CFReleaseSafe(cleanupError);
1989 }
1990 });
1991 }
1992
1993 static bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1994 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL);
1995 if(!fpi) return false;
1996 CFErrorRef localError = NULL;
1997 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1998 CFStringRef retire_key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(pi));
1999 SOSPeerInfoRef retire_peer = NULL;
2000 CFDataRef retire_value = NULL;
2001 bool retval = false;
2002 bool writeCircle = false;
2003
2004 // Create a Retirement Ticket and store it in the retired_peers of the account.
2005 retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
2006 require_action_quiet(retire_peer, errout, secerror("Create ticket failed for peer %@: %@", fpi, localError));
2007 retire_value = SOSPeerInfoCopyEncodedData(retire_peer, NULL, &localError);
2008 require_action_quiet(retire_value, errout, secerror("Failed to encode retirement peer %@: %@", retire_peer, localError));
2009
2010 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
2011 if(SOSCircleHasApplicant(circle, pi, NULL)) {
2012 // Remove our application if we have one.
2013 SOSCircleWithdrawRequest(circle, pi, NULL);
2014 writeCircle = true;
2015 } else if (SOSCircleHasPeer(circle, pi, NULL)) {
2016 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
2017 CFErrorRef cleanupError = NULL;
2018 SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError);
2019 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
2020 CFReleaseSafe(cleanupError);
2021 }
2022 writeCircle = true;
2023 }
2024
2025 // Store the retirement record locally.
2026 CFDictionarySetValue(account->retired_peers, retire_key, retire_value);
2027
2028 // Write pending change to KVS
2029 CFDictionarySetValue(account->pending_changes, retire_key, retire_value);
2030
2031 // Kill peer key but don't return error if we can't.
2032 if(!SOSAccountDestroyCirclePeerInfo(account, circle, &localError))
2033 secerror("Couldn't purge key for peer %@ on retirement: %@", fpi, localError);
2034
2035 if (writeCircle) {
2036 SOSAccountAddCircleToPending(account, circle, NULL);
2037 }
2038 retval = true;
2039
2040 errout:
2041 CFReleaseNull(localError);
2042 CFReleaseNull(retire_peer);
2043 CFReleaseNull(retire_key);
2044 CFReleaseNull(retire_value);
2045 return retval;
2046 }
2047
2048 /*
2049 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
2050 local value that has been overwritten by a distant value. If there is no
2051 conflict between the local and the distant values when doing the initial
2052 sync (e.g. if the cloud has no data stored or the client has not stored
2053 any data yet), you'll never see that notification.
2054
2055 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
2056 with server but initial round trip with server does not imply
2057 NSUbiquitousKeyValueStoreInitialSyncChange.
2058 */
2059
2060 //
2061 // MARK: Handle Circle Updates
2062 //
2063
2064
2065 static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef prospective_circle, bool writeUpdate, bool initialSync, CFErrorRef *error)
2066 {
2067 bool success = true;
2068
2069 secnotice("signing", "start: %@", prospective_circle);
2070 if (!account->user_public || !account->user_public_trusted) {
2071 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Can't handle updates with no trusted public key here"), NULL, error);
2072 return false;
2073 }
2074
2075 if (!prospective_circle) {
2076 secerror("##### Can't update to a NULL circle ######");
2077 return false; // Can't update one we don't have.
2078 }
2079
2080 CFStringRef newCircleName = SOSCircleGetName(prospective_circle);
2081 SOSCircleRef oldCircle = SOSAccountFindCompatibleCircle(account, newCircleName);
2082 SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInCircle(account, oldCircle, NULL);
2083 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full);
2084
2085 if (initialSync)
2086 secerror("##### Processing initial sync. Old (local) circle: %@, New (cloud) circle: %@", oldCircle, prospective_circle);
2087
2088 if (!oldCircle)
2089 return false; // Can't update one we don't have.
2090
2091 SOSAccountScanForRetired(account, prospective_circle, error);
2092 SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error);
2093 if(!newCircle) return false;
2094
2095 SOSCircleUpdatePeerInfo(newCircle, me);
2096
2097 typedef enum {
2098 accept,
2099 countersign,
2100 leave,
2101 revert,
2102 ignore
2103 } circle_action_t;
2104
2105 circle_action_t circle_action = ignore;
2106 enum DepartureReason leave_reason = kSOSNeverLeftCircle;
2107
2108 SecKeyRef old_circle_key = NULL;
2109 if(SOSCircleVerify(oldCircle, account->user_public, NULL)) old_circle_key = account->user_public;
2110 else if(account->previous_public && SOSCircleVerify(oldCircle, account->previous_public, NULL)) old_circle_key = account->previous_public;
2111 bool userTrustedOldCircle = (old_circle_key != NULL);
2112
2113 SOSConcordanceStatus concstat =
2114 SOSCircleConcordanceTrust(oldCircle, newCircle,
2115 old_circle_key, account->user_public,
2116 me, error);
2117
2118 CFStringRef concStr = NULL;
2119 switch(concstat) {
2120 case kSOSConcordanceTrusted:
2121 circle_action = countersign;
2122 concStr = CFSTR("Trusted");
2123 break;
2124 case kSOSConcordanceGenOld:
2125 circle_action = userTrustedOldCircle ? revert : ignore;
2126 concStr = CFSTR("Generation Old");
2127 break;
2128 case kSOSConcordanceBadUserSig:
2129 case kSOSConcordanceBadPeerSig:
2130 circle_action = userTrustedOldCircle ? revert : accept;
2131 concStr = CFSTR("Bad Signature");
2132 break;
2133 case kSOSConcordanceNoUserSig:
2134 circle_action = userTrustedOldCircle ? revert : accept;
2135 concStr = CFSTR("No User Signature");
2136 break;
2137 case kSOSConcordanceNoPeerSig:
2138 circle_action = accept; // We might like this one eventually but don't countersign.
2139 concStr = CFSTR("No trusted peer signature");
2140 secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle);
2141 break;
2142 case kSOSConcordanceNoPeer:
2143 circle_action = leave;
2144 leave_reason = kSOSLeftUntrustedCircle;
2145 concStr = CFSTR("No trusted peer left");
2146 break;
2147 case kSOSConcordanceNoUserKey:
2148 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
2149 abort();
2150 break;
2151 default:
2152 secerror("##### Bad Error Return from ConcordanceTrust");
2153 abort();
2154 break;
2155 }
2156
2157 secnotice("signing", "Decided on action %d based on concordance state %d and %s circle.", circle_action, concstat, userTrustedOldCircle ? "trusted" : "untrusted");
2158
2159 SOSCircleRef circleToPush = NULL;
2160
2161 if (circle_action == leave) {
2162 circle_action = ignore;
2163
2164 if (me && SOSCircleHasPeer(oldCircle, me, NULL)) {
2165 if (sosAccountLeaveCircle(account, newCircle, error)) {
2166 account->departure_code = leave_reason;
2167 circleToPush = newCircle;
2168 circle_action = accept;
2169 me = NULL;
2170 me_full = NULL;
2171 }
2172 }
2173 else {
2174 // We are not in this circle, but we need to update account with it, since we got it from cloud
2175 secnotice("updatecircle", "We are not in this circle, but we need to update account with it");
2176 circle_action = accept;
2177 }
2178 }
2179
2180 if (circle_action == countersign) {
2181 if (me && SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleVerifyPeerSigned(newCircle, me, NULL)) {
2182 CFErrorRef signing_error = NULL;
2183
2184 if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) {
2185 circleToPush = newCircle;
2186 secnotice("signing", "Concurred with: %@", newCircle);
2187 } else {
2188 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error, oldCircle, newCircle);
2189 }
2190 CFReleaseSafe(signing_error);
2191 }
2192 circle_action = accept;
2193 }
2194
2195 if (circle_action == accept) {
2196 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) {
2197 // Don't destroy evidence of other code determining reason for leaving.
2198 if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked;
2199 }
2200
2201 if (me
2202 && SOSCircleHasActivePeer(oldCircle, me, NULL)
2203 && !(SOSCircleCountPeers(oldCircle) == 1 && SOSCircleHasPeer(oldCircle, me, NULL)) // If it was our offering, don't change ID to avoid ghosts
2204 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
2205 secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
2206 SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
2207 me = NULL;
2208 me_full = NULL;
2209 }
2210
2211 if (me && SOSCircleHasRejectedApplicant(newCircle, me, NULL)) {
2212 SOSPeerInfoRef reject = SOSCircleCopyRejectedApplicant(newCircle, me, NULL);
2213 if(CFEqualSafe(reject, me) && SOSPeerInfoApplicationVerify(me, account->user_public, NULL)) {
2214 secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle));
2215 SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL);
2216 me = NULL;
2217 me_full = NULL;
2218 } else {
2219 SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL);
2220 writeUpdate = true;
2221 }
2222 }
2223
2224 CFRetain(oldCircle); // About to replace the oldCircle
2225 CFDictionarySetValue(account->circles, newCircleName, newCircle);
2226 SOSAccountSetPreviousPublic(account);
2227
2228 secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle);
2229
2230 if (me_full && account->user_public_trusted
2231 && SOSCircleHasApplicant(oldCircle, me, NULL)
2232 && SOSCircleCountPeers(newCircle) > 0
2233 && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) {
2234 // We weren't rejected (above would have set me to NULL.
2235 // We were applying and we weren't accepted.
2236 // Our application is declared lost, let us reapply.
2237
2238 if (SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL))
2239 writeUpdate = true;
2240 }
2241
2242 if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) {
2243 SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL);
2244 }
2245
2246 SOSAccountNotifyOfChange(account, oldCircle, newCircle);
2247
2248 CFReleaseNull(oldCircle);
2249
2250 if (writeUpdate)
2251 circleToPush = newCircle;
2252
2253 success = SOSUpdateKeyInterest(account, true, error);
2254 }
2255
2256 if (circle_action == revert) {
2257 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr, newCircle, oldCircle);
2258
2259 circleToPush = oldCircle;
2260 }
2261
2262
2263 if (circleToPush != NULL) {
2264 success = (success
2265 && SOSAccountAddCircleToPending(account, circleToPush, error)
2266 && SOSAccountSendPendingChanges(account, error));
2267 }
2268
2269 CFReleaseSafe(newCircle);
2270
2271 return success;
2272 }
2273
2274 static bool SOSAccountUpdateCircleFromRemote(SOSAccountRef account, SOSCircleRef newCircle, bool initialSync, CFErrorRef *error)
2275 {
2276 return SOSAccountHandleUpdateCircle(account, newCircle, false, initialSync, error);
2277 }
2278
2279 bool SOSAccountUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, CFErrorRef *error)
2280 {
2281 return SOSAccountHandleUpdateCircle(account, newCircle, true, false, error);
2282 }
2283
2284 bool SOSAccountModifyCircle(SOSAccountRef account,
2285 CFStringRef circleName,
2286 CFErrorRef* error,
2287 void (^action)(SOSCircleRef circle))
2288 {
2289 bool success = false;
2290
2291 SOSCircleRef circle = NULL;
2292 SOSCircleRef accountCircle = SOSAccountFindCircle(account, circleName, error);
2293 require_quiet(accountCircle, fail);
2294
2295 circle = SOSCircleCopyCircle(kCFAllocatorDefault, accountCircle, error);
2296 require_quiet(circle, fail);
2297
2298 action(circle);
2299 success = SOSAccountUpdateCircle(account, circle, error);
2300
2301 fail:
2302 CFReleaseSafe(circle);
2303 return success;
2304 }
2305
2306 static SOSCircleRef SOSAccountCreateCircleFrom(CFStringRef circleName, CFTypeRef value, CFErrorRef *error) {
2307 if (value && !isData(value) && !isNull(value)) {
2308 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(value));
2309 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
2310 CFSTR("Expected data or NULL got %@"), description);
2311 CFReleaseSafe(description);
2312 return NULL;
2313 }
2314
2315 SOSCircleRef circle = NULL;
2316 if (!value || isNull(value)) {
2317 circle = SOSCircleCreate(kCFAllocatorDefault, circleName, error);
2318 } else {
2319 circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error);
2320 if (circle) {
2321 CFStringRef name = SOSCircleGetName(circle);
2322 if (!CFEqualSafe(name, circleName)) {
2323 SOSCreateErrorWithFormat(kSOSErrorNameMismatch, NULL, error, NULL,
2324 CFSTR("Expected circle named %@, got %@"), circleName, name);
2325 CFReleaseNull(circle);
2326 }
2327 }
2328 }
2329 return circle;
2330 }
2331
2332 static SOSCCStatus SOSCCCircleStatus(SOSCircleRef circle)
2333 {
2334 if (SOSCircleCountPeers(circle) == 0)
2335 return kSOSCCCircleAbsent;
2336
2337 return kSOSCCNotInCircle;
2338 }
2339
2340 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer)
2341 {
2342 if (SOSCircleCountPeers(circle) == 0)
2343 return kSOSCCCircleAbsent;
2344
2345 if (SOSCircleHasPeer(circle, this_peer, NULL))
2346 return kSOSCCInCircle;
2347
2348 if (SOSCircleHasApplicant(circle, this_peer, NULL))
2349 return kSOSCCRequestPending;
2350
2351 return kSOSCCNotInCircle;
2352 }
2353
2354 static SOSCCStatus UnionStatus(SOSCCStatus accumulated_status, SOSCCStatus additional_circle_status)
2355 {
2356 switch (additional_circle_status) {
2357 case kSOSCCInCircle:
2358 return accumulated_status;
2359 case kSOSCCRequestPending:
2360 return (accumulated_status == kSOSCCInCircle) ?
2361 kSOSCCRequestPending :
2362 accumulated_status;
2363 case kSOSCCNotInCircle:
2364 return (accumulated_status == kSOSCCInCircle ||
2365 accumulated_status == kSOSCCRequestPending) ?
2366 kSOSCCNotInCircle :
2367 accumulated_status;
2368 case kSOSCCCircleAbsent:
2369 return (accumulated_status == kSOSCCInCircle ||
2370 accumulated_status == kSOSCCRequestPending ||
2371 accumulated_status == kSOSCCNotInCircle) ?
2372 kSOSCCCircleAbsent :
2373 accumulated_status;
2374 default:
2375 return additional_circle_status;
2376 };
2377
2378 }
2379
2380 SOSCCStatus SOSAccountIsInCircles(SOSAccountRef account, CFErrorRef* error)
2381 {
2382 if (!SOSAccountHasPublicKey(account, error)) {
2383 return kSOSCCError;
2384 }
2385
2386 __block bool set_once = false;
2387 __block SOSCCStatus status = kSOSCCInCircle;
2388
2389 SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
2390 set_once = true;
2391 status = kSOSCCError;
2392 SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error);
2393 }, ^(SOSCircleRef circle) {
2394 set_once = true;
2395 status = UnionStatus(status, SOSCCCircleStatus(circle));
2396 }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2397 set_once = true;
2398 SOSCCStatus circle_status = SOSCCThisDeviceStatusInCircle(circle, SOSFullPeerInfoGetPeerInfo(full_peer));
2399 status = UnionStatus(status, circle_status);
2400 });
2401
2402 if (!set_once)
2403 status = kSOSCCCircleAbsent;
2404
2405 return status;
2406 }
2407
2408 static SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) {
2409 SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error);
2410 SOSPeerInfoRef cloud_peer = NULL;
2411 CFDictionaryRef query = NULL;
2412 CFDictionaryRef change = NULL;
2413 CFStringRef new_name = NULL;
2414
2415 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2416 kPIUserDefinedDeviceName, CFSTR("iCloud"),
2417 NULL);
2418 require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt")));
2419
2420 cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error);
2421
2422 require(cloud_peer, fail);
2423
2424 query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2425 kSecClass, kSecClassKey,
2426 kSecAttrSynchronizable,kCFBooleanTrue,
2427 kSecUseTombstones, kCFBooleanTrue,
2428 kSecValueRef, cloud_key,
2429 NULL);
2430
2431 new_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
2432 CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(cloud_peer));
2433
2434 change = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
2435 kSecAttrLabel, new_name,
2436 NULL);
2437
2438 SecError(SecItemUpdate(query, change), error, CFSTR("Couldn't update name"));
2439
2440 fail:
2441 CFReleaseNull(new_name);
2442 CFReleaseNull(query);
2443 CFReleaseNull(change);
2444 CFReleaseNull(gestalt);
2445 CFReleaseNull(cloud_key);
2446
2447 return cloud_peer;
2448 }
2449
2450 static SOSFullPeerInfoRef CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer, CFErrorRef *error) {
2451 return SOSFullPeerInfoCreateCloudIdentity(NULL, cloudPeer, error);
2452 }
2453
2454 static bool SOSAccountResetThisCircleToOffering(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
2455 SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2456 if (!myCirclePeer)
2457 return false;
2458
2459 SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2460 bool result = false;
2461 SOSFullPeerInfoRef cloud_identity = NULL;
2462 CFErrorRef localError = NULL;
2463
2464 require_quiet(SOSCircleResetToOffering(circle, user_key, myCirclePeer, &localError), err_out);
2465
2466 {
2467 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
2468 require_quiet(cloud_peer, err_out);
2469 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
2470 require_quiet(cloud_identity, err_out);
2471 }
2472
2473 account->departure_code = kSOSNeverLeftCircle;
2474 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
2475 require_quiet(SOSCircleAcceptRequest(circle, user_key, myCirclePeer, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
2476 result = true;
2477 SOSAccountPublishCloudParameters(account, NULL);
2478
2479 err_out:
2480 if (result == false)
2481 secerror("error resetting circle (%@) to offering: %@", circle, localError);
2482 if (localError && error && *error == NULL) {
2483 *error = localError;
2484 localError = NULL;
2485 }
2486 CFReleaseNull(localError);
2487 CFReleaseNull(cloud_identity);
2488 });
2489
2490 return true;
2491 }
2492
2493 static bool SOSAccountJoinThisCircle(SOSAccountRef account, SecKeyRef user_key,
2494 SOSCircleRef circle, bool use_cloud_peer, CFErrorRef* error) {
2495 __block bool result = false;
2496 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
2497
2498 SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2499 require_action_quiet(myCirclePeer, fail,
2500 SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Can't find/create peer for circle: %@"), circle));
2501 if (use_cloud_peer) {
2502 cloud_full_peer = SOSCircleGetiCloudFullPeerInfoRef(circle);
2503 }
2504
2505 if (SOSCircleCountPeers(circle) == 0) {
2506 result = SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2507 } else {
2508 SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2509 result = SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
2510 account->departure_code = kSOSNeverLeftCircle;
2511 if(result && cloud_full_peer) {
2512 CFErrorRef localError = NULL;
2513 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
2514 require_quiet(cloudid, finish);
2515 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
2516 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
2517 finish:
2518 if (localError){
2519 secerror("Failed to join with cloud identity: %@", localError);
2520 CFReleaseNull(localError);
2521 }
2522 }
2523 });
2524 }
2525
2526 fail:
2527 CFReleaseNull(cloud_full_peer);
2528 return result;
2529 }
2530
2531 static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
2532 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2533 if (!user_key)
2534 return false;
2535
2536 __block bool success = true;
2537
2538 SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { // Incompatible
2539 success = false;
2540 SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error);
2541 }, ^(SOSCircleRef circle) { // No Peer
2542 success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success;
2543 }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { // Have Peer
2544 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(full_peer);
2545 if(SOSCircleHasPeer(circle, myPeer, NULL)) goto already_present;
2546 if(SOSCircleHasApplicant(circle, myPeer, NULL)) goto already_applied;
2547 if(SOSCircleHasRejectedApplicant(circle, myPeer, NULL)) {
2548 SOSCircleRemoveRejectedPeer(circle, myPeer, NULL);
2549 }
2550
2551 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(circle));
2552 CFErrorRef localError = NULL;
2553 if (!SOSAccountDestroyCirclePeerInfo(account, circle, &localError)) {
2554 secerror("Failed to destroy peer (%@) during application, error=%@", myPeer, localError);
2555 CFReleaseNull(localError);
2556 }
2557 already_applied:
2558 success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success;
2559 return;
2560 already_present:
2561 success = true;
2562 return;
2563 });
2564
2565 if(success) account->departure_code = kSOSNeverLeftCircle;
2566
2567 return success;
2568 }
2569
2570 bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
2571 return SOSAccountJoinCircles_internal(account, false, error);
2572 }
2573
2574
2575 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
2576 return SOSAccountJoinCircles_internal(account, true, error);
2577 }
2578
2579
2580 bool SOSAccountLeaveCircles(SOSAccountRef account, CFErrorRef* error)
2581 {
2582 __block bool result = true;
2583
2584 SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) {
2585 SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2586 result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors!
2587 });
2588 });
2589
2590 account->departure_code = kSOSWithdrewMembership;
2591
2592 return SOSAccountSendPendingChanges(account, error) && result;
2593 }
2594
2595 bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error)
2596 {
2597 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2598 dispatch_group_t group = dispatch_group_create();
2599 __block bool result = false;
2600
2601 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
2602 // Add a task to the group
2603 dispatch_group_async(group, queue, ^{
2604 SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) {
2605 SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2606 result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors!
2607 });
2608 });
2609
2610 account->departure_code = kSOSWithdrewMembership;
2611 if(result) result = SOSAccountSendPendingChanges(account, error);
2612 });
2613 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
2614
2615 dispatch_group_wait(group, milestone);
2616 dispatch_release(group);
2617 return result;
2618 }
2619
2620
2621 static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
2622 void (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
2623 SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2624 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(full_peer);
2625 CFErrorRef peer_error = NULL;
2626 if (SOSCircleHasPeer(circle, me, &peer_error)) {
2627 CFArrayForEach(peer_infos, ^(const void *value) {
2628 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
2629 if (SOSCircleHasApplicant(circle, peer, NULL)) {
2630 SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) {
2631 action(circle, full_peer, peer);
2632 });
2633 }
2634 });
2635 }
2636 if (peer_error)
2637 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
2638 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
2639 });
2640 }
2641
2642 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
2643 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2644 if (!user_key)
2645 return false;
2646
2647 __block bool success = true;
2648 __block int64_t num_peers = 0;
2649
2650 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
2651 if (!SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error))
2652 success = false;
2653 else
2654 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
2655 });
2656
2657 return success;
2658 }
2659
2660 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
2661 __block bool success = true;
2662 __block int64_t num_peers = 0;
2663
2664 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
2665 if (!SOSCircleRejectRequest(circle, myCirclePeer, peer, error))
2666 success = false;
2667 else
2668 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
2669 });
2670
2671 return success;
2672 }
2673
2674 bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
2675 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
2676 if (!user_key)
2677 return false;
2678
2679 __block bool result = true;
2680
2681 SOSAccountForEachKnownCircle(account, ^(CFStringRef name) {
2682 SOSCircleRef circle = SOSCircleCreate(NULL, name, NULL);
2683 if (circle)
2684 CFDictionaryAddValue(account->circles, name, circle);
2685
2686 SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2687 }, ^(SOSCircleRef circle) {
2688 SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2689 }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) {
2690 SOSAccountResetThisCircleToOffering(account, circle, user_key, error);
2691 });
2692
2693 return result;
2694 }
2695
2696 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
2697 if (!SOSAccountHasPublicKey(account, error))
2698 return false;
2699
2700 __block bool result = true;
2701 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2702 SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) {
2703 if (!SOSCircleResetToEmpty(circle, error))
2704 {
2705 secerror("error: %@", *error);
2706 result = false;
2707 }
2708 account->departure_code = kSOSWithdrewMembership;
2709 });
2710 });
2711
2712 return result;
2713 }
2714
2715 CFArrayRef SOSAccountCopyApplicants(SOSAccountRef account, CFErrorRef *error) {
2716 if (!SOSAccountHasPublicKey(account, error))
2717 return NULL;
2718 CFMutableArrayRef applicants = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2719
2720 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2721 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
2722 CFArrayAppendValue(applicants, peer);
2723 });
2724 });
2725
2726 return applicants;
2727 }
2728
2729 CFArrayRef SOSAccountCopyPeers(SOSAccountRef account, CFErrorRef *error) {
2730 if (!SOSAccountHasPublicKey(account, error))
2731 return NULL;
2732
2733 CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2734
2735 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2736 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
2737 CFArrayAppendValue(peers, peer);
2738 });
2739 });
2740
2741 return peers;
2742 }
2743
2744 CFArrayRef SOSAccountCopyActivePeers(SOSAccountRef account, CFErrorRef *error) {
2745 if (!SOSAccountHasPublicKey(account, error))
2746 return NULL;
2747
2748 CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2749
2750 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2751 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
2752 CFArrayAppendValue(peers, peer);
2753 });
2754 });
2755
2756 return peers;
2757 }
2758
2759 CFArrayRef SOSAccountCopyActiveValidPeers(SOSAccountRef account, CFErrorRef *error) {
2760 if (!SOSAccountHasPublicKey(account, error))
2761 return NULL;
2762
2763 CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2764
2765 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2766 SOSCircleForEachActiveValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
2767 CFArrayAppendValue(peers, peer);
2768 });
2769 });
2770
2771 return peers;
2772 }
2773
2774
2775 CFArrayRef SOSAccountCopyConcurringPeers(SOSAccountRef account, CFErrorRef *error)
2776 {
2777 if (!SOSAccountHasPublicKey(account, error))
2778 return NULL;
2779
2780 CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2781
2782 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
2783 CFMutableArrayRef circleConcurring = SOSCircleCopyConcurringPeers(circle, NULL);
2784 CFArrayAppendArray(concurringPeers, circleConcurring, CFRangeMake(0, CFArrayGetCount(circleConcurring)));
2785 CFReleaseSafe(circleConcurring);
2786 });
2787
2788 return concurringPeers;
2789 }
2790
2791 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error)
2792 {
2793 return CFSTR("We're compatible, go away");
2794 }
2795
2796 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error)
2797 {
2798 return account->departure_code;
2799 }
2800
2801 //
2802 // TODO: Handle '|' and "¬" in other strings.
2803 //
2804 const CFStringRef kSOSKVSKeyParametersKey = CFSTR(">KeyParameters");
2805 const CFStringRef kSOSKVSInitialSyncKey = CFSTR("^InitialSync");
2806 const CFStringRef kSOSKVSAccountChangedKey = CFSTR("^AccountChanged");
2807
2808 const CFStringRef sWarningPrefix = CFSTR("!");
2809 const CFStringRef sAncientCirclePrefix = CFSTR("@");
2810 const CFStringRef sCirclePrefix = CFSTR("o");
2811 const CFStringRef sRetirementPrefix = CFSTR("-");
2812 const CFStringRef sCircleSeparator = CFSTR("|");
2813 const CFStringRef sFromToSeparator = CFSTR(":");
2814
2815 static CFStringRef stringEndingIn(CFMutableStringRef in, CFStringRef token) {
2816 if(token == NULL) return CFStringCreateCopy(NULL, in);
2817 CFRange tokenAt = CFStringFind(in, token, 0);
2818 if(tokenAt.location == kCFNotFound) return NULL;
2819 CFStringRef retval = CFStringCreateWithSubstring(NULL, in, CFRangeMake(0, tokenAt.location));
2820 CFStringDelete(in, CFRangeMake(0, tokenAt.location+1));
2821 return retval;
2822 }
2823
2824 SOSKVSKeyType SOSKVSKeyGetKeyTypeAndParse(CFStringRef key, CFStringRef *circle, CFStringRef *from, CFStringRef *to)
2825 {
2826 SOSKVSKeyType retval = kUnknownKey;
2827
2828 if(CFStringHasPrefix(key, sCirclePrefix)) retval = kCircleKey;
2829 else if(CFStringHasPrefix(key, sRetirementPrefix)) retval = kRetirementKey;
2830 else if(CFStringHasPrefix(key, kSOSKVSKeyParametersKey)) retval = kParametersKey;
2831 else if(CFStringHasPrefix(key, kSOSKVSInitialSyncKey)) retval = kInitialSyncKey;
2832 else if(CFStringHasPrefix(key, kSOSKVSAccountChangedKey)) retval = kAccountChangedKey;
2833 else retval = kMessageKey;
2834
2835 switch(retval) {
2836 case kCircleKey:
2837 if (circle) {
2838 CFRange fromRange = CFRangeMake(1, CFStringGetLength(key)-1);
2839 *circle = CFStringCreateWithSubstring(NULL, key, fromRange);
2840 }
2841 break;
2842 case kMessageKey: {
2843 CFStringRef mCircle = NULL;
2844 CFStringRef mFrom = NULL;
2845 CFStringRef mTo = NULL;
2846 CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key);
2847
2848 if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) &&
2849 ((mFrom = stringEndingIn(keycopy, sFromToSeparator)) != NULL) &&
2850 (CFStringGetLength(mFrom) > 0) ) {
2851 mTo = stringEndingIn(keycopy, NULL);
2852 if (circle) *circle = CFStringCreateCopy(NULL, mCircle);
2853 if (from) *from = CFStringCreateCopy(NULL, mFrom);
2854 if (to && mTo) *to = CFStringCreateCopy(NULL, mTo);
2855 } else {
2856 retval = kUnknownKey;
2857 }
2858 CFReleaseNull(mCircle);
2859 CFReleaseNull(mFrom);
2860 CFReleaseNull(mTo);
2861 CFReleaseNull(keycopy);
2862 }
2863 break;
2864 case kRetirementKey: {
2865 CFStringRef mCircle = NULL;
2866 CFStringRef mPeer = NULL;
2867 CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key);
2868 CFStringDelete(keycopy, CFRangeMake(0, 1));
2869 if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) &&
2870 ((mPeer = stringEndingIn(keycopy, NULL)) != NULL)) {
2871 if (circle) *circle = CFStringCreateCopy(NULL, mCircle);
2872 if (from) *from = CFStringCreateCopy(NULL, mPeer);
2873 } else {
2874 retval = kUnknownKey;
2875 }
2876 // TODO - Update our circle
2877 CFReleaseNull(mCircle);
2878 CFReleaseNull(mPeer);
2879 CFReleaseNull(keycopy);
2880 }
2881 break;
2882 case kAccountChangedKey:
2883 case kParametersKey:
2884 case kInitialSyncKey:
2885 case kUnknownKey:
2886 break;
2887 }
2888
2889 return retval;
2890 }
2891
2892
2893 SOSKVSKeyType SOSKVSKeyGetKeyType(CFStringRef key)
2894 {
2895 return SOSKVSKeyGetKeyTypeAndParse(key, NULL, NULL, NULL);
2896 }
2897
2898 CFStringRef SOSCircleKeyCreateWithCircle(SOSCircleRef circle, CFErrorRef *error)
2899 {
2900 return SOSCircleKeyCreateWithName(SOSCircleGetName(circle), error);
2901 }
2902
2903
2904 CFStringRef SOSCircleKeyCreateWithName(CFStringRef circleName, CFErrorRef *error)
2905 {
2906 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), sCirclePrefix, circleName);
2907 }
2908
2909 CFStringRef SOSCircleKeyCopyCircleName(CFStringRef key, CFErrorRef *error)
2910 {
2911 CFStringRef circleName = NULL;
2912
2913 if (kCircleKey != SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL)) {
2914 SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key);
2915
2916 CFReleaseNull(circleName);
2917 }
2918
2919 return circleName;
2920 }
2921
2922 CFStringRef SOSMessageKeyCopyCircleName(CFStringRef key, CFErrorRef *error)
2923 {
2924 CFStringRef circleName = NULL;
2925
2926 if (SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL) != kMessageKey) {
2927 SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key);
2928
2929 CFReleaseNull(circleName);
2930 }
2931 return circleName;
2932 }
2933
2934 CFStringRef SOSMessageKeyCopyFromPeerName(CFStringRef messageKey, CFErrorRef *error)
2935 {
2936 CFStringRef fromPeer = NULL;
2937
2938 if (SOSKVSKeyGetKeyTypeAndParse(messageKey, NULL, &fromPeer, NULL) != kMessageKey) {
2939 SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find from peer in key '%@'"), messageKey);
2940
2941 CFReleaseNull(fromPeer);
2942 }
2943 return fromPeer;
2944 }
2945
2946 CFStringRef SOSMessageKeyCreateWithCircleAndPeerNames(SOSCircleRef circle, CFStringRef from_peer_name, CFStringRef to_peer_name)
2947 {
2948 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@%@"),
2949 SOSCircleGetName(circle), sCircleSeparator, from_peer_name, sFromToSeparator, to_peer_name);
2950 }
2951
2952 CFStringRef SOSMessageKeyCreateWithCircleAndPeerInfos(SOSCircleRef circle, SOSPeerInfoRef from_peer, SOSPeerInfoRef to_peer)
2953 {
2954 return SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(from_peer), SOSPeerInfoGetPeerID(to_peer));
2955 }
2956
2957 CFStringRef SOSMessageKeyCreateWithAccountAndPeer(SOSAccountRef account, SOSCircleRef circle, CFStringRef peer_name) {
2958 // TODO: Handle errors!
2959 CFErrorRef error = NULL;
2960
2961 SOSFullPeerInfoRef me = SOSAccountGetMyFullPeerInCircle(account, circle, &error);
2962 SOSPeerInfoRef my_pi = SOSFullPeerInfoGetPeerInfo(me);
2963 CFStringRef result = SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(my_pi), peer_name);
2964 CFReleaseSafe(error);
2965 return result;
2966 }
2967
2968 CFStringRef SOSRetirementKeyCreateWithCircleAndPeer(SOSCircleRef circle, CFStringRef retirement_peer_name)
2969 {
2970 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@"),
2971 sRetirementPrefix, SOSCircleGetName(circle), sCircleSeparator, retirement_peer_name);
2972 }
2973
2974
2975 static SOSPeerCoderStatus SOSAccountHandlePeerMessage(SOSAccountRef account,
2976 CFStringRef circle_id,
2977 CFStringRef peer_name,
2978 CFDataRef message,
2979 SOSAccountSendBlock send_block,
2980 CFErrorRef *error)
2981 {
2982 bool success = false;
2983 CFStringRef peer_key = NULL;
2984
2985 SOSCircleRef circle = SOSAccountFindCircle(account, circle_id, error);
2986 require_quiet(circle, fail);
2987 SOSFullPeerInfoRef myFullPeer = SOSAccountGetMyFullPeerInCircle(account, circle, error);
2988 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(myFullPeer);
2989 require_action_quiet(SOSCircleHasPeer(circle, myPeer, NULL), fail, SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL, CFSTR("Not in circle, can't handle message")));
2990
2991 peer_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_name);
2992
2993 SOSPeerSendBlock peer_send_block = ^bool (CFDataRef message, CFErrorRef *error) {
2994 return send_block(circle, peer_key, message, error);
2995 };
2996
2997 success = SOSCircleHandlePeerMessage(circle, myFullPeer, account->factory, peer_send_block, peer_name, message, error);
2998
2999 fail:
3000 CFReleaseNull(peer_key);
3001 return success;
3002 }
3003
3004 bool SOSAccountHandleUpdates(SOSAccountRef account,
3005 CFDictionaryRef updates,
3006 CFErrorRef *error) {
3007
3008 if(CFDictionaryGetValue(updates, kSOSKVSAccountChangedKey) != NULL) {
3009 SOSAccountSetToNew(account);
3010 }
3011
3012 CFTypeRef parameters = CFDictionaryGetValue(updates, kSOSKVSKeyParametersKey);
3013 if (isData(parameters)) {
3014 SecKeyRef newKey = NULL;
3015 CFDataRef newParameters = NULL;
3016 const uint8_t *parse_end = der_decode_cloud_parameters(kCFAllocatorDefault, kSecECDSAAlgorithmID,
3017 &newKey, &newParameters, error,
3018 CFDataGetBytePtr(parameters), CFDataGetPastEndPtr(parameters));
3019
3020 if (parse_end == CFDataGetPastEndPtr(parameters)) {
3021 if (CFEqualSafe(account->user_public, newKey)) {
3022 secnotice("updates", "Got same public key sent our way. Ignoring.");
3023 } else if (CFEqualSafe(account->previous_public, newKey)) {
3024 secnotice("updates", "Got previous public key repeated. Ignoring.");
3025 } else {
3026 CFReleaseNull(account->user_public);
3027 SOSAccountPurgePrivateCredential(account);
3028 CFReleaseNull(account->user_key_parameters);
3029
3030 account->user_public_trusted = false;
3031
3032 account->user_public = newKey;
3033 newKey = NULL;
3034
3035 account->user_key_parameters = newParameters;
3036 newParameters = NULL;
3037
3038 secnotice("updates", "Got new parameters for public key: %@", account->user_public);
3039 debugDumpUserParameters(CFSTR("params"), account->user_key_parameters);
3040 }
3041 }
3042
3043 CFReleaseNull(newKey);
3044 CFReleaseNull(newParameters);
3045 }
3046
3047 if (!account->user_public_trusted) {
3048 if (!account->deferred_updates) {
3049 account->deferred_updates = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
3050 }
3051
3052 CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3053 if (!CFEqualSafe(key, kSOSKVSKeyParametersKey) && !CFEqualSafe(key, kSOSKVSAccountChangedKey))
3054 CFDictionarySetValue(account->deferred_updates, key, value);
3055 });
3056 secnotice("updates", "No public peer key, deferring updates: %@", updates);
3057 return true;
3058 }
3059
3060 // Iterate though keys in updates. Perform circle change update.
3061 // Then instantiate circles and engines and peers for all peers that
3062 // are receiving a message in updates.
3063 __block bool is_initial_sync = CFDictionaryContainsKey(updates, kSOSKVSInitialSyncKey);
3064
3065 CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3066 CFStringRef circle_name = NULL;
3067 CFErrorRef localError = NULL;
3068 SOSCircleRef circle = NULL;
3069
3070 if (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, NULL, NULL) == kCircleKey) {
3071 circle = SOSAccountCreateCircleFrom(circle_name, value, &localError);
3072 if (!circle) {
3073 if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) {
3074 SOSAccountDestroyCirclePeerInfoNamed(account, circle_name, NULL);
3075 CFDictionarySetValue(account->circles, circle_name, kCFNull);
3076 } else {
3077 SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL,
3078 CFSTR("Bad key for message, no circle '%@'"), key);
3079 goto circle_done;
3080 }
3081 }
3082
3083 if (!SOSAccountUpdateCircleFromRemote(account, circle, is_initial_sync, &localError)) {
3084 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, localError, error, NULL,
3085 CFSTR("Error handling circle change '%@'"), key);
3086 secnotice("update", "Error updating circle '%@': %@", key, circle);
3087 goto circle_done;
3088 }
3089 }
3090 circle_done:
3091 CFReleaseSafe(circle_name);
3092 CFReleaseNull(circle);
3093 CFReleaseNull(localError);
3094 });
3095
3096 CFDictionaryForEach(updates, ^(const void *key, const void *value) {
3097 CFErrorRef localError = NULL;
3098 CFStringRef circle_name = NULL;
3099 CFStringRef from_name = NULL;
3100 CFStringRef to_name = NULL;
3101 switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &from_name, &to_name)) {
3102 case kParametersKey:
3103 case kInitialSyncKey:
3104 case kCircleKey:
3105 break;
3106 case kMessageKey:
3107 {
3108 SOSFullPeerInfoRef my_peer = NULL;
3109
3110 require_action_quiet(isData(value), message_error, SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, localError, error, NULL, CFSTR("Non-Data for message(%@) from '%@'"), value, key));
3111 require_quiet(my_peer = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, circle_name, &localError), message_error);
3112
3113 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(my_peer));
3114 require_quiet(SOSAccountIsActivePeerInCircleNamed(account, circle_name, my_id, &localError), skip);
3115 require_quiet(CFEqual(my_id, to_name), skip);
3116 require_quiet(!CFEqual(my_id, from_name), skip);
3117
3118 SOSAccountSendBlock cacheInDictionary = ^ bool (SOSCircleRef circle, CFStringRef key, CFDataRef new_message, CFErrorRef* error) {
3119 CFDictionarySetValue(account->pending_changes, key, new_message);
3120
3121 if (account->processed_message_block) {
3122 account->processed_message_block(circle, value, new_message);
3123 }
3124
3125 return true;
3126 };
3127
3128 if (SOSAccountHandlePeerMessage(account, circle_name, from_name, value, cacheInDictionary, &localError) == kSOSPeerCoderFailure) {
3129 SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL,
3130 CFSTR("Error handling peer message from '%@'"), key);
3131 localError = NULL; // Released by SOSCreateErrorWithFormat
3132 goto message_error;
3133 }
3134
3135 message_error:
3136 skip:
3137 break;
3138 }
3139 case kRetirementKey:
3140 if(isData(value)) {
3141 SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value);
3142 if(pi && CFEqual(from_name, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) {
3143 CFDictionarySetValue(account->retired_peers, key, value);
3144 SOSAccountRecordRetiredPeerInCircleNamed(account, circle_name, pi);
3145 }
3146 CFReleaseSafe(pi);
3147 }
3148 break;
3149
3150 case kAccountChangedKey: // Handled at entry to function to make sure these are processed first.
3151 break;
3152
3153 case kUnknownKey:
3154 secnotice("updates", "Unknown key '%@', ignoring", key);
3155 break;
3156
3157 }
3158
3159 CFReleaseNull(circle_name);
3160 CFReleaseNull(from_name);
3161 CFReleaseNull(to_name);
3162
3163 if (error && *error)
3164 secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error);
3165 if (localError)
3166 secerror("Peer message local processing error for: %@ -> %@ (%@)", key, value, localError);
3167
3168 CFReleaseNull(localError);
3169 });
3170
3171 return SOSAccountSendPendingChanges(account, error);
3172 }
3173
3174 void SOSAccountSetMessageProcessedBlock(SOSAccountRef account, SOSAccountMessageProcessedBlock processedBlock)
3175 {
3176 CFRetainSafe(processedBlock);
3177 CFReleaseNull(account->processed_message_block);
3178 account->processed_message_block = processedBlock;
3179 }
3180
3181 CFStringRef SOSInterestListCopyDescription(CFArrayRef interests)
3182 {
3183 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
3184 CFStringAppendFormat(description, NULL, CFSTR("<Interest: "));
3185
3186 CFArrayForEach(interests, ^(const void* string) {
3187 if (isString(string))
3188 CFStringAppendFormat(description, NULL, CFSTR(" '%@'"), string);
3189 });
3190 CFStringAppend(description, CFSTR(">"));
3191
3192 return description;
3193 }