Checklist.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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
9/* DEBUG: section 28 Access Control */
10
11#include "squid.h"
12#include "acl/Checklist.h"
13#include "acl/Tree.h"
14#include "debug/Stream.h"
15
16#include <algorithm>
17
19bool
21{
23
24 if (callerGone()) {
25 checkCallback(ACCESS_DUNNO); // the answer does not really matter
26 return false;
27 }
28
36 debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
38 return false;
39 }
40
41 return true;
42}
43
44void
46{
48
49 if (!finished())
51
54}
55
56void
57ACLChecklist::markFinished(const Acl::Answer &finalAnswer, const char *reason)
58{
60 finished_ = true;
61 answer_ = finalAnswer;
62 debugs(28, 3, this << " answer " << answer_ << " for " << reason);
63}
64
66void
67ACLChecklist::preCheck(const char *what)
68{
69 debugs(28, 3, this << " checking " << what);
70
71 // concurrent checks using the same Checklist are not supported
73 occupied_ = true;
75
76 AclMatchedName = nullptr;
77 finished_ = false;
78}
79
80bool
81ACLChecklist::matchChild(const Acl::InnerNode *current, Acl::Nodes::const_iterator pos, const ACL *child)
82{
83 assert(current && child);
84
85 // Remember the current tree location to prevent "async loop" cases where
86 // the same child node wants to go async more than once.
87 matchLoc_ = Breadcrumb(current, pos);
89
90 // if there are any breadcrumbs left, then follow them on the way down
91 bool result = false;
92 if (matchPath.empty()) {
93 result = child->matches(this);
94 } else {
95 const Breadcrumb top(matchPath.top());
96 assert(child == top.parent);
97 matchPath.pop();
98 result = top.parent->resumeMatchingAt(this, top.position);
99 }
100
101 if (asyncInProgress()) {
102 // We get here for node N that called goAsync() and then, as the call
103 // stack unwinds, for the nodes higher in the ACL tree that led to N.
104 matchPath.push(Breadcrumb(current, pos));
105 } else {
107 }
108
110 return result;
111}
112
113bool
115{
116 assert(state);
119
120 // TODO: add a once-in-a-while WARNING about fast directive using slow ACL?
121 if (!asyncCaller_) {
122 debugs(28, 2, this << " a fast-only directive uses a slow ACL!");
123 return false;
124 }
125
126 // TODO: add a once-in-a-while WARNING about async loops?
127 if (matchLoc_ == asyncLoc_) {
128 debugs(28, 2, this << " a slow ACL resumes by going async again! (loop #" << asyncLoopDepth_ << ")");
129 // external_acl_type may cause async auth lookup plus its own async check
130 // which has the appearance of a loop. Allow some retries.
131 // TODO: make it configurable and check BH retry attempts vs this check?
132 if (asyncLoopDepth_ > 5)
133 return false;
134 }
135
136 asyncLoc_ = matchLoc_; // prevent async loops
138
140 changeState(state);
141 state->checkForAsync(this); // this is supposed to go async
142
143 // Did AsyncState object actually go async? If not, tell the caller.
144 if (asyncStage_ != asyncStarting) {
146 asyncStage_ = asyncNone; // sanity restored
147 return false;
148 }
149
150 // yes, we must pause until the async callback calls resumeNonBlockingCheck
152 return true;
153}
154
155// ACLFilledChecklist overwrites this to unclock something before we
156// "delete this"
157void
159{
160 ACLCB *callback_;
161 void *cbdata_;
162 debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
163
164 callback_ = callback;
165 callback = nullptr;
166
168 callback_(answer, cbdata_);
169
170 // not really meaningful just before delete, but here for completeness sake
171 occupied_ = false;
172
173 delete this;
174}
175
177 accessList (nullptr),
178 callback (nullptr),
179 callback_data (nullptr),
180 asyncCaller_(false),
181 occupied_(false),
182 finished_(false),
183 answer_(ACCESS_DENIED),
184 asyncStage_(asyncNone),
185 state_(NullState::Instance()),
186 asyncLoopDepth_(0)
187{
188}
189
191{
193
194 changeAcl(nullptr);
195
196 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
197}
198
201{
202 return &_instance;
203}
204
205void
207{
208 assert(false); // or the Checklist will never get out of the async state
209}
210
212
213void
215{
216 /* only change from null to active and back again,
217 * not active to active.
218 * relax this once conversion to states is complete
219 * RBC 02 2003
220 */
222 state_ = newState;
223}
224
227{
228 return state_;
229}
230
236void
237ACLChecklist::nonBlockingCheck(ACLCB * callback_, void *callback_data_)
238{
239 preCheck("slow rules");
240 callback = callback_;
241 callback_data = cbdataReference(callback_data_);
242 asyncCaller_ = true;
243
247 if (accessList == nullptr) {
248 debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
250 return;
251 }
252
253 if (prepNonBlocking()) {
254 matchAndFinish(); // calls markFinished() on success
255 if (!asyncInProgress())
257 } // else checkCallback() has been called
258}
259
260void
262{
263 assert(asyncState() == state);
265
266 if (asyncStage_ == asyncStarting) { // oops, we did not really go async
267 asyncStage_ = asyncFailed; // goAsync() checks for that
268 // Do not fall through to resume checks from the async callback. Let
269 // the still-pending(!) goAsync() notice and notify its caller instead.
270 return;
271 }
274
275 assert(!matchPath.empty());
276
277 if (!prepNonBlocking())
278 return; // checkCallback() has been called
279
280 if (!finished())
282
283 if (asyncInProgress())
284 assert(!matchPath.empty()); // we have breadcrumbs to resume matching
285 else
287}
288
290void
292{
293 bool result = false;
294 if (matchPath.empty()) {
295 result = accessList->matches(this);
296 } else {
297 const Breadcrumb top(matchPath.top());
298 matchPath.pop();
299 result = top.parent->resumeMatchingAt(this, top.position);
300 }
301
302 if (result) // the entire tree matched
304}
305
306Acl::Answer const &
308{
309 preCheck("fast ACLs");
310 asyncCaller_ = false;
311
312 // Concurrent checks are not supported, but sequential checks are, and they
313 // may use a mixture of fastCheck(void) and fastCheck(list) calls.
314 const Acl::Tree * const savedList = changeAcl(list);
315
316 // assume DENY/ALLOW on mis/matches due to action-free accessList
317 // matchAndFinish() takes care of the ALLOW case
319 matchAndFinish(); // calls markFinished() on success
320 if (!finished())
321 markFinished(ACCESS_DENIED, "ACLs failed to match");
322
323 changeAcl(savedList);
324 occupied_ = false;
325 return currentAnswer();
326}
327
328/* Warning: do not cbdata lock this here - it
329 * may be static or on the stack
330 */
331Acl::Answer const &
333{
334 preCheck("fast rules");
335 asyncCaller_ = false;
336
337 debugs(28, 5, "aclCheckFast: list: " << accessList);
339 if (acl != nullptr && cbdataReferenceValid(acl)) {
340 matchAndFinish(); // calls markFinished() on success
341
342 // if finished (on a match or in exceptional cases), stop
343 if (finished()) {
345 occupied_ = false;
346 return currentAnswer();
347 }
348
349 // fall through for mismatch handling
350 }
351
352 // There were no rules to match or no rules matched
355 occupied_ = false;
356
357 return currentAnswer();
358}
359
362void
364{
365 const auto lastAction = (accessList && cbdataReferenceValid(accessList)) ?
367 auto implicitRuleAnswer = Acl::Answer(ACCESS_DUNNO);
368 if (lastAction == ACCESS_DENIED) // reverse last seen "deny"
369 implicitRuleAnswer = Acl::Answer(ACCESS_ALLOWED);
370 else if (lastAction == ACCESS_ALLOWED) // reverse last seen "allow"
371 implicitRuleAnswer = Acl::Answer(ACCESS_DENIED);
372 // else we saw no rules and will respond with ACCESS_DUNNO
373
374 implicitRuleAnswer.implicit = true;
375 debugs(28, 3, this << " NO match found, last action " <<
376 lastAction << " so returning " << implicitRuleAnswer);
377 markFinished(implicitRuleAnswer, "implicit rule won");
378}
379
380bool
382{
384}
385
386bool
388{
389 const bool found = std::find(bannedActions_.begin(), bannedActions_.end(), action) != bannedActions_.end();
390 debugs(28, 5, "Action '" << action << "/" << action.kind << (found ? "' is " : "' is not") << " banned");
391 return found;
392}
393
394void
396{
397 bannedActions_.push_back(action);
398}
399
void ACLCB(Acl::Answer, void *)
ACL checklist callback.
Definition: Checklist.h:19
#define assert(EX)
Definition: assert.h:17
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:265
#define cbdataReferenceDone(var)
Definition: cbdata.h:352
#define cbdataReference(var)
Definition: cbdata.h:343
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
virtual void checkForAsync(ACLChecklist *) const =0
Position of a child node within an ACL tree.
Definition: Checklist.h:207
const Acl::InnerNode * parent
intermediate node in the ACL tree
Definition: Checklist.h:214
Acl::Nodes::const_iterator position
child position inside parent
Definition: Checklist.h:215
void checkForAsync(ACLChecklist *) const override
Definition: Checklist.cc:206
static NullState _instance
Definition: Checklist.h:63
static NullState * Instance()
Definition: Checklist.cc:200
AsyncStage asyncStage_
Definition: Checklist.h:234
void markFinished(const Acl::Answer &newAnswer, const char *reason)
Definition: Checklist.cc:57
void completeNonBlocking()
Definition: Checklist.cc:45
bool goAsync(AsyncState *)
Definition: Checklist.cc:114
void banAction(const Acl::Answer &action)
add action to the list of banned actions
Definition: Checklist.cc:395
std::vector< Acl::Answer > bannedActions_
the list of actions which must ignored during acl checks
Definition: Checklist.h:245
AsyncState * state_
Definition: Checklist.h:235
Breadcrumb matchLoc_
location of the node running matches() now
Definition: Checklist.h:236
AsyncState * asyncState() const
Definition: Checklist.cc:226
void preCheck(const char *what)
prepare for checking ACLs; called once per check
Definition: Checklist.cc:67
const Acl::Tree * changeAcl(const Acl::Tree *t)
Definition: Checklist.h:176
void checkCallback(Acl::Answer answer)
Calls non-blocking check callback with the answer and destroys self.
Definition: Checklist.cc:158
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
bool asyncCaller_
whether the caller supports async/slow ACLs
Definition: Checklist.h:228
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:237
const Acl::Tree * accessList
Definition: Checklist.h:194
bool finished() const
whether markFinished() was called
Definition: Checklist.h:149
bool occupied_
whether a check (fast or non-blocking) is in progress
Definition: Checklist.h:229
void resumeNonBlockingCheck(AsyncState *state)
Definition: Checklist.cc:261
std::stack< Breadcrumb > matchPath
suspended (due to an async lookup) matches() in the ACL tree
Definition: Checklist.h:243
bool prepNonBlocking()
common parts of nonBlockingCheck() and resumeNonBlockingCheck()
Definition: Checklist.cc:20
bool bannedAction(const Acl::Answer &action) const
whether the action is banned or not
Definition: Checklist.cc:387
void matchAndFinish()
performs (or resumes) an ACL tree match and, if successful, sets the action
Definition: Checklist.cc:291
unsigned asyncLoopDepth_
how many times the current async state has resumed
Definition: Checklist.h:238
void * callback_data
Definition: Checklist.h:198
bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child)
Definition: Checklist.cc:81
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition: Checklist.h:151
void changeState(AsyncState *)
Definition: Checklist.cc:214
ACLCB * callback
Definition: Checklist.h:197
bool finished_
Definition: Checklist.h:230
Breadcrumb asyncLoc_
currentNode_ that called goAsync()
Definition: Checklist.h:237
Acl::Answer answer_
Definition: Checklist.h:231
void calcImplicitAnswer()
Definition: Checklist.cc:363
virtual ~ACLChecklist()
Definition: Checklist.cc:190
bool callerGone()
Definition: Checklist.cc:381
const Acl::Answer & currentAnswer() const
Definition: Checklist.h:156
Definition: Acl.h:46
bool matches(ACLChecklist *checklist) const
Definition: Acl.cc:146
An intermediate ACL tree node. Manages a collection of child tree nodes.
Definition: InnerNode.h:22
bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const
Resumes matching (suspended by an async call) at the given position.
Definition: InnerNode.cc:95
Definition: Tree.h:21
Answer winningAction() const
Returns the corresponding action after a successful tree match.
Definition: Tree.cc:17
Answer lastAction() const
what action to use if no nodes matched
Definition: Tree.cc:23
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
const char * AclMatchedName
Definition: Acl.cc:29
@ ACCESS_DENIED
Definition: Acl.h:115
@ ACCESS_ALLOWED
Definition: Acl.h:116
@ ACCESS_DUNNO
Definition: Acl.h:117
code related to Squid Instance and PID file management
Definition: Instance.h:17
static bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
Definition: purge.cc:315

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors