]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecAccessControl.c
Security-58286.70.7.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 "SecAccessControl.h"
31 #include "SecAccessControlPriv.h"
32 #include "SecItem.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>
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
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
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;
110
111 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
112
113 if (!SecAccessControlSetProtection(access_control, protection, error))
114 goto errOut;
115
116 if (flags) {
117 bool or = (flags & kSecAccessControlOr) ? true : false;
118 bool and = (flags & kSecAccessControlAnd) ? true : false;
119
120 if (or && and) {
121 SecError(errSecParam, error, CFSTR("only one logical operation can be set"));
122 goto errOut;
123 }
124
125 #pragma clang diagnostic push
126 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
127
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"));
131 goto errOut;
132 }
133
134 if (flags & kSecAccessControlUserPresence && flags & ~(kSecAccessControlUserPresence | kSecAccessControlApplicationPassword | kSecAccessControlPrivateKeyUsage)) {
135 SecError(errSecParam, error, CFSTR("kSecAccessControlUserPresence can be combined only with kSecAccessControlApplicationPassword and kSecAccessControlPrivateKeyUsage"));
136 goto errOut;
137 }
138
139 constraints = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
140
141 if (flags & kSecAccessControlUserPresence) {
142 require_quiet(constraint = SecAccessConstraintCreatePolicy(allocator, CFSTR(kACMPolicyDeviceOwnerAuthentication), error), errOut);
143 CFArrayAppendValue(constraints, constraint);
144 CFReleaseNull(constraint);
145 }
146
147 if (flags & kSecAccessControlDevicePasscode) {
148 require_quiet(constraint = SecAccessConstraintCreatePasscode(allocator), errOut);
149 CFArrayAppendValue(constraints, constraint);
150 CFReleaseNull(constraint);
151 }
152
153 if (flags & kSecAccessControlBiometryAny) {
154 require_quiet(constraint = SecAccessConstraintCreateBiometryAny(allocator, _getEmptyData()), errOut);
155 CFArrayAppendValue(constraints, constraint);
156 CFReleaseNull(constraint);
157 }
158
159 if (flags & kSecAccessControlBiometryCurrentSet) {
160 require_quiet(constraint = SecAccessConstraintCreateBiometryCurrentSet(allocator, _getEmptyData(), _getEmptyData()), errOut);
161 CFArrayAppendValue(constraints, constraint);
162 CFReleaseNull(constraint);
163 }
164
165 #pragma clang diagnostic pop
166
167 if (flags & kSecAccessControlApplicationPassword) {
168 SecAccessControlSetRequirePassword(access_control, true);
169 }
170
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);
178 }
179 else {
180 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, constraint, error), errOut);
181 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut);
182 }
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);
190 }
191 else {
192 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, CFArrayGetValueAtIndex(constraints, 0), error), errOut);
193 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut);
194 }
195 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
196 } else {
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);
202 }
203 else {
204 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
205 }
206 }
207
208 CFReleaseNull(constraints);
209 }
210 else {
211 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
212 }
213
214 return access_control;
215
216 errOut:
217 CFReleaseSafe(access_control);
218 CFReleaseSafe(constraints);
219 CFReleaseSafe(constraint);
220 return NULL;
221 }
222
223 CFTypeRef SecAccessControlGetProtection(SecAccessControlRef access_control) {
224 return CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyProtection);
225 }
226
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])) {
230 return true;
231 }
232 }
233 return SecError(errSecParam, error, CFSTR("%@: %@"), errMessage, item);
234 }
235
236 #define CheckItemInArray(item, values, msg) \
237 { \
238 const CFTypeRef vals[] = values; \
239 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), msg, error)) { \
240 return false; \
241 } \
242 }
243
244 #define ItemArray(...) { __VA_ARGS__ }
245
246
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"));
256 }
257
258 // Protection valid, use it.
259 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyProtection, protection);
260 return true;
261 }
262
263 SecAccessConstraintRef SecAccessConstraintCreatePolicy(CFAllocatorRef allocator, CFTypeRef policy, CFErrorRef *error) {
264 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintPolicy), policy, NULL);
265 }
266
267 SecAccessConstraintRef SecAccessConstraintCreatePasscode(CFAllocatorRef allocator) {
268 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintUserPasscode), kCFBooleanTrue, NULL);
269 }
270
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);
275 return constraint;
276 }
277
278 SecAccessConstraintRef SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator, CFDataRef catacombUUID) {
279 return SecAccessConstraintCreateBiometryAny(allocator, catacombUUID);
280 }
281
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);
287 return constraint;
288 }
289
290 SecAccessConstraintRef SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator, CFDataRef catacombUUID, CFDataRef bioDbHash) {
291 return SecAccessConstraintCreateBiometryCurrentSet(allocator, catacombUUID, bioDbHash);
292 }
293
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);
297 CFRelease(k);
298
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);
307 bool found = false;
308 for (CFIndex i = 0; i < (CFIndex)(sizeof(keysToCopy) / sizeof(keysToCopy[0])); i++) {
309 CFTypeRef value = CFDictionaryGetValue(constraint, keysToCopy[i]);
310 if (value) {
311 CFDictionarySetValue(kofn, keysToCopy[i], value);
312 found = true;
313 break;
314 }
315 }
316 require_quiet(found, errOut);
317 }
318
319 return kofn;
320
321 errOut:
322 SecError(errSecParam, error, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
323 CFReleaseSafe(kofn);
324 return NULL;
325 }
326
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);
330
331 SecAccessConstraintRef constraint = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintKofN), valueOfKofN, NULL);
332 CFReleaseSafe(valueOfKofN);
333 return constraint;
334
335 errOut:
336 return NULL;
337 }
338
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"));
346 }
347
348 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
349 CFDictionarySetValue(constraints, operation, constraint);
350 return true;
351 }
352
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;
358
359 SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation);
360 if (!constraint) {
361 constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl);
362 }
363 return constraint;
364 }
365
366 CFDataRef SecAccessControlCopyConstraintData(SecAccessControlRef access_control, CFTypeRef operation) {
367 SecAccessConstraintRef constraint = SecAccessControlGetConstraint(access_control, operation);
368
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;
374 der_end += len;
375 der_end = der_encode_plist(constraint, NULL, der, der_end);
376 if (!der_end) {
377 CFReleaseNull(encoded);
378 }
379 return encoded;
380 }
381
382 CFDictionaryRef SecAccessControlGetConstraints(SecAccessControlRef access_control) {
383 return CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
384 }
385
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);
390 }
391
392 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control, bool require) {
393 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
394 CFDictionarySetValue(constraints, kAKSKeyAclParamRequirePasscode, require?kCFBooleanTrue:kCFBooleanFalse);
395 }
396
397 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control) {
398 CFMutableDictionaryRef acl = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
399 if (acl) {
400 return CFEqualSafe(CFDictionaryGetValue(acl, kAKSKeyAclParamRequirePasscode), kCFBooleanTrue);
401 }
402
403 return false;
404 }
405
406 void SecAccessControlSetBound(SecAccessControlRef access_control, bool bound) {
407 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyBound, bound ? kCFBooleanTrue : kCFBooleanFalse);
408 }
409
410 bool SecAccessControlIsBound(SecAccessControlRef access_control) {
411 CFTypeRef bound = CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyBound);
412 return bound != NULL && CFEqualSafe(bound, kCFBooleanTrue);
413 }
414
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;
421 der_end += len;
422 der_end = der_encode_plist(access_control->dict, NULL, der, der_end);
423 if (!der_end) {
424 CFReleaseNull(encoded);
425 }
426 return encoded;
427 }
428
429 SecAccessControlRef SecAccessControlCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) {
430 SecAccessControlRef access_control;
431 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
432
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"));
439 goto errOut;
440 }
441
442 CFReleaseSafe(access_control->dict);
443 access_control->dict = (CFMutableDictionaryRef)plist;
444 return access_control;
445
446 errOut:
447 CFReleaseSafe(access_control);
448 return NULL;
449 }