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