]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/keychain_util.c
Security-58286.70.7.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 (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);
115 else
116 CFStringAppend(line, CFSTR("??"));
117
118 CFDictionaryRef constraints = SecAccessControlGetConstraints(sac);
119 CFStringRef constraintsString = createStringForOps(constraints);
120 if (constraintsString) {
121 CFStringAppend(line, CFSTR(";"));
122 CFStringAppend(line, constraintsString);
123 }
124 CFReleaseSafe(constraintsString);
125 }
126
127 bool
128 keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
129 CFStringRef s;
130 s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
131 bool result = keychain_query_parse_string(q, s);
132 CFRelease(s);
133 return result;
134 }
135
136 /* Parse a string of the form attr=value,attr=value,attr=value */
137 bool
138 keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
139 bool inkey = true;
140 bool escaped = false;
141 bool error = 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(",\\"));
147 while (rng.length) {
148 CFRange r;
149 CFStringRef sub;
150 bool complete = false;
151 if (escaped) {
152 r.location = rng.location;
153 r.length = 1;
154 sub = CFStringCreateWithSubstring(0, s, r);
155 escaped = false;
156 } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
157 if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
158 escaped = true;
159 } else {
160 complete = true;
161 }
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;
167 rng.location = next;
168 } else {
169 sub = CFStringCreateWithSubstring(0, s, rng);
170 rng.location += rng.length;
171 rng.length = 0;
172 complete = true;
173 }
174 CFStringAppend(str, sub);
175 CFRelease(sub);
176
177 if (complete) {
178 CFStringRef value = CFStringCreateCopy(0, str);
179 CFStringReplaceAll(str, CFSTR(""));
180 if (inkey) {
181 key = value;
182 } else {
183 if(key && CFStringCompare(key, kSecAttrAccessControl, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
184 SecAccessControlRef sac = keychain_query_parse_sac(value);
185 if(sac) {
186 CFDictionarySetValue(q, key, sac);
187 } else {
188 fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
189 error = true;
190 }
191 } else {
192 CFDictionarySetValue(q, key, value);
193 }
194 CFReleaseNull(value);
195 CFReleaseNull(key);
196 }
197 inkey = !inkey;
198 }
199 if(error)
200 break;
201 }
202 if (key) {
203 /* Dangeling key value is true?. */
204 CFDictionarySetValue(q, key, kCFBooleanTrue);
205 CFReleaseNull(key);
206 }
207 CFRelease(str);
208 CFRelease(cs_key);
209 CFRelease(cs_value);
210 return error == false;
211 }
212
213 static uint32_t findLeft(CFStringRef s, uint32_t off)
214 {
215 for (int i = off; i < CFStringGetLength(s); ++i) {
216 if (CFStringGetCharacterAtIndex(s, i) == '(')
217 return i;
218 }
219
220 return 0;
221 }
222
223 static uint32_t findRight(CFStringRef s, uint32_t off)
224 {
225 int bracersCount = 0;
226 for (int i = off; i < CFStringGetLength(s); ++i) {
227 if (CFStringGetCharacterAtIndex(s, i) == '(')
228 ++bracersCount;
229
230 if (CFStringGetCharacterAtIndex(s, i) == ')') {
231 --bracersCount;
232 if (bracersCount == 0) {
233 return i;
234 }
235 }
236 }
237
238 return 0;
239 }
240
241 static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value);
242
243 CF_RETURNS_RETAINED
244 static CFTypeRef parseValue(CFStringRef string)
245 {
246 CFTypeRef result = NULL, key = NULL, value = NULL;
247 CFMutableDictionaryRef resultDictionary = NULL;
248 CFStringRef subString = NULL;
249
250 uint32_t left = findLeft(string, 0);
251 if (left > 0) {
252 uint32_t offset = 0;
253 resultDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
254 for (;;) {
255 uint32_t right = findRight(string, left);
256 if (!right)
257 break;
258 CFAssignRetained(subString, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(offset, (right + 1) - offset)));
259 require_quiet(parseKeyAndValue(subString, &key, &value), out);
260 CFDictionarySetValue(resultDictionary, key, value);
261 offset = right + 1;
262 left = findLeft(string, offset);
263 if (!left)
264 break;
265 }
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);
272 uint8_t byte;
273 for(uint32_t i = 0; i < strlen(asciiString); i += 2) {
274 sscanf(&asciiString[i], "%02hhx", &byte);
275 CFDataAppendBytes(data, &byte, sizeof(byte));
276 }
277 }
278 result = data;
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));
285 } else {
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);
291 }
292
293 if (!result)
294 fprintf(stderr, "Failed to parse value: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8));
295
296 out:
297 CFReleaseSafe(key);
298 CFReleaseSafe(value);
299 CFReleaseSafe(subString);
300 CFReleaseSafe(resultDictionary);
301
302 return result;
303 }
304
305 static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value)
306 {
307 bool ok = false;
308 CFStringRef keyString = NULL;
309 CFStringRef valueString = NULL;
310 CFTypeRef parsedValue = NULL;
311
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)));
317
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);
323 ok = true;
324
325 out:
326 CFReleaseSafe(parsedValue);
327 CFReleaseSafe(keyString);
328 CFReleaseSafe(valueString);
329
330 return ok;
331 }
332
333 SecAccessControlRef
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(";"));
338
339 // process protection part
340 CFStringRef protection = CFArrayGetValueAtIndex(tokens, 0);
341
342 CFErrorRef error = NULL;
343 require_quiet(sac = SecAccessControlCreate(kCFAllocatorDefault, &error), out);
344 require_quiet(SecAccessControlSetProtection(sac, protection, &error), out);
345
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") );
349
350 if (CFEqual(key, CFSTR(kACMKeyAclParamRequirePasscode)))
351 SecAccessControlSetRequirePassword(sac, CFEqual(value, kCFBooleanTrue)?true:false);
352 else
353 SecAccessControlAddConstraintForOperation(sac, key, value, NULL);
354 }
355
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");
360 }
361 }
362
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");
367 }
368 }
369
370 CFRetainAssign(result, sac);
371
372 out:
373 CFReleaseSafe(tokens);
374 CFReleaseSafe(key);
375 CFReleaseSafe(value);
376 CFReleaseSafe(sac);
377
378 return result;
379 }