From fa1e5afa8a26d467aec7c8b36a0b749b690f636c Mon Sep 17 00:00:00 2001 From: Andrew Dunstan Date: Mon, 30 Mar 2015 17:07:52 -0400 Subject: [PATCH] Run pg_upgrade and pg_resetxlog with restricted token on Windows As with initdb these programs need to run with a restricted token, and if they don't pg_upgrade will fail when run as a user with Adminstrator privileges. Backpatch to all live branches. On the development branch the code is reorganized so that the restricted token code is now in a single location. On the stable bramches a less invasive change is made by simply copying the relevant code to pg_upgrade.c and pg_resetxlog.c. Patches and bug report from Muhammad Asif Naeem, reviewed by Michael Paquier, slightly edited by me. --- contrib/pg_upgrade/pg_upgrade.c | 3 + src/bin/initdb/initdb.c | 170 +---------------------- src/bin/pg_resetxlog/pg_resetxlog.c | 3 + src/common/Makefile | 2 +- src/common/restricted_token.c | 193 ++++++++++++++++++++++++++ src/include/common/restricted_token.h | 24 ++++ src/test/regress/pg_regress.c | 105 +------------- src/tools/msvc/Mkvcbuild.pm | 3 +- 8 files changed, 235 insertions(+), 268 deletions(-) create mode 100644 src/common/restricted_token.c create mode 100644 src/include/common/restricted_token.h diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c index ed7de809c8..eb48da779f 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 18614e7a67..b9d6092c2c 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -61,6 +61,7 @@ #endif #include "catalog/catalog.h" +#include "common/restricted_token.h" #include "common/username.h" #include "mb/pg_wchar.h" #include "getaddrinfo.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_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index a16089f3e8..3361111c38 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 c71415ef17..c2e456d89a 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/common/restricted_token.c b/src/common/restricted_token.c new file mode 100644 index 0000000000..a8213c0baf --- /dev/null +++ b/src/common/restricted_token.c @@ -0,0 +1,193 @@ +/*------------------------------------------------------------------------- + * + * 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" + +#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) + { + 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; + } + + 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) + { + 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(restrictedToken); + 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 +} diff --git a/src/include/common/restricted_token.h b/src/include/common/restricted_token.h new file mode 100644 index 0000000000..e24374483c --- /dev/null +++ b/src/include/common/restricted_token.h @@ -0,0 +1,24 @@ +/* + * 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); + +#ifdef WIN32 +/* Create a restricted token and execute the specified process with it. */ +HANDLE CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname); +#endif + +#endif /* COMMON_RESTRICTED_TOKEN_H */ diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 3de479023a..098acf6876 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -29,6 +29,7 @@ #include #endif +#include "common/restricted_token.h" #include "common/username.h" #include "getopt_long.h" #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */ @@ -135,15 +136,6 @@ static void header(const char *fmt,...) pg_attribute_printf(1, 2); static void status(const char *fmt,...) pg_attribute_printf(1, 2); static void psql_command(const char *database, const char *query,...) 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. */ @@ -1226,100 +1218,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 0ca786c1df..7f319dff67 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; -- 2.40.0