summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc480
1 files changed, 480 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
new file mode 100644
index 0000000000..863dee8d69
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2012 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 "sandbox/win/src/restricted_token_utils.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+DWORD GetObjectSecurityDescriptor(HANDLE handle,
+ SECURITY_INFORMATION security_info,
+ std::vector<char>* security_desc_buffer,
+ PSECURITY_DESCRIPTOR* security_desc) {
+ DWORD last_error = 0;
+ DWORD length_needed = 0;
+
+ ::GetKernelObjectSecurity(handle, security_info, nullptr, 0, &length_needed);
+ last_error = ::GetLastError();
+ if (last_error != ERROR_INSUFFICIENT_BUFFER)
+ return last_error;
+
+ security_desc_buffer->resize(length_needed);
+ *security_desc =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(security_desc_buffer->data());
+
+ if (!::GetKernelObjectSecurity(handle, security_info, *security_desc,
+ length_needed, &length_needed)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace
+
+DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
+ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token) {
+ RestrictedToken restricted_token;
+ restricted_token.Init(effective_token);
+ if (lockdown_default_dacl)
+ restricted_token.SetLockdownDefaultDacl();
+ if (unique_restricted_sid) {
+ restricted_token.AddDefaultDaclSid(Sid(unique_restricted_sid), GRANT_ACCESS,
+ GENERIC_ALL);
+ restricted_token.AddDefaultDaclSid(Sid(WinCreatorOwnerRightsSid),
+ GRANT_ACCESS, READ_CONTROL);
+ }
+
+ 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;
+
+ if (use_restricting_sids) {
+ 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_RESTRICTED_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);
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinInteractiveSid);
+ restricted_token.AddRestrictingSid(WinAuthenticatedUserSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ 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);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ 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);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+
+ // 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.
+ restricted_token.AddRestrictingSidLogonSession();
+ } else {
+ restricted_token.AddUserSidForDenyOnly();
+ }
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinNullSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ 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.GetRestrictedToken(token);
+ break;
+ }
+ case IMPERSONATION: {
+ err_code = restricted_token.GetRestrictedTokenForImpersonation(token);
+ break;
+ }
+ default: {
+ err_code = ERROR_BAD_ARGUMENTS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+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 = nullptr;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ sddl.c_str(), SDDL_REVISION, &sec_desc, nullptr)) {
+ if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ error = ::SetSecurityInfo(handle, type, LABEL_SECURITY_INFORMATION,
+ nullptr, nullptr, nullptr, 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_UNTRUSTED:
+ return L"S-1-16-0";
+ case INTEGRITY_LEVEL_LAST:
+ return nullptr;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
+ 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 = nullptr;
+ if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
+ return ::GetLastError();
+
+ TOKEN_MANDATORY_LABEL label = {};
+ 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);
+ auto last_error = ::GetLastError();
+ ::LocalFree(integrity_sid);
+
+ return result ? ERROR_SUCCESS : last_error;
+}
+
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
+ // We don't check for an invalid level here because we'll just let it
+ // fail on the SetTokenIntegrityLevel call later on.
+ if (integrity_level == INTEGRITY_LEVEL_LAST) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return SetTokenIntegrityLevel(token.Get(), integrity_level);
+}
+
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token, LABEL_SECURITY_INFORMATION, &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ return ::GetLastError();
+ }
+
+ for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
+ PSYSTEM_MANDATORY_LABEL_ACE ace;
+
+ if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) &&
+ ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
+ ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP |
+ SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
+ break;
+ }
+ }
+
+ if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ security_desc))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD HardenProcessIntegrityLevelPolicy() {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return HardenTokenIntegrityLevelPolicy(token.Get());
+}
+
+DWORD CreateLowBoxToken(HANDLE base_token,
+ TokenType token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ PHANDLE saved_handles,
+ DWORD saved_handles_count,
+ base::win::ScopedHandle* token) {
+ NtCreateLowBoxToken CreateLowBoxToken = nullptr;
+ ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken);
+
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return ERROR_CALL_NOT_IMPLEMENTED;
+
+ if (token_type != PRIMARY && token_type != IMPERSONATION)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!token)
+ return ERROR_INVALID_PARAMETER;
+
+ base::win::ScopedHandle base_token_handle;
+ if (!base_token) {
+ HANDLE process_token = nullptr;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &process_token)) {
+ return ::GetLastError();
+ }
+ base_token_handle.Set(process_token);
+ base_token = process_token;
+ }
+ OBJECT_ATTRIBUTES obj_attr;
+ InitializeObjectAttributes(&obj_attr, nullptr, 0, nullptr, nullptr);
+ HANDLE token_lowbox = nullptr;
+
+ NTSTATUS status = CreateLowBoxToken(
+ &token_lowbox, base_token, TOKEN_ALL_ACCESS, &obj_attr,
+ security_capabilities->AppContainerSid,
+ security_capabilities->CapabilityCount,
+ security_capabilities->Capabilities, saved_handles_count,
+ saved_handles_count > 0 ? saved_handles : nullptr);
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+
+ base::win::ScopedHandle token_lowbox_handle(token_lowbox);
+ DCHECK(token_lowbox_handle.IsValid());
+
+ // Default from NtCreateLowBoxToken is a Primary token.
+ if (token_type == PRIMARY) {
+ *token = std::move(token_lowbox_handle);
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE dup_handle = nullptr;
+ if (!::DuplicateTokenEx(token_lowbox_handle.Get(), TOKEN_ALL_ACCESS, nullptr,
+ ::SecurityImpersonation, ::TokenImpersonation,
+ &dup_handle)) {
+ return ::GetLastError();
+ }
+
+ // Copy security descriptor from primary token as the new object will have
+ // the DACL from the current token's default DACL.
+ base::win::ScopedHandle token_for_sd(dup_handle);
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token_lowbox_handle.Get(), DACL_SECURITY_INFORMATION,
+ &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ if (!::SetKernelObjectSecurity(token_for_sd.Get(), DACL_SECURITY_INFORMATION,
+ security_desc)) {
+ return ::GetLastError();
+ }
+
+ *token = std::move(token_for_sd);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD CreateLowBoxObjectDirectory(PSID lowbox_sid,
+ bool open_directory,
+ base::win::ScopedHandle* directory) {
+ DWORD session_id = 0;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id))
+ return ::GetLastError();
+
+ LPWSTR sid_string = nullptr;
+ if (!::ConvertSidToStringSid(lowbox_sid, &sid_string))
+ return ::GetLastError();
+
+ std::unique_ptr<wchar_t, LocalFreeDeleter> sid_string_ptr(sid_string);
+ std::wstring directory_path = base::StringPrintf(
+ L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls", session_id, sid_string);
+
+ NtCreateDirectoryObjectFunction CreateObjectDirectory = nullptr;
+ ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory);
+
+ OBJECT_ATTRIBUTES obj_attr;
+ UNICODE_STRING obj_name;
+ DWORD attributes = OBJ_CASE_INSENSITIVE;
+ if (open_directory)
+ attributes |= OBJ_OPENIF;
+
+ sandbox::InitObjectAttribs(directory_path, attributes, nullptr, &obj_attr,
+ &obj_name, nullptr);
+
+ HANDLE handle = nullptr;
+ NTSTATUS status =
+ CreateObjectDirectory(&handle, DIRECTORY_ALL_ACCESS, &obj_attr);
+
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+ directory->Set(handle);
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox