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