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