]>
Commit | Line | Data |
---|---|---|
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 |
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 | ||
d295d770 | 39 | /* does name lookup, returns page_id */ |
40 | err_type | |
7f0b3324 | 41 | aclGetDenyInfoPage(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 */ | |
71 | int | |
72 | aclIsProxyAuth(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 | ||
101 | void | |
7f0b3324 | 102 | aclParseDenyInfoLine(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 | ||
136 | void | |
6f58d7d7 | 137 | aclParseAccessLine(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 | 189 | void |
6f58d7d7 | 190 | aclParseAclList(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 |
220 | void |
221 | aclRegister(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 |
232 | static | |
233 | void | |
234 | aclDeregister(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 | 248 | void |
249 | aclDestroyAcls(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 | ||
266 | void | |
6f58d7d7 | 267 | aclDestroyAclList(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 | ||
275 | void | |
276 | aclDestroyAccessList(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 | |
288 | void | |
7f0b3324 | 289 | aclDestroyDenyInfoList(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 |