static int
do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
{
	SecKeychainItemRef item;
	SecKeychainSearchRef search = NULL;
	int result = 0;
	OSStatus status;

	status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search);
	if (status)
	{
		sec_perror("SecKeychainSearchCreateFromAttributes", status);
		result = 1;
		goto cleanup;
	}

	while ((status = SecKeychainSearchCopyNext(search, &item)) == 0)
	{
		print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive);
		CFRelease(item);
	}

	if (status != errSecItemNotFound)
	{
		sec_perror("SecKeychainSearchCopyNext", status);
		result = 1;
		goto cleanup;
	}

cleanup:
	if (search)
		CFRelease(search);

	return result;
}
Esempio n. 2
0
SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray,
			 char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win)
{
    SecItemClass itemClass = kSecCertificateItemClass;
    SecKeychainSearchRef searchRef;
    SecKeychainItemRef itemRef = NULL;
    OSStatus status;
    SecKeychainAttribute attrs[1];
    const char *serialNumber = "12345678";
 //   const SecKeychainAttributeList attrList;
#if 0
    attrs[1].tag = kSecLabelItemAttr;
    attrs[1].length = strlen(nickname)+1;
    attrs[1].data = nickname;
#else
    attrs[1].tag = kSecSerialNumberItemAttr;
    attrs[1].length = (UInt32)strlen(serialNumber)+1;
    attrs[1].data = (uint8 *)serialNumber;
#endif
    SecKeychainAttributeList attrList = { 0, attrs };
 //   12 34 56 78
	status = SecKeychainSearchCreateFromAttributes(keychainOrArray,itemClass,&attrList,&searchRef);
    if (status)
    {
        printf("CERT_FindUserCertByUsage: SecKeychainSearchCreateFromAttributes:%d",(int)status);
        return NULL;
    }
	status = SecKeychainSearchCopyNext(searchRef,&itemRef);
    if (status)
    	printf("CERT_FindUserCertByUsage: SecKeychainSearchCopyNext:%d",(int)status);
    CFRelease(searchRef);
    return (SecCertificateRef)itemRef;
}
Esempio n. 3
0
OSStatus OTMacKeychain::SearchCreateFromAttributes(
    CFTypeRef keychainOrArray, CFTypeRef SecItemClass, CFTypeRef itemClass,
    const SecKeychainAttributeList* attrList,
    SecKeychainSearchRef* searchRef) const
{
    return SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass,
                                                 attrList, searchRef);
}
Esempio n. 4
0
/* find all the certs that represent the appropriate object (cert, priv key, or
 *  pub key) in the cert store.
 */
