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