From: Tom Lane [[email protected]]
Sent: Tuesday, August 21, 2012 10:31 PM
Amit Kapila <[email protected]> writes:
> [mailto:[email protected]] 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers