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 "cache_cf.h"
18#include "ConfigParser.h"
19#include "debug/Stream.h"
20#include "fatal.h"
21#include "globals.h"
22#include "sbuf/List.h"
23#include "sbuf/Stream.h"
24#include "SquidConfig.h"
25
26#include <algorithm>
27#include <map>
28
29const char *AclMatchedName = nullptr;
30
31namespace Acl {
32
35public:
36 bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
37};
38
40typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
41
43static Makers &
45{
46 static Makers Registry;
47 return Registry;
48}
49
51static
52ACL *
53Make(TypeName typeName)
54{
55 const auto pos = TheMakers().find(typeName);
56 if (pos == TheMakers().end()) {
57 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << typeName << "'");
59 assert(false); // not reached
60 }
61
62 ACL *result = (pos->second)(pos->first);
63 debugs(28, 4, typeName << '=' << result);
64 assert(result);
65 return result;
66}
67
68} // namespace Acl
69
70void
72{
73 assert(typeName);
74 assert(*typeName);
75 TheMakers().emplace(typeName, maker);
76}
77
78void
79Acl::SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey)
80{
81 if (!newKey) {
82 throw TextException(ToSBuf("An acl declaration is missing a ", keyParameterName,
83 Debug::Extra, "ACL name: ", AclMatchedName),
84 Here());
85 }
86
87 if (keyStorage.isEmpty()) {
88 keyStorage = newKey;
89 return;
90 }
91
92 if (keyStorage.caseCmp(newKey) == 0)
93 return; // no change
94
95 throw TextException(ToSBuf("Attempt to change the value of the ", keyParameterName, " argument in a subsequent acl declaration:",
96 Debug::Extra, "previously seen value: ", keyStorage,
97 Debug::Extra, "new/conflicting value: ", newKey,
98 Debug::Extra, "ACL name: ", AclMatchedName,
99 Debug::Extra, "advice: Use a dedicated ACL name for each distinct ", keyParameterName,
100 " (and group those ACLs together using an 'any-of' ACL)."),
101 Here());
102}
103
104void *
105ACL::operator new (size_t)
106{
107 fatal ("unusable ACL::new");
108 return (void *)1;
109}
110
111void
112ACL::operator delete (void *)
113{
114 fatal ("unusable ACL::delete");
115}
116
117ACL *
118ACL::FindByName(const char *name)
119{
120 ACL *a;
121 debugs(28, 9, "ACL::FindByName '" << name << "'");
122
123 for (a = Config.aclList; a; a = a->next)
124 if (!strcasecmp(a->name, name))
125 return a;
126
127 debugs(28, 9, "ACL::FindByName found no match");
128
129 return nullptr;
130}
131
133 cfgline(nullptr),
134 next(nullptr),
135 registered(false)
136{
137 *name = 0;
138}
139
140bool ACL::valid () const
141{
142 return true;
143}
144
145bool
146ACL::matches(ACLChecklist *checklist) const
147{
148 debugs(28, 5, "checking " << name);
149
150 // XXX: AclMatchedName does not contain a matched ACL name when the acl
151 // does not match. It contains the last (usually leaf) ACL name checked
152 // (or is NULL if no ACLs were checked).
154
155 int result = 0;
156 if (!checklist->hasAle() && requiresAle()) {
157 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
158 "context without an ALE state. Assuming mismatch.");
159 } else if (!checklist->hasRequest() && requiresRequest()) {
160 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
161 "context without an HTTP request. Assuming mismatch.");
162 } else if (!checklist->hasReply() && requiresReply()) {
163 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
164 "context without an HTTP response. Assuming mismatch.");
165 } else {
166 // make sure the ALE has as much data as possible
167 if (requiresAle())
168 checklist->verifyAle();
169
170 // have to cast because old match() API is missing const
171 result = const_cast<ACL*>(this)->match(checklist);
172 }
173
174 const char *extra = checklist->asyncInProgress() ? " async" : "";
175 debugs(28, 3, "checked: " << name << " = " << result << extra);
176 return result == 1; // true for match; false for everything else
177}
178
179void
180ACL::context(const char *aName, const char *aCfgLine)
181{
182 name[0] = '\0';
183 if (aName)
184 xstrncpy(name, aName, ACL_NAME_SZ-1);
186 if (aCfgLine)
187 cfgline = xstrdup(aCfgLine);
188}
189
190void
192{
193 /* we're already using strtok() to grok the line */
194 char *t = nullptr;
195 ACL *A = nullptr;
196 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
197 int new_acl = 0;
198
199 /* snarf the ACL name */
200
201 if ((t = ConfigParser::NextToken()) == nullptr) {
202 debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL name.");
203 parser.destruct();
204 return;
205 }
206
207 if (strlen(t) >= ACL_NAME_SZ) {
208 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
209 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
210 parser.destruct();
211 return;
212 }
213
214 xstrncpy(aclname, t, ACL_NAME_SZ);
215 /* snarf the ACL type */
216 const char *theType;
217
218 if ((theType = ConfigParser::NextToken()) == nullptr) {
219 debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL type.");
220 parser.destruct();
221 return;
222 }
223
224 // Is this ACL going to work?
225 if (strcmp(theType, "myip") == 0) {
227 while (p != nullptr) {
228 // Bug 3239: not reliable when there is interception traffic coming
229 if (p->flags.natIntercept)
230 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
231 p = p->next;
232 }
233 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myip' type has been renamed to 'localip' and matches the IP the client connected to.");
234 theType = "localip";
235 } else if (strcmp(theType, "myport") == 0) {
237 while (p != nullptr) {
238 // Bug 3239: not reliable when there is interception traffic coming
239 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
240 if (p->flags.natIntercept)
241 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
242 p = p->next;
243 }
244 theType = "localport";
245 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myport' type has been renamed to 'localport' and matches the port the client connected to.");
246 } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
247 // ACL manager is now a built-in and has a different type.
248 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
249 return; // ignore the line
250 } else if (strcmp(theType, "clientside_mark") == 0) {
251 debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'clientside_mark' type has been renamed to 'client_connection_mark'.");
252 theType = "client_connection_mark";
253 }
254
255 if ((A = FindByName(aclname)) == nullptr) {
256 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
257 A = Acl::Make(theType);
258 A->context(aclname, config_input_line);
259 new_acl = 1;
260 } else {
261 if (strcmp (A->typeString(),theType) ) {
262 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
263 parser.destruct();
264 return;
265 }
266
267 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
268 new_acl = 0;
269 }
270
271 /*
272 * Here we set AclMatchedName in case we need to use it in a
273 * warning message in aclDomainCompare().
274 */
275 AclMatchedName = A->name; /* ugly */
276
277 A->parseFlags();
278
279 /*split the function here */
280 A->parse();
281
282 /*
283 * Clear AclMatchedName from our temporary hack
284 */
285 AclMatchedName = nullptr; /* ugly */
286
287 if (!new_acl)
288 return;
289
290 if (A->empty()) {
291 debugs(28, DBG_CRITICAL, "WARNING: empty ACL: " << A->cfgline);
292 }
293
294 if (!A->valid()) {
295 fatalf("ERROR: Invalid ACL: %s\n",
296 A->cfgline);
297 }
298
299 // add to the global list for searching explicit ACLs by name
300 assert(head && *head == Config.aclList);
301 A->next = *head;
302 *head = A;
303
304 // register for centralized cleanup
305 aclRegister(A);
306}
307
308bool
310{
311 return false;
312}
313
314void
316{
317 Acl::Options allOptions = options();
318 for (const auto lineOption: lineOptions()) {
319 lineOption->unconfigure(); // forget any previous "acl ..." line effects
320 allOptions.push_back(lineOption);
321 }
322 Acl::ParseFlags(allOptions);
323}
324
327{
328 SBufList result;
329
330 const auto &myOptions = options();
331 // XXX: No lineOptions() call here because we do not remember ACL "line"
332 // boundaries and associated "line" options; we cannot report them.
333
334 // optimization: most ACLs do not have myOptions
335 // this check also works around dump_SBufList() adding ' ' after empty items
336 if (!myOptions.empty()) {
337 SBufStream stream;
338 stream << myOptions;
339 const SBuf optionsImage = stream.buf();
340 if (!optionsImage.isEmpty())
341 result.push_back(optionsImage);
342 }
343 return result;
344}
345
346/* ACL result caching routines */
347
348int
350{
351 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
352 * made for supported acl types */
353 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
354 return 0; /* NOTREACHED */
355}
356
357/*
358 * we lookup an acl's cached results, and if we cannot find the acl being
359 * checked we check it and cache the result. This function is a template
360 * method to support caching of multiple acl types.
361 * Note that caching of time based acl's is not
362 * wise in long lived caches (i.e. the auth_user proxy match cache)
363 * RBC
364 * TODO: does a dlink_list perform well enough? Kinkie
365 */
366int
368{
369 acl_proxy_auth_match_cache *auth_match;
370 dlink_node *link;
371 link = cache->head;
372
373 while (link) {
374 auth_match = (acl_proxy_auth_match_cache *)link->data;
375
376 if (auth_match->acl_data == this) {
377 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
378 return auth_match->matchrv;
379 }
380
381 link = link->next;
382 }
383
384 auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
385 dlinkAddTail(auth_match, &auth_match->link, cache);
386 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
387 return auth_match->matchrv;
388}
389
390void
392{
393 acl_proxy_auth_match_cache *auth_match;
394 dlink_node *link, *tmplink;
395 link = cache->head;
396
397 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
398
399 while (link) {
400 auth_match = (acl_proxy_auth_match_cache *)link->data;
401 tmplink = link;
402 link = link->next;
403 dlinkDelete(tmplink, cache);
404 delete auth_match;
405 }
406}
407
408bool
410{
411 return false;
412}
413
414bool
416{
417 return false;
418}
419
420bool
422{
423 return false;
424}
425
426/*********************/
427/* Destroy functions */
428/*********************/
429
431{
432 debugs(28, 3, "freeing ACL " << name);
434 AclMatchedName = nullptr; // in case it was pointing to our name
435}
436
437void
439{
440 ACL *a = Config.aclList;
441 debugs(53, 3, "ACL::Initialize");
442
443 while (a) {
444 a->prepareForUse();
445 a = a->next;
446 }
447}
448
#define Here()
source code location of the caller
Definition: Here.h:15
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
class SquidConfig Config
Definition: SquidConfig.cc:12
#define ACL_NAME_SZ
Definition: forward.h:41
squidaio_request_t * head
Definition: aiops.cc:127
#define assert(EX)
Definition: assert.h:17
char config_input_line[BUFSIZ]
Definition: cache_cf.cc:274
void self_destruct(void)
Definition: cache_cf.cc:277
virtual bool hasReply() const =0
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition: Checklist.h:151
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
Definition: Acl.h:46
ACL()
Definition: Acl.cc:132
virtual bool valid() const
Definition: Acl.cc:140
virtual bool requiresAle() const
whether our (i.e. shallow) match() requires checklist to have a AccessLogEntry
Definition: Acl.cc:409
virtual bool requiresRequest() const
whether our (i.e. shallow) match() requires checklist to have a request
Definition: Acl.cc:421
int cacheMatchAcl(dlink_list *cache, ACLChecklist *)
Definition: Acl.cc:367
char * cfgline
Definition: Acl.h:88
virtual int match(ACLChecklist *checklist)=0
Matches the actual data in checklist against this ACL.
void context(const char *name, const char *configuration)
sets user-specified ACL name and squid.conf context
Definition: Acl.cc:180
virtual int matchForCache(ACLChecklist *checklist)
Definition: Acl.cc:349
virtual bool requiresReply() const
whether our (i.e. shallow) match() requires checklist to have a reply
Definition: Acl.cc:415
static ACL * FindByName(const char *name)
Definition: Acl.cc:118
bool matches(ACLChecklist *checklist) const
Definition: Acl.cc:146
char name[ACL_NAME_SZ]
Definition: Acl.h:87
ACL * next
Definition: Acl.h:89
static void Initialize()
Definition: Acl.cc:438
virtual const Acl::Options & lineOptions()
Definition: Acl.h:109
SBufList dumpOptions()
Definition: Acl.cc:326
virtual void prepareForUse()
Definition: Acl.h:83
void parseFlags()
configures ACL options, throwing on configuration errors
Definition: Acl.cc:315
virtual ~ACL()
Definition: Acl.cc:430
virtual bool isProxyAuth() const
Definition: Acl.cc:309
virtual const Acl::Options & options()
Definition: Acl.h:105
static void ParseAclLine(ConfigParser &parser, ACL **head)
Definition: Acl.cc:191
ACL type name comparison functor.
Definition: Acl.cc:34
bool operator()(TypeName a, TypeName b) const
Definition: Acl.cc:36
static char * NextToken()
void destruct()
Definition: ConfigParser.cc:38
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1313
SBuf buf()
bytes written so far
Definition: Stream.h:41
Definition: SBuf.h:94
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition: SBuf.h:283
bool isEmpty() const
Definition: SBuf.h:431
class ACL * aclList
Definition: SquidConfig.h:356
an std::runtime_error with thrower location info
Definition: TextException.h:21
#define DBG_PARSE_NOTE(x)
Definition: Stream.h:42
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#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:391
void aclRegister(ACL *acl)
Definition: Gadgets.cc:221
const char * AclMatchedName
Definition: Acl.cc:29
static uint32 A
Definition: md4.c:43
Definition: Acl.cc:31
void RegisterMaker(TypeName typeName, Maker maker)
use the given ACL Maker for all ACLs of the named type
Definition: Acl.cc:71
const char * TypeName
the ACL type name known to admins
Definition: Acl.h:27
static Makers & TheMakers()
registered ACL Makers
Definition: Acl.cc:44
static ACL * Make(TypeName typeName)
creates an ACL object of the named (and already registered) ACL child type
Definition: Acl.cc:53
ACL *(* Maker)(TypeName typeName)
a "factory" function for making ACL objects (of some ACL child type)
Definition: Acl.h:29
void ParseFlags(const Options &options)
Definition: Options.cc:227
void SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey)
Definition: Acl.cc:79
std::map< TypeName, Maker, TypeNameCmp > Makers
ACL makers indexed by ACL type name.
Definition: Acl.cc:40
std::vector< const Option * > Options
Definition: Options.h:214
#define xstrdup
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
std::list< SBuf > SBufList
Definition: forward.h:23
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:68
int const char size_t
Definition: stub_liblog.cc:83
#define safe_free(x)
Definition: xalloc.h:73
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors