security/sandbox/win/src/policy_engine_opcodes.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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.
     5 #include "sandbox/win/src/policy_engine_opcodes.h"
     7 #include "base/basictypes.h"
     8 #include "sandbox/win/src/sandbox_nt_types.h"
     9 #include "sandbox/win/src/sandbox_types.h"
    11 namespace {
    12 const unsigned short kMaxUniStrSize = 0xfffc;
    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 }
    26 }  // namespace
    28 namespace sandbox {
    30 SANDBOX_INTERCEPT NtExports g_nt;
    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.
    43 template <int>
    44 EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp,
    45                       MatchContext* match);
    47 //////////////////////////////////////////////////////////////////////////////
    48 // Opcode OpAlwaysFalse:
    49 // Does not require input parameter.
    51 PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
    52   return MakeBase(OP_ALWAYS_FALSE, options, -1);
    53 }
    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 }
    65 //////////////////////////////////////////////////////////////////////////////
    66 // Opcode OpAlwaysTrue:
    67 // Does not require input parameter.
    69 PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
    70   return MakeBase(OP_ALWAYS_TRUE, options, -1);
    71 }
    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 }
    83 //////////////////////////////////////////////////////////////////////////////
    84 // Opcode OpAction:
    85 // Does not require input parameter.
    86 // Argument 0 contains the actual action to return.
    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 }
    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 }
   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.
   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 }
   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 }
   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 }
   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.
   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 false;
   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 }
   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;
   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 }
   191 //////////////////////////////////////////////////////////////////////////////
   192 // Opcode OpUlongAndMatch:
   193 // Requires a unsigned long in selected_param.
   194 // Argument 0 is the stored number to match.
   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 }
   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;
   213   unsigned long number = 0;
   214   opcode->GetArgument(0, &number);
   215   return (number & value)? EVAL_TRUE : EVAL_FALSE;
   216 }
   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.
   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   }
   239   int lenght = lstrlenW(match_str);
   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 }
   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;
   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);
   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));
   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   }
   290   BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE;
   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     }
   307     // Advance start_pos characters. Warning! this does not consider
   308     // utf16 encodings (surrogate pairs) or other Unicode 'features'.
   309     source_str += start_position;
   311     // Since we skipped, lets reevaluate just the lengths again.
   312     if ((match_len + start_position) > source_len) {
   313       return EVAL_FALSE;
   314     }
   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);
   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);
   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 }
   349 //////////////////////////////////////////////////////////////////////////////
   350 // OpcodeMaker (other member functions).
   352 PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
   353                                       uint32 options,
   354                                       int16 selected_param) {
   355   if (memory_size() < sizeof(PolicyOpcode)) {
   356     return NULL;
   357   }
   359   // Create opcode using placement-new on the buffer memory.
   360   PolicyOpcode* opcode = new(memory_top_) PolicyOpcode();
   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 }
   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 }
   386 //////////////////////////////////////////////////////////////////////////////
   387 // Opcode evaluation dispatchers.
   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.
   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);
   410   // Apply the general options regardless of the particular type of opcode.
   411   if (kPolNone == options_) {
   412     return result;
   413   }
   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 }
   435 #define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval<op>(x, y, z)
   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 }
   452 #undef OPCODE_EVAL
   454 }  // namespace sandbox

mercurial