chromium/chrome/browser/ash/net/client_cert_filter.cc

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

#include "chrome/browser/ash/net/client_cert_filter.h"

#include <utility>

#include "base/functional/bind.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_nss_types.h"
#include "net/cert/nss_profile_filter_chromeos.h"

namespace ash {

class ClientCertFilter::CertFilterIO {
 public:
  CertFilterIO(bool use_system_slot, const std::string& username_hash)
      : use_system_slot_(use_system_slot), username_hash_(username_hash) {}

  // Must be called on the IO thread. Calls |callback| on the UI thread.
  void Init(base::OnceClosure callback) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    DCHECK(!init_called_);
    init_called_ = true;

    waiting_for_private_slot_ = true;

    private_slot_ = crypto::GetPrivateSlotForChromeOSUser(
        username_hash_, base::BindOnce(&CertFilterIO::GotPrivateSlot,
                                       weak_ptr_factory_.GetWeakPtr()));

    // If the returned slot is null, GotPrivateSlot will be called back
    // eventually. If it is not null, the private slot was available
    // synchronously and the callback will not be called.
    if (private_slot_)
      waiting_for_private_slot_ = false;

    init_callback_ = std::move(callback);

    if (use_system_slot_) {
      crypto::GetSystemNSSKeySlot(base::BindOnce(
          &CertFilterIO::GotSystemSlot, weak_ptr_factory_.GetWeakPtr()));
      return;
    }

    InitIfSlotsAvailable();
  }

  // May be called on any thread, after Init()'s callback has run.
  bool IsCertAllowed(CERTCertificate* cert) {
    return nss_profile_filter_.IsCertAllowed(cert);
  }

 private:
  // Called back if the system slot was retrieved asynchronously. Continues the
  // initialization.
  void GotSystemSlot(crypto::ScopedPK11Slot system_slot) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    system_slot_ = std::move(system_slot);
    InitIfSlotsAvailable();
  }

  // Called back if the private slot was retrieved asynchronously. Continues the
  // initialization.
  void GotPrivateSlot(crypto::ScopedPK11Slot private_slot) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    waiting_for_private_slot_ = false;
    private_slot_ = std::move(private_slot);
    InitIfSlotsAvailable();
  }

  // If the required slots (|private_slot_| and conditionally |system_slot_|)
  // are available, initializes |nss_profile_filter_| and posts |init_callback_|
  // to the UI thread.
  void InitIfSlotsAvailable() {
    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    if ((use_system_slot_ && !system_slot_) || waiting_for_private_slot_)
      return;
    nss_profile_filter_.Init(
        crypto::GetPublicSlotForChromeOSUser(username_hash_),
        std::move(private_slot_), std::move(system_slot_));
    if (!init_callback_.is_null()) {
      content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                                   std::move(init_callback_));
    }
  }

  // True once Init() was called.
  bool init_called_ = false;

  // The callback provided to Init(). Called on the UI thread.
  base::OnceClosure init_callback_;

  bool use_system_slot_;
  std::string username_hash_;

  // Used to store the system slot, if required, for initialization. Will be
  // null after the filter is initialized.
  crypto::ScopedPK11Slot system_slot_;

  // Used to store the private slot for initialization. Will be null after the
  // filter is initialized.
  crypto::ScopedPK11Slot private_slot_;

  // If a private slot is requested but the slot, maybe null, is not obtained
  // yet, this is equal true. As long as this is true, the NSSProfileFilter will
  // not be initialized.
  bool waiting_for_private_slot_ = false;

  net::NSSProfileFilterChromeOS nss_profile_filter_;
  base::WeakPtrFactory<CertFilterIO> weak_ptr_factory_{this};
};

ClientCertFilter::ClientCertFilter(bool use_system_slot,
                                   const std::string& username_hash)
    : cert_filter_io_(new CertFilterIO(use_system_slot, username_hash)) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}

ClientCertFilter::~ClientCertFilter() {}

bool ClientCertFilter::Init(base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // base::Unretained() is safe here because |cert_filter_io_| is destroyed on
  // a post to the IO thread.
  content::GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(
          &CertFilterIO::Init, base::Unretained(cert_filter_io_.get()),
          // Wrap |callback| in OnInitComplete so it is cancelled if the
          // ClientCertFilter is destroyed earlier.
          base::BindOnce(&ClientCertFilter::OnInitComplete,
                         weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
  return false;
}

bool ClientCertFilter::IsCertAllowed(CERTCertificate* cert) const {
  return cert_filter_io_->IsCertAllowed(cert);
}

void ClientCertFilter::OnInitComplete(base::OnceClosure callback) {
  std::move(callback).Run();
}

}  // namespace ash