CertStatusCache.cpp

Go to the documentation of this file.
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     //std::map<std::string, CPKIFCertStatusCacheEntryPtr> m_cache;
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; //1 day
00224         m_maxCacheSize = 1000;
00225         m_purgeFrequencyInMilliseconds = 360000; //1 hour
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         // XXX *** WARNING: eating a potential exception because it
00322         // doesn't look reasonable to throw from here.
00323         // The potential exception is very unlikely, and there is less
00324         // downside to eating it than leaving code as it was before
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             //remove stuff that's out of date
00342             RemoveOutOfDateStuff();
00343 
00344             if(m_cache.size() > boost::numeric_cast<size_t,int>(m_maxCacheSize))
00345             {
00346                 //remove oldest n% of entries
00347                 RemoveNPercentOfStuff();
00348             }
00349 
00350             m_lastCleanOperation = GetTickCount();
00351         }
00352     }catch(boost::numeric::bad_numeric_cast &){
00353         // XXX *** WARNING: eating an exception here. it doesn't
00354         // look reasonable for this to throw, though, and the
00355         // checked cast is still better than before.
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         //try/catch not needed - extensions have already been handled by this point
00452         CPKIFBasicConstraintsPtr basicConstraints = targetCert->GetExtension<CPKIFBasicConstraints>();
00453         if(basicConstraints != (CPKIFBasicConstraints*)NULL)
00454         {
00455             if(!basicConstraints->isCA())
00456                 return;
00457         }
00458     }
00459 
00460     //this may result in replacement but that will refresh the entry time so it seems OK (and could update the status)
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 //IPKIFColleague functions
00575 void CPKIFCertStatusCache::Initialize()
00576 {
00577 }
00578 
00579 //IPKIFRevocationStatus functions
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     //initialize the outbound low-water revocation status
00632     status = NOT_CHECKED;
00633 
00634     //get certs from path
00635     CPKIFCertificateNodeList certNodeList;
00636     path.GetPath(certNodeList);
00637 
00638     //variables used in the following loop
00639     bool statusSet = false;                     //iterator to indicate if status has been set
00640     CPKIFCertificatePtr curCert, issuersCert;   //pointers to relevant certs
00641     CPKIFCertStatusPtr curCertStatus;           //pointer to cert status object for current cert
00642     RevocationStatus rStatus = NOT_CHECKED, pathStatus = NOT_CHECKED;                   //revocation status of current cert
00643 
00644     CPKIFPathSettingsPtr settings;
00645     path.GetPathSettings(settings);
00646 
00647     //get the trust root from the path
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     //iterate over all certs in the path and invoke the CheckStatusInternal function
00659     CPKIFCertificateNodeList::iterator pos;
00660     CPKIFCertificateNodeList::iterator end = certNodeList.end();
00661     for(pos = certNodeList.begin(); pos != end; ++pos)
00662     {
00663         //initialize loop variables for this iteration
00664         //issuersCert = curCert;
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         //see if there's an entry in the revocation status cache for this cert/issuer pair
00679         CPKIFCertStatusCacheEntryPtr cacheEntry = m_impl->GetCertStatus(curCert, issuersCert);
00680         if(cacheEntry)
00681         {
00682             //update the overall status for the path to the low water mark
00683             if(!statusSet)
00684             {
00685                 //avoid latching on NOT_CHECKED
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             //need to adjust path status down to NOT_CHECKED (but leave alone if already less than that)
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     //variables used in the following loop
00735     CPKIFCertificatePtr curCert, issuersCert;   //pointers to relevant certs
00736     CPKIFCertStatusPtr curCertStatus;           //pointer to cert status object for current cert
00737 
00738     //get the trust root from the path
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     //get certs from path
00750     CPKIFCertificateNodeList certNodeList;
00751     path.GetPath(certNodeList);
00752 
00753     //iterate over all certs in the path and invoke the CheckStatusInternal function
00754     CPKIFCertificateNodeList::iterator pos;
00755     CPKIFCertificateNodeList::iterator end = certNodeList.end();
00756     for(pos = certNodeList.begin(); pos != end; ++pos)
00757     {
00758         //initialize loop variables for this iteration
00759         //issuersCert = curCert;
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 }

Generated on Mon Nov 15 11:15:47 2010 for PublicKeyInfrastructureFramework(PKIF) by  doxygen 1.5.6