| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/win/scoped_handle.h" | |
| 6 #include "base/win/scoped_process_information.h" | |
| 7 #include "base/win/windows_version.h" | |
| 8 #include "sandbox/win/src/restricted_token_utils.h" | |
| 9 | |
| 10 // launcher.exe is an application used to launch another application with a | |
| 11 // restricted token. This is to be used for testing only. | |
| 12 // The parameters are the level of security of the primary token, the | |
| 13 // impersonation token and the job object along with the command line to | |
| 14 // execute. | |
| 15 // See the usage (launcher.exe without parameters) for the correct format. | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 // Starts the process described by the input parameter command_line in a job | |
| 20 // with a restricted token. Also set the main thread of this newly created | |
| 21 // process to impersonate a user with more rights so it can initialize | |
| 22 // correctly. | |
| 23 // | |
| 24 // Parameters: primary_level is the security level of the primary token. | |
| 25 // impersonation_level is the security level of the impersonation token used | |
| 26 // to initialize the process. job_level is the security level of the job | |
| 27 // object used to encapsulate the process. | |
| 28 // | |
| 29 // The output parameter job_handle is the handle to the job object. Closing this | |
| 30 // handle will kill the process started. | |
| 31 // | |
| 32 // Note: The process started with this function has to call RevertToSelf() as | |
| 33 // soon as possible to stop using the impersonation token and start being | |
| 34 // secure. | |
| 35 // | |
| 36 // Note: The Unicode version of this function will fail if the command_line | |
| 37 // parameter is a const string. | |
| 38 DWORD StartRestrictedProcessInJob(wchar_t* command_line, | |
| 39 TokenLevel primary_level, | |
| 40 TokenLevel impersonation_level, | |
| 41 JobLevel job_level, | |
| 42 base::win::ScopedHandle* job_handle) { | |
| 43 Job job; | |
| 44 DWORD err_code = job.Init(job_level, NULL, 0, 0); | |
| 45 if (ERROR_SUCCESS != err_code) | |
| 46 return err_code; | |
| 47 | |
| 48 if (JOB_UNPROTECTED != job_level) { | |
| 49 // Share the Desktop handle to be able to use MessageBox() in the sandboxed | |
| 50 // application. | |
| 51 err_code = job.UserHandleGrantAccess(GetDesktopWindow()); | |
| 52 if (ERROR_SUCCESS != err_code) | |
| 53 return err_code; | |
| 54 } | |
| 55 | |
| 56 // Create the primary (restricted) token for the process | |
| 57 base::win::ScopedHandle primary_token; | |
| 58 err_code = sandbox::CreateRestrictedToken(primary_level, INTEGRITY_LEVEL_LAST, | |
| 59 PRIMARY, &primary_token); | |
| 60 if (ERROR_SUCCESS != err_code) | |
| 61 return err_code; | |
| 62 | |
| 63 | |
| 64 // Create the impersonation token (restricted) to be able to start the | |
| 65 // process. | |
| 66 base::win::ScopedHandle impersonation_token; | |
| 67 err_code = sandbox::CreateRestrictedToken(impersonation_level, | |
| 68 INTEGRITY_LEVEL_LAST, | |
| 69 IMPERSONATION, | |
| 70 &impersonation_token); | |
| 71 if (ERROR_SUCCESS != err_code) | |
| 72 return err_code; | |
| 73 | |
| 74 // Start the process | |
| 75 STARTUPINFO startup_info = {0}; | |
| 76 PROCESS_INFORMATION temp_process_info = {}; | |
| 77 DWORD flags = CREATE_SUSPENDED; | |
| 78 | |
| 79 if (base::win::GetVersion() < base::win::VERSION_WIN8) { | |
| 80 // Windows 8 implements nested jobs, but for older systems we need to | |
| 81 // break out of any job we're in to enforce our restrictions. | |
| 82 flags |= CREATE_BREAKAWAY_FROM_JOB; | |
| 83 } | |
| 84 | |
| 85 if (!::CreateProcessAsUser(primary_token.Get(), | |
| 86 NULL, // No application name. | |
| 87 command_line, | |
| 88 NULL, // No security attribute. | |
| 89 NULL, // No thread attribute. | |
| 90 FALSE, // Do not inherit handles. | |
| 91 flags, | |
| 92 NULL, // Use the environment of the caller. | |
| 93 NULL, // Use current directory of the caller. | |
| 94 &startup_info, | |
| 95 &temp_process_info)) { | |
| 96 return ::GetLastError(); | |
| 97 } | |
| 98 base::win::ScopedProcessInformation process_info(temp_process_info); | |
| 99 | |
| 100 // Change the token of the main thread of the new process for the | |
| 101 // impersonation token with more rights. | |
| 102 { | |
| 103 HANDLE temp_thread = process_info.thread_handle(); | |
| 104 if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) { | |
| 105 auto last_error = ::GetLastError(); | |
| 106 ::TerminateProcess(process_info.process_handle(), | |
| 107 0); // exit code | |
| 108 return last_error; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 err_code = job.AssignProcessToJob(process_info.process_handle()); | |
| 113 if (ERROR_SUCCESS != err_code) { | |
| 114 auto last_error = ::GetLastError(); | |
| 115 ::TerminateProcess(process_info.process_handle(), | |
| 116 0); // exit code | |
| 117 return last_error; | |
| 118 } | |
| 119 | |
| 120 // Start the application | |
| 121 ::ResumeThread(process_info.thread_handle()); | |
| 122 | |
| 123 *job_handle = job.Take(); | |
| 124 | |
| 125 return ERROR_SUCCESS; | |
| 126 } | |
| 127 | |
| 128 } // namespace | |
| 129 | |
| 130 #define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) | |
| 131 | |
| 132 void PrintUsage(const wchar_t *application_name) { | |
| 133 wprintf(L"\n\nUsage: \n %ls --main level --init level --job level cmd_line ", | |
| 134 application_name); | |
| 135 wprintf(L"\n\n Levels : \n\tLOCKDOWN \n\tRESTRICTED " | |
| 136 L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); | |
| 137 wprintf(L"\n\n main: Security level of the main token"); | |
| 138 wprintf(L"\n init: Security level of the impersonation token"); | |
| 139 wprintf(L"\n job: Security level of the job object"); | |
| 140 } | |
| 141 | |
| 142 bool GetTokenLevelFromString(const wchar_t *param, | |
| 143 sandbox::TokenLevel* level) { | |
| 144 if (_wcsicmp(param, L"LOCKDOWN") == 0) { | |
| 145 *level = sandbox::USER_LOCKDOWN; | |
| 146 } else if (_wcsicmp(param, L"RESTRICTED") == 0) { | |
| 147 *level = sandbox::USER_RESTRICTED; | |
| 148 } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { | |
| 149 *level = sandbox::USER_LIMITED; | |
| 150 } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { | |
| 151 *level = sandbox::USER_INTERACTIVE; | |
| 152 } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { | |
| 153 *level = sandbox::USER_NON_ADMIN; | |
| 154 } else if (_wcsicmp(param, L"USER_RESTRICTED_SAME_ACCESS") == 0) { | |
| 155 *level = sandbox::USER_RESTRICTED_SAME_ACCESS; | |
| 156 } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { | |
| 157 *level = sandbox::USER_UNPROTECTED; | |
| 158 } else { | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 return true; | |
| 163 } | |
| 164 | |
| 165 bool GetJobLevelFromString(const wchar_t *param, sandbox::JobLevel* level) { | |
| 166 if (_wcsicmp(param, L"LOCKDOWN") == 0) { | |
| 167 *level = sandbox::JOB_LOCKDOWN; | |
| 168 } else if (_wcsicmp(param, L"RESTRICTED") == 0) { | |
| 169 *level = sandbox::JOB_RESTRICTED; | |
| 170 } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { | |
| 171 *level = sandbox::JOB_LIMITED_USER; | |
| 172 } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { | |
| 173 *level = sandbox::JOB_INTERACTIVE; | |
| 174 } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { | |
| 175 wprintf(L"\nNON_ADMIN is not a supported job type"); | |
| 176 return false; | |
| 177 } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { | |
| 178 *level = sandbox::JOB_UNPROTECTED; | |
| 179 } else { | |
| 180 return false; | |
| 181 } | |
| 182 | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 186 int wmain(int argc, wchar_t *argv[]) { | |
| 187 // Extract the filename from the path. | |
| 188 wchar_t *app_name = wcsrchr(argv[0], L'\\'); | |
| 189 if (!app_name) { | |
| 190 app_name = argv[0]; | |
| 191 } else { | |
| 192 app_name++; | |
| 193 } | |
| 194 | |
| 195 // no argument | |
| 196 if (argc == 1) { | |
| 197 PrintUsage(app_name); | |
| 198 return -1; | |
| 199 } | |
| 200 | |
| 201 sandbox::TokenLevel primary_level = sandbox::USER_LOCKDOWN; | |
| 202 sandbox::TokenLevel impersonation_level = | |
| 203 sandbox::USER_RESTRICTED_SAME_ACCESS; | |
| 204 sandbox::JobLevel job_level = sandbox::JOB_LOCKDOWN; | |
| 205 ATL::CString command_line; | |
| 206 | |
| 207 // parse command line. | |
| 208 for (int i = 1; i < argc; ++i) { | |
| 209 if (PARAM_IS(L"--main")) { | |
| 210 i++; | |
| 211 if (argc > i) { | |
| 212 if (!GetTokenLevelFromString(argv[i], &primary_level)) { | |
| 213 wprintf(L"\nAbord, Unrecognized main token level \"%ls\"", argv[i]); | |
| 214 PrintUsage(app_name); | |
| 215 return -1; | |
| 216 } | |
| 217 } | |
| 218 } else if (PARAM_IS(L"--init")) { | |
| 219 i++; | |
| 220 if (argc > i) { | |
| 221 if (!GetTokenLevelFromString(argv[i], &impersonation_level)) { | |
| 222 wprintf(L"\nAbord, Unrecognized init token level \"%ls\"", argv[i]); | |
| 223 PrintUsage(app_name); | |
| 224 return -1; | |
| 225 } | |
| 226 } | |
| 227 } else if (PARAM_IS(L"--job")) { | |
| 228 i++; | |
| 229 if (argc > i) { | |
| 230 if (!GetJobLevelFromString(argv[i], &job_level)) { | |
| 231 wprintf(L"\nAbord, Unrecognized job security level \"%ls\"", argv[i]); | |
| 232 PrintUsage(app_name); | |
| 233 return -1; | |
| 234 } | |
| 235 } | |
| 236 } else { | |
| 237 if (command_line.GetLength()) { | |
| 238 command_line += L' '; | |
| 239 } | |
| 240 command_line += argv[i]; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 if (!command_line.GetLength()) { | |
| 245 wprintf(L"\nAbord, No command line specified"); | |
| 246 PrintUsage(app_name); | |
| 247 return -1; | |
| 248 } | |
| 249 | |
| 250 wprintf(L"\nLaunching command line: \"%ls\"\n", command_line.GetBuffer()); | |
| 251 | |
| 252 base::win::ScopedHandle job_handle; | |
| 253 DWORD err_code = StartRestrictedProcessInJob( | |
| 254 command_line.GetBuffer(), | |
| 255 primary_level, | |
| 256 impersonation_level, | |
| 257 job_level, | |
| 258 &job_handle); | |
| 259 if (ERROR_SUCCESS != err_code) { | |
| 260 wprintf(L"\nAbord, Error %d while launching command line.", err_code); | |
| 261 return -1; | |
| 262 } | |
| 263 | |
| 264 wprintf(L"\nPress any key to continue."); | |
| 265 while(!_kbhit()) { | |
| 266 Sleep(100); | |
| 267 } | |
| 268 | |
| 269 return 0; | |
| 270 } | |
| OLD | NEW |