]>
Commit | Line | Data |
---|---|---|
8213067d | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 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" |
4eac3407 | 15 | #include "acl/Options.h" |
582c2af2 | 16 | #include "anyp/PortCfg.h" |
33810b1d | 17 | #include "cache_cf.h" |
d295d770 | 18 | #include "ConfigParser.h" |
38dedeed | 19 | #include "Debug.h" |
ed6e9fb9 | 20 | #include "fatal.h" |
582c2af2 | 21 | #include "globals.h" |
6f58d7d7 | 22 | #include "profiler/Profiler.h" |
4eac3407 CT |
23 | #include "sbuf/List.h" |
24 | #include "sbuf/Stream.h" | |
4d5904f7 | 25 | #include "SquidConfig.h" |
23351cb2 | 26 | |
4eac3407 CT |
27 | #include <algorithm> |
28 | #include <map> | |
33810b1d | 29 | |
b0dd28ba | 30 | const char *AclMatchedName = NULL; |
31 | ||
4eac3407 | 32 | namespace Acl { |
76ee67ac | 33 | |
4eac3407 CT |
34 | /// ACL type name comparison functor |
35 | class TypeNameCmp { | |
36 | public: | |
37 | bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; } | |
38 | }; | |
76ee67ac | 39 | |
4eac3407 CT |
40 | /// ACL makers indexed by ACL type name |
41 | typedef std::map<TypeName, Maker, TypeNameCmp> Makers; | |
76ee67ac | 42 | |
4eac3407 CT |
43 | /// registered ACL Makers |
44 | static Makers & | |
45 | TheMakers() | |
76ee67ac | 46 | { |
4eac3407 CT |
47 | static Makers Registry; |
48 | return Registry; | |
76ee67ac CT |
49 | } |
50 | ||
4eac3407 CT |
51 | /// creates an ACL object of the named (and already registered) ACL child type |
52 | static | |
53 | ACL * | |
54 | Make(TypeName typeName) | |
76ee67ac | 55 | { |
4eac3407 CT |
56 | const auto pos = TheMakers().find(typeName); |
57 | if (pos == TheMakers().end()) { | |
58 | debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << typeName << "'"); | |
59 | self_destruct(); | |
60 | assert(false); // not reached | |
75d47340 | 61 | } |
76ee67ac | 62 | |
4eac3407 CT |
63 | ACL *result = (pos->second)(pos->first); |
64 | debugs(28, 4, typeName << '=' << result); | |
65 | assert(result); | |
66 | return result; | |
76ee67ac CT |
67 | } |
68 | ||
4eac3407 | 69 | } // namespace Acl |
33810b1d | 70 | |
aec45181 | 71 | void |
4eac3407 | 72 | Acl::RegisterMaker(TypeName typeName, Maker maker) |
76ee67ac | 73 | { |
4eac3407 CT |
74 | assert(typeName); |
75 | assert(*typeName); | |
76 | TheMakers().emplace(typeName, maker); | |
33810b1d CT |
77 | } |
78 | ||
8000a965 | 79 | void * |
ced8def3 | 80 | ACL::operator new (size_t) |
8000a965 | 81 | { |
b0dd28ba | 82 | fatal ("unusable ACL::new"); |
83 | return (void *)1; | |
8000a965 | 84 | } |
85 | ||
86 | void | |
ced8def3 | 87 | ACL::operator delete (void *) |
8000a965 | 88 | { |
b0dd28ba | 89 | fatal ("unusable ACL::delete"); |
56b63fa1 | 90 | } |
91 | ||
97427e90 | 92 | ACL * |
225b7b10 | 93 | ACL::FindByName(const char *name) |
7dd57fa2 | 94 | { |
97427e90 | 95 | ACL *a; |
bf8fe701 | 96 | debugs(28, 9, "ACL::FindByName '" << name << "'"); |
62e76326 | 97 | |
9bea1d5b | 98 | for (a = Config.aclList; a; a = a->next) |
62e76326 | 99 | if (!strcasecmp(a->name, name)) |
100 | return a; | |
101 | ||
bf8fe701 | 102 | debugs(28, 9, "ACL::FindByName found no match"); |
6bf4f823 | 103 | |
9bea1d5b | 104 | return NULL; |
7dd57fa2 | 105 | } |
106 | ||
478a0611 | 107 | ACL::ACL() : |
d59e4742 FC |
108 | cfgline(nullptr), |
109 | next(nullptr), | |
110 | registered(false) | |
111 | { | |
112 | *name = 0; | |
113 | } | |
114 | ||
4b0f5de8 | 115 | bool ACL::valid () const |
116 | { | |
117 | return true; | |
118 | } | |
119 | ||
6f58d7d7 AR |
120 | bool |
121 | ACL::matches(ACLChecklist *checklist) const | |
122 | { | |
123 | PROF_start(ACL_matches); | |
124 | debugs(28, 5, "checking " << name); | |
125 | ||
126 | // XXX: AclMatchedName does not contain a matched ACL name when the acl | |
127 | // does not match. It contains the last (usually leaf) ACL name checked | |
128 | // (or is NULL if no ACLs were checked). | |
129 | AclMatchedName = name; | |
130 | ||
131 | int result = 0; | |
4ff6370b | 132 | if (!checklist->hasAle() && requiresAle()) { |
4e56d7f6 | 133 | debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << |
acfe8e09 | 134 | "context without an ALE state. Assuming mismatch."); |
4e56d7f6 | 135 | } else if (!checklist->hasRequest() && requiresRequest()) { |
6f58d7d7 AR |
136 | debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << |
137 | "context without an HTTP request. Assuming mismatch."); | |
138 | } else if (!checklist->hasReply() && requiresReply()) { | |
139 | debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << | |
140 | "context without an HTTP response. Assuming mismatch."); | |
141 | } else { | |
fbbea662 AJ |
142 | // make sure the ALE has as much data as possible |
143 | if (requiresAle()) | |
144 | checklist->syncAle(); | |
145 | ||
6f58d7d7 AR |
146 | // have to cast because old match() API is missing const |
147 | result = const_cast<ACL*>(this)->match(checklist); | |
148 | } | |
149 | ||
150 | const char *extra = checklist->asyncInProgress() ? " async" : ""; | |
151 | debugs(28, 3, "checked: " << name << " = " << result << extra); | |
152 | PROF_stop(ACL_matches); | |
153 | return result == 1; // true for match; false for everything else | |
154 | } | |
155 | ||
156 | void | |
157 | ACL::context(const char *aName, const char *aCfgLine) | |
158 | { | |
159 | name[0] = '\0'; | |
160 | if (aName) | |
161 | xstrncpy(name, aName, ACL_NAME_SZ-1); | |
162 | safe_free(cfgline); | |
163 | if (aCfgLine) | |
164 | cfgline = xstrdup(aCfgLine); | |
165 | } | |
166 | ||
8203a132 | 167 | void |
a9f20260 | 168 | ACL::ParseAclLine(ConfigParser &parser, ACL ** head) |
9bea1d5b | 169 | { |
170 | /* we're already using strtok() to grok the line */ | |
171 | char *t = NULL; | |
97427e90 | 172 | ACL *A = NULL; |
9bea1d5b | 173 | LOCAL_ARRAY(char, aclname, ACL_NAME_SZ); |
9bea1d5b | 174 | int new_acl = 0; |
175 | ||
176 | /* snarf the ACL name */ | |
62e76326 | 177 | |
2eceb328 | 178 | if ((t = ConfigParser::NextToken()) == NULL) { |
fa84c01d | 179 | debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name."); |
a9f20260 | 180 | parser.destruct(); |
62e76326 | 181 | return; |
9bea1d5b | 182 | } |
62e76326 | 183 | |
401f503a | 184 | if (strlen(t) >= ACL_NAME_SZ) { |
fa84c01d | 185 | debugs(28, DBG_CRITICAL, "aclParseAclLine: aclParseAclLine: ACL name '" << t << |
bf8fe701 | 186 | "' too long, max " << ACL_NAME_SZ - 1 << " characters supported"); |
401f503a | 187 | parser.destruct(); |
188 | return; | |
189 | } | |
190 | ||
9bea1d5b | 191 | xstrncpy(aclname, t, ACL_NAME_SZ); |
192 | /* snarf the ACL type */ | |
38dedeed | 193 | const char *theType; |
62e76326 | 194 | |
2eceb328 | 195 | if ((theType = ConfigParser::NextToken()) == NULL) { |
fa84c01d | 196 | debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type."); |
a9f20260 | 197 | parser.destruct(); |
62e76326 | 198 | return; |
9bea1d5b | 199 | } |
62e76326 | 200 | |
8306968f | 201 | // Is this ACL going to work? |
18c41901 | 202 | if (strcmp(theType, "myip") == 0) { |
fa720bfb AJ |
203 | AnyP::PortCfgPointer p = HttpPortList; |
204 | while (p != NULL) { | |
8306968f | 205 | // Bug 3239: not reliable when there is interception traffic coming |
6a25a046 | 206 | if (p->flags.natIntercept) |
8306968f AJ |
207 | debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead."); |
208 | p = p->next; | |
209 | } | |
38dedeed | 210 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to."); |
1e40905d | 211 | theType = "localip"; |
18c41901 | 212 | } else if (strcmp(theType, "myport") == 0) { |
fa720bfb AJ |
213 | AnyP::PortCfgPointer p = HttpPortList; |
214 | while (p != NULL) { | |
8306968f AJ |
215 | // Bug 3239: not reliable when there is interception traffic coming |
216 | // Bug 3239: myport - not reliable (yet) when there is interception traffic coming | |
6a25a046 | 217 | if (p->flags.natIntercept) |
8306968f AJ |
218 | debugs(28, DBG_CRITICAL, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead."); |
219 | p = p->next; | |
220 | } | |
1e40905d | 221 | theType = "localport"; |
38dedeed | 222 | debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to."); |
49549cd1 AJ |
223 | } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) { |
224 | // ACL manager is now a built-in and has a different type. | |
225 | debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file."); | |
226 | return; // ignore the line | |
1e40905d AJ |
227 | } |
228 | ||
225b7b10 | 229 | if ((A = FindByName(aclname)) == NULL) { |
bf8fe701 | 230 | debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'"); |
4eac3407 | 231 | A = Acl::Make(theType); |
6f58d7d7 | 232 | A->context(aclname, config_input_line); |
62e76326 | 233 | new_acl = 1; |
9bea1d5b | 234 | } else { |
b0dd28ba | 235 | if (strcmp (A->typeString(),theType) ) { |
fa84c01d | 236 | debugs(28, DBG_CRITICAL, "aclParseAclLine: ACL '" << A->name << "' already exists with different type."); |
a9f20260 | 237 | parser.destruct(); |
62e76326 | 238 | return; |
239 | } | |
240 | ||
bf8fe701 | 241 | debugs(28, 3, "aclParseAclLine: Appending to '" << aclname << "'"); |
62e76326 | 242 | new_acl = 0; |
9bea1d5b | 243 | } |
62e76326 | 244 | |
9bea1d5b | 245 | /* |
246 | * Here we set AclMatchedName in case we need to use it in a | |
247 | * warning message in aclDomainCompare(). | |
248 | */ | |
f53969cc | 249 | AclMatchedName = A->name; /* ugly */ |
8000a965 | 250 | |
4eac3407 | 251 | A->parseFlags(); |
33810b1d | 252 | |
8000a965 | 253 | /*split the function here */ |
254 | A->parse(); | |
62e76326 | 255 | |
8000a965 | 256 | /* |
257 | * Clear AclMatchedName from our temporary hack | |
258 | */ | |
f53969cc | 259 | AclMatchedName = NULL; /* ugly */ |
62e76326 | 260 | |
8000a965 | 261 | if (!new_acl) |
62e76326 | 262 | return; |
263 | ||
4b0f5de8 | 264 | if (A->empty()) { |
fa84c01d | 265 | debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline); |
4b0f5de8 | 266 | } |
267 | ||
268 | if (!A->valid()) { | |
269 | fatalf("ERROR: Invalid ACL: %s\n", | |
270 | A->cfgline); | |
8000a965 | 271 | } |
62e76326 | 272 | |
ed898bdf | 273 | // add to the global list for searching explicit ACLs by name |
6f58d7d7 | 274 | assert(head && *head == Config.aclList); |
a57c224d | 275 | A->next = *head; |
8000a965 | 276 | *head = A; |
ed898bdf AR |
277 | |
278 | // register for centralized cleanup | |
279 | aclRegister(A); | |
8000a965 | 280 | } |
281 | ||
8000a965 | 282 | bool |
283 | ACL::isProxyAuth() const | |
284 | { | |
225b7b10 | 285 | return false; |
8000a965 | 286 | } |
1cfdbcf0 | 287 | |
4eac3407 CT |
288 | void |
289 | ACL::parseFlags() | |
290 | { | |
291 | // ACL kids that carry ACLData which supports parameter flags override this | |
292 | Acl::ParseFlags(options(), Acl::NoFlags()); | |
293 | } | |
294 | ||
295 | SBufList | |
296 | ACL::dumpOptions() | |
297 | { | |
298 | SBufList result; | |
299 | const auto &myOptions = options(); | |
300 | // optimization: most ACLs do not have myOptions | |
301 | // this check also works around dump_SBufList() adding ' ' after empty items | |
302 | if (!myOptions.empty()) { | |
303 | SBufStream stream; | |
304 | stream << myOptions; | |
305 | const SBuf optionsImage = stream.buf(); | |
306 | if (!optionsImage.isEmpty()) | |
307 | result.push_back(optionsImage); | |
308 | } | |
309 | return result; | |
310 | } | |
311 | ||
94439e4e | 312 | /* ACL result caching routines */ |
313 | ||
225b7b10 | 314 | int |
ced8def3 | 315 | ACL::matchForCache(ACLChecklist *) |
225b7b10 | 316 | { |
317 | /* This is a fatal to ensure that cacheMatchAcl calls are _only_ | |
318 | * made for supported acl types */ | |
319 | fatal("aclCacheMatchAcl: unknown or unexpected ACL type"); | |
f53969cc | 320 | return 0; /* NOTREACHED */ |
225b7b10 | 321 | } |
322 | ||
94439e4e | 323 | /* |
26ac0430 | 324 | * we lookup an acl's cached results, and if we cannot find the acl being |
225b7b10 | 325 | * checked we check it and cache the result. This function is a template |
326 | * method to support caching of multiple acl types. | |
327 | * Note that caching of time based acl's is not | |
6bf4f823 | 328 | * wise in long lived caches (i.e. the auth_user proxy match cache) |
94439e4e | 329 | * RBC |
6bf4f823 | 330 | * TODO: does a dlink_list perform well enough? Kinkie |
94439e4e | 331 | */ |
225b7b10 | 332 | int |
333 | ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist) | |
9bea1d5b | 334 | { |
9bea1d5b | 335 | acl_proxy_auth_match_cache *auth_match; |
336 | dlink_node *link; | |
337 | link = cache->head; | |
62e76326 | 338 | |
9bea1d5b | 339 | while (link) { |
62e76326 | 340 | auth_match = (acl_proxy_auth_match_cache *)link->data; |
341 | ||
342 | if (auth_match->acl_data == this) { | |
bf8fe701 | 343 | debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name << "' (" << this << ")"); |
62e76326 | 344 | return auth_match->matchrv; |
345 | } | |
346 | ||
347 | link = link->next; | |
9bea1d5b | 348 | } |
62e76326 | 349 | |
d59e4742 | 350 | auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this); |
9bea1d5b | 351 | dlinkAddTail(auth_match, &auth_match->link, cache); |
bf8fe701 | 352 | debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv); |
225b7b10 | 353 | return auth_match->matchrv; |
94439e4e | 354 | } |
355 | ||
356 | void | |
9bea1d5b | 357 | aclCacheMatchFlush(dlink_list * cache) |
94439e4e | 358 | { |
9bea1d5b | 359 | acl_proxy_auth_match_cache *auth_match; |
360 | dlink_node *link, *tmplink; | |
361 | link = cache->head; | |
62e76326 | 362 | |
bf8fe701 | 363 | debugs(28, 8, "aclCacheMatchFlush called for cache " << cache); |
6bf4f823 | 364 | |
9bea1d5b | 365 | while (link) { |
62e76326 | 366 | auth_match = (acl_proxy_auth_match_cache *)link->data; |
367 | tmplink = link; | |
368 | link = link->next; | |
369 | dlinkDelete(tmplink, cache); | |
11a9f1b1 | 370 | delete auth_match; |
1f38f50a | 371 | } |
c68e9c6b | 372 | } |
73e67ee0 | 373 | |
4e56d7f6 | 374 | bool |
4ff6370b | 375 | ACL::requiresAle() const |
4e56d7f6 AJ |
376 | { |
377 | return false; | |
378 | } | |
379 | ||
b0dd28ba | 380 | bool |
381 | ACL::requiresReply() const | |
382 | { | |
383 | return false; | |
384 | } | |
60d096f4 | 385 | |
b0dd28ba | 386 | bool |
387 | ACL::requiresRequest() const | |
9bea1d5b | 388 | { |
b0dd28ba | 389 | return false; |
390 | } | |
62e76326 | 391 | |
f32789f4 | 392 | /*********************/ |
393 | /* Destroy functions */ | |
394 | /*********************/ | |
395 | ||
8000a965 | 396 | ACL::~ACL() |
397 | { | |
e20d485b | 398 | debugs(28, 3, "freeing ACL " << name); |
62e76326 | 399 | safe_free(cfgline); |
6f58d7d7 | 400 | AclMatchedName = NULL; // in case it was pointing to our name |
92a6f4b1 | 401 | } |
402 | ||
b0dd28ba | 403 | void |
404 | ACL::Initialize() | |
8000a965 | 405 | { |
97427e90 | 406 | ACL *a = Config.aclList; |
bf8fe701 | 407 | debugs(53, 3, "ACL::Initialize"); |
b0dd28ba | 408 | |
409 | while (a) { | |
410 | a->prepareForUse(); | |
411 | a = a->next; | |
412 | } | |
8000a965 | 413 | } |
f53969cc | 414 |