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