Leaked source code of windows server 2003
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.
 
 
 
 
 
 

409 lines
15 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: revfunc.cpp
//
// Contents: Certificate Revocation Dispatch Functions
//
// Functions: I_CertRevFuncDllMain
// CertVerifyRevocation
//
// History: 12-Dec-96 philh created
// 11-Mar-97 philh changed signature of CertVerifyRevocation
//--------------------------------------------------------------------------
#include "global.hxx"
#include <dbgdef.h>
static HCRYPTOIDFUNCSET hRevFuncSet;
typedef BOOL (WINAPI *PFN_CERT_DLL_VERIFY_REVOCATION)(
IN DWORD dwEncodingType,
IN DWORD dwRevType,
IN DWORD cContext,
IN PVOID rgpvContext[],
IN DWORD dwFlags,
IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
IN OUT PCERT_REVOCATION_STATUS pRevStatus
);
//+-------------------------------------------------------------------------
// Dll initialization
//--------------------------------------------------------------------------
BOOL
WINAPI
I_CertRevFuncDllMain(
HMODULE hModule,
ULONG ulReason,
LPVOID lpReserved)
{
BOOL fRet;
switch (ulReason) {
case DLL_PROCESS_ATTACH:
if (NULL == (hRevFuncSet = CryptInitOIDFunctionSet(
CRYPT_OID_VERIFY_REVOCATION_FUNC,
0))) // dwFlags
goto CryptInitOIDFunctionSetError;
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_DETACH:
default:
break;
}
fRet = TRUE;
CommonReturn:
return fRet;
ErrorReturn:
fRet = FALSE;
goto CommonReturn;
TRACE_ERROR(CryptInitOIDFunctionSetError)
}
static inline void ZeroRevStatus(OUT PCERT_REVOCATION_STATUS pRevStatus)
{
DWORD cbSize = pRevStatus->cbSize;
memset(pRevStatus, 0, cbSize);
pRevStatus->cbSize = cbSize;
}
// Remember the first "interesting" error. *pdwError is initialized to
// CRYPT_E_NO_REVOCATION_DLL.
static void UpdateNoRevocationCheckStatus(
IN PCERT_REVOCATION_STATUS pRevStatus,
IN OUT DWORD *pdwError,
IN OUT DWORD *pdwReason,
IN OUT BOOL *pfHasFreshnessTime,
IN OUT DWORD *pdwFreshnessTime
)
{
if (pRevStatus->dwError &&
(*pdwError == (DWORD) CRYPT_E_NO_REVOCATION_DLL ||
*pdwError == (DWORD) CRYPT_E_NO_REVOCATION_CHECK)) {
*pdwError = pRevStatus->dwError;
*pdwReason = pRevStatus->dwReason;
if (pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
dwFreshnessTime)) {
*pfHasFreshnessTime = pRevStatus->fHasFreshnessTime;
*pdwFreshnessTime = pRevStatus->dwFreshnessTime;
}
}
}
static BOOL VerifyDefaultRevocation(
IN DWORD dwEncodingType,
IN DWORD dwRevType,
IN DWORD cContext,
IN PVOID rgpvContext[],
IN DWORD dwFlags,
IN FILETIME *pftEndUrlRetrieval,
IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
IN OUT PCERT_REVOCATION_STATUS pRevStatus
)
{
BOOL fResult;
DWORD dwError = (DWORD) CRYPT_E_NO_REVOCATION_DLL;
DWORD dwReason = 0;
BOOL fHasFreshnessTime = FALSE;
DWORD dwFreshnessTime = 0;
LPWSTR pwszDllList; // _alloca'ed
DWORD cchDllList;
DWORD cchDll;
void *pvFuncAddr;
HCRYPTOIDFUNCADDR hFuncAddr;
// Iterate through the installed default functions.
// Setting pwszDll to NULL searches the installed list. Setting
// hFuncAddr to NULL starts the search at the beginning.
hFuncAddr = NULL;
while (CryptGetDefaultOIDFunctionAddress(
hRevFuncSet,
dwEncodingType,
NULL, // pwszDll
0, // dwFlags
&pvFuncAddr,
&hFuncAddr)) {
ZeroRevStatus(pRevStatus);
if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
pRevPara->dwUrlRetrievalTimeout =
I_CryptRemainingMilliseconds(pftEndUrlRetrieval);
if (0 == pRevPara->dwUrlRetrievalTimeout)
pRevPara->dwUrlRetrievalTimeout = 1;
}
fResult = ((PFN_CERT_DLL_VERIFY_REVOCATION) pvFuncAddr)(
dwEncodingType,
dwRevType,
cContext,
rgpvContext,
dwFlags,
pRevPara,
pRevStatus);
if (fResult || CRYPT_E_REVOKED == pRevStatus->dwError ||
0 < pRevStatus->dwIndex) {
// All contexts successfully checked, one of the contexts
// was revoked or successfully able to check at least one
// of the contexts.
CryptFreeOIDFunctionAddress(hFuncAddr, 0);
goto CommonReturn;
} else
// Unable to check revocation for this installed
// function. However, remember any "interesting"
// errors such as, offline.
UpdateNoRevocationCheckStatus(pRevStatus, &dwError, &dwReason,
&fHasFreshnessTime, &dwFreshnessTime);
}
if (!CryptGetDefaultOIDDllList(
hRevFuncSet,
dwEncodingType,
NULL, // pszDllList
&cchDllList)) goto GetDllListError;
__try {
pwszDllList = (LPWSTR) _alloca(cchDllList * sizeof(WCHAR));
} __except(EXCEPTION_EXECUTE_HANDLER) {
goto OutOfMemory;
}
if (!CryptGetDefaultOIDDllList(
hRevFuncSet,
dwEncodingType,
pwszDllList,
&cchDllList)) goto GetDllListError;
for (; 0 != (cchDll = wcslen(pwszDllList)); pwszDllList += cchDll + 1) {
if (CryptGetDefaultOIDFunctionAddress(
hRevFuncSet,
dwEncodingType,
pwszDllList,
0, // dwFlags
&pvFuncAddr,
&hFuncAddr)) {
ZeroRevStatus(pRevStatus);
if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
pRevPara->dwUrlRetrievalTimeout =
I_CryptRemainingMilliseconds(pftEndUrlRetrieval);
if (0 == pRevPara->dwUrlRetrievalTimeout)
pRevPara->dwUrlRetrievalTimeout = 1;
}
fResult = ((PFN_CERT_DLL_VERIFY_REVOCATION) pvFuncAddr)(
dwEncodingType,
dwRevType,
cContext,
rgpvContext,
dwFlags,
pRevPara,
pRevStatus);
CryptFreeOIDFunctionAddress(hFuncAddr, 0);
if (fResult || CRYPT_E_REVOKED == pRevStatus->dwError ||
0 < pRevStatus->dwIndex)
// All contexts successfully checked, one of the contexts
// was revoked or successfully able to check at least one
// of the contexts.
goto CommonReturn;
else
// Unable to check revocation for this registered
// function. However, remember any "interesting"
// errors such as, offline.
UpdateNoRevocationCheckStatus(pRevStatus, &dwError, &dwReason,
&fHasFreshnessTime, &dwFreshnessTime);
}
}
goto ErrorReturn;
CommonReturn:
return fResult;
ErrorReturn:
pRevStatus->dwIndex = 0;
pRevStatus->dwError = dwError;
pRevStatus->dwReason = dwReason;
if (pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
dwFreshnessTime)) {
pRevStatus->fHasFreshnessTime = fHasFreshnessTime;
pRevStatus->dwFreshnessTime = dwFreshnessTime;
}
fResult = FALSE;
goto CommonReturn;
TRACE_ERROR(GetDllListError)
TRACE_ERROR(OutOfMemory)
}
//+-------------------------------------------------------------------------
// Verifies the array of contexts for revocation. The dwRevType parameter
// indicates the type of the context data structure passed in rgpvContext.
// Currently only the revocation of certificates is defined.
//
// If the CERT_VERIFY_REV_CHAIN_FLAG flag is set, then, CertVerifyRevocation
// is verifying a chain of certs where, rgpvContext[i + 1] is the issuer
// of rgpvContext[i]. Otherwise, CertVerifyRevocation makes no assumptions
// about the order of the contexts.
//
// To assist in finding the issuer, the pRevPara may optionally be set. See
// the CERT_REVOCATION_PARA data structure for details.
//
// The contexts must contain enough information to allow the
// installable or registered revocation DLLs to find the revocation server. For
// certificates, this information would normally be conveyed in an
// extension such as the IETF's AuthorityInfoAccess extension.
//
// CertVerifyRevocation returns TRUE if all of the contexts were successfully
// checked and none were revoked. Otherwise, returns FALSE and updates the
// returned pRevStatus data structure as follows:
// dwIndex
// Index of the first context that was revoked or unable to
// be checked for revocation
// dwError
// Error status. LastError is also set to this error status.
// dwError can be set to one of the following error codes defined
// in winerror.h:
// ERROR_SUCCESS - good context
// CRYPT_E_REVOKED - context was revoked. dwReason contains the
// reason for revocation
// CRYPT_E_REVOCATION_OFFLINE - unable to connect to the
// revocation server
// CRYPT_E_NOT_IN_REVOCATION_DATABASE - the context to be checked
// was not found in the revocation server's database.
// CRYPT_E_NO_REVOCATION_CHECK - the called revocation function
// wasn't able to do a revocation check on the context
// CRYPT_E_NO_REVOCATION_DLL - no installed or registered Dll was
// found to verify revocation
// dwReason
// The dwReason is currently only set for CRYPT_E_REVOKED and contains
// the reason why the context was revoked. May be one of the following
// CRL reasons defined by the CRL Reason Code extension ("2.5.29.21")
// CRL_REASON_UNSPECIFIED 0
// CRL_REASON_KEY_COMPROMISE 1
// CRL_REASON_CA_COMPROMISE 2
// CRL_REASON_AFFILIATION_CHANGED 3
// CRL_REASON_SUPERSEDED 4
// CRL_REASON_CESSATION_OF_OPERATION 5
// CRL_REASON_CERTIFICATE_HOLD 6
//
// For each entry in rgpvContext, CertVerifyRevocation iterates
// through the CRYPT_OID_VERIFY_REVOCATION_FUNC
// function set's list of installed DEFAULT functions.
// CryptGetDefaultOIDFunctionAddress is called with pwszDll = NULL. If no
// installed functions are found capable of doing the revocation verification,
// CryptVerifyRevocation iterates through CRYPT_OID_VERIFY_REVOCATION_FUNC's
// list of registered DEFAULT Dlls. CryptGetDefaultOIDDllList is called to
// get the list. CryptGetDefaultOIDFunctionAddress is called to load the Dll.
//
// The called functions have the same signature as CertVerifyRevocation. A
// called function returns TRUE if it was able to successfully check all of
// the contexts and none were revoked. Otherwise, the called function returns
// FALSE and updates pRevStatus. dwIndex is set to the index of
// the first context that was found to be revoked or unable to be checked.
// dwError and LastError are updated. For CRYPT_E_REVOKED, dwReason
// is updated. Upon input to the called function, dwIndex, dwError and
// dwReason have been zero'ed. cbSize has been checked to be >=
// sizeof(CERT_REVOCATION_STATUS).
//
// If the called function returns FALSE, and dwError isn't set to
// CRYPT_E_REVOKED, then, CertVerifyRevocation either continues on to the
// next DLL in the list for a returned dwIndex of 0 or for a returned
// dwIndex > 0, restarts the process of finding a verify function by
// advancing the start of the context array to the returned dwIndex and
// decrementing the count of remaining contexts.
//--------------------------------------------------------------------------
BOOL
WINAPI
CertVerifyRevocation(
IN DWORD dwEncodingType,
IN DWORD dwRevType,
IN DWORD cContext,
IN PVOID rgpvContext[],
IN DWORD dwFlags,
IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
IN OUT PCERT_REVOCATION_STATUS pRevStatus
)
{
BOOL fResult = FALSE;
DWORD dwIndex;
// Following are only used for CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
CERT_REVOCATION_PARA RevPara;
FILETIME ftEndUrlRetrieval;
assert(pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
dwReason));
if (pRevStatus->cbSize < STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
dwReason))
goto InvalidArg;
if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
// RevPara.dwUrlRetrievalTimeout will be updated with the remaining
// timeout
memset(&RevPara, 0, sizeof(RevPara));
if (pRevPara != NULL)
memcpy(&RevPara, pRevPara, min(pRevPara->cbSize, sizeof(RevPara)));
RevPara.cbSize = sizeof(RevPara);
if (0 == RevPara.dwUrlRetrievalTimeout)
dwFlags &= ~CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
else {
FILETIME ftCurrent;
GetSystemTimeAsFileTime(&ftCurrent);
I_CryptIncrementFileTimeByMilliseconds(
&ftCurrent, RevPara.dwUrlRetrievalTimeout, &ftEndUrlRetrieval);
pRevPara = &RevPara;
}
}
dwIndex = 0;
while (dwIndex < cContext) {
fResult = VerifyDefaultRevocation(
dwEncodingType,
dwRevType,
cContext - dwIndex,
&rgpvContext[dwIndex],
dwFlags,
&ftEndUrlRetrieval,
pRevPara,
pRevStatus
);
if (fResult)
// All contexts successfully checked.
break;
else if (CRYPT_E_REVOKED == pRevStatus->dwError ||
0 == pRevStatus->dwIndex) {
// One of the contexts was revoked or unable to check the
// dwIndex context.
pRevStatus->dwIndex += dwIndex;
SetLastError(pRevStatus->dwError);
break;
} else
// Advance past the checked contexts
dwIndex += pRevStatus->dwIndex;
}
if (dwIndex >= cContext) {
// Able to check all the contexts
fResult = TRUE;
pRevStatus->dwIndex = 0;
pRevStatus->dwError = 0;
pRevStatus->dwReason = 0;
}
CommonReturn:
return fResult;
ErrorReturn:
fResult = FALSE;
goto CommonReturn;
SET_ERROR(InvalidArg, E_INVALIDARG)
}