static PRUint32
collect_class(
  CK_OBJECT_CLASS objClass,
  SecItemClass itemClass,
  CK_ATTRIBUTE_PTR pTemplate, 
  CK_ULONG ulAttributeCount, 
  ckmkInternalObject ***listp,
  PRUint32 *sizep,
  PRUint32 count,
  CK_RV *pError
)
{
  ckmkInternalObject *next = NULL;
  SecKeychainSearchRef searchRef = 0;
  SecKeychainItemRef itemRef = 0;
  OSStatus error;

  /* future, build the attribute list based on the template
   * so we can refine the search */
  error = SecKeychainSearchCreateFromAttributes( 
		NULL, itemClass, NULL, &searchRef);

  while (noErr == SecKeychainSearchCopyNext(searchRef, &itemRef)) {
    /* if we don't have an internal object structure, get one */
    if ((ckmkInternalObject *)NULL == next) {
      next = nss_ZNEW(NULL, ckmkInternalObject);
      if ((ckmkInternalObject *)NULL == next) {
        *pError = CKR_HOST_MEMORY;
        goto loser;
      }
    }
    /* fill in the relevant object data */
    next->type = ckmkItem;
    next->objClass = objClass;
    next->u.item.itemRef = itemRef;
    next->u.item.itemClass = itemClass;

    /* see if this is one of the objects we are looking for */
    if( CK_TRUE == ckmk_match(pTemplate, ulAttributeCount, next) ) {
      /* yes, put it on the list */
      PUT_OBJECT(next, *pError, *sizep, count, *listp);
      next = NULL; /* this one is on the list, need to allocate a new one now */
    } else {
      /* no , release the current item and clear out the structure for reuse */
      CFRelease(itemRef);
      /* don't cache the values we just loaded */
      nsslibc_memset(next, 0, sizeof(*next));
    }
  }
loser:
  if (searchRef) {
    CFRelease(searchRef);
  }
  nss_ZFreeIf(next);
  return count;
}
void tests(void)
{
	SecKeychainRef keychain;
	ok_status(SecKeychainCreate("test", 4, "test", FALSE, NULL, &keychain),
		"create keychain");
	SecKeyRef pub_crypt = NULL, prv_crypt = NULL;
	ok_status(SecKeyCreatePair(keychain, CSSM_ALGID_RSA, 256,
		0 /* contextHandle */,
		CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_WRAP,
		CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
		CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP,
		CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE |
			CSSM_KEYATTR_SENSITIVE,
		NULL /* initialAccess */, &pub_crypt, &prv_crypt),
		"generate encryption keypair");

	SecKeyRef pub_sign = NULL, prv_sign = NULL;
	ok_status(SecKeyCreatePair(keychain, CSSM_ALGID_RSA, 256,
		0 /* contextHandle */,
		CSSM_KEYUSE_VERIFY,
		CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
		CSSM_KEYUSE_SIGN,
		CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE |
			CSSM_KEYATTR_SENSITIVE,
		NULL /* initialAccess */, &pub_sign, &prv_sign),
		"generate signing keypair");

	uint32 btrue = 1;
	uint32 bfalse = 0;
	/* uint32 prv_class = CSSM_KEYCLASS_PRIVATE_KEY; */
	SecKeychainAttribute attrs[] = 
	{
		{ kSecKeyDecrypt, sizeof(uint32), &btrue },
		{ kSecKeyEncrypt, sizeof(uint32), &bfalse },
		/* { kSecKeyKeyClass, sizeof(uint32), &prv_class } */
	};
	SecKeychainAttributeList attrList = { sizeof(attrs) / sizeof(*attrs), attrs };
	SecKeychainSearchRef search;
	OSStatus result;
	SecKeychainItemRef item;
	
	ok_status((result = SecKeychainSearchCreateFromAttributes(keychain,
		CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &search)), "create key search");
	if (result == noErr)
	{
		ok_status(SecKeychainSearchCopyNext(search, &item), "get first key");
		cmp_ok((intptr_t)prv_crypt, ==, (intptr_t)item, "is key found the right one?");
		CFRelease(item);
		item = NULL;
		is_status(SecKeychainSearchCopyNext(search, &item),
			errSecItemNotFound, "get next key");
		is((intptr_t)item, 0, "no item returned");
		CFRelease(search);
	}
/*
 * Add all of the roots in a given KC to SSL ctx's trusted anchors.
 */
OSStatus sslAddTrustedRoots(
	SSLContextRef 	ctx,
	SecKeychainRef	keychain,
	bool			*foundOne)		// RETURNED, true if we found 
									//    at least one root cert
{
	OSStatus 				ortn;
	SecCertificateRef 		secCert;
	SecKeychainSearchRef 	srch;
	
	*foundOne = false;
	ortn = SecKeychainSearchCreateFromAttributes(keychain,
		kSecCertificateItemClass,
		NULL,			// any attrs
		&srch);
	if(ortn) {
		printSslErrStr("SecKeychainSearchCreateFromAttributes", ortn);
		return ortn;
	}
	
	/*
	 * Only use root certs. Not an error if we don't find any.
	 */
	do {
		ortn = SecKeychainSearchCopyNext(srch, 
			(SecKeychainItemRef *)&secCert);
		if(ortn) {
			break;
		}
		
		/* see if it's a root */
		if(!isCertRoot(secCert)) {
			continue;
		}
		
		/* Tell Secure Transport to trust this one. */
		ortn = addTrustedSecCert(ctx, secCert, false);
		if(ortn) {
			/* fatal */
			printSslErrStr("addTrustedSecCert", ortn);
			return ortn;
		}
		CFRelease(secCert);
		*foundOne = true;
	} while(ortn == errSecSuccess);
	CFRelease(srch);
	return errSecSuccess;
}
/*
 * Add all itmes of specified class from a keychain to an array.
 * Item class are things like kSecCertificateItemClass, and
 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately.
 */
static OSStatus addKcItems(
	SecKeychainRef kcRef,
	SecItemClass itemClass,		// kSecCertificateItemClass
	CFMutableArrayRef outArray,
	unsigned *numItems)			// UPDATED on return
{
	OSStatus ortn;
	SecKeychainSearchRef srchRef;

	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
		itemClass,
		NULL,		// no attrs
		&srchRef);
	if(ortn) {
		sec_perror("SecKeychainSearchCreateFromAttributes", ortn);
		return ortn;
	}
	for(;;) {
		SecKeychainItemRef itemRef;
		ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
		if(ortn) {
			if(ortn == errSecItemNotFound) {
				/* normal search end */
				ortn = noErr;
			}
			else {
				sec_perror("SecIdentitySearchCopyNext", ortn);
			}
			break;
		}
		CFArrayAppendValue(outArray, itemRef);
		CFRelease(itemRef);		// array owns the item
		(*numItems)++;
	}
	CFRelease(srchRef);
	return ortn;
}
//	find_first_internet_password
//
//	Returns a SecKeychainItemRef for the first item
//	which matches the specified attributes. Caller is
//	responsible for releasing the item (with CFRelease).
//
SecKeychainItemRef
find_first_internet_password(CFTypeRef keychainOrArray,
	 FourCharCode itemCreator,
	 FourCharCode itemType,
	 const char *kind,
	 const char *comment,
	 const char *label,
	 const char *serverName,
	 const char *securityDomain,
	 const char *accountName,
	 const char *path,
	 UInt16 port,
	 SecProtocolType protocol,
	 SecAuthenticationType authenticationType)
{
	OSStatus status = noErr;
	SecKeychainSearchRef searchRef = NULL;
	SecKeychainItemRef itemRef = NULL;

	SecKeychainAttribute attrs[12]; // maximum number of searchable attributes
	SecKeychainAttributeList attrList = { 0, attrs };

	// the primary key for an internet password item (i.e. the combination of
	// attributes which determine whether the item is unique) consists of:
	// { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr,
	//   kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr,
	//   kSecPortItemAttr, kSecPathItemAttr }
	//
	// if we have a primary key, we don't need to search on other attributes.
	// (and we don't want to, if non-primary attributes are being updated)
	Boolean primaryKey = (accountName && securityDomain && serverName &&
						  protocol && authenticationType && port && path);

	// build the attribute list for searching
	if ((UInt32)itemCreator != 0 && !primaryKey) {
		attrs[attrList.count].tag = kSecCreatorItemAttr;
		attrs[attrList.count].length = sizeof(FourCharCode);
		attrs[attrList.count].data = (FourCharCode *)&itemCreator;
		attrList.count++;
	}
	if ((UInt32)itemType != 0 && !primaryKey) {
		attrs[attrList.count].tag = kSecTypeItemAttr;
		attrs[attrList.count].length = sizeof(FourCharCode);
		attrs[attrList.count].data = (FourCharCode *)&itemType;
		attrList.count++;
	}
	if (kind != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecDescriptionItemAttr;
		attrs[attrList.count].length = strlen(kind);
		attrs[attrList.count].data = (void*)kind;
		attrList.count++;
	}
	if (comment != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecCommentItemAttr;
		attrs[attrList.count].length = strlen(comment);
		attrs[attrList.count].data = (void*)comment;
		attrList.count++;
	}
	if (label != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecLabelItemAttr;
		attrs[attrList.count].length = strlen(label);
		attrs[attrList.count].data = (void*)label;
		attrList.count++;
	}
	if (serverName != NULL) {
		attrs[attrList.count].tag = kSecServerItemAttr;
		attrs[attrList.count].length = strlen(serverName);
		attrs[attrList.count].data = (void*)serverName;
		attrList.count++;
	}
	if (securityDomain != NULL) {
		attrs[attrList.count].tag = kSecSecurityDomainItemAttr;
		attrs[attrList.count].length = strlen(securityDomain);
		attrs[attrList.count].data = (void *)securityDomain;
		attrList.count++;
	}
	if (accountName != NULL) {
		attrs[attrList.count].tag = kSecAccountItemAttr;
		attrs[attrList.count].length = strlen(accountName);
		attrs[attrList.count].data = (void *)accountName;
		attrList.count++;
	}
	if (path != NULL) {
		attrs[attrList.count].tag = kSecPathItemAttr;
		attrs[attrList.count].length = strlen(path);
		attrs[attrList.count].data = (void *)path;
		attrList.count++;
	}
	if (port != 0) {
		attrs[attrList.count].tag = kSecPortItemAttr;
		attrs[attrList.count].length = sizeof(UInt16);
		attrs[attrList.count].data = (UInt16 *)&port;
		attrList.count++;
	}
	if ((UInt32)protocol != 0) {
		attrs[attrList.count].tag = kSecProtocolItemAttr;
		attrs[attrList.count].length = sizeof(SecProtocolType);
		attrs[attrList.count].data = (SecProtocolType *)&protocol;
		attrList.count++;
	}
	if ((UInt32)authenticationType != 0) {
		attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr;
		attrs[attrList.count].length = sizeof(SecAuthenticationType);
		attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType;
		attrList.count++;
	}

	status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef);
	if (status) {
		sec_perror("SecKeychainSearchCreateFromAttributes", status);
		goto cleanup;
	}
	// we're only interested in the first match, if there is a match at all
	status = SecKeychainSearchCopyNext(searchRef, &itemRef);
	if (status) {
		itemRef = NULL;
	}

