]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageKVS.m
Security-58286.270.3.0.1.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 void SOSTransportMessageKVSAddToPendingChanges(SOSMessageKVS* transport, CFStringRef message_key, CFDataRef message_data){
100 if (transport.pending_changes == NULL) {
101 transport.pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
102 }
103 if (message_data == NULL) {
104 CFDictionarySetValue(transport.pending_changes, message_key, kCFNull);
105 } else {
106 CFDictionarySetValue(transport.pending_changes, message_key, message_data);
107 }
108 }
109
110 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSMessageKVS* transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
111 {
112 CFArrayRef enginePeers = SOSEngineGetPeerIDs((SOSEngineRef)[transport SOSTransportMessageGetEngine]);
113
114 CFDictionaryForEach(circle_to_peer_ids, ^(const void *key, const void *value) {
115 if (isString(key) && isArray(value)) {
116 CFStringRef circle_name = (CFStringRef) key;
117 CFArrayRef peers_to_cleanup_after = (CFArrayRef) value;
118
119 CFArrayForEach(peers_to_cleanup_after, ^(const void *value) {
120 if (isString(value)) {
121 CFStringRef cleanup_id = (CFStringRef) value;
122 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers
123 if (enginePeers) CFArrayForEach(enginePeers, ^(const void *value) {
124 if (isString(value)) {
125 CFStringRef in_circle_id = (CFStringRef) value;
126
127 CFStringRef kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, cleanup_id, in_circle_id);
128 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
129 CFReleaseSafe(kvsKey);
130
131 kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, in_circle_id, cleanup_id);
132 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
133 CFReleaseSafe(kvsKey);
134 }
135 });
136
137 }
138 });
139 }
140 });
141
142 return [transport SOSTransportMessageFlushChanges:(SOSMessage*)transport err:error];
143 }
144
145 -(bool) SOSTransportMessageCleanupAfterPeerMessages:(SOSMessage*) transport peers:(CFDictionaryRef) peers err:(CFErrorRef*) error
146 {
147 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSMessageKVS*) transport, peers, error);
148 }
149
150 -(CFDictionaryRef)CF_RETURNS_RETAINED SOSTransportMessageHandlePeerMessageReturnsHandledCopy:(SOSMessage*) transport peerMessages:(CFMutableDictionaryRef) circle_peer_messages_table err:(CFErrorRef *)error
151 {
152 CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
153 CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, (__bridge CFStringRef)(transport.circleName));
154 CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
155
156 if(peerToMessage){
157 CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) {
158 CFStringRef peer_id = asString(key, NULL);
159 CFDataRef peer_message = asData(value, NULL);
160 CFErrorRef localError = NULL;
161
162 if (peer_id && peer_message && [transport SOSTransportMessageHandlePeerMessage:transport id:peer_id cm:peer_message err:&localError ]) {
163 CFArrayAppendValue(handled_peers, key);
164 } else {
165 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError);
166 }
167 CFReleaseNull(localError);
168 });
169 }
170 CFDictionaryAddValue(handled, (__bridge const void *)(transport.circleName), handled_peers);
171 CFReleaseNull(handled_peers);
172
173 return handled;
174 }
175
176
177 static bool sendToPeer(SOSMessage* transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error)
178 {
179 SOSMessageKVS* kvsTransport = (SOSMessageKVS*) transport;
180 bool result = true;
181 SOSAccount* account = [transport SOSTransportMessageGetAccount];
182 CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
183
184 if(dsid == NULL)
185 dsid = kCFNull;
186 NSString* myID = account.peerID;
187 CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer(kvsTransport, (__bridge CFStringRef) myID, peerID);
188
189 CFTypeRef messageToSend = message != NULL ? (CFTypeRef) message : (CFTypeRef) kCFNull;
190 CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL,
191 message_to_peer_key, messageToSend,
192 kSOSKVSRequiredKey, dsid,
193 NULL);
194
195 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport, a_message_to_a_peer, error)) {
196 secerror("Sync with peers failed to send to %@ [%@], %@", peerID, a_message_to_a_peer, *error);
197 result = false;
198 }
199 CFReleaseNull(a_message_to_a_peer);
200 CFReleaseNull(message_to_peer_key);
201
202 return result;
203 }
204
205 -(bool) SOSTransportMessageSyncWithPeers:(SOSMessage*) transport p:(CFSetRef) peers err:(CFErrorRef *)error
206 {
207 // Each entry is keyed by circle name and contains a list of peerIDs
208 __block bool result = true;
209
210 CFSetForEach(peers, ^(const void *value) {
211 CFStringRef peerID = asString(value, NULL);
212
213 result &= [ transport SOSTransportMessageSendMessageIfNeeded:transport id:(__bridge CFStringRef)(transport.circleName) pID:peerID err:error];
214 });
215
216 return result;
217 }
218
219 -(bool) SOSTransportMessageSendMessages:(SOSMessage*) transport pm:(CFDictionaryRef) peer_messages err:(CFErrorRef *)error
220 {
221 __block bool result = true;
222
223 CFDictionaryForEach(peer_messages, ^(const void *key, const void *value) {
224 CFStringRef peerID = asString(key, NULL);
225 CFDataRef message = asData(value,NULL);
226 if (peerID && message) {
227 bool rx = sendToPeer(transport, (__bridge CFStringRef)(transport.circleName), peerID, message, error);
228 result &= rx;
229 }
230 });
231
232 return true;
233 }
234
235 @end
236
237