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 "SecAccessControl.h"
31 #include "SecAccessControlPriv.h"
33 #include "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
;
94 static CFDataRef
_getEmptyData() {
95 static CFMutableDataRef emptyData
= NULL
;
96 static dispatch_once_t onceToken
;
98 dispatch_once(&onceToken
, ^{
99 emptyData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
105 SecAccessControlRef
SecAccessControlCreateWithFlags(CFAllocatorRef allocator
, CFTypeRef protection
,
106 SecAccessControlCreateFlags flags
, CFErrorRef
*error
) {
107 SecAccessControlRef access_control
= NULL
;
108 CFTypeRef constraint
= NULL
;
109 CFMutableArrayRef constraints
= NULL
;
111 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
113 if (!SecAccessControlSetProtection(access_control
, protection
, error
))
117 bool or = (flags
& kSecAccessControlOr
) ? true : false;
118 bool and = (flags
& kSecAccessControlAnd
) ? true : false;
121 SecError(errSecParam
, error
, CFSTR("only one logical operation can be set"));
125 #pragma clang diagnostic push
126 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
128 SecAccessControlCreateFlags maskedFlags
= flags
& (kSecAccessControlBiometryAny
| kSecAccessControlBiometryCurrentSet
);
129 if (maskedFlags
&& maskedFlags
!= kSecAccessControlBiometryAny
&& maskedFlags
!= kSecAccessControlBiometryCurrentSet
) {
130 SecError(errSecParam
, error
, CFSTR("only one bio constraint can be set"));
134 if (flags
& kSecAccessControlUserPresence
&& flags
& ~(kSecAccessControlUserPresence
| kSecAccessControlApplicationPassword
| kSecAccessControlPrivateKeyUsage
)) {
135 SecError(errSecParam
, error
, CFSTR("kSecAccessControlUserPresence can be combined only with kSecAccessControlApplicationPassword and kSecAccessControlPrivateKeyUsage"));
139 constraints
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
141 if (flags
& kSecAccessControlUserPresence
) {
142 require_quiet(constraint
= SecAccessConstraintCreatePolicy(allocator
, CFSTR(kACMPolicyDeviceOwnerAuthentication
), error
), errOut
);
143 CFArrayAppendValue(constraints
, constraint
);
144 CFReleaseNull(constraint
);
147 if (flags
& kSecAccessControlDevicePasscode
) {
148 require_quiet(constraint
= SecAccessConstraintCreatePasscode(allocator
), errOut
);
149 CFArrayAppendValue(constraints
, constraint
);
150 CFReleaseNull(constraint
);
153 if (flags
& kSecAccessControlBiometryAny
) {
154 require_quiet(constraint
= SecAccessConstraintCreateBiometryAny(allocator
, _getEmptyData()), errOut
);
155 CFArrayAppendValue(constraints
, constraint
);
156 CFReleaseNull(constraint
);
159 if (flags
& kSecAccessControlBiometryCurrentSet
) {
160 require_quiet(constraint
= SecAccessConstraintCreateBiometryCurrentSet(allocator
, _getEmptyData(), _getEmptyData()), errOut
);
161 CFArrayAppendValue(constraints
, constraint
);
162 CFReleaseNull(constraint
);
165 #pragma clang diagnostic pop
167 if (flags
& kSecAccessControlApplicationPassword
) {
168 SecAccessControlSetRequirePassword(access_control
, true);
171 CFIndex constraints_count
= CFArrayGetCount(constraints
);
172 if (constraints_count
> 1) {
173 require_quiet(constraint
= SecAccessConstraintCreateValueOfKofN(allocator
, or?1:constraints_count
, constraints
, error
), errOut
);
174 if (flags
& kSecAccessControlPrivateKeyUsage
) {
175 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, constraint
, error
), errOut
);
176 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, constraint
, error
), errOut
);
177 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
180 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, constraint
, error
), errOut
);
181 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
183 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
184 CFReleaseNull(constraint
);
185 } else if (constraints_count
== 1) {
186 if (flags
& kSecAccessControlPrivateKeyUsage
) {
187 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
188 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
189 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
192 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
193 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
195 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
197 if (flags
& kSecAccessControlPrivateKeyUsage
) {
198 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, kCFBooleanTrue
, error
), errOut
);
199 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpComputeKey
, kCFBooleanTrue
, error
), errOut
);
200 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpAttest
, kCFBooleanTrue
, error
), errOut
);
201 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
204 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
208 CFReleaseNull(constraints
);
211 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
214 return access_control
;
217 CFReleaseSafe(access_control
);
218 CFReleaseSafe(constraints
);
219 CFReleaseSafe(constraint
);
223 CFTypeRef
SecAccessControlGetProtection(SecAccessControlRef access_control
) {
224 return CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyProtection
);
227 static bool checkItemInArray(CFTypeRef item
, const CFTypeRef
*values
, CFIndex count
, CFStringRef errMessage
, CFErrorRef
*error
) {
228 for (CFIndex i
= 0; i
< count
; i
++) {
229 if (CFEqualSafe(item
, values
[i
])) {
233 return SecError(errSecParam
, error
, CFSTR("%@: %@"), errMessage
, item
);
236 #define CheckItemInArray(item, values, msg) \
238 const CFTypeRef vals[] = values; \
239 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), msg, error)) { \
244 #define ItemArray(...) { __VA_ARGS__ }
247 bool SecAccessControlSetProtection(SecAccessControlRef access_control
, CFTypeRef protection
, CFErrorRef
*error
) {
248 if (!protection
|| CFGetTypeID(protection
) != CFDictionaryGetTypeID()) {
249 // Verify protection type.
250 CheckItemInArray(protection
, ItemArray(kSecAttrAccessibleAlwaysPrivate
, kSecAttrAccessibleAfterFirstUnlock
,
251 kSecAttrAccessibleWhenUnlocked
, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
,
252 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
,
253 kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,
254 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
),
255 CFSTR("SecAccessControl: invalid protection"));
258 // Protection valid, use it.
259 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyProtection
, protection
);
263 SecAccessConstraintRef
SecAccessConstraintCreatePolicy(CFAllocatorRef allocator
, CFTypeRef policy
, CFErrorRef
*error
) {
264 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintPolicy
), policy
, NULL
);
267 SecAccessConstraintRef
SecAccessConstraintCreatePasscode(CFAllocatorRef allocator
) {
268 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintUserPasscode
), kCFBooleanTrue
, NULL
);
271 SecAccessConstraintRef
SecAccessConstraintCreateBiometryAny(CFAllocatorRef allocator
, CFDataRef catacombUUID
) {
272 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
273 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
274 CFReleaseSafe(bioDict
);
278 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator
, CFDataRef catacombUUID
) {
279 return SecAccessConstraintCreateBiometryAny(allocator
, catacombUUID
);
282 SecAccessConstraintRef
SecAccessConstraintCreateBiometryCurrentSet(CFAllocatorRef allocator
, CFDataRef catacombUUID
, CFDataRef bioDbHash
) {
283 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
284 CFDictionarySetValue(bioDict
, CFSTR(kACMKeyAclParamBioDatabaseHash
), bioDbHash
);
285 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
286 CFReleaseSafe(bioDict
);
290 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator
, CFDataRef catacombUUID
, CFDataRef bioDbHash
) {
291 return SecAccessConstraintCreateBiometryCurrentSet(allocator
, catacombUUID
, bioDbHash
);
294 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
295 CFNumberRef k
= CFNumberCreateWithCFIndex(allocator
, numRequired
);
296 CFMutableDictionaryRef kofn
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamKofN
), k
, NULL
);
299 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
300 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
301 into k-of-n dictionary. */
302 const CFTypeRef keysToCopy
[] = { CFSTR(kACMKeyAclConstraintBio
), CFSTR(kACMKeyAclConstraintPolicy
),
303 CFSTR(kACMKeyAclConstraintUserPasscode
) };
304 SecAccessConstraintRef constraint
;
305 CFArrayForEachC(constraints
, constraint
) {
306 require_quiet(isDictionary(constraint
), errOut
);
308 for (CFIndex i
= 0; i
< (CFIndex
)(sizeof(keysToCopy
) / sizeof(keysToCopy
[0])); i
++) {
309 CFTypeRef value
= CFDictionaryGetValue(constraint
, keysToCopy
[i
]);
311 CFDictionarySetValue(kofn
, keysToCopy
[i
], value
);
316 require_quiet(found
, errOut
);
322 SecError(errSecParam
, error
, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
327 SecAccessConstraintRef
SecAccessConstraintCreateKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
328 SecAccessConstraintRef valueOfKofN
= SecAccessConstraintCreateValueOfKofN(allocator
, numRequired
, constraints
, error
);
329 require_quiet(valueOfKofN
, errOut
);
331 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintKofN
), valueOfKofN
, NULL
);
332 CFReleaseSafe(valueOfKofN
);
339 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
, CFTypeRef constraint
, CFErrorRef
*error
) {
340 CheckItemInArray(operation
, ItemArray(kAKSKeyOpEncrypt
, kAKSKeyOpDecrypt
,
341 kAKSKeyOpSign
, kAKSKeyOpAttest
, kAKSKeyOpComputeKey
,
342 kAKSKeyOpSync
, kAKSKeyOpDefaultAcl
, kAKSKeyOpDelete
),
343 CFSTR("SecAccessControl: invalid operation"));
344 if (!isDictionary(constraint
) && !CFEqual(constraint
, kCFBooleanTrue
) && !CFEqual(constraint
, kCFBooleanFalse
) ) {
345 return SecError(errSecParam
, error
, CFSTR("invalid constraint"));
348 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
349 CFDictionarySetValue(constraints
, operation
, constraint
);
353 SecAccessConstraintRef
SecAccessControlGetConstraint(SecAccessControlRef access_control
, CFTypeRef operation
) {
354 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
355 if (!ops
|| CFDictionaryGetCount(ops
) == 0)
356 // No ACL is present, this means that everything is allowed.
357 return kCFBooleanTrue
;
359 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
361 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
366 CFDataRef
SecAccessControlCopyConstraintData(SecAccessControlRef access_control
, CFTypeRef operation
) {
367 SecAccessConstraintRef constraint
= SecAccessControlGetConstraint(access_control
, operation
);
369 size_t len
= der_sizeof_plist(constraint
, NULL
);
370 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
371 CFDataSetLength(encoded
, len
);
372 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
373 const uint8_t *der
= der_end
;
375 der_end
= der_encode_plist(constraint
, NULL
, der
, der_end
);
377 CFReleaseNull(encoded
);
382 CFDictionaryRef
SecAccessControlGetConstraints(SecAccessControlRef access_control
) {
383 return CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
386 void SecAccessControlSetConstraints(SecAccessControlRef access_control
, CFDictionaryRef constraints
) {
387 CFMutableDictionaryRef mutableConstraints
= CFDictionaryCreateMutableCopy(CFGetAllocator(access_control
), 0, constraints
);
388 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, mutableConstraints
);
389 CFReleaseSafe(mutableConstraints
);
392 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control
, bool require
) {
393 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
394 CFDictionarySetValue(constraints
, kAKSKeyAclParamRequirePasscode
, require
?kCFBooleanTrue
:kCFBooleanFalse
);
397 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control
) {
398 CFMutableDictionaryRef acl
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
400 return CFEqualSafe(CFDictionaryGetValue(acl
, kAKSKeyAclParamRequirePasscode
), kCFBooleanTrue
);
406 void SecAccessControlSetBound(SecAccessControlRef access_control
, bool bound
) {
407 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyBound
, bound
? kCFBooleanTrue
: kCFBooleanFalse
);
410 bool SecAccessControlIsBound(SecAccessControlRef access_control
) {
411 CFTypeRef bound
= CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyBound
);
412 return bound
!= NULL
&& CFEqualSafe(bound
, kCFBooleanTrue
);
415 CFDataRef
SecAccessControlCopyData(SecAccessControlRef access_control
) {
416 size_t len
= der_sizeof_plist(access_control
->dict
, NULL
);
417 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
418 CFDataSetLength(encoded
, len
);
419 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
420 const uint8_t *der
= der_end
;
422 der_end
= der_encode_plist(access_control
->dict
, NULL
, der
, der_end
);
424 CFReleaseNull(encoded
);
429 SecAccessControlRef
SecAccessControlCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
430 SecAccessControlRef access_control
;
431 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
433 CFPropertyListRef plist
;
434 const uint8_t *der
= CFDataGetBytePtr(data
);
435 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
436 require_quiet(der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &plist
, error
, der
, der_end
), errOut
);
437 if (der
!= der_end
) {
438 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
442 CFReleaseSafe(access_control
->dict
);
443 access_control
->dict
= (CFMutableDictionaryRef
)plist
;
444 return access_control
;
447 CFReleaseSafe(access_control
);