security/sandbox/win/src/policy_engine_opcodes.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial