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