8 #include <CoreFoundation/CoreFoundation.h>
10 #include <Security/SecItem.h>
12 #include <SecurityTool/tool_errors.h>
13 #include <SecurityTool/readline.h>
15 #include <utilities/SecCFWrappers.h>
17 #include "SecurityCommands.h"
22 typedef uint32_t SecProtocolType
;
23 typedef uint32_t SecAuthenticationType
;
25 /* Parse a string of the form attr=value,attr=value,attr=value */
27 keychain_query_parse_string(CFMutableDictionaryRef q
, CFStringRef s
) {
30 CFStringRef key
= NULL
;
31 CFMutableStringRef str
= CFStringCreateMutable(0, 0);
32 CFRange rng
= { .location
= 0, .length
= CFStringGetLength(s
) };
33 CFCharacterSetRef cs_key
= CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
34 CFCharacterSetRef cs_value
= CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
38 bool complete
= false;
40 r
.location
= rng
.location
;
42 sub
= CFStringCreateWithSubstring(0, s
, r
);
44 } else if (CFStringFindCharacterFromSet(s
, inkey
? cs_key
: cs_value
, rng
, 0, &r
)) {
45 if (CFStringGetCharacterAtIndex(s
, r
.location
) == '\\') {
50 CFIndex next
= r
.location
+ 1;
51 r
.length
= r
.location
- rng
.location
;
52 r
.location
= rng
.location
;
53 sub
= CFStringCreateWithSubstring(0, s
, r
);
54 rng
.length
-= next
- rng
.location
;
57 sub
= CFStringCreateWithSubstring(0, s
, rng
);
58 rng
.location
+= rng
.length
;
62 CFStringAppend(str
, sub
);
65 CFStringRef value
= CFStringCreateCopy(0, str
);
66 CFStringReplaceAll(str
, CFSTR(""));
70 CFDictionarySetValue(q
, key
, value
);
78 /* Dangeling key value is true?. */
79 CFDictionarySetValue(q
, key
, kCFBooleanTrue
);
84 CFReleaseSafe(cs_key
);
85 CFReleaseSafe(cs_value
);
89 keychain_query_parse_cstring(CFMutableDictionaryRef q
, const char *query
) {
91 s
= CFStringCreateWithCStringNoCopy(0, query
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
92 keychain_query_parse_string(q
, s
);
96 static CFMutableDictionaryRef
97 keychain_create_query_from_string(const char *query
) {
98 CFMutableDictionaryRef q
;
100 q
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
101 keychain_query_parse_cstring(q
, query
);
105 static void add_key(const void *key
, const void *value
, void *context
) {
106 CFArrayAppendValue(context
, key
);
109 static void display_item(const void *v_item
, void *context
) {
110 CFDictionaryRef item
= (CFDictionaryRef
)v_item
;
111 CFIndex dict_count
, key_ix
, key_count
;
112 CFMutableArrayRef keys
= NULL
;
113 CFIndex maxWidth
= 10; /* Maybe precompute this or grab from context? */
115 dict_count
= CFDictionaryGetCount(item
);
116 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
117 &kCFTypeArrayCallBacks
);
118 CFDictionaryApplyFunction(item
, add_key
, keys
);
119 key_count
= CFArrayGetCount(keys
);
120 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
121 (CFComparatorFunction
)CFStringCompare
, 0);
123 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
124 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
125 CFTypeRef value
= CFDictionaryGetValue(item
, key
);
126 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
128 CFStringAppend(line
, key
);
130 for (jx
= CFStringGetLength(key
);
131 jx
< maxWidth
; ++jx
) {
132 CFStringAppend(line
, CFSTR(" "));
134 CFStringAppend(line
, CFSTR(" : "));
135 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
136 CFStringAppend(line
, (CFStringRef
)value
);
137 } else if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
138 CFNumberRef v_n
= (CFNumberRef
)value
;
139 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), v_n
);
140 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
141 CFDateRef v_d
= (CFDateRef
)value
;
142 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), v_d
);
143 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
144 CFDataRef v_d
= (CFDataRef
)value
;
145 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
146 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
148 CFStringAppend(line
, CFSTR("/"));
149 CFStringAppend(line
, v_s
);
150 CFStringAppend(line
, CFSTR("/ "));
153 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
154 CFIndex len
= CFDataGetLength(v_d
);
155 for (jx
= 0; jx
< len
; ++jx
) {
156 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
159 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
162 CFStringWriteToFileWithNewline(line
, stdout
);
168 CFStringWriteToFileWithNewline(CFSTR("===="), stdout
);
174 static void display_results(CFTypeRef results
) {
175 if (CFGetTypeID(results
) == CFArrayGetTypeID()) {
176 CFArrayRef r_a
= (CFArrayRef
)results
;
177 CFArrayApplyFunction(r_a
, CFRangeMake(0, CFArrayGetCount(r_a
)),
179 } else if (CFGetTypeID(results
) == CFArrayGetTypeID()) {
180 display_item(results
, NULL
);
182 fprintf(stderr
, "SecItemCopyMatching returned unexpected results:");
187 static OSStatus
do_find_or_delete(CFDictionaryRef query
, bool do_delete
) {
190 result
= SecItemDelete(query
);
192 sec_perror("SecItemDelete", result
);
195 CFTypeRef results
= NULL
;
196 result
= SecItemCopyMatching(query
, &results
);
198 sec_perror("SecItemCopyMatching", result
);
200 display_results(results
);
202 CFReleaseSafe(results
);
208 do_keychain_find_or_delete_internet_password(Boolean do_delete
,
209 const char *serverName
, const char *securityDomain
,
210 const char *accountName
, const char *path
, UInt16 port
,
211 SecProtocolType protocol
, SecAuthenticationType authenticationType
,
212 Boolean get_password
)
215 CFDictionaryRef query
= NULL
;
216 const void *keys
[11], *values
[11];
219 keys
[ix
] = kSecClass
;
220 values
[ix
++] = kSecClassInternetPassword
;
222 keys
[ix
] = kSecAttrServer
;
223 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serverName
,
224 kCFStringEncodingUTF8
, kCFAllocatorNull
);
226 if (securityDomain
) {
227 keys
[ix
] = kSecAttrSecurityDomain
;
228 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, securityDomain
,
229 kCFStringEncodingUTF8
, kCFAllocatorNull
);
232 keys
[ix
] = kSecAttrAccount
;
233 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
234 kCFStringEncodingUTF8
, kCFAllocatorNull
);
237 keys
[ix
] = kSecAttrPath
;
238 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, path
,
239 kCFStringEncodingUTF8
, kCFAllocatorNull
);
242 keys
[ix
] = kSecAttrPort
;
243 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt16Type
, &port
);
246 /* Protocol is a 4 char code, perhaps we should use a string rep
248 keys
[ix
] = kSecAttrProtocol
;
249 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &protocol
);
251 if (authenticationType
!= 0) {
252 keys
[ix
] = kSecAttrAuthenticationType
;
253 values
[ix
++] = CFNumberCreate(NULL
, kCFNumberSInt32Type
,
254 &authenticationType
);
257 /* Only ask for the data if so required. */
258 keys
[ix
] = kSecReturnData
;
259 values
[ix
++] = kCFBooleanTrue
;
261 keys
[ix
] = kSecReturnAttributes
;
262 values
[ix
++] = kCFBooleanTrue
;
264 /* If we aren't deleting ask for all items. */
265 keys
[ix
] = kSecMatchLimit
;
266 values
[ix
++] = kSecMatchLimitAll
;
269 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
270 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
271 result
= do_find_or_delete(query
, do_delete
);
272 CFReleaseSafe(query
);
278 parse_fourcharcode(const char *name
, uint32_t *code
)
280 /* @@@ Check for errors. */
281 char *p
= (char *)code
;
287 keychain_find_or_delete_internet_password(Boolean do_delete
, int argc
, char * const *argv
)
289 char *serverName
= NULL
, *securityDomain
= NULL
, *accountName
= NULL
, *path
= NULL
;
291 SecProtocolType protocol
= 0;
292 SecAuthenticationType authenticationType
= 0;
294 Boolean get_password
= FALSE
;
296 while ((ch
= getopt(argc
, argv
, "a:d:hgp:P:r:s:t:")) != -1)
301 accountName
= optarg
;
304 securityDomain
= optarg
;
318 result
= parse_fourcharcode(optarg
, &protocol
);
326 result
= parse_fourcharcode(optarg
, &authenticationType
);
332 return 2; /* @@@ Return 2 triggers usage message. */
339 result
= do_keychain_find_or_delete_internet_password(do_delete
, serverName
, securityDomain
,
340 accountName
, path
, port
, protocol
,authenticationType
, get_password
);
349 keychain_find_internet_password(int argc
, char * const *argv
) {
350 return keychain_find_or_delete_internet_password(0, argc
, argv
);
354 keychain_delete_internet_password(int argc
, char * const *argv
) {
355 return keychain_find_or_delete_internet_password(1, argc
, argv
);
359 do_keychain_find_or_delete_generic_password(Boolean do_delete
,
360 const char *serviceName
, const char *accountName
,
361 Boolean get_password
)
364 CFDictionaryRef query
= NULL
;
365 const void *keys
[6], *values
[6];
368 keys
[ix
] = kSecClass
;
369 values
[ix
++] = kSecClassGenericPassword
;
371 keys
[ix
] = kSecAttrService
;
372 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, serviceName
,
373 kCFStringEncodingUTF8
, kCFAllocatorNull
);
376 keys
[ix
] = kSecAttrAccount
;
377 values
[ix
++] = CFStringCreateWithCStringNoCopy(NULL
, accountName
,
378 kCFStringEncodingUTF8
, kCFAllocatorNull
);
381 /* Only ask for the data if so required. */
382 keys
[ix
] = kSecReturnData
;
383 values
[ix
++] = kCFBooleanTrue
;
385 keys
[ix
] = kSecReturnAttributes
;
386 values
[ix
++] = kCFBooleanTrue
;
388 /* If we aren't deleting ask for all items. */
389 keys
[ix
] = kSecMatchLimit
;
390 values
[ix
++] = kSecMatchLimitAll
;
393 query
= CFDictionaryCreate(NULL
, keys
, values
, ix
,
394 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
396 result
= do_find_or_delete(query
, do_delete
);
398 CFReleaseSafe(query
);
403 int keychain_item(int argc
, char * const *argv
) {
405 CFMutableDictionaryRef query
, update
= NULL
;
406 bool get_password
= false;
407 bool do_delete
= false;
409 bool verbose
= false;
412 query
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
413 while ((ch
= getopt(argc
, argv
, "ad:Df:gq:u:vl:")) != -1)
425 CFStringRef data
= CFStringCreateWithCString(0, optarg
, kCFStringEncodingUTF8
);
427 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
437 CFDataRef data
= copyFileContents(optarg
);
438 CFDictionarySetValue(update
? update
: query
, kSecValueData
, data
);
446 keychain_query_parse_cstring(query
, optarg
);
450 update
= keychain_create_query_from_string(optarg
);
452 keychain_query_parse_cstring(update
, optarg
);
458 limit
= atoi(optarg
);
462 /* Return 2 triggers usage message. */
468 if (((do_add
|| do_delete
) && (get_password
|| update
)) || !query
) {
477 for (ix
= 0; ix
< argc
; ++ix
) {
478 keychain_query_parse_cstring(query
, argv
[ix
]);
481 if (!update
&& !do_add
&& !do_delete
) {
482 CFDictionarySetValue(query
, kSecReturnAttributes
, kCFBooleanTrue
);
484 CFNumberRef cfLimit
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &limit
);
485 CFDictionarySetValue(query
, kSecMatchLimit
, cfLimit
);
486 CFReleaseSafe(cfLimit
);
488 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
491 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
499 error
= SecItemAdd(query
, NULL
);
501 sec_perror("SecItemAdd", error
);
505 error
= SecItemUpdate(query
, update
);
507 sec_perror("SecItemUpdate", error
);
510 } else if (do_delete
) {
511 error
= SecItemDelete(query
);
513 sec_perror("SecItemDelete", error
);
517 do_find_or_delete(query
, do_delete
);
521 CFReleaseSafe(query
);
522 CFReleaseSafe(update
);
527 keychain_find_or_delete_generic_password(Boolean do_delete
,
528 int argc
, char * const *argv
)
530 char *serviceName
= NULL
, *accountName
= NULL
;
532 Boolean get_password
= FALSE
;
534 while ((ch
= getopt(argc
, argv
, "a:s:g")) != -1)
539 accountName
= optarg
;
547 serviceName
= optarg
;
551 return 2; /* @@@ Return 2 triggers usage message. */
558 result
= do_keychain_find_or_delete_generic_password(do_delete
,
559 serviceName
, accountName
, get_password
);
565 keychain_find_generic_password(int argc
, char * const *argv
) {
566 return keychain_find_or_delete_generic_password(0, argc
, argv
);
570 keychain_delete_generic_password(int argc
, char * const *argv
) {
571 return keychain_find_or_delete_generic_password(1, argc
, argv
);