]> git.saurik.com Git - apple/security.git/blame_incremental - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageKVS.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageKVS.c
... / ...
CommitLineData
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
11struct __OpaqueSOSTransportMessageKVS {
12 struct __OpaqueSOSTransportMessage m;
13 CFMutableDictionaryRef pending_changes;
14
15};
16
17//
18// V-table implementation forward declarations
19//
20static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error);
21static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error);
22static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error);
23static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error);
24static void destroy(SOSTransportMessageRef transport);
25static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error);
26
27static CF_RETURNS_RETAINED
28CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error);
29
30static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error);
31
32SOSTransportMessageKVSRef 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
52bool 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;
70fail:
71 return false;
72}
73static void destroy(SOSTransportMessageRef transport){
74 SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef)transport;
75 CFReleaseNull(tkvs->pending_changes);
76 SOSUnregisterTransportMessage((SOSTransportMessageRef)tkvs);
77
78}
79
80static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){
81 return kKVS;
82}
83
84static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport, CFDictionaryRef changes, CFErrorRef *error){
85
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
97static 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 }
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);
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
123static 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
134static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
135{
136 CFArrayRef enginePeers = SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef)transport));
137 __block SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport);
138
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
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
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
186static CF_RETURNS_RETAINED
187CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) {
188
189 CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
190 CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, transport->circleName);
191 CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
192
193 if(peerToMessage){
194 CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) {
195 CFStringRef peer_id = asString(key, NULL);
196 CFDataRef peer_message = asData(value, NULL);
197 CFErrorRef localError = NULL;
198
199 if (peer_id && peer_message && SOSTransportMessageHandlePeerMessage(transport, peer_id, peer_message, &localError)) {
200 CFArrayAppendValue(handled_peers, key);
201 } else {
202 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError);
203 }
204 CFReleaseNull(localError);
205 });
206 }
207 CFDictionaryAddValue(handled, transport->circleName, handled_peers);
208 CFReleaseNull(handled_peers);
209
210 return handled;
211}
212
213
214static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error) {
215 SOSTransportMessageKVSRef kvsTransport = (SOSTransportMessageKVSRef) transport;
216 bool result = true;
217 SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport);
218 CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
219
220 if(dsid == NULL)
221 dsid = kCFNull;
222
223 CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer((SOSTransportMessageRef)kvsTransport, peerID);
224 CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL, message_to_peer_key, message, kSOSKVSRequiredKey, dsid, NULL);
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
236static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error) {
237 // Each entry is keyed by circle name and contains a list of peerIDs
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
255static 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
275static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error)
276{
277 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef) transport, error);
278}
279
280static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
281{
282 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef) transport, circle_to_peer_ids, error);
283}
284