Home | History | Annotate | Download | only in src
      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 
     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 
     51 PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
     52   return MakeBase(OP_ALWAYS_FALSE, options, -1);
     53 }
     54 
     55 template <>
     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 
     69 PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
     70   return MakeBase(OP_ALWAYS_TRUE, options, -1);
     71 }
     72 
     73 template <>
     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 
     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 <>
     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 
    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 
    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 <>
    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 
    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 <>
    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 
    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 <>
    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 
    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<>
    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 
    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 
    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 
    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 
    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