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