cleanup:
	if (searchRef)
		CFRelease(searchRef);

	return itemRef;
}
Esempio n. 9
0
/* 
 * Returns true if we are to allow/trust the specified
 * cert as a PKINIT-only anchor.
 */
static bool tpCheckPkinitServerCert(
	TPCertGroup &certGroup)
{
	/* 
	 * Basic requirement: exactly one cert, self-signed.
	 * The numCerts == 1 requirement might change...
	 */
	unsigned numCerts = certGroup.numCerts();
	if(numCerts != 1) {
		tpDebug("tpCheckPkinitServerCert: too many certs");
		return false;
	}
	/* end of chain... */
	TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1);
	if(!theCert->isSelfSigned()) {
		tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed");
		return false;
	}
	const CSSM_DATA *subjectName = theCert->subjectName();
	
	/* 
	 * Open the magic keychain.
	 * We're going up and over the Sec layer here, not generally 
	 * kosher, but this is a temp hack.
	 */
	OSStatus ortn;
	SecKeychainRef kcRef = NULL;
	string fullPathName;
	const char *homeDir = getenv("HOME");
	if (homeDir == NULL)
	{
		// If $HOME is unset get the current user's home directory
		// from the passwd file.
		uid_t uid = geteuid();
		if (!uid) uid = getuid();
		struct passwd *pw = getpwuid(uid);
		if (!pw) {
			return false;
		}
		homeDir = pw->pw_dir;
	}
	fullPathName = homeDir;
	fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain";
	ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: keychain not found (1)");
		return false;
	}
	/* subsequent errors to errOut: */
	
	bool ourRtn = false;
	SecKeychainStatus kcStatus;
	CSSM_DATA_PTR subjSerial = NULL;
	CSSM_RETURN crtn;
	SecKeychainSearchRef		srchRef = NULL;
	SecKeychainAttributeList	attrList;
	SecKeychainAttribute		attrs[2];
	SecKeychainItemRef			foundItem = NULL;
	
	ortn = SecKeychainGetStatus(kcRef, &kcStatus);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: keychain not found (2)");
		goto errOut;
	}
	
	/*
	 * We already have this cert's normalized name; get its
	 * serial number.
	 */
	crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
	if(crtn) {
		/* should never happen */
		tpDebug("tpCheckPkinitServerCert: error fetching serial number");
		goto errOut;
	}
	
	attrs[0].tag    = kSecSubjectItemAttr;
	attrs[0].length = subjectName->Length;
	attrs[0].data   = subjectName->Data;
	attrs[1].tag    = kSecSerialNumberItemAttr;
	attrs[1].length = subjSerial->Length;
	attrs[1].data   = subjSerial->Data;
	attrList.count  = 2;
	attrList.attr   = attrs;
	
	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
		kSecCertificateItemClass,
		&attrList,
		&srchRef);
	if(ortn) {
		tpDebug("tpCheckPkinitServerCert: search failure");
		goto errOut;
	}
	for(;;) {
		ortn = SecKeychainSearchCopyNext(srchRef, &foundItem);
		if(ortn) {
			tpDebug("tpCheckPkinitServerCert: end search");
			break;
		}
		
		/* found a matching cert; do byte-for-byte compare */
		CSSM_DATA certData;
		ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData);
		if(ortn) {
			tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure");
			continue;
		}
		if(tpCompareCssmData(&certData, theCert->itemData())){
			tpDebug("tpCheckPkinitServerCert: FOUND CERT");
			ourRtn = true;
			break;
		}
		tpDebug("tpCheckPkinitServerCert: skipping matching cert");
		CFRelease(foundItem);
		foundItem = NULL;
	}
