]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/TPCertInfo.cpp
Security-179.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 "TPDatabase.h"
30 #include "TPNetwork.h"
31 #include <Security/cssmapi.h>
32 #include <Security/x509defs.h>
33 #include <Security/oidscert.h>
34 #include <Security/oidsalg.h>
35 #include <string.h> /* for memcmp */
36 #include <Security/threading.h> /* for Mutex */
37 #include <Security/globalizer.h>
38 #include <Security/debugging.h>
39 #include <Security/cssmapple.h>
40
41 #define tpTimeDbg(args...) secdebug("tpTime", ## args)
42 #define tpCertInfoDbg(args...) secdebug("tpCert", ## args)
43
44 static const TPClItemCalls tpCertClCalls =
45 {
46 CSSM_CL_CertGetFirstCachedFieldValue,
47 CSSM_CL_CertAbortQuery,
48 CSSM_CL_CertCache,
49 CSSM_CL_CertAbortCache,
50 CSSM_CL_CertVerify,
51 &CSSMOID_X509V1ValidityNotBefore,
52 &CSSMOID_X509V1ValidityNotAfter,
53 CSSMERR_TP_INVALID_CERT_POINTER,
54 CSSMERR_TP_CERT_EXPIRED,
55 CSSMERR_TP_CERT_NOT_VALID_YET
56 };
57
58 TPClItemInfo::TPClItemInfo(
59 CSSM_CL_HANDLE clHand,
60 CSSM_CSP_HANDLE cspHand,
61 const TPClItemCalls &clCalls,
62 const CSSM_DATA *itemData,
63 TPItemCopy copyItemData,
64 const char *verifyTime) // may be NULL
65 :
66 mClHand(clHand),
67 mCspHand(cspHand),
68 mClCalls(clCalls),
69 mWeOwnTheData(false),
70 mCacheHand(0),
71 mIssuerName(NULL),
72 mItemData(NULL),
73 mSigAlg(CSSM_ALGID_NONE),
74 mIsExpired(false),
75 mIsNotValidYet(false),
76 mIndex(0)
77 {
78 try {
79 cacheItem(itemData, copyItemData);
80 /*
81 * Fetch standard fields...
82 * Issue name assumes same OID for Certs and CRLs!
83 */
84 CSSM_RETURN crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
85 if(crtn) {
86 CssmError::throwMe(crtn);
87 }
88
89 /*
90 * Signing algorithm, infer from TBS algId
91 * Note this assumesÊthat the OID for fetching this field is the
92 * same for CRLs and Certs.
93 */
94 CSSM_DATA_PTR algField;
95 crtn = fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS, &algField);
96 if(crtn) {
97 releaseResources();
98 CssmError::throwMe(crtn);
99 }
100 if(algField->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) {
101 tpErrorLog("TPClItemInfo: bad CSSM_X509_ALGORITHM_IDENTIFIER\n");
102 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
103 }
104 CSSM_X509_ALGORITHM_IDENTIFIER *algId =
105 (CSSM_X509_ALGORITHM_IDENTIFIER *)algField->Data;
106 bool algFound = cssmOidToAlg(&algId->algorithm, &mSigAlg);
107 if(!algFound) {
108 tpErrorLog("TPClItemInfo: unknown signature algorithm\n");
109 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
110 }
111 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS, algField);
112
113 fetchNotBeforeAfter();
114 calculateCurrent(verifyTime);
115 }
116 catch(...) {
117 releaseResources();
118 throw;
119 }
120 }
121
122 TPClItemInfo::~TPClItemInfo()
123 {
124 tpCertInfoDbg("TPClItemInfo destruct this %p", this);
125 releaseResources();
126 }
127
128 void TPClItemInfo::releaseResources()
129 {
130 if(mWeOwnTheData && (mItemData != NULL)) {
131 tpFreeCssmData(CssmAllocator::standard(), mItemData, CSSM_TRUE);
132 mWeOwnTheData = false;
133 mItemData = NULL;
134 }
135 if(mIssuerName) {
136 freeField(&CSSMOID_X509V1IssuerName, mIssuerName);
137 mIssuerName = NULL;
138 }
139 if(mCacheHand != 0) {
140 mClCalls.abortCache(mClHand, mCacheHand);
141 mCacheHand = 0;
142 }
143 }
144
145 /* fetch arbitrary field from cached cert */
146 CSSM_RETURN TPClItemInfo::fetchField(
147 const CSSM_OID *fieldOid,
148 CSSM_DATA_PTR *fieldData) // mallocd by CL and RETURNED
149 {
150 CSSM_RETURN crtn;
151
152 uint32 NumberOfFields = 0;
153 CSSM_HANDLE resultHand = 0;
154 *fieldData = NULL;
155
156 assert(mClCalls.getField != NULL);
157 assert(mCacheHand != 0);
158 crtn = mClCalls.getField(
159 mClHand,
160 mCacheHand,
161 fieldOid,
162 &resultHand,
163 &NumberOfFields,
164 fieldData);
165 if(crtn) {
166 return crtn;
167 }
168 if(NumberOfFields != 1) {
169 tpErrorLog("TPCertInfo::fetchField: numFields %d, expected 1\n",
170 (int)NumberOfFields);
171 }
172 mClCalls.abortQuery(mClHand, resultHand);
173 return CSSM_OK;
174 }
175
176 /* free arbitrary field obtained from fetchField() */
177 CSSM_RETURN TPClItemInfo::freeField(
178 const CSSM_OID *fieldOid,
179 CSSM_DATA_PTR fieldData)
180 {
181 return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
182
183 }
184
185 /*
186 * Verify with an issuer cert - works on certs and CRLs.
187 * Issuer/subject name match already performed by caller.
188 * Optional paramCert is used to provide parameters when issuer
189 * has a partial public key.
190 */
191 CSSM_RETURN TPClItemInfo::verifyWithIssuer(
192 TPCertInfo *issuerCert,
193 TPCertInfo *paramCert /* = NULL */) const
194 {
195 CSSM_RETURN crtn;
196
197 assert(mClHand != 0);
198 assert(issuerCert->isIssuerOf(*this));
199 assert(mCspHand != 0);
200
201 /*
202 * Special case: detect partial public key right now; don't even
203 * bother trying the cert verify in that case.
204 */
205 if(issuerCert->hasPartialKey() && (paramCert == NULL)) {
206 /* caller deals with this later */
207 tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE");
208 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
209 }
210
211 CSSM_CC_HANDLE ccHand;
212 crtn = CSSM_CSP_CreateSignatureContext(mCspHand,
213 mSigAlg,
214 NULL, // Access Creds
215 issuerCert->pubKey(),
216 &ccHand);
217 if(crtn != CSSM_OK) {
218 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
219 CssmError::throwMe(crtn);
220 }
221 if(paramCert != NULL) {
222 assert(issuerCert->hasPartialKey());
223
224 /* add in parameter-bearing key */
225 CSSM_CONTEXT_ATTRIBUTE newAttr;
226
227 newAttr.AttributeType = CSSM_ATTRIBUTE_PARAM_KEY;
228 newAttr.AttributeLength = sizeof(CSSM_KEY);
229 newAttr.Attribute.Key = paramCert->pubKey();
230 crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
231 if(crtn) {
232 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n");
233 CssmError::throwMe(crtn);
234 }
235 }
236 crtn = mClCalls.itemVerify(mClHand,
237 ccHand,
238 mItemData,
239 NULL, // issuer cert
240 NULL, // VerifyScope
241 0); // ScopeSize
242
243 switch(crtn) {
244 case CSSM_OK: // success
245 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles
246 tpVfyDebug("verifyWithIssuer GOOD");
247 break;
248 default:
249 /* all others appear here as general cert verify error */
250 crtn = CSSMERR_TP_VERIFICATION_FAILURE;
251 tpVfyDebug("verifyWithIssuer BAD");
252 break;
253 }
254 CSSM_DeleteContext(ccHand);
255 return crtn;
256 }
257
258 CSSM_RETURN TPClItemInfo::cacheItem(
259 const CSSM_DATA *itemData,
260 TPItemCopy copyItemData)
261 {
262 switch(copyItemData) {
263 case TIC_NoCopy:
264 mItemData = const_cast<CSSM_DATA *>(itemData);
265 break;
266 case TIC_CopyData:
267 mItemData = tpMallocCopyCssmData(CssmAllocator::standard(), itemData);
268 mWeOwnTheData = true;
269 break;
270 default:
271 assert(0);
272 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
273 }
274
275 /* cache the cert/CRL in the CL */
276 return mClCalls.cacheItem(mClHand, mItemData, &mCacheHand);
277 }
278
279 /*
280 * Calculate not before/after times as struct tm. Only throws on
281 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
282 *
283 * Only differences between Cert and CRL flavors of this are the
284 * OIDs used to fetch the appropriate before/after times, both of
285 * which are expressed as CSSM_X509_TIME structs for both Certs
286 * and CRLS.
287 */
288 void TPClItemInfo::fetchNotBeforeAfter()
289 {
290 CSSM_DATA_PTR notBeforeField = NULL;
291 CSSM_DATA_PTR notAfterField = NULL;
292 CSSM_RETURN crtn = CSSM_OK;
293 CSSM_X509_TIME *xTime;
294
295 assert(cacheHand() != CSSM_INVALID_HANDLE);
296 crtn = fetchField(mClCalls.notBeforeOid, &notBeforeField);
297 if(crtn) {
298 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
299 CssmError::throwMe(mClCalls.invalidItemRtn);
300 }
301
302 /* subsequent errors to errOut */
303 xTime = (CSSM_X509_TIME *)notBeforeField->Data;
304 if(timeStringToTm((char *)xTime->time.Data, xTime->time.Length, &mNotBefore)) {
305 tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n");
306 crtn = mClCalls.invalidItemRtn;
307 goto errOut;
308 }
309
310 crtn = fetchField(mClCalls.notAfterOid, &notAfterField);
311 if(crtn) {
312 /*
313 * Tolerate a missing NextUpdate in CRL only
314 */
315 if(mClCalls.notAfterOid == &CSSMOID_X509V1ValidityNotAfter) {
316 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
317 crtn = mClCalls.invalidItemRtn;
318 goto errOut;
319 }
320 else {
321 /*
322 * Fake NextUpdate to be "at the end of time"
323 */
324 timeStringToTm(CSSM_APPLE_CRL_END_OF_TIME,
325 strlen(CSSM_APPLE_CRL_END_OF_TIME),
326 &mNotAfter);
327 }
328 }
329 else {
330 xTime = (CSSM_X509_TIME *)notAfterField->Data;
331 if(timeStringToTm((char *)xTime->time.Data, xTime->time.Length, &mNotAfter)) {
332 tpErrorLog("fetchNotBeforeAfter: malformed notAfter time\n");
333 crtn = mClCalls.invalidItemRtn;
334 goto errOut;
335 }
336 }
337 crtn = CSSM_OK;
338 errOut:
339 if(notAfterField) {
340 freeField(mClCalls.notAfterOid, notAfterField);
341 }
342 if(notBeforeField) {
343 freeField(mClCalls.notBeforeOid, notBeforeField);
344 }
345 if(crtn != CSSM_OK) {
346 CssmError::throwMe(crtn);
347 }
348 }
349
350 /*
351 * Verify validity (not before/after) by comparing the reference
352 * time (verifyString if present, or "now" if NULL) to the
353 * not before/after fields fetched from the item at construction.
354 *
355 * Called implicitly at construction; can be called again any time
356 * to re-establish validity (e.g. after fetching an item from a cache).
357 *
358 * We use some stdlib time calls over in tpTime.c; the stdlib function
359 * gmtime() is not thread-safe, so we do the protection here. Note that
360 * this makes *our* calls to gmtime() thread-safe, but if the app has
361 * other threads which are also calling gmtime, we're out of luck.
362 */
363 ModuleNexus<Mutex> tpTimeLock;
364
365 CSSM_RETURN TPClItemInfo::calculateCurrent(
366 const char *verifyString)
367 {
368 struct tm refTime;
369
370 if(verifyString != NULL) {
371 /* caller specifies verification time base */
372 if(timeStringToTm(verifyString, strlen(verifyString), &refTime)) {
373 tpErrorLog("calculateCurrent: timeStringToTm error\n");
374 return CSSMERR_TP_INVALID_TIMESTRING;
375 }
376 }
377 else {
378 /* time base = right now */
379 StLock<Mutex> _(tpTimeLock());
380 nowTime(&refTime);
381 }
382 if(compareTimes(&refTime, &mNotBefore) < 0) {
383 mIsNotValidYet = true;
384 tpTimeDbg("\nTP_CERT_NOT_VALID_YET:\n now y:%d m:%d d:%d h:%d m:%d",
385 refTime.tm_year, refTime.tm_mon, refTime.tm_mday,
386 refTime.tm_hour, refTime.tm_min);
387 tpTimeDbg(" notBefore y:%d m:%d d:%d h:%d m:%d",
388 mNotBefore.tm_year, mNotBefore.tm_mon, mNotBefore.tm_mday,
389 mNotBefore.tm_hour, mNotBefore.tm_min);
390 return mClCalls.notValidYetRtn;
391 }
392 else {
393 mIsNotValidYet = false;
394 }
395
396 if(compareTimes(&refTime, &mNotAfter) > 0) {
397 mIsExpired = true;
398 tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
399 "h:%d m:%d",
400 refTime.tm_year, refTime.tm_mon, refTime.tm_mday,
401 refTime.tm_hour, refTime.tm_min);
402 tpTimeDbg(" notAfter y:%d m:%d d:%d h:%d m:%d",
403 mNotAfter.tm_year, mNotAfter.tm_mon, mNotAfter.tm_mday,
404 mNotAfter.tm_hour, mNotAfter.tm_min);
405 return mClCalls.expiredRtn;
406 }
407 else {
408 mIsExpired = false;
409 return CSSM_OK;
410 }
411 }
412
413
414 /*
415 * No default constructor - this is the only way.
416 * This caches the cert and fetches subjectName, issuerName, and
417 * mPublicKey to ensure the incoming certData is well-constructed.
418 */
419 TPCertInfo::TPCertInfo(
420 CSSM_CL_HANDLE clHand,
421 CSSM_CSP_HANDLE cspHand,
422 const CSSM_DATA *certData,
423 TPItemCopy copyCertData, // true: we copy, we free
424 // false - caller owns
425 const char *verifyTime) // may be NULL
426 :
427 TPClItemInfo(clHand, cspHand, tpCertClCalls, certData,
428 copyCertData, verifyTime),
429 mSubjectName(NULL),
430 mPublicKey(NULL),
431 mIsAnchor(false),
432 mIsFromDb(false),
433 mIsFromNet(false),
434 mNumStatusCodes(0),
435 mStatusCodes(NULL),
436 mUniqueRecord(NULL),
437 mUsed(false),
438 mIsLeaf(false),
439 mIsRoot(TRS_Unknown)
440 {
441 CSSM_RETURN crtn;
442
443 tpCertInfoDbg("TPCertInfo construct this %p", this);
444 mDlDbHandle.DLHandle = 0;
445 mDlDbHandle.DBHandle = 0;
446
447 /* fetch subject name */
448 crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
449 if(crtn) {
450 /* bad cert */
451 releaseResources();
452 CssmError::throwMe(crtn);
453 }
454
455 /* this cert's public key */
456 crtn = CSSM_CL_CertGetKeyInfo(clHand, certData, &mPublicKey);
457 if(crtn) {
458 /* bad cert */
459 releaseResources();
460 CssmError::throwMe(crtn);
461 }
462
463 /* calculate other commonly used fields */
464 if(tpCompareCssmData(mSubjectName, issuerName())) {
465 /*
466 * Per Radar 3374978, perform complete signature verification
467 * lazily - just check subject/issuer match here.
468 */
469 tpAnchorDebug("TPCertInfo potential anchor");
470 mIsRoot = TRS_NamesMatch;
471 }
472 else {
473 mIsRoot = TRS_NotRoot;
474 }
475 }
476
477 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
478 TPCertInfo::~TPCertInfo()
479 {
480 tpCertInfoDbg("TPCertInfo destruct this %p", this);
481 releaseResources();
482 }
483
484 void TPCertInfo::releaseResources()
485 {
486 if(mSubjectName) {
487 freeField(&CSSMOID_X509V1SubjectName, mSubjectName);
488 mSubjectName = NULL;
489 }
490 if(mStatusCodes) {
491 free(mStatusCodes);
492 mStatusCodes = NULL;
493 }
494 if(mPublicKey) {
495 /* allocated by CL */
496 tpFreePluginMemory(clHand(), mPublicKey->KeyData.Data);
497 tpFreePluginMemory(clHand(), mPublicKey);
498 mPublicKey = NULL;
499 }
500 TPClItemInfo::releaseResources();
501 }
502
503 const CSSM_DATA *TPCertInfo::subjectName()
504 {
505 assert(mSubjectName != NULL);
506 return mSubjectName;
507 }
508
509 /*
510 * Perform semi-lazy evaluation of "rootness". Subject and issuer names
511 * compared at constructor.
512 */
513 bool TPCertInfo::isSelfSigned()
514 {
515 switch(mIsRoot) {
516 case TRS_NotRoot: // known not to be root
517 return false;
518 case TRS_IsRoot:
519 return true;
520 case TRS_Unknown: // actually shouldn't happen, but to be safe...
521 case TRS_NamesMatch:
522 default:
523 /* do the signature verify */
524 if(verifyWithIssuer(this) == CSSM_OK) {
525 tpAnchorDebug("isSelfSigned anchor verified");
526 mIsRoot = TRS_IsRoot;
527 return true;
528 }
529 else {
530 tpAnchorDebug("isSelfSigned anchor vfy FAIL");
531 mIsRoot = TRS_NotRoot;
532 return false;
533 }
534 }
535 }
536
537 /*
538 * Am I the issuer of the specified subject item? Returns true if so.
539 * Works for subject certs as well as CRLs.
540 */
541 bool TPCertInfo::isIssuerOf(
542 const TPClItemInfo &subject)
543 {
544 assert(mSubjectName != NULL);
545 assert(subject.issuerName() != NULL);
546 if(tpCompareCssmData(mSubjectName, subject.issuerName())) {
547 return true;
548 }
549 else {
550 return false;
551 }
552 }
553
554 void TPCertInfo::addStatusCode(CSSM_RETURN code)
555 {
556 mNumStatusCodes++;
557 mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes,
558 mNumStatusCodes * sizeof(CSSM_RETURN));
559 mStatusCodes[mNumStatusCodes - 1] = code;
560 }
561
562 /*
563 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
564 * key.
565 */
566 bool TPCertInfo::hasPartialKey()
567 {
568 if(mPublicKey->KeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) {
569 return true;
570 }
571 else {
572 return false;
573 }
574 }
575
576 /***
577 *** TPCertGroup class
578 ***/
579
580 /* build empty group */
581 TPCertGroup::TPCertGroup(
582 CssmAllocator &alloc,
583 TPGroupOwner whoOwns) :
584 mAlloc(alloc),
585 mCertInfo(NULL),
586 mNumCerts(0),
587 mSizeofCertInfo(0),
588 mWhoOwns(whoOwns)
589 {
590 tpCertInfoDbg("TPCertGroup simple construct this %p", this);
591 /* nothing for now */
592 }
593
594 /*
595 * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting
596 * TPCertInfos are more or less in the same order as the incoming
597 * certs, though incoming certs are discarded if they don't parse.
598 * No verification of any sort is performed.
599 */
600 TPCertGroup::TPCertGroup(
601 const CSSM_CERTGROUP &CertGroupFrag,
602 CSSM_CL_HANDLE clHand,
603 CSSM_CSP_HANDLE cspHand,
604 CssmAllocator &alloc,
605 const char *verifyTime, // may be NULL
606 bool firstCertMustBeValid,
607 TPGroupOwner whoOwns) :
608 mAlloc(alloc),
609 mCertInfo(NULL),
610 mNumCerts(0),
611 mSizeofCertInfo(0),
612 mWhoOwns(whoOwns)
613 {
614 tpCertInfoDbg("TPCertGroup hard construct this %p", this);
615
616 /* verify input args */
617 if(cspHand == CSSM_INVALID_HANDLE) {
618 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE);
619 }
620 if(clHand == CSSM_INVALID_HANDLE) {
621 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE);
622 }
623 if(firstCertMustBeValid) {
624 if( (CertGroupFrag.NumCerts == 0) ||
625 (CertGroupFrag.GroupList.CertList[0].Data == NULL) ||
626 (CertGroupFrag.GroupList.CertList[0].Length == 0)) {
627 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
628 }
629 }
630 if(CertGroupFrag.CertGroupType != CSSM_CERTGROUP_DATA) {
631 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
632 }
633 switch(CertGroupFrag.CertType) {
634 case CSSM_CERT_X_509v1:
635 case CSSM_CERT_X_509v2:
636 case CSSM_CERT_X_509v3:
637 break;
638 default:
639 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
640 }
641 switch(CertGroupFrag.CertEncoding) {
642 case CSSM_CERT_ENCODING_BER:
643 case CSSM_CERT_ENCODING_DER:
644 break;
645 default:
646 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
647 }
648
649 /*
650 * Add remaining input certs to mCertInfo.
651 */
652 TPCertInfo *certInfo = NULL;
653 for(unsigned certDex=0; certDex<CertGroupFrag.NumCerts; certDex++) {
654 try {
655 certInfo = new TPCertInfo(clHand,
656 cspHand,
657 &CertGroupFrag.GroupList.CertList[certDex],
658 TIC_NoCopy, // caller owns
659 verifyTime);
660 }
661 catch (...) {
662 if((certDex == 0) && firstCertMustBeValid) {
663 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
664 }
665 /* else just ignore this cert */
666 continue;
667 }
668 certInfo->index(certDex);
669 appendCert(certInfo);
670 }
671 }
672
673 /*
674 * Deletes contents of mCertInfo[] if appropriate.
675 */
676 TPCertGroup::~TPCertGroup()
677 {
678 if(mWhoOwns == TGO_Group) {
679 unsigned i;
680 for(i=0; i<mNumCerts; i++) {
681 delete mCertInfo[i];
682 }
683 }
684 mAlloc.free(mCertInfo);
685 }
686
687 /* add/remove/access TPTCertInfo's. */
688 /*
689 * NOTE: I am aware that most folks would just use an array<> here, but
690 * gdb is so lame that it doesn't even let one examine the contents
691 * of an array<> (or just about anything else in the STL). I prefer
692 * debuggability over saving a few lines of trivial code.
693 */
694 void TPCertGroup::appendCert(
695 TPCertInfo *certInfo) // appends to end of mCertInfo
696 {
697 if(mNumCerts == mSizeofCertInfo) {
698 if(mSizeofCertInfo == 0) {
699 /* appending to empty array */
700 mSizeofCertInfo = 1;
701 }
702 else {
703 mSizeofCertInfo *= 2;
704 }
705 mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo,
706 mSizeofCertInfo * sizeof(TPCertInfo *));
707 }
708 mCertInfo[mNumCerts++] = certInfo;
709 }
710
711 TPCertInfo *TPCertGroup::certAtIndex(
712 unsigned index)
713 {
714 if(index > (mNumCerts - 1)) {
715 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
716 }
717 return mCertInfo[index];
718 }
719
720 TPCertInfo *TPCertGroup::removeCertAtIndex(
721 unsigned index) // doesn't delete the cert, just
722 // removes it from out list
723 {
724 if(index > (mNumCerts - 1)) {
725 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
726 }
727 TPCertInfo *rtn = mCertInfo[index];
728
729 /* removed requested element and compact remaining array */
730 unsigned i;
731 for(i=index; i<(mNumCerts - 1); i++) {
732 mCertInfo[i] = mCertInfo[i+1];
733 }
734 mNumCerts--;
735 return rtn;
736 }
737
738 TPCertInfo *TPCertGroup::firstCert()
739 {
740 if(mNumCerts == 0) {
741 /* the caller really should not do this... */
742 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
743 }
744 else {
745 return mCertInfo[0];
746 }
747 }
748
749 TPCertInfo *TPCertGroup::lastCert()
750 {
751 if(mNumCerts == 0) {
752 return NULL;
753 }
754 else {
755 return mCertInfo[mNumCerts - 1];
756 }
757 }
758
759 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
760 CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup()
761 {
762 CSSM_CERTGROUP_PTR cgrp =
763 (CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP));
764 cgrp->NumCerts = mNumCerts;
765 cgrp->CertGroupType = CSSM_CERTGROUP_DATA;
766 cgrp->CertType = CSSM_CERT_X_509v3;
767 cgrp->CertEncoding = CSSM_CERT_ENCODING_DER;
768 if(mNumCerts == 0) {
769 /* legal */
770 cgrp->GroupList.CertList = NULL;
771 return cgrp;
772 }
773 cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts,
774 sizeof(CSSM_DATA));
775 for(unsigned i=0; i<mNumCerts; i++) {
776 tpCopyCssmData(mAlloc, mCertInfo[i]->itemData(),
777 &cgrp->GroupList.CertList[i]);
778 }
779 return cgrp;
780 }
781
782 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
783 CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo()
784 {
785 CSSM_TP_APPLE_EVIDENCE_INFO *infoArray;
786
787 infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)mAlloc.calloc(mNumCerts,
788 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO));
789 for(unsigned i=0; i<mNumCerts; i++) {
790 TPCertInfo *certInfo = mCertInfo[i];
791 CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[i];
792
793 /* first the booleans */
794 if(certInfo->isExpired()) {
795 evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED;
796 }
797 if(certInfo->isNotValidYet()) {
798 evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET;
799 }
800 if(certInfo->dlDbHandle().DLHandle == 0) {
801 if(certInfo->isAnchor()) {
802 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS;
803 }
804 else if(certInfo->isFromNet()) {
805 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_FROM_NET;
806 }
807 else {
808 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS;
809 }
810 }
811 if(certInfo->isSelfSigned()) {
812 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT;
813 }
814
815 unsigned numCodes = certInfo->numStatusCodes();
816 if(numCodes) {
817 evInfo->NumStatusCodes = numCodes;
818 evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes,
819 sizeof(CSSM_RETURN));
820 for(unsigned j=0; j<numCodes; j++) {
821 evInfo->StatusCodes[j] = (certInfo->statusCodes())[j];
822 }
823 }
824
825 evInfo->Index = certInfo->index();
826 evInfo->DlDbHandle = certInfo->dlDbHandle();
827 evInfo->UniqueRecord = certInfo->uniqueRecord();
828 }
829 return infoArray;
830 }
831
832 /* Given a status for basic construction of a cert group and a status
833 * of (optional) policy verification, plus the implicit notBefore/notAfter
834 * status in the certs, calculate a global return code. This just
835 * encapsulates a policy for CertGroupConstruct and CertGroupVerify.
836 */
837 CSSM_RETURN TPCertGroup::getReturnCode(
838 CSSM_RETURN constructStatus,
839 CSSM_BOOL allowExpired,
840 CSSM_BOOL allowExpiredRoot,
841 CSSM_RETURN policyStatus /* = CSSM_OK */)
842 {
843 if(constructStatus) {
844 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
845 return constructStatus;
846 }
847
848 /* check for expired, not valid yet */
849 bool expired = false;
850 bool notValid = false;
851 for(unsigned i=0; i<mNumCerts; i++) {
852 if(mCertInfo[i]->isExpired() &&
853 !(allowExpiredRoot && mCertInfo[i]->isSelfSigned())) {
854 expired = true;
855 }
856 if(mCertInfo[i]->isNotValidYet()) {
857 notValid = true;
858 }
859 }
860 if(expired && !allowExpired) {
861 return CSSMERR_TP_CERT_EXPIRED;
862 }
863 if(notValid) {
864 return CSSMERR_TP_CERT_NOT_VALID_YET;
865 }
866 return policyStatus;
867 }
868
869 /* set all TPCertINfo.mUsed flags false */
870 void TPCertGroup::setAllUnused()
871 {
872 for(unsigned dex=0; dex<mNumCerts; dex++) {
873 mCertInfo[dex]->used(false);
874 }
875 }
876
877 /*
878 * Search unused incoming certs to find an issuer of specified cert or CRL.
879 * WARNING this assumes a valid "used" state for all certs in this group.
880 * If partialIssuerKey is true on return, caller must re-verify signature
881 * of subject later when sufficient info is available.
882 */
883 TPCertInfo *TPCertGroup::findIssuerForCertOrCrl(
884 const TPClItemInfo &subject,
885 bool &partialIssuerKey)
886 {
887 partialIssuerKey = false;
888 for(unsigned certDex=0; certDex<mNumCerts; certDex++) {
889 TPCertInfo *certInfo = certAtIndex(certDex);
890
891 /* has this one already been used in this search? */
892 if(certInfo->used()) {
893 continue;
894 }
895
896 /* subject/issuer names match? */
897 if(certInfo->isIssuerOf(subject)) {
898 /* yep, do a sig verify */
899 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig");
900 CSSM_RETURN crtn = subject.verifyWithIssuer(certInfo);
901 switch(crtn) {
902 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
903 /* issuer OK, check sig later */
904 partialIssuerKey = true;
905 /* and fall thru */
906 case CSSM_OK:
907 /* YES */
908 certInfo->used(true);
909 return certInfo;
910 default:
911 /* just skip this one and keep looking */
912 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
913 break;
914 }
915 } /* names match */
916 }
917 /* not found */
918 return NULL;
919 }
920
921 /*
922 * Construct ordered, verified cert chain from a variety of inputs.
923 * Time validity is ignored and needs to be checked by caller (it's
924 * stored in each TPCertInfo we add to ourself during construction).
925 * The only error returned is CSSMERR_APPLETP_INVALID_ROOT, meaning
926 * we verified back to a supposed root cert which did not in fact
927 * self-verify. Other interesting status is returned via the
928 * verifiedToRoot and verifiedToAnchor flags.
929 *
930 * NOTE: is it the caller's responsibility to call setAllUnused() for both
931 * incoming cert groups (inCertGroup and gatheredCerts). We don't do that
932 * here because we may call ourself recursively.
933 */
934 CSSM_RETURN TPCertGroup::buildCertGroup(
935 const TPClItemInfo &subjectItem, // Cert or CRL
936 TPCertGroup *inCertGroup, // optional
937 const CSSM_DL_DB_LIST *dbList, // optional
938 CSSM_CL_HANDLE clHand,
939 CSSM_CSP_HANDLE cspHand,
940 const char *verifyTime, // optional, for establishing
941 // validity of new TPCertInfos
942 /* trusted anchors, optional */
943 /* FIXME - maybe this should be a TPCertGroup */
944 uint32 numAnchorCerts,
945 const CSSM_DATA *anchorCerts,
946
947 /*
948 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
949 * as a result of using a cert from anchorCerts or dbList) are added
950 * to this group.
951 */
952 TPCertGroup &certsToBeFreed,
953
954 /*
955 * Other certificates gathered during the course of this operation,
956 * currently consisting of certs fetched from DBs and from the net.
957 * This is not used when called by AppleTPSession::CertGroupConstructPriv;
958 * it's an optimization for the case when we're building a cert group
959 * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from
960 * the net which are needed to verify both the subject cert and a CRL.
961 */
962 TPCertGroup *gatheredCerts,
963
964 /*
965 * Indicates that subjectItem is the last element in this cert group.
966 * If true, that cert will be tested for "root-ness", including
967 * -- subject/issuer compare
968 * -- signature self-verify
969 * -- anchor compare
970 */
971 CSSM_BOOL subjectIsInGroup,
972
973 /* currently, only CSSM_TP_ACTION_FETCH_CERT_FROM_NET is interesting */
974 CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
975
976 /* returned */
977 CSSM_BOOL &verifiedToRoot, // end of chain self-verifies
978 CSSM_BOOL &verifiedToAnchor) // end of chain in anchors
979 {
980 const TPClItemInfo *thisSubject = &subjectItem;
981 CSSM_RETURN crtn = CSSM_OK;
982 TPCertInfo *issuerCert = NULL;
983 unsigned certDex;
984 TPCertInfo *anchorInfo = NULL;
985 bool foundPartialIssuer = false;
986
987 tpVfyDebug("buildCertGroup top");
988
989 /* possible expired root which we'll only use if we can't find
990 * a better one */
991 TPCertInfo *expiredRoot = NULL;
992
993 verifiedToRoot = CSSM_FALSE;
994 verifiedToAnchor = CSSM_FALSE;
995
996 /*** main loop to seach inCertGroup and dbList ***
997 *
998 * Exit loop on:
999 * -- find a root cert in the chain
1000 * -- memory error
1001 * -- or no more certs to add to chain.
1002 */
1003 for(;;) {
1004 /*
1005 * Top of loop: thisSubject is the item we're trying to verify.
1006 */
1007
1008 /* is thisSubject a root cert? */
1009 if(subjectIsInGroup) {
1010 TPCertInfo *subjCert = lastCert();
1011 assert(subjCert != NULL);
1012 if(subjCert->isSelfSigned()) {
1013 /* We're at the end of the chain. */
1014 verifiedToRoot = CSSM_TRUE;
1015
1016 /*
1017 * Special case if this root is expired (and it's not the
1018 * leaf): remove it from the outgoing cert group, save it,
1019 * and try to proceed with anchor cert processing.
1020 */
1021 if(subjCert->isExpired() && (mNumCerts > 1)) {
1022 tpDebug("buildCertGroup: EXPIRED ROOT, looking for good one");
1023 mNumCerts--;
1024 expiredRoot = subjCert;
1025 thisSubject = lastCert();
1026 }
1027 break;
1028 }
1029 }
1030
1031 /*
1032 * Search unused incoming certs to find an issuer.
1033 * Both cert groups are optional.
1034 * We'll add issuer to outCertGroup below.
1035 */
1036 if(inCertGroup != NULL) {
1037 bool partial = false;
1038 issuerCert = inCertGroup->findIssuerForCertOrCrl(*thisSubject,
1039 partial);
1040 if(issuerCert) {
1041 if(partial) {
1042 /* deal with this later */
1043 foundPartialIssuer = true;
1044 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
1045 }
1046 else {
1047 tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
1048 }
1049 }
1050 }
1051 if((issuerCert == NULL) && (gatheredCerts != NULL)) {
1052 bool partial = false;
1053 issuerCert = gatheredCerts->findIssuerForCertOrCrl(*thisSubject,
1054 partial);
1055 if(issuerCert) {
1056 if(partial) {
1057 /* deal with this later */
1058 foundPartialIssuer = true;
1059 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
1060 }
1061 else {
1062 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
1063 }
1064 }
1065 }
1066
1067 if((issuerCert == NULL) && (dbList != NULL)) {
1068 /* Issuer not in incoming cert group. Search DBList. */
1069 bool partial = false;
1070 issuerCert = tpDbFindIssuerCert(mAlloc,
1071 clHand,
1072 cspHand,
1073 thisSubject,
1074 dbList,
1075 verifyTime,
1076 partial);
1077 if(issuerCert) {
1078 /* caller must free */
1079 certsToBeFreed.appendCert(issuerCert);
1080 if(partial) {
1081 /* deal with this later */
1082 foundPartialIssuer = true;
1083 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
1084 }
1085 else {
1086 tpDebug("buildCertGroup: Cert FOUND in dbList");
1087 }
1088 }
1089 } /* Issuer not in incoming cert group */
1090
1091 if(issuerCert == NULL) {
1092 /* end of search, broken chain */
1093 break;
1094 }
1095
1096 /*
1097 * One way or the other, we've found a cert which verifies subjectCert.
1098 * Add the issuer to outCertGroup and make it the new thisSubject for
1099 * the next pass.
1100 */
1101 appendCert(issuerCert);
1102 thisSubject = issuerCert;
1103 subjectIsInGroup = CSSM_TRUE;
1104 issuerCert = NULL;
1105 } /* main loop */
1106
1107 /*
1108 * This can be NULL if we're evaluating a CRL (and we haven't
1109 * gotten very far).
1110 */
1111 TPCertInfo *endCert = lastCert();
1112
1113 if(numAnchorCerts == 0) {
1114 /* we're probably done */
1115 goto post_anchor;
1116 }
1117 assert(anchorCerts != NULL);
1118
1119 /*** anchor cert handling ***/
1120 /*
1121 * Case 1: last cert in output is a root cert. See if
1122 * the root cert is in AnchorCerts. This also applies to
1123 * the expiredRoot case; we report a different error for
1124 * "we trust the root but it's expired" versus "we don't
1125 * trust the root".
1126 * Note that the above loop did the actual root self-verify test.
1127 * FIXME - shouldn't we be searching for a match in AnchorCerts
1128 * whether or not endCert is a root!!?
1129 */
1130 if((endCert && endCert->isSelfSigned()) || expiredRoot) {
1131
1132 TPCertInfo *theRoot;
1133 if(expiredRoot) {
1134 /* this is NOT in our outgoing cert group (yet) */
1135 theRoot = expiredRoot;
1136 }
1137 else {
1138 theRoot = endCert;
1139 }
1140 /* see if that root cert is identical to one of the anchor certs */
1141 for(certDex=0; certDex<numAnchorCerts; certDex++) {
1142 if(tp_CompareCerts(theRoot->itemData(), &anchorCerts[certDex])) {
1143 /* one fully successful return */
1144 verifiedToAnchor = CSSM_TRUE;
1145 theRoot->isAnchor(true);
1146 theRoot->index(certDex);
1147 if(expiredRoot) {
1148 /* verified to anchor but caller will see
1149 * CSSMERR_TP_CERT_EXPIRED */
1150 appendCert(expiredRoot);
1151 }
1152 /* one more thing: partial public key processing needed? */
1153 if(foundPartialIssuer) {
1154 return verifyWithPartialKeys(subjectItem);
1155 }
1156 else {
1157 return CSSM_OK;
1158 }
1159 }
1160 }
1161
1162 if(!expiredRoot) {
1163 /* verified to a root cert which is not an anchor */
1164 /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */
1165 /* one more thing: partial public key processing needed? */
1166 if(foundPartialIssuer) {
1167 return verifyWithPartialKeys(subjectItem);
1168 }
1169 else {
1170 return CSSM_OK;
1171 }
1172 }
1173 /* else try finding a good anchor */
1174 }
1175
1176 /*
1177 * Case 2: try to validate thisSubject with anchor certs
1178 */
1179 for(certDex=0; certDex<numAnchorCerts; certDex++) {
1180
1181 try {
1182 anchorInfo = new TPCertInfo(clHand,
1183 cspHand,
1184 &anchorCerts[certDex],
1185 TIC_NoCopy,
1186 verifyTime);
1187 }
1188 catch(...) {
1189 /* bad anchor cert - ignore it */
1190 anchorInfo = NULL;
1191 continue;
1192 }
1193
1194 /*
1195 * We must subsequently delete goodAnchor one way or the other.
1196 * If we add it to tpCertGroup, we also add it to certsToBeFreed.
1197 * Otherwise we delete it.
1198 */
1199 if(!anchorInfo->isIssuerOf(*thisSubject)) {
1200 /* not this anchor */
1201 tpAnchorDebug("buildCertGroup anchor not issuer");
1202 delete anchorInfo;
1203 anchorInfo = NULL;
1204 continue;
1205 }
1206
1207 crtn = thisSubject->verifyWithIssuer(anchorInfo);
1208 switch(crtn) {
1209 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
1210 /*
1211 * A bit of a corner case. Found an issuer in AnchorCerts, but
1212 * we can't do a signature verify since the issuer has a partial
1213 * public key. Proceed but return
1214 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE.
1215 */
1216 crtn = CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
1217 anchorInfo->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE);
1218 foundPartialIssuer = true;
1219 /* drop thru */
1220 case CSSM_OK:
1221 /* The other normal fully successful return. */
1222 verifiedToAnchor = CSSM_TRUE;
1223 if(anchorInfo->isSelfSigned()) {
1224 verifiedToRoot = CSSM_TRUE;
1225 }
1226
1227 /*
1228 * Add this anchor cert to the output group
1229 * and to certsToBeFreed.
1230 */
1231 appendCert(anchorInfo);
1232 anchorInfo->isAnchor(true);
1233 anchorInfo->index(certDex);
1234 certsToBeFreed.appendCert(anchorInfo);
1235 tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
1236 /* one more thing: partial public key processing needed? */
1237 if(foundPartialIssuer) {
1238 return verifyWithPartialKeys(subjectItem);
1239 }
1240 else {
1241 return crtn;
1242 }
1243
1244 default:
1245 /* continue to next anchor */
1246 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG");
1247 delete anchorInfo;
1248 anchorInfo = NULL;
1249 break;
1250 }
1251 } /* for each anchor */
1252 /* regardless of anchor search status... */
1253 crtn = CSSM_OK;
1254 post_anchor:
1255 if(expiredRoot) {
1256 /*
1257 * One remaining special case: expiredRoot found in input certs, but
1258 * no luck resolving the problem with the anchors. Go ahead and append
1259 * the expired root and return.
1260 */
1261 tpDebug("buildCertGroup: accepting EXPIRED root");
1262 appendCert(expiredRoot);
1263 if(foundPartialIssuer) {
1264 return verifyWithPartialKeys(subjectItem);
1265 }
1266 else {
1267 return CSSM_OK;
1268 }
1269 }
1270
1271 /*
1272 * If we haven't verified to a root, and net fetch of certs is enabled,
1273 * try to get the issuer of the last cert in the chain from the net.
1274 * If that succeeds, then call ourself recursively to perform the
1275 * whole search again (including comparing to or verifying against
1276 * anchor certs).
1277 */
1278 if(!verifiedToRoot && !verifiedToAnchor &&
1279 (endCert != NULL) &&
1280 (actionFlags & CSSM_TP_ACTION_FETCH_CERT_FROM_NET)) {
1281 TPCertInfo *issuer = NULL;
1282 CSSM_RETURN cr = tpFetchIssuerFromNet(*endCert,
1283 clHand,
1284 cspHand,
1285 verifyTime,
1286 issuer);
1287 switch(cr) {
1288 case CSSMERR_TP_CERTGROUP_INCOMPLETE:
1289 /* no issuerAltName, no reason to log this */
1290 break;
1291 default:
1292 /* gross error */
1293 endCert->addStatusCode(cr);
1294 break;
1295 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
1296 /* use this one but re-verify later */
1297 foundPartialIssuer = true;
1298 /* and drop thru */
1299 case CSSM_OK:
1300 tpDebug("buildCertGroup: Cert FOUND from Net; recursing");
1301
1302 /* add this fetched cert to constructed group */
1303 appendCert(issuer);
1304 issuer->isFromNet(true);
1305 certsToBeFreed.appendCert(issuer);
1306
1307 /* and go again */
1308 cr = buildCertGroup(*issuer,
1309 inCertGroup,
1310 dbList,
1311 clHand,
1312 cspHand,
1313 verifyTime,
1314 numAnchorCerts,
1315 anchorCerts,
1316 certsToBeFreed,
1317 gatheredCerts,
1318 CSSM_TRUE, // subjectIsInGroup
1319 actionFlags,
1320 verifiedToRoot,
1321 verifiedToAnchor);
1322 if(cr) {
1323 return cr;
1324 }
1325
1326 /* one more thing: partial public key processing needed? */
1327 if(foundPartialIssuer) {
1328 return verifyWithPartialKeys(subjectItem);
1329 }
1330 else {
1331 return CSSM_OK;
1332 }
1333 }
1334 }
1335 /* regardless of outcome, check for partial keys to log per-cert status */
1336 CSSM_RETURN partRtn = CSSM_OK;
1337 if(foundPartialIssuer) {
1338 partRtn = verifyWithPartialKeys(subjectItem);
1339 }
1340 if(crtn) {
1341 return crtn;
1342 }
1343 else {
1344 return partRtn;
1345 }
1346 }
1347
1348 /*
1349 * Called from buildCertGroup as final processing of a constructed
1350 * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been
1351 * detected. Perform partial public key processing.
1352 *
1353 * We don't have to verify every element, just the ones whose
1354 * issuers have partial public keys.
1355 *
1356 * Returns:
1357 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert
1358 * with a partial public key which can't be completed.
1359 * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with
1360 * a (supposedly) completed partial key
1361 */
1362 CSSM_RETURN TPCertGroup::verifyWithPartialKeys(
1363 const TPClItemInfo &subjectItem) // Cert or CRL
1364 {
1365 TPCertInfo *lastFullKeyCert = NULL;
1366 tpDebug("verifyWithPartialKeys top");
1367
1368 /* start from the end - it's easier */
1369 for(int dex=mNumCerts-1; dex >= 0; dex--) {
1370 TPCertInfo *thisCert = mCertInfo[dex];
1371
1372 /*
1373 * If this is the start of the cert chain, and it's not being
1374 * used to verify subjectItem, then we're done.
1375 */
1376 if(dex == 0) {
1377 if((void *)thisCert == (void *)&subjectItem) {
1378 tpDebug("verifyWithPartialKeys: success at leaf cert");
1379 return CSSM_OK;
1380 }
1381 }
1382 if(!thisCert->hasPartialKey()) {
1383 /*
1384 * Good to know. Record this and move on.
1385 */
1386 lastFullKeyCert = thisCert;
1387 tpDebug("full key cert found at index %d", dex);
1388 continue;
1389 }
1390 if(lastFullKeyCert == NULL) {
1391 /*
1392 * No full keys between here and the end!
1393 */
1394 tpDebug("UNCOMPLETABLE cert at index %d", dex);
1395 thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE);
1396 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
1397 }
1398
1399 /* do the verify - of next cert in chain or of subjectItem */
1400 const TPClItemInfo *subject;
1401 if(dex == 0) {
1402 subject = &subjectItem;
1403 tpDebug("...verifying subject item with partial cert 0");
1404 }
1405 else {
1406 subject = mCertInfo[dex - 1];
1407 tpDebug("...verifying with partial cert %d", dex);
1408 }
1409 CSSM_RETURN crtn = subject->verifyWithIssuer(thisCert,
1410 lastFullKeyCert);
1411 if(crtn) {
1412 tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex);
1413 thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE);
1414 return CSSMERR_TP_INVALID_CERT_AUTHORITY;
1415 }
1416 }
1417
1418 /* we just verified subjectItem - right? */
1419 assert((void *)mCertInfo[0] != (void *)&subjectItem);
1420 tpDebug("verifyWithPartialKeys: success at subjectItem");
1421 return CSSM_OK;
1422 }