#include "cert.h"
#include "cmstpriv.h"
#include <security_asn1/secerr.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainSearch.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 <Security/SecBase.h>
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==errSecSuccess?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 = 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:%ld",status);
return NULL;
}
status = SecKeychainSearchCopyNext(searchRef,&itemRef);
if (status)
printf("CERT_FindUserCertByUsage: SecKeychainSearchCopyNext:%ld",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;
OSStatus status = 0;
SecTrustResultType trustResult = kSecTrustResultInvalid;
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;
status = SecTrustEvaluate(trust, &trustResult);
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 (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;
}
SecCertificateRef CERT_FindCertByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN)
{
SecCertificateRef 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, const SecAsn1Item *subjKeyID)
{
SecCertificateRef 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, issuerAndSN);
if (!certificate)
return NULL;
return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
}
SecIdentityRef
CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SECItem *subjKeyID)
{
SecCertificateRef certificate = CERT_FindCertBySubjectKeyID(keychainOrArray, subjKeyID);
if (!certificate)
return NULL;
return CERT_FindIdentityByCertificate(keychainOrArray, certificate);
}
SECItem *CERT_FindSMimeProfile(SecCertificateRef cert)
{
return NULL;
}
SECStatus CERT_FindSubjectKeyIDExtension (SecCertificateRef cert,SECItem *retItem)
{
fprintf(stderr, "WARNING: CERT_FindSubjectKeyIDExtension unimplemented\n");
return SECFailure;
}
static void * appMalloc (uint32 size, void *allocRef) {
return (malloc (size));
}
static void appFree (void *mem_ptr, void *allocRef) {
free (mem_ptr);
return;
}
static void * appRealloc (void *ptr, uint32 size, void *allocRef) {
return (realloc (ptr, size));
}
static void * appCalloc (uint32 num, uint32 size, void *allocRef) {
return (calloc (num, size));
}
static CSSM_API_MEMORY_FUNCS memFuncs = {
appMalloc,
appFree,
appRealloc,
appCalloc,
NULL
};
static int InitializeCL (CSSM_CL_HANDLE *clHandle)
{
CSSM_VERSION version = {2, 0};
CSSM_RETURN result = CSSM_ModuleLoad (&gGuidAppleX509CL, CSSM_KEY_HIERARCHY_NONE, NULL, NULL);
if (result != 0)
{
return false;
}
result = CSSM_ModuleAttach (&gGuidAppleX509CL, &version, &memFuncs, 0, CSSM_SERVICE_CL, 0, 0, NULL, 0, NULL, clHandle);
if (result != 0)
{
return false;
}
return true;
}
static void CloseCL (CSSM_CL_HANDLE clHandle)
{
CSSM_ModuleDetach (clHandle);
CSSM_ModuleUnload (&gGuidAppleX509CL, NULL, NULL);
}
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);
if (!InitializeCL (&clHandle))
goto loser;
status = SecCertificateGetData(cert, &certData);
if (status)
goto loser;
result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, &OID_X509V1IssuerNameStd, &resultsHandle, &numberOfFields, &issuer);
if (result)
result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, &CSSMOID_X509V1IssuerName, &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);
CloseCL (clHandle);
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);
CloseCL (clHandle);
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 = errSecSuccess;
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,
CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef)
{
CFArrayRef certificates;
const void *certs = cert;
SecTrustRef trust = NULL;
OSStatus rv;
certificates = CFArrayCreate(NULL, &certs, 1, &kCFTypeArrayCallBacks);
rv = SecTrustCreateWithCertificates(certificates, policies, &trust);
CFRelease(certificates);
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 (trust)
CFRelease(trust);
return rv;
}
CFTypeRef
CERT_PolicyForCertUsage(SECCertUsage certUsage)
{
SecPolicySearchRef search;
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:
CFRelease(search);
return policy;
}