Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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:14 +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/backend/port/win32/security.c

index 8109d366cc11dfae190c11c90fe8999fc2a1f532..aadbd08aa57c6f4b0a0476e1a243a6806f14621b 100644 (file)
 #include "postgres.h"
 
 
+static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token,
+                           TOKEN_INFORMATION_CLASS class, char **InfoBuffer,
+                             char *errbuf, int errsize);
+
 /*
  * Returns nonzero if the current user has administrative privileges,
  * or zero if not.
 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))
+   {
+       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,
@@ -48,35 +74,32 @@ pgwin32_is_admin(void)
        exit(1);
    }
 
-   if (!CheckTokenMembership(NULL, AdministratorsSid, &IsAdministrators) ||
-       !CheckTokenMembership(NULL, PowerUsersSid, &IsPowerUsers))
+   success = FALSE;
+
+   for (x = 0; x < Groups->GroupCount; x++)
    {
-       write_stderr("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
@@ -90,62 +113,137 @@ 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: 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;
+}