]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
5b74111a | 2 | * Copyright (C) 1996-2018 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" |
d295d770 | 30 | |
ed898bdf AR |
31 | #include <set> |
32 | #include <algorithm> | |
33 | ||
ed898bdf AR |
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 | ||
d295d770 | 38 | /* does name lookup, returns page_id */ |
39 | err_type | |
7f0b3324 | 40 | aclGetDenyInfoPage(AclDenyInfoList ** head, const char *name, int redirect_allowed) |
d295d770 | 41 | { |
37ea6ef5 NH |
42 | if (!name) { |
43 | debugs(28, 3, "ERR_NONE due to a NULL name"); | |
44 | return ERR_NONE; | |
45 | } | |
46 | ||
7f0b3324 | 47 | AclDenyInfoList *A = NULL; |
d295d770 | 48 | |
99697e8e | 49 | debugs(28, 8, HERE << "got called for " << name); |
d295d770 | 50 | |
9ce7856a | 51 | for (A = *head; A; A = A->next) { |
6be70545 | 52 | AclNameList *L = NULL; |
d295d770 | 53 | |
99697e8e AJ |
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."); | |
d295d770 | 56 | continue; |
57 | } | |
58 | ||
9ce7856a | 59 | for (L = A->acl_list; L; L = L->next) { |
d295d770 | 60 | if (!strcmp(name, L->name)) { |
99697e8e | 61 | debugs(28, 8, HERE << "match on " << name); |
d295d770 | 62 | return A->err_page_id; |
63 | } | |
64 | ||
d295d770 | 65 | } |
d295d770 | 66 | } |
67 | ||
bf8fe701 | 68 | debugs(28, 8, "aclGetDenyInfoPage: no match"); |
d295d770 | 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 | { | |
fc5f792a NH |
76 | if (!name) { |
77 | debugs(28, 3, "false due to a NULL name"); | |
d295d770 | 78 | return false; |
fc5f792a NH |
79 | } |
80 | ||
81 | debugs(28, 5, "aclIsProxyAuth: called for " << name); | |
d295d770 | 82 | |
83 | ACL *a; | |
84 | ||
85 | if ((a = ACL::FindByName(name))) { | |
bf8fe701 | 86 | debugs(28, 5, "aclIsProxyAuth: returning " << a->isProxyAuth()); |
d295d770 | 87 | return a->isProxyAuth(); |
88 | } | |
89 | ||
bf8fe701 | 90 | debugs(28, 3, "aclIsProxyAuth: WARNING, called for nonexistent ACL"); |
d295d770 | 91 | return false; |
92 | } | |
93 | ||
d295d770 | 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 | |
7f0b3324 | 104 | aclParseDenyInfoLine(AclDenyInfoList ** head) |
d295d770 | 105 | { |
106 | char *t = NULL; | |
2e00d185 AJ |
107 | AclDenyInfoList *B; |
108 | AclDenyInfoList **T; | |
6be70545 FC |
109 | AclNameList *L = NULL; |
110 | AclNameList **Tail = NULL; | |
d295d770 | 111 | |
112 | /* first expect a page name */ | |
113 | ||
2eceb328 | 114 | if ((t = ConfigParser::NextToken()) == NULL) { |
fa84c01d FC |
115 | debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); |
116 | debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: missing 'error page' parameter."); | |
d295d770 | 117 | return; |
118 | } | |
119 | ||
2e00d185 AJ |
120 | AclDenyInfoList *A = new AclDenyInfoList(t); |
121 | ||
d295d770 | 122 | /* next expect a list of ACL names */ |
123 | Tail = &A->acl_list; | |
124 | ||
2eceb328 | 125 | while ((t = ConfigParser::NextToken())) { |
2e00d185 | 126 | L = new AclNameList(t); |
d295d770 | 127 | *Tail = L; |
128 | Tail = &L->next; | |
129 | } | |
130 | ||
131 | if (A->acl_list == NULL) { | |
fa84c01d FC |
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"); | |
2e00d185 | 134 | delete A; |
d295d770 | 135 | return; |
136 | } | |
137 | ||
138 | for (B = *head, T = head; B; T = &B->next, B = B->next) | |
139 | ||
f53969cc | 140 | ; /* find the tail */ |
d295d770 | 141 | *T = A; |
142 | } | |
143 | ||
144 | void | |
6f58d7d7 | 145 | aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep) |
d295d770 | 146 | { |
d295d770 | 147 | /* first expect either 'allow' or 'deny' */ |
2eceb328 | 148 | const char *t = ConfigParser::NextToken(); |
d295d770 | 149 | |
6f58d7d7 | 150 | if (!t) { |
fa84c01d FC |
151 | debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); |
152 | debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'."); | |
d295d770 | 153 | return; |
154 | } | |
155 | ||
6f58d7d7 | 156 | allow_t action = ACCESS_DUNNO; |
d295d770 | 157 | if (!strcmp(t, "allow")) |
6f58d7d7 | 158 | action = ACCESS_ALLOWED; |
d295d770 | 159 | else if (!strcmp(t, "deny")) |
6f58d7d7 | 160 | action = ACCESS_DENIED; |
d295d770 | 161 | else { |
fa84c01d FC |
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 << "'."); | |
d295d770 | 164 | return; |
165 | } | |
166 | ||
6f58d7d7 AR |
167 | const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1; |
168 | MemBuf ctxBuf; | |
169 | ctxBuf.init(); | |
4391cd15 | 170 | ctxBuf.appendf("%s#%d", directive, ruleId); |
6f58d7d7 | 171 | ctxBuf.terminate(); |
d295d770 | 172 | |
6f58d7d7 AR |
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); | |
fa84c01d | 178 | debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping"); |
6f58d7d7 | 179 | delete rule; |
d295d770 | 180 | return; |
181 | } | |
182 | ||
d295d770 | 183 | /* Append to the end of this list */ |
184 | ||
6f58d7d7 AR |
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); | |
d295d770 | 192 | |
2efeb0b7 | 193 | /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */ |
d295d770 | 194 | } |
195 | ||
6f58d7d7 | 196 | // aclParseAclList does not expect or set actions (cf. aclParseAccessLine) |
d295d770 | 197 | void |
6f58d7d7 | 198 | aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label) |
d295d770 | 199 | { |
6f58d7d7 AR |
200 | // accomodate callers unable to convert their ACL list context to string |
201 | if (!label) | |
202 | label = "..."; | |
203 | ||
204 | MemBuf ctxLine; | |
205 | ctxLine.init(); | |
4391cd15 | 206 | ctxLine.appendf("(%s %s line)", cfg_directive, label); |
6f58d7d7 AR |
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(); | |
4391cd15 | 215 | ctxTree.appendf("%s %s", cfg_directive, label); |
6f58d7d7 AR |
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; | |
d295d770 | 226 | } |
227 | ||
ed898bdf AR |
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 | ||
855a0107 AR |
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 | ||
d295d770 | 251 | /*********************/ |
252 | /* Destroy functions */ | |
253 | /*********************/ | |
254 | ||
ed898bdf | 255 | /// called to delete ALL Acls. |
d295d770 | 256 | void |
257 | aclDestroyAcls(ACL ** head) | |
258 | { | |
ed898bdf AR |
259 | *head = NULL; // Config.aclList |
260 | if (AclSet *acls = RegisteredAcls) { | |
261 | debugs(28, 8, "deleting all " << acls->size() << " ACLs"); | |
855a0107 AR |
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 | } | |
d295d770 | 271 | } |
d295d770 | 272 | } |
273 | ||
274 | void | |
6f58d7d7 | 275 | aclDestroyAclList(ACLList **list) |
d295d770 | 276 | { |
bf8fe701 | 277 | debugs(28, 8, "aclDestroyAclList: invoked"); |
6f58d7d7 | 278 | assert(list); |
855a0107 AR |
279 | delete *list; |
280 | *list = NULL; | |
d295d770 | 281 | } |
282 | ||
283 | void | |
284 | aclDestroyAccessList(acl_access ** list) | |
285 | { | |
6f58d7d7 AR |
286 | assert(list); |
287 | if (*list) | |
288 | debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name); | |
855a0107 AR |
289 | delete *list; |
290 | *list = NULL; | |
d295d770 | 291 | } |
292 | ||
293 | /* maex@space.net (06.09.1996) | |
7f0b3324 | 294 | * destroy an AclDenyInfoList */ |
d295d770 | 295 | |
296 | void | |
7f0b3324 | 297 | aclDestroyDenyInfoList(AclDenyInfoList ** list) |
d295d770 | 298 | { |
7f0b3324 FC |
299 | AclDenyInfoList *a = NULL; |
300 | AclDenyInfoList *a_next = NULL; | |
6be70545 FC |
301 | AclNameList *l = NULL; |
302 | AclNameList *l_next = NULL; | |
d295d770 | 303 | |
bf8fe701 | 304 | debugs(28, 8, "aclDestroyDenyInfoList: invoked"); |
d295d770 | 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; | |
2e00d185 | 313 | delete a; |
d295d770 | 314 | } |
315 | ||
316 | *list = NULL; | |
317 | } | |
f53969cc | 318 |