|
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 "base/basictypes.h" |
|
8 #include "sandbox/win/src/sandbox_nt_types.h" |
|
9 #include "sandbox/win/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 |