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

Reply via email to