blob: d6f5e44b13ac22a0ce2750804010eb8a700138f0 [file] [log] [blame]
// Copyright 2020 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/enterprise/connectors/connectors_service.h"
#include <memory>
#include "base/memory/singleton.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/enterprise/connectors/connectors_manager.h"
#include "chrome/browser/enterprise/connectors/service_provider_config.h"
#include "chrome/browser/enterprise/util/affiliation.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/dm_token_utils.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/signin/identity_manager_factory.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
#include "components/policy/core/common/cloud/dm_token.h"
#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
#include "components/policy/core/common/policy_types.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/signin/public/identity_manager/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/user_prefs/user_prefs.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
#include "device_management_backend.pb.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/policy/core/user_cloud_policy_manager_chromeos.h"
#endif
namespace enterprise_connectors {
namespace {
const enterprise_management::PolicyData* GetProfilePolicyData(
Profile* profile) {
DCHECK(profile);
auto* manager =
#if BUILDFLAG(IS_CHROMEOS_ASH)
profile->GetUserCloudPolicyManagerChromeOS();
#else
profile->GetUserCloudPolicyManager();
#endif
if (manager && manager->core()->store() &&
manager->core()->store()->has_policy()) {
return manager->core()->store()->policy();
}
return nullptr;
}
void PopulateBrowserMetadata(bool include_device_info,
ClientMetadata::Browser* browser_proto) {
base::FilePath browser_id;
if (base::PathService::Get(base::DIR_EXE, &browser_id))
browser_proto->set_browser_id(browser_id.AsUTF8Unsafe());
browser_proto->set_user_agent(embedder_support::GetUserAgent());
browser_proto->set_chrome_version(version_info::GetVersionNumber());
if (include_device_info)
browser_proto->set_machine_user(policy::GetOSUsername());
}
void PopulateDeviceMetadata(const ReportingSettings& reporting_settings,
Profile* profile,
ClientMetadata::Device* device_proto) {
if (!reporting_settings.per_profile)
device_proto->set_dm_token(reporting_settings.dm_token);
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::string client_id;
auto* manager = profile->GetUserCloudPolicyManagerChromeOS();
if (manager && manager->core() && manager->core()->client())
client_id = manager->core()->client()->client_id();
#else
std::string client_id =
policy::BrowserDMTokenStorage::Get()->RetrieveClientId();
#endif
device_proto->set_client_id(client_id);
device_proto->set_os_version(policy::GetOSVersion());
device_proto->set_os_platform(policy::GetOSPlatform());
device_proto->set_name(policy::GetDeviceName());
}
void PopulateProfileMetadata(const ReportingSettings& reporting_settings,
Profile* profile,
ClientMetadata::Profile* profile_proto) {
if (reporting_settings.per_profile)
profile_proto->set_dm_token(reporting_settings.dm_token);
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
if (identity_manager) {
profile_proto->set_gaia_email(
identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
.email);
}
profile_proto->set_profile_path(profile->GetPath().AsUTF8Unsafe());
ProfileAttributesEntry* entry =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile->GetPath());
if (entry) {
profile_proto->set_profile_name(base::UTF16ToUTF8(entry->GetName()));
}
const enterprise_management::PolicyData* profile_policy =
GetProfilePolicyData(profile);
if (profile_policy) {
if (profile_policy->has_device_id())
profile_proto->set_client_id(profile_policy->device_id());
}
}
} // namespace
const base::Feature kEnterpriseConnectorsEnabled{
"EnterpriseConnectorsEnabled", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kPerProfileConnectorsEnabled{
"PerProfileConnectorsEnabled", base::FEATURE_ENABLED_BY_DEFAULT};
const char kServiceProviderConfig[] = R"({
"version": "1",
"service_providers" : [
{
"name": "google",
"display_name": "Google Cloud",
"version": {
"1": {
"analysis": {
"url": "https://safebrowsing.google.com/safebrowsing/uploads/scan",
"supported_tags": [
{
"name": "malware",
"display_name": "Threat protection",
"mime_types": [
"application/vnd.microsoft.portable-executable",
"application/vnd.rar",
"application/x-msdos-program",
"application/zip"
],
"max_file_size": 52428800
},
{
"name": "dlp",
"display_name": "Sensitive data protection",
"mime_types": [
"application/gzip",
"application/msword",
"application/pdf",
"application/postscript",
"application/rtf",
"application/vnd.google-apps.document.internal",
"application/vnd.google-apps.spreadsheet.internal",
"application/vnd.ms-cab-compressed",
"application/vnd.ms-excel",
"application/vnd.ms-powerpoint",
"application/vnd.ms-xpsdocument",
"application/vnd.oasis.opendocument.text",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
"application/vnd.ms-excel.sheet.macroenabled.12",
"application/vnd.ms-excel.template.macroenabled.12",
"application/vnd.ms-word.document.macroenabled.12",
"application/vnd.ms-word.template.macroenabled.12",
"application/vnd.rar",
"application/vnd.wordperfect",
"application/x-7z-compressed",
"application/x-bzip",
"application/x-bzip2",
"application/x-tar",
"application/zip",
"text/csv",
"text/plain"
],
"max_file_size": 52428800
}
]
},
"reporting": {
"url": "https://chromereporting-pa.googleapis.com/v1/events"
}
}
}
},
{
"name": "box",
"display_name": "Box",
"version": {
"1": {
"file_system": {
"home": "https://box.com",
"authorization_endpoint": "https://account.box.com/api/oauth2/authorize",
"token_endpoint": "https://api.box.com/oauth2/token",
"max_direct_size": 20971520,
"scopes": [],
"disable": [ "box.com" ]
}
}
}
}
]
})";
ServiceProviderConfig* GetServiceProviderConfig() {
static base::NoDestructor<ServiceProviderConfig> config(
kServiceProviderConfig);
return config.get();
}
// --------------------------------
// ConnectorsService implementation
// --------------------------------
ConnectorsService::ConnectorsService(content::BrowserContext* context,
std::unique_ptr<ConnectorsManager> manager)
: context_(context), connectors_manager_(std::move(manager)) {
DCHECK(context_);
DCHECK(connectors_manager_);
}
ConnectorsService::~ConnectorsService() = default;
absl::optional<ReportingSettings> ConnectorsService::GetReportingSettings(
ReportingConnector connector) {
if (!ConnectorsEnabled())
return absl::nullopt;
absl::optional<ReportingSettings> settings =
connectors_manager_->GetReportingSettings(connector);
if (!settings.has_value())
return absl::nullopt;
absl::optional<DmToken> dm_token = GetDmToken(ConnectorScopePref(connector));
if (!dm_token.has_value())
return absl::nullopt;
settings.value().dm_token = dm_token.value().value;
settings.value().per_profile =
dm_token.value().scope == policy::POLICY_SCOPE_USER;
return settings;
}
absl::optional<AnalysisSettings> ConnectorsService::GetAnalysisSettings(
const GURL& url,
AnalysisConnector connector) {
if (!ConnectorsEnabled())
return absl::nullopt;
absl::optional<AnalysisSettings> settings =
connectors_manager_->GetAnalysisSettings(url, connector);
if (!settings.has_value())
return absl::nullopt;
absl::optional<DmToken> dm_token = GetDmToken(ConnectorScopePref(connector));
if (!dm_token.has_value())
return absl::nullopt;
settings.value().dm_token = dm_token.value().value;
settings.value().per_profile =
dm_token.value().scope == policy::POLICY_SCOPE_USER;
settings.value().client_metadata = BuildClientMetadata();
return settings;
}
absl::optional<FileSystemSettings> ConnectorsService::GetFileSystemSettings(
const GURL& url,
FileSystemConnector connector) {
if (!ConnectorsEnabled())
return absl::nullopt;
absl::optional<FileSystemSettings> settings =
connectors_manager_->GetFileSystemSettings(url, connector);
if (!settings.has_value())
return absl::nullopt;
return settings;
}
bool ConnectorsService::IsConnectorEnabled(AnalysisConnector connector) const {
if (!ConnectorsEnabled())
return false;
return connectors_manager_->IsConnectorEnabled(connector);
}
bool ConnectorsService::IsConnectorEnabled(ReportingConnector connector) const {
if (!ConnectorsEnabled())
return false;
return connectors_manager_->IsConnectorEnabled(connector);
}
bool ConnectorsService::IsConnectorEnabled(
FileSystemConnector connector) const {
if (!ConnectorsEnabled())
return false;
return connectors_manager_->IsConnectorEnabled(connector);
}
std::vector<std::string> ConnectorsService::GetReportingServiceProviderNames(
ReportingConnector connector) {
if (!ConnectorsEnabled())
return {};
if (!GetDmToken(ConnectorScopePref(connector)).has_value())
return {};
return connectors_manager_->GetReportingServiceProviderNames(connector);
}
bool ConnectorsService::DelayUntilVerdict(AnalysisConnector connector) {
if (!ConnectorsEnabled())
return false;
return connectors_manager_->DelayUntilVerdict(connector);
}
absl::optional<std::u16string> ConnectorsService::GetCustomMessage(
AnalysisConnector connector,
const std::string& tag) {
if (!ConnectorsEnabled())
return absl::nullopt;
return connectors_manager_->GetCustomMessage(connector, tag);
}
absl::optional<GURL> ConnectorsService::GetLearnMoreUrl(
AnalysisConnector connector,
const std::string& tag) {
if (!ConnectorsEnabled())
return absl::nullopt;
return connectors_manager_->GetLearnMoreUrl(connector, tag);
}
bool ConnectorsService::HasCustomInfoToDisplay(AnalysisConnector connector,
const std::string& tag) {
return GetCustomMessage(connector, tag) || GetLearnMoreUrl(connector, tag);
}
std::vector<std::string> ConnectorsService::GetAnalysisServiceProviderNames(
AnalysisConnector connector) {
if (!ConnectorsEnabled())
return {};
if (!GetDmToken(ConnectorScopePref(connector)).has_value())
return {};
return connectors_manager_->GetAnalysisServiceProviderNames(connector);
}
absl::optional<std::string> ConnectorsService::GetDMTokenForRealTimeUrlCheck()
const {
if (!ConnectorsEnabled())
return absl::nullopt;
if (Profile::FromBrowserContext(context_)->GetPrefs()->GetInteger(
prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckMode) ==
safe_browsing::REAL_TIME_CHECK_DISABLED) {
return absl::nullopt;
}
absl::optional<DmToken> dm_token =
GetDmToken(prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckScope);
if (dm_token.has_value())
return dm_token.value().value;
return absl::nullopt;
}
safe_browsing::EnterpriseRealTimeUrlCheckMode
ConnectorsService::GetAppliedRealTimeUrlCheck() const {
if (!ConnectorsEnabled() ||
!GetDmToken(prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckScope)
.has_value()) {
return safe_browsing::REAL_TIME_CHECK_DISABLED;
}
return static_cast<safe_browsing::EnterpriseRealTimeUrlCheckMode>(
Profile::FromBrowserContext(context_)->GetPrefs()->GetInteger(
prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckMode));
}
ConnectorsManager* ConnectorsService::ConnectorsManagerForTesting() {
return connectors_manager_.get();
}
ConnectorsService::DmToken::DmToken(const std::string& value,
policy::PolicyScope scope)
: value(value), scope(scope) {}
ConnectorsService::DmToken::DmToken(DmToken&&) = default;
ConnectorsService::DmToken& ConnectorsService::DmToken::operator=(DmToken&&) =
default;
ConnectorsService::DmToken::~DmToken() = default;
absl::optional<ConnectorsService::DmToken> ConnectorsService::GetDmToken(
const char* scope_pref) const {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On CrOS, the device must be affiliated to use the DM token for
// scanning/reporting so we always use the browser DM token.
return GetBrowserDmToken();
#else
return GetPolicyScope(scope_pref) == policy::POLICY_SCOPE_USER
? GetProfileDmToken()
: GetBrowserDmToken();
#endif
}
absl::optional<ConnectorsService::DmToken>
ConnectorsService::GetBrowserDmToken() const {
policy::DMToken dm_token =
policy::GetDMToken(Profile::FromBrowserContext(context_));
if (!dm_token.is_valid())
return absl::nullopt;
return DmToken(dm_token.value(), policy::POLICY_SCOPE_MACHINE);
}
#if !BUILDFLAG(IS_CHROMEOS_ASH)
absl::optional<ConnectorsService::DmToken>
ConnectorsService::GetProfileDmToken() const {
if (!base::FeatureList::IsEnabled(kPerProfileConnectorsEnabled))
return absl::nullopt;
if (!CanUseProfileDmToken())
return absl::nullopt;
policy::UserCloudPolicyManager* policy_manager =
Profile::FromBrowserContext(context_)->GetUserCloudPolicyManager();
if (!policy_manager || !policy_manager->IsClientRegistered())
return absl::nullopt;
return DmToken(policy_manager->core()->client()->dm_token(),
policy::POLICY_SCOPE_USER);
}
bool ConnectorsService::CanUseProfileDmToken() const {
// If the browser isn't managed by CBCM, then the profile DM token can be
// used.
if (!policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid())
return true;
return chrome::enterprise_util::IsProfileAffiliated(
Profile::FromBrowserContext(context_));
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
policy::PolicyScope ConnectorsService::GetPolicyScope(
const char* scope_pref) const {
return static_cast<policy::PolicyScope>(
Profile::FromBrowserContext(context_)->GetPrefs()->GetInteger(
scope_pref));
}
bool ConnectorsService::ConnectorsEnabled() const {
if (!base::FeatureList::IsEnabled(kEnterpriseConnectorsEnabled))
return false;
return !Profile::FromBrowserContext(context_)->IsOffTheRecord();
}
std::unique_ptr<ClientMetadata> ConnectorsService::BuildClientMetadata() {
// Check the reporting policy value to check if the analysis should include
// browser/device/profile information.
auto reporting_settings =
GetReportingSettings(ReportingConnector::SECURITY_EVENT);
if (!reporting_settings.has_value())
return nullptr;
bool include_device_info = !reporting_settings.value().per_profile;
Profile* profile = Profile::FromBrowserContext(context_);
auto metadata = std::make_unique<ClientMetadata>();
PopulateBrowserMetadata(include_device_info, metadata->mutable_browser());
if (include_device_info) {
PopulateDeviceMetadata(reporting_settings.value(), profile,
metadata->mutable_device());
}
PopulateProfileMetadata(reporting_settings.value(), profile,
metadata->mutable_profile());
return metadata;
}
// ---------------------------------------
// ConnectorsServiceFactory implementation
// ---------------------------------------
// static
ConnectorsServiceFactory* ConnectorsServiceFactory::GetInstance() {
return base::Singleton<ConnectorsServiceFactory>::get();
}
ConnectorsService* ConnectorsServiceFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<ConnectorsService*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}
ConnectorsServiceFactory::ConnectorsServiceFactory()
: BrowserContextKeyedServiceFactory(
"ConnectorsService",
BrowserContextDependencyManager::GetInstance()) {}
ConnectorsServiceFactory::~ConnectorsServiceFactory() = default;
KeyedService* ConnectorsServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new ConnectorsService(
context,
std::make_unique<ConnectorsManager>(
user_prefs::UserPrefs::Get(context), GetServiceProviderConfig(),
base::FeatureList::IsEnabled(kEnterpriseConnectorsEnabled)));
}
content::BrowserContext* ConnectorsServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return context;
}
} // namespace enterprise_connectors