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