Commit ff30aec7 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Fix and simplify check for whether we're running as Windows service.

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
parent b4ff8609
......@@ -18,11 +18,6 @@
#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.
......@@ -53,33 +48,11 @@ 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};
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);
BOOL IsAdministrators;
BOOL IsPowerUsers;
if (!AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
......@@ -101,34 +74,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;
}
log_error(_("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
......@@ -143,140 +117,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 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment