]> git.saurik.com Git - apple/security.git/blob - sec/Security/Tool/keychain_find.c
Security-55471.14.4.tar.gz
[apple/security.git] / sec / Security / Tool / keychain_find.c
1 //
2 //
3 //
4 //
5
6
7
8 #include <CoreFoundation/CoreFoundation.h>
9
10 #include <Security/SecItem.h>
11
12 #include <SecurityTool/tool_errors.h>
13 #include <SecurityTool/readline.h>
14
15 #include <utilities/SecCFWrappers.h>
16
17 #include "SecurityCommands.h"
18
19 //
20 // Craptastic hacks.
21
22 typedef uint32_t SecProtocolType;
23 typedef uint32_t SecAuthenticationType;
24
25 /* Parse a string of the form attr=value,attr=value,attr=value */
26 static void
27 keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
28 bool inkey = true;
29 bool escaped = false;
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(",\\"));
35 while (rng.length) {
36 CFRange r;
37 CFStringRef sub;
38 bool complete = false;
39 if (escaped) {
40 r.location = rng.location;
41 r.length = 1;
42 sub = CFStringCreateWithSubstring(0, s, r);
43 escaped = false;
44 } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
45 if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
46 escaped = true;
47 } else {
48 complete = true;
49 }
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;
55 rng.location = next;
56 } else {
57 sub = CFStringCreateWithSubstring(0, s, rng);
58 rng.location += rng.length;
59 rng.length = 0;
60 complete = true;
61 }
62 CFStringAppend(str, sub);
63 CFRelease(sub);
64 if (complete) {
65 CFStringRef value = CFStringCreateCopy(0, str);
66 CFStringReplaceAll(str, CFSTR(""));
67 if (inkey) {
68 key = value;
69 } else {
70 CFDictionarySetValue(q, key, value);
71 CFReleaseNull(value);
72 CFReleaseNull(key);
73 }
74 inkey = !inkey;
75 }
76 }
77 if (key) {
78 /* Dangeling key value is true?. */
79 CFDictionarySetValue(q, key, kCFBooleanTrue);
80 CFRelease(key);
81 }
82
83 CFRelease(str);
84 CFReleaseSafe(cs_key);
85 CFReleaseSafe(cs_value);
86 }
87
88 static void
89 keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
90 CFStringRef s;
91 s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
92 keychain_query_parse_string(q, s);
93 CFRelease(s);
94 }
95
96 static CFMutableDictionaryRef
97 keychain_create_query_from_string(const char *query) {
98 CFMutableDictionaryRef q;
99
100 q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
101 keychain_query_parse_cstring(q, query);
102 return q;
103 }
104
105 static void add_key(const void *key, const void *value, void *context) {
106 CFArrayAppendValue(context, key);
107 }
108
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? */
114
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);
122
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);
127
128 CFStringAppend(line, key);
129 CFIndex jx;
130 for (jx = CFStringGetLength(key);
131 jx < maxWidth; ++jx) {
132 CFStringAppend(line, CFSTR(" "));
133 }
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);
147 if (v_s) {
148 CFStringAppend(line, CFSTR("/"));
149 CFStringAppend(line, v_s);
150 CFStringAppend(line, CFSTR("/ "));
151 CFRelease(v_s);
152 }
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]);
157 }
158 } else {
159 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
160 }
161
162 CFStringWriteToFileWithNewline(line, stdout);
163
164 CFRelease(line);
165 }
166 CFRelease(keys);
167
168 CFStringWriteToFileWithNewline(CFSTR("===="), stdout);
169
170 //CFShow(item);
171 }
172
173
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)),
178 display_item, NULL);
179 } else if (CFGetTypeID(results) == CFArrayGetTypeID()) {
180 display_item(results, NULL);
181 } else {
182 fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
183 CFShow(results);
184 }
185 }
186
187 static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) {
188 OSStatus result;
189 if (do_delete) {
190 result = SecItemDelete(query);
191 if (result) {
192 sec_perror("SecItemDelete", result);
193 }
194 } else {
195 CFTypeRef results = NULL;
196 result = SecItemCopyMatching(query, &results);
197 if (result) {
198 sec_perror("SecItemCopyMatching", result);
199 } else {
200 display_results(results);
201 }
202 CFReleaseSafe(results);
203 }
204 return result;
205 }
206
207 static int
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)
213 {
214 OSStatus result;
215 CFDictionaryRef query = NULL;
216 const void *keys[11], *values[11];
217 CFIndex ix = 0;
218
219 keys[ix] = kSecClass;
220 values[ix++] = kSecClassInternetPassword;
221 if (serverName) {
222 keys[ix] = kSecAttrServer;
223 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName,
224 kCFStringEncodingUTF8, kCFAllocatorNull);
225 }
226 if (securityDomain) {
227 keys[ix] = kSecAttrSecurityDomain;
228 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain,
229 kCFStringEncodingUTF8, kCFAllocatorNull);
230 }
231 if (accountName) {
232 keys[ix] = kSecAttrAccount;
233 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
234 kCFStringEncodingUTF8, kCFAllocatorNull);
235 }
236 if (path) {
237 keys[ix] = kSecAttrPath;
238 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path,
239 kCFStringEncodingUTF8, kCFAllocatorNull);
240 }
241 if (port != 0) {
242 keys[ix] = kSecAttrPort;
243 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port);
244 }
245 if (protocol != 0) {
246 /* Protocol is a 4 char code, perhaps we should use a string rep
247 instead. */
248 keys[ix] = kSecAttrProtocol;
249 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol);
250 }
251 if (authenticationType != 0) {
252 keys[ix] = kSecAttrAuthenticationType;
253 values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type,
254 &authenticationType);
255 }
256 if (get_password) {
257 /* Only ask for the data if so required. */
258 keys[ix] = kSecReturnData;
259 values[ix++] = kCFBooleanTrue;
260 }
261 keys[ix] = kSecReturnAttributes;
262 values[ix++] = kCFBooleanTrue;
263 if (!do_delete) {
264 /* If we aren't deleting ask for all items. */
265 keys[ix] = kSecMatchLimit;
266 values[ix++] = kSecMatchLimitAll;
267 }
268
269 query = CFDictionaryCreate(NULL, keys, values, ix,
270 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
271 result = do_find_or_delete(query, do_delete);
272 CFReleaseSafe(query);
273
274 return result;
275 }
276
277 static int
278 parse_fourcharcode(const char *name, uint32_t *code)
279 {
280 /* @@@ Check for errors. */
281 char *p = (char *)code;
282 strncpy(p, name, 4);
283 return 0;
284 }
285
286 static int
287 keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv)
288 {
289 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
290 UInt16 port = 0;
291 SecProtocolType protocol = 0;
292 SecAuthenticationType authenticationType = 0;
293 int ch, result = 0;
294 Boolean get_password = FALSE;
295
296 while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1)
297 {
298 switch (ch)
299 {
300 case 'a':
301 accountName = optarg;
302 break;
303 case 'd':
304 securityDomain = optarg;
305 break;
306 case 'g':
307 if (do_delete)
308 return 2;
309 get_password = TRUE;
310 break;
311 case 'p':
312 path = optarg;
313 break;
314 case 'P':
315 port = atoi(optarg);
316 break;
317 case 'r':
318 result = parse_fourcharcode(optarg, &protocol);
319 if (result)
320 goto loser;
321 break;
322 case 's':
323 serverName = optarg;
324 break;
325 case 't':
326 result = parse_fourcharcode(optarg, &authenticationType);
327 if (result)
328 goto loser;
329 break;
330 case '?':
331 default:
332 return 2; /* @@@ Return 2 triggers usage message. */
333 }
334 }
335
336 argc -= optind;
337 argv += optind;
338
339 result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain,
340 accountName, path, port, protocol,authenticationType, get_password);
341
342
343 loser:
344
345 return result;
346 }
347
348 int
349 keychain_find_internet_password(int argc, char * const *argv) {
350 return keychain_find_or_delete_internet_password(0, argc, argv);
351 }
352
353 int
354 keychain_delete_internet_password(int argc, char * const *argv) {
355 return keychain_find_or_delete_internet_password(1, argc, argv);
356 }
357
358 static int
359 do_keychain_find_or_delete_generic_password(Boolean do_delete,
360 const char *serviceName, const char *accountName,
361 Boolean get_password)
362 {
363 OSStatus result;
364 CFDictionaryRef query = NULL;
365 const void *keys[6], *values[6];
366 CFIndex ix = 0;
367
368 keys[ix] = kSecClass;
369 values[ix++] = kSecClassGenericPassword;
370 if (serviceName) {
371 keys[ix] = kSecAttrService;
372 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName,
373 kCFStringEncodingUTF8, kCFAllocatorNull);
374 }
375 if (accountName) {
376 keys[ix] = kSecAttrAccount;
377 values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
378 kCFStringEncodingUTF8, kCFAllocatorNull);
379 }
380 if (get_password) {
381 /* Only ask for the data if so required. */
382 keys[ix] = kSecReturnData;
383 values[ix++] = kCFBooleanTrue;
384 }
385 keys[ix] = kSecReturnAttributes;
386 values[ix++] = kCFBooleanTrue;
387 if (!do_delete) {
388 /* If we aren't deleting ask for all items. */
389 keys[ix] = kSecMatchLimit;
390 values[ix++] = kSecMatchLimitAll;
391 }
392
393 query = CFDictionaryCreate(NULL, keys, values, ix,
394 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
395
396 result = do_find_or_delete(query, do_delete);
397
398 CFReleaseSafe(query);
399
400 return result;
401 }
402
403 int keychain_item(int argc, char * const *argv) {
404 int ch, result = 0;
405 CFMutableDictionaryRef query, update = NULL;
406 bool get_password = false;
407 bool do_delete = false;
408 bool do_add = false;
409 bool verbose = false;
410 int limit = 0;
411
412 query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
413 while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1)
414 {
415 switch (ch)
416 {
417 case 'a':
418 do_add = true;
419 break;
420 case 'D':
421 do_delete = true;
422 break;
423 case 'd':
424 {
425 CFStringRef data = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
426 if (data) {
427 CFDictionarySetValue(update ? update : query, kSecValueData, data);
428 CFRelease(data);
429 } else {
430 result = 1;
431 goto out;
432 }
433 break;
434 }
435 case 'f':
436 {
437 CFDataRef data = copyFileContents(optarg);
438 CFDictionarySetValue(update ? update : query, kSecValueData, data);
439 CFRelease(data);
440 break;
441 }
442 case 'g':
443 get_password = true;
444 break;
445 case 'q':
446 keychain_query_parse_cstring(query, optarg);
447 break;
448 case 'u':
449 if (!update)
450 update = keychain_create_query_from_string(optarg);
451 else
452 keychain_query_parse_cstring(update, optarg);
453 break;
454 case 'v':
455 verbose = true;
456 break;
457 case 'l':
458 limit = atoi(optarg);
459 break;
460 case '?':
461 default:
462 /* Return 2 triggers usage message. */
463 result = 2;
464 goto out;
465 }
466 }
467
468 if (((do_add || do_delete) && (get_password || update)) || !query) {
469 result = 2;
470 goto out;
471 }
472
473 argc -= optind;
474 argv += optind;
475
476 int ix;
477 for (ix = 0; ix < argc; ++ix) {
478 keychain_query_parse_cstring(query, argv[ix]);
479 }
480
481 if (!update && !do_add && !do_delete) {
482 CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
483 if(limit) {
484 CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
485 CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
486 CFReleaseSafe(cfLimit);
487 } else {
488 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
489 }
490 if (get_password)
491 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
492 }
493
494 if (verbose)
495 CFShow(query);
496
497 OSStatus error;
498 if (do_add) {
499 error = SecItemAdd(query, NULL);
500 if (error) {
501 sec_perror("SecItemAdd", error);
502 result = 1;
503 }
504 } else if (update) {
505 error = SecItemUpdate(query, update);
506 if (error) {
507 sec_perror("SecItemUpdate", error);
508 result = 1;
509 }
510 } else if (do_delete) {
511 error = SecItemDelete(query);
512 if (error) {
513 sec_perror("SecItemDelete", error);
514 result = 1;
515 }
516 } else {
517 do_find_or_delete(query, do_delete);
518 }
519
520 out:
521 CFReleaseSafe(query);
522 CFReleaseSafe(update);
523 return result;
524 }
525
526 static int
527 keychain_find_or_delete_generic_password(Boolean do_delete,
528 int argc, char * const *argv)
529 {
530 char *serviceName = NULL, *accountName = NULL;
531 int ch, result = 0;
532 Boolean get_password = FALSE;
533
534 while ((ch = getopt(argc, argv, "a:s:g")) != -1)
535 {
536 switch (ch)
537 {
538 case 'a':
539 accountName = optarg;
540 break;
541 case 'g':
542 if (do_delete)
543 return 2;
544 get_password = TRUE;
545 break;
546 case 's':
547 serviceName = optarg;
548 break;
549 case '?':
550 default:
551 return 2; /* @@@ Return 2 triggers usage message. */
552 }
553 }
554
555 argc -= optind;
556 argv += optind;
557
558 result = do_keychain_find_or_delete_generic_password(do_delete,
559 serviceName, accountName, get_password);
560
561 return result;
562 }
563
564 int
565 keychain_find_generic_password(int argc, char * const *argv) {
566 return keychain_find_or_delete_generic_password(0, argc, argv);
567 }
568
569 int
570 keychain_delete_generic_password(int argc, char * const *argv) {
571 return keychain_find_or_delete_generic_password(1, argc, argv);
572 }