blob: c8039d131c5dd17c307c29f0c5327bf79a521bcc [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 <vector>
#include <chromeos/dbus/service_constants.h>
#include <google/protobuf/message_lite.h>
#include "debugd/src/process_with_output.h"
#include "rpc.pb.h" // NOLINT(build/include)
namespace debugd {
namespace {
// Time in milliseconds to wait for a D-Bus response from cryptohome.
const int kDbusTimeoutMs = 5000;
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.";
using cryptohome::BaseReply;
using cryptohome::GetLoginStatusRequest;
using cryptohome::GetLoginStatusReply;
// Queries the cryptohome GetLoginStatus D-Bus interface.
//
// Handles lower-level logic for dbus-c++ 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::Connection* system_dbus, BaseReply* reply) {
GetLoginStatusRequest request;
DBus::CallMessage msg;
std::vector<uint8_t> bytes(request.ByteSize(), 0);
// Set up the message target and protobuf bytes to send.
if (request.SerializeToArray(bytes.data(), bytes.size()) &&
msg.destination(cryptohome::kCryptohomeServiceName) &&
msg.path(cryptohome::kCryptohomeServicePath) &&
msg.interface(cryptohome::kCryptohomeInterface) &&
msg.member(cryptohome::kCryptohomeGetLoginStatus)) {
try {
DBus::MessageIter write_iter = msg.writer();
write_iter << bytes;
// Send the D-Bus message. This can return/throw an error on failure.
DBus::Message response = system_dbus->send_blocking(msg, kDbusTimeoutMs);
if (!response.is_error()) {
const uint8_t* response_bytes;
size_t response_size =
response.reader().recurse().get_array(&response_bytes);
// Return true only if we can parse the reply successfully and it
// has the proper GetLoginStatusReply extension.
return reply->ParseFromArray(response_bytes, response_size) &&
reply->HasExtension(GetLoginStatusReply::reply);
}
} catch (...) {
}
}
return false;
}
// Sets a DBus::Error message if the error is non-NULL and hasn't been set yet.
void SetErrorIfNotSet(DBus::Error* error, const char* message) {
if (error && !error->is_set()) {
error->set(kAccessDeniedErrorString, message);
}
}
} // namespace
DevModeNoOwnerRestriction::DevModeNoOwnerRestriction(
DBus::Connection* system_dbus)
: system_dbus_(system_dbus) {
}
bool DevModeNoOwnerRestriction::AllowToolUse(DBus::Error* error) {
// Check dev mode first to avoid unnecessary cryptohome query delays.
if (InDevMode()) {
bool owner_exists, boot_lockbox_finalized;
if (GetOwnerAndLockboxStatus(&owner_exists, &boot_lockbox_finalized)) {
if (!(owner_exists || boot_lockbox_finalized)) {
return true;
}
SetErrorIfNotSet(error, kOwnerAccessErrorString);
}
// We want to specifically indicate when the query failed since it may
// mean that cryptohome is busy and could be tried again later.
SetErrorIfNotSet(error, kOwnerQueryErrorString);
}
SetErrorIfNotSet(error, kDevModeAccessErrorString);
return false;
}
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) {
BaseReply base_reply;
if (CryptohomeGetLoginStatus(system_dbus_, &base_reply)) {
GetLoginStatusReply reply =
base_reply.GetExtension(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