blob: 1ff98028402f22db95fb2419caa150083fa04138 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debugd/src/dev_mode_no_owner_restriction.h"
#include <memory>
#include <string>
#include <vector>
#include <chromeos/dbus/service_constants.h>
#include <dbus/bus.h>
#include <dbus/message.h>
#include <dbus/object_path.h>
#include <dbus/object_proxy.h>
#include <google/protobuf/message_lite.h>
#include "debugd/src/error_utils.h"
#include "debugd/src/process_with_output.h"
#include "rpc.pb.h" // NOLINT(build/include)
namespace debugd {
namespace {
const char kAccessDeniedErrorString[] =
"org.chromium.debugd.error.AccessDenied";
const char kDevModeAccessErrorString[] =
"Use of this tool is restricted to dev mode.";
const char kOwnerAccessErrorString[] =
"Unavailable after device has an owner or boot lockbox is finalized.";
const char kOwnerQueryErrorString[] =
"Error encountered when querying D-Bus, cryptohome may be busy.";
// Queries the cryptohome GetLoginStatus D-Bus interface.
//
// Handles lower-level logic for dbus methods and the cryptohome protobuf
// classes. Cryptohome protobuf responses work by extending the BaseReply class,
// so if an error occurs it's possible to get a reply that does not contain the
// GetLoginStatusReply extension.
//
// |reply| will be filled if a response was received regardless of extension,
// but the function will only return true if reply is filled and has the
// correct GetLoginStatusReply extension.
bool CryptohomeGetLoginStatus(dbus::Bus* bus, cryptohome::BaseReply* reply) {
cryptohome::GetLoginStatusRequest request;
dbus::ObjectProxy* proxy = bus->GetObjectProxy(
cryptohome::kCryptohomeServiceName,
dbus::ObjectPath(cryptohome::kCryptohomeServicePath));
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
cryptohome::kCryptohomeGetLoginStatus);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
std::unique_ptr<dbus::Response> response =
proxy->CallMethodAndBlock(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!response)
return false;
dbus::MessageReader reader(response.get());
return reader.PopArrayOfBytesAsProto(reply);
}
} // namespace
DevModeNoOwnerRestriction::DevModeNoOwnerRestriction(
scoped_refptr<dbus::Bus> bus) : bus_(bus) {}
bool DevModeNoOwnerRestriction::AllowToolUse(brillo::ErrorPtr* error) {
// Check dev mode first to avoid unnecessary cryptohome query delays.
if (!InDevMode()) {
DEBUGD_ADD_ERROR(
error, kAccessDeniedErrorString, kDevModeAccessErrorString);
return false;
}
bool owner_exists, boot_lockbox_finalized;
if (!GetOwnerAndLockboxStatus(&owner_exists, &boot_lockbox_finalized)) {
// We want to specifically indicate when the query failed since it may
// mean that cryptohome is busy and could be tried again later.
DEBUGD_ADD_ERROR(error, kAccessDeniedErrorString, kOwnerQueryErrorString);
return false;
}
if (owner_exists || boot_lockbox_finalized) {
DEBUGD_ADD_ERROR(error, kAccessDeniedErrorString, kOwnerAccessErrorString);
return false;
}
return true;
}
bool DevModeNoOwnerRestriction::InDevMode() const {
// The is_developer_end_user script provides a common way to access this
// information rather than duplicating logic here.
return ProcessWithOutput::RunProcess("/usr/sbin/is_developer_end_user",
ProcessWithOutput::ArgList{},
true, // needs root to run properly.
nullptr, // no stdin.
nullptr, // no stdout.
nullptr, // no stderr.
nullptr) == 0; // no D-Bus error.
}
// Checks for owner user and boot lockbox status.
//
// This function handles the high-level code of checking the cryptohome
// protocol buffer response. Lower-level details of sending the D-Bus function
// and parsing the protocol buffer are handled in CryptohomeGetLoginStatus().
//
// If cryptohome was queried successfully, returns true and |owner_user_exists|
// and |boot_lockbox_finalized| are updated.
bool DevModeNoOwnerRestriction::GetOwnerAndLockboxStatus(
bool* owner_user_exists,
bool* boot_lockbox_finalized) {
cryptohome::BaseReply base_reply;
if (CryptohomeGetLoginStatus(bus_.get(), &base_reply)) {
cryptohome::GetLoginStatusReply reply =
base_reply.GetExtension(cryptohome::GetLoginStatusReply::reply);
if (reply.has_owner_user_exists() && reply.has_boot_lockbox_finalized()) {
*owner_user_exists = reply.owner_user_exists();
*boot_lockbox_finalized = reply.boot_lockbox_finalized();
return true;
}
}
return false;
}
} // namespace debugd