Acl.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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"
23 #include "mem/PoolingAllocator.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 
33 namespace Acl {
34 
36 class NamedAcls: public std::unordered_map<SBuf, Acl::Node::Pointer,
37  CaseInsensitiveSBufHash, CaseInsensitiveSBufEqual,
38  PoolingAllocator< std::pair<const SBuf, Acl::Node::Pointer> > > {
39 };
40 
42 class TypeNameCmp {
43 public:
44  bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
45 };
46 
48 typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
49 
51 static Makers &
53 {
54  static Makers Registry;
55  return Registry;
56 }
57 
59 static
60 Acl::Node *
61 Make(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 {
76 public:
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 
85 private:
87 };
88 
89 } // namespace Acl
90 
91 void
93 {
94  assert(typeName);
95  assert(*typeName);
96  TheMakers().emplace(typeName, maker);
97 }
98 
99 void
100 Acl::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 
122 const 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 
132 ScopedId
134  return ScopedId("acl");
135 }
136 
137 std::ostream &
139 {
140  return os << Debug::Extra << "acl name: " << name_ <<
141  Debug::Extra << "configuration context: " << ConfigParser::CurrentLocation();
142 }
143 
144 /* Acl::Node */
145 
146 void *
147 Acl::Node::operator new (size_t)
148 {
149  fatal ("unusable Acl::Node::new");
150  return (void *)1;
151 }
152 
153 void Acl::Node::operator delete(void *)
154 {
155  fatal ("unusable Acl::Node::delete");
156 }
157 
158 Acl::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 
182 bool
184 {
185  return true;
186 }
187 
188 bool
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 
219 void
220 Acl::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 
228 void
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 
252 void
253 Acl::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 
334 void
335 Acl::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 
345 void
346 Acl::FreeNamedAcls(NamedAcls ** const namedAcls)
347 {
348  assert(namedAcls);
349  delete *namedAcls;
350  *namedAcls = nullptr;
351 }
352 
353 bool
355 {
356  return false;
357 }
358 
359 void
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 
370 void
371 Acl::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 
382 int
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  */
400 int
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 
424 void
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 
442 bool
444 {
445  return false;
446 }
447 
448 bool
450 {
451  return false;
452 }
453 
454 bool
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 
470 void
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 
void fatal(const char *message)
Definition: fatal.cc:28
virtual int matchForCache(ACLChecklist *checklist)
Definition: Acl.cc:383
static auto Make(Args &&... args)
Definition: RefCount.h:34
#define Here()
source code location of the caller
Definition: Here.h:15
static SBuf CurrentLocation()
void DumpNamedAcls(std::ostream &, const char *directiveName, NamedAcls *)
report the given list of "acl" directives (using squid.conf syntax)
Definition: Acl.cc:335
#define DBG_CRITICAL
Definition: Stream.h:37
virtual bool requiresAle() const
whether our (i.e. shallow) match() requires checklist to have a AccessLogEntry
Definition: Acl.cc:443
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
virtual void verifyAle() const =0
warns if there are uninitialized ALE components and fills them
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
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
bool isEmpty() const
Definition: SBuf.h:435
const SBuf & lastCheckDescription() const
describes the ACL that was evaluated last while obtaining this answer (for debugging)
Definition: Acl.cc:123
Node *(*)(TypeName typeName) Maker
a "factory" function for making Acl::Node objects (of some Node child type)
Definition: Acl.h:26
virtual bool requiresRequest() const
whether our (i.e. shallow) match() requires checklist to have a request
Definition: Acl.cc:455
Definition: SBuf.h:93
#define xstrdup
void FreeNamedAcls(NamedAcls **)
delete the given list of "acl" directives
Definition: Acl.cc:346
Definition: Acl.cc:33
void SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey)
Definition: Acl.cc:100
CodeContext of the being-parsed acl directive.
Definition: Acl.cc:74
virtual bool requiresReply() const
whether our (i.e. shallow) match() requires checklist to have a reply
Definition: Acl.cc:449
void context(const SBuf &aName, const char *configuration)
sets user-specified ACL name and squid.conf context
Definition: Acl.cc:220
virtual bool hasAle() const =0
static void ParseNamedAcl(ConfigParser &, NamedAcls *&)
parses acl directive parts that follow directive name (i.e. "acl")
Definition: Acl.cc:229
void dumpWhole(const char *directiveName, std::ostream &)
Definition: Acl.cc:371
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition: Checklist.h:101
parsed "acl aclname ..." directives indexed by aclname
Definition: Acl.cc:36
int const char size_t
Definition: stub_liblog.cc:83
virtual bool valid() const
Definition: Acl.cc:183
void setLastCheckedName(const SBuf &name)
remember the name of the last ACL being evaluated
Definition: Checklist.h:128
void CallParser(const CodeContext::Pointer &parsingContext, Fun &&parse)
Definition: CodeContext.h:138
void ParseFlags(const Options &options)
Definition: Options.cc:227
#define DBG_PARSE_NOTE(x)
Definition: Stream.h:42
static void ParseNamed(ConfigParser &, NamedAcls &, const SBuf &name)
parses acl directive parts that follow aclname
Definition: Acl.cc:253
static Acl::Node * FindByName(const SBuf &)
A configured ACL with a given name or nil.
Definition: Acl.cc:159
static uint32 A
Definition: md4.c:43
ParsingContext(const SBuf &name)
Definition: Acl.cc:79
virtual bool hasRequest() const =0
void destruct()
Definition: ConfigParser.cc:38
#define safe_free(x)
Definition: xalloc.h:73
#define assert(EX)
Definition: assert.h:17
Acl::Node type name comparison functor.
Definition: Acl.cc:42
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
void aclCacheMatchFlush(dlink_list *cache)
Definition: Acl.cc:425
#define Assure(condition)
Definition: Assure.h:35
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1316
static char * NextToken()
virtual bool hasReply() const =0
void parseFlags()
configures Acl::Node options, throwing on configuration errors
Definition: Acl.cc:360
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:279
Definition: Node.h:25
an std::runtime_error with thrower location info
Definition: TextException.h:20
int cacheMatchAcl(dlink_list *cache, ACLChecklist *)
Definition: Acl.cc:401
const char * TypeName
the ACL type name known to admins
Definition: Acl.h:24
Acl::NamedAcls * namedAcls
acl aclname acltype ...
Definition: SquidConfig.h:356
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
auto asList(const Container &c)
a helper to ease AsList object creation
Definition: IoManip.h:246
#define DBG_IMPORTANT
Definition: Stream.h:38
bool operator()(TypeName a, TypeName b) const
Definition: Acl.cc:44
std::optional< SBuf > lastCheckedName
the name of the ACL (if any) that was evaluated last while obtaining this answer
Definition: Acl.h:105
SBuf name_
the aclname parameter of the being-parsed acl directive
Definition: Acl.cc:86
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition: SBuf.h:287
std::ostream & detailCodeContext(std::ostream &os) const override
appends human-friendly context description line(s) to a cache.log record
Definition: Acl.cc:138
virtual ~Node()
Definition: Acl.cc:464
Node()
Definition: Acl.cc:177
char config_input_line[BUFSIZ]
Definition: cache_cf.cc:273
static void Initialize()
Definition: Acl.cc:471
ScopedId codeContextGist() const override
Definition: Acl.cc:133
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
bool matches(ACLChecklist *checklist) const
Definition: Acl.cc:189
class SquidConfig Config
Definition: SquidConfig.cc:12
virtual bool isProxyAuth() const
Definition: Acl.cc:354

 

Introduction

Documentation

Support

Miscellaneous