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
) == CFArrayGetTypeID()) {
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
;
295 result
= parse_fourcharcode(optarg
, &protocol
);
303 result
= parse_fourcharcode(optarg
, &authenticationType
);
309 return 2; /* @@@ Return 2 triggers 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
);
387 while ((ch
= getopt(argc
, argv
, "ad:Df:gq:u:vl:")) != -1)
399 CFStringRef dataString
= CFStringCreateWithCString(0, optarg
, kCFStringEncodingUTF8
);
401 CFDataRef data
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, dataString
, kCFStringEncodingUTF8
, 0);
403 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
406 CFRelease(dataString
);
415 CFDataRef data
= copyFileContents(optarg
);
416 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
424 if (!keychain_query_parse_cstring(query
, optarg
)) {
433 update
= keychain_create_query_from_string(optarg
);
435 success
= keychain_query_parse_cstring(update
, optarg
);
436 if (update
== NULL
|| !success
) {
446 limit
= atoi(optarg
);
450 /* Return 2 triggers usage message. */
456 if (((do_add
|| do_delete
) && (get_password
|| update
)) || !query
) {
465 for (ix
= 0; ix
< argc
; ++ix
) {
466 if (!keychain_query_parse_cstring(query
, argv
[ix
])) {
472 if (!update
&& !do_add
&& !do_delete
) {
473 CFDictionarySetValue(query
, kSecReturnAttributes
, kCFBooleanTrue
);
475 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
476 CFDictionarySetValue(query
, kSecMatchLimit
, cfLimit
);
477 CFReleaseSafe(cfLimit
);
479 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
482 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
490 error
= SecItemAdd(query
, NULL
);
492 sec_perror("SecItemAdd", error
);
496 error
= SecItemUpdate(query
, update
);
498 sec_perror("SecItemUpdate", error
);
501 } else if (do_delete
) {
502 error
= SecItemDelete(query
);
504 sec_perror("SecItemDelete", error
);
508 if (!do_delete
&& CFDictionaryGetValue(query
, kSecUseAuthenticationUI
) == NULL
)
509 CFDictionarySetValue(query
, kSecUseAuthenticationUI
, kSecUseAuthenticationUISkip
);
510 do_find_or_delete(query
, do_delete
);
514 CFReleaseSafe(query
);
515 CFReleaseSafe(update
);
520 keychain_find_or_delete_generic_password(Boolean do_delete
,
521 int argc
, char * const *argv
)
523 char *serviceName
= NULL
, *accountName
= NULL
;
525 Boolean get_password
= FALSE
;
527 while ((ch
= getopt(argc
, argv
, "a:s:g")) != -1)
532 accountName
= optarg
;
540 serviceName
= optarg
;
544 return 2; /* @@@ Return 2 triggers usage message. */
548 result
= do_keychain_find_or_delete_generic_password(do_delete
,
549 serviceName
, accountName
, get_password
);
555 keychain_find_generic_password(int argc
, char * const *argv
) {
556 return keychain_find_or_delete_generic_password(0, argc
, argv
);
560 keychain_delete_generic_password(int argc
, char * const *argv
) {
561 return keychain_find_or_delete_generic_password(1, argc
, argv
);
564 #if TARGET_OS_EMBEDDED
567 keychain_roll_keys(int argc
, char * const *argv
) {
571 while ((ch
= getopt(argc
, argv
, "f")) != -1)
585 (void) argc
; // These are set so folks could use them
586 (void) argv
; // silence the analyzer since they're not used
588 CFErrorRef error
= NULL
;
589 bool ok
= _SecKeychainRollKeys(force
, &error
);
591 fprintf(stderr
, "Keychain keys up to date: %s\n", ok
? "yes" : "no");
593 result
= (int)CFErrorGetCode(error
);