1 /*-------------------------------------------------------------------------
2  *
3  * win32security.c
4  *	  Microsoft Windows Win32 Security Support Functions
5  *
6  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *	  src/port/win32security.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #ifndef FRONTEND
15 #include "postgres.h"
16 #else
17 #include "postgres_fe.h"
18 #endif
19 
20 
21 static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token,
22 							  TOKEN_INFORMATION_CLASS class,
23 							  char **InfoBuffer, char *errbuf, int errsize);
24 
25 
26 /*
27  * Utility wrapper for frontend and backend when reporting an error
28  * message.
29  */
30 static
31 pg_attribute_printf(1, 2)
32 void
log_error(const char * fmt,...)33 log_error(const char *fmt,...)
34 {
35 	va_list		ap;
36 
37 	va_start(ap, fmt);
38 #ifndef FRONTEND
39 	write_stderr(fmt, ap);
40 #else
41 	fprintf(stderr, fmt, ap);
42 #endif
43 	va_end(ap);
44 }
45 
46 /*
47  * Returns nonzero if the current user has administrative privileges,
48  * or zero if not.
49  *
50  * Note: this cannot use ereport() because it's called too early during
51  * startup.
52  */
53 int
pgwin32_is_admin(void)54 pgwin32_is_admin(void)
55 {
56 	HANDLE		AccessToken;
57 	char	   *InfoBuffer = NULL;
58 	char		errbuf[256];
59 	PTOKEN_GROUPS Groups;
60 	PSID		AdministratorsSid;
61 	PSID		PowerUsersSid;
62 	SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
63 	UINT		x;
64 	BOOL		success;
65 
66 	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
67 	{
68 		log_error(_("could not open process token: error code %lu\n"),
69 				  GetLastError());
70 		exit(1);
71 	}
72 
73 	if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups,
74 									   &InfoBuffer, errbuf, sizeof(errbuf)))
75 	{
76 		log_error("%s", errbuf);
77 		exit(1);
78 	}
79 
80 	Groups = (PTOKEN_GROUPS) InfoBuffer;
81 
82 	CloseHandle(AccessToken);
83 
84 	if (!AllocateAndInitializeSid(&NtAuthority, 2,
85 								  SECURITY_BUILTIN_DOMAIN_RID,
86 								  DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
87 								  0, &AdministratorsSid))
88 	{
89 		log_error(_("could not get SID for Administrators group: error code %lu\n"),
90 				  GetLastError());
91 		exit(1);
92 	}
93 
94 	if (!AllocateAndInitializeSid(&NtAuthority, 2,
95 								  SECURITY_BUILTIN_DOMAIN_RID,
96 								  DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
97 								  0, &PowerUsersSid))
98 	{
99 		log_error(_("could not get SID for PowerUsers group: error code %lu\n"),
100 				  GetLastError());
101 		exit(1);
102 	}
103 
104 	success = FALSE;
105 
106 	for (x = 0; x < Groups->GroupCount; x++)
107 	{
108 		if ((EqualSid(AdministratorsSid, Groups->Groups[x].Sid) &&
109 			 (Groups->Groups[x].Attributes & SE_GROUP_ENABLED)) ||
110 			(EqualSid(PowerUsersSid, Groups->Groups[x].Sid) &&
111 			 (Groups->Groups[x].Attributes & SE_GROUP_ENABLED)))
112 		{
113 			success = TRUE;
114 			break;
115 		}
116 	}
117 
118 	free(InfoBuffer);
119 	FreeSid(AdministratorsSid);
120 	FreeSid(PowerUsersSid);
121 	return success;
122 }
123 
124 /*
125  * We consider ourselves running as a service if one of the following is
126  * true:
127  *
128  * 1) We are running as Local System (only used by services)
129  * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
130  *	  process token by the SCM when starting a service)
131  *
132  * Return values:
133  *	 0 = Not service
134  *	 1 = Service
135  *	-1 = Error
136  *
137  * Note: we can't report errors via either ereport (we're called too early
138  * in the backend) or write_stderr (because that calls this).  We are
139  * therefore reduced to writing directly on stderr, which sucks, but we
140  * have few alternatives.
141  */
142 int
pgwin32_is_service(void)143 pgwin32_is_service(void)
144 {
145 	static int	_is_service = -1;
146 	HANDLE		AccessToken;
147 	char	   *InfoBuffer = NULL;
148 	char		errbuf[256];
149 	PTOKEN_GROUPS Groups;
150 	PTOKEN_USER User;
151 	PSID		ServiceSid;
152 	PSID		LocalSystemSid;
153 	SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
154 	UINT		x;
155 
156 	/* Only check the first time */
157 	if (_is_service != -1)
158 		return _is_service;
159 
160 	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
161 	{
162 		fprintf(stderr, "could not open process token: error code %lu\n",
163 				GetLastError());
164 		return -1;
165 	}
166 
167 	/* First check for local system */
168 	if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
169 									   errbuf, sizeof(errbuf)))
170 	{
171 		fprintf(stderr, "%s", errbuf);
172 		return -1;
173 	}
174 
175 	User = (PTOKEN_USER) InfoBuffer;
176 
177 	if (!AllocateAndInitializeSid(&NtAuthority, 1,
178 							  SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
179 								  &LocalSystemSid))
180 	{
181 		fprintf(stderr, "could not get SID for local system account\n");
182 		CloseHandle(AccessToken);
183 		return -1;
184 	}
185 
186 	if (EqualSid(LocalSystemSid, User->User.Sid))
187 	{
188 		FreeSid(LocalSystemSid);
189 		free(InfoBuffer);
190 		CloseHandle(AccessToken);
191 		_is_service = 1;
192 		return _is_service;
193 	}
194 
195 	FreeSid(LocalSystemSid);
196 	free(InfoBuffer);
197 
198 	/* Now check for group SID */
199 	if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
200 									   errbuf, sizeof(errbuf)))
201 	{
202 		fprintf(stderr, "%s", errbuf);
203 		return -1;
204 	}
205 
206 	Groups = (PTOKEN_GROUPS) InfoBuffer;
207 
208 	if (!AllocateAndInitializeSid(&NtAuthority, 1,
209 								  SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
210 								  &ServiceSid))
211 	{
212 		fprintf(stderr, "could not get SID for service group\n");
213 		free(InfoBuffer);
214 		CloseHandle(AccessToken);
215 		return -1;
216 	}
217 
218 	_is_service = 0;
219 	for (x = 0; x < Groups->GroupCount; x++)
220 	{
221 		if (EqualSid(ServiceSid, Groups->Groups[x].Sid) &&
222 			(Groups->Groups[x].Attributes & SE_GROUP_ENABLED))
223 		{
224 			_is_service = 1;
225 			break;
226 		}
227 	}
228 
229 	free(InfoBuffer);
230 	FreeSid(ServiceSid);
231 
232 	CloseHandle(AccessToken);
233 
234 	return _is_service;
235 }
236 
237 
238 /*
239  * Call GetTokenInformation() on a token and return a dynamically sized
240  * buffer with the information in it. This buffer must be free():d by
241  * the calling function!
242  */
243 static BOOL
pgwin32_get_dynamic_tokeninfo(HANDLE token,TOKEN_INFORMATION_CLASS class,char ** InfoBuffer,char * errbuf,int errsize)244 pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
245 							  char **InfoBuffer, char *errbuf, int errsize)
246 {
247 	DWORD		InfoBufferSize;
248 
249 	if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
250 	{
251 		snprintf(errbuf, errsize,
252 			 "could not get token information buffer size: got zero size\n");
253 		return FALSE;
254 	}
255 
256 	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
257 	{
258 		snprintf(errbuf, errsize,
259 			 "could not get token information buffer size: error code %lu\n",
260 				 GetLastError());
261 		return FALSE;
262 	}
263 
264 	*InfoBuffer = malloc(InfoBufferSize);
265 	if (*InfoBuffer == NULL)
266 	{
267 		snprintf(errbuf, errsize,
268 				 "could not allocate %d bytes for token information\n",
269 				 (int) InfoBufferSize);
270 		return FALSE;
271 	}
272 
273 	if (!GetTokenInformation(token, class, *InfoBuffer,
274 							 InfoBufferSize, &InfoBufferSize))
275 	{
276 		snprintf(errbuf, errsize,
277 				 "could not get token information: error code %lu\n",
278 				 GetLastError());
279 		return FALSE;
280 	}
281 
282 	return TRUE;
283 }
284