errOut:
	CFRELEASE(kcRef);
	CFRELEASE(srchRef);
	CFRELEASE(foundItem);
	if(subjSerial != NULL) {
		theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
	}
	return ourRtn;
}
CFDataRef decodePrivateKeyHeader(SecKeychainRef keychain, const FVPrivateKeyHeader &inHeader)
{	
	// kSecKeyLabel is defined in libsecurity_keychain/lib/SecKey.h
	SecKeychainAttribute attrs[] =
	{
		{ 6 /* kSecKeyLabel */, inHeader.publicKeyHashSize, const_cast<uint8 *>(inHeader.publicKeyHash) }
	};
	SecKeychainAttributeList attrList =
	{
		sizeof(attrs) / sizeof(SecKeychainAttribute),
		attrs
	};
	CSSM_CSP_HANDLE cspHandle = 0;
	const CSSM_KEY *cssmKey = NULL;
    const CSSM_ACCESS_CREDENTIALS *accessCred = NULL;
    CSSM_CC_HANDLE cc = 0;
	
	SecKeychainSearchRef _searchRef;
	throwIfError(SecKeychainSearchCreateFromAttributes(keychain, (SecItemClass) CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &_searchRef));
	CFRef<SecKeychainSearchRef> searchRef(_searchRef);
	
	SecKeychainItemRef _item;
    if (SecKeychainSearchCopyNext(searchRef, &_item) != 0) {
		return NULL;  // XXX possibly should throw here?
    }
	
	CFRef<SecKeyRef> keyItem(reinterpret_cast<SecKeyRef>(_item));
	throwIfError(SecKeyGetCSPHandle(keyItem, &cspHandle));
	throwIfError(SecKeyGetCSSMKey(keyItem, &cssmKey));
    throwIfError(SecKeyGetCredentials(keyItem, CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault, &accessCred));
    throwIfError(CSSM_CSP_CreateAsymmetricContext(cspHandle, cssmKey->KeyHeader.AlgorithmId, accessCred, cssmKey, CSSM_PADDING_PKCS1, &cc));
	CFDataRef result;
	
	try
	{		
		CssmMemoryFunctions memFuncs;
		throwIfError(CSSM_GetAPIMemoryFunctions(cspHandle, &memFuncs));
		CssmMemoryFunctionsAllocator allocator(memFuncs);
		
		const CssmData cipherBuf(const_cast<uint8 *>(inHeader.encryptedBlob), inHeader.encryptedBlobSize);
		CssmAutoData clearBuf(allocator);
		CssmAutoData remData(allocator);
		size_t bytesDecrypted;
		CSSM_RETURN crx = CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get());
		secinfo("FDERecovery", "decodePrivateKeyHeader: CSSM_DecryptData result: %d", crx);
		throwIfError(crx);
//		throwIfError(CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get()));
		clearBuf.length(bytesDecrypted);
//		rawKey.copy(clearBuf.get());
		result = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)clearBuf.get().data(), clearBuf.get().length());
//		result = parseKeyBlob(clearBuf.get());
	}
	catch(...)
	{
		CSSM_DeleteContext(cc);
		throw;
	}
	
	throwIfError(CSSM_DeleteContext(cc));
	
	return result;
}
static int
do_keychain_find_certificate(CFTypeRef keychainOrArray,
	const char *name,
	const char *emailAddress,
	Boolean print_hash,
	Boolean output_pem,
	Boolean find_all,
	Boolean print_email)
{
	OSStatus result = noErr;
    SecCertificateRef certificateRef = NULL;
	SecKeychainSearchRef searchRef = NULL;
	CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;

	if (find_all && emailAddress) {
		result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef);
		if (result) {
			sec_perror("SecKeychainSearchCreateForCertificateByEmail", result);
			goto cleanup;
		}
	} else {
		result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
		if (result) {
			sec_perror("SecKeychainSearchCreateFromAttributes", result);
			goto cleanup;
		}
	}

	do
	{
		if (find_all) {
			SecKeychainItemRef itemRef = NULL;
			result = SecKeychainSearchCopyNext(searchRef, &itemRef);
			if (result == errSecItemNotFound) {
				result = 0;
				break;
			}
			else if (result) {
				sec_perror("SecKeychainSearchCopyNext", result);
				goto cleanup;
			}

			if (!emailAddress && name) {
				// match name in common name
				CFStringRef nameRef = NULL;
				if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
					safe_CFRelease(&itemRef);
					continue; // no name, so no match is possible
				}
				CFRange find = { kCFNotFound, 0 };
				if (nameRef && matchRef)
					find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
				if (find.location == kCFNotFound) {
					safe_CFRelease(&nameRef);
					safe_CFRelease(&itemRef);
					continue; // no match
				}
				safe_CFRelease(&nameRef);
			}
			safe_CFRelease(&certificateRef);
			certificateRef = (SecCertificateRef) itemRef;
		}
		else { // only want the first match
			if (emailAddress) {
				result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef);
				if (result) {
					sec_perror("SecCertificateFindByEmail", result);
					goto cleanup;
				}
			} else {
				SecKeychainItemRef itemRef = NULL;
				while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) {
					if (name) {
						// match name in common name
						CFStringRef nameRef = NULL;
						if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
							safe_CFRelease(&itemRef);
							continue; // no name, so no match is possible
						}
						CFRange find = { kCFNotFound, 0 };
						if (nameRef && matchRef)
							find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
						if (find.location == kCFNotFound) {
							safe_CFRelease(&nameRef);
							safe_CFRelease(&itemRef);
							continue; // no match
						}
						safe_CFRelease(&nameRef);
					}
					break; // we have a match!
				}
				if (result == errSecItemNotFound) {
					sec_perror("SecKeychainSearchCopyNext", result);
					goto cleanup;
				}
				certificateRef = (SecCertificateRef) itemRef;
			}
		}

		// process the found certificate

		if (print_hash) {
			uint8 sha1_hash[20];
			CSSM_DATA data;
			CSSM_DATA digest;
			digest.Length = sizeof(sha1_hash);
			digest.Data = sha1_hash;
			if ((SecCertificateGetData(certificateRef, &data) == noErr) &&
				(SecDigestGetData(CSSM_ALGID_SHA1, &digest, &data) == CSSM_OK)) {
				unsigned int i;
				uint32 len = digest.Length;
				uint8 *cp = digest.Data;
				fprintf(stdout, "SHA-1 hash: ");
				for(i=0; i<len; i++) {
					fprintf(stdout, "%02X", ((unsigned char *)cp)[i]);
				}
				fprintf(stdout, "\n");
			}
		}

		if (print_email)
		{
			CFArrayRef emailAddresses = NULL;
			CFIndex ix, count;
			result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses);
			if (result)
			{
				sec_perror("SecCertificateCopyEmailAddresses", result);
				goto cleanup;
			}

			count = CFArrayGetCount(emailAddresses);
			fputs("email addresses: ", stdout);
			for (ix = 0; ix < count; ++ix)
			{
				CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix);
				const char *addr;
				char buffer[256];

				if (ix)
					fputs(", ", stdout);

				addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8);
				if (!addr)
				{
					if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8))
						addr = buffer;
				}

				fprintf(stdout, "%s", addr);
			}
			fputc('\n', stdout);

			CFRelease(emailAddresses);
		}

		if (output_pem)
		{
			CSSM_DATA certData = {};
			result = SecCertificateGetData(certificateRef, &certData);
			if (result)
			{
				sec_perror("SecCertificateGetData", result);
				goto cleanup;
			}

			print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
		}
		else
		{
			print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE);
		}
	} while (find_all);

