1004 lines
27 KiB
C++
1004 lines
27 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cache.cxx
|
|
|
|
Abstract:
|
|
|
|
Credential cache object for digest sspi package.
|
|
|
|
Author:
|
|
|
|
Adriaan Canter (adriaanc) 01-Aug-1998
|
|
|
|
--*/
|
|
|
|
#include "include.hxx"
|
|
|
|
|
|
//-----------------CCredCache Private Functions --------------------------------
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::Lock
|
|
//--------------------------------------------------------------------
|
|
BOOL CCredCache::Lock()
|
|
{
|
|
BOOL bRet;
|
|
DWORD dwError;
|
|
|
|
dwError = WaitForSingleObject(_hMutex, INFINITE);
|
|
|
|
switch (dwError)
|
|
{
|
|
// Mutex is signalled. We own the mutex. Fall through.
|
|
case WAIT_OBJECT_0:
|
|
|
|
// The thread owning the mutex failed to release it
|
|
// before terminating. We still own the mutex.
|
|
case WAIT_ABANDONED:
|
|
bRet = TRUE;
|
|
break;
|
|
|
|
// Fall through.
|
|
case WAIT_FAILED:
|
|
|
|
// Fail.
|
|
default:
|
|
bRet = FALSE;
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::Unlock
|
|
//--------------------------------------------------------------------
|
|
BOOL CCredCache::Unlock()
|
|
{
|
|
BOOL bRet;
|
|
|
|
bRet = ReleaseMutex(_hMutex);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::GetPtrToObject
|
|
//--------------------------------------------------------------------
|
|
LPDWORD CCredCache::GetPtrToObject(DWORD dwObject)
|
|
{
|
|
return _pMMFile->GetHeaderData(dwObject);
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::SearchCredList
|
|
//--------------------------------------------------------------------
|
|
CCred* CCredCache::SearchCredList(CSess *pSess, LPSTR szHost,
|
|
LPSTR szRealm, LPSTR szUser, BOOL fMatchHost)
|
|
{
|
|
CList CredList;
|
|
CCred *pMatch = NULL;
|
|
|
|
if (!pSess->dwCred)
|
|
goto exit;
|
|
|
|
CredList.Init(&pSess->dwCred);
|
|
while (pMatch = (CCred*) CredList.GetNext())
|
|
{
|
|
if ((!szRealm || !lstrcmpi(szRealm, CCred::GetRealm(pMatch)))
|
|
&& (!szUser || !lstrcmpi(szUser, CCred::GetUser(pMatch))))
|
|
{
|
|
if (!fMatchHost)
|
|
break;
|
|
|
|
CNonce *pNonce;
|
|
CList NonceList;
|
|
NonceList.Init(&pMatch->dwNonce);
|
|
while (pNonce = (CNonce*) NonceList.GetNext())
|
|
{
|
|
if (CNonce::IsHostMatch(pNonce, szHost))
|
|
goto exit;
|
|
}
|
|
|
|
pMatch = NULL;
|
|
break;
|
|
}
|
|
}
|
|
exit:
|
|
return pMatch;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::UpdateInfoList
|
|
//--------------------------------------------------------------------
|
|
CCredInfo* CCredCache::UpdateInfoList(CCredInfo *pInfo, CCredInfo *pHead)
|
|
{
|
|
CCredInfo *pList, *pCur;
|
|
BOOL fUpdate = TRUE;
|
|
|
|
if (!pHead)
|
|
return (pInfo);
|
|
|
|
pList = pCur = pHead;
|
|
|
|
while (pCur)
|
|
{
|
|
// Do entry usernames match ?
|
|
if (!strcmp(pInfo->szUser, pCur->szUser))
|
|
{
|
|
// Is the new entry timestamp greater?
|
|
if (pInfo->tStamp > pCur->tStamp)
|
|
{
|
|
// De-link existing entry.
|
|
if (pCur->pPrev)
|
|
pCur->pPrev->pNext = pCur->pNext;
|
|
else
|
|
pList = pCur->pNext;
|
|
|
|
if (pCur->pNext)
|
|
pCur->pNext->pPrev = pCur->pPrev;
|
|
|
|
// Delete existing entry.
|
|
delete pCur;
|
|
}
|
|
else
|
|
{
|
|
// Found a match but time stamp
|
|
// of existing entry was greater.
|
|
fUpdate = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
pCur = pCur->pNext;
|
|
}
|
|
|
|
// If we superceded an existing matching entry
|
|
// or found no matching entries, prepend to list.
|
|
if (fUpdate)
|
|
{
|
|
pInfo->pNext = pList;
|
|
if (pList)
|
|
pList->pPrev = pInfo;
|
|
pList = pInfo;
|
|
}
|
|
|
|
return pList;
|
|
}
|
|
|
|
//-----------------CCredCache Public Functions --------------------------------
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::GetHeapPtr
|
|
//--------------------------------------------------------------------
|
|
DWORD_PTR CCredCache::GetHeapPtr()
|
|
{
|
|
return _pMMFile->GetMapPtr();
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::IsTrustedHost
|
|
// BUGBUG - no limits on szCtx
|
|
//--------------------------------------------------------------------
|
|
BOOL CCredCache::IsTrustedHost(LPSTR szCtx, LPSTR szHost)
|
|
{
|
|
CHAR szBuf[MAX_PATH];
|
|
CHAR szRegPath[MAX_PATH];
|
|
|
|
DWORD dwType, dwError, cbBuf = MAX_PATH;
|
|
BOOL fRet = FALSE;
|
|
HKEY hHosts = (HKEY) INVALID_HANDLE_VALUE;
|
|
|
|
memcpy(szRegPath, DIGEST_HOSTS_REG_KEY, sizeof(DIGEST_HOSTS_REG_KEY) - 1);
|
|
memcpy(szRegPath + sizeof(DIGEST_HOSTS_REG_KEY) - 1, szCtx, strlen(szCtx) + 1);
|
|
|
|
|
|
if ((dwError = RegCreateKey(HKEY_CURRENT_USER, szRegPath, &hHosts)) == ERROR_SUCCESS)
|
|
{
|
|
if ((dwError = RegQueryValueEx(hHosts, szHost, NULL, &dwType, (LPBYTE) szBuf, &cbBuf)) == ERROR_SUCCESS)
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
if (hHosts != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hHosts);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::SetTrustedHostInfo
|
|
//--------------------------------------------------------------------
|
|
BOOL CCredCache::SetTrustedHostInfo(LPSTR szCtx, CParams *pParams)
|
|
{
|
|
CHAR szRegPath[MAX_PATH], *szUrlBuf = NULL, *szHostBuf = NULL;
|
|
DWORD dwZero = 0, dwError = ERROR_SUCCESS, cbUrlBuf, cbHostBuf;
|
|
BOOL fRet = FALSE;
|
|
HKEY hHosts = (HKEY) INVALID_HANDLE_VALUE;
|
|
|
|
// Form path to trusted host reg key.
|
|
memcpy(szRegPath, DIGEST_HOSTS_REG_KEY, sizeof(DIGEST_HOSTS_REG_KEY) - 1);
|
|
memcpy(szRegPath + sizeof(DIGEST_HOSTS_REG_KEY) - 1, szCtx, strlen(szCtx) + 1);
|
|
|
|
// Open top-level reg key.
|
|
if ((dwError = RegCreateKey(HKEY_CURRENT_USER, szRegPath, &hHosts)) != ERROR_SUCCESS)
|
|
goto exit;
|
|
|
|
// First set authenticating host in registry.
|
|
LPSTR szHost;
|
|
szHost = pParams->GetParam(CParams::HOST);
|
|
DIGEST_ASSERT(szHost);
|
|
|
|
if ((dwError = RegSetValueEx(hHosts, szHost, NULL, REG_DWORD,
|
|
(LPBYTE) &dwZero, sizeof(DWORD))) != ERROR_SUCCESS)
|
|
goto exit;
|
|
|
|
// Now check the domain header for any additional trusted hosts.
|
|
LPSTR szDomain, pszUrl;
|
|
DWORD cbDomain, cbUrl;
|
|
pszUrl = NULL;
|
|
pParams->GetParam(CParams::DOMAIN, &szDomain, &cbDomain);
|
|
if (!szDomain)
|
|
{
|
|
fRet = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
// Parse the domain header for urls. Crack each url to get the
|
|
// host and set the host value in the registry.
|
|
|
|
// First attempt to load shlwapi. If this fails then we simply do not have
|
|
// domain header support.
|
|
if (!g_hShlwapi)
|
|
{
|
|
g_hShlwapi = LoadLibrary(SHLWAPI_DLL_SZ);
|
|
if (!g_hShlwapi)
|
|
{
|
|
dwError = ERROR_DLL_INIT_FAILED;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Attempt to get addresses of UrlUnescape and UrlGetPart
|
|
PFNURLUNESCAPE pfnUrlUnescape;
|
|
PFNURLGETPART pfnUrlGetPart;
|
|
pfnUrlUnescape = (PFNURLUNESCAPE) GetProcAddress(g_hShlwapi, "UrlUnescapeA");
|
|
pfnUrlGetPart = (PFNURLGETPART) GetProcAddress(g_hShlwapi, "UrlGetPartA");
|
|
if (!(pfnUrlUnescape && pfnUrlGetPart))
|
|
{
|
|
dwError = ERROR_INVALID_FUNCTION;
|
|
goto exit;
|
|
}
|
|
|
|
// Strtok through string to get each url (ws and tab delimiters)
|
|
pszUrl = NULL;
|
|
while (pszUrl = strtok((pszUrl ? NULL : szDomain), " \t"))
|
|
{
|
|
// Allocate a buffer for the url since we will first unescape it.
|
|
// Also allocate buffer for host which will be returned from
|
|
// call to shlwapi. Unescaped url and host buffer sizes are
|
|
// bounded by length of original url.
|
|
cbUrl = strlen(pszUrl) + 1;
|
|
cbUrlBuf = cbHostBuf = cbUrl;
|
|
szUrlBuf = new CHAR[cbUrlBuf];
|
|
szHostBuf = new CHAR[cbHostBuf];
|
|
if (!(szUrlBuf && szHostBuf))
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
// Copy strtoked url to buffer.
|
|
memcpy(szUrlBuf, pszUrl, cbUrl);
|
|
|
|
// Unescape the url
|
|
if (S_OK == pfnUrlUnescape(szUrlBuf, NULL, NULL, URL_UNESCAPE_INPLACE))
|
|
{
|
|
// If unescape is successful, parse host from url.
|
|
if (S_OK == pfnUrlGetPart(szUrlBuf, szHostBuf, &cbHostBuf, URL_PART_HOSTNAME, 0))
|
|
{
|
|
// If parse is successful, set host in registry.
|
|
if ((dwError = RegSetValueEx(hHosts, szHostBuf, NULL, REG_DWORD,
|
|
(LPBYTE) &dwZero, sizeof(DWORD))) != ERROR_SUCCESS)
|
|
goto exit;
|
|
}
|
|
}
|
|
delete [] szUrlBuf;
|
|
delete [] szHostBuf;
|
|
szUrlBuf = szHostBuf = NULL;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
// Cleanup.
|
|
exit:
|
|
|
|
DIGEST_ASSERT(dwError == ERROR_SUCCESS);
|
|
|
|
if (hHosts != INVALID_HANDLE_VALUE)
|
|
RegCloseKey(hHosts);
|
|
|
|
if (szUrlBuf)
|
|
delete [] szUrlBuf;
|
|
|
|
if (szHostBuf)
|
|
delete [] szHostBuf;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::MapHandleToSession
|
|
//--------------------------------------------------------------------
|
|
// BUGBUG - don't walk the sessionlist, just obfuscate the ptr in handle.
|
|
CSess *CCredCache::MapHandleToSession(DWORD_PTR dwSess)
|
|
{
|
|
// BUGBUG - if locking fails, return error directly,
|
|
// no last error.
|
|
CSess *pSess = NULL;
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
_pSessList->Seek();
|
|
|
|
while (pSess = (CSess*) _pSessList->GetNext())
|
|
{
|
|
if ((CSess*) (dwSess + (DWORD_PTR) _pMMFile->GetMapPtr()) == pSess)
|
|
break;
|
|
}
|
|
|
|
Unlock();
|
|
exit:
|
|
return pSess;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::MapSessionToHandle
|
|
//--------------------------------------------------------------------
|
|
DWORD CCredCache::MapSessionToHandle(CSess* pSess)
|
|
{
|
|
DWORD dwSess = 0;
|
|
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
dwSess = (DWORD) ((DWORD_PTR) pSess - _pMMFile->GetMapPtr());
|
|
Unlock();
|
|
|
|
exit:
|
|
return dwSess;
|
|
}
|
|
|
|
|
|
|
|
// BUGBUG - init mutex issues.
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::CCredCache
|
|
//--------------------------------------------------------------------
|
|
CCredCache::CCredCache()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::~CCredCache
|
|
//--------------------------------------------------------------------
|
|
CCredCache::~CCredCache()
|
|
{
|
|
DeInit();
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::Init
|
|
//--------------------------------------------------------------------
|
|
DWORD CCredCache::Init()
|
|
{
|
|
BOOL fFirstProc;
|
|
CHAR szMutexName[MAX_PATH];
|
|
DWORD cbMutexName = MAX_PATH;
|
|
|
|
_dwSig = SIG_CACH;
|
|
|
|
// IE5# 89288
|
|
// Get mutex name based on user
|
|
if ((_dwStatus = CMMFile::MakeUserObjectName(szMutexName,
|
|
&cbMutexName, MAKE_MUTEX_NAME)) != ERROR_SUCCESS)
|
|
return _dwStatus;
|
|
|
|
// Create/Open mutex.
|
|
_hMutex = CreateMutex(NULL, FALSE, szMutexName);
|
|
|
|
// BUGBUG - this goes at a higher level.
|
|
// BUGBUG - also watch out for failure to create mutex
|
|
// and then unlocking it.
|
|
if (_hMutex)
|
|
{
|
|
// Created/opened mutex. Flag if we're first process.
|
|
fFirstProc = (GetLastError() != ERROR_ALREADY_EXISTS);
|
|
}
|
|
else
|
|
{
|
|
// Failed to create/open mutex.
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
// Acquire mutex.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
return _dwStatus;
|
|
}
|
|
|
|
// Open or create memory map.
|
|
_pMMFile = new CMMFile(CRED_CACHE_HEAP_SIZE,
|
|
CRED_CACHE_ENTRY_SIZE);
|
|
|
|
if (!_pMMFile)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
_dwStatus = _pMMFile->Init();
|
|
if (_dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
goto exit;
|
|
}
|
|
|
|
g_pHeap = GetHeapPtr();
|
|
|
|
// Initialize session list.
|
|
// BUGBUG - check return codes on failure.
|
|
_pSessList = new CList();
|
|
|
|
DIGEST_ASSERT(_pSessList);
|
|
_pSessList->Init(GetPtrToObject(CRED_CACHE_SESSION_LIST));
|
|
|
|
|
|
exit:
|
|
|
|
// Relase mutex.
|
|
Unlock();
|
|
|
|
return _dwStatus;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::DeInit
|
|
//--------------------------------------------------------------------
|
|
DWORD CCredCache::DeInit()
|
|
{
|
|
// bugbug - assert session list is null and destroy.
|
|
// bugbug - release lock before closing handle.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
delete _pMMFile;
|
|
_dwStatus = CloseHandle(_hMutex);
|
|
Unlock();
|
|
|
|
exit:
|
|
return _dwStatus;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::LogOnToCache
|
|
//--------------------------------------------------------------------
|
|
CSess *CCredCache::LogOnToCache(LPSTR szAppCtx, LPSTR szUserCtx, BOOL fHTTP)
|
|
{
|
|
CSess *pSessNew = NULL;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// Obtain mutex.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
fLocked = TRUE;
|
|
|
|
// For non-http clients, find or create the single
|
|
// global session which all non-http clients use.
|
|
if (!fHTTP)
|
|
{
|
|
CSess * pSess;
|
|
pSess = NULL;
|
|
_pSessList->Seek();
|
|
while (pSess = (CSess*) _pSessList->GetNext())
|
|
{
|
|
if (!pSess->fHTTP)
|
|
{
|
|
// Found the session.
|
|
pSessNew = pSess;
|
|
_dwStatus = ERROR_SUCCESS;
|
|
goto exit;
|
|
}
|
|
}
|
|
if (!pSessNew)
|
|
{
|
|
// Create the non-http gobal session.
|
|
pSessNew = CSess::Create(_pMMFile, NULL, NULL, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a session context; add to list.
|
|
pSessNew = CSess::Create(_pMMFile, szAppCtx, szUserCtx, TRUE);
|
|
}
|
|
|
|
if (!pSessNew)
|
|
{
|
|
// This reflects running out of space in the memmap
|
|
// file. Shouldn't happen in practice.
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Push this session on to the session list.
|
|
_pSessList->Insert(pSessNew);
|
|
|
|
_dwStatus = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
|
|
// Release mutex.
|
|
if(fLocked)
|
|
Unlock();
|
|
return pSessNew;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::LogOffFromCache
|
|
//--------------------------------------------------------------------
|
|
DWORD CCredCache::LogOffFromCache(CSess *pSess)
|
|
{
|
|
CList CredList;
|
|
CCred *pCred;
|
|
|
|
// Obtain mutex.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
if (pSess->fHTTP)
|
|
{
|
|
// Purge all credentials for this session.
|
|
// BUGBUG - not needed.
|
|
// CredList.Init((CEntry **) &pSess->pCred);
|
|
|
|
// Flush all credentials for this session
|
|
// This will also delete nonces.
|
|
FlushCreds(pSess, NULL);
|
|
|
|
// Finally, free the session.
|
|
_pSessList->DeLink(pSess);
|
|
CEntry::Free(_pMMFile, pSess);
|
|
}
|
|
|
|
_dwStatus = ERROR_SUCCESS;
|
|
// Release mutex.
|
|
Unlock();
|
|
|
|
exit:
|
|
return _dwStatus;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::CreateCred
|
|
//--------------------------------------------------------------------
|
|
CCred* CCredCache::CreateCred(CSess *pSess, CCredInfo *pInfo)
|
|
{
|
|
CCred* pCred = NULL, *pCredList;
|
|
CList CredList;
|
|
LPSTR szPass = NULL;
|
|
|
|
// Obtain mutex.
|
|
if (!Lock())
|
|
{
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
// First check to see if any credential in this session matches realm.
|
|
pCred = SearchCredList(pSess, NULL, pInfo->szRealm, NULL, FALSE);
|
|
if (pCred)
|
|
{
|
|
CredList.Init(&pSess->dwCred);
|
|
CredList.DeLink(pCred);
|
|
CCred::Free(_pMMFile, pSess, pCred);
|
|
}
|
|
|
|
// Create a credential.
|
|
// BUGBUG - this could fail, transact any cred update.
|
|
pCred = CCred::Create(_pMMFile, pInfo->szHost, pInfo->szRealm,
|
|
pInfo->szUser, (szPass = pInfo->GetPass()),
|
|
pInfo->szNonce, pInfo->szCNonce);
|
|
|
|
DIGEST_ASSERT(pCred);
|
|
|
|
// Insert into head of session's credential list.
|
|
if (!CSess::GetCred(pSess))
|
|
CSess::SetCred(pSess, pCred);
|
|
else
|
|
{
|
|
CredList.Init(&pSess->dwCred);
|
|
CredList.Insert(pCred);
|
|
}
|
|
|
|
_dwStatus = ERROR_SUCCESS;
|
|
|
|
// Relase mutex.
|
|
Unlock();
|
|
|
|
exit:
|
|
if (szPass)
|
|
{
|
|
SecureZeroMemory(szPass, strlen(szPass));
|
|
delete [] szPass;
|
|
}
|
|
return pCred;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::FindCred
|
|
//--------------------------------------------------------------------
|
|
CCredInfo* CCredCache::FindCred(CSess *pSessIn, LPSTR szHost,
|
|
LPSTR szRealm, LPSTR szUser, LPSTR szNonce,
|
|
LPSTR szCNonce, DWORD dwFlags)
|
|
{
|
|
CCred *pCred;
|
|
CCredInfo *pInfo = NULL;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// Obtain mutex.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
fLocked = TRUE;
|
|
|
|
// If finding a credential for preauthentication.
|
|
if (dwFlags & FIND_CRED_PREAUTH)
|
|
{
|
|
// First search this session's credential list for a match,
|
|
// filtering on the host field in available nonces.
|
|
pCred = SearchCredList(pSessIn, szHost, szRealm, szUser, TRUE);
|
|
|
|
// If a credential is found the nonce is also required.
|
|
// We do not attempt to search other sessions for a nonce
|
|
// because nonce counts must remain in sync. See note below.
|
|
if (pCred)
|
|
{
|
|
// Increment this credential's nonce count.
|
|
CNonce *pNonce;
|
|
pNonce = CCred::GetNonce(pCred, szHost, SERVER_NONCE);
|
|
if (pNonce)
|
|
{
|
|
pNonce->cCount++;
|
|
pInfo = new CCredInfo(pCred, szHost);
|
|
if (!pInfo || pInfo->dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise if finding a credential for response to challenge.
|
|
else if (dwFlags & FIND_CRED_AUTH)
|
|
{
|
|
// First search this session's credential list for a match,
|
|
// ignoring the host field in available nonces.
|
|
pCred = SearchCredList(pSessIn, NULL, szRealm, szUser, FALSE);
|
|
|
|
// If a credential was found.
|
|
if (pCred)
|
|
{
|
|
// Update the credential's nonce value if extant.
|
|
// SetNonce will update any existing nonce entry
|
|
// or create a new one if necessary.
|
|
CCred::SetNonce(_pMMFile, pCred, szHost, szNonce, SERVER_NONCE);
|
|
|
|
// BUGBUG - if credential contains a client nonce for a host,
|
|
// (for MD5-sess) and is challenged for MD5, we don't revert
|
|
// the credential's client nonce to null, so that on subsequent
|
|
// auths we will default to MD5. Fix is to delete client nonce
|
|
// in this case. Not serious problem though since we don't expect this.
|
|
if (szCNonce)
|
|
CCred::SetNonce(_pMMFile, pCred, szHost, szCNonce, CLIENT_NONCE);
|
|
|
|
// Increment this credential's nonce count.
|
|
CNonce *pNonce;
|
|
pNonce = CCred::GetNonce(pCred, szHost, SERVER_NONCE);
|
|
pNonce->cCount++;
|
|
|
|
// Create and return the found credential.
|
|
pInfo = new CCredInfo(pCred, szHost);
|
|
if (!pInfo || pInfo->dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// If no credential was found and the username has been specified
|
|
// also search other sessions for the latest matching credential.
|
|
else if (szUser)
|
|
{
|
|
CSess* pSessCur;
|
|
_pSessList->Seek();
|
|
CCred* pMatch;
|
|
while (pSessCur = (CSess*) _pSessList->GetNext())
|
|
{
|
|
// We've already searched the session passed in.
|
|
if (pSessIn == pSessCur)
|
|
continue;
|
|
|
|
// Are this session's credentials shareable?
|
|
if (CSess::CtxMatch(pSessIn, pSessCur))
|
|
{
|
|
// Find latest credential based on time stamp.
|
|
CCred *pCredList;
|
|
pMatch = SearchCredList(pSessCur, NULL, szRealm, szUser, FALSE);
|
|
if (pMatch && ((!pCred || (pMatch->tStamp > pCred->tStamp))))
|
|
{
|
|
pCred = pMatch;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found a credential in another session, duplicate it
|
|
// and add it to the passed in session's credential list.
|
|
// NOTE : WHEN CREATING THE CREDENTIAL DO NOT DUPLICATE
|
|
// THE NONCE, OTHERWISE NONCE COUNTS WILL BE INCORRECT.
|
|
// USE THE NONCE SUPPLIED IN THE CHALLENGE.
|
|
if (pCred)
|
|
{
|
|
LPSTR szPass = NULL;
|
|
|
|
// Create a cred info from the found credential
|
|
// and the nonce received from the challenge.
|
|
pInfo = new CCredInfo(szHost, CCred::GetRealm(pCred),
|
|
CCred::GetUser(pCred), (szPass = CCred::GetPass(pCred)),
|
|
szNonce, szCNonce);
|
|
|
|
if (szPass)
|
|
{
|
|
SecureZeroMemory(szPass, strlen(szPass));
|
|
delete [] szPass;
|
|
szPass = NULL;
|
|
}
|
|
|
|
if (!pInfo || pInfo->dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
// Create the credential in the session list.
|
|
pCred = CreateCred(pSessIn, pInfo);
|
|
|
|
// Increment this credential's nonce count
|
|
CNonce *pNonce;
|
|
pNonce = CCred::GetNonce(pCred, szHost, SERVER_NONCE);
|
|
pNonce->cCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise we are prompting for UI.
|
|
else if (dwFlags & FIND_CRED_UI)
|
|
{
|
|
// First search this session's credential list for a match,
|
|
// ignoring the host field in available nonces.
|
|
pCred = SearchCredList(pSessIn, NULL, szRealm, szUser, FALSE);
|
|
|
|
if (pCred)
|
|
{
|
|
LPSTR szPass = NULL;
|
|
|
|
// Create and return the found credential.
|
|
pInfo = new CCredInfo(szHost, CCred::GetRealm(pCred),
|
|
CCred::GetUser(pCred), (szPass = CCred::GetPass(pCred)),
|
|
szNonce, szCNonce);
|
|
|
|
if (szPass)
|
|
{
|
|
SecureZeroMemory(szPass, strlen(szPass));
|
|
delete [] szPass;
|
|
szPass = NULL;
|
|
}
|
|
|
|
if (!pInfo || pInfo->dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No credential found in this session's list. Search
|
|
// the credentials in other sessions and assemble a list
|
|
// of available credentials. If multiple credentials
|
|
// are found for a user, select the latest based on
|
|
// time stamp.
|
|
CSess* pSessCur;
|
|
_pSessList->Seek();
|
|
while (pSessCur = (CSess*) _pSessList->GetNext())
|
|
{
|
|
// We've already searched the session passed in.
|
|
if (pSessIn == pSessCur)
|
|
continue;
|
|
|
|
// Are this session's credentials shareable?
|
|
if (CSess::CtxMatch(pSessIn, pSessCur))
|
|
{
|
|
pCred = SearchCredList(pSessCur, NULL, szRealm, szUser, FALSE);
|
|
|
|
if (pCred)
|
|
{
|
|
LPSTR szPass = NULL;
|
|
|
|
// Found a valid credential.
|
|
CCredInfo *pNew;
|
|
pNew = new CCredInfo(szHost, CCred::GetRealm(pCred),
|
|
CCred::GetUser(pCred), (szPass = CCred::GetPass(pCred)),
|
|
szNonce, szCNonce);
|
|
|
|
if (szPass)
|
|
{
|
|
SecureZeroMemory(szPass, strlen(szPass));
|
|
delete [] szPass;
|
|
szPass = NULL;
|
|
}
|
|
|
|
if (!pNew || pNew->dwStatus != ERROR_SUCCESS)
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
// Update list based on timestamps.
|
|
pInfo = UpdateInfoList(pNew, pInfo);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_dwStatus = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
|
|
// Clean up allocated cred infos if
|
|
// we failed for some reason.
|
|
// bugbug - clean this up.
|
|
if (_dwStatus != ERROR_SUCCESS)
|
|
{
|
|
CCredInfo *pNext;
|
|
while (pInfo)
|
|
{
|
|
pNext = pInfo->pNext;
|
|
delete pInfo;
|
|
pInfo = pNext;
|
|
}
|
|
pInfo = NULL;
|
|
}
|
|
|
|
// Relase mutex.
|
|
if(fLocked)
|
|
Unlock();
|
|
|
|
// Return any CCredInfo found, possibly a list or NULL.
|
|
return pInfo;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::FlushCreds
|
|
//--------------------------------------------------------------------
|
|
VOID CCredCache::FlushCreds(CSess *pSess, LPSTR szRealm)
|
|
{
|
|
CSess *pSessCur;
|
|
CCred *pCred;
|
|
CList CredList;
|
|
|
|
// Obtain mutex.
|
|
if (!Lock())
|
|
{
|
|
DIGEST_ASSERT(FALSE);
|
|
_dwStatus = GetLastError();
|
|
return;
|
|
}
|
|
|
|
// BUGBUG - don't scan through all sessions.
|
|
// BUGBUG - abstract cred deletion.
|
|
// Flush all credentials if no session specified
|
|
// or only the credentials of the indicated session.
|
|
_pSessList->Seek();
|
|
while (pSessCur = (CSess*) _pSessList->GetNext())
|
|
{
|
|
if (pSess && (pSessCur != pSess))
|
|
continue;
|
|
|
|
CredList.Init(&pSessCur->dwCred);
|
|
while (pCred = (CCred*) CredList.GetNext())
|
|
{
|
|
// If a realm is specified, only delete
|
|
// credentials with that realm.
|
|
if (!szRealm || (!strcmp(szRealm, CCred::GetRealm(pCred))))
|
|
CCred::Free(_pMMFile, pSessCur, pCred);
|
|
}
|
|
}
|
|
|
|
// Release mutex.
|
|
Unlock();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CCredCache::GetStatus
|
|
//--------------------------------------------------------------------
|
|
DWORD CCredCache::GetStatus()
|
|
{
|
|
return _dwStatus;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|