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