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.

537 lines
16 KiB

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