]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/keychain_find.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / Security / Tool / keychain_find.c
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) == CFArrayGetTypeID()) {
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 2;
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 2; /* @@@ Return 2 triggers 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 while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1)
388 {
389 switch (ch)
390 {
391 case 'a':
392 do_add = true;
393 break;
394 case 'D':
395 do_delete = true;
396 break;
397 case 'd':
398 {
399 CFStringRef dataString = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
400 if (dataString) {
401 CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, dataString, kCFStringEncodingUTF8, 0);
402 if (data) {
403 CFDictionarySetValue(update ? update : query, kSecValueData, data);
404 CFRelease(data);
405 }
406 CFRelease(dataString);
407 } else {
408 result = 1;
409 goto out;
410 }
411 break;
412 }
413 case 'f':
414 {
415 CFDataRef data = copyFileContents(optarg);
416 CFDictionarySetValue(update ? update : query, kSecValueData, data);
417 CFRelease(data);
418 break;
419 }
420 case 'g':
421 get_password = true;
422 break;
423 case 'q':
424 if (!keychain_query_parse_cstring(query, optarg)) {
425 result = 1;
426 goto out;
427 }
428 break;
429 case 'u':
430 {
431 bool success = true;
432 if (!update)
433 update = keychain_create_query_from_string(optarg);
434 else
435 success = keychain_query_parse_cstring(update, optarg);
436 if (update == NULL || !success) {
437 result = 1;
438 goto out;
439 }
440 }
441 break;
442 case 'v':
443 verbose = true;
444 break;
445 case 'l':
446 limit = atoi(optarg);
447 break;
448 case '?':
449 default:
450 /* Return 2 triggers usage message. */
451 result = 2;
452 goto out;
453 }
454 }
455
456 if (((do_add || do_delete) && (get_password || update)) || !query) {
457 result = 2;
458 goto out;
459 }
460
461 argc -= optind;
462 argv += optind;
463
464 int ix;
465 for (ix = 0; ix < argc; ++ix) {
466 if (!keychain_query_parse_cstring(query, argv[ix])) {
467 result = 1;
468 goto out;
469 }
470 }
471
472 if (!update && !do_add && !do_delete) {
473 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
474 if(limit) {
475 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
476 CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
477 CFReleaseSafe(cfLimit);
478 } else {
479 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
480 }
481 if (get_password)
482 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
483 }
484
485 if (verbose)
486 CFShow(query);
487
488 OSStatus error;
489 if (do_add) {
490 error = SecItemAdd(query, NULL);
491 if (error) {
492 sec_perror("SecItemAdd", error);
493 result = 1;
494 }
495 } else if (update) {
496 error = SecItemUpdate(query, update);
497 if (error) {
498 sec_perror("SecItemUpdate", error);
499 result = 1;
500 }
501 } else if (do_delete) {
502 error = SecItemDelete(query);
503 if (error) {
504 sec_perror("SecItemDelete", error);
505 result = 1;
506 }
507 } else {
508 if (!do_delete && CFDictionaryGetValue(query, kSecUseAuthenticationUI) == NULL)
509 CFDictionarySetValue(query, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
510 do_find_or_delete(query, do_delete);
511 }
512
513 out:
514 CFReleaseSafe(query);
515 CFReleaseSafe(update);
516 return result;
517 }
518
519 static int
520 keychain_find_or_delete_generic_password(Boolean do_delete,
521 int argc, char * const *argv)
522 {
523 char *serviceName = NULL, *accountName = NULL;
524 int ch, result = 0;
525 Boolean get_password = FALSE;
526
527 while ((ch = getopt(argc, argv, "a:s:g")) != -1)
528 {
529 switch (ch)
530 {
531 case 'a':
532 accountName = optarg;
533 break;
534 case 'g':
535 if (do_delete)
536 return 2;
537 get_password = TRUE;
538 break;
539 case 's':
540 serviceName = optarg;
541 break;
542 case '?':
543 default:
544 return 2; /* @@@ Return 2 triggers usage message. */
545 }
546 }
547
548 result = do_keychain_find_or_delete_generic_password(do_delete,
549 serviceName, accountName, get_password);
550
551 return result;
552 }
553
554 int
555 keychain_find_generic_password(int argc, char * const *argv) {
556 return keychain_find_or_delete_generic_password(0, argc, argv);
557 }
558
559 int
560 keychain_delete_generic_password(int argc, char * const *argv) {
561 return keychain_find_or_delete_generic_password(1, argc, argv);
562 }
563
564 #if TARGET_OS_EMBEDDED
565
566 int
567 keychain_roll_keys(int argc, char * const *argv) {
568 int ch, result = 0;
569 bool force = false;
570
571 while ((ch = getopt(argc, argv, "f")) != -1)
572 {
573 switch (ch)
574 {
575 case 'f':
576 force = true;
577 break;
578 default:
579 return 2;
580 }
581 }
582 // argc -= optind;
583 // argv += optind;
584
585 (void) argc; // These are set so folks could use them
586 (void) argv; // silence the analyzer since they're not used
587
588 CFErrorRef error = NULL;
589 bool ok = _SecKeychainRollKeys(force, &error);
590
591 fprintf(stderr, "Keychain keys up to date: %s\n", ok ? "yes" : "no");
592 if (!ok && error) {
593 result = (int)CFErrorGetCode(error);
594 CFShow(error);
595 }
596
597 return result;
598 }
599
600 #endif