2 * DEBUG: section 28 Access Control
4 * SQUID Web Proxy Cache http://www.squid-cache.org/
5 * ----------------------------------------------------------
7 * Squid is the result of efforts by numerous individuals from
8 * the Internet community; see the CONTRIBUTORS file for full
9 * details. Many organizations have provided support for Squid's
10 * development; see the SPONSORS file for full details. Squid is
11 * Copyrighted (C) 2001 by the Regents of the University of
12 * California; see the COPYRIGHT file for full details. Squid
13 * incorporates software developed and/or copyrighted by other
14 * sources; see the CREDITS file for full details.
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 #include "acl/Checklist.h"
34 #include "anyp/PortCfg.h"
36 #include "ConfigParser.h"
40 #include "profiler/Profiler.h"
41 #include "SquidConfig.h"
43 const ACLFlag
ACLFlags::NoFlags
[1] = {ACL_F_END
};
45 const char *AclMatchedName
= NULL
;
47 bool ACLFlags::supported(const ACLFlag f
) const
49 if (f
== ACL_F_REGEX_CASE
)
51 return (supported_
.find(f
) != std::string::npos
);
55 ACLFlags::parseFlags()
58 while ((nextToken
= ConfigParser::PeekAtToken()) != NULL
&& nextToken
[0] == '-') {
59 (void)ConfigParser::NextToken(); //Get token from cfg line
60 //if token is the "--" break flag
61 if (strcmp(nextToken
, "--") == 0)
64 for (const char *flg
= nextToken
+1; *flg
!='\0'; flg
++ ) {
65 if (supported(*flg
)) {
68 debugs(28, 0, HERE
<< "Flag '" << *flg
<< "' not supported");
74 /*Regex code needs to parse -i file*/
75 if ( isSet(ACL_F_REGEX_CASE
))
76 ConfigParser::TokenPutBack("-i");
80 ACLFlags::flagsStr() const
88 for (ACLFlag f
= 'A'; f
<= 'z'; f
++) {
89 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
90 if (isSet(f
) && f
!= ACL_F_REGEX_CASE
)
98 ACL::operator new (size_t byteCount
)
100 fatal ("unusable ACL::new");
105 ACL::operator delete (void *address
)
107 fatal ("unusable ACL::delete");
111 ACL::FindByName(const char *name
)
114 debugs(28, 9, "ACL::FindByName '" << name
<< "'");
116 for (a
= Config
.aclList
; a
; a
= a
->next
)
117 if (!strcasecmp(a
->name
, name
))
120 debugs(28, 9, "ACL::FindByName found no match");
126 ACL::Factory (char const *type
)
128 ACL
*result
= Prototype::Factory (type
);
131 fatal ("Unknown acl type in ACL::Factory");
144 bool ACL::valid () const
150 ACL::matches(ACLChecklist
*checklist
) const
152 PROF_start(ACL_matches
);
153 debugs(28, 5, "checking " << name
);
155 // XXX: AclMatchedName does not contain a matched ACL name when the acl
156 // does not match. It contains the last (usually leaf) ACL name checked
157 // (or is NULL if no ACLs were checked).
158 AclMatchedName
= name
;
161 if (!checklist
->hasRequest() && requiresRequest()) {
162 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
163 "context without an HTTP request. Assuming mismatch.");
164 } else if (!checklist
->hasReply() && requiresReply()) {
165 debugs(28, DBG_IMPORTANT
, "WARNING: " << name
<< " ACL is used in " <<
166 "context without an HTTP response. Assuming mismatch.");
168 // have to cast because old match() API is missing const
169 result
= const_cast<ACL
*>(this)->match(checklist
);
172 const char *extra
= checklist
->asyncInProgress() ? " async" : "";
173 debugs(28, 3, "checked: " << name
<< " = " << result
<< extra
);
174 PROF_stop(ACL_matches
);
175 return result
== 1; // true for match; false for everything else
179 ACL::context(const char *aName
, const char *aCfgLine
)
183 xstrncpy(name
, aName
, ACL_NAME_SZ
-1);
186 cfgline
= xstrdup(aCfgLine
);
190 ACL::ParseAclLine(ConfigParser
&parser
, ACL
** head
)
192 /* we're already using strtok() to grok the line */
195 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
198 /* snarf the ACL name */
200 if ((t
= ConfigParser::NextToken()) == NULL
) {
201 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL name.");
206 if (strlen(t
) >= ACL_NAME_SZ
) {
207 debugs(28, DBG_CRITICAL
, "aclParseAclLine: aclParseAclLine: ACL name '" << t
<<
208 "' too long, max " << ACL_NAME_SZ
- 1 << " characters supported");
213 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
214 /* snarf the ACL type */
217 if ((theType
= ConfigParser::NextToken()) == NULL
) {
218 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL type.");
223 // Is this ACL going to work?
224 if (strcmp(theType
, "myip") == 0) {
225 AnyP::PortCfg
*p
= Config
.Sockaddr
.http
;
227 // Bug 3239: not reliable when there is interception traffic coming
228 if (p
->flags
.natIntercept
)
229 debugs(28, DBG_CRITICAL
, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
232 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
234 } else if (strcmp(theType
, "myport") == 0) {
235 AnyP::PortCfg
*p
= Config
.Sockaddr
.http
;
237 // Bug 3239: not reliable when there is interception traffic coming
238 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
239 if (p
->flags
.natIntercept
)
240 debugs(28, DBG_CRITICAL
, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
243 theType
= "localport";
244 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
247 if (!Prototype::Registered(theType
)) {
248 debugs(28, DBG_CRITICAL
, "FATAL: Invalid ACL type '" << theType
<< "'");
249 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
254 if ((A
= FindByName(aclname
)) == NULL
) {
255 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname
<< "'");
256 A
= ACL::Factory(theType
);
257 A
->context(aclname
, config_input_line
);
260 if (strcmp (A
->typeString(),theType
) ) {
261 debugs(28, DBG_CRITICAL
, "aclParseAclLine: ACL '" << A
->name
<< "' already exists with different type.");
266 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname
<< "'");
271 * Here we set AclMatchedName in case we need to use it in a
272 * warning message in aclDomainCompare().
274 AclMatchedName
= A
->name
; /* ugly */
276 A
->flags
.parseFlags();
278 /*split the function here */
282 * Clear AclMatchedName from our temporary hack
284 AclMatchedName
= NULL
; /* ugly */
290 debugs(28, DBG_CRITICAL
, "Warning: empty ACL: " << A
->cfgline
);
294 fatalf("ERROR: Invalid ACL: %s\n",
298 // prepend so that ACLs declared later (and possibly using earlier ACLs)
299 // are destroyed earlier (before the ACLs they use are destroyed)
300 assert(head
&& *head
== Config
.aclList
);
301 A
->registered
= true;
307 ACL::isProxyAuth() const
312 /* ACL result caching routines */
315 ACL::matchForCache(ACLChecklist
*checklist
)
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");
320 return 0; /* NOTREACHED */
324 * we lookup an acl's cached results, and if we cannot find the acl being
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
328 * wise in long lived caches (i.e. the auth_user proxy match cache)
330 * TODO: does a dlink_list perform well enough? Kinkie
333 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
335 acl_proxy_auth_match_cache
*auth_match
;
340 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
342 if (auth_match
->acl_data
== this) {
343 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name
<< "' (" << this << ")");
344 return auth_match
->matchrv
;
350 auth_match
= new acl_proxy_auth_match_cache();
351 auth_match
->matchrv
= matchForCache (checklist
);
352 auth_match
->acl_data
= this;
353 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
354 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name
<< "'. Adding result " << auth_match
->matchrv
);
355 return auth_match
->matchrv
;
359 aclCacheMatchFlush(dlink_list
* cache
)
361 acl_proxy_auth_match_cache
*auth_match
;
362 dlink_node
*link
, *tmplink
;
365 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache
);
368 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
371 dlinkDelete(tmplink
, cache
);
377 ACL::requiresReply() const
383 ACL::requiresRequest() const
388 /*********************/
389 /* Destroy functions */
390 /*********************/
394 debugs(28, 3, "ACL::~ACL: '" << cfgline
<< "'");
396 AclMatchedName
= NULL
; // in case it was pointing to our name
399 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
401 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
406 Vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
407 void *ACL::Prototype::Initialized
;
410 ACL::Prototype::Registered(char const *aType
)
412 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType
);
414 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
415 if (!strcmp (aType
, (*i
)->typeString
)) {
416 debugs(28, 7, "ACL::Prototype::Registered: yes");
420 debugs(28, 7, "ACL::Prototype::Registered: no");
425 ACL::Prototype::registerMe ()
427 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
428 /* TODO: extract this */
429 /* Not initialised */
430 Registry
= new Vector
<ACL::Prototype
const *>;
431 Initialized
= (char *)Registry
- 5;
434 if (Registered (typeString
))
435 fatalf ("Attempt to register %s twice", typeString
);
437 Registry
->push_back (this);
440 ACL::Prototype::~Prototype()
442 // TODO: unregister me
446 ACL::Prototype::Factory (char const *typeToClone
)
448 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone
<< "'");
450 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
451 if (!strcmp (typeToClone
, (*i
)->typeString
)) {
452 ACL
*A
= (*i
)->prototype
->clone();
453 A
->flags
= (*i
)->prototype
->flags
;
457 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone
<< "' available");
465 ACL
*a
= Config
.aclList
;
466 debugs(53, 3, "ACL::Initialize");