/*++ 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 #include #include #include "dbgutil.h" #include #include #include #include #include // // Security related headers // #define SECURITY_WIN32 #include extern "C" { #include } // // lonsint.dll related heade files // #include #include 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; }