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 * tpCertGroup.cpp - Cert group functions (construct, verify)
22 * Created 10/5/2000 by Doug Mitchell.
25 #include "AppleTPSession.h"
26 #include "certGroupUtils.h"
27 #include "TPCertInfo.h"
28 #include "tpPolicies.h"
29 #include "tpdebugging.h"
30 #include <Security/oidsalg.h>
33 /*-----------------------------------------------------------------------------
37 * This function returns a pointer to a mallocd CSSM_CERTGROUP which
38 * refers to a mallocd list of raw ordered X.509 certs which verify back as
39 * far as the TP is able to go. The first cert of the returned list is the
40 * subject cert. The TP will attempt to search thru the DBs passed in
41 * DBList in order to complete the chain. The chain is completed when a
42 * self-signed (root) cert is found in the chain. The root cert may be
43 * present in the input CertGroupFrag, or it may have been obtained from
44 * one of the DBs passed in DBList. It is not an error if no root cert is
47 * The error conditions are:
48 * -- The first cert of CertGroupFrag is an invalid cert. NULL is returned,
49 * err = CSSM_TP_INVALID_CERTIFICATE.
50 * -- The root cert (if found) fails to verify. Valid certgroup is returned,
51 * err = CSSMERR_TP_VERIFICATION_FAILURE.
52 * -- Any cert in the (possibly partially) constructed chain has expired or
53 * isn't valid yet, err = CSSMERR_TP_CERT_EXPIRED or
54 * CSSMERR_TP_CERT_NOT_VALID_YET. A CertGroup is returned.
55 * -- CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET. If one of these
56 * conditions obtains for the first (leaf) cert, the function throws this
57 * error immediately and the outgoing cert group is empty. For subsequent certs,
58 * the temporal validity of a cert is only tested AFTER a cert successfully
59 * meets the cert chaining criteria (subject/issuer match and signature
60 * verify). A cert in a chain with this error is not added to the outgoing
62 * -- the usual errors like bad handle or memory failure.
65 * Two handles - to an open CL and CSP. The CSP must be capable of
66 * dealing with the signature algorithms used by the certs. The CL must be
69 * CertGroupFrag, an unordered array of raw X.509 certs in the form of a
70 * CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert
71 * which is eventually to be verified. The other certs can be in any order
72 * and may not even have any relevance to the cert chain being constructed.
73 * They may also be invalid certs.
75 * DBList, a list of DB/DL handles which may contain certs necessary to
76 * complete the desired cert chain. (Not currently implemented.)
78 *---------------------------------------------------------------------------*/
81 void AppleTPSession::CertGroupConstruct(CSSM_CL_HANDLE clHand
,
82 CSSM_CSP_HANDLE cspHand
,
83 const CSSM_DL_DB_LIST
&DBList
,
84 const void *ConstructParams
,
85 const CSSM_CERTGROUP
&CertGroupFrag
,
86 CSSM_CERTGROUP_PTR
&CertGroup
)
88 TPCertGroup
*tpCertGroup
;
89 CertGroupConstructPriv(clHand
,
94 CSSM_FALSE
, // allowExpired
96 CertGroup
= tpCertGroup
->buildCssmCertGroup();
102 * Private version of CertGroupConstruct, used by CertGroupConstruct and
103 * CertGroupVerify. Returns a TP-style TPCertGroup for further processing.
104 * This only throws CSSM-style exceptions in the following cases:
106 * -- input parameter errors
107 * -- the first (leaf) cert is bad (doesn't parse, expired, not valid yet).
109 * All other cert-related errors simply result in the bad cert being ignored.
110 * Other exceptions are gross system errors like malloc failure.
112 void AppleTPSession::CertGroupConstructPriv(CSSM_CL_HANDLE clHand
,
113 CSSM_CSP_HANDLE cspHand
,
114 const CSSM_DL_DB_LIST
&DBList
,
115 const void *ConstructParams
,
116 const CSSM_CERTGROUP
&CertGroupFrag
,
117 CSSM_BOOL allowExpired
,
118 TPCertGroup
*&CertGroup
)
120 TPCertGroup
*inCertGroup
; // unordered input certs
121 TPCertGroup
*outCertGroup
; // ordered, verified output certs
124 * subjectCert refers to the cert we're currently trying to verify. It's either
125 * an element in inCertGroup (if we're verifying a cert from the incoming
126 * CertGroupFrag) or dbSubject (if we're verifying a cert which came from a DB).
128 * Similarly, issuerCert, when non-NULL, points to a cert which has just
129 * been located as a verifiable issuer of subjectCert. It points to either
130 * an element in inCertGroup or to dbIssuer.
132 TPCertInfo
*subjectCert
; // the one we're working on
133 TPCertInfo
*issuerCert
= NULL
; // verified as next one in chain
134 TPCertInfo
*certInfo
; // working cert
135 unsigned certDex
; // index into certInfo
137 CSSM_RETURN outErr
= CSSM_OK
;
139 /* verify input args */
140 if(cspHand
== CSSM_INVALID_HANDLE
) {
141 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
);
143 if(clHand
== CSSM_INVALID_HANDLE
) {
144 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
);
146 if( (CertGroupFrag
.NumCerts
== 0) || // list is empty
147 (CertGroupFrag
.CertGroupType
!= CSSM_CERTGROUP_ENCODED_CERT
) ||
148 (CertGroupFrag
.GroupList
.CertList
[0].Data
== NULL
) || // first cert empty
149 (CertGroupFrag
.GroupList
.CertList
[0].Length
== 0)) { // first cert empty
150 CssmError::throwMe(CSSMERR_CL_INVALID_CERTGROUP_POINTER
);
152 switch(CertGroupFrag
.CertType
) {
153 case CSSM_CERT_X_509v1
:
154 case CSSM_CERT_X_509v2
:
155 case CSSM_CERT_X_509v3
:
158 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
160 switch(CertGroupFrag
.CertEncoding
) {
161 case CSSM_CERT_ENCODING_BER
:
162 case CSSM_CERT_ENCODING_DER
:
165 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
169 * Set up incoming and outgoing TPCertGrorups.
171 inCertGroup
= new TPCertGroup(*this, CertGroupFrag
.NumCerts
- 1);
172 outCertGroup
= new TPCertGroup(*this, CertGroupFrag
.NumCerts
);
175 * Parse first (leaf) cert. Note that this cert is special: if it's bad we abort
176 * immediately; otherwise it goes directly into outCertGroup.
179 certInfo
= new TPCertInfo(
180 &CertGroupFrag
.GroupList
.CertList
[0],
183 catch(CssmError cerr
) {
184 outErr
= CSSMERR_TP_INVALID_CERTIFICATE
;
188 /* everything else is way fatal */
192 /* verify this first one is current */
193 outErr
= certInfo
->isCurrent(allowExpired
);
198 /* Add to outCertGroup */
199 outCertGroup
->appendCert(certInfo
);
201 /* this'll be the first subject cert in the main loop */
202 subjectCert
= certInfo
;
205 * Add remaining input certs to inCertGroup. Note that this lets us
206 * skip bad incoming certs right away.
208 for(certDex
=1; certDex
<CertGroupFrag
.NumCerts
; certDex
++) {
210 certInfo
= new TPCertInfo(&CertGroupFrag
.GroupList
.CertList
[certDex
],
214 /* just ignore this cert */
217 inCertGroup
->appendCert(certInfo
);
222 * On entry, we have two TPCertGroups. InCertGroup contains n-1 certs, where n
223 * is the size of the CertGroupFrag passed to us by the caller. The certs in
224 * inCertGroup are unordered but are known to be parseable, CL-cacheable certs.
225 * OutGroupCert contains one cert, the incoming leaf cert.
227 * The job in this loop is to build an ordered, verified cert chain in
228 * outCertGroup out of certs from inCertGroup and/or DBList. As good certs
229 * are found in inCertGroup, they're removed from that TPCertGroup. On exit
230 * we delete inCertGroup, which deletes all the remaining TPCertInfo's in it.
231 * The constructed outCertGroup is returned to the caller.
234 * -- find a root cert in the chain
236 * -- or no more certs to add to chain.
239 /* top of loop: subjectCert is the cert we're trying to verify. */
241 /* is this a root cert? */
242 if(subjectCert
->isSelfSigned()) {
244 * Verify this alleged root cert. We're at the end of the chain no
245 * matter what happens here.
246 * Note we already validated before/after when this was tested
247 * as issuer (or, if it's the leaf cert, before we entered this loop).
249 outErr
= tp_VerifyCert(clHand
,
253 CSSM_FALSE
, // checkIssuerCurrent
254 CSSM_TRUE
); // allowExpired, don't care
258 /* Search unused incoming certs to find an issuer */
259 for(certDex
=0; certDex
<inCertGroup
->numCerts(); certDex
++) {
260 certInfo
= inCertGroup
->certAtIndex(certDex
);
262 /* potential issuer - names match? */
263 if(tpIsSameName(subjectCert
->issuerName(), certInfo
->subjectName())) {
264 /* yep, do a sig verify with "not before/after" check */
265 crtn
= tp_VerifyCert(clHand
,
273 /* YES! We'll add it to outCertGroup below...*/
274 issuerCert
= certInfo
;
275 inCertGroup
->removeCertAtIndex(certDex
);
277 case CSSMERR_TP_CERT_NOT_VALID_YET
:
278 case CSSMERR_TP_CERT_EXPIRED
:
279 /* special case - abort immediateley (note the cert
280 * sig verify succeeded.) */
284 /* just skip this one and keep looking */
288 } /* searching inCertGroup for issuer */
293 if(issuerCert
== NULL
) {
294 /* Issuer not in incoming cert group. Search DBList. */
295 CSSM_DATA_PTR foundCert
;
297 foundCert
= tpFindIssuer(tpHand
,
300 subjectCert
->certData(),
301 subjectCert
->issuerName(),
305 /* special case - abort immediately */
306 outErr
= subjectExpired
;
309 if(foundCert
!= NULL
) {
310 /* set issuerCert for this found cert */
311 issuerCert
= new TPCertInfo(foundCert
,
315 * free cert data obtained from DB
316 * FIXME: this assumes that OUR session allocators are the
317 * same ones used by the DL to malloc this cert!
318 * FIXME: handle exception here
320 tpFreeCssmData(*this, foundCert
, CSSM_TRUE
);
322 } /* Issuer not in incoming cert group */
323 #endif /* TP_DL_ENABLE */
325 if(issuerCert
== NULL
) {
326 /* end of search, broken chain */
331 * One way or the other, we've found a cert which verifies subjectCert.
332 * Add the issuer to outCertGroup and make it the new subjectCert for
335 outCertGroup
->appendCert(issuerCert
);
336 subjectCert
= issuerCert
;
342 CertGroup
= outCertGroup
;
344 CssmError::throwMe(outErr
);
347 /*-----------------------------------------------------------------------------
351 * -- Construct a cert chain using TP_CertGroupConstruct.
352 * -- Attempt to verify that cert chain against one of the known
353 * good certs passed in AnchorCerts.
354 * -- Optionally enforces additional policies (TBD) when verifying the cert chain.
355 * -- Optionally returns the entire cert chain constructed in
356 * TP_CertGroupConstruct and here, all the way to an anchor cert or as
357 * far as we were able to go, in *Evidence.
360 * Two handles - to an open CL and CSP. The CSP must be capable of
361 * dealing with the signature algorithms used by the certs. The CL must be
364 * RawCerts, an unordered array of raw certs in the form of a
365 * CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert
366 * which is eventually to be verified. The other certs can be in any order
367 * and may not even have any relevance to the cert chain being constructed.
368 * They may also be invalid certs.
370 * DBList, a list of DB/DL handles which may contain certs necessary to
371 * complete the desired cert chain. (Currently not implemented.)
373 * AnchorCerts, a list of known trusted certs.
374 * NumberOfAnchorCerts, size of AnchorCerts array.
376 * PolicyIdentifiers, Optional policy OID. NULL indicates default
377 * X.509 trust policy.
379 * Supported Policies:
380 * CSSMOID_APPLE_ISIGN
381 * CSSMOID_APPLE_X509_BASIC
383 * For both of these, the associated FieldValue must be {0, NULL},
385 * NumberOfPolicyIdentifiers, size of PolicyIdentifiers array, must be
388 * All other arguments must be zero/NULL.
391 * CSSM_OK : cert chain verified all the way back to an AnchorCert.
392 * CSSMERR_TP_INVALID_ANCHOR_CERT : In this case, the cert chain
393 * was validated back to a self-signed (root) cert found in either
394 * CertToBeVerified or in one of the DBs in DBList, but that root cert
395 * was *NOT* found in the AnchorCert list.
396 * CSSMERR_TP_NOT_TRUSTED: no root cert was found and no AnchorCert
397 * verified the end of the constructed cert chain.
398 * CSSMERR_TP_VERIFICATION_FAILURE: a root cert was found which does
400 * CSSMERR_TP_VERIFY_ACTION_FAILED: indicates a failure of the requested
402 * CSSMERR_TP_INVALID_CERTIFICATE: indicates a bad leaf cert.
403 * CSSMERR_TP_INVALID_REQUEST_INPUTS : no incoming VerifyContext.
404 * CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET: see comments
405 * for CertGroupConstruct.
406 *---------------------------------------------------------------------------*/
408 void AppleTPSession::CertGroupVerify(CSSM_CL_HANDLE clHand
,
409 CSSM_CSP_HANDLE cspHand
,
410 const CSSM_CERTGROUP
&CertGroupToBeVerified
,
411 const CSSM_TP_VERIFY_CONTEXT
*VerifyContext
,
412 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR VerifyContextResult
)
415 TPCertInfo
*lastCert
;
416 CSSM_BOOL verifiedToRoot
= CSSM_FALSE
;
418 CSSM_RETURN outErr
= CSSM_OK
;
420 const CSSM_TP_CALLERAUTH_CONTEXT
*cred
;
421 CSSM_OID_PTR oid
= NULL
;
422 CSSM_BOOL allowExpired
= CSSM_FALSE
;
423 TPCertGroup
*tpCertGroup
= NULL
; // created by
424 // CertGroupConstructPriv
425 TPCertInfo
*certInfo
= NULL
;
427 /* verify input args, skipping the ones checked by CertGroupConstruct */
428 if((VerifyContext
== NULL
) || (VerifyContext
->Cred
== NULL
)) {
429 /* the spec says that this is optional but we require it */
430 CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS
);
432 cred
= VerifyContext
->Cred
;
434 /* allow cert expiration errors? */
435 if(cred
->Policy
.PolicyControl
== CSSM_TP_ALLOW_EXPIRE
) {
436 allowExpired
= CSSM_TRUE
;
439 /* Check out requested policies */
440 switch(cred
->Policy
.NumberOfPolicyIds
) {
446 if(cred
->Policy
.PolicyIds
== NULL
) {
447 CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
451 * none of the supported policies allow any additional params
453 if((cred
->Policy
.PolicyIds
->FieldValue
.Data
!= NULL
) ||
454 (cred
->Policy
.PolicyIds
->FieldValue
.Length
!= 0)) {
455 CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
457 oid
= &cred
->Policy
.PolicyIds
->FieldOid
;
458 if(tpCompareOids(oid
, &CSSMOID_APPLE_ISIGN
)) {
461 else if(tpCompareOids(oid
, &CSSMOID_APPLE_X509_BASIC
)) {
462 policy
= kTPx509Basic
;
464 else if(tpCompareOids(oid
, &CSSMOID_APPLE_TP_SSL
)) {
469 CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
473 /* only zero or one allowed */
474 CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
477 /* now the args we can't deal with */
478 if(cred
->CallerCredentials
!= NULL
) {
479 CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER
);
481 /* FIXME - ANY OTHERS? */
483 /* get verified (possibly partial) outCertGroup - error is fatal */
484 /* BUT: we still return partial evidence if asked to...from now on. */
486 CertGroupConstructPriv(
489 *cred
->DBList
, // not optional to Construct!
491 CertGroupToBeVerified
,
495 catch(CssmError cerr
) {
496 outErr
= cerr
.cssmError();
499 /* others are way fatal */
500 CASSERT(tpCertGroup
!= NULL
);
501 CASSERT(tpCertGroup
->numCerts() >= 1);
503 /* subsequent errors and returns to out: */
506 * Case 1: last cert in outCertGroup is a root cert. See if
507 * the root cert is in AnchorCerts.
508 * Note that TP_CertGroupConstruct did the actual root
511 lastCert
= tpCertGroup
->lastCert();
512 if(lastCert
->isSelfSigned()) {
513 verifiedToRoot
= CSSM_TRUE
;
515 /* see if that root cert is identical to one of the anchor certs */
516 for(i
=0; i
<cred
->NumberOfAnchorCerts
; i
++) {
517 if(tp_CompareCerts(lastCert
->certData(), &cred
->AnchorCerts
[i
])) {
518 /* one fully successful return */
524 /* verified to a root cert which is not an anchor */
525 outErr
= CSSMERR_TP_INVALID_ANCHOR_CERT
;
529 /* try to validate lastCert with anchor certs */
530 /* note we're skipping the subject/issuer check...OK? */
531 for(i
=0; i
<cred
->NumberOfAnchorCerts
; i
++) {
533 certInfo
= new TPCertInfo(&cred
->AnchorCerts
[i
],
537 /* bad anchor cert - ignore it */
540 crtn
= tp_VerifyCert(clHand
,
544 CSSM_TRUE
, // check not/before of anchor
548 /* The other normal fully successful return. */
550 if(certInfo
->isSelfSigned()) {
551 verifiedToRoot
= CSSM_TRUE
;
555 * One more thing: add this anchor cert to the Evidence chain
558 tpCertGroup
->appendCert(certInfo
);
561 /* shoot - must be memory error */
562 verifiedToRoot
= CSSM_FALSE
;
564 outErr
= CSSMERR_TP_MEMORY_ERROR
;
568 case CSSMERR_TP_CERT_NOT_VALID_YET
:
569 case CSSMERR_TP_CERT_EXPIRED
:
570 /* special case - abort immediateley */
575 /* continue to next anchor */
579 } /* for each anchor */
581 /* partial chain, no root, not verifiable by anchor */
582 outErr
= CSSMERR_TP_NOT_TRUSTED
;
584 /* common exit - error or success */
587 * Do further policy verification if appropriate.
589 * SSL: CSSMERR_TP_NOT_TRUSTED and CSSMERR_TP_INVALID_ANCHOR_CERT
590 * are both special cases which can result in full success.
592 if((policy
== kTP_SSL
) && (outErr
== CSSMERR_TP_NOT_TRUSTED
)) {
593 /* see if last cert can be verified by an embedded SSL root */
594 certInfo
= tpCertGroup
->lastCert();
595 CSSM_BOOL brtn
= tp_verifyWithSslRoots(clHand
,
599 /* SSL success with no incoming root */
600 /* note unknown incoming root (INVALID_ANCHOR_CERT) is handled
601 * below, after tp_policyVerify */
605 if((outErr
== CSSM_OK
) || // full success so far
606 (outErr
== CSSMERR_TP_INVALID_ANCHOR_CERT
)) { // OK, but root not an anchor
608 CSSM_RETURN crtn
= tp_policyVerify(policy
,
615 /* don't override existing INVALID_ANCHOR_CERT on policy success */
618 else if((outErr
== CSSMERR_TP_INVALID_ANCHOR_CERT
) && (policy
== kTP_SSL
)) {
619 /* SSL - found a good anchor, move to full success */
624 /* return evidence - i.e., current chain - if asked to */
625 if(VerifyContextResult
!= NULL
) {
626 /* The spec is utterly bogus. We're going to punt and use
627 * CSSM_EVIDENCE_FORM_UNSPECIFIC to mean just a pointer to
628 * a CSSM_CERTGROUP. How's that!?
630 VerifyContextResult
->NumberOfEvidences
= 1;
631 VerifyContextResult
->Evidence
=
632 (CSSM_EVIDENCE_PTR
)malloc(sizeof(CSSM_EVIDENCE
));
633 VerifyContextResult
->Evidence
->EvidenceForm
= CSSM_EVIDENCE_FORM_UNSPECIFIC
;
634 VerifyContextResult
->Evidence
->Evidence
=
635 tpCertGroup
->buildCssmCertGroup();
638 /* delete (internaluse only) TPCertGroup */
641 CssmError::throwMe(outErr
);