Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix and simplify check for whether we're running as Windows service.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 17 Mar 2017 09:14:01 +0000 (11:14 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 17 Mar 2017 09:14:58 +0000 (11:14 +0200)
If the process token contains SECURITY_SERVICE_RID, but it has been
disabled by the SE_GROUP_USE_FOR_DENY_ONLY attribute, win32_is_service()
would incorrectly report that we're running as a service. That situation
arises, e.g. if postmaster is launched with a restricted security token,
with the "Log in as Service" privilege explicitly removed.

Replace the broken code with CheckProcessTokenMembership(), which does
this correctly. Also replace similar code in win32_is_admin(), even
though it got this right, for simplicity and consistency.

Per bug #13755, reported by Breen Hagan. Back-patch to all supported
versions. Patch by Takayuki Tsunakawa, reviewed by Michael Paquier.

Discussion: https://www.postgresql.org/message-id/20151104062315.2745.67143%40wrigleys.postgresql.org

src/backend/port/win32/security.c

index 0605ecf240b0fa801aae26d5e08c23e773454cac..a7688f501d0391845ad1c25ab6909d243c2d4056 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.
@@ -28,33 +24,11 @@ static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token,
 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);
+   BOOL        IsAdministrators;
+   BOOL        IsPowerUsers;
 
    if (!AllocateAndInitializeSid(&NtAuthority, 2,
         SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
@@ -74,32 +48,35 @@ pgwin32_is_admin(void)
        exit(1);
    }
 
-   success = FALSE;
-
-   for (x = 0; x < Groups->GroupCount; x++)
+   if (!CheckTokenMembership(NULL, AdministratorsSid, &IsAdministrators) ||
+       !CheckTokenMembership(NULL, PowerUsersSid, &IsPowerUsers))
    {
-       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;
-       }
+       write_stderr("could not check access token membership: error code %lu\n",
+                    GetLastError());
+       exit(1);
    }
 
-   free(InfoBuffer);
    FreeSid(AdministratorsSid);
    FreeSid(PowerUsersSid);
-   return success;
+
+   if (IsAdministrators || IsPowerUsers)
+       return 1;
+   else
+       return 0;
 }
 
 /*
  * We consider ourselves running as a service if one of the following is
  * true:
  *
- * 1) We are running as Local System (only used by services)
+ * 1) We are running as LocalSystem (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
@@ -113,136 +90,62 @@ int
 pgwin32_is_service(void)
 {
    static int  _is_service = -1;
-   HANDLE      AccessToken;
-   char       *InfoBuffer = NULL;
-   char        errbuf[256];
-   PTOKEN_GROUPS Groups;
-   PTOKEN_USER User;
+   BOOL        IsMember;
    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;
 
-   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;
-
+   /* First check for LocalSystem */
    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 (EqualSid(LocalSystemSid, User->User.Sid))
+   if (!CheckTokenMembership(NULL, LocalSystemSid, &IsMember))
    {
+       fprintf(stderr, "could not check access token membership: error code %lu\n",
+               GetLastError());
        FreeSid(LocalSystemSid);
-       free(InfoBuffer);
-       CloseHandle(AccessToken);
-       _is_service = 1;
-       return _is_service;
+       return -1;
    }
-
    FreeSid(LocalSystemSid);
-   free(InfoBuffer);
 
-   /* Now check for group SID */
-   if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
-                                      errbuf, sizeof(errbuf)))
+   if (IsMember)
    {
-       fprintf(stderr, "%s", errbuf);
-       return -1;
+       _is_service = 1;
+       return _is_service;
    }
 
-   Groups = (PTOKEN_GROUPS) InfoBuffer;
-
+   /* Check for service group membership */
    if (!AllocateAndInitializeSid(&NtAuthority, 1,
                                  SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
                                  &ServiceSid))
    {
-       fprintf(stderr, "could not get SID for service group\n");
-       free(InfoBuffer);
-       CloseHandle(AccessToken);
+       fprintf(stderr, "could not get SID for service group: error code %lu\n",
+               GetLastError());
        return -1;
    }
 
-   _is_service = 0;
-   for (x = 0; x < Groups->GroupCount; x++)
+   if (!CheckTokenMembership(NULL, ServiceSid, &IsMember))
    {
-       if (EqualSid(ServiceSid, Groups->Groups[x].Sid))
-       {
-           _is_service = 1;
-           break;
-       }
+       fprintf(stderr, "could not check access token membership: error code %lu\n",
+               GetLastError());
+       FreeSid(ServiceSid);
+       return -1;
    }
-
-   free(InfoBuffer);
    FreeSid(ServiceSid);
 
-   CloseHandle(AccessToken);
+   if (IsMember)
+       _is_service = 1;
+   else
+       _is_service = 0;
 
    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;
-}