Squid Web Cache master
Loading...
Searching...
No Matches
Acl.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 28 Access Control */
10
11#include "squid.h"
12#include "acl/Acl.h"
13#include "acl/Checklist.h"
14#include "acl/Gadgets.h"
15#include "acl/Options.h"
16#include "anyp/PortCfg.h"
17#include "base/IoManip.h"
18#include "cache_cf.h"
19#include "ConfigParser.h"
20#include "debug/Stream.h"
21#include "fatal.h"
22#include "globals.h"
24#include "sbuf/Algorithms.h"
25#include "sbuf/List.h"
26#include "sbuf/Stream.h"
27#include "SquidConfig.h"
28
29#include <algorithm>
30#include <map>
31#include <unordered_map>
32
33namespace Acl {
34
36class NamedAcls: public std::unordered_map<SBuf, Acl::Node::Pointer,
37 CaseInsensitiveSBufHash, CaseInsensitiveSBufEqual,
38 PoolingAllocator< std::pair<const SBuf, Acl::Node::Pointer> > > {
39};
40
43public:
44 bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
45};
46
48typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
49
51static Makers &
53{
54 static Makers Registry;
55 return Registry;
56}
57
59static
61Make(TypeName typeName)
62{
63 const auto pos = TheMakers().find(typeName);
64 if (pos == TheMakers().end())
65 throw TextException(ToSBuf("invalid ACL type '", typeName, "'"), Here());
66
67 auto *result = (pos->second)(pos->first);
68 debugs(28, 4, typeName << '=' << result);
69 assert(result);
70 return result;
71}
72
75{
76public:
78
79 explicit ParsingContext(const SBuf &name): name_(name) {}
80
81 /* CodeContext API */
82 ScopedId codeContextGist() const override;
83 std::ostream &detailCodeContext(std::ostream &os) const override;
84
85private:
87};
88
89} // namespace Acl
90
91void
93{
94 assert(typeName);
95 assert(*typeName);
96 TheMakers().emplace(typeName, maker);
97}
98
99void
100Acl::SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey)
101{
102 if (!newKey) {
103 throw TextException(ToSBuf("An acl declaration is missing a ", keyParameterName), Here());
104 }
105
106 if (keyStorage.isEmpty()) {
107 keyStorage = newKey;
108 return;
109 }
110
111 if (keyStorage.caseCmp(newKey) == 0)
112 return; // no change
113
114 throw TextException(ToSBuf("Attempt to change the value of the ", keyParameterName, " argument in a subsequent acl declaration:",
115 Debug::Extra, "previously seen value: ", keyStorage,
116 Debug::Extra, "new/conflicting value: ", newKey,
117 Debug::Extra, "advice: Use a dedicated ACL name for each distinct ", keyParameterName,
118 " (and group those ACLs together using an 'any-of' ACL)."),
119 Here());
120}
121
122const SBuf &
124{
125 static const auto none = new SBuf("[no-ACL]");
126 // no value_or() because it would create a new SBuf object here
127 return lastCheckedName ? *lastCheckedName : *none;
128}
129
130/* Acl::ParsingContext */
131
134 return ScopedId("acl");
135}
136
137std::ostream &
139{
140 return os << Debug::Extra << "acl name: " << name_ <<
141 Debug::Extra << "configuration context: " << ConfigParser::CurrentLocation();
142}
143
144/* Acl::Node */
145
146void *
147Acl::Node::operator new (size_t)
148{
149 fatal ("unusable Acl::Node::new");
150 return (void *)1;
151}
152
153void Acl::Node::operator delete(void *)
154{
155 fatal ("unusable Acl::Node::delete");
156}
157
158Acl::Node *
160{
161 if (!Config.namedAcls) {
162 debugs(28, 8, "no named ACLs to find " << name);
163 return nullptr;
164 }
165
166 const auto result = Config.namedAcls->find(name);
167 if (result == Config.namedAcls->end()) {
168 debugs(28, 8, "no ACL named " << name);
169 return nullptr;
170 }
171
172 debugs(28, 8, result->second << " is named " << name);
173 assert(result->second);
174 return result->second.getRaw();
175}
176
178{
179 debugs(28, 8, "constructing, this=" << this);
180}
181
182bool
184{
185 return true;
186}
187
188bool
190{
191 debugs(28, 5, "checking " << name);
192
193 checklist->setLastCheckedName(name);
194
195 int result = 0;
196 if (!checklist->hasAle() && requiresAle()) {
197 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
198 "context without an ALE state. Assuming mismatch.");
199 } else if (!checklist->hasRequest() && requiresRequest()) {
200 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
201 "context without an HTTP request. Assuming mismatch.");
202 } else if (!checklist->hasReply() && requiresReply()) {
203 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
204 "context without an HTTP response. Assuming mismatch.");
205 } else {
206 // make sure the ALE has as much data as possible
207 if (requiresAle())
208 checklist->verifyAle();
209
210 // have to cast because old match() API is missing const
211 result = const_cast<Node*>(this)->match(checklist);
212 }
213
214 const char *extra = checklist->asyncInProgress() ? " async" : "";
215 debugs(28, 3, "checked: " << name << " = " << result << extra);
216 return result == 1; // true for match; false for everything else
217}
218
219void
220Acl::Node::context(const SBuf &aName, const char *aCfgLine)
221{
222 name = aName;
223 safe_free(cfgline);
224 if (aCfgLine)
225 cfgline = xstrdup(aCfgLine);
226}
227
228void
230{
231 /* we're already using strtok() to grok the line */
232 char *t = nullptr;
233
234 /* snarf the ACL name */
235
236 if ((t = ConfigParser::NextToken()) == nullptr) {
237 debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL name.");
238 parser.destruct();
239 return;
240 }
241
242 if (!namedAcls)
243 namedAcls = new NamedAcls();
244
245 SBuf aclname(t);
247 ParseNamed(parser, *namedAcls, aclname);
248 });
249}
250
252void
253Acl::Node::ParseNamed(ConfigParser &parser, NamedAcls &namedAcls, const SBuf &aclname)
254{
255 /* snarf the ACL type */
256 const char *theType;
257
258 if ((theType = ConfigParser::NextToken()) == nullptr) {
259 debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL type.");
260 parser.destruct();
261 return;
262 }
263
264 // Is this ACL going to work?
265 if (strcmp(theType, "myip") == 0) {
267 while (p != nullptr) {
268 // Bug 3239: not reliable when there is interception traffic coming
269 if (p->flags.natIntercept)
270 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
271 p = p->next;
272 }
273 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myip' type has been renamed to 'localip' and matches the IP the client connected to.");
274 theType = "localip";
275 } else if (strcmp(theType, "myport") == 0) {
277 while (p != nullptr) {
278 // Bug 3239: not reliable when there is interception traffic coming
279 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
280 if (p->flags.natIntercept)
281 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
282 p = p->next;
283 }
284 theType = "localport";
285 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myport' type has been renamed to 'localport' and matches the port the client connected to.");
286 } else if (strcmp(theType, "proto") == 0 && aclname.cmp("manager") == 0) {
287 // ACL manager is now a built-in and has a different type.
288 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
289 return; // ignore the line
290 } else if (strcmp(theType, "clientside_mark") == 0) {
291 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'clientside_mark' type has been renamed to 'client_connection_mark'.");
292 theType = "client_connection_mark";
293 }
294
295 auto A = FindByName(aclname);
296 int new_acl = 0;
297 if (!A) {
298 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
299 A = Acl::Make(theType);
300 A->context(aclname, config_input_line);
301 new_acl = 1;
302 } else {
303 if (strcmp (A->typeString(),theType) ) {
304 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
305 parser.destruct();
306 return;
307 }
308
309 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
310 new_acl = 0;
311 }
312
313 A->parseFlags();
314
315 /*split the function here */
316 A->parse();
317
318 if (!new_acl)
319 return;
320
321 if (A->empty()) {
322 debugs(28, DBG_CRITICAL, "WARNING: empty ACL: " << A->cfgline);
323 }
324
325 if (!A->valid()) {
326 fatalf("ERROR: Invalid ACL: %s\n",
327 A->cfgline);
328 }
329
330 const auto insertion = namedAcls.emplace(A->name, A);
331 Assure(insertion.second); // FindByName() above checked that A is a new ACL
332}
333
334void
335Acl::DumpNamedAcls(std::ostream &os, const char * const directiveName, NamedAcls * const namedAcls)
336{
337 if (namedAcls) {
338 for (const auto &nameAndAcl: *namedAcls) {
339 debugs(3, 3, directiveName << ' ' << nameAndAcl.first);
340 nameAndAcl.second->dumpWhole(directiveName, os);
341 }
342 }
343}
344
345void
346Acl::FreeNamedAcls(NamedAcls ** const namedAcls)
347{
348 assert(namedAcls);
349 delete *namedAcls;
350 *namedAcls = nullptr;
351}
352
353bool
355{
356 return false;
357}
358
359void
361{
362 Acl::Options allOptions = options();
363 for (const auto lineOption: lineOptions()) {
364 lineOption->unconfigure(); // forget any previous "acl ..." line effects
365 allOptions.push_back(lineOption);
366 }
367 Acl::ParseFlags(allOptions);
368}
369
370void
371Acl::Node::dumpWhole(const char * const directiveName, std::ostream &os)
372{
373 // XXX: No lineOptions() call here because we do not remember ACL "line"
374 // boundaries and associated "line" options; we cannot report them.
375 os << directiveName << ' ' << name << ' ' << typeString() << options() <<
376 asList(dump()).prefixedBy(" ").delimitedBy(" ") <<
377 '\n';
378}
379
380/* ACL result caching routines */
381
382int
384{
385 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
386 * made for supported acl types */
387 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
388 return 0; /* NOTREACHED */
389}
390
391/*
392 * we lookup an acl's cached results, and if we cannot find the acl being
393 * checked we check it and cache the result. This function is a template
394 * method to support caching of multiple acl types.
395 * Note that caching of time based acl's is not
396 * wise in long lived caches (i.e. the auth_user proxy match cache)
397 * RBC
398 * TODO: does a dlink_list perform well enough? Kinkie
399 */
400int
402{
403 acl_proxy_auth_match_cache *auth_match;
404 dlink_node *link;
405 link = cache->head;
406
407 while (link) {
408 auth_match = (acl_proxy_auth_match_cache *)link->data;
409
410 if (auth_match->acl_data == this) {
411 debugs(28, 4, "cache hit on acl '" << name << "' (" << this << ")");
412 return auth_match->matchrv;
413 }
414
415 link = link->next;
416 }
417
418 auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
419 dlinkAddTail(auth_match, &auth_match->link, cache);
420 debugs(28, 4, "miss for acl '" << name << "'. Adding result " << auth_match->matchrv);
421 return auth_match->matchrv;
422}
423
424void
426{
427 acl_proxy_auth_match_cache *auth_match;
428 dlink_node *link, *tmplink;
429 link = cache->head;
430
431 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
432
433 while (link) {
434 auth_match = (acl_proxy_auth_match_cache *)link->data;
435 tmplink = link;
436 link = link->next;
437 dlinkDelete(tmplink, cache);
438 delete auth_match;
439 }
440}
441
442bool
444{
445 return false;
446}
447
448bool
450{
451 return false;
452}
453
454bool
456{
457 return false;
458}
459
460/*********************/
461/* Destroy functions */
462/*********************/
463
465{
466 debugs(28, 8, "destructing " << name << ", this=" << this);
467 safe_free(cfgline);
468}
469
470void
472{
473 debugs(28, 3, (Config.namedAcls ? Config.namedAcls->size() : 0));
474 if (Config.namedAcls) {
475 for (const auto &nameAndAcl: *Config.namedAcls)
476 nameAndAcl.second->prepareForUse();
477 }
478}
479
#define Assure(condition)
Definition Assure.h:35
void CallParser(const CodeContext::Pointer &parsingContext, Fun &&parse)
#define Here()
source code location of the caller
Definition Here.h:15
auto asList(const Container &c)
a helper to ease AsList object creation
Definition IoManip.h:246
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition PortCfg.cc:22
class SquidConfig Config
#define assert(EX)
Definition assert.h:17
char config_input_line[BUFSIZ]
Definition cache_cf.cc:272
virtual bool hasReply() const =0
void setLastCheckedName(const SBuf &name)
remember the name of the last ACL being evaluated
Definition Checklist.h:128
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition Checklist.h:101
virtual void verifyAle() const =0
warns if there are uninitialized ALE components and fills them
virtual bool hasAle() const =0
virtual bool hasRequest() const =0
const SBuf & lastCheckDescription() const
describes the ACL that was evaluated last while obtaining this answer (for debugging)
Definition Acl.cc:123
std::optional< SBuf > lastCheckedName
the name of the ACL (if any) that was evaluated last while obtaining this answer
Definition Acl.h:105
parsed "acl aclname ..." directives indexed by aclname
Definition Acl.cc:38
virtual bool isProxyAuth() const
Definition Acl.cc:354
virtual int matchForCache(ACLChecklist *checklist)
Definition Acl.cc:383
bool matches(ACLChecklist *checklist) const
Definition Acl.cc:189
virtual bool requiresReply() const
whether our (i.e. shallow) match() requires checklist to have a reply
Definition Acl.cc:449
int cacheMatchAcl(dlink_list *cache, ACLChecklist *)
Definition Acl.cc:401
void dumpWhole(const char *directiveName, std::ostream &)
Definition Acl.cc:371
virtual bool requiresAle() const
whether our (i.e. shallow) match() requires checklist to have a AccessLogEntry
Definition Acl.cc:443
virtual ~Node()
Definition Acl.cc:464
static void ParseNamed(ConfigParser &, NamedAcls &, const SBuf &name)
parses acl directive parts that follow aclname
Definition Acl.cc:253
void context(const SBuf &aName, const char *configuration)
sets user-specified ACL name and squid.conf context
Definition Acl.cc:220
virtual bool valid() const
Definition Acl.cc:183
virtual bool requiresRequest() const
whether our (i.e. shallow) match() requires checklist to have a request
Definition Acl.cc:455
Node()
Definition Acl.cc:177
static void ParseNamedAcl(ConfigParser &, NamedAcls *&)
parses acl directive parts that follow directive name (i.e. "acl")
Definition Acl.cc:229
void parseFlags()
configures Acl::Node options, throwing on configuration errors
Definition Acl.cc:360
static Acl::Node * FindByName(const SBuf &)
A configured ACL with a given name or nil.
Definition Acl.cc:159
static void Initialize()
Definition Acl.cc:471
CodeContext of the being-parsed acl directive.
Definition Acl.cc:75
SBuf name_
the aclname parameter of the being-parsed acl directive
Definition Acl.cc:86
std::ostream & detailCodeContext(std::ostream &os) const override
appends human-friendly context description line(s) to a cache.log record
Definition Acl.cc:138
ScopedId codeContextGist() const override
Definition Acl.cc:133
ParsingContext(const SBuf &name)
Definition Acl.cc:79
Acl::Node type name comparison functor.
Definition Acl.cc:42
bool operator()(TypeName a, TypeName b) const
Definition Acl.cc:44
static SBuf CurrentLocation()
static char * NextToken()
static std::ostream & Extra(std::ostream &)
Definition debug.cc:1316
static auto Make(Args &&... args)
Definition RefCount.h:34
Definition SBuf.h:94
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition SBuf.h:287
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition SBuf.h:279
bool isEmpty() const
Definition SBuf.h:435
Acl::NamedAcls * namedAcls
acl aclname acltype ...
an std::runtime_error with thrower location info
#define DBG_PARSE_NOTE(x)
Definition Stream.h:42
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
void fatal(const char *message)
Definition fatal.cc:28
void fatalf(const char *fmt,...)
Definition fatal.cc:68
void aclCacheMatchFlush(dlink_list *cache)
Definition Acl.cc:425
Definition Acl.cc:33
void RegisterMaker(TypeName typeName, Maker maker)
use the given Acl::Node Maker for all ACLs of the named type
Definition Acl.cc:92
static Makers & TheMakers()
registered Acl::Node Makers
Definition Acl.cc:52
static Acl::Node * Make(TypeName typeName)
creates an Acl::Node object of the named (and already registered) Node child type
Definition Acl.cc:61
void DumpNamedAcls(std::ostream &, const char *directiveName, NamedAcls *)
report the given list of "acl" directives (using squid.conf syntax)
Definition Acl.cc:335
Node *(*)(TypeName typeName) Maker
a "factory" function for making Acl::Node objects (of some Node child type)
Definition Acl.h:26
const char * TypeName
the ACL type name known to admins
Definition Acl.h:24
void ParseFlags(const Options &options)
Definition Options.cc:227
void SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey)
Definition Acl.cc:100
void FreeNamedAcls(NamedAcls **)
delete the given list of "acl" directives
Definition Acl.cc:346
std::map< TypeName, Maker, TypeNameCmp > Makers
Acl::Node makers indexed by Node type name.
Definition Acl.cc:48
std::vector< const Option * > Options
Definition Options.h:217
#define xstrdup
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
int const char size_t
#define safe_free(x)
Definition xalloc.h:73