]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_find.c
Security-57740.31.2.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_tool.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/SecACL.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemPriv.h>
40 #include <Security/SecKeychainItem.h>
41 #include <Security/SecKeychainItemPriv.h>
42 #include <Security/SecKeychainSearch.h>
43 #include <Security/SecCertificate.h>
44 #include <CoreFoundation/CFString.h>
45 #include <ctype.h>
46
47
48 // SecDigestGetData, SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindByEmail
49 #include <Security/SecCertificatePriv.h>
50
51 Boolean gDeleteIt = 0;
52
53 // find_first_generic_password
54 //
55 // Returns a SecKeychainItemRef for the first item
56 // which matches the specified attributes. Caller is
57 // responsible for releasing the item (with CFRelease).
58 //
59 SecKeychainItemRef
60 find_first_generic_password(CFTypeRef keychainOrArray,
61 FourCharCode itemCreator,
62 FourCharCode itemType,
63 const char *kind,
64 const char *value,
65 const char *comment,
66 const char *label,
67 const char *serviceName,
68 const char *accountName)
69 {
70 OSStatus status = noErr;
71 SecKeychainSearchRef searchRef = NULL;
72 SecKeychainItemRef itemRef = NULL;
73
74 SecKeychainAttribute attrs[8]; // maximum number of searchable attributes
75 SecKeychainAttributeList attrList = { 0, attrs };
76
77 // the primary key for a generic password item (i.e. the combination of
78 // attributes which determine whether the item is unique) consists of:
79 // { kSecAccountItemAttr, kSecServiceItemAttr }
80 //
81 // if we have a primary key, we don't need to search on other attributes
82 // (and we don't want to, if non-primary attributes are being updated)
83 Boolean primaryKey = (accountName && serviceName);
84
85 // build the attribute list for searching
86 if ((UInt32)itemCreator != 0 && !primaryKey) {
87 attrs[attrList.count].tag = kSecCreatorItemAttr;
88 attrs[attrList.count].length = sizeof(FourCharCode);
89 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
90 attrList.count++;
91 }
92 if ((UInt32)itemType != 0 && !primaryKey) {
93 attrs[attrList.count].tag = kSecTypeItemAttr;
94 attrs[attrList.count].length = sizeof(FourCharCode);
95 attrs[attrList.count].data = (FourCharCode *)&itemType;
96 attrList.count++;
97 }
98 if (kind != NULL && !primaryKey) {
99 attrs[attrList.count].tag = kSecDescriptionItemAttr;
100 attrs[attrList.count].length = (UInt32) strlen(kind);
101 attrs[attrList.count].data = (void*)kind;
102 attrList.count++;
103 }
104 if (value != NULL && !primaryKey) {
105 attrs[attrList.count].tag = kSecGenericItemAttr;
106 attrs[attrList.count].length = (UInt32) strlen(value);
107 attrs[attrList.count].data = (void*)value;
108 attrList.count++;
109 }
110 if (comment != NULL && !primaryKey) {
111 attrs[attrList.count].tag = kSecCommentItemAttr;
112 attrs[attrList.count].length = (UInt32) strlen(comment);
113 attrs[attrList.count].data = (void*)comment;
114 attrList.count++;
115 }
116 if (label != NULL && !primaryKey) {
117 attrs[attrList.count].tag = kSecLabelItemAttr;
118 attrs[attrList.count].length = (UInt32) strlen(label);
119 attrs[attrList.count].data = (void*)label;
120 attrList.count++;
121 }
122 if (serviceName != NULL) {
123 attrs[attrList.count].tag = kSecServiceItemAttr;
124 attrs[attrList.count].length = (UInt32) strlen(serviceName);
125 attrs[attrList.count].data = (void*)serviceName;
126 attrList.count++;
127 }
128 if (accountName != NULL) {
129 attrs[attrList.count].tag = kSecAccountItemAttr;
130 attrs[attrList.count].length = (UInt32) strlen(accountName);
131 attrs[attrList.count].data = (void*)accountName;
132 attrList.count++;
133 }
134
135 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef);
136 if (status) {
137 sec_perror("SecKeychainSearchCreateFromAttributes", status);
138 goto cleanup;
139 }
140 // we're only interested in the first match, if there is a match at all
141 status = SecKeychainSearchCopyNext(searchRef, &itemRef);
142 if (status) {
143 itemRef = NULL;
144 }
145
146 cleanup:
147 if (searchRef)
148 CFRelease(searchRef);
149
150 return itemRef;
151 }
152
153 // find_first_internet_password
154 //
155 // Returns a SecKeychainItemRef for the first item
156 // which matches the specified attributes. Caller is
157 // responsible for releasing the item (with CFRelease).
158 //
159 SecKeychainItemRef
160 find_first_internet_password(CFTypeRef keychainOrArray,
161 FourCharCode itemCreator,
162 FourCharCode itemType,
163 const char *kind,
164 const char *comment,
165 const char *label,
166 const char *serverName,
167 const char *securityDomain,
168 const char *accountName,
169 const char *path,
170 UInt16 port,
171 SecProtocolType protocol,
172 SecAuthenticationType authenticationType)
173 {
174 OSStatus status = noErr;
175 SecKeychainSearchRef searchRef = NULL;
176 SecKeychainItemRef itemRef = NULL;
177
178 SecKeychainAttribute attrs[12]; // maximum number of searchable attributes
179 SecKeychainAttributeList attrList = { 0, attrs };
180
181 // the primary key for an internet password item (i.e. the combination of
182 // attributes which determine whether the item is unique) consists of:
183 // { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr,
184 // kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr,
185 // kSecPortItemAttr, kSecPathItemAttr }
186 //
187 // if we have a primary key, we don't need to search on other attributes.
188 // (and we don't want to, if non-primary attributes are being updated)
189 Boolean primaryKey = (accountName && securityDomain && serverName &&
190 protocol && authenticationType && port && path);
191
192 // build the attribute list for searching
193 if ((UInt32)itemCreator != 0 && !primaryKey) {
194 attrs[attrList.count].tag = kSecCreatorItemAttr;
195 attrs[attrList.count].length = sizeof(FourCharCode);
196 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
197 attrList.count++;
198 }
199 if ((UInt32)itemType != 0 && !primaryKey) {
200 attrs[attrList.count].tag = kSecTypeItemAttr;
201 attrs[attrList.count].length = sizeof(FourCharCode);
202 attrs[attrList.count].data = (FourCharCode *)&itemType;
203 attrList.count++;
204 }
205 if (kind != NULL && !primaryKey) {
206 attrs[attrList.count].tag = kSecDescriptionItemAttr;
207 attrs[attrList.count].length = (UInt32) strlen(kind);
208 attrs[attrList.count].data = (void*)kind;
209 attrList.count++;
210 }
211 if (comment != NULL && !primaryKey) {
212 attrs[attrList.count].tag = kSecCommentItemAttr;
213 attrs[attrList.count].length = (UInt32) strlen(comment);
214 attrs[attrList.count].data = (void*)comment;
215 attrList.count++;
216 }
217 if (label != NULL && !primaryKey) {
218 attrs[attrList.count].tag = kSecLabelItemAttr;
219 attrs[attrList.count].length = (UInt32) strlen(label);
220 attrs[attrList.count].data = (void*)label;
221 attrList.count++;
222 }
223 if (serverName != NULL) {
224 attrs[attrList.count].tag = kSecServerItemAttr;
225 attrs[attrList.count].length = (UInt32) strlen(serverName);
226 attrs[attrList.count].data = (void*)serverName;
227 attrList.count++;
228 }
229 if (securityDomain != NULL) {
230 attrs[attrList.count].tag = kSecSecurityDomainItemAttr;
231 attrs[attrList.count].length = (UInt32) strlen(securityDomain);
232 attrs[attrList.count].data = (void *)securityDomain;
233 attrList.count++;
234 }
235 if (accountName != NULL) {
236 attrs[attrList.count].tag = kSecAccountItemAttr;
237 attrs[attrList.count].length = (UInt32) strlen(accountName);
238 attrs[attrList.count].data = (void *)accountName;
239 attrList.count++;
240 }
241 if (path != NULL) {
242 attrs[attrList.count].tag = kSecPathItemAttr;
243 attrs[attrList.count].length = (UInt32) strlen(path);
244 attrs[attrList.count].data = (void *)path;
245 attrList.count++;
246 }
247 if (port != 0) {
248 attrs[attrList.count].tag = kSecPortItemAttr;
249 attrs[attrList.count].length = sizeof(UInt16);
250 attrs[attrList.count].data = (UInt16 *)&port;
251 attrList.count++;
252 }
253 if ((UInt32)protocol != 0) {
254 attrs[attrList.count].tag = kSecProtocolItemAttr;
255 attrs[attrList.count].length = sizeof(SecProtocolType);
256 attrs[attrList.count].data = (SecProtocolType *)&protocol;
257 attrList.count++;
258 }
259 if ((UInt32)authenticationType != 0) {
260 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr;
261 attrs[attrList.count].length = sizeof(SecAuthenticationType);
262 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType;
263 attrList.count++;
264 }
265
266 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef);
267 if (status) {
268 sec_perror("SecKeychainSearchCreateFromAttributes", status);
269 goto cleanup;
270 }
271 // we're only interested in the first match, if there is a match at all
272 status = SecKeychainSearchCopyNext(searchRef, &itemRef);
273 if (status) {
274 itemRef = NULL;
275 }
276
277 cleanup:
278 if (searchRef)
279 CFRelease(searchRef);
280
281 return itemRef;
282 }
283
284 // find_unique_certificate
285 //
286 // Returns a SecKeychainItemRef for the certificate
287 // in the specified keychain (or keychain list)
288 // which is a unique match for either the specified name
289 // or SHA-1 hash. If more than one match exists, the
290 // certificate is not unique and none are returned. Caller is
291 // responsible for releasing the item (with CFRelease).
292 //
293 SecKeychainItemRef
294 find_unique_certificate(CFTypeRef keychainOrArray,
295 const char *name,
296 const char *hash)
297 {
298 OSStatus status = noErr;
299 SecKeychainSearchRef searchRef = NULL;
300 SecKeychainItemRef uniqueItemRef = NULL;
301
302 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
303 if (status) {
304 return uniqueItemRef;
305 }
306
307 // check input hash string and convert to data
308 CSSM_DATA hashData = { 0, NULL };
309 if (hash) {
310 CSSM_SIZE len = strlen(hash)/2;
311 hashData.Length = len;
312 hashData.Data = (uint8 *)malloc(hashData.Length);
313 fromHex(hash, &hashData);
314 }
315
316 // filter candidates against the hash (or the name, if no hash provided)
317 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
318 Boolean exactMatch = FALSE;
319
320 CSSM_DATA certData = { 0, NULL };
321 SecKeychainItemRef candidate = NULL;
322
323 while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) {
324 SecCertificateRef cert = (SecCertificateRef)candidate;
325 if (SecCertificateGetData(cert, &certData) != noErr) {
326 safe_CFRelease(&candidate);
327 continue;
328 }
329 if (hash) {
330 uint8 candidate_sha1_hash[20];
331 CSSM_DATA digest;
332 digest.Length = sizeof(candidate_sha1_hash);
333 digest.Data = candidate_sha1_hash;
334 if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) &&
335 (hashData.Length == digest.Length) &&
336 (!memcmp(hashData.Data, digest.Data, digest.Length))) {
337 exactMatch = TRUE;
338 uniqueItemRef = candidate; // currently retained
339 break; // we're done - can't get more exact than this
340 }
341 } else {
342 // copy certificate name
343 CFStringRef nameRef = NULL;
344 if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) {
345 safe_CFRelease(&candidate);
346 continue; // no name, so no match is possible
347 }
348 CFIndex nameLen = CFStringGetLength(nameRef);
349 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
350 char *nameBuf = (char *)malloc(bufLen);
351 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
352 nameBuf[0]=0;
353
354 CFRange find = { kCFNotFound, 0 };
355 if (nameRef && matchRef)
356 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
357 Boolean isExact = (find.location == 0 && find.length == nameLen);
358 if (find.location == kCFNotFound) {
359 free(nameBuf);
360 safe_CFRelease(&nameRef);
361 safe_CFRelease(&candidate);
362 continue; // no match
363 }
364 if (uniqueItemRef) { // got two matches
365 if (exactMatch && !isExact) { // prior is better; ignore this one
366 free(nameBuf);
367 safe_CFRelease(&nameRef);
368 safe_CFRelease(&candidate);
369 continue;
370 }
371 if (exactMatch == isExact) { // same class of match
372 if (CFEqual(uniqueItemRef, candidate)) { // same certificate
373 free(nameBuf);
374 safe_CFRelease(&nameRef);
375 safe_CFRelease(&candidate);
376 continue;
377 }
378 // ambiguity - must fail
379 sec_error("\"%s\" is ambiguous, matches more than one certificate", name);
380 free(nameBuf);
381 safe_CFRelease(&nameRef);
382 safe_CFRelease(&candidate);
383 safe_CFRelease(&uniqueItemRef);
384 break;
385 }
386 safe_CFRelease(&uniqueItemRef); // about to replace with this one
387 }
388 uniqueItemRef = candidate; // currently retained
389 exactMatch = isExact;
390 free(nameBuf);
391 safe_CFRelease(&nameRef);
392 }
393 }
394
395 safe_CFRelease(&searchRef);
396 safe_CFRelease(&matchRef);
397 if (hashData.Data) {
398 free(hashData.Data);
399 }
400
401 return uniqueItemRef;
402 }
403
404 static OSStatus
405 do_password_item_printing( SecKeychainItemRef itemRef,
406 Boolean get_password,
407 Boolean password_stdout)
408 {
409 OSStatus result = noErr;
410 void *passwordData = NULL;
411 UInt32 passwordLength = 0;
412
413 if(get_password) {
414 result = SecKeychainItemCopyContent(itemRef, NULL, NULL, &passwordLength, &passwordData);
415 if(result != noErr) return result;
416 }
417 if(!password_stdout) {
418 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
419 if(get_password) {
420 fputs("password: ", stderr);
421 print_buffer(stderr, passwordLength, passwordData);
422 fputc('\n', stderr);
423 }
424 } else {
425 char *password = (char *) passwordData;
426 int doHex = 0;
427 for(int i=0; i<passwordLength; i++) if(!isprint(password[i])) doHex = 1;
428 if(doHex) {
429 for(int i=0; i<passwordLength; i++) printf("%02x", password[i]);
430 } else {
431 for(int i=0; i<passwordLength; i++) putchar(password[i]);
432 }
433 putchar('\n');
434 }
435
436 if (passwordData) SecKeychainItemFreeContent(NULL, passwordData);
437 return noErr;
438
439 }
440
441 static int
442 do_keychain_find_generic_password(CFTypeRef keychainOrArray,
443 FourCharCode itemCreator,
444 FourCharCode itemType,
445 const char *kind,
446 const char *value,
447 const char *comment,
448 const char *label,
449 const char *serviceName,
450 const char *accountName,
451 Boolean get_password,
452 Boolean password_stdout)
453 {
454 OSStatus result = noErr;
455 SecKeychainItemRef itemRef = NULL;
456
457 itemRef = find_first_generic_password(keychainOrArray,
458 itemCreator,
459 itemType,
460 kind,
461 value,
462 comment,
463 label,
464 serviceName,
465 accountName);
466
467 if(itemRef) {
468 result = do_password_item_printing(itemRef, get_password, password_stdout);
469 } else {
470 result = errSecItemNotFound;
471 sec_perror("SecKeychainSearchCopyNext", result);
472 }
473
474 if (itemRef) CFRelease(itemRef);
475
476 return result;
477 }
478
479 static int
480 do_keychain_delete_generic_password(CFTypeRef keychainOrArray,
481 FourCharCode itemCreator,
482 FourCharCode itemType,
483 const char *kind,
484 const char *value,
485 const char *comment,
486 const char *label,
487 const char *serviceName,
488 const char *accountName)
489 {
490 OSStatus result = noErr;
491 SecKeychainItemRef itemRef = NULL;
492 void *passwordData = NULL;
493
494 itemRef = find_first_generic_password(keychainOrArray,
495 itemCreator,
496 itemType,
497 kind,
498 value,
499 comment,
500 label,
501 serviceName,
502 accountName);
503 if (!itemRef) {
504 result = errSecItemNotFound;
505 sec_perror("SecKeychainSearchCopyNext", result);
506 goto cleanup;
507 }
508
509 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
510
511 result = SecKeychainItemDelete(itemRef);
512
513 fputs("password has been deleted.\n", stderr);
514
515 cleanup:
516 if (passwordData)
517 SecKeychainItemFreeContent(NULL, passwordData);
518 if (itemRef)
519 CFRelease(itemRef);
520
521 return result;
522 }
523
524 static int
525 do_keychain_find_internet_password(CFTypeRef keychainOrArray,
526 FourCharCode itemCreator,
527 FourCharCode itemType,
528 const char *kind,
529 const char *comment,
530 const char *label,
531 const char *serverName,
532 const char *securityDomain,
533 const char *accountName,
534 const char *path,
535 UInt16 port,
536 SecProtocolType protocol,
537 SecAuthenticationType authenticationType,
538 Boolean get_password,
539 Boolean password_stdout)
540 {
541 OSStatus result = noErr;
542 SecKeychainItemRef itemRef = NULL;
543
544 itemRef = find_first_internet_password(keychainOrArray,
545 itemCreator,
546 itemType,
547 kind,
548 comment,
549 label,
550 serverName,
551 securityDomain,
552 accountName,
553 path,
554 port,
555 protocol,
556 authenticationType);
557 if(itemRef) {
558 result = do_password_item_printing(itemRef, get_password, password_stdout);
559 } else {
560 result = errSecItemNotFound;
561 sec_perror("SecKeychainSearchCopyNext", result);
562 }
563
564 return result;
565 }
566
567 static int
568 do_keychain_delete_internet_password(CFTypeRef keychainOrArray,
569 FourCharCode itemCreator,
570 FourCharCode itemType,
571 const char *kind,
572 const char *comment,
573 const char *label,
574 const char *serverName,
575 const char *securityDomain,
576 const char *accountName,
577 const char *path,
578 UInt16 port,
579 SecProtocolType protocol,
580 SecAuthenticationType authenticationType)
581 {
582 OSStatus result = noErr;
583 SecKeychainItemRef itemRef = NULL;
584 void *passwordData = NULL;
585
586 itemRef = find_first_internet_password(keychainOrArray,
587 itemCreator,
588 itemType,
589 kind,
590 comment,
591 label,
592 serverName,
593 securityDomain,
594 accountName,
595 path,
596 port,
597 protocol,
598 authenticationType);
599 if (!itemRef) {
600 result = errSecItemNotFound;
601 sec_perror("SecKeychainSearchCopyNext", result);
602 goto cleanup;
603 }
604
605 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
606
607 result = SecKeychainItemDelete(itemRef);
608
609 fputs("password has been deleted.\n", stderr);
610
611 cleanup:
612 if (passwordData)
613 SecKeychainItemFreeContent(NULL, passwordData);
614 if (itemRef)
615 CFRelease(itemRef);
616
617 return result;
618 }
619
620 static int
621 do_keychain_find_certificate(CFTypeRef keychainOrArray,
622 const char *name,
623 const char *emailAddress,
624 Boolean print_hash,
625 Boolean output_pem,
626 Boolean find_all,
627 Boolean print_email)
628 {
629 OSStatus result = noErr;
630 SecCertificateRef certificateRef = NULL;
631 SecKeychainSearchRef searchRef = NULL;
632 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
633
634 if (find_all && emailAddress) {
635 result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef);
636 if (result) {
637 sec_perror("SecKeychainSearchCreateForCertificateByEmail", result);
638 goto cleanup;
639 }
640 } else {
641 result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
642 if (result) {
643 sec_perror("SecKeychainSearchCreateFromAttributes", result);
644 goto cleanup;
645 }
646 }
647
648 do
649 {
650 if (find_all) {
651 SecKeychainItemRef itemRef = NULL;
652 result = SecKeychainSearchCopyNext(searchRef, &itemRef);
653 if (result == errSecItemNotFound) {
654 result = 0;
655 break;
656 }
657 else if (result) {
658 sec_perror("SecKeychainSearchCopyNext", result);
659 goto cleanup;
660 }
661
662 if (!emailAddress && name) {
663 // match name in common name
664 CFStringRef nameRef = NULL;
665 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
666 safe_CFRelease(&itemRef);
667 continue; // no name, so no match is possible
668 }
669 CFRange find = { kCFNotFound, 0 };
670 if (nameRef && matchRef)
671 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
672 if (find.location == kCFNotFound) {
673 safe_CFRelease(&nameRef);
674 safe_CFRelease(&itemRef);
675 continue; // no match
676 }
677 safe_CFRelease(&nameRef);
678 }
679 safe_CFRelease(&certificateRef);
680 certificateRef = (SecCertificateRef) itemRef;
681 }
682 else { // only want the first match
683 if (emailAddress) {
684 result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef);
685 if (result) {
686 sec_perror("SecCertificateFindByEmail", result);
687 goto cleanup;
688 }
689 } else {
690 SecKeychainItemRef itemRef = NULL;
691 while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) {
692 if (name) {
693 // match name in common name
694 CFStringRef nameRef = NULL;
695 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
696 safe_CFRelease(&itemRef);
697 continue; // no name, so no match is possible
698 }
699 CFRange find = { kCFNotFound, 0 };
700 if (nameRef && matchRef)
701 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
702 if (find.location == kCFNotFound) {
703 safe_CFRelease(&nameRef);
704 safe_CFRelease(&itemRef);
705 continue; // no match
706 }
707 safe_CFRelease(&nameRef);
708 }
709 break; // we have a match!
710 }
711 if (result == errSecItemNotFound) {
712 sec_perror("SecKeychainSearchCopyNext", result);
713 goto cleanup;
714 }
715 certificateRef = (SecCertificateRef) itemRef;
716 }
717 }
718
719 // process the found certificate
720
721 if (print_hash) {
722 uint8 sha1_hash[20];
723 CSSM_DATA data;
724 CSSM_DATA digest;
725 digest.Length = sizeof(sha1_hash);
726 digest.Data = sha1_hash;
727 if ((SecCertificateGetData(certificateRef, &data) == noErr) &&
728 (SecDigestGetData(CSSM_ALGID_SHA1, &digest, &data) == CSSM_OK)) {
729 unsigned int i;
730 size_t len = digest.Length;
731 uint8 *cp = digest.Data;
732 fprintf(stdout, "SHA-1 hash: ");
733 for(i=0; i<len; i++) {
734 fprintf(stdout, "%02X", ((unsigned char *)cp)[i]);
735 }
736 fprintf(stdout, "\n");
737 }
738 }
739
740 if (print_email)
741 {
742 CFArrayRef emailAddresses = NULL;
743 CFIndex ix, count;
744 result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses);
745 if (result)
746 {
747 sec_perror("SecCertificateCopyEmailAddresses", result);
748 goto cleanup;
749 }
750
751 count = CFArrayGetCount(emailAddresses);
752 fputs("email addresses: ", stdout);
753 for (ix = 0; ix < count; ++ix)
754 {
755 CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix);
756 const char *addr;
757 char buffer[256];
758
759 if (ix)
760 fputs(", ", stdout);
761
762 addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8);
763 if (!addr)
764 {
765 if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8))
766 addr = buffer;
767 }
768
769 fprintf(stdout, "%s", addr);
770 }
771 fputc('\n', stdout);
772
773 CFRelease(emailAddresses);
774 }
775
776 if (output_pem)
777 {
778 CSSM_DATA certData = {};
779 result = SecCertificateGetData(certificateRef, &certData);
780 if (result)
781 {
782 sec_perror("SecCertificateGetData", result);
783 goto cleanup;
784 }
785
786 print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
787 }
788 else
789 {
790 print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE);
791 }
792 } while (find_all);
793
794 cleanup:
795 safe_CFRelease(&searchRef);
796 safe_CFRelease(&certificateRef);
797 safe_CFRelease(&matchRef);
798
799 return result;
800 }
801
802 int
803 keychain_delete_internet_password(int argc, char * const *argv)
804 {
805 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
806 char *kind = NULL, *label = NULL, *comment = NULL;
807 FourCharCode itemCreator = 0, itemType = 0;
808 UInt16 port = 0;
809 SecProtocolType protocol = 0;
810 SecAuthenticationType authenticationType = 0;
811 CFTypeRef keychainOrArray = NULL;
812 int ch, result = 0;
813
814 /*
815 * " -a Match \"account\" string\n"
816 * " -c Match \"creator\" (four-character code)\n"
817 * " -C Match \"type\" (four-character code)\n"
818 * " -d Match \"securityDomain\" string\n"
819 * " -D Match \"kind\" string\n"
820 * " -j Match \"comment\" string\n"
821 * " -l Match \"label\" string\n"
822 * " -p Match \"path\" string\n"
823 * " -P Match port number\n"
824 * " -r Match \"protocol\" (four-character code)\n"
825 * " -s Match \"server\" string\n"
826 * " -t Match \"authenticationType\" (four-character code)\n"
827 */
828
829 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:t:")) != -1)
830 {
831 switch (ch)
832 {
833 case 'a':
834 accountName = optarg;
835 break;
836 case 'c':
837 result = parse_fourcharcode(optarg, &itemCreator);
838 if (result) goto cleanup;
839 break;
840 case 'C':
841 result = parse_fourcharcode(optarg, &itemType);
842 if (result) goto cleanup;
843 break;
844 case 'd':
845 securityDomain = optarg;
846 break;
847 case 'D':
848 kind = optarg;
849 break;
850 case 'j':
851 comment = optarg;
852 break;
853 case 'l':
854 label = optarg;
855 break;
856 case 'p':
857 path = optarg;
858 break;
859 case 'P':
860 port = atoi(optarg);
861 break;
862 case 'r':
863 result = parse_fourcharcode(optarg, &protocol);
864 if (result) goto cleanup;
865 break;
866 case 's':
867 serverName = optarg;
868 break;
869 case 't':
870 result = parse_fourcharcode(optarg, &authenticationType);
871 if (result) goto cleanup;
872 break;
873 case '?':
874 default:
875 result = 2; /* @@@ Return 2 triggers usage message. */
876 goto cleanup;
877 }
878 }
879
880 argc -= optind;
881 argv += optind;
882
883 keychainOrArray = keychain_create_array(argc, argv);
884
885 result = do_keychain_delete_internet_password(keychainOrArray,
886 itemCreator,
887 itemType,
888 kind,
889 comment,
890 label,
891 serverName,
892 securityDomain,
893 accountName,
894 path,
895 port,
896 protocol,
897 authenticationType);
898 cleanup:
899 if (keychainOrArray)
900 CFRelease(keychainOrArray);
901
902 return result;
903 }
904
905 int
906 keychain_find_internet_password(int argc, char * const *argv)
907 {
908 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
909 char *kind = NULL, *label = NULL, *comment = NULL;
910 FourCharCode itemCreator = 0, itemType = 0;
911 UInt16 port = 0;
912 SecProtocolType protocol = 0;
913 SecAuthenticationType authenticationType = 0;
914 CFTypeRef keychainOrArray = NULL;
915 int ch, result = 0;
916 Boolean get_password = FALSE;
917 Boolean password_stdout = FALSE;
918
919 /*
920 * " -a Match \"account\" string\n"
921 * " -c Match \"creator\" (four-character code)\n"
922 * " -C Match \"type\" (four-character code)\n"
923 * " -d Match \"securityDomain\" string\n"
924 * " -D Match \"kind\" string\n"
925 * " -j Match \"comment\" string\n"
926 * " -l Match \"label\" string\n"
927 * " -p Match \"path\" string\n"
928 * " -P Match port number\n"
929 * " -r Match \"protocol\" (four-character code)\n"
930 * " -s Match \"server\" string\n"
931 * " -t Match \"authenticationType\" (four-character code)\n"
932 * " -g Display the password for the item found\n"
933 * " -w Display the password(only) for the item(s) found\n"
934 */
935
936 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:wt:")) != -1)
937 {
938 switch (ch)
939 {
940 case 'a':
941 accountName = optarg;
942 break;
943 case 'c':
944 result = parse_fourcharcode(optarg, &itemCreator);
945 if (result) goto cleanup;
946 break;
947 case 'C':
948 result = parse_fourcharcode(optarg, &itemType);
949 if (result) goto cleanup;
950 break;
951 case 'd':
952 securityDomain = optarg;
953 break;
954 case 'D':
955 kind = optarg;
956 break;
957 case 'j':
958 comment = optarg;
959 break;
960 case 'l':
961 label = optarg;
962 break;
963 case 'g':
964 get_password = TRUE;
965 break;
966 case 'p':
967 path = optarg;
968 break;
969 case 'P':
970 port = atoi(optarg);
971 break;
972 case 'r':
973 result = parse_fourcharcode(optarg, &protocol);
974 if (result) goto cleanup;
975 break;
976 case 's':
977 serverName = optarg;
978 break;
979 case 'w':
980 get_password = TRUE;
981 password_stdout = TRUE;
982 break;
983 case 't':
984 result = parse_fourcharcode(optarg, &authenticationType);
985 if (result) goto cleanup;
986 /* auth type attribute is special */
987 authenticationType = OSSwapHostToBigInt32(authenticationType);
988 break;
989 case '?':
990 default:
991 result = 2; /* @@@ Return 2 triggers usage message. */
992 goto cleanup;
993 }
994 }
995
996 argc -= optind;
997 argv += optind;
998
999 keychainOrArray = keychain_create_array(argc, argv);
1000
1001 result = do_keychain_find_internet_password(keychainOrArray,
1002 itemCreator,
1003 itemType,
1004 kind,
1005 comment,
1006 label,
1007 serverName,
1008 securityDomain,
1009 accountName,
1010 path,
1011 port,
1012 protocol,
1013 authenticationType,
1014 get_password,
1015 password_stdout);
1016 cleanup:
1017 if (keychainOrArray)
1018 CFRelease(keychainOrArray);
1019
1020 return result;
1021 }
1022
1023 int
1024 keychain_delete_generic_password(int argc, char * const *argv)
1025 {
1026 char *serviceName = NULL, *accountName = NULL;
1027 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
1028 FourCharCode itemCreator = 0, itemType = 0;
1029 CFTypeRef keychainOrArray = nil;
1030 int ch, result = 0;
1031
1032 /*
1033 * " -a Match \"account\" string\n"
1034 * " -c Match \"creator\" (four-character code)\n"
1035 * " -C Match \"type\" (four-character code)\n"
1036 * " -D Match \"kind\" string\n"
1037 * " -G Match \"value\" string (generic attribute)\n"
1038 * " -j Match \"comment\" string\n"
1039 * " -l Match \"label\" string\n"
1040 * " -s Match \"service\" string\n"
1041 */
1042
1043 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:g")) != -1)
1044 {
1045 switch (ch)
1046 {
1047 case 'a':
1048 accountName = optarg;
1049 break;
1050 case 'c':
1051 result = parse_fourcharcode(optarg, &itemCreator);
1052 if (result) goto cleanup;
1053 break;
1054 case 'C':
1055 result = parse_fourcharcode(optarg, &itemType);
1056 if (result) goto cleanup;
1057 break;
1058 case 'D':
1059 kind = optarg;
1060 break;
1061 case 'G':
1062 value = optarg;
1063 break;
1064 case 'j':
1065 comment = optarg;
1066 break;
1067 case 'l':
1068 label = optarg;
1069 break;
1070 case 's':
1071 serviceName = optarg;
1072 break;
1073 case '?':
1074 default:
1075 result = 2; /* @@@ Return 2 triggers usage message. */
1076 goto cleanup;
1077 }
1078 }
1079
1080 argc -= optind;
1081 argv += optind;
1082
1083 keychainOrArray = keychain_create_array(argc, argv);
1084
1085 result = do_keychain_delete_generic_password(keychainOrArray,
1086 itemCreator,
1087 itemType,
1088 kind,
1089 value,
1090 comment,
1091 label,
1092 serviceName,
1093 accountName);
1094 cleanup:
1095 if (keychainOrArray)
1096 CFRelease(keychainOrArray);
1097
1098 return result;
1099 }
1100
1101 int
1102 keychain_find_generic_password(int argc, char * const *argv)
1103 {
1104 char *serviceName = NULL, *accountName = NULL;
1105 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
1106 FourCharCode itemCreator = 0, itemType = 0;
1107 CFTypeRef keychainOrArray = nil;
1108 int ch, result = 0;
1109 Boolean get_password = FALSE;
1110 Boolean password_stdout = FALSE;
1111
1112 /*
1113 * " -a Match \"account\" string\n"
1114 * " -c Match \"creator\" (four-character code)\n"
1115 * " -C Match \"type\" (four-character code)\n"
1116 * " -D Match \"kind\" string\n"
1117 * " -G Match \"value\" string (generic attribute)\n"
1118 * " -j Match \"comment\" string\n"
1119 * " -l Match \"label\" string\n"
1120 * " -s Match \"service\" string\n"
1121 * " -g Display the password for the item(s) found\n"
1122 * " -w Display the password(only) for the item(s) found\n"
1123 */
1124
1125 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:wg")) != -1)
1126 {
1127 switch (ch)
1128 {
1129 case 'a':
1130 accountName = optarg;
1131 break;
1132 case 'c':
1133 result = parse_fourcharcode(optarg, &itemCreator);
1134 if (result) goto cleanup;
1135 break;
1136 case 'C':
1137 result = parse_fourcharcode(optarg, &itemType);
1138 if (result) goto cleanup;
1139 break;
1140 case 'D':
1141 kind = optarg;
1142 break;
1143 case 'G':
1144 value = optarg;
1145 break;
1146 case 'j':
1147 comment = optarg;
1148 break;
1149 case 'l':
1150 label = optarg;
1151 break;
1152 case 's':
1153 serviceName = optarg;
1154 break;
1155 case 'w':
1156 password_stdout = TRUE;
1157 get_password = TRUE;
1158 break;
1159 case 'g':
1160 get_password = TRUE;
1161 break;
1162 case '?':
1163 default:
1164 result = 2; /* @@@ Return 2 triggers usage message. */
1165 goto cleanup;
1166 }
1167 }
1168
1169 argc -= optind;
1170 argv += optind;
1171
1172 keychainOrArray = keychain_create_array(argc, argv);
1173
1174 result = do_keychain_find_generic_password(keychainOrArray,
1175 itemCreator,
1176 itemType,
1177 kind,
1178 value,
1179 comment,
1180 label,
1181 serviceName,
1182 accountName,
1183 get_password,
1184 password_stdout);
1185 cleanup:
1186 if (keychainOrArray)
1187 CFRelease(keychainOrArray);
1188
1189 return result;
1190 }
1191
1192 int
1193 keychain_find_key(int argc, char * const *argv) {
1194 /*
1195 * " -a Match \"application label\" string\n"
1196 * " -c Match \"creator\" (four-character code)\n"
1197 * " -d Match keys that can decrypt\n"
1198 * " -D Match \"description\" string\n"
1199 * " -e Match keys that can encrypt\n"
1200 * " -j Match \"comment\" string\n"
1201 * " -l Match \"label\" string\n"
1202 * " -r Match keys that can derive\n"
1203 * " -s Match keys that can sign\n"
1204 * " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n"
1205 * " -u Match keys that can unwrap\n"
1206 * " -v Match keys that can verify\n"
1207 * " -w Match keys that can wrap\n"
1208 */
1209
1210 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1211 CFDictionarySetValue(query, kSecClass, kSecClassKey);
1212 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
1213
1214 CFTypeRef results = NULL;
1215
1216 int ch, result = 0;
1217 while ((ch = getopt(argc, argv, "a:c:dD:ej:l:rst:uvw")) != -1)
1218 {
1219 switch (ch)
1220 {
1221 case 'a':
1222 CFDictionarySetValue(query, kSecAttrApplicationLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1223 break;
1224 case 'c':
1225 CFDictionarySetValue(query, kSecAttrCreator, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1226 break;
1227 case 'd':
1228 CFDictionarySetValue(query, kSecAttrCanDecrypt, kCFBooleanTrue);
1229 break;
1230 case 'D':
1231 CFDictionarySetValue(query, kSecAttrDescription, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1232 break;
1233 case 'e':
1234 CFDictionarySetValue(query, kSecAttrCanEncrypt, kCFBooleanTrue);
1235 break;
1236 case 'j':
1237 CFDictionarySetValue(query, kSecAttrComment, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1238 break;
1239 case 'l':
1240 CFDictionarySetValue(query, kSecAttrLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1241 break;
1242 case 'r':
1243 CFDictionarySetValue(query, kSecAttrCanDerive, kCFBooleanTrue);
1244 break;
1245 case 's':
1246 CFDictionarySetValue(query, kSecAttrCanSign, kCFBooleanTrue);
1247 break;
1248 case 't':
1249 if(strcmp(optarg, "symmetric") == 0) {
1250 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassSymmetric);
1251 } else if(strcmp(optarg, "public") == 0) {
1252 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPublic);
1253 } else if(strcmp(optarg, "private") == 0) {
1254 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
1255 } else {
1256 result = 2;
1257 goto cleanup;
1258 }
1259 break;
1260 case 'u':
1261 CFDictionarySetValue(query, kSecAttrCanUnwrap, kCFBooleanTrue);
1262 break;
1263 case 'v':
1264 CFDictionarySetValue(query, kSecAttrCanVerify, kCFBooleanTrue);
1265 break;
1266 case 'w':
1267 CFDictionarySetValue(query, kSecAttrCanWrap, kCFBooleanTrue);
1268 break;
1269 case '?':
1270 default:
1271 result = 2;
1272 goto cleanup;
1273 }
1274 }
1275
1276 argc -= optind;
1277 argv += optind;
1278
1279 CFTypeRef keychainOrArray = keychain_create_array(argc, argv);
1280
1281 if(keychainOrArray && CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) {
1282 CFDictionarySetValue(query, kSecMatchSearchList, keychainOrArray);
1283 } else if(keychainOrArray) {
1284 // if it's not an array (but is something), it's a keychain. Put it in an array.
1285 CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
1286 CFArrayAppendValue((CFMutableArrayRef)searchList, keychainOrArray);
1287 CFDictionarySetValue(query, kSecMatchSearchList, searchList);
1288 CFRelease(searchList);
1289 }
1290
1291 OSStatus status = SecItemCopyMatching(query, &results);
1292 if(status) {
1293 sec_perror("SecItemCopyMatching", status);
1294 result = 1;
1295 goto cleanup;
1296 }
1297
1298 if (CFGetTypeID(results) == CFArrayGetTypeID()) {
1299 for(int i = 0; i < CFArrayGetCount(results); i++) {
1300 SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i);
1301
1302 print_keychain_item_attributes(stdout, item, FALSE, FALSE, FALSE, FALSE);
1303 }
1304 }
1305
1306 cleanup:
1307 safe_CFRelease(&results);
1308 safe_CFRelease(&query);
1309 return result;
1310 }
1311
1312 // Declare here to use later.
1313 int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist);
1314 int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password);
1315
1316 int keychain_set_internet_password_partition_list(int argc, char * const *argv) {
1317 /*
1318 * " -a Match \"account\" string\n"
1319 * " -c Match \"creator\" (four-character code)\n"
1320 * " -C Match \"type\" (four-character code)\n"
1321 * " -d Match \"securityDomain\" string\n"
1322 * " -D Match \"kind\" string\n"
1323 * " -j Match \"comment\" string\n"
1324 * " -l Match \"label\" string\n"
1325 * " -p Match \"path\" string\n"
1326 * " -P Match port number\n"
1327 * " -r Match \"protocol\" (four-character code)\n"
1328 * " -s Match \"server\" string\n"
1329 * " -t Match \"authenticationType\" (four-character code)\n"
1330 * " -S Comma-separated list of allowed partition IDs"
1331 * " -k password for keychain"
1332 */
1333
1334 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1335 CFDictionarySetValue(query, kSecClass, kSecClassInternetPassword);
1336 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
1337
1338 CFStringRef partitionidsinput = NULL;
1339 CFStringRef password = NULL;
1340
1341 int ch, result = 0;
1342 while ((ch = getopt(argc, argv, "a:c:C:d:D:j:l:p:P:r:s:S:t:k:")) != -1)
1343 {
1344 switch (ch)
1345 {
1346 case 'a':
1347 CFDictionarySetValue(query, kSecAttrAccount, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1348 break;
1349 case 'c':
1350 CFDictionarySetValue(query, kSecAttrCreator, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1351 break;
1352 case 'C':
1353 CFDictionarySetValue(query, kSecAttrType, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1354 break;
1355 case 'd':
1356 CFDictionarySetValue(query, kSecAttrSecurityDomain, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1357 break;
1358 case 'D':
1359 CFDictionarySetValue(query, kSecAttrDescription, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1360 break;
1361 case 'j':
1362 CFDictionarySetValue(query, kSecAttrComment, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1363 break;
1364 case 'l':
1365 CFDictionarySetValue(query, kSecAttrLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1366 break;
1367 case 'p':
1368 CFDictionarySetValue(query, kSecAttrPath, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1369 break;
1370 case 'P':
1371 CFDictionarySetValue(query, kSecAttrPort, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1372 break;
1373 case 'r':
1374 CFDictionarySetValue(query, kSecAttrProtocol, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1375 break;
1376 case 's':
1377 CFDictionarySetValue(query, kSecAttrService, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1378 break;
1379 case 't':
1380 CFDictionarySetValue(query, kSecAttrAuthenticationType, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1381 break;
1382 case 'S':
1383 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1384 break;
1385 case 'k':
1386 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1387 break;
1388 case '?':
1389 default:
1390 result = 2;
1391 goto cleanup;
1392 }
1393 }
1394
1395 argc -= optind;
1396 argv += optind;
1397
1398 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
1399
1400 cleanup:
1401 safe_CFRelease(&password);
1402 safe_CFRelease(&partitionidsinput);
1403 safe_CFRelease(&query);
1404 return result;
1405 }
1406
1407 int
1408 keychain_set_generic_password_partition_list(int argc, char * const *argv) {
1409 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1410
1411 // -a Match \"account\" string
1412 // -c Match \"creator\" (four-character code)
1413 // -C Match \"type\" (four-character code)
1414 // -D Match \"kind\" string
1415 // -G Match \"value\" string (generic attribute)
1416 // -j Match \"comment\" string
1417 // -l Match \"label\" string
1418 // -s Match \"service\" string
1419 // -S Comma-separated list of allowed partition IDs
1420 // -k password for keychain
1421
1422 CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
1423 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
1424
1425 CFStringRef partitionidsinput = NULL;
1426 CFStringRef password = NULL;
1427
1428 int ch, result = 0;
1429 while ((ch = getopt(argc, argv, ":a:c:C:D:G:j:l:s:S:k:")) != -1)
1430 {
1431 switch (ch)
1432 {
1433 case 'a':
1434 CFDictionarySetValue(query, kSecAttrAccount, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1435 break;
1436 case 'c':
1437 CFDictionarySetValue(query, kSecAttrCreator, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1438 break;
1439 case 'C':
1440 CFDictionarySetValue(query, kSecAttrType, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1441 break;
1442 case 'D':
1443 CFDictionarySetValue(query, kSecAttrDescription, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1444 break;
1445 case 'G':
1446 CFDictionarySetValue(query, kSecAttrGeneric, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1447 break;
1448 case 'j':
1449 CFDictionarySetValue(query, kSecAttrComment, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1450 break;
1451 case 'l':
1452 CFDictionarySetValue(query, kSecAttrLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1453 break;
1454 case 's':
1455 CFDictionarySetValue(query, kSecAttrService, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1456 break;
1457 case 'S':
1458 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1459 break;
1460 case 'k':
1461 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1462 break;
1463 case '?':
1464 case ':':
1465 // They supplied the -k but with no data
1466 // Leaving it null will cause prompt below
1467 if (optopt == 'k') {
1468 break;
1469 }
1470 result = 2;
1471 goto cleanup; /* @@@ Return 2 triggers usage message. */
1472 default:
1473 result = 2;
1474 goto cleanup;
1475 }
1476 }
1477
1478 argc -= optind;
1479 argv += optind;
1480
1481 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
1482
1483 cleanup:
1484 safe_CFRelease(&password);
1485 safe_CFRelease(&partitionidsinput);
1486 safe_CFRelease(&query);
1487 return result;
1488 }
1489
1490 int
1491 keychain_set_key_partition_list(int argc, char * const *argv) {
1492 /*
1493 * " -a Match \"application label\" string\n"
1494 * " -c Match \"creator\" (four-character code)\n"
1495 * " -d Match keys that can decrypt\n"
1496 * " -D Match \"description\" string\n"
1497 * " -e Match keys that can encrypt\n"
1498 * " -j Match \"comment\" string\n"
1499 * " -l Match \"label\" string\n"
1500 * " -r Match keys that can derive\n"
1501 * " -s Match keys that can sign\n"
1502 * " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n"
1503 * " -u Match keys that can unwrap\n"
1504 * " -v Match keys that can verify\n"
1505 * " -w Match keys that can wrap\n"
1506 * " -S Comma-separated list of allowed partition IDs
1507 * " -k password for keychain (required)
1508 */
1509
1510 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1511 CFDictionarySetValue(query, kSecClass, kSecClassKey);
1512 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
1513
1514 CFStringRef partitionidsinput = NULL;
1515 CFStringRef password = NULL;
1516
1517 int ch, result = 0;
1518 while ((ch = getopt(argc, argv, ":a:c:dD:ej:k:l:rsS:t:uvw")) != -1)
1519 {
1520 switch (ch)
1521 {
1522 case 'a':
1523 CFDictionarySetValue(query, kSecAttrApplicationLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1524 break;
1525 case 'c':
1526 CFDictionarySetValue(query, kSecAttrCreator, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1527 break;
1528 case 'd':
1529 CFDictionarySetValue(query, kSecAttrCanDecrypt, kCFBooleanTrue);
1530 break;
1531 case 'D':
1532 CFDictionarySetValue(query, kSecAttrDescription, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1533 break;
1534 case 'e':
1535 CFDictionarySetValue(query, kSecAttrCanEncrypt, kCFBooleanTrue);
1536 break;
1537 case 'j':
1538 CFDictionarySetValue(query, kSecAttrComment, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1539 break;
1540 case 'l':
1541 CFDictionarySetValue(query, kSecAttrLabel, CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull));
1542 break;
1543 case 'r':
1544 CFDictionarySetValue(query, kSecAttrCanDerive, kCFBooleanTrue);
1545 case 's':
1546 CFDictionarySetValue(query, kSecAttrCanSign, kCFBooleanTrue);
1547 break;
1548 case 't':
1549 if(strcmp(optarg, "symmetric") == 0) {
1550 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassSymmetric);
1551 } else if(strcmp(optarg, "public") == 0) {
1552 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPublic);
1553 } else if(strcmp(optarg, "private") == 0) {
1554 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
1555 } else {
1556 result = 2;
1557 goto cleanup;
1558 }
1559 break;
1560 case 'u':
1561 CFDictionarySetValue(query, kSecAttrCanUnwrap, kCFBooleanTrue);
1562 break;
1563 case 'v':
1564 CFDictionarySetValue(query, kSecAttrCanVerify, kCFBooleanTrue);
1565 break;
1566 case 'w':
1567 CFDictionarySetValue(query, kSecAttrCanWrap, kCFBooleanTrue);
1568 break;
1569 case 'S':
1570 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1571 break;
1572 case 'k':
1573 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
1574 break;
1575 case '?':
1576 case ':':
1577 // They supplied the -k but with no data
1578 // Leaving it null will cause prompt below
1579 if (optopt == 'k') {
1580 break;
1581 }
1582 result = 2;
1583 goto cleanup; /* @@@ Return 2 triggers usage message. */
1584 default:
1585 result = 2;
1586 goto cleanup;
1587 }
1588 }
1589
1590 argc -= optind;
1591 argv += optind;
1592
1593 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
1594
1595 cleanup:
1596 safe_CFRelease(&query);
1597 return result;
1598 }
1599
1600
1601 int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password) {
1602 int result = 0;
1603 const char *keychainName = NULL;
1604 SecKeychainRef kc = NULL;
1605
1606 // if we were given a keychain, use it
1607 if (argc == 1)
1608 {
1609 keychainName = argv[0];
1610 if (*keychainName == '\0')
1611 {
1612 result = 2;
1613 goto cleanup;
1614 }
1615
1616 kc = keychain_open(keychainName);
1617 if(!kc) {
1618 sec_error("couldn't open \"%s\"", keychainName);
1619 result = 1;
1620 goto cleanup;
1621 }
1622
1623 CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
1624 CFArrayAppendValue((CFMutableArrayRef)searchList, kc);
1625 CFDictionarySetValue(query, kSecMatchSearchList, searchList);
1626 } else if (argc != 0) {
1627 result = 2;
1628 goto cleanup;
1629 }
1630
1631 if(!partitionidsinput) {
1632 result = 2;
1633 goto cleanup;
1634 }
1635
1636 if(!password) {
1637 char* cpassword = prompt_password(keychainName);
1638 if (!cpassword) {
1639 result = -1;
1640 goto cleanup;
1641 }
1642 password = CFStringCreateWithCString(NULL, cpassword, kCFStringEncodingUTF8);
1643 free(cpassword);
1644 }
1645
1646 result = keychain_set_partition_list(kc, query, password, partitionidsinput);
1647
1648 cleanup:
1649 return result;
1650 }
1651
1652
1653 int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist) {
1654 int result = 0;
1655
1656 char *passwordBuf = NULL;
1657 size_t passwordLen;
1658 GetCStringFromCFString(password, &passwordBuf, &passwordLen);
1659
1660 OSStatus status;
1661
1662 // Unlock the keychain with the given password, since we'll be fetching ACLs
1663 status = SecKeychainUnlock(kc, (UInt32) passwordLen, passwordBuf, true);
1664 if(status) {
1665 sec_perror("SecKeychainUnlock", status);
1666 result = 1;
1667 goto cleanup;
1668 }
1669
1670 CFTypeRef results = NULL;
1671 status = SecItemCopyMatching(query, &results);
1672 if(status) {
1673 sec_perror("SecItemCopyMatching", status);
1674 result = 1;
1675 goto cleanup;
1676 }
1677
1678 if(!results) {
1679 result = 0;
1680 goto cleanup;
1681 }
1682
1683 if (CFGetTypeID(results) == CFArrayGetTypeID()) {
1684 for(int i = 0; i < CFArrayGetCount(results); i++) {
1685 SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i);
1686 SecAccessRef access = NULL;
1687
1688 do_password_item_printing(item, false, false);
1689
1690 status = SecKeychainItemCopyAccess(item, &access);
1691 if (status == errSecNoAccessForItem) {
1692 continue;
1693 }
1694 if(status) {
1695 sec_perror("SecKeychainItemCopyAccess", status);
1696 result = 1;
1697 goto cleanup;
1698 }
1699
1700 CFArrayRef aclList = NULL;
1701 status = SecAccessCopyACLList(access, &aclList);
1702 if (status)
1703 {
1704 sec_perror("SecAccessCopyACLList", status);
1705 result = 1;
1706 goto cleanup;
1707 }
1708
1709 CFIndex size = CFArrayGetCount(aclList);
1710 for(CFIndex i = 0; i < size; i++) {
1711 SecACLRef acl = (SecACLRef) CFArrayGetValueAtIndex(aclList, i);
1712 CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit
1713 uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags);
1714 status = SecACLGetAuthorizations(acl, tags, &tagCount);
1715
1716 if (status)
1717 {
1718 sec_perror("SecACLGetAuthorizations", status);
1719 result = 1;
1720 goto cleanup;
1721 }
1722
1723 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
1724
1725 for (tagix = 0; tagix < tagCount; ++tagix)
1726 {
1727 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix];
1728 if(tag == CSSM_ACL_AUTHORIZATION_PARTITION_ID) {
1729
1730 CFArrayRef applicationList;
1731 CFStringRef promptDescription;
1732
1733 status = SecACLCopySimpleContents(acl, &applicationList, &promptDescription, &promptSelector);
1734 if(status) {
1735 sec_perror("SecACLCopySimpleContents", status);
1736 result = 1;
1737 goto cleanup;
1738 }
1739
1740 CFArrayRef partitionIDs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, partitionlist, CFSTR(","));
1741 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1742 CFDictionarySetValue(dict, CFSTR("Partitions"), partitionIDs);
1743 CFDataRef xml = CFPropertyListCreateXMLData(NULL, dict);
1744 CFStringRef xmlstr = cfToHex(xml);
1745
1746 SecACLSetSimpleContents(acl, applicationList, xmlstr, &promptSelector);
1747
1748 safe_CFRelease(&xmlstr);
1749 safe_CFRelease(&xml);
1750 safe_CFRelease(&dict);
1751 safe_CFRelease(&partitionIDs);
1752 }
1753 }
1754 }
1755
1756 status = SecKeychainItemSetAccessWithPassword(item, access, (UInt32) passwordLen, passwordBuf);
1757 if(status) {
1758 sec_perror("SecKeychainItemSetAccessWithPassword", status);
1759 result = 1;
1760 goto cleanup;
1761 }
1762 }
1763 }
1764
1765 result = 0;
1766
1767 cleanup:
1768 if(passwordBuf) {
1769 free(passwordBuf);
1770 }
1771 safe_CFRelease(&results);
1772
1773 return result;
1774 }
1775
1776
1777
1778 int
1779 keychain_find_certificate(int argc, char * const *argv)
1780 {
1781 char *emailAddress = NULL;
1782 char *name = NULL;
1783 int ch, result = 0;
1784 CFTypeRef keychainOrArray = nil;
1785 Boolean output_pem = FALSE;
1786 Boolean find_all = FALSE;
1787 Boolean print_hash = FALSE;
1788 Boolean print_email = FALSE;
1789
1790 while ((ch = getopt(argc, argv, "hac:e:mpZ")) != -1)
1791 {
1792 switch (ch)
1793 {
1794 case 'a':
1795 find_all = TRUE;
1796 break;
1797 case 'c':
1798 name = optarg;
1799 break;
1800 case 'e':
1801 emailAddress = optarg;
1802 break;
1803 case 'm':
1804 print_email = TRUE;
1805 break;
1806 case 'p':
1807 output_pem = TRUE;
1808 break;
1809 case 'Z':
1810 print_hash = TRUE;
1811 break;
1812 case '?':
1813 default:
1814 result = 2; /* @@@ Return 2 triggers usage message. */
1815 goto cleanup;
1816 }
1817 }
1818
1819 argc -= optind;
1820 argv += optind;
1821
1822 keychainOrArray = keychain_create_array(argc, argv);
1823
1824 result = do_keychain_find_certificate(keychainOrArray, name, emailAddress, print_hash, output_pem, find_all, print_email);
1825
1826 cleanup:
1827 if (keychainOrArray)
1828 CFRelease(keychainOrArray);
1829
1830 return result;
1831 }
1832
1833
1834 static int
1835 do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
1836 {
1837 SecKeychainItemRef item;
1838 SecKeychainSearchRef search = NULL;
1839 int result = 0;
1840 OSStatus status;
1841
1842 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search);
1843 if (status)
1844 {
1845 sec_perror("SecKeychainSearchCreateFromAttributes", status);
1846 result = 1;
1847 goto cleanup;
1848 }
1849
1850 while ((status = SecKeychainSearchCopyNext(search, &item)) == 0)
1851 {
1852 print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive);
1853 CFRelease(item);
1854 }
1855
1856 if (status != errSecItemNotFound)
1857 {
1858 sec_perror("SecKeychainSearchCopyNext", status);
1859 result = 1;
1860 goto cleanup;
1861 }
1862
1863 cleanup:
1864 if (search)
1865 CFRelease(search);
1866
1867 return result;
1868 }
1869
1870 static int
1871 do_keychain_dump(FILE *stream, CFTypeRef keychainOrArray, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
1872 {
1873 return do_keychain_dump_class(stream, keychainOrArray, CSSM_DL_DB_RECORD_ANY, show_data, show_raw_data, show_acl, interactive);
1874 }
1875
1876 int
1877 keychain_dump(int argc, char * const *argv)
1878 {
1879 int ch, result = 0;
1880 Boolean show_data = FALSE, show_raw_data = FALSE, show_acl = FALSE, interactive = FALSE;
1881 CFTypeRef keychainOrArray = NULL;
1882 const char *outputFilename = NULL;
1883 FILE *output;
1884
1885 while ((ch = getopt(argc, argv, "adhiro:")) != -1)
1886 {
1887 switch (ch)
1888 {
1889 case 'a':
1890 show_acl = TRUE;
1891 break;
1892 case 'd':
1893 show_data = TRUE;
1894 break;
1895 case 'i':
1896 show_acl = TRUE;
1897 interactive = TRUE;
1898 break;
1899 case 'r':
1900 show_raw_data = TRUE;
1901 break;
1902 case 'o':
1903 outputFilename = optarg;
1904 break;
1905 case '?':
1906 default:
1907 return 2; /* @@@ Return 2 triggers usage message. */
1908 }
1909 }
1910
1911 argc -= optind;
1912 argv += optind;
1913
1914 keychainOrArray = keychain_create_array(argc, argv);
1915
1916 if (outputFilename)
1917 output = fopen(outputFilename, "w");
1918 else
1919 output = stdout;
1920
1921 result = do_keychain_dump(output, keychainOrArray, show_data, show_raw_data, show_acl, interactive);
1922
1923 if (outputFilename)
1924 fclose(output);
1925
1926 if (keychainOrArray)
1927 CFRelease(keychainOrArray);
1928
1929 return result;
1930 }