6 #include "SOSAccount.h"
7 #include "SOSCloudKeychainClient.h"
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
12 #include <Security/SecureObjectSync/SOSViews.h>
13 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
14 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h>
16 #include "SOSInternal.h"
21 // MARK: V0 Keybag keychain stuff
23 static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
25 OSStatus saveStatus = SecItemUpdate(query, update);
27 if (errSecItemNotFound == saveStatus) {
28 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
29 CFDictionaryForEach(update, ^(const void *key, const void *value) {
30 CFDictionaryAddValue(add, key, value);
32 saveStatus = SecItemAdd(add, NULL);
36 return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
39 static CFDictionaryRef SOSCopyV0Attributes() {
40 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
41 kSecClass, kSecClassGenericPassword,
42 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
43 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
44 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
45 kSecAttrService, CFSTR("SecureBackupService"),
46 kSecAttrSynchronizable, kCFBooleanTrue,
50 bool SOSDeleteV0Keybag(CFErrorRef *error) {
51 CFDictionaryRef attributes = SOSCopyV0Attributes();
53 OSStatus result = SecItemDelete(attributes);
55 CFReleaseNull(attributes);
57 return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %d"), (int)result);
60 static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
61 CFDictionaryRef attributes = SOSCopyV0Attributes();
63 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
64 kSecValueData, v0Keybag,
68 bool result = SecItemUpdateOrAdd(attributes, update, error);
69 CFReleaseNull(attributes);
70 CFReleaseNull(update);
76 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
77 if (CFEqualSafe(kSOSViewKeychainV0, viewName))
80 return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
83 static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccount* account, CFStringRef viewName) {
84 CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
86 SOSCircleRef circle = [account.trust getCircle:NULL];
88 require_quiet(circle, exit);
90 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
91 if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
92 CFSetAddValue(backupPeers, peer);
99 static void SOSAccountWithBackupPeersForView(SOSAccount* account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
100 CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
102 action(backupPeersForView);
104 CFReleaseNull(backupPeersForView);
108 static bool SOSAccountWithBSKBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
109 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
110 __block SOSBackupSliceKeyBagRef bskb = NULL;
112 CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
114 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
116 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
118 CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
119 CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, rkbg);
120 bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, peers, additionalKeys, error);
121 CFReleaseNull(additionalKeys);
126 require_quiet(bskb, exit);
137 CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
138 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
141 static bool SOSAccountUpdateBackupRing(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
142 SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
144 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
146 bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
147 return SOSRingCreate(ringName, (__bridge CFStringRef) account.peerID, kSOSRingBackup, error);
150 CFReleaseNull(ringName);
157 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccount* account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
158 CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
161 if(!SecAllocationError(backupViewSet, error, CFSTR("No backup view set created"))){
162 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
166 CFSetAddValue(backupViewSet, viewName);
168 result = SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
169 SOSRingRef newRing = NULL;
170 CFSetRef viewPeerSet = [account.trust copyPeerSetForView:viewName];
171 CFMutableSetRef cleared = CFSetCreateMutableForCFTypes(NULL);
173 SOSRingSetPeerIDs(existing, cleared);
174 SOSRingAddAll(existing, viewPeerSet);
176 require_quiet(SOSRingSetBackupKeyBag(existing, account.fullPeerInfo, backupViewSet, keyBag, error), exit);
178 newRing = CFRetainSafe(existing);
180 CFReleaseNull(viewPeerSet);
181 CFReleaseNull(cleared);
185 if (result && NULL != error && NULL != *error) {
186 secerror("Got Success and Error (dropping error): %@", *error);
187 CFReleaseNull(*error);
191 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
194 CFReleaseNull(backupViewSet);
198 bool SOSAccountNewBKSBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error)
200 return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
201 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
206 bool SOSAccountIsBackupRingEmpty(SOSAccount* account, CFStringRef viewName) {
207 CFStringRef backupRing = SOSBackupCopyRingNameForView(viewName);
208 SOSRingRef ring = [account.trust copyRing:backupRing err:NULL];
209 CFReleaseNull(backupRing);
211 if(ring) peercnt = SOSRingCountPeers(ring);
216 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccount* account, CFStringRef viewname){
218 CFErrorRef bsError = NULL;
219 CFDataRef backupSliceData = NULL;
220 SOSRingRef ring = NULL;
221 SOSBackupSliceKeyBagRef backupSlice = NULL;
223 require_quiet(SOSPeerInfoIsViewBackupEnabled(account.peerInfo, viewname), errOut);
225 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
226 ring = [account.trust copyRing:ringName err:&bsError];
227 CFReleaseNull(ringName);
229 require_quiet(ring, errOut);
231 //grab the backup slice from the ring
232 backupSliceData = SOSRingGetPayload(ring, &bsError);
233 require_quiet(backupSliceData, errOut);
235 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
236 require_quiet(backupSlice, errOut);
238 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
239 SOSPeerInfoRef myPeer = account.peerInfo;
241 SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
242 require_quiet(isSOSPeerInfo(myPeerInBSKB), errOut);
244 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
245 CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
246 result = CFEqualSafe(myBK, myPeerInBSKBBK);
248 CFReleaseNull(myPeerInBSKBBK);
252 CFReleaseNull(backupSlice);
255 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
257 CFReleaseNull(bsError);
261 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccount* account, SOSPeerInfoRef testPeer, CFStringRef viewname){
263 CFErrorRef bsError = NULL;
264 CFDataRef backupSliceData = NULL;
265 SOSRingRef ring = NULL;
266 SOSBackupSliceKeyBagRef backupSlice = NULL;
268 require_quiet(testPeer, errOut);
270 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
272 ring = [account.trust copyRing:ringName err:&bsError];
273 CFReleaseNull(ringName);
275 require_quiet(ring, errOut);
277 //grab the backup slice from the ring
278 backupSliceData = SOSRingGetPayload(ring, &bsError);
279 require_quiet(backupSliceData, errOut);
281 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
282 require_quiet(backupSlice, errOut);
284 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
286 SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
287 require_quiet(isSOSPeerInfo(peerInBSKB), errOut);
289 result = CFEqualSafe(testPeer, peerInBSKB);
293 CFReleaseNull(backupSlice);
296 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
298 CFReleaseNull(bsError);
303 bool SOSAccountUpdateOurPeerInBackup(SOSAccount* account, SOSRingRef oldRing, CFErrorRef *error){
305 CFSetRef viewNames = SOSBackupRingGetViews(oldRing, error);
306 __block CFStringRef viewName = NULL;
307 require_quiet(viewNames, fail);
308 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames), error, CFSTR("Only support single view backup rings")), fail);
310 CFSetForEach(viewNames, ^(const void *value) {
311 if (isString(value)) {
312 viewName = CFRetainSafe((CFStringRef) value);
316 result = SOSAccountNewBKSBForView(account, viewName, error);
319 CFReleaseNull(viewName);
323 void SOSAccountForEachBackupRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
324 SOSPeerInfoRef myPeer = account.peerInfo;
326 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
328 CFSetForEach(allViews, ^(const void *value) {
329 CFStringRef viewName = asString(value, NULL);
332 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
334 CFReleaseNull(ringName);
337 CFReleaseNull(allViews);
338 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
339 operation(kSOSRecoveryRing);
344 void SOSAccountForEachRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
345 SOSPeerInfoRef myPeer = account.peerInfo;
347 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
349 CFSetForEach(allViews, ^(const void *value) {
350 CFStringRef viewName = asString(value, NULL);
353 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
355 CFReleaseNull(ringName);
358 CFReleaseNull(allViews);
359 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
360 operation(kSOSRecoveryRing);
364 void SOSAccountForEachBackupView(SOSAccount* account, void (^operation)(const void *value)) {
365 SOSPeerInfoRef myPeer = account.peerInfo;
368 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
369 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
370 CFSetForEach(myBackupViews, operation);
371 CFReleaseNull(myBackupViews);
376 bool SOSAccountSetBackupPublicKey(SOSAccountTransaction* aTxn, CFDataRef cfBackupKey, CFErrorRef *error)
378 SOSAccount* account = aTxn.account;
379 NSData* backupKey = [[NSData alloc]initWithData:(__bridge NSData * _Nonnull)(cfBackupKey)];
380 __block bool result = false;
382 if(![account isInCircle:error]) {
386 CFDataPerformWithHexString((__bridge CFDataRef)(backupKey), ^(CFStringRef backupKeyString) {
387 CFDataPerformWithHexString((__bridge CFDataRef)((account.backup_key)), ^(CFStringRef oldBackupKey) {
388 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString, oldBackupKey);
392 if ([backupKey isEqual:account.backup_key])
395 account.backup_key = [[NSData alloc] initWithData:backupKey];
397 account.circle_rings_retirements_need_attention = true;
402 secnotice("backupkey", "SetBackupPublic Failed: %@", error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space"));
407 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccount* account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
408 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
409 __block SOSBackupSliceKeyBagRef bskb = NULL;
412 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
413 CFMutableSetRef newPeerList = CFSetCreateMutableCopy(kCFAllocatorDefault, CFSetGetCount(peers), peers);
414 CFArrayForEach(retiree, ^(const void *value) {
415 if (!isSOSPeerInfo(value)) {
416 secerror("Peer list contains a non-peerInfo element");
418 SOSPeerInfoRef retiringPeer = (SOSPeerInfoRef)value;
419 CFStringRef retiringPeerID = SOSPeerInfoGetPeerID(retiringPeer);
421 CFSetForEach(newPeerList, ^(const void *peerFromAccount) {
422 CFStringRef peerFromAccountID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)peerFromAccount);
423 if (peerFromAccountID && retiringPeerID && CFStringCompare(peerFromAccountID, retiringPeerID, 0) == 0){
424 CFSetRemoveValue(newPeerList, peerFromAccount);
429 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
430 CFReleaseNull(newPeerList);
433 require_quiet(bskb, exit);
444 bool SOSAccountRemoveBackupPublickey(SOSAccountTransaction* aTxn, CFErrorRef *error)
446 SOSAccount* account = aTxn.account;
448 __block bool result = false;
449 __block CFArrayRef removals = NULL;
451 account.backup_key = nil;
453 if(!SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
454 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
455 return SOSFullPeerInfoUpdateBackupKey(fpi, NULL, error);
460 removals = CFArrayCreateForCFTypes(kCFAllocatorDefault,
461 account.peerInfo, NULL);
463 SOSAccountForEachBackupView(account, ^(const void *value) {
464 CFStringRef viewName = (CFStringRef)value;
465 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
466 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
478 bool SOSAccountSetBSKBagForAllSlices(SOSAccount* account, CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
479 __block bool result = false;
480 SOSBackupSliceKeyBagRef backup_slice = NULL;
482 if(![account isInCircle:error]) {
487 result = SOSSaveV0Keybag(aks_bag, error);
488 require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
492 backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
494 SOSAccountForEachBackupView(account, ^(const void *value) {
495 CFStringRef viewname = (CFStringRef) value;
496 result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
501 CFReleaseNull(backup_slice);
505 static CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccount* account, CFArrayRef peers, CFSetRef peersInBackup){
506 CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
508 CFSetForEach(peersInBackup, ^(const void *value) {
509 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
510 CFArrayForEach(peers, ^(const void *value) {
511 CFStringRef peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
512 CFStringRef piPeerID = SOSPeerInfoGetPeerID(peer);
513 if (peerID && piPeerID && CFStringCompare(piPeerID, peerID, 0) == 0){
514 CFArrayAppendValue(removals, peer);
524 bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peers, CFErrorRef *error){
525 __block bool result = true;
527 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
528 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
530 CFSetRef permittedViews = SOSPeerInfoGetPermittedViews(myPeer);
531 CFSetForEach(permittedViews, ^(const void *value) {
532 CFStringRef viewName = (CFStringRef)value;
533 if(SOSPeerInfoIsViewBackupEnabled(myPeer, viewName)){
534 //grab current peers list
535 CFSetRef peersInBackup = SOSAccountCopyBackupPeersForView(account, viewName);
536 //get peer infos that have retired but are still in the backup peer list
537 CFMutableArrayRef removals = SOSAccountIsRetiredPeerIDInBackupPeerList(account, peers, peersInBackup);
538 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
539 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
542 CFReleaseNull(removals);
543 CFReleaseNull(peersInBackup);
551 SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount* account, CFStringRef viewName, CFErrorRef* error){
552 CFDataRef backupSliceData = NULL;
553 CFStringRef ringName = NULL;
554 SOSRingRef ring = NULL;
555 SOSBackupSliceKeyBagRef bskb = NULL;
557 ringName = SOSBackupCopyRingNameForView(viewName);
558 ring = [account.trust copyRing:ringName err:NULL];
559 require_action_quiet(ring, exit, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("failed to get ring")));
561 //grab the backup slice from the ring
562 backupSliceData = SOSRingGetPayload(ring, error);
563 require_action_quiet(backupSliceData, exit, secnotice("backup", "failed to get backup slice (%@)", *error));
565 bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, error);
569 CFReleaseNull(ringName);
574 bool SOSAccountIsLastBackupPeer(SOSAccount* account, CFErrorRef *error) {
575 __block bool retval = false;
576 SOSPeerInfoRef pi = account.peerInfo;
578 if(![account isInCircle:error]) {
582 if(!SOSPeerInfoHasBackupKey(pi))
585 SOSCircleRef circle = [account.trust getCircle:error];
587 if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
591 // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
592 SOSAccountForEachBackupView(account, ^(const void *value) {
593 CFStringRef viewname = (CFStringRef) value;
594 SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
595 require_quiet(keybag, inner_errOut);
596 retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
598 CFReleaseNull(keybag);