blob: b549dae1e0c522030a92f10c730f5162ac7427c2 [file] [log] [blame]
// 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/credential_provider/gaiacp/dllmain.h"
// It is important to include gaia_credential_base.h, reauth_credential.h and
// gaia_credential.h here, even though the classes are not used in this source
// file, in order to ensure that the COM objects will be properly linked into
// the dll. This became important after moving all the COM object code into a
// static library for testing purposes.
//
// This article clearly describes the issue and the fix:
//
// https://blogs.msdn.microsoft.com/larryosterman/2004/09/27/when-i-moved-my-code-into-a-library-what-happened-to-my-atl-com-objects/
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/current_module.h"
#include "base/win/registry.h"
#include "chrome/common/chrome_version.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_credential.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_base.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_module.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/os_process_manager.h"
#include "chrome/credential_provider/gaiacp/os_user_manager.h"
#include "chrome/credential_provider/gaiacp/reauth_credential.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "components/crash/content/app/crash_switches.h"
#include "components/crash/content/app/run_as_crashpad_handler_win.h"
#include "content/public/common/content_switches.h"
using credential_provider::putHR;
credential_provider::CGaiaCredentialProviderModule _AtlModule;
#define __TYPELIB_ID_SUFFIX_STRING(id) L"\\" _T(#id)
#define _TYPELIB_ID_SUFFIX_STRING(id) __TYPELIB_ID_SUFFIX_STRING(id)
#define TYPELIB_SUFFIX_STRING \
_TYPELIB_ID_SUFFIX_STRING(TLB_GAIACREDENTIALPROVIDER)
// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hinstance,
DWORD reason,
LPVOID reserved) {
return _AtlModule.DllMain(hinstance, reason, reserved);
}
using namespace ATL;
// Used to determine whether the DLL can be unloaded by OLE.
STDAPI DllCanUnloadNow(void) {
HRESULT hr = _AtlModule.DllCanUnloadNow();
LOGFN(INFO) << "hr=" << putHR(hr);
return hr;
}
// Returns a class factory to create an object of the requested type.
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
// Check to see if the credential provider has crashed too much recently.
// If it has then do not allow it to create any credential providers.
if (!credential_provider::VerifyStartupSentinel()) {
LOGFN(ERROR) << "Disabled due to previous unsuccessful starts";
return E_NOTIMPL;
}
return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}
// DllRegisterServer - Adds entries to the system registry.
STDAPI DllRegisterServer(void) {
HRESULT hr = credential_provider::CGaiaCredentialBase::OnDllRegisterServer();
LOGFN(INFO) << "CGaiaCredential::OnDllRegisterServer hr=" << putHR(hr);
if (SUCCEEDED(hr)) {
// Registers object. FALSE means don't register typelib. The default
// behaviour is assume the typelib has ID 1. But in this case grit can't
// be forced to use an ID of 1 when writing the rc file.
hr = _AtlModule.DllRegisterServer(FALSE);
LOGFN(INFO) << "_AtlModule.DllRegisterServer hr=" << putHR(hr);
}
#if defined(GOOGLE_CHROME_BUILD)
// Register with Google Update.
if (SUCCEEDED(hr)) {
base::win::RegKey key(HKEY_LOCAL_MACHINE,
credential_provider::kRegUpdaterClientsAppPath,
KEY_SET_VALUE | KEY_WOW64_32KEY);
LONG sts = key.WriteValue(L"pv", TEXT(CHROME_VERSION_STRING));
if (sts != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(sts);
LOGFN(ERROR) << "key.WriteValue(pv) hr=" << putHR(hr);
} else {
sts = key.WriteValue(
L"name",
credential_provider::GetStringResource(IDS_PROJNAME_BASE).c_str());
if (sts != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(sts);
LOGFN(ERROR) << "key.WriteValue(name) hr=" << putHR(hr);
}
}
}
#endif // defined(GOOGLE_CHROME_BUILD)
return hr;
}
// DllUnregisterServer - Removes entries from the system registry.
STDAPI DllUnregisterServer(void) {
#if defined(GOOGLE_CHROME_BUILD)
// Unregister with Google Update.
base::win::RegKey key(HKEY_LOCAL_MACHINE, L"", DELETE | KEY_WOW64_32KEY);
LONG sts = key.DeleteKey(credential_provider::kRegUpdaterClientsAppPath);
bool all_succeeded = sts == ERROR_SUCCESS;
#else
bool all_succeeded = true;
#endif
HRESULT hr =
credential_provider::CGaiaCredentialBase::OnDllUnregisterServer();
LOGFN(INFO) << "CGaiaCredential::OnDllUnregisterServer hr=" << putHR(hr);
all_succeeded &= SUCCEEDED(hr);
hr = _AtlModule.DllUnregisterServer(FALSE);
LOGFN(INFO) << "_AtlModule.DllUnregisterServer hr=" << putHR(hr);
all_succeeded &= SUCCEEDED(hr);
return all_succeeded ? S_OK : E_FAIL;
}
// This entry point is called via rundll32. See
// CGaiaCredential::WaitForLoginUI() for details.
void CALLBACK SaveAccountInfoW(HWND /*hwnd*/,
HINSTANCE /*hinst*/,
wchar_t* /*pszCmdLine*/,
int /*show*/) {
LOGFN(INFO);
HANDLE hStdin = ::GetStdHandle(STD_INPUT_HANDLE); // No need to close.
if (hStdin == INVALID_HANDLE_VALUE) {
LOGFN(INFO) << "No stdin";
return;
}
char buffer[credential_provider::CGaiaCredentialBase::kAccountInfoBufferSize];
DWORD buffer_len_bytes = static_cast<DWORD>(sizeof(buffer)); // In bytes.
if (!::ReadFile(hStdin, buffer, buffer_len_bytes, &buffer_len_bytes,
nullptr)) {
HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
LOGFN(ERROR) << "ReadFile hr=" << putHR(hr);
return;
}
buffer[buffer_len_bytes] = 0;
// Don't log |buffer| since it contains sensitive info like password.
HRESULT hr = S_OK;
base::DictionaryValue* dict = nullptr;
std::unique_ptr<base::Value> properties = base::JSONReader::ReadDeprecated(
buffer, base::JSON_ALLOW_TRAILING_COMMAS);
if (!properties || !properties->GetAsDictionary(&dict)) {
LOGFN(ERROR) << "base::JSONReader::Read failed length=" << buffer_len_bytes;
hr = E_FAIL;
}
hr = credential_provider::CGaiaCredentialBase::SaveAccountInfo(*dict);
if (FAILED(hr))
LOGFN(ERROR) << "SaveAccountInfoW hr=" << putHR(hr);
// If an MDM URL is configured in the registry, use it.
wchar_t mdm_url[256];
ULONG length = base::size(mdm_url);
hr = credential_provider::GetGlobalFlag(L"mdm", mdm_url, &length);
if (SUCCEEDED(hr)) {
dict->SetString(credential_provider::kKeyMdmUrl, mdm_url);
hr = credential_provider::EnrollToGoogleMdmIfNeeded(*dict);
if (FAILED(hr))
LOGFN(INFO) << "EnrollToGoogleMdmIfNeeded hr=" << putHR(hr);
} else {
LOGFN(INFO) << "Not enrolling to MDM";
}
LOGFN(INFO) << "Done";
}
void CALLBACK RunAsCrashpadHandlerW(HWND /*hwnd*/,
HINSTANCE /*hinst*/,
wchar_t* /*pszCmdLine*/,
int /*show*/) {
base::CommandLine::Init(0, nullptr);
const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
DCHECK_EQ(cmd_line->GetSwitchValueASCII(switches::kProcessType),
crash_reporter::switches::kCrashpadHandler);
base::string16 entrypoint_arg;
credential_provider::GetEntryPointArgumentForRunDll(
CURRENT_MODULE(), credential_provider::kRunAsCrashpadHandlerEntryPoint,
&entrypoint_arg);
// Get all the arguments from the original command line except for the
// entrypoint argument otherwise the crashpad handler will fail to start
// thinking that it is an invalid argument.
base::CommandLine::StringVector argv_without_entry_point;
argv_without_entry_point.reserve(cmd_line->argv().size());
for (const auto& argv : cmd_line->argv()) {
if (argv == entrypoint_arg)
continue;
argv_without_entry_point.push_back(argv);
}
base::CommandLine cmd_line_without_entry_point(argv_without_entry_point);
crash_reporter::RunAsCrashpadHandler(cmd_line_without_entry_point,
base::FilePath(), switches::kProcessType,
"user-data-dir");
}
void CALLBACK
SetFakesForTesting(const credential_provider::FakesForTesting* fakes) {
DCHECK(fakes);
credential_provider::ScopedLsaPolicy::SetCreatorForTesting(
fakes->scoped_lsa_policy_creator);
credential_provider::OSUserManager::SetInstanceForTesting(
fakes->os_user_manager_for_testing);
credential_provider::OSProcessManager::SetInstanceForTesting(
fakes->os_process_manager_for_testing);
_AtlModule.set_is_testing(true);
}