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 <AssertMacros.h> 29#include <Security/SecAccessControl.h> 30#include <Security/SecAccessControlPriv.h> 31#include <Security/SecItem.h> 32#include <utilities/SecCFWrappers.h> 33#include <utilities/SecCFError.h> 34#include <utilities/der_plist.h> 35 36#if TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR) 37#define USE_KEYSTORE 1 38#elif TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR 39#define USE_KEYSTORE 1 40#else /* no keystore on this platform */ 41#define USE_KEYSTORE 0 42#endif 43 44#include <libaks_acl_cf_keys.h> 45 46static CFTypeRef kSecAccessControlKeyProtection = CFSTR("prot"); 47 48// TODO: Use real name of this policy from SCred/AppleCredentialManager 49CFTypeRef kSecAccessControlPolicyUserPresent = CFSTR("DeviceOwnerAuthenticated"); 50 51struct __SecAccessControl { 52 CFRuntimeBase _base; 53 CFMutableDictionaryRef dict; 54}; 55 56static CFStringRef SecAccessControlCopyDescription(CFTypeRef cf) { 57 SecAccessControlRef access_control = (SecAccessControlRef)cf; 58 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecAccessControlRef: %p>"), access_control); 59} 60 61static Boolean SecAccessControlCompare(CFTypeRef lhs, CFTypeRef rhs) { 62 SecAccessControlRef laccess_control = (SecAccessControlRef)lhs; 63 SecAccessControlRef raccess_control = (SecAccessControlRef)rhs; 64 return (laccess_control == raccess_control) || CFEqual(laccess_control->dict, raccess_control->dict); 65} 66 67static void SecAccessControlDestroy(CFTypeRef cf) { 68 SecAccessControlRef access_control = (SecAccessControlRef)cf; 69 CFReleaseSafe(access_control->dict); 70} 71 72CFGiblisWithCompareFor(SecAccessControl); 73 74SecAccessControlRef SecAccessControlCreate(CFAllocatorRef allocator, CFErrorRef *error) { 75 SecAccessControlRef access_control = CFTypeAllocate(SecAccessControl, struct __SecAccessControl, allocator); 76 if (!access_control) { 77 SecError(errSecAllocate, error, CFSTR("allocate memory for SecAccessControl")); 78 return NULL; 79 } 80 81 access_control->dict = CFDictionaryCreateMutableForCFTypes(allocator); 82 return access_control; 83} 84 85 86SecAccessControlRef SecAccessControlCreateWithFlags(CFAllocatorRef allocator, CFTypeRef protection, 87 SecAccessControlCreateFlags flags, CFErrorRef *error) { 88 SecAccessControlRef access_control = NULL; 89 CFTypeRef constraint = NULL; 90 91 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut); 92 93 if (!SecAccessControlSetProtection(access_control, protection, error)) 94 goto errOut; 95 96 if (flags & kSecAccessControlUserPresence) { 97 require_quiet(constraint = SecAccessConstraintCreatePolicy(kSecAccessControlPolicyUserPresent, error), errOut); 98 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDecrypt, 99 constraint, error), errOut); 100 CFReleaseNull(constraint); 101 require_quiet(SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpDelete, kCFBooleanTrue, error), errOut); 102 } 103 104 return access_control; 105 106errOut: 107 CFReleaseSafe(access_control); 108 CFReleaseSafe(constraint); 109 return NULL; 110} 111 112CFTypeRef SecAccessControlGetProtection(SecAccessControlRef access_control) { 113 return CFDictionaryGetValue(access_control->dict, kSecAccessControlKeyProtection); 114} 115 116static bool checkItemInArray(CFTypeRef item, const CFTypeRef *values, CFIndex count, CFStringRef errMessage, CFErrorRef *error) { 117 for (CFIndex i = 0; i < count; i++) { 118 if (CFEqualSafe(item, values[i])) { 119 return true; 120 } 121 } 122 return SecError(errSecParam, error, errMessage, item); 123} 124 125#define CheckItemInArray(item, values, msg) \ 126{ \ 127 const CFTypeRef vals[] = values; \ 128 if (!checkItemInArray(item, vals, sizeof(vals)/sizeof(*vals), CFSTR(msg), error)) { \ 129 return false; \ 130 } \ 131} 132 133#define ItemArray(...) { __VA_ARGS__ } 134 135 136bool SecAccessControlSetProtection(SecAccessControlRef access_control, CFTypeRef protection, CFErrorRef *error) { 137 // Verify protection type. 138 CheckItemInArray(protection, ItemArray(kSecAttrAccessibleAlways, kSecAttrAccessibleAfterFirstUnlock, 139 kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAlwaysThisDeviceOnly, 140 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, 141 kSecAttrAccessibleWhenUnlockedThisDeviceOnly, 142 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly), 143 "SecAccessControl: invalid protection %@"); 144 145 // Protection valid, use it. 146 CFDictionarySetValue(access_control->dict, kSecAccessControlKeyProtection, protection); 147 return true; 148} 149 150SecAccessConstraintRef SecAccessConstraintCreatePolicy(CFTypeRef policy, CFErrorRef *error) { 151 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintPolicy, policy, NULL); 152} 153 154SecAccessConstraintRef SecAccessConstraintCreatePasscode(bool systemPasscode) { 155 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintUserPasscode, kCFBooleanTrue, NULL); 156} 157 158SecAccessConstraintRef SecAccessConstraintCreateTouchID(CFDataRef uuid, CFErrorRef *error) { 159 return CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintBio, 160 uuid ? uuid : (const void *)kCFBooleanTrue, NULL); 161} 162 163SecAccessConstraintRef SecAccessConstraintCreateKofN(size_t numRequired, CFArrayRef constraints, CFErrorRef *error) { 164 CFNumberRef k = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numRequired); 165 CFMutableDictionaryRef kofn = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclParamKofN, k, NULL); 166 CFRelease(k); 167 168 /* Populate kofn dictionary with constraint keys from the array. note that for now we just ignore any additional 169 constraint parameters, but we might err-out if some parameter is found, since we cannot propagate parameteres 170 into k-of-n dictionary. */ 171 const CFTypeRef keysToCopy[] = { kAKSKeyAclConstraintBio, kAKSKeyAclConstraintPolicy, 172 kAKSKeyAclConstraintUserPasscode }; 173 SecAccessConstraintRef constraint; 174 CFArrayForEachC(constraints, constraint) { 175 require_quiet(isDictionary(constraint), errOut); 176 bool found = false; 177 for (CFIndex i = 0; i < (CFIndex)(sizeof(keysToCopy) / sizeof(keysToCopy[0])); i++) { 178 CFTypeRef value = CFDictionaryGetValue(constraint, keysToCopy[i]); 179 if (value) { 180 CFDictionarySetValue(kofn, keysToCopy[i], value); 181 found = true; 182 break; 183 } 184 } 185 require_quiet(found, errOut); 186 } 187 188 constraint = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kAKSKeyAclConstraintKofN, kofn, NULL); 189 CFRelease(kofn); 190 return constraint; 191 192errOut: 193 SecError(errSecParam, error, CFSTR("SecAccessControl: invalid constraint for k-of-n")); 194 CFReleaseSafe(kofn); 195 return NULL; 196} 197 198bool SecAccessConstraintSetOption(SecAccessConstraintRef constraint, CFTypeRef option, CFTypeRef value, CFErrorRef *error) { 199 CheckItemInArray(option, ItemArray(kAKSKeyAclConstraintAccessGroups, kAKSKeyAclParamCredentialMaxAge), 200 "SecAccessControl: invalid constraint option %@"); 201 CFDictionarySetValue((CFMutableDictionaryRef)constraint, option, value); 202 return true; 203} 204 205bool SecAccessControlAddConstraintForOperation(SecAccessControlRef access_control, CFTypeRef operation, CFTypeRef constraint, CFErrorRef *error) { 206 CheckItemInArray(operation, ItemArray(kAKSKeyOpEncrypt, kAKSKeyOpDecrypt, 207 kAKSKeyOpSync, kAKSKeyOpDefaultAcl, kAKSKeyOpDelete), 208 "SecAccessControl: invalid operation %@"); 209 if (!isDictionary(constraint) && !CFEqual(constraint, kCFBooleanTrue) && !CFEqual(constraint, kCFBooleanFalse) ) { 210 return SecError(errSecParam, error, CFSTR("invalid constraint")); 211 } 212 213 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 214 if (!ops) { 215 ops = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control)); 216 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, ops); 217 } 218 CFDictionarySetValue(ops, operation, constraint); 219 return true; 220} 221 222void SecAccessControlRemoveConstraintForOperation(SecAccessControlRef access_control, CFTypeRef operation) { 223 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 224 if (ops) 225 CFDictionaryRemoveValue(ops, operation); 226} 227 228SecAccessConstraintRef SecAccessControlGetConstraint(SecAccessControlRef access_control, CFTypeRef operation) { 229 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 230 if (!ops || CFDictionaryGetCount(ops) == 0) 231 // No ACL is present, this means that everything is allowed. 232 return kCFBooleanTrue; 233 234 SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation); 235 if (!constraint) { 236 constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl); 237 } 238 return constraint; 239} 240 241CFDictionaryRef SecAccessControlGetConstraints(SecAccessControlRef access_control) { 242 return CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 243} 244 245void SecAccessControlSetConstraints(SecAccessControlRef access_control, CFDictionaryRef constraints) { 246 CFMutableDictionaryRef mutableConstraints = CFDictionaryCreateMutableCopy(NULL, 0, constraints); 247 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, mutableConstraints); 248 CFReleaseSafe(mutableConstraints); 249} 250 251void SecAccessControlSetAccessGroups(SecAccessControlRef access_control, CFArrayRef access_groups) { 252 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 253 if (!ops) { 254 ops = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(access_control)); 255 CFDictionarySetValue(access_control->dict, kAKSKeyAcl, ops); 256 } 257 CFDictionarySetValue(ops, kAKSKeyAccessGroups, access_groups); 258} 259 260CFArrayRef SecAccessControlGetAccessGroups(SecAccessControlRef access_control, CFTypeRef operation) { 261 CFMutableDictionaryRef ops = (CFMutableDictionaryRef)CFDictionaryGetValue(access_control->dict, kAKSKeyAcl); 262 if (!ops) 263 return NULL; 264 265 CFArrayRef access_groups = NULL; 266 SecAccessConstraintRef constraint = CFDictionaryGetValue(ops, operation); 267 if (!constraint) { 268 constraint = CFDictionaryGetValue(ops, kAKSKeyOpDefaultAcl); 269 } 270 if (constraint && isDictionary(constraint)) { 271 access_groups = CFDictionaryGetValue(constraint, kAKSKeyAclConstraintAccessGroups); 272 } 273 if (!access_groups) { 274 access_groups = CFDictionaryGetValue(ops, kAKSKeyAccessGroups); 275 } 276 return access_groups; 277} 278 279CFDataRef SecAccessControlCopyData(SecAccessControlRef access_control) { 280 size_t len = der_sizeof_plist(access_control->dict, NULL); 281 CFMutableDataRef encoded = CFDataCreateMutable(0, len); 282 CFDataSetLength(encoded, len); 283 uint8_t *der_end = CFDataGetMutableBytePtr(encoded); 284 const uint8_t *der = der_end; 285 der_end += len; 286 der_end = der_encode_plist(access_control->dict, NULL, der, der_end); 287 if (!der_end) { 288 CFReleaseNull(encoded); 289 } 290 return encoded; 291} 292 293SecAccessControlRef SecAccessControlCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) { 294 SecAccessControlRef access_control; 295 require_quiet(access_control = SecAccessControlCreate(allocator, error), errOut); 296 297 CFPropertyListRef plist; 298 const uint8_t *der = CFDataGetBytePtr(data); 299 const uint8_t *der_end = der + CFDataGetLength(data); 300 require_quiet(der = der_decode_plist(0, kCFPropertyListMutableContainers, &plist, error, der, der_end), errOut); 301 if (der != der_end) { 302 SecError(errSecDecode, error, CFSTR("trailing garbage at end of SecAccessControl data")); 303 goto errOut; 304 } 305 306 CFReleaseSafe(access_control->dict); 307 access_control->dict = (CFMutableDictionaryRef)plist; 308 return access_control; 309 310errOut: 311 CFReleaseSafe(access_control); 312 return NULL; 313} 314