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