mirror of
https://github.com/PaperCutSoftware/GhostTrap.git
synced 2025-12-11 00:34:39 -06:00
711 lines
22 KiB
C++
Executable File
711 lines
22 KiB
C++
Executable File
/*
|
|
* Copyright (c) 2012-2023 PaperCut Software Pty Ltd
|
|
* http://www.papercut.com/
|
|
*
|
|
* Author: Chris Dance <chris.dance@papercut.com>
|
|
*
|
|
* $Id: $
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* ----
|
|
* Ghost Trap - GPL Ghostscript secured using Google Chrome sandbox
|
|
* technology (aka " the Ecto Containment Unit" :-).
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <iostream>
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
|
|
// Ghostscript DLL Header files
|
|
#include <ierrors.h>
|
|
#include <iapi.h>
|
|
|
|
#include "sandbox_procmgmt.h"
|
|
|
|
/*
|
|
* Ghost Trap version number starts at 1 and suffixes the Ghostscript version we've
|
|
* tested/written against.
|
|
*/
|
|
#define GHOST_TRAP_VERSION "1.4.10.00"
|
|
#define GHOST_TRAP_COPYRIGHT "Copyright (c) 2012-2023 PaperCut Software Pty Ltd"
|
|
|
|
const wchar_t* PARAM_OUTPUT_FILE = L"OutputFile=";
|
|
const wchar_t* PARAM_FAIL_TEST = L"--fail-test=";
|
|
|
|
// Definitions
|
|
typedef struct GSDLL_S {
|
|
HINSTANCE hmodule; /* DLL module handle */
|
|
PFN_gsapi_revision revision;
|
|
PFN_gsapi_new_instance new_instance;
|
|
PFN_gsapi_delete_instance delete_instance;
|
|
PFN_gsapi_set_stdio set_stdio;
|
|
PFN_gsapi_set_poll set_poll;
|
|
PFN_gsapi_set_display_callback set_display_callback;
|
|
PFN_gsapi_init_with_args init_with_args;
|
|
PFN_gsapi_run_string run_string;
|
|
PFN_gsapi_exit exit;
|
|
} GSDLL;
|
|
|
|
// Globals
|
|
GSDLL global_gsdll;
|
|
void *global_gsinstance;
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Load the Ghostscript Windows DLL and store implementation in GSDLL instances.
|
|
* Any errors are printed on STDOUT.
|
|
*/
|
|
static bool LoadGSDLL(GSDLL *gsdll) {
|
|
|
|
#ifdef _WIN64
|
|
const wchar_t name[] = L"gsdll64.dll";
|
|
#else
|
|
const wchar_t name[] = L"gsdll32.dll";
|
|
#endif
|
|
|
|
wchar_t fullname[1024];
|
|
wchar_t *p;
|
|
|
|
/* First try to load DLL from the same directory as EXE */
|
|
::GetModuleFileName(::GetModuleHandle(NULL), fullname, sizeof(fullname));
|
|
if ((p = wcsrchr(fullname, L'\\')) != NULL) {
|
|
++p;
|
|
} else {
|
|
p = fullname;
|
|
}
|
|
*p = L'\0';
|
|
wcscat_s(fullname, name);
|
|
|
|
gsdll->hmodule = LoadLibrary(fullname);
|
|
|
|
if (gsdll->hmodule < (HINSTANCE)HINSTANCE_ERROR) {
|
|
/* Failed */
|
|
DWORD err = GetLastError();
|
|
fprintf(stderr, "Can't load GhostScript DLL, LoadLibrary error code %ld\n", err);
|
|
return false;
|
|
}
|
|
|
|
gsdll->revision = (PFN_gsapi_revision) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_revision");
|
|
if (gsdll->revision == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_revision\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->new_instance = (PFN_gsapi_new_instance) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_new_instance");
|
|
if (gsdll->new_instance == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_new_instance\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->delete_instance = (PFN_gsapi_delete_instance) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_delete_instance");
|
|
if (gsdll->delete_instance == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_delete_instance\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->set_stdio = (PFN_gsapi_set_stdio) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_set_stdio");
|
|
if (gsdll->set_stdio == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_set_stdio\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->set_poll = (PFN_gsapi_set_poll) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_set_poll");
|
|
if (gsdll->set_poll == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_set_poll\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->set_display_callback = (PFN_gsapi_set_display_callback)
|
|
GetProcAddress(gsdll->hmodule, "gsapi_set_display_callback");
|
|
if (gsdll->set_display_callback == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_set_display_callback\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->init_with_args = (PFN_gsapi_init_with_args)
|
|
GetProcAddress(gsdll->hmodule, "gsapi_init_with_args");
|
|
if (gsdll->init_with_args == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_init_with_args\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->run_string = (PFN_gsapi_run_string) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_run_string");
|
|
if (gsdll->run_string == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_run_string\n");
|
|
return false;
|
|
}
|
|
|
|
gsdll->exit = (PFN_gsapi_exit) GetProcAddress(gsdll->hmodule,
|
|
"gsapi_exit");
|
|
if (gsdll->exit == NULL) {
|
|
fprintf(stderr, "Can't find gsapi_exit\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* A convenience wrapper to read a registry keys using wstrings.
|
|
*/
|
|
static LONG GetStringRegKey(HKEY hKey, const std::wstring &strValueName, std::wstring &strValue, const std::wstring &strDefaultValue) {
|
|
strValue = strDefaultValue;
|
|
WCHAR szBuffer[512];
|
|
DWORD dwBufferSize = sizeof(szBuffer);
|
|
ULONG nError;
|
|
nError = RegQueryValueExW(hKey, strValueName.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize);
|
|
if (ERROR_SUCCESS == nError) {
|
|
strValue = szBuffer;
|
|
}
|
|
return nError;
|
|
}
|
|
|
|
|
|
/*
|
|
* Allow access to a particular file. This function also ensures the file path is valid according
|
|
* to Sandbox requirements (an absolute path). If parent_dir is set to TRUE, access to the whole
|
|
* parent directory (all files) is allowed. Passing a relative file path will raise an error
|
|
* and no white listing will occure.
|
|
*/
|
|
static void AllowAccessToFile(std::unique_ptr<sandbox::TargetPolicy> &policy, wchar_t *file, BOOL parent_dir) {
|
|
|
|
wchar_t drive[8];
|
|
wchar_t dir[512];
|
|
wchar_t filename[512];
|
|
wchar_t ext[256];
|
|
errno_t rc;
|
|
|
|
rc = _wsplitpath_s(
|
|
file, /* the path */
|
|
drive, /* drive */
|
|
8, /* drive buffer size */
|
|
dir, /* dir buffer */
|
|
512, /* dir buffer size */
|
|
filename, /* filename */
|
|
512, /* filename size */
|
|
ext, /* extension */
|
|
256 /* extension size */
|
|
);
|
|
|
|
if (rc != 0) {
|
|
fprintf(stderr, "Ghost Trap: Unable to parse file paths to whitelist - use absolute paths.\n");
|
|
} else {
|
|
wchar_t path_rule[MAX_PATH];
|
|
|
|
if (wcslen(drive) == 0) {
|
|
// Relative paths and network paths don't work in the sandbox :-(
|
|
fprintf(stderr, "Ghost Trap: Invalid resource. Please use absolute paths on a local drive.\n");
|
|
return;
|
|
}
|
|
|
|
if (parent_dir) {
|
|
_snwprintf_s(path_rule, MAX_PATH - 1, L"%s%s*",
|
|
drive,
|
|
dir);
|
|
} else {
|
|
_snwprintf_s(path_rule, MAX_PATH - 1, L"%s%s%s%s",
|
|
drive,
|
|
dir,
|
|
filename,
|
|
ext);
|
|
}
|
|
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowAny,
|
|
path_rule
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Look through the standard Ghostscript cmd-line arguments looking for paths.
|
|
* Expand relative and normalize all paths. This is required as at the time of
|
|
* writing, any open file request in the Google Chrome sandbox must be an absolute
|
|
* path. This function will pass back a new **argv instance any any expanded
|
|
* paths will be malloced buffers.
|
|
*
|
|
* TODO: The input file argument is assumed to be the last argument. This is not
|
|
* necessarily so via the -f option.
|
|
*
|
|
* FIXME: What about other paths such as defining a custom libary files or fonts?
|
|
*/
|
|
static wchar_t ** ExpandPathsInArgs(int argc, wchar_t *argv[]) {
|
|
wchar_t **full_path_argv = (wchar_t **) calloc(argc, sizeof(full_path_argv[0]));
|
|
|
|
if (full_path_argv == NULL) {
|
|
fprintf(stderr, "Ghost Trap: Failed to initialise due to malloc failure\n");
|
|
return argv;
|
|
}
|
|
|
|
wchar_t new_arg[MAX_PATH + 20];
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
wchar_t *current_argv = argv[i];
|
|
|
|
full_path_argv[i] = current_argv;
|
|
// Convert relative paths to absolute if found.
|
|
wchar_t *p;
|
|
if ((p = wcsstr(argv[i], PARAM_OUTPUT_FILE)) != NULL) {
|
|
p += wcslen(PARAM_OUTPUT_FILE);
|
|
|
|
wchar_t full_path[MAX_PATH];
|
|
|
|
if (GetFullPathName(p, MAX_PATH, full_path, NULL) > 0) {
|
|
|
|
full_path_argv[i] = (wchar_t *) malloc(sizeof(new_arg));
|
|
_snwprintf(full_path_argv[i], sizeof(new_arg), L"-sOutputFile=%s", full_path);
|
|
}
|
|
}
|
|
|
|
// Make the input file absolute if required. We assume this is the last argument.
|
|
// FIXME: Assumption not quite correct with -f option.
|
|
if ((i + 1) == argc) {
|
|
wchar_t *last_arg = argv[argc - 1];
|
|
if (*last_arg != L'-') {
|
|
|
|
if (GetFullPathName(last_arg, MAX_PATH, new_arg, NULL) > 0) {
|
|
|
|
full_path_argv[i] = (wchar_t *) malloc(sizeof(new_arg));
|
|
_snwprintf(full_path_argv[i], sizeof(new_arg), L"%s", new_arg);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return full_path_argv;
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is passed into RunConsoleAppInSandbox and is called to apply the sandbox's
|
|
* access policies.
|
|
*
|
|
* IMPORTANT: This code does not run in the sandbox (runs in the parent process). Take care!
|
|
*/
|
|
static void ApplyPolicy(std::unique_ptr<sandbox::TargetPolicy> &policy, int argc, wchar_t* argv[]) {
|
|
// Fix up and expand paths in the args
|
|
wchar_t **nargv = ExpandPathsInArgs(argc, argv);
|
|
|
|
/*
|
|
* These items need to be white listed:
|
|
* - READ access on the directories on GS_LIB.
|
|
* - READ access to Ghostscript specific registry keys.
|
|
* - FULL (write) access to target file output directory.
|
|
* - FULL (write) access to the user's temp directory.
|
|
* - READ access to the Windows Font directory.
|
|
*/
|
|
|
|
// The parent needs to load the DLL to get version information so we
|
|
// can calculate paths (e.g. product and version number).
|
|
if (!LoadGSDLL(&global_gsdll)) {
|
|
fprintf(stderr, "Ghost Trap: Parent unable to load GS DLL\n");
|
|
return;
|
|
}
|
|
|
|
gsapi_revision_t rv;
|
|
if (global_gsdll.revision(&rv, sizeof(rv)) != 0) {
|
|
fprintf(stderr, "Ghost Trap: Unable to identify Ghostscript DLL revision\n");
|
|
return;
|
|
}
|
|
|
|
// *******************************************************************************************************
|
|
// Registry sandboxing has been removed from the Chromium sandbox. Commenting out the code for prosterity.
|
|
// policy->AddRule(
|
|
// sandbox::TargetPolicy::SUBSYS_REGISTRY,
|
|
// sandbox::TargetPolicy::REG_ALLOW_READONLY,
|
|
// L"HKEY_CURRENT_USER"
|
|
// );
|
|
|
|
// Allow READ access to OS keys (e.g. Locale lookup)
|
|
// policy->AddRule(
|
|
// sandbox::TargetPolicy::SUBSYS_REGISTRY,
|
|
// sandbox::TargetPolicy::REG_ALLOW_READONLY,
|
|
// L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet"
|
|
// );
|
|
|
|
// std::wstringstream white_list_path;
|
|
// white_list_path << L"HKEY_LOCAL_MACHINE\\SOFTWARE\\" << rv.product << L"\\*";
|
|
|
|
// Allow READ access to Ghostscript spacific registry keys.
|
|
// policy->AddRule(
|
|
// sandbox::TargetPolicy::SUBSYS_REGISTRY,
|
|
// sandbox::TargetPolicy::REG_ALLOW_READONLY,
|
|
// white_list_path.str().c_str()
|
|
// );
|
|
// *******************************************************************************************************
|
|
|
|
// Read GS_LIB and whitelist path - as per dwdll.c
|
|
wchar_t gs_key[256];
|
|
wchar_t dotversion[16];
|
|
_snwprintf(dotversion, 16, L"%d.%02d",
|
|
(int)(rv.revision / 100),
|
|
(int)(rv.revision % 100));
|
|
_snwprintf(gs_key, 256, L"SOFTWARE\\%hs\\%s",
|
|
rv.product,
|
|
dotversion);
|
|
|
|
// Allow READ access to directory on lib path. Find this by looking
|
|
// up the registry key HKLM/Software/GPL Ghostscript/9.02/GS_LIB.
|
|
// The value is stored is a ";" seperated path.
|
|
HKEY hKey;
|
|
DWORD result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, gs_key, 0, KEY_READ, &hKey);
|
|
if (ERROR_SUCCESS == result) {
|
|
std::wstring gs_lib_path;
|
|
GetStringRegKey(hKey, L"GS_LIB", gs_lib_path, L"");
|
|
|
|
wchar_t* part = wcstok(&gs_lib_path[0], L";", 0);
|
|
while(part != NULL) {
|
|
wchar_t lib_path[MAX_PATH];
|
|
_snwprintf(lib_path, MAX_PATH, L"%s\\*", part);
|
|
|
|
// Whitelist the LIB dir.
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowReadonly,
|
|
lib_path
|
|
);
|
|
|
|
part = wcstok(NULL, L";", 0);
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
// Allow READ and WRITE access to default temp directory.
|
|
{
|
|
wchar_t temp_dir[MAX_PATH];
|
|
GetTempPath(MAX_PATH - 1, temp_dir);
|
|
wchar_t dir_rule[MAX_PATH];
|
|
_snwprintf(dir_rule, MAX_PATH - 1, L"%s*", temp_dir);
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowAny,
|
|
dir_rule
|
|
);
|
|
}
|
|
|
|
|
|
// Allow READ access to C:\Windows\Fonts directory.
|
|
{
|
|
wchar_t win_dir[MAX_PATH];
|
|
GetWindowsDirectory(win_dir, MAX_PATH - 1);
|
|
wchar_t dir_rule[MAX_PATH];
|
|
_snwprintf(dir_rule, MAX_PATH - 1, L"%s\\Fonts\\*", win_dir);
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowReadonly,
|
|
dir_rule
|
|
);
|
|
}
|
|
|
|
// Allow WRITE access to OutputFile target directory
|
|
BOOL has_outfile = FALSE;
|
|
BOOL test_enabled = FALSE;
|
|
BOOL help_requested = FALSE;
|
|
int i;
|
|
for (i = 0; i < argc; ++i) {
|
|
wchar_t *p;
|
|
|
|
if (wcscmp(argv[i], L"-h") == 0) {
|
|
printf("Ghost Trap: GPL Ghostscript running in the Google Chromium Sandbox.\n");
|
|
printf("Ghost Trap: Version %s\n", GHOST_TRAP_VERSION);
|
|
printf("Ghost Trap: %s\n", GHOST_TRAP_COPYRIGHT);
|
|
printf("\n");
|
|
help_requested = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if ((p = wcsstr(nargv[i], PARAM_OUTPUT_FILE)) != NULL) {
|
|
p += wcslen(PARAM_OUTPUT_FILE);
|
|
AllowAccessToFile(policy, p, TRUE);
|
|
has_outfile = TRUE;
|
|
}
|
|
|
|
if ((p = wcsstr(nargv[i], L"--test-sandbox")) != NULL) {
|
|
test_enabled = TRUE;
|
|
}
|
|
|
|
// Sandbox Testing - whitelist test data
|
|
if ((p = wcsstr(nargv[i], PARAM_FAIL_TEST)) != NULL) {
|
|
p += wcslen(PARAM_FAIL_TEST);
|
|
|
|
/* Setup Test 1 for failure
|
|
* Allow write access to C:\Windows\Temp folder
|
|
*/
|
|
if(wcscmp(p, L"1") == 0 && test_enabled) {
|
|
wchar_t win_dir[MAX_PATH];
|
|
GetWindowsDirectory(win_dir, MAX_PATH - 1);
|
|
wchar_t dir_rule[MAX_PATH];
|
|
_snwprintf(dir_rule, MAX_PATH - 1, L"%s\\Temp\\*", win_dir);
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowAny,
|
|
dir_rule
|
|
);
|
|
}
|
|
|
|
/* Setup Test 2 for failure
|
|
* Allow read-only access to C:\Windows\notepad.exe
|
|
*/
|
|
if(wcscmp(p, L"2") == 0 && test_enabled) {
|
|
wchar_t win_dir[MAX_PATH];
|
|
GetWindowsDirectory(win_dir, MAX_PATH - 1);
|
|
wchar_t dir_rule[MAX_PATH];
|
|
_snwprintf(dir_rule, MAX_PATH - 1, L"%s\\notepad.exe", win_dir);
|
|
(void) policy->GetConfig()->AddRule(
|
|
sandbox::SubSystem::kFiles,
|
|
sandbox::Semantics::kFilesAllowReadonly,
|
|
dir_rule
|
|
);
|
|
}
|
|
|
|
/* Setup Test 3 for failure
|
|
* Allow read access to registry key HKCU\Environment
|
|
*
|
|
* Note: Registry sandboxing was removed from the Chromium sandbox. Commenting code for prosterity.
|
|
*/
|
|
// if(wcscmp(p, L"3") == 0 && test_enabled) {
|
|
// policy->AddRule(
|
|
// sandbox::TargetPolicy::SUBSYS_REGISTRY,
|
|
// sandbox::TargetPolicy::REG_ALLOW_READONLY,
|
|
// L"HKEY_CURRENT_USER\\Environment"
|
|
// );
|
|
// }
|
|
}
|
|
}
|
|
|
|
// If no OutputFile, add READ/WRITE access to current working directory?
|
|
if (!has_outfile && !test_enabled && !help_requested) {
|
|
fprintf(stderr, "Ghost Trap: An OutputFile with an absolute path is required.\n");
|
|
}
|
|
|
|
// Add READ/WRITE access to the provided input file (assume this will be the last arg).
|
|
wchar_t *last_arg = nargv[argc - 1];
|
|
if (*last_arg != L'-') {
|
|
AllowAccessToFile(policy, last_arg, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* For convendience, we load the DLL in the child before we sandbox. At this point we're
|
|
* not running any user supplied code so this should be a safe operation.
|
|
*
|
|
* IMPORTANT: This code does not run in the sandbox. Take care when adding new code!
|
|
*/
|
|
static int PreSandboxedInit(int argc, wchar_t* argv[]) {
|
|
if (!LoadGSDLL(&global_gsdll)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert wchar to unicode. Similar to function in gp_wutf8.c in Ghostscript,
|
|
* however without the buffer length calculation problem on generation.
|
|
*/
|
|
static int wchar_to_utf8(char *out, const wchar_t *in) {
|
|
unsigned int i;
|
|
unsigned int len = 1;
|
|
|
|
if (out) {
|
|
while ((i = (unsigned int)*in++)) {
|
|
if (i < 0x80) {
|
|
*out++ = (char)i;
|
|
len++;
|
|
} else if (i < 0x800) {
|
|
*out++ = 0xC0 | ( i>> 6 );
|
|
*out++ = 0x80 | ( i & 0x3F);
|
|
len += 2;
|
|
} else /* if (i < 0x10000) */ {
|
|
*out++ = 0xE0 | ( i>>12 );
|
|
*out++ = 0x80 | ((i>> 6) & 0x3F);
|
|
*out++ = 0x80 | ( i & 0x3F);
|
|
len += 3;
|
|
}
|
|
}
|
|
*out = 0;
|
|
} else {
|
|
while ((i = (unsigned int)*in++)) {
|
|
if (i < 0x80) {
|
|
len++;
|
|
} else if (i < 0x800) {
|
|
len += 2;
|
|
} else /* if (i < 0x10000) */ {
|
|
len += 3;
|
|
}
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* This is an internal method to do a few simple checks to make sure our sandbox is working.
|
|
* The build script will use this to verify that everything is working as expected.
|
|
* A non-zero exit code indicates a possible error that should be looked at.
|
|
*
|
|
* For the moment our tests are:
|
|
* 1 - Check that we can't write a file we should not - i.e. file in c:\Windows\Temp
|
|
* 2 - Check that we can't read files from the general os filesystem (c:\Windows\notepad.exe)
|
|
* 3 - Check that we can't read general registry entries (HKEY_CURRENT_USER\\Environment)
|
|
*
|
|
* The tests can be setup to fail (whitelisted parameters) by adding the --fail-test flag.
|
|
* e.g.
|
|
* --fail-test=1 [will fail test 1]
|
|
* --fail-test=2 [will fail test 2]
|
|
*/
|
|
static int TestSandbox() {
|
|
/*
|
|
* If C:\Windows\Temp\test.txt is whitelisted via -sOutputFile or the last param
|
|
* then this will fail
|
|
*/
|
|
std::ofstream output("C:\\Windows\\Temp\\test.txt");
|
|
if (output.is_open()) {
|
|
// We shouldn't be able to read this file
|
|
return 61;
|
|
}
|
|
|
|
/*
|
|
* Test 2
|
|
* If C:\Windows\\notepad.exe is the last parameter, this will succeed as we
|
|
* are whitelisting the input file.
|
|
*/
|
|
std::ifstream input("C:\\Windows\\notepad.exe", std::ios::binary);
|
|
if (input.is_open()) {
|
|
// Oh... we can read it! Oh no.... we can read something we were not expecting!!!
|
|
return 62;
|
|
}
|
|
|
|
/*
|
|
* Test 3
|
|
* Check that we can't read the registry
|
|
*/
|
|
HKEY hKey;
|
|
DWORD Ret;
|
|
wchar_t skey[256] = L"Environment";
|
|
|
|
Ret = RegOpenKeyEx(HKEY_CURRENT_USER, skey, 0, KEY_READ, &hKey);
|
|
if (Ret == ERROR_SUCCESS) {
|
|
return 63;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The main method (sandboxed). Here we do the heavy lifting in the sandbox. i.e.
|
|
* We hand the hard work off to the Ghostscript DLL :-)
|
|
*/
|
|
static int SandboxedMain(int argc, wchar_t* argv[]) {
|
|
// If -h, print out Ghost Trap information as well.
|
|
for (int i = 0; i < argc; ++i) {
|
|
// Used for developer testing only (not documented in usage)
|
|
if (wcscmp(argv[i], L"--test-sandbox") == 0) {
|
|
return TestSandbox();
|
|
}
|
|
}
|
|
|
|
wchar_t **full_path_argv = ExpandPathsInArgs(argc, argv);
|
|
|
|
/* Duplicate wide args as utf8 */
|
|
char **nargv;
|
|
int nargc = 0;
|
|
int i;
|
|
|
|
nargv = (char **) calloc(argc + 1, sizeof(char *));
|
|
if (nargv == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
// GhostTrap is a secured version, so it's only appropriate to ensure
|
|
// -dSAFER is always on by default. Always append (after arg 0).
|
|
if (i == 1) {
|
|
nargv[nargc] = _strdup("-dSAFER");
|
|
++nargc;
|
|
}
|
|
|
|
wchar_t *current_argv = full_path_argv[i];
|
|
|
|
nargv[nargc] = (char *) malloc(wchar_to_utf8(NULL, current_argv));
|
|
if (nargv[nargc] == NULL) {
|
|
goto error;
|
|
}
|
|
wchar_to_utf8(nargv[nargc], current_argv);
|
|
++nargc;
|
|
}
|
|
|
|
// Run GS code (via DLL)
|
|
int code;
|
|
int code1;
|
|
|
|
code = global_gsdll.new_instance(&global_gsinstance, NULL);
|
|
if (code < 0) {
|
|
return 22;
|
|
}
|
|
|
|
code = global_gsdll.init_with_args(global_gsinstance, nargc, nargv);
|
|
|
|
code1 = global_gsdll.exit(global_gsinstance);
|
|
|
|
if ((code == 0) || (code == gs_error_Quit)) {
|
|
code = code1;
|
|
}
|
|
|
|
if (0) {
|
|
error:
|
|
fprintf(stderr, "Ghost Trap: Failed to initialise due to malloc failure\n");
|
|
code = -1;
|
|
}
|
|
|
|
if (nargv) {
|
|
for (i = 0; i < argc; ++i) {
|
|
free(nargv[i]);
|
|
}
|
|
free(nargv);
|
|
}
|
|
|
|
if (!((code == 0) || (code == gs_error_Quit))) {
|
|
return abs(code);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* The main() function. We simply set up and pass execution across to sandbox_procmgmt.
|
|
*/
|
|
int wmain(int argc, wchar_t* argv[]) {
|
|
return RunConsoleAppInSandbox(ApplyPolicy, PreSandboxedInit, SandboxedMain, argc, argv);
|
|
} |