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