cleanup:
	safe_CFRelease(&searchRef);
	safe_CFRelease(&certificateRef);
	safe_CFRelease(&matchRef);

	return result;
}
Esempio n. 12
0
File: tls2.c Progetto: aosm/OpenLDAP
static void
tls_get_cert_from_keychain( char *host )
{
	/* Cert info is kept in the global options. */
	struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
	if ( !lo ) return;

        /* If the server identity option is set, don't need to do anything
         * as the certificate will get set in the SSL context during context
         * initialization. 
         */
        if (lo->ldo_tls_server_ident_ref_name) {
                return;
        }

	/* If the cert is set in the options, don't override it. */
	if ( lo->ldo_tls_cacertfile || lo->ldo_tls_certfile ) {
		Debug( LDAP_DEBUG_ANY,
		       "TLS: not reading certificate from keychain, option %s is set\n",
		       lo->ldo_tls_cacertfile ? "cacertfile" : "certfile", 0, 0 );

		return;
	}

	SecKeychainRef keychainRef = NULL;
	OSStatus status = SecKeychainOpen( SYSTEM_KEYCHAIN_PATH, &keychainRef );
	if ( status != errSecSuccess ) {
		Debug( LDAP_DEBUG_ANY, "TLS: SecKeychainOpen failed for keychain %s: %d",
		       SYSTEM_KEYCHAIN_PATH, (int)status, 0 );
		syslog( LOG_ERR, "TLS: SecKeychainOpen failed for keychain %s: %d",
			SYSTEM_KEYCHAIN_PATH, (int)status, 0 );
		cssmPerror( "SecKeychainOpen", status );
		return;
	}

	SecKeychainSearchRef searchRef = NULL;
	status = SecKeychainSearchCreateFromAttributes( keychainRef, kSecCertificateItemClass, NULL, &searchRef );
	if ( status != errSecSuccess ) {
		Debug( LDAP_DEBUG_ANY, "TLS: SecKeychainSearchCreateFromAttributes failed: %d",
		       (int)status, 0, 0 );
		syslog( LOG_ERR, "TLS: SecKeychainSearchCreateFromAttributes failed: %d", (int)status );
		cssmPerror( "SecKeychainSearchCreateFromAttributes", status );
	}

	while ( status == errSecSuccess ) {

		SecCertificateRef certificateRef = NULL;
		status = SecKeychainSearchCopyNext( searchRef, (SecKeychainItemRef*)&certificateRef );

		/* Bail on any error. */
		if ( status != errSecSuccess ) {
			/* Only complain if the error is something other than
			 * the normal search end. 
			 */
			if ( status != errSecItemNotFound ) {
				Debug( LDAP_DEBUG_ANY, "TLS: SecKeychainSearchCopyNext failed: %d",
				       (int)status, 0, 0 );
				syslog( LOG_ERR, "SecKeychainSearchCopyNext failed: %d", (int)status );
				cssmPerror( "SecKeychainSearchCopyNext", status );
			}
			break;
		}

		/* Extract the name from the certificate.  Will check against the host
		 * name passed in.
		 */
		CFStringRef commonName = NULL;
		status = SecCertificateCopyCommonName( certificateRef, &commonName );
		if ( status != errSecSuccess || !commonName ) {
			Debug( LDAP_DEBUG_ANY, "TLS: SecCertificateCopyCommonName failed: %d",
			       (int)status, 0, 0 );
			cssmPerror( "SecCertificateCopyCommonName", status );

			/* Reset 'status' to allow the loop to continue.  No need to
			 * stop just because we can't get the name from one of the
			 * certs.
			 */
			status = errSecSuccess;
		}
		else {
			/* If the common name in the certificate matches the host name, save
			 * the certificate reference in the options data for later use.
			 */
			char commonNameCStr[PATH_MAX];
			if ( !CFStringGetCString( commonName,
						  commonNameCStr,
						  sizeof(commonNameCStr),
						  kCFStringEncodingUTF8 ) )
			{
				Debug( LDAP_DEBUG_ANY,
				       "TLS: Unable to convert certificate common name CFString into C String",
				       0, 0, 0);
			}
			CFRelease( commonName );

			if ( commonNameCStr && strcmp( commonNameCStr, host ) == 0 ) {
				if ( lo->ldo_tls_cert_ref ) {
					CFRelease( lo->ldo_tls_cert_ref );
				}

				lo->ldo_tls_cert_ref = certificateRef;
				status = !errSecSuccess;  /* cert found - terminate the loop */
			}
		}

		/* Only release the ref if we don't care about it.  Refs we care
		 * about get released later (when the connection is closed).
		 */
		if ( lo->ldo_tls_cert_ref != certificateRef ) {
			CFRelease( certificateRef );
		}
	}

	if ( searchRef ) {
		CFRelease( searchRef );
	}

	CFRelease( keychainRef );

	Debug( LDAP_DEBUG_ANY,
	       "TLS: %s certificate in keychain for host \"%s\"\n",
	       lo->ldo_tls_cert_ref ? "found" : "did not find", host, 0 );
}
Esempio n. 13
0
bool getCredentialsFromKeychain(const URI &uri, ClientRequest::ptr priorRequest,
    std::string &scheme, std::string &realm, std::string &username,
    std::string &password, size_t attempts)
{
    if (attempts != 1)
        return false;
    bool proxy =
       priorRequest->response().status.status == PROXY_AUTHENTICATION_REQUIRED;
    const ChallengeList &challengeList = proxy ?
        priorRequest->response().response.proxyAuthenticate :
        priorRequest->response().response.wwwAuthenticate;
    if (isAcceptable(challengeList, "Basic"))
        scheme = "Basic";
    else if (isAcceptable(challengeList, "Digest"))
        scheme = "Digest";
    else
        return false;

    std::vector<SecKeychainAttribute> attrVector;
    std::string host = uri.authority.host();
    attrVector.push_back((SecKeychainAttribute){kSecServerItemAttr, host.size(),
       (void *)host.c_str()});

    UInt32 port = 0;
    if (uri.authority.portDefined()) {
        port = uri.authority.port();
        attrVector.push_back((SecKeychainAttribute){kSecPortItemAttr,
           sizeof(UInt32), &port});
    }
    SecProtocolType protocol;
    if (proxy && priorRequest->request().requestLine.method == CONNECT)
        protocol = kSecProtocolTypeHTTPSProxy;
    else if (proxy)
        protocol = kSecProtocolTypeHTTPProxy;
    else if (uri.scheme() == "https")
        protocol = kSecProtocolTypeHTTPS;
    else if (uri.scheme() == "http")
        protocol = kSecProtocolTypeHTTP;
    else
        MORDOR_NOTREACHED();
    attrVector.push_back((SecKeychainAttribute){kSecProtocolItemAttr,
        sizeof(SecProtocolType), &protocol});

    ScopedCFRef<SecKeychainSearchRef> search;
    SecKeychainAttributeList attrList;
    attrList.count = (UInt32)attrVector.size();
    attrList.attr = &attrVector[0];

    OSStatus status = SecKeychainSearchCreateFromAttributes(NULL,
        kSecInternetPasswordItemClass, &attrList, &search);
    if (status != 0)
        return false;
    ScopedCFRef<SecKeychainItemRef> item;
    status = SecKeychainSearchCopyNext(search, &item);
    if (status != 0)
        return false;
    SecKeychainAttributeInfo info;
    SecKeychainAttrType tag = kSecAccountItemAttr;
    CSSM_DB_ATTRIBUTE_FORMAT format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
    info.count = 1;
    info.tag = (UInt32 *)&tag;
    info.format = (UInt32 *)&format;
    
    SecKeychainAttributeList *attrs;
    UInt32 passwordLength = 0;
    void *passwordBytes = NULL;

    status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attrs,
        &passwordLength, &passwordBytes);
    if (status != 0)
        return false;

    try {
        username.assign((const char *)attrs->attr[0].data, attrs->attr[0].length);
        password.assign((const char *)passwordBytes, passwordLength);
    } catch (...) {
        SecKeychainItemFreeContent(attrs, passwordBytes);
        throw;
    }
    SecKeychainItemFreeContent(attrs, passwordBytes);
    return true;
}
		CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &search)), "create key search");
	if (result == noErr)
	{
		ok_status(SecKeychainSearchCopyNext(search, &item), "get first key");
		cmp_ok((intptr_t)prv_crypt, ==, (intptr_t)item, "is key found the right one?");
		CFRelease(item);
		item = NULL;
		is_status(SecKeychainSearchCopyNext(search, &item),
			errSecItemNotFound, "get next key");
		is((intptr_t)item, 0, "no item returned");
		CFRelease(search);
	}
	
	SecKeychainAttribute attrs2[] = { { kSecKeySign, sizeof(btrue), &btrue } };
	SecKeychainAttributeList attrList2 = { sizeof(attrs2) / sizeof(*attrs2), attrs2 };
	ok_status((result = SecKeychainSearchCreateFromAttributes(keychain,
		CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList2, &search)), "create private signing key search");
	
	if (result == noErr)
	{
		ok_status(SecKeychainSearchCopyNext(search, &item), "get first key");
		cmp_ok((intptr_t)prv_sign, ==, (intptr_t)item, "is key found the right one?");
		CFRelease(item);
		is_status(SecKeychainSearchCopyNext(search, &item),
			errSecItemNotFound, "get next key");
		CFRelease(search);
	}
	
	CFRelease(pub_crypt);
	CFRelease(prv_crypt);
	CFRelease(pub_sign);
	CFRelease(prv_sign);
