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
1539 lines
38 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
tokencache.cxx
|
|
|
|
Abstract:
|
|
|
|
Ming's token cache refactored for general consumption
|
|
|
|
Author:
|
|
|
|
Bilal Alam (balam) May-4-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <iis.h>
|
|
#include <time.h>
|
|
#include <irtltoken.h>
|
|
#include "dbgutil.h"
|
|
#include <string.hxx>
|
|
#include <acache.hxx>
|
|
#include <tokencache.hxx>
|
|
#include <ntmsv1_0.h>
|
|
#include <lm.h>
|
|
|
|
//
|
|
// Security related headers
|
|
//
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
|
|
extern "C"
|
|
{
|
|
#include <secint.h>
|
|
}
|
|
//
|
|
// lonsint.dll related heade files
|
|
//
|
|
#include <lonsi.hxx>
|
|
#include <tslogon.hxx>
|
|
|
|
ALLOC_CACHE_HANDLER * TOKEN_CACHE_ENTRY::sm_pachTokenCacheEntry = NULL;
|
|
HCRYPTPROV TOKEN_CACHE_ENTRY::sm_hCryptProv = NULL;
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_KEY::CreateCacheKey(
|
|
WCHAR * pszUserName,
|
|
WCHAR * pszDomainName,
|
|
DWORD dwLogonMethod
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Build the key used for token cache
|
|
|
|
Arguments:
|
|
|
|
pszUserName - User name
|
|
pszDomainName - Domain name
|
|
dwLogonMethod - Logon method
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
WCHAR achNum[ 33 ];
|
|
|
|
if ( pszUserName == NULL ||
|
|
pszDomainName == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
hr = m_strHashKey.Copy( pszUserName );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = m_strHashKey.Append( pszDomainName );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_wcsupr( m_strHashKey.QueryStr() );
|
|
|
|
_ultow( dwLogonMethod, achNum, 10 );
|
|
|
|
hr = m_strHashKey.Append( achNum );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
TOKEN_CACHE_ENTRY::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Token entry lookaside initialization
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Initialize allocation lookaside
|
|
//
|
|
|
|
acConfig.nConcurrency = 1;
|
|
acConfig.nThreshold = 100;
|
|
acConfig.cbSize = sizeof( TOKEN_CACHE_ENTRY );
|
|
|
|
DBG_ASSERT( sm_pachTokenCacheEntry == NULL );
|
|
|
|
sm_pachTokenCacheEntry = new ALLOC_CACHE_HANDLER( "TOKEN_CACHE_ENTRY",
|
|
&acConfig );
|
|
|
|
if ( sm_pachTokenCacheEntry == NULL || !sm_pachTokenCacheEntry->IsValid() )
|
|
{
|
|
if( sm_pachTokenCacheEntry != NULL )
|
|
{
|
|
delete sm_pachTokenCacheEntry;
|
|
sm_pachTokenCacheEntry = NULL;
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error initializing sm_pachTokenCacheEntry. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get a handle to the CSP we'll use for our MD5 hash functions.
|
|
//
|
|
|
|
if ( !CryptAcquireContext( &sm_hCryptProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"CryptAcquireContext() failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
TOKEN_CACHE_ENTRY::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Token cache cleanup
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( sm_pachTokenCacheEntry != NULL )
|
|
{
|
|
delete sm_pachTokenCacheEntry;
|
|
sm_pachTokenCacheEntry = NULL;
|
|
}
|
|
|
|
if ( sm_hCryptProv != NULL)
|
|
{
|
|
CryptReleaseContext( sm_hCryptProv, 0 );
|
|
|
|
sm_hCryptProv = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_ENTRY::GenMD5Password(
|
|
IN WCHAR * pszPassword,
|
|
OUT STRA * pstrMD5Password
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Generate MD5 hashed password
|
|
|
|
Arguments:
|
|
|
|
pszPassword - Password to be MD5 hashed
|
|
pstrMD5Password - MD5 hashed password
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwError;
|
|
HCRYPTHASH hHash = NULL;
|
|
DWORD dwHashDataLen;
|
|
STACK_BUFFER( buffHashData, DEFAULT_MD5_HASH_SIZE );
|
|
|
|
if( pszPassword == NULL || pstrMD5Password == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( !CryptCreateHash( sm_hCryptProv,
|
|
CALG_MD5,
|
|
0,
|
|
0,
|
|
&hHash ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"CryptCreateHash() failed : hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if ( !CryptHashData( hHash,
|
|
( BYTE * )pszPassword,
|
|
( DWORD )wcslen( pszPassword ) * sizeof( WCHAR ),
|
|
0 ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"CryptHashData() failed : hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
dwHashDataLen = DEFAULT_MD5_HASH_SIZE;
|
|
|
|
if ( !CryptGetHashParam( hHash,
|
|
HP_HASHVAL,
|
|
( BYTE * )buffHashData.QueryPtr(),
|
|
&dwHashDataLen,
|
|
0 ) )
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if( dwError == ERROR_MORE_DATA )
|
|
{
|
|
if( !buffHashData.Resize( dwHashDataLen ) )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
if( !CryptGetHashParam( hHash,
|
|
HP_HASHVAL,
|
|
( BYTE * )buffHashData.QueryPtr(),
|
|
&dwHashDataLen,
|
|
0 ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert binary data to ASCII hex representation
|
|
//
|
|
|
|
hr = ToHex( buffHashData, *pstrMD5Password );
|
|
|
|
exit:
|
|
|
|
if( hHash != NULL )
|
|
{
|
|
CryptDestroyHash( hHash );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_ENTRY::EqualMD5Password(
|
|
IN WCHAR * pszPassword,
|
|
OUT BOOL * fEqual
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Does the password in the current entry equal to the one passed in?
|
|
|
|
Arguments:
|
|
|
|
pszPassword - Password to be evaluated
|
|
fEqual - TRUE if two MD5 hashed password equal
|
|
|
|
Return:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
{
|
|
STACK_STRA( strMD5Password, 2 * DEFAULT_MD5_HASH_SIZE + 1 );
|
|
HRESULT hr;
|
|
|
|
hr = GenMD5Password( pszPassword, &strMD5Password );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*fEqual = m_strMD5Password.Equals( strMD5Password );
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_ENTRY::Create(
|
|
IN HANDLE hToken,
|
|
IN WCHAR * pszPassword,
|
|
IN LARGE_INTEGER * pliPwdExpiry,
|
|
IN BOOL fImpersonation
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Initialize a cached token
|
|
|
|
Arguments:
|
|
|
|
hToken - Token
|
|
liPwdExpiry - Password expiration time
|
|
fImpersonation - Is hToken an impersonation token?
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
if ( hToken == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( fImpersonation )
|
|
{
|
|
m_hImpersonationToken = hToken;
|
|
}
|
|
else
|
|
{
|
|
m_hPrimaryToken = hToken;
|
|
}
|
|
|
|
|
|
memcpy( ( VOID * )&m_liPwdExpiry,
|
|
( VOID * )pliPwdExpiry,
|
|
sizeof( LARGE_INTEGER ) );
|
|
|
|
return GenMD5Password( pszPassword, &m_strMD5Password );
|
|
}
|
|
|
|
HANDLE
|
|
TOKEN_CACHE_ENTRY::QueryImpersonationToken(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Get impersonation token
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
Handle to impersonation token
|
|
|
|
--*/
|
|
{
|
|
if ( m_hImpersonationToken == NULL )
|
|
{
|
|
LockCacheEntry();
|
|
|
|
if ( m_hImpersonationToken == NULL )
|
|
{
|
|
DBG_ASSERT( m_hPrimaryToken != NULL );
|
|
|
|
if ( !DuplicateTokenEx( m_hPrimaryToken,
|
|
0,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&m_hImpersonationToken ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"DuplicateTokenEx failed, GetLastError = %lx\n",
|
|
GetLastError() ));
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( m_hImpersonationToken != NULL );
|
|
}
|
|
}
|
|
|
|
UnlockCacheEntry();
|
|
}
|
|
|
|
if( m_hImpersonationToken && !QueryDisBackupPriToken() )
|
|
{
|
|
//
|
|
// Disable the backup privilege for the token
|
|
//
|
|
|
|
DisableTokenBackupPrivilege( m_hImpersonationToken );
|
|
}
|
|
|
|
return m_hImpersonationToken;
|
|
}
|
|
|
|
HANDLE
|
|
TOKEN_CACHE_ENTRY::QueryPrimaryToken(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Get primary token
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
Handle to primary token
|
|
|
|
--*/
|
|
{
|
|
if ( m_hPrimaryToken == NULL )
|
|
{
|
|
LockCacheEntry();
|
|
|
|
if ( m_hPrimaryToken == NULL )
|
|
{
|
|
DBG_ASSERT( m_hImpersonationToken != NULL );
|
|
|
|
if ( !DuplicateTokenEx( m_hImpersonationToken,
|
|
0,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&m_hPrimaryToken ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"DuplicateTokenEx failed, GetLastError = %lx\n",
|
|
GetLastError() ));
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( m_hPrimaryToken != NULL );
|
|
}
|
|
}
|
|
|
|
UnlockCacheEntry();
|
|
}
|
|
|
|
return m_hPrimaryToken;
|
|
}
|
|
|
|
PSID
|
|
TOKEN_CACHE_ENTRY::QuerySid(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Get the sid for this token
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
Points to SID buffer owned by this object
|
|
|
|
--*/
|
|
{
|
|
BYTE abTokenUser[ SID_DEFAULT_SIZE + sizeof( TOKEN_USER ) ];
|
|
TOKEN_USER * pTokenUser = (TOKEN_USER*) abTokenUser;
|
|
BOOL fRet;
|
|
HANDLE hImpersonation;
|
|
DWORD cbBuffer;
|
|
|
|
hImpersonation = QueryImpersonationToken();
|
|
if ( hImpersonation == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if ( m_pSid == NULL )
|
|
{
|
|
LockCacheEntry();
|
|
|
|
fRet = GetTokenInformation( hImpersonation,
|
|
TokenUser,
|
|
pTokenUser,
|
|
sizeof( abTokenUser ),
|
|
&cbBuffer );
|
|
if ( fRet )
|
|
{
|
|
//
|
|
// If we can't get the sid, then that is OK. We're return NULL
|
|
// and as a result we will do the access check always
|
|
//
|
|
|
|
memcpy( m_abSid,
|
|
pTokenUser->User.Sid,
|
|
sizeof( m_abSid ) );
|
|
|
|
m_pSid = m_abSid;
|
|
}
|
|
|
|
UnlockCacheEntry();
|
|
}
|
|
|
|
return m_pSid;
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Initialize token cache
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
LONG lErrorCode;
|
|
DWORD dwData;
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof( DWORD );
|
|
DWORD dwFilter;
|
|
DWORD dwFlags;
|
|
DWORD csecTTL = DEFAULT_CACHED_TOKEN_TTL;
|
|
|
|
hr = TOKEN_CACHE_ENTRY::Initialize();
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Token cache entry init failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// What is the TTL for the token cache
|
|
//
|
|
|
|
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
|
|
0,
|
|
KEY_READ,
|
|
&m_hKey ) == ERROR_SUCCESS )
|
|
{
|
|
DBG_ASSERT( m_hKey != NULL );
|
|
|
|
if ( RegQueryValueEx( m_hKey,
|
|
L"LastPriorityUPNLogon",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&cbData ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD )
|
|
{
|
|
m_dwLastPriorityUPNLogon = dwData;
|
|
}
|
|
|
|
if ( RegQueryValueEx( m_hKey,
|
|
L"UserTokenTTL",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&cbData ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD )
|
|
{
|
|
csecTTL = dwData;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We'll use TTL for scavenge period, and expect two inactive periods to
|
|
// flush
|
|
//
|
|
|
|
hr = SetCacheConfiguration( csecTTL * 1000,
|
|
csecTTL * 1000,
|
|
0,
|
|
NULL );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Create an event
|
|
//
|
|
|
|
m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if( m_hEvent == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"CreateEvent() failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Watch the registry key for a change of value
|
|
//
|
|
|
|
dwFilter = REG_NOTIFY_CHANGE_LAST_SET;
|
|
|
|
lErrorCode = RegNotifyChangeKeyValue( m_hKey,
|
|
TRUE,
|
|
dwFilter,
|
|
m_hEvent,
|
|
TRUE );
|
|
if( lErrorCode != ERROR_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"RegNotifyChangeKeyValue failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Register a callback function to wait on the event
|
|
//
|
|
|
|
dwFlags = WT_EXECUTEINPERSISTENTTHREAD;
|
|
|
|
if( !RegisterWaitForSingleObject(
|
|
&m_hWaitObject,
|
|
m_hEvent,
|
|
( WAITORTIMERCALLBACK )TOKEN_CACHE::FlushTokenCacheWaitCallback,
|
|
this,
|
|
INFINITE,
|
|
dwFlags ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"RegisterWaitForSingleObject failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
hr = ThreadPoolInitialize( 0 );
|
|
if( FAILED(hr) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"ThreadPoolInitialize failed. hr = 0x%x\n",
|
|
hr ));
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
m_fInitializedThreadPool = TRUE;
|
|
|
|
return NO_ERROR;
|
|
|
|
Failure:
|
|
|
|
TOKEN_CACHE::Terminate();
|
|
|
|
return hr;
|
|
}
|
|
|
|
VOID
|
|
TOKEN_CACHE::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Terminate token cache
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if( m_fInitializedThreadPool )
|
|
{
|
|
DBG_REQUIRE(SUCCEEDED(ThreadPoolTerminate()));
|
|
m_fInitializedThreadPool = FALSE;
|
|
}
|
|
|
|
if( m_hWaitObject != NULL )
|
|
{
|
|
UnregisterWait( m_hWaitObject );
|
|
|
|
m_hWaitObject = NULL;
|
|
}
|
|
|
|
if( m_hEvent != NULL )
|
|
{
|
|
CloseHandle( m_hEvent );
|
|
|
|
m_hEvent = NULL;
|
|
}
|
|
|
|
if( m_hKey != NULL )
|
|
{
|
|
RegCloseKey( m_hKey );
|
|
|
|
m_hKey = NULL;
|
|
}
|
|
|
|
return TOKEN_CACHE_ENTRY::Terminate();
|
|
}
|
|
|
|
// static
|
|
VOID
|
|
WINAPI
|
|
TOKEN_CACHE::FlushTokenCacheWaitCallback(
|
|
PVOID pParam,
|
|
BOOL fWaitFired
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Flush the token cache and reset change notification for the
|
|
"UserTokenTTL" registry key.
|
|
|
|
Arguments:
|
|
|
|
Not used for now
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
TOKEN_CACHE * pTokenCache;
|
|
LIST_ENTRY listHead;
|
|
DWORD dwFilter;
|
|
DWORD dwData = 0;
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof( DWORD );
|
|
DWORD dwFlushTokenCache;
|
|
|
|
UNREFERENCED_PARAMETER( fWaitFired );
|
|
|
|
pTokenCache = ( TOKEN_CACHE * )pParam;
|
|
DBG_ASSERT( pTokenCache != NULL );
|
|
DBG_ASSERT( pTokenCache->CheckSignature() );
|
|
|
|
if ( RegQueryValueEx( pTokenCache->QueryRegKey(),
|
|
L"FlushTokenCache",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&cbData ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD )
|
|
{
|
|
dwFlushTokenCache = dwData;
|
|
|
|
if( dwFlushTokenCache > 0 )
|
|
{
|
|
//
|
|
// Flush the whole token cache since the UserTokenTTL
|
|
// has been changed.
|
|
//
|
|
|
|
InitializeListHead( &listHead );
|
|
|
|
pTokenCache->FlushByRegChange( &listHead );
|
|
|
|
//
|
|
// Remove all cache entries in the cache entry list
|
|
//
|
|
pTokenCache->CleanupCacheEntryListItems( &listHead );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the event
|
|
//
|
|
|
|
ResetEvent( pTokenCache->QueryEventHandle() );
|
|
|
|
//
|
|
// Watch the registry key for a change of value
|
|
//
|
|
|
|
dwFilter = REG_NOTIFY_CHANGE_LAST_SET;
|
|
|
|
RegNotifyChangeKeyValue( pTokenCache->QueryRegKey(),
|
|
TRUE,
|
|
dwFilter,
|
|
pTokenCache->QueryEventHandle(),
|
|
TRUE );
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE::GetCachedToken(
|
|
IN LPWSTR pszUserName,
|
|
IN LPWSTR pszDomain,
|
|
IN LPWSTR pszPassword,
|
|
IN DWORD dwLogonMethod,
|
|
IN BOOL fUseSubAuth,
|
|
IN BOOL fPossibleUPNLogon,
|
|
IN PSOCKADDR pSockAddr,
|
|
OUT TOKEN_CACHE_ENTRY ** ppCachedToken,
|
|
OUT DWORD * pdwLogonError
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Get cached token (the friendly interface for the token cache)
|
|
|
|
Arguments:
|
|
|
|
pszUserName - User name
|
|
pszDomain - Domain name
|
|
pszPassword - Password
|
|
dwLogonMethod - Logon method (batch, interactive, etc)
|
|
fUseSubAuth - Use subauthenticator to logon the anonymous user
|
|
fPossibleUPNLogon - TRUE if we may need to do UPN logon,
|
|
otherwise FALSE
|
|
psockAddr - Remote IP address, could be IPv4 or IPv6 address
|
|
ppCachedToken - Filled with cached token on success
|
|
pdwLogonError - Set to logon failure if *ppCacheToken==NULL
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
TOKEN_CACHE_KEY tokenKey;
|
|
TOKEN_CACHE_ENTRY * pCachedToken = NULL;
|
|
HRESULT hr;
|
|
HANDLE hToken = NULL;
|
|
LARGE_INTEGER liPwdExpiry;
|
|
LPVOID pProfile = NULL;
|
|
DWORD dwProfileLength = 0;
|
|
WCHAR * pszAtSign = NULL;
|
|
WCHAR * pDomain[2];
|
|
BOOL fRet;
|
|
WCHAR * pszSlash = NULL;
|
|
CHAR achPassword[32];
|
|
STACK_STRU( strPassword, 32 );
|
|
STACK_STRA( straUserName, 64 );
|
|
STACK_STRA( straDomainName, 64 );
|
|
BOOL fEqualPassword = FALSE;
|
|
SECURITY_STATUS ss = SEC_E_OK;
|
|
|
|
if ( pszUserName == NULL ||
|
|
pszDomain == NULL ||
|
|
pszPassword == NULL ||
|
|
ppCachedToken == NULL ||
|
|
pdwLogonError == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
liPwdExpiry.HighPart = 0x7fffffff;
|
|
liPwdExpiry.LowPart = 0xffffffff;
|
|
|
|
*ppCachedToken = NULL;
|
|
*pdwLogonError = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Find a domain in the username if we don't have one explicitly mentioned
|
|
//
|
|
|
|
if ( pszDomain[ 0 ] == L'\0' )
|
|
{
|
|
//
|
|
// Find the \ in DOMAIN\USERNAME
|
|
//
|
|
|
|
pszSlash = wcschr( pszUserName, L'\\' );
|
|
if ( pszSlash != NULL )
|
|
{
|
|
pszDomain = pszUserName;
|
|
pszUserName = pszSlash + 1;
|
|
*pszSlash = L'\0';
|
|
}
|
|
}
|
|
|
|
if( fUseSubAuth )
|
|
{
|
|
if( FAILED( hr = straUserName.CopyW( pszUserName ) ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
if( !IISNetUserCookieA( straUserName.QueryStr(),
|
|
IIS_SUBAUTH_SEED,
|
|
achPassword,
|
|
sizeof( achPassword ) ) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
|
|
if( FAILED( hr = strPassword.CopyA( achPassword ) ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
if( FAILED( straDomainName.CopyW( pszDomain ) ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
pszPassword = strPassword.QueryStr();
|
|
dwLogonMethod = LOGON32_LOGON_IIS_NETWORK;
|
|
}
|
|
|
|
//
|
|
// Find the key to look for
|
|
//
|
|
|
|
hr = tokenKey.CreateCacheKey( pszUserName,
|
|
pszDomain,
|
|
dwLogonMethod );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
//
|
|
// Look for it
|
|
//
|
|
|
|
hr = FindCacheEntry( &tokenKey,
|
|
(CACHE_ENTRY**) ppCachedToken );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
DBG_ASSERT( *ppCachedToken != NULL );
|
|
|
|
hr = ( *ppCachedToken )->EqualMD5Password( pszPassword,
|
|
&fEqualPassword );
|
|
if( FAILED( hr ) )
|
|
{
|
|
( *ppCachedToken )->DereferenceCacheEntry();
|
|
*ppCachedToken = NULL;
|
|
|
|
goto ExitPoint;
|
|
}
|
|
|
|
if( fEqualPassword )
|
|
{
|
|
//
|
|
// Cache hit
|
|
//
|
|
|
|
goto ExitPoint;
|
|
}
|
|
|
|
//
|
|
// The password does not match
|
|
//
|
|
|
|
( *ppCachedToken )->DereferenceCacheEntry();
|
|
*ppCachedToken = NULL;
|
|
}
|
|
|
|
//
|
|
// Ok. It wasn't in the cache, create a token and add it
|
|
//
|
|
// There are three cases to deal with
|
|
//
|
|
// 1) We just want the local system token (thats trivial)
|
|
// 2) We want to call advapi32!LogonUser for a "normal logon"
|
|
// 3) We are doing passport special logon thru lonsint (LsaLogonUser())
|
|
//
|
|
|
|
if ( dwLogonMethod == IIS_LOGON_METHOD_LOCAL_SYSTEM &&
|
|
_wcsicmp( L"LocalSystem", pszUserName ) == 0 )
|
|
{
|
|
if (!OpenProcessToken(
|
|
GetCurrentProcess(), // handle to process
|
|
TOKEN_ALL_ACCESS, // desired access
|
|
&hToken // returned token
|
|
) )
|
|
{
|
|
//
|
|
// If we couldn't logon, then return no error. The caller will
|
|
// determine failure due to *ppCachedToken == NULL
|
|
//
|
|
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
|
|
//
|
|
// OpenProcessToken gives back a primary token
|
|
// Below in the call to pCachedToken->Create we decide
|
|
// if the token is an impersonation token or not based
|
|
// on the LogonMethod. We know this is a primary token
|
|
// therefor we set the LogonMethod here
|
|
//
|
|
dwLogonMethod = LOGON32_LOGON_SERVICE;
|
|
}
|
|
else if ( dwLogonMethod == IIS_LOGON_METHOD_PASSPORT )
|
|
{
|
|
//
|
|
// Register the remote IP address with LSA so that it can be logged
|
|
//
|
|
|
|
if( pSockAddr != NULL )
|
|
{
|
|
if( pSockAddr->sa_family == AF_INET )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN ) );
|
|
}
|
|
else if( pSockAddr->sa_family == AF_INET6 )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN6 ) );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
if( !NT_SUCCESS( ss ) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
fRet = IISLogonPassportUserW( pszUserName,
|
|
pszDomain,
|
|
&hToken );
|
|
if ( !fRet )
|
|
{
|
|
hr = NO_ERROR;
|
|
*pdwLogonError = ERROR_LOGON_FAILURE;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( fUseSubAuth )
|
|
{
|
|
//
|
|
// Try to logon the anonymous user using the IIS
|
|
// subauthenticator
|
|
//
|
|
if( !IISLogonNetUserA( straUserName.QueryStr(),
|
|
straDomainName.QueryStr(),
|
|
achPassword,
|
|
NULL,
|
|
IIS_SUBAUTH_ID,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
&liPwdExpiry ) )
|
|
{
|
|
hr = NO_ERROR;
|
|
*pdwLogonError = GetLastError();
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszAtSign = wcschr( pszUserName, L'@' );
|
|
if( pszAtSign != NULL && fPossibleUPNLogon )
|
|
{
|
|
if( !m_dwLastPriorityUPNLogon )
|
|
{
|
|
//
|
|
// Try UPN logon first
|
|
//
|
|
pDomain[0] = L"";
|
|
pDomain[1] = pszDomain;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Try default domain logon first
|
|
//
|
|
pDomain[0] = pszDomain;
|
|
pDomain[1] = L"";
|
|
}
|
|
|
|
//
|
|
// Register the remote IP address with LSA so that it can be logged
|
|
//
|
|
|
|
if( pSockAddr != NULL )
|
|
{
|
|
if( pSockAddr->sa_family == AF_INET )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN ) );
|
|
}
|
|
else if( pSockAddr->sa_family == AF_INET6 )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN6 ) );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
if( !NT_SUCCESS( ss ) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
|
|
|
|
fRet = LogonUserEx( pszUserName,
|
|
pDomain[0],
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
);
|
|
|
|
ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
|
|
|
|
if( !fRet )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
|
|
if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
|
|
{
|
|
goto AccountLockedOut;
|
|
}
|
|
|
|
if( *pdwLogonError == ERROR_LOGON_FAILURE )
|
|
{
|
|
|
|
//
|
|
// Register the remote IP address with LSA so that it can be logged
|
|
//
|
|
|
|
if( pSockAddr != NULL )
|
|
{
|
|
if( pSockAddr->sa_family == AF_INET )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN ) );
|
|
}
|
|
else if( pSockAddr->sa_family == AF_INET6 )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN6 ) );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
if( !NT_SUCCESS( ss ) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
|
|
|
|
fRet = LogonUserEx( pszUserName,
|
|
pDomain[1],
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
);
|
|
|
|
ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
|
|
|
|
if ( !fRet )
|
|
{
|
|
//
|
|
// If we couldn't logon, then return no error. The caller will
|
|
// determine failure due to *ppCachedToken == NULL
|
|
//
|
|
|
|
*pdwLogonError = GetLastError();
|
|
|
|
if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
|
|
{
|
|
goto AccountLockedOut;
|
|
}
|
|
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Register the remote IP address with LSA so that it can be logged
|
|
//
|
|
|
|
if( pSockAddr != NULL )
|
|
{
|
|
if( pSockAddr->sa_family == AF_INET )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN ) );
|
|
}
|
|
else if( pSockAddr->sa_family == AF_INET6 )
|
|
{
|
|
ss = SecpSetIPAddress( ( PUCHAR )pSockAddr,
|
|
sizeof( SOCKADDR_IN6 ) );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
if( !NT_SUCCESS( ss ) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The user name is absolutely not in UPN format
|
|
//
|
|
|
|
ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
|
|
|
|
fRet = LogonUserEx( pszUserName,
|
|
pszDomain,
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
);
|
|
|
|
ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
|
|
|
|
if( !fRet )
|
|
{
|
|
//
|
|
// If we couldn't logon, then return no error. The caller will
|
|
// determine failure due to *ppCachedToken == NULL
|
|
//
|
|
|
|
*pdwLogonError = GetLastError();
|
|
|
|
if( *pdwLogonError == ERROR_ACCOUNT_LOCKED_OUT )
|
|
{
|
|
goto AccountLockedOut;
|
|
}
|
|
|
|
hr = NO_ERROR;
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the entry
|
|
//
|
|
|
|
pCachedToken = new TOKEN_CACHE_ENTRY( this );
|
|
if ( pCachedToken == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto ExitPoint;
|
|
}
|
|
|
|
//
|
|
// Set the cache key
|
|
//
|
|
|
|
hr = pCachedToken->SetCacheKey( &tokenKey );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
//
|
|
// Get the password expiration information for the current user
|
|
//
|
|
|
|
//
|
|
// Set the token/properties
|
|
//
|
|
|
|
hr = pCachedToken->Create( hToken,
|
|
pszPassword,
|
|
pProfile ?
|
|
&(( ( PMSV1_0_INTERACTIVE_PROFILE )pProfile )->PasswordMustChange) :
|
|
&liPwdExpiry,
|
|
dwLogonMethod == LOGON32_LOGON_NETWORK ||
|
|
dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT ||
|
|
dwLogonMethod == IIS_LOGON_METHOD_PASSPORT ||
|
|
dwLogonMethod == LOGON32_LOGON_IIS_NETWORK );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
AddCacheEntry( pCachedToken );
|
|
|
|
//
|
|
// Return it
|
|
//
|
|
|
|
*ppCachedToken = pCachedToken;
|
|
|
|
goto ExitPoint;
|
|
|
|
AccountLockedOut:
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
//
|
|
// Succeeded hr means only passwords don't match
|
|
//
|
|
|
|
FlushCacheEntry( &tokenKey );
|
|
}
|
|
|
|
hr = NO_ERROR;
|
|
|
|
ExitPoint:
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( pCachedToken != NULL )
|
|
{
|
|
pCachedToken->DereferenceCacheEntry();
|
|
}
|
|
if ( hToken != NULL )
|
|
{
|
|
CloseHandle( hToken );
|
|
}
|
|
}
|
|
|
|
if ( pProfile != NULL )
|
|
{
|
|
LsaFreeReturnBuffer( pProfile );
|
|
}
|
|
|
|
//
|
|
// Replace the slash before we continue
|
|
//
|
|
|
|
if ( pszSlash != NULL )
|
|
{
|
|
*pszSlash = L'\\';
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
ToHex(
|
|
IN BUFFER & buffSrc,
|
|
OUT STRA & strDst
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert binary data to ASCII hex representation
|
|
|
|
Arguments:
|
|
|
|
buffSrc - binary data to convert
|
|
strDst - buffer receiving ASCII representation of pSrc
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
#define TOHEX(a) ( (a) >= 10 ? 'a' + (a) - 10 : '0' + (a) )
|
|
|
|
HRESULT hr = S_OK;
|
|
PBYTE pSrc;
|
|
PCHAR pDst;
|
|
|
|
hr = strDst.Resize( 2 * buffSrc.QuerySize() + 1 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
pSrc = ( PBYTE ) buffSrc.QueryPtr();
|
|
pDst = strDst.QueryStr();
|
|
|
|
for ( UINT i = 0, j = 0 ; i < buffSrc.QuerySize() ; i++ )
|
|
{
|
|
UINT v;
|
|
v = pSrc[ i ] >> 4;
|
|
pDst[ j++ ] = ( CHAR )TOHEX( v );
|
|
v = pSrc[ i ] & 0x0f;
|
|
pDst[ j++ ] = ( CHAR )TOHEX( v );
|
|
}
|
|
|
|
DBG_REQUIRE( strDst.SetLen( j ) );
|
|
|
|
exit:
|
|
|
|
return hr;
|
|
}
|
|
|
|
|