]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Gadgets.cc
Centrally destroy all explicit and implicit ACLs to avoid destruction segfaults
[thirdparty/squid.git] / src / acl / Gadgets.cc
1 /*
2 * DEBUG: section 28 Access Control
3 * AUTHOR: Duane Wessels
4 *
5 * This file contains ACL routines that are not part of the
6 * ACL class, nor any other class yet, and that need to be
7 * factored into appropriate places. They are here to reduce
8 * unneeded dependencies between the ACL class and the rest
9 * of squid.
10 *
11 * SQUID Web Proxy Cache http://www.squid-cache.org/
12 * ----------------------------------------------------------
13 *
14 * Squid is the result of efforts by numerous individuals from
15 * the Internet community; see the CONTRIBUTORS file for full
16 * details. Many organizations have provided support for Squid's
17 * development; see the SPONSORS file for full details. Squid is
18 * Copyrighted (C) 2001 by the Regents of the University of
19 * California; see the COPYRIGHT file for full details. Squid
20 * incorporates software developed and/or copyrighted by other
21 * sources; see the CREDITS file for full details.
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 2 of the License, or
26 * (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 *
37 */
38
39 #include "squid.h"
40 #include "acl/Acl.h"
41 #include "acl/AclDenyInfoList.h"
42 #include "acl/AclNameList.h"
43 #include "acl/Checklist.h"
44 #include "acl/Gadgets.h"
45 #include "acl/Strategised.h"
46 #include "acl/Tree.h"
47 #include "ConfigParser.h"
48 #include "errorpage.h"
49 #include "globals.h"
50 #include "HttpRequest.h"
51 #include "Mem.h"
52
53 #include <set>
54 #include <algorithm>
55
56
57 typedef std::set<ACL*> AclSet;
58 /// Accumulates all ACLs to facilitate their clean deletion despite reuse.
59 static AclSet *RegisteredAcls; // TODO: Remove when ACLs are refcounted
60
61 /* does name lookup, returns page_id */
62 err_type
63 aclGetDenyInfoPage(AclDenyInfoList ** head, const char *name, int redirect_allowed)
64 {
65 if (!name) {
66 debugs(28, 3, "ERR_NONE due to a NULL name");
67 return ERR_NONE;
68 }
69
70 AclDenyInfoList *A = NULL;
71
72 debugs(28, 8, HERE << "got called for " << name);
73
74 for (A = *head; A; A = A->next) {
75 AclNameList *L = NULL;
76
77 if (!redirect_allowed && strchr(A->err_page_name, ':') ) {
78 debugs(28, 8, HERE << "Skip '" << A->err_page_name << "' 30x redirects not allowed as response here.");
79 continue;
80 }
81
82 for (L = A->acl_list; L; L = L->next) {
83 if (!strcmp(name, L->name)) {
84 debugs(28, 8, HERE << "match on " << name);
85 return A->err_page_id;
86 }
87
88 }
89 }
90
91 debugs(28, 8, "aclGetDenyInfoPage: no match");
92 return ERR_NONE;
93 }
94
95 /* does name lookup, returns if it is a proxy_auth acl */
96 int
97 aclIsProxyAuth(const char *name)
98 {
99 if (!name) {
100 debugs(28, 3, "false due to a NULL name");
101 return false;
102 }
103
104 debugs(28, 5, "aclIsProxyAuth: called for " << name);
105
106 ACL *a;
107
108 if ((a = ACL::FindByName(name))) {
109 debugs(28, 5, "aclIsProxyAuth: returning " << a->isProxyAuth());
110 return a->isProxyAuth();
111 }
112
113 debugs(28, 3, "aclIsProxyAuth: WARNING, called for nonexistent ACL");
114 return false;
115 }
116
117 /* maex@space.net (05.09.96)
118 * get the info for redirecting "access denied" to info pages
119 * TODO (probably ;-)
120 * currently there is no optimization for
121 * - more than one deny_info line with the same url
122 * - a check, whether the given acl really is defined
123 * - a check, whether an acl is added more than once for the same url
124 */
125
126 void
127 aclParseDenyInfoLine(AclDenyInfoList ** head)
128 {
129 char *t = NULL;
130 AclDenyInfoList *A = NULL;
131 AclDenyInfoList *B = NULL;
132 AclDenyInfoList **T = NULL;
133 AclNameList *L = NULL;
134 AclNameList **Tail = NULL;
135
136 /* first expect a page name */
137
138 if ((t = ConfigParser::NextToken()) == NULL) {
139 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
140 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: missing 'error page' parameter.");
141 return;
142 }
143
144 A = (AclDenyInfoList *)memAllocate(MEM_ACL_DENY_INFO_LIST);
145 A->err_page_id = errorReservePageId(t);
146 A->err_page_name = xstrdup(t);
147 A->next = (AclDenyInfoList *) NULL;
148 /* next expect a list of ACL names */
149 Tail = &A->acl_list;
150
151 while ((t = ConfigParser::NextToken())) {
152 L = (AclNameList *)memAllocate(MEM_ACL_NAME_LIST);
153 xstrncpy(L->name, t, ACL_NAME_SZ-1);
154 *Tail = L;
155 Tail = &L->next;
156 }
157
158 if (A->acl_list == NULL) {
159 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
160 debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: deny_info line contains no ACL's, skipping");
161 memFree(A, MEM_ACL_DENY_INFO_LIST);
162 return;
163 }
164
165 for (B = *head, T = head; B; T = &B->next, B = B->next)
166
167 ; /* find the tail */
168 *T = A;
169 }
170
171 void
172 aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
173 {
174 /* first expect either 'allow' or 'deny' */
175 const char *t = ConfigParser::NextToken();
176
177 if (!t) {
178 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
179 debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'.");
180 return;
181 }
182
183 allow_t action = ACCESS_DUNNO;
184 if (!strcmp(t, "allow"))
185 action = ACCESS_ALLOWED;
186 else if (!strcmp(t, "deny"))
187 action = ACCESS_DENIED;
188 else {
189 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
190 debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'.");
191 return;
192 }
193
194 const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1;
195 MemBuf ctxBuf;
196 ctxBuf.init();
197 ctxBuf.Printf("%s#%d", directive, ruleId);
198 ctxBuf.terminate();
199
200 Acl::AndNode *rule = new Acl::AndNode;
201 rule->context(ctxBuf.content(), config_input_line);
202 rule->lineParse();
203 if (rule->empty()) {
204 debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
205 debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping");
206 delete rule;
207 return;
208 }
209
210 /* Append to the end of this list */
211
212 assert(treep);
213 if (!*treep) {
214 *treep = new Acl::Tree;
215 (*treep)->context(directive, config_input_line);
216 }
217
218 (*treep)->add(rule, action);
219
220 /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */
221 }
222
223 // aclParseAclList does not expect or set actions (cf. aclParseAccessLine)
224 void
225 aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label)
226 {
227 // accomodate callers unable to convert their ACL list context to string
228 if (!label)
229 label = "...";
230
231 MemBuf ctxLine;
232 ctxLine.init();
233 ctxLine.Printf("(%s %s line)", cfg_directive, label);
234 ctxLine.terminate();
235
236 Acl::AndNode *rule = new Acl::AndNode;
237 rule->context(ctxLine.content(), config_input_line);
238 rule->lineParse();
239
240 MemBuf ctxTree;
241 ctxTree.init();
242 ctxTree.Printf("%s %s", cfg_directive, label);
243 ctxTree.terminate();
244
245 // We want a cbdata-protected Tree (despite giving it only one child node).
246 Acl::Tree *tree = new Acl::Tree;
247 tree->add(rule);
248 tree->context(ctxTree.content(), config_input_line);
249
250 assert(treep);
251 assert(!*treep);
252 *treep = tree;
253 }
254
255 void
256 aclRegister(ACL *acl)
257 {
258 if (!acl->registered) {
259 if (!RegisteredAcls)
260 RegisteredAcls = new AclSet;
261 RegisteredAcls->insert(acl);
262 acl->registered = true;
263 }
264 }
265
266 /*********************/
267 /* Destroy functions */
268 /*********************/
269
270 /// helper for RegisteredAcls cleanup
271 static void
272 aclDeleteOne(ACL *acl)
273 {
274 delete acl;
275 }
276
277 /// called to delete ALL Acls.
278 void
279 aclDestroyAcls(ACL ** head)
280 {
281 *head = NULL; // Config.aclList
282 if (AclSet *acls = RegisteredAcls) {
283 debugs(28, 8, "deleting all " << acls->size() << " ACLs");
284 std::for_each(acls->begin(), acls->end(), &aclDeleteOne);
285 acls->clear();
286 }
287 }
288
289 void
290 aclDestroyAclList(ACLList **list)
291 {
292 debugs(28, 8, "aclDestroyAclList: invoked");
293 assert(list);
294 cbdataFree(*list);
295 }
296
297 void
298 aclDestroyAccessList(acl_access ** list)
299 {
300 assert(list);
301 if (*list)
302 debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name);
303 cbdataFree(*list);
304 }
305
306 /* maex@space.net (06.09.1996)
307 * destroy an AclDenyInfoList */
308
309 void
310 aclDestroyDenyInfoList(AclDenyInfoList ** list)
311 {
312 AclDenyInfoList *a = NULL;
313 AclDenyInfoList *a_next = NULL;
314 AclNameList *l = NULL;
315 AclNameList *l_next = NULL;
316
317 debugs(28, 8, "aclDestroyDenyInfoList: invoked");
318
319 for (a = *list; a; a = a_next) {
320 for (l = a->acl_list; l; l = l_next) {
321 l_next = l->next;
322 safe_free(l);
323 }
324
325 a_next = a->next;
326 xfree(a->err_page_name);
327 memFree(a, MEM_ACL_DENY_INFO_LIST);
328 }
329
330 *list = NULL;
331 }