]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_find.c
Security-57031.30.12.tar.gz
[apple/security.git] / SecurityTool / keychain_find.c
1 /*
2 * Copyright (c) 2003-2010,2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * keychain_find.c
24 */
25
26 #include "keychain_find.h"
27
28 #include "keychain_utilities.h"
29 #include "readline.h"
30 #include "security.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <libkern/OSByteOrder.h>
37 #include <Security/SecKeychainItem.h>
38 #include <Security/SecKeychainSearch.h>
39 #include <Security/SecCertificate.h>
40 #include <CoreFoundation/CFString.h>
41 #include <ctype.h>
42
43
44 // SecDigestGetData, SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindByEmail
45 #include <Security/SecCertificatePriv.h>
46
47 Boolean gDeleteIt = 0;
48
49 // find_first_generic_password
50 //
51 // Returns a SecKeychainItemRef for the first item
52 // which matches the specified attributes. Caller is
53 // responsible for releasing the item (with CFRelease).
54 //
55 SecKeychainItemRef
56 find_first_generic_password(CFTypeRef keychainOrArray,
57 FourCharCode itemCreator,
58 FourCharCode itemType,
59 const char *kind,
60 const char *value,
61 const char *comment,
62 const char *label,
63 const char *serviceName,
64 const char *accountName)
65 {
66 OSStatus status = noErr;
67 SecKeychainSearchRef searchRef = NULL;
68 SecKeychainItemRef itemRef = NULL;
69
70 SecKeychainAttribute attrs[8]; // maximum number of searchable attributes
71 SecKeychainAttributeList attrList = { 0, attrs };
72
73 // the primary key for a generic password item (i.e. the combination of
74 // attributes which determine whether the item is unique) consists of:
75 // { kSecAccountItemAttr, kSecServiceItemAttr }
76 //
77 // if we have a primary key, we don't need to search on other attributes
78 // (and we don't want to, if non-primary attributes are being updated)
79 Boolean primaryKey = (accountName && serviceName);
80
81 // build the attribute list for searching
82 if ((UInt32)itemCreator != 0 && !primaryKey) {
83 attrs[attrList.count].tag = kSecCreatorItemAttr;
84 attrs[attrList.count].length = sizeof(FourCharCode);
85 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
86 attrList.count++;
87 }
88 if ((UInt32)itemType != 0 && !primaryKey) {
89 attrs[attrList.count].tag = kSecTypeItemAttr;
90 attrs[attrList.count].length = sizeof(FourCharCode);
91 attrs[attrList.count].data = (FourCharCode *)&itemType;
92 attrList.count++;
93 }
94 if (kind != NULL && !primaryKey) {
95 attrs[attrList.count].tag = kSecDescriptionItemAttr;
96 attrs[attrList.count].length = strlen(kind);
97 attrs[attrList.count].data = (void*)kind;
98 attrList.count++;
99 }
100 if (value != NULL && !primaryKey) {
101 attrs[attrList.count].tag = kSecGenericItemAttr;
102 attrs[attrList.count].length = strlen(value);
103 attrs[attrList.count].data = (void*)value;
104 attrList.count++;
105 }
106 if (comment != NULL && !primaryKey) {
107 attrs[attrList.count].tag = kSecCommentItemAttr;
108 attrs[attrList.count].length = strlen(comment);
109 attrs[attrList.count].data = (void*)comment;
110 attrList.count++;
111 }
112 if (label != NULL && !primaryKey) {
113 attrs[attrList.count].tag = kSecLabelItemAttr;
114 attrs[attrList.count].length = strlen(label);
115 attrs[attrList.count].data = (void*)label;
116 attrList.count++;
117 }
118 if (serviceName != NULL) {
119 attrs[attrList.count].tag = kSecServiceItemAttr;
120 attrs[attrList.count].length = strlen(serviceName);
121 attrs[attrList.count].data = (void*)serviceName;
122 attrList.count++;
123 }
124 if (accountName != NULL) {
125 attrs[attrList.count].tag = kSecAccountItemAttr;
126 attrs[attrList.count].length = strlen(accountName);
127 attrs[attrList.count].data = (void*)accountName;
128 attrList.count++;
129 }
130
131 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef);
132 if (status) {
133 sec_perror("SecKeychainSearchCreateFromAttributes", status);
134 goto cleanup;
135 }
136 // we're only interested in the first match, if there is a match at all
137 status = SecKeychainSearchCopyNext(searchRef, &itemRef);
138 if (status) {
139 itemRef = NULL;
140 }
141
142 cleanup:
143 if (searchRef)
144 CFRelease(searchRef);
145
146 return itemRef;
147 }
148
149 // find_first_internet_password
150 //
151 // Returns a SecKeychainItemRef for the first item
152 // which matches the specified attributes. Caller is
153 // responsible for releasing the item (with CFRelease).
154 //
155 SecKeychainItemRef
156 find_first_internet_password(CFTypeRef keychainOrArray,
157 FourCharCode itemCreator,
158 FourCharCode itemType,
159 const char *kind,
160 const char *comment,
161 const char *label,
162 const char *serverName,
163 const char *securityDomain,
164 const char *accountName,
165 const char *path,
166 UInt16 port,
167 SecProtocolType protocol,
168 SecAuthenticationType authenticationType)
169 {
170 OSStatus status = noErr;
171 SecKeychainSearchRef searchRef = NULL;
172 SecKeychainItemRef itemRef = NULL;
173
174 SecKeychainAttribute attrs[12]; // maximum number of searchable attributes
175 SecKeychainAttributeList attrList = { 0, attrs };
176
177 // the primary key for an internet password item (i.e. the combination of
178 // attributes which determine whether the item is unique) consists of:
179 // { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr,
180 // kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr,
181 // kSecPortItemAttr, kSecPathItemAttr }
182 //
183 // if we have a primary key, we don't need to search on other attributes.
184 // (and we don't want to, if non-primary attributes are being updated)
185 Boolean primaryKey = (accountName && securityDomain && serverName &&
186 protocol && authenticationType && port && path);
187
188 // build the attribute list for searching
189 if ((UInt32)itemCreator != 0 && !primaryKey) {
190 attrs[attrList.count].tag = kSecCreatorItemAttr;
191 attrs[attrList.count].length = sizeof(FourCharCode);
192 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
193 attrList.count++;
194 }
195 if ((UInt32)itemType != 0 && !primaryKey) {
196 attrs[attrList.count].tag = kSecTypeItemAttr;
197 attrs[attrList.count].length = sizeof(FourCharCode);
198 attrs[attrList.count].data = (FourCharCode *)&itemType;
199 attrList.count++;
200 }
201 if (kind != NULL && !primaryKey) {
202 attrs[attrList.count].tag = kSecDescriptionItemAttr;
203 attrs[attrList.count].length = strlen(kind);
204 attrs[attrList.count].data = (void*)kind;
205 attrList.count++;
206 }
207 if (comment != NULL && !primaryKey) {
208 attrs[attrList.count].tag = kSecCommentItemAttr;
209 attrs[attrList.count].length = strlen(comment);
210 attrs[attrList.count].data = (void*)comment;
211 attrList.count++;
212 }
213 if (label != NULL && !primaryKey) {
214 attrs[attrList.count].tag = kSecLabelItemAttr;
215 attrs[attrList.count].length = strlen(label);
216 attrs[attrList.count].data = (void*)label;
217 attrList.count++;
218 }
219 if (serverName != NULL) {
220 attrs[attrList.count].tag = kSecServerItemAttr;
221 attrs[attrList.count].length = strlen(serverName);
222 attrs[attrList.count].data = (void*)serverName;
223 attrList.count++;
224 }
225 if (securityDomain != NULL) {
226 attrs[attrList.count].tag = kSecSecurityDomainItemAttr;
227 attrs[attrList.count].length = strlen(securityDomain);
228 attrs[attrList.count].data = (void *)securityDomain;
229 attrList.count++;
230 }
231 if (accountName != NULL) {
232 attrs[attrList.count].tag = kSecAccountItemAttr;
233 attrs[attrList.count].length = strlen(accountName);
234 attrs[attrList.count].data = (void *)accountName;
235 attrList.count++;
236 }
237 if (path != NULL) {
238 attrs[attrList.count].tag = kSecPathItemAttr;
239 attrs[attrList.count].length = strlen(path);
240 attrs[attrList.count].data = (void *)path;
241 attrList.count++;
242 }
243 if (port != 0) {
244 attrs[attrList.count].tag = kSecPortItemAttr;
245 attrs[attrList.count].length = sizeof(UInt16);
246 attrs[attrList.count].data = (UInt16 *)&port;
247 attrList.count++;
248 }
249 if ((UInt32)protocol != 0) {
250 attrs[attrList.count].tag = kSecProtocolItemAttr;
251 attrs[attrList.count].length = sizeof(SecProtocolType);
252 attrs[attrList.count].data = (SecProtocolType *)&protocol;
253 attrList.count++;
254 }
255 if ((UInt32)authenticationType != 0) {
256 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr;
257 attrs[attrList.count].length = sizeof(SecAuthenticationType);
258 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType;
259 attrList.count++;
260 }
261
262 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef);
263 if (status) {
264 sec_perror("SecKeychainSearchCreateFromAttributes", status);
265 goto cleanup;
266 }
267 // we're only interested in the first match, if there is a match at all
268 status = SecKeychainSearchCopyNext(searchRef, &itemRef);
269 if (status) {
270 itemRef = NULL;
271 }
272
273 cleanup:
274 if (searchRef)
275 CFRelease(searchRef);
276
277 return itemRef;
278 }
279
280 // find_unique_certificate
281 //
282 // Returns a SecKeychainItemRef for the certificate
283 // in the specified keychain (or keychain list)
284 // which is a unique match for either the specified name
285 // or SHA-1 hash. If more than one match exists, the
286 // certificate is not unique and none are returned. Caller is
287 // responsible for releasing the item (with CFRelease).
288 //
289 SecKeychainItemRef
290 find_unique_certificate(CFTypeRef keychainOrArray,
291 const char *name,
292 const char *hash)
293 {
294 OSStatus status = noErr;
295 SecKeychainSearchRef searchRef = NULL;
296 SecKeychainItemRef uniqueItemRef = NULL;
297
298 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
299 if (status) {
300 return uniqueItemRef;
301 }
302
303 // check input hash string and convert to data
304 CSSM_DATA hashData = { 0, NULL };
305 if (hash) {
306 CSSM_SIZE len = strlen(hash)/2;
307 hashData.Length = len;
308 hashData.Data = (uint8 *)malloc(hashData.Length);
309 fromHex(hash, &hashData);
310 }
311
312 // filter candidates against the hash (or the name, if no hash provided)
313 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
314 Boolean exactMatch = FALSE;
315
316 CSSM_DATA certData = { 0, NULL };
317 SecKeychainItemRef candidate = NULL;
318
319 while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) {
320 SecCertificateRef cert = (SecCertificateRef)candidate;
321 if (SecCertificateGetData(cert, &certData) != noErr) {
322 safe_CFRelease(&candidate);
323 continue;
324 }
325 if (hash) {
326 uint8 candidate_sha1_hash[20];
327 CSSM_DATA digest;
328 digest.Length = sizeof(candidate_sha1_hash);
329 digest.Data = candidate_sha1_hash;
330 if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) &&
331 (hashData.Length == digest.Length) &&
332 (!memcmp(hashData.Data, digest.Data, digest.Length))) {
333 exactMatch = TRUE;
334 uniqueItemRef = candidate; // currently retained
335 break; // we're done - can't get more exact than this
336 }
337 } else {
338 // copy certificate name
339 CFStringRef nameRef = NULL;
340 if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) {
341 safe_CFRelease(&candidate);
342 continue; // no name, so no match is possible
343 }
344 CFIndex nameLen = CFStringGetLength(nameRef);
345 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
346 char *nameBuf = (char *)malloc(bufLen);
347 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
348 nameBuf[0]=0;
349
350 CFRange find = { kCFNotFound, 0 };
351 if (nameRef && matchRef)
352 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
353 Boolean isExact = (find.location == 0 && find.length == nameLen);
354 if (find.location == kCFNotFound) {
355 free(nameBuf);
356 safe_CFRelease(&nameRef);
357 safe_CFRelease(&candidate);
358 continue; // no match
359 }
360 if (uniqueItemRef) { // got two matches
361 if (exactMatch && !isExact) { // prior is better; ignore this one
362 free(nameBuf);
363 safe_CFRelease(&nameRef);
364 safe_CFRelease(&candidate);
365 continue;
366 }
367 if (exactMatch == isExact) { // same class of match
368 if (CFEqual(uniqueItemRef, candidate)) { // same certificate
369 free(nameBuf);
370 safe_CFRelease(&nameRef);
371 safe_CFRelease(&candidate);
372 continue;
373 }
374 // ambiguity - must fail
375 sec_error("\"%s\" is ambiguous, matches more than one certificate", name);
376 free(nameBuf);
377 safe_CFRelease(&nameRef);
378 safe_CFRelease(&candidate);
379 safe_CFRelease(&uniqueItemRef);
380 break;
381 }
382 safe_CFRelease(&uniqueItemRef); // about to replace with this one
383 }
384 uniqueItemRef = candidate; // currently retained
385 exactMatch = isExact;
386 free(nameBuf);
387 safe_CFRelease(&nameRef);
388 }
389 }
390
391 safe_CFRelease(&searchRef);
392 safe_CFRelease(&matchRef);
393 if (hashData.Data) {
394 free(hashData.Data);
395 }
396
397 return uniqueItemRef;
398 }
399
400 static OSStatus
401 do_password_item_printing( SecKeychainItemRef itemRef,
402 Boolean get_password,
403 Boolean password_stdout)
404 {
405 OSStatus result = noErr;
406 void *passwordData = NULL;
407 UInt32 passwordLength = 0;
408
409 if(get_password) {
410 result = SecKeychainItemCopyContent(itemRef, NULL, NULL, &passwordLength, &passwordData);
411 if(result != noErr) return result;
412 }
413 if(!password_stdout) {
414 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
415 if(get_password) {
416 fputs("password: ", stderr);
417 print_buffer(stderr, passwordLength, passwordData);
418 fputc('\n', stderr);
419 }
420 } else {
421 char *password = (char *) passwordData;
422 int doHex = 0;
423 for(int i=0; i<passwordLength; i++) if(!isprint(password[i])) doHex = 1;
424 if(doHex) {
425 for(int i=0; i<passwordLength; i++) printf("%02x", password[i]);
426 } else {
427 for(int i=0; i<passwordLength; i++) putchar(password[i]);
428 }
429 putchar('\n');
430 }
431
432 if (passwordData) SecKeychainItemFreeContent(NULL, passwordData);
433 return noErr;
434
435 }
436
437 static int
438 do_keychain_find_generic_password(CFTypeRef keychainOrArray,
439 FourCharCode itemCreator,
440 FourCharCode itemType,
441 const char *kind,
442 const char *value,
443 const char *comment,
444 const char *label,
445 const char *serviceName,
446 const char *accountName,
447 Boolean get_password,
448 Boolean password_stdout)
449 {
450 OSStatus result = noErr;
451 SecKeychainItemRef itemRef = NULL;
452
453 itemRef = find_first_generic_password(keychainOrArray,
454 itemCreator,
455 itemType,
456 kind,
457 value,
458 comment,
459 label,
460 serviceName,
461 accountName);
462
463 if(itemRef) {
464 result = do_password_item_printing(itemRef, get_password, password_stdout);
465 } else {
466 result = errSecItemNotFound;
467 sec_perror("SecKeychainSearchCopyNext", result);
468 }
469
470 if (itemRef) CFRelease(itemRef);
471
472 return result;
473 }
474
475 static int
476 do_keychain_delete_generic_password(CFTypeRef keychainOrArray,
477 FourCharCode itemCreator,
478 FourCharCode itemType,
479 const char *kind,
480 const char *value,
481 const char *comment,
482 const char *label,
483 const char *serviceName,
484 const char *accountName)
485 {
486 OSStatus result = noErr;
487 SecKeychainItemRef itemRef = NULL;
488 void *passwordData = NULL;
489
490 itemRef = find_first_generic_password(keychainOrArray,
491 itemCreator,
492 itemType,
493 kind,
494 value,
495 comment,
496 label,
497 serviceName,
498 accountName);
499 if (!itemRef) {
500 result = errSecItemNotFound;
501 sec_perror("SecKeychainSearchCopyNext", result);
502 goto cleanup;
503 }
504
505 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
506
507 result = SecKeychainItemDelete(itemRef);
508
509 fputs("password has been deleted.\n", stderr);
510
511 cleanup:
512 if (passwordData)
513 SecKeychainItemFreeContent(NULL, passwordData);
514 if (itemRef)
515 CFRelease(itemRef);
516
517 return result;
518 }
519
520 static int
521 do_keychain_find_internet_password(CFTypeRef keychainOrArray,
522 FourCharCode itemCreator,
523 FourCharCode itemType,
524 const char *kind,
525 const char *comment,
526 const char *label,
527 const char *serverName,
528 const char *securityDomain,
529 const char *accountName,
530 const char *path,
531 UInt16 port,
532 SecProtocolType protocol,
533 SecAuthenticationType authenticationType,
534 Boolean get_password,
535 Boolean password_stdout)
536 {
537 OSStatus result = noErr;
538 SecKeychainItemRef itemRef = NULL;
539
540 itemRef = find_first_internet_password(keychainOrArray,
541 itemCreator,
542 itemType,
543 kind,
544 comment,
545 label,
546 serverName,
547 securityDomain,
548 accountName,
549 path,
550 port,
551 protocol,
552 authenticationType);
553 if(itemRef) {
554 result = do_password_item_printing(itemRef, get_password, password_stdout);
555 } else {
556 result = errSecItemNotFound;
557 sec_perror("SecKeychainSearchCopyNext", result);
558 }
559
560 return result;
561 }
562
563 static int
564 do_keychain_delete_internet_password(CFTypeRef keychainOrArray,
565 FourCharCode itemCreator,
566 FourCharCode itemType,
567 const char *kind,
568 const char *comment,
569 const char *label,
570 const char *serverName,
571 const char *securityDomain,
572 const char *accountName,
573 const char *path,
574 UInt16 port,
575 SecProtocolType protocol,
576 SecAuthenticationType authenticationType)
577 {
578 OSStatus result = noErr;
579 SecKeychainItemRef itemRef = NULL;
580 void *passwordData = NULL;
581
582 itemRef = find_first_internet_password(keychainOrArray,
583 itemCreator,
584 itemType,
585 kind,
586 comment,
587 label,
588 serverName,
589 securityDomain,
590 accountName,
591 path,
592 port,
593 protocol,
594 authenticationType);
595 if (!itemRef) {
596 result = errSecItemNotFound;
597 sec_perror("SecKeychainSearchCopyNext", result);
598 goto cleanup;
599 }
600
601 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
602
603 result = SecKeychainItemDelete(itemRef);
604
605 fputs("password has been deleted.\n", stderr);
606
607 cleanup:
608 if (passwordData)
609 SecKeychainItemFreeContent(NULL, passwordData);
610 if (itemRef)
611 CFRelease(itemRef);
612
613 return result;
614 }
615
616 static int
617 do_keychain_find_certificate(CFTypeRef keychainOrArray,
618 const char *name,
619 const char *emailAddress,
620 Boolean print_hash,
621 Boolean output_pem,
622 Boolean find_all,
623 Boolean print_email)
624 {
625 OSStatus result = noErr;
626 SecCertificateRef certificateRef = NULL;
627 SecKeychainSearchRef searchRef = NULL;
628 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
629
630 if (find_all && emailAddress) {
631 result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef);
632 if (result) {
633 sec_perror("SecKeychainSearchCreateForCertificateByEmail", result);
634 goto cleanup;
635 }
636 } else {
637 result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
638 if (result) {
639 sec_perror("SecKeychainSearchCreateFromAttributes", result);
640 goto cleanup;
641 }
642 }
643
644 do
645 {
646 if (find_all) {
647 SecKeychainItemRef itemRef = NULL;
648 result = SecKeychainSearchCopyNext(searchRef, &itemRef);
649 if (result == errSecItemNotFound) {
650 result = 0;
651 break;
652 }
653 else if (result) {
654 sec_perror("SecKeychainSearchCopyNext", result);
655 goto cleanup;
656 }
657
658 if (!emailAddress && name) {
659 // match name in common name
660 CFStringRef nameRef = NULL;
661 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
662 safe_CFRelease(&itemRef);
663 continue; // no name, so no match is possible
664 }
665 CFRange find = { kCFNotFound, 0 };
666 if (nameRef && matchRef)
667 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
668 if (find.location == kCFNotFound) {
669 safe_CFRelease(&nameRef);
670 safe_CFRelease(&itemRef);
671 continue; // no match
672 }
673 safe_CFRelease(&nameRef);
674 }
675 safe_CFRelease(&certificateRef);
676 certificateRef = (SecCertificateRef) itemRef;
677 }
678 else { // only want the first match
679 if (emailAddress) {
680 result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef);
681 if (result) {
682 sec_perror("SecCertificateFindByEmail", result);
683 goto cleanup;
684 }
685 } else {
686 SecKeychainItemRef itemRef = NULL;
687 while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) {
688 if (name) {
689 // match name in common name
690 CFStringRef nameRef = NULL;
691 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
692 safe_CFRelease(&itemRef);
693 continue; // no name, so no match is possible
694 }
695 CFRange find = { kCFNotFound, 0 };
696 if (nameRef && matchRef)
697 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
698 if (find.location == kCFNotFound) {
699 safe_CFRelease(&nameRef);
700 safe_CFRelease(&itemRef);
701 continue; // no match
702 }
703 safe_CFRelease(&nameRef);
704 }
705 break; // we have a match!
706 }
707 if (result == errSecItemNotFound) {
708 sec_perror("SecKeychainSearchCopyNext", result);
709 goto cleanup;
710 }
711 certificateRef = (SecCertificateRef) itemRef;
712 }
713 }
714
715 // process the found certificate
716
717 if (print_hash) {
718 uint8 sha1_hash[20];
719 CSSM_DATA data;
720 CSSM_DATA digest;
721 digest.Length = sizeof(sha1_hash);
722 digest.Data = sha1_hash;
723 if ((SecCertificateGetData(certificateRef, &data) == noErr) &&
724 (SecDigestGetData(CSSM_ALGID_SHA1, &digest, &data) == CSSM_OK)) {
725 unsigned int i;
726 uint32 len = digest.Length;
727 uint8 *cp = digest.Data;
728 fprintf(stdout, "SHA-1 hash: ");
729 for(i=0; i<len; i++) {
730 fprintf(stdout, "%02X", ((unsigned char *)cp)[i]);
731 }
732 fprintf(stdout, "\n");
733 }
734 }
735
736 if (print_email)
737 {
738 CFArrayRef emailAddresses = NULL;
739 CFIndex ix, count;
740 result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses);
741 if (result)
742 {
743 sec_perror("SecCertificateCopyEmailAddresses", result);
744 goto cleanup;
745 }
746
747 count = CFArrayGetCount(emailAddresses);
748 fputs("email addresses: ", stdout);
749 for (ix = 0; ix < count; ++ix)
750 {
751 CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix);
752 const char *addr;
753 char buffer[256];
754
755 if (ix)
756 fputs(", ", stdout);
757
758 addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8);
759 if (!addr)
760 {
761 if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8))
762 addr = buffer;
763 }
764
765 fprintf(stdout, "%s", addr);
766 }
767 fputc('\n', stdout);
768
769 CFRelease(emailAddresses);
770 }
771
772 if (output_pem)
773 {
774 CSSM_DATA certData = {};
775 result = SecCertificateGetData(certificateRef, &certData);
776 if (result)
777 {
778 sec_perror("SecCertificateGetData", result);
779 goto cleanup;
780 }
781
782 print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
783 }
784 else
785 {
786 print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE);
787 }
788 } while (find_all);
789
790 cleanup:
791 safe_CFRelease(&searchRef);
792 safe_CFRelease(&certificateRef);
793 safe_CFRelease(&matchRef);
794
795 return result;
796 }
797
798 int
799 keychain_delete_internet_password(int argc, char * const *argv)
800 {
801 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
802 char *kind = NULL, *label = NULL, *comment = NULL;
803 FourCharCode itemCreator = 0, itemType = 0;
804 UInt16 port = 0;
805 SecProtocolType protocol = 0;
806 SecAuthenticationType authenticationType = 0;
807 CFTypeRef keychainOrArray = NULL;
808 int ch, result = 0;
809
810 /*
811 * " -a Match \"account\" string\n"
812 * " -c Match \"creator\" (four-character code)\n"
813 * " -C Match \"type\" (four-character code)\n"
814 * " -d Match \"securityDomain\" string\n"
815 * " -D Match \"kind\" string\n"
816 * " -j Match \"comment\" string\n"
817 * " -l Match \"label\" string\n"
818 * " -p Match \"path\" string\n"
819 * " -P Match port number\n"
820 * " -r Match \"protocol\" (four-character code)\n"
821 * " -s Match \"server\" string\n"
822 * " -t Match \"authenticationType\" (four-character code)\n"
823 */
824
825 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:t:")) != -1)
826 {
827 switch (ch)
828 {
829 case 'a':
830 accountName = optarg;
831 break;
832 case 'c':
833 result = parse_fourcharcode(optarg, &itemCreator);
834 if (result) goto cleanup;
835 break;
836 case 'C':
837 result = parse_fourcharcode(optarg, &itemType);
838 if (result) goto cleanup;
839 break;
840 case 'd':
841 securityDomain = optarg;
842 break;
843 case 'D':
844 kind = optarg;
845 break;
846 case 'j':
847 comment = optarg;
848 break;
849 case 'l':
850 label = optarg;
851 break;
852 case 'p':
853 path = optarg;
854 break;
855 case 'P':
856 port = atoi(optarg);
857 break;
858 case 'r':
859 result = parse_fourcharcode(optarg, &protocol);
860 if (result) goto cleanup;
861 break;
862 case 's':
863 serverName = optarg;
864 break;
865 case 't':
866 result = parse_fourcharcode(optarg, &authenticationType);
867 if (result) goto cleanup;
868 break;
869 case '?':
870 default:
871 result = 2; /* @@@ Return 2 triggers usage message. */
872 goto cleanup;
873 }
874 }
875
876 argc -= optind;
877 argv += optind;
878
879 keychainOrArray = keychain_create_array(argc, argv);
880
881 result = do_keychain_delete_internet_password(keychainOrArray,
882 itemCreator,
883 itemType,
884 kind,
885 comment,
886 label,
887 serverName,
888 securityDomain,
889 accountName,
890 path,
891 port,
892 protocol,
893 authenticationType);
894 cleanup:
895 if (keychainOrArray)
896 CFRelease(keychainOrArray);
897
898 return result;
899 }
900
901 int
902 keychain_find_internet_password(int argc, char * const *argv)
903 {
904 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
905 char *kind = NULL, *label = NULL, *comment = NULL;
906 FourCharCode itemCreator = 0, itemType = 0;
907 UInt16 port = 0;
908 SecProtocolType protocol = 0;
909 SecAuthenticationType authenticationType = 0;
910 CFTypeRef keychainOrArray = NULL;
911 int ch, result = 0;
912 Boolean get_password = FALSE;
913 Boolean password_stdout = FALSE;
914
915 /*
916 * " -a Match \"account\" string\n"
917 * " -c Match \"creator\" (four-character code)\n"
918 * " -C Match \"type\" (four-character code)\n"
919 * " -d Match \"securityDomain\" string\n"
920 * " -D Match \"kind\" string\n"
921 * " -j Match \"comment\" string\n"
922 * " -l Match \"label\" string\n"
923 * " -p Match \"path\" string\n"
924 * " -P Match port number\n"
925 * " -r Match \"protocol\" (four-character code)\n"
926 * " -s Match \"server\" string\n"
927 * " -t Match \"authenticationType\" (four-character code)\n"
928 * " -g Display the password for the item found\n"
929 * " -w Display the password(only) for the item(s) found\n"
930 */
931
932 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:wt:")) != -1)
933 {
934 switch (ch)
935 {
936 case 'a':
937 accountName = optarg;
938 break;
939 case 'c':
940 result = parse_fourcharcode(optarg, &itemCreator);
941 if (result) goto cleanup;
942 break;
943 case 'C':
944 result = parse_fourcharcode(optarg, &itemType);
945 if (result) goto cleanup;
946 break;
947 case 'd':
948 securityDomain = optarg;
949 break;
950 case 'D':
951 kind = optarg;
952 break;
953 case 'j':
954 comment = optarg;
955 break;
956 case 'l':
957 label = optarg;
958 break;
959 case 'g':
960 get_password = TRUE;
961 break;
962 case 'p':
963 path = optarg;
964 break;
965 case 'P':
966 port = atoi(optarg);
967 break;
968 case 'r':
969 result = parse_fourcharcode(optarg, &protocol);
970 if (result) goto cleanup;
971 break;
972 case 's':
973 serverName = optarg;
974 break;
975 case 'w':
976 get_password = TRUE;
977 password_stdout = TRUE;
978 break;
979 case 't':
980 result = parse_fourcharcode(optarg, &authenticationType);
981 if (result) goto cleanup;
982 /* auth type attribute is special */
983 authenticationType = OSSwapHostToBigInt32(authenticationType);
984 break;
985 case '?':
986 default:
987 result = 2; /* @@@ Return 2 triggers usage message. */
988 goto cleanup;
989 }
990 }
991
992 argc -= optind;
993 argv += optind;
994
995 keychainOrArray = keychain_create_array(argc, argv);
996
997 result = do_keychain_find_internet_password(keychainOrArray,
998 itemCreator,
999 itemType,
1000 kind,
1001 comment,
1002 label,
1003 serverName,
1004 securityDomain,
1005 accountName,
1006 path,
1007 port,
1008 protocol,
1009 authenticationType,
1010 get_password,
1011 password_stdout);
1012 cleanup:
1013 if (keychainOrArray)
1014 CFRelease(keychainOrArray);
1015
1016 return result;
1017 }
1018
1019 int
1020 keychain_delete_generic_password(int argc, char * const *argv)
1021 {
1022 char *serviceName = NULL, *accountName = NULL;
1023 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
1024 FourCharCode itemCreator = 0, itemType = 0;
1025 CFTypeRef keychainOrArray = nil;
1026 int ch, result = 0;
1027
1028 /*
1029 * " -a Match \"account\" string\n"
1030 * " -c Match \"creator\" (four-character code)\n"
1031 * " -C Match \"type\" (four-character code)\n"
1032 * " -D Match \"kind\" string\n"
1033 * " -G Match \"value\" string (generic attribute)\n"
1034 * " -j Match \"comment\" string\n"
1035 * " -l Match \"label\" string\n"
1036 * " -s Match \"service\" string\n"
1037 */
1038
1039 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:g")) != -1)
1040 {
1041 switch (ch)
1042 {
1043 case 'a':
1044 accountName = optarg;
1045 break;
1046 case 'c':
1047 result = parse_fourcharcode(optarg, &itemCreator);
1048 if (result) goto cleanup;
1049 break;
1050 case 'C':
1051 result = parse_fourcharcode(optarg, &itemType);
1052 if (result) goto cleanup;
1053 break;
1054 case 'D':
1055 kind = optarg;
1056 break;
1057 case 'G':
1058 value = optarg;
1059 break;
1060 case 'j':
1061 comment = optarg;
1062 break;
1063 case 'l':
1064 label = optarg;
1065 break;
1066 case 's':
1067 serviceName = optarg;
1068 break;
1069 case '?':
1070 default:
1071 result = 2; /* @@@ Return 2 triggers usage message. */
1072 goto cleanup;
1073 }
1074 }
1075
1076 argc -= optind;
1077 argv += optind;
1078
1079 keychainOrArray = keychain_create_array(argc, argv);
1080
1081 result = do_keychain_delete_generic_password(keychainOrArray,
1082 itemCreator,
1083 itemType,
1084 kind,
1085 value,
1086 comment,
1087 label,
1088 serviceName,
1089 accountName);
1090 cleanup:
1091 if (keychainOrArray)
1092 CFRelease(keychainOrArray);
1093
1094 return result;
1095 }
1096
1097 int
1098 keychain_find_generic_password(int argc, char * const *argv)
1099 {
1100 char *serviceName = NULL, *accountName = NULL;
1101 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
1102 FourCharCode itemCreator = 0, itemType = 0;
1103 CFTypeRef keychainOrArray = nil;
1104 int ch, result = 0;
1105 Boolean get_password = FALSE;
1106 Boolean password_stdout = FALSE;
1107
1108 /*
1109 * " -a Match \"account\" string\n"
1110 * " -c Match \"creator\" (four-character code)\n"
1111 * " -C Match \"type\" (four-character code)\n"
1112 * " -D Match \"kind\" string\n"
1113 * " -G Match \"value\" string (generic attribute)\n"
1114 * " -j Match \"comment\" string\n"
1115 * " -l Match \"label\" string\n"
1116 * " -s Match \"service\" string\n"
1117 * " -g Display the password for the item(s) found\n"
1118 * " -w Display the password(only) for the item(s) found\n"
1119 */
1120
1121 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:wg")) != -1)
1122 {
1123 switch (ch)
1124 {
1125 case 'a':
1126 accountName = optarg;
1127 break;
1128 case 'c':
1129 result = parse_fourcharcode(optarg, &itemCreator);
1130 if (result) goto cleanup;
1131 break;
1132 case 'C':
1133 result = parse_fourcharcode(optarg, &itemType);
1134 if (result) goto cleanup;
1135 break;
1136 case 'D':
1137 kind = optarg;
1138 break;
1139 case 'G':
1140 value = optarg;
1141 break;
1142 case 'j':
1143 comment = optarg;
1144 break;
1145 case 'l':
1146 label = optarg;
1147 break;
1148 case 's':
1149 serviceName = optarg;
1150 break;
1151 case 'w':
1152 password_stdout = TRUE;
1153 get_password = TRUE;
1154 break;
1155 case 'g':
1156 get_password = TRUE;
1157 break;
1158 case '?':
1159 default:
1160 result = 2; /* @@@ Return 2 triggers usage message. */
1161 goto cleanup;
1162 }
1163 }
1164
1165 argc -= optind;
1166 argv += optind;
1167
1168 keychainOrArray = keychain_create_array(argc, argv);
1169
1170 result = do_keychain_find_generic_password(keychainOrArray,
1171 itemCreator,
1172 itemType,
1173 kind,
1174 value,
1175 comment,
1176 label,
1177 serviceName,
1178 accountName,
1179 get_password,
1180 password_stdout);
1181 cleanup:
1182 if (keychainOrArray)
1183 CFRelease(keychainOrArray);
1184
1185 return result;
1186 }
1187
1188
1189 int
1190 keychain_find_certificate(int argc, char * const *argv)
1191 {
1192 char *emailAddress = NULL;
1193 char *name = NULL;
1194 int ch, result = 0;
1195 CFTypeRef keychainOrArray = nil;
1196 Boolean output_pem = FALSE;
1197 Boolean find_all = FALSE;
1198 Boolean print_hash = FALSE;
1199 Boolean print_email = FALSE;
1200
1201 while ((ch = getopt(argc, argv, "hac:e:mpZ")) != -1)
1202 {
1203 switch (ch)
1204 {
1205 case 'a':
1206 find_all = TRUE;
1207 break;
1208 case 'c':
1209 name = optarg;
1210 break;
1211 case 'e':
1212 emailAddress = optarg;
1213 break;
1214 case 'm':
1215 print_email = TRUE;
1216 break;
1217 case 'p':
1218 output_pem = TRUE;
1219 break;
1220 case 'Z':
1221 print_hash = TRUE;
1222 break;
1223 case '?':
1224 default:
1225 result = 2; /* @@@ Return 2 triggers usage message. */
1226 goto cleanup;
1227 }
1228 }
1229
1230 argc -= optind;
1231 argv += optind;
1232
1233 keychainOrArray = keychain_create_array(argc, argv);
1234
1235 result = do_keychain_find_certificate(keychainOrArray, name, emailAddress, print_hash, output_pem, find_all, print_email);
1236
1237 cleanup:
1238 if (keychainOrArray)
1239 CFRelease(keychainOrArray);
1240
1241 return result;
1242 }
1243
1244
1245 static int
1246 do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
1247 {
1248 SecKeychainItemRef item;
1249 SecKeychainSearchRef search = NULL;
1250 int result = 0;
1251 OSStatus status;
1252
1253 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search);
1254 if (status)
1255 {
1256 sec_perror("SecKeychainSearchCreateFromAttributes", status);
1257 result = 1;
1258 goto cleanup;
1259 }
1260
1261 while ((status = SecKeychainSearchCopyNext(search, &item)) == 0)
1262 {
1263 print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive);
1264 CFRelease(item);
1265 }
1266
1267 if (status != errSecItemNotFound)
1268 {
1269 sec_perror("SecKeychainSearchCopyNext", status);
1270 result = 1;
1271 goto cleanup;
1272 }
1273
1274 cleanup:
1275 if (search)
1276 CFRelease(search);
1277
1278 return result;
1279 }
1280
1281 static int
1282 do_keychain_dump(FILE *stream, CFTypeRef keychainOrArray, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
1283 {
1284 return do_keychain_dump_class(stream, keychainOrArray, CSSM_DL_DB_RECORD_ANY, show_data, show_raw_data, show_acl, interactive);
1285 }
1286
1287 int
1288 keychain_dump(int argc, char * const *argv)
1289 {
1290 int ch, result = 0;
1291 Boolean show_data = FALSE, show_raw_data = FALSE, show_acl = FALSE, interactive = FALSE;
1292 CFTypeRef keychainOrArray = NULL;
1293 const char *outputFilename = NULL;
1294 FILE *output;
1295
1296 while ((ch = getopt(argc, argv, "adhiro:")) != -1)
1297 {
1298 switch (ch)
1299 {
1300 case 'a':
1301 show_acl = TRUE;
1302 break;
1303 case 'd':
1304 show_data = TRUE;
1305 break;
1306 case 'i':
1307 show_acl = TRUE;
1308 interactive = TRUE;
1309 break;
1310 case 'r':
1311 show_raw_data = TRUE;
1312 break;
1313 case 'o':
1314 outputFilename = optarg;
1315 break;
1316 case '?':
1317 default:
1318 return 2; /* @@@ Return 2 triggers usage message. */
1319 }
1320 }
1321
1322 argc -= optind;
1323 argv += optind;
1324
1325 keychainOrArray = keychain_create_array(argc, argv);
1326
1327 if (outputFilename)
1328 output = fopen(outputFilename, "w");
1329 else
1330 output = stdout;
1331
1332 result = do_keychain_dump(output, keychainOrArray, show_data, show_raw_data, show_acl, interactive);
1333
1334 if (outputFilename)
1335 fclose(output);
1336
1337 if (keychainOrArray)
1338 CFRelease(keychainOrArray);
1339
1340 return result;
1341 }