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 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? */
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);
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);
83 CFStringAppend(line
, key
);
85 for (jx
= CFStringGetLength(key
);
86 jx
< maxWidth
; ++jx
) {
87 CFStringAppend(line
, CFSTR(" "));
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
);
103 CFStringAppend(line
, CFSTR("/"));
104 CFStringAppend(line
, v_s
);
105 CFStringAppend(line
, CFSTR("/ "));
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
]);
113 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value
)) {
114 display_sac_line((SecAccessControlRef
)value
, line
);
116 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
119 CFStringWriteToFileWithNewline(line
, stdout
);
125 CFStringWriteToFileWithNewline(CFSTR("===="), stdout
);
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
)),
136 } else if (CFGetTypeID(results
) == CFArrayGetTypeID()) {
137 display_item(results
, NULL
);
139 fprintf(stderr
, "SecItemCopyMatching returned unexpected results:");
144 static OSStatus
do_find_or_delete(CFDictionaryRef query
, bool do_delete
) {
147 result
= SecItemDelete(query
);
149 sec_perror("SecItemDelete", result
);
152 CFTypeRef results
= NULL
;
153 result
= SecItemCopyMatching(query
, &results
);
155 sec_perror("SecItemCopyMatching", result
);
157 display_results(results
);
159 CFReleaseSafe(results
);
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
)
172 CFDictionaryRef query
= NULL
;
173 const void *keys
[11], *values
[11];
176 keys
[ix
] = kSecClass
;
177 values
[ix
++] = kSecClassInternetPassword
;
179 keys
[ix
] = kSecAttrServer
;
180 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serverName
,
181 kCFStringEncodingUTF8
, kCFAllocatorNull
);
183 if (securityDomain
) {
184 keys
[ix
] = kSecAttrSecurityDomain
;
185 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, securityDomain
,
186 kCFStringEncodingUTF8
, kCFAllocatorNull
);
189 keys
[ix
] = kSecAttrAccount
;
190 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
191 kCFStringEncodingUTF8
, kCFAllocatorNull
);
194 keys
[ix
] = kSecAttrPath
;
195 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, path
,
196 kCFStringEncodingUTF8
, kCFAllocatorNull
);
199 keys
[ix
] = kSecAttrPort
;
200 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt16Type
, &port
);
203 /* Protocol is a 4 char code, perhaps we should use a string rep
205 keys
[ix
] = kSecAttrProtocol
;
206 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &protocol
);
208 if (authenticationType
!= 0) {
209 keys
[ix
] = kSecAttrAuthenticationType
;
210 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
,
211 &authenticationType
);
214 /* Only ask for the data if so required. */
215 keys
[ix
] = kSecReturnData
;
216 values
[ix
++] = kCFBooleanTrue
;
218 keys
[ix
] = kSecReturnAttributes
;
219 values
[ix
++] = kCFBooleanTrue
;
221 /* If we aren't deleting ask for all items. */
222 keys
[ix
] = kSecMatchLimit
;
223 values
[ix
++] = kSecMatchLimitAll
;
226 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
227 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
228 result
= do_find_or_delete(query
, do_delete
);
229 CFReleaseSafe(query
);
235 parse_fourcharcode(const char *name
, uint32_t *code
)
237 /* @@@ Check for errors. */
238 char *p
= (char *)code
;
244 keychain_find_or_delete_internet_password(Boolean do_delete
, int argc
, char * const *argv
)
246 char *serverName
= NULL
, *securityDomain
= NULL
, *accountName
= NULL
, *path
= NULL
;
248 SecProtocolType protocol
= 0;
249 SecAuthenticationType authenticationType
= 0;
251 Boolean get_password
= FALSE
;
253 while ((ch
= getopt(argc
, argv
, "a:d:hgp:P:r:s:t:")) != -1)
258 accountName
= optarg
;
261 securityDomain
= optarg
;
275 result
= parse_fourcharcode(optarg
, &protocol
);
283 result
= parse_fourcharcode(optarg
, &authenticationType
);
289 return 2; /* @@@ Return 2 triggers usage message. */
293 result
= do_keychain_find_or_delete_internet_password(do_delete
, serverName
, securityDomain
,
294 accountName
, path
, port
, protocol
,authenticationType
, get_password
);
303 keychain_find_internet_password(int argc
, char * const *argv
) {
304 return keychain_find_or_delete_internet_password(0, argc
, argv
);
308 keychain_delete_internet_password(int argc
, char * const *argv
) {
309 return keychain_find_or_delete_internet_password(1, argc
, argv
);
313 do_keychain_find_or_delete_generic_password(Boolean do_delete
,
314 const char *serviceName
, const char *accountName
,
315 Boolean get_password
)
318 CFDictionaryRef query
= NULL
;
319 const void *keys
[6], *values
[6];
322 keys
[ix
] = kSecClass
;
323 values
[ix
++] = kSecClassGenericPassword
;
325 keys
[ix
] = kSecAttrService
;
326 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serviceName
,
327 kCFStringEncodingUTF8
, kCFAllocatorNull
);
330 keys
[ix
] = kSecAttrAccount
;
331 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
332 kCFStringEncodingUTF8
, kCFAllocatorNull
);
335 /* Only ask for the data if so required. */
336 keys
[ix
] = kSecReturnData
;
337 values
[ix
++] = kCFBooleanTrue
;
339 keys
[ix
] = kSecReturnAttributes
;
340 values
[ix
++] = kCFBooleanTrue
;
342 /* If we aren't deleting ask for all items. */
343 keys
[ix
] = kSecMatchLimit
;
344 values
[ix
++] = kSecMatchLimitAll
;
347 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
348 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
350 result
= do_find_or_delete(query
, do_delete
);
352 CFReleaseSafe(query
);
357 int keychain_item(int argc
, char * const *argv
) {
359 CFMutableDictionaryRef query
, update
= NULL
;
360 bool get_password
= false;
361 bool do_delete
= false;
363 bool verbose
= false;
366 query
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
367 while ((ch
= getopt(argc
, argv
, "ad:Df:gq:u:vl:")) != -1)
379 CFStringRef dataString
= CFStringCreateWithCString(0, optarg
, kCFStringEncodingUTF8
);
381 CFDataRef data
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, dataString
, kCFStringEncodingUTF8
, 0);
383 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
386 CFRelease(dataString
);
395 CFDataRef data
= copyFileContents(optarg
);
396 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
404 if (!keychain_query_parse_cstring(query
, optarg
)) {
413 update
= keychain_create_query_from_string(optarg
);
415 success
= keychain_query_parse_cstring(update
, optarg
);
416 if (update
== NULL
|| !success
) {
426 limit
= atoi(optarg
);
430 /* Return 2 triggers usage message. */
436 if (((do_add
|| do_delete
) && (get_password
|| update
)) || !query
) {
445 for (ix
= 0; ix
< argc
; ++ix
) {
446 if (!keychain_query_parse_cstring(query
, argv
[ix
])) {
452 if (!update
&& !do_add
&& !do_delete
) {
453 CFDictionarySetValue(query
, kSecReturnAttributes
, kCFBooleanTrue
);
455 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
456 CFDictionarySetValue(query
, kSecMatchLimit
, cfLimit
);
457 CFReleaseSafe(cfLimit
);
459 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
462 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
470 error
= SecItemAdd(query
, NULL
);
472 sec_perror("SecItemAdd", error
);
476 error
= SecItemUpdate(query
, update
);
478 sec_perror("SecItemUpdate", error
);
481 } else if (do_delete
) {
482 error
= SecItemDelete(query
);
484 sec_perror("SecItemDelete", error
);
488 if (!do_delete
&& CFDictionaryGetValue(query
, kSecUseAuthenticationUI
) == NULL
)
489 CFDictionarySetValue(query
, kSecUseAuthenticationUI
, kSecUseAuthenticationUISkip
);
490 do_find_or_delete(query
, do_delete
);
494 CFReleaseSafe(query
);
495 CFReleaseSafe(update
);
500 keychain_find_or_delete_generic_password(Boolean do_delete
,
501 int argc
, char * const *argv
)
503 char *serviceName
= NULL
, *accountName
= NULL
;
505 Boolean get_password
= FALSE
;
507 while ((ch
= getopt(argc
, argv
, "a:s:g")) != -1)
512 accountName
= optarg
;
520 serviceName
= optarg
;
524 return 2; /* @@@ Return 2 triggers usage message. */
528 result
= do_keychain_find_or_delete_generic_password(do_delete
,
529 serviceName
, accountName
, get_password
);
535 keychain_find_generic_password(int argc
, char * const *argv
) {
536 return keychain_find_or_delete_generic_password(0, argc
, argv
);
540 keychain_delete_generic_password(int argc
, char * const *argv
) {
541 return keychain_find_or_delete_generic_password(1, argc
, argv
);
544 #if TARGET_OS_EMBEDDED
547 keychain_roll_keys(int argc
, char * const *argv
) {
551 while ((ch
= getopt(argc
, argv
, "f")) != -1)
565 (void) argc
; // These are set so folks could use them
566 (void) argv
; // silence the analyzer since they're not used
568 CFErrorRef error
= NULL
;
569 bool ok
= _SecKeychainRollKeys(force
, &error
);
571 fprintf(stderr
, "Keychain keys up to date: %s\n", ok
? "yes" : "no");
573 result
= (int)CFErrorGetCode(error
);