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 <TargetConditionals.h>
29 #include <AssertMacros.h>
30 #include <Security/SecAccessControl.h>
31 #include <Security/SecAccessControlPriv.h>
32 #include <Security/SecItem.h>
33 #include <Security/SecItemPriv.h>
34 #include <utilities/SecCFWrappers.h>
35 #include <utilities/SecCFError.h>
36 #include <utilities/der_plist.h>
37 #include <libaks_acl_cf_keys.h>
39 #include <ACMAclDefs.h>
41 static CFTypeRef kSecAccessControlKeyProtection
= CFSTR("prot");
42 static CFTypeRef kSecAccessControlKeyBound
= CFSTR("bound");
44 struct __SecAccessControl
{
46 CFMutableDictionaryRef dict
;
49 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
);
51 static CFStringRef
SecAccessControlCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
52 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
53 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecAccessControlRef: %p>"), access_control
);
56 static Boolean
SecAccessControlCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
57 SecAccessControlRef laccess_control
= (SecAccessControlRef
)lhs
;
58 SecAccessControlRef raccess_control
= (SecAccessControlRef
)rhs
;
59 return (laccess_control
== raccess_control
) || CFEqual(laccess_control
->dict
, raccess_control
->dict
);
62 static void SecAccessControlDestroy(CFTypeRef cf
) {
63 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
64 CFReleaseSafe(access_control
->dict
);
67 CFGiblisWithCompareFor(SecAccessControl
);
69 static CFMutableDictionaryRef
SecAccessControlGetMutableConstraints(SecAccessControlRef access_control
) {
70 CFMutableDictionaryRef constraints
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
73 CFMutableDictionaryRef newConstraints
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control
));
74 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, newConstraints
);
75 CFRelease(newConstraints
);
77 constraints
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
83 SecAccessControlRef
SecAccessControlCreate(CFAllocatorRef allocator
, CFErrorRef
*error
) {
84 SecAccessControlRef access_control
= CFTypeAllocate(SecAccessControl
, struct __SecAccessControl
, allocator
);
85 if (!access_control
) {
86 SecError(errSecAllocate
, error
, CFSTR("allocate memory for SecAccessControl"));
90 access_control
->dict
= CFDictionaryCreateMutableForCFTypes(allocator
);
91 return access_control
;
93 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
94 static CFDataRef
_getEmptyData() {
95 static CFMutableDataRef emptyData
= NULL
;
96 static dispatch_once_t onceToken
;
98 dispatch_once(&onceToken
, ^{
99 emptyData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
106 SecAccessControlRef
SecAccessControlCreateWithFlags(CFAllocatorRef allocator
, CFTypeRef protection
,
107 SecAccessControlCreateFlags flags
, CFErrorRef
*error
) {
108 SecAccessControlRef access_control
= NULL
;
109 CFTypeRef constraint
= NULL
;
110 CFMutableArrayRef constraints
= NULL
;
112 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
114 if (!SecAccessControlSetProtection(access_control
, protection
, error
))
118 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
119 bool or = (flags
& kSecAccessControlOr
) ? true : false;
120 bool and = (flags
& kSecAccessControlAnd
) ? true : false;
123 SecError(errSecParam
, error
, CFSTR("only one logical operation can be set"));
127 SecAccessControlCreateFlags maskedFlags
= flags
& (kSecAccessControlTouchIDAny
| kSecAccessControlTouchIDCurrentSet
);
128 if (maskedFlags
&& maskedFlags
!= kSecAccessControlTouchIDAny
&& maskedFlags
!= kSecAccessControlTouchIDCurrentSet
) {
129 SecError(errSecParam
, error
, CFSTR("only one bio constraint can be set"));
133 if (flags
& kSecAccessControlUserPresence
&& flags
& ~(kSecAccessControlUserPresence
| kSecAccessControlApplicationPassword
| kSecAccessControlPrivateKeyUsage
)) {
135 if (flags
& kSecAccessControlUserPresence
&& flags
!= kSecAccessControlUserPresence
) {
137 SecError(errSecParam
, error
, CFSTR("kSecAccessControlUserPresence can be combined only with kSecAccessControlApplicationPassword and kSecAccessControlPrivateKeyUsage"));
141 constraints
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
143 if (flags
& kSecAccessControlUserPresence
) {
144 require_quiet(constraint
= SecAccessConstraintCreatePolicy(allocator
, CFSTR(kACMPolicyDeviceOwnerAuthentication
), error
), errOut
);
145 CFArrayAppendValue(constraints
, constraint
);
146 CFReleaseNull(constraint
);
149 if (flags
& kSecAccessControlDevicePasscode
) {
150 require_quiet(constraint
= SecAccessConstraintCreatePasscode(allocator
), errOut
);
151 CFArrayAppendValue(constraints
, constraint
);
152 CFReleaseNull(constraint
);
155 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
156 if (flags
& kSecAccessControlTouchIDAny
) {
157 require_quiet(constraint
= SecAccessConstraintCreateTouchIDAny(allocator
, _getEmptyData()), errOut
);
158 CFArrayAppendValue(constraints
, constraint
);
159 CFReleaseNull(constraint
);
162 if (flags
& kSecAccessControlTouchIDCurrentSet
) {
163 require_quiet(constraint
= SecAccessConstraintCreateTouchIDCurrentSet(allocator
, _getEmptyData(), _getEmptyData()), errOut
);
164 CFArrayAppendValue(constraints
, constraint
);
165 CFReleaseNull(constraint
);
168 if (flags
& kSecAccessControlApplicationPassword
) {
169 SecAccessControlSetRequirePassword(access_control
, true);
172 CFIndex constraints_count
= CFArrayGetCount(constraints
);
173 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
174 if (constraints_count
> 1) {
175 require_quiet(constraint
= SecAccessConstraintCreateValueOfKofN(allocator
, or?1:constraints_count
, constraints
, error
), errOut
);
176 if (flags
& kSecAccessControlPrivateKeyUsage
) {
177 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, constraint
, error
), errOut
);
178 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, constraint
, error
), errOut
);
179 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
182 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, constraint
, error
), errOut
);
183 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
185 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
186 CFReleaseNull(constraint
);
189 if (constraints_count
== 1) {
190 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
191 if (flags
& kSecAccessControlPrivateKeyUsage
) {
192 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
193 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
194 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
198 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
199 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
200 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
203 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
205 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
206 if (flags
& kSecAccessControlPrivateKeyUsage
) {
207 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, kCFBooleanTrue
, error
), errOut
);
208 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, kCFBooleanTrue
, error
), errOut
);
209 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
210 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
214 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
215 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
220 CFReleaseNull(constraints
);
223 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
226 return access_control
;
229 CFReleaseSafe(access_control
);
230 CFReleaseSafe(constraints
);
231 CFReleaseSafe(constraint
);
235 CFTypeRef
SecAccessControlGetProtection(SecAccessControlRef access_control
) {
236 return CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyProtection
);
239 static bool checkItemInArray(CFTypeRef item
, const CFTypeRef
*values
, CFIndex count
, CFStringRef errMessage
, CFErrorRef
*error
) {
240 for (CFIndex i
= 0; i
< count
; i
++) {
241 if (CFEqualSafe(item
, values
[i
])) {
245 return SecError(errSecParam
, error
, CFSTR("%@: %@"), errMessage
, item
);
248 #define CheckItemInArray(item, values, msg) \
250 const CFTypeRef vals[] = values; \
251 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), msg, error)) { \
256 #define ItemArray(...) { __VA_ARGS__ }
259 bool SecAccessControlSetProtection(SecAccessControlRef access_control
, CFTypeRef protection
, CFErrorRef
*error
) {
260 if (!protection
|| CFGetTypeID(protection
) != CFDictionaryGetTypeID()) {
261 // Verify protection type.
262 CheckItemInArray(protection
, ItemArray(kSecAttrAccessibleAlwaysPrivate
, kSecAttrAccessibleAfterFirstUnlock
,
263 kSecAttrAccessibleWhenUnlocked
, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
,
264 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
,
265 kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,
266 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
),
267 CFSTR("SecAccessControl: invalid protection"));
270 // Protection valid, use it.
271 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyProtection
, protection
);
275 SecAccessConstraintRef
SecAccessConstraintCreatePolicy(CFAllocatorRef allocator
, CFTypeRef policy
, CFErrorRef
*error
) {
276 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintPolicy
), policy
, NULL
);
279 SecAccessConstraintRef
SecAccessConstraintCreatePasscode(CFAllocatorRef allocator
) {
280 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintUserPasscode
), kCFBooleanTrue
, NULL
);
283 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator
, CFDataRef catacombUUID
) {
284 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
285 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
286 CFReleaseSafe(bioDict
);
290 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator
, CFDataRef catacombUUID
, CFDataRef bioDbHash
) {
291 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
292 CFDictionarySetValue(bioDict
, CFSTR(kACMKeyAclParamBioDatabaseHash
), bioDbHash
);
293 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
294 CFReleaseSafe(bioDict
);
298 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
299 CFNumberRef k
= CFNumberCreateWithCFIndex(allocator
, numRequired
);
300 CFMutableDictionaryRef kofn
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamKofN
), k
, NULL
);
303 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
304 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
305 into k-of-n dictionary. */
306 const CFTypeRef keysToCopy
[] = { CFSTR(kACMKeyAclConstraintBio
), CFSTR(kACMKeyAclConstraintPolicy
),
307 CFSTR(kACMKeyAclConstraintUserPasscode
) };
308 SecAccessConstraintRef constraint
;
309 CFArrayForEachC(constraints
, constraint
) {
310 require_quiet(isDictionary(constraint
), errOut
);
312 for (CFIndex i
= 0; i
< (CFIndex
)(sizeof(keysToCopy
) / sizeof(keysToCopy
[0])); i
++) {
313 CFTypeRef value
= CFDictionaryGetValue(constraint
, keysToCopy
[i
]);
315 CFDictionarySetValue(kofn
, keysToCopy
[i
], value
);
320 require_quiet(found
, errOut
);
326 SecError(errSecParam
, error
, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
331 SecAccessConstraintRef
SecAccessConstraintCreateKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
332 SecAccessConstraintRef valueOfKofN
= SecAccessConstraintCreateValueOfKofN(allocator
, numRequired
, constraints
, error
);
333 require_quiet(valueOfKofN
, errOut
);
335 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintKofN
), valueOfKofN
, NULL
);
336 CFReleaseSafe(valueOfKofN
);
343 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
, CFTypeRef constraint
, CFErrorRef
*error
) {
344 CheckItemInArray(operation
, ItemArray(kAKSKeyOpEncrypt
, kAKSKeyOpDecrypt
,
345 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
346 kAKSKeyOpSign
, kAKSKeyOpAttest
, kAKSKeyOpComputeKey
,
348 kAKSKeyOpSync
, kAKSKeyOpDefaultAcl
, kAKSKeyOpDelete
),
349 CFSTR("SecAccessControl: invalid operation"));
350 if (!isDictionary(constraint
) && !CFEqual(constraint
, kCFBooleanTrue
) && !CFEqual(constraint
, kCFBooleanFalse
) ) {
351 return SecError(errSecParam
, error
, CFSTR("invalid constraint"));
354 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
355 CFDictionarySetValue(constraints
, operation
, constraint
);
359 SecAccessConstraintRef
SecAccessControlGetConstraint(SecAccessControlRef access_control
, CFTypeRef operation
) {
360 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
361 if (!ops
|| CFDictionaryGetCount(ops
) == 0)
362 // No ACL is present, this means that everything is allowed.
363 return kCFBooleanTrue
;
365 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
367 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
372 CFDataRef
SecAccessControlCopyConstraintData(SecAccessControlRef access_control
, CFTypeRef operation
) {
373 SecAccessConstraintRef constraint
= SecAccessControlGetConstraint(access_control
, operation
);
375 size_t len
= der_sizeof_plist(constraint
, NULL
);
376 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
377 CFDataSetLength(encoded
, len
);
378 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
379 const uint8_t *der
= der_end
;
381 der_end
= der_encode_plist(constraint
, NULL
, der
, der_end
);
383 CFReleaseNull(encoded
);
388 CFDictionaryRef
SecAccessControlGetConstraints(SecAccessControlRef access_control
) {
389 return CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
392 void SecAccessControlSetConstraints(SecAccessControlRef access_control
, CFDictionaryRef constraints
) {
393 CFMutableDictionaryRef mutableConstraints
= CFDictionaryCreateMutableCopy(CFGetAllocator(access_control
), 0, constraints
);
394 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, mutableConstraints
);
395 CFReleaseSafe(mutableConstraints
);
398 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control
, bool require
) {
399 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
400 CFDictionarySetValue(constraints
, kAKSKeyAclParamRequirePasscode
, require
?kCFBooleanTrue
:kCFBooleanFalse
);
403 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control
) {
404 CFMutableDictionaryRef acl
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
406 return CFEqualSafe(CFDictionaryGetValue(acl
, kAKSKeyAclParamRequirePasscode
), kCFBooleanTrue
);
412 void SecAccessControlSetBound(SecAccessControlRef access_control
, bool bound
) {
413 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyBound
, bound
? kCFBooleanTrue
: kCFBooleanFalse
);
416 bool SecAccessControlIsBound(SecAccessControlRef access_control
) {
417 CFTypeRef bound
= CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyBound
);
418 return bound
!= NULL
&& CFEqualSafe(bound
, kCFBooleanTrue
);
421 CFDataRef
SecAccessControlCopyData(SecAccessControlRef access_control
) {
422 size_t len
= der_sizeof_plist(access_control
->dict
, NULL
);
423 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
424 CFDataSetLength(encoded
, len
);
425 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
426 const uint8_t *der
= der_end
;
428 der_end
= der_encode_plist(access_control
->dict
, NULL
, der
, der_end
);
430 CFReleaseNull(encoded
);
435 SecAccessControlRef
SecAccessControlCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
436 SecAccessControlRef access_control
;
437 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
439 CFPropertyListRef plist
;
440 const uint8_t *der
= CFDataGetBytePtr(data
);
441 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
442 require_quiet(der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &plist
, error
, der
, der_end
), errOut
);
443 if (der
!= der_end
) {
444 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
448 CFReleaseSafe(access_control
->dict
);
449 access_control
->dict
= (CFMutableDictionaryRef
)plist
;
450 return access_control
;
453 CFReleaseSafe(access_control
);