]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/negotiate/auth_negotiate.cc
Remove redundant user() functions
[thirdparty/squid.git] / src / auth / negotiate / auth_negotiate.cc
CommitLineData
6bf4f823 1/*
262a0e14 2 * $Id$
6bf4f823 3 *
4 * DEBUG: section 29 Negotiate Authenticator
5 * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
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 *
6bf4f823 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 *
6bf4f823 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 */
34
35/* The functions in this file handle authentication.
36 * They DO NOT perform access control or auditing.
37 * See acl.c for access control and client_side.c for auditing */
38
39
40#include "squid.h"
41#include "auth_negotiate.h"
3ad63615 42#include "auth/Gadgets.h"
62ee09ca 43#include "CacheManager.h"
6bf4f823 44#include "Store.h"
45#include "client_side.h"
46#include "HttpReply.h"
47#include "HttpRequest.h"
cc192b50 48#include "SquidTime.h"
63be0a78 49/** \todo remove this include */
6bf4f823 50#include "negotiateScheme.h"
d295d770 51#include "wordlist.h"
6bf4f823 52
63be0a78 53/**
54 \defgroup AuthNegotiateInternal Negotiate Authenticator Internals
55 \ingroup AuthNegotiateAPI
56 */
57
1f16a8cc
AJ
58/**
59 * Maximum length (buffer size) for token strings.
60 */
94fd8c3a 61// AYJ: must match re-definition in helpers/negotiate_auth/squid_kerb_auth/squid_kerb_auth.c
1f16a8cc
AJ
62#define MAX_AUTHTOKEN_LEN 32768
63
6bf4f823 64
63be0a78 65/// \ingroup AuthNegotiateInternal
6bf4f823 66static void
67authenticateStateFree(authenticateStateData * r)
68{
a33a428a 69 r->auth_user_request = NULL;
6bf4f823 70 cbdataFree(r);
71}
72
73/* Negotiate Scheme */
74static HLPSCB authenticateNegotiateHandleReply;
75static AUTHSSTATS authenticateNegotiateStats;
76
63be0a78 77/// \ingroup AuthNegotiateInternal
6bf4f823 78static statefulhelper *negotiateauthenticators = NULL;
79
80CBDATA_TYPE(authenticateStateData);
81
63be0a78 82/// \ingroup AuthNegotiateInternal
6bf4f823 83static int authnegotiate_initialised = 0;
84
63be0a78 85/// \ingroup AuthNegotiateInternal
6bf4f823 86static auth_negotiate_config negotiateConfig;
87
63be0a78 88/// \ingroup AuthNegotiateInternal
6bf4f823 89static hash_table *proxy_auth_cache = NULL;
90
91/*
92 *
93 * Private Functions
94 *
95 */
96
63be0a78 97/**
98 \ingroup AuthNegotiateInternal
99 \todo move to negotiateScheme.cc
100 */
6bf4f823 101void
102negotiateScheme::done()
103{
104 /* TODO: this should be a Config call. */
bf8fe701 105 debugs(29, 2, "negotiateScheme::done: shutting down Negotiate authentication.");
6bf4f823 106
107 if (negotiateauthenticators)
108 helperStatefulShutdown(negotiateauthenticators);
109
110 authnegotiate_initialised = 0;
111
112 if (!shutting_down)
113 return;
114
115 if (negotiateauthenticators)
116 helperStatefulFree(negotiateauthenticators);
117
118 negotiateauthenticators = NULL;
119
bf8fe701 120 debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
6bf4f823 121}
122
6bf4f823 123void
124AuthNegotiateConfig::done()
125{
126 if (authenticate)
127 wordlistDestroy(&authenticate);
128}
129
130void
131AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
132{
133 wordlist *list = authenticate;
134 storeAppendPrintf(entry, "%s %s", name, "negotiate");
135
136 while (list != NULL) {
137 storeAppendPrintf(entry, " %s", list->key);
138 list = list->next;
139 }
140
141 storeAppendPrintf(entry, "\n%s negotiate children %d\n",
142 name, authenticateChildren);
143 storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off");
144
145}
146
147AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(5), keep_alive(1)
148{ }
149
150void
151AuthNegotiateConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
152{
153 if (strcasecmp(param_str, "program") == 0) {
154 if (authenticate)
155 wordlistDestroy(&authenticate);
156
157 parse_wordlist(&authenticate);
158
42900318 159 requirePathnameExists("auth_param negotiate program", authenticate->key);
6bf4f823 160 } else if (strcasecmp(param_str, "children") == 0) {
161 parse_int(&authenticateChildren);
162 } else if (strcasecmp(param_str, "keep_alive") == 0) {
163 parse_onoff(&keep_alive);
164 } else {
bf8fe701 165 debugs(29, 0, "AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '" << param_str << "'");
6bf4f823 166 }
167
168 /*
169 * disable client side request pipelining. There is a race with
170 * Negotiate when the client sends a second request on an Negotiate
171 * connection before the authenticate challenge is sent. With
172 * this patch, the client may fail to authenticate, but squid's
173 * state will be preserved. Caveats: this should be a post-parse
174 * test, but that can wait for the modular parser to be integrated.
175 */
176 if (authenticate)
177 Config.onoff.pipeline_prefetch = 0;
178}
179
180const char *
181AuthNegotiateConfig::type() const
182{
183 return negotiateScheme::GetInstance().type();
184}
185
63be0a78 186/**
187 * Initialize helpers and the like for this auth scheme.
188 * Called AFTER parsing the config file
189 */
6bf4f823 190void
191AuthNegotiateConfig::init(AuthConfig * scheme)
192{
6bf4f823 193 if (authenticate) {
81425fb6 194
6bf4f823 195 authnegotiate_initialised = 1;
196
197 if (negotiateauthenticators == NULL)
198 negotiateauthenticators = helperStatefulCreate("negotiateauthenticator");
199
200 if (!proxy_auth_cache)
30abd221 201 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
6bf4f823 202
203 assert(proxy_auth_cache);
204
205 negotiateauthenticators->cmdline = authenticate;
206
207 negotiateauthenticators->n_to_start = authenticateChildren;
208
209 negotiateauthenticators->ipc_type = IPC_STREAM;
210
211 helperStatefulOpenServers(negotiateauthenticators);
212
6bf4f823 213 CBDATA_INIT_TYPE(authenticateStateData);
214 }
215}
216
62ee09ca 217void
15fab853 218AuthNegotiateConfig::registerWithCacheManager(void)
62ee09ca 219{
15fab853 220 CacheManager::GetInstance()->
26ac0430
AJ
221 registerAction("negotiateauthenticator",
222 "Negotiate User Authenticator Stats",
223 authenticateNegotiateStats, 0, 1);
62ee09ca 224}
225
6bf4f823 226bool
227AuthNegotiateConfig::active() const
228{
229 return authnegotiate_initialised == 1;
230}
231
232bool
233AuthNegotiateConfig::configured() const
234{
235 if ((authenticate != NULL) && (authenticateChildren != 0)) {
bf8fe701 236 debugs(29, 9, "AuthNegotiateConfig::configured: returning configured");
6bf4f823 237 return true;
238 }
239
bf8fe701 240 debugs(29, 9, "AuthNegotiateConfig::configured: returning unconfigured");
6bf4f823 241 return false;
242}
243
244/* Negotiate Scheme */
245/* See AuthUserRequest.cc::authenticateDirection for return values */
246int
247AuthNegotiateUserRequest::module_direction()
248{
249 /* null auth_user is checked for by authenticateDirection */
250
251 if (waiting || client_blob)
252 return -1; /* need helper response to continue */
253
254 switch (auth_state) {
255
256 /* no progress at all. */
257
258 case AUTHENTICATE_STATE_NONE:
bf8fe701 259 debugs(29, 1, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev.");
6bf4f823 260 return -2; /* error */
261
262 case AUTHENTICATE_STATE_FAILED:
263 return -2; /* error */
264
265
266 case AUTHENTICATE_STATE_IN_PROGRESS:
267 assert(server_blob);
268 return 1; /* send to client */
269
6bf4f823 270 case AUTHENTICATE_STATE_DONE:
271 return 0; /* do nothing */
272
273 case AUTHENTICATE_STATE_INITIAL:
bf8fe701 274 debugs(29, 1, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
6bf4f823 275 return -2;
276 }
277
278 return -2;
279}
280
281/* add the [proxy]authorisation header */
282void
283AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
284{
285 http_hdr_type type;
286
287 if (!server_blob)
288 return;
289
290 /* don't add to authentication error pages */
291
292 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
293 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
294 return;
295
296 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
297
298 httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
299
300 safe_free(server_blob);
301}
302
303void
a33a428a 304AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
6bf4f823 305{
306 AuthNegotiateUserRequest *negotiate_request;
307
6bf4f823 308 if (!authenticate)
309 return;
310
81425fb6 311 /* Need keep-alive */
312 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
26ac0430 313 return;
81425fb6 314
6bf4f823 315 /* New request, no user details */
316 if (auth_user_request == NULL) {
076df709
FC
317 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
318 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
6bf4f823 319
320 if (!keep_alive) {
321 /* drop the connection */
a9925b40 322 rep->header.delByName("keep-alive");
6bf4f823 323 request->flags.proxy_keepalive = 0;
324 }
325 } else {
a33a428a 326 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
27da7c21 327 assert(negotiate_request != NULL);
328
6bf4f823 329 switch (negotiate_request->auth_state) {
330
331 case AUTHENTICATE_STATE_FAILED:
332 /* here it makes sense to drop the connection, as auth is
333 * tied to it, even if MAYBE the client could handle it - Kinkie */
a9925b40 334 rep->header.delByName("keep-alive");
6bf4f823 335 request->flags.proxy_keepalive = 0;
336 /* fall through */
337
ebd65a33 338 case AUTHENTICATE_STATE_DONE:
6bf4f823 339 /* Special case: authentication finished OK but disallowed by ACL.
340 * Need to start over to give the client another chance.
341 */
342
343 if (negotiate_request->server_blob) {
076df709
FC
344 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
345 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
6bf4f823 346 safe_free(negotiate_request->server_blob);
347 } else {
bf8fe701 348 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
076df709 349 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
6bf4f823 350 }
351
352 break;
353
354 case AUTHENTICATE_STATE_NONE:
355 /* semantic change: do not drop the connection.
356 * 2.5 implementation used to keep it open - Kinkie */
076df709
FC
357 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
358 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
6bf4f823 359 break;
360
361 case AUTHENTICATE_STATE_IN_PROGRESS:
362 /* we're waiting for a response from the client. Pass it the blob */
076df709
FC
363 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
364 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
6bf4f823 365 safe_free(negotiate_request->server_blob);
366 break;
367
368
369 default:
bf8fe701 370 debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
6bf4f823 371 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
372 }
373 }
374}
375
376NegotiateUser::~NegotiateUser()
377{
81425fb6 378 debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
6bf4f823 379}
380
dcb802aa 381static void
6bf4f823 382authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
383{
384 authenticateStateData *r = static_cast<authenticateStateData *>(data);
385
386 int valid;
6bf4f823 387 char *blob, *arg = NULL;
388
6bf4f823 389 AuthUser *auth_user;
390 NegotiateUser *negotiate_user;
391 AuthNegotiateUserRequest *negotiate_request;
392
bf8fe701 393 debugs(29, 8, "authenticateNegotiateHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
a6151f51 394 valid = cbdataReferenceValid(r->data);
6bf4f823 395
396 if (!valid) {
dcb802aa 397 debugs(29, 1, "authenticateNegotiateHandleReply: invalid callback data. helper '" << lastserver << "'.");
6bf4f823 398 cbdataReferenceDone(r->data);
399 authenticateStateFree(r);
dcb802aa 400 return;
6bf4f823 401 }
402
403 if (!reply) {
bf8fe701 404 debugs(29, 1, "authenticateNegotiateHandleReply: Helper '" << lastserver << "' crashed!.");
c9c40182 405 reply = (char *)"BH Internal error";
6bf4f823 406 }
407
a33a428a 408 AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
6bf4f823 409 assert(auth_user_request != NULL);
6bf4f823 410
a33a428a 411 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
27da7c21 412 assert(negotiate_request != NULL);
a33a428a 413
6bf4f823 414 assert(negotiate_request->waiting);
415 negotiate_request->waiting = 0;
416 safe_free(negotiate_request->client_blob);
417
a33a428a 418 auth_user = auth_user_request->user();
6bf4f823 419 assert(auth_user != NULL);
420 assert(auth_user->auth_type == AUTH_NEGOTIATE);
6bf4f823 421
a33a428a 422 negotiate_user = dynamic_cast<negotiate_user_t *>(auth_user_request->user());
27da7c21 423 assert(negotiate_user != NULL);
424
6bf4f823 425 if (negotiate_request->authserver == NULL)
426 negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
427 else
428 assert(negotiate_request->authserver == lastserver);
429
430 /* seperate out the useful data */
431 blob = strchr(reply, ' ');
432
c9c40182 433 if (blob) {
6bf4f823 434 blob++;
435 arg = strchr(blob + 1, ' ');
c9c40182 436 } else {
437 arg = NULL;
6bf4f823 438 }
439
c9c40182 440 if (strncasecmp(reply, "TT ", 3) == 0) {
6bf4f823 441 /* we have been given a blob to send to the client */
6bf4f823 442 if (arg)
443 *arg++ = '\0';
6bf4f823 444 safe_free(negotiate_request->server_blob);
26ac0430
AJ
445 negotiate_request->request->flags.must_keepalive = 1;
446 if (negotiate_request->request->flags.proxy_keepalive) {
447 negotiate_request->server_blob = xstrdup(blob);
448 negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
449 auth_user_request->denyMessage("Authentication in progress");
450 debugs(29, 4, "authenticateNegotiateHandleReply: Need to challenge the client with a server blob '" << blob << "'");
26ac0430
AJ
451 } else {
452 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
453 auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
26ac0430 454 }
c9c40182 455 } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
6bf4f823 456 /* we're finished, release the helper */
457
458 if (arg)
459 *arg++ = '\0';
460
461 negotiate_user->username(arg);
462
463 auth_user_request->denyMessage("Login successful");
464
465 safe_free(negotiate_request->server_blob);
466
467 negotiate_request->server_blob = xstrdup(blob);
468
d945258c 469 negotiate_request->releaseAuthServer();
6bf4f823 470
71ee0c43 471 negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
6bf4f823 472
81425fb6 473 debugs(29, 4, "authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '" << blob << "'");
71ee0c43 474
475 /* connection is authenticated */
476 debugs(29, 4, "AuthNegotiateUserRequest::authenticate: authenticated user " << negotiate_user->username());
477 /* see if this is an existing user with a different proxy_auth
478 * string */
e1f7507e 479 AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
26ac0430 480 AuthUser *local_auth_user = negotiate_request->user();
71ee0c43 481 while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0))
482 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
483 if (usernamehash) {
484 /* we can't seamlessly recheck the username due to the
485 * challenge-response nature of the protocol.
486 * Just free the temporary auth_user */
487 usernamehash->user()->absorb(local_auth_user);
488 //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
489 local_auth_user = usernamehash->user();
490 negotiate_request->_auth_user = local_auth_user;
491 } else {
492 /* store user in hash's */
493 local_auth_user->addToNameCache();
494 // authenticateUserNameCacheAdd(local_auth_user);
495 }
496 /* set these to now because this is either a new login from an
497 * existing user or a new user */
498 local_auth_user->expiretime = current_time.tv_sec;
d945258c 499 negotiate_request->releaseAuthServer();
26ac0430 500 negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
71ee0c43 501
c9c40182 502 } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
6bf4f823 503 /* authentication failure (wrong password, etc.) */
504
505 if (arg)
506 *arg++ = '\0';
507
508 auth_user_request->denyMessage(arg);
509
510 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
511
512 safe_free(negotiate_request->server_blob);
513
514 negotiate_request->server_blob = xstrdup(blob);
515
d945258c 516 negotiate_request->releaseAuthServer();
6bf4f823 517
81425fb6 518 debugs(29, 4, "authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '" << blob << "'");
6bf4f823 519 } else if (strncasecmp(reply, "BH ", 3) == 0) {
520 /* TODO kick off a refresh process. This can occur after a YR or after
521 * a KK. If after a YR release the helper and resubmit the request via
81425fb6 522 * Authenticate Negotiate start.
6bf4f823 523 * If after a KK deny the user's request w/ 407 and mark the helper as
524 * Needing YR. */
525 auth_user_request->denyMessage(blob);
526 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
527 safe_free(negotiate_request->server_blob);
d945258c 528 negotiate_request->releaseAuthServer();
81425fb6 529 debugs(29, 1, "authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '" << reply << "'");
6bf4f823 530 } else {
531 /* protocol error */
532 fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
533 }
534
a33a428a 535 negotiate_request->request = NULL;
6bf4f823 536 r->handler(r->data, NULL);
537 cbdataReferenceDone(r->data);
538 authenticateStateFree(r);
6bf4f823 539}
540
541static void
542authenticateNegotiateStats(StoreEntry * sentry)
543{
81425fb6 544 helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
6bf4f823 545}
546
547
e1f7507e 548/** send the initial data to a stateful negotiate authenticator module */
6bf4f823 549void
550AuthNegotiateUserRequest::module_start(RH * handler, void *data)
551{
552 authenticateStateData *r = NULL;
1f16a8cc 553 static char buf[MAX_AUTHTOKEN_LEN];
6bf4f823 554 negotiate_user_t *negotiate_user;
e1f7507e 555 AuthUser *auth_user = user();
6bf4f823 556
557 assert(data);
558 assert(handler);
559 assert(auth_user);
560 assert(auth_user->auth_type == AUTH_NEGOTIATE);
561
562 negotiate_user = dynamic_cast<negotiate_user_t *>(user());
563
bf8fe701 564 debugs(29, 8, "AuthNegotiateUserRequest::module_start: auth state is '" << auth_state << "'");
6bf4f823 565
566 if (negotiateConfig.authenticate == NULL) {
81425fb6 567 debugs(29, 0, "AuthNegotiateUserRequest::module_start: no Negotiate program specified.");
6bf4f823 568 handler(data, NULL);
569 return;
570 }
571
572 r = cbdataAlloc(authenticateStateData);
573 r->handler = handler;
1d412b78 574 r->data = cbdataReference(data);
81425fb6 575 r->auth_user_request = this;
81425fb6 576
6bf4f823 577 if (auth_state == AUTHENTICATE_STATE_INITIAL) {
1f16a8cc 578 snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
6bf4f823 579 } else {
1f16a8cc 580 snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
6bf4f823 581 }
582
583 waiting = 1;
584
585 safe_free(client_blob);
586 helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
587}
588
d945258c
AJ
589/**
590 * Atomic action: properly release the Negotiate auth helpers which may have been reserved
591 * for this request connections use.
592 */
593void
594AuthNegotiateUserRequest::releaseAuthServer()
6bf4f823 595{
d945258c
AJ
596 if (authserver) {
597 debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
598 helperStatefulReleaseServer(authserver);
599 authserver = NULL;
e1381638 600 } else
d945258c 601 debugs(29, 6, HERE << "No Negotiate auth server to release.");
6bf4f823 602}
603
604/* clear any connection related authentication details */
605void
4f0ef8e8 606AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
6bf4f823 607{
4f0ef8e8 608 assert(conn != NULL);
6bf4f823 609
4f0ef8e8 610 debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
6bf4f823 611
4f0ef8e8 612 if (conn->auth_user_request == NULL) {
bf8fe701 613 debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
6bf4f823 614 return;
615 }
616
d945258c 617 releaseAuthServer();
6bf4f823 618
619 /* unlock the connection based lock */
4f0ef8e8 620 debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
81425fb6 621
a33a428a 622 conn->auth_user_request = NULL;
6bf4f823 623}
624
625/*
81425fb6 626 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
6bf4f823 627 * Auth_user structure.
628 */
a33a428a 629AuthUserRequest::Pointer
6bf4f823 630AuthNegotiateConfig::decode(char const *proxy_auth)
631{
632 NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
a33a428a 633 AuthUserRequest *auth_user_request = new AuthNegotiateUserRequest();
6bf4f823 634 assert(auth_user_request->user() == NULL);
a33a428a 635
6bf4f823 636 auth_user_request->user(newUser);
637 auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
638 auth_user_request->user()->addRequest(auth_user_request);
639
81425fb6 640 /* all we have to do is identify that it's Negotiate - the helper does the rest */
641 debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
6bf4f823 642 return auth_user_request;
643}
644
645int
646AuthNegotiateUserRequest::authenticated() const
647{
c9acda3a 648 if (auth_state == AUTHENTICATE_STATE_DONE) {
bf8fe701 649 debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated.");
6bf4f823 650 return 1;
651 }
652
bf8fe701 653 debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated.");
6bf4f823 654
655 return 0;
656}
657
658void
e4ae841b 659AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
6bf4f823 660{
661 const char *proxy_auth, *blob;
662
e1f7507e
AJ
663 /** \todo rename this!! */
664 AuthUser *local_auth_user;
6bf4f823 665 negotiate_user_t *negotiate_user;
666
667 local_auth_user = user();
668 assert(local_auth_user);
669 assert(local_auth_user->auth_type == AUTH_NEGOTIATE);
670 negotiate_user = dynamic_cast<negotiate_user_t *>(local_auth_user);
671 assert (this);
672
e1f7507e 673 /** Check that we are in the client side, where we can generate
6bf4f823 674 * auth challenges */
675
486bf0fb 676 if (conn == NULL) {
6bf4f823 677 auth_state = AUTHENTICATE_STATE_FAILED;
bf8fe701 678 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!");
6bf4f823 679 return;
680 }
681
682 if (waiting) {
bf8fe701 683 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: waiting for helper reply!");
6bf4f823 684 return;
685 }
686
687 if (server_blob) {
bf8fe701 688 debugs(29, 2, "AuthNegotiateUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
6bf4f823 689 return;
690 }
691
692 /* get header */
e4ae841b 693 proxy_auth = aRequest->header.getStr(type);
6bf4f823 694
c9c40182 695 /* locate second word */
696 blob = proxy_auth;
697
26ac0430 698 if (blob) {
4e95a930 699 while (xisspace(*blob) && *blob)
700 blob++;
c9c40182 701
4e95a930 702 while (!xisspace(*blob) && *blob)
703 blob++;
6bf4f823 704
4e95a930 705 while (xisspace(*blob) && *blob)
706 blob++;
707 }
6bf4f823 708
709 switch (auth_state) {
710
711 case AUTHENTICATE_STATE_NONE:
2324cda2 712 /* we've received a negotiate request. pass to a helper */
bf8fe701 713 debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '" << proxy_auth << "'");
6bf4f823 714 auth_state = AUTHENTICATE_STATE_INITIAL;
715 safe_free(client_blob);
716 client_blob=xstrdup(blob);
717 conn->auth_type = AUTH_NEGOTIATE;
81425fb6 718 assert(conn->auth_user_request == NULL);
6bf4f823 719 conn->auth_user_request = this;
e4ae841b
FC
720 request = aRequest;
721 HTTPMSGLOCK(request);
6bf4f823 722 return;
723
724 break;
725
726 case AUTHENTICATE_STATE_INITIAL:
bf8fe701 727 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: need to ask helper");
6bf4f823 728
729 return;
730
731 break;
732
733
734 case AUTHENTICATE_STATE_IN_PROGRESS:
735 /* we should have received a blob from the client. Hand it off to
736 * some helper */
737 safe_free(client_blob);
738
739 client_blob = xstrdup (blob);
740
e4ae841b
FC
741 if (request)
742 HTTPMSGUNLOCK(request);
743 request = aRequest;
744 HTTPMSGLOCK(request);
6bf4f823 745 return;
746
747 break;
748
81425fb6 749 case AUTHENTICATE_STATE_DONE:
26ac0430 750 fatal("AuthNegotiateUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
81425fb6 751
26ac0430 752 break;
81425fb6 753
71ee0c43 754 case AUTHENTICATE_STATE_FAILED:
755 /* we've failed somewhere in authentication */
756 debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate failed. " << proxy_auth);
6bf4f823 757
758 return;
759
760 break;
6bf4f823 761 }
762
763 return;
764}
765
766AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
edda1244 767 /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE)
6bf4f823 768{
769 waiting=0;
770 client_blob=0;
771 server_blob=0;
772 authserver=NULL;
01413e4e 773 request=NULL;
6bf4f823 774}
775
776AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
777{
778 safe_free(server_blob);
779 safe_free(client_blob);
780
781 if (authserver != NULL) {
bf8fe701 782 debugs(29, 9, "AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '" << authserver << "'");
6bf4f823 783 helperStatefulReleaseServer(authserver);
784 authserver = NULL;
785 }
8d3b341e 786 if (request) {
26ac0430
AJ
787 HTTPMSGUNLOCK(request);
788 request = NULL;
8d3b341e 789 }
6bf4f823 790}
791
792void
793NegotiateUser::deleteSelf() const
794{
795 delete this;
796}
797
076df709 798NegotiateUser::NegotiateUser (AuthConfig *aConfig) : AuthUser (aConfig)
6bf4f823 799{
800 proxy_auth_list.head = proxy_auth_list.tail = NULL;
801}
802
803AuthConfig *
804negotiateScheme::createConfig()
805{
806 return &negotiateConfig;
807}
808
809const char *
810AuthNegotiateUserRequest::connLastHeader()
811{
812 return NULL;
813}
814