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