]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageKVS.m
c398e55c6d73ef80da74d2ba76f000138e02de9d
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageKVS.m
1 #include <Security/SecureObjectSync/SOSTransport.h>
2 #import <Security/SecureObjectSync/SOSTransportMessage.h>
3 #import <Security/SecureObjectSync/SOSTransportMessageKVS.h>
4 #include <Security/SecureObjectSync/SOSKVSKeys.h>
5 #include <utilities/SecCFWrappers.h>
6 #include <SOSInternal.h>
7 #include <AssertMacros.h>
8 #include <SOSCloudKeychainClient.h>
9
10 @implementation SOSMessageKVS
11
12 @synthesize pending_changes = pending_changes;
13
14 -(id) initWithAccount:(SOSAccount*)acct andName:(NSString*)name
15 {
16 self = [super init];
17
18 if (self) {
19 account = acct;
20 circleName = [[NSString alloc]initWithString:name];
21 SOSEngineRef e = SOSDataSourceFactoryGetEngineForDataSourceName(acct.factory, (__bridge CFStringRef)(circleName), NULL);
22 engine = e;
23 pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
24 SOSRegisterTransportMessage((SOSMessage*)self);
25 }
26
27 return self;
28 }
29
30 -(CFIndex) SOSTransportMessageGetTransportType
31 {
32 return kKVS;
33 }
34 -(CFStringRef) SOSTransportMessageGetCircleName
35 {
36 return (__bridge CFStringRef)circleName;
37 }
38 -(CFTypeRef) SOSTransportMessageGetEngine
39 {
40 return engine;
41 }
42 -(SOSAccount*) SOSTransportMessageGetAccount
43 {
44 return account;
45 }
46
47 -(bool) SOSTransportMessageKVSAppendKeyInterest:(SOSMessageKVS*) transport ak:(CFMutableArrayRef) alwaysKeys firstUnlock:(CFMutableArrayRef) afterFirstUnlockKeys
48 unlocked:(CFMutableArrayRef) unlockedKeys err:(CFErrorRef *)localError
49 {
50 require_quiet(engine, fail);
51
52 CFArrayRef peerInfos = SOSAccountCopyPeersToListenTo( [self SOSTransportMessageGetAccount], localError);
53
54 if(peerInfos){
55 NSString* myID = self.account.peerID;
56
57 CFArrayForEach(peerInfos, ^(const void *value) {
58 CFStringRef peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
59 CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport(transport,(__bridge CFStringRef) myID, peerID);
60 if(peerMessage != NULL)
61 CFArrayAppendValue(unlockedKeys, peerMessage);
62 CFReleaseNull(peerMessage);
63 });
64 CFReleaseNull(peerInfos);
65 }
66 return true;
67 fail:
68 return false;
69 }
70
71
72 -(CFIndex) SOSTransportMessageGetTransportType:(SOSMessage*) transport err:(CFErrorRef *)error
73 {
74 return kKVS;
75 }
76
77 static bool SOSTransportMessageKVSUpdateKVS(SOSMessageKVS* transport, CFDictionaryRef changes, CFErrorRef *error){
78
79 CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
80 if (block_error) {
81 secerror("Error putting: %@", block_error);
82 }
83 };
84
85 SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error);
86 return true;
87 }
88
89 static bool SOSTransportMessageKVSSendPendingChanges(SOSMessageKVS* transport, CFErrorRef *error) {
90 CFErrorRef changeError = NULL;
91
92 if (transport->pending_changes == NULL || CFDictionaryGetCount(transport->pending_changes) == 0) {
93 CFReleaseNull(transport->pending_changes);
94 return true;
95 }
96 SOSAccount* acct = [transport SOSTransportMessageGetAccount];
97 CFTypeRef dsid = SOSAccountGetValue(acct, kSOSDSIDKey, error);
98
99 if(dsid == NULL)
100 dsid = kCFNull;
101
102 CFDictionaryAddValue(transport->pending_changes, kSOSKVSRequiredKey, dsid);
103
104 bool success = SOSTransportMessageKVSUpdateKVS(transport, transport->pending_changes, &changeError);
105 if (success) {
106 CFDictionaryRemoveAllValues(transport->pending_changes);
107 } else {
108 SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
109 CFSTR("Send changes block failed [%@]"), transport->pending_changes);
110 }
111
112 return success;
113 }
114
115 static void SOSTransportMessageKVSAddToPendingChanges(SOSMessageKVS* transport, CFStringRef message_key, CFDataRef message_data){
116 if (transport.pending_changes == NULL) {
117 transport.pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
118 }
119 if (message_data == NULL) {
120 CFDictionarySetValue(transport.pending_changes, message_key, kCFNull);
121 } else {
122 CFDictionarySetValue(transport.pending_changes, message_key, message_data);
123 }
124 }
125
126 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSMessageKVS* transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
127 {
128 CFArrayRef enginePeers = SOSEngineGetPeerIDs((SOSEngineRef)[transport SOSTransportMessageGetEngine]);
129
130 CFDictionaryForEach(circle_to_peer_ids, ^(const void *key, const void *value) {
131 if (isString(key) && isArray(value)) {
132 CFStringRef circle_name = (CFStringRef) key;
133 CFArrayRef peers_to_cleanup_after = (CFArrayRef) value;
134
135 CFArrayForEach(peers_to_cleanup_after, ^(const void *value) {
136 if (isString(value)) {
137 CFStringRef cleanup_id = (CFStringRef) value;
138 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers
139 if (enginePeers) CFArrayForEach(enginePeers, ^(const void *value) {
140 if (isString(value)) {
141 CFStringRef in_circle_id = (CFStringRef) value;
142
143 CFStringRef kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, cleanup_id, in_circle_id);
144 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
145 CFReleaseSafe(kvsKey);
146
147 kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, in_circle_id, cleanup_id);
148 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
149 CFReleaseSafe(kvsKey);
150 }
151 });
152
153 }
154 });
155 }
156 });
157
158 return [transport SOSTransportMessageFlushChanges:(SOSMessage*)transport err:error];
159 }
160
161 -(bool) SOSTransportMessageCleanupAfterPeerMessages:(SOSMessage*) transport peers:(CFDictionaryRef) peers err:(CFErrorRef*) error
162 {
163 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSMessageKVS*) transport, peers, error);
164 }
165
166 -(CFDictionaryRef)CF_RETURNS_RETAINED SOSTransportMessageHandlePeerMessageReturnsHandledCopy:(SOSMessage*) transport peerMessages:(CFMutableDictionaryRef) circle_peer_messages_table err:(CFErrorRef *)error
167 {
168 CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
169 CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, (__bridge CFStringRef)(transport.circleName));
170 CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
171
172 if(peerToMessage){
173 CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) {
174 CFStringRef peer_id = asString(key, NULL);
175 CFDataRef peer_message = asData(value, NULL);
176 CFErrorRef localError = NULL;
177
178 if (peer_id && peer_message && [transport SOSTransportMessageHandlePeerMessage:transport id:peer_id cm:peer_message err:&localError ]) {
179 CFArrayAppendValue(handled_peers, key);
180 } else {
181 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError);
182 }
183 CFReleaseNull(localError);
184 });
185 }
186 CFDictionaryAddValue(handled, (__bridge const void *)(transport.circleName), handled_peers);
187 CFReleaseNull(handled_peers);
188
189 return handled;
190 }
191
192
193 static bool sendToPeer(SOSMessage* transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error)
194 {
195 SOSMessageKVS* kvsTransport = (SOSMessageKVS*) transport;
196 bool result = true;
197 SOSAccount* account = [transport SOSTransportMessageGetAccount];
198 CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
199
200 if(dsid == NULL)
201 dsid = kCFNull;
202 NSString* myID = account.peerID;
203 CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer(kvsTransport, (__bridge CFStringRef) myID, peerID);
204
205 CFTypeRef messageToSend = message != NULL ? (CFTypeRef) message : (CFTypeRef) kCFNull;
206 CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL,
207 message_to_peer_key, messageToSend,
208 kSOSKVSRequiredKey, dsid,
209 NULL);
210
211 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport, a_message_to_a_peer, error)) {
212 secerror("Sync with peers failed to send to %@ [%@], %@", peerID, a_message_to_a_peer, *error);
213 result = false;
214 }
215 CFReleaseNull(a_message_to_a_peer);
216 CFReleaseNull(message_to_peer_key);
217
218 return result;
219 }
220
221 -(bool) SOSTransportMessageSyncWithPeers:(SOSMessage*) transport p:(CFSetRef) peers err:(CFErrorRef *)error
222 {
223 // Each entry is keyed by circle name and contains a list of peerIDs
224 __block bool result = true;
225
226 CFSetForEach(peers, ^(const void *value) {
227 CFStringRef peerID = asString(value, NULL);
228
229 result &= [ transport SOSTransportMessageSendMessageIfNeeded:transport id:(__bridge CFStringRef)(transport.circleName) pID:peerID err:error];
230 });
231
232 return result;
233 }
234
235 -(bool) SOSTransportMessageSendMessages:(SOSMessage*) transport pm:(CFDictionaryRef) peer_messages err:(CFErrorRef *)error
236 {
237 __block bool result = true;
238
239 CFDictionaryForEach(peer_messages, ^(const void *key, const void *value) {
240 CFStringRef peerID = asString(key, NULL);
241 CFDataRef message = asData(value,NULL);
242 if (peerID && message) {
243 bool rx = sendToPeer(transport, (__bridge CFStringRef)(transport.circleName), peerID, message, error);
244 result &= rx;
245 }
246 });
247
248 return true;
249 }
250
251 -(bool) SOSTransportMssageFlushChanges:(SOSMessage*) transport err:(CFErrorRef *)error
252 {
253 return SOSTransportMessageKVSSendPendingChanges((SOSMessageKVS*) transport, error);
254 }
255
256 @end
257
258