2 * Copyright (c) 2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <AssertMacros.h>
27 #include <utilities/SecCFWrappers.h>
29 #include <ACMAclDefs.h>
30 #include <libaks_acl_cf_keys.h>
33 #include "keychain_util.h"
34 #include "SecAccessControlPriv.h"
36 static CFStringRef
createStringForValue(CFTypeRef value
)
38 CFStringRef result
= NULL
;
39 if (CFDataGetTypeID() == CFGetTypeID(value
)) {
40 CFMutableStringRef stringData
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
41 CFStringAppend(stringData
, CFSTR("<"));
42 const UInt8
*dataPtr
= CFDataGetBytePtr(value
);
43 for (int i
= 0; i
< CFDataGetLength(value
); ++i
) {
44 CFStringAppendFormat(stringData
, NULL
, CFSTR("%02x"), dataPtr
[i
]);
46 CFStringAppend(stringData
, CFSTR(">"));
48 } else if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
49 if (CFEqual(value
, kCFBooleanTrue
))
50 result
= CFStringCreateWithCString(kCFAllocatorDefault
, "true", kCFStringEncodingUTF8
);
52 result
= CFStringCreateWithCString(kCFAllocatorDefault
, "false", kCFStringEncodingUTF8
);
53 } else if (CFNumberGetTypeID() == CFGetTypeID(value
))
54 result
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), value
);
55 else if (CFStringGetTypeID() == CFGetTypeID(value
))
56 result
= CFStringCreateCopy(kCFAllocatorDefault
, value
);
58 result
= CFStringCreateWithCString(kCFAllocatorDefault
, "unrecognized value", kCFStringEncodingUTF8
);
63 static CFStringRef
createStringForKofn(CFDictionaryRef kofn
)
65 CFMutableStringRef result
= NULL
;
66 if (kofn
!= NULL
&& CFDictionaryGetTypeID() == CFGetTypeID(kofn
))
68 result
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
69 CFDictionaryForEach(kofn
, ^(const void *key
, const void *value
) {
71 CFStringAppend(result
, key
);
72 CFStringAppend(result
, CFSTR("("));
74 CFStringRef valueString
= createStringForKofn(value
) ?: createStringForValue(value
);
75 CFStringAppend(result
, valueString
);
76 CFStringAppend(result
, CFSTR(")"));
77 CFReleaseSafe(valueString
);
84 static CFStringRef
createStringForOps(CFDictionaryRef constraints
)
86 CFMutableStringRef result
= NULL
;
87 if (constraints
!= NULL
)
89 result
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
90 CFDictionaryForEach(constraints
, ^(const void *key
, const void *value
) {
91 if (CFStringGetLength(result
) > 0)
92 CFStringAppend(result
, CFSTR(";"));
93 CFStringAppend(result
, key
);
94 CFStringAppend(result
, CFSTR("("));
95 CFStringRef valueString
= createStringForKofn(value
) ?: createStringForValue(value
);
96 CFStringAppend(result
, valueString
);
97 CFStringAppend(result
, CFSTR(")"));
98 CFReleaseSafe(valueString
);
106 display_sac_line(SecAccessControlRef sac
, CFMutableStringRef line
)
108 CFTypeRef protection
= SecAccessControlGetProtection(sac
);
109 if (CFStringGetTypeID() == CFGetTypeID(protection
))
110 CFStringAppend(line
, protection
);
112 CFDictionaryRef constraints
= SecAccessControlGetConstraints(sac
);
113 CFStringRef constraintsString
= createStringForOps(constraints
);
114 if (constraintsString
) {
115 CFStringAppend(line
, CFSTR(";"));
116 CFStringAppend(line
, constraintsString
);
118 CFReleaseSafe(constraintsString
);
122 keychain_query_parse_cstring(CFMutableDictionaryRef q
, const char *query
) {
124 s
= CFStringCreateWithCStringNoCopy(0, query
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
125 bool result
= keychain_query_parse_string(q
, s
);
130 /* Parse a string of the form attr=value,attr=value,attr=value */
132 keychain_query_parse_string(CFMutableDictionaryRef q
, CFStringRef s
) {
134 bool escaped
= false;
136 CFStringRef key
= NULL
;
137 CFMutableStringRef str
= CFStringCreateMutable(0, 0);
138 CFRange rng
= { .location
= 0, .length
= CFStringGetLength(s
) };
139 CFCharacterSetRef cs_key
= CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
140 CFCharacterSetRef cs_value
= CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
144 bool complete
= false;
146 r
.location
= rng
.location
;
148 sub
= CFStringCreateWithSubstring(0, s
, r
);
150 } else if (CFStringFindCharacterFromSet(s
, inkey
? cs_key
: cs_value
, rng
, 0, &r
)) {
151 if (CFStringGetCharacterAtIndex(s
, r
.location
) == '\\') {
156 CFIndex next
= r
.location
+ 1;
157 r
.length
= r
.location
- rng
.location
;
158 r
.location
= rng
.location
;
159 sub
= CFStringCreateWithSubstring(0, s
, r
);
160 rng
.length
-= next
- rng
.location
;
163 sub
= CFStringCreateWithSubstring(0, s
, rng
);
164 rng
.location
+= rng
.length
;
168 CFStringAppend(str
, sub
);
172 CFStringRef value
= CFStringCreateCopy(0, str
);
173 CFStringReplaceAll(str
, CFSTR(""));
177 if(key
&& CFStringCompare(key
, kSecAttrAccessControl
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
178 SecAccessControlRef sac
= keychain_query_parse_sac(value
);
180 CFDictionarySetValue(q
, key
, sac
);
182 fprintf(stderr
, "SecItemCopyMatching returned unexpected results:");
186 CFDictionarySetValue(q
, key
, value
);
188 CFReleaseNull(value
);
197 /* Dangeling key value is true?. */
198 CFDictionarySetValue(q
, key
, kCFBooleanTrue
);
204 return error
== false;
207 static uint32_t findLeft(CFStringRef s
, uint32_t off
)
209 for (int i
= off
; i
< CFStringGetLength(s
); ++i
) {
210 if (CFStringGetCharacterAtIndex(s
, i
) == '(')
217 static uint32_t findRight(CFStringRef s
, uint32_t off
)
219 int bracersCount
= 0;
220 for (int i
= off
; i
< CFStringGetLength(s
); ++i
) {
221 if (CFStringGetCharacterAtIndex(s
, i
) == '(')
224 if (CFStringGetCharacterAtIndex(s
, i
) == ')') {
226 if (bracersCount
== 0) {
235 static bool parseKeyAndValue(CFStringRef string
, CFTypeRef
*key
, CFTypeRef
*value
);
238 static CFTypeRef
parseValue(CFStringRef string
)
240 CFTypeRef result
= NULL
, key
= NULL
, value
= NULL
;
241 CFMutableDictionaryRef resultDictionary
= NULL
;
242 CFStringRef subString
= NULL
;
244 uint32_t left
= findLeft(string
, 0);
247 resultDictionary
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
249 uint32_t right
= findRight(string
, left
);
252 CFAssignRetained(subString
, CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(offset
, (right
+ 1) - offset
)));
253 require_quiet(parseKeyAndValue(subString
, &key
, &value
), out
);
254 CFDictionarySetValue(resultDictionary
, key
, value
);
256 left
= findLeft(string
, offset
);
260 result
= CFRetain(resultDictionary
);
261 } else if (CFStringGetCharacterAtIndex(string
, 0) == '<' && CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
) - 1) == '>') {
262 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
263 if (CFStringGetLength(string
) > 2) {
264 CFAssignRetained(subString
, CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(1, CFStringGetLength(string
) - 2)));
265 const char *asciiString
= CFStringGetCStringPtr(subString
, kCFStringEncodingASCII
);
267 for(uint32_t i
= 0; i
< strlen(asciiString
); i
+= 2) {
268 sscanf(&asciiString
[i
], "%02hhx", &byte
);
269 CFDataAppendBytes(data
, &byte
, sizeof(byte
));
273 } else if (CFStringCompare(string
, CFSTR("true"), 0) == kCFCompareEqualTo
) {
274 CFRetainAssign(result
, kCFBooleanTrue
);
275 } else if (CFStringCompare(string
, CFSTR("false"), 0) == kCFCompareEqualTo
) {
276 CFRetainAssign(result
, kCFBooleanFalse
);
277 } else if (CFStringCompare(string
, CFSTR(kACMPolicyDeviceOwnerAuthentication
), 0) == kCFCompareEqualTo
) {
278 CFRetainAssign(result
, CFSTR(kACMPolicyDeviceOwnerAuthentication
));
280 CFLocaleRef currentLocale
= CFLocaleCopyCurrent();
281 CFNumberFormatterRef formaterRef
= CFNumberFormatterCreate(kCFAllocatorDefault
, currentLocale
, kCFNumberFormatterDecimalStyle
);
282 result
= CFNumberFormatterCreateNumberFromString(kCFAllocatorDefault
, formaterRef
, string
, NULL
, kCFNumberFormatterParseIntegersOnly
);
283 CFReleaseSafe(currentLocale
);
284 CFReleaseSafe(formaterRef
);
288 fprintf(stderr
, "Failed to parse value: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
));
292 CFReleaseSafe(value
);
293 CFReleaseSafe(subString
);
294 CFReleaseSafe(resultDictionary
);
299 static bool parseKeyAndValue(CFStringRef string
, CFTypeRef
*key
, CFTypeRef
*value
)
302 CFStringRef keyString
= NULL
;
303 CFStringRef valueString
= NULL
;
304 CFTypeRef parsedValue
= NULL
;
306 uint32_t left
= findLeft(string
, 0);
307 require_action_quiet(left
!= 0, out
, fprintf(stderr
, "Failed to find '(' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
308 uint32_t right
= findRight(string
, left
);
309 require_action_quiet(right
!= 0, out
, fprintf(stderr
, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
310 require_action_quiet(right
== ((uint32_t)CFStringGetLength(string
) - 1), out
, fprintf(stderr
, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
312 keyString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(0, left
));
313 valueString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(left
+ 1, right
- left
- 1));
314 require_quiet(parsedValue
= parseValue(valueString
), out
);
315 CFRetainAssign(*key
, keyString
);
316 CFRetainAssign(*value
, parsedValue
);
320 CFReleaseSafe(parsedValue
);
321 CFReleaseSafe(keyString
);
322 CFReleaseSafe(valueString
);
328 keychain_query_parse_sac(CFStringRef s
) {
329 SecAccessControlRef sac
= NULL
, result
= NULL
;
330 CFTypeRef key
= NULL
, value
= NULL
;
331 CFArrayRef tokens
= CFStringCreateArrayBySeparatingStrings(NULL
, s
, CFSTR(";"));
333 // process protection part
334 CFStringRef protection
= CFArrayGetValueAtIndex(tokens
, 0);
336 CFErrorRef error
= NULL
;
337 require_quiet(sac
= SecAccessControlCreate(kCFAllocatorDefault
, &error
), out
);
338 require_quiet(SecAccessControlSetProtection(sac
, protection
, &error
), out
);
340 CFIndex tokensCnt
= CFArrayGetCount(tokens
);
341 for(CFIndex i
= 1; i
< tokensCnt
; ++i
) {
342 require_action_quiet(parseKeyAndValue(CFArrayGetValueAtIndex(tokens
, i
), &key
, &value
), out
, fprintf(stderr
, "Error constructing SecAccessConstraint object\n") );
344 if (CFEqual(key
, CFSTR(kACMKeyAclParamRequirePasscode
)))
345 SecAccessControlSetRequirePassword(sac
, CFEqual(value
, kCFBooleanTrue
)?true:false);
347 SecAccessControlAddConstraintForOperation(sac
, key
, value
, NULL
);
350 SecAccessConstraintRef constraintForDelete
= SecAccessControlGetConstraint(sac
, kAKSKeyOpDelete
);
351 if (!constraintForDelete
) {
352 if(!SecAccessControlAddConstraintForOperation(sac
, kAKSKeyOpDelete
, kCFBooleanTrue
, &error
)) {
353 fprintf(stderr
, "adding delete operation to sac object failed \n");
357 SecAccessConstraintRef constraintForEncrypt
= SecAccessControlGetConstraint(sac
, kAKSKeyOpEncrypt
);
358 if (!constraintForEncrypt
) {
359 if(!SecAccessControlAddConstraintForOperation(sac
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, &error
)) {
360 fprintf(stderr
, "adding encrypt operation to sac object failed \n");
364 CFRetainAssign(result
, sac
);
367 CFReleaseSafe(tokens
);
369 CFReleaseSafe(value
);