|
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_ |