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.

1390 lines
39 KiB

  1. #include <wininetp.h>
  2. #include <urlmon.h>
  3. #include <splugin.hxx>
  4. #include "htuu.h"
  5. #include "md5.h"
  6. /*---------------------------------------------------------------------------
  7. PASSPORT_CTX
  8. ---------------------------------------------------------------------------*/
  9. PWC *PWC_Create // PWC constructor
  10. (
  11. LPSTR lpszHost, // Host Name to place in structure.
  12. DWORD nPort, // destination port of proxy or server
  13. LPSTR lpszUrl, // URL to template, and place in the structure.
  14. LPSTR lpszRealm, // Realm string to add.
  15. AUTHCTX::SPMData * pSPM
  16. );
  17. VOID PWC_Free(PWC *pwc);
  18. /*---------------------------------------------------------------------------
  19. Constructor
  20. ---------------------------------------------------------------------------*/
  21. PASSPORT_CTX::PASSPORT_CTX(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy,
  22. SPMData* pSPM, PWC* pPWC)
  23. : AUTHCTX(pSPM, pPWC)
  24. {
  25. _fIsProxy = fIsProxy;
  26. _pRequest = pRequest;
  27. m_hLogon = NULL;
  28. m_pNewThreadInfo = NULL;
  29. m_pwszPartnerInfo = NULL;
  30. m_wRealm[0] = '\0';
  31. m_pszFromPP = NULL;
  32. m_hPP = 0;
  33. m_lpszRetUrl = NULL;
  34. // m_fAuthDeferred = FALSE;
  35. //m_fCredsBad = FALSE;
  36. m_fPreauthFailed = FALSE;
  37. m_pCredTimestamp = NULL;
  38. m_fAnonymous = TRUE;
  39. _pPWC = PWC_Create(NULL, 0, NULL, NULL, NULL);
  40. m_hBitmap = NULL;
  41. m_pwszCbText = NULL;
  42. m_dwCbTextLen = 0;
  43. m_pwszReqUserName = NULL;
  44. m_dwReqUserNameLen = 0;
  45. ::MultiByteToWideChar(CP_ACP, 0, _pRequest->GetServerName(), -1, m_wTarget, MAX_AUTH_TARGET_LEN);
  46. }
  47. BOOL PASSPORT_CTX::Init(void)
  48. {
  49. m_pNewThreadInfo = ::InternetCreateThreadInfo(FALSE);
  50. if (m_pNewThreadInfo == NULL)
  51. {
  52. return FALSE;
  53. }
  54. return TRUE;
  55. }
  56. /*---------------------------------------------------------------------------
  57. Destructor
  58. ---------------------------------------------------------------------------*/
  59. PASSPORT_CTX::~PASSPORT_CTX()
  60. {
  61. LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
  62. m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
  63. ::InternetSetThreadInfo(m_pNewThreadInfo);
  64. if (m_hLogon)
  65. {
  66. ::PP_FreeLogonContext(m_hLogon);
  67. m_hLogon = NULL;
  68. }
  69. if (m_hPP)
  70. {
  71. ::PP_FreeContext(m_hPP);
  72. }
  73. ::InternetSetThreadInfo(pCurrentThreadInfo);
  74. if (m_pNewThreadInfo)
  75. {
  76. ::InternetFreeThreadInfo(m_pNewThreadInfo);
  77. }
  78. if (m_pwszPartnerInfo)
  79. {
  80. delete [] m_pwszPartnerInfo;
  81. }
  82. if (m_lpszRetUrl)
  83. {
  84. delete [] m_lpszRetUrl;
  85. }
  86. if (m_pszFromPP)
  87. {
  88. delete [] m_pszFromPP;
  89. }
  90. if (m_pwszCbText)
  91. delete[] m_pwszCbText;
  92. if (m_pwszReqUserName)
  93. delete[] m_pwszReqUserName;
  94. PWC_Free(_pPWC);
  95. _pPWC = NULL;
  96. }
  97. BOOL PASSPORT_CTX::CallbackRegistered(void)
  98. {
  99. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  100. if (lpThreadInfo)
  101. {
  102. DWORD_PTR context;
  103. context = _InternetGetContext(lpThreadInfo);
  104. if (context == INTERNET_NO_CALLBACK)
  105. {
  106. context = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetContext();
  107. }
  108. INTERNET_STATUS_CALLBACK appCallback =
  109. ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetStatusCallback();
  110. if ((appCallback != NULL) && (context != INTERNET_NO_CALLBACK))
  111. {
  112. return TRUE;
  113. }
  114. }
  115. return FALSE;
  116. }
  117. CHAR g_szPassportDAHost[256];
  118. DWORD PASSPORT_CTX::HandleSuccessfulLogon(
  119. LPWSTR* ppwszFromPP,
  120. PDWORD pdwFromPP,
  121. BOOL fPreAuth
  122. )
  123. {
  124. // biaow-todo: I am betting the RU DWORD UrlLength = 1024;
  125. LPWSTR pwszUrl = (LPWSTR) ALLOCATE_FIXED_MEMORY(1024 * sizeof(WCHAR));
  126. DWORD dwwUrlLength = 1024;// won't be too long, but I could be wrong
  127. LPSTR pszUrl = (LPSTR) ALLOCATE_FIXED_MEMORY(dwwUrlLength * sizeof(CHAR));
  128. BOOL fRetrySameUrl;
  129. DWORD dwRet = ERROR_SUCCESS;
  130. if (pwszUrl == NULL || pszUrl == NULL)
  131. {
  132. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  133. goto exit;
  134. }
  135. *pdwFromPP = 0;
  136. if (::PP_GetAuthorizationInfo(m_hLogon,
  137. NULL,
  138. pdwFromPP,
  139. &fRetrySameUrl,
  140. pwszUrl,
  141. &dwwUrlLength
  142. ) == FALSE)
  143. {
  144. *ppwszFromPP = new WCHAR[*pdwFromPP];
  145. if (*ppwszFromPP == NULL)
  146. {
  147. dwRet = ERROR_INTERNET_LOGIN_FAILURE;
  148. goto exit;
  149. }
  150. }
  151. else
  152. {
  153. INET_ASSERT(TRUE); // this shouldn't happen
  154. }
  155. if (::PP_GetAuthorizationInfo(m_hLogon,
  156. *ppwszFromPP,
  157. pdwFromPP,
  158. &fRetrySameUrl,
  159. pwszUrl,
  160. &dwwUrlLength
  161. ) == FALSE)
  162. {
  163. INET_ASSERT(TRUE); // this shouldn't happen
  164. dwRet = ERROR_INTERNET_LOGIN_FAILURE;
  165. goto exit;
  166. }
  167. WCHAR wszDAHost[256];
  168. DWORD dwHostLen = ARRAY_ELEMENTS(wszDAHost);
  169. if (::PP_GetLogonHost(m_hLogon, wszDAHost, &dwHostLen) == TRUE)
  170. {
  171. ::WideCharToMultiByte(CP_ACP, 0, wszDAHost, -1, g_szPassportDAHost, 256, NULL, NULL);
  172. }
  173. if (!fRetrySameUrl)
  174. {
  175. if (_pRequest->GetMethodType() == HTTP_METHOD_TYPE_GET)
  176. {
  177. // DA wanted us to GET to a new Url
  178. ::WideCharToMultiByte(CP_ACP, 0, pwszUrl, -1, pszUrl, 1024, NULL, NULL);
  179. }
  180. else
  181. {
  182. fRetrySameUrl = TRUE; // *** WinInet supports retry custom verb to same URL only ***
  183. }
  184. }
  185. if (fPreAuth)
  186. {
  187. if (fRetrySameUrl)
  188. {
  189. // Here we are sending and the DA told us to keep Verb & Url,
  190. // so there is no more needs to be done
  191. goto exit;
  192. }
  193. // we are sending. Regardless whether we are asked to handle redirect, we'll need to fake
  194. // that a 302 just came in.
  195. // biaow-todo: this is causing problem for QueryHeaders(StatusCode). I don't know why yet...
  196. /*
  197. _pRequest->AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
  198. "HTTP/1.0 302 Object Moved",
  199. strlen("HTTP/1.0 302 Object Moved")
  200. );
  201. _pRequest->AddInternalResponseHeader(HTTP_QUERY_LOCATION,
  202. pszUrl,
  203. strlen(pszUrl));
  204. */
  205. if (_pRequest->GetOpenFlags() & INTERNET_FLAG_NO_AUTO_REDIRECT)
  206. {
  207. if (!CallbackRegistered())
  208. {
  209. _pRequest->SetPPAbort(TRUE);
  210. dwRet = ERROR_INTERNET_LOGIN_FAILURE;
  211. goto exit;
  212. }
  213. }
  214. ::InternetIndicateStatusString(INTERNET_STATUS_REDIRECT, pszUrl);
  215. }
  216. else
  217. {
  218. if (!fRetrySameUrl)
  219. {
  220. if (_pRequest->GetOpenFlags() & INTERNET_FLAG_NO_AUTO_REDIRECT)
  221. {
  222. if (!CallbackRegistered())
  223. {
  224. dwRet = ERROR_INTERNET_LOGIN_FAILURE;
  225. goto exit;
  226. }
  227. ::InternetIndicateStatusString(INTERNET_STATUS_REDIRECT, pszUrl);
  228. }
  229. }
  230. }
  231. PCSTR lpszRetUrl = NULL;
  232. lpszRetUrl = fRetrySameUrl ? _pRequest->GetURL() : pszUrl;
  233. if (m_lpszRetUrl)
  234. {
  235. delete [] m_lpszRetUrl;
  236. }
  237. m_lpszRetUrl = new CHAR[strlen(lpszRetUrl) + 1];
  238. if (m_lpszRetUrl)
  239. {
  240. strcpy(m_lpszRetUrl, lpszRetUrl);
  241. }
  242. exit:
  243. if (pwszUrl)
  244. {
  245. FREE_MEMORY(pwszUrl);
  246. }
  247. if (pszUrl)
  248. {
  249. FREE_MEMORY(pszUrl);
  250. }
  251. return dwRet;
  252. }
  253. DWORD PASSPORT_CTX::SetCreds(BOOL* pfCredSet)
  254. {
  255. DWORD dwError = ERROR_SUCCESS;
  256. BOOL fUseDefaultCreds;
  257. LPWSTR pwszUser = NULL;
  258. LPWSTR pwszPass = NULL;
  259. LPSTR pszPass = NULL;
  260. AuthLock();
  261. if (_pPWC->lpszUser && _pPWC->lpszPass && (pszPass = _pPWC->GetPass()))
  262. {
  263. if (_pPWC->lpszUser[0] || (pszPass && pszPass[0])) // either user pass is not blank -> don't use default creds
  264. {
  265. fUseDefaultCreds = FALSE;
  266. }
  267. else // both user and pass are blank -> use default creds
  268. {
  269. fUseDefaultCreds = TRUE;
  270. }
  271. }
  272. else
  273. {
  274. fUseDefaultCreds = TRUE;
  275. }
  276. if (!fUseDefaultCreds)
  277. {
  278. pwszUser = (LPWSTR) ALLOCATE_FIXED_MEMORY((strlen(_pPWC->lpszUser) + 1) * sizeof(WCHAR));
  279. pwszPass = (LPWSTR) ALLOCATE_FIXED_MEMORY((strlen(pszPass) + 1) * sizeof(WCHAR));
  280. if (pwszUser && pwszPass)
  281. {
  282. ::MultiByteToWideChar(CP_ACP, 0, _pPWC->lpszUser, -1, pwszUser, strlen(_pPWC->lpszUser) + 1);
  283. ::MultiByteToWideChar(CP_ACP, 0, pszPass, -1, pwszPass, strlen(pszPass) + 1);
  284. }
  285. else
  286. {
  287. dwError = ERROR_NOT_ENOUGH_MEMORY;
  288. }
  289. INET_ASSERT(m_pCredTimestamp != NULL);
  290. }
  291. AuthUnlock();
  292. if (dwError == ERROR_SUCCESS)
  293. {
  294. *pfCredSet = ::PP_SetCredentials(m_hLogon, m_wRealm, m_wTarget, pwszUser, pwszPass, m_pCredTimestamp);
  295. }
  296. if (pwszUser)
  297. FREE_MEMORY(pwszUser);
  298. if (pwszPass)
  299. {
  300. SecureZeroMemory(pwszPass, wcslen(pwszPass) * sizeof(WCHAR));
  301. FREE_MEMORY(pwszPass);
  302. }
  303. if (pszPass)
  304. {
  305. SecureZeroMemory(pszPass, strlen(pszPass));
  306. FREE_MEMORY(pszPass);
  307. }
  308. return dwError;
  309. }
  310. DWORD PASSPORT_CTX::ModifyRequestBasedOnRU(void)
  311. {
  312. DWORD dwError = ERROR_SUCCESS;
  313. INTERNET_SCHEME schemeType;
  314. INTERNET_SCHEME currentSchemeType;
  315. INTERNET_PORT currentHostPort;
  316. LPSTR currentHostName;
  317. DWORD currentHostNameLength;
  318. INTERNET_PORT port = 0;
  319. LPSTR pszHostName;
  320. DWORD dwHostNameLength = 0;
  321. LPSTR pszUrlPath;
  322. DWORD dwUrlPathLength = 0;
  323. LPSTR extra;
  324. DWORD extraLength;
  325. dwError = CrackUrl(m_lpszRetUrl,
  326. 0,
  327. FALSE, // don't escape URL-path
  328. &schemeType,
  329. NULL, // scheme name, don't care
  330. NULL,
  331. &pszHostName,
  332. &dwHostNameLength,
  333. &port,
  334. NULL, // UserName, don't care
  335. NULL,
  336. NULL, // Password, don't care
  337. NULL,
  338. &pszUrlPath,
  339. &dwUrlPathLength,
  340. &extra,
  341. &extraLength,
  342. NULL);
  343. if (dwError != ERROR_SUCCESS)
  344. {
  345. goto cleanup;
  346. }
  347. //
  348. // if there is an intra-page link on the redirected URL then get rid of it:
  349. // we don't send it to the server, and we have already indicated it to the
  350. // app
  351. //
  352. if (extraLength != 0) {
  353. INET_ASSERT(extra != NULL);
  354. INET_ASSERT(!IsBadWritePtr(extra, 1));
  355. if (*extra == '#') {
  356. *extra = '\0';
  357. // newUrlLength -= extraLength;
  358. } else {
  359. dwUrlPathLength += extraLength;
  360. }
  361. }
  362. if (port == INTERNET_INVALID_PORT_NUMBER) {
  363. port = (schemeType == INTERNET_SCHEME_HTTPS)
  364. ? INTERNET_DEFAULT_HTTPS_PORT
  365. : INTERNET_DEFAULT_HTTP_PORT;
  366. }
  367. currentHostPort = _pRequest->GetHostPort();
  368. currentHostName = _pRequest->GetHostName(&currentHostNameLength);
  369. if (port != currentHostPort) {
  370. _pRequest->SetHostPort(port);
  371. }
  372. if ((dwHostNameLength != currentHostNameLength)
  373. || (strnicmp(pszHostName, currentHostName, dwHostNameLength) != 0)) {
  374. char hostValue[INTERNET_MAX_HOST_NAME_LENGTH + sizeof(":4294967295")];
  375. LPSTR hostValueStr;
  376. DWORD hostValueSize;
  377. CHAR chBkChar = pszHostName[dwHostNameLength]; // save off char
  378. pszHostName[dwHostNameLength] = '\0';
  379. _pRequest->SetHostName(pszHostName);
  380. hostValueSize = dwHostNameLength;
  381. hostValueStr = pszHostName;
  382. if ((port != INTERNET_DEFAULT_HTTP_PORT)
  383. && (port != INTERNET_DEFAULT_HTTPS_PORT)) {
  384. if (hostValueSize > INTERNET_MAX_HOST_NAME_LENGTH)
  385. {
  386. pszHostName[dwHostNameLength] = chBkChar; // put back char
  387. dwError = ERROR_INVALID_PARAMETER;
  388. goto cleanup;
  389. }
  390. hostValueSize = wsprintf(hostValue, "%s:%d", pszHostName, (port & 0xffff));
  391. hostValueStr = hostValue;
  392. }
  393. pszHostName[dwHostNameLength] = chBkChar; // put back char
  394. //
  395. // replace the "Host:" header
  396. //
  397. _pRequest->ReplaceRequestHeader(HTTP_QUERY_HOST,
  398. hostValueStr,
  399. hostValueSize,
  400. 0, // dwIndex
  401. ADD_HEADER
  402. );
  403. //
  404. // and get the corresponding server info, resolving the name if
  405. // required
  406. //
  407. _pRequest->SetServerInfo(FALSE);
  408. //
  409. // Since we are redirecting to a different host, force an update of the origin
  410. // server. Otherwise, we will still pick up the proxy info of the first server.
  411. //
  412. _pRequest->SetOriginServer(TRUE);
  413. }
  414. currentSchemeType = ((INTERNET_FLAG_SECURE & _pRequest->GetOpenFlags()) ?
  415. INTERNET_SCHEME_HTTPS :
  416. INTERNET_SCHEME_HTTP);
  417. if ( currentSchemeType != schemeType )
  418. {
  419. DWORD OpenFlags = _pRequest->GetOpenFlags();
  420. // Switched From HTTPS to HTTP
  421. if ( currentSchemeType == INTERNET_SCHEME_HTTPS )
  422. {
  423. INET_ASSERT(schemeType != INTERNET_SCHEME_HTTPS );
  424. OpenFlags &= ~(INTERNET_FLAG_SECURE);
  425. }
  426. // Switched From HTTP to HTTPS
  427. else if ( schemeType == INTERNET_SCHEME_HTTPS )
  428. {
  429. INET_ASSERT(currentSchemeType == INTERNET_SCHEME_HTTP );
  430. OpenFlags |= (INTERNET_FLAG_SECURE);
  431. }
  432. _pRequest->SetOpenFlags(OpenFlags);
  433. _pRequest->SetSchemeType(schemeType);
  434. }
  435. _pRequest->SetURL(m_lpszRetUrl);
  436. if (_pRequest->IsRequestUsingProxy())
  437. {
  438. _pRequest->ModifyRequest(_pRequest->GetMethodType(),
  439. m_lpszRetUrl,
  440. strlen(m_lpszRetUrl),
  441. NULL,
  442. 0);
  443. }
  444. else
  445. {
  446. _pRequest->ModifyRequest(_pRequest->GetMethodType(),
  447. pszUrlPath, // m_lpszRetUrl,
  448. strlen(pszUrlPath),//strlen(m_lpszRetUrl),
  449. NULL,
  450. 0);
  451. }
  452. cleanup:
  453. return dwError;
  454. }
  455. /*---------------------------------------------------------------------------
  456. PreAuthUser
  457. ---------------------------------------------------------------------------*/
  458. DWORD PASSPORT_CTX::PreAuthUser(IN LPSTR pBuf, IN OUT LPDWORD pcbBuf)
  459. {
  460. DEBUG_ENTER ((
  461. DBG_HTTPAUTH,
  462. Dword,
  463. "PASSPORT_CTX::PreAuthUser",
  464. "this=%#x pBuf=%#x pcbBuf=%#x {%d}",
  465. this,
  466. pBuf,
  467. pcbBuf,
  468. *pcbBuf
  469. ));
  470. DWORD dwError = ERROR_SUCCESS;
  471. LPWSTR pwszFromPP = NULL;
  472. BOOL bGetCbText;
  473. // Prefix the header value with the auth type.
  474. const static BYTE szPassport[] = "Passport1.4 ";
  475. #define PASSPORT_LEN sizeof(szPassport)-1
  476. if (m_pszFromPP == NULL)
  477. {
  478. if (m_hLogon == NULL)
  479. {
  480. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  481. goto cleanup;
  482. }
  483. DWORD dwFromPPLen = 0;
  484. BOOL fCredSet;
  485. dwError = SetCreds(&fCredSet);
  486. if (dwError != ERROR_SUCCESS)
  487. {
  488. goto cleanup;
  489. }
  490. LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
  491. m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
  492. ::InternetSetThreadInfo(m_pNewThreadInfo);
  493. DWORD dwLogonStatus = ::PP_Logon(m_hLogon,
  494. FALSE,
  495. 0,
  496. NULL,
  497. 0);
  498. ::InternetSetThreadInfo(pCurrentThreadInfo);
  499. IndicatePrivacyEvents();
  500. if (dwLogonStatus != PP_LOGON_SUCCESS)
  501. {
  502. if (dwLogonStatus == PP_LOGON_REQUIRED)
  503. {
  504. m_hBitmap = NULL;
  505. if (m_pwszReqUserName)
  506. {
  507. delete[] m_pwszReqUserName;
  508. m_pwszReqUserName = NULL;
  509. }
  510. m_dwReqUserNameLen = 0;
  511. bGetCbText = (m_pwszCbText == NULL);
  512. //Get the size of CbText and UserName;
  513. ::PP_GetChallengeInfo(m_hLogon,
  514. NULL, NULL, NULL, (bGetCbText? &m_dwCbTextLen : NULL), NULL, 0,
  515. NULL, &m_dwReqUserNameLen);
  516. if (bGetCbText)
  517. m_pwszCbText = new WCHAR[m_dwCbTextLen + 1];
  518. m_pwszReqUserName = new WCHAR[m_dwReqUserNameLen + 1];
  519. if ((bGetCbText && !m_pwszCbText) || !m_pwszReqUserName)
  520. {
  521. dwError = ERROR_NOT_ENOUGH_MEMORY;
  522. goto cleanup;
  523. }
  524. ::PP_GetChallengeInfo(m_hLogon,
  525. &m_hBitmap, NULL, (bGetCbText ? m_pwszCbText : NULL), (bGetCbText ? &m_dwCbTextLen : NULL),
  526. m_wRealm, MAX_AUTH_REALM_LEN, m_pwszReqUserName, &m_dwReqUserNameLen);
  527. }
  528. else if (dwLogonStatus == PP_LOGON_FAILED)
  529. {
  530. m_fPreauthFailed = TRUE;
  531. }
  532. dwError = ERROR_INTERNET_INTERNAL_ERROR; // need to double check this return error
  533. // m_fCredsBad = TRUE;
  534. goto cleanup;
  535. }
  536. dwError = HandleSuccessfulLogon(&pwszFromPP, &dwFromPPLen, TRUE);
  537. if (dwError == ERROR_INTERNET_LOGIN_FAILURE)
  538. {
  539. goto cleanup;
  540. }
  541. m_pszFromPP = new CHAR [dwFromPPLen];
  542. if (m_pszFromPP == NULL)
  543. {
  544. dwError = ERROR_NOT_ENOUGH_MEMORY;
  545. goto cleanup;
  546. }
  547. ::WideCharToMultiByte(CP_ACP, 0, pwszFromPP, -1, m_pszFromPP, dwFromPPLen, NULL, NULL);
  548. }
  549. // check to see if we need to update url
  550. if (m_lpszRetUrl)
  551. {
  552. dwError = ModifyRequestBasedOnRU();
  553. if (dwError != ERROR_SUCCESS)
  554. {
  555. delete [] m_lpszRetUrl;
  556. m_lpszRetUrl = NULL;
  557. goto cleanup;
  558. }
  559. delete [] m_lpszRetUrl;
  560. m_lpszRetUrl = NULL;
  561. }
  562. // Ticket and profile is already present
  563. // put in the header
  564. memcpy (pBuf, szPassport, PASSPORT_LEN);
  565. pBuf += PASSPORT_LEN;
  566. // append the ticket
  567. strcpy(pBuf, m_pszFromPP);
  568. *pcbBuf = (DWORD)(PASSPORT_LEN + strlen(m_pszFromPP));
  569. cleanup:
  570. if (pwszFromPP)
  571. delete [] pwszFromPP;
  572. DEBUG_LEAVE(dwError);
  573. return dwError;
  574. }
  575. BOOL PPEscapeUrl(LPCSTR lpszStringIn,
  576. LPSTR lpszStringOut,
  577. DWORD* pdwStrLen,
  578. DWORD dwMaxLength,
  579. DWORD dwFlags);
  580. /*---------------------------------------------------------------------------
  581. UpdateFromHeaders
  582. ---------------------------------------------------------------------------*/
  583. DWORD PASSPORT_CTX::UpdateFromHeaders(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy)
  584. {
  585. DEBUG_ENTER ((
  586. DBG_HTTPAUTH,
  587. Dword,
  588. "PASSPORT_CTX::UpdateFromHeaders",
  589. "this=%#x request=%#x isproxy=%B",
  590. this,
  591. pRequest,
  592. fIsProxy
  593. ));
  594. DWORD dwAuthIdx, cbChallenge, dwError;
  595. LPSTR szChallenge = NULL;
  596. LPINTERNET_THREAD_INFO pCurrentThreadInfo = NULL;
  597. LPINTERNET_THREAD_INFO pNewThreadInfo = NULL;
  598. // Get the associated header.
  599. if ((dwError = FindHdrIdxFromScheme(&dwAuthIdx)) != ERROR_SUCCESS)
  600. goto exit;
  601. // Get the complete auth header.
  602. dwError = GetAuthHeaderData(pRequest, fIsProxy, NULL,
  603. &szChallenge, &cbChallenge, ALLOCATE_BUFFER, dwAuthIdx);
  604. if (dwError != ERROR_SUCCESS)
  605. {
  606. szChallenge = NULL;
  607. goto exit;
  608. }
  609. if (m_pwszPartnerInfo)
  610. {
  611. delete [] m_pwszPartnerInfo;
  612. }
  613. {
  614. LPSTR lpszVerb;
  615. DWORD dwVerbLength;
  616. lpszVerb = _pRequest->_RequestHeaders.GetVerb(&dwVerbLength);
  617. #define MAX_VERB_LENGTH 16
  618. CHAR szOrgVerb[MAX_VERB_LENGTH] = {0};
  619. if (dwVerbLength > MAX_VERB_LENGTH - 1)
  620. {
  621. goto exit;
  622. }
  623. strncpy(szOrgVerb, lpszVerb, dwVerbLength+1);
  624. // HTTP_METHOD_TYPE tOrgMethod = _pRequest->GetMethodType();
  625. // PCSTR pszOrgVerb;
  626. // ::MapHttpMethodType(tOrgMethod, &pszOrgVerb);
  627. PCSTR pszOrgUrl = _pRequest->GetURL();
  628. /*
  629. DWORD dwEscUrlLen = strlen(pszOrgUrl) * 3 + 1;
  630. DWORD dwEscUrlLenOut;
  631. PSTR pszEscapedUrl = new CHAR[dwEscUrlLen]; // should be long enough
  632. if (pszEscapedUrl == NULL)
  633. {
  634. dwError = ERROR_NOT_ENOUGH_MEMORY;
  635. goto exit;
  636. }
  637. PPEscapeUrl(pszOrgUrl, pszEscapedUrl, &dwEscUrlLenOut, dwEscUrlLen, 0);
  638. EncodeUrlPath(NO_ENCODE_PATH_SEP,
  639. SCHEME_HTTP,
  640. (PSTR)pszOrgUrl,
  641. strlen(pszOrgUrl),
  642. pszEscapedUrl,
  643. &dwEscUrlLen);
  644. */
  645. const LPWSTR pwszOrgVerbAttr = L",OrgVerb=";
  646. const LPWSTR pwszOrgUrlAttr = L",OrgUrl=";
  647. DWORD dwPartnerInfoLength = cbChallenge
  648. +::wcslen(pwszOrgVerbAttr)
  649. +::strlen(szOrgVerb)
  650. +::wcslen(pwszOrgUrlAttr)
  651. +::strlen(pszOrgUrl)
  652. + 1; // NULL terminator
  653. DWORD dwSize = 0;
  654. PWSTR pwszPartnerInfo = NULL;
  655. m_pwszPartnerInfo = new WCHAR[dwPartnerInfoLength];
  656. if (m_pwszPartnerInfo == NULL)
  657. {
  658. dwError = ERROR_NOT_ENOUGH_MEMORY;
  659. // delete [] pszEscapedUrl;
  660. goto exit;
  661. }
  662. pwszPartnerInfo = m_pwszPartnerInfo;
  663. dwSize = ::MultiByteToWideChar(CP_ACP, 0, szChallenge, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
  664. ::wcscat(pwszPartnerInfo, pwszOrgVerbAttr);
  665. pwszPartnerInfo += (dwSize + wcslen(pwszOrgVerbAttr));
  666. dwPartnerInfoLength -= (dwSize + wcslen(pwszOrgVerbAttr));
  667. dwSize = ::MultiByteToWideChar(CP_ACP, 0, szOrgVerb, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
  668. ::wcscat(pwszPartnerInfo, pwszOrgUrlAttr);
  669. pwszPartnerInfo += (dwSize + wcslen(pwszOrgUrlAttr));
  670. dwPartnerInfoLength -= (dwSize + wcslen(pwszOrgUrlAttr));
  671. dwSize = ::MultiByteToWideChar(CP_ACP, 0, pszOrgUrl, -1, pwszPartnerInfo, dwPartnerInfoLength) - 1;
  672. // delete [] pszEscapedUrl;
  673. dwError = ERROR_SUCCESS;
  674. }
  675. exit:
  676. if (szChallenge)
  677. delete []szChallenge;
  678. DEBUG_LEAVE(dwError);
  679. return dwError;
  680. }
  681. BOOL PASSPORT_CTX::InitLogonContext(void)
  682. {
  683. PP_CONTEXT hPP = 0;
  684. LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
  685. m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
  686. ::InternetSetThreadInfo(m_pNewThreadInfo);
  687. if (!m_hPP)
  688. {
  689. hPP = ::PP_InitContext(L"WinInet.Dll", NULL); // the Passport package does not support Async yet, so we'll have to
  690. m_hPP = hPP; // create a new Passport Session here.
  691. PCWSTR pwszRealm = ::wcsstr(m_pwszPartnerInfo, L"srealm");
  692. if (pwszRealm)
  693. {
  694. pwszRealm += ::wcslen(L"srealm");
  695. if (*pwszRealm == L'=')
  696. {
  697. pwszRealm++;
  698. DWORD i = 0;
  699. while (*pwszRealm != 0 && *pwszRealm != L',' && i < MAX_AUTH_REALM_LEN-1)
  700. {
  701. m_wRealm[i++] = *pwszRealm++;
  702. }
  703. m_wRealm[i] = 0; // null-terminate it
  704. }
  705. }
  706. if (!m_wRealm[0])
  707. {
  708. DWORD dwRealmLen = MAX_AUTH_REALM_LEN;
  709. PP_GetRealm(hPP, m_wRealm, &dwRealmLen);
  710. }
  711. }
  712. if (!m_hLogon)
  713. {
  714. m_hLogon = ::PP_InitLogonContext(
  715. hPP,
  716. m_pwszPartnerInfo,
  717. (_pRequest->GetOpenFlags() & INTERNET_FLAG_NO_COOKIES)
  718. );
  719. }
  720. ::InternetSetThreadInfo(pCurrentThreadInfo);
  721. return (m_hLogon != NULL);
  722. }
  723. /*---------------------------------------------------------------------------
  724. PostAuthUser
  725. ---------------------------------------------------------------------------*/
  726. DWORD PASSPORT_CTX::PostAuthUser()
  727. {
  728. DEBUG_ENTER ((
  729. DBG_HTTPAUTH,
  730. Dword,
  731. "PASSPORT_CTX::PostAuthUser",
  732. "this=%#x",
  733. this
  734. ));
  735. DWORD dwRet;
  736. BOOL bGetCbText;
  737. InitLogonContext();
  738. if (m_fPreauthFailed)
  739. {
  740. m_fPreauthFailed = FALSE; // reset the flag
  741. _pRequest->SetStatusCode(401);
  742. Transfer401ContentFromPP();
  743. dwRet = ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY;
  744. DEBUG_LEAVE(dwRet);
  745. return dwRet;
  746. }
  747. if (m_hLogon == NULL)
  748. {
  749. return ERROR_INTERNET_INTERNAL_ERROR;
  750. }
  751. /*
  752. if (_pPWC->lpszUser && _pPWC->lpszPass)
  753. {
  754. if (!m_fAuthDeferred)
  755. {
  756. dwRet = ERROR_INTERNET_FORCE_RETRY;
  757. m_fAuthDeferred = TRUE;
  758. }
  759. else
  760. {
  761. dwRet = ERROR_INTERNET_LOGIN_FAILURE;
  762. }
  763. _pRequest->SetStatusCode(401);
  764. return dwRet;
  765. }
  766. */
  767. BOOL fCredSet;
  768. dwRet = SetCreds(&fCredSet);
  769. if (dwRet != ERROR_SUCCESS)
  770. {
  771. DEBUG_LEAVE(dwRet);
  772. return dwRet;
  773. }
  774. LPINTERNET_THREAD_INFO pCurrentThreadInfo = ::InternetGetThreadInfo();
  775. m_pNewThreadInfo->ThreadId = ::GetCurrentThreadId();
  776. ::InternetSetThreadInfo(m_pNewThreadInfo);
  777. DWORD dwLogonStatus = ::PP_Logon(m_hLogon,
  778. m_fAnonymous,
  779. 0,
  780. NULL,
  781. 0);
  782. ::InternetSetThreadInfo(pCurrentThreadInfo);
  783. IndicatePrivacyEvents();
  784. if (dwLogonStatus == PP_LOGON_REQUIRED /*|| dwLogonStatus == PP_LOGON_FAILED*/)
  785. {
  786. // change from 302 to 401
  787. _pRequest->ReplaceResponseHeader(HTTP_QUERY_STATUS_CODE,
  788. "401", strlen("401"),
  789. 0, HTTP_ADDREQ_FLAG_REPLACE);
  790. // biaow-todo: 1) nice to replace the status text as well; weird to have "HTTP/1.1 401 object moved"
  791. // for example 2) remove the Location: header
  792. _pRequest->SetStatusCode(401);
  793. BOOL fPrompt;
  794. m_hBitmap = NULL;
  795. bGetCbText = (m_pwszCbText == NULL);
  796. if (m_pwszReqUserName)
  797. {
  798. delete[] m_pwszReqUserName;
  799. m_pwszReqUserName = NULL;
  800. }
  801. m_dwReqUserNameLen = 0;
  802. //Get the size of CbText and UserName;
  803. ::PP_GetChallengeInfo(m_hLogon,
  804. NULL, NULL, NULL, (bGetCbText ? &m_dwCbTextLen : NULL), NULL, 0,
  805. NULL, &m_dwReqUserNameLen);
  806. if (bGetCbText)
  807. m_pwszCbText = new WCHAR[m_dwCbTextLen + 1];
  808. m_pwszReqUserName = new WCHAR[m_dwReqUserNameLen + 1];
  809. if (!m_pwszCbText || !m_pwszReqUserName)
  810. {
  811. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  812. }
  813. else
  814. ::PP_GetChallengeInfo(m_hLogon,
  815. &m_hBitmap, &fPrompt, (bGetCbText ? m_pwszCbText : NULL), (bGetCbText ? &m_dwCbTextLen : NULL),
  816. m_wRealm, MAX_AUTH_REALM_LEN, m_pwszReqUserName, &m_dwReqUserNameLen);
  817. if (/*::PP_SetCredentials(m_hLogon, m_wRealm, m_wTarget, NULL, NULL, NULL) == FALSE ||*/
  818. fPrompt)
  819. {
  820. dwRet = ERROR_INTERNET_INCORRECT_PASSWORD;
  821. }
  822. else
  823. {
  824. if (m_fAnonymous)
  825. {
  826. if (fCredSet)
  827. {
  828. dwRet = ERROR_INTERNET_FORCE_RETRY;
  829. }
  830. else
  831. {
  832. dwRet = ERROR_INTERNET_INCORRECT_PASSWORD;
  833. }
  834. m_fAnonymous = FALSE;
  835. }
  836. else
  837. {
  838. dwRet = ERROR_INTERNET_INCORRECT_PASSWORD;
  839. }
  840. }
  841. /*
  842. else
  843. {
  844. // we are not forced to prompt AND we have a cached credentials.
  845. if (m_fCredsBad)
  846. {
  847. dwRet = ERROR_INTERNET_INCORRECT_PASSWORD;
  848. }
  849. else
  850. {
  851. dwRet = ERROR_INTERNET_FORCE_RETRY;
  852. }
  853. }
  854. */
  855. if (dwRet == ERROR_INTERNET_INCORRECT_PASSWORD)
  856. {
  857. Transfer401ContentFromPP();
  858. }
  859. // dwRet = ERROR_INTERNET_INCORRECT_PASSWORD;
  860. // Transfer401ContentFromPP();
  861. }
  862. else if (dwLogonStatus == PP_LOGON_SUCCESS)
  863. {
  864. DWORD dwFromPPLen = 0;
  865. LPWSTR pwszFromPP = NULL;
  866. dwRet = HandleSuccessfulLogon(&pwszFromPP, &dwFromPPLen, FALSE);
  867. if (dwRet != ERROR_INTERNET_LOGIN_FAILURE)
  868. {
  869. if (m_pszFromPP)
  870. {
  871. delete [] m_pszFromPP;
  872. }
  873. m_pszFromPP = new CHAR [dwFromPPLen];
  874. if (m_pszFromPP)
  875. {
  876. ::WideCharToMultiByte(CP_ACP, 0, pwszFromPP, -1, m_pszFromPP, dwFromPPLen, NULL, NULL);
  877. }
  878. }
  879. if (pwszFromPP)
  880. {
  881. delete [] pwszFromPP;
  882. }
  883. m_fAnonymous = FALSE;
  884. }
  885. else
  886. {
  887. _pRequest->SetStatusCode(401);
  888. Transfer401ContentFromPP();
  889. dwRet = ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY;
  890. }
  891. DEBUG_LEAVE(dwRet);
  892. return dwRet;
  893. }
  894. BOOL PASSPORT_CTX::Transfer401ContentFromPP(void)
  895. {
  896. DWORD ContentLength = 0;
  897. ::PP_GetChallengeContent(m_hLogon,
  898. NULL,
  899. &ContentLength);
  900. if (ContentLength > 0)
  901. {
  902. LPBYTE pContent = (LPBYTE)ALLOCATE_FIXED_MEMORY(ContentLength);
  903. if (pContent == NULL)
  904. {
  905. return FALSE;
  906. }
  907. if (::PP_GetChallengeContent(m_hLogon,
  908. pContent,
  909. &ContentLength) == TRUE)
  910. {
  911. BOOL fDrained;
  912. // play with socket mode to force DrainResponse to return synchronously
  913. ICSocket* pSocket = _pRequest->_Socket;
  914. if (pSocket)
  915. {
  916. BOOL fSocketModeSet = FALSE;
  917. if (pSocket->IsNonBlocking())
  918. {
  919. pSocket->SetNonBlockingMode(FALSE);
  920. fSocketModeSet = TRUE;
  921. }
  922. INET_ASSERT(pSocket->IsNonBlocking() == FALSE);
  923. _pRequest->DrainResponse(&fDrained);
  924. if (fSocketModeSet)
  925. {
  926. pSocket->SetNonBlockingMode(TRUE);
  927. }
  928. }
  929. _pRequest->_ResponseHeaders.FreeHeaders();
  930. _pRequest->FreeResponseBuffer();
  931. _pRequest->ResetResponseVariables();
  932. _pRequest->_ResponseHeaders.Initialize();
  933. // _pRequest->_dwCurrentStreamPosition = 0;
  934. _pRequest->CloneResponseBuffer(pContent, ContentLength);
  935. }
  936. FREE_MEMORY(pContent);
  937. }
  938. return TRUE;
  939. }
  940. /*---------------------------------------------------------------------------
  941. PASSPORT_CTX::PromptForCreds
  942. ---------------------------------------------------------------------------*/
  943. BOOL PASSPORT_CTX::PromptForCreds(HBITMAP* phBitmap, PWSTR pwszCbText, PDWORD pdwTextLen,
  944. PWSTR pwszReqUserName, PDWORD pdwReqUserNameLen )
  945. {
  946. DEBUG_ENTER ((
  947. DBG_HTTPAUTH,
  948. Dword,
  949. "PASSPORT_CTX::PromptForCreds",
  950. "this=%#x",
  951. this
  952. ));
  953. if (phBitmap)
  954. {
  955. *phBitmap = m_hBitmap;
  956. m_hBitmap = NULL;
  957. }
  958. if (pdwTextLen)
  959. {
  960. if (pwszCbText && *pdwTextLen >= m_dwCbTextLen)
  961. {
  962. wcsncpy(pwszCbText, m_pwszCbText, *pdwTextLen);
  963. delete[] m_pwszCbText;
  964. m_pwszCbText = NULL;
  965. m_dwCbTextLen = 0;
  966. }
  967. else
  968. *pdwTextLen = m_dwCbTextLen;
  969. }
  970. if (pdwReqUserNameLen)
  971. {
  972. if (pwszReqUserName && m_dwReqUserNameLen && *pdwReqUserNameLen >= m_dwReqUserNameLen)
  973. {
  974. wcsncpy(pwszReqUserName, m_pwszReqUserName, *pdwReqUserNameLen);
  975. }
  976. *pdwReqUserNameLen = m_dwReqUserNameLen;
  977. }
  978. DEBUG_LEAVE((DWORD) TRUE);
  979. return (DWORD) TRUE;
  980. }
  981. void PASSPORT_CTX::IndicatePrivacyEvents(void)
  982. {
  983. PLIST_ENTRY pEventList = ::PP_GetPrivacyEvents(m_hLogon);
  984. INET_ASSERT(pEventList);
  985. while (!IsListEmpty(pEventList))
  986. {
  987. PLIST_ENTRY pEntry = RemoveHeadList(pEventList);
  988. PRIVACY_EVENT* pEvent = (PRIVACY_EVENT*)pEntry;
  989. InternetIndicateStatus(pEvent->dwStatus, pEvent->lpvInfo, pEvent->dwInfoLength);
  990. if (pEvent->dwStatus == INTERNET_STATUS_COOKIE_SENT)
  991. {
  992. delete [] ((OutgoingCookieState*)(pEvent->lpvInfo))->pszLocation;
  993. ((OutgoingCookieState*)(pEvent->lpvInfo))->pszLocation = NULL;
  994. }
  995. else
  996. {
  997. delete [] ((IncomingCookieState*)(pEvent->lpvInfo))->pszLocation;
  998. ((OutgoingCookieState*)(pEvent->lpvInfo))->pszLocation = NULL;
  999. }
  1000. delete [] pEvent->lpvInfo;
  1001. delete pEvent;
  1002. }
  1003. }
  1004. //Determine if the character is unsafe under the URI RFC document
  1005. inline BOOL PPIsUnsafeUrlChar(TCHAR chIn) throw()
  1006. {
  1007. unsigned char ch = (unsigned char)chIn;
  1008. switch(ch)
  1009. {
  1010. case ';': case '\\': case '?': case '@': case '&':
  1011. case '=': case '+': case '$': case ',': case ' ':
  1012. case '<': case '>': case '#': case '%': case '\"':
  1013. case '{': case '}': case '|':
  1014. case '^': case '[': case ']': case '`':
  1015. return TRUE;
  1016. default:
  1017. {
  1018. if (ch < 32 || ch > 126)
  1019. return TRUE;
  1020. return FALSE;
  1021. }
  1022. }
  1023. }
  1024. BOOL PPEscapeUrl(LPCSTR lpszStringIn,
  1025. LPSTR lpszStringOut,
  1026. DWORD* pdwStrLen,
  1027. DWORD dwMaxLength,
  1028. DWORD dwFlags)
  1029. {
  1030. TCHAR ch;
  1031. DWORD dwLen = 0;
  1032. BOOL bRet = TRUE;
  1033. BOOL bSchemeFile = FALSE;
  1034. DWORD dwColonPos = 0;
  1035. DWORD dwFlagsInternal = dwFlags;
  1036. while((ch = *lpszStringIn++) != '\0')
  1037. {
  1038. //if we are at the maximum length, set bRet to FALSE
  1039. //this ensures no more data is written to lpszStringOut, but
  1040. //the length of the string is still updated, so the user
  1041. //knows how much space to allocate
  1042. if (dwLen == dwMaxLength)
  1043. {
  1044. bRet = FALSE;
  1045. }
  1046. //if we are encoding and it is an unsafe character
  1047. if (PPIsUnsafeUrlChar(ch))
  1048. {
  1049. {
  1050. //if there is not enough space for the escape sequence
  1051. if (dwLen >= (dwMaxLength-3))
  1052. {
  1053. bRet = FALSE;
  1054. }
  1055. if (bRet)
  1056. {
  1057. //output the percent, followed by the hex value of the character
  1058. *lpszStringOut++ = '%';
  1059. sprintf(lpszStringOut, "%.2X", (unsigned char)(ch));
  1060. lpszStringOut+= 2;
  1061. }
  1062. dwLen += 2;
  1063. }
  1064. }
  1065. else //safe character
  1066. {
  1067. if (bRet)
  1068. *lpszStringOut++ = ch;
  1069. }
  1070. dwLen++;
  1071. }
  1072. if (bRet)
  1073. *lpszStringOut = '\0';
  1074. *pdwStrLen = dwLen;
  1075. return bRet;
  1076. }
  1077. /////////////////
  1078. // MD5 Hash code
  1079. const CHAR g_rgchHexNumMap[] =
  1080. {
  1081. '0', '1', '2', '3', '4', '5', '6', '7',
  1082. '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  1083. };
  1084. PSTR
  1085. GetMD5Key(PSTR pszChallengeInfo, PSTR pszPassword)
  1086. {
  1087. int cbChallengeInfo = lstrlenA(pszChallengeInfo);
  1088. int cbPassword = lstrlenA(pszPassword);
  1089. PBYTE pbData = new BYTE[cbChallengeInfo + cbPassword + 1];
  1090. if (!pbData)
  1091. {
  1092. return NULL;
  1093. }
  1094. PBYTE pCurrent = pbData;
  1095. ::CopyMemory(pCurrent, pszChallengeInfo, cbChallengeInfo);
  1096. pCurrent += cbChallengeInfo;
  1097. ::CopyMemory(pCurrent, pszPassword, cbPassword);
  1098. pCurrent += cbPassword;
  1099. *pCurrent = '\0';
  1100. return (PSTR)pbData;
  1101. }
  1102. //------------------------------------------------------------------------------------
  1103. //
  1104. // Method: CAuthentication::GetMD5Result()
  1105. //
  1106. // Synopsis: Compute the MD5 hash result based on the ChallengeInfo and password.
  1107. //
  1108. // pbHexHash must be at least MD5DIGESTLEN * 2 + 1 in size
  1109. //
  1110. //------------------------------------------------------------------------------------
  1111. BOOL
  1112. GetMD5Result(PSTR pszChallengeInfo, PSTR pszPassword, PBYTE pbHexHash)
  1113. {
  1114. BOOL bRetVal = FALSE;
  1115. PSTR pMD5Key = GetMD5Key(pszChallengeInfo, pszPassword);
  1116. if (pMD5Key)
  1117. {
  1118. MD5_CTX MD5Buffer;
  1119. MD5Init(&MD5Buffer);
  1120. MD5Update(&MD5Buffer, (const unsigned char*)pMD5Key, lstrlenA(pMD5Key));
  1121. MD5Final(&MD5Buffer);
  1122. PBYTE pbHash = MD5Buffer.digest;
  1123. // pbHexHash = new BYTE[MD5DIGESTLEN * 2 + 1];
  1124. // pbHexHash = (BYTE*)HeapAlloc ( GetProcessHeap(), HEAP_ZERO_MEMORY, MD5DIGESTLEN * 2 + 1);
  1125. if (pbHexHash)
  1126. {
  1127. bRetVal = TRUE;
  1128. PBYTE pCurrent = pbHexHash;
  1129. // Convert the hash data to hex string.
  1130. for (int i = 0; i < MD5DIGESTLEN; i++)
  1131. {
  1132. *pCurrent++ = g_rgchHexNumMap[pbHash[i]/16];
  1133. *pCurrent++ = g_rgchHexNumMap[pbHash[i]%16];
  1134. }
  1135. *pCurrent = '\0';
  1136. }
  1137. delete pMD5Key;
  1138. }
  1139. return bRetVal;
  1140. }