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