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