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