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