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