]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/TPCertInfo.cpp
Security-54.1.9.tar.gz
[apple/security.git] / AppleX509TP / TPCertInfo.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 * TPCertInfo.h - TP's private certificate info classes
21 *
22 * Written 10/23/2000 by Doug Mitchell.
23 */
24
25 #include "TPCertInfo.h"
26 #include "tpdebugging.h"
27 #include "tpTime.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>
38
39 #define tpTimeDbg(args...) debug("tpTime", ## args)
40
41 /*
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.
45 */
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
52 mClHand(clHand),
53 mCacheHand(CSSM_INVALID_HANDLE),
54 mSubjectName(NULL),
55 mIssuerName(NULL),
56 mIndex(0),
57 mIsAnchor(false),
58 mIsFromDb(false),
59 mNumStatusCodes(0),
60 mStatusCodes(NULL),
61 mUniqueRecord(NULL)
62 {
63 CSSM_RETURN crtn;
64
65 mDlDbHandle.DBHandle = 0;
66 mDlDbHandle.DLHandle = 0;
67
68 if(copyCertData) {
69 mCertData = tpMallocCopyCssmData(CssmAllocator::standard(), certData);
70 }
71 else {
72 mCertData = const_cast<CSSM_DATA *>(certData);
73 }
74 mWeOwnTheData = copyCertData;
75
76 /* cache the cert */
77 mClHand = clHand;
78 crtn = CSSM_CL_CertCache(clHand, mCertData, &mCacheHand);
79 if(crtn) {
80 /* bad cert */
81 CssmError::throwMe(crtn);
82 }
83
84 /* fetch subject name */
85 crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
86 if(crtn) {
87 /* bad cert */
88 releaseResources();
89 CssmError::throwMe(crtn);
90 }
91
92 /* fetch issuer name */
93 crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
94 if(crtn) {
95 /* bad cert */
96 releaseResources();
97 CssmError::throwMe(crtn);
98 }
99
100 /* calculate other commonly used fields */
101 mIsRoot = tpCompareCssmData(mSubjectName, mIssuerName) ? true : false;
102 calculateCurrent(cssmTimeStr);
103 }
104
105 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
106 TPCertInfo::~TPCertInfo()
107 {
108 releaseResources();
109 }
110
111 void TPCertInfo::releaseResources()
112 {
113 if(mWeOwnTheData && (mCertData != NULL)) {
114 tpFreeCssmData(CssmAllocator::standard(), mCertData, CSSM_TRUE);
115 }
116 if(mSubjectName) {
117 freeField(&CSSMOID_X509V1SubjectName, mSubjectName);
118 }
119 if(mIssuerName) {
120 freeField(&CSSMOID_X509V1IssuerName, mIssuerName);
121 }
122 if(mCacheHand != CSSM_INVALID_HANDLE) {
123 CSSM_CL_CertAbortCache(mClHand, mCacheHand);
124 }
125 if(mStatusCodes) {
126 free(mStatusCodes);
127 }
128 }
129
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
134 {
135 CSSM_RETURN crtn;
136
137 uint32 NumberOfFields = 0;
138 CSSM_HANDLE resultHand = 0;
139 *fieldData = NULL;
140
141 crtn = CSSM_CL_CertGetFirstCachedFieldValue(
142 mClHand,
143 mCacheHand,
144 fieldOid,
145 &resultHand,
146 &NumberOfFields,
147 fieldData);
148 if(crtn) {
149 return crtn;
150 }
151 if(NumberOfFields != 1) {
152 errorLog1("TPCertInfo::fetchField: numFields %d, expected 1\n",
153 (int)NumberOfFields);
154 }
155 CSSM_CL_CertAbortQuery(mClHand, resultHand);
156 return CSSM_OK;
157 }
158
159 /* free arbitrary field obtained from fetchField() */
160 CSSM_RETURN TPCertInfo::freeField(
161 const CSSM_OID *fieldOid,
162 CSSM_DATA_PTR fieldData)
163 {
164 return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
165
166 }
167
168 /* accessors */
169 CSSM_CL_HANDLE TPCertInfo::clHand()
170 {
171 return mClHand;
172 }
173
174 CSSM_HANDLE TPCertInfo::cacheHand()
175 {
176 return mCacheHand;
177 }
178
179 const CSSM_DATA *TPCertInfo::certData()
180 {
181 CASSERT(mCertData != NULL);
182 return mCertData;
183 }
184
185 const CSSM_DATA *TPCertInfo::subjectName()
186 {
187 CASSERT(mSubjectName != NULL);
188 return mSubjectName;
189 }
190
191 const CSSM_DATA *TPCertInfo::issuerName()
192 {
193 CASSERT(mIssuerName != NULL);
194 return mIssuerName;
195 }
196
197 /*
198 * Verify validity (not before/after). Only throws on gross error
199 * (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
200 *
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.
205 */
206 ModuleNexus<Mutex> tpTimeLock;
207
208 void TPCertInfo::calculateCurrent(
209 const char *cssmTimeStr /* = NULL */)
210 {
211 CSSM_DATA_PTR notBeforeField = NULL;
212 CSSM_DATA_PTR notAfterField = NULL;
213 CSSM_RETURN crtn = CSSM_OK;
214 CSSM_X509_TIME *xNotAfter;
215
216 CASSERT(mCacheHand != CSSM_INVALID_HANDLE);
217 crtn = fetchField(&CSSMOID_X509V1ValidityNotBefore, &notBeforeField);
218 if(crtn) {
219 errorLog0("TPCertInfo::calculateCurrent: GetField error");
220 CssmError::throwMe(crtn);
221 }
222
223 /* subsequent errors to errOut */
224 struct tm now;
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);
230 }
231 }
232 else {
233 /* time base = right now */
234 StLock<Mutex> _(tpTimeLock());
235 nowTime(&now);
236 }
237 struct tm notBefore;
238 CSSM_X509_TIME *xNotBefore = (CSSM_X509_TIME *)notBeforeField->Data;
239
240 if(timeStringToTm((char *)xNotBefore->time.Data, xNotBefore->time.Length,
241 &notBefore)) {
242 errorLog0("TPCertInfo::calculateCurrent: malformed notBefore time\n");
243 crtn = CSSMERR_TP_INVALID_CERT_POINTER;
244 goto errOut;
245 }
246 if(compareTimes(&now, &notBefore) < 0) {
247 mNotValidYet = true;
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,
250 now.tm_min);
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);
254 }
255 else {
256 mNotValidYet = false;
257 }
258
259 struct tm notAfter;
260 crtn = fetchField(&CSSMOID_X509V1ValidityNotAfter, &notAfterField);
261 if(crtn) {
262 errorLog0("TPCertInfo::calculateCurrent: GetField error");
263 goto errOut;
264 }
265
266 xNotAfter = (CSSM_X509_TIME *)notAfterField->Data;
267 if(timeStringToTm((char *)xNotAfter->time.Data, xNotAfter->time.Length,
268 &notAfter)) {
269 errorLog0("TPCertInfo::calculateCurrent: malformed notAfter time\n");
270 crtn = CSSMERR_TP_INVALID_CERT_POINTER;
271 goto errOut;
272 }
273 else if(compareTimes(&now, &notAfter) > 0) {
274 crtn = CSSMERR_TP_CERT_EXPIRED;
275 tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
276 "h:%d m:%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);
282 mExpired = true;
283 }
284 else {
285 mExpired = false;
286 }
287 crtn = CSSM_OK;
288 errOut:
289 if(notAfterField) {
290 freeField(&CSSMOID_X509V1ValidityNotAfter, notAfterField);
291 }
292 if(notBeforeField) {
293 freeField(&CSSMOID_X509V1ValidityNotBefore, notBeforeField);
294 }
295 if(crtn != CSSM_OK) {
296 CssmError::throwMe(crtn);
297 }
298 }
299
300 CSSM_RETURN TPCertInfo::isCurrent(
301 CSSM_BOOL allowExpired)
302 {
303 if(mNotValidYet) {
304 return CSSMERR_TP_CERT_NOT_VALID_YET;
305 }
306 if(allowExpired || !mExpired) {
307 return CSSM_OK;
308 }
309 else {
310 return CSSMERR_TP_CERT_EXPIRED;
311 }
312 }
313
314 void TPCertInfo::addStatusCode(CSSM_RETURN code)
315 {
316 mNumStatusCodes++;
317 mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes,
318 mNumStatusCodes * sizeof(CSSM_RETURN));
319 mStatusCodes[mNumStatusCodes - 1] = code;
320 }
321
322 /***
323 *** TPCertGroup class
324 ***/
325 TPCertGroup::TPCertGroup(
326 CssmAllocator &alloc,
327 unsigned numCerts) :
328 mAlloc(alloc),
329 mNumCerts(0)
330 {
331 mCertInfo = (TPCertInfo **)alloc.malloc(numCerts * sizeof(TPCertInfo *));
332 mSizeofCertInfo = numCerts;
333 }
334
335 /*
336 * Deletes all TPCertInfo's.
337 */
338 TPCertGroup::~TPCertGroup()
339 {
340 unsigned i;
341 for(i=0; i<mNumCerts; i++) {
342 delete mCertInfo[i];
343 }
344 mAlloc.free(mCertInfo);
345 }
346
347 /* add/remove/access TPTCertInfo's. */
348 void TPCertGroup::appendCert(
349 TPCertInfo *certInfo) // appends to end of mCertInfo
350 {
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 *));
356 }
357 mCertInfo[mNumCerts++] = certInfo;
358 }
359
360 TPCertInfo *TPCertGroup::certAtIndex(
361 unsigned index)
362 {
363 if(index > (mNumCerts - 1)) {
364 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
365 }
366 return mCertInfo[index];
367 }
368
369 TPCertInfo *TPCertGroup::removeCertAtIndex(
370 unsigned index) // doesn't delete the cert, just
371 // removes it from out list
372 {
373 if(index > (mNumCerts - 1)) {
374 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
375 }
376 TPCertInfo *rtn = mCertInfo[index];
377
378 /* removed requested element and compact remaining array */
379 unsigned i;
380 for(i=index; i<(mNumCerts - 1); i++) {
381 mCertInfo[i] = mCertInfo[i+1];
382 }
383 mNumCerts--;
384 return rtn;
385 }
386
387 unsigned TPCertGroup::numCerts()
388 {
389 return mNumCerts;
390 }
391
392 TPCertInfo *TPCertGroup::firstCert()
393 {
394 if(mNumCerts == 0) {
395 /* the caller really should not do this... */
396 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
397 }
398 else {
399 return mCertInfo[0];
400 }
401 }
402
403 TPCertInfo *TPCertGroup::lastCert()
404 {
405 if(mNumCerts == 0) {
406 /* the caller really should not do this... */
407 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
408 }
409 else {
410 return mCertInfo[mNumCerts - 1];
411 }
412 }
413
414 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
415 CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup()
416 {
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;
423 if(mNumCerts == 0) {
424 /* legal */
425 cgrp->GroupList.CertList = NULL;
426 return cgrp;
427 }
428 cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts,
429 sizeof(CSSM_DATA));
430 for(unsigned i=0; i<mNumCerts; i++) {
431 tpCopyCssmData(mAlloc, mCertInfo[i]->certData(),
432 &cgrp->GroupList.CertList[i]);
433 }
434 return cgrp;
435 }
436
437 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
438 CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo()
439 {
440 CSSM_TP_APPLE_EVIDENCE_INFO *infoArray;
441
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];
447
448 /* first the booleans */
449 if(certInfo->isExpired()) {
450 evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED;
451 }
452 if(certInfo->isNotValidYet()) {
453 evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET;
454 }
455 if(certInfo->dlDbHandle().DLHandle == 0) {
456 if(certInfo->isAnchor()) {
457 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS;
458 }
459 else {
460 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS;
461 }
462 }
463 if(certInfo->isSelfSigned()) {
464 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT;
465 }
466
467 unsigned numCodes = certInfo->numStatusCodes();
468 if(numCodes) {
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];
474 }
475 }
476
477 evInfo->Index = certInfo->index();
478 evInfo->DlDbHandle = certInfo->dlDbHandle();
479 evInfo->UniqueRecord = certInfo->uniqueRecord();
480 }
481 return infoArray;
482 }
483
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.
488 */
489 CSSM_RETURN TPCertGroup::getReturnCode(
490 CSSM_RETURN constructStatus,
491 CSSM_BOOL allowExpired,
492 CSSM_BOOL allowExpiredRoot,
493 CSSM_RETURN policyStatus /* = CSSM_OK */)
494 {
495 if(constructStatus) {
496 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
497 return constructStatus;
498 }
499
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())) {
506 expired = true;
507 }
508 if(mCertInfo[i]->isNotValidYet()) {
509 notValid = true;
510 }
511 }
512 if(expired && !allowExpired) {
513 return CSSMERR_TP_CERT_EXPIRED;
514 }
515 if(notValid) {
516 return CSSMERR_TP_CERT_NOT_VALID_YET;
517 }
518 return policyStatus;
519 }