blob: defda777fffc0b1d2ac554222596762066d046fc [file] [log] [blame]
//
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <thread>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fruit/fruit.h>
#include <gflags/gflags.h>
#include <keymaster/android_keymaster.h>
#include <keymaster/contexts/pure_soft_keymaster_context.h>
#include <keymaster/soft_keymaster_logger.h>
#include <tss2/tss2_esys.h>
#include <tss2/tss2_rc.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/security/confui_sign.h"
#include "common/libs/security/gatekeeper_channel_sharedfd.h"
#include "common/libs/security/keymaster_channel_sharedfd.h"
#include "common/libs/transport/channel_sharedfd.h"
#include "host/commands/kernel_log_monitor/kernel_log_server.h"
#include "host/commands/kernel_log_monitor/utils.h"
#include "host/commands/secure_env/confui_sign_server.h"
#include "host/commands/secure_env/device_tpm.h"
#include "host/commands/secure_env/gatekeeper_responder.h"
#include "host/commands/secure_env/in_process_tpm.h"
#include "host/commands/secure_env/keymaster_responder.h"
#include "host/commands/secure_env/oemlock/oemlock.h"
#include "host/commands/secure_env/oemlock/oemlock_responder.h"
#include "host/commands/secure_env/proxy_keymaster_context.h"
#include "host/commands/secure_env/rust/kmr_ta.h"
#include "host/commands/secure_env/soft_gatekeeper.h"
#include "host/commands/secure_env/storage/insecure_json_storage.h"
#include "host/commands/secure_env/storage/storage.h"
#include "host/commands/secure_env/storage/tpm_storage.h"
#include "host/commands/secure_env/tpm_gatekeeper.h"
#include "host/commands/secure_env/tpm_keymaster_context.h"
#include "host/commands/secure_env/tpm_keymaster_enforcement.h"
#include "host/commands/secure_env/tpm_resource_manager.h"
#include "host/libs/config/known_paths.h"
#include "host/libs/config/logging.h"
DEFINE_int32(confui_server_fd, -1, "A named socket to serve confirmation UI");
DEFINE_int32(snapshot_control_fd, -1, "A named socket for snapshot operations");
DEFINE_int32(keymaster_fd_in, -1, "A pipe for keymaster communication");
DEFINE_int32(keymaster_fd_out, -1, "A pipe for keymaster communication");
DEFINE_int32(keymint_fd_in, -1, "A pipe for keymint communication");
DEFINE_int32(keymint_fd_out, -1, "A pipe for keymint communication");
DEFINE_int32(gatekeeper_fd_in, -1, "A pipe for gatekeeper communication");
DEFINE_int32(gatekeeper_fd_out, -1, "A pipe for gatekeeper communication");
DEFINE_int32(oemlock_fd_in, -1, "A pipe for oemlock communication");
DEFINE_int32(oemlock_fd_out, -1, "A pipe for oemlock communication");
DEFINE_int32(kernel_events_fd, -1,
"A pipe for monitoring events based on "
"messages written to the kernel log. This "
"is used by secure_env to monitor for "
"device reboots.");
DEFINE_string(tpm_impl, "in_memory",
"The TPM implementation. \"in_memory\" or \"host_device\"");
DEFINE_string(keymint_impl, "tpm",
"The KeyMint implementation. \"tpm\" or \"software\"");
DEFINE_string(gatekeeper_impl, "tpm",
"The gatekeeper implementation. \"tpm\" or \"software\"");
DEFINE_string(oemlock_impl, "tpm",
"The oemlock implementation. \"tpm\" or \"software\"");
namespace cuttlefish {
namespace {
// Copied from AndroidKeymaster4Device
constexpr size_t kOperationTableSize = 16;
// Dup a command line file descriptor into a SharedFD.
SharedFD DupFdFlag(gflags::int32 fd) {
CHECK(fd != -1);
SharedFD duped = SharedFD::Dup(fd);
CHECK(duped->IsOpen()) << "Could not dup output fd: " << duped->StrError();
// The original FD is intentionally kept open so that we can re-exec this
// process without having to do a bunch of argv book-keeping.
return duped;
}
// Re-launch this process with all the same flags it was originallys started
// with.
[[noreturn]] void ReExecSelf() {
// Allocate +1 entry for terminating nullptr.
std::vector<char*> argv(gflags::GetArgvs().size() + 1, nullptr);
for (size_t i = 0; i < gflags::GetArgvs().size(); ++i) {
argv[i] = strdup(gflags::GetArgvs()[i].c_str());
CHECK(argv[i] != nullptr) << "OOM";
}
execv(SecureEnvBinary().c_str(), argv.data());
char buf[128];
LOG(FATAL) << "Exec failed, secure_env is out of sync with the guest: "
<< errno << "(" << strerror_r(errno, buf, sizeof(buf)) << ")";
abort(); // LOG(FATAL) isn't marked as noreturn
}
// Spin up a thread that monitors for a kernel loaded event, then re-execs
// this process. This way, secure_env's boot tracking matches up with the guest.
std::thread StartKernelEventMonitor(SharedFD kernel_events_fd) {
return std::thread([kernel_events_fd]() {
while (kernel_events_fd->IsOpen()) {
auto read_result = monitor::ReadEvent(kernel_events_fd);
CHECK(read_result.has_value()) << kernel_events_fd->StrError();
if (read_result->event == monitor::Event::BootloaderLoaded) {
LOG(DEBUG) << "secure_env detected guest reboot, restarting.";
ReExecSelf();
}
}
});
}
fruit::Component<fruit::Required<gatekeeper::SoftGateKeeper, TpmGatekeeper,
TpmResourceManager>,
gatekeeper::GateKeeper, keymaster::KeymasterEnforcement>
ChooseGatekeeperComponent() {
if (FLAGS_gatekeeper_impl == "software") {
return fruit::createComponent()
.bind<gatekeeper::GateKeeper, gatekeeper::SoftGateKeeper>()
.registerProvider([]() -> keymaster::KeymasterEnforcement* {
return new keymaster::SoftKeymasterEnforcement(64, 64);
});
} else if (FLAGS_gatekeeper_impl == "tpm") {
return fruit::createComponent()
.bind<gatekeeper::GateKeeper, TpmGatekeeper>()
.registerProvider(
[](TpmResourceManager& resource_manager,
TpmGatekeeper& gatekeeper) -> keymaster::KeymasterEnforcement* {
return new TpmKeymasterEnforcement(resource_manager, gatekeeper);
});
} else {
LOG(FATAL) << "Invalid gatekeeper implementation: "
<< FLAGS_gatekeeper_impl;
abort();
}
}
fruit::Component<fruit::Required<TpmResourceManager>, oemlock::OemLock>
ChooseOemlockComponent() {
return fruit::createComponent()
.registerProvider([](TpmResourceManager& resource_manager) -> secure_env::Storage* {
if (FLAGS_oemlock_impl == "software") {
return new secure_env::InsecureJsonStorage("oemlock_insecure");
} else if (FLAGS_oemlock_impl == "tpm") {
return new secure_env::TpmStorage(resource_manager, "oemlock_secure");
} else {
LOG(FATAL) << "Invalid oemlock implementation: "
<< FLAGS_oemlock_impl;
abort();
}
})
.registerProvider([](secure_env::Storage& storage) -> oemlock::OemLock* {
return new oemlock::OemLock(storage);
});;
}
fruit::Component<TpmResourceManager, gatekeeper::GateKeeper,
oemlock::OemLock, keymaster::KeymasterEnforcement>
SecureEnvComponent() {
return fruit::createComponent()
.registerProvider([]() -> Tpm* { // fruit will take ownership
if (FLAGS_tpm_impl == "in_memory") {
return new InProcessTpm();
} else if (FLAGS_tpm_impl == "host_device") {
return new DeviceTpm("/dev/tpm0");
} else {
LOG(FATAL) << "Unknown TPM implementation: " << FLAGS_tpm_impl;
abort();
}
})
.registerProvider([](Tpm* tpm) {
if (tpm->TctiContext() == nullptr) {
LOG(FATAL) << "Unable to connect to TPM implementation.";
}
ESYS_CONTEXT* esys_ptr = nullptr;
std::unique_ptr<ESYS_CONTEXT, void (*)(ESYS_CONTEXT*)> esys(
nullptr, [](ESYS_CONTEXT* esys) { Esys_Finalize(&esys); });
auto rc = Esys_Initialize(&esys_ptr, tpm->TctiContext(), nullptr);
if (rc != TPM2_RC_SUCCESS) {
LOG(FATAL) << "Could not initialize esys: " << Tss2_RC_Decode(rc)
<< " (" << rc << ")";
}
esys.reset(esys_ptr);
return esys;
})
.registerProvider(
[](std::unique_ptr<ESYS_CONTEXT, void (*)(ESYS_CONTEXT*)>& esys) {
return new TpmResourceManager(
esys.get()); // fruit will take ownership
})
.registerProvider([](TpmResourceManager& resource_manager) {
return new secure_env::TpmStorage(resource_manager, "gatekeeper_secure");
})
.registerProvider([]() {
return new secure_env::InsecureJsonStorage("gatekeeper_insecure");
})
.registerProvider([](TpmResourceManager& resource_manager,
secure_env::TpmStorage& secure_storage,
secure_env::InsecureJsonStorage& insecure_storage) {
return new TpmGatekeeper(resource_manager, secure_storage,
insecure_storage);
})
.registerProvider([]() { return new gatekeeper::SoftGateKeeper(); })
.install(ChooseGatekeeperComponent)
.install(ChooseOemlockComponent);
}
} // namespace
Result<void> SecureEnvMain(int argc, char** argv) {
DefaultSubprocessLogging(argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
keymaster::SoftKeymasterLogger km_logger;
fruit::Injector<TpmResourceManager, gatekeeper::GateKeeper,
oemlock::OemLock, keymaster::KeymasterEnforcement>
injector(SecureEnvComponent);
TpmResourceManager* resource_manager = injector.get<TpmResourceManager*>();
gatekeeper::GateKeeper* gatekeeper = injector.get<gatekeeper::GateKeeper*>();
oemlock::OemLock* oemlock = injector.get<oemlock::OemLock*>();
keymaster::KeymasterEnforcement* keymaster_enforcement =
injector.get<keymaster::KeymasterEnforcement*>();
std::unique_ptr<keymaster::KeymasterContext> keymaster_context;
std::unique_ptr<keymaster::AndroidKeymaster> keymaster;
std::vector<std::thread> threads;
int security_level;
if (FLAGS_keymint_impl == "software") {
security_level = KM_SECURITY_LEVEL_SOFTWARE;
} else if (FLAGS_keymint_impl == "tpm") {
security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
} else {
return CF_ERR("Unknown Keymint Implementation: " + FLAGS_keymint_impl);
}
// The guest image may have either the C++ implementation of
// KeyMint/Keymaster, xor the Rust implementation of KeyMint. Those different
// implementations each need to have a matching TA implementation in
// secure_env, but they use distincts ports (/dev/hvc3 for C++, /dev/hvc11 for
// Rust) so start threads for *both* TA implementations -- only one of them
// will receive any traffic from the guest.
// Start the Rust reference implementation of KeyMint.
#ifdef __linux__
LOG(INFO) << "starting Rust KeyMint TA implementation in a thread";
int keymint_in = FLAGS_keymint_fd_in;
int keymint_out = FLAGS_keymint_fd_out;
TpmResourceManager* rm = resource_manager;
threads.emplace_back([rm, keymint_in, keymint_out, security_level]() {
kmr_ta_main(keymint_in, keymint_out, security_level, rm);
});
#endif
// Start the C++ reference implementation of KeyMint.
LOG(INFO) << "starting C++ KeyMint implementation in a thread with FDs in="
<< FLAGS_keymaster_fd_in << ", out=" << FLAGS_keymaster_fd_out;
CF_EXPECTF(security_level == KM_SECURITY_LEVEL_SOFTWARE ||
security_level == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT,
"Unknown keymaster security_level \"{}\" for \"{}\"",
security_level, FLAGS_keymint_impl);
if (security_level == KM_SECURITY_LEVEL_SOFTWARE) {
keymaster_context.reset(new keymaster::PureSoftKeymasterContext(
keymaster::KmVersion::KEYMINT_3, KM_SECURITY_LEVEL_SOFTWARE));
} else /* KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT */ {
keymaster_context.reset(
new TpmKeymasterContext(*resource_manager, *keymaster_enforcement));
}
// keymaster::AndroidKeymaster puts the context pointer into a UniquePtr,
// taking ownership.
keymaster.reset(new keymaster::AndroidKeymaster(
new ProxyKeymasterContext(*keymaster_context), kOperationTableSize,
keymaster::MessageVersion(keymaster::KmVersion::KEYMINT_3,
0 /* km_date */)));
auto keymaster_in = DupFdFlag(FLAGS_keymaster_fd_in);
auto keymaster_out = DupFdFlag(FLAGS_keymaster_fd_out);
keymaster::AndroidKeymaster* borrowed_km = keymaster.get();
threads.emplace_back([keymaster_in, keymaster_out, borrowed_km]() {
while (true) {
SharedFdKeymasterChannel keymaster_channel(keymaster_in, keymaster_out);
KeymasterResponder keymaster_responder(keymaster_channel, *borrowed_km);
while (keymaster_responder.ProcessMessage()) {
}
}
});
auto gatekeeper_in = DupFdFlag(FLAGS_gatekeeper_fd_in);
auto gatekeeper_out = DupFdFlag(FLAGS_gatekeeper_fd_out);
threads.emplace_back([gatekeeper_in, gatekeeper_out, &gatekeeper]() {
while (true) {
SharedFdGatekeeperChannel gatekeeper_channel(gatekeeper_in,
gatekeeper_out);
GatekeeperResponder gatekeeper_responder(gatekeeper_channel, *gatekeeper);
while (gatekeeper_responder.ProcessMessage()) {
}
}
});
auto oemlock_in = DupFdFlag(FLAGS_oemlock_fd_in);
auto oemlock_out = DupFdFlag(FLAGS_oemlock_fd_out);
threads.emplace_back([oemlock_in, oemlock_out, &oemlock]() {
while (true) {
transport::SharedFdChannel channel(oemlock_in, oemlock_out);
oemlock::OemLockResponder responder(channel, *oemlock);
while (responder.ProcessMessage().ok()) {
}
}
});
auto confui_server_fd = DupFdFlag(FLAGS_confui_server_fd);
threads.emplace_back([confui_server_fd, resource_manager]() {
ConfUiSignServer confui_sign_server(*resource_manager, confui_server_fd);
// no return, infinite loop
confui_sign_server.MainLoop();
});
auto kernel_events_fd = DupFdFlag(FLAGS_kernel_events_fd);
threads.emplace_back(StartKernelEventMonitor(kernel_events_fd));
for (auto& t : threads) {
t.join();
}
return {};
}
} // namespace cuttlefish
int main(int argc, char** argv) {
auto result = cuttlefish::SecureEnvMain(argc, argv);
if (result.ok()) {
return 0;
}
LOG(FATAL) << result.error().Trace();
return -1;
}