Home | History | Annotate | Download | only in permissions
      1 // Copyright 2014 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 "extensions/common/permissions/socket_permission_entry.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 "extensions/common/permissions/api_permission.h"
     17 #include "extensions/common/permissions/socket_permission.h"
     18 #include "url/url_canon.h"
     19 
     20 namespace {
     21 
     22 using content::SocketPermissionRequest;
     23 
     24 const char kColon = ':';
     25 const char kDot = '.';
     26 const char kWildcard[] = "*";
     27 const int kWildcardPortNumber = 0;
     28 const int kInvalidPort = -1;
     29 
     30 bool StartsOrEndsWithWhitespace(const std::string& str) {
     31   return !str.empty() &&
     32       (IsWhitespace(str[0]) || IsWhitespace(str[str.length() - 1]));
     33 }
     34 
     35 }  // namespace
     36 
     37 namespace extensions {
     38 
     39 SocketPermissionEntry::SocketPermissionEntry()
     40     : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort),
     41       match_subdomains_(false) {}
     42 
     43 SocketPermissionEntry::~SocketPermissionEntry() {}
     44 
     45 bool SocketPermissionEntry::operator<(const SocketPermissionEntry& rhs) const {
     46   if (pattern_.type < rhs.pattern_.type)
     47     return true;
     48   if (pattern_.type > rhs.pattern_.type)
     49     return false;
     50 
     51   if (pattern_.host < rhs.pattern_.host)
     52     return true;
     53   if (pattern_.host > rhs.pattern_.host)
     54     return false;
     55 
     56   if (match_subdomains_ < rhs.match_subdomains_)
     57     return true;
     58   if (match_subdomains_ > rhs.match_subdomains_)
     59     return false;
     60 
     61   if (pattern_.port < rhs.pattern_.port)
     62     return true;
     63   return false;
     64 }
     65 
     66 bool SocketPermissionEntry::operator==(const SocketPermissionEntry& rhs) const {
     67   return (pattern_.type == rhs.pattern_.type) &&
     68          (pattern_.host == rhs.pattern_.host) &&
     69          (match_subdomains_ == rhs.match_subdomains_) &&
     70          (pattern_.port == rhs.pattern_.port);
     71 }
     72 
     73 bool SocketPermissionEntry::Check(
     74     const content::SocketPermissionRequest& request) const {
     75   if (pattern_.type != request.type)
     76     return false;
     77 
     78   std::string lhost = base::StringToLowerASCII(request.host);
     79   if (pattern_.host != lhost) {
     80     if (!match_subdomains_)
     81       return false;
     82 
     83     if (!pattern_.host.empty()) {
     84       // Do not wildcard part of IP address.
     85       url::Component component(0, lhost.length());
     86       url::RawCanonOutputT<char, 128> ignored_output;
     87       url::CanonHostInfo host_info;
     88       url::CanonicalizeIPAddress(
     89           lhost.c_str(), component, &ignored_output, &host_info);
     90       if (host_info.IsIPAddress())
     91         return false;
     92 
     93       // host should equal one or more chars + "." +  host_.
     94       int i = lhost.length() - pattern_.host.length();
     95       if (i < 2)
     96         return false;
     97 
     98       if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0)
     99         return false;
    100 
    101       if (lhost[i - 1] != kDot)
    102         return false;
    103     }
    104   }
    105 
    106   if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber)
    107     return false;
    108 
    109   return true;
    110 }
    111 
    112 SocketPermissionEntry::HostType SocketPermissionEntry::GetHostType() const {
    113   return pattern_.host.empty()
    114              ? SocketPermissionEntry::ANY_HOST
    115              : match_subdomains_ ? SocketPermissionEntry::HOSTS_IN_DOMAINS
    116                                  : SocketPermissionEntry::SPECIFIC_HOSTS;
    117 }
    118 
    119 bool SocketPermissionEntry::IsAddressBoundType() const {
    120   return pattern_.type == SocketPermissionRequest::TCP_CONNECT ||
    121          pattern_.type == SocketPermissionRequest::TCP_LISTEN ||
    122          pattern_.type == SocketPermissionRequest::UDP_BIND ||
    123          pattern_.type == SocketPermissionRequest::UDP_SEND_TO;
    124 }
    125 
    126 // static
    127 bool SocketPermissionEntry::ParseHostPattern(
    128     SocketPermissionRequest::OperationType type,
    129     const std::string& pattern,
    130     SocketPermissionEntry* entry) {
    131   std::vector<std::string> tokens;
    132   base::SplitStringDontTrim(pattern, kColon, &tokens);
    133   return ParseHostPattern(type, tokens, entry);
    134 }
    135 
    136 // static
    137 bool SocketPermissionEntry::ParseHostPattern(
    138     SocketPermissionRequest::OperationType type,
    139     const std::vector<std::string>& pattern_tokens,
    140     SocketPermissionEntry* entry) {
    141 
    142   SocketPermissionEntry result;
    143 
    144   if (type == SocketPermissionRequest::NONE)
    145     return false;
    146 
    147   if (pattern_tokens.size() > 2)
    148     return false;
    149 
    150   result.pattern_.type = type;
    151   result.pattern_.port = kWildcardPortNumber;
    152   result.match_subdomains_ = true;
    153 
    154   if (pattern_tokens.size() == 0) {
    155     *entry = result;
    156     return true;
    157   }
    158 
    159   // Return an error if address is specified for permissions that don't
    160   // need it (such as 'resolve-host').
    161   if (!result.IsAddressBoundType())
    162     return false;
    163 
    164   result.pattern_.host = pattern_tokens[0];
    165   if (!result.pattern_.host.empty()) {
    166     if (StartsOrEndsWithWhitespace(result.pattern_.host))
    167       return false;
    168     result.pattern_.host = base::StringToLowerASCII(result.pattern_.host);
    169 
    170     // The first component can optionally be '*' to match all subdomains.
    171     std::vector<std::string> host_components;
    172     base::SplitString(result.pattern_.host, kDot, &host_components);
    173     DCHECK(!host_components.empty());
    174 
    175     if (host_components[0] == kWildcard || host_components[0].empty()) {
    176       host_components.erase(host_components.begin(),
    177                             host_components.begin() + 1);
    178     } else {
    179       result.match_subdomains_ = false;
    180     }
    181     result.pattern_.host = JoinString(host_components, kDot);
    182   }
    183 
    184   if (pattern_tokens.size() == 1 || pattern_tokens[1].empty() ||
    185       pattern_tokens[1] == kWildcard) {
    186     *entry = result;
    187     return true;
    188   }
    189 
    190   if (StartsOrEndsWithWhitespace(pattern_tokens[1]))
    191     return false;
    192 
    193   if (!base::StringToInt(pattern_tokens[1], &result.pattern_.port) ||
    194       result.pattern_.port < 1 || result.pattern_.port > 65535)
    195     return false;
    196 
    197   *entry = result;
    198   return true;
    199 }
    200 
    201 std::string SocketPermissionEntry::GetHostPatternAsString() const {
    202   std::string result;
    203 
    204   if (!IsAddressBoundType())
    205     return result;
    206 
    207   if (match_subdomains()) {
    208     result.append(kWildcard);
    209     if (!pattern_.host.empty())
    210       result.append(1, kDot).append(pattern_.host);
    211   } else {
    212     result.append(pattern_.host);
    213   }
    214 
    215   if (pattern_.port == kWildcardPortNumber)
    216     result.append(1, kColon).append(kWildcard);
    217   else
    218     result.append(1, kColon).append(base::IntToString(pattern_.port));
    219 
    220   return result;
    221 }
    222 
    223 }  // namespace extensions
    224