]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/AccessCheck.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
CommitLineData
bbc27441 1/*
77b1029d 2 * Copyright (C) 1996-2020 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
582c2af2 9#include "squid.h"
af0ded40 10#include "AccessLogEntry.h"
127dce76 11#include "acl/FilledChecklist.h"
582c2af2
FC
12#include "adaptation/AccessCheck.h"
13#include "adaptation/AccessRule.h"
14#include "adaptation/Config.h"
79628299 15#include "adaptation/Initiator.h"
62c7f90e
AR
16#include "adaptation/Service.h"
17#include "adaptation/ServiceGroups.h"
582c2af2 18#include "base/AsyncJobCalls.h"
3d93a84d 19#include "base/TextException.h"
582c2af2
FC
20#include "ConfigParser.h"
21#include "globals.h"
22#include "HttpReply.h"
23#include "HttpRequest.h"
62c7f90e 24
d6d0eb11 25/** \cond AUTODOCS_IGNORE */
62c7f90e 26cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
d85b8894 27/** \endcond */
62c7f90e 28
abd4b611
AR
29bool
30Adaptation::AccessCheck::Start(Method method, VectPoint vp,
af0ded40
CT
31 HttpRequest *req, HttpReply *rep,
32 AccessLogEntry::Pointer &al, Adaptation::Initiator *initiator)
26ac0430 33{
abd4b611
AR
34
35 if (Config::Enabled) {
36 // the new check will call the callback and delete self, eventually
4299f876 37 AsyncJob::Start(new AccessCheck( // we do not store so not a CbcPointer
af0ded40 38 ServiceFilter(method, vp, req, rep, al), initiator));
4299f876 39 return true;
26ac0430 40 }
abd4b611
AR
41
42 debugs(83, 3, HERE << "adaptation off, skipping");
43 return false;
44}
45
a22e6cd3 46Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter,
79628299 47 Adaptation::Initiator *initiator):
f53969cc
SM
48 AsyncJob("AccessCheck"), filter(aFilter),
49 theInitiator(initiator),
50 acl_checklist(NULL)
62c7f90e 51{
3ff65596 52#if ICAP_CLIENT
a22e6cd3 53 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
3ff65596
AR
54 if (h != NULL)
55 h->start("ACL");
56#endif
57
a22e6cd3 58 debugs(93, 5, HERE << "AccessCheck constructed for " <<
e1381638 59 methodStr(filter.method) << " " << vectPointStr(filter.point));
62c7f90e
AR
60}
61
62Adaptation::AccessCheck::~AccessCheck()
63{
3ff65596 64#if ICAP_CLIENT
a22e6cd3 65 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
3ff65596
AR
66 if (h != NULL)
67 h->stop("ACL");
68#endif
62c7f90e
AR
69}
70
dd5d7172 71void
e1381638
AJ
72Adaptation::AccessCheck::start()
73{
74 AsyncJob::start();
53340485
AR
75
76 if (!usedDynamicRules())
77 check();
78}
79
80/// returns true if previous services configured dynamic chaining "rules"
81bool
82Adaptation::AccessCheck::usedDynamicRules()
83{
84 Adaptation::History::Pointer ah = filter.request->adaptHistory();
85 if (!ah)
86 return false; // dynamic rules not enabled or not triggered
87
88 DynamicGroupCfg services;
89 if (!ah->extractFutureServices(services)) { // clears history
90 debugs(85,9, HERE << "no service-proposed rules stored");
91 return false; // earlier service did not plan for the future
92 }
93
94 debugs(85,3, HERE << "using stored service-proposed rules: " << services);
95
96 ServiceGroupPointer g = new DynamicServiceChain(services, filter);
97 callBack(g);
98 Must(done());
99 return true;
dd5d7172
AR
100}
101
a22e6cd3 102/// Walk the access rules list to find rules with applicable service groups
62c7f90e
AR
103void
104Adaptation::AccessCheck::check()
105{
192378eb 106 debugs(93, 4, HERE << "start checking");
62c7f90e
AR
107
108 typedef AccessRules::iterator ARI;
109 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
62c7f90e 110 AccessRule *r = *i;
a22e6cd3
AR
111 if (isCandidate(*r)) {
112 debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate");
4c9eadc2 113 candidates.push_back(r->id);
62c7f90e
AR
114 }
115 }
116
117 checkCandidates();
118}
119
120// XXX: Here and everywhere we call FindRule(topCandidate()):
121// Once we identified the candidate, we should not just ignore it
122// if reconfigure changes rules. We should either lock the rule to
123// prevent reconfigure from stealing it or restart the check with
124// new rules. Throwing an exception may also be appropriate.
125void
126Adaptation::AccessCheck::checkCandidates()
127{
192378eb 128 debugs(93, 4, HERE << "has " << candidates.size() << " rules");
62c7f90e
AR
129
130 while (!candidates.empty()) {
131 if (AccessRule *r = FindRule(topCandidate())) {
b50e327b 132 /* BUG 2526: what to do when r->acl is empty?? */
62c7f90e 133 // XXX: we do not have access to conn->rfc931 here.
a22e6cd3 134 acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str);
eced1234
CT
135 if ((acl_checklist->reply = filter.reply))
136 HTTPMSGLOCK(acl_checklist->reply);
d4806c91 137 acl_checklist->al = filter.al;
cb365059 138 acl_checklist->syncAle(filter.request, nullptr);
62c7f90e
AR
139 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
140 return;
141 }
142
c8ea3cc0 143 candidates.erase(candidates.begin()); // the rule apparently went away (reconfigure)
62c7f90e
AR
144 }
145
192378eb 146 debugs(93, 4, HERE << "NO candidates left");
a22e6cd3
AR
147 callBack(NULL);
148 Must(done());
62c7f90e
AR
149}
150
151void
329c128c 152Adaptation::AccessCheck::AccessCheckCallbackWrapper(Acl::Answer answer, void *data)
62c7f90e 153{
192378eb 154 debugs(93, 8, HERE << "callback answer=" << answer);
62c7f90e 155 AccessCheck *ac = (AccessCheck*)data;
f9cf4ba9 156
7dfddb79 157 /** \todo AYJ 2008-06-12: If answer == ACCESS_AUTH_REQUIRED
f9cf4ba9
AJ
158 * we should be kicking off an authentication before continuing
159 * with this request. see bug 2400 for details.
160 */
7866ecc9
AR
161
162 // convert to async call to get async call protections and features
329c128c 163 typedef UnaryMemFunT<AccessCheck, Acl::Answer> MyDialer;
7866ecc9
AR
164 AsyncCall::Pointer call =
165 asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer",
2efeb0b7 166 MyDialer(ac, &Adaptation::AccessCheck::noteAnswer, answer));
7866ecc9
AR
167 ScheduleCallHere(call);
168
62c7f90e
AR
169}
170
a22e6cd3 171/// process the results of the ACL check
62c7f90e 172void
329c128c 173Adaptation::AccessCheck::noteAnswer(Acl::Answer answer)
62c7f90e 174{
a22e6cd3
AR
175 Must(!candidates.empty()); // the candidate we were checking must be there
176 debugs(93,5, HERE << topCandidate() << " answer=" << answer);
177
06bf5384 178 if (answer.allowed()) { // the rule matched
a22e6cd3
AR
179 ServiceGroupPointer g = topGroup();
180 if (g != NULL) { // the corresponding group found
181 callBack(g);
182 Must(done());
183 return;
184 }
62c7f90e
AR
185 }
186
a22e6cd3 187 // no match or the group disappeared during reconfiguration
c8ea3cc0 188 candidates.erase(candidates.begin());
a22e6cd3 189 checkCandidates();
62c7f90e
AR
190}
191
a22e6cd3
AR
192/// call back with a possibly nil group; the job ends here because all failures
193/// at this point are fatal to the access check process
62c7f90e 194void
a22e6cd3 195Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
62c7f90e 196{
a22e6cd3 197 debugs(93,3, HERE << g);
79628299
CT
198 CallJobHere1(93, 5, theInitiator, Adaptation::Initiator,
199 noteAdaptationAclCheckDone, g);
a22e6cd3
AR
200 mustStop("done"); // called back or will never be able to call back
201}
62c7f90e 202
a22e6cd3
AR
203Adaptation::ServiceGroupPointer
204Adaptation::AccessCheck::topGroup() const
205{
206 ServiceGroupPointer g;
62c7f90e
AR
207 if (candidates.size()) {
208 if (AccessRule *r = FindRule(topCandidate())) {
a22e6cd3
AR
209 g = FindGroup(r->groupId);
210 debugs(93,5, HERE << "top group for " << r->id << " is " << g);
62c7f90e 211 } else {
a22e6cd3 212 debugs(93,5, HERE << "no rule for " << topCandidate());
62c7f90e 213 }
26ac0430 214 } else {
a22e6cd3 215 debugs(93,5, HERE << "no candidates"); // should not happen
26ac0430 216 }
62c7f90e 217
a22e6cd3 218 return g;
62c7f90e
AR
219}
220
a22e6cd3
AR
221/** Returns true iff the rule's service group will be used after ACL matches.
222 Used to detect rules worth ACl-checking. */
223bool
224Adaptation::AccessCheck::isCandidate(AccessRule &r)
26ac0430 225{
a22e6cd3 226 debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " <<
e1381638 227 r.groupId);
62c7f90e 228
a22e6cd3 229 ServiceGroupPointer g = FindGroup(r.groupId);
62c7f90e
AR
230
231 if (!g) {
a22e6cd3
AR
232 debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id);
233 return false;
62c7f90e
AR
234 }
235
a22e6cd3
AR
236 const bool wants = g->wants(filter);
237 debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores"));
238 return wants;
62c7f90e 239}
f53969cc 240