#include <Security/cssmtype.h>
#include <Security/cssmapi.h>
#include "tpPolicies.h"
#include <Security/cssmerr.h>
#include "tpdebugging.h"
#include "certGroupUtils.h"
#include <Security/x509defs.h>
#include <Security/oidsalg.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/certextensions.h>
#include <Security/cssmapple.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <CoreFoundation/CFString.h>
#include <CommonCrypto/CommonDigest.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-const-variable"
typedef struct {
CSSM_BOOL present;
CSSM_BOOL critical;
CE_Data *extnData; CSSM_DATA *valToFree; } iSignExtenInfo;
typedef struct {
iSignExtenInfo authorityId;
iSignExtenInfo subjectId;
iSignExtenInfo keyUsage;
iSignExtenInfo extendKeyUsage;
iSignExtenInfo basicConstraints;
iSignExtenInfo netscapeCertType;
iSignExtenInfo subjectAltName;
iSignExtenInfo certPolicies;
iSignExtenInfo qualCertStatements;
iSignExtenInfo nameConstraints;
iSignExtenInfo policyMappings;
iSignExtenInfo policyConstraints;
iSignExtenInfo inhibitAnyPolicy;
iSignExtenInfo certificatePolicies;
CSSM_BOOL foundProvisioningProfileSigningMarker;
CSSM_BOOL foundPassbookSigningMarker;
CSSM_BOOL foundAppleSysInt2Marker;
CSSM_BOOL foundAppleServerAuthMarker;
CSSM_BOOL foundEscrowServiceMarker;
CSSM_BOOL foundAppleWWDRIntMarker;
CSSM_BOOL foundUnknownCritical;
CSSM_BOOL untrustedSigAlg;
} iSignCertInfo;
static const CSSM_OID_PTR knownQualifiedCertStatements[] =
{
(const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1,
(const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2,
(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE,
(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE,
(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION,
(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD
};
#define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
static CSSM_RETURN tp_verifyMacAppStoreReceiptOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo);
static CSSM_RETURN tp_verifyPassbookSigningOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo);
bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind);
#define kSecPolicySHA1Size 20
static const UInt8 kAppleCASHA1[kSecPolicySHA1Size] = {
0x61, 0x1E, 0x5B, 0x66, 0x2C, 0x59, 0x3A, 0x08, 0xFF, 0x58,
0xD1, 0x4A, 0xE2, 0x24, 0x52, 0xD1, 0x98, 0xDF, 0x6C, 0x60
};
static const UInt8 kMobileRootSHA1[kSecPolicySHA1Size] = {
0xBD, 0xD6, 0x7C, 0x34, 0xD0, 0xB2, 0x68, 0x5D, 0x31, 0x82,
0xCD, 0x32, 0xCB, 0xF4, 0x54, 0x69, 0xA1, 0xF1, 0x6B, 0x09
};
static const UInt8 kAppleCorpCASHA1[kSecPolicySHA1Size] = {
0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
};
#define ANY_POLICY_OID OID_EXTENSION, 0x32, 0x00
#define ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 2
#define INHIBIT_ANY_POLICY_OID OID_EXTENSION, 0x54
#define INHIBIT_ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 1
#define US_DOD_INFOSEC 0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
#define US_DOD_INFOSEC_LEN 7
#define PIV_AUTH_OID US_DOD_INFOSEC, 0x0B, 0x0A
#define PIV_AUTH_OID_LEN US_DOD_INFOSEC_LEN + 2
#define PIV_AUTH_2048_OID US_DOD_INFOSEC, 0x0B, 0x14
#define PIV_AUTH_2048_OID_LEN US_DOD_INFOSEC_LEN + 2
static const uint8 OID_ANY_POLICY[] = {ANY_POLICY_OID};
const CSSM_OID CSSMOID_ANY_POLICY = {ANY_POLICY_OID_LEN, (uint8 *)OID_ANY_POLICY};
static const uint8 OID_INHIBIT_ANY_POLICY[] = {INHIBIT_ANY_POLICY_OID};
const CSSM_OID CSSMOID_INHIBIT_ANY_POLICY = {INHIBIT_ANY_POLICY_OID_LEN, (uint8 *)OID_INHIBIT_ANY_POLICY};
static const uint8 OID_PIV_AUTH[] = {PIV_AUTH_OID};
const CSSM_OID CSSMOID_PIV_AUTH = {PIV_AUTH_OID_LEN, (uint8 *)OID_PIV_AUTH};
static const uint8 OID_PIV_AUTH_2048[] = {PIV_AUTH_2048_OID};
const CSSM_OID CSSMOID_PIV_AUTH_2048 = {PIV_AUTH_2048_OID_LEN, (uint8 *)OID_PIV_AUTH_2048};
static CSSM_RETURN tpSetupExtension(
Allocator &alloc,
CSSM_DATA *extnData,
iSignExtenInfo *extnInfo) {
if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
extnInfo->present = CSSM_TRUE;
extnInfo->critical = cssmExt->critical;
extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue;
extnInfo->valToFree = extnData;
return CSSM_OK;
}
static CSSM_RETURN iSignFetchExtension(
Allocator &alloc,
TPCertInfo *tpCert,
const CSSM_OID *fieldOid, iSignExtenInfo *extnInfo) {
CSSM_DATA_PTR fieldValue; CSSM_RETURN crtn;
crtn = tpCert->fetchField(fieldOid, &fieldValue);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CL_NO_FIELD_VALUES:
return CSSM_OK;
default:
return crtn;
}
return tpSetupExtension(alloc,
fieldValue,
extnInfo);
}
static CSSM_RETURN iSignVerifyCriticalExtension(
CSSM_X509_EXTENSION *cssmExt)
{
if (!cssmExt || !cssmExt->extnId.Data)
return CSSMERR_TP_INVALID_FIELD_POINTER;
if (!cssmExt->critical)
return CSSM_OK;
if (!memcmp(cssmExt->extnId.Data, CSSMOID_PolicyConstraints.Data, CSSMOID_PolicyConstraints.Length))
return CSSM_OK;
if (cssmExt->extnId.Length > APPLE_EXTENSION_OID_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION.Data, APPLE_EXTENSION_OID_LENGTH)) {
return CSSM_OK;
}
return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN;
}
static CSSM_RETURN iSignSearchUnknownExtensions(
TPCertInfo *tpCert,
iSignCertInfo *certInfo)
{
CSSM_RETURN crtn;
CSSM_DATA_PTR fieldValue = NULL;
CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE;
uint32 numFields = 0;
certInfo->foundProvisioningProfileSigningMarker = CSSM_FALSE;
certInfo->foundPassbookSigningMarker = CSSM_FALSE;
certInfo->foundAppleSysInt2Marker = CSSM_FALSE;
certInfo->foundAppleWWDRIntMarker = CSSM_FALSE;
certInfo->foundEscrowServiceMarker = CSSM_FALSE;
crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
tpCert->cacheHand(),
&CSSMOID_X509V3CertificateExtensionCStruct,
&searchHand,
&numFields,
&fieldValue);
switch(crtn) {
case CSSM_OK:
break;
case CSSMERR_CL_NO_FIELD_VALUES:
return CSSM_OK;
default:
return crtn;
}
if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
return CSSMERR_TP_UNKNOWN_FORMAT;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
if (cssmExt->extnId.Length == APPLE_EXTENSION_CODE_SIGNING_LENGTH+1 &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING.Data,
APPLE_EXTENSION_CODE_SIGNING_LENGTH+1)) {
certInfo->foundPassbookSigningMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE.Data,
APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH)) {
certInfo->foundAppleSysInt2Marker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_ESCROW_SERVICE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE.Data,
APPLE_EXTENSION_ESCROW_SERVICE_LENGTH)) {
certInfo->foundEscrowServiceMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING.Data,
APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH)) {
certInfo->foundProvisioningProfileSigningMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_WWDR_INTERMEDIATE.Data,
APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH)) {
certInfo->foundAppleWWDRIntMarker = CSSM_TRUE;
}
if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
certInfo->foundUnknownCritical = CSSM_TRUE;
goto fini;
}
CSSM_CL_FreeFieldValue(tpCert->clHand(),
&CSSMOID_X509V3CertificateExtensionCStruct,
fieldValue);
fieldValue = NULL;
for(unsigned i=1; i<numFields; i++) {
crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
searchHand,
&fieldValue);
if(crtn) {
tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
"error");
break;
}
if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
tpPolicyError("iSignSearchUnknownExtensions: "
"malformed CSSM_FIELD");
crtn = CSSMERR_TP_UNKNOWN_FORMAT;
break;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
if (cssmExt->extnId.Length == APPLE_EXTENSION_CODE_SIGNING_LENGTH+1 &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING.Data,
APPLE_EXTENSION_CODE_SIGNING_LENGTH+1)) {
certInfo->foundPassbookSigningMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE.Data,
APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH)) {
certInfo->foundAppleSysInt2Marker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION.Data,
APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH)) {
certInfo->foundAppleServerAuthMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_ESCROW_SERVICE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE.Data,
APPLE_EXTENSION_ESCROW_SERVICE_LENGTH)) {
certInfo->foundEscrowServiceMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING.Data,
APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH)) {
certInfo->foundProvisioningProfileSigningMarker = CSSM_TRUE;
}
if (cssmExt->extnId.Length == APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH &&
!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_WWDR_INTERMEDIATE.Data,
APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH)) {
certInfo->foundAppleWWDRIntMarker = CSSM_TRUE;
}
if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
certInfo->foundUnknownCritical = CSSM_TRUE;
break;
}
CSSM_CL_FreeFieldValue(tpCert->clHand(),
&CSSMOID_X509V3CertificateExtensionCStruct,
fieldValue);
fieldValue = NULL;
}
fini:
if(fieldValue) {
CSSM_CL_FreeFieldValue(tpCert->clHand(),
&CSSMOID_X509V3CertificateExtensionCStruct,
fieldValue);
}
if(searchHand != CSSM_INVALID_HANDLE) {
CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
}
return crtn;
}
static void iSignCheckSignatureAlgorithm(
TPCertInfo *tpCert,
iSignCertInfo *certInfo)
{
CSSM_X509_ALGORITHM_IDENTIFIER *algId = NULL;
CSSM_DATA_PTR valueToFree = NULL;
algId = tp_CertGetAlgId(tpCert, &valueToFree);
if(!algId ||
tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2) ||
tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2WithRSA) ||
tpCompareCssmData(&algId->algorithm, &CSSMOID_MD5) ||
tpCompareCssmData(&algId->algorithm, &CSSMOID_MD5WithRSA) ) {
certInfo->untrustedSigAlg = CSSM_TRUE;
} else {
certInfo->untrustedSigAlg = CSSM_FALSE;
}
if (valueToFree) {
tp_CertFreeAlgId(tpCert->clHand(), valueToFree);
}
}
static CSSM_RETURN iSignGetCertInfo(
Allocator &alloc,
TPCertInfo *tpCert,
iSignCertInfo *certInfo)
{
CSSM_RETURN crtn;
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_AuthorityKeyIdentifier,
&certInfo->authorityId);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_SubjectKeyIdentifier,
&certInfo->subjectId);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_KeyUsage,
&certInfo->keyUsage);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_ExtendedKeyUsage,
&certInfo->extendKeyUsage);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_BasicConstraints,
&certInfo->basicConstraints);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_NetscapeCertType,
&certInfo->netscapeCertType);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_SubjectAltName,
&certInfo->subjectAltName);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_CertificatePolicies,
&certInfo->certPolicies);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_QC_Statements,
&certInfo->qualCertStatements);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_NameConstraints,
&certInfo->nameConstraints);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_PolicyMappings,
&certInfo->policyMappings);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_PolicyConstraints,
&certInfo->policyConstraints);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_InhibitAnyPolicy,
&certInfo->inhibitAnyPolicy);
if(crtn) {
return crtn;
}
crtn = iSignFetchExtension(alloc,
tpCert,
&CSSMOID_CertificatePolicies,
&certInfo->certificatePolicies);
if(crtn) {
return crtn;
}
iSignCheckSignatureAlgorithm(tpCert, certInfo);
return iSignSearchUnknownExtensions(tpCert, certInfo);
}
static void iSignFreeCertInfo(
CSSM_CL_HANDLE clHand,
iSignCertInfo *certInfo)
{
if(certInfo->authorityId.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
certInfo->authorityId.valToFree);
}
if(certInfo->subjectId.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
certInfo->subjectId.valToFree);
}
if(certInfo->keyUsage.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
certInfo->keyUsage.valToFree);
}
if(certInfo->extendKeyUsage.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
certInfo->extendKeyUsage.valToFree);
}
if(certInfo->basicConstraints.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
certInfo->basicConstraints.valToFree);
}
if(certInfo->netscapeCertType.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
certInfo->netscapeCertType.valToFree);
}
if(certInfo->subjectAltName.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectAltName,
certInfo->subjectAltName.valToFree);
}
if(certInfo->certPolicies.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
certInfo->certPolicies.valToFree);
}
if(certInfo->qualCertStatements.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_QC_Statements,
certInfo->qualCertStatements.valToFree);
}
if(certInfo->certificatePolicies.present) {
CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
certInfo->certificatePolicies.valToFree);
}
}
typedef enum {
SN_CommonName, SN_Email, SN_UserID, SN_OrgUnit } SubjSubjNameSearchType;
static CSSM_BOOL tpCompareSubjectName(
TPCertInfo &cert,
SubjSubjNameSearchType searchType,
bool normalizeAll, const char *callerStr, uint32 callerStrLen,
bool &fieldFound)
{
char *certName = NULL; uint32 certNameLen = 0;
CSSM_DATA_PTR subjNameData = NULL;
CSSM_RETURN crtn;
CSSM_BOOL ourRtn = CSSM_FALSE;
const CSSM_OID *oidSrch;
const unsigned char x500_userid_oid[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
CSSM_OID X500_UserID_OID = { sizeof(x500_userid_oid), (uint8*)x500_userid_oid };
fieldFound = false;
switch(searchType) {
case SN_CommonName:
oidSrch = &CSSMOID_CommonName;
break;
case SN_Email:
oidSrch = &CSSMOID_EmailAddress;
break;
case SN_UserID:
oidSrch = &X500_UserID_OID;
break;
case SN_OrgUnit:
oidSrch = &CSSMOID_OrganizationalUnitName;
break;
default:
assert(0);
return CSSM_FALSE;
}
crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
if(crtn) {
tpPolicyError("tpCompareSubjectName: error retrieving subject name");
return CSSM_FALSE;
}
CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
return CSSM_FALSE;
}
CSSM_X509_TYPE_VALUE_PAIR *ptvp;
CSSM_X509_RDN_PTR rdnp;
unsigned rdnDex;
unsigned pairDex;
for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
rdnp = &x509name->RelativeDistinguishedName[rdnDex];
for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
ptvp = &rdnp->AttributeTypeAndValue[pairDex];
if(tpCompareOids(&ptvp->type, oidSrch)) {
fieldFound = true;
certName = (char *)ptvp->value.Data;
certNameLen = (uint32)ptvp->value.Length;
switch(searchType) {
case SN_CommonName:
{
CFStringBuiltInEncodings encoding = kCFStringEncodingUnicode;
CFDataRef cfd = NULL;
bool doConvert = false;
switch(ptvp->valueType) {
case BER_TAG_T61_STRING:
encoding = kCFStringEncodingISOLatin1;
doConvert = true;
break;
case BER_TAG_PKIX_BMP_STRING:
encoding = kCFStringEncodingUnicode;
doConvert = true;
break;
default:
break;
}
if(doConvert) {
cfd = CFDataCreate(NULL, (UInt8 *)certName, certNameLen);
if(cfd == NULL) {
break;
}
CFStringRef cfStr = CFStringCreateFromExternalRepresentation(
NULL, cfd, encoding);
CFRelease(cfd);
if(cfStr == NULL) {
tpPolicyError("tpCompareSubjectName: bad str (1)");
break;
}
cfd = CFStringCreateExternalRepresentation(NULL,
cfStr, kCFStringEncodingASCII, 0);
CFRelease(cfStr);
if(cfd == NULL) {
tpPolicyError("tpCompareSubjectName: bad str (2)");
break;
}
certNameLen = (uint32)CFDataGetLength(cfd);
certName = (char *)CFDataGetBytePtr(cfd);
}
ourRtn = tpCompareHostNames(callerStr, callerStrLen,
certName, certNameLen);
if(doConvert) {
assert(cfd != NULL);
CFRelease(cfd);
}
break;
}
case SN_Email:
ourRtn = tpCompareEmailAddr(callerStr, callerStrLen,
certName, certNameLen, normalizeAll);
break;
case SN_UserID:
case SN_OrgUnit:
ourRtn = ((callerStrLen == certNameLen) &&
!memcmp(callerStr, certName, certNameLen)) ?
CSSM_TRUE : CSSM_FALSE;
break;
}
if(ourRtn) {
break;
}
}
}
if(ourRtn) {
break;
}
}
cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
return ourRtn;
}
static CSSM_BOOL tpCompIpAddrStr(
const char *str,
unsigned strLen,
const CSSM_DATA *numeric)
{
const char *cp = str;
const char *nextDot;
char buf[100];
if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) {
return CSSM_FALSE;
}
if(cp[strLen - 1] == '\0') {
strLen--;
}
for(unsigned dex=0; dex<numeric->Length; dex++) {
const char *lastChar = cp + strLen;
nextDot = cp + 1;
for( ; nextDot<lastChar; nextDot++) {
if(*nextDot == '.') {
break;
}
}
if(nextDot == lastChar) {
if(dex != (numeric->Length - 1)) {
return CSSM_FALSE;
}
}
else if(dex == (numeric->Length - 1)) {
return CSSM_FALSE;
}
ptrdiff_t digLen = nextDot - cp;
if(digLen >= sizeof(buf)) {
return CSSM_FALSE;
}
memmove(buf, cp, digLen);
buf[digLen] = '\0';
digLen++;
cp += digLen;
strLen -= digLen;
int digVal = atoi(buf);
if(digVal != numeric->Data[dex]) {
return CSSM_FALSE;
}
}
return CSSM_TRUE;
}
typedef enum {
SAN_HostName,
SAN_Email
} SubjAltNameSearchType;
static CSSM_BOOL tpCompareSubjectAltName(
const iSignExtenInfo &subjAltNameInfo,
const char *appStr, uint32 appStrLen,
SubjAltNameSearchType searchType,
bool normalizeAll, bool &dnsNameFound, bool &emailFound) {
dnsNameFound = false;
emailFound = false;
if(!subjAltNameInfo.present) {
return CSSM_FALSE;
}
CE_GeneralNames *names = &subjAltNameInfo.extnData->subjectAltName;
CSSM_BOOL ourRtn = CSSM_FALSE;
char *certName;
uint32 certNameLen;
for(unsigned dex=0; dex<names->numNames; dex++) {
CE_GeneralName *name = &names->generalName[dex];
switch(searchType) {
case SAN_HostName:
switch(name->nameType) {
case GNT_IPAddress:
if(appStr == NULL) {
break;
}
ourRtn = tpCompIpAddrStr(appStr, appStrLen, &name->name);
break;
case GNT_DNSName:
if(name->berEncoded) {
tpErrorLog("tpCompareSubjectAltName: malformed "
"CE_GeneralName (1)\n");
break;
}
certName = (char *)name->name.Data;
if(certName == NULL) {
tpErrorLog("tpCompareSubjectAltName: malformed "
"CE_GeneralName (2)\n");
break;
}
certNameLen = (uint32)(name->name.Length);
dnsNameFound = true;
if(appStr != NULL) {
ourRtn = tpCompareHostNames(appStr, appStrLen,
certName, certNameLen);
}
break;
default:
break;
}
break;
case SAN_Email:
if(name->nameType != GNT_RFC822Name) {
break;
}
certName = (char *)name->name.Data;
if(certName == NULL) {
tpErrorLog("tpCompareSubjectAltName: malformed "
"GNT_RFC822Name\n");
break;
}
certNameLen = (uint32)(name->name.Length);
emailFound = true;
if(appStr != NULL) {
ourRtn = tpCompareEmailAddr(appStr, appStrLen, certName,
certNameLen, normalizeAll);
}
break;
}
if(ourRtn) {
break;
}
}
return ourRtn;
}
static CSSM_BOOL tpIsNumeric(
const char *hostName,
unsigned hostNameLen)
{
if(hostName[hostNameLen - 1] == '\0') {
hostNameLen--;
}
for(unsigned i=0; i<hostNameLen; i++) {
char c = *hostName++;
if(isdigit(c)) {
continue;
}
if(c != '.') {
return CSSM_FALSE;
}
}
return CSSM_TRUE;
}
static CFStringRef CF_RETURNS_RETAINED tpTvpToCfString(
const CSSM_X509_TYPE_VALUE_PAIR *tvp)
{
CFStringBuiltInEncodings encoding;
switch(tvp->valueType) {
case BER_TAG_T61_STRING:
encoding = kCFStringEncodingISOLatin1;
break;
case BER_TAG_PKIX_BMP_STRING:
encoding = kCFStringEncodingUnicode;
break;
case BER_TAG_PRINTABLE_STRING:
case BER_TAG_IA5_STRING:
case BER_TAG_PKIX_UTF8_STRING:
encoding = kCFStringEncodingUTF8;
break;
default:
return NULL;
}
CFDataRef cfd = CFDataCreate(NULL, tvp->value.Data, tvp->value.Length);
if(cfd == NULL) {
return NULL;
}
CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL, cfd, encoding);
CFRelease(cfd);
return cfStr;
}
static bool tpCompareTvpToCfString(
const CSSM_X509_TYPE_VALUE_PAIR *tvp,
CFStringRef refStr,
CFOptionFlags flags) {
CFStringRef cfStr = tpTvpToCfString(tvp);
if(cfStr == NULL) {
return false;
}
CFComparisonResult res = CFStringCompare(refStr, cfStr, flags);
CFRelease(cfStr);
if(res == kCFCompareEqualTo) {
return true;
}
else {
return false;
}
}
static bool tpVerifyEKU(
const iSignCertInfo &certInfo,
const CSSM_OID &ekuOid,
bool ekuAnyOK) {
if(!certInfo.extendKeyUsage.present) {
return false;
}
CE_ExtendedKeyUsage *eku = &certInfo.extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
for(unsigned i=0; i<eku->numPurposes; i++) {
const CSSM_OID *foundEku = &eku->purposes[i];
if(tpCompareOids(foundEku, &ekuOid)) {
return true;
}
if(ekuAnyOK && tpCompareOids(foundEku, &CSSMOID_ExtendedKeyUsageAny)) {
return true;
}
}
return false;
}
static bool tpVerifyCPE(
const iSignCertInfo &certInfo,
const CSSM_OID &cpOid,
bool anyPolicyOK) {
if(!certInfo.certPolicies.present) {
return false;
}
CE_CertPolicies *cp = &certInfo.certPolicies.extnData->certPolicies;
assert(cp != NULL);
for(unsigned i=0; i<cp->numPolicies; i++) {
const CE_PolicyInformation *foundPolicy = &cp->policies[i];
if(tpCompareOids(&foundPolicy->certPolicyId, &cpOid)) {
return true;
}
if(anyPolicyOK && tpCompareOids(&foundPolicy->certPolicyId, &CSSMOID_ANY_POLICY)) {
return true;
}
}
return false;
}
static bool tpCompareIChatHandleName(
TPCertInfo &cert,
const char *iChatHandle, uint32 iChatHandleLen)
{
CSSM_DATA_PTR subjNameData = NULL; CSSM_RETURN crtn;
bool ourRtn = false;
CSSM_X509_NAME_PTR x509name;
CSSM_X509_TYPE_VALUE_PAIR *ptvp;
CSSM_X509_RDN_PTR rdnp;
unsigned rdnDex;
unsigned pairDex;
CSSM_BOOL commonNameMatch = CSSM_FALSE; CSSM_BOOL orgUnitMatch = CSSM_FALSE; CSSM_BOOL orgMatch = CSSM_FALSE;
if(iChatHandle[iChatHandleLen - 1] == '\0') {
iChatHandleLen--;
}
CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)iChatHandle, iChatHandleLen);
if(cfd == NULL) {
return false;
}
CFStringRef handleStr = CFStringCreateFromExternalRepresentation(NULL, cfd,
kCFStringEncodingUTF8);
CFRelease(cfd);
if(handleStr == NULL) {
tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
return false;
}
CFRange whereIsAt;
whereIsAt = CFStringFind(handleStr, CFSTR("@"), 0);
if(whereIsAt.length == 0) {
tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
CFRelease(handleStr);
return false;
}
CFRange r = {0, whereIsAt.location};
CFStringRef iChatName = CFStringCreateWithSubstring(NULL, handleStr, r);
if(iChatName == NULL) {
tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
CFRelease(handleStr);
return false;
}
r.location = whereIsAt.location + 1; r.length = CFStringGetLength(handleStr) - r.location;
CFStringRef iChatDomain = CFStringCreateWithSubstring(NULL, handleStr, r);
CFRelease(handleStr);
if(iChatDomain == NULL) {
tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
CFRelease(iChatName);
return false;
}
crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
if(crtn) {
tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
goto errOut;
}
x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
goto errOut;
}
for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
rdnp = &x509name->RelativeDistinguishedName[rdnDex];
for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
ptvp = &rdnp->AttributeTypeAndValue[pairDex];
if(!commonNameMatch &&
tpCompareOids(&ptvp->type, &CSSMOID_CommonName) &&
tpCompareTvpToCfString(ptvp, iChatName, kCFCompareCaseInsensitive)) {
commonNameMatch = CSSM_TRUE;
}
if(!orgUnitMatch &&
tpCompareOids(&ptvp->type, &CSSMOID_OrganizationalUnitName) &&
tpCompareTvpToCfString(ptvp, iChatDomain, kCFCompareCaseInsensitive)) {
orgUnitMatch = CSSM_TRUE;
}
if(!orgMatch &&
tpCompareOids(&ptvp->type, &CSSMOID_OrganizationName) &&
(tpCompareTvpToCfString(ptvp, CFSTR("Apple Computer, Inc."), 0) ||
tpCompareTvpToCfString(ptvp, CFSTR("Apple Inc."), 0))) {
orgMatch = CSSM_TRUE;
}
if(commonNameMatch && orgUnitMatch && orgMatch) {
ourRtn = true;
goto errOut;
}
}
}
errOut:
cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
CFRelease(iChatName);
CFRelease(iChatDomain);
return ourRtn;
}
static CSSM_RETURN tp_verifySslOpts(
TPPolicy policy,
TPCertGroup &certGroup,
const CSSM_DATA *sslFieldOpts,
const iSignCertInfo *certInfo) {
const iSignCertInfo &leafCertInfo = certInfo[0];
CSSM_APPLE_TP_SSL_OPTIONS *sslOpts = NULL;
unsigned hostNameLen = 0;
const char *serverName = NULL;
TPCertInfo *leaf = certGroup.certAtIndex(0);
assert(leaf != NULL);
if((sslFieldOpts != NULL) && (sslFieldOpts->Data != NULL)) {
sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)sslFieldOpts->Data;
switch(sslOpts->Version) {
case CSSM_APPLE_TP_SSL_OPTS_VERSION:
if(sslFieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
}
break;
default:
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
}
hostNameLen = sslOpts->ServerNameLen;
serverName = sslOpts->ServerName;
}
if(hostNameLen != 0) {
if(serverName == NULL) {
return CSSMERR_TP_INVALID_POINTER;
}
char *hostName = (char *)certGroup.alloc().malloc(hostNameLen);
memmove(hostName, serverName, hostNameLen);
tpToLower(hostName, hostNameLen);
CSSM_BOOL match = CSSM_FALSE;
bool dnsNameFound = false;
bool dummy;
match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
hostName, hostNameLen,
SAN_HostName, false, dnsNameFound, dummy);
if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) {
bool fieldFound;
match = tpCompareSubjectName(*leaf, SN_CommonName, false, hostName, hostNameLen,
fieldFound);
}
CSSM_BOOL domainMatch = CSSM_TRUE;
if(match) {
TPCertInfo *tpCert = certGroup.lastCert();
if (tpCert) {
const CSSM_DATA *certData = tpCert->itemData();
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
if (!memcmp(digest, kAppleCorpCASHA1, sizeof(digest))) {
const char *dnlist[] = { "apple.com", "icloud.com" };
unsigned int idx, dncount=2;
domainMatch = CSSM_FALSE;
for(idx=0;idx<dncount;idx++) {
uint32 len=(uint32)strlen(dnlist[idx]);
char *domainName=(char*)certGroup.alloc().malloc(len);
memmove(domainName, (char*)dnlist[idx], len);
if(tpCompareDomainSuffix(hostName, hostNameLen,
domainName, len)) {
domainMatch = CSSM_TRUE;
}
certGroup.alloc().free(domainName);
if (domainMatch) {
break;
}
}
}
}
}
certGroup.alloc().free(hostName);
if(!match) {
if(leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH)) {
return CSSMERR_APPLETP_HOSTNAME_MISMATCH;
}
}
if(!domainMatch) {
if(leaf->addStatusCode(CSSMERR_APPLETP_CA_PIN_MISMATCH)) {
return CSSMERR_APPLETP_CA_PIN_MISMATCH;
}
}
}
const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
if(ekuInfo.present) {
bool foundGoodEku = false;
bool isServer = true;
CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
assert(eku != NULL);
const CSSM_OID *extUse = &CSSMOID_ServerAuth;
if((sslOpts != NULL) &&
(sslOpts->Version > 0) &&
(sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT)) {
extUse = &CSSMOID_ClientAuth;
isServer = false;
}
for(unsigned i=0; i<eku->numPurposes; i++) {
const CSSM_OID *purpose = &eku->purposes[i];
if(tpCompareOids(purpose, extUse)) {
foundGoodEku = true;
break;
}
if(tpCompareOids(purpose, &CSSMOID_ExtendedKeyUsageAny)) {
foundGoodEku = true;
break;
}
if((policy == kTP_IPSec) && (tpCompareOids(purpose, &CSSMOID_EKU_IPSec))) {
foundGoodEku = true;
break;
}
if(isServer) {
if(tpCompareOids(purpose, &CSSMOID_NetscapeSGC)) {
foundGoodEku = true;
break;
}
if(tpCompareOids(purpose, &CSSMOID_MicrosoftSGC)) {
foundGoodEku = true;
break;
}
}
}
if(!foundGoodEku) {
if(leaf->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE)) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
}
if((sslOpts != NULL) &&
(sslOpts->Version > 0) &&
(sslOpts->Flags & 0x00000002)) {
if (certGroup.numCerts() > 1) {
const iSignCertInfo *isCertInfo = &certInfo[1];
if (!(isCertInfo->foundAppleServerAuthMarker == CSSM_TRUE)) {
TPCertInfo *tpCert = certGroup.certAtIndex(1);
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
}
else {
if(leaf->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION)) {
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
}
}
return CSSM_OK;
}
#define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
static CSSM_RETURN tp_verifySmimeOpts(
TPPolicy policy,
TPCertGroup &certGroup,
const CSSM_DATA *smimeFieldOpts,
const iSignCertInfo *certInfo) {
const iSignCertInfo &leafCertInfo = certInfo[0];
bool iChat = (policy == kTP_iChat) ? true : false;
CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts = NULL;
if(smimeFieldOpts != NULL) {
smimeOpts = (CSSM_APPLE_TP_SMIME_OPTIONS *)smimeFieldOpts->Data;
}
if(smimeOpts != NULL) {
switch(smimeOpts->Version) {
case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
if(smimeFieldOpts->Length !=
sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
}
break;
default:
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
}
}
TPCertInfo *leaf = certGroup.certAtIndex(0);
assert(leaf != NULL);
unsigned emailLen = 0;
if(smimeOpts != NULL) {
emailLen = smimeOpts->SenderEmailLen;
}
bool match = false;
bool emailFoundInSAN = false;
bool iChatHandleFound = false;
bool emailFoundInDN = false;
if(emailLen != 0) {
if(smimeOpts->SenderEmail == NULL) {
return CSSMERR_TP_INVALID_POINTER;
}
if(iChat) {
iChatHandleFound = tpCompareIChatHandleName(*leaf, smimeOpts->SenderEmail,
emailLen);
if(iChatHandleFound) {
match = true;
}
}
if(!match) {
char *email = (char *)certGroup.alloc().malloc(emailLen);
memmove(email, smimeOpts->SenderEmail, emailLen);
tpNormalizeAddrSpec(email, emailLen, iChat);
bool dummy;
match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
email, emailLen,
SAN_Email, iChat, dummy, emailFoundInSAN);
if(!match) {
tpNormalizeAddrSpec(email, emailLen, true);
match = tpCompareSubjectName(*leaf, SN_Email, true, email, emailLen,
emailFoundInDN);
}
certGroup.alloc().free(email);
if(!match && (emailFoundInSAN || emailFoundInDN)) {
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND)) {
tpPolicyError("SMIME email addrs in cert but no match");
return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND;
}
}
}
if(iChat && !emailFoundInSAN && !emailFoundInDN && !iChatHandleFound) {
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
tpPolicyError("iChat: no email address or handle in cert");
return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS;
}
}
}
CSSM_RETURN crtn;
CSSM_DATA_PTR subjNameData = NULL;
const iSignExtenInfo &kuInfo = leafCertInfo.keyUsage;
const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
const CSSM_X509_NAME *x509Name = NULL;
if(iChat) {
goto checkEku;
}
crtn = leaf->fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
if(crtn) {
tpPolicyError("SMIME policy: error fetching subjectName");
leaf->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE);
return CSSMERR_TP_INVALID_CERTIFICATE;
}
x509Name = (const CSSM_X509_NAME *)subjNameData->Data;
if(x509Name->numberOfRDNs == 0) {
if(!emailFoundInSAN && (emailLen == 0)) { bool dummy;
tpCompareSubjectAltName(leafCertInfo.subjectAltName,
NULL, 0, SAN_Email, false, dummy,
emailFoundInSAN); }
if(!emailFoundInSAN) {
tpPolicyError("SMIME policy fail: empty subject name and "
"no Email Addrs in SubjectAltName");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
else {
goto postSAN;
}
}
assert(leafCertInfo.subjectAltName.present);
if(!leafCertInfo.subjectAltName.critical) {
tpPolicyError("SMIME policy fail: empty subject name and "
"no Email Addrs in SubjectAltName");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT)) {
leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
}
postSAN:
leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
if(kuInfo.present && (smimeOpts != NULL)) {
CE_KeyUsage certKu = *((CE_KeyUsage *)kuInfo.extnData);
CE_KeyUsage appKu = smimeOpts->IntendedUsage;
CE_KeyUsage intersection = certKu & appKu;
if((intersection & CE_CIPHER_MASK) != (appKu & CE_CIPHER_MASK)) {
tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
appKu, certKu);
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
if(appKu & CE_KU_KeyAgreement) {
if((appKu & (CE_KU_EncipherOnly | CE_KU_DecipherOnly)) == 0) {
tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
"no Encipher or Decipher");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
if((certKu & CE_KU_EncipherOnly) &&
(appKu & CE_KU_DecipherOnly)) {
tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
"app wants to decipher");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
if((certKu & CE_KU_DecipherOnly) &&
(appKu & CE_KU_EncipherOnly)) {
tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
"app wants to encipher");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
}
}
checkEku:
if(iChat && !ekuInfo.present) {
tpPolicyError("iChat: No extended Key Use");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
}
}
if(!iChatHandleFound) {
if(ekuInfo.present) {
bool foundGoodEku = false;
CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
assert(eku != NULL);
for(unsigned i=0; i<eku->numPurposes; i++) {
if(tpCompareOids(&eku->purposes[i], &CSSMOID_EmailProtection)) {
foundGoodEku = true;
break;
}
if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
foundGoodEku = true;
break;
}
}
if(!foundGoodEku) {
tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
}
}
}
}
else {
assert(iChat);
assert(ekuInfo.present);
bool foundAnyEku = false;
bool foundIChatSign = false;
bool foundISignEncrypt = false;
CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
assert(eku != NULL);
for(unsigned i=0; i<eku->numPurposes; i++) {
if(tpCompareOids(&eku->purposes[i],
&CSSMOID_APPLE_EKU_ICHAT_SIGNING)) {
foundIChatSign = true;
}
else if(tpCompareOids(&eku->purposes[i],
&CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION)) {
foundISignEncrypt = true;
}
else if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
foundAnyEku = true;
}
}
if(!foundAnyEku && !foundISignEncrypt && !foundIChatSign) {
tpPolicyError("iChat: No valid extended Key Uses found");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
}
}
if((smimeOpts != NULL) && (smimeOpts->IntendedUsage != 0)) {
if(smimeOpts->IntendedUsage & CE_KU_DigitalSignature) {
if(!foundIChatSign) {
tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
}
}
}
if(smimeOpts->IntendedUsage & CE_KU_DataEncipherment) {
if(!foundISignEncrypt) {
tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
}
}
}
}
}
return CSSM_OK;
}
static CSSM_RETURN tp_verifySWUpdateSigningOpts(
TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo) {
unsigned numCerts = certGroup.numCerts();
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CE_ExtendedKeyUsage *eku;
CSSM_RETURN crtn = CSSM_OK;
if(numCerts != 3) {
if(!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH)) {
tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts);
return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
}
else if(numCerts < 3) {
goto checkLeaf;
}
}
isCertInfo = &certInfo[1];
tpCert = certGroup.certAtIndex(1);
if(!isCertInfo->basicConstraints.present) {
tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS)) {
return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
}
}
if(!isCertInfo->extendKeyUsage.present) {
tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
}
else {
goto checkLeaf;
}
}
eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
if(eku->numPurposes != 1) {
tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
(unsigned long)eku->numPurposes);
if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
else if(eku->numPurposes == 0) {
goto checkLeaf;
}
}
if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
}
checkLeaf:
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if(!isCertInfo->extendKeyUsage.present) {
tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
return crtn ? crtn : CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
}
else {
return CSSM_OK;
}
}
eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
if(eku->numPurposes != 1) {
tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
(unsigned long)eku->numPurposes);
if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
if(crtn == CSSM_OK) {
crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
}
return crtn;
}
if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
if(tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV)) {
tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
if(tpCert->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT)) {
if(crtn == CSSM_OK) {
crtn = CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT;
}
}
}
else {
tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
if(crtn == CSSM_OK) {
crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
}
}
}
return crtn;
}
static CSSM_RETURN tp_verifyResourceSigningOpts(
TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo) {
unsigned numCerts = certGroup.numCerts();
if(numCerts < 2) {
if(!certGroup.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH)) {
tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts);
return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH;
}
}
const iSignCertInfo &leafCert = certInfo[0];
TPCertInfo *leaf = certGroup.certAtIndex(0);
if(!tpVerifyEKU(leafCert, CSSMOID_APPLE_EKU_RESOURCE_SIGNING, false)) {
tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
}
return CSSM_OK;
}
static CSSM_RETURN tp_verifyCodePkgSignOpts(
TPPolicy policy,
TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo) {
const iSignCertInfo &leafCert = certInfo[0];
if(!tpVerifyEKU(leafCert, CSSMOID_ExtendedUseCodeSigning, false)) {
TPCertInfo *leaf = certGroup.certAtIndex(0);
tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
}
return CSSM_OK;
}
static CSSM_RETURN tp_verifyMacAppStoreReceiptOpts(
TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo) {
unsigned numCerts = certGroup.numCerts();
if (numCerts < 3)
{
if (!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH))
{
tpPolicyError("tp_verifyMacAppStoreReceiptOpts: numCerts %u", numCerts);
return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
}
}
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
isCertInfo = &certInfo[1];
tpCert = certGroup.certAtIndex(1);
if (!isCertInfo->basicConstraints.present)
{
tpPolicyError("tp_verifyMacAppStoreReceiptOpts: no basicConstraints in intermediate");
if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS))
return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
}
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (certInfo->certificatePolicies.present)
{
const CE_CertPolicies *certPolicies =
&isCertInfo->certificatePolicies.extnData->certPolicies;
if (!certificatePoliciesContainsOID(certPolicies, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY))
if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
else
{
tpPolicyError("tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present in leaf");
if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
return CSSM_OK;
}
bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind)
{
if (!certPolicies || !oidToFind)
return false;
const uint32 maxIndex = 100; for (uint32 policyIndex = 0; policyIndex < certPolicies->numPolicies && policyIndex < maxIndex; policyIndex++)
{
CE_PolicyInformation *certPolicyInfo = &certPolicies->policies[policyIndex];
CSSM_OID_PTR oid = &certPolicyInfo->certPolicyId;
if (oid && tpCompareOids(oid, oidToFind)) return true;
}
return false;
}
static CSSM_RETURN tp_verifyTimeStampingOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo) {
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CE_ExtendedKeyUsage *eku;
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (!isCertInfo->extendKeyUsage.present)
{
tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
if(!isCertInfo->extendKeyUsage.critical)
{
tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
tpCert->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL);
return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL;
}
eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
if(eku->numPurposes != 1)
{
tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
(unsigned long)eku->numPurposes);
tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
if(!tpCompareOids(&eku->purposes[0], &CSSMOID_TimeStamping))
{
tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
}
return CSSM_OK;
}
static CSSM_RETURN tp_verifyPassbookSigningOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts,
const iSignCertInfo *certInfo) {
unsigned numCerts = certGroup.numCerts();
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CE_ExtendedKeyUsage *eku;
CSSM_RETURN crtn = CSSM_OK;
unsigned int nameLen = 0;
const char *name = NULL;
char *p, *signerName = NULL, *teamIdentifier = NULL;
bool found;
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (!fieldOpts || !fieldOpts->Data)
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
else {
CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)fieldOpts->Data;
switch (opts->Version)
{
case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
if (fieldOpts->Length != sizeof(CSSM_APPLE_TP_SMIME_OPTIONS))
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
break;
default:
return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
}
nameLen = opts->SenderEmailLen;
name = opts->SenderEmail;
if (!name || !nameLen)
return CSSMERR_APPLETP_IDENTIFIER_MISSING;
}
signerName = (char *)certGroup.alloc().malloc(nameLen);
teamIdentifier = (char *)certGroup.alloc().malloc(nameLen);
memmove(signerName, name, nameLen);
teamIdentifier[0] = '\0';
if ((p = strchr(signerName, '\t')) != NULL) {
*p++ = '\0';
memmove(teamIdentifier, p, strlen(p)+1);
}
if (CSSM_FALSE == tpCompareSubjectName(*tpCert,
SN_UserID, false, signerName, (unsigned int)strlen(signerName), found)) {
tpPolicyError("tp_verifyPassbookSigningOpts: signer name not in subject UID field");
tpCert->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING);
crtn = CSSMERR_APPLETP_IDENTIFIER_MISSING;
goto cleanup;
}
if (CSSM_FALSE == tpCompareSubjectName(*tpCert,
SN_OrgUnit, false, teamIdentifier, (unsigned int)strlen(teamIdentifier), found)) {
tpPolicyError("tp_verifyPassbookSigningOpts: team identifier not in subject OU field");
tpCert->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING);
crtn = CSSMERR_APPLETP_IDENTIFIER_MISSING;
goto cleanup;
}
if (!isCertInfo->extendKeyUsage.present) {
tpPolicyError("tp_verifyPassbookSigningOpts: no extendedKeyUse in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
found = false;
for (int ix=0;ix<eku->numPurposes;ix++) {
if (tpCompareOids(&eku->purposes[ix], &CSSMOID_APPLE_EKU_PASSBOOK_SIGNING)) {
found = true;
break;
}
}
if (!found) {
tpPolicyError("tp_verifyPassbookSigningOpts: Passbook Signing purpose not found");
tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
goto cleanup;
}
if (!(isCertInfo->foundPassbookSigningMarker == CSSM_TRUE)) {
tpPolicyError("tp_verifyPassbookSigningOpts: no Passbook Signing extension in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
if (numCerts < 3) {
tpPolicyError("tp_verifyPassbookSigningOpts: numCerts %u", numCerts);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
else {
tpCert = certGroup.certAtIndex(numCerts-1);
const CSSM_DATA *certData = tpCert->itemData();
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
if (memcmp(digest, kAppleCASHA1, sizeof(digest))) {
tpPolicyError("tp_verifyPassbookSigningOpts: invalid anchor for policy");
tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
}
cleanup:
if (signerName)
certGroup.alloc().free(signerName);
if (teamIdentifier)
certGroup.alloc().free(teamIdentifier);
return crtn;
}
static CSSM_RETURN tp_verifyMobileStoreSigningOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts,
const iSignCertInfo *certInfo, bool testPolicy)
{
unsigned numCerts = certGroup.numCerts();
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CE_KeyUsage ku;
CSSM_RETURN crtn = CSSM_OK;
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (!isCertInfo->keyUsage.present) {
tpPolicyError("tp_verifyMobileStoreSigningOpts: no keyUsage in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
ku = isCertInfo->keyUsage.extnData->keyUsage;
if (!(ku & CE_KU_DigitalSignature)) {
tpPolicyError("tp_verifyMobileStoreSigningOpts: DigitalSignature usage not found");
tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
crtn = CSSMERR_APPLETP_INVALID_KEY_USAGE;
goto cleanup;
}
if (isCertInfo->certificatePolicies.present)
{
const CE_CertPolicies *certPolicies =
&isCertInfo->certificatePolicies.extnData->certPolicies;
const CSSM_OID *policyOID = (testPolicy) ?
&CSSMOID_TEST_MOBILE_STORE_SIGNING_POLICY :
&CSSMOID_MOBILE_STORE_SIGNING_POLICY;
if (!certificatePoliciesContainsOID(certPolicies, policyOID))
if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
else
{
tpPolicyError("tp_verifyMobileStoreSigningOpts: no certificatePolicies present in leaf");
if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
}
if (numCerts != 3) {
tpPolicyError("tp_verifyMobileStoreSigningOpts: numCerts %u", numCerts);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
{
tpCert = certGroup.certAtIndex(numCerts-1);
const CSSM_DATA *certData = tpCert->itemData();
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
if (memcmp(digest, kMobileRootSHA1, sizeof(digest))) {
tpPolicyError("tp_verifyMobileStoreSigningOpts: invalid anchor for policy");
tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
}
isCertInfo = &certInfo[1];
tpCert = certGroup.certAtIndex(1);
if (!(isCertInfo->foundAppleSysInt2Marker == CSSM_TRUE)) {
tpPolicyError("tp_verifyMobileStoreSigningOpts: intermediate marker extension not found");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
cleanup:
return crtn;
}
static CSSM_RETURN tp_verifyProvisioningProfileSigningOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts,
const iSignCertInfo *certInfo) {
unsigned numCerts = certGroup.numCerts();
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CSSM_RETURN crtn = CSSM_OK;
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (numCerts < 3) {
tpPolicyError("tp_verifyProvisioningProfileSigningOpts: numCerts %u", numCerts);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
else {
tpCert = certGroup.certAtIndex(numCerts-1);
const CSSM_DATA *certData = tpCert->itemData();
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
if (memcmp(digest, kAppleCASHA1, sizeof(digest))) {
tpPolicyError("tp_verifyProvisioningProfileSigningOpts: invalid anchor for policy");
tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
}
if (!(isCertInfo->foundProvisioningProfileSigningMarker == CSSM_TRUE)) {
tpPolicyError("tp_verifyProvisioningProfileSigningOpts: no Provisioning Profile Signing extension in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
isCertInfo = &certInfo[1];
tpCert = certGroup.certAtIndex(1);
if (!(isCertInfo->foundAppleWWDRIntMarker == CSSM_TRUE)) {
tpPolicyError("tp_verifyProvisioningProfileSigningOpts: intermediate marker extension not found");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
cleanup:
return crtn;
}
static CSSM_RETURN tp_verifyProfileSigningOpts(TPCertGroup &certGroup,
const CSSM_DATA *fieldOpts,
const iSignCertInfo *certInfo, bool testPolicy)
{
unsigned numCerts = certGroup.numCerts();
const iSignCertInfo *isCertInfo;
TPCertInfo *tpCert;
CE_ExtendedKeyUsage *eku;
CSSM_RETURN crtn = CSSM_OK;
bool found;
isCertInfo = &certInfo[0];
tpCert = certGroup.certAtIndex(0);
if (!isCertInfo->extendKeyUsage.present) {
tpPolicyError("tp_verifyProfileSigningOpts: no extendedKeyUse in leaf");
tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
goto cleanup;
}
eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
assert(eku != NULL);
found = false;
for (int ix=0;ix<eku->numPurposes;ix++) {
if (tpCompareOids(&eku->purposes[ix], (testPolicy) ?
&CSSMOID_APPLE_EKU_QA_PROFILE_SIGNING :
&CSSMOID_APPLE_EKU_PROFILE_SIGNING)) {
found = true;
break;
}
}
if (!found) {
tpPolicyError("tp_verifyProfileSigningOpts: Profile Signing purpose not found");
tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
goto cleanup;
}
if (numCerts < 3) {
tpPolicyError("tp_verifyProfileSigningOpts: numCerts %u", numCerts);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
else {
tpCert = certGroup.certAtIndex(numCerts-1);
const CSSM_DATA *certData = tpCert->itemData();
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
if (memcmp(digest, kAppleCASHA1, sizeof(digest))) {
tpPolicyError("tp_verifyProfileSigningOpts: invalid anchor for policy");
tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
goto cleanup;
}
}
cleanup:
return crtn;
}
#define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
#define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
#define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
#define KEY_USAGE_REQUIRED_FOR_ROOT 0
#define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
CSSM_RETURN tp_policyVerify(
TPPolicy policy,
Allocator &alloc,
CSSM_CL_HANDLE clHand,
CSSM_CSP_HANDLE cspHand,
TPCertGroup *certGroup,
CSSM_BOOL verifiedToRoot, CSSM_BOOL verifiedViaTrustSetting, CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
const CSSM_DATA *policyFieldData, void *policyOpts) {
iSignCertInfo *certInfo = NULL;
uint32 numCerts;
iSignCertInfo *thisCertInfo;
uint16 expUsage;
uint16 actUsage;
unsigned certDex;
CSSM_BOOL cA = CSSM_FALSE; bool isLeaf; bool isRoot; CE_ExtendedKeyUsage *extendUsage;
CE_AuthorityKeyID *authorityId;
CSSM_KEY_PTR pubKey;
CSSM_RETURN outErr = CSSM_OK; CSSM_BOOL policyFail = CSSM_FALSE; CSSM_RETURN policyError = CSSM_OK;
if(policy == kTPDefault) {
return CSSM_OK;
}
if(certGroup == NULL) {
return CSSMERR_TP_INVALID_CERTGROUP;
}
numCerts = certGroup->numCerts();
if(numCerts == 0) {
return CSSMERR_TP_INVALID_CERTGROUP;
}
if(policy == kTPiSign) {
if(!verifiedToRoot) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
if(numCerts <= 1) {
return CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
for(certDex=0; certDex<numCerts; certDex++) {
if(iSignGetCertInfo(alloc,
certGroup->certAtIndex(certDex),
&certInfo[certDex])) {
(certGroup->certAtIndex(certDex))->addStatusCode(
CSSMERR_TP_INVALID_CERTIFICATE);
outErr = CSSMERR_TP_INVALID_CERTIFICATE;
goto errOut;
}
}
for(certDex=0; certDex<numCerts; certDex++) {
thisCertInfo = &certInfo[certDex];
TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
if(thisCertInfo->foundUnknownCritical) {
tpPolicyError("tp_policyVerify: critical flag in unknown extension");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN)) {
policyFail = CSSM_TRUE;
}
}
if((pubKey=thisTpCertInfo->pubKey()) != NULL) {
CSSM_KEYHEADER *keyHdr = &pubKey->KeyHeader;
if(keyHdr->AlgorithmId == CSSM_ALGID_RSA && keyHdr->LogicalKeySizeInBits < 1024) {
tpPolicyError("tp_policyVerify: RSA key size too small");
if(thisTpCertInfo->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE)) {
policyFail = CSSM_TRUE;
}
}
}
isLeaf = thisTpCertInfo->isLeaf();
isRoot = thisTpCertInfo->isSelfSigned(true);
if(!thisCertInfo->basicConstraints.present) {
if(isLeaf) {
cA = CSSM_FALSE;
}
else if(isRoot) {
cA = CSSM_TRUE;
}
else {
switch(policy) {
default:
cA = CSSM_FALSE;
break;
case kTPiSign:
tpPolicyError("tp_policyVerify: no "
"basicConstraints");
if(thisTpCertInfo->addStatusCode(
CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS)) {
policyFail = CSSM_TRUE;
}
break;
}
}
}
else {
#if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
if(!thisCertInfo->basicConstraints.critical) {
tpPolicyError("tp_policyVerify: basicConstraints marked "
"not critical");
if(thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED)) {
policyFail = CSSM_TRUE;
}
}
#endif
const CE_BasicConstraints *bcp =
&thisCertInfo->basicConstraints.extnData->basicConstraints;
cA = bcp->cA;
if(!isLeaf && cA && bcp->pathLenConstraintPresent) {
if(certDex > (bcp->pathLenConstraint + 1)) {
tpPolicyError("tp_policyVerify: pathLenConstraint "
"exceeded");
if(thisTpCertInfo->addStatusCode(
CSSMERR_APPLETP_PATH_LEN_CONSTRAINT)) {
policyFail = CSSM_TRUE;
}
}
}
}
if(isLeaf) {
if(cA && !isRoot &&
!(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
tpPolicyError("tp_policyVerify: cA true for leaf");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
policyFail = CSSM_TRUE;
}
}
} else if(!cA) {
tpPolicyError("tp_policyVerify: cA false for non-leaf");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
policyFail = CSSM_TRUE;
}
}
if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
if(isRoot) {
tpPolicyError("tp_policyVerify: authorityId in root");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
policyFail = CSSM_TRUE;
}
}
if(thisCertInfo->authorityId.critical) {
tpPolicyError("tp_policyVerify: authorityId marked "
"critical");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
policyFail = CSSM_TRUE;
}
}
}
if(thisCertInfo->subjectId.present) {
if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
tpPolicyError("tp_policyVerify: subjectId marked critical");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID)) {
policyFail = CSSM_TRUE;
}
}
}
if(thisCertInfo->keyUsage.present) {
if(isLeaf) {
switch(policy) {
case kTPiSign:
case kTP_SWUpdateSign:
case kTP_ResourceSign:
case kTP_CodeSigning:
case kTP_PackageSigning:
expUsage = CE_KU_DigitalSignature;
break;
case kCrlPolicy:
expUsage = CE_KU_CRLSign;
break;
default:
expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
break;
}
}
else {
expUsage = CE_KU_KeyCertSign;
}
actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
if(!(actUsage & expUsage)) {
tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
"usage 0x%x)",
(certDex == 0) ? "TRUE" : "FALSE", actUsage);
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
#if 0
if((policy == kTP_SMIME) && !thisCertInfo->keyUsage.critical) {
if(SMIME_KEY_USAGE_MUST_BE_CRITICAL || isLeaf || isRoot) {
tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
if(thisTpCertInfo->addStatusCode(
CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL)) {
policyFail = CSSM_TRUE;
}
}
}
#endif
}
else if(policy == kTPiSign) {
if(isLeaf && thisCertInfo->netscapeCertType.present) {
CE_NetscapeCertType ct =
thisCertInfo->netscapeCertType.extnData->netscapeCertType;
if(!(ct & CE_NCT_ObjSign)) {
tpPolicyError("tp_policyVerify: netscape-cert-type, "
"!ObjectSign");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
}
else if(!isRoot) {
tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
"!(leaf and netscapeCertType)");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
}
if(isLeaf && thisTpCertInfo->hasEmptySubjectName()) {
bool badEmptySubject = false;
if(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) {
badEmptySubject = true;
}
else if(!thisCertInfo->subjectAltName.present ||
!thisCertInfo->subjectAltName.critical) {
badEmptySubject = true;
}
if(badEmptySubject) {
tpPolicyError("tp_policyVerify: bad empty subject");
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT)) {
policyFail = CSSM_TRUE;
}
}
}
if(thisCertInfo->qualCertStatements.present &&
thisCertInfo->qualCertStatements.critical) {
CE_QC_Statements *qcss =
&thisCertInfo->qualCertStatements.extnData->qualifiedCertStatements;
uint32 numQcs = qcss->numQCStatements;
for(unsigned qdex=0; qdex<numQcs; qdex++) {
CSSM_OID_PTR qid = &qcss->qcStatements[qdex].statementId;
bool ok = false;
for(unsigned kdex=0; kdex<NUM_KNOWN_QUAL_CERT_STATEMENTS; kdex++) {
if(tpCompareCssmData(qid, knownQualifiedCertStatements[kdex])) {
ok = true;
break;
}
}
if(!ok) {
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT)) {
policyFail = CSSM_TRUE;
break;
}
}
}
}
if (tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH, false) ||
tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH_2048, false)) {
if(thisCertInfo->keyUsage.present) {
actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
} else {
actUsage = 0;
}
if(!(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) && (certDex == 0)) {
expUsage = CE_KU_DigitalSignature;
} else {
expUsage = actUsage | CE_KU_KeyCertSign;
}
if(!(actUsage == expUsage)) {
tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
"usage 0x%x)",
(certDex == 0) ? "TRUE" : "FALSE", actUsage);
if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
}
}
if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
if(extendUsage->numPurposes != 1) {
tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
"(%d)",
(int)extendUsage->numPurposes);
if((certGroup->certAtIndex(0))->addStatusCode(
CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
if(!tpCompareOids(extendUsage->purposes,
&CSSMOID_ExtendedUseCodeSigning)) {
tpPolicyError("tp_policyVerify: bad extendKeyUsage");
if((certGroup->certAtIndex(0))->addStatusCode(
CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
policyFail = CSSM_TRUE;
}
}
}
for(certDex=0; certDex<(numCerts-1); certDex++) {
if(!certInfo[certDex].authorityId.present ||
!certInfo[certDex+1].subjectId.present) {
continue;
}
authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
if(!authorityId->keyIdentifierPresent) {
continue;
}
if(!tpCompareCssmData(&authorityId->keyIdentifier,
&certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
tpPolicyError("tp_policyVerify: bad key ID linkage");
if((certGroup->certAtIndex(certDex))->addStatusCode(
CSSMERR_APPLETP_INVALID_ID_LINKAGE)) {
policyFail = CSSM_TRUE;
}
}
}
for(certDex=0; certDex<(numCerts-1); certDex++) {
if(certInfo[certDex].untrustedSigAlg) {
tpPolicyError("tp_policyVerify: untrusted signature algorithm");
if((certGroup->certAtIndex(certDex))->addStatusCode(
CSSMERR_TP_INVALID_CERTIFICATE)) {
policyFail = CSSM_TRUE;
}
}
}
switch(policy) {
case kTP_SSL:
case kTP_EAP:
case kTP_IPSec:
policyError = tp_verifySslOpts(policy, *certGroup, policyFieldData, certInfo);
break;
case kTP_iChat:
tpDebug("iChat policy");
case kTP_SMIME:
policyError = tp_verifySmimeOpts(policy, *certGroup, policyFieldData, certInfo);
break;
case kTP_SWUpdateSign:
policyError = tp_verifySWUpdateSigningOpts(*certGroup, policyFieldData, certInfo);
break;
case kTP_ResourceSign:
policyError = tp_verifyResourceSigningOpts(*certGroup, policyFieldData, certInfo);
break;
case kTP_CodeSigning:
case kTP_PackageSigning:
policyError = tp_verifyCodePkgSignOpts(policy, *certGroup, policyFieldData, certInfo);
break;
case kTP_MacAppStoreRec:
policyError = tp_verifyMacAppStoreReceiptOpts(*certGroup, policyFieldData, certInfo);
break;
case kTP_AppleIDSharing:
tpPolicyError("tp_policyVerify: unexpected attempt to use legacy kTP_AppleIDSharing");
policyFail = CSSM_TRUE;
abort();
case kTP_TimeStamping:
policyError = tp_verifyTimeStampingOpts(*certGroup, policyFieldData, certInfo);
break;
case kTP_PassbookSigning:
policyError = tp_verifyPassbookSigningOpts(*certGroup, policyFieldData, certInfo);
break;
case kTP_MobileStore:
policyError = tp_verifyMobileStoreSigningOpts(*certGroup, policyFieldData, certInfo, false);
break;
case kTP_TestMobileStore:
policyError = tp_verifyMobileStoreSigningOpts(*certGroup, policyFieldData, certInfo, true);
break;
case kTP_EscrowService:
policyFail = CSSM_TRUE;
break;
case kTP_ProfileSigning:
policyError = tp_verifyProfileSigningOpts(*certGroup, policyFieldData, certInfo, false);
break;
case kTP_QAProfileSigning:
policyError = tp_verifyProfileSigningOpts(*certGroup, policyFieldData, certInfo, true);
break;
case kTP_PCSEscrowService:
policyFail = CSSM_TRUE;
break;
case kTP_ProvisioningProfileSigning:
policyError = tp_verifyProvisioningProfileSigningOpts(*certGroup, policyFieldData, certInfo);
break;
case kTPx509Basic:
case kTPiSign:
case kCrlPolicy:
case kTP_PKINIT_Client:
default:
break;
}
if(outErr == CSSM_OK) {
if(policyError != CSSM_OK) {
outErr = policyError;
}
else if(policyFail) {
outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
}
}
errOut:
for(certDex=0; certDex<numCerts; certDex++) {
thisCertInfo = &certInfo[certDex];
iSignFreeCertInfo(clHand, thisCertInfo);
}
tpFree(alloc, certInfo);
return outErr;
}
void tp_policyTrustSettingParams(
TPPolicy policy,
const CSSM_DATA *policyData,
const char **policyStr,
uint32 *policyStrLen,
SecTrustSettingsKeyUsage *keyUse)
{
*policyStr = NULL;
*keyUse = kSecTrustSettingsKeyUseAny;
if((policyData == NULL) || (policyData->Data == NULL)) {
return;
}
switch(policy) {
case kTP_SSL:
case kTP_EAP:
case kTP_IPSec:
{
if(policyData->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
return;
}
CSSM_APPLE_TP_SSL_OPTIONS *sslOpts =
(CSSM_APPLE_TP_SSL_OPTIONS *)policyData->Data;
*policyStr = sslOpts->ServerName;
*policyStrLen = sslOpts->ServerNameLen;
if(sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
*keyUse = kSecTrustSettingsKeyUseSignature;
}
else {
*keyUse = kSecTrustSettingsKeyUseEnDecryptKey;
}
return;
}
case kTP_iChat:
case kTP_SMIME:
{
if(policyData->Length != sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
return;
}
CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts =
(CSSM_APPLE_TP_SMIME_OPTIONS *)policyData->Data;
*policyStr = smimeOpts->SenderEmail;
*policyStrLen = smimeOpts->SenderEmailLen;
SecTrustSettingsKeyUsage ku = 0;
CE_KeyUsage smimeKu = smimeOpts->IntendedUsage;
if(smimeKu & (CE_KU_DigitalSignature | CE_KU_KeyCertSign | CE_KU_CRLSign)) {
ku |= kSecTrustSettingsKeyUseSignature;
}
if(smimeKu & (CE_KU_KeyEncipherment | CE_KU_DataEncipherment)) {
ku |= kSecTrustSettingsKeyUseEnDecryptKey;
}
*keyUse = ku;
return;
}
default:
return;
}
}
#pragma clang diagnostic pop