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