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.
1119 lines
35 KiB
1119 lines
35 KiB
/***************************** Module Header ******************************\
|
|
* Module Name: csrstubs.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Routines to call CSR
|
|
*
|
|
* 02-27-95 JimA Created.
|
|
*
|
|
* Note: This file has been partitioned with #if defines so that the LPC
|
|
* marshalling code can be inside 64bit code when running under wow64 (32bit on
|
|
* 64bit NT). In wow64, the system DLLs for 32bit processes are 32bit.
|
|
*
|
|
* The marshalling code can only be depedent on functions in NTDLL.
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "csrmsg.h"
|
|
#include "csrhlpr.h"
|
|
#include "strid.h"
|
|
#include <dbt.h>
|
|
#include <regstr.h>
|
|
#include <winsta.h> // for WinStationGetTermSrvCountersValue
|
|
#include <allproc.h> // for TS_COUNTER
|
|
|
|
#define ALIGN_DOWN(length, type) \
|
|
((ULONG)(length) & ~(sizeof(type) - 1))
|
|
|
|
#define ALIGN_UP(length, type) \
|
|
(ALIGN_DOWN(((ULONG)(length) + sizeof(type) - 1), type))
|
|
|
|
CONST WCHAR gszReliabilityKey[] = L"\\Registry\\Machine\\" REGSTR_PATH_RELIABILITY;
|
|
CONST WCHAR gszReliabilityPolicyKey[] = L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows NT\\Reliability";
|
|
|
|
#if defined(BUILD_CSRWOW64)
|
|
|
|
#undef RIPERR0
|
|
#undef RIPNTERR0
|
|
#undef RIPMSG0
|
|
|
|
#define RIPNTERR0(status, flags, szFmt) {if (NtCurrentTeb()) NtCurrentTeb()->LastErrorValue = RtlNtStatusToDosError(status);}
|
|
#define RIPERR0(idErr, flags, szFmt) {if (NtCurrentTeb()) NtCurrentTeb()->LastErrorValue = (idErr);}
|
|
#define RIPMSG0(flags, szFmt)
|
|
|
|
#endif
|
|
|
|
#define SET_LAST_ERROR_RETURNED() if (a->dwLastError) RIPERR0(a->dwLastError, RIP_VERBOSE, "")
|
|
|
|
#if !defined(BUILD_WOW6432)
|
|
|
|
NTSTATUS
|
|
APIENTRY
|
|
CallUserpExitWindowsEx(
|
|
IN UINT uFlags,
|
|
OUT PBOOL pfSuccess)
|
|
{
|
|
|
|
USER_API_MSG m;
|
|
PEXITWINDOWSEXMSG a = &m.u.ExitWindowsEx;
|
|
|
|
a->uFlags = uFlags;
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpExitWindowsEx
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
|
|
if (NT_SUCCESS( m.ReturnValue ) || m.ReturnValue == STATUS_CANT_WAIT) {
|
|
SET_LAST_ERROR_RETURNED();
|
|
*pfSuccess = a->fSuccess;
|
|
} else {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "");
|
|
*pfSuccess = FALSE;
|
|
}
|
|
|
|
return m.ReturnValue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(BUILD_CSRWOW64)
|
|
|
|
typedef struct _EXITWINDOWSDATA {
|
|
UINT uFlags;
|
|
} EXITWINDOWSDATA, *PEXITWINDOWSDATA;
|
|
|
|
__inline void GetShutdownType(LPWSTR pszBuff, int cch, DWORD dwFlags)
|
|
{
|
|
if ((dwFlags & (EWX_POWEROFF | EWX_WINLOGON_OLD_POWEROFF)) != 0) {
|
|
LoadString(hmodUser, STR_SHUTDOWN_POWEROFF, pszBuff, cch);
|
|
} else if ((dwFlags & (EWX_REBOOT | EWX_WINLOGON_OLD_REBOOT)) != 0) {
|
|
LoadString(hmodUser, STR_SHUTDOWN_REBOOT, pszBuff, cch);
|
|
} else if ((dwFlags & (EWX_SHUTDOWN | EWX_WINLOGON_OLD_SHUTDOWN)) != 0) {
|
|
LoadString(hmodUser, STR_SHUTDOWN_SHUTDOWN, pszBuff, cch);
|
|
} else {
|
|
LoadString(hmodUser, STR_UNKNOWN, pszBuff, cch);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CsrTestShutdownPrivilege
|
|
*
|
|
* Looks at the user token to determine if they have shutdown privilege
|
|
*
|
|
* Returns TRUE if the user has the privilege, otherwise FALSE
|
|
*
|
|
\***************************************************************************/
|
|
BOOL
|
|
CsrTestShutdownPrivilege(
|
|
HANDLE UserToken
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
LUID LuidPrivilege = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE);
|
|
LUID TokenPrivilege;
|
|
ULONG BytesRequired;
|
|
ULONG i;
|
|
BOOL bHasPrivilege = FALSE;
|
|
BOOL bNetWork = FALSE;
|
|
PSID NetworkSid = NULL;
|
|
PTOKEN_PRIVILEGES Privileges = NULL;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
Status = RtlAllocateAndInitializeSid(&NtAuthority,
|
|
1, SECURITY_NETWORK_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&NetworkSid );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Ok for this call to fail, in that case we assume local shutdown.
|
|
if (CheckTokenMembership(UserToken, NetworkSid, &bNetWork)) {
|
|
if (bNetWork) {
|
|
LuidPrivilege = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);
|
|
}
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken,
|
|
TokenPrivileges,
|
|
NULL,
|
|
0,
|
|
&BytesRequired
|
|
);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Privileges = (PTOKEN_PRIVILEGES)UserLocalAlloc(HEAP_ZERO_MEMORY,
|
|
BytesRequired);
|
|
if (Privileges == NULL) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken,
|
|
TokenPrivileges,
|
|
Privileges,
|
|
BytesRequired,
|
|
&BytesRequired
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
for (i=0; i<Privileges->PrivilegeCount; i++) {
|
|
TokenPrivilege = *((LUID UNALIGNED *) &Privileges->Privileges[i].Luid);
|
|
if (RtlEqualLuid(&TokenPrivilege, &LuidPrivilege)) {
|
|
bHasPrivilege = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (NetworkSid) {
|
|
RtlFreeSid(NetworkSid);
|
|
}
|
|
|
|
if (Privileges) {
|
|
UserLocalFree(Privileges);
|
|
}
|
|
return bHasPrivilege;
|
|
}
|
|
|
|
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, RecordShutdownReason, PSHUTDOWN_REASON, psr)
|
|
BOOL RecordShutdownReason(
|
|
PSHUTDOWN_REASON psr)
|
|
{
|
|
PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
|
|
HANDLE hToken = NULL;
|
|
DWORD dwEventID;
|
|
DWORD dwTotalLen = 0; // length for the capture buffer.
|
|
DWORD dwCntPointers = 0; // number of message pointers for the capture buffer.
|
|
DWORD dwProcessNameLen = MAX_PATH + 1;
|
|
DWORD dwShutdownTypeLen = SHUTDOWN_TYPE_LEN;
|
|
BOOL bRet = FALSE;
|
|
LPWSTR lpszBuf = NULL;
|
|
USER_API_MSG m;
|
|
NTSTATUS status;
|
|
PRECORDSHUTDOWNREASONMSG a = &(m.u.RecordShutdownReason);
|
|
|
|
// Check privilege. We dont want a user without shutdown privilege to call this.
|
|
status = NtOpenThreadToken(NtCurrentThread(),TOKEN_QUERY, FALSE, &hToken);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = NtOpenThreadToken(NtCurrentThread(),TOKEN_QUERY, TRUE, &hToken);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = NtOpenProcessToken(NtCurrentProcess(),TOKEN_QUERY, &hToken);
|
|
if (!NT_SUCCESS(status)) {
|
|
RIPNTERR0(status, RIP_WARNING, "Cannot get token in RecordShutdownReason");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CsrTestShutdownPrivilege(hToken)) {
|
|
NtClose(hToken);
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Access denied in RecordShutdownReason");
|
|
goto Cleanup;
|
|
}
|
|
NtClose(hToken);
|
|
|
|
// Validate the structure
|
|
if (psr == NULL || psr->cbSize != sizeof(SHUTDOWN_REASON)) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Bad psr %p in RecordShutdownReason", psr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwCntPointers = 3;
|
|
dwTotalLen = dwProcessNameLen * sizeof(WCHAR) + dwShutdownTypeLen * sizeof(WCHAR) + sizeof(SHUTDOWN_REASON);
|
|
|
|
// Initialize all lengthes to 0
|
|
a->dwProcessNameLen = a->dwShutdownTypeLen = a->dwCommentLen = 0;
|
|
|
|
// Add Comment if we have one.
|
|
if (psr->lpszComment && wcslen(psr->lpszComment)) {
|
|
dwCntPointers++;
|
|
a->dwCommentLen = wcslen(psr->lpszComment) + 1;
|
|
dwTotalLen += a->dwCommentLen * sizeof(WCHAR);
|
|
}
|
|
|
|
// Adjust for the possible round up.
|
|
dwTotalLen += dwCntPointers * (sizeof(PVOID) - 1);
|
|
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(dwCntPointers, dwTotalLen);
|
|
if (CaptureBuffer == NULL) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// lpszBuf is shared for both process name and shutdown type.
|
|
// Make sure the len is the maximum of all of them.
|
|
lpszBuf = (LPWSTR)UserLocalAlloc(0, (dwProcessNameLen >= dwShutdownTypeLen ?
|
|
dwProcessNameLen : dwShutdownTypeLen) * sizeof(WCHAR));
|
|
if (!lpszBuf) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Fill the process name
|
|
if (!GetCurrentProcessName(lpszBuf, dwProcessNameLen)) {
|
|
RIPMSG0(RIP_WARNING, "Failed to GetCurrentProcessName in RecordShutdownReason");
|
|
goto Cleanup;
|
|
}
|
|
lpszBuf[MAX_PATH] = 0;
|
|
a->dwProcessNameLen = wcslen(lpszBuf)+1;
|
|
CsrAllocateMessagePointer(CaptureBuffer, ALIGN_UP(a->dwProcessNameLen * sizeof(WCHAR), PVOID), &a->pwchProcessName);
|
|
wcscpy(a->pwchProcessName, lpszBuf);
|
|
|
|
// Fill the shutdown type.
|
|
GetShutdownType(lpszBuf, dwShutdownTypeLen, psr->uFlags);
|
|
lpszBuf[SHUTDOWN_TYPE_LEN-1] = 0;
|
|
a->dwShutdownTypeLen = wcslen(lpszBuf)+1;
|
|
CsrAllocateMessagePointer(CaptureBuffer, ALIGN_UP(a->dwShutdownTypeLen * sizeof(WCHAR), PVOID), &a->pwchShutdownType);
|
|
wcscpy(a->pwchShutdownType, lpszBuf);
|
|
|
|
// copy over the SHUTDOWN_REASON.
|
|
CsrAllocateMessagePointer(CaptureBuffer, ALIGN_UP(sizeof(SHUTDOWN_REASON), PVOID), &a->psr);
|
|
memcpy(a->psr, psr, sizeof(SHUTDOWN_REASON));
|
|
|
|
if (psr->lpszComment && !wcslen(psr->lpszComment)) {
|
|
a->psr->lpszComment = NULL;
|
|
}
|
|
|
|
if (psr->lpszComment && wcslen(psr->lpszComment)){
|
|
CsrAllocateMessagePointer(CaptureBuffer, ALIGN_UP(a->dwCommentLen * sizeof(WCHAR), PVOID), &a->pwchComment);
|
|
wcscpy(a->pwchComment, psr->lpszComment);
|
|
}
|
|
|
|
switch (psr->dwEventType) {
|
|
case SR_EVENT_EXITWINDOWS:
|
|
if (psr->fShutdownCancelled) {
|
|
dwEventID = WARNING_EW_SHUTDOWN_CANCELLED;
|
|
} else {
|
|
dwEventID = STATUS_SHUTDOWN_CLEAN;
|
|
}
|
|
break;
|
|
case SR_EVENT_INITIATE_CLEAN:
|
|
dwEventID = STATUS_SHUTDOWN_CLEAN;
|
|
break;
|
|
case SR_EVENT_INITIATE_CLEAN_ABORT:
|
|
dwEventID = WARNING_ISSE_SHUTDOWN_CANCELLED;
|
|
break;
|
|
case SR_EVENT_DIRTY:
|
|
dwEventID = WARNING_DIRTY_REBOOT;
|
|
break;
|
|
default:
|
|
goto Cleanup;
|
|
}
|
|
|
|
a->dwEventID = dwEventID;
|
|
a->dwEventType = psr->dwEventType;
|
|
a->fShutdownCancelled = psr->fShutdownCancelled;
|
|
|
|
status = CsrClientCallServer((PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
|
|
UserpRecordShutdownReason
|
|
),
|
|
sizeof(*a)
|
|
);
|
|
|
|
bRet = NT_SUCCESS(status);
|
|
|
|
Cleanup:
|
|
if (CaptureBuffer) {
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
}
|
|
|
|
if (lpszBuf) {
|
|
UserLocalFree(lpszBuf);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
UINT GetLoggedOnUserCount(
|
|
VOID)
|
|
{
|
|
int iCount = 0;
|
|
BOOLEAN bSuccess;
|
|
|
|
TS_COUNTER TSCountersDyn[2];
|
|
|
|
TSCountersDyn[0].counterHead.dwCounterID = TERMSRV_CURRENT_DISC_SESSIONS;
|
|
TSCountersDyn[1].counterHead.dwCounterID = TERMSRV_CURRENT_ACTIVE_SESSIONS;
|
|
|
|
// access the termsrv counters to find out how many users are logged onto the system
|
|
bSuccess = WinStationGetTermSrvCountersValue(SERVERNAME_CURRENT, 2, TSCountersDyn);
|
|
|
|
if (bSuccess) {
|
|
if (TSCountersDyn[0].counterHead.bResult)
|
|
iCount += TSCountersDyn[0].dwValue;
|
|
|
|
if (TSCountersDyn[1].counterHead.bResult)
|
|
iCount += TSCountersDyn[1].dwValue;
|
|
}
|
|
|
|
return iCount;
|
|
}
|
|
|
|
BOOL IsSeShutdownNameEnabled()
|
|
{
|
|
BOOL bRet = FALSE; // assume the privilege is not held
|
|
NTSTATUS Status;
|
|
HANDLE hToken;
|
|
|
|
// try to get the thread token
|
|
Status = NtOpenThreadToken(GetCurrentThread(),
|
|
TOKEN_QUERY,
|
|
FALSE,
|
|
&hToken);
|
|
if (!NT_SUCCESS(Status)) {
|
|
// try the process token if we failed to get the thread token
|
|
Status = NtOpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&hToken);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
DWORD cbSize = 0;
|
|
TOKEN_PRIVILEGES* ptp;
|
|
|
|
NtQueryInformationToken(hToken,
|
|
TokenPrivileges,
|
|
NULL,
|
|
0,
|
|
&cbSize);
|
|
if (cbSize) {
|
|
ptp = (TOKEN_PRIVILEGES*)UserLocalAlloc(0, cbSize);
|
|
} else {
|
|
ptp = NULL;
|
|
}
|
|
|
|
if (ptp) {
|
|
Status = NtQueryInformationToken(hToken,
|
|
TokenPrivileges,
|
|
ptp,
|
|
cbSize,
|
|
&cbSize);
|
|
if (NT_SUCCESS(Status)) {
|
|
DWORD i;
|
|
for (i = 0; i < ptp->PrivilegeCount; i++) {
|
|
if (((ptp->Privileges[i].Luid.HighPart == 0) && (ptp->Privileges[i].Luid.LowPart == SE_SHUTDOWN_PRIVILEGE)) &&
|
|
(ptp->Privileges[i].Attributes & (SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED))) {
|
|
// found the privilege and it is enabled
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
UserLocalFree(ptp);
|
|
}
|
|
|
|
NtClose(hToken);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL NeedsDisplayWarning (UINT uNumUsers, UINT uExitWindowsFlags)
|
|
{
|
|
|
|
// If EWX_SYSTEM_CALLER then there's nobody on this session.
|
|
// Add one from the number of users.
|
|
|
|
if ((uExitWindowsFlags & EWX_SYSTEM_CALLER) && (uNumUsers > 0))
|
|
{
|
|
++uNumUsers;
|
|
}
|
|
|
|
// If number of users > 1 or EWX_WINLOGON_CALLER display warning.
|
|
|
|
return (uNumUsers > 1) || (uExitWindowsFlags & EWX_WINLOGON_CALLER);
|
|
}
|
|
|
|
FUNCLOG1(LOG_GENERAL, BOOL, APIENTRY, DisplayExitWindowsWarnings, UINT, uExitWindowsFlags)
|
|
BOOL APIENTRY DisplayExitWindowsWarnings(UINT uExitWindowsFlags)
|
|
{
|
|
BOOL bContinue = TRUE;
|
|
BOOL fIsRemote = ISREMOTESESSION();
|
|
UINT uNumUsers = GetLoggedOnUserCount();
|
|
UINT uID = 0;
|
|
|
|
// it would be nice to check the HKCU\ControlPanel\Desktop\AutoEndTask value and not display any UI if it is set,
|
|
// but since we are called from services it is probably better to not go mucking about in the per-user hive
|
|
|
|
if (uExitWindowsFlags & (EWX_POWEROFF | EWX_WINLOGON_OLD_POWEROFF | EWX_SHUTDOWN | EWX_WINLOGON_OLD_SHUTDOWN)) {
|
|
if (fIsRemote) {
|
|
if (NeedsDisplayWarning(uNumUsers, uExitWindowsFlags)) {
|
|
// Warn the user if remote shut down w/ active users
|
|
uID = IDS_SHUTDOWN_REMOTE_OTHERUSERS;
|
|
} else {
|
|
// Warn the user of remote shut down (cut our own legs off!)
|
|
uID = IDS_SHUTDOWN_REMOTE;
|
|
}
|
|
} else {
|
|
if (NeedsDisplayWarning(uNumUsers, uExitWindowsFlags)) {
|
|
// Warn the user if more than one user session active
|
|
uID = IDS_SHUTDOWN_OTHERUSERS;
|
|
}
|
|
}
|
|
} else if (uExitWindowsFlags & (EWX_REBOOT | EWX_WINLOGON_OLD_REBOOT)) {
|
|
// Warn the user if more than one user session active.
|
|
if (NeedsDisplayWarning(uNumUsers, uExitWindowsFlags)) {
|
|
uID = IDS_RESTART_OTHERUSERS;
|
|
}
|
|
}
|
|
|
|
if (uID != 0) {
|
|
TCHAR szTitle[MAX_PATH];
|
|
TCHAR szMessage[MAX_PATH];
|
|
DWORD dwTimeout = INFINITE;
|
|
UNICODE_STRING UnicodeString;
|
|
extern CONST WCHAR szWindowsKey[];
|
|
static CONST WCHAR szTimeout[] = L"ShutdownWarningDialogTimeout";
|
|
OBJECT_ATTRIBUTES OA;
|
|
HANDLE hKey;
|
|
DWORD cbSize;
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
|
|
DWORD dwTimeout;
|
|
} KeyTimeout;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, szWindowsKey);
|
|
InitializeObjectAttributes(&OA, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
if (NT_SUCCESS(NtOpenKey(&hKey, KEY_READ, &OA))) {
|
|
NTSTATUS rc;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, szTimeout);
|
|
|
|
rc = NtQueryValueKey(hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
&KeyTimeout,
|
|
sizeof KeyTimeout,
|
|
&cbSize);
|
|
if (NT_SUCCESS(rc)) {
|
|
dwTimeout = *((PDWORD)KeyTimeout.KeyInfo.Data);
|
|
RIPMSGF1(RIP_VERBOSE, "ShutdownWarningTimeout: set by the reg: %d", dwTimeout);
|
|
}
|
|
|
|
NtClose(hKey);
|
|
}
|
|
|
|
LoadString(hmodUser, IDS_EXITWINDOWS_TITLE, szTitle, sizeof(szTitle)/sizeof(szTitle[0]));
|
|
LoadString(hmodUser, uID, szMessage, sizeof(szMessage)/sizeof(szMessage[0]));
|
|
|
|
// We want to display the message box to be displayed to the user, and since this can be called from winlogon/services
|
|
// we need to pass the MB_SERVICE_NOTIFICATION flag.
|
|
if (MessageBoxTimeout(NULL, szMessage, szTitle,
|
|
MB_ICONEXCLAMATION | MB_YESNO | MB_SERVICE_NOTIFICATION | MB_SYSTEMMODAL | MB_SETFOREGROUND,
|
|
0, dwTimeout) == IDNO) {
|
|
bContinue = FALSE;
|
|
}
|
|
}
|
|
|
|
return bContinue;
|
|
}
|
|
|
|
DWORD ExitWindowsThread(PVOID pvParam);
|
|
|
|
BOOL WINAPI ExitWindowsWorker(
|
|
UINT uFlags,
|
|
BOOL fSecondThread)
|
|
{
|
|
EXITWINDOWSDATA ewd;
|
|
HANDLE hThread;
|
|
DWORD dwThreadId;
|
|
DWORD dwExitCode;
|
|
DWORD idWait;
|
|
MSG msg;
|
|
BOOL fSuccess;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Force a connection so apps will have a windowstation
|
|
* to log off of.
|
|
*/
|
|
if (PtiCurrent() == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Check for UI restrictions
|
|
*/
|
|
if (!NtUserCallOneParam((ULONG_PTR)uFlags, SFI_PREPAREFORLOGOFF)) {
|
|
RIPMSG0(RIP_WARNING, "ExitWindows called by a restricted thread\n");
|
|
return FALSE;
|
|
}
|
|
|
|
Status = CallUserpExitWindowsEx(uFlags, &fSuccess);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
return fSuccess;
|
|
} else if (Status == STATUS_CANT_WAIT && !fSecondThread) {
|
|
ewd.uFlags = uFlags;
|
|
hThread = CreateThread(NULL, 0, ExitWindowsThread, &ewd,
|
|
0, &dwThreadId);
|
|
if (hThread == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
while (1) {
|
|
idWait = MsgWaitForMultipleObjectsEx(1, &hThread,
|
|
INFINITE, QS_ALLINPUT, 0);
|
|
|
|
/*
|
|
* If the thread was signaled, we're done.
|
|
*/
|
|
if (idWait == WAIT_OBJECT_0) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Process any waiting messages
|
|
*/
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
GetExitCodeThread(hThread, &dwExitCode);
|
|
NtClose(hThread);
|
|
if (dwExitCode == ERROR_SUCCESS) {
|
|
return TRUE;
|
|
} else {
|
|
RIPERR0(dwExitCode, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
RIPNTERR0(Status, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
DWORD ExitWindowsThread(
|
|
PVOID pvParam)
|
|
{
|
|
PEXITWINDOWSDATA pewd = pvParam;
|
|
DWORD dwExitCode;
|
|
|
|
if (ExitWindowsWorker(pewd->uFlags, TRUE)) {
|
|
dwExitCode = 0;
|
|
} else {
|
|
dwExitCode = GetLastError();
|
|
}
|
|
|
|
ExitThread(dwExitCode);
|
|
return 0;
|
|
}
|
|
|
|
FUNCLOG2(LOG_GENERAL, BOOL, WINAPI, ExitWindowsEx, UINT, uFlags, DWORD, dwReasonCode)
|
|
BOOL WINAPI ExitWindowsEx(
|
|
UINT uFlags,
|
|
DWORD dwReasonCode)
|
|
{
|
|
BOOL bSuccess;
|
|
BOOL bShutdown = (uFlags & SHUTDOWN_FLAGS) != 0;
|
|
SHUTDOWN_REASON sr;
|
|
|
|
/*
|
|
* Check to see if we should bring up UI warning that there are other
|
|
* Terminal Server users connected to this machine. We only do this if the
|
|
* caller has not specified the EWX_FORCE option.
|
|
*/
|
|
if (bShutdown && !(uFlags & EWX_FORCE)) {
|
|
/*
|
|
* We don't want to display the warning dialog twice! (this function
|
|
* can be called by an application and again by winlogon in response to
|
|
* the first call)
|
|
*/
|
|
if (!gfLogonProcess || (uFlags & EWX_WINLOGON_INITIATED)) {
|
|
/*
|
|
* Don't put up UI if termsrv is our caller. Termsrv uses this api to shutdown winlogon
|
|
* on session 0 when a shutdown was initiated from a different session.
|
|
*/
|
|
if (!(uFlags & EWX_TERMSRV_INITIATED)) {
|
|
/*
|
|
* There are a bunch of lame apps (including InstallShield v5.1) that call ExitWindowsEx and then when it fails
|
|
* they go and enable the SE_SHUTDOWN_NAME privilege and then us call again. The problem is that we end up prompting the
|
|
* user twice in these cases. So before we put up any UI we check for the SE_SHUTDOWN_NAME privilege.
|
|
*/
|
|
if (IsSeShutdownNameEnabled()) {
|
|
if (!DisplayExitWindowsWarnings(uFlags & ~(EWX_WINLOGON_CALLER | EWX_SYSTEM_CALLER))) {
|
|
/*
|
|
* We need to log a cancel event if SET is enabled.
|
|
*/
|
|
if (IsSETEnabled()) {
|
|
SHUTDOWN_REASON sr;
|
|
sr.cbSize = sizeof(SHUTDOWN_REASON);
|
|
sr.uFlags = uFlags;
|
|
sr.dwReasonCode = 0;
|
|
sr.fShutdownCancelled = TRUE;
|
|
sr.dwEventType = SR_EVENT_EXITWINDOWS;
|
|
sr.lpszComment = NULL;
|
|
RecordShutdownReason(&sr);
|
|
}
|
|
|
|
/*
|
|
* We only want to return the real error code if our caller was winlogon. We lie
|
|
* to everyone else and tell them that everything succeeded. If we return failure
|
|
* when the user cancel's the operation, a some of apps just call ExitWindowsEx
|
|
* again, causing another dialog.
|
|
*/
|
|
if (uFlags & EWX_WINLOGON_INITIATED) {
|
|
SetLastError(ERROR_CANCELLED);
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sr.cbSize = sizeof(SHUTDOWN_REASON);
|
|
sr.uFlags = uFlags;
|
|
sr.dwReasonCode = dwReasonCode;
|
|
sr.fShutdownCancelled = FALSE;
|
|
sr.dwEventType = SR_EVENT_EXITWINDOWS;
|
|
sr.lpszComment = NULL;
|
|
|
|
/*
|
|
* If this is winlogon initiating the shutdown, we need to log before
|
|
* calling ExitWindowsWorker. Otherwise, if the user or an app cancels the
|
|
* shutdown, the cancel event will be logged before the initial shutdown
|
|
* event.
|
|
*/
|
|
if (gfLogonProcess && bShutdown && (uFlags & EWX_WINLOGON_INITIATED) != 0) {
|
|
if (IsSETEnabled()) {
|
|
RecordShutdownReason(&sr);
|
|
}
|
|
}
|
|
|
|
bSuccess = ExitWindowsWorker(uFlags, FALSE);
|
|
|
|
/*
|
|
* Log this shutdown if:
|
|
* 1) We're not winlogon (if we are, we might have logged above).
|
|
* 2) The shutdown (inititally, at least) succeeded.
|
|
* 3) We're actually shutting down (i.e., not logging off).
|
|
* 4) The registry key telling us to log is set.
|
|
*/
|
|
if (!gfLogonProcess && bSuccess && bShutdown && IsSETEnabled()) {
|
|
RecordShutdownReason(&sr);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(BUILD_WOW6432)
|
|
|
|
BOOL WINAPI EndTask(
|
|
HWND hwnd,
|
|
BOOL fShutdown,
|
|
BOOL fForce)
|
|
{
|
|
USER_API_MSG m;
|
|
PENDTASKMSG a = &m.u.EndTask;
|
|
|
|
UNREFERENCED_PARAMETER(fShutdown);
|
|
a->hwnd = hwnd;
|
|
a->fForce = fForce;
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpEndTask
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
if (NT_SUCCESS( m.ReturnValue )) {
|
|
SET_LAST_ERROR_RETURNED();
|
|
return a->fSuccess;
|
|
} else {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
Logon(
|
|
BOOL fLogon)
|
|
{
|
|
USER_API_MSG m;
|
|
PLOGONMSG a = &m.u.Logon;
|
|
|
|
a->fLogon = fLogon;
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpLogon
|
|
),
|
|
sizeof(*a)
|
|
);
|
|
}
|
|
|
|
NTSTATUS
|
|
APIENTRY
|
|
CallUserpRegisterLogonProcess(
|
|
IN DWORD dwProcessId)
|
|
{
|
|
|
|
USER_API_MSG m;
|
|
PLOGONMSG a = &m.u.Logon;
|
|
NTSTATUS Status;
|
|
|
|
m.u.IdLogon = dwProcessId;
|
|
Status = CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpRegisterLogonProcess),
|
|
sizeof(*a));
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(BUILD_CSRWOW64)
|
|
|
|
FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, RegisterLogonProcess, DWORD, dwProcessId, BOOL, fSecure)
|
|
BOOL RegisterLogonProcess(
|
|
DWORD dwProcessId,
|
|
BOOL fSecure)
|
|
{
|
|
gfLogonProcess = (BOOL)NtUserCallTwoParam(dwProcessId, fSecure,
|
|
SFI__REGISTERLOGONPROCESS);
|
|
|
|
/*
|
|
* Now, register the logon process into winsrv.
|
|
*/
|
|
if (gfLogonProcess) {
|
|
CallUserpRegisterLogonProcess(dwProcessId);
|
|
}
|
|
|
|
return gfLogonProcess;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(BUILD_WOW6432)
|
|
|
|
BOOL
|
|
WINAPI
|
|
RegisterServicesProcess(
|
|
DWORD dwProcessId)
|
|
{
|
|
USER_API_MSG m;
|
|
PREGISTERSERVICESPROCESSMSG a = &m.u.RegisterServicesProcess;
|
|
|
|
a->dwProcessId = dwProcessId;
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpRegisterServicesProcess
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
if (NT_SUCCESS( m.ReturnValue )) {
|
|
SET_LAST_ERROR_RETURNED();
|
|
return a->fSuccess;
|
|
} else {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
HDESK WINAPI GetThreadDesktop(
|
|
DWORD dwThreadId)
|
|
{
|
|
USER_API_MSG m;
|
|
PGETTHREADCONSOLEDESKTOPMSG a = &m.u.GetThreadConsoleDesktop;
|
|
|
|
a->dwThreadId = dwThreadId;
|
|
CsrClientCallServer( (PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX,
|
|
UserpGetThreadConsoleDesktop
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
if (NT_SUCCESS( m.ReturnValue )) {
|
|
return NtUserGetThreadDesktop(dwThreadId, a->hdeskConsole);
|
|
} else {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* DeviceEventWorker
|
|
*
|
|
* This is a private (not publicly exported) interface that the user-mode
|
|
* pnp manager calls when it needs to send a WM_DEVICECHANGE message to a
|
|
* specific window handle. The user-mode pnp manager is a service within
|
|
* services.exe and as such is not on the interactive window station and
|
|
* active desktop, so it can't directly call SendMessage. For broadcasted
|
|
* messages (messages that go to all top-level windows), the user-mode pnp
|
|
* manager calls BroadcastSystemMessage directly.
|
|
*
|
|
* PaulaT 06/04/97
|
|
*
|
|
\**************************************************************************/
|
|
ULONG
|
|
WINAPI
|
|
DeviceEventWorker(
|
|
IN HWND hWnd,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam,
|
|
IN DWORD dwFlags,
|
|
OUT PDWORD pdwResult)
|
|
{
|
|
USER_API_MSG m;
|
|
PDEVICEEVENTMSG a = &m.u.DeviceEvent;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
|
|
int cb = 0;
|
|
|
|
a->hWnd = hWnd;
|
|
a->wParam = wParam;
|
|
a->lParam = lParam;
|
|
a->dwFlags = dwFlags;
|
|
a->dwResult = 0;
|
|
|
|
//
|
|
// If lParam is specified, it must be marshalled (see the defines
|
|
// for this structure in dbt.h - the structure always starts with
|
|
// DEV_BROADCAST_HDR structure).
|
|
//
|
|
|
|
if (lParam) {
|
|
|
|
cb = ((PDEV_BROADCAST_HDR)lParam)->dbch_size;
|
|
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(1, cb);
|
|
if (CaptureBuffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
CsrCaptureMessageBuffer(CaptureBuffer,
|
|
(PCHAR)lParam,
|
|
cb,
|
|
(PVOID *)&a->lParam);
|
|
|
|
//
|
|
// This ends up calling SrvDeviceEvent routine in the server.
|
|
//
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
|
|
UserpDeviceEvent),
|
|
sizeof(*a));
|
|
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This ends up calling SrvDeviceEvent routine in the server.
|
|
//
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
|
|
UserpDeviceEvent),
|
|
sizeof(*a));
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(m.ReturnValue)) {
|
|
*pdwResult = (DWORD)a->dwResult;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "DeviceEventWorker failed.");
|
|
}
|
|
|
|
return m.ReturnValue;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
APIENTRY
|
|
CsrWin32HeapFail(
|
|
IN DWORD dwFlags,
|
|
IN BOOL bFail)
|
|
{
|
|
USER_API_MSG m;
|
|
PWIN32HEAPFAILMSG a = &m.u.Win32HeapFail;
|
|
|
|
a->dwFlags = dwFlags;
|
|
a->bFail = bFail;
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
|
|
UserpWin32HeapFail),
|
|
sizeof(*a));
|
|
|
|
if (!NT_SUCCESS(m.ReturnValue)) {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "UserpWin32HeapFail failed");
|
|
}
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
CsrWin32HeapStat(
|
|
PDBGHEAPSTAT phs,
|
|
DWORD dwLen)
|
|
{
|
|
USER_API_MSG m;
|
|
PWIN32HEAPSTATMSG a = &m.u.Win32HeapStat;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
|
|
|
|
a->dwLen = dwLen;
|
|
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(1, dwLen);
|
|
if (CaptureBuffer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
CsrCaptureMessageBuffer(CaptureBuffer,
|
|
(PCHAR)phs,
|
|
dwLen,
|
|
(PVOID *)&a->phs);
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
|
|
UserpWin32HeapStat),
|
|
sizeof(*a));
|
|
|
|
if (!NT_SUCCESS(m.ReturnValue)) {
|
|
RIPNTERR0(m.ReturnValue, RIP_VERBOSE, "UserpWin32HeapStat failed");
|
|
a->dwMaxTag = 0;
|
|
goto ErrExit;
|
|
}
|
|
RtlMoveMemory(phs, a->phs, dwLen);
|
|
|
|
ErrExit:
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
|
|
return a->dwMaxTag;
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
#endif
|
|
|
|
#if !defined(BUILD_CSRWOW64)
|
|
|
|
/******************************************************************************\
|
|
* CsrBroadcastSystemMessageExW
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This function is a private API used by the csrss server
|
|
*
|
|
* This function converts the csrss server thread into a GUI thread, then
|
|
* performs a BroadcastSystemMessageExW(), and finally restore the thread's
|
|
* desktop.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* dwFlags - Broadcast System message flags
|
|
*
|
|
* lpdwRecipients - Intended recipients of the message
|
|
*
|
|
* uiMessage - Message type
|
|
*
|
|
* wParam - first message parameter
|
|
*
|
|
* lParam - second message parameter
|
|
*
|
|
* pBSMInfo - BroadcastSystemMessage information
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Appropriate NTSTATUS code
|
|
*
|
|
\******************************************************************************/
|
|
FUNCLOG6(LOG_GENERAL, NTSTATUS, APIENTRY, CsrBroadcastSystemMessageExW, DWORD, dwFlags, LPDWORD, lpdwRecipients, UINT, uiMessage, WPARAM, wParam, LPARAM, lParam, PBSMINFO, pBSMInfo)
|
|
NTSTATUS
|
|
APIENTRY
|
|
CsrBroadcastSystemMessageExW(
|
|
DWORD dwFlags,
|
|
LPDWORD lpdwRecipients,
|
|
UINT uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PBSMINFO pBSMInfo
|
|
)
|
|
{
|
|
USERTHREAD_USEDESKTOPINFO utudi;
|
|
long result;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Caller must be from the csrss server
|
|
*/
|
|
if ( !gfServerProcess ) {
|
|
return( STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
/*
|
|
* Since this thread is a csrss thread, the thread is not a
|
|
* GUI thread and does not have a desktop associated with it.
|
|
* Must set the thread's desktop to the active desktop in
|
|
* order to call BroadcastSystemMessageExW
|
|
*/
|
|
|
|
utudi.hThread = NULL;
|
|
utudi.drdRestore.pdeskRestore = NULL;
|
|
|
|
Status = NtUserSetInformationThread( NtCurrentThread(),
|
|
UserThreadUseActiveDesktop,
|
|
&utudi,
|
|
sizeof(utudi) );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
result = BroadcastSystemMessageExW(
|
|
dwFlags,
|
|
lpdwRecipients,
|
|
uiMessage,
|
|
wParam,
|
|
lParam,
|
|
pBSMInfo );
|
|
|
|
/*
|
|
* Restore the previous desktop of the thread
|
|
*/
|
|
Status = NtUserSetInformationThread( NtCurrentThread(),
|
|
UserThreadUseDesktop,
|
|
&utudi,
|
|
sizeof(utudi) );
|
|
|
|
if ( NT_SUCCESS( Status ) && ( result <= 0 ) ) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
#endif
|