//+--------------------------------------------------------------------------- // // Microsoft Windows NT Security // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: tvo.cpp // // Contents: Implementation of CryptGetTimeValidObject // // History: 25-Sep-97 kirtd Created // //---------------------------------------------------------------------------- #include //+--------------------------------------------------------------------------- // // Function: CryptGetTimeValidObject // // Synopsis: get a time valid CAPI2 object // //---------------------------------------------------------------------------- BOOL WINAPI CryptGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult; HCRYPTOIDFUNCADDR hGetTimeValidObject; PFN_GET_TIME_VALID_OBJECT_FUNC pfnGetTimeValidObject; DWORD LastError; FILETIME CurrentTime; if ( CryptGetOIDFunctionAddress( hGetTimeValidObjectFuncSet, X509_ASN_ENCODING, pszTimeValidOid, 0, (LPVOID *)&pfnGetTimeValidObject, &hGetTimeValidObject ) == FALSE ) { return( FALSE ); } if ( pftValidFor == NULL ) { GetSystemTimeAsFileTime( &CurrentTime ); pftValidFor = &CurrentTime; } fResult = ( *pfnGetTimeValidObject )( pszTimeValidOid, pvPara, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ); LastError = GetLastError(); CryptFreeOIDFunctionAddress( hGetTimeValidObject, 0 ); SetLastError( LastError ); return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CtlGetTimeValidObject // // Synopsis: get a time valid CTL // //---------------------------------------------------------------------------- BOOL WINAPI CtlGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { return( g_pProcessTVOAgent->GetTimeValidObject( pszTimeValidOid, pvPara, CONTEXT_OID_CTL, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: CrlGetTimeValidObject // // Synopsis: get a time valid CRL // //---------------------------------------------------------------------------- BOOL WINAPI CrlGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { return( g_pProcessTVOAgent->GetTimeValidObject( pszTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: CrlFromCertGetTimeValidObject // // Synopsis: get a time valid CRL from a subject certificate // //---------------------------------------------------------------------------- BOOL WINAPI CrlFromCertGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { return( g_pProcessTVOAgent->GetTimeValidObject( pszTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: FreshestCrlFromCertGetTimeValidObject // // Synopsis: get a time valid freshest, delta CRL from a subject certificate // //---------------------------------------------------------------------------- BOOL WINAPI FreshestCrlFromCertGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { return( g_pProcessTVOAgent->GetTimeValidObject( pszTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: FreshestCrlFromCrlGetTimeValidObject // // Synopsis: get a time valid freshest, delta CRL from a base CRL // //---------------------------------------------------------------------------- BOOL WINAPI FreshestCrlFromCrlGetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { return( g_pProcessTVOAgent->GetTimeValidObject( pszTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: CryptFlushTimeValidObject // // Synopsis: flush the object from the "TVO" system // //---------------------------------------------------------------------------- BOOL WINAPI CryptFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { BOOL fResult; HCRYPTOIDFUNCADDR hFlushTimeValidObject; PFN_FLUSH_TIME_VALID_OBJECT_FUNC pfnFlushTimeValidObject; DWORD LastError; if ( CryptGetOIDFunctionAddress( hFlushTimeValidObjectFuncSet, X509_ASN_ENCODING, pszFlushTimeValidOid, 0, (LPVOID *)&pfnFlushTimeValidObject, &hFlushTimeValidObject ) == FALSE ) { return( FALSE ); } fResult = ( *pfnFlushTimeValidObject )( pszFlushTimeValidOid, pvPara, pIssuer, dwFlags, pvReserved ); LastError = GetLastError(); CryptFreeOIDFunctionAddress( hFlushTimeValidObject, 0 ); SetLastError( LastError ); return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CtlFlushTimeValidObject // // Synopsis: flush a CTL from the "TVO" system // //---------------------------------------------------------------------------- BOOL WINAPI CtlFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { return( g_pProcessTVOAgent->FlushTimeValidObject( pszFlushTimeValidOid, pvPara, CONTEXT_OID_CTL, pIssuer, dwFlags, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: CrlFlushTimeValidObject // // Synopsis: flush a CRL from the "TVO" system // //---------------------------------------------------------------------------- BOOL WINAPI CrlFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { return( g_pProcessTVOAgent->FlushTimeValidObject( pszFlushTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, dwFlags, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: CrlFromCertFlushTimeValidObject // // Synopsis: flush a CRL from the "TVO" system given a subject cert // //---------------------------------------------------------------------------- BOOL WINAPI CrlFromCertFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { return( g_pProcessTVOAgent->FlushTimeValidObject( pszFlushTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, dwFlags, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: FreshedtCrlFromCertFlushTimeValidObject // // Synopsis: flush a freshest, delta CRL from the "TVO" system given a // subject cert // //---------------------------------------------------------------------------- BOOL WINAPI FreshestCrlFromCertFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { return( g_pProcessTVOAgent->FlushTimeValidObject( pszFlushTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, dwFlags, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Function: FreshestCrlFromCrlFlushTimeValidObject // // Synopsis: flush a freshest, delta CRL from the "TVO" system given a // base CRL // //---------------------------------------------------------------------------- BOOL WINAPI FreshestCrlFromCrlFlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { return( g_pProcessTVOAgent->FlushTimeValidObject( pszFlushTimeValidOid, pvPara, CONTEXT_OID_CRL, pIssuer, dwFlags, pvReserved ) ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::CTVOCache, public // // Synopsis: Constructor // //---------------------------------------------------------------------------- CTVOCache::CTVOCache ( DWORD cCacheBuckets, DWORD MaxCacheEntries, BOOL& rfResult ) { LRU_CACHE_CONFIG CacheConfig; assert( MaxCacheEntries > 0 ); memset( &CacheConfig, 0, sizeof( CacheConfig ) ); CacheConfig.dwFlags = LRU_CACHE_NO_SERIALIZE | LRU_CACHE_NO_COPY_IDENTIFIER; CacheConfig.cBuckets = cCacheBuckets; CacheConfig.MaxEntries = MaxCacheEntries; CacheConfig.pfnHash = TVOCacheHashOriginIdentifier; CacheConfig.pfnOnRemoval = TVOCacheOnRemoval; rfResult = I_CryptCreateLruCache( &CacheConfig, &m_hCache ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::~CTVOCache, public // // Synopsis: Destructor // //---------------------------------------------------------------------------- CTVOCache::~CTVOCache () { I_CryptFreeLruCache( m_hCache, 0, NULL ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::InsertCacheEntry, public // // Synopsis: insert entry into cache // //---------------------------------------------------------------------------- VOID CTVOCache::InsertCacheEntry (PTVO_CACHE_ENTRY pEntry) { I_CryptInsertLruEntry( pEntry->hLruEntry, NULL ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::RemoveCacheEntry, public // // Synopsis: remove entry from cache // //---------------------------------------------------------------------------- VOID CTVOCache::RemoveCacheEntry (PTVO_CACHE_ENTRY pEntry, BOOL fSuppressFree) { DWORD dwFlags = 0; if ( fSuppressFree == TRUE ) { dwFlags = LRU_SUPPRESS_REMOVAL_NOTIFICATION; } I_CryptRemoveLruEntry( pEntry->hLruEntry, dwFlags, NULL ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::TouchCacheEntry, public // // Synopsis: touch an entry // //---------------------------------------------------------------------------- VOID CTVOCache::TouchCacheEntry (PTVO_CACHE_ENTRY pEntry) { I_CryptTouchLruEntry( pEntry->hLruEntry, 0 ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::FindCacheEntry, public // // Synopsis: find an entry in the cache given the origin identifier. // Skip entries that aren't valid for the subject. // //---------------------------------------------------------------------------- PTVO_CACHE_ENTRY CTVOCache::FindCacheEntry ( CRYPT_ORIGIN_IDENTIFIER OriginIdentifier, LPCSTR pszContextOid, LPVOID pvSubject ) { HLRUENTRY hEntry; CRYPT_DATA_BLOB DataBlob; PTVO_CACHE_ENTRY pEntry = NULL; DataBlob.cbData = MD5DIGESTLEN; DataBlob.pbData = OriginIdentifier; hEntry = I_CryptFindLruEntry( m_hCache, &DataBlob ); while ( hEntry != NULL ) { pEntry = (PTVO_CACHE_ENTRY)I_CryptGetLruEntryData( hEntry ); assert(pEntry); assert(pszContextOid == pEntry->pszContextOid); if (pszContextOid == pEntry->pszContextOid && ObjectContextIsValidForSubject ( pszContextOid, pEntry->pvContext, pvSubject, NULL // pvExtraInfo )) { I_CryptReleaseLruEntry( hEntry ); break; } else { pEntry = NULL; hEntry = I_CryptEnumMatchingLruEntries ( hEntry ); } } return( pEntry ); } //+--------------------------------------------------------------------------- // // Member: CTVOCache::RemoveAllCacheEntries, public // // Synopsis: remove all cache entries // //---------------------------------------------------------------------------- VOID CTVOCache::RemoveAllCacheEntries () { I_CryptFlushLruCache( m_hCache, 0, NULL ); } //+--------------------------------------------------------------------------- // // Function: TVOCacheHashOriginIdentifier // // Synopsis: hash the origin identifier to a DWORD, since the origin // identifier is already a unique MD5 hash our algorithm is // to simply use some of the bytes // //---------------------------------------------------------------------------- DWORD WINAPI TVOCacheHashOriginIdentifier (PCRYPT_DATA_BLOB pIdentifier) { DWORD Hash; assert( pIdentifier->cbData == MD5DIGESTLEN ); memcpy( &Hash, pIdentifier->pbData, sizeof( DWORD ) ); return( Hash ); } //+--------------------------------------------------------------------------- // // Function: TVOCacheOnRemoval // // Synopsis: removal notification callback // //---------------------------------------------------------------------------- VOID WINAPI TVOCacheOnRemoval (LPVOID pvData, LPVOID pvRemovalContext) { ObjectContextFreeTVOCacheEntry( (PTVO_CACHE_ENTRY)pvData ); } //+--------------------------------------------------------------------------- // // Member: CTVOAgent::CTVOAgent, public // // Synopsis: Constructor // //---------------------------------------------------------------------------- CTVOAgent::CTVOAgent ( DWORD cCacheBuckets, DWORD MaxCacheEntries, BOOL& rfResult ) : m_Cache( cCacheBuckets, MaxCacheEntries, rfResult ) { if (!Pki_InitializeCriticalSection( &m_Lock )) { rfResult = FALSE; } } //+--------------------------------------------------------------------------- // // Member: CTVOAgent::~CTVOAgent, public // // Synopsis: Destructor // //---------------------------------------------------------------------------- CTVOAgent::~CTVOAgent () { m_Cache.RemoveAllCacheEntries(); DeleteCriticalSection( &m_Lock ); } //+--------------------------------------------------------------------------- // // Member: CTVOAgent::GetTimeValidObject, public // // Synopsis: get a time valid CAPI2 object // //---------------------------------------------------------------------------- BOOL CTVOAgent::GetTimeValidObject ( IN LPCSTR pszTimeValidOid, IN LPVOID pvPara, IN LPCSTR pszContextOid, IN PCCERT_CONTEXT pIssuer, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult = TRUE; CRYPT_ORIGIN_IDENTIFIER OriginIdentifier; PTVO_CACHE_ENTRY pCacheEntry = NULL; DWORD PreferredUrlIndex = 0; PCRYPT_URL_ARRAY pUrlArray = NULL; DWORD cb = 0; DWORD cbUrlArray = 0; PCRYPT_URL_ARRAY pCacheUrlArray = NULL; LPWSTR pwszUrlHint = NULL; BOOL fHintInArray = FALSE; BOOL fArrayOwned = FALSE; BOOL fCrlFromCert = FALSE; LPCSTR pszUrlOidCrlFromCert = NULL; LPVOID pvSubject = NULL; BOOL fFreshest = FALSE; if ( pszTimeValidOid == TIME_VALID_OID_GET_CRL_FROM_CERT ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CERTIFICATE_CRL_DIST_POINT; pvSubject = pvPara; } else if ( pszTimeValidOid == TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CERT ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CERTIFICATE_FRESHEST_CRL; pvSubject = pvPara; fFreshest = TRUE; } else if ( pszTimeValidOid == TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CRL ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CRL_FRESHEST_CRL; pvSubject = (LPVOID) ((PCCERT_CRL_CONTEXT_PAIR)pvPara)->pCertContext; fFreshest = TRUE; } if (fCrlFromCert) { if ( CrlGetOriginIdentifierFromSubjectCert( (PCCERT_CONTEXT)pvSubject, pIssuer, fFreshest, OriginIdentifier ) == FALSE ) { return( FALSE ); } assert( pszContextOid == CONTEXT_OID_CRL ); } else { if ( ObjectContextGetOriginIdentifier( pszContextOid, pvPara, pIssuer, 0, OriginIdentifier ) == FALSE ) { return( FALSE ); } } PreferredUrlIndex = 0; pUrlArray = NULL; EnterCriticalSection( &m_Lock ); pCacheEntry = m_Cache.FindCacheEntry( OriginIdentifier, pszContextOid, pvSubject ); if ( pCacheEntry != NULL ) { if ( !( dwFlags & CRYPT_WIRE_ONLY_RETRIEVAL ) ) { if ( ( dwFlags & CRYPT_DONT_CHECK_TIME_VALIDITY ) || IsValidCreateOrExpireTime ( 0 != (dwFlags & CRYPT_CHECK_FRESHNESS_TIME_VALIDITY), pftValidFor, &pCacheEntry->CreateTime, &pCacheEntry->ExpireTime ) ) { m_Cache.TouchCacheEntry( pCacheEntry ); if ( ppvObject != NULL ) { *ppvObject = ObjectContextDuplicate( pCacheEntry->pszContextOid, pCacheEntry->pvContext ); } LeaveCriticalSection( &m_Lock ); return( TRUE ); } } if ( !( dwFlags & CRYPT_CACHE_ONLY_RETRIEVAL ) ) { if ( GetOfflineUrlTimeStatus(&pCacheEntry->OfflineUrlTimeInfo) < 0 || !I_CryptNetIsConnected() ) { if ( dwFlags & CRYPT_WIRE_ONLY_RETRIEVAL ) { LeaveCriticalSection( &m_Lock ); SetLastError( (DWORD) ERROR_NOT_CONNECTED ); return( FALSE ); } else { dwFlags |= CRYPT_CACHE_ONLY_RETRIEVAL; } } } if ( pCacheEntry->pUrlArrayNext != NULL ) { cbUrlArray = pCacheEntry->cbUrlArrayNext; pCacheUrlArray = pCacheEntry->pUrlArrayNext; PreferredUrlIndex = pCacheEntry->UrlIndexNext; } else { cbUrlArray = pCacheEntry->cbUrlArrayThis; pCacheUrlArray = pCacheEntry->pUrlArrayThis; PreferredUrlIndex = pCacheEntry->UrlIndexThis; } } else if ( !( dwFlags & CRYPT_CACHE_ONLY_RETRIEVAL ) ) { if ( !I_CryptNetIsConnected() ) { if ( dwFlags & CRYPT_WIRE_ONLY_RETRIEVAL ) { LeaveCriticalSection( &m_Lock ); SetLastError( (DWORD) ERROR_NOT_CONNECTED ); return( FALSE ); } else { dwFlags |= CRYPT_CACHE_ONLY_RETRIEVAL; } } } if ( ( fResult == TRUE ) && ( pUrlArray == NULL ) ) { if ( pCacheEntry != NULL ) { pwszUrlHint = pCacheUrlArray->rgwszUrl[ PreferredUrlIndex ]; } if ( fCrlFromCert ) { fResult = CertificateGetCrlDistPointUrl( pszUrlOidCrlFromCert, pvPara, pwszUrlHint, &pUrlArray, &cb, &PreferredUrlIndex, &fHintInArray ); } else if ( pszTimeValidOid == TIME_VALID_OID_GET_CTL ) { fResult = ObjectContextGetNextUpdateUrl( pszContextOid, pvPara, pIssuer, pwszUrlHint, &pUrlArray, &cb, &PreferredUrlIndex, &fHintInArray ); } else { SetLastError( (DWORD) CRYPT_E_NOT_FOUND ); fResult = FALSE; } if ( fResult == TRUE ) { cbUrlArray = cb; } else if ( pCacheEntry != NULL ) { pUrlArray = (PCRYPT_URL_ARRAY)new BYTE [ cbUrlArray ]; if ( pUrlArray != NULL ) { if (CopyUrlArray( pUrlArray, pCacheUrlArray, cbUrlArray )) { fHintInArray = TRUE; fResult = TRUE; } else { delete [] (BYTE *) pUrlArray; pUrlArray = NULL; SetLastError( (DWORD) E_INVALIDARG ); } } else { SetLastError( (DWORD) E_OUTOFMEMORY ); } } } LeaveCriticalSection( &m_Lock ); if ( fResult == TRUE ) { fResult = GetTimeValidObjectByUrl( cbUrlArray, pUrlArray, PreferredUrlIndex, pszContextOid, pIssuer, pvSubject, OriginIdentifier, pftValidFor, dwFlags, dwTimeout, ppvObject, pCredentials, NULL, &fArrayOwned, pvReserved ); } if ( fArrayOwned == FALSE ) { delete [] (BYTE *) pUrlArray; } return( fResult ); } //+--------------------------------------------------------------------------- // // Member: CTVOAgent::GetTimeValidObjectByUrl, public // // Synopsis: get a time valid object using URL // //---------------------------------------------------------------------------- BOOL CTVOAgent::GetTimeValidObjectByUrl ( IN DWORD cbUrlArray, IN PCRYPT_URL_ARRAY pUrlArray, IN DWORD PreferredUrlIndex, IN LPCSTR pszContextOid, IN PCCERT_CONTEXT pIssuer, IN LPVOID pvSubject, IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, OUT OPTIONAL LPVOID* ppvObject, IN OPTIONAL PCRYPT_CREDENTIALS pCredentials, IN OPTIONAL LPWSTR pwszUrlExtra, OUT BOOL* pfArrayOwned, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult = FALSE; DWORD cCount; LPWSTR pwsz; LPVOID pvContext = NULL; PTVO_CACHE_ENTRY pEntry = NULL; PTVO_CACHE_ENTRY pFound; DWORD LastError; // Following is only used for CRYPT_ACCUMULATIVE_TIMEOUT FILETIME ftEndUrlRetrieval; if ( PreferredUrlIndex != 0 ) { pwsz = pUrlArray->rgwszUrl[PreferredUrlIndex]; pUrlArray->rgwszUrl[PreferredUrlIndex] = pUrlArray->rgwszUrl[0]; pUrlArray->rgwszUrl[0] = pwsz; } if (dwFlags & CRYPT_ACCUMULATIVE_TIMEOUT) { if (0 == dwTimeout) { dwFlags &= ~CRYPT_ACCUMULATIVE_TIMEOUT; } else { FILETIME ftStartUrlRetrieval; GetSystemTimeAsFileTime(&ftStartUrlRetrieval); I_CryptIncrementFileTimeByMilliseconds( &ftStartUrlRetrieval, dwTimeout, &ftEndUrlRetrieval); } } for ( cCount = 0; cCount < pUrlArray->cUrl; cCount++ ) { if (dwFlags & CRYPT_ACCUMULATIVE_TIMEOUT) { // Limit each URL timeout to half of the remaining time dwTimeout = I_CryptRemainingMilliseconds(&ftEndUrlRetrieval) / 2; if (0 == dwTimeout) { dwTimeout = 1; } } fResult = RetrieveTimeValidObjectByUrl( pUrlArray->rgwszUrl[cCount], pszContextOid, pftValidFor, dwFlags, dwTimeout, pCredentials, pIssuer, pvSubject, OriginIdentifier, &pvContext, pvReserved ); if ( fResult == TRUE ) { fResult = ObjectContextCreateTVOCacheEntry( m_Cache.LruCacheHandle(), pszContextOid, pvContext, OriginIdentifier, cbUrlArray, pUrlArray, cCount, pIssuer, &pEntry ); *pfArrayOwned = fResult; break; } } if ( ( PreferredUrlIndex != 0 ) && ( *pfArrayOwned == FALSE ) ) { pwsz = pUrlArray->rgwszUrl[PreferredUrlIndex]; pUrlArray->rgwszUrl[PreferredUrlIndex] = pUrlArray->rgwszUrl[0]; pUrlArray->rgwszUrl[0] = pwsz; } if ( ( fResult == FALSE ) && ( pwszUrlExtra != NULL ) ) { if (dwFlags & CRYPT_ACCUMULATIVE_TIMEOUT) { // Limit each URL timeout to half of the remaining time dwTimeout = I_CryptRemainingMilliseconds(&ftEndUrlRetrieval) / 2; if (0 == dwTimeout) { dwTimeout = 1; } } fResult = RetrieveTimeValidObjectByUrl( pwszUrlExtra, pszContextOid, pftValidFor, dwFlags, dwTimeout, pCredentials, pIssuer, pvSubject, OriginIdentifier, &pvContext, pvReserved ); if ( fResult == TRUE ) { CCryptUrlArray cua( pUrlArray->cUrl + 1, 5, fResult ); DWORD cb = 0; PCRYPT_URL_ARRAY pcua = NULL; if ( fResult == TRUE ) { for ( cCount = 0; cCount < pUrlArray->cUrl; cCount++ ) { fResult = cua.AddUrl( pUrlArray->rgwszUrl[cCount], FALSE ); if ( fResult == FALSE ) { break; } } } if ( fResult == TRUE ) { fResult = cua.GetArrayInSingleBufferEncodedForm( &pcua, &cb ); } if ( fResult == TRUE ) { fResult = ObjectContextCreateTVOCacheEntry( m_Cache.LruCacheHandle(), pszContextOid, pvContext, OriginIdentifier, cb, pcua, pUrlArray->cUrl, pIssuer, &pEntry ); if ( fResult == FALSE ) { CryptMemFree( pcua ); } } cua.FreeArray( FALSE ); } } LastError = GetLastError(); EnterCriticalSection( &m_Lock ); pFound = m_Cache.FindCacheEntry( OriginIdentifier, pszContextOid, pvSubject ); if ( !fResult && pFound && !( dwFlags & CRYPT_CACHE_ONLY_RETRIEVAL ) ) { SetOfflineUrlTime( &pFound->OfflineUrlTimeInfo ); } if ( ( fResult == TRUE ) && !( dwFlags & CRYPT_DONT_VERIFY_SIGNATURE ) ) { if ( ( pFound != NULL ) && ( CompareFileTime( &pFound->CreateTime, &pEntry->CreateTime ) >= 0 ) ) { ObjectContextFree( pszContextOid, pvContext ); pvContext = ObjectContextDuplicate( pFound->pszContextOid, pFound->pvContext ); SetOnlineUrlTime( &pFound->OfflineUrlTimeInfo ); ObjectContextFreeTVOCacheEntry( pEntry ); } else { if ( pFound != NULL ) { m_Cache.RemoveCacheEntry( pFound ); } m_Cache.InsertCacheEntry( pEntry ); } } else if ( pEntry != NULL ) { ObjectContextFreeTVOCacheEntry( pEntry ); } LeaveCriticalSection( &m_Lock ); if ( pvContext != NULL ) { if ( ( ppvObject != NULL ) && ( fResult == TRUE ) ) { *ppvObject = pvContext; } else { ObjectContextFree( pszContextOid, pvContext ); } } SetLastError( LastError ); return( fResult ); } //+--------------------------------------------------------------------------- // // Member: CTVOAgent::FlushTimeValidObject, public // // Synopsis: flush time valid object // //---------------------------------------------------------------------------- BOOL CTVOAgent::FlushTimeValidObject ( IN LPCSTR pszFlushTimeValidOid, IN LPVOID pvPara, IN LPCSTR pszFlushContextOid, IN PCCERT_CONTEXT pIssuer, IN DWORD dwFlags, IN LPVOID pvReserved ) { BOOL fResult = TRUE; CRYPT_ORIGIN_IDENTIFIER OriginIdentifier; PTVO_CACHE_ENTRY pCacheEntry = NULL; PCRYPT_URL_ARRAY pUrlArray = NULL; DWORD cbUrlArray; DWORD dwError = 0; DWORD cCount; BOOL fCrlFromCert = FALSE; LPCSTR pszUrlOidCrlFromCert = NULL; LPVOID pvSubject = NULL; BOOL fFreshest = FALSE; if ( pszFlushTimeValidOid == TIME_VALID_OID_GET_CRL_FROM_CERT ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CERTIFICATE_CRL_DIST_POINT; pvSubject = pvPara; } else if ( pszFlushTimeValidOid == TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CERT ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CERTIFICATE_FRESHEST_CRL; pvSubject = pvPara; fFreshest = TRUE; } else if ( pszFlushTimeValidOid == TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CRL ) { fCrlFromCert = TRUE; pszUrlOidCrlFromCert = URL_OID_CRL_FRESHEST_CRL; pvSubject = (LPVOID) ((PCCERT_CRL_CONTEXT_PAIR)pvPara)->pCertContext; fFreshest = TRUE; } if (fCrlFromCert) { if ( CrlGetOriginIdentifierFromSubjectCert( (PCCERT_CONTEXT)pvSubject, pIssuer, fFreshest, OriginIdentifier ) == FALSE ) { return( FALSE ); } assert( pszFlushContextOid == CONTEXT_OID_CRL ); } else { if ( ObjectContextGetOriginIdentifier( pszFlushContextOid, pvPara, pIssuer, 0, OriginIdentifier ) == FALSE ) { return( FALSE ); } } EnterCriticalSection( &m_Lock ); pCacheEntry = m_Cache.FindCacheEntry( OriginIdentifier, pszFlushContextOid, pvSubject ); if ( pCacheEntry != NULL ) { // Remove the entry but suppress the freeing of it since we are going // to use the data structure later m_Cache.RemoveCacheEntry( pCacheEntry, TRUE ); } LeaveCriticalSection( &m_Lock ); if ( pCacheEntry != NULL ) { if ( pCacheEntry->pUrlArrayThis != NULL ) { for ( cCount = 0; cCount < pCacheEntry->pUrlArrayThis->cUrl; cCount++ ) { if ( ( SchemeDeleteUrlCacheEntry( pCacheEntry->pUrlArrayThis->rgwszUrl[cCount] ) == FALSE ) && ( GetLastError() != ERROR_FILE_NOT_FOUND ) ) { dwError = GetLastError(); } } } if ( pCacheEntry->pUrlArrayNext != NULL ) { for ( cCount = 0; cCount < pCacheEntry->pUrlArrayNext->cUrl; cCount++ ) { if ( ( SchemeDeleteUrlCacheEntry( pCacheEntry->pUrlArrayNext->rgwszUrl[cCount] ) == FALSE ) && ( GetLastError() != ERROR_FILE_NOT_FOUND ) ) { dwError = GetLastError(); } } } // // Can place optimization here where if the hashes of the // cache object and the passed in object are the same, // we don't need to do any more work // ObjectContextFreeTVOCacheEntry( pCacheEntry ); } if ( fCrlFromCert ) { fResult = CertificateGetCrlDistPointUrl( pszUrlOidCrlFromCert, pvPara, NULL, // pwszUrlHint &pUrlArray, &cbUrlArray, NULL, // pPreferredUrlIndex NULL // pfHintInArray ); } else if ( pszFlushTimeValidOid == TIME_VALID_OID_GET_CTL ) { fResult = ObjectContextGetNextUpdateUrl( pszFlushContextOid, pvPara, pIssuer, NULL, &pUrlArray, &cbUrlArray, NULL, NULL ); } if ( ( fResult == TRUE ) && ( pUrlArray != NULL ) ) { for ( cCount = 0; cCount < pUrlArray->cUrl; cCount++ ) { if ( ( SchemeDeleteUrlCacheEntry( pUrlArray->rgwszUrl[cCount] ) == FALSE ) && ( GetLastError() != ERROR_FILE_NOT_FOUND ) ) { dwError = GetLastError(); } } } if ( pUrlArray ) { delete [] (BYTE *) pUrlArray; } if ( ( fResult == TRUE ) && ( dwError != 0 ) ) { SetLastError( dwError ); fResult = FALSE; } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: IsValidCreateOrExpireTime // // Synopsis: for fCheckFreshnessTime, checks if the // specified time is before or the same as the create time. // Otherwise, checks if the specified time is before or the // same as the expire time. A zero expire time matches any time. // //---------------------------------------------------------------------------- BOOL WINAPI IsValidCreateOrExpireTime ( IN BOOL fCheckFreshnessTime, IN LPFILETIME pftValidFor, IN LPFILETIME pftCreateTime, IN LPFILETIME pftExpireTime ) { if (fCheckFreshnessTime) { if (CompareFileTime(pftValidFor, pftCreateTime) <= 0) return TRUE; else return FALSE; } else { if (CompareFileTime(pftValidFor, pftExpireTime) <= 0 || I_CryptIsZeroFileTime(pftExpireTime)) return TRUE; else return FALSE; } } //+--------------------------------------------------------------------------- // // Function: ObjectContextCreateTVOCacheEntry // // Synopsis: create a TVO cache entry // //---------------------------------------------------------------------------- BOOL WINAPI ObjectContextCreateTVOCacheEntry ( IN HLRUCACHE hCache, IN LPCSTR pszContextOid, IN LPVOID pvContext, IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier, IN DWORD cbUrlArrayThis, IN PCRYPT_URL_ARRAY pUrlArrayThis, IN DWORD UrlIndexThis, IN PCCERT_CONTEXT pIssuer, OUT PTVO_CACHE_ENTRY* ppEntry ) { BOOL fResult = TRUE; PTVO_CACHE_ENTRY pEntry; CRYPT_DATA_BLOB DataBlob; pEntry = new TVO_CACHE_ENTRY; if ( pEntry == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); } memset( pEntry, 0, sizeof( TVO_CACHE_ENTRY ) ); // NOTENOTE: This presumes a predefined context oid constant pEntry->pszContextOid = pszContextOid; pEntry->pvContext = ObjectContextDuplicate( pszContextOid, pvContext ); memcpy(pEntry->OriginIdentifier, OriginIdentifier, sizeof(pEntry->OriginIdentifier)); DataBlob.cbData = MD5DIGESTLEN; DataBlob.pbData = pEntry->OriginIdentifier; fResult = I_CryptCreateLruEntry( hCache, &DataBlob, pEntry, &pEntry->hLruEntry ); if ( fResult == TRUE ) { ObjectContextGetNextUpdateUrl( pszContextOid, pvContext, pIssuer, pUrlArrayThis->rgwszUrl[UrlIndexThis], &pEntry->pUrlArrayNext, &pEntry->cbUrlArrayNext, &pEntry->UrlIndexNext, NULL ); fResult = ObjectContextGetCreateAndExpireTimes( pszContextOid, pvContext, &pEntry->CreateTime, &pEntry->ExpireTime ); } if ( fResult == TRUE ) { pEntry->cbUrlArrayThis = cbUrlArrayThis; pEntry->pUrlArrayThis = pUrlArrayThis; pEntry->UrlIndexThis = UrlIndexThis; *ppEntry = pEntry; } else { ObjectContextFreeTVOCacheEntry( pEntry ); } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: ObjectContextFreeTVOCacheEntry // // Synopsis: free TVO cache entry // //---------------------------------------------------------------------------- VOID WINAPI ObjectContextFreeTVOCacheEntry ( IN PTVO_CACHE_ENTRY pEntry ) { if ( pEntry->hLruEntry != NULL ) { I_CryptReleaseLruEntry( pEntry->hLruEntry ); } delete [] (BYTE *) pEntry->pUrlArrayThis; delete [] (BYTE *) pEntry->pUrlArrayNext; if ( pEntry->pvContext != NULL ) { ObjectContextFree( pEntry->pszContextOid, pEntry->pvContext ); } delete pEntry; } //+--------------------------------------------------------------------------- // // Function: CertificateGetCrlDistPointUrl // // Synopsis: get crl dist point URL from certificate // //---------------------------------------------------------------------------- BOOL WINAPI CertificateGetCrlDistPointUrl ( IN LPCSTR pszUrlOid, IN LPVOID pvPara, IN LPWSTR pwszUrlHint, OUT PCRYPT_URL_ARRAY* ppUrlArray, OUT DWORD* pcbUrlArray, OUT DWORD* pPreferredUrlIndex, OUT BOOL* pfHintInArray ) { BOOL fResult; DWORD cbUrlArray; PCRYPT_URL_ARRAY pUrlArray = NULL; DWORD PreferredUrlIndex; fResult = CryptGetObjectUrl( pszUrlOid, pvPara, 0, NULL, &cbUrlArray, NULL, NULL, NULL ); if ( fResult == TRUE ) { pUrlArray = (PCRYPT_URL_ARRAY)new BYTE [ cbUrlArray ]; if ( pUrlArray != NULL ) { fResult = CryptGetObjectUrl( pszUrlOid, pvPara, 0, pUrlArray, &cbUrlArray, NULL, NULL, NULL ); } else { SetLastError( (DWORD) E_OUTOFMEMORY ); fResult = FALSE; } } if ( fResult == TRUE ) { BOOL fHintInArray = FALSE; GetUrlArrayIndex( pUrlArray, pwszUrlHint, 0, &PreferredUrlIndex, &fHintInArray ); *ppUrlArray = pUrlArray; *pcbUrlArray = cbUrlArray; if ( pfHintInArray != NULL ) { *pfHintInArray = fHintInArray; } if ( pPreferredUrlIndex != NULL ) { *pPreferredUrlIndex = PreferredUrlIndex; } } else { if ( pUrlArray ) { delete [] (BYTE *) pUrlArray; } } return( fResult ); } BOOL WINAPI RetrieveObjectByUrlValidForSubject( IN LPWSTR pwszUrl, IN LPCSTR pszContextOid, IN BOOL fCheckFreshnessTime, IN LPFILETIME pftValidFor, IN DWORD dwRetrievalFlags, IN DWORD dwTimeout, IN PCRYPT_CREDENTIALS pCredentials, IN PCCERT_CONTEXT pSigner, IN LPVOID pvSubject, IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier, OUT LPVOID* ppvObject, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult; HCERTSTORE hUrlStore = NULL; LPVOID pvObject; fResult = CryptRetrieveObjectByUrlW( pwszUrl, pszContextOid, (dwRetrievalFlags | CRYPT_RETRIEVE_MULTIPLE_OBJECTS | CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL) & ~CRYPT_VERIFY_CONTEXT_SIGNATURE, dwTimeout, (LPVOID *) &hUrlStore, NULL, // hAsyncRetrieve NULL, // pCredentials NULL, // pSigner NULL // pvReserved ); if (!fResult) goto CommonReturn; pvObject = NULL; while (pvObject = ObjectContextEnumObjectsInStore ( hUrlStore, pszContextOid, pvObject )) { CRYPT_ORIGIN_IDENTIFIER ObjectOriginIdentifier; if (!ObjectContextGetOriginIdentifier( pszContextOid, pvObject, pSigner, 0, ObjectOriginIdentifier )) continue; if (0 != memcmp(OriginIdentifier, ObjectOriginIdentifier, sizeof(ObjectOriginIdentifier))) continue; if (dwRetrievalFlags & CRYPT_VERIFY_CONTEXT_SIGNATURE) { if (!ObjectContextVerifySignature ( pszContextOid, pvObject, pSigner )) continue; } if (!ObjectContextIsValidForSubject ( pszContextOid, pvObject, pvSubject, pvReserved )) continue; if (NULL != pftValidFor) { FILETIME CreateTime; FILETIME ExpireTime; if (!ObjectContextGetCreateAndExpireTimes( pszContextOid, pvObject, &CreateTime, &ExpireTime )) continue; if (!IsValidCreateOrExpireTime ( fCheckFreshnessTime, pftValidFor, &CreateTime, &ExpireTime ) ) continue; } *ppvObject = pvObject; fResult = TRUE; goto CommonReturn; } // Make sure the error isn't CRYPT_E_NOT_FOUND. msrevoke will // return CRYPT_E_NO_REVOCATION_CHECK instead of // CRYPT_E_REVOCATION_OFFLINE for CRYPT_E_NOT_FOUND. SetLastError(ERROR_FILE_NOT_FOUND); fResult = FALSE; CommonReturn: if (hUrlStore) CertCloseStore(hUrlStore, 0); return fResult; } //+--------------------------------------------------------------------------- // // Function: RetrieveTimeValidObjectByUrl // // Synopsis: retrieve a time valid object given an URL // //---------------------------------------------------------------------------- BOOL WINAPI RetrieveTimeValidObjectByUrl ( IN LPWSTR pwszUrl, IN LPCSTR pszContextOid, IN LPFILETIME pftValidFor, IN DWORD dwFlags, IN DWORD dwTimeout, IN PCRYPT_CREDENTIALS pCredentials, IN PCCERT_CONTEXT pSigner, IN LPVOID pvSubject, IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier, OUT LPVOID* ppvObject, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult = FALSE; LPVOID pvContext = NULL; DWORD dwVerifyFlags = 0; DWORD dwCacheStoreFlags = CRYPT_DONT_CACHE_RESULT; if ( dwFlags & CRYPT_DONT_CHECK_TIME_VALIDITY ) { pftValidFor = NULL; } if ( !( dwFlags & CRYPT_DONT_VERIFY_SIGNATURE ) ) { dwVerifyFlags |= CRYPT_VERIFY_CONTEXT_SIGNATURE; dwCacheStoreFlags &= ~CRYPT_DONT_CACHE_RESULT; } if ( !( dwFlags & CRYPT_WIRE_ONLY_RETRIEVAL ) ) { fResult = RetrieveObjectByUrlValidForSubject( pwszUrl, pszContextOid, 0 != (dwFlags & CRYPT_CHECK_FRESHNESS_TIME_VALIDITY), pftValidFor, CRYPT_CACHE_ONLY_RETRIEVAL | dwVerifyFlags, 0, // dwTimeout NULL, // pCredentials pSigner, pvSubject, OriginIdentifier, &pvContext, pvReserved ); } if ( fResult == FALSE ) { if ( !( dwFlags & CRYPT_CACHE_ONLY_RETRIEVAL ) ) { DWORD dwRetrievalFlags = CRYPT_WIRE_ONLY_RETRIEVAL | dwCacheStoreFlags | dwVerifyFlags; LONG lStatus; // +1 - Online // 0 - Offline, current time >= earliest online time, hit the wire // -1 - Offline, current time < earliest onlime time lStatus = GetOriginUrlStatusW( OriginIdentifier, pwszUrl, pszContextOid, dwRetrievalFlags ); if (lStatus >= 0) { fResult = RetrieveObjectByUrlValidForSubject( pwszUrl, pszContextOid, 0 != (dwFlags & CRYPT_CHECK_FRESHNESS_TIME_VALIDITY), pftValidFor, dwRetrievalFlags, dwTimeout, pCredentials, pSigner, pvSubject, OriginIdentifier, &pvContext, pvReserved ); if (!fResult) { DWORD dwErr = GetLastError(); SetOfflineOriginUrlW( OriginIdentifier, pwszUrl, pszContextOid, dwRetrievalFlags ); SetLastError( dwErr ); } else if (lStatus == 0) { // Remove from offline list SetOnlineOriginUrlW( OriginIdentifier, pwszUrl, pszContextOid, dwRetrievalFlags ); } } } } *ppvObject = pvContext; return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CreateProcessTVOAgent // // Synopsis: create process TVO agent // //---------------------------------------------------------------------------- BOOL WINAPI CreateProcessTVOAgent ( OUT CTVOAgent** ppAgent ) { BOOL fResult = FALSE; HKEY hKey = NULL; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof( DWORD ); DWORD cCacheBuckets; DWORD MaxCacheEntries; CTVOAgent* pAgent; if ( RegOpenKeyA( HKEY_LOCAL_MACHINE, TVO_KEY_NAME, &hKey ) == ERROR_SUCCESS ) { if ( RegQueryValueExA( hKey, TVO_CACHE_BUCKETS_VALUE_NAME, NULL, &dwType, (LPBYTE)&cCacheBuckets, &dwSize ) != ERROR_SUCCESS ) { cCacheBuckets = TVO_DEFAULT_CACHE_BUCKETS; } if ( RegQueryValueExA( hKey, TVO_MAX_CACHE_ENTRIES_VALUE_NAME, NULL, &dwType, (LPBYTE)&MaxCacheEntries, &dwSize ) != ERROR_SUCCESS ) { MaxCacheEntries = TVO_DEFAULT_MAX_CACHE_ENTRIES; } RegCloseKey(hKey); } else { cCacheBuckets = TVO_DEFAULT_CACHE_BUCKETS; MaxCacheEntries = TVO_DEFAULT_MAX_CACHE_ENTRIES; } pAgent = new CTVOAgent( cCacheBuckets, MaxCacheEntries, fResult ); if ( pAgent == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); } if ( fResult == TRUE ) { *ppAgent = pAgent; } else { delete pAgent; } return( fResult ); }