diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index af8d8b2..ff5d373 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -258,6 +258,121 @@ xstrdup(const char *s) } /* + * Call GetTokenInformation() on a token and return a dynamically sized + * buffer with the information in it. This buffer must be free():d by + * the calling function! + */ +static BOOL +pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class, + char **InfoBuffer, char *errbuf, int errsize) +{ + DWORD InfoBufferSize; + + if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize)) + { + snprintf(errbuf, errsize, "could not get token information: got zero size\n"); + return FALSE; + } + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + snprintf(errbuf, errsize, "could not get token information: error code %lu\n", + GetLastError()); + return FALSE; + } + + *InfoBuffer = malloc(InfoBufferSize); + if (*InfoBuffer == NULL) + { + snprintf(errbuf, errsize, "could not allocate %d bytes for token information\n", + (int) InfoBufferSize); + return FALSE; + } + + if (!GetTokenInformation(token, class, *InfoBuffer, + InfoBufferSize, &InfoBufferSize)) + { + snprintf(errbuf, errsize, "could not get token information: error code %lu\n", + GetLastError()); + return FALSE; + } + + return TRUE; +} + + +/* + * Returns nonzero if the current user has administrative privileges, + * or zero if not. + */ +static int +pgwin32_is_admin(void) +{ + HANDLE AccessToken; + char *InfoBuffer = NULL; + char errbuf[256]; + PTOKEN_GROUPS Groups; + PSID AdministratorsSid; + PSID PowerUsersSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + UINT x; + BOOL success; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken)) + { + write_stderr("could not open process token: error code %lu\n", + GetLastError()); + exit(1); + } + + if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, + &InfoBuffer, errbuf, sizeof(errbuf))) + { + write_stderr("%s", errbuf); + exit(1); + } + + Groups = (PTOKEN_GROUPS) InfoBuffer; + + CloseHandle(AccessToken); + + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, + 0, &AdministratorsSid)) + { + write_stderr("could not get SID for Administrators group: error code %lu\n", + GetLastError()); + exit(1); + } + + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, + 0, &PowerUsersSid)) + { + write_stderr("could not get SID for PowerUsers group: error code %lu\n", + GetLastError()); + exit(1); + } + + success = FALSE; + + for (x = 0; x < Groups->GroupCount; x++) + { + if ((EqualSid(AdministratorsSid, Groups->Groups[x].Sid) && (Groups->Groups[x].Attributes & SE_GROUP_ENABLED)) || + (EqualSid(PowerUsersSid, Groups->Groups[x].Sid) && (Groups->Groups[x].Attributes & SE_GROUP_ENABLED))) + { + success = TRUE; + break; + } + } + + free(InfoBuffer); + FreeSid(AdministratorsSid); + FreeSid(PowerUsersSid); + return success; +} + +/* * Given an already-localized string, print it to stdout unless the * user has specified that no messages should be printed. */ @@ -2015,6 +2130,47 @@ main(int argc, char **argv) progname); exit(1); } +#else + + /* + * Before we execute another program, make sure that we are running with a + * restricted token. If not, re-execute ourselves with one. + */ + if (pgwin32_is_admin()) + { + PROCESS_INFORMATION pi; + char *cmdline; + + ZeroMemory(&pi, sizeof(pi)); + + cmdline = xstrdup(GetCommandLine()); + + putenv("PG_RESTRICT_EXEC=1"); + + if (!CreateRestrictedProcess(cmdline, &pi, false)) + { + write_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)) + { + write_stderr(_("%s: could not get exit code from subprocess: error code %lu\n"), progname, GetLastError()); + exit(1); + } + + exit(x); + } + } #endif /*