summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/signin/signin_util_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/signin/signin_util_win.cc')
-rw-r--r--chromium/chrome/browser/signin/signin_util_win.cc332
1 files changed, 332 insertions, 0 deletions
diff --git a/chromium/chrome/browser/signin/signin_util_win.cc b/chromium/chrome/browser/signin/signin_util_win.cc
new file mode 100644
index 00000000000..0ffc61db1b6
--- /dev/null
+++ b/chromium/chrome/browser/signin/signin_util_win.cc
@@ -0,0 +1,332 @@
+// Copyright 2018 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/signin/signin_util_win.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "base/win/win_util.h"
+#include "base/win/wincrypt_shim.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/first_run/first_run.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/about_signin_internals_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
+#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
+#include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
+#include "chrome/credential_provider/common/gcp_strings.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/about_signin_internals.h"
+#include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/identity_manager/accounts_mutator.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+
+namespace signin_util {
+
+namespace {
+
+std::unique_ptr<DiceTurnSyncOnHelper::Delegate>*
+GetDiceTurnSyncOnHelperDelegateForTestingStorage() {
+ static base::NoDestructor<std::unique_ptr<DiceTurnSyncOnHelper::Delegate>>
+ delegate;
+ return delegate.get();
+}
+
+std::string DecryptRefreshToken(const std::string& cipher_text) {
+ DATA_BLOB input;
+ input.pbData =
+ const_cast<BYTE*>(reinterpret_cast<const BYTE*>(cipher_text.data()));
+ input.cbData = static_cast<DWORD>(cipher_text.length());
+ DATA_BLOB output;
+ BOOL result = ::CryptUnprotectData(&input, nullptr, nullptr, nullptr, nullptr,
+ CRYPTPROTECT_UI_FORBIDDEN, &output);
+
+ if (!result)
+ return std::string();
+
+ std::string refresh_token(reinterpret_cast<char*>(output.pbData),
+ output.cbData);
+ ::LocalFree(output.pbData);
+ return refresh_token;
+}
+
+// Finish the process of import credentials. This is either called directly
+// from ImportCredentialsFromProvider() if a browser window for the profile is
+// already available or is delayed until a browser can first be opened.
+void FinishImportCredentialsFromProvider(const CoreAccountId& account_id,
+ Browser* browser,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ // DiceTurnSyncOnHelper deletes itself once done.
+ if (GetDiceTurnSyncOnHelperDelegateForTestingStorage()->get()) {
+ new DiceTurnSyncOnHelper(
+ profile, signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON,
+ signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT,
+ signin_metrics::Reason::kSigninPrimaryAccount, account_id,
+ DiceTurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT,
+ std::move(*GetDiceTurnSyncOnHelperDelegateForTestingStorage()),
+ base::DoNothing());
+ } else {
+ if (!browser)
+ browser = chrome::FindLastActiveWithProfile(profile);
+
+ new DiceTurnSyncOnHelper(
+ profile, browser,
+ signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON,
+ signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT,
+ signin_metrics::Reason::kSigninPrimaryAccount, account_id,
+ DiceTurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT);
+ }
+}
+
+// Start the process of importing credentials from the credential provider given
+// that all the required information is available. The process depends on
+// having a browser window for the profile. If a browser window exists the
+// profile be signed in and sync will be starting up. If not, the profile will
+// be still be signed in but sync will be started once the browser window is
+// ready.
+void ImportCredentialsFromProvider(Profile* profile,
+ const std::wstring& gaia_id,
+ const std::wstring& email,
+ const std::string& refresh_token,
+ bool turn_on_sync) {
+ // For debugging purposes, record that the credentials for this profile
+ // came from a credential provider.
+ AboutSigninInternals* signin_internals =
+ AboutSigninInternalsFactory::GetInstance()->GetForProfile(profile);
+ signin_internals->OnAuthenticationResultReceived("Credential Provider");
+
+ CoreAccountId account_id =
+ IdentityManagerFactory::GetForProfile(profile)
+ ->GetAccountsMutator()
+ ->AddOrUpdateAccount(base::WideToUTF8(gaia_id),
+ base::WideToUTF8(email), refresh_token,
+ /*is_under_advanced_protection=*/false,
+ signin_metrics::SourceForRefreshTokenOperation::
+ kMachineLogon_CredentialProvider);
+
+ if (turn_on_sync) {
+ Browser* browser = chrome::FindLastActiveWithProfile(profile);
+ if (browser) {
+ FinishImportCredentialsFromProvider(account_id, browser, profile,
+ Profile::CREATE_STATUS_CREATED);
+ } else {
+ // If no active browser exists yet, this profile is in the process of
+ // being created. Wait for the browser to be created before finishing the
+ // sign in. This object deletes itself when done.
+ new profiles::BrowserAddedForProfileObserver(
+ profile, base::BindRepeating(&FinishImportCredentialsFromProvider,
+ account_id, nullptr));
+ }
+ }
+
+ // Mark this profile as having been signed in with the credential provider.
+ profile->GetPrefs()->SetBoolean(prefs::kSignedInWithCredentialProvider, true);
+}
+
+// Extracts the |cred_provider_gaia_id| and |cred_provider_email| for the user
+// signed in throuhg credential provider.
+void ExtractCredentialProviderUser(std::wstring* cred_provider_gaia_id,
+ std::wstring* cred_provider_email) {
+ DCHECK(cred_provider_gaia_id);
+ DCHECK(cred_provider_email);
+
+ cred_provider_gaia_id->clear();
+ cred_provider_email->clear();
+
+ base::win::RegKey key;
+ if (key.Open(HKEY_CURRENT_USER, credential_provider::kRegHkcuAccountsPath,
+ KEY_READ) != ERROR_SUCCESS) {
+ return;
+ }
+
+ base::win::RegistryKeyIterator it(key.Handle(), L"");
+ if (!it.Valid() || it.SubkeyCount() != 1)
+ return;
+
+ base::win::RegKey key_account(key.Handle(), it.Name(), KEY_QUERY_VALUE);
+ if (!key_account.Valid())
+ return;
+
+ std::wstring email;
+ if (key_account.ReadValue(
+ base::UTF8ToWide(credential_provider::kKeyEmail).c_str(), &email) !=
+ ERROR_SUCCESS) {
+ return;
+ }
+
+ *cred_provider_gaia_id = it.Name();
+ *cred_provider_email = email;
+}
+
+// Attempt to sign in with a credentials from a system installed credential
+// provider if available. If |auth_gaia_id| is not empty then the system
+// credential must be for the same account. Starts the process to turn on DICE
+// only if |turn_on_sync| is true.
+bool TrySigninWithCredentialProvider(Profile* profile,
+ const std::wstring& auth_gaia_id,
+ bool turn_on_sync) {
+ base::win::RegKey key;
+ if (key.Open(HKEY_CURRENT_USER, credential_provider::kRegHkcuAccountsPath,
+ KEY_READ) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ base::win::RegistryKeyIterator it(key.Handle(), L"");
+ if (!it.Valid() || it.SubkeyCount() == 0)
+ return false;
+
+ base::win::RegKey key_account(key.Handle(), it.Name(), KEY_READ | KEY_WRITE);
+ if (!key_account.Valid())
+ return false;
+
+ std::wstring gaia_id = it.Name();
+ if (!auth_gaia_id.empty() && auth_gaia_id != gaia_id)
+ return false;
+
+ std::wstring email;
+ if (key_account.ReadValue(
+ base::UTF8ToWide(credential_provider::kKeyEmail).c_str(), &email) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ // Read the encrypted refresh token. The data is stored in binary format.
+ // No matter what happens, delete the registry entry.
+
+ std::string encrypted_refresh_token;
+ DWORD size = 0;
+ DWORD type;
+ if (key_account.ReadValue(
+ base::UTF8ToWide(credential_provider::kKeyRefreshToken).c_str(),
+ nullptr, &size, &type) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ encrypted_refresh_token.resize(size);
+ bool reauth_attempted = false;
+ key_account.ReadValue(
+ base::UTF8ToWide(credential_provider::kKeyRefreshToken).c_str(),
+ const_cast<char*>(encrypted_refresh_token.c_str()), &size, &type);
+ if (!gaia_id.empty() && !email.empty() && type == REG_BINARY &&
+ !encrypted_refresh_token.empty()) {
+ std::string refresh_token = DecryptRefreshToken(encrypted_refresh_token);
+ if (!refresh_token.empty()) {
+ reauth_attempted = true;
+ ImportCredentialsFromProvider(profile, gaia_id, email, refresh_token,
+ turn_on_sync);
+ }
+ }
+
+ key_account.DeleteValue(
+ base::UTF8ToWide(credential_provider::kKeyRefreshToken).c_str());
+ return reauth_attempted;
+}
+
+} // namespace
+
+void SetDiceTurnSyncOnHelperDelegateForTesting(
+ std::unique_ptr<DiceTurnSyncOnHelper::Delegate> delegate) {
+ GetDiceTurnSyncOnHelperDelegateForTestingStorage()->swap(delegate);
+}
+
+// Credential provider needs to stick to profile it previously used to import
+// credentials. Thus, if there is another profile that was previously signed in
+// with credential provider regardless of whether user signed in or out,
+// credential provider shouldn't attempt to import credentials into current
+// profile.
+bool IsGCPWUsedInOtherProfile(Profile* profile) {
+ DCHECK(profile);
+
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (profile_manager) {
+ std::vector<ProfileAttributesEntry*> entries =
+ profile_manager->GetProfileAttributesStorage()
+ .GetAllProfilesAttributes();
+
+ for (const ProfileAttributesEntry* entry : entries) {
+ if (entry->GetPath() == profile->GetPath())
+ continue;
+
+ if (entry->IsSignedInWithCredentialProvider())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SigninWithCredentialProviderIfPossible(Profile* profile) {
+ // This flow is used for first time signin through credential provider. Any
+ // subsequent signin for the credential provider user needs to go through
+ // reauth flow.
+ if (profile->GetPrefs()->GetBoolean(prefs::kSignedInWithCredentialProvider))
+ return;
+
+ std::wstring cred_provider_gaia_id;
+ std::wstring cred_provider_email;
+
+ ExtractCredentialProviderUser(&cred_provider_gaia_id, &cred_provider_email);
+ if (cred_provider_gaia_id.empty() || cred_provider_email.empty())
+ return;
+
+ // Chrome doesn't allow signing into current profile if the same user is
+ // signed in another profile.
+ if (!CanOfferSignin(profile, base::WideToUTF8(cred_provider_gaia_id),
+ base::WideToUTF8(cred_provider_email))
+ .IsOk() ||
+ IsGCPWUsedInOtherProfile(profile)) {
+ return;
+ }
+
+ auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+ std::wstring gaia_id;
+ if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
+ gaia_id = base::UTF8ToWide(
+ identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync)
+ .gaia);
+ }
+
+ TrySigninWithCredentialProvider(profile, gaia_id, gaia_id.empty());
+}
+
+bool ReauthWithCredentialProviderIfPossible(Profile* profile) {
+ // Check to see if auto signin information is available. Only applies if:
+ //
+ // - The profile is marked as having been signed in with a system credential.
+ // - The profile is already signed in.
+ // - The profile is in an auth error state.
+ auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+ if (!(profile->GetPrefs()->GetBoolean(
+ prefs::kSignedInWithCredentialProvider) &&
+ identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync) &&
+ identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+ identity_manager->GetPrimaryAccountId(
+ signin::ConsentLevel::kSync)))) {
+ return false;
+ }
+
+ std::wstring gaia_id = base::UTF8ToWide(
+ identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync)
+ .gaia.c_str());
+ return TrySigninWithCredentialProvider(profile, gaia_id, false);
+}
+
+} // namespace signin_util