5 // Created by Richard Murphy on 2/23/15.
10 #include "SOSRingTypes.h"
11 #include "SOSRingBasic.h"
12 #include "SOSRingBackup.h"
13 #include <Security/SecureObjectSync/SOSAccountPriv.h>
14 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
16 static ringFuncs ringTypes
[] = {
20 static const size_t typecount
= sizeof(ringTypes
) / sizeof(ringFuncs
);
22 static bool SOSRingValidType(SOSRingType type
) {
23 return type
< typecount
;
26 // MARK: Exported Functions
29 SOSRingRef
SOSRingCreate(CFStringRef name
, CFStringRef myPeerID
, SOSRingType type
, CFErrorRef
*error
) {
30 require(SOSRingValidType(type
), errOut
);
31 require(ringTypes
[type
]->sosRingCreate
, errOut
);
32 return ringTypes
[type
]->sosRingCreate(name
, myPeerID
, error
);
34 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
38 bool SOSRingResetToEmpty(SOSRingRef ring
, CFStringRef myPeerID
, CFErrorRef
*error
) {
39 SOSRingAssertStable(ring
);
40 SOSRingType type
= SOSRingGetType(ring
);
41 require(SOSRingValidType(type
), errOut
);
42 require(ringTypes
[type
]->sosRingResetToEmpty
, errOut
);
43 return ringTypes
[type
]->sosRingResetToEmpty(ring
, myPeerID
, error
);
45 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
49 bool SOSRingResetToOffering(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
50 SOSRingAssertStable(ring
);
51 SOSRingType type
= SOSRingGetType(ring
);
52 require(SOSRingValidType(type
), errOut
);
53 require(ringTypes
[type
]->sosRingResetToOffering
, errOut
);
54 return ringTypes
[type
]->sosRingResetToOffering(ring
, user_privkey
, requestor
, error
);
56 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
60 SOSRingStatus
SOSRingDeviceIsInRing(SOSRingRef ring
, CFStringRef peerID
) {
61 SOSRingAssertStable(ring
);
62 SOSRingType type
= SOSRingGetType(ring
);
63 require(SOSRingValidType(type
), errOut
);
64 require(ringTypes
[type
]->sosRingDeviceIsInRing
, errOut
);
65 return ringTypes
[type
]->sosRingDeviceIsInRing(ring
, peerID
);
70 bool SOSRingApply(SOSRingRef ring
, SecKeyRef user_pubkey
, SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
71 SOSRingAssertStable(ring
);
72 SOSRingType type
= SOSRingGetType(ring
);
73 require(SOSRingValidType(type
), errOut
);
74 require(ringTypes
[type
]->sosRingApply
, shortCircuit
);
75 require_quiet(SOSPeerInfoApplicationVerify(SOSFullPeerInfoGetPeerInfo(fpi
), user_pubkey
, error
), errOut2
);
77 return ringTypes
[type
]->sosRingApply(ring
, user_pubkey
, fpi
, error
);
79 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
82 SOSCreateError(kSOSErrorBadSignature
, CFSTR("FullPeerInfo fails userkey signature check"), NULL
, error
);
88 bool SOSRingWithdraw(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
89 SOSRingAssertStable(ring
);
90 SOSRingType type
= SOSRingGetType(ring
);
91 require(SOSRingValidType(type
), errOut
);
92 require(ringTypes
[type
]->sosRingWithdraw
, shortCircuit
);
93 return ringTypes
[type
]->sosRingWithdraw(ring
, user_privkey
, requestor
, error
);
95 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
101 bool SOSRingGenerationSign(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
102 SOSRingAssertStable(ring
);
103 SOSRingType type
= SOSRingGetType(ring
);
104 require(SOSRingValidType(type
), errOut
);
105 require(ringTypes
[type
]->sosRingGenerationSign
, shortCircuit
);
106 return ringTypes
[type
]->sosRingGenerationSign(ring
, user_privkey
, requestor
, error
);
108 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
114 bool SOSRingConcordanceSign(SOSRingRef ring
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
115 SOSRingAssertStable(ring
);
116 SOSRingType type
= SOSRingGetType(ring
);
117 require(SOSRingValidType(type
), errOut
);
118 require(ringTypes
[type
]->sosRingConcordanceSign
, shortCircuit
);
119 return ringTypes
[type
]->sosRingConcordanceSign(ring
, requestor
, error
);
121 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
127 SOSConcordanceStatus
SOSRingConcordanceTrust(SOSFullPeerInfoRef me
, CFSetRef peers
,
128 SOSRingRef knownRing
, SOSRingRef proposedRing
,
129 SecKeyRef knownPubkey
, SecKeyRef userPubkey
,
130 CFStringRef excludePeerID
, CFErrorRef
*error
) {
131 SOSRingAssertStable(knownRing
);
132 SOSRingAssertStable(proposedRing
);
133 SOSRingType type1
= SOSRingGetType(knownRing
);
134 SOSRingType type2
= SOSRingGetType(proposedRing
);
135 require(SOSRingValidType(type1
), errOut
);
136 require(SOSRingValidType(type2
), errOut
);
137 require(type1
== type2
, errOut
);
139 secnotice("ring", "concordance trust (%s) knownRing: %@ proposedRing: %@ knownkey: %@ userkey: %@ excluded: %@",
140 ringTypes
[type1
]->typeName
, knownRing
, proposedRing
, knownPubkey
, userPubkey
, excludePeerID
);
142 require(ringTypes
[type1
]->sosRingConcordanceTrust
, errOut
);
143 return ringTypes
[type1
]->sosRingConcordanceTrust(me
, peers
, knownRing
, proposedRing
, knownPubkey
, userPubkey
, excludePeerID
, error
);
145 return kSOSConcordanceError
;
148 bool SOSRingAccept(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
149 SOSRingAssertStable(ring
);
150 SOSRingType type
= SOSRingGetType(ring
);
151 require(SOSRingValidType(type
), errOut
);
152 require(ringTypes
[type
]->sosRingAccept
, shortCircuit
);
153 return ringTypes
[type
]->sosRingAccept(ring
, user_privkey
, requestor
, error
);
155 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
161 bool SOSRingReject(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
162 SOSRingAssertStable(ring
);
163 SOSRingType type
= SOSRingGetType(ring
);
164 require(SOSRingValidType(type
), errOut
);
165 require(ringTypes
[type
]->sosRingReject
, shortCircuit
);
166 return ringTypes
[type
]->sosRingReject(ring
, user_privkey
, requestor
, error
);
168 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
174 bool SOSRingSetPayload(SOSRingRef ring
, SecKeyRef user_privkey
, CFDataRef payload
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
175 SOSRingAssertStable(ring
);
176 SOSRingType type
= SOSRingGetType(ring
);
177 require(SOSRingValidType(type
), errOut
);
178 require(ringTypes
[type
]->sosRingSetPayload
, errOut
);
179 return ringTypes
[type
]->sosRingSetPayload(ring
, user_privkey
, payload
, requestor
, error
);
181 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
185 CFDataRef
SOSRingGetPayload(SOSRingRef ring
, CFErrorRef
*error
) {
186 SOSRingAssertStable(ring
);
187 SOSRingType type
= SOSRingGetType(ring
);
188 require(SOSRingValidType(type
), errOut
);
189 require(ringTypes
[type
]->sosRingGetPayload
, errOut
);
190 return ringTypes
[type
]->sosRingGetPayload(ring
, error
);
192 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
196 CFSetRef
SOSRingGetBackupViewset(SOSRingRef ring
, CFErrorRef
*error
) {
197 SOSRingAssertStable(ring
);
198 SOSRingType type
= SOSRingGetType(ring
);
199 require(kSOSRingBackup
== type
, errOut
);
200 return SOSRingGetBackupViewset_Internal(ring
);
202 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not backup ring type"), NULL
, error
);
206 static bool isBackupRing(SOSRingRef ring
, CFErrorRef
*error
) {
207 SOSRingType type
= SOSRingGetType(ring
);
208 require_quiet(kSOSRingBackup
== type
, errOut
);
211 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not backup ring type"), NULL
, error
);
215 bool SOSRingSetBackupKeyBag(SOSRingRef ring
, SOSFullPeerInfoRef fpi
, CFSetRef viewSet
, SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
216 SOSRingAssertStable(ring
);
217 CFDataRef bskb_as_data
= NULL
;
219 require_quiet(isBackupRing(ring
, error
), errOut
);
221 bskb_as_data
= SOSBSKBCopyEncoded(bskb
, error
);
222 result
= bskb_as_data
&&
223 SOSRingSetBackupViewset_Internal(ring
, viewSet
) &&
224 SOSRingSetPayload(ring
, NULL
, bskb_as_data
, fpi
, error
);
226 CFReleaseNull(bskb_as_data
);
230 SOSBackupSliceKeyBagRef
SOSRingCopyBackupSliceKeyBag(SOSRingRef ring
, CFErrorRef
*error
) {
231 SOSRingAssertStable(ring
);
233 CFDataRef bskb_as_data
= NULL
;
234 SOSBackupSliceKeyBagRef result
= NULL
;
235 require_quiet(isBackupRing(ring
, error
), errOut
);
237 bskb_as_data
= SOSRingGetPayload(ring
, error
);
238 require_quiet(bskb_as_data
, errOut
);
240 result
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, bskb_as_data
, error
);
247 bool SOSRingPKTrusted(SOSRingRef ring
, SecKeyRef pubkey
, CFErrorRef
*error
) {
248 SOSRingAssertStable(ring
);
249 SOSRingType type
= SOSRingGetType(ring
);
250 require(SOSRingValidType(type
), errOut
);
251 return SOSRingVerify(ring
, pubkey
, error
);
253 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Not valid ring type"), NULL
, error
);
257 bool SOSRingPeerTrusted(SOSRingRef ring
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
259 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(requestor
);
260 SecKeyRef pubkey
= SOSPeerInfoCopyPubKey(pi
, error
);
261 require_quiet(pubkey
, exit
);
262 retval
= SOSRingPKTrusted(ring
, pubkey
, error
);
264 CFReleaseNull(pubkey
);
271 bool SOSAccountKnowsRings(SOSAccountRef account
, CFErrorRef
*error
) {
272 if(account
->rings
) return true;
273 SOSCreateError(kSOSErrorUnsupported
, CFSTR("This account doesn't support rings"), NULL
, error
);
279 bool SOSRingRequirementKnown(SOSAccountRef account
, CFStringRef name
, CFErrorRef
*error
) {
281 require_quiet(SOSAccountKnowsRings(account
, error
), errOut
);
282 retval
= CFDictionaryContainsValue(account
->rings
, name
);
287 bool SOSRingRequirementCreate(SOSAccountRef account
, CFStringRef name
, SOSRingType type
, CFErrorRef
*error
) {
288 if(account
->rings
) return false;
289 if(CFDictionaryContainsValue(account
->rings
, name
)) return true;
290 if(!SOSRingValidType(type
)) return false;
291 SOSRingRef ring
= SOSRingCreate(NULL
, name
, type
, error
);
292 if(!ring
) return false;
293 CFDictionaryAddValue(account
->rings
, name
, ring
);
297 static SOSRingRef
getRingFromAccount(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
298 SOSRingRef retval
= NULL
;
299 require_quiet(SOSAccountKnowsRings(account
, error
), errOut
);
300 retval
= (SOSRingRef
) CFDictionaryGetValue(account
->rings
, name
);
308 bool SOSRingRequirementResetToOffering(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
309 SOSRingRef ring
= getRingFromAccount(account
, name
, error
);
310 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
311 require_action_quiet(ring
, errOut
,
312 SOSCreateError(kSOSErrorNoCircle
, CFSTR("No ring by name specified"), NULL
, error
));
313 switch(SOSRingGetType(ring
)) {
316 SOSRingResetToOffering(ring
, account
->__user_private
, fpi
, error
);
323 bool SOSRingRequirementResetToEmpty(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
330 bool SOSRingRequirementRequestToJoin(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
334 bool SOSRingRequirementRemoveThisDevice(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
339 CFArrayRef
SOSRingRequirementGetApplicants(SOSAccountRef account
, CFStringRef name
, CFErrorRef
* error
) {
344 bool SOSRingRequirementAcceptApplicants(SOSAccountRef account
, CFStringRef name
, CFArrayRef applicants
, CFErrorRef
* error
) {
349 bool SOSRingRequirementRejectApplicants(SOSAccountRef account
, CFStringRef name
, CFArrayRef applicants
, CFErrorRef
*error
) {
354 bool SOSRingResetToOffering(SOSCircleRef circle
, SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
){
356 return SOSRingResetToEmpty(ring
, error
)
357 && SOSRingRequestAdmission(ring
, user_privkey
, requestor
, error
)
358 && SOSRingAcceptRequest(ring
, user_privkey
, requestor
, SOSFullPeerInfoGetPeerInfo(requestor
), error
);
362 static bool SOSRingRecordAdmissionRequest(SOSRingRef ring
, SecKeyRef user_pubkey
, CFStringRef peerID
, CFErrorRef
*error
) {
363 SOSRingAssertStable(ring
);
365 bool isPeer
= SOSRingHasPeerWithID(ring
, peerID
, error
);
366 require_action_quiet(!isPeer
, fail
, SOSCreateError(kSOSErrorAlreadyPeer
, CFSTR("Cannot request admission when already a peer"), NULL
, error
));
367 CFSetRemoveValue(ring
->rejections
, requestorPeerInfo
); // We remove from rejected list, in case?
368 CFSetSetValue(ring
->applicants
, requestorPeerInfo
);
377 bool SOSRingRequestReadmission(SOSRingRef ring
, SecKeyRef user_pubkey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
378 bool success
= false;
380 require_quiet(SOSPeerInfoApplicationVerify(peer
, user_pubkey
, error
), fail
);
381 success
= SOSRingRecordAdmissionRequest(ring
, user_pubkey
, peer
, error
);
386 bool SOSRingRequestAdmission(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, CFErrorRef
*error
) {
387 bool success
= false;
389 SecKeyRef user_pubkey
= SecKeyCreatePublicFromPrivate(user_privkey
);
390 require_action_quiet(user_pubkey
, fail
, SOSCreateError(kSOSErrorBadKey
, CFSTR("No public key for key"), NULL
, error
));
392 require(SOSFullPeerInfoPromoteToApplication(requestor
, user_privkey
, error
), fail
);
394 success
= SOSRingRecordAdmissionRequest(ring
, user_pubkey
, SOSFullPeerInfoGetPeerInfo(requestor
), error
);
396 CFReleaseNull(user_pubkey
);
401 bool SOSRingRemovePeer(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef requestor
, SOSPeerInfoRef peer_to_remove
, CFErrorRef
*error
) {
402 SOSPeerInfoRef requestor_peer_info
= SOSFullPeerInfoGetPeerInfo(requestor
);
404 if (SOSRingHasApplicant(ring
, peer_to_remove
, error
)) {
405 return SOSRingRejectRequest(ring
, requestor
, peer_to_remove
, error
);
408 if (!SOSRingHasPeer(ring
, requestor_peer_info
, error
)) {
409 SOSCreateError(kSOSErrorAlreadyPeer
, CFSTR("Must be peer to remove peer"), NULL
, error
);
413 CFSetRemoveValue(ring
->peers
, peer_to_remove
);
415 SOSRingGenerationSign(ring
, user_privkey
, requestor
, error
);
420 bool SOSRingAcceptRequest(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef device_approver
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
421 SOSRingAssertStable(ring
);
423 SecKeyRef publicKey
= NULL
;
426 require_action_quiet(CFSetContainsValue(ring
->applicants
, peerInfo
), fail
,
427 SOSCreateError(kSOSErrorNotApplicant
, CFSTR("Cannot accept non-applicant"), NULL
, error
));
429 publicKey
= SecKeyCreatePublicFromPrivate(user_privkey
);
430 require_quiet(SOSPeerInfoApplicationVerify(peerInfo
, publicKey
, error
), fail
);
432 CFSetRemoveValue(ring
->applicants
, peerInfo
);
433 CFSetSetValue(ring
->peers
, peerInfo
);
435 result
= SOSRingGenerationSign(ring
, user_privkey
, device_approver
, error
);
436 secnotice("ring", "Accepted %@", peerInfo
);
439 CFReleaseNull(publicKey
);
443 bool SOSRingWithdrawRequest(SOSRingRef ring
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
444 SOSRingAssertStable(ring
);
446 CFSetRemoveValue(ring
->applicants
, peerInfo
);
451 bool SOSRingRemoveRejectedPeer(SOSRingRef ring
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
452 SOSRingAssertStable(ring
);
454 CFSetRemoveValue(ring
->rejected_applicants
, peerInfo
);
460 bool SOSRingRejectRequest(SOSRingRef ring
, SOSFullPeerInfoRef device_rejector
,
461 SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
462 SOSRingAssertStable(ring
);
464 if (CFEqual(SOSPeerInfoGetPeerID(peerInfo
), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector
))))
465 return SOSRingWithdrawRequest(ring
, peerInfo
, error
);
467 if (!CFSetContainsValue(ring
->applicants
, peerInfo
)) {
468 SOSCreateError(kSOSErrorNotApplicant
, CFSTR("Cannot reject non-applicant"), NULL
, error
);
472 CFSetRemoveValue(ring
->applicants
, peerInfo
);
473 CFSetSetValue(ring
->rejected_applicants
, peerInfo
);
475 // TODO: Maybe we sign the rejection with device_rejector.
480 bool SOSRingAcceptRequests(SOSRingRef ring
, SecKeyRef user_privkey
, SOSFullPeerInfoRef device_approver
,
482 // Returns true if we accepted someone and therefore have to post the ring back to KVS
483 __block
bool result
= false;
485 SOSRingForEachApplicant(ring
, ^(SOSPeerInfoRef peer
) {
486 if (!SOSRingAcceptRequest(ring
, user_privkey
, device_approver
, peer
, error
))
487 printf("error in SOSRingAcceptRequest\n");
489 secnotice("ring", "Accepted peer: %@", peer
);
495 SOSRingGenerationSign(ring
, user_privkey
, device_approver
, error
);
496 secnotice("ring", "Countersigned accepted requests");
502 bool SOSRingPeerSigUpdate(SOSRingRef ring
, SecKeyRef userPrivKey
, SOSFullPeerInfoRef fpi
,
504 // Returns true if we accepted someone and therefore have to post the ring back to KVS
505 __block
bool result
= false;
506 SecKeyRef userPubKey
= SecKeyCreatePublicFromPrivate(userPrivKey
);
508 // We're going to remove any applicants using a mismatched user key.
509 SOSRingForEachApplicant(ring
, ^(SOSPeerInfoRef peer
) {
510 if(!SOSPeerInfoApplicationVerify(peer
, userPubKey
, NULL
)) {
511 if(!SOSRingRejectRequest(ring
, fpi
, peer
, NULL
)) {
517 result
= SOSRingUpdatePeerInfo(ring
, SOSFullPeerInfoGetPeerInfo(fpi
));
520 SOSRingGenerationSign(ring
, userPrivKey
, fpi
, error
);
521 secnotice("ring", "Generation signed updated signatures on peerinfo");
528 SOSFullPeerInfoRef
SOSRingGetiCloudFullPeerInfoRef(SOSCircleRef circle
, SOSRingRef ring
) {
529 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
530 SOSRingForEachActivePeer(circle
, ring
, ^(SOSPeerInfoRef peer
) {
531 if (SOSPeerInfoIsCloudIdentity(peer
)) {
532 if (cloud_full_peer
== NULL
) {
533 CFErrorRef localError
= NULL
;
534 cloud_full_peer
= SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault
, peer
, &localError
);
537 secerror("Found cloud peer in ring but can't make full peer: %@", localError
);
538 CFReleaseNull(localError
);
542 secerror("More than one cloud identity found in ring: %@", ring
);
546 return cloud_full_peer
;
550 CFMutableArrayRef
SOSRingCopyConcurringPeers(SOSRingRef ring
, CFErrorRef
* error
) {
551 SOSRingAssertStable(ring
);
553 CFMutableArrayRef concurringPeers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
555 if (!SOSRingAppendConcurringPeers(ring
, concurringPeers
, error
))
556 CFReleaseNull(concurringPeers
);
558 return concurringPeers
;
562 bool SOSRingAppendConcurringPeers(SOSCircleRef circle
, SOSRingRef ring
, CFMutableArrayRef appendHere
, CFErrorRef
*error
) {
563 SOSRingForEachActivePeer(circle
, ring
, ^(SOSPeerInfoRef peer
) {
564 CFErrorRef localError
= NULL
;
565 if (SOSRingVerifyPeerSigned(ring
, peer
, &localError
)) {
566 CFArrayAppendValue(appendHere
, peer
);
567 } else if (error
!= NULL
) {
568 secerror("Error checking concurrence: %@", localError
);
570 CFReleaseNull(localError
);