2 * Copyright (c) 2000-2012 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>
38 #include <CoreFoundation/CFString.h>
42 * Our private per-extension info. One of these per (understood) extension per
48 CE_Data
*extnData
; // mallocd by CL
49 CSSM_DATA
*valToFree
; // the data we pass to freeField()
53 * Struct to keep track of info pertinent to one cert.
57 /* extensions we're interested in */
58 iSignExtenInfo authorityId
;
59 iSignExtenInfo subjectId
;
60 iSignExtenInfo keyUsage
;
61 iSignExtenInfo extendKeyUsage
;
62 iSignExtenInfo basicConstraints
;
63 iSignExtenInfo netscapeCertType
;
64 iSignExtenInfo subjectAltName
;
65 iSignExtenInfo certPolicies
;
66 iSignExtenInfo qualCertStatements
;
67 iSignExtenInfo nameConstraints
;
68 iSignExtenInfo policyMappings
;
69 iSignExtenInfo policyConstraints
;
70 iSignExtenInfo inhibitAnyPolicy
;
71 iSignExtenInfo certificatePolicies
;
73 /* flag indicating presence of a critical extension we don't understand */
74 CSSM_BOOL foundUnknownCritical
;
75 /* flag indicating that this certificate was signed with a known-broken algorithm */
76 CSSM_BOOL untrustedSigAlg
;
81 * The list of Qualified Cert Statement statementIds we understand, even though
82 * we don't actually do anything with them; if these are found in a Qualified
83 * Cert Statement that's critical, we can truthfully say "yes we understand this".
85 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
87 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
,
88 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
,
89 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
90 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
91 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
92 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
94 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
96 static CSSM_RETURN
tp_verifyMacAppStoreReciptOpts(TPCertGroup
&certGroup
,
97 const CSSM_DATA
*fieldOpts
, const iSignCertInfo
*certInfo
);
98 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
);
101 * Certificate policy OIDs
105 #define ANY_POLICY_OID OID_EXTENSION, 0x32, 0x00
106 #define ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 2
109 #define INHIBIT_ANY_POLICY_OID OID_EXTENSION, 0x54
110 #define INHIBIT_ANY_POLICY_OID_LEN OID_EXTENSION_LENGTH + 1
112 /* 2.16.840.1.101.2.1 */
113 #define US_DOD_INFOSEC 0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
114 #define US_DOD_INFOSEC_LEN 7
116 /* 2.16.840.1.101.2.1.11.10 */
117 #define PIV_AUTH_OID US_DOD_INFOSEC, 0x0B, 0x0A
118 #define PIV_AUTH_OID_LEN US_DOD_INFOSEC_LEN + 2
120 /* 2.16.840.1.101.2.1.11.20 */
121 #define PIV_AUTH_2048_OID US_DOD_INFOSEC, 0x0B, 0x14
122 #define PIV_AUTH_2048_OID_LEN US_DOD_INFOSEC_LEN + 2
124 static const uint8 OID_ANY_POLICY
[] = {ANY_POLICY_OID
};
125 const CSSM_OID CSSMOID_ANY_POLICY
= {ANY_POLICY_OID_LEN
, (uint8
*)OID_ANY_POLICY
};
126 static const uint8 OID_INHIBIT_ANY_POLICY
[] = {INHIBIT_ANY_POLICY_OID
};
127 const CSSM_OID CSSMOID_INHIBIT_ANY_POLICY
= {INHIBIT_ANY_POLICY_OID_LEN
, (uint8
*)OID_INHIBIT_ANY_POLICY
};
128 static const uint8 OID_PIV_AUTH
[] = {PIV_AUTH_OID
};
129 const CSSM_OID CSSMOID_PIV_AUTH
= {PIV_AUTH_OID_LEN
, (uint8
*)OID_PIV_AUTH
};
130 static const uint8 OID_PIV_AUTH_2048
[] = {PIV_AUTH_2048_OID
};
131 const CSSM_OID CSSMOID_PIV_AUTH_2048
= {PIV_AUTH_2048_OID_LEN
, (uint8
*)OID_PIV_AUTH_2048
};
133 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
134 const CSSM_DATA
*fieldOpts
, // optional Common Name
135 const iSignCertInfo
*certInfo
);
137 * Setup a single iSignExtenInfo. Called once per known extension
140 static CSSM_RETURN
tpSetupExtension(
143 iSignExtenInfo
*extnInfo
) // which component of certInfo
145 if(extnData
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
146 tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
147 return CSSMERR_TP_UNKNOWN_FORMAT
;
149 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)extnData
->Data
;
150 extnInfo
->present
= CSSM_TRUE
;
151 extnInfo
->critical
= cssmExt
->critical
;
152 extnInfo
->extnData
= (CE_Data
*)cssmExt
->value
.parsedValue
;
153 extnInfo
->valToFree
= extnData
;
158 * Fetch a known extension, set up associated iSignExtenInfo if present.
160 static CSSM_RETURN
iSignFetchExtension(
163 const CSSM_OID
*fieldOid
, // which extension to fetch
164 iSignExtenInfo
*extnInfo
) // where the info goes
166 CSSM_DATA_PTR fieldValue
; // mallocd by CL
169 crtn
= tpCert
->fetchField(fieldOid
, &fieldValue
);
173 case CSSMERR_CL_NO_FIELD_VALUES
:
174 /* field not present, OK */
179 return tpSetupExtension(alloc
,
185 * This function performs a check of an extension marked 'critical'
186 * to see if it's one we understand. Returns CSSM_OK if the extension
187 * is acceptable, CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN if unknown.
189 static CSSM_RETURN
iSignVerifyCriticalExtension(
190 CSSM_X509_EXTENSION
*cssmExt
)
192 if (!cssmExt
|| !cssmExt
->extnId
.Data
)
193 return CSSMERR_TP_INVALID_FIELD_POINTER
;
195 if (!cssmExt
->critical
)
198 /* FIXME: remove when policyConstraints NSS template is fixed */
199 if (!memcmp(cssmExt
->extnId
.Data
, CSSMOID_PolicyConstraints
.Data
, CSSMOID_PolicyConstraints
.Length
))
202 if (cssmExt
->extnId
.Length
> APPLE_EXTENSION_OID_LENGTH
&&
203 !memcmp(cssmExt
->extnId
.Data
, CSSMOID_APPLE_EXTENSION
.Data
, APPLE_EXTENSION_OID_LENGTH
)) {
204 /* This extension's OID is under the appleCertificateExtensions arc */
208 return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
;
212 * Search for all unknown extensions. If we find one which is flagged critical,
213 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
215 static CSSM_RETURN
iSignSearchUnknownExtensions(
217 iSignCertInfo
*certInfo
)
220 CSSM_DATA_PTR fieldValue
= NULL
;
221 CSSM_HANDLE searchHand
= CSSM_INVALID_HANDLE
;
222 uint32 numFields
= 0;
224 crtn
= CSSM_CL_CertGetFirstCachedFieldValue(tpCert
->clHand(),
226 &CSSMOID_X509V3CertificateExtensionCStruct
,
232 /* found one, proceed */
234 case CSSMERR_CL_NO_FIELD_VALUES
:
235 /* no unknown extensions present, OK */
241 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
242 tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
243 return CSSMERR_TP_UNKNOWN_FORMAT
;
245 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
246 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
247 /* BRRZAPP! Found an unknown extension marked critical */
248 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
251 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
252 &CSSMOID_X509V3CertificateExtensionCStruct
,
256 /* process remaining unknown extensions */
257 for(unsigned i
=1; i
<numFields
; i
++) {
258 crtn
= CSSM_CL_CertGetNextCachedFieldValue(tpCert
->clHand(),
262 /* should never happen */
263 tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
267 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
268 tpPolicyError("iSignSearchUnknownExtensions: "
269 "malformed CSSM_FIELD");
270 crtn
= CSSMERR_TP_UNKNOWN_FORMAT
;
273 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
274 if(iSignVerifyCriticalExtension(cssmExt
) != CSSM_OK
) {
275 /* BRRZAPP! Found an unknown extension marked critical */
276 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
279 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
280 &CSSMOID_X509V3CertificateExtensionCStruct
,
283 } /* for additional fields */
287 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
288 &CSSMOID_X509V3CertificateExtensionCStruct
,
291 if(searchHand
!= CSSM_INVALID_HANDLE
) {
292 CSSM_CL_CertAbortQuery(tpCert
->clHand(), searchHand
);
298 * Check the signature algorithm. If it's known to be untrusted,
299 * flag certInfo->untrustedSigAlg.
301 static void iSignCheckSignatureAlgorithm(
303 iSignCertInfo
*certInfo
)
305 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
= NULL
;
306 CSSM_DATA_PTR valueToFree
= NULL
;
308 algId
= tp_CertGetAlgId(tpCert
, &valueToFree
);
310 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2
) ||
311 tpCompareCssmData(&algId
->algorithm
, &CSSMOID_MD2WithRSA
)) {
312 certInfo
->untrustedSigAlg
= CSSM_TRUE
;
314 certInfo
->untrustedSigAlg
= CSSM_FALSE
;
318 tp_CertFreeAlgId(tpCert
->clHand(), valueToFree
);
323 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
324 * Returns CSSM_FAIL on error.
326 static CSSM_RETURN
iSignGetCertInfo(
329 iSignCertInfo
*certInfo
)
333 /* first grind thru the extensions we're interested in */
334 crtn
= iSignFetchExtension(alloc
,
336 &CSSMOID_AuthorityKeyIdentifier
,
337 &certInfo
->authorityId
);
341 crtn
= iSignFetchExtension(alloc
,
343 &CSSMOID_SubjectKeyIdentifier
,
344 &certInfo
->subjectId
);
348 crtn
= iSignFetchExtension(alloc
,
351 &certInfo
->keyUsage
);
355 crtn
= iSignFetchExtension(alloc
,
357 &CSSMOID_ExtendedKeyUsage
,
358 &certInfo
->extendKeyUsage
);
362 crtn
= iSignFetchExtension(alloc
,
364 &CSSMOID_BasicConstraints
,
365 &certInfo
->basicConstraints
);
369 crtn
= iSignFetchExtension(alloc
,
371 &CSSMOID_NetscapeCertType
,
372 &certInfo
->netscapeCertType
);
376 crtn
= iSignFetchExtension(alloc
,
378 &CSSMOID_SubjectAltName
,
379 &certInfo
->subjectAltName
);
383 crtn
= iSignFetchExtension(alloc
,
385 &CSSMOID_CertificatePolicies
,
386 &certInfo
->certPolicies
);
390 crtn
= iSignFetchExtension(alloc
,
392 &CSSMOID_QC_Statements
,
393 &certInfo
->qualCertStatements
);
397 crtn
= iSignFetchExtension(alloc
,
399 &CSSMOID_NameConstraints
,
400 &certInfo
->nameConstraints
);
404 crtn
= iSignFetchExtension(alloc
,
406 &CSSMOID_PolicyMappings
,
407 &certInfo
->policyMappings
);
411 crtn
= iSignFetchExtension(alloc
,
413 &CSSMOID_PolicyConstraints
,
414 &certInfo
->policyConstraints
);
418 crtn
= iSignFetchExtension(alloc
,
420 &CSSMOID_InhibitAnyPolicy
,
421 &certInfo
->inhibitAnyPolicy
);
425 crtn
= iSignFetchExtension(alloc
,
427 &CSSMOID_CertificatePolicies
,
428 &certInfo
->certificatePolicies
);
433 /* check signature algorithm field */
434 iSignCheckSignatureAlgorithm(tpCert
, certInfo
);
436 /* now look for extensions we don't understand - the only thing we're interested
437 * in is the critical flag. */
438 return iSignSearchUnknownExtensions(tpCert
, certInfo
);
442 * Free (via CL) the fields allocated in iSignGetCertInfo().
444 static void iSignFreeCertInfo(
445 CSSM_CL_HANDLE clHand
,
446 iSignCertInfo
*certInfo
)
448 if(certInfo
->authorityId
.present
) {
449 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_AuthorityKeyIdentifier
,
450 certInfo
->authorityId
.valToFree
);
452 if(certInfo
->subjectId
.present
) {
453 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectKeyIdentifier
,
454 certInfo
->subjectId
.valToFree
);
456 if(certInfo
->keyUsage
.present
) {
457 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_KeyUsage
,
458 certInfo
->keyUsage
.valToFree
);
460 if(certInfo
->extendKeyUsage
.present
) {
461 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_ExtendedKeyUsage
,
462 certInfo
->extendKeyUsage
.valToFree
);
464 if(certInfo
->basicConstraints
.present
) {
465 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_BasicConstraints
,
466 certInfo
->basicConstraints
.valToFree
);
468 if(certInfo
->netscapeCertType
.present
) {
469 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_NetscapeCertType
,
470 certInfo
->netscapeCertType
.valToFree
);
472 if(certInfo
->subjectAltName
.present
) {
473 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectAltName
,
474 certInfo
->subjectAltName
.valToFree
);
476 if(certInfo
->certPolicies
.present
) {
477 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
478 certInfo
->certPolicies
.valToFree
);
480 // if(certInfo->policyConstraints.present) {
481 // CSSM_CL_FreeFieldValue(clHand, &CSSMOID_PolicyConstraints,
482 // certInfo->policyConstraints.valToFree);
484 if(certInfo
->qualCertStatements
.present
) {
485 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_QC_Statements
,
486 certInfo
->qualCertStatements
.valToFree
);
488 if(certInfo
->certificatePolicies
.present
) {
489 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_CertificatePolicies
,
490 certInfo
->certificatePolicies
.valToFree
);
495 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
496 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
497 * Also indicates whether *any* of the specified fields were found, regardless
501 SN_CommonName
, // CSSMOID_CommonName, host name format
502 SN_Email
// CSSMOID_EmailAddress
503 } SubjSubjNameSearchType
;
505 static CSSM_BOOL
tpCompareSubjectName(
507 SubjSubjNameSearchType searchType
,
508 bool normalizeAll
, // for SN_Email case: lower-case all of
509 // the cert's value, not just the portion
511 const char *callerStr
, // already tpToLower'd
515 char *certName
= NULL
; // from cert's subject name
516 uint32 certNameLen
= 0;
517 CSSM_DATA_PTR subjNameData
= NULL
;
519 CSSM_BOOL ourRtn
= CSSM_FALSE
;
520 const CSSM_OID
*oidSrch
;
525 oidSrch
= &CSSMOID_CommonName
;
528 oidSrch
= &CSSMOID_EmailAddress
;
534 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
536 /* should never happen, we shouldn't be here if there is no subject */
537 tpPolicyError("tpCompareSubjectName: error retrieving subject name");
540 CSSM_X509_NAME_PTR x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
541 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
542 tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
543 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
547 /* Now grunge thru the X509 name looking for a common name */
548 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
549 CSSM_X509_RDN_PTR rdnp
;
553 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
554 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
555 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
556 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
557 if(tpCompareOids(&ptvp
->type
, oidSrch
)) {
559 certName
= (char *)ptvp
->value
.Data
;
560 certNameLen
= ptvp
->value
.Length
;
564 /* handle odd encodings that we need to convert to 8-bit */
565 CFStringBuiltInEncodings encoding
;
566 CFDataRef cfd
= NULL
;
567 bool doConvert
= false;
568 switch(ptvp
->valueType
) {
569 case BER_TAG_T61_STRING
:
571 encoding
= kCFStringEncodingISOLatin1
;
574 case BER_TAG_PKIX_BMP_STRING
:
575 encoding
= kCFStringEncodingUnicode
;
579 * All others - either take as is, or let it fail due to
580 * illegal/incomprehensible format
586 /* raw data ==> CFString */
587 cfd
= CFDataCreate(NULL
, (UInt8
*)certName
, certNameLen
);
589 /* try next component */
592 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(
593 NULL
, cfd
, encoding
);
596 tpPolicyError("tpCompareSubjectName: bad str (1)");
600 /* CFString ==> straight ASCII */
601 cfd
= CFStringCreateExternalRepresentation(NULL
,
602 cfStr
, kCFStringEncodingASCII
, 0);
605 tpPolicyError("tpCompareSubjectName: bad str (2)");
608 certNameLen
= CFDataGetLength(cfd
);
609 certName
= (char *)CFDataGetBytePtr(cfd
);
611 ourRtn
= tpCompareHostNames(callerStr
, callerStrLen
,
612 certName
, certNameLen
);
620 ourRtn
= tpCompareEmailAddr(callerStr
, callerStrLen
,
621 certName
, certNameLen
, normalizeAll
);
628 /* else keep going, maybe there's another common name */
635 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
640 * Compare ASCII form of an IP address to a CSSM_DATA containing
641 * the IP address's numeric components. Returns true on match.
643 static CSSM_BOOL
tpCompIpAddrStr(
646 const CSSM_DATA
*numeric
)
648 const char *cp
= str
;
652 if((numeric
== NULL
) || (numeric
->Length
== 0) || (str
== NULL
)) {
655 if(cp
[strLen
- 1] == '\0') {
656 /* ignore NULL terminator */
659 for(unsigned dex
=0; dex
<numeric
->Length
; dex
++) {
660 /* cp points to start of current string digit */
662 const char *lastChar
= cp
+ strLen
;
664 for( ; nextDot
<lastChar
; nextDot
++) {
665 if(*nextDot
== '.') {
669 if(nextDot
== lastChar
) {
670 /* legal and required on last digit */
671 if(dex
!= (numeric
->Length
- 1)) {
675 else if(dex
== (numeric
->Length
- 1)) {
678 unsigned digLen
= nextDot
- cp
;
679 if(digLen
>= sizeof(buf
)) {
683 memmove(buf
, cp
, digLen
);
685 /* incr digLen to include the next dot */
689 int digVal
= atoi(buf
);
690 if(digVal
!= numeric
->Data
[dex
]) {
698 * See if cert's subjectAltName contains an element matching caller-specified
699 * string, hostname, in the following forms:
701 * SAN_HostName : dnsName, iPAddress
702 * SAN_Email : RFC822Name
704 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
706 * Also indicates whether or not a dnsName (search type HostName) or
707 * RFC822Name (search type SAM_Email) was found, regardless of result
710 * The appStr/appStrLen args are optional - if NULL/0, only the
711 * search for dnsName/RFC822Name is done.
716 } SubjAltNameSearchType
;
718 static CSSM_BOOL
tpCompareSubjectAltName(
719 const iSignExtenInfo
&subjAltNameInfo
,
720 const char *appStr
, // caller has lower-cased as appropriate
722 SubjAltNameSearchType searchType
,
723 bool normalizeAll
, // for SAN_Email case: lower-case all of
724 // the cert's value, not just the portion
726 bool &dnsNameFound
, // RETURNED, SAN_HostName case
727 bool &emailFound
) // RETURNED, SAN_Email case
729 dnsNameFound
= false;
731 if(!subjAltNameInfo
.present
) {
732 /* common failure, no subjectAltName found */
736 CE_GeneralNames
*names
= &subjAltNameInfo
.extnData
->subjectAltName
;
737 CSSM_BOOL ourRtn
= CSSM_FALSE
;
739 unsigned certNameLen
;
741 /* Search thru the CE_GeneralNames looking for the appropriate attribute */
742 for(unsigned dex
=0; dex
<names
->numNames
; dex
++) {
743 CE_GeneralName
*name
= &names
->generalName
[dex
];
746 switch(name
->nameType
) {
749 /* nothing to do here */
752 ourRtn
= tpCompIpAddrStr(appStr
, appStrLen
, &name
->name
);
756 if(name
->berEncoded
) {
757 tpErrorLog("tpCompareSubjectAltName: malformed "
758 "CE_GeneralName (1)\n");
761 certName
= (char *)name
->name
.Data
;
762 if(certName
== NULL
) {
763 tpErrorLog("tpCompareSubjectAltName: malformed "
764 "CE_GeneralName (2)\n");
767 certNameLen
= name
->name
.Length
;
770 /* skip if caller passed in NULL */
771 ourRtn
= tpCompareHostNames(appStr
, appStrLen
,
772 certName
, certNameLen
);
777 /* not interested, proceed to next name */
780 break; /* from case HostName */
783 if(name
->nameType
!= GNT_RFC822Name
) {
787 certName
= (char *)name
->name
.Data
;
788 if(certName
== NULL
) {
789 tpErrorLog("tpCompareSubjectAltName: malformed "
793 certNameLen
= name
->name
.Length
;
796 ourRtn
= tpCompareEmailAddr(appStr
, appStrLen
, certName
,
797 certNameLen
, normalizeAll
);
809 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
810 static CSSM_BOOL
tpIsNumeric(
811 const char *hostName
,
812 unsigned hostNameLen
)
814 if(hostName
[hostNameLen
- 1] == '\0') {
815 /* ignore NULL terminator */
818 for(unsigned i
=0; i
<hostNameLen
; i
++) {
819 char c
= *hostName
++;
831 * Convert a typed string represented by a CSSM_X509_TYPE_VALUE_PAIR to a
832 * CFStringRef. Caller owns and must release the result. NULL return means
833 * unconvertible input "string".
835 static CFStringRef
tpTvpToCfString(
836 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
)
838 CFStringBuiltInEncodings encoding
;
839 switch(tvp
->valueType
) {
840 case BER_TAG_T61_STRING
:
842 encoding
= kCFStringEncodingISOLatin1
;
844 case BER_TAG_PKIX_BMP_STRING
:
845 encoding
= kCFStringEncodingUnicode
;
847 case BER_TAG_PRINTABLE_STRING
:
848 case BER_TAG_IA5_STRING
:
849 case BER_TAG_PKIX_UTF8_STRING
:
850 encoding
= kCFStringEncodingUTF8
;
856 /* raw data ==> CFString */
857 CFDataRef cfd
= CFDataCreate(NULL
, tvp
->value
.Data
, tvp
->value
.Length
);
861 CFStringRef cfStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, encoding
);
867 * Compare a CFString and a string represented by a CSSM_X509_TYPE_VALUE_PAIR.
868 * Returns CSSM_TRUE if they are equal.
870 static bool tpCompareTvpToCfString(
871 const CSSM_X509_TYPE_VALUE_PAIR
*tvp
,
873 CFOptionFlags flags
) // e.g., kCFCompareCaseInsensitive
875 CFStringRef cfStr
= tpTvpToCfString(tvp
);
879 CFComparisonResult res
= CFStringCompare(refStr
, cfStr
, flags
);
881 if(res
== kCFCompareEqualTo
) {
890 * Given one iSignCertInfo, determine whether or not the specified
891 * EKU OID, or - optionally - CSSMOID_ExtendedKeyUsageAny - is present.
892 * Returns true if so, else false.
894 static bool tpVerifyEKU(
895 const iSignCertInfo
&certInfo
,
896 const CSSM_OID
&ekuOid
,
897 bool ekuAnyOK
) // if true, CSSMOID_ExtendedKeyUsageAny counts as "found"
899 if(!certInfo
.extendKeyUsage
.present
) {
902 CE_ExtendedKeyUsage
*eku
= &certInfo
.extendKeyUsage
.extnData
->extendedKeyUsage
;
905 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
906 const CSSM_OID
*foundEku
= &eku
->purposes
[i
];
907 if(tpCompareOids(foundEku
, &ekuOid
)) {
910 if(ekuAnyOK
&& tpCompareOids(foundEku
, &CSSMOID_ExtendedKeyUsageAny
)) {
918 * Given one iSignCertInfo, determine whether or not the specified
919 * Certificate Policy OID, or - optionally - CSSMOID_ANY_POLICY - is present.
920 * Returns true if so, else false.
922 static bool tpVerifyCPE(
923 const iSignCertInfo
&certInfo
,
924 const CSSM_OID
&cpOid
,
925 bool anyPolicyOK
) // if true, CSSMOID_ANY_POLICY counts as "found"
927 if(!certInfo
.certPolicies
.present
) {
930 CE_CertPolicies
*cp
= &certInfo
.certPolicies
.extnData
->certPolicies
;
933 for(unsigned i
=0; i
<cp
->numPolicies
; i
++) {
934 const CE_PolicyInformation
*foundPolicy
= &cp
->policies
[i
];
935 if(tpCompareOids(&foundPolicy
->certPolicyId
, &cpOid
)) {
938 if(anyPolicyOK
&& tpCompareOids(&foundPolicy
->certPolicyId
, &CSSMOID_ANY_POLICY
)) {
946 * Verify iChat handle. We search for a matching (case-insensitive) string
949 * -- name component ("dmitch") from subject name's CommonName
951 * -- domain name from subject name's organizationalUnit
953 * Plus we require an Organization component of "Apple Computer, Inc." or "Apple Inc."
955 static bool tpCompareIChatHandleName(
957 const char *iChatHandle
, // UTF8
958 uint32 iChatHandleLen
)
960 CSSM_DATA_PTR subjNameData
= NULL
; // from fetchField
963 CSSM_X509_NAME_PTR x509name
;
964 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
965 CSSM_X509_RDN_PTR rdnp
;
969 /* search until all of these are true */
970 CSSM_BOOL commonNameMatch
= CSSM_FALSE
; // name before '@'
971 CSSM_BOOL orgUnitMatch
= CSSM_FALSE
; // domain after '@
972 CSSM_BOOL orgMatch
= CSSM_FALSE
; // Apple Computer, Inc. (or Apple Inc.)
975 * incoming UTF8 handle ==> two components.
976 * First convert to CFString.
978 if(iChatHandle
[iChatHandleLen
- 1] == '\0') {
979 /* avoid NULL when creating CFStrings */
982 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)iChatHandle
, iChatHandleLen
);
986 CFStringRef handleStr
= CFStringCreateFromExternalRepresentation(NULL
, cfd
,
987 kCFStringEncodingUTF8
);
989 if(handleStr
== NULL
) {
990 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
995 * Find the '@' delimiter
998 whereIsAt
= CFStringFind(handleStr
, CFSTR("@"), 0);
999 if(whereIsAt
.length
== 0) {
1000 tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
1001 CFRelease(handleStr
);
1006 * Two components, before and after delimiter
1008 CFRange r
= {0, whereIsAt
.location
};
1009 CFStringRef iChatName
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1010 if(iChatName
== NULL
) {
1011 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
1012 CFRelease(handleStr
);
1015 r
.location
= whereIsAt
.location
+ 1; // after the '@'
1016 r
.length
= CFStringGetLength(handleStr
) - r
.location
;
1017 CFStringRef iChatDomain
= CFStringCreateWithSubstring(NULL
, handleStr
, r
);
1018 CFRelease(handleStr
);
1019 if(iChatDomain
== NULL
) {
1020 tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
1021 CFRelease(iChatName
);
1024 /* subsequent errors to errOut: */
1026 /* get subject name in CSSM form, all subsequent ops work on that */
1027 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1029 /* should never happen, we shouldn't be here if there is no subject */
1030 tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
1034 x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
1035 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
1036 tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
1040 /* Now grunge thru the X509 name looking for three fields */
1042 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
1043 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
1044 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
1045 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
1046 if(!commonNameMatch
&&
1047 tpCompareOids(&ptvp
->type
, &CSSMOID_CommonName
) &&
1048 tpCompareTvpToCfString(ptvp
, iChatName
, kCFCompareCaseInsensitive
)) {
1049 commonNameMatch
= CSSM_TRUE
;
1053 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationalUnitName
) &&
1054 tpCompareTvpToCfString(ptvp
, iChatDomain
, kCFCompareCaseInsensitive
)) {
1055 orgUnitMatch
= CSSM_TRUE
;
1059 tpCompareOids(&ptvp
->type
, &CSSMOID_OrganizationName
) &&
1060 /* this one is case sensitive */
1061 (tpCompareTvpToCfString(ptvp
, CFSTR("Apple Computer, Inc."), 0) ||
1062 tpCompareTvpToCfString(ptvp
, CFSTR("Apple Inc."), 0))) {
1063 orgMatch
= CSSM_TRUE
;
1066 if(commonNameMatch
&& orgUnitMatch
&& orgMatch
) {
1074 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1075 CFRelease(iChatName
);
1076 CFRelease(iChatDomain
);
1081 * Verify SSL options. Currently this just consists of matching the
1082 * leaf cert's subject common name against the caller's (optional)
1085 static CSSM_RETURN
tp_verifySslOpts(
1087 TPCertGroup
&certGroup
,
1088 const CSSM_DATA
*sslFieldOpts
,
1089 const iSignCertInfo
&leafCertInfo
)
1091 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= NULL
;
1092 unsigned hostNameLen
= 0;
1093 const char *serverName
= NULL
;
1094 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1095 assert(leaf
!= NULL
);
1097 /* CSSM_APPLE_TP_SSL_OPTIONS is optional */
1098 if((sslFieldOpts
!= NULL
) && (sslFieldOpts
->Data
!= NULL
)) {
1099 sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)sslFieldOpts
->Data
;
1100 switch(sslOpts
->Version
) {
1101 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
1102 if(sslFieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
1103 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1106 /* handle backwards compatibility here if necessary */
1108 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1110 hostNameLen
= sslOpts
->ServerNameLen
;
1111 serverName
= sslOpts
->ServerName
;
1114 /* host name check is optional */
1115 if(hostNameLen
!= 0) {
1116 if(serverName
== NULL
) {
1117 return CSSMERR_TP_INVALID_POINTER
;
1120 /* convert caller's hostname string to lower case */
1121 char *hostName
= (char *)certGroup
.alloc().malloc(hostNameLen
);
1122 memmove(hostName
, serverName
, hostNameLen
);
1123 tpToLower(hostName
, hostNameLen
);
1125 CSSM_BOOL match
= CSSM_FALSE
;
1127 /* First check subjectAltName... */
1128 bool dnsNameFound
= false;
1130 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1131 hostName
, hostNameLen
,
1132 SAN_HostName
, false, dnsNameFound
, dummy
);
1135 * Then common name, if
1136 * -- no match from subjectAltName, AND
1137 * -- dnsName was NOT found, AND
1138 * -- hostName is not strictly numeric form (1.2.3.4)
1140 if(!match
&& !dnsNameFound
&& !tpIsNumeric(hostName
, hostNameLen
)) {
1142 match
= tpCompareSubjectName(*leaf
, SN_CommonName
, false, hostName
, hostNameLen
,
1145 certGroup
.alloc().free(hostName
);
1147 if(leaf
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
)) {
1148 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1154 * Ensure that, if an extendedKeyUsage extension is present in the
1155 * leaf, that either anyExtendedKeyUsage or the appropriate
1156 * CSSMOID_{Server,Client}Auth, or a SeverGatedCrypto usage is present.
1158 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1159 if(ekuInfo
.present
) {
1160 bool foundGoodEku
= false;
1161 bool isServer
= true;
1162 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1163 assert(eku
!= NULL
);
1166 * Determine appropriate extended key usage; default is SSL server
1168 const CSSM_OID
*extUse
= &CSSMOID_ServerAuth
;
1169 if((sslOpts
!= NULL
) && /* optional, default server side */
1170 (sslOpts
->Version
> 0) && /* this was added in struct version 1 */
1171 (sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
)) {
1172 extUse
= &CSSMOID_ClientAuth
;
1176 /* search for that one or for "any" indicator */
1177 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1178 const CSSM_OID
*purpose
= &eku
->purposes
[i
];
1179 if(tpCompareOids(purpose
, extUse
)) {
1180 foundGoodEku
= true;
1183 if(tpCompareOids(purpose
, &CSSMOID_ExtendedKeyUsageAny
)) {
1184 foundGoodEku
= true;
1187 if((policy
== kTP_IPSec
) && (tpCompareOids(purpose
, &CSSMOID_EKU_IPSec
))) {
1188 foundGoodEku
= true;
1192 /* server gated crypto: server side only */
1193 if(tpCompareOids(purpose
, &CSSMOID_NetscapeSGC
)) {
1194 foundGoodEku
= true;
1197 if(tpCompareOids(purpose
, &CSSMOID_MicrosoftSGC
)) {
1198 foundGoodEku
= true;
1204 if(leaf
->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE
)) {
1205 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1213 * Verify SMIME and iChat options.
1214 * This deals with both S/MIME and iChat policies; within the iChat domain it
1215 * deals with Apple-specific .mac certs as well as what we call "generic AIM"
1216 * certs, as used in the Windows AIM client.
1218 #define CE_CIPHER_MASK (~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
1220 static CSSM_RETURN
tp_verifySmimeOpts(
1222 TPCertGroup
&certGroup
,
1223 const CSSM_DATA
*smimeFieldOpts
,
1224 const iSignCertInfo
&leafCertInfo
)
1226 bool iChat
= (policy
== kTP_iChat
) ? true : false;
1229 * The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1231 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
= NULL
;
1232 if(smimeFieldOpts
!= NULL
) {
1233 smimeOpts
= (CSSM_APPLE_TP_SMIME_OPTIONS
*)smimeFieldOpts
->Data
;
1235 if(smimeOpts
!= NULL
) {
1236 switch(smimeOpts
->Version
) {
1237 case CSSM_APPLE_TP_SMIME_OPTS_VERSION
:
1238 if(smimeFieldOpts
->Length
!=
1239 sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
1240 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1243 /* handle backwards compatibility here if necessary */
1245 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1249 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1250 assert(leaf
!= NULL
);
1252 /* Verify optional email address, a.k.a. handle for iChat policy */
1253 unsigned emailLen
= 0;
1254 if(smimeOpts
!= NULL
) {
1255 emailLen
= smimeOpts
->SenderEmailLen
;
1259 bool emailFoundInSAN
= false;
1260 bool iChatHandleFound
= false; /* indicates a genuine Apple iChat cert */
1261 bool emailFoundInDN
= false;
1263 if(smimeOpts
->SenderEmail
== NULL
) {
1264 return CSSMERR_TP_INVALID_POINTER
;
1267 /* iChat - first try the Apple custom format */
1269 iChatHandleFound
= tpCompareIChatHandleName(*leaf
, smimeOpts
->SenderEmail
,
1271 if(iChatHandleFound
) {
1279 * normalize caller's email string
1280 * SMIME - lowercase only the portion after '@'
1281 * iChat - lowercase all of it
1283 char *email
= (char *)certGroup
.alloc().malloc(emailLen
);
1284 memmove(email
, smimeOpts
->SenderEmail
, emailLen
);
1285 tpNormalizeAddrSpec(email
, emailLen
, iChat
);
1289 * First check subjectAltName. The emailFound bool indicates
1290 * that *some* email address was found, regardless of a match
1294 match
= tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1296 SAN_Email
, iChat
, dummy
, emailFoundInSAN
);
1299 * Then subject DN, CSSMOID_EmailAddress, if no match from
1300 * subjectAltName. In this case the whole email address is
1301 * case insensitive (RFC 3280, section 4.1.2.6), so
1305 tpNormalizeAddrSpec(email
, emailLen
, true);
1306 match
= tpCompareSubjectName(*leaf
, SN_Email
, true, email
, emailLen
,
1309 certGroup
.alloc().free(email
);
1312 * Error here if no match found but there was indeed *some*
1313 * email address in the cert.
1315 if(!match
&& (emailFoundInSAN
|| emailFoundInDN
)) {
1316 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
)) {
1317 tpPolicyError("SMIME email addrs in cert but no match");
1318 return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
;
1324 * iChat only: error if app specified email address but there was
1327 if(iChat
&& !emailFoundInSAN
&& !emailFoundInDN
&& !iChatHandleFound
) {
1328 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1329 tpPolicyError("iChat: no email address or handle in cert");
1330 return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
;
1336 * Going by the letter of the law, here's what RFC 2632 has to say
1337 * about the legality of an empty Subject Name:
1339 * ...the subject DN in a user's (i.e. end-entity) certificate MAY
1340 * be an empty SEQUENCE in which case the subjectAltName extension
1341 * will include the subject's identifier and MUST be marked as
1344 * OK, first examine the leaf cert's subject name.
1347 CSSM_DATA_PTR subjNameData
= NULL
;
1348 const iSignExtenInfo
&kuInfo
= leafCertInfo
.keyUsage
;
1349 const iSignExtenInfo
&ekuInfo
= leafCertInfo
.extendKeyUsage
;
1350 const CSSM_X509_NAME
*x509Name
= NULL
;
1353 /* empty subject name processing is S/MIME only */
1357 crtn
= leaf
->fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
1359 /* This should really never happen */
1360 tpPolicyError("SMIME policy: error fetching subjectName");
1361 leaf
->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE
);
1362 return CSSMERR_TP_INVALID_CERTIFICATE
;
1364 /* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
1366 x509Name
= (const CSSM_X509_NAME
*)subjNameData
->Data
;
1367 if(x509Name
->numberOfRDNs
== 0) {
1369 * Empty subject name. If we haven't already seen a valid
1370 * email address in the subject alternate name (by looking
1371 * for a specific address specified by app), try to find
1374 if(!emailFoundInSAN
&& // haven't found one, and
1375 (emailLen
== 0)) { // didn't even look yet
1377 tpCompareSubjectAltName(leafCertInfo
.subjectAltName
,
1378 NULL
, 0, // email, emailLen,
1379 SAN_Email
, false, dummy
,
1380 emailFoundInSAN
); // the variable we're updating
1382 if(!emailFoundInSAN
) {
1383 tpPolicyError("SMIME policy fail: empty subject name and "
1384 "no Email Addrs in SubjectAltName");
1385 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
)) {
1386 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1387 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1390 /* have to skip the next block */
1396 * One more thing: this leaf must indeed have a subjAltName
1397 * extension and it must be critical. We would not have gotten this
1398 * far if the subjAltName extension was not actually present....
1400 assert(leafCertInfo
.subjectAltName
.present
);
1401 if(!leafCertInfo
.subjectAltName
.critical
) {
1402 tpPolicyError("SMIME policy fail: empty subject name and "
1403 "no Email Addrs in SubjectAltName");
1404 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT
)) {
1405 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1406 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1411 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
1414 * Enforce the usage of the key associated with the leaf cert.
1415 * Cert's KeyUsage must be a superset of what the app is trying to do.
1416 * Note the {en,de}cipherOnly flags are handled separately....
1418 if(kuInfo
.present
&& (smimeOpts
!= NULL
)) {
1419 CE_KeyUsage certKu
= *((CE_KeyUsage
*)kuInfo
.extnData
);
1420 CE_KeyUsage appKu
= smimeOpts
->IntendedUsage
;
1421 CE_KeyUsage intersection
= certKu
& appKu
;
1422 if((intersection
& CE_CIPHER_MASK
) != (appKu
& CE_CIPHER_MASK
)) {
1423 tpPolicyError("SMIME KeyUsage err: appKu 0x%x certKu 0x%x",
1425 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1426 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1430 /* Now the en/de cipher only bits - for keyAgreement only */
1431 if(appKu
& CE_KU_KeyAgreement
) {
1433 * 1. App wants to use this for key agreement; it must
1434 * say what it wants to do with the derived key.
1435 * In this context, the app's XXXonly bit means that
1436 * it wants to use the key for that op - not necessarliy
1439 if((appKu
& (CE_KU_EncipherOnly
| CE_KU_DecipherOnly
)) == 0) {
1440 tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
1441 "no Encipher or Decipher");
1442 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1443 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1448 * 2. If cert restricts to encipher only make sure the
1449 * app isn't trying to decipher.
1451 if((certKu
& CE_KU_EncipherOnly
) &&
1452 (appKu
& CE_KU_DecipherOnly
)) {
1453 tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
1454 "app wants to decipher");
1455 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1456 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1461 * 3. If cert restricts to decipher only make sure the
1462 * app isn't trying to encipher.
1464 if((certKu
& CE_KU_DecipherOnly
) &&
1465 (appKu
& CE_KU_EncipherOnly
)) {
1466 tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
1467 "app wants to encipher");
1468 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE
)) {
1469 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
1476 * Extended Key Use verification, which is different for the two policies.
1479 if(iChat
&& !ekuInfo
.present
) {
1481 * iChat: whether generic AIM cert or Apple .mac/iChat cert, we must have an
1482 * extended key use extension.
1484 tpPolicyError("iChat: No extended Key Use");
1485 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1486 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1490 if(!iChatHandleFound
) {
1492 * S/MIME and generic AIM certs when evaluating iChat policy.
1493 * Look for either emailProtection or anyExtendedKeyUsage usages.
1495 * S/MIME : the whole extension is optional.
1496 * iChat : extension must be there (which we've already covered, above)
1497 * and we must find one of those extensions.
1499 if(ekuInfo
.present
) {
1500 bool foundGoodEku
= false;
1501 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1502 assert(eku
!= NULL
);
1503 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1504 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_EmailProtection
)) {
1505 foundGoodEku
= true;
1508 if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1509 foundGoodEku
= true;
1514 tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
1515 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1516 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1523 * Apple iChat cert. Look for anyExtendedKeyUsage, iChatSigning,
1524 * ichatEncrypting - the latter of two which can optionally be
1527 assert(iChat
); /* or we could not have even looked for an iChat style handle */
1528 assert(ekuInfo
.present
); /* checked above */
1529 bool foundAnyEku
= false;
1530 bool foundIChatSign
= false;
1531 bool foundISignEncrypt
= false;
1532 CE_ExtendedKeyUsage
*eku
= (CE_ExtendedKeyUsage
*)ekuInfo
.extnData
;
1533 assert(eku
!= NULL
);
1535 for(unsigned i
=0; i
<eku
->numPurposes
; i
++) {
1536 if(tpCompareOids(&eku
->purposes
[i
],
1537 &CSSMOID_APPLE_EKU_ICHAT_SIGNING
)) {
1538 foundIChatSign
= true;
1540 else if(tpCompareOids(&eku
->purposes
[i
],
1541 &CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION
)) {
1542 foundISignEncrypt
= true;
1544 else if(tpCompareOids(&eku
->purposes
[i
], &CSSMOID_ExtendedKeyUsageAny
)) {
1549 if(!foundAnyEku
&& !foundISignEncrypt
&& !foundIChatSign
) {
1550 /* No go - no acceptable uses found */
1551 tpPolicyError("iChat: No valid extended Key Uses found");
1552 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1553 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1557 /* check for specifically required uses */
1558 if((smimeOpts
!= NULL
) && (smimeOpts
->IntendedUsage
!= 0)) {
1559 if(smimeOpts
->IntendedUsage
& CE_KU_DigitalSignature
) {
1560 if(!foundIChatSign
) {
1561 tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
1562 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1563 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1567 if(smimeOpts
->IntendedUsage
& CE_KU_DataEncipherment
) {
1568 if(!foundISignEncrypt
) {
1569 tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
1570 if(leaf
->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
)) {
1571 return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
;
1575 } /* checking IntendedUsage */
1576 } /* iChat cert format */
1582 * Verify Apple SW Update signing (was Apple Code Signing, pre-Leopard) options.
1584 * -- Must have one intermediate cert
1585 * -- intermediate must have basic constraints with path length 0
1586 * -- intermediate has CSSMOID_APPLE_EKU_CODE_SIGNING EKU
1587 * -- leaf cert has either CODE_SIGNING or CODE_SIGN_DEVELOPMENT EKU (the latter of
1588 * which triggers a CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT error)
1590 static CSSM_RETURN
tp_verifySWUpdateSigningOpts(
1591 TPCertGroup
&certGroup
,
1592 const CSSM_DATA
*fieldOpts
, // currently unused
1593 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1595 unsigned numCerts
= certGroup
.numCerts();
1596 const iSignCertInfo
*isCertInfo
;
1598 // const CE_BasicConstraints *bc; // currently unused
1599 CE_ExtendedKeyUsage
*eku
;
1600 CSSM_RETURN crtn
= CSSM_OK
;
1603 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
)) {
1604 tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts
);
1605 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1607 else if(numCerts
< 3) {
1608 /* this error allowed, but no intermediate...check leaf */
1613 /* verify intermediate cert */
1614 isCertInfo
= &certInfo
[1];
1615 tpCert
= certGroup
.certAtIndex(1);
1617 if(!isCertInfo
->basicConstraints
.present
) {
1618 tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
1619 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
)) {
1620 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1624 /* ExtendedKeyUse required, one legal value */
1625 if(!isCertInfo
->extendKeyUsage
.present
) {
1626 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
1627 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1628 return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1635 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1636 assert(eku
!= NULL
);
1637 if(eku
->numPurposes
!= 1) {
1638 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
1639 (unsigned long)eku
->numPurposes
);
1640 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1641 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1643 else if(eku
->numPurposes
== 0) {
1644 /* ignore that error but no EKU - skip EKU check */
1647 /* else ignore error and we have an intermediate EKU; proceed */
1650 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1651 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
1652 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1653 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1659 /* verify leaf cert */
1660 isCertInfo
= &certInfo
[0];
1661 tpCert
= certGroup
.certAtIndex(0);
1662 if(!isCertInfo
->extendKeyUsage
.present
) {
1663 tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
1664 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
)) {
1665 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1668 /* have to skip remainder */
1673 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1674 assert(eku
!= NULL
);
1675 if(eku
->numPurposes
!= 1) {
1676 tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
1677 (unsigned long)eku
->numPurposes
);
1678 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1679 if(crtn
== CSSM_OK
) {
1680 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1685 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING
)) {
1686 if(tpCompareOids(&eku
->purposes
[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV
)) {
1687 tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
1688 if(tpCert
->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
)) {
1689 if(crtn
== CSSM_OK
) {
1690 crtn
= CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
;
1695 tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
1696 if(tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1697 if(crtn
== CSSM_OK
) {
1698 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1708 * Verify Apple Resource Signing options.
1710 * -- leaf cert must have CSSMOID_APPLE_EKU_RESOURCE_SIGNING EKU
1711 * -- chain length must be >= 2
1712 * -- mainline code already verified that leaf KeyUsage = digitalSignature (only)
1714 static CSSM_RETURN
tp_verifyResourceSigningOpts(
1715 TPCertGroup
&certGroup
,
1716 const CSSM_DATA
*fieldOpts
, // currently unused
1717 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1719 unsigned numCerts
= certGroup
.numCerts();
1721 if(!certGroup
.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
)) {
1722 tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts
);
1723 return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
;
1726 const iSignCertInfo
&leafCert
= certInfo
[0];
1727 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1729 /* leaf ExtendedKeyUse required, one legal value */
1730 if(!tpVerifyEKU(leafCert
, CSSMOID_APPLE_EKU_RESOURCE_SIGNING
, false)) {
1731 tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
1732 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1733 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1741 * Common code for Apple Code Signing and Apple Package Signing.
1742 * For now we just require an RFC3280-style CodeSigning EKU in the leaf
1743 * for both policies.
1745 static CSSM_RETURN
tp_verifyCodePkgSignOpts(
1747 TPCertGroup
&certGroup
,
1748 const CSSM_DATA
*fieldOpts
, // currently unused
1749 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1751 const iSignCertInfo
&leafCert
= certInfo
[0];
1753 /* leaf ExtendedKeyUse required, one legal value */
1754 if(!tpVerifyEKU(leafCert
, CSSMOID_ExtendedUseCodeSigning
, false)) {
1755 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
1756 tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
1757 if(leaf
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
1758 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1767 * Verify MacAppStore receipt verification policy options.
1769 * -- Must have one intermediate cert
1770 * -- intermediate must be the FairPlay intermediate
1771 * -- leaf cert has the CSSMOID_APPLE_EXTENSION_MACAPPSTORE_RECEIPT marker extension
1773 static CSSM_RETURN
tp_verifyMacAppStoreReciptOpts(
1774 TPCertGroup
&certGroup
,
1775 const CSSM_DATA
*fieldOpts
, // currently unused
1776 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1778 unsigned numCerts
= certGroup
.numCerts();
1781 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
1783 tpPolicyError("tp_verifyMacAppStoreReciptOpts: numCerts %u", numCerts
);
1784 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1788 const iSignCertInfo
*isCertInfo
;
1791 /* verify intermediate cert */
1792 isCertInfo
= &certInfo
[1];
1793 tpCert
= certGroup
.certAtIndex(1);
1795 if (!isCertInfo
->basicConstraints
.present
)
1797 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1798 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
1799 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1802 // Now check the leaf
1803 isCertInfo
= &certInfo
[0];
1804 tpCert
= certGroup
.certAtIndex(0);
1805 if (certInfo
->certificatePolicies
.present
)
1807 // syslog(LOG_ERR, "tp_verifyMacAppStoreReciptOpts: found certificatePolicies");
1808 const CE_CertPolicies
*certPolicies
=
1809 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
1810 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY
))
1811 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1812 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1816 // syslog(LOG_ERR, "tp_verifyMacAppStoreReciptOpts: no certificatePolicies present"); // DEBUG
1817 tpPolicyError("tp_verifyMacAppStoreReciptOpts: no certificatePolicies present in leaf");
1818 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1819 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1825 bool certificatePoliciesContainsOID(const CE_CertPolicies
*certPolicies
, const CSSM_OID
*oidToFind
)
1827 // returns true if the given OID is present in the cert policies
1829 if (!certPolicies
|| !oidToFind
)
1832 const uint32 maxIndex
= 100; // sanity check
1833 for (uint32 policyIndex
= 0; policyIndex
< certPolicies
->numPolicies
&& policyIndex
< maxIndex
; policyIndex
++)
1835 CE_PolicyInformation
*certPolicyInfo
= &certPolicies
->policies
[policyIndex
];
1836 CSSM_OID_PTR oid
= &certPolicyInfo
->certPolicyId
;
1837 if (oid
&& tpCompareOids(oid
, oidToFind
)) // found it
1846 * Verify Apple ID Sharing options.
1848 * -- Do basic cert validation (OCSP-based certs)
1849 * -- Validate that the cert is an Apple ID sharing cert:
1850 * has a custom extension: OID: Apple ID Sharing Certificate ( 1 2 840 113635 100 4 7 )
1851 * (CSSMOID_APPLE_EXTENSION_APPLEID_SHARING)
1852 * EKU should have both client and server authentication
1853 * chains to the "Apple Application Integration Certification Authority" intermediate
1854 * -- optionally has a client-specified common name, which is the Apple ID account's UUID.
1856 * -- Must have one intermediate cert ("Apple Application Integration Certification Authority")
1857 * -- intermediate must have basic constraints with path length 0
1858 * -- intermediate has CSSMOID_APPLE_EXTENSION_AAI_INTERMEDIATE extension (OID 1 2 840 113635 100 6 2 3)
1859 OR APPLE_EXTENSION_AAI_INTERMEDIATE_2
1862 static CSSM_RETURN
tp_verifyAppleIDSharingOpts(TPCertGroup
&certGroup
,
1863 const CSSM_DATA
*fieldOpts
, // optional Common Name
1864 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
1866 unsigned numCerts
= certGroup
.numCerts();
1867 const iSignCertInfo
*isCertInfo
;
1869 // const CE_BasicConstraints *bc; // currently unused
1870 CE_ExtendedKeyUsage
*eku
;
1871 CSSM_RETURN crtn
= CSSM_OK
;
1872 unsigned int serverNameLen
= 0;
1873 const char *serverName
= NULL
;
1875 // The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1876 if (fieldOpts
&& fieldOpts
->Data
)
1878 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
= (CSSM_APPLE_TP_SSL_OPTIONS
*)fieldOpts
->Data
;
1879 switch (sslOpts
->Version
)
1881 case CSSM_APPLE_TP_SSL_OPTS_VERSION
:
1882 if (fieldOpts
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
))
1883 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1885 /* handle backwards compatibility here if necessary */
1887 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
1889 serverNameLen
= sslOpts
->ServerNameLen
;
1890 serverName
= sslOpts
->ServerName
;
1893 //------------------------------------------------------------------------
1897 if (!certGroup
.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
))
1899 tpPolicyError("tp_verifyAppleIDSharingOpts: numCerts %u", numCerts
);
1900 return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
;
1905 /* this error allowed, but no intermediate...check leaf */
1910 /* verify intermediate cert */
1911 isCertInfo
= &certInfo
[1];
1912 tpCert
= certGroup
.certAtIndex(1);
1914 if (!isCertInfo
->basicConstraints
.present
)
1916 tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1917 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
))
1918 return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
;
1923 /* verify leaf cert */
1924 isCertInfo
= &certInfo
[0];
1925 tpCert
= certGroup
.certAtIndex(0);
1927 /* host name check is optional */
1928 if (serverNameLen
!= 0)
1930 if (serverName
== NULL
)
1931 return CSSMERR_TP_INVALID_POINTER
;
1933 /* convert caller's hostname string to lower case */
1934 char *hostName
= (char *)certGroup
.alloc().malloc(serverNameLen
);
1935 memmove(hostName
, serverName
, serverNameLen
);
1936 tpToLower(hostName
, serverNameLen
);
1938 /* Check common name... */
1941 CSSM_BOOL match
= tpCompareSubjectName(*tpCert
, SN_CommonName
, false, hostName
,
1942 serverNameLen
, fieldFound
);
1944 certGroup
.alloc().free(hostName
);
1945 if (!match
&& tpCert
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
))
1946 return CSSMERR_APPLETP_HOSTNAME_MISMATCH
;
1949 if (certInfo
->certificatePolicies
.present
)
1951 const CE_CertPolicies
*certPolicies
=
1952 &isCertInfo
->certificatePolicies
.extnData
->certPolicies
;
1953 if (!certificatePoliciesContainsOID(certPolicies
, &CSSMOID_APPLEID_SHARING_CERT_POLICY
))
1954 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1955 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1958 if (tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
))
1959 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
1961 if (!isCertInfo
->extendKeyUsage
.present
)
1963 tpPolicyError("tp_verifyAppleIDSharingOpts: no extendedKeyUse in leaf");
1964 if (tpCert
->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
))
1965 return crtn
? crtn
: CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
;
1967 /* have to skip remainder */
1971 // Check that certificate can do Client and Server Authentication (EKU)
1972 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
1973 assert(eku
!= NULL
);
1974 if(eku
->numPurposes
!= 2)
1976 tpPolicyError("tp_verifyAppleIDSharingOpts: bad eku->numPurposes (%lu)",
1977 (unsigned long)eku
->numPurposes
);
1978 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
1980 if (crtn
== CSSM_OK
)
1981 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
1985 bool canDoClientAuth
= false, canDoServerAuth
= false, ekuError
= false;
1986 for (int ix
=0;ix
<2;ix
++)
1988 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ClientAuth
))
1989 canDoClientAuth
= true;
1991 if (tpCompareOids(&eku
->purposes
[ix
], &CSSMOID_ServerAuth
))
1992 canDoServerAuth
= true;
2000 if (!(canDoClientAuth
&& canDoServerAuth
))
2004 tpPolicyError("tp_verifyAppleIDSharingOpts: bad EKU in leaf");
2005 if (tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
))
2007 if (crtn
== CSSM_OK
)
2008 crtn
= CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2016 * Verify Time Stamping (RFC3161) policy options.
2018 * -- Leaf must contain Extended Key Usage (EKU), marked critical
2019 * -- The EKU must contain the id-kp-timeStamping purpose and no other
2021 static CSSM_RETURN
tp_verifyTimeStampingOpts(TPCertGroup
&certGroup
,
2022 const CSSM_DATA
*fieldOpts
, // currently unused
2023 const iSignCertInfo
*certInfo
) // all certs, size certGroup.numCerts()
2025 unsigned numCerts
= certGroup
.numCerts();
2026 const iSignCertInfo
*isCertInfo
;
2028 CE_ExtendedKeyUsage
*eku
;
2030 isCertInfo
= &certInfo
[0];
2031 tpCert
= certGroup
.certAtIndex(0);
2033 if (!isCertInfo
->extendKeyUsage
.present
)
2035 tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
2036 tpCert
->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
);
2037 return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION
;
2040 if(!isCertInfo
->extendKeyUsage
.critical
)
2042 tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
2043 tpCert
->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
);
2044 return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL
;
2047 eku
= &isCertInfo
->extendKeyUsage
.extnData
->extendedKeyUsage
;
2048 assert(eku
!= NULL
);
2050 if(eku
->numPurposes
!= 1)
2052 tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
2053 (unsigned long)eku
->numPurposes
);
2054 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2055 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2058 if(!tpCompareOids(&eku
->purposes
[0], &CSSMOID_TimeStamping
))
2060 tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
2061 tpCert
->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
2062 return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
;
2069 * RFC2459 says basicConstraints must be flagged critical for
2070 * CA certs, but Verisign doesn't work that way.
2072 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
2075 * TP iSign spec says Extended Key Usage required for leaf certs,
2076 * but Verisign doesn't work that way.
2078 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
2081 * TP iSign spec says Subject Alternate Name required for leaf certs,
2082 * but Verisign doesn't work that way.
2084 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
2087 * TP iSign spec originally required KeyUsage for all certs, but
2088 * Verisign doesn't have that in their roots.
2090 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
2093 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
2094 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
2095 * but Thawte's intermediate cert (common name "Thawte Personal
2096 * Freemail Issuing CA") does not meet this requirement.
2098 #define SMIME_KEY_USAGE_MUST_BE_CRITICAL 0
2101 * Public routine to perform TP verification on a constructed
2103 * Returns CSSM_OK on success.
2104 * Assumes the chain has passed basic subject/issuer verification. First cert of
2105 * incoming certGroup is end-entity (leaf).
2107 * Per-policy details:
2108 * iSign: Assumes that last cert in incoming certGroup is a root cert.
2109 * Also assumes a cert group of more than one cert.
2110 * kTPx509Basic: CertGroup of length one allowed.
2112 CSSM_RETURN
tp_policyVerify(
2115 CSSM_CL_HANDLE clHand
,
2116 CSSM_CSP_HANDLE cspHand
,
2117 TPCertGroup
*certGroup
,
2118 CSSM_BOOL verifiedToRoot
, // last cert is good root
2119 CSSM_BOOL verifiedViaTrustSetting
, // last cert verified via
2121 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
2122 const CSSM_DATA
*policyFieldData
, // optional
2123 void *policyOpts
) // future options
2125 iSignCertInfo
*certInfo
= NULL
;
2127 iSignCertInfo
*thisCertInfo
;
2131 CSSM_BOOL cA
= CSSM_FALSE
; // init for compiler warning
2132 bool isLeaf
; // end entity
2133 bool isRoot
; // root cert
2134 CE_ExtendedKeyUsage
*extendUsage
;
2135 CE_AuthorityKeyID
*authorityId
;
2136 CSSM_KEY_PTR pubKey
;
2137 CSSM_RETURN outErr
= CSSM_OK
; // for gross, non-policy errors
2138 CSSM_BOOL policyFail
= CSSM_FALSE
;// generic CSSMERR_TP_VERIFY_ACTION_FAILED
2139 CSSM_RETURN policyError
= CSSM_OK
; // policy-specific failure
2141 /* First, kTPDefault is a nop here */
2142 if(policy
== kTPDefault
) {
2146 if(certGroup
== NULL
) {
2147 return CSSMERR_TP_INVALID_CERTGROUP
;
2149 numCerts
= certGroup
->numCerts();
2151 return CSSMERR_TP_INVALID_CERTGROUP
;
2153 if(policy
== kTPiSign
) {
2154 if(!verifiedToRoot
) {
2155 /* no way, this requires a root cert */
2156 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2159 /* nope, not for iSign */
2160 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
2164 /* cook up an iSignCertInfo array */
2165 certInfo
= (iSignCertInfo
*)tpCalloc(alloc
, numCerts
, sizeof(iSignCertInfo
));
2166 /* subsequent errors to errOut: */
2168 /* fill it with interesting info from parsed certs */
2169 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2170 if(iSignGetCertInfo(alloc
,
2171 certGroup
->certAtIndex(certDex
),
2172 &certInfo
[certDex
])) {
2173 (certGroup
->certAtIndex(certDex
))->addStatusCode(
2174 CSSMERR_TP_INVALID_CERTIFICATE
);
2175 /* this one is fatal (and can't ignore) */
2176 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
2182 * OK, the heart of TP enforcement.
2184 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2185 thisCertInfo
= &certInfo
[certDex
];
2186 TPCertInfo
*thisTpCertInfo
= certGroup
->certAtIndex(certDex
);
2189 * First check for presence of required extensions and
2190 * critical extensions we don't understand.
2192 if(thisCertInfo
->foundUnknownCritical
) {
2193 /* illegal for all policies */
2194 tpPolicyError("tp_policyVerify: critical flag in unknown extension");
2195 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
)) {
2196 policyFail
= CSSM_TRUE
;
2201 * Check for unsupported key length, per <rdar://6892837>
2203 if((pubKey
=thisTpCertInfo
->pubKey()) != NULL
) {
2204 CSSM_KEYHEADER
*keyHdr
= &pubKey
->KeyHeader
;
2205 if(keyHdr
->AlgorithmId
== CSSM_ALGID_RSA
&& keyHdr
->LogicalKeySizeInBits
< 1024) {
2206 tpPolicyError("tp_policyVerify: RSA key size too small");
2207 if(thisTpCertInfo
->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
)) {
2208 policyFail
= CSSM_TRUE
;
2214 * Note it's possible for both of these to be true, for a chain
2215 * of length one (kTPx509Basic, kCrlPolicy only!)
2216 * FIXME: should this code work if the last cert in the chain is NOT a root?
2218 isLeaf
= thisTpCertInfo
->isLeaf();
2219 isRoot
= thisTpCertInfo
->isSelfSigned(true);
2222 * BasicConstraints.cA
2223 * iSign: required in all but leaf and root,
2224 * for which it is optional (with default values of false
2225 * for leaf and true for root).
2226 * all others: always optional, default of false for leaf and
2228 * All: cA must be false for leaf, true for others
2230 if(!thisCertInfo
->basicConstraints
.present
) {
2232 * No basicConstraints present; infer a cA value if appropriate.
2235 /* cool, use default; note that kTPx509Basic with
2236 * certGroup length of one may take this case */
2240 /* cool, use default */
2247 * not present, not leaf, not root....
2248 * ....RFC2459 says this can not be a CA
2253 /* required for iSign in this position */
2254 tpPolicyError("tp_policyVerify: no "
2255 "basicConstraints");
2256 if(thisTpCertInfo
->addStatusCode(
2257 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS
)) {
2258 policyFail
= CSSM_TRUE
;
2263 } /* inferred a default value */
2265 /* basicConstraints present */
2266 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
2267 /* disabled for verisign compatibility */
2268 if(!thisCertInfo
->basicConstraints
.critical
) {
2270 tpPolicyError("tp_policyVerify: basicConstraints marked "
2272 if(thisTpCertInfo
->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED
)) {
2273 policyFail
= CSSM_TRUE
;
2276 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
2278 const CE_BasicConstraints
*bcp
=
2279 &thisCertInfo
->basicConstraints
.extnData
->basicConstraints
;
2283 /* Verify pathLenConstraint if present */
2284 if(!isLeaf
&& // leaf, certDex=0, don't care
2285 cA
&& // p.l.c. only valid for CAs
2286 bcp
->pathLenConstraintPresent
) { // present?
2288 * pathLenConstraint=0 legal for certDex 1 only
2289 * pathLenConstraint=1 legal for certDex {1,2}
2292 if(certDex
> (bcp
->pathLenConstraint
+ 1)) {
2293 tpPolicyError("tp_policyVerify: pathLenConstraint "
2295 if(thisTpCertInfo
->addStatusCode(
2296 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT
)) {
2297 policyFail
= CSSM_TRUE
;
2305 * Special cases to allow a chain of length 1, leaf and root
2306 * both true, and for caller to override the "leaf can't be a CA"
2307 * requirement when a CA cert is explicitly being evaluated as the
2311 !(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
)) {
2312 tpPolicyError("tp_policyVerify: cA true for leaf");
2313 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2314 policyFail
= CSSM_TRUE
;
2318 tpPolicyError("tp_policyVerify: cA false for non-leaf");
2319 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
)) {
2320 policyFail
= CSSM_TRUE
;
2325 * Authority Key Identifier optional
2326 * iSign : only allowed in !root.
2327 * If present, must not be critical.
2328 * all others : ignored (though used later for chain verification)
2330 if((policy
== kTPiSign
) && thisCertInfo
->authorityId
.present
) {
2332 tpPolicyError("tp_policyVerify: authorityId in root");
2333 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
2334 policyFail
= CSSM_TRUE
;
2337 if(thisCertInfo
->authorityId
.critical
) {
2338 /* illegal per RFC 2459 */
2339 tpPolicyError("tp_policyVerify: authorityId marked "
2341 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
)) {
2342 policyFail
= CSSM_TRUE
;
2348 * Subject Key Identifier optional
2349 * iSign : can't be critical.
2350 * all others : ignored (though used later for chain verification)
2352 if(thisCertInfo
->subjectId
.present
) {
2353 if((policy
== kTPiSign
) && thisCertInfo
->subjectId
.critical
) {
2354 tpPolicyError("tp_policyVerify: subjectId marked critical");
2355 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID
)) {
2356 policyFail
= CSSM_TRUE
;
2362 * Key Usage optional except required as noted
2363 * iSign : required for non-root/non-leaf
2364 * Leaf cert : if present, usage = digitalSignature
2365 * Exception : if leaf, and keyUsage not present,
2366 * netscape-cert-type must be present, with
2367 * Object Signing bit set
2368 * kCrlPolicy : Leaf: usage = CRLSign
2369 * kTP_SMIME : if present, must be critical
2370 * kTP_SWUpdateSign, kTP_ResourceSign, kTP_CodeSigning, kTP_PackageSigning : Leaf :
2371 usage = digitalSignature
2372 * all others : non-leaf : usage = keyCertSign
2375 if(thisCertInfo
->keyUsage
.present
) {
2378 * iSign and *Signing: usage = digitalSignature
2379 * all others : don't care
2380 * Others: usage = keyCertSign
2381 * We only require that one bit to be set, we ignore others.
2386 case kTP_SWUpdateSign
:
2387 case kTP_ResourceSign
:
2388 case kTP_CodeSigning
:
2389 case kTP_PackageSigning
:
2390 expUsage
= CE_KU_DigitalSignature
;
2393 /* if present, this bit must be set */
2394 expUsage
= CE_KU_CRLSign
;
2397 /* accept whatever's there */
2398 expUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
2403 /* !leaf: this is true for all policies */
2404 expUsage
= CE_KU_KeyCertSign
;
2406 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
2407 if(!(actUsage
& expUsage
)) {
2408 tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
2410 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
2411 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
2412 policyFail
= CSSM_TRUE
;
2418 * Radar 3523221 renders this whole check obsolete, but I'm leaving
2419 * the code here to document its conspicuous functional absence.
2421 if((policy
== kTP_SMIME
) && !thisCertInfo
->keyUsage
.critical
) {
2423 * Per Radar 3410245, allow this for intermediate certs.
2425 if(SMIME_KEY_USAGE_MUST_BE_CRITICAL
|| isLeaf
|| isRoot
) {
2426 tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
2427 if(thisTpCertInfo
->addStatusCode(
2428 CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL
)) {
2429 policyFail
= CSSM_TRUE
;
2435 else if(policy
== kTPiSign
) {
2437 * iSign requires keyUsage present for non root OR
2438 * netscape-cert-type/ObjectSigning for leaf
2440 if(isLeaf
&& thisCertInfo
->netscapeCertType
.present
) {
2441 CE_NetscapeCertType ct
=
2442 thisCertInfo
->netscapeCertType
.extnData
->netscapeCertType
;
2444 if(!(ct
& CE_NCT_ObjSign
)) {
2445 tpPolicyError("tp_policyVerify: netscape-cert-type, "
2447 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
2448 policyFail
= CSSM_TRUE
;
2453 tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
2454 "!(leaf and netscapeCertType)");
2455 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
2456 policyFail
= CSSM_TRUE
;
2462 * RFC 3280, 4.1.2.6, says that an empty subject name can only appear in a
2463 * leaf cert, and only if subjectAltName is present and marked critical.
2465 if(isLeaf
&& thisTpCertInfo
->hasEmptySubjectName()) {
2466 bool badEmptySubject
= false;
2467 if(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) {
2469 * True when evaluating a CA cert as well as when
2470 * evaluating a CRL's cert chain. Note the odd case of a CRL's
2471 * signer having an empty subject matching an empty issuer
2472 * in the CRL. That'll be caught here.
2474 badEmptySubject
= true;
2476 else if(!thisCertInfo
->subjectAltName
.present
|| /* no subjectAltName */
2477 !thisCertInfo
->subjectAltName
.critical
) { /* not critical */
2478 badEmptySubject
= true;
2480 if(badEmptySubject
) {
2481 tpPolicyError("tp_policyVerify: bad empty subject");
2482 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT
)) {
2483 policyFail
= CSSM_TRUE
;
2489 * RFC 3739: if this cert has a Qualified Cert Statements extension, and
2490 * it's Critical, make sure we understand all of the extension's statementIds.
2492 if(thisCertInfo
->qualCertStatements
.present
&&
2493 thisCertInfo
->qualCertStatements
.critical
) {
2494 CE_QC_Statements
*qcss
=
2495 &thisCertInfo
->qualCertStatements
.extnData
->qualifiedCertStatements
;
2496 uint32 numQcs
= qcss
->numQCStatements
;
2497 for(unsigned qdex
=0; qdex
<numQcs
; qdex
++) {
2498 CSSM_OID_PTR qid
= &qcss
->qcStatements
[qdex
].statementId
;
2500 for(unsigned kdex
=0; kdex
<NUM_KNOWN_QUAL_CERT_STATEMENTS
; kdex
++) {
2501 if(tpCompareCssmData(qid
, knownQualifiedCertStatements
[kdex
])) {
2507 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT
)) {
2508 policyFail
= CSSM_TRUE
;
2513 } /* critical Qualified Cert Statement */
2516 * Certificate Policies extension validation, per section 1.2 of:
2517 * http://iase.disa.mil/pki/dod_cp_v10_final_2_mar_09_signed.pdf
2519 if (tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH
, false) ||
2520 tpVerifyCPE(*thisCertInfo
, CSSMOID_PIV_AUTH_2048
, false)) {
2522 * Certificate asserts one of the PIV-Auth Certificate Policy OIDs;
2523 * check the required Key Usage extension for compliance.
2526 * usage = digitalSignature (only; no other bits asserted)
2528 * usage = keyCertSign (required; other bits ignored)
2530 if(thisCertInfo
->keyUsage
.present
) {
2531 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
2533 /* No key usage! Policy fail. */
2536 if(!(actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
) && (certDex
== 0)) {
2537 expUsage
= CE_KU_DigitalSignature
;
2539 expUsage
= actUsage
| CE_KU_KeyCertSign
;
2541 if(!(actUsage
== expUsage
)) {
2542 tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
2544 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
2545 if(thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
)) {
2546 policyFail
= CSSM_TRUE
;
2549 } /* Certificate Policies */
2552 } /* for certDex, checking presence of extensions */
2555 * Special case checking for leaf (end entity) cert
2557 * iSign only: Extended key usage, optional for leaf,
2558 * value CSSMOID_ExtendedUseCodeSigning
2560 if((policy
== kTPiSign
) && certInfo
[0].extendKeyUsage
.present
) {
2561 extendUsage
= &certInfo
[0].extendKeyUsage
.extnData
->extendedKeyUsage
;
2562 if(extendUsage
->numPurposes
!= 1) {
2563 tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
2565 (int)extendUsage
->numPurposes
);
2566 if((certGroup
->certAtIndex(0))->addStatusCode(
2567 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
2568 policyFail
= CSSM_TRUE
;
2571 if(!tpCompareOids(extendUsage
->purposes
,
2572 &CSSMOID_ExtendedUseCodeSigning
)) {
2573 tpPolicyError("tp_policyVerify: bad extendKeyUsage");
2574 if((certGroup
->certAtIndex(0))->addStatusCode(
2575 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
)) {
2576 policyFail
= CSSM_TRUE
;
2582 * Verify authorityId-->subjectId linkage.
2583 * All optional - skip if needed fields not present.
2584 * Also, always skip last (root) cert.
2586 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
2587 if(!certInfo
[certDex
].authorityId
.present
||
2588 !certInfo
[certDex
+1].subjectId
.present
) {
2591 authorityId
= &certInfo
[certDex
].authorityId
.extnData
->authorityKeyID
;
2592 if(!authorityId
->keyIdentifierPresent
) {
2593 /* we only know how to compare keyIdentifier */
2596 if(!tpCompareCssmData(&authorityId
->keyIdentifier
,
2597 &certInfo
[certDex
+1].subjectId
.extnData
->subjectKeyID
)) {
2598 tpPolicyError("tp_policyVerify: bad key ID linkage");
2599 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
2600 CSSMERR_APPLETP_INVALID_ID_LINKAGE
)) {
2601 policyFail
= CSSM_TRUE
;
2607 * Check signature algorithm on all non-root certs,
2608 * reject if known to be untrusted
2610 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
2611 if(certInfo
[certDex
].untrustedSigAlg
) {
2612 tpPolicyError("tp_policyVerify: untrusted signature algorithm");
2613 if((certGroup
->certAtIndex(certDex
))->addStatusCode(
2614 CSSMERR_TP_INVALID_CERTIFICATE
)) {
2615 policyFail
= CSSM_TRUE
;
2620 /* specific per-policy checking */
2626 * SSL, EAP, IPSec: optionally verify common name; all are identical
2627 * other than their names.
2628 * FIXME - should this be before or after the root cert test? How can
2629 * we return both errors?
2631 policyError
= tp_verifySslOpts(policy
, *certGroup
, policyFieldData
, certInfo
[0]);
2635 tpDebug("iChat policy");
2638 policyError
= tp_verifySmimeOpts(policy
, *certGroup
, policyFieldData
,
2641 case kTP_SWUpdateSign
:
2642 policyError
= tp_verifySWUpdateSigningOpts(*certGroup
, policyFieldData
, certInfo
);
2644 case kTP_ResourceSign
:
2645 policyError
= tp_verifyResourceSigningOpts(*certGroup
, policyFieldData
, certInfo
);
2647 case kTP_CodeSigning
:
2648 case kTP_PackageSigning
:
2649 policyError
= tp_verifyCodePkgSignOpts(policy
, *certGroup
, policyFieldData
, certInfo
);
2651 case kTP_MacAppStoreRec
:
2652 policyError
= tp_verifyMacAppStoreReciptOpts(*certGroup
, policyFieldData
, certInfo
);
2654 case kTP_AppleIDSharing
:
2655 policyError
= tp_verifyAppleIDSharingOpts(*certGroup
, policyFieldData
, certInfo
);
2657 case kTP_TimeStamping
:
2658 policyError
= tp_verifyTimeStampingOpts(*certGroup
, policyFieldData
, certInfo
);
2663 case kTP_PKINIT_Client
:
2669 if(outErr
== CSSM_OK
) {
2670 /* policy-specific error takes precedence here */
2671 if(policyError
!= CSSM_OK
) {
2672 outErr
= policyError
;
2674 else if(policyFail
) {
2675 /* plain vanilla error return from this module */
2676 outErr
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
2680 /* free resources */
2681 for(certDex
=0; certDex
<numCerts
; certDex
++) {
2682 thisCertInfo
= &certInfo
[certDex
];
2683 iSignFreeCertInfo(clHand
, thisCertInfo
);
2685 tpFree(alloc
, certInfo
);
2690 * Obtain policy-specific User Trust parameters
2692 void tp_policyTrustSettingParams(
2694 const CSSM_DATA
*policyData
, // optional
2695 /* returned values - not mallocd */
2696 const char **policyStr
,
2697 uint32
*policyStrLen
,
2698 SecTrustSettingsKeyUsage
*keyUse
)
2700 /* default values */
2702 *keyUse
= kSecTrustSettingsKeyUseAny
;
2704 if((policyData
== NULL
) || (policyData
->Data
== NULL
)) {
2705 /* currently, no further action possible */
2713 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SSL_OPTIONS
)) {
2714 /* this error will be caught later */
2717 CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
=
2718 (CSSM_APPLE_TP_SSL_OPTIONS
*)policyData
->Data
;
2719 *policyStr
= sslOpts
->ServerName
;
2720 *policyStrLen
= sslOpts
->ServerNameLen
;
2721 if(sslOpts
->Flags
& CSSM_APPLE_TP_SSL_CLIENT
) {
2723 * Client signs with its priv key. Server end,
2724 * which (also) verifies the client cert, verifies.
2726 *keyUse
= kSecTrustSettingsKeyUseSignature
;
2729 /* server decrypts */
2730 *keyUse
= kSecTrustSettingsKeyUseEnDecryptKey
;
2738 if(policyData
->Length
!= sizeof(CSSM_APPLE_TP_SMIME_OPTIONS
)) {
2739 /* this error will be caught later */
2742 CSSM_APPLE_TP_SMIME_OPTIONS
*smimeOpts
=
2743 (CSSM_APPLE_TP_SMIME_OPTIONS
*)policyData
->Data
;
2744 *policyStr
= smimeOpts
->SenderEmail
;
2745 *policyStrLen
= smimeOpts
->SenderEmailLen
;
2746 SecTrustSettingsKeyUsage ku
= 0;
2747 CE_KeyUsage smimeKu
= smimeOpts
->IntendedUsage
;
2748 if(smimeKu
& (CE_KU_DigitalSignature
| CE_KU_KeyCertSign
| CE_KU_CRLSign
)) {
2749 ku
|= kSecTrustSettingsKeyUseSignature
;
2751 if(smimeKu
& (CE_KU_KeyEncipherment
| CE_KU_DataEncipherment
)) {
2752 ku
|= kSecTrustSettingsKeyUseEnDecryptKey
;
2759 /* no other options */