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.
4854 lines
141 KiB
4854 lines
141 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvvdm.c
|
|
|
|
Abstract:
|
|
|
|
This module implements windows server functions for VDMs
|
|
|
|
Author:
|
|
|
|
Sudeep Bharati (sudeepb) 03-Sep-1991
|
|
|
|
Revision History:
|
|
|
|
Sudeepb 18-Sep-1992
|
|
Added code to make VDM termination and resource cleanup robust.
|
|
AndyH 23-May-1994
|
|
Added Code to allow the Shared WOW to run if client is Interactive or SYSTEM
|
|
impersonating Interactive.
|
|
VadimB Sep-Dec 1996
|
|
Added code to allow for multiple default wows. Dispatching to an appropriate wow
|
|
is based upon the desktop name. It is still not possible to have multiple shared
|
|
wows on the same desktop (although technically trivial to implement) -- which would
|
|
be the level of OS/2 functionality
|
|
|
|
--*/
|
|
|
|
#include "basesrv.h"
|
|
#include "vdm.h"
|
|
#include "vdmdbg.h"
|
|
|
|
/*
|
|
* VadimB: Work to allow for multiple ntvdms
|
|
* - Add linked list of hwndWowExec's
|
|
* - The list should contain dwWowExecThreadId
|
|
* - dwWowExecProcessId
|
|
* - dwWowExecProcessSequenceNumber
|
|
*
|
|
* List is not completely dynamic - the first entry is static
|
|
* as the case with 1 shared vdm would be the most common one
|
|
*
|
|
*/
|
|
|
|
|
|
// record that reflects winstas with corresponding downlinks for desktops
|
|
// there could be only one wowexec per desktop (the default one, I mean)
|
|
// there could be many desktops per winsta as well as multiple winstas
|
|
//
|
|
// We have made a decision to simplify handling of wow vdms by introducing
|
|
// a single-level list of wowexecs [as opposed to 2-level so searching for the particular
|
|
// winsta would have been improved greatly]. The reason is purely practical: we do not
|
|
// anticipate having a large number of desktops/winsta
|
|
|
|
|
|
BOOL fIsFirstVDM = TRUE;
|
|
PCONSOLERECORD DOSHead = NULL; // Head Of DOS tasks with a valid Console
|
|
PBATRECORD BatRecordHead = NULL;
|
|
|
|
RTL_CRITICAL_SECTION BaseSrvDOSCriticalSection;
|
|
RTL_CRITICAL_SECTION BaseSrvWOWCriticalSection;
|
|
|
|
ULONG WOWTaskIdNext = WOWMINID; // This is global for all the wows in the system
|
|
|
|
|
|
typedef struct tagSharedWOWHead {
|
|
PSHAREDWOWRECORD pSharedWowRecord; // points to the list of shared wows
|
|
|
|
// other wow-related information is stored here
|
|
|
|
} SHAREDWOWRECORDHEAD, *PSHAREDWOWRECORDHEAD;
|
|
|
|
SHAREDWOWRECORDHEAD gWowHead;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Synch macros and functions
|
|
//
|
|
// DOSCriticalSection -- protects CONSOLERECORD list (DOSHead)
|
|
// WOWCriticalSection -- protects SHAREDWOWRECORD list (gpSharedWowRecordHead)
|
|
// each shared wow has it's very own critical section
|
|
|
|
|
|
// function to access console queue for modification
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Macros
|
|
//
|
|
//
|
|
|
|
|
|
// use these macros when manipulating shared wow items (adding, removing) or console
|
|
// records (adding, removing)
|
|
|
|
#define ENTER_WOW_CRITICAL() \
|
|
RtlEnterCriticalSection(&BaseSrvWOWCriticalSection)
|
|
|
|
|
|
#define LEAVE_WOW_CRITICAL() \
|
|
RtlLeaveCriticalSection(&BaseSrvWOWCriticalSection)
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Dynamic linking to system and import api stuff
|
|
//
|
|
//
|
|
|
|
|
|
typedef BOOL (WINAPI *POSTMESSAGEPROC)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
|
POSTMESSAGEPROC BaseSrvPostMessageA;
|
|
|
|
typedef BOOL (WINAPI *GETWINDOWTHREADPROCESSIDPROC)(HWND hWnd, LPDWORD lpdwProcessId);
|
|
GETWINDOWTHREADPROCESSIDPROC BaseSrvGetWindowThreadProcessId;
|
|
|
|
typedef NTSTATUS (*USERTESTTOKENFORINTERACTIVE)(HANDLE Token, PLUID pluidCaller);
|
|
USERTESTTOKENFORINTERACTIVE UserTestTokenForInteractive = NULL;
|
|
|
|
typedef NTSTATUS (*USERRESOLVEDESKTOPFORWOW)(PUNICODE_STRING);
|
|
USERRESOLVEDESKTOPFORWOW BaseSrvUserResolveDesktopForWow = NULL;
|
|
|
|
|
|
|
|
typedef struct tagBaseSrvApiImportRecord {
|
|
PCHAR pszProcedureName;
|
|
PVOID *ppProcAddress;
|
|
} BASESRVAPIIMPORTRECORD, *PBASESRVAPIIMPORTRECORD;
|
|
|
|
typedef struct tagBaseSrvModuleImportRecord {
|
|
PWCHAR pwszModuleName;
|
|
PBASESRVAPIIMPORTRECORD pApiImportRecord;
|
|
UINT nApiImportRecordCount;
|
|
HANDLE ModuleHandle;
|
|
} BASESRVMODULEIMPORTRECORD, *PBASESRVMODULEIMPORTRECORD;
|
|
|
|
|
|
// prototypes
|
|
|
|
NTSTATUS
|
|
BaseSrvFindSharedWowRecordByDesktop(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
PUNICODE_STRING pDesktopName,
|
|
PSHAREDWOWRECORD* ppSharedWowRecord
|
|
);
|
|
|
|
|
|
VOID BaseSrvAddWOWRecord (
|
|
PSHAREDWOWRECORD pSharedWow,
|
|
PWOWRECORD pWOWRecord
|
|
);
|
|
|
|
VOID BaseSrvRemoveWOWRecord (
|
|
PSHAREDWOWRECORD pSharedWow,
|
|
PWOWRECORD pWOWRecord
|
|
);
|
|
|
|
VOID
|
|
BaseSrvFreeSharedWowRecord(
|
|
PSHAREDWOWRECORD pSharedWowRecord
|
|
);
|
|
|
|
ULONG
|
|
BaseSrvGetWOWTaskId(
|
|
PSHAREDWOWRECORDHEAD pSharedWowHead // (->pSharedWowRecord)
|
|
);
|
|
|
|
NTSTATUS
|
|
BaseSrvRemoveWOWRecordByTaskId (
|
|
IN PSHAREDWOWRECORD pSharedWow,
|
|
IN ULONG iWowTask
|
|
);
|
|
|
|
|
|
PWOWRECORD
|
|
BaseSrvCheckAvailableWOWCommand(
|
|
PSHAREDWOWRECORD pSharedWow
|
|
);
|
|
|
|
PWOWRECORD
|
|
BaseSrvAllocateWOWRecord(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead
|
|
);
|
|
|
|
NTSTATUS
|
|
BaseSrvAddWowTask (
|
|
PCSR_API_MSG m,
|
|
ULONG SequenceNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumWowTask (
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b
|
|
);
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumWowProcess(
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Api import definitions
|
|
//
|
|
//
|
|
|
|
|
|
WCHAR wszUser32ModName[] = L"user32";
|
|
WCHAR wszWinSrvModName[] = L"winsrv";
|
|
|
|
BASESRVAPIIMPORTRECORD rgUser32ApiImport[] = {
|
|
{ "PostMessageA", (PVOID*)&BaseSrvPostMessageA }
|
|
, { "GetWindowThreadProcessId", (PVOID*)&BaseSrvGetWindowThreadProcessId }
|
|
, { "ResolveDesktopForWOW", (PVOID*)&BaseSrvUserResolveDesktopForWow }
|
|
};
|
|
|
|
BASESRVAPIIMPORTRECORD rgWinsrvApiImport[] = {
|
|
{ "_UserTestTokenForInteractive", (PVOID*)&UserTestTokenForInteractive }
|
|
};
|
|
|
|
|
|
BASESRVMODULEIMPORTRECORD rgBaseSrvModuleImport[] = {
|
|
{ wszUser32ModName, rgUser32ApiImport, sizeof(rgUser32ApiImport) / sizeof(rgUser32ApiImport[0]), NULL },
|
|
{ wszWinSrvModName, rgWinsrvApiImport, sizeof(rgWinsrvApiImport) / sizeof(rgWinsrvApiImport[0]), NULL }
|
|
};
|
|
|
|
|
|
// import all the necessary apis at once
|
|
// This procedure should execute just once and then the appropriate components just
|
|
// hang around
|
|
// call this with
|
|
// Status = BaseSrvImportApis(rgBaseSrvModuleImport,
|
|
// sizeof(rgBaseSrvModuleImport)/sizeof(rgBaseSrvModuleImport[0]))
|
|
//
|
|
|
|
NTSTATUS
|
|
BaseSrvImportApis(
|
|
PBASESRVMODULEIMPORTRECORD pModuleImport,
|
|
UINT nModules
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UINT uModule, uProcedure;
|
|
PBASESRVAPIIMPORTRECORD pApiImport;
|
|
STRING ProcedureName; // procedure name or module name
|
|
UNICODE_STRING ModuleName;
|
|
HANDLE ModuleHandle;
|
|
|
|
|
|
for (uModule = 0; uModule < nModules; ++uModule, ++pModuleImport) {
|
|
|
|
// see if we can load this particular dll
|
|
RtlInitUnicodeString(&ModuleName, pModuleImport->pwszModuleName);
|
|
Status = LdrLoadDll(NULL,
|
|
NULL,
|
|
&ModuleName, // module name string
|
|
&ModuleHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
// we may have linked to a few dlls at this point - we have to unlink from all of those
|
|
// by unloading the dll which is really a useless exersise.
|
|
// so just abandon and return -- BUGBUG - cleanup later
|
|
KdPrint(("BaseSrvImportApis: Failed to load %ls\n",
|
|
pModuleImport->pwszModuleName));
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
pModuleImport->ModuleHandle = ModuleHandle;
|
|
|
|
pApiImport = pModuleImport->pApiImportRecord;
|
|
|
|
for (uProcedure = 0, pApiImport = pModuleImport->pApiImportRecord;
|
|
uProcedure < pModuleImport->nApiImportRecordCount;
|
|
++uProcedure, ++pApiImport) {
|
|
|
|
RtlInitString(&ProcedureName, pApiImport->pszProcedureName);
|
|
Status = LdrGetProcedureAddress(ModuleHandle,
|
|
&ProcedureName, // procedure name string
|
|
0,
|
|
pApiImport->ppProcAddress);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
// we have failed to get this procedure - something is wrong
|
|
// perform a cleanup
|
|
KdPrint(("BaseSrvImportApis: Failed to link %s from %ls\n",
|
|
pApiImport->pszProcedureName,
|
|
pModuleImport->pwszModuleName));
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
ErrorCleanup:
|
|
|
|
// here we engage into a messy cleanup procedure by returning things back to the way
|
|
// they were before we have started
|
|
|
|
for (; uModule > 0; --uModule, --pModuleImport) {
|
|
|
|
// reset all the apis
|
|
for (uProcedure = 0, pApiImport = pModuleImport->pApiImportRecord;
|
|
uProcedure < pModuleImport->nApiImportRecordCount;
|
|
++uProcedure, ++pApiImport) {
|
|
|
|
*pApiImport->ppProcAddress = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != pModuleImport->ModuleHandle) {
|
|
LdrUnloadDll(pModuleImport->ModuleHandle);
|
|
pModuleImport->ModuleHandle = NULL;
|
|
}
|
|
}
|
|
|
|
return (Status);
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Manipulating shared wows
|
|
//
|
|
//
|
|
// assumes pDesktopName != NULL
|
|
// without the explicit checking
|
|
|
|
PSHAREDWOWRECORD BaseSrvAllocateSharedWowRecord (
|
|
PUNICODE_STRING pDesktopName
|
|
)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
DWORD dwSharedWowRecordSize = sizeof(SHAREDWOWRECORD) +
|
|
pDesktopName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
pSharedWow = RtlAllocateHeap(RtlProcessHeap (),
|
|
MAKE_TAG( VDM_TAG ),
|
|
dwSharedWowRecordSize);
|
|
if (NULL != pSharedWow) {
|
|
RtlZeroMemory ((PVOID)pSharedWow, dwSharedWowRecordSize);
|
|
// initialize desktop name
|
|
pSharedWow->WowExecDesktopName.MaximumLength = pDesktopName->Length + sizeof(WCHAR);
|
|
pSharedWow->WowExecDesktopName.Buffer = (PWCHAR)(pSharedWow + 1);
|
|
RtlCopyUnicodeString(&pSharedWow->WowExecDesktopName, pDesktopName);
|
|
pSharedWow->WowAuthId = RtlConvertLongToLuid(-1);
|
|
}
|
|
|
|
return pSharedWow;
|
|
}
|
|
|
|
// this function completely removes the given shared wow vdm
|
|
// from our accounting
|
|
//
|
|
// removes the record from the list of shared wow records
|
|
//
|
|
// This function also frees the associated memory
|
|
//
|
|
//
|
|
|
|
NTSTATUS
|
|
BaseSrvDeleteSharedWowRecord (
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
PSHAREDWOWRECORD pSharedWowRecord
|
|
)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWowRecordPrev = NULL;
|
|
PSHAREDWOWRECORD pSharedWowRecordCur;
|
|
|
|
if (NULL == pSharedWowRecord) { // this is dumb
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
pSharedWowRecordCur = pSharedWowRecordHead->pSharedWowRecord;
|
|
while (NULL != pSharedWowRecordCur) {
|
|
if (pSharedWowRecordCur == pSharedWowRecord) {
|
|
break;
|
|
}
|
|
|
|
pSharedWowRecordPrev = pSharedWowRecordCur;
|
|
pSharedWowRecordCur = pSharedWowRecordCur->pNextSharedWow;
|
|
}
|
|
|
|
if (NULL == pSharedWowRecordCur) {
|
|
KdPrint(("BaseSrvDeleteSharedWowRecord: invalid pointer to Shared WOW\n"));
|
|
ASSERT(FALSE);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
// unlink here
|
|
if (NULL == pSharedWowRecordPrev) {
|
|
pSharedWowRecordHead->pSharedWowRecord = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
else {
|
|
pSharedWowRecordPrev->pNextSharedWow = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
|
|
BaseSrvFreeSharedWowRecord(pSharedWowRecord);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// assumes no cs is held -- self-contained
|
|
// assoc console record should have been removed by now
|
|
// nukes all tasks associated with this particular shared wow
|
|
|
|
VOID
|
|
BaseSrvFreeSharedWowRecord(
|
|
PSHAREDWOWRECORD pSharedWowRecord)
|
|
{
|
|
PWOWRECORD pWOWRecord,
|
|
pWOWRecordLast;
|
|
|
|
if(pSharedWowRecord->WOWUserToken) {
|
|
NtClose(pSharedWowRecord->WOWUserToken);
|
|
}
|
|
|
|
pWOWRecord = pSharedWowRecord->pWOWRecord;
|
|
|
|
|
|
while (NULL != pWOWRecord) {
|
|
pWOWRecordLast = pWOWRecord->WOWRecordNext;
|
|
if(pWOWRecord->hWaitForParent) {
|
|
NtSetEvent (pWOWRecord->hWaitForParent,NULL);
|
|
NtClose (pWOWRecord->hWaitForParent);
|
|
pWOWRecord->hWaitForParent = 0;
|
|
}
|
|
BaseSrvFreeWOWRecord(pWOWRecord);
|
|
pWOWRecord = pWOWRecordLast;
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pSharedWowRecord);
|
|
}
|
|
|
|
|
|
// assumes: global wow crit sec is held
|
|
|
|
NTSTATUS
|
|
BaseSrvFindSharedWowRecordByDesktop(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
PUNICODE_STRING pDesktopName,
|
|
PSHAREDWOWRECORD* ppSharedWowRecord)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWowRecord = pSharedWowRecordHead->pSharedWowRecord;
|
|
|
|
while (NULL != pSharedWowRecord) {
|
|
if (0 == RtlCompareUnicodeString(&pSharedWowRecord->WowExecDesktopName,
|
|
pDesktopName,
|
|
TRUE)) {
|
|
break;
|
|
}
|
|
pSharedWowRecord = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
|
|
if (NULL != pSharedWowRecord) {
|
|
*ppSharedWowRecord = pSharedWowRecord;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND; // bummer, this is not found
|
|
}
|
|
|
|
// Vadimb : modify this to handle sorted list properly
|
|
// then find should be moded to work a little faster - BUGBUG
|
|
// assumes: pSharedWowRecord->pNextSharedWow is inited to NULL
|
|
|
|
VOID
|
|
BaseSrvAddSharedWowRecord(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
PSHAREDWOWRECORD pSharedWowRecord)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWowRecordCur = pSharedWowRecordHead->pSharedWowRecord;
|
|
|
|
if (NULL == pSharedWowRecordCur) {
|
|
pSharedWowRecordHead->pSharedWowRecord = pSharedWowRecord;
|
|
}
|
|
else {
|
|
|
|
PSHAREDWOWRECORD pSharedWowRecordPrev = NULL;
|
|
LONG lCompare;
|
|
|
|
while (NULL != pSharedWowRecordCur) {
|
|
lCompare = RtlCompareUnicodeString(&pSharedWowRecordCur->WowExecDesktopName,
|
|
&pSharedWowRecord->WowExecDesktopName,
|
|
TRUE);
|
|
if (lCompare > 0) {
|
|
break;
|
|
}
|
|
|
|
pSharedWowRecordPrev = pSharedWowRecordCur;
|
|
pSharedWowRecordCur = pSharedWowRecordCur->pNextSharedWow;
|
|
}
|
|
|
|
pSharedWowRecord->pNextSharedWow = pSharedWowRecordCur;
|
|
|
|
if (NULL == pSharedWowRecordPrev) { // goes to the head
|
|
pSharedWowRecordHead->pSharedWowRecord = pSharedWowRecord;
|
|
}
|
|
else {
|
|
pSharedWowRecordPrev->pNextSharedWow = pSharedWowRecord;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvFindSharedWowRecordByConsoleHandle(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
HANDLE hConsole,
|
|
PSHAREDWOWRECORD *ppSharedWowRecord)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWow = pSharedWowRecordHead->pSharedWowRecord;
|
|
|
|
while (NULL != pSharedWow) {
|
|
// see if same hConsole
|
|
if (pSharedWow->hConsole == hConsole) {
|
|
*ppSharedWowRecord = pSharedWow;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
pSharedWow = pSharedWow->pNextSharedWow;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvFindSharedWowRecordByTaskId(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead,
|
|
ULONG TaskId, // task id
|
|
PSHAREDWOWRECORD *ppSharedWowRecord,
|
|
PWOWRECORD *ppWowRecord) // optional
|
|
{
|
|
PSHAREDWOWRECORD pSharedWow = pSharedWowRecordHead->pSharedWowRecord;
|
|
PWOWRECORD pWowRecord;
|
|
|
|
ASSERT(0 != TaskId); // this is a pre-condition
|
|
|
|
while (NULL != pSharedWow) {
|
|
|
|
pWowRecord = pSharedWow->pWOWRecord;
|
|
|
|
while (NULL != pWowRecord) {
|
|
|
|
if (pWowRecord->iTask == TaskId) {
|
|
|
|
ASSERT(NULL != ppWowRecord);
|
|
|
|
// this is wow task
|
|
*ppSharedWowRecord = pSharedWow;
|
|
if (NULL != ppWowRecord) {
|
|
*ppWowRecord = pWowRecord;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pWowRecord = pWowRecord->WOWRecordNext;
|
|
}
|
|
|
|
pSharedWow = pSharedWow->pNextSharedWow;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvGetVdmSequence(
|
|
HANDLE hProcess,
|
|
PULONG pSequenceNumber)
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS pCsrProcess;
|
|
|
|
Status = CsrLockProcessByClientId(hProcess, &pCsrProcess);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
*pSequenceNumber = pCsrProcess->SequenceNumber;
|
|
CsrUnlockProcess(pCsrProcess);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////// End new code
|
|
|
|
|
|
// internal prototypes
|
|
ULONG
|
|
GetNextDosSesId(VOID);
|
|
|
|
NTSTATUS
|
|
GetConsoleRecordDosSesId (
|
|
IN ULONG DosSesId,
|
|
IN OUT PCONSOLERECORD *pConsoleRecord
|
|
);
|
|
|
|
NTSTATUS
|
|
OkToRunInSharedWOW(
|
|
IN HANDLE UniqueProcessClientId,
|
|
OUT PLUID pAuthenticationId,
|
|
OUT PHANDLE pWOWUserToken
|
|
);
|
|
|
|
BOOL
|
|
IsClientSystem(
|
|
HANDLE hUserToken
|
|
);
|
|
|
|
VOID
|
|
BaseSrvVDMInit(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlInitializeCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
Status = RtlInitializeCriticalSection( &BaseSrvWOWCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
BaseSrvCheckVDM(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PBASE_CHECKVDM_MSG b = (PBASE_CHECKVDM_MSG)&m->u.ApiMessageData;
|
|
|
|
if (BaseSrvIsVdmAllowed() == FALSE) {
|
|
return STATUS_VDM_DISALLOWED;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->CmdLine, b->CmdLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->AppName, b->AppLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Env, b->EnvLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->PifFile, b->PifLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->CurDirectory, b->CurDirectoryLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Title, b->TitleLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Reserved, b->ReservedLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Desktop, b->DesktopLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->StartupInfo, sizeof(STARTUPINFO), sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (b->UserLuid && !CsrValidateMessageBuffer(m, &b->UserLuid, sizeof(LUID), sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(b->BinaryType == BINARY_TYPE_WIN16) {
|
|
Status = BaseSrvCheckWOW (b, m->h.ClientId.UniqueProcess);
|
|
}
|
|
else {
|
|
Status = BaseSrvCheckDOS (b, m->h.ClientId.UniqueProcess);
|
|
}
|
|
|
|
return ((ULONG)Status);
|
|
}
|
|
|
|
ULONG
|
|
BaseSrvUpdateVDMEntry(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
PBASE_UPDATE_VDM_ENTRY_MSG b = (PBASE_UPDATE_VDM_ENTRY_MSG)&m->u.ApiMessageData;
|
|
ULONG SequenceNumber;
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber))) {
|
|
return (ULONG)STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BINARY_TYPE_WIN16 == b->BinaryType)
|
|
return (BaseSrvUpdateWOWEntry (b, SequenceNumber));
|
|
else
|
|
return (BaseSrvUpdateDOSEntry (b, SequenceNumber));
|
|
}
|
|
|
|
|
|
//
|
|
// This call makes an explicit assumption that the very first time ntvdm is accessed --
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
|
|
|
|
ULONG
|
|
BaseSrvGetNextVDMCommand(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b = (PBASE_GET_NEXT_VDM_COMMAND_MSG)&m->u.ApiMessageData;
|
|
PDOSRECORD pDOSRecord,pDOSRecordTemp=NULL;
|
|
PWOWRECORD pWOWRecord;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
PVDMINFO lpVDMInfo;
|
|
HANDLE Handle,TargetHandle;
|
|
LONG WaitState;
|
|
PBATRECORD pBatRecord;
|
|
PSHAREDWOWRECORD pSharedWow = NULL;
|
|
BOOL bWowApp = b->VDMState & ASKING_FOR_WOW_BINARY;
|
|
BOOL bSepWow = b->VDMState & ASKING_FOR_SEPWOW_BINARY;
|
|
ULONG SequenceNumber;
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber))) {
|
|
return (ULONG)STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->Env, b->EnvLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(b->VDMState & ASKING_FOR_WOWPROCLIST) {
|
|
return BaseSrvEnumWowProcess(b);
|
|
}
|
|
else if(b->VDMState & ASKING_FOR_WOWTASKLIST) {
|
|
return BaseSrvEnumWowTask(b);
|
|
}
|
|
else if(b->VDMState & ASKING_TO_ADD_WOWTASK) {
|
|
return BaseSrvAddWowTask (m, SequenceNumber);
|
|
}
|
|
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->CmdLine, b->CmdLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->AppName, b->AppLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->PifFile, b->PifLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->CurDirectory, b->CurDirectoryLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Title, b->TitleLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Reserved, b->ReservedLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (!CsrValidateMessageBuffer(m, &b->Desktop, b->DesktopLen, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->StartupInfo, sizeof(STARTUPINFO), sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (bWowApp) { // wow call please
|
|
|
|
BOOL bPif = b->VDMState & ASKING_FOR_PIF;
|
|
|
|
// find the shared wow record that we are calling
|
|
// to do that we look at iTask which (in case of shared wow
|
|
|
|
|
|
// this could have been the very first call that we've made in so far
|
|
// the iTask in this context procides us with
|
|
|
|
// look for our beloved shared wow using the [supplied] task id
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// grab crit section for read access
|
|
if (bPif && b->iTask) {
|
|
// this is probably the very first call -- update session handles first
|
|
Status = BaseSrvFindSharedWowRecordByTaskId(&gWowHead,
|
|
b->iTask,
|
|
&pSharedWow,
|
|
&pWOWRecord);
|
|
pSharedWow->hConsole = b->ConsoleHandle;
|
|
}
|
|
else { // this is not a pif -- find by a console handle then
|
|
|
|
Status = BaseSrvFindSharedWowRecordByConsoleHandle(&gWowHead,
|
|
b->ConsoleHandle,
|
|
&pSharedWow);
|
|
}
|
|
|
|
// now if we have the share wow - party!
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("BaseSrvGetNextVDMCommand: Shared Wow has not been found. Console : 0x%x\n", b->ConsoleHandle));
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
if (SequenceNumber != pSharedWow->SequenceNumber) {
|
|
KdPrint(("BaseSrvGetNextVdmCommand: Shared wow sequence number doesn't match\n"));
|
|
LEAVE_WOW_CRITICAL();
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ASSERT(NULL != pSharedWow);
|
|
|
|
//
|
|
// WowExec is asking for a command. We never block when
|
|
// asking for a WOW binary, since WOW no longer has a thread
|
|
// blocked in GetNextVDMCommand. Instead, WowExec gets a
|
|
// message posted to it by BaseSrv when there are command(s)
|
|
// waiting for it, and it loops calling GetNextVDMCommand
|
|
// until it fails -- but it must not block.
|
|
//
|
|
|
|
b->WaitObjectForVDM = 0;
|
|
|
|
// Vadimb: this call should uniquelly identify the caller as this is
|
|
// the task running on a particular winsta/desktop
|
|
// thus, as such it should be picked up from the appropriate queue
|
|
|
|
|
|
if (NULL == (pWOWRecord = BaseSrvCheckAvailableWOWCommand(pSharedWow))) {
|
|
|
|
//
|
|
// There's no command waiting for WOW, so just return.
|
|
// This is where we used to cause blocking.
|
|
//
|
|
b->TitleLen =
|
|
b->EnvLen =
|
|
b->DesktopLen =
|
|
b->ReservedLen =
|
|
b->CmdLen =
|
|
b->AppLen =
|
|
b->PifLen =
|
|
b->CurDirectoryLen = 0;
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
|
|
return ((ULONG)STATUS_SUCCESS);
|
|
}
|
|
|
|
lpVDMInfo = pWOWRecord->lpVDMInfo;
|
|
|
|
if (bPif) { // this is initial call made by ntvdm
|
|
|
|
Status = BaseSrvFillPifInfo (lpVDMInfo,b);
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
|
|
return (Status);
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// DOS VDM or Separate WOW is asking for next command.
|
|
//
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
if (b->VDMState & ASKING_FOR_PIF && b->iTask)
|
|
Status = GetConsoleRecordDosSesId(b->iTask,&pConsoleRecord);
|
|
else
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if (!NT_SUCCESS (Status) || SequenceNumber != pConsoleRecord->SequenceNumber) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
KdPrint(("BaseSrvGetNextVdmCommand: Couldn't find dos record or sequence numbers don't match\n"));
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
|
|
if (b->VDMState & ASKING_FOR_PIF) {
|
|
if (pDOSRecord) {
|
|
Status = BaseSrvFillPifInfo (pDOSRecord->lpVDMInfo,b);
|
|
if (b->iTask) {
|
|
if (!pConsoleRecord->hConsole) {
|
|
pConsoleRecord->hConsole = b->ConsoleHandle;
|
|
pConsoleRecord->DosSesId = 0;
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return (Status);
|
|
}
|
|
|
|
if (!bSepWow) {
|
|
if (!(b->VDMState & (ASKING_FOR_FIRST_COMMAND |
|
|
ASKING_FOR_SECOND_TIME |
|
|
NO_PARENT_TO_WAKE))
|
|
|| (b->VDMState & ASKING_FOR_SECOND_TIME && b->ExitCode != 0))
|
|
{
|
|
|
|
// Search first VDM_TO_TAKE_A_COMMAND or last VDM_BUSY record as
|
|
// per the case.
|
|
if (b->VDMState & ASKING_FOR_SECOND_TIME){
|
|
while(pDOSRecord && pDOSRecord->VDMState != VDM_TO_TAKE_A_COMMAND)
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
else {
|
|
while(pDOSRecord){
|
|
if(pDOSRecord->VDMState == VDM_BUSY)
|
|
pDOSRecordTemp = pDOSRecord;
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
pDOSRecord = pDOSRecordTemp;
|
|
}
|
|
|
|
|
|
if (pDOSRecord == NULL) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pDOSRecord->ErrorCode = b->ExitCode;
|
|
pDOSRecord->VDMState = VDM_HAS_RETURNED_ERROR_CODE;
|
|
NtSetEvent (pDOSRecord->hWaitForParentDup,NULL);
|
|
NtClose (pDOSRecord->hWaitForParentDup);
|
|
pDOSRecord->hWaitForParentDup = 0;
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
}
|
|
|
|
while (pDOSRecord && pDOSRecord->VDMState != VDM_TO_TAKE_A_COMMAND)
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
|
|
if (pDOSRecord == NULL) {
|
|
|
|
if (bSepWow ||
|
|
(b->VDMState & RETURN_ON_NO_COMMAND && b->VDMState & ASKING_FOR_SECOND_TIME))
|
|
{
|
|
b->WaitObjectForVDM = 0;
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_NO_MEMORY);
|
|
}
|
|
|
|
if(pConsoleRecord->hWaitForVDMDup == 0 ){
|
|
if(NT_SUCCESS(BaseSrvCreatePairWaitHandles (&Handle,
|
|
&TargetHandle))){
|
|
pConsoleRecord->hWaitForVDMDup = Handle;
|
|
pConsoleRecord->hWaitForVDM = TargetHandle;
|
|
}
|
|
else {
|
|
b->WaitObjectForVDM = 0;
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_NO_MEMORY);
|
|
}
|
|
}
|
|
else {
|
|
NtResetEvent(pConsoleRecord->hWaitForVDMDup,&WaitState);
|
|
}
|
|
b->WaitObjectForVDM = pConsoleRecord->hWaitForVDM;
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
b->WaitObjectForVDM = 0;
|
|
lpVDMInfo = pDOSRecord->lpVDMInfo;
|
|
|
|
}
|
|
|
|
//
|
|
// ASKING_FOR_ENVIRONMENT
|
|
// Return the information but DO NOT delete the lpVDMInfo
|
|
// associated with the DOS record
|
|
// ONLY DOS APPS NEED THIS
|
|
//
|
|
if (b->VDMState & ASKING_FOR_ENVIRONMENT) {
|
|
if (lpVDMInfo->EnviornmentSize <= b->EnvLen) {
|
|
RtlMoveMemory(b->Env,
|
|
lpVDMInfo->Enviornment,
|
|
lpVDMInfo->EnviornmentSize);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
b->EnvLen = lpVDMInfo->EnviornmentSize;
|
|
|
|
if (bWowApp) {
|
|
LEAVE_WOW_CRITICAL();
|
|
}
|
|
else {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// check buffer sizes, CmdLine is mandatory!
|
|
//
|
|
|
|
if (!b->CmdLine || lpVDMInfo->CmdSize > b->CmdLen ||
|
|
(b->AppName && lpVDMInfo->AppLen > b->AppLen) ||
|
|
(b->Env && lpVDMInfo->EnviornmentSize > b->EnvLen) ||
|
|
(b->PifFile && lpVDMInfo->PifLen > b->PifLen) ||
|
|
(b->CurDirectory && lpVDMInfo->CurDirectoryLen > b->CurDirectoryLen) ||
|
|
(b->Title && lpVDMInfo->TitleLen > b->TitleLen) ||
|
|
(b->Reserved && lpVDMInfo->ReservedLen > b->ReservedLen) ||
|
|
(b->Desktop && lpVDMInfo->DesktopLen > b->DesktopLen)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
b->CmdLen = lpVDMInfo->CmdSize;
|
|
b->AppLen = lpVDMInfo->AppLen;
|
|
b->PifLen = lpVDMInfo->PifLen;
|
|
b->EnvLen = lpVDMInfo->EnviornmentSize;
|
|
b->CurDirectoryLen = lpVDMInfo->CurDirectoryLen;
|
|
b->DesktopLen = lpVDMInfo->DesktopLen;
|
|
b->TitleLen = lpVDMInfo->TitleLen;
|
|
b->ReservedLen = lpVDMInfo->ReservedLen;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (bWowApp) {
|
|
LEAVE_WOW_CRITICAL();
|
|
}
|
|
else {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
return (Status);
|
|
}
|
|
|
|
|
|
if (lpVDMInfo->CmdLine && b->CmdLine)
|
|
RtlMoveMemory(b->CmdLine,
|
|
lpVDMInfo->CmdLine,
|
|
lpVDMInfo->CmdSize);
|
|
|
|
if (lpVDMInfo->AppName && b->AppName)
|
|
RtlMoveMemory(b->AppName,
|
|
lpVDMInfo->AppName,
|
|
lpVDMInfo->AppLen);
|
|
|
|
if (lpVDMInfo->PifFile && b->PifFile)
|
|
RtlMoveMemory(b->PifFile,
|
|
lpVDMInfo->PifFile,
|
|
lpVDMInfo->PifLen);
|
|
|
|
if (lpVDMInfo->CurDirectory && b->CurDirectory)
|
|
RtlMoveMemory(b->CurDirectory,
|
|
lpVDMInfo->CurDirectory,
|
|
lpVDMInfo->CurDirectoryLen);
|
|
|
|
if (lpVDMInfo->Title && b->Title)
|
|
RtlMoveMemory(b->Title,
|
|
lpVDMInfo->Title,
|
|
lpVDMInfo->TitleLen);
|
|
|
|
if (lpVDMInfo->Reserved && b->Reserved)
|
|
RtlMoveMemory(b->Reserved,
|
|
lpVDMInfo->Reserved,
|
|
lpVDMInfo->ReservedLen);
|
|
|
|
if (lpVDMInfo->Enviornment && b->Env)
|
|
RtlMoveMemory(b->Env,
|
|
lpVDMInfo->Enviornment,
|
|
lpVDMInfo->EnviornmentSize);
|
|
|
|
|
|
if (lpVDMInfo->VDMState & STARTUP_INFO_RETURNED)
|
|
RtlMoveMemory(b->StartupInfo,
|
|
&lpVDMInfo->StartupInfo,
|
|
sizeof (STARTUPINFOA));
|
|
|
|
if (lpVDMInfo->Desktop && b->Desktop)
|
|
RtlMoveMemory(b->Desktop,
|
|
lpVDMInfo->Desktop,
|
|
lpVDMInfo->DesktopLen);
|
|
|
|
|
|
if ((pBatRecord = BaseSrvGetBatRecord (b->ConsoleHandle)) != NULL)
|
|
b->fComingFromBat = TRUE;
|
|
else
|
|
b->fComingFromBat = FALSE;
|
|
|
|
b->CurrentDrive = lpVDMInfo->CurDrive;
|
|
b->CodePage = lpVDMInfo->CodePage;
|
|
b->dwCreationFlags = lpVDMInfo->dwCreationFlags;
|
|
b->VDMState = lpVDMInfo->VDMState;
|
|
|
|
if (bWowApp) {
|
|
b->iTask = pWOWRecord->iTask;
|
|
pWOWRecord->fDispatched = TRUE;
|
|
}
|
|
else {
|
|
pDOSRecord->VDMState = VDM_BUSY;
|
|
}
|
|
|
|
b->StdIn = lpVDMInfo->StdIn;
|
|
b->StdOut = lpVDMInfo->StdOut;
|
|
b->StdErr = lpVDMInfo->StdErr;
|
|
|
|
if (bSepWow) {
|
|
// this was a sep wow request -- we have done this only record that is to
|
|
// be dispatched to this particular wow -- now just remove every trace of
|
|
// this wow on the server side...
|
|
|
|
NtClose( pConsoleRecord->hVDM );
|
|
BaseSrvFreeConsoleRecord(pConsoleRecord); // unwire as well
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
else {
|
|
// this is shared wow or dos app -- free vdm info and release the
|
|
// appropriate sync object
|
|
|
|
BaseSrvFreeVDMInfo (lpVDMInfo);
|
|
// BUGBUG -- fixed
|
|
|
|
if (bWowApp) {
|
|
pWOWRecord->lpVDMInfo = NULL;
|
|
LEAVE_WOW_CRITICAL();
|
|
}
|
|
else {
|
|
pDOSRecord->lpVDMInfo = NULL;
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
} // END of GetNextVdmCommand
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
BaseSrvExitVDM(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
PBASE_EXIT_VDM_MSG b = (PBASE_EXIT_VDM_MSG)&m->u.ApiMessageData;
|
|
NTSTATUS Status;
|
|
ULONG SequenceNumber;
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber))) {
|
|
return (ULONG)STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (b->iWowTask) {
|
|
Status = BaseSrvExitWOWTask(b, SequenceNumber);
|
|
}
|
|
else {
|
|
Status = BaseSrvExitDOSTask(b, SequenceNumber);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
ULONG
|
|
BaseSrvIsFirstVDM(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
PBASE_IS_FIRST_VDM_MSG c = (PBASE_IS_FIRST_VDM_MSG)&m->u.ApiMessageData;
|
|
|
|
if(!NT_SUCCESS(BaseSrvIsClientVdm(m->h.ClientId.UniqueProcess))) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
c->FirstVDM = fIsFirstVDM;
|
|
if(fIsFirstVDM)
|
|
fIsFirstVDM = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// This call should only be used for DOS apps and not for wow apps
|
|
// hence we don't remove ConsoleHandle == -1 condition here as it is
|
|
// only a validation check
|
|
//
|
|
//
|
|
|
|
ULONG
|
|
BaseSrvSetVDMCurDirs(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PBASE_GET_SET_VDM_CUR_DIRS_MSG b = (PBASE_GET_SET_VDM_CUR_DIRS_MSG)&m->u.ApiMessageData;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
ULONG SequenceNumber;
|
|
|
|
if (b->ConsoleHandle == (HANDLE) -1) {
|
|
return (ULONG) STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->lpszzCurDirs, b->cchCurDirs, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber))) {
|
|
return (ULONG)STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if (!NT_SUCCESS (Status) || SequenceNumber != pConsoleRecord->SequenceNumber) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
if (pConsoleRecord->lpszzCurDirs) {
|
|
RtlFreeHeap(BaseSrvHeap, 0, pConsoleRecord->lpszzCurDirs);
|
|
pConsoleRecord->lpszzCurDirs = NULL;
|
|
pConsoleRecord->cchCurDirs = 0;
|
|
}
|
|
if (b->cchCurDirs && b->lpszzCurDirs) {
|
|
pConsoleRecord->lpszzCurDirs = RtlAllocateHeap(
|
|
BaseSrvHeap,
|
|
MAKE_TAG( VDM_TAG ),
|
|
b->cchCurDirs
|
|
);
|
|
|
|
if (pConsoleRecord->lpszzCurDirs == NULL) {
|
|
pConsoleRecord->cchCurDirs = 0;
|
|
RtlLeaveCriticalSection(&BaseSrvDOSCriticalSection);
|
|
return (ULONG)STATUS_NO_MEMORY;
|
|
}
|
|
RtlMoveMemory(pConsoleRecord->lpszzCurDirs,
|
|
b->lpszzCurDirs,
|
|
b->cchCurDirs
|
|
);
|
|
|
|
pConsoleRecord->cchCurDirs = b->cchCurDirs;
|
|
RtlLeaveCriticalSection(&BaseSrvDOSCriticalSection);
|
|
return (ULONG) STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&BaseSrvDOSCriticalSection);
|
|
return (ULONG) STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
ULONG
|
|
BaseSrvBatNotification(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PBATRECORD pBatRecord;
|
|
PBASE_BAT_NOTIFICATION_MSG b = (PBASE_BAT_NOTIFICATION_MSG)&m->u.ApiMessageData;
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// If BATRECORD does'nt exist for this console, create one only if
|
|
// bat file execution is beginig i.e. fBeginEnd is TRUE.
|
|
|
|
if ((pBatRecord = BaseSrvGetBatRecord(b->ConsoleHandle)) == NULL) {
|
|
if (!(b->fBeginEnd == CMD_BAT_OPERATION_STARTING &&
|
|
(pBatRecord = BaseSrvAllocateAndAddBatRecord (b->ConsoleHandle)))) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else if (b->fBeginEnd == CMD_BAT_OPERATION_TERMINATING)
|
|
BaseSrvFreeAndRemoveBatRecord (pBatRecord);
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
return ((ULONG)STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
BaseSrvRegisterWowExec(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
PBASE_REGISTER_WOWEXEC_MSG b = (PBASE_REGISTER_WOWEXEC_MSG)&m->u.ApiMessageData;
|
|
UNICODE_STRING ModuleNameString_U;
|
|
PVOID ModuleHandle;
|
|
STRING ProcedureNameString;
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS Process;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
ULONG SequenceNumber;
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
//
|
|
// Do a run-time link to PostMessageA and GetWindowThreadProcessId
|
|
// which we'll use to post messages to WowExec.
|
|
//
|
|
|
|
if (NULL == BaseSrvPostMessageA) {
|
|
// this is an impossible event as all the imports are inited at once
|
|
KdPrint(("BaseSrvRegisterWowExec: Api PostMessage is not available to BaseSrv\n"));
|
|
ASSERT(FALSE);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = BaseSrvFindSharedWowRecordByConsoleHandle(&gWowHead,
|
|
b->ConsoleHandle,
|
|
&pSharedWow);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("BaseSrvRegisterWowExec: Could not find record for wow console handle 0x%lx\n", b->ConsoleHandle));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber)) ||
|
|
SequenceNumber != pSharedWow->SequenceNumber) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
KdPrint(("BaseSrvRegisterWowExec: SequenceNumber didn't match\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(NULL != pSharedWow);
|
|
|
|
// see what the window handle is -- special "die wow, die" case
|
|
if (NULL == b->hwndWowExec) {
|
|
//
|
|
// Shared WOW is calling to de-register itself as part of shutdown.
|
|
// Protocol is we check for pending commands for this shared WOW,
|
|
// if there are any we fail this call, otherwise we set our
|
|
// hwndWowExec to NULL and succeed the call, ensuring no more
|
|
// commands will be added to this queue.
|
|
//
|
|
if (NULL != pSharedWow->pWOWRecord) {
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else { // no tasks for this wow
|
|
// it goes KABOOOOOOM!!!!!
|
|
|
|
Status = BaseSrvDeleteSharedWowRecord(&gWowHead, pSharedWow);
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSharedWow->hwndWowExec) {
|
|
// Shared WOW windows was already registered
|
|
// someone else is trying to overwrite it, so don't allow it
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
// set the window handle
|
|
pSharedWow->hwndWowExec = b->hwndWowExec;
|
|
|
|
// rettrieve thread and process id of the calling process
|
|
pSharedWow->dwWowExecThreadId = BaseSrvGetWindowThreadProcessId(
|
|
pSharedWow->hwndWowExec,
|
|
&pSharedWow->dwWowExecProcessId);
|
|
|
|
Cleanup:
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
return (ULONG)Status;
|
|
}
|
|
|
|
PBATRECORD
|
|
BaseSrvGetBatRecord(
|
|
IN HANDLE hConsole
|
|
)
|
|
{
|
|
PBATRECORD pBatRecord = BatRecordHead;
|
|
while (pBatRecord && pBatRecord->hConsole != hConsole)
|
|
pBatRecord = pBatRecord->BatRecordNext;
|
|
return pBatRecord;
|
|
}
|
|
|
|
PBATRECORD
|
|
BaseSrvAllocateAndAddBatRecord(
|
|
HANDLE hConsole
|
|
)
|
|
{
|
|
PCSR_THREAD t;
|
|
PBATRECORD pBatRecord;
|
|
|
|
if((pBatRecord = RtlAllocateHeap(RtlProcessHeap (),
|
|
MAKE_TAG( VDM_TAG ),
|
|
sizeof(BATRECORD))) == NULL)
|
|
return NULL;
|
|
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
pBatRecord->hConsole = hConsole;
|
|
pBatRecord->SequenceNumber = t->Process->SequenceNumber;
|
|
pBatRecord->BatRecordNext = BatRecordHead;
|
|
BatRecordHead = pBatRecord;
|
|
return pBatRecord;
|
|
}
|
|
|
|
VOID
|
|
BaseSrvFreeAndRemoveBatRecord(
|
|
PBATRECORD pBatRecordToFree
|
|
)
|
|
{
|
|
PBATRECORD pBatRecord = BatRecordHead;
|
|
PBATRECORD pBatRecordLast = NULL;
|
|
|
|
while (pBatRecord && pBatRecord != pBatRecordToFree){
|
|
pBatRecordLast = pBatRecord;
|
|
pBatRecord = pBatRecord->BatRecordNext;
|
|
}
|
|
|
|
if (pBatRecord == NULL)
|
|
return;
|
|
|
|
if (pBatRecordLast)
|
|
pBatRecordLast->BatRecordNext = pBatRecord->BatRecordNext;
|
|
else
|
|
BatRecordHead = pBatRecord->BatRecordNext;
|
|
|
|
RtlFreeHeap ( RtlProcessHeap (), 0, pBatRecord);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
BaseSrvGetVDMCurDirs(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PBASE_GET_SET_VDM_CUR_DIRS_MSG b = (PBASE_GET_SET_VDM_CUR_DIRS_MSG)&m->u.ApiMessageData;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
|
|
if (!CsrValidateMessageBuffer(m, &b->lpszzCurDirs, b->cchCurDirs, sizeof(BYTE))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
if (!NT_SUCCESS (Status)) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
b->cchCurDirs = 0;
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
if (pConsoleRecord->lpszzCurDirs != NULL){
|
|
if (b->cchCurDirs < pConsoleRecord->cchCurDirs || b->lpszzCurDirs == NULL)
|
|
{
|
|
b->cchCurDirs = pConsoleRecord->cchCurDirs;
|
|
RtlLeaveCriticalSection(&BaseSrvDOSCriticalSection);
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
else {
|
|
RtlMoveMemory(b->lpszzCurDirs,
|
|
pConsoleRecord->lpszzCurDirs,
|
|
pConsoleRecord->cchCurDirs
|
|
);
|
|
// remove it immediately after the copy. This is done because
|
|
// the next command may be a WOW program(got tagged process handle
|
|
// as VDM command) and in that case we will return incorrect
|
|
//information:
|
|
// c:\>
|
|
// c:\>d:
|
|
// d:\>cd \foo
|
|
// d:\foo>dosapp
|
|
// d:\foo>c:
|
|
// c:\>wowapp
|
|
// d:\foo> -- this is wrong if we don't do the following stuff.
|
|
RtlFreeHeap(BaseSrvHeap, 0, pConsoleRecord->lpszzCurDirs);
|
|
pConsoleRecord->lpszzCurDirs = NULL;
|
|
b->cchCurDirs = pConsoleRecord->cchCurDirs;
|
|
pConsoleRecord->cchCurDirs = 0;
|
|
}
|
|
}
|
|
else {
|
|
b->cchCurDirs = 0;
|
|
}
|
|
RtlLeaveCriticalSection(&BaseSrvDOSCriticalSection);
|
|
return ((ULONG)STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// temporary static desktop name buffer
|
|
// BUGBUG -- change when User gives me better return values
|
|
//
|
|
WCHAR wszDesktopName[MAX_PATH];
|
|
|
|
//
|
|
// This call produces a desktop name and optionally a shared wow running in the context
|
|
// of this particular desktop.
|
|
// extra bad: making conversion Uni->Ansi in client/vdm.c and ansi->Uni here
|
|
// this is BUGBUG -- look into it later
|
|
//
|
|
// this function returns success in all the cases (including when wow is not found)
|
|
// and fails only if under-layers return failures
|
|
|
|
NTSTATUS
|
|
BaseSrvFindSharedWow(
|
|
IN PBASE_CHECKVDM_MSG b,
|
|
IN HANDLE UniqueProcessClientId,
|
|
IN OUT PUNICODE_STRING pDesktopName,
|
|
IN OUT PSHAREDWOWRECORD* ppSharedWowRecord)
|
|
|
|
{
|
|
ANSI_STRING DesktopNameAnsi;
|
|
BOOLEAN fRevertToSelf;
|
|
NTSTATUS Status;
|
|
|
|
// the first time out, we have not dyna-linked NtUserResolveDesktopForWow, so
|
|
// as an optimization, check to see if the list of shared wows is empty
|
|
// see if we need to dyna-link
|
|
if (NULL == BaseSrvUserResolveDesktopForWow) {
|
|
Status = BaseSrvImportApis(rgBaseSrvModuleImport,
|
|
sizeof(rgBaseSrvModuleImport)/sizeof(rgBaseSrvModuleImport[0]));
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("BaseSrvFindSharedWow: Failed to dyna-link apis\n"));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (b->DesktopLen == 0) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ASSERT(NULL != BaseSrvUserResolveDesktopForWow);
|
|
|
|
pDesktopName->Buffer = wszDesktopName;
|
|
pDesktopName->MaximumLength = sizeof(wszDesktopName);
|
|
|
|
DesktopNameAnsi.Buffer = b->Desktop;
|
|
DesktopNameAnsi.Length = (USHORT)(b->DesktopLen - 1);
|
|
DesktopNameAnsi.MaximumLength = (USHORT)(b->DesktopLen);
|
|
|
|
RtlAnsiStringToUnicodeString(pDesktopName, &DesktopNameAnsi, FALSE);
|
|
|
|
// now get the real desktop name there
|
|
// impersonate
|
|
fRevertToSelf = CsrImpersonateClient(NULL);
|
|
if (!fRevertToSelf) {
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
|
|
Status = BaseSrvUserResolveDesktopForWow(pDesktopName);
|
|
|
|
|
|
CsrRevertToSelf();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
// show that desktop is not valid name here by invalidating the pointer to buffer
|
|
pDesktopName->Buffer = NULL;
|
|
pDesktopName->MaximumLength = 0;
|
|
pDesktopName->Length = 0;
|
|
return Status;
|
|
}
|
|
|
|
|
|
// now look for this dektop in our task list
|
|
|
|
Status = BaseSrvFindSharedWowRecordByDesktop(&gWowHead,
|
|
pDesktopName,
|
|
ppSharedWowRecord);
|
|
if (!NT_SUCCESS(Status)) {
|
|
*ppSharedWowRecord = NULL;
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
BaseSrvCheckWOW( //////////////////////////////////// NEW IMP
|
|
IN PBASE_CHECKVDM_MSG b,
|
|
IN HANDLE UniqueProcessClientId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle,TargetHandle;
|
|
PWOWRECORD pWOWRecord;
|
|
INFORECORD InfoRecord;
|
|
USHORT Len;
|
|
LUID ClientAuthId;
|
|
DWORD dwThreadId, dwProcessId;
|
|
PCSR_PROCESS Process;
|
|
PSHAREDWOWRECORD pSharedWow = NULL;
|
|
PSHAREDWOWRECORD pSharedWowPrev;
|
|
UNICODE_STRING DesktopName;
|
|
PCSR_PROCESS ParentProcess;
|
|
HANDLE WOWUserToken = NULL;
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
// see if what we have in startup info matches any of the existing wow vdms
|
|
DesktopName.Buffer = NULL;
|
|
|
|
Status = BaseSrvFindSharedWow(b,
|
|
UniqueProcessClientId,
|
|
&DesktopName,
|
|
&pSharedWow);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(FALSE); // this is some sort of a system error
|
|
b->DesktopLen = 0; // indicate desktop access was denied/not existing
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// here we could either have succeeded and have a shared wow or not -
|
|
// and hence have a desktop name in a global buffer pointed to by DesktopName.Buffer
|
|
//
|
|
|
|
if (NULL != pSharedWow) {
|
|
BOOLEAN fEqual;
|
|
|
|
switch(pSharedWow->VDMState & VDM_READY) {
|
|
|
|
case VDM_READY:
|
|
// meaning: vdm ready to take a command
|
|
// verify if the currently logged-on interactive user will be able to take out task
|
|
//
|
|
Status = OkToRunInSharedWOW( UniqueProcessClientId,
|
|
&ClientAuthId,
|
|
&WOWUserToken
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (!RtlEqualLuid(&ClientAuthId, &pSharedWow->WowAuthId)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
else if (b->UserLuid && !RtlEqualLuid(&ClientAuthId, b->UserLuid)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
else if(!NT_SUCCESS(NtCompareTokens(WOWUserToken,pSharedWow->WOWUserToken,&fEqual) || !fEqual)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
if(WOWUserToken) {
|
|
NtClose(WOWUserToken);
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
LEAVE_WOW_CRITICAL();
|
|
return ((ULONG)Status);
|
|
}
|
|
|
|
|
|
// now we have verified that user 1) has access to this desktop
|
|
// 2) is a currently logged-on interactive user
|
|
|
|
|
|
// Allocate a record for this wow task
|
|
if (NULL == (pWOWRecord = BaseSrvAllocateWOWRecord(&gWowHead))) {
|
|
Status = STATUS_NO_MEMORY;
|
|
break; // failed with mem alloc -- still holding task critical
|
|
}
|
|
|
|
// copy the command parameters
|
|
|
|
InfoRecord.iTag = BINARY_TYPE_WIN16;
|
|
InfoRecord.pRecord.pWOWRecord = pWOWRecord;
|
|
|
|
if (BaseSrvCopyCommand (b,&InfoRecord) == FALSE){
|
|
BaseSrvFreeWOWRecord(pWOWRecord);
|
|
Status = STATUS_NO_MEMORY;
|
|
break; // holding task critical
|
|
}
|
|
|
|
// create pseudo handles
|
|
|
|
Status = BaseSrvCreatePairWaitHandles (&Handle,&TargetHandle);
|
|
|
|
if (!NT_SUCCESS(Status) ){
|
|
BaseSrvFreeWOWRecord(pWOWRecord);
|
|
break;
|
|
}
|
|
else {
|
|
pWOWRecord->hWaitForParent = Handle;
|
|
pWOWRecord->hWaitForParentServer = TargetHandle;
|
|
b->WaitObjectForParent = TargetHandle; // give the handle back to the client
|
|
}
|
|
|
|
// set the state and task id, task id is allocated in BaseSrvAllocateWowRecord
|
|
|
|
b->VDMState = VDM_PRESENT_AND_READY;
|
|
b->iTask = pWOWRecord->iTask;
|
|
|
|
// add wow record to this shared wow list
|
|
|
|
BaseSrvAddWOWRecord (pSharedWow, pWOWRecord);
|
|
|
|
// let User know we have been started
|
|
|
|
if (NULL != UserNotifyProcessCreate) {
|
|
(*UserNotifyProcessCreate)(pWOWRecord->iTask,
|
|
(DWORD)((ULONG_PTR)CSR_SERVER_QUERYCLIENTTHREAD()->ClientId.UniqueThread),
|
|
(DWORD)((ULONG_PTR)TargetHandle),
|
|
0x04);
|
|
}
|
|
|
|
// see if the wowexec window exists and is valid
|
|
|
|
if (NULL != pSharedWow->hwndWowExec) {
|
|
|
|
//
|
|
// Check to see if hwndWowExec still belongs to
|
|
// the same thread/process ID before posting.
|
|
//
|
|
|
|
// BUGBUG -- debug code here -- not really needed
|
|
|
|
dwThreadId = BaseSrvGetWindowThreadProcessId(pSharedWow->hwndWowExec,
|
|
&dwProcessId);
|
|
|
|
if (dwThreadId) {
|
|
ULONG SequenceNumber;
|
|
Status = BaseSrvGetVdmSequence((HANDLE)LongToPtr(dwProcessId), &SequenceNumber);
|
|
if (!NT_SUCCESS(Status) || SequenceNumber != pSharedWow->SequenceNumber) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
KdPrint(("BaseSrvCheckWOW: Not authentic wow by process seq number\n"));
|
|
//
|
|
// Spurious assert was here. The wow process has died while the message was incoming.
|
|
// The code below will cleanup appropriately
|
|
//
|
|
}
|
|
|
|
if (dwThreadId == pSharedWow->dwWowExecThreadId &&
|
|
dwProcessId == pSharedWow->dwWowExecProcessId &&
|
|
NT_SUCCESS(Status)) {
|
|
|
|
HANDLE ThreadId;
|
|
|
|
/*
|
|
* Set the csr thread's desktop temporarily to the client
|
|
* it is servicing
|
|
*/
|
|
|
|
|
|
BaseSrvPostMessageA((HWND)pSharedWow->hwndWowExec,
|
|
WM_WOWEXECSTARTAPP,
|
|
0,
|
|
0);
|
|
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Thread/process IDs don't match, so forget about this shared WOW.
|
|
//
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
KdPrint(("BaseSrvCheckWOW: Thread/Process IDs don't match, shared WOW is gone.\n"
|
|
"Saved PID 0x%x TID 0x%x hwndWowExec (0x%x) maps to \n"
|
|
" PID 0x%x TID 0x%x\n",
|
|
pSharedWow->dwWowExecProcessId,
|
|
pSharedWow->dwWowExecThreadId,
|
|
pSharedWow->hwndWowExec,
|
|
dwProcessId,
|
|
dwThreadId));
|
|
}
|
|
|
|
// okay, panic! our internal list is in fact corrupted - remove the offending
|
|
// entry
|
|
|
|
|
|
// to do this we must have access -- relinquish control
|
|
// and then re-aquire it
|
|
|
|
BaseSrvDeleteSharedWowRecord(&gWowHead, pSharedWow);
|
|
|
|
// reset these values so that new shared wow is created
|
|
pSharedWow = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ELSE
|
|
// if pSharedWow->hwndWowExec == NULL our shared wow doesn't have a window yet.
|
|
// however, it is ok to attach new wowrecord because we know shared wow's ntvdm is
|
|
// still alive. if it wasn't it would have been cleaned up by the process clean-up
|
|
// routine BaseSrvCleanupVDMResources.
|
|
|
|
|
|
|
|
break; // case VDM_READY
|
|
|
|
|
|
default:
|
|
KdPrint(("BaseSrvCheckWOW: bad vdm state: 0x%lx\n", pSharedWow->VDMState));
|
|
ASSERT(FALSE); // how did I get into this mess ?
|
|
break;
|
|
} // end switch
|
|
}
|
|
|
|
|
|
// if we are here then either :
|
|
// -- the first take on a shared wow failed
|
|
// -- the app was successfully handed off to wowexec for execution
|
|
// if pSharedWow is NULL, then we have to start shared wow in this environment
|
|
// as it was either not present or nuked due to seq number/id conflics
|
|
//
|
|
|
|
if (NULL == pSharedWow) {
|
|
|
|
//
|
|
// this check verifies command line for not being too long
|
|
//
|
|
if (b->CmdLen > MAXIMUM_VDM_COMMAND_LENGTH) {
|
|
LEAVE_WOW_CRITICAL();
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Only the currently logged on interactive user can start the
|
|
// shared wow. Verify if the caller is such, and if it is
|
|
// store the Authentication Id of the client which identifies who
|
|
// is allowed to run wow apps in the default ntvdm-wow process.
|
|
//
|
|
|
|
//
|
|
// if needed, do a run-time link to UserTestTokenForInteractive,
|
|
// which is used to verify the client luid.
|
|
//
|
|
|
|
// this dynalink is performed automatically using our all-out api
|
|
// see above for dynalink source
|
|
|
|
ASSERT (NULL != UserTestTokenForInteractive);
|
|
|
|
|
|
ASSERT (NULL != DesktopName.Buffer); // yes, it should be valid
|
|
|
|
//
|
|
// see if we had desktop there. if not (the first time around!) - get it now
|
|
// by calling FindSharedWow (which retrieves desktop as well)
|
|
//
|
|
|
|
//
|
|
// If the caller isn't the currently logged on interactive user,
|
|
// OkToRunInSharedWOW will fail with access denied.
|
|
|
|
Status = OkToRunInSharedWOW(UniqueProcessClientId,
|
|
&ClientAuthId,
|
|
&WOWUserToken);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (b->UserLuid && !RtlEqualLuid(&ClientAuthId, b->UserLuid)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if(WOWUserToken) {
|
|
NtClose(WOWUserToken);
|
|
}
|
|
LEAVE_WOW_CRITICAL();
|
|
return ((ULONG)Status);
|
|
}
|
|
|
|
//
|
|
// Store the Autherntication Id since this now is the currently
|
|
// logged on interactive user.
|
|
//
|
|
|
|
// produce a viable shared wow record
|
|
// this process consists of 2 parts : producing a wow record and producing a
|
|
// console record. the reason for this is to be able to identify this record
|
|
// when the wow process had been created and is calling to update a record with it's
|
|
// own handle (this is twise confusing, but just bear with me for a while).
|
|
//
|
|
// just as a dos program, we might need a temporary session id or a console id for the
|
|
// creating process.
|
|
|
|
|
|
pSharedWow = BaseSrvAllocateSharedWowRecord(&DesktopName);
|
|
if (NULL == pSharedWow) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Store the WOWUserToken since this gives better granularity then just user id
|
|
//
|
|
pSharedWow->WOWUserToken = WOWUserToken;
|
|
|
|
//
|
|
// Store parent process sequence number until ntvdm
|
|
// comes and gives its sequence number
|
|
//
|
|
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientId,
|
|
&ParentProcess);
|
|
if (NT_SUCCESS(Status)) {
|
|
pSharedWow->ParentSequenceNumber = ParentProcess->SequenceNumber;
|
|
CsrUnlockProcess(ParentProcess);
|
|
}
|
|
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
pSharedWow->pWOWRecord = BaseSrvAllocateWOWRecord(&gWowHead); // this is a new shared wow
|
|
if (NULL == pSharedWow->pWOWRecord) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// here we have [successfully] allocated shared struct and a console record
|
|
// and a wow record for the task
|
|
|
|
// copy the command parameters
|
|
|
|
InfoRecord.iTag = BINARY_TYPE_WIN16;
|
|
InfoRecord.pRecord.pWOWRecord = pSharedWow->pWOWRecord;
|
|
|
|
if(!BaseSrvCopyCommand (b, &InfoRecord)) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
#if 0
|
|
pSharedWow->WowSessionId = BaseSrvGetWOWTaskId(&gWowHead); // wow task id
|
|
#endif
|
|
// store the retrieved auth id
|
|
pSharedWow->WowAuthId = ClientAuthId;
|
|
|
|
// link shared wow to the console...
|
|
// set wow state to be ready
|
|
pSharedWow->VDMState = VDM_READY;
|
|
|
|
b->VDMState = VDM_NOT_PRESENT;
|
|
b->iTask = pSharedWow->pWOWRecord->iTask;
|
|
|
|
// now add this shared wow in --
|
|
BaseSrvAddSharedWowRecord(&gWowHead, pSharedWow);
|
|
|
|
}
|
|
else {
|
|
|
|
// this has not succeeded. cleanup
|
|
if (NULL != pSharedWow) {
|
|
BaseSrvFreeSharedWowRecord(pSharedWow);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
return (ULONG)Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
OkToRunInSharedWOW(
|
|
IN HANDLE UniqueProcessClientId,
|
|
OUT PLUID pAuthenticationId,
|
|
OUT PHANDLE pWOWUserToken
|
|
)
|
|
/*
|
|
* Verifies that the client thread is in the currently logged on interactive
|
|
* user session or is SYSTEM impersonating a thread in the currently logged
|
|
* on interactive session.
|
|
*
|
|
* Also retrieves the the authentication ID (logon session Id) for the
|
|
* caller.
|
|
*
|
|
* if the clients TokenGroups is not part of the currently logged on
|
|
* interactive user session STATUS_ACCESS_DENIED is returned.
|
|
*
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS Process;
|
|
PCSR_THREAD t;
|
|
BOOL fRevertToSelf;
|
|
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientId,&Process);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
//
|
|
// Open a token for the client
|
|
//
|
|
Status = NtOpenProcessToken(Process->ProcessHandle,
|
|
TOKEN_QUERY,
|
|
pWOWUserToken
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
CsrUnlockProcess(Process);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Verify the token Group, and see if client's token is the currently
|
|
// logged on interactive user. If this fails and it is System
|
|
// impersonating, then check if the client being impersonated is the
|
|
// currently logged on interactive user.
|
|
//
|
|
|
|
Status = (*UserTestTokenForInteractive)(*pWOWUserToken, pAuthenticationId);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (IsClientSystem(*pWOWUserToken)) {
|
|
NtClose(*pWOWUserToken);
|
|
*pWOWUserToken = NULL;
|
|
|
|
// get impersonation token
|
|
|
|
fRevertToSelf = CsrImpersonateClient(NULL);
|
|
if(!fRevertToSelf) {
|
|
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
else {
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
Status = NtOpenThreadToken(t->ThreadHandle,
|
|
TOKEN_QUERY,
|
|
TRUE,
|
|
pWOWUserToken);
|
|
|
|
CsrRevertToSelf();
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = (*UserTestTokenForInteractive)(*pWOWUserToken,
|
|
pAuthenticationId);
|
|
}
|
|
else {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CsrUnlockProcess(Process);
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvGetUserToken(
|
|
IN HANDLE UniqueProcessClientId,
|
|
OUT PHANDLE pUserToken
|
|
)
|
|
/*
|
|
*
|
|
* retrieves the the authentication ID (logon session Id) for the
|
|
* caller.
|
|
*
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS Process;
|
|
PCSR_THREAD tCsr;
|
|
BOOL fRevertToSelf;
|
|
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientId,&Process);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
fRevertToSelf = CsrImpersonateClient(NULL);
|
|
|
|
if (!fRevertToSelf) {
|
|
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
else {
|
|
tCsr = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
Status = NtOpenThreadToken(tCsr->ThreadHandle,
|
|
TOKEN_QUERY,
|
|
TRUE,
|
|
pUserToken);
|
|
CsrRevertToSelf();
|
|
}
|
|
|
|
if(Status == STATUS_NO_TOKEN) {
|
|
//
|
|
// Open a token for the client
|
|
//
|
|
Status = NtOpenProcessToken(Process->ProcessHandle,
|
|
TOKEN_QUERY,
|
|
pUserToken
|
|
);
|
|
}
|
|
|
|
CsrUnlockProcess(Process);
|
|
return(Status);
|
|
}
|
|
|
|
ULONG
|
|
BaseSrvCheckDOS(
|
|
IN PBASE_CHECKVDM_MSG b,
|
|
IN HANDLE UniqueProcessClientId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLERECORD pConsoleRecord = NULL;
|
|
HANDLE Handle,TargetHandle;
|
|
PDOSRECORD pDOSRecord;
|
|
INFORECORD InfoRecord;
|
|
PCSR_PROCESS ParentProcess;
|
|
HANDLE UserToken;
|
|
BOOLEAN fEqual;
|
|
|
|
if (!NT_SUCCESS(BaseSrvGetUserToken(UniqueProcessClientId,&UserToken))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
Status = NtCompareTokens(UserToken,pConsoleRecord->DosUserToken,&fEqual);
|
|
NtClose(UserToken);
|
|
|
|
if(!NT_SUCCESS(Status) || !fEqual) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
|
|
ASSERT (pDOSRecord != NULL);
|
|
|
|
switch( pDOSRecord->VDMState){
|
|
|
|
case VDM_READY:
|
|
case VDM_HAS_RETURNED_ERROR_CODE:
|
|
|
|
InfoRecord.iTag = BINARY_TYPE_DOS;
|
|
InfoRecord.pRecord.pDOSRecord = pDOSRecord;
|
|
|
|
if(!BaseSrvCopyCommand (b,&InfoRecord)) {
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS ( Status = BaseSrvDupStandardHandles (
|
|
pConsoleRecord->hVDM,
|
|
pDOSRecord)))
|
|
|
|
break;
|
|
|
|
Status = BaseSrvCreatePairWaitHandles (&Handle,&TargetHandle);
|
|
|
|
if (!NT_SUCCESS(Status) ){
|
|
BaseSrvCloseStandardHandles (pConsoleRecord->hVDM, pDOSRecord);
|
|
break;
|
|
}
|
|
else {
|
|
b->WaitObjectForParent = TargetHandle;
|
|
pDOSRecord->hWaitForParent = TargetHandle;
|
|
pDOSRecord->hWaitForParentDup = Handle;
|
|
}
|
|
|
|
pDOSRecord->VDMState = VDM_TO_TAKE_A_COMMAND;
|
|
|
|
b->VDMState = VDM_PRESENT_AND_READY;
|
|
|
|
if(pConsoleRecord->hWaitForVDMDup)
|
|
NtSetEvent (pConsoleRecord->hWaitForVDMDup,NULL);
|
|
|
|
break;
|
|
|
|
case VDM_BUSY:
|
|
case VDM_TO_TAKE_A_COMMAND:
|
|
|
|
if((pDOSRecord = BaseSrvAllocateDOSRecord()) == NULL){
|
|
Status = STATUS_NO_MEMORY ;
|
|
break;
|
|
}
|
|
|
|
InfoRecord.iTag = BINARY_TYPE_DOS;
|
|
InfoRecord.pRecord.pDOSRecord = pDOSRecord;
|
|
|
|
if(!BaseSrvCopyCommand(b, &InfoRecord)){
|
|
Status = STATUS_NO_MEMORY ;
|
|
BaseSrvFreeDOSRecord(pDOSRecord);
|
|
break;
|
|
}
|
|
|
|
Status = BaseSrvCreatePairWaitHandles(&Handle,&TargetHandle);
|
|
if (!NT_SUCCESS(Status) ){
|
|
BaseSrvFreeDOSRecord(pDOSRecord);
|
|
break;
|
|
}
|
|
else {
|
|
b->WaitObjectForParent = TargetHandle;
|
|
pDOSRecord->hWaitForParentDup = Handle;
|
|
pDOSRecord->hWaitForParent = TargetHandle;
|
|
}
|
|
|
|
|
|
Status = BaseSrvDupStandardHandles(pConsoleRecord->hVDM, pDOSRecord);
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSrvClosePairWaitHandles (pDOSRecord);
|
|
BaseSrvFreeDOSRecord(pDOSRecord);
|
|
break;
|
|
}
|
|
|
|
BaseSrvAddDOSRecord(pConsoleRecord,pDOSRecord);
|
|
b->VDMState = VDM_PRESENT_AND_READY;
|
|
if (pConsoleRecord->nReEntrancy) {
|
|
if(pConsoleRecord->hWaitForVDMDup)
|
|
NtSetEvent (pConsoleRecord->hWaitForVDMDup,NULL);
|
|
}
|
|
pDOSRecord->VDMState = VDM_TO_TAKE_A_COMMAND;
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
if (pConsoleRecord == NULL) {
|
|
|
|
pConsoleRecord = BaseSrvAllocateConsoleRecord ();
|
|
|
|
if (pConsoleRecord == NULL)
|
|
Status = STATUS_NO_MEMORY ;
|
|
|
|
else {
|
|
|
|
pConsoleRecord->DOSRecord = BaseSrvAllocateDOSRecord();
|
|
if(!pConsoleRecord->DOSRecord) {
|
|
BaseSrvFreeConsoleRecord(pConsoleRecord);
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return (ULONG)STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientId,
|
|
&ParentProcess);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSrvFreeConsoleRecord(pConsoleRecord);
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return Status;
|
|
}
|
|
|
|
pConsoleRecord->ParentSequenceNumber = ParentProcess->SequenceNumber;
|
|
CsrUnlockProcess(ParentProcess);
|
|
pConsoleRecord->DosUserToken = UserToken;
|
|
|
|
InfoRecord.iTag = b->BinaryType;
|
|
InfoRecord.pRecord.pDOSRecord = pConsoleRecord->DOSRecord;
|
|
|
|
|
|
if(!BaseSrvCopyCommand(b, &InfoRecord)) {
|
|
BaseSrvFreeConsoleRecord(pConsoleRecord);
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return (ULONG)STATUS_NO_MEMORY;
|
|
}
|
|
|
|
|
|
pConsoleRecord->hConsole = b->ConsoleHandle;
|
|
|
|
|
|
// if no console for this ntvdm
|
|
// get a temporary session ID and pass it to the client
|
|
if (!pConsoleRecord->hConsole) {
|
|
b->iTask = pConsoleRecord->DosSesId = GetNextDosSesId();
|
|
}
|
|
else {
|
|
b->iTask = pConsoleRecord->DosSesId = 0;
|
|
}
|
|
|
|
pConsoleRecord->DOSRecord->VDMState = VDM_TO_TAKE_A_COMMAND;
|
|
|
|
BaseSrvAddConsoleRecord(pConsoleRecord);
|
|
b->VDMState = VDM_NOT_PRESENT;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BaseSrvCopyCommand(
|
|
PBASE_CHECKVDM_MSG b,
|
|
PINFORECORD pInfoRecord
|
|
)
|
|
{
|
|
PVDMINFO VDMInfo;
|
|
|
|
if((VDMInfo = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),sizeof(VDMINFO))) == NULL){
|
|
return FALSE;
|
|
}
|
|
|
|
VDMInfo->CmdLine = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->CmdLen);
|
|
|
|
if (b->AppLen) {
|
|
VDMInfo->AppName = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->AppLen);
|
|
}
|
|
else
|
|
VDMInfo->AppName = NULL;
|
|
|
|
if (b->PifLen)
|
|
VDMInfo->PifFile = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->PifLen);
|
|
else
|
|
VDMInfo->PifFile = NULL;
|
|
|
|
if (b->CurDirectoryLen)
|
|
VDMInfo->CurDirectory = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->CurDirectoryLen);
|
|
else
|
|
VDMInfo->CurDirectory = NULL;
|
|
|
|
if (b->EnvLen)
|
|
VDMInfo->Enviornment = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->EnvLen);
|
|
else
|
|
VDMInfo->Enviornment = NULL;
|
|
|
|
if (b->DesktopLen)
|
|
VDMInfo->Desktop = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->DesktopLen);
|
|
else
|
|
VDMInfo->Desktop = NULL;
|
|
|
|
if (b->TitleLen)
|
|
VDMInfo->Title = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->TitleLen);
|
|
else
|
|
VDMInfo->Title = NULL;
|
|
|
|
if (b->ReservedLen)
|
|
VDMInfo->Reserved = RtlAllocateHeap(RtlProcessHeap (), MAKE_TAG( VDM_TAG ),b->ReservedLen);
|
|
else
|
|
VDMInfo->Reserved = NULL;
|
|
|
|
// check that all the allocations were successful
|
|
if (VDMInfo->CmdLine == NULL ||
|
|
(b->AppLen && VDMInfo->AppName == NULL) ||
|
|
(b->PifLen && VDMInfo->PifFile == NULL) ||
|
|
(b->CurDirectoryLen && VDMInfo->CurDirectory == NULL) ||
|
|
(b->EnvLen && VDMInfo->Enviornment == NULL) ||
|
|
(b->DesktopLen && VDMInfo->Desktop == NULL )||
|
|
(b->ReservedLen && VDMInfo->Reserved == NULL )||
|
|
(b->TitleLen && VDMInfo->Title == NULL)) {
|
|
|
|
BaseSrvFreeVDMInfo(VDMInfo);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
RtlMoveMemory(VDMInfo->CmdLine,
|
|
b->CmdLine,
|
|
b->CmdLen);
|
|
|
|
VDMInfo->CmdSize = b->CmdLen;
|
|
|
|
|
|
if (b->AppLen) {
|
|
RtlMoveMemory(VDMInfo->AppName,
|
|
b->AppName,
|
|
b->AppLen);
|
|
}
|
|
|
|
VDMInfo->AppLen = b->AppLen;
|
|
|
|
if (b->PifLen) {
|
|
RtlMoveMemory(VDMInfo->PifFile,
|
|
b->PifFile,
|
|
b->PifLen);
|
|
}
|
|
|
|
VDMInfo->PifLen = b->PifLen;
|
|
|
|
if (b->CurDirectoryLen) {
|
|
RtlMoveMemory(VDMInfo->CurDirectory,
|
|
b->CurDirectory,
|
|
b->CurDirectoryLen);
|
|
}
|
|
VDMInfo->CurDirectoryLen = b->CurDirectoryLen;
|
|
|
|
if (b->EnvLen) {
|
|
RtlMoveMemory(VDMInfo->Enviornment,
|
|
b->Env,
|
|
b->EnvLen);
|
|
}
|
|
VDMInfo->EnviornmentSize = b->EnvLen;
|
|
|
|
if (b->DesktopLen) {
|
|
RtlMoveMemory(VDMInfo->Desktop,
|
|
b->Desktop,
|
|
b->DesktopLen);
|
|
}
|
|
VDMInfo->DesktopLen = b->DesktopLen;
|
|
|
|
if (b->TitleLen) {
|
|
RtlMoveMemory(VDMInfo->Title,
|
|
b->Title,
|
|
b->TitleLen);
|
|
}
|
|
VDMInfo->TitleLen = b->TitleLen;
|
|
|
|
if (b->ReservedLen) {
|
|
RtlMoveMemory(VDMInfo->Reserved,
|
|
b->Reserved,
|
|
b->ReservedLen);
|
|
}
|
|
|
|
VDMInfo->ReservedLen = b->ReservedLen;
|
|
|
|
if (b->StartupInfo) {
|
|
RtlMoveMemory(&VDMInfo->StartupInfo,
|
|
b->StartupInfo,
|
|
sizeof (STARTUPINFOA));
|
|
VDMInfo->VDMState = STARTUP_INFO_RETURNED;
|
|
}
|
|
else
|
|
VDMInfo->VDMState = 0;
|
|
|
|
VDMInfo->dwCreationFlags = b->dwCreationFlags;
|
|
VDMInfo->CurDrive = b->CurDrive;
|
|
VDMInfo->CodePage = b->CodePage;
|
|
|
|
// ATTENTION THIS CODE ASSUMES THAT WOWRECORD AND DOSRECORD HAVE THE SAME LAYOUT
|
|
// THIS IS BAD BAD BAD -- fix later BUGBUG
|
|
//
|
|
|
|
if (pInfoRecord->iTag == BINARY_TYPE_WIN16) {
|
|
pInfoRecord->pRecord.pWOWRecord->lpVDMInfo = VDMInfo;
|
|
}
|
|
else {
|
|
pInfoRecord->pRecord.pDOSRecord->lpVDMInfo = VDMInfo;
|
|
}
|
|
|
|
VDMInfo->StdIn = VDMInfo->StdOut = VDMInfo->StdErr = 0;
|
|
if(pInfoRecord->iTag == BINARY_TYPE_DOS) {
|
|
VDMInfo->StdIn = b->StdIn;
|
|
VDMInfo->StdOut = b->StdOut;
|
|
VDMInfo->StdErr = b->StdErr;
|
|
}
|
|
else if (pInfoRecord->iTag == BINARY_TYPE_WIN16) {
|
|
pInfoRecord->pRecord.pWOWRecord->fDispatched = FALSE;
|
|
}
|
|
|
|
|
|
// else if (pInfoRecord->iTag == BINARY_TYPE_SEPWOW)
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG
|
|
BaseSrvUpdateWOWEntry(
|
|
PBASE_UPDATE_VDM_ENTRY_MSG b,
|
|
ULONG ParentSequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
PWOWRECORD pWOWRecord;
|
|
HANDLE Handle,TargetHandle;
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
// this is fun -- we get the the record using the task id
|
|
// reason: the call is made from the context of a creator process
|
|
// hence console handle means nothing
|
|
|
|
Status = BaseSrvFindSharedWowRecordByTaskId(&gWowHead,
|
|
b->iTask,
|
|
&pSharedWow,
|
|
&pWOWRecord);
|
|
// this returns us the shared wow record and wow record
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
if (ParentSequenceNumber != pSharedWow->ParentSequenceNumber) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto UpdateWowEntryExit;
|
|
}
|
|
|
|
switch ( b->EntryIndex ){
|
|
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
if( b->VDMCreationState & VDM_BEING_REUSED ||
|
|
b->VDMCreationState & VDM_FULLY_CREATED){
|
|
NtClose(pWOWRecord->hWaitForParent);
|
|
pWOWRecord->hWaitForParent = 0;
|
|
}
|
|
|
|
if( b->VDMCreationState & VDM_PARTIALLY_CREATED ||
|
|
b->VDMCreationState & VDM_FULLY_CREATED){
|
|
|
|
BaseSrvRemoveWOWRecord (pSharedWow, pWOWRecord);
|
|
BaseSrvFreeWOWRecord (pWOWRecord);
|
|
|
|
if (NULL == pSharedWow->pWOWRecord) {
|
|
Status = BaseSrvDeleteSharedWowRecord(&gWowHead, pSharedWow);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status) )
|
|
goto UpdateWowEntryExit;
|
|
|
|
switch ( b->EntryIndex ){
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
Status = BaseSrvCreatePairWaitHandles (&Handle,&TargetHandle);
|
|
|
|
if (NT_SUCCESS(Status) ){
|
|
pWOWRecord->hWaitForParent = Handle;
|
|
pWOWRecord->hWaitForParentServer = TargetHandle;
|
|
b->WaitObjectForParent = TargetHandle;
|
|
if (UserNotifyProcessCreate != NULL) {
|
|
(*UserNotifyProcessCreate)(pWOWRecord->iTask,
|
|
(DWORD)((ULONG_PTR)CSR_SERVER_QUERYCLIENTTHREAD()->ClientId.UniqueThread),
|
|
(DWORD)((ULONG_PTR)TargetHandle),
|
|
0x04);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
case UPDATE_VDM_HOOKED_CTRLC:
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
UpdateWowEntryExit:
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
BaseSrvUpdateDOSEntry(
|
|
PBASE_UPDATE_VDM_ENTRY_MSG b,
|
|
ULONG ParentSequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PDOSRECORD pDOSRecord;
|
|
PCONSOLERECORD pConsoleRecord = NULL;
|
|
HANDLE Handle,TargetHandle;
|
|
PCSR_THREAD t;
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
if (b->iTask)
|
|
Status = GetConsoleRecordDosSesId(b->iTask,&pConsoleRecord);
|
|
else
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
if (ParentSequenceNumber != pConsoleRecord->ParentSequenceNumber) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto UpdateDosEntryExit;
|
|
}
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
|
|
switch ( b->EntryIndex ){
|
|
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
Status = NtDuplicateObject (
|
|
t->Process->ProcessHandle,
|
|
b->VDMProcessHandle,
|
|
NtCurrentProcess(),
|
|
&pConsoleRecord->hVDM,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
break;
|
|
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
if( b->VDMCreationState & VDM_BEING_REUSED ||
|
|
b->VDMCreationState & VDM_FULLY_CREATED){
|
|
NtClose(pDOSRecord->hWaitForParentDup);
|
|
pDOSRecord->hWaitForParentDup = 0;
|
|
}
|
|
if( b->VDMCreationState & VDM_PARTIALLY_CREATED ||
|
|
b->VDMCreationState & VDM_FULLY_CREATED){
|
|
|
|
BaseSrvRemoveDOSRecord (pConsoleRecord,pDOSRecord);
|
|
BaseSrvFreeDOSRecord (pDOSRecord);
|
|
if (pConsoleRecord->DOSRecord == NULL) {
|
|
if (b->VDMCreationState & VDM_FULLY_CREATED) {
|
|
if (pConsoleRecord->hVDM)
|
|
NtClose(pConsoleRecord->hVDM);
|
|
}
|
|
BaseSrvFreeConsoleRecord(pConsoleRecord);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UPDATE_VDM_HOOKED_CTRLC:
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status) )
|
|
goto UpdateDosEntryExit;
|
|
|
|
switch ( b->EntryIndex ){
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
// williamh, Oct 24, 1996.
|
|
// if the ntvdm is runnig on a new console, do NOT subsititue
|
|
// the given process handle with event. The caller(CreateProcess)
|
|
// will get the real process handle and so does the application
|
|
// who calls CreateProcess. When it is time for the application
|
|
// to call GetExitCodeProcess, the client side will return the
|
|
// right thing(on the server side, we have nothing because
|
|
// console and dos record are gone).
|
|
//
|
|
// VadimB: this code fixes the problem with GetExitCodeProcess
|
|
// in a way that is not too consistent. We should review
|
|
// TerminateProcess code along with the code that deletes
|
|
// pseudo-handles for processes (in this file) to account for
|
|
// outstanding handle references. For now this code also
|
|
// makes terminateprocess work on the handle we return
|
|
//
|
|
if ((!pConsoleRecord->DosSesId && b->BinaryType == BINARY_TYPE_DOS)) {
|
|
Status = BaseSrvCreatePairWaitHandles (&Handle,&TargetHandle);
|
|
|
|
if (NT_SUCCESS(Status) ){
|
|
if (NT_SUCCESS ( Status = BaseSrvDupStandardHandles (
|
|
pConsoleRecord->hVDM,
|
|
pDOSRecord))){
|
|
pDOSRecord->hWaitForParent = TargetHandle;
|
|
pDOSRecord->hWaitForParentDup = Handle;
|
|
b->WaitObjectForParent = TargetHandle;
|
|
}
|
|
else{
|
|
BaseSrvClosePairWaitHandles (pDOSRecord);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
pDOSRecord->hWaitForParent = NULL;
|
|
pDOSRecord->hWaitForParentDup = NULL;
|
|
b->WaitObjectForParent = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
case UPDATE_VDM_HOOKED_CTRLC:
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
UpdateDosEntryExit:
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return Status;
|
|
}
|
|
|
|
|
|
PWOWRECORD
|
|
BaseSrvCheckAvailableWOWCommand(
|
|
PSHAREDWOWRECORD pSharedWow
|
|
)
|
|
{
|
|
|
|
PWOWRECORD pWOWRecord;
|
|
|
|
if (NULL == pSharedWow)
|
|
return NULL;
|
|
|
|
pWOWRecord = pSharedWow->pWOWRecord;
|
|
|
|
while(NULL != pWOWRecord) {
|
|
if (pWOWRecord->fDispatched == FALSE) {
|
|
break;
|
|
}
|
|
pWOWRecord = pWOWRecord->WOWRecordNext;
|
|
|
|
}
|
|
return pWOWRecord;
|
|
}
|
|
|
|
// this function exits given wow task running in a given shared wow
|
|
//
|
|
|
|
NTSTATUS
|
|
BaseSrvExitWOWTask(
|
|
PBASE_EXIT_VDM_MSG b,
|
|
ULONG SequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
|
|
// now we might get burned here -- although unlikely
|
|
|
|
// find shared wow first
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = BaseSrvFindSharedWowRecordByConsoleHandle(&gWowHead,
|
|
b->ConsoleHandle,
|
|
&pSharedWow);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if(SequenceNumber != pSharedWow->SequenceNumber) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else if (-1 == b->iWowTask) { // the entire vdm goes
|
|
|
|
// remove from the chain first
|
|
Status = BaseSrvDeleteSharedWowRecord(&gWowHead,
|
|
pSharedWow);
|
|
}
|
|
else {
|
|
Status = BaseSrvRemoveWOWRecordByTaskId(pSharedWow,
|
|
b->iWowTask);
|
|
|
|
}
|
|
}
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvExitDOSTask(
|
|
PBASE_EXIT_VDM_MSG b,
|
|
ULONG SequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PDOSRECORD pDOSRecord;
|
|
PCONSOLERECORD pConsoleRecord = NULL;
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if (!NT_SUCCESS (Status) || SequenceNumber != pConsoleRecord->SequenceNumber) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (pConsoleRecord->hWaitForVDMDup){
|
|
NtClose(pConsoleRecord->hWaitForVDMDup);
|
|
pConsoleRecord->hWaitForVDMDup =0;
|
|
b->WaitObjectForVDM = pConsoleRecord->hWaitForVDM;
|
|
}
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
while (pDOSRecord) {
|
|
if (pDOSRecord->hWaitForParentDup) {
|
|
NtSetEvent (pDOSRecord->hWaitForParentDup,NULL);
|
|
NtClose (pDOSRecord->hWaitForParentDup);
|
|
pDOSRecord->hWaitForParentDup = 0;
|
|
}
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
NtClose(pConsoleRecord->hVDM);
|
|
|
|
BaseSrvFreeConsoleRecord (pConsoleRecord);
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
return Status;
|
|
}
|
|
|
|
// assumes: shared wow cs is being held
|
|
// iWowTask is valid
|
|
|
|
NTSTATUS
|
|
BaseSrvRemoveWOWRecordByTaskId (
|
|
IN PSHAREDWOWRECORD pSharedWow,
|
|
IN ULONG iWowTask
|
|
)
|
|
{
|
|
PWOWRECORD pWOWRecordLast = NULL, pWOWRecord;
|
|
|
|
if (pSharedWow == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pWOWRecord = pSharedWow->pWOWRecord;
|
|
|
|
// Find the right WOW record and free it.
|
|
while (NULL != pWOWRecord) {
|
|
|
|
if (pWOWRecord->iTask == iWowTask) {
|
|
|
|
if (NULL == pWOWRecordLast) {
|
|
pSharedWow->pWOWRecord = pWOWRecord->WOWRecordNext;
|
|
}
|
|
else {
|
|
pWOWRecordLast->WOWRecordNext = pWOWRecord->WOWRecordNext;
|
|
}
|
|
|
|
if(pWOWRecord->hWaitForParent) {
|
|
NtSetEvent (pWOWRecord->hWaitForParent,NULL);
|
|
NtClose (pWOWRecord->hWaitForParent);
|
|
pWOWRecord->hWaitForParent = 0;
|
|
}
|
|
BaseSrvFreeWOWRecord(pWOWRecord);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pWOWRecordLast = pWOWRecord;
|
|
pWOWRecord = pWOWRecord->WOWRecordNext;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
ULONG
|
|
BaseSrvGetVDMExitCode( ///////// BUGBUG -- fixme
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLERECORD pConsoleRecord = NULL;
|
|
PDOSRECORD pDOSRecord;
|
|
PBASE_GET_VDM_EXIT_CODE_MSG b = (PBASE_GET_VDM_EXIT_CODE_MSG)&m->u.ApiMessageData;
|
|
|
|
if (b->ConsoleHandle == (HANDLE)-1){
|
|
b->ExitCode = 0;
|
|
}
|
|
else{
|
|
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
Status = BaseSrvGetConsoleRecord (b->ConsoleHandle,&pConsoleRecord);
|
|
if (!NT_SUCCESS(Status)){
|
|
b->ExitCode = 0;
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
while (pDOSRecord) {
|
|
// sudeepb 05-Oct-1992
|
|
// fix for the change markl has made for tagging VDM handles
|
|
|
|
if (pDOSRecord->hWaitForParent == (HANDLE)((ULONG_PTR)b->hParent & ~0x1)) {
|
|
if (pDOSRecord->VDMState == VDM_HAS_RETURNED_ERROR_CODE){
|
|
b->ExitCode = pDOSRecord->ErrorCode;
|
|
if (pDOSRecord == pConsoleRecord->DOSRecord &&
|
|
pDOSRecord->DOSRecordNext == NULL)
|
|
{
|
|
pDOSRecord->VDMState = VDM_READY;
|
|
pDOSRecord->hWaitForParent = 0;
|
|
}
|
|
else {
|
|
BaseSrvRemoveDOSRecord (pConsoleRecord,pDOSRecord);
|
|
BaseSrvFreeDOSRecord(pDOSRecord);
|
|
}
|
|
}
|
|
else {
|
|
if (pDOSRecord->VDMState == VDM_READY)
|
|
b->ExitCode = pDOSRecord->ErrorCode;
|
|
else
|
|
b->ExitCode = STILL_ACTIVE;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
|
|
if (pDOSRecord == NULL)
|
|
b->ExitCode = 0;
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG BaseSrvDupStandardHandles(
|
|
IN HANDLE pVDMProc,
|
|
IN PDOSRECORD pDOSRecord
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE pSrcProc;
|
|
HANDLE StdOutTemp = NULL;
|
|
PCSR_THREAD t;
|
|
PVDMINFO pVDMInfo = pDOSRecord->lpVDMInfo;
|
|
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
pSrcProc = t->Process->ProcessHandle;
|
|
|
|
if (pVDMInfo->StdIn){
|
|
Status = NtDuplicateObject (
|
|
pSrcProc,
|
|
pVDMInfo->StdIn,
|
|
pVDMProc,
|
|
&pVDMInfo->StdIn,
|
|
0,
|
|
OBJ_INHERIT,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
if (!NT_SUCCESS (Status))
|
|
return Status;
|
|
}
|
|
|
|
if (pVDMInfo->StdOut){
|
|
StdOutTemp = pVDMInfo->StdOut;
|
|
Status = NtDuplicateObject (
|
|
pSrcProc,
|
|
pVDMInfo->StdOut,
|
|
pVDMProc,
|
|
&pVDMInfo->StdOut,
|
|
0,
|
|
OBJ_INHERIT,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
if (!NT_SUCCESS (Status))
|
|
return Status;
|
|
}
|
|
|
|
if (pVDMInfo->StdErr){
|
|
if(pVDMInfo->StdErr != StdOutTemp){
|
|
Status = NtDuplicateObject (
|
|
pSrcProc,
|
|
pVDMInfo->StdErr,
|
|
pVDMProc,
|
|
&pVDMInfo->StdErr,
|
|
0,
|
|
OBJ_INHERIT,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
if (!NT_SUCCESS (Status))
|
|
return Status;
|
|
}
|
|
else
|
|
pVDMInfo->StdErr = pVDMInfo->StdOut;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Generates a DosSesId which is unique and nonzero
|
|
ULONG GetNextDosSesId(VOID)
|
|
{
|
|
static BOOLEAN bWrap = FALSE;
|
|
static ULONG NextSesId=1;
|
|
ULONG ul;
|
|
PCONSOLERECORD pConsoleHead;
|
|
|
|
pConsoleHead = DOSHead;
|
|
ul = NextSesId;
|
|
|
|
if (bWrap) {
|
|
while (pConsoleHead) {
|
|
if (!pConsoleHead->hConsole && pConsoleHead->DosSesId == ul)
|
|
{
|
|
pConsoleHead = DOSHead;
|
|
ul++;
|
|
if (!ul) { // never use zero
|
|
bWrap = TRUE;
|
|
ul++;
|
|
}
|
|
}
|
|
else {
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
NextSesId = ul + 1;
|
|
if (!NextSesId) { // never use zero
|
|
bWrap = TRUE;
|
|
NextSesId++;
|
|
}
|
|
return ul;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS BaseSrvGetConsoleRecord (
|
|
IN HANDLE hConsole,
|
|
IN OUT PCONSOLERECORD *pConsoleRecord
|
|
)
|
|
{
|
|
PCONSOLERECORD pConsoleHead;
|
|
|
|
pConsoleHead = DOSHead;
|
|
|
|
if (hConsole) {
|
|
while (pConsoleHead) {
|
|
if (pConsoleHead->hConsole == hConsole){
|
|
*pConsoleRecord = pConsoleHead;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
GetConsoleRecordDosSesId (
|
|
IN ULONG DosSesId,
|
|
IN OUT PCONSOLERECORD *pConsoleRecord
|
|
)
|
|
{
|
|
PCONSOLERECORD pConsoleHead;
|
|
|
|
if (!DosSesId)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
pConsoleHead = DOSHead;
|
|
|
|
while (pConsoleHead) {
|
|
if (!pConsoleHead->hConsole &&
|
|
pConsoleHead->DosSesId == DosSesId)
|
|
{
|
|
*pConsoleRecord = pConsoleHead;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
|
|
PWOWRECORD
|
|
BaseSrvAllocateWOWRecord(
|
|
PSHAREDWOWRECORDHEAD pSharedWowRecordHead
|
|
)
|
|
{
|
|
register PWOWRECORD WOWRecord;
|
|
|
|
WOWRecord = RtlAllocateHeap ( RtlProcessHeap (), MAKE_TAG( VDM_TAG ), sizeof (WOWRECORD));
|
|
|
|
if (WOWRecord == NULL)
|
|
return NULL;
|
|
|
|
RtlZeroMemory ((PVOID)WOWRecord,sizeof(WOWRECORD));
|
|
|
|
// if too many tasks, error out.
|
|
if ((WOWRecord->iTask = BaseSrvGetWOWTaskId(pSharedWowRecordHead)) == WOWMAXID) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, WOWRecord);
|
|
return NULL;
|
|
}
|
|
return WOWRecord;
|
|
}
|
|
|
|
VOID BaseSrvFreeWOWRecord (
|
|
PWOWRECORD pWOWRecord
|
|
)
|
|
{
|
|
if (pWOWRecord == NULL)
|
|
return;
|
|
if (pWOWRecord->pFilePath) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pWOWRecord->pFilePath);
|
|
}
|
|
BaseSrvFreeVDMInfo (pWOWRecord->lpVDMInfo);
|
|
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pWOWRecord);
|
|
}
|
|
|
|
VOID BaseSrvAddWOWRecord (
|
|
PSHAREDWOWRECORD pSharedWow,
|
|
PWOWRECORD pWOWRecord
|
|
)
|
|
{
|
|
PWOWRECORD WOWRecordCurrent,WOWRecordLast;
|
|
|
|
// First WOW app runs first, so add the new ones at the end
|
|
if (NULL == pSharedWow->pWOWRecord) {
|
|
pSharedWow->pWOWRecord = pWOWRecord;
|
|
return;
|
|
}
|
|
|
|
WOWRecordCurrent = pSharedWow->pWOWRecord;
|
|
|
|
while (NULL != WOWRecordCurrent){
|
|
WOWRecordLast = WOWRecordCurrent;
|
|
WOWRecordCurrent = WOWRecordCurrent->WOWRecordNext;
|
|
}
|
|
|
|
WOWRecordLast->WOWRecordNext = pWOWRecord;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID BaseSrvRemoveWOWRecord (
|
|
PSHAREDWOWRECORD pSharedWow,
|
|
PWOWRECORD pWOWRecord
|
|
)
|
|
{
|
|
PWOWRECORD WOWRecordCurrent,WOWRecordLast = NULL;
|
|
|
|
if (NULL == pSharedWow) {
|
|
return;
|
|
}
|
|
|
|
if (NULL == pSharedWow->pWOWRecord) {
|
|
return;
|
|
}
|
|
|
|
if (pSharedWow->pWOWRecord == pWOWRecord) {
|
|
pSharedWow->pWOWRecord = pWOWRecord->WOWRecordNext;
|
|
return;
|
|
}
|
|
|
|
WOWRecordLast = pSharedWow->pWOWRecord;
|
|
WOWRecordCurrent = WOWRecordLast->WOWRecordNext;
|
|
|
|
while (WOWRecordCurrent && WOWRecordCurrent != pWOWRecord){
|
|
WOWRecordLast = WOWRecordCurrent;
|
|
WOWRecordCurrent = WOWRecordCurrent->WOWRecordNext;
|
|
}
|
|
|
|
if (WOWRecordCurrent != NULL)
|
|
WOWRecordLast->WOWRecordNext = pWOWRecord->WOWRecordNext;
|
|
|
|
return;
|
|
}
|
|
|
|
PCONSOLERECORD BaseSrvAllocateConsoleRecord (
|
|
VOID
|
|
)
|
|
{
|
|
PCONSOLERECORD pConsoleRecord;
|
|
|
|
if (NULL == (pConsoleRecord = RtlAllocateHeap (RtlProcessHeap (),
|
|
MAKE_TAG(VDM_TAG),
|
|
sizeof (CONSOLERECORD)))) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
RtlZeroMemory(pConsoleRecord, sizeof(CONSOLERECORD));
|
|
|
|
return pConsoleRecord;
|
|
}
|
|
|
|
|
|
VOID BaseSrvFreeConsoleRecord (
|
|
PCONSOLERECORD pConsoleRecord
|
|
)
|
|
{
|
|
PDOSRECORD pDOSRecord;
|
|
|
|
if (pConsoleRecord == NULL)
|
|
return;
|
|
|
|
while (pDOSRecord = pConsoleRecord->DOSRecord){
|
|
pConsoleRecord->DOSRecord = pDOSRecord->DOSRecordNext;
|
|
BaseSrvFreeDOSRecord (pDOSRecord);
|
|
}
|
|
|
|
if (pConsoleRecord->lpszzCurDirs)
|
|
RtlFreeHeap(BaseSrvHeap, 0, pConsoleRecord->lpszzCurDirs);
|
|
|
|
if (pConsoleRecord->DosUserToken)
|
|
NtClose(pConsoleRecord->DosUserToken);
|
|
|
|
BaseSrvRemoveConsoleRecord (pConsoleRecord);
|
|
|
|
RtlFreeHeap (RtlProcessHeap (), 0, pConsoleRecord );
|
|
}
|
|
|
|
VOID BaseSrvRemoveConsoleRecord (
|
|
PCONSOLERECORD pConsoleRecord
|
|
)
|
|
{
|
|
|
|
PCONSOLERECORD pTempLast,pTemp;
|
|
|
|
if (DOSHead == NULL)
|
|
return;
|
|
|
|
if(DOSHead == pConsoleRecord) {
|
|
DOSHead = pConsoleRecord->Next;
|
|
return;
|
|
}
|
|
|
|
pTempLast = DOSHead;
|
|
pTemp = DOSHead->Next;
|
|
|
|
while (pTemp && pTemp != pConsoleRecord){
|
|
pTempLast = pTemp;
|
|
pTemp = pTemp->Next;
|
|
}
|
|
|
|
if (pTemp)
|
|
pTempLast->Next = pTemp->Next;
|
|
|
|
return;
|
|
}
|
|
|
|
PDOSRECORD
|
|
BaseSrvAllocateDOSRecord(
|
|
VOID
|
|
)
|
|
{
|
|
PDOSRECORD DOSRecord;
|
|
|
|
DOSRecord = RtlAllocateHeap ( RtlProcessHeap (), MAKE_TAG( VDM_TAG ), sizeof (DOSRECORD));
|
|
|
|
if (DOSRecord == NULL)
|
|
return NULL;
|
|
|
|
RtlZeroMemory ((PVOID)DOSRecord,sizeof(DOSRECORD));
|
|
return DOSRecord;
|
|
}
|
|
|
|
VOID BaseSrvFreeDOSRecord (
|
|
PDOSRECORD pDOSRecord
|
|
)
|
|
{
|
|
BaseSrvFreeVDMInfo (pDOSRecord->lpVDMInfo);
|
|
if(pDOSRecord->pFilePath) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pDOSRecord->pFilePath);
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pDOSRecord);
|
|
return;
|
|
}
|
|
|
|
VOID BaseSrvAddDOSRecord (
|
|
PCONSOLERECORD pConsoleRecord,
|
|
PDOSRECORD pDOSRecord
|
|
)
|
|
{
|
|
PDOSRECORD pDOSRecordTemp;
|
|
|
|
pDOSRecord->DOSRecordNext = NULL;
|
|
|
|
if(pConsoleRecord->DOSRecord == NULL){
|
|
pConsoleRecord->DOSRecord = pDOSRecord;
|
|
return;
|
|
}
|
|
|
|
pDOSRecordTemp = pConsoleRecord->DOSRecord;
|
|
|
|
while (pDOSRecordTemp->DOSRecordNext)
|
|
pDOSRecordTemp = pDOSRecordTemp->DOSRecordNext;
|
|
|
|
pDOSRecordTemp->DOSRecordNext = pDOSRecord;
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BaseSrvRemoveDOSRecord (
|
|
PCONSOLERECORD pConsoleRecord,
|
|
PDOSRECORD pDOSRecord
|
|
)
|
|
{
|
|
PDOSRECORD DOSRecordCurrent,DOSRecordLast = NULL;
|
|
|
|
if( pConsoleRecord == NULL)
|
|
return;
|
|
|
|
if(pConsoleRecord->DOSRecord == pDOSRecord){
|
|
pConsoleRecord->DOSRecord = pDOSRecord->DOSRecordNext;
|
|
return;
|
|
}
|
|
|
|
DOSRecordLast = pConsoleRecord->DOSRecord;
|
|
if (DOSRecordLast)
|
|
DOSRecordCurrent = DOSRecordLast->DOSRecordNext;
|
|
else
|
|
return;
|
|
|
|
while (DOSRecordCurrent && DOSRecordCurrent != pDOSRecord){
|
|
DOSRecordLast = DOSRecordCurrent;
|
|
DOSRecordCurrent = DOSRecordCurrent->DOSRecordNext;
|
|
}
|
|
|
|
if (DOSRecordCurrent == NULL)
|
|
return;
|
|
else
|
|
DOSRecordLast->DOSRecordNext = pDOSRecord->DOSRecordNext;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
BaseSrvFreeVDMInfo(
|
|
IN PVDMINFO lpVDMInfo
|
|
)
|
|
{
|
|
if (lpVDMInfo == NULL)
|
|
return;
|
|
|
|
if (lpVDMInfo->CmdLine)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->CmdLine);
|
|
|
|
if (lpVDMInfo->AppName) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, lpVDMInfo->AppName);
|
|
}
|
|
|
|
if (lpVDMInfo->PifFile) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, lpVDMInfo->PifFile);
|
|
}
|
|
|
|
if(lpVDMInfo->Enviornment)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->Enviornment);
|
|
|
|
if(lpVDMInfo->Desktop)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->Desktop);
|
|
|
|
if(lpVDMInfo->Title)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->Title);
|
|
|
|
if(lpVDMInfo->Reserved)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->Reserved);
|
|
|
|
if(lpVDMInfo->CurDirectory)
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo->CurDirectory);
|
|
|
|
RtlFreeHeap(RtlProcessHeap (), 0,lpVDMInfo);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG BaseSrvCreatePairWaitHandles (ServerHandle, ClientHandle)
|
|
HANDLE *ServerHandle;
|
|
HANDLE *ClientHandle;
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_THREAD t;
|
|
|
|
Status = NtCreateEvent(
|
|
ServerHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) )
|
|
return Status;
|
|
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
Status = NtDuplicateObject (
|
|
NtCurrentProcess(),
|
|
*ServerHandle,
|
|
t->Process->ProcessHandle,
|
|
ClientHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ){
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
NtClose (*ServerHandle);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
// generate global task id
|
|
//
|
|
// The handling of a task id should be redone wrt the user notification
|
|
// private apis
|
|
// note that wow task id is never 0 or (ULONG)-1
|
|
//
|
|
|
|
|
|
|
|
ULONG
|
|
BaseSrvGetWOWTaskId(
|
|
PSHAREDWOWRECORDHEAD pSharedWowHead // (->pSharedWowRecord)
|
|
)
|
|
{
|
|
PWOWRECORD pWOWRecord;
|
|
PSHAREDWOWRECORD pSharedWow = pSharedWowHead->pSharedWowRecord;
|
|
|
|
static BOOL fWrapped = FALSE;
|
|
|
|
if (WOWTaskIdNext == WOWMAXID) {
|
|
fWrapped = TRUE;
|
|
WOWTaskIdNext = WOWMINID;
|
|
}
|
|
|
|
if (fWrapped && NULL != pSharedWow) {
|
|
while (NULL != pSharedWow) {
|
|
|
|
#if 0
|
|
if (pSharedWow->WowSessionId == WOWTaskIdNext) {
|
|
if (WOWMAXID == ++WOWTaskIdNext) {
|
|
WOWTaskIdNext = WOWMINID;
|
|
}
|
|
|
|
pSharedWow = pSharedWowHead->pSharedWowRecord;
|
|
continue; // go back to the beginning of the loop
|
|
}
|
|
#endif
|
|
|
|
// examine all the records for this wow
|
|
|
|
pWOWRecord = pSharedWow->pWOWRecord;
|
|
while (NULL != pWOWRecord) {
|
|
|
|
if (pWOWRecord->iTask == WOWTaskIdNext) {
|
|
if (WOWMAXID == ++WOWTaskIdNext) {
|
|
WOWTaskIdNext = WOWMINID;
|
|
}
|
|
|
|
break; // we are breaking out => (pWOWRecord != NULL)
|
|
}
|
|
|
|
pWOWRecord = pWOWRecord->WOWRecordNext;
|
|
}
|
|
|
|
|
|
if (NULL == pWOWRecord) { // id is ok for this wow -- check the next wow
|
|
pSharedWow = pSharedWow->pNextSharedWow;
|
|
}
|
|
else {
|
|
pSharedWow = pSharedWowHead->pSharedWowRecord;
|
|
}
|
|
}
|
|
}
|
|
|
|
return WOWTaskIdNext++;
|
|
}
|
|
|
|
|
|
VOID
|
|
BaseSrvAddConsoleRecord(
|
|
IN PCONSOLERECORD pConsoleRecord
|
|
)
|
|
{
|
|
|
|
pConsoleRecord->Next = DOSHead;
|
|
DOSHead = pConsoleRecord;
|
|
}
|
|
|
|
|
|
VOID BaseSrvCloseStandardHandles (HANDLE hVDM, PDOSRECORD pDOSRecord)
|
|
{
|
|
PVDMINFO pVDMInfo = pDOSRecord->lpVDMInfo;
|
|
|
|
if (pVDMInfo == NULL)
|
|
return;
|
|
|
|
if (pVDMInfo->StdIn)
|
|
NtDuplicateObject (hVDM,
|
|
pVDMInfo->StdIn,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
|
|
if (pVDMInfo->StdOut)
|
|
NtDuplicateObject (hVDM,
|
|
pVDMInfo->StdOut,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
|
|
if (pVDMInfo->StdErr)
|
|
NtDuplicateObject (hVDM,
|
|
pVDMInfo->StdErr,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
|
|
pVDMInfo->StdIn = 0;
|
|
pVDMInfo->StdOut = 0;
|
|
pVDMInfo->StdErr = 0;
|
|
return;
|
|
}
|
|
|
|
VOID BaseSrvClosePairWaitHandles (PDOSRECORD pDOSRecord)
|
|
{
|
|
PCSR_THREAD t;
|
|
|
|
if (pDOSRecord->hWaitForParentDup)
|
|
NtClose (pDOSRecord->hWaitForParentDup);
|
|
|
|
t = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
if (pDOSRecord->hWaitForParent)
|
|
NtDuplicateObject (t->Process->ProcessHandle,
|
|
pDOSRecord->hWaitForParent,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
DUPLICATE_CLOSE_SOURCE);
|
|
|
|
pDOSRecord->hWaitForParentDup = 0;
|
|
pDOSRecord->hWaitForParent = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
BaseSrvSetReenterCount (
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus
|
|
)
|
|
{
|
|
PBASE_SET_REENTER_COUNT_MSG b = (PBASE_SET_REENTER_COUNT_MSG)&m->u.ApiMessageData;
|
|
NTSTATUS Status;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
ULONG SequenceNumber;
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(m->h.ClientId.UniqueProcess, &SequenceNumber))) {
|
|
return (ULONG)STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = BaseSrvGetConsoleRecord(b->ConsoleHandle,&pConsoleRecord);
|
|
|
|
if (!NT_SUCCESS (Status) || SequenceNumber != pConsoleRecord->SequenceNumber) {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return ((ULONG)STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (b->fIncDec == INCREMENT_REENTER_COUNT)
|
|
pConsoleRecord->nReEntrancy++;
|
|
else {
|
|
pConsoleRecord->nReEntrancy--;
|
|
if(pConsoleRecord->hWaitForVDMDup)
|
|
NtSetEvent (pConsoleRecord->hWaitForVDMDup,NULL);
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Spawn of ntvdm failed before CreateProcessW finished.
|
|
* delete the console record.
|
|
*/
|
|
|
|
|
|
VOID
|
|
BaseSrvVDMTerminated (
|
|
IN HANDLE hVDM,
|
|
IN ULONG DosSesId
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
|
|
RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
if (!hVDM) // no-console-handle case
|
|
Status = GetConsoleRecordDosSesId(DosSesId,&pConsoleRecord);
|
|
else
|
|
Status = BaseSrvGetConsoleRecord(hVDM,&pConsoleRecord);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
BaseSrvExitVDMWorker(pConsoleRecord);
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvUpdateVDMSequenceNumber (
|
|
IN ULONG VdmBinaryType, // binary type
|
|
IN HANDLE hVDM, // console handle
|
|
IN ULONG DosSesId, // session id
|
|
IN HANDLE UniqueProcessClientID, // client id
|
|
IN HANDLE UniqueProcessParentID // parent id
|
|
)
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS ProcessVDM;
|
|
ULONG SequenceNumber;
|
|
|
|
if(!NT_SUCCESS(BaseSrvGetVdmSequence(UniqueProcessParentID, &SequenceNumber))){
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
// so how do we know what to update ?
|
|
// this condition is always true: (hvdm ^ DosSesId)
|
|
// hence since shared wow
|
|
|
|
// sequence numbers are important -- hence we need to acquire
|
|
// a wow critical section -- does not hurt -- this op performed once
|
|
// during the new wow creation
|
|
|
|
if (IS_SHARED_WOW_BINARY(VdmBinaryType)) {
|
|
|
|
PSHAREDWOWRECORD pSharedWowRecord;
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// this looks like a shared wow binary -- hence locate the
|
|
// appropriate vdm either by hvdm or by dos session id
|
|
if (!hVDM) { // search by console handle
|
|
Status = BaseSrvFindSharedWowRecordByConsoleHandle(&gWowHead,
|
|
hVDM,
|
|
&pSharedWowRecord);
|
|
}
|
|
else { // search by the task id
|
|
Status = BaseSrvFindSharedWowRecordByTaskId(&gWowHead,
|
|
DosSesId,
|
|
&pSharedWowRecord,
|
|
NULL);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) && 0 == pSharedWowRecord->SequenceNumber) {
|
|
|
|
if (SequenceNumber == pSharedWowRecord->ParentSequenceNumber) {
|
|
|
|
// now obtain a sequence number please
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientID,
|
|
&ProcessVDM);
|
|
if (NT_SUCCESS(Status)) {
|
|
ProcessVDM->fVDM = TRUE;
|
|
pSharedWowRecord->SequenceNumber = ProcessVDM->SequenceNumber;
|
|
pSharedWowRecord->ParentSequenceNumber = 0;
|
|
pSharedWowRecord->dwWowExecProcessId = HandleToLong(UniqueProcessClientID);
|
|
CsrUnlockProcess(ProcessVDM);
|
|
} else {
|
|
// The spawned ntvdm.exe went away, give up.
|
|
BaseSrvDeleteSharedWowRecord(&gWowHead,pSharedWowRecord);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#if DEVL
|
|
DbgPrint( "BASESRV: WOW is in inconsistent state. Contact WOW Team\n");
|
|
#endif
|
|
}
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
}
|
|
else { // not shared wow binary
|
|
PCONSOLERECORD pConsoleRecord;
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
if (!hVDM) // no-console-handle case
|
|
Status = GetConsoleRecordDosSesId(DosSesId,&pConsoleRecord);
|
|
else
|
|
Status = BaseSrvGetConsoleRecord(hVDM,&pConsoleRecord);
|
|
|
|
if (NT_SUCCESS (Status) && 0 == pConsoleRecord->SequenceNumber) {
|
|
|
|
if (SequenceNumber == pConsoleRecord->ParentSequenceNumber) {
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientID,
|
|
&ProcessVDM);
|
|
if (NT_SUCCESS(Status)) {
|
|
ProcessVDM->fVDM = TRUE;
|
|
pConsoleRecord->SequenceNumber = ProcessVDM->SequenceNumber;
|
|
pConsoleRecord->ParentSequenceNumber = 0;
|
|
pConsoleRecord->dwProcessId = HandleToLong(UniqueProcessClientID);
|
|
CsrUnlockProcess(ProcessVDM);
|
|
}
|
|
// The spawned ntvdm.exe went away, give up.
|
|
// The caller BasepCreateProcess will clean up dos records, so we don't need to here.
|
|
// else
|
|
// BaseSrvExitVdmWorker(pConsoleRecord);
|
|
}
|
|
}
|
|
else {
|
|
#if DEVL
|
|
DbgPrint( "BASESRV: DOS is in inconsistent state. Contact DOS Team\n");
|
|
#endif
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
BaseSrvCleanupVDMResources ( //////// BUGBUGBUGBUG
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PCONSOLERECORD pConsoleHead;
|
|
PSHAREDWOWRECORD pSharedWowRecord;
|
|
NTSTATUS Status;
|
|
PBATRECORD pBatRecord;
|
|
|
|
if (!Process->fVDM) {
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
pBatRecord = BatRecordHead;
|
|
while (pBatRecord &&
|
|
pBatRecord->SequenceNumber != Process->SequenceNumber)
|
|
pBatRecord = pBatRecord->BatRecordNext;
|
|
|
|
if (pBatRecord)
|
|
BaseSrvFreeAndRemoveBatRecord(pBatRecord);
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
}
|
|
|
|
// search all the shared wow's
|
|
|
|
Status = ENTER_WOW_CRITICAL();
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
pSharedWowRecord = gWowHead.pSharedWowRecord;
|
|
|
|
while (pSharedWowRecord) {
|
|
if (pSharedWowRecord->SequenceNumber == Process->SequenceNumber) {
|
|
BaseSrvDeleteSharedWowRecord(&gWowHead, pSharedWowRecord);
|
|
break;
|
|
}
|
|
else
|
|
pSharedWowRecord = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
|
|
pSharedWowRecord = gWowHead.pSharedWowRecord;
|
|
|
|
while (pSharedWowRecord) {
|
|
if (pSharedWowRecord->ParentSequenceNumber == Process->SequenceNumber) {
|
|
BaseSrvDeleteSharedWowRecord(&gWowHead, pSharedWowRecord);
|
|
break;
|
|
}
|
|
else
|
|
pSharedWowRecord = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
|
|
LEAVE_WOW_CRITICAL();
|
|
|
|
// search all the dos's and separate wow's
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
pConsoleHead = DOSHead;
|
|
|
|
while (pConsoleHead) {
|
|
if (pConsoleHead->SequenceNumber == Process->SequenceNumber){
|
|
BaseSrvExitVDMWorker (pConsoleHead);
|
|
break;
|
|
}
|
|
else
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
|
|
pConsoleHead = DOSHead;
|
|
|
|
while (pConsoleHead) {
|
|
if (pConsoleHead->ParentSequenceNumber == Process->SequenceNumber){
|
|
BaseSrvExitVDMWorker (pConsoleHead);
|
|
break;
|
|
}
|
|
else
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
BaseSrvExitVDMWorker (
|
|
PCONSOLERECORD pConsoleRecord
|
|
)
|
|
{
|
|
PDOSRECORD pDOSRecord;
|
|
|
|
if (pConsoleRecord->hWaitForVDMDup){
|
|
NtClose(pConsoleRecord->hWaitForVDMDup);
|
|
pConsoleRecord->hWaitForVDMDup =0;
|
|
}
|
|
|
|
pDOSRecord = pConsoleRecord->DOSRecord;
|
|
|
|
while (pDOSRecord) {
|
|
if (pDOSRecord->hWaitForParentDup) {
|
|
NtSetEvent (pDOSRecord->hWaitForParentDup,NULL);
|
|
NtClose (pDOSRecord->hWaitForParentDup);
|
|
pDOSRecord->hWaitForParentDup = 0;
|
|
}
|
|
pDOSRecord = pDOSRecord->DOSRecordNext;
|
|
}
|
|
NtClose(pConsoleRecord->hVDM);
|
|
BaseSrvFreeConsoleRecord (pConsoleRecord);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvFillPifInfo (
|
|
PVDMINFO lpVDMInfo,
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b
|
|
)
|
|
{
|
|
|
|
LPSTR Title;
|
|
ULONG TitleLen;
|
|
NTSTATUS Status;
|
|
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
if (!lpVDMInfo)
|
|
return Status;
|
|
|
|
/*
|
|
* Get the title for the window in precedence order
|
|
*/
|
|
// startupinfo title
|
|
if (lpVDMInfo->TitleLen && lpVDMInfo->Title)
|
|
{
|
|
Title = lpVDMInfo->Title;
|
|
TitleLen = lpVDMInfo->TitleLen;
|
|
}
|
|
// App Name
|
|
else if (lpVDMInfo->AppName && lpVDMInfo->AppLen)
|
|
{
|
|
Title = lpVDMInfo->AppName;
|
|
TitleLen = lpVDMInfo->AppLen;
|
|
}
|
|
// hopeless
|
|
else {
|
|
Title = NULL;
|
|
TitleLen = 0;
|
|
}
|
|
|
|
try {
|
|
|
|
if (b->PifLen) {
|
|
*b->PifFile = '\0';
|
|
}
|
|
|
|
if (b->TitleLen) {
|
|
*b->Title = '\0';
|
|
}
|
|
|
|
if (b->CurDirectoryLen) {
|
|
*b->CurDirectory = '\0';
|
|
}
|
|
|
|
|
|
if ( (!b->TitleLen || TitleLen <= b->TitleLen) &&
|
|
(!b->PifLen || lpVDMInfo->PifLen <= b->PifLen) &&
|
|
(!b->CurDirectoryLen ||
|
|
lpVDMInfo->CurDirectoryLen <= b->CurDirectoryLen) &&
|
|
(!b->ReservedLen || lpVDMInfo->ReservedLen <= b->ReservedLen))
|
|
{
|
|
if (b->TitleLen) {
|
|
if (Title && TitleLen) {
|
|
RtlMoveMemory(b->Title, Title, TitleLen);
|
|
*((LPSTR)b->Title + TitleLen - 1) = '\0';
|
|
}
|
|
else {
|
|
*b->Title = '\0';
|
|
}
|
|
}
|
|
|
|
if (lpVDMInfo->PifLen && b->PifLen)
|
|
RtlMoveMemory(b->PifFile,
|
|
lpVDMInfo->PifFile,
|
|
lpVDMInfo->PifLen);
|
|
|
|
if (lpVDMInfo->CurDirectoryLen && b->CurDirectoryLen)
|
|
RtlMoveMemory(b->CurDirectory,
|
|
lpVDMInfo->CurDirectory,
|
|
lpVDMInfo->CurDirectoryLen
|
|
);
|
|
if (lpVDMInfo->Reserved && b->ReservedLen)
|
|
RtlMoveMemory(b->Reserved,
|
|
lpVDMInfo->Reserved,
|
|
lpVDMInfo->ReservedLen
|
|
);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
|
|
/* fill out the size for each field */
|
|
b->PifLen = (USHORT)lpVDMInfo->PifLen;
|
|
b->CurDirectoryLen = lpVDMInfo->CurDirectoryLen;
|
|
b->TitleLen = TitleLen;
|
|
b->ReservedLen = lpVDMInfo->ReservedLen;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* IsClientSystem
|
|
*
|
|
* Determines if caller is SYSTEM
|
|
*
|
|
* Returns TRUE is caller is system, FALSE if not (or error)
|
|
*
|
|
* History:
|
|
* 12-May-94 AndyH Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
IsClientSystem(
|
|
HANDLE hUserToken
|
|
)
|
|
{
|
|
BYTE achBuffer[100];
|
|
PTOKEN_USER pUser = (PTOKEN_USER) achBuffer;
|
|
DWORD dwBytesRequired;
|
|
NTSTATUS NtStatus;
|
|
BOOL fAllocatedBuffer = FALSE;
|
|
BOOL fSystem;
|
|
SID_IDENTIFIER_AUTHORITY SidIdAuth = SECURITY_NT_AUTHORITY;
|
|
static PSID pSystemSid = NULL;
|
|
|
|
if (!pSystemSid) {
|
|
// Create a sid for local system
|
|
NtStatus = RtlAllocateAndInitializeSid(
|
|
&SidIdAuth,
|
|
1, // SubAuthorityCount, 1 for local system
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0,0,0,0,0,0,0,
|
|
&pSystemSid
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
pSystemSid = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
NtStatus = NtQueryInformationToken(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
sizeof(achBuffer), // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
if (NtStatus != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pUser = (PTOKEN_USER) RtlAllocateHeap(BaseSrvHeap, MAKE_TAG( VDM_TAG ), dwBytesRequired);
|
|
if (pUser == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
fAllocatedBuffer = TRUE;
|
|
|
|
//
|
|
// Read in the UserInfo
|
|
//
|
|
|
|
NtStatus = NtQueryInformationToken(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
dwBytesRequired, // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
RtlFreeHeap(BaseSrvHeap, 0, pUser);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// Compare callers SID with SystemSid
|
|
|
|
fSystem = RtlEqualSid(pSystemSid, pUser->User.Sid);
|
|
|
|
if (fAllocatedBuffer)
|
|
{
|
|
RtlFreeHeap(BaseSrvHeap, 0, pUser);
|
|
}
|
|
|
|
return (fSystem);
|
|
}
|
|
BOOL
|
|
BaseSrvIsVdmAllowed(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks the registry to see if running NTVDM is disabled.
|
|
If the information is not specified in registry, the default will be NOT disabled and
|
|
TRUE will be returned to indicated running VDM is allowed.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE means yes; Otherwise no.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hCurrentUser, hAppCompat;
|
|
UNICODE_STRING unicodeKeyName, unicodeValueName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PKEY_VALUE_PARTIAL_INFORMATION keyValueInformation;
|
|
UCHAR valueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
ULONG resultLength;
|
|
BOOL vdmAllowed, impersonate, checkDefault = TRUE;
|
|
ULONG flags = 0;
|
|
|
|
RtlInitUnicodeString(&unicodeValueName, L"VDMDisallowed" );
|
|
keyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer;
|
|
|
|
//
|
|
// First check if machine policy for Vdm Disallow is enabled, if yes
|
|
// vdm is not allowed to run.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeKeyName,
|
|
L"\\REGISTRY\\MACHINE\\Software\\Policies\\Microsoft\\Windows\\AppCompat");
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
//
|
|
// Open key for READ access.
|
|
//
|
|
|
|
status = NtOpenKey( &hAppCompat,
|
|
KEY_READ,
|
|
&objectAttributes );
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = NtQueryValueKey( hAppCompat,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
keyValueInformation,
|
|
sizeof(valueBuffer),
|
|
&resultLength
|
|
);
|
|
NtClose( hAppCompat);
|
|
if (NT_SUCCESS(status) && keyValueInformation->Type == REG_DWORD)
|
|
{
|
|
checkDefault = FALSE;
|
|
flags = *(PULONG)keyValueInformation->Data;
|
|
if (flags)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we come here, machine policy is either not configured or VDM
|
|
// is allowed to run on this machine.
|
|
// Next check if current user policy for Vdm Disallow is enabled, if yes
|
|
// vdm is not allowed to run.
|
|
//
|
|
|
|
impersonate = CsrImpersonateClient(NULL);
|
|
|
|
if (impersonate) {
|
|
|
|
status = RtlOpenCurrentUser(KEY_READ, &hCurrentUser);
|
|
|
|
CsrRevertToSelf();
|
|
} else {
|
|
status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
RtlInitUnicodeString(&unicodeKeyName,
|
|
L"Software\\Policies\\Microsoft\\Windows\\AppCompat");
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hCurrentUser,
|
|
NULL );
|
|
|
|
//
|
|
// Open key for READ access.
|
|
//
|
|
|
|
status = NtOpenKey( &hAppCompat,
|
|
KEY_READ,
|
|
&objectAttributes );
|
|
|
|
NtClose(hCurrentUser);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = NtQueryValueKey( hAppCompat,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
keyValueInformation,
|
|
sizeof(valueBuffer),
|
|
&resultLength
|
|
);
|
|
NtClose( hAppCompat);
|
|
if (NT_SUCCESS(status) && keyValueInformation->Type == REG_DWORD)
|
|
{
|
|
checkDefault = FALSE;
|
|
flags = *(PULONG)keyValueInformation->Data;
|
|
if (flags)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (checkDefault == FALSE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If we come here, machine policy is not configured AND
|
|
// the current user policy is not configured either.
|
|
// Next check if default setting for Vdm Disallow is enabled, if yes
|
|
// vdm is not allowed to run.
|
|
//
|
|
|
|
vdmAllowed = TRUE;
|
|
RtlInitUnicodeString(&unicodeKeyName, L"\\REGISTRY\\MACHINE\\System\\CurrentControlSet\\Control\\WOW");
|
|
RtlInitUnicodeString(&unicodeValueName, L"DisallowedPolicyDefault" );
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
//
|
|
// Open key for READ access.
|
|
//
|
|
|
|
status = NtOpenKey( &hAppCompat,
|
|
KEY_READ,
|
|
&objectAttributes );
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = NtQueryValueKey( hAppCompat,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
keyValueInformation,
|
|
sizeof(valueBuffer),
|
|
&resultLength
|
|
);
|
|
NtClose( hAppCompat);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (keyValueInformation->Type == REG_DWORD)
|
|
{
|
|
flags = *(PULONG)keyValueInformation->Data;
|
|
if (flags) vdmAllowed = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
return vdmAllowed;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvIsClientVdm(
|
|
IN HANDLE UniqueProcessClientId
|
|
)
|
|
/*
|
|
* Verifies that the client thread is in a VDM process
|
|
*
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_PROCESS Process;
|
|
VDM_QUERY_VDM_PROCESS_DATA QueryVdmProcessData;
|
|
|
|
|
|
Status = CsrLockProcessByClientId(UniqueProcessClientId,&Process);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
//
|
|
// Check the Target Process to see if this is a Wx86 process
|
|
//
|
|
|
|
QueryVdmProcessData.IsVdmProcess = FALSE;
|
|
|
|
QueryVdmProcessData.ProcessHandle = Process->ProcessHandle;
|
|
|
|
Status = NtVdmControl(VdmQueryVdmProcess, &QueryVdmProcessData);
|
|
|
|
if (!NT_SUCCESS(Status) || QueryVdmProcessData.IsVdmProcess == FALSE) {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
CsrUnlockProcess(Process);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvGetSharedWowRecordByPid(
|
|
DWORD dwProcessId,
|
|
PSHAREDWOWRECORD* ppSharedWowRecord
|
|
)
|
|
{
|
|
PSHAREDWOWRECORD pSharedWowRecord = gWowHead.pSharedWowRecord;
|
|
|
|
while (NULL != pSharedWowRecord) {
|
|
if (pSharedWowRecord->dwWowExecProcessId == dwProcessId) {
|
|
break;
|
|
}
|
|
pSharedWowRecord = pSharedWowRecord->pNextSharedWow;
|
|
}
|
|
|
|
if (NULL != pSharedWowRecord) {
|
|
*ppSharedWowRecord = pSharedWowRecord;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND; // bummer, this is not found
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvGetConsoleRecordByPid (
|
|
DWORD dwProcessId,
|
|
OUT PCONSOLERECORD *pConsoleRecord
|
|
)
|
|
{
|
|
PCONSOLERECORD pConsoleHead;
|
|
|
|
pConsoleHead = DOSHead;
|
|
|
|
|
|
while (pConsoleHead) {
|
|
if (pConsoleHead->dwProcessId == dwProcessId){
|
|
*pConsoleRecord = pConsoleHead;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
pConsoleHead = pConsoleHead->Next;
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
NTSTATUS
|
|
BaseSrvAddSepWowTask(
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b,
|
|
PCONSOLERECORD pConsoleRecord
|
|
){
|
|
PDOSRECORD pDosRecord;
|
|
LPSHAREDTASK pSharedTask;
|
|
PCHAR pFilePath;
|
|
DWORD dwFilePath;
|
|
BOOL fWinExecApp = FALSE;
|
|
|
|
if(b->EnvLen != sizeof(SHAREDTASK)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(!pConsoleRecord->pfnW32HungAppNotifyThread) {
|
|
pConsoleRecord->pfnW32HungAppNotifyThread = (LPTHREAD_START_ROUTINE)b->Reserved;
|
|
}
|
|
|
|
if(b->iTask == -1) {
|
|
// this wow app was run through winexec instead of createprocess
|
|
// we need to add a record for this app.
|
|
pDosRecord = BaseSrvAllocateDOSRecord();
|
|
if(NULL == pDosRecord) {
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
pDosRecord->iTask = BaseSrvGetWOWTaskId(&gWowHead);
|
|
pDosRecord->VDMState = VDM_BUSY;
|
|
fWinExecApp = TRUE;
|
|
}
|
|
else {
|
|
pDosRecord = pConsoleRecord->DOSRecord;
|
|
}
|
|
|
|
if(pDosRecord->pFilePath) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pDosRecord->pFilePath);
|
|
pDosRecord->pFilePath = NULL;
|
|
}
|
|
|
|
pSharedTask = (LPSHAREDTASK) b->Env;
|
|
pFilePath = pSharedTask->szFilePath;
|
|
while(*pFilePath &&
|
|
pFilePath < pSharedTask->szFilePath + 127) {
|
|
pFilePath++;
|
|
}
|
|
dwFilePath = (DWORD)(pFilePath - pSharedTask->szFilePath) + 1;
|
|
|
|
if(pDosRecord->pFilePath) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pDosRecord->pFilePath);
|
|
}
|
|
pDosRecord->pFilePath = RtlAllocateHeap ( RtlProcessHeap (), MAKE_TAG( VDM_TAG ), dwFilePath);
|
|
if(!pDosRecord->pFilePath) {
|
|
if(fWinExecApp) {
|
|
BaseSrvFreeDOSRecord(pDosRecord);
|
|
}
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
|
|
pDosRecord->dwThreadId = pSharedTask->dwThreadId;
|
|
pDosRecord->hTask16 = pSharedTask->hTask16;
|
|
pDosRecord->hMod16 = pSharedTask->hMod16;
|
|
RtlCopyMemory( pDosRecord->szModName, pSharedTask->szModName,9);
|
|
RtlCopyMemory( pDosRecord->pFilePath, pSharedTask->szFilePath, dwFilePath);
|
|
pDosRecord->pFilePath[dwFilePath-1] = 0;
|
|
if(fWinExecApp) {
|
|
BaseSrvAddDOSRecord(pConsoleRecord,pDosRecord);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS BaseSrvAddSharedWowTask (
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b,
|
|
PSHAREDWOWRECORD pSharedWow
|
|
)
|
|
{
|
|
PWOWRECORD pWowRecord;
|
|
BOOL fWinExecApp = FALSE;
|
|
LPSHAREDTASK pSharedTask;
|
|
PCHAR pFilePath;
|
|
DWORD dwFilePath;
|
|
|
|
//
|
|
// Make sure source buffer is the right size.
|
|
//
|
|
|
|
if(b->EnvLen != sizeof(SHAREDTASK)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(!pSharedWow->pfnW32HungAppNotifyThread) {
|
|
pSharedWow->pfnW32HungAppNotifyThread = (LPTHREAD_START_ROUTINE)b->Reserved;
|
|
}
|
|
|
|
if(b->iTask == -1) {
|
|
// this wow app was run through winexec instead of createprocess
|
|
// we need to add a record for this app.
|
|
pWowRecord = BaseSrvAllocateWOWRecord(&gWowHead);
|
|
if(NULL == pWowRecord) {
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
pWowRecord->fDispatched = TRUE;
|
|
fWinExecApp = TRUE;
|
|
}
|
|
else {
|
|
// this wow app run through createprocess, there should be a record
|
|
// of it stored someplace.
|
|
// find it and check if it needs to be updated
|
|
|
|
pWowRecord = pSharedWow->pWOWRecord;
|
|
|
|
while (NULL != pWowRecord && pWowRecord->iTask != b->iTask){
|
|
pWowRecord = pWowRecord->WOWRecordNext;
|
|
}
|
|
if (NULL == pWowRecord) {
|
|
// Couldn't find the record,bad iTask then
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
b->iTask = pWowRecord->iTask;
|
|
pSharedTask = (LPSHAREDTASK) b->Env;
|
|
|
|
|
|
pFilePath = pSharedTask->szFilePath;
|
|
while(*pFilePath &&
|
|
pFilePath < pSharedTask->szFilePath + 127) {
|
|
pFilePath++;
|
|
}
|
|
dwFilePath = (DWORD)(pFilePath - pSharedTask->szFilePath) + 1;
|
|
|
|
if(pWowRecord->pFilePath) {
|
|
RtlFreeHeap(RtlProcessHeap (), 0, pWowRecord->pFilePath);
|
|
}
|
|
|
|
pWowRecord->pFilePath = RtlAllocateHeap ( RtlProcessHeap (), MAKE_TAG( VDM_TAG ), dwFilePath);
|
|
if(!pWowRecord->pFilePath) {
|
|
if(fWinExecApp) {
|
|
BaseSrvFreeWOWRecord(pWowRecord);
|
|
}
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
|
|
pWowRecord->dwThreadId = pSharedTask->dwThreadId;
|
|
pWowRecord->hTask16 = pSharedTask->hTask16;
|
|
pWowRecord->hMod16 = pSharedTask->hMod16;
|
|
RtlCopyMemory( pWowRecord->szModName, pSharedTask->szModName,9);
|
|
RtlCopyMemory( pWowRecord->pFilePath, pSharedTask->szFilePath, dwFilePath);
|
|
pWowRecord->pFilePath[dwFilePath-1] = 0;
|
|
|
|
if(fWinExecApp) {
|
|
BaseSrvAddWOWRecord (pSharedWow, pWowRecord);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvAddWowTask (
|
|
PCSR_API_MSG m,
|
|
ULONG SequenceNumber
|
|
){
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b = (PBASE_GET_NEXT_VDM_COMMAND_MSG)&m->u.ApiMessageData;
|
|
DWORD dwProcessId = HandleToLong( m->h.ClientId.UniqueProcess);
|
|
PCONSOLERECORD pConsoleRecord;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
NTSTATUS Status;
|
|
|
|
RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
Status = BaseSrvGetConsoleRecordByPid(dwProcessId, &pConsoleRecord);
|
|
if(NT_SUCCESS(Status)) {
|
|
if(pConsoleRecord->SequenceNumber == SequenceNumber) {
|
|
Status = BaseSrvAddSepWowTask(b, pConsoleRecord);
|
|
}
|
|
else {
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ENTER_WOW_CRITICAL();
|
|
|
|
Status = BaseSrvGetSharedWowRecordByPid(dwProcessId, &pSharedWow);
|
|
if(NT_SUCCESS(Status)) {
|
|
if(pSharedWow->SequenceNumber == SequenceNumber) {
|
|
Status = BaseSrvAddSharedWowTask(b, pSharedWow);
|
|
}
|
|
else
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
BaseSrvCheckProcessAccess(
|
|
DWORD dwProcessId,
|
|
BOOL fImpersonateClientFirst
|
|
){
|
|
BOOL fResult;
|
|
CLIENT_ID ClientId;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE ProcessHandle;
|
|
NTSTATUS Status;
|
|
|
|
if(fImpersonateClientFirst) {
|
|
fResult = CsrImpersonateClient(NULL);
|
|
if(!fResult) {
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
}
|
|
|
|
ClientId.UniqueThread = NULL;
|
|
ClientId.UniqueProcess = LongToHandle(dwProcessId);;
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Status = NtOpenProcess( &ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
&Obja,
|
|
&ClientId);
|
|
|
|
if(fImpersonateClientFirst) {
|
|
CsrRevertToSelf();
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
NtClose(ProcessHandle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumSepWow (
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b,
|
|
PCONSOLERECORD pConsoleRecord
|
|
){
|
|
PDOSRECORD pDosRecord;
|
|
LPSHAREDTASK pCurrentTask;
|
|
DWORD cbTaskArray = 0;
|
|
PCHAR pSource,pDest;
|
|
NTSTATUS Status;
|
|
|
|
pDosRecord = pConsoleRecord->DOSRecord;
|
|
|
|
|
|
RtlZeroMemory(b->Env, b->EnvLen);
|
|
|
|
pCurrentTask = (LPSHAREDTASK)b->Env;
|
|
|
|
while(pDosRecord) {
|
|
if(pDosRecord->pFilePath) {
|
|
cbTaskArray += sizeof(SHAREDTASK);
|
|
if(cbTaskArray <= b->EnvLen) {
|
|
pCurrentTask->dwThreadId = pDosRecord->dwThreadId;
|
|
pCurrentTask->hTask16 = pDosRecord->hTask16;
|
|
pCurrentTask->hMod16 = pDosRecord->hMod16;
|
|
RtlCopyMemory(pCurrentTask->szModName,pDosRecord->szModName,9);
|
|
pSource = pDosRecord->pFilePath;
|
|
pDest = pCurrentTask->szFilePath;
|
|
while(*pSource) {
|
|
*pDest++ = *pSource++;
|
|
}
|
|
pCurrentTask++;
|
|
}
|
|
}
|
|
pDosRecord = pDosRecord->DOSRecordNext;
|
|
}
|
|
|
|
Status = cbTaskArray > b->EnvLen ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
|
|
|
|
b->EnvLen = cbTaskArray;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumSharedWow (
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b,
|
|
PSHAREDWOWRECORD pSharedWow
|
|
){
|
|
PWOWRECORD pWowRecord;
|
|
LPSHAREDTASK pCurrentTask;
|
|
DWORD cbTaskArray = 0;
|
|
PCHAR pSource,pDest;
|
|
NTSTATUS Status;
|
|
|
|
pWowRecord = pSharedWow->pWOWRecord;
|
|
|
|
RtlZeroMemory(b->Env, b->EnvLen);
|
|
|
|
pCurrentTask = (LPSHAREDTASK)b->Env;
|
|
|
|
|
|
|
|
while(pWowRecord) {
|
|
if(pWowRecord->pFilePath) {
|
|
cbTaskArray += sizeof(SHAREDTASK);
|
|
if(cbTaskArray <= b->EnvLen) {
|
|
pCurrentTask->dwThreadId = pWowRecord->dwThreadId;
|
|
pCurrentTask->hTask16 = pWowRecord->hTask16;
|
|
pCurrentTask->hMod16 = pWowRecord->hMod16;
|
|
RtlCopyMemory(pCurrentTask->szModName,pWowRecord->szModName,9);
|
|
pSource = pWowRecord->pFilePath;
|
|
pDest = pCurrentTask->szFilePath;
|
|
while(*pSource) {
|
|
*pDest++ = *pSource++;
|
|
}
|
|
pCurrentTask++;
|
|
}
|
|
}
|
|
pWowRecord = pWowRecord->WOWRecordNext;
|
|
}
|
|
|
|
Status = cbTaskArray > b->EnvLen ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
|
|
|
|
b->EnvLen = cbTaskArray;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumWowTask (
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b
|
|
){
|
|
PCONSOLERECORD pConsoleRecord;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
BOOL fReturnSize = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
Status = BaseSrvCheckProcessAccess(b->iTask,TRUE);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
Status = BaseSrvGetConsoleRecordByPid(b->iTask,&pConsoleRecord);
|
|
if(NT_SUCCESS(Status)) {
|
|
Status = BaseSrvEnumSepWow(b,
|
|
pConsoleRecord);
|
|
}
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
if(NT_SUCCESS(Status)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ENTER_WOW_CRITICAL();
|
|
Status = BaseSrvGetSharedWowRecordByPid(b->iTask,&pSharedWow);
|
|
if(NT_SUCCESS(Status)) {
|
|
Status = BaseSrvEnumSharedWow(b,
|
|
pSharedWow);
|
|
}
|
|
LEAVE_WOW_CRITICAL();
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BaseSrvEnumWowProcess(
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG b
|
|
){
|
|
LPSHAREDPROCESS pSharedProcess;
|
|
PSHAREDWOWRECORD pSharedWow;
|
|
PCONSOLERECORD pConsoleRecord;
|
|
NTSTATUS Status;
|
|
DWORD cbProcArray = 0;
|
|
BOOL fImpersonate;
|
|
|
|
RtlZeroMemory(b->Env, b->EnvLen);
|
|
|
|
if(b->iTask) {
|
|
if(!NT_SUCCESS(BaseSrvCheckProcessAccess(b->iTask,TRUE))) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
if(b->EnvLen != sizeof(SHAREDPROCESS)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ENTER_WOW_CRITICAL();
|
|
|
|
Status = BaseSrvGetSharedWowRecordByPid(b->iTask,&pSharedWow);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
pSharedProcess = (LPSHAREDPROCESS)b->Env;
|
|
pSharedProcess->dwProcessId = pSharedWow->dwWowExecProcessId;
|
|
pSharedProcess->dwAttributes = WOW_SYSTEM;
|
|
pSharedProcess->pfnW32HungAppNotifyThread =pSharedWow->pfnW32HungAppNotifyThread;
|
|
}
|
|
LEAVE_WOW_CRITICAL();
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
Status = BaseSrvGetConsoleRecordByPid(b->iTask,&pConsoleRecord);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
pSharedProcess = (LPSHAREDPROCESS)b->Env;
|
|
pSharedProcess->dwProcessId = pConsoleRecord->dwProcessId;
|
|
pSharedProcess->dwAttributes = 0;
|
|
pSharedProcess->pfnW32HungAppNotifyThread = pConsoleRecord->pfnW32HungAppNotifyThread;
|
|
}
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
fImpersonate = CsrImpersonateClient(NULL);
|
|
if (!fImpersonate) {
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
|
|
ENTER_WOW_CRITICAL();
|
|
|
|
pSharedProcess = (LPSHAREDPROCESS)b->Env;
|
|
pSharedWow = gWowHead.pSharedWowRecord;
|
|
|
|
while(pSharedWow) {
|
|
Status =BaseSrvCheckProcessAccess(
|
|
pSharedWow->dwWowExecProcessId,FALSE);
|
|
if(NT_SUCCESS(Status)) {
|
|
cbProcArray += sizeof(SHAREDPROCESS);
|
|
if(cbProcArray <= b->EnvLen) {
|
|
pSharedProcess->dwProcessId = pSharedWow->dwWowExecProcessId;
|
|
pSharedProcess->dwAttributes = WOW_SYSTEM;
|
|
pSharedProcess->pfnW32HungAppNotifyThread =pSharedWow->pfnW32HungAppNotifyThread;
|
|
pSharedProcess++;
|
|
}
|
|
}
|
|
pSharedWow = pSharedWow->pNextSharedWow;
|
|
}
|
|
LEAVE_WOW_CRITICAL();
|
|
|
|
RtlEnterCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
pConsoleRecord = DOSHead;
|
|
while(pConsoleRecord) {
|
|
|
|
// jarbats. 02-04-26
|
|
// The only difference between separate wow and regular dos console records
|
|
// is that DOSRecord->pFilePath is not NULL for a separate wow (initialized
|
|
// in BaseSrvAddSepWow) whereas for dos it is always NULL.
|
|
|
|
if(pConsoleRecord->DOSRecord && pConsoleRecord->DOSRecord->pFilePath) {
|
|
Status =BaseSrvCheckProcessAccess(
|
|
pConsoleRecord->dwProcessId,FALSE);
|
|
if(NT_SUCCESS(Status)) {
|
|
cbProcArray += sizeof(SHAREDPROCESS);
|
|
if(cbProcArray <= b->EnvLen) {
|
|
pSharedProcess->dwProcessId = pConsoleRecord->dwProcessId;
|
|
pSharedProcess->dwAttributes = 0;
|
|
pSharedProcess->pfnW32HungAppNotifyThread = pConsoleRecord->pfnW32HungAppNotifyThread;
|
|
pSharedProcess++;
|
|
}
|
|
}
|
|
}
|
|
pConsoleRecord = pConsoleRecord->Next;
|
|
}
|
|
RtlLeaveCriticalSection( &BaseSrvDOSCriticalSection );
|
|
|
|
CsrRevertToSelf();
|
|
|
|
Status = cbProcArray > b->EnvLen ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
|
|
|
|
b->EnvLen = cbProcArray;
|
|
|
|
return Status;
|
|
}
|