Squid Web Cache v8/master
Loading...
Searching...
No Matches
Checklist.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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/FilledChecklist.h"
14#include "acl/Tree.h"
15#include "debug/Stream.h"
16
17#include <algorithm>
18
20bool
22{
24
25 if (callerGone()) {
26 checkCallback("caller is gone"); // the answer does not really matter
27 return false;
28 }
29
30 return true;
31}
32
33void
43
44void
45ACLChecklist::markFinished(const Acl::Answer &finalAnswer, const char *reason)
46{
48 finished_ = true;
49 answer_ = finalAnswer;
51 debugs(28, 3, this << " answer " << answer_ << " for " << reason);
52}
53
55void
56ACLChecklist::preCheck(const char *what)
57{
58 debugs(28, 3, this << " checking " << what);
59
60 // concurrent checks using the same Checklist are not supported
62 occupied_ = true;
64
65 lastCheckedName_.reset();
66 finished_ = false;
67}
68
69bool
70ACLChecklist::matchChild(const Acl::InnerNode * const current, const Acl::Nodes::const_iterator pos)
71{
72 const auto &child = *pos;
73 assert(current && child);
74
75 // Remember the current tree location to prevent "async loop" cases where
76 // the same child node wants to go async more than once.
77 matchLoc_ = Breadcrumb(current, pos);
79
80 // if there are any breadcrumbs left, then follow them on the way down
81 bool result = false;
82 if (matchPath.empty()) {
83 result = child->matches(this);
84 } else {
85 const Breadcrumb top(matchPath.top());
86 assert(child == top.parent);
87 matchPath.pop();
88 result = top.parent->resumeMatchingAt(this, top.position);
89 }
90
91 if (asyncInProgress()) {
92 // We get here for node N that called goAsync() and then, as the call
93 // stack unwinds, for the nodes higher in the ACL tree that led to N.
94 matchPath.push(Breadcrumb(current, pos));
95 } else {
97 }
98
100 return result;
101}
102
103bool
105{
108
109 // TODO: add a once-in-a-while WARNING about fast directive using slow ACL?
110 if (!asyncCaller_) {
111 debugs(28, 2, this << " a fast-only directive uses a slow ACL!");
112 return false;
113 }
114
115 // TODO: add a once-in-a-while WARNING about async loops?
116 if (matchLoc_ == asyncLoc_) {
117 debugs(28, 2, this << " a slow ACL resumes by going async again! (loop #" << asyncLoopDepth_ << ")");
118 // external_acl_type may cause async auth lookup plus its own async check
119 // which has the appearance of a loop. Allow some retries.
120 // TODO: make it configurable and check BH retry attempts vs this check?
121 if (asyncLoopDepth_ > 5)
122 return false;
123 }
124
125 asyncLoc_ = matchLoc_; // prevent async loops
127
129 starter(*Filled(this), acl); // this is supposed to go async
130
131 // Did starter() actually go async? If not, tell the caller.
132 if (asyncStage_ != asyncStarting) {
134 asyncStage_ = asyncNone; // sanity restored
135 return false;
136 }
137
138 // yes, we must pause until the async callback calls resumeNonBlockingCheck
140 return true;
141}
142
143// ACLFilledChecklist overwrites this to unclock something before we
144// "delete this"
145void
146ACLChecklist::checkCallback(const char * const abortReason)
147{
148 if (abortReason)
149 markFinished(ACCESS_DUNNO, abortReason);
150 Assure(finished());
151
152 ACLCB *callback_;
153 void *cbdata_;
154
155 callback_ = callback;
156 callback = nullptr;
157
159 callback_(currentAnswer(), cbdata_);
160
161 // not really meaningful just before delete, but here for completeness sake
162 occupied_ = false;
163
164 delete this;
165}
166
168 accessList (nullptr),
169 callback (nullptr),
170 callback_data (nullptr),
171 asyncCaller_(false),
172 occupied_(false),
173 finished_(false),
174 answer_(ACCESS_DENIED),
175 asyncStage_(asyncNone),
176 asyncLoopDepth_(0)
177{
178}
179
181{
183 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
184}
185
186void
187ACLChecklist::changeAcl(const acl_access * const replacement)
188{
189 accessList = replacement ? *replacement : nullptr;
190}
191
193ACLChecklist::swapAcl(const acl_access * const replacement)
194{
195 const auto old = accessList;
196 changeAcl(replacement);
197 return old;
198}
199
205void
206ACLChecklist::nonBlockingCheck(ACLCB * callback_, void *callback_data_)
207{
208 preCheck("slow rules");
209 callback = callback_;
210 callback_data = cbdataReference(callback_data_);
211 asyncCaller_ = true;
212
216 if (accessList == nullptr) {
217 debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
218 checkCallback("nonBlockingCheck() without accessList");
219 return;
220 }
221
222 if (prepNonBlocking()) {
223 matchAndFinish(); // calls markFinished() on success
224 if (!asyncInProgress())
226 } // else checkCallback() has been called
227}
228
229void
231{
232 if (asyncStage_ == asyncStarting) { // oops, we did not really go async
233 asyncStage_ = asyncFailed; // goAsync() checks for that
234 // Do not fall through to resume checks from the async callback. Let
235 // the still-pending(!) goAsync() notice and notify its caller instead.
236 return;
237 }
240
241 assert(!matchPath.empty());
242
243 if (!prepNonBlocking())
244 return; // checkCallback() has been called
245
246 if (!finished())
248
249 if (asyncInProgress())
250 assert(!matchPath.empty()); // we have breadcrumbs to resume matching
251 else
253}
254
256void
258{
259 bool result = false;
260 if (matchPath.empty()) {
261 result = accessList->matches(this);
262 } else {
263 const Breadcrumb top(matchPath.top());
264 matchPath.pop();
265 result = top.parent->resumeMatchingAt(this, top.position);
266 }
267
268 if (result) // the entire tree matched
270}
271
272const Acl::Answer &
274{
275 preCheck("fast ACLs");
276 asyncCaller_ = false;
277
278 // Concurrent checks are not supported, but sequential checks are, and they
279 // may use a mixture of fastCheck(void) and fastCheck(list) calls.
280 const auto savedList = swapAcl(list);
281
282 // assume DENY/ALLOW on mismatches/matches due to action-free accessList
283 // matchAndFinish() takes care of the ALLOW case
284 if (accessList)
285 matchAndFinish(); // calls markFinished() on success
286 if (!finished())
287 markFinished(ACCESS_DENIED, "ACLs failed to match");
288
289 changeAcl(&savedList);
290 occupied_ = false;
291 return currentAnswer();
292}
293
294/* Warning: do not cbdata lock this here - it
295 * may be static or on the stack
296 */
297Acl::Answer const &
299{
300 preCheck("fast rules");
301 asyncCaller_ = false;
302
303 debugs(28, 5, "aclCheckFast: list: " << accessList);
304 if (accessList) {
305 matchAndFinish(); // calls markFinished() on success
306
307 // if finished (on a match or in exceptional cases), stop
308 if (finished()) {
309 occupied_ = false;
310 return currentAnswer();
311 }
312
313 // fall through for mismatch handling
314 }
315
316 // There were no rules to match or no rules matched
318 occupied_ = false;
319
320 return currentAnswer();
321}
322
325void
327{
328 const auto lastAction = accessList ?
330 auto implicitRuleAnswer = Acl::Answer(ACCESS_DUNNO);
331 if (lastAction == ACCESS_DENIED) // reverse last seen "deny"
332 implicitRuleAnswer = Acl::Answer(ACCESS_ALLOWED);
333 else if (lastAction == ACCESS_ALLOWED) // reverse last seen "allow"
334 implicitRuleAnswer = Acl::Answer(ACCESS_DENIED);
335 // else we saw no rules and will respond with ACCESS_DUNNO
336
337 implicitRuleAnswer.implicit = true;
338 debugs(28, 3, this << " NO match found, last action " <<
339 lastAction << " so returning " << implicitRuleAnswer);
340 markFinished(implicitRuleAnswer, "implicit rule won");
341}
342
343bool
348
349bool
351{
352 const bool found = std::find(bannedActions_.begin(), bannedActions_.end(), action) != bannedActions_.end();
353 debugs(28, 5, "Action '" << action << "/" << action.kind << (found ? "' is " : "' is not") << " banned");
354 return found;
355}
356
357void
359{
360 bannedActions_.push_back(action);
361}
362
#define Assure(condition)
Definition Assure.h:35
void ACLCB(Acl::Answer, void *)
ACL checklist callback.
Definition Checklist.h:23
ACLFilledChecklist * Filled(ACLChecklist *checklist)
convenience and safety wrapper for dynamic_cast<ACLFilledChecklist*>
#define assert(EX)
Definition assert.h:17
int cbdataReferenceValid(const void *p)
Definition cbdata.cc:270
#define cbdataReference(var)
Definition cbdata.h:348
#define cbdataReferenceValidDone(var, ptr)
Definition cbdata.h:239
Position of a child node within an Acl::Node tree.
Definition Checklist.h:178
RefCount< const Acl::InnerNode > parent
intermediate node in the ACL tree
Definition Checklist.h:185
Acl::Nodes::const_iterator position
child position inside parent
Definition Checklist.h:186
AsyncStage asyncStage_
Definition Checklist.h:205
void markFinished(const Acl::Answer &newAnswer, const char *reason)
Definition Checklist.cc:45
void completeNonBlocking()
Definition Checklist.cc:34
void banAction(const Acl::Answer &action)
add action to the list of banned actions
Definition Checklist.cc:358
std::vector< Acl::Answer > bannedActions_
the list of actions which must ignored during acl checks
Definition Checklist.h:215
Breadcrumb matchLoc_
location of the node running matches() now
Definition Checklist.h:206
void resumeNonBlockingCheck()
Definition Checklist.cc:230
void preCheck(const char *what)
prepare for checking ACLs; called once per check
Definition Checklist.cc:56
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
bool asyncCaller_
whether the caller supports async/slow ACLs
Definition Checklist.h:199
void(ACLFilledChecklist &, const Acl::Node &) AsyncStarter
a function that initiates asynchronous ACL checks; see goAsync()
Definition Checklist.h:36
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition Checklist.cc:206
bool finished() const
whether markFinished() was called
Definition Checklist.h:99
bool occupied_
whether a check (fast or non-blocking) is in progress
Definition Checklist.h:200
bool goAsync(AsyncStarter, const Acl::Node &)
Definition Checklist.cc:104
void changeAcl(const acl_access *)
change the current ACL list
Definition Checklist.cc:187
Acl::TreePointer swapAcl(const acl_access *)
change the current ACL list
Definition Checklist.cc:193
bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos)
Definition Checklist.cc:70
std::stack< Breadcrumb > matchPath
suspended (due to an async lookup) matches() in the ACL tree
Definition Checklist.h:213
bool prepNonBlocking()
common parts of nonBlockingCheck() and resumeNonBlockingCheck()
Definition Checklist.cc:21
bool bannedAction(const Acl::Answer &action) const
whether the action is banned or not
Definition Checklist.cc:350
Acl::TreePointer accessList
Definition Checklist.h:164
void matchAndFinish()
performs (or resumes) an ACL tree match and, if successful, sets the action
Definition Checklist.cc:257
unsigned asyncLoopDepth_
how many times the current async state has resumed
Definition Checklist.h:208
void * callback_data
Definition Checklist.h:169
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition Checklist.h:101
ACLCB * callback
Definition Checklist.h:168
Breadcrumb asyncLoc_
currentNode_ that called goAsync()
Definition Checklist.h:207
Acl::Answer answer_
Definition Checklist.h:202
void calcImplicitAnswer()
Definition Checklist.cc:326
virtual ~ACLChecklist()
Definition Checklist.cc:180
void checkCallback(const char *abortReason)
Definition Checklist.cc:146
bool callerGone()
Definition Checklist.cc:344
const Acl::Answer & currentAnswer() const
Definition Checklist.h:106
std::optional< SBuf > lastCheckedName_
the name of the last evaluated ACL (if any ACLs were evaluated)
Definition Checklist.h:218
int kind
the matched custom access list verb (or zero)
Definition Acl.h:99
std::optional< SBuf > lastCheckedName
the name of the ACL (if any) that was evaluated last while obtaining this answer
Definition Acl.h:105
An intermediate Acl::Node tree node. Manages a collection of child tree nodes.
Definition InnerNode.h:23
bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const
Resumes matching (suspended by an async call) at the given position.
Definition InnerNode.cc:96
bool matches(ACLChecklist *checklist) const
Definition Acl.cc:189
Answer winningAction() const
Returns the corresponding action after a successful tree match.
Definition Tree.cc:15
Answer lastAction() const
what action to use if no nodes matched
Definition Tree.cc:21
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
@ ACCESS_DENIED
Definition Acl.h:41
@ ACCESS_ALLOWED
Definition Acl.h:42
@ ACCESS_DUNNO
Definition Acl.h:43