2025-04-27 07:49:33 -04:00

390 lines
11 KiB
C++

#include "precomp.h"
#include <comdef.h>
#include <wininet.h>
#include "KeyCrypto.h"
PassportAlertInterface* g_pAlert = NULL;
PpNexusClient::PpNexusClient()
{
LocalConfigurationUpdated();
}
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() };
piStream->Seek(liZero, STREAM_SEEK_END, &liStreamSize);
GetHGlobalFromStream(piStream, &hStreamMem);
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);
}
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[2];
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
// Loop through the array...
for (UINT i = 0; i < sizeof(uiConnectionTypes); i++)
{
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;
hNexusFile = InternetOpenUrlA(
hNexusSession,
T2A(const_cast<TCHAR*>(strFullURL.c_str())),
T2A(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(), A2T(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;
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);
if(lpMsgBuf != NULL && *(LPTSTR)lpMsgBuf != TEXT('\0'))
{
lstrcat(achErrBuf, TEXT(" ("));
lstrcat(achErrBuf, (LPTSTR)lpMsgBuf);
lstrcat(achErrBuf, TEXT(") "));
}
if(achAfter[0])
{
lstrcat(achErrBuf, TEXT(" after a call to "));
lstrcat(achErrBuf, achAfter);
lstrcat(achErrBuf, TEXT("."));
}
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;
}
void
PpNexusClient::LocalConfigurationUpdated()
{
LONG lResult;
TCHAR achBuf[128];
DWORD dwBufLen;
DWORD dwSiteId;
tstring strCreds;
tstring strBase64Creds;
CRegKey NexusRegKey;
CRegKey PassportRegKey;
LPTSTR pszBase64CredBuf;
CKeyCrypto kc;
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, achBuf, 10);
m_strParam = TEXT("?id=");
m_strParam += achBuf;
lResult = NexusRegKey.Open(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Passport\\Nexus"),
KEY_READ);
if(lResult != ERROR_SUCCESS)
goto Cleanup;
dwBufLen = sizeof(achBuf);
lResult = NexusRegKey.QueryStringValue(TEXT("CCDUsername"),
achBuf,
&dwBufLen);
if(lResult != ERROR_SUCCESS)
goto Cleanup;
strCreds += achBuf;
strCreds += TEXT(":");
dwBufLen = sizeof(achBuf);
lResult = RegQueryValueEx(NexusRegKey, TEXT("CCDPassword"), NULL,
NULL, (LPBYTE)achBuf, &dwBufLen);
if(lResult != ERROR_SUCCESS)
goto Cleanup;
DATA_BLOB iBlob;
DATA_BLOB oBlob;
iBlob.cbData = dwBufLen;
iBlob.pbData = (PBYTE)achBuf;
ZeroMemory(&oBlob, sizeof(oBlob));
if (kc.decryptKey(&iBlob, &oBlob) != S_OK)
{
if(g_pAlert != NULL)
g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
PM_CANT_DECRYPT_CONFIG);
goto Cleanup;
}
strCreds += A2T((LPSTR)(oBlob.pbData));
pszBase64CredBuf = (LPTSTR)alloca(strCreds.size() * 2 * sizeof(TCHAR));
Base64Encode((LPBYTE)(T2A(const_cast<TCHAR*>(strCreds.c_str()))), strCreds.length(), pszBase64CredBuf);
m_strAuthHeader = TEXT("Authorization: Basic ");
m_strAuthHeader += pszBase64CredBuf;
Cleanup:
if(lResult != ERROR_SUCCESS)
{
//BUGBUG Throw an exception and an NT Event here.
}
if (oBlob.pbData)
::LocalFree(oBlob.pbData);
}