1 | #include <Access/AllowedClientHosts.h> |
2 | #include <Common/Exception.h> |
3 | #include <common/SimpleCache.h> |
4 | #include <Common/StringUtils/StringUtils.h> |
5 | #include <IO/ReadHelpers.h> |
6 | #include <Poco/Net/SocketAddress.h> |
7 | #include <Poco/RegularExpression.h> |
8 | #include <common/logger_useful.h> |
9 | #include <ext/scope_guard.h> |
10 | #include <boost/range/algorithm/find.hpp> |
11 | #include <boost/range/algorithm/find_first_of.hpp> |
12 | #include <boost/algorithm/string/predicate.hpp> |
13 | #include <ifaddrs.h> |
14 | |
15 | |
16 | namespace DB |
17 | { |
18 | namespace ErrorCodes |
19 | { |
20 | extern const int DNS_ERROR; |
21 | extern const int IP_ADDRESS_NOT_ALLOWED; |
22 | } |
23 | |
24 | namespace |
25 | { |
26 | using IPAddress = Poco::Net::IPAddress; |
27 | using IPSubnet = AllowedClientHosts::IPSubnet; |
28 | const IPSubnet ALL_ADDRESSES{IPAddress{IPAddress::IPv6}, IPAddress{IPAddress::IPv6}}; |
29 | |
30 | const IPAddress & getIPV6Loopback() |
31 | { |
32 | static const IPAddress ip("::1" ); |
33 | return ip; |
34 | } |
35 | |
36 | bool isIPV4LoopbackMappedToIPV6(const IPAddress & ip) |
37 | { |
38 | static const IPAddress prefix("::ffff:127.0.0.0" ); |
39 | /// 104 == 128 - 24, we have to reset the lowest 24 bits of 128 before comparing with `prefix` |
40 | /// (IPv4 loopback means any IP from 127.0.0.0 to 127.255.255.255). |
41 | return (ip & IPAddress(104, IPAddress::IPv6)) == prefix; |
42 | } |
43 | |
44 | /// Converts an address to IPv6. |
45 | /// The loopback address "127.0.0.1" (or any "127.x.y.z") is converted to "::1". |
46 | IPAddress toIPv6(const IPAddress & ip) |
47 | { |
48 | IPAddress v6; |
49 | if (ip.family() == IPAddress::IPv6) |
50 | v6 = ip; |
51 | else |
52 | v6 = IPAddress("::ffff:" + ip.toString()); |
53 | |
54 | // ::ffff:127.XX.XX.XX -> ::1 |
55 | if (isIPV4LoopbackMappedToIPV6(v6)) |
56 | v6 = getIPV6Loopback(); |
57 | |
58 | return v6; |
59 | } |
60 | |
61 | /// Converts a subnet to IPv6. |
62 | IPSubnet toIPv6(const IPSubnet & subnet) |
63 | { |
64 | IPSubnet v6; |
65 | if (subnet.prefix.family() == IPAddress::IPv6) |
66 | v6.prefix = subnet.prefix; |
67 | else |
68 | v6.prefix = IPAddress("::ffff:" + subnet.prefix.toString()); |
69 | |
70 | if (subnet.mask.family() == IPAddress::IPv6) |
71 | v6.mask = subnet.mask; |
72 | else |
73 | v6.mask = IPAddress(96, IPAddress::IPv6) | IPAddress("::ffff:" + subnet.mask.toString()); |
74 | |
75 | v6.prefix = v6.prefix & v6.mask; |
76 | |
77 | // ::ffff:127.XX.XX.XX -> ::1 |
78 | if (isIPV4LoopbackMappedToIPV6(v6.prefix)) |
79 | v6 = {getIPV6Loopback(), IPAddress(128, IPAddress::IPv6)}; |
80 | |
81 | return v6; |
82 | } |
83 | |
84 | /// Helper function for isAddressOfHost(). |
85 | bool isAddressOfHostImpl(const IPAddress & address, const String & host) |
86 | { |
87 | IPAddress addr_v6 = toIPv6(address); |
88 | |
89 | /// Resolve by hand, because Poco don't use AI_ALL flag but we need it. |
90 | addrinfo * ai_begin = nullptr; |
91 | SCOPE_EXIT( |
92 | { |
93 | if (ai_begin) |
94 | freeaddrinfo(ai_begin); |
95 | }); |
96 | |
97 | addrinfo hints; |
98 | memset(&hints, 0, sizeof(hints)); |
99 | hints.ai_family = AF_UNSPEC; |
100 | hints.ai_flags |= AI_V4MAPPED | AI_ALL; |
101 | |
102 | int err = getaddrinfo(host.c_str(), nullptr, &hints, &ai_begin); |
103 | if (err) |
104 | throw Exception("Cannot getaddrinfo(" + host + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); |
105 | |
106 | for (const addrinfo * ai = ai_begin; ai; ai = ai->ai_next) |
107 | { |
108 | if (ai->ai_addrlen && ai->ai_addr) |
109 | { |
110 | if (ai->ai_family == AF_INET) |
111 | { |
112 | const auto & sin = *reinterpret_cast<const sockaddr_in *>(ai->ai_addr); |
113 | if (addr_v6 == toIPv6(IPAddress(&sin.sin_addr, sizeof(sin.sin_addr)))) |
114 | { |
115 | return true; |
116 | } |
117 | } |
118 | else if (ai->ai_family == AF_INET6) |
119 | { |
120 | const auto & sin = *reinterpret_cast<const sockaddr_in6*>(ai->ai_addr); |
121 | if (addr_v6 == IPAddress(&sin.sin6_addr, sizeof(sin.sin6_addr), sin.sin6_scope_id)) |
122 | { |
123 | return true; |
124 | } |
125 | } |
126 | } |
127 | } |
128 | |
129 | return false; |
130 | } |
131 | |
132 | /// Whether a specified address is one of the addresses of a specified host. |
133 | bool isAddressOfHost(const IPAddress & address, const String & host) |
134 | { |
135 | /// We need to cache DNS requests. |
136 | static SimpleCache<decltype(isAddressOfHostImpl), isAddressOfHostImpl> cache; |
137 | return cache(address, host); |
138 | } |
139 | |
140 | /// Helper function for isAddressOfLocalhost(). |
141 | std::vector<IPAddress> getAddressesOfLocalhostImpl() |
142 | { |
143 | std::vector<IPAddress> addresses; |
144 | |
145 | ifaddrs * ifa_begin = nullptr; |
146 | SCOPE_EXIT({ |
147 | if (ifa_begin) |
148 | freeifaddrs(ifa_begin); |
149 | }); |
150 | |
151 | int err = getifaddrs(&ifa_begin); |
152 | if (err) |
153 | return {getIPV6Loopback()}; |
154 | |
155 | for (const ifaddrs * ifa = ifa_begin; ifa; ifa = ifa->ifa_next) |
156 | { |
157 | if (!ifa->ifa_addr) |
158 | continue; |
159 | if (ifa->ifa_addr->sa_family == AF_INET) |
160 | { |
161 | const auto & sin = *reinterpret_cast<const sockaddr_in *>(ifa->ifa_addr); |
162 | addresses.push_back(toIPv6(IPAddress(&sin.sin_addr, sizeof(sin.sin_addr)))); |
163 | } |
164 | else if (ifa->ifa_addr->sa_family == AF_INET6) |
165 | { |
166 | const auto & sin = *reinterpret_cast<const sockaddr_in6 *>(ifa->ifa_addr); |
167 | addresses.push_back(IPAddress(&sin.sin6_addr, sizeof(sin.sin6_addr), sin.sin6_scope_id)); |
168 | } |
169 | } |
170 | return addresses; |
171 | } |
172 | |
173 | /// Whether a specified address is one of the addresses of the localhost. |
174 | bool isAddressOfLocalhost(const IPAddress & address) |
175 | { |
176 | /// We need to cache DNS requests. |
177 | static const std::vector<IPAddress> local_addresses = getAddressesOfLocalhostImpl(); |
178 | return boost::range::find(local_addresses, toIPv6(address)) != local_addresses.end(); |
179 | } |
180 | |
181 | /// Helper function for getHostByAddress(). |
182 | String getHostByAddressImpl(const IPAddress & address) |
183 | { |
184 | Poco::Net::SocketAddress sock_addr(address, 0); |
185 | |
186 | /// Resolve by hand, because Poco library doesn't have such functionality. |
187 | char host[1024]; |
188 | int err = getnameinfo(sock_addr.addr(), sock_addr.length(), host, sizeof(host), nullptr, 0, NI_NAMEREQD); |
189 | if (err) |
190 | throw Exception("Cannot getnameinfo(" + address.toString() + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); |
191 | |
192 | /// Check that PTR record is resolved back to client address |
193 | if (!isAddressOfHost(address, host)) |
194 | throw Exception("Host " + String(host) + " isn't resolved back to " + address.toString(), ErrorCodes::DNS_ERROR); |
195 | |
196 | return host; |
197 | } |
198 | |
199 | /// Returns the host name by its address. |
200 | String getHostByAddress(const IPAddress & address) |
201 | { |
202 | /// We need to cache DNS requests. |
203 | static SimpleCache<decltype(getHostByAddressImpl), &getHostByAddressImpl> cache; |
204 | return cache(address); |
205 | } |
206 | } |
207 | |
208 | |
209 | String AllowedClientHosts::IPSubnet::toString() const |
210 | { |
211 | unsigned int prefix_length = mask.prefixLength(); |
212 | if (IPAddress{prefix_length, mask.family()} == mask) |
213 | return prefix.toString() + "/" + std::to_string(prefix_length); |
214 | |
215 | return prefix.toString() + "/" + mask.toString(); |
216 | } |
217 | |
218 | |
219 | AllowedClientHosts::AllowedClientHosts() |
220 | { |
221 | } |
222 | |
223 | |
224 | AllowedClientHosts::AllowedClientHosts(AllAddressesTag) |
225 | { |
226 | addAllAddresses(); |
227 | } |
228 | |
229 | |
230 | AllowedClientHosts::~AllowedClientHosts() = default; |
231 | |
232 | |
233 | AllowedClientHosts::AllowedClientHosts(const AllowedClientHosts & src) |
234 | { |
235 | *this = src; |
236 | } |
237 | |
238 | |
239 | AllowedClientHosts & AllowedClientHosts::operator =(const AllowedClientHosts & src) |
240 | { |
241 | addresses = src.addresses; |
242 | localhost = src.localhost; |
243 | subnets = src.subnets; |
244 | host_names = src.host_names; |
245 | host_regexps = src.host_regexps; |
246 | compiled_host_regexps.clear(); |
247 | return *this; |
248 | } |
249 | |
250 | |
251 | AllowedClientHosts::AllowedClientHosts(AllowedClientHosts && src) = default; |
252 | AllowedClientHosts & AllowedClientHosts::operator =(AllowedClientHosts && src) = default; |
253 | |
254 | |
255 | void AllowedClientHosts::clear() |
256 | { |
257 | addresses.clear(); |
258 | localhost = false; |
259 | subnets.clear(); |
260 | host_names.clear(); |
261 | host_regexps.clear(); |
262 | compiled_host_regexps.clear(); |
263 | } |
264 | |
265 | |
266 | bool AllowedClientHosts::empty() const |
267 | { |
268 | return addresses.empty() && subnets.empty() && host_names.empty() && host_regexps.empty(); |
269 | } |
270 | |
271 | |
272 | void AllowedClientHosts::addAddress(const IPAddress & address) |
273 | { |
274 | IPAddress addr_v6 = toIPv6(address); |
275 | if (boost::range::find(addresses, addr_v6) != addresses.end()) |
276 | return; |
277 | addresses.push_back(addr_v6); |
278 | if (addr_v6.isLoopback()) |
279 | localhost = true; |
280 | } |
281 | |
282 | |
283 | void AllowedClientHosts::addAddress(const String & address) |
284 | { |
285 | addAddress(IPAddress{address}); |
286 | } |
287 | |
288 | |
289 | void AllowedClientHosts::addSubnet(const IPSubnet & subnet) |
290 | { |
291 | IPSubnet subnet_v6 = toIPv6(subnet); |
292 | |
293 | if (subnet_v6.mask == IPAddress(128, IPAddress::IPv6)) |
294 | { |
295 | addAddress(subnet_v6.prefix); |
296 | return; |
297 | } |
298 | |
299 | if (boost::range::find(subnets, subnet_v6) == subnets.end()) |
300 | subnets.push_back(subnet_v6); |
301 | } |
302 | |
303 | |
304 | void AllowedClientHosts::addSubnet(const IPAddress & prefix, const IPAddress & mask) |
305 | { |
306 | addSubnet(IPSubnet{prefix, mask}); |
307 | } |
308 | |
309 | |
310 | void AllowedClientHosts::addSubnet(const IPAddress & prefix, size_t num_prefix_bits) |
311 | { |
312 | addSubnet(prefix, IPAddress(num_prefix_bits, prefix.family())); |
313 | } |
314 | |
315 | |
316 | void AllowedClientHosts::addSubnet(const String & subnet) |
317 | { |
318 | size_t slash = subnet.find('/'); |
319 | if (slash == String::npos) |
320 | { |
321 | addAddress(subnet); |
322 | return; |
323 | } |
324 | |
325 | IPAddress prefix{String{subnet, 0, slash}}; |
326 | String mask(subnet, slash + 1, subnet.length() - slash - 1); |
327 | if (std::all_of(mask.begin(), mask.end(), isNumericASCII)) |
328 | addSubnet(prefix, parseFromString<UInt8>(mask)); |
329 | else |
330 | addSubnet(prefix, IPAddress{mask}); |
331 | } |
332 | |
333 | |
334 | void AllowedClientHosts::addHostName(const String & host_name) |
335 | { |
336 | if (boost::range::find(host_names, host_name) != host_names.end()) |
337 | return; |
338 | host_names.push_back(host_name); |
339 | if (boost::iequals(host_name, "localhost" )) |
340 | localhost = true; |
341 | } |
342 | |
343 | |
344 | void AllowedClientHosts::addHostRegexp(const String & host_regexp) |
345 | { |
346 | if (boost::range::find(host_regexps, host_regexp) == host_regexps.end()) |
347 | host_regexps.push_back(host_regexp); |
348 | } |
349 | |
350 | |
351 | void AllowedClientHosts::addAllAddresses() |
352 | { |
353 | clear(); |
354 | addSubnet(ALL_ADDRESSES); |
355 | } |
356 | |
357 | |
358 | bool AllowedClientHosts::containsAllAddresses() const |
359 | { |
360 | return (boost::range::find(subnets, ALL_ADDRESSES) != subnets.end()) |
361 | || (boost::range::find(host_regexps, ".*" ) != host_regexps.end()) |
362 | || (boost::range::find(host_regexps, "$" ) != host_regexps.end()); |
363 | } |
364 | |
365 | |
366 | void AllowedClientHosts::checkContains(const IPAddress & address, const String & user_name) const |
367 | { |
368 | if (!contains(address)) |
369 | { |
370 | if (user_name.empty()) |
371 | throw Exception("It's not allowed to connect from address " + address.toString(), ErrorCodes::IP_ADDRESS_NOT_ALLOWED); |
372 | else |
373 | throw Exception("User " + user_name + " is not allowed to connect from address " + address.toString(), ErrorCodes::IP_ADDRESS_NOT_ALLOWED); |
374 | } |
375 | } |
376 | |
377 | |
378 | bool AllowedClientHosts::contains(const IPAddress & address) const |
379 | { |
380 | /// Check `ip_addresses`. |
381 | IPAddress addr_v6 = toIPv6(address); |
382 | if (boost::range::find(addresses, addr_v6) != addresses.end()) |
383 | return true; |
384 | |
385 | if (localhost && isAddressOfLocalhost(addr_v6)) |
386 | return true; |
387 | |
388 | /// Check `ip_subnets`. |
389 | for (const auto & subnet : subnets) |
390 | if ((addr_v6 & subnet.mask) == subnet.prefix) |
391 | return true; |
392 | |
393 | /// Check `hosts`. |
394 | for (const String & host_name : host_names) |
395 | { |
396 | try |
397 | { |
398 | if (isAddressOfHost(addr_v6, host_name)) |
399 | return true; |
400 | } |
401 | catch (const Exception & e) |
402 | { |
403 | if (e.code() != ErrorCodes::DNS_ERROR) |
404 | throw; |
405 | /// Try to ignore DNS errors: if host cannot be resolved, skip it and try next. |
406 | LOG_WARNING( |
407 | &Logger::get("AddressPatterns" ), |
408 | "Failed to check if the allowed client hosts contain address " << address.toString() << ". " << e.displayText() |
409 | << ", code = " << e.code()); |
410 | } |
411 | } |
412 | |
413 | /// Check `host_regexps`. |
414 | try |
415 | { |
416 | String resolved_host = getHostByAddress(addr_v6); |
417 | if (!resolved_host.empty()) |
418 | { |
419 | compileRegexps(); |
420 | for (const auto & compiled_regexp : compiled_host_regexps) |
421 | { |
422 | Poco::RegularExpression::Match match; |
423 | if (compiled_regexp && compiled_regexp->match(resolved_host, match)) |
424 | return true; |
425 | } |
426 | } |
427 | } |
428 | catch (const Exception & e) |
429 | { |
430 | if (e.code() != ErrorCodes::DNS_ERROR) |
431 | throw; |
432 | /// Try to ignore DNS errors: if host cannot be resolved, skip it and try next. |
433 | LOG_WARNING( |
434 | &Logger::get("AddressPatterns" ), |
435 | "Failed to check if the allowed client hosts contain address " << address.toString() << ". " << e.displayText() |
436 | << ", code = " << e.code()); |
437 | } |
438 | |
439 | return false; |
440 | } |
441 | |
442 | |
443 | void AllowedClientHosts::compileRegexps() const |
444 | { |
445 | if (compiled_host_regexps.size() == host_regexps.size()) |
446 | return; |
447 | size_t old_size = compiled_host_regexps.size(); |
448 | compiled_host_regexps.reserve(host_regexps.size()); |
449 | for (size_t i = old_size; i != host_regexps.size(); ++i) |
450 | compiled_host_regexps.emplace_back(std::make_unique<Poco::RegularExpression>(host_regexps[i])); |
451 | } |
452 | |
453 | |
454 | bool operator ==(const AllowedClientHosts & lhs, const AllowedClientHosts & rhs) |
455 | { |
456 | return (lhs.addresses == rhs.addresses) && (lhs.subnets == rhs.subnets) && (lhs.host_names == rhs.host_names) |
457 | && (lhs.host_regexps == rhs.host_regexps); |
458 | } |
459 | } |
460 | |