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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors