1// 2// 3// 4// 5 6 7 8#include <CoreFoundation/CoreFoundation.h> 9 10#include <Security/SecItem.h> 11#include <Security/SecItemPriv.h> 12 13#include <SecurityTool/tool_errors.h> 14#include <SecurityTool/readline.h> 15 16#include <utilities/SecCFWrappers.h> 17 18#include "SecurityCommands.h" 19 20#include "keychain_util.h" 21#include <Security/SecAccessControl.h> 22#include <Security/SecAccessControlPriv.h> 23 24// 25// Craptastic hacks. 26 27typedef uint32_t SecProtocolType; 28typedef uint32_t SecAuthenticationType; 29 30 31static CFMutableDictionaryRef 32keychain_create_query_from_string(const char *query) { 33 CFMutableDictionaryRef q; 34 35 q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 36 if (!keychain_query_parse_cstring(q, query)) { 37 CFReleaseNull(q); 38 } 39 return q; 40} 41 42static void add_key(const void *key, const void *value, void *context) { 43 CFArrayAppendValue(context, key); 44} 45 46static void display_item(const void *v_item, void *context) { 47 CFDictionaryRef item = (CFDictionaryRef)v_item; 48 CFIndex dict_count, key_ix, key_count; 49 CFMutableArrayRef keys = NULL; 50 CFIndex maxWidth = 10; /* Maybe precompute this or grab from context? */ 51 52 dict_count = CFDictionaryGetCount(item); 53 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count, 54 &kCFTypeArrayCallBacks); 55 CFDictionaryApplyFunction(item, add_key, keys); 56 key_count = CFArrayGetCount(keys); 57 CFArraySortValues(keys, CFRangeMake(0, key_count), 58 (CFComparatorFunction)CFStringCompare, 0); 59 60 for (key_ix = 0; key_ix < key_count; ++key_ix) { 61 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix); 62 CFTypeRef value = CFDictionaryGetValue(item, key); 63 CFMutableStringRef line = CFStringCreateMutable(NULL, 0); 64 65 CFStringAppend(line, key); 66 CFIndex jx; 67 for (jx = CFStringGetLength(key); 68 jx < maxWidth; ++jx) { 69 CFStringAppend(line, CFSTR(" ")); 70 } 71 CFStringAppend(line, CFSTR(" : ")); 72 if (CFStringGetTypeID() == CFGetTypeID(value)) { 73 CFStringAppend(line, (CFStringRef)value); 74 } else if (CFNumberGetTypeID() == CFGetTypeID(value)) { 75 CFNumberRef v_n = (CFNumberRef)value; 76 CFStringAppendFormat(line, NULL, CFSTR("%@"), v_n); 77 } else if (CFDateGetTypeID() == CFGetTypeID(value)) { 78 CFDateRef v_d = (CFDateRef)value; 79 CFStringAppendFormat(line, NULL, CFSTR("%@"), v_d); 80 } else if (CFDataGetTypeID() == CFGetTypeID(value)) { 81 CFDataRef v_d = (CFDataRef)value; 82 CFStringRef v_s = CFStringCreateFromExternalRepresentation( 83 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8); 84 if (v_s) { 85 CFStringAppend(line, CFSTR("/")); 86 CFStringAppend(line, v_s); 87 CFStringAppend(line, CFSTR("/ ")); 88 CFRelease(v_s); 89 } 90 const uint8_t *bytes = CFDataGetBytePtr(v_d); 91 CFIndex len = CFDataGetLength(v_d); 92 for (jx = 0; jx < len; ++jx) { 93 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]); 94 } 95 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value)) { 96 display_sac_line((SecAccessControlRef)value, line); 97 } else { 98 CFStringAppendFormat(line, NULL, CFSTR("%@"), value); 99 } 100 101 CFStringWriteToFileWithNewline(line, stdout); 102 103 CFRelease(line); 104 } 105 CFRelease(keys); 106 107 CFStringWriteToFileWithNewline(CFSTR("===="), stdout); 108 109 //CFShow(item); 110} 111 112 113static void display_results(CFTypeRef results) { 114 if (CFGetTypeID(results) == CFArrayGetTypeID()) { 115 CFArrayRef r_a = (CFArrayRef)results; 116 CFArrayApplyFunction(r_a, CFRangeMake(0, CFArrayGetCount(r_a)), 117 display_item, NULL); 118 } else if (CFGetTypeID(results) == CFArrayGetTypeID()) { 119 display_item(results, NULL); 120 } else { 121 fprintf(stderr, "SecItemCopyMatching returned unexpected results:"); 122 CFShow(results); 123 } 124} 125 126static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) { 127 OSStatus result; 128 if (do_delete) { 129 result = SecItemDelete(query); 130 if (result) { 131 sec_perror("SecItemDelete", result); 132 } 133 } else { 134 CFTypeRef results = NULL; 135 result = SecItemCopyMatching(query, &results); 136 if (result) { 137 sec_perror("SecItemCopyMatching", result); 138 } else { 139 display_results(results); 140 } 141 CFReleaseSafe(results); 142 } 143 return result; 144} 145 146static int 147do_keychain_find_or_delete_internet_password(Boolean do_delete, 148 const char *serverName, const char *securityDomain, 149 const char *accountName, const char *path, UInt16 port, 150 SecProtocolType protocol, SecAuthenticationType authenticationType, 151 Boolean get_password) 152 { 153 OSStatus result; 154 CFDictionaryRef query = NULL; 155 const void *keys[11], *values[11]; 156 CFIndex ix = 0; 157 158 keys[ix] = kSecClass; 159 values[ix++] = kSecClassInternetPassword; 160 if (serverName) { 161 keys[ix] = kSecAttrServer; 162 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName, 163 kCFStringEncodingUTF8, kCFAllocatorNull); 164 } 165 if (securityDomain) { 166 keys[ix] = kSecAttrSecurityDomain; 167 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain, 168 kCFStringEncodingUTF8, kCFAllocatorNull); 169 } 170 if (accountName) { 171 keys[ix] = kSecAttrAccount; 172 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName, 173 kCFStringEncodingUTF8, kCFAllocatorNull); 174 } 175 if (path) { 176 keys[ix] = kSecAttrPath; 177 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path, 178 kCFStringEncodingUTF8, kCFAllocatorNull); 179 } 180 if (port != 0) { 181 keys[ix] = kSecAttrPort; 182 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port); 183 } 184 if (protocol != 0) { 185 /* Protocol is a 4 char code, perhaps we should use a string rep 186 instead. */ 187 keys[ix] = kSecAttrProtocol; 188 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol); 189 } 190 if (authenticationType != 0) { 191 keys[ix] = kSecAttrAuthenticationType; 192 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, 193 &authenticationType); 194 } 195 if (get_password) { 196 /* Only ask for the data if so required. */ 197 keys[ix] = kSecReturnData; 198 values[ix++] = kCFBooleanTrue; 199 } 200 keys[ix] = kSecReturnAttributes; 201 values[ix++] = kCFBooleanTrue; 202 if (!do_delete) { 203 /* If we aren't deleting ask for all items. */ 204 keys[ix] = kSecMatchLimit; 205 values[ix++] = kSecMatchLimitAll; 206 } 207 208 query = CFDictionaryCreate(NULL, keys, values, ix, 209 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 210 result = do_find_or_delete(query, do_delete); 211 CFReleaseSafe(query); 212 213 return result; 214} 215 216static int 217parse_fourcharcode(const char *name, uint32_t *code) 218{ 219 /* @@@ Check for errors. */ 220 char *p = (char *)code; 221 strncpy(p, name, 4); 222 return 0; 223} 224 225static int 226keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv) 227{ 228 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL; 229 UInt16 port = 0; 230 SecProtocolType protocol = 0; 231 SecAuthenticationType authenticationType = 0; 232 int ch, result = 0; 233 Boolean get_password = FALSE; 234 235 while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1) 236 { 237 switch (ch) 238 { 239 case 'a': 240 accountName = optarg; 241 break; 242 case 'd': 243 securityDomain = optarg; 244 break; 245 case 'g': 246 if (do_delete) 247 return 2; 248 get_password = TRUE; 249 break; 250 case 'p': 251 path = optarg; 252 break; 253 case 'P': 254 port = atoi(optarg); 255 break; 256 case 'r': 257 result = parse_fourcharcode(optarg, &protocol); 258 if (result) 259 goto loser; 260 break; 261 case 's': 262 serverName = optarg; 263 break; 264 case 't': 265 result = parse_fourcharcode(optarg, &authenticationType); 266 if (result) 267 goto loser; 268 break; 269 case '?': 270 default: 271 return 2; /* @@@ Return 2 triggers usage message. */ 272 } 273 } 274 275 result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain, 276 accountName, path, port, protocol,authenticationType, get_password); 277 278 279loser: 280 281 return result; 282} 283 284int 285keychain_find_internet_password(int argc, char * const *argv) { 286 return keychain_find_or_delete_internet_password(0, argc, argv); 287} 288 289int 290keychain_delete_internet_password(int argc, char * const *argv) { 291 return keychain_find_or_delete_internet_password(1, argc, argv); 292} 293 294static int 295do_keychain_find_or_delete_generic_password(Boolean do_delete, 296 const char *serviceName, const char *accountName, 297 Boolean get_password) 298 { 299 OSStatus result; 300 CFDictionaryRef query = NULL; 301 const void *keys[6], *values[6]; 302 CFIndex ix = 0; 303 304 keys[ix] = kSecClass; 305 values[ix++] = kSecClassGenericPassword; 306 if (serviceName) { 307 keys[ix] = kSecAttrService; 308 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName, 309 kCFStringEncodingUTF8, kCFAllocatorNull); 310 } 311 if (accountName) { 312 keys[ix] = kSecAttrAccount; 313 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName, 314 kCFStringEncodingUTF8, kCFAllocatorNull); 315 } 316 if (get_password) { 317 /* Only ask for the data if so required. */ 318 keys[ix] = kSecReturnData; 319 values[ix++] = kCFBooleanTrue; 320 } 321 keys[ix] = kSecReturnAttributes; 322 values[ix++] = kCFBooleanTrue; 323 if (!do_delete) { 324 /* If we aren't deleting ask for all items. */ 325 keys[ix] = kSecMatchLimit; 326 values[ix++] = kSecMatchLimitAll; 327 } 328 329 query = CFDictionaryCreate(NULL, keys, values, ix, 330 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 331 332 result = do_find_or_delete(query, do_delete); 333 334 CFReleaseSafe(query); 335 336 return result; 337} 338 339int keychain_item(int argc, char * const *argv) { 340 int ch, result = 0; 341 CFMutableDictionaryRef query, update = NULL; 342 bool get_password = false; 343 bool do_delete = false; 344 bool do_add = false; 345 bool verbose = false; 346 int limit = 0; 347 348 query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 349 while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1) 350 { 351 switch (ch) 352 { 353 case 'a': 354 do_add = true; 355 break; 356 case 'D': 357 do_delete = true; 358 break; 359 case 'd': 360 { 361 CFStringRef data = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8); 362 if (data) { 363 CFDictionarySetValue(update ? update : query, kSecValueData, data); 364 CFRelease(data); 365 } else { 366 result = 1; 367 goto out; 368 } 369 break; 370 } 371 case 'f': 372 { 373 CFDataRef data = copyFileContents(optarg); 374 CFDictionarySetValue(update ? update : query, kSecValueData, data); 375 CFRelease(data); 376 break; 377 } 378 case 'g': 379 get_password = true; 380 break; 381 case 'q': 382 if (!keychain_query_parse_cstring(query, optarg)) { 383 result = 1; 384 goto out; 385 } 386 break; 387 case 'u': 388 { 389 bool success = true; 390 if (!update) 391 update = keychain_create_query_from_string(optarg); 392 else 393 success = keychain_query_parse_cstring(update, optarg); 394 if (update == NULL || !success) { 395 result = 1; 396 goto out; 397 } 398 } 399 break; 400 case 'v': 401 verbose = true; 402 break; 403 case 'l': 404 limit = atoi(optarg); 405 break; 406 case '?': 407 default: 408 /* Return 2 triggers usage message. */ 409 result = 2; 410 goto out; 411 } 412 } 413 414 if (((do_add || do_delete) && (get_password || update)) || !query) { 415 result = 2; 416 goto out; 417 } 418 419 argc -= optind; 420 argv += optind; 421 422 int ix; 423 for (ix = 0; ix < argc; ++ix) { 424 if (!keychain_query_parse_cstring(query, argv[ix])) { 425 result = 1; 426 goto out; 427 } 428 } 429 430 if (!update && !do_add && !do_delete) { 431 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue); 432 if(limit) { 433 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit); 434 CFDictionarySetValue(query, kSecMatchLimit, cfLimit); 435 CFReleaseSafe(cfLimit); 436 } else { 437 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); 438 } 439 if (get_password) 440 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); 441 } 442 443 if (verbose) 444 CFShow(query); 445 446 OSStatus error; 447 if (do_add) { 448 error = SecItemAdd(query, NULL); 449 if (error) { 450 sec_perror("SecItemAdd", error); 451 result = 1; 452 } 453 } else if (update) { 454 error = SecItemUpdate(query, update); 455 if (error) { 456 sec_perror("SecItemUpdate", error); 457 result = 1; 458 } 459 } else if (do_delete) { 460 error = SecItemDelete(query); 461 if (error) { 462 sec_perror("SecItemDelete", error); 463 result = 1; 464 } 465 } else { 466 do_find_or_delete(query, do_delete); 467 } 468 469out: 470 CFReleaseSafe(query); 471 CFReleaseSafe(update); 472 return result; 473} 474 475static int 476keychain_find_or_delete_generic_password(Boolean do_delete, 477 int argc, char * const *argv) 478{ 479 char *serviceName = NULL, *accountName = NULL; 480 int ch, result = 0; 481 Boolean get_password = FALSE; 482 483 while ((ch = getopt(argc, argv, "a:s:g")) != -1) 484 { 485 switch (ch) 486 { 487 case 'a': 488 accountName = optarg; 489 break; 490 case 'g': 491 if (do_delete) 492 return 2; 493 get_password = TRUE; 494 break; 495 case 's': 496 serviceName = optarg; 497 break; 498 case '?': 499 default: 500 return 2; /* @@@ Return 2 triggers usage message. */ 501 } 502 } 503 504 result = do_keychain_find_or_delete_generic_password(do_delete, 505 serviceName, accountName, get_password); 506 507 return result; 508} 509 510int 511keychain_find_generic_password(int argc, char * const *argv) { 512 return keychain_find_or_delete_generic_password(0, argc, argv); 513} 514 515int 516keychain_delete_generic_password(int argc, char * const *argv) { 517 return keychain_find_or_delete_generic_password(1, argc, argv); 518} 519 520#if TARGET_OS_EMBEDDED 521 522int 523keychain_roll_keys(int argc, char * const *argv) { 524 int ch, result = 0; 525 bool force = false; 526 527 while ((ch = getopt(argc, argv, "f")) != -1) 528 { 529 switch (ch) 530 { 531 case 'f': 532 force = true; 533 break; 534 default: 535 return 2; 536 } 537 } 538 // argc -= optind; 539 // argv += optind; 540 541 (void) argc; // These are set so folks could use them 542 (void) argv; // silence the analyzer since they're not used 543 544 CFErrorRef error = NULL; 545 bool ok = _SecKeychainRollKeys(force, &error); 546 547 fprintf(stderr, "Keychain keys up to date: %s\n", ok ? "yes" : "no"); 548 if (!ok && error) { 549 result = (int)CFErrorGetCode(error); 550 CFShow(error); 551 } 552 553 return result; 554} 555 556#endif 557