Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2010 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 #ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
      6 #define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
      7 
      8 #include "sandbox/win/src/policy_engine_params.h"
      9 #include "base/basictypes.h"
     10 
     11 // The low-level policy is implemented using the concept of policy 'opcodes'.
     12 // An opcode is a structure that contains enough information to perform one
     13 // comparison against one single input parameter. For example, an opcode can
     14 // encode just one of the following comparison:
     15 //
     16 // - Is input parameter 3 not equal to NULL?
     17 // - Does input parameter 2 start with L"c:\\"?
     18 // - Is input parameter 5, bit 3 is equal 1?
     19 //
     20 // Each opcode is in fact equivalent to a function invocation where all
     21 // the parameters are known by the opcode except one. So say you have a
     22 // function of this form:
     23 //      bool fn(a, b, c, d)  with 4 arguments
     24 //
     25 // Then an opcode is:
     26 //      op(fn, b, c, d)
     27 // Which stores the function to call and its 3 last arguments
     28 //
     29 // Then and opcode evaluation is:
     30 //      op.eval(a)  ------------------------> fn(a,b,c,d)
     31 //                        internally calls
     32 //
     33 // The idea is that complex policy rules can be split into streams of
     34 // opcodes which are evaluated in sequence. The evaluation is done in
     35 // groups of opcodes that have N comparison opcodes plus 1 action opcode:
     36 //
     37 // [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
     38 //    ----- evaluation order----------->
     39 //
     40 // Each opcode group encodes one high-level policy rule. The rule applies
     41 // only if all the conditions on the group evaluate to true. The action
     42 // opcode contains the policy outcome for that particular rule.
     43 //
     44 // Note that this header contains the main building blocks of low-level policy
     45 // but not the low level policy class.
     46 namespace sandbox {
     47 
     48 // These are the possible policy outcomes. Note that some of them might
     49 // not apply and can be removed. Also note that The following values only
     50 // specify what to do, not how to do it and it is acceptable given specific
     51 // cases to ignore the policy outcome.
     52 enum EvalResult {
     53   // Comparison opcode values:
     54   EVAL_TRUE,   // Opcode condition evaluated true.
     55   EVAL_FALSE,  // Opcode condition evaluated false.
     56   EVAL_ERROR,  // Opcode condition generated an error while evaluating.
     57   // Action opcode values:
     58   ASK_BROKER,  // The target must generate an IPC to the broker. On the broker
     59                // side, this means grant access to the resource.
     60   DENY_ACCESS,   // No access granted to the resource.
     61   GIVE_READONLY,  // Give readonly access to the resource.
     62   GIVE_ALLACCESS,  // Give full access to the resource.
     63   GIVE_CACHED,  // IPC is not required. Target can return a cached handle.
     64   GIVE_FIRST,  // TODO(cpu)
     65   SIGNAL_ALARM,  // Unusual activity. Generate an alarm.
     66   FAKE_SUCCESS,  // Do not call original function. Just return 'success'.
     67   FAKE_ACCESS_DENIED,  // Do not call original function. Just return 'denied'
     68                        // and do not do IPC.
     69   TERMINATE_PROCESS,  // Destroy target process. Do IPC as well.
     70 };
     71 
     72 // The following are the implemented opcodes.
     73 enum OpcodeID {
     74   OP_ALWAYS_FALSE,  // Evaluates to false (EVAL_FALSE).
     75   OP_ALWAYS_TRUE,  // Evaluates to true (EVAL_TRUE).
     76   OP_NUMBER_MATCH,  // Match a 32-bit integer as n == a.
     77   OP_ULONG_MATCH_RANGE,  // Match an ulong integer as a <= n <= b.
     78   OP_ULONG_AND_MATCH,  // Match using bitwise AND; as in: n & a != 0.
     79   OP_WSTRING_MATCH,  // Match a string for equality.
     80   OP_ACTION  // Evaluates to an action opcode.
     81 };
     82 
     83 // Options that apply to every opcode. They are specified when creating
     84 // each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
     85 // Do nothing special.
     86 const uint32 kPolNone = 0;
     87 
     88 // Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
     89 // negated conditions such as if ( a && !b).
     90 const uint32 kPolNegateEval = 1;
     91 
     92 // Zero the MatchContext context structure. This happens after the opcode
     93 // is evaluated.
     94 const uint32 kPolClearContext = 2;
     95 
     96 // Use OR when evaluating this set of opcodes. The policy evaluator by default
     97 // uses AND when evaluating. Very helpful when
     98 // used with kPolNegateEval. For example if you have a condition best expressed
     99 // as if(! (a && b && c)), the use of this flags allows it to be expressed as
    100 // if ((!a) || (!b) || (!c)).
    101 const uint32 kPolUseOREval = 4;
    102 
    103 // Keeps the evaluation state between opcode evaluations. This is used
    104 // for string matching where the next opcode needs to continue matching
    105 // from the last character position from the current opcode. The match
    106 // context is preserved across opcode evaluation unless an opcode specifies
    107 // as an option kPolClearContext.
    108 struct MatchContext {
    109   size_t position;
    110   uint32 options;
    111 
    112   MatchContext() {
    113     Clear();
    114   }
    115 
    116   void Clear() {
    117     position = 0;
    118     options = 0;
    119   }
    120 };
    121 
    122 // Models a policy opcode; that is a condition evaluation were all the
    123 // arguments but one are stored in objects of this class. Use OpcodeFactory
    124 // to create objects of this type.
    125 // This class is just an implementation artifact and not exposed to the
    126 // API clients or visible in the intercepted service. Internally, an
    127 // opcode is just:
    128 //  - An integer that identifies the actual opcode.
    129 //  - An index to indicate which one is the input argument
    130 //  - An array of arguments.
    131 // While an OO hierarchy of objects would have been a natural choice, the fact
    132 // that 1) this code can execute before the CRT is loaded, presents serious
    133 // problems in terms of guarantees about the actual state of the vtables and
    134 // 2) because the opcode objects are generated in the broker process, we need to
    135 // use plain objects. To preserve some minimal type safety templates are used
    136 // when possible.
    137 class PolicyOpcode {
    138   friend class OpcodeFactory;
    139  public:
    140   // Evaluates the opcode. For a typical comparison opcode the return value
    141   // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
    142   // the return is EVAL_ERROR. If the opcode is an action opcode then the
    143   // return can take other values such as ASK_BROKER.
    144   // parameters: An array of all input parameters. This argument is normally
    145   // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
    146   // count: The number of parameters passed as first argument.
    147   // match: The match context that is persisted across the opcode evaluation
    148   // sequence.
    149   EvalResult Evaluate(const ParameterSet* parameters, size_t count,
    150                       MatchContext* match);
    151 
    152   // Retrieves a stored argument by index. Valid index values are
    153   // from 0 to < kArgumentCount.
    154   template <typename T>
    155   void GetArgument(size_t index, T* argument) const {
    156     COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
    157     *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
    158   }
    159 
    160   // Sets a stored argument by index. Valid index values are
    161   // from 0 to < kArgumentCount.
    162   template <typename T>
    163   void SetArgument(size_t index, const T& argument) {
    164     COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
    165     *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
    166   }
    167 
    168   // Retrieves the actual address of an string argument. When using
    169   // GetArgument() to retrieve an index that contains a string, the returned
    170   // value is just an offset to the actual string.
    171   // index: the stored string index. Valid values are from 0
    172   // to < kArgumentCount.
    173   const wchar_t* GetRelativeString(size_t index) const {
    174     ptrdiff_t str_delta = 0;
    175     GetArgument(index, &str_delta);
    176     const char* delta = reinterpret_cast<const char*>(this) + str_delta;
    177     return reinterpret_cast<const wchar_t*>(delta);
    178   }
    179 
    180   // Returns true if this opcode is an action opcode without actually
    181   // evaluating it. Used to do a quick scan forward to the next opcode group.
    182   bool IsAction() const {
    183     return (OP_ACTION == opcode_id_);
    184   };
    185 
    186   // Returns the opcode type.
    187   OpcodeID GetID() const {
    188     return opcode_id_;
    189   }
    190 
    191   // Returns the stored options such as kPolNegateEval and others.
    192   uint32 GetOptions() const {
    193     return options_;
    194   }
    195 
    196   // Sets the stored options such as kPolNegateEval.
    197   void SetOptions(int16 options) {
    198     options_ = options;
    199   }
    200 
    201  private:
    202 
    203   static const size_t kArgumentCount = 4;  // The number of supported argument.
    204 
    205   struct OpcodeArgument {
    206     UINT_PTR mem;
    207   };
    208 
    209   // Better define placement new in the class instead of relying on the
    210   // global definition which seems to be fubared.
    211   void* operator new(size_t, void* location) {
    212     return location;
    213   }
    214 
    215   // Helper function to evaluate the opcode. The parameters have the same
    216   // meaning that in Evaluate().
    217   EvalResult EvaluateHelper(const ParameterSet* parameters,
    218                            MatchContext* match);
    219   OpcodeID opcode_id_;
    220   int16 parameter_;
    221   int16 options_;
    222   OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
    223 };
    224 
    225 enum StringMatchOptions {
    226   CASE_SENSITIVE = 0,      // Pay or Not attention to the case as defined by
    227   CASE_INSENSITIVE = 1,    // RtlCompareUnicodeString windows API.
    228   EXACT_LENGHT = 2         // Don't do substring match. Do full string match.
    229 };
    230 
    231 // Opcodes that do string comparisons take a parameter that is the starting
    232 // position to perform the comparison so we can do substring matching. There
    233 // are two special values:
    234 //
    235 // Start from the current position and compare strings advancing forward until
    236 // a match is found if any. Similar to CRT strstr().
    237 const int  kSeekForward = -1;
    238 // Perform a match with the end of the string. It only does a single comparison.
    239 const int  kSeekToEnd = 0xfffff;
    240 
    241 
    242 // A PolicyBuffer is a variable size structure that contains all the opcodes
    243 // that are to be created or evaluated in sequence.
    244 struct PolicyBuffer {
    245   size_t opcode_count;
    246   PolicyOpcode opcodes[1];
    247 };
    248 
    249 // Helper class to create any opcode sequence. This class is normally invoked
    250 // only by the high level policy module or when you need to handcraft a special
    251 // policy.
    252 // The factory works by creating the opcodes using a chunk of memory given
    253 // in the constructor. The opcodes themselves are allocated from the beginning
    254 // (top) of the memory, while any string that an opcode needs is allocated from
    255 // the end (bottom) of the memory.
    256 //
    257 // In essence:
    258 //
    259 //   low address ---> [opcode 1]
    260 //                    [opcode 2]
    261 //                    [opcode 3]
    262 //                    |        | <--- memory_top_
    263 //                    | free   |
    264 //                    |        |
    265 //                    |        | <--- memory_bottom_
    266 //                    [string 1]
    267 //   high address --> [string 2]
    268 //
    269 // Note that this class does not keep track of the number of opcodes made and
    270 // it is designed to be a building block for low-level policy.
    271 //
    272 // Note that any of the MakeOpXXXXX member functions below can return NULL on
    273 // failure. When that happens opcode sequence creation must be aborted.
    274 class OpcodeFactory {
    275  public:
    276   // memory: base pointer to a chunk of memory where the opcodes are created.
    277   // memory_size: the size in bytes of the memory chunk.
    278   OpcodeFactory(char* memory, size_t memory_size)
    279       : memory_top_(memory) {
    280     memory_bottom_ = &memory_top_[memory_size];
    281   }
    282 
    283   // policy: contains the raw memory where the opcodes are created.
    284   // memory_size: contains the actual size of the policy argument.
    285   OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
    286     memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
    287     memory_bottom_ = &memory_top_[memory_size];
    288   }
    289 
    290   // Returns the available memory to make opcodes.
    291   size_t memory_size() const {
    292     return memory_bottom_ - memory_top_;
    293   }
    294 
    295   // Creates an OpAlwaysFalse opcode.
    296   PolicyOpcode* MakeOpAlwaysFalse(uint32 options);
    297 
    298   // Creates an OpAlwaysFalse opcode.
    299   PolicyOpcode* MakeOpAlwaysTrue(uint32 options);
    300 
    301   // Creates an OpAction opcode.
    302   // action: The action to return when Evaluate() is called.
    303   PolicyOpcode* MakeOpAction(EvalResult action, uint32 options);
    304 
    305   // Creates an OpNumberMatch opcode.
    306   // selected_param: index of the input argument. It must be a ulong or the
    307   // evaluation result will generate a EVAL_ERROR.
    308   // match: the number to compare against the selected_param.
    309   PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match,
    310                                   uint32 options);
    311 
    312   // Creates an OpNumberMatch opcode (void pointers are cast to numbers).
    313   // selected_param: index of the input argument. It must be an void* or the
    314   // evaluation result will generate a EVAL_ERROR.
    315   // match: the pointer numeric value to compare against selected_param.
    316   PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match,
    317                                    uint32 options);
    318 
    319   // Creates an OpUlongMatchRange opcode using the memory passed in the ctor.
    320   // selected_param: index of the input argument. It must be a ulong or the
    321   // evaluation result will generate a EVAL_ERROR.
    322   // lower_bound, upper_bound: the range to compare against selected_param.
    323   PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param,
    324                                       unsigned long lower_bound,
    325                                       unsigned long upper_bound,
    326                                       uint32 options);
    327 
    328   // Creates an OpWStringMatch opcode using the raw memory passed in the ctor.
    329   // selected_param: index of the input argument. It must be a wide string
    330   // pointer or the evaluation result will generate a EVAL_ERROR.
    331   // match_str: string to compare against selected_param.
    332   // start_position: when its value is from 0 to < 0x7fff it indicates an
    333   // offset from the selected_param string where to perform the comparison. If
    334   // the value is SeekForward  then a substring search is performed. If the
    335   // value is SeekToEnd the comparison is performed against the last part of
    336   // the selected_param string.
    337   // Note that the range in the position (0 to 0x7fff) is dictated by the
    338   // current implementation.
    339   // match_opts: Indicates additional matching flags. Currently CaseInsensitive
    340   // is supported.
    341   PolicyOpcode* MakeOpWStringMatch(int16 selected_param,
    342                                    const wchar_t* match_str,
    343                                    int start_position,
    344                                    StringMatchOptions match_opts,
    345                                    uint32 options);
    346 
    347   // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor.
    348   // selected_param: index of the input argument. It must be ulong or the
    349   // evaluation result will generate a EVAL_ERROR.
    350   // match: the value to bitwise AND against selected_param.
    351   PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param,
    352                                     unsigned long match,
    353                                     uint32 options);
    354 
    355  private:
    356   // Constructs the common part of every opcode. selected_param is the index
    357   // of the input param to use when evaluating the opcode. Pass -1 in
    358   // selected_param to indicate that no input parameter is required.
    359   PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options,
    360                          int16 selected_param);
    361 
    362   // Allocates (and copies) a string (of size length) inside the buffer and
    363   // returns the displacement with respect to start.
    364   ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght);
    365 
    366   // Points to the lowest currently available address of the memory
    367   // used to make the opcodes. This pointer increments as opcodes are made.
    368   char* memory_top_;
    369 
    370   // Points to the highest currently available address of the memory
    371   // used to make the opcodes. This pointer decrements as opcode strings are
    372   // allocated.
    373   char* memory_bottom_;
    374 
    375   DISALLOW_COPY_AND_ASSIGN(OpcodeFactory);
    376 };
    377 
    378 }  // namespace sandbox
    379 
    380 #endif  // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
    381