]>
Commit | Line | Data |
---|---|---|
94439e4e | 1 | |
2 | /* | |
30abd221 | 3 | * $Id: auth_ntlm.cc,v 1.70 2007/05/29 13:31:46 amosjeffries Exp $ |
94439e4e | 4 | * |
5 | * DEBUG: section 29 NTLM Authenticator | |
6bf4f823 | 6 | * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli |
94439e4e | 7 | * |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
94439e4e | 9 | * ---------------------------------------------------------- |
10 | * | |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
94439e4e | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
36 | /* The functions in this file handle authentication. | |
37 | * They DO NOT perform access control or auditing. | |
38 | * See acl.c for access control and client_side.c for auditing */ | |
39 | ||
40 | ||
41 | #include "squid.h" | |
42 | #include "auth_ntlm.h" | |
e6ccf245 | 43 | #include "authenticate.h" |
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" |
f5691f9c | 49 | /* TODO remove this include */ |
50 | #include "ntlmScheme.h" | |
d295d770 | 51 | #include "wordlist.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 | |
f5691f9c | 144 | requirePathnameExists("authparam 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 |
201 | AuthNTLMConfig::registerWithCacheManager(CacheManager & manager) | |
202 | { | |
203 | manager.registerAction("ntlmauthenticator", | |
204 | "NTLM User Authenticator Stats", | |
205 | authenticateNTLMStats, 0, 1); | |
206 | } | |
207 | ||
f5691f9c | 208 | bool |
209 | AuthNTLMConfig::active() const | |
2d70df72 | 210 | { |
f5691f9c | 211 | return authntlm_initialised == 1; |
2d70df72 | 212 | } |
213 | ||
f5691f9c | 214 | bool |
215 | AuthNTLMConfig::configured() const | |
94439e4e | 216 | { |
6bf4f823 | 217 | if ((authenticate != NULL) && (authenticateChildren != 0)) { |
bf8fe701 | 218 | debugs(29, 9, "AuthNTLMConfig::configured: returning configured"); |
f5691f9c | 219 | return true; |
2d70df72 | 220 | } |
62e76326 | 221 | |
bf8fe701 | 222 | debugs(29, 9, "AuthNTLMConfig::configured: returning unconfigured"); |
f5691f9c | 223 | return false; |
94439e4e | 224 | } |
225 | ||
226 | /* NTLM Scheme */ | |
6bf4f823 | 227 | /* See AuthUserRequest.cc::authenticateDirection for return values */ |
82b045dc | 228 | int |
f5691f9c | 229 | AuthNTLMUserRequest::module_direction() |
94439e4e | 230 | { |
bd507204 | 231 | /* null auth_user is checked for by authenticateDirection */ |
62e76326 | 232 | |
6bf4f823 | 233 | if (waiting || client_blob) |
234 | return -1; /* need helper response to continue */ | |
235 | ||
82b045dc | 236 | switch (auth_state) { |
62e76326 | 237 | |
82b045dc | 238 | /* no progress at all. */ |
239 | ||
240 | case AUTHENTICATE_STATE_NONE: | |
bf8fe701 | 241 | debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev."); |
6bf4f823 | 242 | return -2; /* error */ |
62e76326 | 243 | |
3c641669 | 244 | case AUTHENTICATE_STATE_FAILED: |
6bf4f823 | 245 | return -2; /* error */ |
62e76326 | 246 | |
82b045dc | 247 | |
6bf4f823 | 248 | case AUTHENTICATE_STATE_IN_PROGRESS: |
249 | assert(server_blob); | |
250 | return 1; /* send to client */ | |
82b045dc | 251 | |
82b045dc | 252 | case AUTHENTICATE_STATE_DONE: |
6bf4f823 | 253 | return 0; /* do nothing */ |
254 | ||
255 | case AUTHENTICATE_STATE_INITIAL: | |
bf8fe701 | 256 | debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL"); |
6bf4f823 | 257 | return -2; |
94439e4e | 258 | } |
62e76326 | 259 | |
94439e4e | 260 | return -2; |
261 | } | |
262 | ||
f5691f9c | 263 | void |
76f142cd | 264 | AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request) |
94439e4e | 265 | { |
f5691f9c | 266 | AuthNTLMUserRequest *ntlm_request; |
62e76326 | 267 | |
6bf4f823 | 268 | if (!authenticate) |
269 | return; | |
62e76326 | 270 | |
6bf4f823 | 271 | /* New request, no user details */ |
272 | if (auth_user_request == NULL) { | |
bf8fe701 | 273 | debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'"); |
6bf4f823 | 274 | httpHeaderPutStrf(&rep->header, type, "NTLM"); |
275 | ||
276 | if (!keep_alive) { | |
62e76326 | 277 | /* drop the connection */ |
a9925b40 | 278 | rep->header.delByName("keep-alive"); |
62e76326 | 279 | request->flags.proxy_keepalive = 0; |
62e76326 | 280 | } |
6bf4f823 | 281 | } else { |
282 | ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request); | |
94439e4e | 283 | |
6bf4f823 | 284 | switch (ntlm_request->auth_state) { |
62e76326 | 285 | |
6bf4f823 | 286 | case AUTHENTICATE_STATE_FAILED: |
287 | /* here it makes sense to drop the connection, as auth is | |
288 | * tied to it, even if MAYBE the client could handle it - Kinkie */ | |
a9925b40 | 289 | rep->header.delByName("keep-alive"); |
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 */ | |
bf8fe701 | 302 | debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'"); |
6bf4f823 | 303 | httpHeaderPutStrf(&rep->header, type, "NTLM"); |
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 */ | |
bf8fe701 | 308 | debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM " << ntlm_request->server_blob << "'"); |
6bf4f823 | 309 | httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob); |
310 | request->flags.must_keepalive = 1; | |
311 | safe_free(ntlm_request->server_blob); | |
312 | break; | |
62e76326 | 313 | |
62e76326 | 314 | |
6bf4f823 | 315 | default: |
bf8fe701 | 316 | debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << "."); |
6bf4f823 | 317 | fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n"); |
318 | } | |
319 | } | |
320 | } | |
62e76326 | 321 | |
6bf4f823 | 322 | NTLMUser::~NTLMUser() |
323 | { | |
bf8fe701 | 324 | debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'"); |
94439e4e | 325 | } |
326 | ||
327 | static stateful_helper_callback_t | |
328 | authenticateNTLMHandleReply(void *data, void *lastserver, char *reply) | |
329 | { | |
e6ccf245 | 330 | authenticateStateData *r = static_cast<authenticateStateData *>(data); |
6bf4f823 | 331 | |
332 | int valid; | |
94439e4e | 333 | stateful_helper_callback_t result = S_HELPER_UNKNOWN; |
6bf4f823 | 334 | char *blob; |
335 | ||
76f142cd | 336 | AuthUserRequest *auth_user_request; |
6bf4f823 | 337 | AuthUser *auth_user; |
338 | NTLMUser *ntlm_user; | |
f5691f9c | 339 | AuthNTLMUserRequest *ntlm_request; |
62e76326 | 340 | |
bf8fe701 | 341 | debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'"); |
a6151f51 | 342 | valid = cbdataReferenceValid(r->data); |
6bf4f823 | 343 | |
344 | if (!valid) { | |
bf8fe701 | 345 | debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. Releasing helper '" << lastserver << "'."); |
62e76326 | 346 | cbdataReferenceDone(r->data); |
347 | authenticateStateFree(r); | |
bf8fe701 | 348 | debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << S_HELPER_RELEASE); |
62e76326 | 349 | return S_HELPER_RELEASE; |
9bea1d5b | 350 | } |
62e76326 | 351 | |
5d146f7d | 352 | if (!reply) { |
bf8fe701 | 353 | debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!."); |
c9c40182 | 354 | reply = (char *)"BH Internal error"; |
9bea1d5b | 355 | } |
62e76326 | 356 | |
6bf4f823 | 357 | auth_user_request = r->auth_user_request; |
358 | assert(auth_user_request != NULL); | |
359 | ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request); | |
62e76326 | 360 | |
6bf4f823 | 361 | assert(ntlm_request->waiting); |
362 | ntlm_request->waiting = 0; | |
363 | safe_free(ntlm_request->client_blob); | |
62e76326 | 364 | |
6bf4f823 | 365 | auth_user = ntlm_request->user(); |
366 | assert(auth_user != NULL); | |
367 | assert(auth_user->auth_type == AUTH_NTLM); | |
368 | ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user()); | |
62e76326 | 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); | |
384 | ntlm_request->server_blob = xstrdup(blob); | |
385 | ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS; | |
386 | auth_user_request->denyMessage("Authenication in progress"); | |
bf8fe701 | 387 | debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'"); |
6bf4f823 | 388 | result = S_HELPER_RESERVE; |
c9c40182 | 389 | } else if (strncasecmp(reply, "AF ", 3) == 0) { |
62e76326 | 390 | /* we're finished, release the helper */ |
6bf4f823 | 391 | ntlm_user->username(blob); |
392 | auth_user_request->denyMessage("Login successful"); | |
393 | safe_free(ntlm_request->server_blob); | |
62e76326 | 394 | |
6bf4f823 | 395 | result = S_HELPER_RELEASE; |
bf8fe701 | 396 | debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'"); |
71ee0c43 | 397 | /* connection is authenticated */ |
398 | debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_user->username()); | |
399 | /* see if this is an existing user with a different proxy_auth | |
400 | * string */ | |
401 | auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username())); | |
402 | auth_user_t *local_auth_user = ntlm_request->user(); | |
71ee0c43 | 403 | while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0)) |
404 | usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next); | |
405 | if (usernamehash) { | |
406 | /* we can't seamlessly recheck the username due to the | |
407 | * challenge-response nature of the protocol. | |
408 | * Just free the temporary auth_user */ | |
409 | usernamehash->user()->absorb(local_auth_user); | |
410 | //authenticateAuthUserMerge(local_auth_user, usernamehash->user()); | |
411 | local_auth_user = usernamehash->user(); | |
412 | ntlm_request->_auth_user = local_auth_user; | |
413 | } else { | |
414 | /* store user in hash's */ | |
415 | local_auth_user->addToNameCache(); | |
416 | // authenticateUserNameCacheAdd(local_auth_user); | |
417 | } | |
418 | /* set these to now because this is either a new login from an | |
419 | * existing user or a new user */ | |
420 | local_auth_user->expiretime = current_time.tv_sec; | |
421 | authenticateNTLMReleaseServer(ntlm_request); | |
422 | ntlm_request->auth_state = AUTHENTICATE_STATE_DONE; | |
c9c40182 | 423 | } else if (strncasecmp(reply, "NA ", 3) == 0) { |
6bf4f823 | 424 | /* authentication failure (wrong password, etc.) */ |
425 | auth_user_request->denyMessage(blob); | |
62e76326 | 426 | ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; |
6bf4f823 | 427 | safe_free(ntlm_request->server_blob); |
428 | authenticateNTLMReleaseServer(ntlm_request); | |
429 | result = S_HELPER_RELEASE; | |
bf8fe701 | 430 | debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'"); |
5d146f7d | 431 | } else if (strncasecmp(reply, "BH ", 3) == 0) { |
62e76326 | 432 | /* TODO kick off a refresh process. This can occur after a YR or after |
6bf4f823 | 433 | * a KK. If after a YR release the helper and resubmit the request via |
434 | * Authenticate NTLM start. | |
435 | * If after a KK deny the user's request w/ 407 and mark the helper as | |
62e76326 | 436 | * Needing YR. */ |
6bf4f823 | 437 | auth_user_request->denyMessage(blob); |
62e76326 | 438 | ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; |
6bf4f823 | 439 | safe_free(ntlm_request->server_blob); |
440 | authenticateNTLMReleaseServer(ntlm_request); | |
441 | result = S_HELPER_RELEASE; | |
bf8fe701 | 442 | debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'"); |
94439e4e | 443 | } else { |
6bf4f823 | 444 | /* protocol error */ |
445 | fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply); | |
94439e4e | 446 | } |
62e76326 | 447 | |
5d146f7d | 448 | r->handler(r->data, NULL); |
fa80a8ef | 449 | cbdataReferenceDone(r->data); |
94439e4e | 450 | authenticateStateFree(r); |
bf8fe701 | 451 | debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << result); |
94439e4e | 452 | return result; |
453 | } | |
454 | ||
94439e4e | 455 | static void |
456 | authenticateNTLMStats(StoreEntry * sentry) | |
457 | { | |
9522b380 | 458 | helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics"); |
94439e4e | 459 | } |
460 | ||
94439e4e | 461 | |
462 | /* send the initial data to a stateful ntlm authenticator module */ | |
f5691f9c | 463 | void |
464 | AuthNTLMUserRequest::module_start(RH * handler, void *data) | |
94439e4e | 465 | { |
94439e4e | 466 | authenticateStateData *r = NULL; |
6bf4f823 | 467 | static char buf[8192]; |
94439e4e | 468 | ntlm_user_t *ntlm_user; |
6bf4f823 | 469 | auth_user_t *auth_user = user(); |
94439e4e | 470 | |
94439e4e | 471 | assert(data); |
6bf4f823 | 472 | assert(handler); |
473 | assert(auth_user); | |
9970d4b1 | 474 | assert(auth_user->auth_type == AUTH_NTLM); |
62e76326 | 475 | |
6bf4f823 | 476 | ntlm_user = dynamic_cast<ntlm_user_t *>(user()); |
62e76326 | 477 | |
bf8fe701 | 478 | debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'"); |
62e76326 | 479 | |
f5691f9c | 480 | if (ntlmConfig.authenticate == NULL) { |
bf8fe701 | 481 | debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified."); |
62e76326 | 482 | handler(data, NULL); |
483 | return; | |
94439e4e | 484 | } |
62e76326 | 485 | |
6bf4f823 | 486 | r = cbdataAlloc(authenticateStateData); |
487 | r->handler = handler; | |
1d412b78 | 488 | r->data = cbdataReference(data); |
6bf4f823 | 489 | r->auth_user_request = this; |
4f0ef8e8 | 490 | AUTHUSERREQUESTLOCK(r->auth_user_request, "r"); |
62e76326 | 491 | |
6bf4f823 | 492 | if (auth_state == AUTHENTICATE_STATE_INITIAL) { |
493 | snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? | |
494 | } else { | |
495 | snprintf(buf, 8192, "KK %s\n", client_blob); | |
94439e4e | 496 | } |
62e76326 | 497 | |
6bf4f823 | 498 | waiting = 1; |
94439e4e | 499 | |
6bf4f823 | 500 | safe_free(client_blob); |
501 | helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver); | |
94439e4e | 502 | } |
503 | ||
94439e4e | 504 | /* clear the NTLM helper of being reserved for future requests */ |
c78aa667 | 505 | static void |
76f142cd | 506 | authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request) |
94439e4e | 507 | { |
f5691f9c | 508 | AuthNTLMUserRequest *ntlm_request; |
509 | assert(auth_user_request->user()->auth_type == AUTH_NTLM); | |
510 | ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request); | |
bf8fe701 | 511 | debugs(29, 9, "authenticateNTLMReleaseServer: releasing server '" << ntlm_request->authserver << "'"); |
6bf4f823 | 512 | /* is it possible for the server to be NULL? hno seems to think so. |
513 | * Let's see what happens, might segfault in helperStatefulReleaseServer | |
514 | * if it does. I leave it like this not to cover possibly problematic | |
515 | * code-paths. Kinkie */ | |
4f0ef8e8 | 516 | /* DPW 2007-05-07 |
517 | * yes, it is possible */ | |
518 | if (ntlm_request->authserver) { | |
519 | helperStatefulReleaseServer(ntlm_request->authserver); | |
520 | ntlm_request->authserver = NULL; | |
521 | } | |
94439e4e | 522 | } |
523 | ||
524 | /* clear any connection related authentication details */ | |
f5691f9c | 525 | void |
4f0ef8e8 | 526 | AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn) |
94439e4e | 527 | { |
4f0ef8e8 | 528 | assert(conn != NULL); |
62e76326 | 529 | |
4f0ef8e8 | 530 | debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')"); |
62e76326 | 531 | |
4f0ef8e8 | 532 | if (conn->auth_user_request == NULL) { |
bf8fe701 | 533 | debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request"); |
6bf4f823 | 534 | return; |
535 | } | |
62e76326 | 536 | |
6bf4f823 | 537 | if (authserver != NULL) |
538 | authenticateNTLMReleaseServer(this); | |
62e76326 | 539 | |
6bf4f823 | 540 | /* unlock the connection based lock */ |
4f0ef8e8 | 541 | debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'."); |
62e76326 | 542 | |
4f0ef8e8 | 543 | AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn"); |
60d096f4 | 544 | } |
94439e4e | 545 | |
546 | /* | |
6bf4f823 | 547 | * Decode a NTLM [Proxy-]Auth string, placing the results in the passed |
94439e4e | 548 | * Auth_user structure. |
549 | */ | |
f5691f9c | 550 | AuthUserRequest * |
551 | AuthNTLMConfig::decode(char const *proxy_auth) | |
94439e4e | 552 | { |
f5691f9c | 553 | NTLMUser *newUser = new NTLMUser(&ntlmConfig); |
554 | AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest (); | |
555 | assert(auth_user_request->user() == NULL); | |
556 | auth_user_request->user(newUser); | |
557 | auth_user_request->user()->auth_type = AUTH_NTLM; | |
558 | auth_user_request->user()->addRequest(auth_user_request); | |
94439e4e | 559 | |
560 | /* all we have to do is identify that it's NTLM - the helper does the rest */ | |
bf8fe701 | 561 | debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication"); |
f5691f9c | 562 | return auth_user_request; |
94439e4e | 563 | } |
564 | ||
82b045dc | 565 | int |
f5691f9c | 566 | AuthNTLMUserRequest::authenticated() const |
94439e4e | 567 | { |
c9acda3a | 568 | if (auth_state == AUTHENTICATE_STATE_DONE) { |
bf8fe701 | 569 | debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated."); |
62e76326 | 570 | return 1; |
6bf4f823 | 571 | } |
62e76326 | 572 | |
bf8fe701 | 573 | debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated."); |
62e76326 | 574 | |
94439e4e | 575 | return 0; |
576 | } | |
577 | ||
82b045dc | 578 | void |
f5691f9c | 579 | AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type) |
94439e4e | 580 | { |
6bf4f823 | 581 | const char *proxy_auth, *blob; |
62e76326 | 582 | |
f5691f9c | 583 | /* TODO: rename this!! */ |
6bf4f823 | 584 | auth_user_t *local_auth_user; |
94439e4e | 585 | ntlm_user_t *ntlm_user; |
94439e4e | 586 | |
6bf4f823 | 587 | local_auth_user = user(); |
588 | assert(local_auth_user); | |
589 | assert(local_auth_user->auth_type == AUTH_NTLM); | |
590 | ntlm_user = dynamic_cast<ntlm_user_t *>(local_auth_user); | |
591 | assert (this); | |
592 | ||
3a73fe76 | 593 | /* Check that we are in the client side, where we can generate |
594 | * auth challenges */ | |
62e76326 | 595 | |
a2ac85d9 | 596 | if (conn.getRaw() == NULL) { |
6bf4f823 | 597 | auth_state = AUTHENTICATE_STATE_FAILED; |
bf8fe701 | 598 | debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!"); |
6bf4f823 | 599 | return; |
600 | } | |
601 | ||
602 | if (waiting) { | |
bf8fe701 | 603 | debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!"); |
62e76326 | 604 | return; |
3a73fe76 | 605 | } |
62e76326 | 606 | |
6bf4f823 | 607 | if (server_blob) { |
bf8fe701 | 608 | debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!"); |
6bf4f823 | 609 | return; |
610 | } | |
611 | ||
612 | /* get header */ | |
a9925b40 | 613 | proxy_auth = request->header.getStr(type); |
6bf4f823 | 614 | |
c9c40182 | 615 | /* locate second word */ |
616 | blob = proxy_auth; | |
617 | ||
618 | while (xisspace(*blob) && *blob) | |
619 | blob++; | |
620 | ||
621 | while (!xisspace(*blob) && *blob) | |
622 | blob++; | |
6bf4f823 | 623 | |
c9c40182 | 624 | while (xisspace(*blob) && *blob) |
6bf4f823 | 625 | blob++; |
626 | ||
627 | switch (auth_state) { | |
62e76326 | 628 | |
94439e4e | 629 | case AUTHENTICATE_STATE_NONE: |
6bf4f823 | 630 | /* we've recieved a ntlm request. pass to a helper */ |
bf8fe701 | 631 | debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'"); |
6bf4f823 | 632 | auth_state = AUTHENTICATE_STATE_INITIAL; |
633 | safe_free(client_blob); | |
634 | client_blob=xstrdup(blob); | |
62e76326 | 635 | conn->auth_type = AUTH_NTLM; |
4f0ef8e8 | 636 | assert(conn->auth_user_request == NULL); |
f5691f9c | 637 | conn->auth_user_request = this; |
4f0ef8e8 | 638 | AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn"); |
62e76326 | 639 | return; |
f5691f9c | 640 | |
62e76326 | 641 | break; |
642 | ||
6bf4f823 | 643 | case AUTHENTICATE_STATE_INITIAL: |
bf8fe701 | 644 | debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper"); |
f5691f9c | 645 | |
62e76326 | 646 | return; |
f5691f9c | 647 | |
62e76326 | 648 | break; |
649 | ||
62e76326 | 650 | |
6bf4f823 | 651 | case AUTHENTICATE_STATE_IN_PROGRESS: |
652 | /* we should have received a blob from the client. Hand it off to | |
653 | * some helper */ | |
654 | safe_free(client_blob); | |
62e76326 | 655 | |
6bf4f823 | 656 | client_blob = xstrdup (blob); |
62e76326 | 657 | |
658 | return; | |
6bf4f823 | 659 | |
62e76326 | 660 | break; |
661 | ||
94439e4e | 662 | case AUTHENTICATE_STATE_DONE: |
71ee0c43 | 663 | fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n"); |
6bf4f823 | 664 | |
71ee0c43 | 665 | break; |
62e76326 | 666 | |
3c641669 | 667 | case AUTHENTICATE_STATE_FAILED: |
62e76326 | 668 | /* we've failed somewhere in authentication */ |
bf8fe701 | 669 | debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth); |
6bf4f823 | 670 | |
62e76326 | 671 | return; |
6bf4f823 | 672 | |
673 | break; | |
94439e4e | 674 | } |
62e76326 | 675 | |
94439e4e | 676 | return; |
677 | } | |
82b045dc | 678 | |
6bf4f823 | 679 | AuthNTLMUserRequest::AuthNTLMUserRequest() : |
680 | conn(NULL), auth_state(AUTHENTICATE_STATE_NONE), | |
681 | _theUser(NULL) | |
682 | { | |
683 | waiting=0; | |
684 | client_blob=0; | |
685 | server_blob=0; | |
686 | authserver=NULL; | |
687 | } | |
f5691f9c | 688 | |
689 | AuthNTLMUserRequest::~AuthNTLMUserRequest() | |
690 | { | |
6bf4f823 | 691 | safe_free(server_blob); |
692 | safe_free(client_blob); | |
f5691f9c | 693 | |
6bf4f823 | 694 | if (authserver != NULL) { |
bf8fe701 | 695 | debugs(29, 9, "AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '" << authserver << "'"); |
f5691f9c | 696 | helperStatefulReleaseServer(authserver); |
697 | authserver = NULL; | |
698 | } | |
699 | } | |
700 | ||
f5691f9c | 701 | void |
702 | NTLMUser::deleteSelf() const | |
703 | { | |
704 | delete this; | |
705 | } | |
706 | ||
f5691f9c | 707 | NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config) |
708 | { | |
709 | proxy_auth_list.head = proxy_auth_list.tail = NULL; | |
710 | } | |
711 | ||
712 | AuthConfig * | |
713 | ntlmScheme::createConfig() | |
714 | { | |
715 | return &ntlmConfig; | |
716 | } | |
717 | ||
6bf4f823 | 718 | const char * |
719 | AuthNTLMUserRequest::connLastHeader() | |
720 | { | |
721 | return NULL; | |
722 | } | |
723 |