michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/policy_engine_opcodes.h" michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "sandbox/win/src/sandbox_nt_types.h" michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: michael@0: namespace { michael@0: const unsigned short kMaxUniStrSize = 0xfffc; michael@0: michael@0: bool InitStringUnicode(const wchar_t* source, size_t length, michael@0: UNICODE_STRING* ustring) { michael@0: ustring->Buffer = const_cast(source); michael@0: ustring->Length = static_cast(length) * sizeof(wchar_t); michael@0: if (length > kMaxUniStrSize) { michael@0: return false; michael@0: } michael@0: ustring->MaximumLength = (NULL != source) ? michael@0: ustring->Length + sizeof(wchar_t) : 0; michael@0: return true; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: SANDBOX_INTERCEPT NtExports g_nt; michael@0: michael@0: // Note: The opcodes are implemented as functions (as opposed to classes derived michael@0: // from PolicyOpcode) because you should not add more member variables to the michael@0: // PolicyOpcode class since it would cause object slicing on the target. So to michael@0: // enforce that (instead of just trusting the developer) the opcodes became michael@0: // just functions. michael@0: // michael@0: // In the code that follows I have keep the evaluation function and the factory michael@0: // function together to stress the close relationship between both. For example, michael@0: // only the factory method and the evaluation function know the stored argument michael@0: // order and meaning. michael@0: michael@0: template michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, michael@0: MatchContext* match); michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpAlwaysFalse: michael@0: // Does not require input parameter. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) { michael@0: return MakeBase(OP_ALWAYS_FALSE, options, -1); michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(opcode); michael@0: UNREFERENCED_PARAMETER(param); michael@0: UNREFERENCED_PARAMETER(context); michael@0: return EVAL_FALSE; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpAlwaysTrue: michael@0: // Does not require input parameter. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) { michael@0: return MakeBase(OP_ALWAYS_TRUE, options, -1); michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(opcode); michael@0: UNREFERENCED_PARAMETER(param); michael@0: UNREFERENCED_PARAMETER(context); michael@0: return EVAL_TRUE; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpAction: michael@0: // Does not require input parameter. michael@0: // Argument 0 contains the actual action to return. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, michael@0: uint32 options) { michael@0: PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0); michael@0: if (NULL == opcode) return NULL; michael@0: opcode->SetArgument(0, action); michael@0: return opcode; michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(param); michael@0: UNREFERENCED_PARAMETER(context); michael@0: int action = 0; michael@0: opcode->GetArgument(0, &action); michael@0: return static_cast(action); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpNumberMatch: michael@0: // Requires a unsigned long or void* in selected_param michael@0: // Argument 0 is the stored number to match. michael@0: // Argument 1 is the C++ type of the 0th argument. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param, michael@0: unsigned long match, michael@0: uint32 options) { michael@0: PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); michael@0: if (NULL == opcode) return NULL; michael@0: opcode->SetArgument(0, match); michael@0: opcode->SetArgument(1, ULONG_TYPE); michael@0: return opcode; michael@0: } michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param, michael@0: const void* match, michael@0: uint32 options) { michael@0: PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); michael@0: if (NULL == opcode) return NULL; michael@0: opcode->SetArgument(0, match); michael@0: opcode->SetArgument(1, VOIDPTR_TYPE); michael@0: return opcode; michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(context); michael@0: unsigned long value_ulong = 0; michael@0: if (param->Get(&value_ulong)) { michael@0: unsigned long match_ulong = 0; michael@0: opcode->GetArgument(0, &match_ulong); michael@0: return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE; michael@0: } else { michael@0: const void* value_ptr = NULL; michael@0: if (param->Get(&value_ptr)) { michael@0: const void* match_ptr = NULL; michael@0: opcode->GetArgument(0, &match_ptr); michael@0: return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE; michael@0: } michael@0: } michael@0: return EVAL_ERROR; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpUlongMatchRange michael@0: // Requires a unsigned long in selected_param. michael@0: // Argument 0 is the stored lower bound to match. michael@0: // Argument 1 is the stored upper bound to match. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param, michael@0: unsigned long lower_bound, michael@0: unsigned long upper_bound, michael@0: uint32 options) { michael@0: if (lower_bound > upper_bound) { michael@0: return false; michael@0: } michael@0: PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options, michael@0: selected_param); michael@0: if (NULL == opcode) return NULL; michael@0: opcode->SetArgument(0, lower_bound); michael@0: opcode->SetArgument(1, upper_bound); michael@0: return opcode; michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(context); michael@0: unsigned long value = 0; michael@0: if (!param->Get(&value)) return EVAL_ERROR; michael@0: michael@0: unsigned long lower_bound = 0; michael@0: unsigned long upper_bound = 0; michael@0: opcode->GetArgument(0, &lower_bound); michael@0: opcode->GetArgument(1, &upper_bound); michael@0: return((lower_bound <= value) && (upper_bound >= value))? michael@0: EVAL_TRUE : EVAL_FALSE; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpUlongAndMatch: michael@0: // Requires a unsigned long in selected_param. michael@0: // Argument 0 is the stored number to match. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param, michael@0: unsigned long match, michael@0: uint32 options) { michael@0: PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param); michael@0: if (NULL == opcode) return NULL; michael@0: opcode->SetArgument(0, match); michael@0: return opcode; michael@0: } michael@0: michael@0: template <> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: UNREFERENCED_PARAMETER(context); michael@0: unsigned long value = 0; michael@0: if (!param->Get(&value)) return EVAL_ERROR; michael@0: michael@0: unsigned long number = 0; michael@0: opcode->GetArgument(0, &number); michael@0: return (number & value)? EVAL_TRUE : EVAL_FALSE; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode OpWStringMatch: michael@0: // Requires a wchar_t* in selected_param. michael@0: // Argument 0 is the byte displacement of the stored string. michael@0: // Argument 1 is the lenght in chars of the stored string. michael@0: // Argument 2 is the offset to apply on the input string. It has special values. michael@0: // as noted in the header file. michael@0: // Argument 3 is the string matching options. michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param, michael@0: const wchar_t* match_str, michael@0: int start_position, michael@0: StringMatchOptions match_opts, michael@0: uint32 options) { michael@0: if (NULL == match_str) { michael@0: return NULL; michael@0: } michael@0: if ('\0' == match_str[0]) { michael@0: return NULL; michael@0: } michael@0: michael@0: int lenght = lstrlenW(match_str); michael@0: michael@0: PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param); michael@0: if (NULL == opcode) { michael@0: return NULL; michael@0: } michael@0: ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1); michael@0: if (0 == delta_str) { michael@0: return NULL; michael@0: } michael@0: opcode->SetArgument(0, delta_str); michael@0: opcode->SetArgument(1, lenght); michael@0: opcode->SetArgument(2, start_position); michael@0: opcode->SetArgument(3, match_opts); michael@0: return opcode; michael@0: } michael@0: michael@0: template<> michael@0: EvalResult OpcodeEval(PolicyOpcode* opcode, michael@0: const ParameterSet* param, michael@0: MatchContext* context) { michael@0: if (NULL == context) { michael@0: return EVAL_ERROR; michael@0: } michael@0: const wchar_t* source_str = NULL; michael@0: if (!param->Get(&source_str)) return EVAL_ERROR; michael@0: michael@0: int start_position = 0; michael@0: int match_len = 0; michael@0: unsigned int match_opts = 0; michael@0: opcode->GetArgument(1, &match_len); michael@0: opcode->GetArgument(2, &start_position); michael@0: opcode->GetArgument(3, &match_opts); michael@0: michael@0: const wchar_t* match_str = opcode->GetRelativeString(0); michael@0: // Advance the source string to the last successfully evaluated position michael@0: // according to the match context. michael@0: source_str = &source_str[context->position]; michael@0: int source_len = static_cast(g_nt.wcslen(source_str)); michael@0: michael@0: if (0 == source_len) { michael@0: // If we reached the end of the source string there is nothing we can michael@0: // match against. michael@0: return EVAL_FALSE; michael@0: } michael@0: if (match_len > source_len) { michael@0: // There can't be a positive match when the target string is bigger than michael@0: // the source string michael@0: return EVAL_FALSE; michael@0: } michael@0: michael@0: BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE; michael@0: michael@0: // We have three cases, depending on the value of start_pos: michael@0: // Case 1. We skip N characters and compare once. michael@0: // Case 2: We skip to the end and compare once. michael@0: // Case 3: We match the first substring (if we find any). michael@0: if (start_position >= 0) { michael@0: if (kSeekToEnd == start_position) { michael@0: start_position = source_len - match_len; michael@0: } else if (match_opts & EXACT_LENGHT) { michael@0: // A sub-case of case 3 is when the EXACT_LENGHT flag is on michael@0: // the match needs to be not just substring but full match. michael@0: if ((match_len + start_position) != source_len) { michael@0: return EVAL_FALSE; michael@0: } michael@0: } michael@0: michael@0: // Advance start_pos characters. Warning! this does not consider michael@0: // utf16 encodings (surrogate pairs) or other Unicode 'features'. michael@0: source_str += start_position; michael@0: michael@0: // Since we skipped, lets reevaluate just the lengths again. michael@0: if ((match_len + start_position) > source_len) { michael@0: return EVAL_FALSE; michael@0: } michael@0: michael@0: UNICODE_STRING match_ustr; michael@0: InitStringUnicode(match_str, match_len, &match_ustr); michael@0: UNICODE_STRING source_ustr; michael@0: InitStringUnicode(source_str, match_len, &source_ustr); michael@0: michael@0: if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, michael@0: case_sensitive)) { michael@0: // Match! update the match context. michael@0: context->position += start_position + match_len; michael@0: return EVAL_TRUE; michael@0: } else { michael@0: return EVAL_FALSE; michael@0: } michael@0: } else if (start_position < 0) { michael@0: UNICODE_STRING match_ustr; michael@0: InitStringUnicode(match_str, match_len, &match_ustr); michael@0: UNICODE_STRING source_ustr; michael@0: InitStringUnicode(source_str, match_len, &source_ustr); michael@0: michael@0: do { michael@0: if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, michael@0: case_sensitive)) { michael@0: // Match! update the match context. michael@0: context->position += (source_ustr.Buffer - source_str) + match_len; michael@0: return EVAL_TRUE; michael@0: } michael@0: ++source_ustr.Buffer; michael@0: --source_len; michael@0: } while (source_len >= match_len); michael@0: } michael@0: return EVAL_FALSE; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // OpcodeMaker (other member functions). michael@0: michael@0: PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id, michael@0: uint32 options, michael@0: int16 selected_param) { michael@0: if (memory_size() < sizeof(PolicyOpcode)) { michael@0: return NULL; michael@0: } michael@0: michael@0: // Create opcode using placement-new on the buffer memory. michael@0: PolicyOpcode* opcode = new(memory_top_) PolicyOpcode(); michael@0: michael@0: // Fill in the standard fields, that every opcode has. michael@0: memory_top_ += sizeof(PolicyOpcode); michael@0: opcode->opcode_id_ = opcode_id; michael@0: opcode->options_ = static_cast(options); michael@0: opcode->parameter_ = selected_param; michael@0: return opcode; michael@0: } michael@0: michael@0: ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str, michael@0: size_t lenght) { michael@0: size_t bytes = lenght * sizeof(wchar_t); michael@0: if (memory_size() < bytes) { michael@0: return 0; michael@0: } michael@0: memory_bottom_ -= bytes; michael@0: if (reinterpret_cast(memory_bottom_) & 1) { michael@0: // TODO(cpu) replace this for something better. michael@0: ::DebugBreak(); michael@0: } michael@0: memcpy(memory_bottom_, str, bytes); michael@0: ptrdiff_t delta = memory_bottom_ - reinterpret_cast(start); michael@0: return delta; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // Opcode evaluation dispatchers. michael@0: michael@0: // This function is the one and only entry for evaluating any opcode. It is michael@0: // in charge of applying any relevant opcode options and calling EvaluateInner michael@0: // were the actual dispatch-by-id is made. It would seem at first glance that michael@0: // the dispatch should be done by virtual function (vtable) calls but you have michael@0: // to remember that the opcodes are made in the broker process and copied as michael@0: // raw memory to the target process. michael@0: michael@0: EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params, michael@0: size_t param_count, MatchContext* match) { michael@0: if (NULL == call_params) { michael@0: return EVAL_ERROR; michael@0: } michael@0: const ParameterSet* selected_param = NULL; michael@0: if (parameter_ >= 0) { michael@0: if (static_cast(parameter_) >= param_count) { michael@0: return EVAL_ERROR; michael@0: } michael@0: selected_param = &call_params[parameter_]; michael@0: } michael@0: EvalResult result = EvaluateHelper(selected_param, match); michael@0: michael@0: // Apply the general options regardless of the particular type of opcode. michael@0: if (kPolNone == options_) { michael@0: return result; michael@0: } michael@0: michael@0: if (options_ & kPolNegateEval) { michael@0: if (EVAL_TRUE == result) { michael@0: result = EVAL_FALSE; michael@0: } else if (EVAL_FALSE == result) { michael@0: result = EVAL_TRUE; michael@0: } else if (EVAL_ERROR != result) { michael@0: result = EVAL_ERROR; michael@0: } michael@0: } michael@0: if (NULL != match) { michael@0: if (options_ & kPolClearContext) { michael@0: match->Clear(); michael@0: } michael@0: if (options_ & kPolUseOREval) { michael@0: match->options = kPolUseOREval; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: #define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval(x, y, z) michael@0: michael@0: EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters, michael@0: MatchContext* match) { michael@0: switch (opcode_id_) { michael@0: OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match); michael@0: OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match); michael@0: OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match); michael@0: OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match); michael@0: OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match); michael@0: OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match); michael@0: OPCODE_EVAL(OP_ACTION, this, parameters, match); michael@0: default: michael@0: return EVAL_ERROR; michael@0: } michael@0: } michael@0: michael@0: #undef OPCODE_EVAL michael@0: michael@0: } // namespace sandbox