2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SOSViews.c - Implementation of views
28 #include <AssertMacros.h>
29 #include <TargetConditionals.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/SecCFRelease.h>
34 #include <utilities/SecXPCError.h>
36 #include <utilities/SecCFError.h>
37 #include <utilities/der_set.h>
38 #include <Security/SecureObjectSync/SOSInternal.h>
40 #include <Security/SecureObjectSync/SOSPeerInfo.h>
41 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
42 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
43 #include <Security/SecureObjectSync/SOSCloudCircle.h>
44 #include <Security/SecureObjectSync/SOSAccount.h>
45 #include <Security/SecureObjectSync/SOSAccountPriv.h>
47 CFStringRef viewMemError
= CFSTR("Failed to get memory for views in PeerInfo");
48 CFStringRef viewUnknownError
= CFSTR("Unknown view(%@) (ViewResultCode=%d)");
49 CFStringRef viewInvalidError
= CFSTR("Peer is invalid for this view(%@) (ViewResultCode=%d)");
52 const CFStringRef kSOSViewKeychainV0_tomb
= CFSTR("KeychainV0-tomb"); // iCloud Keychain backup for v0 peers (no tombstones)
53 const CFStringRef kSOSViewBackupBagV0_tomb
= CFSTR("BackupBagV0-tomb"); // iCloud Keychain backup bag for v0 peers (no tombstones)
54 const CFStringRef kSOSViewWiFi_tomb
= CFSTR("WiFi-tomb");
55 const CFStringRef kSOSViewAutofillPasswords_tomb
= CFSTR("Passwords-tomb");
56 const CFStringRef kSOSViewSafariCreditCards_tomb
= CFSTR("CreditCards-tomb");
57 const CFStringRef kSOSViewiCloudIdentity_tomb
= CFSTR("iCloudIdentity-tomb");
58 const CFStringRef kSOSViewOtherSyncable_tomb
= CFSTR("OtherSyncable-tomb");
61 const CFStringRef kSOSViewKeychainV0
= CFSTR("KeychainV0"); // iCloud Keychain syncing for v0 peers
64 #define DOVIEWMACRO(VIEWNAME, DEFSTRING, CMDSTRING, DEFAULTSETTING, INITIALSYNCSETTING, ALWAYSONSETTING, BACKUPSETTING, V0SETTING) \
65 const CFStringRef kSOSView##VIEWNAME = CFSTR(DEFSTRING);
66 #include "Security/SecureObjectSync/ViewList.list"
69 // Note that by definition, there cannot be a V0 view hint
70 // These will be deprecated for new constants found in SecItemPriv.h
71 const CFStringRef kSOSViewHintPCSMasterKey
= CFSTR("PCS-MasterKey");
72 const CFStringRef kSOSViewHintPCSiCloudDrive
= CFSTR("PCS-iCloudDrive");
73 const CFStringRef kSOSViewHintPCSPhotos
= CFSTR("PCS-Photos");
74 const CFStringRef kSOSViewHintPCSCloudKit
= CFSTR("PCS-CloudKit");
75 const CFStringRef kSOSViewHintPCSEscrow
= CFSTR("PCS-Escrow");
76 const CFStringRef kSOSViewHintPCSFDE
= CFSTR("PCS-FDE");
77 const CFStringRef kSOSViewHintPCSMailDrop
= CFSTR("PCS-Maildrop");
78 const CFStringRef kSOSViewHintPCSiCloudBackup
= CFSTR("PCS-Backup");
79 const CFStringRef kSOSViewHintPCSNotes
= CFSTR("PCS-Notes");
80 const CFStringRef kSOSViewHintPCSiMessage
= CFSTR("PCS-iMessage");
81 const CFStringRef kSOSViewHintPCSFeldspar
= CFSTR("PCS-Feldspar");
83 const CFStringRef kSOSViewHintAppleTV
= CFSTR("AppleTV");
84 const CFStringRef kSOSViewHintHomeKit
= CFSTR("HomeKit");
86 CFMutableSetRef
SOSViewCopyViewSet(ViewSetKind setKind
) {
87 CFMutableSetRef result
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
90 #define __TYPE_MEMBER_ false
91 #define __TYPE_MEMBER_D true
92 #define __TYPE_MEMBER_I true
93 #define __TYPE_MEMBER_A true
94 #define __TYPE_MEMBER_V true
95 #define __TYPE_MEMBER_B true
96 #define DOVIEWMACRO(VIEWNAME, DEFSTRING, CMDSTRING, DEFAULT, INITIAL, ALWAYSON, BACKUP, V0) \
97 if ((setKind == kViewSetAll) || \
98 ((setKind == kViewSetDefault) && __TYPE_MEMBER_##DEFAULT) || \
99 ((setKind == kViewSetInitial) && __TYPE_MEMBER_##INITIAL) || \
100 ((setKind == kViewSetAlwaysOn) && __TYPE_MEMBER_##ALWAYSON) || \
101 ((setKind == kViewSetRequiredForBackup) && __TYPE_MEMBER_##BACKUP) || \
102 ((setKind == kViewSetV0) && __TYPE_MEMBER_##V0) ) { \
103 CFSetAddValue(result, kSOSView##VIEWNAME); \
106 #include "Security/SecureObjectSync/ViewList.list"
111 CFGiblisGetSingleton(CFSetRef
, SOSViewsGetV0ViewSet
, defaultViewSet
, ^{
112 // Since peer->views must never be NULL, fill in with a default
113 const void *values
[] = { kSOSViewKeychainV0
};
114 *defaultViewSet
= CFSetCreate(kCFAllocatorDefault
, values
, array_size(values
), &kCFTypeSetCallBacks
);
117 CFGiblisGetSingleton(CFSetRef
, SOSViewsGetV0SubviewSet
, subViewSet
, (^{
118 // Since peer->views must never be NULL, fill in with a default
119 *subViewSet
= SOSViewCopyViewSet(kViewSetV0
);
122 CFGiblisGetSingleton(CFSetRef
, SOSViewsGetV0BackupViewSet
, defaultViewSet
, ^{
123 const void *values
[] = { kSOSViewKeychainV0_tomb
};
124 *defaultViewSet
= CFSetCreate(kCFAllocatorDefault
, values
, array_size(values
), &kCFTypeSetCallBacks
);
127 CFGiblisGetSingleton(CFSetRef
, SOSViewsGetV0BackupBagViewSet
, defaultViewSet
, ^{
128 const void *values
[] = { kSOSViewBackupBagV0_tomb
};
129 *defaultViewSet
= CFSetCreate(kCFAllocatorDefault
, values
, array_size(values
), &kCFTypeSetCallBacks
);
133 CFGiblisGetSingleton(CFSetRef
, SOSViewsGetInitialSyncSubviewSet
, subViewSet
, (^{
134 *subViewSet
= SOSViewCopyViewSet(kViewSetInitial
);
138 bool SOSViewsIsV0Subview(CFStringRef viewName
) {
139 return CFSetContainsValue(SOSViewsGetV0SubviewSet(), viewName
);
142 CFSetRef sTestViewSet
= NULL
;
143 void SOSViewsSetTestViewsSet(CFSetRef testViewNames
) {
144 CFRetainAssign(sTestViewSet
, testViewNames
);
147 CFSetRef
SOSViewsGetAllCurrent(void) {
148 static dispatch_once_t dot
;
149 static CFMutableSetRef allViews
= NULL
;
150 dispatch_once(&dot
, ^{
151 allViews
= SOSViewCopyViewSet(kViewSetAll
);
153 CFSetAddValue(allViews
, kSOSViewKeychainV0
);
154 if(sTestViewSet
) CFSetUnion(allViews
, sTestViewSet
);
159 const char *SOSViewsXlateAction(SOSViewActionCode action
) {
161 case kSOSCCViewEnable
: return "kSOSCCViewEnable";
162 case kSOSCCViewDisable
: return "kSOSCCViewDisable";
163 case kSOSCCViewQuery
: return "kSOSCCViewQuery";
164 default: return "unknownViewAction";
169 // Eventually this will want to know the gestalt or security properties...
170 void SOSViewsForEachDefaultEnabledViewName(void (^operation
)(CFStringRef viewName
)) {
171 CFMutableSetRef defaultViews
= SOSViewCopyViewSet(kViewSetDefault
);
173 CFSetForEach(defaultViews
, ^(const void *value
) {
174 CFStringRef name
= asString(value
, NULL
);
181 CFReleaseNull(defaultViews
);
184 static bool SOSViewsIsKnownView(CFStringRef viewname
) {
185 CFSetRef allViews
= SOSViewsGetAllCurrent();
186 if(CFSetContainsValue(allViews
, viewname
)) return true;
187 secnotice("views","Not a known view");
191 static bool viewErrorReport(CFIndex errorCode
, CFErrorRef
*error
, CFStringRef format
, CFStringRef viewname
, int retval
) {
192 return SOSCreateErrorWithFormat(errorCode
, NULL
, error
, NULL
, format
, viewname
, retval
);
195 static bool SOSViewsRequireIsKnownView(CFStringRef viewname
, CFErrorRef
* error
) {
196 return SOSViewsIsKnownView(viewname
) || viewErrorReport(kSOSErrorNameMismatch
, error
, viewUnknownError
, viewname
, kSOSCCNoSuchView
);
199 bool SOSPeerInfoIsEnabledView(SOSPeerInfoRef pi
, CFStringRef viewName
) {
200 if (pi
->version
< kSOSPeerV2BaseVersion
) {
201 return CFSetContainsValue(SOSViewsGetV0ViewSet(), viewName
);
203 return SOSPeerInfoV2DictionaryHasSetContaining(pi
, sViewsKey
, viewName
);
207 void SOSPeerInfoWithEnabledViewSet(SOSPeerInfoRef pi
, void (^operation
)(CFSetRef enabled
)) {
208 if (pi
->version
< kSOSPeerV2BaseVersion
) {
209 operation(SOSViewsGetV0ViewSet());
211 SOSPeerInfoV2DictionaryWithSet(pi
, sViewsKey
, operation
);
215 CFMutableSetRef
SOSPeerInfoCopyEnabledViews(SOSPeerInfoRef pi
) {
216 if (pi
->version
< kSOSPeerV2BaseVersion
) {
217 return CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(SOSViewsGetV0ViewSet()), SOSViewsGetV0ViewSet());
219 CFMutableSetRef views
= SOSPeerInfoV2DictionaryCopySet(pi
, sViewsKey
);
221 // This is unexpected: log and return an empty set to prevent <rdar://problem/21938868>
222 secerror("%@ v2 peer has no views", SOSPeerInfoGetPeerID(pi
));
223 views
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
229 CFSetRef
SOSPeerInfoGetPermittedViews(SOSPeerInfoRef pi
) {
230 return SOSViewsGetAllCurrent();
233 static void SOSPeerInfoSetViews(SOSPeerInfoRef pi
, CFSetRef newviews
) {
235 secnotice("views","Asked to swap to NULL views");
238 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, newviews
);
241 static bool SOSPeerInfoViewIsValid(SOSPeerInfoRef pi
, CFStringRef viewname
) {
245 SOSViewResultCode
SOSViewsEnable(SOSPeerInfoRef pi
, CFStringRef viewname
, CFErrorRef
*error
) {
246 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
248 CFMutableSetRef newviews
= SOSPeerInfoCopyEnabledViews(pi
);
249 require_action_quiet(newviews
, fail
,
250 SOSCreateError(kSOSErrorAllocationFailure
, viewMemError
, NULL
, error
));
251 require_action_quiet(SOSViewsRequireIsKnownView(viewname
, error
), fail
,
252 retval
= kSOSCCNoSuchView
);
253 require_action_quiet(SOSPeerInfoViewIsValid(pi
, viewname
), fail
,
254 viewErrorReport(kSOSErrorNameMismatch
, error
, viewInvalidError
, viewname
, retval
= kSOSCCViewNotQualified
));
255 CFSetAddValue(newviews
, viewname
);
256 SOSPeerInfoSetViews(pi
, newviews
);
257 CFReleaseSafe(newviews
);
258 return kSOSCCViewMember
;
261 CFReleaseNull(newviews
);
262 secnotice("views","Failed to enable view(%@): %@", viewname
, error
? *error
: NULL
);
266 bool SOSViewSetEnable(SOSPeerInfoRef pi
, CFSetRef viewSet
) {
267 __block
bool addedView
= false;
268 CFMutableSetRef newviews
= SOSPeerInfoCopyEnabledViews(pi
);
269 require_action_quiet(newviews
, errOut
, secnotice("views", "failed to copy enabled views"));
271 CFSetForEach(viewSet
, ^(const void *value
) {
272 CFStringRef viewName
= (CFStringRef
) value
;
273 if(SOSViewsIsKnownView(viewName
) && SOSPeerInfoViewIsValid(pi
, viewName
)) {
274 if (!CFSetContainsValue(newviews
, viewName
)) {
276 CFSetAddValue(newviews
, viewName
);
279 secnotice("views", "couldn't add view %@", viewName
);
282 require_quiet(addedView
, errOut
);
284 SOSPeerInfoSetViews(pi
, newviews
);
287 CFReleaseNull(newviews
);
292 SOSViewResultCode
SOSViewsDisable(SOSPeerInfoRef pi
, CFStringRef viewname
, CFErrorRef
*error
) {
293 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
294 CFMutableSetRef newviews
= SOSPeerInfoCopyEnabledViews(pi
);
295 require_action_quiet(newviews
, fail
,
296 SOSCreateError(kSOSErrorAllocationFailure
, viewMemError
, NULL
, error
));
297 require_action_quiet(SOSViewsRequireIsKnownView(viewname
, error
), fail
, retval
= kSOSCCNoSuchView
);
299 CFSetRemoveValue(newviews
, viewname
);
300 SOSPeerInfoSetViews(pi
, newviews
);
301 CFReleaseSafe(newviews
);
302 return kSOSCCViewNotMember
;
305 CFReleaseNull(newviews
);
306 secnotice("views","Failed to disable view(%@): %@", viewname
, error
? *error
: NULL
);
311 bool SOSViewSetDisable(SOSPeerInfoRef pi
, CFSetRef viewSet
) {
312 __block
bool removed
= false;
313 CFMutableSetRef newviews
= SOSPeerInfoCopyEnabledViews(pi
);
314 require_action_quiet(newviews
, errOut
, secnotice("views", "failed to copy enabled views"));
316 CFSetForEach(viewSet
, ^(const void *value
) {
317 CFStringRef viewName
= (CFStringRef
) value
;
318 if(SOSViewsIsKnownView(viewName
) && CFSetContainsValue(newviews
, viewName
)) {
320 CFSetRemoveValue(newviews
, viewName
);
322 secnotice("views", "couldn't delete view %@", viewName
);
326 require_quiet(removed
, errOut
);
328 SOSPeerInfoSetViews(pi
, newviews
);
331 CFReleaseNull(newviews
);
336 SOSViewResultCode
SOSViewsQuery(SOSPeerInfoRef pi
, CFStringRef viewname
, CFErrorRef
*error
) {
337 SOSViewResultCode retval
= kSOSCCNoSuchView
;
338 CFSetRef views
= NULL
;
339 require_quiet(SOSViewsRequireIsKnownView(viewname
, error
), fail
);
341 views
= SOSPeerInfoCopyEnabledViews(pi
);
343 retval
= kSOSCCViewNotMember
;
344 CFReleaseNull(views
);
348 // kSOSViewKeychainV0 is set if there is a V0 PeerInfo in the circle. It represents all of the subviews in
349 // SOSViewsGetV0SubviewSet() so we return kSOSCCViewMember for that case. kSOSViewKeychainV0 and the subviews
350 // are mutually exclusive.
351 else if(CFSetContainsValue(views
, kSOSViewKeychainV0
) && CFSetContainsValue(SOSViewsGetV0SubviewSet(), viewname
)) {
352 retval
= kSOSCCViewMember
;
354 retval
= (CFSetContainsValue(views
, viewname
)) ? kSOSCCViewMember
: kSOSCCViewNotMember
;
357 CFReleaseNull(views
);
361 secnotice("views","Failed to query view(%@): %@", viewname
, error
? *error
: NULL
);
362 CFReleaseNull(views
);
366 static CFArrayRef
SOSCreateActiveViewIntersectionArrayForPeerInfos(SOSPeerInfoRef pi1
, SOSPeerInfoRef pi2
) {
367 CFMutableArrayRef retval
= NULL
;
368 CFSetRef views1
= SOSPeerInfoCopyEnabledViews(pi1
);
369 CFSetRef views2
= SOSPeerInfoCopyEnabledViews(pi2
);
370 size_t count
= CFSetGetCount(views1
);
372 CFReleaseNull(views1
);
373 CFReleaseNull(views2
);
376 CFStringRef pi1views
[count
];
378 retval
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
379 CFSetGetValues(views1
, (const void **) &pi1views
);
380 for(size_t i
= 0; i
< count
; i
++) {
381 if(CFSetContainsValue(views2
, pi1views
[i
])) {
382 CFArrayAppendValue(retval
, pi1views
[i
]);
385 CFReleaseNull(views1
);
386 CFReleaseNull(views2
);
391 CFArrayRef
SOSCreateActiveViewIntersectionArrayForPeerID(SOSAccountRef account
, CFStringRef peerID
) {
392 CFArrayRef retval
= NULL
;
393 SOSPeerInfoRef myPI
= SOSAccountGetMyPeerInfo(account
);
394 SOSPeerInfoRef theirPI
= SOSAccountCopyPeerWithID(account
, peerID
, NULL
);
395 require_action_quiet(myPI
, errOut
, retval
= NULL
);
396 require_action_quiet(theirPI
, errOut
, retval
= NULL
);
398 retval
= SOSCreateActiveViewIntersectionArrayForPeerInfos(myPI
, theirPI
);
401 CFReleaseNull(theirPI
);
405 // This needs to create a dictionary of sets of intersected views for an account
406 CFDictionaryRef
SOSViewsCreateActiveViewMatrixDictionary(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
) {
407 CFMutableDictionaryRef retval
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
408 SOSPeerInfoRef myPI
= SOSAccountGetMyPeerInfo(account
);
410 // For now, all views require that a valid member peer is in the circle and active/valid
411 CFMutableSetRef peers
= SOSCircleCopyPeers(circle
, kCFAllocatorDefault
);
413 require_action_quiet(retval
, errOut
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("Could not allocate ViewMatrix"), NULL
, error
));
414 require_action_quiet(myPI
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("Could not find our PeerInfo"), NULL
, error
));
415 require(peers
, errOut
);
417 CFSetRef myViews
= SOSPeerInfoCopyEnabledViews(myPI
);
420 CFSetForEach(myViews
, ^(const void *value
) {
421 CFStringRef viewname
= (CFStringRef
) value
;
422 CFMutableSetRef viewset
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
423 CFSetForEach(peers
, ^(const void *peervalue
) {
424 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) peervalue
;
425 CFSetRef piViews
= SOSPeerInfoCopyEnabledViews(pi
);
427 if(piViews
&& CFSetContainsValue(piViews
, viewname
)) {
428 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
429 CFSetAddValue(viewset
, peerID
);
431 CFReleaseNull(piViews
);
434 CFDictionaryAddValue(retval
, viewname
, viewset
);
437 if(CFDictionaryGetCount(retval
) == 0) goto errOut
; // Not really an error - just no intersection of views with anyone
438 CFReleaseNull(peers
);
439 CFReleaseNull(myViews
);
443 CFReleaseNull(retval
);
444 CFReleaseNull(peers
);
452 /* Need XPC way to carry CFSets of views */
455 CFSetRef
CreateCFSetRefFromXPCObject(xpc_object_t xpcSetDER
, CFErrorRef
* error
) {
456 CFSetRef retval
= NULL
;
457 require_action_quiet(xpcSetDER
, errOut
, SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull
, sSecXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("Unexpected Null Set to decode")));
459 require_action_quiet(xpc_get_type(xpcSetDER
) == XPC_TYPE_DATA
, errOut
, SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType
, sSecXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("xpcSetDER not data, got %@"), xpcSetDER
));
461 const uint8_t* der
= xpc_data_get_bytes_ptr(xpcSetDER
);
462 const uint8_t* der_end
= der
+ xpc_data_get_length(xpcSetDER
);
463 der
= der_decode_set(kCFAllocatorDefault
, kCFPropertyListMutableContainersAndLeaves
, &retval
, error
, der
, der_end
);
464 if (der
!= der_end
) {
465 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
470 CFReleaseNull(retval
);
474 xpc_object_t
CreateXPCObjectWithCFSetRef(CFSetRef setref
, CFErrorRef
*error
) {
475 xpc_object_t result
= NULL
;
476 size_t data_size
= 0;
477 uint8_t *data
= NULL
;
478 require_action_quiet(setref
, errOut
, SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull
, sSecXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("Unexpected Null Set to encode")));
479 require_quiet((data_size
= der_sizeof_set(setref
, error
)) != 0, errOut
);
480 require_quiet((data
= (uint8_t *)malloc(data_size
)) != NULL
, errOut
);
482 der_encode_set(setref
, error
, data
, data
+ data_size
);
483 result
= xpc_data_create(data
, data_size
);