Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

490 lines
15 KiB

/**********************************************************************/
/** 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);
}