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