]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Checklist.cc
Merge from trunk
[thirdparty/squid.git] / src / acl / Checklist.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 28 Access Control
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
34 */
35
36 #include "squid-old.h"
37 #include "acl/Checklist.h"
38
39 allow_t const &
40 ACLChecklist::currentAnswer() const
41 {
42 return allow_;
43 }
44
45 void
46 ACLChecklist::currentAnswer(allow_t const newAnswer)
47 {
48 allow_ = newAnswer;
49 }
50
51 void
52 ACLChecklist::matchNonBlocking()
53 {
54 if (checking())
55 return;
56
57 /** Deny if no rules present. */
58 currentAnswer(ACCESS_DENIED);
59
60 if (callerGone()) {
61 checkCallback(currentAnswer());
62 return;
63 }
64
65 /** The ACL List should NEVER be NULL when calling this method.
66 * Always caller should check for NULL and handle appropriate to its needs first.
67 * We cannot select a sensible default for all callers here. */
68 if (accessList == NULL) {
69 debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
70 currentAnswer(ACCESS_DENIED);
71 checkCallback(currentAnswer());
72 return;
73 }
74
75 /* NOTE: This holds a cbdata reference to the current access_list
76 * entry, not the whole list.
77 */
78 while (accessList != NULL) {
79 /** \par
80 * If the _acl_access is no longer valid (i.e. its been
81 * freed because of a reconfigure), then bail on this
82 * access check. For now, return ACCESS_DENIED.
83 */
84
85 if (!cbdataReferenceValid(accessList)) {
86 cbdataReferenceDone(accessList);
87 debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
88 continue;
89 }
90
91 checking (true);
92 checkAccessList();
93 checking (false);
94
95 if (asyncInProgress()) {
96 return;
97 }
98
99 if (finished()) {
100 /** \par
101 * Either the request is allowed, denied, requires authentication.
102 */
103 debugs(28, 3, "ACLChecklist::check: " << this << " match found, calling back with " << currentAnswer());
104 cbdataReferenceDone(accessList); /* A */
105 checkCallback(currentAnswer());
106 /* From here on in, this may be invalid */
107 return;
108 }
109
110 /*
111 * Reference the next access entry
112 */
113 const acl_access *A = accessList;
114
115 assert (A);
116
117 accessList = cbdataReference(A->next);
118
119 cbdataReferenceDone(A);
120 }
121
122 /** If dropped off the end of the list return inversion of last line allow/deny action. */
123 debugs(28, 3, HERE << this << " NO match found, returning " <<
124 (currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED));
125
126 checkCallback(currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED);
127 }
128
129 bool
130 ACLChecklist::asyncInProgress() const
131 {
132 return async_;
133 }
134
135 void
136 ACLChecklist::asyncInProgress(bool const newAsync)
137 {
138 assert (!finished() && !(asyncInProgress() && newAsync));
139 async_ = newAsync;
140 debugs(28, 3, "ACLChecklist::asyncInProgress: " << this <<
141 " async set to " << async_);
142 }
143
144 bool
145 ACLChecklist::finished() const
146 {
147 return finished_;
148 }
149
150 void
151 ACLChecklist::markFinished()
152 {
153 assert (!finished() && !asyncInProgress());
154 finished_ = true;
155 debugs(28, 3, "ACLChecklist::markFinished: " << this <<
156 " checklist processing finished");
157 }
158
159 void
160 ACLChecklist::preCheck()
161 {
162 debugs(28, 3, "ACLChecklist::preCheck: " << this << " checking '" << accessList->cfgline << "'");
163 /* what is our result on a match? */
164 currentAnswer(accessList->allow);
165 }
166
167 void
168 ACLChecklist::checkAccessList()
169 {
170 preCheck();
171 /* does the current AND clause match */
172 matchAclList(accessList->aclList, false);
173 }
174
175 void
176 ACLChecklist::checkForAsync()
177 {
178 asyncState()->checkForAsync(this);
179 }
180
181 // ACLFilledChecklist overwrites this to unclock something before we
182 // "delete this"
183 void
184 ACLChecklist::checkCallback(allow_t answer)
185 {
186 ACLCB *callback_;
187 void *cbdata_;
188 debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
189
190 callback_ = callback;
191 callback = NULL;
192
193 if (cbdataReferenceValidDone(callback_data, &cbdata_))
194 callback_(answer, cbdata_);
195
196 delete this;
197 }
198
199 void
200 ACLChecklist::matchAclList(const ACLList * head, bool const fast)
201 {
202 PROF_start(aclMatchAclList);
203 const ACLList *node = head;
204
205 finished_ = false;
206
207 while (node) {
208 bool nodeMatched = node->matches(this);
209
210 if (fast)
211 changeState(NullState::Instance());
212
213 if (finished()) {
214 PROF_stop(aclMatchAclList);
215 return;
216 }
217
218 if (!nodeMatched || state_ != NullState::Instance()) {
219
220 bool async = state_ != NullState::Instance();
221
222 checkForAsync();
223
224 bool async_in_progress = asyncInProgress();
225 debugs(28, 3, "aclmatchAclList: async=" << (async ? 1 : 0) <<
226 " nodeMatched=" << (nodeMatched ? 1 : 0) <<
227 " async_in_progress=" << (async_in_progress ? 1 : 0) <<
228 " lastACLResult() = " << (lastACLResult() ? 1 : 0) <<
229 " finished() = " << finished());
230
231 if (finished()) {
232 debugs(28, 3, "aclmatchAclList: " << this << " returning (AND list entry failed to match)");
233 PROF_stop(aclMatchAclList);
234 return;
235 }
236
237 if (async && nodeMatched && !asyncInProgress() && lastACLResult()) {
238 // async acl, but using cached response, and it was a match
239 node = node->next;
240 continue;
241 }
242
243 debugs(28, 3, "aclmatchAclList: " << this << " returning (AND list entry awaiting an async lookup)");
244 PROF_stop(aclMatchAclList);
245 return;
246 }
247
248 node = node->next;
249 }
250
251 debugs(28, 3, "aclmatchAclList: " << this << " returning true (AND list satisfied)");
252
253 markFinished();
254 PROF_stop(aclMatchAclList);
255 }
256
257 ACLChecklist::ACLChecklist() :
258 accessList (NULL),
259 callback (NULL),
260 callback_data (NULL),
261 async_(false),
262 finished_(false),
263 allow_(ACCESS_DENIED),
264 state_(NullState::Instance()),
265 lastACLResult_(false)
266 {
267 }
268
269 ACLChecklist::~ACLChecklist()
270 {
271 assert (!asyncInProgress());
272
273 cbdataReferenceDone(accessList);
274
275 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
276 }
277
278
279 void
280 ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
281 {
282 checklist->changeState(newState);
283 }
284
285 ACLChecklist::NullState *
286 ACLChecklist::NullState::Instance()
287 {
288 return &_instance;
289 }
290
291 void
292 ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
293 {}
294
295 ACLChecklist::NullState ACLChecklist::NullState::_instance;
296
297 void
298 ACLChecklist::changeState (AsyncState *newState)
299 {
300 /* only change from null to active and back again,
301 * not active to active.
302 * relax this once conversion to states is complete
303 * RBC 02 2003
304 */
305 assert (state_ == NullState::Instance() || newState == NullState::Instance());
306 state_ = newState;
307 }
308
309 ACLChecklist::AsyncState *
310 ACLChecklist::asyncState() const
311 {
312 return state_;
313 }
314
315 /**
316 * Kick off a non-blocking (slow) ACL access list test
317 *
318 * NP: this should probably be made Async now.
319 */
320 void
321 ACLChecklist::nonBlockingCheck(ACLCB * callback_, void *callback_data_)
322 {
323 callback = callback_;
324 callback_data = cbdataReference(callback_data_);
325 matchNonBlocking();
326 }
327
328 allow_t const &
329 ACLChecklist::fastCheck(const ACLList * list)
330 {
331 PROF_start(aclCheckFast);
332 currentAnswer(ACCESS_DUNNO);
333 matchAclList(list, true);
334 // assume ALLOWED on matches due to not having an acl_access object
335 if (finished())
336 currentAnswer(ACCESS_ALLOWED);
337 PROF_stop(aclCheckFast);
338 return currentAnswer();
339 }
340
341 /* Warning: do not cbdata lock this here - it
342 * may be static or on the stack
343 */
344 allow_t const &
345 ACLChecklist::fastCheck()
346 {
347 PROF_start(aclCheckFast);
348 currentAnswer(ACCESS_DUNNO);
349
350 debugs(28, 5, "aclCheckFast: list: " << accessList);
351 const acl_access *acl = cbdataReference(accessList);
352 while (acl != NULL && cbdataReferenceValid(acl)) {
353 matchAclList(acl->aclList, true);
354 if (finished()) {
355 currentAnswer(acl->allow);
356 PROF_stop(aclCheckFast);
357 cbdataReferenceDone(acl);
358 return currentAnswer();
359 }
360
361 /*
362 * Reference the next access entry
363 */
364 const acl_access *A = acl;
365 acl = cbdataReference(acl->next);
366 cbdataReferenceDone(A);
367 }
368
369 debugs(28, 5, "aclCheckFast: no matches, returning: " << currentAnswer());
370 PROF_stop(aclCheckFast);
371
372 return currentAnswer();
373 }
374
375
376 bool
377 ACLChecklist::checking() const
378 {
379 return checking_;
380 }
381
382 void
383 ACLChecklist::checking (bool const newValue)
384 {
385 checking_ = newValue;
386 }
387
388 bool
389 ACLChecklist::callerGone()
390 {
391 return !cbdataReferenceValid(callback_data);
392 }