Commit 9a8fdbed authored by Alex Gough's avatar Alex Gough Committed by Commit Bot

Adds diagnostics for sandbox policy rules.

This copies policy rule opcodes when snapshotting policies, and
serializes them for display in chrome://sandbox's raw view.

Example rule:

  "NtQueryAttributesFile": [
    "!(p[1] & 1) && !(prefix(p[0], '\\??\\')) -> askBroker",
    "!(p[1] & 1) && scan(p[0], '~') -> askBroker",
    "prefix_i(p[0], '\\??\\pipe\\chrome.') -> askBroker",
    "prefix_i(p[0], '\\??\\C:\\src\\chromium\\src\\out\\release-x86\\') && ends_i(p[0], '.pdb') -> askBroker"
  ],


Bug: 997273
Change-Id: I54f82b0a9523fa1ed94cecf982f2b25203ae7351
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1872304
Commit-Queue: Alex Gough <ajgo@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710437}
parent 30815427
......@@ -195,6 +195,9 @@ class PolicyOpcode {
// Sets the stored options such as kPolNegateEval.
void SetOptions(uint32_t options) { options_ = options; }
// Returns the parameter of the function the opcode concerns.
uint16_t GetParameter() const { return parameter_; }
private:
static const size_t kArgumentCount = 4; // The number of supported argument.
......
......@@ -13,5 +13,6 @@ extern const char kJobLevel[] = "jobLevel";
extern const char kLockdownLevel[] = "lockdownLevel";
extern const char kLowboxSid[] = "lowboxSid";
extern const char kPlatformMitigations[] = "platformMitigations";
extern const char kPolicyRules[] = "policyRules";
extern const char kProcessIds[] = "processIds";
} // namespace sandbox
......@@ -14,8 +14,8 @@ extern const char kJobLevel[];
extern const char kLockdownLevel[];
extern const char kLowboxSid[];
extern const char kPlatformMitigations[];
extern const char kPolicyRules[];
extern const char kProcessIds[];
} // namespace sandbox
#endif // SANDBOX_WIN_SRC_SANDBOX_CONSTANTS_H_
......@@ -17,6 +17,8 @@
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "sandbox/win/src/ipc_tags.h"
#include "sandbox/win/src/policy_engine_opcodes.h"
#include "sandbox/win/src/sandbox_constants.h"
#include "sandbox/win/src/sandbox_policy_base.h"
#include "sandbox/win/src/target_process.h"
......@@ -53,7 +55,6 @@ std::string GetTokenLevelInEnglish(TokenLevel token) {
case USER_RESTRICTED_NON_ADMIN:
return "Restricted Non Admin";
case USER_LAST:
default:
DCHECK(false) << "Unknown TokenType";
return "Unknown";
}
......@@ -73,9 +74,6 @@ std::string GetJobLevelInEnglish(JobLevel job) {
return "Unprotected";
case JOB_NONE:
return "None";
default:
DCHECK(false) << "Unknown JobLevel";
return "Unknown";
}
}
......@@ -97,9 +95,6 @@ std::string GetIntegrityLevelInEnglish(IntegrityLevel integrity) {
return "S-1-16-0 Untrusted";
case INTEGRITY_LEVEL_LAST:
return "Default";
default:
DCHECK(false) << "Unknown IntegrityLevel";
return "Unknown";
}
}
......@@ -128,6 +123,244 @@ std::string GetPlatformMitigationsAsHex(MitigationFlags mitigations) {
return base::StringPrintf("%016" PRIx64, platform_flags[0]);
}
std::string GetIpcTagAsString(IpcTag service) {
switch (service) {
case IpcTag::UNUSED:
DCHECK(false) << "Unused IpcTag";
return "Unused";
case IpcTag::PING1:
return "Ping1";
case IpcTag::PING2:
return "Ping2";
case IpcTag::NTCREATEFILE:
return "NtCreateFile";
case IpcTag::NTOPENFILE:
return "NtOpenFile";
case IpcTag::NTQUERYATTRIBUTESFILE:
return "NtQueryAttributesFile";
case IpcTag::NTQUERYFULLATTRIBUTESFILE:
return "NtQueryFullAttributesFile";
case IpcTag::NTSETINFO_RENAME:
return "NtSetInfoRename";
case IpcTag::CREATENAMEDPIPEW:
return "CreateNamedPipeW";
case IpcTag::NTOPENTHREAD:
return "NtOpenThread";
case IpcTag::NTOPENPROCESS:
return "NtOpenProcess";
case IpcTag::NTOPENPROCESSTOKEN:
return "NtOpenProcessToken";
case IpcTag::NTOPENPROCESSTOKENEX:
return "NtOpenProcessTokenEx";
case IpcTag::CREATEPROCESSW:
return "CreateProcessW";
case IpcTag::CREATEEVENT:
return "CreateEvent";
case IpcTag::OPENEVENT:
return "OpenEvent";
case IpcTag::NTCREATEKEY:
return "NtCreateKey";
case IpcTag::NTOPENKEY:
return "NtOpenKey";
case IpcTag::GDI_GDIDLLINITIALIZE:
return "GdiDllInitialize";
case IpcTag::GDI_GETSTOCKOBJECT:
return "GetStockObject";
case IpcTag::USER_REGISTERCLASSW:
return "RegisterClassW";
case IpcTag::CREATETHREAD:
return "CreateThread";
case IpcTag::USER_ENUMDISPLAYMONITORS:
return "EnumDisplayMonitors";
case IpcTag::USER_ENUMDISPLAYDEVICES:
return "EnumDisplayDevices";
case IpcTag::USER_GETMONITORINFO:
return "GetMonitorInfo";
case IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS:
return "CreateOPMProtectedOutputs";
case IpcTag::GDI_GETCERTIFICATE:
return "GetCertificate";
case IpcTag::GDI_GETCERTIFICATESIZE:
return "GetCertificateSize";
case IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT:
return "DestroyOPMProtectedOutput";
case IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT:
return "ConfigureOPMProtectedOutput";
case IpcTag::GDI_GETOPMINFORMATION:
return "GetOPMInformation";
case IpcTag::GDI_GETOPMRANDOMNUMBER:
return "GetOPMRandomNumber";
case IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE:
return "GetSuggestedOPMProtectedOutputArraySize";
case IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS:
return "SetOPMSigningKeyAndSequenceNumbers";
case IpcTag::NTCREATESECTION:
return "NtCreateSection";
case IpcTag::LAST:
DCHECK(false) << "Unknown IpcTag";
return "Unknown";
}
}
std::string GetOpcodeAction(EvalResult action) {
switch (action) {
case EVAL_TRUE:
return "true";
case EVAL_FALSE:
return "false";
case EVAL_ERROR:
return "error";
case ASK_BROKER:
return "askBroker";
case DENY_ACCESS:
return "deny";
case GIVE_READONLY:
return "readonly";
case GIVE_ALLACCESS:
return "allaccess";
case GIVE_CACHED:
return "cached";
case GIVE_FIRST:
return "first";
case SIGNAL_ALARM:
return "alarm";
case FAKE_SUCCESS:
return "fakeSuccess";
case FAKE_ACCESS_DENIED:
return "fakeDenied";
case TERMINATE_PROCESS:
return "terminate";
}
}
std::string GetStringMatchOperation(int pos, uint32_t options) {
if (pos == 0) {
if (options & EXACT_LENGTH)
return "exact";
else
return "prefix";
} else if (pos < 0) {
return "scan";
} else if (pos == kSeekToEnd) {
return "ends";
} else {
DCHECK(false) << "Invalid pos (" << pos << ")";
return "unknown";
}
}
std::string GetPolicyOpcode(const PolicyOpcode* opcode, bool continuation) {
// See |policy_engine_opcodes.cc|.
uint32_t args[4];
auto options = opcode->GetOptions();
auto param = opcode->GetParameter();
std::string condition;
if (options & kPolNegateEval)
condition += "!(";
switch (opcode->GetID()) {
case OP_ALWAYS_FALSE:
condition += "false";
break;
case OP_ALWAYS_TRUE:
condition += "true";
break;
case OP_NUMBER_MATCH:
opcode->GetArgument(1, &args[1]);
if (args[1] == UINT32_TYPE) {
opcode->GetArgument(0, &args[0]);
condition += base::StringPrintf("p[%d] == %x", param, args[0]);
} else {
const void* match_ptr = nullptr;
opcode->GetArgument(0, &match_ptr);
condition += base::StringPrintf("p[%d] == %p", param, match_ptr);
}
break;
case OP_NUMBER_MATCH_RANGE:
opcode->GetArgument(0, &args[0]);
opcode->GetArgument(1, &args[1]);
condition +=
base::StringPrintf("%x <= p[%d] <= %x", args[0], param, args[1]);
break;
case OP_NUMBER_AND_MATCH:
opcode->GetArgument(0, &args[0]);
condition += base::StringPrintf("p[%d] & %x", param, args[0]);
break;
case OP_WSTRING_MATCH: {
int pos;
opcode->GetArgument(1, &args[1]); // Length.
opcode->GetArgument(2, &pos); // Position.
opcode->GetArgument(3, &args[3]); // Options.
// These are not nul-terminated so we have to wrap them here.
auto match_string = std::wstring(opcode->GetRelativeString(0), 0,
static_cast<size_t>(args[1]));
condition += GetStringMatchOperation(pos, args[3]);
if (args[3] & CASE_INSENSITIVE)
condition += "_i";
condition +=
base::StringPrintf("(p[%d], '%S')", param, match_string.c_str());
} break;
case OP_ACTION:
opcode->GetArgument(0, &args[0]);
condition += GetOpcodeAction(static_cast<EvalResult>(args[0]));
break;
default:
DCHECK(false) << "Unknown Opcode";
return "Unknown";
}
if (options & kPolNegateEval)
condition += ")";
// If there is another rule add a joining token.
if (continuation) {
if (options & kPolUseOREval)
condition += " || ";
else
condition += " && ";
}
return condition;
}
// Uses |service| to index into |policy_rules| returning a list of opcodes.
base::Value GetPolicyOpcodes(const PolicyGlobal* policy_rules, IpcTag service) {
base::Value entry(base::Value::Type::LIST);
PolicyBuffer* policy_buffer =
policy_rules->entry[static_cast<size_t>(service)];
// Build up rules and emit when we hit an action.
std::string cur_rule;
for (size_t i = 0; i < policy_buffer->opcode_count; i++) {
const PolicyOpcode* opcode = &policy_buffer->opcodes[i];
if (opcode->GetID() != OP_ACTION) {
DCHECK(i + 1 < policy_buffer->opcode_count)
<< "Non-actions should not terminate rules";
bool peak = policy_buffer->opcodes[i + 1].GetID() != OP_ACTION;
cur_rule += GetPolicyOpcode(opcode, peak);
} else {
cur_rule += " -> ";
cur_rule += GetPolicyOpcode(opcode, false);
entry.GetList().push_back(base::Value(cur_rule));
cur_rule.clear();
}
}
return entry;
}
base::Value GetPolicyRules(const PolicyGlobal* policy_rules) {
DCHECK(policy_rules);
base::Value results(base::Value::Type::DICTIONARY);
for (size_t i = 0; i < kMaxServiceCount; i++) {
if (!policy_rules->entry[i])
continue;
IpcTag service = static_cast<IpcTag>(i);
results.SetKey(GetIpcTagAsString(service),
GetPolicyOpcodes(policy_rules, service));
}
return results;
}
} // namespace
// We are a friend of PolicyBase so that we can steal its private members
......@@ -158,6 +391,24 @@ PolicyDiagnostic::PolicyDiagnostic(PolicyBase* policy) {
std::make_unique<Sid>(policy->app_container_profile_->GetPackageSid());
if (policy->lowbox_sid_)
lowbox_sid_ = std::make_unique<Sid>(policy->lowbox_sid_);
if (policy->policy_) {
size_t policy_mem_size = policy->policy_->data_size + sizeof(PolicyGlobal);
policy_rules_.reset(
static_cast<sandbox::PolicyGlobal*>(::operator new(policy_mem_size)));
memcpy(policy_rules_.get(), policy->policy_, policy_mem_size);
// Fixup pointers (see |PolicyGlobal| in policy_low_level.h).
PolicyBuffer** original_entries = policy->policy_->entry;
PolicyBuffer** copy_base = policy_rules_->entry;
for (size_t i = 0; i < kMaxServiceCount; i++) {
if (policy_rules_->entry[i]) {
policy_rules_->entry[i] = reinterpret_cast<PolicyBuffer*>(
reinterpret_cast<char*>(copy_base) +
(reinterpret_cast<char*>(original_entries[i]) -
reinterpret_cast<char*>(original_entries)));
}
}
}
}
PolicyDiagnostic::~PolicyDiagnostic() = default;
......@@ -187,6 +438,9 @@ const char* PolicyDiagnostic::JsonString() {
if (lowbox_sid_)
value.SetKey(kLowboxSid, base::Value(GetSidAsString(lowbox_sid_.get())));
if (policy_rules_)
value.SetKey(kPolicyRules, GetPolicyRules(policy_rules_.get()));
auto json_string = std::make_unique<std::string>();
JSONStringValueSerializer to_json(json_string.get());
CHECK(to_json.Serialize(value));
......
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/values.h"
#include "sandbox/win/src/policy_low_level.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/security_level.h"
......@@ -42,6 +43,7 @@ class PolicyDiagnostic final : public PolicyInfo {
MitigationFlags desired_mitigations_ = 0;
std::unique_ptr<Sid> app_container_sid_ = nullptr;
std::unique_ptr<Sid> lowbox_sid_ = nullptr;
std::unique_ptr<PolicyGlobal> policy_rules_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(PolicyDiagnostic);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment