diff options
Diffstat (limited to 'sandbox/src/restricted_token_utils.cc')
-rw-r--r-- | sandbox/src/restricted_token_utils.cc | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/sandbox/src/restricted_token_utils.cc b/sandbox/src/restricted_token_utils.cc new file mode 100644 index 0000000..f92cd7b --- /dev/null +++ b/sandbox/src/restricted_token_utils.cc @@ -0,0 +1,370 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <aclapi.h> +#include <sddl.h> +#include <vector> + +#include "sandbox/src/restricted_token_utils.h" + +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "base/win_util.h" +#include "sandbox/src/job.h" +#include "sandbox/src/restricted_token.h" +#include "sandbox/src/security_level.h" +#include "sandbox/src/sid.h" + +namespace sandbox { + +DWORD CreateRestrictedToken(HANDLE *token_handle, + TokenLevel security_level, + IntegrityLevel integrity_level, + TokenType token_type) { + if (!token_handle) + return ERROR_BAD_ARGUMENTS; + + RestrictedToken restricted_token; + restricted_token.Init(NULL); // Initialized with the current process token + + std::vector<std::wstring> privilege_exceptions; + std::vector<Sid> sid_exceptions; + + bool deny_sids = true; + bool remove_privileges = true; + + switch (security_level) { + case USER_UNPROTECTED: { + deny_sids = false; + remove_privileges = false; + break; + } + case USER_RESTRICTED_SAME_ACCESS: { + deny_sids = false; + remove_privileges = false; + + unsigned err_code = restricted_token.AddRestrictingSidAllSids(); + if (ERROR_SUCCESS != err_code) + return err_code; + + break; + } + case USER_NON_ADMIN: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + sid_exceptions.push_back(WinAuthenticatedUserSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + break; + } + case USER_INTERACTIVE: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + sid_exceptions.push_back(WinAuthenticatedUserSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + restricted_token.AddRestrictingSidCurrentUser(); + restricted_token.AddRestrictingSidLogonSession(); + break; + } + case USER_LIMITED: { + sid_exceptions.push_back(WinBuiltinUsersSid); + sid_exceptions.push_back(WinWorldSid); + sid_exceptions.push_back(WinInteractiveSid); + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + + // This token has to be able to create objects in BNO. + // Unfortunately, on vista, it needs the current logon sid + // in the token to achieve this. You should also set the process to be + // low integrity level so it can't access object created by other + // processes. + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { + restricted_token.AddRestrictingSidLogonSession(); + } + break; + } + case USER_RESTRICTED: { + privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); + restricted_token.AddUserSidForDenyOnly(); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + break; + } + case USER_LOCKDOWN: { + restricted_token.AddUserSidForDenyOnly(); + restricted_token.AddRestrictingSid(WinNullSid); + break; + } + default: { + return ERROR_BAD_ARGUMENTS; + } + } + + DWORD err_code = ERROR_SUCCESS; + if (deny_sids) { + err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); + if (ERROR_SUCCESS != err_code) + return err_code; + } + + if (remove_privileges) { + err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions); + if (ERROR_SUCCESS != err_code) + return err_code; + } + + restricted_token.SetIntegrityLevel(integrity_level); + + switch (token_type) { + case PRIMARY: { + err_code = restricted_token.GetRestrictedTokenHandle(token_handle); + break; + } + case IMPERSONATION: { + err_code = restricted_token.GetRestrictedTokenHandleForImpersonation( + token_handle); + break; + } + default: { + err_code = ERROR_BAD_ARGUMENTS; + break; + } + } + + return err_code; +} + +DWORD StartRestrictedProcessInJob(wchar_t *command_line, + TokenLevel primary_level, + TokenLevel impersonation_level, + JobLevel job_level, + HANDLE *const job_handle_ret) { + Job job; + DWORD err_code = job.Init(job_level, NULL, 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 + HANDLE primary_token_handle = NULL; + err_code = CreateRestrictedToken(&primary_token_handle, + primary_level, + INTEGRITY_LEVEL_LAST, + PRIMARY); + if (ERROR_SUCCESS != err_code) { + return err_code; + } + ScopedHandle primary_token(primary_token_handle); + + // Create the impersonation token (restricted) to be able to start the + // process. + HANDLE impersonation_token_handle; + err_code = CreateRestrictedToken(&impersonation_token_handle, + impersonation_level, + INTEGRITY_LEVEL_LAST, + IMPERSONATION); + if (ERROR_SUCCESS != err_code) { + return err_code; + } + ScopedHandle impersonation_token(impersonation_token_handle); + + // Start the process + STARTUPINFO startup_info = {0}; + PROCESS_INFORMATION process_info = {0}; + + if (!::CreateProcessAsUser(primary_token.Get(), + NULL, // No application name. + command_line, + NULL, // No security attribute. + NULL, // No thread attribute. + FALSE, // Do not inherit handles. + CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, + NULL, // Use the environment of the caller. + NULL, // Use current directory of the caller. + &startup_info, + &process_info)) { + return ::GetLastError(); + } + + ScopedHandle thread_handle(process_info.hThread); + ScopedHandle process_handle(process_info.hProcess); + + // Change the token of the main thread of the new process for the + // impersonation token with more rights. + if (!::SetThreadToken(&process_info.hThread, impersonation_token.Get())) { + ::TerminateProcess(process_handle.Get(), + 0); // exit code + return ::GetLastError(); + } + + err_code = job.AssignProcessToJob(process_handle.Get()); + if (ERROR_SUCCESS != err_code) { + ::TerminateProcess(process_handle.Get(), + 0); // exit code + return ::GetLastError(); + } + + // Start the application + ::ResumeThread(thread_handle.Get()); + + (*job_handle_ret) = job.Detach(); + + return ERROR_SUCCESS; +} + +DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, + const wchar_t* ace_access, + const wchar_t* integrity_level_sid) { + // Build the SDDL string for the label. + std::wstring sddl = L"S:("; // SDDL for a SACL. + sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label". + sddl += L";;"; // No Ace Flags. + sddl += ace_access; // Add the ACE access. + sddl += L";;;"; // No ObjectType and Inherited Object Type. + sddl += integrity_level_sid; // Trustee Sid. + sddl += L")"; + + DWORD error = ERROR_SUCCESS; + PSECURITY_DESCRIPTOR sec_desc = NULL; + + PACL sacl = NULL; + BOOL sacl_present = FALSE; + BOOL sacl_defaulted = FALSE; + + if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), + SDDL_REVISION, + &sec_desc, NULL)) { + if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, + &sacl_defaulted)) { + error = ::SetSecurityInfo(handle, type, + LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, + sacl); + } else { + error = ::GetLastError(); + } + + ::LocalFree(sec_desc); + } else { + return::GetLastError(); + } + + return error; +} + +const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) { + switch (integrity_level) { + case INTEGRITY_LEVEL_SYSTEM: + return L"S-1-16-16384"; + case INTEGRITY_LEVEL_HIGH: + return L"S-1-16-12288"; + case INTEGRITY_LEVEL_MEDIUM: + return L"S-1-16-8192"; + case INTEGRITY_LEVEL_MEDIUM_LOW: + return L"S-1-16-6144"; + case INTEGRITY_LEVEL_LOW: + return L"S-1-16-4096"; + case INTEGRITY_LEVEL_BELOW_LOW: + return L"S-1-16-2048"; + case INTEGRITY_LEVEL_LAST: + return NULL; + } + + NOTREACHED(); + return NULL; +} +DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) { + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) + return ERROR_SUCCESS; + + const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level); + if (!integrity_level_str) { + // No mandatory level specified, we don't change it. + return ERROR_SUCCESS; + } + + PSID integrity_sid = NULL; + if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid)) + return ::GetLastError(); + + TOKEN_MANDATORY_LABEL label = {0}; + label.Label.Attributes = SE_GROUP_INTEGRITY; + label.Label.Sid = integrity_sid; + + DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid); + BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label, + size); + ::LocalFree(integrity_sid); + + return result ? ERROR_SUCCESS : ::GetLastError(); +} + +DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) + return ERROR_SUCCESS; + + const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level); + if (!integrity_level_str) { + // No mandatory level specified, we don't change it. + return ERROR_SUCCESS; + } + + // Before we can change the token, we need to change the security label on the + // process so it is still possible to open the process with the new token. + std::wstring ace_access = SDDL_NO_READ_UP; + ace_access += SDDL_NO_WRITE_UP; + DWORD error = SetObjectIntegrityLabel(::GetCurrentProcess(), SE_KERNEL_OBJECT, + ace_access.c_str(), + integrity_level_str); + if (ERROR_SUCCESS != error) + return error; + + HANDLE token_handle; + if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, + &token_handle)) + return ::GetLastError(); + + ScopedHandle token(token_handle); + + return SetTokenIntegrityLevel(token.Get(), integrity_level); +} + + +} // namespace sandbox |