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