security/sandbox/win/src/policy_engine_opcodes.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2010 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 #ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
michael@0 6 #define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
michael@0 7
michael@0 8 #include "sandbox/win/src/policy_engine_params.h"
michael@0 9 #include "base/basictypes.h"
michael@0 10
michael@0 11 // The low-level policy is implemented using the concept of policy 'opcodes'.
michael@0 12 // An opcode is a structure that contains enough information to perform one
michael@0 13 // comparison against one single input parameter. For example, an opcode can
michael@0 14 // encode just one of the following comparison:
michael@0 15 //
michael@0 16 // - Is input parameter 3 not equal to NULL?
michael@0 17 // - Does input parameter 2 start with L"c:\\"?
michael@0 18 // - Is input parameter 5, bit 3 is equal 1?
michael@0 19 //
michael@0 20 // Each opcode is in fact equivalent to a function invocation where all
michael@0 21 // the parameters are known by the opcode except one. So say you have a
michael@0 22 // function of this form:
michael@0 23 // bool fn(a, b, c, d) with 4 arguments
michael@0 24 //
michael@0 25 // Then an opcode is:
michael@0 26 // op(fn, b, c, d)
michael@0 27 // Which stores the function to call and its 3 last arguments
michael@0 28 //
michael@0 29 // Then and opcode evaluation is:
michael@0 30 // op.eval(a) ------------------------> fn(a,b,c,d)
michael@0 31 // internally calls
michael@0 32 //
michael@0 33 // The idea is that complex policy rules can be split into streams of
michael@0 34 // opcodes which are evaluated in sequence. The evaluation is done in
michael@0 35 // groups of opcodes that have N comparison opcodes plus 1 action opcode:
michael@0 36 //
michael@0 37 // [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
michael@0 38 // ----- evaluation order----------->
michael@0 39 //
michael@0 40 // Each opcode group encodes one high-level policy rule. The rule applies
michael@0 41 // only if all the conditions on the group evaluate to true. The action
michael@0 42 // opcode contains the policy outcome for that particular rule.
michael@0 43 //
michael@0 44 // Note that this header contains the main building blocks of low-level policy
michael@0 45 // but not the low level policy class.
michael@0 46 namespace sandbox {
michael@0 47
michael@0 48 // These are the possible policy outcomes. Note that some of them might
michael@0 49 // not apply and can be removed. Also note that The following values only
michael@0 50 // specify what to do, not how to do it and it is acceptable given specific
michael@0 51 // cases to ignore the policy outcome.
michael@0 52 enum EvalResult {
michael@0 53 // Comparison opcode values:
michael@0 54 EVAL_TRUE, // Opcode condition evaluated true.
michael@0 55 EVAL_FALSE, // Opcode condition evaluated false.
michael@0 56 EVAL_ERROR, // Opcode condition generated an error while evaluating.
michael@0 57 // Action opcode values:
michael@0 58 ASK_BROKER, // The target must generate an IPC to the broker. On the broker
michael@0 59 // side, this means grant access to the resource.
michael@0 60 DENY_ACCESS, // No access granted to the resource.
michael@0 61 GIVE_READONLY, // Give readonly access to the resource.
michael@0 62 GIVE_ALLACCESS, // Give full access to the resource.
michael@0 63 GIVE_CACHED, // IPC is not required. Target can return a cached handle.
michael@0 64 GIVE_FIRST, // TODO(cpu)
michael@0 65 SIGNAL_ALARM, // Unusual activity. Generate an alarm.
michael@0 66 FAKE_SUCCESS, // Do not call original function. Just return 'success'.
michael@0 67 FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
michael@0 68 // and do not do IPC.
michael@0 69 TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
michael@0 70 };
michael@0 71
michael@0 72 // The following are the implemented opcodes.
michael@0 73 enum OpcodeID {
michael@0 74 OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
michael@0 75 OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
michael@0 76 OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
michael@0 77 OP_ULONG_MATCH_RANGE, // Match an ulong integer as a <= n <= b.
michael@0 78 OP_ULONG_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
michael@0 79 OP_WSTRING_MATCH, // Match a string for equality.
michael@0 80 OP_ACTION // Evaluates to an action opcode.
michael@0 81 };
michael@0 82
michael@0 83 // Options that apply to every opcode. They are specified when creating
michael@0 84 // each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
michael@0 85 // Do nothing special.
michael@0 86 const uint32 kPolNone = 0;
michael@0 87
michael@0 88 // Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
michael@0 89 // negated conditions such as if ( a && !b).
michael@0 90 const uint32 kPolNegateEval = 1;
michael@0 91
michael@0 92 // Zero the MatchContext context structure. This happens after the opcode
michael@0 93 // is evaluated.
michael@0 94 const uint32 kPolClearContext = 2;
michael@0 95
michael@0 96 // Use OR when evaluating this set of opcodes. The policy evaluator by default
michael@0 97 // uses AND when evaluating. Very helpful when
michael@0 98 // used with kPolNegateEval. For example if you have a condition best expressed
michael@0 99 // as if(! (a && b && c)), the use of this flags allows it to be expressed as
michael@0 100 // if ((!a) || (!b) || (!c)).
michael@0 101 const uint32 kPolUseOREval = 4;
michael@0 102
michael@0 103 // Keeps the evaluation state between opcode evaluations. This is used
michael@0 104 // for string matching where the next opcode needs to continue matching
michael@0 105 // from the last character position from the current opcode. The match
michael@0 106 // context is preserved across opcode evaluation unless an opcode specifies
michael@0 107 // as an option kPolClearContext.
michael@0 108 struct MatchContext {
michael@0 109 size_t position;
michael@0 110 uint32 options;
michael@0 111
michael@0 112 MatchContext() {
michael@0 113 Clear();
michael@0 114 }
michael@0 115
michael@0 116 void Clear() {
michael@0 117 position = 0;
michael@0 118 options = 0;
michael@0 119 }
michael@0 120 };
michael@0 121
michael@0 122 // Models a policy opcode; that is a condition evaluation were all the
michael@0 123 // arguments but one are stored in objects of this class. Use OpcodeFactory
michael@0 124 // to create objects of this type.
michael@0 125 // This class is just an implementation artifact and not exposed to the
michael@0 126 // API clients or visible in the intercepted service. Internally, an
michael@0 127 // opcode is just:
michael@0 128 // - An integer that identifies the actual opcode.
michael@0 129 // - An index to indicate which one is the input argument
michael@0 130 // - An array of arguments.
michael@0 131 // While an OO hierarchy of objects would have been a natural choice, the fact
michael@0 132 // that 1) this code can execute before the CRT is loaded, presents serious
michael@0 133 // problems in terms of guarantees about the actual state of the vtables and
michael@0 134 // 2) because the opcode objects are generated in the broker process, we need to
michael@0 135 // use plain objects. To preserve some minimal type safety templates are used
michael@0 136 // when possible.
michael@0 137 class PolicyOpcode {
michael@0 138 friend class OpcodeFactory;
michael@0 139 public:
michael@0 140 // Evaluates the opcode. For a typical comparison opcode the return value
michael@0 141 // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
michael@0 142 // the return is EVAL_ERROR. If the opcode is an action opcode then the
michael@0 143 // return can take other values such as ASK_BROKER.
michael@0 144 // parameters: An array of all input parameters. This argument is normally
michael@0 145 // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
michael@0 146 // count: The number of parameters passed as first argument.
michael@0 147 // match: The match context that is persisted across the opcode evaluation
michael@0 148 // sequence.
michael@0 149 EvalResult Evaluate(const ParameterSet* parameters, size_t count,
michael@0 150 MatchContext* match);
michael@0 151
michael@0 152 // Retrieves a stored argument by index. Valid index values are
michael@0 153 // from 0 to < kArgumentCount.
michael@0 154 template <typename T>
michael@0 155 void GetArgument(size_t index, T* argument) const {
michael@0 156 COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
michael@0 157 *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
michael@0 158 }
michael@0 159
michael@0 160 // Sets a stored argument by index. Valid index values are
michael@0 161 // from 0 to < kArgumentCount.
michael@0 162 template <typename T>
michael@0 163 void SetArgument(size_t index, const T& argument) {
michael@0 164 COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
michael@0 165 *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
michael@0 166 }
michael@0 167
michael@0 168 // Retrieves the actual address of an string argument. When using
michael@0 169 // GetArgument() to retrieve an index that contains a string, the returned
michael@0 170 // value is just an offset to the actual string.
michael@0 171 // index: the stored string index. Valid values are from 0
michael@0 172 // to < kArgumentCount.
michael@0 173 const wchar_t* GetRelativeString(size_t index) const {
michael@0 174 ptrdiff_t str_delta = 0;
michael@0 175 GetArgument(index, &str_delta);
michael@0 176 const char* delta = reinterpret_cast<const char*>(this) + str_delta;
michael@0 177 return reinterpret_cast<const wchar_t*>(delta);
michael@0 178 }
michael@0 179
michael@0 180 // Returns true if this opcode is an action opcode without actually
michael@0 181 // evaluating it. Used to do a quick scan forward to the next opcode group.
michael@0 182 bool IsAction() const {
michael@0 183 return (OP_ACTION == opcode_id_);
michael@0 184 };
michael@0 185
michael@0 186 // Returns the opcode type.
michael@0 187 OpcodeID GetID() const {
michael@0 188 return opcode_id_;
michael@0 189 }
michael@0 190
michael@0 191 // Returns the stored options such as kPolNegateEval and others.
michael@0 192 uint32 GetOptions() const {
michael@0 193 return options_;
michael@0 194 }
michael@0 195
michael@0 196 // Sets the stored options such as kPolNegateEval.
michael@0 197 void SetOptions(int16 options) {
michael@0 198 options_ = options;
michael@0 199 }
michael@0 200
michael@0 201 private:
michael@0 202
michael@0 203 static const size_t kArgumentCount = 4; // The number of supported argument.
michael@0 204
michael@0 205 struct OpcodeArgument {
michael@0 206 UINT_PTR mem;
michael@0 207 };
michael@0 208
michael@0 209 // Better define placement new in the class instead of relying on the
michael@0 210 // global definition which seems to be fubared.
michael@0 211 void* operator new(size_t, void* location) {
michael@0 212 return location;
michael@0 213 }
michael@0 214
michael@0 215 // Helper function to evaluate the opcode. The parameters have the same
michael@0 216 // meaning that in Evaluate().
michael@0 217 EvalResult EvaluateHelper(const ParameterSet* parameters,
michael@0 218 MatchContext* match);
michael@0 219 OpcodeID opcode_id_;
michael@0 220 int16 parameter_;
michael@0 221 int16 options_;
michael@0 222 OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
michael@0 223 };
michael@0 224
michael@0 225 enum StringMatchOptions {
michael@0 226 CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
michael@0 227 CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
michael@0 228 EXACT_LENGHT = 2 // Don't do substring match. Do full string match.
michael@0 229 };
michael@0 230
michael@0 231 // Opcodes that do string comparisons take a parameter that is the starting
michael@0 232 // position to perform the comparison so we can do substring matching. There
michael@0 233 // are two special values:
michael@0 234 //
michael@0 235 // Start from the current position and compare strings advancing forward until
michael@0 236 // a match is found if any. Similar to CRT strstr().
michael@0 237 const int kSeekForward = -1;
michael@0 238 // Perform a match with the end of the string. It only does a single comparison.
michael@0 239 const int kSeekToEnd = 0xfffff;
michael@0 240
michael@0 241
michael@0 242 // A PolicyBuffer is a variable size structure that contains all the opcodes
michael@0 243 // that are to be created or evaluated in sequence.
michael@0 244 struct PolicyBuffer {
michael@0 245 size_t opcode_count;
michael@0 246 PolicyOpcode opcodes[1];
michael@0 247 };
michael@0 248
michael@0 249 // Helper class to create any opcode sequence. This class is normally invoked
michael@0 250 // only by the high level policy module or when you need to handcraft a special
michael@0 251 // policy.
michael@0 252 // The factory works by creating the opcodes using a chunk of memory given
michael@0 253 // in the constructor. The opcodes themselves are allocated from the beginning
michael@0 254 // (top) of the memory, while any string that an opcode needs is allocated from
michael@0 255 // the end (bottom) of the memory.
michael@0 256 //
michael@0 257 // In essence:
michael@0 258 //
michael@0 259 // low address ---> [opcode 1]
michael@0 260 // [opcode 2]
michael@0 261 // [opcode 3]
michael@0 262 // | | <--- memory_top_
michael@0 263 // | free |
michael@0 264 // | |
michael@0 265 // | | <--- memory_bottom_
michael@0 266 // [string 1]
michael@0 267 // high address --> [string 2]
michael@0 268 //
michael@0 269 // Note that this class does not keep track of the number of opcodes made and
michael@0 270 // it is designed to be a building block for low-level policy.
michael@0 271 //
michael@0 272 // Note that any of the MakeOpXXXXX member functions below can return NULL on
michael@0 273 // failure. When that happens opcode sequence creation must be aborted.
michael@0 274 class OpcodeFactory {
michael@0 275 public:
michael@0 276 // memory: base pointer to a chunk of memory where the opcodes are created.
michael@0 277 // memory_size: the size in bytes of the memory chunk.
michael@0 278 OpcodeFactory(char* memory, size_t memory_size)
michael@0 279 : memory_top_(memory) {
michael@0 280 memory_bottom_ = &memory_top_[memory_size];
michael@0 281 }
michael@0 282
michael@0 283 // policy: contains the raw memory where the opcodes are created.
michael@0 284 // memory_size: contains the actual size of the policy argument.
michael@0 285 OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
michael@0 286 memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
michael@0 287 memory_bottom_ = &memory_top_[memory_size];
michael@0 288 }
michael@0 289
michael@0 290 // Returns the available memory to make opcodes.
michael@0 291 size_t memory_size() const {
michael@0 292 return memory_bottom_ - memory_top_;
michael@0 293 }
michael@0 294
michael@0 295 // Creates an OpAlwaysFalse opcode.
michael@0 296 PolicyOpcode* MakeOpAlwaysFalse(uint32 options);
michael@0 297
michael@0 298 // Creates an OpAlwaysFalse opcode.
michael@0 299 PolicyOpcode* MakeOpAlwaysTrue(uint32 options);
michael@0 300
michael@0 301 // Creates an OpAction opcode.
michael@0 302 // action: The action to return when Evaluate() is called.
michael@0 303 PolicyOpcode* MakeOpAction(EvalResult action, uint32 options);
michael@0 304
michael@0 305 // Creates an OpNumberMatch opcode.
michael@0 306 // selected_param: index of the input argument. It must be a ulong or the
michael@0 307 // evaluation result will generate a EVAL_ERROR.
michael@0 308 // match: the number to compare against the selected_param.
michael@0 309 PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match,
michael@0 310 uint32 options);
michael@0 311
michael@0 312 // Creates an OpNumberMatch opcode (void pointers are cast to numbers).
michael@0 313 // selected_param: index of the input argument. It must be an void* or the
michael@0 314 // evaluation result will generate a EVAL_ERROR.
michael@0 315 // match: the pointer numeric value to compare against selected_param.
michael@0 316 PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match,
michael@0 317 uint32 options);
michael@0 318
michael@0 319 // Creates an OpUlongMatchRange opcode using the memory passed in the ctor.
michael@0 320 // selected_param: index of the input argument. It must be a ulong or the
michael@0 321 // evaluation result will generate a EVAL_ERROR.
michael@0 322 // lower_bound, upper_bound: the range to compare against selected_param.
michael@0 323 PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param,
michael@0 324 unsigned long lower_bound,
michael@0 325 unsigned long upper_bound,
michael@0 326 uint32 options);
michael@0 327
michael@0 328 // Creates an OpWStringMatch opcode using the raw memory passed in the ctor.
michael@0 329 // selected_param: index of the input argument. It must be a wide string
michael@0 330 // pointer or the evaluation result will generate a EVAL_ERROR.
michael@0 331 // match_str: string to compare against selected_param.
michael@0 332 // start_position: when its value is from 0 to < 0x7fff it indicates an
michael@0 333 // offset from the selected_param string where to perform the comparison. If
michael@0 334 // the value is SeekForward then a substring search is performed. If the
michael@0 335 // value is SeekToEnd the comparison is performed against the last part of
michael@0 336 // the selected_param string.
michael@0 337 // Note that the range in the position (0 to 0x7fff) is dictated by the
michael@0 338 // current implementation.
michael@0 339 // match_opts: Indicates additional matching flags. Currently CaseInsensitive
michael@0 340 // is supported.
michael@0 341 PolicyOpcode* MakeOpWStringMatch(int16 selected_param,
michael@0 342 const wchar_t* match_str,
michael@0 343 int start_position,
michael@0 344 StringMatchOptions match_opts,
michael@0 345 uint32 options);
michael@0 346
michael@0 347 // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor.
michael@0 348 // selected_param: index of the input argument. It must be ulong or the
michael@0 349 // evaluation result will generate a EVAL_ERROR.
michael@0 350 // match: the value to bitwise AND against selected_param.
michael@0 351 PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param,
michael@0 352 unsigned long match,
michael@0 353 uint32 options);
michael@0 354
michael@0 355 private:
michael@0 356 // Constructs the common part of every opcode. selected_param is the index
michael@0 357 // of the input param to use when evaluating the opcode. Pass -1 in
michael@0 358 // selected_param to indicate that no input parameter is required.
michael@0 359 PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options,
michael@0 360 int16 selected_param);
michael@0 361
michael@0 362 // Allocates (and copies) a string (of size length) inside the buffer and
michael@0 363 // returns the displacement with respect to start.
michael@0 364 ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght);
michael@0 365
michael@0 366 // Points to the lowest currently available address of the memory
michael@0 367 // used to make the opcodes. This pointer increments as opcodes are made.
michael@0 368 char* memory_top_;
michael@0 369
michael@0 370 // Points to the highest currently available address of the memory
michael@0 371 // used to make the opcodes. This pointer decrements as opcode strings are
michael@0 372 // allocated.
michael@0 373 char* memory_bottom_;
michael@0 374
michael@0 375 DISALLOW_COPY_AND_ASSIGN(OpcodeFactory);
michael@0 376 };
michael@0 377
michael@0 378 } // namespace sandbox
michael@0 379
michael@0 380 #endif // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_

mercurial