blob: 0e43843e3787fde1ec8c5d4a4ec1f7fe80a2f9bb [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/browser/policy/browser_dm_token_storage_win.h"
#include <windows.h>
#include <comutil.h>
#include <objbase.h>
#include <oleauto.h>
#include <unknwn.h>
#include <winerror.h>
#include <wrl/client.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/win/registry.h"
#include "base/win/scoped_bstr.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/util_constants.h"
#include "content/public/browser/browser_thread.h"
#if defined(GOOGLE_CHROME_BUILD)
#include "google_update/google_update_idl.h"
#endif // defined(GOOGLE_CHROME_BUILD)
namespace policy {
namespace {
#if defined(GOOGLE_CHROME_BUILD)
// Explicitly allow DMTokenStorage impersonate the client since some COM code
// elsewhere in the browser process may have previously used
// CoInitializeSecurity to set the impersonation level to something other than
// the default. Ignore errors since an attempt to use Google Update may succeed
// regardless.
void ConfigureProxyBlanket(IUnknown* interface_pointer) {
::CoSetProxyBlanket(
interface_pointer, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING);
}
#endif // defined(GOOGLE_CHROME_BUILD)
bool StoreDMTokenInRegistry(const std::string& token) {
#if defined(GOOGLE_CHROME_BUILD)
if (token.empty())
return false;
Microsoft::WRL::ComPtr<IGoogleUpdate3Web> google_update;
HRESULT hr = ::CoCreateInstance(CLSID_GoogleUpdate3WebServiceClass, nullptr,
CLSCTX_ALL, IID_PPV_ARGS(&google_update));
if (FAILED(hr))
return false;
ConfigureProxyBlanket(google_update.Get());
Microsoft::WRL::ComPtr<IDispatch> dispatch;
hr = google_update->createAppBundleWeb(dispatch.GetAddressOf());
if (FAILED(hr))
return false;
Microsoft::WRL::ComPtr<IAppBundleWeb> app_bundle;
hr = dispatch.CopyTo(app_bundle.GetAddressOf());
if (FAILED(hr))
return false;
dispatch.Reset();
ConfigureProxyBlanket(app_bundle.Get());
app_bundle->initialize();
const wchar_t* app_guid = install_static::GetAppGuid();
hr = app_bundle->createInstalledApp(base::win::ScopedBstr(app_guid));
if (FAILED(hr))
return false;
hr = app_bundle->get_appWeb(0, dispatch.GetAddressOf());
if (FAILED(hr))
return false;
Microsoft::WRL::ComPtr<IAppWeb> app;
hr = dispatch.CopyTo(app.GetAddressOf());
if (FAILED(hr))
return false;
dispatch.Reset();
ConfigureProxyBlanket(app.Get());
hr = app->get_command(base::win::ScopedBstr(installer::kCmdStoreDMToken),
dispatch.GetAddressOf());
if (FAILED(hr) || !dispatch)
return false;
Microsoft::WRL::ComPtr<IAppCommandWeb> app_command;
hr = dispatch.CopyTo(app_command.GetAddressOf());
if (FAILED(hr))
return false;
ConfigureProxyBlanket(app_command.Get());
std::string token_base64;
base::Base64Encode(token, &token_base64);
VARIANT var;
VariantInit(&var);
_variant_t token_var = token_base64.c_str();
hr = app_command->execute(token_var, var, var, var, var, var, var, var, var);
if (FAILED(hr))
return false;
// TODO(crbug.com/823515): Get the status of the app command execution and
// return a corresponding value for |success|. For now, assume that the call
// to setup.exe succeeds.
return true;
#else
return false;
#endif // defined(GOOGLE_CHROME_BUILD)
}
} // namespace
std::string BrowserDMTokenStorageWin::InitClientId() {
// For the client id, use the Windows machine GUID.
// TODO(crbug.com/821977): Need a backup plan if machine GUID doesn't exist.
base::win::RegKey key;
LSTATUS status =
key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography",
KEY_QUERY_VALUE | KEY_WOW64_64KEY);
if (status != ERROR_SUCCESS)
return std::string();
base::string16 value;
status = key.ReadValue(L"MachineGuid", &value);
if (status != ERROR_SUCCESS)
return std::string();
std::string client_id;
if (!base::WideToUTF8(value.c_str(), value.length(), &client_id))
return std::string();
return client_id;
}
std::string BrowserDMTokenStorageWin::InitEnrollmentToken() {
return base::WideToUTF8(
InstallUtil::GetMachineLevelUserCloudPolicyEnrollmentToken());
}
std::string BrowserDMTokenStorageWin::InitDMToken() {
base::win::RegKey key;
base::string16 dm_token_key_path;
base::string16 dm_token_value_name;
InstallUtil::GetMachineLevelUserCloudPolicyDMTokenRegistryPath(
&dm_token_key_path, &dm_token_value_name);
LONG result = key.Open(HKEY_LOCAL_MACHINE, dm_token_key_path.c_str(),
KEY_QUERY_VALUE | KEY_WOW64_64KEY);
if (result != ERROR_SUCCESS)
return std::string();
// At the time of writing (January 2018), the DM token is about 200 bytes
// long. The initial size of the buffer should be enough to cover most
// realistic future size-increase scenarios, although we still make an effort
// to support somewhat larger token sizes just to be safe.
constexpr size_t kInitialDMTokenSize = 512;
DWORD size = kInitialDMTokenSize;
std::vector<char> raw_value(size);
DWORD dtype = REG_NONE;
result = key.ReadValue(dm_token_value_name.c_str(), raw_value.data(), &size,
&dtype);
if (result == ERROR_MORE_DATA && size <= installer::kMaxDMTokenLength) {
raw_value.resize(size);
result = key.ReadValue(dm_token_value_name.c_str(), raw_value.data(), &size,
&dtype);
}
if (result != ERROR_SUCCESS || dtype != REG_BINARY || size == 0) {
DVLOG(1) << "Failed to get DMToken from Registry.";
return std::string();
}
DCHECK_LE(size, installer::kMaxDMTokenLength);
std::string dm_token;
dm_token.assign(raw_value.data(), size);
return dm_token;
}
bool BrowserDMTokenStorageWin::InitEnrollmentErrorOption() {
return InstallUtil::ShouldCloudManagementBlockOnFailure();
}
void BrowserDMTokenStorageWin::SaveDMToken(const std::string& token) {
base::PostTaskAndReplyWithResult(
com_sta_task_runner_.get(), FROM_HERE,
base::BindOnce(&StoreDMTokenInRegistry, token),
base::BindOnce(&BrowserDMTokenStorage::OnDMTokenStored,
weak_factory_.GetWeakPtr()));
}
// static
BrowserDMTokenStorage* BrowserDMTokenStorage::Get() {
if (storage_for_testing_)
return storage_for_testing_;
static base::NoDestructor<BrowserDMTokenStorageWin> storage;
return storage.get();
}
BrowserDMTokenStorageWin::BrowserDMTokenStorageWin()
: com_sta_task_runner_(
base::CreateCOMSTATaskRunnerWithTraits({base::MayBlock()})),
weak_factory_(this) {}
BrowserDMTokenStorageWin::~BrowserDMTokenStorageWin() {}
} // namespace policy