2 * Copyright (C) 1996-2014 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"
21 #include "profiler/Profiler.h"
22 #include "SquidConfig.h"
26 const ACLFlag
ACLFlags::NoFlags
[1] = {ACL_F_END
};
28 const char *AclMatchedName
= NULL
;
30 bool ACLFlags::supported(const ACLFlag f
) const
32 if (f
== ACL_F_REGEX_CASE
)
34 return (supported_
.find(f
) != std::string::npos
);
38 ACLFlags::parseFlags()
41 while ((nextToken
= ConfigParser::PeekAtToken()) != NULL
&& nextToken
[0] == '-') {
42 (void)ConfigParser::NextToken(); //Get token from cfg line
43 //if token is the "--" break flag
44 if (strcmp(nextToken
, "--") == 0)
47 for (const char *flg
= nextToken
+1; *flg
!='\0'; flg
++ ) {
48 if (supported(*flg
)) {
51 debugs(28, 0, HERE
<< "Flag '" << *flg
<< "' not supported");
57 /*Regex code needs to parse -i file*/
58 if ( isSet(ACL_F_REGEX_CASE
))
59 ConfigParser::TokenPutBack("-i");
63 ACLFlags::flagsStr() const
71 for (ACLFlag f
= 'A'; f
<= 'z'; f
++) {
72 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
73 if (isSet(f
) && f
!= ACL_F_REGEX_CASE
)
81 ACL::operator new (size_t byteCount
)
83 fatal ("unusable ACL::new");
88 ACL::operator delete (void *address
)
90 fatal ("unusable ACL::delete");
94 ACL::FindByName(const char *name
)
97 debugs(28, 9, "ACL::FindByName '" << name
<< "'");
99 for (a
= Config
.aclList
; a
; a
= a
->next
)
100 if (!strcasecmp(a
->name
, name
))
103 debugs(28, 9, "ACL::FindByName found no match");
109 ACL::Factory (char const *type
)
111 ACL
*result
= Prototype::Factory (type
);
114 fatal ("Unknown acl type in ACL::Factory");
127 bool ACL::valid () const
133 ACL::matches(ACLChecklist
*checklist
) const
135 PROF_start(ACL_matches
);
136 debugs(28, 5, "checking " << name
);
138 // XXX: AclMatchedName does not contain a matched ACL name when the acl
139 // does not match. It contains the last (usually leaf) ACL name checked
140 // (or is NULL if no ACLs were checked).
141 AclMatchedName
= name
;
144 if (!checklist
->hasRequest() && requiresRequest()) {
145 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
146 "context without an HTTP request. Assuming mismatch.");
147 } else if (!checklist
->hasReply() && requiresReply()) {
148 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
149 "context without an HTTP response. Assuming mismatch.");
151 // have to cast because old match() API is missing const
152 result
= const_cast<ACL
*>(this)->match(checklist
);
155 const char *extra
= checklist
->asyncInProgress() ? " async" : "";
156 debugs(28, 3, "checked: " << name
<< " = " << result
<< extra
);
157 PROF_stop(ACL_matches
);
158 return result
== 1; // true for match; false for everything else
162 ACL::context(const char *aName
, const char *aCfgLine
)
166 xstrncpy(name
, aName
, ACL_NAME_SZ
-1);
169 cfgline
= xstrdup(aCfgLine
);
173 ACL::ParseAclLine(ConfigParser
&parser
, ACL
** head
)
175 /* we're already using strtok() to grok the line */
178 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
181 /* snarf the ACL name */
183 if ((t
= ConfigParser::NextToken()) == NULL
) {
184 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL name.");
189 if (strlen(t
) >= ACL_NAME_SZ
) {
190 debugs(28, DBG_CRITICAL
, "aclParseAclLine: aclParseAclLine: ACL name '" << t
<<
191 "' too long, max " << ACL_NAME_SZ
- 1 << " characters supported");
196 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
197 /* snarf the ACL type */
200 if ((theType
= ConfigParser::NextToken()) == NULL
) {
201 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL type.");
206 // Is this ACL going to work?
207 if (strcmp(theType
, "myip") == 0) {
208 AnyP::PortCfgPointer p
= HttpPortList
;
210 // Bug 3239: not reliable when there is interception traffic coming
211 if (p
->flags
.natIntercept
)
212 debugs(28, DBG_CRITICAL
, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
215 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
217 } else if (strcmp(theType
, "myport") == 0) {
218 AnyP::PortCfgPointer p
= HttpPortList
;
220 // Bug 3239: not reliable when there is interception traffic coming
221 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
222 if (p
->flags
.natIntercept
)
223 debugs(28, DBG_CRITICAL
, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
226 theType
= "localport";
227 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
230 if (!Prototype::Registered(theType
)) {
231 debugs(28, DBG_CRITICAL
, "FATAL: Invalid ACL type '" << theType
<< "'");
232 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
237 if ((A
= FindByName(aclname
)) == NULL
) {
238 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname
<< "'");
239 A
= ACL::Factory(theType
);
240 A
->context(aclname
, config_input_line
);
243 if (strcmp (A
->typeString(),theType
) ) {
244 debugs(28, DBG_CRITICAL
, "aclParseAclLine: ACL '" << A
->name
<< "' already exists with different type.");
249 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname
<< "'");
254 * Here we set AclMatchedName in case we need to use it in a
255 * warning message in aclDomainCompare().
257 AclMatchedName
= A
->name
; /* ugly */
259 A
->flags
.parseFlags();
261 /*split the function here */
265 * Clear AclMatchedName from our temporary hack
267 AclMatchedName
= NULL
; /* ugly */
273 debugs(28, DBG_CRITICAL
, "Warning: empty ACL: " << A
->cfgline
);
277 fatalf("ERROR: Invalid ACL: %s\n",
281 // add to the global list for searching explicit ACLs by name
282 assert(head
&& *head
== Config
.aclList
);
286 // register for centralized cleanup
291 ACL::isProxyAuth() const
296 /* ACL result caching routines */
299 ACL::matchForCache(ACLChecklist
*checklist
)
301 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
302 * made for supported acl types */
303 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
304 return 0; /* NOTREACHED */
308 * we lookup an acl's cached results, and if we cannot find the acl being
309 * checked we check it and cache the result. This function is a template
310 * method to support caching of multiple acl types.
311 * Note that caching of time based acl's is not
312 * wise in long lived caches (i.e. the auth_user proxy match cache)
314 * TODO: does a dlink_list perform well enough? Kinkie
317 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
319 acl_proxy_auth_match_cache
*auth_match
;
324 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
326 if (auth_match
->acl_data
== this) {
327 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name
<< "' (" << this << ")");
328 return auth_match
->matchrv
;
334 auth_match
= new acl_proxy_auth_match_cache();
335 auth_match
->matchrv
= matchForCache (checklist
);
336 auth_match
->acl_data
= this;
337 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
338 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name
<< "'. Adding result " << auth_match
->matchrv
);
339 return auth_match
->matchrv
;
343 aclCacheMatchFlush(dlink_list
* cache
)
345 acl_proxy_auth_match_cache
*auth_match
;
346 dlink_node
*link
, *tmplink
;
349 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache
);
352 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
355 dlinkDelete(tmplink
, cache
);
361 ACL::requiresReply() const
367 ACL::requiresRequest() const
372 /*********************/
373 /* Destroy functions */
374 /*********************/
378 debugs(28, 3, "ACL::~ACL: '" << cfgline
<< "'");
380 AclMatchedName
= NULL
; // in case it was pointing to our name
383 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
385 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
390 std::vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
391 void *ACL::Prototype::Initialized
;
394 ACL::Prototype::Registered(char const *aType
)
396 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType
);
398 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
399 if (!strcmp (aType
, (*i
)->typeString
)) {
400 debugs(28, 7, "ACL::Prototype::Registered: yes");
404 debugs(28, 7, "ACL::Prototype::Registered: no");
409 ACL::Prototype::registerMe ()
411 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
412 /* TODO: extract this */
413 /* Not initialised */
414 Registry
= new std::vector
<ACL::Prototype
const *>;
415 Initialized
= (char *)Registry
- 5;
418 if (Registered (typeString
))
419 fatalf ("Attempt to register %s twice", typeString
);
421 Registry
->push_back (this);
424 ACL::Prototype::~Prototype()
426 // TODO: unregister me
430 ACL::Prototype::Factory (char const *typeToClone
)
432 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone
<< "'");
434 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
435 if (!strcmp (typeToClone
, (*i
)->typeString
)) {
436 ACL
*A
= (*i
)->prototype
->clone();
437 A
->flags
= (*i
)->prototype
->flags
;
441 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone
<< "' available");
449 ACL
*a
= Config
.aclList
;
450 debugs(53, 3, "ACL::Initialize");