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