|
|
/**********************************************************************/ /** Microsoft Passport **/ /** Copyright(c) Microsoft Corporation, 1999 - 2001 **/ /**********************************************************************/
/*
ppnexusclient.cpp implement the method for collection nexus settings, and fetch nexus database from internet
FILE HISTORY:
*/
#include "precomp.h"
#include <comdef.h>
#include <wininet.h>
#include "BinHex.h"
#include "KeyCrypto.h"
#include "BstrDebug.h"
PassportAlertInterface* g_pAlert = NULL;
//===========================================================================
//
// PpNexusClient
// -- load registry nexus settings
//
PpNexusClient::PpNexusClient() { LocalConfigurationUpdated(); }
//===========================================================================
//
// ReportBadDocument
// -- Log event
// -- called when there is problem parsing the CCD after fetch
//
void PpNexusClient::ReportBadDocument( tstring& strURL, IStream* piStream ) { HGLOBAL hStreamMem; VOID* pStream; ULARGE_INTEGER liStreamSize; DWORD dwOutputSize; LARGE_INTEGER liZero = { 0, 0 }; LPCTSTR apszErrors[] = { strURL.c_str() }; HRESULT hr;
piStream->Seek(liZero, STREAM_SEEK_END, &liStreamSize);
hr = GetHGlobalFromStream(piStream, &hStreamMem);
if (FAILED(hr)) { return; }
pStream = GlobalLock(hStreamMem);
dwOutputSize = (80 < liStreamSize.LowPart) ? 80 : liStreamSize.LowPart;
if(g_pAlert != NULL) { g_pAlert->report(PassportAlertInterface::ERROR_TYPE, NEXUS_INVALIDDOCUMENT, 1, apszErrors, dwOutputSize, pStream); }
GlobalUnlock(hStreamMem); }
//===========================================================================
//
// FetchCCD
// -- fetch a CCD e.g. partner.xml from passport nexus server using WinInet API
// -- trying different approaches 1. direct, 2. proxy, 3. preconfig, 4. no autoproxy
// -- use XMLDocument object to parse the fetched file
//
HRESULT PpNexusClient::FetchCCD( tstring& strURL, IXMLDocument** ppiXMLDocument ) { HRESULT hr; HINTERNET hNexusSession = NULL, hNexusFile = NULL; DWORD dwBytesRead; DWORD dwStatusLen; DWORD dwStatus; tstring strAuthHeader; tstring strFullURL; CHAR achReadBuf[4096]; TCHAR achAfter[64]; LARGE_INTEGER liZero = { 0,0 }; IStreamPtr xmlStream; IPersistStreamInitPtr xmlPSI; UINT uiConnectionTypes[4]; USES_CONVERSION;
achAfter[0] = 0;
if(ppiXMLDocument == NULL) { hr = E_INVALIDARG; goto Cleanup; }
*ppiXMLDocument = NULL;
// This array will contains connection methods for WinInet in the order
// we will attempt them. I am opting for this method instead of just trying
// the PRECONFIG option as this will cause no change to existing customers who
// have no problems so far.
uiConnectionTypes[0] = INTERNET_OPEN_TYPE_DIRECT; //This was the original way of doing things
uiConnectionTypes[1] = INTERNET_OPEN_TYPE_PRECONFIG; //This pulls proxy info from the registry
uiConnectionTypes[2] = INTERNET_OPEN_TYPE_PROXY; uiConnectionTypes[3] = INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY;
// Loop through the array...
for (UINT i = 0; i < sizeof(uiConnectionTypes)/sizeof(UINT); i++) { if(hNexusSession) InternetCloseHandle(hNexusSession);
hNexusSession = InternetOpenA( "Passport Nexus Client", //BUGBUG Should we just put in IE4's user agent?
uiConnectionTypes[i], //Use the connection type
NULL, NULL, 0); if(hNexusSession == NULL) { hr = GetLastError(); lstrcpy(achAfter, TEXT("InternetOpen")); goto Cleanup; }
// Get the document
strFullURL = strURL; strFullURL += m_strParam;
if(hNexusFile) InternetCloseHandle(hNexusFile);
{ // make it a local scope, the alloca will be freed
hNexusFile = InternetOpenUrlA( hNexusSession, W2A(const_cast<TCHAR*>(strFullURL.c_str())), W2A(const_cast<TCHAR*>(m_strAuthHeader.c_str())), -1, INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0); } // If the file was opened the we hop out of the loop and process it. If there is
// and error, we keep looping. If there is an error on the last run through the loop,
// it will be handled after the exit of the loop.
if (hNexusFile != NULL) break; }
// If hNexusFile is NULL when it exits the loop, we process that error.
if(hNexusFile == NULL) { hr = GetLastError(); if(hr == ERROR_INTERNET_SECURITY_CHANNEL_ERROR) { dwStatusLen = sizeof(HRESULT); InternetQueryOption(NULL, INTERNET_OPTION_EXTENDED_ERROR, &hr, &dwStatusLen); }
lstrcpy(achAfter, TEXT("InternetOpenURL")); goto Cleanup; } // Check the status code.
dwStatusLen = sizeof(DWORD); if(!HttpQueryInfoA(hNexusFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwStatusLen, NULL)) { hr = GetLastError(); lstrcpy(achAfter, TEXT("HttpQueryInfo")); goto Cleanup; }
if(dwStatus != 200) { _ultoa(dwStatus, achReadBuf, 10); lstrcatA(achReadBuf, " ");
dwStatusLen = sizeof(achReadBuf) - lstrlenA(achReadBuf); HttpQueryInfoA(hNexusFile, HTTP_QUERY_STATUS_TEXT, (LPTSTR)&(achReadBuf[lstrlenA(achReadBuf)]), &dwStatusLen, NULL);
if(g_pAlert != NULL) { LPCTSTR apszStrings[] = { strURL.c_str(), A2W(achReadBuf) };
g_pAlert->report(PassportAlertInterface::ERROR_TYPE, NEXUS_ERRORSTATUS, 2, apszStrings, 0, NULL ); }
lstrcpy(achAfter, TEXT("InternetOpenURL")); hr = dwStatus; goto Cleanup; }
hr = CreateStreamOnHGlobal(NULL, TRUE, &xmlStream); if(hr != S_OK) { lstrcpy(achAfter, TEXT("CreateStreamOnHGlobal")); goto Cleanup; }
while(TRUE) { if(!InternetReadFile(hNexusFile, achReadBuf, sizeof(achReadBuf), &dwBytesRead)) { hr = GetLastError(); lstrcpy(achAfter, TEXT("InternetReadFile")); goto Cleanup; }
if(dwBytesRead == 0) break;
hr = xmlStream->Write(achReadBuf, dwBytesRead, NULL); if(hr != S_OK) { lstrcpy(achAfter, TEXT("IStream::Write")); goto Cleanup; } }
hr = xmlStream->Seek(liZero, STREAM_SEEK_SET, NULL); if(hr != S_OK) { lstrcpy(achAfter, TEXT("IStream::Seek")); goto Cleanup; }
//
// Now create an XML object and initialize it using the stream.
//
hr = CoCreateInstance(__uuidof(XMLDocument), NULL, CLSCTX_ALL, IID_IPersistStreamInit, (void**)&xmlPSI); if(hr != S_OK) { lstrcpy(achAfter, TEXT("CoCreateInstance")); goto Cleanup; }
hr = xmlPSI->Load((IStream*)xmlStream); if(hr != S_OK) { ReportBadDocument(strFullURL, xmlStream); lstrcpy(achAfter, TEXT("IPersistStreamInit::Load")); goto Cleanup; }
hr = xmlPSI->QueryInterface(__uuidof(IXMLDocument), (void**)ppiXMLDocument); lstrcpy(achAfter, TEXT("QueryInterface(IID_IXMLDocument)"));
Cleanup:
//
// Catch-all event for a fetch failure.
//
if(hr != S_OK && g_pAlert != NULL) { TCHAR achErrBuf[1024]; LPCTSTR apszStrings[] = { strURL.c_str(), achErrBuf }; LPVOID lpMsgBuf = NULL; ULONG cchTmp;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_MAX_WIDTH_MASK, GetModuleHandle(TEXT("wininet.dll")), hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL);
lstrcpy(achErrBuf, TEXT("0x")); _ultot(hr, &(achErrBuf[2]), 16); achErrBuf[sizeof(achErrBuf) / sizeof(achErrBuf[0]) - 1] = TEXT('\0'); if(lpMsgBuf != NULL && *(LPTSTR)lpMsgBuf != TEXT('\0')) { cchTmp = _tcslen(achErrBuf) + 1; _tcsncat(achErrBuf, TEXT(" ("), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp); _tcsncat(achErrBuf, (LPTSTR)lpMsgBuf, (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - (cchTmp + 2)); cchTmp = _tcslen(achErrBuf) + 1; _tcsncat(achErrBuf, TEXT(") "), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp); }
if(achAfter[0]) { cchTmp = _tcslen(achErrBuf) + 1; _tcsncat(achErrBuf, TEXT(" after a call to "), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp); _tcsncat(achErrBuf, achAfter, (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - (cchTmp + 17)); cchTmp = _tcslen(achErrBuf) + 1; _tcsncat(achErrBuf, TEXT("."), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp); }
g_pAlert->report(PassportAlertInterface::ERROR_TYPE, NEXUS_FETCHFAILED, 2, apszStrings, 0, NULL );
LocalFree(lpMsgBuf); } else if(g_pAlert != NULL) { // Emit success event.
g_pAlert->report(PassportAlertInterface::INFORMATION_TYPE, NEXUS_FETCHSUCCEEDED, strURL.c_str()); }
if(hNexusFile != NULL) InternetCloseHandle(hNexusFile); if(hNexusSession != NULL) InternetCloseHandle(hNexusSession);
return hr; }
//===========================================================================
//
// LocalConfigurationUpdated
// -- Sink for Local registry setting change notification
// -- Load nexus settings from registry
// -- it's called once at start up as well
//
void PpNexusClient::LocalConfigurationUpdated() { LONG lResult; TCHAR rgchUsername[128]; TCHAR rgchPassword[128]; DWORD dwBufLen; DWORD dwSiteId; CRegKey NexusRegKey; CRegKey PassportRegKey; BSTR bstrEncodedCreds = NULL; CKeyCrypto kc; CBinHex bh; DATA_BLOB iBlob; DATA_BLOB oBlob = {0}; LONG cCreds; LPSTR pszCreds = NULL;
USES_CONVERSION;
lResult = PassportRegKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Passport"), KEY_READ); if(lResult != ERROR_SUCCESS) goto Cleanup;
lResult = PassportRegKey.QueryDWORDValue(TEXT("SiteId"), dwSiteId); if(lResult != ERROR_SUCCESS) goto Cleanup;
_ultot(dwSiteId, rgchUsername, 10); m_strParam = TEXT("?id="); m_strParam += rgchUsername;
lResult = NexusRegKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Passport\\Nexus"), KEY_READ); if(lResult != ERROR_SUCCESS) goto Cleanup;
dwBufLen = sizeof(rgchUsername)/sizeof(rgchUsername[0]); lResult = NexusRegKey.QueryStringValue(TEXT("CCDUsername"), rgchUsername, &dwBufLen); if(lResult != ERROR_SUCCESS) goto Cleanup;
dwBufLen = sizeof(rgchPassword); lResult = RegQueryValueEx(NexusRegKey, TEXT("CCDPassword"), NULL, NULL, (LPBYTE)rgchPassword, &dwBufLen); if(lResult != ERROR_SUCCESS) goto Cleanup;
iBlob.cbData = dwBufLen; iBlob.pbData = (PBYTE)rgchPassword;
if (kc.decryptKey(&iBlob, &oBlob) != S_OK) { if(g_pAlert != NULL) g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_CANT_DECRYPT_CONFIG);
goto Cleanup; }
//
// convert the CCD username to multi byte and then concatenate the password to the result
// need enough memory for username + ':' + password + NULL char
cCreds = ((wcslen(rgchUsername) + 1) * 2) + 1 + oBlob.cbData; pszCreds = (LPSTR)LocalAlloc(LMEM_FIXED, cCreds); if (NULL == pszCreds) { goto Cleanup; }
if (0 == WideCharToMultiByte(CP_ACP, 0, rgchUsername, -1, pszCreds, (wcslen(rgchUsername) + 1) * 2, NULL, NULL)) { goto Cleanup; } cCreds = strlen(pszCreds); pszCreds[cCreds] = ':'; CopyMemory(pszCreds + cCreds + 1, oBlob.pbData, oBlob.cbData); pszCreds[cCreds + 1 + oBlob.cbData] = '\0';
// base 64 encode the password, so it may be used with the HTML request
if (S_OK != bh.ToBase64(pszCreds, strlen(pszCreds), NULL, //prepend - this is used by CCoCrypto, not needed here
NULL, //IV - this is used by CCoCrypto, not needed here
&bstrEncodedCreds)) { if(g_pAlert != NULL) g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_CANT_DECRYPT_CONFIG); goto Cleanup; }
m_strAuthHeader = TEXT("Authorization: Basic "); m_strAuthHeader += bstrEncodedCreds;
Cleanup: if (NULL != pszCreds) { LocalFree(pszCreds); }
if(lResult != ERROR_SUCCESS) { //BUGBUG Throw an exception and an NT Event here.
}
if (NULL != bstrEncodedCreds) { FREE_BSTR(bstrEncodedCreds); }
if (oBlob.pbData) ::LocalFree(oBlob.pbData); }
|