diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index f3eb38b2dc42083e97fdab3170e3609d9dd60246..41db8a8b02964628ddce3dbd7d7c9ac8e8b72bcb 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -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.153 2008/02/20 22:46:24 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.154 2008/02/29 15:31:33 mha Exp $ * *------------------------------------------------------------------------- */ @@ -2329,7 +2329,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 diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index bd969169298a4c2826c82230ac6e08f886ee2fca..9e0e16ca6b1b04a7421b515faf095c5b981db98c 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.94 2008/02/20 22:46:24 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.95 2008/02/29 15:31:33 mha Exp $ * *------------------------------------------------------------------------- */ @@ -1469,6 +1469,8 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo) } } + AddUserToDacl(processInfo->hProcess); + CloseHandle(restrictedToken); ResumeThread(processInfo->hThread); diff --git a/src/include/port.h b/src/include/port.h index f2907f4b348330db0b95121a701559f217f6518a..38c6c5f68691a1d91a99fd35f4ebd1b6c9ac0870 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/port.h,v 1.117 2008/02/18 14:51:48 petere Exp $ + * $PostgreSQL: pgsql/src/include/port.h,v 1.118 2008/02/29 15:31:33 mha Exp $ * *------------------------------------------------------------------------- */ @@ -79,6 +79,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 diff --git a/src/port/exec.c b/src/port/exec.c index 38a725a28af41ca94fe5669e07e2081dda4b995a..e9a60d67fcc6efde7b34d6e5dbee41f771c94fe8 100644 --- a/src/port/exec.c +++ b/src/port/exec.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/port/exec.c,v 1.57 2008/01/01 19:46:00 momjian Exp $ + * $PostgreSQL: pgsql/src/port/exec.c,v 1.58 2008/02/29 15:31:33 mha Exp $ * *------------------------------------------------------------------------- */ @@ -55,6 +55,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 @@ -657,3 +660,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