4 * DEBUG: section 28 Access Control
5 * AUTHOR: Duane Wessels
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #include "HttpRequest.h"
42 #include "ACLChecklist.h"
43 #include "ConfigParser.h"
45 /* for special-case PURGE test */
46 #include "HttpRequestMethod.h"
47 /* for Vector<> Instances */
50 const char *AclMatchedName
= NULL
;
53 ACL::operator new (size_t byteCount
)
55 fatal ("unusable ACL::new");
60 ACL::operator delete (void *address
)
62 fatal ("unusable ACL::delete");
66 ACL::FindByName(const char *name
)
69 debugs(28, 9, "ACL::FindByName '" << name
<< "'");
71 for (a
= Config
.aclList
; a
; a
= a
->next
)
72 if (!strcasecmp(a
->name
, name
))
75 debugs(28, 9, "ACL::FindByName found no match");
81 ACL::Factory (char const *type
)
83 ACL
*result
= Prototype::Factory (type
);
86 fatal ("Unknown acl type in ACL::Factory");
91 ACL::ACL () :cfgline(NULL
) {}
93 bool ACL::valid () const
99 ACL::ParseAclLine(ConfigParser
&parser
, ACL
** head
)
101 /* we're already using strtok() to grok the line */
104 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
107 /* snarf the ACL name */
109 if ((t
= strtok(NULL
, w_space
)) == NULL
) {
110 debugs(28, 0, "aclParseAclLine: missing ACL name.");
115 if (strlen(t
) >= ACL_NAME_SZ
) {
116 debugs(28, 0, "aclParseAclLine: aclParseAclLine: ACL name '" << t
<<
117 "' too long, max " << ACL_NAME_SZ
- 1 << " characters supported");
122 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
123 /* snarf the ACL type */
126 if ((theType
= strtok(NULL
, w_space
)) == NULL
) {
127 debugs(28, 0, "aclParseAclLine: missing ACL type.");
132 if (!Prototype::Registered (theType
)) {
133 debugs(28, 0, "aclParseAclLine: Invalid ACL type '" << theType
<< "'");
138 if ((A
= FindByName(aclname
)) == NULL
) {
139 debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname
<< "'");
140 A
= ACL::Factory(theType
);
141 xstrncpy(A
->name
, aclname
, ACL_NAME_SZ
);
142 A
->cfgline
= xstrdup(config_input_line
);
145 if (strcmp (A
->typeString(),theType
) ) {
146 debugs(28, 0, "aclParseAclLine: ACL '" << A
->name
<< "' already exists with different type.");
151 debugs(28, 3, "aclParseAclLine: Appending to '" << aclname
<< "'");
156 * Here we set AclMatchedName in case we need to use it in a
157 * warning message in aclDomainCompare().
159 AclMatchedName
= A
->name
; /* ugly */
161 /*split the function here */
165 * Clear AclMatchedName from our temporary hack
167 AclMatchedName
= NULL
; /* ugly */
173 debugs(28, 0, "Warning: empty ACL: " << A
->cfgline
);
177 fatalf("ERROR: Invalid ACL: %s\n",
183 head
= &(*head
)->next
;
189 ACL::isProxyAuth() const
195 ACLList::ACLList() : op (1), _acl (NULL
), next (NULL
)
199 ACLList::negated(bool isNegated
)
207 /* ACL result caching routines */
210 ACL::matchForCache(ACLChecklist
*checklist
)
212 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
213 * made for supported acl types */
214 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
215 return 0; /* NOTREACHED */
219 * we lookup an acl's cached results, and if we cannot find the acl being
220 * checked we check it and cache the result. This function is a template
221 * method to support caching of multiple acl types.
222 * Note that caching of time based acl's is not
223 * wise in long lived caches (i.e. the auth_user proxy match cache)
225 * TODO: does a dlink_list perform well enough? Kinkie
228 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
230 acl_proxy_auth_match_cache
*auth_match
;
235 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
237 if (auth_match
->acl_data
== this) {
238 debugs(28, 4, "ACL::cacheMatchAcl: cache hit on acl '" << name
<< "' (" << this << ")");
239 return auth_match
->matchrv
;
245 auth_match
= new acl_proxy_auth_match_cache();
246 auth_match
->matchrv
= matchForCache (checklist
);
247 auth_match
->acl_data
= this;
248 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
249 debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name
<< "'. Adding result " << auth_match
->matchrv
);
250 return auth_match
->matchrv
;
254 aclCacheMatchFlush(dlink_list
* cache
)
256 acl_proxy_auth_match_cache
*auth_match
;
257 dlink_node
*link
, *tmplink
;
260 debugs(28, 8, "aclCacheMatchFlush called for cache " << cache
);
263 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
266 dlinkDelete(tmplink
, cache
);
272 ACL::requiresReply() const
278 ACL::requiresRequest() const
284 ACL::checklistMatches(ACLChecklist
*checklist
)
288 if (NULL
== checklist
->request
&& requiresRequest()) {
289 debugs(28, 1, "ACL::checklistMatches WARNING: '" << name
<< "' ACL is used but there is no HTTP request -- not matching.");
293 if (NULL
== checklist
->reply
&& requiresReply()) {
294 debugs(28, 1, "ACL::checklistMatches WARNING: '" << name
<< "' ACL is used but there is no HTTP reply -- not matching.");
298 debugs(28, 3, "ACL::checklistMatches: checking '" << name
<< "'");
299 rv
= match(checklist
);
300 debugs(28, 3, "ACL::ChecklistMatches: result for '" << name
<< "' is " << rv
);
305 ACLList::matches (ACLChecklist
*checklist
) const
308 AclMatchedName
= _acl
->name
;
309 debugs(28, 3, "ACLList::matches: checking " << (op
? null_string
: "!") << _acl
->name
);
311 if (_acl
->checklistMatches(checklist
) != op
) {
312 debugs(28, 4, "ACLList::matches: result is false");
313 return checklist
->lastACLResult(false);
316 debugs(28, 4, "ACLList::matches: result is true");
317 return checklist
->lastACLResult(true);
321 /*********************/
322 /* Destroy functions */
323 /*********************/
327 debugs(28, 3, "ACL::~ACL: '" << cfgline
<< "'");
331 #include "ACLStrategised.h"
333 acl_access::containsPURGE() const
335 acl_access
const *a
= this;
338 debugs(28, 6, "acl_access::containsPURGE: invoked for '" << cfgline
<< "'");
340 for (; a
; a
= a
->next
) {
341 for (b
= a
->aclList
; b
; b
= b
->next
) {
342 ACLStrategised
<HttpRequestMethod
> *tempAcl
= dynamic_cast<ACLStrategised
<HttpRequestMethod
> *>(b
->_acl
);
345 debugs(28, 7, "acl_access::containsPURGE: can't create tempAcl");
349 if (tempAcl
->match(METHOD_PURGE
)) {
350 debugs(28, 6, "acl_access::containsPURGE: returning true");
356 debugs(28, 6, "acl_access::containsPURGE: returning false");
360 /* to be split into separate files in the future */
362 CBDATA_CLASS_INIT(acl_access
);
365 acl_access::operator new (size_t)
367 CBDATA_INIT_TYPE(acl_access
);
368 acl_access
*result
= cbdataAlloc(acl_access
);
373 acl_access::operator delete (void *address
)
375 acl_access
*t
= static_cast<acl_access
*>(address
);
379 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
381 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
386 Vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
387 void *ACL::Prototype::Initialized
;
390 ACL::Prototype::Registered(char const *aType
)
392 debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType
);
394 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
395 if (!strcmp (aType
, (*i
)->typeString
)) {
396 debugs(28, 7, "ACL::Prototype::Registered: yes");
400 debugs(28, 7, "ACL::Prototype::Registered: no");
405 ACL::Prototype::registerMe ()
407 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
408 /* TODO: extract this */
409 /* Not initialised */
410 Registry
= new Vector
<ACL::Prototype
const *>;
411 Initialized
= (char *)Registry
- 5;
414 if (Registered (typeString
))
415 fatalf ("Attempt to register %s twice", typeString
);
417 Registry
->push_back (this);
420 ACL::Prototype::~Prototype()
422 // TODO: unregister me
426 ACL::Prototype::Factory (char const *typeToClone
)
428 debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone
<< "'");
430 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
431 if (!strcmp (typeToClone
, (*i
)->typeString
))
432 return (*i
)->prototype
->clone();
434 debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone
<< "' available");
442 ACL
*a
= Config
.aclList
;
443 debugs(53, 3, "ACL::Initialize");