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