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