//
// Load root (anchor) certificates from disk
//
void TrustStore::loadRootCertificates()
{
	StLock<Mutex> _(mMutex);

	CFRef<CFArrayRef> anchors;
	OSStatus ortn;

	/*
	 * Get the current set of all positively trusted anchors.
	 */
	ortn = SecTrustSettingsCopyUnrestrictedRoots(
		true, true, true,		/* all domains */
		anchors.take());
	if(ortn) {
		MacOSError::throwMe(ortn);
	}

	// how many data bytes do we need?
	size_t size = 0;
	CFIndex numCerts = CFArrayGetCount(anchors);
	CSSM_RETURN crtn;
	for(CFIndex dex=0; dex<numCerts; dex++) {
		SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex);
		CSSM_DATA certData;
		crtn = SecCertificateGetData(certRef, &certData);
		if(crtn) {
			CssmError::throwMe(crtn);
		}
		size += certData.Length;
	}
	mRootBytes.length(size);

	// fill CssmData vector while copying data bytes together
	mRoots.clear();
	uint8 *base = mRootBytes.data<uint8>();
	for(CFIndex dex=0; dex<numCerts; dex++) {
		SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex);
		CSSM_DATA certData;
		SecCertificateGetData(certRef, &certData);
		memcpy(base, certData.Data, certData.Length);
		mRoots.push_back(CssmData(base, certData.Length));
		base += certData.Length;
	}

	secdebug("anchors", "%ld anchors loaded", (long)numCerts);

	mRootsValid = true;			// ready to roll
}
Exemplo n.º 2
0
static void writePeerCerts(
	CFArrayRef			peerCerts,
	const char			*fileBase)
{
	CFIndex numCerts;
	SecCertificateRef certRef;
	OSStatus ortn;
	CSSM_DATA certData;
	CFIndex i;
	char fileName[100];
	
	if(peerCerts == NULL) {
		return;
	}
	numCerts = CFArrayGetCount(peerCerts);
	for(i=0; i<numCerts; i++) {
		sprintf(fileName, "%s%02d.cer", fileBase, i);
		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
		ortn = SecCertificateGetData(certRef, &certData);
		if(ortn) {
			printf("***SecCertificateGetData returned %d\n", ortn);
			continue;
		}
		writeFile(fileName, certData.Data, certData.Length);
	}
	printf("...wrote %d certs to fileBase %s\n", numCerts, fileBase);
}
Exemplo n.º 3
0
static void showPeerCerts(
	CFArrayRef			peerCerts,
	CSSM_BOOL			verbose)
{
	CFIndex numCerts;
	SecCertificateRef certRef;
	OSStatus ortn;
	CSSM_DATA certData;
	CFIndex i;
	
	if(peerCerts == NULL) {
		return;
	}
	numCerts = CFArrayGetCount(peerCerts);
	for(i=0; i<numCerts; i++) {
		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
		ortn = SecCertificateGetData(certRef, &certData);
		if(ortn) {
			printf("***SecCertificateGetData returned %d\n", ortn);
			continue;
		}
		printf("\n================== Peer Cert %d ===================\n\n", i);
		printCert(certData.Data, certData.Length, verbose);
		printf("\n=============== End of Peer Cert %d ===============\n", i);
	}
}
Exemplo n.º 4
0
/* Convert a SecCertificateRef to an SSLCertificate * */
static OSStatus secCertToSslCert(
	SSLContext			*ctx,
	SecCertificateRef 	certRef,
	SSLCertificate		**sslCert)
{
	CSSM_DATA		certData;		// struct is transient, referent owned by
									//   Sec layer
	OSStatus		ortn;
	SSLCertificate	*thisSslCert = NULL;

	ortn = SecCertificateGetData(certRef, &certData);
	if(ortn) {
		sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn);
		return ortn;
	}

	thisSslCert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
	if(thisSslCert == NULL) {
		return memFullErr;
	}
	if(SSLAllocBuffer(&thisSslCert->derCert, certData.Length,
			ctx)) {
		return memFullErr;
	}
	memcpy(thisSslCert->derCert.data, certData.Data, certData.Length);
	thisSslCert->derCert.length = certData.Length;
	*sslCert = thisSslCert;
	return noErr;
}
Exemplo n.º 5
0
//
// Ditto, given a SecCertificateRef
//
void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest)
{
	assert(cert);
	CSSM_DATA certData;
	MacOSError::check(SecCertificateGetData(cert, &certData));
	hashOfCertificate(certData.Data, certData.Length, digest);
}
Exemplo n.º 6
0
/*
 * Given a certificate, obtain the DER-encoded issuer and serial number. Result
 * is mallocd and must be freed by caller.
 */