//	find_unique_certificate
//
//	Returns a SecKeychainItemRef for the certificate
//	in the specified keychain (or keychain list)
//	which is a unique match for either the specified name
//	or SHA-1 hash. If more than one match exists, the
//	certificate is not unique and none are returned. Caller is
//	responsible for releasing the item (with CFRelease).
//
SecKeychainItemRef
find_unique_certificate(CFTypeRef keychainOrArray,
	const char *name,
	const char *hash)
{
	OSStatus status = noErr;
	SecKeychainSearchRef searchRef = NULL;
	SecKeychainItemRef uniqueItemRef = NULL;

	status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
	if (status) {
		return uniqueItemRef;
	}

	// check input hash string and convert to data
	CSSM_DATA hashData = { 0, NULL };
	if (hash) {
		CSSM_SIZE len = strlen(hash)/2;
		hashData.Length = len;
		hashData.Data = (uint8 *)malloc(hashData.Length);
		fromHex(hash, &hashData);
	}

	// filter candidates against the hash (or the name, if no hash provided)
	CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
	Boolean exactMatch = FALSE;

	CSSM_DATA certData = { 0, NULL };
	SecKeychainItemRef candidate = NULL;

	while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) {
		SecCertificateRef cert = (SecCertificateRef)candidate;
		if (SecCertificateGetData(cert, &certData) != noErr) {
			safe_CFRelease(&candidate);
			continue;
		}
		if (hash) {
			uint8 candidate_sha1_hash[20];
			CSSM_DATA digest;
			digest.Length = sizeof(candidate_sha1_hash);
			digest.Data = candidate_sha1_hash;
			if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) &&
				(hashData.Length == digest.Length) &&
				(!memcmp(hashData.Data, digest.Data, digest.Length))) {
				exactMatch = TRUE;
				uniqueItemRef = candidate; // currently retained
				break; // we're done - can't get more exact than this
			}
		} else {
			// copy certificate name
			CFStringRef nameRef = NULL;
			if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) {
				safe_CFRelease(&candidate);
				continue; // no name, so no match is possible
			}
			CFIndex nameLen = CFStringGetLength(nameRef);
			CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
			char *nameBuf = (char *)malloc(bufLen);
			if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
				nameBuf[0]=0;

			CFRange find = { kCFNotFound, 0 };
			if (nameRef && matchRef)
				find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
			Boolean isExact = (find.location == 0 && find.length == nameLen);
			if (find.location == kCFNotFound) {
				free(nameBuf);
				safe_CFRelease(&nameRef);
				safe_CFRelease(&candidate);
				continue; // no match
			}
			if (uniqueItemRef) {	// got two matches
				if (exactMatch && !isExact)	{	// prior is better; ignore this one
					free(nameBuf);
					safe_CFRelease(&nameRef);
					safe_CFRelease(&candidate);
					continue;
				}
				if (exactMatch == isExact) {	// same class of match
					if (CFEqual(uniqueItemRef, candidate)) {	// same certificate
						free(nameBuf);
						safe_CFRelease(&nameRef);
						safe_CFRelease(&candidate);
						continue;
					}
					// ambiguity - must fail
					sec_error("\"%s\" is ambiguous, matches more than one certificate", name);
					free(nameBuf);
					safe_CFRelease(&nameRef);
					safe_CFRelease(&candidate);
					safe_CFRelease(&uniqueItemRef);
					break;
				}
				safe_CFRelease(&uniqueItemRef); // about to replace with this one
			}
			uniqueItemRef = candidate; // currently retained
			exactMatch = isExact;
			free(nameBuf);
			safe_CFRelease(&nameRef);
		}
	}

	safe_CFRelease(&searchRef);
	safe_CFRelease(&matchRef);
	if (hashData.Data) {
		free(hashData.Data);
	}

	return uniqueItemRef;
}
//	find_first_generic_password
//
//	Returns a SecKeychainItemRef for the first item
//	which matches the specified attributes. Caller is
//	responsible for releasing the item (with CFRelease).
//
SecKeychainItemRef
find_first_generic_password(CFTypeRef keychainOrArray,
							FourCharCode itemCreator,
							FourCharCode itemType,
							const char *kind,
							const char *value,
							const char *comment,
							const char *label,
							const char *serviceName,
							const char *accountName)
{
	OSStatus status = noErr;
	SecKeychainSearchRef searchRef = NULL;
	SecKeychainItemRef itemRef = NULL;

	SecKeychainAttribute attrs[8]; // maximum number of searchable attributes
	SecKeychainAttributeList attrList = { 0, attrs };

	// the primary key for a generic password item (i.e. the combination of
	// attributes which determine whether the item is unique) consists of:
	// { kSecAccountItemAttr, kSecServiceItemAttr }
	//
	// if we have a primary key, we don't need to search on other attributes
	// (and we don't want to, if non-primary attributes are being updated)
	Boolean primaryKey = (accountName && serviceName);

	// build the attribute list for searching
	if ((UInt32)itemCreator != 0 && !primaryKey) {
		attrs[attrList.count].tag = kSecCreatorItemAttr;
		attrs[attrList.count].length = sizeof(FourCharCode);
		attrs[attrList.count].data = (FourCharCode *)&itemCreator;
		attrList.count++;
	}
	if ((UInt32)itemType != 0 && !primaryKey) {
		attrs[attrList.count].tag = kSecTypeItemAttr;
		attrs[attrList.count].length = sizeof(FourCharCode);
		attrs[attrList.count].data = (FourCharCode *)&itemType;
		attrList.count++;
	}
	if (kind != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecDescriptionItemAttr;
		attrs[attrList.count].length = strlen(kind);
		attrs[attrList.count].data = (void*)kind;
		attrList.count++;
	}
	if (value != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecGenericItemAttr;
		attrs[attrList.count].length = strlen(value);
		attrs[attrList.count].data = (void*)value;
		attrList.count++;
	}
	if (comment != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecCommentItemAttr;
		attrs[attrList.count].length = strlen(comment);
		attrs[attrList.count].data = (void*)comment;
		attrList.count++;
	}
	if (label != NULL && !primaryKey) {
		attrs[attrList.count].tag = kSecLabelItemAttr;
		attrs[attrList.count].length = strlen(label);
		attrs[attrList.count].data = (void*)label;
		attrList.count++;
	}
	if (serviceName != NULL) {
		attrs[attrList.count].tag = kSecServiceItemAttr;
		attrs[attrList.count].length = strlen(serviceName);
		attrs[attrList.count].data = (void*)serviceName;
		attrList.count++;
	}
	if (accountName != NULL) {
		attrs[attrList.count].tag = kSecAccountItemAttr;
		attrs[attrList.count].length = strlen(accountName);
		attrs[attrList.count].data = (void*)accountName;
		attrList.count++;
	}

	status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef);
	if (status) {
		sec_perror("SecKeychainSearchCreateFromAttributes", status);
		goto cleanup;
	}
	// we're only interested in the first match, if there is a match at all
	status = SecKeychainSearchCopyNext(searchRef, &itemRef);
	if (status) {
		itemRef = NULL;
	}

