Skip to content
Snippets Groups Projects
Commit 94856631 authored by Andrew Dunstan's avatar Andrew Dunstan
Browse files

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.
parent f155466f
No related branches found
No related tags found
No related merge requests found
...@@ -50,6 +50,11 @@ static void copy_clog_xlog_xid(void); ...@@ -50,6 +50,11 @@ static void copy_clog_xlog_xid(void);
static void set_frozenxids(void); static void set_frozenxids(void);
static void setup(char *argv0, bool live_check); static void setup(char *argv0, bool live_check);
static void cleanup(void); static void cleanup(void);
static void get_restricted_token(const char *progname);
#ifdef WIN32
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname);
#endif
ClusterInfo old_cluster, ClusterInfo old_cluster,
new_cluster; new_cluster;
...@@ -67,6 +72,9 @@ char *output_files[] = { ...@@ -67,6 +72,9 @@ char *output_files[] = {
NULL NULL
}; };
#ifdef WIN32
static char *restrict_env;
#endif
int int
main(int argc, char **argv) main(int argc, char **argv)
...@@ -78,6 +86,8 @@ main(int argc, char **argv) ...@@ -78,6 +86,8 @@ main(int argc, char **argv)
parseCommandLine(argc, argv); parseCommandLine(argc, argv);
get_restricted_token(os_info.progname);
adjust_data_dir(&old_cluster); adjust_data_dir(&old_cluster);
adjust_data_dir(&new_cluster); adjust_data_dir(&new_cluster);
...@@ -170,6 +180,162 @@ main(int argc, char **argv) ...@@ -170,6 +180,162 @@ main(int argc, char **argv)
return 0; return 0;
} }
#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, 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 to 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
void
get_restricted_token(const char *progname)
{
#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, progname))
{
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
}
static void static void
setup(char *argv0, bool live_check) setup(char *argv0, bool live_check)
......
...@@ -65,6 +65,10 @@ static uint32 newXlogId, ...@@ -65,6 +65,10 @@ static uint32 newXlogId,
static bool guessed = false; /* T if we had to guess at any values */ static bool guessed = false; /* T if we had to guess at any values */
static const char *progname; static const char *progname;
#ifdef WIN32
static char *restrict_env;
#endif
static bool ReadControlFile(void); static bool ReadControlFile(void);
static void GuessControlValues(void); static void GuessControlValues(void);
static void PrintControlValues(bool guessed); static void PrintControlValues(bool guessed);
...@@ -74,7 +78,12 @@ static void KillExistingXLOG(void); ...@@ -74,7 +78,12 @@ static void KillExistingXLOG(void);
static void KillExistingArchiveStatus(void); static void KillExistingArchiveStatus(void);
static void WriteEmptyXLOG(void); static void WriteEmptyXLOG(void);
static void usage(void); static void usage(void);
static void get_restricted_token(const char *progname);
#ifdef WIN32
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname);
static char *pg_strdup(const char *str);
#endif
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
...@@ -256,6 +265,7 @@ main(int argc, char *argv[]) ...@@ -256,6 +265,7 @@ main(int argc, char *argv[])
} }
#endif #endif
get_restricted_token(progname);
DataDir = argv[optind]; DataDir = argv[optind];
if (chdir(DataDir) < 0) if (chdir(DataDir) < 0)
...@@ -380,6 +390,20 @@ main(int argc, char *argv[]) ...@@ -380,6 +390,20 @@ main(int argc, char *argv[])
return 0; return 0;
} }
#ifdef WIN32
static char *
pg_strdup(const char *str)
{
char *result = strdup(str);
if (!result)
{
fprintf(stderr, _("out of memory\n"));
exit(1);
}
return result;
}
#endif
/* /*
* Try to read the existing pg_control file. * Try to read the existing pg_control file.
...@@ -1022,6 +1046,162 @@ WriteEmptyXLOG(void) ...@@ -1022,6 +1046,162 @@ WriteEmptyXLOG(void)
close(fd); close(fd);
} }
#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, 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 to 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
void
get_restricted_token(const char *progname)
{
#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, progname))
{
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
}
static void static void
usage(void) usage(void)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment