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