2 * DEBUG: section 28 Access Control
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "acl/Checklist.h"
35 #include "anyp/PortCfg.h"
37 #include "ConfigParser.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(char * &nextToken
)
57 while ((nextToken
= ConfigParser::strtokFile()) != NULL
&& nextToken
[0] == '-') {
59 //if token is the "--" break flag
60 if (strcmp(nextToken
, "--") == 0)
63 for (const char *flg
= nextToken
+1; *flg
!='\0'; flg
++ ) {
64 if (supported(*flg
)) {
67 debugs(28, 0, HERE
<< "Flag '" << *flg
<< "' not supported");
73 /*Regex code needs to parse -i file*/
74 if ( isSet(ACL_F_REGEX_CASE
))
75 ConfigParser::strtokFilePutBack("-i");
77 if (nextToken
!= NULL
&& strcmp(nextToken
, "--") != 0 )
78 ConfigParser::strtokFileUndo();
82 ACLFlags::flagsStr() const
90 for (ACLFlag f
= 'A'; f
<= 'z'; f
++) {
91 // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
92 if (isSet(f
) && f
!= ACL_F_REGEX_CASE
)
100 ACL::operator new (size_t byteCount
)
102 fatal ("unusable ACL::new");
107 ACL::operator delete (void *address
)
109 fatal ("unusable ACL::delete");
113 ACL::FindByName(const char *name
)
116 debugs(28, 9, "ACL::FindByName '" << name
<< "'");
118 for (a
= Config
.aclList
; a
; a
= a
->next
)
119 if (!strcasecmp(a
->name
, name
))
122 debugs(28, 9, "ACL::FindByName found no match");
128 ACL::Factory (char const *type
)
130 ACL
*result
= Prototype::Factory (type
);
133 fatal ("Unknown acl type in ACL::Factory");
145 bool ACL::valid () const
151 ACL::ParseAclLine(ConfigParser
&parser
, ACL
** head
)
153 /* we're already using strtok() to grok the line */
156 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
159 /* snarf the ACL name */
161 if ((t
= strtok(NULL
, w_space
)) == NULL
) {
162 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL name.");
167 if (strlen(t
) >= ACL_NAME_SZ
) {
168 debugs(28, DBG_CRITICAL
, "aclParseAclLine: aclParseAclLine: ACL name '" << t
<<
169 "' too long, max " << ACL_NAME_SZ
- 1 << " characters supported");
174 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
175 /* snarf the ACL type */
178 if ((theType
= strtok(NULL
, w_space
)) == NULL
) {
179 debugs(28, DBG_CRITICAL
, "aclParseAclLine: missing ACL type.");
184 // Is this ACL going to work?
185 if (strcmp(theType
, "myip") == 0) {
186 AnyP::PortCfg
*p
= Config
.Sockaddr
.http
;
188 // Bug 3239: not reliable when there is interception traffic coming
189 if (p
->flags
.natIntercept
)
190 debugs(28, DBG_CRITICAL
, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
193 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
195 } else if (strcmp(theType
, "myport") == 0) {
196 AnyP::PortCfg
*p
= Config
.Sockaddr
.http
;
198 // Bug 3239: not reliable when there is interception traffic coming
199 // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
200 if (p
->flags
.natIntercept
)
201 debugs(28, DBG_CRITICAL
, "WARNING: 'myport' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
204 theType
= "localport";
205 debugs(28, DBG_IMPORTANT
, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
208 if (!Prototype::Registered(theType
)) {
209 debugs(28, DBG_CRITICAL
, "FATAL: Invalid ACL type '" << theType
<< "'");
210 // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
215 if ((A
= FindByName(aclname
)) == NULL
) {
216 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname
<< "'");
217 A
= ACL::Factory(theType
);
218 xstrncpy(A
->name
, aclname
, ACL_NAME_SZ
);
219 A
->cfgline
= xstrdup(config_input_line
);
222 if (strcmp (A
->typeString(),theType
) ) {
223 debugs(28, DBG_CRITICAL
, "aclParseAclLine: ACL '" << A
->name
<< "' already exists with different type.");
228 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname
<< "'");
233 * Here we set AclMatchedName in case we need to use it in a
234 * warning message in aclDomainCompare().
236 AclMatchedName
= A
->name
; /* ugly */
239 A
->flags
.parseFlags(aTok
);
241 /*split the function here */
245 * Clear AclMatchedName from our temporary hack
247 AclMatchedName
= NULL
; /* ugly */
253 debugs(28, DBG_CRITICAL
, "Warning: empty ACL: " << A
->cfgline
);
257 fatalf("ERROR: Invalid ACL: %s\n",
263 head
= &(*head
)->next
;
269 ACL::isProxyAuth() const
274 ACLList::ACLList() : op (1), _acl (NULL
), next (NULL
)
278 ACLList::negated(bool isNegated
)
286 /* ACL result caching routines */
289 ACL::matchForCache(ACLChecklist
*checklist
)
291 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
292 * made for supported acl types */
293 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
294 return 0; /* NOTREACHED */
298 * we lookup an acl's cached results, and if we cannot find the acl being
299 * checked we check it and cache the result. This function is a template
300 * method to support caching of multiple acl types.
301 * Note that caching of time based acl's is not
302 * wise in long lived caches (i.e. the auth_user proxy match cache)
304 * TODO: does a dlink_list perform well enough? Kinkie
307 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
309 acl_proxy_auth_match_cache
*auth_match
;
314 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
316 if (auth_match
->acl_data
== this) {
317 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name
<< "' (" << this << ")");
318 return auth_match
->matchrv
;
324 auth_match
= new acl_proxy_auth_match_cache();
325 auth_match
->matchrv
= matchForCache (checklist
);
326 auth_match
->acl_data
= this;
327 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
328 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name
<< "'. Adding result " << auth_match
->matchrv
);
329 return auth_match
->matchrv
;
333 aclCacheMatchFlush(dlink_list
* cache
)
335 acl_proxy_auth_match_cache
*auth_match
;
336 dlink_node
*link
, *tmplink
;
339 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache
);
342 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
345 dlinkDelete(tmplink
, cache
);
351 ACL::requiresReply() const
357 ACL::requiresRequest() const
363 ACL::checklistMatches(ACLChecklist
*checklist
)
367 if (!checklist
->hasRequest() && requiresRequest()) {
368 debugs(28, DBG_IMPORTANT
, "ACL::checklistMatches WARNING: '" << name
<< "' ACL is used but there is no HTTP request -- not matching.");
372 if (!checklist
->hasReply() && requiresReply()) {
373 debugs(28, DBG_IMPORTANT
, "ACL::checklistMatches WARNING: '" << name
<< "' ACL is used but there is no HTTP reply -- not matching.");
377 debugs(28, 3, "ACL::checklistMatches: checking '" << name
<< "'");
378 rv
= match(checklist
);
379 debugs(28, 3, "ACL::ChecklistMatches: result for '" << name
<< "' is " << rv
);
384 ACLList::matches (ACLChecklist
*checklist
) const
387 // XXX: AclMatchedName does not contain a matched ACL name when the acl
388 // does not match (or contains stale name if no ACLs are checked). In
389 // either case, we get misleading debugging and possibly incorrect error
390 // messages. Unfortunately, deny_info's "when none http_access
391 // lines match" exception essentially requires this mess.
392 // TODO: Rework by using an acl-free deny_info for the no-match cases?
393 AclMatchedName
= _acl
->name
;
394 debugs(28, 3, "ACLList::matches: checking " << (op
? null_string
: "!") << _acl
->name
);
396 if (_acl
->checklistMatches(checklist
) != op
) {
397 debugs(28, 4, "ACLList::matches: result is false");
401 debugs(28, 4, "ACLList::matches: result is true");
405 /*********************/
406 /* Destroy functions */
407 /*********************/
411 debugs(28, 3, "ACL::~ACL: '" << cfgline
<< "'");
415 /* to be split into separate files in the future */
417 CBDATA_CLASS_INIT(acl_access
);
420 acl_access::operator new (size_t)
422 CBDATA_INIT_TYPE(acl_access
);
423 acl_access
*result
= cbdataAlloc(acl_access
);
428 acl_access::operator delete (void *address
)
430 acl_access
*t
= static_cast<acl_access
*>(address
);
434 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
436 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
441 Vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
442 void *ACL::Prototype::Initialized
;
445 ACL::Prototype::Registered(char const *aType
)
447 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType
);
449 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
450 if (!strcmp (aType
, (*i
)->typeString
)) {
451 debugs(28, 7, "ACL::Prototype::Registered: yes");
455 debugs(28, 7, "ACL::Prototype::Registered: no");
460 ACL::Prototype::registerMe ()
462 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
463 /* TODO: extract this */
464 /* Not initialised */
465 Registry
= new Vector
<ACL::Prototype
const *>;
466 Initialized
= (char *)Registry
- 5;
469 if (Registered (typeString
))
470 fatalf ("Attempt to register %s twice", typeString
);
472 Registry
->push_back (this);
475 ACL::Prototype::~Prototype()
477 // TODO: unregister me
481 ACL::Prototype::Factory (char const *typeToClone
)
483 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone
<< "'");
485 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
486 if (!strcmp (typeToClone
, (*i
)->typeString
)) {
487 ACL
*A
= (*i
)->prototype
->clone();
488 A
->flags
= (*i
)->prototype
->flags
;
492 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone
<< "' available");
500 ACL
*a
= Config
.aclList
;
501 debugs(53, 3, "ACL::Initialize");