blob: 41058409bd68e0a70971e44afc43a1fd4da57e88 [file] [log] [blame]
// Copyright 2016 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 "biod/biometrics_daemon.h"
#include <algorithm>
#include <utility>
#include <base/bind.h>
#include <brillo/dbus/async_event_sequencer.h>
#include "biod/fake_biometric.h"
#include "biod/fpc_biometric.h"
namespace biod {
using brillo::dbus_utils::AsyncEventSequencer;
using brillo::dbus_utils::DBusInterface;
using brillo::dbus_utils::DBusObject;
using brillo::dbus_utils::ExportedObjectManager;
using brillo::dbus_utils::ExportedProperty;
using brillo::dbus_utils::ExportedPropertySet;
using dbus::ObjectPath;
namespace dbus_constants {
const char kBusServiceName[] = "org.freedesktop.DBus";
const char kBusServicePath[] = "/org/freedesktop/DBus";
const char kBusInterface[] = "org.freedesktop.DBus";
const char kServiceName[] = "org.chromium.BiometricsDaemon";
const char kServicePath[] = "/org/chromium/BiometricsDaemon";
const char kBiometricInterface[] = "org.chromium.BiometricsDaemon.Biometric";
const char kAuthenticationInterface[] =
"org.chromium.BiometricsDaemon.Authentication";
const char kEnrollInterface[] = "org.chromium.BiometricsDaemon.Enroll";
const char kEnrollmentInterface[] = "org.chromium.BiometricsDaemon.Enrollment";
}
namespace errors {
const char kDomain[] = "biod";
const char kInternalError[] = "internal_error";
const char kInvalidArguments[] = "invalid_arguments";
}
void LogOnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
if (!success)
LOG(ERROR) << "Failed to connect to signal " << signal_name
<< " of interface " << interface_name;
}
BiometricWrapper::BiometricWrapper(
std::unique_ptr<Biometric> biometric,
ExportedObjectManager* object_manager,
ObjectPath object_path,
const AsyncEventSequencer::CompletionAction& completion_callback)
: biometric_(std::move(biometric)),
dbus_object_(object_manager, object_manager->GetBus(), object_path),
object_path_(std::move(object_path)),
enroll_object_path_(object_path_.value() + "/Enroll"),
authentication_object_path_(object_path_.value() + "/Authentication") {
CHECK(biometric_);
biometric_->SetScannedHandler(
base::Bind(&BiometricWrapper::OnScanned, base::Unretained(this)));
biometric_->SetAttemptHandler(
base::Bind(&BiometricWrapper::OnAttempt, base::Unretained(this)));
biometric_->SetFailureHandler(
base::Bind(&BiometricWrapper::OnFailure, base::Unretained(this)));
dbus::ObjectProxy* bus_proxy = object_manager->GetBus()->GetObjectProxy(
dbus_constants::kBusServiceName,
dbus::ObjectPath(dbus_constants::kBusServicePath));
bus_proxy->ConnectToSignal(
dbus_constants::kBusInterface,
"NameOwnerChanged",
base::Bind(&BiometricWrapper::OnNameOwnerChanged, base::Unretained(this)),
base::Bind(&LogOnSignalConnected));
DBusInterface* bio_interface =
dbus_object_.AddOrGetInterface(dbus_constants::kBiometricInterface);
property_type_.SetValue(static_cast<uint32_t>(biometric_->GetType()));
bio_interface->AddProperty("Type", &property_type_);
bio_interface->AddSimpleMethodHandlerWithErrorAndMessage(
"StartEnroll",
base::Bind(&BiometricWrapper::StartEnroll, base::Unretained(this)));
bio_interface->AddSimpleMethodHandlerWithError(
"GetEnrollments",
base::Bind(&BiometricWrapper::GetEnrollments, base::Unretained(this)));
bio_interface->AddSimpleMethodHandlerWithError(
"DestroyAllEnrollments",
base::Bind(&BiometricWrapper::DestroyAllEnrollments,
base::Unretained(this)));
bio_interface->AddSimpleMethodHandlerWithErrorAndMessage(
"StartAuthentication",
base::Bind(&BiometricWrapper::StartAuthentication,
base::Unretained(this)));
dbus_object_.RegisterAsync(completion_callback);
RefreshEnrollmentObjects();
}
BiometricWrapper::EnrollmentWrapper::EnrollmentWrapper(
BiometricWrapper* biometric,
std::unique_ptr<Biometric::Enrollment> enrollment,
ExportedObjectManager* object_manager,
const ObjectPath& object_path)
: biometric_(biometric),
enrollment_(std::move(enrollment)),
dbus_object_(object_manager, object_manager->GetBus(), object_path),
object_path_(object_path) {
DBusInterface* enrollment_interface =
dbus_object_.AddOrGetInterface(dbus_constants::kEnrollmentInterface);
property_label_.SetValue(enrollment_->GetLabel());
enrollment_interface->AddProperty("Label", &property_label_);
enrollment_interface->AddSimpleMethodHandlerWithError(
"SetLabel",
base::Bind(&EnrollmentWrapper::SetLabel, base::Unretained(this)));
enrollment_interface->AddSimpleMethodHandlerWithError(
"Remove", base::Bind(&EnrollmentWrapper::Remove, base::Unretained(this)));
dbus_object_.RegisterAndBlock();
}
BiometricWrapper::EnrollmentWrapper::~EnrollmentWrapper() {
dbus_object_.UnregisterAsync();
}
bool BiometricWrapper::EnrollmentWrapper::SetLabel(
brillo::ErrorPtr* error, const std::string& new_label) {
if (!enrollment_->SetLabel(new_label)) {
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInternalError,
"Failed to set label");
return false;
}
property_label_.SetValue(new_label);
return true;
}
bool BiometricWrapper::EnrollmentWrapper::Remove(brillo::ErrorPtr* error) {
if (!enrollment_->Remove()) {
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInternalError,
"Failed to remove enrollment");
return false;
}
biometric_->RefreshEnrollmentObjects();
return true;
}
void BiometricWrapper::FinalizeEnrollObject() {
enroll_owner_.clear();
enroll_dbus_object_->UnregisterAsync();
enroll_dbus_object_.reset();
}
void BiometricWrapper::FinalizeAuthenticationObject() {
authentication_owner_.clear();
authentication_dbus_object_->UnregisterAsync();
authentication_dbus_object_.reset();
}
void BiometricWrapper::OnNameOwnerChanged(dbus::Signal* sig) {
dbus::MessageReader reader(sig);
std::string name, old_owner, new_owner;
if (!reader.PopString(&name) || !reader.PopString(&old_owner) ||
!reader.PopString(&new_owner)) {
LOG(ERROR) << "Received invalid NameOwnerChanged signal";
return;
}
// We are only interested in cases where a name gets dropped from D-Bus.
if (name.empty() || !new_owner.empty())
return;
// If one of the session was owned by the dropped name, the session should
// also be dropped, as there is nobody left to end it explicitly.
if (name == enroll_owner_) {
LOG(INFO) << "Enroll object owner " << enroll_owner_
<< " has died. Enrollment is canceled automatically.";
if (enroll_)
enroll_.End();
if (enroll_dbus_object_)
FinalizeEnrollObject();
}
if (name == authentication_owner_) {
LOG(INFO) << "Authentication object owner " << authentication_owner_
<< " has died. Authentication is ended automatically.";
if (authentication_)
authentication_.End();
if (authentication_dbus_object_)
FinalizeAuthenticationObject();
}
}
void BiometricWrapper::OnScanned(Biometric::ScanResult scan_result, bool done) {
if (enroll_dbus_object_) {
dbus::Signal scanned_signal(dbus_constants::kBiometricInterface, "Scanned");
dbus::MessageWriter writer(&scanned_signal);
writer.AppendUint32(static_cast<uint32_t>(scan_result));
writer.AppendBool(done);
dbus_object_.SendSignal(&scanned_signal);
if (done) {
FinalizeEnrollObject();
RefreshEnrollmentObjects();
}
}
}
void BiometricWrapper::OnAttempt(Biometric::ScanResult scan_result,
std::vector<std::string> recognized_user_ids) {
if (authentication_dbus_object_) {
dbus::Signal attempt_signal(dbus_constants::kBiometricInterface, "Attempt");
dbus::MessageWriter writer(&attempt_signal);
writer.AppendUint32(static_cast<uint32_t>(scan_result));
writer.AppendArrayOfStrings(recognized_user_ids);
dbus_object_.SendSignal(&attempt_signal);
}
}
void BiometricWrapper::OnFailure() {
const char kFailureSignal[] = "Failure";
if (enroll_dbus_object_) {
dbus::Signal failure_signal(dbus_constants::kBiometricInterface,
kFailureSignal);
dbus_object_.SendSignal(&failure_signal);
FinalizeEnrollObject();
}
if (authentication_dbus_object_) {
dbus::Signal failure_signal(dbus_constants::kBiometricInterface,
kFailureSignal);
dbus_object_.SendSignal(&failure_signal);
FinalizeAuthenticationObject();
}
}
bool BiometricWrapper::StartEnroll(brillo::ErrorPtr* error,
dbus::Message* message,
const std::string& user_id,
const std::string& label,
ObjectPath* enroll_path) {
Biometric::EnrollSession enroll = biometric_->StartEnroll(user_id, label);
if (!enroll) {
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInternalError,
"Failed to start enroll");
return false;
}
enroll_ = std::move(enroll);
enroll_dbus_object_.reset(
new DBusObject(NULL, dbus_object_.GetBus(), enroll_object_path_));
DBusInterface* enroll_interface =
enroll_dbus_object_->AddOrGetInterface(dbus_constants::kEnrollInterface);
enroll_interface->AddSimpleMethodHandlerWithError(
"Cancel",
base::Bind(&BiometricWrapper::EnrollCancel, base::Unretained(this)));
enroll_dbus_object_->RegisterAndBlock();
*enroll_path = enroll_object_path_;
enroll_owner_ = message->GetSender();
return true;
}
bool BiometricWrapper::GetEnrollments(brillo::ErrorPtr* error,
std::vector<ObjectPath>* out) {
std::vector<std::unique_ptr<Biometric::Enrollment>> enrollments =
biometric_->GetEnrollments();
out->resize(enrollments.size());
std::transform(enrollments_.begin(),
enrollments_.end(),
out->begin(),
[this](std::unique_ptr<EnrollmentWrapper>& enrollment) {
return enrollment->path();
});
return true;
}
bool BiometricWrapper::DestroyAllEnrollments(brillo::ErrorPtr* error) {
if (!biometric_->DestroyAllEnrollments()) {
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInternalError,
"Failed to destroy all enrollments");
return false;
}
RefreshEnrollmentObjects();
return true;
}
bool BiometricWrapper::StartAuthentication(brillo::ErrorPtr* error,
dbus::Message* message,
ObjectPath* authentication_path) {
Biometric::AuthenticationSession authentication =
biometric_->StartAuthentication();
if (!authentication) {
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInternalError,
"Failed to start authentication");
return false;
}
authentication_ = std::move(authentication);
authentication_dbus_object_.reset(
new DBusObject(NULL, dbus_object_.GetBus(), authentication_object_path_));
DBusInterface* authentication_interface =
authentication_dbus_object_->AddOrGetInterface(
dbus_constants::kAuthenticationInterface);
authentication_interface->AddSimpleMethodHandlerWithError(
"End",
base::Bind(&BiometricWrapper::AuthenticationEnd, base::Unretained(this)));
authentication_dbus_object_->RegisterAndBlock();
*authentication_path = authentication_object_path_;
authentication_owner_ = message->GetSender();
return true;
}
bool BiometricWrapper::EnrollCancel(brillo::ErrorPtr* error) {
if (!enroll_) {
LOG(WARNING) << "DBus client attempted to cancel null enrollment";
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInvalidArguments,
"Enroll object was null");
return false;
}
enroll_.End();
if (enroll_dbus_object_) {
FinalizeEnrollObject();
}
return true;
}
bool BiometricWrapper::AuthenticationEnd(brillo::ErrorPtr* error) {
if (!authentication_) {
LOG(WARNING) << "DBus client attempted to cancel null authentication";
*error = brillo::Error::Create(FROM_HERE,
errors::kDomain,
errors::kInvalidArguments,
"Authentication object was null");
return false;
}
authentication_.End();
if (authentication_dbus_object_) {
FinalizeAuthenticationObject();
}
return true;
}
void BiometricWrapper::RefreshEnrollmentObjects() {
enrollments_.clear();
std::vector<std::unique_ptr<Biometric::Enrollment>> enrollments =
biometric_->GetEnrollments();
ExportedObjectManager* object_manager = dbus_object_.GetObjectManager().get();
std::string enrollments_root_path =
object_path_.value() + std::string("/Enrollment");
for (std::unique_ptr<Biometric::Enrollment>& enrollment : enrollments) {
ObjectPath enrollment_path(enrollments_root_path +
std::to_string(enrollment->GetId()));
enrollments_.emplace_back(new EnrollmentWrapper(
this, std::move(enrollment), object_manager, enrollment_path));
}
}
BiometricsDaemon::BiometricsDaemon() {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
bus_ = new dbus::Bus(options);
CHECK(bus_->Connect()) << "Failed to connect to system D-Bus";
object_manager_.reset(new ExportedObjectManager(
bus_, ObjectPath(dbus_constants::kServicePath)));
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
object_manager_->RegisterAsync(
sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
ObjectPath fake_bio_path =
ObjectPath(dbus_constants::kServicePath + std::string("/FakeBiometric"));
biometrics_.emplace_back(new BiometricWrapper(
std::unique_ptr<Biometric>(new FakeBiometric),
object_manager_.get(),
fake_bio_path,
sequencer->GetHandler("Failed to register biometric object", true)));
ObjectPath fpc_bio_path =
ObjectPath(dbus_constants::kServicePath + std::string("/FpcBiometric"));
std::unique_ptr<Biometric> fpc_bio = FpcBiometric::Create();
CHECK(fpc_bio);
biometrics_.emplace_back(new BiometricWrapper(
std::move(fpc_bio),
object_manager_.get(),
fpc_bio_path,
sequencer->GetHandler("Failed to register biometric object", true)));
CHECK(bus_->RequestOwnershipAndBlock(dbus_constants::kServiceName,
dbus::Bus::REQUIRE_PRIMARY));
}
} // namespace biod