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 SOSCircleForEachActiveValidPeer(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
&& *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 static bool SOSAccountUpdatePeerInfo(SOSAccountRef account
, CFStringRef updateDescription
, CFErrorRef
*error
, bool (^update
)(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
)) {
231 if (account
->my_identity
== NULL
)
234 bool result
= update(account
->my_identity
, error
);
236 if (result
&& SOSAccountHasCircle(account
, NULL
)) {
237 return SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle_to_change
) {
238 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription
);
239 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
246 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account
, CFStringRef viewname
){
248 CFErrorRef bsError
= NULL
;
249 CFDataRef backupSliceData
= NULL
;
250 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
252 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account
), viewname
), exit
);
254 CFMutableDictionaryRef trusted_rings
= SOSAccountGetRings(account
, &bsError
);
255 require_quiet(trusted_rings
, exit
);
257 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
258 SOSRingRef ring
= (SOSRingRef
)CFDictionaryGetValue(trusted_rings
, ringName
);
259 CFReleaseNull(ringName
);
261 require_quiet(ring
, exit
);
263 //grab the backup slice from the ring
264 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
265 require_quiet(backupSliceData
, exit
);
267 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
268 require_quiet(backupSlice
, exit
);
270 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
271 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
273 SOSPeerInfoRef myPeerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
274 require_quiet(isSOSPeerInfo(myPeerInBSKB
), exit
);
276 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
277 CFDataRef myPeerInBSKBBK
= SOSPeerInfoCopyBackupKey(myPeerInBSKB
);
278 result
= CFEqualSafe(myBK
, myPeerInBSKBBK
);
280 CFReleaseNull(myPeerInBSKB
);
284 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
286 CFReleaseNull(bsError
);
290 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account
, SOSPeerInfoRef testPeer
, CFStringRef viewname
){
292 CFErrorRef bsError
= NULL
;
293 CFDataRef backupSliceData
= NULL
;
294 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
296 require_quiet(testPeer
, exit
);
298 CFMutableDictionaryRef trusted_rings
= SOSAccountGetRings(account
, &bsError
);
299 require_quiet(trusted_rings
, exit
);
301 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
302 SOSRingRef ring
= (SOSRingRef
)CFDictionaryGetValue(trusted_rings
, ringName
);
303 CFReleaseNull(ringName
);
305 require_quiet(ring
, exit
);
307 //grab the backup slice from the ring
308 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
309 require_quiet(backupSliceData
, exit
);
311 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
312 require_quiet(backupSlice
, exit
);
314 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
316 SOSPeerInfoRef peerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, testPeer
);
317 require_quiet(isSOSPeerInfo(peerInBSKB
), exit
);
319 result
= CFEqualSafe(testPeer
, peerInBSKB
);
323 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
325 CFReleaseNull(bsError
);
330 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account
, SOSRingRef oldRing
, CFErrorRef
*error
){
332 CFSetRef viewNames
= SOSBackupRingGetViews(oldRing
, error
);
333 __block CFStringRef viewName
= NULL
;
334 require_quiet(viewNames
, fail
);
335 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames
), error
, CFSTR("Only support single view backup rings")), fail
);
337 CFSetForEach(viewNames
, ^(const void *value
) {
338 if (isString(value
)) {
339 viewName
= CFRetainSafe((CFStringRef
) value
);
343 result
= SOSAccountStartNewBackup(account
, viewName
, error
);
346 CFReleaseNull(viewName
);
350 void SOSAccountForEachBackupRingName(SOSAccountRef account
, void (^operation
)(CFStringRef value
)) {
351 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
353 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
355 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
357 CFSetForEach(myBackupViews
, ^(const void *value
) {
358 CFStringRef viewName
= asString(value
, NULL
);
361 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
363 CFReleaseNull(ringName
);
367 CFReleaseNull(myBackupViews
);
372 void SOSAccountForEachBackupView(SOSAccountRef account
, void (^operation
)(const void *value
)) {
373 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
376 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
378 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
380 CFSetForEach(myBackupViews
, operation
);
382 CFReleaseNull(myBackupViews
);
386 bool SOSAccountSetBackupPublicKey(SOSAccountRef account
, CFDataRef backupKey
, CFErrorRef
*error
)
388 __block
bool result
= false;
390 #if !ENABLE_V2_BACKUP
391 // We should fill in an error, but exit does it for us.
395 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
397 if (CFEqualSafe(backupKey
, account
->backup_key
))
400 require_quiet(SOSBSKBIsGoodBackupPublic(backupKey
, error
), exit
);
401 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
402 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
403 return SOSFullPeerInfoUpdateBackupKey(fpi
, backupKey
, error
);
406 CFRetainAssign(account
->backup_key
, backupKey
);
408 CFErrorRef localError
= NULL
;
409 if (!SOSDeleteV0Keybag(&localError
)) {
410 secerror("Failed to delete v0 keybag: %@", localError
);
412 CFReleaseNull(localError
);
416 SOSAccountForEachBackupView(account
, ^(const void *value
) {
417 CFStringRef viewName
= (CFStringRef
)value
;
418 result
&= SOSAccountStartNewBackup(account
, viewName
, error
);
423 secnotice("backupkey", "Failed to setup backup public key: %@", error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space provided"));
428 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account
, CFArrayRef retiree
, CFStringRef viewName
, CFErrorRef
*error
,
429 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
430 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
433 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
434 CFMutableSetRef newPeerList
= CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(peers
), peers
);
435 CFArrayForEach(retiree
, ^(const void *value
) {
436 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
437 CFSetRemoveValue(newPeerList
, peer
);
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 includeV0
, 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
));
499 #if !ENABLE_V2_BACKUP
500 // We always succeed when no V2 backup.
504 backup_slice
= SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault
, aks_bag
, error
);
506 SOSAccountForEachBackupView(account
, ^(const void *value
) {
507 CFStringRef viewname
= (CFStringRef
) value
;
508 result
&= SOSAccountSetKeybagForViewBackupRing(account
, viewname
, backup_slice
, error
);
512 CFReleaseNull(backup_slice
);
516 static CFMutableArrayRef
SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account
, CFArrayRef peerIDs
, CFSetRef peersInBackup
){
517 CFMutableArrayRef removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
519 CFSetForEach(peersInBackup
, ^(const void *value
) {
520 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
521 CFArrayForEach(peerIDs
, ^(const void *value
) {
522 CFStringRef peerID
= (CFStringRef
)value
;
523 CFStringRef piPeerID
= SOSPeerInfoGetPeerID(peer
);
524 if (piPeerID
&& CFStringCompare(piPeerID
, peerID
, 0) == 0){
525 CFArrayAppendValue(removals
, peer
);
535 bool SOSAccountRemoveBackupPeers(SOSAccountRef account
, CFArrayRef peerIDs
, CFErrorRef
*error
){
536 __block
bool result
= true;
538 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
539 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
541 CFSetRef permittedViews
= SOSPeerInfoGetPermittedViews(myPeer
);
542 CFSetForEach(permittedViews
, ^(const void *value
) {
543 CFStringRef viewName
= (CFStringRef
)value
;
544 if(SOSPeerInfoIsViewBackupEnabled(myPeer
, viewName
)){
545 //grab current peers list
546 CFSetRef peersInBackup
= SOSAccountCopyBackupPeersForView(account
, viewName
);
547 //get peer infos that have retired but are still in the backup peer list
548 CFMutableArrayRef removals
= SOSAccountIsRetiredPeerIDInBackupPeerList(account
, peerIDs
, peersInBackup
);
549 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
550 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);