PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
win32security.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * win32security.c
4  * Microsoft Windows Win32 Security Support Functions
5  *
6  * Portions Copyright (c) 1996-2017, 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
32 void
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
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
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  {
223  _is_service = 1;
224  break;
225  }
226  }
227 
228  free(InfoBuffer);
229  FreeSid(ServiceSid);
230 
231  CloseHandle(AccessToken);
232 
233  return _is_service;
234 }
235 
236 
237 /*
238  * Call GetTokenInformation() on a token and return a dynamically sized
239  * buffer with the information in it. This buffer must be free():d by
240  * the calling function!
241  */
242 static BOOL
243 pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
244  char **InfoBuffer, char *errbuf, int errsize)
245 {
246  DWORD InfoBufferSize;
247 
248  if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
249  {
250  snprintf(errbuf, errsize,
251  "could not get token information buffer size: got zero size\n");
252  return FALSE;
253  }
254 
255  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
256  {
257  snprintf(errbuf, errsize,
258  "could not get token information buffer size: error code %lu\n",
259  GetLastError());
260  return FALSE;
261  }
262 
263  *InfoBuffer = malloc(InfoBufferSize);
264  if (*InfoBuffer == NULL)
265  {
266  snprintf(errbuf, errsize,
267  "could not allocate %d bytes for token information\n",
268  (int) InfoBufferSize);
269  return FALSE;
270  }
271 
272  if (!GetTokenInformation(token, class, *InfoBuffer,
273  InfoBufferSize, &InfoBufferSize))
274  {
275  snprintf(errbuf, errsize,
276  "could not get token information: error code %lu\n",
277  GetLastError());
278  return FALSE;
279  }
280 
281  return TRUE;
282 }
#define log_error(str, param)
Definition: exec.c:31
int pgwin32_is_service(void)
#define write_stderr(str)
Definition: parallel.c:182
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
int pgwin32_is_admin(void)
Definition: win32security.c:54
static pg_attribute_printf(1, 2)
Definition: win32security.c:31
#define malloc(a)
Definition: header.h:45
#define FALSE
Definition: c.h:218
static bool success
Definition: pg_basebackup.c:99
#define free(a)
Definition: header.h:60
#define NULL
Definition: c.h:226
#define TRUE
Definition: c.h:214
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess
#define _(x)
Definition: elog.c:84
static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class, char **InfoBuffer, char *errbuf, int errsize)