Checklist.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 
19 bool
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 
44 void
46 {
48 
49  if (!finished())
51 
54 }
55 
56 void
57 ACLChecklist::markFinished(const Acl::Answer &finalAnswer, const char *reason)
58 {
59  assert (!finished() && !asyncInProgress());
60  finished_ = true;
61  answer_ = finalAnswer;
62  debugs(28, 3, this << " answer " << answer_ << " for " << reason);
63 }
64 
66 void
67 ACLChecklist::preCheck(const char *what)
68 {
69  debugs(28, 3, this << " checking " << what);
70 
71  // concurrent checks using the same Checklist are not supported
72  assert(!occupied_);
73  occupied_ = true;
74  asyncLoopDepth_ = 0;
75 
77  finished_ = false;
78 }
79 
80 bool
81 ACLChecklist::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);
88  asyncLoopDepth_ = 0;
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 {
106  asyncLoc_.clear();
107  }
108 
109  matchLoc_.clear();
110  return result;
111 }
112 
113 bool
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
137  ++asyncLoopDepth_;
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"
157 void
159 {
160  ACLCB *callback_;
161  void *cbdata_;
162  debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
163 
164  callback_ = callback;
165  callback = NULL;
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 (NULL),
178  callback (NULL),
179  callback_data (NULL),
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 {
192  assert (!asyncInProgress());
193 
194  changeAcl(nullptr);
195 
196  debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
197 }
198 
201 {
202  return &_instance;
203 }
204 
205 void
207 {
208  assert(false); // or the Checklist will never get out of the async state
209 }
210 
212 
213 void
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  */
221  assert (state_ == NullState::Instance() || newState == NullState::Instance());
222  state_ = newState;
223 }
224 
227 {
228  return state_;
229 }
230 
236 void
237 ACLChecklist::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 == NULL) {
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 
260 void
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())
281  matchAndFinish();
282 
283  if (asyncInProgress())
284  assert(!matchPath.empty()); // we have breadcrumbs to resume matching
285  else
287 }
288 
290 void
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 
306 Acl::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  */
331 Acl::Answer const &
333 {
334  preCheck("fast rules");
335  asyncCaller_ = false;
336 
337  debugs(28, 5, "aclCheckFast: list: " << accessList);
338  const Acl::Tree *acl = cbdataReference(accessList);
339  if (acl != NULL && cbdataReferenceValid(acl)) {
340  matchAndFinish(); // calls markFinished() on success
341 
342  // if finished (on a match or in exceptional cases), stop
343  if (finished()) {
344  cbdataReferenceDone(acl);
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
354  cbdataReferenceDone(acl);
355  occupied_ = false;
356 
357  return currentAnswer();
358 }
359 
362 void
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 
380 bool
382 {
384 }
385 
386 bool
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 
394 void
396 {
397  bannedActions_.push_back(action);
398 }
399 
AsyncState * state_
Definition: Checklist.h:235
static bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
Definition: purge.cc:311
Answer winningAction() const
Returns the corresponding action after a successful tree match.
Definition: Tree.cc:17
#define DBG_CRITICAL
Definition: Stream.h:40
bool finished_
Definition: Checklist.h:230
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:256
std::stack< Breadcrumb > matchPath
suspended (due to an async lookup) matches() in the ACL tree
Definition: Checklist.h:243
void banAction(const Acl::Answer &action)
add action to the list of banned actions
Definition: Checklist.cc:395
void checkCallback(Acl::Answer answer)
Calls non-blocking check callback with the answer and destroys self.
Definition: Checklist.cc:158
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:398
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
ACLCB * callback
Definition: Checklist.h:197
An intermediate ACL tree node. Manages a collection of child tree nodes.
Definition: InnerNode.h:22
#define cbdataReference(var)
Definition: cbdata.h:341
Position of a child node within an ACL tree.
Definition: Checklist.h:207
const Acl::Answer & currentAnswer() const
Definition: Checklist.h:156
bool asyncInProgress() const
async call has been started and has not finished (or failed) yet
Definition: Checklist.h:151
AsyncState * asyncState() const
Definition: Checklist.cc:226
void ACLCB(Acl::Answer, void *)
ACL checklist callback.
Definition: Checklist.h:19
bool goAsync(AsyncState *)
Definition: Checklist.cc:114
const char * AclMatchedName
Definition: Acl.cc:29
Answer lastAction() const
what action to use if no nodes matched
Definition: Tree.cc:23
bool finished() const
whether markFinished() was called
Definition: Checklist.h:149
#define NULL
Definition: types.h:166
void * callback_data
Definition: Checklist.h:198
const Acl::Tree * accessList
Definition: Checklist.h:194
std::vector< Acl::Answer > bannedActions_
the list of actions which must ignored during acl checks
Definition: Checklist.h:245
static NullState _instance
Definition: Checklist.h:63
void markFinished(const Acl::Answer &newAnswer, const char *reason)
Definition: Checklist.cc:57
void calcImplicitAnswer()
Definition: Checklist.cc:363
code related to Squid Instance and PID file management
Definition: Instance.h:17
Definition: Tree.h:21
#define assert(EX)
Definition: assert.h:19
static NullState * Instance()
Definition: Checklist.cc:200
void changeState(AsyncState *)
Definition: Checklist.cc:214
virtual void checkForAsync(ACLChecklist *) const =0
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
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
const Acl::InnerNode * parent
intermediate node in the ACL tree
Definition: Checklist.h:214
Definition: Acl.h:40
void completeNonBlocking()
Definition: Checklist.cc:45
bool callerGone()
Definition: Checklist.cc:381
const Acl::Tree * changeAcl(const Acl::Tree *t)
Definition: Checklist.h:176
void preCheck(const char *what)
prepare for checking ACLs; called once per check
Definition: Checklist.cc:67
bool matches(ACLChecklist *checklist) const
Definition: Acl.cc:120
virtual ~ACLChecklist()
Definition: Checklist.cc:190
unsigned asyncLoopDepth_
how many times the current async state has resumed
Definition: Checklist.h:238
Acl::Answer answer_
Definition: Checklist.h:231
void resumeNonBlockingCheck(AsyncState *state)
Definition: Checklist.cc:261
Acl::Nodes::const_iterator position
child position inside parent
Definition: Checklist.h:215
AsyncStage asyncStage_
Definition: Checklist.h:234
void matchAndFinish()
performs (or resumes) an ACL tree match and, if successful, sets the action
Definition: Checklist.cc:291
@ ACCESS_ALLOWED
Definition: Acl.h:110
@ ACCESS_DENIED
Definition: Acl.h:109
bool prepNonBlocking()
common parts of nonBlockingCheck() and resumeNonBlockingCheck()
Definition: Checklist.cc:20
@ ACCESS_DUNNO
Definition: Acl.h:111
bool asyncCaller_
whether the caller supports async/slow ACLs
Definition: Checklist.h:228
bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child)
Definition: Checklist.cc:81
Breadcrumb asyncLoc_
currentNode_ that called goAsync()
Definition: Checklist.h:237
virtual void checkForAsync(ACLChecklist *) const
Definition: Checklist.cc:206
#define false
Definition: GnuRegex.c:233
Breadcrumb matchLoc_
location of the node running matches() now
Definition: Checklist.h:236
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:237
bool bannedAction(const Acl::Answer &action) const
whether the action is banned or not
Definition: Checklist.cc:387
bool occupied_
whether a check (fast or non-blocking) is in progress
Definition: Checklist.h:229

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors