diff --git a/reactos/dll/win32/kernel32/client/proc.c b/reactos/dll/win32/kernel32/client/proc.c index 47d913d07c4..f844b8b17db 100644 --- a/reactos/dll/win32/kernel32/client/proc.c +++ b/reactos/dll/win32/kernel32/client/proc.c @@ -52,7 +52,6 @@ RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle); /* FUNCTIONS ****************************************************************/ -// NOTE: Code duplicated from BasepDuplicateAndWriteHandle VOID WINAPI StuffStdHandle(IN HANDLE ProcessHandle, @@ -68,7 +67,8 @@ StuffStdHandle(IN HANDLE ProcessHandle, StandardHandle, ProcessHandle, &DuplicatedHandle, - 0, 0, + 0, + 0, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES); if (NT_SUCCESS(Status)) { @@ -83,9 +83,9 @@ StuffStdHandle(IN HANDLE ProcessHandle, BOOLEAN WINAPI -BuildSubSysCommandLine(IN LPWSTR SubsystemName, - IN LPWSTR ApplicationName, - IN LPWSTR CommandLine, +BuildSubSysCommandLine(IN LPCWSTR SubsystemName, + IN LPCWSTR ApplicationName, + IN LPCWSTR CommandLine, OUT PUNICODE_STRING SubsysCommandLine) { UNICODE_STRING CommandLineString, ApplicationNameString; @@ -184,9 +184,9 @@ BasepConfigureAppCertDlls(IN PWSTR ValueName, NTSTATUS WINAPI -BasepIsProcessAllowed(IN PCHAR ApplicationName) +BasepIsProcessAllowed(IN LPWSTR ApplicationName) { - NTSTATUS Status; + NTSTATUS Status, Status1; PWCHAR Buffer; UINT Length; HMODULE TrustLibrary; @@ -250,19 +250,22 @@ BasepIsProcessAllowed(IN PCHAR ApplicationName) else { /* Other systems have a registry entry for this */ - Status = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes); - if (NT_SUCCESS(Status)) + Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes); + if (NT_SUCCESS(Status1)) { /* Close it, we'll query it through Rtl */ NtClose(KeyHandle); /* Do the query, which will call a special callback */ - Status = RtlQueryRegistryValues(2, + Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, L"Session Manager", BasepAppCertTable, - 0, - 0); - if (Status == 0xC0000034) Status = STATUS_SUCCESS; + NULL, + NULL); + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + { + Status = STATUS_SUCCESS; + } } } @@ -519,237 +522,6 @@ BasepNotifyCsrOfThread(IN HANDLE ThreadHandle, return STATUS_SUCCESS; } -/* - * Creates the first Thread in a Proces - */ -HANDLE -WINAPI -BasepCreateFirstThread(HANDLE ProcessHandle, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - PSECTION_IMAGE_INFORMATION SectionImageInfo, - PCLIENT_ID ClientId, - DWORD dwCreationFlags) -{ - NTSTATUS Status; - OBJECT_ATTRIBUTES LocalObjectAttributes; - POBJECT_ATTRIBUTES ObjectAttributes; - CONTEXT Context; - INITIAL_TEB InitialTeb; - HANDLE hThread; - BASE_API_MESSAGE ApiMessage; - PBASE_CREATE_PROCESS CreateProcessRequest = &ApiMessage.Data.CreateProcessRequest; - - DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle); - - /* Create the Thread's Stack */ - BaseCreateStack(ProcessHandle, - SectionImageInfo->MaximumStackSize, - SectionImageInfo->CommittedStackSize, - &InitialTeb); - - /* Create the Thread's Context */ - BaseInitializeContext(&Context, - NtCurrentPeb(), - SectionImageInfo->TransferAddress, - InitialTeb.StackBase, - 0); - - /* Convert the thread attributes */ - ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, - lpThreadAttributes, - NULL); - - /* Create the Kernel Thread Object */ - Status = NtCreateThread(&hThread, - THREAD_ALL_ACCESS, - ObjectAttributes, - ProcessHandle, - ClientId, - &Context, - &InitialTeb, - TRUE); - if (!NT_SUCCESS(Status)) - { - return NULL; - } - - /* Fill out the request to notify CSRSS */ - CreateProcessRequest->ClientId = *ClientId; - CreateProcessRequest->ProcessHandle = ProcessHandle; - CreateProcessRequest->ThreadHandle = hThread; - CreateProcessRequest->CreationFlags = dwCreationFlags; - - /* - * For GUI applications we turn on the 2nd bit. This also allows - * us to know whether or not this is a GUI or a TUI application. - */ - if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo->SubSystemType) - { - CreateProcessRequest->ProcessHandle = (HANDLE) - ((ULONG_PTR)CreateProcessRequest->ProcessHandle | 2); - } - - /* Call CSR */ - Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, - NULL, - CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), - sizeof(BASE_CREATE_PROCESS)); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to tell CSRSS about new process: %lx\n", Status); - return NULL; - } - - /* Success */ - return hThread; -} - -/* - * Converts ANSI to Unicode Environment - */ -PVOID -WINAPI -BasepConvertUnicodeEnvironment(OUT SIZE_T* EnvSize, - IN PVOID lpEnvironment) -{ - PCHAR pcScan; - ANSI_STRING AnsiEnv; - UNICODE_STRING UnicodeEnv; - NTSTATUS Status; - - DPRINT("BasepConvertUnicodeEnvironment\n"); - - /* Scan the environment to calculate its Unicode size */ - AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment; - while (*pcScan) - { - pcScan += strlen(pcScan) + 1; - } - - /* Create our ANSI String */ - if (pcScan == (PCHAR)lpEnvironment) - { - AnsiEnv.Length = 2 * sizeof(CHAR); - } - else - { - - AnsiEnv.Length = (USHORT)((ULONG_PTR)pcScan - (ULONG_PTR)lpEnvironment + sizeof(CHAR)); - } - AnsiEnv.MaximumLength = AnsiEnv.Length + 1; - - /* Allocate memory for the Unicode Environment */ - UnicodeEnv.Buffer = NULL; - *EnvSize = AnsiEnv.MaximumLength * sizeof(WCHAR); - Status = NtAllocateVirtualMemory(NtCurrentProcess(), - (PVOID)&UnicodeEnv.Buffer, - 0, - EnvSize, - MEM_COMMIT, - PAGE_READWRITE); - /* Failure */ - if (!NT_SUCCESS(Status)) - { - SetLastError(Status); - *EnvSize = 0; - return NULL; - } - - /* Use the allocated size */ - UnicodeEnv.MaximumLength = (USHORT)*EnvSize; - - /* Convert */ - RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE); - return UnicodeEnv.Buffer; -} - -/* - * Converts a Win32 Priority Class to NT - */ -ULONG -WINAPI -BasepConvertPriorityClass(IN ULONG dwCreationFlags) -{ - ULONG ReturnClass; - - if(dwCreationFlags & IDLE_PRIORITY_CLASS) - { - ReturnClass = PROCESS_PRIORITY_CLASS_IDLE; - } - else if(dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS) - { - ReturnClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; - } - else if(dwCreationFlags & NORMAL_PRIORITY_CLASS) - { - ReturnClass = PROCESS_PRIORITY_CLASS_NORMAL; - } - else if(dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS) - { - ReturnClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; - } - else if(dwCreationFlags & HIGH_PRIORITY_CLASS) - { - ReturnClass = PROCESS_PRIORITY_CLASS_HIGH; - } - else if(dwCreationFlags & REALTIME_PRIORITY_CLASS) - { - /* Check for Privilege First */ - if (BasepIsRealtimeAllowed(TRUE)) - { - ReturnClass = PROCESS_PRIORITY_CLASS_REALTIME; - } - else - { - ReturnClass = PROCESS_PRIORITY_CLASS_HIGH; - } - } - else - { - ReturnClass = PROCESS_PRIORITY_CLASS_INVALID; - } - - return ReturnClass; -} - -/* - * Duplicates a standard handle and writes it where requested. - */ -// NOTE: Code duplicated from StuffStdHandle -VOID -WINAPI -BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle, - IN HANDLE StandardHandle, - IN PHANDLE Address) -{ - NTSTATUS Status; - HANDLE DuplicatedHandle; - SIZE_T Dummy; - - DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx," - "Address: %p\n", ProcessHandle, StandardHandle, Address); - - /* Don't touch Console Handles */ - if (IsConsoleHandle(StandardHandle)) return; - - /* Duplicate the handle */ - Status = NtDuplicateObject(NtCurrentProcess(), - StandardHandle, - ProcessHandle, - &DuplicatedHandle, - 0, 0, - DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES); - if (NT_SUCCESS(Status)) - { - /* Write it */ - NtWriteVirtualMemory(ProcessHandle, - Address, - &DuplicatedHandle, - sizeof(HANDLE), - &Dummy); - } -} - BOOLEAN WINAPI BasePushProcessParameters(IN ULONG ParameterFlags, @@ -854,6 +626,8 @@ BasePushProcessParameters(IN ULONG ParameterFlags, /* Create the Parameter Block */ ProcessParameters = NULL; + DPRINT1("Image Name: %wZ Dll Path: %wZ current directory: %wZ, CmdLine: %wZ, Title: %wZ, Desktop: %wZ, Shell: %wZ, Runtime: %wZ\n", + &ImageName, &DllPath, &CurrentDirectory, &CommandLine, &Title, &Desktop, &Shell, &Runtime); Status = RtlCreateProcessParameters(&ProcessParameters, &ImageName, &DllPath, @@ -992,16 +766,16 @@ BasePushProcessParameters(IN ULONG ParameterFlags, ProcessParameters->ConsoleFlags = 1; } - /* See if the first 1MB should be reserved */ + /* Check if there's a .local file present */ if (ParameterFlags & 1) { - ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB; + ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH; } - /* See if the first 16MB should be reserved */ + /* Check if we failed to open the IFEO key */ if (ParameterFlags & 2) { - ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_16MB; + ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING; } /* Allocate memory for the parameter block */ @@ -1095,7 +869,7 @@ Quickie: if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters); return Result; FailPath: - DPRINT1("Failure to create proecss parameters: %lx\n", Status); + DPRINT1("Failure to create process parameters: %lx\n", Status); BaseSetLastNTError(Status); Result = FALSE; goto Quickie; @@ -2480,89 +2254,216 @@ ProcessIdToSessionId(IN DWORD dwProcessId, return FALSE; } + +#define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y)); +#define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y)); +C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1)); + /* * @implemented */ BOOL WINAPI -CreateProcessInternalW(HANDLE hToken, - LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - PHANDLE hNewToken) +CreateProcessInternalW(IN HANDLE hUserToken, + IN LPCWSTR lpApplicationName, + IN LPWSTR lpCommandLine, + IN LPSECURITY_ATTRIBUTES lpProcessAttributes, + IN LPSECURITY_ATTRIBUTES lpThreadAttributes, + IN BOOL bInheritHandles, + IN DWORD dwCreationFlags, + IN LPVOID lpEnvironment, + IN LPCWSTR lpCurrentDirectory, + IN LPSTARTUPINFOW lpStartupInfo, + IN LPPROCESS_INFORMATION lpProcessInformation, + OUT PHANDLE hNewToken) { - NTSTATUS Status; - PROCESS_PRIORITY_CLASS PriorityClass; - BOOLEAN FoundQuotes = FALSE; - BOOLEAN QuotesNeeded = FALSE; - BOOLEAN CmdLineIsAppName = FALSE; - UNICODE_STRING ApplicationName = { 0, 0, NULL }; + // + // Core variables used for creating the initial process and thread + // + SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes; OBJECT_ATTRIBUTES LocalObjectAttributes; POBJECT_ATTRIBUTES ObjectAttributes; - HANDLE hSection = NULL, hProcess = NULL, hThread = NULL, hDebug = NULL; - SECTION_IMAGE_INFORMATION SectionImageInfo; - LPWSTR CurrentDirectory = NULL; - LPWSTR CurrentDirectoryPart; - PROCESS_BASIC_INFORMATION ProcessBasicInfo; - STARTUPINFOW StartupInfo; - ULONG Dummy; - LPWSTR BatchCommandLine; - ULONG CmdLineLength; - UNICODE_STRING CommandLineString; - PWCHAR Extension; - LPWSTR QuotedCmdLine = NULL; - LPWSTR ScanString; - LPWSTR NullBuffer = NULL; - LPWSTR NameBuffer = NULL; - WCHAR SaveChar = 0; - ULONG RetVal; - UINT Error = 0; - BOOLEAN SearchDone = FALSE; - BOOLEAN Escape = FALSE; + SECTION_IMAGE_INFORMATION ImageInformation; + IO_STATUS_BLOCK IoStatusBlock; CLIENT_ID ClientId; - PPEB OurPeb = NtCurrentPeb(); - PPEB RemotePeb; - SIZE_T EnvSize = 0; - BOOL Ret = FALSE; + ULONG NoWindow, RegionSize, StackSize, ImageMachine, dwErrCode, Flags; + ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse; + ULONG_PTR ErrorParameters[2]; + BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege; + BOOLEAN QuerySection, SkipSaferAndAppCompat; + CONTEXT Context; + BASE_API_MESSAGE CsrMsg; + PBASE_CREATE_PROCESS CreateProcessMsg; + PCSR_CAPTURE_BUFFER CaptureBuffer; + PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState; + HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle; + HANDLE FileHandle, SectionHandle, ProcessHandle; + ULONG ResumeCount; + PROCESS_PRIORITY_CLASS PriorityClass; + NTSTATUS Status, Status1, ImageDbgStatus; + PPEB Peb, RemotePeb; + PTEB Teb; + INITIAL_TEB InitialTeb; + PVOID TibValue; + PIMAGE_NT_HEADERS NtHeaders; + STARTUPINFOW StartupInfo; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + UNICODE_STRING DebuggerString; + BOOL Result; + // + // Variables used for command-line and argument parsing + // + PCHAR pcScan; + SIZE_T n; + WCHAR TempChar; + ULONG Length, CurdirLength, CmdQuoteLength; + ULONG CmdLineLength, ResultSize; + PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory; + PWCHAR TempNull, WhiteScan, NameBuffer, SearchPath, DebuggerCmdLine; + ANSI_STRING AnsiEnv; + UNICODE_STRING UnicodeEnv, PathName; + BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName; - /* FIXME should process - * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options - * key (see http://blogs.msdn.com/oldnewthing/archive/2005/12/19/505449.aspx) - */ + // + // Variables used for Fusion/SxS (Side-by-Side Assemblies) + // + RTL_PATH_TYPE SxsPathType, PathType; +#if _SXS_SUPPORT_ENABLED_ + PRTL_BUFFER ByteBuffer; + PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5]; + PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer; + RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath; + RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath; + RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory; + BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles; + PVOID CapturedStrings[3]; + SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair; + SXS_OVERRIDE_MANIFEST OverrideMannifest; + UNICODE_STRING FreeString, SxsNtExePath; + PWCHAR SxsConglomeratedBuffer, StaticBuffer; + ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i; +#endif + ULONG FusionFlags; - DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S" - " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n", - lpApplicationName, lpCommandLine, lpEnvironment, lpCurrentDirectory, - dwCreationFlags); + // + // Variables used for path conversion (and partially Fusion/SxS) + // + PWCHAR FilePart, PathBuffer, FreeBuffer; + BOOLEAN TranslationStatus; + RTL_RELATIVE_NAME_U SxsWin32RelativePath; + UNICODE_STRING PathBufferString, SxsWin32ExePath; - /* Flags we don't handle yet */ - if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) - { - DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n"); - } - if (dwCreationFlags & CREATE_SHARED_WOW_VDM) - { - DPRINT1("CREATE_SHARED_WOW_VDM not handled\n"); - } - if (dwCreationFlags & CREATE_FORCEDOS) - { - DPRINT1("CREATE_FORCEDOS not handled\n"); - } + // + // Variables used by Application Compatibility (and partially Fusion/SxS) + // + PVOID AppCompatSxsData, AppCompatData; + ULONG AppCompatSxsDataSize, AppCompatDataSize; + // + // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support) + // + ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve; + ULONG VdmUndoLevel; + BOOLEAN UseVdmReserve; + HANDLE VdmWaitObject; + ANSI_STRING VdmAnsiEnv; + UNICODE_STRING VdmString, VdmUnicodeEnv; + BOOLEAN IsWowApp; + PBASE_CHECK_VDM VdmMsg; - /* Fail on this flag, it's only valid with the WithLogonW function */ - if (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL) - { - DPRINT1("Invalid flag used\n"); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } + /* Zero out the initial core variables and handles */ + QuerySection = FALSE; + InJob = FALSE; + SkipSaferAndAppCompat = FALSE; + ParameterFlags = 0; + Flags = 0; + DebugHandle = NULL; + JobHandle = NULL; + TokenHandle = NULL; + FileHandle = NULL; + SectionHandle = NULL; + ProcessHandle = NULL; + ThreadHandle = NULL; + BaseAddress = (PVOID)1; + + /* Zero out initial SxS and Application Compatibility state */ + AppCompatData = NULL; + AppCompatDataSize = 0; + AppCompatSxsData = NULL; + AppCompatSxsDataSize = 0; + CaptureBuffer = NULL; +#if _SXS_SUPPORT_ENABLED_ + SxsConglomeratedBuffer = NULL; +#endif + FusionFlags = 0; + + /* Zero out initial parsing variables -- others are initialized later */ + DebuggerCmdLine = NULL; + PathBuffer = NULL; + SearchPath = NULL; + TempNull = 0; + FreeBuffer = NULL; + NameBuffer = NULL; + CurrentDirectory = NULL; + FilePart = NULL; + DebuggerString.Buffer = NULL; + QuotesNeeded = FALSE; + QuotedCmdLine = NULL; + + /* Zero out initial VDM state */ + VdmAnsiEnv.Buffer = NULL; + VdmUnicodeEnv.Buffer = NULL; + VdmString.Buffer = NULL; + VdmTask = 0; + VdmUndoLevel = 0; + VdmBinaryType = 0; + VdmReserve = 0; + VdmWaitObject = NULL; + UseVdmReserve = FALSE; + IsWowApp = FALSE; + + /* Set message structures */ + CreateProcessMsg = &CsrMsg.Data.CreateProcessRequest; + VdmMsg = &CsrMsg.Data.CheckVdm; + + /* Clear the more complex structures by zeroing out their entire memory */ + RtlZeroMemory(&Context, sizeof(Context)); +#if _SXS_SUPPORT_ENABLED_ + RtlZeroMemory(&FileHandles, sizeof(FileHandles)); + RtlZeroMemory(&MappedHandles, sizeof(MappedHandles)); + RtlZeroMemory(&Handles, sizeof(Handles)); +#endif + RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs)); + RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes)); + RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes)); + + /* Zero out output arguments as well */ + RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation)); + if (hNewToken) *hNewToken = NULL; + + /* Capture the special window flag */ + NoWindow = dwCreationFlags & CREATE_NO_WINDOW; + dwCreationFlags &= ~CREATE_NO_WINDOW; + +#if _SXS_SUPPORT_ENABLED_ + /* Setup the SxS static string arrays and buffers */ + SxsStaticBuffers[0] = &SxsWin32ManifestPath; + SxsStaticBuffers[1] = &SxsWin32PolicyPath; + SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory; + SxsStaticBuffers[3] = &SxsNtManifestPath; + SxsStaticBuffers[4] = &SxsNtPolicyPath; + ExePathPair.Win32 = &SxsWin32ExePath; + ExePathPair.Nt = &SxsNtExePath; + ManifestPathPair.Win32 = &SxsWin32ManifestPath.String; + ManifestPathPair.Nt = &SxsNtManifestPath.String; + PolicyPathPair.Win32 = &SxsWin32PolicyPath.String; + PolicyPathPair.Nt = &SxsNtPolicyPath.String; +#endif + + DPRINT1("CreateProcessInternalW: %S %S %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags); + + /* Finally, set our TEB and PEB */ + Teb = NtCurrentTeb(); + Peb = NtCurrentPeb(); /* This combination is illegal (see MSDN) */ if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) == @@ -2573,445 +2474,1349 @@ CreateProcessInternalW(HANDLE hToken, return FALSE; } - /* Another illegal combo */ - if ((dwCreationFlags & (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM)) == - (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM)) + /* Convert the priority class */ + if (dwCreationFlags & IDLE_PRIORITY_CLASS) { - DPRINT1("Invalid flag combo used\n"); + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; + } + else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS) + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; + } + else if (dwCreationFlags & NORMAL_PRIORITY_CLASS) + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; + } + else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS) + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; + } + else if (dwCreationFlags & HIGH_PRIORITY_CLASS) + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; + } + else if (dwCreationFlags & REALTIME_PRIORITY_CLASS) + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; + PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(0) != NULL); + } + else + { + PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID; + } + + /* Done with the priority masks, so get rid of them */ + PriorityClass.Foreground = FALSE; + dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS | + IDLE_PRIORITY_CLASS | + HIGH_PRIORITY_CLASS | + REALTIME_PRIORITY_CLASS | + BELOW_NORMAL_PRIORITY_CLASS | + ABOVE_NORMAL_PRIORITY_CLASS); + + /* You cannot request both a shared and a separate WoW VDM */ + if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) && + (dwCreationFlags & CREATE_SHARED_WOW_VDM)) + { + /* Fail such nonsensical attempts */ + DPRINT1("Invalid WOW flags\n"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - - if (lpCurrentDirectory) + else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) && + (BaseStaticServerData->DefaultSeparateVDM)) { - if ((GetFileAttributesW(lpCurrentDirectory) == INVALID_FILE_ATTRIBUTES) || - !(GetFileAttributesW(lpCurrentDirectory) & FILE_ATTRIBUTE_DIRECTORY)) + /* A shared WoW VDM was not requested but system enforces separation */ + dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; + } + + /* If a shared WoW VDM is used, make sure the process isn't in a job */ + if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) && + (NtIsProcessInJob(NtCurrentProcess(), NULL))) + { + /* Remove the shared flag and add the separate flag */ + dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) | + CREATE_SEPARATE_WOW_VDM; + } + + /* Convert the environment */ + if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + { + /* Scan the environment to calculate its Unicode size */ + AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment; + while ((*pcScan) || (*(pcScan + 1))) ++pcScan; + + /* Create our ANSI String */ + AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL); + AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL); + + /* Allocate memory for the Unicode Environment */ + UnicodeEnv.Buffer = NULL; + RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR); + Status = NtAllocateVirtualMemory(NtCurrentProcess(), + (PVOID)&UnicodeEnv.Buffer, + 0, + &RegionSize, + MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) { - SetLastError(ERROR_DIRECTORY); + /* Fail */ + BaseSetLastNTError(Status); return FALSE; } + + /* Use the allocated size and convert */ + UnicodeEnv.MaximumLength = RegionSize; + Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + NtFreeVirtualMemory(NtCurrentProcess(), + (PVOID)&UnicodeEnv.Buffer, + &RegionSize, + MEM_RELEASE); + BaseSetLastNTError(Status); + return FALSE; + } + + /* Now set the Unicode environment as the environment string pointer */ + lpEnvironment = UnicodeEnv.Buffer; } - /* - * We're going to modify and mask out flags and stuff in lpStartupInfo, - * so we'll use our own local copy for that. - */ + /* Make a copy of the caller's startup info since we'll modify it */ StartupInfo = *lpStartupInfo; - /* FIXME: Use default Separate/Shared VDM Flag */ - - /* If we are inside a Job, use Separate VDM so it won't escape the Job */ - if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) - { - if (NtIsProcessInJob(NtCurrentProcess(), NULL)) - { - /* Remove the shared flag and add the separate flag. */ - dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) | - CREATE_SEPARATE_WOW_VDM; - } - } - - /* - * According to some sites, ShellExecuteEx uses an undocumented flag to - * send private handle data (such as HMONITOR or HICON). See: - * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the - * standard handles anymore since we'd be overwriting this private data - */ + /* Check if private data is being sent on the same channel as std handles */ if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) && (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))) { + /* Cannot use the std handles since we have monitor/hotkey values */ StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; } - /* Start by zeroing out the fields */ - RtlZeroMemory(lpProcessInformation, sizeof(PROCESS_INFORMATION)); - - /* Easy stuff first, convert the process priority class */ - PriorityClass.Foreground = FALSE; - PriorityClass.PriorityClass = (UCHAR)BasepConvertPriorityClass(dwCreationFlags); - - if (lpCommandLine) + /* If there's a debugger, or we have to launch cmd.exe, we go back here */ +AppNameRetry: + /* New iteration -- free any existing name buffer */ + if (NameBuffer) { - /* Search for escape sequences */ - ScanString = lpCommandLine; - while (NULL != (ScanString = wcschr(ScanString, L'^'))) - { - ScanString++; - if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\') - { - Escape = TRUE; - break; - } - } + RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); + NameBuffer = NULL; } - /* Get the application name and do all the proper formating necessary */ -GetAppName: - /* See if we have an application name (oh please let us have one!) */ + /* New iteration -- free any existing free buffer */ + if (FreeBuffer) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); + FreeBuffer = NULL; + } + + /* New iteration -- close any existing file handle */ + if (FileHandle) + { + NtClose(FileHandle); + FileHandle = NULL; + } + + /* Set the initial parsing state. This code can loop -- don't move this! */ + dwErrCode = FALSE; + SearchRetry = TRUE; + QuotesNeeded = FALSE; + CmdLineIsAppName = FALSE; + + /* First check if we don't have an application name */ if (!lpApplicationName) { - /* The fun begins */ + /* This should be the first time we attempt creating one */ + ASSERT(NameBuffer == NULL); + + /* Allocate a buffer to hold it */ NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR)); - if (NameBuffer == NULL) + if (!NameBuffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto Cleanup; + Result = FALSE; + goto Quickie; } - /* This is all we have to work with :( */ - lpApplicationName = lpCommandLine; + /* Initialize the application name and our parsing parameters */ + lpApplicationName = TempNull = WhiteScan = lpCommandLine; - /* Initialize our friends at the beginning */ - NullBuffer = (LPWSTR)lpApplicationName; - ScanString = (LPWSTR)lpApplicationName; - - /* We will start by looking for a quote */ - if (*ScanString == L'\"') + /* Check for an initial quote*/ + if (*lpCommandLine == L'\"') { - /* That was quick */ - SearchDone = TRUE; - - /* Advance past quote */ - ScanString++; - lpApplicationName = ScanString; - - /* Find the closing quote */ - while (*ScanString) - { - if (*ScanString == L'\"' && *(ScanString - 1) != L'^') - { - /* Found it */ - NullBuffer = ScanString; - FoundQuotes = TRUE; - break; - } - - /* Keep looking */ - ScanString++; - NullBuffer = ScanString; - } - } - else - { - /* No quotes, so we'll be looking for white space */ - WhiteScan: - /* Reset the pointer */ - lpApplicationName = lpCommandLine; - - /* Find whitespace of Tab */ - while (*ScanString) + /* We found a quote, keep searching for another one */ + SearchRetry = FALSE; + WhiteScan++; + lpApplicationName = TempNull = WhiteScan; + while (*WhiteScan) { - if (*ScanString == ' ' || *ScanString == '\t') + /* Have we found the terminating quote? */ + if (*WhiteScan == L'\"') { - /* Found it */ - NullBuffer = ScanString; + /* We're done, get out of here */ + TempNull = WhiteScan; + QuotesNeeded = TRUE; break; } - /* Keep looking */ - ScanString++; - NullBuffer = ScanString; + /* Keep searching for the quote */ + WhiteScan++; + TempNull = WhiteScan; + } + } + else + { +StartScan: + /* We simply make the application name be the command line*/ + lpApplicationName = lpCommandLine; + while (*WhiteScan) + { + /* Check if it starts with a space or tab */ + if ((*WhiteScan == L' ') || (*WhiteScan == '\t')) + { + /* Break out of the search loop */ + TempNull = WhiteScan; + break; + } + + /* Keep searching for a space or tab */ + WhiteScan++; + TempNull = WhiteScan; } } - /* Set the Null Buffer */ - SaveChar = *NullBuffer; - *NullBuffer = UNICODE_NULL; + /* We have found the end of the application name, terminate it */ + TempChar = *TempNull; + *TempNull = UNICODE_NULL; - /* Do a search for the file */ - DPRINT("Ready for SearchPathW: %S\n", lpApplicationName); - RetVal = SearchPathW(NULL, + /* New iteration -- free any existing saved path */ + if (SearchPath) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath); + SearchPath = NULL; + } + + /* Now compute the final EXE path based on the name */ + SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName); + DPRINT1("Search Path: %S\n", SearchPath); + if (!SearchPath) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + Result = FALSE; + goto Quickie; + } + + /* And search for the executable in the search path */ + Length = SearchPathW(SearchPath, lpApplicationName, L".exe", MAX_PATH, NameBuffer, - NULL) * sizeof(WCHAR); + NULL); - /* Did it find something? */ - if (RetVal) + /* Did we find it? */ + if ((Length) && (Length < MAX_PATH)) { /* Get file attributes */ - ULONG Attributes = GetFileAttributesW(NameBuffer); - if (Attributes & FILE_ATTRIBUTE_DIRECTORY) + CurdirLength = GetFileAttributesW(NameBuffer); + if ((CurdirLength != 0xFFFFFFFF) && + (CurdirLength & FILE_ATTRIBUTE_DIRECTORY)) { - /* Give it a length of 0 to fail, this was a directory. */ - RetVal = 0; - } - else - { - /* It's a file! */ - RetVal += sizeof(WCHAR); + /* This was a directory, fail later on */ + Length = 0; } } - - /* Now check if we have a file, and if the path size is OK */ - if (!RetVal || RetVal >= (MAX_PATH * sizeof(WCHAR))) + else { - ULONG PathType; - HANDLE hFile; + /* It's a file! */ + Length++; + } - /* We failed, try to get the Path Type */ - DPRINT("SearchPathW failed. Retval: %ld\n", RetVal); + DPRINT1("Length: %lx Buffer: %S\n", Length, NameBuffer); + + /* Check if there was a failure in SearchPathW */ + if ((Length) && (Length < MAX_PATH)) + { + /* Everything looks good, restore the name */ + *TempNull = TempChar; + lpApplicationName = NameBuffer; + } + else + { + /* Check if this was a relative path, which would explain it */ PathType = RtlDetermineDosPathNameType_U(lpApplicationName); - - /* If it's not relative, try to get the error */ if (PathType != RtlPathTypeRelative) { /* This should fail, and give us a detailed LastError */ - hFile = CreateFileW(lpApplicationName, - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - /* Did it actually NOT fail? */ - if (hFile != INVALID_HANDLE_VALUE) + FileHandle = CreateFileW(lpApplicationName, + GENERIC_READ, + FILE_SHARE_READ | + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (FileHandle != INVALID_HANDLE_VALUE) { - /* Fake the error */ - CloseHandle(hFile); + /* It worked? Return a generic error */ + CloseHandle(FileHandle); + FileHandle = NULL; BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); } } else { - /* Immediately set the error */ + /* Path was absolute, which means it doesn't exist */ BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); } /* Did we already fail once? */ - if (Error) + if (dwErrCode) { - SetLastError(Error); + /* Set the error code */ + SetLastError(dwErrCode); } else { /* Not yet, cache it */ - Error = GetLastError(); + dwErrCode = GetLastError(); } /* Put back the command line */ - *NullBuffer = SaveChar; + *TempNull = TempChar; lpApplicationName = NameBuffer; - /* - * If the search isn't done and we still have cmdline - * then start over. Ex: c:\ha ha ha\haha.exe - */ - if (*ScanString && !SearchDone) + /* It's possible there's whitespace in the directory name */ + if (!(*WhiteScan) || !(SearchRetry)) { - /* Move in the buffer */ - ScanString++; - NullBuffer = ScanString; - - /* We will have to add a quote, since there is a space*/ - QuotesNeeded = TRUE; - - /* And we will also fake the fact we found one */ - FoundQuotes = TRUE; - - /* Start over */ - goto WhiteScan; + /* Not the case, give up completely */ + Result = FALSE; + goto Quickie; } - /* We totally failed */ - goto Cleanup; - } + /* There are spaces, so keep trying the next possibility */ + WhiteScan++; + TempNull = WhiteScan; - /* Put back the command line */ - *NullBuffer = SaveChar; - lpApplicationName = NameBuffer; - DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal, NameBuffer); + /* We will have to add a quote, since there is a space */ + QuotesNeeded = TRUE; + goto StartScan; + } } - else if (!lpCommandLine || *lpCommandLine == UNICODE_NULL) + else if (!(lpCommandLine) || !(*lpCommandLine)) { - /* We have an app name (good!) but no command line */ + /* We don't have a command line, so just use the application name */ CmdLineIsAppName = TRUE; lpCommandLine = (LPWSTR)lpApplicationName; } - /* At this point the name has been toyed with enough to be openable */ - Status = BasepMapFile(lpApplicationName, &hSection, &ApplicationName); + /* Convert the application name to its NT path */ + TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName, + &PathName, + NULL, + &SxsWin32RelativePath); + if (!TranslationStatus) + { + /* Path must be invaild somehow, bail out */ + DPRINT1("Path translation for SxS failed\n"); + SetLastError(ERROR_PATH_NOT_FOUND); + Result = FALSE; + goto Quickie; + } - /* Check for failure */ + /* Setup the buffer that needs to be freed at the end */ + ASSERT(FreeBuffer == NULL); + FreeBuffer = PathName.Buffer; + + /* Check what kind of path the application is, for SxS (Fusion) purposes */ + RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName); + SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName); + if ((SxsPathType != RtlPathTypeDriveAbsolute) && + (SxsPathType != RtlPathTypeLocalDevice) && + (SxsPathType != RtlPathTypeRootLocalDevice) && + (SxsPathType != RtlPathTypeUncAbsolute)) + { + /* Relative-type path, get the full path */ + RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0); + Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath, + NULL, + &PathBufferString, + NULL, + NULL, + NULL, + &SxsPathType, + NULL); + if (!NT_SUCCESS(Status)) + { + /* Fail the rest of the create */ + RtlReleaseRelativeName(&SxsWin32RelativePath); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* Use this full path as the SxS path */ + SxsWin32ExePath = PathBufferString; + PathBuffer = PathBufferString.Buffer; + PathBufferString.Buffer = NULL; + DPRINT1("SxS Path: %S\n", PathBuffer); + } + + /* Also set the .EXE path based on the path name */ +#if _SXS_SUPPORT_ENABLED_ + SxsNtExePath = PathName; +#endif + if (SxsWin32RelativePath.RelativeName.Length) + { + /* If it's relative, capture the relative name */ + PathName = SxsWin32RelativePath.RelativeName; + } + else + { + /* Otherwise, it's absolute, make sure no relative dir is used */ + SxsWin32RelativePath.ContainingDirectory = NULL; + } + + /* Now use the path name, and the root path, to try opening the app */ + DPRINT1("Path: %wZ. Dir: %lx\n", &PathName, SxsWin32RelativePath.ContainingDirectory); + InitializeObjectAttributes(&LocalObjectAttributes, + &PathName, + OBJ_CASE_INSENSITIVE, + SxsWin32RelativePath.ContainingDirectory, + NULL); + Status = NtOpenFile(&FileHandle, + SYNCHRONIZE | + FILE_READ_ATTRIBUTES | + FILE_READ_DATA | + FILE_EXECUTE, + &LocalObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | + FILE_NON_DIRECTORY_FILE); if (!NT_SUCCESS(Status)) { - /* Could be a non-PE File */ - switch (Status) + /* Try to open the app just for execute purposes instead */ + Status = NtOpenFile(&FileHandle, + SYNCHRONIZE | FILE_EXECUTE, + &LocalObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | + FILE_NON_DIRECTORY_FILE); + } + + /* Cleanup in preparation for failure or success */ + RtlReleaseRelativeName(&SxsWin32RelativePath); + if (!NT_SUCCESS(Status)) + { + /* Failure path, try to understand why */ + DPRINT1("Open file failed: %lx\n", Status); + if (RtlIsDosDeviceName_U(lpApplicationName)) { - /* Check if the Kernel tells us it's not even valid MZ */ - case STATUS_INVALID_IMAGE_NE_FORMAT: - case STATUS_INVALID_IMAGE_PROTECT: - case STATUS_INVALID_IMAGE_NOT_MZ: - -#if 0 - /* If it's a DOS app, use VDM */ - if ((BasepCheckDosApp(&ApplicationName))) - { - DPRINT1("Launching VDM...\n"); - RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); - RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer); - return CreateProcessW(L"ntvdm.exe", - (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */ - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - &StartupInfo, - lpProcessInformation); - } -#endif - /* It's a batch file */ - Extension = &ApplicationName.Buffer[ApplicationName.Length / - sizeof(WCHAR) - 4]; - - /* Make sure the extensions are correct */ - if (_wcsnicmp(Extension, L".bat", 4) && _wcsnicmp(Extension, L".cmd", 4)) - { - SetLastError(ERROR_BAD_EXE_FORMAT); - return FALSE; - } - - /* Calculate the length of the command line */ - CmdLineLength = wcslen(CMD_STRING) + wcslen(lpCommandLine) + 1; - - /* If we found quotes, then add them into the length size */ - if (CmdLineIsAppName || FoundQuotes) CmdLineLength += 2; - CmdLineLength *= sizeof(WCHAR); - - /* Allocate space for the new command line */ - BatchCommandLine = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - CmdLineLength); - if (BatchCommandLine == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto Cleanup; - } - - /* Build it */ - wcscpy(BatchCommandLine, CMD_STRING); - if (CmdLineIsAppName || FoundQuotes) - { - wcscat(BatchCommandLine, L"\""); - } - wcscat(BatchCommandLine, lpCommandLine); - if (CmdLineIsAppName || FoundQuotes) - { - wcscat(BatchCommandLine, L"\""); - } - - /* Create it as a Unicode String */ - RtlInitUnicodeString(&CommandLineString, BatchCommandLine); - - /* Set the command line to this */ - lpCommandLine = CommandLineString.Buffer; - lpApplicationName = NULL; - - /* Free memory */ - RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer); - ApplicationName.Buffer = NULL; - goto GetAppName; - break; - - case STATUS_INVALID_IMAGE_WIN_16: - - /* It's a Win16 Image, use VDM */ - DPRINT1("Launching VDM...\n"); - RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); - RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer); - return CreateProcessW(L"ntvdm.exe", - (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */ - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - &StartupInfo, - lpProcessInformation); - - case STATUS_OBJECT_NAME_NOT_FOUND: - case STATUS_OBJECT_PATH_NOT_FOUND: - BaseSetLastNTError(Status); - goto Cleanup; - - default: - /* Invalid Image Type */ - SetLastError(ERROR_BAD_EXE_FORMAT); - goto Cleanup; + /* If a device is being executed, return this special error code */ + SetLastError(ERROR_BAD_DEVICE); + Result = FALSE; + goto Quickie; + } + else + { + /* Otherwise return the converted NT error code */ + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; } } - /* Use our desktop if we didn't get any */ + /* Did the caller specify a desktop? */ if (!StartupInfo.lpDesktop) { - StartupInfo.lpDesktop = OurPeb->ProcessParameters->DesktopInfo.Buffer; + /* Use the one from the current process */ + StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer; } - /* FIXME: Check if Application is allowed to run */ - - /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */ - - /* Get some information about the executable */ - Status = NtQuerySection(hSection, - SectionImageInformation, - &SectionImageInfo, - sizeof(SectionImageInfo), - NULL); - if(!NT_SUCCESS(Status)) + /* Create a section for this file */ + Status = NtCreateSection(&SectionHandle, + SECTION_ALL_ACCESS, + NULL, + NULL, + PAGE_EXECUTE, + SEC_IMAGE, + FileHandle); + DPRINT1("Section status: %lx\n", Status); + if (NT_SUCCESS(Status)) { - DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status); - BaseSetLastNTError(Status); - goto Cleanup; + /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */ + if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT | + VER_SUITE_DATACENTER | + VER_SUITE_PERSONAL | + VER_SUITE_BLADE)) + { + /* These SKUs do not allow running certain applications */ + Status = BasepCheckWebBladeHashes(FileHandle); + if (Status == STATUS_ACCESS_DENIED) + { + /* And this is one of them! */ + DPRINT1("Invalid Blade hashes!\n"); + SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE); + Result = FALSE; + goto Quickie; + } + + /* Did we get some other failure? */ + if (!NT_SUCCESS(Status)) + { + /* If we couldn't check the hashes, assume nefariousness */ + DPRINT1("Tampered Blade hashes!\n"); + SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER); + Result = FALSE; + goto Quickie; + } + } + + /* Now do Winsafer, etc, checks */ + Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName); + if (!NT_SUCCESS(Status)) + { + /* Fail if we're not allowed to launch the process */ + DPRINT1("Process not allowed to launch: %lx\n", Status); + BaseSetLastNTError(Status); + if (SectionHandle) + { + NtClose(SectionHandle); + SectionHandle = NULL; + } + Result = FALSE; + goto Quickie; + } + + /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */ + if ((dwCreationFlags & CREATE_FORCEDOS) && + (BaseStaticServerData->IsWowTaskReady)) + { + /* This request can't be satisifeed, instead, a separate VDM is needed */ + dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM); + dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; + + /* Set a failure code, ask for VDM reservation */ + Status = STATUS_INVALID_IMAGE_WIN_16; + UseVdmReserve = TRUE; + + /* Close the current handle */ + NtClose(SectionHandle); + SectionHandle = NULL; + + /* Don't query the section later */ + QuerySection = FALSE; + } } - /* Don't execute DLLs */ - if (SectionImageInfo.ImageCharacteristics & IMAGE_FILE_DLL) + /* Did we already do these checks? */ + if (!SkipSaferAndAppCompat) { - DPRINT1("Can't execute a DLL\n"); + /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */ + if ((NT_SUCCESS(Status)) || + ((Status == STATUS_INVALID_IMAGE_NOT_MZ) && + !(BaseIsDosApplication(&PathName, Status)))) + { + /* Clear the machine type in case of failure */ + ImageMachine = 0; + + /* Clean any app compat data that may have accumulated */ + BasepFreeAppCompatData(AppCompatData, AppCompatSxsData); + AppCompatData = NULL; + AppCompatSxsData = NULL; + + /* Do we have a section? */ + if (SectionHandle) + { + /* Have we already queried it? */ + if (QuerySection) + { + /* Nothing to do */ + Status = STATUS_SUCCESS; + } + else + { + /* Get some information about the executable */ + Status = NtQuerySection(SectionHandle, + SectionImageInformation, + &ImageInformation, + sizeof(ImageInformation), + NULL); + } + + /* Do we have section information now? */ + if (NT_SUCCESS(Status)) + { + /* Don't ask for it again, save the machine type */ + QuerySection = TRUE; + ImageMachine = ImageInformation.Machine; + } + } + + /* Is there a reason/Shim we shouldn't run this application? */ + Status = BasepCheckBadapp(FileHandle, + FreeBuffer, + lpEnvironment, + ImageMachine, + &AppCompatData, + &AppCompatDataSize, + &AppCompatSxsData, + &AppCompatSxsDataSize, + &FusionFlags); + if (!NT_SUCCESS(Status)) + { + /* This is usually the status we get back */ + DPRINT1("App compat launch failure: %lx\n", Status); + if (Status == STATUS_ACCESS_DENIED) + { + /* Convert it to something more Win32-specific */ + SetLastError(ERROR_CANCELLED); + } + else + { + /* Some other error */ + BaseSetLastNTError(Status); + } + + /* Did we have a section? */ + if (SectionHandle) + { + /* Clean it up */ + NtClose(SectionHandle); + SectionHandle = NULL; + } + + /* Fail the call */ + Result = FALSE; + goto Quickie; + } + } + } + + //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0); + + /* Have we already done, and do we need to do, SRP (WinSafer) checks? */ + if (!(SkipSaferAndAppCompat) && + ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL)) + { + /* Assume yes */ + SaferNeeded = TRUE; + switch (Status) + { + case STATUS_INVALID_IMAGE_NE_FORMAT: + case STATUS_INVALID_IMAGE_PROTECT: + case STATUS_INVALID_IMAGE_WIN_16: + case STATUS_FILE_IS_OFFLINE: + /* For all DOS, 16-bit, OS/2 images, we do*/ + break; + + case STATUS_INVALID_IMAGE_NOT_MZ: + /* For invalid files, we don't, unless it's a .BAT file */ + if (BaseIsDosApplication(&PathName, Status)) break; + + default: + /* Any other error codes we also don't */ + if (!NT_SUCCESS(Status)) + { + SaferNeeded = FALSE; + } + + /* But for success, we do */ + break; + } + + /* Okay, so what did the checks above result in? */ + if (SaferNeeded) + { + /* We have to call into the WinSafer library and actually check */ + Status = BasepCheckWinSaferRestrictions(hUserToken, + (LPWSTR)lpApplicationName, + FileHandle, + &InJob, + &TokenHandle, + &JobHandle); + if (Status == 0xFFFFFFFF) + { + /* Back in 2003, they didn't have an NTSTATUS for this... */ + DPRINT1("WinSafer blocking process launch\n"); + SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY); + Result = FALSE; + goto Quickie; + } + + /* Other status codes are not-Safer related, just convert them */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error checking WinSafer: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + } + } + + /* The last step is to figure out why the section object was not created */ + switch (Status) + { + case STATUS_INVALID_IMAGE_WIN_16: + /* 16-bit binary. Should we use WOW or does the caller force VDM? */ + if (!(dwCreationFlags & CREATE_FORCEDOS)) + { + /* Remember that we're launching WOW */ + IsWowApp = TRUE; + + /* Create the VDM environment, it's valid for WOW too */ + Result = BaseCreateVDMEnvironment(lpEnvironment, + &VdmAnsiEnv, + &VdmUnicodeEnv); + if (!Result) + { + DPRINT1("VDM environment for WOW app failed\n"); + goto Quickie; + } + + /* We're going to try this twice, so do a loop */ + while (TRUE) + { + /* Pick which kind of WOW mode we want to run in */ + VdmBinaryType = (dwCreationFlags & + CREATE_SEPARATE_WOW_VDM) ? + BINARY_TYPE_WOW : BINARY_TYPE_SEPARATE_WOW; + + /* Get all the VDM settings and current status */ + Status = BaseCheckVDM(VdmBinaryType, + lpApplicationName, + lpCommandLine, + lpCurrentDirectory, + &VdmAnsiEnv, + (PCSR_API_MESSAGE)VdmMsg, + &VdmTask, + dwCreationFlags, + &StartupInfo, + hUserToken); + + /* If it worked, no need to try again */ + if (NT_SUCCESS(Status)) break; + + /* Check if it's disallowed or if it's our second time */ + BaseSetLastNTError(Status); + if ((Status == STATUS_VDM_DISALLOWED) || + (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) || + (GetLastError() == ERROR_ACCESS_DENIED)) + { + /* Fail the call -- we won't try again */ + DPRINT1("VDM message failure for WOW: %lx\n", Status); + Result = FALSE; + goto Quickie; + } + + /* Try one more time, but with a separate WOW instance */ + dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; + } + + /* Check which VDM state we're currently in */ + switch (VdmMsg->VDMState & (VDM_NOT_LOADED | + VDM_NOT_READY | + VDM_READY)) + { + case VDM_NOT_LOADED: + /* VDM is not fully loaded, so not that much to undo */ + VdmUndoLevel = VDM_UNDO_PARTIAL; + + /* Reset VDM reserve if needed */ + if (UseVdmReserve) VdmReserve = 1; + + /* Get the required parameters and names for launch */ + Result = BaseGetVdmConfigInfo(lpCommandLine, + VdmTask, + VdmBinaryType, + &VdmString, + &VdmReserve); + if (!Result) + { + DPRINT1("VDM Configuration failed for WOW\n"); + BaseSetLastNTError(Status); + goto Quickie; + } + + /* Update the command-line with the VDM one instead */ + lpCommandLine = VdmString.Buffer; + lpApplicationName = NULL; + + /* We don't want a console, detachment, nor a window */ + dwCreationFlags |= CREATE_NO_WINDOW; + dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS); + + /* Force feedback on */ + StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK; + break; + + + case VDM_READY: + /* VDM is ready, so we have to undo everything */ + VdmUndoLevel = VDM_UNDO_REUSE; + + /* Check if CSRSS wants us to wait on VDM */ + VdmWaitObject = VdmMsg->WaitObjectForParent; + break; + + case VDM_NOT_READY: + /* Something is wrong with VDM, we'll fail the call */ + DPRINT1("VDM is not ready for WOW\n"); + SetLastError(ERROR_NOT_READY); + Result = FALSE; + goto Quickie; + + default: + break; + } + + /* Since to get NULL, we allocate from 0x1, account for this */ + VdmReserve--; + + /* This implies VDM is ready, so skip everything else */ + if (VdmWaitObject) goto VdmShortCircuit; + + /* Don't inherit handles since we're doing VDM now */ + bInheritHandles = FALSE; + + /* Had the user passed in environment? If so, destroy it */ + if ((lpEnvironment) && + !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + { + RtlDestroyEnvironment(lpEnvironment); + } + + /* We've already done all these checks, don't do them again */ + SkipSaferAndAppCompat = TRUE; + goto AppNameRetry; + } + + // There is no break here on purpose, so FORCEDOS drops down! + case STATUS_INVALID_IMAGE_PROTECT: + case STATUS_INVALID_IMAGE_NOT_MZ: + case STATUS_INVALID_IMAGE_NE_FORMAT: + /* We're launching an executable application */ + BinarySubType = BINARY_TYPE_EXE; + + /* We can drop here from other "cases" above too, so check */ + if ((Status == STATUS_INVALID_IMAGE_PROTECT) || + (Status == STATUS_INVALID_IMAGE_NE_FORMAT) || + (BinarySubType = BaseIsDosApplication(&PathName, Status))) + { + /* We're launching a DOS application */ + VdmBinaryType = BINARY_TYPE_DOS; + + /* Based on the caller environment, create a VDM one */ + Result = BaseCreateVDMEnvironment(lpEnvironment, + &VdmAnsiEnv, + &VdmUnicodeEnv); + if (!Result) + { + DPRINT1("VDM environment for DOS failed\n"); + goto Quickie; + } + + /* Check the current state of the VDM subsystem */ + Status = BaseCheckVDM(VdmBinaryType | BinarySubType, + lpApplicationName, + lpCommandLine, + lpCurrentDirectory, + &VdmAnsiEnv, + (PCSR_API_MESSAGE)VdmMsg, + &VdmTask, + dwCreationFlags, + &StartupInfo, + NULL); + if (!NT_SUCCESS(Status)) + { + /* Failed to inquire about VDM, fail the call */ + DPRINT1("VDM message failure for DOS: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + }; + + /* Handle possible VDM states */ + switch (VdmMsg->VDMState & (VDM_NOT_LOADED | + VDM_NOT_READY | + VDM_READY)) + { + case VDM_NOT_LOADED: + /* If VDM is not loaded, we'll do a partial undo */ + VdmUndoLevel = VDM_UNDO_PARTIAL; + + /* A VDM process can't also be detached, so fail */ + if (dwCreationFlags & DETACHED_PROCESS) + { + DPRINT1("Detached process but no VDM, not allowed\n"); + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + + /* Get the required parameters and names for launch */ + Result = BaseGetVdmConfigInfo(lpCommandLine, + VdmTask, + VdmBinaryType, + &VdmString, + &VdmReserve); + if (!Result) + { + DPRINT1("VDM Configuration failed for DOS\n"); + BaseSetLastNTError(Status); + goto Quickie; + } + + /* Update the command-line to launch VDM instead */ + lpCommandLine = VdmString.Buffer; + lpApplicationName = NULL; + break; + + case VDM_READY: + /* VDM is ready, so we have to undo everything */ + VdmUndoLevel = VDM_UNDO_REUSE; + + /* Check if CSRSS wants us to wait on VDM */ + VdmWaitObject = VdmMsg->WaitObjectForParent; + break; + + case VDM_NOT_READY: + /* Something is wrong with VDM, we'll fail the call */ + DPRINT1("VDM is not ready for DOS\n"); + SetLastError(ERROR_NOT_READY); + Result = FALSE; + goto Quickie; + + default: + break; + } + + /* Since to get NULL, we allocate from 0x1, account for this */ + VdmReserve--; + + /* This implies VDM is ready, so skip everything else */ + if (VdmWaitObject) goto VdmShortCircuit; + + /* Don't inherit handles since we're doing VDM now */ + bInheritHandles = FALSE; + + /* Had the user passed in environment? If so, destroy it */ + if ((lpEnvironment) && + !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + { + RtlDestroyEnvironment(lpEnvironment); + } + + /* Use our VDM Unicode environment instead */ + lpEnvironment = VdmUnicodeEnv.Buffer; + } + else + { + /* It's a batch file, get the extension */ + ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4]; + + /* Make sure the extensions are correct */ + if ((PathName.Length < (4 * sizeof(WCHAR))) || + ((_wcsnicmp(ExtBuffer, L".bat", 4)) && + (_wcsnicmp(ExtBuffer, L".cmd", 4)))) + { + DPRINT1("Invalid EXE, and not a batch or script file\n"); + SetLastError(ERROR_BAD_EXE_FORMAT); + Result = FALSE; + goto Quickie; + } + + /* Check if we need to account for quotes around the path */ + CmdQuoteLength = CmdLineIsAppName || QuotesNeeded; + if (!CmdLineIsAppName) + { + if (QuotesNeeded) CmdQuoteLength++; + } + else + { + CmdQuoteLength++; + } + + /* Calculate the length of the command line */ + CmdLineLength = wcslen(lpCommandLine); + CmdLineLength += wcslen(CMD_STRING); + CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL); + CmdLineLength *= sizeof(WCHAR); + + /* Allocate space for the new command line */ + AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + CmdLineLength); + if (!AnsiCmdCommand) + { + BaseSetLastNTError(STATUS_NO_MEMORY); + Result = FALSE; + goto Quickie; + } + + /* Build it */ + wcscpy(AnsiCmdCommand, CMD_STRING); + if ((CmdLineIsAppName) || (QuotesNeeded)) + { + wcscat(AnsiCmdCommand, L"\""); + } + wcscat(AnsiCmdCommand, lpCommandLine); + if ((CmdLineIsAppName) || (QuotesNeeded)) + { + wcscat(AnsiCmdCommand, L"\""); + } + + /* Create it as a Unicode String */ + RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand); + + /* Set the command line to this */ + lpCommandLine = DebuggerString.Buffer; + lpApplicationName = NULL; + DPRINT1("Retrying with: %S\n", lpCommandLine); + } + + /* We've already done all these checks, don't do them again */ + SkipSaferAndAppCompat = TRUE; + goto AppNameRetry; + + case STATUS_INVALID_IMAGE_WIN_64: + /* 64-bit binaries are not allowed to run on 32-bit ReactOS */ + DPRINT1("64-bit binary, failing\n"); + SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); + Result = FALSE; + goto Quickie; + + case STATUS_FILE_IS_OFFLINE: + /* Set the correct last error for this */ + DPRINT1("File is offline, failing\n"); + SetLastError(ERROR_FILE_OFFLINE); + break; + + default: + /* Any other error, convert it to a generic Win32 error */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create section: %lx\n", Status); + SetLastError(ERROR_BAD_EXE_FORMAT); + Result = FALSE; + goto Quickie; + } + + /* Otherwise, this must be success */ + ASSERT(Status == STATUS_SUCCESS); + break; + } + + /* Is this not a WOW application, but a WOW32 VDM was requested for it? */ + if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) + { + /* Ignore the nonsensical request */ + dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM; + } + + /* Did we already check information for the section? */ + if (!QuerySection) + { + /* Get some information about the executable */ + Status = NtQuerySection(SectionHandle, + SectionImageInformation, + &ImageInformation, + sizeof(ImageInformation), + NULL); + if (!NT_SUCCESS(Status)) + { + /* We failed, bail out */ + DPRINT1("Section query failed\n"); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* Don't check this later */ + QuerySection = TRUE; + } + + /* Check if this was linked as a DLL */ + if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) + { + /* These aren't valid images to try to execute! */ + DPRINT1("Trying to launch a DLL, failing\n"); SetLastError(ERROR_BAD_EXE_FORMAT); - goto Cleanup; + Result = FALSE; + goto Quickie; } - /* FIXME: Check for Debugger */ + /* Don't let callers pass in this flag -- we'll only get it from IFRO */ + Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES; - /* FIXME: Check if Machine Type and SubSys Version Match */ + /* Clear the IFEO-missing flag, before we know for sure... */ + ParameterFlags &= ~2; - /* We don't support POSIX or anything else for now */ - if (IMAGE_SUBSYSTEM_WINDOWS_GUI != SectionImageInfo.SubSystemType && - IMAGE_SUBSYSTEM_WINDOWS_CUI != SectionImageInfo.SubSystemType) + /* If the process is being debugged, only read IFEO if the PEB says so */ + if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) || + (NtCurrentPeb()->ReadImageFileExecOptions)) { - DPRINT1("Invalid subsystem %d\n", SectionImageInfo.SubSystemType); - /* - * Despite the name of the error code suggests, it corresponds to the - * well-known "The %1 application cannot be run in Win32 mode" message. - */ - SetLastError(ERROR_CHILD_NOT_COMPLETE); - goto Cleanup; + /* Let's do this! Attempt to open IFEO */ + Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle); + if (!NT_SUCCESS(Status1)) + { + /* We failed, set the flag so we store this in the parameters */ + if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2; + } + else + { + /* Was this our first time going through this path? */ + if (!DebuggerCmdLine) + { + /* Allocate a buffer for the debugger path */ + DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + MAX_PATH * sizeof(WCHAR)); + if (!DebuggerCmdLine) + { + /* Close IFEO on failure */ + Status1 = NtClose(KeyHandle); + ASSERT(NT_SUCCESS(Status1)); + + /* Fail the call */ + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + Result = FALSE; + goto Quickie; + } + } + + /* Now query for the debugger */ + Status1 = LdrQueryImageFileKeyOption(KeyHandle, + L"Debugger", + REG_SZ, + DebuggerCmdLine, + MAX_PATH * sizeof(WCHAR), + &ResultSize); + if (!(NT_SUCCESS(Status1)) || + (ResultSize < sizeof(WCHAR)) || + (DebuggerCmdLine[0] == UNICODE_NULL)) + { + /* If it's not there, or too small, or invalid, ignore it */ + RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); + DebuggerCmdLine = NULL; + } + + /* Also query if we should map with large pages */ + Status1 = LdrQueryImageFileKeyOption(KeyHandle, + L"UseLargePages", + REG_DWORD, + &UseLargePages, + sizeof(UseLargePages), + NULL); + if ((NT_SUCCESS(Status1)) && (UseLargePages)) + { + /* Do it! This is the only way this flag can be set */ + Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES; + } + + /* We're done with IFEO, can close it now */ + Status1 = NtClose(KeyHandle); + ASSERT(NT_SUCCESS(Status1)); + } } - if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo.SubSystemType) + /* Make sure the image was compiled for this processor */ + if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) || + (ImageInformation.Machine > SharedUserData->ImageNumberHigh)) { - /* Do not create a console for GUI applications */ - dwCreationFlags &= ~CREATE_NEW_CONSOLE; - dwCreationFlags |= DETACHED_PROCESS; + /* It was not -- raise a hard error */ + ErrorResponse = ResponseOk; + ErrorParameters[0] = (ULONG_PTR)&PathName; + NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, + 1, + 1, + ErrorParameters, + OptionOk, + &ErrorResponse); + if (Peb->ImageSubsystemMajorVersion <= 3) + { + /* If it's really old, return this error */ + SetLastError(ERROR_BAD_EXE_FORMAT); + } + else + { + /* Otherwise, return a more modern error */ + SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); + } + + /* Go to the failure path */ + DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine); + Result = FALSE; + goto Quickie; + } + + /* Check if this isn't a Windows image */ + if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) && + (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI)) + { + /* Get rid of section-related information since we'll retry */ + NtClose(SectionHandle); + SectionHandle = NULL; + QuerySection = FALSE; + + /* The only other non-Windows image type we support here is POSIX */ + if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI) + { + /* Bail out if it's something else */ + SetLastError(ERROR_CHILD_NOT_COMPLETE); + Result = FALSE; + goto Quickie; + } + + /* Now build the command-line to have posix launch this image */ + Result = BuildSubSysCommandLine(L"POSIX /P ", + lpApplicationName, + lpCommandLine, + &DebuggerString); + if (!Result) + { + /* Bail out if that failed */ + DPRINT1("Subsystem command line failed\n"); + goto Quickie; + } + + /* And re-try launching the process, with the new command-line now */ + lpCommandLine = DebuggerString.Buffer; + lpApplicationName = NULL; + + /* We've already done all these checks, don't do them again */ + SkipSaferAndAppCompat = TRUE; + DPRINT1("Retrying with: %S\n", lpCommandLine); + goto AppNameRetry; + } + + /* Was this image built for a version of Windows whose images we can run? */ + Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion, + ImageInformation.SubSystemMinorVersion); + if (!Result) + { + /* It was not, bail out */ + DPRINT1("Invalid subsystem version: %d.%d\n", + ImageInformation.SubSystemMajorVersion, + ImageInformation.SubSystemMinorVersion); + SetLastError(ERROR_BAD_EXE_FORMAT); + goto Quickie; + } + + /* Check if there is a debugger associated with the application */ + if (DebuggerCmdLine) + { + /* Get the length of the command line */ + n = wcslen(lpCommandLine); + if (!n) + { + /* There's no command line, use the application name instead */ + lpCommandLine = (LPWSTR)lpApplicationName; + n = wcslen(lpCommandLine); + } + + /* Protect against overflow */ + if (n > UNICODE_STRING_MAX_CHARS) + { + BaseSetLastNTError(STATUS_NAME_TOO_LONG); + Result = FALSE; + goto Quickie; + } + + /* Now add the length of the debugger command-line */ + n += wcslen(DebuggerCmdLine); + + /* Again make sure we don't overflow */ + if (n > UNICODE_STRING_MAX_CHARS) + { + BaseSetLastNTError(STATUS_NAME_TOO_LONG); + Result = FALSE; + goto Quickie; + } + + /* Account for the quotes and space between the two */ + n += ((sizeof('""') * 2) + sizeof(' ')); + + /* Convert to bytes, and make sure we don't overflow */ + n *= sizeof(WCHAR); + if (n > UNICODE_STRING_MAX_BYTES) + { + BaseSetLastNTError(STATUS_NAME_TOO_LONG); + Result = FALSE; + goto Quickie; + } + + /* Allocate space for the string */ + DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n); + if (!DebuggerString.Buffer) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + Result = FALSE; + goto Quickie; + } + + /* Set the length */ + RtlInitEmptyUnicodeString(&DebuggerString, + DebuggerString.Buffer, + n); + + /* Now perform the command line creation */ + ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, + DebuggerCmdLine); + ASSERT(NT_SUCCESS(ImageDbgStatus)); + ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" "); + ASSERT(NT_SUCCESS(ImageDbgStatus)); + ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine); + ASSERT(NT_SUCCESS(ImageDbgStatus)); + + /* Make sure it all looks nice */ + DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString); + + /* Update the command line and application name */ + lpCommandLine = DebuggerString.Buffer; + lpApplicationName = NULL; + + /* Close all temporary state */ + NtClose(SectionHandle); + SectionHandle = NULL; + QuerySection = FALSE; + + /* Free all temporary memory */ + RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); + NameBuffer = NULL; + RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); + FreeBuffer = NULL; + RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); + DebuggerCmdLine = NULL; + DPRINT1("Retrying with: %S\n", lpCommandLine); + goto AppNameRetry; } /* Initialize the process object attributes */ ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, - lpProcessAttributes, - NULL); + lpProcessAttributes, + NULL); + if ((hUserToken) && (lpProcessAttributes)) + { + /* Auggment them with information from the user */ + + LocalProcessAttributes = *lpProcessAttributes; + LocalProcessAttributes.lpSecurityDescriptor = NULL; + ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, + &LocalProcessAttributes, + NULL); + } /* Check if we're going to be debugged */ if (dwCreationFlags & DEBUG_PROCESS) { - /* FIXME: Set process flag */ + /* Set process flag */ + Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY; } /* Check if we're going to be debugged */ @@ -3023,275 +3828,749 @@ GetAppName: { DPRINT1("Failed to connect to DbgUI!\n"); BaseSetLastNTError(Status); - goto Cleanup; + Result = FALSE; + goto Quickie; } /* Get the debug object */ - hDebug = DbgUiGetThreadDebugObject(); + DebugHandle = DbgUiGetThreadDebugObject(); /* Check if only this process will be debugged */ if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS) { /* Set process flag */ - hDebug = (HANDLE)((ULONG_PTR)hDebug | 0x1); + Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT; } } - /* Create the Process */ - Status = NtCreateProcess(&hProcess, - PROCESS_ALL_ACCESS, - ObjectAttributes, - NtCurrentProcess(), - (BOOLEAN)bInheritHandles, - hSection, - hDebug, - NULL); - if (!NT_SUCCESS(Status)) + /* Set inherit flag */ + if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES; + + /* Check if the process should be created with large pages */ + HavePrivilege = FALSE; + PrivilegeState = NULL; + if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) { - DPRINT1("Unable to create process, status 0x%x\n", Status); - BaseSetLastNTError(Status); - goto Cleanup; + /* Acquire the required privilege so that the kernel won't fail the call */ + PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE; + Status = RtlAcquirePrivilege(&PrivilegeValue, TRUE, FALSE, &PrivilegeState); + if (NT_SUCCESS(Status)) + { + /* Remember to release it later */ + HavePrivilege = TRUE; + } } - if (PriorityClass.PriorityClass != PROCESS_PRIORITY_CLASS_INVALID) + /* Save the current TIB value since kernel overwrites it to store PEB */ + TibValue = Teb->NtTib.ArbitraryUserPointer; + + /* Tell the kernel to create the process */ + Status = NtCreateProcessEx(&ProcessHandle, + PROCESS_ALL_ACCESS, + ObjectAttributes, + NtCurrentProcess(), + Flags, + SectionHandle, + DebugHandle, + NULL, + InJob); + + /* Load the PEB address from the hacky location where the kernel stores it */ + RemotePeb = Teb->NtTib.ArbitraryUserPointer; + + /* And restore the old TIB value */ + Teb->NtTib.ArbitraryUserPointer = TibValue; + + /* Release the large page privilege if we had acquired it */ + if (HavePrivilege) RtlReleasePrivilege(PrivilegeState); + + /* And now check if the kernel failed to create the process */ + if (!NT_SUCCESS(Status)) { - /* Set new class */ - Status = NtSetInformationProcess(hProcess, + /* Go to failure path */ + DPRINT1("Failed to create process: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* Check if there is a priority class to set */ + if (PriorityClass.PriorityClass) + { + /* Reset current privilege state */ + RealTimePrivilegeState = NULL; + + /* Is realtime priority being requested? */ + if (PriorityClass.PriorityClass == REALTIME_PRIORITY_CLASS) + { + /* Check if the caller has real-time access, and enable it if so */ + RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE); + } + + /* Set the new priority class and release the privilege */ + Status = NtSetInformationProcess(ProcessHandle, ProcessPriorityClass, &PriorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - if(!NT_SUCCESS(Status)) + if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState); + + /* Check if we failed to set the priority class */ + if (!NT_SUCCESS(Status)) { - DPRINT1("Unable to set new process priority, status 0x%x\n", Status); + /* Bail out on failure */ + DPRINT1("Failed to set priority class: %lx\n", Status); BaseSetLastNTError(Status); - goto Cleanup; + Result = FALSE; + goto Quickie; } } - /* Set Error Mode */ + /* Check if the caller wants the default error mode */ if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) { - ULONG ErrorMode = SEM_FAILCRITICALERRORS; - NtSetInformationProcess(hProcess, + /* Set Error Mode to only fail on critical errors */ + HardErrorMode = SEM_FAILCRITICALERRORS; + NtSetInformationProcess(ProcessHandle, ProcessDefaultHardErrorMode, - &ErrorMode, + &HardErrorMode, sizeof(ULONG)); } - /* Convert the directory to a full path */ - if (lpCurrentDirectory) + /* Check if this was a VDM binary */ + if (VdmBinaryType) { - /* Allocate a buffer */ - CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - (MAX_PATH + 1) * sizeof(WCHAR)); - if (CurrentDirectory == NULL) + /* Update VDM by telling it the process has now been created */ + VdmWaitObject = ProcessHandle; + Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess, + &VdmWaitObject, + VdmTask, + VdmBinaryType); { - DPRINT1("Cannot allocate memory for directory name\n"); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto Cleanup; + /* Bail out on failure */ + DPRINT1("Failed to update VDM with wait object\n"); + VdmWaitObject = NULL; + goto Quickie; } - /* Get the length */ - if (GetFullPathNameW(lpCurrentDirectory, - MAX_PATH, - CurrentDirectory, - &CurrentDirectoryPart) > MAX_PATH) + /* At this point, a failure means VDM has to undo all the state */ + VdmUndoLevel |= VDM_UNDO_FULL; + } + + /* Check if VDM needed reserved low-memory */ + if (VdmReserve) + { + /* Reserve the requested allocation */ + Status = NtAllocateVirtualMemory(ProcessHandle, + &BaseAddress, + 0, + &VdmReserve, + MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) { - DPRINT1("Directory name too long\n"); + /* Bail out on failure */ + DPRINT1("Failed to reserved memory for VDM: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + } + + /* Check if we've already queried information on the section */ + if (!QuerySection) + { + /* We haven't, so get some information about the executable */ + Status = NtQuerySection(SectionHandle, + SectionImageInformation, + &ImageInformation, + sizeof(ImageInformation), + NULL); + if (!NT_SUCCESS(Status)) + { + /* Bail out on failure */ + DPRINT1("Failed to query section: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* If we encounter a restart, don't re-query this information again */ + QuerySection = TRUE; + } + + /* Do we need to apply SxS to this image? */ + if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION)) + { + /* Too bad, we don't support this yet */ + DPRINT1("Image should receive SxS Fusion Isolation\n"); + } + + /* There's some SxS flag that we need to set if fusion flags have 1 set */ + if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10; + + /* Check if we have a current directory */ + if (lpCurrentDirectory) + { + /* Allocate a buffer so we can keep a Unicode copy */ + DPRINT1("Current directory: %S\n", lpCurrentDirectory); + CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + (MAX_PATH * sizeof(WCHAR)) + + sizeof(UNICODE_NULL)); + if (!CurrentDirectory) + { + /* Bail out if this failed */ + BaseSetLastNTError(STATUS_NO_MEMORY); + Result = FALSE; + goto Quickie; + } + + /* Get the length in Unicode */ + Length = GetFullPathNameW(lpCurrentDirectory, + MAX_PATH, + CurrentDirectory, + &FilePart); + if (Length > MAX_PATH) + { + /* The directory is too long, so bail out */ SetLastError(ERROR_DIRECTORY); - goto Cleanup; + Result = FALSE; + goto Quickie; + } + + /* Make sure the directory is actually valid */ + CurdirLength = GetFileAttributesW(CurrentDirectory); + if ((CurdirLength == 0xffffffff) || + !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY)) + { + /* It isn't, so bail out */ + DPRINT1("Current directory is invalid\n"); + SetLastError(ERROR_DIRECTORY); + Result = FALSE; + goto Quickie; } } /* Insert quotes if needed */ - if (QuotesNeeded || CmdLineIsAppName) + if ((QuotesNeeded) || (CmdLineIsAppName)) { - /* Allocate a buffer */ + /* Allocate our buffer, plus enough space for quotes and a NULL */ QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(), 0, - (wcslen(lpCommandLine) + 2 + 1) * - sizeof(WCHAR)); - if (QuotedCmdLine == NULL) + wcslen(lpCommandLine) * sizeof(WCHAR) + + 2 * sizeof(L'\"') + sizeof(UNICODE_NULL)); + if (QuotedCmdLine) { - DPRINT1("Cannot allocate memory for quoted command line\n"); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto Cleanup; - } + /* Copy the first quote */ + wcscpy(QuotedCmdLine, L"\""); - /* Copy the first quote */ - wcscpy(QuotedCmdLine, L"\""); - - /* Save a null char */ - if (QuotesNeeded) - { - SaveChar = *NullBuffer; - *NullBuffer = UNICODE_NULL; - } - - /* Add the command line and the finishing quote */ - wcscat(QuotedCmdLine, lpCommandLine); - wcscat(QuotedCmdLine, L"\""); - - /* Add the null char */ - if (QuotesNeeded) - { - *NullBuffer = SaveChar; - wcscat(QuotedCmdLine, NullBuffer); - } - - DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine); - } - - if (Escape) - { - if (QuotedCmdLine == NULL) - { - QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - (wcslen(lpCommandLine) + 1) * sizeof(WCHAR)); - if (QuotedCmdLine == NULL) + /* Save the current null-character */ + if (QuotesNeeded) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto Cleanup; + TempChar = *TempNull; + *TempNull = UNICODE_NULL; } - wcscpy(QuotedCmdLine, lpCommandLine); - } - ScanString = QuotedCmdLine; - while (NULL != (ScanString = wcschr(ScanString, L'^'))) - { - ScanString++; - if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\') + /* Copy the command line and the final quote */ + wcscat(QuotedCmdLine, lpCommandLine); + wcscat(QuotedCmdLine, L"\""); + + /* Copy the null-char back */ + if (QuotesNeeded) { - memmove(ScanString-1, ScanString, wcslen(ScanString) * sizeof(WCHAR) + sizeof(WCHAR)); + *TempNull = TempChar; + wcscat(QuotedCmdLine, TempNull); } } + else + { + /* We can't put quotes around the thing, so try it anyway */ + if (QuotesNeeded) QuotesNeeded = FALSE; + if (CmdLineIsAppName) CmdLineIsAppName = FALSE; + } } - /* Get the Process Information */ - Status = NtQueryInformationProcess(hProcess, - ProcessBasicInformation, - &ProcessBasicInfo, - sizeof(ProcessBasicInfo), - NULL); + /* Reserve the first 1MB if needed */ + if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1; - /* Convert the environment */ - if(lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */ + Result = BasePushProcessParameters(ParameterFlags, + ProcessHandle, + RemotePeb, + lpApplicationName, + CurrentDirectory, + ((QuotesNeeded) || (CmdLineIsAppName)) ? + QuotedCmdLine : lpCommandLine, + lpEnvironment, + &StartupInfo, + dwCreationFlags | NoWindow, + bInheritHandles, + IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0, + AppCompatData, + AppCompatDataSize); + if (!Result) { - lpEnvironment = BasepConvertUnicodeEnvironment(&EnvSize, lpEnvironment); - if (!lpEnvironment) goto Cleanup; + /* The remote process would have an undefined state, so fail the call */ + DPRINT1("BasePushProcessParameters failed\n"); + goto Quickie; } - /* Create Process Environment */ - RemotePeb = ProcessBasicInfo.PebBaseAddress; - Ret = BasePushProcessParameters(0, - hProcess, - RemotePeb, - (LPWSTR)lpApplicationName, - CurrentDirectory, - (QuotesNeeded || CmdLineIsAppName || Escape) ? - QuotedCmdLine : lpCommandLine, - lpEnvironment, - &StartupInfo, - dwCreationFlags, - bInheritHandles, - 0, - NULL, - 0); - if (!Ret) goto Cleanup; + /* Free the VDM command line string as it's no longer needed */ + RtlFreeUnicodeString(&VdmString); + VdmString.Buffer = NULL; - /* Cleanup Environment */ - if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + /* Non-VDM console applications usually inherit handles unless specified */ + if (!(VdmBinaryType) && + !(bInheritHandles) && + !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) && + !(dwCreationFlags & (CREATE_NO_WINDOW | + CREATE_NEW_CONSOLE | + DETACHED_PROCESS)) && + (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI)) { - RtlDestroyEnvironment(lpEnvironment); - } - - /* Close the section */ - NtClose(hSection); - hSection = NULL; - - /* Duplicate the handles if needed */ - if (!bInheritHandles && !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) && - SectionImageInfo.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI) - { - PRTL_USER_PROCESS_PARAMETERS RemoteParameters; - /* Get the remote parameters */ - Status = NtReadVirtualMemory(hProcess, + Status = NtReadVirtualMemory(ProcessHandle, &RemotePeb->ProcessParameters, - &RemoteParameters, - sizeof(PVOID), + &ProcessParameters, + sizeof(PRTL_USER_PROCESS_PARAMETERS), NULL); + if (NT_SUCCESS(Status)) + { + /* Duplicate standard input unless it's a console handle */ + if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput)) + { + StuffStdHandle(&ProcessParameters->StandardInput, + ProcessHandle, + Peb->ProcessParameters->StandardInput); + } + + /* Duplicate standard output unless it's a console handle */ + if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput)) + { + StuffStdHandle(&ProcessParameters->StandardOutput, + ProcessHandle, + Peb->ProcessParameters->StandardOutput); + } + + /* Duplicate standard error unless it's a console handle */ + if (!IsConsoleHandle(Peb->ProcessParameters->StandardError)) + { + StuffStdHandle(&ProcessParameters->StandardError, + ProcessHandle, + Peb->ProcessParameters->StandardError); + } + } + } + + /* Create the Thread's Stack */ + StackSize = max(256 * 1024, ImageInformation.MaximumStackSize); + Status = BaseCreateStack(ProcessHandle, + ImageInformation.CommittedStackSize, + StackSize, + &InitialTeb); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Creating the thread stack failed: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* Create the Thread's Context */ + BaseInitializeContext(&Context, + Peb, + ImageInformation.TransferAddress, + InitialTeb.StackBase, + 0); + + /* Convert the thread attributes */ + ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, + lpThreadAttributes, + NULL); + if ((hUserToken) && (lpThreadAttributes)) + { + /* If the caller specified a user token, zero the security descriptor */ + LocalThreadAttributes = *lpThreadAttributes; + LocalThreadAttributes.lpSecurityDescriptor = NULL; + ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, + &LocalThreadAttributes, + NULL); + } + + /* Create the Kernel Thread Object */ + Status = NtCreateThread(&ThreadHandle, + THREAD_ALL_ACCESS, + ObjectAttributes, + ProcessHandle, + &ClientId, + &Context, + &InitialTeb, + TRUE); + if (!NT_SUCCESS(Status)) + { + /* A process is not allowed to exist without a main thread, so fail */ + DPRINT1("Creating the main thread failed: %lx\n", Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } + + /* Begin filling out the CSRSS message, first with our IDs and handles */ + CreateProcessMsg->ProcessHandle = ProcessHandle; + CreateProcessMsg->ThreadHandle = ThreadHandle; + CreateProcessMsg->ClientId = ClientId; + + /* Write the remote PEB address and clear it locally, we no longer use it */ + CreateProcessMsg->PebAddressNative = RemotePeb; + CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb; + RemotePeb = NULL; + + /* Now check what kind of architecture this image was made for */ + switch (ImageInformation.Machine) + { + /* IA32, IA64 and AMD64 are supported in Server 2003 */ + case IMAGE_FILE_MACHINE_I386: + CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; + break; + case IMAGE_FILE_MACHINE_IA64: + CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64; + break; + case IMAGE_FILE_MACHINE_AMD64: + CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; + break; + + /* Anything else results in image unknown -- but no failure */ + default: + DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", + ImageInformation.Machine); + CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN; + break; + } + + /* Write the input creation flags except any debugger-related flags */ + CreateProcessMsg->CreationFlags = dwCreationFlags & + ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS); + + /* CSRSS needs to know if this is a GUI app or not */ + if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) || + (IsWowApp)) + { + /* Some flag sent to CSRSS, not sure for what purpose */ + AddToHandle(CreateProcessMsg->ProcessHandle, 2); + + /* Also check if the parent is also a GUI process */ + NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL)); + if ((NtHeaders) && + (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)) + { + /* Let it know that it should dislay the hourglass mouse cursor */ + AddToHandle(CreateProcessMsg->ProcessHandle, 1); + } + } + + /* For all apps, if this flag is on, the hourglass mouse cursor is shown */ + if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK) + { + AddToHandle(CreateProcessMsg->ProcessHandle, 1); + } + + /* Likewise, the opposite holds as well */ + if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK) + { + RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1); + } + + /* Also store which kind of VDM app (if any) this is */ + CreateProcessMsg->VdmBinaryType = VdmBinaryType; + + /* And if it really is a VDM app... */ + if (VdmBinaryType) + { + /* Store the task ID and VDM console handle */ + CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle; + CreateProcessMsg->VdmTask = VdmTask; + } + else if (VdmReserve) + { + /* Extended VDM, set a flag */ + CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX; + } + + /* Check if there's side-by-side assembly data associated with the process */ + if (CreateProcessMsg->Sxs.Flags) + { + /* This should not happen in ReactOS yet */ + DPRINT1("This is an SxS Message -- should not happen yet\n"); + BaseSetLastNTError(STATUS_NOT_IMPLEMENTED); + NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED); + Result = FALSE; + goto Quickie; + } + + /* We are finally ready to call CSRSS to tell it about our new process! */ + CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg, + CaptureBuffer, + CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, + BasepCreateProcess), + sizeof(*CreateProcessMsg)); + + /* CSRSS has returned, free the capture buffer now if we had one */ + if (CaptureBuffer) + { + CsrFreeCaptureBuffer(CaptureBuffer); + CaptureBuffer = NULL; + } + + /* Check if CSRSS failed to accept ownership of the new Windows process */ + if (!NT_SUCCESS(CsrMsg.Status)) + { + /* Terminate the process and enter failure path with the CSRSS status */ + DPRINT1("Failed to tell csrss about new process\n"); + BaseSetLastNTError(CsrMsg.Status); + NtTerminateProcess(ProcessHandle, CsrMsg.Status); + Result = FALSE; + goto Quickie; + } + + /* Check if we have a token due to Authz/Safer, not passed by the user */ + if ((TokenHandle) && !(hUserToken)) + { + /* Replace the process and/or thread token with the one from Safer */ + Status = BasepReplaceProcessThreadTokens(TokenHandle, + ProcessHandle, + ThreadHandle); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to read memory\n"); - goto Cleanup; + /* If this failed, kill the process and enter the failure path */ + DPRINT1("Failed to update process token: %lx\n", Status); + NtTerminateProcess(ProcessHandle, Status); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; } - - /* Duplicate and write the handles */ - BasepDuplicateAndWriteHandle(hProcess, - OurPeb->ProcessParameters->StandardInput, - &RemoteParameters->StandardInput); - BasepDuplicateAndWriteHandle(hProcess, - OurPeb->ProcessParameters->StandardOutput, - &RemoteParameters->StandardOutput); - BasepDuplicateAndWriteHandle(hProcess, - OurPeb->ProcessParameters->StandardError, - &RemoteParameters->StandardError); } - /* Create the first thread */ - DPRINT("Creating thread for process (EntryPoint = 0x%p)\n", - SectionImageInfo.TransferAddress); - hThread = BasepCreateFirstThread(hProcess, - lpThreadAttributes, - &SectionImageInfo, - &ClientId, - dwCreationFlags); - - if (hThread == NULL) + /* Check if a job was associated with this process */ + if (JobHandle) { - DPRINT1("Could not create Initial Thread\n"); - /* FIXME - set last error code */ - goto Cleanup; + /* Bind the process and job together now */ + Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle); + if (!NT_SUCCESS(Status)) + { + /* Kill the process and enter the failure path if binding failed */ + DPRINT1("Failed to assign process to job: %lx\n", Status); + NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED); + BaseSetLastNTError(Status); + Result = FALSE; + goto Quickie; + } } + /* Finally, resume the thread to actually get the process started */ if (!(dwCreationFlags & CREATE_SUSPENDED)) { - NtResumeThread(hThread, &Dummy); + NtResumeThread(ThreadHandle, &ResumeCount); } - /* Return Data */ - lpProcessInformation->dwProcessId = (DWORD)ClientId.UniqueProcess; - lpProcessInformation->dwThreadId = (DWORD)ClientId.UniqueThread; - lpProcessInformation->hProcess = hProcess; - lpProcessInformation->hThread = hThread; - DPRINT("hThread[%p]: %p inside hProcess[%p]: %p\n", hThread, - ClientId.UniqueThread, ClientId.UniqueProcess, hProcess); - hProcess = hThread = NULL; - Ret = TRUE; +VdmShortCircuit: + /* We made it this far, meaning we have a fully created process and thread */ + Result = TRUE; -Cleanup: - /* De-allocate heap strings */ - if (NameBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); - if (ApplicationName.Buffer) - RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer); - if (CurrentDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory); - if (QuotedCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine); + /* Anyone doing a VDM undo should now undo everything, since we are done */ + if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED; - /* Kill any handles still alive */ - if (hSection) NtClose(hSection); - if (hThread) + /* Having a VDM wait object implies this must be a VDM process */ + if (VdmWaitObject) { - /* We don't know any more details than this */ - NtTerminateProcess(hProcess, STATUS_UNSUCCESSFUL); - NtClose(hThread); - } - if (hProcess) NtClose(hProcess); + /* Check if it's a 16-bit separate WOW process */ + if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) + { + /* OR-in the special flag to indicate this, and return to caller */ + AddToHandle(VdmWaitObject, 2); + lpProcessInformation->hProcess = VdmWaitObject; - /* Return Success */ - return Ret; + /* Check if this was a re-used VDM */ + if (VdmUndoLevel & VDM_UNDO_REUSE) + { + /* No Client ID should be returned in this case */ + ClientId.UniqueProcess = 0; + ClientId.UniqueThread = 0; + } + } + else + { + /* OR-in the special flag to indicate this is not a separate VDM */ + AddToHandle(VdmWaitObject, 1); + + /* Return handle to the caller */ + lpProcessInformation->hProcess = VdmWaitObject; + } + + /* Close the original process handle, since it's not needed for VDM */ + if (ProcessHandle) NtClose(ProcessHandle); + } + else + { + /* This is a regular process, so return the real process handle */ + lpProcessInformation->hProcess = ProcessHandle; + } + + /* Return the rest of the process information based on what we have so far */ + lpProcessInformation->hThread = ThreadHandle; + lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess); + lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread); + + /* NULL these out here so we know to treat this as a success scenario */ + ProcessHandle = NULL; + ThreadHandle = NULL; + +Quickie: + /* Free the debugger command line if one was allocated */ + if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); + + /* Check if an SxS full path as queried */ + if (PathBuffer) + { + /* Reinitialize the executable path */ + RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0); + SxsWin32ExePath.Length = 0; + + /* Free the path buffer */ + RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer); + } + +#if _SXS_SUPPORT_ENABLED_ + /* Check if this was a non-VDM process */ + if (!VdmBinaryType) + { + /* Then it must've had SxS data, so close the handles used for it */ + BasepSxsCloseHandles(&Handles); + BasepSxsCloseHandles(&FileHandles); + + /* Check if we built SxS byte buffers for this create process request */ + if (SxsConglomeratedBuffer) + { + /* Loop all of them */ + for (i = 0; i < 5; i++) + { + /* Check if this one was allocated */ + ThisBuffer = SxsStaticBuffers[i]; + if (ThisBuffer) + { + /* Get the underlying RTL_BUFFER structure */ + ByteBuffer = &ThisBuffer->ByteBuffer; + if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer)) + { + /* Check if it was dynamic */ + if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer) + { + /* Free it from the heap */ + FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer; + RtlFreeUnicodeString(&FreeString); + } + + /* Reset the buffer to its static data */ + ByteBuffer->Buffer = ByteBuffer->StaticBuffer; + ByteBuffer->Size = ByteBuffer->StaticSize; + } + + /* Reset the string to the static buffer */ + RtlInitEmptyUnicodeString(&ThisBuffer->String, + (PWCHAR)ByteBuffer->StaticBuffer, + ByteBuffer->StaticSize); + if (ThisBuffer->String.Buffer) + { + /* Also NULL-terminate it */ + *ThisBuffer->String.Buffer = UNICODE_NULL; + } + } + } + } + } +#endif + /* Check if an environment was passed in */ + if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) + { + /* Destroy it */ + RtlDestroyEnvironment(lpEnvironment); + + /* If this was the VDM environment too, clear that as well */ + if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL; + lpEnvironment = NULL; + } + + /* Unconditionally free all the name parsing buffers we always allocate */ + RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine); + RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); + RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory); + RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); + + /* Close open file/section handles */ + if (FileHandle) NtClose(FileHandle); + if (SectionHandle) NtClose(SectionHandle); + + /* If we have a thread handle, this was a failure path */ + if (ThreadHandle) + { + /* So kill the process and close the thread handle */ + NtTerminateProcess(ProcessHandle, 0); + NtClose(ThreadHandle); + } + + /* If we have a process handle, this was a failure path, so close it */ + if (ProcessHandle) NtClose(ProcessHandle); + + /* Thread/process handles, if any, are now processed. Now close this one. */ + if (JobHandle) NtClose(JobHandle); + + /* Check if we had created a token */ + if (TokenHandle) + { + /* And if the user asked for one */ + if (hUserToken) + { + /* Then return it */ + *hNewToken = TokenHandle; + } + else + { + /* User didn't want it, so we used it temporarily -- close it */ + NtClose(TokenHandle); + } + } + + /* Free any temporary app compatibility data, it's no longer needed */ + BasepFreeAppCompatData(AppCompatData, AppCompatSxsData); + + /* Free a few strings. The API takes care of these possibly being NULL */ + RtlFreeUnicodeString(&VdmString); + RtlFreeUnicodeString(&DebuggerString); + + /* Check if we had built any sort of VDM environment */ + if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer)) + { + /* Free it */ + BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv); + } + + /* Check if this was any kind of VDM application that we ended up creating */ + if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED))) + { + /* Send an undo */ + BaseUpdateVDMEntry(VdmEntryUndo, + (PHANDLE)&VdmTask, + VdmUndoLevel, + VdmBinaryType); + + /* And close whatever VDM handle we were using for notifications */ + if (VdmWaitObject) NtClose(VdmWaitObject); + } + + /* Check if we ended up here with an allocated search path, and free it */ + if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath); + + /* Finally, return the API's result */ + return Result; } /* @@ -3311,7 +4590,7 @@ CreateProcessW(LPCWSTR lpApplicationName, LPPROCESS_INFORMATION lpProcessInformation) { /* Call the internal (but exported) version */ - return CreateProcessInternalW(0, + return CreateProcessInternalW(NULL, lpApplicationName, lpCommandLine, lpProcessAttributes, @@ -3479,7 +4758,7 @@ CreateProcessA(LPCSTR lpApplicationName, LPPROCESS_INFORMATION lpProcessInformation) { /* Call the internal (but exported) version */ - return CreateProcessInternalA(0, + return CreateProcessInternalA(NULL, lpApplicationName, lpCommandLine, lpProcessAttributes, diff --git a/reactos/dll/win32/kernel32/client/vdm.c b/reactos/dll/win32/kernel32/client/vdm.c index bfbde543b6e..69a701079db 100644 --- a/reactos/dll/win32/kernel32/client/vdm.c +++ b/reactos/dll/win32/kernel32/client/vdm.c @@ -73,7 +73,8 @@ BaseCheckVDM(IN ULONG BinaryType, IN PCSR_API_MESSAGE ApiMessage, IN OUT PULONG iTask, IN DWORD CreationFlags, - IN LPSTARTUPINFOW StartupInfo) + IN LPSTARTUPINFOW StartupInfo, + IN HANDLE hUserToken OPTIONAL) { /* This is not supported */ UNIMPLEMENTED; diff --git a/reactos/dll/win32/kernel32/include/kernel32.h b/reactos/dll/win32/kernel32/include/kernel32.h index 9a203d75a39..9e5e6659dcb 100644 --- a/reactos/dll/win32/kernel32/include/kernel32.h +++ b/reactos/dll/win32/kernel32/include/kernel32.h @@ -410,13 +410,13 @@ BasepLocateExeLdrEntry(IN PLDR_DATA_TABLE_ENTRY Entry, typedef NTSTATUS (NTAPI *PBASEP_APPCERT_PLUGIN_FUNC)( - IN PCHAR ApplicationName, + IN LPWSTR ApplicationName, IN ULONG CertFlag ); typedef NTSTATUS (NTAPI *PBASEP_APPCERT_EMBEDDED_FUNC)( - IN PCHAR ApplicationName + IN LPWSTR ApplicationName ); typedef NTSTATUS @@ -491,6 +491,22 @@ BaseCheckForVDM( OUT LPDWORD ExitCode ); +BOOL +WINAPI +BaseCheckVDM( + IN ULONG BinaryType, + IN PCWCH ApplicationName, + IN PCWCH CommandLine, + IN PCWCH CurrentDirectory, + IN PANSI_STRING AnsiEnvironment, + IN PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG iTask, + IN DWORD CreationFlags, + IN LPSTARTUPINFOW StartupInfo, + IN HANDLE hUserToken OPTIONAL +); + + /* FIXME: This is EXPORTED! It should go in an external kernel32.h header */ VOID WINAPI @@ -498,3 +514,14 @@ BasepFreeAppCompatData( IN PVOID AppCompatData, IN PVOID AppCompatSxsData ); + +NTSTATUS +WINAPI +BasepCheckWinSaferRestrictions( + IN HANDLE UserToken, + IN LPWSTR ApplicationName, + IN HANDLE FileHandle, + OUT PBOOLEAN InJob, + OUT PHANDLE NewToken, + OUT PHANDLE JobHandle +);