X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sec/SOSCircle/SecureObjectSync/SOSAccount.c?ds=sidebyside diff --git a/sec/SOSCircle/SecureObjectSync/SOSAccount.c b/sec/SOSCircle/SecureObjectSync/SOSAccount.c deleted file mode 100644 index 198a3904..00000000 --- a/sec/SOSCircle/SecureObjectSync/SOSAccount.c +++ /dev/null @@ -1,3191 +0,0 @@ -/* - * Created by Michael Brouwer on 6/22/12. - * Copyright 2012 Apple Inc. All Rights Reserved. - */ - -/* - * SOSAccount.c - Implementation of the secure object syncing account. - * An account contains a SOSCircle for each protection domain synced. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include // For SecError - -#include -#include - -#include - -static CFStringRef kicloud_identity_name = CFSTR("Cloud Identity"); - -// -// Forward statics. -// - -static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, bool writeUpdate, bool initialSync, CFErrorRef *error); - -// -// DER Encoding utilities -// - -// -// Encodes data or a zero length data -// -static size_t der_sizeof_data_or_null(CFDataRef data, CFErrorRef* error) -{ - if (data) { - return der_sizeof_data(data, error); - } else { - return der_sizeof_null(kCFNull, error); - } -} - -static uint8_t* der_encode_data_or_null(CFDataRef data, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - if (data) { - return der_encode_data(data, error, der, der_end); - } else { - return der_encode_null(kCFNull, error, der, der_end); - } -} - - -static const uint8_t* der_decode_data_or_null(CFAllocatorRef allocator, CFDataRef* data, - CFErrorRef* error, - const uint8_t* der, const uint8_t* der_end) -{ - CFTypeRef value = NULL; - der = der_decode_plist(allocator, 0, &value, error, der, der_end); - if (value && CFGetTypeID(value) != CFDataGetTypeID()) { - CFReleaseNull(value); - } - if (data) { - *data = value; - } - return der; -} - - -// -// Mark: public_bytes encode/decode -// - -static size_t der_sizeof_public_bytes(SecKeyRef publicKey, CFErrorRef* error) -{ - CFDataRef publicData = NULL; - - if (publicKey) - SecKeyCopyPublicBytes(publicKey, &publicData); - - size_t size = der_sizeof_data_or_null(publicData, error); - - CFReleaseNull(publicData); - - return size; -} - -static uint8_t* der_encode_public_bytes(SecKeyRef publicKey, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - CFDataRef publicData = NULL; - - if (publicKey) - SecKeyCopyPublicBytes(publicKey, &publicData); - - uint8_t *result = der_encode_data_or_null(publicData, error, der, der_end); - - CFReleaseNull(publicData); - - return result; -} - -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) -{ - CFDataRef dataFound = NULL; - der = der_decode_data_or_null(allocator, &dataFound, error, der, der_end); - - if (der && dataFound && publicKey) { - *publicKey = SecKeyCreateFromPublicData(allocator, algorithmID, dataFound); - } - CFReleaseNull(dataFound); - - return der; -} - - -// -// Cloud Paramters encode/decode -// - -static size_t der_sizeof_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error) -{ - size_t public_key_size = der_sizeof_public_bytes(publicKey, error); - size_t parameters_size = der_sizeof_data_or_null(paramters, error); - - return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, public_key_size + parameters_size); -} - -static uint8_t* der_encode_cloud_parameters(SecKeyRef publicKey, CFDataRef paramters, CFErrorRef* error, - const uint8_t* der, uint8_t* der_end) -{ - uint8_t* original_der_end = der_end; - - return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, original_der_end, der, - der_encode_public_bytes(publicKey, error, der, - der_encode_data_or_null(paramters, error, der, der_end))); -} - -static const uint8_t* der_decode_cloud_parameters(CFAllocatorRef allocator, - CFIndex algorithmID, SecKeyRef* publicKey, - CFDataRef *parameters, - CFErrorRef* error, - const uint8_t* der, const uint8_t* der_end) -{ - const uint8_t *sequence_end; - der = ccder_decode_sequence_tl(&sequence_end, der, der_end); - der = der_decode_public_bytes(allocator, algorithmID, publicKey, error, der, sequence_end); - der = der_decode_data_or_null(allocator, parameters, error, der, sequence_end); - - return der; -} - - -// -// bool encoding/decoding -// - - -static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end) -{ - if (NULL == der) - return NULL; - - size_t payload_size = 0; - const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end); - - if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) { - return NULL; - } - - if (boolean) - *boolean = (*payload != 0); - - return payload + payload_size; -} - - -static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error) -{ - return ccder_sizeof(CCDER_BOOLEAN, 1); -} - - -static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end) -{ - uint8_t value_byte = value; - - return ccder_encode_tl(CCDER_BOOLEAN, 1, der, - ccder_encode_body(1, &value_byte, der, der_end)); -} - -struct __OpaqueSOSAccount { - CFRuntimeBase _base; - - dispatch_queue_t queue; - - CFDictionaryRef gestalt; - - CFMutableDictionaryRef circle_identities; - CFMutableDictionaryRef circles; - CFMutableDictionaryRef retired_peers; - - bool user_public_trusted; - CFDataRef user_key_parameters; - SecKeyRef user_public; - SecKeyRef previous_public; - enum DepartureReason departure_code; - - // Non-persistent data - - SOSDataSourceFactoryRef factory; - SecKeyRef _user_private; - dispatch_source_t user_private_timer; - int lock_notification_token; - - // Live Notification - CFMutableArrayRef change_blocks; - - SOSAccountKeyInterestBlock update_interest_block; - SOSAccountDataUpdateBlock update_block; - SOSAccountMessageProcessedBlock processed_message_block; - - CFMutableDictionaryRef deferred_updates; - - CFMutableDictionaryRef pending_changes; -}; - -CFGiblisWithCompareFor(SOSAccount); - -static inline bool SOSAccountHasLeft(SOSAccountRef account) { - switch(account->departure_code) { - case kSOSWithdrewMembership: /* Fallthrough */ - case kSOSMembershipRevoked: /* Fallthrough */ - case kSOSLeftUntrustedCircle: - return true; - case kSOSNeverAppliedToCircle: /* Fallthrough */ - case kSOSNeverLeftCircle: /* Fallthrough */ - default: - return false; - } -} - -// Private static functions. - -static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error); - -static bool SOSAccountEnsureFactoryCircles(SOSAccountRef a) -{ - bool result = false; - if (a) - { - require(a->factory, xit); - CFArrayRef circle_names = a->factory->copy_names(a->factory); - require(circle_names, xit); - CFArrayForEach(circle_names, ^(const void*name) { - if (isString(name)) - SOSAccountEnsureCircle(a, (CFStringRef)name, NULL); - }); - - CFReleaseNull(circle_names); - result = true; - } -xit: - return result; -} - -static SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator, - CFDictionaryRef gestalt, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block) { - SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator); - - a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL); - - a->gestalt = gestalt; - CFRetain(a->gestalt); - - a->circles = CFDictionaryCreateMutableForCFTypes(allocator); - a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator); - a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator); - - a->factory = factory; // We adopt the factory. kthanksbai. - - a->change_blocks = CFArrayCreateMutableForCFTypes(allocator); - - a->update_interest_block = Block_copy(interest_block); - a->update_block = Block_copy(update_block); - - a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator); - a->departure_code = kSOSNeverAppliedToCircle; - - return a; -} - - -static SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamedIfPresent(SOSAccountRef account, CFStringRef name, CFErrorRef *error) { - if (CFDictionaryGetValue(account->circles, name) == NULL) { - SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name); - return NULL; - } - - return (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name); -} - - -static void SOSAccountForEachKnownCircle(SOSAccountRef account, - void (^handle_incompatible)(CFStringRef name), - void (^handle_no_peer)(SOSCircleRef circle), - void (^handle_peer)(SOSCircleRef circle, SOSFullPeerInfoRef full_peer)) { - CFDictionaryForEach(account->circles, ^(const void *key, const void *value) { - if (isNull(value)) { - if (handle_incompatible) - handle_incompatible((CFStringRef)key); - } else { - SOSCircleRef circle = (SOSCircleRef) value; - CFRetainSafe(circle); - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL); - if (!fpi) { - if (handle_no_peer) - handle_no_peer(circle); - } else { - CFRetainSafe(fpi); - if (handle_peer) - handle_peer(circle, fpi); - CFReleaseSafe(fpi); - } - CFReleaseSafe(circle); - } - }); -} - - -bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt) -{ - if (CFEqual(new_gestalt, account->gestalt)) - return false; - - SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { - if (SOSFullPeerInfoUpdateGestalt(full_peer, new_gestalt, NULL)) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), - NULL, ^(SOSCircleRef circle_to_change) { - (void) SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(full_peer)); - }); - }; - }); - - CFReleaseNull(account->gestalt); - account->gestalt = new_gestalt; - CFRetain(account->gestalt); - - return true; -} - -SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator, - CFDictionaryRef gestalt, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block) { - SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory, interest_block, update_block); - - SOSAccountEnsureFactoryCircles(a); - - return a; -} - -static void SOSAccountDestroy(CFTypeRef aObj) { - SOSAccountRef a = (SOSAccountRef) aObj; - - if (a->factory) - a->factory->release(a->factory); - - CFReleaseNull(a->gestalt); - CFReleaseNull(a->circle_identities); - CFReleaseNull(a->circles); - CFReleaseNull(a->retired_peers); - - a->user_public_trusted = false; - CFReleaseNull(a->user_public); - CFReleaseNull(a->user_key_parameters); - - SOSAccountPurgePrivateCredential(a); - CFReleaseNull(a->previous_public); - - CFReleaseNull(a->change_blocks); - Block_release(a->update_interest_block); - Block_release(a->update_block); - CFReleaseNull(a->processed_message_block); - CFReleaseNull(a->pending_changes); - CFReleaseNull(a->deferred_updates); - a->departure_code = kSOSNeverAppliedToCircle; - - dispatch_release(a->queue); -} - -static void SOSAccountSetToNew(SOSAccountRef a) { - CFAllocatorRef allocator = CFGetAllocator(a); - CFReleaseNull(a->circle_identities); - CFReleaseNull(a->circles); - CFReleaseNull(a->retired_peers); - - CFReleaseNull(a->user_key_parameters); - CFReleaseNull(a->user_public); - CFReleaseNull(a->previous_public); - CFReleaseNull(a->_user_private); - - CFReleaseNull(a->pending_changes); - CFReleaseNull(a->deferred_updates); - - a->user_public_trusted = false; - a->departure_code = kSOSNeverAppliedToCircle; - a->user_private_timer = 0; - a->lock_notification_token = 0; - - // keeping gestalt; - // keeping factory; - // Live Notification - // change_blocks; - // update_interest_block; - // update_block; - - a->circles = CFDictionaryCreateMutableForCFTypes(allocator); - a->circle_identities = CFDictionaryCreateMutableForCFTypes(allocator); - a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator); - a->pending_changes = CFDictionaryCreateMutableForCFTypes(allocator); - - SOSAccountEnsureFactoryCircles(a); -} - - -static CFStringRef SOSAccountCopyDescription(CFTypeRef aObj) { - SOSAccountRef a = (SOSAccountRef) aObj; - - return CFStringCreateWithFormat(NULL, NULL, CFSTR(""), a, a->gestalt, a->circles, a->circle_identities); -} - -static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs) -{ - SOSAccountRef laccount = (SOSAccountRef) lhs; - SOSAccountRef raccount = (SOSAccountRef) rhs; - - return CFEqual(laccount->gestalt, raccount->gestalt) - && CFEqual(laccount->circles, raccount->circles) - && CFEqual(laccount->circle_identities, raccount->circle_identities); - // ??? retired_peers -} - -#if OLD_CODERS_SUPPORTED - -// -// MARK: Persistent Encode decode -// -SOSAccountRef SOSAccountCreateFromDER_V1(CFAllocatorRef allocator, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block, - CFErrorRef* error, - const uint8_t** der_p, const uint8_t *der_end) -{ - SOSAccountRef account = NULL; - - const uint8_t *sequence_end; - *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); - - { - CFDictionaryRef decoded_gestalt = NULL; - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, - *der_p, der_end); - - if (*der_p == 0) - return NULL; - - account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block); - CFReleaseNull(decoded_gestalt); - } - - CFArrayRef array = NULL; - *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end); - - *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end); - *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end); - *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end); - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end); - if (*der_p != sequence_end) - *der_p = NULL; - - __block bool success = true; - - require_quiet(array && *der_p, fail); - - CFArrayForEach(array, ^(const void *value) { - if (success) { - if (isString(value)) { - CFDictionaryAddValue(account->circles, value, kCFNull); - } else { - CFDataRef circleData = NULL; - CFDataRef fullPeerInfoData = NULL; - - if (isData(value)) { - circleData = (CFDataRef) value; - } else if (isArray(value)) { - CFArrayRef pair = (CFArrayRef) value; - - CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0); - CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1); - - if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) { - circleData = (CFDataRef) circleObject; - fullPeerInfoData = (CFDataRef) fullPeerInfoObject; - } - } - - if (circleData) { - SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error); - require_action_quiet(circle, fail, success = false); - - CFStringRef circleName = SOSCircleGetName(circle); - CFDictionaryAddValue(account->circles, circleName, circle); - - if (fullPeerInfoData) { - SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error); - require_action_quiet(full_peer, fail, success = false); - - CFDictionaryAddValue(account->circle_identities, circleName, full_peer); - CFReleaseNull(full_peer); - } - fail: - CFReleaseNull(circle); - } - } - } - }); - CFReleaseNull(array); - - require_quiet(success, fail); - require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, - SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); - - return account; - -fail: - // Create a default error if we don't have one: - SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Account DER"), NULL, error); - CFReleaseNull(account); - return NULL; -} - -SOSAccountRef SOSAccountCreateFromDER_V2(CFAllocatorRef allocator, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block, - CFErrorRef* error, - const uint8_t** der_p, const uint8_t *der_end) -{ - SOSAccountRef account = NULL; - const uint8_t *dersave = *der_p; - const uint8_t *derend = der_end; - - const uint8_t *sequence_end; - *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); - - { - CFDictionaryRef decoded_gestalt = NULL; - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, - *der_p, der_end); - - if (*der_p == 0) - return NULL; - - account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block); - CFReleaseNull(decoded_gestalt); - } - - CFArrayRef array = NULL; - *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end); - - uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; - *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end); - *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end); - *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end); - *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end); - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end); - if (*der_p != sequence_end) - *der_p = NULL; - account->departure_code = (enum DepartureReason) tmp_departure_code; - - __block bool success = true; - - require_quiet(array && *der_p, fail); - - CFArrayForEach(array, ^(const void *value) { - if (success) { - if (isString(value)) { - CFDictionaryAddValue(account->circles, value, kCFNull); - } else { - CFDataRef circleData = NULL; - CFDataRef fullPeerInfoData = NULL; - - if (isData(value)) { - circleData = (CFDataRef) value; - } else if (isArray(value)) { - CFArrayRef pair = (CFArrayRef) value; - - CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0); - CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1); - - if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) { - circleData = (CFDataRef) circleObject; - fullPeerInfoData = (CFDataRef) fullPeerInfoObject; - } - } - - if (circleData) { - SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error); - require_action_quiet(circle, fail, success = false); - - CFStringRef circleName = SOSCircleGetName(circle); - CFDictionaryAddValue(account->circles, circleName, circle); - - if (fullPeerInfoData) { - SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error); - require_action_quiet(full_peer, fail, success = false); - - CFDictionaryAddValue(account->circle_identities, circleName, full_peer); - } - fail: - CFReleaseNull(circle); - } - } - } - }); - CFReleaseNull(array); - - require_quiet(success, fail); - require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, - SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); - - return account; - -fail: - // Create a default error if we don't have one: - account->factory = NULL; // give the factory back. - CFReleaseNull(account); - // Try the der inflater from the previous release. - account = SOSAccountCreateFromDER_V1(allocator, factory, interest_block, update_block, error, &dersave, derend); - if(account) account->departure_code = kSOSNeverAppliedToCircle; - return account; -} - -#endif /* OLD_CODERS_SUPPORTED */ - -#define CURRENT_ACCOUNT_PERSISTENT_VERSION 6 - -SOSAccountRef SOSAccountCreateFromDER(CFAllocatorRef allocator, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block, - CFErrorRef* error, - const uint8_t** der_p, const uint8_t *der_end) -{ - SOSAccountRef account = NULL; -#if UPGRADE_FROM_PREVIOUS_VERSION - const uint8_t *dersave = *der_p; - const uint8_t *derend = der_end; -#endif - uint64_t version = 0; - - const uint8_t *sequence_end; - *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); - *der_p = ccder_decode_uint64(&version, *der_p, sequence_end); - if(!(*der_p) || version < CURRENT_ACCOUNT_PERSISTENT_VERSION) { -#if UPGRADE_FROM_PREVIOUS_VERSION - return SOSAccountCreateFromDER_V3(allocator, factory, interest_block, update_block, error, &dersave, derend); -#else - return NULL; -#endif - } - - { - CFDictionaryRef decoded_gestalt = NULL; - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, - *der_p, der_end); - - if (*der_p == 0) - return NULL; - - account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block); - CFReleaseNull(decoded_gestalt); - } - - CFArrayRef array = NULL; - *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end); - - uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; - *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end); - *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end); - *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end); - *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, sequence_end); - *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end); - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end); - if (*der_p != sequence_end) - *der_p = NULL; - account->departure_code = (enum DepartureReason) tmp_departure_code; - - __block bool success = true; - - require_quiet(array && *der_p, fail); - - CFArrayForEach(array, ^(const void *value) { - if (success) { - if (isString(value)) { - CFDictionaryAddValue(account->circles, value, kCFNull); - } else { - CFDataRef circleData = NULL; - CFDataRef fullPeerInfoData = NULL; - - if (isData(value)) { - circleData = (CFDataRef) value; - } else if (isArray(value)) { - CFArrayRef pair = (CFArrayRef) value; - - CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0); - CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1); - - if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) { - circleData = (CFDataRef) circleObject; - fullPeerInfoData = (CFDataRef) fullPeerInfoObject; - } - } - - if (circleData) { - SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error); - require_action_quiet(circle, fail, success = false); - - CFStringRef circleName = SOSCircleGetName(circle); - CFDictionaryAddValue(account->circles, circleName, circle); - - if (fullPeerInfoData) { - SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error); - require_action_quiet(full_peer, fail, success = false); - - CFDictionaryAddValue(account->circle_identities, circleName, full_peer); - } - fail: - CFReleaseNull(circle); - } - } - } - }); - CFReleaseNull(array); - - require_quiet(success, fail); - require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, - SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); - - return account; - -fail: - account->factory = NULL; // give the factory back. - CFReleaseNull(account); - return NULL; -} - - -SOSAccountRef SOSAccountCreateFromDER_V3(CFAllocatorRef allocator, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block, - CFErrorRef* error, - const uint8_t** der_p, const uint8_t *der_end) -{ - SOSAccountRef account = NULL; - uint64_t version = 0; - - const uint8_t *sequence_end; - *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); - *der_p = ccder_decode_uint64(&version, *der_p, sequence_end); - if(!(*der_p) || version != 3) { - // In this case we want to silently fail so that an account gets newly created. - return NULL; - } - - { - CFDictionaryRef decoded_gestalt = NULL; - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, - *der_p, der_end); - - if (*der_p == 0) - return NULL; - - account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory, interest_block, update_block); - CFReleaseNull(decoded_gestalt); - } - - CFArrayRef array = NULL; - *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, sequence_end); - - uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; - *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, sequence_end); - *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, sequence_end); - *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, sequence_end); - *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, sequence_end); - *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &account->retired_peers, error, *der_p, sequence_end); - if (*der_p != sequence_end) - *der_p = NULL; - account->departure_code = (enum DepartureReason) tmp_departure_code; - - __block bool success = true; - - require_quiet(array && *der_p, fail); - - CFArrayForEach(array, ^(const void *value) { - if (success) { - if (isString(value)) { - CFDictionaryAddValue(account->circles, value, kCFNull); - } else { - CFDataRef circleData = NULL; - CFDataRef fullPeerInfoData = NULL; - - if (isData(value)) { - circleData = (CFDataRef) value; - } else if (isArray(value)) { - CFArrayRef pair = (CFArrayRef) value; - - CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0); - CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1); - - if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) { - circleData = (CFDataRef) circleObject; - fullPeerInfoData = (CFDataRef) fullPeerInfoObject; - } - } - - if (circleData) { - SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error); - require_action_quiet(circle, fail, success = false); - - CFStringRef circleName = SOSCircleGetName(circle); - CFDictionaryAddValue(account->circles, circleName, circle); - - if (fullPeerInfoData) { - SOSFullPeerInfoRef full_peer = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, error); - require_action_quiet(full_peer, fail, success = false); - - CFDictionaryAddValue(account->circle_identities, circleName, full_peer); - } - fail: - CFReleaseNull(circle); - } - } - } - }); - CFReleaseNull(array); - - require_quiet(success, fail); - require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, - SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); - - return account; - -fail: - // Create a default error if we don't have one: - account->factory = NULL; // give the factory back. - CFReleaseNull(account); - // Don't try the der inflater from the previous release. - // account = SOSAccountCreateFromDER_V2(allocator, factory, interest_block, update_block, error, &dersave, derend); - if(account) account->departure_code = kSOSNeverAppliedToCircle; - return account; -} - -SOSAccountRef SOSAccountCreateFromData(CFAllocatorRef allocator, CFDataRef circleData, - SOSDataSourceFactoryRef factory, - SOSAccountKeyInterestBlock interest_block, - SOSAccountDataUpdateBlock update_block, - CFErrorRef* error) -{ - size_t size = CFDataGetLength(circleData); - const uint8_t *der = CFDataGetBytePtr(circleData); - SOSAccountRef account = SOSAccountCreateFromDER(allocator, factory, interest_block, update_block, - error, - &der, der + size); - return account; -} - -static CFMutableArrayRef SOSAccountCopyCircleArrayToEncode(SOSAccountRef account) -{ - CFMutableArrayRef arrayToEncode = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - CFDictionaryForEach(account->circles, ^(const void *key, const void *value) { - if (isNull(value)) { - CFArrayAppendValue(arrayToEncode, key); // Encode the name of the circle that's out of date. - } else { - SOSCircleRef circle = (SOSCircleRef) value; - CFDataRef encodedCircle = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, NULL); - CFTypeRef arrayEntry = encodedCircle; - CFRetainSafe(arrayEntry); - - SOSFullPeerInfoRef full_peer = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, key); - - if (full_peer) { - CFDataRef encodedPeer = SOSFullPeerInfoCopyEncodedData(full_peer, kCFAllocatorDefault, NULL); - CFTypeRef originalArrayEntry = arrayEntry; - arrayEntry = CFArrayCreateForCFTypes(kCFAllocatorDefault, encodedCircle, encodedPeer, NULL); - - CFReleaseSafe(originalArrayEntry); - CFReleaseNull(encodedPeer); - } - - CFArrayAppendValue(arrayToEncode, arrayEntry); - - CFReleaseSafe(arrayEntry); - CFReleaseNull(encodedCircle); - } - - }); - - return arrayToEncode; -} - -size_t SOSAccountGetDEREncodedSize(SOSAccountRef account, CFErrorRef *error) -{ - size_t sequence_size = 0; - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION; - - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->previous_public, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail); - - CFReleaseNull(arrayToEncode); - return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size); - -fail: - CFReleaseNull(arrayToEncode); - SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error); - return 0; -} - -uint8_t* SOSAccountEncodeToDER(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION; - der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, - ccder_encode_uint64(version, der, - der_encode_dictionary(account->gestalt, error, der, - der_encode_array(arrayToEncode, error, der, - ccder_encode_uint64(account->departure_code, der, - ccder_encode_bool(account->user_public_trusted, der, - der_encode_public_bytes(account->user_public, error, der, - der_encode_public_bytes(account->previous_public, error, der, - der_encode_data_or_null(account->user_key_parameters, error, der, - der_encode_dictionary(account->retired_peers, error, der, der_end)))))))))); - - CFReleaseNull(arrayToEncode); - - return der_end; -} - - - -size_t SOSAccountGetDEREncodedSize_V3(SOSAccountRef account, CFErrorRef *error) -{ - size_t sequence_size = 0; - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION; - - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail); - - CFReleaseNull(arrayToEncode); - return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size); - -fail: - CFReleaseNull(arrayToEncode); - SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error); - return 0; -} - -uint8_t* SOSAccountEncodeToDER_V3(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - uint64_t version = 3; - der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, - ccder_encode_uint64(version, der, - der_encode_dictionary(account->gestalt, error, der, - der_encode_array(arrayToEncode, error, der, - ccder_encode_uint64(account->departure_code, der, - ccder_encode_bool(account->user_public_trusted, der, - der_encode_public_bytes(account->user_public, error, der, - der_encode_data_or_null(account->user_key_parameters, error, der, - der_encode_dictionary(account->retired_peers, error, der, der_end))))))))); - - CFReleaseNull(arrayToEncode); - - return der_end; -} - -#if OLD_CODERS_SUPPORTED - -/* Original V2 encoders */ - -size_t SOSAccountGetDEREncodedSize_V2(SOSAccountRef account, CFErrorRef *error) -{ - size_t sequence_size = 0; - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail); - - CFReleaseNull(arrayToEncode); - return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size); - -fail: - CFReleaseNull(arrayToEncode); - SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error); - return 0; -} - -uint8_t* SOSAccountEncodeToDER_V2(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - - der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, - der_encode_dictionary(account->gestalt, error, der, - der_encode_array(arrayToEncode, error, der, - ccder_encode_uint64(account->departure_code, der, - ccder_encode_bool(account->user_public_trusted, der, - der_encode_public_bytes(account->user_public, error, der, - der_encode_data_or_null(account->user_key_parameters, error, der, - der_encode_dictionary(account->retired_peers, error, der, der_end)))))))); - - CFReleaseNull(arrayToEncode); - - return der_end; -} - - -/* Original V1 encoders */ - - -size_t SOSAccountGetDEREncodedSize_V1(SOSAccountRef account, CFErrorRef *error) -{ - size_t sequence_size = 0; - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_array(arrayToEncode, error)), fail); - require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail); - require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->retired_peers, error)), fail); - - CFReleaseNull(arrayToEncode); - return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size); - -fail: - CFReleaseNull(arrayToEncode); - SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error); - return 0; -} - -uint8_t* SOSAccountEncodeToDER_V1(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) -{ - CFMutableArrayRef arrayToEncode = SOSAccountCopyCircleArrayToEncode(account); - - der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, - der_encode_dictionary(account->gestalt, error, der, - der_encode_array(arrayToEncode, error, der, - ccder_encode_bool(account->user_public_trusted, der, - der_encode_public_bytes(account->user_public, error, der, - der_encode_data_or_null(account->user_key_parameters, error, der, - der_encode_dictionary(account->retired_peers, error, der, der_end))))))); - - CFReleaseNull(arrayToEncode); - - return der_end; -} -#endif /* OLD_CODERS_SUPPORTED */ - -/************************/ - -CFDataRef SOSAccountCopyEncodedData(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef *error) -{ - size_t size = SOSAccountGetDEREncodedSize(account, error); - if (size == 0) - return NULL; - uint8_t buffer[size]; - uint8_t* start = SOSAccountEncodeToDER(account, error, buffer, buffer + sizeof(buffer)); - CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size); - return result; -} - -dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) { - return account->queue; -} - -// -// MARK: User Credential management -// - -void SOSAccountPurgePrivateCredential(SOSAccountRef account) -{ - CFReleaseNull(account->_user_private); - if (account->user_private_timer) { - dispatch_source_cancel(account->user_private_timer); - dispatch_release(account->user_private_timer); - account->user_private_timer = NULL; - xpc_transaction_end(); - } - if (account->lock_notification_token) { - notify_cancel(account->lock_notification_token); - account->lock_notification_token = 0; - } -} - -static void SOSAccountSetPrivateCredential(SOSAccountRef account, SecKeyRef private) { - if (!private) - return SOSAccountPurgePrivateCredential(account); - - CFRetain(private); - CFReleaseSafe(account->_user_private); - account->_user_private = private; - - bool resume_timer = false; - if (!account->user_private_timer) { - xpc_transaction_begin(); - resume_timer = true; - account->user_private_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, account->queue); - dispatch_source_set_event_handler(account->user_private_timer, ^{ - SOSAccountPurgePrivateCredential(account); - }); - - notify_register_dispatch(kUserKeybagStateChangeNotification, &account->lock_notification_token, account->queue, ^(int token) { - bool locked = false; - CFErrorRef lockCheckError = NULL; - - if (!SecAKSGetIsLocked(&locked, &lockCheckError)) { - secerror("Checking for locked after change failed: %@", lockCheckError); - } - - if (locked) { - SOSAccountPurgePrivateCredential(account); - } - }); - } - - // (Re)set the timer's fire time to now + 120 seconds with a 5 second fuzz factor. - dispatch_time_t purgeTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * 60 * NSEC_PER_SEC)); - dispatch_source_set_timer(account->user_private_timer, purgeTime, DISPATCH_TIME_FOREVER, (int64_t)(5 * NSEC_PER_SEC)); - if (resume_timer) - dispatch_resume(account->user_private_timer); -} - -SecKeyRef SOSAccountGetPrivateCredential(SOSAccountRef account, CFErrorRef* error) -{ - if (account->_user_private == NULL) { - SOSCreateError(kSOSErrorPrivateKeyAbsent, CFSTR("Private Key not available - failed to prompt user recently"), NULL, error); - } - return account->_user_private; -} - -static bool SOSAccountHasPublicKey(SOSAccountRef account, CFErrorRef* error) -{ - if (account->user_public == NULL || account->user_public_trusted == false) { - SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key not available - failed to register before call"), NULL, error); - return false; - } - - return true; -} - -static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error); - -static void SOSAccountGenerationSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) { - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL); - if(SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), NULL) && - !SOSCircleVerify(circle, account->user_public, NULL)) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) { - SOSFullPeerInfoRef cloud_fpi = SOSCircleGetiCloudFullPeerInfoRef(circle); - require_quiet(cloud_fpi != NULL, gen_sign); - require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi, privKey, NULL), gen_sign); - if(!SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(cloud_fpi))) { - } - gen_sign: // finally generation sign this. - SOSCircleGenerationSign(circle, privKey, fpi, NULL); - account->departure_code = kSOSNeverLeftCircle; - }); - } - }); -} - -/* this one is meant to be local - not published over KVS. */ -static void SOSAccountPeerSignatureUpdate(SOSAccountRef account, SecKeyRef privKey) { - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL); - SOSFullPeerInfoUpgradeSignatures(fpi, privKey, NULL); - }); -} - -static void SOSAccountSetPreviousPublic(SOSAccountRef account) { - CFReleaseNull(account->previous_public); - account->previous_public = account->user_public; - CFRetain(account->previous_public); -} - -static void SOSAccountSetTrustedUserPublicKey(SOSAccountRef account, bool public_was_trusted, SecKeyRef privKey) -{ - if (!privKey) return; - SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(privKey); - - if (account->user_public && account->user_public_trusted && CFEqual(publicKey, account->user_public)) return; - - if(public_was_trusted && account->user_public) { - CFReleaseNull(account->previous_public); - account->previous_public = account->user_public; - CFRetain(account->previous_public); - } - - CFReleaseNull(account->user_public); - account->user_public = publicKey; - account->user_public_trusted = true; - - if(!account->previous_public) { - account->previous_public = account->user_public; - CFRetain(account->previous_public); - } - - secnotice("trust", "trusting new public key: %@", account->user_public); -} - -static void SOSAccountProcessDeferredUpdates(SOSAccountRef account) { - CFErrorRef error = NULL; - if (account->deferred_updates && !SOSAccountHandleUpdates(account, account->deferred_updates, &error)) - secerror("Failed to handle updates when setting public key (%@)", error); - - CFReleaseNull(account->deferred_updates); -} - - -bool SOSAccountTryUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error) -{ - bool success = false; - - if (!SOSAccountHasPublicKey(account, error)) - return false; - - if (account->user_key_parameters) { - SecKeyRef new_key = SOSUserKeygen(user_password, account->user_key_parameters, error); - if (new_key) { - SecKeyRef new_public_key = SecKeyCreatePublicFromPrivate(new_key); - - if (CFEqualSafe(new_public_key, account->user_public)) { - SOSAccountSetPrivateCredential(account, new_key); - success = true; - } else { - SOSCreateError(kSOSErrorWrongPassword, CFSTR("Password passed in incorrect: ▇█████▇▇██"), NULL, error); - } - CFReleaseSafe(new_public_key); - CFReleaseSafe(new_key); - } - } else { - SOSCreateError(kSOSErrorProcessingFailure, CFSTR("Have public key but no parameters??"), NULL, error); - } - - return success; -} - -static bool SOSAccountPublishCloudParameters(SOSAccountRef account, CFErrorRef* error) -{ - bool success = false; - CFMutableDataRef cloudParameters = CFDataCreateMutableWithScratch(kCFAllocatorDefault, - der_sizeof_cloud_parameters(account->user_public, - account->user_key_parameters, - error)); - if (der_encode_cloud_parameters(account->user_public, account->user_key_parameters, error, - CFDataGetMutableBytePtr(cloudParameters), - CFDataGetMutablePastEndPtr(cloudParameters)) != NULL) { - - CFDictionaryRef changes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kSOSKVSKeyParametersKey, cloudParameters, - NULL); - - CFErrorRef changeError = NULL; - if (account->update_block(changes, &changeError)) { - success = true; - } else { - SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, - CFSTR("update parameters key failed [%@]"), changes); - } - CFReleaseSafe(changes); - CFReleaseSafe(changeError); - } else { - SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Encoding parameters failed"), NULL, error); - } - - CFReleaseNull(cloudParameters); - - return success; -} - -bool SOSAccountAssertUserCredentials(SOSAccountRef account, CFStringRef user_account __unused, CFDataRef user_password, CFErrorRef *error) -{ - bool public_was_trusted = account->user_public_trusted; - account->user_public_trusted = false; - SecKeyRef user_private = NULL; - - if (account->user_public && account->user_key_parameters) { - // We have an untrusted public key – see if our generation makes the same key: - // if so we trust it and we have the private key. - // if not we still don't trust it. - require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit); - SecKeyRef public_candidate = SecKeyCreatePublicFromPrivate(user_private); - if (!CFEqualSafe(account->user_public, public_candidate)) { - secnotice("trust", "Public keys don't match: calculated: %@, expected: %@", - account->user_public, public_candidate); - debugDumpUserParameters(CFSTR("params"), account->user_key_parameters); - CFReleaseNull(user_private); - } else { - SOSAccountPeerSignatureUpdate(account, user_private); - SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private); - } - CFReleaseSafe(public_candidate); - } - - if (!account->user_public_trusted) { - // We may or may not have parameters here. - // In any case we tried using them and they didn't match - // So forget all that and start again, assume we're the first to push anything useful. - - CFReleaseNull(account->user_key_parameters); - account->user_key_parameters = SOSUserKeyCreateGenerateParameters(error); - require_quiet(user_private = SOSUserKeygen(user_password, account->user_key_parameters, error), exit); - - SOSAccountPeerSignatureUpdate(account, user_private); - SOSAccountSetTrustedUserPublicKey(account, public_was_trusted, user_private); - - CFErrorRef publishError = NULL; - if (!SOSAccountPublishCloudParameters(account, &publishError)) - secerror("Failed to publish new cloud parameters: %@", publishError); - CFReleaseSafe(publishError); - } - - SOSAccountProcessDeferredUpdates(account); - SOSAccountGenerationSignatureUpdate(account, user_private); - SOSAccountSetPrivateCredential(account, user_private); -exit: - CFReleaseSafe(user_private); - - return account->user_public_trusted; -} - -// -// MARK: Circle management -// - -int SOSAccountCountCircles(SOSAccountRef a) { - assert(a); - assert(a->circle_identities); - assert(a->circles); - return (int)CFDictionaryGetCount(a->circles); -} - -static SecKeyRef GeneratePermanentFullECKey_internal(int keySize, CFStringRef name, CFTypeRef accessibility, CFBooleanRef sync, CFErrorRef* error) -{ - SecKeyRef full_key = NULL; - - CFNumberRef key_size_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize); - - CFDictionaryRef priv_key_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kSecAttrIsPermanent, kCFBooleanTrue, - NULL); - - CFDictionaryRef keygen_parameters = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kSecAttrKeyType, kSecAttrKeyTypeEC, - kSecAttrKeySizeInBits, key_size_num, - kSecPrivateKeyAttrs, priv_key_attrs, - kSecAttrAccessible, accessibility, - kSecAttrAccessGroup, kSOSInternalAccessGroup, - kSecAttrLabel, name, - kSecAttrSynchronizable, sync, - kSecUseTombstones, kCFBooleanTrue, - NULL); - - CFReleaseNull(priv_key_attrs); - - CFReleaseNull(key_size_num); - OSStatus status = SecKeyGeneratePair(keygen_parameters, NULL, &full_key); - CFReleaseNull(keygen_parameters); - - if (status) - secerror("status: %ld", (long)status); - if (status != errSecSuccess && error != NULL && *error == NULL) { - *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL); - } - - return full_key; -} - -static SecKeyRef GeneratePermanentFullECKey(int keySize, CFStringRef name, CFErrorRef* error) { - return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kCFBooleanFalse, error); -} - -static SecKeyRef GeneratePermanentFullECKeyForCloudIdentity(int keySize, CFStringRef name, CFErrorRef* error) { - return GeneratePermanentFullECKey_internal(keySize, name, kSecAttrAccessibleWhenUnlocked, kCFBooleanTrue, error); -} - - -SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) { - if (CFDictionaryGetValue(account->circles, name) == NULL) { - SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name); - return NULL; - } - SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name); - - - if (circle_full_peer_info == NULL) { - CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(account->gestalt), name); - SecKeyRef full_key = GeneratePermanentFullECKey(256, keyName, error); - CFReleaseNull(keyName); - - if (full_key) { - circle_full_peer_info = SOSFullPeerInfoCreate(kCFAllocatorDefault, account->gestalt, full_key, error); - - CFReleaseNull(full_key); - - if (!circle_full_peer_info) { - secerror("Can't make FullPeerInfo for %@-%@ (%@) - is AKS ok?", SOSPeerGestaltGetName(account->gestalt), name, error ? (void*)*error : (void*)CFSTR("-")); - return circle_full_peer_info; - } - - CFDictionarySetValue(account->circle_identities, name, circle_full_peer_info); - CFReleaseNull(circle_full_peer_info); - circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name); - } - else - secerror("No full_key: %@:", error ? *error : NULL); - } - - return circle_full_peer_info; -} - -static bool SOSAccountDestroyCirclePeerInfoNamed(SOSAccountRef account, CFStringRef name, CFErrorRef* error) { - if (CFDictionaryGetValue(account->circles, name) == NULL) { - SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("No circle named '%@'"), name); - return false; - } - - SOSFullPeerInfoRef circle_full_peer_info = (SOSFullPeerInfoRef) CFDictionaryGetValue(account->circle_identities, name); - - if (circle_full_peer_info) { - SOSPeerPurgeAllFor(SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(circle_full_peer_info))); - - SOSFullPeerInfoPurgePersistentKey(circle_full_peer_info, NULL); - } - - CFDictionaryRemoveValue(account->circle_identities, name); - - return true; -} - -static bool SOSAccountDestroyCirclePeerInfo(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) { - return SOSAccountDestroyCirclePeerInfoNamed(account, SOSCircleGetName(circle), error); -} - -SOSFullPeerInfoRef SOSAccountGetMyFullPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) { - return SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error); -} - -SOSPeerInfoRef SOSAccountGetMyPeerInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) { - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, SOSCircleGetName(circle), error); - - return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL; -} - -SOSPeerInfoRef SOSAccountGetMyPeerInCircleNamed(SOSAccountRef account, CFStringRef name, CFErrorRef *error) -{ - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamed(account, name, error); - - return fpi ? SOSFullPeerInfoGetPeerInfo(fpi) : NULL; -} - -CFArrayRef SOSAccountCopyAccountIdentityPeerInfos(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error) -{ - CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(allocator); - - CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) { - SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) value; - - CFArrayAppendValue(result, SOSFullPeerInfoGetPeerInfo(fpi)); - }); - - return result; -} - -bool SOSAccountIsAccountIdentity(SOSAccountRef account, SOSPeerInfoRef peer_info, CFErrorRef *error) -{ - __block bool matches = false; - CFDictionaryForEach(account->circle_identities, ^(const void *key, const void *value) { - if (!matches) { - matches = CFEqual(peer_info, SOSFullPeerInfoGetPeerInfo((SOSFullPeerInfoRef) value)); - } - }); - - return matches; -} - -bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error) -{ - __block bool result = true; - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - if (!SOSAccountSyncWithAllPeersInCircle(account, circle, error)) - result = false; - }); - - return result; -} - -bool SOSAccountSyncWithAllPeersInCircle(SOSAccountRef account, SOSCircleRef circle, - CFErrorRef *error) -{ - SOSPeerInfoRef my_peer = SOSAccountGetMyPeerInCircle(account, circle, error); - if (!my_peer) - return false; - - __block bool didSync = false; - __block bool result = true; - - if (SOSCircleHasPeer(circle, my_peer, NULL)) { - SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) { - if (!CFEqual(SOSPeerInfoGetPeerID(my_peer), SOSPeerInfoGetPeerID(peer))) - { - bool local_didSync = false; - if (!SOSAccountSyncWithPeer(account, circle, peer, &local_didSync, error)) - result = false; - if (!didSync && local_didSync) - { - didSync = true; - } - } - }); - - if (didSync) - { - SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1); - } - } - - return result; -} - -bool SOSAccountSyncWithPeer(SOSAccountRef account, SOSCircleRef circle, - SOSPeerInfoRef thisPeer, bool* didSendData, CFErrorRef* error) -{ - CFStringRef peer_id = SOSPeerInfoGetPeerID(thisPeer); - CFStringRef peer_write_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_id); - SOSFullPeerInfoRef myRef = SOSAccountGetMyFullPeerInCircle(account, circle, error); - - __block bool sentData = false; - - - SOSPeerSendBlock writeToKVSKey = ^bool (CFDataRef data, CFErrorRef* error) { - secnotice("account", "writing data of size %ld:", data?CFDataGetLength(data):0); - sentData = (NULL != data); - CFDictionaryRef writeToDo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peer_write_key, data, NULL); - bool written = account->update_block(writeToDo, error); - if (account->processed_message_block) - account->processed_message_block(circle, NULL, data); - CFRelease(writeToDo); - return written; - }; - - if (NULL != didSendData) - { - *didSendData = sentData; - } - - bool result = SOSCircleSyncWithPeer(myRef, circle, account->factory, writeToKVSKey, peer_id, error); - CFReleaseNull(peer_write_key); - return result; -} - -static bool SOSAccountIsActivePeerInCircleNamed(SOSAccountRef account, CFStringRef circle_name, CFStringRef peerid, CFErrorRef* error) { - SOSCircleRef circle = SOSAccountFindCircle(account, circle_name, error); - if(!circle) return false; - return SOSCircleHasActivePeerWithID(circle, peerid, error); -} - -static bool SOSAccountIsMyPeerActiveInCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) { - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, SOSCircleGetName(circle), NULL); - if(!fpi) return false; - return SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(fpi), error); -} - -bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle, - SOSPeerInfoRef cleanupPeer, CFErrorRef* error) -{ - if(!SOSAccountIsMyPeerActiveInCircle(account, circle, NULL)) return true; - - CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef circlePeer) { - CFStringRef from_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, cleanupPeer, circlePeer); - CFStringRef to_key = SOSMessageKeyCreateWithCircleAndPeerInfos(circle, circlePeer, cleanupPeer); - - CFDictionaryAddValue(keysToWrite, from_key, kCFNull); - CFDictionaryAddValue(keysToWrite, to_key, kCFNull); - - CFReleaseNull(from_key); - CFReleaseNull(to_key); - }); - - if(SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer)) { - CFStringRef resignationKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(cleanupPeer)); - CFDictionarySetValue(keysToWrite, resignationKey, kCFNull); - CFDictionaryRemoveValue(account->retired_peers, resignationKey); - CFReleaseNull(resignationKey); - } - - bool success = account->update_block(keysToWrite, error); - - CFReleaseNull(keysToWrite); - - return success; -} - -bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) { - CFMutableDictionaryRef keysToWrite = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); - - CFDictionaryRef copyToIterate = CFDictionaryCreateCopy(kCFAllocatorDefault, account->retired_peers); - - CFDictionaryForEach(copyToIterate, ^(const void* resignationKey, const void* value) { - CFStringRef circle_name = NULL; - CFStringRef retiree_peerid = NULL; - SOSPeerInfoRef pi = NULL; - SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL); - require_quiet(keytype == kRetirementKey && circle_name && retiree_peerid && isData(value), forget); - pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value); - require_quiet(pi && CFEqualSafe(retiree_peerid, SOSPeerInfoGetPeerID(pi)), forget); - - require_quiet(!SOSAccountIsActivePeerInCircleNamed(account, circle_name, retiree_peerid, NULL), keep); - require_quiet(SOSPeerInfoRetireRetirementTicket(seconds, pi), keep); - - // Happy day, it's time and it's a ticket we should eradicate from KVS. - CFDictionarySetValue(keysToWrite, resignationKey, kCFNull); - - forget: - CFDictionaryRemoveValue(account->retired_peers, resignationKey); - keep: - CFReleaseSafe(pi); - CFReleaseSafe(circle_name); - CFReleaseSafe(retiree_peerid); - }); - CFReleaseNull(copyToIterate); - - bool success = true; - if(CFDictionaryGetCount(keysToWrite)) { - success = account->update_block(keysToWrite, error); - } - CFReleaseNull(keysToWrite); - - return success; -} - -bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) { - SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) { - CFStringRef key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(peer)); - if(key && !CFDictionaryGetValueIfPresent(account->retired_peers, key, NULL)) { - CFDataRef value = SOSPeerInfoCopyEncodedData(peer, NULL, NULL); - if(value) { - CFDictionarySetValue(account->retired_peers, key, value); - SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, error); - } - CFReleaseSafe(value); - } - CFReleaseSafe(key); - }); - return true; -} - -SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) { - CFStringRef circle_to_mod = SOSCircleGetName(starting_circle); - SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error); - if(!new_circle) return NULL; - - CFDictionaryForEach(account->retired_peers, ^(const void* resignationKey, const void* value) { - CFStringRef circle_name = NULL; - CFStringRef retiree_peerid = NULL; - - SOSKVSKeyType keytype = SOSKVSKeyGetKeyTypeAndParse(resignationKey, &circle_name, &retiree_peerid, NULL); - if(keytype == kRetirementKey && CFEqualSafe(circle_name, circle_to_mod) && SOSCircleHasPeerWithID(new_circle, retiree_peerid, NULL)) { - if(isData(value)) { - SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value); - SOSCircleUpdatePeerInfo(new_circle, pi); - CFReleaseSafe(pi); - } - } - CFReleaseSafe(circle_name); - CFReleaseSafe(retiree_peerid); - }); - - if(SOSCircleCountPeers(new_circle) == 0) { - SOSCircleResetToEmpty(new_circle, NULL); - } - - return new_circle; -} - - -// -// Circle Finding -// -SOSCircleRef SOSAccountFindCompatibleCircle(SOSAccountRef a, CFStringRef name) -{ - CFTypeRef entry = CFDictionaryGetValue(a->circles, name); - - if (CFGetTypeID(entry) == SOSCircleGetTypeID()) - return (SOSCircleRef) entry; - - return NULL; -} - -SOSCircleRef SOSAccountFindCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error) -{ - CFTypeRef entry = CFDictionaryGetValue(a->circles, name); - - require_action_quiet(!isNull(entry), fail, - SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle in KVS"), NULL, error)); - - require_action_quiet(entry, fail, - SOSCreateError(kSOSErrorNoCircle, CFSTR("No circle found"), NULL, error)); - - - return (SOSCircleRef) entry; - -fail: - return NULL; -} - -SOSCircleRef SOSAccountEnsureCircle(SOSAccountRef a, CFStringRef name, CFErrorRef *error) -{ - CFErrorRef localError = NULL; - - SOSCircleRef circle = SOSAccountFindCircle(a, name, &localError); - - require_action_quiet(circle || !isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle), fail, - if (error) { *error = localError; localError = NULL; }); - - - if (NULL == circle) { - circle = SOSCircleCreate(NULL, name, NULL); - if (circle){ - CFDictionaryAddValue(a->circles, name, circle); - CFRelease(circle); - circle = SOSAccountFindCircle(a, name, &localError); - } - SOSUpdateKeyInterest(a, false, NULL); - } - -fail: - CFReleaseNull(localError); - return circle; -} - - -void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) -{ - CFArrayAppendValue(a->change_blocks, changeBlock); -} - -void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) -{ - CFArrayRemoveAllValue(a->change_blocks, changeBlock); -} - -static void DifferenceAndCall(CFArrayRef old_members, CFArrayRef new_members, void (^updatedCircle)(CFArrayRef additions, CFArrayRef removals)) -{ - CFMutableArrayRef additions = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, new_members); - CFMutableArrayRef removals = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, old_members); - - - CFArrayForEach(old_members, ^(const void * value) { - CFArrayRemoveAllValue(additions, value); - }); - - CFArrayForEach(new_members, ^(const void * value) { - CFArrayRemoveAllValue(removals, value); - }); - - updatedCircle(additions, removals); - - CFReleaseSafe(additions); - CFReleaseSafe(removals); -} - -static void SOSAccountNotifyOfChange(SOSAccountRef account, SOSCircleRef oldCircle, SOSCircleRef newCircle) -{ - CFMutableArrayRef old_members = SOSCircleCopyPeers(oldCircle, kCFAllocatorDefault); - CFMutableArrayRef new_members = SOSCircleCopyPeers(newCircle, kCFAllocatorDefault); - - CFMutableArrayRef old_applicants = SOSCircleCopyApplicants(oldCircle, kCFAllocatorDefault); - CFMutableArrayRef new_applicants = SOSCircleCopyApplicants(newCircle, kCFAllocatorDefault); - - DifferenceAndCall(old_members, new_members, ^(CFArrayRef added_members, CFArrayRef removed_members) { - DifferenceAndCall(old_applicants, new_applicants, ^(CFArrayRef added_applicants, CFArrayRef removed_applicants) { - CFArrayForEach(account->change_blocks, ^(const void * notificationBlock) { - ((SOSAccountCircleMembershipChangeBlock) notificationBlock)(newCircle, added_members, removed_members, added_applicants, removed_applicants); - }); - }); - }); - - CFReleaseNull(old_applicants); - CFReleaseNull(new_applicants); - - CFReleaseNull(old_members); - CFReleaseNull(new_members); -} - -void SOSAccountForEachCircle(SOSAccountRef account, void (^process)(SOSCircleRef circle)) -{ - CFDictionaryForEach(account->circles, ^(const void* key, const void* value) { - assert(value); - process((SOSCircleRef)value); - }); -} - -static void AppendCircleKeyName(CFMutableArrayRef array, CFStringRef name) { - CFStringRef circle_key = SOSCircleKeyCreateWithName(name, NULL); - CFArrayAppendValue(array, circle_key); - CFReleaseNull(circle_key); -} - -static inline void AppendCircleInterests(CFMutableArrayRef circle_keys, CFMutableArrayRef retiree_keys, CFMutableArrayRef message_keys, SOSCircleRef circle, SOSFullPeerInfoRef me) { - CFStringRef my_peer_id = NULL; - - if (me) { - SOSPeerInfoRef my_peer = me ? SOSFullPeerInfoGetPeerInfo(me) : NULL; - my_peer_id = SOSPeerInfoGetPeerID(my_peer); - } - - if (circle_keys) { - CFStringRef circleName = SOSCircleGetName(circle); - AppendCircleKeyName(circle_keys, circleName); - } - - SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) { - if (!CFEqualSafe(my_peer_id, SOSPeerInfoGetPeerID(peer))) { - CFStringRef peer_name = SOSPeerInfoGetPeerID(peer); - if (retiree_keys) { - CFStringRef retirementKey = SOSRetirementKeyCreateWithCircleAndPeer(circle, peer_name); - CFArrayAppendValue(retiree_keys, retirementKey); - CFReleaseNull(retirementKey); - } - - if (my_peer_id && message_keys) { - CFStringRef messageKey = SOSMessageKeyCreateWithCircleAndPeerNames(circle, peer_name, my_peer_id); - CFArrayAppendValue(message_keys, messageKey); - CFRelease(messageKey); - } - } - }); -} - -static void SOSAccountCopyKeyInterests(SOSAccountRef account, - CFMutableArrayRef alwaysKeys, - CFMutableArrayRef afterFirstUnlockKeys, - CFMutableArrayRef whenUnlockedKeys) -{ - CFArrayAppendValue(afterFirstUnlockKeys, kSOSKVSKeyParametersKey); - - SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { - AppendCircleKeyName(afterFirstUnlockKeys, name); - }, ^(SOSCircleRef circle) { - AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, whenUnlockedKeys, circle, NULL); - }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { - bool inCircle = SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(full_peer), NULL); - - AppendCircleInterests(afterFirstUnlockKeys, afterFirstUnlockKeys, inCircle ? whenUnlockedKeys : NULL, circle, full_peer); - }); -} - -static bool SOSUpdateKeyInterest(SOSAccountRef account, bool getNewKeysOnly, CFErrorRef *error) -{ - if (account->update_interest_block) { - - CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountCopyKeyInterests(account, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys); - - account->update_interest_block(getNewKeysOnly, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys); - - CFReleaseNull(alwaysKeys); - CFReleaseNull(afterFirstUnlockKeys); - CFReleaseNull(whenUnlockedKeys); - } - - return true; -} - -static bool SOSAccountSendPendingChanges(SOSAccountRef account, CFErrorRef *error) { - CFErrorRef changeError = NULL; - - if (CFDictionaryGetCount(account->pending_changes) == 0) - return true; - - bool success = account->update_block(account->pending_changes, &changeError); - if (success) { - CFDictionaryRemoveAllValues(account->pending_changes); - } else { - SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, - CFSTR("Send changes block failed [%@]"), account->pending_changes); - } - - return success; -} - -static bool SOSAccountAddCircleToPending(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) -{ - bool success = false; - CFDataRef circle_data = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, error); - - if (circle_data) { - CFStringRef circle_key = SOSCircleKeyCreateWithCircle(circle, NULL); - - CFDictionarySetValue(account->pending_changes, circle_key, circle_data); - success = true; - - CFReleaseNull(circle_data); - CFReleaseNull(circle_key); - } - - return success; -} - - -static void SOSAccountRecordRetiredPeerInCircleNamed(SOSAccountRef account, CFStringRef circleName, SOSPeerInfoRef retiree) -{ - // Replace Peer with RetiredPeer, if were a peer. - SOSAccountModifyCircle(account, circleName, NULL, ^(SOSCircleRef circle) { - if (SOSCircleUpdatePeerInfo(circle, retiree)) { - CFErrorRef cleanupError = NULL; - SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError); - secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError); - CFReleaseSafe(cleanupError); - } - }); -} - -static bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) { - SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInCircle(account, circle, NULL); - if(!fpi) return false; - CFErrorRef localError = NULL; - SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);; - CFStringRef retire_key = SOSRetirementKeyCreateWithCircleAndPeer(circle, SOSPeerInfoGetPeerID(retire_peer)); - CFDataRef retire_value = NULL; - bool retval = false; - bool writeCircle = false; - - // Create a Retirement Ticket and store it in the retired_peers of the account. - require_action_quiet(retire_peer, errout, secerror("Create ticket failed for peer %@: %@", fpi, localError)); - retire_value = SOSPeerInfoCopyEncodedData(retire_peer, NULL, &localError); - require_action_quiet(retire_value, errout, secerror("Failed to encode retirement peer %@: %@", retire_peer, localError)); - - // See if we need to repost the circle we could either be an applicant or a peer already in the circle - if(SOSCircleHasApplicant(circle, retire_peer, NULL)) { - // Remove our application if we have one. - SOSCircleWithdrawRequest(circle, retire_peer, NULL); - writeCircle = true; - } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) { - if (SOSCircleUpdatePeerInfo(circle, retire_peer)) { - CFErrorRef cleanupError = NULL; - SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError); - secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError); - CFReleaseSafe(cleanupError); - } - writeCircle = true; - } - - // Store the retirement record locally. - CFDictionarySetValue(account->retired_peers, retire_key, retire_value); - - // Write pending change to KVS - CFDictionarySetValue(account->pending_changes, retire_key, retire_value); - - // Kill peer key but don't return error if we can't. - if(!SOSAccountDestroyCirclePeerInfo(account, circle, &localError)) - secerror("Couldn't purge key for peer %@ on retirement: %@", fpi, localError); - - if (writeCircle) { - SOSAccountAddCircleToPending(account, circle, NULL); - } - retval = true; - -errout: - CFReleaseNull(localError); - CFReleaseNull(retire_peer); - CFReleaseNull(retire_key); - CFReleaseNull(retire_value); - return retval; -} - -/* - NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any - local value that has been overwritten by a distant value. If there is no - conflict between the local and the distant values when doing the initial - sync (e.g. if the cloud has no data stored or the client has not stored - any data yet), you'll never see that notification. - - NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip - with server but initial round trip with server does not imply - NSUbiquitousKeyValueStoreInitialSyncChange. - */ - -// -// MARK: Handle Circle Updates -// - - -static bool SOSAccountHandleUpdateCircle(SOSAccountRef account, SOSCircleRef prospective_circle, bool writeUpdate, bool initialSync, CFErrorRef *error) -{ - bool success = true; - - secnotice("signing", "start: %@", prospective_circle); - if (!account->user_public || !account->user_public_trusted) { - SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Can't handle updates with no trusted public key here"), NULL, error); - return false; - } - - if (!prospective_circle) { - secerror("##### Can't update to a NULL circle ######"); - return false; // Can't update one we don't have. - } - - CFStringRef newCircleName = SOSCircleGetName(prospective_circle); - SOSCircleRef oldCircle = SOSAccountFindCompatibleCircle(account, newCircleName); - SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInCircle(account, oldCircle, NULL); - SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(me_full); - - if (initialSync) - secerror("##### Processing initial sync. Old (local) circle: %@, New (cloud) circle: %@", oldCircle, prospective_circle); - - if (!oldCircle) - return false; // Can't update one we don't have. - - SOSAccountScanForRetired(account, prospective_circle, error); - SOSCircleRef newCircle = SOSAccountCloneCircleWithRetirement(account, prospective_circle, error); - if(!newCircle) return false; - - SOSCircleUpdatePeerInfo(newCircle, me); - - typedef enum { - accept, - countersign, - leave, - revert, - ignore - } circle_action_t; - - circle_action_t circle_action = ignore; - enum DepartureReason leave_reason = kSOSNeverLeftCircle; - - SecKeyRef old_circle_key = NULL; - if(SOSCircleVerify(oldCircle, account->user_public, NULL)) old_circle_key = account->user_public; - else if(account->previous_public && SOSCircleVerify(oldCircle, account->previous_public, NULL)) old_circle_key = account->previous_public; - bool userTrustedOldCircle = (old_circle_key != NULL); - - SOSConcordanceStatus concstat = - SOSCircleConcordanceTrust(oldCircle, newCircle, - old_circle_key, account->user_public, - me, error); - - CFStringRef concStr = NULL; - switch(concstat) { - case kSOSConcordanceTrusted: - circle_action = countersign; - concStr = CFSTR("Trusted"); - break; - case kSOSConcordanceGenOld: - circle_action = userTrustedOldCircle ? revert : ignore; - concStr = CFSTR("Generation Old"); - break; - case kSOSConcordanceBadUserSig: - case kSOSConcordanceBadPeerSig: - circle_action = userTrustedOldCircle ? revert : accept; - concStr = CFSTR("Bad Signature"); - break; - case kSOSConcordanceNoUserSig: - circle_action = userTrustedOldCircle ? revert : accept; - concStr = CFSTR("No User Signature"); - break; - case kSOSConcordanceNoPeerSig: - circle_action = accept; // We might like this one eventually but don't countersign. - concStr = CFSTR("No trusted peer signature"); - secerror("##### No trusted peer signature found, accepting hoping for concordance later %@", newCircle); - break; - case kSOSConcordanceNoPeer: - circle_action = leave; - leave_reason = kSOSLeftUntrustedCircle; - concStr = CFSTR("No trusted peer left"); - break; - case kSOSConcordanceNoUserKey: - secerror("##### No User Public Key Available, this shouldn't ever happen!!!"); - abort(); - break; - default: - secerror("##### Bad Error Return from ConcordanceTrust"); - abort(); - break; - } - - secnotice("signing", "Decided on action %d based on concordance state %d and %s circle.", circle_action, concstat, userTrustedOldCircle ? "trusted" : "untrusted"); - - SOSCircleRef circleToPush = NULL; - - if (circle_action == leave) { - circle_action = ignore; - - if (me && SOSCircleHasPeer(oldCircle, me, NULL)) { - if (sosAccountLeaveCircle(account, newCircle, error)) { - account->departure_code = leave_reason; - circleToPush = newCircle; - circle_action = accept; - me = NULL; - me_full = NULL; - } - } - else { - // We are not in this circle, but we need to update account with it, since we got it from cloud - secnotice("updatecircle", "We are not in this circle, but we need to update account with it"); - circle_action = accept; - } - } - - if (circle_action == countersign) { - if (me && SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleVerifyPeerSigned(newCircle, me, NULL)) { - CFErrorRef signing_error = NULL; - - if (me_full && SOSCircleConcordanceSign(newCircle, me_full, &signing_error)) { - circleToPush = newCircle; - secnotice("signing", "Concurred with: %@", newCircle); - } else { - secerror("Failed to concurrence sign, error: %@ Old: %@ New: %@", signing_error, oldCircle, newCircle); - } - CFReleaseSafe(signing_error); - } - circle_action = accept; - } - - if (circle_action == accept) { - if (me && SOSCircleHasActivePeer(oldCircle, me, NULL) && !SOSCircleHasPeer(newCircle, me, NULL)) { - // Don't destroy evidence of other code determining reason for leaving. - if(!SOSAccountHasLeft(account)) account->departure_code = kSOSMembershipRevoked; - } - - if (me - && SOSCircleHasActivePeer(oldCircle, me, NULL) - && !(SOSCircleCountPeers(oldCircle) == 1 && SOSCircleHasPeer(oldCircle, me, NULL)) // If it was our offering, don't change ID to avoid ghosts - && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) { - secnotice("circle", "Purging my peer (ID: %@) for circle '%@'!!!", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle)); - SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL); - me = NULL; - me_full = NULL; - } - - if (me && SOSCircleHasRejectedApplicant(newCircle, me, NULL)) { - SOSPeerInfoRef reject = SOSCircleCopyRejectedApplicant(newCircle, me, NULL); - if(CFEqualSafe(reject, me) && SOSPeerInfoApplicationVerify(me, account->user_public, NULL)) { - secnotice("circle", "Rejected, Purging my applicant peer (ID: %@) for circle '%@'", SOSPeerInfoGetPeerID(me), SOSCircleGetName(oldCircle)); - SOSAccountDestroyCirclePeerInfo(account, oldCircle, NULL); - me = NULL; - me_full = NULL; - } else { - SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL); - writeUpdate = true; - } - } - - CFRetain(oldCircle); // About to replace the oldCircle - CFDictionarySetValue(account->circles, newCircleName, newCircle); - SOSAccountSetPreviousPublic(account); - - secnotice("signing", "%@, Accepting circle: %@", concStr, newCircle); - - if (me_full && account->user_public_trusted - && SOSCircleHasApplicant(oldCircle, me, NULL) - && SOSCircleCountPeers(newCircle) > 0 - && !SOSCircleHasPeer(newCircle, me, NULL) && !SOSCircleHasApplicant(newCircle, me, NULL)) { - // We weren't rejected (above would have set me to NULL. - // We were applying and we weren't accepted. - // Our application is declared lost, let us reapply. - - if (SOSCircleRequestReadmission(newCircle, account->user_public, me_full, NULL)) - writeUpdate = true; - } - - if (me && SOSCircleHasActivePeer(oldCircle, me, NULL)) { - SOSAccountCleanupRetirementTickets(account, RETIREMENT_FINALIZATION_SECONDS, NULL); - } - - SOSAccountNotifyOfChange(account, oldCircle, newCircle); - - CFReleaseNull(oldCircle); - - if (writeUpdate) - circleToPush = newCircle; - - success = SOSUpdateKeyInterest(account, true, error); - } - - if (circle_action == revert) { - secnotice("signing", "%@, Rejecting: %@ re-publishing %@", concStr, newCircle, oldCircle); - - circleToPush = oldCircle; - } - - - if (circleToPush != NULL) { - success = (success - && SOSAccountAddCircleToPending(account, circleToPush, error) - && SOSAccountSendPendingChanges(account, error)); - } - - CFReleaseSafe(newCircle); - - return success; -} - -static bool SOSAccountUpdateCircleFromRemote(SOSAccountRef account, SOSCircleRef newCircle, bool initialSync, CFErrorRef *error) -{ - return SOSAccountHandleUpdateCircle(account, newCircle, false, initialSync, error); -} - -bool SOSAccountUpdateCircle(SOSAccountRef account, SOSCircleRef newCircle, CFErrorRef *error) -{ - return SOSAccountHandleUpdateCircle(account, newCircle, true, false, error); -} - -bool SOSAccountModifyCircle(SOSAccountRef account, - CFStringRef circleName, - CFErrorRef* error, - void (^action)(SOSCircleRef circle)) -{ - bool success = false; - - SOSCircleRef circle = NULL; - SOSCircleRef accountCircle = SOSAccountFindCircle(account, circleName, error); - require_quiet(accountCircle, fail); - - circle = SOSCircleCopyCircle(kCFAllocatorDefault, accountCircle, error); - require_quiet(circle, fail); - - action(circle); - success = SOSAccountUpdateCircle(account, circle, error); - -fail: - CFReleaseSafe(circle); - return success; -} - -static SOSCircleRef SOSAccountCreateCircleFrom(CFStringRef circleName, CFTypeRef value, CFErrorRef *error) { - if (value && !isData(value) && !isNull(value)) { - CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(value)); - SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL, - CFSTR("Expected data or NULL got %@"), description); - CFReleaseSafe(description); - return NULL; - } - - SOSCircleRef circle = NULL; - if (!value || isNull(value)) { - circle = SOSCircleCreate(kCFAllocatorDefault, circleName, error); - } else { - circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, error); - if (circle) { - CFStringRef name = SOSCircleGetName(circle); - if (!CFEqualSafe(name, circleName)) { - SOSCreateErrorWithFormat(kSOSErrorNameMismatch, NULL, error, NULL, - CFSTR("Expected circle named %@, got %@"), circleName, name); - CFReleaseNull(circle); - } - } - } - return circle; -} - -static SOSCCStatus SOSCCCircleStatus(SOSCircleRef circle) -{ - if (SOSCircleCountPeers(circle) == 0) - return kSOSCCCircleAbsent; - - return kSOSCCNotInCircle; -} - -static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) -{ - if (SOSCircleCountPeers(circle) == 0) - return kSOSCCCircleAbsent; - - if (SOSCircleHasPeer(circle, this_peer, NULL)) - return kSOSCCInCircle; - - if (SOSCircleHasApplicant(circle, this_peer, NULL)) - return kSOSCCRequestPending; - - return kSOSCCNotInCircle; -} - -static SOSCCStatus UnionStatus(SOSCCStatus accumulated_status, SOSCCStatus additional_circle_status) -{ - switch (additional_circle_status) { - case kSOSCCInCircle: - return accumulated_status; - case kSOSCCRequestPending: - return (accumulated_status == kSOSCCInCircle) ? - kSOSCCRequestPending : - accumulated_status; - case kSOSCCNotInCircle: - return (accumulated_status == kSOSCCInCircle || - accumulated_status == kSOSCCRequestPending) ? - kSOSCCNotInCircle : - accumulated_status; - case kSOSCCCircleAbsent: - return (accumulated_status == kSOSCCInCircle || - accumulated_status == kSOSCCRequestPending || - accumulated_status == kSOSCCNotInCircle) ? - kSOSCCCircleAbsent : - accumulated_status; - default: - return additional_circle_status; - }; - -} - -SOSCCStatus SOSAccountIsInCircles(SOSAccountRef account, CFErrorRef* error) -{ - if (!SOSAccountHasPublicKey(account, error)) { - return kSOSCCError; - } - - __block bool set_once = false; - __block SOSCCStatus status = kSOSCCInCircle; - - SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { - set_once = true; - status = kSOSCCError; - SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error); - }, ^(SOSCircleRef circle) { - set_once = true; - status = UnionStatus(status, SOSCCCircleStatus(circle)); - }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { - set_once = true; - SOSCCStatus circle_status = SOSCCThisDeviceStatusInCircle(circle, SOSFullPeerInfoGetPeerInfo(full_peer)); - status = UnionStatus(status, circle_status); - }); - - if (!set_once) - status = kSOSCCCircleAbsent; - - return status; -} - -static SOSPeerInfoRef GenerateNewCloudIdentityPeerInfo(CFErrorRef *error) { - SecKeyRef cloud_key = GeneratePermanentFullECKeyForCloudIdentity(256, kicloud_identity_name, error); - SOSPeerInfoRef cloud_peer = NULL; - CFDictionaryRef query = NULL; - CFDictionaryRef change = NULL; - CFStringRef new_name = NULL; - - CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kPIUserDefinedDeviceName, CFSTR("iCloud"), - NULL); - require_action_quiet(gestalt, fail, SecError(errSecAllocate, error, CFSTR("Can't allocate gestalt"))); - - cloud_peer = SOSPeerInfoCreateCloudIdentity(kCFAllocatorDefault, gestalt, cloud_key, error); - - require(cloud_peer, fail); - - query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kSecClass, kSecClassKey, - kSecAttrSynchronizable,kCFBooleanTrue, - kSecUseTombstones, kCFBooleanTrue, - kSecValueRef, cloud_key, - NULL); - - new_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("Cloud Identity - '%@'"), SOSPeerInfoGetPeerID(cloud_peer)); - - change = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, - kSecAttrLabel, new_name, - NULL); - - SecError(SecItemUpdate(query, change), error, CFSTR("Couldn't update name")); - -fail: - CFReleaseNull(new_name); - CFReleaseNull(query); - CFReleaseNull(change); - CFReleaseNull(gestalt); - CFReleaseNull(cloud_key); - - return cloud_peer; -} - -static SOSFullPeerInfoRef CopyCloudKeychainIdentity(SOSPeerInfoRef cloudPeer, CFErrorRef *error) { - return SOSFullPeerInfoCreateCloudIdentity(NULL, cloudPeer, error); -} - -static bool SOSAccountResetThisCircleToOffering(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) { - SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error); - if (!myCirclePeer) - return false; - - SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) { - bool result = false; - SOSFullPeerInfoRef cloud_identity = NULL; - CFErrorRef localError = NULL; - - require_quiet(SOSCircleResetToOffering(circle, user_key, myCirclePeer, &localError), err_out); - - { - SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error); - require_quiet(cloud_peer, err_out); - cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error); - require_quiet(cloud_identity, err_out); - } - - account->departure_code = kSOSNeverLeftCircle; - require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out); - require_quiet(SOSCircleAcceptRequest(circle, user_key, myCirclePeer, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out); - result = true; - SOSAccountPublishCloudParameters(account, NULL); - - err_out: - if (result == false) - secerror("error resetting circle (%@) to offering: %@", circle, localError); - if (localError && error && *error == NULL) { - *error = localError; - localError = NULL; - } - CFReleaseNull(localError); - CFReleaseNull(cloud_identity); - }); - - return true; -} - -static bool SOSAccountJoinThisCircle(SOSAccountRef account, SecKeyRef user_key, - SOSCircleRef circle, bool use_cloud_peer, CFErrorRef* error) { - __block bool result = false; - __block SOSFullPeerInfoRef cloud_full_peer = NULL; - - SOSFullPeerInfoRef myCirclePeer = SOSAccountGetMyFullPeerInCircle(account, circle, error); - require_action_quiet(myCirclePeer, fail, - SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Can't find/create peer for circle: %@"), circle)); - if (use_cloud_peer) { - cloud_full_peer = SOSCircleGetiCloudFullPeerInfoRef(circle); - } - - if (SOSCircleCountPeers(circle) == 0) { - result = SOSAccountResetThisCircleToOffering(account, circle, user_key, error); - } else { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) { - result = SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error); - account->departure_code = kSOSNeverLeftCircle; - if(result && cloud_full_peer) { - CFErrorRef localError = NULL; - CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer)); - require_quiet(cloudid, finish); - require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish); - require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish); - finish: - if (localError){ - secerror("Failed to join with cloud identity: %@", localError); - CFReleaseNull(localError); - } - } - }); - } - -fail: - CFReleaseNull(cloud_full_peer); - return result; -} - -static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) { - SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error); - if (!user_key) - return false; - - __block bool success = true; - - SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { // Incompatible - success = false; - SOSCreateError(kSOSErrorIncompatibleCircle, CFSTR("Incompatible circle"), NULL, error); - }, ^(SOSCircleRef circle) { // No Peer - success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success; - }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { // Have Peer - SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(full_peer); - if(SOSCircleHasPeer(circle, myPeer, NULL)) goto already_present; - if(SOSCircleHasApplicant(circle, myPeer, NULL)) goto already_applied; - if(SOSCircleHasRejectedApplicant(circle, myPeer, NULL)) { - SOSCircleRemoveRejectedPeer(circle, myPeer, NULL); - } - - secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(circle)); - CFErrorRef localError = NULL; - if (!SOSAccountDestroyCirclePeerInfo(account, circle, &localError)) { - secerror("Failed to destroy peer (%@) during application, error=%@", myPeer, localError); - CFReleaseNull(localError); - } - already_applied: - success = SOSAccountJoinThisCircle(account, user_key, circle, use_cloud_identity, error) && success; - return; - already_present: - success = true; - return; - }); - - if(success) account->departure_code = kSOSNeverLeftCircle; - - return success; -} - -bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) { - return SOSAccountJoinCircles_internal(account, false, error); -} - - -bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) { - return SOSAccountJoinCircles_internal(account, true, error); -} - - -bool SOSAccountLeaveCircles(SOSAccountRef account, CFErrorRef* error) -{ - __block bool result = true; - - SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) { - result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors! - }); - }); - - account->departure_code = kSOSWithdrewMembership; - - return SOSAccountSendPendingChanges(account, error) && result; -} - -bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) -{ - dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - dispatch_group_t group = dispatch_group_create(); - __block bool result = false; - - secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds); - // Add a task to the group - dispatch_group_async(group, queue, ^{ - SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) { - result = sosAccountLeaveCircle(account, circle, error); // TODO: What about multiple errors! - }); - }); - - account->departure_code = kSOSWithdrewMembership; - if(result) result = SOSAccountSendPendingChanges(account, error); - }); - dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC); - - dispatch_group_wait(group, milestone); - dispatch_release(group); - return result; -} - - -static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos, - void (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) { - SOSAccountForEachKnownCircle(account, NULL, NULL, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { - SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(full_peer); - CFErrorRef peer_error = NULL; - if (SOSCircleHasPeer(circle, me, &peer_error)) { - CFArrayForEach(peer_infos, ^(const void *value) { - SOSPeerInfoRef peer = (SOSPeerInfoRef) value; - if (SOSCircleHasApplicant(circle, peer, NULL)) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), NULL, ^(SOSCircleRef circle) { - action(circle, full_peer, peer); - }); - } - }); - } - if (peer_error) - secerror("Got error in SOSCircleHasPeer: %@", peer_error); - CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here. - }); -} - -bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) { - SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error); - if (!user_key) - return false; - - __block bool success = true; - __block int64_t num_peers = 0; - - for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) { - if (!SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error)) - success = false; - else - num_peers = MAX(num_peers, SOSCircleCountPeers(circle)); - }); - - return success; -} - -bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) { - __block bool success = true; - __block int64_t num_peers = 0; - - for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) { - if (!SOSCircleRejectRequest(circle, myCirclePeer, peer, error)) - success = false; - else - num_peers = MAX(num_peers, SOSCircleCountPeers(circle)); - }); - - return success; -} - -bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) { - SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error); - if (!user_key) - return false; - - __block bool result = true; - - SOSAccountForEachKnownCircle(account, ^(CFStringRef name) { - SOSCircleRef circle = SOSCircleCreate(NULL, name, NULL); - if (circle) - CFDictionaryAddValue(account->circles, name, circle); - - SOSAccountResetThisCircleToOffering(account, circle, user_key, error); - }, ^(SOSCircleRef circle) { - SOSAccountResetThisCircleToOffering(account, circle, user_key, error); - }, ^(SOSCircleRef circle, SOSFullPeerInfoRef full_peer) { - SOSAccountResetThisCircleToOffering(account, circle, user_key, error); - }); - - return result; -} - -bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) { - if (!SOSAccountHasPublicKey(account, error)) - return false; - - __block bool result = true; - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSAccountModifyCircle(account, SOSCircleGetName(circle), error, ^(SOSCircleRef circle) { - if (!SOSCircleResetToEmpty(circle, error)) - { - secerror("error: %@", *error); - result = false; - } - account->departure_code = kSOSWithdrewMembership; - }); - }); - - return result; -} - -CFArrayRef SOSAccountCopyApplicants(SOSAccountRef account, CFErrorRef *error) { - if (!SOSAccountHasPublicKey(account, error)) - return NULL; - CFMutableArrayRef applicants = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) { - CFArrayAppendValue(applicants, peer); - }); - }); - - return applicants; -} - -CFArrayRef SOSAccountCopyPeers(SOSAccountRef account, CFErrorRef *error) { - if (!SOSAccountHasPublicKey(account, error)) - return NULL; - - CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) { - CFArrayAppendValue(peers, peer); - }); - }); - - return peers; -} - -CFArrayRef SOSAccountCopyActivePeers(SOSAccountRef account, CFErrorRef *error) { - if (!SOSAccountHasPublicKey(account, error)) - return NULL; - - CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) { - CFArrayAppendValue(peers, peer); - }); - }); - - return peers; -} - -CFArrayRef SOSAccountCopyActiveValidPeers(SOSAccountRef account, CFErrorRef *error) { - if (!SOSAccountHasPublicKey(account, error)) - return NULL; - - CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - SOSCircleForEachActiveValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) { - CFArrayAppendValue(peers, peer); - }); - }); - - return peers; -} - - -CFArrayRef SOSAccountCopyConcurringPeers(SOSAccountRef account, CFErrorRef *error) -{ - if (!SOSAccountHasPublicKey(account, error)) - return NULL; - - CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); - - SOSAccountForEachCircle(account, ^(SOSCircleRef circle) { - CFMutableArrayRef circleConcurring = SOSCircleCopyConcurringPeers(circle, NULL); - CFArrayAppendArray(concurringPeers, circleConcurring, CFRangeMake(0, CFArrayGetCount(circleConcurring))); - CFReleaseSafe(circleConcurring); - }); - - return concurringPeers; -} - -CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) -{ - return CFSTR("We're compatible, go away"); -} - -enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) -{ - return account->departure_code; -} - -// -// TODO: Handle '|' and "¬" in other strings. -// -const CFStringRef kSOSKVSKeyParametersKey = CFSTR(">KeyParameters"); -const CFStringRef kSOSKVSInitialSyncKey = CFSTR("^InitialSync"); -const CFStringRef kSOSKVSAccountChangedKey = CFSTR("^AccountChanged"); - -const CFStringRef sWarningPrefix = CFSTR("!"); -const CFStringRef sAncientCirclePrefix = CFSTR("@"); -const CFStringRef sCirclePrefix = CFSTR("o"); -const CFStringRef sRetirementPrefix = CFSTR("-"); -const CFStringRef sCircleSeparator = CFSTR("|"); -const CFStringRef sFromToSeparator = CFSTR(":"); - -static CFStringRef stringEndingIn(CFMutableStringRef in, CFStringRef token) { - if(token == NULL) return CFStringCreateCopy(NULL, in); - CFRange tokenAt = CFStringFind(in, token, 0); - if(tokenAt.location == kCFNotFound) return NULL; - CFStringRef retval = CFStringCreateWithSubstring(NULL, in, CFRangeMake(0, tokenAt.location)); - CFStringDelete(in, CFRangeMake(0, tokenAt.location+1)); - return retval; -} - -SOSKVSKeyType SOSKVSKeyGetKeyTypeAndParse(CFStringRef key, CFStringRef *circle, CFStringRef *from, CFStringRef *to) -{ - SOSKVSKeyType retval = kUnknownKey; - - if(CFStringHasPrefix(key, sCirclePrefix)) retval = kCircleKey; - else if(CFStringHasPrefix(key, sRetirementPrefix)) retval = kRetirementKey; - else if(CFStringHasPrefix(key, kSOSKVSKeyParametersKey)) retval = kParametersKey; - else if(CFStringHasPrefix(key, kSOSKVSInitialSyncKey)) retval = kInitialSyncKey; - else if(CFStringHasPrefix(key, kSOSKVSAccountChangedKey)) retval = kAccountChangedKey; - else retval = kMessageKey; - - switch(retval) { - case kCircleKey: - if (circle) { - CFRange fromRange = CFRangeMake(1, CFStringGetLength(key)-1); - *circle = CFStringCreateWithSubstring(NULL, key, fromRange); - } - break; - case kMessageKey: { - CFStringRef mCircle = NULL; - CFStringRef mFrom = NULL; - CFStringRef mTo = NULL; - CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key); - - if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) && - ((mFrom = stringEndingIn(keycopy, sFromToSeparator)) != NULL) && - (CFStringGetLength(mFrom) > 0) ) { - mTo = stringEndingIn(keycopy, NULL); - if (circle) *circle = CFStringCreateCopy(NULL, mCircle); - if (from) *from = CFStringCreateCopy(NULL, mFrom); - if (to && mTo) *to = CFStringCreateCopy(NULL, mTo); - } else { - retval = kUnknownKey; - } - CFReleaseNull(mCircle); - CFReleaseNull(mFrom); - CFReleaseNull(mTo); - CFReleaseNull(keycopy); - } - break; - case kRetirementKey: { - CFStringRef mCircle = NULL; - CFStringRef mPeer = NULL; - CFMutableStringRef keycopy = CFStringCreateMutableCopy(NULL, 128, key); - CFStringDelete(keycopy, CFRangeMake(0, 1)); - if( ((mCircle = stringEndingIn(keycopy, sCircleSeparator)) != NULL) && - ((mPeer = stringEndingIn(keycopy, NULL)) != NULL)) { - if (circle) *circle = CFStringCreateCopy(NULL, mCircle); - if (from) *from = CFStringCreateCopy(NULL, mPeer); - } else { - retval = kUnknownKey; - } - // TODO - Update our circle - CFReleaseNull(mCircle); - CFReleaseNull(mPeer); - CFReleaseNull(keycopy); - } - break; - case kAccountChangedKey: - case kParametersKey: - case kInitialSyncKey: - case kUnknownKey: - break; - } - - return retval; -} - - -SOSKVSKeyType SOSKVSKeyGetKeyType(CFStringRef key) -{ - return SOSKVSKeyGetKeyTypeAndParse(key, NULL, NULL, NULL); -} - -CFStringRef SOSCircleKeyCreateWithCircle(SOSCircleRef circle, CFErrorRef *error) -{ - return SOSCircleKeyCreateWithName(SOSCircleGetName(circle), error); -} - - -CFStringRef SOSCircleKeyCreateWithName(CFStringRef circleName, CFErrorRef *error) -{ - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), sCirclePrefix, circleName); -} - -CFStringRef SOSCircleKeyCopyCircleName(CFStringRef key, CFErrorRef *error) -{ - CFStringRef circleName = NULL; - - if (kCircleKey != SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL)) { - SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key); - - CFReleaseNull(circleName); - } - - return circleName; -} - -CFStringRef SOSMessageKeyCopyCircleName(CFStringRef key, CFErrorRef *error) -{ - CFStringRef circleName = NULL; - - if (SOSKVSKeyGetKeyTypeAndParse(key, &circleName, NULL, NULL) != kMessageKey) { - SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find circle name in key '%@'"), key); - - CFReleaseNull(circleName); - } - return circleName; -} - -CFStringRef SOSMessageKeyCopyFromPeerName(CFStringRef messageKey, CFErrorRef *error) -{ - CFStringRef fromPeer = NULL; - - if (SOSKVSKeyGetKeyTypeAndParse(messageKey, NULL, &fromPeer, NULL) != kMessageKey) { - SOSCreateErrorWithFormat(kSOSErrorNoCircleName, NULL, error, NULL, CFSTR("Couldn't find from peer in key '%@'"), messageKey); - - CFReleaseNull(fromPeer); - } - return fromPeer; -} - -CFStringRef SOSMessageKeyCreateWithCircleAndPeerNames(SOSCircleRef circle, CFStringRef from_peer_name, CFStringRef to_peer_name) -{ - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@%@"), - SOSCircleGetName(circle), sCircleSeparator, from_peer_name, sFromToSeparator, to_peer_name); -} - -CFStringRef SOSMessageKeyCreateWithCircleAndPeerInfos(SOSCircleRef circle, SOSPeerInfoRef from_peer, SOSPeerInfoRef to_peer) -{ - return SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(from_peer), SOSPeerInfoGetPeerID(to_peer)); -} - -CFStringRef SOSMessageKeyCreateWithAccountAndPeer(SOSAccountRef account, SOSCircleRef circle, CFStringRef peer_name) { - // TODO: Handle errors! - CFErrorRef error = NULL; - - SOSFullPeerInfoRef me = SOSAccountGetMyFullPeerInCircle(account, circle, &error); - SOSPeerInfoRef my_pi = SOSFullPeerInfoGetPeerInfo(me); - CFStringRef result = SOSMessageKeyCreateWithCircleAndPeerNames(circle, SOSPeerInfoGetPeerID(my_pi), peer_name); - CFReleaseSafe(error); - return result; -} - -CFStringRef SOSRetirementKeyCreateWithCircleAndPeer(SOSCircleRef circle, CFStringRef retirement_peer_name) -{ - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@%@%@"), - sRetirementPrefix, SOSCircleGetName(circle), sCircleSeparator, retirement_peer_name); -} - - -static SOSPeerCoderStatus SOSAccountHandlePeerMessage(SOSAccountRef account, - CFStringRef circle_id, - CFStringRef peer_name, - CFDataRef message, - SOSAccountSendBlock send_block, - CFErrorRef *error) -{ - bool success = false; - CFStringRef peer_key = NULL; - - SOSCircleRef circle = SOSAccountFindCircle(account, circle_id, error); - require_quiet(circle, fail); - SOSFullPeerInfoRef myFullPeer = SOSAccountGetMyFullPeerInCircle(account, circle, error); - SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(myFullPeer); - require_action_quiet(SOSCircleHasPeer(circle, myPeer, NULL), fail, SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL, CFSTR("Not in circle, can't handle message"))); - - peer_key = SOSMessageKeyCreateWithAccountAndPeer(account, circle, peer_name); - - SOSPeerSendBlock peer_send_block = ^bool (CFDataRef message, CFErrorRef *error) { - return send_block(circle, peer_key, message, error); - }; - - success = SOSCircleHandlePeerMessage(circle, myFullPeer, account->factory, peer_send_block, peer_name, message, error); - -fail: - CFReleaseNull(peer_key); - return success; -} - -bool SOSAccountHandleUpdates(SOSAccountRef account, - CFDictionaryRef updates, - CFErrorRef *error) { - - if(CFDictionaryGetValue(updates, kSOSKVSAccountChangedKey) != NULL) { - SOSAccountSetToNew(account); - } - - CFTypeRef parameters = CFDictionaryGetValue(updates, kSOSKVSKeyParametersKey); - if (isData(parameters)) { - SecKeyRef newKey = NULL; - CFDataRef newParameters = NULL; - const uint8_t *parse_end = der_decode_cloud_parameters(kCFAllocatorDefault, kSecECDSAAlgorithmID, - &newKey, &newParameters, error, - CFDataGetBytePtr(parameters), CFDataGetPastEndPtr(parameters)); - - if (parse_end == CFDataGetPastEndPtr(parameters)) { - if (CFEqualSafe(account->user_public, newKey)) { - secnotice("updates", "Got same public key sent our way. Ignoring."); - } else if (CFEqualSafe(account->previous_public, newKey)) { - secnotice("updates", "Got previous public key repeated. Ignoring."); - } else { - CFReleaseNull(account->user_public); - SOSAccountPurgePrivateCredential(account); - CFReleaseNull(account->user_key_parameters); - - account->user_public_trusted = false; - - account->user_public = newKey; - newKey = NULL; - - account->user_key_parameters = newParameters; - newParameters = NULL; - - secnotice("updates", "Got new parameters for public key: %@", account->user_public); - debugDumpUserParameters(CFSTR("params"), account->user_key_parameters); - } - } - - CFReleaseNull(newKey); - CFReleaseNull(newParameters); - } - - if (!account->user_public_trusted) { - if (!account->deferred_updates) { - account->deferred_updates = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); - } - - CFDictionaryForEach(updates, ^(const void *key, const void *value) { - if (!CFEqualSafe(key, kSOSKVSKeyParametersKey) && !CFEqualSafe(key, kSOSKVSAccountChangedKey)) - CFDictionarySetValue(account->deferred_updates, key, value); - }); - secnotice("updates", "No public peer key, deferring updates: %@", updates); - return true; - } - - // Iterate though keys in updates. Perform circle change update. - // Then instantiate circles and engines and peers for all peers that - // are receiving a message in updates. - __block bool is_initial_sync = CFDictionaryContainsKey(updates, kSOSKVSInitialSyncKey); - - CFDictionaryForEach(updates, ^(const void *key, const void *value) { - CFStringRef circle_name = NULL; - CFErrorRef localError = NULL; - SOSCircleRef circle = NULL; - - if (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, NULL, NULL) == kCircleKey) { - circle = SOSAccountCreateCircleFrom(circle_name, value, &localError); - if (!circle) { - if (isSOSErrorCoded(localError, kSOSErrorIncompatibleCircle)) { - SOSAccountDestroyCirclePeerInfoNamed(account, circle_name, NULL); - CFDictionarySetValue(account->circles, circle_name, kCFNull); - } else { - SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL, - CFSTR("Bad key for message, no circle '%@'"), key); - goto circle_done; - } - } - - if (!SOSAccountUpdateCircleFromRemote(account, circle, is_initial_sync, &localError)) { - SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, localError, error, NULL, - CFSTR("Error handling circle change '%@'"), key); - secnotice("update", "Error updating circle '%@': %@", key, circle); - goto circle_done; - } - } - circle_done: - CFReleaseSafe(circle_name); - CFReleaseNull(circle); - CFReleaseNull(localError); - }); - - CFDictionaryForEach(updates, ^(const void *key, const void *value) { - CFErrorRef localError = NULL; - CFStringRef circle_name = NULL; - CFStringRef from_name = NULL; - CFStringRef to_name = NULL; - switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &from_name, &to_name)) { - case kParametersKey: - case kInitialSyncKey: - case kCircleKey: - break; - case kMessageKey: - { - SOSFullPeerInfoRef my_peer = NULL; - - require_action_quiet(isData(value), message_error, SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, localError, error, NULL, CFSTR("Non-Data for message(%@) from '%@'"), value, key)); - require_quiet(my_peer = SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, circle_name, &localError), message_error); - - CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(my_peer)); - require_quiet(SOSAccountIsActivePeerInCircleNamed(account, circle_name, my_id, &localError), skip); - require_quiet(CFEqual(my_id, to_name), skip); - require_quiet(!CFEqual(my_id, from_name), skip); - - SOSAccountSendBlock cacheInDictionary = ^ bool (SOSCircleRef circle, CFStringRef key, CFDataRef new_message, CFErrorRef* error) { - CFDictionarySetValue(account->pending_changes, key, new_message); - - if (account->processed_message_block) { - account->processed_message_block(circle, value, new_message); - } - - return true; - }; - - if (SOSAccountHandlePeerMessage(account, circle_name, from_name, value, cacheInDictionary, &localError) == kSOSPeerCoderFailure) { - SOSCreateErrorWithFormat(kSOSErrorNameMismatch, localError, error, NULL, - CFSTR("Error handling peer message from '%@'"), key); - localError = NULL; // Released by SOSCreateErrorWithFormat - goto message_error; - } - - message_error: - skip: - break; - } - case kRetirementKey: - if(isData(value)) { - SOSPeerInfoRef pi = SOSPeerInfoCreateFromData(NULL, error, (CFDataRef) value); - if(pi && CFEqual(from_name, SOSPeerInfoGetPeerID(pi)) && SOSPeerInfoInspectRetirementTicket(pi, error)) { - CFDictionarySetValue(account->retired_peers, key, value); - SOSAccountRecordRetiredPeerInCircleNamed(account, circle_name, pi); - } - CFReleaseSafe(pi); - } - break; - - case kAccountChangedKey: // Handled at entry to function to make sure these are processed first. - break; - - case kUnknownKey: - secnotice("updates", "Unknown key '%@', ignoring", key); - break; - - } - - CFReleaseNull(circle_name); - CFReleaseNull(from_name); - CFReleaseNull(to_name); - - if (error && *error) - secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error); - if (localError) - secerror("Peer message local processing error for: %@ -> %@ (%@)", key, value, localError); - - CFReleaseNull(localError); - }); - - return SOSAccountSendPendingChanges(account, error); -} - -void SOSAccountSetMessageProcessedBlock(SOSAccountRef account, SOSAccountMessageProcessedBlock processedBlock) -{ - CFRetainSafe(processedBlock); - CFReleaseNull(account->processed_message_block); - account->processed_message_block = processedBlock; -} - -CFStringRef SOSInterestListCopyDescription(CFArrayRef interests) -{ - CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0); - CFStringAppendFormat(description, NULL, CFSTR("")); - - return description; -}