970 lines
26 KiB
C++
970 lines
26 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;
|
||
|
||
// 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, pInfo->szPass, 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:
|
||
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)
|
||
{
|
||
// 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), CCred::GetPass(pCred),
|
||
szNonce, szCNonce);
|
||
|
||
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)
|
||
{
|
||
// Create and return the found credential.
|
||
pInfo = new CCredInfo(szHost, CCred::GetRealm(pCred),
|
||
CCred::GetUser(pCred), CCred::GetPass(pCred),
|
||
szNonce, szCNonce);
|
||
|
||
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)
|
||
{
|
||
// Found a valid credential.
|
||
CCredInfo *pNew;
|
||
pNew = new CCredInfo(szHost, CCred::GetRealm(pCred),
|
||
CCred::GetUser(pCred), CCred::GetPass(pCred),
|
||
szNonce, szCNonce);
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|