#include "wininetp.h" static const CHAR szRegPathConnections[] = REGSTR_PATH_INTERNET_SETTINGS "\\Connections"; // some winsock stacks fault if we do a gethostbyname(NULL). If we come // accross one of these, don't do any more autodetecting. BOOL g_fGetHostByNameNULLFails = FALSE; // // IsConnectionMatch - a worker function to simply some logic elsewhere, // it just handles Connection Name Matching. // BOOL IsConnectionMatch( LPCSTR lpszConnection1, LPCSTR lpszConnection2) { if ( lpszConnection1 == NULL && lpszConnection2 == NULL) { return TRUE; } if ( lpszConnection1 && lpszConnection2 && stricmp(lpszConnection1, lpszConnection2) == 0 ) { return TRUE; } return FALSE; } /////////////////////////////////////////////////////////////////////////// // // CRegBlob implementation // /////////////////////////////////////////////////////////////////////////// CRegBlob::CRegBlob( BOOL fWrite ) { // initialize members _fWrite = fWrite; _fCommit = TRUE; _dwOffset = 0; _pBuffer = NULL; _dwBufferLimit = 0; _hkey = NULL; } CRegBlob::~CRegBlob( ) { Commit(); if(_hkey) REGCLOSEKEY(_hkey); if(_pBuffer) FREE_FIXED_MEMORY(_pBuffer); // caller owns _pszValue pointer } DWORD CRegBlob::Init( HKEY hBaseKey, LPCSTR pszSubKey, LPCSTR pszValue ) { long lRes; REGSAM regsam = KEY_QUERY_VALUE; DWORD dwDisposition; // If we're writing, save reg value name and set access if(_fWrite) { _pszValue = pszValue; regsam = KEY_SET_VALUE; lRes = REGCREATEKEYEX(hBaseKey, pszSubKey, 0, "", 0, regsam, NULL, &_hkey, &dwDisposition); } else { // If not writing, then use RegOpenKeyEx so we don't need // registry write permissions. lRes = REGOPENKEYEX(hBaseKey, pszSubKey, 0, regsam, &_hkey); } if(lRes != ERROR_SUCCESS) { return lRes; } // figure out buffer size _dwBufferLimit = BLOB_BUFF_GRANULARITY; if(FALSE == _fWrite) { // get size of registry blob lRes = RegQueryValueEx(_hkey, pszValue, NULL, NULL, NULL, &_dwBufferLimit); if(lRes != ERROR_SUCCESS) { // nothing there - make zero size buffer _dwBufferLimit = 0; } } // allocate buffer if necessary if(_dwBufferLimit) { _pBuffer = (BYTE *)ALLOCATE_FIXED_MEMORY(_dwBufferLimit); if(NULL == _pBuffer) return GetLastError(); } // if we're reading, fill in buffer if(FALSE == _fWrite && _dwBufferLimit) { // read reg key DWORD dwSize = _dwBufferLimit; lRes = RegQueryValueEx(_hkey, pszValue, NULL, NULL, _pBuffer, &dwSize); if(lRes != ERROR_SUCCESS) { return lRes; } } // reset pointer to beginning of blob _dwOffset = 0; return 0; } DWORD CRegBlob::Abandon( VOID ) { // don't commit changes when the time comes _fCommit = FALSE; return 0; } DWORD CRegBlob::Commit( ) { long lres = 0; if(_fCommit && _fWrite && _pszValue && _pBuffer) { // save blob to reg key lres = RegSetValueEx(_hkey, _pszValue, 0, REG_BINARY, _pBuffer, _dwOffset); } return lres; } DWORD CRegBlob::WriteString( LPCSTR pszString ) { DWORD dwBytes, dwLen = 0; if(pszString) { dwLen = lstrlen(pszString); } dwBytes = WriteBytes(&dwLen, sizeof(DWORD)); if(dwLen && dwBytes == sizeof(DWORD)) dwBytes = WriteBytes(pszString, dwLen); return dwBytes; } DWORD CRegBlob::ReadString( LPCSTR * ppszString ) { DWORD dwLen, dwBytes = 0; LPSTR lpszTemp = NULL; dwBytes = ReadBytes(&dwLen, sizeof(DWORD)); if(dwBytes == sizeof(DWORD)) { if(dwLen) { lpszTemp = (LPSTR)GlobalAlloc(GPTR, dwLen + 1); if(lpszTemp) { dwBytes = ReadBytes(lpszTemp, dwLen); lpszTemp[dwBytes] = 0; } } } *ppszString = lpszTemp; return dwBytes; } DWORD CRegBlob::WriteBytes( LPCVOID pBytes, DWORD dwByteCount ) { BYTE * pNewBuffer; // can only do this on write blob if(FALSE == _fWrite) return 0; // grow buffer if necessary if(_dwBufferLimit - _dwOffset < dwByteCount) { DWORD dw = _dwBufferLimit + ((dwByteCount / BLOB_BUFF_GRANULARITY)+1)*BLOB_BUFF_GRANULARITY; pNewBuffer = (BYTE *)ALLOCATE_FIXED_MEMORY(dw); if(NULL == pNewBuffer) { // failed to get more memory return 0; } memset(pNewBuffer, 0, dw); memcpy(pNewBuffer, _pBuffer, _dwBufferLimit); FREE_FIXED_MEMORY(_pBuffer); _pBuffer = pNewBuffer; _dwBufferLimit = dw; } // copy callers data to buffer memcpy(_pBuffer + _dwOffset, pBytes, dwByteCount); _dwOffset += dwByteCount; // tell caller how much we wrote return dwByteCount; } DWORD CRegBlob::ReadBytes( LPVOID pBytes, DWORD dwByteCount ) { DWORD dwActual = _dwBufferLimit - _dwOffset; // can only do this on read blob if(_fWrite) return 0; // don't read past end of blob if(dwByteCount < dwActual) dwActual = dwByteCount; // copy bytes and increment offset if(dwActual > 0) { memcpy(pBytes, _pBuffer + _dwOffset, dwActual); _dwOffset += dwActual; } // tell caller how much we actually read return dwActual; } // // Function Declarations // DWORD LoadProxySettings() /* ** Load global proxy info from registry. ** */ { DWORD error; // // Get proxy struct for proxy object // INTERNET_PROXY_INFO_EX info; memset(&info, 0, sizeof(info)); info.dwStructSize = sizeof(info); info.lpszConnectionName = NULL; // // Read LAN proxy settings and stuff them into the GlobalProxyInfo object. // error = ReadProxySettings(&info); if (error == ERROR_SUCCESS) { error = g_pGlobalProxyInfo->SetProxySettings(&info, FALSE); info.lpszConnectionName = NULL; // we don't allocate this field CleanProxyStruct(&info); } return error; } #define INTERNET_SETTINGS_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" HKEY FindBaseProxyKey( VOID ) /* ** Determine whether proxy settings live in HKLM or HKCU ** ** WinHttpX is hard-coded to always use HKEY_LOCAL_MACHINE ** */ { return HKEY_LOCAL_MACHINE; } DWORD ReadProxySettings( LPINTERNET_PROXY_INFO_EX pInfo ) { CRegBlob r(FALSE); LPCSTR pszConnectionName; LPCSTR pszSavedConnectionName; DWORD error = ERROR_SUCCESS; long lRes; HKEY hBaseKey; DEBUG_ENTER((DBG_PROXY, Dword, "ReadProxySettings", "%#x", pInfo )); // verify pInfo if(NULL == pInfo || pInfo->dwStructSize != sizeof(INTERNET_PROXY_INFO_EX)) { DEBUG_LEAVE(ERROR_INVALID_PARAMETER); return ERROR_INVALID_PARAMETER; } // figure out connection name (NULL == 'network') pszConnectionName = pInfo->lpszConnectionName; pszSavedConnectionName = pInfo->lpszConnectionName; if(NULL == pszConnectionName || 0 == *pszConnectionName) { pszConnectionName = "WinHttpSettings"; } // figure out base key hBaseKey = FindBaseProxyKey(); // initialize structure memset(pInfo, 0, sizeof(*pInfo)); pInfo->dwStructSize = sizeof(*pInfo); pInfo->lpszConnectionName = pszSavedConnectionName; pInfo->dwFlags = PROXY_TYPE_DIRECT; // init blob lRes = r.Init(hBaseKey, szRegPathConnections, pszConnectionName); if(lRes) { error = (lRes == ERROR_FILE_NOT_FOUND) ? ERROR_SUCCESS : lRes; goto quit; } // read fields from blob if(0 == r.ReadBytes(&pInfo->dwStructSize, sizeof(DWORD)) || (pInfo->dwStructSize < sizeof(*pInfo))) { // blob didn't exist or in correct format - set default values pInfo->dwStructSize = sizeof(*pInfo); } else { // read the rest of the blob r.ReadBytes(&pInfo->dwCurrentSettingsVersion, sizeof(DWORD)); r.ReadBytes(&pInfo->dwFlags, sizeof(DWORD)); r.ReadString(&pInfo->lpszProxy); r.ReadString(&pInfo->lpszProxyBypass); } // // WinHttpX does not support proxy autodection or autoconfig URL's, // so make sure those PROXY_TYPE flags are turned off. // pInfo->dwFlags &= ~(PROXY_TYPE_AUTO_DETECT | PROXY_TYPE_AUTO_PROXY_URL); DEBUG_PRINT(PROXY, INFO, ("conn=%s, vers=%u, flag=%X, prox=%s, by=%s\n", pszConnectionName, pInfo->dwCurrentSettingsVersion, pInfo->dwFlags, (pInfo->lpszProxy ? pInfo->lpszProxy : ""), (pInfo->lpszProxyBypass ? pInfo->lpszProxyBypass : "") )); quit: DEBUG_LEAVE(error); return error; } void CleanProxyStruct( LPINTERNET_PROXY_INFO_EX pInfo ) { if(pInfo->lpszConnectionName) GlobalFree((LPSTR) pInfo->lpszConnectionName); if(pInfo->lpszProxy) GlobalFree((LPSTR) pInfo->lpszProxy); if(pInfo->lpszProxyBypass) GlobalFree((LPSTR) pInfo->lpszProxyBypass); memset(pInfo, 0, sizeof(INTERNET_PROXY_INFO_EX)); pInfo->dwFlags = PROXY_TYPE_DIRECT; } DWORD WriteProxySettings(INTERNET_PROXY_INFO_EX * pInfo) { CRegBlob r(TRUE); DWORD error = ERROR_SUCCESS; long lRes; // verify pInfo if(NULL == pInfo || pInfo->dwStructSize != sizeof(INTERNET_PROXY_INFO_EX)) { return ERROR_INVALID_PARAMETER; } // init blob lRes = r.Init(HKEY_LOCAL_MACHINE, szRegPathConnections, "WinHttpSettings"); if (lRes) { error = lRes; goto quit; } if (r.WriteBytes(&pInfo->dwStructSize, sizeof(DWORD)) == 0 || r.WriteBytes(&pInfo->dwCurrentSettingsVersion, sizeof(DWORD)) == 0 || r.WriteBytes(&pInfo->dwFlags, sizeof(DWORD)) == 0 || r.WriteString(pInfo->lpszProxy) == 0 || r.WriteString(pInfo->lpszProxyBypass) == 0) { error = ERROR_NOT_ENOUGH_MEMORY; r.Abandon(); goto quit; } lRes = r.Commit(); if (lRes) { error = lRes; goto quit; } quit: return error; } // // support routines for WinHttpGetIEProxyConfigForCurrentUser API /////////// // // // // Maximum size of TOKEN_USER information. // #define SIZE_OF_TOKEN_INFORMATION \ sizeof( TOKEN_USER ) \ + sizeof( SID ) \ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES #define MAX_SID_STRING 256 // type for RtlConvertSidToUnicodeString, exported from ntdll.dll typedef NTSTATUS (* PCONVERTSID)( PUNICODE_STRING UnicodeString, PSID Sid, BOOLEAN AllocateDestinationString ); // // Function Declarations // BOOL InitClientUserString ( LPWSTR pString ) /*++ Routine Description: Arguments: pString - output string of current user Return Value: TRUE = success, FALSE = fail Returns in pString a ansi string if the impersonated client's SID can be expanded successfully into Unicode string. If the conversion was unsuccessful, returns FALSE. --*/ { DEBUG_ENTER((DBG_PROXY, Bool, "InitClientUserString", "%#x", pString )); HANDLE TokenHandle; UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ]; ULONG ReturnLength; BOOL Status; DWORD dwLastError; UNICODE_STRING UnicodeString; HMODULE hNtDll; PCONVERTSID pRtlConvertSid; // // get RtlConvertSideToUnicodeString entry point in NTDLL // hNtDll = LoadLibrary("ntdll.dll"); if(NULL == hNtDll) { return FALSE; } pRtlConvertSid = (PCONVERTSID)GetProcAddress(hNtDll, "RtlConvertSidToUnicodeString"); if(NULL == pRtlConvertSid) { FreeLibrary(hNtDll); DEBUG_LEAVE(FALSE); return FALSE; } // // We can use OpenThreadToken because this server thread // is impersonating a client // Status = OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, // Open as self &TokenHandle ); dwLastError = GetLastError(); if( Status == FALSE ) { DEBUG_PRINT(PROXY, INFO, ("OpenThreadToken() failed: Error=%d\n", dwLastError )); Status = OpenProcessToken( GetCurrentProcess(), TOKEN_READ, &TokenHandle ); dwLastError = GetLastError(); if( Status == FALSE ) { DEBUG_LEAVE(FALSE); return FALSE ; } } // // Notice that we've allocated enough space for the // TokenInformation structure. so if we fail, we // return a NULL pointer indicating failure // Status = GetTokenInformation( TokenHandle, TokenUser, TokenInformation, sizeof( TokenInformation ), &ReturnLength ); dwLastError = GetLastError(); CloseHandle( TokenHandle ); if ( Status == FALSE ) { DEBUG_PRINT(PROXY, INFO, ("GetTokenInformation failed: Error=%d\n", dwLastError )); DEBUG_LEAVE(FALSE); return FALSE; } // // Convert the Sid (pointed to by pSid) to its // equivalent Unicode string representation. // UnicodeString.Length = 0; UnicodeString.MaximumLength = MAX_SID_STRING; UnicodeString.Buffer = pString; Status = (*pRtlConvertSid)( &UnicodeString, ((PTOKEN_USER)TokenInformation)->User.Sid, FALSE ); FreeLibrary(hNtDll); if( !NT_SUCCESS( Status )){ DEBUG_PRINT(PROXY, INFO, ("RtlConvertSidToUnicodeString failed: Error=%d\n", Status )); DEBUG_LEAVE(FALSE); return FALSE; } DEBUG_PRINT(PROXY, INFO, ("User SID = %ws\n", pString )); DEBUG_LEAVE(TRUE); return TRUE; } HKEY GetClientUserHandle( IN REGSAM samDesired ) /*++ Routine Description: Arguments: Returns: ---*/ { DEBUG_ENTER((DBG_PROXY, Dword, "GetClientUserHandle", "%#x", samDesired )); HKEY hKeyClient; WCHAR String[MAX_SID_STRING]; LONG ReturnValue; if (!InitClientUserString(String)) { DEBUG_LEAVE(0); return NULL ; } // // We now have the Unicode string representation of the // local client's Sid we'll use this string to open a handle // to the client's key in the registry. ReturnValue = RegOpenKeyExW( HKEY_USERS, String, 0, samDesired, &hKeyClient ); // // If we couldn't get a handle to the local key // for some reason, return a NULL handle indicating // failure to obtain a handle to the key // if ( ReturnValue != ERROR_SUCCESS ) { DEBUG_PRINT(PROXY, INFO, ("RegOpenKeyW failed: Error=%d\n", ReturnValue )); DEBUG_ERROR(PROXY, ReturnValue); SetLastError( ReturnValue ); DEBUG_LEAVE(0); return NULL; } DEBUG_LEAVE(hKeyClient); return( hKeyClient ); } HKEY FindWinInetBaseProxyKey( VOID ) /* ** Determine whether proxy settings live in HKLM or HKCU ** ** Returns HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE ** ** Checks \HKLM\SW\MS\Win\CV\Internet Settings\ProxySettingsPerUser. If ** exists and is zero, use HKLM otherwise use HKCU. */ { HKEY hkeyBase = NULL; DWORD dwType, dwValue, dwSize = sizeof(DWORD); if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, INTERNET_POLICY_KEY, TEXT("ProxySettingsPerUser"), &dwType, &dwValue, &dwSize) && 0 == dwValue) { hkeyBase = HKEY_LOCAL_MACHINE; } else { // // Find an HKCU equivalent for this process // hkeyBase = GetClientUserHandle(KEY_QUERY_VALUE | KEY_SET_VALUE); if (!hkeyBase) { BOOL fLocalSystem = FALSE; if (GlobalIsProcessNtService) { char szUserName[32]; DWORD cbUserNameSize = sizeof(szUserName); if (GetUserName(szUserName, &cbUserNameSize)) { if (0 == lstrcmpi(szUserName, "SYSTEM") || 0 == lstrcmpi(szUserName, "LOCAL SERVICE") || 0 == lstrcmpi(szUserName, "NETWORK SERVICE")) { fLocalSystem = TRUE; } } } if (hkeyBase == NULL && !fLocalSystem) { hkeyBase = HKEY_CURRENT_USER; } } } return hkeyBase; } DWORD ReadWinInetProxySettings( LPWININET_PROXY_INFO_EX pInfo ) { CRegBlob r(FALSE); LPCSTR pszConnectionName; LPCSTR pszSavedConnectionName; DWORD error = ERROR_SUCCESS; long lRes; BOOL fLanConnection = FALSE; HKEY hBaseKey = NULL; DWORD dwStructVersion = 0; DEBUG_ENTER((DBG_PROXY, Dword, "ReadWinInetProxySettings", "%#x", pInfo )); // verify pInfo if (NULL == pInfo || pInfo->dwStructSize != sizeof(WININET_PROXY_INFO_EX)) { return ERROR_INVALID_PARAMETER; } // figure out connection name (NULL == 'network') pszConnectionName = pInfo->lpszConnectionName; pszSavedConnectionName = pInfo->lpszConnectionName; if(NULL == pszConnectionName || 0 == *pszConnectionName) { fLanConnection = TRUE; pszConnectionName = "DefaultConnectionSettings"; } // figure out base key hBaseKey = FindWinInetBaseProxyKey(); if (hBaseKey == NULL) { error = ERROR_FILE_NOT_FOUND; goto quit; } // initialize structure memset(pInfo, 0, sizeof(*pInfo)); pInfo->dwStructSize = sizeof(*pInfo); pInfo->lpszConnectionName = pszSavedConnectionName; pInfo->dwFlags = PROXY_TYPE_DIRECT; // init blob lRes = r.Init(hBaseKey, szRegPathConnections, pszConnectionName); if(lRes) { error = lRes; goto quit; } // read fields from blob if(0 == r.ReadBytes(&dwStructVersion, sizeof(DWORD)) || (dwStructVersion < WININET_PROXY_INFO_EX_VERSION)) { // blob didn't exist or in correct format - set default values // // All lan connections and overridden dial-ups get autodetect // if(fLanConnection) { pInfo->dwFlags |= PROXY_TYPE_AUTO_DETECT; } } else { // read the rest of the blob r.ReadBytes(&pInfo->dwCurrentSettingsVersion, sizeof(DWORD)); r.ReadBytes(&pInfo->dwFlags, sizeof(DWORD)); r.ReadString(&pInfo->lpszProxy); r.ReadString(&pInfo->lpszProxyBypass); r.ReadString(&pInfo->lpszAutoconfigUrl); r.ReadBytes(&pInfo->dwAutoDiscoveryFlags, sizeof(DWORD)); r.ReadString(&pInfo->lpszLastKnownGoodAutoConfigUrl); #if 0 // WinHttp does not need the rest of the WinInet proxy config data. /* r.ReadBytes(&pInfo->ftLastKnownDetectTime, sizeof(FILETIME)); // read interface ips r.ReadBytes(&pInfo->dwDetectedInterfaceIpCount, sizeof(DWORD)); if(pInfo->dwDetectedInterfaceIpCount) { pInfo->pdwDetectedInterfaceIp = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD) * pInfo->dwDetectedInterfaceIpCount); if(pInfo->pdwDetectedInterfaceIp) { for(i=0; idwDetectedInterfaceIpCount; i++) { r.ReadBytes(&pInfo->pdwDetectedInterfaceIp[i], sizeof(DWORD)); } } } r.ReadString(&pInfo->lpszAutoconfigSecondaryUrl); r.ReadBytes(&pInfo->dwAutoconfigReloadDelayMins, sizeof(DWORD)); */ #endif } DEBUG_PRINT(PROXY, INFO, ("conn=%s, vers=%u, flag=%X, prox=%s, by=%s, acu=%s\n", pszConnectionName, pInfo->dwCurrentSettingsVersion, pInfo->dwFlags, (pInfo->lpszProxy ? pInfo->lpszProxy : ""), (pInfo->lpszProxyBypass ? pInfo->lpszProxyBypass : ""), (pInfo->lpszAutoconfigUrl ? pInfo->lpszAutoconfigUrl : "") )); quit: if (hBaseKey != NULL && hBaseKey != INVALID_HANDLE_VALUE && hBaseKey != HKEY_LOCAL_MACHINE && hBaseKey != HKEY_CURRENT_USER) { RegCloseKey(hBaseKey); } DEBUG_LEAVE(error); return error; } void CleanWinInetProxyStruct( LPWININET_PROXY_INFO_EX pInfo ) { if(pInfo->lpszConnectionName) GlobalFree((LPSTR) pInfo->lpszConnectionName); if(pInfo->lpszProxy) GlobalFree((LPSTR) pInfo->lpszProxy); if(pInfo->lpszProxyBypass) GlobalFree((LPSTR) pInfo->lpszProxyBypass); if(pInfo->lpszAutoconfigUrl) GlobalFree((LPSTR) pInfo->lpszAutoconfigUrl); if(pInfo->lpszLastKnownGoodAutoConfigUrl) GlobalFree((LPSTR) pInfo->lpszLastKnownGoodAutoConfigUrl); if(pInfo->pdwDetectedInterfaceIp) GlobalFree(pInfo->pdwDetectedInterfaceIp); memset(pInfo, 0, sizeof(WININET_PROXY_INFO_EX)); pInfo->dwFlags = PROXY_TYPE_DIRECT; }