cleanup:
	if (searchRef)
		CFRelease(searchRef);

	return itemRef;
}
Esempio n. 17
0
static int 
keychain_iter_start(hx509_context context,
		    hx509_certs certs, void *data, void **cursor)
{
    struct ks_keychain *ctx = data;
    struct iter *iter;

    iter = calloc(1, sizeof(*iter));
    if (iter == NULL) {
	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
	return ENOMEM;
    }

    if (ctx->anchors) {
        CFArrayRef anchors;
	int ret;
	int i;

	ret = hx509_certs_init(context, "MEMORY:ks-file-create", 
			       0, NULL, &iter->certs);
	if (ret) {
	    free(iter);
	    return ret;
	}

	ret = SecTrustCopyAnchorCertificates(&anchors);
	if (ret != 0) {
	    hx509_certs_free(&iter->certs);
	    free(iter);
	    hx509_set_error_string(context, 0, ENOMEM, 
				   "Can't get trust anchors from Keychain");
	    return ENOMEM;
	}
	for (i = 0; i < CFArrayGetCount(anchors); i++) {
	    SecCertificateRef cr; 
	    hx509_cert cert;
	    CSSM_DATA cssm;

	    cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);

	    SecCertificateGetData(cr, &cssm);

	    ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert);
	    if (ret)
		continue;

	    ret = hx509_certs_add(context, iter->certs, cert);
	    hx509_cert_free(cert);
	}
	CFRelease(anchors);
    }

    if (iter->certs) {
	int ret;
	ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
	if (ret) {
	    hx509_certs_free(&iter->certs);
	    free(iter);
	    return ret;
	}
    } else {
	OSStatus ret;

	ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
						    kSecCertificateItemClass,
						    NULL,
						    &iter->searchRef);
	if (ret) {
	    free(iter);
	    hx509_set_error_string(context, 0, ret, 
				   "Failed to start search for attributes");
	    return ENOMEM;
	}
    }

    *cursor = iter;
    return 0;
}
Esempio n. 18
0
static int
keychain_iter(hx509_context context,
	      hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
{
    SecKeychainAttributeList *attrs = NULL;
    SecKeychainAttributeInfo attrInfo;
    uint32 attrFormat[1] = { 0 };
    SecKeychainItemRef itemRef;
    SecItemAttr item[1];
    struct iter *iter = cursor;
    OSStatus ret;
    UInt32 len;
    void *ptr = NULL;

    if (iter->certs)
	return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);

    *cert = NULL;

    ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
    if (ret == errSecItemNotFound)
	return 0;
    else if (ret != 0)
	return EINVAL;
	
    /*
     * Pick out certificate and matching "keyid"
     */

    item[0] = kSecPublicKeyHashItemAttr;

    attrInfo.count = 1;
    attrInfo.tag = item;
    attrInfo.format = attrFormat;
  
    ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
					       &attrs, &len, &ptr);
    if (ret)
	return EINVAL;

    ret = hx509_cert_init_data(context, ptr, len, cert);
    if (ret)
	goto out;

    /* 
     * Find related private key if there is one by looking at
     * kSecPublicKeyHashItemAttr == kSecKeyLabel
     */
    {
	SecKeychainSearchRef search;
	SecKeychainAttribute attrKeyid;
	SecKeychainAttributeList attrList;

	attrKeyid.tag = kSecKeyLabel;
	attrKeyid.length = attrs->attr[0].length;
	attrKeyid.data = attrs->attr[0].data;
	
	attrList.count = 1;
	attrList.attr = &attrKeyid;

	ret = SecKeychainSearchCreateFromAttributes(NULL,
						    CSSM_DL_DB_RECORD_PRIVATE_KEY,
						    &attrList,
						    &search);
	if (ret) {
	    ret = 0;
	    goto out;
	}

	ret = SecKeychainSearchCopyNext(search, &itemRef);
	CFRelease(search);
	if (ret == errSecItemNotFound) {
	    ret = 0;
	    goto out;
	} else if (ret) {
	    ret = EINVAL;
	    goto out;
	}
	set_private_key(context, itemRef, *cert);
    }

