OLD | NEW |
| (Empty) |
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_SRC_POLICY_ENGINE_OPCODES_H__ | |
6 #define SANDBOX_SRC_POLICY_ENGINE_OPCODES_H__ | |
7 | |
8 #include "sandbox/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 // Creates an OpAlwaysFalse opcode. | |
291 PolicyOpcode* MakeOpAlwaysFalse(uint32 options); | |
292 | |
293 // Creates an OpAlwaysFalse opcode. | |
294 PolicyOpcode* MakeOpAlwaysTrue(uint32 options); | |
295 | |
296 // Creates an OpAction opcode. | |
297 // action: The action to return when Evaluate() is called. | |
298 PolicyOpcode* MakeOpAction(EvalResult action, uint32 options); | |
299 | |
300 // Creates an OpNumberMatch opcode. | |
301 // selected_param: index of the input argument. It must be a ulong or the | |
302 // evaluation result will generate a EVAL_ERROR. | |
303 // match: the number to compare against the selected_param. | |
304 PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match, | |
305 uint32 options); | |
306 | |
307 // Creates an OpNumberMatch opcode (void pointers are cast to numbers). | |
308 // selected_param: index of the input argument. It must be an void* or the | |
309 // evaluation result will generate a EVAL_ERROR. | |
310 // match: the pointer numeric value to compare against selected_param. | |
311 PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match, | |
312 uint32 options); | |
313 | |
314 // Creates an OpUlongMatchRange opcode using the memory passed in the ctor. | |
315 // selected_param: index of the input argument. It must be a ulong or the | |
316 // evaluation result will generate a EVAL_ERROR. | |
317 // lower_bound, upper_bound: the range to compare against selected_param. | |
318 PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param, | |
319 unsigned long lower_bound, | |
320 unsigned long upper_bound, | |
321 uint32 options); | |
322 | |
323 // Creates an OpWStringMatch opcode using the raw memory passed in the ctor. | |
324 // selected_param: index of the input argument. It must be a wide string | |
325 // pointer or the evaluation result will generate a EVAL_ERROR. | |
326 // match_str: string to compare against selected_param. | |
327 // start_position: when its value is from 0 to < 0x7fff it indicates an | |
328 // offset from the selected_param string where to perform the comparison. If | |
329 // the value is SeekForward then a substring search is performed. If the | |
330 // value is SeekToEnd the comparison is performed against the last part of | |
331 // the selected_param string. | |
332 // Note that the range in the position (0 to 0x7fff) is dictated by the | |
333 // current implementation. | |
334 // match_opts: Indicates additional matching flags. Currently CaseInsensitive | |
335 // is supported. | |
336 PolicyOpcode* MakeOpWStringMatch(int16 selected_param, | |
337 const wchar_t* match_str, | |
338 int start_position, | |
339 StringMatchOptions match_opts, | |
340 uint32 options); | |
341 | |
342 // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor. | |
343 // selected_param: index of the input argument. It must be ulong or the | |
344 // evaluation result will generate a EVAL_ERROR. | |
345 // match: the value to bitwise AND against selected_param. | |
346 PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param, | |
347 unsigned long match, | |
348 uint32 options); | |
349 | |
350 private: | |
351 // Constructs the common part of every opcode. selected_param is the index | |
352 // of the input param to use when evaluating the opcode. Pass -1 in | |
353 // selected_param to indicate that no input parameter is required. | |
354 PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options, | |
355 int16 selected_param); | |
356 | |
357 // Allocates (and copies) a string (of size length) inside the buffer and | |
358 // returns the displacement with respect to start. | |
359 ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght); | |
360 | |
361 // Returns the available memory to make opcodes. | |
362 size_t memory_size() const { | |
363 return memory_bottom_ - memory_top_; | |
364 } | |
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_SRC_POLICY_ENGINE_OPCODES_H__ | |
OLD | NEW |