2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 28 Access Control */
13 #include "acl/Checklist.h"
14 #include "acl/Gadgets.h"
15 #include "anyp/PortCfg.h"
17 #include "ConfigParser.h"
22 #include "profiler/Profiler.h"
23 #include "SquidConfig.h"
27 const ACLFlag
ACLFlags::NoFlags
[1] = {ACL_F_END
};
29 const char *AclMatchedName
= NULL
;
31 bool ACLFlags::supported(const ACLFlag f
) const
33 if (f
== ACL_F_REGEX_CASE
)
35 return (supported_
.find(f
) != std::string::npos
);
39 ACLFlags::parseFlags()
42 while ((nextToken
= ConfigParser::PeekAtToken()) != NULL
&& nextToken
[0] == '-') {
43 (void)ConfigParser::NextToken(); //Get token from cfg line
44 //if token is the "--" break flag
45 if (strcmp(nextToken
, "--") == 0)
48 for (const char *flg
= nextToken
+1; *flg
!='\0'; flg
++ ) {
49 if (supported(*flg
)) {
52 debugs(28, 0, HERE
<< "Flag '" << *flg
<< "' not supported");
58 /*Regex code needs to parse -i file*/
59 if ( isSet(ACL_F_REGEX_CASE
)) {
60 ConfigParser::TokenPutBack("-i");
66 ACLFlags::flagsStr() const
74 for (ACLFlag f
= 'A'; f
<= 'z'; f
++) {
75 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
76 if (isSet(f
) && f
!= ACL_F_REGEX_CASE
)
84 ACL::operator new (size_t)
86 fatal ("unusable ACL::new");
91 ACL::operator delete (void *)
93 fatal ("unusable ACL::delete");
97 ACL::FindByName(const char *name
)
100 debugs(28, 9, "ACL::FindByName '" << name
<< "'");
102 for (a
= Config
.aclList
; a
; a
= a
->next
)
103 if (!strcasecmp(a
->name
, name
))
106 debugs(28, 9, "ACL::FindByName found no match");
112 ACL::Factory (char const *type
)
114 ACL
*result
= Prototype::Factory (type
);
117 fatal ("Unknown acl type in ACL::Factory");
130 ACL::ACL(const ACLFlag flgs
[]) :
139 bool ACL::valid () const
145 ACL::matches(ACLChecklist
*checklist
) const
147 PROF_start(ACL_matches
);
148 debugs(28, 5, "checking " << name
);
150 // XXX: AclMatchedName does not contain a matched ACL name when the acl
151 // does not match. It contains the last (usually leaf) ACL name checked
152 // (or is NULL if no ACLs were checked).
153 AclMatchedName
= name
;
156 if (!checklist
->hasAle() && requiresAle()) {
157 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
158 "context without an ALE state. Assuming mismatch.");
159 } else if (!checklist
->hasRequest() && requiresRequest()) {
160 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
161 "context without an HTTP request. Assuming mismatch.");
162 } else if (!checklist
->hasReply() && requiresReply()) {
163 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
164 "context without an HTTP response. Assuming mismatch.");
166 // make sure the ALE has as much data as possible
168 checklist
->syncAle();
170 // have to cast because old match() API is missing const
171 result
= const_cast<ACL
*>(this)->match(checklist
);
174 const char *extra
= checklist
->asyncInProgress() ? " async" : "";
175 debugs(28, 3, "checked: " << name
<< " = " << result
<< extra
);
176 PROF_stop(ACL_matches
);
177 return result
== 1; // true for match; false for everything else
181 ACL::context(const char *aName
, const char *aCfgLine
)
185 xstrncpy(name
, aName
, ACL_NAME_SZ
-1);
188 cfgline
= xstrdup(aCfgLine
);
192 ACL::ParseAclLine(ConfigParser
&parser
, ACL
** head
)
194 /* we're already using strtok() to grok the line */
197 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
200 /* snarf the ACL name */
202 if ((t
= ConfigParser::NextToken()) == NULL
) {
203 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL name.");
208 if (strlen(t
) >= ACL_NAME_SZ
) {
209 debugs(28, DBG_CRITICAL
, "aclParseAclLine: aclParseAclLine: ACL name '" << t
<<
210 "' too long, max " << ACL_NAME_SZ
- 1 << " characters supported");
215 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
216 /* snarf the ACL type */
219 if ((theType
= ConfigParser::NextToken()) == NULL
) {
220 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL type.");
225 // Is this ACL going to work?
226 if (strcmp(theType
, "myip") == 0) {
227 AnyP::PortCfgPointer p
= HttpPortList
;
229 // Bug 3239: not reliable when there is interception traffic coming
230 if (p
->flags
.natIntercept
)
231 debugs(28, DBG_CRITICAL
, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
234 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
236 } else if (strcmp(theType
, "myport") == 0) {
237 AnyP::PortCfgPointer p
= HttpPortList
;
239 // Bug 3239: not reliable when there is interception traffic coming
240 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
241 if (p
->flags
.natIntercept
)
242 debugs(28, DBG_CRITICAL
, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
245 theType
= "localport";
246 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
247 } else if (strcmp(theType
, "proto") == 0 && strcmp(aclname
, "manager") == 0) {
248 // ACL manager is now a built-in and has a different type.
249 debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT
), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
250 return; // ignore the line
253 if (!Prototype::Registered(theType
)) {
254 debugs(28, DBG_CRITICAL
, "FATAL: Invalid ACL type '" << theType
<< "'");
255 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
260 if ((A
= FindByName(aclname
)) == NULL
) {
261 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname
<< "'");
262 A
= ACL::Factory(theType
);
263 A
->context(aclname
, config_input_line
);
266 if (strcmp (A
->typeString(),theType
) ) {
267 debugs(28, DBG_CRITICAL
, "aclParseAclLine: ACL '" << A
->name
<< "' already exists with different type.");
272 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname
<< "'");
277 * Here we set AclMatchedName in case we need to use it in a
278 * warning message in aclDomainCompare().
280 AclMatchedName
= A
->name
; /* ugly */
282 A
->flags
.parseFlags();
284 /*split the function here */
288 * Clear AclMatchedName from our temporary hack
290 AclMatchedName
= NULL
; /* ugly */
296 debugs(28, DBG_CRITICAL
, "Warning: empty ACL: " << A
->cfgline
);
300 fatalf("ERROR: Invalid ACL: %s\n",
304 // add to the global list for searching explicit ACLs by name
305 assert(head
&& *head
== Config
.aclList
);
309 // register for centralized cleanup
314 ACL::isProxyAuth() const
319 /* ACL result caching routines */
322 ACL::matchForCache(ACLChecklist
*)
324 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
325 * made for supported acl types */
326 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
327 return 0; /* NOTREACHED */
331 * we lookup an acl's cached results, and if we cannot find the acl being
332 * checked we check it and cache the result. This function is a template
333 * method to support caching of multiple acl types.
334 * Note that caching of time based acl's is not
335 * wise in long lived caches (i.e. the auth_user proxy match cache)
337 * TODO: does a dlink_list perform well enough? Kinkie
340 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
342 acl_proxy_auth_match_cache
*auth_match
;
347 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
349 if (auth_match
->acl_data
== this) {
350 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name
<< "' (" << this << ")");
351 return auth_match
->matchrv
;
357 auth_match
= new acl_proxy_auth_match_cache(matchForCache(checklist
), this);
358 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
359 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name
<< "'. Adding result " << auth_match
->matchrv
);
360 return auth_match
->matchrv
;
364 aclCacheMatchFlush(dlink_list
* cache
)
366 acl_proxy_auth_match_cache
*auth_match
;
367 dlink_node
*link
, *tmplink
;
370 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache
);
373 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
376 dlinkDelete(tmplink
, cache
);
382 ACL::requiresAle() const
388 ACL::requiresReply() const
394 ACL::requiresRequest() const
399 /*********************/
400 /* Destroy functions */
401 /*********************/
405 debugs(28, 3, "freeing ACL " << name
);
407 AclMatchedName
= NULL
; // in case it was pointing to our name
410 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
412 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
417 std::vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
418 void *ACL::Prototype::Initialized
;
421 ACL::Prototype::Registered(char const *aType
)
423 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType
);
425 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
426 if (!strcmp (aType
, (*i
)->typeString
)) {
427 debugs(28, 7, "ACL::Prototype::Registered: yes");
431 debugs(28, 7, "ACL::Prototype::Registered: no");
436 ACL::Prototype::registerMe ()
438 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
439 /* TODO: extract this */
440 /* Not initialised */
441 Registry
= new std::vector
<ACL::Prototype
const *>;
442 Initialized
= (char *)Registry
- 5;
445 if (Registered (typeString
))
446 fatalf ("Attempt to register %s twice", typeString
);
448 Registry
->push_back (this);
451 ACL::Prototype::~Prototype()
453 // TODO: unregister me
457 ACL::Prototype::Factory (char const *typeToClone
)
459 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone
<< "'");
461 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
462 if (!strcmp (typeToClone
, (*i
)->typeString
)) {
463 ACL
*A
= (*i
)->prototype
->clone();
464 A
->flags
= (*i
)->prototype
->flags
;
468 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone
<< "' available");
476 ACL
*a
= Config
.aclList
;
477 debugs(53, 3, "ACL::Initialize");