diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2017-03-17 11:14:01 +0200 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2017-03-17 11:14:45 +0200 |
commit | 96fd76dd287593b3b444ebddc1f817bd08bc812a (patch) | |
tree | d097ae0bc4bec5a5dade2799a930b6b9d5e3e15d /src | |
parent | ee78ad5bc0d2b905fdfcee997c76e98292f65fbb (diff) | |
download | postgresql-96fd76dd287593b3b444ebddc1f817bd08bc812a.tar.gz postgresql-96fd76dd287593b3b444ebddc1f817bd08bc812a.zip |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/port/win32/security.c | 173 |
1 files changed, 38 insertions, 135 deletions
diff --git a/src/backend/port/win32/security.c b/src/backend/port/win32/security.c index e9cfe15133e..9b8581ece77 100644 --- a/src/backend/port/win32/security.c +++ b/src/backend/port/win32/security.c @@ -14,10 +14,6 @@ #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; -} |