/*++ Copyright (c) 2000 Microsoft Corporation Module Name: sandbox.c Abstract: Utilities to run code in isolated processes (sandbox apis). Author: Jim Schmidt (jimschm) 31-Jan-2000 Revision History: --*/ // // Includes // #include "pch.h" #include "utilsp.h" #define DBG_SANDBOX "Sandbox" // // Strings // // None // // Constants // #define S_SBCLASS TEXT("SandboxHost") // // Macros // // None // // Types // typedef struct { BOOL Win32; HANDLE Mapping; HANDLE Ack; UINT Instance; TCHAR WindowTitle[64]; } IPCDATA, *PIPCDATA; typedef struct { DWORD Command; DWORD Result; DWORD TechnicalLogId; DWORD GuiLogId; DWORD DataSize; BYTE Data[]; } MAPDATA, *PMAPDATA; // // Globals // static PCTSTR g_Mode; static BOOL g_Sandbox; static TCHAR g_ExePath16[MAX_TCHAR_PATH] = TEXT("sandbx16.exe"); static TCHAR g_ExePath32[MAX_TCHAR_PATH] = TEXT("sandbx32.exe"); // // Macro expansion list // // None // // Private function prototypes // LRESULT CALLBACK pIpcMessageProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); BOOL pCreateExchangeThread ( IN UINT Instance ); // // Macro expansion definition // // None // // Code // BOOL SbInitialize ( IN BOOL SandboxProcess ) { WNDCLASS wc; // // Set the globals // g_Sandbox = SandboxProcess; g_Mode = SandboxProcess ? TEXT("Sandbox") : TEXT("HostProc"); g_ProcessHandle = NULL; // // Register the window class for message passing // ZeroMemory (&wc, sizeof (wc)); wc.lpfnWndProc = pIpcMessageProc; wc.hInstance = g_hInst; wc.lpszClassName = S_SBCLASS; RegisterClass (&wc); return TRUE; } VOID pCloseIpcData ( IN OUT PIPCDATA IpcData ) { if (IpcData->Ack) { CloseHandle (IpcData->Ack); } if (IpcData->Mapping) { CloseHandle (IpcData->Mapping); } if (IpcData->ProcessHandle) { CloseHandle (IpcData->ProcessHandle); } if (IpcData->File && IpcData->File != INVALID_HANDLE_VALUE) { CloseHandle (IpcData->File); } if (IpcData->HostProcHwnd) { DestroyWindow (ipcData->HostProcHwnd); } ZeroMemory (IpcData, sizeof (IPCDATA)); } DWORD WINAPI pAckThread ( PVOID Arg ) { PIPCDATA ipcData = (PIPCDATA) Arg; MSG msg; HWND hwnd; // // Create a message-only hwnd // hwnd = CreateWindow ( S_SBCLASS, ipcData->WindowTitle, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, ipcData->Instance ); if (!hwnd) { LOG ((LOG_ERROR, "Failed to create host message window")); return 0; } // // Loop until the window is destroyed // while (GetMessage (&msg, hwnd, 0, 0)) { DispatchMessage (&msg); if (msg.message == WM_NCDESTROY) { break; } } return 1; } SBHANDLE SbCreateSandboxA ( IN PCSTR DllPath, IN PCSTR WorkingDir OPTIONAL ) { PSECURITY_DESCRIPTOR psd = NULL; SECURITY_ATTRIBUTES sa, *psa; BOOL result = FALSE; PIPCDATA ipcData = NULL; static UINT instance = 0; TCHAR objectName[64]; TCHAR cmdLine[MAX_TCHAR_PATH * 2]; BOOL win32; STARTUPINFOA si; PROCESS_INFORMATION pi; BOOL processResult; HANDLE objectArray[2]; UINT u; DWORD rc; TCHAR tempPath[MAX_TCHAR_PATH]; TCHAR tempFile[MAX_TCHAR_PATH]; __try { // // TODO - need mutex to guard instance variable // TODO - need to test instance variable against window title // TODO - need to detect dll type // win32 = TRUE; // // Allocate an IPCDATA struct, then fill it in // ipcData = (PIPCDATA) MemAlloc (g_hHeap, HEAP_ZERO_MEMORY, sizeof (IPCDATA)); ipcData.Win32 = win32; ipcData.Instance = instance; if (ISNT()) { // // Create nul DACL for NT // ZeroMemory (&sa, sizeof (sa)); psd = (PSECURITY_DESCRIPTOR) MemAlloc (g_hHeap, 0, SECURITY_DESCRIPTOR_MIN_LENGTH); if (!InitializeSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION)) { __leave; } if (!SetSecurityDescriptorDacl (psd, TRUE, (PACL) NULL, FALSE)) { __leave; } sa.nLength = sizeof (sa); sa.lpSecurityDescriptor = psd; psa = &sa; } else { psa = NULL; } // // Create the IPC objects: an event and a memory mapped file // ipcData->Ack = CreateEvent (psa, FALSE, FALSE, NULL); wsprintf (objectName, TEXT("Sandbox%u.IpcData"), instance); ipcData->Mapping = CreateFileMapping ( INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, 0x10000, objectName ); if (!ipcData->Ack || !ipcData->Mapping) { LOG ((LOG_ERROR, "Can't create IPC objects")); __leave; } // // Create the ack window proc thread and have it wait for messages // wsprintf (ipcData->WindowTitle, TEXT("SandboxHost%u"), instance); if (!pCreateExchangeThread()) { LOG ((LOG_ERROR, "Can't create ack thread")); __leave; } // // Launch the sandbox process // wsprintfA ( cmdLine, "\"%s\" -i:%u", win32 ? g_ExePath32 : g_ExePath16, instance ); ZeroMemory (&si, sizeof (si)); si.cb = sizeof (si); si.dwFlags = STARTF_FORCEOFFFEEDBACK; processResult = CreateProcessA ( NULL, cmdLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, WorkingDir, &si, &pi ); if (!processResult) { LOG ((LOG_ERROR, "Cannot start %s", cmdLine)); __leave; } CloseHandle (pi.hThread); ipcData->ProcessHandle = pi.hProcess; // // Wait for process to fail or wait for it to send an ack // objectArray[0] = ipcData->Ack; objectArray[1] = pi.hProcess; rc = WaitForMultipleObjects (2, objectArray, FALSE, 60000); if (rc != WAIT_OBJECT_0) { DEBUGMSG (( DBG_WARNING, "Process %x did not signal 'ready'. Wait timed out. (%s)", g_ProcessHandle, g_Mode )); LOG ((LOG_ERROR, "Failed to launch sandbox.")); __leave; } // // Launch was successful -- sandbox is now waiting for a command // DEBUGMSG ((DBG_SANDBOX, "Process %s is running (%s)", cmdLine, g_Mode)); instance++; result = TRUE; } __finally { // // Cleanup code // PushError(); if (!result) { if (ipcData) { pCloseIpcData (ipcData); MemFree (g_hHeap, 0, ipcData); } } if (psd) { MemFree (g_hHeap, 0, psd); } PopError(); } return result ? (SBHANDLE) ipcData : NULL; } VOID SbDestroySandbox ( IN SBHANDLE SandboxHandle ) { PIPCDATA ipcData = (PIPCDATA) SandboxHandle; DWORD rc; COPYDATA copyData; if (ipcData) { // // Tell sandbox to close // if (ipcData->Win32) { // // Turn off the ready event // MYASSERT (WAIT_OBJECT_0 == WaitForSingleObject (ipcData->ReadyEvent, 0)); ResetEvent (ipcData->ReadyEvent); // // Wait for the sandbox to close, kill it if necessary // rc = WaitForSingleObject (ipcData->ProcessHandle, 10000); if (rc != WAIT_OBJECT_0) { TerminateProcess (ipcData->ProcessHandle, 0); } } else { // // Send a shutdown message to the sandbox // ZeroMemory (©Data, sizeof (copyData)); copyData.dwData = SB_CLOSE; SendMessage ( ipcData->SandboxHwnd, WM_COPYDATA, ipcData->HostProcHwnd, copyData ); // // Wait for the sandbox to close, kill it if necessary // rc = WaitForSingleObject (ipcData->ProcessHandle, 10000); if (rc != WAIT_OBJECT_0) { TerminateProcess (ipcData->ProcessHandle, 0); } } // // Clean up resources // pCloseIpcData (ipcData); MemFree (g_hHeap, 0, ipcData); } } LRESULT CALLBACK pIpcMessageProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { COPYDATASTRUCT *p; switch (uMsg) { case WM_COPYDATA: p = (COPYDATASTRUCT *) lParam; break; } return DefWindowProc (hwnd, uMsg, wParam, lParam); } BOOL pCreateExchangeThread ( IN UINT Instance ) { HANDLE thread; thread = StartThread (pAckThread, (PVOID) Instance); return thread != NULL; }