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.

582 lines
17 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 OUTPUT_BUFFER_LEN 10000
  7. #define HEADER_IDX 0
  8. #define REALM_IDX 1
  9. #define HOST_IDX 2
  10. #define URL_IDX 3
  11. #define METHOD_IDX 4
  12. #define USER_IDX 5
  13. #define PASS_IDX 6
  14. #define NONCE_IDX 7
  15. #define NC_IDX 8
  16. #define HWND_IDX 9
  17. #define NUM_BUFF 10
  18. #define ISC_MODE_AUTH 0
  19. #define ISC_MODE_PREAUTH 1
  20. #define ISC_MODE_UI 2
  21. struct DIGEST_PKG_DATA
  22. {
  23. LPSTR szAppCtx;
  24. LPSTR szUserCtx;
  25. };
  26. //
  27. // IsSecHandleZero( CtxtHandle)
  28. // Utility function for working with SSPI. It can be used for Security Handles,
  29. // Context Handles, and Credential handles.
  30. //
  31. // Some SSPI functions that maintain a context handle initially take NULL
  32. //as the address of that handle, and then the address of the handle, as an
  33. //input param. To track whether or not it is an initial call, we check if
  34. //the context handle is zero.
  35. //
  36. bool IsSecHandleZero( CtxtHandle h)
  37. {
  38. return h.dwUpper == 0 && h.dwLower == 0;
  39. };
  40. /*-----------------------------------------------------------------------------
  41. DIGEST_CTX
  42. -----------------------------------------------------------------------------*/
  43. // Globals
  44. CCritSec DIGEST_CTX::s_CritSection;
  45. HINSTANCE DIGEST_CTX::g_hSecLib = NULL;
  46. PSecurityFunctionTable DIGEST_CTX::g_pFuncTbl = NULL;
  47. unsigned int DIGEST_CTX::g_iUniquePerDigestCtxInt; // let it start at a mildly pseudorandom value.
  48. /*---------------------------------------------------------------------------
  49. static DIGEST_CTX::GlobalInitialize()
  50. ---------------------------------------------------------------------------*/
  51. BOOL DIGEST_CTX::GlobalInitialize()
  52. {
  53. if (g_pFuncTbl != NULL)
  54. return TRUE;
  55. else if (!s_CritSection.Lock())
  56. return FALSE;
  57. //
  58. // Get the global SSPI dispatch table. (get g_hSecLib and then g_pFuncTbl)
  59. //
  60. if (g_hSecLib == NULL)
  61. {
  62. OSVERSIONINFO VerInfo;
  63. VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  64. GetVersionEx (&VerInfo);
  65. if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
  66. {
  67. g_hSecLib = LoadLibrary (SSP_SPM_NT_DLL);
  68. }
  69. }
  70. if (g_pFuncTbl == NULL && g_hSecLib != NULL)
  71. {
  72. INIT_SECURITY_INTERFACE addrProcISI = NULL;
  73. addrProcISI = (INIT_SECURITY_INTERFACE) GetProcAddress(g_hSecLib,
  74. SECURITY_ENTRYPOINT_ANSI);
  75. if (addrProcISI != NULL)
  76. g_pFuncTbl = (*addrProcISI)();
  77. }
  78. s_CritSection.Unlock();
  79. return g_pFuncTbl != NULL;
  80. }
  81. /*---------------------------------------------------------------------------
  82. static DIGEST_CTX::GlobalRelease()
  83. ---------------------------------------------------------------------------*/
  84. void DIGEST_CTX::GlobalRelease()
  85. {
  86. g_pFuncTbl = NULL;
  87. if( g_hSecLib != NULL)
  88. {
  89. FreeLibrary( g_hSecLib);
  90. g_hSecLib = NULL;
  91. }
  92. }
  93. /*---------------------------------------------------------------------------
  94. DIGEST_CTX::GetRequestUri
  95. ---------------------------------------------------------------------------*/
  96. LPSTR DIGEST_CTX::GetRequestUri()
  97. {
  98. LPSTR szUrl;
  99. DWORD cbUrl;
  100. URL_COMPONENTSA sUrl;
  101. memset(&sUrl, 0, sizeof(sUrl));
  102. sUrl.dwStructSize = sizeof(sUrl);
  103. sUrl.dwHostNameLength = (DWORD)-1;
  104. sUrl.dwUrlPathLength = (DWORD)-1;
  105. sUrl.dwExtraInfoLength = (DWORD)-1;
  106. szUrl = _pRequest->GetURL();
  107. // Generate request-uri
  108. if (WinHttpCrackUrlA(szUrl, strlen(szUrl), 0, &sUrl))
  109. {
  110. cbUrl = sUrl.dwUrlPathLength;
  111. szUrl = New CHAR[cbUrl+1];
  112. if (!szUrl)
  113. {
  114. // Alloc failure. Return NULL. We will
  115. // use _pRequest->GetURL instead.
  116. return NULL;
  117. }
  118. memcpy(szUrl, sUrl.lpszUrlPath, cbUrl);
  119. szUrl[cbUrl] = '\0';
  120. }
  121. else
  122. {
  123. // ICU failed. Return NULL which
  124. // will cause _pRequest->GetURL
  125. // to be used.
  126. return NULL;
  127. }
  128. return szUrl;
  129. }
  130. /*---------------------------------------------------------------------------
  131. DIGEST_CTX::InitSecurityBuffers
  132. ---------------------------------------------------------------------------*/
  133. VOID DIGEST_CTX::InitSecurityBuffers(LPSTR szOutBuf, DWORD cbOutBuf,
  134. LPDWORD pdwSecFlags, DWORD dwISCMode)
  135. {
  136. LPSTR lpszPass = NULL;
  137. // Input Buffer.
  138. _SecBuffInDesc.cBuffers = NUM_BUFF;
  139. _SecBuffInDesc.pBuffers = _SecBuffIn;
  140. // Set Header
  141. _SecBuffIn[HEADER_IDX].pvBuffer = _szData;
  142. _SecBuffIn[HEADER_IDX].cbBuffer = _cbData;
  143. _SecBuffIn[HEADER_IDX].BufferType = SECBUFFER_TOKEN;
  144. // If credentials are supplied will be set to
  145. // ISC_REQ_USE_SUPPLIED_CREDS.
  146. // If prompting for auth dialog will be set to
  147. // ISC_REQ_PROMPT_FOR_CREDS.
  148. *pdwSecFlags = 0;
  149. // Set realm if no header, otherwise NULL.
  150. if (_SecBuffIn[HEADER_IDX].pvBuffer || _pCreds->lpszRealm == NULL)
  151. {
  152. _SecBuffIn[REALM_IDX].pvBuffer = NULL;
  153. _SecBuffIn[REALM_IDX].cbBuffer = 0;
  154. }
  155. else
  156. {
  157. // We are preauthenticating using the realm
  158. _SecBuffIn[REALM_IDX].pvBuffer = _pCreds->lpszRealm;
  159. _SecBuffIn[REALM_IDX].cbBuffer = strlen(_pCreds->lpszRealm);
  160. }
  161. // Host.
  162. _SecBuffIn[HOST_IDX].pvBuffer = _pCreds->lpszHost;
  163. _SecBuffIn[HOST_IDX].cbBuffer = strlen(_pCreds->lpszHost);
  164. _SecBuffIn[HOST_IDX].BufferType = SECBUFFER_TOKEN;
  165. // Request URI.
  166. if (!_szRequestUri)
  167. {
  168. _szRequestUri = GetRequestUri();
  169. if (_szRequestUri)
  170. _SecBuffIn[URL_IDX].pvBuffer = _szRequestUri;
  171. else
  172. _SecBuffIn[URL_IDX].pvBuffer = _pRequest->GetURL();
  173. }
  174. _SecBuffIn[URL_IDX].cbBuffer = strlen((LPSTR) _SecBuffIn[URL_IDX].pvBuffer);
  175. _SecBuffIn[URL_IDX].BufferType = SECBUFFER_TOKEN;
  176. LPSTR lpszVerb;
  177. DWORD dwVerbLength;
  178. lpszVerb = _pRequest->_RequestHeaders.GetVerb(&dwVerbLength);
  179. if(NULL != _pszVerb)
  180. delete[] _pszVerb;
  181. _pszVerb = new CHAR[dwVerbLength+1];
  182. if (_pszVerb)
  183. {
  184. memcpy(_pszVerb, lpszVerb, dwVerbLength);
  185. _pszVerb[dwVerbLength] = 0;
  186. }
  187. // HTTP method.
  188. _SecBuffIn[METHOD_IDX].pvBuffer = _pszVerb;
  189. _SecBuffIn[METHOD_IDX].cbBuffer = dwVerbLength;
  190. // MapHttpMethodType(_pRequest->GetMethodType(), (LPCSTR*) &_SecBuffIn[METHOD_IDX].pvBuffer);
  191. _SecBuffIn[METHOD_IDX].BufferType = SECBUFFER_TOKEN;
  192. if (_SecBuffIn[PASS_IDX].pvBuffer)
  193. {
  194. INET_ASSERT(strlen((LPCSTR)_SecBuffIn[PASS_IDX].pvBuffer) == _SecBuffIn[PASS_IDX].cbBuffer);
  195. SecureZeroMemory(_SecBuffIn[PASS_IDX].pvBuffer, _SecBuffIn[PASS_IDX].cbBuffer);
  196. FREE_MEMORY(_SecBuffIn[PASS_IDX].pvBuffer);
  197. }
  198. // User and pass might be provided from Creds entry. Use only if
  199. // we have a challenge header (we don't pre-auth using supplied creds).
  200. if (dwISCMode == ISC_MODE_AUTH && _pCreds->lpszUser && *_pCreds->lpszUser
  201. && (lpszPass = _pCreds->GetPass()) && *lpszPass)
  202. {
  203. // User.
  204. _SecBuffIn[USER_IDX].pvBuffer = _pCreds->lpszUser;
  205. _SecBuffIn[USER_IDX].cbBuffer = strlen(_pCreds->lpszUser);
  206. _SecBuffIn[USER_IDX].BufferType = SECBUFFER_TOKEN;
  207. // Pass.
  208. _SecBuffIn[PASS_IDX].pvBuffer = lpszPass;
  209. _SecBuffIn[PASS_IDX].cbBuffer = strlen(lpszPass);
  210. _SecBuffIn[PASS_IDX].BufferType = SECBUFFER_TOKEN;
  211. *pdwSecFlags = ISC_REQ_USE_SUPPLIED_CREDS;
  212. }
  213. else
  214. {
  215. // User.
  216. _SecBuffIn[USER_IDX].pvBuffer = NULL;
  217. _SecBuffIn[USER_IDX].cbBuffer = 0;
  218. _SecBuffIn[USER_IDX].BufferType = SECBUFFER_TOKEN;
  219. // Pass.
  220. _SecBuffIn[PASS_IDX].pvBuffer = NULL;
  221. _SecBuffIn[PASS_IDX].cbBuffer = 0;
  222. _SecBuffIn[PASS_IDX].BufferType = SECBUFFER_TOKEN;
  223. }
  224. // If the 'if' statement above caused the lpszPass variable to be allocated
  225. // but it was not assigned to _SecBuffIn[PASS_IDX].pvBuffer, then free it.
  226. if (lpszPass != NULL && _SecBuffIn[PASS_IDX].pvBuffer == NULL)
  227. {
  228. SecureZeroMemory(lpszPass, strlen(lpszPass));
  229. FREE_MEMORY(lpszPass);
  230. }
  231. if (dwISCMode == ISC_MODE_UI)
  232. *pdwSecFlags = ISC_REQ_PROMPT_FOR_CREDS;
  233. // Out Buffer.
  234. _SecBuffOutDesc.cBuffers = 1;
  235. _SecBuffOutDesc.pBuffers = _SecBuffOut;
  236. _SecBuffOut[0].pvBuffer = szOutBuf;
  237. _SecBuffOut[0].cbBuffer = cbOutBuf;
  238. _SecBuffOut[0].BufferType = SECBUFFER_TOKEN;
  239. }
  240. /*---------------------------------------------------------------------------
  241. Constructor
  242. ---------------------------------------------------------------------------*/
  243. DIGEST_CTX::DIGEST_CTX(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy,
  244. SPMData *pSPM, AUTH_CREDS* pCreds)
  245. : AUTHCTX(pSPM, pCreds)
  246. {
  247. _fIsProxy = fIsProxy;
  248. _pRequest = pRequest;
  249. _szAlloc = NULL;
  250. _szData = NULL;
  251. _pvContext = NULL;
  252. _szRequestUri = NULL;
  253. _cbData = 0;
  254. _cbContext = 0;
  255. _pszVerb = NULL;
  256. // Zero out the security buffers and request context.
  257. memset(&_SecBuffInDesc, 0, sizeof(_SecBuffInDesc));
  258. memset(&_SecBuffOutDesc, 0, sizeof(_SecBuffInDesc));
  259. memset(_SecBuffIn, 0, sizeof(_SecBuffIn));
  260. memset(_SecBuffOut, 0, sizeof(_SecBuffOut));
  261. // On the first call to InitializeSecurityContext() it doesn't have a valid handle,
  262. //but afterwards _hCtxt is a handle to accumulated data.
  263. memset(&_hCtxt, 0, sizeof(_hCtxt));
  264. _szUserCtx[0] = '\0';
  265. memset(&_hCred, 0, sizeof(_hCred));
  266. //
  267. // Initialize class global stuff if necessary
  268. //
  269. if (!DIGEST_CTX::GlobalInitialize())
  270. return; // failure indicated by IsSecHandleZero(_hCred)
  271. //
  272. // Get credentials handle unique to this digest context
  273. //
  274. SEC_WINNT_AUTH_IDENTITY_EXA SecIdExA;
  275. DIGEST_PKG_DATA PkgData;
  276. sprintf(_szUserCtx, "digest%pn%x", pRequest, g_iUniquePerDigestCtxInt++);
  277. PkgData.szAppCtx = PkgData.szUserCtx = _szUserCtx;
  278. memset(&SecIdExA, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_EXA));
  279. SecIdExA.Version = sizeof(SEC_WINNT_AUTH_IDENTITY_EXA);
  280. SecIdExA.User = (unsigned char*) &PkgData;
  281. SecIdExA.UserLength = sizeof( PkgData);
  282. (*(g_pFuncTbl->AcquireCredentialsHandleA))
  283. (NULL, "Digest", SECPKG_CRED_OUTBOUND, NULL, &SecIdExA, NULL, 0, &_hCred, NULL);
  284. // success indicated by !IsSecHandleZero(_hCred)
  285. }
  286. /*---------------------------------------------------------------------------
  287. Destructor
  288. ---------------------------------------------------------------------------*/
  289. DIGEST_CTX::~DIGEST_CTX()
  290. {
  291. if(!IsSecHandleZero(_hCtxt))
  292. {
  293. (*(g_pFuncTbl->DeleteSecurityContext))( &_hCtxt);
  294. }
  295. if( !IsSecHandleZero( _hCred))
  296. {
  297. (*(g_pFuncTbl->FreeCredentialsHandle))(&_hCred);
  298. }
  299. if (_szAlloc)
  300. delete [] _szAlloc;
  301. if (_pvContext)
  302. delete [] _pvContext;
  303. if (_szRequestUri)
  304. delete [] _szRequestUri;
  305. if (_pszVerb)
  306. delete [] _pszVerb;
  307. if (_SecBuffIn[PASS_IDX].pvBuffer)
  308. {
  309. INET_ASSERT(strlen((LPCSTR)_SecBuffIn[PASS_IDX].pvBuffer) == _SecBuffIn[PASS_IDX].cbBuffer);
  310. SecureZeroMemory(_SecBuffIn[PASS_IDX].pvBuffer, _SecBuffIn[PASS_IDX].cbBuffer);
  311. FREE_MEMORY(_SecBuffIn[PASS_IDX].pvBuffer);
  312. }
  313. }
  314. /*---------------------------------------------------------------------------
  315. PreAuthUser
  316. ---------------------------------------------------------------------------*/
  317. DWORD DIGEST_CTX::PreAuthUser(OUT LPSTR pBuff, IN OUT LPDWORD pcbBuff)
  318. {
  319. SECURITY_STATUS ssResult = SEC_E_OK;
  320. INET_ASSERT(_pSPMData == _pCreds->pSPM);
  321. if (IsSecHandleZero(_hCred))
  322. return ERROR_WINHTTP_INTERNAL_ERROR;
  323. if (AuthLock())
  324. {
  325. // If a response has been generated copy into output buffer.
  326. if (_cbContext)
  327. {
  328. memcpy(pBuff, _pvContext, _cbContext);
  329. *pcbBuff = _cbContext;
  330. }
  331. // Otherwise attempt to preauthenticate.
  332. else
  333. {
  334. // Call into the SSPI package.
  335. DWORD sf;
  336. InitSecurityBuffers(pBuff, *pcbBuff, &sf, ISC_MODE_AUTH);
  337. ssResult = (*(g_pFuncTbl->InitializeSecurityContext))
  338. (&_hCred, (IsSecHandleZero(_hCtxt) ? NULL : &_hCtxt), NULL, sf, 0, 0,
  339. &_SecBuffInDesc, 0, &_hCtxt, &_SecBuffOutDesc, NULL, NULL);
  340. INET_ASSERT( !IsSecHandleZero( _hCtxt));
  341. *pcbBuff = _SecBuffOut[0].cbBuffer;
  342. }
  343. AuthUnlock();
  344. }
  345. return (DWORD) ssResult;
  346. }
  347. /*---------------------------------------------------------------------------
  348. UpdateFromHeaders
  349. ---------------------------------------------------------------------------*/
  350. DWORD DIGEST_CTX::UpdateFromHeaders(HTTP_REQUEST_HANDLE_OBJECT *pRequest, BOOL fIsProxy)
  351. {
  352. if (IsSecHandleZero(_hCred))
  353. return ERROR_WINHTTP_INTERNAL_ERROR;
  354. if (!AuthLock())
  355. {
  356. return ERROR_NOT_ENOUGH_MEMORY;
  357. }
  358. DWORD dwError, dwAuthIdx;
  359. LPSTR szRealm;
  360. DWORD cbRealm;
  361. // Get the associated header.
  362. if ((dwError = FindHdrIdxFromScheme(&dwAuthIdx)) != ERROR_SUCCESS)
  363. goto exit;
  364. // If this auth ctx does not have Creds then it has been
  365. // just been constructed in response to a 401.
  366. if (!_pCreds)
  367. {
  368. // Get any realm.
  369. dwError = GetAuthHeaderData(pRequest, fIsProxy, "Realm",
  370. &szRealm, &cbRealm, ALLOCATE_BUFFER, dwAuthIdx);
  371. if (dwError != ERROR_SUCCESS)
  372. {
  373. goto exit;
  374. }
  375. _pCreds = CreateCreds(pRequest, fIsProxy, _pSPMData, szRealm);
  376. if (pRequest->_pszRealm)
  377. {
  378. FREE_MEMORY(pRequest->_pszRealm);
  379. }
  380. pRequest->_pszRealm = szRealm;
  381. szRealm = NULL;
  382. if (_pCreds)
  383. {
  384. INET_ASSERT(_pCreds->pSPM == _pSPMData);
  385. }
  386. else
  387. {
  388. dwError = ERROR_WINHTTP_INTERNAL_ERROR;
  389. goto exit;
  390. }
  391. }
  392. else if (_pCreds != NULL && _cbContext)
  393. {
  394. // else if we have credentials and had authorization data with the last request,
  395. //then we failed to authenticate.
  396. dwError = ERROR_WINHTTP_INCORRECT_PASSWORD;
  397. goto exit;
  398. }
  399. // Updating the buffer - delete old one if necessary.
  400. if (_szAlloc)
  401. {
  402. delete [] _szAlloc;
  403. _szAlloc = _szData = NULL;
  404. _cbData = 0;
  405. }
  406. // Get the entire authentication header.
  407. dwError = GetAuthHeaderData(pRequest, fIsProxy, NULL,
  408. &_szAlloc, &_cbData, ALLOCATE_BUFFER, dwAuthIdx);
  409. if (dwError != ERROR_SUCCESS)
  410. {
  411. goto exit;
  412. }
  413. // Point just past scheme
  414. _szData = _szAlloc;
  415. while (*_szData != ' ')
  416. {
  417. _szData++;
  418. _cbData--;
  419. }
  420. // The request will be retried.
  421. dwError = ERROR_SUCCESS;
  422. exit:
  423. AuthUnlock();
  424. return dwError;
  425. }
  426. /*---------------------------------------------------------------------------
  427. PostAuthUser
  428. ---------------------------------------------------------------------------*/
  429. DWORD DIGEST_CTX::PostAuthUser()
  430. {
  431. if (IsSecHandleZero(_hCred))
  432. return ERROR_WINHTTP_INTERNAL_ERROR;
  433. if (!AuthLock())
  434. {
  435. return ERROR_NOT_ENOUGH_MEMORY;
  436. }
  437. INET_ASSERT(_pSPMData == _pCreds->pSPM);
  438. DWORD dwError;
  439. SECURITY_STATUS ssResult;
  440. // Allocate an output buffer if not done so already.
  441. if (!_pvContext)
  442. {
  443. _pvContext = New CHAR[OUTPUT_BUFFER_LEN];
  444. if (!_pvContext)
  445. {
  446. dwError = ERROR_NOT_ENOUGH_MEMORY;
  447. goto exit;
  448. }
  449. }
  450. _cbContext = OUTPUT_BUFFER_LEN;
  451. // Call into the SSPI package.
  452. DWORD sf;
  453. InitSecurityBuffers((LPSTR) _pvContext, _cbContext, &sf, ISC_MODE_AUTH);
  454. ssResult = (*(g_pFuncTbl->InitializeSecurityContext))
  455. (&_hCred, (IsSecHandleZero(_hCtxt) ? NULL : &_hCtxt), NULL, sf,
  456. 0, 0, &_SecBuffInDesc, 0, &_hCtxt, &_SecBuffOutDesc, NULL, NULL);
  457. INET_ASSERT( !IsSecHandleZero( _hCtxt));
  458. _cbContext = _SecBuffOutDesc.pBuffers[0].cbBuffer;
  459. switch(ssResult)
  460. {
  461. case SEC_E_OK:
  462. {
  463. dwError = ERROR_WINHTTP_FORCE_RETRY;
  464. break;
  465. }
  466. case SEC_E_NO_CREDENTIALS:
  467. {
  468. dwError = ERROR_WINHTTP_INCORRECT_PASSWORD;
  469. break;
  470. }
  471. default:
  472. dwError = ERROR_WINHTTP_LOGIN_FAILURE;
  473. }
  474. exit:
  475. _pRequest->SetCreds(NULL);
  476. AuthUnlock();
  477. return dwError;
  478. }