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