On Sun, 2004-12-12 at 23:59 +0000, Dave Page wrote: > Sounds reasonable to me.
Attached is a patch that implements this. src/port/backend/win32/security.c is moved to src/port/win32_security.c, and conditionally added to LIBOBJS. Note that I don't have much experience with the build system, and less still with the Win32 port, so please let me know if there's a better way to do this. Also, I don't have a Windows build environment -- could someone verify whether this builds on Win32? -Neil
# # delete_file "src/backend/port/win32/security.c" # # add_file "src/port/win32_security.c" # # patch "src/Makefile.global.in" # from [4183afa6d7d7945d4e29a3aa30a18c312251ed8e] # to [6a0e08d33676e1c8c17a296653edbbd8afd5a2c2] # # patch "src/backend/port/win32/Makefile" # from [009b45987d1428cbbd1d0bb78ee55868f9729ca3] # to [97a5831267b24977fa4f3770d06e0fef7884bf2f] # # patch "src/bin/pg_resetxlog/pg_resetxlog.c" # from [df43f11496af96317f9f927f84c488ce33492c93] # to [58ddbddf7765426d60a8fddaaea2034e1e259cfb] # # patch "src/include/port/win32.h" # from [39b47f71111b5c1eaafadaff48cb2c5ccf60ca59] # to [e346ae59f9fde1a95a46ccf81ba3ca8c50bf8cef] # # patch "src/port/win32_security.c" # from [] # to [4766ac4547aa1648eaa8596ce31ce12da763a3c8] # --- src/Makefile.global.in +++ src/Makefile.global.in @@ -362,6 +362,10 @@ LIBOBJS = @LIBOBJS@ dirmod.o exec.o noblock.o path.o pipe.o pgsleep.o pgstrcasecmp.o sprompt.o thread.o +ifeq ($(PORTNAME),win32) +LIBOBJS += win32_security.o +endif + ifneq (,$(LIBOBJS)) LIBS := -lpgport $(LIBS) ifdef PGXS --- src/backend/port/win32/Makefile +++ src/backend/port/win32/Makefile @@ -12,7 +12,7 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = sema.o shmem.o timer.o socket.o signal.o security.o error.o +OBJS = sema.o shmem.o timer.o socket.o signal.o error.o all: SUBSYS.o --- src/bin/pg_resetxlog/pg_resetxlog.c +++ src/bin/pg_resetxlog/pg_resetxlog.c @@ -181,6 +181,32 @@ snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); /* + * Don't allow pg_resetxlog to be run as root, to avoid + * overwriting the ownership of files in the data directory. We + * need only check for root -- any other user won't have + * sufficient permissions to modify files in the data directory. + */ +#ifdef WIN32 + if (pgwin32_is_admin()) + { + fprintf(stderr, _("%s: cannot be executed by a user with " + "administrative permissions\n"), progname); + fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"), + progname); + exit(1); + } +#else + if (geteuid() == 0) + { + fprintf(stderr, _("%s: cannot be executed by \"root\"\n"), + progname); + fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"), + progname); + exit(1); + } +#endif + + /* * Check for a postmaster lock file --- if there is one, refuse to * proceed, on grounds we might be interfering with a live * installation. --- src/include/port/win32.h +++ src/include/port/win32.h @@ -145,15 +145,14 @@ const char *pgwin32_socket_strerror(int err); int pgwin32_waitforsinglesocket(SOCKET s, int what); - -/* in backend/port/win32/security.c */ -extern int pgwin32_is_admin(void); -extern int pgwin32_is_service(void); #endif /* in backend/port/win32/error.c */ void _dosmaperr(unsigned long); +/* in port/win32_security.c */ +extern int pgwin32_is_admin(void); +extern int pgwin32_is_service(void); #define WEXITSTATUS(w) (((w) >> 8) & 0xff) #define WIFEXITED(w) (((w) & 0xff) == 0) --- src/port/win32_security.c +++ src/port/win32_security.c @@ -0,0 +1,248 @@ +/*------------------------------------------------------------------------- + * + * win32_security.c + * Microsoft Windows Win32 Security Support Functions + * + * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/port/win32/security.c,v 1.7 2004/11/16 19:52:22 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#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. + * + * Note: this cannot use ereport() because it's called too early during + * startup. + */ +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 %d\n", + (int) GetLastError()); + exit(1); + } + + if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, + &InfoBuffer, errbuf, sizeof(errbuf))) + { + write_stderr(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, + 0, &AdministratorsSid)) + { + write_stderr("could not get SID for Administrators group: error code %d\n", + (int) GetLastError()); + exit(1); + } + + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, + 0, &PowerUsersSid)) + { + write_stderr("could not get SID for PowerUsers group: error code %d\n", + (int) GetLastError()); + exit(1); + } + + success = FALSE; + + for (x = 0; x < Groups->GroupCount; x++) + { + if (EqualSid(AdministratorsSid, Groups->Groups[x].Sid) || + EqualSid(PowerUsersSid, Groups->Groups[x].Sid)) + { + success = TRUE; + break; + } + } + + free(InfoBuffer); + FreeSid(AdministratorsSid); + FreeSid(PowerUsersSid); + return success; +} + +/* + * 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) + * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the + * process token by the SCM when starting a service) + * + * Return values: + * 0 = Not service + * 1 = Service + * -1 = Error + * + * Note: we can't report errors via either ereport (we're called too early) + * or write_stderr (because that calls this). We are therefore reduced to + * writing directly on stderr, which sucks, but we have few alternatives. + */ +int +pgwin32_is_service(void) +{ + static int _is_service = -1; + 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; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken)) + { + fprintf(stderr, "could not open process token: error code %d\n", + (int) GetLastError()); + return -1; + } + + /* First check for local system */ + if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer, + errbuf, sizeof(errbuf))) + { + fprintf(stderr,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 (EqualSid(LocalSystemSid, User->User.Sid)) + { + FreeSid(LocalSystemSid); + free(InfoBuffer); + CloseHandle(AccessToken); + _is_service = 1; + return _is_service; + } + + FreeSid(LocalSystemSid); + free(InfoBuffer); + + /* Now check for group SID */ + if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer, + errbuf, sizeof(errbuf))) + { + fprintf(stderr,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\n"); + free(InfoBuffer); + CloseHandle(AccessToken); + return -1; + } + + _is_service = 0; + for (x = 0; x < Groups->GroupCount; x++) + { + if (EqualSid(ServiceSid, Groups->Groups[x].Sid)) + { + _is_service = 1; + break; + } + } + + free(InfoBuffer); + FreeSid(ServiceSid); + + 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 %d\n", + (int) 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 %d\n", + (int) GetLastError()); + return FALSE; + } + + return TRUE; +}
---------------------------(end of broadcast)--------------------------- TIP 5: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faqs/FAQ.html