]> git.saurik.com Git - apple/security.git/blame_incremental - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageKVS.c
Security-57740.60.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, CFSetRef peers, CFErrorRef *error);
22static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef peersToMessage, 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 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
96static 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
122static 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
133static 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
185static CF_RETURNS_RETAINED
186CFDictionaryRef 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
213static 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
240static 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
252static 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
268static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error)
269{
270 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef) transport, error);
271}
272
273static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
274{
275 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef) transport, circle_to_peer_ids, error);
276}
277