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>
28 #include <Security/SecItem.h>
29 #include <Security/SecItemPriv.h>
31 #include <SecurityTool/tool_errors.h>
32 #include <SecurityTool/readline.h>
34 #include <utilities/SecCFWrappers.h>
36 #include "SecurityCommands.h"
38 #include "keychain_util.h"
39 #include <Security/SecAccessControl.h>
40 #include <Security/SecAccessControlPriv.h>
45 typedef uint32_t SecProtocolType;
46 typedef uint32_t SecAuthenticationType;
49 static CFMutableDictionaryRef
50 keychain_create_query_from_string(const char *query) {
51 CFMutableDictionaryRef q;
53 q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
54 if (!keychain_query_parse_cstring(q, query)) {
60 static void add_key(const void *key, const void *value, void *context) {
61 CFArrayAppendValue(context, key);
64 static bool isPrintableString(CFStringRef theString){
66 CFCharacterSetRef controlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);
67 CFCharacterSetRef newlineSet = CFCharacterSetGetPredefined(kCFCharacterSetNewline);
68 CFCharacterSetRef illegalSet = CFCharacterSetGetPredefined(kCFCharacterSetIllegal);
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);
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? */
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);
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);
97 CFStringAppend(line, key);
99 for (jx = CFStringGetLength(key);
100 jx < maxWidth; ++jx) {
101 CFStringAppend(line, CFSTR(" "));
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);
118 if(!isPrintableString(v_s))
119 CFStringAppend(line, CFSTR("not printable "));
121 CFStringAppend(line, CFSTR("/"));
122 CFStringAppend(line, v_s);
123 CFStringAppend(line, CFSTR("/ "));
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]);
133 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value)) {
134 display_sac_line((SecAccessControlRef)value, line);
136 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
139 CFStringWriteToFileWithNewline(line, stdout);
145 CFStringWriteToFileWithNewline(CFSTR("===="), stdout);
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)),
156 } else if (CFGetTypeID(results) == CFDictionaryGetTypeID()) {
157 display_item(results, NULL);
159 fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
164 static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) {
167 result = SecItemDelete(query);
169 sec_perror("SecItemDelete", result);
172 CFTypeRef results = NULL;
173 result = SecItemCopyMatching(query, &results);
175 sec_perror("SecItemCopyMatching", result);
177 display_results(results);
179 CFReleaseSafe(results);
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)
192 CFDictionaryRef query = NULL;
193 const void *keys[11], *values[11];
196 keys[ix] = kSecClass;
197 values[ix++] = kSecClassInternetPassword;
199 keys[ix] = kSecAttrServer;
200 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName,
201 kCFStringEncodingUTF8, kCFAllocatorNull);
203 if (securityDomain) {
204 keys[ix] = kSecAttrSecurityDomain;
205 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain,
206 kCFStringEncodingUTF8, kCFAllocatorNull);
209 keys[ix] = kSecAttrAccount;
210 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
211 kCFStringEncodingUTF8, kCFAllocatorNull);
214 keys[ix] = kSecAttrPath;
215 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path,
216 kCFStringEncodingUTF8, kCFAllocatorNull);
219 keys[ix] = kSecAttrPort;
220 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port);
223 /* Protocol is a 4 char code, perhaps we should use a string rep
225 keys[ix] = kSecAttrProtocol;
226 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol);
228 if (authenticationType != 0) {
229 keys[ix] = kSecAttrAuthenticationType;
230 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type,
231 &authenticationType);
234 /* Only ask for the data if so required. */
235 keys[ix] = kSecReturnData;
236 values[ix++] = kCFBooleanTrue;
238 keys[ix] = kSecReturnAttributes;
239 values[ix++] = kCFBooleanTrue;
241 /* If we aren't deleting ask for all items. */
242 keys[ix] = kSecMatchLimit;
243 values[ix++] = kSecMatchLimitAll;
246 query = CFDictionaryCreate(NULL, keys, values, ix,
247 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
248 result = do_find_or_delete(query, do_delete);
249 CFReleaseSafe(query);
255 parse_fourcharcode(const char *name, uint32_t *code)
257 /* @@@ Check for errors. */
258 char *p = (char *)code;
264 keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv)
266 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
268 SecProtocolType protocol = 0;
269 SecAuthenticationType authenticationType = 0;
271 Boolean get_password = FALSE;
273 while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1)
278 accountName = optarg;
281 securityDomain = optarg;
285 return SHOW_USAGE_MESSAGE;
295 result = parse_fourcharcode(optarg, &protocol);
303 result = parse_fourcharcode(optarg, &authenticationType);
309 return SHOW_USAGE_MESSAGE;
313 result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain,
314 accountName, path, port, protocol,authenticationType, get_password);
323 keychain_find_internet_password(int argc, char * const *argv) {
324 return keychain_find_or_delete_internet_password(0, argc, argv);
328 keychain_delete_internet_password(int argc, char * const *argv) {
329 return keychain_find_or_delete_internet_password(1, argc, argv);
333 do_keychain_find_or_delete_generic_password(Boolean do_delete,
334 const char *serviceName, const char *accountName,
335 Boolean get_password)
338 CFDictionaryRef query = NULL;
339 const void *keys[6], *values[6];
342 keys[ix] = kSecClass;
343 values[ix++] = kSecClassGenericPassword;
345 keys[ix] = kSecAttrService;
346 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName,
347 kCFStringEncodingUTF8, kCFAllocatorNull);
350 keys[ix] = kSecAttrAccount;
351 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
352 kCFStringEncodingUTF8, kCFAllocatorNull);
355 /* Only ask for the data if so required. */
356 keys[ix] = kSecReturnData;
357 values[ix++] = kCFBooleanTrue;
359 keys[ix] = kSecReturnAttributes;
360 values[ix++] = kCFBooleanTrue;
362 /* If we aren't deleting ask for all items. */
363 keys[ix] = kSecMatchLimit;
364 values[ix++] = kSecMatchLimitAll;
367 query = CFDictionaryCreate(NULL, keys, values, ix,
368 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
370 result = do_find_or_delete(query, do_delete);
372 CFReleaseSafe(query);
377 int keychain_item(int argc, char * const *argv) {
379 CFMutableDictionaryRef query, update = NULL;
380 bool get_password = false;
381 bool do_delete = false;
383 bool verbose = false;
386 query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
388 CFDictionarySetValue(query, kSecAttrNoLegacy, kCFBooleanTrue);
391 while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1)
403 CFStringRef dataString = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
405 CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, dataString, kCFStringEncodingUTF8, 0);
407 CFDictionarySetValue(update ? update : query, kSecValueData, data);
410 CFRelease(dataString);
419 CFDataRef data = copyFileContents(optarg);
420 CFDictionarySetValue(update ? update : query, kSecValueData, data);
428 if (!keychain_query_parse_cstring(query, optarg)) {
437 update = keychain_create_query_from_string(optarg);
439 success = keychain_query_parse_cstring(update, optarg);
440 if (update == NULL || !success) {
450 limit = atoi(optarg);
454 /* Return 2 triggers usage message. */
460 if (((do_add || do_delete) && (get_password || update)) || !query) {
469 for (ix = 0; ix < argc; ++ix) {
470 if (!keychain_query_parse_cstring(query, argv[ix])) {
476 if (!update && !do_add && !do_delete) {
477 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
479 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
480 CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
481 CFReleaseSafe(cfLimit);
483 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
486 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
494 error = SecItemAdd(query, NULL);
496 sec_perror("SecItemAdd", error);
500 error = SecItemUpdate(query, update);
502 sec_perror("SecItemUpdate", error);
505 } else if (do_delete) {
506 error = SecItemDelete(query);
508 sec_perror("SecItemDelete", error);
512 if (!do_delete && CFDictionaryGetValue(query, kSecUseAuthenticationUI) == NULL)
513 CFDictionarySetValue(query, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
514 do_find_or_delete(query, do_delete);
518 CFReleaseSafe(query);
519 CFReleaseSafe(update);
524 keychain_find_or_delete_generic_password(Boolean do_delete,
525 int argc, char * const *argv)
527 char *serviceName = NULL, *accountName = NULL;
529 Boolean get_password = FALSE;
531 while ((ch = getopt(argc, argv, "a:s:g")) != -1)
536 accountName = optarg;
540 return SHOW_USAGE_MESSAGE;
544 serviceName = optarg;
548 return SHOW_USAGE_MESSAGE;
552 result = do_keychain_find_or_delete_generic_password(do_delete,
553 serviceName, accountName, get_password);
559 keychain_find_generic_password(int argc, char * const *argv) {
560 return keychain_find_or_delete_generic_password(0, argc, argv);
564 keychain_delete_generic_password(int argc, char * const *argv) {
565 return keychain_find_or_delete_generic_password(1, argc, argv);
568 int keychain_item_digest(int argc, char * const *argv) {
569 NSString *itemClass = @"inet";
570 NSString *accessGroup = @"com.apple.ProtectedCloudStorage";
572 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
575 itemClass = [NSString stringWithUTF8String:argv[1]];
576 accessGroup = [NSString stringWithUTF8String:argv[2]];
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]);
586 printf("%s\n", [[NSString stringWithFormat:@"Failed to find items (%@/%@): %@", itemClass, accessGroup, error] UTF8String]);
588 dispatch_semaphore_signal(sema);
590 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
596 #if TARGET_OS_EMBEDDED
599 keychain_roll_keys(int argc, char * const *argv) {
603 while ((ch = getopt(argc, argv, "f")) != -1)
611 return SHOW_USAGE_MESSAGE;
617 (void) argc; // These are set so folks could use them
618 (void) argv; // silence the analyzer since they're not used
620 CFErrorRef error = NULL;
621 bool ok = _SecKeychainRollKeys(force, &error);
623 fprintf(stderr, "Keychain keys up to date: %s\n", ok ? "yes" : "no");
625 result = (int)CFErrorGetCode(error);