]>
Commit | Line | Data |
---|---|---|
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 | 53 | static void |
76f142cd | 54 | authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request); |
6bf4f823 | 55 | |
56 | ||
94439e4e | 57 | static void |
58 | authenticateStateFree(authenticateStateData * r) | |
59 | { | |
4f0ef8e8 | 60 | AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r"); |
94439e4e | 61 | cbdataFree(r); |
62 | } | |
63 | ||
64 | /* NTLM Scheme */ | |
65 | static HLPSCB authenticateNTLMHandleReply; | |
94439e4e | 66 | static AUTHSSTATS authenticateNTLMStats; |
94439e4e | 67 | |
94439e4e | 68 | static statefulhelper *ntlmauthenticators = NULL; |
69 | ||
70 | CBDATA_TYPE(authenticateStateData); | |
71 | ||
72 | static int authntlm_initialised = 0; | |
73 | ||
f5691f9c | 74 | static auth_ntlm_config ntlmConfig; |
94439e4e | 75 | |
76 | static hash_table *proxy_auth_cache = NULL; | |
77 | ||
78 | /* | |
79 | * | |
80 | * Private Functions | |
81 | * | |
82 | */ | |
83 | ||
f5691f9c | 84 | /* move to ntlmScheme.cc */ |
85 | void | |
86 | ntlmScheme::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 | 108 | void |
109 | AuthNTLMConfig::done() | |
94439e4e | 110 | { |
f5691f9c | 111 | if (authenticate) |
112 | wordlistDestroy(&authenticate); | |
94439e4e | 113 | } |
114 | ||
f5691f9c | 115 | void |
116 | AuthNTLMConfig::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 | 132 | AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1) |
133 | { } | |
62e76326 | 134 | |
f5691f9c | 135 | void |
136 | AuthNTLMConfig::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 | 165 | const char * |
166 | AuthNTLMConfig::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 | 173 | void |
174 | AuthNTLMConfig::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 | 200 | void |
15fab853 | 201 | AuthNTLMConfig::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 | 209 | bool |
210 | AuthNTLMConfig::active() const | |
2d70df72 | 211 | { |
f5691f9c | 212 | return authntlm_initialised == 1; |
2d70df72 | 213 | } |
214 | ||
f5691f9c | 215 | bool |
216 | AuthNTLMConfig::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 | 229 | int |
f5691f9c | 230 | AuthNTLMUserRequest::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 | 264 | void |
76f142cd | 265 | AuthNTLMConfig::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 | 326 | NTLMUser::~NTLMUser() |
327 | { | |
bf8fe701 | 328 | debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'"); |
94439e4e | 329 | } |
330 | ||
331 | static stateful_helper_callback_t | |
332 | authenticateNTLMHandleReply(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 | 473 | static void |
474 | authenticateNTLMStats(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 | 481 | void |
482 | AuthNTLMUserRequest::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 | 523 | static void |
76f142cd | 524 | authenticateNTLMReleaseServer(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 | 544 | void |
4f0ef8e8 | 545 | AuthNTLMUserRequest::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 | 569 | AuthUserRequest * |
570 | AuthNTLMConfig::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 | 584 | int |
f5691f9c | 585 | AuthNTLMUserRequest::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 | 597 | void |
486bf0fb | 598 | AuthNTLMUserRequest::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 | 707 | AuthNTLMUserRequest::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 | |
718 | AuthNTLMUserRequest::~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 | 734 | void |
735 | NTLMUser::deleteSelf() const | |
736 | { | |
737 | delete this; | |
738 | } | |
739 | ||
f5691f9c | 740 | NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config) |
741 | { | |
742 | proxy_auth_list.head = proxy_auth_list.tail = NULL; | |
743 | } | |
744 | ||
745 | AuthConfig * | |
746 | ntlmScheme::createConfig() | |
747 | { | |
748 | return &ntlmConfig; | |
749 | } | |
750 | ||
6bf4f823 | 751 | const char * |
752 | AuthNTLMUserRequest::connLastHeader() | |
753 | { | |
754 | return NULL; | |
755 | } | |
756 |