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