2 * Copyright (c) 2014 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 * SecAccessControl.c - CoreFoundation based access control object
28 #include <AssertMacros.h>
29 #include <Security/SecAccessControl.h>
30 #include <Security/SecAccessControlPriv.h>
31 #include <Security/SecItem.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/SecCFError.h>
34 #include <utilities/der_plist.h>
36 #if TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR)
37 #define USE_KEYSTORE 1
38 #elif TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
39 #define USE_KEYSTORE 1
40 #else /* no keystore on this platform */
41 #define USE_KEYSTORE 0
44 #include <libaks_acl_cf_keys.h>
46 static CFTypeRef kSecAccessControlKeyProtection
= CFSTR("prot");
48 // TODO: Use real name of this policy from SCred/AppleCredentialManager
49 CFTypeRef kSecAccessControlPolicyUserPresent
= CFSTR("DeviceOwnerAuthenticated");
51 struct __SecAccessControl
{
53 CFMutableDictionaryRef dict
;
56 static CFStringRef
SecAccessControlCopyDescription(CFTypeRef cf
) {
57 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
58 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecAccessControlRef: %p>"), access_control
);
61 static Boolean
SecAccessControlCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
62 SecAccessControlRef laccess_control
= (SecAccessControlRef
)lhs
;
63 SecAccessControlRef raccess_control
= (SecAccessControlRef
)rhs
;
64 return (laccess_control
== raccess_control
) || CFEqual(laccess_control
->dict
, raccess_control
->dict
);
67 static void SecAccessControlDestroy(CFTypeRef cf
) {
68 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
69 CFReleaseSafe(access_control
->dict
);
72 CFGiblisWithCompareFor(SecAccessControl
);
74 SecAccessControlRef
SecAccessControlCreate(CFAllocatorRef allocator
, CFErrorRef
*error
) {
75 SecAccessControlRef access_control
= CFTypeAllocate(SecAccessControl
, struct __SecAccessControl
, allocator
);
76 if (!access_control
) {
77 SecError(errSecAllocate
, error
, CFSTR("allocate memory for SecAccessControl"));
81 access_control
->dict
= CFDictionaryCreateMutableForCFTypes(allocator
);
82 return access_control
;
86 SecAccessControlRef
SecAccessControlCreateWithFlags(CFAllocatorRef allocator
, CFTypeRef protection
,
87 SecAccessControlCreateFlags flags
, CFErrorRef
*error
) {
88 SecAccessControlRef access_control
= NULL
;
89 CFTypeRef constraint
= NULL
;
91 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
93 if (!SecAccessControlSetProtection(access_control
, protection
, error
))
96 if (flags
& kSecAccessControlUserPresence
) {
97 require_quiet(constraint
= SecAccessConstraintCreatePolicy(kSecAccessControlPolicyUserPresent
, error
), errOut
);
98 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
,
99 constraint
, error
), errOut
);
100 CFReleaseNull(constraint
);
101 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
104 return access_control
;
107 CFReleaseSafe(access_control
);
108 CFReleaseSafe(constraint
);
112 CFTypeRef
SecAccessControlGetProtection(SecAccessControlRef access_control
) {
113 return CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyProtection
);
116 static bool checkItemInArray(CFTypeRef item
, const CFTypeRef
*values
, CFIndex count
, CFStringRef errMessage
, CFErrorRef
*error
) {
117 for (CFIndex i
= 0; i
< count
; i
++) {
118 if (CFEqualSafe(item
, values
[i
])) {
122 return SecError(errSecParam
, error
, errMessage
, item
);
125 #define CheckItemInArray(item, values, msg) \
127 const CFTypeRef vals[] = values; \
128 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \
133 #define ItemArray(...) { __VA_ARGS__ }
136 bool SecAccessControlSetProtection(SecAccessControlRef access_control
, CFTypeRef protection
, CFErrorRef
*error
) {
137 // Verify protection type.
138 CheckItemInArray(protection
, ItemArray(kSecAttrAccessibleAlways
, kSecAttrAccessibleAfterFirstUnlock
,
139 kSecAttrAccessibleWhenUnlocked
, kSecAttrAccessibleAlwaysThisDeviceOnly
,
140 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
,
141 kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,
142 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
),
143 "SecAccessControl: invalid protection %@");
145 // Protection valid, use it.
146 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyProtection
, protection
);
150 SecAccessConstraintRef
SecAccessConstraintCreatePolicy(CFTypeRef policy
, CFErrorRef
*error
) {
151 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kAKSKeyAclConstraintPolicy
, policy
, NULL
);
154 SecAccessConstraintRef
SecAccessConstraintCreatePasscode(bool systemPasscode
) {
155 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kAKSKeyAclConstraintUserPasscode
, kCFBooleanTrue
, NULL
);
158 SecAccessConstraintRef
SecAccessConstraintCreateTouchID(CFDataRef uuid
, CFErrorRef
*error
) {
159 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kAKSKeyAclConstraintBio
,
160 uuid
? uuid
: (const void *)kCFBooleanTrue
, NULL
);
163 SecAccessConstraintRef
SecAccessConstraintCreateKofN(size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
164 CFNumberRef k
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, numRequired
);
165 CFMutableDictionaryRef kofn
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kAKSKeyAclParamKofN
, k
, NULL
);
168 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
169 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
170 into k-of-n dictionary. */
171 const CFTypeRef keysToCopy
[] = { kAKSKeyAclConstraintBio
, kAKSKeyAclConstraintPolicy
,
172 kAKSKeyAclConstraintUserPasscode
};
173 SecAccessConstraintRef constraint
;
174 CFArrayForEachC(constraints
, constraint
) {
175 require_quiet(isDictionary(constraint
), errOut
);
177 for (CFIndex i
= 0; i
< (CFIndex
)(sizeof(keysToCopy
) / sizeof(keysToCopy
[0])); i
++) {
178 CFTypeRef value
= CFDictionaryGetValue(constraint
, keysToCopy
[i
]);
180 CFDictionarySetValue(kofn
, keysToCopy
[i
], value
);
185 require_quiet(found
, errOut
);
188 constraint
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kAKSKeyAclConstraintKofN
, kofn
, NULL
);
193 SecError(errSecParam
, error
, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
198 bool SecAccessConstraintSetOption(SecAccessConstraintRef constraint
, CFTypeRef option
, CFTypeRef value
, CFErrorRef
*error
) {
199 CheckItemInArray(option
, ItemArray(kAKSKeyAclConstraintAccessGroups
, kAKSKeyAclParamCredentialMaxAge
),
200 "SecAccessControl: invalid constraint option %@");
201 CFDictionarySetValue((CFMutableDictionaryRef
)constraint
, option
, value
);
205 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
, CFTypeRef constraint
, CFErrorRef
*error
) {
206 CheckItemInArray(operation
, ItemArray(kAKSKeyOpEncrypt
, kAKSKeyOpDecrypt
,
207 kAKSKeyOpSync
, kAKSKeyOpDefaultAcl
, kAKSKeyOpDelete
),
208 "SecAccessControl: invalid operation %@");
209 if (!isDictionary(constraint
) && !CFEqual(constraint
, kCFBooleanTrue
) && !CFEqual(constraint
, kCFBooleanFalse
) ) {
210 return SecError(errSecParam
, error
, CFSTR("invalid constraint"));
213 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
215 ops
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control
));
216 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, ops
);
218 CFDictionarySetValue(ops
, operation
, constraint
);
222 void SecAccessControlRemoveConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
) {
223 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
225 CFDictionaryRemoveValue(ops
, operation
);
228 SecAccessConstraintRef
SecAccessControlGetConstraint(SecAccessControlRef access_control
, CFTypeRef operation
) {
229 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
230 if (!ops
|| CFDictionaryGetCount(ops
) == 0)
231 // No ACL is present, this means that everything is allowed.
232 return kCFBooleanTrue
;
234 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
236 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
241 CFDictionaryRef
SecAccessControlGetConstraints(SecAccessControlRef access_control
) {
242 return CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
245 void SecAccessControlSetConstraints(SecAccessControlRef access_control
, CFDictionaryRef constraints
) {
246 CFMutableDictionaryRef mutableConstraints
= CFDictionaryCreateMutableCopy(NULL
, 0, constraints
);
247 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, mutableConstraints
);
248 CFReleaseSafe(mutableConstraints
);
251 void SecAccessControlSetAccessGroups(SecAccessControlRef access_control
, CFArrayRef access_groups
) {
252 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
254 ops
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control
));
255 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, ops
);
257 CFDictionarySetValue(ops
, kAKSKeyAccessGroups
, access_groups
);
260 CFArrayRef
SecAccessControlGetAccessGroups(SecAccessControlRef access_control
, CFTypeRef operation
) {
261 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
265 CFArrayRef access_groups
= NULL
;
266 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
268 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
270 if (constraint
&& isDictionary(constraint
)) {
271 access_groups
= CFDictionaryGetValue(constraint
, kAKSKeyAclConstraintAccessGroups
);
273 if (!access_groups
) {
274 access_groups
= CFDictionaryGetValue(ops
, kAKSKeyAccessGroups
);
276 return access_groups
;
279 CFDataRef
SecAccessControlCopyData(SecAccessControlRef access_control
) {
280 size_t len
= der_sizeof_plist(access_control
->dict
, NULL
);
281 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
282 CFDataSetLength(encoded
, len
);
283 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
284 const uint8_t *der
= der_end
;
286 der_end
= der_encode_plist(access_control
->dict
, NULL
, der
, der_end
);
288 CFReleaseNull(encoded
);
293 SecAccessControlRef
SecAccessControlCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
294 SecAccessControlRef access_control
;
295 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
297 CFPropertyListRef plist
;
298 const uint8_t *der
= CFDataGetBytePtr(data
);
299 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
300 require_quiet(der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &plist
, error
, der
, der_end
), errOut
);
301 if (der
!= der_end
) {
302 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
306 CFReleaseSafe(access_control
->dict
);
307 access_control
->dict
= (CFMutableDictionaryRef
)plist
;
308 return access_control
;
311 CFReleaseSafe(access_control
);