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.
 
 
 
 
 
 

891 lines
27 KiB

//+----------------------------------------------------------------------------
//
// 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