123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright (c) 2010 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
- #define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
- #include <stddef.h>
- #include <stdint.h>
- #include "base/memory/raw_ptr.h"
- #include "base/numerics/safe_conversions.h"
- #include "sandbox/win/src/policy_engine_params.h"
- // The low-level policy is implemented using the concept of policy 'opcodes'.
- // An opcode is a structure that contains enough information to perform one
- // comparison against one single input parameter. For example, an opcode can
- // encode just one of the following comparison:
- //
- // - Is input parameter 3 not equal to nullptr?
- // - Does input parameter 2 start with L"c:\\"?
- // - Is input parameter 5, bit 3 is equal 1?
- //
- // Each opcode is in fact equivalent to a function invocation where all
- // the parameters are known by the opcode except one. So say you have a
- // function of this form:
- // bool fn(a, b, c, d) with 4 arguments
- //
- // Then an opcode is:
- // op(fn, b, c, d)
- // Which stores the function to call and its 3 last arguments
- //
- // Then and opcode evaluation is:
- // op.eval(a) ------------------------> fn(a,b,c,d)
- // internally calls
- //
- // The idea is that complex policy rules can be split into streams of
- // opcodes which are evaluated in sequence. The evaluation is done in
- // groups of opcodes that have N comparison opcodes plus 1 action opcode:
- //
- // [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
- // ----- evaluation order----------->
- //
- // Each opcode group encodes one high-level policy rule. The rule applies
- // only if all the conditions on the group evaluate to true. The action
- // opcode contains the policy outcome for that particular rule.
- //
- // Note that this header contains the main building blocks of low-level policy
- // but not the low level policy class.
- namespace sandbox {
- // These are the possible policy outcomes. Note that some of them might
- // not apply and can be removed. Also note that The following values only
- // specify what to do, not how to do it and it is acceptable given specific
- // cases to ignore the policy outcome.
- enum EvalResult {
- // Comparison opcode values:
- EVAL_TRUE, // Opcode condition evaluated true.
- EVAL_FALSE, // Opcode condition evaluated false.
- EVAL_ERROR, // Opcode condition generated an error while evaluating.
- // Action opcode values:
- ASK_BROKER, // The target must generate an IPC to the broker. On the broker
- // side, this means grant access to the resource.
- DENY_ACCESS, // No access granted to the resource.
- GIVE_READONLY, // Give readonly access to the resource.
- GIVE_ALLACCESS, // Give full access to the resource.
- GIVE_CACHED, // IPC is not required. Target can return a cached handle.
- GIVE_FIRST, // TODO(cpu)
- SIGNAL_ALARM, // Unusual activity. Generate an alarm.
- FAKE_SUCCESS, // Do not call original function. Just return 'success'.
- FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
- // and do not do IPC.
- TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
- };
- // The following are the implemented opcodes.
- enum OpcodeID {
- OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
- OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
- OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
- OP_NUMBER_MATCH_RANGE, // Match a 32-bit integer as a <= n <= b.
- OP_NUMBER_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
- OP_WSTRING_MATCH, // Match a string for equality.
- OP_ACTION // Evaluates to an action opcode.
- };
- // Options that apply to every opcode. They are specified when creating
- // each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
- // Do nothing special.
- const uint32_t kPolNone = 0;
- // Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
- // negated conditions such as if ( a && !b).
- const uint32_t kPolNegateEval = 1;
- // Zero the MatchContext context structure. This happens after the opcode
- // is evaluated.
- const uint32_t kPolClearContext = 2;
- // Use OR when evaluating this set of opcodes. The policy evaluator by default
- // uses AND when evaluating. Very helpful when
- // used with kPolNegateEval. For example if you have a condition best expressed
- // as if(! (a && b && c)), the use of this flags allows it to be expressed as
- // if ((!a) || (!b) || (!c)).
- const uint32_t kPolUseOREval = 4;
- // Keeps the evaluation state between opcode evaluations. This is used
- // for string matching where the next opcode needs to continue matching
- // from the last character position from the current opcode. The match
- // context is preserved across opcode evaluation unless an opcode specifies
- // as an option kPolClearContext.
- struct MatchContext {
- size_t position;
- uint32_t options;
- MatchContext() { Clear(); }
- void Clear() {
- position = 0;
- options = 0;
- }
- };
- // Models a policy opcode; that is a condition evaluation were all the
- // arguments but one are stored in objects of this class. Use OpcodeFactory
- // to create objects of this type.
- // This class is just an implementation artifact and not exposed to the
- // API clients or visible in the intercepted service. Internally, an
- // opcode is just:
- // - An integer that identifies the actual opcode.
- // - An index to indicate which one is the input argument
- // - An array of arguments.
- // While an OO hierarchy of objects would have been a natural choice, the fact
- // that 1) this code can execute before the CRT is loaded, presents serious
- // problems in terms of guarantees about the actual state of the vtables and
- // 2) because the opcode objects are generated in the broker process, we need to
- // use plain objects. To preserve some minimal type safety templates are used
- // when possible.
- class PolicyOpcode {
- friend class OpcodeFactory;
- public:
- // Evaluates the opcode. For a typical comparison opcode the return value
- // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
- // the return is EVAL_ERROR. If the opcode is an action opcode then the
- // return can take other values such as ASK_BROKER.
- // parameters: An array of all input parameters. This argument is normally
- // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
- // count: The number of parameters passed as first argument.
- // match: The match context that is persisted across the opcode evaluation
- // sequence.
- EvalResult Evaluate(const ParameterSet* parameters,
- size_t count,
- MatchContext* match);
- // Retrieves a stored argument by index. Valid index values are
- // from 0 to < kArgumentCount.
- template <typename T>
- void GetArgument(size_t index, T* argument) const {
- static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
- *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
- }
- // Sets a stored argument by index. Valid index values are
- // from 0 to < kArgumentCount.
- template <typename T>
- void SetArgument(size_t index, const T& argument) {
- static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
- *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
- }
- // Retrieves the actual address of a string argument. When using
- // GetArgument() to retrieve an index that contains a string, the returned
- // value is just an offset to the actual string.
- // index: the stored string index. Valid values are from 0
- // to < kArgumentCount.
- const wchar_t* GetRelativeString(size_t index) const {
- ptrdiff_t str_delta = 0;
- GetArgument(index, &str_delta);
- const char* delta = reinterpret_cast<const char*>(this) + str_delta;
- return reinterpret_cast<const wchar_t*>(delta);
- }
- // Returns true if this opcode is an action opcode without actually
- // evaluating it. Used to do a quick scan forward to the next opcode group.
- bool IsAction() const { return (OP_ACTION == opcode_id_); }
- // Returns the opcode type.
- OpcodeID GetID() const { return opcode_id_; }
- // Returns the stored options such as kPolNegateEval and others.
- uint32_t GetOptions() const { return options_; }
- // Sets the stored options such as kPolNegateEval.
- void SetOptions(uint32_t options) { options_ = options; }
- // Returns the parameter of the function the opcode concerns.
- uint16_t GetParameter() const { return parameter_; }
- private:
- static const size_t kArgumentCount = 4; // The number of supported argument.
- struct OpcodeArgument {
- UINT_PTR mem;
- };
- // Better define placement new in the class instead of relying on the
- // global definition which seems to be fubared.
- void* operator new(size_t, void* location) { return location; }
- // Helper function to evaluate the opcode. The parameters have the same
- // meaning that in Evaluate().
- EvalResult EvaluateHelper(const ParameterSet* parameters,
- MatchContext* match);
- OpcodeID opcode_id_;
- int16_t parameter_;
- uint32_t options_;
- OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
- };
- enum StringMatchOptions {
- CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
- CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
- EXACT_LENGTH = 2 // Don't do substring match. Do full string match.
- };
- // Opcodes that do string comparisons take a parameter that is the starting
- // position to perform the comparison so we can do substring matching. There
- // are two special values:
- //
- // Start from the current position and compare strings advancing forward until
- // a match is found if any. Similar to CRT strstr().
- const int kSeekForward = -1;
- // Perform a match with the end of the string. It only does a single comparison.
- const int kSeekToEnd = 0xfffff;
- // A PolicyBuffer is a variable size structure that contains all the opcodes
- // that are to be created or evaluated in sequence.
- struct PolicyBuffer {
- size_t opcode_count;
- PolicyOpcode opcodes[1];
- };
- // Helper class to create any opcode sequence. This class is normally invoked
- // only by the high level policy module or when you need to handcraft a special
- // policy.
- // The factory works by creating the opcodes using a chunk of memory given
- // in the constructor. The opcodes themselves are allocated from the beginning
- // (top) of the memory, while any string that an opcode needs is allocated from
- // the end (bottom) of the memory.
- //
- // In essence:
- //
- // low address ---> [opcode 1]
- // [opcode 2]
- // [opcode 3]
- // | | <--- memory_top_
- // | free |
- // | |
- // | | <--- memory_bottom_
- // [string 1]
- // high address --> [string 2]
- //
- // Note that this class does not keep track of the number of opcodes made and
- // it is designed to be a building block for low-level policy.
- //
- // Note that any of the MakeOpXXXXX member functions below can return nullptr on
- // failure. When that happens opcode sequence creation must be aborted.
- class OpcodeFactory {
- public:
- // memory: base pointer to a chunk of memory where the opcodes are created.
- // memory_size: the size in bytes of the memory chunk.
- OpcodeFactory(char* memory, size_t memory_size) : memory_top_(memory) {
- memory_bottom_ = &memory_top_[memory_size];
- }
- // policy: contains the raw memory where the opcodes are created.
- // memory_size: contains the actual size of the policy argument.
- OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
- memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
- memory_bottom_ = &memory_top_[memory_size];
- }
- OpcodeFactory(const OpcodeFactory&) = delete;
- OpcodeFactory& operator=(const OpcodeFactory&) = delete;
- // Returns the available memory to make opcodes.
- size_t memory_size() const;
- // Creates an OpAlwaysFalse opcode.
- PolicyOpcode* MakeOpAlwaysFalse(uint32_t options);
- // Creates an OpAlwaysFalse opcode.
- PolicyOpcode* MakeOpAlwaysTrue(uint32_t options);
- // Creates an OpAction opcode.
- // action: The action to return when Evaluate() is called.
- PolicyOpcode* MakeOpAction(EvalResult action, uint32_t options);
- // Creates an OpNumberMatch opcode.
- // selected_param: index of the input argument. It must be a uint32_t or the
- // evaluation result will generate a EVAL_ERROR.
- // match: the number to compare against the selected_param.
- PolicyOpcode* MakeOpNumberMatch(int16_t selected_param,
- uint32_t match,
- uint32_t options);
- // Creates an OpNumberMatch opcode (void pointers are cast to numbers).
- // selected_param: index of the input argument. It must be an void* or the
- // evaluation result will generate a EVAL_ERROR.
- // match: the pointer numeric value to compare against selected_param.
- PolicyOpcode* MakeOpVoidPtrMatch(int16_t selected_param,
- const void* match,
- uint32_t options);
- // Creates an OpNumberMatchRange opcode using the memory passed in the ctor.
- // selected_param: index of the input argument. It must be a uint32_t or the
- // evaluation result will generate a EVAL_ERROR.
- // lower_bound, upper_bound: the range to compare against selected_param.
- PolicyOpcode* MakeOpNumberMatchRange(int16_t selected_param,
- uint32_t lower_bound,
- uint32_t upper_bound,
- uint32_t options);
- // Creates an OpWStringMatch opcode using the raw memory passed in the ctor.
- // selected_param: index of the input argument. It must be a wide string
- // pointer or the evaluation result will generate a EVAL_ERROR.
- // match_str: string to compare against selected_param.
- // start_position: when its value is from 0 to < 0x7fff it indicates an
- // offset from the selected_param string where to perform the comparison. If
- // the value is SeekForward then a substring search is performed. If the
- // value is SeekToEnd the comparison is performed against the last part of
- // the selected_param string.
- // Note that the range in the position (0 to 0x7fff) is dictated by the
- // current implementation.
- // match_opts: Indicates additional matching flags. Currently CaseInsensitive
- // is supported.
- PolicyOpcode* MakeOpWStringMatch(int16_t selected_param,
- const wchar_t* match_str,
- int start_position,
- StringMatchOptions match_opts,
- uint32_t options);
- // Creates an OpNumberAndMatch opcode using the raw memory passed in the ctor.
- // selected_param: index of the input argument. It must be uint32_t or the
- // evaluation result will generate a EVAL_ERROR.
- // match: the value to bitwise AND against selected_param.
- PolicyOpcode* MakeOpNumberAndMatch(int16_t selected_param,
- uint32_t match,
- uint32_t options);
- private:
- // Constructs the common part of every opcode. selected_param is the index
- // of the input param to use when evaluating the opcode. Pass -1 in
- // selected_param to indicate that no input parameter is required.
- PolicyOpcode* MakeBase(OpcodeID opcode_id,
- uint32_t options,
- int16_t selected_param);
- // Allocates (and copies) a string (of size length) inside the buffer and
- // returns the displacement with respect to start.
- ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t length);
- // Points to the lowest currently available address of the memory
- // used to make the opcodes. This pointer increments as opcodes are made.
- raw_ptr<char> memory_top_;
- // Points to the highest currently available address of the memory
- // used to make the opcodes. This pointer decrements as opcode strings are
- // allocated.
- raw_ptr<char> memory_bottom_;
- };
- } // namespace sandbox
- #endif // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
|