| // 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 |