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
11namespace {
12const unsigned short kMaxUniStrSize = 0xfffc;
13
14bool 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
28namespace sandbox {
29
30SANDBOX_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
43template <int>
44EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp,
45                      MatchContext* match);
46
47//////////////////////////////////////////////////////////////////////////////
48// Opcode OpAlwaysFalse:
49// Does not require input parameter.
50
51PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
52  return MakeBase(OP_ALWAYS_FALSE, options, -1);
53}
54
55template <>
56EvalResult 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
69PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
70  return MakeBase(OP_ALWAYS_TRUE, options, -1);
71}
72
73template <>
74EvalResult 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
88PolicyOpcode* 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
96template <>
97EvalResult 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
113PolicyOpcode* 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
123PolicyOpcode* 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
133template <>
134EvalResult 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
160PolicyOpcode* 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
175template <>
176EvalResult 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
196PolicyOpcode* 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
205template <>
206EvalResult 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
227PolicyOpcode* 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
256template<>
257EvalResult 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
352PolicyOpcode* 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
370ptrdiff_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
396EvalResult 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
437EvalResult 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