Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

813 lines
22 KiB

//============================================================
//
// UserHive.cpp - Class to load/unload specified user's profile
// hive from registry
//
// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved
//
// 01/03/97 a-jmoon created
//
//============================================================
#include "precomp.h"
#include <assertbreak.h>
#include <cregcls.h>
#include "sid.h"
#include "UserHive.h"
#include <cominit.h>
#include <strsafe.h>
#pragma warning(disable : 4995) // we introduced as including strsafe.h gives error for all unsafe string functions
CThreadBase CUserHive::m_criticalSection;
/*****************************************************************************
*
* FUNCTION : CUserHive::CUserHive
*
* DESCRIPTION : Constructor
*
* INPUTS : none
*
* OUTPUTS : none
*
* RETURNS : nothing
*
* COMMENTS :
*
*****************************************************************************/
CUserHive::CUserHive()
{
OSInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ;
GetVersionEx(&OSInfo) ;
m_pOriginalPriv = NULL;
m_dwSize = NULL;
m_hKey = NULL;
}
/*****************************************************************************
*
* FUNCTION : CUserHive::~CUserHive
*
* DESCRIPTION : Destructor
*
* INPUTS : none
*
* OUTPUTS : none
*
* RETURNS : nothing
*
* COMMENTS :
*
*****************************************************************************/
CUserHive::~CUserHive()
{
#ifdef NTONLY
if (m_pOriginalPriv)
RestorePrivilege();
#endif
// NOTE: The destructor does not unload the key. Nor does doing a load unload
// a previously loaded key;
ASSERT_BREAK(m_hKey == NULL);
if (m_hKey != NULL)
{
RegCloseKey(m_hKey);
}
}
/*****************************************************************************
*
* FUNCTION : CUserHive::AcquirePrivilege
*
* DESCRIPTION : Acquires SeRestorePrivilege for calling thread
*
* INPUTS : none
*
* OUTPUTS : none
*
* RETURNS : nothing
*
* COMMENTS :
*
*****************************************************************************/
#ifdef NTONLY
DWORD CUserHive::AcquirePrivilege()
{
// are you calling in twice? Shouldn't.
// at worst, it would cause a leak, so I'm going with it anyway.
ASSERT_BREAK(m_pOriginalPriv == NULL);
BOOL bRetCode = FALSE;
SmartCloseHandle hToken;
TOKEN_PRIVILEGES TPriv ;
LUID LUID ;
// Validate the platform
//======================
// Try getting the thread token.
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES |
TOKEN_QUERY, FALSE, &hToken))
{
GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &m_dwSize);
if (m_dwSize > 0)
{
// This is cleaned in the destructor, so no try/catch required
m_pOriginalPriv = (TOKEN_PRIVILEGES*) new BYTE[m_dwSize];
if (m_pOriginalPriv == NULL)
{
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
}
if (m_pOriginalPriv && GetTokenInformation(hToken, TokenPrivileges, m_pOriginalPriv, m_dwSize, &m_dwSize))
{
bRetCode = LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &LUID) ;
if(bRetCode)
{
TPriv.PrivilegeCount = 1 ;
TPriv.Privileges[0].Luid = LUID ;
TPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
bRetCode = AdjustTokenPrivileges(hToken, FALSE, &TPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL) ;
}
bRetCode = LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &LUID) ;
if(bRetCode)
{
TPriv.PrivilegeCount = 1 ;
TPriv.Privileges[0].Luid = LUID ;
TPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
bRetCode = AdjustTokenPrivileges(hToken, FALSE, &TPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL) ;
}
}
}
if(!bRetCode)
{
return GetLastError() ;
}
return ERROR_SUCCESS ;
}
#endif
/*****************************************************************************
*
* FUNCTION : CUserHive::RestorePrivilege
*
* DESCRIPTION : Restores original status of SeRestorePrivilege
*
* INPUTS : none
*
* OUTPUTS : none
*
* RETURNS : nothing
*
* COMMENTS :
*
*****************************************************************************/
#ifdef NTONLY
void CUserHive::RestorePrivilege()
{
ASSERT_BREAK(m_pOriginalPriv != NULL);
if (m_pOriginalPriv != NULL)
{
SmartCloseHandle hToken;
try
{
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, TRUE, &hToken))
{
AdjustTokenPrivileges(hToken, FALSE, m_pOriginalPriv, m_dwSize, NULL, NULL);
}
}
catch ( ... )
{
delete m_pOriginalPriv;
m_pOriginalPriv = NULL;
m_dwSize = 0;
throw;
}
delete m_pOriginalPriv;
m_pOriginalPriv = NULL;
m_dwSize = 0;
}
}
#endif
DWORD CUserHive::Load(LPCWSTR pszUserName, LPWSTR pszKeyName, size_t PATHSIZE)
{
// NOTE: The destructor does not unload the key. Nor does doing a load unload
// a previously loaded key;
ASSERT_BREAK(m_hKey == NULL);
#ifdef NTONLY
return LoadNT(pszUserName, pszKeyName, PATHSIZE);
#endif
#ifdef WIN9XONLY
return Load95(pszUserName, pszKeyName);
#endif
}
/*****************************************************************************
*
* FUNCTION : CUserHive::LoadNT
*
* DESCRIPTION : Locates user's hive & loads into registry if not already
* present
*
* INPUTS : none
*
* OUTPUTS : pszKeyName receives the expanded SID of the user's
* registry key under HKEY_USERS
*
* RETURNS : nothing
*
* COMMENTS : Hive will remain in registry unless unloaded
*
*****************************************************************************/
#ifdef NTONLY
DWORD CUserHive::LoadNT(LPCTSTR pszUserName, LPTSTR pszKeyName, size_t PATHSIZE)
{
DWORD i, dwSIDSize, dwRetCode, dwDomainNameSize, dwSubAuthorities ;
TCHAR szDomainName[_MAX_PATH], szSID[_MAX_PATH], szTemp[_MAX_PATH] ;
PSID pSID = NULL ;
PSID_IDENTIFIER_AUTHORITY pSIA ;
SID_NAME_USE AccountType ;
CHString sTemp ;
CRegistry Reg ;
// Set the necessary privs
//========================
dwRetCode = AcquirePrivilege() ;
if(dwRetCode != ERROR_SUCCESS)
{
return dwRetCode ;
}
// Look up the user's account info
//================================
dwSIDSize = 0L ;
dwDomainNameSize = sizeof(szDomainName) ;
BOOL bLookup = FALSE;
{
bLookup = LookupAccountName(NULL, pszUserName, pSID, &dwSIDSize, szDomainName, &dwDomainNameSize, &AccountType);
ASSERT_BREAK ( bLookup == FALSE );
if ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError () )
{
pSID = (PSID) malloc( dwSIDSize );
if ( NULL != pSID )
{
bLookup = LookupAccountName(NULL, pszUserName, pSID, &dwSIDSize, szDomainName, &dwDomainNameSize, &AccountType);
}
else
{
RestorePrivilege() ;
return ERROR_NOT_ENOUGH_MEMORY ;
}
}
}
if(!bLookup)
{
if ( pSID )
{
free ( pSID ) ;
pSID = NULL ;
}
RestorePrivilege() ;
return ERROR_BAD_USERNAME ;
}
// Translate the SID into text (a la PSS article Q131320)
//=======================================================
pSIA = GetSidIdentifierAuthority(pSID) ;
dwSubAuthorities = *GetSidSubAuthorityCount(pSID) ;
dwSIDSize = _stprintf(szSID, _T("S-%lu-"), (DWORD) SID_REVISION) ;
if((pSIA->Value[0] != 0) || (pSIA->Value[1] != 0) )
{
dwSIDSize += _stprintf(szSID + _tcslen(szSID), _T("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
(USHORT) pSIA->Value[0],
(USHORT) pSIA->Value[1],
(USHORT) pSIA->Value[2],
(USHORT) pSIA->Value[3],
(USHORT) pSIA->Value[4],
(USHORT) pSIA->Value[5]) ;
}
else
{
dwSIDSize += _stprintf(szSID + _tcslen(szSID), _T("%lu"),
(ULONG)(pSIA->Value[5] ) +
(ULONG)(pSIA->Value[4] << 8) +
(ULONG)(pSIA->Value[3] << 16) +
(ULONG)(pSIA->Value[2] << 24));
}
for(i = 0 ; i < dwSubAuthorities ; i++)
{
dwSIDSize += _stprintf(szSID + dwSIDSize, _T("-%lu"),
*GetSidSubAuthority(pSID, i)) ;
}
free ( pSID ) ;
pSID = NULL ;
// See if the key already exists
//==============================
dwRetCode = Reg.Open(HKEY_USERS, szSID, KEY_READ) ;
// We need to keep a handle open. See m_hKey below, so we'll let the destructor close this.
// Reg.Close();
if(dwRetCode != ERROR_SUCCESS)
{
// Try to locate user's registry hive
//===================================
_stprintf(szTemp, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s"), szSID) ;
dwRetCode = Reg.Open(HKEY_LOCAL_MACHINE, szTemp, KEY_READ) ;
if(dwRetCode == ERROR_SUCCESS)
{
dwRetCode = Reg.GetCurrentKeyValue(_T("ProfileImagePath"), sTemp) ;
Reg.Close() ;
if(dwRetCode == ERROR_SUCCESS)
{
// NT 4 doesn't include the file name in the registry
//===================================================
if(OSInfo.dwMajorVersion >= 4)
{
sTemp += _T("\\NTUSER.DAT") ;
}
ExpandEnvironmentStrings(LPCTSTR(sTemp), szTemp, sizeof(szTemp) / sizeof(TCHAR)) ;
// Try it three times, another process may have the file open
bool bTryTryAgain = false;
int nTries = 0;
do
{
// need to serialize access, using "write" because RegLoadKey wants exclusive access
// even though it is a read operation
m_criticalSection.BeginWrite();
try
{
dwRetCode = (DWORD) RegLoadKey(HKEY_USERS, szSID, szTemp) ;
}
catch ( ... )
{
m_criticalSection.EndWrite();
throw;
}
m_criticalSection.EndWrite();
if ((dwRetCode == ERROR_SHARING_VIOLATION)
&& (++nTries < 11))
{
Sleep(20 * nTries);
bTryTryAgain = true;
}
else
{
bTryTryAgain = false;
}
} while (bTryTryAgain);
// if we still can't get in, tell somebody.
if (dwRetCode == ERROR_SHARING_VIOLATION)
LogErrorMessage(_T("Sharing violation on NTUSER.DAT (Load)"));
}
}
}
if(dwRetCode == ERROR_SUCCESS)
{
HRESULT hr = StringCchCopy(pszKeyName, PATHSIZE, szSID) ;
ASSERT_BREAK(SUCCEEDED(hr));
LONG lRetVal;
CHString sKey(szSID);
sKey += _T("\\Software");
lRetVal = RegOpenKeyEx(HKEY_USERS, sKey, 0, KEY_QUERY_VALUE, &m_hKey);
ASSERT_BREAK(lRetVal == ERROR_SUCCESS);
}
// Restore original privilege level & end self-impersonation
//==========================================================
RestorePrivilege() ;
return dwRetCode ;
}
#endif
/*****************************************************************************
*
* FUNCTION : CUserHive::Load95
*
* DESCRIPTION : Locates user's hive & loads into registry if not already
* present
*
* INPUTS : none
*
* OUTPUTS : pszKeyName receives the expanded SID of the user's
* registry key under HKEY_USERS
*
* RETURNS : nothing
*
* COMMENTS : Hive will remain in registry unless unloaded
*
*****************************************************************************/
#ifdef WIN9XONLY
DWORD CUserHive::Load95(LPCWSTR pszUserName, LPWSTR pszKeyName)
{
DWORD dwRetCode;
WCHAR wszTemp[_MAX_PATH];
TCHAR szTemp[_MAX_PATH];
CHString sTemp ;
CRegistry Reg ;
wcscpy(pszKeyName, pszUserName);
// See if the key already exists
//==============================
dwRetCode = Reg.Open(HKEY_USERS, pszKeyName, KEY_READ) ;
// We need to keep a handle open. See m_hKey below, so we'll let the destructor close this.
// Reg.Close() ;
if(dwRetCode == ERROR_SUCCESS)
{
// We need to keep a handle open. See m_hKey below, so we'll let the destructor close this.
// Reg.Close() ;
}
else
{
// Try to locate user's registry hive
//===================================
swprintf(wszTemp, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ProfileList\\%s", pszUserName) ;
dwRetCode = Reg.Open(HKEY_LOCAL_MACHINE, wszTemp, KEY_READ);
if(dwRetCode == ERROR_SUCCESS) {
dwRetCode = Reg.GetCurrentKeyValue(L"ProfileImagePath", sTemp) ;
Reg.Close() ;
if(dwRetCode == ERROR_SUCCESS)
{
sTemp += _T("\\USER.DAT") ;
ExpandEnvironmentStrings(TOBSTRT(sTemp), szTemp, sizeof(szTemp) / sizeof(TCHAR)) ;
// Try it three times, another process may have the file open
bool bTryTryAgain = false;
int nTries = 0;
do
{
// need to serialize access, using "write" because RegLoadKey wants exclusive access
// even though it is a read operation
m_criticalSection.BeginWrite();
try
{
dwRetCode = (DWORD) RegLoadKey(HKEY_USERS, TOBSTRT(pszUserName), szTemp) ;
}
catch ( ... )
{
m_criticalSection.EndWrite();
throw;
}
m_criticalSection.EndWrite();
if ((dwRetCode == ERROR_SHARING_VIOLATION)
&& (++nTries < 11))
{
LogErrorMessage(L"Sharing violation on USER.DAT (Load)");
Sleep(15 * nTries);
bTryTryAgain = true;
}
else
bTryTryAgain = false;
} while (bTryTryAgain);
}
}
}
if (dwRetCode == ERROR_SUCCESS)
{
LONG lRetVal;
CHString sKey(pszUserName);
sKey += L"\\Software";
lRetVal = RegOpenKeyEx(HKEY_USERS, TOBSTRT(sKey), 0, KEY_QUERY_VALUE, &m_hKey);
ASSERT_BREAK(lRetVal == ERROR_SUCCESS);
}
return dwRetCode ;
}
#endif
/*****************************************************************************
*
* FUNCTION : CUserHive::LoadProfile
*
* DESCRIPTION : Locates user's hive & loads into registry if not already
* present
*
* INPUTS : none
*
* OUTPUTS : none.
*
* RETURNS : nothing
*
* COMMENTS : Hive will remain in registry unless unloaded
* NT Only.
*
*****************************************************************************/
DWORD CUserHive::LoadProfile( LPCWSTR pszSID, CHString& strUserName )
{
// NOTE: The destructor does not unload the key. Nor does doing a load unload
// a previously loaded key;
ASSERT_BREAK(m_hKey == NULL);
DWORD dwRetCode = ERROR_SUCCESS;
WCHAR szTemp[_MAX_PATH] ;
CHString sTemp ;
CRegistry Reg ;
strUserName = L"";
// Set the necessary privs
//========================
#ifdef NTONLY
dwRetCode = AcquirePrivilege() ;
#endif
if(dwRetCode != ERROR_SUCCESS)
{
return dwRetCode ;
}
// See if the key already exists
//==============================
dwRetCode = Reg.Open(HKEY_USERS, pszSID, KEY_READ) ;
// We need to keep a handle open. See m_hKey below, so we'll let the destructor close this.
// Reg.Close() ;
// If we got the profile, make sure we can get account information regarding
// the SID.
if(dwRetCode == ERROR_SUCCESS)
{
CRegistry Reg2 ;
StringCbPrintf(szTemp, _MAX_PATH, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s", pszSID) ;
dwRetCode = Reg2.Open(HKEY_LOCAL_MACHINE, szTemp, KEY_READ) ;
if(dwRetCode == ERROR_SUCCESS)
{
// Load the user account information
dwRetCode = UserAccountFromProfile( Reg2, strUserName );
// We need to keep a handle open. See m_hKey below, so we'll let the destructor close this.
Reg2.Close() ;
}
}
else
{
// Try to locate user's registry hive
//===================================
StringCbPrintf(szTemp,_MAX_PATH, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s", pszSID) ;
dwRetCode = Reg.Open(HKEY_LOCAL_MACHINE, szTemp, KEY_READ) ;
if(dwRetCode == ERROR_SUCCESS)
{
UserAccountFromProfile( Reg, strUserName );
dwRetCode = Reg.GetCurrentKeyValue(L"ProfileImagePath", sTemp) ;
Reg.Close() ;
if(dwRetCode == ERROR_SUCCESS)
{
// NT 4 doesn't include the file name in the registry
//===================================================
if(OSInfo.dwMajorVersion >= 4)
{
sTemp += _T("\\NTUSER.DAT") ;
}
TCHAR szTemp[MAX_PATH];
ExpandEnvironmentStrings(TOBSTRT(sTemp), szTemp, sizeof(szTemp) / sizeof(TCHAR)) ;
// Try it three times, another process may have the file open
bool bTryTryAgain = false;
int nTries = 0;
do
{
// need to serialize access, using "write" because RegLoadKey wants exclusive access
// even though it is a read operation
m_criticalSection.BeginWrite();
try
{
dwRetCode = (DWORD) RegLoadKey(HKEY_USERS, TOBSTRT(pszSID), szTemp);
}
catch ( ... )
{
m_criticalSection.EndWrite();
throw;
}
m_criticalSection.EndWrite();
if ((dwRetCode == ERROR_SHARING_VIOLATION)
&& (++nTries < 11))
{
LogErrorMessage(L"Sharing violation on NTUSER.DAT (LoadProfile)");
Sleep(20 * nTries);
bTryTryAgain = true;
}
else
{
bTryTryAgain = false;
}
} while (bTryTryAgain);
}
}
}
if(dwRetCode == ERROR_SUCCESS)
{
LONG lRetVal;
CHString sKey(pszSID);
sKey += _T("\\Software");
lRetVal = RegOpenKeyEx(HKEY_USERS, TOBSTRT(sKey), 0, KEY_QUERY_VALUE, &m_hKey);
ASSERT_BREAK(lRetVal == ERROR_SUCCESS);
}
// Restore original privilege level & end self-impersonation
//==========================================================
#ifdef NTONLY
RestorePrivilege() ;
#endif
return dwRetCode ;
}
/*****************************************************************************
*
* FUNCTION : CUserHive::UserAccountFromProfile
*
* DESCRIPTION : Pulls the PSID out of the registry object, and creates
* a DOMAIN\UserName value.
*
* INPUTS : none
*
* OUTPUTS : none.
*
* RETURNS : nothing
*
* COMMENTS : Registry Object must be preloaded to the correct profile
* key.
*
*****************************************************************************/
DWORD CUserHive::UserAccountFromProfile( CRegistry& reg, CHString& strUserName )
{
DWORD dwReturn = ERROR_SUCCESS,
dwSidSize = 0;
if ( ( dwReturn = reg.GetCurrentBinaryKeyValue( L"Sid", NULL, &dwSidSize ) ) == ERROR_SUCCESS )
{
PSID psid = new byte [ dwSidSize ];
if ( NULL != psid )
{
try
{
if ( ( dwReturn = reg.GetCurrentBinaryKeyValue( L"Sid", (LPBYTE) psid, &dwSidSize ) ) == ERROR_SUCCESS )
{
CSid sid( psid );
// The sid account type must be valid and the lookup must have been
// successful.
if ( sid.IsOK() && sid.IsAccountTypeValid() )
{
sid.GetDomainAccountName( strUserName );
}
else
{
dwReturn = ERROR_NO_SUCH_USER;
}
}
}
catch ( ... )
{
delete [] psid;
throw ;
}
delete [] psid;
}
else
{
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
}
return dwReturn;
}
/*****************************************************************************
*
* FUNCTION : CUserHive::Unload
*
* DESCRIPTION : Unloads key from HKEY_USERS if present
*
* INPUTS : none
*
* OUTPUTS : none
*
* RETURNS : Windows error code
*
* COMMENTS :
*
*****************************************************************************/
DWORD CUserHive::Unload(LPCWSTR pszKeyName)
{
DWORD dwRetCode = ERROR_SUCCESS;
if (m_hKey != NULL)
{
RegCloseKey(m_hKey);
m_hKey = NULL;
}
#ifdef NTONLY
dwRetCode = AcquirePrivilege();
#endif
if(dwRetCode == ERROR_SUCCESS)
{
m_criticalSection.BeginWrite();
try
{
dwRetCode = RegUnLoadKey(HKEY_USERS, TOBSTRT(pszKeyName)) ;
}
catch ( ... )
{
m_criticalSection.EndWrite();
throw;
}
m_criticalSection.EndWrite();
#ifdef NTONLY
RestorePrivilege() ;
#endif
}
return dwRetCode ;
}