]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/Checklist.cc
Kill some old unused & unmaintained code for sending an error response
[thirdparty/squid.git] / src / acl / Checklist.cc
CommitLineData
225b7b10 1/*
225b7b10 2 * DEBUG: section 28 Access Control
225b7b10 3 */
4
582c2af2 5#include "squid.h"
351fe86d 6#include "acl/Checklist.h"
6f58d7d7 7#include "acl/Tree.h"
582c2af2
FC
8#include "Debug.h"
9#include "profiler/Profiler.h"
225b7b10 10
6f58d7d7
AR
11/// common parts of nonBlockingCheck() and resumeNonBlockingCheck()
12bool
13ACLChecklist::prepNonBlocking()
225b7b10 14{
6f58d7d7 15 assert(accessList);
62e76326 16
315b856d 17 if (callerGone()) {
e0f7153c 18 checkCallback(ACCESS_DUNNO); // the answer does not really matter
6f58d7d7 19 return false;
070b2c0c
AJ
20 }
21
e936c41c 22 /** \par
b5e48e70 23 * If the accessList is no longer valid (i.e. its been
e936c41c
AR
24 * freed because of a reconfigure), then bail with ACCESS_DUNNO.
25 */
62e76326 26
e936c41c
AR
27 if (!cbdataReferenceValid(accessList)) {
28 cbdataReferenceDone(accessList);
29 debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
30 checkCallback(ACCESS_DUNNO);
31 return false;
32 }
33
ef689e2b 34 return true;
225b7b10 35}
36
37void
6f58d7d7 38ACLChecklist::completeNonBlocking()
225b7b10 39{
6f58d7d7 40 assert(!asyncInProgress());
225b7b10 41
6f58d7d7
AR
42 if (!finished())
43 calcImplicitAnswer();
44
45 cbdataReferenceDone(accessList);
46 checkCallback(currentAnswer());
225b7b10 47}
48
49void
e0f7153c 50ACLChecklist::markFinished(const allow_t &finalAnswer, const char *reason)
225b7b10 51{
3841dd46 52 assert (!finished() && !asyncInProgress());
225b7b10 53 finished_ = true;
e0f7153c
AR
54 allow_ = finalAnswer;
55 debugs(28, 3, HERE << this << " answer " << allow_ << " for " << reason);
225b7b10 56}
57
e0f7153c 58/// Called first (and once) by all checks to initialize their state
c35e260d 59void
e0f7153c 60ACLChecklist::preCheck(const char *what)
c35e260d 61{
e0f7153c 62 debugs(28, 3, HERE << this << " checking " << what);
6f58d7d7 63 AclMatchedName = NULL;
e0f7153c 64 finished_ = false;
c35e260d 65}
66
6f58d7d7
AR
67bool
68ACLChecklist::matchChild(const Acl::InnerNode *current, Acl::Nodes::const_iterator pos, const ACL *child)
225b7b10 69{
6f58d7d7
AR
70 assert(current && child);
71
115b045f 72 // Remember the current tree location to prevent "async loop" cases where
6f58d7d7
AR
73 // the same child node wants to go async more than once.
74 matchLoc_ = Breadcrumb(current, pos);
75
76 // if there are any breadcrumbs left, then follow them on the way down
77 bool result = false;
78 if (matchPath.empty()) {
79 result = child->matches(this);
80 } else {
81 const Breadcrumb top(matchPath.top());
82 assert(child == top.parent);
83 matchPath.pop();
84 result = top.parent->resumeMatchingAt(this, top.position);
85 }
86
87 if (asyncInProgress()) {
88 // We get here for node N that called goAsync() and then, as the call
89 // stack unwinds, for the nodes higher in the ACL tree that led to N.
90 matchPath.push(Breadcrumb(current, pos));
91 } else {
92 asyncLoc_.clear();
93 }
e0f7153c 94
6f58d7d7
AR
95 matchLoc_.clear();
96 return result;
225b7b10 97}
98
6f58d7d7
AR
99bool
100ACLChecklist::goAsync(AsyncState *state)
225b7b10 101{
6f58d7d7
AR
102 assert(state);
103 assert(!asyncInProgress());
104 assert(matchLoc_.parent);
105
106 // TODO: add a once-in-a-while WARNING about fast directive using slow ACL?
107 if (!asyncCaller_) {
108 debugs(28, 2, this << " a fast-only directive uses a slow ACL!");
109 return false;
110 }
111
112 // TODO: add a once-in-a-while WARNING about async loops?
113 if (matchLoc_ == asyncLoc_) {
114 debugs(28, 2, this << " a slow ACL resumes by going async again!");
115 return false;
116 }
117
118 asyncLoc_ = matchLoc_; // prevent async loops
119
120 asyncStage_ = asyncStarting;
121 changeState(state);
122 state->checkForAsync(this); // this is supposed to go async
123
124 // Did AsyncState object actually go async? If not, tell the caller.
125 if (asyncStage_ != asyncStarting) {
126 assert(asyncStage_ == asyncFailed);
127 asyncStage_ = asyncNone; // sanity restored
128 return false;
129 }
e936c41c 130
6f58d7d7
AR
131 // yes, we must pause until the async callback calls resumeNonBlockingCheck
132 asyncStage_ = asyncRunning;
133 return true;
225b7b10 134}
135
351fe86d
AR
136// ACLFilledChecklist overwrites this to unclock something before we
137// "delete this"
225b7b10 138void
139ACLChecklist::checkCallback(allow_t answer)
140{
2efeb0b7 141 ACLCB *callback_;
225b7b10 142 void *cbdata_;
bf8fe701 143 debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
144
225b7b10 145 callback_ = callback;
146 callback = NULL;
62e76326 147
225b7b10 148 if (cbdataReferenceValidDone(callback_data, &cbdata_))
62e76326 149 callback_(answer, cbdata_);
150
00d77d6b 151 delete this;
225b7b10 152}
62e76326 153
cc192b50 154ACLChecklist::ACLChecklist() :
155 accessList (NULL),
4f0ef8e8 156 callback (NULL),
62e76326 157 callback_data (NULL),
6f58d7d7 158 asyncCaller_(false),
62e76326 159 finished_(false),
160 allow_(ACCESS_DENIED),
6f58d7d7
AR
161 asyncStage_(asyncNone),
162 state_(NullState::Instance())
225b7b10 163{
225b7b10 164}
165
166ACLChecklist::~ACLChecklist()
167{
168 assert (!asyncInProgress());
62e76326 169
225b7b10 170 cbdataReferenceDone(accessList);
62e76326 171
bf8fe701 172 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
225b7b10 173}
174
225b7b10 175ACLChecklist::NullState *
176ACLChecklist::NullState::Instance()
177{
178 return &_instance;
179}
180
181void
182ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
6f58d7d7
AR
183{
184 assert(false); // or the Checklist will never get out of the async state
185}
225b7b10 186
187ACLChecklist::NullState ACLChecklist::NullState::_instance;
188
189void
190ACLChecklist::changeState (AsyncState *newState)
191{
192 /* only change from null to active and back again,
193 * not active to active.
194 * relax this once conversion to states is complete
195 * RBC 02 2003
196 */
197 assert (state_ == NullState::Instance() || newState == NullState::Instance());
198 state_ = newState;
199}
200
201ACLChecklist::AsyncState *
202ACLChecklist::asyncState() const
203{
204 return state_;
205}
206
b50e327b
AJ
207/**
208 * Kick off a non-blocking (slow) ACL access list test
209 *
210 * NP: this should probably be made Async now.
211 */
225b7b10 212void
2efeb0b7 213ACLChecklist::nonBlockingCheck(ACLCB * callback_, void *callback_data_)
225b7b10 214{
e0f7153c 215 preCheck("slow rules");
225b7b10 216 callback = callback_;
217 callback_data = cbdataReference(callback_data_);
6f58d7d7
AR
218 asyncCaller_ = true;
219
220 /** The ACL List should NEVER be NULL when calling this method.
221 * Always caller should check for NULL and handle appropriate to its needs first.
222 * We cannot select a sensible default for all callers here. */
223 if (accessList == NULL) {
224 debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
225 checkCallback(ACCESS_DUNNO);
226 return;
227 }
228
229 if (prepNonBlocking()) {
230 matchAndFinish(); // calls markFinished() on success
231 if (!asyncInProgress())
232 completeNonBlocking();
233 } // else checkCallback() has been called
234}
235
236void
237ACLChecklist::resumeNonBlockingCheck(AsyncState *state)
238{
239 assert(asyncState() == state);
240 changeState(NullState::Instance());
241
242 if (asyncStage_ == asyncStarting) { // oops, we did not really go async
243 asyncStage_ = asyncFailed; // goAsync() checks for that
244 // Do not fall through to resume checks from the async callback. Let
245 // the still-pending(!) goAsync() notice and notify its caller instead.
246 return;
247 }
248 assert(asyncStage_ == asyncRunning);
249 asyncStage_ = asyncNone;
250
251 assert(!matchPath.empty());
252
253 if (!prepNonBlocking())
254 return; // checkCallback() has been called
255
ef689e2b
AR
256 if (!finished())
257 matchAndFinish();
6f58d7d7
AR
258
259 if (asyncInProgress())
260 assert(!matchPath.empty()); // we have breadcrumbs to resume matching
261 else
262 completeNonBlocking();
263}
264
265/// performs (or resumes) an ACL tree match and, if successful, sets the action
266void
e936c41c
AR
267ACLChecklist::matchAndFinish()
268{
6f58d7d7
AR
269 bool result = false;
270 if (matchPath.empty()) {
271 result = accessList->matches(this);
272 } else {
273 const Breadcrumb top(matchPath.top());
274 matchPath.pop();
275 result = top.parent->resumeMatchingAt(this, top.position);
276 }
e936c41c 277
6f58d7d7
AR
278 if (result) // the entire tree matched
279 markFinished(accessList->winningAction(), "match");
2efeb0b7
AJ
280}
281
282allow_t const &
6f58d7d7 283ACLChecklist::fastCheck(const Acl::Tree * list)
2efeb0b7
AJ
284{
285 PROF_start(aclCheckFast);
e0f7153c
AR
286
287 preCheck("fast ACLs");
6f58d7d7
AR
288 asyncCaller_ = false;
289
290 // This call is not compatible with a pre-set accessList because we cannot
291 // tell whether this Checklist is used by some other concurent call, which
292 // is not supported.
293 assert(!accessList);
2154a209 294 accessList = cbdataReference(list);
e0f7153c 295
b5e48e70 296 // assume DENY/ALLOW on mis/matches due to action-free accessList
6f58d7d7 297 // matchAndFinish() takes care of the ALLOW case
6f58d7d7
AR
298 if (accessList && cbdataReferenceValid(accessList))
299 matchAndFinish(); // calls markFinished() on success
300 if (!finished())
301 markFinished(ACCESS_DENIED, "ACLs failed to match");
302
303 cbdataReferenceDone(accessList);
2efeb0b7
AJ
304 PROF_stop(aclCheckFast);
305 return currentAnswer();
225b7b10 306}
307
b448c119 308/* Warning: do not cbdata lock this here - it
309 * may be static or on the stack
310 */
2efeb0b7 311allow_t const &
b448c119 312ACLChecklist::fastCheck()
313{
314 PROF_start(aclCheckFast);
2efeb0b7 315
e0f7153c 316 preCheck("fast rules");
6f58d7d7 317 asyncCaller_ = false;
e0f7153c 318
bf8fe701 319 debugs(28, 5, "aclCheckFast: list: " << accessList);
6f58d7d7
AR
320 const Acl::Tree *acl = cbdataReference(accessList);
321 if (acl != NULL && cbdataReferenceValid(acl)) {
322 matchAndFinish(); // calls markFinished() on success
e0f7153c
AR
323
324 // if finished (on a match or in exceptional cases), stop
2efeb0b7 325 if (finished()) {
bea5dacc 326 cbdataReferenceDone(acl);
e0f7153c 327 PROF_stop(aclCheckFast);
2efeb0b7 328 return currentAnswer();
b448c119 329 }
330
6f58d7d7 331 // fall through for mismatch handling
b448c119 332 }
333
e0f7153c 334 // There were no rules to match or no rules matched
6f58d7d7
AR
335 calcImplicitAnswer();
336 cbdataReferenceDone(acl);
b448c119 337 PROF_stop(aclCheckFast);
2efeb0b7
AJ
338
339 return currentAnswer();
b448c119 340}
341
b5e48e70
AR
342/// When no rules matched, the answer is the inversion of the last rule
343/// action (or ACCESS_DUNNO if the reversal is not possible).
e0f7153c 344void
6f58d7d7 345ACLChecklist::calcImplicitAnswer()
e0f7153c 346{
b5e48e70
AR
347 const allow_t lastAction = (accessList && cbdataReferenceValid(accessList)) ?
348 accessList->lastAction() : allow_t(ACCESS_DUNNO);
e0f7153c 349 allow_t implicitRuleAnswer = ACCESS_DUNNO;
b5e48e70 350 if (lastAction == ACCESS_DENIED) // reverse last seen "deny"
e0f7153c 351 implicitRuleAnswer = ACCESS_ALLOWED;
b5e48e70 352 else if (lastAction == ACCESS_ALLOWED) // reverse last seen "allow"
e0f7153c
AR
353 implicitRuleAnswer = ACCESS_DENIED;
354 // else we saw no rules and will respond with ACCESS_DUNNO
355
356 debugs(28, 3, HERE << this << " NO match found, last action " <<
b5e48e70 357 lastAction << " so returning " << implicitRuleAnswer);
e0f7153c
AR
358 markFinished(implicitRuleAnswer, "implicit rule won");
359}
b448c119 360
351fe86d
AR
361bool
362ACLChecklist::callerGone()
b448c119 363{
351fe86d 364 return !cbdataReferenceValid(callback_data);
b448c119 365}