]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ACLChecklist.cc
Author: Christos Tsantilas <chtsanti@users.sourceforge.net>
[thirdparty/squid.git] / src / ACLChecklist.cc
CommitLineData
225b7b10 1/*
5a363fb1 2 * $Id: ACLChecklist.cc,v 1.40 2007/07/19 00:35:27 hno Exp $
225b7b10 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 "ACLChecklist.h"
225b7b10 38#include "HttpRequest.h"
7dc5f514 39#include "HttpReply.h"
225b7b10 40#include "authenticate.h"
225b7b10 41#include "ACLProxyAuth.h"
a46d2c0e 42#include "client_side.h"
f5691f9c 43#include "AuthUserRequest.h"
225b7b10 44
45int
46ACLChecklist::authenticated()
47{
48 http_hdr_type headertype;
62e76326 49
225b7b10 50 if (NULL == request) {
62e76326 51 fatal ("requiresRequest SHOULD have been true for this ACL!!");
52 return 0;
d048c262 53 } else if (request->flags.accelerated) {
62e76326 54 /* WWW authorization on accelerated requests */
55 headertype = HDR_AUTHORIZATION;
d048c262 56 } else if (request->flags.transparent) {
bf8fe701 57 debugs(28, 1, "ACHChecklist::authenticated: authentication not applicable on transparently intercepted requests.");
62e76326 58 return -1;
d048c262 59 } else {
60 /* Proxy authorization on proxy requests */
61 headertype = HDR_PROXY_AUTHORIZATION;
225b7b10 62 }
62e76326 63
225b7b10 64 /* get authed here */
6bf4f823 65 /* Note: this fills in auth_user_request when applicable */
4f0ef8e8 66 /*
67 * DPW 2007-05-08
68 * tryToAuthenticateAndSetAuthUser used to try to lock and
69 * unlock auth_user_request on our behalf, but it was too
70 * ugly and hard to follow. Now we do our own locking here.
71 *
72 * I'm not sure what tryToAuthenticateAndSetAuthUser does when
73 * auth_user_request is set before calling. I'm tempted to
74 * unlock and set it to NULL, but it seems safer to save the
75 * pointer before calling and unlock it afterwards. If the
76 * pointer doesn't change then its a no-op.
77 */
78 AuthUserRequest *old_auth_user_request = auth_user_request;
79 auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr);
80 if (auth_user_request)
81 AUTHUSERREQUESTLOCK(auth_user_request, "ACLChecklist");
82 AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLChecklist");
83 switch (result) {
62e76326 84
225b7b10 85 case AUTH_ACL_CANNOT_AUTHENTICATE:
bf8fe701 86 debugs(28, 4, "aclMatchAcl: returning 0 user authenticated but not authorised.");
62e76326 87 return 0;
88
225b7b10 89 case AUTH_AUTHENTICATED:
8777d90e 90
62e76326 91 return 1;
92 break;
93
225b7b10 94 case AUTH_ACL_HELPER:
bf8fe701 95 debugs(28, 4, "aclMatchAcl: returning 0 sending credentials to helper.");
62e76326 96 changeState (ProxyAuthLookup::Instance());
97 return 0;
98
225b7b10 99 case AUTH_ACL_CHALLENGE:
bf8fe701 100 debugs(28, 4, "aclMatchAcl: returning 0 sending authentication challenge.");
62e76326 101 changeState (ProxyAuthNeeded::Instance());
102 return 0;
103
225b7b10 104 default:
62e76326 105 fatal("unexpected authenticateAuthenticate reply\n");
106 return 0;
225b7b10 107 }
108}
109
110allow_t const &
111ACLChecklist::currentAnswer() const
112{
113 return allow_;
114}
115
116void
117ACLChecklist::currentAnswer(allow_t const newAnswer)
118{
119 allow_ = newAnswer;
120}
62e76326 121
225b7b10 122void
123ACLChecklist::check()
124{
b6f25d16 125 if (checking())
62e76326 126 return;
127
225b7b10 128 /* deny if no rules present */
129 currentAnswer(ACCESS_DENIED);
62e76326 130
225b7b10 131 /* NOTE: This holds a cbdata reference to the current access_list
132 * entry, not the whole list.
133 */
134 while (accessList != NULL) {
62e76326 135 /*
136 * If the _acl_access is no longer valid (i.e. its been
137 * freed because of a reconfigure), then bail on this
138 * access check. For now, return ACCESS_DENIED.
139 */
140
141 if (!cbdataReferenceValid(accessList)) {
142 cbdataReferenceDone(accessList);
bf8fe701 143 debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
62e76326 144 continue;
145 }
146
147 checking (true);
148 checkAccessList();
149 checking (false);
150
151 if (asyncInProgress()) {
152 return;
153 }
154
155 if (finished()) {
156 /*
157 * We are done. Either the request
158 * is allowed, denied, requires authentication.
159 */
bf8fe701 160 debugs(28, 3, "ACLChecklist::check: " << this << " match found, calling back with " << currentAnswer());
62e76326 161 cbdataReferenceDone(accessList); /* A */
162 checkCallback(currentAnswer());
163 /* From here on in, this may be invalid */
164 return;
165 }
166
167 /*
168 * Reference the next access entry
169 */
170 const acl_access *A = accessList;
171
172 assert (A);
173
174 accessList = cbdataReference(A->next);
175
176 cbdataReferenceDone(A);
225b7b10 177 }
62e76326 178
225b7b10 179 /* dropped off the end of the list */
bf8fe701 180 debugs(28, 3, "ACLChecklist::check: " << this <<
181 " NO match found, returning " <<
182 (currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED));
62e76326 183
225b7b10 184 checkCallback(currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED);
185}
186
187bool
188ACLChecklist::asyncInProgress() const
189{
190 return async_;
191}
192
193void
194ACLChecklist::asyncInProgress(bool const newAsync)
195{
3841dd46 196 assert (!finished() && !(asyncInProgress() && newAsync));
225b7b10 197 async_ = newAsync;
bf8fe701 198 debugs(28, 3, "ACLChecklist::asyncInProgress: " << this <<
199 " async set to " << async_);
225b7b10 200}
201
202bool
203ACLChecklist::finished() const
204{
205 return finished_;
206}
207
208void
209ACLChecklist::markFinished()
210{
3841dd46 211 assert (!finished() && !asyncInProgress());
225b7b10 212 finished_ = true;
bf8fe701 213 debugs(28, 3, "ACLChecklist::markFinished: " << this <<
214 " checklist processing finished");
225b7b10 215}
216
c35e260d 217void
218ACLChecklist::preCheck()
219{
bf8fe701 220 debugs(28, 3, "ACLChecklist::preCheck: " << this << " checking '" << accessList->cfgline << "'");
c35e260d 221 /* what is our result on a match? */
222 currentAnswer(accessList->allow);
223}
224
225b7b10 225void
226ACLChecklist::checkAccessList()
227{
c35e260d 228 preCheck();
225b7b10 229 /* does the current AND clause match */
ef1955a5 230 matchAclListSlow(accessList->aclList);
225b7b10 231}
232
233void
234ACLChecklist::checkForAsync()
235{
3841dd46 236 asyncState()->checkForAsync(this);
225b7b10 237}
238
239void
240ACLChecklist::checkCallback(allow_t answer)
241{
242 PF *callback_;
243 void *cbdata_;
bf8fe701 244 debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
245
6bf4f823 246 /* During reconfigure, we can end up not finishing call
247 * sequences into the auth code */
248
249 if (auth_user_request) {
250 /* the checklist lock */
4f0ef8e8 251 AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLChecklist");
6bf4f823 252 /* it might have been connection based */
4d3a24ca 253 assert(conn() != NULL);
4f0ef8e8 254 /*
255 * DPW 2007-05-08
256 * yuck, this make me uncomfortable. why do this here?
257 * ConnStateData will do its own unlocking.
258 */
259 AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLChecklist");
6bf4f823 260 conn()->auth_type = AUTH_BROKEN;
6bf4f823 261 }
62e76326 262
225b7b10 263 callback_ = callback;
264 callback = NULL;
62e76326 265
225b7b10 266 if (cbdataReferenceValidDone(callback_data, &cbdata_))
62e76326 267 callback_(answer, cbdata_);
268
00d77d6b 269 delete this;
225b7b10 270}
62e76326 271
5ea3aff4 272void
273ACLChecklist::matchAclListSlow(const acl_list * list)
274{
275 matchAclList(list, false);
276}
277
48071869 278void
5dee515e 279ACLChecklist::matchAclList(const acl_list * head, bool const fast)
225b7b10 280{
281 PROF_start(aclMatchAclList);
282 const acl_list *node = head;
62e76326 283
ba63443a 284 finished_ = false;
285
225b7b10 286 while (node) {
62e76326 287 bool nodeMatched = node->matches(this);
288
289 if (fast)
290 changeState(NullState::Instance());
291
ff3f3722 292 if (finished()) {
293 PROF_stop(aclMatchAclList);
294 return;
295 }
296
62e76326 297 if (!nodeMatched || state_ != NullState::Instance()) {
bf8fe701 298 debugs(28, 3, "aclmatchAclList: " << this << " returning false (AND list entry failed to match)");
299
ab321f4b 300 bool async = state_ != NullState::Instance();
301
48071869 302 checkForAsync();
501b58f9 303
ab321f4b 304 bool async_in_progress = asyncInProgress();
bf8fe701 305 debugs(28, 3, "aclmatchAclList: async=" << (async ? 1 : 0) <<
306 " nodeMatched=" << (nodeMatched ? 1 : 0) <<
307 " async_in_progress=" << (async_in_progress ? 1 : 0) <<
ff3f3722 308 " lastACLResult() = " << (lastACLResult() ? 1 : 0) <<
309 " finished() = " << finished());
310
311 if (finished()) {
312 PROF_stop(aclMatchAclList);
313 return;
314 }
ab321f4b 315
316 if (async && nodeMatched && !asyncInProgress() && lastACLResult()) {
317 // async acl, but using cached response, and it was a match
318 node = node->next;
319 continue;
320 }
321
62e76326 322 PROF_stop(aclMatchAclList);
501b58f9 323
48071869 324 return;
62e76326 325 }
326
327 node = node->next;
225b7b10 328 }
62e76326 329
bf8fe701 330 debugs(28, 3, "aclmatchAclList: " << this << " returning true (AND list satisfied)");
331
48071869 332 markFinished();
225b7b10 333 PROF_stop(aclMatchAclList);
225b7b10 334}
335
336CBDATA_CLASS_INIT(ACLChecklist);
337
338void *
339ACLChecklist::operator new (size_t size)
340{
341 assert (size == sizeof(ACLChecklist));
342 CBDATA_INIT_TYPE(ACLChecklist);
343 ACLChecklist *result = cbdataAlloc(ACLChecklist);
aa625860 344 return result;
225b7b10 345}
62e76326 346
225b7b10 347void
348ACLChecklist::operator delete (void *address)
349{
350 ACLChecklist *t = static_cast<ACLChecklist *>(address);
aa625860 351 cbdataFree(t);
225b7b10 352}
353
225b7b10 354ACLChecklist::ACLChecklist() : accessList (NULL), my_port (0), request (NULL),
62e76326 355 reply (NULL),
4f0ef8e8 356 auth_user_request (NULL),
225b7b10 357#if SQUID_SNMP
4f0ef8e8 358 snmp_community(NULL),
225b7b10 359#endif
4f0ef8e8 360 callback (NULL),
62e76326 361 callback_data (NULL),
362 extacl_entry (NULL),
363 conn_(NULL),
364 async_(false),
365 finished_(false),
366 allow_(ACCESS_DENIED),
367 state_(NullState::Instance()),
368 destinationDomainChecked_(false),
ab321f4b 369 sourceDomainChecked_(false),
370 lastACLResult_(false)
225b7b10 371{
62e76326 372
ddfcbc22 373 memset (&src_addr, '\0', sizeof (struct IN_ADDR));
62e76326 374
ddfcbc22 375 memset (&dst_addr, '\0', sizeof (struct IN_ADDR));
62e76326 376
ddfcbc22 377 memset (&my_addr, '\0', sizeof (struct IN_ADDR));
225b7b10 378 rfc931[0] = '\0';
225b7b10 379}
380
381ACLChecklist::~ACLChecklist()
382{
383 assert (!asyncInProgress());
62e76326 384
225b7b10 385 if (extacl_entry)
62e76326 386 cbdataReferenceDone(extacl_entry);
387
6dd9f4bd 388 HTTPMSGUNLOCK(request);
62e76326 389
7dc5f514 390 HTTPMSGUNLOCK(reply);
391
4f0ef8e8 392 /*
393 * DPW 2007-05-08
394 * If this fails, then we'll need a backup UNLOCK call in the
395 * destructor.
396 */
397 assert(auth_user_request == NULL);
398
a2ac85d9 399 conn_ = NULL;
62e76326 400
225b7b10 401 cbdataReferenceDone(accessList);
62e76326 402
bf8fe701 403 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
225b7b10 404}
405
406
a2ac85d9 407ConnStateData::Pointer
225b7b10 408ACLChecklist::conn()
409{
410 return conn_;
411}
412
413void
a2ac85d9 414ACLChecklist::conn(ConnStateData::Pointer aConn)
225b7b10 415{
4d3a24ca 416 assert (conn() == NULL);
225b7b10 417 conn_ = aConn;
418}
419
420void
421ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
422{
423 checklist->changeState(newState);
424}
425
426ACLChecklist::NullState *
427ACLChecklist::NullState::Instance()
428{
429 return &_instance;
430}
431
432void
433ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
62e76326 434 {}
225b7b10 435
436ACLChecklist::NullState ACLChecklist::NullState::_instance;
437
438void
439ACLChecklist::changeState (AsyncState *newState)
440{
441 /* only change from null to active and back again,
442 * not active to active.
443 * relax this once conversion to states is complete
444 * RBC 02 2003
445 */
446 assert (state_ == NullState::Instance() || newState == NullState::Instance());
447 state_ = newState;
448}
449
450ACLChecklist::AsyncState *
451ACLChecklist::asyncState() const
452{
453 return state_;
454}
455
456void
457ACLChecklist::nonBlockingCheck(PF * callback_, void *callback_data_)
458{
459 callback = callback_;
460 callback_data = cbdataReference(callback_data_);
461 check();
462}
463
b448c119 464/* Warning: do not cbdata lock this here - it
465 * may be static or on the stack
466 */
467int
468ACLChecklist::fastCheck()
469{
470 PROF_start(aclCheckFast);
471 currentAnswer(ACCESS_DENIED);
bf8fe701 472 debugs(28, 5, "aclCheckFast: list: " << accessList);
b448c119 473
474 while (accessList) {
c35e260d 475 preCheck();
b448c119 476 matchAclListFast(accessList->aclList);
477
478 if (finished()) {
479 PROF_stop(aclCheckFast);
108d65b2 480 cbdataReferenceDone(accessList);
b448c119 481 return currentAnswer() == ACCESS_ALLOWED;
482 }
483
fddded8f 484 /*
485 * Reference the next access entry
486 */
487 const acl_access *A = accessList;
488
489 assert (A);
490
491 accessList = cbdataReference(A->next);
492
493 cbdataReferenceDone(A);
b448c119 494 }
495
bf8fe701 496 debugs(28, 5, "aclCheckFast: no matches, returning: " << (currentAnswer() == ACCESS_DENIED));
497
b448c119 498 PROF_stop(aclCheckFast);
499 return currentAnswer() == ACCESS_DENIED;
500}
501
502
3841dd46 503bool
504ACLChecklist::destinationDomainChecked() const
505{
506 return destinationDomainChecked_;
507}
508
509void
510ACLChecklist::markDestinationDomainChecked()
511{
512 assert (!finished() && !destinationDomainChecked());
513 destinationDomainChecked_ = true;
514}
515
516bool
517ACLChecklist::sourceDomainChecked() const
518{
519 return sourceDomainChecked_;
520}
521
522void
523ACLChecklist::markSourceDomainChecked()
524{
525 assert (!finished() && !sourceDomainChecked());
526 sourceDomainChecked_ = true;
527}
b6f25d16 528
529bool
530ACLChecklist::checking() const
531{
532 return checking_;
533}
534
535void
536ACLChecklist::checking (bool const newValue)
537{
538 checking_ = newValue;
539}
ef1955a5 540
b448c119 541/*
542 * Any ACLChecklist created by aclChecklistCreate() must eventually be
543 * freed by ACLChecklist::operator delete(). There are two common cases:
544 *
545 * A) Using aclCheckFast(): The caller creates the ACLChecklist using
546 * aclChecklistCreate(), checks it using aclCheckFast(), and frees it
547 * using aclChecklistFree().
548 *
549 * B) Using aclNBCheck() and callbacks: The caller creates the
550 * ACLChecklist using aclChecklistCreate(), and passes it to
551 * aclNBCheck(). Control eventually passes to ACLChecklist::checkCallback(),
552 * which will invoke the callback function as requested by the
553 * original caller of aclNBCheck(). This callback function must
554 * *not* invoke aclChecklistFree(). After the callback function
555 * returns, ACLChecklist::checkCallback() will free the ACLChecklist using
556 * aclChecklistFree().
557 */
558
559ACLChecklist *
560aclChecklistCreate(const acl_access * A, HttpRequest * request, const char *ident)
561{
562 ACLChecklist *checklist = new ACLChecklist;
563
564 if (A)
565 checklist->accessList = cbdataReference(A);
566
567 if (request != NULL) {
6dd9f4bd 568 checklist->request = HTTPMSGLOCK(request);
b448c119 569 checklist->src_addr = request->client_addr;
570 checklist->my_addr = request->my_addr;
571 checklist->my_port = request->my_port;
572 }
573
574#if USE_IDENT
575 if (ident)
576 xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
577
578#endif
579
b448c119 580 return checklist;
581}
582
ef1955a5 583#ifndef _USE_INLINE_
584#include "ACLChecklist.cci"
585#endif