]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecAccessControl.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / Security / SecAccessControl.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecAccessControl.c - CoreFoundation based access control object
26 */
27
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>
38 #include <ACMDefs.h>
39 #include <ACMAclDefs.h>
40
41 static CFTypeRef kSecAccessControlKeyProtection = CFSTR("prot");
42 static CFTypeRef kSecAccessControlKeyBound = CFSTR("bound");
43
44 struct __SecAccessControl {
45 CFRuntimeBase _base;
46 CFMutableDictionaryRef dict;
47 };
48
49 static SecAccessConstraintRef SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator, size_t numRequired, CFArrayRef constraints, CFErrorRef *error);
50
51 static CFStringRef SecAccessControlCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
52 SecAccessControlRef access_control = (SecAccessControlRef)cf;
53 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecAccessControlRef: %p>"), access_control);
54 }
55
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);
60 }
61
62 static void SecAccessControlDestroy(CFTypeRef cf) {
63 SecAccessControlRef access_control = (SecAccessControlRef)cf;
64 CFReleaseSafe(access_control->dict);
65 }
66
67 CFGiblisWithCompareFor(SecAccessControl);
68
69 static CFMutableDictionaryRef SecAccessControlGetMutableConstraints(SecAccessControlRef access_control) {
70 CFMutableDictionaryRef constraints = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
71
72 if (!constraints) {
73 CFMutableDictionaryRef newConstraints = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control));
74 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, newConstraints);
75 CFRelease(newConstraints);
76
77 constraints = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
78 }
79
80 return constraints;
81 }
82
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"));
87 return NULL;
88 }
89
90 access_control->dict = CFDictionaryCreateMutableForCFTypes(allocator);
91 return access_control;
92 }
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;
97
98 dispatch_once(&onceToken, ^{
99 emptyData = CFDataCreateMutable(kCFAllocatorDefault, 0);
100 });
101
102 return emptyData;
103 }
104 #endif
105
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;
111
112 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
113
114 if (!SecAccessControlSetProtection(access_control, protection, error))
115 goto errOut;
116
117 if (flags) {
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;
121
122 if (or && and) {
123 SecError(errSecParam, error, CFSTR("only one logical operation can be set"));
124 goto errOut;
125 }
126
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"));
130 goto errOut;
131 }
132
133 if (flags & kSecAccessControlUserPresence && flags & ~(kSecAccessControlUserPresence | kSecAccessControlApplicationPassword | kSecAccessControlPrivateKeyUsage)) {
134 #else
135 if (flags & kSecAccessControlUserPresence && flags != kSecAccessControlUserPresence) {
136 #endif
137 SecError(errSecParam, error, CFSTR("kSecAccessControlUserPresence can be combined only with kSecAccessControlApplicationPassword and kSecAccessControlPrivateKeyUsage"));
138 goto errOut;
139 }
140
141 constraints = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
142
143 if (flags & kSecAccessControlUserPresence) {
144 require_quiet(constraint = SecAccessConstraintCreatePolicy(allocator, CFSTR(kACMPolicyDeviceOwnerAuthentication), error), errOut);
145 CFArrayAppendValue(constraints, constraint);
146 CFReleaseNull(constraint);
147 }
148
149 if (flags & kSecAccessControlDevicePasscode) {
150 require_quiet(constraint = SecAccessConstraintCreatePasscode(allocator), errOut);
151 CFArrayAppendValue(constraints, constraint);
152 CFReleaseNull(constraint);
153 }
154
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);
160 }
161
162 if (flags & kSecAccessControlTouchIDCurrentSet) {
163 require_quiet(constraint = SecAccessConstraintCreateTouchIDCurrentSet(allocator, _getEmptyData(), _getEmptyData()), errOut);
164 CFArrayAppendValue(constraints, constraint);
165 CFReleaseNull(constraint);
166 }
167
168 if (flags & kSecAccessControlApplicationPassword) {
169 SecAccessControlSetRequirePassword(access_control, true);
170 }
171 #endif
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);
179 }
180 else {
181 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, constraint, error), errOut);
182 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut);
183 }
184 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
185 CFReleaseNull(constraint);
186 } else
187 #endif
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);
193 }
194 else {
195 #endif
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)
199 }
200 #endif
201 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
202 } else {
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);
208 }
209 else {
210 #endif
211 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
212 #if TARGET_OS_IPHONE || (!RC_HIDE_J79 && !RC_HIDE_J80)
213 }
214 #endif
215 }
216
217 CFReleaseNull(constraints);
218 }
219 else {
220 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
221 }
222
223 return access_control;
224
225 errOut:
226 CFReleaseSafe(access_control);
227 CFReleaseSafe(constraints);
228 CFReleaseSafe(constraint);
229 return NULL;
230 }
231
232 CFTypeRef SecAccessControlGetProtection(SecAccessControlRef access_control) {
233 return CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyProtection);
234 }
235
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])) {
239 return true;
240 }
241 }
242 return SecError(errSecParam, error, errMessage, item);
243 }
244
245 #define CheckItemInArray(item, values, msg) \
246 { \
247 const CFTypeRef vals[] = values; \
248 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \
249 return false; \
250 } \
251 }
252
253 #define ItemArray(...) { __VA_ARGS__ }
254
255
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 %@");
265 }
266
267 // Protection valid, use it.
268 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyProtection, protection);
269 return true;
270 }
271
272 SecAccessConstraintRef SecAccessConstraintCreatePolicy(CFAllocatorRef allocator, CFTypeRef policy, CFErrorRef *error) {
273 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintPolicy), policy, NULL);
274 }
275
276 SecAccessConstraintRef SecAccessConstraintCreatePasscode(CFAllocatorRef allocator) {
277 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintUserPasscode), kCFBooleanTrue, NULL);
278 }
279
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);
284 return constraint;
285 }
286
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);
292 return constraint;
293 }
294
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);
298 CFRelease(k);
299
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);
308 bool found = false;
309 for (CFIndex i = 0; i < (CFIndex)(sizeof(keysToCopy) / sizeof(keysToCopy[0])); i++) {
310 CFTypeRef value = CFDictionaryGetValue(constraint, keysToCopy[i]);
311 if (value) {
312 CFDictionarySetValue(kofn, keysToCopy[i], value);
313 found = true;
314 break;
315 }
316 }
317 require_quiet(found, errOut);
318 }
319
320 return kofn;
321
322 errOut:
323 SecError(errSecParam, error, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
324 CFReleaseSafe(kofn);
325 return NULL;
326 }
327
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);
331
332 SecAccessConstraintRef constraint = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintKofN), valueOfKofN, NULL);
333 CFReleaseSafe(valueOfKofN);
334 return constraint;
335
336 errOut:
337 return NULL;
338 }
339
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,
344 #endif
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"));
349 }
350
351 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
352 CFDictionarySetValue(constraints, operation, constraint);
353 return true;
354 }
355
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;
361
362 SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation);
363 if (!constraint) {
364 constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl);
365 }
366 return constraint;
367 }
368
369 CFDataRef SecAccessControlCopyConstraintData(SecAccessControlRef access_control, CFTypeRef operation) {
370 SecAccessConstraintRef constraint = SecAccessControlGetConstraint(access_control, operation);
371
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;
377 der_end += len;
378 der_end = der_encode_plist(constraint, NULL, der, der_end);
379 if (!der_end) {
380 CFReleaseNull(encoded);
381 }
382 return encoded;
383 }
384
385 CFDictionaryRef SecAccessControlGetConstraints(SecAccessControlRef access_control) {
386 return CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
387 }
388
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);
393 }
394
395 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control, bool require) {
396 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
397 CFDictionarySetValue(constraints, kAKSKeyAclParamRequirePasscode, require?kCFBooleanTrue:kCFBooleanFalse);
398 }
399
400 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control) {
401 CFMutableDictionaryRef acl = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
402 if (acl) {
403 return CFEqualSafe(CFDictionaryGetValue(acl, kAKSKeyAclParamRequirePasscode), kCFBooleanTrue);
404 }
405
406 return false;
407 }
408
409 void SecAccessControlSetBound(SecAccessControlRef access_control, bool bound) {
410 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyBound, bound ? kCFBooleanTrue : kCFBooleanFalse);
411 }
412
413 bool SecAccessControlIsBound(SecAccessControlRef access_control) {
414 CFTypeRef bound = CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyBound);
415 return bound != NULL && CFEqualSafe(bound, kCFBooleanTrue);
416 }
417
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;
424 der_end += len;
425 der_end = der_encode_plist(access_control->dict, NULL, der, der_end);
426 if (!der_end) {
427 CFReleaseNull(encoded);
428 }
429 return encoded;
430 }
431
432 SecAccessControlRef SecAccessControlCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) {
433 SecAccessControlRef access_control;
434 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
435
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"));
442 goto errOut;
443 }
444
445 CFReleaseSafe(access_control->dict);
446 access_control->dict = (CFMutableDictionaryRef)plist;
447 return access_control;
448
449 errOut:
450 CFReleaseSafe(access_control);
451 return NULL;
452 }