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.

3292 lines
80 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. security.c
  7. This module manages security for the Internet Services.
  8. FILE HISTORY:
  9. KeithMo 07-Mar-1993 Created.
  10. MuraliK 05-Jan-1995 Enable statistics query on RPC to go free.
  11. */
  12. #include "tcpdllp.hxx"
  13. #pragma hdrstop
  14. #include <string.h>
  15. #if 1 // DBCS
  16. #include <mbstring.h>
  17. #endif
  18. #include <limits.h>
  19. #include "infosec.hxx"
  20. #include <inetsvcs.h>
  21. #include "TokenAcl.hxx"
  22. //
  23. // Token Cache lock. Controls access to the token cache list
  24. //
  25. #define LockTokenCache() EnterCriticalSection( &csTokenCacheLock )
  26. #define UnlockTokenCache() LeaveCriticalSection( &csTokenCacheLock )
  27. //
  28. // The check period for how long a token can be in the cache. Tokens can
  29. // be in the cache for up to two times this value (in seconds)
  30. //
  31. #define DEFAULT_CACHED_TOKEN_TTL (15 * 60)
  32. //
  33. // Globals
  34. //
  35. CRITICAL_SECTION csTokenCacheLock;
  36. HANDLE g_hProcessImpersonationToken = NULL;
  37. HANDLE g_hProcessPrimaryToken = NULL;
  38. BOOL g_fUseSingleToken = FALSE;
  39. BOOL g_fAlwaysCheckForDuplicateLogon = FALSE;
  40. BOOL g_fUseAdvapi32Logon = FALSE;
  41. BOOL g_fCertCheckForRevocation = FALSE;
  42. TS_TOKEN g_pctProcessToken;
  43. BOOL g_fCertCheckCA = TRUE;
  44. HINSTANCE g_hWinTrust = NULL;
  45. PFN_WinVerifyTrust g_pfnWinVerifyTrust = NULL;
  46. //
  47. // Well-known SIDs.
  48. //
  49. PSID psidWorld;
  50. PSID psidLocalSystem;
  51. PSID psidAdmins;
  52. PSID psidServerOps;
  53. PSID psidPowerUsers;
  54. PSID g_psidGuestUser;
  55. PSID g_psidProcessUser;
  56. # define GUEST_USER_SID_BUFFER_LEN (200)
  57. BYTE g_GuestUserSidBuffer[GUEST_USER_SID_BUFFER_LEN];
  58. //
  59. // NT Security
  60. //
  61. BOOL g_fUseNTSecurity = TRUE;
  62. //
  63. // The API security object. Client access to the TCP Server APIs
  64. // are validated against this object.
  65. //
  66. PSECURITY_DESCRIPTOR sdApiObject;
  67. LUID g_ChangeNotifyPrivilegeTcbValue;
  68. PTOKEN_PRIVILEGES g_pTokPrev = NULL;
  69. //
  70. // This table maps generic rights (like GENERIC_READ) to
  71. // specific rights (like TCP_QUERY_SECURITY).
  72. //
  73. GENERIC_MAPPING TCPApiObjectMapping = {
  74. TCP_GENERIC_READ, // generic read
  75. TCP_GENERIC_WRITE, // generic write
  76. TCP_GENERIC_EXECUTE, // generic execute
  77. TCP_ALL_ACCESS // generic all
  78. };
  79. //
  80. // List of cached tokens, the token list lock and the cookie to the token
  81. // scavenger schedule item. The token cache TTL gets converted to msecs
  82. // during startup
  83. //
  84. BOOL IsTokenCacheInitialized = FALSE;
  85. LIST_ENTRY TokenCacheList;
  86. DWORD dwScheduleCookie = 0;
  87. DWORD cmsecTokenCacheTTL = DEFAULT_CACHED_TOKEN_TTL;
  88. CHAR g_achComputerName[DNLEN+1];
  89. LIST_ENTRY CredentialCacheList;
  90. CRITICAL_SECTION csCredentialCacheLock;
  91. //
  92. // Private prototypes.
  93. //
  94. DWORD
  95. CreateWellKnownSids(
  96. HINSTANCE hDll
  97. );
  98. VOID
  99. FreeWellKnownSids(
  100. VOID
  101. );
  102. DWORD
  103. CreateApiSecurityObject(
  104. VOID
  105. );
  106. VOID
  107. DeleteApiSecurityObject(
  108. VOID
  109. );
  110. TS_TOKEN
  111. ValidateUser(
  112. PCHAR pszDomainName,
  113. PCHAR pszUserName,
  114. PCHAR pszPassword,
  115. BOOL fAnonymous,
  116. BOOL * pfAsGuest,
  117. DWORD dwLogonMethod,
  118. TCHAR * pszWorkstation,
  119. LARGE_INTEGER * pExpiry,
  120. BOOL * pfExpiry,
  121. BOOL fUseSubAuthIfAnonymous
  122. );
  123. VOID EnableTcbPrivilege(
  124. VOID
  125. );
  126. BOOL
  127. BuildAcctDesc(
  128. IN const CHAR * pszUser,
  129. IN const CHAR * pszDomain,
  130. IN const CHAR * pszPwd,
  131. IN BOOL fUseSubAuth,
  132. OUT CHAR * pchAcctDesc, // must be MAX_ACCT_DESC_LEN
  133. OUT LPDWORD pdwAcctDescLen
  134. );
  135. BOOL
  136. AddTokenToCache(
  137. IN const CHAR * pszUser,
  138. IN const CHAR * pszDomain,
  139. IN const CHAR * pszPwd,
  140. IN BOOL fUseSubAuth,
  141. IN HANDLE hToken,
  142. IN DWORD dwLogonMethod,
  143. OUT CACHED_TOKEN * * ppct,
  144. BOOL fCheckAlreadyExist,
  145. LPBOOL pfAlreadyExist
  146. );
  147. BOOL
  148. FindCachedToken(
  149. IN const CHAR * pszUser,
  150. IN const CHAR * pszDomain,
  151. IN const CHAR * pszPwd,
  152. IN BOOL fResetTTL,
  153. IN BOOL fUseSubAuth,
  154. IN DWORD dwLogonMethod,
  155. OUT CACHED_TOKEN * * ppct
  156. );
  157. VOID
  158. WINAPI
  159. TokenCacheScavenger(
  160. IN VOID * pContext
  161. );
  162. extern BOOL g_fIgnoreSC;
  163. //
  164. // Public functions.
  165. //
  166. /*******************************************************************
  167. NAME: InitializeSecurity
  168. SYNOPSIS: Initializes security authentication & impersonation
  169. routines.
  170. RETURNS: DWORD - NO_ERROR if successful, otherwise a Win32
  171. error code.
  172. NOTES: This routine may only be called by a single thread
  173. of execution; it is not necessarily multi-thread safe.
  174. HISTORY:
  175. KeithMo 07-Mar-1993 Created.
  176. ********************************************************************/
  177. DWORD
  178. InitializeSecurity(
  179. IN HINSTANCE hDll
  180. )
  181. {
  182. NTSTATUS ntStatus;
  183. HANDLE hAsExe;
  184. DWORD err;
  185. DWORD nName;
  186. HANDLE hAccToken;
  187. HKEY hKey;
  188. DWORD dwType;
  189. DWORD dwValue;
  190. DWORD nBytes;
  191. //
  192. // Read the registry key to see whether tsunami caching is enabled
  193. //
  194. err = RegOpenKeyEx(
  195. HKEY_LOCAL_MACHINE,
  196. INETA_PARAMETERS_KEY,
  197. 0,
  198. KEY_READ,
  199. &hKey
  200. );
  201. if ( err == ERROR_SUCCESS ) {
  202. nBytes = sizeof(dwValue);
  203. err = RegQueryValueEx(
  204. hKey,
  205. INETA_W3ONLY_NO_AUTH,
  206. NULL,
  207. &dwType,
  208. (LPBYTE)&dwValue,
  209. &nBytes
  210. );
  211. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  212. g_fW3OnlyNoAuth = (BOOL)!!dwValue;
  213. if ( g_fW3OnlyNoAuth ) {
  214. DbgPrint("W3OnlyNoAuth set to TRUE in Registry.\n");
  215. } else {
  216. DbgPrint("W3OnlyNoAuth set to FALSE in Registry.\n");
  217. }
  218. }
  219. nBytes = sizeof(dwValue);
  220. err = RegQueryValueEx(
  221. hKey,
  222. INETA_ALWAYS_CHECK_FOR_DUPLICATE_LOGON,
  223. NULL,
  224. &dwType,
  225. (LPBYTE)&dwValue,
  226. &nBytes
  227. );
  228. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  229. g_fAlwaysCheckForDuplicateLogon = (BOOL)dwValue;
  230. }
  231. nBytes = sizeof(dwValue);
  232. err = RegQueryValueEx(
  233. hKey,
  234. INETA_USE_ADVAPI32_LOGON,
  235. NULL,
  236. &dwType,
  237. (LPBYTE)&dwValue,
  238. &nBytes
  239. );
  240. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  241. g_fUseAdvapi32Logon = (BOOL)dwValue;
  242. }
  243. nBytes = sizeof(dwValue);
  244. err = RegQueryValueEx(
  245. hKey,
  246. INETA_CHECK_CERT_REVOCATION,
  247. NULL,
  248. &dwType,
  249. (LPBYTE)&dwValue,
  250. &nBytes
  251. );
  252. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  253. g_fCertCheckForRevocation = (BOOL)dwValue;
  254. }
  255. nBytes = sizeof(dwValue);
  256. err = RegQueryValueEx(
  257. hKey,
  258. "CertCheckCA",
  259. NULL,
  260. &dwType,
  261. (LPBYTE)&dwValue,
  262. &nBytes
  263. );
  264. if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
  265. g_fCertCheckCA = (BOOL)dwValue;
  266. }
  267. RegCloseKey( hKey );
  268. }
  269. IF_DEBUG( DLL_SECURITY )
  270. {
  271. DBGPRINTF(( DBG_CONTEXT, "Initializing security\n" ));
  272. }
  273. IsTokenCacheInitialized = TRUE;
  274. InitializeListHead( &TokenCacheList );
  275. INITIALIZE_CRITICAL_SECTION( &csTokenCacheLock );
  276. InitializeListHead( &CredentialCacheList );
  277. INITIALIZE_CRITICAL_SECTION( &csCredentialCacheLock );
  278. if ( g_fW3OnlyNoAuth ) {
  279. DBGPRINTF((DBG_CONTEXT,
  280. "InitializeSecurity: NT Security disabled for W3OnlyNoAuth\n"));
  281. g_fUseSingleToken = TRUE;
  282. if ( !(g_pctProcessToken = new CACHED_TOKEN) )
  283. {
  284. return ERROR_NOT_ENOUGH_MEMORY;
  285. }
  286. g_pctProcessToken->_cRef = INT_MAX/2;
  287. InitializeListHead( &g_pctProcessToken->_ListEntry );
  288. if ( !OpenProcessToken (
  289. GetCurrentProcess(),
  290. TOKEN_DUPLICATE|TOKEN_IMPERSONATE|TOKEN_QUERY,
  291. &hAccToken
  292. ) )
  293. {
  294. DBGPRINTF((DBG_CONTEXT, "fail OpenProcessToken\n"));
  295. return GetLastError();
  296. }
  297. if ( !pfnDuplicateTokenEx( hAccToken,
  298. 0,
  299. NULL,
  300. SecurityImpersonation,
  301. TokenPrimary,
  302. &g_hProcessPrimaryToken ))
  303. {
  304. DBGPRINTF((DBG_CONTEXT, "fail pfnDuplicateTokenEx primary\n"));
  305. CloseHandle( hAccToken );
  306. return GetLastError();
  307. }
  308. if ( !pfnDuplicateTokenEx( hAccToken,
  309. 0,
  310. NULL,
  311. SecurityImpersonation,
  312. TokenImpersonation,
  313. &g_hProcessImpersonationToken ))
  314. {
  315. DBGPRINTF((DBG_CONTEXT, "fail pfnDuplicateTokenEx impersonate\n"));
  316. CloseHandle( hAccToken );
  317. CloseHandle( g_hProcessPrimaryToken );
  318. return GetLastError();
  319. }
  320. err = CreateWellKnownSids( hDll );
  321. if ( err != NO_ERROR ) {
  322. DBGPRINTF((DBG_CONTEXT,"CreateWellKnownSids failed with %d\n",err));
  323. goto exit;
  324. }
  325. //
  326. // Create the API security object.
  327. //
  328. err = CreateApiSecurityObject();
  329. if ( err != NO_ERROR ) {
  330. DBGPRINTF((DBG_CONTEXT,"CreateApiSecurityObjects failed with %d\n",err));
  331. goto exit;
  332. }
  333. g_pctProcessToken->_hToken = g_hProcessPrimaryToken;
  334. g_pctProcessToken->m_hImpersonationToken = g_hProcessImpersonationToken;
  335. return(NO_ERROR);
  336. }
  337. if ( TsIsWindows95() ) {
  338. g_fIgnoreSC = TRUE;
  339. g_fUseNTSecurity = FALSE;
  340. return(NO_ERROR);
  341. }
  342. //
  343. // See if we should ignore the service controller (useful for running
  344. // as an .exe). Inetsvcs.exe creates an event with this name. So
  345. // if the semaphore creation fails, then we know we're running as an .exe.
  346. //
  347. if ( !(hAsExe = CreateSemaphore( NULL, 1, 1, IIS_AS_EXE_OBJECT_NAME )))
  348. {
  349. g_fIgnoreSC = (GetLastError() == ERROR_INVALID_HANDLE);
  350. }
  351. else
  352. {
  353. DBG_REQUIRE( CloseHandle( hAsExe ) );
  354. }
  355. if ( g_fIgnoreSC )
  356. {
  357. //
  358. // If the service is running as an .exe, we need to enable
  359. // the SeTcbPrivilege (Act as part of the operating system).
  360. // We don't worry about disabling the privilege as this is
  361. // only used in test debug code
  362. //
  363. EnableTcbPrivilege();
  364. }
  365. //
  366. // Create well-known SIDs.
  367. //
  368. err = CreateWellKnownSids( hDll );
  369. if ( err != NO_ERROR ) {
  370. DBGPRINTF((DBG_CONTEXT,"CreateWellKnownSids failed with %d\n",err));
  371. goto exit;
  372. }
  373. //
  374. // Create the API security object.
  375. //
  376. err = CreateApiSecurityObject();
  377. if ( err != NO_ERROR ) {
  378. DBGPRINTF((DBG_CONTEXT,"CreateApiSecurityObjects failed with %d\n",err));
  379. goto exit;
  380. }
  381. {
  382. HKEY hkey;
  383. //
  384. // Get the default token TTL, must be at least one second
  385. //
  386. if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  387. INETA_PARAMETERS_KEY,
  388. 0,
  389. KEY_READ,
  390. &hkey )) {
  391. cmsecTokenCacheTTL = ReadRegistryDword( hkey,
  392. "UserTokenTTL",
  393. DEFAULT_CACHED_TOKEN_TTL);
  394. RegCloseKey( hkey );
  395. }
  396. cmsecTokenCacheTTL = max( 1, cmsecTokenCacheTTL );
  397. cmsecTokenCacheTTL *= 1000;
  398. IF_DEBUG( DLL_SECURITY )
  399. {
  400. DBGPRINTF(( DBG_CONTEXT,
  401. "Scheduling token cached scavenger to %d seconds\n",
  402. cmsecTokenCacheTTL/1000 ));
  403. }
  404. //
  405. // Schedule a work item for the token scavenger
  406. //
  407. dwScheduleCookie = ScheduleWorkItem( TokenCacheScavenger,
  408. NULL,
  409. cmsecTokenCacheTTL,
  410. TRUE ); // Periodic
  411. }
  412. pfnLogon32Initialize( NULL, DLL_PROCESS_ATTACH, NULL );
  413. if ( g_pTokPrev = (PTOKEN_PRIVILEGES)LocalAlloc( LMEM_FIXED,
  414. sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) )
  415. {
  416. if ( !LookupPrivilegeValue(
  417. NULL,
  418. "SeChangeNotifyPrivilege",
  419. &g_ChangeNotifyPrivilegeTcbValue
  420. ) )
  421. {
  422. g_pTokPrev->PrivilegeCount = 0;
  423. }
  424. else
  425. {
  426. g_pTokPrev->PrivilegeCount = 1;
  427. g_pTokPrev->Privileges[0].Luid = g_ChangeNotifyPrivilegeTcbValue;
  428. g_pTokPrev->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  429. }
  430. }
  431. nName = sizeof(g_achComputerName);
  432. if ( !GetComputerName( g_achComputerName, &nName ) )
  433. {
  434. g_achComputerName[0] = '\0';
  435. }
  436. g_hWinTrust = LoadLibrary( "wintrust.dll" );
  437. if ( g_hWinTrust != NULL )
  438. {
  439. g_pfnWinVerifyTrust = (PFN_WinVerifyTrust)GetProcAddress( g_hWinTrust, "WinVerifyTrust" );
  440. }
  441. //
  442. // Success!
  443. //
  444. IF_DEBUG( DLL_SECURITY )
  445. {
  446. DBGPRINTF(( DBG_CONTEXT, "Security initialized\n" ));
  447. }
  448. exit:
  449. return err;
  450. } // InitializeSecurity
  451. /*******************************************************************
  452. NAME: TerminateSecurity
  453. SYNOPSIS: Terminate security authentication & impersonation
  454. routines.
  455. NOTES: This routine may only be called by a single thread
  456. of execution; it is not necessarily multi-thread safe.
  457. HISTORY:
  458. KeithMo 07-Mar-1993 Created.
  459. ********************************************************************/
  460. VOID
  461. TerminateSecurity(
  462. VOID
  463. )
  464. {
  465. CACHED_TOKEN * pct;
  466. CACHED_CREDENTIAL * pcred;
  467. DBGPRINTF((DBG_CONTEXT,"TerminateSecurity called\n"));
  468. IF_DEBUG( DLL_SECURITY )
  469. {
  470. DBGPRINTF(( DBG_CONTEXT,
  471. "Terminating security\n" ));
  472. }
  473. //
  474. // Delete any tokens still in the cache
  475. //
  476. if ( IsTokenCacheInitialized )
  477. {
  478. LockTokenCache();
  479. while ( !IsListEmpty( &TokenCacheList ))
  480. {
  481. pct = CONTAINING_RECORD( TokenCacheList.Flink,
  482. CACHED_TOKEN,
  483. _ListEntry );
  484. RemoveEntryList( &pct->_ListEntry );
  485. pct->_ListEntry.Flink = NULL;
  486. //
  487. // If the ref count isn't zero then somebody didn't delete all of
  488. // their tokens
  489. //
  490. DBG_ASSERT( pct->_cRef == 1 );
  491. CACHED_TOKEN::Dereference( pct );
  492. }
  493. UnlockTokenCache();
  494. DeleteCriticalSection( &csTokenCacheLock );
  495. }
  496. //
  497. // Delete any credential in the cache
  498. //
  499. EnterCriticalSection( &csCredentialCacheLock );
  500. while ( !IsListEmpty( &CredentialCacheList ))
  501. {
  502. pcred = CONTAINING_RECORD( CredentialCacheList.Flink,
  503. CACHED_CREDENTIAL,
  504. _ListEntry );
  505. RemoveEntryList( &pcred->_ListEntry );
  506. pcred->_ListEntry.Flink = NULL;
  507. delete pcred;
  508. }
  509. LeaveCriticalSection( &csCredentialCacheLock );
  510. DeleteCriticalSection( &csCredentialCacheLock );
  511. if ( g_fUseSingleToken ) {
  512. CloseHandle( g_hProcessImpersonationToken );
  513. CloseHandle( g_hProcessPrimaryToken );
  514. delete g_pctProcessToken;
  515. return;
  516. }
  517. if ( !g_fUseNTSecurity ) {
  518. return;
  519. }
  520. FreeWellKnownSids();
  521. DeleteApiSecurityObject();
  522. //
  523. // Remove the scheduled scavenger
  524. //
  525. if ( dwScheduleCookie )
  526. {
  527. RemoveWorkItem( dwScheduleCookie );
  528. }
  529. if ( g_pTokPrev )
  530. {
  531. LocalFree( g_pTokPrev );
  532. g_pTokPrev = NULL;
  533. }
  534. if ( g_hWinTrust != NULL )
  535. {
  536. g_pfnWinVerifyTrust = NULL;
  537. FreeLibrary( g_hWinTrust );
  538. g_hWinTrust = NULL;
  539. }
  540. pfnLogon32Initialize( NULL, DLL_PROCESS_DETACH, NULL );
  541. IF_DEBUG( DLL_SECURITY )
  542. {
  543. DBGPRINTF(( DBG_CONTEXT,
  544. "Security terminated\n" ));
  545. }
  546. } // TerminateSecurity
  547. /*******************************************************************
  548. NAME: TsLogonUser
  549. SYNOPSIS: Validates a user's credentials, then sets the
  550. impersonation for the current thread. In effect,
  551. the current thread "becomes" the user.
  552. ENTRY: pUserData - The user initiating the request (NULL for
  553. the default account).
  554. pszPassword - The user's password. May be NULL.
  555. pfAsGuest - Will receive TRUE if the user was validated
  556. with guest privileges.
  557. pfAsAnonymous - Will receive TRUE if the user received the
  558. services anonymous token
  559. pszWorkstation - workstation name for remote user
  560. can be NULL if default ( local computer) to be used
  561. pExpiry - updated with pwd expiration date/time
  562. pfExpiryAvailable - updated with TRUE if pwd expiration
  563. date/time available
  564. RETURNS: HANDLE - Token handle to use for impersonation or NULL
  565. if the user couldn't be validated. Call GetLastError
  566. for more information.
  567. HISTORY:
  568. KeithMo 18-Mar-1993 Created.
  569. Johnl 14-Oct-1994 Mutilated for TCPSvcs
  570. ********************************************************************/
  571. TS_TOKEN
  572. TsLogonUser(
  573. IN CHAR * pszUser,
  574. IN CHAR * pszPassword,
  575. OUT BOOL * pfAsGuest,
  576. OUT BOOL * pfAsAnonymous,
  577. IN PIIS_SERVER_INSTANCE psi,
  578. PTCP_AUTHENT_INFO pTAI,
  579. IN CHAR * pszWorkstation,
  580. OUT LARGE_INTEGER * pExpiry,
  581. OUT BOOL * pfExpiryAvailable
  582. )
  583. {
  584. CHAR szAnonPwd[PWLEN+1];
  585. CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
  586. CHAR szAnonUser[UNLEN+1];
  587. CHAR * pszUserOnly;
  588. CHAR * pszDomain;
  589. TS_TOKEN hToken;
  590. BOOL fUseDefaultDomain = TRUE;
  591. TCP_AUTHENT_INFO InstanceAuthentInfo;
  592. if ( g_fUseSingleToken ) {
  593. *pfAsGuest = TRUE;
  594. *pfAsAnonymous = TRUE;
  595. *pfExpiryAvailable = FALSE;
  596. CACHED_TOKEN::Reference( g_pctProcessToken );
  597. return g_pctProcessToken;
  598. }
  599. //
  600. // if no NT Security, bail
  601. //
  602. if ( !g_fUseNTSecurity ) {
  603. *pfAsGuest = TRUE;
  604. *pfAsAnonymous = TRUE;
  605. *pfExpiryAvailable = FALSE;
  606. return((TS_TOKEN)BOGUS_WIN95_TOKEN);
  607. }
  608. // If the client didn't pass in metabase info, grab what we need from
  609. // the instance.
  610. //
  611. if (pTAI == NULL)
  612. {
  613. InstanceAuthentInfo.strAnonUserName.Copy( "iusr_xxx" ); //(CHAR *)psi->QueryAnonUserName();
  614. InstanceAuthentInfo.strAnonUserPassword.Copy( "" );
  615. InstanceAuthentInfo.strDefaultLogonDomain.Copy( "" ); //(CHAR *)psi->QueryDefaultLogonDomain();
  616. InstanceAuthentInfo.dwLogonMethod = MD_LOGON_INTERACTIVE; //psi->QueryLogonMethod();
  617. InstanceAuthentInfo.fDontUseAnonSubAuth = FALSE;
  618. pTAI = &InstanceAuthentInfo;
  619. }
  620. //
  621. // Make a quick copy of the anonymous user for this server for later
  622. // usage
  623. //
  624. memcpy( szAnonUser,
  625. pTAI->strAnonUserName.QueryStr(),
  626. pTAI->strAnonUserName.QueryCCH() + sizeof(CHAR) );
  627. memcpy( szAnonPwd,
  628. pTAI->strAnonUserPassword.QueryStr(),
  629. pTAI->strAnonUserPassword.QueryCCH() + sizeof(CHAR) );
  630. //
  631. // Empty user defaults to the anonymous user
  632. //
  633. if ( !pszUser || *pszUser == '\0' )
  634. {
  635. pszUser = szAnonUser;
  636. pszPassword = szAnonPwd;
  637. fUseDefaultDomain = FALSE;
  638. *pfAsAnonymous = TRUE;
  639. }
  640. else
  641. {
  642. *pfAsAnonymous = FALSE;
  643. }
  644. //
  645. // Validate parameters & state.
  646. //
  647. if ( strlen(pszUser) >= sizeof(szDomainAndUser) )
  648. {
  649. SetLastError( ERROR_INVALID_PARAMETER );
  650. return NULL;
  651. }
  652. DBG_ASSERT( pfAsGuest != NULL );
  653. DBG_ASSERT( pfAsAnonymous != NULL );
  654. if( pszPassword == NULL )
  655. {
  656. pszPassword = "";
  657. }
  658. else
  659. {
  660. if ( strlen(pszPassword) >= PWLEN )
  661. {
  662. SetLastError( ERROR_INVALID_PARAMETER );
  663. return NULL;
  664. }
  665. }
  666. //
  667. // Save a copy of the domain\user so we can squirrel around
  668. // with it a bit.
  669. //
  670. int cL = 0;
  671. PCSTR pL = NULL;
  672. //
  673. // prepend default logon domain if no domain
  674. // and the default user name was not used
  675. //
  676. if ( fUseDefaultDomain
  677. && strchr( pszUser, '/' ) == NULL
  678. #if 1 // DBCS enabling for user name
  679. && _mbschr( (PUCHAR)pszUser, '\\' ) == NULL )
  680. #else
  681. && strchr( pszUser, '\\' ) == NULL )
  682. #endif
  683. {
  684. PCSTR pD = pTAI->strDefaultLogonDomain.QueryStr();
  685. if ( pD[0] != '\0' )
  686. {
  687. #if 1 // DBCS enabling for user name
  688. if ( !( pL = (PCHAR)_mbschr( (PUCHAR)pD, '\\' ) ) )
  689. #else
  690. if ( !( pL = strchr( pD, '\\' ) ) )
  691. #endif
  692. {
  693. cL = strlen( pD );
  694. memcpy( szDomainAndUser, pD, cL );
  695. szDomainAndUser[ cL++ ] = '\\';
  696. }
  697. }
  698. }
  699. if( pL )
  700. {
  701. //
  702. // Handle UPN case
  703. //
  704. pszUserOnly = pszUser;
  705. pszDomain = "";
  706. }
  707. else
  708. {
  709. strcpy( szDomainAndUser + cL, pszUser );
  710. //
  711. // Crack the name into domain/user components.
  712. //
  713. if ( !CrackUserAndDomain( szDomainAndUser,
  714. &pszUserOnly,
  715. &pszDomain ))
  716. {
  717. return NULL;
  718. }
  719. }
  720. //
  721. // Validate the domain/user/password combo and create
  722. // an impersonation token.
  723. //
  724. hToken = ValidateUser( pszDomain,
  725. pszUserOnly,
  726. pszPassword,
  727. *pfAsAnonymous,
  728. pfAsGuest,
  729. pTAI->dwLogonMethod,
  730. pszWorkstation,
  731. pExpiry,
  732. pfExpiryAvailable,
  733. !pTAI->fDontUseAnonSubAuth
  734. );
  735. ZeroMemory( szAnonPwd, strlen(szAnonPwd) );
  736. if( hToken == NULL )
  737. {
  738. STR strError;
  739. const CHAR * psz[2];
  740. DWORD dwErr = GetLastError();
  741. psi->LoadStr( strError, dwErr, FALSE );
  742. psz[0] = pszUser;
  743. psz[1] = strError.QueryStr();
  744. psi->m_Service->LogEvent(
  745. INET_SVCS_FAILED_LOGON,
  746. 2,
  747. psz,
  748. dwErr );
  749. //
  750. // Validation failure.
  751. //
  752. if ( dwErr == ERROR_LOGON_TYPE_NOT_GRANTED ||
  753. dwErr == ERROR_ACCOUNT_DISABLED )
  754. {
  755. SetLastError( ERROR_ACCESS_DENIED );
  756. }
  757. else
  758. {
  759. //
  760. // Reset LastError(), as LogEvent() may have overwritten it
  761. // e.g log is full
  762. //
  763. SetLastError( dwErr );
  764. }
  765. return NULL;
  766. }
  767. //
  768. // Success!
  769. //
  770. return hToken;
  771. } // LogonUser
  772. /*******************************************************************
  773. NAME: ValidateUser
  774. SYNOPSIS: Validate a given domain/user/password tuple.
  775. ENTRY: pszDomainName - The user's domain (NULL = current).
  776. pszUserName - The user's name.
  777. pszPassword - The user's (plaintext) password.
  778. fAnonymous - TRUE if this is the anonymous user
  779. pfAsGuest - Will receive TRUE if the user was validated
  780. with guest privileges.
  781. dwLogonMethod - interactive or batch
  782. pszWorkstation - workstation name for remote user
  783. can be NULL if default ( local computer) to be used
  784. pExpiry - updated with pwd expiration date/time
  785. pfExpiryAvailable - updated with TRUE if pwd expiration
  786. date/time available
  787. fUseSubAuthIfAnonymous - TRUE if logon anonymous user
  788. using IIS sub-auth
  789. RETURNS: HANDLE - An impersonation token, NULL if user cannot
  790. be validated. Call GetLastError for more information.
  791. HISTORY:
  792. KeithMo 07-Mar-1993 Created.
  793. ********************************************************************/
  794. TS_TOKEN ValidateUser(
  795. PCHAR pszDomainName,
  796. PCHAR pszUserName,
  797. PCHAR pszPassword,
  798. BOOL fAnonymous,
  799. BOOL * pfAsGuest,
  800. DWORD dwLogonMethod,
  801. CHAR * pszWorkstation,
  802. LARGE_INTEGER * pExpiry,
  803. BOOL * pfExpiryAvailable,
  804. BOOL fUseSubAuthIfAnonymous
  805. )
  806. {
  807. CACHED_TOKEN * pct = NULL;
  808. HANDLE hToken;
  809. HANDLE hImpersonationToken = NULL;
  810. BOOL fExpiry = FALSE;
  811. DWORD dwSubAuth = 0;
  812. CHAR achCookie[32];
  813. BOOL fExist;
  814. if ( pfExpiryAvailable )
  815. {
  816. *pfExpiryAvailable = FALSE;
  817. }
  818. if ( fAnonymous && fUseSubAuthIfAnonymous )
  819. {
  820. if ( !pfnNetUserCookieA( pszUserName,
  821. IIS_SUBAUTH_SEED,
  822. achCookie,
  823. sizeof(achCookie ) ) )
  824. {
  825. return FALSE;
  826. }
  827. dwSubAuth = IIS_SUBAUTH_ID;
  828. pszPassword = achCookie;
  829. dwLogonMethod = LOGON32_LOGON_IIS_NETWORK;
  830. }
  831. //
  832. // Is it in the cache? References the token if we find it
  833. //
  834. if ( FindCachedToken( pszUserName,
  835. pszDomainName,
  836. pszPassword,
  837. fAnonymous, // Reset the TTL if anonymous
  838. fAnonymous && fUseSubAuthIfAnonymous,
  839. dwLogonMethod,
  840. &pct ))
  841. {
  842. *pfAsGuest = pct->IsGuest();
  843. if ( NULL != pExpiry) {
  844. memcpy( pExpiry, pct->QueryExpiry(), sizeof(LARGE_INTEGER) );
  845. }
  846. if ( pfExpiryAvailable )
  847. {
  848. *pfExpiryAvailable = TRUE;
  849. }
  850. return pct;
  851. }
  852. if ( (dwLogonMethod == LOGON32_LOGON_NETWORK ||
  853. dwLogonMethod == LOGON32_LOGON_BATCH ||
  854. dwLogonMethod == LOGON32_LOGON_INTERACTIVE ||
  855. dwLogonMethod == LOGON32_LOGON_IIS_NETWORK ||
  856. dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT ) &&
  857. !g_fUseAdvapi32Logon )
  858. {
  859. if ( !pfnLogonNetUserA( pszUserName,
  860. pszDomainName,
  861. pszPassword,
  862. pszWorkstation,
  863. dwSubAuth,
  864. dwLogonMethod,
  865. LOGON32_PROVIDER_DEFAULT,
  866. &hToken,
  867. pExpiry ))
  868. {
  869. if ( fAnonymous &&
  870. ( GetLastError() == ERROR_LOGON_TYPE_NOT_GRANTED ) &&
  871. ( dwLogonMethod == LOGON32_LOGON_INTERACTIVE ) )
  872. {
  873. // try again
  874. dwLogonMethod = LOGON32_LOGON_BATCH;
  875. if ( !pfnLogonNetUserA( pszUserName,
  876. pszDomainName,
  877. pszPassword,
  878. pszWorkstation,
  879. dwSubAuth,
  880. dwLogonMethod,
  881. LOGON32_PROVIDER_DEFAULT,
  882. &hToken,
  883. pExpiry ))
  884. {
  885. return NULL;
  886. }
  887. }
  888. else
  889. {
  890. return NULL;
  891. }
  892. }
  893. fExpiry = TRUE;
  894. if ( pfExpiryAvailable )
  895. {
  896. *pfExpiryAvailable = TRUE;
  897. }
  898. }
  899. else
  900. {
  901. if ( !LogonUserA( pszUserName,
  902. pszDomainName,
  903. pszPassword,
  904. dwLogonMethod,
  905. LOGON32_PROVIDER_WINNT50,
  906. &hToken ))
  907. {
  908. return NULL;
  909. }
  910. }
  911. if ( dwLogonMethod == LOGON32_LOGON_NETWORK ||
  912. dwLogonMethod == LOGON32_LOGON_IIS_NETWORK ||
  913. dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT )
  914. {
  915. hImpersonationToken = hToken;
  916. if ( !pfnDuplicateTokenEx( hImpersonationToken,
  917. TOKEN_ALL_ACCESS,
  918. NULL,
  919. SecurityDelegation,
  920. TokenPrimary,
  921. &hToken ))
  922. {
  923. if ( !pfnDuplicateTokenEx( hImpersonationToken,
  924. TOKEN_ALL_ACCESS,
  925. NULL,
  926. SecurityImpersonation,
  927. TokenPrimary,
  928. &hToken ))
  929. {
  930. CloseHandle( hImpersonationToken );
  931. return NULL;
  932. }
  933. }
  934. }
  935. *pfAsGuest = IsGuestUser(hToken);
  936. //
  937. // Add this new token to the cache, hToken gets replaced by the
  938. // cached token object
  939. //
  940. if ( !AddTokenToCache( pszUserName,
  941. pszDomainName,
  942. pszPassword,
  943. fAnonymous && fUseSubAuthIfAnonymous,
  944. hToken,
  945. dwLogonMethod,
  946. &pct,
  947. g_fAlwaysCheckForDuplicateLogon | fAnonymous,
  948. &fExist ))
  949. {
  950. if ( hImpersonationToken != NULL )
  951. {
  952. CloseHandle( hImpersonationToken );
  953. }
  954. CloseHandle( hToken );
  955. return NULL;
  956. }
  957. pct->SetGuest(*pfAsGuest);
  958. if ( fExpiry )
  959. {
  960. pct->SetExpiry( pExpiry );
  961. }
  962. //
  963. // DuplicateToken() apparently returns an impersonated token
  964. // so it is not necessary to call pfnDuplicateTokenEx
  965. //
  966. if ( !fExist )
  967. {
  968. if ( hImpersonationToken == NULL
  969. && !pfnDuplicateTokenEx( hToken, // hSourceToken
  970. TOKEN_ALL_ACCESS,
  971. NULL,
  972. SecurityDelegation, // Obtain impersonation
  973. TokenImpersonation,
  974. &hImpersonationToken) // hDestinationToken
  975. ) {
  976. if ( !pfnDuplicateTokenEx( hToken, // hSourceToken
  977. TOKEN_ALL_ACCESS,
  978. NULL,
  979. SecurityImpersonation, // Obtain impersonation
  980. TokenImpersonation,
  981. &hImpersonationToken) // hDestinationToken
  982. ) {
  983. hImpersonationToken = NULL;
  984. }
  985. }
  986. // Bug 86489:
  987. // Grant all access to the token for "Everyone" so that ISAPIs that run out of proc
  988. // can do an OpenThreadToken call
  989. if (FAILED( GrantAllAccessToToken( hImpersonationToken ) ) )
  990. {
  991. CloseHandle( hImpersonationToken );
  992. DBG_ASSERT( FALSE );
  993. return NULL;
  994. }
  995. pct->SetImpersonationToken( hImpersonationToken);
  996. }
  997. else if ( hImpersonationToken )
  998. {
  999. CloseHandle( hImpersonationToken );
  1000. }
  1001. return pct;
  1002. } // ValidateUser
  1003. # define MAX_TOKEN_USER_INFO (300)
  1004. BOOL
  1005. IsGuestUser(IN HANDLE hToken)
  1006. /*++
  1007. Given a user token, this function determines if the token belongs
  1008. to a guest user. It returns true if the token is a guest user token.
  1009. Arguments:
  1010. hToken - handle for the Security token for a user.
  1011. Returns:
  1012. BOOL.
  1013. History:
  1014. MuraliK 22-Jan-1996 Created.
  1015. --*/
  1016. {
  1017. BOOL fGuest = FALSE;
  1018. BYTE rgbInfo[MAX_TOKEN_USER_INFO];
  1019. DWORD cbTotalRequired;
  1020. //
  1021. // Get the user information associated with the token.
  1022. // Using this we can then query to find out if it belongs to a guest user.
  1023. //
  1024. if (GetTokenInformation( hToken,
  1025. TokenUser,
  1026. (LPVOID ) rgbInfo,
  1027. MAX_TOKEN_USER_INFO,
  1028. &cbTotalRequired)
  1029. ) {
  1030. TOKEN_USER * pTokenUser = (TOKEN_USER *) rgbInfo;
  1031. PSID pSid = pTokenUser->User.Sid;
  1032. fGuest = EqualSid( pSid, g_psidGuestUser);
  1033. } else {
  1034. IF_DEBUG( DLL_SECURITY) {
  1035. DBGPRINTF(( DBG_CONTEXT,
  1036. "GetTokenInformation(%08x) failed. Error = %d."
  1037. " sizeof(TOKEN_USER) = %d, cb = %d\n",
  1038. hToken,
  1039. GetLastError(),
  1040. sizeof(TOKEN_USER), cbTotalRequired
  1041. ));
  1042. }
  1043. }
  1044. return ( fGuest);
  1045. } // IsGuestUser()
  1046. /*******************************************************************
  1047. NAME: TsImpersonateUser
  1048. SYNOPSIS: Causes the current thread to impersonate the user
  1049. represented by the given impersonation token.
  1050. ENTRY: hToken - A handle to an impersonation token created
  1051. with ValidateUser. This is actually a pointer to
  1052. a cached token object.
  1053. RETURNS: BOOL - TRUE if successful, FALSE otherwise.
  1054. HISTORY:
  1055. KeithMo 07-Mar-1993 Created.
  1056. MuraliK 21-Feb-1996 Optimized Token caching
  1057. ********************************************************************/
  1058. BOOL TsImpersonateUser( TS_TOKEN hToken )
  1059. {
  1060. HANDLE hTok;
  1061. IF_DEBUG( DLL_SECURITY )
  1062. {
  1063. DBGPRINTF(( DBG_CONTEXT,
  1064. "impersonating user token %08lX : Imperonation(%08lx)\n",
  1065. CTO_TO_TOKEN(hToken),
  1066. ((CACHED_TOKEN *) hToken)->QueryImpersonationToken()
  1067. ));
  1068. }
  1069. hTok = ((CACHED_TOKEN *) hToken)->QueryImpersonationToken();
  1070. if ( hTok == NULL) {
  1071. // if there is no impersonation token use the normal token itself.
  1072. hTok = CTO_TO_TOKEN(hToken);
  1073. }
  1074. #if DBG
  1075. if( !ImpersonateLoggedOnUser( hTok ) )
  1076. {
  1077. DBGPRINTF(( DBG_CONTEXT,
  1078. "cannot impersonate user token %08lX, error %08lX\n",
  1079. CTO_TO_TOKEN(hToken),
  1080. GetLastError() ));
  1081. return FALSE;
  1082. }
  1083. return TRUE;
  1084. # else
  1085. return ( ImpersonateLoggedOnUser(hTok));
  1086. # endif // DBG
  1087. } // TsImpersonateUser
  1088. /*******************************************************************
  1089. NAME: TsDeleteUserToken
  1090. SYNOPSIS: Deletes a token created with ValidateUser.
  1091. ENTRY: hToken - An impersonation token created with
  1092. ValidateUser.
  1093. RETURNS: BOOL - TRUE if successful, FALSE otherwise.
  1094. HISTORY:
  1095. KeithMo 07-Mar-1993 Created.
  1096. ********************************************************************/
  1097. BOOL TsDeleteUserToken(
  1098. TS_TOKEN hToken
  1099. )
  1100. {
  1101. NTSTATUS ntStatus = STATUS_SUCCESS;
  1102. if ( hToken != (TS_TOKEN)BOGUS_WIN95_TOKEN ) {
  1103. CACHED_TOKEN::Dereference( (CACHED_TOKEN *) hToken );
  1104. }
  1105. return TRUE;
  1106. } // DeleteUserToken
  1107. HANDLE
  1108. TsTokenToHandle(
  1109. TS_TOKEN hToken
  1110. )
  1111. /*++
  1112. Description:
  1113. Converts the token object into a real impersonation handle
  1114. Arguments:
  1115. hToken - pointer to cached token object
  1116. Returns:
  1117. Handle of real impersonation token
  1118. --*/
  1119. {
  1120. DBG_ASSERT( hToken != NULL );
  1121. return CTO_TO_TOKEN( hToken );
  1122. }
  1123. HANDLE
  1124. TsTokenToImpHandle(
  1125. TS_TOKEN hToken
  1126. )
  1127. /*++
  1128. Description:
  1129. Converts the token object into an impersonation handle
  1130. Arguments:
  1131. hToken - pointer to cached token object
  1132. Returns:
  1133. Handle of impersonation token
  1134. --*/
  1135. {
  1136. DBG_ASSERT( hToken != NULL );
  1137. return CTO_TO_IMPTOKEN( hToken );
  1138. }
  1139. BOOL
  1140. BuildAnonymousAcctDesc(
  1141. PTCP_AUTHENT_INFO pTAI
  1142. )
  1143. /*++
  1144. Routine Description:
  1145. Builds the anonymous account description based on the authentication
  1146. info structure.
  1147. Arguments:
  1148. pTAI - Pointer to authentication info to build
  1149. Returns:
  1150. TRUE if Success, FALSE otherwise
  1151. --*/
  1152. {
  1153. CHAR szDomainAndUser[IIS_DNLEN+UNLEN+2];
  1154. PCHAR pszUserOnly;
  1155. PCHAR pszDomain;
  1156. CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
  1157. DWORD cbDescLen;
  1158. if ( g_fUseSingleToken ) {
  1159. pTAI->cbAnonAcctDesc = 0;
  1160. return TRUE;
  1161. }
  1162. strncpy( szDomainAndUser,
  1163. pTAI->strAnonUserName.QueryStr(),
  1164. sizeof( szDomainAndUser ) );
  1165. szDomainAndUser[sizeof(szDomainAndUser)-1] = '\0';
  1166. if ( !CrackUserAndDomain( szDomainAndUser,
  1167. &pszUserOnly,
  1168. &pszDomain ))
  1169. {
  1170. DBGPRINTF((DBG_CONTEXT,
  1171. "BuildAnonymousAcctDesc: Call to CrackUserAndDomain failed\n"));
  1172. return(FALSE);
  1173. }
  1174. if ( !BuildAcctDesc( pszUserOnly,
  1175. pszDomain,
  1176. pTAI->strAnonUserPassword.QueryStr(),
  1177. !pTAI->fDontUseAnonSubAuth,
  1178. achAcctDesc,
  1179. &pTAI->cbAnonAcctDesc ) ||
  1180. !pTAI->bAnonAcctDesc.Resize( pTAI->cbAnonAcctDesc ))
  1181. {
  1182. return FALSE;
  1183. }
  1184. memcpy( pTAI->bAnonAcctDesc.QueryPtr(),
  1185. achAcctDesc,
  1186. pTAI->cbAnonAcctDesc );
  1187. return TRUE;
  1188. }
  1189. BOOL
  1190. BuildAcctDesc(
  1191. IN const CHAR * pszUser,
  1192. IN const CHAR * pszDomain,
  1193. IN const CHAR * pszPwd,
  1194. IN BOOL fUseSubAuth,
  1195. OUT CHAR * pchAcctDesc,
  1196. OUT LPDWORD pdwAcctDescLen
  1197. )
  1198. /*++
  1199. Description:
  1200. Builds a cache descriptor for account cache
  1201. Arguments:
  1202. pszUser - User name attempting to logon
  1203. pszDomain - Domain the user belongs to
  1204. pszPwd - password (case sensitive)
  1205. fUseSubAuth - TRUE if sub-authenticator used
  1206. pchAcctDesc - updated with descriptor
  1207. pdwAcctDescLen - updated with descriptor length
  1208. Returns:
  1209. TRUE on success, otherwise FALSE
  1210. --*/
  1211. {
  1212. if ( fUseSubAuth )
  1213. {
  1214. pszPwd = "";
  1215. }
  1216. size_t lU = strlen( pszUser ) + 1;
  1217. size_t lD = strlen( pszDomain ) + 1;
  1218. size_t lP = strlen( pszPwd ) + 1;
  1219. if ( lU > (UNLEN+1) ||
  1220. lD > (IIS_DNLEN+1) ||
  1221. lP > (PWLEN+1) )
  1222. {
  1223. SetLastError( ERROR_INVALID_PARAMETER );
  1224. return FALSE;
  1225. }
  1226. *pdwAcctDescLen = (DWORD)(1 + lU + lD + lP);
  1227. LPBYTE pD = (BYTE *) pchAcctDesc;
  1228. *pD++ = (BYTE)fUseSubAuth;
  1229. memcpy( pD, pszUser, lU );
  1230. #if 1 // DBCS enabling for user name
  1231. CharLower( (LPSTR)pD );
  1232. #else
  1233. _strlwr( (LPSTR)pD );
  1234. #endif
  1235. memcpy( pD + lU, pszDomain, lD );
  1236. _strlwr( (LPSTR)(pD+lU) );
  1237. memcpy( pD + lU + lD, pszPwd, lP );
  1238. DBG_ASSERT( (lU + lD + lP) < MAX_ACCT_DESC_LEN );
  1239. return TRUE;
  1240. }
  1241. BOOL
  1242. FindCachedToken(
  1243. IN const CHAR * pszUser,
  1244. IN const CHAR * pszDomain,
  1245. IN const CHAR * pszPwd,
  1246. IN BOOL fResetTTL,
  1247. IN BOOL fUseSubAuth,
  1248. IN DWORD dwLogonMethod,
  1249. OUT CACHED_TOKEN * * ppct
  1250. )
  1251. /*++
  1252. Description:
  1253. Checks to see if the specified user token handle is cached
  1254. Arguments:
  1255. pszUser - User name attempting to logon
  1256. pszDomain - Domain the user belongs to
  1257. pszPwd - password (case sensitive)
  1258. fResetTTL - Resets the TTL for this token
  1259. fUseSubAuth - TRUE if sub-authenticator used
  1260. dwLogonMethod - Logon method (Batch, Interactive, Network)
  1261. ppct - Receives token object
  1262. Returns:
  1263. TRUE on success and FALSE if the entry couldn't be found
  1264. --*/
  1265. {
  1266. LIST_ENTRY * pEntry;
  1267. CACHED_TOKEN * pct;
  1268. CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
  1269. DWORD dwAcctDescLen;
  1270. LPBYTE pAcctDesc;
  1271. DBG_ASSERT( pszUser != NULL );
  1272. if ( !BuildAcctDesc( pszUser, pszDomain, pszPwd, fUseSubAuth, achAcctDesc, &dwAcctDescLen) )
  1273. {
  1274. return FALSE;
  1275. }
  1276. DBG_ASSERT( dwAcctDescLen < sizeof(achAcctDesc ));
  1277. pAcctDesc = (LPBYTE)achAcctDesc;
  1278. LockTokenCache();
  1279. for ( pEntry = TokenCacheList.Flink;
  1280. pEntry != &TokenCacheList;
  1281. pEntry = pEntry->Flink )
  1282. {
  1283. pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
  1284. if ( pct->m_dwAcctDescLen == dwAcctDescLen &&
  1285. pct->m_dwLogonMethod == dwLogonMethod &&
  1286. !memcmp( pct->_achAcctDesc, pAcctDesc, dwAcctDescLen ) )
  1287. {
  1288. CACHED_TOKEN::Reference( pct );
  1289. *ppct = pct;
  1290. //
  1291. // Reset the TTL if this is the anonymous user so items in the
  1292. // cache don't get invalidated (token handle used as a
  1293. // discriminator)
  1294. if ( fResetTTL )
  1295. {
  1296. pct->_TTL = 2;
  1297. }
  1298. UnlockTokenCache();
  1299. return TRUE;
  1300. }
  1301. if( !_stricmp( pct->m_achUserName, pszUser ) &&
  1302. !_stricmp( pct->m_achDomainName, pszDomain ) &&
  1303. pct->m_dwLogonMethod == dwLogonMethod )
  1304. {
  1305. UnlockTokenCache();
  1306. RemoveTokenFromCache( pct );
  1307. return FALSE;
  1308. }
  1309. }
  1310. UnlockTokenCache();
  1311. return FALSE;
  1312. } // FindCachedToken
  1313. TS_TOKEN
  1314. FastFindAnonymousToken(
  1315. IN PTCP_AUTHENT_INFO pTAI
  1316. )
  1317. /*++
  1318. Description:
  1319. Checks to see if the specified anonymous user token handle is cached.
  1320. Don't call this function when using the sub-authenticator!
  1321. Arguments:
  1322. pTAI - pointer to the anonymous authentication info
  1323. Returns:
  1324. Pointer to the cached object.
  1325. --*/
  1326. {
  1327. LIST_ENTRY * pEntry;
  1328. CACHED_TOKEN * pct;
  1329. LockTokenCache();
  1330. for ( pEntry = TokenCacheList.Flink;
  1331. pEntry != &TokenCacheList;
  1332. pEntry = pEntry->Flink ) {
  1333. pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
  1334. DBG_ASSERT(pct->m_dwAcctDescLen > 0);
  1335. if ( (pct->m_dwAcctDescLen == pTAI->cbAnonAcctDesc ) &&
  1336. RtlEqualMemory(
  1337. pct->_achAcctDesc,
  1338. pTAI->bAnonAcctDesc.QueryPtr(),
  1339. pct->m_dwAcctDescLen ) ) {
  1340. CACHED_TOKEN::Reference( pct );
  1341. //
  1342. // Reset the TTL if this is the anonymous user so items in the
  1343. // cache don't get invalidated (token handle used as a
  1344. // discriminator)
  1345. pct->_TTL = 2;
  1346. UnlockTokenCache();
  1347. return pct;
  1348. }
  1349. }
  1350. UnlockTokenCache();
  1351. return NULL;
  1352. } // FastFindAnonymousToken
  1353. BOOL
  1354. AddTokenToCache(
  1355. IN const CHAR * pszUser,
  1356. IN const CHAR * pszDomain,
  1357. IN const CHAR * pszPwd,
  1358. IN BOOL fUseSubAuth,
  1359. IN HANDLE hToken,
  1360. IN DWORD dwLogonMethod,
  1361. OUT CACHED_TOKEN * * ppct,
  1362. BOOL fCheckAlreadyExist,
  1363. LPBOOL pfExist
  1364. )
  1365. /*++
  1366. Description:
  1367. Adds the specified token to the cache and converts the token handle
  1368. to a cached token object
  1369. Arguments:
  1370. pszUser - User name attempting to logon
  1371. pszDomain - Domain the user belongs to
  1372. pszPwd - Cast sensitive password
  1373. fUseSubAuth - TRUE if subauth to be used
  1374. phToken - Contains the token handle that was just logged on
  1375. dwLogonMethod - Logon Method
  1376. ppct - Receives cached token object
  1377. fCheckAlreadyExist - check if entry with same name already exist
  1378. pfExist - updated with TRUE if acct already exists
  1379. Returns:
  1380. TRUE on success and FALSE if the entry couldn't be found
  1381. --*/
  1382. {
  1383. LIST_ENTRY * pEntry;
  1384. CACHED_TOKEN * pctF;
  1385. CACHED_TOKEN * pct;
  1386. DWORD dwAcctDescLen;
  1387. BOOL fFound = FALSE;
  1388. CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
  1389. DBG_ASSERT( pszUser != NULL );
  1390. if( ( strlen( pszUser ) >= UNLEN ) ||
  1391. ( strlen( pszDomain ) >= IIS_DNLEN ) )
  1392. {
  1393. SetLastError( ERROR_INVALID_PARAMETER );
  1394. return FALSE;
  1395. }
  1396. if ( !BuildAcctDesc( pszUser, pszDomain, pszPwd, fUseSubAuth, achAcctDesc, &dwAcctDescLen) )
  1397. {
  1398. return FALSE;
  1399. }
  1400. pct = new CACHED_TOKEN;
  1401. if ( !pct )
  1402. {
  1403. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  1404. return FALSE;
  1405. }
  1406. pct->_hToken = hToken;
  1407. pct->m_hImpersonationToken = NULL; // initialize to invalid value
  1408. CopyMemory( pct->_achAcctDesc, achAcctDesc, dwAcctDescLen );
  1409. pct->m_dwAcctDescLen = dwAcctDescLen;
  1410. pct->m_dwLogonMethod = dwLogonMethod;
  1411. strcpy( pct->m_achUserName, pszUser );
  1412. strcpy( pct->m_achDomainName, pszDomain );
  1413. //
  1414. // Add the token to the list, we check for duplicates at callers's request
  1415. //
  1416. LockTokenCache();
  1417. if ( fCheckAlreadyExist )
  1418. {
  1419. for ( pEntry = TokenCacheList.Flink;
  1420. pEntry != &TokenCacheList;
  1421. pEntry = pEntry->Flink )
  1422. {
  1423. pctF = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
  1424. if ( pctF->m_dwAcctDescLen == dwAcctDescLen &&
  1425. !memcmp( pctF->_achAcctDesc, pct->_achAcctDesc, dwAcctDescLen ) &&
  1426. pctF->m_dwLogonMethod == dwLogonMethod )
  1427. {
  1428. fFound = TRUE;
  1429. break;
  1430. }
  1431. }
  1432. }
  1433. *pfExist = fFound;
  1434. if ( !fFound )
  1435. {
  1436. InsertHeadList( &TokenCacheList, &pct->_ListEntry );
  1437. }
  1438. else
  1439. {
  1440. // delete cache item ( was not yet on list )
  1441. CACHED_TOKEN::Dereference( pct );
  1442. pct = pctF;
  1443. }
  1444. CACHED_TOKEN::Reference( pct );
  1445. UnlockTokenCache();
  1446. *ppct = pct;
  1447. return TRUE;
  1448. } // AddTokenToCache
  1449. VOID
  1450. RemoveTokenFromCache( IN CACHED_TOKEN * pct)
  1451. {
  1452. DBG_ASSERT( pct != NULL);
  1453. LockTokenCache();
  1454. //
  1455. // Remove from the list
  1456. //
  1457. if ( pct->_ListEntry.Flink )
  1458. {
  1459. RemoveEntryList( &pct->_ListEntry );
  1460. pct->_ListEntry.Flink = NULL;
  1461. //
  1462. // Free any handles this user may still have open
  1463. //
  1464. TsCacheFlushUser( pct->_hToken, FALSE );
  1465. CACHED_TOKEN::Dereference( pct );
  1466. }
  1467. UnlockTokenCache();
  1468. return;
  1469. } // RemoveTokenFromCache()
  1470. VOID
  1471. WINAPI
  1472. TokenCacheScavenger(
  1473. IN VOID * /* pContext */
  1474. )
  1475. /*++
  1476. Description:
  1477. Decrements TTLs and removes tokens that have timed out
  1478. Arguments:
  1479. pContext - Not used
  1480. --*/
  1481. {
  1482. LIST_ENTRY * pEntry;
  1483. LIST_ENTRY * pEntryNext;
  1484. CACHED_TOKEN * pct;
  1485. LockTokenCache();
  1486. for ( pEntry = TokenCacheList.Flink;
  1487. pEntry != &TokenCacheList; )
  1488. {
  1489. pEntryNext = pEntry->Flink;
  1490. pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
  1491. if ( !(--pct->_TTL) )
  1492. {
  1493. IF_DEBUG( DLL_SECURITY )
  1494. {
  1495. DBGPRINTF(( DBG_CONTEXT,
  1496. "[TokenCacheScavenger] Timing out token for %s\n",
  1497. pct->_achAcctDesc ));
  1498. }
  1499. //
  1500. // This item has timed out, remove from the list
  1501. //
  1502. RemoveEntryList( &pct->_ListEntry );
  1503. pct->_ListEntry.Flink = NULL;
  1504. //
  1505. // Free any handles this user may still have open
  1506. //
  1507. TsCacheFlushUser( pct->_hToken, FALSE );
  1508. CACHED_TOKEN::Dereference( pct );
  1509. }
  1510. pEntry = pEntryNext;
  1511. }
  1512. UnlockTokenCache();
  1513. } // TokenCacheScavenger
  1514. BOOL
  1515. TsGetSecretW(
  1516. WCHAR * pszSecretName,
  1517. BUFFER * pbufSecret
  1518. )
  1519. /*++
  1520. Description:
  1521. Retrieves the specified unicode secret
  1522. Arguments:
  1523. pszSecretName - LSA Secret to retrieve
  1524. pbufSecret - Receives found secret
  1525. Returns:
  1526. TRUE on success and FALSE if any failure.
  1527. --*/
  1528. {
  1529. BOOL fResult;
  1530. NTSTATUS ntStatus;
  1531. PUNICODE_STRING punicodePassword = NULL;
  1532. UNICODE_STRING unicodeSecret;
  1533. LSA_HANDLE hPolicy;
  1534. OBJECT_ATTRIBUTES ObjectAttributes;
  1535. if ( pfnLsaOpenPolicy == NULL ) {
  1536. DBGPRINTF((DBG_CONTEXT,"LsaOpenPolicy does not exist on win95\n"));
  1537. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1538. return(FALSE);
  1539. }
  1540. //
  1541. // Open a policy to the remote LSA
  1542. //
  1543. InitializeObjectAttributes( &ObjectAttributes,
  1544. NULL,
  1545. 0L,
  1546. NULL,
  1547. NULL );
  1548. ntStatus = pfnLsaOpenPolicy( NULL,
  1549. &ObjectAttributes,
  1550. POLICY_ALL_ACCESS,
  1551. &hPolicy );
  1552. if ( !NT_SUCCESS( ntStatus ) )
  1553. {
  1554. SetLastError( pfnLsaNtStatusToWinError( ntStatus ) );
  1555. return FALSE;
  1556. }
  1557. InitUnicodeString( &unicodeSecret, pszSecretName );
  1558. //
  1559. // Query the secret value.
  1560. //
  1561. ntStatus = pfnLsaRetrievePrivateData( hPolicy,
  1562. &unicodeSecret,
  1563. &punicodePassword );
  1564. if( NT_SUCCESS(ntStatus) )
  1565. {
  1566. DWORD cbNeeded;
  1567. cbNeeded = punicodePassword->Length + sizeof(WCHAR);
  1568. if ( !pbufSecret->Resize( cbNeeded ) )
  1569. {
  1570. ntStatus = STATUS_NO_MEMORY;
  1571. goto Failure;
  1572. }
  1573. CopyMemory(
  1574. pbufSecret->QueryPtr(),
  1575. punicodePassword->Buffer,
  1576. punicodePassword->Length
  1577. );
  1578. *((WCHAR *) pbufSecret->QueryPtr() +
  1579. punicodePassword->Length / sizeof(WCHAR)) = L'\0';
  1580. ZeroMemory( punicodePassword->Buffer,
  1581. punicodePassword->MaximumLength );
  1582. }
  1583. Failure:
  1584. fResult = NT_SUCCESS(ntStatus);
  1585. //
  1586. // Cleanup & exit.
  1587. //
  1588. if( punicodePassword != NULL )
  1589. {
  1590. pfnLsaFreeMemory( (PVOID)punicodePassword );
  1591. }
  1592. pfnLsaClose( hPolicy );
  1593. if ( !fResult )
  1594. {
  1595. SetLastError( pfnLsaNtStatusToWinError( ntStatus ));
  1596. }
  1597. return fResult;
  1598. } // TsGetSecretW
  1599. DWORD
  1600. TsSetSecretW(
  1601. IN LPWSTR SecretName,
  1602. IN LPWSTR pSecret,
  1603. IN DWORD cbSecret
  1604. )
  1605. /*++
  1606. Description
  1607. Sets the specified LSA secret
  1608. Arguments:
  1609. SecretName - Name of the LSA secret
  1610. pSecret - Pointer to secret memory
  1611. cbSecret - Size of pSecret memory block
  1612. Note:
  1613. --*/
  1614. {
  1615. LSA_HANDLE hPolicy;
  1616. UNICODE_STRING unicodePassword;
  1617. UNICODE_STRING unicodeServer;
  1618. NTSTATUS ntStatus;
  1619. OBJECT_ATTRIBUTES ObjectAttributes;
  1620. UNICODE_STRING unicodeSecret;
  1621. InitUnicodeString( &unicodeServer,
  1622. L"" );
  1623. //
  1624. // Initialize the unicode string by hand so we can handle '\0' in the
  1625. // string
  1626. //
  1627. unicodePassword.Buffer = pSecret;
  1628. unicodePassword.Length = (USHORT) cbSecret;
  1629. unicodePassword.MaximumLength = (USHORT) cbSecret;
  1630. //
  1631. // Open a policy to the remote LSA
  1632. //
  1633. InitializeObjectAttributes( &ObjectAttributes,
  1634. NULL,
  1635. 0L,
  1636. NULL,
  1637. NULL );
  1638. ntStatus = pfnLsaOpenPolicy( &unicodeServer,
  1639. &ObjectAttributes,
  1640. POLICY_ALL_ACCESS,
  1641. &hPolicy );
  1642. if ( !NT_SUCCESS( ntStatus ) )
  1643. return pfnLsaNtStatusToWinError( ntStatus );
  1644. //
  1645. // Create or open the LSA secret
  1646. //
  1647. InitUnicodeString( &unicodeSecret,
  1648. SecretName );
  1649. ntStatus = pfnLsaStorePrivateData( hPolicy,
  1650. &unicodeSecret,
  1651. &unicodePassword );
  1652. pfnLsaClose( hPolicy );
  1653. if ( !NT_SUCCESS( ntStatus ))
  1654. {
  1655. return pfnLsaNtStatusToWinError( ntStatus );
  1656. }
  1657. return NO_ERROR;
  1658. } // TsSetSecretW()
  1659. /*******************************************************************
  1660. NAME: ApiAccessCheck
  1661. SYNOPSIS: Impersonate the RPC client, then check for valid
  1662. access against our server security object.
  1663. ENTRY: maskDesiredAccess - Specifies the desired access mask.
  1664. This mask must not contain generic accesses.
  1665. RETURNS: DWORD - NO_ERROR if access granted, ERROR_ACCESS_DENIED
  1666. if access denied, other Win32 errors if something
  1667. tragic happened.
  1668. HISTORY:
  1669. KeithMo 26-Mar-1993 Created.
  1670. ********************************************************************/
  1671. DWORD TsApiAccessCheck( ACCESS_MASK maskDesiredAccess )
  1672. {
  1673. DWORD err;
  1674. BOOL fRet;
  1675. if ( maskDesiredAccess == TCP_QUERY_STATISTICS) {
  1676. //
  1677. // Statistics query should be allowed without authentication.
  1678. // Any body can bring up perfmon and request statistics.
  1679. //
  1680. return ( NO_ERROR);
  1681. }
  1682. //
  1683. // Impersonate the RPC client.
  1684. //
  1685. err = (DWORD)RpcImpersonateClient( NULL );
  1686. if( err != NO_ERROR )
  1687. {
  1688. IF_DEBUG( DLL_SECURITY )
  1689. {
  1690. DBGPRINTF(( DBG_CONTEXT,
  1691. "cannot impersonate rpc client, error %lu\n",
  1692. err ));
  1693. }
  1694. } else {
  1695. BOOL fAccessStatus;
  1696. BOOL fGenerateOnClose;
  1697. ACCESS_MASK maskAccessGranted;
  1698. //
  1699. // Validate access.
  1700. //
  1701. if ( g_fUseSingleToken )
  1702. {
  1703. HANDLE hAccToken;
  1704. BYTE Set[256];
  1705. DWORD dwSet = sizeof(Set);
  1706. if ( OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, &hAccToken ) )
  1707. {
  1708. fRet = AccessCheck( sdApiObject,
  1709. hAccToken,
  1710. maskDesiredAccess,
  1711. &TCPApiObjectMapping,
  1712. (PPRIVILEGE_SET)&Set,
  1713. &dwSet,
  1714. &maskAccessGranted,
  1715. &fAccessStatus );
  1716. CloseHandle( hAccToken );
  1717. }
  1718. else
  1719. {
  1720. fRet = FALSE;
  1721. }
  1722. }
  1723. else
  1724. {
  1725. fRet = AccessCheckAndAuditAlarmW( SUBSYSTEM_NAME,
  1726. NULL,
  1727. OBJECTTYPE_NAME,
  1728. OBJECT_NAME,
  1729. sdApiObject,
  1730. maskDesiredAccess,
  1731. &TCPApiObjectMapping,
  1732. FALSE,
  1733. &maskAccessGranted,
  1734. &fAccessStatus,
  1735. &fGenerateOnClose );
  1736. }
  1737. if ( !fRet ) {
  1738. err = GetLastError();
  1739. }
  1740. //
  1741. // Revert to our former self.
  1742. //
  1743. DBG_REQUIRE( !RpcRevertToSelf() );
  1744. //
  1745. // Check the results.
  1746. //
  1747. if( err != NO_ERROR ) {
  1748. IF_DEBUG( DLL_SECURITY ) {
  1749. DBGPRINTF(( DBG_CONTEXT,
  1750. "cannot check access, error %lu\n",
  1751. err ));
  1752. }
  1753. } else if( !fAccessStatus ) {
  1754. err = ERROR_ACCESS_DENIED;
  1755. IF_DEBUG( DLL_SECURITY ) {
  1756. DBGPRINTF(( DBG_CONTEXT,
  1757. "bad access status, error %lu\n",
  1758. err ));
  1759. }
  1760. }
  1761. }
  1762. return (err);
  1763. } // ApiAccessCheck
  1764. /*******************************************************************
  1765. NAME: CreateWellKnownSids
  1766. SYNOPSIS: Create some well-known SIDs used to create a security
  1767. descriptor for the API security object.
  1768. RETURNS: NTSTATUS - An NT Status code.
  1769. HISTORY:
  1770. KeithMo 26-Mar-1993 Created.
  1771. ********************************************************************/
  1772. DWORD CreateWellKnownSids( HINSTANCE hDll )
  1773. {
  1774. DWORD error = NO_ERROR;
  1775. SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
  1776. SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
  1777. BOOL fRet;
  1778. fRet = AllocateAndInitializeSid( &siaWorld,
  1779. 1,
  1780. SECURITY_WORLD_RID,
  1781. 0,0,0,0,0,0,0,
  1782. &psidWorld );
  1783. if( fRet )
  1784. {
  1785. fRet = AllocateAndInitializeSid( &siaNt,
  1786. 1,
  1787. SECURITY_LOCAL_SYSTEM_RID,
  1788. 0,0,0,0,0,0,0,
  1789. &psidLocalSystem );
  1790. }
  1791. if( fRet )
  1792. {
  1793. fRet = AllocateAndInitializeSid( &siaNt,
  1794. 2,
  1795. SECURITY_BUILTIN_DOMAIN_RID,
  1796. DOMAIN_ALIAS_RID_ADMINS,
  1797. 0,0,0,0,0,0,
  1798. &psidAdmins );
  1799. }
  1800. if( fRet )
  1801. {
  1802. fRet = AllocateAndInitializeSid( &siaNt,
  1803. 2,
  1804. SECURITY_BUILTIN_DOMAIN_RID,
  1805. DOMAIN_ALIAS_RID_SYSTEM_OPS,
  1806. 0,0,0,0,0,0,
  1807. &psidServerOps );
  1808. }
  1809. if( fRet )
  1810. {
  1811. fRet = AllocateAndInitializeSid( &siaNt,
  1812. 2,
  1813. SECURITY_BUILTIN_DOMAIN_RID,
  1814. DOMAIN_ALIAS_RID_POWER_USERS,
  1815. 0,0,0,0,0,0,
  1816. &psidPowerUsers );
  1817. }
  1818. if( fRet )
  1819. {
  1820. USER_MODALS_INFO_2 * pUsrModals2 = NULL;
  1821. HINSTANCE hInstance = NULL;
  1822. NET_USER_MODALS_GET_FN pfnNetUserModalsGet = NULL;
  1823. NET_API_BUFFER_FREE_FN pfnNetApiBufferFree = NULL;
  1824. //
  1825. // Construct well-known-sid for Guest User on the local computer
  1826. //
  1827. // 1) Obtain the sid for the local machine's domain
  1828. // 2) copy domain sid to guest user sid
  1829. // 3) append DOMAIN_USER_RID_GUEST to the domain sid in GuestUser sid.
  1830. //
  1831. g_psidGuestUser = (PSID ) g_GuestUserSidBuffer;
  1832. hInstance = LoadLibrary("netapi32.dll");
  1833. if ( hInstance != NULL ) {
  1834. pfnNetUserModalsGet = (NET_USER_MODALS_GET_FN)
  1835. GetProcAddress(hInstance,"NetUserModalsGet");
  1836. pfnNetApiBufferFree = (NET_API_BUFFER_FREE_FN)
  1837. GetProcAddress(hInstance,"NetApiBufferFree");
  1838. }
  1839. if ( (pfnNetUserModalsGet != NULL) &&
  1840. (pfnNetApiBufferFree != NULL) ) {
  1841. fRet = ( (pfnNetUserModalsGet(NULL, // local computer
  1842. 2, // get level 2 information
  1843. (LPBYTE *) &pUsrModals2
  1844. ) == 0)
  1845. &&
  1846. CopySid(GUEST_USER_SID_BUFFER_LEN - 4,// Buffer len
  1847. g_psidGuestUser, // psidDestination
  1848. pUsrModals2->usrmod2_domain_id // obtain domain sid.
  1849. )
  1850. );
  1851. } else {
  1852. DBGPRINTF((DBG_CONTEXT,"Unable to get netapi32 entrypoints\n"));
  1853. fRet = FALSE;
  1854. }
  1855. //
  1856. // if successful append the DOMAIN_USER_RID_GUEST.
  1857. //
  1858. if ( fRet) {
  1859. DWORD lenSid = GetLengthSid( g_psidGuestUser);
  1860. CHAR nSubAuth;
  1861. //
  1862. // There is no Win32 way to set a SID value.
  1863. // We will munge around on our own.
  1864. // Pretty dangerous thing to do :-(
  1865. //
  1866. // increment the number of sub authorities
  1867. nSubAuth = *((UCHAR *) ((UCHAR *) g_psidGuestUser + 1));
  1868. nSubAuth++;
  1869. *((UCHAR *) ((UCHAR *) g_psidGuestUser + 1)) = nSubAuth;
  1870. // Store the new sub authority (Domain User Rid for Guest).
  1871. *((ULONG *) ((BYTE *) g_psidGuestUser + lenSid)) =
  1872. DOMAIN_USER_RID_GUEST;
  1873. } else {
  1874. g_psidGuestUser = NULL;
  1875. }
  1876. if ( pUsrModals2 != NULL) {
  1877. NET_API_STATUS ns = pfnNetApiBufferFree( (LPVOID )pUsrModals2);
  1878. pUsrModals2 = NULL;
  1879. }
  1880. if ( hInstance != NULL ) {
  1881. FreeLibrary(hInstance);
  1882. }
  1883. }
  1884. if ( fRet && g_fUseSingleToken )
  1885. {
  1886. BYTE abInfo[256];
  1887. DWORD dwInfo;
  1888. if ( GetTokenInformation( g_hProcessPrimaryToken,
  1889. TokenUser,
  1890. abInfo,
  1891. sizeof(abInfo),
  1892. &dwInfo ) )
  1893. {
  1894. if ( !(g_psidProcessUser = (PSID)LocalAlloc( LMEM_FIXED,
  1895. GetLengthSid(((TOKEN_USER*)abInfo)->User.Sid))) )
  1896. {
  1897. fRet = FALSE;
  1898. }
  1899. else
  1900. {
  1901. memcpy ( g_psidProcessUser,
  1902. ((TOKEN_USER*)abInfo)->User.Sid,
  1903. GetLengthSid(((TOKEN_USER*)abInfo)->User.Sid) );
  1904. }
  1905. }
  1906. else
  1907. {
  1908. fRet = FALSE;
  1909. }
  1910. }
  1911. if ( !fRet ) {
  1912. error = GetLastError( );
  1913. IF_DEBUG( DLL_SECURITY ) {
  1914. DBGPRINTF(( DBG_CONTEXT,
  1915. "cannot create well-known sids\n" ));
  1916. }
  1917. }
  1918. return error;
  1919. } // CreateWellKnownSids
  1920. /*******************************************************************
  1921. NAME: FreeWellKnownSids
  1922. SYNOPSIS: Frees the SIDs created with CreateWellKnownSids.
  1923. HISTORY:
  1924. KeithMo 26-Mar-1993 Created.
  1925. ********************************************************************/
  1926. VOID FreeWellKnownSids( VOID )
  1927. {
  1928. if( psidWorld != NULL )
  1929. {
  1930. FreeSid( psidWorld );
  1931. psidWorld = NULL;
  1932. }
  1933. if( psidLocalSystem != NULL )
  1934. {
  1935. FreeSid( psidLocalSystem );
  1936. psidLocalSystem = NULL;
  1937. }
  1938. if( psidAdmins != NULL )
  1939. {
  1940. FreeSid( psidAdmins );
  1941. psidAdmins = NULL;
  1942. }
  1943. if( psidServerOps != NULL )
  1944. {
  1945. FreeSid( psidServerOps );
  1946. psidServerOps = NULL;
  1947. }
  1948. if( psidPowerUsers != NULL )
  1949. {
  1950. FreeSid( psidPowerUsers );
  1951. psidPowerUsers = NULL;
  1952. }
  1953. if( g_psidProcessUser != NULL )
  1954. {
  1955. LocalFree( g_psidProcessUser );
  1956. g_psidProcessUser = NULL;
  1957. }
  1958. } // FreeWellKnownSids
  1959. /*******************************************************************
  1960. NAME: CreateApiSecurityObject
  1961. SYNOPSIS: Create an abstract security object used for validating
  1962. user access to the TCP Server APIs.
  1963. RETURNS: NTSTATUS - An NT Status code.
  1964. HISTORY:
  1965. KeithMo 26-Mar-1993 Created.
  1966. ********************************************************************/
  1967. DWORD CreateApiSecurityObject( VOID )
  1968. {
  1969. DWORD err;
  1970. ACE_DATA aces[] =
  1971. {
  1972. {
  1973. ACCESS_ALLOWED_ACE_TYPE,
  1974. 0,
  1975. 0,
  1976. TCP_ALL_ACCESS,
  1977. &psidLocalSystem
  1978. },
  1979. {
  1980. ACCESS_ALLOWED_ACE_TYPE,
  1981. 0,
  1982. 0,
  1983. TCP_ALL_ACCESS,
  1984. &psidAdmins
  1985. },
  1986. {
  1987. ACCESS_ALLOWED_ACE_TYPE,
  1988. 0,
  1989. 0,
  1990. TCP_ALL_ACCESS,
  1991. &psidServerOps
  1992. },
  1993. {
  1994. ACCESS_ALLOWED_ACE_TYPE,
  1995. 0,
  1996. 0,
  1997. TCP_ALL_ACCESS,
  1998. &psidPowerUsers
  1999. },
  2000. {
  2001. ACCESS_ALLOWED_ACE_TYPE,
  2002. 0,
  2003. 0,
  2004. TCP_GENERIC_EXECUTE,
  2005. &psidWorld
  2006. },
  2007. {
  2008. ACCESS_ALLOWED_ACE_TYPE,
  2009. 0,
  2010. 0,
  2011. TCP_GENERIC_EXECUTE,
  2012. &g_psidProcessUser
  2013. },
  2014. };
  2015. #define NUM_ACES (sizeof(aces) / sizeof(RTL_ACE_DATA))
  2016. err = INetCreateSecurityObject( aces,
  2017. (ULONG)(g_fUseSingleToken ? NUM_ACES : NUM_ACES-1),
  2018. NULL,
  2019. NULL,
  2020. &TCPApiObjectMapping,
  2021. &sdApiObject );
  2022. IF_DEBUG( DLL_SECURITY )
  2023. {
  2024. if( err )
  2025. {
  2026. DBGPRINTF(( DBG_CONTEXT,
  2027. "cannot create api security object, error %d\n",
  2028. err ));
  2029. }
  2030. }
  2031. return err;
  2032. } // CreateApiSecurityObject
  2033. /*******************************************************************
  2034. NAME: DeleteApiSecurityObject
  2035. SYNOPSIS: Frees the security descriptor created with
  2036. CreateApiSecurityObject.
  2037. HISTORY:
  2038. KeithMo 26-Mar-1993 Created.
  2039. ********************************************************************/
  2040. VOID DeleteApiSecurityObject( VOID )
  2041. {
  2042. INetDeleteSecurityObject( &sdApiObject );
  2043. } // DeleteApiSecurityObject
  2044. //
  2045. // Short routine to enable the TcbPrivilege for testing services running
  2046. // as an executable (rather then a service). Note that the account
  2047. // running the .exe must be added in User Manager's User Right's dialog
  2048. // under "Act as part of the OS"
  2049. //
  2050. VOID EnableTcbPrivilege(
  2051. VOID
  2052. )
  2053. {
  2054. HANDLE ProcessHandle = NULL;
  2055. HANDLE TokenHandle = NULL;
  2056. BOOL Result;
  2057. LUID TcbValue;
  2058. LUID AuditValue;
  2059. TOKEN_PRIVILEGES * TokenPrivileges;
  2060. CHAR buf[ 5 * sizeof(TOKEN_PRIVILEGES) ];
  2061. ProcessHandle = OpenProcess(
  2062. PROCESS_QUERY_INFORMATION,
  2063. FALSE,
  2064. GetCurrentProcessId()
  2065. );
  2066. if ( ProcessHandle == NULL ) {
  2067. //
  2068. // This should not happen
  2069. //
  2070. goto Cleanup;
  2071. }
  2072. Result = OpenProcessToken (
  2073. ProcessHandle,
  2074. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  2075. &TokenHandle
  2076. );
  2077. if ( !Result ) {
  2078. //
  2079. // This should not happen
  2080. //
  2081. goto Cleanup;
  2082. }
  2083. //
  2084. // Find out the value of TakeOwnershipPrivilege
  2085. //
  2086. Result = LookupPrivilegeValue(
  2087. NULL,
  2088. "SeTcbPrivilege",
  2089. &TcbValue
  2090. );
  2091. if ( !Result ) {
  2092. goto Cleanup;
  2093. }
  2094. //
  2095. // Need this for RPC impersonation (calls NtAccessCheckAndAuditAlarm)
  2096. //
  2097. Result = LookupPrivilegeValue(
  2098. NULL,
  2099. "SeAuditPrivilege",
  2100. &AuditValue
  2101. );
  2102. if ( !Result ) {
  2103. goto Cleanup;
  2104. }
  2105. //
  2106. // Set up the privilege set we will need
  2107. //
  2108. TokenPrivileges = (TOKEN_PRIVILEGES *) buf;
  2109. TokenPrivileges->PrivilegeCount = 2;
  2110. TokenPrivileges->Privileges[0].Luid = TcbValue;
  2111. TokenPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  2112. TokenPrivileges->Privileges[1].Luid = AuditValue;
  2113. TokenPrivileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
  2114. (VOID) AdjustTokenPrivileges (
  2115. TokenHandle,
  2116. FALSE,
  2117. TokenPrivileges,
  2118. sizeof(buf),
  2119. NULL,
  2120. NULL
  2121. );
  2122. Cleanup:
  2123. if ( TokenHandle )
  2124. {
  2125. CloseHandle( TokenHandle );
  2126. }
  2127. if ( ProcessHandle )
  2128. {
  2129. CloseHandle( ProcessHandle );
  2130. }
  2131. }
  2132. BOOL CrackUserAndDomain(
  2133. CHAR * pszDomainAndUser,
  2134. CHAR * * ppszUser,
  2135. CHAR * * ppszDomain
  2136. )
  2137. /*++
  2138. Routine Description:
  2139. Given a user name potentially in the form domain\user, zero terminates
  2140. the domain name and returns pointers to the domain name and the user name
  2141. Arguments:
  2142. pszDomainAndUser - Pointer to user name or domain and user name
  2143. ppszUser - Receives pointer to user portion of name
  2144. ppszDomain - Receives pointer to domain portion of name
  2145. Return Value:
  2146. TRUE if successful, FALSE otherwise (call GetLastError)
  2147. --*/
  2148. {
  2149. static CHAR szDefaultDomain[MAX_COMPUTERNAME_LENGTH+1];
  2150. //
  2151. // Crack the name into domain/user components.
  2152. //
  2153. *ppszDomain = pszDomainAndUser;
  2154. #if 1 // DBCS enabling for user name
  2155. *ppszUser = (PCHAR)_mbspbrk( (PUCHAR)pszDomainAndUser, (PUCHAR)"/\\" );
  2156. #else
  2157. *ppszUser = strpbrk( pszDomainAndUser, "/\\" );
  2158. #endif
  2159. if( *ppszUser == NULL )
  2160. {
  2161. //
  2162. // No domain name specified, just the username so we assume the
  2163. // user is on the local machine
  2164. //
  2165. if ( !*szDefaultDomain )
  2166. {
  2167. if ( !pfnGetDefaultDomainName( szDefaultDomain,
  2168. sizeof(szDefaultDomain)))
  2169. {
  2170. return FALSE;
  2171. }
  2172. }
  2173. *ppszDomain = szDefaultDomain;
  2174. *ppszUser = pszDomainAndUser;
  2175. }
  2176. else
  2177. {
  2178. //
  2179. // Both domain & user specified, skip delimiter.
  2180. //
  2181. **ppszUser = '\0';
  2182. (*ppszUser)++;
  2183. if( ( **ppszUser == '\0' ) ||
  2184. ( **ppszUser == '\\' ) ||
  2185. ( **ppszUser == '/' ) ||
  2186. ( *pszDomainAndUser == '\0' ) )
  2187. {
  2188. //
  2189. // Name is of one of the following (invalid) forms:
  2190. //
  2191. // "domain\"
  2192. // "domain\\..."
  2193. // "domain/..."
  2194. // "\username"
  2195. // "/username"
  2196. //
  2197. SetLastError( ERROR_INVALID_PARAMETER );
  2198. return FALSE;
  2199. }
  2200. }
  2201. return TRUE;
  2202. }
  2203. LONG
  2204. WINAPI
  2205. NullReferenceMapper(
  2206. IN HMAPPER *pMap
  2207. )
  2208. /*++
  2209. Routine Description:
  2210. Increment reference count to mapper
  2211. Arguments:
  2212. pMap - ptr to mapper struct
  2213. Returns:
  2214. Ref count
  2215. --*/
  2216. {
  2217. DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
  2218. return pfnInterlockedExchangeAdd( &((IisMapper*)pMap)->lRefCount, 1 ) + 1;
  2219. }
  2220. LONG
  2221. WINAPI
  2222. NullDeReferenceMapper(
  2223. IN HMAPPER *pMap
  2224. )
  2225. /*++
  2226. Routine Description:
  2227. Decrement reference count to mapper
  2228. Arguments:
  2229. pMap - ptr to mapper struct
  2230. Returns:
  2231. Ref count
  2232. --*/
  2233. {
  2234. LONG l;
  2235. DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
  2236. if ( !(l = pfnInterlockedExchangeAdd( &((IisMapper*)pMap)->lRefCount, -1 ) - 1 ) )
  2237. {
  2238. LocalFree( pMap );
  2239. }
  2240. return l;
  2241. }
  2242. DWORD WINAPI NullGetIssuerList(
  2243. HMAPPER *phMapper, // in
  2244. VOID * Reserved, // in
  2245. BYTE * pIssuerList, // out
  2246. DWORD * pcbIssuerList // out
  2247. )
  2248. /*++
  2249. Routine Description:
  2250. Called to retrieve the list of preferred cert issuers
  2251. Arguments:
  2252. ppIssuer -- updated with ptr buffer of issuers
  2253. pdwIssuer -- updated with issuers buffer size
  2254. Returns:
  2255. TRUE if success, FALSE if error
  2256. --*/
  2257. {
  2258. return SEC_E_UNSUPPORTED_FUNCTION;
  2259. }
  2260. DWORD WINAPI NullGetChallenge(
  2261. HMAPPER *pMap, // in
  2262. BYTE * pAuthenticatorId, // in
  2263. DWORD cbAuthenticatorId, // in
  2264. BYTE * pChallenge, // out
  2265. DWORD * pcbChallenge // out
  2266. )
  2267. /*++
  2268. Routine Description:
  2269. Get challenge for auth sequence
  2270. Arguments:
  2271. Not used
  2272. Returns:
  2273. FALSE ( not supported )
  2274. --*/
  2275. {
  2276. DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
  2277. return SEC_E_UNSUPPORTED_FUNCTION;
  2278. }
  2279. DWORD WINAPI NullMapCredential(
  2280. HMAPPER * phMapper,
  2281. DWORD dwCredentialType,
  2282. const VOID* pCredential, // in
  2283. const VOID* pAuthority, // in
  2284. HLOCATOR * phToken
  2285. )
  2286. /*++
  2287. Routine Description:
  2288. Called to map a certificate to a NT account
  2289. Arguments:
  2290. phMapper - ptr to mapper descriptor
  2291. dwCredentialType -- type of credential
  2292. pCredential - ptr to PCERT_CONTEXT for client cert
  2293. pAuthority - ptr to PCERT_CONTEXT for Certifying authority
  2294. phToken -- updated with impersonation access token
  2295. Returns:
  2296. FALSE ( mapping always fail )
  2297. --*/
  2298. {
  2299. DBG_ASSERT( ((IisMapper*)phMapper)->dwSignature == IIS_MAPPER_SIGNATURE );
  2300. return SEC_E_UNSUPPORTED_FUNCTION;
  2301. }
  2302. DWORD WINAPI NullCloseLocator(
  2303. HMAPPER *pMap,
  2304. HLOCATOR hLocator //in
  2305. )
  2306. /*++
  2307. Routine Description:
  2308. Called to close a HLOCATOR returned by MapCredential
  2309. Arguments:
  2310. tokenhandle -- HLOCATOR
  2311. Returns:
  2312. TRUE if success, FALSE if error
  2313. --*/
  2314. {
  2315. DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
  2316. if (hLocator == 1) {
  2317. return SEC_E_OK;
  2318. }
  2319. else {
  2320. if (CloseHandle( (HANDLE)hLocator )) {\
  2321. return SEC_E_OK;
  2322. }
  2323. else {
  2324. }
  2325. }
  2326. return hLocator == 1 ? TRUE : CloseHandle( (HANDLE)hLocator );
  2327. }
  2328. DWORD WINAPI NullGetAccessToken(
  2329. HMAPPER *pMap,
  2330. HLOCATOR tokenhandle,
  2331. HANDLE * phToken
  2332. )
  2333. /*++
  2334. Routine Description:
  2335. Called to retrieve an access token from a mapping
  2336. Arguments:
  2337. tokenhandle -- HLOCATOR returned by MapCredential
  2338. phToken -- updated with potentially new token
  2339. Returns:
  2340. TRUE if success, FALSE if error
  2341. --*/
  2342. {
  2343. DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
  2344. if ( tokenhandle == 1 )
  2345. {
  2346. *phToken = (HANDLE)tokenhandle;
  2347. }
  2348. else if ( !pfnDuplicateTokenEx( (HANDLE)tokenhandle,
  2349. TOKEN_ALL_ACCESS,
  2350. NULL,
  2351. SecurityImpersonation,
  2352. TokenImpersonation,
  2353. phToken ))
  2354. {
  2355. return SEC_E_UNSUPPORTED_FUNCTION;
  2356. }
  2357. return SEC_E_OK;
  2358. }
  2359. DWORD WINAPI NullQueryMappedCredentialAttributes(
  2360. HMAPPER *phMapper, // in
  2361. HLOCATOR hLocator, // in
  2362. ULONG ulAttribute, // in
  2363. PVOID pBuffer, //out
  2364. DWORD *pcbBuffer // in out
  2365. )
  2366. {
  2367. return ( SEC_E_NOT_SUPPORTED );
  2368. }
  2369. QuerySingleAccessToken(
  2370. VOID
  2371. )
  2372. /*++
  2373. Routine Description:
  2374. Query status of single access token mode
  2375. Arguments:
  2376. None
  2377. Returns:
  2378. TRUE if single access token mode used, otherwise FALSE
  2379. --*/
  2380. {
  2381. return g_fUseSingleToken;
  2382. }
  2383. BOOL
  2384. CACHED_CREDENTIAL::GetCredential(
  2385. LPSTR pszPackage,
  2386. PIIS_SERVER_INSTANCE psi,
  2387. PTCP_AUTHENT_INFO pTAI,
  2388. CredHandle* prcred,
  2389. ULONG* pcbMaxToken
  2390. )
  2391. /*++
  2392. Routine Description:
  2393. Get SSPI credential handle from cache
  2394. Arguments:
  2395. pszPackage - SSPI package name, e.g NTLM
  2396. psi - pointer to server instance
  2397. pTAI - pointer to authent info, only DomainName used
  2398. prcred - updated with CredHandle from cache
  2399. pcbMaxToken - updated with max token size used by this package
  2400. Returns:
  2401. TRUE if success, otherwise FALSE
  2402. --*/
  2403. {
  2404. LIST_ENTRY * pEntry;
  2405. CACHED_CREDENTIAL * pcred;
  2406. SEC_WINNT_AUTH_IDENTITY AuthIdentity;
  2407. SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
  2408. SecPkgInfo * pspkg;
  2409. TimeStamp Lifetime;
  2410. STACK_STR ( strDefaultLogonDomain, IIS_DNLEN+1 );
  2411. SECURITY_STATUS ss;
  2412. DBG_ASSERT( pszPackage != NULL );
  2413. DBG_ASSERT( pTAI != NULL );
  2414. EnterCriticalSection( &csCredentialCacheLock );
  2415. for ( pEntry = CredentialCacheList.Flink;
  2416. pEntry != &CredentialCacheList;
  2417. pEntry = pEntry->Flink )
  2418. {
  2419. pcred = CONTAINING_RECORD( pEntry, CACHED_CREDENTIAL, _ListEntry );
  2420. if ( !strcmp( pszPackage, pcred->_PackageName.QueryStr() ) &&
  2421. !strcmp( pTAI->strDefaultLogonDomain.QueryStr(), pcred->_DefaultDomain.QueryStr() ) )
  2422. {
  2423. goto Exit;
  2424. }
  2425. }
  2426. if ( (pcred = new CACHED_CREDENTIAL) == NULL )
  2427. {
  2428. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  2429. goto Exit;
  2430. }
  2431. if ( !pcred->_PackageName.Copy( pszPackage ) ||
  2432. !pcred->_DefaultDomain.Copy( pTAI->strDefaultLogonDomain ) )
  2433. {
  2434. delete pcred;
  2435. pcred = NULL;
  2436. goto Exit;
  2437. }
  2438. //
  2439. // provide default logon domain
  2440. //
  2441. if ( psi == NULL )
  2442. {
  2443. pAuthIdentity = NULL;
  2444. }
  2445. else
  2446. {
  2447. pAuthIdentity = &AuthIdentity;
  2448. memset( &AuthIdentity,
  2449. 0,
  2450. sizeof( AuthIdentity ));
  2451. if ( pTAI->strDefaultLogonDomain.QueryCCH() <= IIS_DNLEN )
  2452. {
  2453. strDefaultLogonDomain.Copy( pTAI->strDefaultLogonDomain );
  2454. AuthIdentity.Domain = (LPBYTE)strDefaultLogonDomain.QueryStr();
  2455. }
  2456. if ( AuthIdentity.Domain != NULL )
  2457. {
  2458. if ( AuthIdentity.DomainLength =
  2459. strlen( (LPCTSTR)AuthIdentity.Domain ) )
  2460. {
  2461. // remove trailing '\\' if present
  2462. if ( AuthIdentity.Domain[AuthIdentity.DomainLength-1]
  2463. == '\\' )
  2464. {
  2465. --AuthIdentity.DomainLength;
  2466. }
  2467. }
  2468. }
  2469. if ( AuthIdentity.DomainLength == 0 )
  2470. {
  2471. pAuthIdentity = NULL;
  2472. }
  2473. else
  2474. {
  2475. AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
  2476. }
  2477. }
  2478. ss = pfnAcquireCredentialsHandle( NULL, // New principal
  2479. pszPackage, // Package name
  2480. SECPKG_CRED_INBOUND,
  2481. NULL, // Logon ID
  2482. pAuthIdentity, // Auth Data
  2483. NULL, // Get key func
  2484. NULL, // Get key arg
  2485. &pcred->_hcred,
  2486. &Lifetime );
  2487. //
  2488. // Need to determine the max token size for this package
  2489. //
  2490. if ( ss == STATUS_SUCCESS )
  2491. {
  2492. pcred->_fHaveCredHandle = TRUE;
  2493. ss = pfnQuerySecurityPackageInfo( (char *) pszPackage,
  2494. &pspkg );
  2495. }
  2496. if ( ss == STATUS_SUCCESS )
  2497. {
  2498. pcred->_cbMaxToken = pspkg->cbMaxToken;
  2499. DBG_ASSERT( pspkg->fCapabilities & SECPKG_FLAG_CONNECTION );
  2500. pfnFreeContextBuffer( pspkg );
  2501. }
  2502. if ( ss != STATUS_SUCCESS )
  2503. {
  2504. DBGPRINTF(( DBG_CONTEXT,
  2505. "[GetCredential] AcquireCredentialsHandle or QuerySecurityPackageInfo failed, error %d\n",
  2506. ss ));
  2507. SetLastError( ss );
  2508. delete pcred;
  2509. pcred = NULL;
  2510. }
  2511. else
  2512. {
  2513. InsertHeadList( &CredentialCacheList, &pcred->_ListEntry );
  2514. }
  2515. Exit:
  2516. if ( pcred )
  2517. {
  2518. *pcbMaxToken = pcred->_cbMaxToken;
  2519. *prcred = pcred->_hcred;
  2520. }
  2521. LeaveCriticalSection( &csCredentialCacheLock );
  2522. return pcred ? TRUE : FALSE;
  2523. }
  2524. CACHED_CREDENTIAL::~CACHED_CREDENTIAL(
  2525. )
  2526. /*++
  2527. Routine Description:
  2528. SSPI Credential cache entry destructor
  2529. Arguments:
  2530. None
  2531. Returns:
  2532. Nothing
  2533. --*/
  2534. {
  2535. if ( _fHaveCredHandle )
  2536. {
  2537. pfnFreeCredentialsHandle( &_hcred );
  2538. }
  2539. }