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

  1. /**********************************************************************/
  2. /** Microsoft Passport **/
  3. /** Copyright(c) Microsoft Corporation, 1999 - 2001 **/
  4. /**********************************************************************/
  5. /*
  6. ppnexusclient.cpp
  7. implement the method for collection nexus settings, and fetch
  8. nexus database from internet
  9. FILE HISTORY:
  10. */
  11. #include "precomp.h"
  12. #include <comdef.h>
  13. #include <wininet.h>
  14. #include "BinHex.h"
  15. #include "KeyCrypto.h"
  16. #include "BstrDebug.h"
  17. PassportAlertInterface* g_pAlert = NULL;
  18. //===========================================================================
  19. //
  20. // PpNexusClient
  21. // -- load registry nexus settings
  22. //
  23. PpNexusClient::PpNexusClient()
  24. {
  25. LocalConfigurationUpdated();
  26. }
  27. //===========================================================================
  28. //
  29. // ReportBadDocument
  30. // -- Log event
  31. // -- called when there is problem parsing the CCD after fetch
  32. //
  33. void
  34. PpNexusClient::ReportBadDocument(
  35. tstring& strURL,
  36. IStream* piStream
  37. )
  38. {
  39. HGLOBAL hStreamMem;
  40. VOID* pStream;
  41. ULARGE_INTEGER liStreamSize;
  42. DWORD dwOutputSize;
  43. LARGE_INTEGER liZero = { 0, 0 };
  44. LPCTSTR apszErrors[] = { strURL.c_str() };
  45. HRESULT hr;
  46. piStream->Seek(liZero, STREAM_SEEK_END, &liStreamSize);
  47. hr = GetHGlobalFromStream(piStream, &hStreamMem);
  48. if (FAILED(hr))
  49. {
  50. return;
  51. }
  52. pStream = GlobalLock(hStreamMem);
  53. dwOutputSize = (80 < liStreamSize.LowPart) ? 80 : liStreamSize.LowPart;
  54. if(g_pAlert != NULL)
  55. {
  56. g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
  57. NEXUS_INVALIDDOCUMENT,
  58. 1,
  59. apszErrors,
  60. dwOutputSize,
  61. pStream);
  62. }
  63. GlobalUnlock(hStreamMem);
  64. }
  65. //===========================================================================
  66. //
  67. // FetchCCD
  68. // -- fetch a CCD e.g. partner.xml from passport nexus server using WinInet API
  69. // -- trying different approaches 1. direct, 2. proxy, 3. preconfig, 4. no autoproxy
  70. // -- use XMLDocument object to parse the fetched file
  71. //
  72. HRESULT
  73. PpNexusClient::FetchCCD(
  74. tstring& strURL,
  75. IXMLDocument** ppiXMLDocument
  76. )
  77. {
  78. HRESULT hr;
  79. HINTERNET hNexusSession = NULL, hNexusFile = NULL;
  80. DWORD dwBytesRead;
  81. DWORD dwStatusLen;
  82. DWORD dwStatus;
  83. tstring strAuthHeader;
  84. tstring strFullURL;
  85. CHAR achReadBuf[4096];
  86. TCHAR achAfter[64];
  87. LARGE_INTEGER liZero = { 0,0 };
  88. IStreamPtr xmlStream;
  89. IPersistStreamInitPtr xmlPSI;
  90. UINT uiConnectionTypes[4];
  91. USES_CONVERSION;
  92. achAfter[0] = 0;
  93. if(ppiXMLDocument == NULL)
  94. {
  95. hr = E_INVALIDARG;
  96. goto Cleanup;
  97. }
  98. *ppiXMLDocument = NULL;
  99. // This array will contains connection methods for WinInet in the order
  100. // we will attempt them. I am opting for this method instead of just trying
  101. // the PRECONFIG option as this will cause no change to existing customers who
  102. // have no problems so far.
  103. uiConnectionTypes[0] = INTERNET_OPEN_TYPE_DIRECT; //This was the original way of doing things
  104. uiConnectionTypes[1] = INTERNET_OPEN_TYPE_PRECONFIG; //This pulls proxy info from the registry
  105. uiConnectionTypes[2] = INTERNET_OPEN_TYPE_PROXY;
  106. uiConnectionTypes[3] = INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY;
  107. // Loop through the array...
  108. for (UINT i = 0; i < sizeof(uiConnectionTypes)/sizeof(UINT); i++)
  109. {
  110. if(hNexusSession)
  111. InternetCloseHandle(hNexusSession);
  112. hNexusSession = InternetOpenA(
  113. "Passport Nexus Client", //BUGBUG Should we just put in IE4's user agent?
  114. uiConnectionTypes[i], //Use the connection type
  115. NULL,
  116. NULL,
  117. 0);
  118. if(hNexusSession == NULL)
  119. {
  120. hr = GetLastError();
  121. lstrcpy(achAfter, TEXT("InternetOpen"));
  122. goto Cleanup;
  123. }
  124. // Get the document
  125. strFullURL = strURL;
  126. strFullURL += m_strParam;
  127. if(hNexusFile)
  128. InternetCloseHandle(hNexusFile);
  129. { // make it a local scope, the alloca will be freed
  130. hNexusFile = InternetOpenUrlA(
  131. hNexusSession,
  132. W2A(const_cast<TCHAR*>(strFullURL.c_str())),
  133. W2A(const_cast<TCHAR*>(m_strAuthHeader.c_str())),
  134. -1,
  135. INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE,
  136. 0);
  137. }
  138. // If the file was opened the we hop out of the loop and process it. If there is
  139. // and error, we keep looping. If there is an error on the last run through the loop,
  140. // it will be handled after the exit of the loop.
  141. if (hNexusFile != NULL)
  142. break;
  143. }
  144. // If hNexusFile is NULL when it exits the loop, we process that error.
  145. if(hNexusFile == NULL)
  146. {
  147. hr = GetLastError();
  148. if(hr == ERROR_INTERNET_SECURITY_CHANNEL_ERROR)
  149. {
  150. dwStatusLen = sizeof(HRESULT);
  151. InternetQueryOption(NULL, INTERNET_OPTION_EXTENDED_ERROR, &hr, &dwStatusLen);
  152. }
  153. lstrcpy(achAfter, TEXT("InternetOpenURL"));
  154. goto Cleanup;
  155. }
  156. // Check the status code.
  157. dwStatusLen = sizeof(DWORD);
  158. if(!HttpQueryInfoA(hNexusFile,
  159. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  160. &dwStatus,
  161. &dwStatusLen,
  162. NULL))
  163. {
  164. hr = GetLastError();
  165. lstrcpy(achAfter, TEXT("HttpQueryInfo"));
  166. goto Cleanup;
  167. }
  168. if(dwStatus != 200)
  169. {
  170. _ultoa(dwStatus, achReadBuf, 10);
  171. lstrcatA(achReadBuf, " ");
  172. dwStatusLen = sizeof(achReadBuf) - lstrlenA(achReadBuf);
  173. HttpQueryInfoA(hNexusFile,
  174. HTTP_QUERY_STATUS_TEXT,
  175. (LPTSTR)&(achReadBuf[lstrlenA(achReadBuf)]),
  176. &dwStatusLen,
  177. NULL);
  178. if(g_pAlert != NULL)
  179. {
  180. LPCTSTR apszStrings[] = { strURL.c_str(), A2W(achReadBuf) };
  181. g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
  182. NEXUS_ERRORSTATUS,
  183. 2,
  184. apszStrings,
  185. 0,
  186. NULL
  187. );
  188. }
  189. lstrcpy(achAfter, TEXT("InternetOpenURL"));
  190. hr = dwStatus;
  191. goto Cleanup;
  192. }
  193. hr = CreateStreamOnHGlobal(NULL, TRUE, &xmlStream);
  194. if(hr != S_OK)
  195. {
  196. lstrcpy(achAfter, TEXT("CreateStreamOnHGlobal"));
  197. goto Cleanup;
  198. }
  199. while(TRUE)
  200. {
  201. if(!InternetReadFile(hNexusFile, achReadBuf, sizeof(achReadBuf), &dwBytesRead))
  202. {
  203. hr = GetLastError();
  204. lstrcpy(achAfter, TEXT("InternetReadFile"));
  205. goto Cleanup;
  206. }
  207. if(dwBytesRead == 0)
  208. break;
  209. hr = xmlStream->Write(achReadBuf, dwBytesRead, NULL);
  210. if(hr != S_OK)
  211. {
  212. lstrcpy(achAfter, TEXT("IStream::Write"));
  213. goto Cleanup;
  214. }
  215. }
  216. hr = xmlStream->Seek(liZero, STREAM_SEEK_SET, NULL);
  217. if(hr != S_OK)
  218. {
  219. lstrcpy(achAfter, TEXT("IStream::Seek"));
  220. goto Cleanup;
  221. }
  222. //
  223. // Now create an XML object and initialize it using the stream.
  224. //
  225. hr = CoCreateInstance(__uuidof(XMLDocument), NULL, CLSCTX_ALL, IID_IPersistStreamInit, (void**)&xmlPSI);
  226. if(hr != S_OK)
  227. {
  228. lstrcpy(achAfter, TEXT("CoCreateInstance"));
  229. goto Cleanup;
  230. }
  231. hr = xmlPSI->Load((IStream*)xmlStream);
  232. if(hr != S_OK)
  233. {
  234. ReportBadDocument(strFullURL, xmlStream);
  235. lstrcpy(achAfter, TEXT("IPersistStreamInit::Load"));
  236. goto Cleanup;
  237. }
  238. hr = xmlPSI->QueryInterface(__uuidof(IXMLDocument), (void**)ppiXMLDocument);
  239. lstrcpy(achAfter, TEXT("QueryInterface(IID_IXMLDocument)"));
  240. Cleanup:
  241. //
  242. // Catch-all event for a fetch failure.
  243. //
  244. if(hr != S_OK && g_pAlert != NULL)
  245. {
  246. TCHAR achErrBuf[1024];
  247. LPCTSTR apszStrings[] = { strURL.c_str(), achErrBuf };
  248. LPVOID lpMsgBuf = NULL;
  249. ULONG cchTmp;
  250. FormatMessage(
  251. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  252. FORMAT_MESSAGE_FROM_SYSTEM |
  253. FORMAT_MESSAGE_IGNORE_INSERTS |
  254. FORMAT_MESSAGE_FROM_HMODULE |
  255. FORMAT_MESSAGE_MAX_WIDTH_MASK,
  256. GetModuleHandle(TEXT("wininet.dll")),
  257. hr,
  258. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  259. (LPTSTR) &lpMsgBuf,
  260. 0,
  261. NULL);
  262. lstrcpy(achErrBuf, TEXT("0x"));
  263. _ultot(hr, &(achErrBuf[2]), 16);
  264. achErrBuf[sizeof(achErrBuf) / sizeof(achErrBuf[0]) - 1] = TEXT('\0');
  265. if(lpMsgBuf != NULL && *(LPTSTR)lpMsgBuf != TEXT('\0'))
  266. {
  267. cchTmp = _tcslen(achErrBuf) + 1;
  268. _tcsncat(achErrBuf, TEXT(" ("), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp);
  269. _tcsncat(achErrBuf, (LPTSTR)lpMsgBuf, (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - (cchTmp + 2));
  270. cchTmp = _tcslen(achErrBuf) + 1;
  271. _tcsncat(achErrBuf, TEXT(") "), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp);
  272. }
  273. if(achAfter[0])
  274. {
  275. cchTmp = _tcslen(achErrBuf) + 1;
  276. _tcsncat(achErrBuf, TEXT(" after a call to "), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp);
  277. _tcsncat(achErrBuf, achAfter, (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - (cchTmp + 17));
  278. cchTmp = _tcslen(achErrBuf) + 1;
  279. _tcsncat(achErrBuf, TEXT("."), (sizeof(achErrBuf) / sizeof(achErrBuf[0])) - cchTmp);
  280. }
  281. g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
  282. NEXUS_FETCHFAILED,
  283. 2,
  284. apszStrings,
  285. 0,
  286. NULL
  287. );
  288. LocalFree(lpMsgBuf);
  289. }
  290. else if(g_pAlert != NULL)
  291. {
  292. // Emit success event.
  293. g_pAlert->report(PassportAlertInterface::INFORMATION_TYPE,
  294. NEXUS_FETCHSUCCEEDED,
  295. strURL.c_str());
  296. }
  297. if(hNexusFile != NULL)
  298. InternetCloseHandle(hNexusFile);
  299. if(hNexusSession != NULL)
  300. InternetCloseHandle(hNexusSession);
  301. return hr;
  302. }
  303. //===========================================================================
  304. //
  305. // LocalConfigurationUpdated
  306. // -- Sink for Local registry setting change notification
  307. // -- Load nexus settings from registry
  308. // -- it's called once at start up as well
  309. //
  310. void
  311. PpNexusClient::LocalConfigurationUpdated()
  312. {
  313. LONG lResult;
  314. TCHAR rgchUsername[128];
  315. TCHAR rgchPassword[128];
  316. DWORD dwBufLen;
  317. DWORD dwSiteId;
  318. CRegKey NexusRegKey;
  319. CRegKey PassportRegKey;
  320. BSTR bstrEncodedCreds = NULL;
  321. CKeyCrypto kc;
  322. CBinHex bh;
  323. DATA_BLOB iBlob;
  324. DATA_BLOB oBlob = {0};
  325. LONG cCreds;
  326. LPSTR pszCreds = NULL;
  327. USES_CONVERSION;
  328. lResult = PassportRegKey.Open(HKEY_LOCAL_MACHINE,
  329. TEXT("Software\\Microsoft\\Passport"),
  330. KEY_READ);
  331. if(lResult != ERROR_SUCCESS)
  332. goto Cleanup;
  333. lResult = PassportRegKey.QueryDWORDValue(TEXT("SiteId"),
  334. dwSiteId);
  335. if(lResult != ERROR_SUCCESS)
  336. goto Cleanup;
  337. _ultot(dwSiteId, rgchUsername, 10);
  338. m_strParam = TEXT("?id=");
  339. m_strParam += rgchUsername;
  340. lResult = NexusRegKey.Open(HKEY_LOCAL_MACHINE,
  341. TEXT("Software\\Microsoft\\Passport\\Nexus"),
  342. KEY_READ);
  343. if(lResult != ERROR_SUCCESS)
  344. goto Cleanup;
  345. dwBufLen = sizeof(rgchUsername)/sizeof(rgchUsername[0]);
  346. lResult = NexusRegKey.QueryStringValue(TEXT("CCDUsername"),
  347. rgchUsername,
  348. &dwBufLen);
  349. if(lResult != ERROR_SUCCESS)
  350. goto Cleanup;
  351. dwBufLen = sizeof(rgchPassword);
  352. lResult = RegQueryValueEx(NexusRegKey, TEXT("CCDPassword"), NULL,
  353. NULL, (LPBYTE)rgchPassword, &dwBufLen);
  354. if(lResult != ERROR_SUCCESS)
  355. goto Cleanup;
  356. iBlob.cbData = dwBufLen;
  357. iBlob.pbData = (PBYTE)rgchPassword;
  358. if (kc.decryptKey(&iBlob, &oBlob) != S_OK)
  359. {
  360. if(g_pAlert != NULL)
  361. g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
  362. PM_CANT_DECRYPT_CONFIG);
  363. goto Cleanup;
  364. }
  365. //
  366. // convert the CCD username to multi byte and then concatenate the password to the result
  367. // need enough memory for username + ':' + password + NULL char
  368. cCreds = ((wcslen(rgchUsername) + 1) * 2) + 1 + oBlob.cbData;
  369. pszCreds = (LPSTR)LocalAlloc(LMEM_FIXED, cCreds);
  370. if (NULL == pszCreds)
  371. {
  372. goto Cleanup;
  373. }
  374. if (0 == WideCharToMultiByte(CP_ACP, 0, rgchUsername, -1, pszCreds, (wcslen(rgchUsername) + 1) * 2, NULL, NULL))
  375. {
  376. goto Cleanup;
  377. }
  378. cCreds = strlen(pszCreds);
  379. pszCreds[cCreds] = ':';
  380. CopyMemory(pszCreds + cCreds + 1, oBlob.pbData, oBlob.cbData);
  381. pszCreds[cCreds + 1 + oBlob.cbData] = '\0';
  382. // base 64 encode the password, so it may be used with the HTML request
  383. if (S_OK != bh.ToBase64(pszCreds,
  384. strlen(pszCreds),
  385. NULL, //prepend - this is used by CCoCrypto, not needed here
  386. NULL, //IV - this is used by CCoCrypto, not needed here
  387. &bstrEncodedCreds))
  388. {
  389. if(g_pAlert != NULL)
  390. g_pAlert->report(PassportAlertInterface::ERROR_TYPE,
  391. PM_CANT_DECRYPT_CONFIG);
  392. goto Cleanup;
  393. }
  394. m_strAuthHeader = TEXT("Authorization: Basic ");
  395. m_strAuthHeader += bstrEncodedCreds;
  396. Cleanup:
  397. if (NULL != pszCreds)
  398. {
  399. LocalFree(pszCreds);
  400. }
  401. if(lResult != ERROR_SUCCESS)
  402. {
  403. //BUGBUG Throw an exception and an NT Event here.
  404. }
  405. if (NULL != bstrEncodedCreds)
  406. {
  407. FREE_BSTR(bstrEncodedCreds);
  408. }
  409. if (oBlob.pbData)
  410. ::LocalFree(oBlob.pbData);
  411. }