diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c index ed7de80..eb48da7 100644 --- a/contrib/pg_upgrade/pg_upgrade.c +++ b/contrib/pg_upgrade/pg_upgrade.c @@ -37,6 +37,7 @@ #include "postgres_fe.h" #include "pg_upgrade.h" +#include "common/restricted_token.h" #ifdef HAVE_LANGINFO_H #include @@ -75,6 +76,8 @@ main(int argc, char **argv) parseCommandLine(argc, argv); + get_restricted_token(os_info.progname); + adjust_data_dir(&old_cluster); adjust_data_dir(&new_cluster); diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 18614e7..b8abf19 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -62,6 +62,7 @@ #include "catalog/catalog.h" #include "common/username.h" +#include "common/restricted_token.h" #include "mb/pg_wchar.h" #include "getaddrinfo.h" #include "getopt_long.h" @@ -178,9 +179,6 @@ static char *authwarning = NULL; static const char *boot_options = "-F"; static const char *backend_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true"; -#ifdef WIN32 -char *restrict_env; -#endif static const char *subdirs[] = { "global", "pg_xlog", @@ -260,7 +258,6 @@ static void check_locale_name(int category, const char *locale, static bool check_locale_encoding(const char *locale, int encoding); static void setlocales(void); static void usage(const char *progname); -void get_restricted_token(void); void setup_pgdata(void); void setup_bin_paths(const char *argv0); void setup_data_file_paths(void); @@ -272,12 +269,6 @@ void create_xlog_symlink(void); void warn_on_mount_point(int error); void initialize_data_directory(void); - -#ifdef WIN32 -static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo); -#endif - - /* * macros for running pipes to postgres */ @@ -2754,116 +2745,6 @@ setlocales(void) #endif } -#ifdef WIN32 -typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); - -/* Windows API define missing from some versions of MingW headers */ -#ifndef DISABLE_MAX_PRIVILEGE -#define DISABLE_MAX_PRIVILEGE 0x1 -#endif - -/* - * Create a restricted token and execute the specified process with it. - * - * Returns 0 on failure, non-zero on success, same as CreateProcess(). - * - * On NT4, or any other system not containing the required functions, will - * NOT execute anything. - */ -static int -CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo) -{ - BOOL b; - STARTUPINFO si; - HANDLE origToken; - HANDLE restrictedToken; - SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; - SID_AND_ATTRIBUTES dropSids[2]; - __CreateRestrictedToken _CreateRestrictedToken = NULL; - HANDLE Advapi32Handle; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); - if (Advapi32Handle != NULL) - { - _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); - } - - if (_CreateRestrictedToken == NULL) - { - fprintf(stderr, _("%s: WARNING: cannot create restricted tokens on this platform\n"), progname); - if (Advapi32Handle != NULL) - FreeLibrary(Advapi32Handle); - return 0; - } - - /* Open the current token to use as a base for the restricted one */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) - { - fprintf(stderr, _("%s: could not open process token: error code %lu\n"), progname, GetLastError()); - return 0; - } - - /* Allocate list of SIDs to remove */ - ZeroMemory(&dropSids, sizeof(dropSids)); - if (!AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, - 0, &dropSids[0].Sid) || - !AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, - 0, &dropSids[1].Sid)) - { - fprintf(stderr, _("%s: could not allocate SIDs: error code %lu\n"), - progname, GetLastError()); - return 0; - } - - b = _CreateRestrictedToken(origToken, - DISABLE_MAX_PRIVILEGE, - sizeof(dropSids) / sizeof(dropSids[0]), - dropSids, - 0, NULL, - 0, NULL, - &restrictedToken); - - FreeSid(dropSids[1].Sid); - FreeSid(dropSids[0].Sid); - CloseHandle(origToken); - FreeLibrary(Advapi32Handle); - - if (!b) - { - fprintf(stderr, _("%s: could not create restricted token: error code %lu\n"), progname, GetLastError()); - return 0; - } - -#ifndef __CYGWIN__ - AddUserToTokenDacl(restrictedToken); -#endif - - if (!CreateProcessAsUser(restrictedToken, - NULL, - cmd, - NULL, - NULL, - TRUE, - CREATE_SUSPENDED, - NULL, - NULL, - &si, - processInfo)) - - { - fprintf(stderr, _("%s: could not start process for command \"%s\": error code %lu\n"), progname, cmd, GetLastError()); - return 0; - } - - return ResumeThread(processInfo->hThread); -} -#endif - /* * print help text */ @@ -2957,53 +2838,6 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost) } } -void -get_restricted_token(void) -{ -#ifdef WIN32 - - /* - * Before we execute another program, make sure that we are running with a - * restricted token. If not, re-execute ourselves with one. - */ - - if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL - || strcmp(restrict_env, "1") != 0) - { - PROCESS_INFORMATION pi; - char *cmdline; - - ZeroMemory(&pi, sizeof(pi)); - - cmdline = pg_strdup(GetCommandLine()); - - putenv("PG_RESTRICT_EXEC=1"); - - if (!CreateRestrictedProcess(cmdline, &pi)) - { - fprintf(stderr, _("%s: could not re-execute with restricted token: error code %lu\n"), progname, GetLastError()); - } - else - { - /* - * Successfully re-execed. Now wait for child process to capture - * exitcode. - */ - DWORD x; - - CloseHandle(pi.hThread); - WaitForSingleObject(pi.hProcess, INFINITE); - - if (!GetExitCodeProcess(pi.hProcess, &x)) - { - fprintf(stderr, _("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError()); - exit(1); - } - exit(x); - } - } -#endif -} void setup_pgdata(void) @@ -3759,7 +3593,7 @@ main(int argc, char *argv[]) check_need_password(authmethodlocal, authmethodhost); - get_restricted_token(); + get_restricted_token(progname); setup_pgdata(); diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 052caff..90daa1d 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -21,6 +21,7 @@ #include "libpq-fe.h" #include "pqexpbuffer.h" +#include "common/restricted_token.h" #include #include @@ -151,7 +152,8 @@ static void pgwin32_SetServiceStatus(DWORD); static void WINAPI pgwin32_ServiceHandler(DWORD); static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *); static void pgwin32_doRunAsService(void); -static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service); +static int AssignProcessToJob(PROCESS_INFORMATION *processInfo, + bool as_service, HANDLE restrictedToken); #endif static pgpid_t get_pgpid(bool is_status_request); @@ -454,6 +456,7 @@ start_postmaster(void) * it to handle redirection etc. */ PROCESS_INFORMATION pi; + HANDLE restrictedToken; if (log_file != NULL) snprintf(cmd, MAXPGPATH, "CMD /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"", @@ -462,8 +465,9 @@ start_postmaster(void) snprintf(cmd, MAXPGPATH, "CMD /C \"\"%s\" %s%s < \"%s\" 2>&1\"", exec_path, pgdata_opt, post_opts, DEVNULL); - if (!CreateRestrictedProcess(cmd, &pi, false)) + if ((restrictedToken = CreateRestrictedProcess(cmd, &pi, progname)) == 0) return GetLastError(); + CloseHandle(restrictedToken); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; @@ -1548,6 +1552,7 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR *argv) { PROCESS_INFORMATION pi; DWORD ret; + HANDLE restrictedToken; /* Initialize variables */ status.dwWin32ExitCode = S_OK; @@ -1571,14 +1576,18 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR *argv) /* Start the postmaster */ pgwin32_SetServiceStatus(SERVICE_START_PENDING); - if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, true)) + if ((restrictedToken = + CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, progname)) == 0) { pgwin32_SetServiceStatus(SERVICE_STOPPED); return; } + + AssignProcessToJob(&pi, true, restrictedToken); postmasterPID = pi.dwProcessId; postmasterProcess = pi.hProcess; CloseHandle(pi.hThread); + CloseHandle(restrictedToken); if (do_wait) { @@ -1647,125 +1656,27 @@ pgwin32_doRunAsService(void) * also load the couple of functions that *do* exist in minwg headers but not * on NT4. That way, we don't break on NT4. */ -typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); typedef BOOL (WINAPI * __IsProcessInJob) (HANDLE, HANDLE, PBOOL); typedef HANDLE (WINAPI * __CreateJobObject) (LPSECURITY_ATTRIBUTES, LPCTSTR); typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD); typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE); typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD); -/* Windows API define missing from some versions of MingW headers */ -#ifndef DISABLE_MAX_PRIVILEGE -#define DISABLE_MAX_PRIVILEGE 0x1 -#endif - /* - * Create a restricted token, a job object sandbox, and execute the specified - * process with it. - * - * Returns 0 on success, non-zero on failure, same as CreateProcess(). - * - * On NT4, or any other system not containing the required functions, will - * launch the process under the current token without doing any modifications. - * - * NOTE! Job object will only work when running as a service, because it's - * automatically destroyed when pg_ctl exits. + * Create a job object and associate the specified process with it. + * Returns 0 on failure, non-zero on success */ static int -CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service) +AssignProcessToJob(PROCESS_INFORMATION *processInfo, bool as_service, HANDLE restrictedToken) { - int r; - BOOL b; - STARTUPINFO si; - HANDLE origToken; - HANDLE restrictedToken; - SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; - SID_AND_ATTRIBUTES dropSids[2]; - /* Functions loaded dynamically */ - __CreateRestrictedToken _CreateRestrictedToken = NULL; __IsProcessInJob _IsProcessInJob = NULL; __CreateJobObject _CreateJobObject = NULL; __SetInformationJobObject _SetInformationJobObject = NULL; __AssignProcessToJobObject _AssignProcessToJobObject = NULL; __QueryInformationJobObject _QueryInformationJobObject = NULL; HANDLE Kernel32Handle; - HANDLE Advapi32Handle; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); - if (Advapi32Handle != NULL) - { - _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); - } - - if (_CreateRestrictedToken == NULL) - { - /* - * NT4 doesn't have CreateRestrictedToken, so just call ordinary - * CreateProcess - */ - write_stderr(_("%s: WARNING: cannot create restricted tokens on this platform\n"), progname); - if (Advapi32Handle != NULL) - FreeLibrary(Advapi32Handle); - return CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, processInfo); - } - - /* Open the current token to use as a base for the restricted one */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) - { - /* - * Most Windows targets make DWORD a 32-bit unsigned long. Cygwin - * x86_64, an LP64 target, makes it a 32-bit unsigned int. In code - * built for Cygwin as well as for native Windows targets, cast DWORD - * before printing. - */ - write_stderr(_("%s: could not open process token: error code %lu\n"), - progname, (unsigned long) GetLastError()); - return 0; - } - - /* Allocate list of SIDs to remove */ - ZeroMemory(&dropSids, sizeof(dropSids)); - if (!AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, - 0, &dropSids[0].Sid) || - !AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, - 0, &dropSids[1].Sid)) - { - write_stderr(_("%s: could not allocate SIDs: error code %lu\n"), - progname, (unsigned long) GetLastError()); - return 0; - } - - b = _CreateRestrictedToken(origToken, - DISABLE_MAX_PRIVILEGE, - sizeof(dropSids) / sizeof(dropSids[0]), - dropSids, - 0, NULL, - 0, NULL, - &restrictedToken); - - FreeSid(dropSids[1].Sid); - FreeSid(dropSids[0].Sid); - CloseHandle(origToken); - FreeLibrary(Advapi32Handle); - - if (!b) - { - write_stderr(_("%s: could not create restricted token: error code %lu\n"), - progname, (unsigned long) GetLastError()); - return 0; - } - -#ifndef __CYGWIN__ - AddUserToTokenDacl(restrictedToken); -#endif - - r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, processInfo); + BOOL inJob; Kernel32Handle = LoadLibrary("KERNEL32.DLL"); if (Kernel32Handle != NULL) @@ -1777,97 +1688,77 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser _QueryInformationJobObject = (__QueryInformationJobObject) GetProcAddress(Kernel32Handle, "QueryInformationJobObject"); } - /* Verify that we found all functions */ - if (_IsProcessInJob == NULL || _CreateJobObject == NULL || _SetInformationJobObject == NULL || _AssignProcessToJobObject == NULL || _QueryInformationJobObject == NULL) + if (_IsProcessInJob(processInfo->hProcess, NULL, &inJob)) { - /* - * IsProcessInJob() is not available on < WinXP, so there is no need - * to log the error every time in that case - */ - if (IsWindowsXPOrGreater()) - + if (!inJob) + { /* - * Log error if we can't get version, or if we're on WinXP/2003 or - * newer + * Job objects are working, and the new process isn't in one, + * so we can create one safely. If any problems show up when + * setting it, we're going to ignore them. */ - write_stderr(_("%s: WARNING: could not locate all job object functions in system API\n"), progname); - } - else - { - BOOL inJob; + HANDLE job; + char jobname[128]; - if (_IsProcessInJob(processInfo->hProcess, NULL, &inJob)) - { - if (!inJob) - { - /* - * Job objects are working, and the new process isn't in one, - * so we can create one safely. If any problems show up when - * setting it, we're going to ignore them. - */ - HANDLE job; - char jobname[128]; - - sprintf(jobname, "PostgreSQL_%lu", - (unsigned long) processInfo->dwProcessId); + sprintf(jobname, "PostgreSQL_%lu", + (unsigned long) processInfo->dwProcessId); - job = _CreateJobObject(NULL, jobname); - if (job) - { - JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit; - JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions; - JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit; + job = _CreateJobObject(NULL, jobname); + if (job) + { + JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit; + JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions; + JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit; - ZeroMemory(&basicLimit, sizeof(basicLimit)); - ZeroMemory(&uiRestrictions, sizeof(uiRestrictions)); - ZeroMemory(&securityLimit, sizeof(securityLimit)); + ZeroMemory(&basicLimit, sizeof(basicLimit)); + ZeroMemory(&uiRestrictions, sizeof(uiRestrictions)); + ZeroMemory(&securityLimit, sizeof(securityLimit)); - basicLimit.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_PRIORITY_CLASS; - basicLimit.PriorityClass = NORMAL_PRIORITY_CLASS; - _SetInformationJobObject(job, JobObjectBasicLimitInformation, &basicLimit, sizeof(basicLimit)); + basicLimit.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_PRIORITY_CLASS; + basicLimit.PriorityClass = NORMAL_PRIORITY_CLASS; + _SetInformationJobObject(job, JobObjectBasicLimitInformation, &basicLimit, sizeof(basicLimit)); - uiRestrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | - JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_READCLIPBOARD | - JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD; + uiRestrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | + JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_READCLIPBOARD | + JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD; - if (as_service) + if (as_service) + { + if (!IsWindows7OrGreater()) { - if (!IsWindows7OrGreater()) - { - /* - * On Windows 7 (and presumably later), - * JOB_OBJECT_UILIMIT_HANDLES prevents us from - * starting as a service. So we only enable it on - * Vista and earlier (version <= 6.0) - */ - uiRestrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; - } + /* + * On Windows 7 (and presumably later), + * JOB_OBJECT_UILIMIT_HANDLES prevents us from + * starting as a service. So we only enable it on + * Vista and earlier (version <= 6.0) + */ + uiRestrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; } - _SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions)); + } + _SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions)); - securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN; - securityLimit.JobToken = restrictedToken; - _SetInformationJobObject(job, JobObjectSecurityLimitInformation, &securityLimit, sizeof(securityLimit)); + securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN; + securityLimit.JobToken = restrictedToken; + _SetInformationJobObject(job, JobObjectSecurityLimitInformation, &securityLimit, sizeof(securityLimit)); - _AssignProcessToJobObject(job, processInfo->hProcess); + if(!_AssignProcessToJobObject(job, processInfo->hProcess)) + { + write_stderr(_("%s: could not associate the process with a job object: error code %lu\n"), + progname, GetLastError()); + return 0; } } + else + { + write_stderr(_("%s: could not create job object: error code %lu\n"), + progname, GetLastError()); + return 0; + } } } - - - CloseHandle(restrictedToken); - - ResumeThread(processInfo->hThread); - - FreeLibrary(Kernel32Handle); - - /* - * We intentionally don't close the job object handle, because we want the - * object to live on until pg_ctl shuts down. - */ - return r; + return 1; } + #endif /* defined(WIN32) || defined(__CYGWIN__) */ static void diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index a16089f..3361111 100644 --- a/src/bin/pg_resetxlog/pg_resetxlog.c +++ b/src/bin/pg_resetxlog/pg_resetxlog.c @@ -53,6 +53,7 @@ #include "catalog/catversion.h" #include "catalog/pg_control.h" #include "common/fe_memutils.h" +#include "common/restricted_token.h" #include "storage/large_object.h" #include "pg_getopt.h" @@ -310,6 +311,8 @@ main(int argc, char *argv[]) } #endif + get_restricted_token(progname); + if (chdir(DataDir) < 0) { fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"), diff --git a/src/common/Makefile b/src/common/Makefile index c71415e..c2e456d 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -26,7 +26,7 @@ LIBS += $(PTHREAD_LIBS) OBJS_COMMON = exec.o pg_crc.o pg_lzcompress.o pgfnames.o psprintf.o relpath.o \ rmtree.o string.o username.o wait_error.o -OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o +OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o) diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 9d4fb9a..dc910b0 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -30,6 +30,7 @@ #endif #include "common/username.h" +#include "common/restricted_token.h" #include "getopt_long.h" #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */ #include "pg_config_paths.h" @@ -147,15 +148,6 @@ psql_command(const char *database, const char *query,...) the supplied arguments. */ pg_attribute_printf(2, 3); -#ifdef WIN32 -typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); - -/* Windows API define missing from some versions of MingW headers */ -#ifndef DISABLE_MAX_PRIVILEGE -#define DISABLE_MAX_PRIVILEGE 0x1 -#endif -#endif - /* * allow core files if possible. */ @@ -1238,100 +1230,17 @@ spawn_process(const char *cmdline) /* in parent */ return pid; #else - char *cmdline2; - BOOL b; - STARTUPINFO si; - PROCESS_INFORMATION pi; - HANDLE origToken; - HANDLE restrictedToken; - SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; - SID_AND_ATTRIBUTES dropSids[2]; - __CreateRestrictedToken _CreateRestrictedToken = NULL; - HANDLE Advapi32Handle; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); - if (Advapi32Handle != NULL) - { - _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); - } - - if (_CreateRestrictedToken == NULL) - { - if (Advapi32Handle != NULL) - FreeLibrary(Advapi32Handle); - fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"), - progname); - exit(2); - } - - /* Open the current token to use as base for the restricted one */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) - { - fprintf(stderr, _("could not open process token: error code %lu\n"), - GetLastError()); - exit(2); - } - - /* Allocate list of SIDs to remove */ - ZeroMemory(&dropSids, sizeof(dropSids)); - if (!AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) || - !AllocateAndInitializeSid(&NtAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid)) - { - fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError()); - exit(2); - } - - b = _CreateRestrictedToken(origToken, - DISABLE_MAX_PRIVILEGE, - sizeof(dropSids) / sizeof(dropSids[0]), - dropSids, - 0, NULL, - 0, NULL, - &restrictedToken); - - FreeSid(dropSids[1].Sid); - FreeSid(dropSids[0].Sid); - CloseHandle(origToken); - FreeLibrary(Advapi32Handle); - - if (!b) - { - fprintf(stderr, _("could not create restricted token: error code %lu\n"), - GetLastError()); - exit(2); - } + PROCESS_INFORMATION pi; + char *cmdline2; + HANDLE restrictedToken; + memset(&pi, 0, sizeof(pi)); cmdline2 = psprintf("cmd /c \"%s\"", cmdline); -#ifndef __CYGWIN__ - AddUserToTokenDacl(restrictedToken); -#endif - - if (!CreateProcessAsUser(restrictedToken, - NULL, - cmdline2, - NULL, - NULL, - TRUE, - CREATE_SUSPENDED, - NULL, - NULL, - &si, - &pi)) - { - fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"), - cmdline2, GetLastError()); + if((restrictedToken = + CreateRestrictedProcess(cmdline2, &pi, progname)) == 0) exit(2); - } - - free(cmdline2); - ResumeThread(pi.hThread); CloseHandle(pi.hThread); return pi.hProcess; #endif diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 989a2ec..9c52a46 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -95,7 +95,8 @@ sub mkvcbuild exec.c pg_crc.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c string.c username.c wait_error.c); - our @pgcommonfrontendfiles = (@pgcommonallfiles, qw(fe_memutils.c)); + our @pgcommonfrontendfiles = (@pgcommonallfiles, qw(fe_memutils.c + restricted_token.c)); our @pgcommonbkndfiles = @pgcommonallfiles; diff --git a/src/common/restricted_token.c b/src/common/restricted_token.c new file mode 100644 index 0000000..6425517 --- /dev/null +++ b/src/common/restricted_token.c @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------- + * + * restricted_token.c + * helper routine to ensure restricted token on Windows + * + * + * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/restricted_token.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#error "This file is not expected to be compiled for backend code" +#endif + +#include "postgres_fe.h" + +#include "common/restricted_token.h" + +#define write_stderr(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + +#ifdef WIN32 + +/* internal vars */ +char *restrict_env; + +typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); + +/* Windows API define missing from some versions of MingW headers */ +#ifndef DISABLE_MAX_PRIVILEGE +#define DISABLE_MAX_PRIVILEGE 0x1 +#endif + +/* + * Create a restricted token and execute the specified process with it. + * + * Returns restricted token on success and 0 on failure. + * + * On NT4, or any other system not containing the required functions, will + * NOT execute anything. + */ +HANDLE +CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname) +{ + BOOL b; + STARTUPINFO si; + HANDLE origToken; + HANDLE restrictedToken; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + SID_AND_ATTRIBUTES dropSids[2]; + __CreateRestrictedToken _CreateRestrictedToken = NULL; + HANDLE Advapi32Handle; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); + if (Advapi32Handle != NULL) + { + _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); + } + + if (_CreateRestrictedToken == NULL) + { + write_stderr(_("%s: WARNING: cannot create restricted tokens on this platform\n"), progname); + if (Advapi32Handle != NULL) + FreeLibrary(Advapi32Handle); + return 0; + } + + /* Open the current token to use as a base for the restricted one */ + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) + { + write_stderr(_("%s: could not open process token: error code %lu\n"), progname, GetLastError()); + return 0; + } + + /* Allocate list of SIDs to remove */ + ZeroMemory(&dropSids, sizeof(dropSids)); + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, + 0, &dropSids[0].Sid) || + !AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, + 0, &dropSids[1].Sid)) + { + write_stderr(_("%s: could not allocate SIDs: error code %lu\n"), + progname, GetLastError()); + return 0; + } + + b = _CreateRestrictedToken(origToken, + DISABLE_MAX_PRIVILEGE, + sizeof(dropSids) / sizeof(dropSids[0]), + dropSids, + 0, NULL, + 0, NULL, + &restrictedToken); + + FreeSid(dropSids[1].Sid); + FreeSid(dropSids[0].Sid); + CloseHandle(origToken); + FreeLibrary(Advapi32Handle); + + if (!b) + { + write_stderr(_("%s: could not create restricted token: error code %lu\n"), + progname, GetLastError()); + return 0; + } + +#ifndef __CYGWIN__ + AddUserToTokenDacl(restrictedToken); +#endif + + if (!CreateProcessAsUser(restrictedToken, + NULL, + cmd, + NULL, + NULL, + TRUE, + CREATE_SUSPENDED, + NULL, + NULL, + &si, + processInfo)) + + { + write_stderr(_("%s: could not start process for command \"%s\": error code %lu\n"), progname, cmd, GetLastError()); + return 0; + } + + ResumeThread(processInfo->hThread); + return restrictedToken; +} +#endif + +/* + * On Windows make sure that we are running with a restricted token, + * On other platforms do nothing. + */ + +void +get_restricted_token(const char *progname) +{ +#ifdef WIN32 + HANDLE restrictedToken; + + /* + * Before we execute another program, make sure that we are running with a + * restricted token. If not, re-execute ourselves with one. + */ + + if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL + || strcmp(restrict_env, "1") != 0) + { + PROCESS_INFORMATION pi; + char *cmdline; + + ZeroMemory(&pi, sizeof(pi)); + + cmdline = pg_strdup(GetCommandLine()); + + putenv("PG_RESTRICT_EXEC=1"); + + if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi, progname)) == 0) + { + write_stderr(_("%s: could not re-execute with restricted token: error code %lu\n"), progname, GetLastError()); + } + else + { + /* + * Successfully re-execed. Now wait for child process to capture + * exitcode. + */ + DWORD x; + + CloseHandle(restrictedToken); + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + if (!GetExitCodeProcess(pi.hProcess, &x)) + { + write_stderr(_("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError()); + exit(1); + } + exit(x); + } + } +#endif +} diff --git a/src/include/common/restricted_token.h b/src/include/common/restricted_token.h new file mode 100644 index 0000000..5cdf38e --- /dev/null +++ b/src/include/common/restricted_token.h @@ -0,0 +1,23 @@ +/* + * restricted_token.h + * helper routine to ensure restricted token on Windows + * + * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/common/restricted_token.h + */ +#ifndef COMMON_RESTRICTED_TOKEN_H +#define COMMON_RESTRICTED_TOKEN_H + +/* + * On Windows make sure that we are running with a restricted token, + * On other platforms do nothing. + */ +void get_restricted_token(const char *progname); + + +/* Create a restricted token and execute the specified process with it. */ +HANDLE CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname); + +#endif /* COMMON_RESTRICTED_TOKEN_H */