1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/policy_low_level.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,348 @@ 1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include <string> 1.9 +#include <map> 1.10 + 1.11 +#include "sandbox/win/src/policy_low_level.h" 1.12 +#include "base/basictypes.h" 1.13 + 1.14 +namespace { 1.15 + 1.16 + // A single rule can use at most this amount of memory. 1.17 + const size_t kRuleBufferSize = 1024*4; 1.18 + 1.19 + // The possible states of the string matching opcode generator. 1.20 + enum { 1.21 + PENDING_NONE, 1.22 + PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode. 1.23 + PENDING_QMARK, // Have seen an '?' but have not generated an opcode. 1.24 + }; 1.25 + 1.26 + // The category of the last character seen by the string matching opcode 1.27 + // generator. 1.28 + const uint32 kLastCharIsNone = 0; 1.29 + const uint32 kLastCharIsAlpha = 1; 1.30 + const uint32 kLastCharIsWild = 2; 1.31 + const uint32 kLastCharIsAsterisk = kLastCharIsWild + 4; 1.32 + const uint32 kLastCharIsQuestionM = kLastCharIsWild + 8; 1.33 +} 1.34 + 1.35 +namespace sandbox { 1.36 + 1.37 +// Adding a rule is nothing more than pushing it into an stl container. Done() 1.38 +// is called for the rule in case the code that made the rule in the first 1.39 +// place has not done it. 1.40 +bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) { 1.41 + if (!rule->Done()) { 1.42 + return false; 1.43 + } 1.44 + 1.45 + PolicyRule* local_rule = new PolicyRule(*rule); 1.46 + RuleNode node = {local_rule, service}; 1.47 + rules_.push_back(node); 1.48 + return true; 1.49 +} 1.50 + 1.51 +LowLevelPolicy::~LowLevelPolicy() { 1.52 + // Delete all the rules. 1.53 + typedef std::list<RuleNode> RuleNodes; 1.54 + for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { 1.55 + delete it->rule; 1.56 + } 1.57 +} 1.58 + 1.59 +// Here is where the heavy byte shuffling is done. We take all the rules and 1.60 +// 'compile' them into a single memory region. Now, the rules are in random 1.61 +// order so the first step is to reorganize them into a stl map that is keyed 1.62 +// by the service id and as a value contains a list with all the rules that 1.63 +// belong to that service. Then we enter the big for-loop where we carve a 1.64 +// memory zone for the opcodes and the data and call RebindCopy on each rule 1.65 +// so they all end up nicely packed in the policy_store_. 1.66 +bool LowLevelPolicy::Done() { 1.67 + typedef std::list<RuleNode> RuleNodes; 1.68 + typedef std::list<const PolicyRule*> RuleList; 1.69 + typedef std::map<uint32, RuleList> Mmap; 1.70 + Mmap mmap; 1.71 + 1.72 + for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { 1.73 + mmap[it->service].push_back(it->rule); 1.74 + } 1.75 + 1.76 + PolicyBuffer* current_buffer = &policy_store_->data[0]; 1.77 + char* buffer_end = reinterpret_cast<char*>(current_buffer) + 1.78 + policy_store_->data_size; 1.79 + size_t avail_size = policy_store_->data_size; 1.80 + 1.81 + for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) { 1.82 + uint32 service = (*it).first; 1.83 + if (service >= kMaxServiceCount) { 1.84 + return false; 1.85 + } 1.86 + policy_store_->entry[service] = current_buffer; 1.87 + 1.88 + RuleList::iterator rules_it = (*it).second.begin(); 1.89 + RuleList::iterator rules_it_end = (*it).second.end(); 1.90 + 1.91 + size_t svc_opcode_count = 0; 1.92 + 1.93 + for (; rules_it != rules_it_end; ++rules_it) { 1.94 + const PolicyRule* rule = (*rules_it); 1.95 + size_t op_count = rule->GetOpcodeCount(); 1.96 + 1.97 + size_t opcodes_size = op_count * sizeof(PolicyOpcode); 1.98 + if (avail_size < opcodes_size) { 1.99 + return false; 1.100 + } 1.101 + size_t data_size = avail_size - opcodes_size; 1.102 + PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; 1.103 + if (!rule->RebindCopy(opcodes_start, opcodes_size, 1.104 + buffer_end, &data_size)) { 1.105 + return false; 1.106 + } 1.107 + size_t used = avail_size - data_size; 1.108 + buffer_end -= used; 1.109 + avail_size -= used; 1.110 + svc_opcode_count += op_count; 1.111 + } 1.112 + 1.113 + current_buffer->opcode_count += svc_opcode_count; 1.114 + size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode)) 1.115 + / sizeof(current_buffer[0]); 1.116 + current_buffer = ¤t_buffer[policy_byte_count + 1]; 1.117 + } 1.118 + 1.119 + return true; 1.120 +} 1.121 + 1.122 +PolicyRule::PolicyRule(EvalResult action) 1.123 + : action_(action), done_(false) { 1.124 + char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize]; 1.125 + buffer_ = reinterpret_cast<PolicyBuffer*>(memory); 1.126 + buffer_->opcode_count = 0; 1.127 + opcode_factory_ = new OpcodeFactory(buffer_, 1.128 + kRuleBufferSize + sizeof(PolicyOpcode)); 1.129 +} 1.130 + 1.131 +PolicyRule::PolicyRule(const PolicyRule& other) { 1.132 + if (this == &other) 1.133 + return; 1.134 + action_ = other.action_; 1.135 + done_ = other.done_; 1.136 + size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize; 1.137 + char* memory = new char[buffer_size]; 1.138 + buffer_ = reinterpret_cast<PolicyBuffer*>(memory); 1.139 + memcpy(buffer_, other.buffer_, buffer_size); 1.140 + 1.141 + char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]); 1.142 + char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)]; 1.143 + opcode_factory_ = 1.144 + new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size()); 1.145 +} 1.146 + 1.147 +// This function get called from a simple state machine implemented in 1.148 +// AddStringMatch() which passes the current state (in state) and it passes 1.149 +// true in last_call if AddStringMatch() has finished processing the input 1.150 +// pattern string and this would be the last call to generate any pending 1.151 +// opcode. The skip_count is the currently accumulated number of '?' seen so 1.152 +// far and once the associated opcode is generated this function sets it back 1.153 +// to zero. 1.154 +bool PolicyRule::GenStringOpcode(RuleType rule_type, 1.155 + StringMatchOptions match_opts, 1.156 + uint16 parameter, int state, bool last_call, 1.157 + int* skip_count, std::wstring* fragment) { 1.158 + 1.159 + // The last opcode must: 1.160 + // 1) Always clear the context. 1.161 + // 2) Preserve the negation. 1.162 + // 3) Remove the 'OR' mode flag. 1.163 + uint32 options = kPolNone; 1.164 + if (last_call) { 1.165 + if (IF_NOT == rule_type) { 1.166 + options = kPolClearContext | kPolNegateEval; 1.167 + } else { 1.168 + options = kPolClearContext; 1.169 + } 1.170 + } else if (IF_NOT == rule_type) { 1.171 + options = kPolUseOREval | kPolNegateEval; 1.172 + } 1.173 + 1.174 + PolicyOpcode* op = NULL; 1.175 + 1.176 + // The fragment string contains the accumulated characters to match with, it 1.177 + // never contains wildcards (unless they have been escaped) and while there 1.178 + // is no fragment there is no new string match opcode to generate. 1.179 + if (fragment->empty()) { 1.180 + // There is no new opcode to generate but in the last call we have to fix 1.181 + // the previous opcode because it was really the last but we did not know 1.182 + // it at that time. 1.183 + if (last_call && (buffer_->opcode_count > 0)) { 1.184 + op = &buffer_->opcodes[buffer_->opcode_count - 1]; 1.185 + op->SetOptions(options); 1.186 + } 1.187 + return true; 1.188 + } 1.189 + 1.190 + if (PENDING_ASTERISK == state) { 1.191 + if (last_call) { 1.192 + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 1.193 + kSeekToEnd, match_opts, 1.194 + options); 1.195 + } else { 1.196 + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 1.197 + kSeekForward, match_opts, 1.198 + options); 1.199 + } 1.200 + 1.201 + } else if (PENDING_QMARK == state) { 1.202 + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 1.203 + *skip_count, match_opts, options); 1.204 + *skip_count = 0; 1.205 + } else { 1.206 + if (last_call) { 1.207 + match_opts = static_cast<StringMatchOptions>(EXACT_LENGHT | match_opts); 1.208 + } 1.209 + op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0, 1.210 + match_opts, options); 1.211 + } 1.212 + if (NULL == op) { 1.213 + return false; 1.214 + } 1.215 + ++buffer_->opcode_count; 1.216 + fragment->clear(); 1.217 + return true; 1.218 +} 1.219 + 1.220 +bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, 1.221 + const wchar_t* string, 1.222 + StringMatchOptions match_opts) { 1.223 + if (done_) { 1.224 + // Do not allow to add more rules after generating the action opcode. 1.225 + return false; 1.226 + } 1.227 + 1.228 + const wchar_t* current_char = string; 1.229 + uint32 last_char = kLastCharIsNone; 1.230 + int state = PENDING_NONE; 1.231 + int skip_count = 0; // counts how many '?' we have seen in a row. 1.232 + std::wstring fragment; // accumulates the non-wildcard part of the string. 1.233 + 1.234 + while (L'\0' != *current_char) { 1.235 + switch (*current_char) { 1.236 + case L'*': 1.237 + if (kLastCharIsWild & last_char) { 1.238 + // '**' and '&*' is an error. 1.239 + return false; 1.240 + } 1.241 + if (!GenStringOpcode(rule_type, match_opts, parameter, 1.242 + state, false, &skip_count, &fragment)) { 1.243 + return false; 1.244 + } 1.245 + last_char = kLastCharIsAsterisk; 1.246 + state = PENDING_ASTERISK; 1.247 + break; 1.248 + case L'?': 1.249 + if (kLastCharIsAsterisk == last_char) { 1.250 + // '*?' is an error. 1.251 + return false; 1.252 + } 1.253 + if (!GenStringOpcode(rule_type, match_opts, parameter, 1.254 + state, false, &skip_count, &fragment)) { 1.255 + return false; 1.256 + } 1.257 + ++skip_count; 1.258 + last_char = kLastCharIsQuestionM; 1.259 + state = PENDING_QMARK; 1.260 + break; 1.261 + case L'/': 1.262 + // Note: "/?" is an escaped '?'. Eat the slash and fall through. 1.263 + if (L'?' == current_char[1]) { 1.264 + ++current_char; 1.265 + } 1.266 + default: 1.267 + fragment += *current_char; 1.268 + last_char = kLastCharIsAlpha; 1.269 + } 1.270 + ++current_char; 1.271 + } 1.272 + 1.273 + if (!GenStringOpcode(rule_type, match_opts, parameter, 1.274 + state, true, &skip_count, &fragment)) { 1.275 + return false; 1.276 + } 1.277 + return true; 1.278 +} 1.279 + 1.280 +bool PolicyRule::AddNumberMatch(RuleType rule_type, int16 parameter, 1.281 + unsigned long number, RuleOp comparison_op) { 1.282 + if (done_) { 1.283 + // Do not allow to add more rules after generating the action opcode. 1.284 + return false; 1.285 + } 1.286 + uint32 opts = (rule_type == IF_NOT)? kPolNegateEval : kPolNone; 1.287 + 1.288 + if (EQUAL == comparison_op) { 1.289 + if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) { 1.290 + return false; 1.291 + } 1.292 + } else if (AND == comparison_op) { 1.293 + if (NULL == opcode_factory_->MakeOpUlongAndMatch(parameter, number, opts)) { 1.294 + return false; 1.295 + } 1.296 + } 1.297 + ++buffer_->opcode_count; 1.298 + return true; 1.299 +} 1.300 + 1.301 +bool PolicyRule::Done() { 1.302 + if (done_) { 1.303 + return true; 1.304 + } 1.305 + if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) { 1.306 + return false; 1.307 + } 1.308 + ++buffer_->opcode_count; 1.309 + done_ = true; 1.310 + return true; 1.311 +} 1.312 + 1.313 +bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, 1.314 + char* data_start, size_t* data_size) const { 1.315 + size_t count = buffer_->opcode_count; 1.316 + for (size_t ix = 0; ix != count; ++ix) { 1.317 + if (opcode_size < sizeof(PolicyOpcode)) { 1.318 + return false; 1.319 + } 1.320 + PolicyOpcode& opcode = buffer_->opcodes[ix]; 1.321 + *opcode_start = opcode; 1.322 + if (OP_WSTRING_MATCH == opcode.GetID()) { 1.323 + // For this opcode argument 0 is a delta to the string and argument 1 1.324 + // is the length (in chars) of the string. 1.325 + const wchar_t* str = opcode.GetRelativeString(0); 1.326 + size_t str_len; 1.327 + opcode.GetArgument(1, &str_len); 1.328 + str_len = str_len * sizeof(wchar_t); 1.329 + if ((*data_size) < str_len) { 1.330 + return false; 1.331 + } 1.332 + *data_size -= str_len; 1.333 + data_start -= str_len; 1.334 + memcpy(data_start, str, str_len); 1.335 + // Recompute the string displacement 1.336 + ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start); 1.337 + opcode_start->SetArgument(0, delta); 1.338 + } 1.339 + ++opcode_start; 1.340 + opcode_size -= sizeof(PolicyOpcode); 1.341 + } 1.342 + 1.343 + return true; 1.344 +} 1.345 + 1.346 +PolicyRule::~PolicyRule() { 1.347 + delete [] reinterpret_cast<char*>(buffer_); 1.348 + delete opcode_factory_; 1.349 +} 1.350 + 1.351 +} // namespace sandbox