chromium/chromeos/ash/components/dbus/usb/usbguard_client.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/dbus/usb/usbguard_client.h"

#include <map>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "chromeos/ash/components/dbus/usb/fake_usbguard_client.h"
#include "chromeos/ash/components/dbus/usb/usbguard_observer.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/usbguard/dbus-constants.h"

namespace ash {

namespace {
UsbguardClient* g_instance = nullptr;
}  // namespace

class UsbguardClientImpl : public UsbguardClient {
 public:
  UsbguardClientImpl() = default;

  UsbguardClientImpl(const UsbguardClientImpl&) = delete;
  UsbguardClientImpl& operator=(const UsbguardClientImpl&) = delete;

  ~UsbguardClientImpl() override = default;

  // UsbguardClient:
  void AddObserver(UsbguardObserver* observer) override {
    observers_.AddObserver(observer);
  }

  // UsbguardClient:
  void RemoveObserver(UsbguardObserver* observer) override {
    observers_.RemoveObserver(observer);
  }

  // UsbguardClient:
  bool HasObserver(const UsbguardObserver* observer) const override {
    return observers_.HasObserver(observer);
  }

  void Init(dbus::Bus* bus) {
    bus_ = bus;

    usbguard_proxy_ = bus_->GetObjectProxy(
        usbguard::kUsbguardServiceName,
        dbus::ObjectPath(usbguard::kUsbguardDevicesInterfacePath));

    usbguard_proxy_->ConnectToSignal(
        usbguard::kUsbguardDevicesInterface,
        usbguard::kDevicePolicyChangedSignalName,
        base::BindRepeating(&UsbguardClientImpl::DevicePolicyChanged,
                            weak_ptr_factory_.GetWeakPtr()),
        base::BindOnce(&UsbguardClientImpl::OnSignalConnected,
                       weak_ptr_factory_.GetWeakPtr()));
  }

 private:
  // Dispatches the DevicePolicyChanged signal with signature: uuusua{ss}
  void DevicePolicyChanged(dbus::Signal* signal) {
    dbus::MessageReader signal_reader(signal);
    dbus::MessageReader array_reader(nullptr);

    uint32_t id;
    uint32_t target_old;
    uint32_t target_new;
    std::string device_rule;
    uint32_t rule_id;
    if (!signal_reader.PopUint32(&id) ||
        !signal_reader.PopUint32(&target_old) ||
        !signal_reader.PopUint32(&target_new) ||
        !signal_reader.PopString(&device_rule) ||
        !signal_reader.PopUint32(&rule_id) ||
        !signal_reader.PopArray(&array_reader)) {
      LOG(ERROR) << "Error reading signal from usbguard: "
                 << signal->ToString();
      return;
    }

    std::map<std::string, std::string> attributes;
    while (array_reader.HasMoreData()) {
      dbus::MessageReader dict_entry(nullptr);
      std::string key;
      std::string value;
      if (!array_reader.PopDictEntry(&dict_entry) ||
          !dict_entry.PopString(&key) || !dict_entry.PopString(&value)) {
        LOG(ERROR) << "Error reading array from signal from usbguard: "
                   << signal->ToString();
        return;
      }
      attributes[key] = value;
    }

    for (auto& observer : observers_) {
      observer.DevicePolicyChanged(
          id, static_cast<UsbguardObserver::Target>(target_old),
          static_cast<UsbguardObserver::Target>(target_new), device_rule,
          rule_id, attributes);
    }
  }

  // Called when the biometrics signal is initially connected.
  void OnSignalConnected(const std::string& interface_name,
                         const std::string& signal_name,
                         bool success) {
    LOG_IF(ERROR, !success)
        << "Failed to connect to usbguard signal: " << signal_name;
  }

  raw_ptr<dbus::Bus> bus_ = nullptr;
  raw_ptr<dbus::ObjectProxy> usbguard_proxy_ = nullptr;
  base::ObserverList<UsbguardObserver> observers_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<UsbguardClientImpl> weak_ptr_factory_{this};
};

UsbguardClient::UsbguardClient() {
  DCHECK(!g_instance);
  g_instance = this;
}

UsbguardClient::~UsbguardClient() {
  DCHECK_EQ(this, g_instance);
  g_instance = nullptr;
}

// static
void UsbguardClient::Initialize(dbus::Bus* bus) {
  DCHECK(bus);
  (new UsbguardClientImpl())->Init(bus);
}

// static
void UsbguardClient::InitializeFake() {
  new FakeUsbguardClient();
}

// static
void UsbguardClient::Shutdown() {
  DCHECK(g_instance);
  delete g_instance;
}

// static
UsbguardClient* UsbguardClient::Get() {
  return g_instance;
}

}  // namespace ash