2 * Copyright (c) 2000-2001 Apple Computer, 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
22 Created 10/9/2000 by Doug Mitchell.
25 #include <Security/cssmtype.h>
26 #include <Security/cssmapi.h>
27 #include "tpPolicies.h"
28 #include <Security/oidsattr.h>
29 #include <Security/cssmerr.h>
30 #include "tpdebugging.h"
31 #include "rootCerts.h"
32 #include "certGroupUtils.h"
33 #include <Security/x509defs.h>
34 #include <Security/oidscert.h>
35 #include <Security/certextensions.h>
36 #include <Security/cssmapple.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 pertinent to iSign */
58 iSignExtenInfo authorityId
;
59 iSignExtenInfo subjectId
;
60 iSignExtenInfo keyUsage
;
61 iSignExtenInfo extendKeyUsage
;
62 iSignExtenInfo basicConstraints
;
63 iSignExtenInfo netscapeCertType
;
65 /* flag indicating presence of a critical extension we don't understand */
66 CSSM_BOOL foundUnknownCritical
;
72 * Setup a single iSignExtenInfo. Called once per known extension
75 static CSSM_RETURN
tpSetupExtension(
78 iSignExtenInfo
*extnInfo
) // which component of certInfo
80 if(extnData
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
81 errorLog0("tpSetupExtension: malformed CSSM_FIELD\n");
82 return CSSMERR_TP_UNKNOWN_FORMAT
;
84 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)extnData
->Data
;
85 extnInfo
->present
= CSSM_TRUE
;
86 extnInfo
->critical
= cssmExt
->critical
;
87 extnInfo
->extnData
= (CE_Data
*)cssmExt
->value
.parsedValue
;
88 extnInfo
->valToFree
= extnData
;
93 * Fetch a known extension, set up associated iSignExtenInfo if present.
95 static CSSM_RETURN
iSignFetchExtension(
98 const CSSM_OID
*fieldOid
, // which extension to fetch
99 iSignExtenInfo
*extnInfo
) // where the info goes
101 CSSM_DATA_PTR fieldValue
; // mallocd by CL
104 crtn
= tpCert
->fetchField(fieldOid
, &fieldValue
);
108 case CSSMERR_CL_NO_FIELD_VALUES
:
109 /* field not present, OK */
114 return tpSetupExtension(alloc
,
120 * Search for al unknown extensions. If we find one which is flagged critical,
121 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
123 static CSSM_RETURN
iSignSearchUnknownExtensions(
125 iSignCertInfo
*certInfo
)
128 CSSM_DATA_PTR fieldValue
= NULL
;
129 CSSM_HANDLE searchHand
= CSSM_INVALID_HANDLE
;
130 uint32 numFields
= 0;
132 crtn
= CSSM_CL_CertGetFirstCachedFieldValue(tpCert
->clHand(),
134 &CSSMOID_X509V3CertificateExtensionCStruct
,
140 /* found one, proceed */
142 case CSSMERR_CL_NO_FIELD_VALUES
:
143 /* no unknown extensions present, OK */
149 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
150 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
151 return CSSMERR_TP_UNKNOWN_FORMAT
;
153 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
154 if(cssmExt
->critical
) {
155 /* BRRZAPP! Found an unknown extension marked critical */
156 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
159 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
160 &CSSMOID_X509V3CertificateExtensionCStruct
,
164 /* process remaining unknown extensions */
165 for(unsigned i
=1; i
<numFields
; i
++) {
166 crtn
= CSSM_CL_CertGetNextCachedFieldValue(tpCert
->clHand(),
170 /* should never happen */
171 errorLog0("searchUnknownExtensions: GetNextCachedFieldValue error\n");
174 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
175 errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n");
176 crtn
= CSSMERR_TP_UNKNOWN_FORMAT
;
179 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
180 if(cssmExt
->critical
) {
181 /* BRRZAPP! Found an unknown extension marked critical */
182 certInfo
->foundUnknownCritical
= CSSM_TRUE
;
185 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
186 &CSSMOID_X509V3CertificateExtensionCStruct
,
189 } /* for additional fields */
193 CSSM_CL_FreeFieldValue(tpCert
->clHand(),
194 &CSSMOID_X509V3CertificateExtensionCStruct
,
197 if(searchHand
!= CSSM_INVALID_HANDLE
) {
198 CSSM_CL_CertAbortQuery(tpCert
->clHand(), searchHand
);
203 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
204 * Returns CSSM_FAIL on error.
206 static CSSM_RETURN
iSignGetCertInfo(
207 CssmAllocator
&alloc
,
209 iSignCertInfo
*certInfo
)
213 /* first grind thru the extensions we're interested in */
214 crtn
= iSignFetchExtension(alloc
,
216 &CSSMOID_AuthorityKeyIdentifier
,
217 &certInfo
->authorityId
);
221 crtn
= iSignFetchExtension(alloc
,
223 &CSSMOID_SubjectKeyIdentifier
,
224 &certInfo
->subjectId
);
228 crtn
= iSignFetchExtension(alloc
,
231 &certInfo
->keyUsage
);
235 crtn
= iSignFetchExtension(alloc
,
237 &CSSMOID_ExtendedKeyUsage
,
238 &certInfo
->extendKeyUsage
);
242 crtn
= iSignFetchExtension(alloc
,
244 &CSSMOID_BasicConstraints
,
245 &certInfo
->basicConstraints
);
249 crtn
= iSignFetchExtension(alloc
,
251 &CSSMOID_NetscapeCertType
,
252 &certInfo
->netscapeCertType
);
257 /* now look for extensions we don't understand - the only thing we're interested
258 * in is the critical flag. */
259 return iSignSearchUnknownExtensions(tpCert
, certInfo
);
263 * Free (via CL) the fields allocated in iSignGetCertInfo().
265 static void iSignFreeCertInfo(
266 CSSM_CL_HANDLE clHand
,
267 iSignCertInfo
*certInfo
)
269 if(certInfo
->authorityId
.present
) {
270 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_AuthorityKeyIdentifier
,
271 certInfo
->authorityId
.valToFree
);
273 if(certInfo
->subjectId
.present
) {
274 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_SubjectKeyIdentifier
,
275 certInfo
->subjectId
.valToFree
);
277 if(certInfo
->keyUsage
.present
) {
278 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_KeyUsage
,
279 certInfo
->keyUsage
.valToFree
);
281 if(certInfo
->extendKeyUsage
.present
) {
282 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_ExtendedKeyUsage
,
283 certInfo
->extendKeyUsage
.valToFree
);
285 if(certInfo
->basicConstraints
.present
) {
286 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_BasicConstraints
,
287 certInfo
->basicConstraints
.valToFree
);
289 if(certInfo
->netscapeCertType
.present
) {
290 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_NetscapeCertType
,
291 certInfo
->netscapeCertType
.valToFree
);
295 #if TP_ROOT_CERT_ENABLE
297 * Common code for comparing a root to a list of known embedded roots.
299 static CSSM_BOOL
tp_isKnownRootCert(
300 TPCertInfo
*rootCert
, // raw cert to compare
301 const tpRootCert
*knownRoots
,
302 unsigned numKnownRoots
)
304 const CSSM_DATA
*subjectName
= NULL
;
305 CSSM_DATA_PTR publicKey
= NULL
;
307 CSSM_BOOL brtn
= CSSM_FALSE
;
308 CSSM_DATA_PTR valToFree
= NULL
;
310 subjectName
= rootCert
->subjectName();
311 publicKey
= tp_CertGetPublicKey(rootCert
, &valToFree
);
312 if(publicKey
== NULL
) {
313 errorLog0("tp_isKnownRootCert: error retrieving public key info!\n");
318 * Grind thru the list of known certs, demanding perfect match of
321 for(dex
=0; dex
<numKnownRoots
; dex
++) {
322 if(!tpCompareCssmData(subjectName
,
323 &knownRoots
[dex
].subjectName
)) {
326 if(!tpCompareCssmData(publicKey
,
327 &knownRoots
[dex
].publicKey
)) {
330 #if ENABLE_APPLE_DEBUG_ROOT
331 if( dex
== (knownRoots
- 1) ){
333 //tpSetError(CSSM_TP_DEBUG_CERT);
341 tp_CertFreePublicKey(rootCert
->clHand(), valToFree
);
346 * See if specified root cert is a known (embedded) iSign root cert.
347 * Returns CSSM_TRUE if the cert is a known root cert.
349 * Note as of 6/12/02, we do not distinguish between internally
350 * cached iSign roots and SSL roots. Maybe someday we will do so again,
351 * so let's leave these two functions separate.
353 static CSSM_BOOL
tp_isIsignRootCert(
354 CSSM_CL_HANDLE clHand
,
355 TPCertInfo
*rootCert
) // raw cert from cert group
357 const tpRootCert
*roots
;
359 roots
= TPRootStore::tpGlobalRoots().rootCerts(clHand
, numRoots
);
360 return tp_isKnownRootCert(rootCert
, roots
, numRoots
);
364 * See if specified root cert is a known (embedded) SSL root cert.
365 * Returns CSSM_TRUE if the cert is a known root cert.
367 static CSSM_BOOL
tp_isSslRootCert(
368 CSSM_CL_HANDLE clHand
,
369 TPCertInfo
*rootCert
) // raw cert from cert group
371 const tpRootCert
*roots
;
373 roots
= TPRootStore::tpGlobalRoots().rootCerts(clHand
, numRoots
);
374 return tp_isKnownRootCert(rootCert
, roots
, numRoots
);
378 * Attempt to verify specified cert (from the end of a chain) with one of
379 * our known SSL roots.
381 CSSM_BOOL
tp_verifyWithSslRoots(
382 CSSM_CL_HANDLE clHand
,
383 CSSM_CSP_HANDLE cspHand
,
384 TPCertInfo
*certToVfy
) // last in chain, not root
386 CSSM_KEY rootKey
; // pub key manufactured from tpRootCert info
387 CSSM_CC_HANDLE ccHand
; // signature context
390 const tpRootCert
*rootInfo
;
391 CSSM_BOOL brtn
= CSSM_FALSE
;
392 CSSM_KEYHEADER
*hdr
= &rootKey
.KeyHeader
;
393 CSSM_X509_ALGORITHM_IDENTIFIER_PTR algId
;
394 CSSM_DATA_PTR valToFree
= NULL
;
395 CSSM_ALGORITHMS sigAlg
;
396 const tpRootCert
*rootCerts
= NULL
;
397 unsigned numRootCerts
= 0;
399 memset(&rootKey
, 0, sizeof(CSSM_KEY
));
402 * Get signature algorithm from subject key
404 algId
= tp_CertGetAlgId(certToVfy
, &valToFree
);
409 /* subsequest errors to errOut: */
411 /* map to key and signature algorithm */
412 sigAlg
= tpOidToAldId(&algId
->algorithm
, &hdr
->AlgorithmId
);
413 if(sigAlg
== CSSM_ALGID_NONE
) {
414 errorLog0("tp_verifyWithSslRoots: unknown sig alg\n");
418 /* Set up other constant key fields */
419 hdr
->BlobType
= CSSM_KEYBLOB_RAW
;
420 switch(hdr
->AlgorithmId
) {
422 hdr
->Format
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
425 hdr
->Format
= CSSM_KEYBLOB_RAW_FORMAT_FIPS186
;
428 hdr
->Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
432 hdr
->Format
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
434 hdr
->KeyClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
435 hdr
->KeyAttr
= CSSM_KEYATTR_MODIFIABLE
| CSSM_KEYATTR_EXTRACTABLE
;
436 hdr
->KeyUsage
= CSSM_KEYUSE_VERIFY
;
438 rootCerts
= TPRootStore::tpGlobalRoots().rootCerts(clHand
, numRootCerts
);
439 for(dex
=0; dex
<numRootCerts
; dex
++) {
440 rootInfo
= &rootCerts
[dex
];
441 if(!tpIsSameName(&rootInfo
->subjectName
, certToVfy
->issuerName())) {
446 /* only variation in key in the loop - raw key bits and size */
447 rootKey
.KeyData
= rootInfo
->publicKey
;
448 hdr
->LogicalKeySizeInBits
= rootInfo
->keySize
;
449 crtn
= CSSM_CSP_CreateSignatureContext(cspHand
,
455 errorLog0("tp_verifyWithSslRoots: CSSM_CSP_CreateSignatureContext err\n");
456 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
458 crtn
= CSSM_CL_CertVerify(clHand
,
460 certToVfy
->certData(),
461 NULL
, // no signer cert
464 CSSM_DeleteContext(ccHand
);
465 if(crtn
== CSSM_OK
) {
472 if(valToFree
!= NULL
) {
473 tp_CertFreeAlgId(clHand
, valToFree
);
477 #endif /* TP_ROOT_CERT_ENABLE */
480 * Convert a C string to lower case in place. NULL terminator not needed.
482 static void tpToLower(
486 for(unsigned i
=0; i
<strLen
; i
++) {
487 *str
++ = tolower(*str
);
492 * Verify SSL options. Currently this just consists of matching the
493 * leaf cert's subject common name against the caller's (optional)
496 static CSSM_RETURN
tp_verifySslOpts(
497 TPCertGroup
&certGroup
,
498 const CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
)
500 if(sslOpts
== NULL
) {
505 CSSM_DATA_PTR subjNameData
= NULL
;
506 char *serverName
= NULL
;
507 unsigned serverNameLen
= sslOpts
->ServerNameLen
;
508 char *commonName
= NULL
;
509 uint32 commonNameLen
= 0;
511 if(serverNameLen
== 0) {
515 if(sslOpts
->ServerName
== NULL
) {
516 return CSSMERR_TP_INVALID_POINTER
;
519 /* Obtain subject name of leaf cert in CSSM_X509_NAME_PTR form */
520 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
521 assert(leaf
!= NULL
);
523 crtn
= leaf
->fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
525 /* should never happen, we shouldn't be here if there is no subject */
526 errorLog0("tp_verifySslOpts: error retrieving subject name\n");
529 CSSM_X509_NAME_PTR x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
530 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
531 errorLog0("tp_verifySslOpts: malformed CSSM_X509_NAME\n");
532 crtn
= CSSMERR_TP_INVALID_CERTGROUP
;
536 /* Now grunge thru the X509 name looking for a common name */
537 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
538 CSSM_X509_RDN_PTR rdnp
;
542 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
543 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
544 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
545 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
546 if(tpCompareOids(&ptvp
->type
, &CSSMOID_CommonName
)) {
547 commonName
= (char *)ptvp
->value
.Data
;
548 commonNameLen
= ptvp
->value
.Length
;
553 if(commonName
== NULL
) {
554 errorLog0("tp_verifySslOpts: NO COMMON NAME in subject\n");
555 crtn
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
559 /* tolerate optional NULL terminators for both */
560 if(commonName
[commonNameLen
- 1] == '\0') {
563 if(sslOpts
->ServerName
[serverNameLen
- 1] == '\0') {
567 /* convert both name strings to lower case. The one in the X509 Name can
568 * be done in place; we have to malloc and copy the caller's string. */
569 tpToLower(commonName
, commonNameLen
);
570 serverName
= (char *)certGroup
.alloc().malloc(serverNameLen
);
571 memmove(serverName
, sslOpts
->ServerName
, serverNameLen
);
572 tpToLower(serverName
, serverNameLen
);
574 /* case 1: exact match */
575 if((serverNameLen
== commonNameLen
) &&
576 !memcmp(commonName
, serverName
, commonNameLen
)) {
581 /* case 2: handle optional '*' in cert's common name */
582 if(commonName
[0] == '*') {
583 /* last (commonNameLen - 1) chars have to match */
584 unsigned effectLen
= commonNameLen
- 1; // skip '*'
585 if(serverNameLen
< effectLen
) {
586 errorLog0("tp_verifySslOpts: subject/server name wildcard mismatch (1)\n");
587 crtn
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
589 else if(memcmp(commonName
+1, // skip '*'
590 serverName
+ serverNameLen
- effectLen
,
592 errorLog0("tp_verifySslOpts: subject/server name wildcard mismatch (2)\n");
593 crtn
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
602 errorLog0("tp_verifySslOpts: subject/server name mismatch\n");
603 crtn
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
606 if(subjNameData
!= NULL
) {
607 leaf
->freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
609 if(serverName
!= NULL
) {
610 certGroup
.alloc().free(serverName
);
612 if(crtn
== CSSMERR_TP_VERIFY_ACTION_FAILED
) {
613 leaf
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
);
619 * RFC2459 says basicConstraints must be flagged critical for
620 * CA certs, but Verisign doesn't work that way.
622 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
625 * TP iSign spec says Extended Key Usage required for leaf certs,
626 * but Verisign doesn't work that way.
628 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
631 * TP iSign spec says Subject Alternate Name required for leaf certs,
632 * but Verisign doesn't work that way.
634 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
637 * TP iSign spec originally required KeyUsage for all certs, but
638 * Verisign doesn't have that in their roots.
640 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
643 * Public routine to perform TP verification on a constructed
645 * Returns CSSM_TRUE on success.
646 * Asumes the chain has passed basic subject/issuer verification. First cert of
647 * incoming certGroup is end-entity (leaf).
649 * Per-policy details:
650 * iSign: Assumes that last cert in incoming certGroup is a root cert.
651 * Also assumes a cert group of more than one cert.
652 * kTPx509Basic: CertGroup of length one allowed.
654 CSSM_RETURN
tp_policyVerify(
656 CssmAllocator
&alloc
,
657 CSSM_CL_HANDLE clHand
,
658 CSSM_CSP_HANDLE cspHand
,
659 TPCertGroup
*certGroup
,
660 CSSM_BOOL verifiedToRoot
, // last cert is good root
661 const CSSM_APPLE_TP_ACTION_DATA
*actionData
,
662 const CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
,
663 void *policyOpts
) // future options
665 iSignCertInfo
*certInfo
= NULL
;
667 iSignCertInfo
*thisCertInfo
;
671 CSSM_BOOL cA
= CSSM_FALSE
; // init for compiler warning
672 CSSM_BOOL isLeaf
; // end entity
673 CSSM_BOOL isRoot
; // root cert
674 CE_ExtendedKeyUsage
*extendUsage
;
675 CE_AuthorityKeyID
*authorityId
;
676 CSSM_RETURN outErr
= CSSM_OK
; // for gross, non-policy errors
677 CSSM_BOOL policyFail
= CSSM_FALSE
;
679 /* First, kTPDefault is a nop here */
680 if(policy
== kTPDefault
) {
684 if(certGroup
== NULL
) {
685 return CSSMERR_TP_INVALID_CERTGROUP
;
687 numCerts
= certGroup
->numCerts();
689 return CSSMERR_TP_INVALID_CERTGROUP
;
691 if(policy
== kTPiSign
) {
692 if(!verifiedToRoot
) {
693 /* no way, this requires a root cert */
694 return CSSMERR_TP_INVALID_CERTGROUP
;
697 /* nope, not for iSign */
698 return CSSMERR_TP_INVALID_CERTGROUP
;
702 /* cook up an iSignCertInfo array */
703 certInfo
= (iSignCertInfo
*)tpCalloc(alloc
, numCerts
, sizeof(iSignCertInfo
));
704 /* subsequent errors to errOut: */
706 /* fill it with interesting info from parsed certs */
707 for(certDex
=0; certDex
<numCerts
; certDex
++) {
708 if(iSignGetCertInfo(alloc
,
709 certGroup
->certAtIndex(certDex
),
710 &certInfo
[certDex
])) {
711 (certGroup
->certAtIndex(certDex
))->addStatusCode(
712 CSSMERR_TP_INVALID_CERTIFICATE
);
713 /* this one is fatal */
714 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
720 * OK, the heart of TP enforcement.
721 * First check for presence of required extensions and
722 * critical extensions we don't understand.
724 for(certDex
=0; certDex
<numCerts
; certDex
++) {
725 thisCertInfo
= &certInfo
[certDex
];
726 TPCertInfo
*thisTpCertInfo
= certGroup
->certAtIndex(certDex
);
728 if(thisCertInfo
->foundUnknownCritical
) {
729 /* illegal for all policies */
730 errorLog0("tp_policyVerify: critical flag in unknown extension\n");
731 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
);
732 policyFail
= CSSM_TRUE
;
736 * Note it's possible for both of these to be true, for a
737 * of length one (kTPx509Basic only!)
739 isLeaf
= (certDex
== 0) ? CSSM_TRUE
: CSSM_FALSE
;
740 isRoot
= (certDex
== (numCerts
- 1)) ? CSSM_TRUE
: CSSM_FALSE
;
743 * BasicConstraints.cA
744 * iSign: required in all but leaf and root,
745 * for which it is optional (with default values of false
746 * for leaf and true for root).
748 * kTP_SSL: always optional, default of false for leaf and
750 * All: cA must be false for leaf, true for others
752 if(!thisCertInfo
->basicConstraints
.present
) {
754 /* cool, use default; note that kTPx509Basic with
755 * certGroup length of one may take this case */
759 /* cool, use default */
767 * not present, not leaf, not root, kTPx509Basic
768 * ....OK; infer as true
773 /* required for iSign in this position */
774 errorLog0("tp_policyVerify: no basicConstraints\n");
775 policyFail
= CSSM_TRUE
;
776 thisTpCertInfo
->addStatusCode(
777 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS
);
786 /* basicConstraints present */
787 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
788 /* disabled for verisign compatibility */
789 if(!thisCertInfo
->basicConstraints
.critical
) {
791 errorLog0("tp_policyVerify: basicConstraints marked not critical\n");
792 policyFail
= CSSM_TRUE
;
793 thisTpCertInfo
->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED
);
795 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
796 cA
= thisCertInfo
->basicConstraints
.extnData
->basicConstraints
.cA
;
800 /* special case to allow a chain of length 1, leaf and root
801 * both true (kTPx509Basic, kTP_SSL only) */
803 errorLog0("tp_policyVerify: cA true for leaf\n");
804 policyFail
= CSSM_TRUE
;
805 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
);
808 errorLog0("tp_policyVerify: cA false for non-leaf\n");
809 policyFail
= CSSM_TRUE
;
810 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
);
814 * Authority Key Identifier optional
815 * iSign : only allowed in !root.
816 * If present, must not be critical.
818 * kTP_SSL : ignored (though used later for chain verification)
820 if((policy
== kTPiSign
) && thisCertInfo
->authorityId
.present
) {
822 errorLog0("tp_policyVerify: authorityId in root\n");
823 policyFail
= CSSM_TRUE
;
824 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
);
826 if(thisCertInfo
->authorityId
.critical
) {
827 /* illegal per RFC 2459 */
828 errorLog0("tp_policyVerify: authorityId marked critical\n");
829 policyFail
= CSSM_TRUE
;
830 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
);
835 * Subject Key Identifier optional
836 * iSign : can't be critical.
838 * kTP_SSL : ignored (though used later for chain verification)
840 if(thisCertInfo
->subjectId
.present
) {
841 if((policy
== kTPiSign
) && thisCertInfo
->subjectId
.critical
) {
842 errorLog0("tp_policyVerify: subjectId marked critical\n");
843 policyFail
= CSSM_TRUE
;
844 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID
);
849 * Key Usage optional except as noted required
850 * iSign : required for non-root/non-leaf
851 * Leaf cert : if present, usage = digitalSignature
852 * Exception : if leaf, and keyUsage not present,
853 * netscape-cert-type must be present, with
854 * Object Signing bit set
855 * kTPx509Basic : non-leaf : usage = keyCertSign
858 if(thisCertInfo
->keyUsage
.present
) {
860 * Leaf cert: usage = digitalSignature
861 * Others: usage = keyCertSign
862 * We only require that one bit to be set, we ignore others.
865 if(policy
== kTPiSign
) {
866 expUsage
= CE_KU_DigitalSignature
;
869 /* hack to accept whatever's there */
870 expUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
874 /* this is true for all policies */
875 expUsage
= CE_KU_KeyCertSign
;
877 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
878 if(!(actUsage
& expUsage
)) {
879 errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n",
880 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
881 policyFail
= CSSM_TRUE
;
882 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
885 else if(policy
== kTPiSign
) {
887 * iSign requires keyUsage present for non root OR
888 * netscape-cert-type/ObjectSigning for leaf
890 if(isLeaf
&& thisCertInfo
->netscapeCertType
.present
) {
891 CE_NetscapeCertType ct
=
892 thisCertInfo
->netscapeCertType
.extnData
->netscapeCertType
;
894 if(!(ct
& CE_NCT_ObjSign
)) {
895 errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n");
896 policyFail
= CSSM_TRUE
;
897 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
901 errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n");
902 policyFail
= CSSM_TRUE
;
903 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
906 } /* for certDex, checking presence of extensions */
909 * Special case checking for leaf (end entity) cert
911 * iSign only: Extended key usage, optional for leaf,
912 * value CSSMOID_ExtendedUseCodeSigning
914 if((policy
== kTPiSign
) && certInfo
[0].extendKeyUsage
.present
) {
915 extendUsage
= &certInfo
[0].extendKeyUsage
.extnData
->extendedKeyUsage
;
916 if(extendUsage
->numPurposes
!= 1) {
917 errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n",
918 (int)extendUsage
->numPurposes
);
919 policyFail
= CSSM_TRUE
;
920 (certGroup
->certAtIndex(0))->addStatusCode(
921 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
923 if(!tpCompareOids(extendUsage
->purposes
,
924 &CSSMOID_ExtendedUseCodeSigning
)) {
925 errorLog0("tp_policyVerify: bad extendKeyUsage\n");
926 policyFail
= CSSM_TRUE
;
927 (certGroup
->certAtIndex(0))->addStatusCode(
928 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
933 * Verify authorityId-->subjectId linkage.
934 * All optional - skip if needed fields not present.
935 * Also, always skip last (root) cert.
937 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
938 if(!certInfo
[certDex
].authorityId
.present
||
939 !certInfo
[certDex
+1].subjectId
.present
) {
942 authorityId
= &certInfo
[certDex
].authorityId
.extnData
->authorityKeyID
;
943 if(!authorityId
->keyIdentifierPresent
) {
944 /* we only know how to compare keyIdentifier */
947 if(!tpCompareCssmData(&authorityId
->keyIdentifier
,
948 &certInfo
[certDex
+1].subjectId
.extnData
->subjectKeyID
)) {
949 errorLog0("tp_policyVerify: bad key ID linkage\n");
950 policyFail
= CSSM_TRUE
;
951 (certGroup
->certAtIndex(certDex
))->addStatusCode(
952 CSSMERR_APPLETP_INVALID_ID_LINKAGE
);
957 * SSL: optionally verify common name.
958 * FIXME - should this be before or after the root cert test? How can
959 * we return both errors?
961 if(policy
== kTP_SSL
) {
962 CSSM_RETURN cerr
= tp_verifySslOpts(*certGroup
, sslOpts
);
964 policyFail
= CSSM_TRUE
;
968 /* iSign, SSL: compare root against known root certs */
969 /* FIXME - this goes away soon */
970 #if TP_ROOT_CERT_ENABLE
971 if((outErr
== CSSM_OK
) && // skip if we have a gross error (other than policy failure)
972 (actionData
!= NULL
) &&
973 (actionData
->ActionFlags
& 0x80000000)) { // The secret "enable root cert check" flag
974 TPCertInfo
*lastCert
= certGroup
->lastCert();
975 if(policy
== kTPiSign
) {
976 bool brtn
= tp_isIsignRootCert(clHand
, lastCert
);
978 policyFail
= CSSM_TRUE
;
981 else if(verifiedToRoot
&& (policy
== kTP_SSL
)) {
982 /* note SSL doesn't require root here */
983 bool brtn
= tp_isSslRootCert(clHand
, lastCert
);
985 outErr
= CSSMERR_TP_INVALID_ANCHOR_CERT
;
989 #endif /* TP_ROOT_CERT_ENABLE */
990 if(policyFail
&& (outErr
== CSSM_OK
)) {
991 /* only error in this function was policy failure */
992 outErr
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
996 for(certDex
=0; certDex
<numCerts
; certDex
++) {
997 thisCertInfo
= &certInfo
[certDex
];
998 iSignFreeCertInfo(clHand
, thisCertInfo
);
1000 tpFree(alloc
, certInfo
);