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