• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_engine_opcodes.h"
6 
7 #include "base/basictypes.h"
8 #include "sandbox/win/src/sandbox_nt_types.h"
9 #include "sandbox/win/src/sandbox_types.h"
10 
11 namespace {
12 const unsigned short kMaxUniStrSize = 0xfffc;
13 
InitStringUnicode(const wchar_t * source,size_t length,UNICODE_STRING * ustring)14 bool InitStringUnicode(const wchar_t* source, size_t length,
15                        UNICODE_STRING* ustring) {
16   ustring->Buffer = const_cast<wchar_t*>(source);
17   ustring->Length = static_cast<USHORT>(length) * sizeof(wchar_t);
18   if (length > kMaxUniStrSize) {
19       return false;
20   }
21   ustring->MaximumLength = (NULL != source) ?
22                                 ustring->Length + sizeof(wchar_t) : 0;
23   return true;
24 }
25 
26 }  // namespace
27 
28 namespace sandbox {
29 
30 SANDBOX_INTERCEPT NtExports g_nt;
31 
32 // Note: The opcodes are implemented as functions (as opposed to classes derived
33 // from PolicyOpcode) because you should not add more member variables to the
34 // PolicyOpcode class since it would cause object slicing on the target. So to
35 // enforce that (instead of just trusting the developer) the opcodes became
36 // just functions.
37 //
38 // In the code that follows I have keep the evaluation function and the factory
39 // function together to stress the close relationship between both. For example,
40 // only the factory method and the evaluation function know the stored argument
41 // order and meaning.
42 
43 template <int>
44 EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp,
45                       MatchContext* match);
46 
47 //////////////////////////////////////////////////////////////////////////////
48 // Opcode OpAlwaysFalse:
49 // Does not require input parameter.
50 
MakeOpAlwaysFalse(uint32 options)51 PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
52   return MakeBase(OP_ALWAYS_FALSE, options, -1);
53 }
54 
55 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)56 EvalResult OpcodeEval<OP_ALWAYS_FALSE>(PolicyOpcode* opcode,
57                                        const ParameterSet* param,
58                                        MatchContext* context) {
59   UNREFERENCED_PARAMETER(opcode);
60   UNREFERENCED_PARAMETER(param);
61   UNREFERENCED_PARAMETER(context);
62   return EVAL_FALSE;
63 }
64 
65 //////////////////////////////////////////////////////////////////////////////
66 // Opcode OpAlwaysTrue:
67 // Does not require input parameter.
68 
MakeOpAlwaysTrue(uint32 options)69 PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
70   return MakeBase(OP_ALWAYS_TRUE, options, -1);
71 }
72 
73 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)74 EvalResult OpcodeEval<OP_ALWAYS_TRUE>(PolicyOpcode* opcode,
75                                       const ParameterSet* param,
76                                       MatchContext* context) {
77   UNREFERENCED_PARAMETER(opcode);
78   UNREFERENCED_PARAMETER(param);
79   UNREFERENCED_PARAMETER(context);
80   return EVAL_TRUE;
81 }
82 
83 //////////////////////////////////////////////////////////////////////////////
84 // Opcode OpAction:
85 // Does not require input parameter.
86 // Argument 0 contains the actual action to return.
87 
MakeOpAction(EvalResult action,uint32 options)88 PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action,
89                                           uint32 options) {
90   PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0);
91   if (NULL == opcode) return NULL;
92   opcode->SetArgument(0, action);
93   return opcode;
94 }
95 
96 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)97 EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode,
98                                  const ParameterSet* param,
99                                  MatchContext* context) {
100   UNREFERENCED_PARAMETER(param);
101   UNREFERENCED_PARAMETER(context);
102   int action = 0;
103   opcode->GetArgument(0, &action);
104   return static_cast<EvalResult>(action);
105 }
106 
107 //////////////////////////////////////////////////////////////////////////////
108 // Opcode OpNumberMatch:
109 // Requires a unsigned long or void* in selected_param
110 // Argument 0 is the stored number to match.
111 // Argument 1 is the C++ type of the 0th argument.
112 
MakeOpNumberMatch(int16 selected_param,unsigned long match,uint32 options)113 PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param,
114                                                unsigned long match,
115                                                uint32 options) {
116   PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
117   if (NULL == opcode) return NULL;
118   opcode->SetArgument(0, match);
119   opcode->SetArgument(1, ULONG_TYPE);
120   return opcode;
121 }
122 
MakeOpVoidPtrMatch(int16 selected_param,const void * match,uint32 options)123 PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param,
124                                                 const void* match,
125                                                 uint32 options) {
126   PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
127   if (NULL == opcode) return NULL;
128   opcode->SetArgument(0, match);
129   opcode->SetArgument(1, VOIDPTR_TYPE);
130   return opcode;
131 }
132 
133 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)134 EvalResult OpcodeEval<OP_NUMBER_MATCH>(PolicyOpcode* opcode,
135                                        const ParameterSet* param,
136                                        MatchContext* context) {
137   UNREFERENCED_PARAMETER(context);
138   unsigned long value_ulong = 0;
139   if (param->Get(&value_ulong)) {
140     unsigned long match_ulong = 0;
141     opcode->GetArgument(0, &match_ulong);
142     return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE;
143   } else {
144     const void* value_ptr = NULL;
145     if (param->Get(&value_ptr)) {
146       const void* match_ptr = NULL;
147       opcode->GetArgument(0, &match_ptr);
148       return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE;
149     }
150   }
151   return EVAL_ERROR;
152 }
153 
154 //////////////////////////////////////////////////////////////////////////////
155 // Opcode OpUlongMatchRange
156 // Requires a unsigned long in selected_param.
157 // Argument 0 is the stored lower bound to match.
158 // Argument 1 is the stored upper bound to match.
159 
MakeOpUlongMatchRange(int16 selected_param,unsigned long lower_bound,unsigned long upper_bound,uint32 options)160 PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param,
161                                                    unsigned long lower_bound,
162                                                    unsigned long upper_bound,
163                                                    uint32 options) {
164   if (lower_bound > upper_bound) {
165     return NULL;
166   }
167   PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options,
168                                   selected_param);
169   if (NULL == opcode) return NULL;
170   opcode->SetArgument(0, lower_bound);
171   opcode->SetArgument(1, upper_bound);
172   return opcode;
173 }
174 
175 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)176 EvalResult OpcodeEval<OP_ULONG_MATCH_RANGE>(PolicyOpcode* opcode,
177                                             const ParameterSet* param,
178                                             MatchContext* context) {
179   UNREFERENCED_PARAMETER(context);
180   unsigned long value = 0;
181   if (!param->Get(&value)) return EVAL_ERROR;
182 
183   unsigned long lower_bound = 0;
184   unsigned long upper_bound = 0;
185   opcode->GetArgument(0, &lower_bound);
186   opcode->GetArgument(1, &upper_bound);
187   return((lower_bound <= value) && (upper_bound >= value))?
188     EVAL_TRUE : EVAL_FALSE;
189 }
190 
191 //////////////////////////////////////////////////////////////////////////////
192 // Opcode OpUlongAndMatch:
193 // Requires a unsigned long in selected_param.
194 // Argument 0 is the stored number to match.
195 
MakeOpUlongAndMatch(int16 selected_param,unsigned long match,uint32 options)196 PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param,
197                                                  unsigned long match,
198                                                  uint32 options) {
199   PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param);
200   if (NULL == opcode) return NULL;
201   opcode->SetArgument(0, match);
202   return opcode;
203 }
204 
205 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)206 EvalResult OpcodeEval<OP_ULONG_AND_MATCH>(PolicyOpcode* opcode,
207                                           const ParameterSet* param,
208                                           MatchContext* context) {
209   UNREFERENCED_PARAMETER(context);
210   unsigned long value = 0;
211   if (!param->Get(&value)) return EVAL_ERROR;
212 
213   unsigned long number = 0;
214   opcode->GetArgument(0, &number);
215   return (number & value)? EVAL_TRUE : EVAL_FALSE;
216 }
217 
218 //////////////////////////////////////////////////////////////////////////////
219 // Opcode OpWStringMatch:
220 // Requires a wchar_t* in selected_param.
221 // Argument 0 is the byte displacement of the stored string.
222 // Argument 1 is the lenght in chars of the stored string.
223 // Argument 2 is the offset to apply on the input string. It has special values.
224 // as noted in the header file.
225 // Argument 3 is the string matching options.
226 
MakeOpWStringMatch(int16 selected_param,const wchar_t * match_str,int start_position,StringMatchOptions match_opts,uint32 options)227 PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param,
228                                                 const wchar_t* match_str,
229                                                 int start_position,
230                                                 StringMatchOptions match_opts,
231                                                 uint32 options) {
232   if (NULL == match_str) {
233     return NULL;
234   }
235   if ('\0' == match_str[0]) {
236     return NULL;
237   }
238 
239   int lenght = lstrlenW(match_str);
240 
241   PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param);
242   if (NULL == opcode) {
243     return NULL;
244   }
245   ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1);
246   if (0 == delta_str) {
247     return NULL;
248   }
249   opcode->SetArgument(0, delta_str);
250   opcode->SetArgument(1, lenght);
251   opcode->SetArgument(2, start_position);
252   opcode->SetArgument(3, match_opts);
253   return opcode;
254 }
255 
256 template<>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)257 EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode,
258                                         const ParameterSet* param,
259                                         MatchContext* context) {
260   if (NULL == context) {
261     return EVAL_ERROR;
262   }
263   const wchar_t* source_str = NULL;
264   if (!param->Get(&source_str)) return EVAL_ERROR;
265 
266   int start_position = 0;
267   int match_len = 0;
268   unsigned int match_opts = 0;
269   opcode->GetArgument(1, &match_len);
270   opcode->GetArgument(2, &start_position);
271   opcode->GetArgument(3, &match_opts);
272 
273   const wchar_t* match_str = opcode->GetRelativeString(0);
274   // Advance the source string to the last successfully evaluated position
275   // according to the match context.
276   source_str = &source_str[context->position];
277   int source_len  = static_cast<int>(g_nt.wcslen(source_str));
278 
279   if (0 == source_len) {
280     // If we reached the end of the source string there is nothing we can
281     // match against.
282     return EVAL_FALSE;
283   }
284   if (match_len > source_len) {
285     // There can't be a positive match when the target string is bigger than
286     // the source string
287     return EVAL_FALSE;
288   }
289 
290   BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE;
291 
292   // We have three cases, depending on the value of start_pos:
293   // Case 1. We skip N characters and compare once.
294   // Case 2: We skip to the end and compare once.
295   // Case 3: We match the first substring (if we find any).
296   if (start_position >= 0) {
297     if (kSeekToEnd == start_position) {
298         start_position = source_len - match_len;
299     } else if (match_opts & EXACT_LENGHT) {
300         // A sub-case of case 3 is when the EXACT_LENGHT flag is on
301         // the match needs to be not just substring but full match.
302         if ((match_len + start_position) != source_len) {
303           return EVAL_FALSE;
304         }
305     }
306 
307     // Advance start_pos characters. Warning! this does not consider
308     // utf16 encodings (surrogate pairs) or other Unicode 'features'.
309     source_str += start_position;
310 
311     // Since we skipped, lets reevaluate just the lengths again.
312     if ((match_len + start_position) > source_len) {
313       return EVAL_FALSE;
314     }
315 
316     UNICODE_STRING match_ustr;
317     InitStringUnicode(match_str, match_len, &match_ustr);
318     UNICODE_STRING source_ustr;
319     InitStringUnicode(source_str, match_len, &source_ustr);
320 
321     if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
322                                           case_sensitive)) {
323       // Match! update the match context.
324       context->position += start_position + match_len;
325       return EVAL_TRUE;
326     } else {
327       return EVAL_FALSE;
328     }
329   } else if (start_position < 0) {
330     UNICODE_STRING match_ustr;
331     InitStringUnicode(match_str, match_len, &match_ustr);
332     UNICODE_STRING source_ustr;
333     InitStringUnicode(source_str, match_len, &source_ustr);
334 
335     do {
336       if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
337                                             case_sensitive)) {
338         // Match! update the match context.
339         context->position += (source_ustr.Buffer - source_str) + match_len;
340         return EVAL_TRUE;
341       }
342       ++source_ustr.Buffer;
343       --source_len;
344     } while (source_len >= match_len);
345   }
346   return EVAL_FALSE;
347 }
348 
349 //////////////////////////////////////////////////////////////////////////////
350 // OpcodeMaker (other member functions).
351 
MakeBase(OpcodeID opcode_id,uint32 options,int16 selected_param)352 PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
353                                       uint32 options,
354                                       int16 selected_param) {
355   if (memory_size() < sizeof(PolicyOpcode)) {
356     return NULL;
357   }
358 
359   // Create opcode using placement-new on the buffer memory.
360   PolicyOpcode* opcode = new(memory_top_) PolicyOpcode();
361 
362   // Fill in the standard fields, that every opcode has.
363   memory_top_ += sizeof(PolicyOpcode);
364   opcode->opcode_id_ = opcode_id;
365   opcode->options_ = static_cast<int16>(options);
366   opcode->parameter_ = selected_param;
367   return opcode;
368 }
369 
AllocRelative(void * start,const wchar_t * str,size_t lenght)370 ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str,
371                                        size_t lenght) {
372   size_t bytes = lenght * sizeof(wchar_t);
373   if (memory_size() < bytes) {
374     return 0;
375   }
376   memory_bottom_ -= bytes;
377   if (reinterpret_cast<UINT_PTR>(memory_bottom_) & 1) {
378     // TODO(cpu) replace this for something better.
379     ::DebugBreak();
380   }
381   memcpy(memory_bottom_, str, bytes);
382   ptrdiff_t delta = memory_bottom_ - reinterpret_cast<char*>(start);
383   return delta;
384 }
385 
386 //////////////////////////////////////////////////////////////////////////////
387 // Opcode evaluation dispatchers.
388 
389 // This function is the one and only entry for evaluating any opcode. It is
390 // in charge of applying any relevant opcode options and calling EvaluateInner
391 // were the actual dispatch-by-id is made. It would seem at first glance that
392 // the dispatch should be done by virtual function (vtable) calls but you have
393 // to remember that the opcodes are made in the broker process and copied as
394 // raw memory to the target process.
395 
Evaluate(const ParameterSet * call_params,size_t param_count,MatchContext * match)396 EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params,
397                                   size_t param_count, MatchContext* match) {
398   if (NULL == call_params) {
399     return EVAL_ERROR;
400   }
401   const ParameterSet* selected_param = NULL;
402   if (parameter_ >= 0) {
403     if (static_cast<size_t>(parameter_) >= param_count) {
404       return EVAL_ERROR;
405     }
406     selected_param = &call_params[parameter_];
407   }
408   EvalResult result = EvaluateHelper(selected_param, match);
409 
410   // Apply the general options regardless of the particular type of opcode.
411   if (kPolNone == options_) {
412     return result;
413   }
414 
415   if (options_ & kPolNegateEval) {
416     if (EVAL_TRUE == result) {
417       result = EVAL_FALSE;
418     } else if (EVAL_FALSE == result) {
419       result = EVAL_TRUE;
420     } else if (EVAL_ERROR != result) {
421       result = EVAL_ERROR;
422     }
423   }
424   if (NULL != match) {
425     if (options_ & kPolClearContext) {
426       match->Clear();
427     }
428     if (options_ & kPolUseOREval) {
429       match->options = kPolUseOREval;
430     }
431   }
432   return result;
433 }
434 
435 #define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval<op>(x, y, z)
436 
EvaluateHelper(const ParameterSet * parameters,MatchContext * match)437 EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters,
438                                        MatchContext* match) {
439   switch (opcode_id_) {
440     OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match);
441     OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match);
442     OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match);
443     OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match);
444     OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match);
445     OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match);
446     OPCODE_EVAL(OP_ACTION, this, parameters, match);
447     default:
448       return EVAL_ERROR;
449   }
450 }
451 
452 #undef OPCODE_EVAL
453 
454 }  // namespace sandbox
455