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