]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/Acl.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / acl / Acl.cc
CommitLineData
8213067d 1/*
ef57eb7b 2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
8213067d 7 */
bbc27441
AJ
8
9/* DEBUG: section 28 Access Control */
10
f7f3304a 11#include "squid.h"
c15d448c
AR
12#include "acl/Acl.h"
13#include "acl/Checklist.h"
ed898bdf 14#include "acl/Gadgets.h"
582c2af2 15#include "anyp/PortCfg.h"
33810b1d 16#include "cache_cf.h"
d295d770 17#include "ConfigParser.h"
38dedeed 18#include "Debug.h"
e1f7507e 19#include "dlink.h"
ed6e9fb9 20#include "fatal.h"
582c2af2 21#include "globals.h"
6f58d7d7 22#include "profiler/Profiler.h"
4d5904f7 23#include "SquidConfig.h"
23351cb2 24
81481ec0
FC
25#include <vector>
26
76ee67ac
CT
27#define abortFlags(CONTENT) \
28 do { \
29 debugs(28, 0, CONTENT); \
30 self_destruct(); \
31 } while (0)
32
33810b1d
CT
33const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
34
b0dd28ba 35const char *AclMatchedName = NULL;
36
76ee67ac
CT
37ACLFlags::FlagsTokenizer::FlagsTokenizer(): tokPos(NULL) { }
38
39ACLFlag
40ACLFlags::FlagsTokenizer::nextFlag()
41{
42 if (needNextToken()) {
43 if (!nextToken())
44 return 0;
45 } else
46 ++tokPos;
47 return *tokPos;
48}
49
50bool
51ACLFlags::FlagsTokenizer::hasParameter() const
52{
53 return tokPos && tokPos[0] && tokPos[1] == '=' && tokPos[2];
54}
55
56SBuf
57ACLFlags::FlagsTokenizer::getParameter() const
58{
59 return hasParameter() ? SBuf(&tokPos[2]) : SBuf();
60}
61
62bool
63ACLFlags::FlagsTokenizer::needNextToken() const
64{
65 return !tokPos || !tokPos[0] || !tokPos[1] || tokPos[1] == '=';
66}
67
68bool
69ACLFlags::FlagsTokenizer::nextToken()
70{
71 char *t = ConfigParser::PeekAtToken();
72 if (t == NULL || t[0] != '-' || !t[1])
73 return false;
74 (void)ConfigParser::NextQuotedToken();
75 if (strcmp(t, "--") == 0)
76 return false;
77 tokPos = t + 1;
78 return true;
79}
80
81ACLFlags::~ACLFlags()
82{
83 delete delimiters_;
84}
85
86ACLFlags::Status
87ACLFlags::flagStatus(const ACLFlag f) const
33810b1d
CT
88{
89 if (f == ACL_F_REGEX_CASE)
76ee67ac
CT
90 return noParameter;
91 if (f == ACL_F_SUBSTRING)
92 return parameterOptional;
93 if (supported_.find(f) != std::string::npos)
94 return noParameter;
95 return notSupported;
96}
97
98bool
99ACLFlags::parameterSupported(const ACLFlag f, const SBuf &val) const
100{
101 if (f == ACL_F_SUBSTRING)
102 return val.findFirstOf(CharacterSet::ALPHA + CharacterSet::DIGIT) == SBuf::npos;
103 return true;
104}
105
106void
107ACLFlags::makeSet(const ACLFlag f, const SBuf &param)
108{
109 flags_ |= flagToInt(f);
110 if (!param.isEmpty())
111 flagParameters_[f].append(param);
112}
113
114void
115ACLFlags::makeUnSet(const ACLFlag f)
116{
5269fa82
SM
117 flags_ &= ~flagToInt(f);
118 flagParameters_[f].clear();
33810b1d
CT
119}
120
aec45181 121void
788542bd 122ACLFlags::parseFlags()
33810b1d 123{
76ee67ac
CT
124 FlagsTokenizer tokenizer;
125 ACLFlag flag('\0');
126 while ((flag = tokenizer.nextFlag())) {
127 switch (flagStatus(flag))
128 {
5269fa82
SM
129 case notSupported:
130 abortFlags("Flag '" << flag << "' not supported");
131 break;
132 case noParameter:
133 makeSet(flag);
134 break;
135 case parameterRequired:
136 if (!tokenizer.hasParameter()) {
137 abortFlags("Flag '" << flag << "' must have a parameter");
76ee67ac 138 break;
5269fa82
SM
139 }
140 case parameterOptional:
141 SBuf param;
142 if (tokenizer.hasParameter()) {
143 param = tokenizer.getParameter();
144 if (!parameterSupported(flag, param))
145 abortFlags("Parameter '" << param << "' for flag '" << flag << "' not supported");
146 }
147 makeSet(flag, param);
148 break;
33810b1d
CT
149 }
150 }
151
152 /*Regex code needs to parse -i file*/
702240e4 153 if ( isSet(ACL_F_REGEX_CASE)) {
2eceb328 154 ConfigParser::TokenPutBack("-i");
702240e4
FC
155 makeUnSet('i');
156 }
33810b1d
CT
157}
158
76ee67ac
CT
159SBuf
160ACLFlags::parameter(const ACLFlag f) const
161{
162 assert(static_cast<uint32_t>(f - 'A') < FlagIndexMax);
163 auto p = flagParameters_.find(f);
164 return p == flagParameters_.end() ? SBuf() : p->second;
165}
166
167const CharacterSet *
168ACLFlags::delimiters()
169{
170 if (isSet(ACL_F_SUBSTRING) && !delimiters_) {
171 static const SBuf defaultParameter(",");
172 SBuf rawParameter = parameter(ACL_F_SUBSTRING);
173 if (rawParameter.isEmpty())
174 rawParameter = defaultParameter;
175 delimiters_ = new CharacterSet("ACLFlags::delimiters", rawParameter.c_str());
176 }
177 return delimiters_;
178}
179
33810b1d
CT
180const char *
181ACLFlags::flagsStr() const
182{
183 static char buf[64];
184 if (flags_ == 0)
185 return "";
186
187 char *s = buf;
188 *s++ = '-';
189 for (ACLFlag f = 'A'; f <= 'z'; f++) {
190 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
191 if (isSet(f) && f != ACL_F_REGEX_CASE)
192 *s++ = f;
193 }
194 *s = '\0';
195 return buf;
196}
197
8000a965 198void *
ced8def3 199ACL::operator new (size_t)
8000a965 200{
b0dd28ba 201 fatal ("unusable ACL::new");
202 return (void *)1;
8000a965 203}
204
205void
ced8def3 206ACL::operator delete (void *)
8000a965 207{
b0dd28ba 208 fatal ("unusable ACL::delete");
56b63fa1 209}
210
97427e90 211ACL *
225b7b10 212ACL::FindByName(const char *name)
7dd57fa2 213{
97427e90 214 ACL *a;
bf8fe701 215 debugs(28, 9, "ACL::FindByName '" << name << "'");
62e76326 216
9bea1d5b 217 for (a = Config.aclList; a; a = a->next)
62e76326 218 if (!strcasecmp(a->name, name))
219 return a;
220
bf8fe701 221 debugs(28, 9, "ACL::FindByName found no match");
6bf4f823 222
9bea1d5b 223 return NULL;
7dd57fa2 224}
225
8000a965 226ACL *
227ACL::Factory (char const *type)
228{
229 ACL *result = Prototype::Factory (type);
62e76326 230
b0dd28ba 231 if (!result)
62e76326 232 fatal ("Unknown acl type in ACL::Factory");
62e76326 233
8000a965 234 return result;
235}
236
478a0611 237ACL::ACL() :
d59e4742
FC
238 cfgline(nullptr),
239 next(nullptr),
240 registered(false)
241{
242 *name = 0;
243}
244
245ACL::ACL(const ACLFlag flgs[]) :
f53969cc
SM
246 cfgline(NULL),
247 next(NULL),
d59e4742 248 flags(flgs),
f53969cc 249 registered(false)
478a0611
AJ
250{
251 *name = 0;
252}
8000a965 253
4b0f5de8 254bool ACL::valid () const
255{
256 return true;
257}
258
6f58d7d7
AR
259bool
260ACL::matches(ACLChecklist *checklist) const
261{
262 PROF_start(ACL_matches);
263 debugs(28, 5, "checking " << name);
264
265 // XXX: AclMatchedName does not contain a matched ACL name when the acl
266 // does not match. It contains the last (usually leaf) ACL name checked
267 // (or is NULL if no ACLs were checked).
268 AclMatchedName = name;
269
270 int result = 0;
4ff6370b 271 if (!checklist->hasAle() && requiresAle()) {
4e56d7f6 272 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
acfe8e09 273 "context without an ALE state. Assuming mismatch.");
4e56d7f6 274 } else if (!checklist->hasRequest() && requiresRequest()) {
6f58d7d7
AR
275 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
276 "context without an HTTP request. Assuming mismatch.");
277 } else if (!checklist->hasReply() && requiresReply()) {
278 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
279 "context without an HTTP response. Assuming mismatch.");
280 } else {
fbbea662
AJ
281 // make sure the ALE has as much data as possible
282 if (requiresAle())
283 checklist->syncAle();
284
6f58d7d7
AR
285 // have to cast because old match() API is missing const
286 result = const_cast<ACL*>(this)->match(checklist);
287 }
288
289 const char *extra = checklist->asyncInProgress() ? " async" : "";
290 debugs(28, 3, "checked: " << name << " = " << result << extra);
291 PROF_stop(ACL_matches);
292 return result == 1; // true for match; false for everything else
293}
294
295void
296ACL::context(const char *aName, const char *aCfgLine)
297{
298 name[0] = '\0';
299 if (aName)
300 xstrncpy(name, aName, ACL_NAME_SZ-1);
301 safe_free(cfgline);
302 if (aCfgLine)
303 cfgline = xstrdup(aCfgLine);
304}
305
8203a132 306void
a9f20260 307ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
9bea1d5b 308{
309 /* we're already using strtok() to grok the line */
310 char *t = NULL;
97427e90 311 ACL *A = NULL;
9bea1d5b 312 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
9bea1d5b 313 int new_acl = 0;
314
315 /* snarf the ACL name */
62e76326 316
2eceb328 317 if ((t = ConfigParser::NextToken()) == NULL) {
fa84c01d 318 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
a9f20260 319 parser.destruct();
62e76326 320 return;
9bea1d5b 321 }
62e76326 322
401f503a 323 if (strlen(t) >= ACL_NAME_SZ) {
fa84c01d 324 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
bf8fe701 325 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
401f503a 326 parser.destruct();
327 return;
328 }
329
9bea1d5b 330 xstrncpy(aclname, t, ACL_NAME_SZ);
331 /* snarf the ACL type */
38dedeed 332 const char *theType;
62e76326 333
2eceb328 334 if ((theType = ConfigParser::NextToken()) == NULL) {
fa84c01d 335 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
a9f20260 336 parser.destruct();
62e76326 337 return;
9bea1d5b 338 }
62e76326 339
8306968f 340 // Is this ACL going to work?
18c41901 341 if (strcmp(theType, "myip") == 0) {
fa720bfb
AJ
342 AnyP::PortCfgPointer p = HttpPortList;
343 while (p != NULL) {
8306968f 344 // Bug 3239: not reliable when there is interception traffic coming
6a25a046 345 if (p->flags.natIntercept)
8306968f
AJ
346 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
347 p = p->next;
348 }
38dedeed 349 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
1e40905d 350 theType = "localip";
18c41901 351 } else if (strcmp(theType, "myport") == 0) {
fa720bfb
AJ
352 AnyP::PortCfgPointer p = HttpPortList;
353 while (p != NULL) {
8306968f
AJ
354 // Bug 3239: not reliable when there is interception traffic coming
355 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
6a25a046 356 if (p->flags.natIntercept)
8306968f
AJ
357 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
358 p = p->next;
359 }
1e40905d 360 theType = "localport";
38dedeed 361 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
49549cd1
AJ
362 } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
363 // ACL manager is now a built-in and has a different type.
364 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
365 return; // ignore the line
1e40905d
AJ
366 }
367
368 if (!Prototype::Registered(theType)) {
369 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
370 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
371 parser.destruct();
372 return;
8306968f
AJ
373 }
374
225b7b10 375 if ((A = FindByName(aclname)) == NULL) {
bf8fe701 376 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
b0dd28ba 377 A = ACL::Factory(theType);
6f58d7d7 378 A->context(aclname, config_input_line);
62e76326 379 new_acl = 1;
9bea1d5b 380 } else {
b0dd28ba 381 if (strcmp (A->typeString(),theType) ) {
fa84c01d 382 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
a9f20260 383 parser.destruct();
62e76326 384 return;
385 }
386
bf8fe701 387 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
62e76326 388 new_acl = 0;
9bea1d5b 389 }
62e76326 390
9bea1d5b 391 /*
392 * Here we set AclMatchedName in case we need to use it in a
393 * warning message in aclDomainCompare().
394 */
f53969cc 395 AclMatchedName = A->name; /* ugly */
8000a965 396
788542bd 397 A->flags.parseFlags();
33810b1d 398
8000a965 399 /*split the function here */
400 A->parse();
62e76326 401
8000a965 402 /*
403 * Clear AclMatchedName from our temporary hack
404 */
f53969cc 405 AclMatchedName = NULL; /* ugly */
62e76326 406
8000a965 407 if (!new_acl)
62e76326 408 return;
409
4b0f5de8 410 if (A->empty()) {
fa84c01d 411 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
4b0f5de8 412 }
413
414 if (!A->valid()) {
415 fatalf("ERROR: Invalid ACL: %s\n",
416 A->cfgline);
8000a965 417 }
62e76326 418
ed898bdf 419 // add to the global list for searching explicit ACLs by name
6f58d7d7 420 assert(head && *head == Config.aclList);
a57c224d 421 A->next = *head;
8000a965 422 *head = A;
ed898bdf
AR
423
424 // register for centralized cleanup
425 aclRegister(A);
8000a965 426}
427
8000a965 428bool
429ACL::isProxyAuth() const
430{
225b7b10 431 return false;
8000a965 432}
1cfdbcf0 433
94439e4e 434/* ACL result caching routines */
435
225b7b10 436int
ced8def3 437ACL::matchForCache(ACLChecklist *)
225b7b10 438{
439 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
440 * made for supported acl types */
441 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
f53969cc 442 return 0; /* NOTREACHED */
225b7b10 443}
444
94439e4e 445/*
26ac0430 446 * we lookup an acl's cached results, and if we cannot find the acl being
225b7b10 447 * checked we check it and cache the result. This function is a template
448 * method to support caching of multiple acl types.
449 * Note that caching of time based acl's is not
6bf4f823 450 * wise in long lived caches (i.e. the auth_user proxy match cache)
94439e4e 451 * RBC
6bf4f823 452 * TODO: does a dlink_list perform well enough? Kinkie
94439e4e 453 */
225b7b10 454int
455ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
9bea1d5b 456{
9bea1d5b 457 acl_proxy_auth_match_cache *auth_match;
458 dlink_node *link;
459 link = cache->head;
62e76326 460
9bea1d5b 461 while (link) {
62e76326 462 auth_match = (acl_proxy_auth_match_cache *)link->data;
463
464 if (auth_match->acl_data == this) {
bf8fe701 465 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
62e76326 466 return auth_match->matchrv;
467 }
468
469 link = link->next;
9bea1d5b 470 }
62e76326 471
d59e4742 472 auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
9bea1d5b 473 dlinkAddTail(auth_match, &auth_match->link, cache);
bf8fe701 474 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
225b7b10 475 return auth_match->matchrv;
94439e4e 476}
477
478void
9bea1d5b 479aclCacheMatchFlush(dlink_list * cache)
94439e4e 480{
9bea1d5b 481 acl_proxy_auth_match_cache *auth_match;
482 dlink_node *link, *tmplink;
483 link = cache->head;
62e76326 484
bf8fe701 485 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
6bf4f823 486
9bea1d5b 487 while (link) {
62e76326 488 auth_match = (acl_proxy_auth_match_cache *)link->data;
489 tmplink = link;
490 link = link->next;
491 dlinkDelete(tmplink, cache);
11a9f1b1 492 delete auth_match;
1f38f50a 493 }
c68e9c6b 494}
73e67ee0 495
4e56d7f6 496bool
4ff6370b 497ACL::requiresAle() const
4e56d7f6
AJ
498{
499 return false;
500}
501
b0dd28ba 502bool
503ACL::requiresReply() const
504{
505 return false;
506}
60d096f4 507
b0dd28ba 508bool
509ACL::requiresRequest() const
9bea1d5b 510{
b0dd28ba 511 return false;
512}
62e76326 513
f32789f4 514/*********************/
515/* Destroy functions */
516/*********************/
517
8000a965 518ACL::~ACL()
519{
e20d485b 520 debugs(28, 3, "freeing ACL " << name);
62e76326 521 safe_free(cfgline);
6f58d7d7 522 AclMatchedName = NULL; // in case it was pointing to our name
92a6f4b1 523}
524
8000a965 525ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
526
62e76326 527ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
8000a965 528{
529 registerMe ();
530}
531
81481ec0 532std::vector<ACL::Prototype const *> * ACL::Prototype::Registry;
8000a965 533void *ACL::Prototype::Initialized;
534
535bool
62e76326 536ACL::Prototype::Registered(char const *aType)
8000a965 537{
bf8fe701 538 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
6bf4f823 539
8000a965 540 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
6bf4f823 541 if (!strcmp (aType, (*i)->typeString)) {
bf8fe701 542 debugs(28, 7, "ACL::Prototype::Registered: yes");
62e76326 543 return true;
6bf4f823 544 }
62e76326 545
bf8fe701 546 debugs(28, 7, "ACL::Prototype::Registered: no");
8000a965 547 return false;
548}
549
550void
551ACL::Prototype::registerMe ()
552{
553 if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
62e76326 554 /* TODO: extract this */
555 /* Not initialised */
81481ec0 556 Registry = new std::vector<ACL::Prototype const *>;
62e76326 557 Initialized = (char *)Registry - 5;
8000a965 558 }
62e76326 559
8000a965 560 if (Registered (typeString))
62e76326 561 fatalf ("Attempt to register %s twice", typeString);
562
8000a965 563 Registry->push_back (this);
564}
565
62e76326 566ACL::Prototype::~Prototype()
8000a965 567{
67b7fa0d 568 // TODO: unregister me
8000a965 569}
570
571ACL *
572ACL::Prototype::Factory (char const *typeToClone)
573{
bf8fe701 574 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
6bf4f823 575
8000a965 576 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
33810b1d
CT
577 if (!strcmp (typeToClone, (*i)->typeString)) {
578 ACL *A = (*i)->prototype->clone();
579 A->flags = (*i)->prototype->flags;
580 return A;
581 }
62e76326 582
bf8fe701 583 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
6bf4f823 584
8000a965 585 return NULL;
586}
587
b0dd28ba 588void
589ACL::Initialize()
8000a965 590{
97427e90 591 ACL *a = Config.aclList;
bf8fe701 592 debugs(53, 3, "ACL::Initialize");
b0dd28ba 593
594 while (a) {
595 a->prepareForUse();
596 a = a->next;
597 }
8000a965 598}
f53969cc 599