2 #include <Security/SecureObjectSync/SOSInternal.h>
3 #include <Security/SecureObjectSync/SOSKVSKeys.h>
4 #include <Security/SecureObjectSync/SOSAccountPriv.h>
5 #include <Security/SecureObjectSync/SOSTransport.h>
6 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
7 #include <Security/SecureObjectSync/SOSTransportCircleKVS.h>
8 #include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
9 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
10 #include <Security/SecureObjectSync/SOSTransportMessage.h>
11 #include <Security/SecureObjectSync/SOSRing.h>
13 #include <SOSCloudKeychainClient.h>
14 #include <utilities/debugging.h>
15 #include <utilities/SecCFWrappers.h>
16 #include <CoreFoundation/CFBase.h>
18 CFStringRef kKeyParameter = CFSTR("KeyParameter");
19 CFStringRef kCircle = CFSTR("Circle");
20 CFStringRef kMessage = CFSTR("Message");
21 CFStringRef kAlwaysKeys = CFSTR("AlwaysKeys");
22 CFStringRef kFirstUnlocked = CFSTR("FirstUnlockKeys");
23 CFStringRef kUnlocked = CFSTR("UnlockedKeys");
24 extern CFStringRef kSOSAccountDebugScope;
26 #define DATE_LENGTH 18
28 CFStringRef SOSInterestListCopyDescription(CFArrayRef interests)
30 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
31 CFStringAppendFormat(description, NULL, CFSTR("<Interest: "));
34 CFArrayForEach(interests, ^(const void* string) {
37 CFStringAppendFormat(description, NULL, CFSTR(" '%@'"), string);
40 CFStringAppend(description, CFSTR(">"));
47 // MARK: Key Interest Processing
50 CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportMessages, sTransportMessages, ^{
51 *sTransportMessages = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
54 CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportKeyParameters, sTransportKeyParameters, ^{
55 *sTransportKeyParameters = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
58 CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportCircles, sTransportCircles, ^{
59 *sTransportCircles = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
63 void SOSRegisterTransportMessage(SOSMessage* additional) {
65 CFArrayAppendValue(SOSGetTransportMessages(), (__bridge CFTypeRef)(additional));
68 void SOSUnregisterTransportMessage(SOSMessage* removal) {
69 CFArrayRemoveAllValue(SOSGetTransportMessages(), (__bridge CFTypeRef)(removal));
72 void SOSUnregisterAllTransportMessages() {
73 CFArrayRemoveAllValues(SOSGetTransportMessages());
76 void SOSRegisterTransportCircle(SOSCircleStorageTransport* additional) {
78 CFArrayAppendValue(SOSGetTransportCircles(), (__bridge CFTypeRef)(additional));
81 void SOSUnregisterTransportCircle(SOSCircleStorageTransport* removal) {
82 CFArrayRemoveAllValue(SOSGetTransportCircles(), (__bridge CFTypeRef)removal);
85 void SOSUnregisterAllTransportCircles() {
86 CFArrayRemoveAllValues(SOSGetTransportCircles());
89 void SOSRegisterTransportKeyParameter(CKKeyParameter* additional) {
91 CFArrayAppendValue(SOSGetTransportKeyParameters(), (__bridge CFTypeRef)(additional));
94 void SOSUnregisterTransportKeyParameter(CKKeyParameter* removal) {
95 CFArrayRemoveAllValue(SOSGetTransportKeyParameters(), (__bridge CFTypeRef)(removal));
98 void SOSUnregisterAllTransportKeyParameters() {
99 CFArrayRemoveAllValues(SOSGetTransportKeyParameters());
103 // Should we be dispatching back to our queue to handle later
105 void SOSUpdateKeyInterest(SOSAccount* account)
107 CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
108 CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
109 CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
110 CFMutableDictionaryRef keyDict = CFDictionaryCreateMutableForCFTypes (kCFAllocatorDefault);
112 NSMutableArray *temp = (__bridge NSMutableArray *)(SOSGetTransportKeyParameters());
114 [temp enumerateObjectsUsingBlock:^(CKKeyParameter *value, NSUInteger idx, BOOL * _Nonnull stop) {
115 CKKeyParameter* tKP = (CKKeyParameter*) value;
116 if ([tKP SOSTransportKeyParameterGetAccount:tKP] == account && [tKP SOSTransportKeyParameterGetTransportType:tKP err:NULL] == kKVS) {
117 CKKeyParameter* tkvs = (CKKeyParameter*) value;
118 CFErrorRef localError = NULL;
120 if (![tkvs SOSTransportKeyParameterKVSAppendKeyInterests:tkvs ak:alwaysKeys firstUnLock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]) {
121 secerror("Error getting key parameters interests %@", localError);
123 CFReleaseNull(localError);
128 CFMutableDictionaryRef keyParamsDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
129 CFDictionarySetValue(keyParamsDict, kAlwaysKeys, alwaysKeys);
130 CFDictionarySetValue(keyParamsDict, kFirstUnlocked, afterFirstUnlockKeys);
131 CFDictionarySetValue(keyParamsDict, kUnlocked, whenUnlockedKeys);
132 CFDictionarySetValue(keyDict, kKeyParameter, keyParamsDict);
134 CFReleaseNull(alwaysKeys);
135 CFReleaseNull(afterFirstUnlockKeys);
136 CFReleaseNull(whenUnlockedKeys);
137 alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
138 afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
139 whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
141 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
142 SOSKVSCircleStorageTransport *transport = (__bridge SOSKVSCircleStorageTransport*)value;
143 if ( [[transport getAccount] isEqual: account] ) {
144 SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
145 CFErrorRef localError = NULL;
147 if(! [tkvs kvsAppendKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]){
148 secerror("Error getting circle interests %@", localError);
150 if(![tkvs kvsAppendRingKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]){
151 secerror("Error getting ring interests %@", localError);
153 if(![tkvs kvsAppendDebugKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]) {
154 secerror("Error getting debug key interests %@", localError);
157 CFReleaseNull(localError);
161 CFMutableDictionaryRef circleDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
162 CFDictionarySetValue(circleDict, kAlwaysKeys, alwaysKeys);
163 CFDictionarySetValue(circleDict, kFirstUnlocked, afterFirstUnlockKeys);
164 CFDictionarySetValue(circleDict, kUnlocked, whenUnlockedKeys);
165 CFDictionarySetValue(keyDict, kCircle, circleDict);
167 CFReleaseNull(alwaysKeys);
168 CFReleaseNull(afterFirstUnlockKeys);
169 CFReleaseNull(whenUnlockedKeys);
170 alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
171 afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
172 whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
174 CFArrayForEach(SOSGetTransportMessages(), ^(const void *value) {
175 SOSMessage* transport = (__bridge SOSMessage*)value;
176 if ([transport SOSTransportMessageGetAccount] == account && [transport SOSTransportMessageGetTransportType] == kKVS) {
177 CFErrorRef localError = NULL;
178 SOSMessageKVS* tks = (__bridge SOSMessageKVS*)value;
179 if(![tks SOSTransportMessageKVSAppendKeyInterest:tks ak:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]){
180 secerror("Error getting message interests %@", localError);
182 CFReleaseNull(localError);
186 CFMutableDictionaryRef messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
187 CFDictionarySetValue(messageDict, kAlwaysKeys, alwaysKeys);
188 CFDictionarySetValue(messageDict, kFirstUnlocked, afterFirstUnlockKeys);
189 CFDictionarySetValue(messageDict, kUnlocked, whenUnlockedKeys);
190 CFDictionarySetValue(keyDict, kMessage, messageDict);
193 // Log what we are about to do.
195 NSUInteger itemCount = 0;
196 for (NSString *subsystem in @[(__bridge id)kMessage, (__bridge id)kCircle, (__bridge id)kKeyParameter]) {
197 secnotice("key-interests", "Updating interests: %@", subsystem);
198 for (NSString *lockState in @[(__bridge id)kAlwaysKeys, (__bridge id)kFirstUnlocked, (__bridge id)kUnlocked]) {
199 NSArray *items = ((__bridge NSDictionary *)keyDict)[subsystem][lockState];
200 itemCount += items.count;
201 for (NSString *item in items) {
202 secnotice("key-interests", " key-intrest: %@->%@: %@", subsystem, lockState, item);
206 secnotice("key-interests", "Updating interests done: %lu", (unsigned long)itemCount);
208 CFStringRef uuid = SOSAccountCopyUUID(account);
209 SOSCloudKeychainUpdateKeys(keyDict, uuid, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error) {
211 secerror("Error updating keys: %@", error);
216 CFReleaseNull(alwaysKeys);
217 CFReleaseNull(afterFirstUnlockKeys);
218 CFReleaseNull(whenUnlockedKeys);
219 CFReleaseNull(keyParamsDict);
220 CFReleaseNull(circleDict);
221 CFReleaseNull(messageDict);
222 CFReleaseNull(keyDict);
226 static void showWhatWasHandled(CFDictionaryRef updates, CFMutableArrayRef handledKeys) {
228 CFMutableStringRef updateStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
229 CFMutableStringRef handledKeysStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
231 CFDictionaryForEach(updates, ^(const void *key, const void *value) {
233 CFStringAppendFormat(updateStr, NULL, CFSTR("%@ "), (CFStringRef)key);
236 CFArrayForEach(handledKeys, ^(const void *value) {
237 if (isString(value)) {
238 CFStringAppendFormat(handledKeysStr, NULL, CFSTR("%@ "), (CFStringRef)value);
241 secinfo("updates", "Updates [%ld]: %@", CFDictionaryGetCount(updates), updateStr);
242 secinfo("updates", "Handled [%ld]: %@", CFArrayGetCount(handledKeys), handledKeysStr);
244 CFReleaseSafe(updateStr);
245 CFReleaseSafe(handledKeysStr);
248 #define KVS_STATE_INTERVAL 50
251 CFMutableArrayRef SOSTransportDispatchMessages(SOSAccountTransaction* txn, CFDictionaryRef updates, CFErrorRef *error){
252 __block SOSAccount* account = txn.account;
254 CFMutableArrayRef handledKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
255 CFStringRef dsid = NULL;
257 if(CFDictionaryGetValueIfPresent(updates, kSOSKVSAccountChangedKey, (const void**)&dsid)){
258 secnotice("accountChange", "SOSTransportDispatchMessages received kSOSKVSAccountChangedKey");
259 // While changing accounts we may modify the key params array. To avoid stepping on ourselves we
260 // copy the list for iteration. Now modifying the transport outside of the list iteration.
261 CFMutableArrayRef transportsToUse = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
263 CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) {
264 CKKeyParameter* transport = (__bridge CKKeyParameter*) value;
266 if(CFEqualSafe((__bridge CFTypeRef)([transport SOSTransportKeyParameterGetAccount:transport]), (__bridge CFTypeRef)(account))){
267 CFArrayAppendValue(transportsToUse, (__bridge const void *)(transport));
271 CFArrayForEach(transportsToUse, ^(const void *value) {
272 CKKeyParameter* tempTransport = (__bridge CKKeyParameter*) value;
274 CFStringRef accountDSID = (CFStringRef)SOSAccountGetValue(account, kSOSDSIDKey, error);
276 if(accountDSID == NULL){
277 [tempTransport SOSTransportKeyParameterHandleNewAccount:tempTransport acct:account];
278 SOSAccountSetValue(account, kSOSDSIDKey, dsid, error);
279 secdebug("dsid", "Assigning new DSID: %@", dsid);
280 } else if(accountDSID != NULL && CFStringCompare(accountDSID, dsid, 0) != 0 ) {
281 [tempTransport SOSTransportKeyParameterHandleNewAccount:tempTransport acct:account];
282 SOSAccountSetValue(account, kSOSDSIDKey, dsid, error);
283 secdebug("dsid", "Assigning new DSID: %@", dsid);
285 secdebug("dsid", "DSIDs are the same!");
289 CFReleaseNull(transportsToUse);
291 CFArrayAppendValue(handledKeys, kSOSKVSAccountChangedKey);
295 // Iterate through keys in updates. Perform circle change update.
296 // Then instantiate circles and engines and peers for all peers that
297 // are receiving a message in updates.
298 CFMutableDictionaryRef circle_peer_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
299 CFMutableDictionaryRef circle_circle_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
300 CFMutableDictionaryRef circle_retirement_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
301 CFMutableDictionaryRef ring_update_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
302 CFMutableDictionaryRef debug_info_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
303 __block CFMutableDictionaryRef config_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
305 __block CFDataRef newParameters = NULL;
306 __block bool initial_sync = false;
307 __block bool new_account = false;
309 CFDictionaryForEach(updates, ^(const void *key, const void *value) {
310 CFStringRef circle_name = NULL;
311 CFStringRef ring_name = NULL;
312 CFStringRef peer_info_name = NULL;
313 CFStringRef from_name = NULL;
314 CFStringRef to_name = NULL;
315 CFStringRef backup_name = NULL;
317 require_quiet(isString(key), errOut);
318 switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &peer_info_name, &ring_name, &backup_name, &from_name, &to_name)) {
320 CFDictionarySetValue(circle_circle_messages_table, circle_name, value);
322 case kInitialSyncKey:
327 newParameters = (CFDataRef) CFRetainSafe(value);
331 CFMutableDictionaryRef circle_messages = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_peer_messages_table, circle_name);
332 CFDictionarySetValue(circle_messages, from_name, value);
335 case kRetirementKey: {
336 CFMutableDictionaryRef circle_retirements = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_retirement_messages_table, circle_name);
337 CFDictionarySetValue(circle_retirements, from_name, value);
340 case kAccountChangedKey:
344 if(isString(ring_name))
345 CFDictionarySetValue(ring_update_message_table, ring_name, value);
348 CFDictionarySetValue(debug_info_message_table, peer_info_name, value);
351 case kLastKeyParameterKey:
353 secnotice("updates", "Unknown key '%@', ignoring", key);
358 CFReleaseNull(circle_name);
359 CFReleaseNull(from_name);
360 CFReleaseNull(to_name);
361 CFReleaseNull(ring_name);
362 CFReleaseNull(peer_info_name);
363 CFReleaseNull(backup_name);
366 secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error);
371 CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) {
372 CKKeyParameter* tkvs = (__bridge CKKeyParameter*) value;
373 CFErrorRef localError = NULL;
374 if([[tkvs SOSTransportKeyParameterGetAccount:tkvs] isEqual:account]){
375 if(![tkvs SOSTransportKeyParameterHandleKeyParameterChanges:tkvs data:newParameters err:localError])
376 secerror("Transport failed to handle new key parameters: %@", localError);
379 CFArrayAppendValue(handledKeys, kSOSKVSKeyParametersKey);
381 CFReleaseNull(newParameters);
384 CFArrayAppendValue(handledKeys, kSOSKVSInitialSyncKey);
387 if(CFDictionaryGetCount(debug_info_message_table)) {
388 /* check for a newly set circle debug scope */
389 CFTypeRef debugScope = CFDictionaryGetValue(debug_info_message_table, kSOSAccountDebugScope);
391 if(isString(debugScope)){
392 ApplyScopeListForID(debugScope, kScopeIDCircle);
393 }else if(isDictionary(debugScope)){
394 ApplyScopeDictionaryForID(debugScope, kScopeIDCircle);
397 CFStringRef debugInfoKey = SOSDebugInfoKeyCreateWithTypeName(kSOSAccountDebugScope);
398 CFArrayAppendValue(handledKeys, debugInfoKey);
399 CFReleaseNull(debugInfoKey);
402 if(CFDictionaryGetCount(circle_retirement_messages_table)) {
403 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
404 SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
405 if([[tkvs getAccount] isEqual:account]){
406 CFErrorRef localError = NULL;
407 CFDictionaryRef handledRetirementKeys = [tkvs handleRetirementMessages:circle_retirement_messages_table err:error];
408 if(handledRetirementKeys == NULL){
409 secerror("Transport failed to handle retirement messages: %@", localError);
411 CFDictionaryForEach(handledRetirementKeys, ^(const void *key, const void *value) {
412 CFStringRef circle_name = (CFStringRef)key;
413 CFArrayRef handledPeerIDs = (CFArrayRef)value;
414 CFArrayForEach(handledPeerIDs, ^(const void *value) {
415 CFStringRef peer_id = (CFStringRef)value;
416 CFStringRef keyHandled = SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name, peer_id);
417 CFArrayAppendValue(handledKeys, keyHandled);
418 CFReleaseNull(keyHandled);
422 CFReleaseNull(handledRetirementKeys);
423 CFReleaseNull(localError);
427 if(CFDictionaryGetCount(circle_peer_messages_table)) {
428 CFArrayForEach(SOSGetTransportMessages(), ^(const void *value) {
429 SOSMessage* tmsg = (__bridge SOSMessage*) value;
430 CFDictionaryRef circleToPeersHandled = NULL;
431 CFErrorRef handleMessagesError = NULL;
432 CFErrorRef flushError = NULL;
434 if(!([([tmsg SOSTransportMessageGetAccount]) isEqual:account])){
435 CFReleaseNull(flushError);
436 CFReleaseNull(circleToPeersHandled);
437 CFReleaseNull(handleMessagesError);
440 circleToPeersHandled = [tmsg SOSTransportMessageHandlePeerMessageReturnsHandledCopy:tmsg peerMessages:circle_peer_messages_table err:&handleMessagesError];
441 if(!circleToPeersHandled){
442 secnotice("msg", "No messages handled: %@", handleMessagesError);
443 CFReleaseNull(flushError);
444 CFReleaseNull(circleToPeersHandled);
445 CFReleaseNull(handleMessagesError);
448 CFArrayRef handledPeers = asArray(CFDictionaryGetValue(circleToPeersHandled, [tmsg SOSTransportMessageGetCircleName]), NULL);
451 CFArrayForEach(handledPeers, ^(const void *value) {
452 CFStringRef peerID = asString(value, NULL);
454 CFStringRef kvsHandledKey = SOSMessageKeyCreateFromPeerToTransport(tmsg, (__bridge CFStringRef) account.peerID, peerID);
456 CFArrayAppendValue(handledKeys, kvsHandledKey);
458 CFReleaseNull(kvsHandledKey);
463 if(![tmsg SOSTransportMessageFlushChanges:tmsg err:&flushError])
464 secnotice("msg", "Flush failed: %@", flushError);
466 CFReleaseNull(flushError);
467 CFReleaseNull(circleToPeersHandled);
468 CFReleaseNull(handleMessagesError);
471 if(CFDictionaryGetCount(circle_circle_messages_table)) {
472 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
473 SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
474 if([[tkvs getAccount] isEqual: account]){
475 CFArrayRef handleCircleMessages = [tkvs handleCircleMessagesAndReturnHandledCopy:circle_circle_messages_table err:error];
476 CFErrorRef localError = NULL;
477 if(handleCircleMessages == NULL){
478 secerror("Transport failed to handle circle messages: %@", localError);
479 } else if(CFArrayGetCount(handleCircleMessages) == 0) {
480 if(CFDictionaryGetCount(circle_circle_messages_table) != 0) {
481 secerror("Transport failed to process all circle messages: (%ld/%ld) %@",
482 CFArrayGetCount(handleCircleMessages),
483 CFDictionaryGetCount(circle_circle_messages_table), localError);
485 secnotice("circle", "Transport handled no circle messages");
488 CFArrayForEach(handleCircleMessages, ^(const void *value) {
489 CFStringRef keyHandled = SOSCircleKeyCreateWithName((CFStringRef)value, error);
490 CFArrayAppendValue(handledKeys, keyHandled);
491 CFReleaseNull(keyHandled);
495 CFReleaseNull(handleCircleMessages);
496 CFReleaseNull(localError);
501 if(CFDictionaryGetCount(ring_update_message_table)){
502 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
503 SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
504 if([[tkvs getAccount] isEqual:account]){
505 CFErrorRef localError = NULL;
506 CFMutableArrayRef handledRingMessages = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
508 CFDictionaryForEach(ring_update_message_table, ^(const void *key, const void *value) {
509 CFDataRef ringData = asData(value, NULL);
510 SOSRingRef ring = SOSRingCreateFromData(error, ringData);
512 if(SOSAccountUpdateRingFromRemote(account, ring, error)){
513 CFArrayAppendValue(handledRingMessages, key);
517 if(CFArrayGetCount(handledRingMessages) == 0){
518 secerror("Transport failed to handle ring messages: %@", localError);
520 CFArrayForEach(handledRingMessages, ^(const void *value) {
521 CFStringRef ring_name = (CFStringRef)value;
522 CFStringRef keyHandled = SOSRingKeyCreateWithRingName(ring_name);
523 CFArrayAppendValue(handledKeys, keyHandled);
524 CFReleaseNull(keyHandled);
527 CFReleaseNull(handledRingMessages);
528 CFReleaseNull(localError);
533 CFReleaseNull(circle_retirement_messages_table);
534 CFReleaseNull(circle_circle_messages_table);
535 CFReleaseNull(circle_peer_messages_table);
536 CFReleaseNull(debug_info_message_table);
537 CFReleaseNull(ring_update_message_table);
538 CFReleaseNull(debug_info_message_table);
539 CFReleaseNull(config_message_table);
540 showWhatWasHandled(updates, handledKeys);