diff options
Diffstat (limited to 'src/common/restricted_token.c')
-rw-r--r-- | src/common/restricted_token.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/common/restricted_token.c b/src/common/restricted_token.c new file mode 100644 index 0000000..667040d --- /dev/null +++ b/src/common/restricted_token.c @@ -0,0 +1,202 @@ +/*------------------------------------------------------------------------- + * + * restricted_token.c + * helper routine to ensure restricted token on Windows + * + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/restricted_token.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#error "This file is not expected to be compiled for backend code" +#endif + +#include "postgres_fe.h" + +#include "common/logging.h" +#include "common/restricted_token.h" + +#ifdef WIN32 + +/* internal vars */ +char *restrict_env; + +typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); + +/* Windows API define missing from some versions of MingW headers */ +#ifndef DISABLE_MAX_PRIVILEGE +#define DISABLE_MAX_PRIVILEGE 0x1 +#endif + +/* + * Create a restricted token and execute the specified process with it. + * + * Returns restricted token on success and 0 on failure. + * + * On any system not containing the required functions, do nothing + * but still report an error. + */ +HANDLE +CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo) +{ + BOOL b; + STARTUPINFO si; + HANDLE origToken; + HANDLE restrictedToken; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + SID_AND_ATTRIBUTES dropSids[2]; + __CreateRestrictedToken _CreateRestrictedToken; + HANDLE Advapi32Handle; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); + if (Advapi32Handle == NULL) + { + pg_log_error("could not load library \"%s\": error code %lu", + "ADVAPI32.DLL", GetLastError()); + return 0; + } + + _CreateRestrictedToken = (__CreateRestrictedToken) (pg_funcptr_t) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); + + if (_CreateRestrictedToken == NULL) + { + pg_log_error("cannot create restricted tokens on this platform: error code %lu", + GetLastError()); + FreeLibrary(Advapi32Handle); + return 0; + } + + /* Open the current token to use as a base for the restricted one */ + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) + { + pg_log_error("could not open process token: error code %lu", + GetLastError()); + FreeLibrary(Advapi32Handle); + return 0; + } + + /* Allocate list of SIDs to remove */ + ZeroMemory(&dropSids, sizeof(dropSids)); + if (!AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, + 0, &dropSids[0].Sid) || + !AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, + 0, &dropSids[1].Sid)) + { + pg_log_error("could not allocate SIDs: error code %lu", + GetLastError()); + CloseHandle(origToken); + FreeLibrary(Advapi32Handle); + return 0; + } + + b = _CreateRestrictedToken(origToken, + DISABLE_MAX_PRIVILEGE, + sizeof(dropSids) / sizeof(dropSids[0]), + dropSids, + 0, NULL, + 0, NULL, + &restrictedToken); + + FreeSid(dropSids[1].Sid); + FreeSid(dropSids[0].Sid); + CloseHandle(origToken); + FreeLibrary(Advapi32Handle); + + if (!b) + { + pg_log_error("could not create restricted token: error code %lu", GetLastError()); + return 0; + } + +#ifndef __CYGWIN__ + AddUserToTokenDacl(restrictedToken); +#endif + + if (!CreateProcessAsUser(restrictedToken, + NULL, + cmd, + NULL, + NULL, + TRUE, + CREATE_SUSPENDED, + NULL, + NULL, + &si, + processInfo)) + + { + pg_log_error("could not start process for command \"%s\": error code %lu", cmd, GetLastError()); + return 0; + } + + ResumeThread(processInfo->hThread); + return restrictedToken; +} +#endif + +/* + * On Windows make sure that we are running with a restricted token, + * On other platforms do nothing. + */ +void +get_restricted_token(void) +{ +#ifdef WIN32 + HANDLE restrictedToken; + + /* + * Before we execute another program, make sure that we are running with a + * restricted token. If not, re-execute ourselves with one. + */ + + if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL + || strcmp(restrict_env, "1") != 0) + { + PROCESS_INFORMATION pi; + char *cmdline; + + ZeroMemory(&pi, sizeof(pi)); + + cmdline = pg_strdup(GetCommandLine()); + + setenv("PG_RESTRICT_EXEC", "1", 1); + + if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi)) == 0) + { + pg_log_error("could not re-execute with restricted token: error code %lu", GetLastError()); + } + else + { + /* + * Successfully re-executed. Now wait for child process to capture + * the exit code. + */ + DWORD x; + + CloseHandle(restrictedToken); + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + if (!GetExitCodeProcess(pi.hProcess, &x)) + { + pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError()); + exit(1); + } + exit(x); + } + pg_free(cmdline); + } +#endif +} |