]>
Commit | Line | Data |
---|---|---|
8213067d | 1 | /* |
262a0e14 | 2 | * $Id$ |
30a4f2a8 | 3 | * |
4 | * DEBUG: section 28 Access Control | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 8 | * ---------------------------------------------------------- |
30a4f2a8 | 9 | * |
2b6662ba | 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. | |
30a4f2a8 | 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. | |
26ac0430 | 23 | * |
30a4f2a8 | 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. | |
26ac0430 | 28 | * |
30a4f2a8 | 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 | |
cbdec147 | 31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 32 | * |
8213067d | 33 | */ |
f7f3304a | 34 | #include "squid.h" |
c15d448c AR |
35 | #include "acl/Acl.h" |
36 | #include "acl/Checklist.h" | |
d295d770 | 37 | #include "ConfigParser.h" |
38dedeed | 38 | #include "Debug.h" |
e1f7507e | 39 | #include "dlink.h" |
65d448bc | 40 | #include "anyp/PortCfg.h" |
23351cb2 | 41 | |
b0dd28ba | 42 | const char *AclMatchedName = NULL; |
43 | ||
8000a965 | 44 | void * |
45 | ACL::operator new (size_t byteCount) | |
46 | { | |
b0dd28ba | 47 | fatal ("unusable ACL::new"); |
48 | return (void *)1; | |
8000a965 | 49 | } |
50 | ||
51 | void | |
52 | ACL::operator delete (void *address) | |
53 | { | |
b0dd28ba | 54 | fatal ("unusable ACL::delete"); |
56b63fa1 | 55 | } |
56 | ||
97427e90 | 57 | ACL * |
225b7b10 | 58 | ACL::FindByName(const char *name) |
7dd57fa2 | 59 | { |
97427e90 | 60 | ACL *a; |
bf8fe701 | 61 | debugs(28, 9, "ACL::FindByName '" << name << "'"); |
62e76326 | 62 | |
9bea1d5b | 63 | for (a = Config.aclList; a; a = a->next) |
62e76326 | 64 | if (!strcasecmp(a->name, name)) |
65 | return a; | |
66 | ||
bf8fe701 | 67 | debugs(28, 9, "ACL::FindByName found no match"); |
6bf4f823 | 68 | |
9bea1d5b | 69 | return NULL; |
7dd57fa2 | 70 | } |
71 | ||
8000a965 | 72 | ACL * |
73 | ACL::Factory (char const *type) | |
74 | { | |
75 | ACL *result = Prototype::Factory (type); | |
62e76326 | 76 | |
b0dd28ba | 77 | if (!result) |
62e76326 | 78 | fatal ("Unknown acl type in ACL::Factory"); |
62e76326 | 79 | |
8000a965 | 80 | return result; |
81 | } | |
82 | ||
d295d770 | 83 | ACL::ACL () :cfgline(NULL) {} |
8000a965 | 84 | |
4b0f5de8 | 85 | bool ACL::valid () const |
86 | { | |
87 | return true; | |
88 | } | |
89 | ||
8203a132 | 90 | void |
a9f20260 | 91 | ACL::ParseAclLine(ConfigParser &parser, ACL ** head) |
9bea1d5b | 92 | { |
93 | /* we're already using strtok() to grok the line */ | |
94 | char *t = NULL; | |
97427e90 | 95 | ACL *A = NULL; |
9bea1d5b | 96 | LOCAL_ARRAY(char, aclname, ACL_NAME_SZ); |
9bea1d5b | 97 | int new_acl = 0; |
98 | ||
99 | /* snarf the ACL name */ | |
62e76326 | 100 | |
9bea1d5b | 101 | if ((t = strtok(NULL, w_space)) == NULL) { |
bf8fe701 | 102 | debugs(28, 0, "aclParseAclLine: missing ACL name."); |
a9f20260 | 103 | parser.destruct(); |
62e76326 | 104 | return; |
9bea1d5b | 105 | } |
62e76326 | 106 | |
401f503a | 107 | if (strlen(t) >= ACL_NAME_SZ) { |
bf8fe701 | 108 | debugs(28, 0, "aclParseAclLine: aclParseAclLine: ACL name '" << t << |
109 | "' too long, max " << ACL_NAME_SZ - 1 << " characters supported"); | |
401f503a | 110 | parser.destruct(); |
111 | return; | |
112 | } | |
113 | ||
9bea1d5b | 114 | xstrncpy(aclname, t, ACL_NAME_SZ); |
115 | /* snarf the ACL type */ | |
38dedeed | 116 | const char *theType; |
62e76326 | 117 | |
b0dd28ba | 118 | if ((theType = strtok(NULL, w_space)) == NULL) { |
bf8fe701 | 119 | debugs(28, 0, "aclParseAclLine: missing ACL type."); |
a9f20260 | 120 | parser.destruct(); |
62e76326 | 121 | return; |
9bea1d5b | 122 | } |
62e76326 | 123 | |
8306968f | 124 | // Is this ACL going to work? |
18c41901 | 125 | if (strcmp(theType, "myip") == 0) { |
65d448bc | 126 | AnyP::PortCfg *p = Config.Sockaddr.http; |
a9336c23 | 127 | while (p) { |
8306968f AJ |
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 | } | |
38dedeed | 133 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to."); |
1e40905d | 134 | theType = "localip"; |
18c41901 | 135 | } else if (strcmp(theType, "myport") == 0) { |
65d448bc | 136 | AnyP::PortCfg *p = Config.Sockaddr.http; |
a9336c23 | 137 | while (p) { |
8306968f AJ |
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 | } | |
1e40905d | 144 | theType = "localport"; |
38dedeed | 145 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to."); |
1e40905d AJ |
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; | |
8306968f AJ |
153 | } |
154 | ||
225b7b10 | 155 | if ((A = FindByName(aclname)) == NULL) { |
bf8fe701 | 156 | debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'"); |
b0dd28ba | 157 | A = ACL::Factory(theType); |
62e76326 | 158 | xstrncpy(A->name, aclname, ACL_NAME_SZ); |
159 | A->cfgline = xstrdup(config_input_line); | |
160 | new_acl = 1; | |
9bea1d5b | 161 | } else { |
b0dd28ba | 162 | if (strcmp (A->typeString(),theType) ) { |
bf8fe701 | 163 | debugs(28, 0, "aclParseAclLine: ACL '" << A->name << "' already exists with different type."); |
a9f20260 | 164 | parser.destruct(); |
62e76326 | 165 | return; |
166 | } | |
167 | ||
bf8fe701 | 168 | debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'"); |
62e76326 | 169 | new_acl = 0; |
9bea1d5b | 170 | } |
62e76326 | 171 | |
9bea1d5b | 172 | /* |
173 | * Here we set AclMatchedName in case we need to use it in a | |
174 | * warning message in aclDomainCompare(). | |
175 | */ | |
8000a965 | 176 | AclMatchedName = A->name; /* ugly */ |
177 | ||
178 | /*split the function here */ | |
179 | A->parse(); | |
62e76326 | 180 | |
8000a965 | 181 | /* |
182 | * Clear AclMatchedName from our temporary hack | |
183 | */ | |
184 | AclMatchedName = NULL; /* ugly */ | |
62e76326 | 185 | |
8000a965 | 186 | if (!new_acl) |
62e76326 | 187 | return; |
188 | ||
4b0f5de8 | 189 | if (A->empty()) { |
bf8fe701 | 190 | debugs(28, 0, "Warning: empty ACL: " << A->cfgline); |
4b0f5de8 | 191 | } |
192 | ||
193 | if (!A->valid()) { | |
194 | fatalf("ERROR: Invalid ACL: %s\n", | |
195 | A->cfgline); | |
8000a965 | 196 | } |
62e76326 | 197 | |
8000a965 | 198 | /* append */ |
199 | while (*head) | |
62e76326 | 200 | head = &(*head)->next; |
201 | ||
8000a965 | 202 | *head = A; |
203 | } | |
204 | ||
8000a965 | 205 | bool |
206 | ACL::isProxyAuth() const | |
207 | { | |
225b7b10 | 208 | return false; |
8000a965 | 209 | } |
1cfdbcf0 | 210 | |
8000a965 | 211 | |
212 | ACLList::ACLList() : op (1), _acl (NULL), next (NULL) | |
62e76326 | 213 | {} |
8000a965 | 214 | |
215 | void | |
216 | ACLList::negated(bool isNegated) | |
217 | { | |
218 | if (isNegated) | |
62e76326 | 219 | op = 0; |
8000a965 | 220 | else |
62e76326 | 221 | op = 1; |
d6827718 | 222 | } |
223 | ||
94439e4e | 224 | /* ACL result caching routines */ |
225 | ||
225b7b10 | 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 | ||
94439e4e | 235 | /* |
26ac0430 | 236 | * we lookup an acl's cached results, and if we cannot find the acl being |
225b7b10 | 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 | |
6bf4f823 | 240 | * wise in long lived caches (i.e. the auth_user proxy match cache) |
94439e4e | 241 | * RBC |
6bf4f823 | 242 | * TODO: does a dlink_list perform well enough? Kinkie |
94439e4e | 243 | */ |
225b7b10 | 244 | int |
245 | ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist) | |
9bea1d5b | 246 | { |
9bea1d5b | 247 | acl_proxy_auth_match_cache *auth_match; |
248 | dlink_node *link; | |
249 | link = cache->head; | |
62e76326 | 250 | |
9bea1d5b | 251 | while (link) { |
62e76326 | 252 | auth_match = (acl_proxy_auth_match_cache *)link->data; |
253 | ||
254 | if (auth_match->acl_data == this) { | |
bf8fe701 | 255 | debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")"); |
62e76326 | 256 | return auth_match->matchrv; |
257 | } | |
258 | ||
259 | link = link->next; | |
9bea1d5b | 260 | } |
62e76326 | 261 | |
11a9f1b1 | 262 | auth_match = new acl_proxy_auth_match_cache(); |
225b7b10 | 263 | auth_match->matchrv = matchForCache (checklist); |
264 | auth_match->acl_data = this; | |
9bea1d5b | 265 | dlinkAddTail(auth_match, &auth_match->link, cache); |
bf8fe701 | 266 | debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv); |
225b7b10 | 267 | return auth_match->matchrv; |
94439e4e | 268 | } |
269 | ||
270 | void | |
9bea1d5b | 271 | aclCacheMatchFlush(dlink_list * cache) |
94439e4e | 272 | { |
9bea1d5b | 273 | acl_proxy_auth_match_cache *auth_match; |
274 | dlink_node *link, *tmplink; | |
275 | link = cache->head; | |
62e76326 | 276 | |
bf8fe701 | 277 | debugs(28, 8, "aclCacheMatchFlush called for cache " << cache); |
6bf4f823 | 278 | |
9bea1d5b | 279 | while (link) { |
62e76326 | 280 | auth_match = (acl_proxy_auth_match_cache *)link->data; |
281 | tmplink = link; | |
282 | link = link->next; | |
283 | dlinkDelete(tmplink, cache); | |
11a9f1b1 | 284 | delete auth_match; |
1f38f50a | 285 | } |
c68e9c6b | 286 | } |
73e67ee0 | 287 | |
b0dd28ba | 288 | bool |
289 | ACL::requiresReply() const | |
290 | { | |
291 | return false; | |
292 | } | |
60d096f4 | 293 | |
b0dd28ba | 294 | bool |
295 | ACL::requiresRequest() const | |
9bea1d5b | 296 | { |
b0dd28ba | 297 | return false; |
298 | } | |
62e76326 | 299 | |
b0dd28ba | 300 | int |
301 | ACL::checklistMatches(ACLChecklist *checklist) | |
302 | { | |
6bf4f823 | 303 | int rv; |
304 | ||
c15d448c | 305 | if (!checklist->hasRequest() && requiresRequest()) { |
bf8fe701 | 306 | debugs(28, 1, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP request -- not matching."); |
b0dd28ba | 307 | return 0; |
d35b9a94 | 308 | } |
62e76326 | 309 | |
c15d448c | 310 | if (!checklist->hasReply() && requiresReply()) { |
bf8fe701 | 311 | debugs(28, 1, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP reply -- not matching."); |
b0dd28ba | 312 | return 0; |
78e0ce43 | 313 | } |
62e76326 | 314 | |
bf8fe701 | 315 | debugs(28, 3, "ACL::checklistMatches: checking '" << name << "'"); |
6bf4f823 | 316 | rv= match(checklist); |
bf8fe701 | 317 | debugs(28, 3, "ACL::ChecklistMatches: result for '" << name << "' is " << rv); |
6bf4f823 | 318 | return rv; |
60d096f4 | 319 | } |
c68e9c6b | 320 | |
b0dd28ba | 321 | bool |
322 | ACLList::matches (ACLChecklist *checklist) const | |
60d096f4 | 323 | { |
b0dd28ba | 324 | assert (_acl); |
e0f7153c | 325 | // XXX: AclMatchedName does not contain a matched ACL name when the acl |
c0f81932 | 326 | // does not match (or contains stale name if no ACLs are checked). In |
e0f7153c AR |
327 | // either case, we get misleading debugging and possibly incorrect error |
328 | // messages. Unfortunately, deny_info's "when none http_access | |
329 | // lines match" exception essentially requires this mess. | |
330 | // TODO: Rework by using an acl-free deny_info for the no-match cases? | |
b0dd28ba | 331 | AclMatchedName = _acl->name; |
bf8fe701 | 332 | debugs(28, 3, "ACLList::matches: checking " << (op ? null_string : "!") << _acl->name); |
62e76326 | 333 | |
b0dd28ba | 334 | if (_acl->checklistMatches(checklist) != op) { |
bf8fe701 | 335 | debugs(28, 4, "ACLList::matches: result is false"); |
e0f7153c | 336 | return false; |
b0dd28ba | 337 | } |
62e76326 | 338 | |
bf8fe701 | 339 | debugs(28, 4, "ACLList::matches: result is true"); |
e0f7153c | 340 | return true; |
60d096f4 | 341 | } |
94439e4e | 342 | |
348b2031 | 343 | |
f32789f4 | 344 | /*********************/ |
345 | /* Destroy functions */ | |
346 | /*********************/ | |
347 | ||
8000a965 | 348 | ACL::~ACL() |
349 | { | |
bf8fe701 | 350 | debugs(28, 3, "ACL::~ACL: '" << cfgline << "'"); |
62e76326 | 351 | safe_free(cfgline); |
92a6f4b1 | 352 | } |
353 | ||
29b17d63 | 354 | /* to be split into separate files in the future */ |
355 | ||
8000a965 | 356 | CBDATA_CLASS_INIT(acl_access); |
357 | ||
358 | void * | |
359 | acl_access::operator new (size_t) | |
360 | { | |
361 | CBDATA_INIT_TYPE(acl_access); | |
362 | acl_access *result = cbdataAlloc(acl_access); | |
363 | return result; | |
364 | } | |
365 | ||
366 | void | |
367 | acl_access::operator delete (void *address) | |
368 | { | |
369 | acl_access *t = static_cast<acl_access *>(address); | |
370 | cbdataFree(t); | |
371 | } | |
372 | ||
8000a965 | 373 | ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {} |
374 | ||
62e76326 | 375 | ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType) |
8000a965 | 376 | { |
377 | registerMe (); | |
378 | } | |
379 | ||
380 | Vector<ACL::Prototype const *> * ACL::Prototype::Registry; | |
381 | void *ACL::Prototype::Initialized; | |
382 | ||
383 | bool | |
62e76326 | 384 | ACL::Prototype::Registered(char const *aType) |
8000a965 | 385 | { |
bf8fe701 | 386 | debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType); |
6bf4f823 | 387 | |
8000a965 | 388 | for (iterator i = Registry->begin(); i != Registry->end(); ++i) |
6bf4f823 | 389 | if (!strcmp (aType, (*i)->typeString)) { |
bf8fe701 | 390 | debugs(28, 7, "ACL::Prototype::Registered: yes"); |
62e76326 | 391 | return true; |
6bf4f823 | 392 | } |
62e76326 | 393 | |
bf8fe701 | 394 | debugs(28, 7, "ACL::Prototype::Registered: no"); |
8000a965 | 395 | return false; |
396 | } | |
397 | ||
398 | void | |
399 | ACL::Prototype::registerMe () | |
400 | { | |
401 | if (!Registry || (Initialized != ((char *)Registry - 5)) ) { | |
62e76326 | 402 | /* TODO: extract this */ |
403 | /* Not initialised */ | |
404 | Registry = new Vector <ACL::Prototype const *>; | |
405 | Initialized = (char *)Registry - 5; | |
8000a965 | 406 | } |
62e76326 | 407 | |
8000a965 | 408 | if (Registered (typeString)) |
62e76326 | 409 | fatalf ("Attempt to register %s twice", typeString); |
410 | ||
8000a965 | 411 | Registry->push_back (this); |
412 | } | |
413 | ||
62e76326 | 414 | ACL::Prototype::~Prototype() |
8000a965 | 415 | { |
67b7fa0d | 416 | // TODO: unregister me |
8000a965 | 417 | } |
418 | ||
419 | ACL * | |
420 | ACL::Prototype::Factory (char const *typeToClone) | |
421 | { | |
bf8fe701 | 422 | debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'"); |
6bf4f823 | 423 | |
8000a965 | 424 | for (iterator i = Registry->begin(); i != Registry->end(); ++i) |
62e76326 | 425 | if (!strcmp (typeToClone, (*i)->typeString)) |
426 | return (*i)->prototype->clone(); | |
427 | ||
bf8fe701 | 428 | debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available"); |
6bf4f823 | 429 | |
8000a965 | 430 | return NULL; |
431 | } | |
432 | ||
b0dd28ba | 433 | void |
434 | ACL::Initialize() | |
8000a965 | 435 | { |
97427e90 | 436 | ACL *a = Config.aclList; |
bf8fe701 | 437 | debugs(53, 3, "ACL::Initialize"); |
b0dd28ba | 438 | |
439 | while (a) { | |
440 | a->prepareForUse(); | |
441 | a = a->next; | |
442 | } | |
8000a965 | 443 | } |