chromium/chrome/credential_provider/gaiacp/reauth_credential.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/credential_provider/gaiacp/reauth_credential.h"

#include "base/command_line.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_resources.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/gcpw_strings.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/os_user_manager.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"

namespace credential_provider {

// TODO(crbug.com/40142636): Change it back to emdedded/reauth/windows
// once reauth flow latency issue is resolved.
constexpr char kGaiaReauthPath[] = "embedded/setup/windows";

CReauthCredential::CReauthCredential() = default;

CReauthCredential::~CReauthCredential() = default;

HRESULT CReauthCredential::FinalConstruct() {
  LOGFN(VERBOSE);
  return S_OK;
}

void CReauthCredential::FinalRelease() {
  LOGFN(VERBOSE);
}

// CGaiaCredentialBase /////////////////////////////////////////////////////////
bool CReauthCredential::CheckIfTosAccepted() {
  DCHECK(os_user_sid_.Length());

  DWORD acceptTos = 0;
  HRESULT hr = GetUserProperty(OLE2W(os_user_sid_), kKeyAcceptTos, &acceptTos);
  if (FAILED(hr))
    LOGFN(ERROR) << "Failed getting accept_tos. hr = " << putHR(hr);
  return acceptTos == 1;
}

HRESULT CReauthCredential::GetUserGlsCommandline(
    base::CommandLine* command_line) {
  DCHECK(command_line);
  DCHECK(os_user_sid_.Length());

  // This boolean is set to false if generating GlsCommandLine HRESULT
  // is E_UNEXPECTED.
  bool get_cmd_line_status = false;

  // Check if tos is accepted. If not, we need to load gaia login page
  // with ToS acceptance screen.
  // Note:
  // 1. We need to append this switch irrespective of whether its a
  // reauth flow vs add user flow.
  // 2. We only show tos for GEM usecases.
  bool show_tos = false;
  if (!CheckIfTosAccepted() && IsGemEnabled())
    show_tos = true;

  // If this is an existing user with an SID, try to get its gaia id and pass
  // it to the GLS for verification.
  std::wstring gaia_id;
  if (GetIdFromSid(OLE2CW(os_user_sid_), &gaia_id) == S_OK &&
      !gaia_id.empty()) {
    command_line->AppendSwitchNative(kGaiaIdSwitch, gaia_id);
    get_cmd_line_status = true;
  } else if (CGaiaCredentialBase::IsCloudAssociationEnabled() &&
             OSUserManager::Get()->IsUserDomainJoined(OLE2CW(os_user_sid_))) {
    // Note that if ADAssociationIsEnabled and the reauth credential is an AD
    // user account, then fallback to the GaiaCredentialBase for loading Gls.
    get_cmd_line_status = true;
  }

  HRESULT hr;
  // If there is an existing email with an SID then pass it to the GLS
  // as PrefillEmail switch.
  if (email_for_reauth_.Length()) {
    get_cmd_line_status = true;
    command_line->AppendSwitchNative(kPrefillEmailSwitch,
                                     OLE2CW(email_for_reauth_));
    // Use kGaiaReauthPath when there is no email_for_reauth_ field set.
    hr = SetGaiaEndpointCommandLineIfNeeded(L"ep_reauth_url", kGaiaReauthPath,
                                            IsGemEnabled(), show_tos,
                                            command_line);
  } else {
    // Use kGaiaSetupPath when there is no email_for_reauth_ field set.
    hr = SetGaiaEndpointCommandLineIfNeeded(L"ep_reauth_url", kGaiaSetupPath,
                                            IsGemEnabled(), show_tos,
                                            command_line);
  }

  if (FAILED(hr)) {
    LOGFN(ERROR) << "Setting gaia url for reauth credential on user="
                 << os_username_ << " failed";
    return E_FAIL;
  }

  if (get_cmd_line_status) {
    return CGaiaCredentialBase::GetUserGlsCommandline(command_line);
  } else {
    LOGFN(ERROR) << "Reauth credential on user=" << os_username_
                 << " does not have an associated Gaia id or Email address";
    return E_UNEXPECTED;
  }
}

HRESULT CReauthCredential::ValidateExistingUser(const std::wstring& username,
                                                const std::wstring& domain,
                                                const std::wstring& sid,
                                                BSTR* error_text) {
  DCHECK(os_username_.Length());
  DCHECK(os_user_sid_.Length());

  // SID, domain and username found must match what is stored in this
  // credential.
  if ((os_username_ != W2COLE(username.c_str())) ||
      (os_user_domain_.Length() && os_user_domain_ != W2COLE(domain.c_str())) ||
      (os_user_sid_.Length() && os_user_sid_ != W2COLE(sid.c_str()))) {
    LOGFN(ERROR) << "Username '" << domain << "\\" << username << "' or SID '"
                 << sid << "' does not match the username '"
                 << OLE2CW(os_user_domain_) << "\\" << OLE2CW(os_username_)
                 << "' or SID '" << OLE2CW(os_user_sid_)
                 << "' for this credential";
    *error_text = AllocErrorString(IDS_ACCOUNT_IN_USE_BASE);
    return E_UNEXPECTED;
  }

  return S_OK;
}

HRESULT CReauthCredential::GetStringValueImpl(DWORD field_id, wchar_t** value) {
  LOGFN(VERBOSE) << "field_id=" << field_id;

  if (field_id == FID_PROVIDER_LABEL) {
    std::wstring label(
        GetStringResource(IDS_EXISTING_AUTH_FID_PROVIDER_LABEL_BASE));
    LOGFN(VERBOSE) << "label=" << label;
    return ::SHStrDupW(label.c_str(), value);
  } else if (field_id == FID_DESCRIPTION) {
    wchar_t* sid_buffer = nullptr;
    HRESULT hr = GetUserSid(&sid_buffer);
    if (FAILED(hr)) {
      LOGFN(ERROR) << "GetUserSid: Empty sid found";
      return ::SHStrDupW(std::wstring().c_str(), value);
    }
    std::wstring sid = sid_buffer;
    ::CoTaskMemFree(sid_buffer);

    int description_label_id;
    // If its an AD user sid without a user_id set in the registry, then
    // we need to show a different description message.
    if (email_for_reauth_.Length() == 0 &&
        CGaiaCredentialBase::IsCloudAssociationEnabled() &&
        OSUserManager::Get()->IsUserDomainJoined(sid)) {
      description_label_id = IDS_REAUTH_AD_NO_USER_FID_DESCRIPTION_BASE;
    } else {
      auto auth_enforce_reason =
          AssociatedUserValidator::Get()->GetAuthEnforceReason(sid);
      switch (auth_enforce_reason) {
        case AssociatedUserValidator::EnforceAuthReason::NOT_ENROLLED_WITH_MDM:
          description_label_id =
              IDS_REAUTH_NOT_ENROLLED_WITH_MDM_FID_DESCRIPTION_BASE;
          break;
        case AssociatedUserValidator::EnforceAuthReason::
            MISSING_PASSWORD_RECOVERY_INFO:
          description_label_id =
              IDS_REAUTH_MISSING_PASSWORD_RECOVERY_INFO_FID_DESCRIPTION_BASE;
          break;
        case AssociatedUserValidator::EnforceAuthReason::
            UPLOAD_DEVICE_DETAILS_FAILED:
          description_label_id =
              IDS_REAUTH_FAILED_UPLOAD_DEVICE_DETAILS_DESCRIPTION_BASE;
          break;
        case AssociatedUserValidator::EnforceAuthReason::
            ONLINE_LOGIN_ENFORCED:
          description_label_id =
              IDS_REAUTH_ONLINE_LOGIN_ENFORCED_DESCRIPTION_BASE;
          break;
        case AssociatedUserValidator::EnforceAuthReason::
            MISSING_OR_STALE_USER_POLICIES:
          description_label_id = IDS_REAUTH_MISSING_POLICIES_DESCRIPTION_BASE;
          break;
        default:
          description_label_id = IDS_REAUTH_FID_DESCRIPTION_BASE;
          break;
      }
    }

    std::wstring label(GetStringResource(description_label_id));
    LOGFN(VERBOSE) << "field_id=" << field_id << " label=" << label;

    return ::SHStrDupW(label.c_str(), value);
  }

  return CGaiaCredentialBase::GetStringValueImpl(field_id, value);
}

// ICredentialProviderCredential2 //////////////////////////////////////////////

HRESULT CReauthCredential::GetUserSid(wchar_t** sid) {
  USES_CONVERSION;
  DCHECK(sid);
  LOGFN(VERBOSE) << "sid=" << OLE2CW(get_os_user_sid());

  HRESULT hr = ::SHStrDupW(OLE2CW(get_os_user_sid()), sid);
  if (FAILED(hr))
    LOGFN(ERROR) << "SHStrDupW hr=" << putHR(hr);

  return hr;
}

// IReauthCredential //////////////////////////////////////////////

HRESULT CReauthCredential::SetOSUserInfo(BSTR sid, BSTR domain, BSTR username) {
  DCHECK(sid);
  DCHECK(domain);
  DCHECK(username);
  LOGFN(VERBOSE);

  os_user_domain_ = domain;
  os_user_sid_ = sid;
  os_username_ = username;

  // Set the default credential provider for this tile.
  HRESULT hr =
      SetLogonUiUserTileEntry(OLE2W(sid), CLSID_GaiaCredentialProvider);
  if (FAILED(hr))
    LOGFN(ERROR) << "SetLogonUIUserTileEntry hr=" << putHR(hr);

  return hr;
}

HRESULT CReauthCredential::SetEmailForReauth(BSTR email) {
  DCHECK(email);
  LOGFN(VERBOSE) << "email=" << email;

  email_for_reauth_ = email;
  return S_OK;
}

}  // namespace credential_provider