]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/UserRequest.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / auth / UserRequest.cc
1 /*
2 * $Id$
3 *
4 * DO NOT MODIFY NEXT 2 LINES:
5 * arch-tag: 6803fde1-d5a2-4c29-9034-1c0c9f650eb4
6 *
7 * DEBUG: section 29 Authenticator
8 * AUTHOR: Robert Collins
9 *
10 * SQUID Web Proxy Cache http://www.squid-cache.org/
11 * ----------------------------------------------------------
12 *
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.
21 *
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.
26 *
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.
31 *
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.
35 *
36 */
37
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 */
41
42 #include "squid-old.h"
43 #include "auth/Config.h"
44 #include "auth/Scheme.h"
45 #include "auth/UserRequest.h"
46 #include "auth/User.h"
47 #include "comm/Connection.h"
48 #include "HttpReply.h"
49 #include "HttpRequest.h"
50
51 /* Generic Functions */
52
53 char const *
54 Auth::UserRequest::username() const
55 {
56 if (user() != NULL)
57 return user()->username();
58 else
59 return NULL;
60 }
61
62 /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
63
64 /* send the initial data to an authenticator module */
65 void
66 Auth::UserRequest::start(RH * handler, void *data)
67 {
68 assert(handler);
69 assert(data);
70 debugs(29, 9, HERE << "auth_user_request '" << this << "'");
71 module_start(handler, data);
72 }
73
74 bool
75 Auth::UserRequest::valid() const
76 {
77 debugs(29, 9, HERE << "Validating Auth::UserRequest '" << this << "'.");
78
79 if (user() == NULL) {
80 debugs(29, 4, HERE << "No associated Auth::User data");
81 return false;
82 }
83
84 if (user()->auth_type == Auth::AUTH_UNKNOWN) {
85 debugs(29, 4, HERE << "Auth::User '" << user() << "' uses unknown scheme.");
86 return false;
87 }
88
89 if (user()->auth_type == Auth::AUTH_BROKEN) {
90 debugs(29, 4, HERE << "Auth::User '" << user() << "' is broken for it's scheme.");
91 return false;
92 }
93
94 /* any other sanity checks that we need in the future */
95
96 /* finally return ok */
97 debugs(29, 5, HERE << "Validated. Auth::UserRequest '" << this << "'.");
98 return true;
99 }
100
101 void *
102 Auth::UserRequest::operator new (size_t byteCount)
103 {
104 fatal("Auth::UserRequest not directly allocatable\n");
105 return (void *)1;
106 }
107
108 void
109 Auth::UserRequest::operator delete (void *address)
110 {
111 fatal("Auth::UserRequest child failed to override operator delete\n");
112 }
113
114 Auth::UserRequest::UserRequest():
115 _auth_user(NULL),
116 message(NULL),
117 lastReply(AUTH_ACL_CANNOT_AUTHENTICATE)
118 {
119 debugs(29, 5, HERE << "initialised request " << this);
120 }
121
122 Auth::UserRequest::~UserRequest()
123 {
124 assert(RefCountCount()==0);
125 debugs(29, 5, HERE << "freeing request " << this);
126
127 if (user() != NULL) {
128 /* release our references to the user credentials */
129 user(NULL);
130 }
131
132 safe_free(message);
133 }
134
135 void
136 Auth::UserRequest::setDenyMessage(char const *aString)
137 {
138 safe_free(message);
139 message = xstrdup(aString);
140 }
141
142 char const *
143 Auth::UserRequest::getDenyMessage()
144 {
145 return message;
146 }
147
148 char const *
149 Auth::UserRequest::denyMessage(char const * const default_message)
150 {
151 if (this == NULL || getDenyMessage() == NULL) {
152 return default_message;
153 }
154
155 return getDenyMessage();
156 }
157
158 static void
159 authenticateAuthUserRequestSetIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
160 {
161 Auth::User::Pointer auth_user = auth_user_request->user();
162
163 if (!auth_user)
164 return;
165
166 auth_user->addIp(ipaddr);
167 }
168
169 void
170 authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
171 {
172 Auth::User::Pointer auth_user = auth_user_request->user();
173
174 if (!auth_user)
175 return;
176
177 auth_user->removeIp(ipaddr);
178 }
179
180 void
181 authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer auth_user_request)
182 {
183 if (auth_user_request != NULL)
184 auth_user_request->user()->clearIp();
185 }
186
187 int
188 authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer auth_user_request)
189 {
190 assert(auth_user_request != NULL);
191 assert(auth_user_request->user() != NULL);
192 return auth_user_request->user()->ipcount;
193 }
194
195
196 /*
197 * authenticateUserAuthenticated: is this auth_user structure logged in ?
198 */
199 int
200 authenticateUserAuthenticated(Auth::UserRequest::Pointer auth_user_request)
201 {
202 if (auth_user_request == NULL || !auth_user_request->valid())
203 return 0;
204
205 return auth_user_request->authenticated();
206 }
207
208 Auth::Direction
209 Auth::UserRequest::direction()
210 {
211 if (user() == NULL)
212 return Auth::CRED_ERROR; // No credentials. Should this be a CHALLENGE instead?
213
214 if (authenticateUserAuthenticated(this))
215 return Auth::CRED_VALID;
216
217 return module_direction();
218 }
219
220 void
221 Auth::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accelerated)
222 {}
223
224 void
225 Auth::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accelerated)
226 {}
227
228 void
229 Auth::UserRequest::onConnectionClose(ConnStateData *)
230 {}
231
232 const char *
233 Auth::UserRequest::connLastHeader()
234 {
235 fatal("Auth::UserRequest::connLastHeader should always be overridden by conn based auth schemes");
236 return NULL;
237 }
238
239 /*
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.
244 */
245 static void
246 authenticateAuthenticateUser(Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
247 {
248 assert(auth_user_request.getRaw() != NULL);
249
250 auth_user_request->authenticate(request, conn, type);
251 }
252
253 static Auth::UserRequest::Pointer
254 authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * conn, HttpRequest * request)
255 {
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;
262 else
263 return NULL;
264 }
265
266 /* returns one of
267 * AUTH_ACL_CHALLENGE,
268 * AUTH_ACL_HELPER,
269 * AUTH_ACL_CANNOT_AUTHENTICATE,
270 * AUTH_AUTHENTICATED
271 *
272 * How to use: In your proxy-auth dependent acl code, use the following
273 * construct:
274 * int rv;
275 * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED)
276 * return rv;
277 *
278 * when this code is reached, the request/connection is authenticated.
279 *
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
284 *
285 * Caller is responsible for locking and unlocking their *auth_user_request!
286 */
287 AuthAclState
288 Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
289 {
290 const char *proxy_auth;
291 assert(headertype != 0);
292
293 proxy_auth = request->header.getStr(headertype);
294
295 /*
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.
300 */
301
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.");
306
307 /* something wrong with the AUTH credentials. Force a new attempt */
308
309 /* connection auth we must reset on auth errors */
310 if (conn != NULL) {
311 conn->auth_user_request = NULL;
312 }
313
314 *auth_user_request = NULL;
315 return AUTH_ACL_CHALLENGE;
316 }
317
318 /*
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
321 * auth modules
322 */
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 " <<
330 proxy_auth);
331
332 /* remove this request struct - the link is already authed and it can't be to reauth. */
333
334 /* This should _only_ ever occur on the first pass through
335 * authenticateAuthenticate
336 */
337 assert(*auth_user_request == NULL);
338 conn->auth_user_request = NULL;
339 }
340
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 : "-") << ".");
344
345 if (*auth_user_request == NULL) {
346 if (conn != NULL) {
347 debugs(29, 9, HERE << "This is a new checklist test on:" << conn->clientConnection);
348 }
349
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);
352
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 " <<
357 src_addr << ")");
358
359 conn->auth_user_request = NULL;
360 }
361 }
362
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");
366
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
372 * the user */
373
374 if ((*auth_user_request)->username()) {
375 request->auth_user_request = *auth_user_request;
376 }
377
378 *auth_user_request = NULL;
379 return AUTH_ACL_CHALLENGE;
380 }
381
382 } else if (request->auth_user_request != NULL) {
383 *auth_user_request = request->auth_user_request;
384 } else {
385 assert (conn != NULL);
386 if (conn->auth_user_request != NULL) {
387 *auth_user_request = conn->auth_user_request;
388 } else {
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.");
394
395 *auth_user_request = NULL;
396 return AUTH_ACL_CHALLENGE;
397 }
398 }
399 }
400
401 if (!authenticateUserAuthenticated(*auth_user_request)) {
402 /* User not logged in. Try to log them in */
403 authenticateAuthenticateUser(*auth_user_request, request, conn, headertype);
404
405 switch ((*auth_user_request)->direction()) {
406
407 case Auth::CRED_CHALLENGE:
408
409 if (request->auth_user_request == NULL) {
410 request->auth_user_request = *auth_user_request;
411 }
412
413 /* fallthrough to ERROR case and do the challenge */
414
415 case Auth::CRED_ERROR:
416 /* this ACL check is finished. */
417 *auth_user_request = NULL;
418 return AUTH_ACL_CHALLENGE;
419
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;
425
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;
433 }
434 }
435
436 *auth_user_request = NULL;
437 return AUTH_ACL_CHALLENGE;
438 }
439 // otherwise fallthrough to acceptance.
440 }
441 }
442
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);
448 }
449
450 return AUTH_AUTHENTICATED;
451 }
452
453 AuthAclState
454 Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * aUR, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
455 {
456 // If we have already been called, return the cached value
457 Auth::UserRequest::Pointer t = authTryGetUser(*aUR, conn, request);
458
459 if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
460 if (*aUR == NULL)
461 *aUR = t;
462
463 if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
464 request->auth_user_request = t;
465 }
466 return t->lastReply;
467 }
468
469 // ok, call the actual authenticator routine.
470 AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr);
471
472 // auth process may have changed the UserRequest we are dealing with
473 t = authTryGetUser(*aUR, conn, request);
474
475 if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
476 t->lastReply = result;
477
478 return result;
479 }
480
481 void
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!) */
484 {
485 http_hdr_type type;
486
487 switch (rep->sline.status) {
488
489 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
490 /* Proxy authorisation needed */
491 type = HDR_PROXY_AUTHENTICATE;
492 break;
493
494 case HTTP_UNAUTHORIZED:
495 /* WWW Authorisation needed */
496 type = HDR_WWW_AUTHENTICATE;
497 break;
498
499 default:
500 /* Keep GCC happy */
501 /* some other HTTP status */
502 type = HDR_ENUM_END;
503 break;
504 }
505
506 debugs(29, 9, HERE << "headertype:" << type << " authuser:" << auth_user_request);
507
508 if (((rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
509 || (rep->sline.status == HTTP_UNAUTHORIZED)) && internal)
510 /* this is a authenticate-needed response */
511 {
512
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);
516 else {
517 /* call each configured & running authscheme */
518
519 for (Auth::ConfigVector::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i) {
520 Auth::Config *scheme = *i;
521
522 if (scheme->active())
523 scheme->fixHeader(NULL, rep, type, request);
524 else
525 debugs(29, 4, HERE << "Configured scheme " << scheme->type() << " not Active");
526 }
527 }
528
529 }
530
531 /*
532 * allow protocol specific headers to be _added_ to the existing
533 * response - currently Digest or Negotiate auth
534 */
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;
539 }
540 }
541
542 // TODO remove wrapper.
543 void
544 authenticateFixHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
545 {
546 Auth::UserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal);
547 }
548
549 /* call the active auth module and allow it to add a trailer to the request */
550 // TODO remove wrapper
551 void
552 authenticateAddTrailer(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated)
553 {
554 if (auth_user_request != NULL)
555 auth_user_request->addAuthenticationInfoTrailer(rep, accelerated);
556 }
557
558 Auth::Scheme::Pointer
559 Auth::UserRequest::scheme() const
560 {
561 return Auth::Scheme::Find(user()->config->type());
562 }