Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2366761

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 a6a8bf5 commit 2366761

File tree

2 files changed

+332
-0
lines changed

2 files changed

+332
-0
lines changed

contrib/pg_upgrade/pg_upgrade.c

Lines changed: 166 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(bool minmxid_only);
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
ClusterInfo old_cluster,
5459
new_cluster;
@@ -65,6 +70,9 @@ char *output_files[] = {
6570
NULL
6671
};
6772

73+
#ifdef WIN32
74+
static char *restrict_env;
75+
#endif
6876

6977
int
7078
main(int argc, char **argv)
@@ -76,6 +84,8 @@ main(int argc, char **argv)
7684

7785
parseCommandLine(argc, argv);
7886

87+
get_restricted_token(os_info.progname);
88+
7989
adjust_data_dir(&old_cluster);
8090
adjust_data_dir(&new_cluster);
8191

@@ -174,6 +184,162 @@ main(int argc, char **argv)
174184
return 0;
175185
}
176186

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

178344
static void
179345
setup(char *argv0, bool *live_check)

src/bin/pg_resetxlog/pg_resetxlog.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
6868
static uint32 minXlogTli = 0;
6969
static XLogSegNo minXlogSegNo = 0;
7070

71+
#ifdef WIN32
72+
static char *restrict_env;
73+
#endif
74+
7175
static bool ReadControlFile(void);
7276
static void GuessControlValues(void);
7377
static void PrintControlValues(bool guessed);
@@ -78,6 +82,11 @@ static void KillExistingXLOG(void);
7882
static void KillExistingArchiveStatus(void);
7983
static void WriteEmptyXLOG(void);
8084
static void usage(void);
85+
static void get_restricted_token(const char *progname);
86+
87+
#ifdef WIN32
88+
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname);
89+
#endif
8190

8291

8392
int
@@ -257,6 +266,7 @@ main(int argc, char *argv[])
257266
}
258267
#endif
259268

269+
get_restricted_token(progname);
260270
DataDir = argv[optind];
261271

262272
if (chdir(DataDir) < 0)
@@ -1072,6 +1082,162 @@ WriteEmptyXLOG(void)
10721082
close(fd);
10731083
}
10741084

