Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1181 lines
27 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. INITIALIZE_CRITICAL_SECTION( &sm_csCredentials );
  36. return NO_ERROR;
  37. }
  38. //static
  39. VOID
  40. SSPI_CREDENTIAL::Terminate(
  41. VOID
  42. )
  43. /*++
  44. Description:
  45. Credential cache cleanup
  46. Arguments:
  47. None
  48. Returns:
  49. None
  50. --*/
  51. {
  52. SSPI_CREDENTIAL * pCred = NULL;
  53. EnterCriticalSection( &sm_csCredentials );
  54. while ( !IsListEmpty( &sm_CredentialListHead ))
  55. {
  56. pCred = CONTAINING_RECORD( sm_CredentialListHead.Flink,
  57. SSPI_CREDENTIAL,
  58. m_ListEntry );
  59. RemoveEntryList( &pCred->m_ListEntry );
  60. pCred->m_ListEntry.Flink = NULL;
  61. delete pCred;
  62. }
  63. LeaveCriticalSection( &sm_csCredentials );
  64. DeleteCriticalSection( &sm_csCredentials );
  65. }
  66. //static
  67. HRESULT
  68. SSPI_CREDENTIAL::GetCredential(
  69. CHAR * pszPackage,
  70. SSPI_CREDENTIAL ** ppCredential
  71. )
  72. /*++
  73. Description:
  74. Get SSPI credential handle from cache. If it does not exist
  75. for the SSPI package, generates a new cache entry and adds
  76. it to the credential cache
  77. Arguments:
  78. pszPackage - SSPI package name, e.g NTLM
  79. ppCredential - Set to cached credential if found
  80. Returns:
  81. HRESULT
  82. --*/
  83. {
  84. LIST_ENTRY * pEntry;
  85. SSPI_CREDENTIAL * pCred;
  86. SecPkgInfoA * pSecPkg;
  87. TimeStamp LifeTime;
  88. SECURITY_STATUS ss;
  89. HRESULT hr = S_OK;
  90. if ( pszPackage == NULL ||
  91. ppCredential == NULL )
  92. {
  93. DBG_ASSERT( FALSE );
  94. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  95. }
  96. *ppCredential = NULL;
  97. EnterCriticalSection( &sm_csCredentials );
  98. for ( pEntry = sm_CredentialListHead.Flink;
  99. pEntry != &sm_CredentialListHead;
  100. pEntry = pEntry->Flink )
  101. {
  102. pCred = CONTAINING_RECORD( pEntry,
  103. SSPI_CREDENTIAL,
  104. m_ListEntry );
  105. if ( !strcmp( pszPackage, pCred->m_strPackageName.QueryStr() ) )
  106. {
  107. //
  108. // Since we only need to read the credential info at this
  109. // point, leave the critical section first.
  110. //
  111. LeaveCriticalSection( &sm_csCredentials );
  112. *ppCredential = pCred;
  113. return NO_ERROR;
  114. }
  115. }
  116. if ( ( pCred = new SSPI_CREDENTIAL ) == NULL )
  117. {
  118. LeaveCriticalSection( &sm_csCredentials );
  119. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  120. return hr;
  121. }
  122. hr = pCred->m_strPackageName.Copy( pszPackage );
  123. if ( FAILED( hr ) )
  124. {
  125. LeaveCriticalSection( &sm_csCredentials );
  126. delete pCred;
  127. pCred = NULL;
  128. return hr;
  129. }
  130. ss = AcquireCredentialsHandleA( NULL,
  131. pszPackage,
  132. SECPKG_CRED_INBOUND,
  133. NULL,
  134. NULL,
  135. NULL,
  136. NULL,
  137. &pCred->m_hCredHandle,
  138. &LifeTime );
  139. if ( ss != STATUS_SUCCESS )
  140. {
  141. LeaveCriticalSection( &sm_csCredentials );
  142. hr = HRESULT_FROM_WIN32( ss );
  143. DBGPRINTF(( DBG_CONTEXT,
  144. "Error acquiring credential handle, hr = %x\n",
  145. hr ));
  146. delete pCred;
  147. pCred = NULL;
  148. return hr;
  149. }
  150. //
  151. // Need to determine the max token size for this package
  152. //
  153. ss = QuerySecurityPackageInfoA( pszPackage,
  154. &pSecPkg );
  155. if ( ss != STATUS_SUCCESS )
  156. {
  157. LeaveCriticalSection( &sm_csCredentials );
  158. hr = HRESULT_FROM_WIN32( ss );
  159. DBGPRINTF(( DBG_CONTEXT,
  160. "Error querying security package info, hr = %x\n",
  161. hr ));
  162. delete pCred;
  163. pCred = NULL;
  164. return hr;
  165. }
  166. pCred->m_cbMaxTokenLen = pSecPkg->cbMaxToken;
  167. pCred->m_fSupportsEncoding = !(pSecPkg->fCapabilities & SECPKG_FLAG_ASCII_BUFFERS);
  168. //
  169. // Insert the credential handle to the list for future use
  170. //
  171. InsertHeadList( &sm_CredentialListHead, &pCred->m_ListEntry );
  172. LeaveCriticalSection( &sm_csCredentials );
  173. *ppCredential = pCred;
  174. FreeContextBuffer( pSecPkg );
  175. return hr;
  176. }
  177. HRESULT
  178. SSPI_AUTH_PROVIDER::Initialize(
  179. DWORD dwInternalId
  180. )
  181. /*++
  182. Routine Description:
  183. Initialize SSPI provider
  184. Arguments:
  185. None
  186. Return Value:
  187. HRESULT
  188. --*/
  189. {
  190. HRESULT hr;
  191. SetInternalId( dwInternalId );
  192. hr = SSPI_SECURITY_CONTEXT::Initialize();
  193. if ( FAILED( hr ) )
  194. {
  195. return hr;
  196. }
  197. hr = SSPI_CREDENTIAL::Initialize();
  198. if ( FAILED( hr ) )
  199. {
  200. SSPI_SECURITY_CONTEXT::Terminate();
  201. return hr;
  202. }
  203. return NO_ERROR;
  204. }
  205. VOID
  206. SSPI_AUTH_PROVIDER::Terminate(
  207. VOID
  208. )
  209. /*++
  210. Routine Description:
  211. Terminate SSPI provider
  212. Arguments:
  213. None
  214. Return Value:
  215. None
  216. --*/
  217. {
  218. SSPI_CREDENTIAL::Terminate();
  219. SSPI_SECURITY_CONTEXT::Terminate();
  220. }
  221. HRESULT
  222. SSPI_AUTH_PROVIDER::DoesApply(
  223. W3_MAIN_CONTEXT * pMainContext,
  224. BOOL * pfApplies
  225. )
  226. /*++
  227. Routine Description:
  228. Does the given request have credentials applicable to the SSPI
  229. provider
  230. Arguments:
  231. pMainContext - Main context representing request
  232. pfApplies - Set to true if SSPI is applicable
  233. Return Value:
  234. HRESULT
  235. --*/
  236. {
  237. HRESULT hr;
  238. W3_METADATA * pMetaData;
  239. SSPI_CONTEXT_STATE * pContextState;
  240. STACK_STRA( strPackage, 64 );
  241. CHAR * pszAuthHeader;
  242. if ( pMainContext == NULL ||
  243. pfApplies == NULL )
  244. {
  245. DBG_ASSERT( FALSE );
  246. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  247. }
  248. *pfApplies = FALSE;
  249. //
  250. // Get the package name
  251. //
  252. hr = pMainContext->QueryRequest()->GetAuthType( &strPackage );
  253. if ( FAILED( hr ) )
  254. {
  255. return hr;
  256. }
  257. //
  258. // No package, then this doesn't apply
  259. //
  260. if ( strPackage.IsEmpty() )
  261. {
  262. return NO_ERROR;
  263. }
  264. //
  265. // Check metabase for whether SSPI package is supported
  266. //
  267. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  268. DBG_ASSERT( pMetaData != NULL );
  269. if ( pMetaData->CheckAuthProvider( strPackage.QueryStr() ) )
  270. {
  271. pszAuthHeader = pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization );
  272. DBG_ASSERT( pszAuthHeader != NULL );
  273. //
  274. // Save away the package so we don't have to calc again
  275. //
  276. DBG_ASSERT( !strPackage.IsEmpty() );
  277. pContextState = new (pMainContext) SSPI_CONTEXT_STATE(
  278. pszAuthHeader + strPackage.QueryCCH() + 1 );
  279. if ( pContextState == NULL )
  280. {
  281. return HRESULT_FROM_WIN32( GetLastError() );
  282. }
  283. hr = pContextState->SetPackage( strPackage.QueryStr() );
  284. if ( FAILED( hr ) )
  285. {
  286. delete pContextState;
  287. return hr;
  288. }
  289. pMainContext->SetContextState( pContextState );
  290. *pfApplies = TRUE;
  291. }
  292. return NO_ERROR;
  293. }
  294. HRESULT
  295. SSPI_AUTH_PROVIDER::DoAuthenticate(
  296. W3_MAIN_CONTEXT * pMainContext
  297. )
  298. /*++
  299. Description:
  300. Do authentication work (we will be called if we apply)
  301. Arguments:
  302. pMainContext - Main context
  303. Return Value:
  304. HRESULT
  305. --*/
  306. {
  307. SSPI_CONTEXT_STATE * pContextState = NULL;
  308. W3_METADATA * pMetaData = NULL;
  309. SSPI_SECURITY_CONTEXT * pSecurityContext = NULL;
  310. SECURITY_STATUS ss;
  311. TimeStamp Lifetime;
  312. SecBufferDesc OutBuffDesc;
  313. SecBuffer OutSecBuff;
  314. SecBufferDesc InBuffDesc;
  315. SecBuffer InSecBuff;
  316. ULONG ContextAttributes;
  317. SSPI_CREDENTIAL * pCredentials = NULL;
  318. HRESULT hr;
  319. STACK_BUFFER ( buffDecoded, 256 );
  320. CHAR * pszFinalBlob = NULL;
  321. DWORD cbFinalBlob;
  322. CtxtHandle hCtxtHandle;
  323. BOOL fNeedContinue = FALSE;
  324. SSPI_USER_CONTEXT * pUserContext;
  325. BUFFER buffResponse;
  326. BOOL fNewConversation = TRUE;
  327. DWORD err;
  328. if ( pMainContext == NULL )
  329. {
  330. DBG_ASSERT( FALSE );
  331. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  332. }
  333. pContextState = (SSPI_CONTEXT_STATE*) pMainContext->QueryContextState();
  334. DBG_ASSERT( pContextState != NULL );
  335. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  336. DBG_ASSERT( pMetaData != NULL );
  337. //
  338. // If we got to here, then the package better be supported!
  339. //
  340. DBG_ASSERT( pMetaData->CheckAuthProvider( pContextState->QueryPackage() ) );
  341. //
  342. // Are we in the middle of a handshake?
  343. //
  344. pSecurityContext =
  345. ( SSPI_SECURITY_CONTEXT * ) QueryConnectionAuthContext( pMainContext );
  346. //
  347. // If the security context indicates we are complete already, then
  348. // cleanup that context before proceeding to create a new one.
  349. //
  350. if ( pSecurityContext != NULL &&
  351. pSecurityContext->QueryIsComplete() )
  352. {
  353. SetConnectionAuthContext( pMainContext,
  354. NULL );
  355. pSecurityContext = NULL;
  356. }
  357. if ( pSecurityContext != NULL )
  358. {
  359. DBG_ASSERT( pSecurityContext->CheckSignature() );
  360. pCredentials = pSecurityContext->QueryCredentials();
  361. fNewConversation = FALSE;
  362. }
  363. else
  364. {
  365. //
  366. // Nope. Need to create a new SSPI_SECURITY_CONTEXT and find
  367. // credentials for this package
  368. //
  369. hr = SSPI_CREDENTIAL::GetCredential( pContextState->QueryPackage(),
  370. &pCredentials );
  371. if ( FAILED( hr ) )
  372. {
  373. DBGPRINTF((DBG_CONTEXT,
  374. "Error get credential handle. hr = 0x%x \n",
  375. hr ));
  376. goto Failure;
  377. }
  378. pSecurityContext = new SSPI_SECURITY_CONTEXT( pCredentials );
  379. if ( pSecurityContext == NULL )
  380. {
  381. hr = HRESULT_FROM_WIN32( GetLastError() );
  382. goto Failure;
  383. }
  384. hr = SetConnectionAuthContext( pMainContext,
  385. pSecurityContext );
  386. if ( FAILED( hr ) )
  387. {
  388. DBGPRINTF((DBG_CONTEXT,
  389. "Failed to set Connection Auth Context. hr = 0x%x \n",
  390. hr ));
  391. goto Failure;
  392. }
  393. }
  394. DBG_ASSERT( pCredentials != NULL );
  395. DBG_ASSERT( pSecurityContext != NULL );
  396. //
  397. // Process credential blob.
  398. //
  399. //
  400. // Should we uudecode this buffer?
  401. //
  402. if ( pCredentials->QuerySupportsEncoding() )
  403. {
  404. if ( !uudecode( pContextState->QueryCredentials(),
  405. &buffDecoded,
  406. &cbFinalBlob ) )
  407. {
  408. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  409. Http401BadLogon );
  410. return NO_ERROR;
  411. }
  412. pszFinalBlob = (CHAR*) buffDecoded.QueryPtr();
  413. }
  414. else
  415. {
  416. pszFinalBlob = pContextState->QueryCredentials();
  417. cbFinalBlob = strlen(pContextState->QueryCredentials()) + 1;
  418. }
  419. //
  420. // Setup the response blob buffer
  421. //
  422. if ( !buffResponse.Resize( pCredentials->QueryMaxTokenSize() ) )
  423. {
  424. hr = HRESULT_FROM_WIN32( GetLastError() );
  425. goto Failure;
  426. }
  427. //
  428. // Setup the call to AcceptSecurityContext()
  429. //
  430. OutBuffDesc.ulVersion = 0;
  431. OutBuffDesc.cBuffers = 1;
  432. OutBuffDesc.pBuffers = &OutSecBuff;
  433. OutSecBuff.cbBuffer = pCredentials->QueryMaxTokenSize();
  434. OutSecBuff.BufferType = SECBUFFER_TOKEN;
  435. OutSecBuff.pvBuffer = buffResponse.QueryPtr();
  436. InBuffDesc.ulVersion = 0;
  437. InBuffDesc.cBuffers = 1;
  438. InBuffDesc.pBuffers = &InSecBuff;
  439. InSecBuff.cbBuffer = cbFinalBlob;
  440. InSecBuff.BufferType = SECBUFFER_TOKEN;
  441. InSecBuff.pvBuffer = pszFinalBlob;
  442. //
  443. // Let'r rip!
  444. //
  445. //
  446. // Set required context attributes ASC_REQ_EXTENDED_ERROR, this
  447. // allows Negotiate/Kerberos to support time-skew recovery.
  448. //
  449. ss = AcceptSecurityContext( pCredentials->QueryCredHandle(),
  450. fNewConversation ?
  451. NULL :
  452. pSecurityContext->QueryContextHandle(),
  453. &InBuffDesc,
  454. ASC_REQ_EXTENDED_ERROR,
  455. SECURITY_NATIVE_DREP,
  456. &hCtxtHandle,
  457. &OutBuffDesc,
  458. &ContextAttributes,
  459. &Lifetime );
  460. if ( !NT_SUCCESS( ss ) )
  461. {
  462. DBGPRINTF(( DBG_CONTEXT,
  463. "AcceptSecurityContext failed, error %x\n",
  464. ss ));
  465. if ( ss == SEC_E_LOGON_DENIED ||
  466. ss == SEC_E_INVALID_TOKEN )
  467. {
  468. err = GetLastError();
  469. if( err == ERROR_PASSWORD_MUST_CHANGE ||
  470. err == ERROR_PASSWORD_EXPIRED )
  471. {
  472. return HRESULT_FROM_WIN32( err );
  473. }
  474. //
  475. // Could not logon the user because of wrong credentials
  476. //
  477. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  478. Http401BadLogon );
  479. pMainContext->SetErrorStatus( ss );
  480. hr = NO_ERROR;
  481. }
  482. else
  483. {
  484. hr = ss;
  485. }
  486. goto Failure;
  487. }
  488. pSecurityContext->SetContextHandle( hCtxtHandle );
  489. pSecurityContext->SetContextAttributes( ContextAttributes );
  490. if ( ss == SEC_I_CONTINUE_NEEDED ||
  491. ss == SEC_I_COMPLETE_AND_CONTINUE )
  492. {
  493. fNeedContinue = TRUE;
  494. }
  495. else if ( ( ss == SEC_I_COMPLETE_NEEDED ) ||
  496. ( ss == SEC_I_COMPLETE_AND_CONTINUE ) )
  497. {
  498. //
  499. // Now we just need to complete the token (if requested) and
  500. // prepare it for shipping to the other side if needed
  501. //
  502. ss = CompleteAuthToken( &hCtxtHandle,
  503. &OutBuffDesc );
  504. if ( !NT_SUCCESS( ss ))
  505. {
  506. hr = HRESULT_FROM_WIN32( ss );
  507. DBGPRINTF(( DBG_CONTEXT,
  508. "Error on CompleteAuthToken, hr = 0x%x\n",
  509. hr ));
  510. goto Failure;
  511. }
  512. }
  513. //
  514. // Format or copy to the output buffer if we need to reply
  515. //
  516. if ( OutSecBuff.cbBuffer != 0 && fNeedContinue )
  517. {
  518. STACK_BUFFER( buffAuthData, 256 );
  519. hr = pContextState->QueryResponseHeader()->Copy(
  520. pContextState->QueryPackage() );
  521. if( FAILED( hr ) )
  522. {
  523. DBGPRINTF(( DBG_CONTEXT,
  524. "Error copying auth type, hr = 0x%x.\n",
  525. hr ));
  526. goto Failure;
  527. }
  528. hr = pContextState->QueryResponseHeader()->Append( " ", 1 );
  529. if( FAILED( hr ) )
  530. {
  531. DBGPRINTF(( DBG_CONTEXT,
  532. "Error copying auth header, hr = 0x%x.\n",
  533. hr ));
  534. goto Failure;
  535. }
  536. DBG_ASSERT( pCredentials != NULL );
  537. if ( pCredentials->QuerySupportsEncoding() )
  538. {
  539. if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
  540. (DWORD) OutSecBuff.cbBuffer,
  541. &buffAuthData ) )
  542. {
  543. DBGPRINTF(( DBG_CONTEXT,
  544. "Error uuencoding the output buffer.\n"
  545. ));
  546. hr = HRESULT_FROM_WIN32( GetLastError() );
  547. goto Failure;
  548. }
  549. pszFinalBlob = (CHAR *)buffAuthData.QueryPtr();
  550. }
  551. else
  552. {
  553. pszFinalBlob = (CHAR *)OutSecBuff.pvBuffer;
  554. }
  555. hr = pContextState->QueryResponseHeader()->Append( pszFinalBlob );
  556. if( FAILED( hr ) )
  557. {
  558. DBGPRINTF(( DBG_CONTEXT,
  559. "Error appending resp header, hr = 0x%x.\n",
  560. hr ));
  561. goto Failure;
  562. }
  563. //
  564. // Add the WWW-Authenticate header
  565. //
  566. hr = pMainContext->QueryResponse()->SetHeader(
  567. "WWW-Authenticate",
  568. 16, // number of chars in above string
  569. pContextState->QueryResponseHeader()->QueryStr(),
  570. pContextState->QueryResponseHeader()->QueryCCH() );
  571. if ( FAILED( hr ) )
  572. {
  573. goto Failure;
  574. }
  575. //
  576. // Don't let anyone else send back authentication headers when
  577. // the 401 is sent
  578. //
  579. pMainContext->SetProviderHandled( TRUE );
  580. }
  581. if ( !fNeedContinue )
  582. {
  583. //
  584. // Create a user context and setup it up
  585. //
  586. pUserContext = new SSPI_USER_CONTEXT( this );
  587. if ( pUserContext == NULL )
  588. {
  589. hr = HRESULT_FROM_WIN32( GetLastError() );
  590. goto Failure;
  591. }
  592. hr = pUserContext->Create( pSecurityContext,
  593. pMainContext );
  594. if ( FAILED( hr ) )
  595. {
  596. pUserContext->DereferenceUserContext();
  597. pUserContext = NULL;
  598. goto Failure;
  599. }
  600. pMainContext->SetUserContext( pUserContext );
  601. //
  602. // Mark the security context is complete, so we can detect
  603. // reauthentication on the same connection
  604. //
  605. // CODEWORK: Can probably get away will just un-associating/deleting
  606. // the SSPI_SECURITY_CONTEXT now!
  607. //
  608. pSecurityContext->SetIsComplete( TRUE );
  609. }
  610. else
  611. {
  612. //
  613. // We need to send a 401 response to continue the handshake.
  614. // We have already setup the WWW-Authenticate header
  615. //
  616. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  617. Http401BadLogon );
  618. pMainContext->SetFinishedResponse();
  619. }
  620. return NO_ERROR;
  621. Failure:
  622. if ( pSecurityContext != NULL )
  623. {
  624. SetConnectionAuthContext( pMainContext,
  625. NULL );
  626. pSecurityContext = NULL;
  627. }
  628. return hr;
  629. }
  630. HRESULT
  631. SSPI_AUTH_PROVIDER::OnAccessDenied(
  632. W3_MAIN_CONTEXT * pMainContext
  633. )
  634. /*++
  635. Description:
  636. Add WWW-Authenticate headers
  637. Arguments:
  638. pMainContext - main context
  639. Return Value:
  640. HRESULT
  641. --*/
  642. {
  643. MULTISZA * pProviders;
  644. W3_METADATA * pMetaData;
  645. const CHAR * pszProvider;
  646. HRESULT hr;
  647. if ( pMainContext == NULL )
  648. {
  649. DBG_ASSERT( FALSE );
  650. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  651. }
  652. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  653. DBG_ASSERT( pMetaData != NULL );
  654. pProviders = pMetaData->QueryAuthProviders();
  655. if ( pProviders != NULL )
  656. {
  657. pszProvider = pProviders->First();
  658. while ( pszProvider != NULL )
  659. {
  660. hr = pMainContext->QueryResponse()->SetHeader(
  661. "WWW-Authenticate",
  662. 16,
  663. (CHAR *)pszProvider,
  664. strlen(pszProvider) );
  665. if ( FAILED( hr ) )
  666. {
  667. return hr;
  668. }
  669. pszProvider = pProviders->Next( pszProvider );
  670. }
  671. }
  672. return NO_ERROR;
  673. }
  674. //static
  675. HRESULT
  676. SSPI_SECURITY_CONTEXT::Initialize(
  677. VOID
  678. )
  679. /*++
  680. Description:
  681. Global SSPI_SECURITY_CONTEXT initialization
  682. Arguments:
  683. None
  684. Return Value:
  685. HRESULT
  686. --*/
  687. {
  688. ALLOC_CACHE_CONFIGURATION acConfig;
  689. //
  690. // Initialize allocation lookaside
  691. //
  692. acConfig.nConcurrency = 1;
  693. acConfig.nThreshold = 100;
  694. acConfig.cbSize = sizeof( SSPI_SECURITY_CONTEXT );
  695. DBG_ASSERT( sm_pachSSPISecContext == NULL );
  696. sm_pachSSPISecContext = new ALLOC_CACHE_HANDLER(
  697. "SSPI_SECURITY_CONTEXT",
  698. &acConfig );
  699. if ( sm_pachSSPISecContext == NULL )
  700. {
  701. HRESULT hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  702. DBGPRINTF(( DBG_CONTEXT,
  703. "Error initializing sm_pachSSPISecContext. hr = 0x%x\n",
  704. hr ));
  705. return hr;
  706. }
  707. return S_OK;
  708. } // SSPI_SECURITY_CONTEXT::Initialize
  709. //static
  710. VOID
  711. SSPI_SECURITY_CONTEXT::Terminate(
  712. VOID
  713. )
  714. /*++
  715. Routine Description:
  716. Destroy SSPI_SECURITY_CONTEXT globals
  717. Arguments:
  718. None
  719. Return Value:
  720. None
  721. --*/
  722. {
  723. DBG_ASSERT( sm_pachSSPISecContext != NULL );
  724. delete sm_pachSSPISecContext;
  725. sm_pachSSPISecContext = NULL;
  726. }
  727. HRESULT
  728. SSPI_USER_CONTEXT::Create(
  729. SSPI_SECURITY_CONTEXT * pSecurityContext,
  730. W3_MAIN_CONTEXT * pMainContext
  731. )
  732. /*++
  733. Routine Description:
  734. Create an SSPI user context
  735. Arguments:
  736. pSecurityContext - container of important SSPI handles
  737. Return Value:
  738. HRESULT
  739. --*/
  740. {
  741. SECURITY_STATUS ss;
  742. HANDLE hImpersonationToken;
  743. HRESULT hr;
  744. SecPkgContext_Names CredNames;
  745. if ( pSecurityContext == NULL ||
  746. pMainContext == NULL )
  747. {
  748. DBG_ASSERT( pSecurityContext != NULL );
  749. DBG_ASSERT( pMainContext != NULL );
  750. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  751. }
  752. //
  753. // Get the token
  754. //
  755. ss = QuerySecurityContextToken( pSecurityContext->QueryContextHandle(),
  756. &_hImpersonationToken );
  757. if ( ss == SEC_E_INVALID_HANDLE )
  758. {
  759. hr = ss;
  760. DBGPRINTF(( DBG_CONTEXT,
  761. "Error QuerySecurityContextToken, hr = 0x%x.\n",
  762. ss ));
  763. return hr;
  764. }
  765. //
  766. // Disable SeBackupPrivilege for impersonation token to get rid of the
  767. // problem introduced by using FILE_FLAG_BACKUP_SEMANTICS in CreateFileW
  768. // call in W3_FILE_INFO::OpenFile.
  769. //
  770. if ( W3_STATE_AUTHENTICATION::sm_pTokenPrivilege != NULL )
  771. {
  772. AdjustTokenPrivileges(
  773. _hImpersonationToken,
  774. FALSE,
  775. W3_STATE_AUTHENTICATION::sm_pTokenPrivilege,
  776. NULL,
  777. NULL,
  778. NULL );
  779. }
  780. //
  781. // Next, the user name
  782. //
  783. ss = QueryContextAttributes( pSecurityContext->QueryContextHandle(),
  784. SECPKG_ATTR_NAMES,
  785. &CredNames );
  786. if ( !NT_SUCCESS( ss ) )
  787. {
  788. hr = ss;
  789. DBGPRINTF(( DBG_CONTEXT,
  790. "QueryContextAttributes() failed with ss = 0x%x.\n",
  791. ss ));
  792. return hr;
  793. }
  794. else
  795. {
  796. //
  797. // Digest SSP may have a bug in it since the user name returned
  798. // is NULL, workaround here
  799. //
  800. if( CredNames.sUserName )
  801. {
  802. hr = _strUserName.Copy( CredNames.sUserName );
  803. FreeContextBuffer( CredNames.sUserName );
  804. if ( FAILED( hr ) )
  805. {
  806. return hr;
  807. }
  808. }
  809. }
  810. //
  811. // Get the package name
  812. //
  813. hr = _strPackageName.Copy( *(pSecurityContext->QueryCredentials()->QueryPackageName()));
  814. if ( FAILED( hr ) )
  815. {
  816. return hr;
  817. }
  818. //
  819. // Is this token delegatable?
  820. //
  821. _fDelegatable = !!(pSecurityContext->QueryContextAttributes() & ASC_RET_DELEGATE);
  822. //
  823. // If password expiration notification is enabled
  824. // and Url is configured properly
  825. // then save expiration info
  826. //
  827. if( pMainContext->QuerySite()->IsAuthPwdChangeNotificationEnabled() &&
  828. pMainContext->QuerySite()->QueryAdvNotPwdExpUrl() != NULL )
  829. {
  830. SecPkgContext_PasswordExpiry speExpiry;
  831. ss = QueryContextAttributes(
  832. pSecurityContext->QueryContextHandle(),
  833. SECPKG_ATTR_PASSWORD_EXPIRY,
  834. &speExpiry );
  835. if ( ss == STATUS_SUCCESS )
  836. {
  837. memcpy( &_AccountPwdExpiry,
  838. &speExpiry.tsPasswordExpires,
  839. sizeof(speExpiry.tsPasswordExpires) );
  840. _fSetAccountPwdExpiry = TRUE;
  841. }
  842. }
  843. //
  844. // Save a pointer to the security context
  845. //
  846. _pSecurityContext = pSecurityContext;
  847. return NO_ERROR;
  848. }
  849. HANDLE
  850. SSPI_USER_CONTEXT::QueryPrimaryToken(
  851. VOID
  852. )
  853. /*++
  854. Routine Description:
  855. Get primary token for this user
  856. Arguments:
  857. None
  858. Return Value:
  859. Token handle
  860. --*/
  861. {
  862. DBG_ASSERT( _hImpersonationToken != NULL );
  863. if ( _hPrimaryToken == NULL )
  864. {
  865. if ( DuplicateTokenEx( _hImpersonationToken,
  866. TOKEN_ALL_ACCESS,
  867. NULL,
  868. SecurityImpersonation,
  869. TokenPrimary,
  870. &_hPrimaryToken ) )
  871. {
  872. DBG_ASSERT( _hPrimaryToken != NULL );
  873. }
  874. }
  875. return _hPrimaryToken;
  876. }
  877. LARGE_INTEGER *
  878. SSPI_USER_CONTEXT::QueryExpiry(
  879. VOID
  880. )
  881. /*++
  882. Routine Description:
  883. User account expiry information
  884. Arguments:
  885. None
  886. Return Value:
  887. LARGE_INTEGER
  888. --*/
  889. {
  890. if ( _fSetAccountPwdExpiry )
  891. {
  892. return &_AccountPwdExpiry;
  893. }
  894. return NULL;
  895. }