2 * $Id: auth_ntlm.cc,v 1.79 2008/02/12 23:17:53 rousskov Exp $
4 * DEBUG: section 29 NTLM Authenticator
5 * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 /* The functions in this file handle authentication.
36 * They DO NOT perform access control or auditing.
37 * See acl.c for access control and client_side.c for auditing */
41 #include "auth_ntlm.h"
42 #include "authenticate.h"
43 #include "CacheManager.h"
45 #include "client_side.h"
46 #include "HttpReply.h"
47 #include "HttpRequest.h"
48 /* TODO remove this include */
49 #include "ntlmScheme.h"
51 #include "SquidTime.h"
54 authenticateNTLMReleaseServer(AuthUserRequest
* auth_user_request
);
58 authenticateStateFree(authenticateStateData
* r
)
60 AUTHUSERREQUESTUNLOCK(r
->auth_user_request
, "r");
65 static HLPSCB authenticateNTLMHandleReply
;
66 static AUTHSSTATS authenticateNTLMStats
;
68 static statefulhelper
*ntlmauthenticators
= NULL
;
70 CBDATA_TYPE(authenticateStateData
);
72 static int authntlm_initialised
= 0;
74 static auth_ntlm_config ntlmConfig
;
76 static hash_table
*proxy_auth_cache
= NULL
;
84 /* move to ntlmScheme.cc */
88 /* TODO: this should be a Config call. */
89 debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
91 if (ntlmauthenticators
)
92 helperStatefulShutdown(ntlmauthenticators
);
94 authntlm_initialised
= 0;
99 if (ntlmauthenticators
)
100 helperStatefulFree(ntlmauthenticators
);
102 ntlmauthenticators
= NULL
;
104 debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
107 /* free any allocated configuration details */
109 AuthNTLMConfig::done()
112 wordlistDestroy(&authenticate
);
116 AuthNTLMConfig::dump(StoreEntry
* entry
, const char *name
, AuthConfig
* scheme
)
118 wordlist
*list
= authenticate
;
119 storeAppendPrintf(entry
, "%s %s", name
, "ntlm");
121 while (list
!= NULL
) {
122 storeAppendPrintf(entry
, " %s", list
->key
);
126 storeAppendPrintf(entry
, "\n%s ntlm children %d\n",
127 name
, authenticateChildren
);
128 storeAppendPrintf(entry
, "%s %s keep_alive %s\n", name
, "ntlm", keep_alive
? "on" : "off");
132 AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1)
136 AuthNTLMConfig::parse(AuthConfig
* scheme
, int n_configured
, char *param_str
)
138 if (strcasecmp(param_str
, "program") == 0) {
140 wordlistDestroy(&authenticate
);
142 parse_wordlist(&authenticate
);
144 requirePathnameExists("authparam ntlm program", authenticate
->key
);
145 } else if (strcasecmp(param_str
, "children") == 0) {
146 parse_int(&authenticateChildren
);
147 } else if (strcasecmp(param_str
, "keep_alive") == 0) {
148 parse_onoff(&keep_alive
);
150 debugs(29, 0, "AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '" << param_str
<< "'");
154 * disable client side request pipelining. There is a race with
155 * NTLM when the client sends a second request on an NTLM
156 * connection before the authenticate challenge is sent. With
157 * this patch, the client may fail to authenticate, but squid's
158 * state will be preserved. Caveats: this should be a post-parse
159 * test, but that can wait for the modular parser to be integrated.
162 Config
.onoff
.pipeline_prefetch
= 0;
166 AuthNTLMConfig::type() const
168 return ntlmScheme::GetInstance().type();
171 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
174 AuthNTLMConfig::init(AuthConfig
* scheme
)
178 authntlm_initialised
= 1;
180 if (ntlmauthenticators
== NULL
)
181 ntlmauthenticators
= helperStatefulCreate("ntlmauthenticator");
183 if (!proxy_auth_cache
)
184 proxy_auth_cache
= hash_create((HASHCMP
*) strcmp
, 7921, hash_string
);
186 assert(proxy_auth_cache
);
188 ntlmauthenticators
->cmdline
= authenticate
;
190 ntlmauthenticators
->n_to_start
= authenticateChildren
;
192 ntlmauthenticators
->ipc_type
= IPC_STREAM
;
194 helperStatefulOpenServers(ntlmauthenticators
);
196 CBDATA_INIT_TYPE(authenticateStateData
);
201 AuthNTLMConfig::registerWithCacheManager(void)
203 CacheManager::GetInstance()->
204 registerAction("ntlmauthenticator",
205 "NTLM User Authenticator Stats",
206 authenticateNTLMStats
, 0, 1);
210 AuthNTLMConfig::active() const
212 return authntlm_initialised
== 1;
216 AuthNTLMConfig::configured() const
218 if ((authenticate
!= NULL
) && (authenticateChildren
!= 0)) {
219 debugs(29, 9, "AuthNTLMConfig::configured: returning configured");
223 debugs(29, 9, "AuthNTLMConfig::configured: returning unconfigured");
228 /* See AuthUserRequest.cc::authenticateDirection for return values */
230 AuthNTLMUserRequest::module_direction()
232 /* null auth_user is checked for by authenticateDirection */
234 if (waiting
|| client_blob
)
235 return -1; /* need helper response to continue */
237 switch (auth_state
) {
239 /* no progress at all. */
241 case AUTHENTICATE_STATE_NONE
:
242 debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
243 return -2; /* error */
245 case AUTHENTICATE_STATE_FAILED
:
246 return -2; /* error */
249 case AUTHENTICATE_STATE_IN_PROGRESS
:
251 return 1; /* send to client */
253 case AUTHENTICATE_STATE_DONE
:
254 return 0; /* do nothing */
256 case AUTHENTICATE_STATE_INITIAL
:
257 debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
265 AuthNTLMConfig::fixHeader(AuthUserRequest
*auth_user_request
, HttpReply
*rep
, http_hdr_type type
, HttpRequest
* request
)
267 AuthNTLMUserRequest
*ntlm_request
;
272 /* Need keep-alive */
273 if (!request
->flags
.proxy_keepalive
&& request
->flags
.must_keepalive
)
276 /* New request, no user details */
277 if (auth_user_request
== NULL
) {
278 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type
<< " header: 'NTLM'");
279 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
282 /* drop the connection */
283 request
->flags
.proxy_keepalive
= 0;
286 ntlm_request
= dynamic_cast<AuthNTLMUserRequest
*>(auth_user_request
);
288 assert(ntlm_request
!= NULL
);
290 switch (ntlm_request
->auth_state
) {
292 case AUTHENTICATE_STATE_FAILED
:
293 /* here it makes sense to drop the connection, as auth is
294 * tied to it, even if MAYBE the client could handle it - Kinkie */
295 request
->flags
.proxy_keepalive
= 0;
298 case AUTHENTICATE_STATE_DONE
:
299 /* Special case: authentication finished OK but disallowed by ACL.
300 * Need to start over to give the client another chance.
304 case AUTHENTICATE_STATE_NONE
:
305 /* semantic change: do not drop the connection.
306 * 2.5 implementation used to keep it open - Kinkie */
307 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type
<< " header: 'NTLM'");
308 httpHeaderPutStrf(&rep
->header
, type
, "NTLM");
311 case AUTHENTICATE_STATE_IN_PROGRESS
:
312 /* we're waiting for a response from the client. Pass it the blob */
313 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type
<< " header: 'NTLM " << ntlm_request
->server_blob
<< "'");
314 httpHeaderPutStrf(&rep
->header
, type
, "NTLM %s", ntlm_request
->server_blob
);
315 safe_free(ntlm_request
->server_blob
);
320 debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request
->auth_state
<< ".");
321 fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
326 NTLMUser::~NTLMUser()
328 debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
331 static stateful_helper_callback_t
332 authenticateNTLMHandleReply(void *data
, void *lastserver
, char *reply
)
334 authenticateStateData
*r
= static_cast<authenticateStateData
*>(data
);
337 stateful_helper_callback_t result
= S_HELPER_UNKNOWN
;
340 AuthUserRequest
*auth_user_request
;
343 AuthNTLMUserRequest
*ntlm_request
;
345 debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver
<< "' sent us '" << (reply
? reply
: "<NULL>") << "'");
346 valid
= cbdataReferenceValid(r
->data
);
349 debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. Releasing helper '" << lastserver
<< "'.");
350 cbdataReferenceDone(r
->data
);
351 authenticateStateFree(r
);
352 debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << S_HELPER_RELEASE
);
353 return S_HELPER_RELEASE
;
357 debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver
<< "' crashed!.");
358 reply
= (char *)"BH Internal error";
361 auth_user_request
= r
->auth_user_request
;
362 assert(auth_user_request
!= NULL
);
363 ntlm_request
= dynamic_cast<AuthNTLMUserRequest
*>(auth_user_request
);
365 assert(ntlm_request
!= NULL
);
366 assert(ntlm_request
->waiting
);
367 ntlm_request
->waiting
= 0;
368 safe_free(ntlm_request
->client_blob
);
370 auth_user
= ntlm_request
->user();
371 assert(auth_user
!= NULL
);
372 assert(auth_user
->auth_type
== AUTH_NTLM
);
373 ntlm_user
= dynamic_cast<ntlm_user_t
*>(auth_user_request
->user());
375 assert(ntlm_user
!= NULL
);
377 if (ntlm_request
->authserver
== NULL
)
378 ntlm_request
->authserver
= static_cast<helper_stateful_server
*>(lastserver
);
380 assert(ntlm_request
->authserver
== lastserver
);
382 /* seperate out the useful data */
383 blob
= strchr(reply
, ' ');
388 if (strncasecmp(reply
, "TT ", 3) == 0) {
389 /* we have been given a blob to send to the client */
390 safe_free(ntlm_request
->server_blob
);
391 ntlm_request
->request
->flags
.must_keepalive
= 1;
392 if (ntlm_request
->request
->flags
.proxy_keepalive
) {
393 ntlm_request
->server_blob
= xstrdup(blob
);
394 ntlm_request
->auth_state
= AUTHENTICATE_STATE_IN_PROGRESS
;
395 auth_user_request
->denyMessage("Authentication in progress");
396 debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob
<< "'");
397 result
= S_HELPER_RESERVE
;
399 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
400 auth_user_request
->denyMessage("NTLM authentication requires a persistent connection");
401 result
= S_HELPER_RELEASE
;
403 } else if (strncasecmp(reply
, "AF ", 3) == 0) {
404 /* we're finished, release the helper */
405 ntlm_user
->username(blob
);
406 auth_user_request
->denyMessage("Login successful");
407 safe_free(ntlm_request
->server_blob
);
409 result
= S_HELPER_RELEASE
;
410 debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob
<< "'");
411 /* connection is authenticated */
412 debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_user
->username());
413 /* see if this is an existing user with a different proxy_auth
415 auth_user_hash_pointer
*usernamehash
= static_cast<AuthUserHashPointer
*>(hash_lookup(proxy_auth_username_cache
, ntlm_user
->username()));
416 AuthUser
*local_auth_user
= ntlm_request
->user();
417 while (usernamehash
&& (usernamehash
->user()->auth_type
!= AUTH_NTLM
|| strcmp(usernamehash
->user()->username(), ntlm_user
->username()) != 0))
418 usernamehash
= static_cast<AuthUserHashPointer
*>(usernamehash
->next
);
420 /* we can't seamlessly recheck the username due to the
421 * challenge-response nature of the protocol.
422 * Just free the temporary auth_user */
423 usernamehash
->user()->absorb(local_auth_user
);
424 //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
425 local_auth_user
= usernamehash
->user();
426 ntlm_request
->_auth_user
= local_auth_user
;
428 /* store user in hash's */
429 local_auth_user
->addToNameCache();
430 // authenticateUserNameCacheAdd(local_auth_user);
432 /* set these to now because this is either a new login from an
433 * existing user or a new user */
434 local_auth_user
->expiretime
= current_time
.tv_sec
;
435 authenticateNTLMReleaseServer(ntlm_request
);
436 ntlm_request
->auth_state
= AUTHENTICATE_STATE_DONE
;
437 } else if (strncasecmp(reply
, "NA ", 3) == 0) {
438 /* authentication failure (wrong password, etc.) */
439 auth_user_request
->denyMessage(blob
);
440 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
441 safe_free(ntlm_request
->server_blob
);
442 authenticateNTLMReleaseServer(ntlm_request
);
443 result
= S_HELPER_RELEASE
;
444 debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob
<< "'");
445 } else if (strncasecmp(reply
, "BH ", 3) == 0) {
446 /* TODO kick off a refresh process. This can occur after a YR or after
447 * a KK. If after a YR release the helper and resubmit the request via
448 * Authenticate NTLM start.
449 * If after a KK deny the user's request w/ 407 and mark the helper as
451 auth_user_request
->denyMessage(blob
);
452 ntlm_request
->auth_state
= AUTHENTICATE_STATE_FAILED
;
453 safe_free(ntlm_request
->server_blob
);
454 authenticateNTLMReleaseServer(ntlm_request
);
455 result
= S_HELPER_RELEASE
;
456 debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply
<< "'");
459 fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply
);
462 if (ntlm_request
->request
) {
463 HTTPMSGUNLOCK(ntlm_request
->request
);
464 ntlm_request
->request
= NULL
;
466 r
->handler(r
->data
, NULL
);
467 cbdataReferenceDone(r
->data
);
468 authenticateStateFree(r
);
469 debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << result
);
474 authenticateNTLMStats(StoreEntry
* sentry
)
476 helperStatefulStats(sentry
, ntlmauthenticators
, "NTLM Authenticator Statistics");
480 /* send the initial data to a stateful ntlm authenticator module */
482 AuthNTLMUserRequest::module_start(RH
* handler
, void *data
)
484 authenticateStateData
*r
= NULL
;
485 static char buf
[8192];
486 ntlm_user_t
*ntlm_user
;
487 AuthUser
*auth_user
= user();
492 assert(auth_user
->auth_type
== AUTH_NTLM
);
494 ntlm_user
= dynamic_cast<ntlm_user_t
*>(user());
496 debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state
<< "'");
498 if (ntlmConfig
.authenticate
== NULL
) {
499 debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
504 r
= cbdataAlloc(authenticateStateData
);
505 r
->handler
= handler
;
506 r
->data
= cbdataReference(data
);
507 r
->auth_user_request
= this;
508 AUTHUSERREQUESTLOCK(r
->auth_user_request
, "r");
510 if (auth_state
== AUTHENTICATE_STATE_INITIAL
) {
511 snprintf(buf
, 8192, "YR %s\n", client_blob
); //CHECKME: can ever client_blob be 0 here?
513 snprintf(buf
, 8192, "KK %s\n", client_blob
);
518 safe_free(client_blob
);
519 helperStatefulSubmit(ntlmauthenticators
, buf
, authenticateNTLMHandleReply
, r
, authserver
);
522 /* clear the NTLM helper of being reserved for future requests */
524 authenticateNTLMReleaseServer(AuthUserRequest
* auth_user_request
)
526 AuthNTLMUserRequest
*ntlm_request
;
527 assert(auth_user_request
->user()->auth_type
== AUTH_NTLM
);
528 ntlm_request
= dynamic_cast< AuthNTLMUserRequest
*>(auth_user_request
);
529 debugs(29, 9, "authenticateNTLMReleaseServer: releasing server '" << ntlm_request
->authserver
<< "'");
530 /* is it possible for the server to be NULL? hno seems to think so.
531 * Let's see what happens, might segfault in helperStatefulReleaseServer
532 * if it does. I leave it like this not to cover possibly problematic
533 * code-paths. Kinkie */
535 * yes, it is possible */
536 assert(ntlm_request
!= NULL
);
537 if (ntlm_request
->authserver
) {
538 helperStatefulReleaseServer(ntlm_request
->authserver
);
539 ntlm_request
->authserver
= NULL
;
543 /* clear any connection related authentication details */
545 AuthNTLMUserRequest::onConnectionClose(ConnStateData
*conn
)
547 assert(conn
!= NULL
);
549 debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn
<< "' (this is '" << this << "')");
551 if (conn
->auth_user_request
== NULL
) {
552 debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
556 if (authserver
!= NULL
)
557 authenticateNTLMReleaseServer(this);
559 /* unlock the connection based lock */
560 debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn
<< "'.");
562 AUTHUSERREQUESTUNLOCK(conn
->auth_user_request
, "conn");
566 * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
567 * Auth_user structure.
570 AuthNTLMConfig::decode(char const *proxy_auth
)
572 NTLMUser
*newUser
= new NTLMUser(&ntlmConfig
);
573 AuthNTLMUserRequest
*auth_user_request
= new AuthNTLMUserRequest ();
574 assert(auth_user_request
->user() == NULL
);
575 auth_user_request
->user(newUser
);
576 auth_user_request
->user()->auth_type
= AUTH_NTLM
;
577 auth_user_request
->user()->addRequest(auth_user_request
);
579 /* all we have to do is identify that it's NTLM - the helper does the rest */
580 debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
581 return auth_user_request
;
585 AuthNTLMUserRequest::authenticated() const
587 if (auth_state
== AUTHENTICATE_STATE_DONE
) {
588 debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
592 debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
598 AuthNTLMUserRequest::authenticate(HttpRequest
* request
, ConnStateData
* conn
, http_hdr_type type
)
600 const char *proxy_auth
, *blob
;
602 /* TODO: rename this!! */
603 AuthUser
*local_auth_user
;
604 ntlm_user_t
*ntlm_user
;
606 local_auth_user
= user();
607 assert(local_auth_user
);
608 assert(local_auth_user
->auth_type
== AUTH_NTLM
);
609 ntlm_user
= dynamic_cast<ntlm_user_t
*>(local_auth_user
);
612 /* Check that we are in the client side, where we can generate
615 if (conn
== NULL
|| !cbdataReferenceValid(conn
)) {
616 auth_state
= AUTHENTICATE_STATE_FAILED
;
617 debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
622 debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
627 debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob
<< "'!");
632 proxy_auth
= request
->header
.getStr(type
);
634 /* locate second word */
637 /* if proxy_auth is actually NULL, we'd better not manipulate it. */
639 while (xisspace(*blob
) && *blob
)
642 while (!xisspace(*blob
) && *blob
)
645 while (xisspace(*blob
) && *blob
)
649 switch (auth_state
) {
651 case AUTHENTICATE_STATE_NONE
:
652 /* we've received a ntlm request. pass to a helper */
653 debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth
<< "'");
654 auth_state
= AUTHENTICATE_STATE_INITIAL
;
655 safe_free(client_blob
);
656 client_blob
=xstrdup(blob
);
657 conn
->auth_type
= AUTH_NTLM
;
658 assert(conn
->auth_user_request
== NULL
);
659 conn
->auth_user_request
= this;
660 AUTHUSERREQUESTLOCK(conn
->auth_user_request
, "conn");
661 this->request
= request
;
662 HTTPMSGLOCK(this->request
);
667 case AUTHENTICATE_STATE_INITIAL
:
668 debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
675 case AUTHENTICATE_STATE_IN_PROGRESS
:
676 /* we should have received a blob from the client. Hand it off to
678 safe_free(client_blob
);
680 client_blob
= xstrdup (blob
);
683 HTTPMSGUNLOCK(this->request
);
684 this->request
= request
;
685 HTTPMSGLOCK(this->request
);
690 case AUTHENTICATE_STATE_DONE
:
691 fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
695 case AUTHENTICATE_STATE_FAILED
:
696 /* we've failed somewhere in authentication */
697 debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth
);
707 AuthNTLMUserRequest::AuthNTLMUserRequest() :
708 /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE
),
718 AuthNTLMUserRequest::~AuthNTLMUserRequest()
720 safe_free(server_blob
);
721 safe_free(client_blob
);
723 if (authserver
!= NULL
) {
724 debugs(29, 9, "AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '" << authserver
<< "'");
725 helperStatefulReleaseServer(authserver
);
729 HTTPMSGUNLOCK(request
);
735 NTLMUser::deleteSelf() const
740 NTLMUser::NTLMUser (AuthConfig
*config
) : AuthUser (config
)
742 proxy_auth_list
.head
= proxy_auth_list
.tail
= NULL
;
746 ntlmScheme::createConfig()
752 AuthNTLMUserRequest::connLastHeader()