PostgreSQL Source Code  git master
restricted_token.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * restricted_token.c
4  * helper routine to ensure restricted token on Windows
5  *
6  *
7  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/common/restricted_token.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 #error "This file is not expected to be compiled for backend code"
19 #endif
20 
21 #include "postgres_fe.h"
22 
23 #include "common/logging.h"
25 
26 #ifdef WIN32
27 
28 /* internal vars */
29 char *restrict_env;
30 
31 typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
32 
33 /* Windows API define missing from some versions of MingW headers */
34 #ifndef DISABLE_MAX_PRIVILEGE
35 #define DISABLE_MAX_PRIVILEGE 0x1
36 #endif
37 
38 /*
39  * Create a restricted token and execute the specified process with it.
40  *
41  * Returns restricted token on success and 0 on failure.
42  *
43  * On any system not containing the required functions, do nothing
44  * but still report an error.
45  */
46 HANDLE
47 CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo)
48 {
49  BOOL b;
50  STARTUPINFO si;
51  HANDLE origToken;
52  HANDLE restrictedToken;
53  SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
54  SID_AND_ATTRIBUTES dropSids[2];
55  __CreateRestrictedToken _CreateRestrictedToken;
56  HANDLE Advapi32Handle;
57 
58  ZeroMemory(&si, sizeof(si));
59  si.cb = sizeof(si);
60 
61  Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
62  if (Advapi32Handle == NULL)
63  {
64  pg_log_error("could not load library \"%s\": error code %lu",
65  "ADVAPI32.DLL", GetLastError());
66  return 0;
67  }
68 
69  _CreateRestrictedToken = (__CreateRestrictedToken) (pg_funcptr_t) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
70 
71  if (_CreateRestrictedToken == NULL)
72  {
73  pg_log_error("cannot create restricted tokens on this platform: error code %lu",
74  GetLastError());
75  FreeLibrary(Advapi32Handle);
76  return 0;
77  }
78 
79  /* Open the current token to use as a base for the restricted one */
80  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
81  {
82  pg_log_error("could not open process token: error code %lu",
83  GetLastError());
84  FreeLibrary(Advapi32Handle);
85  return 0;
86  }
87 
88  /* Allocate list of SIDs to remove */
89  ZeroMemory(&dropSids, sizeof(dropSids));
90  if (!AllocateAndInitializeSid(&NtAuthority, 2,
91  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
92  0, &dropSids[0].Sid) ||
93  !AllocateAndInitializeSid(&NtAuthority, 2,
94  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
95  0, &dropSids[1].Sid))
96  {
97  pg_log_error("could not allocate SIDs: error code %lu",
98  GetLastError());
99  CloseHandle(origToken);
100  FreeLibrary(Advapi32Handle);
101  return 0;
102  }
103 
104  b = _CreateRestrictedToken(origToken,
105  DISABLE_MAX_PRIVILEGE,
106  sizeof(dropSids) / sizeof(dropSids[0]),
107  dropSids,
108  0, NULL,
109  0, NULL,
110  &restrictedToken);
111 
112  FreeSid(dropSids[1].Sid);
113  FreeSid(dropSids[0].Sid);
114  CloseHandle(origToken);
115  FreeLibrary(Advapi32Handle);
116 
117  if (!b)
118  {
119  pg_log_error("could not create restricted token: error code %lu", GetLastError());
120  return 0;
121  }
122 
123 #ifndef __CYGWIN__
124  AddUserToTokenDacl(restrictedToken);
125 #endif
126 
127  if (!CreateProcessAsUser(restrictedToken,
128  NULL,
129  cmd,
130  NULL,
131  NULL,
132  TRUE,
133  CREATE_SUSPENDED,
134  NULL,
135  NULL,
136  &si,
137  processInfo))
138 
139  {
140  pg_log_error("could not start process for command \"%s\": error code %lu", cmd, GetLastError());
141  return 0;
142  }
143 
144  ResumeThread(processInfo->hThread);
145  return restrictedToken;
146 }
147 #endif
148 
149 /*
150  * On Windows make sure that we are running with a restricted token,
151  * On other platforms do nothing.
152  */
153 void
155 {
156 #ifdef WIN32
157  HANDLE restrictedToken;
158 
159  /*
160  * Before we execute another program, make sure that we are running with a
161  * restricted token. If not, re-execute ourselves with one.
162  */
163 
164  if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL
165  || strcmp(restrict_env, "1") != 0)
166  {
167  PROCESS_INFORMATION pi;
168  char *cmdline;
169 
170  ZeroMemory(&pi, sizeof(pi));
171 
172  cmdline = pg_strdup(GetCommandLine());
173 
174  putenv("PG_RESTRICT_EXEC=1");
175 
176  if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi)) == 0)
177  {
178  pg_log_error("could not re-execute with restricted token: error code %lu", GetLastError());
179  }
180  else
181  {
182  /*
183  * Successfully re-executed. Now wait for child process to capture
184  * the exit code.
185  */
186  DWORD x;
187 
188  CloseHandle(restrictedToken);
189  CloseHandle(pi.hThread);
190  WaitForSingleObject(pi.hProcess, INFINITE);
191 
192  if (!GetExitCodeProcess(pi.hProcess, &x))
193  {
194  pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError());
195  exit(1);
196  }
197  exit(x);
198  }
199  pg_free(cmdline);
200  }
201 #endif
202 }
#define pg_log_error(...)
Definition: logging.h:80
void get_restricted_token(void)
BOOL AddUserToTokenDacl(HANDLE hToken)
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
#define putenv(x)
Definition: win32_port.h:502
void(* pg_funcptr_t)(void)
Definition: c.h:328