Windows2003-3790/inetcore/wininet/http/cookieprompt.cxx
2020-09-30 16:53:55 +02:00

624 lines
18 KiB
C++

#include <wininetp.h>
#include "cookieprompt.h"
// checks a string to see if its a domain
BOOL IsStringADomain( LPCSTR pszString)
{
int iLength = 0;
bool fLastCharWasDot = false;
while( pszString[iLength] != '\0')
{
if( fLastCharWasDot && pszString[iLength] == '.')
return FALSE;
fLastCharWasDot = pszString[iLength] == '.';
if( !(IsCharAlphaNumericA( pszString[iLength])
|| pszString[iLength] == '.'
|| pszString[iLength] == '-'))
{
return FALSE;
}
iLength++;
}
return iLength > 0 ? TRUE : FALSE;
}
LPCSTR FindMinimizedCookieDomainInDomain( LPCSTR pszDomain)
{
LPCSTR pMinimizedDomain = pszDomain + strlen( pszDomain);
do
{
pMinimizedDomain--;
while( pszDomain < pMinimizedDomain
&& *(pMinimizedDomain-1) != L'.')
{
pMinimizedDomain--;
}
} while( !IsDomainLegalCookieDomainA( pMinimizedDomain, pszDomain)
&& pszDomain < pMinimizedDomain);
return pMinimizedDomain;
}
CCookiePromptHistory::CCookiePromptHistory(const char *pchRegistryPath, bool fUseHKLM) {
_fUseHKLM = fUseHKLM;
lstrcpyn(_szRootKeyName, pchRegistryPath, sizeof(_szRootKeyName)/sizeof(_szRootKeyName[0]));
_hkHistoryRoot = NULL;
}
CCookiePromptHistory::~CCookiePromptHistory() {
if (_hkHistoryRoot)
{
RegCloseKey(_hkHistoryRoot);
_hkHistoryRoot = NULL;
}
}
HKEY CCookiePromptHistory::OpenRootKey()
{
HKEY hkey;
if (_hkHistoryRoot == NULL)
{
if (RegCreateKeyEx(_fUseHKLM ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
_szRootKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS,
NULL,
&hkey,
NULL) == ERROR_SUCCESS)
{
// if we aren't running in a service, we can cache the key
if (!GlobalIsProcessNtService)
{
if (InterlockedCompareExchangePointer((void**)&_hkHistoryRoot, (void*)hkey, NULL))
{
// someone beat us in the race to fill in _hkHistoryRoot, close ours since we
// failed to set it into _hkHistoryRoot
RegCloseKey(hkey);
hkey = _hkHistoryRoot;
}
}
}
else
{
hkey = NULL;
}
}
else
{
// use the cached value
hkey = _hkHistoryRoot;
}
return hkey;
}
BOOL CCookiePromptHistory::CloseRootKey(HKEY hkeyRoot)
{
BOOL bClosedKey = FALSE;
if (hkeyRoot)
{
if (GlobalIsProcessNtService)
{
// we never cache the key when runnint in a service!
INET_ASSERT(_hkHistoryRoot == NULL);
RegCloseKey(hkeyRoot);
bClosedKey = TRUE;
}
else
{
INET_ASSERT(_hkHistoryRoot == hkeyRoot);
}
}
return bClosedKey;
}
/*
Lookup user-decision for given host+policy combination.
If "pchPolicyID" is NULL the decision applies regardless of policy.
*/
BOOL CCookiePromptHistory::lookupDecision(const char *pchHostName,
const char *pchPolicyID,
unsigned long *pdwDecision) {
BOOL fRet = FALSE;
HKEY hSiteKey;
CHAR szBuffer[ INTERNET_MAX_URL_LENGTH];
DWORD dwBufferSize = INTERNET_MAX_URL_LENGTH;
if (SUCCEEDED( UrlUnescape( (LPSTR)pchHostName, szBuffer, &dwBufferSize, 0)) // forced LPSTR conv necessary because COULD be inplace unescape
&& IsStringADomain( szBuffer))
{
HKEY hkHistoryRoot = OpenRootKey();
if (hSiteKey = lookupSiteKey(hkHistoryRoot, FindMinimizedCookieDomainInDomain(szBuffer)))
{
DWORD dwType, dwCookieState;
DWORD dwSize = sizeof(dwCookieState);
if (ERROR_SUCCESS == RegQueryValueEx(hSiteKey, pchPolicyID, 0, &dwType, (LPBYTE) &dwCookieState, &dwSize)
&& (dwType==REG_DWORD)) {
*pdwDecision = dwCookieState;
fRet = TRUE;
}
RegCloseKey(hSiteKey);
}
CloseRootKey(hkHistoryRoot);
}
//commented code - legacy design where we allowed rules on a non-minimized domain
// while (pchHostName && !fRet)
// {
// if (hSiteKey=lookupSiteKey(pchHostName)) {
//
// DWORD dwType, dwCookieState;
// DWORD dwSize = sizeof(dwCookieState);
//
// if (ERROR_SUCCESS == RegQueryValueEx(hSiteKey, pchPolicyID, 0, &dwType, (LPBYTE) &dwCookieState, &dwSize)
// && (dwType==REG_DWORD)) {
//
// *pdwDecision = dwCookieState;
// fRet = TRUE;
// }
//
// RegCloseKey(hSiteKey);
// }
//
// /* Find and skip over next dot if there is one */
// if (pchHostName = strchr(pchHostName, '.'))
// pchHostName++;
// }
return fRet;
}
/*
Save user-decision for given host+policy combination.
If "pchPolicyID" is NULL the decision applies to all policies
*/
BOOL CCookiePromptHistory::saveDecision(const char *pchHostName,
const char *pszPolicyID,
unsigned long dwDecision) {
BOOL fRet = FALSE;
HKEY hSiteKey;
CHAR szBuffer[ INTERNET_MAX_URL_LENGTH];
DWORD dwBufferSize = INTERNET_MAX_URL_LENGTH;
if (SUCCEEDED( UrlUnescape( (LPSTR)pchHostName, szBuffer, &dwBufferSize, 0)) // forced LPSTR conv necessary because COULD be inplace unescape
&& IsStringADomain( szBuffer))
{
HKEY hkHistoryRoot = OpenRootKey();
if (hSiteKey = lookupSiteKey(hkHistoryRoot, FindMinimizedCookieDomainInDomain( szBuffer), true))
{
if (ERROR_SUCCESS == RegSetValueEx(hSiteKey, pszPolicyID, 0, REG_DWORD,
(LPBYTE) &dwDecision, sizeof(dwDecision)))
fRet = TRUE;
RegCloseKey(hSiteKey);
}
CloseRootKey(hkHistoryRoot);
}
return fRet;
}
/*
Clear previously saved decision for given hostname+policy combination.
If the policy-ID is "*" all decisions about the site are cleared.
*/
BOOL CCookiePromptHistory::clearDecision(const char *pchHostName, const char *pchPolicyID) {
BOOL fRet = FALSE;
int error = ERROR_SUCCESS;
HKEY hkHistoryRoot = OpenRootKey();
if ( pchPolicyID != NULL && !strcmp(pchPolicyID, "*")) {
error = SHDeleteKey(hkHistoryRoot, pchHostName);
}
else if (HKEY hSiteKey = lookupSiteKey(hkHistoryRoot, pchHostName, false)) {
error = RegDeleteValue(hSiteKey, pchPolicyID);
RegCloseKey(hSiteKey);
}
CloseRootKey(hkHistoryRoot);
/* If neither of the previous conditionals were TRUE, then there is
no decision corresponding to that hostname */
return (error==ERROR_SUCCESS);
}
HKEY CCookiePromptHistory::lookupSiteKey(HKEY hkHistoryRoot, const char *pchHostName, bool fCreate) {
HKEY hSiteKey = NULL;
if (hkHistoryRoot)
{
LONG error;
if (fCreate)
{
RegCreateKeyEx(hkHistoryRoot,
pchHostName,
0,
NULL,
0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL,
&hSiteKey,
NULL);
}
else
{
RegOpenKeyEx(hkHistoryRoot,
pchHostName,
0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
&hSiteKey);
}
}
return hSiteKey;
}
BOOL CCookiePromptHistory::clearAll() {
DWORD dwIndex = 0;
DWORD dwRet;
HKEY hkHistoryRoot = OpenRootKey();
do {
FILETIME ft;
char achHostName[INTERNET_MAX_HOST_NAME_LENGTH];
DWORD dwNameLen = sizeof(achHostName);
dwRet = RegEnumKeyEx(hkHistoryRoot,
dwIndex,
achHostName,
& dwNameLen,
NULL, NULL, NULL,
&ft);
if (dwRet == ERROR_SUCCESS)
{
if (SHDeleteKey(hkHistoryRoot, achHostName) != ERROR_SUCCESS)
{
dwIndex++;
}
}
}
while (dwRet == ERROR_SUCCESS);
CloseRootKey(hkHistoryRoot);
return TRUE;
}
unsigned long CCookiePromptHistory::enumerateDecisions(char *pchSiteName, unsigned long *pcbName,
unsigned long *pdwDecision,
unsigned long dwIndex) {
FILETIME ft;
HKEY hkHistoryRoot = OpenRootKey();
DWORD dwRet = RegEnumKeyEx(hkHistoryRoot, dwIndex, pchSiteName, pcbName, NULL, NULL, NULL, &ft);
if (dwRet==ERROR_SUCCESS) {
if (HKEY hSiteKey = lookupSiteKey(hkHistoryRoot, pchSiteName, false)) {
DWORD dwType;
DWORD dwSize = sizeof(DWORD);
dwRet = RegQueryValueEx(hSiteKey, NULL, 0, &dwType, (LPBYTE) pdwDecision, &dwSize);
RegCloseKey(hSiteKey);
}
else
{
dwRet = ERROR_NO_DATA;
}
}
CloseRootKey(hkHistoryRoot);
return dwRet;
}
/*
Exported APIs for manipulating per-site cookie settings
*/
extern CCookiePromptHistory cookieUIhistory;
BOOL DeletePersistentCookies(const char *pszDomainSuffix);
INTERNETAPI_(BOOL) InternetSetPerSiteCookieDecisionA( IN LPCSTR pchHostName, DWORD dwDecision)
{
BOOL retVal = FALSE;
if( !pchHostName
|| IsBadStringPtr(pchHostName, INTERNET_MAX_URL_LENGTH))
{
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
}
if( dwDecision == COOKIE_STATE_UNKNOWN)
{
retVal = cookieUIhistory.clearDecision( pchHostName, "*");
}
else if ( (dwDecision == COOKIE_STATE_ACCEPT) || (dwDecision == COOKIE_STATE_REJECT))
{
retVal = cookieUIhistory.saveDecision( pchHostName, NULL, dwDecision);
/* side-effect: choosing to reject all future cookies for a given website
implicitly deletes existing cache cookies from that site*/
if (dwDecision==COOKIE_STATE_REJECT)
DeletePersistentCookies(pchHostName);
}
return retVal;
}
INTERNETAPI_(BOOL) InternetSetPerSiteCookieDecisionW( IN LPCWSTR pwchHostName, DWORD dwDecision)
{
if( !pwchHostName || IsBadStringPtrW( pwchHostName, INTERNET_MAX_URL_LENGTH))
{
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
}
MEMORYPACKET mpHostName;
ALLOC_MB(pwchHostName,0,mpHostName);
if (!mpHostName.psStr)
{
return FALSE;
}
UNICODE_TO_ANSI(pwchHostName, mpHostName);
return InternetSetPerSiteCookieDecisionA( mpHostName.psStr, dwDecision);
}
INTERNETAPI_(BOOL) InternetGetPerSiteCookieDecisionA( IN LPCSTR pchHostName, unsigned long* pResult)
{
if( IsBadWritePtr( pResult, sizeof(unsigned long))
|| !pchHostName
|| IsBadStringPtr(pchHostName, INTERNET_MAX_URL_LENGTH))
{
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
}
return cookieUIhistory.lookupDecision( pchHostName, NULL, pResult);
}
INTERNETAPI_(BOOL) InternetGetPerSiteCookieDecisionW( IN LPCWSTR pwchHostName, unsigned long* pResult)
{
if( IsBadWritePtr( pResult, sizeof(unsigned long))
|| !pwchHostName
|| IsBadStringPtrW(pwchHostName, INTERNET_MAX_URL_LENGTH))
{
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
}
MEMORYPACKET mpHostName;
ALLOC_MB(pwchHostName,0,mpHostName);
if (!mpHostName.psStr)
{
return FALSE;
}
UNICODE_TO_ANSI(pwchHostName, mpHostName);
return InternetGetPerSiteCookieDecisionA( mpHostName.psStr, pResult);
}
INTERNETAPI_(BOOL) InternetEnumPerSiteCookieDecisionA(OUT LPSTR pszSiteName, IN OUT unsigned long *pcSiteNameSize, OUT unsigned long *pdwDecision, IN unsigned long dwIndex) {
int error = ERROR_INVALID_PARAMETER;
if( !pcSiteNameSize || IsBadWritePtr( pcSiteNameSize, sizeof(DWORD)))
{
goto doneInternetEnumPerSiteCookieDecisionA;
}
if( !pszSiteName || IsBadWritePtr( pszSiteName, *pcSiteNameSize))
{
goto doneInternetEnumPerSiteCookieDecisionA;
}
if( !pdwDecision || IsBadWritePtr(pdwDecision, sizeof(DWORD)))
{
goto doneInternetEnumPerSiteCookieDecisionA;
}
error = cookieUIhistory.enumerateDecisions(pszSiteName, pcSiteNameSize, pdwDecision, dwIndex);
if( error == ERROR_SUCCESS)
*pcSiteNameSize += 1; // reg function doesn't count null terminator in size, it should
doneInternetEnumPerSiteCookieDecisionA:
if (error!=ERROR_SUCCESS)
SetLastError(error);
return (error==ERROR_SUCCESS);
}
INTERNETAPI_(BOOL) InternetEnumPerSiteCookieDecisionW(LPWSTR pwszSiteName, unsigned long *pcSiteNameSize, unsigned long *pdwDecision, unsigned long dwIndex) {
DWORD dwErr = ERROR_INVALID_PARAMETER;
BOOL fRet = FALSE;
LPSTR pszSiteName = NULL;
if( !pcSiteNameSize || IsBadWritePtr( pcSiteNameSize, sizeof(DWORD)))
goto cleanup;
if( !pwszSiteName || IsBadWritePtr( pwszSiteName, *pcSiteNameSize * sizeof(WCHAR)))
goto cleanup;
if( !pdwDecision || IsBadWritePtr( pdwDecision, sizeof(DWORD)))
goto cleanup;
pszSiteName = new char[*pcSiteNameSize];
if( !pszSiteName)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
fRet = InternetEnumPerSiteCookieDecisionA( pszSiteName, pcSiteNameSize, pdwDecision, dwIndex);
if (fRet)
{
SHAnsiToUnicode( pszSiteName, pwszSiteName, *pcSiteNameSize);
}
dwErr = ERROR_SUCCESS;
cleanup:
if (dwErr!=ERROR_SUCCESS)
SetLastError(dwErr);
if ( pszSiteName != NULL)
delete [] pszSiteName;
return fRet;
}
INTERNETAPI_(BOOL) InternetClearAllPerSiteCookieDecisions()
{
return cookieUIhistory.clearAll();
}
BOOL DeletePersistentCookies(const char *pszDomainSuffix)
{
BOOL bRetval = TRUE;
DWORD dwEntrySize, dwLastEntrySize;
LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntry;
HANDLE hCacheDir = NULL;
dwEntrySize = dwLastEntrySize = MAX_CACHE_ENTRY_INFO_SIZE;
lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA) new BYTE[dwEntrySize];
if( lpCacheEntry == NULL)
{
bRetval = FALSE;
goto Exit;
}
lpCacheEntry->dwStructSize = dwEntrySize;
Again:
if (!(hCacheDir = FindFirstUrlCacheEntryA("cookie:",lpCacheEntry,&dwEntrySize)))
{
delete [] lpCacheEntry;
switch(GetLastError())
{
case ERROR_NO_MORE_ITEMS:
goto Exit;
case ERROR_INSUFFICIENT_BUFFER:
lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA)
new BYTE[dwEntrySize];
if( lpCacheEntry == NULL)
{
bRetval = FALSE;
goto Exit;
}
lpCacheEntry->dwStructSize = dwLastEntrySize = dwEntrySize;
goto Again;
default:
bRetval = FALSE;
goto Exit;
}
}
do
{
if (lpCacheEntry->CacheEntryType & COOKIE_CACHE_ENTRY) {
const char achEmpty[] = "";
const char *pszFind = NULL;
const char *pszAtSign = strchr(lpCacheEntry->lpszSourceUrlName, '@');
/*
The source URL for a cookie has the format:
cookie:username@domain/path
The logic for determining whether to delete the cookie checks for
the following conditions on source URL:
1. presence of the @ sign
2. presence of argument passed in "pszDomainSuffix" as substring
3. the substring must occur after a dot or the @ sign
(this avoids partial name matching on domains)
4. substring must occur as suffix, eg only at the end
this happens IFF the match is followed by forward slash
*/
if (pszDomainSuffix==NULL || /* null argument means "delete all cookies" */
(pszAtSign &&
(pszFind=strstr(pszAtSign+1, pszDomainSuffix)) &&
(pszFind[-1]=='.' || pszFind[-1]=='@') &&
pszFind[strlen(pszDomainSuffix)]=='/'))
DeleteUrlCacheEntryA(lpCacheEntry->lpszSourceUrlName);
}
dwEntrySize = dwLastEntrySize;
Retry:
if (!FindNextUrlCacheEntryA(hCacheDir,lpCacheEntry, &dwEntrySize))
{
delete [] lpCacheEntry;
switch(GetLastError())
{
case ERROR_NO_MORE_ITEMS:
goto Exit;
case ERROR_INSUFFICIENT_BUFFER:
lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA)
new BYTE[dwEntrySize];
if( lpCacheEntry == NULL)
{
bRetval = FALSE;
goto Exit;
}
lpCacheEntry->dwStructSize = dwLastEntrySize = dwEntrySize;
goto Retry;
default:
bRetval = FALSE;
goto Exit;
}
}
}
while (TRUE);
Exit:
if (hCacheDir)
FindCloseUrlCache(hCacheDir);
return bRetval;
}