Commit e036cb41 authored by Michael Ershov's avatar Michael Ershov Committed by Commit Bot

Backend for device attestation during SAML authentication

This CL implements handler for request from authenticator.js
that calculates response for a challenge to perform device
attestation during SAML authentication.

Bug: 1000589
Change-Id: I98ef94c2598fae5a741b7b639b4ede425c913e26
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1849375Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Commit-Queue: Michael Ershov <miersh@google.com>
Cr-Commit-Position: refs/heads/master@{#712059}
parent 6c3814bc
......@@ -700,6 +700,8 @@ source_set("chromeos") {
"attestation/platform_verification_flow.h",
"attestation/tpm_challenge_key.cc",
"attestation/tpm_challenge_key.h",
"attestation/tpm_challenge_key_with_timeout.cc",
"attestation/tpm_challenge_key_with_timeout.h",
"authpolicy/auth_policy_credentials_manager.cc",
"authpolicy/auth_policy_credentials_manager.h",
"authpolicy/authpolicy_helper.cc",
......
......@@ -15,12 +15,13 @@ MockTpmChallengeKey::MockTpmChallengeKey() = default;
MockTpmChallengeKey::~MockTpmChallengeKey() = default;
void MockTpmChallengeKey::EnableFake() {
ON_CALL(*this, Run)
.WillByDefault(
WithArgs<2>(Invoke(this, &MockTpmChallengeKey::FakeRunSuccess)));
ON_CALL(*this, BuildResponse)
.WillByDefault(WithArgs<2>(
Invoke(this, &MockTpmChallengeKey::FakeBuildResponseSuccess)));
}
void MockTpmChallengeKey::FakeRunSuccess(TpmChallengeKeyCallback callback) {
void MockTpmChallengeKey::FakeBuildResponseSuccess(
TpmChallengeKeyCallback callback) {
std::move(callback).Run(TpmChallengeKeyResult::MakeResult("response"));
}
......
......@@ -22,7 +22,7 @@ class MockTpmChallengeKey : public TpmChallengeKey {
void EnableFake();
MOCK_METHOD(void,
Run,
BuildResponse,
(chromeos::attestation::AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
......@@ -31,7 +31,7 @@ class MockTpmChallengeKey : public TpmChallengeKey {
const std::string& key_name_for_spkac),
(override));
void FakeRunSuccess(TpmChallengeKeyCallback callback);
void FakeBuildResponseSuccess(TpmChallengeKeyCallback callback);
};
} // namespace attestation
......
......@@ -122,12 +122,12 @@ TpmChallengeKeyImpl::~TpmChallengeKeyImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void TpmChallengeKeyImpl::Run(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) {
void TpmChallengeKeyImpl::BuildResponse(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// |key_name_for_spkac| was designed to only be used with KEY_DEVICE.
......
......@@ -71,17 +71,17 @@ class TpmChallengeKey {
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
// Should be called only once for every instance. |TpmChallengeKey| object
// should live as long as response from |Run| function via |callback| is
// expected. On destruction it stops challenge process and silently discards
// callback. |key_name_for_spkac| the name of the key used for
// SignedPublicKeyAndChallenge when sending a challenge machine key request
// with |registerKey|=true.
virtual void Run(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) = 0;
// should live as long as response from |BuildResponse| function via
// |callback| is expected. On destruction it stops challenge process and
// silently discards callback. |key_name_for_spkac| the name of the key used
// for SignedPublicKeyAndChallenge when sending a challenge machine key
// request with |registerKey|=true.
virtual void BuildResponse(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) = 0;
protected:
// Use TpmChallengeKeyFactory for creation.
......@@ -110,12 +110,12 @@ class TpmChallengeKeyImpl : public TpmChallengeKey {
~TpmChallengeKeyImpl() override;
// TpmChallengeKey
void Run(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) override;
void BuildResponse(AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) override;
private:
enum class PrepareKeyResult {
......
......@@ -248,9 +248,9 @@ class TpmChallengeKeyTestBase : public BrowserWithTestWindowTest {
};
base::RunLoop loop;
challenge_key_impl_->Run(key_type_, GetProfile(),
base::Bind(callback, loop.QuitClosure(), res),
challenge, register_key, key_name_for_spkac);
challenge_key_impl_->BuildResponse(
key_type_, GetProfile(), base::Bind(callback, loop.QuitClosure(), res),
challenge, register_key, key_name_for_spkac);
loop.Run();
}
......
// 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/attestation/tpm_challenge_key_with_timeout.h"
#include "base/memory/ptr_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace chromeos {
namespace attestation {
namespace {
const char kTimeoutError[] =
"Device web based attestation failed with timeout error";
}
TpmChallengeKeyWithTimeout::TpmChallengeKeyWithTimeout() = default;
TpmChallengeKeyWithTimeout::~TpmChallengeKeyWithTimeout() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void TpmChallengeKeyWithTimeout::BuildResponse(
base::TimeDelta timeout,
AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac) {
DCHECK(!callback_);
callback_ = std::move(callback);
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&TpmChallengeKeyWithTimeout::ResolveCallback,
weak_factory_.GetWeakPtr(),
TpmChallengeKeyResult::MakeError(kTimeoutError)),
timeout);
challenger_ = TpmChallengeKeyFactory::Create();
challenger_->BuildResponse(
key_type, profile,
base::BindOnce(&TpmChallengeKeyWithTimeout::ResolveCallback,
weak_factory_.GetWeakPtr()),
challenge, register_key, key_name_for_spkac);
}
void TpmChallengeKeyWithTimeout::ResolveCallback(
const TpmChallengeKeyResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
challenger_.reset();
std::move(callback_).Run(result);
// No more member accesses here: |this| could be deleted at this point.
}
} // namespace attestation
} // namespace chromeos
// 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_ATTESTATION_TPM_CHALLENGE_KEY_WITH_TIMEOUT_H_
#define CHROME_BROWSER_CHROMEOS_ATTESTATION_TPM_CHALLENGE_KEY_WITH_TIMEOUT_H_
#include <memory>
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h"
#include "chrome/browser/profiles/profile.h"
namespace chromeos {
namespace attestation {
// Adds timeout restriction to |TpmChallengeKey| class. Can be safely deleted
// to cancel both timeout and challenge key tasks.
class TpmChallengeKeyWithTimeout final {
public:
// This object should live as long as result from |BuildResponse| via callback
// is expected.
TpmChallengeKeyWithTimeout();
TpmChallengeKeyWithTimeout(const TpmChallengeKeyWithTimeout&) = delete;
TpmChallengeKeyWithTimeout& operator=(const TpmChallengeKeyWithTimeout&) =
delete;
~TpmChallengeKeyWithTimeout();
// Tries to build a response for the |challenge|. Returns either timeout
// error or result from |TpmChallengeKey::BuildResponse| via |callback|.
void BuildResponse(base::TimeDelta timeout,
AttestationKeyType key_type,
Profile* profile,
TpmChallengeKeyCallback callback,
const std::string& challenge,
bool register_key,
const std::string& key_name_for_spkac);
private:
void ResolveCallback(const TpmChallengeKeyResult& result);
SEQUENCE_CHECKER(sequence_checker_);
std::unique_ptr<TpmChallengeKey> challenger_;
TpmChallengeKeyCallback callback_;
base::WeakPtrFactory<TpmChallengeKeyWithTimeout> weak_factory_{this};
};
} // namespace attestation
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_TPM_CHALLENGE_KEY_WITH_TIMEOUT_H_
......@@ -195,7 +195,7 @@ TEST_F(EPKChallengeMachineKeyTest, KeyNotRegisteredByDefault) {
whitelist.AppendString(extension_->id());
prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist);
EXPECT_CALL(*mock_tpm_challenge_key_, Run)
EXPECT_CALL(*mock_tpm_challenge_key_, BuildResponse)
.WillOnce(Invoke(FakeRunCheckNotRegister));
EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(),
......
......@@ -85,8 +85,8 @@ void EPKPChallengeKey::Run(
}
impl_ = chromeos::attestation::TpmChallengeKeyFactory::Create();
impl_->Run(type, profile, std::move(callback), challenge, register_key,
key_name_for_spkac);
impl_->BuildResponse(type, profile, std::move(callback), challenge,
register_key, key_name_for_spkac);
}
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
......
......@@ -1782,6 +1782,8 @@ jumbo_static_library("ui") {
"webui/chromeos/login/recommend_apps_screen_handler.h",
"webui/chromeos/login/reset_screen_handler.cc",
"webui/chromeos/login/reset_screen_handler.h",
"webui/chromeos/login/saml_challenge_key_handler.cc",
"webui/chromeos/login/saml_challenge_key_handler.h",
"webui/chromeos/login/signin_screen_handler.cc",
"webui/chromeos/login/signin_screen_handler.h",
"webui/chromeos/login/supervision_transition_screen_handler.cc",
......
......@@ -683,7 +683,8 @@ void GaiaScreenHandler::DeclareLocalizedValues(
void GaiaScreenHandler::Initialize() {
initialized_ = true;
// This should be called only once on page load.
AllowJavascript();
if (show_when_ready_)
ShowGaiaScreenIfReady();
}
......@@ -699,6 +700,8 @@ void GaiaScreenHandler::RegisterMessages() {
&GaiaScreenHandler::HandleScrapedPasswordCount);
AddCallback("scrapedPasswordVerificationFailed",
&GaiaScreenHandler::HandleScrapedPasswordVerificationFailed);
AddCallback("samlChallengeMachineKey",
&GaiaScreenHandler::HandleSamlChallengeMachineKey);
AddCallback("loginWebuiReady", &GaiaScreenHandler::HandleGaiaUIReady);
AddCallback("identifierEntered", &GaiaScreenHandler::HandleIdentifierEntered);
AddCallback("updateOfflineLogin",
......@@ -972,6 +975,18 @@ void GaiaScreenHandler::HandleScrapedPasswordVerificationFailed() {
RecordSAMLScrapingVerificationResultInHistogram(false);
}
void GaiaScreenHandler::HandleSamlChallengeMachineKey(
const std::string& callback_id,
const std::string& url,
const std::string& challenge) {
CreateSamlChallengeKeyHandler();
saml_challenge_key_handler_->Run(
Profile::FromWebUI(web_ui()),
base::BindOnce(&GaiaScreenHandler::ResolveJavascriptCallback,
weak_factory_.GetWeakPtr(), base::Value(callback_id)),
GURL(url), challenge);
}
void GaiaScreenHandler::HandleGaiaUIReady() {
VLOG(1) << "Gaia is loaded";
......@@ -1022,7 +1037,6 @@ void GaiaScreenHandler::HandleGetIsSamlUserPasswordless(
const std::string& callback_id,
const std::string& typed_email,
const std::string& gaia_id) {
AllowJavascript();
const bool is_saml_user_passwordless =
extension_provided_client_cert_usage_observer_ &&
extension_provided_client_cert_usage_observer_->ClientCertsWereUsed();
......@@ -1308,6 +1322,10 @@ bool GaiaScreenHandler::IsOfflineLoginActive() const {
return (screen_mode_ == GAIA_SCREEN_MODE_OFFLINE) || offline_login_is_active_;
}
void GaiaScreenHandler::CreateSamlChallengeKeyHandler() {
saml_challenge_key_handler_ = std::make_unique<SamlChallengeKeyHandler>();
}
void GaiaScreenHandler::CancelShowGaiaAsync() {
show_when_ready_ = false;
}
......
......@@ -17,6 +17,7 @@
#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
#include "chrome/browser/ui/webui/chromeos/login/saml_challenge_key_handler.h"
#include "chromeos/network/portal_detector/network_portal_detector.h"
#include "components/user_manager/user_type.h"
#include "net/base/net_errors.h"
......@@ -218,6 +219,9 @@ class GaiaScreenHandler : public BaseScreenHandler,
void HandleUsingSAMLAPI(bool is_third_party_idp);
void HandleScrapedPasswordCount(int password_count);
void HandleScrapedPasswordVerificationFailed();
void HandleSamlChallengeMachineKey(const std::string& callback_id,
const std::string& url,
const std::string& challenge);
void HandleGaiaUIReady();
......@@ -332,6 +336,11 @@ class GaiaScreenHandler : public BaseScreenHandler,
return !security_token_pin_dialog_closed_callback_.is_null();
}
// Assigns new SamlChallengeKeyHandler object to
// |saml_challenge_key_handler_|.
// TODO(miersh): ... or assigns an object for testing.
void CreateSamlChallengeKeyHandler();
// Current state of Gaia frame.
FrameState frame_state_ = FRAME_STATE_UNKNOWN;
......@@ -435,6 +444,9 @@ class GaiaScreenHandler : public BaseScreenHandler,
SecurityTokenPinDialogClosedCallback
security_token_pin_dialog_closed_callback_;
// Handler for |samlChallengeMachineKey| request.
std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_;
base::WeakPtrFactory<GaiaScreenHandler> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(GaiaScreenHandler);
......
// 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/ui/webui/chromeos/login/saml_challenge_key_handler.h"
#include "base/bind.h"
#include "base/values.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/login/login_state/login_state.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
namespace chromeos {
namespace {
const char kDeviceWebBasedAttestationUrlError[] =
"Device web based attestation is not enabled for the provided URL";
const char kDeviceWebBasedAttestationNotOobeError[] =
"Device web based attestation is only available on the OOBE screen";
const char kSuccessField[] = "success";
const char kResponseField[] = "response";
const size_t kPatternsSizeWarningLevel = 500;
// Checks if |url| matches one of the |patterns|.
bool IsDeviceWebBasedAttestationEnabledForUrl(const GURL& url,
const base::ListValue* patterns) {
if (!patterns) {
return false;
}
if (patterns->GetSize() >= kPatternsSizeWarningLevel) {
LOG(WARNING) << "Allowed urls list size is " << patterns->GetSize()
<< ". Check may be slow.";
}
for (const base::Value& cur_pattern : *patterns) {
if (ContentSettingsPattern::FromString(cur_pattern.GetString())
.Matches(url)) {
return true;
}
}
return false;
}
} // namespace
SamlChallengeKeyHandler::SamlChallengeKeyHandler() = default;
SamlChallengeKeyHandler::~SamlChallengeKeyHandler() = default;
void SamlChallengeKeyHandler::Run(Profile* profile,
CallbackType callback,
const GURL& url,
const std::string& challenge) {
DCHECK(!callback_);
callback_ = std::move(callback);
profile_ = profile;
challenge_ = challenge;
// Device attestation is currently allowed only on the OOBE screen.
if (LoginState::Get()->IsUserLoggedIn()) {
ReturnResult(attestation::TpmChallengeKeyResult::MakeError(
kDeviceWebBasedAttestationNotOobeError));
}
BuildResponseForWhitelistedUrl(url);
}
void SamlChallengeKeyHandler::SetTpmResponseTimeoutForTesting(
base::TimeDelta timeout) {
tpm_response_timeout_for_testing_ = timeout;
}
void SamlChallengeKeyHandler::BuildResponseForWhitelistedUrl(const GURL& url) {
CrosSettings* settings = CrosSettings::Get();
CrosSettingsProvider::TrustedStatus status =
settings->PrepareTrustedValues(base::BindRepeating(
&SamlChallengeKeyHandler::BuildResponseForWhitelistedUrl,
weak_factory_.GetWeakPtr(), url));
const base::ListValue* patterns = nullptr;
switch (status) {
case CrosSettingsProvider::TRUSTED:
if (!settings->GetList(kDeviceWebBasedAttestationAllowedUrls,
&patterns)) {
patterns = nullptr;
}
break;
case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
// Do nothing. This function will be called again when the values are
// ready.
return;
case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
patterns = nullptr;
break;
}
if (!IsDeviceWebBasedAttestationEnabledForUrl(url, patterns)) {
ReturnResult(attestation::TpmChallengeKeyResult::MakeError(
kDeviceWebBasedAttestationUrlError));
return;
}
BuildChallengeResponse();
}
void SamlChallengeKeyHandler::BuildChallengeResponse() {
tpm_key_challenger_ =
std::make_unique<attestation::TpmChallengeKeyWithTimeout>();
tpm_key_challenger_->BuildResponse(
GetTpmResponseTimeout(), attestation::KEY_DEVICE, profile_,
base::BindOnce(&SamlChallengeKeyHandler::ReturnResult,
weak_factory_.GetWeakPtr()),
challenge_, /*register_key=*/false, /*key_name_for_spkac=*/"");
}
base::TimeDelta SamlChallengeKeyHandler::GetTpmResponseTimeout() const {
if (tpm_response_timeout_for_testing_.has_value()) {
return tpm_response_timeout_for_testing_.value();
}
return default_tpm_response_timeout_;
}
void SamlChallengeKeyHandler::ReturnResult(
const attestation::TpmChallengeKeyResult& result) {
base::Value js_result(base::Value::Type::DICTIONARY);
if (!result.is_success) {
LOG(WARNING) << "Device attestation error: " << result.error_message;
}
js_result.SetKey(kSuccessField, base::Value(result.is_success));
js_result.SetKey(kResponseField, base::Value(result.data));
std::move(callback_).Run(std::move(js_result));
tpm_key_challenger_.reset();
}
} // namespace chromeos
// 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_UI_WEBUI_CHROMEOS_LOGIN_SAML_CHALLENGE_KEY_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SAML_CHALLENGE_KEY_HANDLER_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/attestation/tpm_challenge_key_with_timeout.h"
namespace chromeos {
// This class handles "samlChallengeMachineKey" request for GaiaScreenHandler.
// It calculates response for a challenge from Verified Access server for remote
// attestation during SAML authentication.
class SamlChallengeKeyHandler final {
public:
using CallbackType = base::OnceCallback<void(const base::Value& response)>;
SamlChallengeKeyHandler();
SamlChallengeKeyHandler(const SamlChallengeKeyHandler&) = delete;
SamlChallengeKeyHandler& operator=(const SamlChallengeKeyHandler&) = delete;
~SamlChallengeKeyHandler();
// Checks that provided |url| is whitelisted and tries to calculate response
// for the |challenge|.
void Run(Profile* profile,
CallbackType callback,
const GURL& url,
const std::string& challenge);
void SetTpmResponseTimeoutForTesting(base::TimeDelta timeout);
private:
// Checks if it is allowed for provided |url| to perform device attestation.
void BuildResponseForWhitelistedUrl(const GURL& url);
// Starts flow that acutally builds a response.
void BuildChallengeResponse();
// Returns current timeout for |tpm_key_challenger_| to response.
base::TimeDelta GetTpmResponseTimeout() const;
// Single return point from all checks, |tpm_key_challenger_| and timeout
// task.
void ReturnResult(const attestation::TpmChallengeKeyResult& result);
Profile* profile_ = nullptr;
std::string challenge_;
// Callback to return a result of ChallengeKey.
CallbackType callback_;
// Timeout for |tpm_key_challenger_| to response.
const base::TimeDelta default_tpm_response_timeout_ =
base::TimeDelta::FromSeconds(15);
base::Optional<base::TimeDelta> tpm_response_timeout_for_testing_;
// Performs attestation flow.
std::unique_ptr<attestation::TpmChallengeKeyWithTimeout> tpm_key_challenger_;
base::WeakPtrFactory<SamlChallengeKeyHandler> weak_factory_{this};
};
} // namespace chromeos
#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SAML_CHALLENGE_KEY_HANDLER_H_
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