PostgreSQL Source Code  git master
restricted_token.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * restricted_token.c
4  * helper routine to ensure restricted token on Windows
5  *
6  *
7  * Portions Copyright (c) 1996-2024, 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"
25 
26 #ifdef WIN32
27 
28 /* internal vars */
29 static char *restrict_env;
30 
31 /* Windows API define missing from some versions of MingW headers */
32 #ifndef DISABLE_MAX_PRIVILEGE
33 #define DISABLE_MAX_PRIVILEGE 0x1
34 #endif
35 
36 /*
37  * Create a restricted token and execute the specified process with it.
38  *
39  * Returns restricted token on success and 0 on failure.
40  *
41  * On any system not containing the required functions, do nothing
42  * but still report an error.
43  */
44 HANDLE
45 CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo)
46 {
47  BOOL b;
48  STARTUPINFO si;
49  HANDLE origToken;
50  HANDLE restrictedToken;
51  SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
52  SID_AND_ATTRIBUTES dropSids[2];
53 
54  ZeroMemory(&si, sizeof(si));
55  si.cb = sizeof(si);
56 
57  /* Open the current token to use as a base for the restricted one */
58  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
59  {
60  pg_log_error("could not open process token: error code %lu",
61  GetLastError());
62  return 0;
63  }
64 
65  /* Allocate list of SIDs to remove */
66  ZeroMemory(&dropSids, sizeof(dropSids));
67  if (!AllocateAndInitializeSid(&NtAuthority, 2,
68  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
69  0, &dropSids[0].Sid) ||
70  !AllocateAndInitializeSid(&NtAuthority, 2,
71  SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
72  0, &dropSids[1].Sid))
73  {
74  pg_log_error("could not allocate SIDs: error code %lu",
75  GetLastError());
76  CloseHandle(origToken);
77  return 0;
78  }
79 
80  b = CreateRestrictedToken(origToken,
81  DISABLE_MAX_PRIVILEGE,
82  sizeof(dropSids) / sizeof(dropSids[0]),
83  dropSids,
84  0, NULL,
85  0, NULL,
86  &restrictedToken);
87 
88  FreeSid(dropSids[1].Sid);
89  FreeSid(dropSids[0].Sid);
90  CloseHandle(origToken);
91 
92  if (!b)
93  {
94  pg_log_error("could not create restricted token: error code %lu", GetLastError());
95  return 0;
96  }
97 
98 #ifndef __CYGWIN__
99  AddUserToTokenDacl(restrictedToken);
100 #endif
101 
102  if (!CreateProcessAsUser(restrictedToken,
103  NULL,
104  cmd,
105  NULL,
106  NULL,
107  TRUE,
108  CREATE_SUSPENDED,
109  NULL,
110  NULL,
111  &si,
112  processInfo))
113 
114  {
115  pg_log_error("could not start process for command \"%s\": error code %lu", cmd, GetLastError());
116  return 0;
117  }
118 
119  ResumeThread(processInfo->hThread);
120  return restrictedToken;
121 }
122 #endif
123 
124 /*
125  * On Windows make sure that we are running with a restricted token,
126  * On other platforms do nothing.
127  */
128 void
130 {
131 #ifdef WIN32
132  HANDLE restrictedToken;
133 
134  /*
135  * Before we execute another program, make sure that we are running with a
136  * restricted token. If not, re-execute ourselves with one.
137  */
138 
139  if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL
140  || strcmp(restrict_env, "1") != 0)
141  {
142  PROCESS_INFORMATION pi;
143  char *cmdline;
144 
145  ZeroMemory(&pi, sizeof(pi));
146 
147  cmdline = pg_strdup(GetCommandLine());
148 
149  setenv("PG_RESTRICT_EXEC", "1", 1);
150 
151  if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi)) == 0)
152  {
153  pg_log_error("could not re-execute with restricted token: error code %lu", GetLastError());
154  }
155  else
156  {
157  /*
158  * Successfully re-executed. Now wait for child process to capture
159  * the exit code.
160  */
161  DWORD x;
162 
163  CloseHandle(restrictedToken);
164  CloseHandle(pi.hThread);
165  WaitForSingleObject(pi.hProcess, INFINITE);
166 
167  if (!GetExitCodeProcess(pi.hProcess, &x))
168  pg_fatal("could not get exit code from subprocess: error code %lu", GetLastError());
169  exit(x);
170  }
171  pg_free(cmdline);
172  }
173 #endif
174 }
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
int b
Definition: isn.c:69
int x
Definition: isn.c:70
exit(1)
#define pg_log_error(...)
Definition: logging.h:106
#define pg_fatal(...)
void get_restricted_token(void)
BOOL AddUserToTokenDacl(HANDLE hToken)
#define setenv(x, y, z)
Definition: win32_port.h:555