]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Acl.cc
Merged from trunk rev.13515
[thirdparty/squid.git] / src / acl / Acl.cc
1 /*
2 * DEBUG: section 28 Access Control
3 *
4 * SQUID Web Proxy Cache http://www.squid-cache.org/
5 * ----------------------------------------------------------
6 *
7 * Squid is the result of efforts by numerous individuals from
8 * the Internet community; see the CONTRIBUTORS file for full
9 * details. Many organizations have provided support for Squid's
10 * development; see the SPONSORS file for full details. Squid is
11 * Copyrighted (C) 2001 by the Regents of the University of
12 * California; see the COPYRIGHT file for full details. Squid
13 * incorporates software developed and/or copyrighted by other
14 * sources; see the CREDITS file for full details.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
29 *
30 */
31 #include "squid.h"
32 #include "acl/Acl.h"
33 #include "acl/Checklist.h"
34 #include "acl/Gadgets.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 "profiler/Profiler.h"
42 #include "SquidConfig.h"
43
44 #include <vector>
45
46 const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
47
48 const char *AclMatchedName = NULL;
49
50 bool ACLFlags::supported(const ACLFlag f) const
51 {
52 if (f == ACL_F_REGEX_CASE)
53 return true;
54 return (supported_.find(f) != std::string::npos);
55 }
56
57 void
58 ACLFlags::parseFlags()
59 {
60 char *nextToken;
61 while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') {
62 (void)ConfigParser::NextToken(); //Get token from cfg line
63 //if token is the "--" break flag
64 if (strcmp(nextToken, "--") == 0)
65 break;
66
67 for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
68 if (supported(*flg)) {
69 makeSet(*flg);
70 } else {
71 debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
72 self_destruct();
73 }
74 }
75 }
76
77 /*Regex code needs to parse -i file*/
78 if ( isSet(ACL_F_REGEX_CASE))
79 ConfigParser::TokenPutBack("-i");
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 registered(false)
143 {
144 *name = 0;
145 }
146
147 bool ACL::valid () const
148 {
149 return true;
150 }
151
152 bool
153 ACL::matches(ACLChecklist *checklist) const
154 {
155 PROF_start(ACL_matches);
156 debugs(28, 5, "checking " << name);
157
158 // XXX: AclMatchedName does not contain a matched ACL name when the acl
159 // does not match. It contains the last (usually leaf) ACL name checked
160 // (or is NULL if no ACLs were checked).
161 AclMatchedName = name;
162
163 int result = 0;
164 if (!checklist->hasRequest() && requiresRequest()) {
165 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
166 "context without an HTTP request. Assuming mismatch.");
167 } else if (!checklist->hasReply() && requiresReply()) {
168 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
169 "context without an HTTP response. Assuming mismatch.");
170 } else {
171 // have to cast because old match() API is missing const
172 result = const_cast<ACL*>(this)->match(checklist);
173 }
174
175 const char *extra = checklist->asyncInProgress() ? " async" : "";
176 debugs(28, 3, "checked: " << name << " = " << result << extra);
177 PROF_stop(ACL_matches);
178 return result == 1; // true for match; false for everything else
179 }
180
181 void
182 ACL::context(const char *aName, const char *aCfgLine)
183 {
184 name[0] = '\0';
185 if (aName)
186 xstrncpy(name, aName, ACL_NAME_SZ-1);
187 safe_free(cfgline);
188 if (aCfgLine)
189 cfgline = xstrdup(aCfgLine);
190 }
191
192 void
193 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
194 {
195 /* we're already using strtok() to grok the line */
196 char *t = NULL;
197 ACL *A = NULL;
198 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
199 int new_acl = 0;
200
201 /* snarf the ACL name */
202
203 if ((t = ConfigParser::NextToken()) == NULL) {
204 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
205 parser.destruct();
206 return;
207 }
208
209 if (strlen(t) >= ACL_NAME_SZ) {
210 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
211 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
212 parser.destruct();
213 return;
214 }
215
216 xstrncpy(aclname, t, ACL_NAME_SZ);
217 /* snarf the ACL type */
218 const char *theType;
219
220 if ((theType = ConfigParser::NextToken()) == NULL) {
221 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
222 parser.destruct();
223 return;
224 }
225
226 // Is this ACL going to work?
227 if (strcmp(theType, "myip") == 0) {
228 AnyP::PortCfgPointer p = HttpPortList;
229 while (p != NULL) {
230 // Bug 3239: not reliable when there is interception traffic coming
231 if (p->flags.natIntercept)
232 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
233 p = p->next;
234 }
235 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
236 theType = "localip";
237 } else if (strcmp(theType, "myport") == 0) {
238 AnyP::PortCfgPointer p = HttpPortList;
239 while (p != NULL) {
240 // Bug 3239: not reliable when there is interception traffic coming
241 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
242 if (p->flags.natIntercept)
243 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
244 p = p->next;
245 }
246 theType = "localport";
247 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
248 }
249
250 if (!Prototype::Registered(theType)) {
251 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
252 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
253 parser.destruct();
254 return;
255 }
256
257 if ((A = FindByName(aclname)) == NULL) {
258 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
259 A = ACL::Factory(theType);
260 A->context(aclname, config_input_line);
261 new_acl = 1;
262 } else {
263 if (strcmp (A->typeString(),theType) ) {
264 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
265 parser.destruct();
266 return;
267 }
268
269 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
270 new_acl = 0;
271 }
272
273 /*
274 * Here we set AclMatchedName in case we need to use it in a
275 * warning message in aclDomainCompare().
276 */
277 AclMatchedName = A->name; /* ugly */
278
279 A->flags.parseFlags();
280
281 /*split the function here */
282 A->parse();
283
284 /*
285 * Clear AclMatchedName from our temporary hack
286 */
287 AclMatchedName = NULL; /* ugly */
288
289 if (!new_acl)
290 return;
291
292 if (A->empty()) {
293 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
294 }
295
296 if (!A->valid()) {
297 fatalf("ERROR: Invalid ACL: %s\n",
298 A->cfgline);
299 }
300
301 // add to the global list for searching explicit ACLs by name
302 assert(head && *head == Config.aclList);
303 A->next = *head;
304 *head = A;
305
306 // register for centralized cleanup
307 aclRegister(A);
308 }
309
310 bool
311 ACL::isProxyAuth() const
312 {
313 return false;
314 }
315
316 /* ACL result caching routines */
317
318 int
319 ACL::matchForCache(ACLChecklist *checklist)
320 {
321 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
322 * made for supported acl types */
323 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
324 return 0; /* NOTREACHED */
325 }
326
327 /*
328 * we lookup an acl's cached results, and if we cannot find the acl being
329 * checked we check it and cache the result. This function is a template
330 * method to support caching of multiple acl types.
331 * Note that caching of time based acl's is not
332 * wise in long lived caches (i.e. the auth_user proxy match cache)
333 * RBC
334 * TODO: does a dlink_list perform well enough? Kinkie
335 */
336 int
337 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
338 {
339 acl_proxy_auth_match_cache *auth_match;
340 dlink_node *link;
341 link = cache->head;
342
343 while (link) {
344 auth_match = (acl_proxy_auth_match_cache *)link->data;
345
346 if (auth_match->acl_data == this) {
347 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
348 return auth_match->matchrv;
349 }
350
351 link = link->next;
352 }
353
354 auth_match = new acl_proxy_auth_match_cache();
355 auth_match->matchrv = matchForCache (checklist);
356 auth_match->acl_data = this;
357 dlinkAddTail(auth_match, &auth_match->link, cache);
358 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
359 return auth_match->matchrv;
360 }
361
362 void
363 aclCacheMatchFlush(dlink_list * cache)
364 {
365 acl_proxy_auth_match_cache *auth_match;
366 dlink_node *link, *tmplink;
367 link = cache->head;
368
369 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
370
371 while (link) {
372 auth_match = (acl_proxy_auth_match_cache *)link->data;
373 tmplink = link;
374 link = link->next;
375 dlinkDelete(tmplink, cache);
376 delete auth_match;
377 }
378 }
379
380 bool
381 ACL::requiresReply() const
382 {
383 return false;
384 }
385
386 bool
387 ACL::requiresRequest() const
388 {
389 return false;
390 }
391
392 /*********************/
393 /* Destroy functions */
394 /*********************/
395
396 ACL::~ACL()
397 {
398 debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
399 safe_free(cfgline);
400 AclMatchedName = NULL; // in case it was pointing to our name
401 }
402
403 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
404
405 ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
406 {
407 registerMe ();
408 }
409
410 std::vector<ACL::Prototype const *> * ACL::Prototype::Registry;
411 void *ACL::Prototype::Initialized;
412
413 bool
414 ACL::Prototype::Registered(char const *aType)
415 {
416 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
417
418 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
419 if (!strcmp (aType, (*i)->typeString)) {
420 debugs(28, 7, "ACL::Prototype::Registered: yes");
421 return true;
422 }
423
424 debugs(28, 7, "ACL::Prototype::Registered: no");
425 return false;
426 }
427
428 void
429 ACL::Prototype::registerMe ()
430 {
431 if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
432 /* TODO: extract this */
433 /* Not initialised */
434 Registry = new std::vector<ACL::Prototype const *>;
435 Initialized = (char *)Registry - 5;
436 }
437
438 if (Registered (typeString))
439 fatalf ("Attempt to register %s twice", typeString);
440
441 Registry->push_back (this);
442 }
443
444 ACL::Prototype::~Prototype()
445 {
446 // TODO: unregister me
447 }
448
449 ACL *
450 ACL::Prototype::Factory (char const *typeToClone)
451 {
452 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
453
454 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
455 if (!strcmp (typeToClone, (*i)->typeString)) {
456 ACL *A = (*i)->prototype->clone();
457 A->flags = (*i)->prototype->flags;
458 return A;
459 }
460
461 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
462
463 return NULL;
464 }
465
466 void
467 ACL::Initialize()
468 {
469 ACL *a = Config.aclList;
470 debugs(53, 3, "ACL::Initialize");
471
472 while (a) {
473 a->prepareForUse();
474 a = a->next;
475 }
476 }