1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/common/extensions/permissions/socket_permission_data.h" 6 7#include <cstdlib> 8#include <sstream> 9#include <vector> 10 11#include "base/logging.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/string_split.h" 15#include "base/strings/string_util.h" 16#include "chrome/common/extensions/permissions/api_permission.h" 17#include "chrome/common/extensions/permissions/socket_permission.h" 18#include "url/url_canon.h" 19 20namespace { 21 22using content::SocketPermissionRequest; 23using extensions::SocketPermissionData; 24 25const char kColon = ':'; 26const char kDot = '.'; 27const char kWildcard[] = "*"; 28const char kInvalid[] = "invalid"; 29const char kTCPConnect[] = "tcp-connect"; 30const char kTCPListen[] = "tcp-listen"; 31const char kUDPBind[] = "udp-bind"; 32const char kUDPSendTo[] = "udp-send-to"; 33const char kUDPMulticastMembership[] = "udp-multicast-membership"; 34const char kResolveHost[] = "resolve-host"; 35const char kResolveProxy[] = "resolve-proxy"; 36const int kWildcardPortNumber = 0; 37const int kInvalidPort = -1; 38 39SocketPermissionRequest::OperationType StringToType(const std::string& s) { 40 if (s == kTCPConnect) 41 return SocketPermissionRequest::TCP_CONNECT; 42 if (s == kTCPListen) 43 return SocketPermissionRequest::TCP_LISTEN; 44 if (s == kUDPBind) 45 return SocketPermissionRequest::UDP_BIND; 46 if (s == kUDPSendTo) 47 return SocketPermissionRequest::UDP_SEND_TO; 48 if (s == kUDPMulticastMembership) 49 return SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP; 50 if (s == kResolveHost) 51 return SocketPermissionRequest::RESOLVE_HOST; 52 if (s == kResolveProxy) 53 return SocketPermissionRequest::RESOLVE_PROXY; 54 return SocketPermissionRequest::NONE; 55} 56 57const char* TypeToString(SocketPermissionRequest::OperationType type) { 58 switch (type) { 59 case SocketPermissionRequest::TCP_CONNECT: 60 return kTCPConnect; 61 case SocketPermissionRequest::TCP_LISTEN: 62 return kTCPListen; 63 case SocketPermissionRequest::UDP_BIND: 64 return kUDPBind; 65 case SocketPermissionRequest::UDP_SEND_TO: 66 return kUDPSendTo; 67 case SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP: 68 return kUDPMulticastMembership; 69 case SocketPermissionRequest::RESOLVE_HOST: 70 return kResolveHost; 71 case SocketPermissionRequest::RESOLVE_PROXY: 72 return kResolveProxy; 73 default: 74 return kInvalid; 75 } 76} 77 78bool StartsOrEndsWithWhitespace(const std::string& str) { 79 if (str.find_first_not_of(kWhitespaceASCII) != 0) 80 return true; 81 if (str.find_last_not_of(kWhitespaceASCII) != str.length() - 1) 82 return true; 83 return false; 84} 85 86} // namespace 87 88namespace extensions { 89 90SocketPermissionData::SocketPermissionData() 91 : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort) { 92 Reset(); 93} 94 95SocketPermissionData::~SocketPermissionData() { 96} 97 98bool SocketPermissionData::operator<(const SocketPermissionData& rhs) const { 99 if (pattern_.type < rhs.pattern_.type) 100 return true; 101 if (pattern_.type > rhs.pattern_.type) 102 return false; 103 104 if (pattern_.host < rhs.pattern_.host) 105 return true; 106 if (pattern_.host > rhs.pattern_.host) 107 return false; 108 109 if (match_subdomains_ < rhs.match_subdomains_) 110 return true; 111 if (match_subdomains_ > rhs.match_subdomains_) 112 return false; 113 114 if (pattern_.port < rhs.pattern_.port) 115 return true; 116 return false; 117} 118 119bool SocketPermissionData::operator==(const SocketPermissionData& rhs) const { 120 return (pattern_.type == rhs.pattern_.type) && 121 (pattern_.host == rhs.pattern_.host) && 122 (match_subdomains_ == rhs.match_subdomains_) && 123 (pattern_.port == rhs.pattern_.port); 124} 125 126bool SocketPermissionData::Check( 127 const APIPermission::CheckParam* param) const { 128 if (!param) 129 return false; 130 const SocketPermission::CheckParam& specific_param = 131 *static_cast<const SocketPermission::CheckParam*>(param); 132 const SocketPermissionRequest &request = specific_param.request; 133 134 if (pattern_.type != request.type) 135 return false; 136 137 std::string lhost = StringToLowerASCII(request.host); 138 if (pattern_.host != lhost) { 139 if (!match_subdomains_) 140 return false; 141 142 if (!pattern_.host.empty()) { 143 // Do not wildcard part of IP address. 144 url_parse::Component component(0, lhost.length()); 145 url_canon::RawCanonOutputT<char, 128> ignored_output; 146 url_canon::CanonHostInfo host_info; 147 url_canon::CanonicalizeIPAddress(lhost.c_str(), component, 148 &ignored_output, &host_info); 149 if (host_info.IsIPAddress()) 150 return false; 151 152 // host should equal one or more chars + "." + host_. 153 int i = lhost.length() - pattern_.host.length(); 154 if (i < 2) 155 return false; 156 157 if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0) 158 return false; 159 160 if (lhost[i - 1] != kDot) 161 return false; 162 } 163 } 164 165 if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber) 166 return false; 167 168 return true; 169} 170 171scoped_ptr<base::Value> SocketPermissionData::ToValue() const { 172 return scoped_ptr<base::Value>(new base::StringValue(GetAsString())); 173} 174 175bool SocketPermissionData::FromValue(const base::Value* value) { 176 std::string spec; 177 if (!value->GetAsString(&spec)) 178 return false; 179 180 return Parse(spec); 181} 182 183SocketPermissionData::HostType SocketPermissionData::GetHostType() const { 184 return pattern_.host.empty() ? SocketPermissionData::ANY_HOST : 185 match_subdomains_ ? SocketPermissionData::HOSTS_IN_DOMAINS : 186 SocketPermissionData::SPECIFIC_HOSTS; 187} 188 189const std::string SocketPermissionData::GetHost() const { 190 return pattern_.host; 191} 192 193content::SocketPermissionRequest& SocketPermissionData::pattern() { 194 // Clear the spec because the caller could mutate |this|. 195 spec_.clear(); 196 return pattern_; 197} 198 199bool& SocketPermissionData::match_subdomains() { 200 // Clear the spec because the caller could mutate |this|. 201 spec_.clear(); 202 return match_subdomains_; 203} 204 205// TODO(ikarienator): Rewrite this method to support IPv6. 206bool SocketPermissionData::Parse(const std::string& permission) { 207 do { 208 pattern_.host.clear(); 209 match_subdomains_ = true; 210 pattern_.port = kWildcardPortNumber; 211 spec_.clear(); 212 213 std::vector<std::string> tokens; 214 base::SplitStringDontTrim(permission, kColon, &tokens); 215 216 if (tokens.empty() || tokens.size() > 3) 217 break; 218 219 pattern_.type = StringToType(tokens[0]); 220 if (pattern_.type == SocketPermissionRequest::NONE) 221 break; 222 223 if (tokens.size() == 1) 224 return true; 225 226 // Multicast membership, resolve proxy and resolve host permission strings 227 // do not carry an address. 228 if (pattern_.type == SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP || 229 pattern_.type == SocketPermissionRequest::RESOLVE_PROXY || 230 pattern_.type == SocketPermissionRequest::RESOLVE_HOST) 231 break; 232 233 pattern_.host = tokens[1]; 234 if (!pattern_.host.empty()) { 235 if (StartsOrEndsWithWhitespace(pattern_.host)) 236 break; 237 pattern_.host = StringToLowerASCII(pattern_.host); 238 239 // The first component can optionally be '*' to match all subdomains. 240 std::vector<std::string> host_components; 241 base::SplitString(pattern_.host, kDot, &host_components); 242 DCHECK(!host_components.empty()); 243 244 if (host_components[0] == kWildcard || host_components[0].empty()) { 245 host_components.erase(host_components.begin(), 246 host_components.begin() + 1); 247 } else { 248 match_subdomains_ = false; 249 } 250 pattern_.host = JoinString(host_components, kDot); 251 } 252 253 if (tokens.size() == 2 || tokens[2].empty() || tokens[2] == kWildcard) 254 return true; 255 256 if (StartsOrEndsWithWhitespace(tokens[2])) 257 break; 258 259 if (!base::StringToInt(tokens[2], &pattern_.port) || 260 pattern_.port < 1 || pattern_.port > 65535) 261 break; 262 return true; 263 } while (false); 264 265 Reset(); 266 return false; 267} 268 269const std::string& SocketPermissionData::GetAsString() const { 270 if (!spec_.empty()) 271 return spec_; 272 273 spec_.reserve(64); 274 spec_.append(TypeToString(pattern_.type)); 275 276 if (pattern_.type == SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP || 277 pattern_.type == SocketPermissionRequest::RESOLVE_PROXY || 278 pattern_.type == SocketPermissionRequest::RESOLVE_HOST) 279 return spec_; 280 281 if (match_subdomains_) { 282 spec_.append(1, kColon).append(kWildcard); 283 if (!pattern_.host.empty()) 284 spec_.append(1, kDot).append(pattern_.host); 285 } else { 286 spec_.append(1, kColon).append(pattern_.host); 287 } 288 289 if (pattern_.port == kWildcardPortNumber) 290 spec_.append(1, kColon).append(kWildcard); 291 else 292 spec_.append(1, kColon).append(base::IntToString(pattern_.port)); 293 294 return spec_; 295} 296 297void SocketPermissionData::Reset() { 298 pattern_.type = SocketPermissionRequest::NONE; 299 pattern_.host.clear(); 300 match_subdomains_ = false; 301 pattern_.port = kInvalidPort; 302 spec_.clear(); 303} 304 305} // namespace extensions 306