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