diff options
author | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-26 04:06:38 +0000 |
---|---|---|
committer | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-26 04:06:38 +0000 |
commit | f371c89b3a65c1a24a19ff5b69e24425b0a7ed4d (patch) | |
tree | 4aae7a77520b012eea0a572956c96f5327b33481 | |
parent | 76d206cbdd193d21f6c6dbf279c4e9e990021eb5 (diff) | |
download | chromium_src-f371c89b3a65c1a24a19ff5b69e24425b0a7ed4d.zip chromium_src-f371c89b3a65c1a24a19ff5b69e24425b0a7ed4d.tar.gz chromium_src-f371c89b3a65c1a24a19ff5b69e24425b0a7ed4d.tar.bz2 |
Add support for top level "sockets" manifest key.
Given the parsing logic for sockets permission string is non trivial, one of the challenges of this CL is to re-use the code from SocketPermissionData.
Note it would have been ideal to re-use the SocketPermission class itself, but i found it to be too tightly coupled with the permission system, and we are implementing a top level manifest key (i.e. we deal with ManifestData structures).
What this CL addresses specifically is the ability for "SocketsUdpXxx" API function to call SocketsApiPermissions::CheckParam for checking permissions.
What this CL doesn't address (yet) is to make sure the "Permissions" UI shows the socket permissions -- i.e. calling SocketPermission::GetMessages(). This proves non-trivial because this code is also tightly coupled to the permission code. I am open to suggestion on how to address this issue.
BUG=275964
Review URL: https://codereview.chromium.org/22935014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@225315 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 626 insertions, 242 deletions
diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc index 7a4abc4..295594e 100644 --- a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc @@ -6,8 +6,7 @@ #include "chrome/browser/extensions/api/socket/udp_socket.h" #include "chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h" -#include "chrome/common/extensions/permissions/permissions_data.h" -#include "chrome/common/extensions/permissions/socket_permission.h" +#include "chrome/common/extensions/api/sockets/sockets_handler.h" #include "content/public/common/socket_permission_request.h" #include "net/base/net_errors.h" @@ -156,12 +155,11 @@ void SocketsUdpBindFunction::Work() { return; } - SocketPermission::CheckParam param( - SocketPermissionRequest::UDP_BIND, params_->address, params_->port); - if (!PermissionsData::CheckAPIPermissionWithParam( - GetExtension(), - APIPermission::kSocket, - ¶m)) { + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_BIND, + params_->address, + params_->port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { error_ = kPermissionError; return; } @@ -198,14 +196,11 @@ void SocketsUdpSendFunction::AsyncWorkStart() { return; } - SocketPermission::CheckParam param( + content::SocketPermissionRequest param( SocketPermissionRequest::UDP_SEND_TO, params_->address, params_->port); - if (!PermissionsData::CheckAPIPermissionWithParam( - GetExtension(), - APIPermission::kSocket, - ¶m)) { + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { error_ = kPermissionError; AsyncWorkCompleted(); return; @@ -341,13 +336,11 @@ void SocketsUdpJoinGroupFunction::Work() { return; } - SocketPermission::CheckParam param( + content::SocketPermissionRequest param( SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kWildcardAddress, kWildcardPort); - - if (!PermissionsData::CheckAPIPermissionWithParam( - GetExtension(), APIPermission::kSocket, ¶m)) { + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { error_ = kPermissionError; return; } @@ -375,13 +368,11 @@ void SocketsUdpLeaveGroupFunction::Work() { return; } - SocketPermission::CheckParam param( + content::SocketPermissionRequest param( SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kWildcardAddress, kWildcardPort); - if (!PermissionsData::CheckAPIPermissionWithParam(GetExtension(), - APIPermission::kSocket, - ¶m)) { + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { error_ = kPermissionError; return; } @@ -459,14 +450,11 @@ void SocketsUdpGetJoinedGroupsFunction::Work() { return; } - SocketPermission::CheckParam param( + content::SocketPermissionRequest param( SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kWildcardAddress, kWildcardPort); - if (!PermissionsData::CheckAPIPermissionWithParam( - GetExtension(), - APIPermission::kSocket, - ¶m)) { + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { error_ = kPermissionError; return; } diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index b7a7230..3e7cef5 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -150,6 +150,8 @@ 'common/extensions/api/omnibox/omnibox_handler.h', 'common/extensions/api/plugins/plugins_handler.cc', 'common/extensions/api/plugins/plugins_handler.h', + 'common/extensions/api/sockets/sockets_handler.cc', + 'common/extensions/api/sockets/sockets_handler.h', 'common/extensions/api/speech/tts_engine_manifest_handler.cc', 'common/extensions/api/speech/tts_engine_manifest_handler.h', 'common/extensions/api/spellcheck/spellcheck_handler.cc', @@ -272,6 +274,8 @@ 'common/extensions/permissions/socket_permission.h', 'common/extensions/permissions/socket_permission_data.cc', 'common/extensions/permissions/socket_permission_data.h', + 'common/extensions/permissions/socket_permission_entry.cc', + 'common/extensions/permissions/socket_permission_entry.h', 'common/extensions/permissions/usb_device_permission.cc', 'common/extensions/permissions/usb_device_permission.h', 'common/extensions/permissions/usb_device_permission_data.cc', diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json index 4acfde3..c2f64ec 100644 --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json @@ -481,7 +481,7 @@ "contexts": ["blessed_extension"] }, "sockets.udp": { - "dependencies": ["permission:sockets.udp"], + "dependencies": ["manifest:sockets"], "channel": "dev", "contexts": ["blessed_extension"] }, diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json index 81a8427..d893129 100644 --- a/chrome/common/extensions/api/_manifest_features.json +++ b/chrome/common/extensions/api/_manifest_features.json @@ -331,6 +331,10 @@ "channel": "stable", "extension_types": "all" }, + "sockets": { + "channel": "dev", + "extension_types": ["platform_app"] + }, "spellcheck": { "channel": "dev", "extension_types": ["extension"] diff --git a/chrome/common/extensions/api/manifest_types.json b/chrome/common/extensions/api/manifest_types.json index e14ed37..1658cf6 100644 --- a/chrome/common/extensions/api/manifest_types.json +++ b/chrome/common/extensions/api/manifest_types.json @@ -32,6 +32,35 @@ "items": {"type": "string"} } } + }, + { + "id": "sockets", + "type": "object", + "description": "The <code>sockets</code> manifest property declares which sockets operations an app can issue.", + "properties": { + "udp": { + "description": "The <code>udp</code> manifest property declares which sockets.udp operations an app can issue.", + "optional": true, + "type": "object", + "properties": { + "bind": { + "description": "<p>The host:port pattern for <code>bind</code> operations.</p>", + "optional": true, + "type": "string" + }, + "send": { + "description": "<p>The host:port pattern for <code>send</code> operations.</p>", + "optional": true, + "type": "string" + }, + "multicastMembership": { + "description": "<p>The host:port pattern for <code>joinGroup</code> operations.</p>", + "optional": true, + "type": "string" + } + } + } + } } ] } diff --git a/chrome/common/extensions/api/sockets/sockets_handler.cc b/chrome/common/extensions/api/sockets/sockets_handler.cc new file mode 100644 index 0000000..a367239 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_handler.cc @@ -0,0 +1,139 @@ +// Copyright 2013 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/common/extensions/api/sockets/sockets_handler.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/common/extensions/api/manifest_types.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/permissions/api_permission_set.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "chrome/common/extensions/permissions/socket_permission_data.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +namespace sockets_errors { +const char kErrorInvalidHostPattern[] = "Invalid host:port pattern '*'"; +} + +namespace keys = extensions::manifest_keys; +namespace errors = sockets_errors; +using api::manifest_types::Sockets; + +SocketsHandler::SocketsHandler() {} + +SocketsHandler::~SocketsHandler() {} + +bool SocketsHandler::Parse(Extension* extension, string16* error) { + const base::Value* sockets = NULL; + CHECK(extension->manifest()->Get(keys::kSockets, &sockets)); + std::vector<InstallWarning> install_warnings; + scoped_ptr<SocketsManifestData> data = + SocketsManifestData::FromValue(*sockets, + &install_warnings, + error); + if (!data) + return false; + + extension->AddInstallWarnings(install_warnings); + extension->SetManifestData(keys::kSockets, data.release()); + return true; +} + +const std::vector<std::string> SocketsHandler::Keys() const { + return SingleKey(manifest_keys::kSockets); +} + +SocketsManifestData::SocketsManifestData() {} +SocketsManifestData::~SocketsManifestData() {} + +// static +SocketsManifestData* SocketsManifestData::Get(const Extension* extension) { + return static_cast<SocketsManifestData*>( + extension->GetManifestData(keys::kSockets)); +} + +// static +bool SocketsManifestData::CheckRequest( + const Extension* extension, + const content::SocketPermissionRequest& request) { + SocketsManifestData* data = SocketsManifestData::Get(extension); + if (data == NULL) + return false; + + return data->CheckRequestImpl(extension, request); +} + +// static +scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( + const base::Value& value, + std::vector<InstallWarning>* install_warnings, + string16* error) { + scoped_ptr<Sockets> sockets = Sockets::FromValue(value, error); + if (!sockets) + return scoped_ptr<SocketsManifestData>(); + + scoped_ptr<SocketsManifestData> result(new SocketsManifestData()); + if (sockets->udp) { + if (!ParseHostPattern(result.get(), + content::SocketPermissionRequest::UDP_BIND, + sockets->udp->bind, + error)) { + return scoped_ptr<SocketsManifestData>(); + } + if (!ParseHostPattern(result.get(), + content::SocketPermissionRequest::UDP_SEND_TO, + sockets->udp->send, + error)) { + return scoped_ptr<SocketsManifestData>(); + } + if (!ParseHostPattern(result.get(), + content::SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + sockets->udp->multicast_membership, + error)) { + return scoped_ptr<SocketsManifestData>(); + } + } + return result.Pass(); +} + +// static +bool SocketsManifestData::ParseHostPattern( + SocketsManifestData* manifest_data, + content::SocketPermissionRequest::OperationType operation_type, + const scoped_ptr<std::string>& value, + string16* error) { + if (value) { + SocketPermissionEntry entry; + if (!SocketPermissionEntry::ParseHostPattern( + operation_type, *value, &entry)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kErrorInvalidHostPattern, *value); + return false; + } + manifest_data->AddPermission(entry); + } + return true; +} + +void SocketsManifestData::AddPermission(const SocketPermissionEntry& entry) { + permissions_.insert(entry); +} + +bool SocketsManifestData::CheckRequestImpl( + const Extension* extension, + const content::SocketPermissionRequest& request) { + for (PermissionSet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->Check(request)) + return true; + } + return false; +} + +} // namespace extensions diff --git a/chrome/common/extensions/api/sockets/sockets_handler.h b/chrome/common/extensions/api/sockets/sockets_handler.h new file mode 100644 index 0000000..ff280d1 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_handler.h @@ -0,0 +1,68 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ +#define CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ + +#include "base/strings/string16.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/manifest_handler.h" +#include "chrome/common/extensions/permissions/socket_permission_data.h" + +namespace extensions { + +// Parses the "sockets" manifest key. +class SocketsHandler : public ManifestHandler { + public: + SocketsHandler(); + virtual ~SocketsHandler(); + + virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + + private: + virtual const std::vector<std::string> Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(SocketsHandler); +}; + +// The parsed form of the "sockets" manifest entry. +class SocketsManifestData : public Extension::ManifestData { + public: + SocketsManifestData(); + virtual ~SocketsManifestData(); + + // Gets the ExternallyConnectableInfo for |extension|, or NULL if none was + // specified. + static SocketsManifestData* Get(const Extension* extension); + + static bool CheckRequest(const Extension* extension, + const content::SocketPermissionRequest& request); + + // Tries to construct the info based on |value|, as it would have appeared in + // the manifest. Sets |error| and returns an empty scoped_ptr on failure. + static scoped_ptr<SocketsManifestData> FromValue( + const base::Value& value, + std::vector<InstallWarning>* install_warnings, + string16* error); + + private: + typedef std::set<SocketPermissionEntry> PermissionSet; + + static bool ParseHostPattern( + SocketsManifestData* manifest_data, + content::SocketPermissionRequest::OperationType operation_type, + const scoped_ptr<std::string>& value, + string16* error); + + void AddPermission(const SocketPermissionEntry& entry); + + bool CheckRequestImpl(const Extension* extension, + const content::SocketPermissionRequest& request); + + PermissionSet permissions_; +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc index 07137d9..333996cb 100644 --- a/chrome/common/extensions/chrome_manifest_handlers.cc +++ b/chrome/common/extensions/chrome_manifest_handlers.cc @@ -20,6 +20,7 @@ #include "chrome/common/extensions/api/media_galleries_private/media_galleries_handler.h" #include "chrome/common/extensions/api/omnibox/omnibox_handler.h" #include "chrome/common/extensions/api/plugins/plugins_handler.h" +#include "chrome/common/extensions/api/sockets/sockets_handler.h" #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h" #include "chrome/common/extensions/api/spellcheck/spellcheck_handler.h" #include "chrome/common/extensions/api/system_indicator/system_indicator_handler.h" @@ -86,6 +87,7 @@ void RegisterChromeManifestHandlers() { (new SandboxedPageHandler)->Register(); (new ScriptBadgeHandler)->Register(); (new SharedModuleHandler)->Register(); + (new SocketsHandler)->Register(); (new SpellcheckHandler)->Register(); (new StorageSchemaManifestHandler)->Register(); (new SystemIndicatorHandler)->Register(); diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 8d3b3e1..00a3275 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -168,9 +168,13 @@ IPC_STRUCT_TRAITS_BEGIN(content::SocketPermissionRequest) IPC_STRUCT_TRAITS_MEMBER(port) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(extensions::SocketPermissionEntry) + IPC_STRUCT_TRAITS_MEMBER(pattern_) + IPC_STRUCT_TRAITS_MEMBER(match_subdomains_) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(extensions::SocketPermissionData) - IPC_STRUCT_TRAITS_MEMBER(pattern()) - IPC_STRUCT_TRAITS_MEMBER(match_subdomains()) + IPC_STRUCT_TRAITS_MEMBER(entry()) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(extensions::UsbDevicePermissionData) diff --git a/chrome/common/extensions/permissions/socket_permission.cc b/chrome/common/extensions/permissions/socket_permission.cc index ebf7205..9f2c35c 100644 --- a/chrome/common/extensions/permissions/socket_permission.cc +++ b/chrome/common/extensions/permissions/socket_permission.cc @@ -37,8 +37,8 @@ PermissionMessages SocketPermission::GetMessages() const { bool SocketPermission::AddAnyHostMessage(PermissionMessages& messages) const { std::set<SocketPermissionData>::const_iterator i; for (i = data_set_.begin(); i != data_set_.end(); ++i) { - if (i->IsAddressBoundType() && - i->GetHostType() == SocketPermissionData::ANY_HOST) { + if (i->entry().IsAddressBoundType() && + i->entry().GetHostType() == SocketPermissionEntry::ANY_HOST) { messages.push_back(PermissionMessage( PermissionMessage::kSocketAnyHost, l10n_util::GetStringUTF16( @@ -54,8 +54,8 @@ void SocketPermission::AddSubdomainHostMessage( std::set<string16> domains; std::set<SocketPermissionData>::const_iterator i; for (i = data_set_.begin(); i != data_set_.end(); ++i) { - if (i->GetHostType() == SocketPermissionData::HOSTS_IN_DOMAINS) - domains.insert(UTF8ToUTF16(i->GetHost())); + if (i->entry().GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS) + domains.insert(UTF8ToUTF16(i->entry().pattern().host)); } if (!domains.empty()) { int id = (domains.size() == 1) ? @@ -76,8 +76,8 @@ void SocketPermission::AddSpecificHostMessage( std::set<string16> hostnames; std::set<SocketPermissionData>::const_iterator i; for (i = data_set_.begin(); i != data_set_.end(); ++i) { - if (i->GetHostType() == SocketPermissionData::SPECIFIC_HOSTS) - hostnames.insert(UTF8ToUTF16(i->GetHost())); + if (i->entry().GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS) + hostnames.insert(UTF8ToUTF16(i->entry().pattern().host)); } if (!hostnames.empty()) { int id = (hostnames.size() == 1) ? @@ -97,7 +97,8 @@ void SocketPermission::AddNetworkListMessage( PermissionMessages& messages) const { std::set<SocketPermissionData>::const_iterator i; for (i = data_set_.begin(); i != data_set_.end(); ++i) { - if (i->pattern().type == content::SocketPermissionRequest::NETWORK_STATE) { + if (i->entry().pattern().type == + content::SocketPermissionRequest::NETWORK_STATE) { messages.push_back(PermissionMessage( PermissionMessage::kNetworkState, l10n_util::GetStringUTF16( diff --git a/chrome/common/extensions/permissions/socket_permission_data.cc b/chrome/common/extensions/permissions/socket_permission_data.cc index fc7908e..e87fd84 100644 --- a/chrome/common/extensions/permissions/socket_permission_data.cc +++ b/chrome/common/extensions/permissions/socket_permission_data.cc @@ -23,8 +23,6 @@ using content::SocketPermissionRequest; using extensions::SocketPermissionData; const char kColon = ':'; -const char kDot = '.'; -const char kWildcard[] = "*"; const char kInvalid[] = "invalid"; const char kTCPConnect[] = "tcp-connect"; const char kTCPListen[] = "tcp-listen"; @@ -34,8 +32,6 @@ const char kUDPMulticastMembership[] = "udp-multicast-membership"; const char kResolveHost[] = "resolve-host"; const char kResolveProxy[] = "resolve-proxy"; const char kNetworkState[] = "network-state"; -const int kWildcardPortNumber = 0; -const int kInvalidPort = -1; SocketPermissionRequest::OperationType StringToType(const std::string& s) { if (s == kTCPConnect) @@ -80,52 +76,20 @@ const char* TypeToString(SocketPermissionRequest::OperationType type) { } } -bool StartsOrEndsWithWhitespace(const std::string& str) { - if (str.find_first_not_of(kWhitespaceASCII) != 0) - return true; - if (str.find_last_not_of(kWhitespaceASCII) != str.length() - 1) - return true; - return false; -} - } // namespace namespace extensions { -SocketPermissionData::SocketPermissionData() - : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort) { - Reset(); -} +SocketPermissionData::SocketPermissionData() { } -SocketPermissionData::~SocketPermissionData() { -} +SocketPermissionData::~SocketPermissionData() { } bool SocketPermissionData::operator<(const SocketPermissionData& rhs) const { - if (pattern_.type < rhs.pattern_.type) - return true; - if (pattern_.type > rhs.pattern_.type) - return false; - - if (pattern_.host < rhs.pattern_.host) - return true; - if (pattern_.host > rhs.pattern_.host) - return false; - - if (match_subdomains_ < rhs.match_subdomains_) - return true; - if (match_subdomains_ > rhs.match_subdomains_) - return false; - - if (pattern_.port < rhs.pattern_.port) - return true; - return false; + return entry_ < rhs.entry_; } bool SocketPermissionData::operator==(const SocketPermissionData& rhs) const { - return (pattern_.type == rhs.pattern_.type) && - (pattern_.host == rhs.pattern_.host) && - (match_subdomains_ == rhs.match_subdomains_) && - (pattern_.port == rhs.pattern_.port); + return entry_ == rhs.entry_; } bool SocketPermissionData::Check( @@ -136,41 +100,7 @@ bool SocketPermissionData::Check( *static_cast<const SocketPermission::CheckParam*>(param); const SocketPermissionRequest &request = specific_param.request; - if (pattern_.type != request.type) - return false; - - std::string lhost = StringToLowerASCII(request.host); - if (pattern_.host != lhost) { - if (!match_subdomains_) - return false; - - if (!pattern_.host.empty()) { - // Do not wildcard part of IP address. - url_parse::Component component(0, lhost.length()); - url_canon::RawCanonOutputT<char, 128> ignored_output; - url_canon::CanonHostInfo host_info; - url_canon::CanonicalizeIPAddress(lhost.c_str(), component, - &ignored_output, &host_info); - if (host_info.IsIPAddress()) - return false; - - // host should equal one or more chars + "." + host_. - int i = lhost.length() - pattern_.host.length(); - if (i < 2) - return false; - - if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0) - return false; - - if (lhost[i - 1] != kDot) - return false; - } - } - - if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber) - return false; - - return true; + return entry_.Check(request); } scoped_ptr<base::Value> SocketPermissionData::ToValue() const { @@ -185,95 +115,27 @@ bool SocketPermissionData::FromValue(const base::Value* value) { return Parse(spec); } -bool SocketPermissionData::IsAddressBoundType() const { - return pattern_.type == SocketPermissionRequest::TCP_CONNECT || - pattern_.type == SocketPermissionRequest::TCP_LISTEN || - pattern_.type == SocketPermissionRequest::UDP_BIND || - pattern_.type == SocketPermissionRequest::UDP_SEND_TO; -} - -SocketPermissionData::HostType SocketPermissionData::GetHostType() const { - return pattern_.host.empty() ? SocketPermissionData::ANY_HOST : - match_subdomains_ ? SocketPermissionData::HOSTS_IN_DOMAINS : - SocketPermissionData::SPECIFIC_HOSTS; -} - -const std::string SocketPermissionData::GetHost() const { - return pattern_.host; -} - -content::SocketPermissionRequest& SocketPermissionData::pattern() { - // Clear the spec because the caller could mutate |this|. - spec_.clear(); - return pattern_; -} - -bool& SocketPermissionData::match_subdomains() { - // Clear the spec because the caller could mutate |this|. - spec_.clear(); - return match_subdomains_; +SocketPermissionEntry& SocketPermissionData::entry() { + // Clear the spec because the caller could mutate |this|. + spec_.clear(); + return entry_; } // TODO(ikarienator): Rewrite this method to support IPv6. bool SocketPermissionData::Parse(const std::string& permission) { - do { - pattern_.host.clear(); - match_subdomains_ = true; - pattern_.port = kWildcardPortNumber; - spec_.clear(); - - std::vector<std::string> tokens; - base::SplitStringDontTrim(permission, kColon, &tokens); - - if (tokens.empty() || tokens.size() > 3) - break; - - pattern_.type = StringToType(tokens[0]); - if (pattern_.type == SocketPermissionRequest::NONE) - break; - - if (tokens.size() == 1) - return true; - - // Return an error if address is specified for permissions that don't - // need it (such as 'resolve-host'). - if (!IsAddressBoundType()) - break; - - pattern_.host = tokens[1]; - if (!pattern_.host.empty()) { - if (StartsOrEndsWithWhitespace(pattern_.host)) - break; - pattern_.host = StringToLowerASCII(pattern_.host); - - // The first component can optionally be '*' to match all subdomains. - std::vector<std::string> host_components; - base::SplitString(pattern_.host, kDot, &host_components); - DCHECK(!host_components.empty()); - - if (host_components[0] == kWildcard || host_components[0].empty()) { - host_components.erase(host_components.begin(), - host_components.begin() + 1); - } else { - match_subdomains_ = false; - } - pattern_.host = JoinString(host_components, kDot); - } - - if (tokens.size() == 2 || tokens[2].empty() || tokens[2] == kWildcard) - return true; + Reset(); - if (StartsOrEndsWithWhitespace(tokens[2])) - break; + std::vector<std::string> tokens; + base::SplitStringDontTrim(permission, kColon, &tokens); + if (tokens.empty()) + return false; - if (!base::StringToInt(tokens[2], &pattern_.port) || - pattern_.port < 1 || pattern_.port > 65535) - break; - return true; - } while (false); + SocketPermissionRequest::OperationType type = StringToType(tokens[0]); + if (type == SocketPermissionRequest::NONE) + return false; - Reset(); - return false; + tokens.erase(tokens.begin()); + return SocketPermissionEntry::ParseHostPattern(type, tokens, &entry_); } const std::string& SocketPermissionData::GetAsString() const { @@ -281,32 +143,16 @@ const std::string& SocketPermissionData::GetAsString() const { return spec_; spec_.reserve(64); - spec_.append(TypeToString(pattern_.type)); - - if (!IsAddressBoundType()) - return spec_; - - if (match_subdomains_) { - spec_.append(1, kColon).append(kWildcard); - if (!pattern_.host.empty()) - spec_.append(1, kDot).append(pattern_.host); - } else { - spec_.append(1, kColon).append(pattern_.host); + spec_.append(TypeToString(entry_.pattern().type)); + std::string pattern = entry_.GetHostPatternAsString(); + if (!pattern.empty()) { + spec_.append(1, kColon).append(pattern); } - - if (pattern_.port == kWildcardPortNumber) - spec_.append(1, kColon).append(kWildcard); - else - spec_.append(1, kColon).append(base::IntToString(pattern_.port)); - return spec_; } void SocketPermissionData::Reset() { - pattern_.type = SocketPermissionRequest::NONE; - pattern_.host.clear(); - match_subdomains_ = false; - pattern_.port = kInvalidPort; + entry_ = SocketPermissionEntry(); spec_.clear(); } diff --git a/chrome/common/extensions/permissions/socket_permission_data.h b/chrome/common/extensions/permissions/socket_permission_data.h index 1a23cc2..d647568 100644 --- a/chrome/common/extensions/permissions/socket_permission_data.h +++ b/chrome/common/extensions/permissions/socket_permission_data.h @@ -6,9 +6,11 @@ #include <string> -#include "base/memory/scoped_ptr.h" #include "chrome/common/extensions/permissions/api_permission.h" -#include "content/public/common/socket_permission_request.h" +#include "chrome/common/extensions/permissions/socket_permission_entry.h" +#include "ipc/ipc_param_traits.h" + +template <class T> struct FuzzTraits; namespace extensions { @@ -35,12 +37,6 @@ namespace extensions { // The multicast membership permission implies a permission to any address. class SocketPermissionData { public: - enum HostType { - ANY_HOST, - HOSTS_IN_DOMAINS, - SPECIFIC_HOSTS, - }; - SocketPermissionData(); ~SocketPermissionData(); @@ -59,33 +55,26 @@ class SocketPermissionData { // Populate |this| from a base::Value. bool FromValue(const base::Value* value); - // Returns true if the permission type can be bound to a host or port. - bool IsAddressBoundType() const; - - HostType GetHostType() const; - const std::string GetHost() const; - - const content::SocketPermissionRequest& pattern() const { return pattern_; } - const bool& match_subdomains() const { return match_subdomains_; } - - // These accessors are provided for IPC_STRUCT_TRAITS_MEMBER. Please - // think twice before using them for anything else. - content::SocketPermissionRequest& pattern(); - bool& match_subdomains(); - // TODO(bryeung): SocketPermissionData should be encoded as a base::Value // instead of a string. Until that is done, expose these methods for // testing. bool ParseForTest(const std::string& permission) { return Parse(permission); } const std::string& GetAsStringForTest() const { return GetAsString(); } + const SocketPermissionEntry& entry() const { return entry_; } + private: + // Friend so ParamTraits can serialize us. + friend struct IPC::ParamTraits<SocketPermissionData>; + friend struct FuzzTraits<SocketPermissionData>; + + SocketPermissionEntry& entry(); + bool Parse(const std::string& permission); const std::string& GetAsString() const; void Reset(); - content::SocketPermissionRequest pattern_; - bool match_subdomains_; + SocketPermissionEntry entry_; mutable std::string spec_; }; diff --git a/chrome/common/extensions/permissions/socket_permission_entry.cc b/chrome/common/extensions/permissions/socket_permission_entry.cc new file mode 100644 index 0000000..17f2ef7 --- /dev/null +++ b/chrome/common/extensions/permissions/socket_permission_entry.cc @@ -0,0 +1,227 @@ +// Copyright 2013 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/common/extensions/permissions/socket_permission_entry.h" + +#include <cstdlib> +#include <sstream> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "chrome/common/extensions/permissions/api_permission.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "url/url_canon.h" + +namespace { + +using content::SocketPermissionRequest; + +const char kColon = ':'; +const char kDot = '.'; +const char kWildcard[] = "*"; +const int kWildcardPortNumber = 0; +const int kInvalidPort = -1; + +bool StartsOrEndsWithWhitespace(const std::string& str) { + if (str.find_first_not_of(kWhitespaceASCII) != 0) + return true; + if (str.find_last_not_of(kWhitespaceASCII) != str.length() - 1) + return true; + return false; +} + +} // namespace + +namespace extensions { + +SocketPermissionEntry::SocketPermissionEntry() + : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort), + match_subdomains_(false) { +} + +SocketPermissionEntry::~SocketPermissionEntry() {} + +bool SocketPermissionEntry::operator<(const SocketPermissionEntry& rhs) const { + if (pattern_.type < rhs.pattern_.type) + return true; + if (pattern_.type > rhs.pattern_.type) + return false; + + if (pattern_.host < rhs.pattern_.host) + return true; + if (pattern_.host > rhs.pattern_.host) + return false; + + if (match_subdomains_ < rhs.match_subdomains_) + return true; + if (match_subdomains_ > rhs.match_subdomains_) + return false; + + if (pattern_.port < rhs.pattern_.port) + return true; + return false; +} + +bool SocketPermissionEntry::operator==(const SocketPermissionEntry& rhs) const { + return (pattern_.type == rhs.pattern_.type) && + (pattern_.host == rhs.pattern_.host) && + (match_subdomains_ == rhs.match_subdomains_) && + (pattern_.port == rhs.pattern_.port); +} + +bool SocketPermissionEntry::Check( + const content::SocketPermissionRequest& request) const { + if (pattern_.type != request.type) + return false; + + std::string lhost = StringToLowerASCII(request.host); + if (pattern_.host != lhost) { + if (!match_subdomains_) + return false; + + if (!pattern_.host.empty()) { + // Do not wildcard part of IP address. + url_parse::Component component(0, lhost.length()); + url_canon::RawCanonOutputT<char, 128> ignored_output; + url_canon::CanonHostInfo host_info; + url_canon::CanonicalizeIPAddress(lhost.c_str(), component, + &ignored_output, &host_info); + if (host_info.IsIPAddress()) + return false; + + // host should equal one or more chars + "." + host_. + int i = lhost.length() - pattern_.host.length(); + if (i < 2) + return false; + + if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0) + return false; + + if (lhost[i - 1] != kDot) + return false; + } + } + + if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber) + return false; + + return true; +} + +SocketPermissionEntry::HostType SocketPermissionEntry::GetHostType() const { + return pattern_.host.empty() ? SocketPermissionEntry::ANY_HOST : + match_subdomains_ ? SocketPermissionEntry::HOSTS_IN_DOMAINS : + SocketPermissionEntry::SPECIFIC_HOSTS; +} + +bool SocketPermissionEntry::IsAddressBoundType() const { + return pattern_.type == SocketPermissionRequest::TCP_CONNECT || + pattern_.type == SocketPermissionRequest::TCP_LISTEN || + pattern_.type == SocketPermissionRequest::UDP_BIND || + pattern_.type == SocketPermissionRequest::UDP_SEND_TO; +} + +// static +bool SocketPermissionEntry::ParseHostPattern( + SocketPermissionRequest::OperationType type, + const std::string& pattern, + SocketPermissionEntry* entry) { + std::vector<std::string> tokens; + base::SplitStringDontTrim(pattern, kColon, &tokens); + return ParseHostPattern(type, tokens, entry); +} + +// static +bool SocketPermissionEntry::ParseHostPattern( + SocketPermissionRequest::OperationType type, + const std::vector<std::string>& pattern_tokens, + SocketPermissionEntry* entry) { + + SocketPermissionEntry result; + + if (type == SocketPermissionRequest::NONE) + return false; + + if (pattern_tokens.size() > 2) + return false; + + result.pattern_.type = type; + result.pattern_.port = kWildcardPortNumber; + result.match_subdomains_ = true; + + if (pattern_tokens.size() == 0) { + *entry = result; + return true; + } + + // Return an error if address is specified for permissions that don't + // need it (such as 'resolve-host'). + if (!result.IsAddressBoundType()) + return false; + + result.pattern_.host = pattern_tokens[0]; + if (!result.pattern_.host.empty()) { + if (StartsOrEndsWithWhitespace(result.pattern_.host)) + return false; + result.pattern_.host = StringToLowerASCII(result.pattern_.host); + + // The first component can optionally be '*' to match all subdomains. + std::vector<std::string> host_components; + base::SplitString(result.pattern_.host, kDot, &host_components); + DCHECK(!host_components.empty()); + + if (host_components[0] == kWildcard || host_components[0].empty()) { + host_components.erase(host_components.begin(), + host_components.begin() + 1); + } else { + result.match_subdomains_ = false; + } + result.pattern_.host = JoinString(host_components, kDot); + } + + if (pattern_tokens.size() == 1 || + pattern_tokens[1].empty() || + pattern_tokens[1] == kWildcard) { + *entry = result; + return true; + } + + if (StartsOrEndsWithWhitespace(pattern_tokens[1])) + return false; + + if (!base::StringToInt(pattern_tokens[1], &result.pattern_.port) || + result.pattern_.port < 1 || result.pattern_.port > 65535) + return false; + + *entry = result; + return true; +} + +std::string SocketPermissionEntry::GetHostPatternAsString() const { + std::string result; + + if (!IsAddressBoundType()) + return result; + + if (match_subdomains()) { + result.append(kWildcard); + if (!pattern_.host.empty()) + result.append(1, kDot).append(pattern_.host); + } else { + result.append(pattern_.host); + } + + if (pattern_.port == kWildcardPortNumber) + result.append(1, kColon).append(kWildcard); + else + result.append(1, kColon).append(base::IntToString(pattern_.port)); + + return result; +} + +} // namespace extensions diff --git a/chrome/common/extensions/permissions/socket_permission_entry.h b/chrome/common/extensions/permissions/socket_permission_entry.h new file mode 100644 index 0000000..b656471 --- /dev/null +++ b/chrome/common/extensions/permissions/socket_permission_entry.h @@ -0,0 +1,82 @@ +// Copyright 2013 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. +#ifndef CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_ +#define CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_ + +#include <string> +#include <vector> + +#include "content/public/common/socket_permission_request.h" +#include "ipc/ipc_param_traits.h" + +template <class T> struct FuzzTraits; + +namespace extensions { + +// Internal representation of a socket permission for a specific operation, such +// as UDP "bind", host 127.0.0.1, port *. +class SocketPermissionEntry { + public: + enum HostType { + ANY_HOST, + HOSTS_IN_DOMAINS, + SPECIFIC_HOSTS, + }; + + SocketPermissionEntry(); + ~SocketPermissionEntry(); + + // operators <, == are needed by container std::set and algorithms + // std::set_includes and std::set_differences. + bool operator<(const SocketPermissionEntry& rhs) const; + bool operator==(const SocketPermissionEntry& rhs) const; + + bool Check(const content::SocketPermissionRequest& request) const; + + // Parse a host:port pattern for a given operation type. + // <pattern> := '' | + // <host> | + // ':' <port> | + // <host> ':' <port> | + // + // <host> := '*' | + // '*.' <anychar except '/' and '*'>+ | + // <anychar except '/' and '*'>+ + // + // <port> := '*' | + // <port number between 0 and 65535>) + static bool ParseHostPattern( + content::SocketPermissionRequest::OperationType type, + const std::string& pattern, + SocketPermissionEntry* entry); + + static bool ParseHostPattern( + content::SocketPermissionRequest::OperationType type, + const std::vector<std::string>& pattern_tokens, + SocketPermissionEntry* entry); + + // Returns true if the permission type can be bound to a host or port. + bool IsAddressBoundType() const; + + std::string GetHostPatternAsString() const; + HostType GetHostType() const; + + const content::SocketPermissionRequest& pattern() const { return pattern_; } + bool match_subdomains() const { return match_subdomains_; } + + private: + // Friend so ParamTraits can serialize us. + friend struct IPC::ParamTraits<SocketPermissionEntry>; + friend struct FuzzTraits<SocketPermissionEntry>; + + // The permission type, host and port. + content::SocketPermissionRequest pattern_; + + // True if there was a wildcard in the host name. + bool match_subdomains_; +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_ diff --git a/chrome/test/data/extensions/api_test/sockets_udp/api/manifest.json b/chrome/test/data/extensions/api_test/sockets_udp/api/manifest.json index a8f9b17..1ba7c5e 100644 --- a/chrome/test/data/extensions/api_test/sockets_udp/api/manifest.json +++ b/chrome/test/data/extensions/api_test/sockets_udp/api/manifest.json @@ -8,8 +8,7 @@ "scripts": ["multicast.js", "background.js"] } }, - "permissions": [ - { "socket": ["udp-send-to", "udp-bind", "udp-multicast-membership"] }, - "sockets.udp" - ] + "sockets": { + "udp": {"send": "*", "bind" : "*", "multicastMembership": ""} + } } diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index e9b5938..ec5a878 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -112,6 +112,7 @@ const char kShiftKey[] = "shiftKey"; const char kShortcutKey[] = "shortcutKey"; const char kShortName[] = "short_name"; const char kSignature[] = "signature"; +const char kSockets[] = "sockets"; const char kSpellcheck[] = "spellcheck"; const char kSpellcheckDictionaryFormat[] = "dictionary_format"; const char kSpellcheckDictionaryLanguage[] = "dictionary_language"; diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index 53fb1ef..241387e 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -121,6 +121,7 @@ extern const char kShiftKey[]; extern const char kShortcutKey[]; extern const char kShortName[]; extern const char kSignature[]; +extern const char kSockets[]; extern const char kSpellcheck[]; extern const char kSpellcheckDictionaryFormat[]; extern const char kSpellcheckDictionaryLanguage[]; |