1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sandbox/win/src/policy_low_level.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <map>
11 #include <string>
12 
13 namespace {
14 
15   // A single rule can use at most this amount of memory.
16   const size_t kRuleBufferSize = 1024*4;
17 
18   // The possible states of the string matching opcode generator.
19   enum {
20     PENDING_NONE,
21     PENDING_ASTERISK,    // Have seen an '*' but have not generated an opcode.
22     PENDING_QMARK,       // Have seen an '?' but have not generated an opcode.
23   };
24 
25   // The category of the last character seen by the string matching opcode
26   // generator.
27   const uint32_t kLastCharIsNone = 0;
28   const uint32_t kLastCharIsAlpha = 1;
29   const uint32_t kLastCharIsWild = 2;
30   const uint32_t kLastCharIsAsterisk = kLastCharIsWild + 4;
31   const uint32_t kLastCharIsQuestionM = kLastCharIsWild + 8;
32 }
33 
34 namespace sandbox {
35 
LowLevelPolicy(PolicyGlobal * policy_store)36 LowLevelPolicy::LowLevelPolicy(PolicyGlobal* policy_store)
37     : policy_store_(policy_store) {
38 }
39 
40 // Adding a rule is nothing more than pushing it into an stl container. Done()
41 // is called for the rule in case the code that made the rule in the first
42 // place has not done it.
AddRule(int service,PolicyRule * rule)43 bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) {
44   if (!rule->Done()) {
45     return false;
46   }
47 
48   PolicyRule* local_rule = new PolicyRule(*rule);
49   RuleNode node = {local_rule, service};
50   rules_.push_back(node);
51   return true;
52 }
53 
~LowLevelPolicy()54 LowLevelPolicy::~LowLevelPolicy() {
55   // Delete all the rules.
56   typedef std::list<RuleNode> RuleNodes;
57   for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
58     delete it->rule;
59   }
60 }
61 
62 // Here is where the heavy byte shuffling is done. We take all the rules and
63 // 'compile' them into a single memory region. Now, the rules are in random
64 // order so the first step is to reorganize them into a stl map that is keyed
65 // by the service id and as a value contains a list with all the rules that
66 // belong to that service. Then we enter the big for-loop where we carve a
67 // memory zone for the opcodes and the data and call RebindCopy on each rule
68 // so they all end up nicely packed in the policy_store_.
Done()69 bool LowLevelPolicy::Done() {
70   typedef std::list<RuleNode> RuleNodes;
71   typedef std::list<const PolicyRule*> RuleList;
72   typedef std::map<uint32_t, RuleList> Mmap;
73   Mmap mmap;
74 
75   for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
76     mmap[it->service].push_back(it->rule);
77   }
78 
79   PolicyBuffer* current_buffer = &policy_store_->data[0];
80   char* buffer_end = reinterpret_cast<char*>(current_buffer) +
81     policy_store_->data_size;
82   size_t avail_size =  policy_store_->data_size;
83 
84   for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) {
85     uint32_t service = (*it).first;
86     if (service >= kMaxServiceCount) {
87       return false;
88     }
89     policy_store_->entry[service] = current_buffer;
90 
91     RuleList::iterator rules_it = (*it).second.begin();
92     RuleList::iterator rules_it_end = (*it).second.end();
93 
94     size_t svc_opcode_count = 0;
95 
96     for (; rules_it != rules_it_end; ++rules_it) {
97       const PolicyRule* rule = (*rules_it);
98       size_t op_count = rule->GetOpcodeCount();
99 
100       size_t opcodes_size = op_count * sizeof(PolicyOpcode);
101       if (avail_size < opcodes_size) {
102         return false;
103       }
104       size_t data_size = avail_size - opcodes_size;
105       PolicyOpcode* opcodes_start = &current_buffer->opcodes[svc_opcode_count];
106       if (!rule->RebindCopy(opcodes_start, opcodes_size,
107                             buffer_end, &data_size)) {
108         return false;
109       }
110       size_t used = avail_size - data_size;
111       buffer_end -= used;
112       avail_size -= used;
113       svc_opcode_count += op_count;
114     }
115 
116     current_buffer->opcode_count += svc_opcode_count;
117     size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode))
118                                 / sizeof(current_buffer[0]);
119     current_buffer = &current_buffer[policy_byte_count + 1];
120   }
121 
122   return true;
123 }
124 
PolicyRule(EvalResult action)125 PolicyRule::PolicyRule(EvalResult action)
126     : action_(action), done_(false) {
127   char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize];
128   buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
129   buffer_->opcode_count = 0;
130   opcode_factory_ = new OpcodeFactory(buffer_,
131                                       kRuleBufferSize + sizeof(PolicyOpcode));
132 }
133 
PolicyRule(const PolicyRule & other)134 PolicyRule::PolicyRule(const PolicyRule& other) {
135   if (this == &other)
136     return;
137   action_ = other.action_;
138   done_ = other.done_;
139   size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize;
140   char* memory = new char[buffer_size];
141   buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
142   memcpy(buffer_, other.buffer_, buffer_size);
143 
144   char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]);
145   char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)];
146   opcode_factory_ =
147       new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size());
148 }
149 
150 // This function get called from a simple state machine implemented in
151 // AddStringMatch() which passes the current state (in state) and it passes
152 // true in last_call if AddStringMatch() has finished processing the input
153 // pattern string and this would be the last call to generate any pending
154 // opcode. The skip_count is the currently accumulated number of '?' seen so
155 // far and once the associated opcode is generated this function sets it back
156 // to zero.
GenStringOpcode(RuleType rule_type,StringMatchOptions match_opts,uint16_t parameter,int state,bool last_call,int * skip_count,base::string16 * fragment)157 bool PolicyRule::GenStringOpcode(RuleType rule_type,
158                                  StringMatchOptions match_opts,
159                                  uint16_t parameter,
160                                  int state,
161                                  bool last_call,
162                                  int* skip_count,
163                                  base::string16* fragment) {
164   // The last opcode must:
165   //   1) Always clear the context.
166   //   2) Preserve the negation.
167   //   3) Remove the 'OR' mode flag.
168   uint32_t options = kPolNone;
169   if (last_call) {
170     if (IF_NOT == rule_type) {
171       options = kPolClearContext | kPolNegateEval;
172     } else {
173       options = kPolClearContext;
174     }
175   } else if (IF_NOT == rule_type) {
176     options = kPolUseOREval | kPolNegateEval;
177   }
178 
179   PolicyOpcode* op = NULL;
180 
181   // The fragment string contains the accumulated characters to match with, it
182   // never contains wildcards (unless they have been escaped) and while there
183   // is no fragment there is no new string match opcode to generate.
184   if (fragment->empty()) {
185     // There is no new opcode to generate but in the last call we have to fix
186     // the previous opcode because it was really the last but we did not know
187     // it at that time.
188     if (last_call && (buffer_->opcode_count > 0)) {
189       op = &buffer_->opcodes[buffer_->opcode_count - 1];
190       op->SetOptions(options);
191     }
192     return true;
193   }
194 
195   if (PENDING_ASTERISK == state) {
196     if (last_call) {
197       op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
198                                                kSeekToEnd, match_opts,
199                                                options);
200     } else {
201       op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
202                                                kSeekForward, match_opts,
203                                                options);
204     }
205 
206   } else if (PENDING_QMARK == state) {
207     op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
208                                              *skip_count, match_opts, options);
209     *skip_count = 0;
210   } else {
211     if (last_call) {
212       match_opts = static_cast<StringMatchOptions>(EXACT_LENGHT | match_opts);
213     }
214     op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0,
215                                              match_opts, options);
216   }
217   if (NULL == op) {
218     return false;
219   }
220   ++buffer_->opcode_count;
221   fragment->clear();
222   return true;
223 }
224 
AddStringMatch(RuleType rule_type,int16_t parameter,const wchar_t * string,StringMatchOptions match_opts)225 bool PolicyRule::AddStringMatch(RuleType rule_type,
226                                 int16_t parameter,
227                                 const wchar_t* string,
228                                 StringMatchOptions match_opts) {
229   if (done_) {
230     // Do not allow to add more rules after generating the action opcode.
231     return false;
232   }
233 
234   const wchar_t* current_char = string;
235   uint32_t last_char = kLastCharIsNone;
236   int state = PENDING_NONE;
237   int skip_count = 0;       // counts how many '?' we have seen in a row.
238   base::string16 fragment;  // accumulates the non-wildcard part.
239 
240   while (L'\0' != *current_char) {
241     switch (*current_char) {
242       case L'*':
243         if (kLastCharIsWild & last_char) {
244           // '**' and '&*' is an error.
245           return false;
246         }
247         if (!GenStringOpcode(rule_type, match_opts, parameter,
248                              state, false, &skip_count, &fragment)) {
249           return false;
250         }
251         last_char = kLastCharIsAsterisk;
252         state = PENDING_ASTERISK;
253         break;
254       case L'?':
255         if (kLastCharIsAsterisk == last_char) {
256           // '*?' is an error.
257           return false;
258         }
259         if (!GenStringOpcode(rule_type, match_opts, parameter,
260                              state, false, &skip_count, &fragment)) {
261           return false;
262         }
263         ++skip_count;
264         last_char = kLastCharIsQuestionM;
265         state = PENDING_QMARK;
266         break;
267       case L'/':
268         // Note: "/?" is an escaped '?'. Eat the slash and fall through.
269         if (L'?' == current_char[1]) {
270           ++current_char;
271         }
272       default:
273         fragment += *current_char;
274         last_char = kLastCharIsAlpha;
275     }
276     ++current_char;
277   }
278 
279   if (!GenStringOpcode(rule_type, match_opts, parameter,
280                        state, true, &skip_count, &fragment)) {
281     return false;
282   }
283   return true;
284 }
285 
AddNumberMatch(RuleType rule_type,int16_t parameter,uint32_t number,RuleOp comparison_op)286 bool PolicyRule::AddNumberMatch(RuleType rule_type,
287                                 int16_t parameter,
288                                 uint32_t number,
289                                 RuleOp comparison_op) {
290   if (done_) {
291     // Do not allow to add more rules after generating the action opcode.
292     return false;
293   }
294   uint32_t opts = (rule_type == IF_NOT) ? kPolNegateEval : kPolNone;
295 
296   if (EQUAL == comparison_op) {
297     if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) {
298       return false;
299     }
300   } else if (AND == comparison_op) {
301     if (NULL == opcode_factory_->MakeOpNumberAndMatch(parameter, number,
302                                                       opts)) {
303       return false;
304     }
305   }
306   ++buffer_->opcode_count;
307   return true;
308 }
309 
Done()310 bool PolicyRule::Done() {
311   if (done_) {
312     return true;
313   }
314   if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) {
315     return false;
316   }
317   ++buffer_->opcode_count;
318   done_ = true;
319   return true;
320 }
321 
RebindCopy(PolicyOpcode * opcode_start,size_t opcode_size,char * data_start,size_t * data_size) const322 bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size,
323                             char* data_start, size_t* data_size) const {
324   size_t count = buffer_->opcode_count;
325   for (size_t ix = 0; ix != count; ++ix) {
326     if (opcode_size < sizeof(PolicyOpcode)) {
327       return false;
328     }
329     PolicyOpcode& opcode = buffer_->opcodes[ix];
330     *opcode_start = opcode;
331     if (OP_WSTRING_MATCH == opcode.GetID()) {
332       // For this opcode argument 0 is a delta to the string and argument 1
333       // is the length (in chars) of the string.
334       const wchar_t* str = opcode.GetRelativeString(0);
335       size_t str_len;
336       opcode.GetArgument(1, &str_len);
337       str_len = str_len * sizeof(wchar_t);
338       if ((*data_size) < str_len) {
339         return false;
340       }
341       *data_size -= str_len;
342       data_start -= str_len;
343       memcpy(data_start, str, str_len);
344       // Recompute the string displacement
345       ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start);
346       opcode_start->SetArgument(0, delta);
347     }
348     ++opcode_start;
349     opcode_size -= sizeof(PolicyOpcode);
350   }
351 
352   return true;
353 }
354 
~PolicyRule()355 PolicyRule::~PolicyRule() {
356   delete [] reinterpret_cast<char*>(buffer_);
357   delete opcode_factory_;
358 }
359 
360 }  // namespace sandbox
361