// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" #include "base/win/windows_version.h" #include "sandbox/win/src/restricted_token_utils.h" // launcher.exe is an application used to launch another application with a // restricted token. This is to be used for testing only. // The parameters are the level of security of the primary token, the // impersonation token and the job object along with the command line to // execute. // See the usage (launcher.exe without parameters) for the correct format. namespace { // Starts the process described by the input parameter command_line in a job // with a restricted token. Also set the main thread of this newly created // process to impersonate a user with more rights so it can initialize // correctly. // // Parameters: primary_level is the security level of the primary token. // impersonation_level is the security level of the impersonation token used // to initialize the process. job_level is the security level of the job // object used to encapsulate the process. // // The output parameter job_handle is the handle to the job object. Closing this // handle will kill the process started. // // Note: The process started with this function has to call RevertToSelf() as // soon as possible to stop using the impersonation token and start being // secure. // // Note: The Unicode version of this function will fail if the command_line // parameter is a const string. DWORD StartRestrictedProcessInJob(wchar_t* command_line, TokenLevel primary_level, TokenLevel impersonation_level, JobLevel job_level, base::win::ScopedHandle* job_handle) { Job job; DWORD err_code = job.Init(job_level, NULL, 0, 0); if (ERROR_SUCCESS != err_code) return err_code; if (JOB_UNPROTECTED != job_level) { // Share the Desktop handle to be able to use MessageBox() in the sandboxed // application. err_code = job.UserHandleGrantAccess(GetDesktopWindow()); if (ERROR_SUCCESS != err_code) return err_code; } // Create the primary (restricted) token for the process base::win::ScopedHandle primary_token; err_code = sandbox::CreateRestrictedToken(primary_level, INTEGRITY_LEVEL_LAST, PRIMARY, &primary_token); if (ERROR_SUCCESS != err_code) return err_code; // Create the impersonation token (restricted) to be able to start the // process. base::win::ScopedHandle impersonation_token; err_code = sandbox::CreateRestrictedToken(impersonation_level, INTEGRITY_LEVEL_LAST, IMPERSONATION, &impersonation_token); if (ERROR_SUCCESS != err_code) return err_code; // Start the process STARTUPINFO startup_info = {0}; PROCESS_INFORMATION temp_process_info = {}; DWORD flags = CREATE_SUSPENDED; if (base::win::GetVersion() < base::win::VERSION_WIN8) { // Windows 8 implements nested jobs, but for older systems we need to // break out of any job we're in to enforce our restrictions. flags |= CREATE_BREAKAWAY_FROM_JOB; } if (!::CreateProcessAsUser(primary_token.Get(), NULL, // No application name. command_line, NULL, // No security attribute. NULL, // No thread attribute. FALSE, // Do not inherit handles. flags, NULL, // Use the environment of the caller. NULL, // Use current directory of the caller. &startup_info, &temp_process_info)) { return ::GetLastError(); } base::win::ScopedProcessInformation process_info(temp_process_info); // Change the token of the main thread of the new process for the // impersonation token with more rights. { HANDLE temp_thread = process_info.thread_handle(); if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) { auto last_error = ::GetLastError(); ::TerminateProcess(process_info.process_handle(), 0); // exit code return last_error; } } err_code = job.AssignProcessToJob(process_info.process_handle()); if (ERROR_SUCCESS != err_code) { auto last_error = ::GetLastError(); ::TerminateProcess(process_info.process_handle(), 0); // exit code return last_error; } // Start the application ::ResumeThread(process_info.thread_handle()); *job_handle = job.Take(); return ERROR_SUCCESS; } } // namespace #define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0) void PrintUsage(const wchar_t *application_name) { wprintf(L"\n\nUsage: \n %ls --main level --init level --job level cmd_line ", application_name); wprintf(L"\n\n Levels : \n\tLOCKDOWN \n\tRESTRICTED " L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED"); wprintf(L"\n\n main: Security level of the main token"); wprintf(L"\n init: Security level of the impersonation token"); wprintf(L"\n job: Security level of the job object"); } bool GetTokenLevelFromString(const wchar_t *param, sandbox::TokenLevel* level) { if (_wcsicmp(param, L"LOCKDOWN") == 0) { *level = sandbox::USER_LOCKDOWN; } else if (_wcsicmp(param, L"RESTRICTED") == 0) { *level = sandbox::USER_RESTRICTED; } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { *level = sandbox::USER_LIMITED; } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { *level = sandbox::USER_INTERACTIVE; } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { *level = sandbox::USER_NON_ADMIN; } else if (_wcsicmp(param, L"USER_RESTRICTED_SAME_ACCESS") == 0) { *level = sandbox::USER_RESTRICTED_SAME_ACCESS; } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { *level = sandbox::USER_UNPROTECTED; } else { return false; } return true; } bool GetJobLevelFromString(const wchar_t *param, sandbox::JobLevel* level) { if (_wcsicmp(param, L"LOCKDOWN") == 0) { *level = sandbox::JOB_LOCKDOWN; } else if (_wcsicmp(param, L"RESTRICTED") == 0) { *level = sandbox::JOB_RESTRICTED; } else if (_wcsicmp(param, L"LIMITED_USER") == 0) { *level = sandbox::JOB_LIMITED_USER; } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) { *level = sandbox::JOB_INTERACTIVE; } else if (_wcsicmp(param, L"NON_ADMIN") == 0) { wprintf(L"\nNON_ADMIN is not a supported job type"); return false; } else if (_wcsicmp(param, L"UNPROTECTED") == 0) { *level = sandbox::JOB_UNPROTECTED; } else { return false; } return true; } int wmain(int argc, wchar_t *argv[]) { // Extract the filename from the path. wchar_t *app_name = wcsrchr(argv[0], L'\\'); if (!app_name) { app_name = argv[0]; } else { app_name++; } // no argument if (argc == 1) { PrintUsage(app_name); return -1; } sandbox::TokenLevel primary_level = sandbox::USER_LOCKDOWN; sandbox::TokenLevel impersonation_level = sandbox::USER_RESTRICTED_SAME_ACCESS; sandbox::JobLevel job_level = sandbox::JOB_LOCKDOWN; ATL::CString command_line; // parse command line. for (int i = 1; i < argc; ++i) { if (PARAM_IS(L"--main")) { i++; if (argc > i) { if (!GetTokenLevelFromString(argv[i], &primary_level)) { wprintf(L"\nAbord, Unrecognized main token level \"%ls\"", argv[i]); PrintUsage(app_name); return -1; } } } else if (PARAM_IS(L"--init")) { i++; if (argc > i) { if (!GetTokenLevelFromString(argv[i], &impersonation_level)) { wprintf(L"\nAbord, Unrecognized init token level \"%ls\"", argv[i]); PrintUsage(app_name); return -1; } } } else if (PARAM_IS(L"--job")) { i++; if (argc > i) { if (!GetJobLevelFromString(argv[i], &job_level)) { wprintf(L"\nAbord, Unrecognized job security level \"%ls\"", argv[i]); PrintUsage(app_name); return -1; } } } else { if (command_line.GetLength()) { command_line += L' '; } command_line += argv[i]; } } if (!command_line.GetLength()) { wprintf(L"\nAbord, No command line specified"); PrintUsage(app_name); return -1; } wprintf(L"\nLaunching command line: \"%ls\"\n", command_line.GetBuffer()); base::win::ScopedHandle job_handle; DWORD err_code = StartRestrictedProcessInJob( command_line.GetBuffer(), primary_level, impersonation_level, job_level, &job_handle); if (ERROR_SUCCESS != err_code) { wprintf(L"\nAbord, Error %d while launching command line.", err_code); return -1; } wprintf(L"\nPress any key to continue."); while(!_kbhit()) { Sleep(100); } return 0; }