OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 #include "sandbox/src/policy_engine_opcodes.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "sandbox/src/sandbox_nt_types.h" | |
9 #include "sandbox/src/sandbox_types.h" | |
10 | |
11 namespace { | |
12 const unsigned short kMaxUniStrSize = 0xfffc; | |
13 | |
14 bool InitStringUnicode(const wchar_t* source, size_t length, | |
15 UNICODE_STRING* ustring) { | |
16 ustring->Buffer = const_cast<wchar_t*>(source); | |
17 ustring->Length = static_cast<USHORT>(length) * sizeof(wchar_t); | |
18 if (length > kMaxUniStrSize) { | |
19 return false; | |
20 } | |
21 ustring->MaximumLength = (NULL != source) ? | |
22 ustring->Length + sizeof(wchar_t) : 0; | |
23 return true; | |
24 } | |
25 | |
26 } // namespace | |
27 | |
28 namespace sandbox { | |
29 | |
30 SANDBOX_INTERCEPT NtExports g_nt; | |
31 | |
32 // Note: The opcodes are implemented as functions (as opposed to classes derived | |
33 // from PolicyOpcode) because you should not add more member variables to the | |
34 // PolicyOpcode class since it would cause object slicing on the target. So to | |
35 // enforce that (instead of just trusting the developer) the opcodes became | |
36 // just functions. | |
37 // | |
38 // In the code that follows I have keep the evaluation function and the factory | |
39 // function together to stress the close relationship between both. For example, | |
40 // only the factory method and the evaluation function know the stored argument | |
41 // order and meaning. | |
42 | |
43 template <int> | |
44 EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, | |
45 MatchContext* match); | |
46 | |
47 ////////////////////////////////////////////////////////////////////////////// | |
48 // Opcode OpAlwaysFalse: | |
49 // Does not require input parameter. | |
50 | |
51 PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) { | |
52 return MakeBase(OP_ALWAYS_FALSE, options, -1); | |
53 } | |
54 | |
55 template <> | |
56 EvalResult OpcodeEval<OP_ALWAYS_FALSE>(PolicyOpcode* opcode, | |
57 const ParameterSet* param, | |
58 MatchContext* context) { | |
59 UNREFERENCED_PARAMETER(opcode); | |
60 UNREFERENCED_PARAMETER(param); | |
61 UNREFERENCED_PARAMETER(context); | |
62 return EVAL_FALSE; | |
63 } | |
64 | |
65 ////////////////////////////////////////////////////////////////////////////// | |
66 // Opcode OpAlwaysTrue: | |
67 // Does not require input parameter. | |
68 | |
69 PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) { | |
70 return MakeBase(OP_ALWAYS_TRUE, options, -1); | |
71 } | |
72 | |
73 template <> | |
74 EvalResult OpcodeEval<OP_ALWAYS_TRUE>(PolicyOpcode* opcode, | |
75 const ParameterSet* param, | |
76 MatchContext* context) { | |
77 UNREFERENCED_PARAMETER(opcode); | |
78 UNREFERENCED_PARAMETER(param); | |
79 UNREFERENCED_PARAMETER(context); | |
80 return EVAL_TRUE; | |
81 } | |
82 | |
83 ////////////////////////////////////////////////////////////////////////////// | |
84 // Opcode OpAction: | |
85 // Does not require input parameter. | |
86 // Argument 0 contains the actual action to return. | |
87 | |
88 PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, | |
89 uint32 options) { | |
90 PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0); | |
91 if (NULL == opcode) return NULL; | |
92 opcode->SetArgument(0, action); | |
93 return opcode; | |
94 } | |
95 | |
96 template <> | |
97 EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode, | |
98 const ParameterSet* param, | |
99 MatchContext* context) { | |
100 UNREFERENCED_PARAMETER(param); | |
101 UNREFERENCED_PARAMETER(context); | |
102 int action = 0; | |
103 opcode->GetArgument(0, &action); | |
104 return static_cast<EvalResult>(action); | |
105 } | |
106 | |
107 ////////////////////////////////////////////////////////////////////////////// | |
108 // Opcode OpNumberMatch: | |
109 // Requires a unsigned long or void* in selected_param | |
110 // Argument 0 is the stored number to match. | |
111 // Argument 1 is the C++ type of the 0th argument. | |
112 | |
113 PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param, | |
114 unsigned long match, | |
115 uint32 options) { | |
116 PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); | |
117 if (NULL == opcode) return NULL; | |
118 opcode->SetArgument(0, match); | |
119 opcode->SetArgument(1, ULONG_TYPE); | |
120 return opcode; | |
121 } | |
122 | |
123 PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param, | |
124 const void* match, | |
125 uint32 options) { | |
126 PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); | |
127 if (NULL == opcode) return NULL; | |
128 opcode->SetArgument(0, match); | |
129 opcode->SetArgument(1, VOIDPTR_TYPE); | |
130 return opcode; | |
131 } | |
132 | |
133 template <> | |
134 EvalResult OpcodeEval<OP_NUMBER_MATCH>(PolicyOpcode* opcode, | |
135 const ParameterSet* param, | |
136 MatchContext* context) { | |
137 UNREFERENCED_PARAMETER(context); | |
138 unsigned long value_ulong = 0; | |
139 if (param->Get(&value_ulong)) { | |
140 unsigned long match_ulong = 0; | |
141 opcode->GetArgument(0, &match_ulong); | |
142 return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE; | |
143 } else { | |
144 const void* value_ptr = NULL; | |
145 if (param->Get(&value_ptr)) { | |
146 const void* match_ptr = NULL; | |
147 opcode->GetArgument(0, &match_ptr); | |
148 return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE; | |
149 } | |
150 } | |
151 return EVAL_ERROR; | |
152 } | |
153 | |
154 ////////////////////////////////////////////////////////////////////////////// | |
155 // Opcode OpUlongMatchRange | |
156 // Requires a unsigned long in selected_param. | |
157 // Argument 0 is the stored lower bound to match. | |
158 // Argument 1 is the stored upper bound to match. | |
159 | |
160 PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param, | |
161 unsigned long lower_bound, | |
162 unsigned long upper_bound, | |
163 uint32 options) { | |
164 if (lower_bound > upper_bound) { | |
165 return false; | |
166 } | |
167 PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options, | |
168 selected_param); | |
169 if (NULL == opcode) return NULL; | |
170 opcode->SetArgument(0, lower_bound); | |
171 opcode->SetArgument(1, upper_bound); | |
172 return opcode; | |
173 } | |
174 | |
175 template <> | |
176 EvalResult OpcodeEval<OP_ULONG_MATCH_RANGE>(PolicyOpcode* opcode, | |
177 const ParameterSet* param, | |
178 MatchContext* context) { | |
179 UNREFERENCED_PARAMETER(context); | |
180 unsigned long value = 0; | |
181 if (!param->Get(&value)) return EVAL_ERROR; | |
182 | |
183 unsigned long lower_bound = 0; | |
184 unsigned long upper_bound = 0; | |
185 opcode->GetArgument(0, &lower_bound); | |
186 opcode->GetArgument(1, &upper_bound); | |
187 return((lower_bound <= value) && (upper_bound >= value))? | |
188 EVAL_TRUE : EVAL_FALSE; | |
189 } | |
190 | |
191 ////////////////////////////////////////////////////////////////////////////// | |
192 // Opcode OpUlongAndMatch: | |
193 // Requires a unsigned long in selected_param. | |
194 // Argument 0 is the stored number to match. | |
195 | |
196 PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param, | |
197 unsigned long match, | |
198 uint32 options) { | |
199 PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param); | |
200 if (NULL == opcode) return NULL; | |
201 opcode->SetArgument(0, match); | |
202 return opcode; | |
203 } | |
204 | |
205 template <> | |
206 EvalResult OpcodeEval<OP_ULONG_AND_MATCH>(PolicyOpcode* opcode, | |
207 const ParameterSet* param, | |
208 MatchContext* context) { | |
209 UNREFERENCED_PARAMETER(context); | |
210 unsigned long value = 0; | |
211 if (!param->Get(&value)) return EVAL_ERROR; | |
212 | |
213 unsigned long number = 0; | |
214 opcode->GetArgument(0, &number); | |
215 return (number & value)? EVAL_TRUE : EVAL_FALSE; | |
216 } | |
217 | |
218 ////////////////////////////////////////////////////////////////////////////// | |
219 // Opcode OpWStringMatch: | |
220 // Requires a wchar_t* in selected_param. | |
221 // Argument 0 is the byte displacement of the stored string. | |
222 // Argument 1 is the lenght in chars of the stored string. | |
223 // Argument 2 is the offset to apply on the input string. It has special values. | |
224 // as noted in the header file. | |
225 // Argument 3 is the string matching options. | |
226 | |
227 PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param, | |
228 const wchar_t* match_str, | |
229 int start_position, | |
230 StringMatchOptions match_opts, | |
231 uint32 options) { | |
232 if (NULL == match_str) { | |
233 return NULL; | |
234 } | |
235 if ('\0' == match_str[0]) { | |
236 return NULL; | |
237 } | |
238 | |
239 int lenght = lstrlenW(match_str); | |
240 | |
241 PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param); | |
242 if (NULL == opcode) { | |
243 return NULL; | |
244 } | |
245 ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1); | |
246 if (0 == delta_str) { | |
247 return NULL; | |
248 } | |
249 opcode->SetArgument(0, delta_str); | |
250 opcode->SetArgument(1, lenght); | |
251 opcode->SetArgument(2, start_position); | |
252 opcode->SetArgument(3, match_opts); | |
253 return opcode; | |
254 } | |
255 | |
256 template<> | |
257 EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode, | |
258 const ParameterSet* param, | |
259 MatchContext* context) { | |
260 if (NULL == context) { | |
261 return EVAL_ERROR; | |
262 } | |
263 const wchar_t* source_str = NULL; | |
264 if (!param->Get(&source_str)) return EVAL_ERROR; | |
265 | |
266 int start_position = 0; | |
267 int match_len = 0; | |
268 unsigned int match_opts = 0; | |
269 opcode->GetArgument(1, &match_len); | |
270 opcode->GetArgument(2, &start_position); | |
271 opcode->GetArgument(3, &match_opts); | |
272 | |
273 const wchar_t* match_str = opcode->GetRelativeString(0); | |
274 // Advance the source string to the last successfully evaluated position | |
275 // according to the match context. | |
276 source_str = &source_str[context->position]; | |
277 int source_len = static_cast<int>(g_nt.wcslen(source_str)); | |
278 | |
279 if (0 == source_len) { | |
280 // If we reached the end of the source string there is nothing we can | |
281 // match against. | |
282 return EVAL_FALSE; | |
283 } | |
284 if (match_len > source_len) { | |
285 // There can't be a positive match when the target string is bigger than | |
286 // the source string | |
287 return EVAL_FALSE; | |
288 } | |
289 | |
290 BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE; | |
291 | |
292 // We have three cases, depending on the value of start_pos: | |
293 // Case 1. We skip N characters and compare once. | |
294 // Case 2: We skip to the end and compare once. | |
295 // Case 3: We match the first substring (if we find any). | |
296 if (start_position >= 0) { | |
297 if (kSeekToEnd == start_position) { | |
298 start_position = source_len - match_len; | |
299 } else if (match_opts & EXACT_LENGHT) { | |
300 // A sub-case of case 3 is when the EXACT_LENGHT flag is on | |
301 // the match needs to be not just substring but full match. | |
302 if ((match_len + start_position) != source_len) { | |
303 return EVAL_FALSE; | |
304 } | |
305 } | |
306 | |
307 // Advance start_pos characters. Warning! this does not consider | |
308 // utf16 encodings (surrogate pairs) or other Unicode 'features'. | |
309 source_str += start_position; | |
310 | |
311 // Since we skipped, lets reevaluate just the lengths again. | |
312 if ((match_len + start_position) > source_len) { | |
313 return EVAL_FALSE; | |
314 } | |
315 | |
316 UNICODE_STRING match_ustr; | |
317 InitStringUnicode(match_str, match_len, &match_ustr); | |
318 UNICODE_STRING source_ustr; | |
319 InitStringUnicode(source_str, match_len, &source_ustr); | |
320 | |
321 if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, | |
322 case_sensitive)) { | |
323 // Match! update the match context. | |
324 context->position += start_position + match_len; | |
325 return EVAL_TRUE; | |
326 } else { | |
327 return EVAL_FALSE; | |
328 } | |
329 } else if (start_position < 0) { | |
330 UNICODE_STRING match_ustr; | |
331 InitStringUnicode(match_str, match_len, &match_ustr); | |
332 UNICODE_STRING source_ustr; | |
333 InitStringUnicode(source_str, match_len, &source_ustr); | |
334 | |
335 do { | |
336 if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, | |
337 case_sensitive)) { | |
338 // Match! update the match context. | |
339 context->position += (source_ustr.Buffer - source_str) + match_len; | |
340 return EVAL_TRUE; | |
341 } | |
342 ++source_ustr.Buffer; | |
343 --source_len; | |
344 } while (source_len >= match_len); | |
345 } | |
346 return EVAL_FALSE; | |
347 } | |
348 | |
349 ////////////////////////////////////////////////////////////////////////////// | |
350 // OpcodeMaker (other member functions). | |
351 | |
352 PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id, | |
353 uint32 options, | |
354 int16 selected_param) { | |
355 if (memory_size() < sizeof(PolicyOpcode)) { | |
356 return NULL; | |
357 } | |
358 | |
359 // Create opcode using placement-new on the buffer memory. | |
360 PolicyOpcode* opcode = new(memory_top_) PolicyOpcode(); | |
361 | |
362 // Fill in the standard fields, that every opcode has. | |
363 memory_top_ += sizeof(PolicyOpcode); | |
364 opcode->opcode_id_ = opcode_id; | |
365 opcode->options_ = static_cast<int16>(options); | |
366 opcode->parameter_ = selected_param; | |
367 return opcode; | |
368 } | |
369 | |
370 ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str, | |
371 size_t lenght) { | |
372 size_t bytes = lenght * sizeof(wchar_t); | |
373 if (memory_size() < bytes) { | |
374 return 0; | |
375 } | |
376 memory_bottom_ -= bytes; | |
377 if (reinterpret_cast<UINT_PTR>(memory_bottom_) & 1) { | |
378 // TODO(cpu) replace this for something better. | |
379 ::DebugBreak(); | |
380 } | |
381 memcpy(memory_bottom_, str, bytes); | |
382 ptrdiff_t delta = memory_bottom_ - reinterpret_cast<char*>(start); | |
383 return delta; | |
384 } | |
385 | |
386 ////////////////////////////////////////////////////////////////////////////// | |
387 // Opcode evaluation dispatchers. | |
388 | |
389 // This function is the one and only entry for evaluating any opcode. It is | |
390 // in charge of applying any relevant opcode options and calling EvaluateInner | |
391 // were the actual dispatch-by-id is made. It would seem at first glance that | |
392 // the dispatch should be done by virtual function (vtable) calls but you have | |
393 // to remember that the opcodes are made in the broker process and copied as | |
394 // raw memory to the target process. | |
395 | |
396 EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params, | |
397 size_t param_count, MatchContext* match) { | |
398 if (NULL == call_params) { | |
399 return EVAL_ERROR; | |
400 } | |
401 const ParameterSet* selected_param = NULL; | |
402 if (parameter_ >= 0) { | |
403 if (static_cast<size_t>(parameter_) >= param_count) { | |
404 return EVAL_ERROR; | |
405 } | |
406 selected_param = &call_params[parameter_]; | |
407 } | |
408 EvalResult result = EvaluateHelper(selected_param, match); | |
409 | |
410 // Apply the general options regardless of the particular type of opcode. | |
411 if (kPolNone == options_) { | |
412 return result; | |
413 } | |
414 | |
415 if (options_ & kPolNegateEval) { | |
416 if (EVAL_TRUE == result) { | |
417 result = EVAL_FALSE; | |
418 } else if (EVAL_FALSE == result) { | |
419 result = EVAL_TRUE; | |
420 } else if (EVAL_ERROR != result) { | |
421 result = EVAL_ERROR; | |
422 } | |
423 } | |
424 if (NULL != match) { | |
425 if (options_ & kPolClearContext) { | |
426 match->Clear(); | |
427 } | |
428 if (options_ & kPolUseOREval) { | |
429 match->options = kPolUseOREval; | |
430 } | |
431 } | |
432 return result; | |
433 } | |
434 | |
435 #define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval<op>(x, y, z) | |
436 | |
437 EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters, | |
438 MatchContext* match) { | |
439 switch (opcode_id_) { | |
440 OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match); | |
441 OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match); | |
442 OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match); | |
443 OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match); | |
444 OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match); | |
445 OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match); | |
446 OPCODE_EVAL(OP_ACTION, this, parameters, match); | |
447 default: | |
448 return EVAL_ERROR; | |
449 } | |
450 } | |
451 | |
452 #undef OPCODE_EVAL | |
453 | |
454 } // namespace sandbox | |
OLD | NEW |