2 * $Id: acl.cc,v 1.311 2003/08/10 11:00:40 robertc Exp $
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.
37 #include "ACLChecklist.h"
38 #include "HttpRequest.h"
40 const char *AclMatchedName
= NULL
;
43 ACL::operator new (size_t byteCount
)
45 fatal ("unusable ACL::new");
50 ACL::operator delete (void *address
)
52 fatal ("unusable ACL::delete");
56 ACL::FindByName(const char *name
)
60 for (a
= Config
.aclList
; a
; a
= a
->next
)
61 if (!strcasecmp(a
->name
, name
))
68 ACL::Factory (char const *type
)
70 ACL
*result
= Prototype::Factory (type
);
73 fatal ("Unknown acl type in ACL::Factory");
81 ACL::ParseAclLine(acl
** head
)
83 /* we're already using strtok() to grok the line */
86 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
89 /* snarf the ACL name */
91 if ((t
= strtok(NULL
, w_space
)) == NULL
) {
92 debug(28, 0) ("%s line %d: %s\n",
93 cfg_filename
, config_lineno
, config_input_line
);
94 debug(28, 0) ("aclParseAclLine: missing ACL name.\n");
98 xstrncpy(aclname
, t
, ACL_NAME_SZ
);
99 /* snarf the ACL type */
102 if ((theType
= strtok(NULL
, w_space
)) == NULL
) {
103 debug(28, 0) ("%s line %d: %s\n",
104 cfg_filename
, config_lineno
, config_input_line
);
105 debug(28, 0) ("aclParseAclLine: missing ACL type.\n");
109 if (!Prototype::Registered (theType
)) {
110 debug(28, 0) ("%s line %d: %s\n",
111 cfg_filename
, config_lineno
, config_input_line
);
112 debug(28, 0) ("aclParseAclLine: Invalid ACL type '%s'\n", theType
);
116 if ((A
= FindByName(aclname
)) == NULL
) {
117 debug(28, 3) ("aclParseAclLine: Creating ACL '%s'\n", aclname
);
118 A
= ACL::Factory(theType
);
119 xstrncpy(A
->name
, aclname
, ACL_NAME_SZ
);
120 A
->cfgline
= xstrdup(config_input_line
);
123 if (strcmp (A
->typeString(),theType
) ) {
124 debug(28, 0) ("aclParseAclLine: ACL '%s' already exists with different type, skipping.\n", A
->name
);
128 debug(28, 3) ("aclParseAclLine: Appending to '%s'\n", aclname
);
133 * Here we set AclMatchedName in case we need to use it in a
134 * warning message in aclDomainCompare().
136 AclMatchedName
= A
->name
; /* ugly */
138 /*split the function here */
142 * Clear AclMatchedName from our temporary hack
144 AclMatchedName
= NULL
; /* ugly */
150 debug(28, 0) ("aclParseAclLine: IGNORING invalid ACL: %s\n",
153 /* Do we need this? */
160 head
= &(*head
)->next
;
165 /* does name lookup, returns page_id */
167 aclGetDenyInfoPage(acl_deny_info_list
** head
, const char *name
)
169 acl_deny_info_list
*A
= NULL
;
170 acl_name_list
*L
= NULL
;
174 if (NULL
== *head
) /* empty list */
180 if (NULL
== L
) /* empty list should never happen, but in case */
184 if (!strcmp(name
, L
->name
))
185 return A
->err_page_id
;
196 /* does name lookup, returns if it is a proxy_auth acl */
198 aclIsProxyAuth(const char *name
)
205 if ((a
= ACL::FindByName(name
)))
206 return a
->isProxyAuth();
212 ACL::isProxyAuth() const
217 /* maex@space.net (05.09.96)
218 * get the info for redirecting "access denied" to info pages
220 * currently there is no optimization for
221 * - more than one deny_info line with the same url
222 * - a check, whether the given acl really is defined
223 * - a check, whether an acl is added more than once for the same url
227 aclParseDenyInfoLine(acl_deny_info_list
** head
)
230 acl_deny_info_list
*A
= NULL
;
231 acl_deny_info_list
*B
= NULL
;
232 acl_deny_info_list
**T
= NULL
;
233 acl_name_list
*L
= NULL
;
234 acl_name_list
**Tail
= NULL
;
236 /* first expect a page name */
238 if ((t
= strtok(NULL
, w_space
)) == NULL
) {
239 debug(28, 0) ("%s line %d: %s\n",
240 cfg_filename
, config_lineno
, config_input_line
);
241 debug(28, 0) ("aclParseDenyInfoLine: missing 'error page' parameter.\n");
245 A
= (acl_deny_info_list
*)memAllocate(MEM_ACL_DENY_INFO_LIST
);
246 A
->err_page_id
= errorReservePageId(t
);
247 A
->err_page_name
= xstrdup(t
);
248 A
->next
= (acl_deny_info_list
*) NULL
;
249 /* next expect a list of ACL names */
252 while ((t
= strtok(NULL
, w_space
))) {
253 L
= (acl_name_list
*)memAllocate(MEM_ACL_NAME_LIST
);
254 xstrncpy(L
->name
, t
, ACL_NAME_SZ
);
259 if (A
->acl_list
== NULL
) {
260 debug(28, 0) ("%s line %d: %s\n",
261 cfg_filename
, config_lineno
, config_input_line
);
262 debug(28, 0) ("aclParseDenyInfoLine: deny_info line contains no ACL's, skipping\n");
263 memFree(A
, MEM_ACL_DENY_INFO_LIST
);
267 for (B
= *head
, T
= head
; B
; T
= &B
->next
, B
= B
->next
)
269 ; /* find the tail */
274 aclParseAccessLine(acl_access
** head
)
277 acl_access
*A
= NULL
;
278 acl_access
*B
= NULL
;
279 acl_access
**T
= NULL
;
281 /* first expect either 'allow' or 'deny' */
283 if ((t
= strtok(NULL
, w_space
)) == NULL
) {
284 debug(28, 0) ("%s line %d: %s\n",
285 cfg_filename
, config_lineno
, config_input_line
);
286 debug(28, 0) ("aclParseAccessLine: missing 'allow' or 'deny'.\n");
292 if (!strcmp(t
, "allow"))
293 A
->allow
= ACCESS_ALLOWED
;
294 else if (!strcmp(t
, "deny"))
295 A
->allow
= ACCESS_DENIED
;
297 debug(28, 0) ("%s line %d: %s\n",
298 cfg_filename
, config_lineno
, config_input_line
);
299 debug(28, 0) ("aclParseAccessLine: expecting 'allow' or 'deny', got '%s'.\n", t
);
304 aclParseAclList(&A
->aclList
);
306 if (A
->aclList
== NULL
) {
307 debug(28, 0) ("%s line %d: %s\n",
308 cfg_filename
, config_lineno
, config_input_line
);
309 debug(28, 0) ("aclParseAccessLine: Access line contains no ACL's, skipping\n");
314 A
->cfgline
= xstrdup(config_input_line
);
315 /* Append to the end of this list */
317 for (B
= *head
, T
= head
; B
; T
= &B
->next
, B
= B
->next
)
322 /* We lock _acl_access structures in ACLChecklist::check() */
325 ACLList::ACLList() : op (1), _acl (NULL
), next (NULL
)
329 ACLList::negated(bool isNegated
)
338 aclParseAclList(acl_list
** head
)
340 acl_list
**Tail
= head
; /* sane name in the use below */
344 /* next expect a list of ACL names, possibly preceeded
345 * by '!' for negation */
347 while ((t
= strtok(NULL
, w_space
))) {
348 acl_list
*L
= new ACLList
;
355 debug(28, 3) ("aclParseAccessLine: looking for ACL name '%s'\n", t
);
356 a
= ACL::FindByName(t
);
359 debug(28, 0) ("%s line %d: %s\n",
360 cfg_filename
, config_lineno
, config_input_line
);
361 debug(28, 0) ("aclParseAccessLine: ACL name '%s' not found.\n", t
);
372 /* ACL result caching routines */
375 ACL::matchForCache(ACLChecklist
*checklist
)
377 /* This is a fatal to ensure that cacheMatchAcl calls are _only_
378 * made for supported acl types */
379 fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
380 return 0; /* NOTREACHED */
384 * we lookup an acl's cached results, and if we cannot find the acl being
385 * checked we check it and cache the result. This function is a template
386 * method to support caching of multiple acl types.
387 * Note that caching of time based acl's is not
388 * wise in long lived caches (i.e. the auth_user proxy match cache.
392 ACL::cacheMatchAcl(dlink_list
* cache
, ACLChecklist
*checklist
)
394 acl_proxy_auth_match_cache
*auth_match
;
399 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
401 if (auth_match
->acl_data
== this) {
402 debug(28, 4) ("ACL::cacheMatchAcl: cache hit on acl '%p'\n", this);
403 return auth_match
->matchrv
;
410 auth_match
= (acl_proxy_auth_match_cache
*)memAllocate(MEM_ACL_PROXY_AUTH_MATCH
);
411 auth_match
->matchrv
= matchForCache (checklist
);
412 auth_match
->acl_data
= this;
413 dlinkAddTail(auth_match
, &auth_match
->link
, cache
);
414 return auth_match
->matchrv
;
418 aclCacheMatchFlush(dlink_list
* cache
)
420 acl_proxy_auth_match_cache
*auth_match
;
421 dlink_node
*link
, *tmplink
;
425 auth_match
= (acl_proxy_auth_match_cache
*)link
->data
;
428 dlinkDelete(tmplink
, cache
);
429 memFree(auth_match
, MEM_ACL_PROXY_AUTH_MATCH
);
434 ACL::requiresReply() const
440 ACL::requiresRequest() const
446 ACL::checklistMatches(ACLChecklist
*checklist
)
448 if (NULL
== checklist
->request
&& requiresRequest()) {
449 debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
450 " HTTP request -- not matching.\n", name
);
454 if (NULL
== checklist
->reply
&& requiresReply()) {
455 debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
456 " HTTP reply -- not matching.\n", name
);
460 debug(28, 3) ("aclMatchAcl: checking '%s'\n", cfgline
);
461 return match(checklist
);
465 ACLList::matches (ACLChecklist
*checklist
) const
468 AclMatchedName
= _acl
->name
;
469 debug(28, 3) ("ACLList::matches: checking %s%s\n",
470 op
? null_string
: "!", _acl
->name
);
472 if (_acl
->checklistMatches(checklist
) != op
) {
479 /* Warning: do not cbdata lock checklist here - it
480 * may be static or on the stack
483 aclCheckFast(const acl_access
* A
, ACLChecklist
* checklist
)
485 allow_t allow
= ACCESS_DENIED
;
486 PROF_start(aclCheckFast
);
487 debug(28, 5) ("aclCheckFast: list: %p\n", A
);
491 checklist
->matchAclListFast(A
->aclList
);
493 if (checklist
->finished()) {
494 PROF_stop(aclCheckFast
);
495 return allow
== ACCESS_ALLOWED
;
501 debug(28, 5) ("aclCheckFast: no matches, returning: %d\n", allow
== ACCESS_DENIED
);
502 PROF_stop(aclCheckFast
);
503 return allow
== ACCESS_DENIED
;
507 * Any ACLChecklist created by aclChecklistCreate() must eventually be
508 * freed by ACLChecklist::operator delete(). There are two common cases:
510 * A) Using aclCheckFast(): The caller creates the ACLChecklist using
511 * aclChecklistCreate(), checks it using aclCheckFast(), and frees it
512 * using aclChecklistFree().
514 * B) Using aclNBCheck() and callbacks: The caller creates the
515 * ACLChecklist using aclChecklistCreate(), and passes it to
516 * aclNBCheck(). Control eventually passes to ACLChecklist::checkCallback(),
517 * which will invoke the callback function as requested by the
518 * original caller of aclNBCheck(). This callback function must
519 * *not* invoke aclChecklistFree(). After the callback function
520 * returns, ACLChecklist::checkCallback() will free the ACLChecklist using
521 * aclChecklistFree().
526 aclChecklistCreate(const acl_access
* A
, HttpRequest
* request
, const char *ident
)
528 ACLChecklist
*checklist
= new ACLChecklist
;
531 checklist
->accessList
= cbdataReference(A
);
533 if (request
!= NULL
) {
534 checklist
->request
= requestLink(request
);
535 checklist
->src_addr
= request
->client_addr
;
536 checklist
->my_addr
= request
->my_addr
;
537 checklist
->my_port
= request
->my_port
;
542 xstrncpy(checklist
->rfc931
, ident
, USER_IDENT_SZ
);
546 checklist
->auth_user_request
= NULL
;
551 /*********************/
552 /* Destroy functions */
553 /*********************/
556 aclDestroyAcls(acl
** head
)
560 for (acl
*a
= *head
; a
; a
= next
) {
570 debug(28, 3) ("aclDestroyAcls: '%s'\n", cfgline
);
575 aclDestroyAclList(acl_list
** head
)
579 for (l
= *head
; l
; l
= *head
) {
586 aclDestroyAccessList(acl_access
** list
)
588 acl_access
*l
= NULL
;
589 acl_access
*next
= NULL
;
591 for (l
= *list
; l
; l
= next
) {
592 debug(28, 3) ("aclDestroyAccessList: '%s'\n", l
->cfgline
);
594 aclDestroyAclList(&l
->aclList
);
595 safe_free(l
->cfgline
);
602 /* maex@space.net (06.09.1996)
603 * destroy an _acl_deny_info_list */
606 aclDestroyDenyInfoList(acl_deny_info_list
** list
)
608 acl_deny_info_list
*a
= NULL
;
609 acl_deny_info_list
*a_next
= NULL
;
610 acl_name_list
*l
= NULL
;
611 acl_name_list
*l_next
= NULL
;
613 for (a
= *list
; a
; a
= a_next
) {
614 for (l
= a
->acl_list
; l
; l
= l_next
) {
620 xfree(a
->err_page_name
);
621 memFree(a
, MEM_ACL_DENY_INFO_LIST
);
628 ACL::dumpGeneric () const
630 debug(28, 3) ("ACL::dumpGeneric: %s type %s\n", name
, typeString());
635 * This function traverses all ACL elements referenced
636 * by an access list (presumably 'http_access'). If
637 * it finds a PURGE method ACL, then it returns TRUE,
640 /* XXX: refactor this more sensibly. perhaps have the parser detect it ? */
642 aclPurgeMethodInUse(acl_access
* a
)
644 return a
->containsPURGE();
647 #include "ACLStrategised.h"
649 acl_access::containsPURGE() const
651 acl_access
const *a
= this;
654 for (; a
; a
= a
->next
) {
655 for (b
= a
->aclList
; b
; b
= b
->next
) {
656 ACLStrategised
<method_t
> *tempAcl
= dynamic_cast<ACLStrategised
<method_t
> *>(b
->_acl
);
661 if (tempAcl
->match(METHOD_PURGE
))
669 /* to be split into separate files in the future */
671 MemPool (*ACLList::Pool
)(NULL
);
673 ACLList::operator new (size_t byteCount
)
675 /* derived classes with different sizes must implement their own new */
676 assert (byteCount
== sizeof (ACLList
));
679 Pool
= memPoolCreate("ACLList", sizeof (ACLList
));
681 return memPoolAlloc(Pool
);
685 ACLList::operator delete (void *address
)
687 memPoolFree (Pool
, address
);
690 CBDATA_CLASS_INIT(acl_access
);
693 acl_access::operator new (size_t)
695 CBDATA_INIT_TYPE(acl_access
);
696 acl_access
*result
= cbdataAlloc(acl_access
);
701 acl_access::operator delete (void *address
)
703 acl_access
*t
= static_cast<acl_access
*>(address
);
707 ACL::Prototype::Prototype() : prototype (NULL
), typeString (NULL
) {}
709 ACL::Prototype::Prototype (ACL
const *aPrototype
, char const *aType
) : prototype (aPrototype
), typeString (aType
)
714 Vector
<ACL::Prototype
const *> * ACL::Prototype::Registry
;
715 void *ACL::Prototype::Initialized
;
718 ACL::Prototype::Registered(char const *aType
)
720 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
721 if (!strcmp (aType
, (*i
)->typeString
))
728 ACL::Prototype::registerMe ()
730 if (!Registry
|| (Initialized
!= ((char *)Registry
- 5)) ) {
731 /* TODO: extract this */
732 /* Not initialised */
733 Registry
= new Vector
<ACL::Prototype
const *>;
734 Initialized
= (char *)Registry
- 5;
737 if (Registered (typeString
))
738 fatalf ("Attempt to register %s twice", typeString
);
740 Registry
->push_back (this);
743 ACL::Prototype::~Prototype()
745 debug (28,2)("ACL::Prototype::~Prototype: TODO: unregister me\n");
749 ACL::Prototype::Factory (char const *typeToClone
)
751 for (iterator i
= Registry
->begin(); i
!= Registry
->end(); ++i
)
752 if (!strcmp (typeToClone
, (*i
)->typeString
))
753 return (*i
)->prototype
->clone();
761 acl
*a
= Config
.aclList
;
762 debug(53, 3) ("ACL::Initialize\n");