blob: e77fd4ac6fb13c9d9a6e6bdb4cfe8cbfa02fd7eb [file] [log] [blame]
// Copyright (c) 2012 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/permissions_updater.h"
#include <utility>
#include "base/memory/ref_counted.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/permissions.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/notification_types.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
using content::RenderProcessHost;
using extensions::permissions_api_helpers::PackPermissionSet;
namespace extensions {
namespace permissions = api::permissions;
namespace {
// Returns a PermissionSet that has the active permissions of the extension,
// bounded to its current manifest.
std::unique_ptr<const PermissionSet> GetBoundedActivePermissions(
const Extension* extension,
const PermissionSet* active_permissions) {
// If the extension has used the optional permissions API, it will have a
// custom set of active permissions defined in the extension prefs. Here,
// we update the extension's active permissions based on the prefs.
if (!active_permissions)
return extension->permissions_data()->active_permissions().Clone();
const PermissionSet& required_permissions =
PermissionsParser::GetRequiredPermissions(extension);
// We restrict the active permissions to be within the bounds defined in the
// extension's manifest.
// a) active permissions must be a subset of optional + default permissions
// b) active permissions must contains all default permissions
std::unique_ptr<const PermissionSet> total_permissions =
PermissionSet::CreateUnion(
required_permissions,
PermissionsParser::GetOptionalPermissions(extension));
// Make sure the active permissions contain no more than optional + default.
std::unique_ptr<const PermissionSet> adjusted_active =
PermissionSet::CreateIntersection(*total_permissions,
*active_permissions);
// Make sure the active permissions contain the default permissions.
adjusted_active =
PermissionSet::CreateUnion(required_permissions, *adjusted_active);
return adjusted_active;
}
PermissionsUpdater::Delegate* g_delegate = nullptr;
} // namespace
PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
: browser_context_(browser_context), init_flag_(INIT_FLAG_NONE) {
}
PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context,
InitFlag init_flag)
: browser_context_(browser_context), init_flag_(init_flag) {
}
PermissionsUpdater::~PermissionsUpdater() {}
// static
void PermissionsUpdater::SetPlatformDelegate(Delegate* delegate) {
// Make sure we're setting it only once (allow setting to nullptr, but then
// take special care of actually freeing it).
CHECK(!g_delegate || !delegate);
g_delegate = delegate;
}
void PermissionsUpdater::AddPermissions(const Extension* extension,
const PermissionSet& permissions) {
const PermissionSet& active =
extension->permissions_data()->active_permissions();
std::unique_ptr<const PermissionSet> total =
PermissionSet::CreateUnion(active, permissions);
std::unique_ptr<const PermissionSet> added =
PermissionSet::CreateDifference(*total, active);
std::unique_ptr<const PermissionSet> new_withheld =
PermissionSet::CreateDifference(
extension->permissions_data()->withheld_permissions(), permissions);
SetPermissions(extension, std::move(total), std::move(new_withheld));
// Update the granted permissions so we don't auto-disable the extension.
GrantActivePermissions(extension);
NotifyPermissionsUpdated(ADDED, extension, *added);
}
void PermissionsUpdater::RemovePermissions(const Extension* extension,
const PermissionSet& to_remove,
RemoveType remove_type) {
// We should only be revoking revokable permissions.
CHECK(GetRevokablePermissions(extension)->Contains(to_remove));
const PermissionSet& active =
extension->permissions_data()->active_permissions();
std::unique_ptr<const PermissionSet> remaining =
PermissionSet::CreateDifference(active, to_remove);
// Move any granted permissions that were in the withheld set back to the
// withheld set so they can be added back later.
// Any revoked permission that isn't from the optional permissions can only
// be a withheld permission.
std::unique_ptr<const PermissionSet> removed_withheld =
PermissionSet::CreateDifference(
to_remove, PermissionsParser::GetOptionalPermissions(extension));
std::unique_ptr<const PermissionSet> withheld = PermissionSet::CreateUnion(
*removed_withheld, extension->permissions_data()->withheld_permissions());
SetPermissions(extension, std::move(remaining), std::move(withheld));
// We might not want to revoke the granted permissions because the extension,
// not the user, removed the permissions. This allows the extension to add
// them again without prompting the user.
if (remove_type == REMOVE_HARD) {
ExtensionPrefs::Get(browser_context_)
->RemoveGrantedPermissions(extension->id(), to_remove);
}
NotifyPermissionsUpdated(REMOVED, extension, to_remove);
}
void PermissionsUpdater::SetPolicyHostRestrictions(
const Extension* extension,
const URLPatternSet& runtime_blocked_hosts,
const URLPatternSet& runtime_allowed_hosts) {
extension->permissions_data()->SetPolicyHostRestrictions(
runtime_blocked_hosts, runtime_allowed_hosts);
// Send notification to the currently running renderers of the runtime block
// hosts settings.
const PermissionSet perms;
NotifyPermissionsUpdated(POLICY, extension, perms);
}
void PermissionsUpdater::SetUsesDefaultHostRestrictions(
const Extension* extension) {
extension->permissions_data()->SetUsesDefaultHostRestrictions();
const PermissionSet perms;
NotifyPermissionsUpdated(POLICY, extension, perms);
}
void PermissionsUpdater::SetDefaultPolicyHostRestrictions(
const URLPatternSet& default_runtime_blocked_hosts,
const URLPatternSet& default_runtime_allowed_hosts) {
PermissionsData::SetDefaultPolicyHostRestrictions(
default_runtime_blocked_hosts, default_runtime_allowed_hosts);
// Send notification to the currently running renderers of the runtime block
// hosts settings.
NotifyDefaultPolicyHostRestrictionsUpdated(default_runtime_blocked_hosts,
default_runtime_allowed_hosts);
}
void PermissionsUpdater::RemovePermissionsUnsafe(
const Extension* extension,
const PermissionSet& to_remove) {
const PermissionSet& active =
extension->permissions_data()->active_permissions();
std::unique_ptr<const PermissionSet> total =
PermissionSet::CreateDifference(active, to_remove);
// |successfully_removed| might not equal |to_remove| if |to_remove| contains
// permissions the extension didn't have.
std::unique_ptr<const PermissionSet> successfully_removed =
PermissionSet::CreateDifference(active, *total);
SetPermissions(extension, std::move(total), nullptr);
NotifyPermissionsUpdated(REMOVED, extension, *successfully_removed);
}
std::unique_ptr<const PermissionSet>
PermissionsUpdater::GetRevokablePermissions(const Extension* extension) const {
// The user can revoke any permissions they granted. In other words, any
// permissions the extension didn't start with can be revoked.
const PermissionSet& required =
PermissionsParser::GetRequiredPermissions(extension);
std::unique_ptr<const PermissionSet> granted;
std::unique_ptr<const PermissionSet> withheld;
ScriptingPermissionsModifier(browser_context_,
base::WrapRefCounted(extension))
.WithholdPermissions(required, &granted, &withheld, true);
return PermissionSet::CreateDifference(
extension->permissions_data()->active_permissions(), *granted);
}
void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {
CHECK(extension);
ExtensionPrefs::Get(browser_context_)
->AddGrantedPermissions(
extension->id(), extension->permissions_data()->active_permissions());
}
void PermissionsUpdater::InitializePermissions(const Extension* extension) {
std::unique_ptr<const PermissionSet> bounded_wrapper;
const PermissionSet* bounded_active = nullptr;
// If |extension| is a transient dummy extension, we do not want to look for
// it in preferences.
if (init_flag_ & INIT_FLAG_TRANSIENT) {
bounded_active = &extension->permissions_data()->active_permissions();
} else {
std::unique_ptr<const PermissionSet> active_permissions =
ExtensionPrefs::Get(browser_context_)
->GetActivePermissions(extension->id());
bounded_wrapper =
GetBoundedActivePermissions(extension, active_permissions.get());
bounded_active = bounded_wrapper.get();
}
std::unique_ptr<const PermissionSet> granted_permissions;
std::unique_ptr<const PermissionSet> withheld_permissions;
ScriptingPermissionsModifier(browser_context_,
base::WrapRefCounted(extension))
.WithholdPermissions(*bounded_active, &granted_permissions,
&withheld_permissions,
(init_flag_ & INIT_FLAG_TRANSIENT) != 0);
if (g_delegate)
g_delegate->InitializePermissions(extension, &granted_permissions);
if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
// Apply per-extension policy if set.
ExtensionManagement* management =
ExtensionManagementFactory::GetForBrowserContext(browser_context_);
if (!management->UsesDefaultRuntimeHostRestrictions(extension)) {
SetPolicyHostRestrictions(extension,
management->GetRuntimeBlockedHosts(extension),
management->GetRuntimeAllowedHosts(extension));
}
}
SetPermissions(extension, std::move(granted_permissions),
std::move(withheld_permissions));
}
void PermissionsUpdater::SetPermissions(
const Extension* extension,
std::unique_ptr<const PermissionSet> active,
std::unique_ptr<const PermissionSet> withheld) {
DCHECK(active);
const PermissionSet& active_weak = *active;
if (withheld) {
extension->permissions_data()->SetPermissions(std::move(active),
std::move(withheld));
} else {
extension->permissions_data()->SetActivePermissions(std::move(active));
}
if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
ExtensionPrefs::Get(browser_context_)
->SetActivePermissions(extension->id(), active_weak);
}
}
void PermissionsUpdater::DispatchEvent(
const std::string& extension_id,
events::HistogramValue histogram_value,
const char* event_name,
const PermissionSet& changed_permissions) {
EventRouter* event_router = EventRouter::Get(browser_context_);
if (!event_router)
return;
std::unique_ptr<base::ListValue> value(new base::ListValue());
std::unique_ptr<api::permissions::Permissions> permissions =
PackPermissionSet(changed_permissions);
value->Append(permissions->ToValue());
auto event = std::make_unique<Event>(histogram_value, event_name,
std::move(value), browser_context_);
event_router->DispatchEventToExtension(extension_id, std::move(event));
}
void PermissionsUpdater::NotifyPermissionsUpdated(
EventType event_type,
const Extension* extension,
const PermissionSet& changed) {
DCHECK_EQ(0, init_flag_ & INIT_FLAG_TRANSIENT);
if (changed.IsEmpty() && event_type != POLICY)
return;
UpdatedExtensionPermissionsInfo::Reason reason;
events::HistogramValue histogram_value = events::UNKNOWN;
const char* event_name = NULL;
Profile* profile = Profile::FromBrowserContext(browser_context_);
if (event_type == REMOVED) {
reason = UpdatedExtensionPermissionsInfo::REMOVED;
histogram_value = events::PERMISSIONS_ON_REMOVED;
event_name = permissions::OnRemoved::kEventName;
} else if (event_type == ADDED) {
reason = UpdatedExtensionPermissionsInfo::ADDED;
histogram_value = events::PERMISSIONS_ON_ADDED;
event_name = permissions::OnAdded::kEventName;
} else {
DCHECK_EQ(POLICY, event_type);
reason = UpdatedExtensionPermissionsInfo::POLICY;
}
// Notify other APIs or interested parties.
UpdatedExtensionPermissionsInfo info =
UpdatedExtensionPermissionsInfo(extension, changed, reason);
content::NotificationService::current()->Notify(
extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
content::Source<Profile>(profile),
content::Details<UpdatedExtensionPermissionsInfo>(&info));
ExtensionMsg_UpdatePermissions_Params params;
params.extension_id = extension->id();
params.active_permissions = ExtensionMsg_PermissionSetStruct(
extension->permissions_data()->active_permissions());
params.withheld_permissions = ExtensionMsg_PermissionSetStruct(
extension->permissions_data()->withheld_permissions());
params.uses_default_policy_host_restrictions =
extension->permissions_data()->UsesDefaultPolicyHostRestrictions();
if (!params.uses_default_policy_host_restrictions) {
params.policy_blocked_hosts =
extension->permissions_data()->policy_blocked_hosts();
params.policy_allowed_hosts =
extension->permissions_data()->policy_allowed_hosts();
}
// Send the new permissions to the renderers.
for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
RenderProcessHost* host = i.GetCurrentValue();
if (profile->IsSameProfile(
Profile::FromBrowserContext(host->GetBrowserContext()))) {
host->Send(new ExtensionMsg_UpdatePermissions(params));
}
}
// Trigger the onAdded and onRemoved events in the extension. We explicitly
// don't do this for policy-related events.
if (event_name)
DispatchEvent(extension->id(), histogram_value, event_name, changed);
}
// Notify the renderers that extension policy (policy_blocked_hosts) is updated
// and provide new set of hosts.
void PermissionsUpdater::NotifyDefaultPolicyHostRestrictionsUpdated(
const URLPatternSet& default_runtime_blocked_hosts,
const URLPatternSet& default_runtime_allowed_hosts) {
DCHECK_EQ(0, init_flag_ & INIT_FLAG_TRANSIENT);
Profile* profile = Profile::FromBrowserContext(browser_context_);
ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params params;
params.default_policy_blocked_hosts = default_runtime_blocked_hosts;
params.default_policy_allowed_hosts = default_runtime_allowed_hosts;
// Send the new policy to the renderers.
for (RenderProcessHost::iterator host_iterator(
RenderProcessHost::AllHostsIterator());
!host_iterator.IsAtEnd(); host_iterator.Advance()) {
RenderProcessHost* host = host_iterator.GetCurrentValue();
if (profile->IsSameProfile(
Profile::FromBrowserContext(host->GetBrowserContext()))) {
host->Send(new ExtensionMsg_UpdateDefaultPolicyHostRestrictions(params));
}
}
}
} // namespace extensions