]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix handling of restricted processes for Windows Vista (mainly),
authorMagnus Hagander <magnus@hagander.net>
Fri, 29 Feb 2008 15:31:37 +0000 (15:31 +0000)
committerMagnus Hagander <magnus@hagander.net>
Fri, 29 Feb 2008 15:31:37 +0000 (15:31 +0000)
by explicitly adding back the user to the DACL of the new process.
This fixes the failure case when executing as the Administrator
user, which had no permissions left at all after we dropped the
Administrators group.

Dave Page with some modifications from me

src/bin/initdb/initdb.c
src/bin/pg_ctl/pg_ctl.c
src/include/port.h
src/port/exec.c

index ce5439a1dd12ab5ab9c55c1a9a3b7c4a61c9e296..831eed319f02d408c7220ba998378ea90fceb621 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  * Portions taken from FreeBSD.
  *
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.125 2006/10/04 18:58:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.125.2.1 2008/02/29 15:31:37 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2324,7 +2324,26 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                return 0;
        }
 
-       return CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, processInfo);
+       if (!CreateProcessAsUser(restrictedToken,
+                                               NULL,
+                                               cmd,
+                                               NULL,
+                                               NULL,
+                                               TRUE,
+                                               CREATE_SUSPENDED,
+                                               NULL,
+                                               NULL,
+                                               &si,
+                                               processInfo))
+
+       {
+               fprintf(stderr, "CreateProcessAsUser failed: %lu\n", GetLastError());
+               return 0;
+       }
+
+       AddUserToDacl(processInfo->hProcess);
+
+       return ResumeThread(processInfo->hThread);
 }
 #endif
 
index ae8e3747e11c0d47d3c08270da783ca63820dd40..9b65a09382cedd4943464c40f91797bacd7e42d3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.74.2.2 2008/02/20 22:18:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.74.2.3 2008/02/29 15:31:37 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1424,6 +1424,8 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                }
        }
 
+    AddUserToDacl(processInfo->hProcess);
+    
        CloseHandle(restrictedToken);
 
        ResumeThread(processInfo->hThread);
index 9481c66961c0f0e35f92cf56a9f851a2eb57019e..c9d3e48f0e8232ba91abf3753fdf1b47ebfdc179 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/port.h,v 1.106.2.2 2007/11/29 16:44:26 mha Exp $
+ * $PostgreSQL: pgsql/src/include/port.h,v 1.106.2.3 2008/02/29 15:31:37 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -74,6 +74,12 @@ extern int   find_my_exec(const char *argv0, char *retpath);
 extern int find_other_exec(const char *argv0, const char *target,
                                const char *versionstr, char *retpath);
 
+/* Windows security token manipulation (in exec.c) */
+#ifdef WIN32
+extern BOOL AddUserToDacl(HANDLE hProcess);
+#endif
+
+
 #if defined(WIN32) || defined(__CYGWIN__)
 #define EXE ".exe"
 #else
index 642bd5fdc9eae42c70f6097f3716c60fcbd45e40..1fc0dffaa139eea45a00650e729727737d1ca294 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/port/exec.c,v 1.43 2006/09/11 20:10:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/port/exec.c,v 1.43.2.1 2008/02/29 15:31:37 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,6 +54,9 @@ static int    validate_exec(const char *path);
 static int     resolve_symlinks(char *path);
 static char *pipe_read_line(char *cmd, char *line, int maxsize);
 
+#ifdef WIN32
+static BOOL GetUserSid(PSID * ppSidUser, HANDLE hToken);
+#endif
 
 /*
  * validate_exec -- validate "path" as an executable file
@@ -642,3 +645,217 @@ set_pglocale_pgservice(const char *argv0, const char *app)
                putenv(strdup(env_path));
        }
 }
+
+#ifdef WIN32
+
+/*
+ * AddUserToDacl(HANDLE hProcess)
+ *
+ * This function adds the current user account to the default DACL
+ * which gets attached to the restricted token used when we create
+ * a restricted process.
+ *
+ * This is required because of some security changes in Windows
+ * that appeared in patches to XP/2K3 and in Vista/2008.
+ *
+ * On these machines, the Administrator account is not included in
+ * the default DACL - you just get Administrators + System. For
+ * regular users you get User + System. Because we strip Administrators
+ * when we create the restricted token, we are left with only System
+ * in the DACL which leads to access denied errors for later CreatePipe()
+ * and CreateProcess() calls when running as Administrator.
+ *
+ * This function fixes this problem by modifying the DACL of the
+ * specified process and explicitly re-adding the current user account.
+ * This is still secure because the Administrator account inherits it's
+ * privileges from the Administrators group - it doesn't have any of
+ * it's own.
+ */
+BOOL
+AddUserToDacl(HANDLE hProcess)
+{
+       int                     i;
+       ACL_SIZE_INFORMATION asi;
+       ACCESS_ALLOWED_ACE *pace;
+       DWORD           dwNewAclSize;
+       DWORD           dwSize = 0;
+       DWORD           dwTokenInfoLength = 0;
+       DWORD           dwResult = 0;
+       HANDLE          hToken = NULL;
+       PACL            pacl = NULL;
+       PSID            psidUser = NULL;
+       TOKEN_DEFAULT_DACL tddNew;
+       TOKEN_DEFAULT_DACL *ptdd = NULL;
+       TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
+       BOOL            ret = FALSE;
+
+       /* Get the token for the process */
+       if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &hToken))
+       {
+               log_error("could not open process token: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       /* Figure out the buffer size for the DACL info */
+       if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
+       {
+               if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+               {
+                       ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
+                       if (ptdd == NULL)
+                       {
+                               log_error("could not allocate %i bytes of memory", dwSize);
+                               goto cleanup;
+                       }
+
+                       if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
+                       {
+                               log_error("could not get token information: %ui", GetLastError());
+                               goto cleanup;
+                       }
+               }
+               else
+               {
+                       log_error("could not get token information buffer size: %ui", GetLastError());
+                       goto cleanup;
+               }
+       }
+
+       /* Get the ACL info */
+       if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) & asi,
+                                                  (DWORD) sizeof(ACL_SIZE_INFORMATION),
+                                                  AclSizeInformation))
+       {
+               log_error("could not get ACL information: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       /* Get the SID for the current user. We need to add this to the ACL. */
+       if (!GetUserSid(&psidUser, hToken))
+       {
+               log_error("could not get user SID: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       /* Figure out the size of the new ACL */
+       dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidUser) - sizeof(DWORD);
+
+       /* Allocate the ACL buffer & initialize it */
+       pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
+       if (pacl == NULL)
+       {
+               log_error("could not allocate %i bytes of memory", dwNewAclSize);
+               goto cleanup;
+       }
+
+       if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
+       {
+               log_error("could not initialize ACL: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       /* Loop through the existing ACEs, and build the new ACL */
+       for (i = 0; i < (int) asi.AceCount; i++)
+       {
+               if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) & pace))
+               {
+                       log_error("could not get ACE: %ui", GetLastError());
+                       goto cleanup;
+               }
+
+               if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
+               {
+                       log_error("could not add ACE: %ui", GetLastError());
+                       goto cleanup;
+               }
+       }
+
+       /* Add the new ACE for the current user */
+       if (!AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_ALL, psidUser))
+       {
+               log_error("could not add access allowed ACE: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       /* Set the new DACL in the token */
+       tddNew.DefaultDacl = pacl;
+
+       if (!SetTokenInformation(hToken, tic, (LPVOID) & tddNew, dwNewAclSize))
+       {
+               log_error("could not set token information: %ui", GetLastError());
+               goto cleanup;
+       }
+
+       ret = TRUE;
+
+cleanup:
+       if (psidUser)
+               FreeSid(psidUser);
+
+       if (pacl)
+               LocalFree((HLOCAL) pacl);
+
+       if (ptdd)
+               LocalFree((HLOCAL) ptdd);
+
+       if (hToken)
+               CloseHandle(hToken);
+
+       return ret;
+}
+
+/*
+ * GetUserSid*PSID *ppSidUser, HANDLE hToken)
+ *
+ * Get the SID for the current user
+ */
+static BOOL
+GetUserSid(PSID * ppSidUser, HANDLE hToken)
+{
+       DWORD           dwLength;
+       DWORD           cbName = 250;
+       DWORD           cbDomainName = 250;
+       PTOKEN_USER pTokenUser = NULL;
+
+
+       if (!GetTokenInformation(hToken,
+                                                        TokenUser,
+                                                        pTokenUser,
+                                                        0,
+                                                        &dwLength))
+       {
+               if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+               {
+                       pTokenUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
+
+                       if (pTokenUser == NULL)
+                       {
+                               log_error("could not allocate %ui bytes of memory", dwLength);
+                               return FALSE;
+                       }
+               }
+               else
+               {
+                       log_error("could not get token information buffer size: %ui", GetLastError());
+                       return FALSE;
+               }
+       }
+
+       if (!GetTokenInformation(hToken,
+                                                        TokenUser,
+                                                        pTokenUser,
+                                                        dwLength,
+                                                        &dwLength))
+       {
+               HeapFree(GetProcessHeap(), 0, pTokenUser);
+               pTokenUser = NULL;
+
+               log_error("could not get token information: %ui", GetLastError());
+               return FALSE;
+       }
+
+       *ppSidUser = pTokenUser->User.Sid;
+       return TRUE;
+}
+
+#endif