2 * Created by Michael Brouwer on 6/22/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
7 * SOSAccount.c - Implementation of the secure object syncing account.
8 * An account contains a SOSCircle for each protection domain synced.
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>
27 #include <AssertMacros.h>
28 #include <utilities/SecCFWrappers.h>
30 #include <utilities/der_plist.h>
31 #include <utilities/der_plist_internal.h>
32 #include <utilities/iOSforOSX.h>
34 #include <utilities/SecAKSWrappers.h>
36 #include <corecrypto/ccder.h>
38 #include <securityd/SOSCloudCircleServer.h>
39 #include <securityd/SecDbItem.h> // For SecError
41 #include <utilities/debugging.h>
42 #include <utilities/iCloudKeychainTrace.h>
46 static CFStringRef kicloud_identity_name
= CFSTR("Cloud Identity");
52 static bool SOSAccountHandleUpdateCircle(SOSAccountRef account
, SOSCircleRef newCircle
, bool writeUpdate
, bool initialSync
, CFErrorRef
*error
);
55 // DER Encoding utilities
59 // Encodes data or a zero length data
61 static size_t der_sizeof_data_or_null(CFDataRef data
, CFErrorRef
* error
)
64 return der_sizeof_data(data
, error
);
66 return der_sizeof_null(kCFNull
, error
);
70 static uint8_t* der_encode_data_or_null(CFDataRef data
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
73 return der_encode_data(data
, error
, der
, der_end
);
75 return der_encode_null(kCFNull
, error
, der
, der_end
);
80 static const uint8_t* der_decode_data_or_null(CFAllocatorRef allocator
, CFDataRef
* data
,
82 const uint8_t* der
, const uint8_t* der_end
)
84 CFTypeRef value
= NULL
;
85 der
= der_decode_plist(allocator
, 0, &value
, error
, der
, der_end
);
86 if (value
&& CFGetTypeID(value
) != CFDataGetTypeID()) {
97 // Mark: public_bytes encode/decode
100 static size_t der_sizeof_public_bytes(SecKeyRef publicKey
, CFErrorRef
* error
)
102 CFDataRef publicData
= NULL
;
105 SecKeyCopyPublicBytes(publicKey
, &publicData
);
107 size_t size
= der_sizeof_data_or_null(publicData
, error
);
109 CFReleaseNull(publicData
);
114 static uint8_t* der_encode_public_bytes(SecKeyRef publicKey
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
116 CFDataRef publicData
= NULL
;
119 SecKeyCopyPublicBytes(publicKey
, &publicData
);
121 uint8_t *result
= der_encode_data_or_null(publicData
, error
, der
, der_end
);
123 CFReleaseNull(publicData
);
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
)
130 CFDataRef dataFound
= NULL
;
131 der
= der_decode_data_or_null(allocator
, &dataFound
, error
, der
, der_end
);
133 if (der
&& dataFound
&& publicKey
) {
134 *publicKey
= SecKeyCreateFromPublicData(allocator
, algorithmID
, dataFound
);
136 CFReleaseNull(dataFound
);
143 // Cloud Paramters encode/decode
146 static size_t der_sizeof_cloud_parameters(SecKeyRef publicKey
, CFDataRef paramters
, CFErrorRef
* error
)
148 size_t public_key_size
= der_sizeof_public_bytes(publicKey
, error
);
149 size_t parameters_size
= der_sizeof_data_or_null(paramters
, error
);
151 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, public_key_size
+ parameters_size
);
154 static uint8_t* der_encode_cloud_parameters(SecKeyRef publicKey
, CFDataRef paramters
, CFErrorRef
* error
,
155 const uint8_t* der
, uint8_t* der_end
)
157 uint8_t* original_der_end
= der_end
;
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
)));
164 static const uint8_t* der_decode_cloud_parameters(CFAllocatorRef allocator
,
165 CFIndex algorithmID
, SecKeyRef
* publicKey
,
166 CFDataRef
*parameters
,
168 const uint8_t* der
, const uint8_t* der_end
)
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
);
180 // bool encoding/decoding
184 static const uint8_t* ccder_decode_bool(bool* boolean
, const uint8_t* der
, const uint8_t *der_end
)
189 size_t payload_size
= 0;
190 const uint8_t *payload
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
192 if (NULL
== payload
|| (der_end
- payload
) < 1 || payload_size
!= 1) {
197 *boolean
= (*payload
!= 0);
199 return payload
+ payload_size
;
203 static size_t ccder_sizeof_bool(bool value __unused
, CFErrorRef
*error
)
205 return ccder_sizeof(CCDER_BOOLEAN
, 1);
209 static uint8_t* ccder_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
)
211 uint8_t value_byte
= value
;
213 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
214 ccder_encode_body(1, &value_byte
, der
, der_end
));
217 struct __OpaqueSOSAccount
{
220 dispatch_queue_t queue
;
222 CFDictionaryRef gestalt
;
224 CFMutableDictionaryRef circle_identities
;
225 CFMutableDictionaryRef circles
;
226 CFMutableDictionaryRef retired_peers
;
228 bool user_public_trusted
;
229 CFDataRef user_key_parameters
;
230 SecKeyRef user_public
;
231 SecKeyRef previous_public
;
232 enum DepartureReason departure_code
;
234 // Non-persistent data
236 SOSDataSourceFactoryRef factory
;
237 SecKeyRef _user_private
;
238 dispatch_source_t user_private_timer
;
239 int lock_notification_token
;
242 CFMutableArrayRef change_blocks
;
244 SOSAccountKeyInterestBlock update_interest_block
;
245 SOSAccountDataUpdateBlock update_block
;
246 SOSAccountMessageProcessedBlock processed_message_block
;
248 CFMutableDictionaryRef deferred_updates
;
250 CFMutableDictionaryRef pending_changes
;
253 CFGiblisWithCompareFor(SOSAccount
);
255 static inline bool SOSAccountHasLeft(SOSAccountRef account
) {
256 switch(account
->departure_code
) {
257 case kSOSWithdrewMembership
: /* Fallthrough */
258 case kSOSMembershipRevoked
: /* Fallthrough */
259 case kSOSLeftUntrustedCircle
:
261 case kSOSNeverAppliedToCircle
: /* Fallthrough */
262 case kSOSNeverLeftCircle
: /* Fallthrough */
268 // Private static functions.
270 static bool SOSUpdateKeyInterest(SOSAccountRef account
, bool getNewKeysOnly
, CFErrorRef
*error
);
272 static bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
)
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
) {
282 SOSAccountEnsureCircle(a
, (CFStringRef
)name
, NULL
);
285 CFReleaseNull(circle_names
);
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
);
299 a
->queue
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
);
301 a
->gestalt
= gestalt
;
302 CFRetain(a
->gestalt
);
304 a
->circles
= CFDictionaryCreateMutableForCFTypes(allocator
);
305 a
->circle_identities
= CFDictionaryCreateMutableForCFTypes(allocator
);
306 a
->retired_peers
= CFDictionaryCreateMutableForCFTypes(allocator
);
308 a
->factory
= factory
; // We adopt the factory. kthanksbai.
310 a
->change_blocks
= CFArrayCreateMutableForCFTypes(allocator
);
312 a
->update_interest_block
= Block_copy(interest_block
);
313 a
->update_block
= Block_copy(update_block
);
315 a
->pending_changes
= CFDictionaryCreateMutableForCFTypes(allocator
);
316 a
->departure_code
= kSOSNeverAppliedToCircle
;
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
);
328 return (SOSFullPeerInfoRef
) CFDictionaryGetValue(account
->circle_identities
, name
);
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
) {
338 if (handle_incompatible
)
339 handle_incompatible((CFStringRef
)key
);
341 SOSCircleRef circle
= (SOSCircleRef
) value
;
342 CFRetainSafe(circle
);
343 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInCircleNamedIfPresent(account
, SOSCircleGetName(circle
), NULL
);
346 handle_no_peer(circle
);
350 handle_peer(circle
, fpi
);
353 CFReleaseSafe(circle
);
359 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
)
361 if (CFEqual(new_gestalt
, account
->gestalt
))
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
));
373 CFReleaseNull(account
->gestalt
);
374 account
->gestalt
= new_gestalt
;
375 CFRetain(account
->gestalt
);
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
);
387 SOSAccountEnsureFactoryCircles(a
);
392 static void SOSAccountDestroy(CFTypeRef aObj
) {
393 SOSAccountRef a
= (SOSAccountRef
) aObj
;
396 a
->factory
->release(a
->factory
);
398 CFReleaseNull(a
->gestalt
);
399 CFReleaseNull(a
->circle_identities
);
400 CFReleaseNull(a
->circles
);
401 CFReleaseNull(a
->retired_peers
);
403 a
->user_public_trusted
= false;
404 CFReleaseNull(a
->user_public
);
405 CFReleaseNull(a
->user_key_parameters
);
407 SOSAccountPurgePrivateCredential(a
);
408 CFReleaseNull(a
->previous_public
);
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
;
418 dispatch_release(a
->queue
);
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
);
427 CFReleaseNull(a
->user_key_parameters
);
428 CFReleaseNull(a
->user_public
);
429 CFReleaseNull(a
->previous_public
);
430 CFReleaseNull(a
->_user_private
);
432 CFReleaseNull(a
->pending_changes
);
433 CFReleaseNull(a
->deferred_updates
);
435 a
->user_public_trusted
= false;
436 a
->departure_code
= kSOSNeverAppliedToCircle
;
437 a
->user_private_timer
= 0;
438 a
->lock_notification_token
= 0;
444 // update_interest_block;
447 a
->circles
= CFDictionaryCreateMutableForCFTypes(allocator
);
448 a
->circle_identities
= CFDictionaryCreateMutableForCFTypes(allocator
);
449 a
->retired_peers
= CFDictionaryCreateMutableForCFTypes(allocator
);
450 a
->pending_changes
= CFDictionaryCreateMutableForCFTypes(allocator
);
452 SOSAccountEnsureFactoryCircles(a
);
456 static CFStringRef
SOSAccountCopyDescription(CFTypeRef aObj
) {
457 SOSAccountRef a
= (SOSAccountRef
) aObj
;
459 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SOSAccount@%p: Gestalt: %@\n Circles: %@ CircleIDs: %@>"), a
, a
->gestalt
, a
->circles
, a
->circle_identities
);
462 static Boolean
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
)
464 SOSAccountRef laccount
= (SOSAccountRef
) lhs
;
465 SOSAccountRef raccount
= (SOSAccountRef
) rhs
;
467 return CFEqual(laccount
->gestalt
, raccount
->gestalt
)
468 && CFEqual(laccount
->circles
, raccount
->circles
)
469 && CFEqual(laccount
->circle_identities
, raccount
->circle_identities
);
473 #if OLD_CODERS_SUPPORTED
476 // MARK: Persistent Encode decode
478 SOSAccountRef
SOSAccountCreateFromDER_V1(CFAllocatorRef allocator
,
479 SOSDataSourceFactoryRef factory
,
480 SOSAccountKeyInterestBlock interest_block
,
481 SOSAccountDataUpdateBlock update_block
,
483 const uint8_t** der_p
, const uint8_t *der_end
)
485 SOSAccountRef account
= NULL
;
487 const uint8_t *sequence_end
;
488 *der_p
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, *der_p
, der_end
);
491 CFDictionaryRef decoded_gestalt
= NULL
;
492 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
498 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
, interest_block
, update_block
);
499 CFReleaseNull(decoded_gestalt
);
502 CFArrayRef array
= NULL
;
503 *der_p
= der_decode_array(kCFAllocatorDefault
, 0, &array
, error
, *der_p
, sequence_end
);
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
)
512 __block
bool success
= true;
514 require_quiet(array
&& *der_p
, fail
);
516 CFArrayForEach(array
, ^(const void *value
) {
518 if (isString(value
)) {
519 CFDictionaryAddValue(account
->circles
, value
, kCFNull
);
521 CFDataRef circleData
= NULL
;
522 CFDataRef fullPeerInfoData
= NULL
;
525 circleData
= (CFDataRef
) value
;
526 } else if (isArray(value
)) {
527 CFArrayRef pair
= (CFArrayRef
) value
;
529 CFTypeRef circleObject
= CFArrayGetValueAtIndex(pair
, 0);
530 CFTypeRef fullPeerInfoObject
= CFArrayGetValueAtIndex(pair
, 1);
532 if (CFArrayGetCount(pair
) == 2 && isData(circleObject
) && isData(fullPeerInfoObject
)) {
533 circleData
= (CFDataRef
) circleObject
;
534 fullPeerInfoData
= (CFDataRef
) fullPeerInfoObject
;
539 SOSCircleRef circle
= SOSCircleCreateFromData(kCFAllocatorDefault
, circleData
, error
);
540 require_action_quiet(circle
, fail
, success
= false);
542 CFStringRef circleName
= SOSCircleGetName(circle
);
543 CFDictionaryAddValue(account
->circles
, circleName
, circle
);
545 if (fullPeerInfoData
) {
546 SOSFullPeerInfoRef full_peer
= SOSFullPeerInfoCreateFromData(kCFAllocatorDefault
, fullPeerInfoData
, error
);
547 require_action_quiet(full_peer
, fail
, success
= false);
549 CFDictionaryAddValue(account
->circle_identities
, circleName
, full_peer
);
550 CFReleaseNull(full_peer
);
553 CFReleaseNull(circle
);
558 CFReleaseNull(array
);
560 require_quiet(success
, fail
);
561 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), fail
,
562 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
567 // Create a default error if we don't have one:
568 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Bad Account DER"), NULL
, error
);
569 CFReleaseNull(account
);
573 SOSAccountRef
SOSAccountCreateFromDER_V2(CFAllocatorRef allocator
,
574 SOSDataSourceFactoryRef factory
,
575 SOSAccountKeyInterestBlock interest_block
,
576 SOSAccountDataUpdateBlock update_block
,
578 const uint8_t** der_p
, const uint8_t *der_end
)
580 SOSAccountRef account
= NULL
;
581 const uint8_t *dersave
= *der_p
;
582 const uint8_t *derend
= der_end
;
584 const uint8_t *sequence_end
;
585 *der_p
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, *der_p
, der_end
);
588 CFDictionaryRef decoded_gestalt
= NULL
;
589 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
595 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
, interest_block
, update_block
);
596 CFReleaseNull(decoded_gestalt
);
599 CFArrayRef array
= NULL
;
600 *der_p
= der_decode_array(kCFAllocatorDefault
, 0, &array
, error
, *der_p
, sequence_end
);
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
)
610 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
612 __block
bool success
= true;
614 require_quiet(array
&& *der_p
, fail
);
616 CFArrayForEach(array
, ^(const void *value
) {
618 if (isString(value
)) {
619 CFDictionaryAddValue(account
->circles
, value
, kCFNull
);
621 CFDataRef circleData
= NULL
;
622 CFDataRef fullPeerInfoData
= NULL
;
625 circleData
= (CFDataRef
) value
;
626 } else if (isArray(value
)) {
627 CFArrayRef pair
= (CFArrayRef
) value
;
629 CFTypeRef circleObject
= CFArrayGetValueAtIndex(pair
, 0);
630 CFTypeRef fullPeerInfoObject
= CFArrayGetValueAtIndex(pair
, 1);
632 if (CFArrayGetCount(pair
) == 2 && isData(circleObject
) && isData(fullPeerInfoObject
)) {
633 circleData
= (CFDataRef
) circleObject
;
634 fullPeerInfoData
= (CFDataRef
) fullPeerInfoObject
;
639 SOSCircleRef circle
= SOSCircleCreateFromData(kCFAllocatorDefault
, circleData
, error
);
640 require_action_quiet(circle
, fail
, success
= false);
642 CFStringRef circleName
= SOSCircleGetName(circle
);
643 CFDictionaryAddValue(account
->circles
, circleName
, circle
);
645 if (fullPeerInfoData
) {
646 SOSFullPeerInfoRef full_peer
= SOSFullPeerInfoCreateFromData(kCFAllocatorDefault
, fullPeerInfoData
, error
);
647 require_action_quiet(full_peer
, fail
, success
= false);
649 CFDictionaryAddValue(account
->circle_identities
, circleName
, full_peer
);
652 CFReleaseNull(circle
);
657 CFReleaseNull(array
);
659 require_quiet(success
, fail
);
660 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), fail
,
661 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
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
;
675 #endif /* OLD_CODERS_SUPPORTED */
677 #define CURRENT_ACCOUNT_PERSISTENT_VERSION 6
679 SOSAccountRef
SOSAccountCreateFromDER(CFAllocatorRef allocator
,
680 SOSDataSourceFactoryRef factory
,
681 SOSAccountKeyInterestBlock interest_block
,
682 SOSAccountDataUpdateBlock update_block
,
684 const uint8_t** der_p
, const uint8_t *der_end
)
686 SOSAccountRef account
= NULL
;
687 #if UPGRADE_FROM_PREVIOUS_VERSION
688 const uint8_t *dersave
= *der_p
;
689 const uint8_t *derend
= der_end
;
691 uint64_t version
= 0;
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
);
705 CFDictionaryRef decoded_gestalt
= NULL
;
706 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
712 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
, interest_block
, update_block
);
713 CFReleaseNull(decoded_gestalt
);
716 CFArrayRef array
= NULL
;
717 *der_p
= der_decode_array(kCFAllocatorDefault
, 0, &array
, error
, *der_p
, sequence_end
);
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
)
728 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
730 __block
bool success
= true;
732 require_quiet(array
&& *der_p
, fail
);
734 CFArrayForEach(array
, ^(const void *value
) {
736 if (isString(value
)) {
737 CFDictionaryAddValue(account
->circles
, value
, kCFNull
);
739 CFDataRef circleData
= NULL
;
740 CFDataRef fullPeerInfoData
= NULL
;
743 circleData
= (CFDataRef
) value
;
744 } else if (isArray(value
)) {
745 CFArrayRef pair
= (CFArrayRef
) value
;
747 CFTypeRef circleObject
= CFArrayGetValueAtIndex(pair
, 0);
748 CFTypeRef fullPeerInfoObject
= CFArrayGetValueAtIndex(pair
, 1);
750 if (CFArrayGetCount(pair
) == 2 && isData(circleObject
) && isData(fullPeerInfoObject
)) {
751 circleData
= (CFDataRef
) circleObject
;
752 fullPeerInfoData
= (CFDataRef
) fullPeerInfoObject
;
757 SOSCircleRef circle
= SOSCircleCreateFromData(kCFAllocatorDefault
, circleData
, error
);
758 require_action_quiet(circle
, fail
, success
= false);
760 CFStringRef circleName
= SOSCircleGetName(circle
);
761 CFDictionaryAddValue(account
->circles
, circleName
, circle
);
763 if (fullPeerInfoData
) {
764 SOSFullPeerInfoRef full_peer
= SOSFullPeerInfoCreateFromData(kCFAllocatorDefault
, fullPeerInfoData
, error
);
765 require_action_quiet(full_peer
, fail
, success
= false);
767 CFDictionaryAddValue(account
->circle_identities
, circleName
, full_peer
);
770 CFReleaseNull(circle
);
775 CFReleaseNull(array
);
777 require_quiet(success
, fail
);
778 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), fail
,
779 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
784 account
->factory
= NULL
; // give the factory back.
785 CFReleaseNull(account
);
790 SOSAccountRef
SOSAccountCreateFromDER_V3(CFAllocatorRef allocator
,
791 SOSDataSourceFactoryRef factory
,
792 SOSAccountKeyInterestBlock interest_block
,
793 SOSAccountDataUpdateBlock update_block
,
795 const uint8_t** der_p
, const uint8_t *der_end
)
797 SOSAccountRef account
= NULL
;
798 uint64_t version
= 0;
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.
809 CFDictionaryRef decoded_gestalt
= NULL
;
810 *der_p
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_gestalt
, error
,
816 account
= SOSAccountCreateBasic(allocator
, decoded_gestalt
, factory
, interest_block
, update_block
);
817 CFReleaseNull(decoded_gestalt
);
820 CFArrayRef array
= NULL
;
821 *der_p
= der_decode_array(kCFAllocatorDefault
, 0, &array
, error
, *der_p
, sequence_end
);
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
)
831 account
->departure_code
= (enum DepartureReason
) tmp_departure_code
;
833 __block
bool success
= true;
835 require_quiet(array
&& *der_p
, fail
);
837 CFArrayForEach(array
, ^(const void *value
) {
839 if (isString(value
)) {
840 CFDictionaryAddValue(account
->circles
, value
, kCFNull
);
842 CFDataRef circleData
= NULL
;
843 CFDataRef fullPeerInfoData
= NULL
;
846 circleData
= (CFDataRef
) value
;
847 } else if (isArray(value
)) {
848 CFArrayRef pair
= (CFArrayRef
) value
;
850 CFTypeRef circleObject
= CFArrayGetValueAtIndex(pair
, 0);
851 CFTypeRef fullPeerInfoObject
= CFArrayGetValueAtIndex(pair
, 1);
853 if (CFArrayGetCount(pair
) == 2 && isData(circleObject
) && isData(fullPeerInfoObject
)) {
854 circleData
= (CFDataRef
) circleObject
;
855 fullPeerInfoData
= (CFDataRef
) fullPeerInfoObject
;
860 SOSCircleRef circle
= SOSCircleCreateFromData(kCFAllocatorDefault
, circleData
, error
);
861 require_action_quiet(circle
, fail
, success
= false);
863 CFStringRef circleName
= SOSCircleGetName(circle
);
864 CFDictionaryAddValue(account
->circles
, circleName
, circle
);
866 if (fullPeerInfoData
) {
867 SOSFullPeerInfoRef full_peer
= SOSFullPeerInfoCreateFromData(kCFAllocatorDefault
, fullPeerInfoData
, error
);
868 require_action_quiet(full_peer
, fail
, success
= false);
870 CFDictionaryAddValue(account
->circle_identities
, circleName
, full_peer
);
873 CFReleaseNull(circle
);
878 CFReleaseNull(array
);
880 require_quiet(success
, fail
);
881 require_action_quiet(SOSAccountEnsureFactoryCircles(account
), fail
,
882 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Cannot EnsureFactoryCircles"), (error
!= NULL
) ? *error
: NULL
, error
));
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
;
896 SOSAccountRef
SOSAccountCreateFromData(CFAllocatorRef allocator
, CFDataRef circleData
,
897 SOSDataSourceFactoryRef factory
,
898 SOSAccountKeyInterestBlock interest_block
,
899 SOSAccountDataUpdateBlock update_block
,
902 size_t size
= CFDataGetLength(circleData
);
903 const uint8_t *der
= CFDataGetBytePtr(circleData
);
904 SOSAccountRef account
= SOSAccountCreateFromDER(allocator
, factory
, interest_block
, update_block
,
910 static CFMutableArrayRef
SOSAccountCopyCircleArrayToEncode(SOSAccountRef account
)
912 CFMutableArrayRef arrayToEncode
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
914 CFDictionaryForEach(account
->circles
, ^(const void *key
, const void *value
) {
916 CFArrayAppendValue(arrayToEncode
, key
); // Encode the name of the circle that's out of date.
918 SOSCircleRef circle
= (SOSCircleRef
) value
;
919 CFDataRef encodedCircle
= SOSCircleCopyEncodedData(circle
, kCFAllocatorDefault
, NULL
);
920 CFTypeRef arrayEntry
= encodedCircle
;
921 CFRetainSafe(arrayEntry
);
923 SOSFullPeerInfoRef full_peer
= (SOSFullPeerInfoRef
) CFDictionaryGetValue(account
->circle_identities
, key
);
926 CFDataRef encodedPeer
= SOSFullPeerInfoCopyEncodedData(full_peer
, kCFAllocatorDefault
, NULL
);
927 CFTypeRef originalArrayEntry
= arrayEntry
;
928 arrayEntry
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, encodedCircle
, encodedPeer
, NULL
);
930 CFReleaseSafe(originalArrayEntry
);
931 CFReleaseNull(encodedPeer
);
934 CFArrayAppendValue(arrayToEncode
, arrayEntry
);
936 CFReleaseSafe(arrayEntry
);
937 CFReleaseNull(encodedCircle
);
942 return arrayToEncode
;
945 size_t SOSAccountGetDEREncodedSize(SOSAccountRef account
, CFErrorRef
*error
)
947 size_t sequence_size
= 0;
948 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
949 uint64_t version
= CURRENT_ACCOUNT_PERSISTENT_VERSION
;
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
);
961 CFReleaseNull(arrayToEncode
);
962 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, sequence_size
);
965 CFReleaseNull(arrayToEncode
);
966 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
970 uint8_t* SOSAccountEncodeToDER(SOSAccountRef account
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
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
))))))))));
985 CFReleaseNull(arrayToEncode
);
992 size_t SOSAccountGetDEREncodedSize_V3(SOSAccountRef account
, CFErrorRef
*error
)
994 size_t sequence_size
= 0;
995 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
996 uint64_t version
= CURRENT_ACCOUNT_PERSISTENT_VERSION
;
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
);
1007 CFReleaseNull(arrayToEncode
);
1008 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, sequence_size
);
1011 CFReleaseNull(arrayToEncode
);
1012 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
1016 uint8_t* SOSAccountEncodeToDER_V3(SOSAccountRef account
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
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
)))))))));
1030 CFReleaseNull(arrayToEncode
);
1035 #if OLD_CODERS_SUPPORTED
1037 /* Original V2 encoders */
1039 size_t SOSAccountGetDEREncodedSize_V2(SOSAccountRef account
, CFErrorRef
*error
)
1041 size_t sequence_size
= 0;
1042 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
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
);
1052 CFReleaseNull(arrayToEncode
);
1053 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, sequence_size
);
1056 CFReleaseNull(arrayToEncode
);
1057 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
1061 uint8_t* SOSAccountEncodeToDER_V2(SOSAccountRef account
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
1063 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
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
))))))));
1074 CFReleaseNull(arrayToEncode
);
1080 /* Original V1 encoders */
1083 size_t SOSAccountGetDEREncodedSize_V1(SOSAccountRef account
, CFErrorRef
*error
)
1085 size_t sequence_size
= 0;
1086 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
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
);
1095 CFReleaseNull(arrayToEncode
);
1096 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, sequence_size
);
1099 CFReleaseNull(arrayToEncode
);
1100 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("don't know how to encode"), NULL
, error
);
1104 uint8_t* SOSAccountEncodeToDER_V1(SOSAccountRef account
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
)
1106 CFMutableArrayRef arrayToEncode
= SOSAccountCopyCircleArrayToEncode(account
);
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
)))))));
1116 CFReleaseNull(arrayToEncode
);
1120 #endif /* OLD_CODERS_SUPPORTED */
1122 /************************/
1124 CFDataRef
SOSAccountCopyEncodedData(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
*error
)
1126 size_t size
= SOSAccountGetDEREncodedSize(account
, error
);
1129 uint8_t buffer
[size
];
1130 uint8_t* start
= SOSAccountEncodeToDER(account
, error
, buffer
, buffer
+ sizeof(buffer
));
1131 CFDataRef result
= CFDataCreate(kCFAllocatorDefault
, start
, size
);
1135 dispatch_queue_t
SOSAccountGetQueue(SOSAccountRef account
) {
1136 return account
->queue
;
1140 // MARK: User Credential management
1143 void SOSAccountPurgePrivateCredential(SOSAccountRef account
)
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();
1152 if (account
->lock_notification_token
) {
1153 notify_cancel(account
->lock_notification_token
);
1154 account
->lock_notification_token
= 0;
1158 static void SOSAccountSetPrivateCredential(SOSAccountRef account
, SecKeyRef
private) {
1160 return SOSAccountPurgePrivateCredential(account
);
1163 CFReleaseSafe(account
->_user_private
);
1164 account
->_user_private
= private;
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
);
1175 notify_register_dispatch(kUserKeybagStateChangeNotification
, &account
->lock_notification_token
, account
->queue
, ^(int token
) {
1176 bool locked
= false;
1177 CFErrorRef lockCheckError
= NULL
;
1179 if (!SecAKSGetIsLocked(&locked
, &lockCheckError
)) {
1180 secerror("Checking for locked after change failed: %@", lockCheckError
);
1184 SOSAccountPurgePrivateCredential(account
);
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
));
1193 dispatch_resume(account
->user_private_timer
);
1196 SecKeyRef
SOSAccountGetPrivateCredential(SOSAccountRef account
, CFErrorRef
* error
)
1198 if (account
->_user_private
== NULL
) {
1199 SOSCreateError(kSOSErrorPrivateKeyAbsent
, CFSTR("Private Key not available - failed to prompt user recently"), NULL
, error
);
1201 return account
->_user_private
;
1204 static bool SOSAccountHasPublicKey(SOSAccountRef account
, CFErrorRef
* error
)
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
);
1214 static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
);
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
))) {
1227 gen_sign
: // finally generation sign this.
1228 SOSCircleGenerationSign(circle
, privKey
, fpi
, NULL
);
1229 account
->departure_code
= kSOSNeverLeftCircle
;
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
);
1243 static void SOSAccountSetPreviousPublic(SOSAccountRef account
) {
1244 CFReleaseNull(account
->previous_public
);
1245 account
->previous_public
= account
->user_public
;
1246 CFRetain(account
->previous_public
);
1249 static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account
, bool public_was_trusted
, SecKeyRef privKey
)
1251 if (!privKey
) return;
1252 SecKeyRef publicKey
= SecKeyCreatePublicFromPrivate(privKey
);
1254 if (account
->user_public
&& account
->user_public_trusted
&& CFEqual(publicKey
, account
->user_public
)) return;
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
);
1262 CFReleaseNull(account
->user_public
);
1263 account
->user_public
= publicKey
;
1264 account
->user_public_trusted
= true;
1266 if(!account
->previous_public
) {
1267 account
->previous_public
= account
->user_public
;
1268 CFRetain(account
->previous_public
);
1271 secnotice("trust", "trusting new public key: %@", account
->user_public
);
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
);
1279 CFReleaseNull(account
->deferred_updates
);
1283 bool SOSAccountTryUserCredentials(SOSAccountRef account
, CFStringRef user_account __unused
, CFDataRef user_password
, CFErrorRef
*error
)
1285 bool success
= false;
1287 if (!SOSAccountHasPublicKey(account
, error
))
1290 if (account
->user_key_parameters
) {
1291 SecKeyRef new_key
= SOSUserKeygen(user_password
, account
->user_key_parameters
, error
);
1293 SecKeyRef new_public_key
= SecKeyCreatePublicFromPrivate(new_key
);
1295 if (CFEqualSafe(new_public_key
, account
->user_public
)) {
1296 SOSAccountSetPrivateCredential(account
, new_key
);
1299 SOSCreateError(kSOSErrorWrongPassword
, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL
, error
);
1301 CFReleaseSafe(new_public_key
);
1302 CFReleaseSafe(new_key
);
1305 SOSCreateError(kSOSErrorProcessingFailure
, CFSTR("Have public key but no parameters??"), NULL
, error
);
1311 static bool SOSAccountPublishCloudParameters(SOSAccountRef account
, CFErrorRef
* error
)
1313 bool success
= false;
1314 CFMutableDataRef cloudParameters
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
,
1315 der_sizeof_cloud_parameters(account
->user_public
,
1316 account
->user_key_parameters
,
1318 if (der_encode_cloud_parameters(account
->user_public
, account
->user_key_parameters
, error
,
1319 CFDataGetMutableBytePtr(cloudParameters
),
1320 CFDataGetMutablePastEndPtr(cloudParameters
)) != NULL
) {
1322 CFDictionaryRef changes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1323 kSOSKVSKeyParametersKey
, cloudParameters
,
1326 CFErrorRef changeError
= NULL
;
1327 if (account
->update_block(changes
, &changeError
)) {
1330 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
1331 CFSTR("update parameters key failed [%@]"), changes
);
1333 CFReleaseSafe(changes
);
1334 CFReleaseSafe(changeError
);
1336 SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Encoding parameters failed"), NULL
, error
);
1339 CFReleaseNull(cloudParameters
);
1344 bool SOSAccountAssertUserCredentials(SOSAccountRef account
, CFStringRef user_account __unused
, CFDataRef user_password
, CFErrorRef
*error
)
1346 bool public_was_trusted
= account
->user_public_trusted
;
1347 account
->user_public_trusted
= false;
1348 SecKeyRef user_private
= NULL
;
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
);
1362 SOSAccountPeerSignatureUpdate(account
, user_private
);
1363 SOSAccountSetTrustedUserPublicKey(account
, public_was_trusted
, user_private
);
1365 CFReleaseSafe(public_candidate
);
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.
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
);
1377 SOSAccountPeerSignatureUpdate(account
, user_private
);
1378 SOSAccountSetTrustedUserPublicKey(account
, public_was_trusted
, user_private
);
1380 CFErrorRef publishError
= NULL
;
1381 if (!SOSAccountPublishCloudParameters(account
, &publishError
))
1382 secerror("Failed to publish new cloud parameters: %@", publishError
);
1383 CFReleaseSafe(publishError
);
1386 SOSAccountProcessDeferredUpdates(account
);
1387 SOSAccountGenerationSignatureUpdate(account
, user_private
);
1388 SOSAccountSetPrivateCredential(account
, user_private
);
1390 CFReleaseSafe(user_private
);
1392 return account
->user_public_trusted
;
1396 // MARK: Circle management
1399 int SOSAccountCountCircles(SOSAccountRef a
) {
1401 assert(a
->circle_identities
);
1403 return (int)CFDictionaryGetCount(a
->circles
);
1406 static SecKeyRef
GeneratePermanentFullECKey_internal(int keySize
, CFStringRef name
, CFTypeRef accessibility
, CFBooleanRef sync
, CFErrorRef
* error
)
1408 SecKeyRef full_key
= NULL
;
1410 CFNumberRef key_size_num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &keySize
);
1412 CFDictionaryRef priv_key_attrs
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1413 kSecAttrIsPermanent
, kCFBooleanTrue
,
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
,
1427 CFReleaseNull(priv_key_attrs
);
1429 CFReleaseNull(key_size_num
);
1430 OSStatus status
= SecKeyGeneratePair(keygen_parameters
, NULL
, &full_key
);
1431 CFReleaseNull(keygen_parameters
);
1434 secerror("status: %ld", (long)status
);
1435 if (status
!= errSecSuccess
&& error
!= NULL
&& *error
== NULL
) {
1436 *error
= CFErrorCreate(kCFAllocatorDefault
, kCFErrorDomainOSStatus
, status
, NULL
);
1442 static SecKeyRef
GeneratePermanentFullECKey(int keySize
, CFStringRef name
, CFErrorRef
* error
) {
1443 return GeneratePermanentFullECKey_internal(keySize
, name
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
, kCFBooleanFalse
, error
);
1446 static SecKeyRef
GeneratePermanentFullECKeyForCloudIdentity(int keySize
, CFStringRef name
, CFErrorRef
* error
) {
1447 return GeneratePermanentFullECKey_internal(keySize
, name
, kSecAttrAccessibleWhenUnlocked
, kCFBooleanTrue
, error
);
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
);
1456 SOSFullPeerInfoRef circle_full_peer_info
= (SOSFullPeerInfoRef
) CFDictionaryGetValue(account
->circle_identities
, name
);
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
);
1465 circle_full_peer_info
= SOSFullPeerInfoCreate(kCFAllocatorDefault
, account
->gestalt
, full_key
, error
);
1467 CFReleaseNull(full_key
);
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
;
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
);
1479 secerror("No full_key: %@:", error
? *error
: NULL
);
1482 return circle_full_peer_info
;
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
);
1491 SOSFullPeerInfoRef circle_full_peer_info
= (SOSFullPeerInfoRef
) CFDictionaryGetValue(account
->circle_identities
, name
);
1493 if (circle_full_peer_info
) {
1494 SOSPeerPurgeAllFor(SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(circle_full_peer_info
)));
1496 SOSFullPeerInfoPurgePersistentKey(circle_full_peer_info
, NULL
);
1499 CFDictionaryRemoveValue(account
->circle_identities
, name
);
1504 static bool SOSAccountDestroyCirclePeerInfo(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
1505 return SOSAccountDestroyCirclePeerInfoNamed(account
, SOSCircleGetName(circle
), error
);
1508 SOSFullPeerInfoRef
SOSAccountGetMyFullPeerInCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
1509 return SOSAccountGetMyFullPeerInCircleNamed(account
, SOSCircleGetName(circle
), error
);
1512 SOSPeerInfoRef
SOSAccountGetMyPeerInCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
1513 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInCircleNamed(account
, SOSCircleGetName(circle
), error
);
1515 return fpi
? SOSFullPeerInfoGetPeerInfo(fpi
) : NULL
;
1518 SOSPeerInfoRef
SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account
, CFStringRef name
, CFErrorRef
*error
)
1520 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInCircleNamed(account
, name
, error
);
1522 return fpi
? SOSFullPeerInfoGetPeerInfo(fpi
) : NULL
;
1525 CFArrayRef
SOSAccountCopyAccountIdentityPeerInfos(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
)
1527 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(allocator
);
1529 CFDictionaryForEach(account
->circle_identities
, ^(const void *key
, const void *value
) {
1530 SOSFullPeerInfoRef fpi
= (SOSFullPeerInfoRef
) value
;
1532 CFArrayAppendValue(result
, SOSFullPeerInfoGetPeerInfo(fpi
));
1538 bool SOSAccountIsAccountIdentity(SOSAccountRef account
, SOSPeerInfoRef peer_info
, CFErrorRef
*error
)
1540 __block
bool matches
= false;
1541 CFDictionaryForEach(account
->circle_identities
, ^(const void *key
, const void *value
) {
1543 matches
= CFEqual(peer_info
, SOSFullPeerInfoGetPeerInfo((SOSFullPeerInfoRef
) value
));
1550 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef
*error
)
1552 __block
bool result
= true;
1553 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
1554 if (!SOSAccountSyncWithAllPeersInCircle(account
, circle
, error
))
1561 bool SOSAccountSyncWithAllPeersInCircle(SOSAccountRef account
, SOSCircleRef circle
,
1564 SOSPeerInfoRef my_peer
= SOSAccountGetMyPeerInCircle(account
, circle
, error
);
1568 __block
bool didSync
= false;
1569 __block
bool result
= true;
1571 if (SOSCircleHasPeer(circle
, my_peer
, NULL
)) {
1572 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
1573 if (!CFEqual(SOSPeerInfoGetPeerID(my_peer
), SOSPeerInfoGetPeerID(peer
)))
1575 bool local_didSync
= false;
1576 if (!SOSAccountSyncWithPeer(account
, circle
, peer
, &local_didSync
, error
))
1578 if (!didSync
&& local_didSync
)
1587 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
1594 bool SOSAccountSyncWithPeer(SOSAccountRef account
, SOSCircleRef circle
,
1595 SOSPeerInfoRef thisPeer
, bool* didSendData
, CFErrorRef
* error
)
1597 CFStringRef peer_id
= SOSPeerInfoGetPeerID(thisPeer
);
1598 CFStringRef peer_write_key
= SOSMessageKeyCreateWithAccountAndPeer(account
, circle
, peer_id
);
1599 SOSFullPeerInfoRef myRef
= SOSAccountGetMyFullPeerInCircle(account
, circle
, error
);
1601 __block
bool sentData
= false;
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
);
1615 if (NULL
!= didSendData
)
1617 *didSendData
= sentData
;
1620 bool result
= SOSCircleSyncWithPeer(myRef
, circle
, account
->factory
, writeToKVSKey
, peer_id
, error
);
1621 CFReleaseNull(peer_write_key
);
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
);
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
);
1637 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
1638 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
1640 if(!SOSAccountIsMyPeerActiveInCircle(account
, circle
, NULL
)) return true;
1642 CFMutableDictionaryRef keysToWrite
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1644 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef circlePeer
) {
1645 CFStringRef from_key
= SOSMessageKeyCreateWithCircleAndPeerInfos(circle
, cleanupPeer
, circlePeer
);
1646 CFStringRef to_key
= SOSMessageKeyCreateWithCircleAndPeerInfos(circle
, circlePeer
, cleanupPeer
);
1648 CFDictionaryAddValue(keysToWrite
, from_key
, kCFNull
);
1649 CFDictionaryAddValue(keysToWrite
, to_key
, kCFNull
);
1651 CFReleaseNull(from_key
);
1652 CFReleaseNull(to_key
);
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
);
1662 bool success
= account
->update_block(keysToWrite
, error
);
1664 CFReleaseNull(keysToWrite
);
1669 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
1670 CFMutableDictionaryRef keysToWrite
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1672 CFDictionaryRef copyToIterate
= CFDictionaryCreateCopy(kCFAllocatorDefault
, account
->retired_peers
);
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
);
1683 require_quiet(!SOSAccountIsActivePeerInCircleNamed(account
, circle_name
, retiree_peerid
, NULL
), keep
);
1684 require_quiet(SOSPeerInfoRetireRetirementTicket(seconds
, pi
), keep
);
1686 // Happy day, it's time and it's a ticket we should eradicate from KVS.
1687 CFDictionarySetValue(keysToWrite
, resignationKey
, kCFNull
);
1690 CFDictionaryRemoveValue(account
->retired_peers
, resignationKey
);
1693 CFReleaseSafe(circle_name
);
1694 CFReleaseSafe(retiree_peerid
);
1696 CFReleaseNull(copyToIterate
);
1698 bool success
= true;
1699 if(CFDictionaryGetCount(keysToWrite
)) {
1700 success
= account
->update_block(keysToWrite
, error
);
1702 CFReleaseNull(keysToWrite
);
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
);
1713 CFDictionarySetValue(account
->retired_peers
, key
, value
);
1714 SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, error
);
1716 CFReleaseSafe(value
);
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
;
1728 CFDictionaryForEach(account
->retired_peers
, ^(const void* resignationKey
, const void* value
) {
1729 CFStringRef circle_name
= NULL
;
1730 CFStringRef retiree_peerid
= NULL
;
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
)) {
1735 SOSPeerInfoRef pi
= SOSPeerInfoCreateFromData(NULL
, error
, (CFDataRef
) value
);
1736 SOSCircleUpdatePeerInfo(new_circle
, pi
);
1740 CFReleaseSafe(circle_name
);
1741 CFReleaseSafe(retiree_peerid
);
1744 if(SOSCircleCountPeers(new_circle
) == 0) {
1745 SOSCircleResetToEmpty(new_circle
, NULL
);
1755 SOSCircleRef
SOSAccountFindCompatibleCircle(SOSAccountRef a
, CFStringRef name
)
1757 CFTypeRef entry
= CFDictionaryGetValue(a
->circles
, name
);
1759 if (CFGetTypeID(entry
) == SOSCircleGetTypeID())
1760 return (SOSCircleRef
) entry
;
1765 SOSCircleRef
SOSAccountFindCircle(SOSAccountRef a
, CFStringRef name
, CFErrorRef
*error
)
1767 CFTypeRef entry
= CFDictionaryGetValue(a
->circles
, name
);
1769 require_action_quiet(!isNull(entry
), fail
,
1770 SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("Incompatible circle in KVS"), NULL
, error
));
1772 require_action_quiet(entry
, fail
,
1773 SOSCreateError(kSOSErrorNoCircle
, CFSTR("No circle found"), NULL
, error
));
1776 return (SOSCircleRef
) entry
;
1782 SOSCircleRef
SOSAccountEnsureCircle(SOSAccountRef a
, CFStringRef name
, CFErrorRef
*error
)
1784 CFErrorRef localError
= NULL
;
1786 SOSCircleRef circle
= SOSAccountFindCircle(a
, name
, &localError
);
1788 require_action_quiet(circle
|| !isSOSErrorCoded(localError
, kSOSErrorIncompatibleCircle
), fail
,
1789 if (error
) { *error
= localError
; localError
= NULL
; });
1792 if (NULL
== circle
) {
1793 circle
= SOSCircleCreate(NULL
, name
, NULL
);
1795 CFDictionaryAddValue(a
->circles
, name
, circle
);
1797 circle
= SOSAccountFindCircle(a
, name
, &localError
);
1799 SOSUpdateKeyInterest(a
, false, NULL
);
1803 CFReleaseNull(localError
);
1808 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
)
1810 CFArrayAppendValue(a
->change_blocks
, changeBlock
);
1813 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
)
1815 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
1818 static void DifferenceAndCall(CFArrayRef old_members
, CFArrayRef new_members
, void (^updatedCircle
)(CFArrayRef additions
, CFArrayRef removals
))
1820 CFMutableArrayRef additions
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, new_members
);
1821 CFMutableArrayRef removals
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, old_members
);
1824 CFArrayForEach(old_members
, ^(const void * value
) {
1825 CFArrayRemoveAllValue(additions
, value
);
1828 CFArrayForEach(new_members
, ^(const void * value
) {
1829 CFArrayRemoveAllValue(removals
, value
);
1832 updatedCircle(additions
, removals
);
1834 CFReleaseSafe(additions
);
1835 CFReleaseSafe(removals
);
1838 static void SOSAccountNotifyOfChange(SOSAccountRef account
, SOSCircleRef oldCircle
, SOSCircleRef newCircle
)
1840 CFMutableArrayRef old_members
= SOSCircleCopyPeers(oldCircle
, kCFAllocatorDefault
);
1841 CFMutableArrayRef new_members
= SOSCircleCopyPeers(newCircle
, kCFAllocatorDefault
);
1843 CFMutableArrayRef old_applicants
= SOSCircleCopyApplicants(oldCircle
, kCFAllocatorDefault
);
1844 CFMutableArrayRef new_applicants
= SOSCircleCopyApplicants(newCircle
, kCFAllocatorDefault
);
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
);
1854 CFReleaseNull(old_applicants
);
1855 CFReleaseNull(new_applicants
);
1857 CFReleaseNull(old_members
);
1858 CFReleaseNull(new_members
);
1861 void SOSAccountForEachCircle(SOSAccountRef account
, void (^process
)(SOSCircleRef circle
))
1863 CFDictionaryForEach(account
->circles
, ^(const void* key
, const void* value
) {
1865 process((SOSCircleRef
)value
);
1869 static void AppendCircleKeyName(CFMutableArrayRef array
, CFStringRef name
) {
1870 CFStringRef circle_key
= SOSCircleKeyCreateWithName(name
, NULL
);
1871 CFArrayAppendValue(array
, circle_key
);
1872 CFReleaseNull(circle_key
);
1875 static inline void AppendCircleInterests(CFMutableArrayRef circle_keys
, CFMutableArrayRef retiree_keys
, CFMutableArrayRef message_keys
, SOSCircleRef circle
, SOSFullPeerInfoRef me
) {
1876 CFStringRef my_peer_id
= NULL
;
1879 SOSPeerInfoRef my_peer
= me
? SOSFullPeerInfoGetPeerInfo(me
) : NULL
;
1880 my_peer_id
= SOSPeerInfoGetPeerID(my_peer
);
1884 CFStringRef circleName
= SOSCircleGetName(circle
);
1885 AppendCircleKeyName(circle_keys
, circleName
);
1888 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
1889 if (!CFEqualSafe(my_peer_id
, SOSPeerInfoGetPeerID(peer
))) {
1890 CFStringRef peer_name
= SOSPeerInfoGetPeerID(peer
);
1892 CFStringRef retirementKey
= SOSRetirementKeyCreateWithCircleAndPeer(circle
, peer_name
);
1893 CFArrayAppendValue(retiree_keys
, retirementKey
);
1894 CFReleaseNull(retirementKey
);
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
);
1906 static void SOSAccountCopyKeyInterests(SOSAccountRef account
,
1907 CFMutableArrayRef alwaysKeys
,
1908 CFMutableArrayRef afterFirstUnlockKeys
,
1909 CFMutableArrayRef whenUnlockedKeys
)
1911 CFArrayAppendValue(afterFirstUnlockKeys
, kSOSKVSKeyParametersKey
);
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
);
1920 AppendCircleInterests(afterFirstUnlockKeys
, afterFirstUnlockKeys
, inCircle
? whenUnlockedKeys
: NULL
, circle
, full_peer
);
1924 static bool SOSUpdateKeyInterest(SOSAccountRef account
, bool getNewKeysOnly
, CFErrorRef
*error
)
1926 if (account
->update_interest_block
) {
1928 CFMutableArrayRef alwaysKeys
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1929 CFMutableArrayRef afterFirstUnlockKeys
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1930 CFMutableArrayRef whenUnlockedKeys
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1932 SOSAccountCopyKeyInterests(account
, alwaysKeys
, afterFirstUnlockKeys
, whenUnlockedKeys
);
1934 account
->update_interest_block(getNewKeysOnly
, alwaysKeys
, afterFirstUnlockKeys
, whenUnlockedKeys
);
1936 CFReleaseNull(alwaysKeys
);
1937 CFReleaseNull(afterFirstUnlockKeys
);
1938 CFReleaseNull(whenUnlockedKeys
);
1944 static bool SOSAccountSendPendingChanges(SOSAccountRef account
, CFErrorRef
*error
) {
1945 CFErrorRef changeError
= NULL
;
1947 if (CFDictionaryGetCount(account
->pending_changes
) == 0)
1950 bool success
= account
->update_block(account
->pending_changes
, &changeError
);
1952 CFDictionaryRemoveAllValues(account
->pending_changes
);
1954 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
1955 CFSTR("Send changes block failed [%@]"), account
->pending_changes
);
1961 static bool SOSAccountAddCircleToPending(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
)
1963 bool success
= false;
1964 CFDataRef circle_data
= SOSCircleCopyEncodedData(circle
, kCFAllocatorDefault
, error
);
1967 CFStringRef circle_key
= SOSCircleKeyCreateWithCircle(circle
, NULL
);
1969 CFDictionarySetValue(account
->pending_changes
, circle_key
, circle_data
);
1972 CFReleaseNull(circle_data
);
1973 CFReleaseNull(circle_key
);
1980 static void SOSAccountRecordRetiredPeerInCircleNamed(SOSAccountRef account
, CFStringRef circleName
, SOSPeerInfoRef retiree
)
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
);
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;
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
));
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
);
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
);
2025 // Store the retirement record locally.
2026 CFDictionarySetValue(account
->retired_peers
, retire_key
, retire_value
);
2028 // Write pending change to KVS
2029 CFDictionarySetValue(account
->pending_changes
, retire_key
, retire_value
);
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
);
2036 SOSAccountAddCircleToPending(account
, circle
, NULL
);
2041 CFReleaseNull(localError
);
2042 CFReleaseNull(retire_peer
);
2043 CFReleaseNull(retire_key
);
2044 CFReleaseNull(retire_value
);
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.
2055 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
2056 with server but initial round trip with server does not imply
2057 NSUbiquitousKeyValueStoreInitialSyncChange.
2061 // MARK: Handle Circle Updates
2065 static bool SOSAccountHandleUpdateCircle(SOSAccountRef account
, SOSCircleRef prospective_circle
, bool writeUpdate
, bool initialSync
, CFErrorRef
*error
)
2067 bool success
= true;
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
);
2075 if (!prospective_circle
) {
2076 secerror("##### Can't update to a NULL circle ######");
2077 return false; // Can't update one we don't have.
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
);
2086 secerror("##### Processing initial sync. Old (local) circle: %@, New (cloud) circle: %@", oldCircle
, prospective_circle
);
2089 return false; // Can't update one we don't have.
2091 SOSAccountScanForRetired(account
, prospective_circle
, error
);
2092 SOSCircleRef newCircle
= SOSAccountCloneCircleWithRetirement(account
, prospective_circle
, error
);
2093 if(!newCircle
) return false;
2095 SOSCircleUpdatePeerInfo(newCircle
, me
);
2105 circle_action_t circle_action
= ignore
;
2106 enum DepartureReason leave_reason
= kSOSNeverLeftCircle
;
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
);
2113 SOSConcordanceStatus concstat
=
2114 SOSCircleConcordanceTrust(oldCircle
, newCircle
,
2115 old_circle_key
, account
->user_public
,
2118 CFStringRef concStr
= NULL
;
2120 case kSOSConcordanceTrusted
:
2121 circle_action
= countersign
;
2122 concStr
= CFSTR("Trusted");
2124 case kSOSConcordanceGenOld
:
2125 circle_action
= userTrustedOldCircle
? revert
: ignore
;
2126 concStr
= CFSTR("Generation Old");
2128 case kSOSConcordanceBadUserSig
:
2129 case kSOSConcordanceBadPeerSig
:
2130 circle_action
= userTrustedOldCircle
? revert
: accept
;
2131 concStr
= CFSTR("Bad Signature");
2133 case kSOSConcordanceNoUserSig
:
2134 circle_action
= userTrustedOldCircle
? revert
: accept
;
2135 concStr
= CFSTR("No User Signature");
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
);
2142 case kSOSConcordanceNoPeer
:
2143 circle_action
= leave
;
2144 leave_reason
= kSOSLeftUntrustedCircle
;
2145 concStr
= CFSTR("No trusted peer left");
2147 case kSOSConcordanceNoUserKey
:
2148 secerror("##### No User Public Key Available, this shouldn't ever happen!!!");
2152 secerror("##### Bad Error Return from ConcordanceTrust");
2157 secnotice("signing", "Decided on action %d based on concordance state %d and %s circle.", circle_action
, concstat
, userTrustedOldCircle
? "trusted" : "untrusted");
2159 SOSCircleRef circleToPush
= NULL
;
2161 if (circle_action
== leave
) {
2162 circle_action
= ignore
;
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
;
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
;
2180 if (circle_action
== countersign
) {
2181 if (me
&& SOSCircleHasPeer(newCircle
, me
, NULL
) && !SOSCircleVerifyPeerSigned(newCircle
, me
, NULL
)) {
2182 CFErrorRef signing_error
= NULL
;
2184 if (me_full
&& SOSCircleConcordanceSign(newCircle
, me_full
, &signing_error
)) {
2185 circleToPush
= newCircle
;
2186 secnotice("signing", "Concurred with: %@", newCircle
);
2188 secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error
, oldCircle
, newCircle
);
2190 CFReleaseSafe(signing_error
);
2192 circle_action
= accept
;
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
;
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
);
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
);
2219 SOSCircleRequestReadmission(newCircle
, account
->user_public
, me_full
, NULL
);
2224 CFRetain(oldCircle
); // About to replace the oldCircle
2225 CFDictionarySetValue(account
->circles
, newCircleName
, newCircle
);
2226 SOSAccountSetPreviousPublic(account
);
2228 secnotice("signing", "%@, Accepting circle: %@", concStr
, newCircle
);
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.
2238 if (SOSCircleRequestReadmission(newCircle
, account
->user_public
, me_full
, NULL
))
2242 if (me
&& SOSCircleHasActivePeer(oldCircle
, me
, NULL
)) {
2243 SOSAccountCleanupRetirementTickets(account
, RETIREMENT_FINALIZATION_SECONDS
, NULL
);
2246 SOSAccountNotifyOfChange(account
, oldCircle
, newCircle
);
2248 CFReleaseNull(oldCircle
);
2251 circleToPush
= newCircle
;
2253 success
= SOSUpdateKeyInterest(account
, true, error
);
2256 if (circle_action
== revert
) {
2257 secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr
, newCircle
, oldCircle
);
2259 circleToPush
= oldCircle
;
2263 if (circleToPush
!= NULL
) {
2265 && SOSAccountAddCircleToPending(account
, circleToPush
, error
)
2266 && SOSAccountSendPendingChanges(account
, error
));
2269 CFReleaseSafe(newCircle
);
2274 static bool SOSAccountUpdateCircleFromRemote(SOSAccountRef account
, SOSCircleRef newCircle
, bool initialSync
, CFErrorRef
*error
)
2276 return SOSAccountHandleUpdateCircle(account
, newCircle
, false, initialSync
, error
);
2279 bool SOSAccountUpdateCircle(SOSAccountRef account
, SOSCircleRef newCircle
, CFErrorRef
*error
)
2281 return SOSAccountHandleUpdateCircle(account
, newCircle
, true, false, error
);
2284 bool SOSAccountModifyCircle(SOSAccountRef account
,
2285 CFStringRef circleName
,
2287 void (^action
)(SOSCircleRef circle
))
2289 bool success
= false;
2291 SOSCircleRef circle
= NULL
;
2292 SOSCircleRef accountCircle
= SOSAccountFindCircle(account
, circleName
, error
);
2293 require_quiet(accountCircle
, fail
);
2295 circle
= SOSCircleCopyCircle(kCFAllocatorDefault
, accountCircle
, error
);
2296 require_quiet(circle
, fail
);
2299 success
= SOSAccountUpdateCircle(account
, circle
, error
);
2302 CFReleaseSafe(circle
);
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
);
2315 SOSCircleRef circle
= NULL
;
2316 if (!value
|| isNull(value
)) {
2317 circle
= SOSCircleCreate(kCFAllocatorDefault
, circleName
, error
);
2319 circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, error
);
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
);
2332 static SOSCCStatus
SOSCCCircleStatus(SOSCircleRef circle
)
2334 if (SOSCircleCountPeers(circle
) == 0)
2335 return kSOSCCCircleAbsent
;
2337 return kSOSCCNotInCircle
;
2340 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
)
2342 if (SOSCircleCountPeers(circle
) == 0)
2343 return kSOSCCCircleAbsent
;
2345 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
2346 return kSOSCCInCircle
;
2348 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
2349 return kSOSCCRequestPending
;
2351 return kSOSCCNotInCircle
;
2354 static SOSCCStatus
UnionStatus(SOSCCStatus accumulated_status
, SOSCCStatus additional_circle_status
)
2356 switch (additional_circle_status
) {
2357 case kSOSCCInCircle
:
2358 return accumulated_status
;
2359 case kSOSCCRequestPending
:
2360 return (accumulated_status
== kSOSCCInCircle
) ?
2361 kSOSCCRequestPending
:
2363 case kSOSCCNotInCircle
:
2364 return (accumulated_status
== kSOSCCInCircle
||
2365 accumulated_status
== kSOSCCRequestPending
) ?
2368 case kSOSCCCircleAbsent
:
2369 return (accumulated_status
== kSOSCCInCircle
||
2370 accumulated_status
== kSOSCCRequestPending
||
2371 accumulated_status
== kSOSCCNotInCircle
) ?
2372 kSOSCCCircleAbsent
:
2375 return additional_circle_status
;
2380 SOSCCStatus
SOSAccountIsInCircles(SOSAccountRef account
, CFErrorRef
* error
)
2382 if (!SOSAccountHasPublicKey(account
, error
)) {
2386 __block
bool set_once
= false;
2387 __block SOSCCStatus status
= kSOSCCInCircle
;
2389 SOSAccountForEachKnownCircle(account
, ^(CFStringRef name
) {
2391 status
= kSOSCCError
;
2392 SOSCreateError(kSOSErrorIncompatibleCircle
, CFSTR("Incompatible circle"), NULL
, error
);
2393 }, ^(SOSCircleRef circle
) {
2395 status
= UnionStatus(status
, SOSCCCircleStatus(circle
));
2396 }, ^(SOSCircleRef circle
, SOSFullPeerInfoRef full_peer
) {
2398 SOSCCStatus circle_status
= SOSCCThisDeviceStatusInCircle(circle
, SOSFullPeerInfoGetPeerInfo(full_peer
));
2399 status
= UnionStatus(status
, circle_status
);
2403 status
= kSOSCCCircleAbsent
;
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
;
2415 CFDictionaryRef gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
2416 kPIUserDefinedDeviceName
, CFSTR("iCloud"),
2418 require_action_quiet(gestalt
, fail
, SecError(errSecAllocate
, error
, CFSTR("Can't allocate gestalt")));
2420 cloud_peer
= SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, gestalt
, cloud_key
, error
);
2422 require(cloud_peer
, fail
);
2424 query
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
2425 kSecClass
, kSecClassKey
,
2426 kSecAttrSynchronizable
,kCFBooleanTrue
,
2427 kSecUseTombstones
, kCFBooleanTrue
,
2428 kSecValueRef
, cloud_key
,
2431 new_name
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
2432 CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(cloud_peer
));
2434 change
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
2435 kSecAttrLabel
, new_name
,
2438 SecError(SecItemUpdate(query
, change
), error
, CFSTR("Couldn't update name"));
2441 CFReleaseNull(new_name
);
2442 CFReleaseNull(query
);
2443 CFReleaseNull(change
);
2444 CFReleaseNull(gestalt
);
2445 CFReleaseNull(cloud_key
);
2450 static SOSFullPeerInfoRef
CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer
, CFErrorRef
*error
) {
2451 return SOSFullPeerInfoCreateCloudIdentity(NULL
, cloudPeer
, error
);
2454 static bool SOSAccountResetThisCircleToOffering(SOSAccountRef account
, SOSCircleRef circle
, SecKeyRef user_key
, CFErrorRef
*error
) {
2455 SOSFullPeerInfoRef myCirclePeer
= SOSAccountGetMyFullPeerInCircle(account
, circle
, error
);
2459 SOSAccountModifyCircle(account
, SOSCircleGetName(circle
), error
, ^(SOSCircleRef circle
) {
2460 bool result
= false;
2461 SOSFullPeerInfoRef cloud_identity
= NULL
;
2462 CFErrorRef localError
= NULL
;
2464 require_quiet(SOSCircleResetToOffering(circle
, user_key
, myCirclePeer
, &localError
), err_out
);
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
);
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
);
2477 SOSAccountPublishCloudParameters(account
, NULL
);
2480 if (result
== false)
2481 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
2482 if (localError
&& error
&& *error
== NULL
) {
2483 *error
= localError
;
2486 CFReleaseNull(localError
);
2487 CFReleaseNull(cloud_identity
);
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
;
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
);
2505 if (SOSCircleCountPeers(circle
) == 0) {
2506 result
= SOSAccountResetThisCircleToOffering(account
, circle
, user_key
, error
);
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
);
2519 secerror("Failed to join with cloud identity: %@", localError
);
2520 CFReleaseNull(localError
);
2527 CFReleaseNull(cloud_full_peer
);
2531 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) {
2532 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
2536 __block
bool success
= true;
2538 SOSAccountForEachKnownCircle(account
, ^(CFStringRef name
) { // Incompatible
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
);
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
);
2558 success
= SOSAccountJoinThisCircle(account
, user_key
, circle
, use_cloud_identity
, error
) && success
;
2565 if(success
) account
->departure_code
= kSOSNeverLeftCircle
;
2570 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) {
2571 return SOSAccountJoinCircles_internal(account
, false, error
);
2575 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) {
2576 return SOSAccountJoinCircles_internal(account
, true, error
);
2580 bool SOSAccountLeaveCircles(SOSAccountRef account
, CFErrorRef
* error
)
2582 __block
bool result
= true;
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!
2590 account
->departure_code
= kSOSWithdrewMembership
;
2592 return SOSAccountSendPendingChanges(account
, error
) && result
;
2595 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
)
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;
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!
2610 account
->departure_code
= kSOSWithdrewMembership
;
2611 if(result
) result
= SOSAccountSendPendingChanges(account
, error
);
2613 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
2615 dispatch_group_wait(group
, milestone
);
2616 dispatch_release(group
);
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
);
2637 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
2638 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
2642 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
2643 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
2647 __block
bool success
= true;
2648 __block
int64_t num_peers
= 0;
2650 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
2651 if (!SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
))
2654 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
2660 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
2661 __block
bool success
= true;
2662 __block
int64_t num_peers
= 0;
2664 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
2665 if (!SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
))
2668 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
2674 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) {
2675 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
2679 __block
bool result
= true;
2681 SOSAccountForEachKnownCircle(account
, ^(CFStringRef name
) {
2682 SOSCircleRef circle
= SOSCircleCreate(NULL
, name
, NULL
);
2684 CFDictionaryAddValue(account
->circles
, name
, circle
);
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
);
2696 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
2697 if (!SOSAccountHasPublicKey(account
, error
))
2700 __block
bool result
= true;
2701 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2702 SOSAccountModifyCircle(account
, SOSCircleGetName(circle
), error
, ^(SOSCircleRef circle
) {
2703 if (!SOSCircleResetToEmpty(circle
, error
))
2705 secerror("error: %@", *error
);
2708 account
->departure_code
= kSOSWithdrewMembership
;
2715 CFArrayRef
SOSAccountCopyApplicants(SOSAccountRef account
, CFErrorRef
*error
) {
2716 if (!SOSAccountHasPublicKey(account
, error
))
2718 CFMutableArrayRef applicants
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2720 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2721 SOSCircleForEachApplicant(circle
, ^(SOSPeerInfoRef peer
) {
2722 CFArrayAppendValue(applicants
, peer
);
2729 CFArrayRef
SOSAccountCopyPeers(SOSAccountRef account
, CFErrorRef
*error
) {
2730 if (!SOSAccountHasPublicKey(account
, error
))
2733 CFMutableArrayRef peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2735 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2736 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
2737 CFArrayAppendValue(peers
, peer
);
2744 CFArrayRef
SOSAccountCopyActivePeers(SOSAccountRef account
, CFErrorRef
*error
) {
2745 if (!SOSAccountHasPublicKey(account
, error
))
2748 CFMutableArrayRef peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2750 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2751 SOSCircleForEachActivePeer(circle
, ^(SOSPeerInfoRef peer
) {
2752 CFArrayAppendValue(peers
, peer
);
2759 CFArrayRef
SOSAccountCopyActiveValidPeers(SOSAccountRef account
, CFErrorRef
*error
) {
2760 if (!SOSAccountHasPublicKey(account
, error
))
2763 CFMutableArrayRef peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2765 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2766 SOSCircleForEachActiveValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
2767 CFArrayAppendValue(peers
, peer
);
2775 CFArrayRef
SOSAccountCopyConcurringPeers(SOSAccountRef account
, CFErrorRef
*error
)
2777 if (!SOSAccountHasPublicKey(account
, error
))
2780 CFMutableArrayRef concurringPeers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2782 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
2783 CFMutableArrayRef circleConcurring
= SOSCircleCopyConcurringPeers(circle
, NULL
);
2784 CFArrayAppendArray(concurringPeers
, circleConcurring
, CFRangeMake(0, CFArrayGetCount(circleConcurring
)));
2785 CFReleaseSafe(circleConcurring
);
2788 return concurringPeers
;
2791 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
)
2793 return CFSTR("We're compatible, go away");
2796 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
)
2798 return account
->departure_code
;
2802 // TODO: Handle '|' and "¬" in other strings.
2804 const CFStringRef kSOSKVSKeyParametersKey
= CFSTR(">KeyParameters");
2805 const CFStringRef kSOSKVSInitialSyncKey
= CFSTR("^InitialSync");
2806 const CFStringRef kSOSKVSAccountChangedKey
= CFSTR("^AccountChanged");
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(":");
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));
2824 SOSKVSKeyType
SOSKVSKeyGetKeyTypeAndParse(CFStringRef key
, CFStringRef
*circle
, CFStringRef
*from
, CFStringRef
*to
)
2826 SOSKVSKeyType retval
= kUnknownKey
;
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
;
2838 CFRange fromRange
= CFRangeMake(1, CFStringGetLength(key
)-1);
2839 *circle
= CFStringCreateWithSubstring(NULL
, key
, fromRange
);
2843 CFStringRef mCircle
= NULL
;
2844 CFStringRef mFrom
= NULL
;
2845 CFStringRef mTo
= NULL
;
2846 CFMutableStringRef keycopy
= CFStringCreateMutableCopy(NULL
, 128, key
);
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
);
2856 retval
= kUnknownKey
;
2858 CFReleaseNull(mCircle
);
2859 CFReleaseNull(mFrom
);
2861 CFReleaseNull(keycopy
);
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
);
2874 retval
= kUnknownKey
;
2876 // TODO - Update our circle
2877 CFReleaseNull(mCircle
);
2878 CFReleaseNull(mPeer
);
2879 CFReleaseNull(keycopy
);
2882 case kAccountChangedKey
:
2883 case kParametersKey
:
2884 case kInitialSyncKey
:
2893 SOSKVSKeyType
SOSKVSKeyGetKeyType(CFStringRef key
)
2895 return SOSKVSKeyGetKeyTypeAndParse(key
, NULL
, NULL
, NULL
);
2898 CFStringRef
SOSCircleKeyCreateWithCircle(SOSCircleRef circle
, CFErrorRef
*error
)
2900 return SOSCircleKeyCreateWithName(SOSCircleGetName(circle
), error
);
2904 CFStringRef
SOSCircleKeyCreateWithName(CFStringRef circleName
, CFErrorRef
*error
)
2906 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@%@"), sCirclePrefix
, circleName
);
2909 CFStringRef
SOSCircleKeyCopyCircleName(CFStringRef key
, CFErrorRef
*error
)
2911 CFStringRef circleName
= NULL
;
2913 if (kCircleKey
!= SOSKVSKeyGetKeyTypeAndParse(key
, &circleName
, NULL
, NULL
)) {
2914 SOSCreateErrorWithFormat(kSOSErrorNoCircleName
, NULL
, error
, NULL
, CFSTR("Couldn't find circle name in key '%@'"), key
);
2916 CFReleaseNull(circleName
);
2922 CFStringRef
SOSMessageKeyCopyCircleName(CFStringRef key
, CFErrorRef
*error
)
2924 CFStringRef circleName
= NULL
;
2926 if (SOSKVSKeyGetKeyTypeAndParse(key
, &circleName
, NULL
, NULL
) != kMessageKey
) {
2927 SOSCreateErrorWithFormat(kSOSErrorNoCircleName
, NULL
, error
, NULL
, CFSTR("Couldn't find circle name in key '%@'"), key
);
2929 CFReleaseNull(circleName
);
2934 CFStringRef
SOSMessageKeyCopyFromPeerName(CFStringRef messageKey
, CFErrorRef
*error
)
2936 CFStringRef fromPeer
= NULL
;
2938 if (SOSKVSKeyGetKeyTypeAndParse(messageKey
, NULL
, &fromPeer
, NULL
) != kMessageKey
) {
2939 SOSCreateErrorWithFormat(kSOSErrorNoCircleName
, NULL
, error
, NULL
, CFSTR("Couldn't find from peer in key '%@'"), messageKey
);
2941 CFReleaseNull(fromPeer
);
2946 CFStringRef
SOSMessageKeyCreateWithCircleAndPeerNames(SOSCircleRef circle
, CFStringRef from_peer_name
, CFStringRef to_peer_name
)
2948 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@%@%@%@%@"),
2949 SOSCircleGetName(circle
), sCircleSeparator
, from_peer_name
, sFromToSeparator
, to_peer_name
);
2952 CFStringRef
SOSMessageKeyCreateWithCircleAndPeerInfos(SOSCircleRef circle
, SOSPeerInfoRef from_peer
, SOSPeerInfoRef to_peer
)
2954 return SOSMessageKeyCreateWithCircleAndPeerNames(circle
, SOSPeerInfoGetPeerID(from_peer
), SOSPeerInfoGetPeerID(to_peer
));
2957 CFStringRef
SOSMessageKeyCreateWithAccountAndPeer(SOSAccountRef account
, SOSCircleRef circle
, CFStringRef peer_name
) {
2958 // TODO: Handle errors!
2959 CFErrorRef error
= NULL
;
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
);
2968 CFStringRef
SOSRetirementKeyCreateWithCircleAndPeer(SOSCircleRef circle
, CFStringRef retirement_peer_name
)
2970 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@%@%@%@"),
2971 sRetirementPrefix
, SOSCircleGetName(circle
), sCircleSeparator
, retirement_peer_name
);
2975 static SOSPeerCoderStatus
SOSAccountHandlePeerMessage(SOSAccountRef account
,
2976 CFStringRef circle_id
,
2977 CFStringRef peer_name
,
2979 SOSAccountSendBlock send_block
,
2982 bool success
= false;
2983 CFStringRef peer_key
= NULL
;
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")));
2991 peer_key
= SOSMessageKeyCreateWithAccountAndPeer(account
, circle
, peer_name
);
2993 SOSPeerSendBlock peer_send_block
= ^bool (CFDataRef message
, CFErrorRef
*error
) {
2994 return send_block(circle
, peer_key
, message
, error
);
2997 success
= SOSCircleHandlePeerMessage(circle
, myFullPeer
, account
->factory
, peer_send_block
, peer_name
, message
, error
);
3000 CFReleaseNull(peer_key
);
3004 bool SOSAccountHandleUpdates(SOSAccountRef account
,
3005 CFDictionaryRef updates
,
3006 CFErrorRef
*error
) {
3008 if(CFDictionaryGetValue(updates
, kSOSKVSAccountChangedKey
) != NULL
) {
3009 SOSAccountSetToNew(account
);
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
));
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.");
3026 CFReleaseNull(account
->user_public
);
3027 SOSAccountPurgePrivateCredential(account
);
3028 CFReleaseNull(account
->user_key_parameters
);
3030 account
->user_public_trusted
= false;
3032 account
->user_public
= newKey
;
3035 account
->user_key_parameters
= newParameters
;
3036 newParameters
= NULL
;
3038 secnotice("updates", "Got new parameters for public key: %@", account
->user_public
);
3039 debugDumpUserParameters(CFSTR("params"), account
->user_key_parameters
);
3043 CFReleaseNull(newKey
);
3044 CFReleaseNull(newParameters
);
3047 if (!account
->user_public_trusted
) {
3048 if (!account
->deferred_updates
) {
3049 account
->deferred_updates
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
3052 CFDictionaryForEach(updates
, ^(const void *key
, const void *value
) {
3053 if (!CFEqualSafe(key
, kSOSKVSKeyParametersKey
) && !CFEqualSafe(key
, kSOSKVSAccountChangedKey
))
3054 CFDictionarySetValue(account
->deferred_updates
, key
, value
);
3056 secnotice("updates", "No public peer key, deferring updates: %@", updates
);
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
);
3065 CFDictionaryForEach(updates
, ^(const void *key
, const void *value
) {
3066 CFStringRef circle_name
= NULL
;
3067 CFErrorRef localError
= NULL
;
3068 SOSCircleRef circle
= NULL
;
3070 if (SOSKVSKeyGetKeyTypeAndParse(key
, &circle_name
, NULL
, NULL
) == kCircleKey
) {
3071 circle
= SOSAccountCreateCircleFrom(circle_name
, value
, &localError
);
3073 if (isSOSErrorCoded(localError
, kSOSErrorIncompatibleCircle
)) {
3074 SOSAccountDestroyCirclePeerInfoNamed(account
, circle_name
, NULL
);
3075 CFDictionarySetValue(account
->circles
, circle_name
, kCFNull
);
3077 SOSCreateErrorWithFormat(kSOSErrorNameMismatch
, localError
, error
, NULL
,
3078 CFSTR("Bad key for message, no circle '%@'"), key
);
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
);
3091 CFReleaseSafe(circle_name
);
3092 CFReleaseNull(circle
);
3093 CFReleaseNull(localError
);
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
:
3108 SOSFullPeerInfoRef my_peer
= NULL
;
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
);
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
);
3118 SOSAccountSendBlock cacheInDictionary
= ^ bool (SOSCircleRef circle
, CFStringRef key
, CFDataRef new_message
, CFErrorRef
* error
) {
3119 CFDictionarySetValue(account
->pending_changes
, key
, new_message
);
3121 if (account
->processed_message_block
) {
3122 account
->processed_message_block(circle
, value
, new_message
);
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
3139 case kRetirementKey
:
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
);
3150 case kAccountChangedKey
: // Handled at entry to function to make sure these are processed first.
3154 secnotice("updates", "Unknown key '%@', ignoring", key
);
3159 CFReleaseNull(circle_name
);
3160 CFReleaseNull(from_name
);
3161 CFReleaseNull(to_name
);
3163 if (error
&& *error
)
3164 secerror("Peer message processing error for: %@ -> %@ (%@)", key
, value
, *error
);
3166 secerror("Peer message local processing error for: %@ -> %@ (%@)", key
, value
, localError
);
3168 CFReleaseNull(localError
);
3171 return SOSAccountSendPendingChanges(account
, error
);
3174 void SOSAccountSetMessageProcessedBlock(SOSAccountRef account
, SOSAccountMessageProcessedBlock processedBlock
)
3176 CFRetainSafe(processedBlock
);
3177 CFReleaseNull(account
->processed_message_block
);
3178 account
->processed_message_block
= processedBlock
;
3181 CFStringRef
SOSInterestListCopyDescription(CFArrayRef interests
)
3183 CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
3184 CFStringAppendFormat(description
, NULL
, CFSTR("<Interest: "));
3186 CFArrayForEach(interests
, ^(const void* string
) {
3187 if (isString(string
))
3188 CFStringAppendFormat(description
, NULL
, CFSTR(" '%@'"), string
);
3190 CFStringAppend(description
, CFSTR(">"));