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