8 #include <CoreFoundation/CoreFoundation.h>
10 #include <Security/SecItem.h>
11 #include <Security/SecItemPriv.h>
13 #include <SecurityTool/tool_errors.h>
14 #include <SecurityTool/readline.h>
16 #include <utilities/SecCFWrappers.h>
18 #include "SecurityCommands.h"
20 #include "keychain_util.h"
21 #include <Security/SecAccessControl.h>
22 #include <Security/SecAccessControlPriv.h>
27 typedef uint32_t SecProtocolType
;
28 typedef uint32_t SecAuthenticationType
;
31 static CFMutableDictionaryRef
32 keychain_create_query_from_string(const char *query
) {
33 CFMutableDictionaryRef q
;
35 q
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
36 if (!keychain_query_parse_cstring(q
, query
)) {
42 static void add_key(const void *key
, const void *value
, void *context
) {
43 CFArrayAppendValue(context
, key
);
46 static void display_item(const void *v_item
, void *context
) {
47 CFDictionaryRef item
= (CFDictionaryRef
)v_item
;
48 CFIndex dict_count
, key_ix
, key_count
;
49 CFMutableArrayRef keys
= NULL
;
50 CFIndex maxWidth
= 10; /* Maybe precompute this or grab from context? */
52 dict_count
= CFDictionaryGetCount(item
);
53 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
54 &kCFTypeArrayCallBacks
);
55 CFDictionaryApplyFunction(item
, add_key
, keys
);
56 key_count
= CFArrayGetCount(keys
);
57 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
58 (CFComparatorFunction
)CFStringCompare
, 0);
60 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
61 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
62 CFTypeRef value
= CFDictionaryGetValue(item
, key
);
63 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
65 CFStringAppend(line
, key
);
67 for (jx
= CFStringGetLength(key
);
68 jx
< maxWidth
; ++jx
) {
69 CFStringAppend(line
, CFSTR(" "));
71 CFStringAppend(line
, CFSTR(" : "));
72 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
73 CFStringAppend(line
, (CFStringRef
)value
);
74 } else if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
75 CFNumberRef v_n
= (CFNumberRef
)value
;
76 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), v_n
);
77 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
78 CFDateRef v_d
= (CFDateRef
)value
;
79 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), v_d
);
80 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
81 CFDataRef v_d
= (CFDataRef
)value
;
82 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
83 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
85 CFStringAppend(line
, CFSTR("/"));
86 CFStringAppend(line
, v_s
);
87 CFStringAppend(line
, CFSTR("/ "));
90 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
91 CFIndex len
= CFDataGetLength(v_d
);
92 for (jx
= 0; jx
< len
; ++jx
) {
93 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
95 } else if (SecAccessControlGetTypeID() == CFGetTypeID(value
)) {
96 display_sac_line((SecAccessControlRef
)value
, line
);
98 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
101 CFStringWriteToFileWithNewline(line
, stdout
);
107 CFStringWriteToFileWithNewline(CFSTR("===="), stdout
);
113 static void display_results(CFTypeRef results
) {
114 if (CFGetTypeID(results
) == CFArrayGetTypeID()) {
115 CFArrayRef r_a
= (CFArrayRef
)results
;
116 CFArrayApplyFunction(r_a
, CFRangeMake(0, CFArrayGetCount(r_a
)),
118 } else if (CFGetTypeID(results
) == CFArrayGetTypeID()) {
119 display_item(results
, NULL
);
121 fprintf(stderr
, "SecItemCopyMatching returned unexpected results:");
126 static OSStatus
do_find_or_delete(CFDictionaryRef query
, bool do_delete
) {
129 result
= SecItemDelete(query
);
131 sec_perror("SecItemDelete", result
);
134 CFTypeRef results
= NULL
;
135 result
= SecItemCopyMatching(query
, &results
);
137 sec_perror("SecItemCopyMatching", result
);
139 display_results(results
);
141 CFReleaseSafe(results
);
147 do_keychain_find_or_delete_internet_password(Boolean do_delete
,
148 const char *serverName
, const char *securityDomain
,
149 const char *accountName
, const char *path
, UInt16 port
,
150 SecProtocolType protocol
, SecAuthenticationType authenticationType
,
151 Boolean get_password
)
154 CFDictionaryRef query
= NULL
;
155 const void *keys
[11], *values
[11];
158 keys
[ix
] = kSecClass
;
159 values
[ix
++] = kSecClassInternetPassword
;
161 keys
[ix
] = kSecAttrServer
;
162 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serverName
,
163 kCFStringEncodingUTF8
, kCFAllocatorNull
);
165 if (securityDomain
) {
166 keys
[ix
] = kSecAttrSecurityDomain
;
167 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, securityDomain
,
168 kCFStringEncodingUTF8
, kCFAllocatorNull
);
171 keys
[ix
] = kSecAttrAccount
;
172 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
173 kCFStringEncodingUTF8
, kCFAllocatorNull
);
176 keys
[ix
] = kSecAttrPath
;
177 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, path
,
178 kCFStringEncodingUTF8
, kCFAllocatorNull
);
181 keys
[ix
] = kSecAttrPort
;
182 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt16Type
, &port
);
185 /* Protocol is a 4 char code, perhaps we should use a string rep
187 keys
[ix
] = kSecAttrProtocol
;
188 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &protocol
);
190 if (authenticationType
!= 0) {
191 keys
[ix
] = kSecAttrAuthenticationType
;
192 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
,
193 &authenticationType
);
196 /* Only ask for the data if so required. */
197 keys
[ix
] = kSecReturnData
;
198 values
[ix
++] = kCFBooleanTrue
;
200 keys
[ix
] = kSecReturnAttributes
;
201 values
[ix
++] = kCFBooleanTrue
;
203 /* If we aren't deleting ask for all items. */
204 keys
[ix
] = kSecMatchLimit
;
205 values
[ix
++] = kSecMatchLimitAll
;
208 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
209 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
210 result
= do_find_or_delete(query
, do_delete
);
211 CFReleaseSafe(query
);
217 parse_fourcharcode(const char *name
, uint32_t *code
)
219 /* @@@ Check for errors. */
220 char *p
= (char *)code
;
226 keychain_find_or_delete_internet_password(Boolean do_delete
, int argc
, char * const *argv
)
228 char *serverName
= NULL
, *securityDomain
= NULL
, *accountName
= NULL
, *path
= NULL
;
230 SecProtocolType protocol
= 0;
231 SecAuthenticationType authenticationType
= 0;
233 Boolean get_password
= FALSE
;
235 while ((ch
= getopt(argc
, argv
, "a:d:hgp:P:r:s:t:")) != -1)
240 accountName
= optarg
;
243 securityDomain
= optarg
;
257 result
= parse_fourcharcode(optarg
, &protocol
);
265 result
= parse_fourcharcode(optarg
, &authenticationType
);
271 return 2; /* @@@ Return 2 triggers usage message. */
275 result
= do_keychain_find_or_delete_internet_password(do_delete
, serverName
, securityDomain
,
276 accountName
, path
, port
, protocol
,authenticationType
, get_password
);
285 keychain_find_internet_password(int argc
, char * const *argv
) {
286 return keychain_find_or_delete_internet_password(0, argc
, argv
);
290 keychain_delete_internet_password(int argc
, char * const *argv
) {
291 return keychain_find_or_delete_internet_password(1, argc
, argv
);
295 do_keychain_find_or_delete_generic_password(Boolean do_delete
,
296 const char *serviceName
, const char *accountName
,
297 Boolean get_password
)
300 CFDictionaryRef query
= NULL
;
301 const void *keys
[6], *values
[6];
304 keys
[ix
] = kSecClass
;
305 values
[ix
++] = kSecClassGenericPassword
;
307 keys
[ix
] = kSecAttrService
;
308 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serviceName
,
309 kCFStringEncodingUTF8
, kCFAllocatorNull
);
312 keys
[ix
] = kSecAttrAccount
;
313 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
314 kCFStringEncodingUTF8
, kCFAllocatorNull
);
317 /* Only ask for the data if so required. */
318 keys
[ix
] = kSecReturnData
;
319 values
[ix
++] = kCFBooleanTrue
;
321 keys
[ix
] = kSecReturnAttributes
;
322 values
[ix
++] = kCFBooleanTrue
;
324 /* If we aren't deleting ask for all items. */
325 keys
[ix
] = kSecMatchLimit
;
326 values
[ix
++] = kSecMatchLimitAll
;
329 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
330 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
332 result
= do_find_or_delete(query
, do_delete
);
334 CFReleaseSafe(query
);
339 int keychain_item(int argc
, char * const *argv
) {
341 CFMutableDictionaryRef query
, update
= NULL
;
342 bool get_password
= false;
343 bool do_delete
= false;
345 bool verbose
= false;
348 query
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
349 while ((ch
= getopt(argc
, argv
, "ad:Df:gq:u:vl:")) != -1)
361 CFStringRef dataString
= CFStringCreateWithCString(0, optarg
, kCFStringEncodingUTF8
);
363 CFDataRef data
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, dataString
, kCFStringEncodingUTF8
, 0);
365 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
368 CFRelease(dataString
);
377 CFDataRef data
= copyFileContents(optarg
);
378 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
386 if (!keychain_query_parse_cstring(query
, optarg
)) {
395 update
= keychain_create_query_from_string(optarg
);
397 success
= keychain_query_parse_cstring(update
, optarg
);
398 if (update
== NULL
|| !success
) {
408 limit
= atoi(optarg
);
412 /* Return 2 triggers usage message. */
418 if (((do_add
|| do_delete
) && (get_password
|| update
)) || !query
) {
427 for (ix
= 0; ix
< argc
; ++ix
) {
428 if (!keychain_query_parse_cstring(query
, argv
[ix
])) {
434 if (!update
&& !do_add
&& !do_delete
) {
435 CFDictionarySetValue(query
, kSecReturnAttributes
, kCFBooleanTrue
);
437 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
438 CFDictionarySetValue(query
, kSecMatchLimit
, cfLimit
);
439 CFReleaseSafe(cfLimit
);
441 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
444 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
452 error
= SecItemAdd(query
, NULL
);
454 sec_perror("SecItemAdd", error
);
458 error
= SecItemUpdate(query
, update
);
460 sec_perror("SecItemUpdate", error
);
463 } else if (do_delete
) {
464 error
= SecItemDelete(query
);
466 sec_perror("SecItemDelete", error
);
470 do_find_or_delete(query
, do_delete
);
474 CFReleaseSafe(query
);
475 CFReleaseSafe(update
);
480 keychain_find_or_delete_generic_password(Boolean do_delete
,
481 int argc
, char * const *argv
)
483 char *serviceName
= NULL
, *accountName
= NULL
;
485 Boolean get_password
= FALSE
;
487 while ((ch
= getopt(argc
, argv
, "a:s:g")) != -1)
492 accountName
= optarg
;
500 serviceName
= optarg
;
504 return 2; /* @@@ Return 2 triggers usage message. */
508 result
= do_keychain_find_or_delete_generic_password(do_delete
,
509 serviceName
, accountName
, get_password
);
515 keychain_find_generic_password(int argc
, char * const *argv
) {
516 return keychain_find_or_delete_generic_password(0, argc
, argv
);
520 keychain_delete_generic_password(int argc
, char * const *argv
) {
521 return keychain_find_or_delete_generic_password(1, argc
, argv
);
524 #if TARGET_OS_EMBEDDED
527 keychain_roll_keys(int argc
, char * const *argv
) {
531 while ((ch
= getopt(argc
, argv
, "f")) != -1)
545 (void) argc
; // These are set so folks could use them
546 (void) argv
; // silence the analyzer since they're not used
548 CFErrorRef error
= NULL
;
549 bool ok
= _SecKeychainRollKeys(force
, &error
);
551 fprintf(stderr
, "Keychain keys up to date: %s\n", ok
? "yes" : "no");
553 result
= (int)CFErrorGetCode(error
);