// This class centralizes most of the knowledge related to file system policy classFileSystemPolicy { public: // Creates the required low-level policy rules to evaluate a high-level // policy rule for File IO, in particular open or create actions. // 'name' is the file or directory name. // 'semantics' is the desired semantics for the open or create. // 'policy' is the policy generator to which the rules are going to be added. // 使用low-level policy来evaluate一个high-level FILE IO policy rule staticboolGenerateRules(constwchar_t* name, TargetPolicy::Semantics semantics, LowLevelPolicy* policy);
// Add basic file system rules. // 基础的文件系统rule添加接口 staticboolSetInitialRules(LowLevelPolicy* policy);
// Performs the desired policy action on a create request with an // API that is compatible with the IPC-received parameters. // 'client_info' : the target process that is making the request. // 'eval_result' : The desired policy action to accomplish. // 'file' : The target file or directory. // 通过IPC收到的create request staticboolCreateFileAction(EvalResult eval_result, const ClientInfo& client_info, const base::string16& file, uint32_t attributes, uint32_t desired_access, uint32_t file_attributes, uint32_t share_access, uint32_t create_disposition, uint32_t create_options, HANDLE* handle, NTSTATUS* nt_status, ULONG_PTR* io_information);
// Performs the desired policy action on an open request with an // API that is compatible with the IPC-received parameters. // 'client_info' : the target process that is making the request. // 'eval_result' : The desired policy action to accomplish. // 'file' : The target file or directory. // IPC收到的open request staticboolOpenFileAction(EvalResult eval_result, const ClientInfo& client_info, const base::string16& file, uint32_t attributes, uint32_t desired_access, uint32_t share_access, uint32_t open_options, HANDLE* handle, NTSTATUS* nt_status, ULONG_PTR* io_information);
// Performs the desired policy action on a query request with an // API that is compatible with the IPC-received parameters. // IPC收到的query request staticboolQueryAttributesFileAction(EvalResult eval_result, const ClientInfo& client_info, const base::string16& file, uint32_t attributes, FILE_BASIC_INFORMATION* file_info, NTSTATUS* nt_status);
// Performs the desired policy action on a query request with an // API that is compatible with the IPC-received parameters. // IPC收到的query full request staticboolQueryFullAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, const base::string16& file, uint32_t attributes, FILE_NETWORK_OPEN_INFORMATION* file_info, NTSTATUS* nt_status);
// Performs the desired policy action on a set_info request with an // API that is compatible with the IPC-received parameters. // IPC收到的set_info request staticboolSetInformationFileAction(EvalResult eval_result, const ClientInfo& client_info, HANDLE target_file_handle, void* file_info, uint32_t length, uint32_t info_class, IO_STATUS_BLOCK* io_block, NTSTATUS* nt_status); };
FILES_ALLOW_ANY, // Allows open or create for any kind of access that // the file system supports. FILES_ALLOW_READONLY, // Allows open or create with read access only. FILES_ALLOW_QUERY, // Allows access to query the attributes of a file. FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics // only.
// 处理reparse(..),就是把当前路径'.',上一路径'..'这些先处理一下 // 内部调用的是ConvertToLongPath接IsReparsePoint,我们此前见过 if (!PreProcessName(&mod_name)) { // The path to be added might contain a reparse point. NOTREACHED(); returnfalse; }
// TODO(cpu) bug 32224: This prefix add is a hack because we don't have the // infrastructure to normalize names. In any case we need to escape the // question marks. // 对"\\Device\\"前缀的一个patch,具体原因得参考bug 32224,这里不是我们研究的主题,先pass if (_wcsnicmp(mod_name.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) { mod_name = FixNTPrefixForMatch(mod_name); name = mod_name.c_str(); }
// 生成的rule的action opcode是ASK_BROKER EvalResult result = ASK_BROKER;
// List of supported calls for the filesystem. constunsigned kCallNtCreateFile = 0x1; constunsigned kCallNtOpenFile = 0x2; constunsigned kCallNtQueryAttributesFile = 0x4; constunsigned kCallNtQueryFullAttributesFile = 0x8; constunsigned kCallNtSetInfoRename = 0x10;
//根据semantics处理分支 switch (semantics) { case TargetPolicy::FILES_ALLOW_DIR_ANY: { // 只能打开目录,这种情况就对open和create两套规则的OpenFile::OPTIONS parameter_ // 设置值为FILE_DIRECTORY_FILE的按位与匹配 open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); break; } case TargetPolicy::FILES_ALLOW_READONLY: { // We consider all flags that are not known to be readonly as potentially // used for write. // 只读方式打开,所以对OpenFile::ACCESS来说,allowed_flags以外的任何flag都不能设置 // 而OpenFile::DISPOSITION则必须得是FILE_OPEN DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE | GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; DWORD restricted_flags = ~allowed_flags; open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL); create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
// Read only access don't work for rename. // 移除Rename的rule,本次不设置 rule_to_add &= ~kCallNtSetInfoRename; break; } case TargetPolicy::FILES_ALLOW_QUERY: { // Here we don't want to add policy for the open or the create. // 允许对某个文件属性的query,本次open/create/rename也就无需设置了 rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile | kCallNtSetInfoRename); break; } case TargetPolicy::FILES_ALLOW_ANY: { break; } default: { NOTREACHED(); returnfalse; } }
// Right now we insert two rules, to be evaluated before any user supplied rule: // - go to the broker if the path doesn't look like the paths that we push on // the policy (namely \??\something). // - go to the broker if it looks like this is a short-name path. // // It is possible to add a rule to go to the broker in any case; it would look // something like: // rule = new PolicyRule(ASK_BROKER); // rule->AddNumberMatch(IF_NOT, FileName::BROKER, true, AND); // policy->AddRule(service, rule); boolFileSystemPolicy::SetInitialRules(LowLevelPolicy* policy){ // 两个ASK_BROKER的action rule PolicyRule format(ASK_BROKER); PolicyRule short_name(ASK_BROKER);
// To evaluate the policy we need to call back to the policy object. We // are just middlemen in the operation since is the FileSystemPolicy which // knows what to do. // EvalPolicy检查各种rules是否通过,这些rules此前已通过PolicyBase::AddRules设置好了 // 然后去找Policy模块的CreateFileAction来干实事,也就是前面分析的 // FileSystemPolicy::CreateFileAction EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEFILE_TAG, params.GetBase()); HANDLE handle; ULONG_PTR io_information = 0; NTSTATUS nt_status; // 在这内部,会判断result是否是ASK_BROKER,如果是说明匹配到了rule,那就执行API // 如果不是说明凉凉 if (!FileSystemPolicy::CreateFileAction( result, *ipc->client_info, *name, attributes, desired_access, file_attributes, share_access, create_disposition, create_options, &handle, &nt_status, &io_information)) { ipc->return_info.nt_status = STATUS_ACCESS_DENIED; returntrue; } // 把返回的结果给IPC调用的CrossCallReturn结构,用于指示target client端 // Return operation status on the IPC. ipc->return_info.extended[0].ulong_ptr = io_information; ipc->return_info.nt_status = nt_status; ipc->return_info.handle = handle; returntrue; }
EvalResult PolicyBase::EvalPolicy(int service, CountedParameterSetBase* params){ if (policy_) { // 找到PolicyGlobal中对应的entry[service]一项 if (!policy_->entry[service]) { // There is no policy for this particular service. This is not a big // deal. return DENY_ACCESS; } for (size_t i = 0; i < params->count; i++) { if (!params->parameters[i].IsValid()) { NOTREACHED(); return SIGNAL_ALARM; } } // 基于policy_->entry[service]这个PolicyBuffer建一个PolicyProcessor PolicyProcessor pol_evaluator(policy_->entry[service]); // 逐group进行evaluate PolicyResult result = pol_evaluator.Evaluate(kShortEval, params->parameters, params->count); // 如果匹配到了,就获取这一group的action if (POLICY_MATCH == result) { return pol_evaluator.GetAction(); } DCHECK(POLICY_ERROR != result); }
NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, PHANDLE file, ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status, PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing, ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length){ // Check if the process can open it first. // x86的第一个参数是original function地址,先试试能不能打开这个文件,如果不是STATUS_ACCESS_DENIED // 就直接调用返回status就行了,无需请求IPC NTSTATUS status = orig_CreateFile( file, desired_access, object_attributes, io_status, allocation_size, file_attributes, sharing, disposition, options, ea_buffer, ea_length); if (STATUS_ACCESS_DENIED != status) return status;
// 确保IPC已经可用 // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status;
wchar_t* name = nullptr; do { if (!ValidParameter(file, sizeof(HANDLE), WRITE)) break; if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE)) break;
// 获取SharedMemory IPC的全局内存空间指针g_shared_IPC_memory void* memory = GetGlobalIPCMemory(); if (!memory) break;
// 设置文件属性 uint32_t attributes = 0; NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, nullptr); if (!NT_SUCCESS(ret) || !name) break;
// 内部会获取承载PolicyBuffer的g_shared_policy_memory // 找到PolicyBuffer并架设一个PolicyProcessor来对CountedParameterSet<OpenFile>进行Evaluate // 如果匹配rule且action为ASK_BROKER,才继续发起IPC请求 if (!QueryBroker(IPC_NTCREATEFILE_TAG, params.GetBase())) break;
SharedMemIPCClient ipc(memory); CrossCallReturn answer = {0}; // The following call must match in the parameters with // FilesystemDispatcher::ProcessNtCreateFile. // 发起IPC请求,填充这些参数 ResultCode code = CrossCall(ipc, IPC_NTCREATEFILE_TAG, name, attributes, desired_access_uint32, file_attributes, sharing, disposition, options_uint32, &answer); if (SBOX_ALL_OK != code) break;
// Replace current target policy with one that forwards all interceptions to // broker. PolicyGlobal* policy = GenerateBlankPolicy(); PolicyGlobal* current_policy = (PolicyGlobal*)sandbox::GetGlobalPolicyMemory(); CopyPolicyToTarget(policy, policy->data_size + sizeof(PolicyGlobal), current_policy); ...