/ keychain / SecureObjectSync / SOSTransport.m
SOSTransport.m
  1  
  2  #include "keychain/SecureObjectSync/SOSInternal.h"
  3  #include "keychain/SecureObjectSync/SOSKVSKeys.h"
  4  #include "keychain/SecureObjectSync/SOSAccountPriv.h"
  5  #include "keychain/SecureObjectSync/SOSTransport.h"
  6  #include "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
  7  #include "keychain/SecureObjectSync/SOSTransportCircleKVS.h"
  8  #include "keychain/SecureObjectSync/SOSTransportMessageKVS.h"
  9  #include "keychain/SecureObjectSync/SOSTransportMessage.h"
 10  #include "keychain/SecureObjectSync/SOSRing.h"
 11  
 12  #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
 13  #include <utilities/debugging.h>
 14  #include <utilities/SecCFWrappers.h>
 15  #include <CoreFoundation/CFBase.h>
 16  
 17  CFStringRef kKeyParameter = CFSTR("KeyParameter");
 18  CFStringRef kCircle = CFSTR("Circle");
 19  CFStringRef kMessage = CFSTR("Message");
 20  CFStringRef kAlwaysKeys = CFSTR("AlwaysKeys");
 21  CFStringRef kFirstUnlocked = CFSTR("FirstUnlockKeys");
 22  CFStringRef kUnlocked = CFSTR("UnlockedKeys");
 23  extern CFStringRef kSOSAccountDebugScope;
 24  
 25  #define DATE_LENGTH 18
 26  
 27  CFStringRef SOSInterestListCopyDescription(CFArrayRef interests)
 28  {
 29      CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
 30      CFStringAppendFormat(description, NULL, CFSTR("<Interest: "));
 31      
 32      if (interests) {
 33          CFArrayForEach(interests, ^(const void* string) {
 34              if (isString(string))
 35               
 36                  CFStringAppendFormat(description, NULL, CFSTR(" '%@'"), string);
 37          });
 38      }
 39      CFStringAppend(description, CFSTR(">"));
 40  
 41      return description;
 42  }
 43  
 44  
 45  //
 46  // MARK: Key Interest Processing
 47  //
 48  
 49  CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportMessages, sTransportMessages,  ^{
 50      *sTransportMessages = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
 51  });
 52  
 53  CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportKeyParameters, sTransportKeyParameters,  ^{
 54      *sTransportKeyParameters = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
 55  });
 56  
 57  CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportCircles, sTransportCircles,  ^{
 58      *sTransportCircles = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
 59  });
 60  
 61  
 62  void SOSRegisterTransportMessage(SOSMessage* additional) {
 63      if(additional != nil)
 64          CFArrayAppendValue(SOSGetTransportMessages(), (__bridge CFTypeRef)(additional));
 65  }
 66  
 67  void SOSUnregisterTransportMessage(SOSMessage* removal) {
 68      CFArrayRemoveAllValue(SOSGetTransportMessages(), (__bridge CFTypeRef)(removal));
 69  }
 70  
 71  void SOSUnregisterAllTransportMessages() {
 72      CFArrayRemoveAllValues(SOSGetTransportMessages());
 73  }
 74  
 75  void SOSRegisterTransportCircle(SOSCircleStorageTransport* additional) {
 76      if(additional != nil)
 77          CFArrayAppendValue(SOSGetTransportCircles(), (__bridge CFTypeRef)(additional));
 78  }
 79  
 80  void SOSUnregisterTransportCircle(SOSCircleStorageTransport* removal) {
 81      CFArrayRemoveAllValue(SOSGetTransportCircles(), (__bridge CFTypeRef)removal);
 82  }
 83  
 84  void SOSUnregisterAllTransportCircles() {
 85      CFArrayRemoveAllValues(SOSGetTransportCircles());
 86  }
 87  
 88  void SOSRegisterTransportKeyParameter(CKKeyParameter* additional) {
 89      if(additional != nil)
 90          CFArrayAppendValue(SOSGetTransportKeyParameters(), (__bridge CFTypeRef)(additional));
 91  }
 92  
 93  void SOSUnregisterTransportKeyParameter(CKKeyParameter* removal) {
 94      CFArrayRemoveAllValue(SOSGetTransportKeyParameters(), (__bridge CFTypeRef)(removal));
 95  }
 96  
 97  void SOSUnregisterAllTransportKeyParameters() {
 98      CFArrayRemoveAllValues(SOSGetTransportKeyParameters());
 99  }
100  
101  //
102  // Should we be dispatching back to our queue to handle later
103  //
104  
105  
106  void SOSUpdateKeyInterest(SOSAccount* account)
107  {
108      CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
109      CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
110      CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
111      CFMutableDictionaryRef keyDict = CFDictionaryCreateMutableForCFTypes (kCFAllocatorDefault);
112  
113      NSMutableArray *temp = (__bridge NSMutableArray *)(SOSGetTransportKeyParameters());
114  
115      [temp enumerateObjectsUsingBlock:^(CKKeyParameter *value, NSUInteger idx, BOOL * _Nonnull stop) {
116          CKKeyParameter* tKP = (CKKeyParameter*) value;
117          if ([tKP SOSTransportKeyParameterGetAccount:tKP] == account && [tKP SOSTransportKeyParameterGetTransportType:tKP err:NULL] == kKVS) {
118              CKKeyParameter* tkvs = (CKKeyParameter*) value;
119              CFErrorRef localError = NULL;
120  
121              if (![tkvs SOSTransportKeyParameterKVSAppendKeyInterests:tkvs ak:alwaysKeys firstUnLock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]) {
122                  secerror("Error getting key parameters interests %@", localError);
123              }
124              CFReleaseNull(localError);
125          }
126      }];
127      
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);
133  
134      CFReleaseNull(alwaysKeys);
135      CFReleaseNull(afterFirstUnlockKeys);
136      CFReleaseNull(whenUnlockedKeys);
137      alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
138      afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
139      whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
140      
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;
146  
147              if(! [tkvs kvsAppendKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]){
148                  secerror("Error getting circle interests %@", localError);
149              }
150              if(![tkvs kvsAppendRingKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]){
151                  secerror("Error getting ring interests %@", localError);
152              }
153              if(![tkvs kvsAppendDebugKeyInterest:alwaysKeys firstUnlock:afterFirstUnlockKeys unlocked:whenUnlockedKeys err:&localError]) {
154                  secerror("Error getting debug key interests %@", localError);
155              }
156              
157              CFReleaseNull(localError);
158          }
159          
160      });
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);
166      
167      CFReleaseNull(alwaysKeys);
168      CFReleaseNull(afterFirstUnlockKeys);
169      CFReleaseNull(whenUnlockedKeys);
170      alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
171      afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
172      whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
173      
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);
181              }
182              CFReleaseNull(localError);
183          }
184      });
185      
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);
191      
192      //
193      // Log what we are about to do.
194      //
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);
203              }
204          }
205      }
206      secnotice("key-interests", "Updating interests done: %lu", (unsigned long)itemCount);
207  
208      CFStringRef uuid = SOSAccountCopyUUID(account);
209      SOSCloudKeychainUpdateKeys(keyDict, uuid, dispatch_get_global_queue(SOS_TRANSPORT_PRIORITY, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error) {
210          if (error) {
211              secerror("Error updating keys: %@", error);
212              account.key_interests_need_updating = true;
213          } else {
214              account.key_interests_need_updating = false;
215          }
216      });
217      CFReleaseNull(uuid);
218  
219      CFReleaseNull(alwaysKeys);
220      CFReleaseNull(afterFirstUnlockKeys);
221      CFReleaseNull(whenUnlockedKeys);
222      CFReleaseNull(keyParamsDict);
223      CFReleaseNull(circleDict);
224      CFReleaseNull(messageDict);
225      CFReleaseNull(keyDict);
226  }
227  
228  
229  static void showWhatWasHandled(CFDictionaryRef updates, CFMutableArrayRef handledKeys) {
230      
231      CFMutableStringRef updateStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
232      CFMutableStringRef handledKeysStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
233      
234      CFDictionaryForEach(updates, ^(const void *key, const void *value) {
235          if (isString(key)) {
236              CFStringAppendFormat(updateStr, NULL, CFSTR("%@ "), (CFStringRef)key);
237          }
238      });
239      CFArrayForEach(handledKeys, ^(const void *value) {
240          if (isString(value)) {
241              CFStringAppendFormat(handledKeysStr, NULL, CFSTR("%@ "), (CFStringRef)value);
242          }
243      });
244      secinfo("updates", "Updates [%ld]: %@", CFDictionaryGetCount(updates), updateStr);
245      secinfo("updates", "Handled [%ld]: %@", CFArrayGetCount(handledKeys), handledKeysStr);
246      
247      CFReleaseSafe(updateStr);
248      CFReleaseSafe(handledKeysStr);
249  }
250  
251  #define KVS_STATE_INTERVAL 50
252  
253  CF_RETURNS_RETAINED
254  CFMutableArrayRef SOSTransportDispatchMessages(SOSAccountTransaction* txn, CFDictionaryRef updates, CFErrorRef *error){
255      __block SOSAccount* account = txn.account;
256      
257      CFMutableArrayRef handledKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
258      CFStringRef dsid = NULL;
259      
260      if(CFDictionaryGetValueIfPresent(updates, kSOSKVSAccountChangedKey, (const void**)&dsid)){
261          secnotice("accountChange", "SOSTransportDispatchMessages received kSOSKVSAccountChangedKey");
262          // While changing accounts we may modify the key params array. To avoid stepping on ourselves we
263          // copy the list for iteration.  Now modifying the transport outside of the list iteration.
264          CFMutableArrayRef transportsToUse = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
265          
266          CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) {
267              CKKeyParameter* transport = (__bridge CKKeyParameter*) value;
268  
269              if(CFEqualSafe((__bridge CFTypeRef)([transport SOSTransportKeyParameterGetAccount:transport]), (__bridge CFTypeRef)(account))){
270                  CFArrayAppendValue(transportsToUse, (__bridge const void *)(transport));
271              }
272          });
273          
274          CFArrayForEach(transportsToUse, ^(const void *value) {
275              CKKeyParameter* tempTransport = (__bridge CKKeyParameter*) value;
276              
277              CFStringRef accountDSID = (CFStringRef)SOSAccountGetValue(account, kSOSDSIDKey, error);
278              
279              if(accountDSID == NULL){
280                  [tempTransport SOSTransportKeyParameterHandleNewAccount:tempTransport acct:account];
281                  SOSAccountSetValue(account, kSOSDSIDKey, dsid, error);
282                  secdebug("dsid", "Assigning new DSID: %@", dsid);
283              } else if(accountDSID != NULL && CFStringCompare(accountDSID, dsid, 0) != 0 ) {
284                  [tempTransport SOSTransportKeyParameterHandleNewAccount:tempTransport acct:account];
285                  SOSAccountSetValue(account, kSOSDSIDKey, dsid, error);
286                  secdebug("dsid", "Assigning new DSID: %@", dsid);
287              } else {
288                  secdebug("dsid", "DSIDs are the same!");
289              }
290          });
291          
292          CFReleaseNull(transportsToUse);
293      
294          CFArrayAppendValue(handledKeys, kSOSKVSAccountChangedKey);
295      }
296  
297      
298      // Iterate through keys in updates.  Perform circle change update.
299      // Then instantiate circles and engines and peers for all peers that
300      // are receiving a message in updates.
301      CFMutableDictionaryRef circle_peer_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
302      CFMutableDictionaryRef circle_circle_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
303      CFMutableDictionaryRef circle_retirement_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
304      CFMutableDictionaryRef ring_update_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
305      CFMutableDictionaryRef debug_info_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
306      __block CFMutableDictionaryRef config_message_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
307      
308      __block CFDataRef newParameters = NULL;
309      __block bool initial_sync = false;
310      __block bool new_account = false;
311      
312      CFDictionaryForEach(updates, ^(const void *key, const void *value) {
313          CFStringRef circle_name = NULL;
314          CFStringRef ring_name = NULL;
315          CFStringRef peer_info_name = NULL;
316          CFStringRef from_name = NULL;
317          CFStringRef to_name = NULL;
318          CFStringRef backup_name = NULL;
319          
320          require_quiet(isString(key), errOut);
321          switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &peer_info_name, &ring_name, &backup_name, &from_name, &to_name)) {
322              case kCircleKey:
323                  CFDictionarySetValue(circle_circle_messages_table, circle_name, value);
324                  break;
325              case kInitialSyncKey:
326                  initial_sync = true;
327                  break;
328              case kParametersKey:
329                  if (isData(value)) {
330                      newParameters = (CFDataRef) CFRetainSafe(value);
331                  }
332                  break;
333              case kMessageKey: {
334                  CFMutableDictionaryRef circle_messages = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_peer_messages_table, circle_name);
335                  CFDictionarySetValue(circle_messages, from_name, value);
336                  break;
337              }
338              case kRetirementKey: {
339                  CFMutableDictionaryRef circle_retirements = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_retirement_messages_table, circle_name);
340                  CFDictionarySetValue(circle_retirements, from_name, value);
341                  break;
342              }
343              case kAccountChangedKey:
344                  new_account = true;
345                  break;
346              case kRingKey:
347                  if(isString(ring_name))
348                      CFDictionarySetValue(ring_update_message_table, ring_name, value);
349                  break;
350              case kDebugInfoKey:
351                  CFDictionarySetValue(debug_info_message_table, peer_info_name, value);
352                  break;
353              case kLastCircleKey:
354              case kLastKeyParameterKey:
355              case kUnknownKey:
356                  secnotice("updates", "Unknown key '%@', ignoring", key);
357                  break;
358          }
359  
360      errOut:
361          CFReleaseNull(circle_name);
362          CFReleaseNull(from_name);
363          CFReleaseNull(to_name);
364          CFReleaseNull(ring_name);
365          CFReleaseNull(peer_info_name);
366          CFReleaseNull(backup_name);
367          
368          if (error && *error)
369              secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error);
370      });
371      
372      
373      if (newParameters) {
374          CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) {
375              CKKeyParameter* tkvs = (__bridge CKKeyParameter*) value;
376              CFErrorRef localError = NULL;
377              if([[tkvs SOSTransportKeyParameterGetAccount:tkvs] isEqual:account]){
378                  if(![tkvs SOSTransportKeyParameterHandleKeyParameterChanges:tkvs data:newParameters err:localError])
379                      secerror("Transport failed to handle new key parameters: %@", localError);
380              }
381          });
382          CFArrayAppendValue(handledKeys, kSOSKVSKeyParametersKey);
383      }
384      CFReleaseNull(newParameters);
385      
386      if(initial_sync){
387          CFArrayAppendValue(handledKeys, kSOSKVSInitialSyncKey);
388      }
389  
390      if(CFDictionaryGetCount(debug_info_message_table)) {
391          /* check for a newly set circle debug scope */
392          CFTypeRef debugScope = CFDictionaryGetValue(debug_info_message_table, kSOSAccountDebugScope);
393          if (debugScope) {
394              if(isString(debugScope)){
395                  ApplyScopeListForID(debugScope, kScopeIDCircle);
396              }else if(isDictionary(debugScope)){
397                  ApplyScopeDictionaryForID(debugScope, kScopeIDCircle);
398              }
399          }
400          CFStringRef debugInfoKey = SOSDebugInfoKeyCreateWithTypeName(kSOSAccountDebugScope);
401          CFArrayAppendValue(handledKeys, debugInfoKey);
402          CFReleaseNull(debugInfoKey);
403      }
404      
405      if(CFDictionaryGetCount(circle_retirement_messages_table)) {
406          CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
407              SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
408              if([[tkvs getAccount] isEqual:account]){
409                  CFErrorRef localError = NULL;
410                  CFDictionaryRef handledRetirementKeys = [tkvs handleRetirementMessages:circle_retirement_messages_table err:error];
411                  if(handledRetirementKeys == NULL){
412                      secerror("Transport failed to handle retirement messages: %@", localError);
413                  } else {
414                      CFDictionaryForEach(handledRetirementKeys, ^(const void *key, const void *value) {
415                          CFStringRef circle_name = (CFStringRef)key;
416                          CFArrayRef handledPeerIDs = (CFArrayRef)value;
417                          CFArrayForEach(handledPeerIDs, ^(const void *value) {
418                              CFStringRef peer_id = (CFStringRef)value;
419                              CFStringRef keyHandled = SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name, peer_id);
420                              CFArrayAppendValue(handledKeys, keyHandled);
421                              CFReleaseNull(keyHandled);
422                          });
423                      });
424                  }
425                  CFReleaseNull(handledRetirementKeys);
426                  CFReleaseNull(localError);
427              }
428          });
429      }
430      if(CFDictionaryGetCount(circle_peer_messages_table)) {
431          CFArrayForEach(SOSGetTransportMessages(), ^(const void *value) {
432              SOSMessage* tmsg = (__bridge SOSMessage*) value;
433              CFDictionaryRef circleToPeersHandled = NULL;
434              CFErrorRef handleMessagesError = NULL;
435              CFErrorRef flushError = NULL;
436  
437              if(!([([tmsg SOSTransportMessageGetAccount]) isEqual:account])){
438                  CFReleaseNull(flushError);
439                  CFReleaseNull(circleToPeersHandled);
440                  CFReleaseNull(handleMessagesError);
441                  return;
442              }
443              circleToPeersHandled = [tmsg SOSTransportMessageHandlePeerMessageReturnsHandledCopy:tmsg peerMessages:circle_peer_messages_table err:&handleMessagesError];
444              if(!circleToPeersHandled){
445                  secnotice("msg", "No messages handled: %@", handleMessagesError);
446                  CFReleaseNull(flushError);
447                  CFReleaseNull(circleToPeersHandled);
448                  CFReleaseNull(handleMessagesError);
449                  return;
450              }
451              CFArrayRef handledPeers = asArray(CFDictionaryGetValue(circleToPeersHandled, [tmsg SOSTransportMessageGetCircleName]), NULL);
452  
453              if (handledPeers) {
454                  CFArrayForEach(handledPeers, ^(const void *value) {
455                      CFStringRef peerID = asString(value, NULL);
456                      if (peerID) {
457                          CFStringRef kvsHandledKey = SOSMessageKeyCreateFromPeerToTransport(tmsg, (__bridge CFStringRef) account.peerID, peerID);
458                          if (kvsHandledKey) {
459                              CFArrayAppendValue(handledKeys, kvsHandledKey);
460                          }
461                          CFReleaseNull(kvsHandledKey);
462                      }
463                  });
464              }
465  
466              if(![tmsg SOSTransportMessageFlushChanges:tmsg err:&flushError])
467                  secnotice("msg", "Flush failed: %@", flushError);
468  
469              CFReleaseNull(flushError);
470              CFReleaseNull(circleToPeersHandled);
471              CFReleaseNull(handleMessagesError);
472          });
473      }
474      if(CFDictionaryGetCount(circle_circle_messages_table)) {
475          CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
476              SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
477              if([[tkvs getAccount] isEqual: account]){
478                  CFArrayRef handleCircleMessages = [tkvs handleCircleMessagesAndReturnHandledCopy:circle_circle_messages_table err:error];
479                  CFErrorRef localError = NULL;
480                  if(handleCircleMessages == NULL){
481                      secerror("Transport failed to handle circle messages: %@", localError);
482                  } else if(CFArrayGetCount(handleCircleMessages) == 0) {
483                      if(CFDictionaryGetCount(circle_circle_messages_table) != 0) {
484                          secerror("Transport failed to process all circle messages: (%ld/%ld) %@",
485                                   CFArrayGetCount(handleCircleMessages),
486                                   CFDictionaryGetCount(circle_circle_messages_table), localError);
487                      } else {
488                          secnotice("circle", "Transport handled no circle messages");
489                      }
490                  } else {
491                      CFArrayForEach(handleCircleMessages, ^(const void *value) {
492                          CFStringRef keyHandled = SOSCircleKeyCreateWithName((CFStringRef)value, error);
493                          CFArrayAppendValue(handledKeys, keyHandled);
494                          CFReleaseNull(keyHandled);
495                      });
496                  }
497  
498                  CFReleaseNull(handleCircleMessages);
499                  CFReleaseNull(localError);
500              }
501              
502          });
503      }
504      if(CFDictionaryGetCount(ring_update_message_table)){
505          CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) {
506              SOSKVSCircleStorageTransport* tkvs = (__bridge SOSKVSCircleStorageTransport*) value;
507              if([[tkvs getAccount] isEqual:account]){
508                  CFErrorRef localError = NULL;
509                  CFMutableArrayRef handledRingMessages = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
510  
511                  CFDictionaryForEach(ring_update_message_table, ^(const void *key, const void *value) {
512                      CFDataRef ringData = asData(value, NULL);
513                      SOSRingRef ring = SOSRingCreateFromData(error, ringData);
514  
515                      if(SOSAccountUpdateRingFromRemote(account, ring, error)){
516                          CFArrayAppendValue(handledRingMessages, key);
517                      }
518                      CFReleaseNull(ring);
519                  });
520                  if(CFArrayGetCount(handledRingMessages) == 0){
521                      secerror("Transport failed to handle ring messages: %@", localError);
522                  } else {
523                      CFArrayForEach(handledRingMessages, ^(const void *value) {
524                          CFStringRef ring_name = (CFStringRef)value;
525                          CFStringRef keyHandled = SOSRingKeyCreateWithRingName(ring_name);
526                          CFArrayAppendValue(handledKeys, keyHandled);
527                          CFReleaseNull(keyHandled);
528                      });
529                  }
530                  CFReleaseNull(handledRingMessages);
531                  CFReleaseNull(localError);
532              }
533          });
534      }
535  
536      CFReleaseNull(circle_retirement_messages_table);
537      CFReleaseNull(circle_circle_messages_table);
538      CFReleaseNull(circle_peer_messages_table);
539      CFReleaseNull(debug_info_message_table);
540      CFReleaseNull(ring_update_message_table);
541      CFReleaseNull(debug_info_message_table);
542      CFReleaseNull(config_message_table);
543      showWhatWasHandled(updates, handledKeys);
544      
545      return handledKeys;
546  }
547