1 /** @file
2
3 Access control by IP address and HTTP method.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /*****************************************************************************
25 *
26 * IPAllow.h - Interface to IP Access Control system
27 *
28 *
29 ****************************************************************************/
30
31 #pragma once
32
33 #include <string>
34 #include <string_view>
35 #include <vector>
36
37 #include "hdrs/HTTP.h"
38 #include "ProxyConfig.h"
39 #include "tscore/IpMap.h"
40 #include "tscpp/util/TextView.h"
41 #include "tscore/ts_file.h"
42
43 // forward declare in name only so it can be a friend.
44 struct IpAllowUpdate;
45 namespace YAML
46 {
47 class Node;
48 }
49
50 /** Singleton class for access controls.
51 */
52 class IpAllow : public ConfigInfo
53 {
54 friend struct IpAllowUpdate;
55
56 using MethodNames = std::vector<std::string>;
57
58 static constexpr uint32_t ALL_METHOD_MASK = ~0; // Mask for all methods.
59
60 /** An access control record.
61 It has the methods permitted and the source line. This is a POD used by @a ACL.
62 */
63 struct Record {
64 /// Default constructor.
65 /// Present only to make Vec<> happy, do not use.
66 Record() = default;
67 Record(Record &&that) = default;
68 explicit Record(uint32_t method_mask);
69 Record(uint32_t method_mask, int line, MethodNames &&nonstandard_methods, bool deny_nonstandard_methods);
70
71 uint32_t _method_mask{0};
72 int _src_line{0};
73 MethodNames _nonstandard_methods;
74 bool _deny_nonstandard_methods{false};
75 };
76
77 public:
78 using self_type = IpAllow; ///< Self reference type.
79 using scoped_config = ConfigProcessor::scoped_config<self_type, self_type>;
80
81 // indicator for whether we should be checking the acl record for src ip or dest ip
82 enum match_key_t { SRC_ADDR, DST_ADDR };
83 /// Token strings for configuration
84 static constexpr ts::TextView OPT_MATCH_SRC{"src_ip"};
85 static constexpr ts::TextView OPT_MATCH_DST{"dest_ip"};
86 static constexpr ts::TextView OPT_ACTION_TAG{"action"};
87 static constexpr ts::TextView OPT_ACTION_ALLOW{"ip_allow"};
88 static constexpr ts::TextView OPT_ACTION_DENY{"ip_deny"};
89 static constexpr ts::TextView OPT_METHOD{"method"};
90 static constexpr ts::TextView OPT_METHOD_ALL{"all"};
91
92 static constexpr ts::TextView YAML_TAG_ROOT{"ip_allow"};
93 static constexpr ts::TextView YAML_TAG_IP_ADDRS{"ip_addrs"};
94 static constexpr ts::TextView YAML_TAG_APPLY{"apply"};
95 static constexpr ts::TextView YAML_VALUE_APPLY_IN{"in"};
96 static constexpr ts::TextView YAML_VALUE_APPLY_OUT{"out"};
97 static constexpr ts::TextView YAML_TAG_ACTION{"action"};
98 static constexpr ts::TextView YAML_VALUE_ACTION_ALLOW{"allow"};
99 static constexpr ts::TextView YAML_VALUE_ACTION_DENY{"deny"};
100 static constexpr ts::TextView YAML_TAG_METHODS{"methods"};
101 static constexpr ts::TextView YAML_VALUE_METHODS_ALL{"all"};
102
103 static constexpr const char *MODULE_NAME = "IPAllow";
104
105 /** An access control record and support data.
106 * The primary point of this is to hold the backing configuration in memory while the ACL
107 * is in use.
108 */
109 class ACL
110 {
111 friend class IpAllow;
112 using self_type = ACL; ///< Self reference type.
113 public:
114 ACL() = default;
115 ACL(const self_type &) = delete; // no copies.
116 explicit ACL(self_type &&that) noexcept; // move allowed.
117 ~ACL();
118
119 self_type &operator=(const self_type &) = delete;
120 self_type &operator =(self_type &&that) noexcept;
121
122 void clear(); ///< Drop data and config reference.
123
124 static uint32_t MethodIdxToMask(int wksidx);
125
126 /// Check if the ACL is valid (i.e. not uninitialized or missing).
127 bool isValid() const;
128 /// Check if the ACL denies all access.
129 bool isDenyAll() const;
130 /// Check if the ACL allows all access.
131 bool isAllowAll() const;
132
133 bool isMethodAllowed(int method_wksidx) const;
134
135 bool isNonstandardMethodAllowed(std::string_view method) const;
136
137 /// Return the configuration source line for this ACL.
138 int source_line() const;
139
140 private:
141 // @a config must already be ref counted.
142 ACL(const Record *r, IpAllow *config) noexcept;
143
144 const Record *_r{nullptr}; ///< The actual ACL record.
145 IpAllow *_config{nullptr}; ///< The backing configuration.
146 };
147
148 explicit IpAllow(const char *config_var);
149
150 void Print() const;
151
152 static ACL match(sockaddr const *ip, match_key_t key);
153 static ACL match(IpEndpoint const *ip, match_key_t key);
154
155 static void startup();
156 static void reconfigure();
157 /// @return The global instance.
158 static IpAllow *acquire();
159 /// Release the configuration.
160 /// @a config is released and can then be garbage collected.
161 static void release(IpAllow *config);
162 /// Release this configuration.
163 /// @a this is released and can then be garbage collected.
164 void release();
165
166 /// A static ACL that permits all methods.
167 static ACL makeAllowAllACL();
168 /// A static ACL that denies everything.
169 static const ACL DENY_ALL_ACL;
170
171 /* @return The previous accept check state
172 * This is a global variable that is independent of
173 * the ip_allow configuration
174 */
175 static bool enableAcceptCheck(bool state);
176
177 /* @return The current accept check state
178 * This is a global variable that is independent of
179 * the ip_allow configuration
180 */
181 static bool isAcceptCheckEnabled();
182
183 const ts::file::path &get_config_file() const;
184
185 private:
186 static size_t configid; ///< Configuration ID for update management.
187 static const Record ALLOW_ALL_RECORD; ///< Static record that allows all access.
188 static bool accept_check_p; ///< @c true if deny all can be enforced during accept.
189
190 void PrintMap(const IpMap *map) const;
191
192 int BuildTable();
193 int ATSBuildTable(const std::string &);
194 int YAMLBuildTable(const std::string &);
195 bool YAMLLoadEntry(const YAML::Node &);
196 bool YAMLLoadIPAddrRange(const YAML::Node &, IpMap *map, void *mark);
197 bool YAMLLoadMethod(const YAML::Node &node, Record &rec);
198
199 ts::file::path config_file; ///< Path to configuration file.
200 IpMap _src_map;
201 IpMap _dst_map;
202 std::vector<Record> _src_acls;
203 std::vector<Record> _dst_acls;
204 };
205
206 // ------ Record methods --------
207
Record(uint32_t method_mask)208 inline IpAllow::Record::Record(uint32_t method_mask) : _method_mask(method_mask) {}
209
Record(uint32_t method_mask,int ln,MethodNames && nonstandard_methods,bool deny_nonstandard_methods)210 inline IpAllow::Record::Record(uint32_t method_mask, int ln, MethodNames &&nonstandard_methods, bool deny_nonstandard_methods)
211 : _method_mask(method_mask),
212 _src_line(ln),
213 _nonstandard_methods(nonstandard_methods),
214 _deny_nonstandard_methods(deny_nonstandard_methods)
215 {
216 }
217
218 // ------ ACL methods --------
219
ACL(const IpAllow::Record * r,IpAllow * config)220 inline IpAllow::ACL::ACL(const IpAllow::Record *r, IpAllow *config) noexcept : _r(r), _config(config) {}
221
ACL(self_type && that)222 inline IpAllow::ACL::ACL(self_type &&that) noexcept : _r(that._r), _config(that._config)
223 {
224 that._r = nullptr;
225 that._config = nullptr;
226 }
227
~ACL()228 inline IpAllow::ACL::~ACL()
229 {
230 if (_config != nullptr) {
231 _config->release();
232 }
233 }
234
235 inline auto
236 IpAllow::ACL::operator=(self_type &&that) noexcept -> self_type &
237 {
238 // move and clear so @a that doesn't drop the config reference.
239 this->_r = that._r;
240 that._r = nullptr;
241 this->_config = that._config;
242 that._config = nullptr;
243
244 return *this;
245 }
246
247 inline uint32_t
MethodIdxToMask(int wksidx)248 IpAllow::ACL::MethodIdxToMask(int wksidx)
249 {
250 return 1U << (wksidx - HTTP_WKSIDX_CONNECT);
251 }
252
253 inline bool
isValid()254 IpAllow::ACL::isValid() const
255 {
256 return _r != nullptr;
257 }
258
259 inline bool
isDenyAll()260 IpAllow::ACL::isDenyAll() const
261 {
262 return _r == nullptr || (_r->_method_mask == 0 && _r->_nonstandard_methods.empty());
263 }
264
265 inline bool
isAllowAll()266 IpAllow::ACL::isAllowAll() const
267 {
268 return _r && _r->_method_mask == ALL_METHOD_MASK;
269 }
270
271 inline bool
isMethodAllowed(int method_wksidx)272 IpAllow::ACL::isMethodAllowed(int method_wksidx) const
273 {
274 return _r && 0 != (_r->_method_mask & MethodIdxToMask(method_wksidx));
275 }
276
277 inline bool
isNonstandardMethodAllowed(std::string_view method)278 IpAllow::ACL::isNonstandardMethodAllowed(std::string_view method) const
279 {
280 if (_r == nullptr) {
281 return false;
282 } else if (_r->_method_mask == ALL_METHOD_MASK) {
283 return true;
284 }
285 bool method_in_set =
286 std::find_if(_r->_nonstandard_methods.begin(), _r->_nonstandard_methods.end(),
287 [method](std::string_view const &s) { return 0 == strcasecmp(s, method); }) != _r->_nonstandard_methods.end();
288 return _r->_deny_nonstandard_methods ? !method_in_set : method_in_set;
289 }
290
291 inline void
clear()292 IpAllow::ACL::clear()
293 {
294 if (_config) {
295 _config->release();
296 _config = nullptr;
297 }
298 _r = nullptr;
299 }
300
301 inline int
source_line()302 IpAllow::ACL::source_line() const
303 {
304 return _r ? _r->_src_line : 0;
305 }
306
307 // ------ IpAllow methods --------
308
309 inline bool
enableAcceptCheck(bool state)310 IpAllow::enableAcceptCheck(bool state)
311 {
312 bool temp = accept_check_p;
313 accept_check_p = state;
314 return temp;
315 }
316
317 inline bool
isAcceptCheckEnabled()318 IpAllow::isAcceptCheckEnabled()
319 {
320 return accept_check_p;
321 }
322
323 inline auto
324 IpAllow::match(IpEndpoint const *ip, match_key_t key) -> ACL
325 {
326 return self_type::match(&ip->sa, key);
327 }
328
329 inline auto
330 IpAllow::makeAllowAllACL() -> ACL
331 {
332 return {&ALLOW_ALL_RECORD, nullptr};
333 }
334
335 inline const ts::file::path &
get_config_file()336 IpAllow::get_config_file() const
337 {
338 return config_file;
339 }
340