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