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
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
181 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, constraint
, error
), errOut
);
182 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
184 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
185 CFReleaseNull(constraint
);
188 if (constraints_count
== 1) {
189 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
190 if (flags
& kSecAccessControlPrivateKeyUsage
) {
191 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
192 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
196 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
197 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
198 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
201 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
203 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
204 if (flags
& kSecAccessControlPrivateKeyUsage
) {
205 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, kCFBooleanTrue
, error
), errOut
);
206 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
207 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
211 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
212 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
217 CFReleaseNull(constraints
);
220 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
223 return access_control
;
226 CFReleaseSafe(access_control
);
227 CFReleaseSafe(constraints
);
228 CFReleaseSafe(constraint
);
232 CFTypeRef
SecAccessControlGetProtection(SecAccessControlRef access_control
) {
233 return CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyProtection
);
236 static bool checkItemInArray(CFTypeRef item
, const CFTypeRef
*values
, CFIndex count
, CFStringRef errMessage
, CFErrorRef
*error
) {
237 for (CFIndex i
= 0; i
< count
; i
++) {
238 if (CFEqualSafe(item
, values
[i
])) {
242 return SecError(errSecParam
, error
, errMessage
, item
);
245 #define CheckItemInArray(item, values, msg) \
247 const CFTypeRef vals[] = values; \
248 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \
253 #define ItemArray(...) { __VA_ARGS__ }
256 bool SecAccessControlSetProtection(SecAccessControlRef access_control
, CFTypeRef protection
, CFErrorRef
*error
) {
257 if (!protection
|| CFGetTypeID(protection
) != CFDictionaryGetTypeID()) {
258 // Verify protection type.
259 CheckItemInArray(protection
, ItemArray(kSecAttrAccessibleAlwaysPrivate
, kSecAttrAccessibleAfterFirstUnlock
,
260 kSecAttrAccessibleWhenUnlocked
, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
,
261 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
,
262 kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,
263 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
),
264 "SecAccessControl: invalid protection %@");
267 // Protection valid, use it.
268 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyProtection
, protection
);
272 SecAccessConstraintRef
SecAccessConstraintCreatePolicy(CFAllocatorRef allocator
, CFTypeRef policy
, CFErrorRef
*error
) {
273 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintPolicy
), policy
, NULL
);
276 SecAccessConstraintRef
SecAccessConstraintCreatePasscode(CFAllocatorRef allocator
) {
277 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintUserPasscode
), kCFBooleanTrue
, NULL
);
280 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator
, CFDataRef catacombUUID
) {
281 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
282 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
283 CFReleaseSafe(bioDict
);
287 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator
, CFDataRef catacombUUID
, CFDataRef bioDbHash
) {
288 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
289 CFDictionarySetValue(bioDict
, CFSTR(kACMKeyAclParamBioDatabaseHash
), bioDbHash
);
290 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
291 CFReleaseSafe(bioDict
);
295 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
296 CFNumberRef k
= CFNumberCreateWithCFIndex(allocator
, numRequired
);
297 CFMutableDictionaryRef kofn
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamKofN
), k
, NULL
);
300 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
301 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
302 into k-of-n dictionary. */
303 const CFTypeRef keysToCopy
[] = { CFSTR(kACMKeyAclConstraintBio
), CFSTR(kACMKeyAclConstraintPolicy
),
304 CFSTR(kACMKeyAclConstraintUserPasscode
) };
305 SecAccessConstraintRef constraint
;
306 CFArrayForEachC(constraints
, constraint
) {
307 require_quiet(isDictionary(constraint
), errOut
);
309 for (CFIndex i
= 0; i
< (CFIndex
)(sizeof(keysToCopy
) / sizeof(keysToCopy
[0])); i
++) {
310 CFTypeRef value
= CFDictionaryGetValue(constraint
, keysToCopy
[i
]);
312 CFDictionarySetValue(kofn
, keysToCopy
[i
], value
);
317 require_quiet(found
, errOut
);
323 SecError(errSecParam
, error
, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
328 SecAccessConstraintRef
SecAccessConstraintCreateKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
329 SecAccessConstraintRef valueOfKofN
= SecAccessConstraintCreateValueOfKofN(allocator
, numRequired
, constraints
, error
);
330 require_quiet(valueOfKofN
, errOut
);
332 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintKofN
), valueOfKofN
, NULL
);
333 CFReleaseSafe(valueOfKofN
);
340 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
, CFTypeRef constraint
, CFErrorRef
*error
) {
341 CheckItemInArray(operation
, ItemArray(kAKSKeyOpEncrypt
, kAKSKeyOpDecrypt
,
342 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
343 kAKSKeyOpSign
, kAKSKeyOpAttest
,
345 kAKSKeyOpSync
, kAKSKeyOpDefaultAcl
, kAKSKeyOpDelete
),
346 "SecAccessControl: invalid operation %@");
347 if (!isDictionary(constraint
) && !CFEqual(constraint
, kCFBooleanTrue
) && !CFEqual(constraint
, kCFBooleanFalse
) ) {
348 return SecError(errSecParam
, error
, CFSTR("invalid constraint"));
351 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
352 CFDictionarySetValue(constraints
, operation
, constraint
);
356 SecAccessConstraintRef
SecAccessControlGetConstraint(SecAccessControlRef access_control
, CFTypeRef operation
) {
357 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
358 if (!ops
|| CFDictionaryGetCount(ops
) == 0)
359 // No ACL is present, this means that everything is allowed.
360 return kCFBooleanTrue
;
362 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
364 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
369 CFDataRef
SecAccessControlCopyConstraintData(SecAccessControlRef access_control
, CFTypeRef operation
) {
370 SecAccessConstraintRef constraint
= SecAccessControlGetConstraint(access_control
, operation
);
372 size_t len
= der_sizeof_plist(constraint
, NULL
);
373 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
374 CFDataSetLength(encoded
, len
);
375 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
376 const uint8_t *der
= der_end
;
378 der_end
= der_encode_plist(constraint
, NULL
, der
, der_end
);
380 CFReleaseNull(encoded
);
385 CFDictionaryRef
SecAccessControlGetConstraints(SecAccessControlRef access_control
) {
386 return CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
389 void SecAccessControlSetConstraints(SecAccessControlRef access_control
, CFDictionaryRef constraints
) {
390 CFMutableDictionaryRef mutableConstraints
= CFDictionaryCreateMutableCopy(CFGetAllocator(access_control
), 0, constraints
);
391 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, mutableConstraints
);
392 CFReleaseSafe(mutableConstraints
);
395 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control
, bool require
) {
396 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
397 CFDictionarySetValue(constraints
, kAKSKeyAclParamRequirePasscode
, require
?kCFBooleanTrue
:kCFBooleanFalse
);
400 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control
) {
401 CFMutableDictionaryRef acl
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
403 return CFEqualSafe(CFDictionaryGetValue(acl
, kAKSKeyAclParamRequirePasscode
), kCFBooleanTrue
);
409 void SecAccessControlSetBound(SecAccessControlRef access_control
, bool bound
) {
410 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyBound
, bound
? kCFBooleanTrue
: kCFBooleanFalse
);
413 bool SecAccessControlIsBound(SecAccessControlRef access_control
) {
414 CFTypeRef bound
= CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyBound
);
415 return bound
!= NULL
&& CFEqualSafe(bound
, kCFBooleanTrue
);
418 CFDataRef
SecAccessControlCopyData(SecAccessControlRef access_control
) {
419 size_t len
= der_sizeof_plist(access_control
->dict
, NULL
);
420 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
421 CFDataSetLength(encoded
, len
);
422 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
423 const uint8_t *der
= der_end
;
425 der_end
= der_encode_plist(access_control
->dict
, NULL
, der
, der_end
);
427 CFReleaseNull(encoded
);
432 SecAccessControlRef
SecAccessControlCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
433 SecAccessControlRef access_control
;
434 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
436 CFPropertyListRef plist
;
437 const uint8_t *der
= CFDataGetBytePtr(data
);
438 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
439 require_quiet(der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &plist
, error
, der
, der_end
), errOut
);
440 if (der
!= der_end
) {
441 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
445 CFReleaseSafe(access_control
->dict
);
446 access_control
->dict
= (CFMutableDictionaryRef
)plist
;
447 return access_control
;
450 CFReleaseSafe(access_control
);