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