static OSStatus pkinit_get_cert_issuer_sn(
    SecCertificateRef certRef,
    CSSM_DATA *issuerSerial)            /* mallocd and RETURNED */
{
    OSStatus ortn;
    CSSM_DATA certData;
    krb5_data INIT_KDATA(issuerSerialKrb);
    krb5_data certDataKrb;
    krb5_error_code krtn;

    assert(certRef != NULL);
    assert(issuerSerial != NULL);

    ortn = SecCertificateGetData(certRef, &certData);
    if(ortn) {
        pkiCssmErr("SecCertificateGetData", ortn);
        return ortn;
    }
    PKI_CSSM_TO_KRB_DATA(&certData, &certDataKrb);
    krtn = krb5int_pkinit_get_issuer_serial(&certDataKrb, &issuerSerialKrb);
    if(krtn) {
        return CSSMERR_CL_INVALID_DATA;
    }
    PKI_KRB_TO_CSSM_DATA(&issuerSerialKrb, issuerSerial);
    return noErr;
}
Exemplo n.º 7
0
/*
 * Convert CFArray of SecCertificateRefs to a mallocd array of krb5_datas.
 */
static krb5_error_code pkiCertArrayToKrb5Data(
    CFArrayRef  cf_certs,
    unsigned	*num_all_certs,
    krb5_data   **all_certs)	
{
    CFIndex num_certs;
    krb5_data *allCerts = NULL;
    krb5_error_code krtn = 0;
    unsigned dex;
    
    if(cf_certs == NULL) {
	*all_certs = NULL;
	return 0;
    }
    num_certs = CFArrayGetCount(cf_certs);
    *num_all_certs = (unsigned)num_certs;
    if(num_certs == 0) {
	*all_certs = NULL;
	return 0;
    }
    allCerts = (krb5_data *)malloc(sizeof(krb5_data) * num_certs);
    if(allCerts == NULL) {
	return ENOMEM;
    }
    for(dex=0; dex<num_certs; dex++) {	
	CSSM_DATA cert_data;
	OSStatus ortn;
	SecCertificateRef sec_cert;
	
	sec_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cf_certs, dex);
	ortn = SecCertificateGetData(sec_cert, &cert_data);
	if(ortn) {
	    pkiCssmErr("SecCertificateGetData", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    break;
	}
	krtn = pkiCssmDataToKrb5Data(&cert_data, &allCerts[dex]);
	if(krtn) {
	    break;
	}
    }
    if(krtn) {
	if(allCerts) {
	    free(allCerts);
	}
    }
    else {
	*all_certs = allCerts;
    }
    return krtn;
}
OSStatus CertParser::initWithSecCert(
	SecCertificateRef 	secCert)
{
	OSStatus ortn;
	CSSM_DATA certData;
	
	assert(mClHand == 0);
	ortn = SecCertificateGetCLHandle(secCert, &mClHand);
	if(ortn) {
		return ortn;
	}
	ortn = SecCertificateGetData(secCert, &certData);
	if(ortn) {
		return ortn;
	}
	return (OSStatus)initWithData(certData);
}
Exemplo n.º 9
0
void extract_certificate_from_identity(const void *value, void *context)
{
	if (!context || !value)
		return;
		
	CSSM_DATA certData = {0,};
	SecCertificateRef certificateRef;
	OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)value, &certificateRef);
	if (!status)
	{
		status = SecCertificateGetData(certificateRef, &certData);
			CFRelease(certificateRef);

		if (!status)
		{
			CFDataRef cert = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certData.Data, certData.Length);
			CFArrayAppendValue((CFMutableArrayRef)context, cert);
			CFRelease(cert);
			if (certData.Data)
				free(certData.Data);
		}
	}
}
Exemplo n.º 10
0
/*
 * Parse a ContentInfo as best we can. All return fields are optional.
 * If signer_cert_status is NULL on entry, NO signature or cert evaluation 
 * will be performed. 
 */
