Commit 0239bc50 authored by kalman@chromium.org's avatar kalman@chromium.org

Change the default implementation of ExtensionFunction::HasPermission to use

the real context type, not assume it's BLESSED_CONTEXT_TYPE (which has always
been wrong). This cleans up some other code.

BUG=391944
R=rockot@chromium.org

Review URL: https://codereview.chromium.org/427983003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287987 0039d316-1c4b-4281-b951-d872f2087c98
parent be7407da
......@@ -777,6 +777,9 @@
},
"webstorePrivate": {
"dependencies": ["permission:webstorePrivate"],
// NOTE: even though this is only used by the webstore hosted app, which
// normally would mean blessed_web_page, component hosted apps are actually
// given the blessed_extension denomination. Confusing.
"contexts": ["blessed_extension"]
},
"webViewInternal": [{
......
......@@ -539,6 +539,8 @@ void EventRouter::DispatchEventToProcess(const std::string& extension_id,
BrowserContext* listener_context = process->GetBrowserContext();
ProcessMap* process_map = ProcessMap::Get(listener_context);
// TODO(kalman): Convert this method to use ProcessMap::GuessContextType.
const Extension* extension =
ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID(
extension_id);
......
......@@ -171,7 +171,8 @@ ExtensionFunction::ExtensionFunction()
user_gesture_(false),
bad_message_(false),
histogram_value_(extensions::functions::UNKNOWN),
source_tab_id_(-1) {
source_tab_id_(-1),
source_context_type_(Feature::UNSPECIFIED_CONTEXT) {
}
ExtensionFunction::~ExtensionFunction() {
......@@ -188,7 +189,7 @@ IOThreadExtensionFunction* ExtensionFunction::AsIOThreadExtensionFunction() {
bool ExtensionFunction::HasPermission() {
Feature::Availability availability =
ExtensionAPI::GetSharedInstance()->IsAvailable(
name_, extension_, Feature::BLESSED_EXTENSION_CONTEXT, source_url());
name_, extension_, source_context_type_, source_url());
return availability.is_available();
}
......
......@@ -20,6 +20,7 @@
#include "extensions/browser/extension_function_histogram_value.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/extension.h"
#include "extensions/common/features/feature.h"
#include "ipc/ipc_message.h"
class ExtensionFunction;
......@@ -246,6 +247,13 @@ class ExtensionFunction
void set_source_tab_id(int source_tab_id) { source_tab_id_ = source_tab_id; }
int source_tab_id() const { return source_tab_id_; }
void set_source_context_type(extensions::Feature::Context type) {
source_context_type_ = type;
}
extensions::Feature::Context source_context_type() const {
return source_context_type_;
}
protected:
friend struct ExtensionFunctionDeleteTraits;
......@@ -372,6 +380,9 @@ class ExtensionFunction
// The ID of the tab triggered this function call, or -1 if there is no tab.
int source_tab_id_;
// The type of the JavaScript context where this call originated.
extensions::Feature::Context source_context_type_;
private:
void OnRespondingLater(ResponseValue response);
......
......@@ -14,7 +14,6 @@
#include "base/values.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
......@@ -429,36 +428,6 @@ bool ExtensionFunctionDispatcher::CheckPermissions(
return true;
}
namespace {
// Only COMPONENT hosted apps may call extension APIs, and they are limited
// to just the permissions they explicitly request. They should not have access
// to extension APIs like eg chrome.runtime, chrome.windows, etc. that normally
// are available without permission.
// TODO(mpcomplete): move this to ExtensionFunction::HasPermission (or remove
// it altogether).
bool AllowHostedAppAPICall(const Extension& extension,
const GURL& source_url,
const std::string& function_name) {
if (extension.location() != Manifest::COMPONENT)
return false;
if (!extension.web_extent().MatchesURL(source_url))
return false;
// Note: Not BLESSED_WEB_PAGE_CONTEXT here because these component hosted app
// entities have traditionally been treated as blessed extensions, for better
// or worse.
Feature::Availability availability =
ExtensionAPI::GetSharedInstance()->IsAvailable(
function_name, &extension, Feature::BLESSED_EXTENSION_CONTEXT,
source_url);
return availability.is_available();
}
} // namespace
// static
ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
const ExtensionHostMsg_Request_Params& params,
......@@ -468,44 +437,6 @@ ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
ExtensionAPI* api,
void* profile_id,
const ExtensionFunction::ResponseCallback& callback) {
const char* disallowed_reason = NULL;
if (extension) {
// Extension is calling this API.
if (extension->is_hosted_app() &&
!AllowHostedAppAPICall(*extension, params.source_url, params.name)) {
// Most hosted apps can't call APIs.
disallowed_reason = "Hosted apps cannot call privileged APIs";
} else if (!process_map.Contains(extension->id(), requesting_process_id) &&
!api->IsAvailableInUntrustedContext(params.name, extension)) {
// Privileged APIs can only be called from the process the extension
// is running in.
disallowed_reason =
"Privileged APIs cannot be called from untrusted processes";
}
} else if (content::ChildProcessSecurityPolicy::GetInstance()
->HasWebUIBindings(requesting_process_id)) {
// WebUI is calling this API.
if (!api->IsAvailableToWebUI(params.name, params.source_url)) {
disallowed_reason = "WebUI can only call webui-enabled APIs";
}
} else {
// Web page is calling this API. However, the APIs that are available to
// web pages (e.g. messaging) don't go through ExtensionFunctionDispatcher,
// so this should be impossible.
NOTREACHED();
disallowed_reason = "Specified extension does not exist.";
}
if (disallowed_reason != NULL) {
LOG(ERROR) << "Extension API call disallowed - name:" << params.name
<< ", pid:" << requesting_process_id
<< ", from URL: " << params.source_url.spec()
<< ", reason: " << disallowed_reason;
SendAccessDenied(callback);
return NULL;
}
ExtensionFunction* function =
ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
if (!function) {
......@@ -523,6 +454,8 @@ ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
function->set_profile_id(profile_id);
function->set_response_callback(callback);
function->set_source_tab_id(params.source_tab_id);
function->set_source_context_type(
process_map.GuessContextType(extension, requesting_process_id));
return function;
}
......
......@@ -4,7 +4,11 @@
#include "extensions/browser/process_map.h"
#include "content/public/browser/child_process_security_policy.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_map_factory.h"
#include "extensions/common/extension.h"
#include "extensions/common/features/feature.h"
namespace extensions {
......@@ -119,4 +123,33 @@ std::set<std::string> ProcessMap::GetExtensionsInProcess(int process_id) const {
return result;
}
Feature::Context ProcessMap::GuessContextType(const Extension* extension,
int process_id) const {
// WARNING: This logic must match Dispatcher::ClassifyJavaScriptContext, as
// much as possible.
if (content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
process_id)) {
return Feature::WEBUI_CONTEXT;
}
if (!extension) {
return Feature::WEB_PAGE_CONTEXT;
}
if (!Contains(extension->id(), process_id)) {
// This could equally be UNBLESSED_EXTENSION_CONTEXT, but we don't record
// which processes have extension frames in them.
// TODO(kalman): Investigate this.
return Feature::CONTENT_SCRIPT_CONTEXT;
}
if (extension->is_hosted_app() &&
extension->location() != Manifest::COMPONENT) {
return Feature::BLESSED_WEB_PAGE_CONTEXT;
}
return Feature::BLESSED_EXTENSION_CONTEXT;
}
} // namespace extensions
......@@ -10,12 +10,14 @@
#include "base/basictypes.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/common/features/feature.h"
namespace content {
class BrowserContext;
}
namespace extensions {
class Extension;
// Contains information about which extensions are assigned to which processes.
//
......@@ -66,6 +68,10 @@ namespace extensions {
// RenderProcessHost::FromID() and check the profile of the resulting object.
//
// TODO(aa): The above warnings suggest this class could use improvement :).
//
// TODO(kalman): This class is not threadsafe, but is used on both the UI and
// IO threads. Somebody should fix that, either make it
// threadsafe or enforce single thread. Investigation required.
class ProcessMap : public KeyedService {
public:
ProcessMap();
......@@ -89,6 +95,25 @@ class ProcessMap : public KeyedService {
std::set<std::string> GetExtensionsInProcess(int process_id) const;
// Guesses the most permissive context type for the process with ID
// |process_id|. Context types are renderer (JavaScript) concepts but the
// browser can do a decent job in guessing what the process hosts.
//
// - For hosted app processes, this will be blessed_web_page.
// - For other extension processes, this will be blessed_extension.
// - For WebUI processes, this will be a webui.
// - For anything else we have the choice of unblessed_extension or
// content_script. Since content scripts are more common, guess that.
// We *could* in theory track which web processes have extension frames
// in them, and those would be unblessed_extension, but we don't at the
// moment, and once OOP iframes exist then there won't even be such a
// thing as an unblessed_extension context.
//
// |extension| isn't used to upgrade the process trust level, but rather used
// as a tiebreaker if a process is found to contain multiple extensions.
Feature::Context GuessContextType(const Extension* extension,
int process_id) const;
private:
struct Item;
......
......@@ -128,7 +128,12 @@
"extension_types": "all",
// Everything except web pages and WebUI. WebUI is declared in a separate
// rule to keep the "matches" property isolated.
"contexts": ["blessed_extension", "content_script", "unblessed_extension"]
"contexts": [
"blessed_extension",
"blessed_web_page",
"content_script",
"unblessed_extension"
]
}, {
"internal": true,
"channel": "stable",
......
......@@ -1174,6 +1174,9 @@ Feature::Context Dispatcher::ClassifyJavaScriptContext(
int extension_group,
const GURL& url,
const blink::WebSecurityOrigin& origin) {
// WARNING: This logic must match ProcessMap::GuessContextType, as much as
// possible.
DCHECK_GE(extension_group, 0);
if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
return extension ? // TODO(kalman): when does this happen?
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment