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