]>
Commit | Line | Data |
---|---|---|
5c19dc3a A |
1 | #include <Security/SecureObjectSync/SOSTransport.h> |
2 | #include <Security/SecureObjectSync/SOSTransportMessage.h> | |
3 | #include <Security/SecureObjectSync/SOSTransportMessageKVS.h> | |
4 | #include <Security/SecureObjectSync/SOSKVSKeys.h> | |
5 | #include <Security/SecureObjectSync/SOSAccountPriv.h> | |
d8f41ccd A |
6 | #include <utilities/SecCFWrappers.h> |
7 | #include <SOSInternal.h> | |
8 | #include <AssertMacros.h> | |
9 | #include <SOSCloudKeychainClient.h> | |
10 | ||
11 | struct __OpaqueSOSTransportMessageKVS { | |
12 | struct __OpaqueSOSTransportMessage m; | |
d8f41ccd A |
13 | CFMutableDictionaryRef pending_changes; |
14 | ||
15 | }; | |
16 | ||
17 | // | |
18 | // V-table implementation forward declarations | |
19 | // | |
20 | static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error); | |
21 | static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error); | |
22 | static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error); | |
23 | static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error); | |
24 | static void destroy(SOSTransportMessageRef transport); | |
5c19dc3a A |
25 | static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error); |
26 | ||
d8f41ccd A |
27 | static CF_RETURNS_RETAINED |
28 | CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error); | |
29 | ||
30 | static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error); | |
31 | ||
d8f41ccd A |
32 | SOSTransportMessageKVSRef SOSTransportMessageKVSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error){ |
33 | SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageKVS) - sizeof(CFRuntimeBase), account, circleName, error); | |
34 | ||
35 | if (tkvs) { | |
36 | // Fill in vtable: | |
37 | tkvs->m.sendMessages = sendMessages; | |
38 | tkvs->m.syncWithPeers = syncWithPeers; | |
39 | tkvs->m.flushChanges = flushChanges; | |
40 | tkvs->m.cleanupAfterPeerMessages = cleanupAfterPeer; | |
41 | tkvs->m.destroy = destroy; | |
42 | tkvs->m.handleMessages = handleMessages; | |
5c19dc3a | 43 | tkvs->m.getTransportType = getTransportType; |
d8f41ccd A |
44 | // Initialize ourselves |
45 | tkvs->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
d8f41ccd A |
46 | SOSRegisterTransportMessage((SOSTransportMessageRef)tkvs); |
47 | } | |
48 | ||
49 | return tkvs; | |
50 | } | |
5c19dc3a | 51 | |
d8f41ccd A |
52 | bool SOSTransportMessageKVSAppendKeyInterest(SOSTransportMessageKVSRef transport, CFMutableArrayRef alwaysKeys, CFMutableArrayRef afterFirstUnlockKeys, CFMutableArrayRef unlockedKeys, CFErrorRef *localError){ |
53 | SOSEngineRef engine = SOSTransportMessageGetEngine((SOSTransportMessageRef)transport); | |
54 | require_quiet(engine, fail); | |
fa7225c8 | 55 | |
5c19dc3a A |
56 | CFArrayRef peerInfos = SOSAccountCopyPeersToListenTo(SOSTransportMessageGetAccount((SOSTransportMessageRef) transport), localError); |
57 | ||
58 | if(peerInfos){ | |
59 | CFArrayForEach(peerInfos, ^(const void *value) { | |
60 | SOSPeerInfoRef peer = (SOSPeerInfoRef)value; | |
61 | CFStringRef peerID = SOSPeerInfoGetPeerID(peer); | |
fa7225c8 A |
62 | CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport((SOSTransportMessageRef)transport, peerID); |
63 | if(peerMessage != NULL) | |
64 | CFArrayAppendValue(unlockedKeys, peerMessage); | |
d8f41ccd A |
65 | CFReleaseNull(peerMessage); |
66 | }); | |
5c19dc3a | 67 | CFReleaseNull(peerInfos); |
d8f41ccd A |
68 | } |
69 | return true; | |
70 | fail: | |
71 | return false; | |
72 | } | |
73 | static void destroy(SOSTransportMessageRef transport){ | |
74 | SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef)transport; | |
d8f41ccd A |
75 | CFReleaseNull(tkvs->pending_changes); |
76 | SOSUnregisterTransportMessage((SOSTransportMessageRef)tkvs); | |
77 | ||
78 | } | |
79 | ||
5c19dc3a A |
80 | static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){ |
81 | return kKVS; | |
82 | } | |
83 | ||
d8f41ccd | 84 | static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport, CFDictionaryRef changes, CFErrorRef *error){ |
5c19dc3a | 85 | |
d8f41ccd A |
86 | CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef error) { |
87 | if (error) { | |
88 | secerror("Error putting: %@", error); | |
89 | CFReleaseSafe(error); | |
90 | } | |
91 | }; | |
92 | ||
93 | SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error); | |
94 | return true; | |
95 | } | |
96 | ||
97 | static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport, CFErrorRef *error) { | |
98 | CFErrorRef changeError = NULL; | |
99 | ||
100 | if (transport->pending_changes == NULL || CFDictionaryGetCount(transport->pending_changes) == 0) { | |
101 | CFReleaseNull(transport->pending_changes); | |
102 | return true; | |
103 | } | |
5c19dc3a A |
104 | SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport); |
105 | CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error); | |
106 | ||
107 | if(dsid == NULL) | |
108 | dsid = kCFNull; | |
109 | ||
110 | CFDictionaryAddValue(transport->pending_changes, kSOSKVSRequiredKey, dsid); | |
d8f41ccd A |
111 | |
112 | bool success = SOSTransportMessageKVSUpdateKVS(transport, transport->pending_changes, &changeError); | |
113 | if (success) { | |
114 | CFDictionaryRemoveAllValues(transport->pending_changes); | |
115 | } else { | |
116 | SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, | |
117 | CFSTR("Send changes block failed [%@]"), transport->pending_changes); | |
118 | } | |
119 | ||
120 | return success; | |
121 | } | |
122 | ||
123 | static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport, CFStringRef message_key, CFDataRef message_data){ | |
124 | if (transport->pending_changes == NULL) { | |
125 | transport->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
126 | } | |
127 | if (message_data == NULL) { | |
128 | CFDictionarySetValue(transport->pending_changes, message_key, kCFNull); | |
129 | } else { | |
130 | CFDictionarySetValue(transport->pending_changes, message_key, message_data); | |
131 | } | |
132 | } | |
133 | ||
134 | static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error) | |
135 | { | |
136 | CFArrayRef enginePeers = SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef)transport)); | |
5c19dc3a A |
137 | __block SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport); |
138 | ||
d8f41ccd A |
139 | CFDictionaryForEach(circle_to_peer_ids, ^(const void *key, const void *value) { |
140 | if (isString(key) && isArray(value)) { | |
141 | CFStringRef circle_name = (CFStringRef) key; | |
142 | CFArrayRef peers_to_cleanup_after = (CFArrayRef) value; | |
143 | ||
144 | CFArrayForEach(peers_to_cleanup_after, ^(const void *value) { | |
145 | if (isString(value)) { | |
146 | CFStringRef cleanup_id = (CFStringRef) value; | |
147 | // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers | |
148 | if (enginePeers) CFArrayForEach(enginePeers, ^(const void *value) { | |
149 | if (isString(value)) { | |
150 | CFStringRef in_circle_id = (CFStringRef) value; | |
151 | ||
152 | CFStringRef kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, cleanup_id, in_circle_id); | |
153 | SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL); | |
154 | CFReleaseSafe(kvsKey); | |
155 | ||
5c19dc3a A |
156 | CFStringRef lastCirclePushedKey = SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name, cleanup_id); |
157 | SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL); | |
158 | CFReleaseSafe(lastCirclePushedKey); | |
159 | ||
160 | CFStringRef lastKeyParameterPushedKey = SOSLastKeyParametersPushedKeyCreateWithPeerID(cleanup_id); | |
161 | SOSTransportMessageKVSAddToPendingChanges(transport, lastKeyParameterPushedKey, NULL); | |
162 | CFReleaseSafe(lastKeyParameterPushedKey); | |
163 | ||
164 | CFStringRef lastCirclePushedWithAccountGestaltKey = SOSLastCirclePushedKeyCreateWithAccountGestalt(account); | |
165 | SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL); | |
166 | CFReleaseSafe(lastCirclePushedWithAccountGestaltKey); | |
167 | ||
168 | CFStringRef lastKeyParameterWithAccountGestaltKey = SOSLastKeyParametersPushedKeyCreateWithAccountGestalt(account); | |
169 | SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL); | |
170 | CFReleaseSafe(lastKeyParameterWithAccountGestaltKey); | |
171 | ||
d8f41ccd A |
172 | kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, in_circle_id, cleanup_id); |
173 | SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL); | |
174 | CFReleaseSafe(kvsKey); | |
175 | } | |
176 | }); | |
177 | ||
178 | } | |
179 | }); | |
180 | } | |
181 | }); | |
182 | ||
183 | return SOSTransportMessageFlushChanges((SOSTransportMessageRef)transport, error); | |
184 | } | |
185 | ||
186 | static CF_RETURNS_RETAINED | |
187 | CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) { | |
5c19dc3a | 188 | |
d8f41ccd | 189 | CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); |
5c19dc3a | 190 | CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, transport->circleName); |
d8f41ccd | 191 | CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); |
d8f41ccd A |
192 | |
193 | if(peerToMessage){ | |
194 | CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) { | |
fa7225c8 A |
195 | CFStringRef peer_id = asString(key, NULL); |
196 | CFDataRef peer_message = asData(value, NULL); | |
d8f41ccd A |
197 | CFErrorRef localError = NULL; |
198 | ||
fa7225c8 | 199 | if (peer_id && peer_message && SOSTransportMessageHandlePeerMessage(transport, peer_id, peer_message, &localError)) { |
d8f41ccd A |
200 | CFArrayAppendValue(handled_peers, key); |
201 | } else { | |
5c19dc3a | 202 | secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError); |
d8f41ccd A |
203 | } |
204 | CFReleaseNull(localError); | |
205 | }); | |
206 | } | |
5c19dc3a | 207 | CFDictionaryAddValue(handled, transport->circleName, handled_peers); |
d8f41ccd A |
208 | CFReleaseNull(handled_peers); |
209 | ||
210 | return handled; | |
211 | } | |
212 | ||
213 | ||
214 | static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error) { | |
215 | SOSTransportMessageKVSRef kvsTransport = (SOSTransportMessageKVSRef) transport; | |
216 | bool result = true; | |
5c19dc3a A |
217 | SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport); |
218 | CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error); | |
219 | ||
220 | if(dsid == NULL) | |
221 | dsid = kCFNull; | |
222 | ||
fa7225c8 | 223 | CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer((SOSTransportMessageRef)kvsTransport, peerID); |
5c19dc3a | 224 | CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL, message_to_peer_key, message, kSOSKVSRequiredKey, dsid, NULL); |
d8f41ccd A |
225 | |
226 | if (!SOSTransportMessageKVSUpdateKVS(kvsTransport, a_message_to_a_peer, error)) { | |
227 | secerror("Sync with peers failed to send to %@ [%@], %@", peerID, a_message_to_a_peer, *error); | |
228 | result = false; | |
229 | } | |
230 | CFReleaseNull(a_message_to_a_peer); | |
231 | CFReleaseNull(message_to_peer_key); | |
232 | ||
233 | return result; | |
234 | } | |
235 | ||
5c19dc3a | 236 | static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error) { |
d8f41ccd | 237 | // Each entry is keyed by circle name and contains a list of peerIDs |
d8f41ccd A |
238 | __block bool result = true; |
239 | ||
240 | CFDictionaryForEach(circleToPeerIDs, ^(const void *key, const void *value) { | |
241 | if (isString(key) && isArray(value)) { | |
242 | CFStringRef circleName = (CFStringRef) key; | |
243 | CFArrayForEach(value, ^(const void *value) { | |
244 | if (isString(value)) { | |
245 | CFStringRef peerID = (CFStringRef) value; | |
246 | result &= SOSTransportMessageSendMessageIfNeeded(transport, circleName, peerID, error); | |
247 | } | |
248 | }); | |
249 | } | |
250 | }); | |
251 | ||
252 | return result; | |
253 | } | |
254 | ||
255 | static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error) { | |
256 | __block bool result = true; | |
257 | ||
258 | CFDictionaryForEach(circleToPeersToMessage, ^(const void *key, const void *value) { | |
259 | if (isString(key) && isDictionary(value)) { | |
260 | CFStringRef circleName = (CFStringRef) key; | |
261 | CFDictionaryForEach(value, ^(const void *key, const void *value) { | |
262 | if (isString(key) && isData(value)) { | |
263 | CFStringRef peerID = (CFStringRef) key; | |
264 | CFDataRef message = (CFDataRef) value; | |
265 | bool rx = sendToPeer(transport, circleName, peerID, message, error); | |
266 | result &= rx; | |
267 | } | |
268 | }); | |
269 | } | |
270 | }); | |
271 | ||
272 | return true; | |
273 | } | |
274 | ||
275 | static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error) | |
276 | { | |
277 | return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef) transport, error); | |
278 | } | |
279 | ||
280 | static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error) | |
281 | { | |
282 | return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef) transport, circle_to_peer_ids, error); | |
283 | } | |
284 |