blob: 837b829fcdec372334162d2db082275fca15abcb [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/attestation/enrollment_certificate_uploader_impl.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/time/time.h"
#include "chrome/browser/ash/attestation/attestation_ca_client.h"
#include "chromeos/attestation/attestation_flow.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "components/account_id/account_id.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace {
// Constants for retrying certificate obtention and upload.
constexpr base::TimeDelta kRetryDelay = base::TimeDelta::FromSeconds(5);
const int kRetryLimit = 100;
void DBusPrivacyCACallback(
const base::RepeatingCallback<void(const std::string&)> on_success,
const base::RepeatingCallback<
void(chromeos::attestation::AttestationStatus)> on_failure,
const base::Location& from_here,
chromeos::attestation::AttestationStatus status,
const std::string& data) {
if (status == chromeos::attestation::ATTESTATION_SUCCESS) {
on_success.Run(data);
return;
}
LOG(ERROR) << "Attestation DBus method or server called failed with status: "
<< status << ": " << from_here.ToString();
if (!on_failure.is_null())
on_failure.Run(status);
}
} // namespace
namespace ash {
namespace attestation {
EnrollmentCertificateUploaderImpl::EnrollmentCertificateUploaderImpl(
policy::CloudPolicyClient* policy_client)
: EnrollmentCertificateUploaderImpl(policy_client,
nullptr /* attestation_flow */) {}
EnrollmentCertificateUploaderImpl::EnrollmentCertificateUploaderImpl(
policy::CloudPolicyClient* policy_client,
AttestationFlow* attestation_flow)
: policy_client_(policy_client),
attestation_flow_(attestation_flow),
retry_limit_(kRetryLimit),
retry_delay_(kRetryDelay) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
EnrollmentCertificateUploaderImpl::~EnrollmentCertificateUploaderImpl() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void EnrollmentCertificateUploaderImpl::ObtainAndUploadCertificate(
UploadCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool start = callbacks_.empty();
callbacks_.push(std::move(callback));
if (start)
Start();
}
void EnrollmentCertificateUploaderImpl::Start() {
num_retries_ = 0;
if (has_already_uploaded_) {
// Certificate was successfully uploaded earlier. Do not upload second time.
RunCallbacks(Status::kSuccess);
return;
}
// We expect a registered CloudPolicyClient.
if (!policy_client_->is_registered()) {
LOG(ERROR)
<< "EnrollmentCertificateUploaderImpl: Invalid CloudPolicyClient.";
RunCallbacks(Status::kFailedToFetch);
return;
}
if (!attestation_flow_) {
std::unique_ptr<ServerProxy> attestation_ca_client(
new AttestationCAClient());
default_attestation_flow_ =
std::make_unique<AttestationFlow>(std::move(attestation_ca_client));
attestation_flow_ = default_attestation_flow_.get();
}
GetCertificate();
}
void EnrollmentCertificateUploaderImpl::RunCallbacks(Status status) {
for (; !callbacks_.empty(); callbacks_.pop())
std::move(callbacks_.front()).Run(status);
}
void EnrollmentCertificateUploaderImpl::GetCertificate() {
// We can reuse the dbus callback handler logic.
attestation_flow_->GetCertificate(
PROFILE_ENTERPRISE_ENROLLMENT_CERTIFICATE,
EmptyAccountId(), // Not used.
std::string(), // Not used.
false, // Do not force a new key to be generated.
std::string(), // Leave key name empty to generate a default name.
base::BindOnce(
[](const base::RepeatingCallback<void(const std::string&)> on_success,
const base::RepeatingCallback<void(AttestationStatus)> on_failure,
const base::Location& from_here, AttestationStatus status,
const std::string& data) {
DBusPrivacyCACallback(on_success, on_failure, from_here, status,
std::move(data));
},
base::BindRepeating(
&EnrollmentCertificateUploaderImpl::UploadCertificate,
weak_factory_.GetWeakPtr()),
base::BindRepeating(
&EnrollmentCertificateUploaderImpl::HandleGetCertificateFailure,
weak_factory_.GetWeakPtr()),
FROM_HERE));
}
void EnrollmentCertificateUploaderImpl::UploadCertificate(
const std::string& pem_certificate_chain) {
policy_client_->UploadEnterpriseEnrollmentCertificate(
pem_certificate_chain,
base::BindOnce(&EnrollmentCertificateUploaderImpl::OnUploadComplete,
weak_factory_.GetWeakPtr()));
}
void EnrollmentCertificateUploaderImpl::OnUploadComplete(bool status) {
if (status) {
has_already_uploaded_ = true;
VLOG(1) << "Enterprise Enrollment Certificate uploaded to DMServer.";
RunCallbacks(Status::kSuccess);
} else {
LOG(ERROR)
<< "Failed to upload Enterprise Enrollment Certificate to DMServer.";
RunCallbacks(Status::kFailedToUpload);
}
}
void EnrollmentCertificateUploaderImpl::HandleGetCertificateFailure(
AttestationStatus status) {
if (status != ATTESTATION_SERVER_BAD_REQUEST_FAILURE)
Reschedule();
else
RunCallbacks(Status::kFailedToFetch);
}
void EnrollmentCertificateUploaderImpl::Reschedule() {
if (++num_retries_ < retry_limit_) {
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE,
base::BindOnce(&EnrollmentCertificateUploaderImpl::GetCertificate,
weak_factory_.GetWeakPtr()),
retry_delay_);
} else {
LOG(WARNING) << "EnrollmentCertificateUploaderImpl: Retry limit exceeded.";
RunCallbacks(Status::kFailedToFetch);
}
}
} // namespace attestation
} // namespace ash