blob: 13a6e22d6f96dafa73f79c9051d091570a1d27b2 [file] [log] [blame]
// Copyright 2021 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/signals/context_info_fetcher.h"
#include <memory>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/string_split.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/connectors/connectors_service.h"
#include "chrome/browser/enterprise/signals/signals_utils.h"
#include "chrome/browser/enterprise/util/affiliation.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/component_updater/pref_names.h"
#include "components/policy/content/policy_blocklist_service.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/site_isolation_policy.h"
#include "device_management_backend.pb.h"
#if defined(OS_POSIX)
#include "net/dns/public/resolv_reader.h"
#include "net/dns/public/scoped_res_state.h"
#endif
#if defined(OS_MAC)
#include <CoreFoundation/CoreFoundation.h>
#endif
#if defined(OS_WIN)
#include <netfw.h>
#include <windows.h>
#include <wrl/client.h>
#include "net/dns/public/win_dns_system_settings.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chromeos/dbus/constants/dbus_switches.h"
#endif
namespace enterprise_signals {
namespace {
#if defined(OS_LINUX)
const char** GetUfwConfigPath() {
static const char* path = "/etc/ufw/ufw.conf";
return &path;
}
SettingValue GetUfwStatus() {
base::FilePath path(*GetUfwConfigPath());
std::string file_content;
base::StringPairs values;
if (!base::PathExists(path) || !base::PathIsReadable(path) ||
!base::ReadFileToString(path, &file_content)) {
return SettingValue::UNKNOWN;
}
base::SplitStringIntoKeyValuePairs(file_content, '=', '\n', &values);
auto is_ufw_enabled = std::find_if(values.begin(), values.end(), [](auto v) {
return v.first == "ENABLED";
});
if (is_ufw_enabled == values.end())
return SettingValue::UNKNOWN;
if (is_ufw_enabled->second == "yes")
return SettingValue::ENABLED;
else if (is_ufw_enabled->second == "no")
return SettingValue::DISABLED;
else
return SettingValue::UNKNOWN;
}
#endif // defined(OS_LINUX)
#if defined(OS_WIN)
SettingValue GetWinOSFirewall() {
Microsoft::WRL::ComPtr<INetFwPolicy2> firewall_policy;
HRESULT hr = CoCreateInstance(CLSID_NetFwPolicy2, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&firewall_policy));
if (FAILED(hr)) {
DLOG(ERROR) << logging::SystemErrorCodeToString(hr);
return SettingValue::UNKNOWN;
}
long profile_types = 0;
hr = firewall_policy->get_CurrentProfileTypes(&profile_types);
if (FAILED(hr))
return SettingValue::UNKNOWN;
// The most restrictive active profile takes precedence.
constexpr NET_FW_PROFILE_TYPE2 kProfileTypes[] = {
NET_FW_PROFILE2_PUBLIC, NET_FW_PROFILE2_PRIVATE, NET_FW_PROFILE2_DOMAIN};
for (size_t i = 0; i < base::size(kProfileTypes); ++i) {
if ((profile_types & kProfileTypes[i]) != 0) {
VARIANT_BOOL enabled = VARIANT_TRUE;
hr = firewall_policy->get_FirewallEnabled(kProfileTypes[i], &enabled);
if (FAILED(hr))
return SettingValue::UNKNOWN;
if (enabled == VARIANT_TRUE)
return SettingValue::ENABLED;
else if (enabled == VARIANT_FALSE)
return SettingValue::DISABLED;
else
return SettingValue::UNKNOWN;
}
}
return SettingValue::UNKNOWN;
}
#endif
#if defined(OS_MAC)
SettingValue GetMacOSFirewall() {
// There is no official Apple documentation on how to obtain the enabled
// status of the firewall (System Preferences> Security & Privacy> Firewall).
// Reading globalstate from com.apple.alf is the closest way to get such an
// API in Chrome without delegating to potentially unstable commands.
Boolean key_exists_with_valid_format = false;
CFIndex globalstate = CFPreferencesGetAppIntegerValue(
CFSTR("globalstate"), CFSTR("com.apple.alf"),
&key_exists_with_valid_format);
if (!key_exists_with_valid_format)
return SettingValue::UNKNOWN;
switch (globalstate) {
case 0:
return SettingValue::DISABLED;
case 1:
return SettingValue::ENABLED;
default:
return SettingValue::UNKNOWN;
}
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
SettingValue GetChromeosFirewall() {
// The firewall is always enabled and can only be disabled in dev mode on
// ChromeOS. If the device isn't in dev mode, the firewall is guaranteed to be
// enabled whereas if it's in dev mode, the firewall could be enabled or not.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
return command_line->HasSwitch(chromeos::switches::kSystemDevMode)
? SettingValue::UNKNOWN
: SettingValue::ENABLED;
}
#endif
} // namespace
ContextInfo::ContextInfo() = default;
ContextInfo::ContextInfo(ContextInfo&&) = default;
ContextInfo::~ContextInfo() = default;
ContextInfoFetcher::ContextInfoFetcher(
content::BrowserContext* browser_context,
enterprise_connectors::ConnectorsService* connectors_service)
: browser_context_(browser_context),
connectors_service_(connectors_service) {
DCHECK(connectors_service_);
}
ContextInfoFetcher::~ContextInfoFetcher() = default;
// static
std::unique_ptr<ContextInfoFetcher> ContextInfoFetcher::CreateInstance(
content::BrowserContext* browser_context,
enterprise_connectors::ConnectorsService* connectors_service) {
// TODO(domfc): Add platform overrides of the class once they are needed for
// an attribute.
return std::make_unique<ContextInfoFetcher>(browser_context,
connectors_service);
}
ContextInfo ContextInfoFetcher::FetchAsyncSignals(ContextInfo info) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
// Add other async signals here
info.system_dns_servers = GetDnsServers();
info.os_firewall = GetOSFirewall();
return info;
}
void ContextInfoFetcher::Fetch(ContextInfoCallback callback) {
ContextInfo info;
info.browser_affiliation_ids = GetBrowserAffiliationIDs();
info.profile_affiliation_ids = GetProfileAffiliationIDs();
info.on_file_attached_providers =
GetAnalysisConnectorProviders(enterprise_connectors::FILE_ATTACHED);
info.on_file_downloaded_providers =
GetAnalysisConnectorProviders(enterprise_connectors::FILE_DOWNLOADED);
info.on_bulk_data_entry_providers =
GetAnalysisConnectorProviders(enterprise_connectors::BULK_DATA_ENTRY);
info.realtime_url_check_mode = GetRealtimeUrlCheckMode();
info.on_security_event_providers = GetOnSecurityEventProviders();
info.browser_version = version_info::GetVersionNumber();
info.site_isolation_enabled =
content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites();
info.built_in_dns_client_enabled =
utils::GetBuiltInDnsClientEnabled(g_browser_process->local_state());
info.chrome_cleanup_enabled =
utils::GetChromeCleanupEnabled(g_browser_process->local_state());
info.chrome_remote_desktop_app_blocked =
utils::GetChromeRemoteDesktopAppBlocked(
PolicyBlocklistFactory::GetForBrowserContext(browser_context_));
info.third_party_blocking_enabled =
utils::GetThirdPartyBlockingEnabled(g_browser_process->local_state());
Profile* profile = Profile::FromBrowserContext(browser_context_);
info.safe_browsing_protection_level =
utils::GetSafeBrowsingProtectionLevel(profile->GetPrefs());
info.password_protection_warning_trigger =
utils::GetPasswordProtectionWarningTrigger(profile->GetPrefs());
#if defined(OS_WIN)
base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()})
.get()
->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ContextInfoFetcher::FetchAsyncSignals,
base::Unretained(this), std::move(info)),
std::move(callback));
#else
base::ThreadPool::CreateTaskRunner({base::MayBlock()})
.get()
->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ContextInfoFetcher::FetchAsyncSignals,
base::Unretained(this), std::move(info)),
std::move(callback));
#endif
}
std::vector<std::string> ContextInfoFetcher::GetBrowserAffiliationIDs() {
auto ids =
g_browser_process->browser_policy_connector()->device_affiliation_ids();
return {ids.begin(), ids.end()};
}
std::vector<std::string> ContextInfoFetcher::GetProfileAffiliationIDs() {
auto ids = Profile::FromBrowserContext(browser_context_)
->GetProfilePolicyConnector()
->user_affiliation_ids();
return {ids.begin(), ids.end()};
}
std::vector<std::string> ContextInfoFetcher::GetAnalysisConnectorProviders(
enterprise_connectors::AnalysisConnector connector) {
return connectors_service_->GetAnalysisServiceProviderNames(connector);
}
safe_browsing::EnterpriseRealTimeUrlCheckMode
ContextInfoFetcher::GetRealtimeUrlCheckMode() {
return connectors_service_->GetAppliedRealTimeUrlCheck();
}
std::vector<std::string> ContextInfoFetcher::GetOnSecurityEventProviders() {
return connectors_service_->GetReportingServiceProviderNames(
enterprise_connectors::ReportingConnector::SECURITY_EVENT);
}
SettingValue ContextInfoFetcher::GetOSFirewall() {
#if defined(OS_LINUX)
return GetUfwStatus();
#elif defined(OS_WIN)
return GetWinOSFirewall();
#elif defined(OS_MAC)
return GetMacOSFirewall();
#elif BUILDFLAG(IS_CHROMEOS_ASH)
return GetChromeosFirewall();
#else
return SettingValue::UNKNOWN;
#endif
}
#if defined(OS_LINUX)
ScopedUfwConfigPathForTesting::ScopedUfwConfigPathForTesting(const char* path)
: initial_path_(*GetUfwConfigPath()) {
*GetUfwConfigPath() = path;
}
ScopedUfwConfigPathForTesting::~ScopedUfwConfigPathForTesting() {
*GetUfwConfigPath() = initial_path_;
}
#endif // defined(OS_LINUX)
std::vector<std::string> ContextInfoFetcher::GetDnsServers() {
std::vector<std::string> dns_addresses;
#if defined(OS_POSIX)
std::unique_ptr<net::ScopedResState> res = net::ResolvReader().GetResState();
if (res) {
absl::optional<std::vector<net::IPEndPoint>> nameservers =
net::GetNameservers(res->state());
if (nameservers) {
// If any name server is 0.0.0.0, assume the configuration is invalid.
for (const net::IPEndPoint& nameserver : nameservers.value()) {
if (nameserver.address().IsZero())
return std::vector<std::string>();
else
dns_addresses.push_back(nameserver.ToString());
}
}
}
#elif defined(OS_WIN)
absl::optional<std::vector<net::IPEndPoint>> nameservers;
absl::optional<net::WinDnsSystemSettings> settings =
net::ReadWinSystemDnsSettings();
if (settings.has_value()) {
nameservers = settings->GetAllNameservers();
}
if (nameservers.has_value()) {
for (const net::IPEndPoint& nameserver : nameservers.value()) {
dns_addresses.push_back(nameserver.ToString());
}
}
#endif
return dns_addresses;
}
} // namespace enterprise_signals