krb5_error_code krb5int_pkinit_parse_cms_msg(
    const krb5_data	    *content_info,
    krb5_pkinit_cert_db_t   cert_db,		/* may be required for SignedData */
    krb5_boolean	    is_client_msg,	/* TRUE : msg is from client */
    krb5_boolean	    *is_signed,		/* RETURNED */
    krb5_boolean	    *is_encrypted,	/* RETURNED */
    krb5_data		    *raw_data,		/* RETURNED */
    krb5int_cms_content_type *inner_content_type,/* Returned, ContentType of */
						/*    EncapsulatedData */
    krb5_data		    *signer_cert,	/* RETURNED */
    krb5int_cert_sig_status *signer_cert_status,/* RETURNED */
    unsigned		    *num_all_certs,	/* size of *all_certs RETURNED */
    krb5_data		    **all_certs)	/* entire cert chain RETURNED */
{
    SecPolicySearchRef policy_search = NULL;
    SecPolicyRef policy = NULL;
    OSStatus ortn;
    krb5_error_code krtn = 0;
    CMSDecoderRef decoder = NULL;
    size_t num_signers;
    CMSSignerStatus signer_status;
    OSStatus cert_verify_status;
    CFArrayRef cf_all_certs = NULL;
    int msg_is_signed = 0;
    
    if(content_info == NULL) {
	pkiDebug("krb5int_pkinit_parse_cms_msg: no ContentInfo\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    ortn = CMSDecoderCreate(&decoder);
    if(ortn) {
	return ENOMEM;
    }
    ortn = CMSDecoderUpdateMessage(decoder, content_info->data, content_info->length);
    if(ortn) {
	/* no verify yet, must be bad message */
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }
    ortn = CMSDecoderFinalizeMessage(decoder);
    if(ortn) {
	pkiCssmErr("CMSDecoderFinalizeMessage", ortn);
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }

    /* expect zero or one signers */
    ortn = CMSDecoderGetNumSigners(decoder, &num_signers);
    switch(num_signers) {
	case 0:
	    msg_is_signed = 0;
	    break;
	case 1:
	    msg_is_signed = 1;
	    break;
	default:
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
    }

    /*
     * We need a cert verify policy even if we're not actually evaluating 
     * the cert due to requirements in libsecurity_smime.
     */
    ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
	is_client_msg ? &CSSMOID_APPLE_TP_PKINIT_CLIENT : &CSSMOID_APPLE_TP_PKINIT_SERVER, 
	NULL, &policy_search);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCreate", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    ortn = SecPolicySearchCopyNext(policy_search, &policy);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCopyNext", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    
    /* get some basic status that doesn't need heavyweight evaluation */
    if(msg_is_signed) {
	if(is_signed) {
	    *is_signed = TRUE;
	}
	if(inner_content_type) {
	    CSSM_OID ec_oid = {0, NULL};
	    CFDataRef ec_data = NULL;
	    
	    krb5int_cms_content_type ctype;
	    
	    ortn = CMSDecoderCopyEncapsulatedContentType(decoder, &ec_data);
	    if(ortn || (ec_data == NULL)) {
		pkiCssmErr("CMSDecoderCopyEncapsulatedContentType", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ec_oid.Data = (uint8 *)CFDataGetBytePtr(ec_data);
	    ec_oid.Length = CFDataGetLength(ec_data);
	    if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_Data)) {
		ctype = ECT_Data;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_SignedData)) {
		ctype = ECT_SignedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EnvelopedData)) {
		ctype = ECT_EnvelopedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EncryptedData)) {
		ctype = ECT_EncryptedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_AUTH_DATA)) {
		ctype = ECT_PkAuthData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_RKEY_DATA)) {
		ctype = ECT_PkReplyKeyKata;
	    }
	    else {
		ctype = ECT_Other;
	    }
	    *inner_content_type = ctype;
	    CFRelease(ec_data);
	}
	
	/* 
	 * Get SignedData's certs if the caller wants them
	 */
	if(all_certs) {	    
	    ortn = CMSDecoderCopyAllCerts(decoder, &cf_all_certs);
	    if(ortn) {
		pkiCssmErr("CMSDecoderCopyAllCerts", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiCertArrayToKrb5Data(cf_all_certs, num_all_certs, all_certs);
	    if(krtn) {
		goto errOut;
	    }
	}
	
	/* optional signer cert */
	if(signer_cert) {
	    SecCertificateRef sec_signer_cert = NULL;
	    CSSM_DATA cert_data;

	    ortn = CMSDecoderCopySignerCert(decoder, 0, &sec_signer_cert);
	    if(ortn) {
		/* should never happen if it's signed */
		pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ortn = SecCertificateGetData(sec_signer_cert, &cert_data);
	    if(ortn) {
		pkiCssmErr("SecCertificateGetData", ortn);
		CFRelease(sec_signer_cert);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiDataToKrb5Data(cert_data.Data, cert_data.Length, signer_cert);
	    CFRelease(sec_signer_cert);
	    if(krtn) {
		goto errOut;
	    }
	}
    }
    else {
	/* not signed */
	if(is_signed) {
	    *is_signed = FALSE;
	}
	if(inner_content_type) {
	    *inner_content_type = ECT_Other;
	}
	if(signer_cert) {
	    signer_cert->data = NULL;
	    signer_cert->length = 0;
	}
	if(signer_cert_status) {
	    *signer_cert_status = pki_not_signed;
	}
	if(num_all_certs) {
	    *num_all_certs = 0;
	}
	if(all_certs) {
	    *all_certs = NULL;
	}
    }
    if(is_encrypted) {
	Boolean bencr;
	ortn = CMSDecoderIsContentEncrypted(decoder, &bencr);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_CRYPTO_INTERNAL;
	    goto errOut;
	}
	*is_encrypted = bencr ? TRUE : FALSE;
    }
    
    /* 
     * Verify signature and cert. The actual verify operation is optional,
     * per our signer_cert_status argument, but we do this anyway if we need
     * to get the signer cert.
     */
    if((signer_cert_status != NULL) || (signer_cert != NULL)) {
	
	ortn = CMSDecoderCopySignerStatus(decoder, 
	    0,					    /* signerIndex */
	    policy,
	    signer_cert_status ? TRUE : FALSE,	    /* evaluateSecTrust */
	    &signer_status,
	    NULL,				    /* secTrust - not needed */
	    &cert_verify_status);
	if(ortn) {
	    /* gross error - subsequent processing impossible */
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
    }
    /* obtain & return status */
    if(signer_cert_status) {
	*signer_cert_status = pkiInferSigStatus(signer_status, cert_verify_status);
    }
    
    /* finally, the payload */
    if(raw_data) {
	CFDataRef cf_content = NULL;
	
	ortn = CMSDecoderCopyContent(decoder, &cf_content);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopyContent", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
	krtn = pkiCfDataToKrb5Data(cf_content, raw_data);
	CFRELEASE(cf_content);
    }
errOut:
    CFRELEASE(policy_search);
    CFRELEASE(policy);
    CFRELEASE(cf_all_certs);
    CFRELEASE(decoder);
    return krtn;
}
Exemplo n.º 11
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;
}
Exemplo n.º 12
0
/*
 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData
 *     after all the encapsulated data was passed through the encoder.
 *
 * In detail:
 *  - create the signatures in all the SignerInfos
 *
 * Please note that nothing is done to the Certificates and CRLs in the message - this
 * is entirely the responsibility of our callers.
 */
OSStatus
SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd)
{
    SecCmsSignerInfoRef *signerinfos, signerinfo;
    SecCmsContentInfoRef cinfo;
    SECOidTag digestalgtag;
    OSStatus ret = SECFailure;
    OSStatus rv;
    CSSM_DATA_PTR contentType;
    int certcount;
    int i, ci, n, rci, si;
    PLArenaPool *poolp;
    CFArrayRef certlist;
    extern const SecAsn1Template SecCmsSignerInfoTemplate[];

    poolp = sigd->cmsg->poolp;
    cinfo = &(sigd->contentInfo);

    /* did we have digest calculation going on? */
    if (cinfo->digcx) {
	rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests));
	if (rv != SECSuccess)
	    goto loser;		/* error has been set by SecCmsDigestContextFinishMultiple */
	cinfo->digcx = NULL;
    }

    signerinfos = sigd->signerInfos;
    certcount = 0;

    /* prepare all the SignerInfos (there may be none) */
    for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
	signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
	
	/* find correct digest for this signerinfo */
	digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
	n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
	if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
	    /* oops - digest not found */
	    PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
	    goto loser;
	}

	/* XXX if our content is anything else but data, we need to force the
	 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
	 * collection...") */

	/* pass contentType here as we want a contentType attribute */
	if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL)
	    goto loser;

	/* sign the thing */
	rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType);
	if (rv != SECSuccess)
	    goto loser;

	/* while we're at it, count number of certs in certLists */
	certlist = SecCmsSignerInfoGetCertList(signerinfo);
	if (certlist)
	    certcount += CFArrayGetCount(certlist);
    }

    /* Now we can get a timestamp, since we have all the digests */

    // We force the setting of a callback, since this is the most usual case
    if (!sigd->cmsg->tsaCallback)
        SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback);
        
    if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext)
    {
        CSSM_DATA tsaResponse = {0,};
        SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}};
        // <rdar://problem/11073466> Add nonce support for timestamping client

        uint64_t nonce = 0;

        require_noerr(getRandomNonce(&nonce), tsxit);
        dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd));

        // Calculate hash of encDigest and put in messageImprint.hashedMessage
        SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0);    // NB - assume 1 signer only!
        CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo);
        require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit);
        
        // Callback to fire up XPC service to talk to TimeStamping server, etc.
        require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint,
                                                     nonce, &tsaResponse), tsxit);
        
        require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit);

        /*
            It is likely that every occurrence of "goto loser" in this file should
            also do a PORT_SetError. Since it is not clear what might depend on this
            behavior, we just do this in the timestamping case.
        */
