// 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 #include "sandbox/win/src/sync_policy.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/policy_engine_opcodes.h" #include "sandbox/win/src/policy_params.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/sandbox_utils.h" #include "sandbox/win/src/sync_interception.h" #include "sandbox/win/src/win_utils.h" namespace sandbox { // Provides functionality to resolve a symbolic link within the object // directory passed in. NTSTATUS ResolveSymbolicLink(const base::string16& directory_name, const base::string16& name, base::string16* target) { NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL; ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject); NtQuerySymbolicLinkObjectFunction NtQuerySymbolicLinkObject = NULL; ResolveNTFunctionPtr("NtQuerySymbolicLinkObject", &NtQuerySymbolicLinkObject); NtOpenSymbolicLinkObjectFunction NtOpenSymbolicLinkObject = NULL; ResolveNTFunctionPtr("NtOpenSymbolicLinkObject", &NtOpenSymbolicLinkObject); NtCloseFunction NtClose = NULL; ResolveNTFunctionPtr("NtClose", &NtClose); OBJECT_ATTRIBUTES symbolic_link_directory_attributes = {}; UNICODE_STRING symbolic_link_directory_string = {}; InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, NULL, &symbolic_link_directory_attributes, &symbolic_link_directory_string); HANDLE symbolic_link_directory = NULL; NTSTATUS status = NtOpenDirectoryObject(&symbolic_link_directory, DIRECTORY_QUERY, &symbolic_link_directory_attributes); if (status != STATUS_SUCCESS) { DLOG(ERROR) << "Failed to open symbolic link directory. Error: " << status; return status; } OBJECT_ATTRIBUTES symbolic_link_attributes = {}; UNICODE_STRING name_string = {}; InitObjectAttribs(name, OBJ_CASE_INSENSITIVE, symbolic_link_directory, &symbolic_link_attributes, &name_string); HANDLE symbolic_link = NULL; status = NtOpenSymbolicLinkObject(&symbolic_link, GENERIC_READ, &symbolic_link_attributes); NtClose(symbolic_link_directory); if (status != STATUS_SUCCESS) { DLOG(ERROR) << "Failed to open symbolic link Error: " << status; return status; } UNICODE_STRING target_path = {}; unsigned long target_length = 0; status = NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length); if (status != STATUS_BUFFER_TOO_SMALL) { NtClose(symbolic_link); DLOG(ERROR) << "Failed to get length for symbolic link target. Error: " << status; return status; } target_path.Buffer = new wchar_t[target_length + 1]; target_path.Length = 0; target_path.MaximumLength = target_length; status = NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length); if (status == STATUS_SUCCESS) { target->assign(target_path.Buffer, target_length); } else { DLOG(ERROR) << "Failed to resolve symbolic link. Error: " << status; } NtClose(symbolic_link); delete[] target_path.Buffer; return status; } NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) { static HANDLE base_named_objects_handle = NULL; if (base_named_objects_handle) { *directory = base_named_objects_handle; return STATUS_SUCCESS; } NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL; ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject); DWORD session_id = 0; ProcessIdToSessionId(::GetCurrentProcessId(), &session_id); base::string16 base_named_objects_path; NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS", base::StringPrintf(L"%d", session_id), &base_named_objects_path); if (status != STATUS_SUCCESS) { DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: " << status; return status; } UNICODE_STRING directory_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, NULL, &object_attributes, &directory_name); status = NtOpenDirectoryObject(&base_named_objects_handle, DIRECTORY_ALL_ACCESS, &object_attributes); if (status == STATUS_SUCCESS) *directory = base_named_objects_handle; return status; } bool SyncPolicy::GenerateRules(const wchar_t* name, TargetPolicy::Semantics semantics, LowLevelPolicy* policy) { base::string16 mod_name(name); if (mod_name.empty()) { return false; } if (TargetPolicy::EVENTS_ALLOW_ANY != semantics && TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { // Other flags are not valid for sync policy yet. NOTREACHED(); return false; } // Add the open rule. EvalResult result = ASK_BROKER; PolicyRule open(result); if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE)) return false; if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) { // We consider all flags that are not known to be readonly as potentially // used for write. DWORD allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL; DWORD restricted_flags = ~allowed_flags; open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND); } if (!policy->AddRule(IPC_OPENEVENT_TAG, &open)) return false; // If it's not a read only, add the create rule. if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) { PolicyRule create(result); if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) return false; if (!policy->AddRule(IPC_CREATEEVENT_TAG, &create)) return false; } return true; } NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result, const ClientInfo& client_info, const base::string16 &event_name, uint32 event_type, uint32 initial_state, HANDLE *handle) { NtCreateEventFunction NtCreateEvent = NULL; ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent); // The only action supported is ASK_BROKER which means create the requested // file as specified. if (ASK_BROKER != eval_result) return false; HANDLE object_directory = NULL; NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory); if (status != STATUS_SUCCESS) return status; UNICODE_STRING unicode_event_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, &object_attributes, &unicode_event_name); HANDLE local_handle = NULL; status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes, static_cast(event_type), initial_state); if (NULL == local_handle) return status; if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { return STATUS_ACCESS_DENIED; } return status; } NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result, const ClientInfo& client_info, const base::string16 &event_name, uint32 desired_access, HANDLE *handle) { NtOpenEventFunction NtOpenEvent = NULL; ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent); // The only action supported is ASK_BROKER which means create the requested // event as specified. if (ASK_BROKER != eval_result) return false; HANDLE object_directory = NULL; NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory); if (status != STATUS_SUCCESS) return status; UNICODE_STRING unicode_event_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, &object_attributes, &unicode_event_name); HANDLE local_handle = NULL; status = NtOpenEvent(&local_handle, desired_access, &object_attributes); if (NULL == local_handle) return status; if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { return STATUS_ACCESS_DENIED; } return status; } } // namespace sandbox