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 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 <> 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 455