]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfo.c
Security-57336.10.29.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSPeerInfo.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <AssertMacros.h>
26 #include <TargetConditionals.h>
27
28 #include <Security/SecureObjectSync/SOSPeerInfo.h>
29 #include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
30 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
31 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
32 #include <Security/SecureObjectSync/SOSCircle.h>
33 #include <Security/SecureObjectSync/SOSAccountPriv.h>
34 #include <Security/SecureObjectSync/SOSInternal.h>
35 #include <ipc/securityd_client.h>
36
37 #include <CoreFoundation/CFArray.h>
38 #include <dispatch/dispatch.h>
39
40 #include <stdlib.h>
41 #include <assert.h>
42
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/SecCFRelease.h>
45 #include <utilities/SecCFError.h>
46 #include <utilities/SecXPCError.h>
47
48 #include <utilities/der_plist.h>
49 #include <utilities/der_plist_internal.h>
50 #include <corecrypto/ccder.h>
51 #include <utilities/der_date.h>
52
53 #include <corecrypto/ccdigest.h>
54 #include <corecrypto/ccsha2.h>
55
56
57 #include <CoreFoundation/CoreFoundation.h>
58 #include <CoreFoundation/CFDate.h>
59
60 #include <xpc/xpc.h>
61
62 #if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
63 #include <MobileGestalt.h>
64 #endif
65
66 #include <Security/SecBase64.h>
67 #include <Security/SecKeyPriv.h>
68 #include <Security/SecOTR.h>
69 #include <Security/SecuritydXPC.h>
70
71 CFGiblisWithHashFor(SOSPeerInfo);
72
73
74 const CFStringRef kPIUserDefinedDeviceNameKey = CFSTR("ComputerName");
75 const CFStringRef kPIDeviceModelNameKey = CFSTR("ModelName");
76 const CFStringRef kPIMessageProtocolVersionKey = CFSTR("MessageProtocolVersion");
77 const CFStringRef kPIOSVersionKey = CFSTR("OSVersion");
78
79 // Description Dictionary Entries
80 static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
81 const CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
82 const CFStringRef sVersionKey = CFSTR("ConflictVersion");
83 static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity");
84 static CFStringRef sApplicationDate = CFSTR("ApplicationDate");
85 static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig");
86 static CFStringRef sRetirementDate = CFSTR("RetirementDate");
87
88 // Peerinfo Entries
89 CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription");
90 CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature");
91 CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName");
92
93 //Peer Info V2 Dictionary IDS keys
94 CFStringRef sPreferIDS = CFSTR("PreferIDS");
95 CFStringRef sTransportType = CFSTR("TransportType");
96 CFStringRef sDeviceID = CFSTR("DeviceID");
97
98 const CFStringRef peerIDLengthKey = CFSTR("idLength");
99
100 SOSPeerInfoRef SOSPeerInfoAllocate(CFAllocatorRef allocator) {
101 return CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
102 }
103
104 SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer) {
105 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
106 if (pubKeyBytes == NULL)
107 return NULL;
108 CFAllocatorRef allocator = CFGetAllocator(peer);
109 SecKeyRef pubKey = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes);
110 return pubKey;
111 }
112
113 CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) {
114 CFDataRef pubKeyBytes = NULL;
115
116 pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
117 if (!pubKeyBytes || CFGetTypeID(pubKeyBytes) != CFDataGetTypeID()) {
118 pubKeyBytes = NULL;
119 }
120
121 return pubKeyBytes;
122 }
123
124 static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) {
125 ccdigest_di_decl(di, ctx);
126 ccdigest_init(di, ctx);
127 void *ctx_p = ctx;
128 if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false;
129 ccdigest_final(di, ctx, hashresult);
130 return true;
131 }
132
133
134 #define SIGLEN 128
135 static CFDataRef sosSignHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) {
136 OSStatus stat;
137 size_t siglen = SIGLEN;
138 uint8_t sig[siglen];
139 if((stat = SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen)) != 0) {
140 return NULL;
141 }
142 return CFDataCreate(NULL, sig, (CFIndex)siglen);
143 }
144
145 static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) {
146 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size,
147 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
148 }
149
150 bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
151 bool status = false;
152 const struct ccdigest_info *di = ccsha256_di();
153 uint8_t hbuf[di->output_size];
154 CFDataRef newSignature = NULL;
155
156 require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail,
157 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error));
158
159 newSignature = sosSignHash(privKey, di, hbuf);
160 require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error));
161
162 CFReleaseNull(peer->signature);
163 peer->signature = newSignature;
164 newSignature = NULL;
165 status = true;
166
167 fail:
168 CFReleaseNull(newSignature);
169 return status;
170 }
171
172 // Return true (1) if the signature verifies.
173 bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) {
174 bool result = false;
175 const struct ccdigest_info *di = ccsha256_di();
176 uint8_t hbuf[di->output_size];
177
178 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer);
179 require_action_quiet(pubKey, error_out,
180 SOSErrorCreate(kSOSErrorNoKey, error, NULL,
181 CFSTR("Couldn't find pub key for %@"), peer));
182
183 require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out);
184
185 require_action_quiet(sosVerifyHash(pubKey, di, hbuf, peer->signature), error_out,
186 SOSErrorCreate(kSOSErrorBadSignature, error, NULL,
187 CFSTR("Signature didn't verify for %@"), peer));
188 result = true;
189
190 error_out:
191 CFReleaseNull(pubKey);
192 return result;
193 }
194
195 void SOSPeerInfoSetVersionNumber(SOSPeerInfoRef pi, int version) {
196 pi->version = version;
197 CFNumberRef versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
198 CFDictionarySetValue(pi->description, sVersionKey, versionNumber);
199 CFReleaseNull(versionNumber);
200 }
201
202 static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
203 CFDictionaryRef gestalt, CFDataRef backup_key,
204 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
205 CFSetRef enabledViews,
206 SecKeyRef signingKey, CFErrorRef* error,
207 void (^ description_modifier)(CFMutableDictionaryRef description)) {
208 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
209 pi->gestalt = gestalt;
210 CFRetain(pi->gestalt);
211
212 pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
213 CFDataRef publicBytes = NULL;
214 CFNumberRef versionNumber = NULL;
215
216 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
217 if (publicKey == NULL) {
218 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error);
219 CFReleaseNull(pi);
220 goto exit;
221 }
222
223 OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes);
224
225 if (result != errSecSuccess) {
226 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
227 CFReleaseNull(pi);
228 goto exit;
229 }
230
231 pi->signature = CFDataCreateMutable(allocator, 0);
232
233 versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
234
235 pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
236 sVersionKey, versionNumber,
237 sPublicKeyKey, publicBytes,
238 sGestaltKey, pi->gestalt,
239 NULL);
240
241 if (backup_key != NULL)
242 SOSPeerInfoV2DictionarySetValue(pi, sBackupKeyKey, backup_key);
243
244 description_modifier(pi->description);
245
246
247 pi->id = SOSCopyIDOfKey(publicKey, error);
248 CFReleaseNull(publicKey);
249
250 require_quiet(pi->id, exit);
251
252 // ================ V2 Additions Start
253
254 if(!SOSPeerInfoUpdateToV2(pi, error)) {
255 CFReleaseNull(pi);
256 goto exit;
257 }
258
259 // V2DictionarySetValue handles NULL as remove
260 SOSPeerInfoV2DictionarySetValue(pi, sBackupKeyKey, backup_key);
261 SOSPeerInfoV2DictionarySetValue(pi, sDeviceID, IDSID);
262 SOSPeerInfoV2DictionarySetValue(pi, sTransportType, transportType);
263 SOSPeerInfoV2DictionarySetValue(pi, sPreferIDS, preferIDS);
264
265 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
266
267 // ================ V2 Additions End
268
269 if (!SOSPeerInfoSign(signingKey, pi, error)) {
270 CFReleaseNull(pi);
271 goto exit;
272 }
273
274 exit:
275 CFReleaseNull(versionNumber);
276 CFReleaseNull(publicBytes);
277 return pi;
278 }
279
280 SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, CFErrorRef* error) {
281 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, signingKey, error, ^(CFMutableDictionaryRef description) {});
282 }
283
284 SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key,
285 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
286 CFSetRef enabledViews,
287 SecKeyRef signingKey, CFErrorRef* error)
288 {
289 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, enabledViews, signingKey, error, ^(CFMutableDictionaryRef description) {});
290 }
291
292
293 SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
294 return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, signingKey, error, ^(CFMutableDictionaryRef description) {
295 CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
296 });
297
298 }
299
300
301 SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) {
302 if(!toCopy) return NULL;
303 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
304
305 pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description);
306 pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
307
308 pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
309 pi->id = CFStringCreateCopy(allocator, toCopy->id);
310
311 pi->version = toCopy->version;
312 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error);
313
314 return pi;
315 }
316
317
318 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi) {
319 return pi->version >= PEERINFO_CURRENT_VERSION;
320 }
321
322 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi) {
323 return pi->version >= kSOSPeerV2BaseVersion;
324 }
325
326 SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
327 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, CFSetRef enabledViews,
328 SecKeyRef signingKey, CFErrorRef* error) {
329
330 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
331 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoUpdateToV2(pi, error);
332
333 SOSPeerInfoSetSerialNumber(pi);
334
335 if (IDSID) {
336 SOSPeerInfoV2DictionarySetValue(pi, sDeviceID, IDSID);
337 }
338 if (transportType) {
339 SOSPeerInfoV2DictionarySetValue(pi, sTransportType, transportType);
340 }
341 if (preferIDS) {
342 SOSPeerInfoV2DictionarySetValue(pi, sPreferIDS, preferIDS);
343 }
344 if (enabledViews) {
345 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
346 }
347
348 if(!SOSPeerInfoSign(signingKey, pi, error)) {
349 CFReleaseNull(pi);
350 }
351
352 return pi;
353 }
354
355
356 static SOSPeerInfoRef SOSPeerInfoCopyWithModification(CFAllocatorRef allocator, SOSPeerInfoRef original,
357 SecKeyRef signingKey, CFErrorRef *error,
358 bool (^modification)(SOSPeerInfoRef peerToModify, CFErrorRef *error)) {
359
360 SOSPeerInfoRef result = NULL;
361 SOSPeerInfoRef copy = SOSPeerInfoCreateCopy(allocator, original, error);
362
363 require_quiet(modification(copy, error), fail);
364
365 require_quiet(SOSPeerInfoSign(signingKey, copy, error), fail);
366
367 CFTransferRetained(result, copy);
368
369 fail:
370 CFReleaseNull(copy);
371 return result;
372
373 }
374
375 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
376 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
377 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
378 if(!gestalt || !peerToModify) return false;
379 CFRetainAssign(peerToModify->gestalt, gestalt);
380 CFDictionarySetValue(peerToModify->description, sGestaltKey, peerToModify->gestalt);
381 return true;
382
383 });
384 }
385
386
387 SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef *error) {
388 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
389 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
390 if (backupKey != NULL)
391 SOSPeerInfoV2DictionarySetValue(peerToModify, sBackupKeyKey, backupKey);
392 else
393 SOSPeerInfoV2DictionaryRemoveValue(peerToModify, sBackupKeyKey);
394 return true;
395 });
396 }
397
398 static CFDictionaryRef SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord){
399
400 CFMutableDictionaryRef existingEscrowRecords = SOSPeerInfoCopyEscrowRecord(peer);
401
402 if(escrowRecord == NULL && existingEscrowRecords != NULL)
403 {
404 CFDictionaryRemoveValue(existingEscrowRecords, dsid);
405 return existingEscrowRecords;
406 }
407
408 if(existingEscrowRecords == NULL)
409 existingEscrowRecords = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
410
411 CFDictionarySetValue(existingEscrowRecords, dsid, escrowRecord);
412
413 return existingEscrowRecords;
414 }
415
416
417 SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error) {
418 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
419 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
420
421 CFDictionaryRef updatedEscrowRecords = SOSPeerInfoUpdateAndCopyRecord(peerToModify, dsid, escrowRecord);
422 SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, updatedEscrowRecords);
423 CFReleaseNull(updatedEscrowRecords);
424 return true;
425 });
426 }
427
428 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
429 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
430 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
431 if(escrowRecords != NULL)
432 SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, escrowRecords);
433
434 return true;
435 });
436 }
437
438 CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer) {
439 return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
440 }
441
442 CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer){
443 return SOSPeerInfoV2DictionaryCopyDictionary(peer, sEscrowRecord);
444 }
445
446 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
447 CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
448 bool success = bk != NULL;
449 CFReleaseNull(bk);
450 return success;
451 }
452
453 SOSPeerInfoRef SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
454 SOSViewActionCode action, CFStringRef viewname, SOSViewResultCode *retval,
455 SecKeyRef signingKey, CFErrorRef* error) {
456 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
457 if(action == kSOSCCViewEnable) {
458 *retval = SOSViewsEnable(pi, viewname, error);
459 require((kSOSCCViewMember == *retval), exit);
460 } else if(action == kSOSCCViewDisable) {
461 *retval = SOSViewsDisable(pi, viewname, error);
462 require((kSOSCCViewNotMember == *retval), exit);
463 }
464
465 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, *retval = kSOSCCGeneralViewError);
466 return pi;
467
468 exit:
469 CFReleaseNull(pi);
470 return NULL;
471 }
472
473
474 CFStringRef sPingKey = CFSTR("Ping");
475
476 SOSPeerInfoRef SOSPeerInfoCopyWithPing(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, SecKeyRef signingKey, CFErrorRef* error) {
477 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
478 CFDataRef ping = CFDataCreateWithRandomBytes(8);
479 SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping);
480 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi);
481 pi->id = SOSCopyIDOfKey(pub_key, error);
482 require_quiet(pi->id, exit);
483 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
484 exit:
485 CFReleaseNull(ping);
486 CFReleaseNull(pub_key);
487 return pi;
488 }
489
490
491 SOSViewResultCode SOSPeerInfoViewStatus(SOSPeerInfoRef pi, CFStringRef view, CFErrorRef *error) {
492 return SOSViewsQuery(pi, view, error);
493 }
494
495
496 SOSPeerInfoRef SOSPeerInfoCopyWithSecurityPropertyChange(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
497 SOSSecurityPropertyActionCode action, CFStringRef property, SOSSecurityPropertyResultCode *retval,
498 SecKeyRef signingKey, CFErrorRef* error) {
499 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
500 if(action == kSOSCCSecurityPropertyEnable) {
501 *retval = SOSSecurityPropertyEnable(pi, property, error);
502 require((kSOSCCSecurityPropertyValid == *retval), exit);
503 } else if(action == kSOSCCSecurityPropertyDisable) {
504 *retval = SOSSecurityPropertyDisable(pi, property, error);
505 require((kSOSCCSecurityPropertyNotValid == *retval), exit);
506 }
507
508 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, *retval = kSOSCCGeneralViewError);
509 return pi;
510
511 exit:
512 CFReleaseNull(pi);
513 return NULL;
514 }
515
516 SOSViewResultCode SOSPeerInfoSecurityPropertyStatus(SOSPeerInfoRef pi, CFStringRef property, CFErrorRef *error) {
517 return SOSSecurityPropertyQuery(pi, property, error);
518 }
519
520
521
522 static void SOSPeerInfoDestroy(CFTypeRef aObj) {
523 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
524
525 if(!pi) return;
526 CFReleaseNull(pi->description);
527 CFReleaseNull(pi->signature);
528 CFReleaseNull(pi->gestalt);
529 CFReleaseNull(pi->id);
530 CFReleaseNull(pi->v2Dictionary);
531 CFReleaseNull(pi->secproperties);
532 }
533
534 static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
535 SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs;
536 SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs;
537 if(!lpeer || !rpeer) return false;
538 return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature);
539 }
540
541
542 CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) {
543 // The code below is necessary but not sufficient; not returning a CFComparisonResult
544 // It probably is OK to say that a NULL is < <non-NULL>
545 if (val1 == NULL || val2 == NULL) {
546 ptrdiff_t dv = val1 - val2;
547 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
548 }
549
550 CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1);
551 CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2);
552 if (v1 == NULL || v2 == NULL) {
553 ptrdiff_t dv = (const void *)v1 - (const void *)v2;
554 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
555 }
556
557 return CFStringCompare(v1, v2, 0);
558 }
559
560 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
561 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
562
563 return CFHash(peer->description) ^ CFHash(peer->signature);
564 }
565
566 static CFStringRef copyDescriptionWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){
567
568 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
569 CFStringRef description = NULL;
570
571
572 // Get the format options we care about:
573 CFNumberRef peerIDLengthNumber = formatOptions ? CFDictionaryGetValue(formatOptions, peerIDLengthKey) : NULL;
574 bool useSyncDFormat = formatOptions && CFDictionaryContainsKey(formatOptions, CFSTR("SyncD"));
575
576 // Calculate the truncated length
577 CFIndex truncatedLength = CFStringGetLength(pi->id);
578 if (isNumber(peerIDLengthNumber)) {
579 CFIndex peerIDMaximumLength = truncatedLength;
580 CFNumberGetValue(peerIDLengthNumber, kCFNumberCFIndexType, &peerIDMaximumLength);
581 truncatedLength = MIN(truncatedLength, peerIDMaximumLength);
582 }
583 CFStringRef displayPeerID = CFStringCreateWithSubstring(kCFAllocatorDefault, pi->id, CFRangeMake(0, truncatedLength));
584
585 CFStringRef objectPrefix = useSyncDFormat ? CFSTR("PI") :
586 CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("PeerInfo@%p"), pi);
587
588 CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey);
589
590 CFStringRef transportType = SOSPeerInfoV2DictionaryCopyString(pi, sTransportType);
591 CFStringRef deviceID = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID);
592
593 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<%@: %s%s '%@' %@ %@ %@ %@ %@ %ld>"),
594 objectPrefix,
595 SOSPeerInfoIsRetirementTicket(pi) ? "R" : "-",
596 SOSPeerInfoHasBackupKey(pi) ? "B" : "-",
597 CFDictionaryGetValue(pi->gestalt, kPIUserDefinedDeviceNameKey),
598 CFDictionaryGetValue(pi->gestalt, kPIDeviceModelNameKey),
599 osVersion ? osVersion : CFSTR("????"),
600 transportType ? transportType : CFSTR("KVS"),
601 deviceID ? deviceID : CFSTR(""),
602 displayPeerID,
603 pi->version);
604
605 CFReleaseNull(transportType);
606 CFReleaseNull(deviceID);
607
608 CFReleaseNull(objectPrefix);
609 CFReleaseNull(displayPeerID);
610
611 return description;
612 }
613
614 static CFStringRef SOSPeerInfoCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
615
616 CFStringRef description = NULL;
617
618 description = copyDescriptionWithFormatOptions(aObj, formatOptions);
619
620 return description;
621 }
622
623 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
624 CFRetain(pi->gestalt);
625 return pi->gestalt;
626 }
627
628 CFDictionaryRef SOSPeerGetGestalt(SOSPeerInfoRef pi){
629 return pi->gestalt;
630 }
631
632 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
633 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceNameKey);
634 }
635
636 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
637 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelNameKey);
638 }
639
640 CFIndex SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer) {
641 CFIndex version = PEERINFO_CURRENT_VERSION;
642 CFTypeRef val = SOSPeerInfoLookupGestaltValue(peer, kPIMessageProtocolVersionKey);
643 if (val && CFGetTypeID(val) == CFNumberGetTypeID())
644 CFNumberGetValue(val, kCFNumberCFIndexType, &version);
645 return version;
646 }
647
648 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
649 return CFDictionaryGetValue(pi->gestalt, key);
650 }
651
652 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
653 return pi ? pi->id : NULL;
654 }
655
656 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
657 return CFEqualSafe(myPeerID, SOSPeerInfoGetPeerID(pi));
658 }
659
660 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
661 return pi->version;
662 }
663
664 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di,
665 ccdigest_ctx_t ctx, CFErrorRef *error) {
666 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
667
668 if(!pubKeyBytes) {
669 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key"));
670 return false;
671 }
672
673 ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes));
674
675 return true;
676 }
677
678 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di,
679 ccdigest_ctx_t ctx, CFErrorRef *error) {
680 if(SOSPeerInfoVersionHasV2Data(peer)) SOSPeerInfoPackV2Data(peer);
681 size_t description_size = der_sizeof_plist(peer->description, error);
682 uint8_t data_begin[description_size];
683 uint8_t *data_end = data_begin + description_size;
684 uint8_t *encoded = der_encode_plist(peer->description, error, data_begin, data_end);
685
686 if(!encoded) {
687 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed"));
688 return false;
689 }
690
691 ccdigest_update(di, ctx, description_size, data_begin);
692
693 return true;
694 }
695
696
697 static CFDataRef sosCreateDate() {
698 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
699 size_t bufsiz = der_sizeof_date(now, NULL);
700 uint8_t buf[bufsiz];
701 der_encode_date(now, NULL, buf, buf+bufsiz);
702 CFReleaseNull(now);
703 return CFDataCreate(NULL, buf, bufsiz);
704 }
705
706 static CFDateRef sosCreateCFDate(CFDataRef sosdate) {
707 CFDateRef date;
708 der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate),
709 CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate));
710 return date;
711 }
712
713 static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) {
714 CFDataRef appdate = CFDictionaryGetValue(pi->description, sApplicationDate);
715 if(!appdate) return false;
716 ccdigest_di_decl(di, ctx);
717 ccdigest_init(di, ctx);
718 ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate));
719 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false;
720 ccdigest_final(di, ctx, hbuf);
721 return true;
722 }
723
724 SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) {
725 SOSPeerInfoRef result = NULL;
726 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error);
727
728 const struct ccdigest_info *di = ccsha256_di();
729 uint8_t hbuf[di->output_size];
730 CFDataRef usersig = NULL;
731
732 CFDataRef creationDate = sosCreateDate();
733 CFDictionarySetValue(pi->description, sApplicationDate, creationDate);
734 CFReleaseNull(creationDate);
735
736 // Create User Application Signature
737 require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail,
738 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
739
740 usersig = sosSignHash(userkey, di, hbuf);
741 require_action_quiet(usersig, fail,
742 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error));
743
744 CFDictionarySetValue(pi->description, sApplicationUsig, usersig);
745
746 require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail);
747
748 result = pi;
749 pi = NULL;
750
751 fail:
752 CFReleaseNull(usersig);
753 CFReleaseNull(pi);
754 return result;
755 }
756
757 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) {
758 const struct ccdigest_info *di = ccsha256_di();
759 uint8_t hbuf[di->output_size];
760 bool result = false;
761
762 CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig);
763 require_action_quiet(usig, exit,
764 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error));
765 // Verify User Application Signature
766 require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit,
767 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
768 require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit,
769 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error));
770
771 result = SOSPeerInfoVerify(pi, error);
772
773 exit:
774 return result;
775 }
776
777
778 static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) {
779 if(!pi) return NULL;
780 CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry);
781 if(!sosdate) return NULL;
782 CFDateRef date = sosCreateCFDate(sosdate);
783
784 return date;
785 }
786
787 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
788 return sosPeerInfoGetDate(pi, sApplicationDate);
789 }
790
791 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
792 return sosPeerInfoGetDate(pi, sRetirementDate);
793 }
794
795
796
797 //
798 // Gestalt helpers
799 //
800
801 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
802 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceNameKey);
803 return isString(name) ? name : NULL;
804 }
805
806 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
807 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
808 }
809
810 //
811 // Peer Retirement
812 //
813
814
815 SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
816 // Copy PeerInfo
817 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error);
818
819 require(pi, fail);
820
821 // Fill out Resignation Date
822 CFDataRef resignationDate = sosCreateDate();
823 CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate);
824 CFReleaseNull(resignationDate);
825
826 require(SOSPeerInfoSign(privKey, pi, error), fail);
827
828 return pi;
829
830 fail:
831 CFReleaseNull(pi);
832 return NULL;
833 }
834
835 CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) {
836 CFStringRef retval = NULL;
837 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
838 CFDateRef retirement = NULL;
839
840 require_quiet(SOSPeerInfoVerify(pi, error), err);
841
842 retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
843
844 require_action_quiet(retirement, err,
845 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error));
846
847 require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err,
848 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error));
849
850 retval = SOSPeerInfoGetPeerID(pi);
851
852 err:
853 CFReleaseNull(now);
854 CFReleaseNull(retirement);
855 return retval;
856 }
857
858 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) {
859 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
860 CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
861 CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds
862 CFReleaseNull(now);
863 CFReleaseNull(retirement);
864 if(timediff > (max_seconds)) return true;
865 return false;
866 }
867
868 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
869 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
870 return flag != NULL;
871 }
872
873 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
874 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
875 return CFEqualSafe(value, kCFBooleanTrue);
876 }
877
878 SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) {
879 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
880 SOSPeerInfoRef retval = NULL;
881
882 retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error);
883 CFReleaseNull(pubKey);
884 return retval;
885 }
886
887 CFBooleanRef SOSPeerInfoCopyIDSPreference(SOSPeerInfoRef peer){
888 CFBooleanRef preference = (CFBooleanRef)SOSPeerInfoV2DictionaryCopyBoolean(peer, sPreferIDS);
889 return (preference ? preference : CFRetain(kCFBooleanFalse));
890 }
891
892
893 SOSPeerInfoRef SOSPeerInfoSetIDSPreference(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFBooleanRef preference, SecKeyRef signingKey, CFErrorRef *error){
894 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
895 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
896 SOSPeerInfoV2DictionarySetValue(peerToModify, sPreferIDS, preference);
897 return true;
898 });
899 }
900
901 CFStringRef SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer){
902 CFStringRef transportType = (CFStringRef)SOSPeerInfoV2DictionaryCopyString(peer, sTransportType);
903 return (transportType ? transportType : CFRetain(SOSTransportMessageTypeKVS));
904 }
905
906 SOSPeerInfoRef SOSPeerInfoSetTransportType(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef transportType, SecKeyRef signingKey, CFErrorRef *error){
907
908 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
909 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
910 SOSPeerInfoV2DictionarySetValue(peerToModify, sTransportType, transportType);
911 return true;
912 });
913 }
914
915 bool SOSPeerInfoHasDeviceID(SOSPeerInfoRef peer) {
916 return SOSPeerInfoV2DictionaryHasString(peer, sDeviceID);
917 }
918
919 CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer){
920 return (CFStringRef)SOSPeerInfoV2DictionaryCopyString(peer, sDeviceID);
921 }
922
923 SOSPeerInfoRef SOSPeerInfoSetDeviceID(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef IDS, SecKeyRef signingKey, CFErrorRef *error){
924
925 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
926 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
927 SOSPeerInfoV2DictionarySetValue(peerToModify, sDeviceID, IDS);
928 return true;
929 });
930 }
931
932 bool SOSPeerInfoShouldUseIDSTransport(SOSPeerInfoRef myPeer, SOSPeerInfoRef theirPeer){
933
934 CFBooleanRef myPreference = SOSPeerInfoCopyIDSPreference(myPeer);
935 CFStringRef myTransportType = SOSPeerInfoCopyTransportType(myPeer);
936
937 CFBooleanRef theirPreference = SOSPeerInfoCopyIDSPreference(theirPeer);
938 CFStringRef theirTransportType = SOSPeerInfoCopyTransportType(theirPeer);
939
940 bool success = false;
941 //If I'm a galarch KVS + peer is KVS and ids only is true for both == KVS
942 if((CFStringCompare(myTransportType, SOSTransportMessageTypeKVS, 0) == 0 && CFStringCompare(theirTransportType, SOSTransportMessageTypeKVS, 0) == 0)&& (myPreference == kCFBooleanTrue && theirPreference == kCFBooleanTrue))
943 success = false;
944
945 //If transport is IDS Galarch +1 (Pref is true) and peer is KVS (ids is true) == IDS
946 else if ((CFStringCompare(myTransportType, SOSTransportMessageTypeIDS, 0) == 0 && CFStringCompare(theirTransportType, SOSTransportMessageTypeKVS, 0) == 0) && (myPreference == kCFBooleanTrue && theirPreference == kCFBooleanTrue))
947 success = true;
948
949 else if ((CFStringCompare(theirTransportType, SOSTransportMessageTypeIDS, 0) == 0 && CFStringCompare(myTransportType, SOSTransportMessageTypeKVS, 0) == 0) && (theirPreference == kCFBooleanTrue && myPreference == kCFBooleanTrue))
950 success = true;
951
952 //If transport is IDS Galarch +1 and peer is IDS Galarch +1 (prefer IDS is true) == IDS
953 else if ((CFStringCompare(myTransportType, SOSTransportMessageTypeIDS, 0) == 0 && CFStringCompare(theirTransportType, SOSTransportMessageTypeIDS, 0) == 0))
954 success = true;
955
956 //If KVS and KVS prefer IDS is false (set to false or doesn't exist) == KVS
957 else if ((CFStringCompare(myTransportType, SOSTransportMessageTypeKVS, 0) == 0 && CFStringCompare(theirTransportType, SOSTransportMessageTypeKVS, 0) == 0) && (myPreference == kCFBooleanFalse && theirPreference == kCFBooleanFalse))
958 success = false;
959
960 else
961 success = false;
962
963 CFReleaseSafe(myPreference);
964 CFReleaseSafe(myTransportType);
965 CFReleaseSafe(theirPreference);
966 CFReleaseSafe(theirTransportType);
967 return success;
968 }