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.
 
 
 
 
 
 

805 lines
23 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: crobu.cpp
//
// Contents: CryptRetrieveObjectByUrl
//
// History: 23-Jul-97 kirtd Created
// 01-Jan-02 philh Changed to internally use UNICODE Urls
//
//----------------------------------------------------------------------------
#include <global.hxx>
#include <dbgdef.h>
#ifndef INTERNET_MAX_PATH_LENGTH
#define INTERNET_MAX_PATH_LENGTH 2048
#endif
// Initial commit size of the stack, in bytes. Estimate of 20K needed for
// wininet.
#define URL_WITH_TIMEOUT_THREAD_STACK_SIZE 0x5000
//
// CryptRetrieveObjectByUrl Entry
//
// Passed to the thread that does the real URL retrieval. The creator
// thread waits for either the URL retrieval to complete or a timeout.
//
typedef struct _CROBU_ENTRY CROBU_ENTRY, *PCROBU_ENTRY;
struct _CROBU_ENTRY {
LPWSTR pwszUrl;
LPCSTR pszObjectOid;
DWORD dwRetrievalFlags;
DWORD dwTimeout;
LPVOID pvObject;
CRYPT_RETRIEVE_AUX_INFO AuxInfo;
FILETIME LastSyncTime;
BOOL fResult;
DWORD dwErr;
HMODULE hModule;
HANDLE hWaitEvent;
DWORD dwState;
PCROBU_ENTRY pNext;
PCROBU_ENTRY pPrev;
};
#define CROBU_RUN_STATE 1
#define CROBU_DONE_STATE 2
#define CROBU_PENDING_STATE 3
CRITICAL_SECTION CrobuCriticalSection;
HMODULE hCrobuModule;
// Linked list of pending URL retrievals
PCROBU_ENTRY pCrobuPendingHead;
VOID
WINAPI
InitializeCryptRetrieveObjectByUrl(
HMODULE hModule
)
{
Pki_InitializeCriticalSection(&CrobuCriticalSection);
hCrobuModule = hModule;
}
VOID
WINAPI
DeleteCryptRetrieveObjectByUrl()
{
DeleteCriticalSection(&CrobuCriticalSection);
}
//
// Local Functions (Forward Reference)
//
BOOL WINAPI IsPendingCryptRetrieveObjectByUrl (
IN LPCWSTR pwszUrl
);
BOOL WINAPI CryptRetrieveObjectByUrlWithTimeout (
IN LPCWSTR pwszUrl,
IN LPCSTR pszObjectOid,
IN DWORD dwRetrievalFlags,
IN DWORD dwTimeout,
OUT LPVOID* ppvObject,
IN PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
);
void
DebugPrintUrlRetrievalError(
IN LPCWSTR pwszUrl,
IN DWORD dwTimeout,
IN DWORD dwErr
);
//+---------------------------------------------------------------------------
//
// Function: CryptRetrieveObjectByUrlA
//
// Synopsis: retrieve PKI object given an URL
//
//----------------------------------------------------------------------------
BOOL WINAPI CryptRetrieveObjectByUrlA (
IN LPCSTR pszUrl,
IN LPCSTR pszObjectOid,
IN DWORD dwRetrievalFlags,
IN DWORD dwTimeout,
OUT LPVOID* ppvObject,
IN HCRYPTASYNC hAsyncRetrieve,
IN PCRYPT_CREDENTIALS pCredentials,
IN LPVOID pvVerify,
IN PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
WCHAR pwszUrl[INTERNET_MAX_PATH_LENGTH+1];
if ( !MultiByteToWideChar(
CP_ACP,
0,
pszUrl,
-1,
pwszUrl,
INTERNET_MAX_PATH_LENGTH+1
))
{
return( FALSE );
}
return( CryptRetrieveObjectByUrlW(
pwszUrl,
pszObjectOid,
dwRetrievalFlags,
dwTimeout,
ppvObject,
hAsyncRetrieve,
pCredentials,
pvVerify,
pAuxInfo
) );
}
//+---------------------------------------------------------------------------
//
// Function: CryptRetrieveObjectByUrlW
//
// Synopsis: retrieve PKI object given an URL
//
//----------------------------------------------------------------------------
BOOL WINAPI CryptRetrieveObjectByUrlW (
IN LPCWSTR pwszUrl,
IN LPCSTR pszObjectOid,
IN DWORD dwRetrievalFlags,
IN DWORD dwTimeout,
OUT LPVOID* ppvObject,
IN HCRYPTASYNC hAsyncRetrieve,
IN PCRYPT_CREDENTIALS pCredentials,
IN LPVOID pvVerify,
IN PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult;
CObjectRetrievalManager* porm = NULL;
// Remove any leading spaces
while (L' ' == *pwszUrl)
pwszUrl++;
I_CryptNetDebugTracePrintfA(
"CRYPTNET.DLL --> %s URL to retrieve: %S\n",
0 != (dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ?
"Cached" : "Wire", pwszUrl);
// For a nonCache retrieval with timeout, do the retrieval in another
// thread. wininet and winldap don't always honor the timeout value.
//
// Check for parameters not supported by doing in another thread.
//
// Also, check that a cancel callback hasn't been registered via
// CryptInstallCancelRetrieval()
if (0 != dwTimeout && !(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) &&
0xFFFF >= (DWORD_PTR) pszObjectOid &&
NULL == hAsyncRetrieve && NULL == pCredentials &&
NULL == pvVerify &&
NULL == I_CryptGetTls(hCryptNetCancelTls) )
{
if (IsPendingCryptRetrieveObjectByUrl( pwszUrl ))
{
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> CryptRetrieveObjectByUrl, already pending for : %S\n",
pwszUrl);
SetLastError( (DWORD) ERROR_BAD_NET_RESP );
return( FALSE );
}
else
{
return CryptRetrieveObjectByUrlWithTimeout (
pwszUrl,
pszObjectOid,
dwRetrievalFlags,
dwTimeout,
ppvObject,
pAuxInfo
);
}
}
porm = new CObjectRetrievalManager;
if ( porm == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
fResult = porm->RetrieveObjectByUrl(
pwszUrl,
pszObjectOid,
dwRetrievalFlags,
dwTimeout,
ppvObject,
NULL,
NULL,
hAsyncRetrieve,
pCredentials,
pvVerify,
pAuxInfo
);
porm->Release();
if (!fResult)
{
DWORD dwLastErr = GetLastError();
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> %s URL to retrieve: %S, failed: %d (0x%x)\n",
0 != (dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ?
"Cached" : "Wire", pwszUrl, dwLastErr, dwLastErr);
SetLastError(dwLastErr);
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: CryptCancelAsyncRetrieval
//
// Synopsis: cancel asynchronous object retrieval
//
//----------------------------------------------------------------------------
BOOL WINAPI CryptCancelAsyncRetrieval (HCRYPTASYNC hAsyncRetrieval)
{
SetLastError( (DWORD) E_NOTIMPL );
return( FALSE );
}
//+===========================================================================
//
// Functions supporting URL retrieval with timeout. The actual retrieval
// is done in another, created thread.
//
//============================================================================
//+---------------------------------------------------------------------------
// Returns TRUE if the previously initiated URL retrieval hasn't completed.
//----------------------------------------------------------------------------
BOOL WINAPI IsPendingCryptRetrieveObjectByUrl (
IN LPCWSTR pwszUrl
)
{
BOOL fPending = FALSE;
PCROBU_ENTRY pEntry;
EnterCriticalSection(&CrobuCriticalSection);
for (pEntry = pCrobuPendingHead; NULL != pEntry; pEntry = pEntry->pNext) {
assert(CROBU_PENDING_STATE == pEntry->dwState);
if (0 == wcscmp(pwszUrl, pEntry->pwszUrl)) {
fPending = TRUE;
break;
}
}
LeaveCriticalSection(&CrobuCriticalSection);
return fPending;
}
//+-------------------------------------------------------------------------
// Duplicate the Dll library's handle
//--------------------------------------------------------------------------
static HMODULE DuplicateLibrary(
IN HMODULE hDll
)
{
if (hDll) {
WCHAR wszModule[_MAX_PATH + 1];
if (0 == GetModuleFileNameU(hDll, wszModule, _MAX_PATH))
goto GetModuleFileNameError;
wszModule[_MAX_PATH] = L'\0';
if (NULL == (hDll = LoadLibraryExU(wszModule, NULL, 0)))
goto LoadLibraryError;
}
CommonReturn:
return hDll;
ErrorReturn:
hDll = NULL;
goto CommonReturn;
TRACE_ERROR(GetModuleFileNameError)
TRACE_ERROR(LoadLibraryError)
}
//+---------------------------------------------------------------------------
// Thread procedure that does the actual URL retrieval.
//
// Note, even if the creator thread times out, this thread will continue to
// execute until the underlying URL retrieval returns.
//----------------------------------------------------------------------------
DWORD WINAPI CryptRetrieveObjectByUrlWithTimeoutThreadProc (
LPVOID lpThreadParameter
)
{
PCROBU_ENTRY pEntry = (PCROBU_ENTRY) lpThreadParameter;
CObjectRetrievalManager* porm = NULL;
HMODULE hModule;
// Do the actual URL retrieval using the parameters passed to this
// thread by the creator thread.
porm = new CObjectRetrievalManager;
if (NULL == porm ) {
pEntry->dwErr = (DWORD) E_OUTOFMEMORY;
pEntry->fResult = FALSE;
} else {
pEntry->fResult = porm->RetrieveObjectByUrl(
pEntry->pwszUrl,
pEntry->pszObjectOid,
pEntry->dwRetrievalFlags,
pEntry->dwTimeout,
&pEntry->pvObject,
NULL, // ppfnFreeObject
NULL, // ppvFreeContext
NULL, // hAsyncRetrieve
NULL, // pCredentials
NULL, // pvVerify
&pEntry->AuxInfo
);
pEntry->dwErr = GetLastError();
porm->Release();
}
EnterCriticalSection(&CrobuCriticalSection);
// The creator thread incremented cryptnet's ref count to prevent us
// from being unloaded until this thread exits.
hModule = pEntry->hModule;
pEntry->hModule = NULL;
if (CROBU_RUN_STATE == pEntry->dwState) {
// The creator thread didn't timeout. Wake it up and set the
// state to indicate we completed.
assert(pEntry->hWaitEvent);
SetEvent(pEntry->hWaitEvent);
pEntry->dwState = CROBU_DONE_STATE;
LeaveCriticalSection(&CrobuCriticalSection);
} else {
// The creator thread timed out. We were added to the pending
// list when it timed out.
LPVOID pv = pEntry->pvObject;
LPCSTR pOID = pEntry->pszObjectOid;
assert(CROBU_PENDING_STATE == pEntry->dwState);
assert(NULL == pEntry->hWaitEvent);
// Remove from pending list
if (pEntry->pNext)
pEntry->pNext->pPrev = pEntry->pPrev;
if (pEntry->pPrev)
pEntry->pPrev->pNext = pEntry->pNext;
else {
assert(pCrobuPendingHead == pEntry);
pCrobuPendingHead = pEntry->pNext;
}
LeaveCriticalSection(&CrobuCriticalSection);
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> CryptRetrieveObjectByUrl, pending completed for : %S\n",
pEntry->pwszUrl);
if (pv) {
// Free the returned object
if (NULL == pOID)
CryptMemFree( pv );
else if (pEntry->dwRetrievalFlags &
CRYPT_RETRIEVE_MULTIPLE_OBJECTS)
CertCloseStore((HCERTSTORE) pv, 0);
else if (CONTEXT_OID_CERTIFICATE == pOID)
CertFreeCertificateContext((PCCERT_CONTEXT) pv);
else if (CONTEXT_OID_CTL == pOID)
CertFreeCTLContext((PCCTL_CONTEXT) pv);
else if (CONTEXT_OID_CRL == pOID)
CertFreeCRLContext((PCCRL_CONTEXT) pv);
else {
assert(CONTEXT_OID_CAPI2_ANY == pOID ||
CONTEXT_OID_PKCS7 == pOID);
if (CONTEXT_OID_CAPI2_ANY == pOID ||
CONTEXT_OID_PKCS7 == pOID)
CertCloseStore((HCERTSTORE) pv, 0);
}
}
// Finally free the entry
PkiFree(pEntry);
}
if (hModule)
FreeLibraryAndExitThread(hModule, 0);
else
ExitThread(0);
}
//+---------------------------------------------------------------------------
// Creates another thread to do the URL retrieval. Waits for either the
// URL retrieval to complete or the timeout. For a timeout, the URL retrieval
// entry is added to a pending list and the URL retrieval is allowed to
// complete. However, for a timeout, this procedure returns.
//
// This function guarantees the timeout value is honored.
//----------------------------------------------------------------------------
BOOL WINAPI CryptRetrieveObjectByUrlWithTimeout (
IN LPCWSTR pwszUrl,
IN LPCSTR pszObjectOid,
IN DWORD dwRetrievalFlags,
IN DWORD dwTimeout,
OUT LPVOID* ppvObject,
IN PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
{
BOOL fResult;
DWORD dwErr = 0;
PCROBU_ENTRY pEntry = NULL;
HANDLE hThread = NULL;
HANDLE hToken = NULL;
DWORD dwThreadId;
DWORD cchUrl;
// Allocate and initialize the entry to be passed to the created
// thread for doing the URL retrieval.
cchUrl = wcslen(pwszUrl) + 1;
pEntry = (PCROBU_ENTRY) PkiZeroAlloc(sizeof(CROBU_ENTRY) +
cchUrl * sizeof(WCHAR));
if (NULL == pEntry)
goto OutOfMemory;
pEntry->pwszUrl = (LPWSTR) &pEntry[1];
memcpy(pEntry->pwszUrl, pwszUrl, cchUrl * sizeof(WCHAR));
assert(0xFFFF >= (DWORD_PTR) pszObjectOid);
pEntry->pszObjectOid = pszObjectOid;
pEntry->dwRetrievalFlags = dwRetrievalFlags;
pEntry->dwTimeout = dwTimeout;
// pEntry->pvObject
pEntry->AuxInfo.cbSize = sizeof(pEntry->AuxInfo);
pEntry->AuxInfo.pLastSyncTime = &pEntry->LastSyncTime;
if ( pAuxInfo &&
offsetof(CRYPT_RETRIEVE_AUX_INFO, dwMaxUrlRetrievalByteCount) <
pAuxInfo->cbSize ) {
pEntry->AuxInfo.dwMaxUrlRetrievalByteCount =
pAuxInfo->dwMaxUrlRetrievalByteCount;
}
// else
// pEntry->AuxInfo = zero'ed via PkiZeroAlloc
// pEntry->LastSyncTime
// pEntry->fResult
// pEntry->dwErr
// pEntry->hModule
// pEntry->hWaitEvent
// pEntry->dwState
// pEntry->pNext
// pEntry->pPrev
if (NULL == (pEntry->hWaitEvent =
CreateEvent(
NULL, // lpsa
FALSE, // fManualReset
FALSE, // fInitialState
NULL))) // lpszEventName
goto CreateWaitEventError;
// Inhibit cryptnet.dll from being unloaded until the created thread
// exits.
pEntry->hModule = DuplicateLibrary(hCrobuModule);
pEntry->dwState = CROBU_RUN_STATE;
// Create the thread to do the Url retrieval
if (NULL == (hThread = CreateThread(
NULL, // lpThreadAttributes
URL_WITH_TIMEOUT_THREAD_STACK_SIZE,
CryptRetrieveObjectByUrlWithTimeoutThreadProc,
pEntry,
CREATE_SUSPENDED,
&dwThreadId
)))
goto CreateThreadError;
// If we are impersonating, then, the created thread should also impersonate
if (OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
TRUE,
&hToken
)) {
// There isn't any security problem if the following fails.
// If it fails will do the retrieval using the process's identity.
if (!SetThreadToken(&hThread, hToken)) {
DWORD dwLastErr = GetLastError();
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> SetThreadToken failed: %d (0x%x)\n",
dwLastErr, dwLastErr);
}
CloseHandle(hToken);
hToken = NULL;
}
ResumeThread(hThread);
CloseHandle(hThread);
hThread = NULL;
// Wait for either the Url retrieval to complete or a timeout
WaitForSingleObjectEx(
pEntry->hWaitEvent,
dwTimeout,
FALSE // bAlertable
);
EnterCriticalSection(&CrobuCriticalSection);
if (CROBU_DONE_STATE == pEntry->dwState) {
// The URL retrieval completed in the created thread. Copy the
// results from the entry block shared by this and the created
// thread.
fResult = pEntry->fResult;
dwErr = pEntry->dwErr;
*ppvObject = pEntry->pvObject;
if ( pAuxInfo &&
offsetof(CRYPT_RETRIEVE_AUX_INFO, pLastSyncTime) <
pAuxInfo->cbSize &&
pAuxInfo->pLastSyncTime )
{
*pAuxInfo->pLastSyncTime = pEntry->LastSyncTime;
}
LeaveCriticalSection(&CrobuCriticalSection);
} else {
// The URL retrieval didn't complete in the created thread.
// Add to the pending queue and return URL retrieval failure status.
// Note, the created thread will be allowed to complete the initiated
// retrieval.
assert(CROBU_RUN_STATE == pEntry->dwState);
CloseHandle(pEntry->hWaitEvent);
pEntry->hWaitEvent = NULL;
pEntry->dwState = CROBU_PENDING_STATE;
// Add to the pending queue
if (pCrobuPendingHead) {
pCrobuPendingHead->pPrev = pEntry;
pEntry->pNext = pCrobuPendingHead;
}
pCrobuPendingHead = pEntry;
I_CryptNetDebugErrorPrintfA(
"CRYPTNET.DLL --> CryptRetrieveObjectByUrl, %d timeout for : %S\n",
pEntry->dwTimeout, pEntry->pwszUrl);
pEntry = NULL;
LeaveCriticalSection(&CrobuCriticalSection);
goto RetrieveObjectByUrlTimeout;
}
CommonReturn:
if (!fResult)
DebugPrintUrlRetrievalError(
pwszUrl,
dwTimeout,
dwErr
);
if (pEntry) {
if (pEntry->hWaitEvent)
CloseHandle(pEntry->hWaitEvent);
if (pEntry->hModule)
FreeLibrary(pEntry->hModule);
PkiFree(pEntry);
}
SetLastError(dwErr);
return fResult;
ErrorReturn:
dwErr = GetLastError();
fResult = FALSE;
goto CommonReturn;
TRACE_ERROR(OutOfMemory)
TRACE_ERROR(CreateWaitEventError)
TRACE_ERROR(CreateThreadError)
SET_ERROR(RetrieveObjectByUrlTimeout, ERROR_TIMEOUT)
}
DWORD
GetCryptNetDebugFlags()
{
HKEY hKey = NULL;
DWORD dwType = 0;
DWORD dwValue = 0;
DWORD cbValue = sizeof(dwValue);
DWORD dwLastErr = GetLastError();
if (ERROR_SUCCESS != RegOpenKeyExA(
HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\crypt32",
0, // dwReserved
KEY_READ,
&hKey
))
goto ErrorReturn;
if (ERROR_SUCCESS != RegQueryValueExA(
hKey,
"DebugFlags",
NULL, // pdwReserved
&dwType,
(BYTE *) &dwValue,
&cbValue
))
goto ErrorReturn;
if (dwType != REG_DWORD || cbValue != sizeof(dwValue))
goto ErrorReturn;
CommonReturn:
if (NULL != hKey)
RegCloseKey(hKey);
SetLastError(dwLastErr);
return dwValue;
ErrorReturn:
dwValue = 0;
goto CommonReturn;
}
BOOL
I_CryptNetIsDebugErrorPrintEnabled()
{
return 0 != (GetCryptNetDebugFlags() & 0x1);
}
BOOL
I_CryptNetIsDebugTracePrintEnabled()
{
static BOOL fIKnow = FALSE;
static BOOL fIsDebugTracePrintEnabled = FALSE;
if (!fIKnow) {
fIsDebugTracePrintEnabled =
(0 != (GetCryptNetDebugFlags() & 0x2));
fIKnow = TRUE;
}
return fIsDebugTracePrintEnabled;
}
void
I_CryptNetDebugPrintfA(
LPCSTR szFormat,
...
)
{
char szBuffer[1024];
va_list arglist;
DWORD dwLastErr = GetLastError();
_try
{
va_start(arglist, szFormat);
_vsnprintf(szBuffer, sizeof(szBuffer), szFormat, arglist);
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(arglist);
OutputDebugStringA(szBuffer);
} _except( EXCEPTION_EXECUTE_HANDLER) {
}
SetLastError(dwLastErr);
}
void
I_CryptNetDebugErrorPrintfA(
LPCSTR szFormat,
...
)
{
if (!I_CryptNetIsDebugErrorPrintEnabled())
return;
else {
char szBuffer[1024];
va_list arglist;
DWORD dwLastErr = GetLastError();
_try
{
va_start(arglist, szFormat);
_vsnprintf(szBuffer, sizeof(szBuffer), szFormat, arglist);
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(arglist);
OutputDebugStringA(szBuffer);
} _except( EXCEPTION_EXECUTE_HANDLER) {
}
SetLastError(dwLastErr);
}
}
void
I_CryptNetDebugTracePrintfA(
LPCSTR szFormat,
...
)
{
if (!I_CryptNetIsDebugTracePrintEnabled())
return;
else {
char szBuffer[1024];
va_list arglist;
DWORD dwLastErr = GetLastError();
_try
{
va_start(arglist, szFormat);
_vsnprintf(szBuffer, sizeof(szBuffer), szFormat, arglist);
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(arglist);
OutputDebugStringA(szBuffer);
} _except( EXCEPTION_EXECUTE_HANDLER) {
}
SetLastError(dwLastErr);
}
}
void
DebugPrintUrlRetrievalError(
IN LPCWSTR pwszUrl,
IN DWORD dwTimeout,
IN DWORD dwErr
)
{
I_CryptNetDebugErrorPrintfA("CRYPTNET.DLL --> Url retrieval timeout: %d error: %d (0x%x) for::\n %S\n",
dwTimeout, dwErr, dwErr, pwszUrl);
}