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.
46namespace 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.
52enum 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.
73enum 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.
86const 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).
90const uint32 kPolNegateEval = 1;
91
92// Zero the MatchContext context structure. This happens after the opcode
93// is evaluated.
94const 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)).
101const 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.
108struct 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.
137class 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
225enum 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().
237const int  kSeekForward = -1;
238// Perform a match with the end of the string. It only does a single comparison.
239const 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.
244struct 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.
274class 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