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