]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/keychain_find.m
Security-58286.270.3.0.1.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 #import <Foundation/Foundation.h>
28
29 #include <Security/SecItem.h>
30 #include <Security/SecItemPriv.h>
31
32 #include <SecurityTool/tool_errors.h>
33 #include <SecurityTool/readline.h>
34
35 #include <utilities/SecCFWrappers.h>
36
37 #include "SecurityCommands.h"
38
39 #include "keychain_util.h"
40 #include <Security/SecAccessControl.h>
41 #include <Security/SecAccessControlPriv.h>
42
43 #import <SecurityFoundation/SFKeychain.h>
44
45 //
46 // Craptastic hacks.
47 #ifndef _SECURITY_SECKEYCHAIN_H_
48 typedef uint32_t SecProtocolType;
49 typedef uint32_t SecAuthenticationType;
50 #endif
51
52
53 static CFMutableDictionaryRef
54 keychain_create_query_from_string(const char *query) {
55 CFMutableDictionaryRef q;
56
57 q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
58 if (!keychain_query_parse_cstring(q, query)) {
59 CFReleaseNull(q);
60 }
61 return q;
62 }
63
64 static void add_key(const void *key, const void *value, void *context) {
65 CFArrayAppendValue(context, key);
66 }
67
68 static bool isPrintableString(CFStringRef theString){
69 bool result = false;
70 CFCharacterSetRef controlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);
71 CFCharacterSetRef newlineSet = CFCharacterSetGetPredefined(kCFCharacterSetNewline);
72 CFCharacterSetRef illegalSet = CFCharacterSetGetPredefined(kCFCharacterSetIllegal);
73
74 CFMutableCharacterSetRef unacceptable = CFCharacterSetCreateMutableCopy(kCFAllocatorDefault, controlSet);
75 CFCharacterSetUnion(unacceptable, newlineSet);
76 CFCharacterSetUnion(unacceptable, illegalSet);
77 result = CFStringFindCharacterFromSet(theString, unacceptable, CFRangeMake(0, CFStringGetLength(theString)), 0, NULL);
78 CFReleaseNull(unacceptable);
79 return result;
80 }
81
82 static void display_item(const void *v_item, void *context) {
83 CFDictionaryRef item = (CFDictionaryRef)v_item;
84 CFIndex dict_count, key_ix, key_count;
85 CFMutableArrayRef keys = NULL;
86 CFIndex maxWidth = 10; /* Maybe precompute this or grab from context? */
87
88 dict_count = CFDictionaryGetCount(item);
89 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
90 &kCFTypeArrayCallBacks);
91 CFDictionaryApplyFunction(item, add_key, keys);
92 key_count = CFArrayGetCount(keys);
93 CFArraySortValues(keys, CFRangeMake(0, key_count),
94 (CFComparatorFunction)CFStringCompare, 0);
95
96 for (key_ix = 0; key_ix < key_count; ++key_ix) {
97 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
98 CFTypeRef value = CFDictionaryGetValue(item, key);
99 CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
100
101 CFStringAppend(line, key);
102 CFIndex jx;
103 for (jx = CFStringGetLength(key);
104 jx < maxWidth; ++jx) {
105 CFStringAppend(line, CFSTR(" "));
106 }
107 CFStringAppend(line, CFSTR(" : "));
108 if (CFStringGetTypeID() == CFGetTypeID(value)) {
109 CFStringAppend(line, (CFStringRef)value);
110 } else if (CFNumberGetTypeID() == CFGetTypeID(value)) {
111 CFNumberRef v_n = (CFNumberRef)value;
112 CFStringAppendFormat(line, NULL, CFSTR("%@"), v_n);
113 } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
114 CFDateRef v_d = (CFDateRef)value;
115 CFStringAppendFormat(line, NULL, CFSTR("%@"), v_d);
116 } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
117 CFDataRef v_d = (CFDataRef)value;
118 CFStringRef v_s = CFStringCreateFromExternalRepresentation(
119 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
120
121 if (v_s) {
122 if(!isPrintableString(v_s))
123 CFStringAppend(line, CFSTR("not printable "));
124 else{
125 CFStringAppend(line, CFSTR("/"));
126 CFStringAppend(line, v_s);
127 CFStringAppend(line, CFSTR("/ "));
128 }
129 }
130 CFReleaseNull(v_s);
131
132 const uint8_t *bytes = CFDataGetBytePtr(v_d);
133 CFIndex len = CFDataGetLength(v_d);
134 for (jx = 0; jx < len; ++jx) {
135 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
136 }
137 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value)) {
138 display_sac_line((SecAccessControlRef)value, line);
139 } else {
140 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
141 }
142
143 CFStringWriteToFileWithNewline(line, stdout);
144
145 CFRelease(line);
146 }
147 CFRelease(keys);
148
149 CFStringWriteToFileWithNewline(CFSTR("===="), stdout);
150
151 //CFShow(item);
152 }
153
154
155 static void display_results(CFTypeRef results) {
156 if (CFGetTypeID(results) == CFArrayGetTypeID()) {
157 CFArrayRef r_a = (CFArrayRef)results;
158 CFArrayApplyFunction(r_a, CFRangeMake(0, CFArrayGetCount(r_a)),
159 display_item, NULL);
160 } else if (CFGetTypeID(results) == CFDictionaryGetTypeID()) {
161 display_item(results, NULL);
162 } else {
163 fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
164 CFShow(results);
165 }
166 }
167
168 static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) {
169 OSStatus result;
170 if (do_delete) {
171 result = SecItemDelete(query);
172 if (result) {
173 sec_perror("SecItemDelete", result);
174 }
175 } else {
176 CFTypeRef results = NULL;
177 result = SecItemCopyMatching(query, &results);
178 if (result) {
179 sec_perror("SecItemCopyMatching", result);
180 } else {
181 display_results(results);
182 }
183 CFReleaseSafe(results);
184 }
185 return result;
186 }
187
188 static int
189 do_keychain_find_or_delete_internet_password(Boolean do_delete,
190 const char *serverName, const char *securityDomain,
191 const char *accountName, const char *path, UInt16 port,
192 SecProtocolType protocol, SecAuthenticationType authenticationType,
193 Boolean get_password)
194 {
195 OSStatus result;
196 CFDictionaryRef query = NULL;
197 const void *keys[11], *values[11];
198 CFIndex ix = 0;
199
200 if (do_delete && !serverName && !securityDomain && !accountName && !path && !port && !protocol && !authenticationType) {
201 return SHOW_USAGE_MESSAGE;
202 }
203
204 keys[ix] = kSecClass;
205 values[ix++] = kSecClassInternetPassword;
206 if (serverName) {
207 keys[ix] = kSecAttrServer;
208 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName,
209 kCFStringEncodingUTF8, kCFAllocatorNull);
210 }
211 if (securityDomain) {
212 keys[ix] = kSecAttrSecurityDomain;
213 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain,
214 kCFStringEncodingUTF8, kCFAllocatorNull);
215 }
216 if (accountName) {
217 keys[ix] = kSecAttrAccount;
218 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
219 kCFStringEncodingUTF8, kCFAllocatorNull);
220 }
221 if (path) {
222 keys[ix] = kSecAttrPath;
223 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path,
224 kCFStringEncodingUTF8, kCFAllocatorNull);
225 }
226 if (port != 0) {
227 keys[ix] = kSecAttrPort;
228 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port);
229 }
230 if (protocol != 0) {
231 /* Protocol is a 4 char code, perhaps we should use a string rep
232 instead. */
233 keys[ix] = kSecAttrProtocol;
234 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol);
235 }
236 if (authenticationType != 0) {
237 keys[ix] = kSecAttrAuthenticationType;
238 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type,
239 &authenticationType);
240 }
241 if (get_password) {
242 /* Only ask for the data if so required. */
243 keys[ix] = kSecReturnData;
244 values[ix++] = kCFBooleanTrue;
245 }
246 keys[ix] = kSecReturnAttributes;
247 values[ix++] = kCFBooleanTrue;
248 if (!do_delete) {
249 /* If we aren't deleting ask for all items. */
250 keys[ix] = kSecMatchLimit;
251 values[ix++] = kSecMatchLimitAll;
252 }
253
254 query = CFDictionaryCreate(NULL, keys, values, ix,
255 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
256 result = do_find_or_delete(query, do_delete);
257 CFReleaseSafe(query);
258
259 return result;
260 }
261
262 static int
263 parse_fourcharcode(const char *name, uint32_t *code)
264 {
265 /* @@@ Check for errors. */
266 char *p = (char *)code;
267 strncpy(p, name, 4);
268 return 0;
269 }
270
271 static int
272 keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv)
273 {
274 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
275 UInt16 port = 0;
276 SecProtocolType protocol = 0;
277 SecAuthenticationType authenticationType = 0;
278 int ch, result = 0;
279 Boolean get_password = FALSE;
280
281 while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1)
282 {
283 switch (ch)
284 {
285 case 'a':
286 accountName = optarg;
287 break;
288 case 'd':
289 securityDomain = optarg;
290 break;
291 case 'g':
292 if (do_delete)
293 return SHOW_USAGE_MESSAGE;
294 get_password = TRUE;
295 break;
296 case 'p':
297 path = optarg;
298 break;
299 case 'P':
300 port = atoi(optarg);
301 break;
302 case 'r':
303 result = parse_fourcharcode(optarg, &protocol);
304 if (result)
305 goto loser;
306 break;
307 case 's':
308 serverName = optarg;
309 break;
310 case 't':
311 result = parse_fourcharcode(optarg, &authenticationType);
312 if (result)
313 goto loser;
314 break;
315 case '?':
316 default:
317 return SHOW_USAGE_MESSAGE;
318 }
319 }
320
321 result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain,
322 accountName, path, port, protocol,authenticationType, get_password);
323
324
325 loser:
326
327 return result;
328 }
329
330 int
331 keychain_find_internet_password(int argc, char * const *argv) {
332 return keychain_find_or_delete_internet_password(0, argc, argv);
333 }
334
335 int
336 keychain_delete_internet_password(int argc, char * const *argv) {
337 return keychain_find_or_delete_internet_password(1, argc, argv);
338 }
339
340 static int
341 do_keychain_find_or_delete_generic_password(Boolean do_delete,
342 const char *serviceName, const char *accountName,
343 Boolean get_password)
344 {
345 OSStatus result;
346 CFDictionaryRef query = NULL;
347 const void *keys[6], *values[6];
348 CFIndex ix = 0;
349
350 if (do_delete && !serviceName && !accountName) {
351 return SHOW_USAGE_MESSAGE;
352 }
353
354 keys[ix] = kSecClass;
355 values[ix++] = kSecClassGenericPassword;
356 if (serviceName) {
357 keys[ix] = kSecAttrService;
358 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName,
359 kCFStringEncodingUTF8, kCFAllocatorNull);
360 }
361 if (accountName) {
362 keys[ix] = kSecAttrAccount;
363 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
364 kCFStringEncodingUTF8, kCFAllocatorNull);
365 }
366 if (get_password) {
367 /* Only ask for the data if so required. */
368 keys[ix] = kSecReturnData;
369 values[ix++] = kCFBooleanTrue;
370 }
371 keys[ix] = kSecReturnAttributes;
372 values[ix++] = kCFBooleanTrue;
373 if (!do_delete) {
374 /* If we aren't deleting ask for all items. */
375 keys[ix] = kSecMatchLimit;
376 values[ix++] = kSecMatchLimitAll;
377 }
378
379 query = CFDictionaryCreate(NULL, keys, values, ix,
380 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
381
382 result = do_find_or_delete(query, do_delete);
383
384 CFReleaseSafe(query);
385
386 return result;
387 }
388
389 static SFServiceIdentifier* serviceIdentifierInQuery(NSDictionary* query)
390 {
391 NSString* domain = query[@"domain"];
392 NSString* bundleID = query[@"bundleID"];
393 NSString* accessGroup = query[@"accessGroup"];
394 NSString* customServiceID = query[@"customServiceID"];
395
396 SFServiceIdentifier* serviceIdentifier = nil;
397 if (domain) {
398 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:domain forType:SFServiceIdentifierTypeDomain];
399 }
400 else if (bundleID) {
401 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:bundleID forType:SFServiceIdentifierTypeBundleID];
402 }
403 else if (accessGroup) {
404 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:accessGroup forType:SFServiceIdentifierTypeAccessGroup];
405 }
406 else if (customServiceID) {
407 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:customServiceID forType:SFServiceIdentifierTypeCustom];
408 }
409 if (!serviceIdentifier) {
410 sec_error("need service identifier");
411 }
412
413 return serviceIdentifier;
414 }
415
416 static SFPasswordCredential* lookupCredential(SFServiceIdentifier* serviceIdentifier, NSString* username)
417 {
418 __block SFPasswordCredential* result = nil;
419 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
420 [[SFCredentialStore defaultCredentialStore] lookupCredentialsForServiceIdentifiers:@[serviceIdentifier] withResultHandler:^(NSArray<SFCredential*>* results, NSError* error) {
421 if (error) {
422 sec_error("error looking up credentials: %s", error.description.UTF8String);
423 }
424 else {
425 bool foundCredential = false;
426 for (SFPasswordCredential* credential in results) {
427 if ([credential.username isEqualToString:username]) {
428 result = credential;
429 dispatch_semaphore_signal(semaphore);
430 return;
431 }
432 }
433
434 if (!foundCredential) {
435 sec_error("did not find credential");
436 }
437 }
438
439 dispatch_semaphore_signal(semaphore);
440 }];
441 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
442 sec_error("timed out trying to communicate with credential store");
443 }
444
445 return result;
446 }
447
448 static SFPasswordCredential* credentialFromQuery(NSDictionary* query)
449 {
450 NSString* username = query[@"username"];
451 NSData* passwordData = query[(__bridge id)kSecValueData];
452 NSString* password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding];
453 if (!username) {
454 sec_error("need username");
455 return nil;
456 }
457 if (!password) {
458 sec_error("need password");
459 return nil;
460 }
461
462 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery(query);
463
464 if (username && password && serviceIdentifier) {
465 return [[SFPasswordCredential alloc] initWithUsername:username password:password primaryServiceIdentifier:serviceIdentifier];
466 }
467 else {
468 return nil;
469 }
470 }
471
472 static SFAccessPolicy* accessPolicyInQuery(NSDictionary* query)
473 {
474 CFStringRef accessibility = (__bridge CFStringRef)query[(__bridge id)kSecAttrAccessible];
475 if (!accessibility) {
476 accessibility = kSecAttrAccessibleWhenUnlocked;
477 }
478
479 SFAccessPolicy* accessPolicy = [SFAccessPolicy accessPolicyWithSecAccessibility:accessibility error:nil];
480 NSNumber* synchronizableValue = query[(__bridge id)kSecAttrSynchronizable];
481 if (accessPolicy.sharingPolicy == SFSharingPolicyWithTrustedDevices && synchronizableValue && synchronizableValue.boolValue == NO) {
482 accessPolicy.sharingPolicy = SFSharingPolicyWithBackup;
483 }
484 if (!accessPolicy) {
485 sec_error("need access policy");
486 }
487
488 return accessPolicy;
489 }
490
491 int keychain_item(int argc, char * const *argv) {
492 int ch = 0;
493 __block int result = 0;
494 CFMutableDictionaryRef query, update = NULL;
495 bool get_password = false;
496 bool do_delete = false;
497 bool do_add = false;
498 bool verbose = false;
499 int limit = 0;
500
501 query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
502 #if TARGET_OS_OSX
503 CFDictionarySetValue(query, kSecAttrNoLegacy, kCFBooleanTrue);
504 #endif
505
506 while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1)
507 {
508 switch (ch)
509 {
510 case 'a':
511 do_add = true;
512 break;
513 case 'D':
514 do_delete = true;
515 break;
516 case 'd':
517 {
518 CFStringRef dataString = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
519 if (dataString) {
520 CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, dataString, kCFStringEncodingUTF8, 0);
521 if (data) {
522 CFDictionarySetValue(update ? update : query, kSecValueData, data);
523 CFRelease(data);
524 }
525 CFRelease(dataString);
526 } else {
527 result = 1;
528 goto out;
529 }
530 break;
531 }
532 case 'f':
533 {
534 CFDataRef data = copyFileContents(optarg);
535 CFDictionarySetValue(update ? update : query, kSecValueData, data);
536 CFRelease(data);
537 break;
538 }
539 case 'g':
540 get_password = true;
541 break;
542 case 'q':
543 if (!keychain_query_parse_cstring(query, optarg)) {
544 result = 1;
545 goto out;
546 }
547 break;
548 case 'u':
549 {
550 bool success = true;
551 if (!update)
552 update = keychain_create_query_from_string(optarg);
553 else
554 success = keychain_query_parse_cstring(update, optarg);
555 if (update == NULL || !success) {
556 result = 1;
557 goto out;
558 }
559 }
560 break;
561 case 'v':
562 verbose = true;
563 break;
564 case 'l':
565 limit = atoi(optarg);
566 break;
567 case '?':
568 default:
569 /* Return 2 triggers usage message. */
570 result = 2;
571 goto out;
572 }
573 }
574
575 if (((do_add || do_delete) && (get_password || update)) || !query) {
576 result = 2;
577 goto out;
578 }
579
580 argc -= optind;
581 argv += optind;
582
583 int ix;
584 for (ix = 0; ix < argc; ++ix) {
585 if (!keychain_query_parse_cstring(query, argv[ix])) {
586 result = 1;
587 goto out;
588 }
589 }
590
591 if (!update && !do_add && !do_delete) {
592 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
593 if(limit) {
594 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
595 CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
596 CFReleaseSafe(cfLimit);
597 } else {
598 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
599 }
600 if (get_password)
601 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
602 }
603
604 if (verbose)
605 CFShow(query);
606
607 OSStatus error;
608 bool useCredentialStore = [(__bridge id)CFDictionaryGetValue(query, kSecClass) isEqual:@"credential"];
609 if (do_add) {
610 if (useCredentialStore) {
611 SFPasswordCredential* credential = credentialFromQuery((__bridge NSDictionary*)query);
612 SFAccessPolicy* accessPolicy = accessPolicyInQuery((__bridge NSDictionary*)query);
613 if (credential && accessPolicy) {
614 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
615 [[SFCredentialStore defaultCredentialStore] addCredential:credential withAccessPolicy:accessPolicy resultHandler:^(NSString* persistentIdentifier, NSError* error) {
616 if (error) {
617 sec_error("error adding credential to credential store: %s", error.description.UTF8String);
618 result = 1;
619 }
620 dispatch_semaphore_signal(semaphore);
621 }];
622 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
623 sec_error("timed out waiting for response from credential store");
624 result = 1;
625 }
626 }
627 else {
628 sec_error("need username, password, service identiifer, and access policy to create a credential");
629 result = 1;
630 }
631 }
632 else {
633 error = SecItemAdd(query, NULL);
634 if (error) {
635 sec_perror("SecItemAdd", error);
636 result = 1;
637 }
638 }
639 } else if (update) {
640 if (useCredentialStore) {
641 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
642 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
643 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
644 if (credential) {
645 SFPasswordCredential* updatedCredential = credentialFromQuery((__bridge NSDictionary*)update);
646 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
647 [[SFCredentialStore defaultCredentialStore] replaceOldCredential:credential withNewCredential:updatedCredential resultHandler:^(NSString* newPersistentIdentifier, NSError* error) {
648 if (error) {
649 sec_error("error updating credential: %s", error.description.UTF8String);
650 result = 1;
651 }
652 dispatch_semaphore_signal(semaphore);
653 }];
654 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
655 sec_error("timed out trying to communicate with credential store");
656 result = 1;
657 }
658 }
659 else {
660 result = 1;
661 }
662 }
663 else {
664 error = SecItemUpdate(query, update);
665 if (error) {
666 sec_perror("SecItemUpdate", error);
667 result = 1;
668 }
669 }
670 } else if (do_delete) {
671 if (useCredentialStore) {
672 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
673 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
674 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
675 if (credential) {
676 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
677 [[SFCredentialStore defaultCredentialStore] removeCredentialWithPersistentIdentifier:credential.persistentIdentifier withResultHandler:^(BOOL success, NSError* error) {
678 if (!success) {
679 sec_error("failed to remove credential with error: %s", error.description.UTF8String);
680 result = 1;
681 }
682 dispatch_semaphore_signal(semaphore);
683 }];
684 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
685 sec_error("timed out trying to communicate with credential store");
686 result = 1;
687 }
688 }
689 else {
690 result = 1;
691 }
692 }
693 else {
694 error = SecItemDelete(query);
695 if (error) {
696 sec_perror("SecItemDelete", error);
697 result = 1;
698 }
699 }
700 } else {
701 if (useCredentialStore) {
702 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
703 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
704 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
705 if (credential) {
706 CFStringWriteToFileWithNewline((__bridge CFStringRef)credential.description, stdout);
707 }
708 else {
709 result = 1;
710 }
711 }
712 else {
713 if (!do_delete && CFDictionaryGetValue(query, kSecUseAuthenticationUI) == NULL) {
714 CFDictionarySetValue(query, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
715 }
716 do_find_or_delete(query, do_delete);
717 }
718 }
719
720 out:
721 CFReleaseSafe(query);
722 CFReleaseSafe(update);
723 return result;
724 }
725
726 static int
727 keychain_find_or_delete_generic_password(Boolean do_delete,
728 int argc, char * const *argv)
729 {
730 char *serviceName = NULL, *accountName = NULL;
731 int ch, result = 0;
732 Boolean get_password = FALSE;
733
734 while ((ch = getopt(argc, argv, "a:s:g")) != -1)
735 {
736 switch (ch)
737 {
738 case 'a':
739 accountName = optarg;
740 break;
741 case 'g':
742 if (do_delete)
743 return SHOW_USAGE_MESSAGE;
744 get_password = TRUE;
745 break;
746 case 's':
747 serviceName = optarg;
748 break;
749 case '?':
750 default:
751 return SHOW_USAGE_MESSAGE;
752 }
753 }
754
755 result = do_keychain_find_or_delete_generic_password(do_delete,
756 serviceName, accountName, get_password);
757
758 return result;
759 }
760
761 int
762 keychain_find_generic_password(int argc, char * const *argv) {
763 return keychain_find_or_delete_generic_password(0, argc, argv);
764 }
765
766 int
767 keychain_delete_generic_password(int argc, char * const *argv) {
768 return keychain_find_or_delete_generic_password(1, argc, argv);
769 }
770
771 int keychain_item_digest(int argc, char * const *argv) {
772 NSString *itemClass = @"inet";
773 NSString *accessGroup = @"com.apple.ProtectedCloudStorage";
774
775 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
776
777 if (argc == 3) {
778 itemClass = [NSString stringWithUTF8String:argv[1]];
779 accessGroup = [NSString stringWithUTF8String:argv[2]];
780 }
781
782 _SecItemFetchDigests(itemClass, accessGroup, ^(NSArray *items, NSError *error) {
783 for (NSDictionary *item in items) {
784 for (NSString *key in item) {
785 printf("%s\n", [[NSString stringWithFormat:@"%@\t\t%@", key, item[key]] UTF8String]);
786 }
787 }
788 if (error) {
789 printf("%s\n", [[NSString stringWithFormat:@"Failed to find items (%@/%@): %@", itemClass, accessGroup, error] UTF8String]);
790 }
791 dispatch_semaphore_signal(sema);
792 });
793 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
794
795 return 0;
796 }
797
798
799 #if TARGET_OS_EMBEDDED
800
801 int
802 keychain_roll_keys(int argc, char * const *argv) {
803 int ch, result = 0;
804 bool force = false;
805
806 while ((ch = getopt(argc, argv, "f")) != -1)
807 {
808 switch (ch)
809 {
810 case 'f':
811 force = true;
812 break;
813 default:
814 return SHOW_USAGE_MESSAGE;
815 }
816 }
817 // argc -= optind;
818 // argv += optind;
819
820 (void) argc; // These are set so folks could use them
821 (void) argv; // silence the analyzer since they're not used
822
823 CFErrorRef error = NULL;
824 bool ok = _SecKeychainRollKeys(force, &error);
825
826 fprintf(stderr, "Keychain keys up to date: %s\n", ok ? "yes" : "no");
827 if (!ok && error) {
828 result = (int)CFErrorGetCode(error);
829 CFShow(error);
830 }
831
832 return result;
833 }
834
835 #endif