1085+
#ifdef WIN32
1086+
typedef BOOL(WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
1087+
1088+
/* Windows API define missing from some versions of MingW headers */
1089+
#ifndef DISABLE_MAX_PRIVILEGE
1090+
#define DISABLE_MAX_PRIVILEGE 0x1
1091+
#endif
1092+
1093+
/*
1094+
* Create a restricted token and execute the specified process with it.
1095+
*
1096+
* Returns 0 on failure, non-zero on success, same as CreateProcess().
1097+
*
1098+
* On NT4, or any other system not containing the required functions, will
1099+
* NOT execute anything.
1100+
*/
1101+
static int
1102+
CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, const char *progname)
1103+
{
1104+
BOOL b;
1105+
STARTUPINFO si;
1106+
HANDLE origToken;
1107+
HANDLE restrictedToken;
1108+
SID_IDENTIFIER_AUTHORITY NtAuthority = { SECURITY_NT_AUTHORITY };
1109+
SID_AND_ATTRIBUTES dropSids[2];
1110+
__CreateRestrictedToken _CreateRestrictedToken = NULL;
1111+
HANDLE Advapi32Handle;
1112+
1113+
ZeroMemory(&si, sizeof(si));
1114+
si.cb = sizeof(si);
1115+
1116+
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
1117+
if (Advapi32Handle != NULL)
1118+
{
1119+
_CreateRestrictedToken = (__CreateRestrictedToken)GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
1120+
}
1121+
1122+
if (_CreateRestrictedToken == NULL)
1123+
{
1124+
fprintf(stderr, _("%s: WARNING: cannot create restricted tokens on this platform\n"), progname);
1125+
if (Advapi32Handle != NULL)
1126+
FreeLibrary(Advapi32Handle);
1127+
return 0;
1128+
}
1129+
1130+
/* Open the current token to use as a base for the restricted one */
1131+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
1132+
{
1133+
fprintf(stderr, _("%s: could not open process token: error code %lu\n"), progname, GetLastError());
1134+
return 0;
1135+
}
1136+
1137+
/* Allocate list of SIDs to remove */
1138+
ZeroMemory(&dropSids, sizeof(dropSids));
1139+
if (!AllocateAndInitializeSid(&NtAuthority, 2,
1140+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
1141+
0, &dropSids[0].Sid) ||
1142+
!AllocateAndInitializeSid(&NtAuthority, 2,
1143+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
1144+
0, &dropSids[1].Sid))
1145+
{
1146+
fprintf(stderr, _("%s: could not to allocate SIDs: error code %lu\n"), progname, GetLastError());
1147+
return 0;
1148+
}
1149+
1150+
b = _CreateRestrictedToken(origToken,
1151+
DISABLE_MAX_PRIVILEGE,
1152+
sizeof(dropSids) / sizeof(dropSids[0]),
1153+
dropSids,
1154+
0, NULL,
1155+
0, NULL,
1156+
&restrictedToken);
1157+
1158+
FreeSid(dropSids[1].Sid);
1159+
FreeSid(dropSids[0].Sid);
1160+
CloseHandle(origToken);
1161+
FreeLibrary(Advapi32Handle);
1162+
1163+
if (!b)
1164+
{
1165+
fprintf(stderr, _("%s: could not create restricted token: error code %lu\n"), progname, GetLastError());
1166+
return 0;
1167+
}
1168+
1169+
#ifndef __CYGWIN__
1170+
AddUserToTokenDacl(restrictedToken);
1171+
#endif
1172+
1173+
if (!CreateProcessAsUser(restrictedToken,
1174+
NULL,
1175+
cmd,
1176+
NULL,
1177+
NULL,
1178+
TRUE,
1179+
CREATE_SUSPENDED,
1180+
NULL,
1181+
NULL,
1182+
&si,
1183+
processInfo))
1184+
1185+
{
1186+
fprintf(stderr, _("%s: could not start process for command \"%s\": error code %lu\n"), progname, cmd, GetLastError());
1187+
return 0;
1188+
}
1189+
1190+
return ResumeThread(processInfo->hThread);
1191+
}
1192+
#endif
1193+
1194+
void
1195+
get_restricted_token(const char *progname)
1196+
{
1197+
#ifdef WIN32
1198+
1199+
/*
1200+
* Before we execute another program, make sure that we are running with a
1201+
* restricted token. If not, re-execute ourselves with one.
1202+
*/
1203+
1204+
if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL
1205+
|| strcmp(restrict_env, "1") != 0)
1206+
{
1207+
PROCESS_INFORMATION pi;
1208+
char *cmdline;
1209+
1210+
ZeroMemory(&pi, sizeof(pi));
1211+
1212+
cmdline = pg_strdup(GetCommandLine());
1213+
1214+
putenv("PG_RESTRICT_EXEC=1");
1215+
1216+
if (!CreateRestrictedProcess(cmdline, &pi, progname))
1217+
{
1218+
fprintf(stderr, _("%s: could not re-execute with restricted token: error code %lu\n"), progname, GetLastError());
1219+
}
1220+
else
1221+
{
1222+
/*
1223+
* Successfully re-execed. Now wait for child process to capture
1224+
* exitcode.
1225+
*/
1226+
DWORD x;
1227+
1228+
CloseHandle(pi.hThread);
1229+
WaitForSingleObject(pi.hProcess, INFINITE);
1230+
1231+
if (!GetExitCodeProcess(pi.hProcess, &x))
1232+
{
1233+
fprintf(stderr, _("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError());
1234+
exit(1);
1235+
}
1236+
exit(x);
1237+
}
1238+
}
1239+
#endif
1240+
}
10751241

10761242
static void
10771243
usage(void)

0 commit comments

Comments
 (0)