2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
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.
10 #include "AccessLogEntry.h"
11 #include "acl/FilledChecklist.h"
12 #include "adaptation/AccessCheck.h"
13 #include "adaptation/AccessRule.h"
14 #include "adaptation/Config.h"
15 #include "adaptation/Initiator.h"
16 #include "adaptation/Service.h"
17 #include "adaptation/ServiceGroups.h"
18 #include "base/AsyncJobCalls.h"
19 #include "base/TextException.h"
20 #include "ConfigParser.h"
22 #include "HttpReply.h"
23 #include "HttpRequest.h"
25 /** \cond AUTODOCS_IGNORE */
26 cbdata_type
Adaptation::AccessCheck::CBDATA_AccessCheck
= CBDATA_UNKNOWN
;
30 Adaptation::AccessCheck::Start(Method method
, VectPoint vp
,
31 HttpRequest
*req
, HttpReply
*rep
,
32 AccessLogEntry::Pointer
&al
, Adaptation::Initiator
*initiator
)
35 if (Config::Enabled
) {
36 // the new check will call the callback and delete self, eventually
37 AsyncJob::Start(new AccessCheck( // we do not store so not a CbcPointer
38 ServiceFilter(method
, vp
, req
, rep
, al
), initiator
));
42 debugs(83, 3, HERE
<< "adaptation off, skipping");
46 Adaptation::AccessCheck::AccessCheck(const ServiceFilter
&aFilter
,
47 Adaptation::Initiator
*initiator
):
48 AsyncJob("AccessCheck"), filter(aFilter
),
49 theInitiator(initiator
),
53 Adaptation::Icap::History::Pointer h
= filter
.request
->icapHistory();
58 debugs(93, 5, HERE
<< "AccessCheck constructed for " <<
59 methodStr(filter
.method
) << " " << vectPointStr(filter
.point
));
62 Adaptation::AccessCheck::~AccessCheck()
65 Adaptation::Icap::History::Pointer h
= filter
.request
->icapHistory();
72 Adaptation::AccessCheck::start()
76 if (!usedDynamicRules())
80 /// returns true if previous services configured dynamic chaining "rules"
82 Adaptation::AccessCheck::usedDynamicRules()
84 Adaptation::History::Pointer ah
= filter
.request
->adaptHistory();
86 return false; // dynamic rules not enabled or not triggered
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
94 debugs(85,3, HERE
<< "using stored service-proposed rules: " << services
);
96 ServiceGroupPointer g
= new DynamicServiceChain(services
, filter
);
102 /// Walk the access rules list to find rules with applicable service groups
104 Adaptation::AccessCheck::check()
106 debugs(93, 4, HERE
<< "start checking");
108 typedef AccessRules::iterator ARI
;
109 for (ARI i
= AllRules().begin(); i
!= AllRules().end(); ++i
) {
111 if (isCandidate(*r
)) {
112 debugs(93, 5, HERE
<< "check: rule '" << r
->id
<< "' is a candidate");
113 candidates
.push_back(r
->id
);
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.
126 Adaptation::AccessCheck::checkCandidates()
128 debugs(93, 4, HERE
<< "has " << candidates
.size() << " rules");
130 while (!candidates
.empty()) {
131 if (AccessRule
*r
= FindRule(topCandidate())) {
132 /* BUG 2526: what to do when r->acl is empty?? */
133 // XXX: we do not have access to conn->rfc931 here.
134 acl_checklist
= new ACLFilledChecklist(r
->acl
, filter
.request
, dash_str
);
135 if ((acl_checklist
->reply
= filter
.reply
))
136 HTTPMSGLOCK(acl_checklist
->reply
);
137 acl_checklist
->al
= filter
.al
;
138 acl_checklist
->syncAle(filter
.request
, nullptr);
139 acl_checklist
->nonBlockingCheck(AccessCheckCallbackWrapper
, this);
143 candidates
.erase(candidates
.begin()); // the rule apparently went away (reconfigure)
146 debugs(93, 4, HERE
<< "NO candidates left");
152 Adaptation::AccessCheck::AccessCheckCallbackWrapper(allow_t answer
, void *data
)
154 debugs(93, 8, HERE
<< "callback answer=" << answer
);
155 AccessCheck
*ac
= (AccessCheck
*)data
;
157 /** \todo AYJ 2008-06-12: If answer == ACCESS_AUTH_REQUIRED
158 * we should be kicking off an authentication before continuing
159 * with this request. see bug 2400 for details.
162 // convert to async call to get async call protections and features
163 typedef UnaryMemFunT
<AccessCheck
, allow_t
> MyDialer
;
164 AsyncCall::Pointer call
=
165 asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer",
166 MyDialer(ac
, &Adaptation::AccessCheck::noteAnswer
, answer
));
167 ScheduleCallHere(call
);
171 /// process the results of the ACL check
173 Adaptation::AccessCheck::noteAnswer(allow_t answer
)
175 Must(!candidates
.empty()); // the candidate we were checking must be there
176 debugs(93,5, HERE
<< topCandidate() << " answer=" << answer
);
178 if (answer
.allowed()) { // the rule matched
179 ServiceGroupPointer g
= topGroup();
180 if (g
!= NULL
) { // the corresponding group found
187 // no match or the group disappeared during reconfiguration
188 candidates
.erase(candidates
.begin());
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
195 Adaptation::AccessCheck::callBack(const ServiceGroupPointer
&g
)
197 debugs(93,3, HERE
<< g
);
198 CallJobHere1(93, 5, theInitiator
, Adaptation::Initiator
,
199 noteAdaptationAclCheckDone
, g
);
200 mustStop("done"); // called back or will never be able to call back
203 Adaptation::ServiceGroupPointer
204 Adaptation::AccessCheck::topGroup() const
206 ServiceGroupPointer g
;
207 if (candidates
.size()) {
208 if (AccessRule
*r
= FindRule(topCandidate())) {
209 g
= FindGroup(r
->groupId
);
210 debugs(93,5, HERE
<< "top group for " << r
->id
<< " is " << g
);
212 debugs(93,5, HERE
<< "no rule for " << topCandidate());
215 debugs(93,5, HERE
<< "no candidates"); // should not happen
221 /** Returns true iff the rule's service group will be used after ACL matches.
222 Used to detect rules worth ACl-checking. */
224 Adaptation::AccessCheck::isCandidate(AccessRule
&r
)
226 debugs(93,7,HERE
<< "checking candidacy of " << r
.id
<< ", group " <<
229 ServiceGroupPointer g
= FindGroup(r
.groupId
);
232 debugs(93,7,HERE
<< "lost " << r
.groupId
<< " group in rule" << r
.id
);
236 const bool wants
= g
->wants(filter
);
237 debugs(93,7,HERE
<< r
.groupId
<< (wants
? " wants" : " ignores"));