]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/ntlm/auth_ntlm.cc
This file does not need to include authenticate.h, apparently.
[thirdparty/squid.git] / src / auth / ntlm / auth_ntlm.cc
CommitLineData
94439e4e 1/*
262a0e14 2 * $Id$
94439e4e 3 *
4 * DEBUG: section 29 NTLM Authenticator
6bf4f823 5 * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
94439e4e 6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
94439e4e 8 * ----------------------------------------------------------
9 *
2b6662ba 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.
94439e4e 18 *
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.
26ac0430 23 *
94439e4e 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.
26ac0430 28 *
94439e4e 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.
32 *
33 */
34
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 */
38
39
40#include "squid.h"
41#include "auth_ntlm.h"
e6ccf245 42#include "authenticate.h"
62ee09ca 43#include "CacheManager.h"
e6ccf245 44#include "Store.h"
a46d2c0e 45#include "client_side.h"
924f73bc 46#include "HttpReply.h"
a2ac85d9 47#include "HttpRequest.h"
f5691f9c 48/* TODO remove this include */
49#include "ntlmScheme.h"
d295d770 50#include "wordlist.h"
cc192b50 51#include "SquidTime.h"
c78aa667 52
6bf4f823 53static void
76f142cd 54authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request);
6bf4f823 55
56
94439e4e 57static void
58authenticateStateFree(authenticateStateData * r)
59{
4f0ef8e8 60 AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
94439e4e 61 cbdataFree(r);
62}
63
64/* NTLM Scheme */
65static HLPSCB authenticateNTLMHandleReply;
94439e4e 66static AUTHSSTATS authenticateNTLMStats;
94439e4e 67
94439e4e 68static statefulhelper *ntlmauthenticators = NULL;
69
70CBDATA_TYPE(authenticateStateData);
71
72static int authntlm_initialised = 0;
73
f5691f9c 74static auth_ntlm_config ntlmConfig;
94439e4e 75
76static hash_table *proxy_auth_cache = NULL;
77
78/*
79 *
80 * Private Functions
81 *
82 */
83
f5691f9c 84/* move to ntlmScheme.cc */
85void
86ntlmScheme::done()
94439e4e 87{
f5691f9c 88 /* TODO: this should be a Config call. */
bf8fe701 89 debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
62e76326 90
bd507204 91 if (ntlmauthenticators)
62e76326 92 helperStatefulShutdown(ntlmauthenticators);
93
94439e4e 94 authntlm_initialised = 0;
62e76326 95
94439e4e 96 if (!shutting_down)
62e76326 97 return;
98
bd507204 99 if (ntlmauthenticators)
62e76326 100 helperStatefulFree(ntlmauthenticators);
101
94439e4e 102 ntlmauthenticators = NULL;
62e76326 103
bf8fe701 104 debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
94439e4e 105}
106
107/* free any allocated configuration details */
f5691f9c 108void
109AuthNTLMConfig::done()
94439e4e 110{
f5691f9c 111 if (authenticate)
112 wordlistDestroy(&authenticate);
94439e4e 113}
114
f5691f9c 115void
116AuthNTLMConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
94439e4e 117{
f5691f9c 118 wordlist *list = authenticate;
94439e4e 119 storeAppendPrintf(entry, "%s %s", name, "ntlm");
62e76326 120
94439e4e 121 while (list != NULL) {
62e76326 122 storeAppendPrintf(entry, " %s", list->key);
123 list = list->next;
94439e4e 124 }
62e76326 125
6bf4f823 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");
94439e4e 129
130}
131
6bf4f823 132AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1)
133{ }
62e76326 134
f5691f9c 135void
136AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
137{
94439e4e 138 if (strcasecmp(param_str, "program") == 0) {
f5691f9c 139 if (authenticate)
140 wordlistDestroy(&authenticate);
62e76326 141
f5691f9c 142 parse_wordlist(&authenticate);
62e76326 143
42900318 144 requirePathnameExists("auth_param ntlm program", authenticate->key);
94439e4e 145 } else if (strcasecmp(param_str, "children") == 0) {
f5691f9c 146 parse_int(&authenticateChildren);
6bf4f823 147 } else if (strcasecmp(param_str, "keep_alive") == 0) {
148 parse_onoff(&keep_alive);
94439e4e 149 } else {
bf8fe701 150 debugs(29, 0, "AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '" << param_str << "'");
94439e4e 151 }
62e76326 152
dff99376 153 /*
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.
721b0310 160 */
f5691f9c 161 if (authenticate)
62e76326 162 Config.onoff.pipeline_prefetch = 0;
94439e4e 163}
164
f5691f9c 165const char *
166AuthNTLMConfig::type() const
94439e4e 167{
f5691f9c 168 return ntlmScheme::GetInstance().type();
94439e4e 169}
170
171/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
172 * config file */
f5691f9c 173void
174AuthNTLMConfig::init(AuthConfig * scheme)
94439e4e 175{
f5691f9c 176 if (authenticate) {
6bf4f823 177
62e76326 178 authntlm_initialised = 1;
179
180 if (ntlmauthenticators == NULL)
181 ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
182
183 if (!proxy_auth_cache)
30abd221 184 proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
62e76326 185
186 assert(proxy_auth_cache);
187
f5691f9c 188 ntlmauthenticators->cmdline = authenticate;
62e76326 189
f5691f9c 190 ntlmauthenticators->n_to_start = authenticateChildren;
62e76326 191
192 ntlmauthenticators->ipc_type = IPC_STREAM;
193
62e76326 194 helperStatefulOpenServers(ntlmauthenticators);
195
62e76326 196 CBDATA_INIT_TYPE(authenticateStateData);
94439e4e 197 }
198}
199
62ee09ca 200void
15fab853 201AuthNTLMConfig::registerWithCacheManager(void)
62ee09ca 202{
15fab853 203 CacheManager::GetInstance()->
26ac0430
AJ
204 registerAction("ntlmauthenticator",
205 "NTLM User Authenticator Stats",
206 authenticateNTLMStats, 0, 1);
62ee09ca 207}
208
f5691f9c 209bool
210AuthNTLMConfig::active() const
2d70df72 211{
f5691f9c 212 return authntlm_initialised == 1;
2d70df72 213}
214
f5691f9c 215bool
216AuthNTLMConfig::configured() const
94439e4e 217{
6bf4f823 218 if ((authenticate != NULL) && (authenticateChildren != 0)) {
bf8fe701 219 debugs(29, 9, "AuthNTLMConfig::configured: returning configured");
f5691f9c 220 return true;
2d70df72 221 }
62e76326 222
bf8fe701 223 debugs(29, 9, "AuthNTLMConfig::configured: returning unconfigured");
f5691f9c 224 return false;
94439e4e 225}
226
227/* NTLM Scheme */
6bf4f823 228/* See AuthUserRequest.cc::authenticateDirection for return values */
82b045dc 229int
f5691f9c 230AuthNTLMUserRequest::module_direction()
94439e4e 231{
bd507204 232 /* null auth_user is checked for by authenticateDirection */
62e76326 233
6bf4f823 234 if (waiting || client_blob)
235 return -1; /* need helper response to continue */
236
82b045dc 237 switch (auth_state) {
62e76326 238
82b045dc 239 /* no progress at all. */
240
241 case AUTHENTICATE_STATE_NONE:
bf8fe701 242 debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
6bf4f823 243 return -2; /* error */
62e76326 244
3c641669 245 case AUTHENTICATE_STATE_FAILED:
6bf4f823 246 return -2; /* error */
62e76326 247
82b045dc 248
6bf4f823 249 case AUTHENTICATE_STATE_IN_PROGRESS:
250 assert(server_blob);
251 return 1; /* send to client */
82b045dc 252
82b045dc 253 case AUTHENTICATE_STATE_DONE:
6bf4f823 254 return 0; /* do nothing */
255
256 case AUTHENTICATE_STATE_INITIAL:
bf8fe701 257 debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
6bf4f823 258 return -2;
94439e4e 259 }
62e76326 260
94439e4e 261 return -2;
262}
263
f5691f9c 264void
76f142cd 265AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
94439e4e 266{
f5691f9c 267 AuthNTLMUserRequest *ntlm_request;
62e76326 268
6bf4f823 269 if (!authenticate)
270 return;
62e76326 271
63a05fa3 272 /* Need keep-alive */
273 if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
26ac0430 274 return;
63a05fa3 275
6bf4f823 276 /* New request, no user details */
277 if (auth_user_request == NULL) {
bf8fe701 278 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
6bf4f823 279 httpHeaderPutStrf(&rep->header, type, "NTLM");
280
281 if (!keep_alive) {
62e76326 282 /* drop the connection */
62e76326 283 request->flags.proxy_keepalive = 0;
62e76326 284 }
6bf4f823 285 } else {
286 ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
94439e4e 287
3a11f20d 288 assert(ntlm_request != NULL);
289
6bf4f823 290 switch (ntlm_request->auth_state) {
62e76326 291
6bf4f823 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 */
6bf4f823 295 request->flags.proxy_keepalive = 0;
296 /* fall through */
94439e4e 297
ebd65a33 298 case AUTHENTICATE_STATE_DONE:
6bf4f823 299 /* Special case: authentication finished OK but disallowed by ACL.
300 * Need to start over to give the client another chance.
301 */
302 /* fall through */
62e76326 303
6bf4f823 304 case AUTHENTICATE_STATE_NONE:
305 /* semantic change: do not drop the connection.
306 * 2.5 implementation used to keep it open - Kinkie */
bf8fe701 307 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
6bf4f823 308 httpHeaderPutStrf(&rep->header, type, "NTLM");
309 break;
62e76326 310
6bf4f823 311 case AUTHENTICATE_STATE_IN_PROGRESS:
312 /* we're waiting for a response from the client. Pass it the blob */
bf8fe701 313 debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM " << ntlm_request->server_blob << "'");
6bf4f823 314 httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob);
6bf4f823 315 safe_free(ntlm_request->server_blob);
316 break;
62e76326 317
62e76326 318
6bf4f823 319 default:
bf8fe701 320 debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << ".");
6bf4f823 321 fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
322 }
323 }
324}
62e76326 325
6bf4f823 326NTLMUser::~NTLMUser()
327{
bf8fe701 328 debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
94439e4e 329}
330
331static stateful_helper_callback_t
332authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
333{
e6ccf245 334 authenticateStateData *r = static_cast<authenticateStateData *>(data);
6bf4f823 335
336 int valid;
94439e4e 337 stateful_helper_callback_t result = S_HELPER_UNKNOWN;
6bf4f823 338 char *blob;
339
76f142cd 340 AuthUserRequest *auth_user_request;
6bf4f823 341 AuthUser *auth_user;
342 NTLMUser *ntlm_user;
f5691f9c 343 AuthNTLMUserRequest *ntlm_request;
62e76326 344
bf8fe701 345 debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
a6151f51 346 valid = cbdataReferenceValid(r->data);
6bf4f823 347
348 if (!valid) {
bf8fe701 349 debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. Releasing helper '" << lastserver << "'.");
62e76326 350 cbdataReferenceDone(r->data);
351 authenticateStateFree(r);
bf8fe701 352 debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << S_HELPER_RELEASE);
62e76326 353 return S_HELPER_RELEASE;
9bea1d5b 354 }
62e76326 355
5d146f7d 356 if (!reply) {
bf8fe701 357 debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
c9c40182 358 reply = (char *)"BH Internal error";
9bea1d5b 359 }
62e76326 360
6bf4f823 361 auth_user_request = r->auth_user_request;
362 assert(auth_user_request != NULL);
363 ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
62e76326 364
3a11f20d 365 assert(ntlm_request != NULL);
6bf4f823 366 assert(ntlm_request->waiting);
367 ntlm_request->waiting = 0;
368 safe_free(ntlm_request->client_blob);
62e76326 369
6bf4f823 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());
62e76326 374
93d633e5 375 assert(ntlm_user != NULL);
3a11f20d 376
6bf4f823 377 if (ntlm_request->authserver == NULL)
378 ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
379 else
380 assert(ntlm_request->authserver == lastserver);
62e76326 381
6bf4f823 382 /* seperate out the useful data */
383 blob = strchr(reply, ' ');
62e76326 384
c9c40182 385 if (blob)
6bf4f823 386 blob++;
62e76326 387
c9c40182 388 if (strncasecmp(reply, "TT ", 3) == 0) {
6bf4f823 389 /* we have been given a blob to send to the client */
390 safe_free(ntlm_request->server_blob);
26ac0430
AJ
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;
398 } else {
399 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
400 auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
401 result = S_HELPER_RELEASE;
402 }
c9c40182 403 } else if (strncasecmp(reply, "AF ", 3) == 0) {
62e76326 404 /* we're finished, release the helper */
6bf4f823 405 ntlm_user->username(blob);
406 auth_user_request->denyMessage("Login successful");
407 safe_free(ntlm_request->server_blob);
62e76326 408
6bf4f823 409 result = S_HELPER_RELEASE;
bf8fe701 410 debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
71ee0c43 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
414 * string */
415 auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
26ac0430 416 AuthUser *local_auth_user = ntlm_request->user();
71ee0c43 417 while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
418 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
419 if (usernamehash) {
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;
427 } else {
428 /* store user in hash's */
429 local_auth_user->addToNameCache();
430 // authenticateUserNameCacheAdd(local_auth_user);
431 }
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);
26ac0430 436 ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
c9c40182 437 } else if (strncasecmp(reply, "NA ", 3) == 0) {
6bf4f823 438 /* authentication failure (wrong password, etc.) */
439 auth_user_request->denyMessage(blob);
62e76326 440 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
6bf4f823 441 safe_free(ntlm_request->server_blob);
442 authenticateNTLMReleaseServer(ntlm_request);
443 result = S_HELPER_RELEASE;
bf8fe701 444 debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
5d146f7d 445 } else if (strncasecmp(reply, "BH ", 3) == 0) {
62e76326 446 /* TODO kick off a refresh process. This can occur after a YR or after
6bf4f823 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
62e76326 450 * Needing YR. */
6bf4f823 451 auth_user_request->denyMessage(blob);
62e76326 452 ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
6bf4f823 453 safe_free(ntlm_request->server_blob);
454 authenticateNTLMReleaseServer(ntlm_request);
455 result = S_HELPER_RELEASE;
bf8fe701 456 debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
94439e4e 457 } else {
6bf4f823 458 /* protocol error */
459 fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
94439e4e 460 }
62e76326 461
8d3b341e 462 if (ntlm_request->request) {
26ac0430
AJ
463 HTTPMSGUNLOCK(ntlm_request->request);
464 ntlm_request->request = NULL;
8d3b341e 465 }
5d146f7d 466 r->handler(r->data, NULL);
fa80a8ef 467 cbdataReferenceDone(r->data);
94439e4e 468 authenticateStateFree(r);
bf8fe701 469 debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << result);
94439e4e 470 return result;
471}
472
94439e4e 473static void
474authenticateNTLMStats(StoreEntry * sentry)
475{
9522b380 476 helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics");
94439e4e 477}
478
94439e4e 479
480/* send the initial data to a stateful ntlm authenticator module */
f5691f9c 481void
482AuthNTLMUserRequest::module_start(RH * handler, void *data)
94439e4e 483{
94439e4e 484 authenticateStateData *r = NULL;
6bf4f823 485 static char buf[8192];
94439e4e 486 ntlm_user_t *ntlm_user;
e1f7507e 487 AuthUser *auth_user = user();
94439e4e 488
94439e4e 489 assert(data);
6bf4f823 490 assert(handler);
491 assert(auth_user);
9970d4b1 492 assert(auth_user->auth_type == AUTH_NTLM);
62e76326 493
6bf4f823 494 ntlm_user = dynamic_cast<ntlm_user_t *>(user());
62e76326 495
bf8fe701 496 debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
62e76326 497
f5691f9c 498 if (ntlmConfig.authenticate == NULL) {
bf8fe701 499 debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
62e76326 500 handler(data, NULL);
501 return;
94439e4e 502 }
62e76326 503
6bf4f823 504 r = cbdataAlloc(authenticateStateData);
505 r->handler = handler;
1d412b78 506 r->data = cbdataReference(data);
6bf4f823 507 r->auth_user_request = this;
4f0ef8e8 508 AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
62e76326 509
6bf4f823 510 if (auth_state == AUTHENTICATE_STATE_INITIAL) {
511 snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
512 } else {
513 snprintf(buf, 8192, "KK %s\n", client_blob);
94439e4e 514 }
62e76326 515
6bf4f823 516 waiting = 1;
94439e4e 517
6bf4f823 518 safe_free(client_blob);
519 helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
94439e4e 520}
521
94439e4e 522/* clear the NTLM helper of being reserved for future requests */
c78aa667 523static void
76f142cd 524authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request)
94439e4e 525{
f5691f9c 526 AuthNTLMUserRequest *ntlm_request;
527 assert(auth_user_request->user()->auth_type == AUTH_NTLM);
528 ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
bf8fe701 529 debugs(29, 9, "authenticateNTLMReleaseServer: releasing server '" << ntlm_request->authserver << "'");
6bf4f823 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 */
4f0ef8e8 534 /* DPW 2007-05-07
535 * yes, it is possible */
3a11f20d 536 assert(ntlm_request != NULL);
4f0ef8e8 537 if (ntlm_request->authserver) {
26ac0430
AJ
538 helperStatefulReleaseServer(ntlm_request->authserver);
539 ntlm_request->authserver = NULL;
4f0ef8e8 540 }
94439e4e 541}
542
543/* clear any connection related authentication details */
f5691f9c 544void
4f0ef8e8 545AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
94439e4e 546{
4f0ef8e8 547 assert(conn != NULL);
62e76326 548
4f0ef8e8 549 debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
62e76326 550
4f0ef8e8 551 if (conn->auth_user_request == NULL) {
bf8fe701 552 debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
6bf4f823 553 return;
554 }
62e76326 555
6bf4f823 556 if (authserver != NULL)
557 authenticateNTLMReleaseServer(this);
62e76326 558
6bf4f823 559 /* unlock the connection based lock */
4f0ef8e8 560 debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
62e76326 561
4f0ef8e8 562 AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
60d096f4 563}
94439e4e 564
565/*
6bf4f823 566 * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
94439e4e 567 * Auth_user structure.
568 */
f5691f9c 569AuthUserRequest *
570AuthNTLMConfig::decode(char const *proxy_auth)
94439e4e 571{
f5691f9c 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);
94439e4e 578
579 /* all we have to do is identify that it's NTLM - the helper does the rest */
bf8fe701 580 debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
f5691f9c 581 return auth_user_request;
94439e4e 582}
583
82b045dc 584int
f5691f9c 585AuthNTLMUserRequest::authenticated() const
94439e4e 586{
c9acda3a 587 if (auth_state == AUTHENTICATE_STATE_DONE) {
bf8fe701 588 debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
62e76326 589 return 1;
6bf4f823 590 }
62e76326 591
bf8fe701 592 debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
62e76326 593
94439e4e 594 return 0;
595}
596
82b045dc 597void
486bf0fb 598AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
94439e4e 599{
6bf4f823 600 const char *proxy_auth, *blob;
62e76326 601
f5691f9c 602 /* TODO: rename this!! */
e1f7507e 603 AuthUser *local_auth_user;
94439e4e 604 ntlm_user_t *ntlm_user;
94439e4e 605
6bf4f823 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);
610 assert (this);
611
3a73fe76 612 /* Check that we are in the client side, where we can generate
613 * auth challenges */
62e76326 614
486bf0fb 615 if (conn == NULL || !cbdataReferenceValid(conn)) {
6bf4f823 616 auth_state = AUTHENTICATE_STATE_FAILED;
bf8fe701 617 debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
6bf4f823 618 return;
619 }
620
621 if (waiting) {
bf8fe701 622 debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
62e76326 623 return;
3a73fe76 624 }
62e76326 625
6bf4f823 626 if (server_blob) {
bf8fe701 627 debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
6bf4f823 628 return;
629 }
630
631 /* get header */
a9925b40 632 proxy_auth = request->header.getStr(type);
6bf4f823 633
c9c40182 634 /* locate second word */
635 blob = proxy_auth;
636
6ed9bee4 637 /* if proxy_auth is actually NULL, we'd better not manipulate it. */
26ac0430 638 if (blob) {
6ed9bee4 639 while (xisspace(*blob) && *blob)
640 blob++;
c9c40182 641
6ed9bee4 642 while (!xisspace(*blob) && *blob)
643 blob++;
6bf4f823 644
6ed9bee4 645 while (xisspace(*blob) && *blob)
646 blob++;
647 }
6bf4f823 648
649 switch (auth_state) {
62e76326 650
94439e4e 651 case AUTHENTICATE_STATE_NONE:
2324cda2 652 /* we've received a ntlm request. pass to a helper */
bf8fe701 653 debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
6bf4f823 654 auth_state = AUTHENTICATE_STATE_INITIAL;
655 safe_free(client_blob);
656 client_blob=xstrdup(blob);
62e76326 657 conn->auth_type = AUTH_NTLM;
4f0ef8e8 658 assert(conn->auth_user_request == NULL);
f5691f9c 659 conn->auth_user_request = this;
26ac0430
AJ
660 AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
661 this->request = request;
662 HTTPMSGLOCK(this->request);
62e76326 663 return;
f5691f9c 664
62e76326 665 break;
666
6bf4f823 667 case AUTHENTICATE_STATE_INITIAL:
bf8fe701 668 debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
f5691f9c 669
62e76326 670 return;
f5691f9c 671
62e76326 672 break;
673
62e76326 674
6bf4f823 675 case AUTHENTICATE_STATE_IN_PROGRESS:
676 /* we should have received a blob from the client. Hand it off to
677 * some helper */
678 safe_free(client_blob);
62e76326 679
6bf4f823 680 client_blob = xstrdup (blob);
62e76326 681
26ac0430
AJ
682 if (this->request)
683 HTTPMSGUNLOCK(this->request);
684 this->request = request;
685 HTTPMSGLOCK(this->request);
62e76326 686 return;
6bf4f823 687
62e76326 688 break;
689
94439e4e 690 case AUTHENTICATE_STATE_DONE:
26ac0430 691 fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
6bf4f823 692
26ac0430 693 break;
62e76326 694
3c641669 695 case AUTHENTICATE_STATE_FAILED:
62e76326 696 /* we've failed somewhere in authentication */
bf8fe701 697 debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
6bf4f823 698
62e76326 699 return;
6bf4f823 700
701 break;
94439e4e 702 }
62e76326 703
94439e4e 704 return;
705}
82b045dc 706
6bf4f823 707AuthNTLMUserRequest::AuthNTLMUserRequest() :
26ac0430 708 /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
6bf4f823 709 _theUser(NULL)
710{
711 waiting=0;
712 client_blob=0;
713 server_blob=0;
714 authserver=NULL;
01413e4e 715 request = NULL;
6bf4f823 716}
f5691f9c 717
718AuthNTLMUserRequest::~AuthNTLMUserRequest()
719{
6bf4f823 720 safe_free(server_blob);
721 safe_free(client_blob);
f5691f9c 722
6bf4f823 723 if (authserver != NULL) {
bf8fe701 724 debugs(29, 9, "AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '" << authserver << "'");
f5691f9c 725 helperStatefulReleaseServer(authserver);
726 authserver = NULL;
727 }
8d3b341e 728 if (request) {
26ac0430
AJ
729 HTTPMSGUNLOCK(request);
730 request = NULL;
8d3b341e 731 }
f5691f9c 732}
733
f5691f9c 734void
735NTLMUser::deleteSelf() const
736{
737 delete this;
738}
739
f5691f9c 740NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config)
741{
742 proxy_auth_list.head = proxy_auth_list.tail = NULL;
743}
744
745AuthConfig *
746ntlmScheme::createConfig()
747{
748 return &ntlmConfig;
749}
750
6bf4f823 751const char *
752AuthNTLMUserRequest::connLastHeader()
753{
754 return NULL;
755}
756