// 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_SRC_POLICY_ENGINE_PROCESSOR_H__
#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__

#include <stddef.h>
#include <stdint.h>

#include "base/macros.h"
#include "sandbox/win/src/policy_engine_opcodes.h"
#include "sandbox/win/src/policy_engine_params.h"

namespace sandbox {

// This header contains the core policy evaluator. In its simplest form
// it evaluates a stream of opcodes assuming that they are laid out in
// memory as opcode groups.
//
// An opcode group has N comparison opcodes plus 1 action opcode. For
// example here we have 3 opcode groups (A, B,C):
//
// [comparison 1]  <-- group A start
// [comparison 2]
// [comparison 3]
// [action A    ]
// [comparison 1]  <-- group B start
// [action B    ]
// [comparison 1]  <-- group C start
// [comparison 2]
// [action C    ]
//
// The opcode evaluator proceeds from the top, evaluating each opcode in
// sequence. An opcode group is evaluated until the first comparison that
// returns false. At that point the rest of the group is skipped and evaluation
// resumes with the first comparison of the next group. When all the comparisons
// in a group have evaluated to true and the action is reached. The group is
// considered a matching group.
//
// In the 'ShortEval' mode evaluation stops when it reaches the end or the first
// matching group. The action opcode from this group is the resulting policy
// action.
//
// In the 'RankedEval' mode evaluation stops only when it reaches the end of the
// the opcode stream. In the process all matching groups are saved and at the
// end the 'best' group is selected (what makes the best is TBD) and the action
// from this group is the resulting policy action.
//
// As explained above, the policy evaluation of a group is a logical AND of
// the evaluation of each opcode. However an opcode can request kPolUseOREval
// which makes the evaluation to use logical OR. Given that each opcode can
// request its evaluation result to be negated with kPolNegateEval you can
// achieve the negation of the total group evaluation. This means that if you
// need to express:
//             if (!(c1 && c2 && c3))
// You can do it by:
//             if ((!c1) || (!c2) || (!c3))
//

// Possible outcomes of policy evaluation.
enum PolicyResult {
  NO_POLICY_MATCH,
  POLICY_MATCH,
  POLICY_ERROR
};

// Policy evaluation flags
// TODO(cpu): implement the options kStopOnErrors & kRankedEval.
//
// Stop evaluating as soon as an error is encountered.
const uint32_t kStopOnErrors = 1;
// Ignore all non fatal opcode evaluation errors.
const uint32_t kIgnoreErrors = 2;
// Short-circuit evaluation: Only evaluate until opcode group that
// evaluated to true has been found.
const uint32_t kShortEval = 4;
// Discussed briefly at the policy design meeting. It will evaluate
// all rules and then return the 'best' rule that evaluated true.
const uint32_t kRankedEval = 8;

// This class evaluates a policy-opcode stream given the memory where the
// opcodes are and an input 'parameter set'.
//
// This class is designed to be callable from interception points
// as low as the NtXXXX service level (it is not currently safe, but
// it is designed to be made safe).
//
// Its usage in an interception is:
//
//   POLPARAMS_BEGIN(eval_params)
//     POLPARAM(param1)
//     POLPARAM(param2)
//     POLPARAM(param3)
//     POLPARAM(param4)
//     POLPARAM(param5)
//   POLPARAMS_END;
//
//   PolicyProcessor pol_evaluator(policy_memory);
//   PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params,
//                                            _countof(eval_params));
//   if (NO_POLICY_MATCH == pr) {
//     EvalResult policy_action =  pol_evaluator.GetAction();
//     // apply policy here...
//   }
//
// Where the POLPARAM() arguments are derived from the intercepted function
// arguments, and represent all the 'interesting' policy inputs, and
// policy_memory is a memory buffer containing the opcode stream that is the
// relevant policy for this intercept.
class PolicyProcessor {
 public:
  // policy_buffer contains opcodes made with OpcodeFactory. They are usually
  // created in the broker process and evaluated in the target process.

  // This constructor is just a variant of the previous constructor.
  explicit PolicyProcessor(PolicyBuffer* policy)
      : policy_(policy) {
    SetInternalState(0, EVAL_FALSE);
  }

  // Evaluates a policy-opcode stream. See the comments at the top of this
  // class for more info. Returns POLICY_MATCH if a rule set was found that
  // matches an active policy.
  PolicyResult Evaluate(uint32_t options,
                        ParameterSet* parameters,
                        size_t parameter_count);

  // If the result of Evaluate() was POLICY_MATCH, calling this function returns
  // the recommended policy action.
  EvalResult GetAction() const;

 private:
  struct {
    size_t current_index_;
    EvalResult current_result_;
  } state_;

  // Sets the currently matching action result.
  void SetInternalState(size_t index, EvalResult result);

  PolicyBuffer* policy_;
  DISALLOW_COPY_AND_ASSIGN(PolicyProcessor);
};

}  // namespace sandbox

#endif  // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
