]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/UserRequest.cc
Removed CVS $ markers
[thirdparty/squid.git] / src / auth / UserRequest.cc
CommitLineData
f5691f9c 1/*
f5691f9c 2 * DO NOT MODIFY NEXT 2 LINES:
3 * arch-tag: 6803fde1-d5a2-4c29-9034-1c0c9f650eb4
4 *
5 * DEBUG: section 29 Authenticator
6 * AUTHOR: Robert Collins
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
26ac0430 24 *
f5691f9c 25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
26ac0430 29 *
f5691f9c 30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36/* The functions in this file handle authentication.
37 * They DO NOT perform access control or auditing.
38 * See acl.c for access control and client_side.c for auditing */
39
582c2af2 40#include "squid.h"
2d2b0bb7
AR
41#include "auth/Config.h"
42#include "auth/Scheme.h"
c7baff40
AJ
43#include "auth/UserRequest.h"
44#include "auth/User.h"
582c2af2 45#include "client_side.h"
5c336a3b 46#include "comm/Connection.h"
f5691f9c 47#include "HttpReply.h"
48#include "HttpRequest.h"
f5691f9c 49
50/* Generic Functions */
51
f5691f9c 52char const *
c7baff40 53Auth::UserRequest::username() const
f5691f9c 54{
56a49fda 55 if (user() != NULL)
f5691f9c 56 return user()->username();
57 else
58 return NULL;
59}
60
f5691f9c 61/**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
62
63/* send the initial data to an authenticator module */
64void
4c535e87 65Auth::UserRequest::start(AUTHCB * handler, void *data)
f5691f9c 66{
67 assert(handler);
a088a999 68 assert(data);
c7baff40 69 debugs(29, 9, HERE << "auth_user_request '" << this << "'");
f5691f9c 70 module_start(handler, data);
71}
72
2e39494f 73bool
c7baff40 74Auth::UserRequest::valid() const
f5691f9c 75{
c7baff40 76 debugs(29, 9, HERE << "Validating Auth::UserRequest '" << this << "'.");
f5691f9c 77
2e39494f 78 if (user() == NULL) {
d87154ee 79 debugs(29, 4, HERE << "No associated Auth::User data");
0a608df9 80 return false;
f5691f9c 81 }
82
616cfc4c 83 if (user()->auth_type == Auth::AUTH_UNKNOWN) {
d87154ee 84 debugs(29, 4, HERE << "Auth::User '" << user() << "' uses unknown scheme.");
2e39494f 85 return false;
f5691f9c 86 }
87
616cfc4c 88 if (user()->auth_type == Auth::AUTH_BROKEN) {
d87154ee 89 debugs(29, 4, HERE << "Auth::User '" << user() << "' is broken for it's scheme.");
2e39494f 90 return false;
f5691f9c 91 }
92
93 /* any other sanity checks that we need in the future */
94
f5691f9c 95 /* finally return ok */
c7baff40 96 debugs(29, 5, HERE << "Validated. Auth::UserRequest '" << this << "'.");
2e39494f 97 return true;
f5691f9c 98}
99
100void *
c7baff40 101Auth::UserRequest::operator new (size_t byteCount)
f5691f9c 102{
c7baff40 103 fatal("Auth::UserRequest not directly allocatable\n");
f5691f9c 104 return (void *)1;
105}
106
107void
c7baff40 108Auth::UserRequest::operator delete (void *address)
f5691f9c 109{
c7baff40 110 fatal("Auth::UserRequest child failed to override operator delete\n");
f5691f9c 111}
112
c7baff40 113Auth::UserRequest::UserRequest():
d232141d
AJ
114 _auth_user(NULL),
115 message(NULL),
116 lastReply(AUTH_ACL_CANNOT_AUTHENTICATE)
f5691f9c 117{
c7baff40 118 debugs(29, 5, HERE << "initialised request " << this);
f5691f9c 119}
120
c7baff40 121Auth::UserRequest::~UserRequest()
f5691f9c 122{
ea0695f2 123 assert(RefCountCount()==0);
c7baff40 124 debugs(29, 5, HERE << "freeing request " << this);
f5691f9c 125
56a49fda 126 if (user() != NULL) {
56a49fda 127 /* release our references to the user credentials */
f5691f9c 128 user(NULL);
129 }
130
56a49fda 131 safe_free(message);
f5691f9c 132}
133
134void
c7baff40 135Auth::UserRequest::setDenyMessage(char const *aString)
f5691f9c 136{
56a49fda
AJ
137 safe_free(message);
138 message = xstrdup(aString);
f5691f9c 139}
140
141char const *
c7baff40 142Auth::UserRequest::getDenyMessage()
f5691f9c 143{
144 return message;
145}
146
147char const *
c7baff40 148Auth::UserRequest::denyMessage(char const * const default_message)
f5691f9c 149{
150 if (this == NULL || getDenyMessage() == NULL) {
151 return default_message;
152 }
153
154 return getDenyMessage();
155}
156
157static void
c7baff40 158authenticateAuthUserRequestSetIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
f5691f9c 159{
d87154ee 160 Auth::User::Pointer auth_user = auth_user_request->user();
f5691f9c 161
4c19ba24 162 if (!auth_user)
f5691f9c 163 return;
164
4c19ba24 165 auth_user->addIp(ipaddr);
f5691f9c 166}
167
168void
c7baff40 169authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
f5691f9c 170{
d87154ee 171 Auth::User::Pointer auth_user = auth_user_request->user();
f5691f9c 172
4c19ba24 173 if (!auth_user)
f5691f9c 174 return;
175
4c19ba24 176 auth_user->removeIp(ipaddr);
f5691f9c 177}
178
179void
c7baff40 180authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer auth_user_request)
f5691f9c 181{
a33a428a 182 if (auth_user_request != NULL)
f5691f9c 183 auth_user_request->user()->clearIp();
184}
185
4b0f5de8 186int
c7baff40 187authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer auth_user_request)
f5691f9c 188{
a33a428a 189 assert(auth_user_request != NULL);
56a49fda 190 assert(auth_user_request->user() != NULL);
f5691f9c 191 return auth_user_request->user()->ipcount;
192}
193
f5691f9c 194/*
195 * authenticateUserAuthenticated: is this auth_user structure logged in ?
196 */
197int
c7baff40 198authenticateUserAuthenticated(Auth::UserRequest::Pointer auth_user_request)
f5691f9c 199{
0a608df9 200 if (auth_user_request == NULL || !auth_user_request->valid())
f5691f9c 201 return 0;
202
203 return auth_user_request->authenticated();
204}
205
51a3dd58 206Auth::Direction
c7baff40 207Auth::UserRequest::direction()
f5691f9c 208{
51a3dd58
AJ
209 if (user() == NULL)
210 return Auth::CRED_ERROR; // No credentials. Should this be a CHALLENGE instead?
211
f5691f9c 212 if (authenticateUserAuthenticated(this))
51a3dd58 213 return Auth::CRED_VALID;
f5691f9c 214
215 return module_direction();
f5691f9c 216}
217
218void
c7baff40 219Auth::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accelerated)
f5691f9c 220{}
221
222void
c7baff40 223Auth::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accelerated)
f5691f9c 224{}
225
226void
c7baff40 227Auth::UserRequest::onConnectionClose(ConnStateData *)
f5691f9c 228{}
229
230const char *
c7baff40 231Auth::UserRequest::connLastHeader()
f5691f9c 232{
c7baff40 233 fatal("Auth::UserRequest::connLastHeader should always be overridden by conn based auth schemes");
f5691f9c 234 return NULL;
235}
236
237/*
26ac0430 238 * authenticateAuthenticateUser: call the module specific code to
f5691f9c 239 * log this user request in.
240 * Cache hits may change the auth_user pointer in the structure if needed.
241 * This is basically a handle approach.
242 */
243static void
c7baff40 244authenticateAuthenticateUser(Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
f5691f9c 245{
a33a428a 246 assert(auth_user_request.getRaw() != NULL);
f5691f9c 247
248 auth_user_request->authenticate(request, conn, type);
249}
250
c7baff40
AJ
251static Auth::UserRequest::Pointer
252authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * conn, HttpRequest * request)
f5691f9c 253{
a33a428a
AJ
254 if (auth_user_request != NULL)
255 return auth_user_request;
256 else if (request != NULL && request->auth_user_request != NULL)
f5691f9c 257 return request->auth_user_request;
4d3a24ca 258 else if (conn != NULL)
f5691f9c 259 return conn->auth_user_request;
260 else
261 return NULL;
262}
263
264/* returns one of
265 * AUTH_ACL_CHALLENGE,
266 * AUTH_ACL_HELPER,
267 * AUTH_ACL_CANNOT_AUTHENTICATE,
268 * AUTH_AUTHENTICATED
269 *
26ac0430 270 * How to use: In your proxy-auth dependent acl code, use the following
f5691f9c 271 * construct:
272 * int rv;
273 * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED)
274 * return rv;
26ac0430 275 *
f5691f9c 276 * when this code is reached, the request/connection is authenticated.
277 *
26ac0430 278 * if you have non-acl code, but want to force authentication, you need a
f5691f9c 279 * callback mechanism like the acl testing routines that will send a 40[1|7] to
26ac0430 280 * the client when rv==AUTH_ACL_CHALLENGE, and will communicate with
f5691f9c 281 * the authenticateStart routine for rv==AUTH_ACL_HELPER
4f0ef8e8 282 *
283 * Caller is responsible for locking and unlocking their *auth_user_request!
f5691f9c 284 */
56a49fda 285AuthAclState
c7baff40 286Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
f5691f9c 287{
288 const char *proxy_auth;
289 assert(headertype != 0);
290
a9925b40 291 proxy_auth = request->header.getStr(headertype);
f5691f9c 292
293 /*
294 * a note on proxy_auth logix here:
295 * proxy_auth==NULL -> unauthenticated request || already
296 * authenticated connection so we test for an authenticated
297 * connection when we recieve no authentication header.
298 */
299
a1ce83aa
AJ
300 /* a) can we find other credentials to use? and b) are they logged in already? */
301 if (proxy_auth == NULL && !authenticateUserAuthenticated(authTryGetUser(*auth_user_request,conn,request))) {
f5691f9c 302 /* no header or authentication failed/got corrupted - restart */
a1ce83aa
AJ
303 debugs(29, 4, HERE << "No Proxy-Auth header and no working alternative. Requesting auth header.");
304
f5691f9c 305 /* something wrong with the AUTH credentials. Force a new attempt */
306
a1ce83aa 307 /* connection auth we must reset on auth errors */
4d3a24ca 308 if (conn != NULL) {
a33a428a 309 conn->auth_user_request = NULL;
f5691f9c 310 }
311
26ac0430 312 *auth_user_request = NULL;
f5691f9c 313 return AUTH_ACL_CHALLENGE;
314 }
315
316 /*
317 * Is this an already authenticated connection with a new auth header?
26ac0430 318 * No check for function required in the if: its compulsory for conn based
f5691f9c 319 * auth modules
320 */
a33a428a 321 if (proxy_auth && conn != NULL && conn->auth_user_request != NULL &&
f5691f9c 322 authenticateUserAuthenticated(conn->auth_user_request) &&
6bf4f823 323 conn->auth_user_request->connLastHeader() != NULL &&
26ac0430 324 strcmp(proxy_auth, conn->auth_user_request->connLastHeader())) {
a1ce83aa 325 debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
bf8fe701 326 conn->auth_user_request << ", Current user '" <<
327 conn->auth_user_request->username() << "' proxy_auth " <<
328 proxy_auth);
329
a1ce83aa 330 /* remove this request struct - the link is already authed and it can't be to reauth. */
f5691f9c 331
332 /* This should _only_ ever occur on the first pass through
26ac0430 333 * authenticateAuthenticate
f5691f9c 334 */
335 assert(*auth_user_request == NULL);
a33a428a 336 conn->auth_user_request = NULL;
f5691f9c 337 }
338
339 /* we have a proxy auth header and as far as we know this connection has
340 * not had bungled connection oriented authentication happen on it. */
a1ce83aa 341 debugs(29, 9, HERE << "header " << (proxy_auth ? proxy_auth : "-") << ".");
f5691f9c 342
26ac0430 343 if (*auth_user_request == NULL) {
5c336a3b 344 if (conn != NULL) {
73c36fd9 345 debugs(29, 9, HERE << "This is a new checklist test on:" << conn->clientConnection);
5c336a3b 346 }
f5691f9c 347
a33a428a 348 if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->auth_user_request != NULL) {
9f3d2b2e 349 Auth::Config * scheme = Auth::Config::Find(proxy_auth);
638b2ce3 350
a1ce83aa 351 if (conn->auth_user_request->user() == NULL || conn->auth_user_request->user()->config != scheme) {
c7baff40 352 debugs(29, DBG_IMPORTANT, "WARNING: Unexpected change of authentication scheme from '" <<
bf8fe701 353 conn->auth_user_request->user()->config->type() <<
354 "' to '" << proxy_auth << "' (client " <<
cc192b50 355 src_addr << ")");
bf8fe701 356
a33a428a 357 conn->auth_user_request = NULL;
638b2ce3 358 }
359 }
360
a1ce83aa 361 if (request->auth_user_request == NULL && (conn == NULL || conn->auth_user_request == NULL)) {
f5691f9c 362 /* beginning of a new request check */
a1ce83aa 363 debugs(29, 4, HERE << "No connection authentication type");
f5691f9c 364
9f3d2b2e 365 *auth_user_request = Auth::Config::CreateAuthUser(proxy_auth);
0a608df9
AJ
366 if (*auth_user_request == NULL)
367 return AUTH_ACL_CHALLENGE;
368 else if (!(*auth_user_request)->valid()) {
f5691f9c 369 /* the decode might have left a username for logging, or a message to
370 * the user */
371
372 if ((*auth_user_request)->username()) {
f5691f9c 373 request->auth_user_request = *auth_user_request;
374 }
375
f5691f9c 376 *auth_user_request = NULL;
f5691f9c 377 return AUTH_ACL_CHALLENGE;
378 }
379
a33a428a 380 } else if (request->auth_user_request != NULL) {
f5691f9c 381 *auth_user_request = request->auth_user_request;
f5691f9c 382 } else {
4d3a24ca 383 assert (conn != NULL);
a33a428a 384 if (conn->auth_user_request != NULL) {
f5691f9c 385 *auth_user_request = conn->auth_user_request;
f5691f9c 386 } else {
387 /* failed connection based authentication */
a1ce83aa 388 debugs(29, 4, HERE << "Auth user request " <<
bf8fe701 389 *auth_user_request << " conn-auth user request " <<
390 conn->auth_user_request << " conn type " <<
a1ce83aa 391 conn->auth_user_request->user()->auth_type << " authentication failed.");
bf8fe701 392
f5691f9c 393 *auth_user_request = NULL;
394 return AUTH_ACL_CHALLENGE;
395 }
396 }
397 }
398
26ac0430 399 if (!authenticateUserAuthenticated(*auth_user_request)) {
51a3dd58 400 /* User not logged in. Try to log them in */
a33a428a 401 authenticateAuthenticateUser(*auth_user_request, request, conn, headertype);
f5691f9c 402
51a3dd58 403 switch ((*auth_user_request)->direction()) {
f5691f9c 404
51a3dd58 405 case Auth::CRED_CHALLENGE:
f5691f9c 406
a33a428a 407 if (request->auth_user_request == NULL) {
f5691f9c 408 request->auth_user_request = *auth_user_request;
409 }
410
51a3dd58 411 /* fallthrough to ERROR case and do the challenge */
f5691f9c 412
51a3dd58 413 case Auth::CRED_ERROR:
4f0ef8e8 414 /* this ACL check is finished. */
f5691f9c 415 *auth_user_request = NULL;
f5691f9c 416 return AUTH_ACL_CHALLENGE;
417
51a3dd58 418 case Auth::CRED_LOOKUP:
f5691f9c 419 /* we are partway through authentication within squid,
420 * the *auth_user_request variables stores the auth_user_request
421 * for the callback to here - Do not Unlock */
422 return AUTH_ACL_HELPER;
f5691f9c 423
51a3dd58
AJ
424 case Auth::CRED_VALID:
425 /* authentication is finished */
426 /* See if user authentication failed for some reason */
427 if (!authenticateUserAuthenticated(*auth_user_request)) {
428 if ((*auth_user_request)->username()) {
429 if (!request->auth_user_request) {
430 request->auth_user_request = *auth_user_request;
431 }
f5691f9c 432 }
f5691f9c 433
51a3dd58
AJ
434 *auth_user_request = NULL;
435 return AUTH_ACL_CHALLENGE;
436 }
437 // otherwise fallthrough to acceptance.
f5691f9c 438 }
439 }
440
441 /* copy username to request for logging on client-side */
442 /* the credentials are correct at this point */
a33a428a 443 if (request->auth_user_request == NULL) {
f5691f9c 444 request->auth_user_request = *auth_user_request;
f5691f9c 445 authenticateAuthUserRequestSetIp(*auth_user_request, src_addr);
446 }
447
f5691f9c 448 return AUTH_AUTHENTICATED;
449}
450
56a49fda 451AuthAclState
c7baff40 452Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * aUR, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
f5691f9c 453{
c7baff40
AJ
454 // If we have already been called, return the cached value
455 Auth::UserRequest::Pointer t = authTryGetUser(*aUR, conn, request);
f5691f9c 456
a33a428a 457 if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
c7baff40
AJ
458 if (*aUR == NULL)
459 *aUR = t;
f5691f9c 460
a33a428a 461 if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
26ac0430 462 request->auth_user_request = t;
26ac0430 463 }
f5691f9c 464 return t->lastReply;
465 }
466
c7baff40
AJ
467 // ok, call the actual authenticator routine.
468 AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr);
f5691f9c 469
c7baff40
AJ
470 // auth process may have changed the UserRequest we are dealing with
471 t = authTryGetUser(*aUR, conn, request);
f5691f9c 472
a33a428a 473 if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
f5691f9c 474 t->lastReply = result;
475
476 return result;
477}
478
f5691f9c 479void
c7baff40 480Auth::UserRequest::addReplyAuthHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
f5691f9c 481/* send the auth types we are configured to support (and have compiled in!) */
482{
483 http_hdr_type type;
484
26ac0430 485 switch (rep->sline.status) {
f5691f9c 486
487 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
488 /* Proxy authorisation needed */
489 type = HDR_PROXY_AUTHENTICATE;
490 break;
491
492 case HTTP_UNAUTHORIZED:
493 /* WWW Authorisation needed */
494 type = HDR_WWW_AUTHENTICATE;
495 break;
496
497 default:
498 /* Keep GCC happy */
499 /* some other HTTP status */
500 type = HDR_ENUM_END;
501 break;
502 }
503
c7baff40 504 debugs(29, 9, HERE << "headertype:" << type << " authuser:" << auth_user_request);
f5691f9c 505
506 if (((rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
507 || (rep->sline.status == HTTP_UNAUTHORIZED)) && internal)
508 /* this is a authenticate-needed response */
509 {
510
51a3dd58
AJ
511 if (auth_user_request != NULL && auth_user_request->direction() == Auth::CRED_CHALLENGE)
512 /* add the scheme specific challenge header to the response */
f5691f9c 513 auth_user_request->user()->config->fixHeader(auth_user_request, rep, type, request);
26ac0430 514 else {
f5691f9c 515 /* call each configured & running authscheme */
516
9f3d2b2e
AJ
517 for (Auth::ConfigVector::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i) {
518 Auth::Config *scheme = *i;
f5691f9c 519
520 if (scheme->active())
521 scheme->fixHeader(NULL, rep, type, request);
522 else
c7baff40 523 debugs(29, 4, HERE << "Configured scheme " << scheme->type() << " not Active");
f5691f9c 524 }
525 }
526
527 }
7afc3bf2 528
f5691f9c 529 /*
530 * allow protocol specific headers to be _added_ to the existing
7afc3bf2 531 * response - currently Digest or Negotiate auth
f5691f9c 532 */
26ac0430 533 if (auth_user_request != NULL) {
7afc3bf2 534 auth_user_request->addAuthenticationInfoHeader(rep, accelerated);
26ac0430
AJ
535 if (auth_user_request->lastReply != AUTH_AUTHENTICATED)
536 auth_user_request->lastReply = AUTH_ACL_CANNOT_AUTHENTICATE;
f5691f9c 537 }
538}
539
c7baff40 540// TODO remove wrapper.
f5691f9c 541void
c7baff40 542authenticateFixHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
f5691f9c 543{
c7baff40 544 Auth::UserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal);
f5691f9c 545}
546
f5691f9c 547/* call the active auth module and allow it to add a trailer to the request */
c7baff40 548// TODO remove wrapper
f5691f9c 549void
c7baff40 550authenticateAddTrailer(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated)
f5691f9c 551{
552 if (auth_user_request != NULL)
7afc3bf2 553 auth_user_request->addAuthenticationInfoTrailer(rep, accelerated);
f5691f9c 554}
555
c6cf8dee 556Auth::Scheme::Pointer
c7baff40 557Auth::UserRequest::scheme() const
f5691f9c 558{
c6cf8dee 559 return Auth::Scheme::Find(user()->config->type());
f5691f9c 560}