security/sandbox/win/src/policy_low_level.cc

changeset 0
6474c204b198
     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 = &current_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 = &current_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

mercurial