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 <Security/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, "failed to parse access control\n"); 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 }