diff --git a/src/backend/port/win32/security.c b/src/backend/port/win32/security.c index 8109d366cc1..aadbd08aa57 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; - } - FreeSid(LocalSystemSid); - - if (IsMember) - { + free(InfoBuffer); + CloseHandle(AccessToken); _is_service = 1; return _is_service; } - /* Check for service group membership */ + FreeSid(LocalSystemSid); + free(InfoBuffer); + + /* Now check for group SID */ + if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer, + errbuf, sizeof(errbuf))) + { + fprintf(stderr, "%s", errbuf); + return -1; + } + + 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; +}