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.

1539 lines
38 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. tokencache.cxx
  5. Abstract:
  6. Ming's token cache refactored for general consumption
  7. Author:
  8. Bilal Alam (balam) May-4-2000
  9. Revision History:
  10. --*/
  11. #include <iis.h>
  12. #include <time.h>
  13. #include <irtltoken.h>
  14. #include "dbgutil.h"
  15. #include <string.hxx>
  16. #include <acache.hxx>
  17. #include <tokencache.hxx>
  18. #include <ntmsv1_0.h>
  19. #include <lm.h>
  20. //
  21. // Security related headers
  22. //
  23. #define SECURITY_WIN32
  24. #include <security.h>
  25. extern "C"
  26. {
  27. #include <secint.h>
  28. }
  29. //
  30. // lonsint.dll related heade files
  31. //
  32. #include <lonsi.hxx>
  33. #include <tslogon.hxx>
  34. ALLOC_CACHE_HANDLER * TOKEN_CACHE_ENTRY::sm_pachTokenCacheEntry = NULL;
  35. HCRYPTPROV TOKEN_CACHE_ENTRY::sm_hCryptProv = NULL;
  36. HRESULT
  37. TOKEN_CACHE_KEY::CreateCacheKey(
  38. WCHAR * pszUserName,
  39. WCHAR * pszDomainName,
  40. DWORD dwLogonMethod
  41. )
  42. /*++
  43. Description:
  44. Build the key used for token cache
  45. Arguments:
  46. pszUserName - User name
  47. pszDomainName - Domain name
  48. dwLogonMethod - Logon method
  49. Return:
  50. HRESULT
  51. --*/
  52. {
  53. HRESULT hr;
  54. WCHAR achNum[ 33 ];
  55. if ( pszUserName == NULL ||
  56. pszDomainName == NULL )
  57. {
  58. DBG_ASSERT( FALSE );
  59. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  60. }
  61. hr = m_strHashKey.Copy( pszUserName );
  62. if ( FAILED( hr ) )
  63. {
  64. return hr;
  65. }
  66. hr = m_strHashKey.Append( pszDomainName );
  67. if ( FAILED( hr ) )
  68. {
  69. return hr;
  70. }
  71. _wcsupr( m_strHashKey.QueryStr() );
  72. _ultow( dwLogonMethod, achNum, 10 );
  73. hr = m_strHashKey.Append( achNum );
  74. if ( FAILED( hr ) )
  75. {
  76. return hr;
  77. }
  78. return hr;
  79. }
  80. //static
  81. HRESULT
  82. TOKEN_CACHE_ENTRY::Initialize(
  83. VOID
  84. )
  85. /*++
  86. Description:
  87. Token entry lookaside initialization
  88. Arguments:
  89. None
  90. Return:
  91. HRESULT
  92. --*/
  93. {
  94. ALLOC_CACHE_CONFIGURATION acConfig;
  95. HRESULT hr;
  96. //
  97. // Initialize allocation lookaside
  98. //
  99. acConfig.nConcurrency = 1;
  100. acConfig.nThreshold = 100;
  101. acConfig.cbSize = sizeof( TOKEN_CACHE_ENTRY );
  102. DBG_ASSERT( sm_pachTokenCacheEntry == NULL );
  103. sm_pachTokenCacheEntry = new ALLOC_CACHE_HANDLER( "TOKEN_CACHE_ENTRY",
  104. &acConfig );
  105. if ( sm_pachTokenCacheEntry == NULL || !sm_pachTokenCacheEntry->IsValid() )
  106. {
  107. if( sm_pachTokenCacheEntry != NULL )
  108. {
  109. delete sm_pachTokenCacheEntry;
  110. sm_pachTokenCacheEntry = NULL;
  111. }
  112. hr = HRESULT_FROM_WIN32( GetLastError() );
  113. DBGPRINTF(( DBG_CONTEXT,
  114. "Error initializing sm_pachTokenCacheEntry. hr = 0x%x\n",
  115. hr ));
  116. return hr;
  117. }
  118. //
  119. // Get a handle to the CSP we'll use for our MD5 hash functions.
  120. //
  121. if ( !CryptAcquireContext( &sm_hCryptProv,
  122. NULL,
  123. NULL,
  124. PROV_RSA_FULL,
  125. CRYPT_VERIFYCONTEXT ) )
  126. {
  127. hr = HRESULT_FROM_WIN32( GetLastError() );
  128. DBGPRINTF(( DBG_CONTEXT,
  129. "CryptAcquireContext() failed. hr = 0x%x\n",
  130. hr ));
  131. return hr;
  132. }
  133. return NO_ERROR;
  134. }
  135. //static
  136. VOID
  137. TOKEN_CACHE_ENTRY::Terminate(
  138. VOID
  139. )
  140. /*++
  141. Description:
  142. Token cache cleanup
  143. Arguments:
  144. None
  145. Return:
  146. None
  147. --*/
  148. {
  149. if ( sm_pachTokenCacheEntry != NULL )
  150. {
  151. delete sm_pachTokenCacheEntry;
  152. sm_pachTokenCacheEntry = NULL;
  153. }
  154. if ( sm_hCryptProv != NULL)
  155. {
  156. CryptReleaseContext( sm_hCryptProv, 0 );
  157. sm_hCryptProv = NULL;
  158. }
  159. }
  160. HRESULT
  161. TOKEN_CACHE_ENTRY::GenMD5Password(
  162. IN WCHAR * pszPassword,
  163. OUT STRA * pstrMD5Password
  164. )
  165. /*++
  166. Description:
  167. Generate MD5 hashed password
  168. Arguments:
  169. pszPassword - Password to be MD5 hashed
  170. pstrMD5Password - MD5 hashed password
  171. Return:
  172. HRESULT
  173. --*/
  174. {
  175. HRESULT hr;
  176. DWORD dwError;
  177. HCRYPTHASH hHash = NULL;
  178. DWORD dwHashDataLen;
  179. STACK_BUFFER( buffHashData, DEFAULT_MD5_HASH_SIZE );
  180. if( pszPassword == NULL || pstrMD5Password == NULL )
  181. {
  182. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  183. }
  184. if ( !CryptCreateHash( sm_hCryptProv,
  185. CALG_MD5,
  186. 0,
  187. 0,
  188. &hHash ) )
  189. {
  190. hr = HRESULT_FROM_WIN32( GetLastError() );
  191. DBGPRINTF((DBG_CONTEXT,
  192. "CryptCreateHash() failed : hr = 0x%x\n",
  193. hr ));
  194. goto exit;
  195. }
  196. if ( !CryptHashData( hHash,
  197. ( BYTE * )pszPassword,
  198. ( DWORD )wcslen( pszPassword ) * sizeof( WCHAR ),
  199. 0 ) )
  200. {
  201. hr = HRESULT_FROM_WIN32( GetLastError() );
  202. DBGPRINTF((DBG_CONTEXT,
  203. "CryptHashData() failed : hr = 0x%x\n",
  204. hr ));
  205. goto exit;
  206. }
  207. dwHashDataLen = DEFAULT_MD5_HASH_SIZE;
  208. if ( !CryptGetHashParam( hHash,
  209. HP_HASHVAL,
  210. ( BYTE * )buffHashData.QueryPtr(),
  211. &dwHashDataLen,
  212. 0 ) )
  213. {
  214. dwError = GetLastError();
  215. if( dwError == ERROR_MORE_DATA )
  216. {
  217. if( !buffHashData.Resize( dwHashDataLen ) )
  218. {
  219. hr = E_OUTOFMEMORY;
  220. goto exit;
  221. }
  222. if( !CryptGetHashParam( hHash,
  223. HP_HASHVAL,
  224. ( BYTE * )buffHashData.QueryPtr(),
  225. &dwHashDataLen,
  226. 0 ) )
  227. {
  228. hr = HRESULT_FROM_WIN32( GetLastError() );
  229. goto exit;
  230. }
  231. }
  232. else
  233. {
  234. hr = HRESULT_FROM_WIN32( dwError );
  235. goto exit;
  236. }
  237. }
  238. //
  239. // Convert binary data to ASCII hex representation
  240. //
  241. hr = ToHex( buffHashData, *pstrMD5Password );
  242. exit:
  243. if( hHash != NULL )
  244. {
  245. CryptDestroyHash( hHash );
  246. }
  247. return hr;
  248. }
  249. HRESULT
  250. TOKEN_CACHE_ENTRY::EqualMD5Password(
  251. IN WCHAR * pszPassword,
  252. OUT BOOL * fEqual
  253. )
  254. /*++
  255. Description:
  256. Does the password in the current entry equal to the one passed in?
  257. Arguments:
  258. pszPassword - Password to be evaluated
  259. fEqual - TRUE if two MD5 hashed password equal
  260. Return:
  261. BOOL
  262. --*/
  263. {
  264. STACK_STRA( strMD5Password, 2 * DEFAULT_MD5_HASH_SIZE + 1 );
  265. HRESULT hr;
  266. hr = GenMD5Password( pszPassword, &strMD5Password );
  267. if( FAILED( hr ) )
  268. {
  269. return hr;
  270. }
  271. *fEqual = m_strMD5Password.Equals( strMD5Password );
  272. return NO_ERROR;
  273. }
  274. HRESULT
  275. TOKEN_CACHE_ENTRY::Create(
  276. IN HANDLE hToken,
  277. IN WCHAR * pszPassword,
  278. IN LARGE_INTEGER * pliPwdExpiry,
  279. IN BOOL fImpersonation
  280. )
  281. /*++
  282. Description:
  283. Initialize a cached token
  284. Arguments:
  285. hToken - Token
  286. liPwdExpiry - Password expiration time
  287. fImpersonation - Is hToken an impersonation token?
  288. Return:
  289. HRESULT
  290. --*/
  291. {
  292. if ( hToken == NULL )
  293. {
  294. DBG_ASSERT( FALSE );
  295. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  296. }
  297. if ( fImpersonation )
  298. {
  299. m_hImpersonationToken = hToken;
  300. }
  301. else
  302. {
  303. m_hPrimaryToken = hToken;
  304. }
  305. memcpy( ( VOID * )&m_liPwdExpiry,
  306. ( VOID * )pliPwdExpiry,
  307. sizeof( LARGE_INTEGER ) );
  308. return GenMD5Password( pszPassword, &m_strMD5Password );
  309. }
  310. HANDLE
  311. TOKEN_CACHE_ENTRY::QueryImpersonationToken(
  312. VOID
  313. )
  314. /*++
  315. Description:
  316. Get impersonation token
  317. Arguments:
  318. None
  319. Return:
  320. Handle to impersonation token
  321. --*/
  322. {
  323. if ( m_hImpersonationToken == NULL )
  324. {
  325. LockCacheEntry();
  326. if ( m_hImpersonationToken == NULL )
  327. {
  328. DBG_ASSERT( m_hPrimaryToken != NULL );
  329. if ( !DuplicateTokenEx( m_hPrimaryToken,
  330. 0,
  331. NULL,
  332. SecurityImpersonation,
  333. TokenImpersonation,
  334. &m_hImpersonationToken ) )
  335. {
  336. DBGPRINTF(( DBG_CONTEXT,
  337. "DuplicateTokenEx failed, GetLastError = %lx\n",
  338. GetLastError() ));
  339. }
  340. else
  341. {
  342. DBG_ASSERT( m_hImpersonationToken != NULL );
  343. }
  344. }
  345. UnlockCacheEntry();
  346. }
  347. if( m_hImpersonationToken && !QueryDisBackupPriToken() )
  348. {
  349. //
  350. // Disable the backup privilege for the token
  351. //
  352. DisableTokenBackupPrivilege( m_hImpersonationToken );
  353. }
  354. return m_hImpersonationToken;
  355. }
  356. HANDLE
  357. TOKEN_CACHE_ENTRY::QueryPrimaryToken(
  358. VOID
  359. )
  360. /*++
  361. Description:
  362. Get primary token
  363. Arguments:
  364. None
  365. Return:
  366. Handle to primary token
  367. --*/
  368. {
  369. if ( m_hPrimaryToken == NULL )
  370. {
  371. LockCacheEntry();
  372. if ( m_hPrimaryToken == NULL )
  373. {
  374. DBG_ASSERT( m_hImpersonationToken != NULL );
  375. if ( !DuplicateTokenEx( m_hImpersonationToken,
  376. 0,
  377. NULL,
  378. SecurityImpersonation,
  379. TokenPrimary,
  380. &m_hPrimaryToken ) )
  381. {
  382. DBGPRINTF(( DBG_CONTEXT,
  383. "DuplicateTokenEx failed, GetLastError = %lx\n",
  384. GetLastError() ));
  385. }
  386. else
  387. {
  388. DBG_ASSERT( m_hPrimaryToken != NULL );
  389. }
  390. }
  391. UnlockCacheEntry();
  392. }
  393. return m_hPrimaryToken;
  394. }
  395. PSID
  396. TOKEN_CACHE_ENTRY::QuerySid(
  397. VOID
  398. )
  399. /*++
  400. Description:
  401. Get the sid for this token
  402. Arguments:
  403. None
  404. Return:
  405. Points to SID buffer owned by this object
  406. --*/
  407. {
  408. BYTE abTokenUser[ SID_DEFAULT_SIZE + sizeof( TOKEN_USER ) ];
  409. TOKEN_USER * pTokenUser = (TOKEN_USER*) abTokenUser;
  410. BOOL fRet;
  411. HANDLE hImpersonation;
  412. DWORD cbBuffer;
  413. hImpersonation = QueryImpersonationToken();
  414. if ( hImpersonation == NULL )
  415. {
  416. return NULL;
  417. }
  418. if ( m_pSid == NULL )
  419. {
  420. LockCacheEntry();
  421. fRet = GetTokenInformation( hImpersonation,
  422. TokenUser,
  423. pTokenUser,
  424. sizeof( abTokenUser ),
  425. &cbBuffer );
  426. if ( fRet )
  427. {
  428. //
  429. // If we can't get the sid, then that is OK. We're return NULL
  430. // and as a result we will do the access check always
  431. //
  432. memcpy( m_abSid,
  433. pTokenUser->User.Sid,
  434. sizeof( m_abSid ) );
  435. m_pSid = m_abSid;
  436. }
  437. UnlockCacheEntry();
  438. }
  439. return m_pSid;
  440. }
  441. HRESULT
  442. TOKEN_CACHE::Initialize(
  443. VOID
  444. )
  445. /*++
  446. Description:
  447. Initialize token cache
  448. Arguments:
  449. None
  450. Return:
  451. HRESULT
  452. --*/
  453. {
  454. HRESULT hr;
  455. LONG lErrorCode;
  456. DWORD dwData;
  457. DWORD dwType;
  458. DWORD cbData = sizeof( DWORD );
  459. DWORD dwFilter;
  460. DWORD dwFlags;
  461. DWORD csecTTL = DEFAULT_CACHED_TOKEN_TTL;
  462. hr = TOKEN_CACHE_ENTRY::Initialize();
  463. if( FAILED( hr ) )
  464. {
  465. DBGPRINTF(( DBG_CONTEXT,
  466. "Token cache entry init failed. hr = 0x%x\n",
  467. hr ));
  468. goto Failure;
  469. }
  470. //
  471. // What is the TTL for the token cache
  472. //
  473. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  474. L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
  475. 0,
  476. KEY_READ,
  477. &m_hKey ) == ERROR_SUCCESS )
  478. {
  479. DBG_ASSERT( m_hKey != NULL );
  480. if ( RegQueryValueEx( m_hKey,
  481. L"LastPriorityUPNLogon",
  482. NULL,
  483. &dwType,
  484. (LPBYTE) &dwData,
  485. &cbData ) == ERROR_SUCCESS &&
  486. dwType == REG_DWORD )
  487. {
  488. m_dwLastPriorityUPNLogon = dwData;
  489. }
  490. if ( RegQueryValueEx( m_hKey,
  491. L"UserTokenTTL",
  492. NULL,
  493. &dwType,
  494. (LPBYTE) &dwData,
  495. &cbData ) == ERROR_SUCCESS &&
  496. dwType == REG_DWORD )
  497. {
  498. csecTTL = dwData;
  499. }
  500. }
  501. //
  502. // We'll use TTL for scavenge period, and expect two inactive periods to
  503. // flush
  504. //
  505. hr = SetCacheConfiguration( csecTTL * 1000,
  506. csecTTL * 1000,
  507. 0,
  508. NULL );
  509. if ( FAILED( hr ) )
  510. {
  511. goto Failure;
  512. }
  513. //
  514. // Create an event
  515. //
  516. m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  517. if( m_hEvent == NULL )
  518. {
  519. hr = HRESULT_FROM_WIN32( GetLastError() );
  520. DBGPRINTF(( DBG_CONTEXT,
  521. "CreateEvent() failed. hr = 0x%x\n",
  522. hr ));
  523. goto Failure;
  524. }
  525. //
  526. // Watch the registry key for a change of value
  527. //
  528. dwFilter = REG_NOTIFY_CHANGE_LAST_SET;
  529. lErrorCode = RegNotifyChangeKeyValue( m_hKey,
  530. TRUE,
  531. dwFilter,
  532. m_hEvent,
  533. TRUE );
  534. if( lErrorCode != ERROR_SUCCESS )
  535. {
  536. hr = HRESULT_FROM_WIN32( GetLastError() );
  537. DBGPRINTF(( DBG_CONTEXT,
  538. "RegNotifyChangeKeyValue failed. hr = 0x%x\n",
  539. hr ));
  540. goto Failure;
  541. }
  542. //
  543. // Register a callback function to wait on the event
  544. //
  545. dwFlags = WT_EXECUTEINPERSISTENTTHREAD;
  546. if( !RegisterWaitForSingleObject(
  547. &m_hWaitObject,
  548. m_hEvent,
  549. ( WAITORTIMERCALLBACK )TOKEN_CACHE::FlushTokenCacheWaitCallback,
  550. this,
  551. INFINITE,
  552. dwFlags ) )
  553. {
  554. hr = HRESULT_FROM_WIN32( GetLastError() );
  555. DBGPRINTF(( DBG_CONTEXT,
  556. "RegisterWaitForSingleObject failed. hr = 0x%x\n",
  557. hr ));
  558. goto Failure;
  559. }
  560. hr = ThreadPoolInitialize( 0 );
  561. if( FAILED(hr) )
  562. {
  563. DBGPRINTF(( DBG_CONTEXT,
  564. "ThreadPoolInitialize failed. hr = 0x%x\n",
  565. hr ));
  566. goto Failure;
  567. }
  568. m_fInitializedThreadPool = TRUE;
  569. return NO_ERROR;
  570. Failure:
  571. TOKEN_CACHE::Terminate();
  572. return hr;
  573. }
  574. VOID
  575. TOKEN_CACHE::Terminate(
  576. VOID
  577. )
  578. /*++
  579. Description:
  580. Terminate token cache
  581. Arguments:
  582. None
  583. Return:
  584. None
  585. --*/
  586. {
  587. if( m_fInitializedThreadPool )
  588. {
  589. DBG_REQUIRE(SUCCEEDED(ThreadPoolTerminate()));
  590. m_fInitializedThreadPool = FALSE;
  591. }
  592. if( m_hWaitObject != NULL )
  593. {
  594. UnregisterWait( m_hWaitObject );
  595. m_hWaitObject = NULL;
  596. }
  597. if( m_hEvent != NULL )
  598. {
  599. CloseHandle( m_hEvent );
  600. m_hEvent = NULL;
  601. }
  602. if( m_hKey != NULL )
  603. {
  604. RegCloseKey( m_hKey );
  605. m_hKey = NULL;
  606. }
  607. return TOKEN_CACHE_ENTRY::Terminate();
  608. }
  609. // static
  610. VOID
  611. WINAPI
  612. TOKEN_CACHE::FlushTokenCacheWaitCallback(
  613. PVOID pParam,
  614. BOOL fWaitFired
  615. )
  616. /*++
  617. Description:
  618. Flush the token cache and reset change notification for the
  619. "UserTokenTTL" registry key.
  620. Arguments:
  621. Not used for now
  622. Return:
  623. None
  624. --*/
  625. {
  626. TOKEN_CACHE * pTokenCache;
  627. LIST_ENTRY listHead;
  628. DWORD dwFilter;
  629. DWORD dwData = 0;
  630. DWORD dwType;
  631. DWORD cbData = sizeof( DWORD );
  632. DWORD dwFlushTokenCache;
  633. UNREFERENCED_PARAMETER( fWaitFired );
  634. pTokenCache = ( TOKEN_CACHE * )pParam;
  635. DBG_ASSERT( pTokenCache != NULL );
  636. DBG_ASSERT( pTokenCache->CheckSignature() );
  637. if ( RegQueryValueEx( pTokenCache->QueryRegKey(),
  638. L"FlushTokenCache",
  639. NULL,
  640. &dwType,
  641. (LPBYTE) &dwData,
  642. &cbData ) == ERROR_SUCCESS &&
  643. dwType == REG_DWORD )
  644. {
  645. dwFlushTokenCache = dwData;
  646. if( dwFlushTokenCache > 0 )
  647. {
  648. //
  649. // Flush the whole token cache since the UserTokenTTL
  650. // has been changed.
  651. //
  652. InitializeListHead( &listHead );
  653. pTokenCache->FlushByRegChange( &listHead );
  654. //
  655. // Remove all cache entries in the cache entry list
  656. //
  657. pTokenCache->CleanupCacheEntryListItems( &listHead );
  658. }
  659. }
  660. //
  661. // Reset the event
  662. //
  663. ResetEvent( pTokenCache->QueryEventHandle() );
  664. //
  665. // Watch the registry key for a change of value
  666. //
  667. dwFilter = REG_NOTIFY_CHANGE_LAST_SET;
  668. RegNotifyChangeKeyValue( pTokenCache->QueryRegKey(),
  669. TRUE,
  670. dwFilter,
  671. pTokenCache->QueryEventHandle(),
  672. TRUE );
  673. }
  674. HRESULT
  675. TOKEN_CACHE::GetCachedToken(
  676. IN LPWSTR pszUserName,
  677. IN LPWSTR pszDomain,
  678. IN LPWSTR pszPassword,
  679. IN DWORD dwLogonMethod,
  680. IN BOOL fUseSubAuth,
  681. IN BOOL fPossibleUPNLogon,
  682. IN PSOCKADDR pSockAddr,
  683. OUT TOKEN_CACHE_ENTRY ** ppCachedToken,
  684. OUT DWORD * pdwLogonError
  685. )
  686. /*++
  687. Description:
  688. Get cached token (the friendly interface for the token cache)
  689. Arguments:
  690. pszUserName - User name
  691. pszDomain - Domain name
  692. pszPassword - Password
  693. dwLogonMethod - Logon method (batch, interactive, etc)
  694. fUseSubAuth - Use subauthenticator to logon the anonymous user
  695. fPossibleUPNLogon - TRUE if we may need to do UPN logon,
  696. otherwise FALSE
  697. psockAddr - Remote IP address, could be IPv4 or IPv6 address
  698. ppCachedToken - Filled with cached token on success
  699. pdwLogonError - Set to logon failure if *ppCacheToken==NULL
  700. Return:
  701. HRESULT
  702. --*/
  703. {
  704. TOKEN_CACHE_KEY tokenKey;
  705. TOKEN_CACHE_ENTRY * pCachedToken = NULL;
  706. HRESULT hr;
  707. HANDLE hToken = NULL;
  708. LARGE_INTEGER liPwdExpiry;
  709. LPVOID pProfile = NULL;
  710. DWORD dwProfileLength = 0;
  711. WCHAR * pszAtSign = NULL;
  712. WCHAR * pDomain[2];
  713. BOOL fRet;
  714. WCHAR * pszSlash = NULL;
  715. CHAR achPassword[32];
  716. STACK_STRU( strPassword, 32 );
  717. STACK_STRA( straUserName, 64 );
  718. STACK_STRA( straDomainName, 64 );
  719. BOOL fEqualPassword = FALSE;
  720. SECURITY_STATUS ss = SEC_E_OK;
  721. if ( pszUserName == NULL ||
  722. pszDomain == NULL ||
  723. pszPassword == NULL ||
  724. ppCachedToken == NULL ||
  725. pdwLogonError == NULL )
  726. {
  727. DBG_ASSERT( FALSE );
  728. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  729. }
  730. liPwdExpiry.HighPart = 0x7fffffff;
  731. liPwdExpiry.LowPart = 0xffffffff;
  732. *ppCachedToken = NULL;
  733. *pdwLogonError = ERROR_SUCCESS;
  734. //
  735. // Find a domain in the username if we don't have one explicitly mentioned
  736. //
  737. if ( pszDomain[ 0 ] == L'\0' )
  738. {
  739. //
  740. // Find the \ in DOMAIN\USERNAME
  741. //
  742. pszSlash = wcschr( pszUserName, L'\\' );
  743. if ( pszSlash != NULL )
  744. {
  745. pszDomain = pszUserName;
  746. pszUserName = pszSlash + 1;
  747. *pszSlash = L'\0';
  748. }
  749. }
  750. if( fUseSubAuth )
  751. {
  752. if( FAILED( hr = straUserName.CopyW( pszUserName ) ) )
  753. {
  754. goto ExitPoint;
  755. }
  756. if( !IISNetUserCookieA( straUserName.QueryStr(),
  757. IIS_SUBAUTH_SEED,
  758. achPassword,
  759. sizeof( achPassword ) ) )
  760. {
  761. *pdwLogonError = GetLastError();
  762. hr = NO_ERROR;
  763. goto ExitPoint;
  764. }
  765. if( FAILED( hr = strPassword.CopyA( achPassword ) ) )
  766. {
  767. goto ExitPoint;
  768. }
  769. if( FAILED( straDomainName.CopyW( pszDomain ) ) )
  770. {
  771. goto ExitPoint;
  772. }
  773. pszPassword = strPassword.QueryStr();
  774. dwLogonMethod = LOGON32_LOGON_IIS_NETWORK;
  775. }
  776. //
  777. // Find the key to look for
  778. //
  779. hr = tokenKey.CreateCacheKey( pszUserName,
  780. pszDomain,
  781. dwLogonMethod );
  782. if ( FAILED( hr ) )
  783. {
  784. goto ExitPoint;
  785. }
  786. //
  787. // Look for it
  788. //
  789. hr = FindCacheEntry( &tokenKey,
  790. (CACHE_ENTRY**) ppCachedToken );
  791. if( SUCCEEDED( hr ) )
  792. {
  793. DBG_ASSERT( *ppCachedToken != NULL );
  794. hr = ( *ppCachedToken )->EqualMD5Password( pszPassword,
  795. &fEqualPassword );
  796. if( FAILED( hr ) )
  797. {
  798. ( *ppCachedToken )->DereferenceCacheEntry();
  799. *ppCachedToken = NULL;
  800. goto ExitPoint;
  801. }
  802. if( fEqualPassword )
  803. {
  804. //
  805. // Cache hit
  806. //
  807. goto ExitPoint;
  808. }
  809. //
  810. // The password does not match
  811. //
  812. ( *ppCachedToken )->DereferenceCacheEntry();
  813. *ppCachedToken = NULL;
  814. }
  815. //
  816. // Ok. It wasn't in the cache, create a token and add it
  817. //
  818. // There are three cases to deal with
  819. //
  820. // 1) We just want the local system token (thats trivial)
  821. // 2) We want to call advapi32!LogonUser for a "normal logon"
  822. // 3) We are doing passport special logon thru lonsint (LsaLogonUser())
  823. //
  824. if ( dwLogonMethod == IIS_LOGON_METHOD_LOCAL_SYSTEM &&
  825. _wcsicmp( L"LocalSystem", pszUserName ) == 0 )
  826. {
  827. if (!OpenProcessToken(
  828. GetCurrentProcess(), // handle to process
  829. TOKEN_ALL_ACCESS, // desired access
  830. &hToken // returned token
  831. ) )
  832. {
  833. //
  834. // If we couldn't logon, then return no error. The caller will
  835. // determine failure due to *ppCachedToken == NULL
  836. //
  837. *pdwLogonError = GetLastError();
  838. hr = NO_ERROR;
  839. goto ExitPoint;
  840. }
  841. //
  842. // OpenProcessToken gives back a primary token
  843. // Below in the call to pCachedToken->Create we decide
  844. // if the token is an impersonation token or not based
  845. // on the LogonMethod. We know this is a primary token
  846. // therefor we set the LogonMethod here
  847. //
  848. dwLogonMethod = LOGON32_LOGON_SERVICE;
  849. }
  850. else if ( dwLogonMethod == IIS_LOGON_METHOD_PASSPORT )
  851. {
  852. //
  853. // Register the remote IP address with LSA so that it can be logged
  854. //
  855. if( pSockAddr != NULL )
  856. {
  857. if( pSockAddr->sa_family == AF_INET )
  858. {
  859. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  860. sizeof( SOCKADDR_IN ) );
  861. }
  862. else if( pSockAddr->sa_family == AF_INET6 )
  863. {
  864. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  865. sizeof( SOCKADDR_IN6 ) );
  866. }
  867. else
  868. {
  869. DBG_ASSERT( FALSE );
  870. }
  871. if( !NT_SUCCESS( ss ) )
  872. {
  873. *pdwLogonError = GetLastError();
  874. hr = NO_ERROR;
  875. goto ExitPoint;
  876. }
  877. }
  878. fRet = IISLogonPassportUserW( pszUserName,
  879. pszDomain,
  880. &hToken );
  881. if ( !fRet )
  882. {
  883. hr = NO_ERROR;
  884. *pdwLogonError = ERROR_LOGON_FAILURE;
  885. goto ExitPoint;
  886. }
  887. }
  888. else
  889. {
  890. if( fUseSubAuth )
  891. {
  892. //
  893. // Try to logon the anonymous user using the IIS
  894. // subauthenticator
  895. //
  896. if( !IISLogonNetUserA( straUserName.QueryStr(),
  897. straDomainName.QueryStr(),
  898. achPassword,
  899. NULL,
  900. IIS_SUBAUTH_ID,
  901. dwLogonMethod,
  902. LOGON32_PROVIDER_DEFAULT,
  903. &hToken,
  904. &liPwdExpiry ) )
  905. {
  906. hr = NO_ERROR;
  907. *pdwLogonError = GetLastError();
  908. goto ExitPoint;
  909. }
  910. }
  911. else
  912. {
  913. pszAtSign = wcschr( pszUserName, L'@' );
  914. if( pszAtSign != NULL && fPossibleUPNLogon )
  915. {
  916. if( !m_dwLastPriorityUPNLogon )
  917. {
  918. //
  919. // Try UPN logon first
  920. //
  921. pDomain[0] = L"";
  922. pDomain[1] = pszDomain;
  923. }
  924. else
  925. {
  926. //
  927. // Try default domain logon first
  928. //
  929. pDomain[0] = pszDomain;
  930. pDomain[1] = L"";
  931. }
  932. //
  933. // Register the remote IP address with LSA so that it can be logged
  934. //
  935. if( pSockAddr != NULL )
  936. {
  937. if( pSockAddr->sa_family == AF_INET )
  938. {
  939. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  940. sizeof( SOCKADDR_IN ) );
  941. }
  942. else if( pSockAddr->sa_family == AF_INET6 )
  943. {
  944. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  945. sizeof( SOCKADDR_IN6 ) );
  946. }
  947. else
  948. {
  949. DBG_ASSERT( FALSE );
  950. }
  951. if( !NT_SUCCESS( ss ) )
  952. {
  953. *pdwLogonError = GetLastError();
  954. hr = NO_ERROR;
  955. goto ExitPoint;
  956. }
  957. }
  958. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  959. fRet = LogonUserEx( pszUserName,
  960. pDomain[0],
  961. pszPassword,
  962. dwLogonMethod,
  963. LOGON32_PROVIDER_DEFAULT,
  964. &hToken,
  965. NULL, // Logon sid
  966. &pProfile,
  967. &dwProfileLength,
  968. NULL // Quota limits
  969. );
  970. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  971. if( !fRet )
  972. {
  973. *pdwLogonError = GetLastError();
  974. if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
  975. {
  976. goto AccountLockedOut;
  977. }
  978. if( *pdwLogonError == ERROR_LOGON_FAILURE )
  979. {
  980. //
  981. // Register the remote IP address with LSA so that it can be logged
  982. //
  983. if( pSockAddr != NULL )
  984. {
  985. if( pSockAddr->sa_family == AF_INET )
  986. {
  987. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  988. sizeof( SOCKADDR_IN ) );
  989. }
  990. else if( pSockAddr->sa_family == AF_INET6 )
  991. {
  992. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  993. sizeof( SOCKADDR_IN6 ) );
  994. }
  995. else
  996. {
  997. DBG_ASSERT( FALSE );
  998. }
  999. if( !NT_SUCCESS( ss ) )
  1000. {
  1001. *pdwLogonError = GetLastError();
  1002. hr = NO_ERROR;
  1003. goto ExitPoint;
  1004. }
  1005. }
  1006. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  1007. fRet = LogonUserEx( pszUserName,
  1008. pDomain[1],
  1009. pszPassword,
  1010. dwLogonMethod,
  1011. LOGON32_PROVIDER_DEFAULT,
  1012. &hToken,
  1013. NULL, // Logon sid
  1014. &pProfile,
  1015. &dwProfileLength,
  1016. NULL // Quota limits
  1017. );
  1018. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  1019. if ( !fRet )
  1020. {
  1021. //
  1022. // If we couldn't logon, then return no error. The caller will
  1023. // determine failure due to *ppCachedToken == NULL
  1024. //
  1025. *pdwLogonError = GetLastError();
  1026. if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
  1027. {
  1028. goto AccountLockedOut;
  1029. }
  1030. hr = NO_ERROR;
  1031. goto ExitPoint;
  1032. }
  1033. }
  1034. else
  1035. {
  1036. hr = NO_ERROR;
  1037. goto ExitPoint;
  1038. }
  1039. }
  1040. }
  1041. else
  1042. {
  1043. //
  1044. // Register the remote IP address with LSA so that it can be logged
  1045. //
  1046. if( pSockAddr != NULL )
  1047. {
  1048. if( pSockAddr->sa_family == AF_INET )
  1049. {
  1050. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  1051. sizeof( SOCKADDR_IN ) );
  1052. }
  1053. else if( pSockAddr->sa_family == AF_INET6 )
  1054. {
  1055. ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
  1056. sizeof( SOCKADDR_IN6 ) );
  1057. }
  1058. else
  1059. {
  1060. DBG_ASSERT( FALSE );
  1061. }
  1062. if( !NT_SUCCESS( ss ) )
  1063. {
  1064. *pdwLogonError = GetLastError();
  1065. hr = NO_ERROR;
  1066. goto ExitPoint;
  1067. }
  1068. }
  1069. //
  1070. // The user name is absolutely not in UPN format
  1071. //
  1072. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  1073. fRet = LogonUserEx( pszUserName,
  1074. pszDomain,
  1075. pszPassword,
  1076. dwLogonMethod,
  1077. LOGON32_PROVIDER_DEFAULT,
  1078. &hToken,
  1079. NULL, // Logon sid
  1080. &pProfile,
  1081. &dwProfileLength,
  1082. NULL // Quota limits
  1083. );
  1084. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  1085. if( !fRet )
  1086. {
  1087. //
  1088. // If we couldn't logon, then return no error. The caller will
  1089. // determine failure due to *ppCachedToken == NULL
  1090. //
  1091. *pdwLogonError = GetLastError();
  1092. if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
  1093. {
  1094. goto AccountLockedOut;
  1095. }
  1096. hr = NO_ERROR;
  1097. goto ExitPoint;
  1098. }
  1099. }
  1100. }
  1101. }
  1102. //
  1103. // Create the entry
  1104. //
  1105. pCachedToken = new TOKEN_CACHE_ENTRY( this );
  1106. if ( pCachedToken == NULL )
  1107. {
  1108. hr = HRESULT_FROM_WIN32( GetLastError() );
  1109. goto ExitPoint;
  1110. }
  1111. //
  1112. // Set the cache key
  1113. //
  1114. hr = pCachedToken->SetCacheKey( &tokenKey );
  1115. if ( FAILED( hr ) )
  1116. {
  1117. goto ExitPoint;
  1118. }
  1119. //
  1120. // Get the password expiration information for the current user
  1121. //
  1122. //
  1123. // Set the token/properties
  1124. //
  1125. hr = pCachedToken->Create( hToken,
  1126. pszPassword,
  1127. pProfile ?
  1128. &(( ( PMSV1_0_INTERACTIVE_PROFILE )pProfile )->PasswordMustChange) :
  1129. &liPwdExpiry,
  1130. dwLogonMethod == LOGON32_LOGON_NETWORK ||
  1131. dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT ||
  1132. dwLogonMethod == IIS_LOGON_METHOD_PASSPORT ||
  1133. dwLogonMethod == LOGON32_LOGON_IIS_NETWORK );
  1134. if ( FAILED( hr ) )
  1135. {
  1136. goto ExitPoint;
  1137. }
  1138. AddCacheEntry( pCachedToken );
  1139. //
  1140. // Return it
  1141. //
  1142. *ppCachedToken = pCachedToken;
  1143. goto ExitPoint;
  1144. AccountLockedOut:
  1145. if( SUCCEEDED( hr ) )
  1146. {
  1147. //
  1148. // Succeeded hr means only passwords don't match
  1149. //
  1150. FlushCacheEntry( &tokenKey );
  1151. }
  1152. hr = NO_ERROR;
  1153. ExitPoint:
  1154. if ( FAILED( hr ) )
  1155. {
  1156. if ( pCachedToken != NULL )
  1157. {
  1158. pCachedToken->DereferenceCacheEntry();
  1159. }
  1160. if ( hToken != NULL )
  1161. {
  1162. CloseHandle( hToken );
  1163. }
  1164. }
  1165. if ( pProfile != NULL )
  1166. {
  1167. LsaFreeReturnBuffer( pProfile );
  1168. }
  1169. //
  1170. // Replace the slash before we continue
  1171. //
  1172. if ( pszSlash != NULL )
  1173. {
  1174. *pszSlash = L'\\';
  1175. }
  1176. return hr;
  1177. }
  1178. HRESULT
  1179. ToHex(
  1180. IN BUFFER & buffSrc,
  1181. OUT STRA & strDst
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. Convert binary data to ASCII hex representation
  1186. Arguments:
  1187. buffSrc - binary data to convert
  1188. strDst - buffer receiving ASCII representation of pSrc
  1189. Return Value:
  1190. HRESULT
  1191. --*/
  1192. {
  1193. #define TOHEX(a) ( (a) >= 10 ? 'a' + (a) - 10 : '0' + (a) )
  1194. HRESULT hr = S_OK;
  1195. PBYTE pSrc;
  1196. PCHAR pDst;
  1197. hr = strDst.Resize( 2 * buffSrc.QuerySize() + 1 );
  1198. if( FAILED( hr ) )
  1199. {
  1200. goto exit;
  1201. }
  1202. pSrc = ( PBYTE ) buffSrc.QueryPtr();
  1203. pDst = strDst.QueryStr();
  1204. for ( UINT i = 0, j = 0 ; i < buffSrc.QuerySize() ; i++ )
  1205. {
  1206. UINT v;
  1207. v = pSrc[ i ] >> 4;
  1208. pDst[ j++ ] = ( CHAR )TOHEX( v );
  1209. v = pSrc[ i ] & 0x0f;
  1210. pDst[ j++ ] = ( CHAR )TOHEX( v );
  1211. }
  1212. DBG_REQUIRE( strDst.SetLen( j ) );
  1213. exit:
  1214. return hr;
  1215. }