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 "net/base/port_util.h"
6 
7 #include <limits>
8 #include <set>
9 
10 #include "base/lazy_instance.h"
11 #include "base/notreached.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "url/url_constants.h"
15 
16 namespace net {
17 
18 namespace {
19 
20 // The general list of blocked ports. Will be blocked unless a specific
21 // protocol overrides it. (Ex: ftp can use ports 20 and 21)
22 const int kRestrictedPorts[] = {
23     1,     // tcpmux
24     7,     // echo
25     9,     // discard
26     11,    // systat
27     13,    // daytime
28     15,    // netstat
29     17,    // qotd
30     19,    // chargen
31     20,    // ftp data
32     21,    // ftp access
33     22,    // ssh
34     23,    // telnet
35     25,    // smtp
36     37,    // time
37     42,    // name
38     43,    // nicname
39     53,    // domain
40     69,    // tftp
41     77,    // priv-rjs
42     79,    // finger
43     87,    // ttylink
44     95,    // supdup
45     101,   // hostriame
46     102,   // iso-tsap
47     103,   // gppitnp
48     104,   // acr-nema
49     109,   // pop2
50     110,   // pop3
51     111,   // sunrpc
52     113,   // auth
53     115,   // sftp
54     117,   // uucp-path
55     119,   // nntp
56     123,   // NTP
57     135,   // loc-srv /epmap
58     137,   // netbios
59     139,   // netbios
60     143,   // imap2
61     161,   // snmp
62     179,   // BGP
63     389,   // ldap
64     427,   // SLP (Also used by Apple Filing Protocol)
65     465,   // smtp+ssl
66     512,   // print / exec
67     513,   // login
68     514,   // shell
69     515,   // printer
70     526,   // tempo
71     530,   // courier
72     531,   // chat
73     532,   // netnews
74     540,   // uucp
75     548,   // AFP (Apple Filing Protocol)
76     556,   // remotefs
77     563,   // nntp+ssl
78     587,   // smtp (rfc6409)
79     601,   // syslog-conn (rfc3195)
80     636,   // ldap+ssl
81     993,   // ldap+ssl
82     995,   // pop3+ssl
83     1719,  // h323gatestat
84     1720,  // h323hostcall
85     1723,  // pptp
86     2049,  // nfs
87     3659,  // apple-sasl / PasswordServer
88     4045,  // lockd
89     5060,  // sip
90     5061,  // sips
91     6000,  // X11
92     6566,  // sane-port
93     6665,  // Alternate IRC [Apple addition]
94     6666,  // Alternate IRC [Apple addition]
95     6667,  // Standard IRC [Apple addition]
96     6668,  // Alternate IRC [Apple addition]
97     6669,  // Alternate IRC [Apple addition]
98     6697,  // IRC + TLS
99 };
100 
101 // FTP overrides the following restricted port.
102 const int kAllowedFtpPorts[] = {
103     21,  // ftp data
104 };
105 
106 base::LazyInstance<std::multiset<int>>::Leaky g_explicitly_allowed_ports =
107     LAZY_INSTANCE_INITIALIZER;
108 
109 }  // namespace
110 
IsPortValid(int port)111 bool IsPortValid(int port) {
112   return port >= 0 && port <= std::numeric_limits<uint16_t>::max();
113 }
114 
IsWellKnownPort(int port)115 bool IsWellKnownPort(int port) {
116   return port >= 0 && port < 1024;
117 }
118 
IsPortAllowedForScheme(int port,base::StringPiece url_scheme)119 bool IsPortAllowedForScheme(int port, base::StringPiece url_scheme) {
120   // Reject invalid ports.
121   if (!IsPortValid(port))
122     return false;
123 
124   // Allow explitly allowed ports for any scheme.
125   if (g_explicitly_allowed_ports.Get().count(port) > 0)
126     return true;
127 
128   // FTP requests have an extra set of allowed schemes.
129   if (base::LowerCaseEqualsASCII(url_scheme, url::kFtpScheme)) {
130     for (int allowed_ftp_port : kAllowedFtpPorts) {
131       if (allowed_ftp_port == port)
132         return true;
133     }
134   }
135 
136   // Finally check against the generic list of restricted ports for all
137   // schemes.
138   for (int restricted_port : kRestrictedPorts) {
139     if (restricted_port == port)
140       return false;
141   }
142 
143   return true;
144 }
145 
GetCountOfExplicitlyAllowedPorts()146 size_t GetCountOfExplicitlyAllowedPorts() {
147   return g_explicitly_allowed_ports.Get().size();
148 }
149 
150 // Specifies a comma separated list of port numbers that should be accepted
151 // despite bans. If the string is invalid no allowed ports are stored.
SetExplicitlyAllowedPorts(const std::string & allowed_ports)152 void SetExplicitlyAllowedPorts(const std::string& allowed_ports) {
153   if (allowed_ports.empty())
154     return;
155 
156   std::multiset<int> ports;
157   size_t last = 0;
158   size_t size = allowed_ports.size();
159   // The comma delimiter.
160   const std::string::value_type kComma = ',';
161 
162   // Overflow is still possible for evil user inputs.
163   for (size_t i = 0; i <= size; ++i) {
164     // The string should be composed of only digits and commas.
165     if (i != size && !base::IsAsciiDigit(allowed_ports[i]) &&
166         (allowed_ports[i] != kComma))
167       return;
168     if (i == size || allowed_ports[i] == kComma) {
169       if (i > last) {
170         int port;
171         base::StringToInt(base::StringPiece(allowed_ports.begin() + last,
172                                             allowed_ports.begin() + i),
173                           &port);
174         ports.insert(port);
175       }
176       last = i + 1;
177     }
178   }
179   g_explicitly_allowed_ports.Get() = ports;
180 }
181 
ScopedPortException(int port)182 ScopedPortException::ScopedPortException(int port) : port_(port) {
183   g_explicitly_allowed_ports.Get().insert(port);
184 }
185 
~ScopedPortException()186 ScopedPortException::~ScopedPortException() {
187   auto it = g_explicitly_allowed_ports.Get().find(port_);
188   if (it != g_explicitly_allowed_ports.Get().end())
189     g_explicitly_allowed_ports.Get().erase(it);
190   else
191     NOTREACHED();
192 }
193 
194 }  // namespace net
195