00001
00010 #include "pkif.h"
00011 #include "CertStatusCache.h"
00012 #include "PKIFCryptUtils.h"
00013 #include "RevocationSource.h"
00014 #include "PKIFErrors.h"
00015
00016 #include <vector>
00017 #include <map>
00018
00019 #include <boost/shared_ptr.hpp>
00020 #include <boost/numeric/conversion/cast.hpp>
00021 using namespace boost;
00022 using namespace std;
00023
00025
00030 class CPKIFCertStatusCacheEntry
00031 {
00032 public:
00040 CPKIFCertStatusCacheEntry()
00041 {
00042 }
00043
00051 bool operator==(const CPKIFCertStatusCacheEntry& rhs) const
00052 {
00053 if(!m_target || !rhs.m_target)
00054 return false;
00055 else
00056 return *m_target == *rhs.m_target;
00057 }
00058
00059 unsigned long m_ticksAtEntry;
00060 CPKIFTimePtr m_timeAtEntry;
00061
00062 CPKIFCertificatePtr m_target;
00063 CPKIFCertificatePtr m_issuer;
00064 RevocationStatus m_revStatus;
00065 CPKIFCertStatusPtr m_certStatus;
00066 };
00067 DECLARE_SMART_POINTERS(CPKIFCertStatusCacheEntry);
00068 typedef std::vector<CPKIFCertStatusCacheEntryPtr, PKIFAlloc<CPKIFCertStatusCacheEntryPtr> > CPKIFCertStatusCacheEntryList;
00069 DECLARE_SMART_POINTERS(CPKIFCertStatusCacheEntryList);
00070
00076 class CacheMatch
00077 {
00078 public:
00086 bool operator()(const CPKIFCertStatusCacheEntryPtr& t)
00087 {
00088 if(!(*t->m_target == *m_target))
00089 return false;
00090
00091 return *t->m_issuer == *m_issuer;
00092 }
00100 void SetRHS(const CPKIFCertificatePtr& target, const CPKIFCertificatePtr& issuer)
00101 {
00102 m_target = target;
00103 m_issuer = issuer;
00104 }
00105 private:
00106 CPKIFCertificatePtr m_target;
00107 CPKIFCertificatePtr m_issuer;
00108 };
00114 class IsOld
00115 {
00116 public:
00124 IsOld(unsigned long l)
00125 {
00126 m_maxAge = l;
00127 }
00135 bool operator()(const CPKIFCertStatusCacheEntryPtr& t)
00136 {
00137 unsigned long curTicks = GetTickCount();
00138 if(t->m_ticksAtEntry < (curTicks - m_maxAge))
00139 return true;
00140
00141 if(!t->m_certStatus)
00142 return false;
00143
00144 RevocationSourceList rsl;
00145 t->m_certStatus->GetRevocationSources(rsl);
00146
00147 CPKIFTimePtr curTime = CPKIFTime::CurrentTime();
00148 RevocationSourceList::iterator rslPos;
00149 RevocationSourceList::iterator rslEnd = rsl.end();
00150 int count = 0;
00151 for(rslPos = rsl.begin(), count = 1; rslPos != rslEnd; ++rslPos, ++count)
00152 {
00153 IPKIFRevSourceInfoPtr rsip = (*rslPos)->m_sourceInfo;
00154 if(rsip)
00155 {
00156 switch((*rslPos)->m_sourceType)
00157 {
00158 case REVSOURCE_CRL:
00159 {
00160 CPKIFCRLInfoPtr crlInfo = RevInfoCast<CPKIFCRLInfo>(rsip);
00161 CPKIFCRLList crls;
00162 crlInfo->GetCRLs(crls);
00163
00164 CPKIFCRLList::iterator crlPos;
00165 CPKIFCRLList::iterator crlEnd = crls.end();
00166 int crlCount = 1;
00167 for(crlPos = crls.begin(); crlPos != crlEnd; ++crlPos, ++crlCount)
00168 {
00169 CPKIFTimePtr nextUpdate = (*crlPos)->NextUpdate();
00170 if(nextUpdate != (CPKIFTime*)NULL && *nextUpdate < *curTime)
00171 {
00172 return true;
00173 }
00174 }
00175 }
00176 break;
00177 case REVSOURCE_OCSP:
00178 {
00179 CPKIFOCSPInfoPtr ocspInfo = RevInfoCast<CPKIFOCSPInfo>(rsip);
00180
00181 if(ocspInfo != (CPKIFOCSPInfo*)NULL)
00182 {
00183 CPKIFSingleResponsePtr singleResponse = ocspInfo->GetSingleResponse();
00184 if(singleResponse != (CPKIFSingleResponse*)NULL)
00185 {
00186 CPKIFTimePtr nextUpdate = singleResponse->GetNextUpdate();
00187 if(nextUpdate != (CPKIFTime*)NULL && *nextUpdate < *curTime)
00188 {
00189 return true;
00190 }
00191 }
00192 }
00193 }
00194 break;
00195 }
00196 }
00197 }
00198 return false;
00199 }
00200 private:
00201 long m_maxAge;
00202 IsOld();
00203 };
00204
00205 struct CertStatusCacheImpl
00206 {
00207 private:
00208
00209 std::vector<CPKIFCertStatusCacheEntryPtr> m_cache;
00210
00211 public:
00212 unsigned long m_lastCleanOperation;
00213 double m_percentToRemove;
00214 unsigned long m_maxCacheAgeInMilliseconds;
00215 int m_maxCacheSize;
00216 bool m_bCacheCaCertsOnly;
00217 long m_purgeFrequencyInMilliseconds;
00218
00219 CertStatusCacheImpl()
00220 {
00221 m_lastCleanOperation = GetTickCount();
00222 m_percentToRemove = .1;
00223 m_maxCacheAgeInMilliseconds = 8640000;
00224 m_maxCacheSize = 1000;
00225 m_purgeFrequencyInMilliseconds = 360000;
00226 m_bCacheCaCertsOnly = false;
00227 }
00228
00229 void CleanCacheIfNecessary();
00230 void RemoveOutOfDateStuff();
00231 void RemoveNPercentOfStuff();
00232
00233 CPKIFCertStatusCacheEntryPtr GetCertStatus(const CPKIFCertificatePtr& target, const CPKIFCertificatePtr& issuer);
00234 void PutCertStatus(CPKIFCertStatusCacheEntryPtr& csc);
00235 bool CacheIsEmpty()
00236 {
00237 return m_cache.empty();
00238 }
00239 };
00247 CPKIFCertStatusCacheEntryPtr CertStatusCacheImpl::GetCertStatus(
00249 const CPKIFCertificatePtr& target,
00251 const CPKIFCertificatePtr& issuer)
00252 {
00253 std::vector<CPKIFCertStatusCacheEntryPtr>::iterator foundObject;
00254
00255 CacheMatch cm;
00256 cm.SetRHS(target, issuer);
00257
00258 foundObject = find_if(m_cache.begin(), m_cache.end(), cm);
00259 if(foundObject != m_cache.end())
00260 {
00261 return *foundObject;
00262 }
00263 else
00264 {
00265 CPKIFCertStatusCacheEntryPtr empty;
00266 return empty;
00267 }
00268 }
00276 void CertStatusCacheImpl::PutCertStatus(
00278 CPKIFCertStatusCacheEntryPtr& csc)
00279 {
00280 if(!csc)
00281 return;
00282
00283 CacheMatch cm;
00284 cm.SetRHS(csc->m_target, csc->m_issuer);
00285
00286 std::vector<CPKIFCertStatusCacheEntryPtr>::iterator newEnd;
00287 newEnd = remove_if(m_cache.begin(), m_cache.end(), cm);
00288 m_cache.erase(newEnd, m_cache.end());
00289 m_cache.push_back(csc);
00290 }
00298 void CertStatusCacheImpl::RemoveOutOfDateStuff()
00299 {
00300 IsOld io(m_maxCacheAgeInMilliseconds);
00301 std::vector<CPKIFCertStatusCacheEntryPtr>::iterator newEnd;
00302 newEnd = remove_if(m_cache.begin(), m_cache.end(), io);
00303 m_cache.erase(newEnd, m_cache.end());
00304 }
00312 void CertStatusCacheImpl::RemoveNPercentOfStuff()
00313 {
00314 try {
00315 int numToRemove = boost::numeric_cast<int,double>(m_cache.size() * m_percentToRemove);
00316 int numToKeep = boost::numeric_cast<int,size_t>(m_cache.size() - numToRemove);
00317 sort(m_cache.begin(), m_cache.end());
00318
00319 m_cache.erase(m_cache.begin()+ numToKeep, m_cache.end());
00320 } catch(boost::numeric::bad_numeric_cast &) {
00321
00322
00323
00324
00325 }
00326 }
00334 void CertStatusCacheImpl::CleanCacheIfNecessary()
00335 {
00336 unsigned long curTicks = GetTickCount();
00337 unsigned long diff = curTicks - m_lastCleanOperation;
00338 try {
00339 if(diff > m_purgeFrequencyInMilliseconds || m_cache.size() > boost::numeric_cast<size_t,int>(m_maxCacheSize))
00340 {
00341
00342 RemoveOutOfDateStuff();
00343
00344 if(m_cache.size() > boost::numeric_cast<size_t,int>(m_maxCacheSize))
00345 {
00346
00347 RemoveNPercentOfStuff();
00348 }
00349
00350 m_lastCleanOperation = GetTickCount();
00351 }
00352 }catch(boost::numeric::bad_numeric_cast &){
00353
00354
00355
00356 }
00357 }
00358
00360
00368 CPKIFCertStatusCache::CPKIFCertStatusCache() : m_impl(new CertStatusCacheImpl)
00369 {
00370 }
00371
00379 CPKIFCertStatusCache::~CPKIFCertStatusCache()
00380 {
00381 if(m_impl)
00382 delete m_impl;
00383 }
00391 CPKIFBufferPtr GetThumbprintForSubjectAndIssuerPair(
00393 const CPKIFCertificatePtr& target,
00395 const CPKIFCertificatePtr& issuer)
00396 {
00397 CPKIFBufferPtr empty;
00398 if(!target || !issuer)
00399 return empty;
00400
00401 IPKIFCryptoMisc* misc = GetPlatformCryptoMisc();
00402 if(NULL == misc)
00403 return empty;
00404
00405 unsigned char sha1Buffer[20];
00406 int length = 20;
00407 try
00408 {
00409 CPKIFBufferPtr encTarget = target->Encoded();
00410 CPKIFBufferPtr encIssuer = issuer->Encoded();
00411
00412 IPKIFHashContext* hash = misc->HashInit(PKIFCRYPTO::SHA1);
00413 misc->HashUpdate(hash, (unsigned char*)encTarget->GetBuffer(), encTarget->GetLength());
00414 misc->HashUpdate(hash, (unsigned char*)encIssuer->GetBuffer(), encIssuer->GetLength());
00415 misc->HashFinal(hash, sha1Buffer, &length);
00416 delete hash;
00417 }
00418 catch(...)
00419 {
00420 return empty;
00421 }
00422
00423 CPKIFBufferPtr newBuf(new CPKIFBuffer);
00424 unsigned char* asciiVal = newBuf->AllocateBuffer(41);
00425 btoa((char*)sha1Buffer, (char*)asciiVal, 20);
00426 return newBuf;
00427 }
00428
00436 void CPKIFCertStatusCache::PutObject(
00438 const CPKIFCertificatePtr& targetCert,
00440 const CPKIFCertificatePtr& issuersCert,
00442 RevocationStatus& status,
00444 CPKIFCertStatusPtr& certStatus)
00445 {
00446 if(!targetCert || !issuersCert)
00447 return;
00448
00449 if(m_impl->m_bCacheCaCertsOnly)
00450 {
00451
00452 CPKIFBasicConstraintsPtr basicConstraints = targetCert->GetExtension<CPKIFBasicConstraints>();
00453 if(basicConstraints != (CPKIFBasicConstraints*)NULL)
00454 {
00455 if(!basicConstraints->isCA())
00456 return;
00457 }
00458 }
00459
00460
00461 CPKIFCertStatusCacheEntryPtr cacheEntry(new CPKIFCertStatusCacheEntry);
00462 cacheEntry->m_target = targetCert;
00463 cacheEntry->m_issuer = issuersCert;
00464 cacheEntry->m_revStatus = status;
00465 cacheEntry->m_certStatus = certStatus;
00466 cacheEntry->m_ticksAtEntry = GetTickCount();
00467 cacheEntry->m_timeAtEntry = CPKIFTime::CurrentTime();
00468 m_impl->PutCertStatus(cacheEntry);
00469 }
00477 void CPKIFCertStatusCache::SetMaxCacheAge(
00479 unsigned long maxAgeInMilliseconds)
00480 {
00481 m_impl->m_maxCacheAgeInMilliseconds = maxAgeInMilliseconds;
00482 }
00490 long CPKIFCertStatusCache::GetMaxCacheAge() const
00491 {
00492 return m_impl->m_maxCacheAgeInMilliseconds;
00493 }
00501 void CPKIFCertStatusCache::SetMaxCacheSize(
00503 int count)
00504 {
00505 m_impl->m_maxCacheSize = count;
00506 }
00514 int CPKIFCertStatusCache::GetMaxCacheSize() const
00515 {
00516 return m_impl->m_maxCacheSize;
00517 }
00525 void CPKIFCertStatusCache::SetCacheCaCertsOnly(
00527 bool bCacheCaCertsOnly)
00528 {
00529 m_impl->m_bCacheCaCertsOnly = bCacheCaCertsOnly;
00530 }
00538 bool CPKIFCertStatusCache::GetCacheCaCertsOnly() const
00539 {
00540 return m_impl->m_bCacheCaCertsOnly;
00541 }
00549 void CPKIFCertStatusCache::SetPurgeFrequency(
00551 int minTimeBetweenPurgesInSeconds)
00552 {
00553 m_impl->m_purgeFrequencyInMilliseconds = minTimeBetweenPurgesInSeconds*1000;
00554 }
00562 int CPKIFCertStatusCache::GetPurgeFrequency() const
00563 {
00564 return m_impl->m_purgeFrequencyInMilliseconds/1000;
00565 }
00566
00567
00575 void CPKIFCertStatusCache::Initialize()
00576 {
00577 }
00578
00579
00587 bool CPKIFCertStatusCache::CheckStatus(
00589 const CPKIFCertificatePtr& targetCert,
00591 const CPKIFCertificatePtr& issuersCert,
00593 RevocationStatus& status,
00595 CPKIFCertStatusPtr& certStatus)
00596 {
00597 if(!targetCert || !issuersCert)
00598 return false;
00599
00600 m_impl->CleanCacheIfNecessary();
00601 CPKIFCertStatusCacheEntryPtr cacheEntry = m_impl->GetCertStatus(targetCert, issuersCert);
00602 if(cacheEntry)
00603 {
00604 status = cacheEntry->m_revStatus;
00605 certStatus = cacheEntry->m_certStatus;
00606
00607 return true;
00608 }
00609 else
00610 return false;
00611 }
00620 bool CPKIFCertStatusCache::CheckStatusPath(
00622 CPKIFCertificatePath& path,
00624 RevocationStatus& status)
00625 {
00626 if(m_impl->CacheIsEmpty())
00627 return false;
00628
00629 m_impl->CleanCacheIfNecessary();
00630
00631
00632 status = NOT_CHECKED;
00633
00634
00635 CPKIFCertificateNodeList certNodeList;
00636 path.GetPath(certNodeList);
00637
00638
00639 bool statusSet = false;
00640 CPKIFCertificatePtr curCert, issuersCert;
00641 CPKIFCertStatusPtr curCertStatus;
00642 RevocationStatus rStatus = NOT_CHECKED, pathStatus = NOT_CHECKED;
00643
00644 CPKIFPathSettingsPtr settings;
00645 path.GetPathSettings(settings);
00646
00647
00648 IPKIFTrustAnchorPtr ta;
00649 CPKIFCertificatePtr rootCert;
00650 path.GetTrustRoot(ta);
00651 CPKIFTrustRootPtr tr = dynamic_pointer_cast<CPKIFTrustRoot, IPKIFTrustAnchor>(ta);
00652 if(tr)
00653 tr->GetCert(issuersCert);
00654
00655 if(!issuersCert)
00656 return false;
00657
00658
00659 CPKIFCertificateNodeList::iterator pos;
00660 CPKIFCertificateNodeList::iterator end = certNodeList.end();
00661 for(pos = certNodeList.begin(); pos != end; ++pos)
00662 {
00663
00664
00665 curCert = (*pos)->GetCert();
00666 curCertStatus = (*pos)->GetStatus();
00667
00668 if(NOT_CHECKED != curCertStatus->GetRevocationStatus())
00669 {
00670 statusSet = true;
00671 pathStatus = min(pathStatus, curCertStatus->GetRevocationStatus());
00672
00673 issuersCert = curCert;
00674 continue;
00675 }
00676 rStatus = NOT_CHECKED;
00677
00678
00679 CPKIFCertStatusCacheEntryPtr cacheEntry = m_impl->GetCertStatus(curCert, issuersCert);
00680 if(cacheEntry)
00681 {
00682
00683 if(!statusSet)
00684 {
00685
00686 pathStatus = cacheEntry->m_revStatus;
00687 statusSet = true;
00688 }
00689 else
00690 pathStatus = min(pathStatus, cacheEntry->m_revStatus);
00691
00692 RevocationSourceList rsl;
00693 cacheEntry->m_certStatus->GetRevocationSources(rsl);
00694 RevocationSourceList::iterator rslBegin = rsl.begin();
00695 RevocationSourceList::iterator ii;
00696 for(ii = rsl.begin(); ii != rsl.end(); ++ii)
00697 {
00698 IPKIFRevSourceInfoPtr rsip = (*ii)->m_sourceInfo;
00699 curCertStatus->AddRevocationSource((*ii)->m_sourceType, rsip, (*ii)->m_status, (*ii)->m_errorCode);
00700 }
00701
00702 curCertStatus->SetRevocationStatus(cacheEntry->m_revStatus);
00703 if(REVOKED == cacheEntry->m_revStatus)
00704 curCertStatus->SetDiagnosticCode(PATH_CERT_REVOKED);
00705 else if (NOT_CHECKED == cacheEntry->m_revStatus)
00706 curCertStatus->SetDiagnosticCode(PATH_CERT_REVOCATION_STATUS_NOT_DETERMINED);
00707 else
00708 curCertStatus->SetDiagnosticCode(0);
00709 }
00710 else
00711 {
00712
00713 pathStatus = min(pathStatus, NOT_CHECKED);
00714 statusSet = true;
00715 }
00716
00717 issuersCert = curCert;
00718 }
00719
00720 status = pathStatus;
00721 return status != NOT_CHECKED;
00722 }
00730 void CPKIFCertStatusCache::PutObjectsInPath(
00732 CPKIFCertificatePath& path)
00733 {
00734
00735 CPKIFCertificatePtr curCert, issuersCert;
00736 CPKIFCertStatusPtr curCertStatus;
00737
00738
00739 IPKIFTrustAnchorPtr ta;
00740 CPKIFCertificatePtr rootCert;
00741 path.GetTrustRoot(ta);
00742 CPKIFTrustRootPtr tr = dynamic_pointer_cast<CPKIFTrustRoot, IPKIFTrustAnchor>(ta);
00743 if(tr)
00744 tr->GetCert(issuersCert);
00745
00746 if(!issuersCert)
00747 return;
00748
00749
00750 CPKIFCertificateNodeList certNodeList;
00751 path.GetPath(certNodeList);
00752
00753
00754 CPKIFCertificateNodeList::iterator pos;
00755 CPKIFCertificateNodeList::iterator end = certNodeList.end();
00756 for(pos = certNodeList.begin(); pos != end; ++pos)
00757 {
00758
00759
00760 curCert = (*pos)->GetCert();
00761 curCertStatus = (*pos)->GetStatus();
00762
00763 RevocationStatus rs = curCertStatus->GetRevocationStatus();
00764 PutObject(curCert, issuersCert, rs, curCertStatus);
00765
00766 issuersCert = curCert;
00767 }
00768 }