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