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