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