Run pg_upgrade and pg_resetxlog with restricted token on Windows
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 30 Mar 2015 21:07:52 +0000 (17:07 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 30 Mar 2015 21:07:52 +0000 (17:07 -0400)
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
src/bin/initdb/initdb.c
src/bin/pg_resetxlog/pg_resetxlog.c
src/common/Makefile
src/common/restricted_token.c [new file with mode: 0644]
src/include/common/restricted_token.h [new file with mode: 0644]
src/test/regress/pg_regress.c
src/tools/msvc/Mkvcbuild.pm

index ed7de809c8ee2c5abc3b17614ba39b7c01afcb8b..eb48da779f9ed244267a74130ad3b166304883d3 100644 (file)
@@ -37,6 +37,7 @@
 #include "postgres_fe.h"
 
 #include "pg_upgrade.h"
+#include "common/restricted_token.h"
 
 #ifdef HAVE_LANGINFO_H
 #include <langinfo.h>
@@ -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);
 
index 18614e7a678fc523ca143d98616f704b23d81329..b9d6092c2c018243e05be2b2948f70d40b054575 100644 (file)
@@ -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();
 
index a16089f3e8fecc4c02cc921a0cd5786205293ac3..3361111c38077c2cdc8ec389323c4946e31ed47f 100644 (file)
@@ -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"),
index c71415ef17d86fada36bcc0d51d56c18e758bdb7..c2e456d89a25d03e579df5315b541340ce9c9eeb 100644 (file)
@@ -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 (file)
index 0000000..a8213c0
--- /dev/null
@@ -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 (file)
index 0000000..e243744
--- /dev/null
@@ -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 */
index 3de479023ad139a65a2ff33063970715a4b69814..098acf68762869970e3d64326360e2cfc0fada00 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/resource.h>
 #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
index 0ca786c1df95553a680933e0794f62a03f272479..7f319dff67de08cbedb84491dc1cd1b7eff685a1 100644 (file)
@@ -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;