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 * See if cert's Subject.commonName matches caller-specified hostname.
481 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
483 static CSSM_BOOL
tpCompareCommonName(
485 const char *hostName
,
488 char *commonName
= NULL
; // from cert's subject name
489 uint32 commonNameLen
= 0;
490 CSSM_DATA_PTR subjNameData
= NULL
;
492 CSSM_BOOL ourRtn
= CSSM_FALSE
;
494 crtn
= cert
.fetchField(&CSSMOID_X509V1SubjectNameCStruct
, &subjNameData
);
496 /* should never happen, we shouldn't be here if there is no subject */
497 errorLog0("tp_verifySslOpts: error retrieving subject name");
500 CSSM_X509_NAME_PTR x509name
= (CSSM_X509_NAME_PTR
)subjNameData
->Data
;
501 if((x509name
== NULL
) || (subjNameData
->Length
!= sizeof(CSSM_X509_NAME
))) {
502 errorLog0("tp_verifySslOpts: malformed CSSM_X509_NAME");
503 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
507 /* Now grunge thru the X509 name looking for a common name */
508 CSSM_X509_TYPE_VALUE_PAIR
*ptvp
;
509 CSSM_X509_RDN_PTR rdnp
;
513 for(rdnDex
=0; rdnDex
<x509name
->numberOfRDNs
; rdnDex
++) {
514 rdnp
= &x509name
->RelativeDistinguishedName
[rdnDex
];
515 for(pairDex
=0; pairDex
<rdnp
->numberOfPairs
; pairDex
++) {
516 ptvp
= &rdnp
->AttributeTypeAndValue
[pairDex
];
517 if(tpCompareOids(&ptvp
->type
, &CSSMOID_CommonName
)) {
518 commonName
= (char *)ptvp
->value
.Data
;
519 commonNameLen
= ptvp
->value
.Length
;
520 ourRtn
= tpCompareHostNames(hostName
, hostNameLen
,
521 commonName
, commonNameLen
);
526 /* else keep going, maybe there's another common name */
533 cert
.freeField(&CSSMOID_X509V1SubjectNameCStruct
, subjNameData
);
538 * Compare ASCII form of an IP address to a CSSM_DATA containing
539 * the IP address's numeric components. Returns true on match.
541 static CSSM_BOOL
tpCompIpAddrStr(
544 const CSSM_DATA
*numeric
)
546 const char *cp
= str
;
550 if((numeric
== NULL
) || (numeric
->Length
== 0) || (str
== NULL
)) {
553 if(cp
[strLen
- 1] == '\0') {
554 /* ignore NULL terminator */
557 for(unsigned dex
=0; dex
<numeric
->Length
; dex
++) {
558 /* cp points to start of current string digit */
560 const char *lastChar
= cp
+ strLen
;
562 for( ; nextDot
<lastChar
; nextDot
++) {
563 if(*nextDot
== '.') {
567 if(nextDot
== lastChar
) {
568 /* legal and required on last digit */
569 if(dex
!= (numeric
->Length
- 1)) {
573 else if(dex
== (numeric
->Length
- 1)) {
576 unsigned digLen
= nextDot
- cp
;
577 if(digLen
>= sizeof(buf
)) {
581 memmove(buf
, cp
, digLen
);
583 /* incr digLen to include the next dot */
587 int digVal
= atoi(buf
);
588 if(digVal
!= numeric
->Data
[dex
]) {
596 * See if cert's subjectAltName matches caller-specified hostname, either
597 * as a dnsName or an iPAddress.
599 * Returns CSSM_TRUE if match, else returns CSSM_FALSE. Also indicates
600 * whether or not a dnsName was found (in which case the subject's
601 * common name should NOT be a candidate for verification).
603 static CSSM_BOOL
tpCompareSubjectAltName(
605 const char *hostName
,
607 bool &dnsNameFound
) // RETURNED
609 CSSM_DATA_PTR subjAltNameData
= NULL
;
611 CSSM_BOOL ourRtn
= CSSM_FALSE
;
613 dnsNameFound
= false;
614 crtn
= cert
.fetchField(&CSSMOID_SubjectAltName
, &subjAltNameData
);
616 /* common failure, no subjectAltName found */
619 CSSM_X509_EXTENSION_PTR exten
=
620 (CSSM_X509_EXTENSION_PTR
)subjAltNameData
->Data
;
621 /* Paranoid check of extension integrity */
622 if((exten
== NULL
) ||
623 (subjAltNameData
->Length
!= sizeof(CSSM_X509_EXTENSION
)) ||
624 (exten
->format
!= CSSM_X509_DATAFORMAT_PARSED
) ||
625 (exten
->value
.parsedValue
== NULL
)) {
626 errorLog0("tpCompareSubjectAltName: malformed CSSM_X509_EXTENSION");
627 cert
.freeField(&CSSMOID_SubjectAltName
, subjAltNameData
);
631 CE_GeneralNames
*names
= (CE_GeneralNames
*)exten
->value
.parsedValue
;
633 unsigned serverNameLen
;
635 /* Search thru the CE_GeneralNames looking for a DNSName or IP Address */
636 for(unsigned dex
=0; dex
<names
->numNames
; dex
++) {
637 CE_GeneralName
*name
= &names
->generalName
[dex
];
638 switch(name
->nameType
) {
640 ourRtn
= tpCompIpAddrStr(hostName
, hostNameLen
, &name
->name
);
644 if(name
->berEncoded
) {
645 errorLog0("tpCompareSubjectAltName: malformed "
646 "CE_GeneralName (1)\n");
649 serverName
= (char *)name
->name
.Data
;
650 if(serverName
== NULL
) {
651 errorLog0("tpCompareSubjectAltName: malformed "
652 "CE_GeneralName (2)\n");
655 serverNameLen
= name
->name
.Length
;
656 ourRtn
= tpCompareHostNames(hostName
, hostNameLen
,
657 serverName
, serverNameLen
);
662 /* not interested, proceed to next name */
670 cert
.freeField(&CSSMOID_SubjectAltName
, subjAltNameData
);
674 /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
675 static CSSM_BOOL
tpIsNumeric(
676 const char *hostName
,
677 unsigned hostNameLen
)
679 if(hostName
[hostNameLen
- 1] == '\0') {
680 /* ignore NULL terminator */
683 for(unsigned i
=0; i
<hostNameLen
; i
++) {
684 char c
= *hostName
++;
696 * Verify SSL options. Currently this just consists of matching the
697 * leaf cert's subject common name against the caller's (optional)
700 static CSSM_RETURN
tp_verifySslOpts(
701 TPCertGroup
&certGroup
,
702 const CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
)
704 if(sslOpts
== NULL
) {
709 unsigned hostNameLen
= sslOpts
->ServerNameLen
;
711 if(hostNameLen
== 0) {
715 if(sslOpts
->ServerName
== NULL
) {
716 return CSSMERR_TP_INVALID_POINTER
;
719 /* convert caller's hostname string to lower case */
720 char *hostName
= (char *)certGroup
.alloc().malloc(hostNameLen
);
721 memmove(hostName
, sslOpts
->ServerName
, hostNameLen
);
722 tpToLower(hostName
, hostNameLen
);
724 TPCertInfo
*leaf
= certGroup
.certAtIndex(0);
725 assert(leaf
!= NULL
);
727 CSSM_BOOL match
= CSSM_FALSE
;
729 /* First check subjectAltName... */
730 bool dnsNameFound
= false;
731 match
= tpCompareSubjectAltName(*leaf
, hostName
, hostNameLen
,
734 * Then common name, if
735 * -- no match from subjectAltName, AND
736 * -- dnsName was NOT found, AND
737 * -- hostName is not strictly numeric form (1.2.3.4)
739 if(!match
&& !dnsNameFound
&& !tpIsNumeric(hostName
, hostNameLen
)) {
740 match
= tpCompareCommonName(*leaf
, hostName
, hostNameLen
);
742 certGroup
.alloc().free(hostName
);
747 leaf
->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH
);
748 return CSSMERR_TP_VERIFY_ACTION_FAILED
;
753 * RFC2459 says basicConstraints must be flagged critical for
754 * CA certs, but Verisign doesn't work that way.
756 #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0
759 * TP iSign spec says Extended Key Usage required for leaf certs,
760 * but Verisign doesn't work that way.
762 #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0
765 * TP iSign spec says Subject Alternate Name required for leaf certs,
766 * but Verisign doesn't work that way.
768 #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0
771 * TP iSign spec originally required KeyUsage for all certs, but
772 * Verisign doesn't have that in their roots.
774 #define KEY_USAGE_REQUIRED_FOR_ROOT 0
777 * Public routine to perform TP verification on a constructed
779 * Returns CSSM_TRUE on success.
780 * Asumes the chain has passed basic subject/issuer verification. First cert of
781 * incoming certGroup is end-entity (leaf).
783 * Per-policy details:
784 * iSign: Assumes that last cert in incoming certGroup is a root cert.
785 * Also assumes a cert group of more than one cert.
786 * kTPx509Basic: CertGroup of length one allowed.
788 CSSM_RETURN
tp_policyVerify(
790 CssmAllocator
&alloc
,
791 CSSM_CL_HANDLE clHand
,
792 CSSM_CSP_HANDLE cspHand
,
793 TPCertGroup
*certGroup
,
794 CSSM_BOOL verifiedToRoot
, // last cert is good root
795 const CSSM_APPLE_TP_ACTION_DATA
*actionData
,
796 const CSSM_APPLE_TP_SSL_OPTIONS
*sslOpts
,
797 void *policyOpts
) // future options
799 iSignCertInfo
*certInfo
= NULL
;
801 iSignCertInfo
*thisCertInfo
;
805 CSSM_BOOL cA
= CSSM_FALSE
; // init for compiler warning
806 CSSM_BOOL isLeaf
; // end entity
807 CSSM_BOOL isRoot
; // root cert
808 CE_ExtendedKeyUsage
*extendUsage
;
809 CE_AuthorityKeyID
*authorityId
;
810 CSSM_RETURN outErr
= CSSM_OK
; // for gross, non-policy errors
811 CSSM_BOOL policyFail
= CSSM_FALSE
;
813 /* First, kTPDefault is a nop here */
814 if(policy
== kTPDefault
) {
818 if(certGroup
== NULL
) {
819 return CSSMERR_TP_INVALID_CERTGROUP
;
821 numCerts
= certGroup
->numCerts();
823 return CSSMERR_TP_INVALID_CERTGROUP
;
825 if(policy
== kTPiSign
) {
826 if(!verifiedToRoot
) {
827 /* no way, this requires a root cert */
828 return CSSMERR_TP_INVALID_CERTGROUP
;
831 /* nope, not for iSign */
832 return CSSMERR_TP_INVALID_CERTGROUP
;
836 /* cook up an iSignCertInfo array */
837 certInfo
= (iSignCertInfo
*)tpCalloc(alloc
, numCerts
, sizeof(iSignCertInfo
));
838 /* subsequent errors to errOut: */
840 /* fill it with interesting info from parsed certs */
841 for(certDex
=0; certDex
<numCerts
; certDex
++) {
842 if(iSignGetCertInfo(alloc
,
843 certGroup
->certAtIndex(certDex
),
844 &certInfo
[certDex
])) {
845 (certGroup
->certAtIndex(certDex
))->addStatusCode(
846 CSSMERR_TP_INVALID_CERTIFICATE
);
847 /* this one is fatal */
848 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
854 * OK, the heart of TP enforcement.
855 * First check for presence of required extensions and
856 * critical extensions we don't understand.
858 for(certDex
=0; certDex
<numCerts
; certDex
++) {
859 thisCertInfo
= &certInfo
[certDex
];
860 TPCertInfo
*thisTpCertInfo
= certGroup
->certAtIndex(certDex
);
862 if(thisCertInfo
->foundUnknownCritical
) {
863 /* illegal for all policies */
864 errorLog0("tp_policyVerify: critical flag in unknown extension\n");
865 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
);
866 policyFail
= CSSM_TRUE
;
870 * Note it's possible for both of these to be true, for a
871 * of length one (kTPx509Basic only!)
873 isLeaf
= (certDex
== 0) ? CSSM_TRUE
: CSSM_FALSE
;
874 isRoot
= (certDex
== (numCerts
- 1)) ? CSSM_TRUE
: CSSM_FALSE
;
877 * BasicConstraints.cA
878 * iSign: required in all but leaf and root,
879 * for which it is optional (with default values of false
880 * for leaf and true for root).
882 * kTP_SSL: always optional, default of false for leaf and
884 * All: cA must be false for leaf, true for others
886 if(!thisCertInfo
->basicConstraints
.present
) {
888 /* cool, use default; note that kTPx509Basic with
889 * certGroup length of one may take this case */
893 /* cool, use default */
901 * not present, not leaf, not root, kTPx509Basic
902 * ....RFC2459 says this can not be a CA
907 /* required for iSign in this position */
908 errorLog0("tp_policyVerify: no basicConstraints\n");
909 policyFail
= CSSM_TRUE
;
910 thisTpCertInfo
->addStatusCode(
911 CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS
);
920 /* basicConstraints present */
921 #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL
922 /* disabled for verisign compatibility */
923 if(!thisCertInfo
->basicConstraints
.critical
) {
925 errorLog0("tp_policyVerify: basicConstraints marked not critical\n");
926 policyFail
= CSSM_TRUE
;
927 thisTpCertInfo
->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED
);
929 #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
931 const CE_BasicConstraints
*bcp
=
932 &thisCertInfo
->basicConstraints
.extnData
->basicConstraints
;
936 /* Verify pathLenConstraint if present */
937 if(!isLeaf
&& // leaf, certDex=0, don't care
938 cA
&& // p.l.c. only valid for CAs
939 bcp
->pathLenConstraintPresent
) { // present?
941 * pathLenConstraint=0 legal for certDex 1 only
942 * pathLenConstraint=1 legal for certDex {1,2}
945 if(certDex
> (bcp
->pathLenConstraint
+ 1)) {
946 errorLog0("tp_policyVerify: pathLenConstraint exceeded\n");
947 policyFail
= CSSM_TRUE
;
948 thisTpCertInfo
->addStatusCode(
949 CSSMERR_APPLETP_PATH_LEN_CONSTRAINT
);
955 /* special case to allow a chain of length 1, leaf and root
956 * both true (kTPx509Basic, kTP_SSL only) */
958 errorLog0("tp_policyVerify: cA true for leaf\n");
959 policyFail
= CSSM_TRUE
;
960 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
);
963 errorLog0("tp_policyVerify: cA false for non-leaf\n");
964 policyFail
= CSSM_TRUE
;
965 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_CA
);
969 * Authority Key Identifier optional
970 * iSign : only allowed in !root.
971 * If present, must not be critical.
973 * kTP_SSL : ignored (though used later for chain verification)
975 if((policy
== kTPiSign
) && thisCertInfo
->authorityId
.present
) {
977 errorLog0("tp_policyVerify: authorityId in root\n");
978 policyFail
= CSSM_TRUE
;
979 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
);
981 if(thisCertInfo
->authorityId
.critical
) {
982 /* illegal per RFC 2459 */
983 errorLog0("tp_policyVerify: authorityId marked critical\n");
984 policyFail
= CSSM_TRUE
;
985 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID
);
990 * Subject Key Identifier optional
991 * iSign : can't be critical.
993 * kTP_SSL : ignored (though used later for chain verification)
995 if(thisCertInfo
->subjectId
.present
) {
996 if((policy
== kTPiSign
) && thisCertInfo
->subjectId
.critical
) {
997 errorLog0("tp_policyVerify: subjectId marked critical\n");
998 policyFail
= CSSM_TRUE
;
999 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID
);
1004 * Key Usage optional except as noted required
1005 * iSign : required for non-root/non-leaf
1006 * Leaf cert : if present, usage = digitalSignature
1007 * Exception : if leaf, and keyUsage not present,
1008 * netscape-cert-type must be present, with
1009 * Object Signing bit set
1010 * kTPx509Basic : non-leaf : usage = keyCertSign
1013 if(thisCertInfo
->keyUsage
.present
) {
1015 * Leaf cert: usage = digitalSignature
1016 * Others: usage = keyCertSign
1017 * We only require that one bit to be set, we ignore others.
1020 if(policy
== kTPiSign
) {
1021 expUsage
= CE_KU_DigitalSignature
;
1024 /* hack to accept whatever's there */
1025 expUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
1029 /* this is true for all policies */
1030 expUsage
= CE_KU_KeyCertSign
;
1032 actUsage
= thisCertInfo
->keyUsage
.extnData
->keyUsage
;
1033 if(!(actUsage
& expUsage
)) {
1034 errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n",
1035 (certDex
== 0) ? "TRUE" : "FALSE", actUsage
);
1036 policyFail
= CSSM_TRUE
;
1037 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
1040 else if(policy
== kTPiSign
) {
1042 * iSign requires keyUsage present for non root OR
1043 * netscape-cert-type/ObjectSigning for leaf
1045 if(isLeaf
&& thisCertInfo
->netscapeCertType
.present
) {
1046 CE_NetscapeCertType ct
=
1047 thisCertInfo
->netscapeCertType
.extnData
->netscapeCertType
;
1049 if(!(ct
& CE_NCT_ObjSign
)) {
1050 errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n");
1051 policyFail
= CSSM_TRUE
;
1052 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
1056 errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n");
1057 policyFail
= CSSM_TRUE
;
1058 thisTpCertInfo
->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE
);
1061 } /* for certDex, checking presence of extensions */
1064 * Special case checking for leaf (end entity) cert
1066 * iSign only: Extended key usage, optional for leaf,
1067 * value CSSMOID_ExtendedUseCodeSigning
1069 if((policy
== kTPiSign
) && certInfo
[0].extendKeyUsage
.present
) {
1070 extendUsage
= &certInfo
[0].extendKeyUsage
.extnData
->extendedKeyUsage
;
1071 if(extendUsage
->numPurposes
!= 1) {
1072 errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n",
1073 (int)extendUsage
->numPurposes
);
1074 policyFail
= CSSM_TRUE
;
1075 (certGroup
->certAtIndex(0))->addStatusCode(
1076 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
1078 if(!tpCompareOids(extendUsage
->purposes
,
1079 &CSSMOID_ExtendedUseCodeSigning
)) {
1080 errorLog0("tp_policyVerify: bad extendKeyUsage\n");
1081 policyFail
= CSSM_TRUE
;
1082 (certGroup
->certAtIndex(0))->addStatusCode(
1083 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
);
1088 * Verify authorityId-->subjectId linkage.
1089 * All optional - skip if needed fields not present.
1090 * Also, always skip last (root) cert.
1092 for(certDex
=0; certDex
<(numCerts
-1); certDex
++) {
1093 if(!certInfo
[certDex
].authorityId
.present
||
1094 !certInfo
[certDex
+1].subjectId
.present
) {
1097 authorityId
= &certInfo
[certDex
].authorityId
.extnData
->authorityKeyID
;
1098 if(!authorityId
->keyIdentifierPresent
) {
1099 /* we only know how to compare keyIdentifier */
1102 if(!tpCompareCssmData(&authorityId
->keyIdentifier
,
1103 &certInfo
[certDex
+1].subjectId
.extnData
->subjectKeyID
)) {
1104 errorLog0("tp_policyVerify: bad key ID linkage\n");
1105 policyFail
= CSSM_TRUE
;
1106 (certGroup
->certAtIndex(certDex
))->addStatusCode(
1107 CSSMERR_APPLETP_INVALID_ID_LINKAGE
);
1112 * SSL: optionally verify common name.
1113 * FIXME - should this be before or after the root cert test? How can
1114 * we return both errors?
1116 if(policy
== kTP_SSL
) {
1117 CSSM_RETURN cerr
= tp_verifySslOpts(*certGroup
, sslOpts
);
1119 policyFail
= CSSM_TRUE
;
1123 /* iSign, SSL: compare root against known root certs */
1124 /* FIXME - this goes away soon */
1125 #if TP_ROOT_CERT_ENABLE
1126 if((outErr
== CSSM_OK
) && // skip if we have a gross error (other than policy failure)
1127 (actionData
!= NULL
) &&
1128 (actionData
->ActionFlags
& 0x80000000)) { // The secret "enable root cert check" flag
1129 TPCertInfo
*lastCert
= certGroup
->lastCert();
1130 if(policy
== kTPiSign
) {
1131 bool brtn
= tp_isIsignRootCert(clHand
, lastCert
);
1133 policyFail
= CSSM_TRUE
;
1136 else if(verifiedToRoot
&& (policy
== kTP_SSL
)) {
1137 /* note SSL doesn't require root here */
1138 bool brtn
= tp_isSslRootCert(clHand
, lastCert
);
1140 outErr
= CSSMERR_TP_INVALID_ANCHOR_CERT
;
1144 #endif /* TP_ROOT_CERT_ENABLE */
1145 if(policyFail
&& (outErr
== CSSM_OK
)) {
1146 /* only error in this function was policy failure */
1147 outErr
= CSSMERR_TP_VERIFY_ACTION_FAILED
;
1150 /* free resources */
1151 for(certDex
=0; certDex
<numCerts
; certDex
++) {
1152 thisCertInfo
= &certInfo
[certDex
];
1153 iSignFreeCertInfo(clHand
, thisCertInfo
);
1155 tpFree(alloc
, certInfo
);