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 (CFDictionaryGetTypeID() == CFGetTypeID(protection
)) {
110 CFStringRef protectionStr
= createStringForOps(protection
);
111 CFStringAppend(line
, protectionStr
);
112 CFRelease(protectionStr
);
113 } else if (CFStringGetTypeID() == CFGetTypeID(protection
))
114 CFStringAppend(line
, protection
);
116 CFStringAppend(line
, CFSTR("??"));
118 CFDictionaryRef constraints
= SecAccessControlGetConstraints(sac
);
119 CFStringRef constraintsString
= createStringForOps(constraints
);
120 if (constraintsString
) {
121 CFStringAppend(line
, CFSTR(";"));
122 CFStringAppend(line
, constraintsString
);
124 CFReleaseSafe(constraintsString
);
128 keychain_query_parse_cstring(CFMutableDictionaryRef q
, const char *query
) {
130 s
= CFStringCreateWithCStringNoCopy(0, query
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
131 bool result
= keychain_query_parse_string(q
, s
);
136 /* Parse a string of the form attr=value,attr=value,attr=value */
138 keychain_query_parse_string(CFMutableDictionaryRef q
, CFStringRef s
) {
140 bool escaped
= false;
142 CFStringRef key
= NULL
;
143 CFMutableStringRef str
= CFStringCreateMutable(0, 0);
144 CFRange rng
= { .location
= 0, .length
= CFStringGetLength(s
) };
145 CFCharacterSetRef cs_key
= CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
146 CFCharacterSetRef cs_value
= CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
150 bool complete
= false;
152 r
.location
= rng
.location
;
154 sub
= CFStringCreateWithSubstring(0, s
, r
);
156 } else if (CFStringFindCharacterFromSet(s
, inkey
? cs_key
: cs_value
, rng
, 0, &r
)) {
157 if (CFStringGetCharacterAtIndex(s
, r
.location
) == '\\') {
162 CFIndex next
= r
.location
+ 1;
163 r
.length
= r
.location
- rng
.location
;
164 r
.location
= rng
.location
;
165 sub
= CFStringCreateWithSubstring(0, s
, r
);
166 rng
.length
-= next
- rng
.location
;
169 sub
= CFStringCreateWithSubstring(0, s
, rng
);
170 rng
.location
+= rng
.length
;
174 CFStringAppend(str
, sub
);
178 CFStringRef value
= CFStringCreateCopy(0, str
);
179 CFStringReplaceAll(str
, CFSTR(""));
183 if(key
&& CFStringCompare(key
, kSecAttrAccessControl
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
184 SecAccessControlRef sac
= keychain_query_parse_sac(value
);
186 CFDictionarySetValue(q
, key
, sac
);
188 fprintf(stderr
, "SecItemCopyMatching returned unexpected results:");
192 CFDictionarySetValue(q
, key
, value
);
194 CFReleaseNull(value
);
203 /* Dangeling key value is true?. */
204 CFDictionarySetValue(q
, key
, kCFBooleanTrue
);
210 return error
== false;
213 static uint32_t findLeft(CFStringRef s
, uint32_t off
)
215 for (int i
= off
; i
< CFStringGetLength(s
); ++i
) {
216 if (CFStringGetCharacterAtIndex(s
, i
) == '(')
223 static uint32_t findRight(CFStringRef s
, uint32_t off
)
225 int bracersCount
= 0;
226 for (int i
= off
; i
< CFStringGetLength(s
); ++i
) {
227 if (CFStringGetCharacterAtIndex(s
, i
) == '(')
230 if (CFStringGetCharacterAtIndex(s
, i
) == ')') {
232 if (bracersCount
== 0) {
241 static bool parseKeyAndValue(CFStringRef string
, CFTypeRef
*key
, CFTypeRef
*value
);
244 static CFTypeRef
parseValue(CFStringRef string
)
246 CFTypeRef result
= NULL
, key
= NULL
, value
= NULL
;
247 CFMutableDictionaryRef resultDictionary
= NULL
;
248 CFStringRef subString
= NULL
;
250 uint32_t left
= findLeft(string
, 0);
253 resultDictionary
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
255 uint32_t right
= findRight(string
, left
);
258 CFAssignRetained(subString
, CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(offset
, (right
+ 1) - offset
)));
259 require_quiet(parseKeyAndValue(subString
, &key
, &value
), out
);
260 CFDictionarySetValue(resultDictionary
, key
, value
);
262 left
= findLeft(string
, offset
);
266 result
= CFRetain(resultDictionary
);
267 } else if (CFStringGetCharacterAtIndex(string
, 0) == '<' && CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
) - 1) == '>') {
268 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
269 if (CFStringGetLength(string
) > 2) {
270 CFAssignRetained(subString
, CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(1, CFStringGetLength(string
) - 2)));
271 const char *asciiString
= CFStringGetCStringPtr(subString
, kCFStringEncodingASCII
);
273 for(uint32_t i
= 0; i
< strlen(asciiString
); i
+= 2) {
274 sscanf(&asciiString
[i
], "%02hhx", &byte
);
275 CFDataAppendBytes(data
, &byte
, sizeof(byte
));
279 } else if (CFStringCompare(string
, CFSTR("true"), 0) == kCFCompareEqualTo
) {
280 CFRetainAssign(result
, kCFBooleanTrue
);
281 } else if (CFStringCompare(string
, CFSTR("false"), 0) == kCFCompareEqualTo
) {
282 CFRetainAssign(result
, kCFBooleanFalse
);
283 } else if (CFStringCompare(string
, CFSTR(kACMPolicyDeviceOwnerAuthentication
), 0) == kCFCompareEqualTo
) {
284 CFRetainAssign(result
, CFSTR(kACMPolicyDeviceOwnerAuthentication
));
286 CFLocaleRef currentLocale
= CFLocaleCopyCurrent();
287 CFNumberFormatterRef formaterRef
= CFNumberFormatterCreate(kCFAllocatorDefault
, currentLocale
, kCFNumberFormatterDecimalStyle
);
288 result
= CFNumberFormatterCreateNumberFromString(kCFAllocatorDefault
, formaterRef
, string
, NULL
, kCFNumberFormatterParseIntegersOnly
);
289 CFReleaseSafe(currentLocale
);
290 CFReleaseSafe(formaterRef
);
294 fprintf(stderr
, "Failed to parse value: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
));
298 CFReleaseSafe(value
);
299 CFReleaseSafe(subString
);
300 CFReleaseSafe(resultDictionary
);
305 static bool parseKeyAndValue(CFStringRef string
, CFTypeRef
*key
, CFTypeRef
*value
)
308 CFStringRef keyString
= NULL
;
309 CFStringRef valueString
= NULL
;
310 CFTypeRef parsedValue
= NULL
;
312 uint32_t left
= findLeft(string
, 0);
313 require_action_quiet(left
!= 0, out
, fprintf(stderr
, "Failed to find '(' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
314 uint32_t right
= findRight(string
, left
);
315 require_action_quiet(right
!= 0, out
, fprintf(stderr
, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
316 require_action_quiet(right
== ((uint32_t)CFStringGetLength(string
) - 1), out
, fprintf(stderr
, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
)));
318 keyString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(0, left
));
319 valueString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, string
, CFRangeMake(left
+ 1, right
- left
- 1));
320 require_quiet(parsedValue
= parseValue(valueString
), out
);
321 CFRetainAssign(*key
, keyString
);
322 CFRetainAssign(*value
, parsedValue
);
326 CFReleaseSafe(parsedValue
);
327 CFReleaseSafe(keyString
);
328 CFReleaseSafe(valueString
);
334 keychain_query_parse_sac(CFStringRef s
) {
335 SecAccessControlRef sac
= NULL
, result
= NULL
;
336 CFTypeRef key
= NULL
, value
= NULL
;
337 CFArrayRef tokens
= CFStringCreateArrayBySeparatingStrings(NULL
, s
, CFSTR(";"));
339 // process protection part
340 CFStringRef protection
= CFArrayGetValueAtIndex(tokens
, 0);
342 CFErrorRef error
= NULL
;
343 require_quiet(sac
= SecAccessControlCreate(kCFAllocatorDefault
, &error
), out
);
344 require_quiet(SecAccessControlSetProtection(sac
, protection
, &error
), out
);
346 CFIndex tokensCnt
= CFArrayGetCount(tokens
);
347 for(CFIndex i
= 1; i
< tokensCnt
; ++i
) {
348 require_action_quiet(parseKeyAndValue(CFArrayGetValueAtIndex(tokens
, i
), &key
, &value
), out
, fprintf(stderr
, "Error constructing SecAccessConstraint object\n") );
350 if (CFEqual(key
, CFSTR(kACMKeyAclParamRequirePasscode
)))
351 SecAccessControlSetRequirePassword(sac
, CFEqual(value
, kCFBooleanTrue
)?true:false);
353 SecAccessControlAddConstraintForOperation(sac
, key
, value
, NULL
);
356 SecAccessConstraintRef constraintForDelete
= SecAccessControlGetConstraint(sac
, kAKSKeyOpDelete
);
357 if (!constraintForDelete
) {
358 if(!SecAccessControlAddConstraintForOperation(sac
, kAKSKeyOpDelete
, kCFBooleanTrue
, &error
)) {
359 fprintf(stderr
, "adding delete operation to sac object failed \n");
363 SecAccessConstraintRef constraintForEncrypt
= SecAccessControlGetConstraint(sac
, kAKSKeyOpEncrypt
);
364 if (!constraintForEncrypt
) {
365 if(!SecAccessControlAddConstraintForOperation(sac
, kAKSKeyOpEncrypt
, kCFBooleanTrue
, &error
)) {
366 fprintf(stderr
, "adding encrypt operation to sac object failed \n");
370 CFRetainAssign(result
, sac
);
373 CFReleaseSafe(tokens
);
375 CFReleaseSafe(value
);