You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
918 lines
29 KiB
918 lines
29 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows NT Security
|
|
// Copyright (C) Microsoft Corporation, 1997 - 2000
|
|
//
|
|
// File: xcert.cpp
|
|
//
|
|
// Contents: CCertChainEngine's Cross Certificate Methods
|
|
//
|
|
// History: 22-Dec-99 philh Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
#include <global.hxx>
|
|
#include <dbgdef.h>
|
|
|
|
|
|
|
|
|
|
//+=========================================================================
|
|
// Cross Certificate Distribution Point Support Functions
|
|
//==========================================================================
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Get and allocate the cross certificate distribution points Url array
|
|
// and info for the specified certificate.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
XCertGetDistPointsUrl(
|
|
IN PCCERT_CONTEXT pCert,
|
|
OUT PCRYPT_URL_ARRAY *ppUrlArray,
|
|
OUT PCRYPT_URL_INFO *ppUrlInfo
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PCRYPT_URL_ARRAY pUrlArray = NULL;
|
|
DWORD cbUrlArray = 0;
|
|
PCRYPT_URL_INFO pUrlInfo = NULL;
|
|
DWORD cbUrlInfo = 0;
|
|
|
|
if (!ChainGetObjectUrl(
|
|
URL_OID_CROSS_CERT_DIST_POINT,
|
|
(LPVOID) pCert,
|
|
CRYPT_GET_URL_FROM_PROPERTY | CRYPT_GET_URL_FROM_EXTENSION,
|
|
NULL, // pUrlArray
|
|
&cbUrlArray,
|
|
NULL, // pUrlInfo
|
|
&cbUrlInfo,
|
|
NULL // pvReserved
|
|
))
|
|
goto GetObjectUrlError;
|
|
|
|
pUrlArray = (PCRYPT_URL_ARRAY) new BYTE [cbUrlArray];
|
|
if (NULL == pUrlArray)
|
|
goto OutOfMemory;
|
|
|
|
pUrlInfo = (PCRYPT_URL_INFO) new BYTE [cbUrlInfo];
|
|
if (NULL == pUrlInfo)
|
|
goto OutOfMemory;
|
|
|
|
if (!ChainGetObjectUrl(
|
|
URL_OID_CROSS_CERT_DIST_POINT,
|
|
(LPVOID) pCert,
|
|
CRYPT_GET_URL_FROM_PROPERTY | CRYPT_GET_URL_FROM_EXTENSION,
|
|
pUrlArray,
|
|
&cbUrlArray,
|
|
pUrlInfo,
|
|
&cbUrlInfo,
|
|
NULL // pvReserved
|
|
))
|
|
goto GetObjectUrlError;
|
|
|
|
if (0 == pUrlArray->cUrl || 0 == pUrlInfo->cGroup)
|
|
goto NoDistPointUrls;
|
|
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
*ppUrlArray = pUrlArray;
|
|
*ppUrlInfo = pUrlInfo;
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
if (pUrlArray) {
|
|
delete (LPBYTE) pUrlArray;
|
|
pUrlArray = NULL;
|
|
}
|
|
if (pUrlInfo) {
|
|
delete (LPBYTE) pUrlInfo;
|
|
pUrlInfo = NULL;
|
|
}
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(GetObjectUrlError)
|
|
SET_ERROR(OutOfMemory, E_OUTOFMEMORY)
|
|
SET_ERROR(NoDistPointUrls, CRYPT_E_NOT_FOUND)
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Checks and returns TRUE if all the Urls are contained in the
|
|
// distribution point.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
XCertIsUrlInDistPoint(
|
|
IN DWORD cUrl,
|
|
IN LPWSTR *ppwszUrl,
|
|
IN PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
for ( ; 0 < cUrl; cUrl--, ppwszUrl++) {
|
|
DWORD cDPUrl = pEntry->cUrl;
|
|
LPWSTR *ppwszDPUrl = pEntry->rgpwszUrl;
|
|
|
|
for ( ; 0 < cDPUrl; cDPUrl--, ppwszDPUrl++) {
|
|
if (0 == wcscmp(*ppwszUrl, *ppwszDPUrl))
|
|
break;
|
|
}
|
|
|
|
if (0 == cDPUrl)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Finds a distribution point link containing all the Urls.
|
|
//--------------------------------------------------------------------------
|
|
PXCERT_DP_LINK
|
|
WINAPI
|
|
XCertFindUrlInDistPointLinks(
|
|
IN DWORD cUrl,
|
|
IN LPWSTR *rgpwszUrl,
|
|
IN PXCERT_DP_LINK pLink
|
|
)
|
|
{
|
|
for ( ; pLink; pLink = pLink->pNext) {
|
|
if (XCertIsUrlInDistPoint(cUrl, rgpwszUrl, pLink->pCrossCertDPEntry))
|
|
return pLink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Finds a distribution point entry containing all the Urls.
|
|
//--------------------------------------------------------------------------
|
|
PXCERT_DP_ENTRY
|
|
WINAPI
|
|
XCertFindUrlInDistPointEntries(
|
|
IN DWORD cUrl,
|
|
IN LPWSTR *rgpwszUrl,
|
|
PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
for ( ; pEntry; pEntry = pEntry->pNext) {
|
|
if (XCertIsUrlInDistPoint(cUrl, rgpwszUrl, pEntry))
|
|
return pEntry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Inserts the cross certificate distribution entry into the engine's
|
|
// list. The list is ordered according to ascending NextSyncTimes.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::InsertCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
if (NULL == m_pCrossCertDPEntry) {
|
|
// First entry to be added to engine's list
|
|
pEntry->pNext = NULL;
|
|
pEntry->pPrev = NULL;
|
|
m_pCrossCertDPEntry = pEntry;
|
|
} else {
|
|
PXCERT_DP_ENTRY pListEntry = m_pCrossCertDPEntry;
|
|
BOOL fLast = FALSE;
|
|
|
|
// Loop while Entry's NextSyncTime > list's NextSyncTime
|
|
while (0 < CompareFileTime(&pEntry->NextSyncTime,
|
|
&pListEntry->NextSyncTime)) {
|
|
if (NULL == pListEntry->pNext) {
|
|
fLast = TRUE;
|
|
break;
|
|
} else
|
|
pListEntry = pListEntry->pNext;
|
|
}
|
|
|
|
if (fLast) {
|
|
assert(NULL == pListEntry->pNext);
|
|
pEntry->pNext = NULL;
|
|
pEntry->pPrev = pListEntry;
|
|
pListEntry->pNext = pEntry;
|
|
} else {
|
|
pEntry->pNext = pListEntry;
|
|
pEntry->pPrev = pListEntry->pPrev;
|
|
if (pListEntry->pPrev) {
|
|
assert(pListEntry->pPrev->pNext == pListEntry);
|
|
pListEntry->pPrev->pNext = pEntry;
|
|
} else {
|
|
assert(m_pCrossCertDPEntry == pListEntry);
|
|
m_pCrossCertDPEntry = pEntry;
|
|
}
|
|
pListEntry->pPrev = pEntry;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Removes the cross certificate distribution point from the engine's list.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::RemoveCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
if (pEntry->pNext)
|
|
pEntry->pNext->pPrev = pEntry->pPrev;
|
|
if (pEntry->pPrev)
|
|
pEntry->pPrev->pNext = pEntry->pNext;
|
|
else
|
|
m_pCrossCertDPEntry = pEntry->pNext;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// For an online certificate distribution point updates the NextSyncTime
|
|
// and repositions accordingly in the engine's list.
|
|
//
|
|
// NextSyncTime = LastSyncTime + dwSyncDeltaTime.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::RepositionOnlineCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry,
|
|
IN LPFILETIME pLastSyncTime
|
|
)
|
|
{
|
|
assert(!I_CryptIsZeroFileTime(pLastSyncTime));
|
|
pEntry->LastSyncTime = *pLastSyncTime;
|
|
pEntry->dwOfflineCnt = 0;
|
|
|
|
I_CryptIncrementFileTimeBySeconds(
|
|
pLastSyncTime,
|
|
pEntry->dwSyncDeltaTime,
|
|
&pEntry->NextSyncTime
|
|
);
|
|
|
|
RemoveCrossCertDistPointEntry(pEntry);
|
|
InsertCrossCertDistPointEntry(pEntry);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// For an offline certificate distribution point, increments the offline
|
|
// count, updates the NextSyncTime to be some delta from the current time
|
|
// and repositions accordingly in the engine's list.
|
|
//
|
|
// NextSyncTime = CurrentTime +
|
|
// rgChainOfflineUrlDeltaSeconds[dwOfflineCnt - 1]
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::RepositionOfflineCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry,
|
|
IN LPFILETIME pCurrentTime
|
|
)
|
|
{
|
|
pEntry->dwOfflineCnt++;
|
|
|
|
I_CryptIncrementFileTimeBySeconds(
|
|
pCurrentTime,
|
|
ChainGetOfflineUrlDeltaSeconds(pEntry->dwOfflineCnt),
|
|
&pEntry->NextSyncTime
|
|
);
|
|
|
|
RemoveCrossCertDistPointEntry(pEntry);
|
|
InsertCrossCertDistPointEntry(pEntry);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// For a smaller SyncDeltaTime in a certificate distribution point,
|
|
// updates the NextSyncTime and repositions accordingly in the engine's list.
|
|
//
|
|
// Note, if the distribution point is offline, the NextSyncTime isn't
|
|
// updated.
|
|
//
|
|
// NextSyncTime = LastSyncTime + dwSyncDeltaTime.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry,
|
|
IN DWORD dwSyncDeltaTime
|
|
)
|
|
{
|
|
if (dwSyncDeltaTime >= pEntry->dwSyncDeltaTime)
|
|
return;
|
|
|
|
pEntry->dwSyncDeltaTime = dwSyncDeltaTime;
|
|
|
|
if (I_CryptIsZeroFileTime(&pEntry->LastSyncTime) ||
|
|
0 != pEntry->dwOfflineCnt)
|
|
return;
|
|
|
|
RepositionOnlineCrossCertDistPointEntry(pEntry, &pEntry->LastSyncTime);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Creates the cross certificate distribution point and insert's in the
|
|
// engine's list.
|
|
//
|
|
// The returned entry has a refCnt of 1.
|
|
//--------------------------------------------------------------------------
|
|
PXCERT_DP_ENTRY
|
|
CCertChainEngine::CreateCrossCertDistPointEntry(
|
|
IN DWORD dwSyncDeltaTime,
|
|
IN DWORD cUrl,
|
|
IN LPWSTR *rgpwszUrl
|
|
)
|
|
{
|
|
PXCERT_DP_ENTRY pEntry;
|
|
DWORD cbEntry;
|
|
LPWSTR *ppwszEntryUrl;
|
|
LPWSTR pwszEntryUrl;
|
|
DWORD i;
|
|
|
|
cbEntry = sizeof(XCERT_DP_ENTRY) + cUrl * sizeof(LPWSTR);
|
|
for (i = 0; i < cUrl; i++)
|
|
cbEntry += (wcslen(rgpwszUrl[i]) + 1) * sizeof(WCHAR);
|
|
|
|
pEntry = (PXCERT_DP_ENTRY) new BYTE [cbEntry];
|
|
if (NULL == pEntry) {
|
|
SetLastError((DWORD) E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
memset(pEntry, 0, sizeof(XCERT_DP_ENTRY));
|
|
pEntry->lRefCnt = 1;
|
|
pEntry->dwSyncDeltaTime = dwSyncDeltaTime;
|
|
|
|
pEntry->cUrl = cUrl;
|
|
pEntry->rgpwszUrl = ppwszEntryUrl = (LPWSTR *) &pEntry[1];
|
|
pwszEntryUrl = (LPWSTR) &ppwszEntryUrl[cUrl];
|
|
|
|
for (i = 0; i < cUrl; i++) {
|
|
ppwszEntryUrl[i] = pwszEntryUrl;
|
|
wcscpy(pwszEntryUrl, rgpwszUrl[i]);
|
|
pwszEntryUrl += wcslen(rgpwszUrl[i]) + 1;
|
|
}
|
|
|
|
InsertCrossCertDistPointEntry(pEntry);
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Increments the cross certificate distribution point's reference count.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::AddRefCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
pEntry->lRefCnt++;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Decrements the cross certificate distribution point's reference count.
|
|
//
|
|
// When decremented to 0, removed from the engine's list and freed.
|
|
//
|
|
// Returns TRUE if decremented to 0 and freed.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CCertChainEngine::ReleaseCrossCertDistPointEntry(
|
|
IN OUT PXCERT_DP_ENTRY pEntry
|
|
)
|
|
{
|
|
if (0 != --pEntry->lRefCnt)
|
|
return FALSE;
|
|
|
|
RemoveCrossCertDistPointEntry(pEntry);
|
|
FreeCrossCertDistPoints(&pEntry->pChildCrossCertDPLink);
|
|
|
|
if (pEntry->hUrlStore) {
|
|
CertRemoveStoreFromCollection(
|
|
m_hCrossCertStore,
|
|
pEntry->hUrlStore
|
|
);
|
|
CertCloseStore(pEntry->hUrlStore, 0);
|
|
}
|
|
|
|
delete (LPBYTE) pEntry;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Finds and gets the Cross Certificate Distribution Points for the
|
|
// specified certificate store.
|
|
//
|
|
// *ppLinkHead is updated to contain the store's distribution point links.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CCertChainEngine::GetCrossCertDistPointsForStore(
|
|
IN HCERTSTORE hStore,
|
|
IN BOOL fOnlyLMSystemStore,
|
|
IN OUT PXCERT_DP_LINK *ppLinkHead
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PXCERT_DP_LINK pOldLinkHead = *ppLinkHead;
|
|
PXCERT_DP_LINK pNewLinkHead = NULL;
|
|
PCCERT_CONTEXT pCert = NULL;
|
|
PCRYPT_URL_ARRAY pUrlArray = NULL;
|
|
PCRYPT_URL_INFO pUrlInfo = NULL;
|
|
|
|
while (pCert = CertFindCertificateInStore(
|
|
hStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0, // dwFindFlags
|
|
CERT_FIND_CROSS_CERT_DIST_POINTS,
|
|
NULL, // pvFindPara,
|
|
pCert
|
|
)) {
|
|
|
|
DWORD dwSyncDeltaTime;
|
|
DWORD cDP;
|
|
DWORD *pcUrl;
|
|
LPWSTR *ppwszUrl;
|
|
|
|
if (fOnlyLMSystemStore) {
|
|
DWORD dwAccessStateFlags = 0;
|
|
DWORD cbData = sizeof(dwAccessStateFlags);
|
|
|
|
if (!CertGetCertificateContextProperty(
|
|
pCert,
|
|
CERT_ACCESS_STATE_PROP_ID,
|
|
&dwAccessStateFlags,
|
|
&cbData
|
|
) ||
|
|
(0 == (dwAccessStateFlags &
|
|
CERT_ACCESS_STATE_LM_SYSTEM_STORE_FLAG)))
|
|
continue;
|
|
}
|
|
|
|
if (!XCertGetDistPointsUrl(
|
|
pCert,
|
|
&pUrlArray,
|
|
&pUrlInfo
|
|
))
|
|
continue;
|
|
|
|
dwSyncDeltaTime = pUrlInfo->dwSyncDeltaTime;
|
|
if (0 == dwSyncDeltaTime)
|
|
dwSyncDeltaTime = XCERT_DEFAULT_SYNC_DELTA_TIME;
|
|
else if (XCERT_MIN_SYNC_DELTA_TIME > dwSyncDeltaTime)
|
|
dwSyncDeltaTime = XCERT_MIN_SYNC_DELTA_TIME;
|
|
|
|
cDP = pUrlInfo->cGroup;
|
|
pcUrl = pUrlInfo->rgcGroupEntry;
|
|
ppwszUrl = pUrlArray->rgwszUrl;
|
|
|
|
for ( ; 0 < cDP; cDP--, ppwszUrl += *pcUrl++) {
|
|
PXCERT_DP_LINK pLink;
|
|
PXCERT_DP_ENTRY pEntry;
|
|
DWORD cUrl = *pcUrl;
|
|
|
|
if (0 == cUrl)
|
|
continue;
|
|
|
|
// Do we already have an entry in the new list
|
|
if (XCertFindUrlInDistPointLinks(cUrl, ppwszUrl, pNewLinkHead))
|
|
continue;
|
|
|
|
// If the entry existed in the old list, move to the new list
|
|
if (pLink = XCertFindUrlInDistPointLinks(
|
|
cUrl, ppwszUrl, pOldLinkHead)) {
|
|
if (pLink->pNext)
|
|
pLink->pNext->pPrev = pLink->pPrev;
|
|
if (pLink->pPrev)
|
|
pLink->pPrev->pNext = pLink->pNext;
|
|
else
|
|
pOldLinkHead = pLink->pNext;
|
|
|
|
RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
|
|
pLink->pCrossCertDPEntry, dwSyncDeltaTime);
|
|
} else {
|
|
// Check if the entry already exists for the engine
|
|
if (pEntry = XCertFindUrlInDistPointEntries(
|
|
cUrl, ppwszUrl, m_pCrossCertDPEntry)) {
|
|
AddRefCrossCertDistPointEntry(pEntry);
|
|
RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
|
|
pEntry, dwSyncDeltaTime);
|
|
} else {
|
|
// Create entry and insert at beginning of
|
|
// entries list.
|
|
if (NULL == (pEntry = CreateCrossCertDistPointEntry(
|
|
dwSyncDeltaTime,
|
|
cUrl,
|
|
ppwszUrl
|
|
)))
|
|
goto CreateDistPointEntryError;
|
|
}
|
|
|
|
pLink = new XCERT_DP_LINK;
|
|
if (NULL == pLink) {
|
|
ReleaseCrossCertDistPointEntry(pEntry);
|
|
goto CreateDistPointLinkError;
|
|
}
|
|
|
|
pLink->pCrossCertDPEntry = pEntry;
|
|
|
|
}
|
|
|
|
if (pNewLinkHead) {
|
|
assert(NULL == pNewLinkHead->pPrev);
|
|
pNewLinkHead->pPrev = pLink;
|
|
}
|
|
pLink->pNext = pNewLinkHead;
|
|
pLink->pPrev = NULL;
|
|
pNewLinkHead = pLink;
|
|
}
|
|
|
|
delete (LPBYTE) pUrlArray;
|
|
pUrlArray = NULL;
|
|
delete (LPBYTE) pUrlInfo;
|
|
pUrlInfo = NULL;
|
|
}
|
|
|
|
assert(NULL == pUrlArray);
|
|
assert(NULL == pUrlInfo);
|
|
assert(NULL == pCert);
|
|
|
|
*ppLinkHead = pNewLinkHead;
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
if (pOldLinkHead) {
|
|
DWORD dwErr = GetLastError();
|
|
|
|
FreeCrossCertDistPoints(&pOldLinkHead);
|
|
|
|
SetLastError(dwErr);
|
|
}
|
|
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
*ppLinkHead = NULL;
|
|
if (pUrlArray)
|
|
delete (LPBYTE) pUrlArray;
|
|
if (pUrlInfo)
|
|
delete (LPBYTE) pUrlInfo;
|
|
if (pCert)
|
|
CertFreeCertificateContext(pCert);
|
|
|
|
if (pNewLinkHead) {
|
|
FreeCrossCertDistPoints(&pNewLinkHead);
|
|
assert(NULL == pNewLinkHead);
|
|
}
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(CreateDistPointEntryError)
|
|
TRACE_ERROR(CreateDistPointLinkError)
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Removes an orphan'ed entry not in any list of links.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::RemoveCrossCertDistPointOrphanEntry(
|
|
IN PXCERT_DP_ENTRY pOrphanEntry
|
|
)
|
|
{
|
|
PXCERT_DP_ENTRY pEntry;
|
|
|
|
for (pEntry = m_pCrossCertDPEntry; pEntry; pEntry = pEntry->pNext) {
|
|
PXCERT_DP_LINK pLink = pEntry->pChildCrossCertDPLink;
|
|
|
|
while (pLink) {
|
|
if (pLink->pCrossCertDPEntry == pOrphanEntry) {
|
|
if (pLink->pNext)
|
|
pLink->pNext->pPrev = pLink->pPrev;
|
|
if (pLink->pPrev)
|
|
pLink->pPrev->pNext = pLink->pNext;
|
|
else
|
|
pEntry->pChildCrossCertDPLink = pLink->pNext;
|
|
|
|
delete pLink;
|
|
|
|
if (ReleaseCrossCertDistPointEntry(pOrphanEntry))
|
|
return;
|
|
else
|
|
break;
|
|
}
|
|
|
|
pLink = pLink->pNext;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Returns TRUE if the entry is in this or any child link list
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
XCertIsDistPointInLinkList(
|
|
IN PXCERT_DP_ENTRY pOrphanEntry,
|
|
IN PXCERT_DP_LINK pLink
|
|
)
|
|
{
|
|
for (; pLink; pLink = pLink->pNext) {
|
|
PXCERT_DP_ENTRY pEntry = pLink->pCrossCertDPEntry;
|
|
if (pOrphanEntry == pEntry)
|
|
return TRUE;
|
|
|
|
// Note, inhibit recursion by checking an entry's list of links
|
|
// only once.
|
|
if (!pEntry->fChecked) {
|
|
pEntry->fChecked = TRUE;
|
|
|
|
if (XCertIsDistPointInLinkList(pOrphanEntry,
|
|
pEntry->pChildCrossCertDPLink))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Frees the cross certificate distribution point links.
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CCertChainEngine::FreeCrossCertDistPoints(
|
|
IN OUT PXCERT_DP_LINK *ppLinkHead
|
|
)
|
|
{
|
|
PXCERT_DP_LINK pLink = *ppLinkHead;
|
|
*ppLinkHead = NULL;
|
|
|
|
while (pLink) {
|
|
PXCERT_DP_LINK pDelete;
|
|
PXCERT_DP_ENTRY pEntry;
|
|
|
|
pEntry = pLink->pCrossCertDPEntry;
|
|
if (ReleaseCrossCertDistPointEntry(pEntry))
|
|
;
|
|
else {
|
|
// Clear the fChecked flag for all entries
|
|
PXCERT_DP_ENTRY pCheckEntry;
|
|
for (pCheckEntry = m_pCrossCertDPEntry; pCheckEntry;
|
|
pCheckEntry = pCheckEntry->pNext)
|
|
pCheckEntry->fChecked = FALSE;
|
|
|
|
if (!XCertIsDistPointInLinkList(pEntry, m_pCrossCertDPLink))
|
|
// An orphaned entry. Not in anyone else's list
|
|
RemoveCrossCertDistPointOrphanEntry(pEntry);
|
|
}
|
|
|
|
pDelete = pLink;
|
|
pLink = pLink->pNext;
|
|
delete pDelete;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Retrieve the cross certificates
|
|
//
|
|
// Leaves the engine's critical section to do the URL
|
|
// fetching. If the engine was touched by another thread,
|
|
// it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
|
|
//
|
|
// If the URL store is changed, increments engine's touch count and flushes
|
|
// issuer and end cert object caches.
|
|
//
|
|
// Assumption: Chain engine is locked once in the calling thread.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CCertChainEngine::RetrieveCrossCertUrl(
|
|
IN PCCHAINCALLCONTEXT pCallContext,
|
|
IN OUT PXCERT_DP_ENTRY pEntry,
|
|
IN DWORD dwRetrievalFlags,
|
|
IN OUT BOOL *pfTimeValid
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
FILETIME CurrentTime;
|
|
HCERTSTORE hNewUrlStore = NULL;
|
|
FILETIME NewLastSyncTime;
|
|
CRYPT_RETRIEVE_AUX_INFO RetrieveAuxInfo;
|
|
DWORD i;
|
|
|
|
memset(&RetrieveAuxInfo, 0, sizeof(RetrieveAuxInfo));
|
|
RetrieveAuxInfo.cbSize = sizeof(RetrieveAuxInfo);
|
|
RetrieveAuxInfo.pLastSyncTime = &NewLastSyncTime;
|
|
|
|
pCallContext->CurrentTime(&CurrentTime);
|
|
|
|
// Loop through Urls and try to retrieve a time valid cross cert URL
|
|
for (i = 0; i < pEntry->cUrl; i++) {
|
|
NewLastSyncTime = CurrentTime;
|
|
LPWSTR pwszUrl = NULL;
|
|
DWORD cbUrl;
|
|
|
|
// Do URL fetching outside of the engine's critical section
|
|
|
|
// Need to make a copy of the Url string. pEntry
|
|
// can be modified by another thread outside of the critical section.
|
|
cbUrl = (wcslen(pEntry->rgpwszUrl[i]) + 1) * sizeof(WCHAR);
|
|
pwszUrl = (LPWSTR) PkiNonzeroAlloc(cbUrl);
|
|
if (NULL == pwszUrl)
|
|
goto OutOfMemory;
|
|
memcpy(pwszUrl, pEntry->rgpwszUrl[i], cbUrl);
|
|
|
|
pCallContext->ChainEngine()->UnlockEngine();
|
|
fResult = ChainRetrieveObjectByUrlW(
|
|
pwszUrl,
|
|
CONTEXT_OID_CAPI2_ANY,
|
|
dwRetrievalFlags |
|
|
CRYPT_RETRIEVE_MULTIPLE_OBJECTS |
|
|
CRYPT_STICKY_CACHE_RETRIEVAL,
|
|
pCallContext->ChainPara()->dwUrlRetrievalTimeout,
|
|
(LPVOID *) &hNewUrlStore,
|
|
NULL, // hAsyncRetrieve
|
|
NULL, // pCredentials
|
|
NULL, // pvVerify
|
|
&RetrieveAuxInfo
|
|
);
|
|
pCallContext->ChainEngine()->LockEngine();
|
|
|
|
PkiFree(pwszUrl);
|
|
|
|
if (pCallContext->IsTouchedEngine())
|
|
goto TouchedDuringUrlRetrieval;
|
|
|
|
if (fResult) {
|
|
assert(hNewUrlStore);
|
|
|
|
if (0 > CompareFileTime(&pEntry->LastSyncTime, &NewLastSyncTime)) {
|
|
BOOL fStoreChanged = FALSE;
|
|
|
|
// Move us to the head of the Url list
|
|
DWORD j;
|
|
LPWSTR pwszUrl = pEntry->rgpwszUrl[i];
|
|
|
|
for (j = i; 0 < j; j--)
|
|
pEntry->rgpwszUrl[j] = pEntry->rgpwszUrl[j - 1];
|
|
pEntry->rgpwszUrl[0] = pwszUrl;
|
|
|
|
if (NULL == pEntry->hUrlStore) {
|
|
if (!CertAddStoreToCollection(
|
|
m_hCrossCertStore,
|
|
hNewUrlStore,
|
|
0,
|
|
0
|
|
))
|
|
goto AddStoreToCollectionError;
|
|
pEntry->hUrlStore = hNewUrlStore;
|
|
hNewUrlStore = NULL;
|
|
fStoreChanged = TRUE;
|
|
} else {
|
|
DWORD dwOutFlags = 0;
|
|
if (!I_CertSyncStoreEx(
|
|
pEntry->hUrlStore,
|
|
hNewUrlStore,
|
|
ICERT_SYNC_STORE_INHIBIT_SYNC_PROPERTY_IN_FLAG,
|
|
&dwOutFlags,
|
|
NULL // pvReserved
|
|
))
|
|
goto SyncStoreError;
|
|
if (dwOutFlags & ICERT_SYNC_STORE_CHANGED_OUT_FLAG)
|
|
fStoreChanged = TRUE;
|
|
}
|
|
|
|
if (fStoreChanged) {
|
|
m_pCertObjectCache->FlushObjects( pCallContext );
|
|
pCallContext->TouchEngine();
|
|
|
|
if (!GetCrossCertDistPointsForStore(
|
|
pEntry->hUrlStore,
|
|
FALSE, // fOnlyLMSystemStore
|
|
&pEntry->pChildCrossCertDPLink
|
|
))
|
|
goto UpdateDistPointError;
|
|
}
|
|
|
|
RepositionOnlineCrossCertDistPointEntry(pEntry,
|
|
&NewLastSyncTime);
|
|
|
|
if (0 < CompareFileTime(&pEntry->NextSyncTime, &CurrentTime)) {
|
|
*pfTimeValid = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hNewUrlStore) {
|
|
CertCloseStore(hNewUrlStore, 0);
|
|
hNewUrlStore = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
if (hNewUrlStore)
|
|
CertCloseStore(hNewUrlStore, 0);
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(AddStoreToCollectionError)
|
|
TRACE_ERROR(SyncStoreError)
|
|
TRACE_ERROR(UpdateDistPointError)
|
|
TRACE_ERROR(OutOfMemory)
|
|
SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Update cross certificate distribution points whose NextSyncTime has
|
|
// expired.
|
|
//
|
|
// Leaves the engine's critical section to do the URL
|
|
// fetching. If the engine was touched by another thread,
|
|
// it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
|
|
//
|
|
// If the URL store is changed, increments engine's touch count and flushes
|
|
// issuer and end cert object caches.
|
|
//
|
|
// Assumption: Chain engine is locked once in the calling thread.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CCertChainEngine::UpdateCrossCerts(
|
|
IN PCCHAINCALLCONTEXT pCallContext
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PXCERT_DP_ENTRY pEntry;
|
|
FILETIME CurrentTime;
|
|
|
|
pEntry = m_pCrossCertDPEntry;
|
|
if (NULL == pEntry)
|
|
goto SuccessReturn;
|
|
|
|
m_dwCrossCertDPResyncIndex++;
|
|
|
|
pCallContext->CurrentTime(&CurrentTime);
|
|
while (pEntry &&
|
|
0 >= CompareFileTime(&pEntry->NextSyncTime, &CurrentTime)) {
|
|
PXCERT_DP_ENTRY pNextEntry = pEntry->pNext;
|
|
|
|
if (pEntry->dwResyncIndex < m_dwCrossCertDPResyncIndex) {
|
|
BOOL fTimeValid = FALSE;
|
|
|
|
if (0 == pEntry->dwResyncIndex || pCallContext->IsOnline()) {
|
|
RetrieveCrossCertUrl(
|
|
pCallContext,
|
|
pEntry,
|
|
CRYPT_CACHE_ONLY_RETRIEVAL,
|
|
&fTimeValid
|
|
);
|
|
if (pCallContext->IsTouchedEngine())
|
|
goto TouchedDuringUrlRetrieval;
|
|
|
|
if (!fTimeValid && pCallContext->IsOnline()) {
|
|
RetrieveCrossCertUrl(
|
|
pCallContext,
|
|
pEntry,
|
|
CRYPT_WIRE_ONLY_RETRIEVAL,
|
|
&fTimeValid
|
|
);
|
|
if (pCallContext->IsTouchedEngine())
|
|
goto TouchedDuringUrlRetrieval;
|
|
|
|
if (!fTimeValid)
|
|
RepositionOfflineCrossCertDistPointEntry(pEntry,
|
|
&CurrentTime);
|
|
}
|
|
|
|
// Start over at the beginning. May have added some entries.
|
|
pNextEntry = m_pCrossCertDPEntry;
|
|
}
|
|
|
|
pEntry->dwResyncIndex = m_dwCrossCertDPResyncIndex;
|
|
|
|
}
|
|
// else
|
|
// Skip entries we have already processed.
|
|
|
|
pEntry = pNextEntry;
|
|
}
|
|
|
|
SuccessReturn:
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
|
|
}
|
|
|
|
|
|
|