6 #include "SOSAccountPriv.h"
7 #include "SOSCloudKeychainClient.h"
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSViews.h>
13 #include "SOSInternal.h"
16 // MARK: V0 Keybag keychain stuff
18 static bool SecItemUpdateOrAdd(CFDictionaryRef query
, CFDictionaryRef update
, CFErrorRef
*error
)
20 OSStatus saveStatus
= SecItemUpdate(query
, update
);
22 if (errSecItemNotFound
== saveStatus
) {
23 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
24 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
25 CFDictionaryAddValue(add
, key
, value
);
27 saveStatus
= SecItemAdd(add
, NULL
);
31 return SecError(saveStatus
, error
, CFSTR("Error saving %@"), query
);
34 static CFDictionaryRef
SOSCopyV0Attributes() {
35 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
36 kSecClass
, kSecClassGenericPassword
,
37 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
38 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
39 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
40 kSecAttrService
, CFSTR("SecureBackupService"),
41 kSecAttrSynchronizable
, kCFBooleanTrue
,
45 bool SOSDeleteV0Keybag(CFErrorRef
*error
) {
46 CFDictionaryRef attributes
= SOSCopyV0Attributes();
48 OSStatus result
= SecItemDelete(attributes
);
50 CFReleaseNull(attributes
);
52 return SecError(result
!= errSecItemNotFound
? result
: errSecSuccess
, error
, CFSTR("Deleting V0 Keybag failed - %ld"), result
);
55 static bool SOSSaveV0Keybag(CFDataRef v0Keybag
, CFErrorRef
*error
) {
56 CFDictionaryRef attributes
= SOSCopyV0Attributes();
58 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
59 kSecValueData
, v0Keybag
,
63 bool result
= SecItemUpdateOrAdd(attributes
, update
, error
);
64 CFReleaseNull(attributes
);
65 CFReleaseNull(update
);
71 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo
, CFStringRef viewName
) {
72 if (CFEqualSafe(kSOSViewKeychainV0
, viewName
))
75 return SOSPeerInfoHasBackupKey(peerInfo
) && SOSPeerInfoIsViewPermitted(peerInfo
, viewName
);
78 static CFSetRef
SOSAccountCopyBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
) {
79 CFMutableSetRef backupPeers
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
81 SOSCircleRef circle
= SOSAccountGetCircle(account
, NULL
);
83 require_quiet(circle
, exit
);
85 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
86 if (SOSPeerInfoIsViewBackupEnabled(peer
, viewName
))
87 CFSetAddValue(backupPeers
, peer
);
94 static void SOSAccountWithBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
, void (^action
)(CFSetRef peers
)) {
95 CFSetRef backupPeersForView
= SOSAccountCopyBackupPeersForView(account
, viewName
);
97 action(backupPeersForView
);
99 CFReleaseNull(backupPeersForView
);
102 static bool SOSAccountWithBSKBForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
103 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
104 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
107 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
108 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, peers
, error
);
111 require_quiet(bskb
, exit
);
122 CFStringRef
SOSBackupCopyRingNameForView(CFStringRef viewName
) {
123 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewName
);
126 static bool SOSAccountUpdateNamedRing(SOSAccountRef account
, CFStringRef ringName
, CFErrorRef
*error
,
127 SOSRingRef (^create
)(CFStringRef ringName
, CFErrorRef
*error
),
128 SOSRingRef (^copyModified
)(SOSRingRef existing
, CFErrorRef
*error
)) {
130 SOSRingRef newRing
= NULL
;
131 SOSRingRef found
= (SOSRingRef
) CFDictionaryGetValue(account
->trusted_rings
, ringName
);
132 if (isSOSRing(found
)) {
133 found
= SOSRingCopyRing(found
, error
);
136 secerror("Non ring in ring table: %@, purging!", found
);
137 CFDictionaryRemoveValue(account
->trusted_rings
, ringName
);
139 found
= create(ringName
, error
);
142 require_quiet(found
, exit
);
143 newRing
= copyModified(found
, error
);
144 CFReleaseNull(found
);
146 require_quiet(newRing
, exit
);
148 result
= SOSAccountHandleUpdateRing(account
, newRing
, true, error
);
151 CFReleaseNull(found
);
152 CFReleaseNull(newRing
);
156 static bool SOSAccountUpdateBackupRing(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
157 SOSRingRef (^modify
)(SOSRingRef existing
, CFErrorRef
*error
)) {
159 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
161 bool result
= SOSAccountUpdateNamedRing(account
, ringName
, error
, ^SOSRingRef(CFStringRef ringName
, CFErrorRef
*error
) {
162 return SOSRingCreate(ringName
, SOSAccountGetMyPeerID(account
), kSOSRingBackup
, error
);
165 CFReleaseNull(ringName
);
170 static CFSetRef
SOSAccountCopyPeerSetForView(SOSAccountRef account
, CFStringRef viewName
) {
171 CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
173 if (account
->trusted_circle
) {
174 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
175 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer
), viewName
)) {
176 CFSetAddValue(result
, peer
);
184 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccountRef account
, CFStringRef viewName
, SOSBackupSliceKeyBagRef keyBag
, CFErrorRef
*error
) {
185 CFMutableSetRef backupViewSet
= CFSetCreateMutableForCFTypes(NULL
);
187 require_quiet(SecAllocationError(backupViewSet
, error
, CFSTR("No backup view set created")), errOut
);
188 CFSetAddValue(backupViewSet
, viewName
);
190 result
= SOSAccountUpdateBackupRing(account
, viewName
, error
, ^SOSRingRef(SOSRingRef existing
, CFErrorRef
*error
) {
191 SOSRingRef newRing
= NULL
;
192 CFSetRef viewPeerSet
= SOSAccountCopyPeerSetForView(account
, viewName
);
193 CFMutableSetRef cleared
= CFSetCreateMutableForCFTypes(NULL
);
195 SOSRingSetPeerIDs(existing
, cleared
);
196 SOSRingAddAll(existing
, viewPeerSet
);
198 require_quiet(SOSRingSetBackupKeyBag(existing
, SOSAccountGetMyFullPeerInfo(account
), backupViewSet
, keyBag
, error
), exit
);
200 newRing
= CFRetainSafe(existing
);
202 CFReleaseNull(viewPeerSet
);
203 CFReleaseNull(cleared
);
209 if (result
&& NULL
!= error
&& NULL
!= *error
) {
210 secerror("Got Success and Error (dropping error): %@", *error
);
211 CFReleaseNull(*error
);
215 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName
, error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space."));
218 CFReleaseNull(backupViewSet
);
222 bool SOSAccountStartNewBackup(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
)
224 return SOSAccountWithBSKBForView(account
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
225 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
230 bool SOSAccountIsBackupRingEmpty(SOSAccountRef account
, CFStringRef viewName
) {
231 CFStringRef backupRing
= SOSBackupCopyRingNameForView(viewName
);
232 SOSRingRef ring
= SOSAccountGetRing(account
, backupRing
, NULL
);
233 CFReleaseNull(backupRing
);
235 if(ring
) peercnt
= SOSRingCountPeers(ring
);
239 bool SOSAccountUpdatePeerInfo(SOSAccountRef account
, CFStringRef updateDescription
, CFErrorRef
*error
, bool (^update
)(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
)) {
240 if (account
->my_identity
== NULL
)
243 bool result
= update(account
->my_identity
, error
);
245 if (result
&& SOSAccountHasCircle(account
, NULL
)) {
246 return SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle_to_change
) {
247 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription
);
248 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
255 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account
, CFStringRef viewname
){
257 CFErrorRef bsError
= NULL
;
258 CFDataRef backupSliceData
= NULL
;
259 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
261 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account
), viewname
), exit
);
263 CFMutableDictionaryRef trusted_rings
= SOSAccountGetRings(account
, &bsError
);
264 require_quiet(trusted_rings
, exit
);
266 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
267 SOSRingRef ring
= (SOSRingRef
)CFDictionaryGetValue(trusted_rings
, ringName
);
268 CFReleaseNull(ringName
);
270 require_quiet(ring
, exit
);
272 //grab the backup slice from the ring
273 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
274 require_quiet(backupSliceData
, exit
);
276 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
277 require_quiet(backupSlice
, exit
);
279 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
280 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
282 SOSPeerInfoRef myPeerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
283 require_quiet(isSOSPeerInfo(myPeerInBSKB
), exit
);
285 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
286 CFDataRef myPeerInBSKBBK
= SOSPeerInfoCopyBackupKey(myPeerInBSKB
);
287 result
= CFEqualSafe(myBK
, myPeerInBSKBBK
);
289 CFReleaseNull(myPeerInBSKBBK
);
293 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
295 CFReleaseNull(bsError
);
299 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account
, SOSPeerInfoRef testPeer
, CFStringRef viewname
){
301 CFErrorRef bsError
= NULL
;
302 CFDataRef backupSliceData
= NULL
;
303 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
305 require_quiet(testPeer
, exit
);
307 CFMutableDictionaryRef trusted_rings
= SOSAccountGetRings(account
, &bsError
);
308 require_quiet(trusted_rings
, exit
);
310 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
311 SOSRingRef ring
= (SOSRingRef
)CFDictionaryGetValue(trusted_rings
, ringName
);
312 CFReleaseNull(ringName
);
314 require_quiet(ring
, exit
);
316 //grab the backup slice from the ring
317 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
318 require_quiet(backupSliceData
, exit
);
320 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
321 require_quiet(backupSlice
, exit
);
323 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
325 SOSPeerInfoRef peerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, testPeer
);
326 require_quiet(isSOSPeerInfo(peerInBSKB
), exit
);
328 result
= CFEqualSafe(testPeer
, peerInBSKB
);
332 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
334 CFReleaseNull(bsError
);
339 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account
, SOSRingRef oldRing
, CFErrorRef
*error
){
341 CFSetRef viewNames
= SOSBackupRingGetViews(oldRing
, error
);
342 __block CFStringRef viewName
= NULL
;
343 require_quiet(viewNames
, fail
);
344 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames
), error
, CFSTR("Only support single view backup rings")), fail
);
346 CFSetForEach(viewNames
, ^(const void *value
) {
347 if (isString(value
)) {
348 viewName
= CFRetainSafe((CFStringRef
) value
);
352 result
= SOSAccountStartNewBackup(account
, viewName
, error
);
355 CFReleaseNull(viewName
);
359 void SOSAccountForEachBackupRingName(SOSAccountRef account
, void (^operation
)(CFStringRef value
)) {
360 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
362 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
364 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
366 CFSetForEach(myBackupViews
, ^(const void *value
) {
367 CFStringRef viewName
= asString(value
, NULL
);
370 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
372 CFReleaseNull(ringName
);
376 CFReleaseNull(myBackupViews
);
380 void SOSAccountForEachBackupView(SOSAccountRef account
, void (^operation
)(const void *value
)) {
381 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
384 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
386 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
388 CFSetForEach(myBackupViews
, operation
);
390 CFReleaseNull(myBackupViews
);
394 bool SOSAccountSetBackupPublicKey(SOSAccountRef account
, CFDataRef backupKey
, CFErrorRef
*error
)
396 __block
bool result
= false;
398 secnotice("backup", "setting backup public key");
399 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
401 if (CFEqualSafe(backupKey
, account
->backup_key
))
404 CFRetainAssign(account
->backup_key
, backupKey
);
406 SOSAccountEnsureBackupStarts(account
);
412 secnotice("backupkey", "Failed to setup backup public key: %@", error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space provided"));
417 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account
, CFArrayRef retiree
, CFStringRef viewName
, CFErrorRef
*error
,
418 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
419 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
422 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
423 CFMutableSetRef newPeerList
= CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(peers
), peers
);
424 CFArrayForEach(retiree
, ^(const void *value
) {
425 if (!isSOSPeerInfo(value
)) {
426 secerror("Peer list contains a non-peerInfo element");
428 SOSPeerInfoRef retiringPeer
= (SOSPeerInfoRef
)value
;
429 CFStringRef retiringPeerID
= SOSPeerInfoGetPeerID(retiringPeer
);
431 CFSetForEach(newPeerList
, ^(const void *peerFromAccount
) {
432 CFStringRef peerFromAccountID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)peerFromAccount
);
433 if (peerFromAccountID
&& retiringPeerID
&& CFStringCompare(peerFromAccountID
, retiringPeerID
, 0) == 0){
434 CFSetRemoveValue(newPeerList
, peerFromAccount
);
439 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, newPeerList
, error
);
440 CFReleaseNull(newPeerList
);
443 require_quiet(bskb
, exit
);
454 bool SOSAccountRemoveBackupPublickey(SOSAccountRef account
, CFErrorRef
*error
)
456 __block
bool result
= false;
457 __block CFMutableArrayRef removals
= NULL
;
459 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
460 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
461 return SOSFullPeerInfoUpdateBackupKey(fpi
, NULL
, error
);
464 CFReleaseNull(account
->backup_key
);
466 removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
467 CFArrayAppendValue(removals
, SOSAccountGetMyPeerInfo(account
));
469 SOSAccountForEachBackupView(account
, ^(const void *value
) {
470 CFStringRef viewName
= (CFStringRef
)value
;
471 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
472 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
485 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account
, CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
486 __block
bool result
= false;
487 SOSBackupSliceKeyBagRef backup_slice
= NULL
;
489 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
492 result
= SOSSaveV0Keybag(aks_bag
, error
);
493 require_action_quiet(result
, exit
, secnotice("keybag", "failed to set V0 keybag (%@)", *error
));
497 backup_slice
= SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault
, aks_bag
, error
);
499 SOSAccountForEachBackupView(account
, ^(const void *value
) {
500 CFStringRef viewname
= (CFStringRef
) value
;
501 result
&= SOSAccountSetKeybagForViewBackupRing(account
, viewname
, backup_slice
, error
);
506 CFReleaseNull(backup_slice
);
510 static CFMutableArrayRef
SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account
, CFArrayRef peers
, CFSetRef peersInBackup
){
511 CFMutableArrayRef removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
513 CFSetForEach(peersInBackup
, ^(const void *value
) {
514 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
515 CFArrayForEach(peers
, ^(const void *value
) {
516 CFStringRef peerID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)value
);
517 CFStringRef piPeerID
= SOSPeerInfoGetPeerID(peer
);
518 if (peerID
&& piPeerID
&& CFStringCompare(piPeerID
, peerID
, 0) == 0){
519 CFArrayAppendValue(removals
, peer
);
529 bool SOSAccountRemoveBackupPeers(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
*error
){
530 __block
bool result
= true;
532 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
533 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
535 CFSetRef permittedViews
= SOSPeerInfoGetPermittedViews(myPeer
);
536 CFSetForEach(permittedViews
, ^(const void *value
) {
537 CFStringRef viewName
= (CFStringRef
)value
;
538 if(SOSPeerInfoIsViewBackupEnabled(myPeer
, viewName
)){
539 //grab current peers list
540 CFSetRef peersInBackup
= SOSAccountCopyBackupPeersForView(account
, viewName
);
541 //get peer infos that have retired but are still in the backup peer list
542 CFMutableArrayRef removals
= SOSAccountIsRetiredPeerIDInBackupPeerList(account
, peers
, peersInBackup
);
543 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
544 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
554 SOSBackupSliceKeyBagRef
SOSAccountBackupSliceKeyBagForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
* error
){
555 CFMutableDictionaryRef trusted_rings
= NULL
;
556 CFDataRef backupSliceData
= NULL
;
557 CFStringRef ringName
= NULL
;
558 SOSRingRef ring
= NULL
;
559 SOSBackupSliceKeyBagRef bskb
= NULL
;
561 trusted_rings
= SOSAccountGetRings(account
, error
);
562 require_action_quiet(trusted_rings
, exit
, secnotice("keybag", "failed to get trusted rings (%@)", *error
));
564 ringName
= SOSBackupCopyRingNameForView(viewName
);
566 ring
= (SOSRingRef
)CFDictionaryGetValue(trusted_rings
, ringName
);
567 require_action_quiet(ring
, exit
, SOSCreateErrorWithFormat(kSOSErrorNoCircle
, NULL
, error
, NULL
, CFSTR("failed to get ring")));
569 //grab the backup slice from the ring
570 backupSliceData
= SOSRingGetPayload(ring
, error
);
571 require_action_quiet(backupSliceData
, exit
, secnotice("backup", "failed to get backup slice (%@)", *error
));
573 bskb
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, error
);
576 CFReleaseNull(ringName
);