]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/ntlm/auth_ntlm.cc
Kill some leftovers from the winbind helpers
[thirdparty/squid.git] / src / auth / ntlm / auth_ntlm.cc
CommitLineData
94439e4e 1
2/*
638b2ce3 3 * $Id: auth_ntlm.cc,v 1.49 2005/10/16 16:47:02 serassio Exp $
94439e4e 4 *
5 * DEBUG: section 29 NTLM Authenticator
6 * AUTHOR: Robert Collins
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
94439e4e 9 * ----------------------------------------------------------
10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
94439e4e 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36/* The functions in this file handle authentication.
37 * They DO NOT perform access control or auditing.
38 * See acl.c for access control and client_side.c for auditing */
39
40
41#include "squid.h"
42#include "auth_ntlm.h"
e6ccf245 43#include "authenticate.h"
44#include "Store.h"
a46d2c0e 45#include "client_side.h"
924f73bc 46#include "HttpReply.h"
a2ac85d9 47#include "HttpRequest.h"
f5691f9c 48/* TODO remove this include */
49#include "ntlmScheme.h"
c78aa667 50
94439e4e 51static void
52authenticateStateFree(authenticateStateData * r)
53{
8777d90e 54 r->auth_user_request->unlock();
55 r->auth_user_request = NULL;
94439e4e 56 cbdataFree(r);
57}
58
59/* NTLM Scheme */
60static HLPSCB authenticateNTLMHandleReply;
61static HLPSCB authenticateNTLMHandleplaceholder;
94439e4e 62static AUTHSSTATS authenticateNTLMStats;
94439e4e 63
64/* helper callbacks to handle per server state data */
65static HLPSAVAIL authenticateNTLMHelperServerAvailable;
66static HLPSONEQ authenticateNTLMHelperServerOnEmpty;
67
68static statefulhelper *ntlmauthenticators = NULL;
69
70CBDATA_TYPE(authenticateStateData);
71
72static int authntlm_initialised = 0;
73
b001e822 74static MemAllocatorProxy *ntlm_helper_state_pool = NULL;
75static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
e6ccf245 76
f5691f9c 77static auth_ntlm_config ntlmConfig;
94439e4e 78
79static hash_table *proxy_auth_cache = NULL;
80
81/*
82 *
83 * Private Functions
84 *
85 */
86
f5691f9c 87/* move to ntlmScheme.cc */
88void
89ntlmScheme::done()
94439e4e 90{
f5691f9c 91 /* TODO: this should be a Config call. */
2d70df72 92 debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
62e76326 93
bd507204 94 if (ntlmauthenticators)
62e76326 95 helperStatefulShutdown(ntlmauthenticators);
96
94439e4e 97 authntlm_initialised = 0;
62e76326 98
94439e4e 99 if (!shutting_down)
62e76326 100 return;
101
bd507204 102 if (ntlmauthenticators)
62e76326 103 helperStatefulFree(ntlmauthenticators);
104
94439e4e 105 ntlmauthenticators = NULL;
62e76326 106
2f44bd34 107#if DEBUGSHUTDOWN
62e76326 108
5dae8514 109 if (ntlm_helper_state_pool) {
b001e822 110 delete ntlm_helper_state_pool;
111 ntlm_helper_state_pool = NULL;
5dae8514 112 }
62e76326 113
b001e822 114 /* Removed for some reason..
115 if (ntlm_user_pool) {
116 delete ntlm_user_pool;ntlm_user_pool = NULL;
117 }
118 */
119
2f44bd34 120#endif
5dae8514 121 debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
94439e4e 122}
123
124/* free any allocated configuration details */
f5691f9c 125void
126AuthNTLMConfig::done()
94439e4e 127{
f5691f9c 128 if (authenticate)
129 wordlistDestroy(&authenticate);
94439e4e 130}
131
f5691f9c 132void
133AuthNTLMConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
94439e4e 134{
f5691f9c 135 wordlist *list = authenticate;
94439e4e 136 storeAppendPrintf(entry, "%s %s", name, "ntlm");
62e76326 137
94439e4e 138 while (list != NULL) {
62e76326 139 storeAppendPrintf(entry, " %s", list->key);
140 list = list->next;
94439e4e 141 }
62e76326 142
94439e4e 143 storeAppendPrintf(entry, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
f5691f9c 144 name, "ntlm", authenticateChildren,
145 name, "ntlm", challengeuses,
146 name, "ntlm", (int) challengelifetime);
94439e4e 147
148}
149
f5691f9c 150AuthNTLMConfig::AuthNTLMConfig()
94439e4e 151{
f5691f9c 152 /* TODO Move into initialisation list */
153 authenticateChildren = 5;
154 challengeuses = 0;
155 challengelifetime = 60;
156}
62e76326 157
f5691f9c 158void
159AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
160{
94439e4e 161 if (strcasecmp(param_str, "program") == 0) {
f5691f9c 162 if (authenticate)
163 wordlistDestroy(&authenticate);
62e76326 164
f5691f9c 165 parse_wordlist(&authenticate);
62e76326 166
f5691f9c 167 requirePathnameExists("authparam ntlm program", authenticate->key);
94439e4e 168 } else if (strcasecmp(param_str, "children") == 0) {
f5691f9c 169 parse_int(&authenticateChildren);
94439e4e 170 } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
f5691f9c 171 parse_int(&challengeuses);
94439e4e 172 } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
f5691f9c 173 parse_time_t(&challengelifetime);
94439e4e 174 } else {
62e76326 175 debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str);
94439e4e 176 }
62e76326 177
dff99376 178 /*
179 * disable client side request pipelining. There is a race with
180 * NTLM when the client sends a second request on an NTLM
181 * connection before the authenticate challenge is sent. With
182 * this patch, the client may fail to authenticate, but squid's
183 * state will be preserved. Caveats: this should be a post-parse
184 * test, but that can wait for the modular parser to be integrated.
721b0310 185 */
f5691f9c 186 if (authenticate)
62e76326 187 Config.onoff.pipeline_prefetch = 0;
94439e4e 188}
189
f5691f9c 190const char *
191AuthNTLMConfig::type() const
94439e4e 192{
f5691f9c 193 return ntlmScheme::GetInstance().type();
94439e4e 194}
195
196/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
197 * config file */
f5691f9c 198void
199AuthNTLMConfig::init(AuthConfig * scheme)
94439e4e 200{
201 static int ntlminit = 0;
62e76326 202
f5691f9c 203 if (authenticate) {
62e76326 204 if (!ntlm_helper_state_pool)
b001e822 205 ntlm_helper_state_pool = new MemAllocatorProxy("NTLM Helper State data", sizeof(ntlm_helper_state_t));
62e76326 206
62e76326 207 if (!ntlm_user_hash_pool)
208
b001e822 209 ntlm_user_hash_pool = new MemAllocatorProxy("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer));
62e76326 210
211 authntlm_initialised = 1;
212
213 if (ntlmauthenticators == NULL)
214 ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
215
216 if (!proxy_auth_cache)
217 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
218
219 assert(proxy_auth_cache);
220
f5691f9c 221 ntlmauthenticators->cmdline = authenticate;
62e76326 222
f5691f9c 223 ntlmauthenticators->n_to_start = authenticateChildren;
62e76326 224
225 ntlmauthenticators->ipc_type = IPC_STREAM;
226
227 ntlmauthenticators->datapool = ntlm_helper_state_pool;
228
229 ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
230
231 ntlmauthenticators->OnEmptyQueue = authenticateNTLMHelperServerOnEmpty;
232
233 helperStatefulOpenServers(ntlmauthenticators);
234
235 /*
236 * TODO: In here send the initial YR to preinitialise the
237 * challenge cache
238 */
239 /*
240 * Think about this... currently we ask when the challenge
241 * is needed. Better?
242 */
243 if (!ntlminit) {
244 cachemgrRegister("ntlmauthenticator",
245 "NTLM User Authenticator Stats",
246 authenticateNTLMStats, 0, 1);
247 ntlminit++;
248 }
249
250 CBDATA_INIT_TYPE(authenticateStateData);
94439e4e 251 }
252}
253
f5691f9c 254bool
255AuthNTLMConfig::active() const
2d70df72 256{
f5691f9c 257 return authntlm_initialised == 1;
2d70df72 258}
259
f5691f9c 260bool
261AuthNTLMConfig::configured() const
94439e4e 262{
f5691f9c 263 if ((authenticate != NULL) && (authenticateChildren != 0) && (challengeuses > -1) && (challengelifetime > -1)) {
62e76326 264 debug(29, 9) ("authNTLMConfigured: returning configured\n");
f5691f9c 265 return true;
2d70df72 266 }
62e76326 267
2d70df72 268 debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
f5691f9c 269 return false;
94439e4e 270}
271
272/* NTLM Scheme */
82b045dc 273int
f5691f9c 274AuthNTLMUserRequest::module_direction()
94439e4e 275{
bd507204 276 /* null auth_user is checked for by authenticateDirection */
62e76326 277
82b045dc 278 switch (auth_state) {
62e76326 279
82b045dc 280 /* no progress at all. */
281
282 case AUTHENTICATE_STATE_NONE:
f5691f9c 283 debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate!. Report a bug to squid-dev.\n");
62e76326 284 /* fall thru */
285
3c641669 286 case AUTHENTICATE_STATE_FAILED:
62e76326 287 return -2;
288
82b045dc 289 /* send to helper */
290
291 case AUTHENTICATE_STATE_NEGOTIATE:
292
293 /*send to helper */
62e76326 294
82b045dc 295 case AUTHENTICATE_STATE_RESPONSE:
62e76326 296 return -1;
297
82b045dc 298 /* send to client */
299
300 case AUTHENTICATE_STATE_CHALLENGE:
62e76326 301 return 1;
302
82b045dc 303 /* do nothing.. */
304
305 case AUTHENTICATE_STATE_DONE:
62e76326 306 return 0;
94439e4e 307 }
62e76326 308
94439e4e 309 return -2;
310}
311
312/*
313 * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
314 * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
315 * else.
316 */
f5691f9c 317void
318AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
94439e4e 319{
f5691f9c 320 AuthNTLMUserRequest *ntlm_request;
62e76326 321
f5e45ad8 322 if (!request->flags.proxy_keepalive)
323 return;
324
f5691f9c 325 if (authenticate) {
62e76326 326 /* New request, no user details */
327
328 if (auth_user_request == NULL) {
329 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
330 httpHeaderPutStrf(&rep->header, type, "NTLM");
331 /* drop the connection */
332 httpHeaderDelByName(&rep->header, "keep-alive");
333 /* NTLM has problems if the initial connection is not dropped
334 * I haven't checked the RFC compliance of this hack - RBCollins */
335 request->flags.proxy_keepalive = 0;
336 } else {
f5691f9c 337 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
82b045dc 338 assert (ntlm_request);
62e76326 339
340 switch (ntlm_request->auth_state) {
341
342 case AUTHENTICATE_STATE_NONE:
343
344 case AUTHENTICATE_STATE_FAILED:
345 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
346 httpHeaderPutStrf(&rep->header, type, "NTLM");
347 /* drop the connection */
348 httpHeaderDelByName(&rep->header, "keep-alive");
349 /* NTLM has problems if the initial connection is not dropped
350 * I haven't checked the RFC compliance of this hack - RBCollins */
351 request->flags.proxy_keepalive = 0;
352 break;
353
354 case AUTHENTICATE_STATE_CHALLENGE:
355 /* we are 'waiting' for a response */
356 /* pass the challenge to the client */
357 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->authchallenge);
358 httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->authchallenge);
f5e45ad8 359 request->flags.must_keepalive = 1;
62e76326 360 break;
361
362 default:
363 debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
364 fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
365 }
366 }
94439e4e 367 }
368}
369
f5691f9c 370NTLMUser::~NTLMUser()
94439e4e 371{
372 dlink_node *link, *tmplink;
e6ccf245 373 ProxyAuthCachePointer *proxy_auth_hash;
f5691f9c 374 debug(29, 5) ("NTLMUser::~NTLMUser: Clearing NTLM scheme data\n");
62e76326 375
94439e4e 376 /* were they linked in by one or more proxy-authenticate headers */
f5691f9c 377 link = proxy_auth_list.head;
62e76326 378
94439e4e 379 while (link) {
62e76326 380 debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link->data);
381 proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(link->data);
382 tmplink = link;
383 link = link->next;
f5691f9c 384 dlinkDelete(tmplink, &proxy_auth_list);
62e76326 385 hash_remove_link(proxy_auth_cache, (hash_link *) proxy_auth_hash);
386 /* free the key (usually the proxy_auth header) */
387 xfree(proxy_auth_hash->key);
b001e822 388 ntlm_user_hash_pool->free(proxy_auth_hash);
94439e4e 389 }
62e76326 390
94439e4e 391}
392
393static stateful_helper_callback_t
394authenticateNTLMHandleplaceholder(void *data, void *lastserver, char *reply)
395{
e6ccf245 396 authenticateStateData *r = static_cast<authenticateStateData *>(data);
94439e4e 397 stateful_helper_callback_t result = S_HELPER_UNKNOWN;
94439e4e 398 /* we should only be called for placeholder requests - which have no reply string */
399 assert(reply == NULL);
400 assert(r->auth_user_request);
401 /* standard callback stuff */
62e76326 402
fa80a8ef 403 if (!cbdataReferenceValid(r->data)) {
62e76326 404 debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
405 return result;
60d096f4 406 }
62e76326 407
94439e4e 408 /* call authenticateNTLMStart to retry this request */
409 debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
62e76326 410
f5691f9c 411 r->auth_user_request->start(r->handler, r->data);
62e76326 412
fa80a8ef 413 cbdataReferenceDone(r->data);
62e76326 414
94439e4e 415 authenticateStateFree(r);
62e76326 416
94439e4e 417 return result;
418}
419
420static stateful_helper_callback_t
421authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
422{
e6ccf245 423 authenticateStateData *r = static_cast<authenticateStateData *>(data);
94439e4e 424 ntlm_helper_state_t *helperstate;
94439e4e 425 stateful_helper_callback_t result = S_HELPER_UNKNOWN;
94439e4e 426 auth_user_request_t *auth_user_request;
427 auth_user_t *auth_user;
428 ntlm_user_t *ntlm_user;
f5691f9c 429 AuthNTLMUserRequest *ntlm_request;
33272404 430 debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver, reply ? reply : "<NULL>");
62e76326 431
fa80a8ef 432 if (!cbdataReferenceValid(r->data)) {
62e76326 433 debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
434 cbdataReferenceDone(r->data);
435 authenticateStateFree(r);
436 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE);
437 return S_HELPER_RELEASE;
9bea1d5b 438 }
62e76326 439
5d146f7d 440 if (!reply) {
62e76326 441 /*
442 * TODO: this occurs when a helper crashes. We should clean
443 * up that helpers resources and queued requests.
444 */
445 fatal("authenticateNTLMHandleReply: called with no result string\n");
9bea1d5b 446 }
62e76326 447
5d146f7d 448 /* seperate out the useful data */
449 if (strncasecmp(reply, "TT ", 3) == 0) {
62e76326 450 reply += 3;
451 /* we have been given a Challenge */
452 /* we should check we weren't given an empty challenge */
453 /* copy the challenge to the state data */
454 helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(static_cast<helper_stateful_server *>(lastserver)));
455
456 if (helperstate == NULL)
457 fatal("lost NTLM helper state! quitting\n");
458
fb8944ba 459 helperstate->challenge = xstrdup(reply);
62e76326 460
461 helperstate->challengeuses = 0;
462
463 helperstate->renewed = squid_curtime;
464
465 /* and we satisfy the request that happended on the refresh boundary */
466 /* note this code is now in two places FIXME */
467 assert(r->auth_user_request != NULL);
468
f5691f9c 469 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 470
471 auth_user_request = r->auth_user_request;
472
f5691f9c 473 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
62e76326 474
475 assert(ntlm_request != NULL);
476
477 result = S_HELPER_DEFER;
478
479 /* reserve the server for future authentication */
480 ntlm_request->authserver_deferred = 1;
481
482 debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver);
483
484 assert(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
485
486 ntlm_request->authserver = static_cast<helper_stateful_server *>(lastserver);
487
fb8944ba 488 ntlm_request->authchallenge = xstrdup(reply);
5d146f7d 489 } else if (strncasecmp(reply, "AF ", 3) == 0) {
62e76326 490 /* we're finished, release the helper */
491 reply += 3;
492 assert(r->auth_user_request != NULL);
f5691f9c 493 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 494 auth_user_request = r->auth_user_request;
f5691f9c 495 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
82b045dc 496 assert(ntlm_request);
f5691f9c 497 auth_user = auth_user_request->user();
498 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
62e76326 499 assert(ntlm_user != NULL);
500 result = S_HELPER_RELEASE;
501 /* we only expect OK when finishing the handshake */
502 assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
fb8944ba 503 ntlm_user->username(xstrdup(reply));
62e76326 504 ntlm_request->authserver = NULL;
6437ac71 505#ifdef NTLM_FAIL_OPEN
62e76326 506
5d146f7d 507 } else if (strncasecmp(reply, "LD ", 3) == 0) {
62e76326 508 /* This is a variant of BH, which rather than deny access
509 * allows the user through. The helper is starved and then refreshed
510 * via YR, all pending authentications are likely to fail also.
511 * It is meant for those helpers which occasionally fail for
512 * no reason at all (casus belli, NTLMSSP helper on NT domain,
513 * failing about 1 auth out of 1k.
514 * The code is a merge from the BH case with snippets of the AF
515 * case */
516 /* AF code: mark user as authenticated */
517 reply += 3;
518 assert(r->auth_user_request != NULL);
f5691f9c 519 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 520 auth_user_request = r->auth_user_request;
f5691f9c 521 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
82b045dc 522 assert(ntlm_request);
f5691f9c 523 auth_user = auth_user_request->user();
524 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
62e76326 525 assert(ntlm_user != NULL);
526 result = S_HELPER_RELEASE;
527 /* we only expect LD when finishing the handshake */
528 assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
fb8944ba 529 ntlm_user->username_ = xstrdup(reply);
7928e475 530 helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
62e76326 531 ntlm_request->authserver = NULL;
532 /* BH code: mark helper as broken */
533 /* mark it for starving */
534 helperstate->starve = 1;
6437ac71 535#endif
62e76326 536
5d146f7d 537 } else if (strncasecmp(reply, "NA ", 3) == 0) {
62e76326 538 /* TODO: only work with auth_user here if it exists */
539 assert(r->auth_user_request != NULL);
f5691f9c 540 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 541 auth_user_request = r->auth_user_request;
f5691f9c 542 auth_user = auth_user_request->user();
62e76326 543 assert(auth_user != NULL);
f5691f9c 544 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
545 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
62e76326 546 assert((ntlm_user != NULL) && (ntlm_request != NULL));
547 /* todo: action of Negotiate state on error */
548 result = S_HELPER_RELEASE; /*some error has occured. no more requests */
549 ntlm_request->authserver = NULL;
550 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
551 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
0a0c70cd 552 reply += 3;
62e76326 553
0a0c70cd 554 if (*reply)
555 auth_user_request->setDenyMessage(reply);
5d146f7d 556 } else if (strncasecmp(reply, "NA", 2) == 0) {
62e76326 557 /* NTLM Helper protocol violation! */
558 fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
5d146f7d 559 } else if (strncasecmp(reply, "BH ", 3) == 0) {
62e76326 560 /* TODO kick off a refresh process. This can occur after a YR or after
561 * a KK. If after a YR release the helper and resubmit the request via
562 * Authenticate NTLM start.
563 * If after a KK deny the user's request w/ 407 and mark the helper as
564 * Needing YR. */
565 assert(r->auth_user_request != NULL);
f5691f9c 566 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 567 auth_user_request = r->auth_user_request;
f5691f9c 568 auth_user = auth_user_request->user();
62e76326 569 assert(auth_user != NULL);
f5691f9c 570 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
571 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
62e76326 572 assert((ntlm_user != NULL) && (ntlm_request != NULL));
82b045dc 573 /*some error has occured. no more requests for
574 * this helper */
575 result = S_HELPER_RELEASE;
62e76326 576 assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
577 helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
578 ntlm_request->authserver = NULL;
579
580 if (ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE) {
581 /* The helper broke on YR. It automatically
582 * resets */
583 debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver, reply);
584 /* mark it for starving */
585 helperstate->starve = 1;
586 /* resubmit the request. This helper is currently busy, so we will get
587 * a different one. Our auth state stays the same */
f5691f9c 588 auth_user_request->start(r->handler, r->data);
62e76326 589 /* don't call the callback */
590 cbdataReferenceDone(r->data);
591 authenticateStateFree(r);
592 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
593 return result;
594 }
595
596 /* the helper broke on a KK */
597 /* first the standard KK stuff */
598 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
599
62e76326 600 /* now we mark the helper for resetting. */
601 helperstate->starve = 1;
602
603 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
0a0c70cd 604
605 reply += 3;
606
607 if (*reply)
608 auth_user_request->setDenyMessage(reply);
94439e4e 609 } else {
62e76326 610 /* TODO: only work with auth_user here if it exists */
611 /* TODO: take the request state into consideration */
612 assert(r->auth_user_request != NULL);
f5691f9c 613 assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
62e76326 614 auth_user_request = r->auth_user_request;
f5691f9c 615 auth_user = auth_user_request->user();
62e76326 616 assert(auth_user != NULL);
f5691f9c 617 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
618 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
62e76326 619 assert((ntlm_user != NULL) && (ntlm_request != NULL));
620 debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
621 /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
622 /* restart the authentication process */
623 ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
624 assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
625 ntlm_request->authserver = NULL;
94439e4e 626 }
62e76326 627
5d146f7d 628 r->handler(r->data, NULL);
fa80a8ef 629 cbdataReferenceDone(r->data);
94439e4e 630 authenticateStateFree(r);
631 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
632 return result;
633}
634
94439e4e 635static void
636authenticateNTLMStats(StoreEntry * sentry)
637{
638 storeAppendPrintf(sentry, "NTLM Authenticator Statistics:\n");
639 helperStatefulStats(sentry, ntlmauthenticators);
640}
641
642/* is a particular challenge still valid ? */
c78aa667 643static int
94439e4e 644authenticateNTLMValidChallenge(ntlm_helper_state_t * helperstate)
645{
6437ac71 646 debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate->challenge ? "Valid" : "Invalid");
62e76326 647
94439e4e 648 if (helperstate->challenge == NULL)
62e76326 649 return 0;
650
94439e4e 651 return 1;
652}
653
654/* does our policy call for changing the challenge now? */
c78aa667 655static int
60d096f4 656authenticateNTLMChangeChallenge_p(ntlm_helper_state_t * helperstate)
94439e4e 657{
658 /* don't check for invalid challenges just for expiry choices */
659 /* this is needed because we have to starve the helper until all old
660 * requests have been satisfied */
62e76326 661
6437ac71 662 if (!helperstate->renewed) {
62e76326 663 /* first use, no challenge has been set. Without this check, it will
664 * loop forever */
665 debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
666 return 0;
6437ac71 667 }
62e76326 668
f5691f9c 669 if (helperstate->challengeuses > ntlmConfig.challengeuses) {
670 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate->challengeuses, ntlmConfig.challengeuses);
62e76326 671 return 1;
6437ac71 672 }
62e76326 673
f5691f9c 674 if (helperstate->renewed + ntlmConfig.challengelifetime < squid_curtime) {
675 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime - (helperstate->renewed + ntlmConfig.challengelifetime)));
62e76326 676 return 1;
6437ac71 677 }
62e76326 678
6437ac71 679 debug(29, 9) ("Challenge is to be reused\n");
94439e4e 680 return 0;
681}
682
683/* send the initial data to a stateful ntlm authenticator module */
f5691f9c 684void
685AuthNTLMUserRequest::module_start(RH * handler, void *data)
94439e4e 686{
94439e4e 687 authenticateStateData *r = NULL;
688 helper_stateful_server *server;
689 ntlm_helper_state_t *helperstate;
690 char buf[8192];
691 char *sent_string = NULL;
692 ntlm_user_t *ntlm_user;
94439e4e 693 auth_user_t *auth_user;
694
f5691f9c 695 auth_user = this->user();
696 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
94439e4e 697 assert(ntlm_user);
94439e4e 698 assert(data);
9970d4b1 699 assert(auth_user->auth_type == AUTH_NTLM);
f5691f9c 700 debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", auth_state);
62e76326 701
f5691f9c 702 switch (auth_state) {
62e76326 703
94439e4e 704 case AUTHENTICATE_STATE_NEGOTIATE:
f5691f9c 705 sent_string = ntlmnegotiate;
62e76326 706 break;
707
94439e4e 708 case AUTHENTICATE_STATE_RESPONSE:
f5691f9c 709 sent_string = ntlmauthenticate;
710 assert(authserver);
711 debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", authserver);
62e76326 712 break;
713
94439e4e 714 default:
62e76326 715 fatal("Invalid authenticate state for NTLMStart");
94439e4e 716 }
717
ba53f4b8 718 while (xisgraph(*sent_string)) /*trim NTLM */
62e76326 719 sent_string++;
94439e4e 720
721 while (xisspace(*sent_string)) /*trim leading spaces */
62e76326 722 sent_string++;
94439e4e 723
f5691f9c 724 debug(29, 9) ("authenticateNTLMStart: state '%d'\n", auth_state);
62e76326 725
94439e4e 726 debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
62e76326 727
f5691f9c 728 if (ntlmConfig.authenticate == NULL) {
62e76326 729 debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string);
730 handler(data, NULL);
731 return;
94439e4e 732 }
62e76326 733
94439e4e 734 /* this is ugly TODO: move the challenge generation routines to their own function and
735 * tidy the logic up to make use of the efficiency we now have */
f5691f9c 736 switch (auth_state) {
62e76326 737
94439e4e 738 case AUTHENTICATE_STATE_NEGOTIATE:
62e76326 739 /*
740 * 1: get a helper server
741 * 2: does it have a challenge?
742 * 3: tell it to get a challenge, or give ntlmauthdone the challenge
743 */
744 server = helperStatefulDefer(ntlmauthenticators);
745 helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
746
747 while ((server != NULL) && authenticateNTLMChangeChallenge_p(helperstate)) {
748 /* flag this helper for challenge changing */
749 helperstate->starve = 1;
750 /* and release the deferred request */
751 helperStatefulReleaseServer(server);
752 /* Get another deferrable server */
753 server = helperStatefulDefer(ntlmauthenticators);
754 helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
755 }
756
757 if (server == NULL)
758 debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
759
f5691f9c 760 authserver = server;
62e76326 761
762 /* tell the log what helper we have been given */
763 debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server);
764
765 /* server and valid challenge? */
766 if ((server == NULL) || !authenticateNTLMValidChallenge(helperstate)) {
767 /* No server, or server with invalid challenge */
768 r = cbdataAlloc(authenticateStateData);
769 r->handler = handler;
770 r->data = cbdataReference(data);
f5691f9c 771 r->auth_user_request = this;
62e76326 772
8777d90e 773 lock()
774
775 ; /* locking myself */
776
62e76326 777 if (server == NULL) {
778 helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, NULL);
779 } else {
780 /* Server with invalid challenge */
781 snprintf(buf, 8192, "YR\n");
f5691f9c 782 helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
62e76326 783 }
784 } else {
785 /* (server != NULL and we have a valid challenge) */
786 /* TODO: turn the below into a function and call from here and handlereply */
787 /* increment the challenge uses */
788 helperstate->challengeuses++;
789 /* assign the challenge */
fb8944ba 790 authchallenge = xstrdup(helperstate->challenge);
62e76326 791 /* we're not actually submitting a request, so we need to release the helper
792 * should the connection close unexpectedly
793 */
f5691f9c 794 authserver_deferred = 1;
62e76326 795 handler(data, NULL);
796 }
797
798 break;
799
94439e4e 800 case AUTHENTICATE_STATE_RESPONSE:
62e76326 801 r = cbdataAlloc(authenticateStateData);
802 r->handler = handler;
803 r->data = cbdataReference(data);
f5691f9c 804 r->auth_user_request = this;
8777d90e 805
806 lock()
807
808 ;
62e76326 809 snprintf(buf, 8192, "KK %s\n", sent_string);
8777d90e 810
62e76326 811 /* getting rid of deferred request status */
f5691f9c 812 authserver_deferred = 0;
8777d90e 813
f5691f9c 814 helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
8777d90e 815
62e76326 816 debug(29, 9) ("authenticateNTLMstart: finished\n");
8777d90e 817
62e76326 818 break;
819
94439e4e 820 default:
62e76326 821 fatal("Invalid authenticate state for NTLMStart");
94439e4e 822 }
94439e4e 823}
824
825/* callback used by stateful helper routines */
c78aa667 826static int
94439e4e 827authenticateNTLMHelperServerAvailable(void *data)
828{
e6ccf245 829 ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
62e76326 830
94439e4e 831 if (statedata != NULL) {
62e76326 832 if (statedata->starve) {
833 debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
834 return 0;
835 } else {
836 debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
837 return 1;
838 }
94439e4e 839 }
62e76326 840
94439e4e 841 debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
842 return 0;
843}
844
c78aa667 845static void
94439e4e 846authenticateNTLMHelperServerOnEmpty(void *data)
847{
e6ccf245 848 ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
62e76326 849
94439e4e 850 if (statedata == NULL)
62e76326 851 return;
852
94439e4e 853 if (statedata->starve) {
62e76326 854 /* we have been starving the helper */
855 debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
856 statedata->starve = 0;
857 statedata->challengeuses = 0;
858 statedata->renewed = 0;
859 xfree(statedata->challenge);
860 statedata->challenge = NULL;
94439e4e 861 }
862}
863
864
865/* clear the NTLM helper of being reserved for future requests */
c78aa667 866static void
60d096f4 867authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
94439e4e 868{
f5691f9c 869 AuthNTLMUserRequest *ntlm_request;
870 assert(auth_user_request->user()->auth_type == AUTH_NTLM);
871 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
82b045dc 872 assert (ntlm_request);
33272404 873 debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver);
60d096f4 874 helperStatefulReleaseServer(ntlm_request->authserver);
875 ntlm_request->authserver = NULL;
94439e4e 876}
877
878/* clear any connection related authentication details */
f5691f9c 879void
880AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
94439e4e 881{
94439e4e 882 assert(conn != NULL);
62e76326 883
94439e4e 884 if (conn->auth_user_request != NULL) {
f5691f9c 885 assert (conn->auth_user_request == this);
886 assert(this->conn == conn);
62e76326 887
f5691f9c 888 if (authserver != NULL && authserver_deferred)
889 authenticateNTLMReleaseServer(this);
62e76326 890
891 /* unlock the connection based lock */
892 debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
893
3587fdd2 894 /* This still breaks the abstraction, but is at least read only now.
895 * If needed, this could be ignored, as the conn deletion will also unlock
896 * the auth user request.
897 */
f5691f9c 898 this->unlock();
62e76326 899
900 conn->auth_user_request = NULL;
94439e4e 901 }
902}
903
60d096f4 904/* NTLMLastHeader: return a pointer to the last header used in authenticating
905 * the request/conneciton
906 */
f5691f9c 907const char *
908AuthNTLMUserRequest::connLastHeader()
60d096f4 909{
f5691f9c 910 return ntlmauthenticate;
60d096f4 911}
94439e4e 912
913/*
914 * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
915 * Auth_user structure.
916 */
f5691f9c 917AuthUserRequest *
918AuthNTLMConfig::decode(char const *proxy_auth)
94439e4e 919{
f5691f9c 920 NTLMUser *newUser = new NTLMUser(&ntlmConfig);
921 AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest ();
922 assert(auth_user_request->user() == NULL);
923 auth_user_request->user(newUser);
924 auth_user_request->user()->auth_type = AUTH_NTLM;
925 auth_user_request->user()->addRequest(auth_user_request);
94439e4e 926
927 /* all we have to do is identify that it's NTLM - the helper does the rest */
928 debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
f5691f9c 929 return auth_user_request;
94439e4e 930}
931
c78aa667 932static int
94439e4e 933authenticateNTLMcmpUsername(ntlm_user_t * u1, ntlm_user_t * u2)
934{
f5691f9c 935 return strcmp(u1->username(), u2->username());
94439e4e 936}
937
60d096f4 938
939/* there is a known race where a single client recieves the same challenge
940 * and sends the same response to squid on a single select cycle.
941 * Check for this and if found ignore the new link
942 */
c78aa667 943static void
94439e4e 944authenticateProxyAuthCacheAddLink(const char *key, auth_user_t * auth_user)
945{
62e76326 946
e6ccf245 947 struct ProxyAuthCachePointer *proxy_auth_hash;
60d096f4 948 dlink_node *node;
94439e4e 949 ntlm_user_t *ntlm_user;
f5691f9c 950 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
60d096f4 951 node = ntlm_user->proxy_auth_list.head;
952 /* prevent duplicates */
62e76326 953
60d096f4 954 while (node) {
62e76326 955
956 if (!strcmp(key, (char const *)((struct ProxyAuthCachePointer *) node->data)->key))
957 return;
958
959 node = node->next;
60d096f4 960 }
62e76326 961
b001e822 962 proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(ntlm_user_hash_pool->alloc());
94439e4e 963 proxy_auth_hash->key = xstrdup(key);
964 proxy_auth_hash->auth_user = auth_user;
6437ac71 965 dlinkAddTail(proxy_auth_hash, &proxy_auth_hash->link, &ntlm_user->proxy_auth_list);
94439e4e 966 hash_join(proxy_auth_cache, (hash_link *) proxy_auth_hash);
967}
968
82b045dc 969int
f5691f9c 970AuthNTLMUserRequest::authenticated() const
94439e4e 971{
82b045dc 972 if (auth_state == AUTHENTICATE_STATE_DONE)
62e76326 973 return 1;
974
94439e4e 975 debug(29, 9) ("User not fully authenticated.\n");
62e76326 976
94439e4e 977 return 0;
978}
979
82b045dc 980void
f5691f9c 981AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
94439e4e 982{
983 const char *proxy_auth;
62e76326 984
e6ccf245 985 struct ProxyAuthCachePointer *proxy_auth_hash = NULL;
986 auth_user_hash_pointer *usernamehash;
f5691f9c 987 /* TODO: rename this!! */
94439e4e 988 auth_user_t *auth_user;
f5691f9c 989 AuthNTLMUserRequest *ntlm_request;
94439e4e 990 ntlm_user_t *ntlm_user;
991 LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
992 /* get header */
993 proxy_auth = httpHeaderGetStr(&request->header, type);
994
f5691f9c 995 auth_user = user();
94439e4e 996 assert(auth_user);
997 assert(auth_user->auth_type == AUTH_NTLM);
f5691f9c 998 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
999 ntlm_request = this;
82b045dc 1000 assert (ntlm_request);
3a73fe76 1001 /* Check that we are in the client side, where we can generate
1002 * auth challenges */
62e76326 1003
a2ac85d9 1004 if (conn.getRaw() == NULL) {
62e76326 1005 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
1006 debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
1007 return;
3a73fe76 1008 }
62e76326 1009
94439e4e 1010 switch (ntlm_request->auth_state) {
62e76326 1011
94439e4e 1012 case AUTHENTICATE_STATE_NONE:
62e76326 1013 /* we've recieved a negotiate request. pass to a helper */
1014 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth);
1015 ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
fb8944ba 1016 ntlm_request->ntlmnegotiate = xstrdup(proxy_auth);
62e76326 1017 conn->auth_type = AUTH_NTLM;
f5691f9c 1018 conn->auth_user_request = this;
62e76326 1019 ntlm_request->conn = conn;
1020 /* and lock for the connection duration */
1021 debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
f5691f9c 1022
1023 this->lock()
1024
1025 ;
62e76326 1026 return;
f5691f9c 1027
62e76326 1028 break;
1029
94439e4e 1030 case AUTHENTICATE_STATE_NEGOTIATE:
62e76326 1031 ntlm_request->auth_state = AUTHENTICATE_STATE_CHALLENGE;
f5691f9c 1032
62e76326 1033 /* We _MUST_ have the auth challenge by now */
1034 assert(ntlm_request->authchallenge);
f5691f9c 1035
62e76326 1036 return;
f5691f9c 1037
62e76326 1038 break;
1039
94439e4e 1040 case AUTHENTICATE_STATE_CHALLENGE:
62e76326 1041 /* we should have recieved a NTLM challenge. pass it to the same
1042 * helper process */
1043 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
f5691f9c 1044
62e76326 1045 /* do a cache lookup here. If it matches it's a successful ntlm
1046 * challenge - release the helper and use the existing auth_user
1047 * details. */
1048
638b2ce3 1049 ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
62e76326 1050
1051 /* cache entries have authenticateauthheaderchallengestring */
1052 snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
1053 ntlm_request->ntlmauthenticate,
1054 ntlm_request->authchallenge);
1055
1056 /* see if we already know this user's authenticate */
1057 debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
1058
1059 assert(proxy_auth_cache != NULL);
1060
1061 proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(hash_lookup(proxy_auth_cache, ntlmhash));
1062
1063 if (!proxy_auth_hash) { /* not in the hash table */
1064 debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
1065 ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
1066 /* verify with the ntlm helper */
1067 } else {
1068 debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
1069 /* throw away the temporary entry */
1070 ntlm_request->authserver_deferred = 0;
f5691f9c 1071 authenticateNTLMReleaseServer(this);
62e76326 1072 authenticateAuthUserMerge(auth_user, proxy_auth_hash->auth_user);
1073 auth_user = proxy_auth_hash->auth_user;
f5691f9c 1074 this->user(auth_user);
62e76326 1075 ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
1076 /* we found one */
1077 debug(29, 9) ("found matching cache entry\n");
1078 assert(auth_user->auth_type == AUTH_NTLM);
1079 /* get the existing entries details */
f5691f9c 1080 ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
1081 debug(29, 9) ("Username to be used is %s\n", ntlm_user->username());
62e76326 1082 /* on ntlm auth we do not unlock the auth_user until the
1083 * connection is dropped. Thank MS for this quirk */
1084 auth_user->expiretime = current_time.tv_sec;
1085 }
1086
1087 return;
1088 break;
1089
94439e4e 1090 case AUTHENTICATE_STATE_RESPONSE:
62e76326 1091 /* auth-challenge pair cache miss. We've just got the response from the helper */
1092 /*add to cache and let them through */
1093 ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
1094 /* this connection is authenticated */
1095 debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
1096 ntlm_request->authchallenge,
1097 ntlm_request->ntlmauthenticate,
f5691f9c 1098 ntlm_user->username());
62e76326 1099 /* cache entries have authenticateauthheaderchallengestring */
1100 snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
1101 ntlm_request->ntlmauthenticate,
1102 ntlm_request->authchallenge);
1103 /* see if this is an existing user with a different proxy_auth
1104 * string */
1105
f5691f9c 1106 if ((usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username())))
62e76326 1107 ) {
f5691f9c 1108 while ((usernamehash->user()->auth_type != auth_user->auth_type) && (usernamehash->next) && !authenticateNTLMcmpUsername(dynamic_cast<ntlm_user_t *>(usernamehash->user()), ntlm_user)
62e76326 1109 )
1110 usernamehash = static_cast<AuthUserHashPointer*>(usernamehash->next);
f5691f9c 1111 if (usernamehash->user()->auth_type == auth_user->auth_type) {
62e76326 1112 /*
1113 * add another link from the new proxy_auth to the
1114 * auth_user structure and update the information */
1115 assert(proxy_auth_hash == NULL);
f5691f9c 1116 authenticateProxyAuthCacheAddLink(ntlmhash, usernamehash->user());
62e76326 1117 /* we can't seamlessly recheck the username due to the
1118 * challenge nature of the protocol. Just free the
1119 * temporary auth_user */
f5691f9c 1120 authenticateAuthUserMerge(auth_user, usernamehash->user());
1121 auth_user = usernamehash->user();
1122 this->user(auth_user);
62e76326 1123 }
1124 } else {
1125 /* store user in hash's */
f5691f9c 1126 auth_user->addToNameCache();
62e76326 1127 authenticateProxyAuthCacheAddLink(ntlmhash, auth_user);
1128 }
1129
1130 /* set these to now because this is either a new login from an
1131 * existing user or a new user */
1132 auth_user->expiretime = current_time.tv_sec;
1133 return;
1134 break;
1135
94439e4e 1136 case AUTHENTICATE_STATE_DONE:
62e76326 1137 fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
1138 break;
1139
3c641669 1140 case AUTHENTICATE_STATE_FAILED:
62e76326 1141 /* we've failed somewhere in authentication */
1142 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth);
1143 return;
94439e4e 1144 }
62e76326 1145
94439e4e 1146 return;
1147}
82b045dc 1148
f5691f9c 1149AuthNTLMUserRequest::AuthNTLMUserRequest() : ntlmnegotiate(NULL), authchallenge(NULL), ntlmauthenticate(NULL),
1150 authserver(NULL), auth_state(AUTHENTICATE_STATE_NONE),
1151 authserver_deferred(0), conn(NULL), _theUser(NULL)
1152{}
1153
1154AuthNTLMUserRequest::~AuthNTLMUserRequest()
1155{
1156 if (ntlmnegotiate)
1157 xfree(ntlmnegotiate);
1158
1159 if (authchallenge)
1160 xfree(authchallenge);
1161
1162 if (ntlmauthenticate)
1163 xfree(ntlmauthenticate);
1164
1165 if (authserver != NULL && authserver_deferred) {
1166 debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", authserver);
1167 helperStatefulReleaseServer(authserver);
1168 authserver = NULL;
1169 }
1170}
1171
f5691f9c 1172void
1173NTLMUser::deleteSelf() const
1174{
1175 delete this;
1176}
1177
f5691f9c 1178NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config)
1179{
1180 proxy_auth_list.head = proxy_auth_list.tail = NULL;
1181}
1182
1183AuthConfig *
1184ntlmScheme::createConfig()
1185{
1186 return &ntlmConfig;
1187}
1188