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