910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/smart_card/smart_card_service.h"

#include "base/check_deref.h"
#include "base/containers/extend.h"
#include "base/containers/map_util.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/smart_card/smart_card_histograms.h"
#include "content/public/browser/isolated_context_util.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/smart_card_delegate.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/smart_card.mojom.h"
#include "third_party/blink/public/common/features_generated.h"

namespace content {

using device::mojom::SmartCardConnectResult;
using device::mojom::SmartCardCreateContextResult;
using device::mojom::SmartCardError;

namespace {
SmartCardDelegate& GetSmartCardDelegate() {
  return CHECK_DEREF(GetContentClient()->browser()->GetSmartCardDelegate());
}
}  // namespace

SmartCardService::SmartCardService(
    RenderFrameHost& render_frame_host,
    mojo::PendingReceiver<blink::mojom::SmartCardService> receiver,
    mojo::PendingRemote<device::mojom::SmartCardContextFactory> context_factory)
    : DocumentService(render_frame_host, std::move(receiver)),
      context_factory_(std::move(context_factory)) {
  // base::Unretained is safe in below cases, as receiver sets, being members,
  // are bound to the lifetime of this object.
  context_wrapper_receivers_.set_disconnect_handler(
      base::BindRepeating(&SmartCardService::OnMojoWrapperContextDisconnected,
                          base::Unretained(this)));
  connection_watcher_receivers_.set_disconnect_handler(base::BindRepeating(
      &SmartCardService::OnMojoWatcherPipeClosed, base::Unretained(this)));
  GetSmartCardDelegate().AddObserver(render_frame_host, this);
}

SmartCardService::~SmartCardService() {
  GetSmartCardDelegate().RemoveObserver(render_frame_host(), this);
}

// static
void SmartCardService::Create(
    RenderFrameHost* render_frame_host,
    mojo::PendingReceiver<blink::mojom::SmartCardService> receiver) {
  BrowserContext* browser_context = render_frame_host->GetBrowserContext();
  DCHECK(browser_context);

  if (!base::FeatureList::IsEnabled(blink::features::kSmartCard)) {
    mojo::ReportBadMessage("The SmartCard feature is disabled.");
    return;
  }

  if (!render_frame_host->IsFeatureEnabled(
          network::mojom::PermissionsPolicyFeature::kSmartCard)) {
    mojo::ReportBadMessage(
        "Access to the feature \"smart-card\" is disallowed by permissions "
        "policy.");
    return;
  }

  if (!HasIsolatedContextCapability(render_frame_host)) {
    mojo::ReportBadMessage(
        "Frame is not sufficiently isolated to use the Smart Card API.");
    return;
  }

  SmartCardDelegate* delegate =
      GetContentClient()->browser()->GetSmartCardDelegate();
  if (!delegate) {
    mojo::ReportBadMessage("Browser has no Smart Card delegate.");
    return;
  }

  new SmartCardService(*render_frame_host, std::move(receiver),
                       delegate->GetSmartCardContextFactory(*browser_context));
}

void SmartCardService::CreateContext(CreateContextCallback callback) {
  if (GetSmartCardDelegate().IsPermissionBlocked(render_frame_host())) {
    std::move(callback).Run(SmartCardCreateContextResult::NewError(
        SmartCardError::kPermissionDenied));
    return;
  }

  context_factory_->CreateContext(
      base::BindOnce(&SmartCardService::OnContextCreated,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SmartCardService::ListReaders(ListReadersCallback callback) {
  mojo::ReceiverId context_wrapper_id =
      context_wrapper_receivers_.current_receiver();

  mojo::Remote<SmartCardContext>& context_remote =
      CHECK_DEREF(base::FindOrNull(context_remotes_, context_wrapper_id));

  context_remote->ListReaders(
      base::BindOnce(&SmartCardService::OnListReadersResult,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void SmartCardService::GetStatusChange(
    base::TimeDelta timeout,
    std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states,
    GetStatusChangeCallback callback) {
  mojo::ReceiverId context_wrapper_id =
      context_wrapper_receivers_.current_receiver();

  mojo::Remote<SmartCardContext>& context_remote =
      CHECK_DEREF(base::FindOrNull(context_remotes_, context_wrapper_id));

  context_remote->GetStatusChange(timeout, std::move(reader_states),
                                  std::move(callback));
}

void SmartCardService::Cancel(CancelCallback callback) {
  mojo::ReceiverId context_wrapper_id =
      context_wrapper_receivers_.current_receiver();

  mojo::Remote<SmartCardContext>& context_remote =
      CHECK_DEREF(base::FindOrNull(context_remotes_, context_wrapper_id));

  context_remote->Cancel(std::move(callback));
}

void SmartCardService::Connect(
    const std::string& reader,
    device::mojom::SmartCardShareMode share_mode,
    device::mojom::SmartCardProtocolsPtr preferred_protocols,
    mojo::PendingRemote<device::mojom::SmartCardConnectionWatcher>
        connection_watcher,
    ConnectCallback callback) {
  // This argument should be passed only from here to the platform-specific
  // component - not to here.
  CHECK(!mojo::Remote(std::move(connection_watcher)).is_bound());

  SmartCardDelegate& delegate = GetSmartCardDelegate();

  mojo::ReceiverId context_wrapper_id =
      context_wrapper_receivers_.current_receiver();

  if (delegate.HasReaderPermission(render_frame_host(), reader)) {
    mojo::Remote<SmartCardContext>& context_remote =
        CHECK_DEREF(base::FindOrNull(context_remotes_, context_wrapper_id));

    context_remote->Connect(reader, share_mode, std::move(preferred_protocols),
                            GetNewConnectionWatcher(reader),
                            std::move(callback));
    return;
  }

  if (!valid_reader_names_.contains(reader)) {
    // Avoid showing the user a string that comes directly from the application.
    //
    // This will also block the case where an application asks to connect to a
    // reader without first checking whether it exists in the system (via
    // ListReaders). But no sane application should be doing this anyway.  If
    // this turns out to be a problem we will have to do a ListReaders() here on
    // our own before coming to a decision.
    std::move(callback).Run(
        SmartCardConnectResult::NewError(SmartCardError::kPermissionDenied));
    return;
  }

  delegate.RequestReaderPermission(
      render_frame_host(), reader,
      base::BindOnce(&SmartCardService::OnReaderPermissionResult,
                     weak_ptr_factory_.GetWeakPtr(), context_wrapper_id, reader,
                     share_mode, std::move(preferred_protocols),
                     std::move(callback)));
}

void SmartCardService::NotifyConnectionUsed() {
  GetSmartCardDelegate().NotifyConnectionUsed(render_frame_host());
}

void SmartCardService::OnReaderPermissionResult(
    mojo::ReceiverId context_wrapper_id,
    const std::string& reader,
    device::mojom::SmartCardShareMode share_mode,
    device::mojom::SmartCardProtocolsPtr preferred_protocols,
    ConnectCallback callback,
    bool granted) {
  auto it = context_remotes_.find(context_wrapper_id);
  if (it == context_remotes_.end()) {
    // Can happen if the renderer has dropped the wrapper remote in the
    // meantime.
    std::move(callback).Run(
        SmartCardConnectResult::NewError(SmartCardError::kUnexpected));
    return;
  }

  if (!granted) {
    std::move(callback).Run(
        SmartCardConnectResult::NewError(SmartCardError::kPermissionDenied));
    return;
  }

  mojo::Remote<SmartCardContext>& context_remote = it->second;

  context_remote->Connect(reader, share_mode, std::move(preferred_protocols),
                          GetNewConnectionWatcher(reader), std::move(callback));
}

void SmartCardService::OnMojoWrapperContextDisconnected() {
  mojo::ReceiverId context_wrapper_id =
      context_wrapper_receivers_.current_receiver();

  context_remotes_.erase(context_wrapper_id);
}

void SmartCardService::OnListReadersResult(
    ListReadersCallback callback,
    device::mojom::SmartCardListReadersResultPtr result) {
  if (result->is_readers()) {
    // Update our set of valid reader names.
    // Note that this is larger than the set of "currently available readers".
    for (const auto& reader : result->get_readers()) {
      valid_reader_names_.insert(reader);
    }
  }

  // And finally forward the result to the original caller.
  std::move(callback).Run(std::move(result));
}

void SmartCardService::OnContextCreated(
    CreateContextCallback callback,
    ::device::mojom::SmartCardCreateContextResultPtr result) {
  if (result->is_error()) {
    std::move(callback).Run(std::move(result));
    return;
  }

  // Wrap the context so that we can do permission checking/prompting before
  // forwarding a call where appropriate.

  mojo::PendingRemote<device::mojom::SmartCardContext> wrapper_remote;

  mojo::ReceiverId context_wrapper_id = context_wrapper_receivers_.Add(
      this, wrapper_remote.InitWithNewPipeAndPassReceiver());

  context_remotes_[context_wrapper_id] =
      mojo::Remote<SmartCardContext>(std::move(result->get_context()));

  result->set_context(std::move(wrapper_remote));

  std::move(callback).Run(std::move(result));
}

mojo::PendingRemote<device::mojom::SmartCardConnectionWatcher>
SmartCardService::GetNewConnectionWatcher(const std::string& reader) {
  mojo::PendingRemote<device::mojom::SmartCardConnectionWatcher>
      ceaseless_watcher;
  auto receiver_id = connection_watcher_receivers_.Add(
      this, ceaseless_watcher.InitWithNewPipeAndPassReceiver());
  connection_watchers_per_reader_[reader].insert(receiver_id);
  reader_names_per_watcher_[receiver_id] = reader;
  return ceaseless_watcher;
}

void SmartCardService::OnMojoWatcherPipeClosed() {
  auto receiver_id = connection_watcher_receivers_.current_receiver();
  auto reader_it = reader_names_per_watcher_.find(receiver_id);
  if (reader_it == reader_names_per_watcher_.end()) {
    return;
  }
  const std::string& reader = reader_it->second;
  auto reader_ids_it = connection_watchers_per_reader_.find(reader);
  if (reader_ids_it == connection_watchers_per_reader_.end()) {
    return;
  }
  reader_ids_it->second.erase(receiver_id);
  reader_names_per_watcher_.erase(reader_it);

  if (reader_names_per_watcher_.empty()) {
    GetSmartCardDelegate().NotifyLastConnectionLost(render_frame_host());
  }
  RecordSmartCardConnectionClosedReason(
      SmartCardConnectionClosedReason::kSmartCardConnectionClosedDisconnect);
}

void SmartCardService::OnPermissionRevoked(const url::Origin& origin) {
  if (render_frame_host().GetLastCommittedOrigin() != origin) {
    return;
  }

  auto& delegate = GetSmartCardDelegate();

  std::vector<mojo::ReceiverId> watchers_of_connections_to_remove;
  for (const auto& [reader_name, receiver_ids] :
       connection_watchers_per_reader_) {
    if (delegate.HasReaderPermission(render_frame_host(), reader_name)) {
      continue;
    }
    base::Extend(watchers_of_connections_to_remove, receiver_ids);
  }
  for (const auto& receiver_id : watchers_of_connections_to_remove) {
    connection_watcher_receivers_.Remove(receiver_id);
    RecordSmartCardConnectionClosedReason(
        SmartCardConnectionClosedReason::
            kSmartCardConnectionClosedPermissionRevoked);
  }
}

}  // namespace content