windows-nt/Source/XPSP1/NT/termsrv/wtsapi/wtsapi32.c
2020-09-26 16:20:57 +08:00

551 lines
14 KiB
C

/*******************************************************************************
* wtsapi32.c
*
* Published Terminal Server APIs
*
* Copyright 1998, Citrix Systems Inc.
* Copyright (C) 1997-1999 Microsoft Corp.
/******************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#include <windows.h>
#include <winbase.h>
#include <winerror.h>
#if(WINVER >= 0x0500)
#include <ntstatus.h>
#include <winsta.h>
#else
#include <citrix\cxstatus.h>
#include <citrix\winsta.h>
#endif
#include <utildll.h>
#include <stdio.h>
#include <stdarg.h>
#include <wtsapi32.h>
// Private User function that returns user token for session 0 only
// Used in the case when TS is not running
extern
HANDLE
GetCurrentUserTokenW (
WCHAR Winsta[],
DWORD DesiredAccess
);
/*=============================================================================
== External procedures defined
=============================================================================*/
BOOL WINAPI WTSShutdownSystem( HANDLE, DWORD );
BOOL WINAPI WTSWaitSystemEvent( HANDLE, DWORD, DWORD * );
VOID WINAPI WTSFreeMemory( PVOID pMemory );
BOOL WINAPI WTSQueryUserToken(ULONG SessionId, PHANDLE phToken);
/*=============================================================================
== Internal procedures defined
=============================================================================*/
BOOL WINAPI DllEntryPoint( HINSTANCE, DWORD, LPVOID );
BOOL IsTerminalServiceRunning(VOID);
BOOL IsProcessPrivileged(CONST PCWSTR szPrivilege);
/*=============================================================================
== Local function prototypes
=============================================================================*/
BOOLEAN CheckShutdownPrivilege();
/****************************************************************************
*
* WTSShutdowSystem
*
* Shutdown and/or reboot system
*
* ENTRY:
* hServer (input)
* Terminal Server handle (or WTS_CURRENT_SERVER)
* ShutdownFlags (input)
* Flags which specify shutdown options.
*
* EXIT:
*
* TRUE -- The operation succeeded.
*
* FALSE -- The operation failed. Extended error status is available
* using GetLastError.
*
****************************************************************************/
BOOL
WINAPI
WTSShutdownSystem(
IN HANDLE hServer,
IN DWORD ShutdownFlags
)
{
ULONG uiOptions = 0;
// Make sure the user has the proper privilege to shutdown the system when
// hServer is a local server handle. For remote server, the user privilege
// is checked when WTSOpenServer is called.
if (hServer == SERVERNAME_CURRENT && !CheckShutdownPrivilege()) {
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
return(FALSE);
}
// Construct the shutdown flag
if (ShutdownFlags == WTS_WSD_LOGOFF) {
// log off all users and deletes sessions
uiOptions = WSD_LOGOFF;
} else if (ShutdownFlags == WTS_WSD_SHUTDOWN) {
uiOptions = WSD_LOGOFF | WSD_SHUTDOWN;
} else if (ShutdownFlags == WTS_WSD_REBOOT) {
uiOptions = WSD_LOGOFF | WSD_SHUTDOWN | WSD_REBOOT;
} else if (ShutdownFlags == WTS_WSD_POWEROFF) {
uiOptions = WSD_LOGOFF | WSD_SHUTDOWN | WSD_POWEROFF;
} else if (ShutdownFlags == WTS_WSD_FASTREBOOT) {
uiOptions = WSD_FASTREBOOT | WSD_REBOOT;
} else {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return( WinStationShutdownSystem( hServer, uiOptions ));
}
/****************************************************************************
*
* WTSWaitSystemEvent
*
* Waits for an event (WinStation create, delete, connect, etc) before
* returning to the caller.
*
* ENTRY:
* hServer (input)
* Terminal Server handle (or WTS_CURRENT_SERVER)
* EventFlags (input)
* Bit mask that specifies which event(s) to wait for (WTS_EVENT_?)
* pEventFlags (output)
* Bit mask of event(s) that occurred.
*
* EXIT:
*
* TRUE -- The operation succeeded.
*
* FALSE -- The operation failed. Extended error status is available
* using GetLastError.
*
****************************************************************************/
BOOL
WINAPI
WTSWaitSystemEvent(
IN HANDLE hServer,
IN DWORD EventMask,
OUT DWORD * pEventFlags
)
{
BOOL fSuccess;
ULONG WSEventMask;
ULONG WSEventFlags = 0;
if (IsBadWritePtr(pEventFlags, sizeof(DWORD))) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/*
* Map event mask
*/
WSEventMask = 0;
if ( EventMask & WTS_EVENT_CREATE )
WSEventMask |= WEVENT_CREATE;
if ( EventMask & WTS_EVENT_DELETE )
WSEventMask |= WEVENT_DELETE;
if ( EventMask & WTS_EVENT_RENAME )
WSEventMask |= WEVENT_RENAME;
if ( EventMask & WTS_EVENT_CONNECT )
WSEventMask |= WEVENT_CONNECT;
if ( EventMask & WTS_EVENT_DISCONNECT )
WSEventMask |= WEVENT_DISCONNECT;
if ( EventMask & WTS_EVENT_LOGON )
WSEventMask |= WEVENT_LOGON;
if ( EventMask & WTS_EVENT_LOGOFF )
WSEventMask |= WEVENT_LOGOFF;
if ( EventMask & WTS_EVENT_STATECHANGE )
WSEventMask |= WEVENT_STATECHANGE;
if ( EventMask & WTS_EVENT_LICENSE )
WSEventMask |= WEVENT_LICENSE;
if ( EventMask & WTS_EVENT_FLUSH )
WSEventMask |= WEVENT_FLUSH;
/*
* Wait for system event
*/
fSuccess = WinStationWaitSystemEvent( hServer, WSEventMask, &WSEventFlags );
/*
* Map event mask
*/
*pEventFlags = 0;
if ( WSEventFlags & WEVENT_CREATE )
*pEventFlags |= WTS_EVENT_CREATE;
if ( WSEventFlags & WEVENT_DELETE )
*pEventFlags |= WTS_EVENT_DELETE;
if ( WSEventFlags & WEVENT_RENAME )
*pEventFlags |= WTS_EVENT_RENAME;
if ( WSEventFlags & WEVENT_CONNECT )
*pEventFlags |= WTS_EVENT_CONNECT;
if ( WSEventFlags & WEVENT_DISCONNECT )
*pEventFlags |= WTS_EVENT_DISCONNECT;
if ( WSEventFlags & WEVENT_LOGON )
*pEventFlags |= WTS_EVENT_LOGON;
if ( WSEventFlags & WEVENT_LOGOFF )
*pEventFlags |= WTS_EVENT_LOGOFF;
if ( WSEventFlags & WEVENT_STATECHANGE )
*pEventFlags |= WTS_EVENT_STATECHANGE;
if ( WSEventFlags & WEVENT_LICENSE )
*pEventFlags |= WTS_EVENT_LICENSE;
return( fSuccess );
}
/****************************************************************************
*
* WTSFreeMemory
*
* Free memory allocated by Terminal Server APIs
*
* ENTRY:
* pMemory (input)
* Pointer to memory to free
*
* EXIT:
* nothing
*
****************************************************************************/
VOID
WINAPI
WTSFreeMemory( PVOID pMemory )
{
LocalFree( pMemory );
}
/****************************************************************************
*
* DllEntryPoint
*
* Function is called when the DLL is loaded and unloaded.
*
* ENTRY:
* hinstDLL (input)
* Handle of DLL module
* fdwReason (input)
* Why function was called
* lpvReserved (input)
* Reserved; must be NULL
*
* EXIT:
* TRUE - Success
* FALSE - Error occurred
*
****************************************************************************/
BOOL WINAPI
DllEntryPoint( HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved )
{
switch ( fdwReason ) {
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return( TRUE );
}
/*****************************************************************************
*
* CheckShutdownPrivilege
*
* Check whether the current process has shutdown permission.
*
* ENTRY:
*
* EXIT:
*
*
****************************************************************************/
BOOLEAN
CheckShutdownPrivilege()
{
NTSTATUS Status;
BOOLEAN WasEnabled;
//
// Try the thread token first
//
Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
TRUE,
TRUE,
&WasEnabled);
if (Status == STATUS_NO_TOKEN) {
//
// No thread token, use the process token
//
Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
TRUE,
FALSE,
&WasEnabled);
}
if (!NT_SUCCESS(Status)) {
return(FALSE);
}
return(TRUE);
}
/*++
Routine Description:
Allows to read the token of the user interactively logged in the session identified by SessionId.
The caller must be running under local system account and hold SE_TCB_NAME privilege. This API
is intended for highly trusted services. Service Providers using it must be very cautious not to
leak user tokens.
NOTE : The API is RPC based and hence cannot be called with the loader lock held (specifically
from DLL attach/detach code)
Arguments:
SessionId: IN. Identifies the session the user is logged in.
phToken: OUT. Points to the user token handle, if the function succeeded.
Return Values:
TRUE in case of success. phToken points to the user token.
FALSE in case of failure. Use GetLastError() to get extended error code.
The token returned is a duplicate of a primary token.
--*/
BOOL
WINAPI
WTSQueryUserToken(ULONG SessionId, PHANDLE phToken)
{
BOOL IsTsUp = FALSE;
BOOL Result, bHasPrivilege;
ULONG ReturnLength;
WINSTATIONUSERTOKEN Info;
NTSTATUS Status;
HANDLE hUserToken = NULL;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
// Do parameter Validation
if (NULL == phToken) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
// We will first check if the process which is calling us, has SE_TCB_NAME privilege
bHasPrivilege = IsProcessPrivileged(SE_TCB_NAME);
if (!bHasPrivilege) {
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
return(FALSE);
}
// If it is session 0, don't call winsta. Use GetCurrentUserToken instead.
if (SessionId == 0)
{
hUserToken = GetCurrentUserTokenW(L"WinSta0",
TOKEN_QUERY |
TOKEN_DUPLICATE |
TOKEN_ASSIGN_PRIMARY
);
if (hUserToken == NULL)
return FALSE;
else
*phToken = hUserToken;
}
else // Non-zero sessions
{
// No one except TS has any idea about non-zero sessions. So, check if the TS is running.
IsTsUp = IsTerminalServiceRunning();
if (IsTsUp)
{ // This is so that CSRSS can dup the handle to our process
Info.ProcessId = LongToHandle(GetCurrentProcessId());
Info.ThreadId = LongToHandle(GetCurrentThreadId());
Result = WinStationQueryInformation(
SERVERNAME_CURRENT,
SessionId,
WinStationUserToken,
&Info,
sizeof(Info),
&ReturnLength
);
if( !Result )
return FALSE;
else
*phToken = Info.UserToken ;
}
else
{ // TS is not running. So, set error for non-zero sessions: WINSTATION_NOT_FOUND.
SetLastError(ERROR_CTX_WINSTATION_NOT_FOUND);
return FALSE;
}
}
return TRUE;
}
// This function determines if the Terminal Service is currently Running
BOOL IsTerminalServiceRunning (VOID)
{
BOOL bReturn = FALSE;
SC_HANDLE hServiceController;
hServiceController = OpenSCManager(NULL, NULL, GENERIC_READ);
if (hServiceController) {
SC_HANDLE hTermServ ;
hTermServ = OpenService(hServiceController, L"TermService", SERVICE_QUERY_STATUS);
if (hTermServ) {
SERVICE_STATUS tTermServStatus;
if (QueryServiceStatus(hTermServ, &tTermServStatus)) {
bReturn = (tTermServStatus.dwCurrentState == SERVICE_RUNNING);
} else {
CloseServiceHandle(hTermServ);
CloseServiceHandle(hServiceController);
return FALSE;
}
CloseServiceHandle(hTermServ);
} else {
CloseServiceHandle(hServiceController);
return FALSE;
}
CloseServiceHandle(hServiceController);
} else {
return FALSE;
}
return bReturn;
}
/*++
Routine Description:
This function checks to see if the specified privilege is enabled
in the primary access token for the current thread.
Arguments:
szPrivilege - The privilege to be checked for
Return Value:
TRUE if the specified privilege is enabled, FALSE otherwise.
--*/
BOOL
IsProcessPrivileged(
CONST PCWSTR szPrivilege
)
{
LUID luidValue; // LUID (locally unique ID) for the privilege
BOOL bResult = FALSE, bHasPrivilege = FALSE;
HANDLE hToken = NULL;
PRIVILEGE_SET privilegeSet;
// Get the LUID for the privilege from the privilege name
bResult = LookupPrivilegeValue(
NULL,
szPrivilege,
&luidValue
);
if (!bResult) {
return FALSE;
}
// Get the token of the present thread
bResult = OpenThreadToken(
GetCurrentThread(),
MAXIMUM_ALLOWED,
FALSE,
&hToken
);
if (!bResult) {
// We want to use the token for the current process
bResult = OpenProcessToken(
GetCurrentProcess(),
MAXIMUM_ALLOWED,
&hToken
);
if (!bResult) {
return FALSE;
}
}
// And check for the privilege
privilegeSet.PrivilegeCount = 1;
privilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
privilegeSet.Privilege[0].Luid = luidValue;
privilegeSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
bResult = PrivilegeCheck(hToken, &privilegeSet, &bHasPrivilege);
CloseHandle(hToken);
return (bResult && bHasPrivilege);
}