2 * Copyright (c) 2000-2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 policies.cpp - TP module policy implementation
23 #include <Security/cssmtype.h>
24 #include <Security/cssmapi.h>
25 #include "tpPolicies.h"
26 #include <Security/cssmerr.h>
27 #include "tpdebugging.h"
28 #include "certGroupUtils.h"
29 #include <Security/x509defs.h>
30 #include <Security/oidsalg.h>
31 #include <Security/oidsattr.h>
32 #include <Security/oidscert.h>
33 #include <Security/certextensions.h>
34 #include <Security/cssmapple.h>
35 #include <Security/SecCertificate.h>
36 #include <Security/SecCertificatePriv.h>
40 #include <CoreFoundation/CFString.h>
41 #include <CommonCrypto/CommonDigest.h>
43 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Wunused-const-variable"
47 * Our private per-extension info. One of these per (understood) extension per
53 CE_Data
*extnData
; // mallocd by CL
54 CSSM_DATA
*valToFree
; // the data we pass to freeField()
58 * Struct to keep track of info pertinent to one cert.
62 /* extensions we're interested in */
63 iSignExtenInfo authorityId
;
64 iSignExtenInfo subjectId
;
65 iSignExtenInfo keyUsage
;
66 iSignExtenInfo extendKeyUsage
;
67 iSignExtenInfo basicConstraints
;
68 iSignExtenInfo netscapeCertType
;
69 iSignExtenInfo subjectAltName
;
70 iSignExtenInfo certPolicies
;
71 iSignExtenInfo qualCertStatements
;
72 iSignExtenInfo nameConstraints
;
73 iSignExtenInfo policyMappings
;
74 iSignExtenInfo policyConstraints
;
75 iSignExtenInfo inhibitAnyPolicy
;
76 iSignExtenInfo certificatePolicies
;
78 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING */
79 CSSM_BOOL foundPassbookSigning
;
80 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE */
81 CSSM_BOOL foundAppleSysInt2Marker
;
82 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION */
83 CSSM_BOOL foundAppleServerAuthMarker
;
84 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE */
85 CSSM_BOOL foundEscrowServiceMarker
;
86 /* flag indicating presence of a critical extension we don't understand */
87 CSSM_BOOL foundUnknownCritical
;
88 /* flag indicating that this certificate was signed with a known-broken algorithm */
89 CSSM_BOOL untrustedSigAlg
;
94 * The list of Qualified Cert Statement statementIds we understand, even though
95 * we don't actually do anything with them; if these are found in a Qualified
96 * Cert Statement that's critical, we can truthfully say "yes we understand this".
98 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
100 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
,
101 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
,
102 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
103 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
104 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
105 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
107 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
109 static CSSM_RETURN
tp_verifyMacAppStoreReceiptOpts(TPCertGroup
&certGroup
,
110 const CSSM_DATA
*fieldOpts
, const iSignCertInfo
*certInfo
);
112 static CSSM_RETURN
tp_verifyPassbookSigningOpts(TPCertGroup
&certGroup
,
113 const CSSM_DATA
*fieldOpts
, const iSignCertInfo
*certInfo
);
115 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
);
117 #define kSecPolicySHA1Size 20
118 static const UInt8 kAppleCASHA1
[kSecPolicySHA1Size
] = {
119 0x61, 0x1E, 0x5B, 0x66, 0x2C, 0x59, 0x3A, 0x08, 0xFF, 0x58,
120 0xD1, 0x4A, 0xE2, 0x24, 0x52, 0xD1, 0x98, 0xDF, 0x6C, 0x60
123 static const UInt8 kMobileRootSHA1
[kSecPolicySHA1Size
] = {
124 0xBD, 0xD6, 0x7C, 0x34, 0xD0, 0xB2, 0x68, 0x5D, 0x31, 0x82,
125 0xCD, 0x32, 0xCB, 0xF4, 0x54, 0x69, 0xA1, 0xF1, 0x6B, 0x09
128 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
129 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
130 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
134 * Certificate policy OIDs
138 #define ANY_POLICY_OID OID_EXTENSION, 0x32, 0x00
139 #define ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 2
142 #define INHIBIT_ANY_POLICY_OID OID_EXTENSION, 0x54
143 #define INHIBIT_ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 1
145 /* 2.16.840.1.101.2.1 */
146 #define US_DOD_INFOSEC 0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
147 #define US_DOD_INFOSEC_LEN 7
149 /* 2.16.840.1.101.2.1.11.10 */
150 #define PIV_AUTH_OID US_DOD_INFOSEC, 0x0B, 0x0A
151 #define PIV_AUTH_OID_LEN US_DOD_INFOSEC_LEN + 2
153 /* 2.16.840.1.101.2.1.11.20 */
154 #define PIV_AUTH_2048_OID US_DOD_INFOSEC, 0x0B, 0x14
155 #define PIV_AUTH_2048_OID_LEN US_DOD_INFOSEC_LEN + 2
157 static const uint8 OID_ANY_POLICY
[] = {ANY_POLICY_OID
};
158 const CSSM_OID CSSMOID_ANY_POLICY
= {ANY_POLICY_OID_LEN
, (uint8
*)OID_ANY_POLICY
};
159 static const uint8 OID_INHIBIT_ANY_POLICY
[] = {INHIBIT_ANY_POLICY_OID
};
160 const CSSM_OID CSSMOID_INHIBIT_ANY_POLICY
= {INHIBIT_ANY_POLICY_OID_LEN
, (uint8
*)OID_INHIBIT_ANY_POLICY
};
161 static const uint8 OID_PIV_AUTH
[] = {PIV_AUTH_OID
};
162 const CSSM_OID CSSMOID_PIV_AUTH
= {PIV_AUTH_OID_LEN
, (uint8
*)OID_PIV_AUTH
};
163 static const uint8 OID_PIV_AUTH_2048
[] = {PIV_AUTH_2048_OID
};
164 const CSSM_OID CSSMOID_PIV_AUTH_2048
= {PIV_AUTH_2048_OID_LEN
, (uint8
*)OID_PIV_AUTH_2048
};
166 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
167 const CSSM_DATA
*fieldOpts
, // optional Common Name
168 const iSignCertInfo
*certInfo
);
170 * Setup a single iSignExtenInfo. Called once per known extension
173 static CSSM_RETURN
tpSetupExtension(
176 iSignExtenInfo
*extnInfo
) // which component of certInfo
178 if(extnData
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
179 tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
180 return CSSMERR_TP_UNKNOWN_FORMAT
;
182 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)extnData
->Data
;
183 extnInfo
->present
= CSSM_TRUE
;
184 extnInfo
->critical
= cssmExt
->critical
;
185 extnInfo
->extnData
= (CE_Data
*)cssmExt
->value
.parsedValue
;
186 extnInfo
->valToFree
= extnData
;
191 * Fetch a known extension, set up associated iSignExtenInfo if present.
193 static CSSM_RETURN
iSignFetchExtension(
196 const CSSM_OID
*fieldOid
, // which extension to fetch
197 iSignExtenInfo
*extnInfo
) // where the info goes
199 CSSM_DATA_PTR fieldValue
; // mallocd by CL
202 crtn
= tpCert
->fetchField(fieldOid
, &fieldValue
);
206 case CSSMERR_CL_NO_FIELD_VALUES
:
207 /* field not present, OK */
212 return tpSetupExtension(alloc
,
218 * This function performs a check of an extension marked 'critical'
219 * to see if it's one we understand. Returns CSSM_OK if the extension
220 * is acceptable, CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN if unknown.
222 static CSSM_RETURN
iSignVerifyCriticalExtension(
223 CSSM_X509_EXTENSION
*cssmExt
)
225 if (!cssmExt
|| !cssmExt
->extnId
.Data
)
226 return CSSMERR_TP_INVALID_FIELD_POINTER
;
228 if (!cssmExt
->critical
)
231 /* FIXME: remove when policyConstraints NSS template is fixed */
232 if (!memcmp(cssmExt
->extnId
.Data
, CSSMOID_PolicyConstraints
.Data
, CSSMOID_PolicyConstraints
.Length
))
235 if (cssmExt
->extnId
.Length
> APPLE_EXTENSION_OID_LENGTH
&&
236 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION
.Data
, APPLE_EXTENSION_OID_LENGTH
)) {
237 /* This extension's OID is under the appleCertificateExtensions arc */
241 return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
;
245 * Search for all unknown extensions. If we find one which is flagged critical,
246 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
248 static CSSM_RETURN
iSignSearchUnknownExtensions(
250 iSignCertInfo
*certInfo
)
253 CSSM_DATA_PTR fieldValue
= NULL
;
254 CSSM_HANDLE searchHand
= CSSM_INVALID_HANDLE
;
255 uint32 numFields
= 0;
257 certInfo
->foundPassbookSigning
= CSSM_FALSE
;
258 certInfo
->foundAppleSysInt2Marker
= CSSM_FALSE
;
259 certInfo
->foundEscrowServiceMarker
= CSSM_FALSE
;
261 crtn
= CSSM_CL_CertGetFirstCachedFieldValue(tpCert
->clHand(),
263 &CSSMOID_X509V3CertificateExtensionCStruct
,
269 /* found one, proceed */
271 case CSSMERR_CL_NO_FIELD_VALUES
:
272 /* no unknown extensions present, OK */
278 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
279 tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
280 return CSSMERR_TP_UNKNOWN_FORMAT
;
283 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
284 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1 &&
285 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING
.Data
,
286 APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1)) {
287 /* this is the Passbook Signing extension */
288 certInfo
->foundPassbookSigning
= CSSM_TRUE
;
290 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
&&
291 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE
.Data
,
292 APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
)) {
293 /* this is the Apple System Integration 2 Signing extension */
294 certInfo
->foundAppleSysInt2Marker
= CSSM_TRUE
;
296 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
&&
297 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE
.Data
,
298 APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
)) {
299 /* this is the Escrow Service Signing extension */
300 certInfo
->foundEscrowServiceMarker
= CSSM_TRUE
;
303 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
304 /* BRRZAPP! Found an unknown extension marked critical */
305 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
308 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
309 &CSSMOID_X509V3CertificateExtensionCStruct
,
313 /* process remaining unknown extensions */
314 for(unsigned i
=1; i
<numFields
; i
++) {
315 crtn
= CSSM_CL_CertGetNextCachedFieldValue(tpCert
->clHand(),
319 /* should never happen */
320 tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
324 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
325 tpPolicyError("iSignSearchUnknownExtensions: "
326 "malformed CSSM_FIELD");
327 crtn
= CSSMERR_TP_UNKNOWN_FORMAT
;
331 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
332 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1 &&
333 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING
.Data
,
334 APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1)) {
335 /* this is the Passbook Signing extension */
336 certInfo
->foundPassbookSigning
= CSSM_TRUE
;
338 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
&&
339 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE
.Data
,
340 APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
)) {
341 /* this is the Apple System Integration 2 Signing extension */
342 certInfo
->foundAppleSysInt2Marker
= CSSM_TRUE
;
344 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH
&&
345 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION
.Data
,
346 APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH
)) {
347 /* this is the Apple Server Authentication extension */
348 certInfo
->foundAppleServerAuthMarker
= CSSM_TRUE
;
350 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
&&
351 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE
.Data
,
352 APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
)) {
353 /* this is the Escrow Service Signing extension */
354 certInfo
->foundEscrowServiceMarker
= CSSM_TRUE
;
357 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
358 /* BRRZAPP! Found an unknown extension marked critical */
359 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
362 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
363 &CSSMOID_X509V3CertificateExtensionCStruct
,
366 } /* for additional fields */
370 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
371 &CSSMOID_X509V3CertificateExtensionCStruct
,
374 if(searchHand
!= CSSM_INVALID_HANDLE
) {
375 CSSM_CL_CertAbortQuery(tpCert
->clHand(), searchHand
);
381 * Check the signature algorithm. If it's known to be untrusted,
382 * flag certInfo->untrustedSigAlg.
384 static void iSignCheckSignatureAlgorithm(
386 iSignCertInfo
*certInfo
)
388 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
= NULL
;
389 CSSM_DATA_PTR valueToFree
= NULL
;
391 algId
= tp_CertGetAlgId(tpCert
, &valueToFree
);
393 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2
) ||
394 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2WithRSA
) ||
395 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD5
) ||
396 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD5WithRSA
) ) {
397 certInfo
->untrustedSigAlg
= CSSM_TRUE
;
399 certInfo
->untrustedSigAlg
= CSSM_FALSE
;
403 tp_CertFreeAlgId(tpCert
->clHand(), valueToFree
);
408 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
409 * Returns CSSM_FAIL on error.
411 static CSSM_RETURN
iSignGetCertInfo(
414 iSignCertInfo
*certInfo
)
418 /* first grind thru the extensions we're interested in */
419 crtn
= iSignFetchExtension(alloc
,
421 &CSSMOID_AuthorityKeyIdentifier
,
422 &certInfo
->authorityId
);
426 crtn
= iSignFetchExtension(alloc
,
428 &CSSMOID_SubjectKeyIdentifier
,
429 &certInfo
->subjectId
);
433 crtn
= iSignFetchExtension(alloc
,
436 &certInfo
->keyUsage
);
440 crtn
= iSignFetchExtension(alloc
,
442 &CSSMOID_ExtendedKeyUsage
,
443 &certInfo
->extendKeyUsage
);
447 crtn
= iSignFetchExtension(alloc
,
449 &CSSMOID_BasicConstraints
,
450 &certInfo
->basicConstraints
);
454 crtn
= iSignFetchExtension(alloc
,
456 &CSSMOID_NetscapeCertType
,
457 &certInfo
->netscapeCertType
);
461 crtn
= iSignFetchExtension(alloc
,
463 &CSSMOID_SubjectAltName
,
464 &certInfo
->subjectAltName
);
468 crtn
= iSignFetchExtension(alloc
,
470 &CSSMOID_CertificatePolicies
,
471 &certInfo
->certPolicies
);
475 crtn
= iSignFetchExtension(alloc
,
477 &CSSMOID_QC_Statements
,
478 &certInfo
->qualCertStatements
);
482 crtn
= iSignFetchExtension(alloc
,
484 &CSSMOID_NameConstraints
,
485 &certInfo
->nameConstraints
);
489 crtn
= iSignFetchExtension(alloc
,
491 &CSSMOID_PolicyMappings
,
492 &certInfo
->policyMappings
);
496 crtn
= iSignFetchExtension(alloc
,
498 &CSSMOID_PolicyConstraints
,
499 &certInfo
->policyConstraints
);
503 crtn
= iSignFetchExtension(alloc
,
505 &CSSMOID_InhibitAnyPolicy
,
506 &certInfo
->inhibitAnyPolicy
);
510 crtn
= iSignFetchExtension(alloc
,
512 &CSSMOID_CertificatePolicies
,
513 &certInfo
->certificatePolicies
);
518 /* check signature algorithm field */
519 iSignCheckSignatureAlgorithm(tpCert
, certInfo
);
521 /* now look for extensions we don't understand - the only thing we're interested
522 * in is the critical flag. */
523 return iSignSearchUnknownExtensions(tpCert
, certInfo
);
527 * Free (via CL) the fields allocated in iSignGetCertInfo().
529 static void iSignFreeCertInfo(
530 CSSM_CL_HANDLE clHand
,
531 iSignCertInfo
*certInfo
)
533 if(certInfo
->authorityId
.present
) {
534 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_AuthorityKeyIdentifier
,
535 certInfo
->authorityId
.valToFree
);
537 if(certInfo
->subjectId
.present
) {
538 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectKeyIdentifier
,
539 certInfo
->subjectId
.valToFree
);
541 if(certInfo
->keyUsage
.present
) {
542 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_KeyUsage
,
543 certInfo
->keyUsage
.valToFree
);
545 if(certInfo
->extendKeyUsage
.present
) {
546 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_ExtendedKeyUsage
,
547 certInfo
->extendKeyUsage
.valToFree
);
549 if(certInfo
->basicConstraints
.present
) {
550 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_BasicConstraints
,
551 certInfo
->basicConstraints
.valToFree
);
553 if(certInfo
->netscapeCertType
.present
) {
554 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_NetscapeCertType
,
555 certInfo
->netscapeCertType
.valToFree
);
557 if(certInfo
->subjectAltName
.present
) {
558 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectAltName
,
559 certInfo
->subjectAltName
.valToFree
);
561 if(certInfo
->certPolicies
.present
) {
562 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
563 certInfo
->certPolicies
.valToFree
);
565 // if(certInfo->policyConstraints.present) {
566 // CSSM_CL_FreeFieldValue(clHand, &CSSMOID_PolicyConstraints,
567 // certInfo->policyConstraints.valToFree);
569 if(certInfo
->qualCertStatements
.present
) {
570 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_QC_Statements
,
571 certInfo
->qualCertStatements
.valToFree
);
573 if(certInfo
->certificatePolicies
.present
) {
574 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
575 certInfo
->certificatePolicies
.valToFree
);
580 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
581 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
582 * Also indicates whether *any* of the specified fields were found, regardless
586 SN_CommonName
, // CSSMOID_CommonName, host name format
587 SN_Email
, // CSSMOID_EmailAddress
588 SN_UserID
, // CSSMOID_UserID
589 SN_OrgUnit
// CSSMOID_OrganizationalUnitName
590 } SubjSubjNameSearchType
;
592 static CSSM_BOOL
tpCompareSubjectName(
594 SubjSubjNameSearchType searchType
,
595 bool normalizeAll
, // for SN_Email case: lower-case all of
596 // the cert's value, not just the portion
598 const char *callerStr
, // already tpToLower'd
602 char *certName
= NULL
; // from cert's subject name
603 uint32 certNameLen
= 0;
604 CSSM_DATA_PTR subjNameData
= NULL
;
606 CSSM_BOOL ourRtn
= CSSM_FALSE
;
607 const CSSM_OID
*oidSrch
;
609 const char x500_userid_oid
[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
610 CSSM_OID X500_UserID_OID
= { sizeof(x500_userid_oid
), (uint8
*)x500_userid_oid
};
615 oidSrch
= &CSSMOID_CommonName
;
618 oidSrch
= &CSSMOID_EmailAddress
;
621 oidSrch
= &X500_UserID_OID
;
624 oidSrch
= &CSSMOID_OrganizationalUnitName
;
630 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
632 /* should never happen, we shouldn't be here if there is no subject */
633 tpPolicyError("tpCompareSubjectName: error retrieving subject name");
636 CSSM_X509_NAME_PTR x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
637 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
638 tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
639 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
643 /* Now grunge thru the X509 name looking for a common name */
644 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
645 CSSM_X509_RDN_PTR rdnp
;
649 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
650 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
651 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
652 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
653 if(tpCompareOids(&ptvp
->type
, oidSrch
)) {
655 certName
= (char *)ptvp
->value
.Data
;
656 certNameLen
= (uint32
)ptvp
->value
.Length
;
660 /* handle odd encodings that we need to convert to 8-bit */
661 CFStringBuiltInEncodings encoding
;
662 CFDataRef cfd
= NULL
;
663 bool doConvert
= false;
664 switch(ptvp
->valueType
) {
665 case BER_TAG_T61_STRING
:
667 encoding
= kCFStringEncodingISOLatin1
;
670 case BER_TAG_PKIX_BMP_STRING
:
671 encoding
= kCFStringEncodingUnicode
;
675 * All others - either take as is, or let it fail due to
676 * illegal/incomprehensible format
682 /* raw data ==> CFString */
683 cfd
= CFDataCreate(NULL
, (UInt8
*)certName
, certNameLen
);
685 /* try next component */
688 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(
689 NULL
, cfd
, encoding
);
692 tpPolicyError("tpCompareSubjectName: bad str (1)");
696 /* CFString ==> straight ASCII */
697 cfd
= CFStringCreateExternalRepresentation(NULL
,
698 cfStr
, kCFStringEncodingASCII
, 0);
701 tpPolicyError("tpCompareSubjectName: bad str (2)");
704 certNameLen
= (uint32
)CFDataGetLength(cfd
);
705 certName
= (char *)CFDataGetBytePtr(cfd
);
707 ourRtn
= tpCompareHostNames(callerStr
, callerStrLen
,
708 certName
, certNameLen
);
716 ourRtn
= tpCompareEmailAddr(callerStr
, callerStrLen
,
717 certName
, certNameLen
, normalizeAll
);
721 /* exact match only here, for now */
722 ourRtn
= ((callerStrLen
== certNameLen
) &&
723 !memcmp(callerStr
, certName
, certNameLen
)) ?
724 CSSM_TRUE
: CSSM_FALSE
;
731 /* else keep going, maybe there's another common name */
738 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
743 * Compare ASCII form of an IP address to a CSSM_DATA containing
744 * the IP address's numeric components. Returns true on match.
746 static CSSM_BOOL
tpCompIpAddrStr(
749 const CSSM_DATA
*numeric
)
751 const char *cp
= str
;
755 if((numeric
== NULL
) || (numeric
->Length
== 0) || (str
== NULL
)) {
758 if(cp
[strLen
- 1] == '\0') {
759 /* ignore NULL terminator */
762 for(unsigned dex
=0; dex
<numeric
->Length
; dex
++) {
763 /* cp points to start of current string digit */
765 const char *lastChar
= cp
+ strLen
;
767 for( ; nextDot
<lastChar
; nextDot
++) {
768 if(*nextDot
== '.') {
772 if(nextDot
== lastChar
) {
773 /* legal and required on last digit */
774 if(dex
!= (numeric
->Length
- 1)) {
778 else if(dex
== (numeric
->Length
- 1)) {
781 ptrdiff_t digLen
= nextDot
- cp
;
782 if(digLen
>= sizeof(buf
)) {
786 memmove(buf
, cp
, digLen
);
788 /* incr digLen to include the next dot */
792 int digVal
= atoi(buf
);
793 if(digVal
!= numeric
->Data
[dex
]) {
801 * See if cert's subjectAltName contains an element matching caller-specified
802 * string, hostname, in the following forms:
804 * SAN_HostName : dnsName, iPAddress
805 * SAN_Email : RFC822Name
807 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
809 * Also indicates whether or not a dnsName (search type HostName) or
810 * RFC822Name (search type SAM_Email) was found, regardless of result
813 * The appStr/appStrLen args are optional - if NULL/0, only the
814 * search for dnsName/RFC822Name is done.
819 } SubjAltNameSearchType
;
821 static CSSM_BOOL
tpCompareSubjectAltName(
822 const iSignExtenInfo
&subjAltNameInfo
,
823 const char *appStr
, // caller has lower-cased as appropriate
825 SubjAltNameSearchType searchType
,
826 bool normalizeAll
, // for SAN_Email case: lower-case all of
827 // the cert's value, not just the portion
829 bool &dnsNameFound
, // RETURNED, SAN_HostName case
830 bool &emailFound
) // RETURNED, SAN_Email case
832 dnsNameFound
= false;
834 if(!subjAltNameInfo
.present
) {
835 /* common failure, no subjectAltName found */
839 CE_GeneralNames
*names
= &subjAltNameInfo
.extnData
->subjectAltName
;
840 CSSM_BOOL ourRtn
= CSSM_FALSE
;
844 /* Search thru the CE_GeneralNames looking for the appropriate attribute */
845 for(unsigned dex
=0; dex
<names
->numNames
; dex
++) {
846 CE_GeneralName
*name
= &names
->generalName
[dex
];
849 switch(name
->nameType
) {
852 /* nothing to do here */
855 ourRtn
= tpCompIpAddrStr(appStr
, appStrLen
, &name
->name
);
859 if(name
->berEncoded
) {
860 tpErrorLog("tpCompareSubjectAltName: malformed "
861 "CE_GeneralName (1)\n");
864 certName
= (char *)name
->name
.Data
;
865 if(certName
== NULL
) {
866 tpErrorLog("tpCompareSubjectAltName: malformed "
867 "CE_GeneralName (2)\n");
870 certNameLen
= (uint32
)(name
->name
.Length
);
873 /* skip if caller passed in NULL */
874 ourRtn
= tpCompareHostNames(appStr
, appStrLen
,
875 certName
, certNameLen
);
880 /* not interested, proceed to next name */
883 break; /* from case HostName */
886 if(name
->nameType
!= GNT_RFC822Name
) {
890 certName
= (char *)name
->name
.Data
;
891 if(certName
== NULL
) {
892 tpErrorLog("tpCompareSubjectAltName: malformed "
896 certNameLen
= (uint32
)(name
->name
.Length
);
899 ourRtn
= tpCompareEmailAddr(appStr
, appStrLen
, certName
,
900 certNameLen
, normalizeAll
);
912 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
913 static CSSM_BOOL
tpIsNumeric(
914 const char *hostName
,
915 unsigned hostNameLen
)
917 if(hostName
[hostNameLen
- 1] == '\0') {
918 /* ignore NULL terminator */
921 for(unsigned i
=0; i
<hostNameLen
; i
++) {
922 char c
= *hostName
++;
934 * Convert a typed string represented by a CSSM_X509_TYPE_VALUE_PAIR to a
935 * CFStringRef. Caller owns and must release the result. NULL return means
936 * unconvertible input "string".
938 static CFStringRef
tpTvpToCfString(
939 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
)
941 CFStringBuiltInEncodings encoding
;
942 switch(tvp
->valueType
) {
943 case BER_TAG_T61_STRING
:
945 encoding
= kCFStringEncodingISOLatin1
;
947 case BER_TAG_PKIX_BMP_STRING
:
948 encoding
= kCFStringEncodingUnicode
;
950 case BER_TAG_PRINTABLE_STRING
:
951 case BER_TAG_IA5_STRING
:
952 case BER_TAG_PKIX_UTF8_STRING
:
953 encoding
= kCFStringEncodingUTF8
;
959 /* raw data ==> CFString */
960 CFDataRef cfd
= CFDataCreate(NULL
, tvp
->value
.Data
, tvp
->value
.Length
);
964 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, encoding
);
970 * Compare a CFString and a string represented by a CSSM_X509_TYPE_VALUE_PAIR.
971 * Returns CSSM_TRUE if they are equal.
973 static bool tpCompareTvpToCfString(
974 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
,
976 CFOptionFlags flags
) // e.g., kCFCompareCaseInsensitive
978 CFStringRef cfStr
= tpTvpToCfString(tvp
);
982 CFComparisonResult res
= CFStringCompare(refStr
, cfStr
, flags
);
984 if(res
== kCFCompareEqualTo
) {
993 * Given one iSignCertInfo, determine whether or not the specified
994 * EKU OID, or - optionally - CSSMOID_ExtendedKeyUsageAny - is present.
995 * Returns true if so, else false.
997 static bool tpVerifyEKU(
998 const iSignCertInfo
&certInfo
,
999 const CSSM_OID
&ekuOid
,
1000 bool ekuAnyOK
) // if true, CSSMOID_ExtendedKeyUsageAny counts as "found"
1002 if(!certInfo
.extendKeyUsage
.present
) {
1005 CE_ExtendedKeyUsage
*eku
= &certInfo
.extendKeyUsage
.extnData
->extendedKeyUsage
;
1006 assert(eku
!= NULL
);
1008 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1009 const CSSM_OID
*foundEku
= &eku
->purposes
[i
];
1010 if(tpCompareOids(foundEku
, &ekuOid
)) {
1013 if(ekuAnyOK
&& tpCompareOids(foundEku
, &CSSMOID_ExtendedKeyUsageAny
)) {
1021 * Given one iSignCertInfo, determine whether or not the specified
1022 * Certificate Policy OID, or - optionally - CSSMOID_ANY_POLICY - is present.
1023 * Returns true if so, else false.
1025 static bool tpVerifyCPE(
1026 const iSignCertInfo
&certInfo
,
1027 const CSSM_OID
&cpOid
,
1028 bool anyPolicyOK
) // if true, CSSMOID_ANY_POLICY counts as "found"
1030 if(!certInfo
.certPolicies
.present
) {
1033 CE_CertPolicies
*cp
= &certInfo
.certPolicies
.extnData
->certPolicies
;
1036 for(unsigned i
=0; i
<cp
->numPolicies
; i
++) {
1037 const CE_PolicyInformation
*foundPolicy
= &cp
->policies
[i
];
1038 if(tpCompareOids(&foundPolicy
->certPolicyId
, &cpOid
)) {
1041 if(anyPolicyOK
&& tpCompareOids(&foundPolicy
->certPolicyId
, &CSSMOID_ANY_POLICY
)) {
1049 * Verify iChat handle. We search for a matching (case-insensitive) string
1052 * -- name component ("dmitch") from subject name's CommonName
1054 * -- domain name from subject name's organizationalUnit
1056 * Plus we require an Organization component of "Apple Computer, Inc." or "Apple Inc."
1058 static bool tpCompareIChatHandleName(
1060 const char *iChatHandle
, // UTF8
1061 uint32 iChatHandleLen
)
1063 CSSM_DATA_PTR subjNameData
= NULL
; // from fetchField
1065 bool ourRtn
= false;
1066 CSSM_X509_NAME_PTR x509name
;
1067 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
1068 CSSM_X509_RDN_PTR rdnp
;
1072 /* search until all of these are true */
1073 CSSM_BOOL commonNameMatch
= CSSM_FALSE
; // name before '@'
1074 CSSM_BOOL orgUnitMatch
= CSSM_FALSE
; // domain after '@
1075 CSSM_BOOL orgMatch
= CSSM_FALSE
; // Apple Computer, Inc. (or Apple Inc.)
1078 * incoming UTF8 handle ==> two components.
1079 * First convert to CFString.
1081 if(iChatHandle
[iChatHandleLen
- 1] == '\0') {
1082 /* avoid NULL when creating CFStrings */
1085 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)iChatHandle
, iChatHandleLen
);
1089 CFStringRef handleStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
,
1090 kCFStringEncodingUTF8
);
1092 if(handleStr
== NULL
) {
1093 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
1098 * Find the '@' delimiter
1101 whereIsAt
= CFStringFind(handleStr
, CFSTR("@"), 0);
1102 if(whereIsAt
.length
== 0) {
1103 tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
1104 CFRelease(handleStr
);
1109 * Two components, before and after delimiter
1111 CFRange r
= {0, whereIsAt
.location
};
1112 CFStringRef iChatName
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1113 if(iChatName
== NULL
) {
1114 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
1115 CFRelease(handleStr
);
1118 r
.location
= whereIsAt
.location
+ 1; // after the '@'
1119 r
.length
= CFStringGetLength(handleStr
) - r
.location
;
1120 CFStringRef iChatDomain
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1121 CFRelease(handleStr
);
1122 if(iChatDomain
== NULL
) {
1123 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
1124 CFRelease(iChatName
);
1127 /* subsequent errors to errOut: */
1129 /* get subject name in CSSM form, all subsequent ops work on that */
1130 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1132 /* should never happen, we shouldn't be here if there is no subject */
1133 tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
1137 x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
1138 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
1139 tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
1143 /* Now grunge thru the X509 name looking for three fields */
1145 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
1146 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
1147 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
1148 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
1149 if(!commonNameMatch
&&
1150 tpCompareOids(&ptvp
->type
, &CSSMOID_CommonName
) &&
1151 tpCompareTvpToCfString(ptvp
, iChatName
, kCFCompareCaseInsensitive
)) {
1152 commonNameMatch
= CSSM_TRUE
;
1156 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationalUnitName
) &&
1157 tpCompareTvpToCfString(ptvp
, iChatDomain
, kCFCompareCaseInsensitive
)) {
1158 orgUnitMatch
= CSSM_TRUE
;
1162 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationName
) &&
1163 /* this one is case sensitive */
1164 (tpCompareTvpToCfString(ptvp
, CFSTR("Apple Computer, Inc."), 0) ||
1165 tpCompareTvpToCfString(ptvp
, CFSTR("Apple Inc."), 0))) {
1166 orgMatch
= CSSM_TRUE
;
1169 if(commonNameMatch
&& orgUnitMatch
&& orgMatch
) {
1177 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1178 CFRelease(iChatName
);
1179 CFRelease(iChatDomain
);
1184 * Verify SSL options. Currently this just consists of matching the
1185 * leaf cert's subject common name against the caller's (optional)
1188 static CSSM_RETURN
tp_verifySslOpts(
1190 TPCertGroup
&certGroup
,
1191 const CSSM_DATA
*sslFieldOpts
,
1192 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1194 const iSignCertInfo
&leafCertInfo
= certInfo
[0];
1195 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= NULL
;
1196 unsigned hostNameLen
= 0;
1197 const char *serverName
= NULL
;
1198 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1199 assert(leaf
!= NULL
);
1201 /* CSSM_APPLE_TP_SSL_OPTIONS is optional */
1202 if((sslFieldOpts
!= NULL
) && (sslFieldOpts
->Data
!= NULL
)) {
1203 sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)sslFieldOpts
->Data
;
1204 switch(sslOpts
->Version
) {
1205 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
1206 if(sslFieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
1207 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1210 /* handle backwards compatibility here if necessary */
1212 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1214 hostNameLen
= sslOpts
->ServerNameLen
;
1215 serverName
= sslOpts
->ServerName
;
1218 /* host name check is optional */
1219 if(hostNameLen
!= 0) {
1220 if(serverName
== NULL
) {
1221 return CSSMERR_TP_INVALID_POINTER
;
1224 /* convert caller's hostname string to lower case */
1225 char *hostName
= (char *)certGroup
.alloc().malloc(hostNameLen
);
1226 memmove(hostName
, serverName
, hostNameLen
);
1227 tpToLower(hostName
, hostNameLen
);
1229 CSSM_BOOL match
= CSSM_FALSE
;
1231 /* First check subjectAltName... */
1232 bool dnsNameFound
= false;
1234 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1235 hostName
, hostNameLen
,
1236 SAN_HostName
, false, dnsNameFound
, dummy
);
1239 * Then common name, if
1240 * -- no match from subjectAltName, AND
1241 * -- dnsName was NOT found, AND
1242 * -- hostName is not strictly numeric form (1.2.3.4)
1244 if(!match
&& !dnsNameFound
&& !tpIsNumeric(hostName
, hostNameLen
)) {
1246 match
= tpCompareSubjectName(*leaf
, SN_CommonName
, false, hostName
, hostNameLen
,
1251 * Limit allowed domains for specific anchors
1253 CSSM_BOOL domainMatch
= CSSM_TRUE
;
1255 TPCertInfo
*tpCert
= certGroup
.lastCert();
1257 const CSSM_DATA
*certData
= tpCert
->itemData();
1258 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
1259 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
1260 if (!memcmp(digest
, kAppleCorpCASHA1
, sizeof(digest
))) {
1261 const char *dnlist
[] = { "apple.com", "icloud.com" };
1262 unsigned int idx
, dncount
=2;
1263 domainMatch
= CSSM_FALSE
;
1264 for(idx
=0;idx
<dncount
;idx
++) {
1265 uint32 len
=(uint32
)strlen(dnlist
[idx
]);
1266 char *domainName
=(char*)certGroup
.alloc().malloc(len
);
1267 memmove(domainName
, (char*)dnlist
[idx
], len
);
1268 if(tpCompareDomainSuffix(hostName
, hostNameLen
,
1270 domainMatch
= CSSM_TRUE
;
1272 certGroup
.alloc().free(domainName
);
1281 certGroup
.alloc().free(hostName
);
1283 if(leaf
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
)) {
1284 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1288 if(leaf
->addStatusCode(CSSMERR_APPLETP_CA_PIN_MISMATCH
)) {
1289 return CSSMERR_APPLETP_CA_PIN_MISMATCH
;
1295 * Ensure that, if an extendedKeyUsage extension is present in the
1296 * leaf, that either anyExtendedKeyUsage or the appropriate
1297 * CSSMOID_{Server,Client}Auth, or a SeverGatedCrypto usage is present.
1299 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1300 if(ekuInfo
.present
) {
1301 bool foundGoodEku
= false;
1302 bool isServer
= true;
1303 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1304 assert(eku
!= NULL
);
1307 * Determine appropriate extended key usage; default is SSL server
1309 const CSSM_OID
*extUse
= &CSSMOID_ServerAuth
;
1310 if((sslOpts
!= NULL
) && /* optional, default server side */
1311 (sslOpts
->Version
> 0) && /* this was added in struct version 1 */
1312 (sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
)) {
1313 extUse
= &CSSMOID_ClientAuth
;
1317 /* search for that one or for "any" indicator */
1318 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1319 const CSSM_OID
*purpose
= &eku
->purposes
[i
];
1320 if(tpCompareOids(purpose
, extUse
)) {
1321 foundGoodEku
= true;
1324 if(tpCompareOids(purpose
, &CSSMOID_ExtendedKeyUsageAny
)) {
1325 foundGoodEku
= true;
1328 if((policy
== kTP_IPSec
) && (tpCompareOids(purpose
, &CSSMOID_EKU_IPSec
))) {
1329 foundGoodEku
= true;
1333 /* server gated crypto: server side only */
1334 if(tpCompareOids(purpose
, &CSSMOID_NetscapeSGC
)) {
1335 foundGoodEku
= true;
1338 if(tpCompareOids(purpose
, &CSSMOID_MicrosoftSGC
)) {
1339 foundGoodEku
= true;
1345 if(leaf
->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE
)) {
1346 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1352 * Check for additional options flag (2nd lowest bit set) which indicates
1353 * we must be issued by an Apple intermediate with a particular extension.
1354 * (This flag is set by SecPolicyCreateAppleSSLService in SecPolicy.cpp.)
1356 if((sslOpts
!= NULL
) &&
1357 (sslOpts
->Version
> 0) && /* this was added in struct version 1 */
1358 (sslOpts
->Flags
& 0x00000002)) {
1360 if (certGroup
.numCerts() > 1) {
1361 const iSignCertInfo
*isCertInfo
= &certInfo
[1];
1362 if (!(isCertInfo
->foundAppleServerAuthMarker
== CSSM_TRUE
)) {
1363 TPCertInfo
*tpCert
= certGroup
.certAtIndex(1);
1364 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
1365 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1369 /* we only have the leaf? */
1370 if(leaf
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
)) {
1371 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1379 * Verify SMIME and iChat options.
1380 * This deals with both S/MIME and iChat policies; within the iChat domain it
1381 * deals with Apple-specific .mac certs as well as what we call "generic AIM"
1382 * certs, as used in the Windows AIM client.
1384 #define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
1386 static CSSM_RETURN
tp_verifySmimeOpts(
1388 TPCertGroup
&certGroup
,
1389 const CSSM_DATA
*smimeFieldOpts
,
1390 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1392 const iSignCertInfo
&leafCertInfo
= certInfo
[0];
1393 bool iChat
= (policy
== kTP_iChat
) ? true : false;
1395 * The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1397 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
= NULL
;
1398 if(smimeFieldOpts
!= NULL
) {
1399 smimeOpts
= (CSSM_APPLE_TP_SMIME_OPTIONS
*)smimeFieldOpts
->Data
;
1401 if(smimeOpts
!= NULL
) {
1402 switch(smimeOpts
->Version
) {
1403 case CSSM_APPLE_TP_SMIME_OPTS_VERSION
:
1404 if(smimeFieldOpts
->Length
!=
1405 sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
1406 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1409 /* handle backwards compatibility here if necessary */
1411 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1415 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1416 assert(leaf
!= NULL
);
1418 /* Verify optional email address, a.k.a. handle for iChat policy */
1419 unsigned emailLen
= 0;
1420 if(smimeOpts
!= NULL
) {
1421 emailLen
= smimeOpts
->SenderEmailLen
;
1425 bool emailFoundInSAN
= false;
1426 bool iChatHandleFound
= false; /* indicates a genuine Apple iChat cert */
1427 bool emailFoundInDN
= false;
1429 if(smimeOpts
->SenderEmail
== NULL
) {
1430 return CSSMERR_TP_INVALID_POINTER
;
1433 /* iChat - first try the Apple custom format */
1435 iChatHandleFound
= tpCompareIChatHandleName(*leaf
, smimeOpts
->SenderEmail
,
1437 if(iChatHandleFound
) {
1445 * normalize caller's email string
1446 * SMIME - lowercase only the portion after '@'
1447 * iChat - lowercase all of it
1449 char *email
= (char *)certGroup
.alloc().malloc(emailLen
);
1450 memmove(email
, smimeOpts
->SenderEmail
, emailLen
);
1451 tpNormalizeAddrSpec(email
, emailLen
, iChat
);
1455 * First check subjectAltName. The emailFound bool indicates
1456 * that *some* email address was found, regardless of a match
1460 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1462 SAN_Email
, iChat
, dummy
, emailFoundInSAN
);
1465 * Then subject DN, CSSMOID_EmailAddress, if no match from
1466 * subjectAltName. In this case the whole email address is
1467 * case insensitive (RFC 3280, section 4.1.2.6), so
1471 tpNormalizeAddrSpec(email
, emailLen
, true);
1472 match
= tpCompareSubjectName(*leaf
, SN_Email
, true, email
, emailLen
,
1475 certGroup
.alloc().free(email
);
1478 * Error here if no match found but there was indeed *some*
1479 * email address in the cert.
1481 if(!match
&& (emailFoundInSAN
|| emailFoundInDN
)) {
1482 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
)) {
1483 tpPolicyError("SMIME email addrs in cert but no match");
1484 return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
;
1490 * iChat only: error if app specified email address but there was
1493 if(iChat
&& !emailFoundInSAN
&& !emailFoundInDN
&& !iChatHandleFound
) {
1494 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1495 tpPolicyError("iChat: no email address or handle in cert");
1496 return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
;
1502 * Going by the letter of the law, here's what RFC 2632 has to say
1503 * about the legality of an empty Subject Name:
1505 * ...the subject DN in a user's (i.e. end-entity) certificate MAY
1506 * be an empty SEQUENCE in which case the subjectAltName extension
1507 * will include the subject's identifier and MUST be marked as
1510 * OK, first examine the leaf cert's subject name.
1513 CSSM_DATA_PTR subjNameData
= NULL
;
1514 const iSignExtenInfo
&kuInfo
= leafCertInfo
.keyUsage
;
1515 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1516 const CSSM_X509_NAME
*x509Name
= NULL
;
1519 /* empty subject name processing is S/MIME only */
1523 crtn
= leaf
->fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1525 /* This should really never happen */
1526 tpPolicyError("SMIME policy: error fetching subjectName");
1527 leaf
->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE
);
1528 return CSSMERR_TP_INVALID_CERTIFICATE
;
1530 /* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
1532 x509Name
= (const CSSM_X509_NAME
*)subjNameData
->Data
;
1533 if(x509Name
->numberOfRDNs
== 0) {
1535 * Empty subject name. If we haven't already seen a valid
1536 * email address in the subject alternate name (by looking
1537 * for a specific address specified by app), try to find
1540 if(!emailFoundInSAN
&& // haven't found one, and
1541 (emailLen
== 0)) { // didn't even look yet
1543 tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1544 NULL
, 0, // email, emailLen,
1545 SAN_Email
, false, dummy
,
1546 emailFoundInSAN
); // the variable we're updating
1548 if(!emailFoundInSAN
) {
1549 tpPolicyError("SMIME policy fail: empty subject name and "
1550 "no Email Addrs in SubjectAltName");
1551 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1552 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1553 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1556 /* have to skip the next block */
1562 * One more thing: this leaf must indeed have a subjAltName
1563 * extension and it must be critical. We would not have gotten this
1564 * far if the subjAltName extension was not actually present....
1566 assert(leafCertInfo
.subjectAltName
.present
);
1567 if(!leafCertInfo
.subjectAltName
.critical
) {
1568 tpPolicyError("SMIME policy fail: empty subject name and "
1569 "no Email Addrs in SubjectAltName");
1570 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT
)) {
1571 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1572 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1577 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1580 * Enforce the usage of the key associated with the leaf cert.
1581 * Cert's KeyUsage must be a superset of what the app is trying to do.
1582 * Note the {en,de}cipherOnly flags are handled separately....
1584 if(kuInfo
.present
&& (smimeOpts
!= NULL
)) {
1585 CE_KeyUsage certKu
= *((CE_KeyUsage
*)kuInfo
.extnData
);
1586 CE_KeyUsage appKu
= smimeOpts
->IntendedUsage
;
1587 CE_KeyUsage intersection
= certKu
& appKu
;
1588 if((intersection
& CE_CIPHER_MASK
) != (appKu
& CE_CIPHER_MASK
)) {
1589 tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
1591 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1592 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1596 /* Now the en/de cipher only bits - for keyAgreement only */
1597 if(appKu
& CE_KU_KeyAgreement
) {
1599 * 1. App wants to use this for key agreement; it must
1600 * say what it wants to do with the derived key.
1601 * In this context, the app's XXXonly bit means that
1602 * it wants to use the key for that op - not necessarliy
1605 if((appKu
& (CE_KU_EncipherOnly
| CE_KU_DecipherOnly
)) == 0) {
1606 tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
1607 "no Encipher or Decipher");
1608 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1609 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1614 * 2. If cert restricts to encipher only make sure the
1615 * app isn't trying to decipher.
1617 if((certKu
& CE_KU_EncipherOnly
) &&
1618 (appKu
& CE_KU_DecipherOnly
)) {
1619 tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
1620 "app wants to decipher");
1621 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1622 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1627 * 3. If cert restricts to decipher only make sure the
1628 * app isn't trying to encipher.
1630 if((certKu
& CE_KU_DecipherOnly
) &&
1631 (appKu
& CE_KU_EncipherOnly
)) {
1632 tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
1633 "app wants to encipher");
1634 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1635 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1642 * Extended Key Use verification, which is different for the two policies.
1645 if(iChat
&& !ekuInfo
.present
) {
1647 * iChat: whether generic AIM cert or Apple .mac/iChat cert, we must have an
1648 * extended key use extension.
1650 tpPolicyError("iChat: No extended Key Use");
1651 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1652 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1656 if(!iChatHandleFound
) {
1658 * S/MIME and generic AIM certs when evaluating iChat policy.
1659 * Look for either emailProtection or anyExtendedKeyUsage usages.
1661 * S/MIME : the whole extension is optional.
1662 * iChat : extension must be there (which we've already covered, above)
1663 * and we must find one of those extensions.
1665 if(ekuInfo
.present
) {
1666 bool foundGoodEku
= false;
1667 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1668 assert(eku
!= NULL
);
1669 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1670 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_EmailProtection
)) {
1671 foundGoodEku
= true;
1674 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1675 foundGoodEku
= true;
1680 tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
1681 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1682 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1689 * Apple iChat cert. Look for anyExtendedKeyUsage, iChatSigning,
1690 * ichatEncrypting - the latter of two which can optionally be
1693 assert(iChat
); /* or we could not have even looked for an iChat style handle */
1694 assert(ekuInfo
.present
); /* checked above */
1695 bool foundAnyEku
= false;
1696 bool foundIChatSign
= false;
1697 bool foundISignEncrypt
= false;
1698 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1699 assert(eku
!= NULL
);
1701 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1702 if(tpCompareOids(&eku
->purposes
[i
],
1703 &CSSMOID_APPLE_EKU_ICHAT_SIGNING
)) {
1704 foundIChatSign
= true;
1706 else if(tpCompareOids(&eku
->purposes
[i
],
1707 &CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION
)) {
1708 foundISignEncrypt
= true;
1710 else if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1715 if(!foundAnyEku
&& !foundISignEncrypt
&& !foundIChatSign
) {
1716 /* No go - no acceptable uses found */
1717 tpPolicyError("iChat: No valid extended Key Uses found");
1718 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1719 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1723 /* check for specifically required uses */
1724 if((smimeOpts
!= NULL
) && (smimeOpts
->IntendedUsage
!= 0)) {
1725 if(smimeOpts
->IntendedUsage
& CE_KU_DigitalSignature
) {
1726 if(!foundIChatSign
) {
1727 tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
1728 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1729 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1733 if(smimeOpts
->IntendedUsage
& CE_KU_DataEncipherment
) {
1734 if(!foundISignEncrypt
) {
1735 tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
1736 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1737 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1741 } /* checking IntendedUsage */
1742 } /* iChat cert format */
1748 * Verify Apple SW Update signing (was Apple Code Signing, pre-Leopard) options.
1750 * -- Must have one intermediate cert
1751 * -- intermediate must have basic constraints with path length 0
1752 * -- intermediate has CSSMOID_APPLE_EKU_CODE_SIGNING EKU
1753 * -- leaf cert has either CODE_SIGNING or CODE_SIGN_DEVELOPMENT EKU (the latter of
1754 * which triggers a CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT error)
1756 static CSSM_RETURN
tp_verifySWUpdateSigningOpts(
1757 TPCertGroup
&certGroup
,
1758 const CSSM_DATA
*fieldOpts
, // currently unused
1759 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1761 unsigned numCerts
= certGroup
.numCerts();
1762 const iSignCertInfo
*isCertInfo
;
1764 // const CE_BasicConstraints *bc; // currently unused
1765 CE_ExtendedKeyUsage
*eku
;
1766 CSSM_RETURN crtn
= CSSM_OK
;
1769 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
)) {
1770 tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts
);
1771 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1773 else if(numCerts
< 3) {
1774 /* this error allowed, but no intermediate...check leaf */
1779 /* verify intermediate cert */
1780 isCertInfo
= &certInfo
[1];
1781 tpCert
= certGroup
.certAtIndex(1);
1783 if(!isCertInfo
->basicConstraints
.present
) {
1784 tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
1785 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
)) {
1786 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1790 /* ExtendedKeyUse required, one legal value */
1791 if(!isCertInfo
->extendKeyUsage
.present
) {
1792 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
1793 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1794 return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1801 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1802 assert(eku
!= NULL
);
1803 if(eku
->numPurposes
!= 1) {
1804 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
1805 (unsigned long)eku
->numPurposes
);
1806 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1807 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1809 else if(eku
->numPurposes
== 0) {
1810 /* ignore that error but no EKU - skip EKU check */
1813 /* else ignore error and we have an intermediate EKU; proceed */
1816 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1817 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
1818 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1819 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1825 /* verify leaf cert */
1826 isCertInfo
= &certInfo
[0];
1827 tpCert
= certGroup
.certAtIndex(0);
1828 if(!isCertInfo
->extendKeyUsage
.present
) {
1829 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
1830 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1831 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1834 /* have to skip remainder */
1839 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1840 assert(eku
!= NULL
);
1841 if(eku
->numPurposes
!= 1) {
1842 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
1843 (unsigned long)eku
->numPurposes
);
1844 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1845 if(crtn
== CSSM_OK
) {
1846 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1851 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1852 if(tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV
)) {
1853 tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
1854 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
)) {
1855 if(crtn
== CSSM_OK
) {
1856 crtn
= CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
;
1861 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
1862 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1863 if(crtn
== CSSM_OK
) {
1864 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1874 * Verify Apple Resource Signing options.
1876 * -- leaf cert must have CSSMOID_APPLE_EKU_RESOURCE_SIGNING EKU
1877 * -- chain length must be >= 2
1878 * -- mainline code already verified that leaf KeyUsage = digitalSignature (only)
1880 static CSSM_RETURN
tp_verifyResourceSigningOpts(
1881 TPCertGroup
&certGroup
,
1882 const CSSM_DATA
*fieldOpts
, // currently unused
1883 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1885 unsigned numCerts
= certGroup
.numCerts();
1887 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
)) {
1888 tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts
);
1889 return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
;
1892 const iSignCertInfo
&leafCert
= certInfo
[0];
1893 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1895 /* leaf ExtendedKeyUse required, one legal value */
1896 if(!tpVerifyEKU(leafCert
, CSSMOID_APPLE_EKU_RESOURCE_SIGNING
, false)) {
1897 tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
1898 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1899 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1907 * Common code for Apple Code Signing and Apple Package Signing.
1908 * For now we just require an RFC3280-style CodeSigning EKU in the leaf
1909 * for both policies.
1911 static CSSM_RETURN
tp_verifyCodePkgSignOpts(
1913 TPCertGroup
&certGroup
,
1914 const CSSM_DATA
*fieldOpts
, // currently unused
1915 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1917 const iSignCertInfo
&leafCert
= certInfo
[0];
1919 /* leaf ExtendedKeyUse required, one legal value */
1920 if(!tpVerifyEKU(leafCert
, CSSMOID_ExtendedUseCodeSigning
, false)) {
1921 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1922 tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
1923 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1924 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1933 * Verify MacAppStore receipt verification policy options.
1935 * -- Must have one intermediate cert
1936 * -- intermediate must be the FairPlay intermediate
1937 * -- leaf cert has the CSSMOID_APPLE_EXTENSION_MACAPPSTORE_RECEIPT marker extension
1939 static CSSM_RETURN
tp_verifyMacAppStoreReceiptOpts(
1940 TPCertGroup
&certGroup
,
1941 const CSSM_DATA
*fieldOpts
, // currently unused
1942 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1944 unsigned numCerts
= certGroup
.numCerts();
1947 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
1949 tpPolicyError("tp_verifyMacAppStoreReceiptOpts: numCerts %u", numCerts
);
1950 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1954 const iSignCertInfo
*isCertInfo
;
1957 /* verify intermediate cert */
1958 isCertInfo
= &certInfo
[1];
1959 tpCert
= certGroup
.certAtIndex(1);
1961 if (!isCertInfo
->basicConstraints
.present
)
1963 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1964 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
1965 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1968 // Now check the leaf
1969 isCertInfo
= &certInfo
[0];
1970 tpCert
= certGroup
.certAtIndex(0);
1971 if (certInfo
->certificatePolicies
.present
)
1973 // syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: found certificatePolicies");
1974 const CE_CertPolicies
*certPolicies
=
1975 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
1976 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY
))
1977 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1978 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1982 // syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present"); // DEBUG
1983 tpPolicyError("tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present in leaf");
1984 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1985 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1991 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
)
1993 // returns true if the given OID is present in the cert policies
1995 if (!certPolicies
|| !oidToFind
)
1998 const uint32 maxIndex
= 100; // sanity check
1999 for (uint32 policyIndex
= 0; policyIndex
< certPolicies
->numPolicies
&& policyIndex
< maxIndex
; policyIndex
++)
2001 CE_PolicyInformation
*certPolicyInfo
= &certPolicies
->policies
[policyIndex
];
2002 CSSM_OID_PTR oid
= &certPolicyInfo
->certPolicyId
;
2003 if (oid
&& tpCompareOids(oid
, oidToFind
)) // found it
2012 * Verify Apple ID Sharing options.
2014 * -- Do basic cert validation (OCSP-based certs)
2015 * -- Validate that the cert is an Apple ID sharing cert:
2016 * has a custom extension: OID: Apple ID Sharing Certificate ( 1 2 840 113635 100 4 7 )
2017 * (CSSMOID_APPLE_EXTENSION_APPLEID_SHARING)
2018 * EKU should have both client and server authentication
2019 * chains to the "Apple Application Integration Certification Authority" intermediate
2020 * -- optionally has a client-specified common name, which is the Apple ID account's UUID.
2022 * -- Must have one intermediate cert ("Apple Application Integration Certification Authority")
2023 * -- intermediate must have basic constraints with path length 0
2024 * -- intermediate has CSSMOID_APPLE_EXTENSION_AAI_INTERMEDIATE extension (OID 1 2 840 113635 100 6 2 3)
2025 OR APPLE_EXTENSION_AAI_INTERMEDIATE_2
2028 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
2029 const CSSM_DATA
*fieldOpts
, // optional Common Name
2030 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2032 unsigned numCerts
= certGroup
.numCerts();
2033 const iSignCertInfo
*isCertInfo
;
2035 // const CE_BasicConstraints *bc; // currently unused
2036 CE_ExtendedKeyUsage
*eku
;
2037 CSSM_RETURN crtn
= CSSM_OK
;
2038 unsigned int serverNameLen
= 0;
2039 const char *serverName
= NULL
;
2041 // The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
2042 if (fieldOpts
&& fieldOpts
->Data
)
2044 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)fieldOpts
->Data
;
2045 switch (sslOpts
->Version
)
2047 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
2048 if (fieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
))
2049 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2051 /* handle backwards compatibility here if necessary */
2053 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2055 serverNameLen
= sslOpts
->ServerNameLen
;
2056 serverName
= sslOpts
->ServerName
;
2059 //------------------------------------------------------------------------
2063 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
2065 tpPolicyError("tp_verifyAppleIDSharingOpts: numCerts %u", numCerts
);
2066 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2071 /* this error allowed, but no intermediate...check leaf */
2076 /* verify intermediate cert */
2077 isCertInfo
= &certInfo
[1];
2078 tpCert
= certGroup
.certAtIndex(1);
2080 if (!isCertInfo
->basicConstraints
.present
)
2082 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
2083 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
2084 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
2089 /* verify leaf cert */
2090 isCertInfo
= &certInfo
[0];
2091 tpCert
= certGroup
.certAtIndex(0);
2093 /* host name check is optional */
2094 if (serverNameLen
!= 0)
2096 if (serverName
== NULL
)
2097 return CSSMERR_TP_INVALID_POINTER
;
2099 /* convert caller's hostname string to lower case */
2100 char *hostName
= (char *)certGroup
.alloc().malloc(serverNameLen
);
2101 memmove(hostName
, serverName
, serverNameLen
);
2102 tpToLower(hostName
, serverNameLen
);
2104 /* Check common name... */
2107 CSSM_BOOL match
= tpCompareSubjectName(*tpCert
, SN_CommonName
, false, hostName
,
2108 serverNameLen
, fieldFound
);
2110 certGroup
.alloc().free(hostName
);
2111 if (!match
&& tpCert
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
))
2112 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
2115 if (certInfo
->certificatePolicies
.present
)
2117 const CE_CertPolicies
*certPolicies
=
2118 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
2119 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_APPLEID_SHARING_CERT_POLICY
))
2120 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2121 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2124 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2125 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2127 if (!isCertInfo
->extendKeyUsage
.present
)
2129 tpPolicyError("tp_verifyAppleIDSharingOpts: no extendedKeyUse in leaf");
2130 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
))
2131 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
2133 /* have to skip remainder */
2137 // Check that certificate can do Client and Server Authentication (EKU)
2138 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2139 assert(eku
!= NULL
);
2140 if(eku
->numPurposes
!= 2)
2142 tpPolicyError("tp_verifyAppleIDSharingOpts: bad eku->numPurposes (%lu)",
2143 (unsigned long)eku
->numPurposes
);
2144 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
2146 if (crtn
== CSSM_OK
)
2147 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2151 bool canDoClientAuth
= false, canDoServerAuth
= false, ekuError
= false;
2152 for (int ix
=0;ix
<2;ix
++)
2154 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ClientAuth
))
2155 canDoClientAuth
= true;
2157 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ServerAuth
))
2158 canDoServerAuth
= true;
2166 if (!(canDoClientAuth
&& canDoServerAuth
))
2170 tpPolicyError("tp_verifyAppleIDSharingOpts: bad EKU in leaf");
2171 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
2173 if (crtn
== CSSM_OK
)
2174 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2182 * Verify Time Stamping (RFC3161) policy options.
2184 * -- Leaf must contain Extended Key Usage (EKU), marked critical
2185 * -- The EKU must contain the id-kp-timeStamping purpose and no other
2187 static CSSM_RETURN
tp_verifyTimeStampingOpts(TPCertGroup
&certGroup
,
2188 const CSSM_DATA
*fieldOpts
, // currently unused
2189 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2191 //unsigned numCerts = certGroup.numCerts();
2192 const iSignCertInfo
*isCertInfo
;
2194 CE_ExtendedKeyUsage
*eku
;
2196 isCertInfo
= &certInfo
[0];
2197 tpCert
= certGroup
.certAtIndex(0);
2199 if (!isCertInfo
->extendKeyUsage
.present
)
2201 tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
2202 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2203 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2206 if(!isCertInfo
->extendKeyUsage
.critical
)
2208 tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
2209 tpCert
->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
);
2210 return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
;
2213 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2214 assert(eku
!= NULL
);
2216 if(eku
->numPurposes
!= 1)
2218 tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
2219 (unsigned long)eku
->numPurposes
);
2220 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2221 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2224 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_TimeStamping
))
2226 tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
2227 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2228 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2235 * Verify Passbook Signing policy options.
2237 * -- Do basic cert validation (OCSP-based certs)
2238 * -- Chains to the Apple root CA
2239 * -- Has custom marker extension (1.2.840.113635.100.6.1.16)
2240 * (CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING)
2241 * -- EKU contains Passbook Signing purpose (1.2.840.113635.100.4.14)
2242 * (CSSMOID_APPLE_EKU_PASSBOOK_SIGNING)
2243 * -- UID field of Subject must contain provided card signer string
2244 * -- OU field of Subject must contain provided team identifier string
2246 static CSSM_RETURN
tp_verifyPassbookSigningOpts(TPCertGroup
&certGroup
,
2247 const CSSM_DATA
*fieldOpts
,
2248 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2250 unsigned numCerts
= certGroup
.numCerts();
2251 const iSignCertInfo
*isCertInfo
;
2253 CE_ExtendedKeyUsage
*eku
;
2254 CSSM_RETURN crtn
= CSSM_OK
;
2255 unsigned int nameLen
= 0;
2256 const char *name
= NULL
;
2257 char *p
, *signerName
= NULL
, *teamIdentifier
= NULL
;
2260 isCertInfo
= &certInfo
[0];
2261 tpCert
= certGroup
.certAtIndex(0);
2263 /* The CSSM_APPLE_TP_SMIME_OPTIONS pointer is required. */
2264 if (!fieldOpts
|| !fieldOpts
->Data
)
2265 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2267 CSSM_APPLE_TP_SMIME_OPTIONS
*opts
= (CSSM_APPLE_TP_SMIME_OPTIONS
*)fieldOpts
->Data
;
2268 switch (opts
->Version
)
2270 case CSSM_APPLE_TP_SMIME_OPTS_VERSION
:
2271 if (fieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
))
2272 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2274 /* handle backwards compatibility here if necessary */
2276 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2278 nameLen
= opts
->SenderEmailLen
;
2279 name
= opts
->SenderEmail
;
2280 if (!name
|| !nameLen
)
2281 return CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2284 /* Split the provided name into signer name and team identifier
2285 * (allocates memory, which must be freed at end) */
2286 signerName
= (char *)certGroup
.alloc().malloc(nameLen
);
2287 teamIdentifier
= (char *)certGroup
.alloc().malloc(nameLen
);
2288 memmove(signerName
, name
, nameLen
);
2289 teamIdentifier
[0] = '\0';
2290 if ((p
= strchr(signerName
, '\t')) != NULL
) {
2292 memmove(teamIdentifier
, p
, strlen(p
)+1);
2295 /* Check signer name in UID field */
2296 if (CSSM_FALSE
== tpCompareSubjectName(*tpCert
,
2297 SN_UserID
, false, signerName
, (unsigned int)strlen(signerName
), found
)) {
2298 tpPolicyError("tp_verifyPassbookSigningOpts: signer name not in subject UID field");
2299 tpCert
->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING
);
2300 crtn
= CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2304 /* Check team identifier in OU field */
2305 if (CSSM_FALSE
== tpCompareSubjectName(*tpCert
,
2306 SN_OrgUnit
, false, teamIdentifier
, (unsigned int)strlen(teamIdentifier
), found
)) {
2307 tpPolicyError("tp_verifyPassbookSigningOpts: team identifier not in subject OU field");
2308 tpCert
->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING
);
2309 crtn
= CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2313 /* Check that EKU extension is present */
2314 if (!isCertInfo
->extendKeyUsage
.present
) {
2315 tpPolicyError("tp_verifyPassbookSigningOpts: no extendedKeyUse in leaf");
2316 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2317 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2321 /* Check that EKU contains Passbook Signing purpose */
2322 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2323 assert(eku
!= NULL
);
2325 for (int ix
=0;ix
<eku
->numPurposes
;ix
++) {
2326 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_APPLE_EKU_PASSBOOK_SIGNING
)) {
2332 tpPolicyError("tp_verifyPassbookSigningOpts: Passbook Signing purpose not found");
2333 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2334 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2338 /* Check that Passbook Signing marker extension is present */
2339 if (!(isCertInfo
->foundPassbookSigning
== CSSM_TRUE
)) {
2340 tpPolicyError("tp_verifyPassbookSigningOpts: no Passbook Signing extension in leaf");
2341 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2342 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2346 /* Check that cert chain is anchored by the Apple Root CA */
2348 tpPolicyError("tp_verifyPassbookSigningOpts: numCerts %u", numCerts
);
2349 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2353 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2354 const CSSM_DATA
*certData
= tpCert
->itemData();
2355 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2356 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2357 if (memcmp(digest
, kAppleCASHA1
, sizeof(digest
))) {
2358 tpPolicyError("tp_verifyPassbookSigningOpts: invalid anchor for policy");
2359 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2360 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2367 certGroup
.alloc().free(signerName
);
2369 certGroup
.alloc().free(teamIdentifier
);
2375 * Verify Mobile Store policy options.
2377 * -- Do basic cert validation.
2378 * -- Chain length must be exactly 3.
2379 * -- Must chain to known Mobile Store root.
2380 * -- Intermediate must have CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE marker
2381 * (1.2.840.113635.100.6.2.10)
2382 * -- Key usage in leaf certificate must be Digital Signature.
2383 * -- Leaf has certificatePolicies extension with appropriate policy:
2384 * (1.2.840.113635.100.5.12) if testPolicy is false
2385 * (1.2.840.113635.100.5.12.1) if testPolicy is true
2387 static CSSM_RETURN
tp_verifyMobileStoreSigningOpts(TPCertGroup
&certGroup
,
2388 const CSSM_DATA
*fieldOpts
,
2389 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2392 unsigned numCerts
= certGroup
.numCerts();
2393 const iSignCertInfo
*isCertInfo
;
2396 CSSM_RETURN crtn
= CSSM_OK
;
2398 isCertInfo
= &certInfo
[0];
2399 tpCert
= certGroup
.certAtIndex(0);
2401 /* Check that KU extension is present */
2402 if (!isCertInfo
->keyUsage
.present
) {
2403 tpPolicyError("tp_verifyMobileStoreSigningOpts: no keyUsage in leaf");
2404 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2405 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2409 /* Check that KU contains Digital Signature usage */
2410 ku
= isCertInfo
->keyUsage
.extnData
->keyUsage
;
2411 if (!(ku
& CE_KU_DigitalSignature
)) {
2412 tpPolicyError("tp_verifyMobileStoreSigningOpts: DigitalSignature usage not found");
2413 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
2414 crtn
= CSSMERR_APPLETP_INVALID_KEY_USAGE
;
2418 /* Check that Mobile Store Signing certicate policy is present in leaf */
2419 if (isCertInfo
->certificatePolicies
.present
)
2421 const CE_CertPolicies
*certPolicies
=
2422 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
2423 const CSSM_OID
*policyOID
= (testPolicy
) ?
2424 &CSSMOID_TEST_MOBILE_STORE_SIGNING_POLICY
:
2425 &CSSMOID_MOBILE_STORE_SIGNING_POLICY
;
2426 if (!certificatePoliciesContainsOID(certPolicies
, policyOID
))
2427 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2428 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2432 tpPolicyError("tp_verifyMobileStoreSigningOpts: no certificatePolicies present in leaf");
2433 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2434 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2437 /* Check that cert chain length is 3 */
2438 if (numCerts
!= 3) {
2439 tpPolicyError("tp_verifyMobileStoreSigningOpts: numCerts %u", numCerts
);
2440 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2444 /* Check that cert chain is anchored by a known root */
2446 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2447 const CSSM_DATA
*certData
= tpCert
->itemData();
2448 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2449 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2450 if (memcmp(digest
, kMobileRootSHA1
, sizeof(digest
))) {
2451 tpPolicyError("tp_verifyMobileStoreSigningOpts: invalid anchor for policy");
2452 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2453 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2458 /* Check that Apple System Integration 2 marker extension is present in intermediate */
2459 isCertInfo
= &certInfo
[1];
2460 tpCert
= certGroup
.certAtIndex(1);
2461 if (!(isCertInfo
->foundAppleSysInt2Marker
== CSSM_TRUE
)) {
2462 tpPolicyError("tp_verifyMobileStoreSigningOpts: intermediate marker extension not found");
2463 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2464 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2473 * Verify Escrow Service policy options.
2475 * -- Chain length must be exactly 2.
2476 * -- Must be issued by known escrow root.
2477 * -- Key usage in leaf certificate must be Key Encipherment.
2478 * -- Leaf has CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE_MARKER extension
2479 * (1.2.840.113635.100.6.23.1)
2481 static CSSM_RETURN
tp_verifyEscrowServiceCommon(TPCertGroup
&certGroup
,
2482 const CSSM_DATA
*fieldOpts
,
2483 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2484 SecCertificateEscrowRootType rootType
)
2486 unsigned numCerts
= certGroup
.numCerts();
2487 const iSignCertInfo
*isCertInfo
;
2490 CSSM_RETURN crtn
= CSSM_OK
;
2492 isCertInfo
= &certInfo
[0];
2493 tpCert
= certGroup
.certAtIndex(0);
2495 /* Check that KU extension is present */
2496 if (!isCertInfo
->keyUsage
.present
) {
2497 tpPolicyError("tp_verifyEscrowServiceCommon: no keyUsage in leaf");
2498 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2499 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2503 /* Check that KU contains Key Encipherment usage */
2504 ku
= isCertInfo
->keyUsage
.extnData
->keyUsage
;
2505 if (!(ku
& CE_KU_KeyEncipherment
)) {
2506 tpPolicyError("tp_verifyEscrowServiceCommon: KeyEncipherment usage not found");
2507 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
2508 crtn
= CSSMERR_APPLETP_INVALID_KEY_USAGE
;
2512 /* Check that Escrow Service marker extension is present */
2513 if (!(isCertInfo
->foundEscrowServiceMarker
== CSSM_TRUE
)) {
2514 tpPolicyError("tp_verifyEscrowServiceCommon: no Escrow Service extension in leaf");
2515 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2516 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2520 /* Check that cert chain length is 2 */
2521 if (numCerts
!= 2) {
2522 tpPolicyError("tp_verifyEscrowServiceCommon: numCerts %u", numCerts
);
2523 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2527 /* Check that cert chain is anchored by a known root */
2529 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2530 const CSSM_DATA
*certData
= tpCert
->itemData();
2531 bool anchorMatch
= false;
2532 SecCertificateRef anchor
= NULL
;
2533 OSStatus status
= SecCertificateCreateFromData(certData
, CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
, &anchor
);
2535 CFArrayRef anchors
= SecCertificateCopyEscrowRoots(rootType
);
2536 CFIndex idx
, count
= (anchors
) ? CFArrayGetCount(anchors
) : 0;
2537 for (idx
= 0; idx
< count
; idx
++) {
2538 SecCertificateRef cert
= (SecCertificateRef
) CFArrayGetValueAtIndex(anchors
, idx
);
2539 if (cert
&& CFEqual(cert
, anchor
)) {
2551 tpPolicyError("tp_verifyEscrowServiceCommon: invalid anchor for policy");
2552 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2553 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2562 static CSSM_RETURN
tp_verifyEscrowServiceSigningOpts(TPCertGroup
&certGroup
,
2563 const CSSM_DATA
*fieldOpts
,
2564 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2566 return tp_verifyEscrowServiceCommon(certGroup
, fieldOpts
, certInfo
, kSecCertificateProductionEscrowRoot
);
2569 static CSSM_RETURN
tp_verifyPCSEscrowServiceSigningOpts(TPCertGroup
&certGroup
,
2570 const CSSM_DATA
*fieldOpts
,
2571 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2573 return tp_verifyEscrowServiceCommon(certGroup
, fieldOpts
, certInfo
, kSecCertificateProductionPCSEscrowRoot
);
2577 * Verify Configuration Profile Signing policy options.
2579 * -- Do basic cert validation (OCSP-based certs)
2580 * -- Chains to the Apple root CA
2581 * -- Leaf has EKU extension with appropriate purpose:
2582 * (1.2.840.113635.100.4.16) if testPolicy is false
2583 * (1.2.840.113635.100.4.17) if testPolicy is true
2585 static CSSM_RETURN
tp_verifyProfileSigningOpts(TPCertGroup
&certGroup
,
2586 const CSSM_DATA
*fieldOpts
,
2587 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2590 unsigned numCerts
= certGroup
.numCerts();
2591 const iSignCertInfo
*isCertInfo
;
2593 CE_ExtendedKeyUsage
*eku
;
2594 CSSM_RETURN crtn
= CSSM_OK
;
2597 isCertInfo
= &certInfo
[0];
2598 tpCert
= certGroup
.certAtIndex(0);
2600 /* Check that EKU extension is present */
2601 if (!isCertInfo
->extendKeyUsage
.present
) {
2602 tpPolicyError("tp_verifyProfileSigningOpts: no extendedKeyUse in leaf");
2603 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2604 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2608 /* Check that EKU contains appropriate Profile Signing purpose */
2609 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2610 assert(eku
!= NULL
);
2612 for (int ix
=0;ix
<eku
->numPurposes
;ix
++) {
2613 if (tpCompareOids(&eku
->purposes
[ix
], (testPolicy
) ?
2614 &CSSMOID_APPLE_EKU_QA_PROFILE_SIGNING
:
2615 &CSSMOID_APPLE_EKU_PROFILE_SIGNING
)) {
2621 tpPolicyError("tp_verifyProfileSigningOpts: Profile Signing purpose not found");
2622 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2623 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2627 /* Check that cert chain is anchored by the Apple Root CA */
2629 tpPolicyError("tp_verifyProfileSigningOpts: numCerts %u", numCerts
);
2630 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2634 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2635 const CSSM_DATA
*certData
= tpCert
->itemData();
2636 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2637 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2638 if (memcmp(digest
, kAppleCASHA1
, sizeof(digest
))) {
2639 tpPolicyError("tp_verifyProfileSigningOpts: invalid anchor for policy");
2640 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2641 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2651 * RFC2459 says basicConstraints must be flagged critical for
2652 * CA certs, but Verisign doesn't work that way.
2654 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
2657 * TP iSign spec says Extended Key Usage required for leaf certs,
2658 * but Verisign doesn't work that way.
2660 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
2663 * TP iSign spec says Subject Alternate Name required for leaf certs,
2664 * but Verisign doesn't work that way.
2666 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
2669 * TP iSign spec originally required KeyUsage for all certs, but
2670 * Verisign doesn't have that in their roots.
2672 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
2675 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
2676 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
2677 * but Thawte's intermediate cert (common name "Thawte Personal
2678 * Freemail Issuing CA") does not meet this requirement.
2680 #define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
2683 * Public routine to perform TP verification on a constructed
2685 * Returns CSSM_OK on success.
2686 * Assumes the chain has passed basic subject/issuer verification. First cert of
2687 * incoming certGroup is end-entity (leaf).
2689 * Per-policy details:
2690 * iSign: Assumes that last cert in incoming certGroup is a root cert.
2691 * Also assumes a cert group of more than one cert.
2692 * kTPx509Basic: CertGroup of length one allowed.
2694 CSSM_RETURN
tp_policyVerify(
2697 CSSM_CL_HANDLE clHand
,
2698 CSSM_CSP_HANDLE cspHand
,
2699 TPCertGroup
*certGroup
,
2700 CSSM_BOOL verifiedToRoot
, // last cert is good root
2701 CSSM_BOOL verifiedViaTrustSetting
, // last cert verified via
2703 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
2704 const CSSM_DATA
*policyFieldData
, // optional
2705 void *policyOpts
) // future options
2707 iSignCertInfo
*certInfo
= NULL
;
2709 iSignCertInfo
*thisCertInfo
;
2713 CSSM_BOOL cA
= CSSM_FALSE
; // init for compiler warning
2714 bool isLeaf
; // end entity
2715 bool isRoot
; // root cert
2716 CE_ExtendedKeyUsage
*extendUsage
;
2717 CE_AuthorityKeyID
*authorityId
;
2718 CSSM_KEY_PTR pubKey
;
2719 CSSM_RETURN outErr
= CSSM_OK
; // for gross, non-policy errors
2720 CSSM_BOOL policyFail
= CSSM_FALSE
;// generic CSSMERR_TP_VERIFY_ACTION_FAILED
2721 CSSM_RETURN policyError
= CSSM_OK
; // policy-specific failure
2723 /* First, kTPDefault is a nop here */
2724 if(policy
== kTPDefault
) {
2728 if(certGroup
== NULL
) {
2729 return CSSMERR_TP_INVALID_CERTGROUP
;
2731 numCerts
= certGroup
->numCerts();
2733 return CSSMERR_TP_INVALID_CERTGROUP
;
2735 if(policy
== kTPiSign
) {
2736 if(!verifiedToRoot
) {
2737 /* no way, this requires a root cert */
2738 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2741 /* nope, not for iSign */
2742 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2746 /* cook up an iSignCertInfo array */
2747 certInfo
= (iSignCertInfo
*)tpCalloc(alloc
, numCerts
, sizeof(iSignCertInfo
));
2748 /* subsequent errors to errOut: */
2750 /* fill it with interesting info from parsed certs */
2751 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2752 if(iSignGetCertInfo(alloc
,
2753 certGroup
->certAtIndex(certDex
),
2754 &certInfo
[certDex
])) {
2755 (certGroup
->certAtIndex(certDex
))->addStatusCode(
2756 CSSMERR_TP_INVALID_CERTIFICATE
);
2757 /* this one is fatal (and can't ignore) */
2758 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
2764 * OK, the heart of TP enforcement.
2766 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2767 thisCertInfo
= &certInfo
[certDex
];
2768 TPCertInfo
*thisTpCertInfo
= certGroup
->certAtIndex(certDex
);
2771 * First check for presence of required extensions and
2772 * critical extensions we don't understand.
2774 if(thisCertInfo
->foundUnknownCritical
) {
2775 /* illegal for all policies */
2776 tpPolicyError("tp_policyVerify: critical flag in unknown extension");
2777 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
)) {
2778 policyFail
= CSSM_TRUE
;
2783 * Check for unsupported key length, per <rdar://6892837>
2785 if((pubKey
=thisTpCertInfo
->pubKey()) != NULL
) {
2786 CSSM_KEYHEADER
*keyHdr
= &pubKey
->KeyHeader
;
2787 if(keyHdr
->AlgorithmId
== CSSM_ALGID_RSA
&& keyHdr
->LogicalKeySizeInBits
< 1024) {
2788 tpPolicyError("tp_policyVerify: RSA key size too small");
2789 if(thisTpCertInfo
->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
)) {
2790 policyFail
= CSSM_TRUE
;
2796 * Note it's possible for both of these to be true, for a chain
2797 * of length one (kTPx509Basic, kCrlPolicy only!)
2798 * FIXME: should this code work if the last cert in the chain is NOT a root?
2800 isLeaf
= thisTpCertInfo
->isLeaf();
2801 isRoot
= thisTpCertInfo
->isSelfSigned(true);
2804 * BasicConstraints.cA
2805 * iSign: required in all but leaf and root,
2806 * for which it is optional (with default values of false
2807 * for leaf and true for root).
2808 * all others: always optional, default of false for leaf and
2810 * All: cA must be false for leaf, true for others
2812 if(!thisCertInfo
->basicConstraints
.present
) {
2814 * No basicConstraints present; infer a cA value if appropriate.
2817 /* cool, use default; note that kTPx509Basic with
2818 * certGroup length of one may take this case */
2822 /* cool, use default */
2829 * not present, not leaf, not root....
2830 * ....RFC2459 says this can not be a CA
2835 /* required for iSign in this position */
2836 tpPolicyError("tp_policyVerify: no "
2837 "basicConstraints");
2838 if(thisTpCertInfo
->addStatusCode(
2839 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS
)) {
2840 policyFail
= CSSM_TRUE
;
2845 } /* inferred a default value */
2847 /* basicConstraints present */
2848 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
2849 /* disabled for verisign compatibility */
2850 if(!thisCertInfo
->basicConstraints
.critical
) {
2852 tpPolicyError("tp_policyVerify: basicConstraints marked "
2854 if(thisTpCertInfo
->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED
)) {
2855 policyFail
= CSSM_TRUE
;
2858 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
2860 const CE_BasicConstraints
*bcp
=
2861 &thisCertInfo
->basicConstraints
.extnData
->basicConstraints
;
2865 /* Verify pathLenConstraint if present */
2866 if(!isLeaf
&& // leaf, certDex=0, don't care
2867 cA
&& // p.l.c. only valid for CAs
2868 bcp
->pathLenConstraintPresent
) { // present?
2870 * pathLenConstraint=0 legal for certDex 1 only
2871 * pathLenConstraint=1 legal for certDex {1,2}
2874 if(certDex
> (bcp
->pathLenConstraint
+ 1)) {
2875 tpPolicyError("tp_policyVerify: pathLenConstraint "
2877 if(thisTpCertInfo
->addStatusCode(
2878 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT
)) {
2879 policyFail
= CSSM_TRUE
;
2887 * Special cases to allow a chain of length 1, leaf and root
2888 * both true, and for caller to override the "leaf can't be a CA"
2889 * requirement when a CA cert is explicitly being evaluated as the
2893 !(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
)) {
2894 tpPolicyError("tp_policyVerify: cA true for leaf");
2895 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2896 policyFail
= CSSM_TRUE
;
2900 tpPolicyError("tp_policyVerify: cA false for non-leaf");
2901 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2902 policyFail
= CSSM_TRUE
;
2907 * Authority Key Identifier optional
2908 * iSign : only allowed in !root.
2909 * If present, must not be critical.
2910 * all others : ignored (though used later for chain verification)
2912 if((policy
== kTPiSign
) && thisCertInfo
->authorityId
.present
) {
2914 tpPolicyError("tp_policyVerify: authorityId in root");
2915 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
2916 policyFail
= CSSM_TRUE
;
2919 if(thisCertInfo
->authorityId
.critical
) {
2920 /* illegal per RFC 2459 */
2921 tpPolicyError("tp_policyVerify: authorityId marked "
2923 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
2924 policyFail
= CSSM_TRUE
;
2930 * Subject Key Identifier optional
2931 * iSign : can't be critical.
2932 * all others : ignored (though used later for chain verification)
2934 if(thisCertInfo
->subjectId
.present
) {
2935 if((policy
== kTPiSign
) && thisCertInfo
->subjectId
.critical
) {
2936 tpPolicyError("tp_policyVerify: subjectId marked critical");
2937 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID
)) {
2938 policyFail
= CSSM_TRUE
;
2944 * Key Usage optional except required as noted
2945 * iSign : required for non-root/non-leaf
2946 * Leaf cert : if present, usage = digitalSignature
2947 * Exception : if leaf, and keyUsage not present,
2948 * netscape-cert-type must be present, with
2949 * Object Signing bit set
2950 * kCrlPolicy : Leaf: usage = CRLSign
2951 * kTP_SMIME : if present, must be critical
2952 * kTP_SWUpdateSign, kTP_ResourceSign, kTP_CodeSigning, kTP_PackageSigning : Leaf :
2953 usage = digitalSignature
2954 * all others : non-leaf : usage = keyCertSign
2957 if(thisCertInfo
->keyUsage
.present
) {
2960 * iSign and *Signing: usage = digitalSignature
2961 * all others : don't care
2962 * Others: usage = keyCertSign
2963 * We only require that one bit to be set, we ignore others.
2968 case kTP_SWUpdateSign
:
2969 case kTP_ResourceSign
:
2970 case kTP_CodeSigning
:
2971 case kTP_PackageSigning
:
2972 expUsage
= CE_KU_DigitalSignature
;
2975 /* if present, this bit must be set */
2976 expUsage
= CE_KU_CRLSign
;
2979 /* accept whatever's there */
2980 expUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
2985 /* !leaf: this is true for all policies */
2986 expUsage
= CE_KU_KeyCertSign
;
2988 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
2989 if(!(actUsage
& expUsage
)) {
2990 tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
2992 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
2993 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
2994 policyFail
= CSSM_TRUE
;
3000 * Radar 3523221 renders this whole check obsolete, but I'm leaving
3001 * the code here to document its conspicuous functional absence.
3003 if((policy
== kTP_SMIME
) && !thisCertInfo
->keyUsage
.critical
) {
3005 * Per Radar 3410245, allow this for intermediate certs.
3007 if(SMIME_KEY_USAGE_MUST_BE_CRITICAL
|| isLeaf
|| isRoot
) {
3008 tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
3009 if(thisTpCertInfo
->addStatusCode(
3010 CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL
)) {
3011 policyFail
= CSSM_TRUE
;
3017 else if(policy
== kTPiSign
) {
3019 * iSign requires keyUsage present for non root OR
3020 * netscape-cert-type/ObjectSigning for leaf
3022 if(isLeaf
&& thisCertInfo
->netscapeCertType
.present
) {
3023 CE_NetscapeCertType ct
=
3024 thisCertInfo
->netscapeCertType
.extnData
->netscapeCertType
;
3026 if(!(ct
& CE_NCT_ObjSign
)) {
3027 tpPolicyError("tp_policyVerify: netscape-cert-type, "
3029 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3030 policyFail
= CSSM_TRUE
;
3035 tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
3036 "!(leaf and netscapeCertType)");
3037 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3038 policyFail
= CSSM_TRUE
;
3044 * RFC 3280, 4.1.2.6, says that an empty subject name can only appear in a
3045 * leaf cert, and only if subjectAltName is present and marked critical.
3047 if(isLeaf
&& thisTpCertInfo
->hasEmptySubjectName()) {
3048 bool badEmptySubject
= false;
3049 if(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) {
3051 * True when evaluating a CA cert as well as when
3052 * evaluating a CRL's cert chain. Note the odd case of a CRL's
3053 * signer having an empty subject matching an empty issuer
3054 * in the CRL. That'll be caught here.
3056 badEmptySubject
= true;
3058 else if(!thisCertInfo
->subjectAltName
.present
|| /* no subjectAltName */
3059 !thisCertInfo
->subjectAltName
.critical
) { /* not critical */
3060 badEmptySubject
= true;
3062 if(badEmptySubject
) {
3063 tpPolicyError("tp_policyVerify: bad empty subject");
3064 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT
)) {
3065 policyFail
= CSSM_TRUE
;
3071 * RFC 3739: if this cert has a Qualified Cert Statements extension, and
3072 * it's Critical, make sure we understand all of the extension's statementIds.
3074 if(thisCertInfo
->qualCertStatements
.present
&&
3075 thisCertInfo
->qualCertStatements
.critical
) {
3076 CE_QC_Statements
*qcss
=
3077 &thisCertInfo
->qualCertStatements
.extnData
->qualifiedCertStatements
;
3078 uint32 numQcs
= qcss
->numQCStatements
;
3079 for(unsigned qdex
=0; qdex
<numQcs
; qdex
++) {
3080 CSSM_OID_PTR qid
= &qcss
->qcStatements
[qdex
].statementId
;
3082 for(unsigned kdex
=0; kdex
<NUM_KNOWN_QUAL_CERT_STATEMENTS
; kdex
++) {
3083 if(tpCompareCssmData(qid
, knownQualifiedCertStatements
[kdex
])) {
3089 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT
)) {
3090 policyFail
= CSSM_TRUE
;
3095 } /* critical Qualified Cert Statement */
3098 * Certificate Policies extension validation, per section 1.2 of:
3099 * http://iase.disa.mil/pki/dod_cp_v10_final_2_mar_09_signed.pdf
3101 if (tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH
, false) ||
3102 tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH_2048
, false)) {
3104 * Certificate asserts one of the PIV-Auth Certificate Policy OIDs;
3105 * check the required Key Usage extension for compliance.
3108 * usage = digitalSignature (only; no other bits asserted)
3110 * usage = keyCertSign (required; other bits ignored)
3112 if(thisCertInfo
->keyUsage
.present
) {
3113 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
3115 /* No key usage! Policy fail. */
3118 if(!(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) && (certDex
== 0)) {
3119 expUsage
= CE_KU_DigitalSignature
;
3121 expUsage
= actUsage
| CE_KU_KeyCertSign
;
3123 if(!(actUsage
== expUsage
)) {
3124 tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
3126 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
3127 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3128 policyFail
= CSSM_TRUE
;
3131 } /* Certificate Policies */
3134 } /* for certDex, checking presence of extensions */
3137 * Special case checking for leaf (end entity) cert
3139 * iSign only: Extended key usage, optional for leaf,
3140 * value CSSMOID_ExtendedUseCodeSigning
3142 if((policy
== kTPiSign
) && certInfo
[0].extendKeyUsage
.present
) {
3143 extendUsage
= &certInfo
[0].extendKeyUsage
.extnData
->extendedKeyUsage
;
3144 if(extendUsage
->numPurposes
!= 1) {
3145 tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
3147 (int)extendUsage
->numPurposes
);
3148 if((certGroup
->certAtIndex(0))->addStatusCode(
3149 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
3150 policyFail
= CSSM_TRUE
;
3153 if(!tpCompareOids(extendUsage
->purposes
,
3154 &CSSMOID_ExtendedUseCodeSigning
)) {
3155 tpPolicyError("tp_policyVerify: bad extendKeyUsage");
3156 if((certGroup
->certAtIndex(0))->addStatusCode(
3157 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
3158 policyFail
= CSSM_TRUE
;
3164 * Verify authorityId-->subjectId linkage.
3165 * All optional - skip if needed fields not present.
3166 * Also, always skip last (root) cert.
3168 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
3169 if(!certInfo
[certDex
].authorityId
.present
||
3170 !certInfo
[certDex
+1].subjectId
.present
) {
3173 authorityId
= &certInfo
[certDex
].authorityId
.extnData
->authorityKeyID
;
3174 if(!authorityId
->keyIdentifierPresent
) {
3175 /* we only know how to compare keyIdentifier */
3178 if(!tpCompareCssmData(&authorityId
->keyIdentifier
,
3179 &certInfo
[certDex
+1].subjectId
.extnData
->subjectKeyID
)) {
3180 tpPolicyError("tp_policyVerify: bad key ID linkage");
3181 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
3182 CSSMERR_APPLETP_INVALID_ID_LINKAGE
)) {
3183 policyFail
= CSSM_TRUE
;
3189 * Check signature algorithm on all non-root certs,
3190 * reject if known to be untrusted
3192 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
3193 if(certInfo
[certDex
].untrustedSigAlg
) {
3194 tpPolicyError("tp_policyVerify: untrusted signature algorithm");
3195 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
3196 CSSMERR_TP_INVALID_CERTIFICATE
)) {
3197 policyFail
= CSSM_TRUE
;
3202 /* specific per-policy checking */
3208 * SSL, EAP, IPSec: optionally verify common name; all are identical
3209 * other than their names.
3210 * FIXME - should this be before or after the root cert test? How can
3211 * we return both errors?
3213 policyError
= tp_verifySslOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3217 tpDebug("iChat policy");
3220 policyError
= tp_verifySmimeOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3222 case kTP_SWUpdateSign
:
3223 policyError
= tp_verifySWUpdateSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3225 case kTP_ResourceSign
:
3226 policyError
= tp_verifyResourceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3228 case kTP_CodeSigning
:
3229 case kTP_PackageSigning
:
3230 policyError
= tp_verifyCodePkgSignOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3232 case kTP_MacAppStoreRec
:
3233 policyError
= tp_verifyMacAppStoreReceiptOpts(*certGroup
, policyFieldData
, certInfo
);
3235 case kTP_AppleIDSharing
:
3236 policyError
= tp_verifyAppleIDSharingOpts(*certGroup
, policyFieldData
, certInfo
);
3238 case kTP_TimeStamping
:
3239 policyError
= tp_verifyTimeStampingOpts(*certGroup
, policyFieldData
, certInfo
);
3241 case kTP_PassbookSigning
:
3242 policyError
= tp_verifyPassbookSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3244 case kTP_MobileStore
:
3245 policyError
= tp_verifyMobileStoreSigningOpts(*certGroup
, policyFieldData
, certInfo
, false);
3247 case kTP_TestMobileStore
:
3248 policyError
= tp_verifyMobileStoreSigningOpts(*certGroup
, policyFieldData
, certInfo
, true);
3250 case kTP_EscrowService
:
3251 policyError
= tp_verifyEscrowServiceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3253 case kTP_ProfileSigning
:
3254 policyError
= tp_verifyProfileSigningOpts(*certGroup
, policyFieldData
, certInfo
, false);
3256 case kTP_QAProfileSigning
:
3257 policyError
= tp_verifyProfileSigningOpts(*certGroup
, policyFieldData
, certInfo
, true);
3259 case kTP_PCSEscrowService
:
3260 policyError
= tp_verifyPCSEscrowServiceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3265 case kTP_PKINIT_Client
:
3271 if(outErr
== CSSM_OK
) {
3272 /* policy-specific error takes precedence here */
3273 if(policyError
!= CSSM_OK
) {
3274 outErr
= policyError
;
3276 else if(policyFail
) {
3277 /* plain vanilla error return from this module */
3278 outErr
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
3282 /* free resources */
3283 for(certDex
=0; certDex
<numCerts
; certDex
++) {
3284 thisCertInfo
= &certInfo
[certDex
];
3285 iSignFreeCertInfo(clHand
, thisCertInfo
);
3287 tpFree(alloc
, certInfo
);
3292 * Obtain policy-specific User Trust parameters
3294 void tp_policyTrustSettingParams(
3296 const CSSM_DATA
*policyData
, // optional
3297 /* returned values - not mallocd */
3298 const char **policyStr
,
3299 uint32
*policyStrLen
,
3300 SecTrustSettingsKeyUsage
*keyUse
)
3302 /* default values */
3304 *keyUse
= kSecTrustSettingsKeyUseAny
;
3306 if((policyData
== NULL
) || (policyData
->Data
== NULL
)) {
3307 /* currently, no further action possible */
3315 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
3316 /* this error will be caught later */
3319 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
=
3320 (CSSM_APPLE_TP_SSL_OPTIONS
*)policyData
->Data
;
3321 *policyStr
= sslOpts
->ServerName
;
3322 *policyStrLen
= sslOpts
->ServerNameLen
;
3323 if(sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
) {
3325 * Client signs with its priv key. Server end,
3326 * which (also) verifies the client cert, verifies.
3328 *keyUse
= kSecTrustSettingsKeyUseSignature
;
3331 /* server decrypts */
3332 *keyUse
= kSecTrustSettingsKeyUseEnDecryptKey
;
3340 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
3341 /* this error will be caught later */
3344 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
=
3345 (CSSM_APPLE_TP_SMIME_OPTIONS
*)policyData
->Data
;
3346 *policyStr
= smimeOpts
->SenderEmail
;
3347 *policyStrLen
= smimeOpts
->SenderEmailLen
;
3348 SecTrustSettingsKeyUsage ku
= 0;
3349 CE_KeyUsage smimeKu
= smimeOpts
->IntendedUsage
;
3350 if(smimeKu
& (CE_KU_DigitalSignature
| CE_KU_KeyCertSign
| CE_KU_CRLSign
)) {
3351 ku
|= kSecTrustSettingsKeyUseSignature
;
3353 if(smimeKu
& (CE_KU_KeyEncipherment
| CE_KU_DataEncipherment
)) {
3354 ku
|= kSecTrustSettingsKeyUseEnDecryptKey
;
3361 /* no other options */
3366 #pragma clang diagnostic pop