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 20namespace { 21 22using content::SocketPermissionRequest; 23 24const char kColon = ':'; 25const char kDot = '.'; 26const char kWildcard[] = "*"; 27const int kWildcardPortNumber = 0; 28const int kInvalidPort = -1; 29 30bool StartsOrEndsWithWhitespace(const std::string& str) { 31 return !str.empty() && 32 (IsWhitespace(str[0]) || IsWhitespace(str[str.length() - 1])); 33} 34 35} // namespace 36 37namespace extensions { 38 39SocketPermissionEntry::SocketPermissionEntry() 40 : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort), 41 match_subdomains_(false) {} 42 43SocketPermissionEntry::~SocketPermissionEntry() {} 44 45bool 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 66bool 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 73bool 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 112SocketPermissionEntry::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 119bool 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 127bool 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 137bool 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 201std::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