]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/SecureObjectSync/SOSPeer.c
Security-55471.14.8.tar.gz
[apple/security.git] / sec / SOSCircle / SecureObjectSync / SOSPeer.c
1 /*
2 * Created by Michael Brouwer on 6/22/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
4 */
5
6 /*
7 * SOSPeer.c - Implementation of a secure object syncing peer
8 */
9 #include <SecureObjectSync/SOSPeer.h>
10 #include <SecureObjectSync/SOSEngine.h>
11 #include <SecureObjectSync/SOSFullPeerInfo.h>
12 #include <SecureObjectSync/SOSPeerInfo.h>
13 #include <SecureObjectSync/SOSCoder.h>
14 #include <SecureObjectSync/SOSInternal.h>
15 #include <utilities/SecCFRelease.h>
16 #include <CommonCrypto/CommonDigest.h>
17 #include <CommonCrypto/CommonDigestSPI.h>
18 #include <utilities/SecCFError.h>
19 #include <utilities/SecCFWrappers.h>
20 #include <utilities/debugging.h>
21 #include <utilities/SecFileLocations.h>
22 #include <utilities/der_plist.h>
23 #include <utilities/der_plist_internal.h>
24
25 #include <utilities/SecDb.h>
26
27 #include <securityd/SOSCloudCircleServer.h>
28
29 #include <CoreFoundation/CoreFoundation.h>
30
31 #include <stdlib.h>
32
33 #include <AssertMacros.h>
34
35 //
36 //
37 //
38 static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.peer.error");
39
40 static CFMutableDictionaryRef sPersistenceCache = NULL;
41 static CFStringRef peerFile = CFSTR("PeerManifestCache.plist");
42
43 static CFMutableDictionaryRef SOSPeerGetPersistenceCache(CFStringRef my_id)
44 {
45 static dispatch_once_t onceToken;
46 dispatch_once(&onceToken, ^{
47 CFErrorRef localError = NULL;
48 CFMutableDictionaryRef peerDict = NULL;
49 CFDataRef dictAsData = SOSItemGet(kSOSPeerDataLabel, &localError);
50
51 if (dictAsData) {
52 der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef*)&peerDict, &localError,
53 CFDataGetBytePtr(dictAsData),
54 CFDataGetBytePtr(dictAsData) + CFDataGetLength(dictAsData));
55 }
56
57 if (!isDictionary(peerDict)) {
58 CFReleaseNull(peerDict);
59 secnotice("peer", "Error finding persisted peer data %@, using empty", localError);
60 peerDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
61 CFReleaseNull(localError);
62 }
63
64 if (CFDictionaryGetValue(peerDict, my_id) != NULL) {
65 CFMutableDictionaryRef mySubDictionary = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
66
67 CFDictionaryForEach(peerDict, ^(const void *key, const void *value) {
68 if (!isDictionary(value)) {
69 CFDictionaryAddValue(mySubDictionary, key, value);
70 };
71 });
72
73 CFDictionaryForEach(mySubDictionary, ^(const void *key, const void *value) {
74 CFDictionaryRemoveValue(peerDict, key);
75 });
76
77 CFDictionaryAddValue(peerDict, my_id, mySubDictionary);
78 }
79 sPersistenceCache = peerDict;
80 });
81
82 return sPersistenceCache;
83 }
84
85 static void SOSPeerFlushPersistenceCache()
86 {
87 if (!sPersistenceCache)
88 return;
89
90 CFErrorRef localError = NULL;
91 CFIndex size = der_sizeof_dictionary(sPersistenceCache, &localError);
92 CFMutableDataRef dataToStore = CFDataCreateMutableWithScratch(kCFAllocatorDefault, size);
93
94 if (size == 0) {
95 secerror("Error calculating size of persistence cache: %@", localError);
96 goto fail;
97 }
98
99 uint8_t *der = NULL;
100 if (CFDataGetBytePtr(dataToStore) != (der = der_encode_dictionary(sPersistenceCache, &localError,
101 CFDataGetBytePtr(dataToStore),
102 CFDataGetMutableBytePtr(dataToStore) + CFDataGetLength(dataToStore)))) {
103 secerror("Error flattening peer cache: %@", localError);
104 secerror("ERROR flattening peer cache (%@): size=%zd %@ (%p %p)", sPersistenceCache, size, dataToStore, CFDataGetBytePtr(dataToStore), der);
105 goto fail;
106 }
107
108 if (!SOSItemUpdateOrAdd(kSOSPeerDataLabel, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, dataToStore, &localError)) {
109 secerror("Peer cache item save failed: %@", localError);
110 goto fail;
111 }
112
113 fail:
114 CFReleaseNull(localError);
115 CFReleaseNull(dataToStore);
116 }
117
118 void SOSPeerPurge(SOSPeerRef peer) {
119 // TODO: Do we use this or some other end-around for PurgeAll?
120 }
121
122 void SOSPeerPurgeAllFor(CFStringRef my_id)
123 {
124 if (!my_id)
125 return;
126
127 CFMutableDictionaryRef persistenceCache = SOSPeerGetPersistenceCache(my_id);
128
129 CFMutableDictionaryRef myPeerIDs = (CFMutableDictionaryRef) CFDictionaryGetValue(persistenceCache, my_id);
130 if (myPeerIDs)
131 {
132 CFRetainSafe(myPeerIDs);
133
134 CFDictionaryRemoveValue(myPeerIDs, my_id);
135
136 if (isDictionary(myPeerIDs)) {
137 CFDictionaryForEach(myPeerIDs, ^(const void *key, const void *value) {
138 // TODO: Inflate each and purge its keys.
139 });
140 }
141
142 CFReleaseNull(myPeerIDs);
143 }
144 }
145
146 static bool SOSPeerFindDataFor(CFTypeRef *peerData, CFStringRef my_id, CFStringRef peer_id, CFErrorRef *error)
147 {
148 CFDictionaryRef table = (CFDictionaryRef) CFDictionaryGetValue(SOSPeerGetPersistenceCache(my_id), my_id);
149
150 *peerData = isDictionary(table) ? CFDictionaryGetValue(table, peer_id) : NULL;
151
152 return true;
153 }
154
155 static bool SOSPeerCopyPersistedManifest(SOSManifestRef* manifest, CFStringRef my_id, CFStringRef peer_id, CFErrorRef *error)
156 {
157 CFTypeRef persistedObject = NULL;
158
159 require(SOSPeerFindDataFor(&persistedObject, my_id, peer_id, error), fail);
160
161 CFDataRef persistedData = NULL;
162
163 if (isData(persistedObject))
164 persistedData = (CFDataRef)persistedObject;
165 else if (isArray(persistedObject) && (CFArrayGetCount((CFArrayRef) persistedObject) == 2))
166 persistedData = CFArrayGetValueAtIndex((CFArrayRef) persistedObject, 1);
167
168 if (isData(persistedData)) {
169 SOSManifestRef createdManifest = SOSManifestCreateWithData(persistedData, error);
170
171 require(createdManifest, fail);
172
173 *manifest = createdManifest;
174 }
175
176 return true;
177
178 fail:
179 return false;
180 }
181
182
183 static bool SOSPeerCopyCoderData(CFDataRef *data, CFStringRef my_id, CFStringRef peer_id, CFErrorRef *error)
184 {
185 CFTypeRef persistedObject = NULL;
186
187 require(SOSPeerFindDataFor(&persistedObject, my_id, peer_id, error), fail);
188
189 CFDataRef persistedData = NULL;
190
191 if (isArray(persistedObject))
192 persistedData = CFArrayGetValueAtIndex((CFArrayRef) persistedObject, 0);
193
194 if (isData(persistedData)) {
195 CFRetainSafe(persistedData);
196 *data = persistedData;
197 }
198
199 return true;
200
201 fail:
202 return false;
203 }
204
205
206 static void SOSPeerPersistData(CFStringRef my_id, CFStringRef peer_id, SOSManifestRef manifest, CFDataRef coderData)
207 {
208 CFMutableArrayRef data_array = CFArrayCreateMutableForCFTypes(0);
209 if (coderData) {
210 CFArrayAppendValue(data_array, coderData);
211 } else {
212 CFDataRef nullData = CFDataCreate(kCFAllocatorDefault, NULL, 0);
213 CFArrayAppendValue(data_array, nullData);
214 CFReleaseNull(nullData);
215 }
216
217 if (manifest) {
218 CFArrayAppendValue(data_array, SOSManifestGetData(manifest));
219 }
220
221 CFMutableDictionaryRef mySubDict = (CFMutableDictionaryRef) CFDictionaryGetValue(SOSPeerGetPersistenceCache(my_id), my_id);
222
223 if (mySubDict == NULL) {
224 mySubDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
225 CFDictionaryAddValue(SOSPeerGetPersistenceCache(my_id), my_id, mySubDict);
226 }
227
228 CFDictionarySetValue(mySubDict, peer_id, data_array);
229
230 CFReleaseNull(data_array);
231
232 SOSPeerFlushPersistenceCache();
233 }
234
235 struct __OpaqueSOSPeer {
236 SOSPeerSendBlock send_block;
237 CFStringRef my_id;
238 CFStringRef peer_id;
239 CFIndex version;
240 SOSManifestRef manifest;
241 CFDataRef manifest_digest;
242 SOSCoderRef coder; // Currently will be used for OTR stuff.
243 };
244
245 static SOSPeerRef SOSPeerCreate_Internal(CFStringRef myPeerID, CFStringRef theirPeerID, CFIndex version, CFErrorRef *error,
246 SOSPeerSendBlock sendBlock) {
247 SOSPeerRef p = calloc(1, sizeof(struct __OpaqueSOSPeer));
248 p->send_block = sendBlock;
249 p->peer_id = theirPeerID;
250 CFRetainSafe(p->peer_id);
251
252 p->version = version;
253
254 p->my_id = myPeerID;
255 CFRetainSafe(myPeerID);
256
257 require(SOSPeerCopyPersistedManifest(&p->manifest, p->my_id, p->peer_id, error), fail);
258
259 return p;
260
261 fail:
262 CFReleaseSafe(p->peer_id);
263 CFReleaseSafe(p->my_id);
264 free(p);
265 return NULL;
266 }
267
268
269 SOSPeerRef SOSPeerCreate(SOSFullPeerInfoRef myPeerInfo, SOSPeerInfoRef peerInfo,
270 CFErrorRef *error, SOSPeerSendBlock sendBlock) {
271
272 if (myPeerInfo == NULL) {
273 SOSCreateError(kSOSErrorUnsupported, CFSTR("Can't create peer without my peer info!"), NULL, error);
274 return NULL;
275 }
276 if (peerInfo == NULL) {
277 SOSCreateError(kSOSErrorUnsupported, CFSTR("Can't create peer without their peer info!"), NULL, error);
278 return NULL;
279 }
280
281 SOSPeerRef result = NULL;
282 SOSPeerRef p = SOSPeerCreate_Internal(SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(myPeerInfo)),
283 SOSPeerInfoGetPeerID(peerInfo),
284 SOSPeerInfoGetVersion(peerInfo),
285 error, sendBlock);
286
287 require(p, fail);
288
289 CFDataRef coderData = NULL;
290 CFErrorRef coderError = NULL;
291
292 if (SOSPeerCopyCoderData(&coderData, p->my_id, p->peer_id, &coderError)
293 && coderData && CFDataGetLength(coderData) != 0) {
294 p->coder = SOSCoderCreateFromData(coderData, &coderError);
295 }
296
297 if (p->coder) {
298 secnotice("peer", "Old coder for me: %@ to peer: %@", p->my_id, p->peer_id);
299 } else {
300 secnotice("peer", "New coder for me: %@ to peer: %@ [Got error: %@]", p->my_id, p->peer_id, coderError);
301
302 p->coder = SOSCoderCreate(peerInfo, myPeerInfo, error);
303
304 if (!p->coder) {
305 SOSPeerDispose(p);
306 p = NULL;
307 }
308 }
309
310 CFReleaseNull(coderData);
311 CFReleaseNull(coderError);
312
313 result = p;
314 p = NULL;
315
316 fail:
317 CFReleaseNull(p);
318 return result;
319 }
320
321 SOSPeerRef SOSPeerCreateSimple(CFStringRef peer_id, CFIndex version, CFErrorRef *error,
322 SOSPeerSendBlock sendBlock) {
323 return SOSPeerCreate_Internal(CFSTR("FakeTestID"), peer_id, version, error, sendBlock);
324 }
325
326 void SOSPeerDispose(SOSPeerRef peer) {
327 CFErrorRef error = NULL;
328 CFDataRef coderData = NULL;
329 if (peer->coder) {
330 coderData = SOSCoderCopyDER(peer->coder, &error);
331 if (coderData == NULL) {
332 secerror("Coder data failed to export (%@), zapping data for me: %@ to peer: %@", error, peer->my_id, peer->peer_id);
333 }
334 CFReleaseNull(error);
335 }
336
337 if (!coderData) {
338 coderData = CFDataCreate(NULL, NULL, 0);
339 }
340
341 SOSPeerPersistData(peer->my_id, peer->peer_id, peer->manifest, coderData);
342
343 CFReleaseNull(coderData);
344 CFReleaseSafe(peer->peer_id);
345 CFReleaseSafe(peer->my_id);
346 if (peer->manifest)
347 SOSManifestDispose(peer->manifest);
348 CFReleaseSafe(peer->manifest_digest);
349 if (peer->coder)
350 SOSCoderDispose(peer->coder);
351
352 free(peer);
353 }
354
355 SOSPeerCoderStatus SOSPeerHandleMessage(SOSPeerRef peer, SOSEngineRef engine, CFDataRef codedMessage, CFErrorRef *error) {
356 CFMutableDataRef message = NULL;
357 SOSPeerCoderStatus coderStatus = kSOSPeerCoderDataReturned;
358
359 if (peer->coder) {
360 coderStatus = SOSCoderUnwrap(peer->coder, peer->send_block, codedMessage, &message, peer->peer_id, error);
361 } else {
362 message = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, codedMessage);
363 }
364
365 switch(coderStatus) {
366 case kSOSPeerCoderDataReturned: {
367 CFStringRef description = SOSMessageCopyDescription(message);
368 secnotice("peer", "Got message from %@: %@", peer->peer_id, description);
369 CFReleaseSafe(description);
370 coderStatus = (SOSEngineHandleMessage(engine, peer, message, error)) ? coderStatus: kSOSPeerCoderFailure;
371 break;
372 }
373 case kSOSPeerCoderNegotiating: // Sent message already in Unwrap.
374 secnotice("peer", "Negotiating with %@: Got: %@", peer->peer_id, codedMessage);
375 break;
376 case kSOSPeerCoderNegotiationCompleted:
377 if (SOSEngineSyncWithPeer(engine, peer, true, error)) {
378 secnotice("peer", "Negotiating with %@ completed: %@" , peer->peer_id, codedMessage);
379 } else {
380 secerror("Negotiating with %@ completed syncWithPeer: %@ calling syncWithAllPeers" , peer->peer_id, error ? *error : NULL);
381 // Clearing the manifest forces SOSEngineSyncWithPeer(engine, peer, false, error) to send a message no matter what.
382 // This is needed because that's what gets called by SOSPeerStartSync, which is what SOSCCSyncWithAllPeers triggers.
383 SOSPeerSetManifest(peer, NULL, NULL);
384 SOSCCSyncWithAllPeers();
385 coderStatus = kSOSPeerCoderFailure;
386 }
387 break;
388 case kSOSPeerCoderFailure: // Probably restart coder
389 secnotice("peer", "Failed handling message from %@: Got: %@", peer->peer_id, codedMessage);
390 SOSCoderReset(peer->coder);
391 coderStatus = SOSCoderStart(peer->coder, peer->send_block, peer->peer_id, error);
392 break;
393 case kSOSPeerCoderStaleEvent: // We received an event we have already processed in the past.
394 secnotice("peer", "StaleEvent from %@: Got: %@", peer->peer_id, codedMessage);
395 break;
396 default:
397 assert(false);
398 break;
399 }
400
401 CFReleaseNull(message);
402
403 return coderStatus;
404 }
405
406 SOSPeerCoderStatus SOSPeerStartSync(SOSPeerRef peer, SOSEngineRef engine, CFErrorRef *error) {
407 SOSPeerCoderStatus coderStatus = kSOSPeerCoderDataReturned;
408
409 if (peer->coder) {
410 coderStatus = SOSCoderStart(peer->coder, peer->send_block, peer->peer_id, error);
411 }
412
413 switch(coderStatus) {
414 case kSOSPeerCoderDataReturned: // fallthrough
415 case kSOSPeerCoderNegotiationCompleted: // fallthrough
416 coderStatus = (SOSEngineSyncWithPeer(engine, peer, false, error)) ? coderStatus: kSOSPeerCoderFailure;
417 break;
418 case kSOSPeerCoderNegotiating: // Sent message already in Unwrap.
419 secnotice("peer", "Started sync with %@", peer->peer_id);
420 break;
421 case kSOSPeerCoderFailure: // Probably restart coder
422 break;
423 default:
424 assert(false);
425 break;
426 }
427 return coderStatus;
428 }
429
430 bool SOSPeerSendMessage(SOSPeerRef peer, CFDataRef message, CFErrorRef *error) {
431 CFMutableDataRef codedMessage = NULL;
432 CFStringRef description = SOSMessageCopyDescription(message);
433
434 SOSPeerCoderStatus coderStatus = kSOSPeerCoderDataReturned;
435
436 if (peer->coder) {
437 coderStatus = SOSCoderWrap(peer->coder, message, &codedMessage, peer->peer_id, error);
438 } else {
439 codedMessage = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, message);
440 }
441 bool ok = true;
442 switch(coderStatus) {
443 case kSOSPeerCoderDataReturned:
444 secnotice("peer", "%@ message: %@", peer->peer_id, description);
445 peer->send_block(codedMessage, error);
446 break;
447 case kSOSPeerCoderNegotiating:
448 secnotice("peer", "%@ coder Negotiating - message not sent", peer->peer_id);
449 ok = SOSCreateErrorWithFormat(kSOSCCError, NULL, error, NULL, CFSTR("%@ failed to send message peer still negotiating"), peer->peer_id);
450 break;
451 default: // includes kSOSPeerCoderFailure
452 secerror("%@ coder failure - message not sent %@", peer->peer_id, error ? *error : NULL);
453 ok = false;
454 break;
455 }
456 CFReleaseSafe(description);
457 return ok;
458 }
459
460 bool SOSPeerCanSendMessage(SOSPeerRef peer) {
461 return (!peer->coder || SOSCoderCanWrap(peer->coder));
462 }
463
464 CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
465 return peer->version;
466 }
467
468 CFStringRef SOSPeerGetID(SOSPeerRef peer) {
469 return peer->peer_id;
470 }
471
472 bool SOSPeersEqual(SOSPeerRef peerA, SOSPeerRef peerB)
473 {
474 // Use mainly to see if peerB is actually this device (peerA)
475 return CFStringCompare(SOSPeerGetID(peerA), SOSPeerGetID(peerB), 0) == kCFCompareEqualTo;
476 }
477
478 bool SOSPeerSetManifest(SOSPeerRef peer, SOSManifestRef manifest, CFErrorRef *error __unused) {
479 CFRetainSafe(manifest);
480 CFReleaseSafe(peer->manifest);
481 peer->manifest = manifest;
482
483 CFReleaseNull(peer->manifest_digest);
484 return true;
485 }
486
487 SOSManifestRef SOSPeerCopyManifest(SOSPeerRef peer, CFErrorRef *error __unused) {
488 if (!peer->manifest) {
489 SecCFCreateError(kSOSPeerHasNoManifest, sErrorDomain, CFSTR("failed to find peer manifest - not yet implemented"), NULL, error);
490 return NULL;
491 }
492
493 CFRetain(peer->manifest);
494 return peer->manifest;
495 }
496
497 CFDataRef SOSPeerCopyManifestDigest(SOSPeerRef peer, CFErrorRef *error) {
498 if (peer->manifest_digest) {
499 CFRetain(peer->manifest_digest);
500 } else {
501 if (peer->manifest) {
502 CFMutableDataRef data = CFDataCreateMutable(NULL, CC_SHA1_DIGEST_LENGTH);
503 if (data) {
504 CFDataSetLength(data, CC_SHA1_DIGEST_LENGTH);
505 CCDigest(kCCDigestSHA1, SOSManifestGetBytePtr(peer->manifest), (CC_LONG)SOSManifestGetSize(peer->manifest), CFDataGetMutableBytePtr(data));
506 peer->manifest_digest = data;
507 CFRetain(peer->manifest_digest);
508 } else {
509 SecCFCreateError(kSOSPeerDigestFailure, sErrorDomain, CFSTR("failed to create digest"), NULL, error);
510 }
511 } else {
512 SecCFCreateError(kSOSPeerHasNoManifest, sErrorDomain, CFSTR("peer has no manifest, can't create digest"), NULL, error);
513 }
514 }
515
516 return peer->manifest_digest;
517 }