]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Checklist.cc
Merged 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.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::check()
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 matchAclListSlow(accessList->aclList);
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 PF *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::matchAclListSlow(const ACLList * list)
201 {
202 matchAclList(list, false);
203 }
204
205 void
206 ACLChecklist::matchAclList(const ACLList * head, bool const fast)
207 {
208 PROF_start(aclMatchAclList);
209 const ACLList *node = head;
210
211 finished_ = false;
212
213 while (node) {
214 bool nodeMatched = node->matches(this);
215
216 if (fast)
217 changeState(NullState::Instance());
218
219 if (finished()) {
220 PROF_stop(aclMatchAclList);
221 return;
222 }
223
224 if (!nodeMatched || state_ != NullState::Instance()) {
225 debugs(28, 3, "aclmatchAclList: " << this << " returning false (AND list entry failed to match)");
226
227 bool async = state_ != NullState::Instance();
228
229 checkForAsync();
230
231 bool async_in_progress = asyncInProgress();
232 debugs(28, 3, "aclmatchAclList: async=" << (async ? 1 : 0) <<
233 " nodeMatched=" << (nodeMatched ? 1 : 0) <<
234 " async_in_progress=" << (async_in_progress ? 1 : 0) <<
235 " lastACLResult() = " << (lastACLResult() ? 1 : 0) <<
236 " finished() = " << finished());
237
238 if (finished()) {
239 PROF_stop(aclMatchAclList);
240 return;
241 }
242
243 if (async && nodeMatched && !asyncInProgress() && lastACLResult()) {
244 // async acl, but using cached response, and it was a match
245 node = node->next;
246 continue;
247 }
248
249 PROF_stop(aclMatchAclList);
250
251 return;
252 }
253
254 node = node->next;
255 }
256
257 debugs(28, 3, "aclmatchAclList: " << this << " returning true (AND list satisfied)");
258
259 markFinished();
260 PROF_stop(aclMatchAclList);
261 }
262
263 ACLChecklist::ACLChecklist() :
264 accessList (NULL),
265 callback (NULL),
266 callback_data (NULL),
267 async_(false),
268 finished_(false),
269 allow_(ACCESS_DENIED),
270 state_(NullState::Instance()),
271 lastACLResult_(false)
272 {
273 }
274
275 ACLChecklist::~ACLChecklist()
276 {
277 assert (!asyncInProgress());
278
279 cbdataReferenceDone(accessList);
280
281 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
282 }
283
284
285 void
286 ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
287 {
288 checklist->changeState(newState);
289 }
290
291 ACLChecklist::NullState *
292 ACLChecklist::NullState::Instance()
293 {
294 return &_instance;
295 }
296
297 void
298 ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
299 {}
300
301 ACLChecklist::NullState ACLChecklist::NullState::_instance;
302
303 void
304 ACLChecklist::changeState (AsyncState *newState)
305 {
306 /* only change from null to active and back again,
307 * not active to active.
308 * relax this once conversion to states is complete
309 * RBC 02 2003
310 */
311 assert (state_ == NullState::Instance() || newState == NullState::Instance());
312 state_ = newState;
313 }
314
315 ACLChecklist::AsyncState *
316 ACLChecklist::asyncState() const
317 {
318 return state_;
319 }
320
321 /**
322 * Kick off a non-blocking (slow) ACL access list test
323 *
324 * NP: this should probably be made Async now.
325 */
326 void
327 ACLChecklist::nonBlockingCheck(PF * callback_, void *callback_data_)
328 {
329 callback = callback_;
330 callback_data = cbdataReference(callback_data_);
331 check();
332 }
333
334 /* Warning: do not cbdata lock this here - it
335 * may be static or on the stack
336 */
337 int
338 ACLChecklist::fastCheck()
339 {
340 PROF_start(aclCheckFast);
341 currentAnswer(ACCESS_DENIED);
342 debugs(28, 5, "aclCheckFast: list: " << accessList);
343
344 while (accessList) {
345 preCheck();
346 matchAclListFast(accessList->aclList);
347
348 if (finished()) {
349 PROF_stop(aclCheckFast);
350 cbdataReferenceDone(accessList);
351 return currentAnswer() == ACCESS_ALLOWED;
352 }
353
354 /*
355 * Reference the next access entry
356 */
357 const acl_access *A = accessList;
358
359 assert (A);
360
361 accessList = cbdataReference(A->next);
362
363 cbdataReferenceDone(A);
364 }
365
366 debugs(28, 5, "aclCheckFast: no matches, returning: " << (currentAnswer() == ACCESS_DENIED));
367
368 PROF_stop(aclCheckFast);
369 return currentAnswer() == ACCESS_DENIED;
370 }
371
372
373 bool
374 ACLChecklist::checking() const
375 {
376 return checking_;
377 }
378
379 void
380 ACLChecklist::checking (bool const newValue)
381 {
382 checking_ = newValue;
383 }
384
385 bool
386 ACLChecklist::callerGone()
387 {
388 return !cbdataReferenceValid(callback_data);
389 }
390
391 bool
392 ACLChecklist::matchAclListFast(const ACLList * list)
393 {
394 matchAclList(list, true);
395 return finished();
396 }
397
398