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.

1442 lines
36 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. sspiprovider.cxx
  5. Abstract:
  6. SSPI authentication provider
  7. Author:
  8. Bilal Alam (balam) 10-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "sspiprovider.hxx"
  16. #include "uuencode.hxx"
  17. ALLOC_CACHE_HANDLER * SSPI_SECURITY_CONTEXT::sm_pachSSPISecContext = NULL;
  18. CRITICAL_SECTION SSPI_CREDENTIAL::sm_csCredentials;
  19. LIST_ENTRY SSPI_CREDENTIAL::sm_CredentialListHead;
  20. //static
  21. HRESULT
  22. SSPI_CREDENTIAL::Initialize(
  23. VOID
  24. )
  25. /*++
  26. Description:
  27. Credential cache initialization
  28. Arguments:
  29. None
  30. Returns:
  31. HRESULT
  32. --*/
  33. {
  34. InitializeListHead( &sm_CredentialListHead );
  35. if( !INITIALIZE_CRITICAL_SECTION( &sm_csCredentials ) )
  36. {
  37. return HRESULT_FROM_WIN32( GetLastError() );
  38. }
  39. return NO_ERROR;
  40. }
  41. //static
  42. VOID
  43. SSPI_CREDENTIAL::Terminate(
  44. VOID
  45. )
  46. /*++
  47. Description:
  48. Credential cache cleanup
  49. Arguments:
  50. None
  51. Returns:
  52. None
  53. --*/
  54. {
  55. SSPI_CREDENTIAL * pCred = NULL;
  56. EnterCriticalSection( &sm_csCredentials );
  57. while ( !IsListEmpty( &sm_CredentialListHead ))
  58. {
  59. pCred = CONTAINING_RECORD( sm_CredentialListHead.Flink,
  60. SSPI_CREDENTIAL,
  61. m_ListEntry );
  62. RemoveEntryList( &pCred->m_ListEntry );
  63. pCred->m_ListEntry.Flink = NULL;
  64. delete pCred;
  65. pCred = NULL;
  66. }
  67. LeaveCriticalSection( &sm_csCredentials );
  68. DeleteCriticalSection( &sm_csCredentials );
  69. }
  70. //static
  71. HRESULT
  72. SSPI_CREDENTIAL::GetCredential(
  73. CHAR * pszPackage,
  74. SSPI_CREDENTIAL ** ppCredential
  75. )
  76. /*++
  77. Description:
  78. Get SSPI credential handle from cache. If it does not exist
  79. for the SSPI package, generates a new cache entry and adds
  80. it to the credential cache
  81. Arguments:
  82. pszPackage - SSPI package name, e.g NTLM
  83. ppCredential - Set to cached credential if found
  84. Returns:
  85. HRESULT
  86. --*/
  87. {
  88. LIST_ENTRY * pEntry;
  89. SSPI_CREDENTIAL * pCred;
  90. SecPkgInfoA * pSecPkg;
  91. TimeStamp LifeTime;
  92. SECURITY_STATUS ss;
  93. HRESULT hr = S_OK;
  94. if ( pszPackage == NULL ||
  95. ppCredential == NULL )
  96. {
  97. DBG_ASSERT( FALSE );
  98. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  99. }
  100. *ppCredential = NULL;
  101. EnterCriticalSection( &sm_csCredentials );
  102. for ( pEntry = sm_CredentialListHead.Flink;
  103. pEntry != &sm_CredentialListHead;
  104. pEntry = pEntry->Flink )
  105. {
  106. pCred = CONTAINING_RECORD( pEntry,
  107. SSPI_CREDENTIAL,
  108. m_ListEntry );
  109. if ( !strcmp( pszPackage, pCred->m_strPackageName.QueryStr() ) )
  110. {
  111. //
  112. // Since we only need to read the credential info at this
  113. // point, leave the critical section first.
  114. //
  115. LeaveCriticalSection( &sm_csCredentials );
  116. *ppCredential = pCred;
  117. return NO_ERROR;
  118. }
  119. }
  120. if ( ( pCred = new SSPI_CREDENTIAL ) == NULL )
  121. {
  122. LeaveCriticalSection( &sm_csCredentials );
  123. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  124. return hr;
  125. }
  126. hr = pCred->m_strPackageName.Copy( pszPackage );
  127. if ( FAILED( hr ) )
  128. {
  129. LeaveCriticalSection( &sm_csCredentials );
  130. delete pCred;
  131. pCred = NULL;
  132. return hr;
  133. }
  134. ss = AcquireCredentialsHandleA( NULL,
  135. pszPackage,
  136. SECPKG_CRED_INBOUND,
  137. NULL,
  138. NULL,
  139. NULL,
  140. NULL,
  141. &pCred->m_hCredHandle,
  142. &LifeTime );
  143. if ( ss != STATUS_SUCCESS )
  144. {
  145. LeaveCriticalSection( &sm_csCredentials );
  146. hr = HRESULT_FROM_WIN32( ss );
  147. DBGPRINTF(( DBG_CONTEXT,
  148. "Error acquiring credential handle, hr = %x\n",
  149. hr ));
  150. delete pCred;
  151. pCred = NULL;
  152. return hr;
  153. }
  154. //
  155. // Need to determine the max token size for this package
  156. //
  157. ss = QuerySecurityPackageInfoA( pszPackage,
  158. &pSecPkg );
  159. if ( ss != STATUS_SUCCESS )
  160. {
  161. LeaveCriticalSection( &sm_csCredentials );
  162. hr = HRESULT_FROM_WIN32( ss );
  163. DBGPRINTF(( DBG_CONTEXT,
  164. "Error querying security package info, hr = %x\n",
  165. hr ));
  166. delete pCred;
  167. pCred = NULL;
  168. return hr;
  169. }
  170. pCred->m_cbMaxTokenLen = pSecPkg->cbMaxToken;
  171. pCred->m_fSupportsEncoding = !(pSecPkg->fCapabilities & SECPKG_FLAG_ASCII_BUFFERS);
  172. //
  173. // Insert the credential handle to the list for future use
  174. //
  175. InsertHeadList( &sm_CredentialListHead, &pCred->m_ListEntry );
  176. LeaveCriticalSection( &sm_csCredentials );
  177. *ppCredential = pCred;
  178. FreeContextBuffer( pSecPkg );
  179. return hr;
  180. }
  181. //static
  182. VOID
  183. SSPI_CREDENTIAL::RemoveCredentialFromCache(
  184. SSPI_CREDENTIAL * pCredential
  185. )
  186. /*++
  187. Description:
  188. Remove SSPI credential handle from cache.
  189. Arguments:
  190. pCredential - Point to SSPI credential handle to be deleted
  191. Returns:
  192. HRESULT
  193. --*/
  194. {
  195. EnterCriticalSection( &sm_csCredentials );
  196. RemoveEntryList( &pCredential->m_ListEntry );
  197. LeaveCriticalSection( &sm_csCredentials );
  198. pCredential->m_ListEntry.Flink = NULL;
  199. delete pCredential;
  200. pCredential = NULL;
  201. }
  202. HRESULT
  203. SSPI_AUTH_PROVIDER::Initialize(
  204. DWORD dwInternalId
  205. )
  206. /*++
  207. Routine Description:
  208. Initialize SSPI provider
  209. Arguments:
  210. None
  211. Return Value:
  212. HRESULT
  213. --*/
  214. {
  215. HRESULT hr;
  216. SetInternalId( dwInternalId );
  217. hr = SSPI_SECURITY_CONTEXT::Initialize();
  218. if ( FAILED( hr ) )
  219. {
  220. return hr;
  221. }
  222. hr = SSPI_CREDENTIAL::Initialize();
  223. if ( FAILED( hr ) )
  224. {
  225. SSPI_SECURITY_CONTEXT::Terminate();
  226. return hr;
  227. }
  228. return NO_ERROR;
  229. }
  230. VOID
  231. SSPI_AUTH_PROVIDER::Terminate(
  232. VOID
  233. )
  234. /*++
  235. Routine Description:
  236. Terminate SSPI provider
  237. Arguments:
  238. None
  239. Return Value:
  240. None
  241. --*/
  242. {
  243. SSPI_CREDENTIAL::Terminate();
  244. SSPI_SECURITY_CONTEXT::Terminate();
  245. }
  246. HRESULT
  247. SSPI_AUTH_PROVIDER::DoesApply(
  248. W3_MAIN_CONTEXT * pMainContext,
  249. BOOL * pfApplies
  250. )
  251. /*++
  252. Routine Description:
  253. Does the given request have credentials applicable to the SSPI
  254. provider
  255. Arguments:
  256. pMainContext - Main context representing request
  257. pfApplies - Set to true if SSPI is applicable
  258. Return Value:
  259. HRESULT
  260. --*/
  261. {
  262. HRESULT hr;
  263. W3_METADATA * pMetaData;
  264. SSPI_CONTEXT_STATE * pContextState;
  265. STACK_STRA( strPackage, 64 );
  266. LPCSTR pszAuthHeader;
  267. USHORT cchAuthHeader = 0;
  268. if ( pMainContext == NULL ||
  269. pfApplies == NULL )
  270. {
  271. DBG_ASSERT( FALSE );
  272. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  273. }
  274. *pfApplies = FALSE;
  275. pszAuthHeader = pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization,
  276. &cchAuthHeader );
  277. if ( pszAuthHeader == NULL )
  278. {
  279. // no authorization header
  280. return NO_ERROR;
  281. }
  282. //
  283. // Get the package name
  284. //
  285. hr = pMainContext->QueryRequest()->GetAuthType( &strPackage );
  286. if ( FAILED( hr ) )
  287. {
  288. return hr;
  289. }
  290. //
  291. // No package, then this doesn't apply
  292. //
  293. if ( strPackage.IsEmpty() )
  294. {
  295. return NO_ERROR;
  296. }
  297. //
  298. // Check metabase for whether SSPI package is supported
  299. //
  300. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  301. DBG_ASSERT( pMetaData != NULL );
  302. if ( pMetaData->CheckAuthProvider( strPackage.QueryStr() ) )
  303. {
  304. DBG_ASSERT( pszAuthHeader != NULL );
  305. //
  306. // Save away the package so we don't have to calc again
  307. //
  308. DBG_ASSERT( !strPackage.IsEmpty() );
  309. pContextState = new (pMainContext) SSPI_CONTEXT_STATE(
  310. ( cchAuthHeader > strPackage.QueryCCH() ) ?
  311. ( pszAuthHeader + strPackage.QueryCCH() + 1 ) : "" );
  312. if ( pContextState == NULL )
  313. {
  314. return HRESULT_FROM_WIN32( GetLastError() );
  315. }
  316. hr = pContextState->SetPackage( strPackage.QueryStr() );
  317. if ( FAILED( hr ) )
  318. {
  319. delete pContextState;
  320. return hr;
  321. }
  322. pMainContext->SetContextState( pContextState );
  323. *pfApplies = TRUE;
  324. }
  325. return NO_ERROR;
  326. }
  327. HRESULT
  328. SSPI_AUTH_PROVIDER::DoAuthenticate(
  329. W3_MAIN_CONTEXT * pMainContext,
  330. BOOL * // unused
  331. )
  332. /*++
  333. Description:
  334. Do authentication work (we will be called if we apply)
  335. Arguments:
  336. pMainContext - Main context
  337. Return Value:
  338. HRESULT
  339. --*/
  340. {
  341. SSPI_CONTEXT_STATE * pContextState = NULL;
  342. W3_METADATA * pMetaData = NULL;
  343. W3_REQUEST * pW3Request = NULL;
  344. SSPI_SECURITY_CONTEXT * pSecurityContext = NULL;
  345. SECURITY_STATUS ss = SEC_E_OK;
  346. TimeStamp Lifetime;
  347. SecBufferDesc OutBuffDesc;
  348. SecBuffer OutSecBuff;
  349. SecBufferDesc InBuffDesc;
  350. SecBuffer InSecBuff;
  351. ULONG ContextAttributes;
  352. SSPI_CREDENTIAL * pCredentials = NULL;
  353. HRESULT hr;
  354. STACK_BUFFER ( buffDecoded, 256 );
  355. CHAR * pszFinalBlob = NULL;
  356. DWORD cbFinalBlob;
  357. CtxtHandle hCtxtHandle;
  358. BOOL fNeedContinue = FALSE;
  359. SSPI_USER_CONTEXT * pUserContext;
  360. BUFFER buffResponse;
  361. BOOL fNewConversation = TRUE;
  362. DWORD err;
  363. if ( pMainContext == NULL )
  364. {
  365. DBG_ASSERT( FALSE );
  366. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  367. }
  368. pContextState = (SSPI_CONTEXT_STATE*) pMainContext->QueryContextState();
  369. DBG_ASSERT( pContextState != NULL );
  370. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  371. DBG_ASSERT( pMetaData != NULL );
  372. pW3Request = pMainContext->QueryRequest();
  373. DBG_ASSERT( pW3Request != NULL );
  374. //
  375. // If we got to here, then the package better be supported!
  376. //
  377. DBG_ASSERT( pMetaData->CheckAuthProvider( pContextState->QueryPackage() ) );
  378. //
  379. // Are we in the middle of a handshake?
  380. //
  381. pSecurityContext =
  382. ( SSPI_SECURITY_CONTEXT * ) QueryConnectionAuthContext( pMainContext );
  383. //
  384. // If the security context indicates we are complete already, then
  385. // cleanup that context before proceeding to create a new one.
  386. //
  387. if ( pSecurityContext != NULL &&
  388. pSecurityContext->QueryIsComplete() )
  389. {
  390. SetConnectionAuthContext( pMainContext,
  391. NULL );
  392. pSecurityContext = NULL;
  393. }
  394. if ( pSecurityContext != NULL )
  395. {
  396. DBG_ASSERT( pSecurityContext->CheckSignature() );
  397. pCredentials = pSecurityContext->QueryCredentials();
  398. fNewConversation = FALSE;
  399. }
  400. else
  401. {
  402. //
  403. // Nope. Need to create a new SSPI_SECURITY_CONTEXT and find
  404. // credentials for this package
  405. //
  406. hr = SSPI_CREDENTIAL::GetCredential((CHAR*) pContextState->QueryPackage(),
  407. &pCredentials );
  408. if ( FAILED( hr ) )
  409. {
  410. DBGPRINTF((DBG_CONTEXT,
  411. "Error get credential handle. hr = 0x%x \n",
  412. hr ));
  413. goto Failure;
  414. }
  415. pSecurityContext = new SSPI_SECURITY_CONTEXT( pCredentials );
  416. if ( pSecurityContext == NULL )
  417. {
  418. hr = HRESULT_FROM_WIN32( GetLastError() );
  419. goto Failure;
  420. }
  421. hr = SetConnectionAuthContext( pMainContext,
  422. pSecurityContext );
  423. if ( FAILED( hr ) )
  424. {
  425. DBGPRINTF((DBG_CONTEXT,
  426. "Failed to set Connection Auth Context. hr = 0x%x \n",
  427. hr ));
  428. goto Failure;
  429. }
  430. }
  431. DBG_ASSERT( pCredentials != NULL );
  432. DBG_ASSERT( pSecurityContext != NULL );
  433. //
  434. // Process credential blob.
  435. //
  436. //
  437. // Should we uudecode this buffer?
  438. //
  439. if ( pCredentials->QuerySupportsEncoding() )
  440. {
  441. if ( !uudecode( (PCHAR) pContextState->QueryCredentials(),
  442. &buffDecoded,
  443. &cbFinalBlob ) )
  444. {
  445. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  446. Http401BadLogon );
  447. return NO_ERROR;
  448. }
  449. pszFinalBlob = (CHAR*) buffDecoded.QueryPtr();
  450. }
  451. else
  452. {
  453. pszFinalBlob = (PCHAR) pContextState->QueryCredentials();
  454. cbFinalBlob = strlen(pContextState->QueryCredentials()) + 1;
  455. }
  456. //
  457. // Setup the response blob buffer
  458. //
  459. if ( !buffResponse.Resize( pCredentials->QueryMaxTokenSize() ) )
  460. {
  461. hr = HRESULT_FROM_WIN32( GetLastError() );
  462. goto Failure;
  463. }
  464. //
  465. // Setup the call to AcceptSecurityContext()
  466. //
  467. OutBuffDesc.ulVersion = 0;
  468. OutBuffDesc.cBuffers = 1;
  469. OutBuffDesc.pBuffers = &OutSecBuff;
  470. OutSecBuff.cbBuffer = pCredentials->QueryMaxTokenSize();
  471. OutSecBuff.BufferType = SECBUFFER_TOKEN;
  472. OutSecBuff.pvBuffer = buffResponse.QueryPtr();
  473. InBuffDesc.ulVersion = 0;
  474. InBuffDesc.cBuffers = 1;
  475. InBuffDesc.pBuffers = &InSecBuff;
  476. InSecBuff.cbBuffer = cbFinalBlob;
  477. InSecBuff.BufferType = SECBUFFER_TOKEN;
  478. InSecBuff.pvBuffer = pszFinalBlob;
  479. //
  480. // Let'r rip!
  481. //
  482. //
  483. // Register the remote IP address with LSA so that it can be logged
  484. //
  485. if( pW3Request->QueryRemoteAddressType() == AF_INET )
  486. {
  487. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  488. sizeof( SOCKADDR_IN ) );
  489. }
  490. else if( pW3Request->QueryRemoteAddressType() == AF_INET6 )
  491. {
  492. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  493. sizeof( SOCKADDR_IN6 ) );
  494. }
  495. else
  496. {
  497. DBG_ASSERT( FALSE );
  498. }
  499. if( !NT_SUCCESS( ss ) )
  500. {
  501. hr = ss;
  502. goto Failure;
  503. }
  504. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  505. //
  506. // Set required context attributes ASC_REQ_EXTENDED_ERROR, this
  507. // allows Negotiate/Kerberos to support time-skew recovery.
  508. //
  509. ss = AcceptSecurityContext( pCredentials->QueryCredHandle(),
  510. fNewConversation ?
  511. NULL :
  512. pSecurityContext->QueryContextHandle(),
  513. &InBuffDesc,
  514. ASC_REQ_EXTENDED_ERROR,
  515. SECURITY_NATIVE_DREP,
  516. &hCtxtHandle,
  517. &OutBuffDesc,
  518. &ContextAttributes,
  519. &Lifetime );
  520. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  521. if( ss == SEC_E_INVALID_TOKEN )
  522. {
  523. //
  524. // The client has abandoned the previous conversation and possibly
  525. // started a new conversation on the same connection. Get rid of
  526. // the previous partially formed context first then retry
  527. // authentication.
  528. //
  529. if ( SecIsValidHandle( pSecurityContext->QueryContextHandle() ) )
  530. {
  531. DeleteSecurityContext( pSecurityContext->QueryContextHandle() );
  532. SecInvalidateHandle( pSecurityContext->QueryContextHandle() );
  533. }
  534. //
  535. // Register the remote IP address with LSA so that it can be logged
  536. //
  537. if( pW3Request->QueryRemoteAddressType() == AF_INET )
  538. {
  539. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  540. sizeof( SOCKADDR_IN ) );
  541. }
  542. else if( pW3Request->QueryRemoteAddressType() == AF_INET6 )
  543. {
  544. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  545. sizeof( SOCKADDR_IN6 ) );
  546. }
  547. else
  548. {
  549. DBG_ASSERT( FALSE );
  550. }
  551. if( !NT_SUCCESS( ss ) )
  552. {
  553. hr = ss;
  554. goto Failure;
  555. }
  556. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  557. ss = AcceptSecurityContext( pCredentials->QueryCredHandle(),
  558. NULL,
  559. &InBuffDesc,
  560. ASC_REQ_EXTENDED_ERROR,
  561. SECURITY_NATIVE_DREP,
  562. &hCtxtHandle,
  563. &OutBuffDesc,
  564. &ContextAttributes,
  565. &Lifetime );
  566. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  567. }
  568. else if( ss == SEC_E_WRONG_PRINCIPAL )
  569. {
  570. //
  571. // The error is caused by changes of the machine password, we
  572. // need to regenerate a credential handle in this case
  573. //
  574. SSPI_CREDENTIAL::RemoveCredentialFromCache( pCredentials );
  575. hr = SSPI_CREDENTIAL::GetCredential((CHAR*) pContextState->QueryPackage(),
  576. &pCredentials );
  577. if ( FAILED( hr ) )
  578. {
  579. DBGPRINTF((DBG_CONTEXT,
  580. "Error get credential handle. hr = 0x%x \n",
  581. hr ));
  582. goto Failure;
  583. }
  584. pSecurityContext->SetCredentials( pCredentials );
  585. //
  586. // Register the remote IP address with LSA so that it can be logged
  587. //
  588. if( pW3Request->QueryRemoteAddressType() == AF_INET )
  589. {
  590. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  591. sizeof( SOCKADDR_IN ) );
  592. }
  593. else if( pW3Request->QueryRemoteAddressType() == AF_INET6 )
  594. {
  595. ss = SecpSetIPAddress( ( PUCHAR )pW3Request->QueryRemoteSockAddress(),
  596. sizeof( SOCKADDR_IN6 ) );
  597. }
  598. else
  599. {
  600. DBG_ASSERT( FALSE );
  601. }
  602. if( !NT_SUCCESS( ss ) )
  603. {
  604. hr = ss;
  605. goto Failure;
  606. }
  607. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  608. ss = AcceptSecurityContext( pCredentials->QueryCredHandle(),
  609. NULL,
  610. &InBuffDesc,
  611. ASC_REQ_EXTENDED_ERROR,
  612. SECURITY_NATIVE_DREP,
  613. &hCtxtHandle,
  614. &OutBuffDesc,
  615. &ContextAttributes,
  616. &Lifetime );
  617. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  618. }
  619. if ( !NT_SUCCESS( ss ) )
  620. {
  621. DBGPRINTF(( DBG_CONTEXT,
  622. "AcceptSecurityContext failed, error %x\n",
  623. ss ));
  624. if ( ss == SEC_E_LOGON_DENIED ||
  625. ss == SEC_E_INVALID_TOKEN ||
  626. ss == SEC_E_INVALID_HANDLE )
  627. {
  628. err = GetLastError();
  629. if( err == ERROR_PASSWORD_MUST_CHANGE ||
  630. err == ERROR_PASSWORD_EXPIRED )
  631. {
  632. return HRESULT_FROM_WIN32( err );
  633. }
  634. //
  635. // Could not logon the user because of wrong credentials
  636. //
  637. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  638. Http401BadLogon );
  639. pMainContext->SetErrorStatus( ss );
  640. hr = NO_ERROR;
  641. }
  642. else
  643. {
  644. hr = ss;
  645. }
  646. goto Failure;
  647. }
  648. if( ContextAttributes & ASC_RET_NULL_SESSION )
  649. {
  650. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  651. Http401BadLogon );
  652. pMainContext->SetErrorStatus( HRESULT_FROM_WIN32(
  653. ERROR_ACCESS_DENIED ) );
  654. hr = NO_ERROR;
  655. goto Failure;
  656. }
  657. pSecurityContext->SetContextHandle( hCtxtHandle );
  658. pSecurityContext->SetContextAttributes( ContextAttributes );
  659. if ( ss == SEC_I_CONTINUE_NEEDED ||
  660. ss == SEC_I_COMPLETE_AND_CONTINUE )
  661. {
  662. fNeedContinue = TRUE;
  663. }
  664. else if ( ( ss == SEC_I_COMPLETE_NEEDED ) ||
  665. ( ss == SEC_I_COMPLETE_AND_CONTINUE ) )
  666. {
  667. //
  668. // Now we just need to complete the token (if requested) and
  669. // prepare it for shipping to the other side if needed
  670. //
  671. ss = CompleteAuthToken( &hCtxtHandle,
  672. &OutBuffDesc );
  673. if ( !NT_SUCCESS( ss ))
  674. {
  675. hr = ss;
  676. DBGPRINTF(( DBG_CONTEXT,
  677. "Error on CompleteAuthToken, hr = 0x%x\n",
  678. hr ));
  679. goto Failure;
  680. }
  681. }
  682. //
  683. // Format or copy to the output buffer if we need to reply
  684. //
  685. if ( OutSecBuff.cbBuffer != 0 )
  686. {
  687. STACK_BUFFER( buffAuthData, 256 );
  688. hr = pContextState->QueryResponseHeader()->Copy(
  689. pContextState->QueryPackage() );
  690. if( FAILED( hr ) )
  691. {
  692. DBGPRINTF(( DBG_CONTEXT,
  693. "Error copying auth type, hr = 0x%x.\n",
  694. hr ));
  695. goto Failure;
  696. }
  697. hr = pContextState->QueryResponseHeader()->Append( " ", 1 );
  698. if( FAILED( hr ) )
  699. {
  700. DBGPRINTF(( DBG_CONTEXT,
  701. "Error copying auth header, hr = 0x%x.\n",
  702. hr ));
  703. goto Failure;
  704. }
  705. DBG_ASSERT( pCredentials != NULL );
  706. if ( pCredentials->QuerySupportsEncoding() )
  707. {
  708. if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
  709. (DWORD) OutSecBuff.cbBuffer,
  710. &buffAuthData ) )
  711. {
  712. DBGPRINTF(( DBG_CONTEXT,
  713. "Error uuencoding the output buffer.\n"
  714. ));
  715. hr = HRESULT_FROM_WIN32( GetLastError() );
  716. goto Failure;
  717. }
  718. pszFinalBlob = (CHAR *)buffAuthData.QueryPtr();
  719. }
  720. else
  721. {
  722. pszFinalBlob = (CHAR *)OutSecBuff.pvBuffer;
  723. }
  724. hr = pContextState->QueryResponseHeader()->Append( pszFinalBlob );
  725. if( FAILED( hr ) )
  726. {
  727. DBGPRINTF(( DBG_CONTEXT,
  728. "Error appending resp header, hr = 0x%x.\n",
  729. hr ));
  730. goto Failure;
  731. }
  732. //
  733. // If we are not done authentication, then add the challenge header
  734. // now (we are guaranteed to want to send it, which is not the case
  735. // if this is just a continuation header for kerberos mutual auth)
  736. //
  737. if ( fNeedContinue )
  738. {
  739. //
  740. // Add the WWW-Authenticate header
  741. //
  742. hr = pMainContext->QueryResponse()->SetHeader(
  743. "WWW-Authenticate",
  744. 16, // number of chars in above string
  745. pContextState->QueryResponseHeader()->QueryStr(),
  746. (USHORT)pContextState->QueryResponseHeader()->QueryCCH() );
  747. if ( FAILED( hr ) )
  748. {
  749. goto Failure;
  750. }
  751. //
  752. // Don't let anyone else send back authentication headers when
  753. // the 401 is sent
  754. //
  755. pMainContext->SetProviderHandled( TRUE );
  756. }
  757. }
  758. if ( !fNeedContinue )
  759. {
  760. //
  761. // Create a user context and setup it up
  762. //
  763. pUserContext = new SSPI_USER_CONTEXT( this );
  764. if ( pUserContext == NULL )
  765. {
  766. hr = HRESULT_FROM_WIN32( GetLastError() );
  767. goto Failure;
  768. }
  769. hr = pUserContext->Create( pSecurityContext,
  770. pMainContext );
  771. if ( FAILED( hr ) )
  772. {
  773. pUserContext->DereferenceUserContext();
  774. pUserContext = NULL;
  775. goto Failure;
  776. }
  777. //
  778. // If there is an auth token remaining to be sent, associate that
  779. // with the user context. We will send it
  780. //
  781. if ( OutSecBuff.cbBuffer != 0 )
  782. {
  783. hr = pUserContext->QueryAuthToken()->Copy( *(pContextState->QueryResponseHeader()) );
  784. if ( FAILED( hr ) )
  785. {
  786. pUserContext->DereferenceUserContext();
  787. pUserContext = NULL;
  788. goto Failure;
  789. }
  790. }
  791. pMainContext->SetUserContext( pUserContext );
  792. pSecurityContext->SetIsComplete( TRUE );
  793. }
  794. else
  795. {
  796. //
  797. // We need to send a 401 response to continue the handshake.
  798. // We have already setup the WWW-Authenticate header
  799. //
  800. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  801. Http401BadLogon );
  802. pMainContext->SetFinishedResponse();
  803. }
  804. return NO_ERROR;
  805. Failure:
  806. if ( pSecurityContext != NULL )
  807. {
  808. SetConnectionAuthContext( pMainContext,
  809. NULL );
  810. pSecurityContext = NULL;
  811. }
  812. return hr;
  813. }
  814. HRESULT
  815. SSPI_AUTH_PROVIDER::OnAccessDenied(
  816. W3_MAIN_CONTEXT * pMainContext
  817. )
  818. /*++
  819. Description:
  820. Add WWW-Authenticate headers
  821. Arguments:
  822. pMainContext - main context
  823. Return Value:
  824. HRESULT
  825. --*/
  826. {
  827. MULTISZA * pProviders;
  828. W3_METADATA * pMetaData;
  829. const CHAR * pszProvider;
  830. W3_USER_CONTEXT * pUserContext = NULL;
  831. HRESULT hr;
  832. if ( pMainContext == NULL )
  833. {
  834. DBG_ASSERT( FALSE );
  835. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  836. }
  837. //
  838. // If the user context has an associated auth token with it, clear it now
  839. //
  840. pUserContext = pMainContext->QueryUserContext();
  841. if ( pUserContext != NULL )
  842. {
  843. if ( pUserContext->QueryAuthToken() != NULL )
  844. {
  845. pUserContext->QueryAuthToken()->Reset();
  846. }
  847. }
  848. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  849. DBG_ASSERT( pMetaData != NULL );
  850. pProviders = pMetaData->QueryAuthProviders();
  851. if ( pProviders != NULL )
  852. {
  853. pszProvider = pProviders->First();
  854. while ( pszProvider != NULL )
  855. {
  856. hr = pMainContext->QueryResponse()->SetHeader(
  857. "WWW-Authenticate",
  858. 16,
  859. ( CHAR * )pszProvider,
  860. strlen( pszProvider ) );
  861. if ( FAILED( hr ) )
  862. {
  863. return hr;
  864. }
  865. pszProvider = pProviders->Next( pszProvider );
  866. }
  867. }
  868. return NO_ERROR;
  869. }
  870. //static
  871. HRESULT
  872. SSPI_SECURITY_CONTEXT::Initialize(
  873. VOID
  874. )
  875. /*++
  876. Description:
  877. Global SSPI_SECURITY_CONTEXT initialization
  878. Arguments:
  879. None
  880. Return Value:
  881. HRESULT
  882. --*/
  883. {
  884. ALLOC_CACHE_CONFIGURATION acConfig;
  885. //
  886. // Initialize allocation lookaside
  887. //
  888. acConfig.nConcurrency = 1;
  889. acConfig.nThreshold = 100;
  890. acConfig.cbSize = sizeof( SSPI_SECURITY_CONTEXT );
  891. DBG_ASSERT( sm_pachSSPISecContext == NULL );
  892. sm_pachSSPISecContext = new ALLOC_CACHE_HANDLER(
  893. "SSPI_SECURITY_CONTEXT",
  894. &acConfig );
  895. if ( sm_pachSSPISecContext == NULL ||
  896. !sm_pachSSPISecContext->IsValid() )
  897. {
  898. if( sm_pachSSPISecContext != NULL )
  899. {
  900. delete sm_pachSSPISecContext;
  901. sm_pachSSPISecContext = NULL;
  902. }
  903. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  904. DBGPRINTF(( DBG_CONTEXT,
  905. "Error initializing sm_pachSSPISecContext. hr = 0x%x\n",
  906. hr ));
  907. return hr;
  908. }
  909. return S_OK;
  910. } // SSPI_SECURITY_CONTEXT::Initialize
  911. //static
  912. VOID
  913. SSPI_SECURITY_CONTEXT::Terminate(
  914. VOID
  915. )
  916. /*++
  917. Routine Description:
  918. Destroy SSPI_SECURITY_CONTEXT globals
  919. Arguments:
  920. None
  921. Return Value:
  922. None
  923. --*/
  924. {
  925. DBG_ASSERT( sm_pachSSPISecContext != NULL );
  926. delete sm_pachSSPISecContext;
  927. sm_pachSSPISecContext = NULL;
  928. }
  929. HRESULT
  930. SSPI_USER_CONTEXT::Create(
  931. SSPI_SECURITY_CONTEXT * pSecurityContext,
  932. W3_MAIN_CONTEXT * pMainContext
  933. )
  934. /*++
  935. Routine Description:
  936. Create an SSPI user context
  937. Arguments:
  938. pSecurityContext - container of important SSPI handles
  939. Return Value:
  940. HRESULT
  941. --*/
  942. {
  943. SECURITY_STATUS ss;
  944. HRESULT hr;
  945. SecPkgContext_Names CredNames;
  946. SecPkgContext_PackageInfo PkgInfo;
  947. if ( pSecurityContext == NULL ||
  948. pMainContext == NULL )
  949. {
  950. DBG_ASSERT( pSecurityContext != NULL );
  951. DBG_ASSERT( pMainContext != NULL );
  952. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  953. }
  954. //
  955. // Get the token
  956. //
  957. ss = QuerySecurityContextToken( pSecurityContext->QueryContextHandle(),
  958. &_hImpersonationToken );
  959. if ( ss == SEC_E_INVALID_HANDLE )
  960. {
  961. hr = ss;
  962. DBGPRINTF(( DBG_CONTEXT,
  963. "Error QuerySecurityContextToken, hr = 0x%x.\n",
  964. ss ));
  965. return hr;
  966. }
  967. //
  968. // Disable the backup privilege for the token
  969. //
  970. DisableTokenBackupPrivilege( _hImpersonationToken );
  971. //
  972. // Next, the user name
  973. //
  974. ss = QueryContextAttributes( pSecurityContext->QueryContextHandle(),
  975. SECPKG_ATTR_NAMES,
  976. &CredNames );
  977. if ( !NT_SUCCESS( ss ) )
  978. {
  979. hr = ss;
  980. DBGPRINTF(( DBG_CONTEXT,
  981. "QueryContextAttributes() failed with ss = 0x%x.\n",
  982. ss ));
  983. return hr;
  984. }
  985. else
  986. {
  987. //
  988. // Digest SSP may have a bug in it since the user name returned
  989. // is NULL, workaround here
  990. //
  991. if( CredNames.sUserName )
  992. {
  993. hr = _strUserName.Copy( CredNames.sUserName );
  994. FreeContextBuffer( CredNames.sUserName );
  995. if ( FAILED( hr ) )
  996. {
  997. return hr;
  998. }
  999. }
  1000. }
  1001. //
  1002. // Get the package name
  1003. //
  1004. ss = QueryContextAttributes( pSecurityContext->QueryContextHandle(),
  1005. SECPKG_ATTR_PACKAGE_INFO,
  1006. &PkgInfo );
  1007. if ( !NT_SUCCESS( ss ) )
  1008. {
  1009. hr = ss;
  1010. DBGPRINTF(( DBG_CONTEXT,
  1011. "QueryContextAttributes() failed with ss = 0x%x.\n",
  1012. ss ));
  1013. return hr;
  1014. }
  1015. else
  1016. {
  1017. hr = _strPackageName.Copy( PkgInfo.PackageInfo->Name );
  1018. FreeContextBuffer( PkgInfo.PackageInfo );
  1019. if ( FAILED( hr ) )
  1020. {
  1021. return hr;
  1022. }
  1023. if( _strPackageName.Equals( L"NTLM" ) )
  1024. {
  1025. _fAuthNTLM = TRUE;
  1026. }
  1027. }
  1028. //
  1029. // If password expiration notification is enabled
  1030. // and Url is configured properly
  1031. // then save expiration info
  1032. //
  1033. if( pMainContext->QuerySite()->IsAuthPwdChangeNotificationEnabled() &&
  1034. pMainContext->QuerySite()->QueryAdvNotPwdExpUrl() != NULL )
  1035. {
  1036. SecPkgContext_PasswordExpiry speExpiry;
  1037. ss = QueryContextAttributes(
  1038. pSecurityContext->QueryContextHandle(),
  1039. SECPKG_ATTR_PASSWORD_EXPIRY,
  1040. &speExpiry );
  1041. if ( ss == STATUS_SUCCESS )
  1042. {
  1043. memcpy( &_AccountPwdExpiry,
  1044. &speExpiry.tsPasswordExpires,
  1045. sizeof( LARGE_INTEGER ) );
  1046. _fSetAccountPwdExpiry = TRUE;
  1047. }
  1048. }
  1049. //
  1050. // Save a pointer to the security context
  1051. //
  1052. _pSecurityContext = pSecurityContext;
  1053. return NO_ERROR;
  1054. }
  1055. HANDLE
  1056. SSPI_USER_CONTEXT::QueryPrimaryToken(
  1057. VOID
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. Get primary token for this user
  1062. Arguments:
  1063. None
  1064. Return Value:
  1065. Token handle
  1066. --*/
  1067. {
  1068. DBG_ASSERT( _hImpersonationToken != NULL );
  1069. if ( _hPrimaryToken == NULL )
  1070. {
  1071. if ( DuplicateTokenEx( _hImpersonationToken,
  1072. 0,
  1073. NULL,
  1074. SecurityImpersonation,
  1075. TokenPrimary,
  1076. &_hPrimaryToken ) )
  1077. {
  1078. DBG_ASSERT( _hPrimaryToken != NULL );
  1079. }
  1080. }
  1081. return _hPrimaryToken;
  1082. }
  1083. LARGE_INTEGER *
  1084. SSPI_USER_CONTEXT::QueryExpiry(
  1085. VOID
  1086. )
  1087. /*++
  1088. Routine Description:
  1089. User account expiry information
  1090. Arguments:
  1091. None
  1092. Return Value:
  1093. LARGE_INTEGER
  1094. --*/
  1095. {
  1096. if ( _fSetAccountPwdExpiry )
  1097. {
  1098. return &_AccountPwdExpiry;
  1099. }
  1100. return NULL;
  1101. }