blob: 0e6e650f88e6aadd46bec96a8b41768c37d7cc6a [file] [log] [blame] [edit]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sandbox/mac/sandbox_serializer.h"
#include <optional>
#include <string>
#include <vector>
#include "sandbox/mac/sandbox_logging.h"
#include "sandbox/mac/seatbelt.h"
namespace sandbox {
namespace {
// TODO(crbug.com/393161124): replace with different encoding.
void EncodeVarInt(uint64_t from, std::string* into) {
do {
unsigned char c = from & 0x7f;
from >>= 7;
if (from) {
c |= 0x80;
}
into->push_back(c);
} while (from);
}
bool DecodeVarInt(std::string_view* from, uint64_t* into) {
std::string_view::const_iterator it = from->begin();
int shift = 0;
uint64_t ret = 0;
do {
// Shifting 32 or more bits is undefined behavior.
if (it == from->end() || shift >= 32) {
return false;
}
unsigned char c = *it;
ret |= static_cast<uint64_t>(c & 0x7f) << shift;
shift += 7;
} while (*it++ & 0x80);
*into = ret;
from->remove_prefix(it - from->begin());
return true;
}
void EncodeString(const std::string& value, std::string* into) {
EncodeVarInt(value.length(), into);
into->append(value);
}
bool DecodeString(std::string_view* slice, std::string* value) {
uint64_t length;
if (!DecodeVarInt(slice, &length) || length < 0) {
return false;
}
size_t size = length;
if (slice->size() < size) {
return false;
}
value->assign(slice->data(), size);
slice->remove_prefix(size);
return true;
}
} // namespace
SandboxSerializer::SandboxSerializer(Target mode) : mode_(mode) {
if (mode_ == Target::kCompiled) {
params_ = Seatbelt::Parameters::Create();
}
}
SandboxSerializer::~SandboxSerializer() = default;
SandboxSerializer::DeserializedPolicy::DeserializedPolicy() = default;
SandboxSerializer::DeserializedPolicy::DeserializedPolicy(
DeserializedPolicy&& other) = default;
SandboxSerializer::DeserializedPolicy::~DeserializedPolicy() = default;
void SandboxSerializer::SetProfile(const std::string& profile) {
profile_ = profile;
}
bool SandboxSerializer::SetBooleanParameter(const std::string& key,
bool value) {
return SetParameter(key, value ? "TRUE" : "FALSE");
}
bool SandboxSerializer::SetParameter(const std::string& key,
const std::string& value) {
// Regardless of the mode, add the strings to the map because
// Seatbelt::Parameters::Set does not copy the strings, which means temporary
// std::string references need to be owned somewhere.
auto it = source_params_.insert({key, value});
if (mode_ == Target::kCompiled && it.second) {
if (!params_.Set(it.first->first.c_str(), it.first->second.c_str())) {
source_params_.erase(it.first);
return false;
}
}
return it.second;
}
bool SandboxSerializer::SerializePolicy(std::string& serialized_policy,
std::string& error) {
constexpr size_t kBytesForModeVarInt = 1;
constexpr size_t kMaxBytesForVarInt = (sizeof(uint64_t) * 8 + 6) / 7;
serialized_policy.clear();
switch (mode_) {
case Target::kSource:
serialized_policy.reserve(kBytesForModeVarInt + kMaxBytesForVarInt +
profile_.size() + 512 * source_params_.size());
EncodeVarInt(static_cast<int>(Target::kSource), &serialized_policy);
EncodeString(profile_, &serialized_policy);
for (const auto& [key, value] : source_params_) {
EncodeString(key, &serialized_policy);
EncodeString(value, &serialized_policy);
}
serialized_policy.shrink_to_fit();
return true;
case Target::kCompiled:
std::string compiled;
if (!Seatbelt::Compile(profile_.c_str(), params_, compiled, &error)) {
return false;
}
serialized_policy.reserve(kBytesForModeVarInt + kMaxBytesForVarInt +
compiled.size());
EncodeVarInt(static_cast<int>(Target::kCompiled), &serialized_policy);
EncodeString(compiled, &serialized_policy);
return true;
}
}
// static
std::optional<SandboxSerializer::DeserializedPolicy>
SandboxSerializer::DeserializePolicy(const std::string& serialized_policy,
std::string& error) {
std::string_view remaining_serialized_policy = serialized_policy;
uint64_t mode;
if (!DecodeVarInt(&remaining_serialized_policy, &mode)) {
error = "unexpected serialized policy mode";
return std::nullopt;
}
if (mode > static_cast<uint64_t>(Target::kMaxValue)) {
error = "unexpected policy mode";
return std::nullopt;
}
DeserializedPolicy deserialized_policy;
deserialized_policy.mode = static_cast<Target>(mode);
switch (deserialized_policy.mode) {
case Target::kCompiled:
if (!DecodeString(&remaining_serialized_policy,
&(deserialized_policy.profile))) {
error = "could not decode compiled policy string";
return std::nullopt;
}
break;
case Target::kSource:
std::string profile;
if (!DecodeString(&remaining_serialized_policy,
&(deserialized_policy.profile))) {
error = "could not decode source mode profile string";
return std::nullopt;
}
while (!remaining_serialized_policy.empty()) {
std::string key;
if (!DecodeString(&remaining_serialized_policy, &key)) {
error = "could not decode source mode parameter key";
return std::nullopt;
}
deserialized_policy.params.push_back(key);
std::string value;
if (!DecodeString(&remaining_serialized_policy, &value)) {
error = "could not decode source mode parameter value";
return std::nullopt;
}
deserialized_policy.params.push_back(value);
}
break;
}
return deserialized_policy;
}
// static
bool SandboxSerializer::ApplySerializedPolicy(
const std::string& serialized_policy) {
std::string error;
std::optional<DeserializedPolicy> deserialized_policy =
DeserializePolicy(serialized_policy, error);
if (!deserialized_policy) {
logging::Error("SandboxSerializer: Failed to deserialize policy: %s",
error.c_str());
return false;
}
switch (deserialized_policy->mode) {
case Target::kCompiled:
if (!Seatbelt::ApplyCompiledProfile(deserialized_policy->profile,
&error)) {
logging::Error("SandboxSerializer: Failed to apply compiled policy: %s",
error.c_str());
return false;
}
break;
case Target::kSource:
if (!Seatbelt::InitWithParams(deserialized_policy->profile, 0,
deserialized_policy->params, &error)) {
logging::Error(
"SandboxSerializer: Failed to initialize sandbox with source mode "
"policy: %s",
error.c_str());
return false;
}
break;
}
return true;
}
} // namespace sandbox