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