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