]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl.cc
Summary: Final MSVC fixups.
[thirdparty/squid.git] / src / acl.cc
1 /*
2 * $Id: acl.cc,v 1.311 2003/08/10 11:00:40 robertc Exp $
3 *
4 * DEBUG: section 28 Access Control
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
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.
18 *
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.
23 *
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.
28 *
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.
32 *
33 */
34
35 #include "squid.h"
36 #include "ACL.h"
37 #include "ACLChecklist.h"
38 #include "HttpRequest.h"
39
40 const char *AclMatchedName = NULL;
41
42 void *
43 ACL::operator new (size_t byteCount)
44 {
45 fatal ("unusable ACL::new");
46 return (void *)1;
47 }
48
49 void
50 ACL::operator delete (void *address)
51 {
52 fatal ("unusable ACL::delete");
53 }
54
55 acl *
56 ACL::FindByName(const char *name)
57 {
58 acl *a;
59
60 for (a = Config.aclList; a; a = a->next)
61 if (!strcasecmp(a->name, name))
62 return a;
63
64 return NULL;
65 }
66
67 ACL *
68 ACL::Factory (char const *type)
69 {
70 ACL *result = Prototype::Factory (type);
71
72 if (!result)
73 fatal ("Unknown acl type in ACL::Factory");
74
75 return result;
76 }
77
78 ACL::ACL () {}
79
80 void
81 ACL::ParseAclLine(acl ** head)
82 {
83 /* we're already using strtok() to grok the line */
84 char *t = NULL;
85 acl *A = NULL;
86 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
87 int new_acl = 0;
88
89 /* snarf the ACL name */
90
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");
95 return;
96 }
97
98 xstrncpy(aclname, t, ACL_NAME_SZ);
99 /* snarf the ACL type */
100 char *theType;
101
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");
106 return;
107 }
108
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);
113 return;
114 }
115
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);
121 new_acl = 1;
122 } else {
123 if (strcmp (A->typeString(),theType) ) {
124 debug(28, 0) ("aclParseAclLine: ACL '%s' already exists with different type, skipping.\n", A->name);
125 return;
126 }
127
128 debug(28, 3) ("aclParseAclLine: Appending to '%s'\n", aclname);
129 new_acl = 0;
130 }
131
132 /*
133 * Here we set AclMatchedName in case we need to use it in a
134 * warning message in aclDomainCompare().
135 */
136 AclMatchedName = A->name; /* ugly */
137
138 /*split the function here */
139 A->parse();
140
141 /*
142 * Clear AclMatchedName from our temporary hack
143 */
144 AclMatchedName = NULL; /* ugly */
145
146 if (!new_acl)
147 return;
148
149 if (!A->valid()) {
150 debug(28, 0) ("aclParseAclLine: IGNORING invalid ACL: %s\n",
151 A->cfgline);
152 delete A;
153 /* Do we need this? */
154 A = NULL;
155 return;
156 }
157
158 /* append */
159 while (*head)
160 head = &(*head)->next;
161
162 *head = A;
163 }
164
165 /* does name lookup, returns page_id */
166 err_type
167 aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name)
168 {
169 acl_deny_info_list *A = NULL;
170 acl_name_list *L = NULL;
171
172 A = *head;
173
174 if (NULL == *head) /* empty list */
175 return ERR_NONE;
176
177 while (A) {
178 L = A->acl_list;
179
180 if (NULL == L) /* empty list should never happen, but in case */
181 continue;
182
183 while (L) {
184 if (!strcmp(name, L->name))
185 return A->err_page_id;
186
187 L = L->next;
188 }
189
190 A = A->next;
191 }
192
193 return ERR_NONE;
194 }
195
196 /* does name lookup, returns if it is a proxy_auth acl */
197 int
198 aclIsProxyAuth(const char *name)
199 {
200 if (NULL == name)
201 return false;
202
203 acl *a;
204
205 if ((a = ACL::FindByName(name)))
206 return a->isProxyAuth();
207
208 return false;
209 }
210
211 bool
212 ACL::isProxyAuth() const
213 {
214 return false;
215 }
216
217 /* maex@space.net (05.09.96)
218 * get the info for redirecting "access denied" to info pages
219 * TODO (probably ;-)
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
224 */
225
226 void
227 aclParseDenyInfoLine(acl_deny_info_list ** head)
228 {
229 char *t = NULL;
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;
235
236 /* first expect a page name */
237
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");
242 return;
243 }
244
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 */
250 Tail = &A->acl_list;
251
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);
255 *Tail = L;
256 Tail = &L->next;
257 }
258
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);
264 return;
265 }
266
267 for (B = *head, T = head; B; T = &B->next, B = B->next)
268
269 ; /* find the tail */
270 *T = A;
271 }
272
273 void
274 aclParseAccessLine(acl_access ** head)
275 {
276 char *t = NULL;
277 acl_access *A = NULL;
278 acl_access *B = NULL;
279 acl_access **T = NULL;
280
281 /* first expect either 'allow' or 'deny' */
282
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");
287 return;
288 }
289
290 A = new acl_access;
291
292 if (!strcmp(t, "allow"))
293 A->allow = ACCESS_ALLOWED;
294 else if (!strcmp(t, "deny"))
295 A->allow = ACCESS_DENIED;
296 else {
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);
300 delete A;
301 return;
302 }
303
304 aclParseAclList(&A->aclList);
305
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");
310 delete A;
311 return;
312 }
313
314 A->cfgline = xstrdup(config_input_line);
315 /* Append to the end of this list */
316
317 for (B = *head, T = head; B; T = &B->next, B = B->next)
318
319 ;
320 *T = A;
321
322 /* We lock _acl_access structures in ACLChecklist::check() */
323 }
324
325 ACLList::ACLList() : op (1), _acl (NULL), next (NULL)
326 {}
327
328 void
329 ACLList::negated(bool isNegated)
330 {
331 if (isNegated)
332 op = 0;
333 else
334 op = 1;
335 }
336
337 void
338 aclParseAclList(acl_list ** head)
339 {
340 acl_list **Tail = head; /* sane name in the use below */
341 acl *a = NULL;
342 char *t;
343
344 /* next expect a list of ACL names, possibly preceeded
345 * by '!' for negation */
346
347 while ((t = strtok(NULL, w_space))) {
348 acl_list *L = new ACLList;
349
350 if (*t == '!') {
351 L->negated (true);
352 t++;
353 }
354
355 debug(28, 3) ("aclParseAccessLine: looking for ACL name '%s'\n", t);
356 a = ACL::FindByName(t);
357
358 if (a == NULL) {
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);
362 delete L;
363 continue;
364 }
365
366 L->_acl = a;
367 *Tail = L;
368 Tail = &L->next;
369 }
370 }
371
372 /* ACL result caching routines */
373
374 int
375 ACL::matchForCache(ACLChecklist *checklist)
376 {
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 */
381 }
382
383 /*
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.
389 * RBC
390 */
391 int
392 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
393 {
394 acl_proxy_auth_match_cache *auth_match;
395 dlink_node *link;
396 link = cache->head;
397
398 while (link) {
399 auth_match = (acl_proxy_auth_match_cache *)link->data;
400
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;
404 }
405
406 link = link->next;
407 }
408
409 auth_match = NULL;
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;
415 }
416
417 void
418 aclCacheMatchFlush(dlink_list * cache)
419 {
420 acl_proxy_auth_match_cache *auth_match;
421 dlink_node *link, *tmplink;
422 link = cache->head;
423
424 while (link) {
425 auth_match = (acl_proxy_auth_match_cache *)link->data;
426 tmplink = link;
427 link = link->next;
428 dlinkDelete(tmplink, cache);
429 memFree(auth_match, MEM_ACL_PROXY_AUTH_MATCH);
430 }
431 }
432
433 bool
434 ACL::requiresReply() const
435 {
436 return false;
437 }
438
439 bool
440 ACL::requiresRequest() const
441 {
442 return false;
443 }
444
445 int
446 ACL::checklistMatches(ACLChecklist *checklist)
447 {
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);
451 return 0;
452 }
453
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);
457 return 0;
458 }
459
460 debug(28, 3) ("aclMatchAcl: checking '%s'\n", cfgline);
461 return match(checklist);
462 }
463
464 bool
465 ACLList::matches (ACLChecklist *checklist) const
466 {
467 assert (_acl);
468 AclMatchedName = _acl->name;
469 debug(28, 3) ("ACLList::matches: checking %s%s\n",
470 op ? null_string : "!", _acl->name);
471
472 if (_acl->checklistMatches(checklist) != op) {
473 return false;
474 }
475
476 return true;
477 }
478
479 /* Warning: do not cbdata lock checklist here - it
480 * may be static or on the stack
481 */
482 int
483 aclCheckFast(const acl_access * A, ACLChecklist * checklist)
484 {
485 allow_t allow = ACCESS_DENIED;
486 PROF_start(aclCheckFast);
487 debug(28, 5) ("aclCheckFast: list: %p\n", A);
488
489 while (A) {
490 allow = A->allow;
491 checklist->matchAclListFast(A->aclList);
492
493 if (checklist->finished()) {
494 PROF_stop(aclCheckFast);
495 return allow == ACCESS_ALLOWED;
496 }
497
498 A = A->next;
499 }
500
501 debug(28, 5) ("aclCheckFast: no matches, returning: %d\n", allow == ACCESS_DENIED);
502 PROF_stop(aclCheckFast);
503 return allow == ACCESS_DENIED;
504 }
505
506 /*
507 * Any ACLChecklist created by aclChecklistCreate() must eventually be
508 * freed by ACLChecklist::operator delete(). There are two common cases:
509 *
510 * A) Using aclCheckFast(): The caller creates the ACLChecklist using
511 * aclChecklistCreate(), checks it using aclCheckFast(), and frees it
512 * using aclChecklistFree().
513 *
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().
522 */
523
524
525 ACLChecklist *
526 aclChecklistCreate(const acl_access * A, HttpRequest * request, const char *ident)
527 {
528 ACLChecklist *checklist = new ACLChecklist;
529
530 if (A)
531 checklist->accessList = cbdataReference(A);
532
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;
538 }
539
540 #if USE_IDENT
541 if (ident)
542 xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
543
544 #endif
545
546 checklist->auth_user_request = NULL;
547
548 return checklist;
549 }
550
551 /*********************/
552 /* Destroy functions */
553 /*********************/
554
555 void
556 aclDestroyAcls(acl ** head)
557 {
558 ACL *next = NULL;
559
560 for (acl *a = *head; a; a = next) {
561 next = a->next;
562 delete a;
563 }
564
565 *head = NULL;
566 }
567
568 ACL::~ACL()
569 {
570 debug(28, 3) ("aclDestroyAcls: '%s'\n", cfgline);
571 safe_free(cfgline);
572 }
573
574 void
575 aclDestroyAclList(acl_list ** head)
576 {
577 acl_list *l;
578
579 for (l = *head; l; l = *head) {
580 *head = l->next;
581 delete l;
582 }
583 }
584
585 void
586 aclDestroyAccessList(acl_access ** list)
587 {
588 acl_access *l = NULL;
589 acl_access *next = NULL;
590
591 for (l = *list; l; l = next) {
592 debug(28, 3) ("aclDestroyAccessList: '%s'\n", l->cfgline);
593 next = l->next;
594 aclDestroyAclList(&l->aclList);
595 safe_free(l->cfgline);
596 cbdataFree(l);
597 }
598
599 *list = NULL;
600 }
601
602 /* maex@space.net (06.09.1996)
603 * destroy an _acl_deny_info_list */
604
605 void
606 aclDestroyDenyInfoList(acl_deny_info_list ** list)
607 {
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;
612
613 for (a = *list; a; a = a_next) {
614 for (l = a->acl_list; l; l = l_next) {
615 l_next = l->next;
616 safe_free(l);
617 }
618
619 a_next = a->next;
620 xfree(a->err_page_name);
621 memFree(a, MEM_ACL_DENY_INFO_LIST);
622 }
623
624 *list = NULL;
625 }
626
627 wordlist *
628 ACL::dumpGeneric () const
629 {
630 debug(28, 3) ("ACL::dumpGeneric: %s type %s\n", name, typeString());
631 return dump();
632 }
633
634 /*
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,
638 * otherwise FALSE.
639 */
640 /* XXX: refactor this more sensibly. perhaps have the parser detect it ? */
641 int
642 aclPurgeMethodInUse(acl_access * a)
643 {
644 return a->containsPURGE();
645 }
646
647 #include "ACLStrategised.h"
648 bool
649 acl_access::containsPURGE() const
650 {
651 acl_access const *a = this;
652 acl_list *b;
653
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);
657
658 if (!tempAcl)
659 continue;
660
661 if (tempAcl->match(METHOD_PURGE))
662 return true;
663 }
664 }
665
666 return false;
667 }
668
669 /* to be split into separate files in the future */
670
671 MemPool (*ACLList::Pool)(NULL);
672 void *
673 ACLList::operator new (size_t byteCount)
674 {
675 /* derived classes with different sizes must implement their own new */
676 assert (byteCount == sizeof (ACLList));
677
678 if (!Pool)
679 Pool = memPoolCreate("ACLList", sizeof (ACLList));
680
681 return memPoolAlloc(Pool);
682 }
683
684 void
685 ACLList::operator delete (void *address)
686 {
687 memPoolFree (Pool, address);
688 }
689
690 CBDATA_CLASS_INIT(acl_access);
691
692 void *
693 acl_access::operator new (size_t)
694 {
695 CBDATA_INIT_TYPE(acl_access);
696 acl_access *result = cbdataAlloc(acl_access);
697 return result;
698 }
699
700 void
701 acl_access::operator delete (void *address)
702 {
703 acl_access *t = static_cast<acl_access *>(address);
704 cbdataFree(t);
705 }
706
707 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
708
709 ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
710 {
711 registerMe ();
712 }
713
714 Vector<ACL::Prototype const *> * ACL::Prototype::Registry;
715 void *ACL::Prototype::Initialized;
716
717 bool
718 ACL::Prototype::Registered(char const *aType)
719 {
720 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
721 if (!strcmp (aType, (*i)->typeString))
722 return true;
723
724 return false;
725 }
726
727 void
728 ACL::Prototype::registerMe ()
729 {
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;
735 }
736
737 if (Registered (typeString))
738 fatalf ("Attempt to register %s twice", typeString);
739
740 Registry->push_back (this);
741 }
742
743 ACL::Prototype::~Prototype()
744 {
745 debug (28,2)("ACL::Prototype::~Prototype: TODO: unregister me\n");
746 }
747
748 ACL *
749 ACL::Prototype::Factory (char const *typeToClone)
750 {
751 for (iterator i = Registry->begin(); i != Registry->end(); ++i)
752 if (!strcmp (typeToClone, (*i)->typeString))
753 return (*i)->prototype->clone();
754
755 return NULL;
756 }
757
758 void
759 ACL::Initialize()
760 {
761 acl *a = Config.aclList;
762 debug(53, 3) ("ACL::Initialize\n");
763
764 while (a) {
765 a->prepareForUse();
766 a = a->next;
767 }
768 }