1 2#include <SecureObjectSync/SOSInternal.h> 3#include <SecureObjectSync/SOSKVSKeys.h> 4#include <SecureObjectSync/SOSAccountPriv.h> 5#include <SecureObjectSync/SOSTransport.h> 6#include <SecureObjectSync/SOSTransportKeyParameterKVS.h> 7#include <SecureObjectSync/SOSTransportCircleKVS.h> 8#include <SecureObjectSync/SOSTransportMessageKVS.h> 9#include <SOSCloudKeychainClient.h> 10#include <utilities/debugging.h> 11 12CFStringRef kKeyParameter = CFSTR("KeyParameter"); 13CFStringRef kCircle = CFSTR("Circle"); 14CFStringRef kMessage = CFSTR("Message"); 15CFStringRef kAlwaysKeys = CFSTR("AlwaysKeys"); 16CFStringRef kFirstUnlocked = CFSTR("FirstUnlockKeys"); 17CFStringRef kUnlocked = CFSTR("UnlockedKeys"); 18 19 20 21CFStringRef SOSInterestListCopyDescription(CFArrayRef interests) 22{ 23 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0); 24 CFStringAppendFormat(description, NULL, CFSTR("<Interest: ")); 25 26 CFArrayForEach(interests, ^(const void* string) { 27 if (isString(string)) 28 CFStringAppendFormat(description, NULL, CFSTR(" '%@'"), string); 29 }); 30 CFStringAppend(description, CFSTR(">")); 31 32 return description; 33} 34 35 36// 37// MARK: Key Interest Processing 38// 39 40CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportMessages, sTransportMessages, ^{ 41 *sTransportMessages = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 42}); 43 44CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportKeyParameters, sTransportKeyParameters, ^{ 45 *sTransportKeyParameters = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 46}); 47 48CFGiblisGetSingleton(CFMutableArrayRef, SOSGetTransportCircles, sTransportCircles, ^{ 49 *sTransportCircles = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 50}); 51 52 53void SOSRegisterTransportMessage(SOSTransportMessageRef additional) { 54 CFArrayAppendValue(SOSGetTransportMessages(), additional); 55} 56 57void SOSUnregisterTransportMessage(SOSTransportMessageRef removal) { 58 CFArrayRemoveAllValue(SOSGetTransportMessages(), removal); 59} 60 61void SOSUnregisterAllTransportMessages() { 62 CFArrayRemoveAllValues(SOSGetTransportMessages()); 63} 64 65void SOSRegisterTransportCircle(SOSTransportCircleRef additional) { 66 CFArrayAppendValue(SOSGetTransportCircles(), additional); 67} 68 69void SOSUnregisterTransportCircle(SOSTransportCircleRef removal) { 70 CFArrayRemoveAllValue(SOSGetTransportCircles(), removal); 71} 72 73void SOSUnregisterAllTransportCircles() { 74 CFArrayRemoveAllValues(SOSGetTransportCircles()); 75} 76 77void SOSRegisterTransportKeyParameter(SOSTransportKeyParameterRef additional) { 78 CFArrayAppendValue(SOSGetTransportKeyParameters(), additional); 79} 80 81void SOSUnregisterTransportKeyParameter(SOSTransportKeyParameterRef removal) { 82 CFArrayRemoveAllValue(SOSGetTransportKeyParameters(), removal); 83} 84 85void SOSUnregisterAllTransportKeyParameters() { 86 CFArrayRemoveAllValues(SOSGetTransportKeyParameters()); 87} 88 89// 90// Should we be dispatching back to our queue to handle later 91// 92void SOSUpdateKeyInterest(void) 93{ 94 CFMutableArrayRef alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 95 CFMutableArrayRef afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 96 CFMutableArrayRef whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 97 CFMutableDictionaryRef keyDict = CFDictionaryCreateMutableForCFTypes (kCFAllocatorDefault); 98 99 CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) { 100 SOSTransportKeyParameterKVSRef tkvs = (SOSTransportKeyParameterKVSRef) value; 101 CFErrorRef localError = NULL; 102 103 if (!SOSTransportKeyParameterKVSAppendKeyInterests(tkvs, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys, &localError)) { 104 secerror("Error getting key parameters interests %@", localError); 105 } 106 CFReleaseNull(localError); 107 }); 108 CFMutableDictionaryRef keyParamsDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 109 CFDictionarySetValue(keyParamsDict, kAlwaysKeys, alwaysKeys); 110 CFDictionarySetValue(keyParamsDict, kFirstUnlocked, afterFirstUnlockKeys); 111 CFDictionarySetValue(keyParamsDict, kUnlocked, whenUnlockedKeys); 112 CFDictionarySetValue(keyDict, kKeyParameter, keyParamsDict); 113 CFErrorRef updateError = NULL; 114 CFReleaseNull(alwaysKeys); 115 CFReleaseNull(afterFirstUnlockKeys); 116 CFReleaseNull(whenUnlockedKeys); 117 alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 118 afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 119 whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 120 121 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) { 122 SOSTransportCircleKVSRef tkvs = (SOSTransportCircleKVSRef) value; 123 CFErrorRef localError = NULL; 124 125 if(!SOSTransportCircleKVSAppendKeyInterest(tkvs, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys, &localError)){ 126 secerror("Error getting circle interests %@", localError); 127 } 128 CFReleaseNull(localError); 129 130 }); 131 CFMutableDictionaryRef circleDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 132 CFDictionarySetValue(circleDict, kAlwaysKeys, alwaysKeys); 133 CFDictionarySetValue(circleDict, kFirstUnlocked, afterFirstUnlockKeys); 134 CFDictionarySetValue(circleDict, kUnlocked, whenUnlockedKeys); 135 CFDictionarySetValue(keyDict, kCircle, circleDict); 136 137 CFReleaseNull(alwaysKeys); 138 CFReleaseNull(afterFirstUnlockKeys); 139 CFReleaseNull(whenUnlockedKeys); 140 alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 141 afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 142 whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 143 144 CFArrayForEach(SOSGetTransportMessages(), ^(const void *value) { 145 SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef) value; 146 CFErrorRef localError = NULL; 147 148 if(!SOSTransportMessageKVSAppendKeyInterest(tkvs, alwaysKeys, afterFirstUnlockKeys, whenUnlockedKeys, &localError)){ 149 secerror("Error getting message interests %@", localError); 150 } 151 CFReleaseNull(localError); 152 153 }); 154 155 CFMutableDictionaryRef messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 156 CFDictionarySetValue(messageDict, kAlwaysKeys, alwaysKeys); 157 CFDictionarySetValue(messageDict, kFirstUnlocked, afterFirstUnlockKeys); 158 CFDictionarySetValue(messageDict, kUnlocked, whenUnlockedKeys); 159 CFDictionarySetValue(keyDict, kMessage, messageDict); 160 161 CFReleaseNull(alwaysKeys); 162 CFReleaseNull(afterFirstUnlockKeys); 163 CFReleaseNull(whenUnlockedKeys); 164 alwaysKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 165 afterFirstUnlockKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 166 whenUnlockedKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 167 168 169 if (!SOSCloudKeychainUpdateKeys(keyDict, &updateError)) 170 { 171 secerror("Error updating keys: %@", updateError); 172 // TODO: propagate error(s) to callers. 173 } else { 174 if (CFArrayGetCount(whenUnlockedKeys) == 0) { 175 secnotice("sync", "Unlocked keys were empty!"); 176 } 177 // This leaks 3 CFStringRefs in DEBUG builds. 178 CFStringRef alwaysKeysDesc = SOSInterestListCopyDescription(alwaysKeys); 179 CFStringRef afterFirstUnlockKeysDesc = SOSInterestListCopyDescription(afterFirstUnlockKeys); 180 CFStringRef unlockedKeysDesc = SOSInterestListCopyDescription(whenUnlockedKeys); 181 secdebug("sync", "Updating interest: always: %@,\nfirstUnlock: %@,\nunlockedKeys: %@", 182 alwaysKeysDesc, 183 afterFirstUnlockKeysDesc, 184 unlockedKeysDesc); 185 CFReleaseNull(alwaysKeysDesc); 186 CFReleaseNull(afterFirstUnlockKeysDesc); 187 CFReleaseNull(unlockedKeysDesc); 188 } 189 190 CFReleaseNull(updateError); 191 CFReleaseNull(alwaysKeys); 192 CFReleaseNull(afterFirstUnlockKeys); 193 CFReleaseNull(whenUnlockedKeys); 194 CFReleaseNull(keyParamsDict); 195 CFReleaseNull(circleDict); 196 CFReleaseNull(messageDict); 197 CFReleaseNull(keyDict); 198} 199 200 201static void showWhatWasHandled(CFDictionaryRef updates, CFMutableArrayRef handledKeys) { 202 203 CFMutableStringRef updateStr = CFStringCreateMutable(kCFAllocatorDefault, 0); 204 CFMutableStringRef handledKeysStr = CFStringCreateMutable(kCFAllocatorDefault, 0); 205 206 CFDictionaryForEach(updates, ^(const void *key, const void *value) { 207 if (isString(key)) { 208 CFStringAppendFormat(updateStr, NULL, CFSTR("%@ "), (CFStringRef)key); 209 } 210 }); 211 CFArrayForEach(handledKeys, ^(const void *value) { 212 if (isString(value)) { 213 CFStringAppendFormat(handledKeysStr, NULL, CFSTR("%@ "), (CFStringRef)value); 214 } 215 }); 216 secnotice("updates", "Updates [%ld]: %@\n", CFDictionaryGetCount(updates), updateStr); 217 secnotice("updates", "Handled [%ld]: %@\n", CFArrayGetCount(handledKeys), handledKeysStr); 218 // secnotice("updates", "Updates: %@\n", updates); 219 // secnotice("updates", "Handled: %@\n", handledKeys); 220 221 CFReleaseSafe(updateStr); 222 CFReleaseSafe(handledKeysStr); 223} 224 225CF_RETURNS_RETAINED 226CFMutableArrayRef SOSTransportDispatchMessages(SOSAccountRef account, CFDictionaryRef updates, CFErrorRef *error){ 227 228 CFMutableArrayRef handledKeys = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 229 230 if(CFDictionaryContainsKey(updates, kSOSKVSAccountChangedKey)){ 231 // While changing accounts we may modify the key params array. To avoid stepping on ourselves we 232 // copy the list for iteration. 233 CFArrayRef originalKeyParams = CFArrayCreateCopy(kCFAllocatorDefault, SOSGetTransportKeyParameters()); 234 CFArrayForEach(originalKeyParams, ^(const void *value) { 235 SOSTransportKeyParameterRef tkvs = (SOSTransportKeyParameterRef) value; 236 if( (SOSTransportKeyParameterGetAccount((SOSTransportKeyParameterRef)value), account)){ 237 SOSTransportKeyParameterHandleNewAccount(tkvs); 238 } 239 }); 240 CFReleaseNull(originalKeyParams); 241 CFArrayAppendValue(handledKeys, kSOSKVSAccountChangedKey); 242 } 243 244 // Iterate through keys in updates. Perform circle change update. 245 // Then instantiate circles and engines and peers for all peers that 246 // are receiving a message in updates. 247 CFMutableDictionaryRef circle_peer_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 248 CFMutableDictionaryRef circle_circle_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 249 CFMutableDictionaryRef circle_retirement_messages_table = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 250 __block CFDataRef newParameters = NULL; 251 __block bool initial_sync = false; 252 __block bool new_account = false; 253 254 CFDictionaryForEach(updates, ^(const void *key, const void *value) { 255 CFErrorRef localError = NULL; 256 CFStringRef circle_name = NULL; 257 CFStringRef from_name = NULL; 258 CFStringRef to_name = NULL; 259 switch (SOSKVSKeyGetKeyTypeAndParse(key, &circle_name, &from_name, &to_name)) { 260 case kCircleKey: 261 CFDictionarySetValue(circle_circle_messages_table, circle_name, value); 262 break; 263 case kInitialSyncKey: 264 initial_sync = true; 265 break; 266 case kParametersKey: 267 if (isData(value)) { 268 newParameters = (CFDataRef) CFRetainSafe(value); 269 } 270 break; 271 case kMessageKey: { 272 CFMutableDictionaryRef circle_messages = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_peer_messages_table, circle_name); 273 CFDictionarySetValue(circle_messages, from_name, value); 274 break; 275 } 276 case kRetirementKey: { 277 CFMutableDictionaryRef circle_retirements = CFDictionaryEnsureCFDictionaryAndGetCurrentValue(circle_retirement_messages_table, circle_name); 278 CFDictionarySetValue(circle_retirements, from_name, value); 279 break; 280 } 281 case kAccountChangedKey: 282 new_account = true; 283 break; 284 285 case kUnknownKey: 286 secnotice("updates", "Unknown key '%@', ignoring", key); 287 break; 288 289 } 290 291 CFReleaseNull(circle_name); 292 CFReleaseNull(from_name); 293 CFReleaseNull(to_name); 294 295 if (error && *error) 296 secerror("Peer message processing error for: %@ -> %@ (%@)", key, value, *error); 297 if (localError) 298 secerror("Peer message local processing error for: %@ -> %@ (%@)", key, value, localError); 299 300 CFReleaseNull(localError); 301 }); 302 303 304 if (newParameters) { 305 CFArrayForEach(SOSGetTransportKeyParameters(), ^(const void *value) { 306 SOSTransportKeyParameterRef tkvs = (SOSTransportKeyParameterRef) value; 307 CFErrorRef localError = NULL; 308 if(CFEqualSafe(SOSTransportKeyParameterGetAccount(tkvs), account)){ 309 if(!SOSTransportKeyParameterHandleKeyParameterChanges(tkvs, newParameters, localError)) 310 secerror("Transport failed to handle new key parameters: %@", localError); 311 } 312 }); 313 CFArrayAppendValue(handledKeys, kSOSKVSKeyParametersKey); 314 } 315 CFReleaseNull(newParameters); 316 317 if(initial_sync){ 318 CFArrayAppendValue(handledKeys, kSOSKVSInitialSyncKey); 319 } 320 321 322 if(CFDictionaryGetCount(circle_retirement_messages_table)) { 323 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) { 324 SOSTransportCircleRef tkvs = (SOSTransportCircleRef) value; 325 if(CFEqualSafe(SOSTransportCircleGetAccount((SOSTransportCircleRef)value), account)){ 326 CFErrorRef localError = NULL; 327 CFDictionaryRef handledRetirementKeys = SOSTransportCircleHandleRetirementMessages(tkvs, circle_retirement_messages_table, error); 328 if(handledRetirementKeys == NULL){ 329 secerror("Transport failed to handle retirement messages: %@", localError); 330 } else { 331 CFDictionaryForEach(handledRetirementKeys, ^(const void *key, const void *value) { 332 CFStringRef circle_name = (CFStringRef)key; 333 CFArrayRef handledPeerIDs = (CFArrayRef)value; 334 CFArrayForEach(handledPeerIDs, ^(const void *value) { 335 CFStringRef peer_id = (CFStringRef)value; 336 CFStringRef keyHandled = SOSRetirementKeyCreateWithCircleNameAndPeer(circle_name, peer_id); 337 CFArrayAppendValue(handledKeys, keyHandled); 338 CFReleaseNull(keyHandled); 339 }); 340 }); 341 } 342 CFReleaseNull(handledRetirementKeys); 343 CFReleaseNull(localError); 344 } 345 }); 346 } 347 348 if(CFDictionaryGetCount(circle_circle_messages_table)) { 349 CFArrayForEach(SOSGetTransportCircles(), ^(const void *value) { 350 SOSTransportCircleRef tkvs = (SOSTransportCircleRef) value; 351 if(CFEqualSafe(SOSTransportCircleGetAccount((SOSTransportCircleRef)value), account)){ 352 CFArrayRef handleCircleMessages = SOSTransportCircleHandleCircleMessages(tkvs, circle_circle_messages_table, error); 353 CFErrorRef localError = NULL; 354 if(handleCircleMessages == NULL){ 355 secerror("Transport failed to handle circle messages: %@", localError); 356 } else if(CFArrayGetCount(handleCircleMessages) == 0) { 357 if(CFDictionaryGetCount(circle_circle_messages_table) != 0) { 358 secerror("Transport failed to process all circle messages: (%ld/%ld) %@", 359 CFArrayGetCount(handleCircleMessages), 360 CFDictionaryGetCount(circle_circle_messages_table), localError); 361 } else { 362 secnotice("circle", "Transport handled no circle messages"); 363 } 364 } else { 365 CFArrayForEach(handleCircleMessages, ^(const void *value) { 366 CFStringRef keyHandled = SOSCircleKeyCreateWithName((CFStringRef)value, error); 367 CFArrayAppendValue(handledKeys, keyHandled); 368 CFReleaseNull(keyHandled); 369 }); 370 } 371 CFReleaseNull(localError); 372 373 if (!SOSTransportCircleFlushChanges(tkvs, &localError)) 374 secerror("error flushing changes: %@", localError); 375 376 CFReleaseNull(localError); 377 CFReleaseNull(handleCircleMessages); 378 } 379 380 }); 381 } 382 // TODO: These should all produce circle -> messageTypeHandled messages 383 // That probably needs to wait for separation of transport types. 384 if(CFDictionaryGetCount(circle_peer_messages_table)) { 385 CFArrayForEach(SOSGetTransportMessages(), ^(const void *value) { 386 SOSTransportMessageRef tkvs = (SOSTransportMessageRef) value; 387 if(CFEqualSafe(SOSTransportMessageGetAccount((SOSTransportMessageRef)value), account)){ 388 CFErrorRef handleMessagesError = NULL; 389 CFDictionaryRef handledPeers = SOSTransportMessageHandleMessages(tkvs, circle_peer_messages_table, &handleMessagesError); 390 391 if (handledPeers) { 392 // We need to look for and send responses. 393 394 CFErrorRef syncError = NULL; 395 if (!SOSTransportMessageSyncWithPeers((SOSTransportMessageRef)tkvs, handledPeers, &syncError)) { 396 secerror("Sync with peers failed: %@", syncError); 397 } 398 399 CFDictionaryForEach(handledPeers, ^(const void *key, const void *value) { 400 if (isString(key) && isArray(value)) { 401 CFArrayForEach(value, ^(const void *value) { 402 if (isString(value)) { 403 CFStringRef peerID = (CFStringRef) value; 404 405 CFStringRef kvsHandledKey = SOSMessageKeyCreateFromPeerToTransport((SOSTransportMessageKVSRef)tkvs, peerID); 406 CFArrayAppendValue(handledKeys, kvsHandledKey); 407 CFReleaseSafe(kvsHandledKey); 408 } 409 }); 410 } 411 }); 412 413 CFErrorRef flushError = NULL; 414 if (!SOSTransportMessageFlushChanges((SOSTransportMessageRef)tkvs, &flushError)) { 415 secerror("Flush failed: %@", flushError); 416 } 417 } 418 else { 419 secerror("Didn't handle? : %@", handleMessagesError); 420 } 421 CFReleaseNull(handledPeers); 422 CFReleaseNull(handleMessagesError); 423 } 424 }); 425 } 426 CFReleaseNull(circle_retirement_messages_table); 427 CFReleaseNull(circle_circle_messages_table); 428 CFReleaseNull(circle_peer_messages_table); 429 430 431 432 showWhatWasHandled(updates, handledKeys); 433 434 return handledKeys; 435} 436 437 438