2 * Copyright (c) 2003-2007,2009-2010,2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <CoreFoundation/CoreFoundation.h>
27 #import <Foundation/Foundation.h>
29 #include <Security/SecItem.h>
30 #include <Security/SecItemPriv.h>
32 #include "SecurityTool/sharedTool/tool_errors.h"
33 #include "SecurityTool/sharedTool/readline.h"
35 #include <utilities/SecCFWrappers.h>
37 #include "SecurityCommands.h"
39 #include "keychain_util.h"
40 #include <Security/SecAccessControl.h>
41 #include <Security/SecAccessControlPriv.h>
43 #import <SecurityFoundation/SFKeychain.h>
47 #ifndef _SECURITY_SECKEYCHAIN_H_
48 typedef uint32_t SecProtocolType;
49 typedef uint32_t SecAuthenticationType;
53 static CFMutableDictionaryRef
54 keychain_create_query_from_string(const char *query) {
55 CFMutableDictionaryRef q;
57 q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
58 if (!keychain_query_parse_cstring(q, query)) {
64 static void add_key(const void *key, const void *value, void *context) {
65 CFArrayAppendValue(context, key);
68 static bool isPrintableString(CFStringRef theString){
70 CFCharacterSetRef controlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);
71 CFCharacterSetRef newlineSet = CFCharacterSetGetPredefined(kCFCharacterSetNewline);
72 CFCharacterSetRef illegalSet = CFCharacterSetGetPredefined(kCFCharacterSetIllegal);
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);
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? */
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);
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);
101 CFStringAppend(line, key);
103 for (jx = CFStringGetLength(key);
104 jx < maxWidth; ++jx) {
105 CFStringAppend(line, CFSTR(" "));
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);
122 if(!isPrintableString(v_s))
123 CFStringAppend(line, CFSTR("not printable "));
125 CFStringAppend(line, CFSTR("/"));
126 CFStringAppend(line, v_s);
127 CFStringAppend(line, CFSTR("/ "));
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]);
137 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value)) {
138 display_sac_line((SecAccessControlRef)value, line);
140 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
143 CFStringWriteToFileWithNewline(line, stdout);
149 CFStringWriteToFileWithNewline(CFSTR("===="), stdout);
155 static void display_results(CFTypeRef results) {
156 if (results && CFGetTypeID(results) == CFArrayGetTypeID()) {
157 CFArrayRef r_a = (CFArrayRef)results;
158 CFArrayApplyFunction(r_a, CFRangeMake(0, CFArrayGetCount(r_a)),
160 } else if (results && CFGetTypeID(results) == CFDictionaryGetTypeID()) {
161 display_item(results, NULL);
163 fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
168 static NSDictionary* cleanNSDictionaryForJSON(NSDictionary* dict) {
172 NSMutableDictionary* mutDict = [NSMutableDictionary dictionary];
173 for(id key in dict.allKeys) {
176 if([obj isKindOfClass:[NSDictionary class]]) {
177 mutDict[key] = cleanNSDictionaryForJSON(obj);
179 } else if([NSJSONSerialization isValidJSONObject:obj]) {
182 } else if([obj isKindOfClass:[NSString class]]) {
185 } else if([obj isKindOfClass:[NSNumber class]]) {
188 } else if([obj isKindOfClass:[NSData class]]) {
189 mutDict[key] = [(NSData*)obj base64EncodedStringWithOptions:0];
191 } else if([obj isKindOfClass:[NSDate class]]) {
192 NSISO8601DateFormatter* dateFormat = [[NSISO8601DateFormatter alloc] init];
193 mutDict[key] = [dateFormat stringFromDate:obj];
195 } else if (SecAccessControlGetTypeID() == CFGetTypeID((__bridge CFTypeRef)obj)) {
196 NSMutableString* str = [NSMutableString string];
197 display_sac_line((__bridge SecAccessControlRef)obj, (__bridge CFMutableStringRef)str);
201 NSLog(@"can't jsonify: %@ %@ %@", key, obj, [obj class]);
207 static void display_results_json(CFTypeRef cfitem) {
208 id item = (__bridge id)cfitem;
210 if([item isKindOfClass:[NSArray class]]) {
211 NSArray* array = (NSArray*)item;
212 NSMutableArray* cleanArray = [NSMutableArray array];
215 NSDictionary* cleanDictionary = cleanNSDictionaryForJSON((NSDictionary*)x);
216 [cleanArray addObject:cleanDictionary];
218 if(![NSJSONSerialization isValidJSONObject:cleanDictionary]) {
219 fprintf(stderr, "%s", [[NSString stringWithFormat:@"Can't JSONify: %@", x] UTF8String]);
223 NSError* error = nil;
224 NSData *json = [NSJSONSerialization dataWithJSONObject:cleanArray
225 options:(NSJSONWritingPrettyPrinted | NSJSONWritingSortedKeys)
228 fprintf(stderr, "%s", [[NSString stringWithFormat:@"error: %@", error.localizedDescription] UTF8String]);
230 printf("%s\n", [[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] UTF8String]);
233 } else if([item isKindOfClass:[NSDictionary class]]) {
234 NSError* error = nil;
235 NSData *json = [NSJSONSerialization dataWithJSONObject:cleanNSDictionaryForJSON(item)
236 options:(NSJSONWritingPrettyPrinted | NSJSONWritingSortedKeys)
239 fprintf(stderr, "%s", [[NSString stringWithFormat:@"error: %@", error.localizedDescription] UTF8String]);
241 printf("%s\n", [[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] UTF8String]);
245 fprintf(stderr, "SecItemCopyMatching returned unexpected results (can't JSONify):");
251 static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) {
254 result = SecItemDelete(query);
256 sec_perror("SecItemDelete", result);
259 CFTypeRef results = NULL;
260 result = SecItemCopyMatching(query, &results);
262 sec_perror("SecItemCopyMatching", result);
264 display_results(results);
266 CFReleaseSafe(results);
272 do_keychain_find_or_delete_internet_password(Boolean do_delete,
273 const char *serverName, const char *securityDomain,
274 const char *accountName, const char *path, UInt16 port,
275 SecProtocolType protocol, SecAuthenticationType authenticationType,
276 Boolean get_password)
279 CFDictionaryRef query = NULL;
280 const void *keys[11], *values[11];
283 if (do_delete && !serverName && !securityDomain && !accountName && !path && !port && !protocol && !authenticationType) {
284 return SHOW_USAGE_MESSAGE;
287 keys[ix] = kSecClass;
288 values[ix++] = kSecClassInternetPassword;
290 keys[ix] = kSecAttrServer;
291 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName,
292 kCFStringEncodingUTF8, kCFAllocatorNull);
294 if (securityDomain) {
295 keys[ix] = kSecAttrSecurityDomain;
296 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain,
297 kCFStringEncodingUTF8, kCFAllocatorNull);
300 keys[ix] = kSecAttrAccount;
301 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
302 kCFStringEncodingUTF8, kCFAllocatorNull);
305 keys[ix] = kSecAttrPath;
306 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path,
307 kCFStringEncodingUTF8, kCFAllocatorNull);
310 keys[ix] = kSecAttrPort;
311 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port);
314 /* Protocol is a 4 char code, perhaps we should use a string rep
316 keys[ix] = kSecAttrProtocol;
317 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol);
319 if (authenticationType != 0) {
320 keys[ix] = kSecAttrAuthenticationType;
321 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type,
322 &authenticationType);
325 /* Only ask for the data if so required. */
326 keys[ix] = kSecReturnData;
327 values[ix++] = kCFBooleanTrue;
329 keys[ix] = kSecReturnAttributes;
330 values[ix++] = kCFBooleanTrue;
332 /* If we aren't deleting ask for all items. */
333 keys[ix] = kSecMatchLimit;
334 values[ix++] = kSecMatchLimitAll;
337 query = CFDictionaryCreate(NULL, keys, values, ix,
338 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
339 result = do_find_or_delete(query, do_delete);
340 CFReleaseSafe(query);
346 parse_fourcharcode(const char *name, uint32_t *code)
348 /* @@@ Check for errors. */
349 char *p = (char *)code;
355 keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv)
357 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
359 SecProtocolType protocol = 0;
360 SecAuthenticationType authenticationType = 0;
362 Boolean get_password = FALSE;
364 while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1)
369 accountName = optarg;
372 securityDomain = optarg;
376 return SHOW_USAGE_MESSAGE;
386 result = parse_fourcharcode(optarg, &protocol);
394 result = parse_fourcharcode(optarg, &authenticationType);
400 return SHOW_USAGE_MESSAGE;
404 result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain,
405 accountName, path, port, protocol,authenticationType, get_password);
414 keychain_find_internet_password(int argc, char * const *argv) {
415 return keychain_find_or_delete_internet_password(0, argc, argv);
419 keychain_delete_internet_password(int argc, char * const *argv) {
420 return keychain_find_or_delete_internet_password(1, argc, argv);
424 do_keychain_find_or_delete_generic_password(Boolean do_delete,
425 const char *serviceName, const char *accountName,
426 Boolean get_password)
429 CFDictionaryRef query = NULL;
430 const void *keys[6], *values[6];
433 if (do_delete && !serviceName && !accountName) {
434 return SHOW_USAGE_MESSAGE;
437 keys[ix] = kSecClass;
438 values[ix++] = kSecClassGenericPassword;
440 keys[ix] = kSecAttrService;
441 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName,
442 kCFStringEncodingUTF8, kCFAllocatorNull);
445 keys[ix] = kSecAttrAccount;
446 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
447 kCFStringEncodingUTF8, kCFAllocatorNull);
450 /* Only ask for the data if so required. */
451 keys[ix] = kSecReturnData;
452 values[ix++] = kCFBooleanTrue;
454 keys[ix] = kSecReturnAttributes;
455 values[ix++] = kCFBooleanTrue;
457 /* If we aren't deleting ask for all items. */
458 keys[ix] = kSecMatchLimit;
459 values[ix++] = kSecMatchLimitAll;
462 query = CFDictionaryCreate(NULL, keys, values, ix,
463 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
465 result = do_find_or_delete(query, do_delete);
467 CFReleaseSafe(query);
472 static SFServiceIdentifier* serviceIdentifierInQuery(NSDictionary* query)
474 NSString* domain = query[@"domain"];
475 NSString* bundleID = query[@"bundleID"];
476 NSString* accessGroup = query[@"accessGroup"];
477 NSString* customServiceID = query[@"customServiceID"];
479 SFServiceIdentifier* serviceIdentifier = nil;
481 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:domain forType:SFServiceIdentifierTypeDomain];
484 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:bundleID forType:SFServiceIdentifierTypeBundleID];
486 else if (accessGroup) {
487 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:accessGroup forType:SFServiceIdentifierTypeAccessGroup];
489 else if (customServiceID) {
490 serviceIdentifier = [[SFServiceIdentifier alloc] initWithServiceID:customServiceID forType:SFServiceIdentifierTypeCustom];
492 if (!serviceIdentifier) {
493 sec_error("need service identifier");
496 return serviceIdentifier;
499 static SFPasswordCredential* lookupCredential(SFServiceIdentifier* serviceIdentifier, NSString* username)
501 __block SFPasswordCredential* result = nil;
502 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
503 [[SFCredentialStore defaultCredentialStore] lookupCredentialsForServiceIdentifiers:@[serviceIdentifier] withResultHandler:^(NSArray<SFCredential*>* results, NSError* error) {
505 sec_error("error looking up credentials: %s", error.description.UTF8String);
508 bool foundCredential = false;
509 for (SFPasswordCredential* credential in results) {
510 if ([credential.username isEqualToString:username]) {
512 dispatch_semaphore_signal(semaphore);
517 if (!foundCredential) {
518 sec_error("did not find credential");
522 dispatch_semaphore_signal(semaphore);
524 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
525 sec_error("timed out trying to communicate with credential store");
531 static SFPasswordCredential* credentialFromQuery(NSDictionary* query)
533 NSString* username = query[@"username"];
534 NSData* passwordData = query[(__bridge id)kSecValueData];
535 NSString* password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding];
537 sec_error("need username");
541 sec_error("need password");
545 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery(query);
547 if (username && password && serviceIdentifier) {
548 return [[SFPasswordCredential alloc] initWithUsername:username password:password primaryServiceIdentifier:serviceIdentifier];
555 static SFAccessPolicy* accessPolicyInQuery(NSDictionary* query)
557 CFStringRef accessibility = (__bridge CFStringRef)query[(__bridge id)kSecAttrAccessible];
558 if (!accessibility) {
559 accessibility = kSecAttrAccessibleWhenUnlocked;
562 SFAccessPolicy* accessPolicy = [SFAccessPolicy accessPolicyWithSecAccessibility:accessibility error:nil];
563 NSNumber* synchronizableValue = query[(__bridge id)kSecAttrSynchronizable];
564 if (accessPolicy.sharingPolicy == SFSharingPolicyWithTrustedDevices && synchronizableValue && synchronizableValue.boolValue == NO) {
565 accessPolicy.sharingPolicy = SFSharingPolicyWithBackup;
568 sec_error("need access policy");
574 int keychain_item(int argc, char * const *argv) {
576 __block int result = 0;
577 CFMutableDictionaryRef query, update = NULL;
578 bool get_password = false;
579 bool do_delete = false;
581 bool verbose = false;
585 query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
587 CFDictionarySetValue(query, kSecUseDataProtectionKeychain, kCFBooleanTrue);
590 while ((ch = getopt(argc, argv, "ad:Df:jgq:u:vl:")) != -1)
602 CFStringRef dataString = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
604 CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, dataString, kCFStringEncodingUTF8, 0);
606 CFDictionarySetValue(update ? update : query, kSecValueData, data);
609 CFRelease(dataString);
618 CFDataRef data = copyFileContents(optarg);
619 CFDictionarySetValue(update ? update : query, kSecValueData, data);
630 if (!keychain_query_parse_cstring(query, optarg)) {
639 update = keychain_create_query_from_string(optarg);
641 success = keychain_query_parse_cstring(update, optarg);
642 if (update == NULL || !success) {
652 limit = atoi(optarg);
656 /* Return 2 triggers usage message. */
662 if (((do_add || do_delete) && (get_password || update)) || !query) {
671 for (ix = 0; ix < argc; ++ix) {
672 if (!keychain_query_parse_cstring(query, argv[ix])) {
678 if (!update && !do_add && !do_delete) {
679 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
681 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
682 CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
683 CFReleaseSafe(cfLimit);
685 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
688 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
693 display_results_json(query);
700 bool useCredentialStore = [(__bridge id)CFDictionaryGetValue(query, kSecClass) isEqual:@"credential"];
702 if (useCredentialStore) {
703 SFPasswordCredential* credential = credentialFromQuery((__bridge NSDictionary*)query);
704 SFAccessPolicy* accessPolicy = accessPolicyInQuery((__bridge NSDictionary*)query);
705 if (credential && accessPolicy) {
706 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
707 [[SFCredentialStore defaultCredentialStore] addCredential:credential withAccessPolicy:accessPolicy resultHandler:^(NSString* persistentIdentifier, NSError* error) {
709 sec_error("error adding credential to credential store: %s", error.description.UTF8String);
712 dispatch_semaphore_signal(semaphore);
714 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
715 sec_error("timed out waiting for response from credential store");
720 sec_error("need username, password, service identiifer, and access policy to create a credential");
725 error = SecItemAdd(query, NULL);
727 sec_perror("SecItemAdd", error);
732 if (useCredentialStore) {
733 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
734 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
735 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
737 SFPasswordCredential* updatedCredential = credentialFromQuery((__bridge NSDictionary*)update);
738 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
739 [[SFCredentialStore defaultCredentialStore] replaceOldCredential:credential withNewCredential:updatedCredential resultHandler:^(NSString* newPersistentIdentifier, NSError* error) {
741 sec_error("error updating credential: %s", error.description.UTF8String);
744 dispatch_semaphore_signal(semaphore);
746 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
747 sec_error("timed out trying to communicate with credential store");
756 error = SecItemUpdate(query, update);
758 sec_perror("SecItemUpdate", error);
762 } else if (do_delete) {
763 if (useCredentialStore) {
764 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
765 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
766 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
768 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
769 [[SFCredentialStore defaultCredentialStore] removeCredentialWithPersistentIdentifier:credential.persistentIdentifier withResultHandler:^(BOOL success, NSError* error) {
771 sec_error("failed to remove credential with error: %s", error.description.UTF8String);
774 dispatch_semaphore_signal(semaphore);
776 if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
777 sec_error("timed out trying to communicate with credential store");
786 error = SecItemDelete(query);
788 sec_perror("SecItemDelete", error);
793 if (useCredentialStore) {
794 NSString* username = (__bridge NSString*)CFDictionaryGetValue(query, CFSTR("username"));
795 SFServiceIdentifier* serviceIdentifier = serviceIdentifierInQuery((__bridge NSDictionary*)query);
796 SFPasswordCredential* credential = lookupCredential(serviceIdentifier, username);
798 CFStringWriteToFileWithNewline((__bridge CFStringRef)credential.description, stdout);
805 if (!do_delete && CFDictionaryGetValue(query, kSecUseAuthenticationUI) == NULL) {
806 CFDictionarySetValue(query, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
809 CFTypeRef results = NULL;
810 OSStatus status = SecItemCopyMatching(query, &results);
812 sec_perror("SecItemCopyMatching", status);
814 display_results_json(results);
816 display_results(results);
818 CFReleaseNull(results);
823 CFReleaseSafe(query);
824 CFReleaseSafe(update);
829 keychain_find_or_delete_generic_password(Boolean do_delete,
830 int argc, char * const *argv)
832 char *serviceName = NULL, *accountName = NULL;
834 Boolean get_password = FALSE;
836 while ((ch = getopt(argc, argv, "a:s:g")) != -1)
841 accountName = optarg;
845 return SHOW_USAGE_MESSAGE;
849 serviceName = optarg;
853 return SHOW_USAGE_MESSAGE;
857 result = do_keychain_find_or_delete_generic_password(do_delete,
858 serviceName, accountName, get_password);
864 keychain_find_generic_password(int argc, char * const *argv) {
865 return keychain_find_or_delete_generic_password(0, argc, argv);
869 keychain_delete_generic_password(int argc, char * const *argv) {
870 return keychain_find_or_delete_generic_password(1, argc, argv);
873 int keychain_item_digest(int argc, char * const *argv) {
874 NSString *itemClass = @"inet";
875 NSString *accessGroup = @"com.apple.ProtectedCloudStorage";
877 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
880 itemClass = [NSString stringWithUTF8String:argv[1]];
881 accessGroup = [NSString stringWithUTF8String:argv[2]];
884 _SecItemFetchDigests(itemClass, accessGroup, ^(NSArray *items, NSError *error) {
885 for (NSDictionary *item in items) {
886 for (NSString *key in item) {
887 printf("%s\n", [[NSString stringWithFormat:@"%@\t\t%@", key, item[key]] UTF8String]);
891 printf("%s\n", [[NSString stringWithFormat:@"Failed to find items (%@/%@): %@", itemClass, accessGroup, error] UTF8String]);
893 dispatch_semaphore_signal(sema);
895 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
901 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
904 keychain_roll_keys(int argc, char * const *argv) {
908 while ((ch = getopt(argc, argv, "f")) != -1)
916 return SHOW_USAGE_MESSAGE;
922 (void) argc; // These are set so folks could use them
923 (void) argv; // silence the analyzer since they're not used
925 CFErrorRef error = NULL;
926 bool ok = _SecKeychainRollKeys(force, &error);
928 fprintf(stderr, "Keychain keys up to date: %s\n", ok ? "yes" : "no");
930 result = (int)CFErrorGetCode(error);