Skip to content

Commit

Permalink
Enable Auto Re-Enrollment on Chromad
Browse files Browse the repository at this point in the history
Upload the enrollment ID (EID) to DMServer. This enables Auto
Re-Enrollment (Auto RE) on Chromad. Forced Re-enrollment (FRE) is
already supported on Chromad but the devices are currently just required
to be manually re-enrolled to the same domain.

Also, add a local state boolean preference that indicates whether the
enrollment ID has already being uploaded or not - used on Chromad only.
This pref is used to cease attempting to upload the EID.

Bug: 1248117
Change-Id: Ib354822245cf2dd02e40855bf9e5b4fbe3d0ef3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3178972
Commit-Queue: Felipe Andrade <fsandrade@chromium.org>
Reviewed-by: Roman Sorokin [CET] <rsorokin@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/main@{#939114}
  • Loading branch information
Felipe Andrade authored and Chromium LUCI CQ committed Nov 6, 2021
1 parent 15e456b commit b5ae0d8
Show file tree
Hide file tree
Showing 13 changed files with 724 additions and 236 deletions.
Expand Up @@ -56,14 +56,7 @@ 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);
Expand Down
Expand Up @@ -32,25 +32,26 @@ class EnrollmentCertificateUploaderImpl : public EnrollmentCertificateUploader {
explicit EnrollmentCertificateUploaderImpl(
policy::CloudPolicyClient* policy_client);

// A constructor which allows custom AttestationFlow implementations. Useful
// for testing.
EnrollmentCertificateUploaderImpl(policy::CloudPolicyClient* policy_client,
AttestationFlow* attestation_flow);

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

~EnrollmentCertificateUploaderImpl() override;

// Sets the retry limit in number of tries; useful in testing.
// Sets the retry limit in number of tries; useful for testing.
void set_retry_limit_for_testing(int limit) { retry_limit_ = limit; }
// Sets the retry delay; useful in testing.

// Sets the retry delay; useful for testing.
void set_retry_delay_for_testing(base::TimeDelta retry_delay) {
retry_delay_ = retry_delay;
}

// Sets a custom AttestationFlow implementation; useful for testing.
void set_attestation_flow_for_testing(AttestationFlow* attestation_flow) {
attestation_flow_ = attestation_flow;
}

// Obtains a fresh enrollment certificate and uploads it. If certificate has
// already been uploaded - reports success immediately and does not upload
// second time.
Expand Down Expand Up @@ -94,7 +95,7 @@ class EnrollmentCertificateUploaderImpl : public EnrollmentCertificateUploader {
void RunCallbacks(Status status);

policy::CloudPolicyClient* policy_client_;
AttestationFlow* attestation_flow_;
AttestationFlow* attestation_flow_ = nullptr;
std::unique_ptr<AttestationFlow> default_attestation_flow_;
// Callbacks to run when a certificate is uploaded (or we fail to).
std::queue<UploadCallback> callbacks_;
Expand Down
Expand Up @@ -72,13 +72,13 @@ void StatusCallbackSuccess(policy::CloudPolicyClient::StatusCallback callback) {

class EnrollmentCertificateUploaderTest : public ::testing::Test {
public:
EnrollmentCertificateUploaderTest()
: uploader_(&policy_client_, &attestation_flow_) {
EnrollmentCertificateUploaderTest() : uploader_(&policy_client_) {
settings_helper_.ReplaceDeviceSettingsProviderWithStub();
policy_client_.SetDMToken("fake_dm_token");

uploader_.set_retry_limit_for_testing(kRetryLimit);
uploader_.set_retry_delay_for_testing(base::TimeDelta());
uploader_.set_attestation_flow_for_testing(&attestation_flow_);
}

protected:
Expand Down
59 changes: 39 additions & 20 deletions chrome/browser/ash/attestation/enrollment_id_upload_manager.cc
Expand Up @@ -65,7 +65,7 @@ EnrollmentIdUploadManager::EnrollmentIdUploadManager(

EnrollmentIdUploadManager::~EnrollmentIdUploadManager() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(DeviceSettingsService::IsInitialized());
DCHECK(device_settings_service_);
device_settings_service_->RemoveObserver(this);
}

Expand All @@ -82,27 +82,33 @@ void EnrollmentIdUploadManager::Start() {
if (!policy_data || !policy_data->enrollment_id_needed())
return;

ObtainAndUploadEnrollmentId();
// Do not check the result of the upload request, because DMServer will
// inform us if the enrollment ID is needed the next time device policies
// are fetched.
ObtainAndUploadEnrollmentId(base::DoNothing());
}

void EnrollmentIdUploadManager::ObtainAndUploadEnrollmentId() {
// Do not allow multiple concurrent requests.
if (request_in_flight_)
return;
void EnrollmentIdUploadManager::ObtainAndUploadEnrollmentId(
UploadManagerCallback callback) {
DCHECK(!callback.is_null());

request_in_flight_ = true;
bool start = upload_manager_callbacks_.empty();
upload_manager_callbacks_.push(std::move(callback));

certificate_uploader_->ObtainAndUploadCertificate(base::BindOnce(
&EnrollmentIdUploadManager::OnEnrollmentCertificateUploaded,
weak_factory_.GetWeakPtr()));
// Do not allow multiple concurrent requests.
if (start) {
certificate_uploader_->ObtainAndUploadCertificate(base::BindOnce(
&EnrollmentIdUploadManager::OnEnrollmentCertificateUploaded,
weak_factory_.GetWeakPtr()));
}
}

void EnrollmentIdUploadManager::OnEnrollmentCertificateUploaded(
EnrollmentCertificateUploader::Status status) {
switch (status) {
case EnrollmentCertificateUploader::Status::kSuccess:
// Enrollment certificate uploaded successfully. No need to compute EID.
request_in_flight_ = false;
RunCallbacks(/*status=*/true);
break;
case EnrollmentCertificateUploader::Status::kFailedToFetch:
LOG(WARNING) << "EnrollmentIdUploadManager: Failed to fetch certificate. "
Expand All @@ -112,7 +118,7 @@ void EnrollmentIdUploadManager::OnEnrollmentCertificateUploaded(
case EnrollmentCertificateUploader::Status::kFailedToUpload:
// Enrollment certificate was fetched but not uploaded. It can be uploaded
// later so we will not proceed with computed EID.
request_in_flight_ = false;
RunCallbacks(/*status=*/false);
break;
}
}
Expand All @@ -124,14 +130,14 @@ void EnrollmentIdUploadManager::GetEnrollmentId() {
// AIK certificate sent to request an enrollment certificate does not
// contain an EID).
if (did_upload_empty_eid_) {
request_in_flight_ = false;
RunCallbacks(/*status=*/false);
return;
}

// We expect a registered CloudPolicyClient.
if (!policy_client_->is_registered()) {
LOG(ERROR) << "EnrollmentIdUploadManager: Invalid CloudPolicyClient.";
request_in_flight_ = false;
RunCallbacks(/*status=*/false);
return;
}

Expand Down Expand Up @@ -168,7 +174,7 @@ void EnrollmentIdUploadManager::RescheduleGetEnrollmentId() {
base::Seconds(retry_delay_));
} else {
LOG(WARNING) << "EnrollmentIdUploadManager: Retry limit exceeded.";
request_in_flight_ = false;
RunCallbacks(/*status=*/false);
}
}

Expand All @@ -177,17 +183,30 @@ void EnrollmentIdUploadManager::OnUploadComplete(
bool status) {
const std::string& printable_enrollment_id = base::ToLowerASCII(
base::HexEncode(enrollment_id.data(), enrollment_id.size()));
request_in_flight_ = false;
if (status) {
if (enrollment_id.empty())
did_upload_empty_eid_ = true;
} else {

if (!status) {
LOG(ERROR) << "Failed to upload Enrollment Identifier \""
<< printable_enrollment_id << "\" to DMServer.";
RunCallbacks(/*status=*/false);
return;
}

if (enrollment_id.empty())
did_upload_empty_eid_ = true;

VLOG(1) << "Enrollment Identifier \"" << printable_enrollment_id
<< "\" uploaded to DMServer.";
RunCallbacks(/*status=*/true);
}

void EnrollmentIdUploadManager::RunCallbacks(bool status) {
std::queue<UploadManagerCallback> callbacks;
callbacks.swap(upload_manager_callbacks_);

while (!callbacks.empty()) {
std::move(callbacks.front()).Run(status);
callbacks.pop();
}
}

} // namespace attestation
Expand Down
28 changes: 20 additions & 8 deletions chrome/browser/ash/attestation/enrollment_id_upload_manager.h
Expand Up @@ -6,6 +6,7 @@
#define CHROME_BROWSER_ASH_ATTESTATION_ENROLLMENT_ID_UPLOAD_MANAGER_H_

#include <memory>
#include <queue>
#include <string>

#include "base/callback.h"
Expand All @@ -27,6 +28,8 @@ namespace attestation {
// for enrollment if necessary.
class EnrollmentIdUploadManager : public DeviceSettingsService::Observer {
public:
using UploadManagerCallback = base::OnceCallback<void(bool success)>;

// The manager immediately connects with DeviceSettingsService to listen for
// policy changes. The EnrollmentCertificateUploader is used to attempt to
// upload enrollment certificate first. If it fails, the manager attempts to
Expand All @@ -50,9 +53,17 @@ class EnrollmentIdUploadManager : public DeviceSettingsService::Observer {

~EnrollmentIdUploadManager() override;

// Sets the retry limit in number of tries; useful in testing.
// Obtains a fresh enrollment certificate, which contains enrollment ID, and
// uploads it. If it fails, the manager will attempt to upload enrollment ID.
// Calls the callback when the processing is complete, with `success` set to
// `true` if either the enrollment certificate or the enrollment ID was
// uploaded, or `false`, otherwise.
void ObtainAndUploadEnrollmentId(UploadManagerCallback callback);

// Sets the retry limit in number of tries; useful for testing.
void set_retry_limit_for_testing(int limit) { retry_limit_ = limit; }
// Sets the retry delay in seconds; useful in testing.

// Sets the retry delay in seconds; useful for testing.
void set_retry_delay_for_testing(int retry_delay) {
retry_delay_ = retry_delay;
}
Expand All @@ -64,10 +75,6 @@ class EnrollmentIdUploadManager : public DeviceSettingsService::Observer {
// Checks enrollment setting and starts any necessary work.
void Start();

// Obtains a fresh enrollment certificate, which contains enrollment ID, and
// uploads it. If it fails, the manager will attempt to upload enrollment ID.
void ObtainAndUploadEnrollmentId();

// Handles certificate upload status. If succeeded or failed to upload - does
// nothing more. If failed to fetch - starts computed enrollment ID flow.
void OnEnrollmentCertificateUploaded(
Expand All @@ -88,19 +95,24 @@ class EnrollmentIdUploadManager : public DeviceSettingsService::Observer {
// the enrollment identifier that was uploaded.
void OnUploadComplete(const std::string& enrollment_id, bool status);

// Run all callbacks with |status|.
void RunCallbacks(bool status);

DeviceSettingsService* const device_settings_service_;
policy::CloudPolicyClient* const policy_client_;
EnrollmentCertificateUploader* const certificate_uploader_;
int num_retries_;
int retry_limit_;
int retry_delay_;
// Whether we are requesting an EID right now.
bool request_in_flight_ = false;

// Used to remember we uploaded an empty identifier this session for
// devices that can't obtain the identifier until they are powerwashed or
// updated and rebooted (see http://crbug.com/867724).
bool did_upload_empty_eid_ = false;

// Callbacks for the enrollment ID upload that is in progress.
std::queue<UploadManagerCallback> upload_manager_callbacks_;

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

0 comments on commit b5ae0d8

Please sign in to comment.