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

Reply via email to