3 * $Id: auth_ntlm.cc,v 1.29 2003/02/21 22:50:28 robertc Exp $
5 * DEBUG: section 29 NTLM Authenticator
6 * AUTHOR: Robert Collins
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
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 */
42 #include "auth_ntlm.h"
43 #include "authenticate.h"
46 extern AUTHSSETUP authSchemeSetup_ntlm
;
49 authenticateStateFree(authenticateStateData
* r
)
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
;
77 /* helper callbacks to handle per server state data */
78 static HLPSAVAIL authenticateNTLMHelperServerAvailable
;
79 static HLPSONEQ authenticateNTLMHelperServerOnEmpty
;
81 static statefulhelper
*ntlmauthenticators
= NULL
;
83 CBDATA_TYPE(authenticateStateData
);
85 static int authntlm_initialised
= 0;
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
;
92 static auth_ntlm_config
*ntlmConfig
= NULL
;
94 static hash_table
*proxy_auth_cache
= NULL
;
105 debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
107 if (ntlmauthenticators
)
108 helperStatefulShutdown(ntlmauthenticators
);
110 authntlm_initialised
= 0;
115 if (ntlmauthenticators
)
116 helperStatefulFree(ntlmauthenticators
);
118 ntlmauthenticators
= NULL
;
122 if (ntlm_helper_state_pool
) {
123 memPoolDestroy(&ntlm_helper_state_pool
);
126 if (ntlm_request_pool
) {
127 memPoolDestroy(&ntlm_request_pool
);
130 if (ntlm_user_pool
) {
131 memPoolDestroy(&ntlm_user_pool
);
135 debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
138 /* free any allocated configuration details */
140 authNTLMFreeConfig(authScheme
* scheme
)
142 if (ntlmConfig
== NULL
)
145 assert(ntlmConfig
== scheme
->scheme_data
);
147 if (ntlmConfig
->authenticate
)
148 wordlistDestroy(&ntlmConfig
->authenticate
);
156 authNTLMCfgDump(StoreEntry
* entry
, const char *name
, authScheme
* scheme
)
158 auth_ntlm_config
*config
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
159 wordlist
*list
= config
->authenticate
;
160 storeAppendPrintf(entry
, "%s %s", name
, "ntlm");
162 while (list
!= NULL
) {
163 storeAppendPrintf(entry
, " %s", list
->key
);
167 storeAppendPrintf(entry
, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
168 name
, "ntlm", config
->authenticateChildren
,
169 name
, "ntlm", config
->challengeuses
,
170 name
, "ntlm", (int) config
->challengelifetime
);
175 authNTLMParse(authScheme
* scheme
, int n_configured
, char *param_str
)
177 if (scheme
->scheme_data
== NULL
) {
178 assert(ntlmConfig
== NULL
);
179 /* this is the first param to be found */
180 scheme
->scheme_data
= xmalloc(sizeof(auth_ntlm_config
));
181 memset(scheme
->scheme_data
, 0, sizeof(auth_ntlm_config
));
182 ntlmConfig
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
183 ntlmConfig
->authenticateChildren
= 5;
184 ntlmConfig
->challengeuses
= 0;
185 ntlmConfig
->challengelifetime
= 60;
188 ntlmConfig
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
190 if (strcasecmp(param_str
, "program") == 0) {
191 if (ntlmConfig
->authenticate
)
192 wordlistDestroy(&ntlmConfig
->authenticate
);
194 parse_wordlist(&ntlmConfig
->authenticate
);
196 requirePathnameExists("authparam ntlm program", ntlmConfig
->authenticate
->key
);
197 } else if (strcasecmp(param_str
, "children") == 0) {
198 parse_int(&ntlmConfig
->authenticateChildren
);
199 } else if (strcasecmp(param_str
, "max_challenge_reuses") == 0) {
200 parse_int(&ntlmConfig
->challengeuses
);
201 } else if (strcasecmp(param_str
, "max_challenge_lifetime") == 0) {
202 parse_time_t(&ntlmConfig
->challengelifetime
);
204 debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str
);
208 * disable client side request pipelining. There is a race with
209 * NTLM when the client sends a second request on an NTLM
210 * connection before the authenticate challenge is sent. With
211 * this patch, the client may fail to authenticate, but squid's
212 * state will be preserved. Caveats: this should be a post-parse
213 * test, but that can wait for the modular parser to be integrated.
215 if (ntlmConfig
->authenticate
)
216 Config
.onoff
.pipeline_prefetch
= 0;
221 authSchemeSetup_ntlm(authscheme_entry_t
* authscheme
)
223 assert(!authntlm_initialised
);
224 authscheme
->Active
= authenticateNTLMActive
;
225 authscheme
->configured
= authNTLMConfigured
;
226 authscheme
->parse
= authNTLMParse
;
227 authscheme
->dump
= authNTLMCfgDump
;
228 authscheme
->requestFree
= authNTLMAURequestFree
;
229 authscheme
->freeconfig
= authNTLMFreeConfig
;
230 authscheme
->init
= authNTLMInit
;
231 authscheme
->authAuthenticate
= authenticateNTLMAuthenticateUser
;
232 authscheme
->authenticated
= authNTLMAuthenticated
;
233 authscheme
->authFixHeader
= authenticateNTLMFixErrorHeader
;
234 authscheme
->FreeUser
= authenticateNTLMFreeUser
;
235 authscheme
->authStart
= authenticateNTLMStart
;
236 authscheme
->authStats
= authenticateNTLMStats
;
237 authscheme
->authUserUsername
= authenticateNTLMUsername
;
238 authscheme
->getdirection
= authenticateNTLMDirection
;
239 authscheme
->decodeauth
= authenticateDecodeNTLMAuth
;
240 authscheme
->donefunc
= authNTLMDone
;
241 authscheme
->oncloseconnection
= authenticateNTLMOnCloseConnection
;
242 authscheme
->authConnLastHeader
= NTLMLastHeader
;
245 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
248 authNTLMInit(authScheme
* scheme
)
250 static int ntlminit
= 0;
252 if (ntlmConfig
->authenticate
) {
253 if (!ntlm_helper_state_pool
)
254 ntlm_helper_state_pool
= memPoolCreate("NTLM Helper State data", sizeof(ntlm_helper_state_t
));
257 ntlm_user_pool
= memPoolCreate("NTLM Scheme User Data", sizeof(ntlm_user_t
));
259 if (!ntlm_request_pool
)
260 ntlm_request_pool
= memPoolCreate("NTLM Scheme Request Data", sizeof(ntlm_request_t
));
262 if (!ntlm_user_hash_pool
)
264 ntlm_user_hash_pool
= memPoolCreate("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer
));
266 authntlm_initialised
= 1;
268 if (ntlmauthenticators
== NULL
)
269 ntlmauthenticators
= helperStatefulCreate("ntlmauthenticator");
271 if (!proxy_auth_cache
)
272 proxy_auth_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
274 assert(proxy_auth_cache
);
276 ntlmauthenticators
->cmdline
= ntlmConfig
->authenticate
;
278 ntlmauthenticators
->n_to_start
= ntlmConfig
->authenticateChildren
;
280 ntlmauthenticators
->ipc_type
= IPC_STREAM
;
282 ntlmauthenticators
->datapool
= ntlm_helper_state_pool
;
284 ntlmauthenticators
->IsAvailable
= authenticateNTLMHelperServerAvailable
;
286 ntlmauthenticators
->OnEmptyQueue
= authenticateNTLMHelperServerOnEmpty
;
288 helperStatefulOpenServers(ntlmauthenticators
);
291 * TODO: In here send the initial YR to preinitialise the
295 * Think about this... currently we ask when the challenge
299 cachemgrRegister("ntlmauthenticator",
300 "NTLM User Authenticator Stats",
301 authenticateNTLMStats
, 0, 1);
305 CBDATA_INIT_TYPE(authenticateStateData
);
310 authenticateNTLMActive()
312 return (authntlm_initialised
== 1) ? 1 : 0;
319 if ((ntlmConfig
!= NULL
) && (ntlmConfig
->authenticate
!= NULL
) && (ntlmConfig
->authenticateChildren
!= 0) && (ntlmConfig
->challengeuses
> -1) && (ntlmConfig
->challengelifetime
> -1)) {
320 debug(29, 9) ("authNTLMConfigured: returning configured\n");
324 debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
331 authenticateNTLMDirection(auth_user_request_t
* auth_user_request
)
333 ntlm_request_t
*ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
334 /* null auth_user is checked for by authenticateDirection */
336 switch (ntlm_request
->auth_state
) {
338 case AUTHENTICATE_STATE_NONE
: /* no progress at all. */
339 debug(29, 1) ("authenticateNTLMDirection: called before NTLM Authenticate!. Report a bug to squid-dev. au %p\n", auth_user_request
);
342 case AUTHENTICATE_STATE_FAILED
:
345 case AUTHENTICATE_STATE_NEGOTIATE
: /* send to helper */
347 case AUTHENTICATE_STATE_RESPONSE
: /*send to helper */
350 case AUTHENTICATE_STATE_CHALLENGE
: /* send to client */
353 case AUTHENTICATE_STATE_DONE
: /* do nothing.. */
361 * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
362 * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
366 authenticateNTLMFixErrorHeader(auth_user_request_t
* auth_user_request
, HttpReply
* rep
, http_hdr_type type
, request_t
* request
)
368 ntlm_request_t
*ntlm_request
;
370 if (ntlmConfig
->authenticate
) {
371 /* New request, no user details */
373 if (auth_user_request
== NULL
) {
374 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type
);
375 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
376 /* drop the connection */
377 httpHeaderDelByName(&rep
->header
, "keep-alive");
378 /* NTLM has problems if the initial connection is not dropped
379 * I haven't checked the RFC compliance of this hack - RBCollins */
380 request
->flags
.proxy_keepalive
= 0;
382 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
384 switch (ntlm_request
->auth_state
) {
386 case AUTHENTICATE_STATE_NONE
:
388 case AUTHENTICATE_STATE_FAILED
:
389 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type
);
390 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
391 /* drop the connection */
392 httpHeaderDelByName(&rep
->header
, "keep-alive");
393 /* NTLM has problems if the initial connection is not dropped
394 * I haven't checked the RFC compliance of this hack - RBCollins */
395 request
->flags
.proxy_keepalive
= 0;
398 case AUTHENTICATE_STATE_CHALLENGE
:
399 /* we are 'waiting' for a response */
400 /* pass the challenge to the client */
401 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type
, ntlm_request
->authchallenge
);
402 httpHeaderPutStrf(&rep
->header
, type
, "NTLM %s", ntlm_request
->authchallenge
);
406 debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request
->auth_state
);
407 fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
414 authNTLMRequestFree(ntlm_request_t
* ntlm_request
)
419 if (ntlm_request
->ntlmnegotiate
)
420 xfree(ntlm_request
->ntlmnegotiate
);
422 if (ntlm_request
->authchallenge
)
423 xfree(ntlm_request
->authchallenge
);
425 if (ntlm_request
->ntlmauthenticate
)
426 xfree(ntlm_request
->ntlmauthenticate
);
428 if (ntlm_request
->authserver
!= NULL
&& ntlm_request
->authserver_deferred
) {
429 debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", ntlm_request
->authserver
);
430 helperStatefulReleaseServer(ntlm_request
->authserver
);
431 ntlm_request
->authserver
= NULL
;
434 memPoolFree(ntlm_request_pool
, ntlm_request
);
438 authNTLMAURequestFree(auth_user_request_t
* auth_user_request
)
440 if (auth_user_request
->scheme_data
)
441 authNTLMRequestFree(static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
));
442 auth_user_request
->scheme_data
= NULL
;
446 authenticateNTLMFreeUser(auth_user_t
* auth_user
)
448 dlink_node
*link
, *tmplink
;
449 ntlm_user_t
*ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
450 ProxyAuthCachePointer
*proxy_auth_hash
;
452 debug(29, 5) ("authenticateNTLMFreeUser: Clearing NTLM scheme data\n");
454 if (ntlm_user
->username
)
455 xfree(ntlm_user
->username
);
457 /* were they linked in by one or more proxy-authenticate headers */
458 link
= ntlm_user
->proxy_auth_list
.head
;
461 debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link
->data
);
462 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(link
->data
);
465 dlinkDelete(tmplink
, &ntlm_user
->proxy_auth_list
);
466 hash_remove_link(proxy_auth_cache
, (hash_link
*) proxy_auth_hash
);
467 /* free the key (usually the proxy_auth header) */
468 xfree(proxy_auth_hash
->key
);
469 memPoolFree(ntlm_user_hash_pool
, proxy_auth_hash
);
472 memPoolFree(ntlm_user_pool
, ntlm_user
);
473 auth_user
->scheme_data
= NULL
;
476 static stateful_helper_callback_t
477 authenticateNTLMHandleplaceholder(void *data
, void *lastserver
, char *reply
)
479 authenticateStateData
*r
= static_cast<authenticateStateData
*>(data
);
480 stateful_helper_callback_t result
= S_HELPER_UNKNOWN
;
481 /* we should only be called for placeholder requests - which have no reply string */
482 assert(reply
== NULL
);
483 assert(r
->auth_user_request
);
484 /* standard callback stuff */
486 if (!cbdataReferenceValid(r
->data
)) {
487 debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
491 /* call authenticateNTLMStart to retry this request */
492 debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
494 authenticateNTLMStart(r
->auth_user_request
, r
->handler
, r
->data
);
496 cbdataReferenceDone(r
->data
);
498 authenticateStateFree(r
);
503 static stateful_helper_callback_t
504 authenticateNTLMHandleReply(void *data
, void *lastserver
, char *reply
)
506 authenticateStateData
*r
= static_cast<authenticateStateData
*>(data
);
507 ntlm_helper_state_t
*helperstate
;
508 stateful_helper_callback_t result
= S_HELPER_UNKNOWN
;
510 auth_user_request_t
*auth_user_request
;
511 auth_user_t
*auth_user
;
512 ntlm_user_t
*ntlm_user
;
513 ntlm_request_t
*ntlm_request
;
514 debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver
, reply
? reply
: "<NULL>");
516 if (!cbdataReferenceValid(r
->data
)) {
517 debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver
);
518 cbdataReferenceDone(r
->data
);
519 authenticateStateFree(r
);
520 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE
);
521 return S_HELPER_RELEASE
;
526 * TODO: this occurs when a helper crashes. We should clean
527 * up that helpers resources and queued requests.
529 fatal("authenticateNTLMHandleReply: called with no result string\n");
532 /* seperate out the useful data */
533 if (strncasecmp(reply
, "TT ", 3) == 0) {
535 /* we have been given a Challenge */
536 /* we should check we weren't given an empty challenge */
537 /* copy the challenge to the state data */
538 helperstate
= static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(static_cast<helper_stateful_server
*>(lastserver
)));
540 if (helperstate
== NULL
)
541 fatal("lost NTLM helper state! quitting\n");
543 helperstate
->challenge
= xstrndup(reply
, NTLM_CHALLENGE_SZ
+ 5);
545 helperstate
->challengeuses
= 0;
547 helperstate
->renewed
= squid_curtime
;
549 /* and we satisfy the request that happended on the refresh boundary */
550 /* note this code is now in two places FIXME */
551 assert(r
->auth_user_request
!= NULL
);
553 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
555 auth_user_request
= r
->auth_user_request
;
557 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
559 assert(ntlm_request
!= NULL
);
561 result
= S_HELPER_DEFER
;
563 /* reserve the server for future authentication */
564 ntlm_request
->authserver_deferred
= 1;
566 debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver
);
568 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_NEGOTIATE
);
570 ntlm_request
->authserver
= static_cast<helper_stateful_server
*>(lastserver
);
572 ntlm_request
->authchallenge
= xstrndup(reply
, NTLM_CHALLENGE_SZ
+ 5);
573 } else if (strncasecmp(reply
, "AF ", 3) == 0) {
574 /* we're finished, release the helper */
576 assert(r
->auth_user_request
!= NULL
);
577 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
578 auth_user_request
= r
->auth_user_request
;
579 assert(auth_user_request
->scheme_data
!= NULL
);
580 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
581 auth_user
= auth_user_request
->auth_user
;
582 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user_request
->auth_user
->scheme_data
);
583 assert(ntlm_user
!= NULL
);
584 result
= S_HELPER_RELEASE
;
585 /* we only expect OK when finishing the handshake */
586 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_RESPONSE
);
587 ntlm_user
->username
= xstrndup(reply
, MAX_LOGIN_SZ
);
588 ntlm_request
->authserver
= NULL
;
589 #ifdef NTLM_FAIL_OPEN
591 } else if (strncasecmp(reply
, "LD ", 3) == 0) {
592 /* This is a variant of BH, which rather than deny access
593 * allows the user through. The helper is starved and then refreshed
594 * via YR, all pending authentications are likely to fail also.
595 * It is meant for those helpers which occasionally fail for
596 * no reason at all (casus belli, NTLMSSP helper on NT domain,
597 * failing about 1 auth out of 1k.
598 * The code is a merge from the BH case with snippets of the AF
600 /* AF code: mark user as authenticated */
602 assert(r
->auth_user_request
!= NULL
);
603 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
604 auth_user_request
= r
->auth_user_request
;
605 assert(auth_user_request
->scheme_data
!= NULL
);
606 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
607 auth_user
= auth_user_request
->auth_user
;
608 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user_request
->auth_user
->scheme_data
);
609 assert(ntlm_user
!= NULL
);
610 result
= S_HELPER_RELEASE
;
611 /* we only expect LD when finishing the handshake */
612 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_RESPONSE
);
613 ntlm_user
->username
= xstrndup(reply
, MAX_LOGIN_SZ
);
614 helperstate
= helperStatefulServerGetData(ntlm_request
->authserver
);
615 ntlm_request
->authserver
= NULL
;
616 /* BH code: mark helper as broken */
617 /* mark it for starving */
618 helperstate
->starve
= 1;
621 } else if (strncasecmp(reply
, "NA ", 3) == 0) {
622 /* TODO: only work with auth_user here if it exists */
623 assert(r
->auth_user_request
!= NULL
);
624 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
625 auth_user_request
= r
->auth_user_request
;
626 auth_user
= auth_user_request
->auth_user
;
627 assert(auth_user
!= NULL
);
628 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
629 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
630 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
631 /* todo: action of Negotiate state on error */
632 result
= S_HELPER_RELEASE
; /*some error has occured. no more requests */
633 ntlm_request
->authserver
= NULL
;
634 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply
);
635 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
637 if ((t
= strchr(reply
, ' '))) /* strip after a space */
639 } else if (strncasecmp(reply
, "NA", 2) == 0) {
640 /* NTLM Helper protocol violation! */
641 fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
642 } else if (strncasecmp(reply
, "BH ", 3) == 0) {
643 /* TODO kick off a refresh process. This can occur after a YR or after
644 * a KK. If after a YR release the helper and resubmit the request via
645 * Authenticate NTLM start.
646 * If after a KK deny the user's request w/ 407 and mark the helper as
648 assert(r
->auth_user_request
!= NULL
);
649 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
650 auth_user_request
= r
->auth_user_request
;
651 auth_user
= auth_user_request
->auth_user
;
652 assert(auth_user
!= NULL
);
653 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
654 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
655 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
656 result
= S_HELPER_RELEASE
; /*some error has occured. no more requests for
658 assert(ntlm_request
->authserver
? ntlm_request
->authserver
== lastserver
: 1);
659 helperstate
= static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(ntlm_request
->authserver
));
660 ntlm_request
->authserver
= NULL
;
662 if (ntlm_request
->auth_state
== AUTHENTICATE_STATE_NEGOTIATE
) {
663 /* The helper broke on YR. It automatically
665 debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver
, reply
);
666 /* mark it for starving */
667 helperstate
->starve
= 1;
668 /* resubmit the request. This helper is currently busy, so we will get
669 * a different one. Our auth state stays the same */
670 authenticateNTLMStart(auth_user_request
, r
->handler
, r
->data
);
671 /* don't call the callback */
672 cbdataReferenceDone(r
->data
);
673 authenticateStateFree(r
);
674 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result
);
678 /* the helper broke on a KK */
679 /* first the standard KK stuff */
680 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply
);
682 if ((t
= strchr(reply
, ' '))) /* strip after a space */
685 /* now we mark the helper for resetting. */
686 helperstate
->starve
= 1;
688 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
690 /* TODO: only work with auth_user here if it exists */
691 /* TODO: take the request state into consideration */
692 assert(r
->auth_user_request
!= NULL
);
693 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
694 auth_user_request
= r
->auth_user_request
;
695 auth_user
= auth_user_request
->auth_user
;
696 assert(auth_user
!= NULL
);
697 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
698 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
699 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
700 debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply
);
701 /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
702 /* restart the authentication process */
703 ntlm_request
->auth_state
= AUTHENTICATE_STATE_NONE
;
704 assert(ntlm_request
->authserver
? ntlm_request
->authserver
== lastserver
: 1);
705 ntlm_request
->authserver
= NULL
;
708 r
->handler(r
->data
, NULL
);
709 cbdataReferenceDone(r
->data
);
710 authenticateStateFree(r
);
711 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result
);
716 authenticateNTLMStats(StoreEntry
* sentry
)
718 storeAppendPrintf(sentry
, "NTLM Authenticator Statistics:\n");
719 helperStatefulStats(sentry
, ntlmauthenticators
);
722 /* is a particular challenge still valid ? */
724 authenticateNTLMValidChallenge(ntlm_helper_state_t
* helperstate
)
726 debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate
->challenge
? "Valid" : "Invalid");
728 if (helperstate
->challenge
== NULL
)
734 /* does our policy call for changing the challenge now? */
736 authenticateNTLMChangeChallenge_p(ntlm_helper_state_t
* helperstate
)
738 /* don't check for invalid challenges just for expiry choices */
739 /* this is needed because we have to starve the helper until all old
740 * requests have been satisfied */
742 if (!helperstate
->renewed
) {
743 /* first use, no challenge has been set. Without this check, it will
745 debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
749 if (helperstate
->challengeuses
> ntlmConfig
->challengeuses
) {
750 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate
->challengeuses
, ntlmConfig
->challengeuses
);
754 if (helperstate
->renewed
+ ntlmConfig
->challengelifetime
< squid_curtime
) {
755 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime
- (helperstate
->renewed
+ ntlmConfig
->challengelifetime
)));
759 debug(29, 9) ("Challenge is to be reused\n");
763 /* send the initial data to a stateful ntlm authenticator module */
765 authenticateNTLMStart(auth_user_request_t
* auth_user_request
, RH
* handler
, void *data
)
767 authenticateStateData
*r
= NULL
;
768 helper_stateful_server
*server
;
769 ntlm_helper_state_t
*helperstate
;
771 char *sent_string
= NULL
;
772 ntlm_user_t
*ntlm_user
;
773 ntlm_request_t
*ntlm_request
;
774 auth_user_t
*auth_user
;
776 assert(auth_user_request
);
777 auth_user
= auth_user_request
->auth_user
;
778 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
779 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
781 assert(ntlm_request
);
784 assert(auth_user
->auth_type
= AUTH_NTLM
);
785 debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", ntlm_request
->auth_state
);
787 switch (ntlm_request
->auth_state
) {
789 case AUTHENTICATE_STATE_NEGOTIATE
:
790 sent_string
= ntlm_request
->ntlmnegotiate
;
793 case AUTHENTICATE_STATE_RESPONSE
:
794 sent_string
= ntlm_request
->ntlmauthenticate
;
795 assert(ntlm_request
->authserver
);
796 debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", ntlm_request
->authserver
);
800 fatal("Invalid authenticate state for NTLMStart");
803 while (!xisspace(*sent_string
)) /*trim NTLM */
806 while (xisspace(*sent_string
)) /*trim leading spaces */
809 debug(29, 9) ("authenticateNTLMStart: state '%d'\n", ntlm_request
->auth_state
);
811 debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string
);
813 if (ntlmConfig
->authenticate
== NULL
) {
814 debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string
);
819 /* this is ugly TODO: move the challenge generation routines to their own function and
820 * tidy the logic up to make use of the efficiency we now have */
821 switch (ntlm_request
->auth_state
) {
823 case AUTHENTICATE_STATE_NEGOTIATE
:
825 * 1: get a helper server
826 * 2: does it have a challenge?
827 * 3: tell it to get a challenge, or give ntlmauthdone the challenge
829 server
= helperStatefulDefer(ntlmauthenticators
);
830 helperstate
= server
? static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(server
)) : NULL
;
832 while ((server
!= NULL
) && authenticateNTLMChangeChallenge_p(helperstate
)) {
833 /* flag this helper for challenge changing */
834 helperstate
->starve
= 1;
835 /* and release the deferred request */
836 helperStatefulReleaseServer(server
);
837 /* Get another deferrable server */
838 server
= helperStatefulDefer(ntlmauthenticators
);
839 helperstate
= server
? static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(server
)) : NULL
;
843 debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
845 ntlm_request
->authserver
= server
;
847 /* tell the log what helper we have been given */
848 debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server
);
850 /* server and valid challenge? */
851 if ((server
== NULL
) || !authenticateNTLMValidChallenge(helperstate
)) {
852 /* No server, or server with invalid challenge */
853 r
= cbdataAlloc(authenticateStateData
);
854 r
->handler
= handler
;
855 r
->data
= cbdataReference(data
);
856 r
->auth_user_request
= auth_user_request
;
858 if (server
== NULL
) {
859 helperStatefulSubmit(ntlmauthenticators
, NULL
, authenticateNTLMHandleplaceholder
, r
, NULL
);
861 /* Server with invalid challenge */
862 snprintf(buf
, 8192, "YR\n");
863 helperStatefulSubmit(ntlmauthenticators
, buf
, authenticateNTLMHandleReply
, r
, ntlm_request
->authserver
);
866 /* (server != NULL and we have a valid challenge) */
867 /* TODO: turn the below into a function and call from here and handlereply */
868 /* increment the challenge uses */
869 helperstate
->challengeuses
++;
870 /* assign the challenge */
871 ntlm_request
->authchallenge
= xstrndup(helperstate
->challenge
, NTLM_CHALLENGE_SZ
+ 5);
872 /* we're not actually submitting a request, so we need to release the helper
873 * should the connection close unexpectedly
875 ntlm_request
->authserver_deferred
= 1;
881 case AUTHENTICATE_STATE_RESPONSE
:
882 r
= cbdataAlloc(authenticateStateData
);
883 r
->handler
= handler
;
884 r
->data
= cbdataReference(data
);
885 r
->auth_user_request
= auth_user_request
;
886 snprintf(buf
, 8192, "KK %s\n", sent_string
);
887 /* getting rid of deferred request status */
888 ntlm_request
->authserver_deferred
= 0;
889 helperStatefulSubmit(ntlmauthenticators
, buf
, authenticateNTLMHandleReply
, r
, ntlm_request
->authserver
);
890 debug(29, 9) ("authenticateNTLMstart: finished\n");
894 fatal("Invalid authenticate state for NTLMStart");
898 /* callback used by stateful helper routines */
900 authenticateNTLMHelperServerAvailable(void *data
)
902 ntlm_helper_state_t
*statedata
= static_cast<ntlm_helper_state_t
*>(data
);
904 if (statedata
!= NULL
) {
905 if (statedata
->starve
) {
906 debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
909 debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
914 debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
919 authenticateNTLMHelperServerOnEmpty(void *data
)
921 ntlm_helper_state_t
*statedata
= static_cast<ntlm_helper_state_t
*>(data
);
923 if (statedata
== NULL
)
926 if (statedata
->starve
) {
927 /* we have been starving the helper */
928 debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
929 statedata
->starve
= 0;
930 statedata
->challengeuses
= 0;
931 statedata
->renewed
= 0;
932 xfree(statedata
->challenge
);
933 statedata
->challenge
= NULL
;
938 /* clear the NTLM helper of being reserved for future requests */
940 authenticateNTLMReleaseServer(auth_user_request_t
* auth_user_request
)
942 ntlm_request_t
*ntlm_request
;
943 assert(auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
944 assert(auth_user_request
->scheme_data
!= NULL
);
945 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
946 debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request
->authserver
);
947 helperStatefulReleaseServer(ntlm_request
->authserver
);
948 ntlm_request
->authserver
= NULL
;
951 /* clear any connection related authentication details */
953 authenticateNTLMOnCloseConnection(ConnStateData
* conn
)
955 ntlm_request_t
*ntlm_request
;
956 assert(conn
!= NULL
);
958 if (conn
->auth_user_request
!= NULL
) {
959 assert(conn
->auth_user_request
->scheme_data
!= NULL
);
960 ntlm_request
= static_cast< ntlm_request_t
*>(conn
->auth_user_request
->scheme_data
);
961 assert(ntlm_request
->conn
== conn
);
963 if (ntlm_request
->authserver
!= NULL
&& ntlm_request
->authserver_deferred
)
964 authenticateNTLMReleaseServer(conn
->auth_user_request
);
966 /* unlock the connection based lock */
967 debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
969 /* This still breaks the abstraction, but is at least read only now */
970 /* Ensure that the auth user request will be getting closed */
971 /* IFF we start persisting the struct after the conn closes - say for logging
972 * then this test may become invalid
974 assert(authenticateRequestRefCount(conn
->auth_user_request
) == 1);
976 authenticateAuthUserRequestUnlock(conn
->auth_user_request
);
978 conn
->auth_user_request
= NULL
;
982 /* authenticateUserUsername: return a pointer to the username in the */
984 authenticateNTLMUsername(auth_user_t
const * auth_user
)
986 ntlm_user_t
*ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
989 return ntlm_user
->username
;
994 /* NTLMLastHeader: return a pointer to the last header used in authenticating
995 * the request/conneciton
998 NTLMLastHeader(auth_user_request_t
* auth_user_request
)
1000 ntlm_request_t
*ntlm_request
;
1001 assert(auth_user_request
!= NULL
);
1002 assert(auth_user_request
->scheme_data
!= NULL
);
1003 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
1004 return ntlm_request
->ntlmauthenticate
;
1008 * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
1009 * Auth_user structure.
1013 authenticateDecodeNTLMAuth(auth_user_request_t
* auth_user_request
, const char *proxy_auth
)
1016 assert(auth_user_request
->auth_user
== NULL
);
1017 auth_user_request
->auth_user
= authenticateAuthUserNew("ntlm");
1018 auth_user_request
->auth_user
->auth_type
= AUTH_NTLM
;
1019 auth_user_request
->auth_user
->scheme_data
= memPoolAlloc(ntlm_user_pool
);
1020 auth_user_request
->scheme_data
= memPoolAlloc(ntlm_request_pool
);
1021 memset(auth_user_request
->scheme_data
, '\0', sizeof(ntlm_request_t
));
1022 /* lock for the auth_user_request link */
1023 authenticateAuthUserLock(auth_user_request
->auth_user
);
1024 node
= dlinkNodeNew();
1025 dlinkAdd(auth_user_request
, node
, &auth_user_request
->auth_user
->requests
);
1027 /* all we have to do is identify that it's NTLM - the helper does the rest */
1028 debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
1033 authenticateNTLMcmpUsername(ntlm_user_t
* u1
, ntlm_user_t
* u2
)
1035 return strcmp(u1
->username
, u2
->username
);
1039 /* there is a known race where a single client recieves the same challenge
1040 * and sends the same response to squid on a single select cycle.
1041 * Check for this and if found ignore the new link
1044 authenticateProxyAuthCacheAddLink(const char *key
, auth_user_t
* auth_user
)
1047 struct ProxyAuthCachePointer
*proxy_auth_hash
;
1049 ntlm_user_t
*ntlm_user
;
1050 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1051 node
= ntlm_user
->proxy_auth_list
.head
;
1052 /* prevent duplicates */
1056 if (!strcmp(key
, (char const *)((struct ProxyAuthCachePointer
*) node
->data
)->key
))
1062 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(memPoolAlloc(ntlm_user_hash_pool
));
1063 proxy_auth_hash
->key
= xstrdup(key
);
1064 proxy_auth_hash
->auth_user
= auth_user
;
1065 dlinkAddTail(proxy_auth_hash
, &proxy_auth_hash
->link
, &ntlm_user
->proxy_auth_list
);
1066 hash_join(proxy_auth_cache
, (hash_link
*) proxy_auth_hash
);
1071 authNTLMAuthenticated(auth_user_request_t
* auth_user_request
)
1073 ntlm_request_t
*ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
1075 if (ntlm_request
->auth_state
== AUTHENTICATE_STATE_DONE
)
1078 debug(29, 9) ("User not fully authenticated.\n");
1084 authenticateNTLMAuthenticateUser(auth_user_request_t
* auth_user_request
, request_t
* request
, ConnStateData
* conn
, http_hdr_type type
)
1086 const char *proxy_auth
;
1088 struct ProxyAuthCachePointer
*proxy_auth_hash
= NULL
;
1089 auth_user_hash_pointer
*usernamehash
;
1090 auth_user_t
*auth_user
;
1091 ntlm_request_t
*ntlm_request
;
1092 ntlm_user_t
*ntlm_user
;
1093 LOCAL_ARRAY(char, ntlmhash
, NTLM_CHALLENGE_SZ
* 2);
1095 proxy_auth
= httpHeaderGetStr(&request
->header
, type
);
1097 auth_user
= auth_user_request
->auth_user
;
1099 assert(auth_user
->auth_type
== AUTH_NTLM
);
1100 assert(auth_user
->scheme_data
!= NULL
);
1101 assert(auth_user_request
->scheme_data
!= NULL
);
1102 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1103 ntlm_request
= static_cast< ntlm_request_t
*>(auth_user_request
->scheme_data
);
1104 /* Check that we are in the client side, where we can generate
1105 * auth challenges */
1108 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
1109 debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
1113 switch (ntlm_request
->auth_state
) {
1115 case AUTHENTICATE_STATE_NONE
:
1116 /* we've recieved a negotiate request. pass to a helper */
1117 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth
);
1118 ntlm_request
->auth_state
= AUTHENTICATE_STATE_NEGOTIATE
;
1119 ntlm_request
->ntlmnegotiate
= xstrndup(proxy_auth
, NTLM_CHALLENGE_SZ
+ 5);
1120 conn
->auth_type
= AUTH_NTLM
;
1121 conn
->auth_user_request
= auth_user_request
;
1122 ntlm_request
->conn
= conn
;
1123 /* and lock for the connection duration */
1124 debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
1125 authenticateAuthUserRequestLock(auth_user_request
);
1129 case AUTHENTICATE_STATE_NEGOTIATE
:
1130 ntlm_request
->auth_state
= AUTHENTICATE_STATE_CHALLENGE
;
1131 /* We _MUST_ have the auth challenge by now */
1132 assert(ntlm_request
->authchallenge
);
1136 case AUTHENTICATE_STATE_CHALLENGE
:
1137 /* we should have recieved a NTLM challenge. pass it to the same
1139 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth
);
1140 /* do a cache lookup here. If it matches it's a successful ntlm
1141 * challenge - release the helper and use the existing auth_user
1144 if (strncmp("NTLM ", proxy_auth
, 5) == 0) {
1145 ntlm_request
->ntlmauthenticate
= xstrdup(proxy_auth
);
1147 fatal("Incorrect scheme in auth header\n");
1148 /* TODO: more fault tolerance.. reset the auth scheme here */
1151 /* cache entries have authenticateauthheaderchallengestring */
1152 snprintf(ntlmhash
, sizeof(ntlmhash
) - 1, "%s%s",
1153 ntlm_request
->ntlmauthenticate
,
1154 ntlm_request
->authchallenge
);
1156 /* see if we already know this user's authenticate */
1157 debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash
);
1159 assert(proxy_auth_cache
!= NULL
);
1161 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(hash_lookup(proxy_auth_cache
, ntlmhash
));
1163 if (!proxy_auth_hash
) { /* not in the hash table */
1164 debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
1165 ntlm_request
->auth_state
= AUTHENTICATE_STATE_RESPONSE
;
1166 /* verify with the ntlm helper */
1168 debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
1169 /* throw away the temporary entry */
1170 ntlm_request
->authserver_deferred
= 0;
1171 authenticateNTLMReleaseServer(auth_user_request
);
1172 authenticateAuthUserMerge(auth_user
, proxy_auth_hash
->auth_user
);
1173 auth_user
= proxy_auth_hash
->auth_user
;
1174 auth_user_request
->auth_user
= auth_user
;
1175 ntlm_request
->auth_state
= AUTHENTICATE_STATE_DONE
;
1177 debug(29, 9) ("found matching cache entry\n");
1178 assert(auth_user
->auth_type
== AUTH_NTLM
);
1179 /* get the existing entries details */
1180 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1181 debug(29, 9) ("Username to be used is %s\n", ntlm_user
->username
);
1182 /* on ntlm auth we do not unlock the auth_user until the
1183 * connection is dropped. Thank MS for this quirk */
1184 auth_user
->expiretime
= current_time
.tv_sec
;
1190 case AUTHENTICATE_STATE_RESPONSE
:
1191 /* auth-challenge pair cache miss. We've just got the response from the helper */
1192 /*add to cache and let them through */
1193 ntlm_request
->auth_state
= AUTHENTICATE_STATE_DONE
;
1194 /* this connection is authenticated */
1195 debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
1196 ntlm_request
->authchallenge
,
1197 ntlm_request
->ntlmauthenticate
,
1198 ntlm_user
->username
);
1199 /* cache entries have authenticateauthheaderchallengestring */
1200 snprintf(ntlmhash
, sizeof(ntlmhash
) - 1, "%s%s",
1201 ntlm_request
->ntlmauthenticate
,
1202 ntlm_request
->authchallenge
);
1203 /* see if this is an existing user with a different proxy_auth
1206 if ((usernamehash
= static_cast<AuthUserHashPointer
*>(hash_lookup(proxy_auth_username_cache
, ntlm_user
->username
)))
1208 while ((authUserHashPointerUser(usernamehash
)->auth_type
!= auth_user
->auth_type
) && (usernamehash
->next
) && !authenticateNTLMcmpUsername(static_cast<ntlm_user_t
*>(authUserHashPointerUser(usernamehash
)->scheme_data
), ntlm_user
)
1210 usernamehash
= static_cast<AuthUserHashPointer
*>(usernamehash
->next
);
1211 if (authUserHashPointerUser(usernamehash
)->auth_type
== auth_user
->auth_type
) {
1213 * add another link from the new proxy_auth to the
1214 * auth_user structure and update the information */
1215 assert(proxy_auth_hash
== NULL
);
1216 authenticateProxyAuthCacheAddLink(ntlmhash
, authUserHashPointerUser(usernamehash
));
1217 /* we can't seamlessly recheck the username due to the
1218 * challenge nature of the protocol. Just free the
1219 * temporary auth_user */
1220 authenticateAuthUserMerge(auth_user
, authUserHashPointerUser(usernamehash
));
1221 auth_user
= authUserHashPointerUser(usernamehash
);
1222 auth_user_request
->auth_user
= auth_user
;
1225 /* store user in hash's */
1226 authenticateUserNameCacheAdd(auth_user
);
1227 authenticateProxyAuthCacheAddLink(ntlmhash
, auth_user
);
1230 /* set these to now because this is either a new login from an
1231 * existing user or a new user */
1232 auth_user
->expiretime
= current_time
.tv_sec
;
1236 case AUTHENTICATE_STATE_DONE
:
1237 fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
1240 case AUTHENTICATE_STATE_FAILED
:
1241 /* we've failed somewhere in authentication */
1242 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth
);