Commit 19d45475 authored by Ivan Sandrk's avatar Ivan Sandrk Committed by Commit Bot

[Remote Commands] Add "Remote Powerwash" command

Currently managed Chrome OS devices support remote commands issued by the admin
on CPanel.  Add a command that starts a powerwash on the device when received.

Design doc @ go/remote-powerwash-command

Bug: 891222
Change-Id: I8aab58db3de12681fd655642e984cff6f24e392f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1729219Reviewed-by: default avatarJulian Pastarmov <pastarmovj@chromium.org>
Reviewed-by: default avatarRyo Hashimoto <hashimoto@chromium.org>
Commit-Queue: Ivan Šandrk <isandrk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686378}
parent 35a03a6a
...@@ -1712,6 +1712,8 @@ source_set("chromeos") { ...@@ -1712,6 +1712,8 @@ source_set("chromeos") {
"policy/remote_commands/device_command_reboot_job.h", "policy/remote_commands/device_command_reboot_job.h",
"policy/remote_commands/device_command_refresh_machine_certificate_job.cc", "policy/remote_commands/device_command_refresh_machine_certificate_job.cc",
"policy/remote_commands/device_command_refresh_machine_certificate_job.h", "policy/remote_commands/device_command_refresh_machine_certificate_job.h",
"policy/remote_commands/device_command_remote_powerwash_job.cc",
"policy/remote_commands/device_command_remote_powerwash_job.h",
"policy/remote_commands/device_command_screenshot_job.cc", "policy/remote_commands/device_command_screenshot_job.cc",
"policy/remote_commands/device_command_screenshot_job.h", "policy/remote_commands/device_command_screenshot_job.h",
"policy/remote_commands/device_command_set_volume_job.cc", "policy/remote_commands/device_command_set_volume_job.cc",
...@@ -2629,6 +2631,7 @@ source_set("unit_tests") { ...@@ -2629,6 +2631,7 @@ source_set("unit_tests") {
"policy/off_hours/off_hours_policy_applier_unittest.cc", "policy/off_hours/off_hours_policy_applier_unittest.cc",
"policy/off_hours/off_hours_proto_parser_unittest.cc", "policy/off_hours/off_hours_proto_parser_unittest.cc",
"policy/pre_signin_policy_fetcher_unittest.cc", "policy/pre_signin_policy_fetcher_unittest.cc",
"policy/remote_commands/device_command_remote_powerwash_job_unittest.cc",
"policy/remote_commands/device_command_screenshot_job_unittest.cc", "policy/remote_commands/device_command_screenshot_job_unittest.cc",
"policy/remote_commands/device_command_set_volume_job_unittest.cc", "policy/remote_commands/device_command_set_volume_job_unittest.cc",
"policy/remote_commands/device_command_start_crd_session_unittest.cc", "policy/remote_commands/device_command_start_crd_session_unittest.cc",
......
// 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/chromeos/policy/remote_commands/device_command_remote_powerwash_job.h"
#include <utility>
#include "base/bind.h"
#include "base/syslog_logging.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chromeos/dbus/session_manager/session_manager_client.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace policy {
namespace {
// Expiration time for the command is this high because this command is supposed
// to be a security feature where the device gets wiped even if it's turned on
// again only after several years of being powered off.
constexpr base::TimeDelta kRemotePowerwashCommandExpirationTime =
base::TimeDelta::FromDays(5 * 365); // 5 years.
// The time that we wait for the server to get the ACK, if that passes we
// immediately start the powerwash process.
constexpr base::TimeDelta kFailsafeTimerTimeout =
base::TimeDelta::FromSeconds(1);
void StartPowerwash(enterprise_management::SignedData signed_command) {
chromeos::SessionManagerClient::Get()->StartRemoteDeviceWipe(signed_command);
}
} // namespace
DeviceCommandRemotePowerwashJob::DeviceCommandRemotePowerwashJob(
RemoteCommandsService* service)
: service_(service) {}
DeviceCommandRemotePowerwashJob::~DeviceCommandRemotePowerwashJob() = default;
enterprise_management::RemoteCommand_Type
DeviceCommandRemotePowerwashJob::GetType() const {
return enterprise_management::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH;
}
bool DeviceCommandRemotePowerwashJob::IsExpired(base::TimeTicks now) {
return now > issued_time() + kRemotePowerwashCommandExpirationTime;
}
void DeviceCommandRemotePowerwashJob::RunImpl(
CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback) {
// Don't support unsigned remote powerwash command.
if (!signed_command()) {
SYSLOG(ERROR) << "Unsigned remote powerwash command received, aborting.";
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(failed_callback), nullptr));
}
// Set callback which gets called after command is ACKd to the server. We want
// to start the powerwash process only after the server got the ACK, otherwise
// we could reboot before ACKing and then the server would never get the ACK.
service_->SetOnCommandAckedCallback(
base::BindOnce(&StartPowerwash, signed_command().value()));
// Also set a failsafe timer that starts the powerwash so a faulty network
// connection doesn't prevent the powerwash from happening.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(&StartPowerwash, signed_command().value()),
kFailsafeTimerTimeout);
// Ack the command.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(succeeded_callback), nullptr));
}
} // namespace policy
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REMOTE_POWERWASH_JOB_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REMOTE_POWERWASH_JOB_H_
#include "base/macros.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
namespace policy {
class RemoteCommandsService;
class DeviceCommandRemotePowerwashJob : public RemoteCommandJob {
public:
explicit DeviceCommandRemotePowerwashJob(RemoteCommandsService* service);
~DeviceCommandRemotePowerwashJob() override;
// RemoteCommandJob:
enterprise_management::RemoteCommand_Type GetType() const override;
protected:
// RemoteCommandJob:
bool IsExpired(base::TimeTicks now) override;
void RunImpl(CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback) override;
private:
RemoteCommandsService* const service_;
DISALLOW_COPY_AND_ASSIGN(DeviceCommandRemotePowerwashJob);
};
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REMOTE_POWERWASH_JOB_H_
// 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/chromeos/policy/remote_commands/device_command_remote_powerwash_job.h"
#include <memory>
#include <utility>
#include "base/location.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_commands_factory_chromeos.h"
#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/dbus/session_manager/session_manager_client.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
constexpr base::TimeDelta kCommandAge = base::TimeDelta::FromMinutes(10);
constexpr base::TimeDelta kVeryoldCommandAge =
base::TimeDelta::FromDays(5 * 365 - 1);
class TestingRemoteCommandsService : public RemoteCommandsService {
public:
explicit TestingRemoteCommandsService(MockCloudPolicyClient* client)
: RemoteCommandsService(std::make_unique<DeviceCommandsFactoryChromeOS>(
nullptr /* policy_manager */),
client,
nullptr /* store */) {}
// RemoteCommandsService:
void SetOnCommandAckedCallback(base::OnceClosure callback) override {
on_command_acked_callback_ = std::move(callback);
}
base::OnceClosure OnCommandAckedCallback() {
return std::move(on_command_acked_callback_);
}
protected:
base::OnceClosure on_command_acked_callback_;
private:
DISALLOW_COPY_AND_ASSIGN(TestingRemoteCommandsService);
};
std::unique_ptr<policy::RemoteCommandJob> CreateRemotePowerwashJob(
base::TimeDelta age_of_command,
RemoteCommandsService* service) {
// Create the job proto.
enterprise_management::RemoteCommand command_proto;
command_proto.set_type(
enterprise_management::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH);
constexpr policy::RemoteCommandJob::UniqueIDType kUniqueID = 123456789;
command_proto.set_command_id(kUniqueID);
command_proto.set_age_of_command(age_of_command.InMilliseconds());
// Create the job and validate.
auto job = std::make_unique<policy::DeviceCommandRemotePowerwashJob>(service);
enterprise_management::SignedData signed_command;
EXPECT_TRUE(
job->Init(base::TimeTicks::Now(), command_proto, &signed_command));
EXPECT_EQ(kUniqueID, job->unique_id());
EXPECT_EQ(policy::RemoteCommandJob::NOT_STARTED, job->status());
return job;
}
class DeviceCommandRemotePowerwashJobTest : public testing::Test {
protected:
DeviceCommandRemotePowerwashJobTest();
~DeviceCommandRemotePowerwashJobTest() override;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
base::RunLoop run_loop_;
const std::unique_ptr<MockCloudPolicyClient> client_;
const std::unique_ptr<TestingRemoteCommandsService> service_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceCommandRemotePowerwashJobTest);
};
DeviceCommandRemotePowerwashJobTest::DeviceCommandRemotePowerwashJobTest()
: task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::TestMockTimeTaskRunner::Type::kBoundToThread)),
client_(std::make_unique<MockCloudPolicyClient>()),
service_(std::make_unique<TestingRemoteCommandsService>(client_.get())) {
chromeos::SessionManagerClient::InitializeFakeInMemory();
}
DeviceCommandRemotePowerwashJobTest::~DeviceCommandRemotePowerwashJobTest() {
chromeos::SessionManagerClient::Shutdown();
}
// Make sure that the command is still valid 5*365-1 days after being issued.
TEST_F(DeviceCommandRemotePowerwashJobTest, TestCommandLifetime) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateRemotePowerwashJob(kVeryoldCommandAge, service_.get());
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), base::OnceClosure()));
}
// Make sure that powerwash starts once the command gets ACK'd to the server.
TEST_F(DeviceCommandRemotePowerwashJobTest, TestCommandAckStartsPowerwash) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateRemotePowerwashJob(kCommandAge, service_.get());
// No powerwash at this point.
EXPECT_EQ(0, chromeos::FakeSessionManagerClient::Get()
->start_device_wipe_call_count());
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), run_loop_.QuitClosure()));
// At this point the job is run, and the succeeded_callback is waiting to be
// invoked.
run_loop_.Run();
// Now the succeeded_callback has been invoked, and normally it would cause an
// ACK to be sent to the server, and upon receiving a response back from the
// server the powerwash would start.
base::RunLoop run_loop2;
chromeos::FakeSessionManagerClient::Get()->set_on_start_device_wipe_callback(
base::BindLambdaForTesting([&]() { run_loop2.Quit(); }));
// Simulate a response from the server by posting a task and waiting for
// StartDeviceWipe to be called.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, service_->OnCommandAckedCallback());
run_loop2.Run();
// One powerwash coming up.
EXPECT_EQ(1, chromeos::FakeSessionManagerClient::Get()
->start_device_wipe_call_count());
}
// Make sure that the failsafe timer starts the powerwash in case of no ACK.
TEST_F(DeviceCommandRemotePowerwashJobTest, TestFailsafeTimerStartsPowerwash) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateRemotePowerwashJob(kCommandAge, service_.get());
// No powerwash at this point.
EXPECT_EQ(0, chromeos::FakeSessionManagerClient::Get()
->start_device_wipe_call_count());
// Run job + succeeded_callback.
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), run_loop_.QuitClosure()));
run_loop_.Run();
// After 500ms the timer is not run yet.
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_EQ(0, chromeos::FakeSessionManagerClient::Get()
->start_device_wipe_call_count());
// After 1s the timer is run.
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_EQ(1, chromeos::FakeSessionManagerClient::Get()
->start_device_wipe_call_count());
}
} // namespace policy
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_refresh_machine_certificate_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_refresh_machine_certificate_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h" #include "chrome/browser/chromeos/policy/remote_commands/device_command_start_crd_session_job.h"
...@@ -51,6 +52,8 @@ DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type, ...@@ -51,6 +52,8 @@ DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type,
case em::RemoteCommand_Type_DEVICE_REFRESH_ENTERPRISE_MACHINE_CERTIFICATE: case em::RemoteCommand_Type_DEVICE_REFRESH_ENTERPRISE_MACHINE_CERTIFICATE:
return std::make_unique<DeviceCommandRefreshMachineCertificateJob>( return std::make_unique<DeviceCommandRefreshMachineCertificateJob>(
policy_manager_->GetMachineCertificateUploader()); policy_manager_->GetMachineCertificateUploader());
case em::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH:
return std::make_unique<DeviceCommandRemotePowerwashJob>(service);
default: default:
// Other types of commands should be sent to UserCommandsFactoryChromeOS // Other types of commands should be sent to UserCommandsFactoryChromeOS
// instead of here. // instead of here.
......
...@@ -316,6 +316,19 @@ void FakeSessionManagerClient::StopSession() { ...@@ -316,6 +316,19 @@ void FakeSessionManagerClient::StopSession() {
void FakeSessionManagerClient::StartDeviceWipe() { void FakeSessionManagerClient::StartDeviceWipe() {
start_device_wipe_call_count_++; start_device_wipe_call_count_++;
if (!on_start_device_wipe_callback_.is_null()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(on_start_device_wipe_callback_));
}
}
void FakeSessionManagerClient::StartRemoteDeviceWipe(
const enterprise_management::SignedData& signed_command) {
start_device_wipe_call_count_++;
if (!on_start_device_wipe_callback_.is_null()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(on_start_device_wipe_callback_));
}
} }
void FakeSessionManagerClient::ClearForcedReEnrollmentVpd( void FakeSessionManagerClient::ClearForcedReEnrollmentVpd(
...@@ -741,4 +754,9 @@ void FakeSessionManagerClient::HandleOwnerKeySet( ...@@ -741,4 +754,9 @@ void FakeSessionManagerClient::HandleOwnerKeySet(
std::move(callback_to_run).Run(); std::move(callback_to_run).Run();
} }
void FakeSessionManagerClient::set_on_start_device_wipe_callback(
base::OnceClosure callback) {
on_start_device_wipe_callback_ = std::move(callback);
}
} // namespace chromeos } // namespace chromeos
...@@ -74,6 +74,8 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient ...@@ -74,6 +74,8 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient
const cryptohome::AccountIdentifier& cryptohome_id) override; const cryptohome::AccountIdentifier& cryptohome_id) override;
void StopSession() override; void StopSession() override;
void StartDeviceWipe() override; void StartDeviceWipe() override;
void StartRemoteDeviceWipe(
const enterprise_management::SignedData& signed_command) override;
void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) override; void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) override;
void StartTPMFirmwareUpdate(const std::string& update_mode) override; void StartTPMFirmwareUpdate(const std::string& update_mode) override;
void RequestLockScreen() override; void RequestLockScreen() override;
...@@ -214,6 +216,7 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient ...@@ -214,6 +216,7 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient
return clear_forced_re_enrollment_vpd_call_count_; return clear_forced_re_enrollment_vpd_call_count_;
} }
void set_on_start_device_wipe_callback(base::OnceClosure callback);
int start_device_wipe_call_count() const { int start_device_wipe_call_count() const {
return start_device_wipe_call_count_; return start_device_wipe_call_count_;
} }
...@@ -292,6 +295,9 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient ...@@ -292,6 +295,9 @@ class COMPONENT_EXPORT(SESSION_MANAGER) FakeSessionManagerClient
bool force_retrieve_policy_load_error_ = false; bool force_retrieve_policy_load_error_ = false;
int clear_forced_re_enrollment_vpd_call_count_ = 0; int clear_forced_re_enrollment_vpd_call_count_ = 0;
// Callback which is run after calling |StartDeviceWipe| or
// |StartRemoteDeviceWipe|.
base::OnceClosure on_start_device_wipe_callback_;
int start_device_wipe_call_count_ = 0; int start_device_wipe_call_count_ = 0;
int request_lock_screen_call_count_ = 0; int request_lock_screen_call_count_ = 0;
int notify_lock_screen_shown_call_count_ = 0; int notify_lock_screen_shown_call_count_ = 0;
......
...@@ -299,6 +299,18 @@ class SessionManagerClientImpl : public SessionManagerClient { ...@@ -299,6 +299,18 @@ class SessionManagerClientImpl : public SessionManagerClient {
login_manager::kSessionManagerStartDeviceWipe); login_manager::kSessionManagerStartDeviceWipe);
} }
void StartRemoteDeviceWipe(
const enterprise_management::SignedData& signed_command) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartRemoteDeviceWipe);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(signed_command);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) override { void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call( dbus::MethodCall method_call(
login_manager::kSessionManagerInterface, login_manager::kSessionManagerInterface,
......
...@@ -25,6 +25,10 @@ namespace dbus { ...@@ -25,6 +25,10 @@ namespace dbus {
class Bus; class Bus;
} }
namespace enterprise_management {
class SignedData;
}
namespace login_manager { namespace login_manager {
class LoginScreenStorageMetadata; class LoginScreenStorageMetadata;
class PolicyDescriptor; class PolicyDescriptor;
...@@ -180,6 +184,11 @@ class COMPONENT_EXPORT(SESSION_MANAGER) SessionManagerClient { ...@@ -180,6 +184,11 @@ class COMPONENT_EXPORT(SESSION_MANAGER) SessionManagerClient {
// Starts the factory reset. // Starts the factory reset.
virtual void StartDeviceWipe() = 0; virtual void StartDeviceWipe() = 0;
// Starts a remotely initiated factory reset, similar to |StartDeviceWipe|
// above, but also performs additional checks on Chrome OS side.
virtual void StartRemoteDeviceWipe(
const enterprise_management::SignedData& signed_command) = 0;
// Set the block_demode and check_enrollment flags to 0 in the VPD. // Set the block_demode and check_enrollment flags to 0 in the VPD.
virtual void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) = 0; virtual void ClearForcedReEnrollmentVpd(VoidDBusMethodCallback callback) = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment