Commit 65a25ae8 authored by Tien Mai's avatar Tien Mai Committed by Commit Bot

[GCPW] Disable GCPW signin dialog if Chrome is not running under winlogon

desktop.

Bug: 960774
Change-Id: I5fbacb88d28d9a2c3414409c82c832ad8e4b647d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1625306
Commit-Queue: Tien Mai <tienmai@chromium.org>
Reviewed-by: default avatarZhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarRoger Tawa <rogerta@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@google.com>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Reviewed-by: default avatarTommy Martino <tmartino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664722}
parent 5001c8ff
......@@ -761,6 +761,38 @@ void* GetUser32FunctionPointer(const char* function_name,
return nullptr;
}
string16 GetWindowObjectName(HANDLE handle) {
// Get the size of the name.
string16 object_name;
DWORD size = 0;
::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
if (!size) {
DPCHECK(false);
return object_name;
}
LOG_ASSERT(size % sizeof(wchar_t) == 0u);
// Query the name of the object.
if (!::GetUserObjectInformation(
handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
size, &size)) {
DPCHECK(false);
}
return object_name;
}
bool IsRunningUnderDesktopName(StringPiece16 desktop_name) {
HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
if (!thread_desktop)
return false;
string16 current_desktop_name = GetWindowObjectName(thread_desktop);
return EqualsCaseInsensitiveASCII(current_desktop_name, desktop_name);
}
ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
: initial_state_(IsEnrolledToDomain()) {
*GetDomainEnrollmentStateStorage() = state;
......
......@@ -31,6 +31,7 @@
#include "base/base_export.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
struct IPropertyStore;
struct _tagpropertykey;
......@@ -92,7 +93,8 @@ BASE_EXPORT bool SetAppIdForPropertyStore(IPropertyStore* property_store,
// Adds the specified |command| using the specified |name| to the AutoRun key.
// |root_key| could be HKCU or HKLM or the root of any user hive.
BASE_EXPORT bool AddCommandToAutoRun(HKEY root_key, const string16& name,
BASE_EXPORT bool AddCommandToAutoRun(HKEY root_key,
const string16& name,
const string16& command);
// Removes the command specified by |name| from the AutoRun key. |root_key|
// could be HKCU or HKLM or the root of any user hive.
......@@ -213,6 +215,14 @@ BASE_EXPORT void* GetUser32FunctionPointer(
const char* function_name,
NativeLibraryLoadError* error = nullptr);
// Returns the name of a desktop or a window station.
BASE_EXPORT string16 GetWindowObjectName(HANDLE handle);
// Checks if the calling thread is running under a desktop with the name
// given by |desktop_name|. |desktop_name| is ASCII case insensitive (non-ASCII
// characters will be compared with exact matches).
BASE_EXPORT bool IsRunningUnderDesktopName(StringPiece16 desktop_name);
// Allows changing the domain enrolled state for the life time of the object.
// The original state is restored upon destruction.
class BASE_EXPORT ScopedDomainStateForTesting {
......
......@@ -100,5 +100,31 @@ TEST(BaseWinUtilTest, String16FromGUID) {
EXPECT_STREQ(as_wcstr(guid_string16), clsid_string.get());
}
TEST(BaseWinUtilTest, GetWindowObjectName) {
base::string16 created_desktop_name(STRING16_LITERAL("test_desktop"));
HDESK desktop_handle =
::CreateDesktop(created_desktop_name.c_str(), nullptr, nullptr, 0,
DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
READ_CONTROL | WRITE_DAC | WRITE_OWNER,
nullptr);
ASSERT_NE(desktop_handle, nullptr);
EXPECT_EQ(created_desktop_name, GetWindowObjectName(desktop_handle));
ASSERT_TRUE(::CloseDesktop(desktop_handle));
}
TEST(BaseWinUtilTest, IsRunningUnderDesktopName) {
HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
ASSERT_NE(thread_desktop, nullptr);
base::string16 desktop_name = GetWindowObjectName(thread_desktop);
EXPECT_TRUE(IsRunningUnderDesktopName(desktop_name));
EXPECT_TRUE(IsRunningUnderDesktopName(base::ToLowerASCII(desktop_name)));
EXPECT_TRUE(IsRunningUnderDesktopName(base::ToUpperASCII(desktop_name)));
EXPECT_FALSE(IsRunningUnderDesktopName(
desktop_name + STRING16_LITERAL("_non_existent_desktop_name")));
}
} // namespace win
} // namespace base
......@@ -2298,6 +2298,7 @@ jumbo_split_static_library("ui") {
]
deps += [
"//chrome/browser/safe_browsing/chrome_cleaner:public",
"//chrome/browser/ui/startup:buildflags",
"//chrome/browser/win/conflicts:module_info",
"//chrome/credential_provider/common:common_constants",
"//components/search_engines",
......
# 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.
import("//build/buildflag_header.gni")
import("//build/config/chrome_build.gni")
# Allow browser tests that startup with --gcpw-signin flag only on
# non-Google Chrome builds.
can_test_gcpw_signin_startup = !is_chrome_branded
buildflag_header("buildflags") {
header = "buildflags.h"
flags = [ "CAN_TEST_GCPW_SIGNIN_STARTUP=$can_test_gcpw_signin_startup" ]
}
......@@ -13,6 +13,7 @@
#include "base/json/json_writer.h"
#include "base/strings/string16.h"
#include "base/syslog_logging.h"
#include "base/win/win_util.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.h"
......@@ -32,6 +33,10 @@
namespace {
#if BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
bool g_enable_gcpw_signin_during_tests = false;
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
// This message must match the one sent in inline_login.js: sendLSTFetchResults.
constexpr char kLSTFetchResultsMessage[] = "lstFetchResults";
......@@ -391,22 +396,49 @@ bool ValidateSigninCompleteResult(const std::string& access_token,
signin_result.is_dict();
}
void StartGCPWSignin(const base::CommandLine& command_line,
#if BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
void EnableGcpwSigninDialogForTesting(bool enable) {
g_enable_gcpw_signin_during_tests = enable;
}
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
bool CanStartGCPWSignin() {
#if BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
if (g_enable_gcpw_signin_during_tests)
return true;
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
// Ensure that we are running under a "winlogon" desktop before starting the
// gcpw sign in dialog.
return base::win::IsRunningUnderDesktopName(STRING16_LITERAL("winlogon"));
}
bool StartGCPWSignin(const base::CommandLine& command_line,
content::BrowserContext* context) {
// This keep_alive is created since there is no browser created when
// --gcpw-logon is specified. Since there is no browser there is no holder of
// a ScopedKeepAlive present that will ensure Chrome kills itself when the
// last keep alive is released. So instead, keep the keep alive across the
// callbacks that will be sent during the signin process. Once the full fetch
// of the information necesssary for the GCPW is finished (or there is a
// failure) release the keep alive so that Chrome can shutdown.
// If we are prevented from showing gcpw signin, return false and write our
// result so that the launch fails and the process can exit gracefully.
if (!CanStartGCPWSignin()) {
base::Value failure_result(base::Value::Type::DICTIONARY);
failure_result.SetKey(credential_provider::kKeyExitCode,
base::Value(static_cast<int>(
credential_provider::kUiecMissingSigninData)));
WriteResultToHandle(failure_result);
return false;
}
// This keep_alive is created since there is no browser created when
// --gcpw-logon is specified. Since there is no browser there is no holder
// of a ScopedKeepAlive present that will ensure Chrome kills itself when
// the last keep alive is released. So instead, keep the keep alive across
// the callbacks that will be sent during the signin process. Once the full
// fetch of the information necesssary for the GCPW is finished (or there is
// a failure) release the keep alive so that Chrome can shutdown.
ShowCredentialProviderSigninDialog(
command_line, context,
base::BindOnce(&HandleSigninCompleteForGcpwLogin,
std::make_unique<ScopedKeepAlive>(
KeepAliveOrigin::CREDENTIAL_PROVIDER_SIGNIN_DIALOG,
KeepAliveRestartOption::DISABLED)));
return true;
}
views::WebDialogView* ShowCredentialProviderSigninDialog(
......
......@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/ui/startup/buildflags.h"
namespace base {
class CommandLine;
......@@ -42,7 +43,9 @@ using HandleGcpwSigninCompleteResult =
// to choose an account to logon to Windows. Once the signin is complete, the
// flow will automatically start requesting additional information required by
// GCPW to complete Windows logon.
void StartGCPWSignin(const base::CommandLine& command_line,
// Returns false if the dialog could not be loaded due to the current execution
// mode.
bool StartGCPWSignin(const base::CommandLine& command_line,
content::BrowserContext* context);
// This function displays a dialog window with a Gaia signin page. Once
......@@ -54,4 +57,10 @@ views::WebDialogView* ShowCredentialProviderSigninDialog(
content::BrowserContext* context,
HandleGcpwSigninCompleteResult signin_complete_handler);
#if BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
// Allow displaying of GCPW signin dialog when not under the winlogon desktop
// for testing purposes.
void EnableGcpwSigninDialogForTesting(bool enable);
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
#endif // CHROME_BROWSER_UI_STARTUP_CREDENTIAL_PROVIDER_SIGNIN_DIALOG_WIN_H_
......@@ -8,6 +8,7 @@
#include "base/test/test_switches.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/startup/buildflags.h"
#include "chrome/browser/ui/startup/credential_provider_signin_dialog_win.h"
#include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
......@@ -375,31 +376,86 @@ INSTANTIATE_TEST_SUITE_P(
// the integration of the dialog into Chrome, This test mainly verifies
// correct start up state if we provide the --gcpw-signin switch.
class CredentialProviderSigninDialogWinIntegrationTest
class CredentialProviderSigninDialogWinIntegrationTestBase
: public CredentialProviderSigninDialogWinBaseTest {
protected:
CredentialProviderSigninDialogWinIntegrationTest();
CredentialProviderSigninDialogWinIntegrationTestBase();
// InProcessBrowserTest:
void SetUpCommandLine(base::CommandLine* command_line) override;
// CredentialProviderSigninDialogWinBaseTest:
void WaitForDialogToLoad() override;
private:
DISALLOW_COPY_AND_ASSIGN(CredentialProviderSigninDialogWinIntegrationTest);
DISALLOW_COPY_AND_ASSIGN(
CredentialProviderSigninDialogWinIntegrationTestBase);
};
CredentialProviderSigninDialogWinIntegrationTest::
CredentialProviderSigninDialogWinIntegrationTest()
CredentialProviderSigninDialogWinIntegrationTestBase::
CredentialProviderSigninDialogWinIntegrationTestBase()
: CredentialProviderSigninDialogWinBaseTest() {}
void CredentialProviderSigninDialogWinIntegrationTest::SetUpCommandLine(
void CredentialProviderSigninDialogWinIntegrationTestBase::SetUpCommandLine(
base::CommandLine* command_line) {
command_line->AppendSwitch(::credential_provider::kGcpwSigninSwitch);
}
void CredentialProviderSigninDialogWinIntegrationTest::WaitForDialogToLoad() {
// In the default state, the dialog would not be allowed to be displayed since
// Chrome will not be running on Winlogon desktop.
class CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest
: public CredentialProviderSigninDialogWinIntegrationTestBase {
protected:
CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest();
private:
DISALLOW_COPY_AND_ASSIGN(
CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest);
};
CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest::
CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest()
: CredentialProviderSigninDialogWinIntegrationTestBase() {}
IN_PROC_BROWSER_TEST_F(
CredentialProviderSigninDialogWinIntegrationDesktopVerificationTest,
DialogFailsToLoadOnIncorrectDesktop) {
// Normally the GCPW signin dialog should only run on "winlogon" desktops. If
// we are just running the test, we should not be under this desktop and the
// dialog should fail to load.
// No widgets should have been created.
views::Widget::Widgets all_widgets = views::test::WidgetTest::GetAllWidgets();
EXPECT_EQ(all_widgets.size(), 0ull);
}
#if BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
// This test overrides the check for the correct desktop to allow the dialog to
// be displayed even when not running on Winlogon desktop.
class CredentialProviderSigninDialogWinIntegrationDialogDisplayTest
: public CredentialProviderSigninDialogWinIntegrationTestBase {
protected:
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest();
~CredentialProviderSigninDialogWinIntegrationDialogDisplayTest() override;
// CredentialProviderSigninDialogWinBaseTest:
void WaitForDialogToLoad() override;
private:
DISALLOW_COPY_AND_ASSIGN(
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest);
};
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest::
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest()
: CredentialProviderSigninDialogWinIntegrationTestBase() {
EnableGcpwSigninDialogForTesting(true);
}
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest::
~CredentialProviderSigninDialogWinIntegrationDialogDisplayTest() {
EnableGcpwSigninDialogForTesting(false);
}
void CredentialProviderSigninDialogWinIntegrationDialogDisplayTest::
WaitForDialogToLoad() {
// The browser has already been created by the time this start starts and
// web_contents_ is not yet available. In this run case there should only
// be one widget available and that widget should contain the web contents
......@@ -412,7 +468,7 @@ void CredentialProviderSigninDialogWinIntegrationTest::WaitForDialogToLoad() {
web_contents_ = web_dialog->web_contents();
EXPECT_TRUE(web_contents_);
CredentialProviderSigninDialogWinBaseTest::WaitForDialogToLoad();
CredentialProviderSigninDialogWinIntegrationTestBase::WaitForDialogToLoad();
// When running with --gcpw-signin, browser creation is completely bypassed
// only a dialog for the signin should be created directly. In a normal
......@@ -421,7 +477,8 @@ void CredentialProviderSigninDialogWinIntegrationTest::WaitForDialogToLoad() {
EXPECT_FALSE(browser());
}
IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinIntegrationTest,
IN_PROC_BROWSER_TEST_F(
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest,
ShowDialogOnlyTest) {
WaitForDialogToLoad();
EXPECT_TRUE(
......@@ -431,7 +488,8 @@ IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinIntegrationTest,
RunUntilBrowserProcessQuits();
}
IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinIntegrationTest,
IN_PROC_BROWSER_TEST_F(
CredentialProviderSigninDialogWinIntegrationDialogDisplayTest,
EscapeClosesDialogTest) {
WaitForDialogToLoad();
views::Widget::Widgets all_widgets = views::test::WidgetTest::GetAllWidgets();
......@@ -441,3 +499,5 @@ IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinIntegrationTest,
(*all_widgets.begin())->OnKeyEvent(&escape_key_event);
RunUntilBrowserProcessQuits();
}
#endif // BUILDFLAG(CAN_TEST_GCPW_SIGNIN_STARTUP)
......@@ -358,9 +358,14 @@ bool StartupBrowserCreatorImpl::Launch(Profile* profile,
// signin page.
if (command_line_.HasSwitch(credential_provider::kGcpwSigninSwitch)) {
DCHECK(profile_->IsIncognitoProfile());
// NOTE: All launch urls are ignored when running with --gcpw-logon since
// NOTE: All launch urls are ignored when running with --gcpw-signin since
// this mode only loads Google's sign in page.
StartGCPWSignin(command_line_, profile_);
// If GCPW signin dialog fails, returning false here will allow Chrome to
// exit gracefully during the launch.
if (!StartGCPWSignin(command_line_, profile_))
return false;
RecordLaunchModeHistogram(LM_CREDENTIAL_PROVIDER_SIGNIN);
return true;
}
......
......@@ -414,13 +414,7 @@ class GpuSandboxedProcessLauncherDelegate
if (UseOpenGLRenderer())
return true;
HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
if (!thread_desktop)
return false;
base::string16 desktop_name = sandbox::GetWindowObjectName(thread_desktop);
::CloseDesktop(thread_desktop);
return !lstrcmpi(desktop_name.c_str(), L"winlogon");
return base::win::IsRunningUnderDesktopName(STRING16_LITERAL("winlogon"));
}
base::CommandLine cmd_line_;
......
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/filesystem_policy.h"
#include "sandbox/win/src/interception.h"
......@@ -215,7 +216,7 @@ ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
// Verify that everything is fine.
if (!alternate_winstation_handle_ ||
GetWindowObjectName(alternate_winstation_handle_).empty())
base::win::GetWindowObjectName(alternate_winstation_handle_).empty())
return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
// Create the destkop.
......@@ -226,7 +227,7 @@ ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
// Verify that everything is fine.
if (!alternate_desktop_handle_ ||
GetWindowObjectName(alternate_desktop_handle_).empty()) {
base::win::GetWindowObjectName(alternate_desktop_handle_).empty()) {
return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
}
} else {
......@@ -242,7 +243,8 @@ ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
// Verify that everything is fine.
if (!alternate_desktop_local_winstation_handle_ ||
GetWindowObjectName(alternate_desktop_local_winstation_handle_)
base::win::GetWindowObjectName(
alternate_desktop_local_winstation_handle_)
.empty()) {
return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
}
......
......@@ -9,6 +9,7 @@
#include <memory>
#include "base/logging.h"
#include "base/win/win_util.h"
#include "sandbox/win/src/acl.h"
#include "sandbox/win/src/sid.h"
......@@ -127,29 +128,6 @@ ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
}
base::string16 GetWindowObjectName(HANDLE handle) {
// Get the size of the name.
DWORD size = 0;
::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
if (!size) {
NOTREACHED();
return base::string16();
}
// Create the buffer that will hold the name.
std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
// Query the name of the object.
if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size,
&size)) {
NOTREACHED();
return base::string16();
}
return base::string16(name_buffer.get());
}
base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
if (!desktop) {
NOTREACHED();
......@@ -158,11 +136,11 @@ base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
base::string16 name;
if (winsta) {
name = GetWindowObjectName(winsta);
name = base::win::GetWindowObjectName(winsta);
name += L'\\';
}
name += GetWindowObjectName(desktop);
name += base::win::GetWindowObjectName(desktop);
return name;
}
......
......@@ -27,9 +27,6 @@ ResultCode CreateAltWindowStation(HWINSTA* winsta);
// the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION.
ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop);
// Returns the name of a desktop or a window station.
base::string16 GetWindowObjectName(HANDLE handle);
// Returns the name of the desktop referenced by |desktop|. If a window
// station is specified, the name is prepended with the window station name,
// followed by a backslash. This name can be used as the lpDesktop parameter
......
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