3 * $Id: auth_ntlm.cc,v 1.31 2003/03/04 01:40:55 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"
45 #include "client_side.h"
47 extern AUTHSSETUP authSchemeSetup_ntlm
;
50 authenticateStateFree(authenticateStateData
* r
)
56 static HLPSCB authenticateNTLMHandleReply
;
57 static HLPSCB authenticateNTLMHandleplaceholder
;
58 static AUTHSACTIVE authenticateNTLMActive
;
59 static AUTHSAUTHUSER authenticateNTLMAuthenticateUser
;
60 static AUTHSCONFIGURED authNTLMConfigured
;
61 static AUTHSFIXERR authenticateNTLMFixErrorHeader
;
62 static AUTHSFREE authenticateNTLMFreeUser
;
63 static AUTHSDECODE authenticateDecodeNTLMAuth
;
64 static AUTHSDUMP authNTLMCfgDump
;
65 static AUTHSFREECONFIG authNTLMFreeConfig
;
66 static AUTHSINIT authNTLMInit
;
67 static AUTHSONCLOSEC authenticateNTLMOnCloseConnection
;
68 static AUTHSCONNLASTHEADER NTLMLastHeader
;
69 static AUTHSUSERNAME authenticateNTLMUsername
;
70 static AUTHSPARSE authNTLMParse
;
71 static AUTHSSTART authenticateNTLMStart
;
72 static AUTHSSTATS authenticateNTLMStats
;
73 static AUTHSSHUTDOWN authNTLMDone
;
75 /* helper callbacks to handle per server state data */
76 static HLPSAVAIL authenticateNTLMHelperServerAvailable
;
77 static HLPSONEQ authenticateNTLMHelperServerOnEmpty
;
79 static statefulhelper
*ntlmauthenticators
= NULL
;
81 CBDATA_TYPE(authenticateStateData
);
83 static int authntlm_initialised
= 0;
85 static MemPool
*ntlm_helper_state_pool
= NULL
;
86 static MemPool
*ntlm_user_pool
= NULL
;
87 static MemPool
*ntlm_user_hash_pool
= NULL
;
89 static auth_ntlm_config
*ntlmConfig
= NULL
;
91 static hash_table
*proxy_auth_cache
= NULL
;
102 debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
104 if (ntlmauthenticators
)
105 helperStatefulShutdown(ntlmauthenticators
);
107 authntlm_initialised
= 0;
112 if (ntlmauthenticators
)
113 helperStatefulFree(ntlmauthenticators
);
115 ntlmauthenticators
= NULL
;
119 if (ntlm_helper_state_pool
) {
120 memPoolDestroy(&ntlm_helper_state_pool
);
123 if (ntlm_user_pool
) {
124 memPoolDestroy(&ntlm_user_pool
);
128 debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
131 /* free any allocated configuration details */
133 authNTLMFreeConfig(authScheme
* scheme
)
135 if (ntlmConfig
== NULL
)
138 assert(ntlmConfig
== scheme
->scheme_data
);
140 if (ntlmConfig
->authenticate
)
141 wordlistDestroy(&ntlmConfig
->authenticate
);
149 authNTLMCfgDump(StoreEntry
* entry
, const char *name
, authScheme
* scheme
)
151 auth_ntlm_config
*config
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
152 wordlist
*list
= config
->authenticate
;
153 storeAppendPrintf(entry
, "%s %s", name
, "ntlm");
155 while (list
!= NULL
) {
156 storeAppendPrintf(entry
, " %s", list
->key
);
160 storeAppendPrintf(entry
, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
161 name
, "ntlm", config
->authenticateChildren
,
162 name
, "ntlm", config
->challengeuses
,
163 name
, "ntlm", (int) config
->challengelifetime
);
168 authNTLMParse(authScheme
* scheme
, int n_configured
, char *param_str
)
170 if (scheme
->scheme_data
== NULL
) {
171 assert(ntlmConfig
== NULL
);
172 /* this is the first param to be found */
173 scheme
->scheme_data
= xmalloc(sizeof(auth_ntlm_config
));
174 memset(scheme
->scheme_data
, 0, sizeof(auth_ntlm_config
));
175 ntlmConfig
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
176 ntlmConfig
->authenticateChildren
= 5;
177 ntlmConfig
->challengeuses
= 0;
178 ntlmConfig
->challengelifetime
= 60;
181 ntlmConfig
= static_cast<auth_ntlm_config
*>(scheme
->scheme_data
);
183 if (strcasecmp(param_str
, "program") == 0) {
184 if (ntlmConfig
->authenticate
)
185 wordlistDestroy(&ntlmConfig
->authenticate
);
187 parse_wordlist(&ntlmConfig
->authenticate
);
189 requirePathnameExists("authparam ntlm program", ntlmConfig
->authenticate
->key
);
190 } else if (strcasecmp(param_str
, "children") == 0) {
191 parse_int(&ntlmConfig
->authenticateChildren
);
192 } else if (strcasecmp(param_str
, "max_challenge_reuses") == 0) {
193 parse_int(&ntlmConfig
->challengeuses
);
194 } else if (strcasecmp(param_str
, "max_challenge_lifetime") == 0) {
195 parse_time_t(&ntlmConfig
->challengelifetime
);
197 debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str
);
201 * disable client side request pipelining. There is a race with
202 * NTLM when the client sends a second request on an NTLM
203 * connection before the authenticate challenge is sent. With
204 * this patch, the client may fail to authenticate, but squid's
205 * state will be preserved. Caveats: this should be a post-parse
206 * test, but that can wait for the modular parser to be integrated.
208 if (ntlmConfig
->authenticate
)
209 Config
.onoff
.pipeline_prefetch
= 0;
214 authSchemeSetup_ntlm(authscheme_entry_t
* authscheme
)
216 assert(!authntlm_initialised
);
217 authscheme
->Active
= authenticateNTLMActive
;
218 authscheme
->configured
= authNTLMConfigured
;
219 authscheme
->parse
= authNTLMParse
;
220 authscheme
->dump
= authNTLMCfgDump
;
221 authscheme
->requestFree
= NULL
;
222 authscheme
->freeconfig
= authNTLMFreeConfig
;
223 authscheme
->init
= authNTLMInit
;
224 authscheme
->authAuthenticate
= authenticateNTLMAuthenticateUser
;
225 authscheme
->authenticated
= NULL
;
226 authscheme
->authFixHeader
= authenticateNTLMFixErrorHeader
;
227 authscheme
->FreeUser
= authenticateNTLMFreeUser
;
228 authscheme
->authStart
= authenticateNTLMStart
;
229 authscheme
->authStats
= authenticateNTLMStats
;
230 authscheme
->authUserUsername
= authenticateNTLMUsername
;
231 authscheme
->getdirection
= NULL
;
232 authscheme
->decodeauth
= authenticateDecodeNTLMAuth
;
233 authscheme
->donefunc
= authNTLMDone
;
234 authscheme
->oncloseconnection
= authenticateNTLMOnCloseConnection
;
235 authscheme
->authConnLastHeader
= NTLMLastHeader
;
238 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
241 authNTLMInit(authScheme
* scheme
)
243 static int ntlminit
= 0;
245 if (ntlmConfig
->authenticate
) {
246 if (!ntlm_helper_state_pool
)
247 ntlm_helper_state_pool
= memPoolCreate("NTLM Helper State data", sizeof(ntlm_helper_state_t
));
250 ntlm_user_pool
= memPoolCreate("NTLM Scheme User Data", sizeof(ntlm_user_t
));
252 if (!ntlm_user_hash_pool
)
254 ntlm_user_hash_pool
= memPoolCreate("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer
));
256 authntlm_initialised
= 1;
258 if (ntlmauthenticators
== NULL
)
259 ntlmauthenticators
= helperStatefulCreate("ntlmauthenticator");
261 if (!proxy_auth_cache
)
262 proxy_auth_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
264 assert(proxy_auth_cache
);
266 ntlmauthenticators
->cmdline
= ntlmConfig
->authenticate
;
268 ntlmauthenticators
->n_to_start
= ntlmConfig
->authenticateChildren
;
270 ntlmauthenticators
->ipc_type
= IPC_STREAM
;
272 ntlmauthenticators
->datapool
= ntlm_helper_state_pool
;
274 ntlmauthenticators
->IsAvailable
= authenticateNTLMHelperServerAvailable
;
276 ntlmauthenticators
->OnEmptyQueue
= authenticateNTLMHelperServerOnEmpty
;
278 helperStatefulOpenServers(ntlmauthenticators
);
281 * TODO: In here send the initial YR to preinitialise the
285 * Think about this... currently we ask when the challenge
289 cachemgrRegister("ntlmauthenticator",
290 "NTLM User Authenticator Stats",
291 authenticateNTLMStats
, 0, 1);
295 CBDATA_INIT_TYPE(authenticateStateData
);
300 authenticateNTLMActive()
302 return (authntlm_initialised
== 1) ? 1 : 0;
309 if ((ntlmConfig
!= NULL
) && (ntlmConfig
->authenticate
!= NULL
) && (ntlmConfig
->authenticateChildren
!= 0) && (ntlmConfig
->challengeuses
> -1) && (ntlmConfig
->challengelifetime
> -1)) {
310 debug(29, 9) ("authNTLMConfigured: returning configured\n");
314 debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
320 ntlm_request_t::direction()
322 /* null auth_user is checked for by authenticateDirection */
324 switch (auth_state
) {
326 /* no progress at all. */
328 case AUTHENTICATE_STATE_NONE
:
329 debug(29, 1) ("ntlm_request_t::direction: called before NTLM Authenticate!. Report a bug to squid-dev.\n");
332 case AUTHENTICATE_STATE_FAILED
:
337 case AUTHENTICATE_STATE_NEGOTIATE
:
341 case AUTHENTICATE_STATE_RESPONSE
:
346 case AUTHENTICATE_STATE_CHALLENGE
:
351 case AUTHENTICATE_STATE_DONE
:
359 * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
360 * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
364 authenticateNTLMFixErrorHeader(auth_user_request_t
* auth_user_request
, HttpReply
* rep
, http_hdr_type type
, request_t
* request
)
366 ntlm_request_t
*ntlm_request
;
368 if (ntlmConfig
->authenticate
) {
369 /* New request, no user details */
371 if (auth_user_request
== NULL
) {
372 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type
);
373 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
374 /* drop the connection */
375 httpHeaderDelByName(&rep
->header
, "keep-alive");
376 /* NTLM has problems if the initial connection is not dropped
377 * I haven't checked the RFC compliance of this hack - RBCollins */
378 request
->flags
.proxy_keepalive
= 0;
380 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
381 assert (ntlm_request
);
383 switch (ntlm_request
->auth_state
) {
385 case AUTHENTICATE_STATE_NONE
:
387 case AUTHENTICATE_STATE_FAILED
:
388 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type
);
389 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
390 /* drop the connection */
391 httpHeaderDelByName(&rep
->header
, "keep-alive");
392 /* NTLM has problems if the initial connection is not dropped
393 * I haven't checked the RFC compliance of this hack - RBCollins */
394 request
->flags
.proxy_keepalive
= 0;
397 case AUTHENTICATE_STATE_CHALLENGE
:
398 /* we are 'waiting' for a response */
399 /* pass the challenge to the client */
400 debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type
, ntlm_request
->authchallenge
);
401 httpHeaderPutStrf(&rep
->header
, type
, "NTLM %s", ntlm_request
->authchallenge
);
405 debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request
->auth_state
);
406 fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
412 ntlm_request_t::~ntlm_request_t()
415 xfree(ntlmnegotiate
);
418 xfree(authchallenge
);
420 if (ntlmauthenticate
)
421 xfree(ntlmauthenticate
);
423 if (authserver
!= NULL
&& authserver_deferred
) {
424 debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", authserver
);
425 helperStatefulReleaseServer(authserver
);
431 authenticateNTLMFreeUser(auth_user_t
* auth_user
)
433 dlink_node
*link
, *tmplink
;
434 ntlm_user_t
*ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
435 ProxyAuthCachePointer
*proxy_auth_hash
;
437 debug(29, 5) ("authenticateNTLMFreeUser: Clearing NTLM scheme data\n");
439 if (ntlm_user
->username
)
440 xfree(ntlm_user
->username
);
442 /* were they linked in by one or more proxy-authenticate headers */
443 link
= ntlm_user
->proxy_auth_list
.head
;
446 debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link
->data
);
447 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(link
->data
);
450 dlinkDelete(tmplink
, &ntlm_user
->proxy_auth_list
);
451 hash_remove_link(proxy_auth_cache
, (hash_link
*) proxy_auth_hash
);
452 /* free the key (usually the proxy_auth header) */
453 xfree(proxy_auth_hash
->key
);
454 memPoolFree(ntlm_user_hash_pool
, proxy_auth_hash
);
457 memPoolFree(ntlm_user_pool
, ntlm_user
);
458 auth_user
->scheme_data
= NULL
;
461 static stateful_helper_callback_t
462 authenticateNTLMHandleplaceholder(void *data
, void *lastserver
, char *reply
)
464 authenticateStateData
*r
= static_cast<authenticateStateData
*>(data
);
465 stateful_helper_callback_t result
= S_HELPER_UNKNOWN
;
466 /* we should only be called for placeholder requests - which have no reply string */
467 assert(reply
== NULL
);
468 assert(r
->auth_user_request
);
469 /* standard callback stuff */
471 if (!cbdataReferenceValid(r
->data
)) {
472 debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
476 /* call authenticateNTLMStart to retry this request */
477 debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
479 authenticateNTLMStart(r
->auth_user_request
, r
->handler
, r
->data
);
481 cbdataReferenceDone(r
->data
);
483 authenticateStateFree(r
);
488 static stateful_helper_callback_t
489 authenticateNTLMHandleReply(void *data
, void *lastserver
, char *reply
)
491 authenticateStateData
*r
= static_cast<authenticateStateData
*>(data
);
492 ntlm_helper_state_t
*helperstate
;
493 stateful_helper_callback_t result
= S_HELPER_UNKNOWN
;
495 auth_user_request_t
*auth_user_request
;
496 auth_user_t
*auth_user
;
497 ntlm_user_t
*ntlm_user
;
498 ntlm_request_t
*ntlm_request
;
499 debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver
, reply
? reply
: "<NULL>");
501 if (!cbdataReferenceValid(r
->data
)) {
502 debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver
);
503 cbdataReferenceDone(r
->data
);
504 authenticateStateFree(r
);
505 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE
);
506 return S_HELPER_RELEASE
;
511 * TODO: this occurs when a helper crashes. We should clean
512 * up that helpers resources and queued requests.
514 fatal("authenticateNTLMHandleReply: called with no result string\n");
517 /* seperate out the useful data */
518 if (strncasecmp(reply
, "TT ", 3) == 0) {
520 /* we have been given a Challenge */
521 /* we should check we weren't given an empty challenge */
522 /* copy the challenge to the state data */
523 helperstate
= static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(static_cast<helper_stateful_server
*>(lastserver
)));
525 if (helperstate
== NULL
)
526 fatal("lost NTLM helper state! quitting\n");
528 helperstate
->challenge
= xstrndup(reply
, NTLM_CHALLENGE_SZ
+ 5);
530 helperstate
->challengeuses
= 0;
532 helperstate
->renewed
= squid_curtime
;
534 /* and we satisfy the request that happended on the refresh boundary */
535 /* note this code is now in two places FIXME */
536 assert(r
->auth_user_request
!= NULL
);
538 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
540 auth_user_request
= r
->auth_user_request
;
542 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
544 assert(ntlm_request
!= NULL
);
546 result
= S_HELPER_DEFER
;
548 /* reserve the server for future authentication */
549 ntlm_request
->authserver_deferred
= 1;
551 debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver
);
553 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_NEGOTIATE
);
555 ntlm_request
->authserver
= static_cast<helper_stateful_server
*>(lastserver
);
557 ntlm_request
->authchallenge
= xstrndup(reply
, NTLM_CHALLENGE_SZ
+ 5);
558 } else if (strncasecmp(reply
, "AF ", 3) == 0) {
559 /* we're finished, release the helper */
561 assert(r
->auth_user_request
!= NULL
);
562 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
563 auth_user_request
= r
->auth_user_request
;
564 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
565 assert(ntlm_request
);
566 auth_user
= auth_user_request
->auth_user
;
567 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user_request
->auth_user
->scheme_data
);
568 assert(ntlm_user
!= NULL
);
569 result
= S_HELPER_RELEASE
;
570 /* we only expect OK when finishing the handshake */
571 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_RESPONSE
);
572 ntlm_user
->username
= xstrndup(reply
, MAX_LOGIN_SZ
);
573 ntlm_request
->authserver
= NULL
;
574 #ifdef NTLM_FAIL_OPEN
576 } else if (strncasecmp(reply
, "LD ", 3) == 0) {
577 /* This is a variant of BH, which rather than deny access
578 * allows the user through. The helper is starved and then refreshed
579 * via YR, all pending authentications are likely to fail also.
580 * It is meant for those helpers which occasionally fail for
581 * no reason at all (casus belli, NTLMSSP helper on NT domain,
582 * failing about 1 auth out of 1k.
583 * The code is a merge from the BH case with snippets of the AF
585 /* AF code: mark user as authenticated */
587 assert(r
->auth_user_request
!= NULL
);
588 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
589 auth_user_request
= r
->auth_user_request
;
590 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
591 assert(ntlm_request
);
592 auth_user
= auth_user_request
->auth_user
;
593 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user_request
->auth_user
->scheme_data
);
594 assert(ntlm_user
!= NULL
);
595 result
= S_HELPER_RELEASE
;
596 /* we only expect LD when finishing the handshake */
597 assert(ntlm_request
->auth_state
== AUTHENTICATE_STATE_RESPONSE
);
598 ntlm_user
->username
= xstrndup(reply
, MAX_LOGIN_SZ
);
599 helperstate
= helperStatefulServerGetData(ntlm_request
->authserver
);
600 ntlm_request
->authserver
= NULL
;
601 /* BH code: mark helper as broken */
602 /* mark it for starving */
603 helperstate
->starve
= 1;
606 } else if (strncasecmp(reply
, "NA ", 3) == 0) {
607 /* TODO: only work with auth_user here if it exists */
608 assert(r
->auth_user_request
!= NULL
);
609 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
610 auth_user_request
= r
->auth_user_request
;
611 auth_user
= auth_user_request
->auth_user
;
612 assert(auth_user
!= NULL
);
613 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
614 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
615 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
616 /* todo: action of Negotiate state on error */
617 result
= S_HELPER_RELEASE
; /*some error has occured. no more requests */
618 ntlm_request
->authserver
= NULL
;
619 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply
);
620 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
622 if ((t
= strchr(reply
, ' '))) /* strip after a space */
624 } else if (strncasecmp(reply
, "NA", 2) == 0) {
625 /* NTLM Helper protocol violation! */
626 fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
627 } else if (strncasecmp(reply
, "BH ", 3) == 0) {
628 /* TODO kick off a refresh process. This can occur after a YR or after
629 * a KK. If after a YR release the helper and resubmit the request via
630 * Authenticate NTLM start.
631 * If after a KK deny the user's request w/ 407 and mark the helper as
633 assert(r
->auth_user_request
!= NULL
);
634 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
635 auth_user_request
= r
->auth_user_request
;
636 auth_user
= auth_user_request
->auth_user
;
637 assert(auth_user
!= NULL
);
638 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
639 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
640 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
641 /*some error has occured. no more requests for
643 result
= S_HELPER_RELEASE
;
644 assert(ntlm_request
->authserver
? ntlm_request
->authserver
== lastserver
: 1);
645 helperstate
= static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(ntlm_request
->authserver
));
646 ntlm_request
->authserver
= NULL
;
648 if (ntlm_request
->auth_state
== AUTHENTICATE_STATE_NEGOTIATE
) {
649 /* The helper broke on YR. It automatically
651 debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver
, reply
);
652 /* mark it for starving */
653 helperstate
->starve
= 1;
654 /* resubmit the request. This helper is currently busy, so we will get
655 * a different one. Our auth state stays the same */
656 authenticateNTLMStart(auth_user_request
, r
->handler
, r
->data
);
657 /* don't call the callback */
658 cbdataReferenceDone(r
->data
);
659 authenticateStateFree(r
);
660 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result
);
664 /* the helper broke on a KK */
665 /* first the standard KK stuff */
666 debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply
);
668 if ((t
= strchr(reply
, ' '))) /* strip after a space */
671 /* now we mark the helper for resetting. */
672 helperstate
->starve
= 1;
674 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
676 /* TODO: only work with auth_user here if it exists */
677 /* TODO: take the request state into consideration */
678 assert(r
->auth_user_request
!= NULL
);
679 assert(r
->auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
680 auth_user_request
= r
->auth_user_request
;
681 auth_user
= auth_user_request
->auth_user
;
682 assert(auth_user
!= NULL
);
683 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
684 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
685 assert((ntlm_user
!= NULL
) && (ntlm_request
!= NULL
));
686 debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply
);
687 /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
688 /* restart the authentication process */
689 ntlm_request
->auth_state
= AUTHENTICATE_STATE_NONE
;
690 assert(ntlm_request
->authserver
? ntlm_request
->authserver
== lastserver
: 1);
691 ntlm_request
->authserver
= NULL
;
694 r
->handler(r
->data
, NULL
);
695 cbdataReferenceDone(r
->data
);
696 authenticateStateFree(r
);
697 debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result
);
702 authenticateNTLMStats(StoreEntry
* sentry
)
704 storeAppendPrintf(sentry
, "NTLM Authenticator Statistics:\n");
705 helperStatefulStats(sentry
, ntlmauthenticators
);
708 /* is a particular challenge still valid ? */
710 authenticateNTLMValidChallenge(ntlm_helper_state_t
* helperstate
)
712 debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate
->challenge
? "Valid" : "Invalid");
714 if (helperstate
->challenge
== NULL
)
720 /* does our policy call for changing the challenge now? */
722 authenticateNTLMChangeChallenge_p(ntlm_helper_state_t
* helperstate
)
724 /* don't check for invalid challenges just for expiry choices */
725 /* this is needed because we have to starve the helper until all old
726 * requests have been satisfied */
728 if (!helperstate
->renewed
) {
729 /* first use, no challenge has been set. Without this check, it will
731 debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
735 if (helperstate
->challengeuses
> ntlmConfig
->challengeuses
) {
736 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate
->challengeuses
, ntlmConfig
->challengeuses
);
740 if (helperstate
->renewed
+ ntlmConfig
->challengelifetime
< squid_curtime
) {
741 debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime
- (helperstate
->renewed
+ ntlmConfig
->challengelifetime
)));
745 debug(29, 9) ("Challenge is to be reused\n");
749 /* send the initial data to a stateful ntlm authenticator module */
751 authenticateNTLMStart(auth_user_request_t
* auth_user_request
, RH
* handler
, void *data
)
753 authenticateStateData
*r
= NULL
;
754 helper_stateful_server
*server
;
755 ntlm_helper_state_t
*helperstate
;
757 char *sent_string
= NULL
;
758 ntlm_user_t
*ntlm_user
;
759 ntlm_request_t
*ntlm_request
;
760 auth_user_t
*auth_user
;
762 assert(auth_user_request
);
763 auth_user
= auth_user_request
->auth_user
;
764 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
765 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
767 assert(ntlm_request
);
770 assert(auth_user
->auth_type
= AUTH_NTLM
);
771 debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", ntlm_request
->auth_state
);
773 switch (ntlm_request
->auth_state
) {
775 case AUTHENTICATE_STATE_NEGOTIATE
:
776 sent_string
= ntlm_request
->ntlmnegotiate
;
779 case AUTHENTICATE_STATE_RESPONSE
:
780 sent_string
= ntlm_request
->ntlmauthenticate
;
781 assert(ntlm_request
->authserver
);
782 debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", ntlm_request
->authserver
);
786 fatal("Invalid authenticate state for NTLMStart");
789 while (!xisspace(*sent_string
)) /*trim NTLM */
792 while (xisspace(*sent_string
)) /*trim leading spaces */
795 debug(29, 9) ("authenticateNTLMStart: state '%d'\n", ntlm_request
->auth_state
);
797 debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string
);
799 if (ntlmConfig
->authenticate
== NULL
) {
800 debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string
);
805 /* this is ugly TODO: move the challenge generation routines to their own function and
806 * tidy the logic up to make use of the efficiency we now have */
807 switch (ntlm_request
->auth_state
) {
809 case AUTHENTICATE_STATE_NEGOTIATE
:
811 * 1: get a helper server
812 * 2: does it have a challenge?
813 * 3: tell it to get a challenge, or give ntlmauthdone the challenge
815 server
= helperStatefulDefer(ntlmauthenticators
);
816 helperstate
= server
? static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(server
)) : NULL
;
818 while ((server
!= NULL
) && authenticateNTLMChangeChallenge_p(helperstate
)) {
819 /* flag this helper for challenge changing */
820 helperstate
->starve
= 1;
821 /* and release the deferred request */
822 helperStatefulReleaseServer(server
);
823 /* Get another deferrable server */
824 server
= helperStatefulDefer(ntlmauthenticators
);
825 helperstate
= server
? static_cast<ntlm_helper_state_t
*>(helperStatefulServerGetData(server
)) : NULL
;
829 debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
831 ntlm_request
->authserver
= server
;
833 /* tell the log what helper we have been given */
834 debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server
);
836 /* server and valid challenge? */
837 if ((server
== NULL
) || !authenticateNTLMValidChallenge(helperstate
)) {
838 /* No server, or server with invalid challenge */
839 r
= cbdataAlloc(authenticateStateData
);
840 r
->handler
= handler
;
841 r
->data
= cbdataReference(data
);
842 r
->auth_user_request
= auth_user_request
;
844 if (server
== NULL
) {
845 helperStatefulSubmit(ntlmauthenticators
, NULL
, authenticateNTLMHandleplaceholder
, r
, NULL
);
847 /* Server with invalid challenge */
848 snprintf(buf
, 8192, "YR\n");
849 helperStatefulSubmit(ntlmauthenticators
, buf
, authenticateNTLMHandleReply
, r
, ntlm_request
->authserver
);
852 /* (server != NULL and we have a valid challenge) */
853 /* TODO: turn the below into a function and call from here and handlereply */
854 /* increment the challenge uses */
855 helperstate
->challengeuses
++;
856 /* assign the challenge */
857 ntlm_request
->authchallenge
= xstrndup(helperstate
->challenge
, NTLM_CHALLENGE_SZ
+ 5);
858 /* we're not actually submitting a request, so we need to release the helper
859 * should the connection close unexpectedly
861 ntlm_request
->authserver_deferred
= 1;
867 case AUTHENTICATE_STATE_RESPONSE
:
868 r
= cbdataAlloc(authenticateStateData
);
869 r
->handler
= handler
;
870 r
->data
= cbdataReference(data
);
871 r
->auth_user_request
= auth_user_request
;
872 snprintf(buf
, 8192, "KK %s\n", sent_string
);
873 /* getting rid of deferred request status */
874 ntlm_request
->authserver_deferred
= 0;
875 helperStatefulSubmit(ntlmauthenticators
, buf
, authenticateNTLMHandleReply
, r
, ntlm_request
->authserver
);
876 debug(29, 9) ("authenticateNTLMstart: finished\n");
880 fatal("Invalid authenticate state for NTLMStart");
884 /* callback used by stateful helper routines */
886 authenticateNTLMHelperServerAvailable(void *data
)
888 ntlm_helper_state_t
*statedata
= static_cast<ntlm_helper_state_t
*>(data
);
890 if (statedata
!= NULL
) {
891 if (statedata
->starve
) {
892 debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
895 debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
900 debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
905 authenticateNTLMHelperServerOnEmpty(void *data
)
907 ntlm_helper_state_t
*statedata
= static_cast<ntlm_helper_state_t
*>(data
);
909 if (statedata
== NULL
)
912 if (statedata
->starve
) {
913 /* we have been starving the helper */
914 debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
915 statedata
->starve
= 0;
916 statedata
->challengeuses
= 0;
917 statedata
->renewed
= 0;
918 xfree(statedata
->challenge
);
919 statedata
->challenge
= NULL
;
924 /* clear the NTLM helper of being reserved for future requests */
926 authenticateNTLMReleaseServer(auth_user_request_t
* auth_user_request
)
928 ntlm_request_t
*ntlm_request
;
929 assert(auth_user_request
->auth_user
->auth_type
== AUTH_NTLM
);
930 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
931 assert (ntlm_request
);
932 debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request
->authserver
);
933 helperStatefulReleaseServer(ntlm_request
->authserver
);
934 ntlm_request
->authserver
= NULL
;
937 /* clear any connection related authentication details */
939 authenticateNTLMOnCloseConnection(ConnStateData
* conn
)
941 ntlm_request_t
*ntlm_request
;
942 assert(conn
!= NULL
);
944 if (conn
->auth_user_request
!= NULL
) {
945 ntlm_request
= dynamic_cast< ntlm_request_t
*>(conn
->auth_user_request
->state());
946 assert (ntlm_request
);
947 assert(ntlm_request
->conn
== conn
);
949 if (ntlm_request
->authserver
!= NULL
&& ntlm_request
->authserver_deferred
)
950 authenticateNTLMReleaseServer(conn
->auth_user_request
);
952 /* unlock the connection based lock */
953 debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
955 /* This still breaks the abstraction, but is at least read only now */
956 /* Ensure that the auth user request will be getting closed */
957 /* IFF we start persisting the struct after the conn closes - say for logging
958 * then this test may become invalid
960 assert(authenticateRequestRefCount(conn
->auth_user_request
) == 1);
962 authenticateAuthUserRequestUnlock(conn
->auth_user_request
);
964 conn
->auth_user_request
= NULL
;
968 /* authenticateUserUsername: return a pointer to the username in the */
970 authenticateNTLMUsername(auth_user_t
const * auth_user
)
972 ntlm_user_t
*ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
975 return ntlm_user
->username
;
980 /* NTLMLastHeader: return a pointer to the last header used in authenticating
981 * the request/conneciton
984 NTLMLastHeader(auth_user_request_t
* auth_user_request
)
986 ntlm_request_t
*ntlm_request
;
987 assert(auth_user_request
!= NULL
);
988 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
989 assert (ntlm_request
);
990 return ntlm_request
->ntlmauthenticate
;
994 * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
995 * Auth_user structure.
999 authenticateDecodeNTLMAuth(auth_user_request_t
* auth_user_request
, const char *proxy_auth
)
1002 assert(auth_user_request
->auth_user
== NULL
);
1003 auth_user_request
->auth_user
= authenticateAuthUserNew("ntlm");
1004 auth_user_request
->auth_user
->auth_type
= AUTH_NTLM
;
1005 auth_user_request
->auth_user
->scheme_data
= memPoolAlloc(ntlm_user_pool
);
1006 auth_user_request
->state (new ntlm_request_t
);
1007 /* lock for the auth_user_request link */
1008 authenticateAuthUserLock(auth_user_request
->auth_user
);
1009 node
= dlinkNodeNew();
1010 dlinkAdd(auth_user_request
, node
, &auth_user_request
->auth_user
->requests
);
1012 /* all we have to do is identify that it's NTLM - the helper does the rest */
1013 debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
1018 authenticateNTLMcmpUsername(ntlm_user_t
* u1
, ntlm_user_t
* u2
)
1020 return strcmp(u1
->username
, u2
->username
);
1024 /* there is a known race where a single client recieves the same challenge
1025 * and sends the same response to squid on a single select cycle.
1026 * Check for this and if found ignore the new link
1029 authenticateProxyAuthCacheAddLink(const char *key
, auth_user_t
* auth_user
)
1032 struct ProxyAuthCachePointer
*proxy_auth_hash
;
1034 ntlm_user_t
*ntlm_user
;
1035 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1036 node
= ntlm_user
->proxy_auth_list
.head
;
1037 /* prevent duplicates */
1041 if (!strcmp(key
, (char const *)((struct ProxyAuthCachePointer
*) node
->data
)->key
))
1047 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(memPoolAlloc(ntlm_user_hash_pool
));
1048 proxy_auth_hash
->key
= xstrdup(key
);
1049 proxy_auth_hash
->auth_user
= auth_user
;
1050 dlinkAddTail(proxy_auth_hash
, &proxy_auth_hash
->link
, &ntlm_user
->proxy_auth_list
);
1051 hash_join(proxy_auth_cache
, (hash_link
*) proxy_auth_hash
);
1055 ntlm_request_t::authenticated() const
1057 if (auth_state
== AUTHENTICATE_STATE_DONE
)
1060 debug(29, 9) ("User not fully authenticated.\n");
1066 ntlm_request_t::authenticate(request_t
* request
, ConnStateData
* conn
, http_hdr_type type
)
1072 authenticateNTLMAuthenticateUser(auth_user_request_t
* auth_user_request
, request_t
* request
, ConnStateData
* conn
, http_hdr_type type
)
1074 const char *proxy_auth
;
1076 struct ProxyAuthCachePointer
*proxy_auth_hash
= NULL
;
1077 auth_user_hash_pointer
*usernamehash
;
1078 auth_user_t
*auth_user
;
1079 ntlm_request_t
*ntlm_request
;
1080 ntlm_user_t
*ntlm_user
;
1081 LOCAL_ARRAY(char, ntlmhash
, NTLM_CHALLENGE_SZ
* 2);
1083 proxy_auth
= httpHeaderGetStr(&request
->header
, type
);
1085 auth_user
= auth_user_request
->auth_user
;
1087 assert(auth_user
->auth_type
== AUTH_NTLM
);
1088 assert(auth_user
->scheme_data
!= NULL
);
1089 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1090 ntlm_request
= dynamic_cast< ntlm_request_t
*>(auth_user_request
->state());
1091 assert (ntlm_request
);
1092 /* Check that we are in the client side, where we can generate
1093 * auth challenges */
1096 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
1097 debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
1101 switch (ntlm_request
->auth_state
) {
1103 case AUTHENTICATE_STATE_NONE
:
1104 /* we've recieved a negotiate request. pass to a helper */
1105 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth
);
1106 ntlm_request
->auth_state
= AUTHENTICATE_STATE_NEGOTIATE
;
1107 ntlm_request
->ntlmnegotiate
= xstrndup(proxy_auth
, NTLM_CHALLENGE_SZ
+ 5);
1108 conn
->auth_type
= AUTH_NTLM
;
1109 conn
->auth_user_request
= auth_user_request
;
1110 ntlm_request
->conn
= conn
;
1111 /* and lock for the connection duration */
1112 debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
1113 authenticateAuthUserRequestLock(auth_user_request
);
1117 case AUTHENTICATE_STATE_NEGOTIATE
:
1118 ntlm_request
->auth_state
= AUTHENTICATE_STATE_CHALLENGE
;
1119 /* We _MUST_ have the auth challenge by now */
1120 assert(ntlm_request
->authchallenge
);
1124 case AUTHENTICATE_STATE_CHALLENGE
:
1125 /* we should have recieved a NTLM challenge. pass it to the same
1127 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth
);
1128 /* do a cache lookup here. If it matches it's a successful ntlm
1129 * challenge - release the helper and use the existing auth_user
1132 if (strncmp("NTLM ", proxy_auth
, 5) == 0) {
1133 ntlm_request
->ntlmauthenticate
= xstrdup(proxy_auth
);
1135 fatal("Incorrect scheme in auth header\n");
1136 /* TODO: more fault tolerance.. reset the auth scheme here */
1139 /* cache entries have authenticateauthheaderchallengestring */
1140 snprintf(ntlmhash
, sizeof(ntlmhash
) - 1, "%s%s",
1141 ntlm_request
->ntlmauthenticate
,
1142 ntlm_request
->authchallenge
);
1144 /* see if we already know this user's authenticate */
1145 debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash
);
1147 assert(proxy_auth_cache
!= NULL
);
1149 proxy_auth_hash
= static_cast<ProxyAuthCachePointer
*>(hash_lookup(proxy_auth_cache
, ntlmhash
));
1151 if (!proxy_auth_hash
) { /* not in the hash table */
1152 debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
1153 ntlm_request
->auth_state
= AUTHENTICATE_STATE_RESPONSE
;
1154 /* verify with the ntlm helper */
1156 debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
1157 /* throw away the temporary entry */
1158 ntlm_request
->authserver_deferred
= 0;
1159 authenticateNTLMReleaseServer(auth_user_request
);
1160 authenticateAuthUserMerge(auth_user
, proxy_auth_hash
->auth_user
);
1161 auth_user
= proxy_auth_hash
->auth_user
;
1162 auth_user_request
->auth_user
= auth_user
;
1163 ntlm_request
->auth_state
= AUTHENTICATE_STATE_DONE
;
1165 debug(29, 9) ("found matching cache entry\n");
1166 assert(auth_user
->auth_type
== AUTH_NTLM
);
1167 /* get the existing entries details */
1168 ntlm_user
= static_cast<ntlm_user_t
*>(auth_user
->scheme_data
);
1169 debug(29, 9) ("Username to be used is %s\n", ntlm_user
->username
);
1170 /* on ntlm auth we do not unlock the auth_user until the
1171 * connection is dropped. Thank MS for this quirk */
1172 auth_user
->expiretime
= current_time
.tv_sec
;
1178 case AUTHENTICATE_STATE_RESPONSE
:
1179 /* auth-challenge pair cache miss. We've just got the response from the helper */
1180 /*add to cache and let them through */
1181 ntlm_request
->auth_state
= AUTHENTICATE_STATE_DONE
;
1182 /* this connection is authenticated */
1183 debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
1184 ntlm_request
->authchallenge
,
1185 ntlm_request
->ntlmauthenticate
,
1186 ntlm_user
->username
);
1187 /* cache entries have authenticateauthheaderchallengestring */
1188 snprintf(ntlmhash
, sizeof(ntlmhash
) - 1, "%s%s",
1189 ntlm_request
->ntlmauthenticate
,
1190 ntlm_request
->authchallenge
);
1191 /* see if this is an existing user with a different proxy_auth
1194 if ((usernamehash
= static_cast<AuthUserHashPointer
*>(hash_lookup(proxy_auth_username_cache
, ntlm_user
->username
)))
1196 while ((authUserHashPointerUser(usernamehash
)->auth_type
!= auth_user
->auth_type
) && (usernamehash
->next
) && !authenticateNTLMcmpUsername(static_cast<ntlm_user_t
*>(authUserHashPointerUser(usernamehash
)->scheme_data
), ntlm_user
)
1198 usernamehash
= static_cast<AuthUserHashPointer
*>(usernamehash
->next
);
1199 if (authUserHashPointerUser(usernamehash
)->auth_type
== auth_user
->auth_type
) {
1201 * add another link from the new proxy_auth to the
1202 * auth_user structure and update the information */
1203 assert(proxy_auth_hash
== NULL
);
1204 authenticateProxyAuthCacheAddLink(ntlmhash
, authUserHashPointerUser(usernamehash
));
1205 /* we can't seamlessly recheck the username due to the
1206 * challenge nature of the protocol. Just free the
1207 * temporary auth_user */
1208 authenticateAuthUserMerge(auth_user
, authUserHashPointerUser(usernamehash
));
1209 auth_user
= authUserHashPointerUser(usernamehash
);
1210 auth_user_request
->auth_user
= auth_user
;
1213 /* store user in hash's */
1214 authenticateUserNameCacheAdd(auth_user
);
1215 authenticateProxyAuthCacheAddLink(ntlmhash
, auth_user
);
1218 /* set these to now because this is either a new login from an
1219 * existing user or a new user */
1220 auth_user
->expiretime
= current_time
.tv_sec
;
1224 case AUTHENTICATE_STATE_DONE
:
1225 fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
1228 case AUTHENTICATE_STATE_FAILED
:
1229 /* we've failed somewhere in authentication */
1230 debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth
);
1237 MemPool
*ntlm_request_t::Pool(NULL
);
1239 ntlm_request_t::operator new (size_t byteCount
)
1241 /* derived classes with different sizes must implement their own new */
1242 assert (byteCount
== sizeof (ntlm_request_t
));
1245 Pool
= memPoolCreate("ntlm_request_t", sizeof (ntlm_request_t
));
1247 return memPoolAlloc(Pool
);
1251 ntlm_request_t::operator delete (void *address
)
1253 memPoolFree (Pool
, address
);
1257 ntlm_request_t::deleteSelf() const