]>
Commit | Line | Data |
---|---|---|
5c19dc3a A |
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 | /* | |
dbe77505 | 25 | * SecAccessControl.m - CoreFoundation based access control object |
5c19dc3a A |
26 | */ |
27 | ||
28 | #include <TargetConditionals.h> | |
29 | #include <AssertMacros.h> | |
b54c578e A |
30 | #include <Security/SecAccessControl.h> |
31 | #include <Security/SecAccessControlPriv.h> | |
32 | #include <Security/SecItem.h> | |
33 | #include <Security/SecItemPriv.h> | |
5c19dc3a A |
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 | ||
dbe77505 A |
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()) { | |
b3971512 A |
55 | NSNumber *boolValue = value; |
56 | [target appendString:boolValue.boolValue ? @"true" : @"false"]; | |
dbe77505 | 57 | } else if ([value isKindOfClass:NSNumber.class]) { |
b3971512 A |
58 | NSNumber *numberValue = value; |
59 | [target appendString:numberValue.stringValue]; | |
dbe77505 A |
60 | } else if ([value isKindOfClass:NSString.class]) { |
61 | [target appendString:value]; | |
b3971512 A |
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 | } | |
dbe77505 A |
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 | ||
5c19dc3a A |
83 | static CFStringRef SecAccessControlCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
84 | SecAccessControlRef access_control = (SecAccessControlRef)cf; | |
dbe77505 A |
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]); | |
5c19dc3a A |
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 | } | |
ecaf5866 | 129 | |
5c19dc3a A |
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 | } | |
5c19dc3a A |
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) { | |
5c19dc3a A |
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 | ||
ecaf5866 A |
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) { | |
5c19dc3a A |
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)) { | |
5c19dc3a A |
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 | ||
ecaf5866 A |
189 | if (flags & kSecAccessControlBiometryAny) { |
190 | require_quiet(constraint = SecAccessConstraintCreateBiometryAny(allocator, _getEmptyData()), errOut); | |
5c19dc3a A |
191 | CFArrayAppendValue(constraints, constraint); |
192 | CFReleaseNull(constraint); | |
193 | } | |
194 | ||
ecaf5866 A |
195 | if (flags & kSecAccessControlBiometryCurrentSet) { |
196 | require_quiet(constraint = SecAccessConstraintCreateBiometryCurrentSet(allocator, _getEmptyData(), _getEmptyData()), errOut); | |
5c19dc3a A |
197 | CFArrayAppendValue(constraints, constraint); |
198 | CFReleaseNull(constraint); | |
199 | } | |
200 | ||
b54c578e A |
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 | ||
ecaf5866 A |
209 | #pragma clang diagnostic pop |
210 | ||
5c19dc3a A |
211 | if (flags & kSecAccessControlApplicationPassword) { |
212 | SecAccessControlSetRequirePassword(access_control, true); | |
213 | } | |
ecaf5866 | 214 | |
5c19dc3a | 215 | CFIndex constraints_count = CFArrayGetCount(constraints); |
5c19dc3a A |
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); | |
3a7be6fd | 220 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, constraint, error), errOut); |
fa7225c8 | 221 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut); |
5c19dc3a A |
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); | |
ecaf5866 | 229 | } else if (constraints_count == 1) { |
5c19dc3a A |
230 | if (flags & kSecAccessControlPrivateKeyUsage) { |
231 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpSign, CFArrayGetValueAtIndex(constraints, 0), error), errOut); | |
3a7be6fd | 232 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, CFArrayGetValueAtIndex(constraints, 0), error), errOut); |
fa7225c8 | 233 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut); |
5c19dc3a A |
234 | } |
235 | else { | |
5c19dc3a A |
236 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, CFArrayGetValueAtIndex(constraints, 0), error), errOut); |
237 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, error), errOut); | |
5c19dc3a | 238 | } |
5c19dc3a A |
239 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut); |
240 | } else { | |
5c19dc3a A |
241 | if (flags & kSecAccessControlPrivateKeyUsage) { |
242 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpSign, kCFBooleanTrue, error), errOut); | |
3a7be6fd | 243 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpComputeKey, kCFBooleanTrue, error), errOut); |
fa7225c8 | 244 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpAttest, kCFBooleanTrue, error), errOut); |
5c19dc3a A |
245 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut); |
246 | } | |
247 | else { | |
5c19dc3a | 248 | require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDefaultAcl, kCFBooleanTrue, error), errOut); |
5c19dc3a | 249 | } |
5c19dc3a A |
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 | } | |
866f8763 | 277 | return SecError(errSecParam, error, CFSTR("%@: %@"), errMessage, item); |
5c19dc3a A |
278 | } |
279 | ||
280 | #define CheckItemInArray(item, values, msg) \ | |
281 | { \ | |
282 | const CFTypeRef vals[] = values; \ | |
866f8763 | 283 | if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), msg, error)) { \ |
5c19dc3a A |
284 | return false; \ |
285 | } \ | |
286 | } | |
287 | ||
288 | #define ItemArray(...) { __VA_ARGS__ } | |
289 | ||
290 | ||
291 | bool SecAccessControlSetProtection(SecAccessControlRef access_control, CFTypeRef protection, CFErrorRef *error) { | |
fa7225c8 A |
292 | if (!protection || CFGetTypeID(protection) != CFDictionaryGetTypeID()) { |
293 | // Verify protection type. | |
294 | CheckItemInArray(protection, ItemArray(kSecAttrAccessibleAlwaysPrivate, kSecAttrAccessibleAfterFirstUnlock, | |
295 | kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate, | |
296 | kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, | |
297 | kSecAttrAccessibleWhenUnlockedThisDeviceOnly, | |
0e1db9d1 A |
298 | kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, |
299 | kSecAttrAccessibleUntilReboot), | |
866f8763 | 300 | CFSTR("SecAccessControl: invalid protection")); |
fa7225c8 | 301 | } |
5c19dc3a A |
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 | ||
ecaf5866 | 316 | SecAccessConstraintRef SecAccessConstraintCreateBiometryAny(CFAllocatorRef allocator, CFDataRef catacombUUID) { |
5c19dc3a A |
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 | ||
ecaf5866 A |
323 | SecAccessConstraintRef SecAccessConstraintCreateTouchIDAny(CFAllocatorRef allocator, CFDataRef catacombUUID) { |
324 | return SecAccessConstraintCreateBiometryAny(allocator, catacombUUID); | |
325 | } | |
326 | ||
327 | SecAccessConstraintRef SecAccessConstraintCreateBiometryCurrentSet(CFAllocatorRef allocator, CFDataRef catacombUUID, CFDataRef bioDbHash) { | |
5c19dc3a A |
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 | ||
ecaf5866 A |
335 | SecAccessConstraintRef SecAccessConstraintCreateTouchIDCurrentSet(CFAllocatorRef allocator, CFDataRef catacombUUID, CFDataRef bioDbHash) { |
336 | return SecAccessConstraintCreateBiometryCurrentSet(allocator, catacombUUID, bioDbHash); | |
337 | } | |
338 | ||
b54c578e A |
339 | SecAccessConstraintRef SecAccessConstraintCreateWatch(CFAllocatorRef allocator) { |
340 | return CFDictionaryCreateMutableForCFTypesWith(allocator, CFSTR(kACMKeyAclConstraintWatch), kCFBooleanTrue, NULL); | |
341 | } | |
342 | ||
5c19dc3a A |
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), | |
b54c578e | 352 | CFSTR(kACMKeyAclConstraintUserPasscode), CFSTR(kACMKeyAclConstraintWatch) }; |
5c19dc3a A |
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) { | |
5c19dc3a A |
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); | |
d64be36e | 481 | require_quiet(der = der_decode_plist(0, &plist, error, der, der_end), errOut); |
5c19dc3a A |
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 | } |