]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ACLChecklist.cc
Import IPv6 support from squid3-ipv6 branch to 3-HEAD.
[thirdparty/squid.git] / src / ACLChecklist.cc
CommitLineData
225b7b10 1/*
cc192b50 2 * $Id: ACLChecklist.cc,v 1.43 2007/12/14 23:11:45 amosjeffries 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
315b856d 131 if (callerGone()) {
132 checkCallback(currentAnswer());
133 return;
134 }
135
225b7b10 136 /* NOTE: This holds a cbdata reference to the current access_list
137 * entry, not the whole list.
138 */
139 while (accessList != NULL) {
62e76326 140 /*
141 * If the _acl_access is no longer valid (i.e. its been
142 * freed because of a reconfigure), then bail on this
143 * access check. For now, return ACCESS_DENIED.
144 */
145
146 if (!cbdataReferenceValid(accessList)) {
147 cbdataReferenceDone(accessList);
bf8fe701 148 debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
62e76326 149 continue;
150 }
151
152 checking (true);
153 checkAccessList();
154 checking (false);
155
156 if (asyncInProgress()) {
157 return;
158 }
159
160 if (finished()) {
161 /*
162 * We are done. Either the request
163 * is allowed, denied, requires authentication.
164 */
bf8fe701 165 debugs(28, 3, "ACLChecklist::check: " << this << " match found, calling back with " << currentAnswer());
62e76326 166 cbdataReferenceDone(accessList); /* A */
167 checkCallback(currentAnswer());
168 /* From here on in, this may be invalid */
169 return;
170 }
171
172 /*
173 * Reference the next access entry
174 */
175 const acl_access *A = accessList;
176
177 assert (A);
178
179 accessList = cbdataReference(A->next);
180
181 cbdataReferenceDone(A);
225b7b10 182 }
62e76326 183
225b7b10 184 /* dropped off the end of the list */
bf8fe701 185 debugs(28, 3, "ACLChecklist::check: " << this <<
186 " NO match found, returning " <<
187 (currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED));
62e76326 188
225b7b10 189 checkCallback(currentAnswer() != ACCESS_DENIED ? ACCESS_DENIED : ACCESS_ALLOWED);
190}
191
192bool
193ACLChecklist::asyncInProgress() const
194{
195 return async_;
196}
197
198void
199ACLChecklist::asyncInProgress(bool const newAsync)
200{
3841dd46 201 assert (!finished() && !(asyncInProgress() && newAsync));
225b7b10 202 async_ = newAsync;
bf8fe701 203 debugs(28, 3, "ACLChecklist::asyncInProgress: " << this <<
204 " async set to " << async_);
225b7b10 205}
206
207bool
208ACLChecklist::finished() const
209{
210 return finished_;
211}
212
213void
214ACLChecklist::markFinished()
215{
3841dd46 216 assert (!finished() && !asyncInProgress());
225b7b10 217 finished_ = true;
bf8fe701 218 debugs(28, 3, "ACLChecklist::markFinished: " << this <<
219 " checklist processing finished");
225b7b10 220}
221
c35e260d 222void
223ACLChecklist::preCheck()
224{
bf8fe701 225 debugs(28, 3, "ACLChecklist::preCheck: " << this << " checking '" << accessList->cfgline << "'");
c35e260d 226 /* what is our result on a match? */
227 currentAnswer(accessList->allow);
228}
229
225b7b10 230void
231ACLChecklist::checkAccessList()
232{
c35e260d 233 preCheck();
225b7b10 234 /* does the current AND clause match */
ef1955a5 235 matchAclListSlow(accessList->aclList);
225b7b10 236}
237
238void
239ACLChecklist::checkForAsync()
240{
3841dd46 241 asyncState()->checkForAsync(this);
225b7b10 242}
243
244void
245ACLChecklist::checkCallback(allow_t answer)
246{
247 PF *callback_;
248 void *cbdata_;
bf8fe701 249 debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
250
6bf4f823 251 /* During reconfigure, we can end up not finishing call
252 * sequences into the auth code */
253
254 if (auth_user_request) {
255 /* the checklist lock */
4f0ef8e8 256 AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLChecklist");
6bf4f823 257 /* it might have been connection based */
4d3a24ca 258 assert(conn() != NULL);
4f0ef8e8 259 /*
260 * DPW 2007-05-08
261 * yuck, this make me uncomfortable. why do this here?
262 * ConnStateData will do its own unlocking.
263 */
264 AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLChecklist");
6bf4f823 265 conn()->auth_type = AUTH_BROKEN;
6bf4f823 266 }
62e76326 267
225b7b10 268 callback_ = callback;
269 callback = NULL;
62e76326 270
225b7b10 271 if (cbdataReferenceValidDone(callback_data, &cbdata_))
62e76326 272 callback_(answer, cbdata_);
273
00d77d6b 274 delete this;
225b7b10 275}
62e76326 276
5ea3aff4 277void
76cd39d7 278ACLChecklist::matchAclListSlow(const ACLList * list)
5ea3aff4 279{
280 matchAclList(list, false);
281}
282
48071869 283void
76cd39d7 284ACLChecklist::matchAclList(const ACLList * head, bool const fast)
225b7b10 285{
286 PROF_start(aclMatchAclList);
76cd39d7 287 const ACLList *node = head;
62e76326 288
ba63443a 289 finished_ = false;
290
225b7b10 291 while (node) {
62e76326 292 bool nodeMatched = node->matches(this);
293
294 if (fast)
295 changeState(NullState::Instance());
296
ff3f3722 297 if (finished()) {
298 PROF_stop(aclMatchAclList);
299 return;
300 }
301
62e76326 302 if (!nodeMatched || state_ != NullState::Instance()) {
bf8fe701 303 debugs(28, 3, "aclmatchAclList: " << this << " returning false (AND list entry failed to match)");
304
ab321f4b 305 bool async = state_ != NullState::Instance();
306
48071869 307 checkForAsync();
501b58f9 308
ab321f4b 309 bool async_in_progress = asyncInProgress();
bf8fe701 310 debugs(28, 3, "aclmatchAclList: async=" << (async ? 1 : 0) <<
311 " nodeMatched=" << (nodeMatched ? 1 : 0) <<
312 " async_in_progress=" << (async_in_progress ? 1 : 0) <<
ff3f3722 313 " lastACLResult() = " << (lastACLResult() ? 1 : 0) <<
314 " finished() = " << finished());
315
316 if (finished()) {
317 PROF_stop(aclMatchAclList);
318 return;
319 }
ab321f4b 320
321 if (async && nodeMatched && !asyncInProgress() && lastACLResult()) {
322 // async acl, but using cached response, and it was a match
323 node = node->next;
324 continue;
325 }
326
62e76326 327 PROF_stop(aclMatchAclList);
501b58f9 328
48071869 329 return;
62e76326 330 }
331
332 node = node->next;
225b7b10 333 }
62e76326 334
bf8fe701 335 debugs(28, 3, "aclmatchAclList: " << this << " returning true (AND list satisfied)");
336
48071869 337 markFinished();
225b7b10 338 PROF_stop(aclMatchAclList);
225b7b10 339}
340
341CBDATA_CLASS_INIT(ACLChecklist);
342
343void *
344ACLChecklist::operator new (size_t size)
345{
346 assert (size == sizeof(ACLChecklist));
347 CBDATA_INIT_TYPE(ACLChecklist);
348 ACLChecklist *result = cbdataAlloc(ACLChecklist);
aa625860 349 return result;
225b7b10 350}
62e76326 351
225b7b10 352void
353ACLChecklist::operator delete (void *address)
354{
355 ACLChecklist *t = static_cast<ACLChecklist *>(address);
aa625860 356 cbdataFree(t);
225b7b10 357}
358
cc192b50 359ACLChecklist::ACLChecklist() :
360 accessList (NULL),
361 request (NULL),
62e76326 362 reply (NULL),
4f0ef8e8 363 auth_user_request (NULL),
225b7b10 364#if SQUID_SNMP
4f0ef8e8 365 snmp_community(NULL),
225b7b10 366#endif
4f0ef8e8 367 callback (NULL),
62e76326 368 callback_data (NULL),
369 extacl_entry (NULL),
370 conn_(NULL),
371 async_(false),
372 finished_(false),
373 allow_(ACCESS_DENIED),
374 state_(NullState::Instance()),
375 destinationDomainChecked_(false),
ab321f4b 376 sourceDomainChecked_(false),
377 lastACLResult_(false)
225b7b10 378{
cc192b50 379 my_addr.SetEmpty();
380 src_addr.SetEmpty();
381 dst_addr.SetEmpty();
225b7b10 382 rfc931[0] = '\0';
225b7b10 383}
384
385ACLChecklist::~ACLChecklist()
386{
387 assert (!asyncInProgress());
62e76326 388
225b7b10 389 if (extacl_entry)
62e76326 390 cbdataReferenceDone(extacl_entry);
391
6dd9f4bd 392 HTTPMSGUNLOCK(request);
62e76326 393
7dc5f514 394 HTTPMSGUNLOCK(reply);
395
4f0ef8e8 396 /*
397 * DPW 2007-05-08
398 * If this fails, then we'll need a backup UNLOCK call in the
399 * destructor.
400 */
cc192b50 401 /* AYJ: It fails in builds without any Authentication configured */
4f0ef8e8 402 assert(auth_user_request == NULL);
403
a2ac85d9 404 conn_ = NULL;
62e76326 405
225b7b10 406 cbdataReferenceDone(accessList);
62e76326 407
bf8fe701 408 debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
225b7b10 409}
410
411
a2ac85d9 412ConnStateData::Pointer
225b7b10 413ACLChecklist::conn()
414{
415 return conn_;
416}
417
418void
a2ac85d9 419ACLChecklist::conn(ConnStateData::Pointer aConn)
225b7b10 420{
4d3a24ca 421 assert (conn() == NULL);
225b7b10 422 conn_ = aConn;
423}
424
425void
426ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
427{
428 checklist->changeState(newState);
429}
430
431ACLChecklist::NullState *
432ACLChecklist::NullState::Instance()
433{
434 return &_instance;
435}
436
437void
438ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
62e76326 439 {}
225b7b10 440
441ACLChecklist::NullState ACLChecklist::NullState::_instance;
442
443void
444ACLChecklist::changeState (AsyncState *newState)
445{
446 /* only change from null to active and back again,
447 * not active to active.
448 * relax this once conversion to states is complete
449 * RBC 02 2003
450 */
451 assert (state_ == NullState::Instance() || newState == NullState::Instance());
452 state_ = newState;
453}
454
455ACLChecklist::AsyncState *
456ACLChecklist::asyncState() const
457{
458 return state_;
459}
460
461void
462ACLChecklist::nonBlockingCheck(PF * callback_, void *callback_data_)
463{
464 callback = callback_;
465 callback_data = cbdataReference(callback_data_);
466 check();
467}
468
b448c119 469/* Warning: do not cbdata lock this here - it
470 * may be static or on the stack
471 */
472int
473ACLChecklist::fastCheck()
474{
475 PROF_start(aclCheckFast);
476 currentAnswer(ACCESS_DENIED);
bf8fe701 477 debugs(28, 5, "aclCheckFast: list: " << accessList);
b448c119 478
479 while (accessList) {
c35e260d 480 preCheck();
b448c119 481 matchAclListFast(accessList->aclList);
482
483 if (finished()) {
484 PROF_stop(aclCheckFast);
108d65b2 485 cbdataReferenceDone(accessList);
b448c119 486 return currentAnswer() == ACCESS_ALLOWED;
487 }
488
fddded8f 489 /*
490 * Reference the next access entry
491 */
492 const acl_access *A = accessList;
493
494 assert (A);
495
496 accessList = cbdataReference(A->next);
497
498 cbdataReferenceDone(A);
b448c119 499 }
500
bf8fe701 501 debugs(28, 5, "aclCheckFast: no matches, returning: " << (currentAnswer() == ACCESS_DENIED));
502
b448c119 503 PROF_stop(aclCheckFast);
504 return currentAnswer() == ACCESS_DENIED;
505}
506
507
3841dd46 508bool
509ACLChecklist::destinationDomainChecked() const
510{
511 return destinationDomainChecked_;
512}
513
514void
515ACLChecklist::markDestinationDomainChecked()
516{
517 assert (!finished() && !destinationDomainChecked());
518 destinationDomainChecked_ = true;
519}
520
521bool
522ACLChecklist::sourceDomainChecked() const
523{
524 return sourceDomainChecked_;
525}
526
527void
528ACLChecklist::markSourceDomainChecked()
529{
530 assert (!finished() && !sourceDomainChecked());
531 sourceDomainChecked_ = true;
532}
b6f25d16 533
534bool
535ACLChecklist::checking() const
536{
537 return checking_;
538}
539
540void
541ACLChecklist::checking (bool const newValue)
542{
543 checking_ = newValue;
544}
ef1955a5 545
b448c119 546/*
547 * Any ACLChecklist created by aclChecklistCreate() must eventually be
548 * freed by ACLChecklist::operator delete(). There are two common cases:
549 *
550 * A) Using aclCheckFast(): The caller creates the ACLChecklist using
551 * aclChecklistCreate(), checks it using aclCheckFast(), and frees it
552 * using aclChecklistFree().
553 *
554 * B) Using aclNBCheck() and callbacks: The caller creates the
555 * ACLChecklist using aclChecklistCreate(), and passes it to
556 * aclNBCheck(). Control eventually passes to ACLChecklist::checkCallback(),
557 * which will invoke the callback function as requested by the
558 * original caller of aclNBCheck(). This callback function must
559 * *not* invoke aclChecklistFree(). After the callback function
560 * returns, ACLChecklist::checkCallback() will free the ACLChecklist using
561 * aclChecklistFree().
562 */
563
564ACLChecklist *
565aclChecklistCreate(const acl_access * A, HttpRequest * request, const char *ident)
566{
567 ACLChecklist *checklist = new ACLChecklist;
568
569 if (A)
570 checklist->accessList = cbdataReference(A);
571
572 if (request != NULL) {
6dd9f4bd 573 checklist->request = HTTPMSGLOCK(request);
b448c119 574 checklist->src_addr = request->client_addr;
575 checklist->my_addr = request->my_addr;
b448c119 576 }
577
578#if USE_IDENT
579 if (ident)
580 xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
581
582#endif
583
b448c119 584 return checklist;
585}
586
315b856d 587bool
588ACLChecklist::callerGone()
589{
590 return !cbdataReferenceValid(callback_data);
591}
592
ef1955a5 593#ifndef _USE_INLINE_
594#include "ACLChecklist.cci"
595#endif