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