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 * TPCertInfo.h - TP's private certificate info classes
22 * Written 10/23/2000 by Doug Mitchell.
25 #include "TPCertInfo.h"
26 #include "tpdebugging.h"
28 #include "certGroupUtils.h"
29 #include <Security/cssmapi.h>
30 #include <Security/x509defs.h>
31 #include <Security/oidscert.h>
32 #include <Security/oidsalg.h>
33 #include <string.h> /* for memcmp */
34 #include <Security/threading.h> /* for Mutex */
35 #include <Security/globalizer.h>
36 #include <Security/debugging.h>
37 #include <Security/cssmapple.h>
39 #define tpTimeDbg(args...) debug("tpTime", ## args)
42 * No default constructor - this is the only way.
43 * This caches the cert and fetches subjectName and issuerName
44 * to ensure the incoming certData is well-constructed.
46 TPCertInfo::TPCertInfo(
47 const CSSM_DATA
*certData
,
48 CSSM_CL_HANDLE clHand
,
49 const char *cssmTimeStr
, // = NULL
50 bool copyCertData
) : // true: we copy, we free
51 // false - caller owns
53 mCacheHand(CSSM_INVALID_HANDLE
),
65 mDlDbHandle
.DBHandle
= 0;
66 mDlDbHandle
.DLHandle
= 0;
69 mCertData
= tpMallocCopyCssmData(CssmAllocator::standard(), certData
);
72 mCertData
= const_cast<CSSM_DATA
*>(certData
);
74 mWeOwnTheData
= copyCertData
;
78 crtn
= CSSM_CL_CertCache(clHand
, mCertData
, &mCacheHand
);
81 CssmError::throwMe(crtn
);
84 /* fetch subject name */
85 crtn
= fetchField(&CSSMOID_X509V1SubjectName
, &mSubjectName
);
89 CssmError::throwMe(crtn
);
92 /* fetch issuer name */
93 crtn
= fetchField(&CSSMOID_X509V1IssuerName
, &mIssuerName
);
97 CssmError::throwMe(crtn
);
100 /* calculate other commonly used fields */
101 mIsRoot
= tpCompareCssmData(mSubjectName
, mIssuerName
) ? true : false;
102 calculateCurrent(cssmTimeStr
);
105 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
106 TPCertInfo::~TPCertInfo()
111 void TPCertInfo::releaseResources()
113 if(mWeOwnTheData
&& (mCertData
!= NULL
)) {
114 tpFreeCssmData(CssmAllocator::standard(), mCertData
, CSSM_TRUE
);
117 freeField(&CSSMOID_X509V1SubjectName
, mSubjectName
);
120 freeField(&CSSMOID_X509V1IssuerName
, mIssuerName
);
122 if(mCacheHand
!= CSSM_INVALID_HANDLE
) {
123 CSSM_CL_CertAbortCache(mClHand
, mCacheHand
);
130 /* fetch arbitrary field from cached cert */
131 CSSM_RETURN
TPCertInfo::fetchField(
132 const CSSM_OID
*fieldOid
,
133 CSSM_DATA_PTR
*fieldData
) // mallocd by CL and RETURNED
137 uint32 NumberOfFields
= 0;
138 CSSM_HANDLE resultHand
= 0;
141 crtn
= CSSM_CL_CertGetFirstCachedFieldValue(
151 if(NumberOfFields
!= 1) {
152 errorLog1("TPCertInfo::fetchField: numFields %d, expected 1\n",
153 (int)NumberOfFields
);
155 CSSM_CL_CertAbortQuery(mClHand
, resultHand
);
159 /* free arbitrary field obtained from fetchField() */
160 CSSM_RETURN
TPCertInfo::freeField(
161 const CSSM_OID
*fieldOid
,
162 CSSM_DATA_PTR fieldData
)
164 return CSSM_CL_FreeFieldValue(mClHand
, fieldOid
, fieldData
);
169 CSSM_CL_HANDLE
TPCertInfo::clHand()
174 CSSM_HANDLE
TPCertInfo::cacheHand()
179 const CSSM_DATA
*TPCertInfo::certData()
181 CASSERT(mCertData
!= NULL
);
185 const CSSM_DATA
*TPCertInfo::subjectName()
187 CASSERT(mSubjectName
!= NULL
);
191 const CSSM_DATA
*TPCertInfo::issuerName()
193 CASSERT(mIssuerName
!= NULL
);
198 * Verify validity (not before/after). Only throws on gross error
199 * (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
201 * We use some stdlib time calls over in tpTime.c; the stdlib function
202 * gmtime() is not thread-safe, so we do the protection here. Note that
203 * this makes *our* calls to gmtime() thread-safe, but if the app has
204 * other threads which are also calling gmtime, we're out of luck.
206 ModuleNexus
<Mutex
> tpTimeLock
;
208 void TPCertInfo::calculateCurrent(
209 const char *cssmTimeStr
/* = NULL */)
211 CSSM_DATA_PTR notBeforeField
= NULL
;
212 CSSM_DATA_PTR notAfterField
= NULL
;
213 CSSM_RETURN crtn
= CSSM_OK
;
214 CSSM_X509_TIME
*xNotAfter
;
216 CASSERT(mCacheHand
!= CSSM_INVALID_HANDLE
);
217 crtn
= fetchField(&CSSMOID_X509V1ValidityNotBefore
, ¬BeforeField
);
219 errorLog0("TPCertInfo::calculateCurrent: GetField error");
220 CssmError::throwMe(crtn
);
223 /* subsequent errors to errOut */
225 if(cssmTimeStr
!= NULL
) {
226 /* caller specifies verification time base */
227 if(timeStringToTm(cssmTimeStr
, strlen(cssmTimeStr
), &now
)) {
228 errorLog0("TPCertInfo::calculateCurrent: timeStringToTm error");
229 CssmError::throwMe(CSSMERR_TP_INVALID_TIMESTRING
);
233 /* time base = right now */
234 StLock
<Mutex
> _(tpTimeLock());
238 CSSM_X509_TIME
*xNotBefore
= (CSSM_X509_TIME
*)notBeforeField
->Data
;
240 if(timeStringToTm((char *)xNotBefore
->time
.Data
, xNotBefore
->time
.Length
,
242 errorLog0("TPCertInfo::calculateCurrent: malformed notBefore time\n");
243 crtn
= CSSMERR_TP_INVALID_CERT_POINTER
;
246 if(compareTimes(&now
, ¬Before
) < 0) {
248 tpTimeDbg("\nTP_CERT_NOT_VALID_YET:\n now y:%d m:%d d:%d h:%d m:%d",
249 now
.tm_year
, now
.tm_mon
, now
.tm_mday
, now
.tm_hour
,
251 tpTimeDbg(" notBefore y:%d m:%d d:%d h:%d m:%d",
252 notBefore
.tm_year
, notBefore
.tm_mon
, notBefore
.tm_mday
,
253 notBefore
.tm_hour
, notBefore
.tm_min
);
256 mNotValidYet
= false;
260 crtn
= fetchField(&CSSMOID_X509V1ValidityNotAfter
, ¬AfterField
);
262 errorLog0("TPCertInfo::calculateCurrent: GetField error");
266 xNotAfter
= (CSSM_X509_TIME
*)notAfterField
->Data
;
267 if(timeStringToTm((char *)xNotAfter
->time
.Data
, xNotAfter
->time
.Length
,
269 errorLog0("TPCertInfo::calculateCurrent: malformed notAfter time\n");
270 crtn
= CSSMERR_TP_INVALID_CERT_POINTER
;
273 else if(compareTimes(&now
, ¬After
) > 0) {
274 crtn
= CSSMERR_TP_CERT_EXPIRED
;
275 tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
277 now
.tm_year
, now
.tm_mon
, now
.tm_mday
,
278 now
.tm_hour
, now
.tm_min
);
279 tpTimeDbg(" notAfter y:%d m:%d d:%d h:%d m:%d",
280 notAfter
.tm_year
, notAfter
.tm_mon
, notAfter
.tm_mday
,
281 notAfter
.tm_hour
, notAfter
.tm_min
);
290 freeField(&CSSMOID_X509V1ValidityNotAfter
, notAfterField
);
293 freeField(&CSSMOID_X509V1ValidityNotBefore
, notBeforeField
);
295 if(crtn
!= CSSM_OK
) {
296 CssmError::throwMe(crtn
);
300 CSSM_RETURN
TPCertInfo::isCurrent(
301 CSSM_BOOL allowExpired
)
304 return CSSMERR_TP_CERT_NOT_VALID_YET
;
306 if(allowExpired
|| !mExpired
) {
310 return CSSMERR_TP_CERT_EXPIRED
;
314 void TPCertInfo::addStatusCode(CSSM_RETURN code
)
317 mStatusCodes
= (CSSM_RETURN
*)realloc(mStatusCodes
,
318 mNumStatusCodes
* sizeof(CSSM_RETURN
));
319 mStatusCodes
[mNumStatusCodes
- 1] = code
;
323 *** TPCertGroup class
325 TPCertGroup::TPCertGroup(
326 CssmAllocator
&alloc
,
331 mCertInfo
= (TPCertInfo
**)alloc
.malloc(numCerts
* sizeof(TPCertInfo
*));
332 mSizeofCertInfo
= numCerts
;
336 * Deletes all TPCertInfo's.
338 TPCertGroup::~TPCertGroup()
341 for(i
=0; i
<mNumCerts
; i
++) {
344 mAlloc
.free(mCertInfo
);
347 /* add/remove/access TPTCertInfo's. */
348 void TPCertGroup::appendCert(
349 TPCertInfo
*certInfo
) // appends to end of mCertInfo
351 if(mNumCerts
== mSizeofCertInfo
) {
352 /* FIXME - do we need the realloc workaround we used to have in TPSession? */
353 mSizeofCertInfo
*= 2;
354 mCertInfo
= (TPCertInfo
**)mAlloc
.realloc(mCertInfo
,
355 mSizeofCertInfo
* sizeof(TPCertInfo
*));
357 mCertInfo
[mNumCerts
++] = certInfo
;
360 TPCertInfo
*TPCertGroup::certAtIndex(
363 if(index
> (mNumCerts
- 1)) {
364 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
366 return mCertInfo
[index
];
369 TPCertInfo
*TPCertGroup::removeCertAtIndex(
370 unsigned index
) // doesn't delete the cert, just
371 // removes it from out list
373 if(index
> (mNumCerts
- 1)) {
374 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
376 TPCertInfo
*rtn
= mCertInfo
[index
];
378 /* removed requested element and compact remaining array */
380 for(i
=index
; i
<(mNumCerts
- 1); i
++) {
381 mCertInfo
[i
] = mCertInfo
[i
+1];
387 unsigned TPCertGroup::numCerts()
392 TPCertInfo
*TPCertGroup::firstCert()
395 /* the caller really should not do this... */
396 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
403 TPCertInfo
*TPCertGroup::lastCert()
406 /* the caller really should not do this... */
407 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
410 return mCertInfo
[mNumCerts
- 1];
414 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
415 CSSM_CERTGROUP_PTR
TPCertGroup::buildCssmCertGroup()
417 CSSM_CERTGROUP_PTR cgrp
=
418 (CSSM_CERTGROUP_PTR
)mAlloc
.malloc(sizeof(CSSM_CERTGROUP
));
419 cgrp
->NumCerts
= mNumCerts
;
420 cgrp
->CertGroupType
= CSSM_CERTGROUP_DATA
;
421 cgrp
->CertType
= CSSM_CERT_X_509v3
;
422 cgrp
->CertEncoding
= CSSM_CERT_ENCODING_DER
;
425 cgrp
->GroupList
.CertList
= NULL
;
428 cgrp
->GroupList
.CertList
= (CSSM_DATA_PTR
)mAlloc
.calloc(mNumCerts
,
430 for(unsigned i
=0; i
<mNumCerts
; i
++) {
431 tpCopyCssmData(mAlloc
, mCertInfo
[i
]->certData(),
432 &cgrp
->GroupList
.CertList
[i
]);
437 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
438 CSSM_TP_APPLE_EVIDENCE_INFO
*TPCertGroup::buildCssmEvidenceInfo()
440 CSSM_TP_APPLE_EVIDENCE_INFO
*infoArray
;
442 infoArray
= (CSSM_TP_APPLE_EVIDENCE_INFO
*)mAlloc
.calloc(mNumCerts
,
443 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO
));
444 for(unsigned i
=0; i
<mNumCerts
; i
++) {
445 TPCertInfo
*certInfo
= mCertInfo
[i
];
446 CSSM_TP_APPLE_EVIDENCE_INFO
*evInfo
= &infoArray
[i
];
448 /* first the booleans */
449 if(certInfo
->isExpired()) {
450 evInfo
->StatusBits
|= CSSM_CERT_STATUS_EXPIRED
;
452 if(certInfo
->isNotValidYet()) {
453 evInfo
->StatusBits
|= CSSM_CERT_STATUS_NOT_VALID_YET
;
455 if(certInfo
->dlDbHandle().DLHandle
== 0) {
456 if(certInfo
->isAnchor()) {
457 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_ANCHORS
;
460 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
;
463 if(certInfo
->isSelfSigned()) {
464 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_ROOT
;
467 unsigned numCodes
= certInfo
->numStatusCodes();
469 evInfo
->NumStatusCodes
= numCodes
;
470 evInfo
->StatusCodes
= (CSSM_RETURN
*)mAlloc
.calloc(numCodes
,
471 sizeof(CSSM_RETURN
));
472 for(unsigned j
=0; j
<numCodes
; j
++) {
473 evInfo
->StatusCodes
[j
] = (certInfo
->statusCodes())[j
];
477 evInfo
->Index
= certInfo
->index();
478 evInfo
->DlDbHandle
= certInfo
->dlDbHandle();
479 evInfo
->UniqueRecord
= certInfo
->uniqueRecord();
484 /* Given a status for basic construction of a cert group and a status
485 * of (optional) policy verification, plus the implicit notBefore/notAfter
486 * status in the certs, calculate a global return code. This just
487 * encapsulates a policy for CertGroupeConstruct and CertGroupVerify.
489 CSSM_RETURN
TPCertGroup::getReturnCode(
490 CSSM_RETURN constructStatus
,
491 CSSM_BOOL allowExpired
,
492 CSSM_BOOL allowExpiredRoot
,
493 CSSM_RETURN policyStatus
/* = CSSM_OK */)
495 if(constructStatus
) {
496 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
497 return constructStatus
;
500 /* check for expired, not valid yet */
501 bool expired
= false;
502 bool notValid
= false;
503 for(unsigned i
=0; i
<mNumCerts
; i
++) {
504 if(mCertInfo
[i
]->isExpired() &&
505 !(allowExpiredRoot
&& mCertInfo
[i
]->isSelfSigned())) {
508 if(mCertInfo
[i
]->isNotValidYet()) {
512 if(expired
&& !allowExpired
) {
513 return CSSMERR_TP_CERT_EXPIRED
;
516 return CSSMERR_TP_CERT_NOT_VALID_YET
;