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"
24 #include "common/restricted_token.h"
25 
26 #ifdef WIN32
connectToServer(ClusterInfo * cluster,const char * db_name)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];
get_db_conn(ClusterInfo * cluster,const char * db_name)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) 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));
cluster_conn_opts(ClusterInfo * cluster)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());
executeQueryOrDie(PGconn * conn,const char * fmt,...)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
154 get_restricted_token(void)
155 {
156 #ifdef WIN32
get_major_server_version(ClusterInfo * cluster)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);
stop_postmaster_atexit(void)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);
start_postmaster(ClusterInfo * cluster,bool report_and_exit_on_error)196 			}
197 			exit(x);
198 		}
199 		pg_free(cmdline);
200 	}
201 #endif
202 }
203