Home | History | Annotate | Download | only in permissions
      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 
     20 namespace {
     21 
     22 using content::SocketPermissionRequest;
     23 using extensions::SocketPermissionData;
     24 
     25 const char kColon = ':';
     26 const char kDot = '.';
     27 const char kWildcard[] = "*";
     28 const char kInvalid[] = "invalid";
     29 const char kTCPConnect[] = "tcp-connect";
     30 const char kTCPListen[] = "tcp-listen";
     31 const char kUDPBind[] = "udp-bind";
     32 const char kUDPSendTo[] = "udp-send-to";
     33 const char kUDPMulticastMembership[] = "udp-multicast-membership";
     34 const char kResolveHost[] = "resolve-host";
     35 const char kResolveProxy[] = "resolve-proxy";
     36 const int kWildcardPortNumber = 0;
     37 const int kInvalidPort = -1;
     38 
     39 SocketPermissionRequest::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 
     57 const 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 
     78 bool 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 
     88 namespace extensions {
     89 
     90 SocketPermissionData::SocketPermissionData()
     91   : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort) {
     92   Reset();
     93 }
     94 
     95 SocketPermissionData::~SocketPermissionData() {
     96 }
     97 
     98 bool 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 
    119 bool 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 
    126 bool 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 
    171 scoped_ptr<base::Value> SocketPermissionData::ToValue() const {
    172   return scoped_ptr<base::Value>(new base::StringValue(GetAsString()));
    173 }
    174 
    175 bool 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 
    183 SocketPermissionData::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 
    189 const std::string SocketPermissionData::GetHost() const {
    190   return pattern_.host;
    191 }
    192 
    193 content::SocketPermissionRequest& SocketPermissionData::pattern() {
    194   // Clear the spec because the caller could mutate |this|.
    195   spec_.clear();
    196   return pattern_;
    197 }
    198 
    199 bool& 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.
    206 bool 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 
    269 const 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 
    297 void 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