blob: 48363ce3366eb7f305ce224fe930cd1ea14bd904 [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/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/policy/browser_dm_token_storage.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/machine_level_user_cloud_policy_controller.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/profiles/reporting_util.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/common/extensions/api/safe_browsing_private.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
#include "components/safe_browsing/proto/webprotect.pb.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/event_router.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
namespace extensions {
const base::Feature SafeBrowsingPrivateEventRouter::kRealtimeReportingFeature{
"SafeBrowsingRealtimeReporting", base::FEATURE_ENABLED_BY_DEFAULT};
// Key names used with when building the dictionary to pass to the real-time
// reporting API.
const char SafeBrowsingPrivateEventRouter::kKeyUrl[] = "url";
const char SafeBrowsingPrivateEventRouter::kKeyUserName[] = "userName";
const char SafeBrowsingPrivateEventRouter::kKeyIsPhishingUrl[] =
"isPhishingUrl";
const char SafeBrowsingPrivateEventRouter::kKeyProfileUserName[] =
"profileUserName";
const char SafeBrowsingPrivateEventRouter::kKeyFileName[] = "fileName";
const char SafeBrowsingPrivateEventRouter::kKeyDownloadDigestSha256[] =
"downloadDigestSha256";
const char SafeBrowsingPrivateEventRouter::kKeyReason[] = "reason";
const char SafeBrowsingPrivateEventRouter::kKeyNetErrorCode[] = "netErrorCode";
const char SafeBrowsingPrivateEventRouter::kKeyClickedThrough[] =
"clickedThrough";
const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRules[] =
"triggeredRules";
const char SafeBrowsingPrivateEventRouter::kKeyThreatType[] = "threatType";
const char SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent[] =
"passwordReuseEvent";
const char SafeBrowsingPrivateEventRouter::kKeyPasswordChangedEvent[] =
"passwordChangedEvent";
const char SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent[] =
"dangerousDownloadEvent";
const char SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent[] =
"interstitialEvent";
const char SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent[] =
"sensitiveDataEvent";
const char SafeBrowsingPrivateEventRouter::kKeyLargeUnscannedFileEvent[] =
"largeUnscannedFileEvent";
SafeBrowsingPrivateEventRouter::SafeBrowsingPrivateEventRouter(
content::BrowserContext* context)
: context_(context) {
event_router_ = EventRouter::Get(context_);
InitRealtimeReportingClient();
}
SafeBrowsingPrivateEventRouter::~SafeBrowsingPrivateEventRouter() {}
void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordReuseDetected(
const GURL& url,
const std::string& user_name,
bool is_phishing_url) {
api::safe_browsing_private::PolicySpecifiedPasswordReuse params;
params.url = url.spec();
params.user_name = user_name;
params.is_phishing_url = is_phishing_url;
// |event_router_| can be null in tests.
if (event_router_) {
auto event_value = std::make_unique<base::ListValue>();
event_value->Append(params.ToValue());
auto extension_event = std::make_unique<Event>(
events::
SAFE_BROWSING_PRIVATE_ON_POLICY_SPECIFIED_PASSWORD_REUSE_DETECTED,
api::safe_browsing_private::OnPolicySpecifiedPasswordReuseDetected::
kEventName,
std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
if (client_) {
// Convert |params| to a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, params.url);
event.SetStringKey(kKeyUserName, params.user_name);
event.SetBoolKey(kKeyIsPhishingUrl, params.is_phishing_url);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
ReportRealtimeEvent(kKeyPasswordReuseEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordChanged(
const std::string& user_name) {
// |event_router_| can be null in tests.
if (event_router_) {
auto event_value = std::make_unique<base::ListValue>();
event_value->Append(std::make_unique<base::Value>(user_name));
auto extension_event = std::make_unique<Event>(
events::SAFE_BROWSING_PRIVATE_ON_POLICY_SPECIFIED_PASSWORD_CHANGED,
api::safe_browsing_private::OnPolicySpecifiedPasswordChanged::
kEventName,
std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
if (client_) {
// Convert |params| to a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUserName, user_name);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
ReportRealtimeEvent(kKeyPasswordChangedEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnDangerousDownloadOpened(
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256) {
api::safe_browsing_private::DangerousDownloadInfo params;
params.url = url.spec();
params.file_name = file_name;
params.download_digest_sha256 = download_digest_sha256;
params.user_name = GetProfileUserName();
// |event_router_| can be null in tests.
if (event_router_) {
auto event_value = std::make_unique<base::ListValue>();
event_value->Append(params.ToValue());
auto extension_event = std::make_unique<Event>(
events::SAFE_BROWSING_PRIVATE_ON_DANGEROUS_DOWNLOAD_OPENED,
api::safe_browsing_private::OnDangerousDownloadOpened::kEventName,
std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
if (client_) {
// Convert |params| to a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, params.url);
event.SetStringKey(kKeyFileName, params.file_name);
event.SetStringKey(kKeyDownloadDigestSha256, params.download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, params.user_name);
ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialShown(
const GURL& url,
const std::string& reason,
int net_error_code) {
api::safe_browsing_private::InterstitialInfo params;
params.url = url.spec();
params.reason = reason;
if (net_error_code < 0) {
params.net_error_code =
std::make_unique<std::string>(base::NumberToString(net_error_code));
}
params.user_name = GetProfileUserName();
// |event_router_| can be null in tests.
if (event_router_) {
auto event_value = std::make_unique<base::ListValue>();
event_value->Append(params.ToValue());
auto extension_event = std::make_unique<Event>(
events::SAFE_BROWSING_PRIVATE_ON_SECURITY_INTERSTITIAL_SHOWN,
api::safe_browsing_private::OnSecurityInterstitialShown::kEventName,
std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
if (client_) {
// Convert |params| to a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, params.url);
event.SetStringKey(kKeyReason, params.reason);
event.SetIntKey(kKeyNetErrorCode, net_error_code);
event.SetStringKey(kKeyProfileUserName, params.user_name);
event.SetBoolKey(kKeyClickedThrough, false);
ReportRealtimeEvent(kKeyInterstitialEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialProceeded(
const GURL& url,
const std::string& reason,
int net_error_code) {
api::safe_browsing_private::InterstitialInfo params;
params.url = url.spec();
params.reason = reason;
if (net_error_code < 0) {
params.net_error_code =
std::make_unique<std::string>(base::NumberToString(net_error_code));
}
params.user_name = GetProfileUserName();
// |event_router_| can be null in tests.
if (event_router_) {
auto event_value = std::make_unique<base::ListValue>();
event_value->Append(params.ToValue());
auto extension_event = std::make_unique<Event>(
events::SAFE_BROWSING_PRIVATE_ON_SECURITY_INTERSTITIAL_PROCEEDED,
api::safe_browsing_private::OnSecurityInterstitialProceeded::kEventName,
std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
if (client_) {
// Convert |params| to a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, params.url);
event.SetStringKey(kKeyReason, params.reason);
event.SetIntKey(kKeyNetErrorCode, net_error_code);
event.SetStringKey(kKeyProfileUserName, params.user_name);
event.SetBoolKey(kKeyClickedThrough, true);
ReportRealtimeEvent(kKeyInterstitialEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnDangerousDeepScanningResult(
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256,
const std::string& threat_type) {
if (client_) {
// Create a real-time event dictionary from the arguments and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, url.spec());
event.SetStringKey(kKeyFileName, file_name);
event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
event.SetStringKey(kKeyThreatType, threat_type);
ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent(
const safe_browsing::DlpDeepScanningVerdict& verdict,
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256) {
if (client_) {
// Create a real-time event dictionary from the arguments and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, url.spec());
event.SetStringKey(kKeyFileName, file_name);
event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
base::ListValue triggered_rules;
for (auto rule : verdict.triggered_rules()) {
triggered_rules.AppendString(rule.rule_name());
}
event.SetKey(kKeyTriggeredRules, std::move(triggered_rules));
ReportRealtimeEvent(kKeySensitiveDataEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnLargeUnscannedFileEvent(
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256) {
if (client_) {
// Create a real-time event dictionary from the arguments and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, url.spec());
event.SetStringKey(kKeyFileName, file_name);
event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
ReportRealtimeEvent(kKeyLargeUnscannedFileEvent, std::move(event));
}
}
void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarning(
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256,
const std::string& threat_type) {
if (!client_)
return;
// Create a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, url.spec());
event.SetStringKey(kKeyFileName, file_name);
event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
event.SetStringKey(kKeyThreatType, threat_type);
event.SetBoolKey(kKeyClickedThrough, false);
ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
}
void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarningBypassed(
const GURL& url,
const std::string& file_name,
const std::string& download_digest_sha256,
const std::string& threat_type) {
if (!client_)
return;
// Create a real-time event dictionary and report it.
base::Value event(base::Value::Type::DICTIONARY);
event.SetStringKey(kKeyUrl, url.spec());
event.SetStringKey(kKeyFileName, file_name);
event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
event.SetStringKey(kKeyThreatType, threat_type);
event.SetBoolKey(kKeyClickedThrough, true);
ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
}
void SafeBrowsingPrivateEventRouter::SetCloudPolicyClientForTesting(
std::unique_ptr<policy::CloudPolicyClient> client) {
DCHECK_EQ(nullptr, client_.get());
client_ = std::move(client);
}
void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClient() {
#if !defined(OS_CHROMEOS)
// This method is not compiled on Chrome OS because
// MachineLevelUserCloudPolicyController does not exist. Also,
// policy::BrowserDMTokenStorage::Get()->RetrieveDMToken() doesn't return a
// valid token either. Once these are fixed the #if !defined can be removed.
if (!policy::MachineLevelUserCloudPolicyController::
IsMachineLevelUserCloudPolicyEnabled()) {
return;
}
if (!base::FeatureList::IsEnabled(kRealtimeReportingFeature))
return;
// |identity_manager_| may be null in tests. If there is no identity
// manager don't enable the real-time reporting API since the router won't
// be able to fill in all the info needed for the reports.
identity_manager_ = IdentityManagerFactory::GetForProfile(
Profile::FromBrowserContext(context_));
if (!identity_manager_)
return;
// |device_management_service| may be null in tests. If there is no device
// management service don't enable the real-time reporting API since the
// router won't be able to create the reporting server client below.
policy::DeviceManagementService* device_management_service =
g_browser_process->browser_policy_connector()
->device_management_service();
if (!device_management_service)
return;
// Make sure we have a DM token to proceed. During the lifetime of a running
// chrome browser, this can only change from empty to non-empty. There are
// no cases where chrome starts with a dm token and then it goes away.
// When chrome starts without a dm token and determines that one is needed,
// browser startup is blocked until it is retrieved or an error occurs. In
// the latter case, chrome won't try to retrieve it again until the next
// restart.
//
// Therefore, it is OK to retrieve the dm token once here on initialization
// of the router to determine if real-time reporting can be enabled or not.
std::string dm_token =
policy::BrowserDMTokenStorage::Get()->RetrieveDMToken();
std::string client_id =
policy::BrowserDMTokenStorage::Get()->RetrieveClientId();
if (dm_token.empty())
return;
// Make sure DeviceManagementService has been initialized.
device_management_service->ScheduleInitialization(0);
client_ = std::make_unique<policy::CloudPolicyClient>(
/*machine_id=*/std::string(), /*machine_model=*/std::string(),
/*brand_code=*/std::string(), /*ethernet_mac_address=*/std::string(),
/*dock_mac_address=*/std::string(), /*manufacture_date=*/std::string(),
device_management_service, g_browser_process->shared_url_loader_factory(),
nullptr, policy::CloudPolicyClient::DeviceDMTokenCallback());
if (!client_->is_registered()) {
client_->SetupRegistration(
dm_token, client_id,
/*user_affiliation_ids=*/std::vector<std::string>());
}
#endif
}
void SafeBrowsingPrivateEventRouter::ReportRealtimeEvent(const char* name,
base::Value event) {
// Format the current time (UTC) in RFC3339 format.
base::Time::Exploded now_exploded;
base::Time::Now().UTCExplode(&now_exploded);
std::string now_str = base::StringPrintf(
"%d-%02d-%02dT%02d:%02d:%02d.%03dZ", now_exploded.year,
now_exploded.month, now_exploded.day_of_month, now_exploded.hour,
now_exploded.minute, now_exploded.second, now_exploded.millisecond);
base::Value wrapper(base::Value::Type::DICTIONARY);
wrapper.SetStringKey("time", now_str);
wrapper.SetKey(name, std::move(event));
base::Value event_list(base::Value::Type::LIST);
event_list.Append(std::move(wrapper));
client_->UploadRealtimeReport(
policy::RealtimeReportingJobConfiguration::BuildReport(
std::move(event_list),
reporting::GetContext(Profile::FromBrowserContext(context_))),
base::DoNothing());
}
std::string SafeBrowsingPrivateEventRouter::GetProfileUserName() {
// |identity_manager_| may be null is some tests.
return identity_manager_ && identity_manager_->HasPrimaryAccount()
? identity_manager_->GetPrimaryAccountInfo().email
: std::string();
}
} // namespace extensions