]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/negotiate/auth_negotiate.cc
Merged from trunk
[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.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 "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 AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
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 /**
98 \ingroup AuthNegotiateInternal
99 \todo move to negotiateScheme.cc
100 */
101 void
102 negotiateScheme::done()
103 {
104 /* TODO: this should be a Config call. */
105 debugs(29, 2, "negotiateScheme::done: shutting down Negotiate authentication.");
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
120 debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
121 }
122
123 void
124 AuthNegotiateConfig::done()
125 {
126 if (authenticate)
127 wordlistDestroy(&authenticate);
128 }
129
130 void
131 AuthNegotiateConfig::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
147 AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(5), keep_alive(1)
148 { }
149
150 void
151 AuthNegotiateConfig::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
159 requirePathnameExists("auth_param negotiate program", authenticate->key);
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 {
165 debugs(29, 0, "AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '" << param_str << "'");
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
180 const char *
181 AuthNegotiateConfig::type() const
182 {
183 return negotiateScheme::GetInstance().type();
184 }
185
186 /**
187 * Initialize helpers and the like for this auth scheme.
188 * Called AFTER parsing the config file
189 */
190 void
191 AuthNegotiateConfig::init(AuthConfig * scheme)
192 {
193 if (authenticate) {
194
195 authnegotiate_initialised = 1;
196
197 if (negotiateauthenticators == NULL)
198 negotiateauthenticators = helperStatefulCreate("negotiateauthenticator");
199
200 if (!proxy_auth_cache)
201 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
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
213 CBDATA_INIT_TYPE(authenticateStateData);
214 }
215 }
216
217 void
218 AuthNegotiateConfig::registerWithCacheManager(void)
219 {
220 CacheManager::GetInstance()->
221 registerAction("negotiateauthenticator",
222 "Negotiate User Authenticator Stats",
223 authenticateNegotiateStats, 0, 1);
224 }
225
226 bool
227 AuthNegotiateConfig::active() const
228 {
229 return authnegotiate_initialised == 1;
230 }
231
232 bool
233 AuthNegotiateConfig::configured() const
234 {
235 if ((authenticate != NULL) && (authenticateChildren != 0)) {
236 debugs(29, 9, "AuthNegotiateConfig::configured: returning configured");
237 return true;
238 }
239
240 debugs(29, 9, "AuthNegotiateConfig::configured: returning unconfigured");
241 return false;
242 }
243
244 /* Negotiate Scheme */
245 /* See AuthUserRequest.cc::authenticateDirection for return values */
246 int
247 AuthNegotiateUserRequest::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:
259 debugs(29, 1, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev.");
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
270 case AUTHENTICATE_STATE_DONE:
271 return 0; /* do nothing */
272
273 case AUTHENTICATE_STATE_INITIAL:
274 debugs(29, 1, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
275 return -2;
276 }
277
278 return -2;
279 }
280
281 /* add the [proxy]authorisation header */
282 void
283 AuthNegotiateUserRequest::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
303 void
304 AuthNegotiateConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
305 {
306 AuthNegotiateUserRequest *negotiate_request;
307
308 if (!authenticate)
309 return;
310
311 /* Need keep-alive */
312 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
313 return;
314
315 /* New request, no user details */
316 if (auth_user_request == NULL) {
317 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << type << " header: 'Negotiate'");
318 httpHeaderPutStrf(&rep->header, type, "Negotiate");
319
320 if (!keep_alive) {
321 /* drop the connection */
322 rep->header.delByName("keep-alive");
323 request->flags.proxy_keepalive = 0;
324 }
325 } else {
326 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
327
328 assert(negotiate_request != NULL);
329
330 switch (negotiate_request->auth_state) {
331
332 case AUTHENTICATE_STATE_FAILED:
333 /* here it makes sense to drop the connection, as auth is
334 * tied to it, even if MAYBE the client could handle it - Kinkie */
335 rep->header.delByName("keep-alive");
336 request->flags.proxy_keepalive = 0;
337 /* fall through */
338
339 case AUTHENTICATE_STATE_DONE:
340 /* Special case: authentication finished OK but disallowed by ACL.
341 * Need to start over to give the client another chance.
342 */
343
344 if (negotiate_request->server_blob) {
345 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << type << " header: 'Negotiate " << negotiate_request->server_blob << "'");
346 httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
347 safe_free(negotiate_request->server_blob);
348 } else {
349 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
350 httpHeaderPutStrf(&rep->header, type, "Negotiate");
351 }
352
353 break;
354
355 case AUTHENTICATE_STATE_NONE:
356 /* semantic change: do not drop the connection.
357 * 2.5 implementation used to keep it open - Kinkie */
358 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << type << " header: 'Negotiate'");
359 httpHeaderPutStrf(&rep->header, type, "Negotiate");
360 break;
361
362 case AUTHENTICATE_STATE_IN_PROGRESS:
363 /* we're waiting for a response from the client. Pass it the blob */
364 debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << type << " header: 'Negotiate " << negotiate_request->server_blob << "'");
365 httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
366 safe_free(negotiate_request->server_blob);
367 break;
368
369
370 default:
371 debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
372 fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
373 }
374 }
375 }
376
377 NegotiateUser::~NegotiateUser()
378 {
379 debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
380 }
381
382 static stateful_helper_callback_t
383 authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
384 {
385 authenticateStateData *r = static_cast<authenticateStateData *>(data);
386
387 int valid;
388 stateful_helper_callback_t result = S_HELPER_UNKNOWN;
389 char *blob, *arg = NULL;
390
391 AuthUserRequest *auth_user_request;
392 AuthUser *auth_user;
393 NegotiateUser *negotiate_user;
394 AuthNegotiateUserRequest *negotiate_request;
395
396 debugs(29, 8, "authenticateNegotiateHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
397 valid = cbdataReferenceValid(r->data);
398
399 if (!valid) {
400 debugs(29, 1, "authenticateNegotiateHandleReply: invalid callback data. Releasing helper '" << lastserver << "'.");
401 cbdataReferenceDone(r->data);
402 authenticateStateFree(r);
403 debugs(29, 9, "authenticateNegotiateHandleReply: telling stateful helper : " << S_HELPER_RELEASE);
404 return S_HELPER_RELEASE;
405 }
406
407 if (!reply) {
408 debugs(29, 1, "authenticateNegotiateHandleReply: Helper '" << lastserver << "' crashed!.");
409 reply = (char *)"BH Internal error";
410 }
411
412 auth_user_request = r->auth_user_request;
413 assert(auth_user_request != NULL);
414 negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
415
416 assert(negotiate_request != NULL);
417 assert(negotiate_request->waiting);
418 negotiate_request->waiting = 0;
419 safe_free(negotiate_request->client_blob);
420
421 auth_user = negotiate_request->user();
422 assert(auth_user != NULL);
423 assert(auth_user->auth_type == AUTH_NEGOTIATE);
424 negotiate_user = dynamic_cast<negotiate_user_t *>(auth_user_request->user());
425
426 assert(negotiate_user != NULL);
427
428 if (negotiate_request->authserver == NULL)
429 negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
430 else
431 assert(negotiate_request->authserver == lastserver);
432
433 /* seperate out the useful data */
434 blob = strchr(reply, ' ');
435
436 if (blob) {
437 blob++;
438 arg = strchr(blob + 1, ' ');
439 } else {
440 arg = NULL;
441 }
442
443 if (strncasecmp(reply, "TT ", 3) == 0) {
444 /* we have been given a blob to send to the client */
445 if (arg)
446 *arg++ = '\0';
447 safe_free(negotiate_request->server_blob);
448 negotiate_request->request->flags.must_keepalive = 1;
449 if (negotiate_request->request->flags.proxy_keepalive) {
450 negotiate_request->server_blob = xstrdup(blob);
451 negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
452 auth_user_request->denyMessage("Authentication in progress");
453 debugs(29, 4, "authenticateNegotiateHandleReply: Need to challenge the client with a server blob '" << blob << "'");
454 result = S_HELPER_RESERVE;
455 } else {
456 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
457 auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
458 result = S_HELPER_RELEASE;
459 }
460 } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
461 /* we're finished, release the helper */
462
463 if (arg)
464 *arg++ = '\0';
465
466 negotiate_user->username(arg);
467
468 auth_user_request->denyMessage("Login successful");
469
470 safe_free(negotiate_request->server_blob);
471
472 negotiate_request->server_blob = xstrdup(blob);
473
474 negotiate_request->releaseAuthServer();
475
476 negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
477
478 result = S_HELPER_RELEASE;
479
480 debugs(29, 4, "authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '" << blob << "'");
481
482 /* connection is authenticated */
483 debugs(29, 4, "AuthNegotiateUserRequest::authenticate: authenticated user " << negotiate_user->username());
484 /* see if this is an existing user with a different proxy_auth
485 * string */
486 AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
487 AuthUser *local_auth_user = negotiate_request->user();
488 while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0))
489 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
490 if (usernamehash) {
491 /* we can't seamlessly recheck the username due to the
492 * challenge-response nature of the protocol.
493 * Just free the temporary auth_user */
494 usernamehash->user()->absorb(local_auth_user);
495 //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
496 local_auth_user = usernamehash->user();
497 negotiate_request->_auth_user = local_auth_user;
498 } else {
499 /* store user in hash's */
500 local_auth_user->addToNameCache();
501 // authenticateUserNameCacheAdd(local_auth_user);
502 }
503 /* set these to now because this is either a new login from an
504 * existing user or a new user */
505 local_auth_user->expiretime = current_time.tv_sec;
506 negotiate_request->releaseAuthServer();
507 negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
508
509 } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
510 /* authentication failure (wrong password, etc.) */
511
512 if (arg)
513 *arg++ = '\0';
514
515 auth_user_request->denyMessage(arg);
516
517 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
518
519 safe_free(negotiate_request->server_blob);
520
521 negotiate_request->server_blob = xstrdup(blob);
522
523 negotiate_request->releaseAuthServer();
524
525 result = S_HELPER_RELEASE;
526
527 debugs(29, 4, "authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '" << blob << "'");
528 } else if (strncasecmp(reply, "BH ", 3) == 0) {
529 /* TODO kick off a refresh process. This can occur after a YR or after
530 * a KK. If after a YR release the helper and resubmit the request via
531 * Authenticate Negotiate start.
532 * If after a KK deny the user's request w/ 407 and mark the helper as
533 * Needing YR. */
534 auth_user_request->denyMessage(blob);
535 negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
536 safe_free(negotiate_request->server_blob);
537 negotiate_request->releaseAuthServer();
538 result = S_HELPER_RELEASE;
539 debugs(29, 1, "authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '" << reply << "'");
540 } else {
541 /* protocol error */
542 fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
543 }
544
545 if (negotiate_request->request) {
546 HTTPMSGUNLOCK(negotiate_request->request);
547 negotiate_request->request = NULL;
548 }
549 r->handler(r->data, NULL);
550 cbdataReferenceDone(r->data);
551 authenticateStateFree(r);
552 debugs(29, 9, "authenticateNegotiateHandleReply: telling stateful helper : " << result);
553 return result;
554 }
555
556 static void
557 authenticateNegotiateStats(StoreEntry * sentry)
558 {
559 helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
560 }
561
562
563 /** send the initial data to a stateful negotiate authenticator module */
564 void
565 AuthNegotiateUserRequest::module_start(RH * handler, void *data)
566 {
567 authenticateStateData *r = NULL;
568 static char buf[MAX_AUTHTOKEN_LEN];
569 negotiate_user_t *negotiate_user;
570 AuthUser *auth_user = user();
571
572 assert(data);
573 assert(handler);
574 assert(auth_user);
575 assert(auth_user->auth_type == AUTH_NEGOTIATE);
576
577 negotiate_user = dynamic_cast<negotiate_user_t *>(user());
578
579 debugs(29, 8, "AuthNegotiateUserRequest::module_start: auth state is '" << auth_state << "'");
580
581 if (negotiateConfig.authenticate == NULL) {
582 debugs(29, 0, "AuthNegotiateUserRequest::module_start: no Negotiate program specified.");
583 handler(data, NULL);
584 return;
585 }
586
587 r = cbdataAlloc(authenticateStateData);
588 r->handler = handler;
589 r->data = cbdataReference(data);
590 r->auth_user_request = this;
591 AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
592
593 if (auth_state == AUTHENTICATE_STATE_INITIAL) {
594 snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
595 } else {
596 snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
597 }
598
599 waiting = 1;
600
601 safe_free(client_blob);
602 helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
603 }
604
605 /**
606 * Atomic action: properly release the Negotiate auth helpers which may have been reserved
607 * for this request connections use.
608 */
609 void
610 AuthNegotiateUserRequest::releaseAuthServer()
611 {
612 if (authserver) {
613 debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
614 helperStatefulReleaseServer(authserver);
615 authserver = NULL;
616 }
617 else
618 debugs(29, 6, HERE << "No Negotiate auth server to release.");
619 }
620
621 /* clear any connection related authentication details */
622 void
623 AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
624 {
625 assert(conn != NULL);
626
627 debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
628
629 if (conn->auth_user_request == NULL) {
630 debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
631 return;
632 }
633
634 releaseAuthServer();
635
636 /* unlock the connection based lock */
637 debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
638
639 AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
640 }
641
642 /*
643 * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
644 * Auth_user structure.
645 */
646 AuthUserRequest *
647 AuthNegotiateConfig::decode(char const *proxy_auth)
648 {
649 NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
650 AuthNegotiateUserRequest *auth_user_request = new AuthNegotiateUserRequest ();
651 assert(auth_user_request->user() == NULL);
652 auth_user_request->user(newUser);
653 auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
654 auth_user_request->user()->addRequest(auth_user_request);
655
656 /* all we have to do is identify that it's Negotiate - the helper does the rest */
657 debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
658 return auth_user_request;
659 }
660
661 int
662 AuthNegotiateUserRequest::authenticated() const
663 {
664 if (auth_state == AUTHENTICATE_STATE_DONE) {
665 debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated.");
666 return 1;
667 }
668
669 debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated.");
670
671 return 0;
672 }
673
674 void
675 AuthNegotiateUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
676 {
677 const char *proxy_auth, *blob;
678
679 /** \todo rename this!! */
680 AuthUser *local_auth_user;
681 negotiate_user_t *negotiate_user;
682
683 local_auth_user = user();
684 assert(local_auth_user);
685 assert(local_auth_user->auth_type == AUTH_NEGOTIATE);
686 negotiate_user = dynamic_cast<negotiate_user_t *>(local_auth_user);
687 assert (this);
688
689 /** Check that we are in the client side, where we can generate
690 * auth challenges */
691
692 if (conn == NULL) {
693 auth_state = AUTHENTICATE_STATE_FAILED;
694 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!");
695 return;
696 }
697
698 if (waiting) {
699 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: waiting for helper reply!");
700 return;
701 }
702
703 if (server_blob) {
704 debugs(29, 2, "AuthNegotiateUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
705 return;
706 }
707
708 /* get header */
709 proxy_auth = request->header.getStr(type);
710
711 /* locate second word */
712 blob = proxy_auth;
713
714 if (blob) {
715 while (xisspace(*blob) && *blob)
716 blob++;
717
718 while (!xisspace(*blob) && *blob)
719 blob++;
720
721 while (xisspace(*blob) && *blob)
722 blob++;
723 }
724
725 switch (auth_state) {
726
727 case AUTHENTICATE_STATE_NONE:
728 /* we've received a negotiate request. pass to a helper */
729 debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '" << proxy_auth << "'");
730 auth_state = AUTHENTICATE_STATE_INITIAL;
731 safe_free(client_blob);
732 client_blob=xstrdup(blob);
733 conn->auth_type = AUTH_NEGOTIATE;
734 assert(conn->auth_user_request == NULL);
735 conn->auth_user_request = this;
736 AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
737 this->request = request;
738 HTTPMSGLOCK(this->request);
739 return;
740
741 break;
742
743 case AUTHENTICATE_STATE_INITIAL:
744 debugs(29, 1, "AuthNegotiateUserRequest::authenticate: need to ask helper");
745
746 return;
747
748 break;
749
750
751 case AUTHENTICATE_STATE_IN_PROGRESS:
752 /* we should have received a blob from the client. Hand it off to
753 * some helper */
754 safe_free(client_blob);
755
756 client_blob = xstrdup (blob);
757
758 if (this->request)
759 HTTPMSGUNLOCK(this->request);
760 this->request = request;
761 HTTPMSGLOCK(this->request);
762 return;
763
764 break;
765
766 case AUTHENTICATE_STATE_DONE:
767 fatal("AuthNegotiateUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
768
769 break;
770
771 case AUTHENTICATE_STATE_FAILED:
772 /* we've failed somewhere in authentication */
773 debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate failed. " << proxy_auth);
774
775 return;
776
777 break;
778 }
779
780 return;
781 }
782
783 AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
784 /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
785 _theUser(NULL)
786 {
787 waiting=0;
788 client_blob=0;
789 server_blob=0;
790 authserver=NULL;
791 request=NULL;
792 }
793
794 AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
795 {
796 safe_free(server_blob);
797 safe_free(client_blob);
798
799 if (authserver != NULL) {
800 debugs(29, 9, "AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '" << authserver << "'");
801 helperStatefulReleaseServer(authserver);
802 authserver = NULL;
803 }
804 if (request) {
805 HTTPMSGUNLOCK(request);
806 request = NULL;
807 }
808 }
809
810 void
811 NegotiateUser::deleteSelf() const
812 {
813 delete this;
814 }
815
816 NegotiateUser::NegotiateUser (AuthConfig *config) : AuthUser (config)
817 {
818 proxy_auth_list.head = proxy_auth_list.tail = NULL;
819 }
820
821 AuthConfig *
822 negotiateScheme::createConfig()
823 {
824 return &negotiateConfig;
825 }
826
827 const char *
828 AuthNegotiateUserRequest::connLastHeader()
829 {
830 return NULL;
831 }
832