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