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
InitStringUnicode(const wchar_t * source,size_t length,UNICODE_STRING * ustring)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
MakeOpAlwaysFalse(uint32 options)51 PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
52 return MakeBase(OP_ALWAYS_FALSE, options, -1);
53 }
54
55 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpAlwaysTrue(uint32 options)69 PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
70 return MakeBase(OP_ALWAYS_TRUE, options, -1);
71 }
72
73 template <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpAction(EvalResult action,uint32 options)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 <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpNumberMatch(int16 selected_param,unsigned long match,uint32 options)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
MakeOpVoidPtrMatch(int16 selected_param,const void * match,uint32 options)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 <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpUlongMatchRange(int16 selected_param,unsigned long lower_bound,unsigned long upper_bound,uint32 options)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 NULL;
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 <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpUlongAndMatch(int16 selected_param,unsigned long match,uint32 options)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 <>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeOpWStringMatch(int16 selected_param,const wchar_t * match_str,int start_position,StringMatchOptions match_opts,uint32 options)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<>
OpcodeEval(PolicyOpcode * opcode,const ParameterSet * param,MatchContext * context)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
MakeBase(OpcodeID opcode_id,uint32 options,int16 selected_param)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
AllocRelative(void * start,const wchar_t * str,size_t lenght)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
Evaluate(const ParameterSet * call_params,size_t param_count,MatchContext * match)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
EvaluateHelper(const ParameterSet * parameters,MatchContext * match)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
455