From: Tom Lane [t...@sss.pgh.pa.us] Sent: Tuesday, August 21, 2012 10:31 PM Amit Kapila <amit.kap...@huawei.com> writes: > [mailto:pgsql-hackers-ow...@postgresql.org] On Behalf Of Tom Lane >> * pg_ctl crashes on Win32 when neither PGDATA nor -D specified
>>> isn't there a way to actually test if we're in a restricted process? >> Do you mean to say that it should check if pg_ctl runs as an administrative >> user then do the re-fork in restricted mode. > Something like that. The proposed patch depends on there not being a > conflicting environment variable, which seems rather fragile to me. > Can't we test the same condition that postgres.exe itself would test? To implement the postgre.exe way we have following options: 1. Duplicate the function pgwin32_is_admin and related function to pg_ctl, as currently it is not exposed. 2. Make that visible to pg_ctl, but for that it need to link with postgre lib. 3. Move the functions to some common place may be src/port. 4. any other better way? Curretly I have implemented the patch with Approach-1, but I believe Approach-3 would have been better. However I was not sure which is the best place to move functions, so I have implemented with Approach-1. Please let me know if the attached patch is acceptable. I shall wait today night for your confirmation and shall let you know before I leave my work place in which case I shall complete tommorow morning but not sure whether that much delay is acceptable. With Regards, Amit Kapila.
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 /*
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers