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