]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/Acl.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / acl / Acl.cc
CommitLineData
8213067d 1/*
5b74111a 2 * Copyright (C) 1996-2018 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"
4eac3407 15#include "acl/Options.h"
582c2af2 16#include "anyp/PortCfg.h"
33810b1d 17#include "cache_cf.h"
d295d770 18#include "ConfigParser.h"
38dedeed 19#include "Debug.h"
ed6e9fb9 20#include "fatal.h"
582c2af2 21#include "globals.h"
6f58d7d7 22#include "profiler/Profiler.h"
4eac3407
CT
23#include "sbuf/List.h"
24#include "sbuf/Stream.h"
4d5904f7 25#include "SquidConfig.h"
23351cb2 26
4eac3407
CT
27#include <algorithm>
28#include <map>
33810b1d 29
b0dd28ba 30const char *AclMatchedName = NULL;
31
4eac3407 32namespace Acl {
76ee67ac 33
4eac3407
CT
34/// ACL type name comparison functor
35class TypeNameCmp {
36public:
37 bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
38};
76ee67ac 39
4eac3407
CT
40/// ACL makers indexed by ACL type name
41typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
76ee67ac 42
4eac3407
CT
43/// registered ACL Makers
44static Makers &
45TheMakers()
76ee67ac 46{
4eac3407
CT
47 static Makers Registry;
48 return Registry;
76ee67ac
CT
49}
50
4eac3407
CT
51/// creates an ACL object of the named (and already registered) ACL child type
52static
53ACL *
54Make(TypeName typeName)
76ee67ac 55{
4eac3407
CT
56 const auto pos = TheMakers().find(typeName);
57 if (pos == TheMakers().end()) {
58 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << typeName << "'");
59 self_destruct();
60 assert(false); // not reached
75d47340 61 }
76ee67ac 62
4eac3407
CT
63 ACL *result = (pos->second)(pos->first);
64 debugs(28, 4, typeName << '=' << result);
65 assert(result);
66 return result;
76ee67ac
CT
67}
68
4eac3407 69} // namespace Acl
33810b1d 70
aec45181 71void
4eac3407 72Acl::RegisterMaker(TypeName typeName, Maker maker)
76ee67ac 73{
4eac3407
CT
74 assert(typeName);
75 assert(*typeName);
76 TheMakers().emplace(typeName, maker);
33810b1d
CT
77}
78
8000a965 79void *
ced8def3 80ACL::operator new (size_t)
8000a965 81{
b0dd28ba 82 fatal ("unusable ACL::new");
83 return (void *)1;
8000a965 84}
85
86void
ced8def3 87ACL::operator delete (void *)
8000a965 88{
b0dd28ba 89 fatal ("unusable ACL::delete");
56b63fa1 90}
91
97427e90 92ACL *
225b7b10 93ACL::FindByName(const char *name)
7dd57fa2 94{
97427e90 95 ACL *a;
bf8fe701 96 debugs(28, 9, "ACL::FindByName '" << name << "'");
62e76326 97
9bea1d5b 98 for (a = Config.aclList; a; a = a->next)
62e76326 99 if (!strcasecmp(a->name, name))
100 return a;
101
bf8fe701 102 debugs(28, 9, "ACL::FindByName found no match");
6bf4f823 103
9bea1d5b 104 return NULL;
7dd57fa2 105}
106
478a0611 107ACL::ACL() :
d59e4742
FC
108 cfgline(nullptr),
109 next(nullptr),
110 registered(false)
111{
112 *name = 0;
113}
114
4b0f5de8 115bool ACL::valid () const
116{
117 return true;
118}
119
6f58d7d7
AR
120bool
121ACL::matches(ACLChecklist *checklist) const
122{
123 PROF_start(ACL_matches);
124 debugs(28, 5, "checking " << name);
125
126 // XXX: AclMatchedName does not contain a matched ACL name when the acl
127 // does not match. It contains the last (usually leaf) ACL name checked
128 // (or is NULL if no ACLs were checked).
129 AclMatchedName = name;
130
131 int result = 0;
4ff6370b 132 if (!checklist->hasAle() && requiresAle()) {
4e56d7f6 133 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
acfe8e09 134 "context without an ALE state. Assuming mismatch.");
4e56d7f6 135 } else if (!checklist->hasRequest() && requiresRequest()) {
6f58d7d7
AR
136 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
137 "context without an HTTP request. Assuming mismatch.");
138 } else if (!checklist->hasReply() && requiresReply()) {
139 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
140 "context without an HTTP response. Assuming mismatch.");
141 } else {
fbbea662
AJ
142 // make sure the ALE has as much data as possible
143 if (requiresAle())
144 checklist->syncAle();
145
6f58d7d7
AR
146 // have to cast because old match() API is missing const
147 result = const_cast<ACL*>(this)->match(checklist);
148 }
149
150 const char *extra = checklist->asyncInProgress() ? " async" : "";
151 debugs(28, 3, "checked: " << name << " = " << result << extra);
152 PROF_stop(ACL_matches);
153 return result == 1; // true for match; false for everything else
154}
155
156void
157ACL::context(const char *aName, const char *aCfgLine)
158{
159 name[0] = '\0';
160 if (aName)
161 xstrncpy(name, aName, ACL_NAME_SZ-1);
162 safe_free(cfgline);
163 if (aCfgLine)
164 cfgline = xstrdup(aCfgLine);
165}
166
8203a132 167void
a9f20260 168ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
9bea1d5b 169{
170 /* we're already using strtok() to grok the line */
171 char *t = NULL;
97427e90 172 ACL *A = NULL;
9bea1d5b 173 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
9bea1d5b 174 int new_acl = 0;
175
176 /* snarf the ACL name */
62e76326 177
2eceb328 178 if ((t = ConfigParser::NextToken()) == NULL) {
fa84c01d 179 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
a9f20260 180 parser.destruct();
62e76326 181 return;
9bea1d5b 182 }
62e76326 183
401f503a 184 if (strlen(t) >= ACL_NAME_SZ) {
fa84c01d 185 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
bf8fe701 186 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
401f503a 187 parser.destruct();
188 return;
189 }
190
9bea1d5b 191 xstrncpy(aclname, t, ACL_NAME_SZ);
192 /* snarf the ACL type */
38dedeed 193 const char *theType;
62e76326 194
2eceb328 195 if ((theType = ConfigParser::NextToken()) == NULL) {
fa84c01d 196 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
a9f20260 197 parser.destruct();
62e76326 198 return;
9bea1d5b 199 }
62e76326 200
8306968f 201 // Is this ACL going to work?
18c41901 202 if (strcmp(theType, "myip") == 0) {
fa720bfb
AJ
203 AnyP::PortCfgPointer p = HttpPortList;
204 while (p != NULL) {
8306968f 205 // Bug 3239: not reliable when there is interception traffic coming
6a25a046 206 if (p->flags.natIntercept)
8306968f
AJ
207 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
208 p = p->next;
209 }
38dedeed 210 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
1e40905d 211 theType = "localip";
18c41901 212 } else if (strcmp(theType, "myport") == 0) {
fa720bfb
AJ
213 AnyP::PortCfgPointer p = HttpPortList;
214 while (p != NULL) {
8306968f
AJ
215 // Bug 3239: not reliable when there is interception traffic coming
216 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
6a25a046 217 if (p->flags.natIntercept)
8306968f
AJ
218 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
219 p = p->next;
220 }
1e40905d 221 theType = "localport";
38dedeed 222 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
49549cd1
AJ
223 } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
224 // ACL manager is now a built-in and has a different type.
225 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
226 return; // ignore the line
1e40905d
AJ
227 }
228
225b7b10 229 if ((A = FindByName(aclname)) == NULL) {
bf8fe701 230 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
4eac3407 231 A = Acl::Make(theType);
6f58d7d7 232 A->context(aclname, config_input_line);
62e76326 233 new_acl = 1;
9bea1d5b 234 } else {
b0dd28ba 235 if (strcmp (A->typeString(),theType) ) {
fa84c01d 236 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
a9f20260 237 parser.destruct();
62e76326 238 return;
239 }
240
bf8fe701 241 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
62e76326 242 new_acl = 0;
9bea1d5b 243 }
62e76326 244
9bea1d5b 245 /*
246 * Here we set AclMatchedName in case we need to use it in a
247 * warning message in aclDomainCompare().
248 */
f53969cc 249 AclMatchedName = A->name; /* ugly */
8000a965 250
4eac3407 251 A->parseFlags();
33810b1d 252
8000a965 253 /*split the function here */
254 A->parse();
62e76326 255
8000a965 256 /*
257 * Clear AclMatchedName from our temporary hack
258 */
f53969cc 259 AclMatchedName = NULL; /* ugly */
62e76326 260
8000a965 261 if (!new_acl)
62e76326 262 return;
263
4b0f5de8 264 if (A->empty()) {
fa84c01d 265 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
4b0f5de8 266 }
267
268 if (!A->valid()) {
269 fatalf("ERROR: Invalid ACL: %s\n",
270 A->cfgline);
8000a965 271 }
62e76326 272
ed898bdf 273 // add to the global list for searching explicit ACLs by name
6f58d7d7 274 assert(head && *head == Config.aclList);
a57c224d 275 A->next = *head;
8000a965 276 *head = A;
ed898bdf
AR
277
278 // register for centralized cleanup
279 aclRegister(A);
8000a965 280}
281
8000a965 282bool
283ACL::isProxyAuth() const
284{
225b7b10 285 return false;
8000a965 286}
1cfdbcf0 287
4eac3407
CT
288void
289ACL::parseFlags()
290{
291 // ACL kids that carry ACLData which supports parameter flags override this
292 Acl::ParseFlags(options(), Acl::NoFlags());
293}
294
295SBufList
296ACL::dumpOptions()
297{
298 SBufList result;
299 const auto &myOptions = options();
300 // optimization: most ACLs do not have myOptions
301 // this check also works around dump_SBufList() adding ' ' after empty items
302 if (!myOptions.empty()) {
303 SBufStream stream;
304 stream << myOptions;
305 const SBuf optionsImage = stream.buf();
306 if (!optionsImage.isEmpty())
307 result.push_back(optionsImage);
308 }
309 return result;
310}
311
94439e4e 312/* ACL result caching routines */
313
225b7b10 314int
ced8def3 315ACL::matchForCache(ACLChecklist *)
225b7b10 316{
317 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
318 * made for supported acl types */
319 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
f53969cc 320 return 0; /* NOTREACHED */
225b7b10 321}
322
94439e4e 323/*
26ac0430 324 * we lookup an acl's cached results, and if we cannot find the acl being
225b7b10 325 * checked we check it and cache the result. This function is a template
326 * method to support caching of multiple acl types.
327 * Note that caching of time based acl's is not
6bf4f823 328 * wise in long lived caches (i.e. the auth_user proxy match cache)
94439e4e 329 * RBC
6bf4f823 330 * TODO: does a dlink_list perform well enough? Kinkie
94439e4e 331 */
225b7b10 332int
333ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
9bea1d5b 334{
9bea1d5b 335 acl_proxy_auth_match_cache *auth_match;
336 dlink_node *link;
337 link = cache->head;
62e76326 338
9bea1d5b 339 while (link) {
62e76326 340 auth_match = (acl_proxy_auth_match_cache *)link->data;
341
342 if (auth_match->acl_data == this) {
bf8fe701 343 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
62e76326 344 return auth_match->matchrv;
345 }
346
347 link = link->next;
9bea1d5b 348 }
62e76326 349
d59e4742 350 auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
9bea1d5b 351 dlinkAddTail(auth_match, &auth_match->link, cache);
bf8fe701 352 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
225b7b10 353 return auth_match->matchrv;
94439e4e 354}
355
356void
9bea1d5b 357aclCacheMatchFlush(dlink_list * cache)
94439e4e 358{
9bea1d5b 359 acl_proxy_auth_match_cache *auth_match;
360 dlink_node *link, *tmplink;
361 link = cache->head;
62e76326 362
bf8fe701 363 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
6bf4f823 364
9bea1d5b 365 while (link) {
62e76326 366 auth_match = (acl_proxy_auth_match_cache *)link->data;
367 tmplink = link;
368 link = link->next;
369 dlinkDelete(tmplink, cache);
11a9f1b1 370 delete auth_match;
1f38f50a 371 }
c68e9c6b 372}
73e67ee0 373
4e56d7f6 374bool
4ff6370b 375ACL::requiresAle() const
4e56d7f6
AJ
376{
377 return false;
378}
379
b0dd28ba 380bool
381ACL::requiresReply() const
382{
383 return false;
384}
60d096f4 385
b0dd28ba 386bool
387ACL::requiresRequest() const
9bea1d5b 388{
b0dd28ba 389 return false;
390}
62e76326 391
f32789f4 392/*********************/
393/* Destroy functions */
394/*********************/
395
8000a965 396ACL::~ACL()
397{
e20d485b 398 debugs(28, 3, "freeing ACL " << name);
62e76326 399 safe_free(cfgline);
6f58d7d7 400 AclMatchedName = NULL; // in case it was pointing to our name
92a6f4b1 401}
402
b0dd28ba 403void
404ACL::Initialize()
8000a965 405{
97427e90 406 ACL *a = Config.aclList;
bf8fe701 407 debugs(53, 3, "ACL::Initialize");
b0dd28ba 408
409 while (a) {
410 a->prepareForUse();
411 a = a->next;
412 }
8000a965 413}
f53969cc 414