]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Acl.cc
No-lookup DNS ACLs
[thirdparty/squid.git] / src / acl / Acl.cc
1 /*
2 * DEBUG: section 28 Access Control
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32 #include "squid.h"
33 #include "acl/Acl.h"
34 #include "acl/Checklist.h"
35 #include "anyp/PortCfg.h"
36 #include "cache_cf.h"
37 #include "ConfigParser.h"
38 #include "Debug.h"
39 #include "dlink.h"
40 #include "globals.h"
41 #include "SquidConfig.h"
42
43 const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
44
45 const char *AclMatchedName = NULL;
46
47 bool ACLFlags::supported(const ACLFlag f) const
48 {
49 if (f == ACL_F_REGEX_CASE)
50 return true;
51 return (supported_.find(f) != std::string::npos);
52 }
53
54 void
55 ACLFlags::parseFlags(char * &nextToken)
56 {
57 while((nextToken = ConfigParser::strtokFile()) != NULL && nextToken[0] == '-') {
58
59 //if token is the "--" break flag
60 if (strcmp(nextToken, "--") == 0)
61 break;
62
63 for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
64 if (supported(*flg)) {
65 makeSet(*flg);
66 }
67 else {
68 debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
69 self_destruct();
70 }
71 }
72 }
73
74 /*Regex code needs to parse -i file*/
75 if ( isSet(ACL_F_REGEX_CASE))
76 ConfigParser::strtokFilePutBack("-i");
77
78 if (nextToken != NULL && strcmp(nextToken, "--") != 0 )
79 ConfigParser::strtokFileUndo();
80 }
81
82 const char *
83 ACLFlags::flagsStr() const
84 {
85 static char buf[64];
86 if (flags_ == 0)
87 return "";
88
89 char *s = buf;
90 *s++ = '-';
91 for (ACLFlag f = 'A'; f <= 'z'; f++) {
92 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
93 if (isSet(f) && f != ACL_F_REGEX_CASE)
94 *s++ = f;
95 }
96 *s = '\0';
97 return buf;
98 }
99
100 void *
101 ACL::operator new (size_t byteCount)
102 {
103 fatal ("unusable ACL::new");
104 return (void *)1;
105 }
106
107 void
108 ACL::operator delete (void *address)
109 {
110 fatal ("unusable ACL::delete");
111 }
112
113 ACL *
114 ACL::FindByName(const char *name)
115 {
116 ACL *a;
117 debugs(28, 9, "ACL::FindByName '" << name << "'");
118
119 for (a = Config.aclList; a; a = a->next)
120 if (!strcasecmp(a->name, name))
121 return a;
122
123 debugs(28, 9, "ACL::FindByName found no match");
124
125 return NULL;
126 }
127
128 ACL *
129 ACL::Factory (char const *type)
130 {
131 ACL *result = Prototype::Factory (type);
132
133 if (!result)
134 fatal ("Unknown acl type in ACL::Factory");
135
136 return result;
137 }
138
139 ACL::ACL() :
140 cfgline(NULL),
141 next(NULL)
142 {
143 *name = 0;
144 }
145
146 bool ACL::valid () const
147 {
148 return true;
149 }
150
151 void
152 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
153 {
154 /* we're already using strtok() to grok the line */
155 char *t = NULL;
156 ACL *A = NULL;
157 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
158 int new_acl = 0;
159
160 /* snarf the ACL name */
161
162 if ((t = strtok(NULL, w_space)) == NULL) {
163 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
164 parser.destruct();
165 return;
166 }
167
168 if (strlen(t) >= ACL_NAME_SZ) {
169 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
170 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
171 parser.destruct();
172 return;
173 }
174
175 xstrncpy(aclname, t, ACL_NAME_SZ);
176 /* snarf the ACL type */
177 const char *theType;
178
179 if ((theType = strtok(NULL, w_space)) == NULL) {
180 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
181 parser.destruct();
182 return;
183 }
184
185 // Is this ACL going to work?
186 if (strcmp(theType, "myip") == 0) {
187 AnyP::PortCfg *p = Config.Sockaddr.http;
188 while (p) {
189 // Bug 3239: not reliable when there is interception traffic coming
190 if (p->intercepted)
191 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
192 p = p->next;
193 }
194 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
195 theType = "localip";
196 } else if (strcmp(theType, "myport") == 0) {
197 AnyP::PortCfg *p = Config.Sockaddr.http;
198 while (p) {
199 // Bug 3239: not reliable when there is interception traffic coming
200 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
201 if (p->intercepted)
202 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
203 p = p->next;
204 }
205 theType = "localport";
206 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
207 }
208
209 if (!Prototype::Registered(theType)) {
210 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
211 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
212 parser.destruct();
213 return;
214 }
215
216 if ((A = FindByName(aclname)) == NULL) {
217 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
218 A = ACL::Factory(theType);
219 xstrncpy(A->name, aclname, ACL_NAME_SZ);
220 A->cfgline = xstrdup(config_input_line);
221 new_acl = 1;
222 } else {
223 if (strcmp (A->typeString(),theType) ) {
224 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
225 parser.destruct();
226 return;
227 }
228
229 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
230 new_acl = 0;
231 }
232
233 /*
234 * Here we set AclMatchedName in case we need to use it in a
235 * warning message in aclDomainCompare().
236 */
237 AclMatchedName = A->name; /* ugly */
238
239 char *aTok;
240 A->flags.parseFlags(aTok);
241
242 /*split the function here */
243 A->parse();
244
245 /*
246 * Clear AclMatchedName from our temporary hack
247 */
248 AclMatchedName = NULL; /* ugly */
249
250 if (!new_acl)
251 return;
252
253 if (A->empty()) {
254 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
255 }
256
257 if (!A->valid()) {
258 fatalf("ERROR: Invalid ACL: %s\n",
259 A->cfgline);
260 }
261
262 /* append */
263 while (*head)
264 head = &(*head)->next;
265
266 *head = A;
267 }
268
269 bool
270 ACL::isProxyAuth() const
271 {
272 return false;
273 }
274
275 ACLList::ACLList() : op (1), _acl (NULL), next (NULL)
276 {}
277
278 void
279 ACLList::negated(bool isNegated)
280 {
281 if (isNegated)
282 op = 0;
283 else
284 op = 1;
285 }
286
287 /* ACL result caching routines */
288
289 int
290 ACL::matchForCache(ACLChecklist *checklist)
291 {
292 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
293 * made for supported acl types */
294 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
295 return 0; /* NOTREACHED */
296 }
297
298 /*
299 * we lookup an acl's cached results, and if we cannot find the acl being
300 * checked we check it and cache the result. This function is a template
301 * method to support caching of multiple acl types.
302 * Note that caching of time based acl's is not
303 * wise in long lived caches (i.e. the auth_user proxy match cache)
304 * RBC
305 * TODO: does a dlink_list perform well enough? Kinkie
306 */
307 int
308 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
309 {
310 acl_proxy_auth_match_cache *auth_match;
311 dlink_node *link;
312 link = cache->head;
313
314 while (link) {
315 auth_match = (acl_proxy_auth_match_cache *)link->data;
316
317 if (auth_match->acl_data == this) {
318 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
319 return auth_match->matchrv;
320 }
321
322 link = link->next;
323 }
324
325 auth_match = new acl_proxy_auth_match_cache();
326 auth_match->matchrv = matchForCache (checklist);
327 auth_match->acl_data = this;
328 dlinkAddTail(auth_match, &auth_match->link, cache);
329 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
330 return auth_match->matchrv;
331 }
332
333 void
334 aclCacheMatchFlush(dlink_list * cache)
335 {
336 acl_proxy_auth_match_cache *auth_match;
337 dlink_node *link, *tmplink;
338 link = cache->head;
339
340 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
341
342 while (link) {
343 auth_match = (acl_proxy_auth_match_cache *)link->data;
344 tmplink = link;
345 link = link->next;
346 dlinkDelete(tmplink, cache);
347 delete auth_match;
348 }
349 }
350
351 bool
352 ACL::requiresReply() const
353 {
354 return false;
355 }
356
357 bool
358 ACL::requiresRequest() const
359 {
360 return false;
361 }
362
363 int
364 ACL::checklistMatches(ACLChecklist *checklist)
365 {
366 int rv;
367
368 if (!checklist->hasRequest() && requiresRequest()) {
369 debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP request -- not matching.");
370 return 0;
371 }
372
373 if (!checklist->hasReply() && requiresReply()) {
374 debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP reply -- not matching.");
375 return 0;
376 }
377
378 debugs(28, 3, "ACL::checklistMatches: checking '" << name << "'");
379 rv= match(checklist);
380 debugs(28, 3, "ACL::ChecklistMatches: result for '" << name << "' is " << rv);
381 return rv;
382 }
383
384 bool
385 ACLList::matches (ACLChecklist *checklist) const
386 {
387 assert (_acl);
388 // XXX: AclMatchedName does not contain a matched ACL name when the acl
389 // does not match (or contains stale name if no ACLs are checked). In
390 // either case, we get misleading debugging and possibly incorrect error
391 // messages. Unfortunately, deny_info's "when none http_access
392 // lines match" exception essentially requires this mess.
393 // TODO: Rework by using an acl-free deny_info for the no-match cases?
394 AclMatchedName = _acl->name;
395 debugs(28, 3, "ACLList::matches: checking " << (op ? null_string : "!") << _acl->name);
396
397 if (_acl->checklistMatches(checklist) != op) {
398 debugs(28, 4, "ACLList::matches: result is false");
399 return false;
400 }
401
402 debugs(28, 4, "ACLList::matches: result is true");
403 return true;
404 }
405
406 /*********************/
407 /* Destroy functions */
408 /*********************/
409
410 ACL::~ACL()
411 {
412 debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
413 safe_free(cfgline);
414 }
415
416 /* to be split into separate files in the future */
417
418 CBDATA_CLASS_INIT(acl_access);
419
420 void *
421 acl_access::operator new (size_t)
422 {
423 CBDATA_INIT_TYPE(acl_access);
424 acl_access *result = cbdataAlloc(acl_access);
425 return result;
426 }
427
428 void
429 acl_access::operator delete (void *address)
430 {
431 acl_access *t = static_cast<acl_access *>(address);
432 cbdataFree(t);
433 }
434
435 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
436
437 ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
438 {
439 registerMe ();
440 }
441
442 Vector<ACL::Prototype const *> * ACL::Prototype::Registry;
443 void *ACL::Prototype::Initialized;
444
445 bool
446 ACL::Prototype::Registered(char const *aType)
447 {
448 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
449
450 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
451 if (!strcmp (aType, (*i)->typeString)) {
452 debugs(28, 7, "ACL::Prototype::Registered: yes");
453 return true;
454 }
455
456 debugs(28, 7, "ACL::Prototype::Registered: no");
457 return false;
458 }
459
460 void
461 ACL::Prototype::registerMe ()
462 {
463 if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
464 /* TODO: extract this */
465 /* Not initialised */
466 Registry = new Vector <ACL::Prototype const *>;
467 Initialized = (char *)Registry - 5;
468 }
469
470 if (Registered (typeString))
471 fatalf ("Attempt to register %s twice", typeString);
472
473 Registry->push_back (this);
474 }
475
476 ACL::Prototype::~Prototype()
477 {
478 // TODO: unregister me
479 }
480
481 ACL *
482 ACL::Prototype::Factory (char const *typeToClone)
483 {
484 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
485
486 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
487 if (!strcmp (typeToClone, (*i)->typeString)) {
488 ACL *A = (*i)->prototype->clone();
489 A->flags = (*i)->prototype->flags;
490 return A;
491 }
492
493 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
494
495 return NULL;
496 }
497
498 void
499 ACL::Initialize()
500 {
501 ACL *a = Config.aclList;
502 debugs(53, 3, "ACL::Initialize");
503
504 while (a) {
505 a->prepareForUse();
506 a = a->next;
507 }
508 }