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