]>
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" | |
582c2af2 | 37 | #include "anyp/PortCfg.h" |
d295d770 | 38 | #include "ConfigParser.h" |
38dedeed | 39 | #include "Debug.h" |
e1f7507e | 40 | #include "dlink.h" |
582c2af2 | 41 | #include "globals.h" |
23351cb2 | 42 | |
b0dd28ba | 43 | const char *AclMatchedName = NULL; |
44 | ||
8000a965 | 45 | void * |
46 | ACL::operator new (size_t byteCount) | |
47 | { | |
b0dd28ba | 48 | fatal ("unusable ACL::new"); |
49 | return (void *)1; | |
8000a965 | 50 | } |
51 | ||
52 | void | |
53 | ACL::operator delete (void *address) | |
54 | { | |
b0dd28ba | 55 | fatal ("unusable ACL::delete"); |
56b63fa1 | 56 | } |
57 | ||
97427e90 | 58 | ACL * |
225b7b10 | 59 | ACL::FindByName(const char *name) |
7dd57fa2 | 60 | { |
97427e90 | 61 | ACL *a; |
bf8fe701 | 62 | debugs(28, 9, "ACL::FindByName '" << name << "'"); |
62e76326 | 63 | |
9bea1d5b | 64 | for (a = Config.aclList; a; a = a->next) |
62e76326 | 65 | if (!strcasecmp(a->name, name)) |
66 | return a; | |
67 | ||
bf8fe701 | 68 | debugs(28, 9, "ACL::FindByName found no match"); |
6bf4f823 | 69 | |
9bea1d5b | 70 | return NULL; |
7dd57fa2 | 71 | } |
72 | ||
8000a965 | 73 | ACL * |
74 | ACL::Factory (char const *type) | |
75 | { | |
76 | ACL *result = Prototype::Factory (type); | |
62e76326 | 77 | |
b0dd28ba | 78 | if (!result) |
62e76326 | 79 | fatal ("Unknown acl type in ACL::Factory"); |
62e76326 | 80 | |
8000a965 | 81 | return result; |
82 | } | |
83 | ||
d295d770 | 84 | ACL::ACL () :cfgline(NULL) {} |
8000a965 | 85 | |
4b0f5de8 | 86 | bool ACL::valid () const |
87 | { | |
88 | return true; | |
89 | } | |
90 | ||
8203a132 | 91 | void |
a9f20260 | 92 | ACL::ParseAclLine(ConfigParser &parser, ACL ** head) |
9bea1d5b | 93 | { |
94 | /* we're already using strtok() to grok the line */ | |
95 | char *t = NULL; | |
97427e90 | 96 | ACL *A = NULL; |
9bea1d5b | 97 | LOCAL_ARRAY(char, aclname, ACL_NAME_SZ); |
9bea1d5b | 98 | int new_acl = 0; |
99 | ||
100 | /* snarf the ACL name */ | |
62e76326 | 101 | |
9bea1d5b | 102 | if ((t = strtok(NULL, w_space)) == NULL) { |
fa84c01d | 103 | debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name."); |
a9f20260 | 104 | parser.destruct(); |
62e76326 | 105 | return; |
9bea1d5b | 106 | } |
62e76326 | 107 | |
401f503a | 108 | if (strlen(t) >= ACL_NAME_SZ) { |
fa84c01d | 109 | debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t << |
bf8fe701 | 110 | "' too long, max " << ACL_NAME_SZ - 1 << " characters supported"); |
401f503a | 111 | parser.destruct(); |
112 | return; | |
113 | } | |
114 | ||
9bea1d5b | 115 | xstrncpy(aclname, t, ACL_NAME_SZ); |
116 | /* snarf the ACL type */ | |
38dedeed | 117 | const char *theType; |
62e76326 | 118 | |
b0dd28ba | 119 | if ((theType = strtok(NULL, w_space)) == NULL) { |
fa84c01d | 120 | debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type."); |
a9f20260 | 121 | parser.destruct(); |
62e76326 | 122 | return; |
9bea1d5b | 123 | } |
62e76326 | 124 | |
8306968f | 125 | // Is this ACL going to work? |
18c41901 | 126 | if (strcmp(theType, "myip") == 0) { |
65d448bc | 127 | AnyP::PortCfg *p = Config.Sockaddr.http; |
a9336c23 | 128 | while (p) { |
8306968f AJ |
129 | // Bug 3239: not reliable when there is interception traffic coming |
130 | if (p->intercepted) | |
131 | debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead."); | |
132 | p = p->next; | |
133 | } | |
38dedeed | 134 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to."); |
1e40905d | 135 | theType = "localip"; |
18c41901 | 136 | } else if (strcmp(theType, "myport") == 0) { |
65d448bc | 137 | AnyP::PortCfg *p = Config.Sockaddr.http; |
a9336c23 | 138 | while (p) { |
8306968f AJ |
139 | // Bug 3239: not reliable when there is interception traffic coming |
140 | // Bug 3239: myport - not reliable (yet) when there is interception traffic coming | |
141 | if (p->intercepted) | |
142 | debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead."); | |
143 | p = p->next; | |
144 | } | |
1e40905d | 145 | theType = "localport"; |
38dedeed | 146 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to."); |
1e40905d AJ |
147 | } |
148 | ||
149 | if (!Prototype::Registered(theType)) { | |
150 | debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'"); | |
151 | // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not. | |
152 | parser.destruct(); | |
153 | return; | |
8306968f AJ |
154 | } |
155 | ||
225b7b10 | 156 | if ((A = FindByName(aclname)) == NULL) { |
bf8fe701 | 157 | debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'"); |
b0dd28ba | 158 | A = ACL::Factory(theType); |
62e76326 | 159 | xstrncpy(A->name, aclname, ACL_NAME_SZ); |
160 | A->cfgline = xstrdup(config_input_line); | |
161 | new_acl = 1; | |
9bea1d5b | 162 | } else { |
b0dd28ba | 163 | if (strcmp (A->typeString(),theType) ) { |
fa84c01d | 164 | debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type."); |
a9f20260 | 165 | parser.destruct(); |
62e76326 | 166 | return; |
167 | } | |
168 | ||
bf8fe701 | 169 | debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'"); |
62e76326 | 170 | new_acl = 0; |
9bea1d5b | 171 | } |
62e76326 | 172 | |
9bea1d5b | 173 | /* |
174 | * Here we set AclMatchedName in case we need to use it in a | |
175 | * warning message in aclDomainCompare(). | |
176 | */ | |
8000a965 | 177 | AclMatchedName = A->name; /* ugly */ |
178 | ||
179 | /*split the function here */ | |
180 | A->parse(); | |
62e76326 | 181 | |
8000a965 | 182 | /* |
183 | * Clear AclMatchedName from our temporary hack | |
184 | */ | |
185 | AclMatchedName = NULL; /* ugly */ | |
62e76326 | 186 | |
8000a965 | 187 | if (!new_acl) |
62e76326 | 188 | return; |
189 | ||
4b0f5de8 | 190 | if (A->empty()) { |
fa84c01d | 191 | debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline); |
4b0f5de8 | 192 | } |
193 | ||
194 | if (!A->valid()) { | |
195 | fatalf("ERROR: Invalid ACL: %s\n", | |
196 | A->cfgline); | |
8000a965 | 197 | } |
62e76326 | 198 | |
8000a965 | 199 | /* append */ |
200 | while (*head) | |
62e76326 | 201 | head = &(*head)->next; |
202 | ||
8000a965 | 203 | *head = A; |
204 | } | |
205 | ||
8000a965 | 206 | bool |
207 | ACL::isProxyAuth() const | |
208 | { | |
225b7b10 | 209 | return false; |
8000a965 | 210 | } |
1cfdbcf0 | 211 | |
8000a965 | 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()) { |
e0236918 | 306 | debugs(28, DBG_IMPORTANT, "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()) { |
e0236918 | 311 | debugs(28, DBG_IMPORTANT, "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 | |
f32789f4 | 343 | /*********************/ |
344 | /* Destroy functions */ | |
345 | /*********************/ | |
346 | ||
8000a965 | 347 | ACL::~ACL() |
348 | { | |
bf8fe701 | 349 | debugs(28, 3, "ACL::~ACL: '" << cfgline << "'"); |
62e76326 | 350 | safe_free(cfgline); |
92a6f4b1 | 351 | } |
352 | ||
29b17d63 | 353 | /* to be split into separate files in the future */ |
354 | ||
8000a965 | 355 | CBDATA_CLASS_INIT(acl_access); |
356 | ||
357 | void * | |
358 | acl_access::operator new (size_t) | |
359 | { | |
360 | CBDATA_INIT_TYPE(acl_access); | |
361 | acl_access *result = cbdataAlloc(acl_access); | |
362 | return result; | |
363 | } | |
364 | ||
365 | void | |
366 | acl_access::operator delete (void *address) | |
367 | { | |
368 | acl_access *t = static_cast<acl_access *>(address); | |
369 | cbdataFree(t); | |
370 | } | |
371 | ||
8000a965 | 372 | ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {} |
373 | ||
62e76326 | 374 | ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType) |
8000a965 | 375 | { |
376 | registerMe (); | |
377 | } | |
378 | ||
379 | Vector<ACL::Prototype const *> * ACL::Prototype::Registry; | |
380 | void *ACL::Prototype::Initialized; | |
381 | ||
382 | bool | |
62e76326 | 383 | ACL::Prototype::Registered(char const *aType) |
8000a965 | 384 | { |
bf8fe701 | 385 | debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType); |
6bf4f823 | 386 | |
8000a965 | 387 | for (iterator i = Registry->begin(); i != Registry->end(); ++i) |
6bf4f823 | 388 | if (!strcmp (aType, (*i)->typeString)) { |
bf8fe701 | 389 | debugs(28, 7, "ACL::Prototype::Registered: yes"); |
62e76326 | 390 | return true; |
6bf4f823 | 391 | } |
62e76326 | 392 | |
bf8fe701 | 393 | debugs(28, 7, "ACL::Prototype::Registered: no"); |
8000a965 | 394 | return false; |
395 | } | |
396 | ||
397 | void | |
398 | ACL::Prototype::registerMe () | |
399 | { | |
400 | if (!Registry || (Initialized != ((char *)Registry - 5)) ) { | |
62e76326 | 401 | /* TODO: extract this */ |
402 | /* Not initialised */ | |
403 | Registry = new Vector <ACL::Prototype const *>; | |
404 | Initialized = (char *)Registry - 5; | |
8000a965 | 405 | } |
62e76326 | 406 | |
8000a965 | 407 | if (Registered (typeString)) |
62e76326 | 408 | fatalf ("Attempt to register %s twice", typeString); |
409 | ||
8000a965 | 410 | Registry->push_back (this); |
411 | } | |
412 | ||
62e76326 | 413 | ACL::Prototype::~Prototype() |
8000a965 | 414 | { |
67b7fa0d | 415 | // TODO: unregister me |
8000a965 | 416 | } |
417 | ||
418 | ACL * | |
419 | ACL::Prototype::Factory (char const *typeToClone) | |
420 | { | |
bf8fe701 | 421 | debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'"); |
6bf4f823 | 422 | |
8000a965 | 423 | for (iterator i = Registry->begin(); i != Registry->end(); ++i) |
62e76326 | 424 | if (!strcmp (typeToClone, (*i)->typeString)) |
425 | return (*i)->prototype->clone(); | |
426 | ||
bf8fe701 | 427 | debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available"); |
6bf4f823 | 428 | |
8000a965 | 429 | return NULL; |
430 | } | |
431 | ||
b0dd28ba | 432 | void |
433 | ACL::Initialize() | |
8000a965 | 434 | { |
97427e90 | 435 | ACL *a = Config.aclList; |
bf8fe701 | 436 | debugs(53, 3, "ACL::Initialize"); |
b0dd28ba | 437 | |
438 | while (a) { | |
439 | a->prepareForUse(); | |
440 | a = a->next; | |
441 | } | |
8000a965 | 442 | } |