]> granicus.if.org Git - postgresql/commitdiff
Revert Windows service check refactoring, and replace with a different fix.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 24 Mar 2017 10:39:01 +0000 (12:39 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 24 Mar 2017 10:39:23 +0000 (12:39 +0200)
This reverts commit 38bdba54a64bacec78e3266f0848b0b4a824132a, "Fix and
simplify check for whether we're running as Windows service". It turns out
that older versions of MinGW - like that on buildfarm member narwhal - do
not support the CheckTokenMembership() function. This replaces the
refactoring with a much smaller fix, to add a check for SE_GROUP_ENABLED to
pgwin32_is_service().

Only apply to back-branches, and keep the refactoring in HEAD. It's
unlikely that anyone is still really using such an old version of MinGW -
aside from narwhal - but let's not change the minimum requirements in
minor releases.

Discussion: https://www.postgresql.org/message-id/16609.1489773427@sss.pgh.pa.us
Patch: https://www.postgresql.org/message-id/CAB7nPqSvfu%3DKpJ%3DNX%2BYAHmgAmQdzA7N5h31BjzXeMgczhGCC%2BQ%40mail.gmail.com

src/port/win32security.c

index fe4c22dfbf16006b5ac62e591ed8d20757070b0e..d5a7346aca584a4c7c145c795661f706147c698b 100644 (file)
 #endif
 
 
+static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token,
+                                                         TOKEN_INFORMATION_CLASS class,
+                                                         char **InfoBuffer, char *errbuf, int errsize);
+
+
 /*
  * Utility wrapper for frontend and backend when reporting an error
  * message.
@@ -48,11 +53,33 @@ log_error(const char *fmt,...)
 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};
-       BOOL            IsAdministrators;
-       BOOL            IsPowerUsers;
+       UINT            x;
+       BOOL            success;
+
+       if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
+       {
+               log_error(_("could not open process token: error code %lu\n"),
+                                 GetLastError());
+               exit(1);
+       }
+
+       if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups,
+                                                                          &InfoBuffer, errbuf, sizeof(errbuf)))
+       {
+               log_error("%s", errbuf);
+               exit(1);
+       }
+
+       Groups = (PTOKEN_GROUPS) InfoBuffer;
+
+       CloseHandle(AccessToken);
 
        if (!AllocateAndInitializeSid(&NtAuthority, 2,
                                                                  SECURITY_BUILTIN_DOMAIN_RID,
@@ -74,35 +101,34 @@ pgwin32_is_admin(void)
                exit(1);
        }
 
-       if (!CheckTokenMembership(NULL, AdministratorsSid, &IsAdministrators) ||
-               !CheckTokenMembership(NULL, PowerUsersSid, &IsPowerUsers))
+       success = FALSE;
+
+       for (x = 0; x < Groups->GroupCount; x++)
        {
-               log_error(_("could not check access token membership: error code %lu\n"),
-                                 GetLastError());
-               exit(1);
+               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);
-
-       if (IsAdministrators || IsPowerUsers)
-               return 1;
-       else
-               return 0;
+       return success;
 }
 
 /*
  * We consider ourselves running as a service if one of the following is
  * true:
  *
- * 1) We are running as LocalSystem (only used by services)
+ * 1) We are running as Local System (only used by services)
  * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
  *       process token by the SCM when starting a service)
  *
- * The check for LocalSystem is needed, because surprisingly, if a service
- * is running as LocalSystem, it does not have SECURITY_SERVICE_RID in its
- * process token.
- *
  * Return values:
  *      0 = Not service
  *      1 = Service
@@ -117,62 +143,141 @@ int
 pgwin32_is_service(void)
 {
        static int      _is_service = -1;
-       BOOL            IsMember;
+       HANDLE          AccessToken;
+       char       *InfoBuffer = NULL;
+       char            errbuf[256];
+       PTOKEN_GROUPS Groups;
+       PTOKEN_USER User;
        PSID            ServiceSid;
        PSID            LocalSystemSid;
        SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
+       UINT            x;
 
        /* Only check the first time */
        if (_is_service != -1)
                return _is_service;
 
-       /* First check for LocalSystem */
+       if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
+       {
+               fprintf(stderr, "could not open process token: error code %lu\n",
+                               GetLastError());
+               return -1;
+       }
+
+       /* First check for local system */
+       if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
+                                                                          errbuf, sizeof(errbuf)))
+       {
+               fprintf(stderr, "%s", errbuf);
+               return -1;
+       }
+
+       User = (PTOKEN_USER) InfoBuffer;
+
        if (!AllocateAndInitializeSid(&NtAuthority, 1,
                                                          SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
                                                                  &LocalSystemSid))
        {
                fprintf(stderr, "could not get SID for local system account\n");
+               CloseHandle(AccessToken);
                return -1;
        }
 
-       if (!CheckTokenMembership(NULL, LocalSystemSid, &IsMember))
+       if (EqualSid(LocalSystemSid, User->User.Sid))
        {
-               fprintf(stderr, "could not check access token membership: error code %lu\n",
-                               GetLastError());
                FreeSid(LocalSystemSid);
-               return -1;
+               free(InfoBuffer);
+               CloseHandle(AccessToken);
+               _is_service = 1;
+               return _is_service;
        }
+
        FreeSid(LocalSystemSid);
+       free(InfoBuffer);
 
-       if (IsMember)
+       /* Now check for group SID */
+       if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
+                                                                          errbuf, sizeof(errbuf)))
        {
-               _is_service = 1;
-               return _is_service;
+               fprintf(stderr, "%s", errbuf);
+               return -1;
        }
 
-       /* Check for service group membership */
+       Groups = (PTOKEN_GROUPS) InfoBuffer;
+
        if (!AllocateAndInitializeSid(&NtAuthority, 1,
                                                                  SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
                                                                  &ServiceSid))
        {
-               fprintf(stderr, "could not get SID for service group: error code %lu\n",
-                               GetLastError());
+               fprintf(stderr, "could not get SID for service group\n");
+               free(InfoBuffer);
+               CloseHandle(AccessToken);
                return -1;
        }
 
-       if (!CheckTokenMembership(NULL, ServiceSid, &IsMember))
+       _is_service = 0;
+       for (x = 0; x < Groups->GroupCount; x++)
        {
-               fprintf(stderr, "could not check access token membership: error code %lu\n",
-                               GetLastError());
-               FreeSid(ServiceSid);
-               return -1;
+               if (EqualSid(ServiceSid, Groups->Groups[x].Sid) &&
+                       (Groups->Groups[x].Attributes & SE_GROUP_ENABLED))
+               {
+                       _is_service = 1;
+                       break;
+               }
        }
+
+       free(InfoBuffer);
        FreeSid(ServiceSid);
 
-       if (IsMember)
-               _is_service = 1;
-       else
-               _is_service = 0;
+       CloseHandle(AccessToken);
 
        return _is_service;
 }
+
+
+/*
+ * 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 buffer size: got zero size\n");
+               return FALSE;
+       }
+
+       if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+       {
+               snprintf(errbuf, errsize,
+                        "could not get token information buffer size: 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;
+}