Skip to content
Snippets Groups Projects
Commit ff30aec7 authored by Heikki Linnakangas's avatar Heikki Linnakangas
Browse files

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
No related branches found
No related tags found
No related merge requests found
...@@ -18,11 +18,6 @@ ...@@ -18,11 +18,6 @@
#endif #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 * Utility wrapper for frontend and backend when reporting an error
* message. * message.
...@@ -53,33 +48,11 @@ log_error(const char *fmt,...) ...@@ -53,33 +48,11 @@ log_error(const char *fmt,...)
int int
pgwin32_is_admin(void) pgwin32_is_admin(void)
{ {
HANDLE AccessToken;
char *InfoBuffer = NULL;
char errbuf[256];
PTOKEN_GROUPS Groups;
PSID AdministratorsSid; PSID AdministratorsSid;
PSID PowerUsersSid; PSID PowerUsersSid;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
UINT x; BOOL IsAdministrators;
BOOL success; BOOL IsPowerUsers;
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, if (!AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID, SECURITY_BUILTIN_DOMAIN_RID,
...@@ -101,34 +74,35 @@ pgwin32_is_admin(void) ...@@ -101,34 +74,35 @@ pgwin32_is_admin(void)
exit(1); exit(1);
} }
success = FALSE; if (!CheckTokenMembership(NULL, AdministratorsSid, &IsAdministrators) ||
!CheckTokenMembership(NULL, PowerUsersSid, &IsPowerUsers))
for (x = 0; x < Groups->GroupCount; x++)
{ {
if ((EqualSid(AdministratorsSid, Groups->Groups[x].Sid) && log_error(_("could not check access token membership: error code %lu\n"),
(Groups->Groups[x].Attributes & SE_GROUP_ENABLED)) || GetLastError());
(EqualSid(PowerUsersSid, Groups->Groups[x].Sid) && exit(1);
(Groups->Groups[x].Attributes & SE_GROUP_ENABLED)))
{
success = TRUE;
break;
}
} }
free(InfoBuffer);
FreeSid(AdministratorsSid); FreeSid(AdministratorsSid);
FreeSid(PowerUsersSid); 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 * We consider ourselves running as a service if one of the following is
* true: * 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 * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
* process token by the SCM when starting a service) * 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: * Return values:
* 0 = Not service * 0 = Not service
* 1 = Service * 1 = Service
...@@ -143,140 +117,62 @@ int ...@@ -143,140 +117,62 @@ int
pgwin32_is_service(void) pgwin32_is_service(void)
{ {
static int _is_service = -1; static int _is_service = -1;
HANDLE AccessToken; BOOL IsMember;
char *InfoBuffer = NULL;
char errbuf[256];
PTOKEN_GROUPS Groups;
PTOKEN_USER User;
PSID ServiceSid; PSID ServiceSid;
PSID LocalSystemSid; PSID LocalSystemSid;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
UINT x;
/* Only check the first time */ /* Only check the first time */
if (_is_service != -1) if (_is_service != -1)
return _is_service; return _is_service;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken)) /* First check for LocalSystem */
{
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, if (!AllocateAndInitializeSid(&NtAuthority, 1,
SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
&LocalSystemSid)) &LocalSystemSid))
{ {
fprintf(stderr, "could not get SID for local system account\n"); fprintf(stderr, "could not get SID for local system account\n");
CloseHandle(AccessToken);
return -1; 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); FreeSid(LocalSystemSid);
free(InfoBuffer); return -1;
CloseHandle(AccessToken);
_is_service = 1;
return _is_service;
} }
FreeSid(LocalSystemSid); FreeSid(LocalSystemSid);
free(InfoBuffer);
/* Now check for group SID */ if (IsMember)
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
errbuf, sizeof(errbuf)))
{ {
fprintf(stderr, "%s", errbuf); _is_service = 1;
return -1; return _is_service;
} }
Groups = (PTOKEN_GROUPS) InfoBuffer; /* Check for service group membership */
if (!AllocateAndInitializeSid(&NtAuthority, 1, if (!AllocateAndInitializeSid(&NtAuthority, 1,
SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
&ServiceSid)) &ServiceSid))
{ {
fprintf(stderr, "could not get SID for service group\n"); fprintf(stderr, "could not get SID for service group: error code %lu\n",
free(InfoBuffer); GetLastError());
CloseHandle(AccessToken);
return -1; return -1;
} }
_is_service = 0; if (!CheckTokenMembership(NULL, ServiceSid, &IsMember))
for (x = 0; x < Groups->GroupCount; x++)
{ {
if (EqualSid(ServiceSid, Groups->Groups[x].Sid)) fprintf(stderr, "could not check access token membership: error code %lu\n",
{ GetLastError());
_is_service = 1; FreeSid(ServiceSid);
break; return -1;
}
} }
free(InfoBuffer);
FreeSid(ServiceSid); FreeSid(ServiceSid);
CloseHandle(AccessToken); if (IsMember)
_is_service = 1;
else
_is_service = 0;
return _is_service; 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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment