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 <utilities/SecCFWrappers.h>
34 #include <utilities/SecCFError.h>
35 #include <utilities/der_plist.h>
36 #include <libaks_acl_cf_keys.h>
38 #include <ACMAclDefs.h>
40 static CFTypeRef kSecAccessControlKeyProtection
= CFSTR("prot");
41 static CFTypeRef kSecAccessControlKeyBound
= CFSTR("bound");
43 struct __SecAccessControl
{
45 CFMutableDictionaryRef dict
;
48 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
);
50 static CFStringRef
SecAccessControlCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
51 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
52 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecAccessControlRef: %p>"), access_control
);
55 static Boolean
SecAccessControlCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
56 SecAccessControlRef laccess_control
= (SecAccessControlRef
)lhs
;
57 SecAccessControlRef raccess_control
= (SecAccessControlRef
)rhs
;
58 return (laccess_control
== raccess_control
) || CFEqual(laccess_control
->dict
, raccess_control
->dict
);
61 static void SecAccessControlDestroy(CFTypeRef cf
) {
62 SecAccessControlRef access_control
= (SecAccessControlRef
)cf
;
63 CFReleaseSafe(access_control
->dict
);
66 CFGiblisWithCompareFor(SecAccessControl
);
68 static CFMutableDictionaryRef
SecAccessControlGetMutableConstraints(SecAccessControlRef access_control
) {
69 CFMutableDictionaryRef constraints
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
72 CFMutableDictionaryRef newConstraints
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control
));
73 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, newConstraints
);
74 CFRelease(newConstraints
);
76 constraints
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
82 SecAccessControlRef
SecAccessControlCreate(CFAllocatorRef allocator
, CFErrorRef
*error
) {
83 SecAccessControlRef access_control
= CFTypeAllocate(SecAccessControl
, struct __SecAccessControl
, allocator
);
84 if (!access_control
) {
85 SecError(errSecAllocate
, error
, CFSTR("allocate memory for SecAccessControl"));
89 access_control
->dict
= CFDictionaryCreateMutableForCFTypes(allocator
);
90 return access_control
;
93 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
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_EMBEDDED || TARGET_OS_IPHONE
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_EMBEDDED || TARGET_OS_IPHONE
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_EMBEDDED || TARGET_OS_IPHONE
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
);
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
);
187 if (constraints_count
== 1) {
188 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
189 if (flags
& kSecAccessControlPrivateKeyUsage
) {
190 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
194 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDecrypt
, CFArrayGetValueAtIndex(constraints
, 0), error
), errOut
);
195 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, error
), errOut
);
196 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
199 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
201 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
202 if (flags
& kSecAccessControlPrivateKeyUsage
) {
203 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpSign
, kCFBooleanTrue
, error
), errOut
);
204 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDelete
, kCFBooleanTrue
, error
), errOut
);
208 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
209 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
214 CFReleaseNull(constraints
);
217 require_quiet(SecAccessControlAddConstraintForOperation(access_control
, kAKSKeyOpDefaultAcl
, kCFBooleanTrue
, error
), errOut
);
220 return access_control
;
223 CFReleaseSafe(access_control
);
224 CFReleaseSafe(constraints
);
225 CFReleaseSafe(constraint
);
229 CFTypeRef
SecAccessControlGetProtection(SecAccessControlRef access_control
) {
230 return CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyProtection
);
233 static bool checkItemInArray(CFTypeRef item
, const CFTypeRef
*values
, CFIndex count
, CFStringRef errMessage
, CFErrorRef
*error
) {
234 for (CFIndex i
= 0; i
< count
; i
++) {
235 if (CFEqualSafe(item
, values
[i
])) {
239 return SecError(errSecParam
, error
, errMessage
, item
);
242 #define CheckItemInArray(item, values, msg) \
244 const CFTypeRef vals[] = values; \
245 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \
250 #define ItemArray(...) { __VA_ARGS__ }
253 bool SecAccessControlSetProtection(SecAccessControlRef access_control
, CFTypeRef protection
, CFErrorRef
*error
) {
254 // Verify protection type.
255 CheckItemInArray(protection
, ItemArray(kSecAttrAccessibleAlways
, kSecAttrAccessibleAfterFirstUnlock
,
256 kSecAttrAccessibleWhenUnlocked
, kSecAttrAccessibleAlwaysThisDeviceOnly
,
257 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
,
258 kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,
259 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
),
260 "SecAccessControl: invalid protection %@");
262 // Protection valid, use it.
263 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyProtection
, protection
);
267 SecAccessConstraintRef
SecAccessConstraintCreatePolicy(CFAllocatorRef allocator
, CFTypeRef policy
, CFErrorRef
*error
) {
268 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintPolicy
), policy
, NULL
);
271 SecAccessConstraintRef
SecAccessConstraintCreatePasscode(CFAllocatorRef allocator
) {
272 return CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintUserPasscode
), kCFBooleanTrue
, NULL
);
275 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator
, CFDataRef catacombUUID
) {
276 CFMutableDictionaryRef bioDict
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamBioCatacombUUID
), catacombUUID
, NULL
);
277 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintBio
), bioDict
, NULL
);
278 CFReleaseSafe(bioDict
);
282 SecAccessConstraintRef
SecAccessConstraintCreateTouchIDCurrentSet(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 static SecAccessConstraintRef
SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
291 CFNumberRef k
= CFNumberCreateWithCFIndex(allocator
, numRequired
);
292 CFMutableDictionaryRef kofn
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclParamKofN
), k
, NULL
);
295 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
296 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
297 into k-of-n dictionary. */
298 const CFTypeRef keysToCopy
[] = { CFSTR(kACMKeyAclConstraintBio
), CFSTR(kACMKeyAclConstraintPolicy
),
299 CFSTR(kACMKeyAclConstraintUserPasscode
) };
300 SecAccessConstraintRef constraint
;
301 CFArrayForEachC(constraints
, constraint
) {
302 require_quiet(isDictionary(constraint
), errOut
);
304 for (CFIndex i
= 0; i
< (CFIndex
)(sizeof(keysToCopy
) / sizeof(keysToCopy
[0])); i
++) {
305 CFTypeRef value
= CFDictionaryGetValue(constraint
, keysToCopy
[i
]);
307 CFDictionarySetValue(kofn
, keysToCopy
[i
], value
);
312 require_quiet(found
, errOut
);
318 SecError(errSecParam
, error
, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
323 SecAccessConstraintRef
SecAccessConstraintCreateKofN(CFAllocatorRef allocator
, size_t numRequired
, CFArrayRef constraints
, CFErrorRef
*error
) {
324 SecAccessConstraintRef valueOfKofN
= SecAccessConstraintCreateValueOfKofN(allocator
, numRequired
, constraints
, error
);
325 require_quiet(valueOfKofN
, errOut
);
327 SecAccessConstraintRef constraint
= CFDictionaryCreateMutableForCFTypesWith(allocator
, CFSTR(kACMKeyAclConstraintKofN
), valueOfKofN
, NULL
);
328 CFReleaseSafe(valueOfKofN
);
335 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control
, CFTypeRef operation
, CFTypeRef constraint
, CFErrorRef
*error
) {
336 CheckItemInArray(operation
, ItemArray(kAKSKeyOpEncrypt
, kAKSKeyOpDecrypt
,
337 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
340 kAKSKeyOpSync
, kAKSKeyOpDefaultAcl
, kAKSKeyOpDelete
),
341 "SecAccessControl: invalid operation %@");
342 if (!isDictionary(constraint
) && !CFEqual(constraint
, kCFBooleanTrue
) && !CFEqual(constraint
, kCFBooleanFalse
) ) {
343 return SecError(errSecParam
, error
, CFSTR("invalid constraint"));
346 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
347 CFDictionarySetValue(constraints
, operation
, constraint
);
351 SecAccessConstraintRef
SecAccessControlGetConstraint(SecAccessControlRef access_control
, CFTypeRef operation
) {
352 CFMutableDictionaryRef ops
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
353 if (!ops
|| CFDictionaryGetCount(ops
) == 0)
354 // No ACL is present, this means that everything is allowed.
355 return kCFBooleanTrue
;
357 SecAccessConstraintRef constraint
= CFDictionaryGetValue(ops
, operation
);
359 constraint
= CFDictionaryGetValue(ops
, kAKSKeyOpDefaultAcl
);
364 CFDataRef
SecAccessControlCopyConstraintData(SecAccessControlRef access_control
, CFTypeRef operation
) {
365 SecAccessConstraintRef constraint
= SecAccessControlGetConstraint(access_control
, operation
);
367 size_t len
= der_sizeof_plist(constraint
, NULL
);
368 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
369 CFDataSetLength(encoded
, len
);
370 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
371 const uint8_t *der
= der_end
;
373 der_end
= der_encode_plist(constraint
, NULL
, der
, der_end
);
375 CFReleaseNull(encoded
);
380 CFDictionaryRef
SecAccessControlGetConstraints(SecAccessControlRef access_control
) {
381 return CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
384 void SecAccessControlSetConstraints(SecAccessControlRef access_control
, CFDictionaryRef constraints
) {
385 CFMutableDictionaryRef mutableConstraints
= CFDictionaryCreateMutableCopy(CFGetAllocator(access_control
), 0, constraints
);
386 CFDictionarySetValue(access_control
->dict
, kAKSKeyAcl
, mutableConstraints
);
387 CFReleaseSafe(mutableConstraints
);
390 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control
, bool require
) {
391 CFMutableDictionaryRef constraints
= SecAccessControlGetMutableConstraints(access_control
);
392 CFDictionarySetValue(constraints
, kAKSKeyAclParamRequirePasscode
, require
?kCFBooleanTrue
:kCFBooleanFalse
);
395 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control
) {
396 CFMutableDictionaryRef acl
= (CFMutableDictionaryRef
)CFDictionaryGetValue(access_control
->dict
, kAKSKeyAcl
);
398 return CFEqualSafe(CFDictionaryGetValue(acl
, kAKSKeyAclParamRequirePasscode
), kCFBooleanTrue
);
404 void SecAccessControlSetBound(SecAccessControlRef access_control
, bool bound
) {
405 CFDictionarySetValue(access_control
->dict
, kSecAccessControlKeyBound
, bound
? kCFBooleanTrue
: kCFBooleanFalse
);
408 bool SecAccessControlIsBound(SecAccessControlRef access_control
) {
409 CFTypeRef bound
= CFDictionaryGetValue(access_control
->dict
, kSecAccessControlKeyBound
);
410 return bound
!= NULL
&& CFEqualSafe(bound
, kCFBooleanTrue
);
413 CFDataRef
SecAccessControlCopyData(SecAccessControlRef access_control
) {
414 size_t len
= der_sizeof_plist(access_control
->dict
, NULL
);
415 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
416 CFDataSetLength(encoded
, len
);
417 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
418 const uint8_t *der
= der_end
;
420 der_end
= der_encode_plist(access_control
->dict
, NULL
, der
, der_end
);
422 CFReleaseNull(encoded
);
427 SecAccessControlRef
SecAccessControlCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
428 SecAccessControlRef access_control
;
429 require_quiet(access_control
= SecAccessControlCreate(allocator
, error
), errOut
);
431 CFPropertyListRef plist
;
432 const uint8_t *der
= CFDataGetBytePtr(data
);
433 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
434 require_quiet(der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &plist
, error
, der
, der_end
), errOut
);
435 if (der
!= der_end
) {
436 SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of SecAccessControl data"));
440 CFReleaseSafe(access_control
->dict
);
441 access_control
->dict
= (CFMutableDictionaryRef
)plist
;
442 return access_control
;
445 CFReleaseSafe(access_control
);