]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Acl.cc
Merged from trunk 13172.
[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 "anyp/PortCfg.h"
35 #include "cache_cf.h"
36 #include "ConfigParser.h"
37 #include "Debug.h"
38 #include "dlink.h"
39 #include "globals.h"
40 #include "profiler/Profiler.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()
56 {
57 char *nextToken;
58 while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') {
59 (void)ConfigParser::NextToken(); //Get token from cfg line
60 //if token is the "--" break flag
61 if (strcmp(nextToken, "--") == 0)
62 break;
63
64 for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
65 if (supported(*flg)) {
66 makeSet(*flg);
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::TokenPutBack("-i");
77 }
78
79 const char *
80 ACLFlags::flagsStr() const
81 {
82 static char buf[64];
83 if (flags_ == 0)
84 return "";
85
86 char *s = buf;
87 *s++ = '-';
88 for (ACLFlag f = 'A'; f <= 'z'; f++) {
89 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
90 if (isSet(f) && f != ACL_F_REGEX_CASE)
91 *s++ = f;
92 }
93 *s = '\0';
94 return buf;
95 }
96
97 void *
98 ACL::operator new (size_t byteCount)
99 {
100 fatal ("unusable ACL::new");
101 return (void *)1;
102 }
103
104 void
105 ACL::operator delete (void *address)
106 {
107 fatal ("unusable ACL::delete");
108 }
109
110 ACL *
111 ACL::FindByName(const char *name)
112 {
113 ACL *a;
114 debugs(28, 9, "ACL::FindByName '" << name << "'");
115
116 for (a = Config.aclList; a; a = a->next)
117 if (!strcasecmp(a->name, name))
118 return a;
119
120 debugs(28, 9, "ACL::FindByName found no match");
121
122 return NULL;
123 }
124
125 ACL *
126 ACL::Factory (char const *type)
127 {
128 ACL *result = Prototype::Factory (type);
129
130 if (!result)
131 fatal ("Unknown acl type in ACL::Factory");
132
133 return result;
134 }
135
136 ACL::ACL() :
137 cfgline(NULL),
138 next(NULL),
139 registered(false)
140 {
141 *name = 0;
142 }
143
144 bool ACL::valid () const
145 {
146 return true;
147 }
148
149 bool
150 ACL::matches(ACLChecklist *checklist) const
151 {
152 PROF_start(ACL_matches);
153 debugs(28, 5, "checking " << name);
154
155 // XXX: AclMatchedName does not contain a matched ACL name when the acl
156 // does not match. It contains the last (usually leaf) ACL name checked
157 // (or is NULL if no ACLs were checked).
158 AclMatchedName = name;
159
160 int result = 0;
161 if (!checklist->hasRequest() && requiresRequest()) {
162 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
163 "context without an HTTP request. Assuming mismatch.");
164 } else if (!checklist->hasReply() && requiresReply()) {
165 debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
166 "context without an HTTP response. Assuming mismatch.");
167 } else {
168 // have to cast because old match() API is missing const
169 result = const_cast<ACL*>(this)->match(checklist);
170 }
171
172 const char *extra = checklist->asyncInProgress() ? " async" : "";
173 debugs(28, 3, "checked: " << name << " = " << result << extra);
174 PROF_stop(ACL_matches);
175 return result == 1; // true for match; false for everything else
176 }
177
178 void
179 ACL::context(const char *aName, const char *aCfgLine)
180 {
181 name[0] = '\0';
182 if (aName)
183 xstrncpy(name, aName, ACL_NAME_SZ-1);
184 safe_free(cfgline);
185 if (aCfgLine)
186 cfgline = xstrdup(aCfgLine);
187 }
188
189 void
190 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
191 {
192 /* we're already using strtok() to grok the line */
193 char *t = NULL;
194 ACL *A = NULL;
195 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
196 int new_acl = 0;
197
198 /* snarf the ACL name */
199
200 if ((t = ConfigParser::NextToken()) == NULL) {
201 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
202 parser.destruct();
203 return;
204 }
205
206 if (strlen(t) >= ACL_NAME_SZ) {
207 debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t <<
208 "' too long, max " << ACL_NAME_SZ - 1 << " characters supported");
209 parser.destruct();
210 return;
211 }
212
213 xstrncpy(aclname, t, ACL_NAME_SZ);
214 /* snarf the ACL type */
215 const char *theType;
216
217 if ((theType = ConfigParser::NextToken()) == NULL) {
218 debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
219 parser.destruct();
220 return;
221 }
222
223 // Is this ACL going to work?
224 if (strcmp(theType, "myip") == 0) {
225 AnyP::PortCfg *p = Config.Sockaddr.http;
226 while (p) {
227 // Bug 3239: not reliable when there is interception traffic coming
228 if (p->flags.natIntercept)
229 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
230 p = p->next;
231 }
232 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
233 theType = "localip";
234 } else if (strcmp(theType, "myport") == 0) {
235 AnyP::PortCfg *p = Config.Sockaddr.http;
236 while (p) {
237 // Bug 3239: not reliable when there is interception traffic coming
238 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
239 if (p->flags.natIntercept)
240 debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
241 p = p->next;
242 }
243 theType = "localport";
244 debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
245 }
246
247 if (!Prototype::Registered(theType)) {
248 debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
249 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
250 parser.destruct();
251 return;
252 }
253
254 if ((A = FindByName(aclname)) == NULL) {
255 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
256 A = ACL::Factory(theType);
257 A->context(aclname, config_input_line);
258 new_acl = 1;
259 } else {
260 if (strcmp (A->typeString(),theType) ) {
261 debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type.");
262 parser.destruct();
263 return;
264 }
265
266 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'");
267 new_acl = 0;
268 }
269
270 /*
271 * Here we set AclMatchedName in case we need to use it in a
272 * warning message in aclDomainCompare().
273 */
274 AclMatchedName = A->name; /* ugly */
275
276 A->flags.parseFlags();
277
278 /*split the function here */
279 A->parse();
280
281 /*
282 * Clear AclMatchedName from our temporary hack
283 */
284 AclMatchedName = NULL; /* ugly */
285
286 if (!new_acl)
287 return;
288
289 if (A->empty()) {
290 debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
291 }
292
293 if (!A->valid()) {
294 fatalf("ERROR: Invalid ACL: %s\n",
295 A->cfgline);
296 }
297
298 // prepend so that ACLs declared later (and possibly using earlier ACLs)
299 // are destroyed earlier (before the ACLs they use are destroyed)
300 assert(head && *head == Config.aclList);
301 A->registered = true;
302 A->next = *head;
303 *head = A;
304 }
305
306 bool
307 ACL::isProxyAuth() const
308 {
309 return false;
310 }
311
312 /* ACL result caching routines */
313
314 int
315 ACL::matchForCache(ACLChecklist *checklist)
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");
320 return 0; /* NOTREACHED */
321 }
322
323 /*
324 * we lookup an acl's cached results, and if we cannot find the acl being
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
328 * wise in long lived caches (i.e. the auth_user proxy match cache)
329 * RBC
330 * TODO: does a dlink_list perform well enough? Kinkie
331 */
332 int
333 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
334 {
335 acl_proxy_auth_match_cache *auth_match;
336 dlink_node *link;
337 link = cache->head;
338
339 while (link) {
340 auth_match = (acl_proxy_auth_match_cache *)link->data;
341
342 if (auth_match->acl_data == this) {
343 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")");
344 return auth_match->matchrv;
345 }
346
347 link = link->next;
348 }
349
350 auth_match = new acl_proxy_auth_match_cache();
351 auth_match->matchrv = matchForCache (checklist);
352 auth_match->acl_data = this;
353 dlinkAddTail(auth_match, &auth_match->link, cache);
354 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
355 return auth_match->matchrv;
356 }
357
358 void
359 aclCacheMatchFlush(dlink_list * cache)
360 {
361 acl_proxy_auth_match_cache *auth_match;
362 dlink_node *link, *tmplink;
363 link = cache->head;
364
365 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache);
366
367 while (link) {
368 auth_match = (acl_proxy_auth_match_cache *)link->data;
369 tmplink = link;
370 link = link->next;
371 dlinkDelete(tmplink, cache);
372 delete auth_match;
373 }
374 }
375
376 bool
377 ACL::requiresReply() const
378 {
379 return false;
380 }
381
382 bool
383 ACL::requiresRequest() const
384 {
385 return false;
386 }
387
388 /*********************/
389 /* Destroy functions */
390 /*********************/
391
392 ACL::~ACL()
393 {
394 debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
395 safe_free(cfgline);
396 AclMatchedName = NULL; // in case it was pointing to our name
397 }
398
399 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
400
401 ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
402 {
403 registerMe ();
404 }
405
406 Vector<ACL::Prototype const *> * ACL::Prototype::Registry;
407 void *ACL::Prototype::Initialized;
408
409 bool
410 ACL::Prototype::Registered(char const *aType)
411 {
412 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
413
414 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
415 if (!strcmp (aType, (*i)->typeString)) {
416 debugs(28, 7, "ACL::Prototype::Registered: yes");
417 return true;
418 }
419
420 debugs(28, 7, "ACL::Prototype::Registered: no");
421 return false;
422 }
423
424 void
425 ACL::Prototype::registerMe ()
426 {
427 if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
428 /* TODO: extract this */
429 /* Not initialised */
430 Registry = new Vector <ACL::Prototype const *>;
431 Initialized = (char *)Registry - 5;
432 }
433
434 if (Registered (typeString))
435 fatalf ("Attempt to register %s twice", typeString);
436
437 Registry->push_back (this);
438 }
439
440 ACL::Prototype::~Prototype()
441 {
442 // TODO: unregister me
443 }
444
445 ACL *
446 ACL::Prototype::Factory (char const *typeToClone)
447 {
448 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
449
450 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
451 if (!strcmp (typeToClone, (*i)->typeString)) {
452 ACL *A = (*i)->prototype->clone();
453 A->flags = (*i)->prototype->flags;
454 return A;
455 }
456
457 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
458
459 return NULL;
460 }
461
462 void
463 ACL::Initialize()
464 {
465 ACL *a = Config.aclList;
466 debugs(53, 3, "ACL::Initialize");
467
468 while (a) {
469 a->prepareForUse();
470 a = a->next;
471 }
472 }