blob: cfa4e98be82cb354b1218beaa4754eebbceec937 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "secagentd/device_user.h"
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "bindings/chrome_device_policy.pb.h"
#include "bindings/device_management_backend.pb.h"
#include "brillo/errors/error.h"
#include "metrics/metrics_library.h"
namespace secagentd {
DeviceUser::DeviceUser(
std::unique_ptr<org::chromium::SessionManagerInterfaceProxyInterface>
session_manager)
: weak_ptr_factory_(this), session_manager_(std::move(session_manager)) {}
void DeviceUser::RegisterSessionChangeHandler() {
session_manager_->RegisterSessionStateChangedSignalHandler(
base::BindRepeating(&DeviceUser::OnSessionStateChange,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&DeviceUser::HandleRegistrationResult,
weak_ptr_factory_.GetWeakPtr()));
}
std::string DeviceUser::GetDeviceUser() {
return device_user_;
}
void DeviceUser::HandleRegistrationResult(const std::string& interface,
const std::string& signal,
bool success) {
if (!success) {
LOG(ERROR) << "Callback registration failed for dbus signal: " << signal
<< " on interface: " << interface;
device_user_ = "Unknown";
} else {
UpdateDeviceId();
UpdateDeviceUser();
}
}
void DeviceUser::OnSessionStateChange(const std::string& state) {
if (state == "started") {
UpdateDeviceId();
// When a user logs in for the first time there is a delay for their
// ID to be added. Add a slight delay so the ID can appear.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DeviceUser::UpdateDeviceUser,
weak_ptr_factory_.GetWeakPtr()),
base::Seconds(2));
} else if (state == "stopping" || state == "stopped") {
device_user_ = "";
}
}
void DeviceUser::UpdateDeviceId() {
if (device_id_ != "") {
return;
}
auto response = RetrievePolicy(login_manager::ACCOUNT_TYPE_DEVICE, "");
if (!response.ok()) {
LOG(ERROR) << response.status();
return;
}
auto device_policy = response.value();
if (device_policy.device_affiliation_ids_size() >= 1) {
device_id_ = device_policy.device_affiliation_ids()[0];
if (device_policy.user_affiliation_ids_size() > 1) {
// There should only be 1 ID in the list.
LOG(ERROR) << "Greater than 1 Device ID. Count = "
<< device_policy.user_affiliation_ids_size();
}
}
}
void DeviceUser::UpdateDeviceUser() {
// Check if guest session is active.
bool is_guest = false;
brillo::ErrorPtr error;
if (!session_manager_->IsGuestSessionActive(&is_guest, &error) ||
error.get()) {
device_user_ = "Unknown";
// Do not exit method because possible that it is user session.
LOG(ERROR) << "Failed to deterimine if guest session "
<< error->GetMessage();
} else if (is_guest) {
device_user_ = "GuestUser";
return;
}
// Retrieve the device username.
std::string username;
std::string sanitized;
if (!session_manager_->RetrievePrimarySession(&username, &sanitized,
&error) ||
error.get()) {
device_user_ = "Unknown";
LOG(ERROR) << "Failed to retrieve primary session " << error->GetMessage();
return;
} else {
// No active session.
if (username.empty()) {
// Only set as empty when Guest session retrieval succeeds.
if (device_user_ != "Unknown") {
device_user_ = "";
}
return;
}
// Retrieve user policy information.
auto response = RetrievePolicy(login_manager::ACCOUNT_TYPE_USER, username);
if (!response.ok()) {
device_user_ = "Unknown";
LOG(ERROR) << response.status();
return;
} else {
auto user_policy = response.value();
// Fill in device_user if user is affiliated.
if (IsAffiliated(user_policy)) {
device_user_ = username;
} else {
// TODO(b/277796550): Make count stable across reboots.
device_user_ = "UnaffiliatedUser";
}
}
}
}
absl::StatusOr<enterprise_management::PolicyData> DeviceUser::RetrievePolicy(
login_manager::PolicyAccountType account_type,
const std::string& account_id) {
login_manager::PolicyDescriptor descriptor;
descriptor.set_account_type(account_type);
descriptor.set_account_id(account_id);
descriptor.set_domain(login_manager::POLICY_DOMAIN_CHROME);
std::string account_type_string =
account_type == login_manager::PolicyAccountType::ACCOUNT_TYPE_DEVICE
? "device"
: "user";
brillo::ErrorPtr error;
std::vector<uint8_t> out_blob;
std::string descriptor_string = descriptor.SerializeAsString();
if (!session_manager_->RetrievePolicyEx(
std::vector<uint8_t>(descriptor_string.begin(),
descriptor_string.end()),
&out_blob, &error) ||
error.get()) {
return absl::InternalError("Failed to retrieve " + account_type_string +
" policy " + error->GetMessage());
}
enterprise_management::PolicyFetchResponse response;
if (!response.ParseFromArray(out_blob.data(), out_blob.size())) {
return absl::InternalError("Failed to parse policy response for " +
account_type_string);
}
enterprise_management::PolicyData policy_data;
if (!policy_data.ParseFromArray(response.policy_data().data(),
response.policy_data().size())) {
return absl::InternalError("Failed to parse policy data for " +
account_type_string);
}
return policy_data;
}
bool DeviceUser::IsAffiliated(
const enterprise_management::PolicyData& user_policy) {
std::string user_id = "unset";
if (user_policy.user_affiliation_ids_size() >= 1) {
user_id = user_policy.user_affiliation_ids()[0];
if (user_policy.user_affiliation_ids_size() > 1) {
// There should only be 1 ID in the list.
LOG(ERROR) << "Greater than 1 User ID. Count = "
<< user_policy.user_affiliation_ids_size();
}
}
return user_id == device_id_;
}
} // namespace secagentd