Skip to content

Commit fc9c20e

Browse files
committed
Make it possible to run initdb from an admin account on Windows,
by giving up admin privileges (only works if newer than NT4). Magnus
1 parent a25cd81 commit fc9c20e

File tree

1 file changed

+138
-1
lines changed

1 file changed

+138
-1
lines changed

src/bin/initdb/initdb.c

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Portions Copyright (c) 1994, Regents of the University of California
4343
* Portions taken from FreeBSD.
4444
*
45-
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.107 2006/01/27 19:01:15 tgl Exp $
45+
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.108 2006/02/10 22:05:42 tgl Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -95,6 +95,9 @@ static char *authmethod = "";
9595
static bool debug = false;
9696
static bool noclean = false;
9797
static bool show_setting = false;
98+
#ifdef WIN32
99+
static bool restricted_exec = false;
100+
#endif
98101

99102

100103
/* internal vars */
@@ -192,6 +195,9 @@ static int locale_date_order(const char *locale);
192195
static bool chklocale(const char *locale);
193196
static void setlocales(void);
194197
static void usage(const char *progname);
198+
#ifdef WIN32
199+
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo);
200+
#endif
195201

196202

197203
/*
@@ -2239,6 +2245,91 @@ setlocales(void)
22392245

22402246
}
22412247

2248+
#ifdef WIN32
2249+
/* MingW headers are incomplete */
2250+
typedef WINAPI BOOL (*__CreateRestrictedToken)(HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
2251+
#define DISABLE_MAX_PRIVILEGE 0x1
2252+
2253+
/*
2254+
* Create a restricted token and execute the specified process with it.
2255+
*
2256+
* Returns 0 on failure, non-zero on success, same as CreateProcess().
2257+
*
2258+
* On NT4, or any other system not containing the required functions, will
2259+
* NOT execute anything.
2260+
*/
2261+
static int
2262+
CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo)
2263+
{
2264+
BOOL b;
2265+
STARTUPINFO si;
2266+
HANDLE origToken;
2267+
HANDLE restrictedToken;
2268+
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
2269+
SID_AND_ATTRIBUTES dropSids[2];
2270+
__CreateRestrictedToken _CreateRestrictedToken = NULL;
2271+
HANDLE Advapi32Handle;
2272+
2273+
ZeroMemory(&si, sizeof(si));
2274+
si.cb = sizeof(si);
2275+
2276+
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
2277+
if (Advapi32Handle != NULL)
2278+
{
2279+
_CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
2280+
}
2281+
2282+
if (_CreateRestrictedToken == NULL)
2283+
{
2284+
fprintf(stderr,"WARNING: Unable to create restricted tokens on this platform\n");
2285+
if (Advapi32Handle != NULL)
2286+
FreeLibrary(Advapi32Handle);
2287+
return 0;
2288+
}
2289+
2290+
/* Open the current token to use as a base for the restricted one */
2291+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
2292+
{
2293+
fprintf(stderr, "Failed to open process token: %lu\n", GetLastError());
2294+
return 0;
2295+
}
2296+
2297+
/* Allocate list of SIDs to remove */
2298+
ZeroMemory(&dropSids, sizeof(dropSids));
2299+
if (!AllocateAndInitializeSid(&NtAuthority, 2,
2300+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,
2301+
0, &dropSids[0].Sid) ||
2302+
!AllocateAndInitializeSid(&NtAuthority, 2,
2303+
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0,0,0,0,0,
2304+
0, &dropSids[1].Sid))
2305+
{
2306+
fprintf(stderr,"Failed to allocate SIDs: %lu\n", GetLastError());
2307+
return 0;
2308+
}
2309+
2310+
b = _CreateRestrictedToken(origToken,
2311+
DISABLE_MAX_PRIVILEGE,
2312+
sizeof(dropSids)/sizeof(dropSids[0]),
2313+
dropSids,
2314+
0, NULL,
2315+
0, NULL,
2316+
&restrictedToken);
2317+
2318+
FreeSid(dropSids[1].Sid);
2319+
FreeSid(dropSids[0].Sid);
2320+
CloseHandle(origToken);
2321+
FreeLibrary(Advapi32Handle);
2322+
2323+
if (!b)
2324+
{
2325+
fprintf(stderr,"Failed to create restricted token: %lu\n", GetLastError());
2326+
return 0;
2327+
}
2328+
2329+
return CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, processInfo);
2330+
}
2331+
#endif
2332+
22422333
/*
22432334
* print help text
22442335
*/
@@ -2295,6 +2386,9 @@ main(int argc, char *argv[])
22952386
{"auth", required_argument, NULL, 'A'},
22962387
{"pwprompt", no_argument, NULL, 'W'},
22972388
{"pwfile", required_argument, NULL, 9},
2389+
#ifdef WIN32
2390+
{"restrictedexec", no_argument, NULL, 10},
2391+
#endif
22982392
{"username", required_argument, NULL, 'U'},
22992393
{"help", no_argument, NULL, '?'},
23002394
{"version", no_argument, NULL, 'V'},
@@ -2403,6 +2497,11 @@ main(int argc, char *argv[])
24032497
case 9:
24042498
pwfilename = xstrdup(optarg);
24052499
break;
2500+
#ifdef WIN32
2501+
case 10:
2502+
restricted_exec = true;
2503+
break;
2504+
#endif
24062505
case 's':
24072506
show_setting = true;
24082507
break;
@@ -2497,6 +2596,44 @@ main(int argc, char *argv[])
24972596
pg_data_native = pg_data;
24982597
canonicalize_path(pg_data);
24992598

2599+
#ifdef WIN32
2600+
/*
2601+
* Before we execute another program, make sure that we are running with a
2602+
* restricted token. If not, re-execute ourselves with one.
2603+
*/
2604+
if (!restricted_exec)
2605+
{
2606+
PROCESS_INFORMATION pi;
2607+
char *cmdline;
2608+
2609+
ZeroMemory(&pi, sizeof(pi));
2610+
2611+
cmdline = pg_malloc(strlen(GetCommandLine()) + 19);
2612+
strcpy(cmdline, GetCommandLine());
2613+
strcat(cmdline, " --restrictedexec");
2614+
2615+
if (!CreateRestrictedProcess(cmdline, &pi))
2616+
{
2617+
fprintf(stderr,"Failed to re-exec with restricted token: %lu.\n", GetLastError());
2618+
}
2619+
else
2620+
{
2621+
/* Successfully re-execed. Now wait for child process to capture exitcode. */
2622+
DWORD x;
2623+
2624+
CloseHandle(pi.hThread);
2625+
WaitForSingleObject(pi.hProcess, INFINITE);
2626+
2627+
if (!GetExitCodeProcess(pi.hProcess, &x))
2628+
{
2629+
fprintf(stderr,"Failed to get exit code from subprocess: %lu\n", GetLastError());
2630+
exit(1);
2631+
}
2632+
exit(x);
2633+
}
2634+
}
2635+
#endif
2636+
25002637
/*
25012638
* we have to set PGDATA for postgres rather than pass it on the command
25022639
* line to avoid dumb quoting problems on Windows, and we would especially

0 commit comments

Comments
 (0)