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