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.

1175 lines
35 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. digestprovider.cxx
  5. Abstract:
  6. Digest authentication provider
  7. Author:
  8. Ming Lu (minglu) 24-Jun-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "uuencode.hxx"
  16. //static
  17. HRESULT
  18. DIGEST_AUTH_PROVIDER::Initialize(
  19. DWORD dwInternalId
  20. )
  21. /*++
  22. Routine Description:
  23. Initialize Digest SSPI provider
  24. Arguments:
  25. None
  26. Return Value:
  27. HRESULT
  28. --*/
  29. {
  30. SetInternalId( dwInternalId );
  31. return NO_ERROR;
  32. }
  33. //static
  34. VOID
  35. DIGEST_AUTH_PROVIDER::Terminate(
  36. VOID
  37. )
  38. /*++
  39. Routine Description:
  40. Terminate SSPI Digest provider
  41. Arguments:
  42. None
  43. Return Value:
  44. None
  45. --*/
  46. {
  47. // no-op
  48. }
  49. HRESULT
  50. DIGEST_AUTH_PROVIDER::DoesApply(
  51. W3_MAIN_CONTEXT * pMainContext,
  52. BOOL * pfApplies
  53. )
  54. /*++
  55. Routine Description:
  56. Does the given request have credentials applicable to the Digest
  57. provider
  58. Arguments:
  59. pMainContext - Main context representing request
  60. pfApplies - Set to true if Digest is applicable
  61. Return Value:
  62. HRESULT
  63. --*/
  64. {
  65. SSPI_CONTEXT_STATE * pContextState;
  66. LPCSTR pszAuthHeader;
  67. HRESULT hr;
  68. PCHAR szDigest = "Digest";
  69. DWORD cchDigest = sizeof("Digest") - 1;
  70. USHORT cchAuthHeader = 0;
  71. if ( pMainContext == NULL ||
  72. pfApplies == NULL )
  73. {
  74. DBG_ASSERT( FALSE );
  75. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  76. }
  77. *pfApplies = FALSE;
  78. //
  79. // Is using of Digest SSP enabled?
  80. //
  81. if ( !g_pW3Server->QueryUseDigestSSP() )
  82. {
  83. return NO_ERROR;
  84. }
  85. //
  86. // Get authorization header
  87. //
  88. pszAuthHeader = pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization,
  89. &cchAuthHeader );
  90. //
  91. // No package, no auth
  92. //
  93. if ( pszAuthHeader == NULL )
  94. {
  95. return NO_ERROR;
  96. }
  97. //
  98. // Is it Digest?
  99. //
  100. if ( _strnicmp( pszAuthHeader, szDigest, cchDigest ) == 0 )
  101. {
  102. //
  103. // Save away the package so we don't have to calc again
  104. //
  105. DBG_ASSERT( pszAuthHeader != NULL );
  106. pContextState = new (pMainContext) SSPI_CONTEXT_STATE(
  107. ( cchAuthHeader > cchDigest ) ? ( pszAuthHeader + cchDigest + 1 ) : "" );
  108. if ( pContextState == NULL )
  109. {
  110. return HRESULT_FROM_WIN32( GetLastError() );
  111. }
  112. hr = pContextState->SetPackage( szDigest );
  113. if ( FAILED( hr ) )
  114. {
  115. DBGPRINTF(( DBG_CONTEXT,
  116. "Error in SetPackage(). hr = %x\n",
  117. hr ));
  118. delete pContextState;
  119. pContextState = NULL;
  120. return hr;
  121. }
  122. pMainContext->SetContextState( pContextState );
  123. *pfApplies = TRUE;
  124. }
  125. return NO_ERROR;
  126. }
  127. HRESULT
  128. DIGEST_AUTH_PROVIDER::DoAuthenticate(
  129. W3_MAIN_CONTEXT * pMainContext,
  130. BOOL * // unused
  131. )
  132. /*++
  133. Description:
  134. Do authentication work (we will be called if we apply)
  135. Arguments:
  136. pMainContext - Main context
  137. pfFilterFinished - Set to TRUE if filter wants out
  138. Return Value:
  139. HRESULT
  140. --*/
  141. {
  142. DWORD err;
  143. HRESULT hr = E_FAIL;
  144. W3_METADATA * pMetaData = NULL;
  145. W3_REQUEST * pW3Request = NULL;
  146. SSPI_SECURITY_CONTEXT * pDigestSecurityContext = NULL;
  147. SSPI_CONTEXT_STATE * pContextState = NULL;
  148. SSPI_USER_CONTEXT * pUserContext = NULL;
  149. SSPI_CREDENTIAL * pDigestCredentials = NULL;
  150. SecBufferDesc SecBuffDescOutput;
  151. SecBufferDesc SecBuffDescInput;
  152. //
  153. // We have 5 input buffer and 1 output buffer to fill data
  154. // in for digest authentication
  155. //
  156. SecBuffer SecBuffTokenOut[ 1 ];
  157. SecBuffer SecBuffTokenIn[ 5 ];
  158. SECURITY_STATUS secStatus = SEC_E_OK;
  159. CtxtHandle hServerCtxtHandle;
  160. TimeStamp Lifetime;
  161. ULONG ContextReqFlags = 0;
  162. ULONG ContextAttributes = 0;
  163. STACK_STRU( strOutputHeader, 256 );
  164. STACK_BUFFER( bufOutputBuffer, 4096 );
  165. STACK_STRA( strMethod, 10 );
  166. STACK_STRU( strUrl, MAX_PATH );
  167. STACK_STRA( strUrlA, MAX_PATH );
  168. STACK_STRU( strRealm, 128 );
  169. SecPkgContext_Target Target;
  170. STACK_STRU( strDigestUri, MAX_PATH + 1 );
  171. ULONG cbBytesCopied;
  172. SecInvalidateHandle( &hServerCtxtHandle );
  173. if ( pMainContext == NULL )
  174. {
  175. DBG_ASSERT( FALSE );
  176. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  177. }
  178. pContextState = ( SSPI_CONTEXT_STATE* )
  179. pMainContext->QueryContextState();
  180. DBG_ASSERT( pContextState != NULL );
  181. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  182. DBG_ASSERT( pMetaData != NULL );
  183. pW3Request = pMainContext->QueryRequest();
  184. DBG_ASSERT( pW3Request != NULL );
  185. //
  186. // clean the memory and set it to zero
  187. //
  188. ZeroMemory( &SecBuffDescInput , sizeof( SecBufferDesc ) );
  189. ZeroMemory( SecBuffTokenIn , sizeof( SecBuffTokenIn ) );
  190. //
  191. // define the buffer descriptor for the Input
  192. //
  193. SecBuffDescInput.ulVersion = SECBUFFER_VERSION;
  194. SecBuffDescInput.cBuffers = 5;
  195. SecBuffDescInput.pBuffers = SecBuffTokenIn;
  196. //
  197. // set the digest auth header in the buffer
  198. //
  199. SecBuffTokenIn[0].BufferType = SECBUFFER_TOKEN;
  200. SecBuffTokenIn[0].cbBuffer = strlen(pContextState->QueryCredentials());
  201. SecBuffTokenIn[0].pvBuffer = (void*) pContextState->QueryCredentials();
  202. //
  203. // Get and Set the information for the method
  204. //
  205. hr = pW3Request->GetVerbString( &strMethod );
  206. if ( FAILED( hr ) )
  207. {
  208. DBGPRINTF(( DBG_CONTEXT,
  209. "Error getting the method. hr = %x\n",
  210. hr ));
  211. return hr;
  212. }
  213. SecBuffTokenIn[1].BufferType = SECBUFFER_PKG_PARAMS;
  214. SecBuffTokenIn[1].cbBuffer = strMethod.QueryCB();
  215. SecBuffTokenIn[1].pvBuffer = ( PVOID )strMethod.QueryStr();
  216. //
  217. // Get and Set the infomation for the Url
  218. //
  219. hr = pW3Request->GetUrl( &strUrl );
  220. if( FAILED( hr ) )
  221. {
  222. DBGPRINTF(( DBG_CONTEXT,
  223. "Error getting the URL. hr = %x\n",
  224. hr ));
  225. return hr;
  226. }
  227. hr = strUrlA.CopyW( strUrl.QueryStr() );
  228. if( FAILED( hr ) )
  229. {
  230. DBGPRINTF(( DBG_CONTEXT,
  231. "Error copying the URL. hr = %x\n",
  232. hr ));
  233. return hr;
  234. }
  235. SecBuffTokenIn[2].BufferType = SECBUFFER_PKG_PARAMS;
  236. SecBuffTokenIn[2].cbBuffer = strUrlA.QueryCB();
  237. SecBuffTokenIn[2].pvBuffer = ( PVOID )strUrlA.QueryStr();
  238. //
  239. // Get and Set the information for the hentity
  240. //
  241. SecBuffTokenIn[3].BufferType = SECBUFFER_PKG_PARAMS;
  242. SecBuffTokenIn[3].cbBuffer = 0; // this is not yet implemeted
  243. SecBuffTokenIn[3].pvBuffer = NULL; // this is not yet implemeted
  244. //
  245. // Get a Security Context
  246. //
  247. //
  248. // get the credential for the server
  249. //
  250. hr = SSPI_CREDENTIAL::GetCredential( NTDIGEST_SP_NAME,
  251. &pDigestCredentials );
  252. if ( FAILED( hr ) )
  253. {
  254. DBGPRINTF((DBG_CONTEXT,
  255. "Error get credential handle. hr = 0x%x \n",
  256. hr ));
  257. return hr;
  258. }
  259. DBG_ASSERT( pDigestCredentials != NULL );
  260. //
  261. // Resize the output buffer to max token size
  262. //
  263. if( !bufOutputBuffer.Resize(
  264. pDigestCredentials->QueryMaxTokenSize() ) )
  265. {
  266. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  267. }
  268. if( pW3Request->IsProxyRequest() )
  269. {
  270. //
  271. // If the request comes from a proxy, we shouldn't use the
  272. // security context associate with the connection because
  273. // the request could actually come from a different client
  274. //
  275. SetConnectionAuthContext( pMainContext,
  276. NULL );
  277. }
  278. pDigestSecurityContext =
  279. ( SSPI_SECURITY_CONTEXT * ) QueryConnectionAuthContext( pMainContext );
  280. //
  281. // check to see if there is an old Context Handle
  282. //
  283. if ( pDigestSecurityContext != NULL )
  284. {
  285. //
  286. // defined the buffer
  287. //
  288. SecBuffTokenIn[4].BufferType = SECBUFFER_TOKEN;
  289. SecBuffTokenIn[4].cbBuffer = bufOutputBuffer.QuerySize();
  290. SecBuffTokenIn[4].pvBuffer =
  291. ( PVOID )bufOutputBuffer.QueryPtr();
  292. secStatus = VerifySignature(
  293. pDigestSecurityContext->QueryContextHandle(),
  294. &SecBuffDescInput,
  295. 0,
  296. 0 );
  297. if( FAILED( secStatus ) )
  298. {
  299. //
  300. // Clean up the security context cause we will initialize
  301. // another new challenge on the same connection
  302. //
  303. SetConnectionAuthContext( pMainContext,
  304. NULL );
  305. }
  306. }
  307. if( pDigestSecurityContext == NULL || FAILED( secStatus ) )
  308. {
  309. //
  310. // clean the memory and set it to zero
  311. //
  312. ZeroMemory( &SecBuffDescOutput, sizeof( SecBufferDesc ) );
  313. ZeroMemory( SecBuffTokenOut , sizeof( SecBuffTokenOut ) );
  314. //
  315. // define the buffer descriptor for the Outpt
  316. //
  317. SecBuffDescOutput.ulVersion = SECBUFFER_VERSION;
  318. SecBuffDescOutput.cBuffers = 1;
  319. SecBuffDescOutput.pBuffers = SecBuffTokenOut;
  320. SecBuffTokenOut[0].BufferType = SECBUFFER_TOKEN;
  321. SecBuffTokenOut[0].cbBuffer = bufOutputBuffer.QuerySize();
  322. SecBuffTokenOut[0].pvBuffer = ( PVOID )bufOutputBuffer.QueryPtr();
  323. //
  324. // Get and Set the Realm Information
  325. //
  326. if( pMetaData->QueryRealm() )
  327. {
  328. hr = strRealm.Copy( pMetaData->QueryRealm() );
  329. if( FAILED( hr ) )
  330. {
  331. DBGPRINTF(( DBG_CONTEXT,
  332. "Error copying the realm. hr = %x\n",
  333. hr ));
  334. return hr;
  335. }
  336. //
  337. // Limit the realm length to 1024 since there is limitation in Digest SSP
  338. //
  339. if( strRealm.QueryCCH() > 1024 )
  340. {
  341. return E_FAIL;
  342. }
  343. SecBuffTokenIn[4].BufferType = SECBUFFER_PKG_PARAMS;
  344. SecBuffTokenIn[4].cbBuffer = strRealm.QueryCB();
  345. SecBuffTokenIn[4].pvBuffer = ( PVOID )strRealm.QueryStr();
  346. }
  347. else
  348. {
  349. SecBuffTokenIn[4].BufferType = SECBUFFER_PKG_PARAMS;
  350. SecBuffTokenIn[4].cbBuffer = 0;
  351. SecBuffTokenIn[4].pvBuffer = NULL;
  352. }
  353. //
  354. // set the flags
  355. //
  356. ContextReqFlags = ASC_REQ_REPLAY_DETECT |
  357. ASC_REQ_CONNECTION;
  358. //
  359. // Register the remote IP address with LSA so that it can be logged
  360. //
  361. if( pW3Request->QueryRemoteAddressType() == AF_INET )
  362. {
  363. secStatus = SecpSetIPAddress(
  364. ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  365. sizeof( SOCKADDR_IN ) );
  366. }
  367. else if( pW3Request->QueryRemoteAddressType() == AF_INET6 )
  368. {
  369. secStatus = SecpSetIPAddress(
  370. ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  371. sizeof( SOCKADDR_IN6 ) );
  372. }
  373. else
  374. {
  375. DBG_ASSERT( FALSE );
  376. }
  377. if( FAILED( secStatus ) )
  378. {
  379. return secStatus;
  380. }
  381. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  382. //
  383. // get the security context
  384. //
  385. secStatus = AcceptSecurityContext(
  386. pDigestCredentials->QueryCredHandle(),
  387. NULL,
  388. &SecBuffDescInput,
  389. ContextReqFlags,
  390. SECURITY_NATIVE_DREP,
  391. &hServerCtxtHandle,
  392. &SecBuffDescOutput,
  393. &ContextAttributes,
  394. &Lifetime);
  395. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  396. {
  397. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  398. 1,
  399. (PVOID)hServerCtxtHandle.dwLower,
  400. (PVOID)hServerCtxtHandle.dwUpper,
  401. (PVOID)(ULONG_PTR)secStatus,
  402. NULL);
  403. }
  404. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  405. if( secStatus == SEC_E_WRONG_PRINCIPAL )
  406. {
  407. //
  408. // The error is caused by changes of the machine password, we
  409. // need to regenerate a credential handle in this case
  410. //
  411. SSPI_CREDENTIAL::RemoveCredentialFromCache( pDigestCredentials );
  412. hr = SSPI_CREDENTIAL::GetCredential( NTDIGEST_SP_NAME,
  413. &pDigestCredentials );
  414. if ( FAILED( hr ) )
  415. {
  416. DBGPRINTF((DBG_CONTEXT,
  417. "Error get credential handle. hr = 0x%x \n",
  418. hr ));
  419. return hr;
  420. }
  421. //
  422. // Register the remote IP address with LSA so that it can be logged
  423. //
  424. if( pW3Request->QueryRemoteAddressType() == AF_INET )
  425. {
  426. secStatus = SecpSetIPAddress(
  427. ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  428. sizeof( SOCKADDR_IN ) );
  429. }
  430. else if( pW3Request->QueryRemoteAddressType() == AF_INET6 )
  431. {
  432. secStatus = SecpSetIPAddress(
  433. ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  434. sizeof( SOCKADDR_IN6 ) );
  435. }
  436. else
  437. {
  438. DBG_ASSERT( FALSE );
  439. }
  440. if( FAILED( secStatus ) )
  441. {
  442. return secStatus;
  443. }
  444. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  445. secStatus = AcceptSecurityContext( pDigestCredentials->QueryCredHandle(),
  446. NULL,
  447. &SecBuffDescInput,
  448. ContextReqFlags,
  449. SECURITY_NATIVE_DREP,
  450. &hServerCtxtHandle,
  451. &SecBuffDescOutput,
  452. &ContextAttributes,
  453. &Lifetime );
  454. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  455. {
  456. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  457. 1,
  458. (PVOID)hServerCtxtHandle.dwLower,
  459. (PVOID)hServerCtxtHandle.dwUpper,
  460. (PVOID)(ULONG_PTR)secStatus,
  461. NULL);
  462. }
  463. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  464. }
  465. if( SEC_I_COMPLETE_NEEDED == secStatus )
  466. {
  467. //
  468. //defined the buffer
  469. //
  470. SecBuffTokenIn[4].BufferType = SECBUFFER_TOKEN;
  471. SecBuffTokenIn[4].cbBuffer = bufOutputBuffer.QuerySize();
  472. SecBuffTokenIn[4].pvBuffer =
  473. ( PVOID )bufOutputBuffer.QueryPtr();
  474. secStatus = CompleteAuthToken(
  475. &hServerCtxtHandle,
  476. &SecBuffDescInput
  477. );
  478. }
  479. if ( SUCCEEDED( secStatus ) )
  480. {
  481. //
  482. // Check URI field match URL
  483. //
  484. secStatus = QueryContextAttributes( &hServerCtxtHandle,
  485. SECPKG_ATTR_TARGET,
  486. &Target );
  487. if( SUCCEEDED( secStatus ) )
  488. {
  489. if( Target.TargetLength )
  490. {
  491. if( !strDigestUri.QueryBuffer()->Resize(
  492. (Target.TargetLength + 1) * sizeof(WCHAR) ) )
  493. {
  494. FreeContextBuffer( Target.Target );
  495. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  496. goto Cleanup;
  497. }
  498. //
  499. // Normalize DigestUri
  500. //
  501. hr = UlCleanAndCopyUrl( ( PUCHAR )Target.Target,
  502. Target.TargetLength,
  503. &cbBytesCopied,
  504. strDigestUri.QueryStr(),
  505. NULL );
  506. FreeContextBuffer( Target.Target );
  507. if( FAILED( hr ) )
  508. {
  509. goto Cleanup;
  510. }
  511. //
  512. // after modyfing string data in internal buffer
  513. // call SyncWithBuffer to synchronize string length
  514. //
  515. strDigestUri.SyncWithBuffer();
  516. if ( !strUrl.Equals( strDigestUri ) )
  517. {
  518. //
  519. // Note: RFC says that BAD REQUEST should be returned
  520. // but for now to be backward compatible with IIS5.1
  521. // we will return ACCESS_DENIED
  522. //
  523. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  524. {
  525. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  526. 0,
  527. (PVOID)hServerCtxtHandle.dwLower,
  528. (PVOID)hServerCtxtHandle.dwUpper,
  529. NULL,
  530. NULL);
  531. }
  532. DeleteSecurityContext( &hServerCtxtHandle );
  533. secStatus = E_FAIL;
  534. }
  535. else
  536. {
  537. pDigestSecurityContext = new SSPI_SECURITY_CONTEXT(
  538. pDigestCredentials,
  539. TRUE );
  540. if ( NULL == pDigestSecurityContext )
  541. {
  542. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  543. goto Cleanup;
  544. }
  545. pDigestSecurityContext->SetContextHandle(
  546. hServerCtxtHandle );
  547. pDigestSecurityContext->SetContextAttributes(
  548. ContextAttributes );
  549. //
  550. // Mark the security context is complete, so we can detect
  551. // reauthentication on the same connection
  552. //
  553. pDigestSecurityContext->SetIsComplete( TRUE );
  554. if (FAILED( hr = SetConnectionAuthContext(
  555. pMainContext,
  556. pDigestSecurityContext )))
  557. {
  558. //
  559. // There is no connection, no point creating
  560. // the response
  561. //
  562. goto Cleanup;
  563. }
  564. }
  565. }
  566. else
  567. {
  568. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  569. {
  570. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  571. 0,
  572. (PVOID)hServerCtxtHandle.dwLower,
  573. (PVOID)hServerCtxtHandle.dwUpper,
  574. NULL,
  575. NULL);
  576. }
  577. DeleteSecurityContext( &hServerCtxtHandle );
  578. secStatus = E_FAIL;
  579. }
  580. }
  581. else
  582. {
  583. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  584. {
  585. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  586. 0,
  587. (PVOID)hServerCtxtHandle.dwLower,
  588. (PVOID)hServerCtxtHandle.dwUpper,
  589. NULL,
  590. NULL);
  591. }
  592. DeleteSecurityContext( &hServerCtxtHandle );
  593. }
  594. }
  595. }
  596. if( FAILED( secStatus ) )
  597. {
  598. err = GetLastError();
  599. if( err == ERROR_PASSWORD_MUST_CHANGE ||
  600. err == ERROR_PASSWORD_EXPIRED )
  601. {
  602. return HRESULT_FROM_WIN32( err );
  603. }
  604. hr = SetDigestHeader( pMainContext );
  605. if( FAILED( hr ) )
  606. {
  607. return hr;
  608. }
  609. pMainContext->SetProviderHandled( TRUE );
  610. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  611. Http401BadLogon );
  612. pMainContext->SetErrorStatus( secStatus );
  613. }
  614. else
  615. {
  616. //
  617. // Create a user context and set it up
  618. //
  619. pUserContext = new SSPI_USER_CONTEXT( this );
  620. if ( pUserContext == NULL )
  621. {
  622. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  623. }
  624. hr = pUserContext->Create( pDigestSecurityContext, pMainContext );
  625. if ( FAILED( hr ) )
  626. {
  627. pUserContext->DereferenceUserContext();
  628. pUserContext = NULL;
  629. return hr;
  630. }
  631. pMainContext->SetUserContext( pUserContext );
  632. }
  633. return NO_ERROR;
  634. Cleanup:
  635. if ( pDigestSecurityContext != NULL )
  636. {
  637. pDigestSecurityContext->Cleanup();
  638. pDigestSecurityContext = NULL;
  639. }
  640. else if ( SecIsValidHandle( &hServerCtxtHandle ) )
  641. {
  642. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  643. {
  644. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  645. 0,
  646. (PVOID)hServerCtxtHandle.dwLower,
  647. (PVOID)hServerCtxtHandle.dwUpper,
  648. NULL,
  649. NULL);
  650. }
  651. DeleteSecurityContext( &hServerCtxtHandle );
  652. }
  653. return hr;
  654. }
  655. HRESULT
  656. DIGEST_AUTH_PROVIDER::OnAccessDenied(
  657. W3_MAIN_CONTEXT * pMainContext
  658. )
  659. /*++
  660. Description:
  661. Add WWW-Authenticate Digest headers on access denied
  662. Arguments:
  663. pMainContext - main context
  664. Return Value:
  665. HRESULT
  666. --*/
  667. {
  668. if ( pMainContext == NULL )
  669. {
  670. DBG_ASSERT( FALSE );
  671. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  672. }
  673. //
  674. // Is using of Digest SSP enabled?
  675. //
  676. if ( !g_pW3Server->QueryUseDigestSSP() )
  677. {
  678. return NO_ERROR;
  679. }
  680. if( !W3_STATE_AUTHENTICATION::QueryIsDomainMember() )
  681. {
  682. //
  683. // We are not a domain member, so do nothing
  684. //
  685. return NO_ERROR;
  686. }
  687. return SetDigestHeader( pMainContext );
  688. }
  689. HRESULT
  690. DIGEST_AUTH_PROVIDER::SetDigestHeader(
  691. IN W3_MAIN_CONTEXT * pMainContext
  692. )
  693. /*++
  694. Description:
  695. Add WWW-Authenticate Digest headers
  696. Arguments:
  697. pMainContext - main context
  698. Return Value:
  699. HRESULT
  700. --*/
  701. {
  702. HRESULT hr = E_FAIL;
  703. W3_METADATA * pMetaData;
  704. //
  705. // 4096 is the max output for digest authenticaiton
  706. //
  707. STACK_BUFFER( bufOutputBuffer, 4096 );
  708. STACK_STRA( strOutputHeader, MAX_PATH );
  709. STACK_STRA( strMethod, 10 );
  710. STACK_STRU( strUrl, MAX_PATH );
  711. STACK_STRA( strUrlA, MAX_PATH );
  712. STACK_STRU( strRealm, 128 );
  713. SecBufferDesc SecBuffDescOutput;
  714. SecBufferDesc SecBuffDescInput;
  715. SecBuffer SecBuffTokenOut[ 1 ];
  716. SecBuffer SecBuffTokenIn[ 5 ];
  717. SECURITY_STATUS secStatus = SEC_E_OK;
  718. SSPI_CREDENTIAL * pDigestCredential = NULL;
  719. CtxtHandle hServerCtxtHandle;
  720. ULONG ContextReqFlags = 0;
  721. ULONG ContextAttributes = 0;
  722. TimeStamp Lifetime;
  723. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  724. DBG_ASSERT( pMetaData != NULL );
  725. //
  726. // Get a Security Context
  727. //
  728. //
  729. // get the credential for the server
  730. //
  731. hr = SSPI_CREDENTIAL::GetCredential( NTDIGEST_SP_NAME,
  732. &pDigestCredential );
  733. if ( FAILED( hr ) )
  734. {
  735. DBGPRINTF((DBG_CONTEXT,
  736. "Error get credential handle. hr = 0x%x \n",
  737. hr ));
  738. return hr;
  739. }
  740. DBG_ASSERT( pDigestCredential != NULL );
  741. if( !bufOutputBuffer.Resize(
  742. pDigestCredential->QueryMaxTokenSize() ) )
  743. {
  744. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  745. DBGPRINTF((DBG_CONTEXT,
  746. "Error resize the output buffer. hr = 0x%x \n",
  747. hr ));
  748. return hr;
  749. }
  750. //
  751. // clean the memory and set it to zero
  752. //
  753. ZeroMemory( &SecBuffDescOutput, sizeof( SecBufferDesc ) );
  754. ZeroMemory( SecBuffTokenOut , sizeof( SecBuffTokenOut ) );
  755. ZeroMemory( &SecBuffDescInput , sizeof( SecBufferDesc ) );
  756. ZeroMemory( SecBuffTokenIn , sizeof( SecBuffTokenIn ) );
  757. //
  758. // define the OUTPUT
  759. //
  760. SecBuffDescOutput.ulVersion = SECBUFFER_VERSION;
  761. SecBuffDescOutput.cBuffers = 1;
  762. SecBuffDescOutput.pBuffers = SecBuffTokenOut;
  763. SecBuffTokenOut[0].BufferType = SECBUFFER_TOKEN;
  764. SecBuffTokenOut[0].cbBuffer = bufOutputBuffer.QuerySize();
  765. SecBuffTokenOut[0].pvBuffer = ( PVOID )bufOutputBuffer.QueryPtr();
  766. //
  767. // define the Input
  768. //
  769. SecBuffDescInput.ulVersion = SECBUFFER_VERSION;
  770. SecBuffDescInput.cBuffers = 5;
  771. SecBuffDescInput.pBuffers = SecBuffTokenIn;
  772. //
  773. // Get and Set the information for the challenge
  774. //
  775. //
  776. // set the inforamtion in the buffer, this case is Null to
  777. // authenticate user
  778. //
  779. SecBuffTokenIn[0].BufferType = SECBUFFER_TOKEN;
  780. SecBuffTokenIn[0].cbBuffer = 0;
  781. SecBuffTokenIn[0].pvBuffer = NULL;
  782. //
  783. // Get and Set the information for the method
  784. //
  785. hr = pMainContext->QueryRequest()->GetVerbString( &strMethod );
  786. if ( FAILED( hr ) )
  787. {
  788. DBGPRINTF(( DBG_CONTEXT,
  789. "Error getting the method. hr = %x\n",
  790. hr ));
  791. return hr;
  792. }
  793. SecBuffTokenIn[1].BufferType = SECBUFFER_PKG_PARAMS;
  794. SecBuffTokenIn[1].cbBuffer = strMethod.QueryCB();
  795. SecBuffTokenIn[1].pvBuffer = strMethod.QueryStr();
  796. //
  797. // Get and Set the infomation for the Url
  798. //
  799. hr = pMainContext->QueryRequest()->GetUrl( &strUrl );
  800. if( FAILED( hr ) )
  801. {
  802. DBGPRINTF(( DBG_CONTEXT,
  803. "Error getting the URL. hr = %x\n",
  804. hr ));
  805. return hr;
  806. }
  807. hr = strUrlA.CopyW( strUrl.QueryStr() );
  808. if( FAILED( hr ) )
  809. {
  810. DBGPRINTF(( DBG_CONTEXT,
  811. "Error copying the URL. hr = %x\n",
  812. hr ));
  813. return hr;
  814. }
  815. SecBuffTokenIn[2].BufferType = SECBUFFER_PKG_PARAMS;
  816. SecBuffTokenIn[2].cbBuffer = strUrlA.QueryCB();
  817. SecBuffTokenIn[2].pvBuffer = ( PVOID )strUrlA.QueryStr();
  818. //
  819. // Get and Set the information for the hentity
  820. //
  821. SecBuffTokenIn[3].BufferType = SECBUFFER_PKG_PARAMS;
  822. SecBuffTokenIn[3].cbBuffer = 0; // this is not yet implemeted
  823. SecBuffTokenIn[3].pvBuffer = NULL; // this is not yet implemeted
  824. //
  825. //Get and Set the Realm Information
  826. //
  827. if( pMetaData->QueryRealm() )
  828. {
  829. hr = strRealm.Copy( pMetaData->QueryRealm() );
  830. if( FAILED( hr ) )
  831. {
  832. DBGPRINTF(( DBG_CONTEXT,
  833. "Error copying the realm. hr = %x\n",
  834. hr ));
  835. return hr;
  836. }
  837. //
  838. // Limit the realm length to 1024 since there is limitation in Digest SSP
  839. //
  840. if( strRealm.QueryCCH() > 1024 )
  841. {
  842. return E_FAIL;
  843. }
  844. SecBuffTokenIn[4].BufferType = SECBUFFER_PKG_PARAMS;
  845. SecBuffTokenIn[4].cbBuffer = strRealm.QueryCB();
  846. SecBuffTokenIn[4].pvBuffer = ( PVOID )strRealm.QueryStr();
  847. }
  848. else
  849. {
  850. SecBuffTokenIn[4].BufferType = SECBUFFER_PKG_PARAMS;
  851. SecBuffTokenIn[4].cbBuffer = 0;
  852. SecBuffTokenIn[4].pvBuffer = NULL;
  853. }
  854. //
  855. // set the flags
  856. //
  857. ContextReqFlags = ASC_REQ_REPLAY_DETECT |
  858. ASC_REQ_CONNECTION;
  859. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  860. //
  861. // get the security context
  862. //
  863. secStatus = AcceptSecurityContext(
  864. pDigestCredential->QueryCredHandle(),
  865. NULL,
  866. &SecBuffDescInput,
  867. ContextReqFlags,
  868. SECURITY_NATIVE_DREP,
  869. &hServerCtxtHandle,
  870. &SecBuffDescOutput,
  871. &ContextAttributes,
  872. &Lifetime);
  873. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  874. {
  875. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  876. 1,
  877. (PVOID)hServerCtxtHandle.dwLower,
  878. (PVOID)hServerCtxtHandle.dwUpper,
  879. (PVOID)(ULONG_PTR)secStatus,
  880. NULL);
  881. }
  882. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  883. //
  884. // a challenge has to be send back to the client
  885. //
  886. if ( SEC_I_CONTINUE_NEEDED == secStatus )
  887. {
  888. //
  889. // The partial server context generated from the ASC call needs
  890. // to be deleted if the client doesn't authenticate again in some
  891. // time, the digest security context cache will handle this
  892. //
  893. hr = g_pW3Server->QueryDigestContextCache()->
  894. AddContextCacheEntry( &hServerCtxtHandle );
  895. if( FAILED( hr ) )
  896. {
  897. if (g_pW3Server->QueryDigestContextCache()->QueryTraceLog() != NULL)
  898. {
  899. WriteRefTraceLogEx(g_pW3Server->QueryDigestContextCache()->QueryTraceLog(),
  900. 0,
  901. (PVOID)hServerCtxtHandle.dwLower,
  902. (PVOID)hServerCtxtHandle.dwUpper,
  903. NULL,
  904. NULL);
  905. }
  906. DeleteSecurityContext( &hServerCtxtHandle );
  907. return hr;
  908. }
  909. //
  910. // Do we already have a digest security context
  911. //
  912. hr = strOutputHeader.Copy( "Digest " );
  913. if( FAILED( hr ) )
  914. {
  915. return hr;
  916. }
  917. hr = strOutputHeader.Append(
  918. ( CHAR * )SecBuffDescOutput.pBuffers[0].pvBuffer,
  919. SecBuffDescOutput.pBuffers[0].cbBuffer );
  920. if( FAILED( hr ) )
  921. {
  922. return hr;
  923. }
  924. //
  925. // Add the header WWW-Authenticate to the response after a
  926. // 401 server error
  927. //
  928. hr = pMainContext->QueryResponse()->SetHeader(
  929. "WWW-Authenticate",
  930. 16,
  931. strOutputHeader.QueryStr(),
  932. (USHORT)strOutputHeader.QueryCCH()
  933. );
  934. }
  935. else
  936. {
  937. hr = S_OK;
  938. }
  939. return hr;
  940. }