Commit d6dbbe9a authored by igorcov's avatar igorcov Committed by Commit bot

Update FWMP in TPM

As part of enrollment, the firmware management parameters (FWMP)
partition from TPM has to be set including the flags to mark if the
devmode is blocked. The update has to be done before the TPM is locked
but after the policy is retrieved.

It is implemented by including additional step in enrollment process
that makes the D-Bus call to cryptohome to set the data in FWMP.

Similarly when the device is deprovisioned, the firmware management
parameters are removed from TPM when it is established that it is a
consumer owned device.

BUG=685144

Review-Url: https://codereview.chromium.org/2727713003
Cr-Commit-Position: refs/heads/master@{#462886}
parent 77a23bfb
......@@ -13,6 +13,9 @@
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/system/statistics_provider.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "net/url_request/url_request_context_getter.h"
......@@ -132,10 +135,7 @@ AutoEnrollmentController::Mode AutoEnrollmentController::GetMode() {
return MODE_NONE;
}
AutoEnrollmentController::AutoEnrollmentController()
: state_(policy::AUTO_ENROLLMENT_STATE_IDLE),
safeguard_timer_(false, false),
client_start_weak_factory_(this) {}
AutoEnrollmentController::AutoEnrollmentController() {}
AutoEnrollmentController::~AutoEnrollmentController() {}
......@@ -178,9 +178,10 @@ void AutoEnrollmentController::Start() {
}
// Arm the belts-and-suspenders timer to avoid hangs.
safeguard_timer_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
base::Bind(&AutoEnrollmentController::Timeout, base::Unretained(this)));
safeguard_timer_.Start(FROM_HERE,
base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
base::Bind(&AutoEnrollmentController::Timeout,
weak_ptr_factory_.GetWeakPtr()));
// Start by checking if the device has already been owned.
UpdateState(policy::AUTO_ENROLLMENT_STATE_PENDING);
......@@ -269,15 +270,12 @@ void AutoEnrollmentController::StartClient(
power_initial = power_limit;
}
client_.reset(new policy::AutoEnrollmentClient(
client_ = base::MakeUnique<policy::AutoEnrollmentClient>(
base::Bind(&AutoEnrollmentController::UpdateState,
base::Unretained(this)),
service,
g_browser_process->local_state(),
g_browser_process->system_request_context(),
state_keys.front(),
power_initial,
power_limit));
weak_ptr_factory_.GetWeakPtr()),
service, g_browser_process->local_state(),
g_browser_process->system_request_context(), state_keys.front(),
power_initial, power_limit);
VLOG(1) << "Starting auto-enrollment client.";
client_->Start();
......@@ -301,6 +299,35 @@ void AutoEnrollmentController::UpdateState(
break;
}
if (state_ == policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT) {
StartRemoveFirmwareManagementParameters();
} else {
progress_callbacks_.Notify(state_);
}
}
void AutoEnrollmentController::StartRemoveFirmwareManagementParameters() {
DCHECK_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_);
cryptohome::RemoveFirmwareManagementParametersRequest request;
chromeos::DBusThreadManager::Get()
->GetCryptohomeClient()
->RemoveFirmwareManagementParametersFromTpm(
request,
base::Bind(
&AutoEnrollmentController::OnFirmwareManagementParametersRemoved,
weak_ptr_factory_.GetWeakPtr()));
}
void AutoEnrollmentController::OnFirmwareManagementParametersRemoved(
chromeos::DBusMethodCallStatus call_status,
bool result,
const cryptohome::BaseReply& reply) {
if (!result) {
LOG(ERROR) << "Failed to remove firmware management parameters, error: "
<< reply.error();
}
progress_callbacks_.Notify(state_);
}
......
......@@ -16,6 +16,10 @@
#include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
namespace cryptohome {
class BaseReply;
} // namespace cryptohome
namespace chromeos {
// Drives the forced re-enrollment check (for historical reasons called
......@@ -86,10 +90,28 @@ class AutoEnrollmentController {
// Sets |state_| and notifies |progress_callbacks_|.
void UpdateState(policy::AutoEnrollmentState state);
// Makes a D-Bus call to cryptohome to remove the firmware management
// parameters (FWMP) from TPM. Stops the |safeguard_timer_| and notifies the
// |progress_callbacks_| after update is done if the timer is still running.
// The notifications have to be sent only after the FWMP is cleared, because
// the user might try to switch to devmode. In this case, if block_devmode is
// in FWMP and the clear operation didn't finish, the switch would be denied.
// Also the safeguard timer has to be active until the FWMP is cleared to
// avoid the risk of blocked flow.
void StartRemoveFirmwareManagementParameters();
// Callback for RemoveFirmwareManagementParameters(). If an error is received
// here, it is logged only, without changing the flow after that, because
// the FWMP is used only for newer devices.
void OnFirmwareManagementParametersRemoved(
chromeos::DBusMethodCallStatus call_status,
bool result,
const cryptohome::BaseReply& reply);
// Handles timeout of the safeguard timer and stops waiting for a result.
void Timeout();
policy::AutoEnrollmentState state_;
policy::AutoEnrollmentState state_ = policy::AUTO_ENROLLMENT_STATE_IDLE;
ProgressCallbackList progress_callbacks_;
std::unique_ptr<policy::AutoEnrollmentClient> client_;
......@@ -102,12 +124,15 @@ class AutoEnrollmentController {
// something goes wrong, the timer will ensure that a decision gets made
// eventually, which is crucial to not block OOBE forever. See
// http://crbug.com/433634 for background.
base::Timer safeguard_timer_;
base::Timer safeguard_timer_{false, false};
// Whether the forced re-enrollment check has to be applied.
FRERequirement fre_requirement_ = REQUIRED;
base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_;
// TODO(igorcov): Merge the two weak_ptr factories in one.
base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_{
this};
base::WeakPtrFactory<AutoEnrollmentController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentController);
};
......
......@@ -29,6 +29,7 @@
#include "chromeos/attestation/attestation_flow.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/auth_policy_client.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/upstart_client.h"
#include "components/version_info/version_info.h"
......@@ -76,6 +77,33 @@ em::DeviceRegisterRequest::Flavor EnrollmentModeToRegistrationFlavor(
return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL;
}
// Returns whether block_devmode is set.
bool GetBlockdevmodeFromPolicy(
const enterprise_management::PolicyFetchResponse* policy) {
DCHECK(policy);
em::PolicyData policy_data;
if (!policy_data.ParseFromString(policy->policy_data())) {
LOG(ERROR) << "Failed to parse policy data";
return false;
}
em::ChromeDeviceSettingsProto payload;
if (!payload.ParseFromString(policy_data.policy_value())) {
LOG(ERROR) << "Failed to parse policy value";
return false;
}
bool block_devmode = false;
if (payload.has_system_settings()) {
const em::SystemSettingsProto& container = payload.system_settings();
if (container.has_block_devmode()) {
block_devmode = container.block_devmode();
}
}
return block_devmode;
}
} // namespace
EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
......@@ -397,6 +425,40 @@ void EnrollmentHandlerChromeOS::OnGetTokensResponse(
StartJoinAdDomain();
}
void EnrollmentHandlerChromeOS::SetFirmwareManagementParametersData() {
DCHECK_EQ(STEP_SET_FWMP_DATA, enrollment_step_);
// In case of reenrollment, the device has the TPM locked and nothing has to
// change in install attributes. No need to update firmware parameters in this
// case.
if (install_attributes_->IsDeviceLocked()) {
SetStep(STEP_LOCK_DEVICE);
StartLockDevice();
return;
}
install_attributes_->SetBlockDevmodeInTpm(
GetBlockdevmodeFromPolicy(policy_.get()),
base::Bind(
&EnrollmentHandlerChromeOS::OnFirmwareManagementParametersDataSet,
weak_ptr_factory_.GetWeakPtr()));
}
void EnrollmentHandlerChromeOS::OnFirmwareManagementParametersDataSet(
chromeos::DBusMethodCallStatus call_status,
bool result,
const cryptohome::BaseReply& reply) {
DCHECK_EQ(STEP_SET_FWMP_DATA, enrollment_step_);
if (!result) {
LOG(ERROR)
<< "Failed to update firmware management parameters in TPM, error: "
<< reply.error();
}
SetStep(STEP_LOCK_DEVICE);
StartLockDevice();
}
// GaiaOAuthClient::Delegate
void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
const std::string& access_token,
......@@ -427,8 +489,8 @@ void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) {
void EnrollmentHandlerChromeOS::StartJoinAdDomain() {
DCHECK_EQ(STEP_AD_DOMAIN_JOIN, enrollment_step_);
if (device_mode_ != DEVICE_MODE_ENTERPRISE_AD) {
SetStep(STEP_LOCK_DEVICE);
StartLockDevice();
SetStep(STEP_SET_FWMP_DATA);
SetFirmwareManagementParametersData();
return;
}
DCHECK(ad_join_delegate_);
......@@ -441,8 +503,8 @@ void EnrollmentHandlerChromeOS::OnAdDomainJoined(const std::string& realm) {
DCHECK_EQ(STEP_AD_DOMAIN_JOIN, enrollment_step_);
CHECK(!realm.empty());
realm_ = realm;
SetStep(STEP_LOCK_DEVICE);
StartLockDevice();
SetStep(STEP_SET_FWMP_DATA);
SetFirmwareManagementParametersData();
}
void EnrollmentHandlerChromeOS::StartLockDevice() {
......
......@@ -116,12 +116,13 @@ class EnrollmentHandlerChromeOS : public CloudPolicyClient::Observer,
STEP_ROBOT_AUTH_FETCH = 6, // Fetching device API auth code.
STEP_ROBOT_AUTH_REFRESH = 7, // Fetching device API refresh token.
STEP_AD_DOMAIN_JOIN = 8, // Joining Active Directory domain.
STEP_LOCK_DEVICE = 9, // Writing installation-time attributes.
STEP_STORE_TOKEN = 10, // Encrypting and storing DM token.
STEP_STORE_ROBOT_AUTH = 11, // Encrypting & writing robot refresh token.
STEP_STORE_POLICY = 12, // Storing policy and API refresh token. For
STEP_SET_FWMP_DATA = 9, // Setting the firmware management parameters.
STEP_LOCK_DEVICE = 10, // Writing installation-time attributes.
STEP_STORE_TOKEN = 11, // Encrypting and storing DM token.
STEP_STORE_ROBOT_AUTH = 12, // Encrypting & writing robot refresh token.
STEP_STORE_POLICY = 13, // Storing policy and API refresh token. For
// AD, includes policy fetch via authpolicyd.
STEP_FINISHED = 13, // Enrollment process done, no further action.
STEP_FINISHED = 14, // Enrollment process done, no further action.
};
// Handles the response to a request for server-backed state keys.
......@@ -149,6 +150,17 @@ class EnrollmentHandlerChromeOS : public CloudPolicyClient::Observer,
// Handles successful Active Directory domain join.
void OnAdDomainJoined(const std::string& realm);
// Updates the firmware management partition from TPM, setting the flags
// according to enum FirmwareManagementParametersFlags from rpc.proto if
// devmode is blocked.
void SetFirmwareManagementParametersData();
// Invoked after the firmware management partition in TPM is updated.
void OnFirmwareManagementParametersDataSet(
chromeos::DBusMethodCallStatus call_status,
bool result,
const cryptohome::BaseReply& reply);
// Calls InstallAttributes::LockDevice() for enterprise enrollment and
// DeviceSettingsService::SetManagementSettings() for consumer
// enrollment.
......
......@@ -20,8 +20,10 @@
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/proto/install_attributes.pb.h"
#include "chromeos/cryptohome/cryptohome_util.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
......@@ -186,6 +188,24 @@ void InstallAttributes::ReadAttributesIfReady(const base::Closure& callback,
callback.Run();
}
void InstallAttributes::SetBlockDevmodeInTpm(
bool block_devmode,
const CryptohomeClient::ProtobufMethodCallback& callback) {
DCHECK(!callback.is_null());
DCHECK(!device_locked_);
cryptohome::SetFirmwareManagementParametersRequest request;
// Set the flags, according to enum FirmwareManagementParametersFlags from
// rpc.proto if devmode is blocked.
if (block_devmode) {
request.set_flags(
cryptohome::DEVELOPER_DISABLE_BOOT |
cryptohome::DEVELOPER_DISABLE_CASE_CLOSED_DEBUGGING_UNLOCK);
}
cryptohome_client_->SetFirmwareManagementParametersInTpm(request, callback);
}
void InstallAttributes::LockDevice(policy::DeviceMode device_mode,
const std::string& domain,
const std::string& realm,
......
......@@ -70,6 +70,13 @@ class InstallAttributes {
// ReadAttributesIfReady().
void ReadImmutableAttributes(const base::Closure& callback);
// Updates the firmware management parameters from TPM, storing the devmode
// flag according to |block_devmode|. Invokes |callback| when done. Must be
// called before LockDevice is done. Used to update TPM on enrollment.
void SetBlockDevmodeInTpm(
bool block_devmode,
const CryptohomeClient::ProtobufMethodCallback& callback);
// Locks the device into |device_mode|. Depending on |device_mode|, a
// specific subset of |domain|, |realm| and |device_id| must be set. Can also
// be called after the lock has already been taken, in which case it checks
......@@ -111,6 +118,9 @@ class InstallAttributes {
// device id was not stored in the lockbox (prior to R19).
std::string GetDeviceId() const { return registration_device_id_; }
// Return whether TPM is locked.
bool IsDeviceLocked() const { return device_locked_; }
protected:
// True if install attributes have been read successfully. False if read
// failed or no read attempt was made.
......
......@@ -32,6 +32,13 @@ void CopyLockResult(base::RunLoop* loop,
loop->Quit();
}
void OnSetBlockDevmode(chromeos::DBusMethodCallStatus* out_status,
chromeos::DBusMethodCallStatus call_status,
bool result,
const cryptohome::BaseReply& reply) {
*out_status = call_status;
}
} // namespace
static const char kTestDomain[] = "example.com";
......@@ -295,4 +302,14 @@ TEST_F(InstallAttributesTest, VerifyFakeInstallAttributesCache) {
EXPECT_EQ(std::string(), install_attributes_->GetDeviceId());
}
TEST_F(InstallAttributesTest, CheckSetBlockDevmodeInTpm) {
chromeos::DBusMethodCallStatus status =
chromeos::DBusMethodCallStatus::DBUS_METHOD_CALL_FAILURE;
install_attributes_->SetBlockDevmodeInTpm(
true, base::Bind(&OnSetBlockDevmode, &status));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(chromeos::DBusMethodCallStatus::DBUS_METHOD_CALL_SUCCESS, status);
}
} // namespace chromeos
......@@ -889,48 +889,36 @@ class CryptohomeClientImpl : public CryptohomeClient {
void GetBootAttribute(const cryptohome::GetBootAttributeRequest& request,
const ProtobufMethodCallback& callback) override {
const char* method_name = cryptohome::kCryptohomeGetBootAttribute;
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
proxy_->CallMethod(&method_call,
kTpmDBusTimeoutMs ,
base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
weak_ptr_factory_.GetWeakPtr(),
callback));
CallCryptohomeMethod(cryptohome::kCryptohomeGetBootAttribute, request,
callback);
}
void SetBootAttribute(const cryptohome::SetBootAttributeRequest& request,
const ProtobufMethodCallback& callback) override {
const char* method_name = cryptohome::kCryptohomeSetBootAttribute;
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
proxy_->CallMethod(&method_call,
kTpmDBusTimeoutMs ,
base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
weak_ptr_factory_.GetWeakPtr(),
callback));
CallCryptohomeMethod(cryptohome::kCryptohomeSetBootAttribute, request,
callback);
}
void FlushAndSignBootAttributes(
const cryptohome::FlushAndSignBootAttributesRequest& request,
const ProtobufMethodCallback& callback) override {
const char* method_name = cryptohome::kCryptohomeFlushAndSignBootAttributes;
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
CallCryptohomeMethod(cryptohome::kCryptohomeFlushAndSignBootAttributes,
request, callback);
}
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
void RemoveFirmwareManagementParametersFromTpm(
const cryptohome::RemoveFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) override {
CallCryptohomeMethod(
cryptohome::kCryptohomeRemoveFirmwareManagementParameters, request,
callback);
}
proxy_->CallMethod(&method_call,
kTpmDBusTimeoutMs ,
base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
weak_ptr_factory_.GetWeakPtr(),
callback));
void SetFirmwareManagementParametersInTpm(
const cryptohome::SetFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) override {
CallCryptohomeMethod(cryptohome::kCryptohomeSetFirmwareManagementParameters,
request, callback);
}
void MigrateToDircrypto(const cryptohome::Identification& cryptohome_id,
......@@ -1246,6 +1234,23 @@ class CryptohomeClientImpl : public CryptohomeClient {
signal << " failed.";
}
// Makes an asynchronous D-Bus call, using cryptohome interface. |method_name|
// is the name of the method to be called. |request| is the specific request
// for the method, including the data required to be sent. |callback| is
// invoked when the response is received.
void CallCryptohomeMethod(const std::string& method_name,
const google::protobuf::MessageLite& request,
const ProtobufMethodCallback& callback) {
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs,
base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
weak_ptr_factory_.GetWeakPtr(), callback));
}
dbus::ObjectProxy* proxy_;
std::unique_ptr<BlockingMethodCaller> blocking_method_caller_;
AsyncCallStatusHandler async_call_status_handler_;
......
......@@ -28,8 +28,10 @@ class FlushAndSignBootAttributesRequest;
class GetBootAttributeRequest;
class GetKeyDataRequest;
class MountRequest;
class RemoveFirmwareManagementParametersRequest;
class RemoveKeyRequest;
class SetBootAttributeRequest;
class SetFirmwareManagementParametersRequest;
class UpdateKeyRequest;
class Identification;
......@@ -578,6 +580,20 @@ class CHROMEOS_EXPORT CryptohomeClient : public DBusClient {
const cryptohome::AuthorizationRequest& auth,
const VoidDBusMethodCallback& callback) = 0;
// Asynchronously calls RemoveFirmwareManagementParameters method. |callback|
// is called after method call, and with reply protobuf.
virtual void RemoveFirmwareManagementParametersFromTpm(
const cryptohome::RemoveFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) = 0;
// Asynchronously calls SetFirmwareManagementParameters method. |callback|
// is called after method call, and with reply protobuf. |request| contains
// the flags to be set. SetFirmwareManagementParameters creates the firmware
// management parameters in TPM and sets flags included in the request.
virtual void SetFirmwareManagementParametersInTpm(
const cryptohome::SetFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) = 0;
protected:
// Create() should be used instead.
CryptohomeClient();
......
......@@ -603,6 +603,18 @@ void FakeCryptohomeClient::MigrateToDircrypto(
this, &FakeCryptohomeClient::OnDircryptoMigrationProgressUpdated);
}
void FakeCryptohomeClient::RemoveFirmwareManagementParametersFromTpm(
const cryptohome::RemoveFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) {
ReturnProtobufMethodCallback(cryptohome::BaseReply(), callback);
}
void FakeCryptohomeClient::SetFirmwareManagementParametersInTpm(
const cryptohome::SetFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) {
ReturnProtobufMethodCallback(cryptohome::BaseReply(), callback);
}
void FakeCryptohomeClient::SetServiceIsAvailable(bool is_available) {
service_is_available_ = is_available;
if (is_available) {
......
......@@ -201,6 +201,12 @@ class CHROMEOS_EXPORT FakeCryptohomeClient : public CryptohomeClient {
const VoidDBusMethodCallback& callback) override;
void SetDircryptoMigrationProgressHandler(
const DircryptoMigrationProgessHandler& handler) override;
void RemoveFirmwareManagementParametersFromTpm(
const cryptohome::RemoveFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) override;
void SetFirmwareManagementParametersInTpm(
const cryptohome::SetFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback) override;
// Changes the behavior of WaitForServiceToBeAvailable(). This method runs
// pending callbacks if is_available is true.
......
......@@ -228,6 +228,14 @@ class MockCryptohomeClient : public CryptohomeClient {
const VoidDBusMethodCallback& callback));
MOCK_METHOD1(SetDircryptoMigrationProgressHandler,
void(const DircryptoMigrationProgessHandler& handler));
MOCK_METHOD2(
RemoveFirmwareManagementParametersFromTpm,
void(const cryptohome::RemoveFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback));
MOCK_METHOD2(
SetFirmwareManagementParametersInTpm,
void(const cryptohome::SetFirmwareManagementParametersRequest& request,
const ProtobufMethodCallback& callback));
};
} // namespace chromeos
......
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