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