tsxit:
        if (rv)
        {
            dprintf("Original timestamp error: %d\n", (int)rv);
            rv = remapTimestampError(rv);
            PORT_SetError(rv);
            goto loser;
        }
    }
        
    /* this is a SET OF, so we need to sort them guys */
    rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL);
    if (rv != SECSuccess)
	goto loser;

    /*
     * now prepare certs & crls
     */

    /* count the rest of the certs */
    if (sigd->certs != NULL)
	certcount += CFArrayGetCount(sigd->certs);

    if (certcount == 0) {
	sigd->rawCerts = NULL;
    } else {
	/*
	 * Combine all of the certs and cert chains into rawcerts.
	 * Note: certcount is an upper bound; we may not need that many slots
	 * but we will allocate anyway to avoid having to do another pass.
	 * (The temporary space saving is not worth it.)
	 *
	 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
	 *  SetOfDERcertficates implementation
	 */
	sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR));
	if (sigd->rawCerts == NULL)
	    return SECFailure;

	/*
	 * XXX Want to check for duplicates and not add *any* cert that is
	 * already in the set.  This will be more important when we start
	 * dealing with larger sets of certs, dual-key certs (signing and
	 * encryption), etc.  For the time being we can slide by...
	 *
	 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
	 *  SetOfDERcertficates implementation
	 */
	rci = 0;
	if (signerinfos != NULL) {
	    for (si = 0; signerinfos[si] != NULL; si++) {
		signerinfo = signerinfos[si];
		for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) {
		    sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA));
		    SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci);
		    SecCertificateGetData(cert, sigd->rawCerts[rci++]);
		}
	    }
	}

	if (sigd->certs != NULL) {
	    for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) {
		sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA));
		SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci);
		SecCertificateGetData(cert, sigd->rawCerts[rci++]);
	    }
	}

	sigd->rawCerts[rci] = NULL;

	/* this is a SET OF, so we need to sort them guys - we have the DER already, though */
	SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL);
    }

    ret = SECSuccess;

