Source code of Windows XP (NT5)
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.

619 lines
17 KiB

  1. #include <wininetp.h>
  2. #include <urlmon.h>
  3. #include <splugin.hxx>
  4. #include <security.h>
  5. #include "auth.h"
  6. #define SSP_SPM_NT_DLL "security.dll"
  7. #define SSP_SPM_WIN95_DLL "secur32.dll"
  8. #define MAX_SILENT_RETRIES 3
  9. #define OUTPUT_BUFFER_LEN 10000
  10. #define HEADER_IDX 0
  11. #define REALM_IDX 1
  12. #define HOST_IDX 2
  13. #define URL_IDX 3
  14. #define METHOD_IDX 4
  15. #define USER_IDX 5
  16. #define PASS_IDX 6
  17. #define NONCE_IDX 7
  18. #define NC_IDX 8
  19. #define HWND_IDX 9
  20. #define NUM_BUFF 10
  21. #define ISC_MODE_AUTH 0
  22. #define ISC_MODE_PREAUTH 1
  23. #define ISC_MODE_UI 2
  24. struct DIGEST_PKG_DATA
  25. {
  26. LPSTR szAppCtx;
  27. LPSTR szUserCtx;
  28. };
  29. /*-----------------------------------------------------------------------------
  30. DIGEST_CTX
  31. -----------------------------------------------------------------------------*/
  32. // Globals
  33. PSecurityFunctionTable DIGEST_CTX::g_pFuncTbl = NULL;
  34. CredHandle DIGEST_CTX::g_hCred;
  35. /*---------------------------------------------------------------------------
  36. DIGEST_CTX::GetFuncTbl
  37. ---------------------------------------------------------------------------*/
  38. VOID DIGEST_CTX::GetFuncTbl()
  39. {
  40. HINSTANCE hSecLib;
  41. INIT_SECURITY_INTERFACE addrProcISI = NULL;
  42. #ifndef UNIX
  43. OSVERSIONINFO VerInfo;
  44. VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  45. GetVersionEx (&VerInfo);
  46. if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
  47. {
  48. hSecLib = LoadLibrary (SSP_SPM_NT_DLL);
  49. }
  50. else if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
  51. #endif /* UNIX */
  52. {
  53. hSecLib = LoadLibrary (SSP_SPM_WIN95_DLL);
  54. }
  55. addrProcISI = (INIT_SECURITY_INTERFACE) GetProcAddress(hSecLib,
  56. SECURITY_ENTRYPOINT_ANSI);
  57. g_pFuncTbl = (*addrProcISI)();
  58. }
  59. /*---------------------------------------------------------------------------
  60. DIGEST_CTX::GetRequestUri
  61. ---------------------------------------------------------------------------*/
  62. LPSTR DIGEST_CTX::GetRequestUri()
  63. {
  64. DEBUG_ENTER ((
  65. DBG_HTTPAUTH,
  66. String,
  67. "DIGEST_CTX::GetRequestUri",
  68. "this=%#x",
  69. this
  70. ));
  71. LPSTR szUrl;
  72. DWORD cbUrl;
  73. URL_COMPONENTS sUrl;
  74. memset(&sUrl, 0, sizeof(sUrl));
  75. sUrl.dwStructSize = sizeof(sUrl);
  76. sUrl.dwHostNameLength = -1;
  77. sUrl.dwUrlPathLength = -1;
  78. sUrl.dwExtraInfoLength = -1;
  79. szUrl = _pRequest->GetURL();
  80. // Generate request-uri
  81. if (InternetCrackUrl(szUrl, strlen(szUrl), 0, &sUrl))
  82. {
  83. cbUrl = sUrl.dwUrlPathLength;
  84. szUrl = new CHAR[cbUrl+1];
  85. if (!szUrl)
  86. {
  87. // Alloc failure. Return NULL. We will
  88. // use _pRequest->GetURL instead.
  89. DEBUG_LEAVE(NULL);
  90. return NULL;
  91. }
  92. memcpy(szUrl, sUrl.lpszUrlPath, cbUrl);
  93. szUrl[cbUrl] = '\0';
  94. }
  95. else
  96. {
  97. // ICU failed. Return NULL which
  98. // will cause _pRequest->GetURL
  99. // to be used.
  100. DEBUG_LEAVE(NULL);
  101. return NULL;
  102. }
  103. DEBUG_LEAVE(szUrl);
  104. return szUrl;
  105. }
  106. /*---------------------------------------------------------------------------
  107. DIGEST_CTX::InitSecurityBuffers
  108. ---------------------------------------------------------------------------*/
  109. VOID DIGEST_CTX::InitSecurityBuffers(LPSTR szOutBuf, DWORD cbOutBuf,
  110. LPDWORD pdwSecFlags, DWORD dwISCMode)
  111. {
  112. DEBUG_ENTER ((
  113. DBG_HTTPAUTH,
  114. None,
  115. "DIGEST_CTX::InitSecurityBuffers",
  116. "this=%#x szout=%#x cbout=%d secflags=%#x iscmode=%#x",
  117. this,
  118. szOutBuf,
  119. cbOutBuf,
  120. (pdwSecFlags ? *pdwSecFlags : NULL),
  121. dwISCMode
  122. ));
  123. // Input Buffer.
  124. _SecBuffInDesc.cBuffers = NUM_BUFF;
  125. _SecBuffInDesc.pBuffers = _SecBuffIn;
  126. // Set Header
  127. _SecBuffIn[HEADER_IDX].pvBuffer = _szData;
  128. _SecBuffIn[HEADER_IDX].cbBuffer = _cbData;
  129. _SecBuffIn[HEADER_IDX].BufferType = SECBUFFER_TOKEN;
  130. // If credentials are supplied will be set to
  131. // ISC_REQ_USE_SUPPLIED_CREDS.
  132. // If prompting for auth dialog will be set to
  133. // ISC_REQ_PROMPT_FOR_CREDS.
  134. *pdwSecFlags = 0;
  135. // Set realm if no header, otherwise NULL.
  136. if (_SecBuffIn[HEADER_IDX].pvBuffer)
  137. {
  138. _SecBuffIn[REALM_IDX].pvBuffer = NULL;
  139. _SecBuffIn[REALM_IDX].cbBuffer = 0;
  140. }
  141. else
  142. {
  143. // We are preauthenticating using the realm
  144. _SecBuffIn[REALM_IDX].pvBuffer = _pPWC->lpszRealm;
  145. _SecBuffIn[REALM_IDX].cbBuffer = strlen(_pPWC->lpszRealm);
  146. }
  147. // Host.
  148. _SecBuffIn[HOST_IDX].pvBuffer = _pPWC->lpszHost;
  149. _SecBuffIn[HOST_IDX].cbBuffer = strlen(_pPWC->lpszHost);
  150. _SecBuffIn[HOST_IDX].BufferType = SECBUFFER_TOKEN;
  151. // Request URI.
  152. if (!_szRequestUri)
  153. {
  154. _szRequestUri = GetRequestUri();
  155. if (_szRequestUri)
  156. _SecBuffIn[URL_IDX].pvBuffer = _szRequestUri;
  157. else
  158. _SecBuffIn[URL_IDX].pvBuffer = _pRequest->GetURL();
  159. }
  160. _SecBuffIn[URL_IDX].cbBuffer = strlen((LPSTR) _SecBuffIn[URL_IDX].pvBuffer);
  161. _SecBuffIn[URL_IDX].BufferType = SECBUFFER_TOKEN;
  162. // HTTP method.
  163. _SecBuffIn[METHOD_IDX].cbBuffer =
  164. MapHttpMethodType(_pRequest->GetMethodType(), (LPCSTR*) &_SecBuffIn[METHOD_IDX].pvBuffer);
  165. _SecBuffIn[METHOD_IDX].BufferType = SECBUFFER_TOKEN;
  166. // User and pass might be provided from pwc entry. Use only if
  167. // we have a challenge header (we don't pre-auth using supplied creds).
  168. if (dwISCMode == ISC_MODE_AUTH && _pPWC->lpszUser && *_pPWC->lpszUser
  169. && _pPWC->lpszPass && *_pPWC->lpszPass)
  170. {
  171. // User.
  172. _SecBuffIn[USER_IDX].pvBuffer = _pPWC->lpszUser;
  173. _SecBuffIn[USER_IDX].cbBuffer = strlen(_pPWC->lpszUser);
  174. _SecBuffIn[USER_IDX].BufferType = SECBUFFER_TOKEN;
  175. // Pass.
  176. _SecBuffIn[PASS_IDX].pvBuffer = _pPWC->lpszPass;
  177. _SecBuffIn[PASS_IDX].cbBuffer = strlen(_pPWC->lpszPass);
  178. _SecBuffIn[PASS_IDX].BufferType = SECBUFFER_TOKEN;
  179. *pdwSecFlags = ISC_REQ_USE_SUPPLIED_CREDS;
  180. }
  181. else
  182. {
  183. // User.
  184. _SecBuffIn[USER_IDX].pvBuffer = NULL;
  185. _SecBuffIn[USER_IDX].cbBuffer = 0;
  186. _SecBuffIn[USER_IDX].BufferType = SECBUFFER_TOKEN;
  187. // Pass.
  188. _SecBuffIn[PASS_IDX].pvBuffer = NULL;
  189. _SecBuffIn[PASS_IDX].cbBuffer = 0;
  190. _SecBuffIn[PASS_IDX].BufferType = SECBUFFER_TOKEN;
  191. }
  192. if (dwISCMode == ISC_MODE_UI)
  193. *pdwSecFlags = ISC_REQ_PROMPT_FOR_CREDS;
  194. // Out Buffer.
  195. _SecBuffOutDesc.cBuffers = 1;
  196. _SecBuffOutDesc.pBuffers = _SecBuffOut;
  197. _SecBuffOut[0].pvBuffer = szOutBuf;
  198. _SecBuffOut[0].cbBuffer = cbOutBuf;
  199. _SecBuffOut[0].BufferType = SECBUFFER_TOKEN;
  200. DEBUG_LEAVE(0);
  201. }
  202. /*---------------------------------------------------------------------------
  203. Constructor
  204. ---------------------------------------------------------------------------*/
  205. DIGEST_CTX::DIGEST_CTX(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy,
  206. SPMData *pSPM, PWC* pPWC)
  207. : AUTHCTX(pSPM, pPWC)
  208. {
  209. SECURITY_STATUS ssResult;
  210. _fIsProxy = fIsProxy;
  211. _pRequest = pRequest;
  212. _szAlloc = NULL;
  213. _szData = NULL;
  214. _pvContext = NULL;
  215. _szRequestUri = NULL;
  216. _cbData = 0;
  217. _cbContext = 0;
  218. _nRetries = 0;
  219. // Zero out the security buffers and request context.
  220. memset(&_SecBuffInDesc, 0, sizeof(_SecBuffInDesc));
  221. memset(&_SecBuffOutDesc, 0, sizeof(_SecBuffInDesc));
  222. memset(_SecBuffIn, 0, sizeof(_SecBuffIn));
  223. memset(_SecBuffOut, 0, sizeof(_SecBuffOut));
  224. memset(&_hCtxt, 0, sizeof(_hCtxt));
  225. // Is this the first time that the digest SSPI package
  226. // is being called for this process.
  227. if (!g_pFuncTbl)
  228. {
  229. // Get the global SSPI dispatch table.
  230. GetFuncTbl();
  231. DIGEST_PKG_DATA PkgData;
  232. SEC_WINNT_AUTH_IDENTITY_EXA SecIdExA;
  233. // Logon with szAppCtx = szUserCtx = NULL.
  234. PkgData.szAppCtx = PkgData.szUserCtx = NULL;
  235. memset(&SecIdExA, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_EXA));
  236. SecIdExA.Version = sizeof(SEC_WINNT_AUTH_IDENTITY_EXA);
  237. SecIdExA.User = (unsigned char*) &PkgData;
  238. SecIdExA.UserLength = sizeof(DIGEST_PKG_DATA);
  239. // Get the global credentials handle.
  240. ssResult = (*(g_pFuncTbl->AcquireCredentialsHandleA))
  241. (NULL, "Digest", SECPKG_CRED_OUTBOUND, NULL, &SecIdExA, NULL, 0, &g_hCred, NULL);
  242. }
  243. }
  244. /*---------------------------------------------------------------------------
  245. DIGEST_CTX::PromptForCreds
  246. ---------------------------------------------------------------------------*/
  247. DWORD DIGEST_CTX::PromptForCreds(HWND hWnd)
  248. {
  249. DEBUG_ENTER ((
  250. DBG_HTTPAUTH,
  251. Dword,
  252. "DIGEST_CTX::PromptForCreds",
  253. "this=%#x hwnd=%#x",
  254. this,
  255. hWnd
  256. ));
  257. SECURITY_STATUS ssResult;
  258. // Prompt for the credentials.
  259. INET_ASSERT(_pvContext);
  260. _cbContext = OUTPUT_BUFFER_LEN;
  261. DWORD sf;
  262. InitSecurityBuffers((LPSTR) _pvContext, _cbContext, &sf, ISC_MODE_UI);
  263. _SecBuffIn[HWND_IDX].pvBuffer = &hWnd;
  264. _SecBuffIn[HWND_IDX].cbBuffer = sizeof(HWND);
  265. ssResult = (*(g_pFuncTbl->InitializeSecurityContextA))(&g_hCred, &_hCtxt, NULL, sf,
  266. 0, 0, &_SecBuffInDesc, 0, &_hCtxt, &_SecBuffOutDesc, NULL, NULL);
  267. _cbContext = _SecBuffOutDesc.pBuffers[0].cbBuffer;
  268. if (ssResult == SEC_E_NO_CREDENTIALS)
  269. {
  270. DEBUG_LEAVE(ERROR_CANCELLED);
  271. return ERROR_CANCELLED;
  272. }
  273. DEBUG_LEAVE((DWORD) ssResult);
  274. return (DWORD) ssResult;
  275. }
  276. /*---------------------------------------------------------------------------
  277. Destructor
  278. ---------------------------------------------------------------------------*/
  279. DIGEST_CTX::~DIGEST_CTX()
  280. {
  281. if (_szAlloc)
  282. delete _szAlloc;
  283. if (_pvContext)
  284. delete _pvContext;
  285. if (_szRequestUri)
  286. delete _szRequestUri;
  287. }
  288. /*---------------------------------------------------------------------------
  289. PreAuthUser
  290. ---------------------------------------------------------------------------*/
  291. DWORD DIGEST_CTX::PreAuthUser(OUT LPSTR pBuff, IN OUT LPDWORD pcbBuff)
  292. {
  293. DEBUG_ENTER ((
  294. DBG_HTTPAUTH,
  295. Dword,
  296. "DIGEST_CTX::PreAuthUser",
  297. "this=%#x pBuf=%#x pcbBuf=%#x {%d}",
  298. this,
  299. pBuff,
  300. pcbBuff,
  301. *pcbBuff
  302. ));
  303. AuthLock();
  304. SECURITY_STATUS ssResult = SEC_E_OK;
  305. INET_ASSERT(_pSPMData == _pPWC->pSPM);
  306. // If a response has been generated copy into output buffer.
  307. if (_cbContext)
  308. {
  309. memcpy(pBuff, _pvContext, _cbContext);
  310. *pcbBuff = _cbContext;
  311. }
  312. // Otherwise attempt to preauthenticate.
  313. else
  314. {
  315. // Call into the SSPI package.
  316. DWORD sf;
  317. InitSecurityBuffers(pBuff, *pcbBuff, &sf, ISC_MODE_PREAUTH);
  318. ssResult = (*(g_pFuncTbl->InitializeSecurityContext))(&g_hCred, NULL, NULL, sf,
  319. 0, 0, &_SecBuffInDesc, 0, &_hCtxt, &_SecBuffOutDesc, NULL, NULL);
  320. *pcbBuff = _SecBuffOut[0].cbBuffer;
  321. }
  322. AuthUnlock();
  323. DEBUG_LEAVE((DWORD) ssResult);
  324. return (DWORD) ssResult;
  325. }
  326. /*---------------------------------------------------------------------------
  327. UpdateFromHeaders
  328. ---------------------------------------------------------------------------*/
  329. DWORD DIGEST_CTX::UpdateFromHeaders(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy)
  330. {
  331. DEBUG_ENTER ((
  332. DBG_HTTPAUTH,
  333. Dword,
  334. "DIGEST_CTX::UpdateFromHeaders",
  335. "this=%#x request=%#x isproxy=%B",
  336. this,
  337. pRequest,
  338. fIsProxy
  339. ));
  340. DWORD dwError, cbExtra, dwAuthIdx;
  341. LPSTR szAuthHeader, szExtra, szScheme;
  342. LPSTR szRealm;
  343. DWORD cbRealm;
  344. AuthLock();
  345. // Get the associated header.
  346. if ((dwError = FindHdrIdxFromScheme(&dwAuthIdx)) != ERROR_SUCCESS)
  347. goto exit;
  348. // If this auth ctx does not have pwc then it has been
  349. // just been constructed in response to a 401.
  350. if (!_pPWC)
  351. {
  352. // Get any realm.
  353. dwError = GetAuthHeaderData(pRequest, fIsProxy, "Realm",
  354. &szRealm, &cbRealm, ALLOCATE_BUFFER, dwAuthIdx);
  355. _pPWC = FindOrCreatePWC(pRequest, fIsProxy, _pSPMData, szRealm);
  356. if (_pPWC)
  357. {
  358. INET_ASSERT(_pPWC->pSPM == _pSPMData);
  359. _pPWC->nLockCount++;
  360. }
  361. else
  362. {
  363. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  364. goto exit;
  365. }
  366. }
  367. // Updating the buffer - delete old one if necessary.
  368. if (_szAlloc)
  369. {
  370. delete _szAlloc;
  371. _szAlloc = _szData = NULL;
  372. _cbData = 0;
  373. }
  374. // Get the entire authentication header.
  375. dwError = GetAuthHeaderData(pRequest, fIsProxy, NULL,
  376. &_szAlloc, &_cbData, ALLOCATE_BUFFER, dwAuthIdx);
  377. // Point just past scheme
  378. _szData = _szAlloc;
  379. while (*_szData != ' ')
  380. {
  381. _szData++;
  382. _cbData--;
  383. }
  384. // The request will be retried.
  385. dwError = ERROR_SUCCESS;
  386. exit:
  387. AuthUnlock();
  388. DEBUG_LEAVE(dwError);
  389. return dwError;
  390. }
  391. /*---------------------------------------------------------------------------
  392. PostAuthUser
  393. ---------------------------------------------------------------------------*/
  394. DWORD DIGEST_CTX::PostAuthUser()
  395. {
  396. DEBUG_ENTER ((
  397. DBG_HTTPAUTH,
  398. Dword,
  399. "DIGEST_CTX::PostAuthUser",
  400. "this=%#x",
  401. this
  402. ));
  403. INET_ASSERT(_pSPMData == _pPWC->pSPM);
  404. AuthLock();
  405. DWORD dwError;
  406. SECURITY_STATUS ssResult;
  407. // Allocate an output buffer if not done so already.
  408. if (!_pvContext)
  409. {
  410. _pvContext = new CHAR[OUTPUT_BUFFER_LEN];
  411. if (!_pvContext)
  412. {
  413. dwError = ERROR_NOT_ENOUGH_MEMORY;
  414. goto exit;
  415. }
  416. }
  417. _cbContext = OUTPUT_BUFFER_LEN;
  418. if (_nRetries++ < MAX_SILENT_RETRIES)
  419. {
  420. // If we pre-authenticated, treat as second
  421. // or subsequent attempt. We depend on the
  422. // server correctly sending stale=FALSE (or no stale)
  423. // if the credentials sent during pre-auth were bad.
  424. // In this case the digest pkg will return SEC_E_NO_CREDENTIALS
  425. // and we will prompt for credentials.
  426. // BUGBUG - Use ApplyControlToken
  427. if (_nRetries == 1 && _pRequest->GetPWC())
  428. {
  429. // Increment num of retries to 2
  430. _nRetries++;
  431. // The dwLower member has to have the correct value
  432. // so that secur32.dll can route to correct provider.
  433. _hCtxt.dwLower = g_hCred.dwLower;
  434. }
  435. // Call into the SSPI package.
  436. DWORD sf;
  437. InitSecurityBuffers((LPSTR) _pvContext, _cbContext, &sf, ISC_MODE_AUTH);
  438. ssResult = (*(g_pFuncTbl->InitializeSecurityContext))
  439. (&g_hCred, (_nRetries == 1 ? NULL : &_hCtxt), NULL, sf,
  440. 0, 0, &_SecBuffInDesc, 0, &_hCtxt, &_SecBuffOutDesc, NULL, NULL);
  441. _cbContext = _SecBuffOutDesc.pBuffers[0].cbBuffer;
  442. switch(ssResult)
  443. {
  444. case SEC_E_OK:
  445. {
  446. dwError = ERROR_INTERNET_FORCE_RETRY;
  447. break;
  448. }
  449. case SEC_E_NO_CREDENTIALS:
  450. {
  451. dwError = ERROR_INTERNET_INCORRECT_PASSWORD;
  452. break;
  453. }
  454. case SEC_E_CONTEXT_EXPIRED:
  455. {
  456. dwError = ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY;
  457. break;
  458. }
  459. default:
  460. dwError = ERROR_INTERNET_LOGIN_FAILURE;
  461. }
  462. }
  463. else
  464. {
  465. _cbContext = 0;
  466. _nRetries = 0;
  467. dwError = ERROR_INTERNET_INCORRECT_PASSWORD;
  468. }
  469. exit:
  470. _pRequest->SetPWC(NULL);
  471. AuthUnlock();
  472. DEBUG_LEAVE(dwError);
  473. return dwError;
  474. }
  475. /*---------------------------------------------------------------------------
  476. Flush creds
  477. ---------------------------------------------------------------------------*/
  478. VOID DIGEST_CTX::FlushCreds()
  479. {
  480. DEBUG_ENTER ((
  481. DBG_HTTPAUTH,
  482. None,
  483. "DIGEST_CTX::FlushCreds",
  484. NULL
  485. ));
  486. DWORD ssResult;
  487. if (g_pFuncTbl)
  488. {
  489. DWORD sf = ISC_REQ_NULL_SESSION;
  490. ssResult = (*(g_pFuncTbl->InitializeSecurityContext))(&g_hCred, NULL, NULL, sf,
  491. 0, 0, NULL, 0, NULL, NULL, NULL, NULL);
  492. }
  493. DEBUG_LEAVE(0);
  494. }
  495. /*---------------------------------------------------------------------------
  496. Logoff
  497. ---------------------------------------------------------------------------*/
  498. VOID DIGEST_CTX::Logoff()
  499. {
  500. DEBUG_ENTER ((
  501. DBG_HTTPAUTH,
  502. None,
  503. "DIGEST_CTX::Logoff",
  504. NULL
  505. ));
  506. DWORD ssResult;
  507. if (g_pFuncTbl)
  508. {
  509. ssResult = (*(g_pFuncTbl->FreeCredentialsHandle))(&g_hCred);
  510. }
  511. DEBUG_LEAVE(0);
  512. }