1 /*------------------------------------------------------------------------- 2 * 3 * restricted_token.c 4 * helper routine to ensure restricted token on Windows 5 * 6 * 7 * Portions Copyright (c) 1996-2020, 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" 24 #include "common/restricted_token.h" 25 26 #ifdef WIN32 connectToServer(ClusterInfo * cluster,const char * db_name)27 28 /* internal vars */ 29 char *restrict_env; 30 31 typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); 32 33 /* Windows API define missing from some versions of MingW headers */ 34 #ifndef DISABLE_MAX_PRIVILEGE 35 #define DISABLE_MAX_PRIVILEGE 0x1 36 #endif 37 38 /* 39 * Create a restricted token and execute the specified process with it. 40 * 41 * Returns restricted token on success and 0 on failure. 42 * 43 * On any system not containing the required functions, do nothing 44 * but still report an error. 45 */ 46 HANDLE 47 CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo) 48 { 49 BOOL b; 50 STARTUPINFO si; 51 HANDLE origToken; 52 HANDLE restrictedToken; 53 SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; 54 SID_AND_ATTRIBUTES dropSids[2]; get_db_conn(ClusterInfo * cluster,const char * db_name)55 __CreateRestrictedToken _CreateRestrictedToken; 56 HANDLE Advapi32Handle; 57 58 ZeroMemory(&si, sizeof(si)); 59 si.cb = sizeof(si); 60 61 Advapi32Handle = LoadLibrary("ADVAPI32.DLL"); 62 if (Advapi32Handle == NULL) 63 { 64 pg_log_error("could not load library \"%s\": error code %lu", 65 "ADVAPI32.DLL", GetLastError()); 66 return 0; 67 } 68 69 _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken"); 70 71 if (_CreateRestrictedToken == NULL) 72 { 73 pg_log_error("cannot create restricted tokens on this platform: error code %lu", 74 GetLastError()); 75 FreeLibrary(Advapi32Handle); 76 return 0; 77 } 78 79 /* Open the current token to use as a base for the restricted one */ 80 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) 81 { 82 pg_log_error("could not open process token: error code %lu", 83 GetLastError()); 84 FreeLibrary(Advapi32Handle); 85 return 0; 86 } 87 88 /* Allocate list of SIDs to remove */ 89 ZeroMemory(&dropSids, sizeof(dropSids)); cluster_conn_opts(ClusterInfo * cluster)90 if (!AllocateAndInitializeSid(&NtAuthority, 2, 91 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 92 0, &dropSids[0].Sid) || 93 !AllocateAndInitializeSid(&NtAuthority, 2, 94 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 95 0, &dropSids[1].Sid)) 96 { 97 pg_log_error("could not allocate SIDs: error code %lu", 98 GetLastError()); 99 CloseHandle(origToken); 100 FreeLibrary(Advapi32Handle); 101 return 0; 102 } 103 104 b = _CreateRestrictedToken(origToken, 105 DISABLE_MAX_PRIVILEGE, 106 sizeof(dropSids) / sizeof(dropSids[0]), 107 dropSids, 108 0, NULL, 109 0, NULL, 110 &restrictedToken); 111 112 FreeSid(dropSids[1].Sid); 113 FreeSid(dropSids[0].Sid); 114 CloseHandle(origToken); 115 FreeLibrary(Advapi32Handle); 116 117 if (!b) 118 { 119 pg_log_error("could not create restricted token: error code %lu", GetLastError()); executeQueryOrDie(PGconn * conn,const char * fmt,...)120 return 0; 121 } 122 123 #ifndef __CYGWIN__ 124 AddUserToTokenDacl(restrictedToken); 125 #endif 126 127 if (!CreateProcessAsUser(restrictedToken, 128 NULL, 129 cmd, 130 NULL, 131 NULL, 132 TRUE, 133 CREATE_SUSPENDED, 134 NULL, 135 NULL, 136 &si, 137 processInfo)) 138 139 { 140 pg_log_error("could not start process for command \"%s\": error code %lu", cmd, GetLastError()); 141 return 0; 142 } 143 144 ResumeThread(processInfo->hThread); 145 return restrictedToken; 146 } 147 #endif 148 149 /* 150 * On Windows make sure that we are running with a restricted token, 151 * On other platforms do nothing. 152 */ 153 void 154 get_restricted_token(void) 155 { 156 #ifdef WIN32 get_major_server_version(ClusterInfo * cluster)157 HANDLE restrictedToken; 158 159 /* 160 * Before we execute another program, make sure that we are running with a 161 * restricted token. If not, re-execute ourselves with one. 162 */ 163 164 if ((restrict_env = getenv("PG_RESTRICT_EXEC")) == NULL 165 || strcmp(restrict_env, "1") != 0) 166 { 167 PROCESS_INFORMATION pi; 168 char *cmdline; 169 170 ZeroMemory(&pi, sizeof(pi)); 171 172 cmdline = pg_strdup(GetCommandLine()); 173 174 putenv("PG_RESTRICT_EXEC=1"); 175 176 if ((restrictedToken = CreateRestrictedProcess(cmdline, &pi)) == 0) 177 { 178 pg_log_error("could not re-execute with restricted token: error code %lu", GetLastError()); 179 } 180 else 181 { 182 /* 183 * Successfully re-executed. Now wait for child process to capture 184 * the exit code. 185 */ 186 DWORD x; 187 188 CloseHandle(restrictedToken); stop_postmaster_atexit(void)189 CloseHandle(pi.hThread); 190 WaitForSingleObject(pi.hProcess, INFINITE); 191 192 if (!GetExitCodeProcess(pi.hProcess, &x)) 193 { 194 pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError()); 195 exit(1); start_postmaster(ClusterInfo * cluster,bool report_and_exit_on_error)196 } 197 exit(x); 198 } 199 pg_free(cmdline); 200 } 201 #endif 202 } 203