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 |