]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Acl.cc
merge from trunk r14444
[thirdparty/squid.git] / src / acl / Acl.cc
1 /*
2 * Copyright (C) 1996-2015 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 "anyp/PortCfg.h"
16 #include "cache_cf.h"
17 #include "ConfigParser.h"
18 #include "Debug.h"
19 #include "dlink.h"
20 #include "fatal.h"
21 #include "globals.h"
22 #include "profiler/Profiler.h"
23 #include "SquidConfig.h"
24
25 #include <vector>
26
27 const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
28
29 const char *AclMatchedName = NULL;
30
31 bool ACLFlags::supported(const ACLFlag f) const
32 {
33 if (f == ACL_F_REGEX_CASE)
34 return true;
35 return (supported_.find(f) != std::string::npos);
36 }
37
38 void
39 ACLFlags::parseFlags()
40 {
41 char *nextToken;
42 while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') {
43 (void)ConfigParser::NextToken(); //Get token from cfg line
44 //if token is the "--" break flag
45 if (strcmp(nextToken, "--") == 0)
46 break;
47
48 for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
49 if (supported(*flg)) {
50 makeSet(*flg);
51 } else {
52 debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
53 self_destruct();
54 }
55 }
56 }
57
58 /*Regex code needs to parse -i file*/
59 if ( isSet(ACL_F_REGEX_CASE)) {
60 ConfigParser::TokenPutBack("-i");
61 makeUnSet('i');
62 }
63 }
64
65 const char *
66 ACLFlags::flagsStr() const
67 {
68 static char buf[64];
69 if (flags_ == 0)
70 return "";
71
72 char *s = buf;
73 *s++ = '-';
74 for (ACLFlag f = 'A'; f <= 'z'; f++) {
75 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
76 if (isSet(f) && f != ACL_F_REGEX_CASE)
77 *s++ = f;
78 }
79 *s = '\0';
80 return buf;
81 }
82
83 void *
84 ACL::operator new (size_t)
85 {
86 fatal ("unusable ACL::new");
87 return (void *)1;
88 }
89
90 void
91 ACL::operator delete (void *)
92 {
93 fatal ("unusable ACL::delete");
94 }
95
96 ACL *
97 ACL::FindByName(const char *name)
98 {
99 ACL *a;
100 debugs(28, 9, "ACL::FindByName '" << name << "'");
101
102 for (a = Config.aclList; a; a = a->next)
103 if (!strcasecmp(a->name, name))
104 return a;
105
106 debugs(28, 9, "ACL::FindByName found no match");
107
108 return NULL;
109 }
110
111 ACL *
112 ACL::Factory (char const *type)
113 {
114 ACL *result = Prototype::Factory (type);
115
116 if (!result)
117 fatal ("Unknown acl type in ACL::Factory");
118
119 return result;
120 }
121
122 ACL::ACL() :
123 cfgline(nullptr),
124 next(nullptr),
125 registered(false)
126 {
127 *name = 0;
128 }
129
130 ACL::ACL(const ACLFlag flgs[]) :
131 cfgline(NULL),
132 next(NULL),
133 flags(flgs),
134 registered(false)
135 {
136 *name = 0;
137 }
138
139 bool ACL::valid () const
140 {
141 return true;
142 }
143
144 bool
145 ACL::matches(ACLChecklist *checklist) const
146 {
147 PROF_start(ACL_matches);
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).
153 AclMatchedName = name;
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->syncAle();
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 PROF_stop(ACL_matches);
177 return result == 1; // true for match; false for everything else
178 }
179
180 void
181 ACL::context(const char *aName, const char *aCfgLine)
182 {
183 name[0] = '\0';
184 if (aName)
185 xstrncpy(name, aName, ACL_NAME_SZ-1);
186 safe_free(cfgline);
187 if (aCfgLine)
188 cfgline = xstrdup(aCfgLine);
189 }
190
191 void
192 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
193 {
194 /* we're already using strtok() to grok the line */
195 char *t = NULL;
196 ACL *A = NULL;
197 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
198 int new_acl = 0;
199
200 /* snarf the ACL name */
201
202 if ((t = ConfigParser::NextToken()) == NULL) {
203 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
204 parser.destruct();
205 return;
206 }
207
208 if (strlen(t) >= ACL_NAME_SZ) {
209 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
210 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
211 parser.destruct();
212 return;
213 }
214
215 xstrncpy(aclname, t, ACL_NAME_SZ);
216 /* snarf the ACL type */
217 const char *theType;
218
219 if ((theType = ConfigParser::NextToken()) == NULL) {
220 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
221 parser.destruct();
222 return;
223 }
224
225 // Is this ACL going to work?
226 if (strcmp(theType, "myip") == 0) {
227 AnyP::PortCfgPointer p = HttpPortList;
228 while (p != NULL) {
229 // Bug 3239: not reliable when there is interception traffic coming
230 if (p->flags.natIntercept)
231 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
232 p = p->next;
233 }
234 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
235 theType = "localip";
236 } else if (strcmp(theType, "myport") == 0) {
237 AnyP::PortCfgPointer p = HttpPortList;
238 while (p != NULL) {
239 // Bug 3239: not reliable when there is interception traffic coming
240 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
241 if (p->flags.natIntercept)
242 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
243 p = p->next;
244 }
245 theType = "localport";
246 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
247 } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
248 // ACL manager is now a built-in and has a different type.
249 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
250 return; // ignore the line
251 }
252
253 if (!Prototype::Registered(theType)) {
254 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
255 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
256 parser.destruct();
257 return;
258 }
259
260 if ((A = FindByName(aclname)) == NULL) {
261 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
262 A = ACL::Factory(theType);
263 A->context(aclname, config_input_line);
264 new_acl = 1;
265 } else {
266 if (strcmp (A->typeString(),theType) ) {
267 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
268 parser.destruct();
269 return;
270 }
271
272 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
273 new_acl = 0;
274 }
275
276 /*
277 * Here we set AclMatchedName in case we need to use it in a
278 * warning message in aclDomainCompare().
279 */
280 AclMatchedName = A->name; /* ugly */
281
282 A->flags.parseFlags();
283
284 /*split the function here */
285 A->parse();
286
287 /*
288 * Clear AclMatchedName from our temporary hack
289 */
290 AclMatchedName = NULL; /* ugly */
291
292 if (!new_acl)
293 return;
294
295 if (A->empty()) {
296 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
297 }
298
299 if (!A->valid()) {
300 fatalf("ERROR: Invalid ACL: %s\n",
301 A->cfgline);
302 }
303
304 // add to the global list for searching explicit ACLs by name
305 assert(head && *head == Config.aclList);
306 A->next = *head;
307 *head = A;
308
309 // register for centralized cleanup
310 aclRegister(A);
311 }
312
313 bool
314 ACL::isProxyAuth() const
315 {
316 return false;
317 }
318
319 /* ACL result caching routines */
320
321 int
322 ACL::matchForCache(ACLChecklist *)
323 {
324 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
325 * made for supported acl types */
326 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
327 return 0; /* NOTREACHED */
328 }
329
330 /*
331 * we lookup an acl's cached results, and if we cannot find the acl being
332 * checked we check it and cache the result. This function is a template
333 * method to support caching of multiple acl types.
334 * Note that caching of time based acl's is not
335 * wise in long lived caches (i.e. the auth_user proxy match cache)
336 * RBC
337 * TODO: does a dlink_list perform well enough? Kinkie
338 */
339 int
340 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
341 {
342 acl_proxy_auth_match_cache *auth_match;
343 dlink_node *link;
344 link = cache->head;
345
346 while (link) {
347 auth_match = (acl_proxy_auth_match_cache *)link->data;
348
349 if (auth_match->acl_data == this) {
350 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
351 return auth_match->matchrv;
352 }
353
354 link = link->next;
355 }
356
357 auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
358 dlinkAddTail(auth_match, &auth_match->link, cache);
359 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
360 return auth_match->matchrv;
361 }
362
363 void
364 aclCacheMatchFlush(dlink_list * cache)
365 {
366 acl_proxy_auth_match_cache *auth_match;
367 dlink_node *link, *tmplink;
368 link = cache->head;
369
370 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
371
372 while (link) {
373 auth_match = (acl_proxy_auth_match_cache *)link->data;
374 tmplink = link;
375 link = link->next;
376 dlinkDelete(tmplink, cache);
377 delete auth_match;
378 }
379 }
380
381 bool
382 ACL::requiresAle() const
383 {
384 return false;
385 }
386
387 bool
388 ACL::requiresReply() const
389 {
390 return false;
391 }
392
393 bool
394 ACL::requiresRequest() const
395 {
396 return false;
397 }
398
399 /*********************/
400 /* Destroy functions */
401 /*********************/
402
403 ACL::~ACL()
404 {
405 debugs(28, 3, "freeing ACL " << name);
406 safe_free(cfgline);
407 AclMatchedName = NULL; // in case it was pointing to our name
408 }
409
410 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
411
412 ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
413 {
414 registerMe ();
415 }
416
417 std::vector<ACL::Prototype const *> * ACL::Prototype::Registry;
418 void *ACL::Prototype::Initialized;
419
420 bool
421 ACL::Prototype::Registered(char const *aType)
422 {
423 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
424
425 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
426 if (!strcmp (aType, (*i)->typeString)) {
427 debugs(28, 7, "ACL::Prototype::Registered: yes");
428 return true;
429 }
430
431 debugs(28, 7, "ACL::Prototype::Registered: no");
432 return false;
433 }
434
435 void
436 ACL::Prototype::registerMe ()
437 {
438 if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
439 /* TODO: extract this */
440 /* Not initialised */
441 Registry = new std::vector<ACL::Prototype const *>;
442 Initialized = (char *)Registry - 5;
443 }
444
445 if (Registered (typeString))
446 fatalf ("Attempt to register %s twice", typeString);
447
448 Registry->push_back (this);
449 }
450
451 ACL::Prototype::~Prototype()
452 {
453 // TODO: unregister me
454 }
455
456 ACL *
457 ACL::Prototype::Factory (char const *typeToClone)
458 {
459 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
460
461 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
462 if (!strcmp (typeToClone, (*i)->typeString)) {
463 ACL *A = (*i)->prototype->clone();
464 A->flags = (*i)->prototype->flags;
465 return A;
466 }
467
468 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
469
470 return NULL;
471 }
472
473 void
474 ACL::Initialize()
475 {
476 ACL *a = Config.aclList;
477 debugs(53, 3, "ACL::Initialize");
478
479 while (a) {
480 a->prepareForUse();
481 a = a->next;
482 }
483 }
484