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