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