diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2017-03-24 12:39:01 +0200 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2017-03-24 12:39:20 +0200 |
commit | 42a60aa7f2074d1e1cd48f278a00c7d1423f2fb6 (patch) | |
tree | 69cd78bd09df0a91ea4ccb39cba99251bab80b40 | |
parent | 3e04d005c455153a2242c172ed61db2f0b362bc6 (diff) | |
download | postgresql-42a60aa7f2074d1e1cd48f278a00c7d1423f2fb6.tar.gz postgresql-42a60aa7f2074d1e1cd48f278a00c7d1423f2fb6.zip |
Revert Windows service check refactoring, and replace with a different fix.
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
-rw-r--r-- | src/backend/port/win32/security.c | 174 |
1 files changed, 136 insertions, 38 deletions
diff --git a/src/backend/port/win32/security.c b/src/backend/port/win32/security.c index 9b8581ece77..a7c0c44cae8 100644 --- a/src/backend/port/win32/security.c +++ b/src/backend/port/win32/security.c @@ -14,6 +14,10 @@ #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. @@ -24,11 +28,33 @@ 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; +} |