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