4 * DO NOT MODIFY NEXT 2 LINES:
5 * arch-tag: 6803fde1-d5a2-4c29-9034-1c0c9f650eb4
7 * DEBUG: section 29 Authenticator
8 * AUTHOR: Robert Collins
10 * SQUID Web Proxy Cache http://www.squid-cache.org/
11 * ----------------------------------------------------------
13 * Squid is the result of efforts by numerous individuals from
14 * the Internet community; see the CONTRIBUTORS file for full
15 * details. Many organizations have provided support for Squid's
16 * development; see the SPONSORS file for full details. Squid is
17 * Copyrighted (C) 2001 by the Regents of the University of
18 * California; see the COPYRIGHT file for full details. Squid
19 * incorporates software developed and/or copyrighted by other
20 * sources; see the CREDITS file for full details.
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 /* The functions in this file handle authentication.
39 * They DO NOT perform access control or auditing.
40 * See acl.c for access control and client_side.c for auditing */
43 #include "auth/Config.h"
44 #include "auth/Scheme.h"
45 #include "auth/UserRequest.h"
46 #include "auth/User.h"
47 #include "client_side.h"
48 #include "comm/Connection.h"
49 #include "HttpReply.h"
50 #include "HttpRequest.h"
52 /* Generic Functions */
55 Auth::UserRequest::username() const
58 return user()->username();
63 /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
65 /* send the initial data to an authenticator module */
67 Auth::UserRequest::start(AUTHCB
* handler
, void *data
)
71 debugs(29, 9, HERE
<< "auth_user_request '" << this << "'");
72 module_start(handler
, data
);
76 Auth::UserRequest::valid() const
78 debugs(29, 9, HERE
<< "Validating Auth::UserRequest '" << this << "'.");
81 debugs(29, 4, HERE
<< "No associated Auth::User data");
85 if (user()->auth_type
== Auth::AUTH_UNKNOWN
) {
86 debugs(29, 4, HERE
<< "Auth::User '" << user() << "' uses unknown scheme.");
90 if (user()->auth_type
== Auth::AUTH_BROKEN
) {
91 debugs(29, 4, HERE
<< "Auth::User '" << user() << "' is broken for it's scheme.");
95 /* any other sanity checks that we need in the future */
97 /* finally return ok */
98 debugs(29, 5, HERE
<< "Validated. Auth::UserRequest '" << this << "'.");
103 Auth::UserRequest::operator new (size_t byteCount
)
105 fatal("Auth::UserRequest not directly allocatable\n");
110 Auth::UserRequest::operator delete (void *address
)
112 fatal("Auth::UserRequest child failed to override operator delete\n");
115 Auth::UserRequest::UserRequest():
118 lastReply(AUTH_ACL_CANNOT_AUTHENTICATE
)
120 debugs(29, 5, HERE
<< "initialised request " << this);
123 Auth::UserRequest::~UserRequest()
125 assert(RefCountCount()==0);
126 debugs(29, 5, HERE
<< "freeing request " << this);
128 if (user() != NULL
) {
129 /* release our references to the user credentials */
137 Auth::UserRequest::setDenyMessage(char const *aString
)
140 message
= xstrdup(aString
);
144 Auth::UserRequest::getDenyMessage()
150 Auth::UserRequest::denyMessage(char const * const default_message
)
152 if (this == NULL
|| getDenyMessage() == NULL
) {
153 return default_message
;
156 return getDenyMessage();
160 authenticateAuthUserRequestSetIp(Auth::UserRequest::Pointer auth_user_request
, Ip::Address
&ipaddr
)
162 Auth::User::Pointer auth_user
= auth_user_request
->user();
167 auth_user
->addIp(ipaddr
);
171 authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer auth_user_request
, Ip::Address
const &ipaddr
)
173 Auth::User::Pointer auth_user
= auth_user_request
->user();
178 auth_user
->removeIp(ipaddr
);
182 authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer auth_user_request
)
184 if (auth_user_request
!= NULL
)
185 auth_user_request
->user()->clearIp();
189 authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer auth_user_request
)
191 assert(auth_user_request
!= NULL
);
192 assert(auth_user_request
->user() != NULL
);
193 return auth_user_request
->user()->ipcount
;
197 * authenticateUserAuthenticated: is this auth_user structure logged in ?
200 authenticateUserAuthenticated(Auth::UserRequest::Pointer auth_user_request
)
202 if (auth_user_request
== NULL
|| !auth_user_request
->valid())
205 return auth_user_request
->authenticated();
209 Auth::UserRequest::direction()
212 return Auth::CRED_ERROR
; // No credentials. Should this be a CHALLENGE instead?
214 if (authenticateUserAuthenticated(this))
215 return Auth::CRED_VALID
;
217 return module_direction();
221 Auth::UserRequest::addAuthenticationInfoHeader(HttpReply
* rep
, int accelerated
)
225 Auth::UserRequest::addAuthenticationInfoTrailer(HttpReply
* rep
, int accelerated
)
229 Auth::UserRequest::onConnectionClose(ConnStateData
*)
233 Auth::UserRequest::connLastHeader()
235 fatal("Auth::UserRequest::connLastHeader should always be overridden by conn based auth schemes");
240 * authenticateAuthenticateUser: call the module specific code to
241 * log this user request in.
242 * Cache hits may change the auth_user pointer in the structure if needed.
243 * This is basically a handle approach.
246 authenticateAuthenticateUser(Auth::UserRequest::Pointer auth_user_request
, HttpRequest
* request
, ConnStateData
* conn
, http_hdr_type type
)
248 assert(auth_user_request
.getRaw() != NULL
);
250 auth_user_request
->authenticate(request
, conn
, type
);
253 static Auth::UserRequest::Pointer
254 authTryGetUser(Auth::UserRequest::Pointer auth_user_request
, ConnStateData
* conn
, HttpRequest
* request
)
256 if (auth_user_request
!= NULL
)
257 return auth_user_request
;
258 else if (request
!= NULL
&& request
->auth_user_request
!= NULL
)
259 return request
->auth_user_request
;
260 else if (conn
!= NULL
)
261 return conn
->auth_user_request
;
267 * AUTH_ACL_CHALLENGE,
269 * AUTH_ACL_CANNOT_AUTHENTICATE,
272 * How to use: In your proxy-auth dependent acl code, use the following
275 * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED)
278 * when this code is reached, the request/connection is authenticated.
280 * if you have non-acl code, but want to force authentication, you need a
281 * callback mechanism like the acl testing routines that will send a 40[1|7] to
282 * the client when rv==AUTH_ACL_CHALLENGE, and will communicate with
283 * the authenticateStart routine for rv==AUTH_ACL_HELPER
285 * Caller is responsible for locking and unlocking their *auth_user_request!
288 Auth::UserRequest::authenticate(Auth::UserRequest::Pointer
* auth_user_request
, http_hdr_type headertype
, HttpRequest
* request
, ConnStateData
* conn
, Ip::Address
&src_addr
)
290 const char *proxy_auth
;
291 assert(headertype
!= 0);
293 proxy_auth
= request
->header
.getStr(headertype
);
296 * a note on proxy_auth logix here:
297 * proxy_auth==NULL -> unauthenticated request || already
298 * authenticated connection so we test for an authenticated
299 * connection when we recieve no authentication header.
302 /* a) can we find other credentials to use? and b) are they logged in already? */
303 if (proxy_auth
== NULL
&& !authenticateUserAuthenticated(authTryGetUser(*auth_user_request
,conn
,request
))) {
304 /* no header or authentication failed/got corrupted - restart */
305 debugs(29, 4, HERE
<< "No Proxy-Auth header and no working alternative. Requesting auth header.");
307 /* something wrong with the AUTH credentials. Force a new attempt */
309 /* connection auth we must reset on auth errors */
311 conn
->auth_user_request
= NULL
;
314 *auth_user_request
= NULL
;
315 return AUTH_ACL_CHALLENGE
;
319 * Is this an already authenticated connection with a new auth header?
320 * No check for function required in the if: its compulsory for conn based
323 if (proxy_auth
&& conn
!= NULL
&& conn
->auth_user_request
!= NULL
&&
324 authenticateUserAuthenticated(conn
->auth_user_request
) &&
325 conn
->auth_user_request
->connLastHeader() != NULL
&&
326 strcmp(proxy_auth
, conn
->auth_user_request
->connLastHeader())) {
327 debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
328 conn
->auth_user_request
<< ", Current user '" <<
329 conn
->auth_user_request
->username() << "' proxy_auth " <<
332 /* remove this request struct - the link is already authed and it can't be to reauth. */
334 /* This should _only_ ever occur on the first pass through
335 * authenticateAuthenticate
337 assert(*auth_user_request
== NULL
);
338 conn
->auth_user_request
= NULL
;
341 /* we have a proxy auth header and as far as we know this connection has
342 * not had bungled connection oriented authentication happen on it. */
343 debugs(29, 9, HERE
<< "header " << (proxy_auth
? proxy_auth
: "-") << ".");
345 if (*auth_user_request
== NULL
) {
347 debugs(29, 9, HERE
<< "This is a new checklist test on:" << conn
->clientConnection
);
350 if (proxy_auth
&& request
->auth_user_request
== NULL
&& conn
!= NULL
&& conn
->auth_user_request
!= NULL
) {
351 Auth::Config
* scheme
= Auth::Config::Find(proxy_auth
);
353 if (conn
->auth_user_request
->user() == NULL
|| conn
->auth_user_request
->user()->config
!= scheme
) {
354 debugs(29, DBG_IMPORTANT
, "WARNING: Unexpected change of authentication scheme from '" <<
355 conn
->auth_user_request
->user()->config
->type() <<
356 "' to '" << proxy_auth
<< "' (client " <<
359 conn
->auth_user_request
= NULL
;
363 if (request
->auth_user_request
== NULL
&& (conn
== NULL
|| conn
->auth_user_request
== NULL
)) {
364 /* beginning of a new request check */
365 debugs(29, 4, HERE
<< "No connection authentication type");
367 *auth_user_request
= Auth::Config::CreateAuthUser(proxy_auth
);
368 if (*auth_user_request
== NULL
)
369 return AUTH_ACL_CHALLENGE
;
370 else if (!(*auth_user_request
)->valid()) {
371 /* the decode might have left a username for logging, or a message to
374 if ((*auth_user_request
)->username()) {
375 request
->auth_user_request
= *auth_user_request
;
378 *auth_user_request
= NULL
;
379 return AUTH_ACL_CHALLENGE
;
382 } else if (request
->auth_user_request
!= NULL
) {
383 *auth_user_request
= request
->auth_user_request
;
385 assert (conn
!= NULL
);
386 if (conn
->auth_user_request
!= NULL
) {
387 *auth_user_request
= conn
->auth_user_request
;
389 /* failed connection based authentication */
390 debugs(29, 4, HERE
<< "Auth user request " <<
391 *auth_user_request
<< " conn-auth user request " <<
392 conn
->auth_user_request
<< " conn type " <<
393 conn
->auth_user_request
->user()->auth_type
<< " authentication failed.");
395 *auth_user_request
= NULL
;
396 return AUTH_ACL_CHALLENGE
;
401 if (!authenticateUserAuthenticated(*auth_user_request
)) {
402 /* User not logged in. Try to log them in */
403 authenticateAuthenticateUser(*auth_user_request
, request
, conn
, headertype
);
405 switch ((*auth_user_request
)->direction()) {
407 case Auth::CRED_CHALLENGE
:
409 if (request
->auth_user_request
== NULL
) {
410 request
->auth_user_request
= *auth_user_request
;
413 /* fallthrough to ERROR case and do the challenge */
415 case Auth::CRED_ERROR
:
416 /* this ACL check is finished. */
417 *auth_user_request
= NULL
;
418 return AUTH_ACL_CHALLENGE
;
420 case Auth::CRED_LOOKUP
:
421 /* we are partway through authentication within squid,
422 * the *auth_user_request variables stores the auth_user_request
423 * for the callback to here - Do not Unlock */
424 return AUTH_ACL_HELPER
;
426 case Auth::CRED_VALID
:
427 /* authentication is finished */
428 /* See if user authentication failed for some reason */
429 if (!authenticateUserAuthenticated(*auth_user_request
)) {
430 if ((*auth_user_request
)->username()) {
431 if (!request
->auth_user_request
) {
432 request
->auth_user_request
= *auth_user_request
;
436 *auth_user_request
= NULL
;
437 return AUTH_ACL_CHALLENGE
;
439 // otherwise fallthrough to acceptance.
443 /* copy username to request for logging on client-side */
444 /* the credentials are correct at this point */
445 if (request
->auth_user_request
== NULL
) {
446 request
->auth_user_request
= *auth_user_request
;
447 authenticateAuthUserRequestSetIp(*auth_user_request
, src_addr
);
450 return AUTH_AUTHENTICATED
;
454 Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer
* aUR
, http_hdr_type headertype
, HttpRequest
* request
, ConnStateData
* conn
, Ip::Address
&src_addr
)
456 // If we have already been called, return the cached value
457 Auth::UserRequest::Pointer t
= authTryGetUser(*aUR
, conn
, request
);
459 if (t
!= NULL
&& t
->lastReply
!= AUTH_ACL_CANNOT_AUTHENTICATE
&& t
->lastReply
!= AUTH_ACL_HELPER
) {
463 if (request
->auth_user_request
== NULL
&& t
->lastReply
== AUTH_AUTHENTICATED
) {
464 request
->auth_user_request
= t
;
469 // ok, call the actual authenticator routine.
470 AuthAclState result
= authenticate(aUR
, headertype
, request
, conn
, src_addr
);
472 // auth process may have changed the UserRequest we are dealing with
473 t
= authTryGetUser(*aUR
, conn
, request
);
475 if (t
!= NULL
&& result
!= AUTH_ACL_CANNOT_AUTHENTICATE
&& result
!= AUTH_ACL_HELPER
)
476 t
->lastReply
= result
;
482 Auth::UserRequest::addReplyAuthHeader(HttpReply
* rep
, Auth::UserRequest::Pointer auth_user_request
, HttpRequest
* request
, int accelerated
, int internal
)
483 /* send the auth types we are configured to support (and have compiled in!) */
487 switch (rep
->sline
.status
) {
489 case HTTP_PROXY_AUTHENTICATION_REQUIRED
:
490 /* Proxy authorisation needed */
491 type
= HDR_PROXY_AUTHENTICATE
;
494 case HTTP_UNAUTHORIZED
:
495 /* WWW Authorisation needed */
496 type
= HDR_WWW_AUTHENTICATE
;
501 /* some other HTTP status */
506 debugs(29, 9, HERE
<< "headertype:" << type
<< " authuser:" << auth_user_request
);
508 if (((rep
->sline
.status
== HTTP_PROXY_AUTHENTICATION_REQUIRED
)
509 || (rep
->sline
.status
== HTTP_UNAUTHORIZED
)) && internal
)
510 /* this is a authenticate-needed response */
513 if (auth_user_request
!= NULL
&& auth_user_request
->direction() == Auth::CRED_CHALLENGE
)
514 /* add the scheme specific challenge header to the response */
515 auth_user_request
->user()->config
->fixHeader(auth_user_request
, rep
, type
, request
);
517 /* call each configured & running authscheme */
519 for (Auth::ConfigVector::iterator i
= Auth::TheConfig
.begin(); i
!= Auth::TheConfig
.end(); ++i
) {
520 Auth::Config
*scheme
= *i
;
522 if (scheme
->active())
523 scheme
->fixHeader(NULL
, rep
, type
, request
);
525 debugs(29, 4, HERE
<< "Configured scheme " << scheme
->type() << " not Active");
532 * allow protocol specific headers to be _added_ to the existing
533 * response - currently Digest or Negotiate auth
535 if (auth_user_request
!= NULL
) {
536 auth_user_request
->addAuthenticationInfoHeader(rep
, accelerated
);
537 if (auth_user_request
->lastReply
!= AUTH_AUTHENTICATED
)
538 auth_user_request
->lastReply
= AUTH_ACL_CANNOT_AUTHENTICATE
;
542 // TODO remove wrapper.
544 authenticateFixHeader(HttpReply
* rep
, Auth::UserRequest::Pointer auth_user_request
, HttpRequest
* request
, int accelerated
, int internal
)
546 Auth::UserRequest::addReplyAuthHeader(rep
, auth_user_request
, request
, accelerated
, internal
);
549 /* call the active auth module and allow it to add a trailer to the request */
550 // TODO remove wrapper
552 authenticateAddTrailer(HttpReply
* rep
, Auth::UserRequest::Pointer auth_user_request
, HttpRequest
* request
, int accelerated
)
554 if (auth_user_request
!= NULL
)
555 auth_user_request
->addAuthenticationInfoTrailer(rep
, accelerated
);
558 Auth::Scheme::Pointer
559 Auth::UserRequest::scheme() const
561 return Auth::Scheme::Find(user()->config
->type());