loser:

    dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv);
    return ret;
}
Exemplo n.º 13
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;
}
Exemplo n.º 14
0
// Extract the issuer and serial number from a certificate
SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert)
{
    OSStatus status;
    SecCmsIssuerAndSN *certIssuerAndSN;
    CSSM_CL_HANDLE clHandle;
    CSSM_DATA_PTR serialNumber = 0;
    CSSM_DATA_PTR issuer = 0;
    CSSM_DATA certData = {};
    CSSM_HANDLE resultsHandle = 0;
    uint32 numberOfFields = 0;
    CSSM_RETURN result;
    void *mark;

    mark = PORT_ArenaMark(pl);

    status = SecCertificateGetCLHandle(cert, &clHandle);
    if (status)
	goto loser;
    status = SecCertificateGetData(cert, &certData);
    if (status)
	goto loser;

    /* Get the issuer from the cert. */
    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, 
	&OID_X509V1IssuerNameStd, &resultsHandle, &numberOfFields, &issuer);

    if (result || numberOfFields < 1)
	goto loser;
    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
    if (result)
	goto loser;


    /* Get the serialNumber from the cert. */
    result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, 
	&CSSMOID_X509V1SerialNumber, &resultsHandle, &numberOfFields, &serialNumber);
    if (result || numberOfFields < 1)
	goto loser;
    result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
    if (result)
	goto loser;

    /* Allocate the SecCmsIssuerAndSN struct. */
    certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN));
    if (certIssuerAndSN == NULL)
	goto loser;

    /* Copy the issuer. */
    certIssuerAndSN->derIssuer.Data = (uint8 *) PORT_ArenaAlloc(pl, issuer->Length);
    if (!certIssuerAndSN->derIssuer.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer->Data, issuer->Length);
    certIssuerAndSN->derIssuer.Length = issuer->Length;

    /* Copy the serialNumber. */
    certIssuerAndSN->serialNumber.Data = (uint8 *) PORT_ArenaAlloc(pl, serialNumber->Length);
    if (!certIssuerAndSN->serialNumber.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber->Data, serialNumber->Length);
    certIssuerAndSN->serialNumber.Length = serialNumber->Length;

    PORT_ArenaUnmark(pl, mark);

    CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
    CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);

    return certIssuerAndSN;

loser:
    PORT_ArenaRelease(pl, mark);

    if (serialNumber)
	CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber);
    if (issuer)
	CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer);

    PORT_SetError(SEC_INTERNAL_ONLY);
    return NULL;
}
//	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;
}
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;
}
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;
}