policy_engine_opcodes.h 16 KB

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