1002 lines
22 KiB
C++
1002 lines
22 KiB
C++
/*++
|
|
|
|
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 "dbgutil.h"
|
|
#include <acache.hxx>
|
|
#include <string.hxx>
|
|
#include <tokencache.hxx>
|
|
#include <irtltoken.h>
|
|
#include <ntsecapi.h>
|
|
#include <wincrypt.h>
|
|
|
|
ALLOC_CACHE_HANDLER * TOKEN_CACHE_ENTRY::sm_pachTokenCacheEntry = NULL;
|
|
|
|
//
|
|
// Handle of a cryptographic service provider
|
|
//
|
|
|
|
HCRYPTPROV g_hCryptProv = NULL;
|
|
|
|
//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 )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error initializing sm_pachTokenCacheEntry. 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;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_ENTRY::Create(
|
|
IN HANDLE hToken,
|
|
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;
|
|
}
|
|
|
|
if (pliPwdExpiry)
|
|
{
|
|
memcpy( ( VOID * )&m_liPwdExpiry,
|
|
( VOID * )pliPwdExpiry,
|
|
sizeof( LARGE_INTEGER ) );
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
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,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&m_hImpersonationToken ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"DuplicateTokenEx failed, GetLastError = %lx\n",
|
|
GetLastError() ));
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( m_hImpersonationToken != NULL );
|
|
|
|
//
|
|
// Tweak the token so that all member of the worker process group
|
|
// can access it, and so that it works correctly for OOP requests
|
|
//
|
|
|
|
HRESULT hr = GrantWpgAccessToToken( m_hImpersonationToken );
|
|
|
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
|
|
|
hr = AddWpgToTokenDefaultDacl( m_hImpersonationToken );
|
|
|
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
|
}
|
|
}
|
|
|
|
UnlockCacheEntry();
|
|
}
|
|
|
|
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,
|
|
TOKEN_ALL_ACCESS,
|
|
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_KEY::GenMD5HashKey(
|
|
IN STRU & strKey,
|
|
OUT STRA * strHashKey
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Generate MD5 hash key used for token cache
|
|
|
|
Arguments:
|
|
|
|
strKey - string to be MD5 hashed
|
|
strHashKey - MD5 hashed string
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwError;
|
|
HCRYPTHASH hHash = NULL;
|
|
DWORD dwHashDataLen;
|
|
STACK_BUFFER( buffHashData, DEFAULT_MD5_HASH_SIZE );
|
|
|
|
if ( !CryptCreateHash( g_hCryptProv,
|
|
CALG_MD5,
|
|
0,
|
|
0,
|
|
&hHash ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"CryptCreateHash() failed : hr = 0x%x\n",
|
|
hr ));
|
|
|
|
return hr;
|
|
}
|
|
|
|
if ( !CryptHashData( hHash,
|
|
( BYTE * )strKey.QueryStr(),
|
|
strKey.QueryCB(),
|
|
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, _strHashKey );
|
|
|
|
exit:
|
|
|
|
CryptDestroyHash( hHash );
|
|
|
|
ZeroMemory( ( VOID * )strKey.QueryStr(), strKey.QueryCB() );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE_KEY::CreateCacheKey(
|
|
WCHAR * pszUserName,
|
|
WCHAR * pszDomainName,
|
|
WCHAR * pszPassword,
|
|
DWORD dwLogonMethod
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Build the key used for token cache
|
|
|
|
Arguments:
|
|
|
|
pszUserName - User name
|
|
pszDomainName - Domain name
|
|
pszPassword - Password
|
|
dwLogonMethod - Logon method
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
WCHAR achNum[ 64 ];
|
|
STACK_STRU( strKey, 64 );
|
|
|
|
if ( pszUserName == NULL ||
|
|
pszDomainName == NULL ||
|
|
pszPassword == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
hr = strKey.Copy( pszUserName );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = strKey.Append( pszDomainName );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = strKey.Append( pszPassword );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_ultow( dwLogonMethod, achNum, 10 );
|
|
|
|
hr = strKey.Append( achNum );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return GenMD5HashKey( strKey, &_strHashKey );
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Initialize token cache
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwData;
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof( DWORD );
|
|
DWORD csecTTL = DEFAULT_CACHED_TOKEN_TTL;
|
|
HKEY hKey;
|
|
|
|
//
|
|
// What is the TTL for the token cache
|
|
//
|
|
|
|
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS )
|
|
{
|
|
DBG_ASSERT( hKey != NULL );
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
L"LastPriorityUPNLogon",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&cbData ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD )
|
|
{
|
|
m_dwLastPriorityUPNLogon = dwData;
|
|
}
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
L"UserTokenTTL",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&cbData ) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD )
|
|
{
|
|
csecTTL = dwData;
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
//
|
|
// 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 ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get a handle to the CSP we'll use for our MD5 hash functions.
|
|
//
|
|
|
|
if ( !CryptAcquireContext( &g_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 TOKEN_CACHE_ENTRY::Initialize();
|
|
}
|
|
|
|
VOID
|
|
TOKEN_CACHE::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Terminate token cache
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( g_hCryptProv )
|
|
{
|
|
CryptReleaseContext( g_hCryptProv, 0 );
|
|
|
|
g_hCryptProv = NULL;
|
|
}
|
|
|
|
return TOKEN_CACHE_ENTRY::Terminate();
|
|
}
|
|
|
|
HRESULT
|
|
TOKEN_CACHE::GetCachedToken(
|
|
IN LPWSTR pszUserName,
|
|
IN LPWSTR pszDomain,
|
|
IN LPWSTR pszPassword,
|
|
IN DWORD dwLogonMethod,
|
|
IN BOOL fPossibleUPNLogon,
|
|
OUT TOKEN_CACHE_ENTRY ** ppCachedToken,
|
|
OUT DWORD * pdwLogonError,
|
|
BOOL fAllowLocalSystem /* = FALSE */
|
|
)
|
|
/*++
|
|
|
|
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)
|
|
fPossibleUPNLogon - TRUE if we may need to do UPN logon,
|
|
otherwise FALSE
|
|
ppCachedToken - Filled with cached token on success
|
|
pdwLogonError - Set to logon failure if *ppCacheToken==NULL
|
|
pszDefaultDomain - Default domain specified in metabase
|
|
|
|
Return:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
TOKEN_CACHE_KEY tokenKey;
|
|
TOKEN_CACHE_ENTRY * pCachedToken;
|
|
HRESULT hr;
|
|
HANDLE hToken = NULL;
|
|
LARGE_INTEGER liPwdExpiry;
|
|
LPVOID pProfile = NULL;
|
|
DWORD dwProfileLength = 0;
|
|
WCHAR * pszAtSign = NULL;
|
|
WCHAR * pDomain[2];
|
|
|
|
if ( pszUserName == NULL ||
|
|
pszDomain == NULL ||
|
|
pszPassword == NULL ||
|
|
ppCachedToken == NULL ||
|
|
pdwLogonError == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
*ppCachedToken = NULL;
|
|
*pdwLogonError = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Find the key to look for
|
|
//
|
|
|
|
hr = tokenKey.CreateCacheKey( pszUserName,
|
|
pszDomain,
|
|
pszPassword,
|
|
dwLogonMethod );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Look for it
|
|
//
|
|
|
|
hr = FindCacheEntry( &tokenKey,
|
|
(CACHE_ENTRY**) ppCachedToken );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
DBG_ASSERT( *ppCachedToken != NULL );
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Ok. It wasn't in the cache, create a token and add it
|
|
//
|
|
|
|
if ( fAllowLocalSystem &&
|
|
0 == _wcsicmp(L"LocalSystem", pszUserName) )
|
|
{
|
|
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
|
|
{
|
|
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"";
|
|
}
|
|
|
|
if(!LogonUserEx( pszUserName,
|
|
pDomain[0],
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
) )
|
|
{
|
|
*pdwLogonError = GetLastError();
|
|
if( *pdwLogonError == ERROR_LOGON_FAILURE )
|
|
{
|
|
if(!LogonUserEx( pszUserName,
|
|
pDomain[1],
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
) )
|
|
{
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The user name is absolutely not in UPN format
|
|
//
|
|
|
|
if(!LogonUserEx( pszUserName,
|
|
pszDomain,
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
NULL, // Logon sid
|
|
&pProfile,
|
|
&dwProfileLength,
|
|
NULL // Quota limits
|
|
) )
|
|
{
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if ( dwLogonMethod == LOGON32_LOGON_NETWORK )
|
|
{
|
|
//
|
|
// Tweak the token so that all member of the worker process group
|
|
// can access it, and so that it works correctly for OOP requests
|
|
//
|
|
// Note that we only do this for impersonation tokens. In the case
|
|
// of a primary token, the TOKEN_CACHE_ENTRY::QueryImpersonationToken
|
|
// will do it.
|
|
//
|
|
|
|
hr = GrantWpgAccessToToken( hToken );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
hr = AddWpgToTokenDefaultDacl( hToken );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the password expiration information for the current user
|
|
//
|
|
|
|
//
|
|
// Set the token/properties
|
|
//
|
|
|
|
hr = pCachedToken->Create( hToken,
|
|
pProfile ?
|
|
&(( ( PMSV1_0_INTERACTIVE_PROFILE )pProfile )->PasswordMustChange) :
|
|
NULL,
|
|
dwLogonMethod == LOGON32_LOGON_NETWORK );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto ExitPoint;
|
|
}
|
|
|
|
AddCacheEntry( pCachedToken );
|
|
|
|
//
|
|
// Return it
|
|
//
|
|
|
|
*ppCachedToken = pCachedToken;
|
|
|
|
ExitPoint:
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( pCachedToken != NULL )
|
|
{
|
|
pCachedToken->DereferenceCacheEntry();
|
|
}
|
|
if ( hToken != NULL )
|
|
{
|
|
CloseHandle( hToken );
|
|
}
|
|
}
|
|
|
|
if ( pProfile != NULL )
|
|
{
|
|
LsaFreeReturnBuffer( pProfile );
|
|
}
|
|
|
|
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++ ] = TOHEX( v );
|
|
v = pSrc[ i ] & 0x0f;
|
|
pDst[ j++ ] = TOHEX( v );
|
|
}
|
|
|
|
DBG_REQUIRE( strDst.SetLen( j ) );
|
|
|
|
exit:
|
|
|
|
return hr;
|
|
}
|
|
|
|
|