|
|
//+----------------------------------------------------------------------------
//
// Job Scheduler Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: getuser.cxx
//
// Contents: Get the identity of the logged in user.
//
// History: 19-Jun-96 EricB created
//
// Notes: This is for NT only since Win95 doesn't have security.
//
//-----------------------------------------------------------------------------
//
// Some NT header definitions conflict with some of the standard windows
// definitions. Thus, the project precompiled header can't be used.
//
extern "C" { #include <nt.h> // NT definitions
#include <ntrtl.h> // NT runtime library definitions
#include <nturtl.h>
#include <ntlsa.h> // BUGBUG 254102
}
#include <windows.h>
#define SECURITY_WIN32 // needed by security.h
#include <security.h> // GetUserNameEx
#include <winbase.h> // SecureZeroMemory
#include <StrSafe.h>
#include <lmcons.h> // BUGBUG 254102
#include <defines.hxx> // BUGBUG 254102
#include <..\..\..\smdebug\smdebug.h>
#include <debug.hxx>
#include "globals.hxx"
#include <Wtsapi32.h>
const int SCH_BIGBUF_LEN = 256;
//
// Registry key/value for default shell.
//
#define SHELL_REGKEY L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
#define SHELL_REGVAL L"Shell"
#define DEFAULT_SHELL L"explorer.exe"
// This function is actually declared in proto.hxx. But including proto.hxx
// brings in alot of things we don't need. Just define it here to the includes
// simple.
//
HANDLE ImpersonateUser(HANDLE hUserToken, HANDLE hImpersonationToken);
// so's this one - here's to hoping that type-safe linkage works well...
HANDLE ImpersonateLoggedInUser(void);
BOOL StopImpersonating(HANDLE ThreadHandle, BOOL fCloseHandle);
// toggle to allow conditional compilation of fix for RAID 720688
// "old" method found user token for shell process (usually explorer.exe)
// "new" method uses Terminal Server functions
#define FIND_USER_WITH_TS
//+----------------------------------------------------------------------------
//
// Function: LogonSessionDataCleanup
//
// Synopsis: Close all open handles and free memory.
//
// Notes: **** Important ****
//
// No need to enter gcsLogonSessionInfoCritSection prior to
// calling this function since it is entered here.
//
//-----------------------------------------------------------------------------
void LogonSessionDataCleanup(void) { EnterCriticalSection(gUserLogonInfo.CritSection);
if (gUserLogonInfo.ImpersonationThread != NULL) { CloseHandle(gUserLogonInfo.ImpersonationThread); gUserLogonInfo.ImpersonationThread = NULL; }
if (gUserLogonInfo.DomainUserName != NULL) { delete gUserLogonInfo.DomainUserName; gUserLogonInfo.DomainUserName= NULL; }
if (gUserLogonInfo.ShellToken) { CloseHandle(gUserLogonInfo.ShellToken); gUserLogonInfo.ShellToken = NULL; }
SecureZeroMemory(gUserLogonInfo.Sid, sizeof(gUserLogonInfo.Sid));
LeaveCriticalSection(gUserLogonInfo.CritSection); }
//+----------------------------------------------------------------------------
//
// Function: ImpersonateUser
//
// Synopsis: Impersonate the user associated with the token.
//
// Arguments: [hUserToken] - Handle to the token to be impersonated.
// [ThreadHandle] - Handle to the thread that is to impersonate
// hUserToken. If this is NULL, the function opens a handle
// to the current thread.
//
// Returns: Handle to the thread that is impersonating hUserToken.
//
// Notes: BUGBUG : This code was taken from RAS. It is quite different
// than that in winlogon
// (windows\gina\winlogon\secutil.c).
//
//-----------------------------------------------------------------------------
HANDLE ImpersonateUser(HANDLE hUserToken, HANDLE ThreadHandle) { NTSTATUS Status; SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ImpersonationToken; BOOL ThreadHandleOpened = FALSE;
if (ThreadHandle == NULL) { //
// Get a handle to the current thread.
// Once we have this handle, we can set the user's impersonation
// token into the thread and remove it later even though we ARE
// the user for the removal operation. This is because the handle
// contains the access rights - the access is not re-evaluated
// at token removal time.
//
Status = NtDuplicateObject( NtCurrentProcess(), // Source process
NtCurrentThread(), // Source handle
NtCurrentProcess(), // Target process
&ThreadHandle, // Target handle
THREAD_SET_THREAD_TOKEN,// Access
0L, // Attributes
DUPLICATE_SAME_ATTRIBUTES);
if (!NT_SUCCESS(Status)) { ERR_OUT("ImpersonateUser: NtDuplicateObject", Status); return(NULL); }
ThreadHandleOpened = TRUE; }
//
// If the usertoken is NULL, there's nothing to do
//
if (hUserToken != NULL) { //
// hUserToken is a primary token - create an impersonation token
// version of it so we can set it on our thread
//
InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, NULL, // UserProcessData->NewThreadTokenSD);
NULL);
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; SecurityQualityOfService.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken(hUserToken, TOKEN_IMPERSONATE | TOKEN_READ, &ObjectAttributes, FALSE, TokenImpersonation, &ImpersonationToken);
if (!NT_SUCCESS(Status)) { ERR_OUT("ImpersonateUser: NtDuplicateToken", Status);
if (ThreadHandleOpened) { NtClose(ThreadHandle); }
return(NULL); }
//
// Set the impersonation token on this thread so we 'are' the user
//
Status = NtSetInformationThread(ThreadHandle, ThreadImpersonationToken, (PVOID)&ImpersonationToken, sizeof(ImpersonationToken));
//
// We're finished with our handle to the impersonation token
//
NtClose(ImpersonationToken);
//
// Check we set the token on our thread ok
//
if (!NT_SUCCESS(Status)) { ERR_OUT("ImpersonateUser: NTSetInformationThread", Status);
if (ThreadHandleOpened) { NtClose(ThreadHandle); }
return(NULL); }
}
return(ThreadHandle); }
//+----------------------------------------------------------------------------
//
// Function: GetLoggedOnUser
//
// Synopsis: Called when a user logs in.
//
// Returns: None. Sets the global gUserLogonInfo.
//
// Notes: **** Important ****
//
// Caller must have entered the gcsLogonSessionInfoCritSection
// critical section for the duration of this call and continue
// to remain in this critical section for the lifetime use of
// the returned string.
//
// DO NOT attempt to dealloc the returned string! It is a
// pointer to global memory.
//
//-----------------------------------------------------------------------------
void GetLoggedOnUser(void) { LPWSTR pwszLoggedOnUser; DWORD cchName = 0; DWORD dwErr = ERROR_SUCCESS;
if (gUserLogonInfo.DomainUserName != NULL) { //
// Already done.
//
return; }
//
// Impersonate the logged in user.
//
if (ImpersonateLoggedInUser()) { //
// Get the size of the user name string.
//
if (!GetUserNameEx(NameSamCompatible, NULL, &cchName)) { dwErr = GetLastError(); if (dwErr != ERROR_MORE_DATA || cchName == 0) { StopImpersonating(gUserLogonInfo.ImpersonationThread, TRUE); ERR_OUT("GetLoggedOnUser: GetUserName", dwErr); return; } } cchName++; // contrary to docs, cchName excludes the null
//
// Allocate the user name string buffer and get the user name.
//
pwszLoggedOnUser = new WCHAR[cchName * 2];
if (pwszLoggedOnUser != NULL) { if (!GetUserNameEx(NameSamCompatible, pwszLoggedOnUser, &cchName)) { dwErr = GetLastError(); ERR_OUT("GetLoggedOnUser: GetUserName", dwErr); delete pwszLoggedOnUser; } else { schDebugOut((DEB_ITRACE, "GetLoggedOnUser: got '%S'\n", pwszLoggedOnUser)); cchName++; // contrary to docs, cchName excludes the null
//
// This name is in the format "domain\\user".
// Make a copy of the domain name right after it, so
// we end up with a single buffer in the format
// "domain\\user\0domain". Set up pointers into this
// buffer for all 3 parts of the name:
// domain
// user
// domain\user
//
gUserLogonInfo.DomainUserName = pwszLoggedOnUser;
WCHAR *pSlash = wcschr(pwszLoggedOnUser, L'\\'); schAssert(pSlash != NULL); gUserLogonInfo.UserName = pSlash + 1;
DWORD cchDomain = (DWORD) (pSlash - pwszLoggedOnUser); gUserLogonInfo.DomainName = pwszLoggedOnUser + cchName;
wcsncpy(gUserLogonInfo.DomainName, pwszLoggedOnUser, cchDomain); gUserLogonInfo.DomainName[cchDomain] = L'\0';
schDebugOut((DEB_ITRACE, "GetLoggedOnUser: domain '%S', user '%S'\n", gUserLogonInfo.DomainName, gUserLogonInfo.UserName)); } } else { ERR_OUT("GetLoggedOnUser", ERROR_OUTOFMEMORY); }
//
// BUGBUG 254102 - Cache the logged-on user's SID, since
// LookupAccountName doesn't do it when offline.
// Remove this code when bug 254102 is fixed.
//
#define USER_TOKEN_STACK_BUFFER_SIZE \
(sizeof(TOKEN_USER) + sizeof(SID_AND_ATTRIBUTES) + MAX_SID_SIZE)
BYTE rgbTokenInformation[USER_TOKEN_STACK_BUFFER_SIZE]; TOKEN_USER * pTokenUser = (TOKEN_USER *)rgbTokenInformation; DWORD cbReturnLength;
if (!GetTokenInformation(gUserLogonInfo.ShellToken, TokenUser, pTokenUser, USER_TOKEN_STACK_BUFFER_SIZE, &cbReturnLength)) { schAssert(GetLastError() != ERROR_INSUFFICIENT_BUFFER); CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError())); SecureZeroMemory(gUserLogonInfo.Sid, sizeof(gUserLogonInfo.Sid)); } else if (!CopySid(sizeof(gUserLogonInfo.Sid), gUserLogonInfo.Sid, pTokenUser->User.Sid)) { schAssert(!"CopySid failed"); CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError())); SecureZeroMemory(gUserLogonInfo.Sid, sizeof(gUserLogonInfo.Sid)); }
StopImpersonating(gUserLogonInfo.ImpersonationThread, FALSE); } }
//+----------------------------------------------------------------------------
//
// Function: StopImpersonating
//
// Synopsis: Stop impersonating.
//
// Notes: This code was taken from winlogon. Specifically:
// windows\gina\winlogon\secutil.c.
//
//-----------------------------------------------------------------------------
BOOL StopImpersonating(HANDLE ThreadHandle, BOOL fCloseHandle) { NTSTATUS Status, IgnoreStatus; HANDLE ImpersonationToken;
//
// Remove the user's token from our thread so we are 'ourself' again
//
ImpersonationToken = NULL;
Status = NtSetInformationThread(ThreadHandle, ThreadImpersonationToken, (PVOID)&ImpersonationToken, sizeof(ImpersonationToken));
//
// We're finished with the thread handle
//
if (fCloseHandle) { IgnoreStatus = NtClose(ThreadHandle); schAssert(NT_SUCCESS(IgnoreStatus)); }
if (!NT_SUCCESS(Status)) { schDebugOut((DEB_ERROR, "Failed to remove user impersonation token from SA service, " \ "status = 0x%lx", Status)); }
return(NT_SUCCESS(Status)); }
#ifdef FIND_USER_WITH_TS
//+----------------------------------------------------------------------------
//
// Function: ImpersonateLoggedInUser
//
// Synopsis: Impersonate the shell user.
//
// Returns: Handle to thread that's impersonating user
//
// Notes: **** Important ****
//
// Caller must have entered the gcsLogonSessionInfoCritSection
// critical section for the duration of this call.
//
// GLOBALS: sets gUserLogonInfo.ShellToken and gUserLogonInfo.ImpersonationThread
//
//-----------------------------------------------------------------------------
HANDLE ImpersonateLoggedInUser(void) { if (gUserLogonInfo.ShellToken) { CloseHandle(gUserLogonInfo.ShellToken); gUserLogonInfo.ShellToken = NULL; } DWORD sessionID; sessionID = WTSGetActiveConsoleSessionId();
if (sessionID == 0xFFFFFFFF) return NULL;
if (!WTSQueryUserToken(sessionID, &gUserLogonInfo.ShellToken)) return NULL;
if (gUserLogonInfo.ImpersonationThread) CloseHandle(gUserLogonInfo.ImpersonationThread);
return (gUserLogonInfo.ImpersonationThread = ImpersonateUser( gUserLogonInfo.ShellToken, gUserLogonInfo.ImpersonationThread)); }
#else // #ifdef FIND_USER_WITH_TS
HANDLE GetShellProcessHandle(void); PSYSTEM_PROCESS_INFORMATION GetSystemProcessInfo(void); PSYSTEM_PROCESS_INFORMATION FindProcessByName(PSYSTEM_PROCESS_INFORMATION, LPWSTR); VOID FreeSystemProcessInfo(PSYSTEM_PROCESS_INFORMATION pProcessInfo);
//+----------------------------------------------------------------------------
//
// Function: GetShellProcessHandle
//
// Synopsis: Initialize & return the shell handle of the current logged
// on user, gUserLogonInfo.ShellHandle.
//
// Returns: ERROR_SUCCESS or an error code.
//
// Notes: **** Important ****
//
// Caller must have entered gUserLogonInfo.CriticalSection
// for the duration of this call and continue to remain in
// in it for the lifetime use of the returned handle.
//
// DO NOT close the returned handle. It is a global handle.
//
//-----------------------------------------------------------------------------
HANDLE GetShellProcessHandle(void) { PSYSTEM_PROCESS_INFORMATION pSystemInfo, pProcessInfo; WCHAR wszShellName[MAX_PATH + 1]; WCHAR * pwszShellName = wszShellName; WCHAR * pwsz; HKEY hReg = NULL; HANDLE hProcess = NULL; DWORD dwErr = ERROR_SUCCESS; DWORD dwType; DWORD dwSize;
//
// Get the shell process name. We will look for this
// to find out who the currently logged-on user is.
//
if (gUserLogonInfo.ShellHandle != NULL) { //
// Check if the handle is valid.
//
if (WaitForSingleObject(gUserLogonInfo.ShellHandle, 0) == WAIT_TIMEOUT) { //
// Still valid.
//
return(gUserLogonInfo.ShellHandle); }
//
// Re-acquire handle.
//
CloseHandle(gUserLogonInfo.ShellHandle); gUserLogonInfo.ShellHandle = NULL; }
StringCchCopy(pwszShellName, MAX_PATH + 1, DEFAULT_SHELL);
if ((dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SHELL_REGKEY, 0, KEY_READ, &hReg)) == ERROR_SUCCESS) { dwSize = sizeof(wszShellName);
dwErr = RegQueryValueEx(hReg, SHELL_REGVAL, NULL, &dwType, (PBYTE)pwszShellName, &dwSize);
RegCloseKey(hReg); }
if (dwErr != ERROR_SUCCESS) { ERR_OUT("GetShellProcessHandle: RegQueryValueEx", dwErr); return(NULL); }
//
// Remove parameters from command line.
//
pwsz = pwszShellName; while (*pwsz != L' ' && *pwsz != L'\0') { pwsz++; } *pwsz = L'\0';
//
// Get the process list.
//
pSystemInfo = GetSystemProcessInfo();
if (pSystemInfo == NULL) { return(NULL); }
//
// See if wszShell is running.
//
pProcessInfo = FindProcessByName(pSystemInfo, pwszShellName);
if (pProcessInfo != NULL) { //
// Open the process.
//
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, HandleToUlong(pProcessInfo->UniqueProcessId));
#if DBG == 1
if (hProcess == NULL) { ERR_OUT("GetShellProcessHandle: OpenProcess", GetLastError()); } #endif
}
//
// Free resources.
//
FreeSystemProcessInfo(pSystemInfo);
//
// Return process handle.
//
return(gUserLogonInfo.ShellHandle = hProcess); }
//+----------------------------------------------------------------------------
//
// Function: GetShellProcessToken
//
// Synopsis:
//
// Returns: ERROR_SUCCESS or an error code.
//
// Notes: **** Important ****
//
// Caller must have entered the gcsLogonSessionInfoCritSection
// critical section for the duration of this call and continue
// to remain in this critical section for the lifetime use of
// the returned handle.
//
// DO NOT close the returned handle. It is a global handle.
//
//-----------------------------------------------------------------------------
HANDLE GetShellProcessToken(void) { HANDLE hProcess = GetShellProcessHandle();
if (hProcess == NULL) { return(NULL); }
HANDLE hToken = gUserLogonInfo.ShellToken;
if (gUserLogonInfo.ShellToken == NULL) { if (OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &hToken)) { return(gUserLogonInfo.ShellToken = hToken); } else { ERR_OUT("GetShellProcessToken: OpenProcessToken", GetLastError()); return(NULL); } }
return gUserLogonInfo.ShellToken; }
//+----------------------------------------------------------------------------
//
// Function: GetSystemProcessInfo
//
// Synopsis: Return a block containing information about all processes
// currently running in the system.
//
// Returns: A pointer to the system process information or NULL if it could
// not be allocated or retrieved.
//
//-----------------------------------------------------------------------------
PSYSTEM_PROCESS_INFORMATION GetSystemProcessInfo(void) { #define SYSTEM_PROCESS_BUFFER_INCREMENT 4096
NTSTATUS Status = 0; PUCHAR pBuffer; DWORD cbBufferSize;
//
// Get the process list.
//
cbBufferSize = SYSTEM_PROCESS_BUFFER_INCREMENT;
pBuffer = (PUCHAR)LocalAlloc(LMEM_FIXED, cbBufferSize);
if (pBuffer == NULL) { ERR_OUT("GetSystemProcessInfo: LocalAlloc", GetLastError()); return(NULL); }
for (;;) { Status = NtQuerySystemInformation(SystemProcessInformation, pBuffer, cbBufferSize, NULL);
if (Status == STATUS_SUCCESS) { break; } else if (Status == STATUS_INFO_LENGTH_MISMATCH) { cbBufferSize += SYSTEM_PROCESS_BUFFER_INCREMENT; PUCHAR pTempBuffer = (PUCHAR)LocalReAlloc(pBuffer, cbBufferSize, LMEM_MOVEABLE); if (pTempBuffer == NULL) { LocalFree(pBuffer); // original handle is still valid; use it to free the memory
ERR_OUT("GetSystemProcessInfo: LocalReAlloc", GetLastError()); return(NULL); } pBuffer = pTempBuffer; // LocalReAlloc succeeded, so use the new handle now
} else { break; } }
if (Status != STATUS_SUCCESS && pBuffer != NULL) { LocalFree(pBuffer); pBuffer = NULL; }
return (PSYSTEM_PROCESS_INFORMATION)pBuffer; }
//+----------------------------------------------------------------------------
//
// Function: FindProcessByName
//
// Synopsis: Given a pointer returned by GetSystemProcessInfo(), find
// a process by name.
// Hydra modification: Only processes on the physical console
// session are included.
//
// Arguments: [pProcessInfo] - a pointer returned by GetSystemProcessInfo().
// [lpExeName] - a pointer to a Unicode string containing the
// process to be found.
//
// Returns: A pointer to the process information for the supplied
// process or NULL if it could not be found.
//
//-----------------------------------------------------------------------------
PSYSTEM_PROCESS_INFORMATION FindProcessByName(PSYSTEM_PROCESS_INFORMATION pProcessInfo, LPWSTR lpExeName) { PUCHAR pLargeBuffer = (PUCHAR)pProcessInfo; ULONG ulTotalOffset = 0;
//
// Look in the process list for lpExeName.
//
for (;;) { if (pProcessInfo->ImageName.Buffer != NULL) { schDebugOut((DEB_USER3, "FindProcessByName: process: %S (%d)\n", pProcessInfo->ImageName.Buffer, pProcessInfo->UniqueProcessId)); if (!_wcsicmp(pProcessInfo->ImageName.Buffer, lpExeName)) { //
// Pick this process only if it's
// running on the physical console session
//
DWORD dwSessionId; if (! ProcessIdToSessionId( HandleToUlong(pProcessInfo->UniqueProcessId), &dwSessionId)) { schDebugOut((DEB_ERROR, "ProcessIdToSessionId FAILED, %lu\n", GetLastError)); } else if (dwSessionId == 0) { return pProcessInfo; } } } //
// Increment offset to next process information block.
//
if (!pProcessInfo->NextEntryOffset) { break; } ulTotalOffset += pProcessInfo->NextEntryOffset; pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pLargeBuffer[ulTotalOffset]; }
schDebugOut((DEB_ITRACE, "FindProcessByName: process %ws not found\n", lpExeName)); return NULL; }
//+----------------------------------------------------------------------------
//
// Function: FreeSystemProcessInfo
//
// Synopsis: Free a buffer returned by GetSystemProcessInfo().
//
// Arguments: [pProcessInfo] - a pointer returned by GetSystemProcessInfo().
//
//-----------------------------------------------------------------------------
VOID FreeSystemProcessInfo(PSYSTEM_PROCESS_INFORMATION pProcessInfo) { LocalFree(pProcessInfo); }
//+----------------------------------------------------------------------------
//
// Function: ImpersonateLoggedInUser
//
// Synopsis: Impersonate the shell user.
//
// Returns:
//
// Notes: **** Important ****
//
// Caller must have entered the gcsLogonSessionInfoCritSection
// critical section for the duration of this call.
//
//-----------------------------------------------------------------------------
HANDLE ImpersonateLoggedInUser(void) { BOOL fDuplicateToken;
//
// Open the impersonation token for the
// process we want to impersonate.
//
if (gUserLogonInfo.ImpersonationThread == NULL) { if (gUserLogonInfo.ShellHandle == NULL) { if (GetShellProcessHandle() == NULL) { return(NULL); } }
if (gUserLogonInfo.ShellToken == NULL) { if (GetShellProcessToken() == NULL) { return(NULL); } } }
return(gUserLogonInfo.ImpersonationThread = ImpersonateUser( gUserLogonInfo.ShellToken, gUserLogonInfo.ImpersonationThread)); }
#endif // FIND_USER_WITH_TS
|