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_PROVISIONING_PROFILE_SIGNING */
79 CSSM_BOOL foundProvisioningProfileSigningMarker
;
80 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING */
81 CSSM_BOOL foundPassbookSigningMarker
;
82 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE */
83 CSSM_BOOL foundAppleSysInt2Marker
;
84 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION */
85 CSSM_BOOL foundAppleServerAuthMarker
;
86 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE */
87 CSSM_BOOL foundEscrowServiceMarker
;
88 /* flag indicating presence of CSSMOID_APPLE_EXTENSION_WWDR_INTERMEDIATE */
89 CSSM_BOOL foundAppleWWDRIntMarker
;
90 /* flag indicating presence of a critical extension we don't understand */
91 CSSM_BOOL foundUnknownCritical
;
92 /* flag indicating that this certificate was signed with a known-broken algorithm */
93 CSSM_BOOL untrustedSigAlg
;
98 * The list of Qualified Cert Statement statementIds we understand, even though
99 * we don't actually do anything with them; if these are found in a Qualified
100 * Cert Statement that's critical, we can truthfully say "yes we understand this".
102 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
104 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
,
105 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
,
106 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
107 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
108 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
109 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
111 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
113 static CSSM_RETURN
tp_verifyMacAppStoreReceiptOpts(TPCertGroup
&certGroup
,
114 const CSSM_DATA
*fieldOpts
, const iSignCertInfo
*certInfo
);
116 static CSSM_RETURN
tp_verifyPassbookSigningOpts(TPCertGroup
&certGroup
,
117 const CSSM_DATA
*fieldOpts
, const iSignCertInfo
*certInfo
);
119 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
);
121 #define kSecPolicySHA1Size 20
122 static const UInt8 kAppleCASHA1
[kSecPolicySHA1Size
] = {
123 0x61, 0x1E, 0x5B, 0x66, 0x2C, 0x59, 0x3A, 0x08, 0xFF, 0x58,
124 0xD1, 0x4A, 0xE2, 0x24, 0x52, 0xD1, 0x98, 0xDF, 0x6C, 0x60
127 static const UInt8 kMobileRootSHA1
[kSecPolicySHA1Size
] = {
128 0xBD, 0xD6, 0x7C, 0x34, 0xD0, 0xB2, 0x68, 0x5D, 0x31, 0x82,
129 0xCD, 0x32, 0xCB, 0xF4, 0x54, 0x69, 0xA1, 0xF1, 0x6B, 0x09
132 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
133 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
134 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
138 * Certificate policy OIDs
142 #define ANY_POLICY_OID OID_EXTENSION, 0x32, 0x00
143 #define ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 2
146 #define INHIBIT_ANY_POLICY_OID OID_EXTENSION, 0x54
147 #define INHIBIT_ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 1
149 /* 2.16.840.1.101.2.1 */
150 #define US_DOD_INFOSEC 0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
151 #define US_DOD_INFOSEC_LEN 7
153 /* 2.16.840.1.101.2.1.11.10 */
154 #define PIV_AUTH_OID US_DOD_INFOSEC, 0x0B, 0x0A
155 #define PIV_AUTH_OID_LEN US_DOD_INFOSEC_LEN + 2
157 /* 2.16.840.1.101.2.1.11.20 */
158 #define PIV_AUTH_2048_OID US_DOD_INFOSEC, 0x0B, 0x14
159 #define PIV_AUTH_2048_OID_LEN US_DOD_INFOSEC_LEN + 2
161 static const uint8 OID_ANY_POLICY
[] = {ANY_POLICY_OID
};
162 const CSSM_OID CSSMOID_ANY_POLICY
= {ANY_POLICY_OID_LEN
, (uint8
*)OID_ANY_POLICY
};
163 static const uint8 OID_INHIBIT_ANY_POLICY
[] = {INHIBIT_ANY_POLICY_OID
};
164 const CSSM_OID CSSMOID_INHIBIT_ANY_POLICY
= {INHIBIT_ANY_POLICY_OID_LEN
, (uint8
*)OID_INHIBIT_ANY_POLICY
};
165 static const uint8 OID_PIV_AUTH
[] = {PIV_AUTH_OID
};
166 const CSSM_OID CSSMOID_PIV_AUTH
= {PIV_AUTH_OID_LEN
, (uint8
*)OID_PIV_AUTH
};
167 static const uint8 OID_PIV_AUTH_2048
[] = {PIV_AUTH_2048_OID
};
168 const CSSM_OID CSSMOID_PIV_AUTH_2048
= {PIV_AUTH_2048_OID_LEN
, (uint8
*)OID_PIV_AUTH_2048
};
170 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
171 const CSSM_DATA
*fieldOpts
, // optional Common Name
172 const iSignCertInfo
*certInfo
);
174 * Setup a single iSignExtenInfo. Called once per known extension
177 static CSSM_RETURN
tpSetupExtension(
180 iSignExtenInfo
*extnInfo
) // which component of certInfo
182 if(extnData
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
183 tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
184 return CSSMERR_TP_UNKNOWN_FORMAT
;
186 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)extnData
->Data
;
187 extnInfo
->present
= CSSM_TRUE
;
188 extnInfo
->critical
= cssmExt
->critical
;
189 extnInfo
->extnData
= (CE_Data
*)cssmExt
->value
.parsedValue
;
190 extnInfo
->valToFree
= extnData
;
195 * Fetch a known extension, set up associated iSignExtenInfo if present.
197 static CSSM_RETURN
iSignFetchExtension(
200 const CSSM_OID
*fieldOid
, // which extension to fetch
201 iSignExtenInfo
*extnInfo
) // where the info goes
203 CSSM_DATA_PTR fieldValue
; // mallocd by CL
206 crtn
= tpCert
->fetchField(fieldOid
, &fieldValue
);
210 case CSSMERR_CL_NO_FIELD_VALUES
:
211 /* field not present, OK */
216 return tpSetupExtension(alloc
,
222 * This function performs a check of an extension marked 'critical'
223 * to see if it's one we understand. Returns CSSM_OK if the extension
224 * is acceptable, CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN if unknown.
226 static CSSM_RETURN
iSignVerifyCriticalExtension(
227 CSSM_X509_EXTENSION
*cssmExt
)
229 if (!cssmExt
|| !cssmExt
->extnId
.Data
)
230 return CSSMERR_TP_INVALID_FIELD_POINTER
;
232 if (!cssmExt
->critical
)
235 /* FIXME: remove when policyConstraints NSS template is fixed */
236 if (!memcmp(cssmExt
->extnId
.Data
, CSSMOID_PolicyConstraints
.Data
, CSSMOID_PolicyConstraints
.Length
))
239 if (cssmExt
->extnId
.Length
> APPLE_EXTENSION_OID_LENGTH
&&
240 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION
.Data
, APPLE_EXTENSION_OID_LENGTH
)) {
241 /* This extension's OID is under the appleCertificateExtensions arc */
245 return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
;
249 * Search for all unknown extensions. If we find one which is flagged critical,
250 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
252 static CSSM_RETURN
iSignSearchUnknownExtensions(
254 iSignCertInfo
*certInfo
)
257 CSSM_DATA_PTR fieldValue
= NULL
;
258 CSSM_HANDLE searchHand
= CSSM_INVALID_HANDLE
;
259 uint32 numFields
= 0;
261 certInfo
->foundProvisioningProfileSigningMarker
= CSSM_FALSE
;
262 certInfo
->foundPassbookSigningMarker
= CSSM_FALSE
;
263 certInfo
->foundAppleSysInt2Marker
= CSSM_FALSE
;
264 certInfo
->foundAppleWWDRIntMarker
= CSSM_FALSE
;
265 certInfo
->foundEscrowServiceMarker
= CSSM_FALSE
;
267 crtn
= CSSM_CL_CertGetFirstCachedFieldValue(tpCert
->clHand(),
269 &CSSMOID_X509V3CertificateExtensionCStruct
,
275 /* found one, proceed */
277 case CSSMERR_CL_NO_FIELD_VALUES
:
278 /* no unknown extensions present, OK */
284 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
285 tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
286 return CSSMERR_TP_UNKNOWN_FORMAT
;
289 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
290 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1 &&
291 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING
.Data
,
292 APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1)) {
293 /* this is the Passbook Signing extension */
294 certInfo
->foundPassbookSigningMarker
= CSSM_TRUE
;
296 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
&&
297 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE
.Data
,
298 APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
)) {
299 /* this is the Apple System Integration 2 Signing extension */
300 certInfo
->foundAppleSysInt2Marker
= CSSM_TRUE
;
302 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
&&
303 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE
.Data
,
304 APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
)) {
305 /* this is the Escrow Service Signing extension */
306 certInfo
->foundEscrowServiceMarker
= CSSM_TRUE
;
308 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH
&&
309 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING
.Data
,
310 APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH
)) {
311 /* this is the Provisioning Profile Signing extension */
312 certInfo
->foundProvisioningProfileSigningMarker
= CSSM_TRUE
;
314 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH
&&
315 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_WWDR_INTERMEDIATE
.Data
,
316 APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH
)) {
317 /* this is the Apple WWDR extension */
318 certInfo
->foundAppleWWDRIntMarker
= CSSM_TRUE
;
321 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
322 /* BRRZAPP! Found an unknown extension marked critical */
323 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
326 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
327 &CSSMOID_X509V3CertificateExtensionCStruct
,
331 /* process remaining unknown extensions */
332 for(unsigned i
=1; i
<numFields
; i
++) {
333 crtn
= CSSM_CL_CertGetNextCachedFieldValue(tpCert
->clHand(),
337 /* should never happen */
338 tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
342 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
343 tpPolicyError("iSignSearchUnknownExtensions: "
344 "malformed CSSM_FIELD");
345 crtn
= CSSMERR_TP_UNKNOWN_FORMAT
;
349 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
350 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1 &&
351 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING
.Data
,
352 APPLE_EXTENSION_CODE_SIGNING_LENGTH
+1)) {
353 /* this is the Passbook Signing extension */
354 certInfo
->foundPassbookSigningMarker
= CSSM_TRUE
;
356 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
&&
357 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE
.Data
,
358 APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH
)) {
359 /* this is the Apple System Integration 2 Signing extension */
360 certInfo
->foundAppleSysInt2Marker
= CSSM_TRUE
;
362 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH
&&
363 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION
.Data
,
364 APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH
)) {
365 /* this is the Apple Server Authentication extension */
366 certInfo
->foundAppleServerAuthMarker
= CSSM_TRUE
;
368 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
&&
369 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE
.Data
,
370 APPLE_EXTENSION_ESCROW_SERVICE_LENGTH
)) {
371 /* this is the Escrow Service Signing extension */
372 certInfo
->foundEscrowServiceMarker
= CSSM_TRUE
;
374 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH
&&
375 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING
.Data
,
376 APPLE_EXTENSION_PROVISIONING_PROFILE_SIGNING_LENGTH
)) {
377 /* this is the Provisioning Profile Signing extension */
378 certInfo
->foundProvisioningProfileSigningMarker
= CSSM_TRUE
;
380 if (cssmExt
->extnId
.Length
== APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH
&&
381 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION_WWDR_INTERMEDIATE
.Data
,
382 APPLE_EXTENSION_WWDR_INTERMEDIATE_LENGTH
)) {
383 /* this is the Apple WWDR extension */
384 certInfo
->foundAppleWWDRIntMarker
= CSSM_TRUE
;
387 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
388 /* BRRZAPP! Found an unknown extension marked critical */
389 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
392 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
393 &CSSMOID_X509V3CertificateExtensionCStruct
,
396 } /* for additional fields */
400 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
401 &CSSMOID_X509V3CertificateExtensionCStruct
,
404 if(searchHand
!= CSSM_INVALID_HANDLE
) {
405 CSSM_CL_CertAbortQuery(tpCert
->clHand(), searchHand
);
411 * Check the signature algorithm. If it's known to be untrusted,
412 * flag certInfo->untrustedSigAlg.
414 static void iSignCheckSignatureAlgorithm(
416 iSignCertInfo
*certInfo
)
418 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
= NULL
;
419 CSSM_DATA_PTR valueToFree
= NULL
;
421 algId
= tp_CertGetAlgId(tpCert
, &valueToFree
);
423 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2
) ||
424 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2WithRSA
) ||
425 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD5
) ||
426 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD5WithRSA
) ) {
427 certInfo
->untrustedSigAlg
= CSSM_TRUE
;
429 certInfo
->untrustedSigAlg
= CSSM_FALSE
;
433 tp_CertFreeAlgId(tpCert
->clHand(), valueToFree
);
438 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
439 * Returns CSSM_FAIL on error.
441 static CSSM_RETURN
iSignGetCertInfo(
444 iSignCertInfo
*certInfo
)
448 /* first grind thru the extensions we're interested in */
449 crtn
= iSignFetchExtension(alloc
,
451 &CSSMOID_AuthorityKeyIdentifier
,
452 &certInfo
->authorityId
);
456 crtn
= iSignFetchExtension(alloc
,
458 &CSSMOID_SubjectKeyIdentifier
,
459 &certInfo
->subjectId
);
463 crtn
= iSignFetchExtension(alloc
,
466 &certInfo
->keyUsage
);
470 crtn
= iSignFetchExtension(alloc
,
472 &CSSMOID_ExtendedKeyUsage
,
473 &certInfo
->extendKeyUsage
);
477 crtn
= iSignFetchExtension(alloc
,
479 &CSSMOID_BasicConstraints
,
480 &certInfo
->basicConstraints
);
484 crtn
= iSignFetchExtension(alloc
,
486 &CSSMOID_NetscapeCertType
,
487 &certInfo
->netscapeCertType
);
491 crtn
= iSignFetchExtension(alloc
,
493 &CSSMOID_SubjectAltName
,
494 &certInfo
->subjectAltName
);
498 crtn
= iSignFetchExtension(alloc
,
500 &CSSMOID_CertificatePolicies
,
501 &certInfo
->certPolicies
);
505 crtn
= iSignFetchExtension(alloc
,
507 &CSSMOID_QC_Statements
,
508 &certInfo
->qualCertStatements
);
512 crtn
= iSignFetchExtension(alloc
,
514 &CSSMOID_NameConstraints
,
515 &certInfo
->nameConstraints
);
519 crtn
= iSignFetchExtension(alloc
,
521 &CSSMOID_PolicyMappings
,
522 &certInfo
->policyMappings
);
526 crtn
= iSignFetchExtension(alloc
,
528 &CSSMOID_PolicyConstraints
,
529 &certInfo
->policyConstraints
);
533 crtn
= iSignFetchExtension(alloc
,
535 &CSSMOID_InhibitAnyPolicy
,
536 &certInfo
->inhibitAnyPolicy
);
540 crtn
= iSignFetchExtension(alloc
,
542 &CSSMOID_CertificatePolicies
,
543 &certInfo
->certificatePolicies
);
548 /* check signature algorithm field */
549 iSignCheckSignatureAlgorithm(tpCert
, certInfo
);
551 /* now look for extensions we don't understand - the only thing we're interested
552 * in is the critical flag. */
553 return iSignSearchUnknownExtensions(tpCert
, certInfo
);
557 * Free (via CL) the fields allocated in iSignGetCertInfo().
559 static void iSignFreeCertInfo(
560 CSSM_CL_HANDLE clHand
,
561 iSignCertInfo
*certInfo
)
563 if(certInfo
->authorityId
.present
) {
564 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_AuthorityKeyIdentifier
,
565 certInfo
->authorityId
.valToFree
);
567 if(certInfo
->subjectId
.present
) {
568 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectKeyIdentifier
,
569 certInfo
->subjectId
.valToFree
);
571 if(certInfo
->keyUsage
.present
) {
572 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_KeyUsage
,
573 certInfo
->keyUsage
.valToFree
);
575 if(certInfo
->extendKeyUsage
.present
) {
576 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_ExtendedKeyUsage
,
577 certInfo
->extendKeyUsage
.valToFree
);
579 if(certInfo
->basicConstraints
.present
) {
580 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_BasicConstraints
,
581 certInfo
->basicConstraints
.valToFree
);
583 if(certInfo
->netscapeCertType
.present
) {
584 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_NetscapeCertType
,
585 certInfo
->netscapeCertType
.valToFree
);
587 if(certInfo
->subjectAltName
.present
) {
588 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectAltName
,
589 certInfo
->subjectAltName
.valToFree
);
591 if(certInfo
->certPolicies
.present
) {
592 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
593 certInfo
->certPolicies
.valToFree
);
595 // if(certInfo->policyConstraints.present) {
596 // CSSM_CL_FreeFieldValue(clHand, &CSSMOID_PolicyConstraints,
597 // certInfo->policyConstraints.valToFree);
599 if(certInfo
->qualCertStatements
.present
) {
600 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_QC_Statements
,
601 certInfo
->qualCertStatements
.valToFree
);
603 if(certInfo
->certificatePolicies
.present
) {
604 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
605 certInfo
->certificatePolicies
.valToFree
);
610 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
611 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
612 * Also indicates whether *any* of the specified fields were found, regardless
616 SN_CommonName
, // CSSMOID_CommonName, host name format
617 SN_Email
, // CSSMOID_EmailAddress
618 SN_UserID
, // CSSMOID_UserID
619 SN_OrgUnit
// CSSMOID_OrganizationalUnitName
620 } SubjSubjNameSearchType
;
622 static CSSM_BOOL
tpCompareSubjectName(
624 SubjSubjNameSearchType searchType
,
625 bool normalizeAll
, // for SN_Email case: lower-case all of
626 // the cert's value, not just the portion
628 const char *callerStr
, // already tpToLower'd
632 char *certName
= NULL
; // from cert's subject name
633 uint32 certNameLen
= 0;
634 CSSM_DATA_PTR subjNameData
= NULL
;
636 CSSM_BOOL ourRtn
= CSSM_FALSE
;
637 const CSSM_OID
*oidSrch
;
639 const char x500_userid_oid
[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
640 CSSM_OID X500_UserID_OID
= { sizeof(x500_userid_oid
), (uint8
*)x500_userid_oid
};
645 oidSrch
= &CSSMOID_CommonName
;
648 oidSrch
= &CSSMOID_EmailAddress
;
651 oidSrch
= &X500_UserID_OID
;
654 oidSrch
= &CSSMOID_OrganizationalUnitName
;
660 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
662 /* should never happen, we shouldn't be here if there is no subject */
663 tpPolicyError("tpCompareSubjectName: error retrieving subject name");
666 CSSM_X509_NAME_PTR x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
667 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
668 tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
669 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
673 /* Now grunge thru the X509 name looking for a common name */
674 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
675 CSSM_X509_RDN_PTR rdnp
;
679 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
680 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
681 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
682 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
683 if(tpCompareOids(&ptvp
->type
, oidSrch
)) {
685 certName
= (char *)ptvp
->value
.Data
;
686 certNameLen
= (uint32
)ptvp
->value
.Length
;
690 /* handle odd encodings that we need to convert to 8-bit */
691 CFStringBuiltInEncodings encoding
;
692 CFDataRef cfd
= NULL
;
693 bool doConvert
= false;
694 switch(ptvp
->valueType
) {
695 case BER_TAG_T61_STRING
:
697 encoding
= kCFStringEncodingISOLatin1
;
700 case BER_TAG_PKIX_BMP_STRING
:
701 encoding
= kCFStringEncodingUnicode
;
705 * All others - either take as is, or let it fail due to
706 * illegal/incomprehensible format
712 /* raw data ==> CFString */
713 cfd
= CFDataCreate(NULL
, (UInt8
*)certName
, certNameLen
);
715 /* try next component */
718 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(
719 NULL
, cfd
, encoding
);
722 tpPolicyError("tpCompareSubjectName: bad str (1)");
726 /* CFString ==> straight ASCII */
727 cfd
= CFStringCreateExternalRepresentation(NULL
,
728 cfStr
, kCFStringEncodingASCII
, 0);
731 tpPolicyError("tpCompareSubjectName: bad str (2)");
734 certNameLen
= (uint32
)CFDataGetLength(cfd
);
735 certName
= (char *)CFDataGetBytePtr(cfd
);
737 ourRtn
= tpCompareHostNames(callerStr
, callerStrLen
,
738 certName
, certNameLen
);
746 ourRtn
= tpCompareEmailAddr(callerStr
, callerStrLen
,
747 certName
, certNameLen
, normalizeAll
);
751 /* exact match only here, for now */
752 ourRtn
= ((callerStrLen
== certNameLen
) &&
753 !memcmp(callerStr
, certName
, certNameLen
)) ?
754 CSSM_TRUE
: CSSM_FALSE
;
761 /* else keep going, maybe there's another common name */
768 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
773 * Compare ASCII form of an IP address to a CSSM_DATA containing
774 * the IP address's numeric components. Returns true on match.
776 static CSSM_BOOL
tpCompIpAddrStr(
779 const CSSM_DATA
*numeric
)
781 const char *cp
= str
;
785 if((numeric
== NULL
) || (numeric
->Length
== 0) || (str
== NULL
)) {
788 if(cp
[strLen
- 1] == '\0') {
789 /* ignore NULL terminator */
792 for(unsigned dex
=0; dex
<numeric
->Length
; dex
++) {
793 /* cp points to start of current string digit */
795 const char *lastChar
= cp
+ strLen
;
797 for( ; nextDot
<lastChar
; nextDot
++) {
798 if(*nextDot
== '.') {
802 if(nextDot
== lastChar
) {
803 /* legal and required on last digit */
804 if(dex
!= (numeric
->Length
- 1)) {
808 else if(dex
== (numeric
->Length
- 1)) {
811 ptrdiff_t digLen
= nextDot
- cp
;
812 if(digLen
>= sizeof(buf
)) {
816 memmove(buf
, cp
, digLen
);
818 /* incr digLen to include the next dot */
822 int digVal
= atoi(buf
);
823 if(digVal
!= numeric
->Data
[dex
]) {
831 * See if cert's subjectAltName contains an element matching caller-specified
832 * string, hostname, in the following forms:
834 * SAN_HostName : dnsName, iPAddress
835 * SAN_Email : RFC822Name
837 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
839 * Also indicates whether or not a dnsName (search type HostName) or
840 * RFC822Name (search type SAM_Email) was found, regardless of result
843 * The appStr/appStrLen args are optional - if NULL/0, only the
844 * search for dnsName/RFC822Name is done.
849 } SubjAltNameSearchType
;
851 static CSSM_BOOL
tpCompareSubjectAltName(
852 const iSignExtenInfo
&subjAltNameInfo
,
853 const char *appStr
, // caller has lower-cased as appropriate
855 SubjAltNameSearchType searchType
,
856 bool normalizeAll
, // for SAN_Email case: lower-case all of
857 // the cert's value, not just the portion
859 bool &dnsNameFound
, // RETURNED, SAN_HostName case
860 bool &emailFound
) // RETURNED, SAN_Email case
862 dnsNameFound
= false;
864 if(!subjAltNameInfo
.present
) {
865 /* common failure, no subjectAltName found */
869 CE_GeneralNames
*names
= &subjAltNameInfo
.extnData
->subjectAltName
;
870 CSSM_BOOL ourRtn
= CSSM_FALSE
;
874 /* Search thru the CE_GeneralNames looking for the appropriate attribute */
875 for(unsigned dex
=0; dex
<names
->numNames
; dex
++) {
876 CE_GeneralName
*name
= &names
->generalName
[dex
];
879 switch(name
->nameType
) {
882 /* nothing to do here */
885 ourRtn
= tpCompIpAddrStr(appStr
, appStrLen
, &name
->name
);
889 if(name
->berEncoded
) {
890 tpErrorLog("tpCompareSubjectAltName: malformed "
891 "CE_GeneralName (1)\n");
894 certName
= (char *)name
->name
.Data
;
895 if(certName
== NULL
) {
896 tpErrorLog("tpCompareSubjectAltName: malformed "
897 "CE_GeneralName (2)\n");
900 certNameLen
= (uint32
)(name
->name
.Length
);
903 /* skip if caller passed in NULL */
904 ourRtn
= tpCompareHostNames(appStr
, appStrLen
,
905 certName
, certNameLen
);
910 /* not interested, proceed to next name */
913 break; /* from case HostName */
916 if(name
->nameType
!= GNT_RFC822Name
) {
920 certName
= (char *)name
->name
.Data
;
921 if(certName
== NULL
) {
922 tpErrorLog("tpCompareSubjectAltName: malformed "
926 certNameLen
= (uint32
)(name
->name
.Length
);
929 ourRtn
= tpCompareEmailAddr(appStr
, appStrLen
, certName
,
930 certNameLen
, normalizeAll
);
942 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
943 static CSSM_BOOL
tpIsNumeric(
944 const char *hostName
,
945 unsigned hostNameLen
)
947 if(hostName
[hostNameLen
- 1] == '\0') {
948 /* ignore NULL terminator */
951 for(unsigned i
=0; i
<hostNameLen
; i
++) {
952 char c
= *hostName
++;
964 * Convert a typed string represented by a CSSM_X509_TYPE_VALUE_PAIR to a
965 * CFStringRef. Caller owns and must release the result. NULL return means
966 * unconvertible input "string".
968 static CFStringRef
tpTvpToCfString(
969 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
)
971 CFStringBuiltInEncodings encoding
;
972 switch(tvp
->valueType
) {
973 case BER_TAG_T61_STRING
:
975 encoding
= kCFStringEncodingISOLatin1
;
977 case BER_TAG_PKIX_BMP_STRING
:
978 encoding
= kCFStringEncodingUnicode
;
980 case BER_TAG_PRINTABLE_STRING
:
981 case BER_TAG_IA5_STRING
:
982 case BER_TAG_PKIX_UTF8_STRING
:
983 encoding
= kCFStringEncodingUTF8
;
989 /* raw data ==> CFString */
990 CFDataRef cfd
= CFDataCreate(NULL
, tvp
->value
.Data
, tvp
->value
.Length
);
994 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, encoding
);
1000 * Compare a CFString and a string represented by a CSSM_X509_TYPE_VALUE_PAIR.
1001 * Returns CSSM_TRUE if they are equal.
1003 static bool tpCompareTvpToCfString(
1004 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
,
1006 CFOptionFlags flags
) // e.g., kCFCompareCaseInsensitive
1008 CFStringRef cfStr
= tpTvpToCfString(tvp
);
1012 CFComparisonResult res
= CFStringCompare(refStr
, cfStr
, flags
);
1014 if(res
== kCFCompareEqualTo
) {
1023 * Given one iSignCertInfo, determine whether or not the specified
1024 * EKU OID, or - optionally - CSSMOID_ExtendedKeyUsageAny - is present.
1025 * Returns true if so, else false.
1027 static bool tpVerifyEKU(
1028 const iSignCertInfo
&certInfo
,
1029 const CSSM_OID
&ekuOid
,
1030 bool ekuAnyOK
) // if true, CSSMOID_ExtendedKeyUsageAny counts as "found"
1032 if(!certInfo
.extendKeyUsage
.present
) {
1035 CE_ExtendedKeyUsage
*eku
= &certInfo
.extendKeyUsage
.extnData
->extendedKeyUsage
;
1036 assert(eku
!= NULL
);
1038 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1039 const CSSM_OID
*foundEku
= &eku
->purposes
[i
];
1040 if(tpCompareOids(foundEku
, &ekuOid
)) {
1043 if(ekuAnyOK
&& tpCompareOids(foundEku
, &CSSMOID_ExtendedKeyUsageAny
)) {
1051 * Given one iSignCertInfo, determine whether or not the specified
1052 * Certificate Policy OID, or - optionally - CSSMOID_ANY_POLICY - is present.
1053 * Returns true if so, else false.
1055 static bool tpVerifyCPE(
1056 const iSignCertInfo
&certInfo
,
1057 const CSSM_OID
&cpOid
,
1058 bool anyPolicyOK
) // if true, CSSMOID_ANY_POLICY counts as "found"
1060 if(!certInfo
.certPolicies
.present
) {
1063 CE_CertPolicies
*cp
= &certInfo
.certPolicies
.extnData
->certPolicies
;
1066 for(unsigned i
=0; i
<cp
->numPolicies
; i
++) {
1067 const CE_PolicyInformation
*foundPolicy
= &cp
->policies
[i
];
1068 if(tpCompareOids(&foundPolicy
->certPolicyId
, &cpOid
)) {
1071 if(anyPolicyOK
&& tpCompareOids(&foundPolicy
->certPolicyId
, &CSSMOID_ANY_POLICY
)) {
1079 * Verify iChat handle. We search for a matching (case-insensitive) string
1082 * -- name component ("dmitch") from subject name's CommonName
1084 * -- domain name from subject name's organizationalUnit
1086 * Plus we require an Organization component of "Apple Computer, Inc." or "Apple Inc."
1088 static bool tpCompareIChatHandleName(
1090 const char *iChatHandle
, // UTF8
1091 uint32 iChatHandleLen
)
1093 CSSM_DATA_PTR subjNameData
= NULL
; // from fetchField
1095 bool ourRtn
= false;
1096 CSSM_X509_NAME_PTR x509name
;
1097 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
1098 CSSM_X509_RDN_PTR rdnp
;
1102 /* search until all of these are true */
1103 CSSM_BOOL commonNameMatch
= CSSM_FALSE
; // name before '@'
1104 CSSM_BOOL orgUnitMatch
= CSSM_FALSE
; // domain after '@
1105 CSSM_BOOL orgMatch
= CSSM_FALSE
; // Apple Computer, Inc. (or Apple Inc.)
1108 * incoming UTF8 handle ==> two components.
1109 * First convert to CFString.
1111 if(iChatHandle
[iChatHandleLen
- 1] == '\0') {
1112 /* avoid NULL when creating CFStrings */
1115 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)iChatHandle
, iChatHandleLen
);
1119 CFStringRef handleStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
,
1120 kCFStringEncodingUTF8
);
1122 if(handleStr
== NULL
) {
1123 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
1128 * Find the '@' delimiter
1131 whereIsAt
= CFStringFind(handleStr
, CFSTR("@"), 0);
1132 if(whereIsAt
.length
== 0) {
1133 tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
1134 CFRelease(handleStr
);
1139 * Two components, before and after delimiter
1141 CFRange r
= {0, whereIsAt
.location
};
1142 CFStringRef iChatName
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1143 if(iChatName
== NULL
) {
1144 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
1145 CFRelease(handleStr
);
1148 r
.location
= whereIsAt
.location
+ 1; // after the '@'
1149 r
.length
= CFStringGetLength(handleStr
) - r
.location
;
1150 CFStringRef iChatDomain
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1151 CFRelease(handleStr
);
1152 if(iChatDomain
== NULL
) {
1153 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
1154 CFRelease(iChatName
);
1157 /* subsequent errors to errOut: */
1159 /* get subject name in CSSM form, all subsequent ops work on that */
1160 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1162 /* should never happen, we shouldn't be here if there is no subject */
1163 tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
1167 x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
1168 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
1169 tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
1173 /* Now grunge thru the X509 name looking for three fields */
1175 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
1176 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
1177 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
1178 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
1179 if(!commonNameMatch
&&
1180 tpCompareOids(&ptvp
->type
, &CSSMOID_CommonName
) &&
1181 tpCompareTvpToCfString(ptvp
, iChatName
, kCFCompareCaseInsensitive
)) {
1182 commonNameMatch
= CSSM_TRUE
;
1186 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationalUnitName
) &&
1187 tpCompareTvpToCfString(ptvp
, iChatDomain
, kCFCompareCaseInsensitive
)) {
1188 orgUnitMatch
= CSSM_TRUE
;
1192 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationName
) &&
1193 /* this one is case sensitive */
1194 (tpCompareTvpToCfString(ptvp
, CFSTR("Apple Computer, Inc."), 0) ||
1195 tpCompareTvpToCfString(ptvp
, CFSTR("Apple Inc."), 0))) {
1196 orgMatch
= CSSM_TRUE
;
1199 if(commonNameMatch
&& orgUnitMatch
&& orgMatch
) {
1207 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1208 CFRelease(iChatName
);
1209 CFRelease(iChatDomain
);
1214 * Verify SSL options. Currently this just consists of matching the
1215 * leaf cert's subject common name against the caller's (optional)
1218 static CSSM_RETURN
tp_verifySslOpts(
1220 TPCertGroup
&certGroup
,
1221 const CSSM_DATA
*sslFieldOpts
,
1222 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1224 const iSignCertInfo
&leafCertInfo
= certInfo
[0];
1225 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= NULL
;
1226 unsigned hostNameLen
= 0;
1227 const char *serverName
= NULL
;
1228 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1229 assert(leaf
!= NULL
);
1231 /* CSSM_APPLE_TP_SSL_OPTIONS is optional */
1232 if((sslFieldOpts
!= NULL
) && (sslFieldOpts
->Data
!= NULL
)) {
1233 sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)sslFieldOpts
->Data
;
1234 switch(sslOpts
->Version
) {
1235 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
1236 if(sslFieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
1237 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1240 /* handle backwards compatibility here if necessary */
1242 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1244 hostNameLen
= sslOpts
->ServerNameLen
;
1245 serverName
= sslOpts
->ServerName
;
1248 /* host name check is optional */
1249 if(hostNameLen
!= 0) {
1250 if(serverName
== NULL
) {
1251 return CSSMERR_TP_INVALID_POINTER
;
1254 /* convert caller's hostname string to lower case */
1255 char *hostName
= (char *)certGroup
.alloc().malloc(hostNameLen
);
1256 memmove(hostName
, serverName
, hostNameLen
);
1257 tpToLower(hostName
, hostNameLen
);
1259 CSSM_BOOL match
= CSSM_FALSE
;
1261 /* First check subjectAltName... */
1262 bool dnsNameFound
= false;
1264 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1265 hostName
, hostNameLen
,
1266 SAN_HostName
, false, dnsNameFound
, dummy
);
1269 * Then common name, if
1270 * -- no match from subjectAltName, AND
1271 * -- dnsName was NOT found, AND
1272 * -- hostName is not strictly numeric form (1.2.3.4)
1274 if(!match
&& !dnsNameFound
&& !tpIsNumeric(hostName
, hostNameLen
)) {
1276 match
= tpCompareSubjectName(*leaf
, SN_CommonName
, false, hostName
, hostNameLen
,
1281 * Limit allowed domains for specific anchors
1283 CSSM_BOOL domainMatch
= CSSM_TRUE
;
1285 TPCertInfo
*tpCert
= certGroup
.lastCert();
1287 const CSSM_DATA
*certData
= tpCert
->itemData();
1288 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
1289 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
1290 if (!memcmp(digest
, kAppleCorpCASHA1
, sizeof(digest
))) {
1291 const char *dnlist
[] = { "apple.com", "icloud.com" };
1292 unsigned int idx
, dncount
=2;
1293 domainMatch
= CSSM_FALSE
;
1294 for(idx
=0;idx
<dncount
;idx
++) {
1295 uint32 len
=(uint32
)strlen(dnlist
[idx
]);
1296 char *domainName
=(char*)certGroup
.alloc().malloc(len
);
1297 memmove(domainName
, (char*)dnlist
[idx
], len
);
1298 if(tpCompareDomainSuffix(hostName
, hostNameLen
,
1300 domainMatch
= CSSM_TRUE
;
1302 certGroup
.alloc().free(domainName
);
1311 certGroup
.alloc().free(hostName
);
1313 if(leaf
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
)) {
1314 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1318 if(leaf
->addStatusCode(CSSMERR_APPLETP_CA_PIN_MISMATCH
)) {
1319 return CSSMERR_APPLETP_CA_PIN_MISMATCH
;
1325 * Ensure that, if an extendedKeyUsage extension is present in the
1326 * leaf, that either anyExtendedKeyUsage or the appropriate
1327 * CSSMOID_{Server,Client}Auth, or a SeverGatedCrypto usage is present.
1329 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1330 if(ekuInfo
.present
) {
1331 bool foundGoodEku
= false;
1332 bool isServer
= true;
1333 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1334 assert(eku
!= NULL
);
1337 * Determine appropriate extended key usage; default is SSL server
1339 const CSSM_OID
*extUse
= &CSSMOID_ServerAuth
;
1340 if((sslOpts
!= NULL
) && /* optional, default server side */
1341 (sslOpts
->Version
> 0) && /* this was added in struct version 1 */
1342 (sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
)) {
1343 extUse
= &CSSMOID_ClientAuth
;
1347 /* search for that one or for "any" indicator */
1348 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1349 const CSSM_OID
*purpose
= &eku
->purposes
[i
];
1350 if(tpCompareOids(purpose
, extUse
)) {
1351 foundGoodEku
= true;
1354 if(tpCompareOids(purpose
, &CSSMOID_ExtendedKeyUsageAny
)) {
1355 foundGoodEku
= true;
1358 if((policy
== kTP_IPSec
) && (tpCompareOids(purpose
, &CSSMOID_EKU_IPSec
))) {
1359 foundGoodEku
= true;
1363 /* server gated crypto: server side only */
1364 if(tpCompareOids(purpose
, &CSSMOID_NetscapeSGC
)) {
1365 foundGoodEku
= true;
1368 if(tpCompareOids(purpose
, &CSSMOID_MicrosoftSGC
)) {
1369 foundGoodEku
= true;
1375 if(leaf
->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE
)) {
1376 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1382 * Check for additional options flag (2nd lowest bit set) which indicates
1383 * we must be issued by an Apple intermediate with a particular extension.
1384 * (This flag is set by SecPolicyCreateAppleSSLService in SecPolicy.cpp.)
1386 if((sslOpts
!= NULL
) &&
1387 (sslOpts
->Version
> 0) && /* this was added in struct version 1 */
1388 (sslOpts
->Flags
& 0x00000002)) {
1390 if (certGroup
.numCerts() > 1) {
1391 const iSignCertInfo
*isCertInfo
= &certInfo
[1];
1392 if (!(isCertInfo
->foundAppleServerAuthMarker
== CSSM_TRUE
)) {
1393 TPCertInfo
*tpCert
= certGroup
.certAtIndex(1);
1394 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
1395 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1399 /* we only have the leaf? */
1400 if(leaf
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
)) {
1401 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1409 * Verify SMIME and iChat options.
1410 * This deals with both S/MIME and iChat policies; within the iChat domain it
1411 * deals with Apple-specific .mac certs as well as what we call "generic AIM"
1412 * certs, as used in the Windows AIM client.
1414 #define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
1416 static CSSM_RETURN
tp_verifySmimeOpts(
1418 TPCertGroup
&certGroup
,
1419 const CSSM_DATA
*smimeFieldOpts
,
1420 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1422 const iSignCertInfo
&leafCertInfo
= certInfo
[0];
1423 bool iChat
= (policy
== kTP_iChat
) ? true : false;
1425 * The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1427 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
= NULL
;
1428 if(smimeFieldOpts
!= NULL
) {
1429 smimeOpts
= (CSSM_APPLE_TP_SMIME_OPTIONS
*)smimeFieldOpts
->Data
;
1431 if(smimeOpts
!= NULL
) {
1432 switch(smimeOpts
->Version
) {
1433 case CSSM_APPLE_TP_SMIME_OPTS_VERSION
:
1434 if(smimeFieldOpts
->Length
!=
1435 sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
1436 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1439 /* handle backwards compatibility here if necessary */
1441 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1445 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1446 assert(leaf
!= NULL
);
1448 /* Verify optional email address, a.k.a. handle for iChat policy */
1449 unsigned emailLen
= 0;
1450 if(smimeOpts
!= NULL
) {
1451 emailLen
= smimeOpts
->SenderEmailLen
;
1455 bool emailFoundInSAN
= false;
1456 bool iChatHandleFound
= false; /* indicates a genuine Apple iChat cert */
1457 bool emailFoundInDN
= false;
1459 if(smimeOpts
->SenderEmail
== NULL
) {
1460 return CSSMERR_TP_INVALID_POINTER
;
1463 /* iChat - first try the Apple custom format */
1465 iChatHandleFound
= tpCompareIChatHandleName(*leaf
, smimeOpts
->SenderEmail
,
1467 if(iChatHandleFound
) {
1475 * normalize caller's email string
1476 * SMIME - lowercase only the portion after '@'
1477 * iChat - lowercase all of it
1479 char *email
= (char *)certGroup
.alloc().malloc(emailLen
);
1480 memmove(email
, smimeOpts
->SenderEmail
, emailLen
);
1481 tpNormalizeAddrSpec(email
, emailLen
, iChat
);
1485 * First check subjectAltName. The emailFound bool indicates
1486 * that *some* email address was found, regardless of a match
1490 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1492 SAN_Email
, iChat
, dummy
, emailFoundInSAN
);
1495 * Then subject DN, CSSMOID_EmailAddress, if no match from
1496 * subjectAltName. In this case the whole email address is
1497 * case insensitive (RFC 3280, section 4.1.2.6), so
1501 tpNormalizeAddrSpec(email
, emailLen
, true);
1502 match
= tpCompareSubjectName(*leaf
, SN_Email
, true, email
, emailLen
,
1505 certGroup
.alloc().free(email
);
1508 * Error here if no match found but there was indeed *some*
1509 * email address in the cert.
1511 if(!match
&& (emailFoundInSAN
|| emailFoundInDN
)) {
1512 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
)) {
1513 tpPolicyError("SMIME email addrs in cert but no match");
1514 return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
;
1520 * iChat only: error if app specified email address but there was
1523 if(iChat
&& !emailFoundInSAN
&& !emailFoundInDN
&& !iChatHandleFound
) {
1524 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1525 tpPolicyError("iChat: no email address or handle in cert");
1526 return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
;
1532 * Going by the letter of the law, here's what RFC 2632 has to say
1533 * about the legality of an empty Subject Name:
1535 * ...the subject DN in a user's (i.e. end-entity) certificate MAY
1536 * be an empty SEQUENCE in which case the subjectAltName extension
1537 * will include the subject's identifier and MUST be marked as
1540 * OK, first examine the leaf cert's subject name.
1543 CSSM_DATA_PTR subjNameData
= NULL
;
1544 const iSignExtenInfo
&kuInfo
= leafCertInfo
.keyUsage
;
1545 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1546 const CSSM_X509_NAME
*x509Name
= NULL
;
1549 /* empty subject name processing is S/MIME only */
1553 crtn
= leaf
->fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1555 /* This should really never happen */
1556 tpPolicyError("SMIME policy: error fetching subjectName");
1557 leaf
->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE
);
1558 return CSSMERR_TP_INVALID_CERTIFICATE
;
1560 /* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
1562 x509Name
= (const CSSM_X509_NAME
*)subjNameData
->Data
;
1563 if(x509Name
->numberOfRDNs
== 0) {
1565 * Empty subject name. If we haven't already seen a valid
1566 * email address in the subject alternate name (by looking
1567 * for a specific address specified by app), try to find
1570 if(!emailFoundInSAN
&& // haven't found one, and
1571 (emailLen
== 0)) { // didn't even look yet
1573 tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1574 NULL
, 0, // email, emailLen,
1575 SAN_Email
, false, dummy
,
1576 emailFoundInSAN
); // the variable we're updating
1578 if(!emailFoundInSAN
) {
1579 tpPolicyError("SMIME policy fail: empty subject name and "
1580 "no Email Addrs in SubjectAltName");
1581 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1582 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1583 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1586 /* have to skip the next block */
1592 * One more thing: this leaf must indeed have a subjAltName
1593 * extension and it must be critical. We would not have gotten this
1594 * far if the subjAltName extension was not actually present....
1596 assert(leafCertInfo
.subjectAltName
.present
);
1597 if(!leafCertInfo
.subjectAltName
.critical
) {
1598 tpPolicyError("SMIME policy fail: empty subject name and "
1599 "no Email Addrs in SubjectAltName");
1600 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT
)) {
1601 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1602 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1607 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1610 * Enforce the usage of the key associated with the leaf cert.
1611 * Cert's KeyUsage must be a superset of what the app is trying to do.
1612 * Note the {en,de}cipherOnly flags are handled separately....
1614 if(kuInfo
.present
&& (smimeOpts
!= NULL
)) {
1615 CE_KeyUsage certKu
= *((CE_KeyUsage
*)kuInfo
.extnData
);
1616 CE_KeyUsage appKu
= smimeOpts
->IntendedUsage
;
1617 CE_KeyUsage intersection
= certKu
& appKu
;
1618 if((intersection
& CE_CIPHER_MASK
) != (appKu
& CE_CIPHER_MASK
)) {
1619 tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
1621 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1622 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1626 /* Now the en/de cipher only bits - for keyAgreement only */
1627 if(appKu
& CE_KU_KeyAgreement
) {
1629 * 1. App wants to use this for key agreement; it must
1630 * say what it wants to do with the derived key.
1631 * In this context, the app's XXXonly bit means that
1632 * it wants to use the key for that op - not necessarliy
1635 if((appKu
& (CE_KU_EncipherOnly
| CE_KU_DecipherOnly
)) == 0) {
1636 tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
1637 "no Encipher or Decipher");
1638 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1639 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1644 * 2. If cert restricts to encipher only make sure the
1645 * app isn't trying to decipher.
1647 if((certKu
& CE_KU_EncipherOnly
) &&
1648 (appKu
& CE_KU_DecipherOnly
)) {
1649 tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
1650 "app wants to decipher");
1651 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1652 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1657 * 3. If cert restricts to decipher only make sure the
1658 * app isn't trying to encipher.
1660 if((certKu
& CE_KU_DecipherOnly
) &&
1661 (appKu
& CE_KU_EncipherOnly
)) {
1662 tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
1663 "app wants to encipher");
1664 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1665 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1672 * Extended Key Use verification, which is different for the two policies.
1675 if(iChat
&& !ekuInfo
.present
) {
1677 * iChat: whether generic AIM cert or Apple .mac/iChat cert, we must have an
1678 * extended key use extension.
1680 tpPolicyError("iChat: No extended Key Use");
1681 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1682 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1686 if(!iChatHandleFound
) {
1688 * S/MIME and generic AIM certs when evaluating iChat policy.
1689 * Look for either emailProtection or anyExtendedKeyUsage usages.
1691 * S/MIME : the whole extension is optional.
1692 * iChat : extension must be there (which we've already covered, above)
1693 * and we must find one of those extensions.
1695 if(ekuInfo
.present
) {
1696 bool foundGoodEku
= false;
1697 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1698 assert(eku
!= NULL
);
1699 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1700 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_EmailProtection
)) {
1701 foundGoodEku
= true;
1704 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1705 foundGoodEku
= true;
1710 tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
1711 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1712 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1719 * Apple iChat cert. Look for anyExtendedKeyUsage, iChatSigning,
1720 * ichatEncrypting - the latter of two which can optionally be
1723 assert(iChat
); /* or we could not have even looked for an iChat style handle */
1724 assert(ekuInfo
.present
); /* checked above */
1725 bool foundAnyEku
= false;
1726 bool foundIChatSign
= false;
1727 bool foundISignEncrypt
= false;
1728 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1729 assert(eku
!= NULL
);
1731 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1732 if(tpCompareOids(&eku
->purposes
[i
],
1733 &CSSMOID_APPLE_EKU_ICHAT_SIGNING
)) {
1734 foundIChatSign
= true;
1736 else if(tpCompareOids(&eku
->purposes
[i
],
1737 &CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION
)) {
1738 foundISignEncrypt
= true;
1740 else if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1745 if(!foundAnyEku
&& !foundISignEncrypt
&& !foundIChatSign
) {
1746 /* No go - no acceptable uses found */
1747 tpPolicyError("iChat: No valid extended Key Uses found");
1748 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1749 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1753 /* check for specifically required uses */
1754 if((smimeOpts
!= NULL
) && (smimeOpts
->IntendedUsage
!= 0)) {
1755 if(smimeOpts
->IntendedUsage
& CE_KU_DigitalSignature
) {
1756 if(!foundIChatSign
) {
1757 tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
1758 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1759 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1763 if(smimeOpts
->IntendedUsage
& CE_KU_DataEncipherment
) {
1764 if(!foundISignEncrypt
) {
1765 tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
1766 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1767 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1771 } /* checking IntendedUsage */
1772 } /* iChat cert format */
1778 * Verify Apple SW Update signing (was Apple Code Signing, pre-Leopard) options.
1780 * -- Must have one intermediate cert
1781 * -- intermediate must have basic constraints with path length 0
1782 * -- intermediate has CSSMOID_APPLE_EKU_CODE_SIGNING EKU
1783 * -- leaf cert has either CODE_SIGNING or CODE_SIGN_DEVELOPMENT EKU (the latter of
1784 * which triggers a CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT error)
1786 static CSSM_RETURN
tp_verifySWUpdateSigningOpts(
1787 TPCertGroup
&certGroup
,
1788 const CSSM_DATA
*fieldOpts
, // currently unused
1789 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1791 unsigned numCerts
= certGroup
.numCerts();
1792 const iSignCertInfo
*isCertInfo
;
1794 // const CE_BasicConstraints *bc; // currently unused
1795 CE_ExtendedKeyUsage
*eku
;
1796 CSSM_RETURN crtn
= CSSM_OK
;
1799 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
)) {
1800 tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts
);
1801 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1803 else if(numCerts
< 3) {
1804 /* this error allowed, but no intermediate...check leaf */
1809 /* verify intermediate cert */
1810 isCertInfo
= &certInfo
[1];
1811 tpCert
= certGroup
.certAtIndex(1);
1813 if(!isCertInfo
->basicConstraints
.present
) {
1814 tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
1815 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
)) {
1816 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1820 /* ExtendedKeyUse required, one legal value */
1821 if(!isCertInfo
->extendKeyUsage
.present
) {
1822 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
1823 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1824 return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1831 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1832 assert(eku
!= NULL
);
1833 if(eku
->numPurposes
!= 1) {
1834 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
1835 (unsigned long)eku
->numPurposes
);
1836 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1837 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1839 else if(eku
->numPurposes
== 0) {
1840 /* ignore that error but no EKU - skip EKU check */
1843 /* else ignore error and we have an intermediate EKU; proceed */
1846 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1847 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
1848 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1849 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1855 /* verify leaf cert */
1856 isCertInfo
= &certInfo
[0];
1857 tpCert
= certGroup
.certAtIndex(0);
1858 if(!isCertInfo
->extendKeyUsage
.present
) {
1859 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
1860 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1861 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1864 /* have to skip remainder */
1869 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1870 assert(eku
!= NULL
);
1871 if(eku
->numPurposes
!= 1) {
1872 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
1873 (unsigned long)eku
->numPurposes
);
1874 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1875 if(crtn
== CSSM_OK
) {
1876 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1881 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1882 if(tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV
)) {
1883 tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
1884 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
)) {
1885 if(crtn
== CSSM_OK
) {
1886 crtn
= CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
;
1891 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
1892 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1893 if(crtn
== CSSM_OK
) {
1894 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1904 * Verify Apple Resource Signing options.
1906 * -- leaf cert must have CSSMOID_APPLE_EKU_RESOURCE_SIGNING EKU
1907 * -- chain length must be >= 2
1908 * -- mainline code already verified that leaf KeyUsage = digitalSignature (only)
1910 static CSSM_RETURN
tp_verifyResourceSigningOpts(
1911 TPCertGroup
&certGroup
,
1912 const CSSM_DATA
*fieldOpts
, // currently unused
1913 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1915 unsigned numCerts
= certGroup
.numCerts();
1917 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
)) {
1918 tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts
);
1919 return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
;
1922 const iSignCertInfo
&leafCert
= certInfo
[0];
1923 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1925 /* leaf ExtendedKeyUse required, one legal value */
1926 if(!tpVerifyEKU(leafCert
, CSSMOID_APPLE_EKU_RESOURCE_SIGNING
, false)) {
1927 tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
1928 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1929 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1937 * Common code for Apple Code Signing and Apple Package Signing.
1938 * For now we just require an RFC3280-style CodeSigning EKU in the leaf
1939 * for both policies.
1941 static CSSM_RETURN
tp_verifyCodePkgSignOpts(
1943 TPCertGroup
&certGroup
,
1944 const CSSM_DATA
*fieldOpts
, // currently unused
1945 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1947 const iSignCertInfo
&leafCert
= certInfo
[0];
1949 /* leaf ExtendedKeyUse required, one legal value */
1950 if(!tpVerifyEKU(leafCert
, CSSMOID_ExtendedUseCodeSigning
, false)) {
1951 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1952 tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
1953 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1954 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1963 * Verify MacAppStore receipt verification policy options.
1965 * -- Must have one intermediate cert
1966 * -- intermediate must be the FairPlay intermediate
1967 * -- leaf cert has the CSSMOID_APPLE_EXTENSION_MACAPPSTORE_RECEIPT marker extension
1969 static CSSM_RETURN
tp_verifyMacAppStoreReceiptOpts(
1970 TPCertGroup
&certGroup
,
1971 const CSSM_DATA
*fieldOpts
, // currently unused
1972 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1974 unsigned numCerts
= certGroup
.numCerts();
1977 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
1979 tpPolicyError("tp_verifyMacAppStoreReceiptOpts: numCerts %u", numCerts
);
1980 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1984 const iSignCertInfo
*isCertInfo
;
1987 /* verify intermediate cert */
1988 isCertInfo
= &certInfo
[1];
1989 tpCert
= certGroup
.certAtIndex(1);
1991 if (!isCertInfo
->basicConstraints
.present
)
1993 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1994 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
1995 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1998 // Now check the leaf
1999 isCertInfo
= &certInfo
[0];
2000 tpCert
= certGroup
.certAtIndex(0);
2001 if (certInfo
->certificatePolicies
.present
)
2003 // syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: found certificatePolicies");
2004 const CE_CertPolicies
*certPolicies
=
2005 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
2006 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY
))
2007 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2008 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2012 // syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present"); // DEBUG
2013 tpPolicyError("tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present in leaf");
2014 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2015 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2021 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
)
2023 // returns true if the given OID is present in the cert policies
2025 if (!certPolicies
|| !oidToFind
)
2028 const uint32 maxIndex
= 100; // sanity check
2029 for (uint32 policyIndex
= 0; policyIndex
< certPolicies
->numPolicies
&& policyIndex
< maxIndex
; policyIndex
++)
2031 CE_PolicyInformation
*certPolicyInfo
= &certPolicies
->policies
[policyIndex
];
2032 CSSM_OID_PTR oid
= &certPolicyInfo
->certPolicyId
;
2033 if (oid
&& tpCompareOids(oid
, oidToFind
)) // found it
2042 * Verify Apple ID Sharing options.
2044 * -- Do basic cert validation (OCSP-based certs)
2045 * -- Validate that the cert is an Apple ID sharing cert:
2046 * has a custom extension: OID: Apple ID Sharing Certificate ( 1 2 840 113635 100 4 7 )
2047 * (CSSMOID_APPLE_EXTENSION_APPLEID_SHARING)
2048 * EKU should have both client and server authentication
2049 * chains to the "Apple Application Integration Certification Authority" intermediate
2050 * -- optionally has a client-specified common name, which is the Apple ID account's UUID.
2052 * -- Must have one intermediate cert ("Apple Application Integration Certification Authority")
2053 * -- intermediate must have basic constraints with path length 0
2054 * -- intermediate has CSSMOID_APPLE_EXTENSION_AAI_INTERMEDIATE extension (OID 1 2 840 113635 100 6 2 3)
2055 OR APPLE_EXTENSION_AAI_INTERMEDIATE_2
2058 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
2059 const CSSM_DATA
*fieldOpts
, // optional Common Name
2060 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2062 unsigned numCerts
= certGroup
.numCerts();
2063 const iSignCertInfo
*isCertInfo
;
2065 // const CE_BasicConstraints *bc; // currently unused
2066 CE_ExtendedKeyUsage
*eku
;
2067 CSSM_RETURN crtn
= CSSM_OK
;
2068 unsigned int serverNameLen
= 0;
2069 const char *serverName
= NULL
;
2071 // The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
2072 if (fieldOpts
&& fieldOpts
->Data
)
2074 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)fieldOpts
->Data
;
2075 switch (sslOpts
->Version
)
2077 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
2078 if (fieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
))
2079 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2081 /* handle backwards compatibility here if necessary */
2083 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2085 serverNameLen
= sslOpts
->ServerNameLen
;
2086 serverName
= sslOpts
->ServerName
;
2089 //------------------------------------------------------------------------
2093 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
2095 tpPolicyError("tp_verifyAppleIDSharingOpts: numCerts %u", numCerts
);
2096 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2101 /* this error allowed, but no intermediate...check leaf */
2106 /* verify intermediate cert */
2107 isCertInfo
= &certInfo
[1];
2108 tpCert
= certGroup
.certAtIndex(1);
2110 if (!isCertInfo
->basicConstraints
.present
)
2112 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
2113 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
2114 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
2119 /* verify leaf cert */
2120 isCertInfo
= &certInfo
[0];
2121 tpCert
= certGroup
.certAtIndex(0);
2123 /* host name check is optional */
2124 if (serverNameLen
!= 0)
2126 if (serverName
== NULL
)
2127 return CSSMERR_TP_INVALID_POINTER
;
2129 /* convert caller's hostname string to lower case */
2130 char *hostName
= (char *)certGroup
.alloc().malloc(serverNameLen
);
2131 memmove(hostName
, serverName
, serverNameLen
);
2132 tpToLower(hostName
, serverNameLen
);
2134 /* Check common name... */
2137 CSSM_BOOL match
= tpCompareSubjectName(*tpCert
, SN_CommonName
, false, hostName
,
2138 serverNameLen
, fieldFound
);
2140 certGroup
.alloc().free(hostName
);
2141 if (!match
&& tpCert
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
))
2142 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
2145 if (certInfo
->certificatePolicies
.present
)
2147 const CE_CertPolicies
*certPolicies
=
2148 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
2149 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_APPLEID_SHARING_CERT_POLICY
))
2150 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2151 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2154 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2155 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2157 if (!isCertInfo
->extendKeyUsage
.present
)
2159 tpPolicyError("tp_verifyAppleIDSharingOpts: no extendedKeyUse in leaf");
2160 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
))
2161 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
2163 /* have to skip remainder */
2167 // Check that certificate can do Client and Server Authentication (EKU)
2168 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2169 assert(eku
!= NULL
);
2170 if(eku
->numPurposes
!= 2)
2172 tpPolicyError("tp_verifyAppleIDSharingOpts: bad eku->numPurposes (%lu)",
2173 (unsigned long)eku
->numPurposes
);
2174 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
2176 if (crtn
== CSSM_OK
)
2177 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2181 bool canDoClientAuth
= false, canDoServerAuth
= false, ekuError
= false;
2182 for (int ix
=0;ix
<2;ix
++)
2184 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ClientAuth
))
2185 canDoClientAuth
= true;
2187 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ServerAuth
))
2188 canDoServerAuth
= true;
2196 if (!(canDoClientAuth
&& canDoServerAuth
))
2200 tpPolicyError("tp_verifyAppleIDSharingOpts: bad EKU in leaf");
2201 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
2203 if (crtn
== CSSM_OK
)
2204 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2212 * Verify Time Stamping (RFC3161) policy options.
2214 * -- Leaf must contain Extended Key Usage (EKU), marked critical
2215 * -- The EKU must contain the id-kp-timeStamping purpose and no other
2217 static CSSM_RETURN
tp_verifyTimeStampingOpts(TPCertGroup
&certGroup
,
2218 const CSSM_DATA
*fieldOpts
, // currently unused
2219 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2221 //unsigned numCerts = certGroup.numCerts();
2222 const iSignCertInfo
*isCertInfo
;
2224 CE_ExtendedKeyUsage
*eku
;
2226 isCertInfo
= &certInfo
[0];
2227 tpCert
= certGroup
.certAtIndex(0);
2229 if (!isCertInfo
->extendKeyUsage
.present
)
2231 tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
2232 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2233 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2236 if(!isCertInfo
->extendKeyUsage
.critical
)
2238 tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
2239 tpCert
->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
);
2240 return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
;
2243 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2244 assert(eku
!= NULL
);
2246 if(eku
->numPurposes
!= 1)
2248 tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
2249 (unsigned long)eku
->numPurposes
);
2250 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2251 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2254 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_TimeStamping
))
2256 tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
2257 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2258 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2265 * Verify Passbook Signing policy options.
2267 * -- Do basic cert validation (OCSP-based certs)
2268 * -- Chains to the Apple root CA
2269 * -- Has custom marker extension (1.2.840.113635.100.6.1.16)
2270 * (CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING)
2271 * -- EKU contains Passbook Signing purpose (1.2.840.113635.100.4.14)
2272 * (CSSMOID_APPLE_EKU_PASSBOOK_SIGNING)
2273 * -- UID field of Subject must contain provided card signer string
2274 * -- OU field of Subject must contain provided team identifier string
2276 static CSSM_RETURN
tp_verifyPassbookSigningOpts(TPCertGroup
&certGroup
,
2277 const CSSM_DATA
*fieldOpts
,
2278 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2280 unsigned numCerts
= certGroup
.numCerts();
2281 const iSignCertInfo
*isCertInfo
;
2283 CE_ExtendedKeyUsage
*eku
;
2284 CSSM_RETURN crtn
= CSSM_OK
;
2285 unsigned int nameLen
= 0;
2286 const char *name
= NULL
;
2287 char *p
, *signerName
= NULL
, *teamIdentifier
= NULL
;
2290 isCertInfo
= &certInfo
[0];
2291 tpCert
= certGroup
.certAtIndex(0);
2293 /* The CSSM_APPLE_TP_SMIME_OPTIONS pointer is required. */
2294 if (!fieldOpts
|| !fieldOpts
->Data
)
2295 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2297 CSSM_APPLE_TP_SMIME_OPTIONS
*opts
= (CSSM_APPLE_TP_SMIME_OPTIONS
*)fieldOpts
->Data
;
2298 switch (opts
->Version
)
2300 case CSSM_APPLE_TP_SMIME_OPTS_VERSION
:
2301 if (fieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
))
2302 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2304 /* handle backwards compatibility here if necessary */
2306 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
2308 nameLen
= opts
->SenderEmailLen
;
2309 name
= opts
->SenderEmail
;
2310 if (!name
|| !nameLen
)
2311 return CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2314 /* Split the provided name into signer name and team identifier
2315 * (allocates memory, which must be freed at end) */
2316 signerName
= (char *)certGroup
.alloc().malloc(nameLen
);
2317 teamIdentifier
= (char *)certGroup
.alloc().malloc(nameLen
);
2318 memmove(signerName
, name
, nameLen
);
2319 teamIdentifier
[0] = '\0';
2320 if ((p
= strchr(signerName
, '\t')) != NULL
) {
2322 memmove(teamIdentifier
, p
, strlen(p
)+1);
2325 /* Check signer name in UID field */
2326 if (CSSM_FALSE
== tpCompareSubjectName(*tpCert
,
2327 SN_UserID
, false, signerName
, (unsigned int)strlen(signerName
), found
)) {
2328 tpPolicyError("tp_verifyPassbookSigningOpts: signer name not in subject UID field");
2329 tpCert
->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING
);
2330 crtn
= CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2334 /* Check team identifier in OU field */
2335 if (CSSM_FALSE
== tpCompareSubjectName(*tpCert
,
2336 SN_OrgUnit
, false, teamIdentifier
, (unsigned int)strlen(teamIdentifier
), found
)) {
2337 tpPolicyError("tp_verifyPassbookSigningOpts: team identifier not in subject OU field");
2338 tpCert
->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING
);
2339 crtn
= CSSMERR_APPLETP_IDENTIFIER_MISSING
;
2343 /* Check that EKU extension is present */
2344 if (!isCertInfo
->extendKeyUsage
.present
) {
2345 tpPolicyError("tp_verifyPassbookSigningOpts: no extendedKeyUse in leaf");
2346 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2347 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2351 /* Check that EKU contains Passbook Signing purpose */
2352 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2353 assert(eku
!= NULL
);
2355 for (int ix
=0;ix
<eku
->numPurposes
;ix
++) {
2356 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_APPLE_EKU_PASSBOOK_SIGNING
)) {
2362 tpPolicyError("tp_verifyPassbookSigningOpts: Passbook Signing purpose not found");
2363 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2364 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2368 /* Check that Passbook Signing marker extension is present */
2369 if (!(isCertInfo
->foundPassbookSigningMarker
== CSSM_TRUE
)) {
2370 tpPolicyError("tp_verifyPassbookSigningOpts: no Passbook Signing extension in leaf");
2371 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2372 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2376 /* Check that cert chain is anchored by the Apple Root CA */
2378 tpPolicyError("tp_verifyPassbookSigningOpts: numCerts %u", numCerts
);
2379 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2383 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2384 const CSSM_DATA
*certData
= tpCert
->itemData();
2385 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2386 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2387 if (memcmp(digest
, kAppleCASHA1
, sizeof(digest
))) {
2388 tpPolicyError("tp_verifyPassbookSigningOpts: invalid anchor for policy");
2389 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2390 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2397 certGroup
.alloc().free(signerName
);
2399 certGroup
.alloc().free(teamIdentifier
);
2405 * Verify Mobile Store policy options.
2407 * -- Do basic cert validation.
2408 * -- Chain length must be exactly 3.
2409 * -- Must chain to known Mobile Store root.
2410 * -- Intermediate must have CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE marker
2411 * (1.2.840.113635.100.6.2.10)
2412 * -- Key usage in leaf certificate must be Digital Signature.
2413 * -- Leaf has certificatePolicies extension with appropriate policy:
2414 * (1.2.840.113635.100.5.12) if testPolicy is false
2415 * (1.2.840.113635.100.5.12.1) if testPolicy is true
2417 static CSSM_RETURN
tp_verifyMobileStoreSigningOpts(TPCertGroup
&certGroup
,
2418 const CSSM_DATA
*fieldOpts
,
2419 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2422 unsigned numCerts
= certGroup
.numCerts();
2423 const iSignCertInfo
*isCertInfo
;
2426 CSSM_RETURN crtn
= CSSM_OK
;
2428 isCertInfo
= &certInfo
[0];
2429 tpCert
= certGroup
.certAtIndex(0);
2431 /* Check that KU extension is present */
2432 if (!isCertInfo
->keyUsage
.present
) {
2433 tpPolicyError("tp_verifyMobileStoreSigningOpts: no keyUsage in leaf");
2434 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2435 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2439 /* Check that KU contains Digital Signature usage */
2440 ku
= isCertInfo
->keyUsage
.extnData
->keyUsage
;
2441 if (!(ku
& CE_KU_DigitalSignature
)) {
2442 tpPolicyError("tp_verifyMobileStoreSigningOpts: DigitalSignature usage not found");
2443 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
2444 crtn
= CSSMERR_APPLETP_INVALID_KEY_USAGE
;
2448 /* Check that Mobile Store Signing certicate policy is present in leaf */
2449 if (isCertInfo
->certificatePolicies
.present
)
2451 const CE_CertPolicies
*certPolicies
=
2452 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
2453 const CSSM_OID
*policyOID
= (testPolicy
) ?
2454 &CSSMOID_TEST_MOBILE_STORE_SIGNING_POLICY
:
2455 &CSSMOID_MOBILE_STORE_SIGNING_POLICY
;
2456 if (!certificatePoliciesContainsOID(certPolicies
, policyOID
))
2457 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2458 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2462 tpPolicyError("tp_verifyMobileStoreSigningOpts: no certificatePolicies present in leaf");
2463 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
2464 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2467 /* Check that cert chain length is 3 */
2468 if (numCerts
!= 3) {
2469 tpPolicyError("tp_verifyMobileStoreSigningOpts: numCerts %u", numCerts
);
2470 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2474 /* Check that cert chain is anchored by a known root */
2476 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2477 const CSSM_DATA
*certData
= tpCert
->itemData();
2478 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2479 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2480 if (memcmp(digest
, kMobileRootSHA1
, sizeof(digest
))) {
2481 tpPolicyError("tp_verifyMobileStoreSigningOpts: invalid anchor for policy");
2482 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2483 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2488 /* Check that Apple System Integration 2 marker extension is present in intermediate */
2489 isCertInfo
= &certInfo
[1];
2490 tpCert
= certGroup
.certAtIndex(1);
2491 if (!(isCertInfo
->foundAppleSysInt2Marker
== CSSM_TRUE
)) {
2492 tpPolicyError("tp_verifyMobileStoreSigningOpts: intermediate marker extension not found");
2493 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2494 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2503 * Verify Escrow Service policy options.
2505 * -- Chain length must be exactly 2.
2506 * -- Must be issued by known escrow root.
2507 * -- Key usage in leaf certificate must be Key Encipherment.
2508 * -- Leaf has CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE_MARKER extension
2509 * (1.2.840.113635.100.6.23.1)
2511 static CSSM_RETURN
tp_verifyEscrowServiceCommon(TPCertGroup
&certGroup
,
2512 const CSSM_DATA
*fieldOpts
,
2513 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2514 SecCertificateEscrowRootType rootType
)
2516 unsigned numCerts
= certGroup
.numCerts();
2517 const iSignCertInfo
*isCertInfo
;
2520 CSSM_RETURN crtn
= CSSM_OK
;
2522 isCertInfo
= &certInfo
[0];
2523 tpCert
= certGroup
.certAtIndex(0);
2525 /* Check that KU extension is present */
2526 if (!isCertInfo
->keyUsage
.present
) {
2527 tpPolicyError("tp_verifyEscrowServiceCommon: no keyUsage in leaf");
2528 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2529 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2533 /* Check that KU contains Key Encipherment usage */
2534 ku
= isCertInfo
->keyUsage
.extnData
->keyUsage
;
2535 if (!(ku
& CE_KU_KeyEncipherment
)) {
2536 tpPolicyError("tp_verifyEscrowServiceCommon: KeyEncipherment usage not found");
2537 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
2538 crtn
= CSSMERR_APPLETP_INVALID_KEY_USAGE
;
2542 /* Check that Escrow Service marker extension is present */
2543 if (!(isCertInfo
->foundEscrowServiceMarker
== CSSM_TRUE
)) {
2544 tpPolicyError("tp_verifyEscrowServiceCommon: no Escrow Service extension in leaf");
2545 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2546 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2550 /* Check that cert chain length is 2 */
2551 if (numCerts
!= 2) {
2552 tpPolicyError("tp_verifyEscrowServiceCommon: numCerts %u", numCerts
);
2553 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2557 /* Check that cert chain is anchored by a known root */
2559 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2560 const CSSM_DATA
*certData
= tpCert
->itemData();
2561 bool anchorMatch
= false;
2562 SecCertificateRef anchor
= NULL
;
2563 OSStatus status
= SecCertificateCreateFromData(certData
, CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
, &anchor
);
2565 CFArrayRef anchors
= SecCertificateCopyEscrowRoots(rootType
);
2566 CFIndex idx
, count
= (anchors
) ? CFArrayGetCount(anchors
) : 0;
2567 for (idx
= 0; idx
< count
; idx
++) {
2568 SecCertificateRef cert
= (SecCertificateRef
) CFArrayGetValueAtIndex(anchors
, idx
);
2569 if (cert
&& CFEqual(cert
, anchor
)) {
2581 tpPolicyError("tp_verifyEscrowServiceCommon: invalid anchor for policy");
2582 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2583 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2592 static CSSM_RETURN
tp_verifyEscrowServiceSigningOpts(TPCertGroup
&certGroup
,
2593 const CSSM_DATA
*fieldOpts
,
2594 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2596 return tp_verifyEscrowServiceCommon(certGroup
, fieldOpts
, certInfo
, kSecCertificateProductionEscrowRoot
);
2599 static CSSM_RETURN
tp_verifyPCSEscrowServiceSigningOpts(TPCertGroup
&certGroup
,
2600 const CSSM_DATA
*fieldOpts
,
2601 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2603 return tp_verifyEscrowServiceCommon(certGroup
, fieldOpts
, certInfo
, kSecCertificateProductionPCSEscrowRoot
);
2607 * Verify Provisioning Profile Signing policy options.
2609 * -- Do basic cert validation (OCSP-based certs)
2610 * -- Chains to the Apple root CA
2611 * -- Leaf has Provisioning Profile marker OID (1.2.840.113635.100.4.11)
2612 * -- Intermediate has WWDR marker OID (1.2.840.113635.100.6.2.1)
2614 static CSSM_RETURN
tp_verifyProvisioningProfileSigningOpts(TPCertGroup
&certGroup
,
2615 const CSSM_DATA
*fieldOpts
,
2616 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2618 unsigned numCerts
= certGroup
.numCerts();
2619 const iSignCertInfo
*isCertInfo
;
2621 CSSM_RETURN crtn
= CSSM_OK
;
2623 isCertInfo
= &certInfo
[0];
2624 tpCert
= certGroup
.certAtIndex(0);
2626 /* Check that cert chain is anchored by the Apple Root CA */
2628 tpPolicyError("tp_verifyProvisioningProfileSigningOpts: numCerts %u", numCerts
);
2629 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2633 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2634 const CSSM_DATA
*certData
= tpCert
->itemData();
2635 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2636 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2637 if (memcmp(digest
, kAppleCASHA1
, sizeof(digest
))) {
2638 tpPolicyError("tp_verifyProvisioningProfileSigningOpts: invalid anchor for policy");
2639 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2640 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2645 /* Check that Provisioning Profile Signing marker extension is present */
2646 if (!(isCertInfo
->foundProvisioningProfileSigningMarker
== CSSM_TRUE
)) {
2647 tpPolicyError("tp_verifyProvisioningProfileSigningOpts: no Provisioning Profile Signing extension in leaf");
2648 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2649 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2653 /* Check that Apple WWDR marker extension is present in intermediate */
2654 isCertInfo
= &certInfo
[1];
2655 tpCert
= certGroup
.certAtIndex(1);
2656 if (!(isCertInfo
->foundAppleWWDRIntMarker
== CSSM_TRUE
)) {
2657 tpPolicyError("tp_verifyProvisioningProfileSigningOpts: intermediate marker extension not found");
2658 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2659 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2669 * Verify Configuration Profile Signing policy options.
2671 * -- Do basic cert validation (OCSP-based certs)
2672 * -- Chains to the Apple root CA
2673 * -- Leaf has EKU extension with appropriate purpose:
2674 * (1.2.840.113635.100.4.16) if testPolicy is false
2675 * (1.2.840.113635.100.4.17) if testPolicy is true
2677 static CSSM_RETURN
tp_verifyProfileSigningOpts(TPCertGroup
&certGroup
,
2678 const CSSM_DATA
*fieldOpts
,
2679 const iSignCertInfo
*certInfo
, // all certs, size certGroup.numCerts()
2682 unsigned numCerts
= certGroup
.numCerts();
2683 const iSignCertInfo
*isCertInfo
;
2685 CE_ExtendedKeyUsage
*eku
;
2686 CSSM_RETURN crtn
= CSSM_OK
;
2689 isCertInfo
= &certInfo
[0];
2690 tpCert
= certGroup
.certAtIndex(0);
2692 /* Check that EKU extension is present */
2693 if (!isCertInfo
->extendKeyUsage
.present
) {
2694 tpPolicyError("tp_verifyProfileSigningOpts: no extendedKeyUse in leaf");
2695 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2696 crtn
= CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2700 /* Check that EKU contains appropriate Profile Signing purpose */
2701 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2702 assert(eku
!= NULL
);
2704 for (int ix
=0;ix
<eku
->numPurposes
;ix
++) {
2705 if (tpCompareOids(&eku
->purposes
[ix
], (testPolicy
) ?
2706 &CSSMOID_APPLE_EKU_QA_PROFILE_SIGNING
:
2707 &CSSMOID_APPLE_EKU_PROFILE_SIGNING
)) {
2713 tpPolicyError("tp_verifyProfileSigningOpts: Profile Signing purpose not found");
2714 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2715 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2719 /* Check that cert chain is anchored by the Apple Root CA */
2721 tpPolicyError("tp_verifyProfileSigningOpts: numCerts %u", numCerts
);
2722 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2726 tpCert
= certGroup
.certAtIndex(numCerts
-1);
2727 const CSSM_DATA
*certData
= tpCert
->itemData();
2728 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
2729 CC_SHA1(certData
->Data
, (CC_LONG
)certData
->Length
, digest
);
2730 if (memcmp(digest
, kAppleCASHA1
, sizeof(digest
))) {
2731 tpPolicyError("tp_verifyProfileSigningOpts: invalid anchor for policy");
2732 tpCert
->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
);
2733 crtn
= CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
2743 * RFC2459 says basicConstraints must be flagged critical for
2744 * CA certs, but Verisign doesn't work that way.
2746 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
2749 * TP iSign spec says Extended Key Usage required for leaf certs,
2750 * but Verisign doesn't work that way.
2752 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
2755 * TP iSign spec says Subject Alternate Name required for leaf certs,
2756 * but Verisign doesn't work that way.
2758 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
2761 * TP iSign spec originally required KeyUsage for all certs, but
2762 * Verisign doesn't have that in their roots.
2764 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
2767 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
2768 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
2769 * but Thawte's intermediate cert (common name "Thawte Personal
2770 * Freemail Issuing CA") does not meet this requirement.
2772 #define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
2775 * Public routine to perform TP verification on a constructed
2777 * Returns CSSM_OK on success.
2778 * Assumes the chain has passed basic subject/issuer verification. First cert of
2779 * incoming certGroup is end-entity (leaf).
2781 * Per-policy details:
2782 * iSign: Assumes that last cert in incoming certGroup is a root cert.
2783 * Also assumes a cert group of more than one cert.
2784 * kTPx509Basic: CertGroup of length one allowed.
2786 CSSM_RETURN
tp_policyVerify(
2789 CSSM_CL_HANDLE clHand
,
2790 CSSM_CSP_HANDLE cspHand
,
2791 TPCertGroup
*certGroup
,
2792 CSSM_BOOL verifiedToRoot
, // last cert is good root
2793 CSSM_BOOL verifiedViaTrustSetting
, // last cert verified via
2795 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
2796 const CSSM_DATA
*policyFieldData
, // optional
2797 void *policyOpts
) // future options
2799 iSignCertInfo
*certInfo
= NULL
;
2801 iSignCertInfo
*thisCertInfo
;
2805 CSSM_BOOL cA
= CSSM_FALSE
; // init for compiler warning
2806 bool isLeaf
; // end entity
2807 bool isRoot
; // root cert
2808 CE_ExtendedKeyUsage
*extendUsage
;
2809 CE_AuthorityKeyID
*authorityId
;
2810 CSSM_KEY_PTR pubKey
;
2811 CSSM_RETURN outErr
= CSSM_OK
; // for gross, non-policy errors
2812 CSSM_BOOL policyFail
= CSSM_FALSE
;// generic CSSMERR_TP_VERIFY_ACTION_FAILED
2813 CSSM_RETURN policyError
= CSSM_OK
; // policy-specific failure
2815 /* First, kTPDefault is a nop here */
2816 if(policy
== kTPDefault
) {
2820 if(certGroup
== NULL
) {
2821 return CSSMERR_TP_INVALID_CERTGROUP
;
2823 numCerts
= certGroup
->numCerts();
2825 return CSSMERR_TP_INVALID_CERTGROUP
;
2827 if(policy
== kTPiSign
) {
2828 if(!verifiedToRoot
) {
2829 /* no way, this requires a root cert */
2830 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2833 /* nope, not for iSign */
2834 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2838 /* cook up an iSignCertInfo array */
2839 certInfo
= (iSignCertInfo
*)tpCalloc(alloc
, numCerts
, sizeof(iSignCertInfo
));
2840 /* subsequent errors to errOut: */
2842 /* fill it with interesting info from parsed certs */
2843 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2844 if(iSignGetCertInfo(alloc
,
2845 certGroup
->certAtIndex(certDex
),
2846 &certInfo
[certDex
])) {
2847 (certGroup
->certAtIndex(certDex
))->addStatusCode(
2848 CSSMERR_TP_INVALID_CERTIFICATE
);
2849 /* this one is fatal (and can't ignore) */
2850 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
2856 * OK, the heart of TP enforcement.
2858 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2859 thisCertInfo
= &certInfo
[certDex
];
2860 TPCertInfo
*thisTpCertInfo
= certGroup
->certAtIndex(certDex
);
2863 * First check for presence of required extensions and
2864 * critical extensions we don't understand.
2866 if(thisCertInfo
->foundUnknownCritical
) {
2867 /* illegal for all policies */
2868 tpPolicyError("tp_policyVerify: critical flag in unknown extension");
2869 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
)) {
2870 policyFail
= CSSM_TRUE
;
2875 * Check for unsupported key length, per <rdar://6892837>
2877 if((pubKey
=thisTpCertInfo
->pubKey()) != NULL
) {
2878 CSSM_KEYHEADER
*keyHdr
= &pubKey
->KeyHeader
;
2879 if(keyHdr
->AlgorithmId
== CSSM_ALGID_RSA
&& keyHdr
->LogicalKeySizeInBits
< 1024) {
2880 tpPolicyError("tp_policyVerify: RSA key size too small");
2881 if(thisTpCertInfo
->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
)) {
2882 policyFail
= CSSM_TRUE
;
2888 * Note it's possible for both of these to be true, for a chain
2889 * of length one (kTPx509Basic, kCrlPolicy only!)
2890 * FIXME: should this code work if the last cert in the chain is NOT a root?
2892 isLeaf
= thisTpCertInfo
->isLeaf();
2893 isRoot
= thisTpCertInfo
->isSelfSigned(true);
2896 * BasicConstraints.cA
2897 * iSign: required in all but leaf and root,
2898 * for which it is optional (with default values of false
2899 * for leaf and true for root).
2900 * all others: always optional, default of false for leaf and
2902 * All: cA must be false for leaf, true for others
2904 if(!thisCertInfo
->basicConstraints
.present
) {
2906 * No basicConstraints present; infer a cA value if appropriate.
2909 /* cool, use default; note that kTPx509Basic with
2910 * certGroup length of one may take this case */
2914 /* cool, use default */
2921 * not present, not leaf, not root....
2922 * ....RFC2459 says this can not be a CA
2927 /* required for iSign in this position */
2928 tpPolicyError("tp_policyVerify: no "
2929 "basicConstraints");
2930 if(thisTpCertInfo
->addStatusCode(
2931 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS
)) {
2932 policyFail
= CSSM_TRUE
;
2937 } /* inferred a default value */
2939 /* basicConstraints present */
2940 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
2941 /* disabled for verisign compatibility */
2942 if(!thisCertInfo
->basicConstraints
.critical
) {
2944 tpPolicyError("tp_policyVerify: basicConstraints marked "
2946 if(thisTpCertInfo
->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED
)) {
2947 policyFail
= CSSM_TRUE
;
2950 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
2952 const CE_BasicConstraints
*bcp
=
2953 &thisCertInfo
->basicConstraints
.extnData
->basicConstraints
;
2957 /* Verify pathLenConstraint if present */
2958 if(!isLeaf
&& // leaf, certDex=0, don't care
2959 cA
&& // p.l.c. only valid for CAs
2960 bcp
->pathLenConstraintPresent
) { // present?
2962 * pathLenConstraint=0 legal for certDex 1 only
2963 * pathLenConstraint=1 legal for certDex {1,2}
2966 if(certDex
> (bcp
->pathLenConstraint
+ 1)) {
2967 tpPolicyError("tp_policyVerify: pathLenConstraint "
2969 if(thisTpCertInfo
->addStatusCode(
2970 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT
)) {
2971 policyFail
= CSSM_TRUE
;
2979 * Special cases to allow a chain of length 1, leaf and root
2980 * both true, and for caller to override the "leaf can't be a CA"
2981 * requirement when a CA cert is explicitly being evaluated as the
2985 !(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
)) {
2986 tpPolicyError("tp_policyVerify: cA true for leaf");
2987 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2988 policyFail
= CSSM_TRUE
;
2992 tpPolicyError("tp_policyVerify: cA false for non-leaf");
2993 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2994 policyFail
= CSSM_TRUE
;
2999 * Authority Key Identifier optional
3000 * iSign : only allowed in !root.
3001 * If present, must not be critical.
3002 * all others : ignored (though used later for chain verification)
3004 if((policy
== kTPiSign
) && thisCertInfo
->authorityId
.present
) {
3006 tpPolicyError("tp_policyVerify: authorityId in root");
3007 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
3008 policyFail
= CSSM_TRUE
;
3011 if(thisCertInfo
->authorityId
.critical
) {
3012 /* illegal per RFC 2459 */
3013 tpPolicyError("tp_policyVerify: authorityId marked "
3015 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
3016 policyFail
= CSSM_TRUE
;
3022 * Subject Key Identifier optional
3023 * iSign : can't be critical.
3024 * all others : ignored (though used later for chain verification)
3026 if(thisCertInfo
->subjectId
.present
) {
3027 if((policy
== kTPiSign
) && thisCertInfo
->subjectId
.critical
) {
3028 tpPolicyError("tp_policyVerify: subjectId marked critical");
3029 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID
)) {
3030 policyFail
= CSSM_TRUE
;
3036 * Key Usage optional except required as noted
3037 * iSign : required for non-root/non-leaf
3038 * Leaf cert : if present, usage = digitalSignature
3039 * Exception : if leaf, and keyUsage not present,
3040 * netscape-cert-type must be present, with
3041 * Object Signing bit set
3042 * kCrlPolicy : Leaf: usage = CRLSign
3043 * kTP_SMIME : if present, must be critical
3044 * kTP_SWUpdateSign, kTP_ResourceSign, kTP_CodeSigning, kTP_PackageSigning : Leaf :
3045 usage = digitalSignature
3046 * all others : non-leaf : usage = keyCertSign
3049 if(thisCertInfo
->keyUsage
.present
) {
3052 * iSign and *Signing: usage = digitalSignature
3053 * all others : don't care
3054 * Others: usage = keyCertSign
3055 * We only require that one bit to be set, we ignore others.
3060 case kTP_SWUpdateSign
:
3061 case kTP_ResourceSign
:
3062 case kTP_CodeSigning
:
3063 case kTP_PackageSigning
:
3064 expUsage
= CE_KU_DigitalSignature
;
3067 /* if present, this bit must be set */
3068 expUsage
= CE_KU_CRLSign
;
3071 /* accept whatever's there */
3072 expUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
3077 /* !leaf: this is true for all policies */
3078 expUsage
= CE_KU_KeyCertSign
;
3080 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
3081 if(!(actUsage
& expUsage
)) {
3082 tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
3084 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
3085 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3086 policyFail
= CSSM_TRUE
;
3092 * Radar 3523221 renders this whole check obsolete, but I'm leaving
3093 * the code here to document its conspicuous functional absence.
3095 if((policy
== kTP_SMIME
) && !thisCertInfo
->keyUsage
.critical
) {
3097 * Per Radar 3410245, allow this for intermediate certs.
3099 if(SMIME_KEY_USAGE_MUST_BE_CRITICAL
|| isLeaf
|| isRoot
) {
3100 tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
3101 if(thisTpCertInfo
->addStatusCode(
3102 CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL
)) {
3103 policyFail
= CSSM_TRUE
;
3109 else if(policy
== kTPiSign
) {
3111 * iSign requires keyUsage present for non root OR
3112 * netscape-cert-type/ObjectSigning for leaf
3114 if(isLeaf
&& thisCertInfo
->netscapeCertType
.present
) {
3115 CE_NetscapeCertType ct
=
3116 thisCertInfo
->netscapeCertType
.extnData
->netscapeCertType
;
3118 if(!(ct
& CE_NCT_ObjSign
)) {
3119 tpPolicyError("tp_policyVerify: netscape-cert-type, "
3121 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3122 policyFail
= CSSM_TRUE
;
3127 tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
3128 "!(leaf and netscapeCertType)");
3129 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3130 policyFail
= CSSM_TRUE
;
3136 * RFC 3280, 4.1.2.6, says that an empty subject name can only appear in a
3137 * leaf cert, and only if subjectAltName is present and marked critical.
3139 if(isLeaf
&& thisTpCertInfo
->hasEmptySubjectName()) {
3140 bool badEmptySubject
= false;
3141 if(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) {
3143 * True when evaluating a CA cert as well as when
3144 * evaluating a CRL's cert chain. Note the odd case of a CRL's
3145 * signer having an empty subject matching an empty issuer
3146 * in the CRL. That'll be caught here.
3148 badEmptySubject
= true;
3150 else if(!thisCertInfo
->subjectAltName
.present
|| /* no subjectAltName */
3151 !thisCertInfo
->subjectAltName
.critical
) { /* not critical */
3152 badEmptySubject
= true;
3154 if(badEmptySubject
) {
3155 tpPolicyError("tp_policyVerify: bad empty subject");
3156 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT
)) {
3157 policyFail
= CSSM_TRUE
;
3163 * RFC 3739: if this cert has a Qualified Cert Statements extension, and
3164 * it's Critical, make sure we understand all of the extension's statementIds.
3166 if(thisCertInfo
->qualCertStatements
.present
&&
3167 thisCertInfo
->qualCertStatements
.critical
) {
3168 CE_QC_Statements
*qcss
=
3169 &thisCertInfo
->qualCertStatements
.extnData
->qualifiedCertStatements
;
3170 uint32 numQcs
= qcss
->numQCStatements
;
3171 for(unsigned qdex
=0; qdex
<numQcs
; qdex
++) {
3172 CSSM_OID_PTR qid
= &qcss
->qcStatements
[qdex
].statementId
;
3174 for(unsigned kdex
=0; kdex
<NUM_KNOWN_QUAL_CERT_STATEMENTS
; kdex
++) {
3175 if(tpCompareCssmData(qid
, knownQualifiedCertStatements
[kdex
])) {
3181 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT
)) {
3182 policyFail
= CSSM_TRUE
;
3187 } /* critical Qualified Cert Statement */
3190 * Certificate Policies extension validation, per section 1.2 of:
3191 * http://iase.disa.mil/pki/dod_cp_v10_final_2_mar_09_signed.pdf
3193 if (tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH
, false) ||
3194 tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH_2048
, false)) {
3196 * Certificate asserts one of the PIV-Auth Certificate Policy OIDs;
3197 * check the required Key Usage extension for compliance.
3200 * usage = digitalSignature (only; no other bits asserted)
3202 * usage = keyCertSign (required; other bits ignored)
3204 if(thisCertInfo
->keyUsage
.present
) {
3205 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
3207 /* No key usage! Policy fail. */
3210 if(!(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) && (certDex
== 0)) {
3211 expUsage
= CE_KU_DigitalSignature
;
3213 expUsage
= actUsage
| CE_KU_KeyCertSign
;
3215 if(!(actUsage
== expUsage
)) {
3216 tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
3218 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
3219 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
3220 policyFail
= CSSM_TRUE
;
3223 } /* Certificate Policies */
3226 } /* for certDex, checking presence of extensions */
3229 * Special case checking for leaf (end entity) cert
3231 * iSign only: Extended key usage, optional for leaf,
3232 * value CSSMOID_ExtendedUseCodeSigning
3234 if((policy
== kTPiSign
) && certInfo
[0].extendKeyUsage
.present
) {
3235 extendUsage
= &certInfo
[0].extendKeyUsage
.extnData
->extendedKeyUsage
;
3236 if(extendUsage
->numPurposes
!= 1) {
3237 tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
3239 (int)extendUsage
->numPurposes
);
3240 if((certGroup
->certAtIndex(0))->addStatusCode(
3241 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
3242 policyFail
= CSSM_TRUE
;
3245 if(!tpCompareOids(extendUsage
->purposes
,
3246 &CSSMOID_ExtendedUseCodeSigning
)) {
3247 tpPolicyError("tp_policyVerify: bad extendKeyUsage");
3248 if((certGroup
->certAtIndex(0))->addStatusCode(
3249 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
3250 policyFail
= CSSM_TRUE
;
3256 * Verify authorityId-->subjectId linkage.
3257 * All optional - skip if needed fields not present.
3258 * Also, always skip last (root) cert.
3260 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
3261 if(!certInfo
[certDex
].authorityId
.present
||
3262 !certInfo
[certDex
+1].subjectId
.present
) {
3265 authorityId
= &certInfo
[certDex
].authorityId
.extnData
->authorityKeyID
;
3266 if(!authorityId
->keyIdentifierPresent
) {
3267 /* we only know how to compare keyIdentifier */
3270 if(!tpCompareCssmData(&authorityId
->keyIdentifier
,
3271 &certInfo
[certDex
+1].subjectId
.extnData
->subjectKeyID
)) {
3272 tpPolicyError("tp_policyVerify: bad key ID linkage");
3273 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
3274 CSSMERR_APPLETP_INVALID_ID_LINKAGE
)) {
3275 policyFail
= CSSM_TRUE
;
3281 * Check signature algorithm on all non-root certs,
3282 * reject if known to be untrusted
3284 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
3285 if(certInfo
[certDex
].untrustedSigAlg
) {
3286 tpPolicyError("tp_policyVerify: untrusted signature algorithm");
3287 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
3288 CSSMERR_TP_INVALID_CERTIFICATE
)) {
3289 policyFail
= CSSM_TRUE
;
3294 /* specific per-policy checking */
3300 * SSL, EAP, IPSec: optionally verify common name; all are identical
3301 * other than their names.
3302 * FIXME - should this be before or after the root cert test? How can
3303 * we return both errors?
3305 policyError
= tp_verifySslOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3309 tpDebug("iChat policy");
3312 policyError
= tp_verifySmimeOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3314 case kTP_SWUpdateSign
:
3315 policyError
= tp_verifySWUpdateSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3317 case kTP_ResourceSign
:
3318 policyError
= tp_verifyResourceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3320 case kTP_CodeSigning
:
3321 case kTP_PackageSigning
:
3322 policyError
= tp_verifyCodePkgSignOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
3324 case kTP_MacAppStoreRec
:
3325 policyError
= tp_verifyMacAppStoreReceiptOpts(*certGroup
, policyFieldData
, certInfo
);
3327 case kTP_AppleIDSharing
:
3328 policyError
= tp_verifyAppleIDSharingOpts(*certGroup
, policyFieldData
, certInfo
);
3330 case kTP_TimeStamping
:
3331 policyError
= tp_verifyTimeStampingOpts(*certGroup
, policyFieldData
, certInfo
);
3333 case kTP_PassbookSigning
:
3334 policyError
= tp_verifyPassbookSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3336 case kTP_MobileStore
:
3337 policyError
= tp_verifyMobileStoreSigningOpts(*certGroup
, policyFieldData
, certInfo
, false);
3339 case kTP_TestMobileStore
:
3340 policyError
= tp_verifyMobileStoreSigningOpts(*certGroup
, policyFieldData
, certInfo
, true);
3342 case kTP_EscrowService
:
3343 policyError
= tp_verifyEscrowServiceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3345 case kTP_ProfileSigning
:
3346 policyError
= tp_verifyProfileSigningOpts(*certGroup
, policyFieldData
, certInfo
, false);
3348 case kTP_QAProfileSigning
:
3349 policyError
= tp_verifyProfileSigningOpts(*certGroup
, policyFieldData
, certInfo
, true);
3351 case kTP_PCSEscrowService
:
3352 policyError
= tp_verifyPCSEscrowServiceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3354 case kTP_ProvisioningProfileSigning
:
3355 policyError
= tp_verifyProvisioningProfileSigningOpts(*certGroup
, policyFieldData
, certInfo
);
3360 case kTP_PKINIT_Client
:
3366 if(outErr
== CSSM_OK
) {
3367 /* policy-specific error takes precedence here */
3368 if(policyError
!= CSSM_OK
) {
3369 outErr
= policyError
;
3371 else if(policyFail
) {
3372 /* plain vanilla error return from this module */
3373 outErr
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
3377 /* free resources */
3378 for(certDex
=0; certDex
<numCerts
; certDex
++) {
3379 thisCertInfo
= &certInfo
[certDex
];
3380 iSignFreeCertInfo(clHand
, thisCertInfo
);
3382 tpFree(alloc
, certInfo
);
3387 * Obtain policy-specific User Trust parameters
3389 void tp_policyTrustSettingParams(
3391 const CSSM_DATA
*policyData
, // optional
3392 /* returned values - not mallocd */
3393 const char **policyStr
,
3394 uint32
*policyStrLen
,
3395 SecTrustSettingsKeyUsage
*keyUse
)
3397 /* default values */
3399 *keyUse
= kSecTrustSettingsKeyUseAny
;
3401 if((policyData
== NULL
) || (policyData
->Data
== NULL
)) {
3402 /* currently, no further action possible */
3410 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
3411 /* this error will be caught later */
3414 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
=
3415 (CSSM_APPLE_TP_SSL_OPTIONS
*)policyData
->Data
;
3416 *policyStr
= sslOpts
->ServerName
;
3417 *policyStrLen
= sslOpts
->ServerNameLen
;
3418 if(sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
) {
3420 * Client signs with its priv key. Server end,
3421 * which (also) verifies the client cert, verifies.
3423 *keyUse
= kSecTrustSettingsKeyUseSignature
;
3426 /* server decrypts */
3427 *keyUse
= kSecTrustSettingsKeyUseEnDecryptKey
;
3435 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
3436 /* this error will be caught later */
3439 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
=
3440 (CSSM_APPLE_TP_SMIME_OPTIONS
*)policyData
->Data
;
3441 *policyStr
= smimeOpts
->SenderEmail
;
3442 *policyStrLen
= smimeOpts
->SenderEmailLen
;
3443 SecTrustSettingsKeyUsage ku
= 0;
3444 CE_KeyUsage smimeKu
= smimeOpts
->IntendedUsage
;
3445 if(smimeKu
& (CE_KU_DigitalSignature
| CE_KU_KeyCertSign
| CE_KU_CRLSign
)) {
3446 ku
|= kSecTrustSettingsKeyUseSignature
;
3448 if(smimeKu
& (CE_KU_KeyEncipherment
| CE_KU_DataEncipherment
)) {
3449 ku
|= kSecTrustSettingsKeyUseEnDecryptKey
;
3456 /* no other options */
3461 #pragma clang diagnostic pop