]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSCircle.c
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSCircle.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 * SOSCircle.c - Implementation of the secure object syncing transport
27 */
28
29 #include <AssertMacros.h>
30
31 #include <CoreFoundation/CFArray.h>
32 #include "keychain/SecureObjectSync/SOSTypes.h"
33 #include "keychain/SecureObjectSync/SOSPeerInfo.h"
34 #include "keychain/SecureObjectSync/SOSPeer.h"
35 #include "keychain/SecureObjectSync/SOSCircle.h"
36 #include "keychain/SecureObjectSync/SOSCloudCircle.h"
37 #include "keychain/SecureObjectSync/SOSCloudCircleInternal.h"
38 #include "keychain/SecureObjectSync/SOSInternal.h"
39 #include "keychain/SecureObjectSync/SOSEnginePriv.h"
40 #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
41 #include "keychain/SecureObjectSync/SOSGenCount.h"
42 #include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Security/SecFramework.h>
45
46 #include <Security/SecKey.h>
47 #include <Security/SecKeyPriv.h>
48 #include <utilities/SecBuffer.h>
49
50 #include <utilities/SecCFWrappers.h>
51 #include <utilities/SecCFError.h>
52
53 #include "keychain/SecureObjectSync/SOSCirclePriv.h"
54
55 //#include "ckdUtilities.h"
56
57 #include <corecrypto/ccder.h>
58 #include <corecrypto/ccdigest.h>
59 #include <corecrypto/ccsha2.h>
60
61 #include <stdlib.h>
62 #include <assert.h>
63
64 CFGiblisWithCompareFor(SOSCircle);
65
66 SOSCircleRef SOSCircleCreate(CFAllocatorRef allocator, CFStringRef name, CFErrorRef *error) {
67 SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
68 assert(name);
69
70 c->name = CFStringCreateCopy(allocator, name);
71 c->generation = SOSGenerationCreate();
72 c->peers = CFSetCreateMutableForSOSPeerInfosByID(allocator);
73 c->applicants = CFSetCreateMutableForSOSPeerInfosByID(allocator);
74 c->rejected_applicants = CFSetCreateMutableForSOSPeerInfosByID(allocator);
75 c->signatures = CFDictionaryCreateMutableForCFTypes(allocator);
76 return c;
77 }
78
79 static CFMutableSetRef CFSetOfPeerInfoDeepCopy(CFAllocatorRef allocator, CFSetRef peerInfoSet)
80 {
81 __block CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator);
82 CFSetForEach(peerInfoSet, ^(const void *value) {
83 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
84 CFErrorRef localError = NULL;
85 SOSPeerInfoRef copiedPeer = SOSPeerInfoCreateCopy(allocator, pi, &localError);
86 if (copiedPeer) {
87 CFSetAddValue(result, copiedPeer);
88 } else {
89 secerror("Failed to copy peer: %@ (%@)", pi, localError);
90 }
91 CFReleaseSafe(copiedPeer);
92 CFReleaseSafe(localError);
93 });
94 return result;
95 }
96
97 SOSCircleRef SOSCircleCopyCircle(CFAllocatorRef allocator, SOSCircleRef otherCircle, CFErrorRef *error)
98 {
99 SOSCircleRef c = CFTypeAllocate(SOSCircle, struct __OpaqueSOSCircle, allocator);
100
101 assert(otherCircle);
102 c->name = CFStringCreateCopy(allocator, otherCircle->name);
103 c->generation = SOSGenerationCopy(otherCircle->generation);
104
105 c->peers = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->peers);
106 c->applicants = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->applicants);
107 c->rejected_applicants = CFSetOfPeerInfoDeepCopy(allocator, otherCircle->rejected_applicants);
108
109 c->signatures = CFDictionaryCreateMutableCopy(allocator, 0, otherCircle->signatures);
110
111 return c;
112 }
113
114 static Boolean SOSCircleCompare(CFTypeRef lhs, CFTypeRef rhs) {
115 if (CFGetTypeID(lhs) != SOSCircleGetTypeID()
116 || CFGetTypeID(rhs) != SOSCircleGetTypeID())
117 return false;
118
119 SOSCircleRef left = SOSCircleConvertAndAssertStable(lhs);
120 SOSCircleRef right = SOSCircleConvertAndAssertStable(rhs);
121
122 // TODO: we should be doing set equality for peers and applicants.
123 return NULL != left && NULL != right
124 && CFEqualSafe(left->generation, right->generation)
125 && SOSPeerInfoSetContainsIdenticalPeers(left->peers, right->peers)
126 && SOSPeerInfoSetContainsIdenticalPeers(left->applicants, right->applicants)
127 && SOSPeerInfoSetContainsIdenticalPeers(left->rejected_applicants, right->rejected_applicants)
128 && CFEqualSafe(left->signatures, right->signatures);
129 }
130
131 static CFMutableArrayRef CFSetCopyValuesCFArray(CFSetRef set)
132 {
133 CFIndex count = CFSetGetCount(set);
134
135 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
136 if (count > 0) {
137 const void * values[count];
138 CFSetGetValues(set, values);
139 for (int current = 0; current < count; ++current) {
140 CFArrayAppendValue(result, values[current]);
141 }
142 }
143
144 return result;
145 }
146
147 static bool SOSCircleDigestArray(const struct ccdigest_info *di, CFMutableArrayRef array, void *hash_result, CFErrorRef *error)
148 {
149 __block bool success = true;
150 ccdigest_di_decl(di, array_digest);
151 void * a_digest = (void * )array_digest;
152
153 ccdigest_init(di, array_digest);
154 CFArraySortValues(array, CFRangeMake(0, CFArrayGetCount(array)), SOSPeerInfoCompareByID, (void *)SOSPeerCmpPubKeyHash);
155 CFArrayForEach(array, ^(const void *peer) {
156 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes((SOSPeerInfoRef)peer, di, a_digest, error))
157 success = false;
158 });
159 ccdigest_final(di, array_digest, hash_result);
160
161 return success;
162 }
163
164 static bool SOSCircleDigestSet(const struct ccdigest_info *di, CFMutableSetRef set, void *hash_result, CFErrorRef *error)
165 {
166 CFMutableArrayRef values = CFSetCopyValuesCFArray(set);
167
168 bool result = SOSCircleDigestArray(di, values, hash_result, error);
169
170 CFReleaseSafe(values);
171
172 return result;
173 }
174
175 static bool SOSCircleHashGenAndPeers(const struct ccdigest_info *di, SOSGenCountRef gen, CFMutableSetRef peers, void*hash_result, CFErrorRef *error) {
176 ccdigest_di_decl(di, circle_digest);
177 ccdigest_init(di, circle_digest);
178 int64_t generation = SOSGetGenerationSint(gen);
179 ccdigest_update(di, circle_digest, sizeof(generation), &generation);
180
181 SOSCircleDigestSet(di, peers, hash_result, error);
182 ccdigest_update(di, circle_digest, di->output_size, hash_result);
183 ccdigest_final(di, circle_digest, hash_result);
184 return true;
185 }
186
187 static bool SOSCircleHash(const struct ccdigest_info *di, SOSCircleRef circle, void *hash_result, CFErrorRef *error) {
188 return SOSCircleHashGenAndPeers(di, SOSCircleGetGeneration(circle), circle->peers, hash_result, error);
189 }
190
191 static bool SOSCircleHashNextGenWithAdditionalPeer(const struct ccdigest_info *di, SOSCircleRef circle, SOSPeerInfoRef additionalPeer, void *hash_result, CFErrorRef *error) {
192 bool result = false;
193 CFMutableSetRef peers = CFSetCreateMutableCopy(NULL, 0, circle->peers);
194 CFSetAddValue(peers, additionalPeer);
195
196 SOSGenCountRef nextGen = SOSGenerationIncrementAndCreate(circle->generation);
197
198 result = SOSCircleHashGenAndPeers(di, nextGen, peers, hash_result, error);
199
200 CFReleaseNull(nextGen);
201 CFReleaseNull(peers);
202
203 return result;
204 }
205
206 bool SOSCircleSetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFDataRef signature, CFErrorRef *error) {
207 bool result = false;
208
209 CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
210 require_quiet(pubKeyID, fail);
211 CFDictionarySetValue(circle->signatures, pubKeyID, signature);
212 result = true;
213
214 fail:
215 CFReleaseSafe(pubKeyID);
216 return result;
217 }
218
219 static bool SOSCircleRemoveSignatures(SOSCircleRef circle, CFErrorRef *error) {
220 CFDictionaryRemoveAllValues(circle->signatures);
221 return true;
222 }
223
224 CFDataRef SOSCircleGetSignature(SOSCircleRef circle, SecKeyRef pubkey, CFErrorRef *error) {
225 CFStringRef pubKeyID = SOSCopyIDOfKey(pubkey, error);
226 CFDataRef result = NULL;
227 require_quiet(pubKeyID, fail);
228
229 CFTypeRef value = (CFDataRef)CFDictionaryGetValue(circle->signatures, pubKeyID);
230
231 if (isData(value)) result = (CFDataRef) value;
232
233 fail:
234 CFReleaseSafe(pubKeyID);
235 return result;
236 }
237
238 CFDictionaryRef SOSCircleCopyAllSignatures(SOSCircleRef circle) {
239 return CFDictionaryCreateCopy(kCFAllocatorDefault, circle->signatures);
240 }
241
242 #define circle_signature_di() ccsha256_di()
243
244 static CFDataRef SecKeyCopyRawHashSignature(const struct ccdigest_info *di, const uint8_t* hashToSign, SecKeyRef privKey, CFErrorRef *error) {
245 CFDataRef result = NULL;
246
247 CFMutableDataRef signature = CFDataCreateMutableWithScratch(kCFAllocatorDefault, SecKeyGetSize(privKey, kSecKeySignatureSize));
248 size_t signatureSpace = CFDataGetLength(signature);
249
250 OSStatus status = SecKeyRawSign(privKey, kSecPaddingNone, hashToSign, di->output_size, CFDataGetMutableBytePtr(signature), &signatureSpace);
251 require_quiet(SecError(status, error, CFSTR("Signing failed: %d"), (int)status), fail);
252
253 if (signatureSpace < (size_t)CFDataGetLength(signature)) {
254 CFDataSetLength(signature, signatureSpace);
255 }
256
257 CFTransferRetained(result, signature);
258 fail:
259 CFReleaseNull(signature);
260 return result;
261 }
262
263 bool SOSCircleSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
264 const struct ccdigest_info *di = circle_signature_di();
265
266 __block CFDataRef signature = NULL;
267 bool didSign = false;
268 require_quiet(privKey, fail);
269
270 PerformWithBuffer(di->output_size, ^(size_t size, uint8_t *hash_result) {
271 if (SOSCircleHash(di, circle, hash_result, error)) {
272 signature = SecKeyCopyRawHashSignature(di, hash_result, privKey, error);
273 }
274 });
275 require_quiet(signature, fail);
276 require_quiet(SOSCircleSetSignature(circle, privKey, signature, error), fail);
277
278 didSign = true;
279
280 fail:
281 CFReleaseNull(signature);
282 return didSign;
283 }
284
285 CFDataRef SOSCircleCopyNextGenSignatureWithPeerAdded(SOSCircleRef circle, SOSPeerInfoRef peer, SecKeyRef privKey, CFErrorRef *error) {
286 const struct ccdigest_info *di = circle_signature_di();
287
288 __block CFDataRef signature = NULL;
289 require_quiet(privKey, fail);
290
291 PerformWithBuffer(di->output_size, ^(size_t size, uint8_t *hash_result) {
292 if (SOSCircleHashNextGenWithAdditionalPeer(di, circle, peer, hash_result, error)) {
293 signature = SecKeyCopyRawHashSignature(di, hash_result, privKey, error);
294 }
295 });
296
297 fail:
298 return signature;
299 }
300
301
302 static bool SOSCircleConcordanceRingSign(SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
303 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring signing op", NULL);
304 return true;
305 }
306
307
308 bool SOSCircleVerifySignatureExists(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
309 if(!pubKey) {
310 secerror("SOSCircleVerifySignatureExists no pubKey");
311 SOSCreateError(kSOSErrorBadFormat, CFSTR("SOSCircleVerifySignatureExists no pubKey"), (error != NULL) ? *error : NULL, error);
312 return false;
313 }
314 CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
315 return NULL != signature;
316 }
317
318 CFStringRef SOSCircleCopyHashString(SOSCircleRef circle) {
319 const struct ccdigest_info *di = ccsha256_di();
320 uint8_t hash_result[di->output_size];
321 SOSCircleHash(di, circle, hash_result, NULL);
322 return SOSCopyHashBufAsString(hash_result, sizeof(hash_result));
323 }
324
325 bool SOSCircleVerify(SOSCircleRef circle, SecKeyRef pubKey, CFErrorRef *error) {
326 const struct ccdigest_info *di = ccsha256_di();
327 uint8_t hash_result[di->output_size];
328
329 SOSCircleHash(di, circle, hash_result, error);
330
331 CFDataRef signature = SOSCircleGetSignature(circle, pubKey, error);
332 if(!signature) return false;
333
334 return SecError(SecKeyRawVerify(pubKey, kSecPaddingNone, hash_result, di->output_size,
335 CFDataGetBytePtr(signature), CFDataGetLength(signature)), error, CFSTR("Signature verification failed."));;
336 }
337
338 bool SOSCircleVerifyPeerSignatureExists(SOSCircleRef circle, SOSPeerInfoRef peer) {
339 bool result = false;
340 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, NULL);
341 require_quiet(pub_key, fail);
342 result = SOSCircleVerifySignatureExists(circle, pub_key, NULL);
343 fail:
344 CFReleaseSafe(pub_key);
345 return result;
346 }
347
348 bool SOSCircleVerifyPeerSigned(SOSCircleRef circle, SOSPeerInfoRef peer, CFErrorRef *error) {
349 bool result = false;
350 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, error);
351 require_quiet(pub_key, fail);
352
353 result = SOSCircleVerify(circle, pub_key, error);
354 fail:
355 CFReleaseSafe(pub_key);
356 return result;
357 }
358
359 static void CFSetRemoveAllPassing(CFMutableSetRef set, bool (^test)(const void *) ){
360 CFMutableArrayRef toBeRemoved = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
361
362 CFSetForEach(set, ^(const void *value) {
363 if (test(value))
364 CFArrayAppendValue(toBeRemoved, value);
365 });
366
367 CFArrayForEach(toBeRemoved, ^(const void *value) {
368 CFSetRemoveValue(set, value);
369 });
370 CFReleaseNull(toBeRemoved);
371 }
372
373 static void SOSCircleRejectNonValidApplicants(SOSCircleRef circle, SecKeyRef pubkey) {
374 CFMutableSetRef applicants = SOSCircleCopyApplicants(circle, NULL);
375 CFSetForEach(applicants, ^(const void *value) {
376 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
377 if(!SOSPeerInfoApplicationVerify(pi, pubkey, NULL)) {
378 CFSetTransferObject(pi, circle->applicants, circle->rejected_applicants);
379 }
380 });
381 CFReleaseNull(applicants);
382 }
383
384 static SOSPeerInfoRef SOSCircleCopyPeerInfo(SOSCircleRef circle, CFStringRef peer_id, CFErrorRef *error) {
385 __block SOSPeerInfoRef result = NULL;
386
387 CFSetForEach(circle->peers, ^(const void *value) {
388 if (result == NULL) {
389 SOSPeerInfoRef tpi = (SOSPeerInfoRef)value;
390 if (CFEqual(SOSPeerInfoGetPeerID(tpi), peer_id))
391 result = tpi;
392 }
393 });
394
395 CFRetainSafe(result);
396 return result;
397 }
398
399 static bool SOSCircleUpgradePeerInfo(SOSCircleRef circle, SecKeyRef user_approver, SOSFullPeerInfoRef peerinfo) {
400 bool retval = false;
401 SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(user_approver);
402 SOSPeerInfoRef fpi_pi = SOSFullPeerInfoGetPeerInfo(peerinfo);
403 SOSPeerInfoRef pi = SOSCircleCopyPeerInfo(circle, SOSPeerInfoGetPeerID(fpi_pi), NULL);
404 require_quiet(pi, out);
405 require_quiet(SOSPeerInfoApplicationVerify(pi, userPubKey, NULL), re_sign);
406 CFReleaseNull(userPubKey);
407 CFReleaseNull(pi);
408 return true;
409
410 re_sign:
411 secnotice("circle", "SOSCircleGenerationSign: Upgraded peer's Application Signature");
412 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peerinfo, NULL);
413 require_quiet(device_key, out);
414 SOSPeerInfoRef new_pi = SOSPeerInfoCopyAsApplication(pi, user_approver, device_key, NULL);
415 if(SOSCircleUpdatePeerInfo(circle, new_pi))
416 retval = true;
417 CFReleaseNull(new_pi);
418 CFReleaseNull(device_key);
419 out:
420 CFReleaseNull(userPubKey);
421 CFReleaseNull(pi);
422 return retval;
423 }
424
425 static bool SOSCircleEnsureRingConsistency(SOSCircleRef circle, CFErrorRef *error) {
426 secnotice("Development", "SOSCircleEnsureRingConsistency requires ring membership and generation count consistency check", NULL);
427 return true;
428 }
429
430 bool SOSCircleSignOldStyleResetToOfferingCircle(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, SecKeyRef user_approver, CFErrorRef *error){
431
432 SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
433 SecKeyRef publicKey = NULL;
434 require_quiet(ourKey, fail);
435
436 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
437 require_quiet(SOSCircleUpgradePeerInfo(circle, user_approver, peerinfo), fail);
438 SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
439 CFSetRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
440 publicKey = SecKeyCreatePublicFromPrivate(user_approver);
441 SOSCircleRejectNonValidApplicants(circle, publicKey);
442 require_quiet(SOSCircleEnsureRingConsistency(circle, error), fail);
443 require_quiet(SOSCircleRemoveSignatures(circle, error), fail);
444 require_quiet(SOSCircleSign(circle, user_approver, error), fail);
445 require_quiet(SOSCircleSign(circle, ourKey, error), fail);
446
447 CFReleaseNull(ourKey);
448 CFReleaseNull(publicKey);
449 return true;
450
451 fail:
452 CFReleaseNull(ourKey);
453 CFReleaseNull(publicKey);
454 return false;
455 }
456
457 bool SOSCirclePreGenerationSign(SOSCircleRef circle, SecKeyRef userPubKey, CFErrorRef *error) {
458 bool retval = false;
459
460 SOSCircleRemoveRetired(circle, error); // Prune off retirees since we're signing this one
461 CFSetRemoveAllValues(circle->rejected_applicants); // Dump rejects so we clean them up sometime.
462 SOSCircleRejectNonValidApplicants(circle, userPubKey);
463
464 require_quiet(SOSCircleRemoveSignatures(circle, error), errOut);
465
466 retval = true;
467
468 errOut:
469 return retval;
470
471 }
472
473 static bool SOSCircleGenerationSign_Internal(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
474 // require_quiet(SOSCircleEnsureRingConsistency(circle, error), fail); Placeholder - this was never implemented
475 bool retval = false;
476 SecKeyRef ourKey = NULL;
477 if (SOSCircleCountPeers(circle) != 0) {
478 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
479 require_quiet(ourKey, errOut);
480
481 // Check if we're using an invalid peerinfo for this op. There are cases where we might not be "upgraded".
482 require_quiet(SOSCircleUpgradePeerInfo(circle, userKey, fpi), errOut);
483
484 require_quiet(SOSCircleSign(circle, userKey, error), errOut);
485 require_quiet(SOSCircleSign(circle, ourKey, error), errOut);
486 CFReleaseNull(ourKey);
487 }
488 retval = true;
489
490 errOut:
491 CFReleaseNull(ourKey);
492 return retval;
493 }
494
495 bool SOSCircleGenerationSign(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
496 bool retval = false;
497 SecKeyRef publicKey = NULL;
498 publicKey = SecKeyCreatePublicFromPrivate(userKey);
499
500 require_quiet(SOSCirclePreGenerationSign(circle, publicKey, error), errOut);
501 SOSCircleGenerationIncrement(circle);
502 require_quiet(SOSCircleGenerationSign_Internal(circle, userKey, fpi, error), errOut);
503 retval = true;
504
505 errOut:
506 CFReleaseNull(publicKey);
507 return retval;
508 }
509
510
511 static bool SOSCircleGenerationSignWithGenCount(SOSCircleRef circle, SecKeyRef userKey, SOSFullPeerInfoRef fpi, SOSGenCountRef gencount, CFErrorRef *error) {
512 bool retval = false;
513 SOSGenCountRef currentGen = SOSCircleGetGeneration(circle);
514 require_action_quiet(SOSGenerationIsOlder(currentGen, gencount), errOut, SOSCreateError(kSOSErrorReplay, CFSTR("Generation Count for new circle is too old"), NULL, error));
515 require_quiet(SOSCirclePreGenerationSign(circle, userKey, error), errOut);
516 SOSCircleSetGeneration(circle, gencount);
517 require_quiet(SOSCircleGenerationSign_Internal(circle, userKey, fpi, error), errOut);
518 retval = true;
519
520 errOut:
521 return retval;
522 }
523
524
525 bool SOSCircleConcordanceSign(SOSCircleRef circle, SOSFullPeerInfoRef peerinfo, CFErrorRef *error) {
526 bool success = false;
527 SecKeyRef ourKey = SOSFullPeerInfoCopyDeviceKey(peerinfo, error);
528 require_quiet(ourKey, exit);
529
530 success = SOSCircleSign(circle, ourKey, error);
531 SOSCircleConcordanceRingSign(circle, ourKey, error);
532
533 exit:
534 CFReleaseNull(ourKey);
535 return success;
536 }
537
538 static inline SOSConcordanceStatus CheckPeerStatus(SOSCircleRef circle, SOSPeerInfoRef peer, SecKeyRef user_public_key, CFErrorRef *error) {
539 SOSConcordanceStatus result = kSOSConcordanceNoPeer;
540 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer, error);
541 require_quiet(pubKey, exit);
542
543 require_action_quiet(SOSCircleHasActiveValidPeer(circle, peer, user_public_key, error), exit, result = kSOSConcordanceNoPeer);
544 require_action_quiet(SOSCircleVerifySignatureExists(circle, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
545 require_action_quiet(SOSCircleVerify(circle, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
546
547 result = kSOSConcordanceTrusted;
548
549 exit:
550 CFReleaseNull(pubKey);
551 return result;
552 }
553
554 static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
555 {
556 if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
557 return kSOSConcordanceTrusted;
558
559 if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
560 return kSOSConcordanceBadPeerSig;
561
562 if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
563 return kSOSConcordanceNoPeerSig;
564
565 return status1;
566 }
567
568 static inline bool SOSCircleIsEmpty(SOSCircleRef circle) {
569 return SOSCircleCountPeers(circle) == 0;
570 }
571
572 static inline bool SOSCircleHasDegenerateGeneration(SOSCircleRef deGenCircle){
573 CFIndex testPtr;
574 CFNumberRef genCountTest = SOSCircleGetGeneration(deGenCircle);
575 CFNumberGetValue(genCountTest, kCFNumberCFIndexType, &testPtr);
576 return (testPtr== 0);
577 }
578
579
580 static inline bool SOSCircleIsDegenerateReset(SOSCircleRef deGenCircle){
581 return SOSCircleHasDegenerateGeneration(deGenCircle) && SOSCircleIsEmpty(deGenCircle);
582 }
583
584
585 __unused static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
586 return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
587 }
588
589 static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
590 SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
591 CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
592
593 __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
594 SOSCircleForEachActivePeer(signers_circle, ^(SOSPeerInfoRef peer) {
595 SOSConcordanceStatus peerStatus = CheckPeerStatus(status_circle, peer, user_pubKey, error);
596
597 if (peerStatus == kSOSConcordanceNoPeerSig &&
598 (CFEqualSafe(SOSPeerInfoGetPeerID(peer), excluded_id) || SOSPeerInfoIsCloudIdentity(peer)))
599 peerStatus = kSOSConcordanceNoPeer;
600
601 status = CombineStatus(status, peerStatus); // TODO: Use multiple error gathering.
602 });
603
604 return status;
605 }
606
607 // Is current older than proposed?
608 bool SOSCircleIsOlderGeneration(SOSCircleRef older, SOSCircleRef newer) {
609 return SOSGenerationIsOlder(older->generation, newer->generation);
610 }
611
612 static inline bool SOSCircleIsValidReset(SOSCircleRef current, SOSCircleRef proposed) {
613 bool retval = false;
614 retval = SOSCircleIsEmpty(proposed);
615 require_quiet(retval, errOut);
616 retval = SOSCircleIsOlderGeneration(current, proposed);
617 errOut:
618 return retval;
619 }
620
621
622 bool SOSCircleSharedTrustedPeers(SOSCircleRef current, SOSCircleRef proposed, SOSPeerInfoRef me) {
623 __block bool retval = false;
624 SOSCircleForEachPeer(current, ^(SOSPeerInfoRef peer) {
625 if(!CFEqual(me, peer) && SOSCircleHasPeer(proposed, peer, NULL)) retval = true;
626 });
627 return retval;
628 }
629
630 static SOSConcordanceStatus GetOfferingStatus(SOSCircleRef circle, SecKeyRef user_pubKey, CFErrorRef *error) {
631 __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
632 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
633 status = CheckPeerStatus(circle, peer, user_pubKey, error);
634 if(status != kSOSConcordanceTrusted) status = kSOSConcordanceNoPeer;
635 });
636 return status;
637 }
638
639
640 SOSConcordanceStatus SOSCircleConcordanceTrust(SOSCircleRef known_circle, SOSCircleRef proposed_circle,
641 SecKeyRef known_pubkey, SecKeyRef user_pubkey,
642 SOSPeerInfoRef me, CFErrorRef *error) {
643 if(user_pubkey == NULL) {
644 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no user public key"), NULL, error);
645 return kSOSConcordanceNoUserKey;
646 }
647
648 if(SOSCircleIsDegenerateReset(proposed_circle)) {
649 return kSOSConcordanceTrusted;
650 }
651
652 if (SOSCircleIsValidReset(known_circle, proposed_circle)) {
653 return kSOSConcordanceTrusted;
654 }
655
656 if(!SOSCircleVerifySignatureExists(proposed_circle, user_pubkey, error)) {
657 SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature to match current user key"), (error != NULL) ? *error : NULL, error);
658 return kSOSConcordanceNoUserSig;
659 }
660
661 if(!SOSCircleVerify(proposed_circle, user_pubkey, error)) {
662 SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad user public signature"), (error != NULL) ? *error : NULL, error);
663 debugDumpCircle(CFSTR("proposed_circle"), proposed_circle);
664 return kSOSConcordanceBadUserSig;
665 }
666
667 if (SOSCircleIsEmpty(known_circle)) {
668 return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
669 }
670
671 if(SOSCircleHasDegenerateGeneration(proposed_circle) && SOSCircleIsOffering(proposed_circle)){
672 return GetSignersStatus(proposed_circle, proposed_circle, user_pubkey, NULL, error);
673 }
674
675 if(SOSCircleIsOlderGeneration(proposed_circle, known_circle)) {
676 SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation - proposed circle gencount is older than known circle gencount"), NULL, error);
677 debugDumpCircle(CFSTR("isOlderGeneration known_circle"), known_circle);
678 debugDumpCircle(CFSTR("isOlderGeneration proposed_circle"), proposed_circle);
679 return kSOSConcordanceGenOld;
680 }
681
682 if(SOSCircleIsOffering(proposed_circle)){
683 return GetOfferingStatus(proposed_circle, user_pubkey, error);
684 }
685
686 return GetSignersStatus(known_circle, proposed_circle, user_pubkey, me, error);
687 }
688
689
690 static void SOSCircleDestroy(CFTypeRef aObj) {
691 SOSCircleRef c = (SOSCircleRef) aObj;
692
693 CFReleaseNull(c->name);
694 CFReleaseNull(c->generation);
695 CFReleaseNull(c->peers);
696 CFReleaseNull(c->applicants);
697 CFReleaseNull(c->rejected_applicants);
698 CFReleaseNull(c->signatures);
699 }
700
701 static CFMutableStringRef defaultDescriptionCreate(CFTypeRef aObj){
702 SOSCircleRef c = (SOSCircleRef) aObj;
703 CFStringRef initPeerSep = CFSTR("\n");
704 CFStringRef peerSep = CFSTR("\n");
705
706 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
707
708 SOSGenerationCountWithDescription(c->generation, ^(CFStringRef genDescription) {
709 CFStringAppendFormat(description, NULL, CFSTR("<SOSCircle@%p: '%@' %@ P:["), c, c->name, genDescription);
710 });
711
712 __block CFStringRef separator = initPeerSep;
713 SOSCircleForEachActivePeer(c, ^(SOSPeerInfoRef peer) {
714 CFStringRef sig = NULL;
715 if (SOSCircleVerifyPeerSigned(c, peer, NULL)) {
716 sig = CFSTR("√");
717 } else {
718 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, NULL);
719 CFDataRef signature = pub_key ? SOSCircleGetSignature(c, pub_key, NULL) : NULL;
720 sig = (signature == NULL) ? CFSTR("-") : CFSTR("?");
721 CFReleaseNull(pub_key);
722 }
723
724 CFStringAppendFormat(description, NULL, CFSTR("%@%@ %@"), separator, peer, sig);
725 separator = peerSep;
726 });
727
728 //applicants
729 CFStringAppend(description, CFSTR("], A:["));
730 separator = initPeerSep;
731 if(CFSetGetCount(c->applicants) == 0 )
732 CFStringAppendFormat(description, NULL, CFSTR("-"));
733 else{
734
735 SOSCircleForEachApplicant(c, ^(SOSPeerInfoRef peer) {
736 CFStringAppendFormat(description, NULL, CFSTR("%@%@"), separator, peer);
737 separator = peerSep;
738 });
739 }
740
741 //rejected
742 CFStringAppend(description, CFSTR("], R:["));
743 separator = initPeerSep;
744 if(CFSetGetCount(c->rejected_applicants) == 0)
745 CFStringAppendFormat(description, NULL, CFSTR("-"));
746 else{
747 CFSetForEach(c->rejected_applicants, ^(const void *value) {
748 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
749 CFStringAppendFormat(description, NULL, CFSTR("%@%@"), separator, peer);
750 separator = peerSep;
751 });
752 }
753 CFStringAppend(description, CFSTR("]>"));
754 return description;
755
756 }
757 static CFMutableStringRef descriptionCreateWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){
758 SOSCircleRef c = (SOSCircleRef) aObj;
759
760 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
761
762 if(CFDictionaryContainsKey(formatOptions, CFSTR("SyncD"))) {
763 CFStringRef generationDescription = SOSGenerationCountCopyDescription(c->generation);
764 CFStringAppendFormat(description, NULL, CFSTR("<C: gen:'%@' %@>\n"), generationDescription, c->name);
765 CFReleaseNull(generationDescription);
766 __block CFStringRef separator = CFSTR("\t\t");
767 SOSCircleForEachActivePeer(c, ^(SOSPeerInfoRef peer) {
768 CFStringRef sig = NULL;
769 if (SOSCircleVerifyPeerSigned(c, peer, NULL)) {
770 sig = CFSTR("√");
771 } else {
772 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(peer, NULL);
773 CFDataRef signature = pub_key ? SOSCircleGetSignature(c, pub_key, NULL) : NULL;
774 sig = (signature == NULL) ? CFSTR("-") : CFSTR("?");
775 CFReleaseNull(pub_key);
776 }
777
778 CFStringAppendFormat(description, formatOptions, CFSTR("%@%@ %@"), separator, peer, sig);
779 separator = CFSTR("\n\t\t");
780 });
781 CFStringAppend(description, CFSTR("\n\t\t<A:["));
782 separator = CFSTR("");
783
784 //applicants list
785 if(CFSetGetCount(c->applicants) == 0 )
786 CFStringAppendFormat(description, NULL, CFSTR("-"));
787 else{
788
789 SOSCircleForEachApplicant(c, ^(SOSPeerInfoRef peer) {
790 CFStringAppendFormat(description, formatOptions, CFSTR("%@A: %@"), separator, peer);
791 separator = CFSTR("\n\t\t\t");
792 });
793 }
794 //rejected list
795 CFStringAppend(description, CFSTR("]> \n\t\t<R:["));
796 separator = CFSTR("");
797 if(CFSetGetCount(c->rejected_applicants) == 0)
798 CFStringAppendFormat(description, NULL, CFSTR("-"));
799 else{
800 CFSetForEach(c->rejected_applicants, ^(const void *value) {
801 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
802 CFStringAppendFormat(description, formatOptions, CFSTR("%@R: %@"), separator, peer);
803 separator = CFSTR("\n\t\t");
804 });
805 }
806 CFStringAppend(description, CFSTR("]>"));
807 }
808
809 else{
810 CFReleaseNull(description);
811 description = defaultDescriptionCreate(aObj);
812 }
813
814 return description;
815
816 }
817
818
819 static CFStringRef SOSCircleCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
820 SOSCircleRef c = (SOSCircleRef) aObj;
821 SOSCircleAssertStable(c);
822 CFMutableStringRef description = NULL;
823
824 if(formatOptions != NULL){
825 description = descriptionCreateWithFormatOptions(aObj, formatOptions);
826 }
827 else{
828 description = defaultDescriptionCreate(aObj);
829 }
830 return description;
831 }
832
833 CFStringRef SOSCircleGetName(SOSCircleRef circle) {
834 assert(circle);
835 assert(circle->name);
836 return circle->name;
837 }
838
839 const char *SOSCircleGetNameC(SOSCircleRef circle) {
840 CFStringRef name = SOSCircleGetName(circle);
841 if (!name)
842 return strdup("");
843 return CFStringToCString(name);
844 }
845
846 SOSGenCountRef SOSCircleGetGeneration(SOSCircleRef circle) {
847 assert(circle);
848 assert(circle->generation);
849 return circle->generation;
850 }
851
852 void SOSCircleSetGeneration(SOSCircleRef circle, SOSGenCountRef gencount) {
853 assert(circle);
854 CFReleaseNull(circle->generation);
855 circle->generation = CFRetainSafe(gencount);
856 }
857
858 int64_t SOSCircleGetGenerationSint(SOSCircleRef circle) {
859 SOSGenCountRef gen = SOSCircleGetGeneration(circle);
860 return SOSGetGenerationSint(gen);
861 }
862
863 void SOSCircleGenerationSetValue(SOSCircleRef circle, int64_t value) {
864 CFAssignRetained(circle->generation, SOSGenerationCreateWithValue(value));
865 }
866
867 void SOSCircleGenerationIncrement(SOSCircleRef circle) {
868 SOSGenCountRef old = circle->generation;
869 circle->generation = SOSGenerationIncrementAndCreate(old);
870 CFReleaseNull(old);
871 }
872
873 int SOSCircleCountPeers(SOSCircleRef circle) {
874 SOSCircleAssertStable(circle);
875 __block int count = 0;
876 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
877 ++count;
878 });
879 return count;
880 }
881
882 int SOSCircleCountActivePeers(SOSCircleRef circle) {
883 SOSCircleAssertStable(circle);
884 __block int count = 0;
885 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
886 ++count;
887 });
888 return count;
889 }
890
891 int SOSCircleCountActiveValidPeers(SOSCircleRef circle, SecKeyRef pubkey) {
892 SOSCircleAssertStable(circle);
893 __block int count = 0;
894 SOSCircleForEachActiveValidPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
895 ++count;
896 });
897 return count;
898 }
899
900 int SOSCircleCountValidSyncingPeers(SOSCircleRef circle, SecKeyRef pubkey) {
901 SOSCircleAssertStable(circle);
902 __block int count = 0;
903 SOSCircleForEachValidSyncingPeer(circle, pubkey, ^(SOSPeerInfoRef peer) {
904 ++count;
905 });
906 return count;
907
908 }
909
910 int SOSCircleCountRetiredPeers(SOSCircleRef circle) {
911 SOSCircleAssertStable(circle);
912 __block int count = 0;
913 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
914 ++count;
915 });
916 return count;
917 }
918
919 int SOSCircleCountApplicants(SOSCircleRef circle) {
920 SOSCircleAssertStable(circle);
921
922 return (int)CFSetGetCount(circle->applicants);
923 }
924
925 bool SOSCircleHasApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
926 SOSCircleAssertStable(circle);
927
928 return CFSetContainsValue(circle->applicants, peerInfo);
929 }
930
931 CFMutableSetRef SOSCircleCopyApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
932 SOSCircleAssertStable(circle);
933
934 return CFSetCreateMutableCopy(allocator, 0, circle->applicants);
935 }
936
937 int SOSCircleCountRejectedApplicants(SOSCircleRef circle) {
938 SOSCircleAssertStable(circle);
939
940 return (int)CFSetGetCount(circle->rejected_applicants);
941 }
942
943 bool SOSCircleHasRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
944 SOSCircleAssertStable(circle);
945 return CFSetContainsValue(circle->rejected_applicants, peerInfo);
946 }
947
948 SOSPeerInfoRef SOSCircleCopyRejectedApplicant(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
949 SOSCircleAssertStable(circle);
950 return CFRetainSafe((SOSPeerInfoRef)CFSetGetValue(circle->rejected_applicants, peerInfo));
951 }
952
953 CFMutableArrayRef SOSCircleCopyRejectedApplicants(SOSCircleRef circle, CFAllocatorRef allocator) {
954 SOSCircleAssertStable(circle);
955
956 return CFSetCopyValuesCFArray(circle->rejected_applicants);
957 }
958
959 bool SOSCircleResetToEmpty(SOSCircleRef circle, CFErrorRef *error) {
960 CFSetRemoveAllValues(circle->applicants);
961 CFSetRemoveAllValues(circle->rejected_applicants);
962 CFSetRemoveAllValues(circle->peers);
963 CFDictionaryRemoveAllValues(circle->signatures);
964 SOSGenCountRef oldGen = SOSCircleGetGeneration(circle);
965 SOSGenCountRef newGen = SOSGenerationCreateWithBaseline(oldGen);
966 SOSCircleSetGeneration(circle, newGen);
967 CFReleaseSafe(newGen);
968 return true;
969 }
970
971 bool SOSCircleResetToEmptyWithSameGeneration(SOSCircleRef circle, CFErrorRef *error) {
972 SOSGenCountRef gen = SOSGenerationCopy(SOSCircleGetGeneration(circle));
973 SOSCircleResetToEmpty(circle, error);
974 SOSCircleSetGeneration(circle, gen);
975 CFReleaseNull(gen);
976 return true;
977 }
978
979 bool SOSCircleResetToOffering(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error){
980
981 return SOSCircleResetToEmpty(circle, error)
982 && SOSCircleRequestAdmission(circle, user_privkey, requestor, error)
983 && SOSCircleAcceptRequest(circle, user_privkey, requestor, SOSFullPeerInfoGetPeerInfo(requestor), error);
984 }
985
986 bool SOSCircleRemoveRetired(SOSCircleRef circle, CFErrorRef *error) {
987 CFSetRemoveAllPassing(circle->peers, ^ bool (const void *element) {
988 SOSPeerInfoRef peer = (SOSPeerInfoRef) element;
989
990 return SOSPeerInfoIsRetirementTicket(peer);
991 });
992
993 return true;
994 }
995
996 static bool SOSCircleRecordAdmissionRequest(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef requestorPeerInfo, CFErrorRef *error) {
997 SOSCircleAssertStable(circle);
998
999 bool isPeer = SOSCircleHasPeer(circle, requestorPeerInfo, error);
1000
1001 require_action_quiet(!isPeer, fail, SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Cannot request admission when already a peer"), NULL, error));
1002
1003 // This adds to applicants and will take off rejected if it's there.
1004 CFSetTransferObject(requestorPeerInfo, circle->rejected_applicants, circle->applicants);
1005
1006 return true;
1007
1008 fail:
1009 return false;
1010
1011 }
1012
1013 bool SOSCircleRequestReadmission(SOSCircleRef circle, SecKeyRef user_pubkey, SOSPeerInfoRef peer, CFErrorRef *error) {
1014 bool success = false;
1015
1016 require_quiet(SOSPeerInfoApplicationVerify(peer, user_pubkey, error), fail);
1017 success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, peer, error);
1018 fail:
1019 return success;
1020 }
1021
1022 bool SOSCircleRequestAdmission(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
1023 bool success = false;
1024
1025 SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
1026 require_action_quiet(user_pubkey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("No public key for key"), NULL, error));
1027
1028 require(SOSFullPeerInfoPromoteToApplication(requestor, user_privkey, error), fail);
1029
1030 success = SOSCircleRecordAdmissionRequest(circle, user_pubkey, SOSFullPeerInfoGetPeerInfo(requestor), error);
1031 fail:
1032 CFReleaseNull(user_pubkey);
1033 return success;
1034 }
1035
1036 static bool sosCircleUpdatePeerInfoSet(CFMutableSetRef theSet, SOSPeerInfoRef replacement_peer_info) {
1037 CFTypeRef old = NULL;
1038 if(!replacement_peer_info) return false;
1039 if(!(old = CFSetGetValue(theSet, replacement_peer_info))) return false;
1040 if(CFEqualSafe(old, replacement_peer_info)) return false;
1041 CFSetReplaceValue(theSet, replacement_peer_info);
1042 return true;
1043 }
1044
1045 bool SOSCircleUpdatePeerInfo(SOSCircleRef circle, SOSPeerInfoRef replacement_peer_info) {
1046 if(sosCircleUpdatePeerInfoSet(circle->peers, replacement_peer_info)) return true;
1047 if(sosCircleUpdatePeerInfoSet(circle->applicants, replacement_peer_info)) return true;
1048 if(sosCircleUpdatePeerInfoSet(circle->rejected_applicants, replacement_peer_info)) return true;
1049 return false;
1050 }
1051
1052 static bool SOSCircleRemovePeerInternal(SOSCircleRef circle, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
1053 SOSPeerInfoRef requestor_peer_info = SOSFullPeerInfoGetPeerInfo(requestor);
1054
1055 if (SOSCircleHasPeer(circle, peer_to_remove, NULL)) {
1056 if (!SOSCircleHasPeer(circle, requestor_peer_info, error)) {
1057 SOSCreateError(kSOSErrorAlreadyPeer, CFSTR("Must be peer to remove peer"), NULL, error);
1058 return false;
1059 }
1060 CFSetRemoveValue(circle->peers, peer_to_remove);
1061 }
1062
1063 if (SOSCircleHasApplicant(circle, peer_to_remove, error)) {
1064 return SOSCircleRejectRequest(circle, requestor, peer_to_remove, error);
1065 }
1066
1067 return true;
1068 }
1069
1070 bool SOSCircleRemovePeers(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFSetRef peersToRemove, CFErrorRef *error) {
1071
1072 bool success = false;
1073
1074 __block bool removed_all = true;
1075 CFSetForEach(peersToRemove, ^(const void *value) {
1076 SOSPeerInfoRef peerInfo = asSOSPeerInfo(value);
1077 if (peerInfo) {
1078 removed_all &= SOSCircleRemovePeerInternal(circle, requestor, peerInfo, error);
1079 }
1080 });
1081
1082 require_quiet(removed_all, exit);
1083
1084 require_quiet(SOSCircleGenerationSign(circle, user_privkey, requestor, error), exit);
1085
1086 success = true;
1087
1088 exit:
1089 return success;
1090 }
1091
1092
1093 bool SOSCircleRemovePeersByID(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFSetRef peersToRemove, CFErrorRef *error) {
1094
1095 bool success = false;
1096
1097 __block bool removed_all = true;
1098 CFSetForEach(peersToRemove, ^(const void *value) {
1099 CFStringRef peerID = asString(value, NULL);
1100 if(peerID) {
1101 SOSPeerInfoRef peerInfo = SOSCircleCopyPeerInfo(circle, peerID, NULL);
1102 if (peerInfo) {
1103 removed_all &= SOSCircleRemovePeerInternal(circle, requestor, peerInfo, error);
1104 CFReleaseNull(peerInfo);
1105 }
1106 }
1107 });
1108
1109 require_quiet(removed_all, exit);
1110
1111 require_quiet(SOSCircleGenerationSign(circle, user_privkey, requestor, error), exit);
1112
1113 success = true;
1114
1115 exit:
1116 return success;
1117 }
1118
1119 static bool SOSCircleRemovePeerUnsigned(SOSCircleRef circle, SOSPeerInfoRef peer_to_remove) {
1120 bool retval = false;
1121 if (SOSCircleHasPeer(circle, peer_to_remove, NULL)) {
1122 CFSetRemoveValue(circle->peers, peer_to_remove);
1123 retval = true;
1124 }
1125 return retval;
1126 }
1127
1128 bool SOSCircleRemovePeersByIDUnsigned(SOSCircleRef circle, CFSetRef peersToRemove) {
1129 __block bool removed_all = true;
1130 CFSetForEach(peersToRemove, ^(const void *value) {
1131 CFStringRef peerID = asString(value, NULL);
1132 SOSPeerInfoRef peerInfo = SOSCircleCopyPeerInfo(circle, peerID, NULL);
1133 removed_all &= SOSCircleRemovePeerUnsigned(circle, peerInfo);
1134 CFReleaseNull(peerInfo);
1135 });
1136 return removed_all;
1137 }
1138
1139 bool SOSCircleRemovePeer(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, SOSPeerInfoRef peer_to_remove, CFErrorRef *error) {
1140 bool success = false;
1141
1142 require_quiet(SOSCircleRemovePeerInternal(circle, requestor, peer_to_remove, error), exit);
1143
1144 require_quiet(SOSCircleGenerationSign(circle, user_privkey, requestor, error), exit);
1145
1146 success = true;
1147 exit:
1148 return success;
1149 }
1150
1151 bool SOSCircleAcceptRequest(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1152 SOSCircleAssertStable(circle);
1153
1154 SecKeyRef publicKey = NULL;
1155 bool result = false;
1156
1157 require_action_quiet(CFSetContainsValue(circle->applicants, peerInfo), fail,
1158 SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot accept non-applicant"), NULL, error));
1159
1160 publicKey = SecKeyCreatePublicFromPrivate(user_privkey);
1161 require_quiet(SOSPeerInfoApplicationVerify(peerInfo, publicKey, error), fail);
1162
1163 CFSetTransferObject(peerInfo, circle->applicants, circle->peers);
1164
1165 result = SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
1166
1167 fail:
1168 CFReleaseNull(publicKey);
1169 return result;
1170 }
1171
1172 bool SOSCircleWithdrawRequest(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1173 SOSCircleAssertStable(circle);
1174
1175 CFSetRemoveValue(circle->applicants, peerInfo);
1176
1177 return true;
1178 }
1179
1180 bool SOSCircleRemoveRejectedPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1181 SOSCircleAssertStable(circle);
1182
1183 CFSetRemoveValue(circle->rejected_applicants, peerInfo);
1184
1185 return true;
1186 }
1187
1188
1189 bool SOSCircleRejectRequest(SOSCircleRef circle, SOSFullPeerInfoRef device_rejector,
1190 SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1191 SOSCircleAssertStable(circle);
1192
1193 if (CFEqual(SOSPeerInfoGetPeerID(peerInfo), SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(device_rejector))))
1194 return SOSCircleWithdrawRequest(circle, peerInfo, error);
1195
1196 if (!CFSetContainsValue(circle->applicants, peerInfo)) {
1197 SOSCreateError(kSOSErrorNotApplicant, CFSTR("Cannot reject non-applicant"), NULL, error);
1198 return false;
1199 }
1200
1201 CFSetTransferObject(peerInfo, circle->applicants, circle->rejected_applicants);
1202
1203 // TODO: Maybe we sign the rejection with device_rejector.
1204
1205 return true;
1206 }
1207
1208 bool SOSCircleAcceptRequests(SOSCircleRef circle, SecKeyRef user_privkey, SOSFullPeerInfoRef device_approver,
1209 CFErrorRef *error) {
1210 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1211 __block bool result = false;
1212
1213 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
1214 if (!SOSCircleAcceptRequest(circle, user_privkey, device_approver, peer, error)) {
1215 secnotice("circle", "error in SOSCircleAcceptRequest\n");
1216 } else {
1217 secnotice("circle", "Accepted peer: %@", peer);
1218 result = true;
1219 }
1220 });
1221
1222 if (result) {
1223 SOSCircleGenerationSign(circle, user_privkey, device_approver, error);
1224 secnotice("circle", "Countersigned accepted requests");
1225 }
1226
1227 return result;
1228 }
1229
1230 bool SOSCirclePeerSigUpdate(SOSCircleRef circle, SecKeyRef userPrivKey, SOSFullPeerInfoRef fpi,
1231 CFErrorRef *error) {
1232 // Returns true if we accepted someone and therefore have to post the circle back to KVS
1233 __block bool result = false;
1234 SecKeyRef userPubKey = SecKeyCreatePublicFromPrivate(userPrivKey);
1235
1236 // We're going to remove any applicants using a mismatched user key.
1237 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
1238 if(!SOSPeerInfoApplicationVerify(peer, userPubKey, NULL)) {
1239 if(!SOSCircleRejectRequest(circle, fpi, peer, NULL)) {
1240 // do we care?
1241 }
1242 }
1243 });
1244
1245 result = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(fpi));
1246
1247 if (result) {
1248 SOSCircleGenerationSign(circle, userPrivKey, fpi, error);
1249 secnotice("circle", "Generation signed updated signatures on peerinfo");
1250 }
1251
1252 return result;
1253 }
1254
1255 //
1256 // Peer iteration and membership
1257 //
1258
1259 static inline void SOSCircleForEachPeerMatching(SOSCircleRef circle,
1260 void (^action)(SOSPeerInfoRef peer),
1261 bool (^condition)(SOSPeerInfoRef peer)) {
1262 CFSetForEach(circle->peers, ^(const void *value) {
1263 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1264 if (condition(peer))
1265 action(peer);
1266 });
1267 }
1268
1269 static inline bool isHiddenPeer(SOSPeerInfoRef peer) {
1270 return SOSPeerInfoIsRetirementTicket(peer) || SOSPeerInfoIsCloudIdentity(peer);
1271 }
1272
1273 void SOSCircleForEachPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1274 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1275 return !isHiddenPeer(peer);
1276 });
1277 }
1278
1279 void SOSCircleForEachRetiredPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1280 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1281 return SOSPeerInfoIsRetirementTicket(peer);
1282 });
1283 }
1284
1285 void SOSCircleForEachiCloudIdentityPeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1286 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1287 return SOSPeerInfoIsCloudIdentity(peer);
1288 });
1289 }
1290
1291
1292 void SOSCircleForEachActivePeer(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1293 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1294 return true;
1295 });
1296 }
1297
1298 void SOSCircleForEachActiveValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1299 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1300 return SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
1301 });
1302 }
1303
1304 void SOSCircleForEachValidPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1305 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1306 return !isHiddenPeer(peer) && SOSPeerInfoApplicationVerify(peer, user_public_key, NULL);
1307 });
1308 }
1309
1310 void SOSCircleForEachValidSyncingPeer(SOSCircleRef circle, SecKeyRef user_public_key, void (^action)(SOSPeerInfoRef peer)) {
1311 SOSCircleForEachValidPeer(circle, user_public_key, action);
1312 }
1313
1314 void SOSCircleForEachBackupCapablePeerForView(SOSCircleRef circle, SecKeyRef user_public_key, CFStringRef viewName, void (^action)(SOSPeerInfoRef peer)) {
1315 SOSCircleForEachPeerMatching(circle, action, ^bool(SOSPeerInfoRef peer) {
1316 return (!isHiddenPeer(peer) && SOSPeerInfoIsEnabledView(peer, viewName) && SOSPeerInfoHasBackupKey(peer) && SOSPeerInfoApplicationVerify(peer, user_public_key, NULL));
1317 });
1318 }
1319
1320 void SOSCircleForEachApplicant(SOSCircleRef circle, void (^action)(SOSPeerInfoRef peer)) {
1321 CFSetForEach(circle->applicants, ^(const void*value) { action((SOSPeerInfoRef) value); } );
1322 }
1323
1324
1325 bool SOSCircleHasPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
1326 SOSCircleAssertStable(circle);
1327
1328 SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerid));
1329 return found && !isHiddenPeer(found);
1330 }
1331
1332 SOSPeerInfoRef SOSCircleCopyPeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
1333 SOSCircleAssertStable(circle);
1334
1335 SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerid));
1336 return found ? SOSPeerInfoCreateCopy(kCFAllocatorDefault, found, NULL) : NULL;
1337 }
1338
1339 bool SOSCircleHasPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1340 if(!peerInfo) return false;
1341 return SOSCircleHasPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
1342 }
1343
1344 bool SOSCircleHasActivePeerWithID(SOSCircleRef circle, CFStringRef peerid, CFErrorRef *error) {
1345 SOSCircleAssertStable(circle);
1346 SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerid));
1347 return found;
1348 }
1349
1350 bool SOSCircleHasActivePeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
1351 if(!peerInfo) return false;
1352 return SOSCircleHasActivePeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), error);
1353 }
1354
1355 bool SOSCircleHasActiveValidPeerWithID(SOSCircleRef circle, CFStringRef peerid, SecKeyRef user_public_key, CFErrorRef *error) {
1356 SOSCircleAssertStable(circle);
1357 SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerid));
1358 return found && SOSPeerInfoApplicationVerify(found, user_public_key, NULL);
1359 }
1360
1361 bool SOSCircleHasValidSyncingPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, SecKeyRef user_public_key, CFErrorRef *error) {
1362 SOSCircleAssertStable(circle);
1363 SOSPeerInfoRef found = asSOSPeerInfo(CFSetGetValue(circle->peers, peerInfo));
1364 return found && !isHiddenPeer(found) && SOSPeerInfoApplicationVerify(found, user_public_key, NULL);
1365 }
1366
1367 bool SOSCircleHasActiveValidPeer(SOSCircleRef circle, SOSPeerInfoRef peerInfo, SecKeyRef user_public_key, CFErrorRef *error) {
1368 if(!peerInfo) return false;
1369 return SOSCircleHasActiveValidPeerWithID(circle, SOSPeerInfoGetPeerID(peerInfo), user_public_key, error);
1370 }
1371
1372
1373 CFMutableSetRef SOSCircleCopyPeers(SOSCircleRef circle, CFAllocatorRef allocator) {
1374 SOSCircleAssertStable(circle);
1375
1376 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator);
1377
1378 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1379 CFSetAddValue(result, peer);
1380 });
1381
1382 return result;
1383 }
1384
1385
1386 CFMutableSetRef SOSCircleCopyBackupCapablePeersForView(SOSCircleRef circle, CFAllocatorRef allocator, SecKeyRef userPubKey, CFStringRef viewName) {
1387 SOSCircleAssertStable(circle);
1388
1389 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator);
1390
1391 SOSCircleForEachBackupCapablePeerForView(circle, userPubKey, viewName, ^(SOSPeerInfoRef peer) {
1392 CFSetAddValue(result, peer);
1393 });
1394
1395 return result;
1396 }
1397
1398 bool SOSCircleAppendConcurringPeers(SOSCircleRef circle, CFMutableArrayRef appendHere, CFErrorRef *error) {
1399 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1400 CFErrorRef localError = NULL;
1401 if (SOSCircleVerifyPeerSigned(circle, peer, &localError)) {
1402 SOSPeerInfoRef peerInfo = SOSPeerInfoCreateCopy(kCFAllocatorDefault, peer, error);
1403 CFArrayAppendValue(appendHere, peerInfo);
1404 CFRelease(peerInfo);
1405 } else if (error != NULL) {
1406 secerror("Error checking concurrence: %@", localError);
1407 }
1408 CFReleaseNull(localError);
1409 });
1410
1411 return true;
1412 }
1413
1414 CFMutableArrayRef SOSCircleCopyConcurringPeers(SOSCircleRef circle, CFErrorRef* error) {
1415 SOSCircleAssertStable(circle);
1416
1417 CFMutableArrayRef concurringPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1418
1419 if (!SOSCircleAppendConcurringPeers(circle, concurringPeers, error))
1420 CFReleaseNull(concurringPeers);
1421
1422 return concurringPeers;
1423 }
1424
1425 SOSFullPeerInfoRef SOSCircleCopyiCloudFullPeerInfoRef(SOSCircleRef circle, CFErrorRef *error) {
1426 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1427 __block CFErrorRef searchError = NULL;
1428 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1429 if (SOSPeerInfoIsCloudIdentity(peer)) {
1430 if (cloud_full_peer == NULL) {
1431 if (searchError) {
1432 secerror("More than one cloud identity found, first had error, trying new one.");
1433 }
1434 CFReleaseNull(searchError);
1435 cloud_full_peer = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, &searchError);
1436 if (!cloud_full_peer) {
1437 secnotice("icloud-identity", "Failed to make FullPeer for iCloud Identity: %@ (%@)", cloud_full_peer, searchError);
1438 }
1439 } else {
1440 secerror("Additional cloud identity found in circle after successful creation: %@", circle);
1441 }
1442 }
1443 });
1444 // If we didn't find one at all, report the error.
1445 if (cloud_full_peer == NULL && searchError == NULL) {
1446 SOSErrorCreate(kSOSErrorNoiCloudPeer, &searchError, NULL, CFSTR("No iCloud identity PeerInfo found in circle"));
1447 secnotice("icloud-identity", "No iCloud identity PeerInfo found in circle");
1448 }
1449 if (error) {
1450 CFTransferRetained(*error, searchError);
1451 }
1452 CFReleaseNull(searchError);
1453 return cloud_full_peer;
1454 }
1455
1456 SOSFullPeerInfoRef SOSCircleCopyiCloudFullPeerInfoVerifier(SOSCircleRef circle, CFErrorRef *error) {
1457 __block CFErrorRef searchError = NULL;
1458 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1459 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1460 // There should only ever be one signing iCloud identity. If there are more we'll take the first one.
1461 if (!cloud_full_peer && SOSPeerInfoIsCloudIdentity(peer) && SOSCircleVerifyPeerSignatureExists(circle, peer)) {
1462 cloud_full_peer = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, &searchError);
1463 }
1464 });
1465 // If we didn't find one at all, report the error.
1466 if (cloud_full_peer == NULL && searchError == NULL) {
1467 SOSErrorCreate(kSOSErrorNoiCloudPeer, &searchError, NULL, CFSTR("No iCloud identity PeerInfo found in circle"));
1468 secnotice("icloud-identity", "No iCloud identity PeerInfo found in circle");
1469 }
1470 if (error) {
1471 CFTransferRetained(*error, searchError);
1472 }
1473 CFReleaseNull(searchError);
1474 return cloud_full_peer;
1475 }
1476
1477 void debugDumpCircle(CFStringRef message, SOSCircleRef circle) {
1478 CFErrorRef error;
1479
1480 secinfo("circledebug", "%@: %@", message, circle);
1481 if (!circle)
1482 return;
1483
1484 CFDataRef derdata = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
1485 if (derdata) {
1486 CFStringRef hex = CFDataCopyHexString(derdata);
1487 secinfo("circledebug", "Full contents: %@", hex);
1488 if (hex) CFRelease(hex);
1489 CFRelease(derdata);
1490 }
1491 }
1492
1493 bool SOSCircleAcceptPeerFromHSA2(SOSCircleRef circle, SecKeyRef userKey, SOSGenCountRef gencount, SecKeyRef pPubKey, CFDataRef signature, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1494 SOSPeerInfoRef peerInfo = SOSFullPeerInfoGetPeerInfo(fpi);
1495 bool res;
1496
1497 CFSetAddValue(circle->peers, peerInfo);
1498
1499 // Gen sign first, then add signature from our approver - remember gensign removes all existing sigs.
1500 res = SOSCircleGenerationSignWithGenCount(circle, userKey, fpi, gencount, error);
1501 if (!res) {
1502 secnotice("circleOps", "Failed to regenerate circle with new gen count: %@", error ? *error : NULL);
1503 return res;
1504 }
1505 res = SOSCircleSetSignature(circle, pPubKey, signature, error);
1506 if (!res) {
1507 secnotice("circleOps", "Failed to set signature: %@", error ? *error : NULL);
1508 return res;
1509 }
1510 res = SOSCircleVerify(circle, pPubKey, error);
1511 if (!res) {
1512 secnotice("circleOps", "Circle failed to validate after peer signature: %@", error ? *error : NULL);
1513 return res;
1514 }
1515 secnotice("circleOps", "Circle accepted successfully");
1516
1517 return true;
1518 }
1519
1520
1521 /*
1522 ccstatus: Not in Circle (1)
1523 Account user public is trusted
1524 Generation Count: [2016-05-19 15:53 4]
1525
1526 */
1527
1528 static inline void logPeerInfo(char *category, SOSCircleRef circle, SecKeyRef pubKey, CFStringRef myPID, SOSPeerInfoRef peer) {
1529 char sigchr = 'v';
1530 if (SOSCircleVerifyPeerSignatureExists(circle, peer)) {
1531 sigchr = 'V';
1532 }
1533 SOSPeerInfoLogState(category, peer, pubKey, myPID, sigchr);
1534 }
1535
1536 void SOSCircleLogState(char *category, SOSCircleRef circle, SecKeyRef pubKey, CFStringRef myPID) {
1537 if(!circle) return;
1538 CFStringRef genString = SOSGenerationCountCopyDescription(SOSCircleGetGeneration(circle));
1539 char sigchr = 'v';
1540 if(pubKey && SOSCircleVerifySignatureExists(circle, pubKey, NULL)) {
1541 sigchr = 'V';
1542 }
1543 secnotice(category, "CIRCLE: [%20@] UserSigned: %c", genString, sigchr);
1544 if(CFSetGetCount(circle->peers) == 0 )
1545 secnotice(category, "Peers In Circle: None");
1546 else{
1547 secnotice(category, "Peers In Circle:");
1548 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
1549 logPeerInfo(category, circle, pubKey, myPID, peer);
1550 });
1551 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1552 logPeerInfo(category, circle, pubKey, myPID, peer);
1553 });
1554 SOSCircleForEachiCloudIdentityPeer(circle, ^(SOSPeerInfoRef peer) {
1555 logPeerInfo(category, circle, pubKey, myPID, peer);
1556 });
1557 }
1558
1559 //applicants
1560 if(CFSetGetCount(circle->applicants) == 0 )
1561 secnotice(category, "Applicants To Circle: None");
1562 else{
1563 secnotice(category, "Applicants To Circle:");
1564
1565 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
1566 SOSPeerInfoLogState(category, peer, pubKey, myPID, 'v');
1567 });
1568 }
1569
1570 //rejected
1571 if(CFSetGetCount(circle->rejected_applicants) == 0)
1572 secnotice(category, "Rejected Applicants To Circle: None");
1573 else{
1574 secnotice(category, "Rejected Applicants To Circle:");
1575 CFSetForEach(circle->rejected_applicants, ^(const void *value) {
1576 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1577 SOSPeerInfoLogState(category, peer, pubKey, myPID, 'v');
1578 });
1579 }
1580 CFReleaseNull(genString);
1581 }
1582