]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/keychain_util.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / Security / Tool / keychain_util.c
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 }