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 static 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 static 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
);
381 void SOSAccountForEachBackupView(SOSAccountRef account
, void (^operation
)(const void *value
)) {
382 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
385 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
387 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
389 CFSetForEach(myBackupViews
, operation
);
391 CFReleaseNull(myBackupViews
);
395 bool SOSAccountSetBackupPublicKey(SOSAccountRef account
, CFDataRef backupKey
, CFErrorRef
*error
)
397 __block
bool result
= false;
399 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
401 if (CFEqualSafe(backupKey
, account
->backup_key
))
404 require_quiet(SOSBSKBIsGoodBackupPublic(backupKey
, error
), exit
);
405 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
406 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
407 return SOSFullPeerInfoUpdateBackupKey(fpi
, backupKey
, error
);
410 CFRetainAssign(account
->backup_key
, backupKey
);
412 CFErrorRef localError
= NULL
;
413 if (!SOSDeleteV0Keybag(&localError
)) {
414 secerror("Failed to delete v0 keybag: %@", localError
);
416 CFReleaseNull(localError
);
420 SOSAccountForEachBackupView(account
, ^(const void *value
) {
421 CFStringRef viewName
= (CFStringRef
)value
;
422 result
&= SOSAccountStartNewBackup(account
, viewName
, error
);
427 secnotice("backupkey", "Failed to setup backup public key: %@", error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space provided"));
432 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account
, CFArrayRef retiree
, CFStringRef viewName
, CFErrorRef
*error
,
433 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
434 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
437 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
438 CFMutableSetRef newPeerList
= CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(peers
), peers
);
439 CFArrayForEach(retiree
, ^(const void *value
) {
440 if (!isSOSPeerInfo(value
)) {
441 secerror("Peer list contains a non-peerInfo element");
443 SOSPeerInfoRef retiringPeer
= (SOSPeerInfoRef
)value
;
444 CFStringRef retiringPeerID
= SOSPeerInfoGetPeerID(retiringPeer
);
446 CFSetForEach(newPeerList
, ^(const void *peerFromAccount
) {
447 CFStringRef peerFromAccountID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)peerFromAccount
);
448 if (peerFromAccountID
&& retiringPeerID
&& CFStringCompare(peerFromAccountID
, retiringPeerID
, 0) == 0){
449 CFSetRemoveValue(newPeerList
, peerFromAccount
);
454 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, newPeerList
, error
);
455 CFReleaseNull(newPeerList
);
458 require_quiet(bskb
, exit
);
469 bool SOSAccountRemoveBackupPublickey(SOSAccountRef account
, CFErrorRef
*error
)
471 __block
bool result
= false;
472 __block CFMutableArrayRef removals
= NULL
;
474 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
475 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
476 return SOSFullPeerInfoUpdateBackupKey(fpi
, NULL
, error
);
479 CFReleaseNull(account
->backup_key
);
481 removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
482 CFArrayAppendValue(removals
, SOSAccountGetMyPeerInfo(account
));
484 SOSAccountForEachBackupView(account
, ^(const void *value
) {
485 CFStringRef viewName
= (CFStringRef
)value
;
486 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
487 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
500 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account
, CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
501 __block
bool result
= false;
502 SOSBackupSliceKeyBagRef backup_slice
= NULL
;
504 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
507 result
= SOSSaveV0Keybag(aks_bag
, error
);
508 require_action_quiet(result
, exit
, secnotice("keybag", "failed to set V0 keybag (%@)", *error
));
512 backup_slice
= SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault
, aks_bag
, error
);
514 SOSAccountForEachBackupView(account
, ^(const void *value
) {
515 CFStringRef viewname
= (CFStringRef
) value
;
516 result
&= SOSAccountSetKeybagForViewBackupRing(account
, viewname
, backup_slice
, error
);
521 CFReleaseNull(backup_slice
);
525 static CFMutableArrayRef
SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account
, CFArrayRef peers
, CFSetRef peersInBackup
){
526 CFMutableArrayRef removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
528 CFSetForEach(peersInBackup
, ^(const void *value
) {
529 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
530 CFArrayForEach(peers
, ^(const void *value
) {
531 CFStringRef peerID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)value
);
532 CFStringRef piPeerID
= SOSPeerInfoGetPeerID(peer
);
533 if (peerID
&& piPeerID
&& CFStringCompare(piPeerID
, peerID
, 0) == 0){
534 CFArrayAppendValue(removals
, peer
);
544 bool SOSAccountRemoveBackupPeers(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
*error
){
545 __block
bool result
= true;
547 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
548 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
550 CFSetRef permittedViews
= SOSPeerInfoGetPermittedViews(myPeer
);
551 CFSetForEach(permittedViews
, ^(const void *value
) {
552 CFStringRef viewName
= (CFStringRef
)value
;
553 if(SOSPeerInfoIsViewBackupEnabled(myPeer
, viewName
)){
554 //grab current peers list
555 CFSetRef peersInBackup
= SOSAccountCopyBackupPeersForView(account
, viewName
);
556 //get peer infos that have retired but are still in the backup peer list
557 CFMutableArrayRef removals
= SOSAccountIsRetiredPeerIDInBackupPeerList(account
, peers
, peersInBackup
);
558 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
559 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);