1 /*-------------------------------------------------------------------------
2 *
3 * restricted_token.c
4 * helper routine to ensure restricted token on Windows
5 *
6 *
7 * Portions Copyright (c) 1996-2021, 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"
24 #include "common/restricted_token.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
CreateRestrictedProcess(char * cmd,PROCESS_INFORMATION * processInfo)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
get_restricted_token(void)154 get_restricted_token(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 setenv("PG_RESTRICT_EXEC", "1", 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 }
203