]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecAccessControl.m
Security-59306.101.1.tar.gz
[apple/security.git] / OSX / sec / Security / SecAccessControl.m
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.m - 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 void dumpValue(id value, NSMutableString *target, NSString *separator) {
52 if (value == nil) {
53 // Do nothing.
54 } else if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
55 NSNumber *boolValue = value;
56 [target appendString:boolValue.boolValue ? @"true" : @"false"];
57 } else if ([value isKindOfClass:NSNumber.class]) {
58 NSNumber *numberValue = value;
59 [target appendString:numberValue.stringValue];
60 } else if ([value isKindOfClass:NSString.class]) {
61 [target appendString:value];
62 } else if ([value isKindOfClass:NSData.class]) {
63 NSData *dataValue = value;
64 const uint8_t *dataBuffer = dataValue.bytes;
65 NSUInteger dumpLength = dataValue.length > 64 ? 64 : dataValue.length;
66 for (NSUInteger i = 0; i < dumpLength; i++) {
67 [target appendFormat:@"%02X", (unsigned)dataBuffer[i]];
68 }
69 if (dumpLength < dataValue.length) {
70 [target appendFormat:@"...(%db)", (int)dataValue.length];
71 }
72 } else if ([value isKindOfClass:NSDictionary.class]) {
73 [value enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
74 [target appendString:separator];
75 dumpValue(key, target, @"");
76 [target appendString:@"("];
77 dumpValue(obj, target, @"");
78 [target appendString:@")"];
79 }];
80 }
81 }
82
83 static CFStringRef SecAccessControlCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
84 SecAccessControlRef access_control = (SecAccessControlRef)cf;
85 NSDictionary *contents = (__bridge NSDictionary *)access_control->dict;
86 NSMutableString *dump = [NSMutableString string];
87 dumpValue(contents[(__bridge id)kSecAccessControlKeyProtection], dump, @"");
88 dumpValue(contents[(__bridge id)kAKSKeyAcl], dump, @";");
89 return CFBridgingRetain([NSString stringWithFormat:@"<SecAccessControlRef: %@>", dump]);
90 }
91
92 static Boolean SecAccessControlCompare(CFTypeRef lhs, CFTypeRef rhs) {
93 SecAccessControlRef laccess_control = (SecAccessControlRef)lhs;
94 SecAccessControlRef raccess_control = (SecAccessControlRef)rhs;
95 return (laccess_control == raccess_control) || CFEqual(laccess_control->dict, raccess_control->dict);
96 }
97
98 static void SecAccessControlDestroy(CFTypeRef cf) {
99 SecAccessControlRef access_control = (SecAccessControlRef)cf;
100 CFReleaseSafe(access_control->dict);
101 }
102
103 CFGiblisWithCompareFor(SecAccessControl);
104
105 static CFMutableDictionaryRef SecAccessControlGetMutableConstraints(SecAccessControlRef access_control) {
106 CFMutableDictionaryRef constraints = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
107
108 if (!constraints) {
109 CFMutableDictionaryRef newConstraints = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control));
110 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, newConstraints);
111 CFRelease(newConstraints);
112
113 constraints = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
114 }
115
116 return constraints;
117 }
118
119 SecAccessControlRef SecAccessControlCreate(CFAllocatorRef allocator, CFErrorRef *error) {
120 SecAccessControlRef access_control = CFTypeAllocate(SecAccessControl, struct __SecAccessControl, allocator);
121 if (!access_control) {
122 SecError(errSecAllocate, error, CFSTR("allocate memory for SecAccessControl"));
123 return NULL;
124 }
125
126 access_control->dict = CFDictionaryCreateMutableForCFTypes(allocator);
127 return access_control;
128 }
129
130 static CFDataRef _getEmptyData() {
131 static CFMutableDataRef emptyData = NULL;
132 static dispatch_once_t onceToken;
133
134 dispatch_once(&onceToken, ^{
135 emptyData = CFDataCreateMutable(kCFAllocatorDefault, 0);
136 });
137
138 return emptyData;
139 }
140
141 SecAccessControlRef SecAccessControlCreateWithFlags(CFAllocatorRef allocator, CFTypeRef protection,
142 SecAccessControlCreateFlags flags, CFErrorRef *error) {
143 SecAccessControlRef access_control = NULL;
144 CFTypeRef constraint = NULL;
145 CFMutableArrayRef constraints = NULL;
146
147 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
148
149 if (!SecAccessControlSetProtection(access_control, protection, error))
150 goto errOut;
151
152 if (flags) {
153 bool or = (flags & kSecAccessControlOr) ? true : false;
154 bool and = (flags & kSecAccessControlAnd) ? true : false;
155
156 if (or && and) {
157 SecError(errSecParam, error, CFSTR("only one logical operation can be set"));
158 goto errOut;
159 }
160
161 #pragma clang diagnostic push
162 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
163
164 SecAccessControlCreateFlags maskedFlags = flags & (kSecAccessControlBiometryAny | kSecAccessControlBiometryCurrentSet);
165 if (maskedFlags && maskedFlags != kSecAccessControlBiometryAny && maskedFlags != kSecAccessControlBiometryCurrentSet) {
166 SecError(errSecParam, error, CFSTR("only one bio constraint can be set"));
167 goto errOut;
168 }
169
170 if (flags & kSecAccessControlUserPresence && flags & ~(kSecAccessControlUserPresence | kSecAccessControlApplicationPassword | kSecAccessControlPrivateKeyUsage)) {
171 SecError(errSecParam, error, CFSTR("kSecAccessControlUserPresence can be combined only with kSecAccessControlApplicationPassword and kSecAccessControlPrivateKeyUsage"));
172 goto errOut;
173 }
174
175 constraints = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
176
177 if (flags & kSecAccessControlUserPresence) {
178 require_quiet(constraint = SecAccessConstraintCreatePolicy(allocator, CFSTR(kACMPolicyDeviceOwnerAuthentication), error), errOut);
179 CFArrayAppendValue(constraints, constraint);
180 CFReleaseNull(constraint);
181 }
182
183 if (flags & kSecAccessControlDevicePasscode) {
184 require_quiet(constraint = SecAccessConstraintCreatePasscode(allocator), errOut);
185 CFArrayAppendValue(constraints, constraint);
186 CFReleaseNull(constraint);
187 }
188
189 if (flags & kSecAccessControlBiometryAny) {
190 require_quiet(constraint = SecAccessConstraintCreateBiometryAny(allocator, _getEmptyData()), errOut);
191 CFArrayAppendValue(constraints, constraint);
192 CFReleaseNull(constraint);
193 }
194
195 if (flags & kSecAccessControlBiometryCurrentSet) {
196 require_quiet(constraint = SecAccessConstraintCreateBiometryCurrentSet(allocator, _getEmptyData(), _getEmptyData()), errOut);
197 CFArrayAppendValue(constraints, constraint);
198 CFReleaseNull(constraint);
199 }
200
201 #if TARGET_OS_OSX
202 if (flags & kSecAccessControlWatch) {
203 require_quiet(constraint = SecAccessConstraintCreateWatch(allocator), errOut);
204 CFArrayAppendValue(constraints, constraint);
205 CFReleaseNull(constraint);
206 }
207 #endif
208
209 #pragma clang diagnostic pop
210
211 if (flags & kSecAccessControlApplicationPassword) {
212 SecAccessControlSetRequirePassword(access_control, true);
213 }
214
215 CFIndex constraints_count = CFArrayGetCount(constraints);
216 if (constraints_count > 1) {
217 require_quiet(constraint = SecAccessConstraintCreateValueOfKofN(allocator, or?1:constraints_count, constraints, error), errOut);
218 if (flags & kSecAccessControlPrivateKeyUsage) {
219 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpSign, constraint, error), errOut);
220 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, constraint, error), errOut);
221 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut);
222 }
223 else {
224 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, constraint, error), errOut);
225 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut);
226 }
227 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
228 CFReleaseNull(constraint);
229 } else if (constraints_count == 1) {
230 if (flags & kSecAccessControlPrivateKeyUsage) {
231 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpSign, CFArrayGetValueAtIndex(constraints, 0), error), errOut);
232 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, CFArrayGetValueAtIndex(constraints, 0), error), errOut);
233 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut);
234 }
235 else {
236 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, CFArrayGetValueAtIndex(constraints, 0), error), errOut);
237 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut);
238 }
239 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
240 } else {
241 if (flags & kSecAccessControlPrivateKeyUsage) {
242 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpSign, kCFBooleanTrue, error), errOut);
243 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, kCFBooleanTrue, error), errOut);
244 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut);
245 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut);
246 }
247 else {
248 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
249 }
250 }
251
252 CFReleaseNull(constraints);
253 }
254 else {
255 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut);
256 }
257
258 return access_control;
259
260 errOut:
261 CFReleaseSafe(access_control);
262 CFReleaseSafe(constraints);
263 CFReleaseSafe(constraint);
264 return NULL;
265 }
266
267 CFTypeRef SecAccessControlGetProtection(SecAccessControlRef access_control) {
268 return CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyProtection);
269 }
270
271 static bool checkItemInArray(CFTypeRef item, const CFTypeRef *values, CFIndex count, CFStringRef errMessage, CFErrorRef *error) {
272 for (CFIndex i = 0; i < count; i++) {
273 if (CFEqualSafe(item, values[i])) {
274 return true;
275 }
276 }
277 return SecError(errSecParam, error, CFSTR("%@: %@"), errMessage, item);
278 }
279
280 #define CheckItemInArray(item, values, msg) \
281 { \
282 const CFTypeRef vals[] = values; \
283 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), msg, error)) { \
284 return false; \
285 } \
286 }
287
288 #define ItemArray(...) { __VA_ARGS__ }
289
290
291 bool SecAccessControlSetProtection(SecAccessControlRef access_control, CFTypeRef protection, CFErrorRef *error) {
292 if (!protection || CFGetTypeID(protection) != CFDictionaryGetTypeID()) {
293 // Verify protection type.
294 CheckItemInArray(protection, ItemArray(kSecAttrAccessibleAlwaysPrivate, kSecAttrAccessibleAfterFirstUnlock,
295 kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate,
296 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
297 kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
298 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
299 kSecAttrAccessibleUntilReboot),
300 CFSTR("SecAccessControl: invalid protection"));
301 }
302
303 // Protection valid, use it.
304 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyProtection, protection);
305 return true;
306 }
307
308 SecAccessConstraintRef SecAccessConstraintCreatePolicy(CFAllocatorRef allocator, CFTypeRef policy, CFErrorRef *error) {
309 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintPolicy), policy, NULL);
310 }
311
312 SecAccessConstraintRef SecAccessConstraintCreatePasscode(CFAllocatorRef allocator) {
313 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintUserPasscode), kCFBooleanTrue, NULL);
314 }
315
316 SecAccessConstraintRef SecAccessConstraintCreateBiometryAny(CFAllocatorRef allocator, CFDataRef catacombUUID) {
317 CFMutableDictionaryRef bioDict = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclParamBioCatacombUUID), catacombUUID, NULL);
318 SecAccessConstraintRef constraint = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintBio), bioDict, NULL);
319 CFReleaseSafe(bioDict);
320 return constraint;
321 }
322
323 SecAccessConstraintRef SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator, CFDataRef catacombUUID) {
324 return SecAccessConstraintCreateBiometryAny(allocator, catacombUUID);
325 }
326
327 SecAccessConstraintRef SecAccessConstraintCreateBiometryCurrentSet(CFAllocatorRef allocator, CFDataRef catacombUUID, CFDataRef bioDbHash) {
328 CFMutableDictionaryRef bioDict = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclParamBioCatacombUUID), catacombUUID, NULL);
329 CFDictionarySetValue(bioDict, CFSTR(kACMKeyAclParamBioDatabaseHash), bioDbHash);
330 SecAccessConstraintRef constraint = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintBio), bioDict, NULL);
331 CFReleaseSafe(bioDict);
332 return constraint;
333 }
334
335 SecAccessConstraintRef SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator, CFDataRef catacombUUID, CFDataRef bioDbHash) {
336 return SecAccessConstraintCreateBiometryCurrentSet(allocator, catacombUUID, bioDbHash);
337 }
338
339 SecAccessConstraintRef SecAccessConstraintCreateWatch(CFAllocatorRef allocator) {
340 return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintWatch), kCFBooleanTrue, NULL);
341 }
342
343 static SecAccessConstraintRef SecAccessConstraintCreateValueOfKofN(CFAllocatorRef allocator, size_t numRequired, CFArrayRef constraints, CFErrorRef *error) {
344 CFNumberRef k = CFNumberCreateWithCFIndex(allocator, numRequired);
345 CFMutableDictionaryRef kofn = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclParamKofN), k, NULL);
346 CFRelease(k);
347
348 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional
349 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres
350 into k-of-n dictionary. */
351 const CFTypeRef keysToCopy[] = { CFSTR(kACMKeyAclConstraintBio), CFSTR(kACMKeyAclConstraintPolicy),
352 CFSTR(kACMKeyAclConstraintUserPasscode), CFSTR(kACMKeyAclConstraintWatch) };
353 SecAccessConstraintRef constraint;
354 CFArrayForEachC(constraints, constraint) {
355 require_quiet(isDictionary(constraint), errOut);
356 bool found = false;
357 for (CFIndex i = 0; i < (CFIndex)(sizeof(keysToCopy) / sizeof(keysToCopy[0])); i++) {
358 CFTypeRef value = CFDictionaryGetValue(constraint, keysToCopy[i]);
359 if (value) {
360 CFDictionarySetValue(kofn, keysToCopy[i], value);
361 found = true;
362 break;
363 }
364 }
365 require_quiet(found, errOut);
366 }
367
368 return kofn;
369
370 errOut:
371 SecError(errSecParam, error, CFSTR("SecAccessControl: invalid constraint for k-of-n"));
372 CFReleaseSafe(kofn);
373 return NULL;
374 }
375
376 SecAccessConstraintRef SecAccessConstraintCreateKofN(CFAllocatorRef allocator, size_t numRequired, CFArrayRef constraints, CFErrorRef *error) {
377 SecAccessConstraintRef valueOfKofN = SecAccessConstraintCreateValueOfKofN(allocator, numRequired, constraints, error);
378 require_quiet(valueOfKofN, errOut);
379
380 SecAccessConstraintRef constraint = CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintKofN), valueOfKofN, NULL);
381 CFReleaseSafe(valueOfKofN);
382 return constraint;
383
384 errOut:
385 return NULL;
386 }
387
388 bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control, CFTypeRef operation, CFTypeRef constraint, CFErrorRef *error) {
389 if (!isDictionary(constraint) && !CFEqual(constraint, kCFBooleanTrue) && !CFEqual(constraint, kCFBooleanFalse) ) {
390 return SecError(errSecParam, error, CFSTR("invalid constraint"));
391 }
392
393 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
394 CFDictionarySetValue(constraints, operation, constraint);
395 return true;
396 }
397
398 SecAccessConstraintRef SecAccessControlGetConstraint(SecAccessControlRef access_control, CFTypeRef operation) {
399 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
400 if (!ops || CFDictionaryGetCount(ops) == 0)
401 // No ACL is present, this means that everything is allowed.
402 return kCFBooleanTrue;
403
404 SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation);
405 if (!constraint) {
406 constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl);
407 }
408 return constraint;
409 }
410
411 CFDataRef SecAccessControlCopyConstraintData(SecAccessControlRef access_control, CFTypeRef operation) {
412 SecAccessConstraintRef constraint = SecAccessControlGetConstraint(access_control, operation);
413
414 size_t len = der_sizeof_plist(constraint, 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;
419 der_end += len;
420 der_end = der_encode_plist(constraint, NULL, der, der_end);
421 if (!der_end) {
422 CFReleaseNull(encoded);
423 }
424 return encoded;
425 }
426
427 CFDictionaryRef SecAccessControlGetConstraints(SecAccessControlRef access_control) {
428 return CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
429 }
430
431 void SecAccessControlSetConstraints(SecAccessControlRef access_control, CFDictionaryRef constraints) {
432 CFMutableDictionaryRef mutableConstraints = CFDictionaryCreateMutableCopy(CFGetAllocator(access_control), 0, constraints);
433 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, mutableConstraints);
434 CFReleaseSafe(mutableConstraints);
435 }
436
437 void SecAccessControlSetRequirePassword(SecAccessControlRef access_control, bool require) {
438 CFMutableDictionaryRef constraints = SecAccessControlGetMutableConstraints(access_control);
439 CFDictionarySetValue(constraints, kAKSKeyAclParamRequirePasscode, require?kCFBooleanTrue:kCFBooleanFalse);
440 }
441
442 bool SecAccessControlGetRequirePassword(SecAccessControlRef access_control) {
443 CFMutableDictionaryRef acl = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl);
444 if (acl) {
445 return CFEqualSafe(CFDictionaryGetValue(acl, kAKSKeyAclParamRequirePasscode), kCFBooleanTrue);
446 }
447
448 return false;
449 }
450
451 void SecAccessControlSetBound(SecAccessControlRef access_control, bool bound) {
452 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyBound, bound ? kCFBooleanTrue : kCFBooleanFalse);
453 }
454
455 bool SecAccessControlIsBound(SecAccessControlRef access_control) {
456 CFTypeRef bound = CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyBound);
457 return bound != NULL && CFEqualSafe(bound, kCFBooleanTrue);
458 }
459
460 CFDataRef SecAccessControlCopyData(SecAccessControlRef access_control) {
461 size_t len = der_sizeof_plist(access_control->dict, NULL);
462 CFMutableDataRef encoded = CFDataCreateMutable(0, len);
463 CFDataSetLength(encoded, len);
464 uint8_t *der_end = CFDataGetMutableBytePtr(encoded);
465 const uint8_t *der = der_end;
466 der_end += len;
467 der_end = der_encode_plist(access_control->dict, NULL, der, der_end);
468 if (!der_end) {
469 CFReleaseNull(encoded);
470 }
471 return encoded;
472 }
473
474 SecAccessControlRef SecAccessControlCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) {
475 SecAccessControlRef access_control;
476 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut);
477
478 CFPropertyListRef plist;
479 const uint8_t *der = CFDataGetBytePtr(data);
480 const uint8_t *der_end = der + CFDataGetLength(data);
481 require_quiet(der = der_decode_plist(0, kCFPropertyListMutableContainers, &plist, error, der, der_end), errOut);
482 if (der != der_end) {
483 SecError(errSecDecode, error, CFSTR("trailing garbage at end of SecAccessControl data"));
484 goto errOut;
485 }
486
487 CFReleaseSafe(access_control->dict);
488 access_control->dict = (CFMutableDictionaryRef)plist;
489 return access_control;
490
491 errOut:
492 CFReleaseSafe(access_control);
493 return NULL;
494 }