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