]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountBackup.m
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountBackup.m
1 //
2 // SOSAccountCircles.c
3 // sec
4 //
5
6 #include "SOSAccount.h"
7 #include "SOSCloudKeychainClient.h"
8
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSViews.h>
12 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
13 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h>
14
15 #include "SOSInternal.h"
16
17
18
19 //
20 // MARK: V0 Keybag keychain stuff
21 //
22 static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
23 {
24 OSStatus saveStatus = SecItemUpdate(query, update);
25
26 if (errSecItemNotFound == saveStatus) {
27 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
28 CFDictionaryForEach(update, ^(const void *key, const void *value) {
29 CFDictionaryAddValue(add, key, value);
30 });
31 saveStatus = SecItemAdd(add, NULL);
32 CFReleaseNull(add);
33 }
34
35 return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
36 }
37
38 static CFDictionaryRef SOSCopyV0Attributes() {
39 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
40 kSecClass, kSecClassGenericPassword,
41 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
42 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
43 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
44 kSecAttrService, CFSTR("SecureBackupService"),
45 kSecAttrSynchronizable, kCFBooleanTrue,
46 NULL);
47 }
48
49 bool SOSDeleteV0Keybag(CFErrorRef *error) {
50 CFDictionaryRef attributes = SOSCopyV0Attributes();
51
52 OSStatus result = SecItemDelete(attributes);
53
54 CFReleaseNull(attributes);
55
56 return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %d"), (int)result);
57 }
58
59 static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
60 CFDictionaryRef attributes = SOSCopyV0Attributes();
61
62 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
63 kSecValueData, v0Keybag,
64 NULL);
65
66
67 bool result = SecItemUpdateOrAdd(attributes, update, error);
68 CFReleaseNull(attributes);
69 CFReleaseNull(update);
70
71 return result;
72 }
73
74
75 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
76 if (CFEqualSafe(kSOSViewKeychainV0, viewName))
77 return false;
78
79 return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
80 }
81
82 static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccount* account, CFStringRef viewName) {
83 CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
84
85 SOSCircleRef circle = [account.trust getCircle:NULL];
86
87 require_quiet(circle, exit);
88
89 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
90 if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
91 CFSetAddValue(backupPeers, peer);
92 });
93
94 exit:
95 return backupPeers;
96 }
97
98 static void SOSAccountWithBackupPeersForView(SOSAccount* account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
99 CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
100
101 action(backupPeersForView);
102
103 CFReleaseNull(backupPeersForView);
104 }
105
106
107 static bool SOSAccountWithBSKBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
108 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
109 __block SOSBackupSliceKeyBagRef bskb = NULL;
110 bool result = false;
111 CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
112
113 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
114 if(! rkbg) {
115 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
116 } else {
117 CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
118 CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, rkbg);
119 bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, peers, additionalKeys, error);
120 CFReleaseNull(additionalKeys);
121 }
122 });
123 CFReleaseNull(rkbg);
124
125 require_quiet(bskb, exit);
126
127 action(bskb, error);
128
129 result = true;
130
131 exit:
132 CFReleaseNull(bskb);
133 return result;
134 }
135
136 CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
137 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
138 }
139
140 static bool SOSAccountUpdateBackupRing(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
141 SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
142
143 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
144
145 bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
146 return SOSRingCreate(ringName, (__bridge CFStringRef) account.peerID, kSOSRingBackup, error);
147 }, modify);
148
149 CFReleaseNull(ringName);
150
151 return result;
152 }
153
154
155
156 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccount* account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
157 CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
158 bool result = false;
159
160 if(!SecAllocationError(backupViewSet, error, CFSTR("No backup view set created"))){
161 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
162
163 return result;
164 }
165 CFSetAddValue(backupViewSet, viewName);
166
167 result = SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
168 SOSRingRef newRing = NULL;
169 CFSetRef viewPeerSet = [account.trust copyPeerSetForView:viewName];
170 CFMutableSetRef cleared = CFSetCreateMutableForCFTypes(NULL);
171
172 SOSRingSetPeerIDs(existing, cleared);
173 SOSRingAddAll(existing, viewPeerSet);
174
175 require_quiet(SOSRingSetBackupKeyBag(existing, account.fullPeerInfo, backupViewSet, keyBag, error), exit);
176
177 newRing = CFRetainSafe(existing);
178 exit:
179 CFReleaseNull(viewPeerSet);
180 CFReleaseNull(cleared);
181 return newRing;
182 });
183
184 if (result && NULL != error && NULL != *error) {
185 secerror("Got Success and Error (dropping error): %@", *error);
186 CFReleaseNull(*error);
187 }
188
189 if (!result) {
190 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
191 }
192
193 CFReleaseNull(backupViewSet);
194 return result;
195 }
196
197 bool SOSAccountNewBKSBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error)
198 {
199 return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
200 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
201 return result;
202 });
203 }
204
205 bool SOSAccountIsBackupRingEmpty(SOSAccount* account, CFStringRef viewName) {
206 CFStringRef backupRing = SOSBackupCopyRingNameForView(viewName);
207 SOSRingRef ring = [account.trust copyRing:backupRing err:NULL];
208 CFReleaseNull(backupRing);
209 int peercnt = 0;
210 if(ring) peercnt = SOSRingCountPeers(ring);
211 CFReleaseNull(ring);
212 return peercnt == 0;
213 }
214
215 bool SOSAccountUpdatePeerInfo(SOSAccount* account, CFStringRef updateDescription, CFErrorRef *error, bool (^update)(SOSFullPeerInfoRef fpi, CFErrorRef *error)) {
216
217 if (!account.hasPeerInfo)
218 return true;
219
220 bool result = update(account.fullPeerInfo, error);
221
222 if (result && SOSAccountHasCircle(account, NULL)) {
223 return [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle_to_change) {
224 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription);
225 return SOSCircleUpdatePeerInfo(circle_to_change, account.peerInfo);
226 }];
227 }
228
229 return result;
230 }
231
232 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccount* account, CFStringRef viewname){
233 bool result = false;
234 CFErrorRef bsError = NULL;
235 CFDataRef backupSliceData = NULL;
236 SOSRingRef ring = NULL;
237 SOSBackupSliceKeyBagRef backupSlice = NULL;
238
239 require_quiet(SOSPeerInfoIsViewBackupEnabled(account.peerInfo, viewname), errOut);
240
241 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
242 ring = [account.trust copyRing:ringName err:&bsError];
243 CFReleaseNull(ringName);
244
245 require_quiet(ring, errOut);
246
247 //grab the backup slice from the ring
248 backupSliceData = SOSRingGetPayload(ring, &bsError);
249 require_quiet(backupSliceData, errOut);
250
251 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
252 require_quiet(backupSlice, errOut);
253
254 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
255 SOSPeerInfoRef myPeer = account.peerInfo;
256
257 SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
258 require_quiet(isSOSPeerInfo(myPeerInBSKB), errOut);
259
260 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
261 CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
262 result = CFEqualSafe(myBK, myPeerInBSKBBK);
263 CFReleaseNull(myBK);
264 CFReleaseNull(myPeerInBSKBBK);
265
266 errOut:
267 CFReleaseNull(ring);
268 CFReleaseNull(backupSlice);
269
270 if (bsError) {
271 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
272 }
273 CFReleaseNull(bsError);
274 return result;
275 }
276
277 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccount* account, SOSPeerInfoRef testPeer, CFStringRef viewname){
278 bool result = false;
279 CFErrorRef bsError = NULL;
280 CFDataRef backupSliceData = NULL;
281 SOSRingRef ring = NULL;
282 SOSBackupSliceKeyBagRef backupSlice = NULL;
283
284 require_quiet(testPeer, errOut);
285
286 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
287
288 ring = [account.trust copyRing:ringName err:&bsError];
289 CFReleaseNull(ringName);
290
291 require_quiet(ring, errOut);
292
293 //grab the backup slice from the ring
294 backupSliceData = SOSRingGetPayload(ring, &bsError);
295 require_quiet(backupSliceData, errOut);
296
297 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
298 require_quiet(backupSlice, errOut);
299
300 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
301
302 SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
303 require_quiet(isSOSPeerInfo(peerInBSKB), errOut);
304
305 result = CFEqualSafe(testPeer, peerInBSKB);
306
307 errOut:
308 CFReleaseNull(ring);
309 CFReleaseNull(backupSlice);
310
311 if (bsError) {
312 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
313 }
314 CFReleaseNull(bsError);
315 return result;
316
317 }
318
319 bool SOSAccountUpdateOurPeerInBackup(SOSAccount* account, SOSRingRef oldRing, CFErrorRef *error){
320 bool result = false;
321 CFSetRef viewNames = SOSBackupRingGetViews(oldRing, error);
322 __block CFStringRef viewName = NULL;
323 require_quiet(viewNames, fail);
324 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames), error, CFSTR("Only support single view backup rings")), fail);
325
326 CFSetForEach(viewNames, ^(const void *value) {
327 if (isString(value)) {
328 viewName = CFRetainSafe((CFStringRef) value);
329 }
330 });
331
332 result = SOSAccountNewBKSBForView(account, viewName, error);
333
334 fail:
335 CFReleaseNull(viewName);
336 return result;
337 }
338
339 void SOSAccountForEachBackupRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
340 SOSPeerInfoRef myPeer = account.peerInfo;
341 if (myPeer) {
342 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
343
344 CFSetForEach(allViews, ^(const void *value) {
345 CFStringRef viewName = asString(value, NULL);
346
347 if (viewName) {
348 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
349 operation(ringName);
350 CFReleaseNull(ringName);
351 }
352 });
353 CFReleaseNull(allViews);
354 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
355 operation(kSOSRecoveryRing);
356 }
357 }
358
359
360 void SOSAccountForEachRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
361 SOSPeerInfoRef myPeer = account.peerInfo;
362 if (myPeer) {
363 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
364
365 CFSetForEach(allViews, ^(const void *value) {
366 CFStringRef viewName = asString(value, NULL);
367
368 if (viewName) {
369 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
370 operation(ringName);
371 CFReleaseNull(ringName);
372 }
373 });
374 CFReleaseNull(allViews);
375 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
376 operation(kSOSRecoveryRing);
377 }
378 }
379
380 void SOSAccountForEachBackupView(SOSAccount* account, void (^operation)(const void *value)) {
381 SOSPeerInfoRef myPeer = account.peerInfo;
382
383 if (myPeer) {
384 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
385 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
386 CFSetForEach(myBackupViews, operation);
387 CFReleaseNull(myBackupViews);
388 }
389 }
390
391
392 bool SOSAccountSetBackupPublicKey(SOSAccountTransaction* aTxn, CFDataRef cfBackupKey, CFErrorRef *error)
393 {
394 SOSAccount* account = aTxn.account;
395 NSData* backupKey = [[NSData alloc]initWithData:(__bridge NSData * _Nonnull)(cfBackupKey)];
396
397 __block bool result = false;
398
399 CFDataPerformWithHexString((__bridge CFDataRef)(backupKey), ^(CFStringRef backupKeyString) {
400 CFDataPerformWithHexString((__bridge CFDataRef)((account.backup_key)), ^(CFStringRef oldBackupKey) {
401 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString, oldBackupKey);
402 });
403 });
404
405 require_quiet([account.trust isInCircle:error], exit);
406
407 if ([backupKey isEqual:account.backup_key])
408 return true;
409
410 account.backup_key = [[NSData alloc] initWithData:backupKey];
411
412 account.circle_rings_retirements_need_attention = true;
413
414 result = true;
415
416 exit:
417 if (!result) {
418 secnotice("backupkey", "SetBackupPublic Failed: %@", error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space"));
419 }
420 return result;
421 }
422
423 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccount* account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
424 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
425 __block SOSBackupSliceKeyBagRef bskb = NULL;
426 bool result = false;
427
428 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
429 CFMutableSetRef newPeerList = CFSetCreateMutableCopy(kCFAllocatorDefault, CFSetGetCount(peers), peers);
430 CFArrayForEach(retiree, ^(const void *value) {
431 if (!isSOSPeerInfo(value)) {
432 secerror("Peer list contains a non-peerInfo element");
433 } else {
434 SOSPeerInfoRef retiringPeer = (SOSPeerInfoRef)value;
435 CFStringRef retiringPeerID = SOSPeerInfoGetPeerID(retiringPeer);
436
437 CFSetForEach(newPeerList, ^(const void *peerFromAccount) {
438 CFStringRef peerFromAccountID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)peerFromAccount);
439 if (peerFromAccountID && retiringPeerID && CFStringCompare(peerFromAccountID, retiringPeerID, 0) == 0){
440 CFSetRemoveValue(newPeerList, peerFromAccount);
441 }
442 });
443 }
444 });
445 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
446 CFReleaseNull(newPeerList);
447 });
448
449 require_quiet(bskb, exit);
450
451 action(bskb, error);
452
453 result = true;
454
455 exit:
456 CFReleaseNull(bskb);
457 return result;
458 }
459
460 bool SOSAccountRemoveBackupPublickey(SOSAccountTransaction* aTxn, CFErrorRef *error)
461 {
462 SOSAccount* account = aTxn.account;
463
464 __block bool result = false;
465 __block CFArrayRef removals = NULL;
466
467 account.backup_key = nil;
468
469 if(!SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
470 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
471 return SOSFullPeerInfoUpdateBackupKey(fpi, NULL, error);
472 })){
473 return result;
474 }
475
476 removals = CFArrayCreateForCFTypes(kCFAllocatorDefault,
477 account.peerInfo, NULL);
478
479 SOSAccountForEachBackupView(account, ^(const void *value) {
480 CFStringRef viewName = (CFStringRef)value;
481 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
482 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
483 return result;
484 });
485 });
486
487
488 result = true;
489
490 return result;
491
492 }
493
494 bool SOSAccountSetBSKBagForAllSlices(SOSAccount* account, CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
495 __block bool result = false;
496 SOSBackupSliceKeyBagRef backup_slice = NULL;
497
498 require_quiet([account.trust isInCircle:error], exit);
499
500 if (setupV0Only) {
501 result = SOSSaveV0Keybag(aks_bag, error);
502 require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
503 } else {
504 result = true;
505
506 backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
507
508 SOSAccountForEachBackupView(account, ^(const void *value) {
509 CFStringRef viewname = (CFStringRef) value;
510 result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
511 });
512 }
513
514 exit:
515 CFReleaseNull(backup_slice);
516 return result;
517 }
518
519 static CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccount* account, CFArrayRef peers, CFSetRef peersInBackup){
520 CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
521
522 CFSetForEach(peersInBackup, ^(const void *value) {
523 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
524 CFArrayForEach(peers, ^(const void *value) {
525 CFStringRef peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
526 CFStringRef piPeerID = SOSPeerInfoGetPeerID(peer);
527 if (peerID && piPeerID && CFStringCompare(piPeerID, peerID, 0) == 0){
528 CFArrayAppendValue(removals, peer);
529 }
530 });
531
532 });
533
534 return removals;
535
536 }
537
538 bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peers, CFErrorRef *error){
539 __block bool result = true;
540
541 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
542 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
543
544 CFSetRef permittedViews = SOSPeerInfoGetPermittedViews(myPeer);
545 CFSetForEach(permittedViews, ^(const void *value) {
546 CFStringRef viewName = (CFStringRef)value;
547 if(SOSPeerInfoIsViewBackupEnabled(myPeer, viewName)){
548 //grab current peers list
549 CFSetRef peersInBackup = SOSAccountCopyBackupPeersForView(account, viewName);
550 //get peer infos that have retired but are still in the backup peer list
551 CFMutableArrayRef removals = SOSAccountIsRetiredPeerIDInBackupPeerList(account, peers, peersInBackup);
552 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
553 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
554 return result;
555 });
556 CFReleaseNull(removals);
557 CFReleaseNull(peersInBackup);
558 }
559 });
560
561 return result;
562
563 }
564
565 SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount* account, CFStringRef viewName, CFErrorRef* error){
566 CFDataRef backupSliceData = NULL;
567 CFStringRef ringName = NULL;
568 SOSRingRef ring = NULL;
569 SOSBackupSliceKeyBagRef bskb = NULL;
570
571 ringName = SOSBackupCopyRingNameForView(viewName);
572 ring = [account.trust copyRing:ringName err:NULL];
573 require_action_quiet(ring, exit, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("failed to get ring")));
574
575 //grab the backup slice from the ring
576 backupSliceData = SOSRingGetPayload(ring, error);
577 require_action_quiet(backupSliceData, exit, secnotice("backup", "failed to get backup slice (%@)", *error));
578
579 bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, error);
580
581 exit:
582 CFReleaseNull(ring);
583 CFReleaseNull(ringName);
584
585 return bskb;
586 }
587
588 bool SOSAccountIsLastBackupPeer(SOSAccount* account, CFErrorRef *error) {
589 __block bool retval = false;
590 SOSPeerInfoRef pi = account.peerInfo;
591 if(!SOSPeerInfoHasBackupKey(pi))
592 return retval;
593
594 SOSCircleRef circle = [account.trust getCircle:error];
595 if(![account.trust isInCircle:error])
596 return retval;
597 if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
598 retval = true;
599 return retval;
600 }
601 // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
602 SOSAccountForEachBackupView(account, ^(const void *value) {
603 CFStringRef viewname = (CFStringRef) value;
604 SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
605 require_quiet(keybag, inner_errOut);
606 retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
607 inner_errOut:
608 CFReleaseNull(keybag);
609 });
610
611 return retval;
612 }