summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-26 04:06:38 +0000
committerrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-26 04:06:38 +0000
commitf371c89b3a65c1a24a19ff5b69e24425b0a7ed4d (patch)
tree4aae7a77520b012eea0a572956c96f5327b33481
parent76d206cbdd193d21f6c6dbf279c4e9e990021eb5 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc40
-rw-r--r--chrome/chrome_common.gypi4
-rw-r--r--chrome/common/extensions/api/_api_features.json2
-rw-r--r--chrome/common/extensions/api/_manifest_features.json4
-rw-r--r--chrome/common/extensions/api/manifest_types.json29
-rw-r--r--chrome/common/extensions/api/sockets/sockets_handler.cc139
-rw-r--r--chrome/common/extensions/api/sockets/sockets_handler.h68
-rw-r--r--chrome/common/extensions/chrome_manifest_handlers.cc2
-rw-r--r--chrome/common/extensions/extension_messages.h8
-rw-r--r--chrome/common/extensions/permissions/socket_permission.cc15
-rw-r--r--chrome/common/extensions/permissions/socket_permission_data.cc202
-rw-r--r--chrome/common/extensions/permissions/socket_permission_data.h37
-rw-r--r--chrome/common/extensions/permissions/socket_permission_entry.cc227
-rw-r--r--chrome/common/extensions/permissions/socket_permission_entry.h82
-rw-r--r--chrome/test/data/extensions/api_test/sockets_udp/api/manifest.json7
-rw-r--r--extensions/common/manifest_constants.cc1
-rw-r--r--extensions/common/manifest_constants.h1
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,
- &param)) {
+ 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,
- &param)) {
+ 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, &param)) {
+ 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,
- &param)) {
+ 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,
- &param)) {
+ 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[];