Skip to content

Commit 22b3f5b

Browse files
committed
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.
1 parent 46bfe44 commit 22b3f5b

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

contrib/pg_upgrade/pg_upgrade.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ static void copy_clog_xlog_xid(void);
4949
static void set_frozenxids(void);
5050
static void setup(char *argv0, bool live_check);
5151
static void cleanup(void);
52+
static void get_restricted_token(const char *progname);
53+
54+
#ifdef WIN32
55+
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname);
56+
#endif
5257

5358
/* This is the database used by pg_dumpall to restore global tables */
5459
#define GLOBAL_DUMP_DB "postgres"
@@ -57,6 +62,10 @@ ClusterInfo old_cluster,
5762
new_cluster;
5863
OSInfo os_info;
5964

65+
#ifdef WIN32
66+
static char *restrict_env;
67+
#endif
68+
6069
int
6170
main(int argc, char **argv)
6271
{
@@ -66,6 +75,8 @@ main(int argc, char **argv)
6675

6776
parseCommandLine(argc, argv);
6877

78+
get_restricted_token(os_info.progname);
79+
6980
output_check_banner(&live_check);
7081

7182
setup(argv[0], live_check);
@@ -139,6 +150,162 @@ main(int argc, char **argv)
139150
return 0;
140151
}
141152

153+
#ifdef WIN32
154+
typedef BOOL(WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
155+
156+
/* Windows API define missing from some versions of MingW headers */
157+
#ifndef DISABLE_MAX_PRIVILEGE
158+
#define DISABLE_MAX_PRIVILEGE 0x1
159+
#endif
160+
161+
/*
162+
* Create a restricted token and execute the specified process with it.
163+
*
164+
* Returns 0 on failure, non-zero on success, same as CreateProcess().
165+
*
166+
* On NT4, or any other system not containing the required functions, will
167+
* NOT execute anything.
168+
*/
169+
static int
170+
CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname)
171+
{
172+
BOOL b;
173+
STARTUPINFO si;
174+
HANDLE origToken;
175+
HANDLE restrictedToken;
176+
SID_IDENTIFIER_AUTHORITY NtAuthority = { SECURITY_NT_AUTHORITY };
177+
SID_AND_ATTRIBUTES dropSids[2];
178+
__CreateRestrictedToken _CreateRestrictedToken = NULL;
179+
HANDLE Advapi32Handle;
180+
181+
ZeroMemory(&si, sizeof(si));
182+
si.cb = sizeof(si);
183+
184+
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
185+
if (Advapi32Handle != NULL)
186+
{
187+
_CreateRestrictedToken = (__CreateRestrictedToken)GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
188+
}
189+
190+
if (_CreateRestrictedToken == NULL)
191+
{
192+
fprintf(stderr, _("%s: WARNING: cannot create restricted tokens on this platform\n"), progname);
193+
if (Advapi32Handle != NULL)
194+
FreeLibrary(Advapi32Handle);
195+
return 0;
196+
}
197+
198+
/* Open the current token to use as a base for the restricted one */
199+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
200+
{
201+
fprintf(stderr, _("%s: could not open process token: error code %lu\n"), progname, GetLastError());
202+
return 0;
203+
}
204+
205+
/* Allocate list of SIDs to remove */
206+
ZeroMemory(&dropSids, sizeof(dropSids));
207+
if (!AllocateAndInitializeSid(&NtAuthority, 2,
208+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
209+
0, &dropSids[0].Sid) ||
210+
!AllocateAndInitializeSid(&NtAuthority, 2,
211+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
212+
0, &dropSids[1].Sid))
213+
{
214+
fprintf(stderr, _("%s: could not to allocate SIDs: error code %lu\n"), progname, GetLastError());
215+
return 0;
216+
}
217+
218+
b = _CreateRestrictedToken(origToken,
219+
DISABLE_MAX_PRIVILEGE,
220+
sizeof(dropSids) / sizeof(dropSids[0]),
221+
dropSids,
222+
0, NULL,
223+
0, NULL,
224+
&restrictedToken);
225+
226+
FreeSid(dropSids[1].Sid);
227+
FreeSid(dropSids[0].Sid);
228+
CloseHandle(origToken);
229+
FreeLibrary(Advapi32Handle);
230+
231+
if (!b)
232+
{
233+
fprintf(stderr, _("%s: could not create restricted token: error code %lu\n"), progname, GetLastError());
234+
return 0;
235+
}
236+
237+
#ifndef __CYGWIN__
238+
AddUserToTokenDacl(restrictedToken);
239+
#endif
240+
241+
if (!CreateProcessAsUser(restrictedToken,
242+
NULL,
243+
cmd,
244+
NULL,
245+
NULL,
246+
TRUE,
247+
CREATE_SUSPENDED,
248+
NULL,
249+
NULL,
250+
&si,
251+
processInfo))
252+
253+
{
254+
fprintf(stderr, _("%s: could not start process for command \"%s\": error code %lu\n"), progname, cmd, GetLastError());
255+
return 0;
256+
}
257+
258+
return ResumeThread(processInfo->hThread);
259+
}
260+
#endif
261+
262+
void
263+
get_restricted_token(const char *progname)
264+
{
265+
#ifdef WIN32
266+
267+
/*
268+
* Before we execute another program, make sure that we are running with a
269+
* restricted token. If not, re-execute ourselves with one.
270+
*/
271+
272+
if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL
273+
|| strcmp(restrict_env, "1") != 0)
274+
{
275+
PROCESS_INFORMATION pi;
276+
char *cmdline;
277+
278+
ZeroMemory(&pi, sizeof(pi));
279+
280+
cmdline = pg_strdup(GetCommandLine());
281+
282+
putenv("PG_RESTRICT_EXEC=1");
283+
284+
if (!CreateRestrictedProcess(cmdline, &pi, progname))
285+
{
286+
fprintf(stderr, _("%s: could not re-execute with restricted token: error code %lu\n"), progname, GetLastError());
287+
}
288+
else
289+
{
290+
/*
291+
* Successfully re-execed. Now wait for child process to capture
292+
* exitcode.
293+
*/
294+
DWORD x;
295+
296+
CloseHandle(pi.hThread);
297+
WaitForSingleObject(pi.hProcess, INFINITE);
298+
299+
if (!GetExitCodeProcess(pi.hProcess, &x))
300+
{
301+
fprintf(stderr, _("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError());
302+
exit(1);
303+
}
304+
exit(x);
305+
}
306+
}
307+
#endif
308+
}
142309

143310
static void
144311
setup(char *argv0, bool live_check)

0 commit comments

Comments
 (0)