Skip to content

Commit

Permalink
Add AuthPerformer::AuthenticateWithRecovery
Browse files Browse the repository at this point in the history
This commit adds a helper class to orchestrate the userdataauth and network requests necessary to authenticate a user via recovery.

The commit also moves `common_types.h` relevant to recovery service calls to the public module of `chromeos/ash/components/login/auth`, because `AuthenticationError` can now contain the status code returned by the recovery service.

Bug: b:245293194
Change-Id: I87e3a46435d4630fb0a3ac98c628d920cf1ccabb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3858108
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Commit-Queue: Martin Bidlingmaier <mbid@google.com>
Cr-Commit-Position: refs/heads/main@{#1067765}
  • Loading branch information
mbid authored and Chromium LUCI CQ committed Nov 4, 2022
1 parent b81a9fb commit 73cf79a
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ LoginFailureReason GetLoginFailureReasonForReport(
case AuthFailure::NETWORK_AUTH_FAILED:
case AuthFailure::ALLOWLIST_CHECK_FAILED:
case AuthFailure::AUTH_DISABLED:
case AuthFailure::CRYPTOHOME_RECOVERY_SERVICE_ERROR:
case AuthFailure::NUM_FAILURE_REASONS:
return LoginFailureReason::UNKNOWN_LOGIN_FAILURE_REASON;
}
Expand Down
3 changes: 3 additions & 0 deletions chromeos/ash/components/cryptohome/userdataauth_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ template COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
template COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
CryptohomeErrorCode
ReplyToCryptohomeError(const absl::optional<TerminateAuthFactorReply>&);
template COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
CryptohomeErrorCode
ReplyToCryptohomeError(const absl::optional<GetRecoveryRequestReply>&);

std::vector<cryptohome::KeyDefinition> GetKeyDataReplyToKeyDefinitions(
const absl::optional<GetKeyDataReply>& reply) {
Expand Down
3 changes: 2 additions & 1 deletion chromeos/ash/components/login/auth/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ component("auth") {
"mount_performer.h",
"password_visibility_utils.cc",
"password_visibility_utils.h",
"recovery/common_types.h",
"recovery/cryptohome_recovery_performer.cc",
"recovery/cryptohome_recovery_performer.h",
"recovery/cryptohome_recovery_service_client.cc",
"recovery/cryptohome_recovery_service_client.h",
"recovery/service_constants.cc",
Expand Down
1 change: 1 addition & 0 deletions chromeos/ash/components/login/auth/public/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ component("authpublic") {
"key.h",
"operation_chain_runner.cc",
"operation_chain_runner.h",
"recovery_types.h",
"saml_password_attributes.cc",
"saml_password_attributes.h",
"session_auth_factors.cc",
Expand Down
29 changes: 19 additions & 10 deletions chromeos/ash/components/login/auth/public/auth_failure.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@
// found in the LICENSE file.

#include "chromeos/ash/components/login/auth/public/auth_failure.h"
#include "base/check_op.h"

namespace ash {

AuthFailure::AuthFailure(FailureReason reason)
: reason_(reason), error_(GoogleServiceAuthError::NONE) {
DCHECK(reason != NETWORK_AUTH_FAILED);
AuthFailure::AuthFailure(FailureReason reason) : reason_(reason) {
DCHECK_NE(reason, NETWORK_AUTH_FAILED);
DCHECK_NE(reason, CRYPTOHOME_RECOVERY_SERVICE_ERROR);
}

// private
AuthFailure::AuthFailure(FailureReason reason, GoogleServiceAuthError error)
: reason_(reason), error_(error) {}
AuthFailure::AuthFailure(GoogleServiceAuthError error)
: reason_(NETWORK_AUTH_FAILED), google_service_auth_error_(error) {}

AuthFailure::AuthFailure(CryptohomeRecoveryServerStatusCode status_code)
: reason_(CRYPTOHOME_RECOVERY_SERVICE_ERROR),
cryptohome_recovery_server_error_(status_code) {
DCHECK_NE(status_code, CryptohomeRecoveryServerStatusCode::kSuccess);
}

// static
AuthFailure AuthFailure::FromNetworkAuthFailure(
const GoogleServiceAuthError& error) {
return AuthFailure(NETWORK_AUTH_FAILED, error);
AuthFailure AuthFailure::FromNetworkAuthFailure(GoogleServiceAuthError error) {
return AuthFailure(std::move(error));
}

const std::string AuthFailure::GetErrorString() const {
Expand All @@ -36,8 +42,9 @@ const std::string AuthFailure::GetErrorString() const {
case UNLOCK_FAILED:
return "Unlock failed.";
case NETWORK_AUTH_FAILED:
if (error_.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
return net::ErrorToString(error_.network_error());
if (google_service_auth_error_.state() ==
GoogleServiceAuthError::CONNECTION_FAILED) {
return net::ErrorToString(google_service_auth_error_.network_error());
}
return "Google authentication failed.";
case OWNER_REQUIRED:
Expand All @@ -58,6 +65,8 @@ const std::string AuthFailure::GetErrorString() const {
return "Cryptohome is corrupted.";
case USERNAME_HASH_FAILED:
return "Failed to get hashed username";
case CRYPTOHOME_RECOVERY_SERVICE_ERROR:
return "Failed interaction with cryptohome recovery server";
case NONE:
case NUM_FAILURE_REASONS:
NOTREACHED();
Expand Down
31 changes: 24 additions & 7 deletions chromeos/ash/components/login/auth/public/auth_failure.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#include <string>

#include "base/check.h"
#include "base/check_op.h"
#include "base/component_export.h"
#include "base/notreached.h"
#include "chromeos/ash/components/login/auth/public/recovery_types.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/net_errors.h"
Expand Down Expand Up @@ -44,34 +46,49 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_PUBLIC) AuthFailure {
AUTH_DISABLED = 14, // Authentication disabled for user.
TPM_UPDATE_REQUIRED = 15, // TPM firmware update is required.
UNRECOVERABLE_CRYPTOHOME = 16, // cryptohome is corrupted.
NUM_FAILURE_REASONS, // This has to be the last item.
// Failed interaction with cryptohome recovery service.
CRYPTOHOME_RECOVERY_SERVICE_ERROR = 17,
NUM_FAILURE_REASONS, // This has to be the last item.
};

explicit AuthFailure(FailureReason reason);
explicit AuthFailure(CryptohomeRecoveryServerStatusCode);

static AuthFailure FromNetworkAuthFailure(
const GoogleServiceAuthError& error);
static AuthFailure FromNetworkAuthFailure(GoogleServiceAuthError error);

inline bool operator==(const AuthFailure& b) const {
if (reason_ != b.reason_) {
return false;
}
if (reason_ == NETWORK_AUTH_FAILED) {
return error_ == b.error_;
return google_service_auth_error_ == b.google_service_auth_error_;
}
return true;
}

const std::string GetErrorString() const;

const GoogleServiceAuthError& error() const { return error_; }
const FailureReason& reason() const { return reason_; }

const GoogleServiceAuthError& error() const {
return google_service_auth_error_;
}

CryptohomeRecoveryServerStatusCode cryptohome_recovery_server_error() const {
DCHECK_EQ(reason_, CRYPTOHOME_RECOVERY_SERVICE_ERROR);
return cryptohome_recovery_server_error_;
}

private:
AuthFailure(FailureReason reason, GoogleServiceAuthError error);
explicit AuthFailure(GoogleServiceAuthError error);

FailureReason reason_;
GoogleServiceAuthError error_;
// The detailed error if `reason_ == NETWORK_AUTH_FAILED`.
GoogleServiceAuthError google_service_auth_error_ =
GoogleServiceAuthError::AuthErrorNone();
// The detailed error if `reason_ == CRYPTOHOME_RECOVERY_SERVICE_ERROR`.
CryptohomeRecoveryServerStatusCode cryptohome_recovery_server_error_ =
CryptohomeRecoveryServerStatusCode::kSuccess;
};

// Enum used for UMA. Do NOT reorder or remove entry. Don't forget to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ AuthenticationError::AuthenticationError(
: origin_(Origin::kCryptohome), cryptohome_code_(cryptohome_code) {}

AuthenticationError::AuthenticationError(
AuthFailure::FailureReason auth_failure)
: origin_(Origin::kChrome), failure_reason_(auth_failure) {}
AuthFailure::FailureReason auth_failure_reason)
: AuthenticationError(AuthFailure(auth_failure_reason)) {}

AuthenticationError::AuthenticationError(AuthFailure auth_failure)
: origin_(Origin::kChrome), auth_failure_(std::move(auth_failure)) {}

AuthenticationError::~AuthenticationError() = default;

void AuthenticationError::ResolveToFailure(
AuthFailure::FailureReason auth_failure) {
failure_reason_ = auth_failure;
AuthFailure::FailureReason auth_failure_reason) {
auth_failure_ = AuthFailure{auth_failure_reason};
}

} // namespace ash
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_PUBLIC)
};
explicit AuthenticationError(
user_data_auth::CryptohomeErrorCode cryptohome_code);
explicit AuthenticationError(AuthFailure::FailureReason auth_failure);
explicit AuthenticationError(AuthFailure::FailureReason auth_failure_reason);
explicit AuthenticationError(AuthFailure auth_failure);

~AuthenticationError();

Origin get_origin() const { return origin_; }

AuthFailure::FailureReason get_resolved_failure() const {
return failure_reason_;
return auth_failure_.reason();
}

void ResolveToFailure(AuthFailure::FailureReason auth_failure);
void ResolveToFailure(AuthFailure::FailureReason auth_failure_reason);

user_data_auth::CryptohomeErrorCode get_cryptohome_code() const {
return cryptohome_code_;
Expand All @@ -46,7 +47,7 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_PUBLIC)
user_data_auth::CryptohomeErrorCode cryptohome_code_;

// Mapping of the `error_code` to auth flow failure reason.
AuthFailure::FailureReason failure_reason_ = AuthFailure::NONE;
AuthFailure auth_failure_{AuthFailure::NONE};
};

} // namespace ash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_COMMON_TYPES_H_
#define CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_COMMON_TYPES_H_
#ifndef CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_PUBLIC_RECOVERY_TYPES_H_
#define CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_PUBLIC_RECOVERY_TYPES_H_

#include <stdint.h>

Expand Down Expand Up @@ -48,4 +48,4 @@ enum class CryptohomeRecoveryServerStatusCode {

} // namespace ash

#endif // CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_COMMON_TYPES_H_
#endif // CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_COMMON_TYPES_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/login/auth/recovery/cryptohome_recovery_performer.h"
#include "base/check.h"
#include "chromeos/ash/components/cryptohome/userdataauth_util.h"
#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "chromeos/ash/components/login/auth/recovery/cryptohome_recovery_service_client.h"
#include "chromeos/ash/components/login/auth/recovery/service_constants.h"
#include "components/device_event_log/device_event_log.h"

namespace ash {

CryptohomeRecoveryPerformer::CryptohomeRecoveryPerformer(
UserDataAuthClient* user_data_auth_client,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: user_data_auth_client_(user_data_auth_client),
service_client_(std::move(url_loader_factory)) {}

CryptohomeRecoveryPerformer::~CryptohomeRecoveryPerformer() = default;

void CryptohomeRecoveryPerformer::AuthenticateWithRecovery(
std::unique_ptr<UserContext> context,
AuthOperationCallback callback) {
DCHECK(!context->GetAuthSessionId().empty());

LOGIN_LOG(EVENT) << "Authenticating with recovery";

const std::string& access_token{context->GetAccessToken()};
DCHECK(!access_token.empty()) << "Gaia access token must be set for recovery";
service_client_.FetchEpoch(
GaiaAccessToken(access_token),
base::BindOnce(&CryptohomeRecoveryPerformer::OnNetworkFetchEpoch,
weak_factory_.GetWeakPtr(), std::move(context),
std::move(callback)));
}

void CryptohomeRecoveryPerformer::OnNetworkFetchEpoch(
std::unique_ptr<UserContext> context,
AuthOperationCallback callback,
absl::optional<CryptohomeRecoveryEpochResponse> opt_epoch,
CryptohomeRecoveryServerStatusCode status) {
if (status != CryptohomeRecoveryServerStatusCode::kSuccess) {
std::move(callback).Run(std::move(context),
AuthenticationError(AuthFailure(status)));
return;
}
CryptohomeRecoveryEpochResponse epoch = std::move(*opt_epoch);

user_data_auth::GetRecoveryRequestRequest request;

request.set_auth_session_id(context->GetAuthSessionId());

const std::string& gaia_id = context->GetGaiaID();
DCHECK(!gaia_id.empty()) << "Recovery is only supported for gaia users";
const std::string& access_token = context->GetAccessToken();
DCHECK(!access_token.empty());
const std::string& reauth_proof_token = context->GetReauthProofToken();
CHECK(!reauth_proof_token.empty()) << "Reauth proof token must be set";

request.set_requestor_user_id_type(
user_data_auth::GetRecoveryRequestRequest::GAIA_ID);
request.set_requestor_user_id(gaia_id);
request.set_auth_factor_label(kCryptohomeRecoveryKeyLabel);
request.set_gaia_access_token(access_token);
request.set_gaia_reauth_proof_token(reauth_proof_token);
request.set_epoch_response(epoch->data(), epoch->size());

user_data_auth_client_->GetRecoveryRequest(
std::move(request),
base::BindOnce(&CryptohomeRecoveryPerformer::OnGetRecoveryRequest,
weak_factory_.GetWeakPtr(), std::move(context),
std::move(callback), std::move(epoch)));
}

void CryptohomeRecoveryPerformer::OnGetRecoveryRequest(
std::unique_ptr<UserContext> context,
AuthOperationCallback callback,
CryptohomeRecoveryEpochResponse epoch,
absl::optional<user_data_auth::GetRecoveryRequestReply> reply) {
auto error = user_data_auth::ReplyToCryptohomeError(reply);
if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET) {
LOGIN_LOG(EVENT) << "Failed to obtain recovery request, error code "
<< error;
std::move(callback).Run(std::move(context), AuthenticationError{error});
return;
}

DCHECK(!reply->recovery_request().empty());
const std::string& access_token = context->GetAccessToken();
DCHECK(!access_token.empty());

service_client_.FetchRecoveryResponse(
reply->recovery_request(), GaiaAccessToken(access_token),
base::BindOnce(
&CryptohomeRecoveryPerformer::OnFetchRecoveryServiceResponse,
weak_factory_.GetWeakPtr(), std::move(context), std::move(callback),
std::move(epoch)));
}

void CryptohomeRecoveryPerformer::OnFetchRecoveryServiceResponse(
std::unique_ptr<UserContext> context,
AuthOperationCallback callback,
CryptohomeRecoveryEpochResponse epoch,
absl::optional<CryptohomeRecoveryResponse> opt_response,
CryptohomeRecoveryServerStatusCode status) {
if (status != CryptohomeRecoveryServerStatusCode::kSuccess) {
std::move(callback).Run(std::move(context),
AuthenticationError(AuthFailure(status)));
return;
}
const CryptohomeRecoveryResponse& response = *opt_response;

user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(context->GetAuthSessionId());
request.set_auth_factor_label(kCryptohomeRecoveryKeyLabel);

user_data_auth::CryptohomeRecoveryAuthInput* recovery_input =
request.mutable_auth_input()->mutable_cryptohome_recovery_input();
recovery_input->set_epoch_response(epoch->data(), epoch->size());
recovery_input->set_recovery_response(response->data(), response->size());

user_data_auth_client_->AuthenticateAuthFactor(
std::move(request),
base::BindOnce(&CryptohomeRecoveryPerformer::OnAuthenticateAuthFactor,
weak_factory_.GetWeakPtr(), std::move(context),
std::move(callback)));
}

void CryptohomeRecoveryPerformer::OnAuthenticateAuthFactor(
std::unique_ptr<UserContext> context,
AuthOperationCallback callback,
absl::optional<user_data_auth::AuthenticateAuthFactorReply> reply) {
auto error = user_data_auth::ReplyToCryptohomeError(reply);
if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET) {
LOGIN_LOG(EVENT)
<< "Failed to authenticate session via recovery factor, error code "
<< error;
std::move(callback).Run(std::move(context), AuthenticationError{error});
return;
}
CHECK(reply.has_value());
DCHECK(reply->authenticated());
LOGIN_LOG(EVENT) << "Authenticated successfully";
std::move(callback).Run(std::move(context), absl::nullopt);
}

} // namespace ash

0 comments on commit 73cf79a

Please sign in to comment.