#include "cert.h"
#include "cmstpriv.h"
#include "cmslocal.h"
#include "secitem.h"
#include <security_asn1/secerr.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainSearch.h>
#include <Security/SecIdentity.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecIdentitySearch.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecPolicySearch.h>
#include <Security/oidsalg.h>
#include <Security/cssmapi.h>
#include <Security/oidscert.h>
#include <Security/oidscert.h>
#include <syslog.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#define CERT_DEBUG 0
#if CERT_DEBUG
#define dprintf(args...) fprintf(stderr, args)
#else
#define dprintf(args...)
#endif
static const uint8 X509V1IssuerNameStd[] = {INTEL_X509V3_CERT_R08, 23};
static const CSSM_OID OID_X509V1IssuerNameStd = {INTEL_X509V3_CERT_R08_LENGTH+1, (uint8 *)X509V1IssuerNameStd};
static void
CERT_NormalizeString(CSSM_DATA_PTR string)
{
char *pD, *pCh, *pEos;
if (!string->Length)
return;
pD = pCh = (char *)string->Data;
pEos = pCh + string->Length - 1;
while(*pEos == 0)
pEos--;
while(isspace(*pEos))
pEos--;
pEos++;
while(isspace(*pCh) && (pCh < pEos))
pCh++;
while(pCh < pEos)
{
char ch = *pCh++;
*pD++ = toupper(ch);
if(isspace(ch))
{
while(isspace(*pCh) && (pCh < pEos))
pCh++;
}
}
string->Length = pD - (char *)string->Data;
}
void
CERT_NormalizeX509NameNSS(NSS_Name *nssName)
{
NSS_RDN *rdn;
for (rdn = *nssName->rdns; rdn; ++rdn)
{
NSS_ATV *attr;
for (attr = *rdn->atvs; attr; ++attr)
{
NSS_TaggedItem *attrVal = &attr->value;
if(attrVal->tag != SEC_ASN1_PRINTABLE_STRING)
continue;
CERT_NormalizeString(&attrVal->item);
}
}
}
SecCertificateRef CERT_FindCertByNicknameOrEmailAddr(SecKeychainRef keychainOrArray, char *name)
{
SecCertificateRef certificate;
OSStatus status=SecCertificateFindByEmail(keychainOrArray,name,&certificate);
return status==noErr?certificate:NULL;
}
SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey)
{
CFRetain(pubKey);
return pubKey;
}
void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey)
{
CFRelease(pubKey);
}
SecPublicKeyRef SECKEY_CopyPrivateKey(SecPublicKeyRef privKey)
{
CFRetain(privKey);
return privKey;
}
void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey)
{
CFRelease(privKey);
}
void CERT_DestroyCertificate(SecCertificateRef cert)
{
CFRelease(cert);
}
SecCertificateRef CERT_DupCertificate(SecCertificateRef cert)
{
CFRetain(cert);
return cert;
}
SecIdentityRef CERT_FindIdentityByUsage(SecKeychainRef keychainOrArray,
char *nickname, SECCertUsage usage, Boolean validOnly, void *proto_win)
{
SecIdentityRef identityRef = NULL;
SecCertificateRef cert = CERT_FindCertByNicknameOrEmailAddr(keychainOrArray, nickname);
if (!cert)
return NULL;
SecIdentityCreateWithCertificate(keychainOrArray, cert, &identityRef);
CFRelease(cert);
return identityRef;
}
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";
#if 0
attrs[0].tag = kSecLabelItemAttr;
attrs[0].length = strlen(nickname)+1;
attrs[0].data = nickname;
#else
attrs[0].tag = kSecSerialNumberItemAttr;
attrs[0].length = (UInt32)strlen(serialNumber)+1;
attrs[0].data = (uint8 *)serialNumber;
#endif
SecKeychainAttributeList attrList = { 0, attrs };
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;
}
CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot)
{
SecPolicySearchRef searchRef = NULL;
SecPolicyRef policy = NULL;
CFArrayRef wrappedCert = NULL;
SecTrustRef trust = NULL;
CFArrayRef certChain = NULL;
CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
CFDataRef actionData = NULL;
OSStatus status = 0;
if (!cert)
goto loser;
status = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef);
if (status)
goto loser;
status = SecPolicySearchCopyNext(searchRef, &policy);
if (status)
goto loser;
wrappedCert = CERT_CertListFromCert(cert);
status = SecTrustCreateWithCertificates(wrappedCert, policy, &trust);
if (status)
goto loser;
CSSM_APPLE_TP_ACTION_DATA localActionData = {
CSSM_APPLE_TP_ACTION_VERSION,
CSSM_TP_ACTION_ALLOW_EXPIRED | CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
};
actionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)&localActionData, sizeof(localActionData), kCFAllocatorNull);
if (!actionData)
goto loser;
status = SecTrustSetParameters(trust, CSSM_TP_ACTION_DEFAULT, actionData);
if (status)
goto loser;
status = SecTrustEvaluate(trust, NULL);
if (status)
goto loser;
status = SecTrustGetResult(trust, NULL, &certChain, &statusChain);
if (status)
goto loser;
if (!includeRoot && CFArrayGetCount(certChain) > 1)
{
CFMutableArrayRef subChain = CFArrayCreateMutableCopy(NULL, 0, certChain);
CFRelease(certChain);
certChain = subChain;
if (subChain)
CFArrayRemoveValueAtIndex(subChain, CFArrayGetCount(subChain) - 1);
}
loser:
if (searchRef)
CFRelease(searchRef);
if (policy)
CFRelease(policy);
if (wrappedCert)
CFRelease(wrappedCert);
if (trust)
CFRelease(trust);
if (actionData)
CFRelease(actionData);
if (certChain && status)
{
CFRelease(certChain);
certChain = NULL;
}
return certChain;
}
CFArrayRef CERT_CertListFromCert(SecCertificateRef cert)
{
const void *value = cert;
return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL;
}
CFArrayRef CERT_DupCertList(CFArrayRef oldList)
{
CFRetain(oldList);
return oldList;
}
SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert)
{
SecPublicKeyRef keyRef = NULL;
SecCertificateCopyPublicKey(cert,&keyRef);
return keyRef;
}
SECStatus CERT_CheckCertUsage (SecCertificateRef cert,unsigned char usage)
{
return SECSuccess;
}
SecCertificateRef CERT_FindCertByEmailAddr(SecKeychainRef keychainOrArray, char *emailAddr)
{
abort();
return NULL;
}
SecCertificateRef CERT_FindCertByDERCert(SecKeychainRef keychainOrArray, const SECItem *derCert)
{
SecCertificateRef cert = NULL;
OSStatus rv;
rv = SecCertificateCreateFromData(derCert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
if (rv && cert)
{
PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
CFRelease(cert);
cert = NULL;
}
return cert;
}
static int compareCssmData(
const CSSM_DATA *d1,
const CSSM_DATA *d2)
{
if((d1 == NULL) || (d2 == NULL)) {
return 0;
}
if(d1->Length != d2->Length) {
return 0;
}
if(memcmp(d1->Data, d2->Data, d1->Length)) {
return 0;
}
return 1;
}
SecCertificateRef CERT_FindCertByIssuerAndSN (CFTypeRef keychainOrArray,
CSSM_DATA_PTR *rawCerts, PRArenaPool *pl, const SecCmsIssuerAndSN *issuerAndSN)
{
SecCertificateRef certificate;
int numRawCerts = SecCmsArrayCount((void **)rawCerts);
int dex;
OSStatus ortn;
for(dex=0; dex<numRawCerts; dex++) {
ortn = SecCertificateCreateFromData(rawCerts[dex],
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
&certificate);
if(ortn) {
continue;
}
SecCmsIssuerAndSN *isn = CERT_GetCertIssuerAndSN(pl, certificate);
if(isn == NULL) {
CFRelease(certificate);
continue;
}
if(!compareCssmData(&isn->derIssuer, &issuerAndSN->derIssuer)) {
CFRelease(certificate);
continue;
}
if(!compareCssmData(&isn->serialNumber, &issuerAndSN->serialNumber)) {
CFRelease(certificate);
continue;
}
dprintf("CERT_FindCertByIssuerAndSN: found cert %p\n", certificate);
return certificate;
}
OSStatus status = SecCertificateFindByIssuerAndSN(keychainOrArray, &issuerAndSN->derIssuer,
&issuerAndSN->serialNumber, &certificate);
if (status)
{
PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
certificate = NULL;
}
return certificate;
}
SecCertificateRef CERT_FindCertBySubjectKeyID (CFTypeRef keychainOrArray,
CSSM_DATA_PTR *rawCerts, const SECItem *subjKeyID)
{
SecCertificateRef certificate;
int numRawCerts = SecCmsArrayCount((void **)rawCerts);
int dex;
OSStatus ortn;
SECItem skid;
for(dex=0; dex<numRawCerts; dex++) {
int match;
ortn = SecCertificateCreateFromData(rawCerts[dex],
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
&certificate);
if(ortn) {
continue;
}
if(CERT_FindSubjectKeyIDExtension(certificate, &skid)) {
CFRelease(certificate);
continue;
}
match = compareCssmData(subjKeyID, &skid);
SECITEM_FreeItem(&skid, PR_FALSE);
if(match) {
return certificate;
}
CFRelease(certificate);
}
OSStatus status = SecCertificateFindBySubjectKeyID(keychainOrArray,subjKeyID,&certificate);
if (status)
{
PORT_SetError(SEC_ERROR_NO_EMAIL_CERT);
certificate = NULL;
}
return certificate;
}
static SecIdentityRef
CERT_FindIdentityByCertificate (CFTypeRef keychainOrArray, SecCertificateRef certificate)
{
SecIdentityRef identity = NULL;
SecIdentityCreateWithCertificate(keychainOrArray, certificate, &identity);
if (!identity)
PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
return identity;
}
SecIdentityRef
CERT_FindIdentityByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN)
{
SecCertificateRef certificate = CERT_FindCertByIssuerAndSN(keychainOrArray, NULL, NULL, issuerAndSN);
if (!certificate)
return NULL;
return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
}
SecIdentityRef
CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SECItem *subjKeyID)
{
SecCertificateRef certificate = CERT_FindCertBySubjectKeyID(keychainOrArray, NULL, subjKeyID);
if (!certificate)
return NULL;
return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
}
SECItem *CERT_FindSMimeProfile(SecCertificateRef cert)
{
return NULL;
}
SECStatus CERT_FindSubjectKeyIDExtension (SecCertificateRef cert, SECItem *retItem)
{
CSSM_DATA_PTR fieldValue = NULL;
OSStatus ortn;
CSSM_X509_EXTENSION *extp;
CE_SubjectKeyID *skid;
ortn = SecCertificateCopyFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier,
&fieldValue);
if(ortn || (fieldValue == NULL)) {
return SECFailure;
}
extp = (CSSM_X509_EXTENSION *)fieldValue->Data;
skid = (CE_SubjectKeyID *)extp->value.parsedValue;
retItem->Data = (uint8 *)PORT_Alloc(skid->Length);
retItem->Length = skid->Length;
memmove(retItem->Data, skid->Data, retItem->Length);
SecCertificateReleaseFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier,
fieldValue);
return SECSuccess;
}
SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert)
{
OSStatus status;
SecCmsIssuerAndSN *certIssuerAndSN;
SecCertificateRef certRef;
SecCertificateRef itemImplRef = NULL;
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);
certRef = (SecCertificateRef)((cert) ? CFRetain(cert) : NULL);
status = SecCertificateGetData(certRef, &certData);
if (status)
goto loser;
#if 1
itemImplRef = SecCertificateCreateItemImplInstance(cert);
status = SecCertificateGetCLHandle_legacy(itemImplRef, &clHandle);
#else
status = SecCertificateGetCLHandle(certRef, &clHandle);
#endif
if (status)
goto loser;
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;
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;
certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN));
if (certIssuerAndSN == NULL)
goto loser;
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;
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);
if (itemImplRef)
CFRelease(itemImplRef);
if (certRef)
CFRelease(certRef);
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);
if (itemImplRef)
CFRelease(itemImplRef);
if (certRef)
CFRelease(certRef);
PORT_SetError(SEC_INTERNAL_ONLY);
return NULL;
}
SECStatus CERT_ImportCerts(SecKeychainRef keychain, SECCertUsage usage, unsigned int ncerts,
SECItem **derCerts, SecCertificateRef **retCerts, Boolean keepCerts, Boolean caOnly, char *nickname)
{
OSStatus rv = SECFailure;
SecCertificateRef cert;
unsigned int ci;
if (caOnly || nickname)
abort();
for (ci = 0; ci < ncerts; ++ci)
{
rv = SecCertificateCreateFromData(derCerts[ci], CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
if (rv)
break;
if (keepCerts)
{
rv = SecCertificateAddToKeychain(cert, keychain);
if (rv)
{
if (rv == errKCDuplicateItem)
rv = noErr;
else
{
CFRelease(cert);
break;
}
}
}
if (retCerts)
{
abort();
}
else
CFRelease(cert);
}
return rv;
}
SECStatus CERT_SaveSMimeProfile(SecCertificateRef cert, SECItem *emailProfile,SECItem *profileTime)
{
fprintf(stderr, "WARNING: CERT_SaveSMimeProfile unimplemented\n");
return SECSuccess;
}
SECStatus CERT_VerifyCertName(SecCertificateRef cert, const char *hostname)
{
fprintf(stderr, "WARNING: CERT_VerifyCertName unimplemented\n");
return SECSuccess;
}
SECStatus
CERT_VerifyCert(SecKeychainRef keychainOrArray, SecCertificateRef cert,
const CSSM_DATA_PTR *otherCerts,
CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef)
{
CFMutableArrayRef certificates = NULL;
SecTrustRef trust = NULL;
OSStatus rv;
int numOtherCerts = SecCmsArrayCount((void **)otherCerts);
int dex;
certificates = CFArrayCreateMutable(NULL, numOtherCerts + 1, &kCFTypeArrayCallBacks);
CFArrayAppendValue(certificates, cert);
for(dex=0; dex<numOtherCerts; dex++) {
SecCertificateRef intCert;
rv = SecCertificateCreateFromData(otherCerts[dex],
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
&intCert);
if(rv) {
goto loser;
}
CFArrayAppendValue(certificates, intCert);
CFRelease(intCert);
}
rv = SecTrustCreateWithCertificates(certificates, policies, &trust);
CFRelease(certificates);
certificates = NULL;
if (rv)
goto loser;
rv = SecTrustSetKeychains(trust, keychainOrArray);
if (rv)
goto loser;
CFDateRef verifyDate = CFDateCreate(NULL, stime);
rv = SecTrustSetVerifyDate(trust, verifyDate);
CFRelease(verifyDate);
if (rv)
goto loser;
if (trustRef)
{
*trustRef = trust;
}
else
{
SecTrustResultType result;
rv = SecTrustEvaluate(trust, &result);
if (rv)
goto loser;
switch (result)
{
case kSecTrustResultProceed:
case kSecTrustResultUnspecified:
CFRelease(trust);
break;
default:
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
rv = SECFailure;
goto loser;
break;
}
}
return SECSuccess;
loser:
#if 0
syslog(LOG_ERR, "CERT_VerifyCert has failed with %d (input policies and output trust follow)",
(int)rv);
if (policies) CFShow(policies);
if (trust) CFShow(trust);
#endif
if (trust)
CFRelease(trust);
if(certificates)
CFRelease(certificates);
return rv;
}
CFTypeRef
CERT_PolicyForCertUsage(SECCertUsage certUsage)
{
SecPolicySearchRef search = NULL;
SecPolicyRef policy = NULL;
const CSSM_OID *policyOID;
OSStatus rv;
switch (certUsage)
{
case certUsageSSLServerWithStepUp:
case certUsageSSLCA:
case certUsageVerifyCA:
case certUsageAnyCA:
goto loser;
break;
case certUsageSSLClient:
case certUsageSSLServer:
policyOID = &CSSMOID_APPLE_TP_SSL;
break;
case certUsageUserCertImport:
policyOID = &CSSMOID_APPLE_TP_CSR_GEN;
break;
case certUsageStatusResponder:
policyOID = &CSSMOID_APPLE_TP_REVOCATION_OCSP;
break;
case certUsageObjectSigner:
case certUsageProtectedObjectSigner:
policyOID = &CSSMOID_APPLE_ISIGN;
break;
case certUsageEmailSigner:
case certUsageEmailRecipient:
policyOID = &CSSMOID_APPLE_X509_BASIC;
break;
default:
goto loser;
}
rv = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &search);
if (rv)
goto loser;
rv = SecPolicySearchCopyNext(search, &policy);
if (rv)
goto loser;
loser:
if(search) CFRelease(search);
return policy;
}