]>
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 | ||
25 | #include <stdio.h> | |
26 | #include <AssertMacros.h> | |
27 | #include <utilities/SecCFWrappers.h> | |
28 | #include <ACMDefs.h> | |
29 | #include <ACMAclDefs.h> | |
30 | #include <libaks_acl_cf_keys.h> | |
31 | ||
32 | #include "security.h" | |
33 | #include "keychain_util.h" | |
34 | #include "SecAccessControlPriv.h" | |
35 | ||
36 | static CFStringRef createStringForValue(CFTypeRef value) | |
37 | { | |
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]); | |
45 | } | |
46 | CFStringAppend(stringData, CFSTR(">")); | |
47 | result = stringData; | |
48 | } else if (CFBooleanGetTypeID() == CFGetTypeID(value)) { | |
49 | if (CFEqual(value, kCFBooleanTrue)) | |
50 | result = CFStringCreateWithCString(kCFAllocatorDefault, "true", kCFStringEncodingUTF8); | |
51 | else | |
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); | |
57 | else | |
58 | result = CFStringCreateWithCString(kCFAllocatorDefault, "unrecognized value", kCFStringEncodingUTF8); | |
59 | ||
60 | return result; | |
61 | } | |
62 | ||
63 | static CFStringRef createStringForKofn(CFDictionaryRef kofn) | |
64 | { | |
65 | CFMutableStringRef result = NULL; | |
66 | if (kofn != NULL && CFDictionaryGetTypeID() == CFGetTypeID(kofn)) | |
67 | { | |
68 | result = CFStringCreateMutable(kCFAllocatorDefault, 0); | |
69 | CFDictionaryForEach(kofn, ^(const void *key, const void *value) { | |
70 | ||
71 | CFStringAppend(result, key); | |
72 | CFStringAppend(result, CFSTR("(")); | |
73 | ||
74 | CFStringRef valueString = createStringForKofn(value) ?: createStringForValue(value); | |
75 | CFStringAppend(result, valueString); | |
76 | CFStringAppend(result, CFSTR(")")); | |
77 | CFReleaseSafe(valueString); | |
78 | }); | |
79 | } | |
80 | ||
81 | return result; | |
82 | } | |
83 | ||
84 | static CFStringRef createStringForOps(CFDictionaryRef constraints) | |
85 | { | |
86 | CFMutableStringRef result = NULL; | |
87 | if (constraints != NULL) | |
88 | { | |
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); | |
99 | }); | |
100 | } | |
101 | ||
102 | return result; | |
103 | } | |
104 | ||
105 | void | |
106 | display_sac_line(SecAccessControlRef sac, CFMutableStringRef line) | |
107 | { | |
108 | CFTypeRef protection = SecAccessControlGetProtection(sac); | |
109 | if (CFStringGetTypeID() == CFGetTypeID(protection)) | |
110 | CFStringAppend(line, protection); | |
111 | ||
112 | CFDictionaryRef constraints = SecAccessControlGetConstraints(sac); | |
113 | CFStringRef constraintsString = createStringForOps(constraints); | |
114 | if (constraintsString) { | |
115 | CFStringAppend(line, CFSTR(";")); | |
116 | CFStringAppend(line, constraintsString); | |
117 | } | |
118 | CFReleaseSafe(constraintsString); | |
119 | } | |
120 | ||
121 | bool | |
122 | keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) { | |
123 | CFStringRef s; | |
124 | s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull); | |
125 | bool result = keychain_query_parse_string(q, s); | |
126 | CFRelease(s); | |
127 | return result; | |
128 | } | |
129 | ||
130 | /* Parse a string of the form attr=value,attr=value,attr=value */ | |
131 | bool | |
132 | keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) { | |
133 | bool inkey = true; | |
134 | bool escaped = false; | |
135 | bool error = 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(",\\")); | |
141 | while (rng.length) { | |
142 | CFRange r; | |
143 | CFStringRef sub; | |
144 | bool complete = false; | |
145 | if (escaped) { | |
146 | r.location = rng.location; | |
147 | r.length = 1; | |
148 | sub = CFStringCreateWithSubstring(0, s, r); | |
149 | escaped = false; | |
150 | } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) { | |
151 | if (CFStringGetCharacterAtIndex(s, r.location) == '\\') { | |
152 | escaped = true; | |
153 | } else { | |
154 | complete = true; | |
155 | } | |
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; | |
161 | rng.location = next; | |
162 | } else { | |
163 | sub = CFStringCreateWithSubstring(0, s, rng); | |
164 | rng.location += rng.length; | |
165 | rng.length = 0; | |
166 | complete = true; | |
167 | } | |
168 | CFStringAppend(str, sub); | |
169 | CFRelease(sub); | |
170 | ||
171 | if (complete) { | |
172 | CFStringRef value = CFStringCreateCopy(0, str); | |
173 | CFStringReplaceAll(str, CFSTR("")); | |
174 | if (inkey) { | |
175 | key = value; | |
176 | } else { | |
177 | if(key && CFStringCompare(key, kSecAttrAccessControl, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { | |
178 | SecAccessControlRef sac = keychain_query_parse_sac(value); | |
179 | if(sac) { | |
180 | CFDictionarySetValue(q, key, sac); | |
181 | } else { | |
182 | fprintf(stderr, "SecItemCopyMatching returned unexpected results:"); | |
183 | error = true; | |
184 | } | |
185 | } else { | |
186 | CFDictionarySetValue(q, key, value); | |
187 | } | |
188 | CFReleaseNull(value); | |
189 | CFReleaseNull(key); | |
190 | } | |
191 | inkey = !inkey; | |
192 | } | |
193 | if(error) | |
194 | break; | |
195 | } | |
196 | if (key) { | |
197 | /* Dangeling key value is true?. */ | |
198 | CFDictionarySetValue(q, key, kCFBooleanTrue); | |
199 | CFReleaseNull(key); | |
200 | } | |
201 | CFRelease(str); | |
202 | CFRelease(cs_key); | |
203 | CFRelease(cs_value); | |
204 | return error == false; | |
205 | } | |
206 | ||
207 | static uint32_t findLeft(CFStringRef s, uint32_t off) | |
208 | { | |
209 | for (int i = off; i < CFStringGetLength(s); ++i) { | |
210 | if (CFStringGetCharacterAtIndex(s, i) == '(') | |
211 | return i; | |
212 | } | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | static uint32_t findRight(CFStringRef s, uint32_t off) | |
218 | { | |
219 | int bracersCount = 0; | |
220 | for (int i = off; i < CFStringGetLength(s); ++i) { | |
221 | if (CFStringGetCharacterAtIndex(s, i) == '(') | |
222 | ++bracersCount; | |
223 | ||
224 | if (CFStringGetCharacterAtIndex(s, i) == ')') { | |
225 | --bracersCount; | |
226 | if (bracersCount == 0) { | |
227 | return i; | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value); | |
236 | ||
237 | CF_RETURNS_RETAINED | |
238 | static CFTypeRef parseValue(CFStringRef string) | |
239 | { | |
240 | CFTypeRef result = NULL, key = NULL, value = NULL; | |
241 | CFMutableDictionaryRef resultDictionary = NULL; | |
242 | CFStringRef subString = NULL; | |
243 | ||
244 | uint32_t left = findLeft(string, 0); | |
245 | if (left > 0) { | |
246 | uint32_t offset = 0; | |
247 | resultDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
248 | for (;;) { | |
249 | uint32_t right = findRight(string, left); | |
250 | if (!right) | |
251 | break; | |
252 | CFAssignRetained(subString, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(offset, (right + 1) - offset))); | |
253 | require_quiet(parseKeyAndValue(subString, &key, &value), out); | |
254 | CFDictionarySetValue(resultDictionary, key, value); | |
255 | offset = right + 1; | |
256 | left = findLeft(string, offset); | |
257 | if (!left) | |
258 | break; | |
259 | } | |
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); | |
266 | uint8_t byte; | |
267 | for(uint32_t i = 0; i < strlen(asciiString); i += 2) { | |
268 | sscanf(&asciiString[i], "%02hhx", &byte); | |
269 | CFDataAppendBytes(data, &byte, sizeof(byte)); | |
270 | } | |
271 | } | |
272 | result = data; | |
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)); | |
279 | } else { | |
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); | |
285 | } | |
286 | ||
287 | if (!result) | |
288 | fprintf(stderr, "Failed to parse value: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8)); | |
289 | ||
290 | out: | |
291 | CFReleaseSafe(key); | |
292 | CFReleaseSafe(value); | |
293 | CFReleaseSafe(subString); | |
294 | CFReleaseSafe(resultDictionary); | |
295 | ||
296 | return result; | |
297 | } | |
298 | ||
299 | static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value) | |
300 | { | |
301 | bool ok = false; | |
302 | CFStringRef keyString = NULL; | |
303 | CFStringRef valueString = NULL; | |
304 | CFTypeRef parsedValue = NULL; | |
305 | ||
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))); | |
311 | ||
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); | |
317 | ok = true; | |
318 | ||
319 | out: | |
320 | CFReleaseSafe(parsedValue); | |
321 | CFReleaseSafe(keyString); | |
322 | CFReleaseSafe(valueString); | |
323 | ||
324 | return ok; | |
325 | } | |
326 | ||
327 | SecAccessControlRef | |
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(";")); | |
332 | ||
333 | // process protection part | |
334 | CFStringRef protection = CFArrayGetValueAtIndex(tokens, 0); | |
335 | ||
336 | CFErrorRef error = NULL; | |
337 | require_quiet(sac = SecAccessControlCreate(kCFAllocatorDefault, &error), out); | |
338 | require_quiet(SecAccessControlSetProtection(sac, protection, &error), out); | |
339 | ||
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") ); | |
343 | ||
344 | if (CFEqual(key, CFSTR(kACMKeyAclParamRequirePasscode))) | |
345 | SecAccessControlSetRequirePassword(sac, CFEqual(value, kCFBooleanTrue)?true:false); | |
346 | else | |
347 | SecAccessControlAddConstraintForOperation(sac, key, value, NULL); | |
348 | } | |
349 | ||
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"); | |
354 | } | |
355 | } | |
356 | ||
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"); | |
361 | } | |
362 | } | |
363 | ||
364 | CFRetainAssign(result, sac); | |
365 | ||
366 | out: | |
367 | CFReleaseSafe(tokens); | |
368 | CFReleaseSafe(key); | |
369 | CFReleaseSafe(value); | |
370 | CFReleaseSafe(sac); | |
371 | ||
372 | return result; | |
373 | } |