out:
    SecKeychainItemFreeAttributesAndData(attrs, ptr);

    return ret;
}
int main(int argc, char **argv)
{
	if(argc < 2) {
		usage(argv);
	}
	
	bool print_cert = false;
	bool allCerts = false;
	const char *emailAddress = NULL;
	bool addToKC = false;
	
	extern int optind;
	optind = 1;

	if(argv[1][0] != '-') {
		/* normal case, email address specified */
		emailAddress = argv[1];
		optind++;
	}
	
	extern char *optarg;
	int arg;
	while ((arg = getopt(argc, argv, "aphA")) != -1) {
		switch (arg) {
			case 'p':
				print_cert = true;
				break;
			case 'a':
				allCerts = true;
				break;
			case 'A':
				addToKC = true;
				break;
			case 'h':
			default:
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	if(!allCerts && (emailAddress == NULL)) {
		printf("***You must specify either an email address or the -a option.\n");
		exit(1);
	}
	
	OSStatus					ortn;
	SecKeychainSearchRef		srch;
	SecKeychainAttributeList	attrList;
	SecKeychainAttribute		attr;
	unsigned					numCerts = 0;
	
	if(emailAddress) {
		attr.tag = kSecAlias;			// i.e., email address
		attr.length = strlen(emailAddress);
		attr.data = (void *)emailAddress;
		attrList.count = 1;
		attrList.attr = &attr;
	}
	else {
		attrList.count = 0;
		attrList.attr = NULL;
	}
	ortn = SecKeychainSearchCreateFromAttributes(NULL,	// default search list
		kSecCertificateItemClass,
		&attrList,
		&srch);
	if(ortn) {
		cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
		exit(1);
	}
	
	do {
		SecCertificateRef certRef = NULL;
		CSSM_DATA certData;
		
		ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)&certRef);
		if(ortn) {
			break;
		}
		ortn = SecCertificateGetData(certRef, &certData);
		if(ortn) {
			cssmPerror("SecCertificateGetData", ortn);
			continue;
		}
		
		printf("=== Cert %u ===\n", numCerts);
		printCertName(certData.Data, certData.Length, NameBoth);
		if(print_cert) {
			printCert(certData.Data, certData.Length, CSSM_FALSE);
		}
		if(addToKC) {
			/* 
			 * Can't call SecCertificateAddToKeychain directly since this 
			 * cert already has a keychain. 
			 */
			SecCertificateRef newCertRef = NULL;
			ortn = SecCertificateCreateFromData(&certData,	
				CSSM_CERT_X_509v3,	CSSM_CERT_ENCODING_DER, 
				&newCertRef);
			if(ortn) {
				cssmPerror("SecCertificateCreateFromData", ortn);
				printf("***Error adding this cert to default keychain.\n");
			}
			else {
				ortn = SecCertificateAddToKeychain(newCertRef, NULL);
				if(ortn) {
					cssmPerror("SecCertificateAddToKeychain", ortn);
					printf("***Error adding this cert to default keychain.\n");
				}
				else {
					printf("...cert added to default keychain.\n");
				}
				CFRelease(newCertRef);
			}
		}
		CFRelease(certRef);
		numCerts++;
	} while(ortn == noErr);
	printf("...%u certs found matching email address \'%s\'\n", numCerts, 
		emailAddress ? emailAddress : "<any>");
	CFRelease(srch);
	return 0;
}