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