]>
Commit | Line | Data |
---|---|---|
94439e4e | 1 | |
2 | /* | |
a9925b40 | 3 | * $Id: auth_ntlm.cc,v 1.57 2006/05/06 22:13:19 wessels 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" |
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" |
c78aa667 | 51 | |
6bf4f823 | 52 | static void |
53 | authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request); | |
54 | ||
55 | ||
94439e4e | 56 | static void |
57 | authenticateStateFree(authenticateStateData * r) | |
58 | { | |
59 | cbdataFree(r); | |
60 | } | |
61 | ||
62 | /* NTLM Scheme */ | |
63 | static HLPSCB authenticateNTLMHandleReply; | |
94439e4e | 64 | static AUTHSSTATS authenticateNTLMStats; |
94439e4e | 65 | |
94439e4e | 66 | static statefulhelper *ntlmauthenticators = NULL; |
67 | ||
68 | CBDATA_TYPE(authenticateStateData); | |
69 | ||
70 | static int authntlm_initialised = 0; | |
71 | ||
6bf4f823 | 72 | //static MemAllocatorProxy *ntlm_user_hash_pool = NULL; |
e6ccf245 | 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. */ |
6bf4f823 | 89 | debug(29, 2) ("ntlmScheme::done: shutting down NTLM authentication.\n"); |
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 | |
6bf4f823 | 104 | debug(29, 2) ("ntlmScheme::done: NTLM authentication Shutdown.\n"); |
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 { |
6bf4f823 | 150 | debug(28, 0) ("AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '%s'\n", 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 | { |
6bf4f823 | 176 | static unsigned char ntlm_was_already_initialised = 0; |
62e76326 | 177 | |
f5691f9c | 178 | if (authenticate) { |
6bf4f823 | 179 | #if PLACEHOLDER |
62e76326 | 180 | |
62e76326 | 181 | if (!ntlm_user_hash_pool) |
182 | ||
b001e822 | 183 | ntlm_user_hash_pool = new MemAllocatorProxy("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer)); |
62e76326 | 184 | |
6bf4f823 | 185 | #endif |
186 | ||
62e76326 | 187 | authntlm_initialised = 1; |
188 | ||
189 | if (ntlmauthenticators == NULL) | |
190 | ntlmauthenticators = helperStatefulCreate("ntlmauthenticator"); | |
191 | ||
192 | if (!proxy_auth_cache) | |
193 | proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); | |
194 | ||
195 | assert(proxy_auth_cache); | |
196 | ||
f5691f9c | 197 | ntlmauthenticators->cmdline = authenticate; |
62e76326 | 198 | |
f5691f9c | 199 | ntlmauthenticators->n_to_start = authenticateChildren; |
62e76326 | 200 | |
201 | ntlmauthenticators->ipc_type = IPC_STREAM; | |
202 | ||
62e76326 | 203 | helperStatefulOpenServers(ntlmauthenticators); |
204 | ||
6bf4f823 | 205 | if (!ntlm_was_already_initialised) { |
62e76326 | 206 | cachemgrRegister("ntlmauthenticator", |
207 | "NTLM User Authenticator Stats", | |
208 | authenticateNTLMStats, 0, 1); | |
6bf4f823 | 209 | ntlm_was_already_initialised++; |
62e76326 | 210 | } |
211 | ||
212 | CBDATA_INIT_TYPE(authenticateStateData); | |
94439e4e | 213 | } |
214 | } | |
215 | ||
f5691f9c | 216 | bool |
217 | AuthNTLMConfig::active() const | |
2d70df72 | 218 | { |
f5691f9c | 219 | return authntlm_initialised == 1; |
2d70df72 | 220 | } |
221 | ||
f5691f9c | 222 | bool |
223 | AuthNTLMConfig::configured() const | |
94439e4e | 224 | { |
6bf4f823 | 225 | if ((authenticate != NULL) && (authenticateChildren != 0)) { |
226 | debug(29, 9) ("AuthNTLMConfig::configured: returning configured\n"); | |
f5691f9c | 227 | return true; |
2d70df72 | 228 | } |
62e76326 | 229 | |
6bf4f823 | 230 | debug(29, 9) ("AuthNTLMConfig::configured: returning unconfigured\n"); |
f5691f9c | 231 | return false; |
94439e4e | 232 | } |
233 | ||
234 | /* NTLM Scheme */ | |
6bf4f823 | 235 | /* See AuthUserRequest.cc::authenticateDirection for return values */ |
82b045dc | 236 | int |
f5691f9c | 237 | AuthNTLMUserRequest::module_direction() |
94439e4e | 238 | { |
bd507204 | 239 | /* null auth_user is checked for by authenticateDirection */ |
62e76326 | 240 | |
6bf4f823 | 241 | if (waiting || client_blob) |
242 | return -1; /* need helper response to continue */ | |
243 | ||
82b045dc | 244 | switch (auth_state) { |
62e76326 | 245 | |
82b045dc | 246 | /* no progress at all. */ |
247 | ||
248 | case AUTHENTICATE_STATE_NONE: | |
6bf4f823 | 249 | debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate for request %p!. Report a bug to squid-dev.\n",this); |
250 | return -2; /* error */ | |
62e76326 | 251 | |
3c641669 | 252 | case AUTHENTICATE_STATE_FAILED: |
6bf4f823 | 253 | return -2; /* error */ |
62e76326 | 254 | |
82b045dc | 255 | |
6bf4f823 | 256 | case AUTHENTICATE_STATE_IN_PROGRESS: |
257 | assert(server_blob); | |
258 | return 1; /* send to client */ | |
82b045dc | 259 | |
6bf4f823 | 260 | case AUTHENTICATE_STATE_FINISHED: |
261 | return 0; /* do nothing */ | |
82b045dc | 262 | |
263 | case AUTHENTICATE_STATE_DONE: | |
6bf4f823 | 264 | return 0; /* do nothing */ |
265 | ||
266 | case AUTHENTICATE_STATE_INITIAL: | |
267 | debug(29, 1) ("AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n"); | |
268 | return -2; | |
94439e4e | 269 | } |
62e76326 | 270 | |
94439e4e | 271 | return -2; |
272 | } | |
273 | ||
f5691f9c | 274 | void |
275 | AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request) | |
94439e4e | 276 | { |
f5691f9c | 277 | AuthNTLMUserRequest *ntlm_request; |
62e76326 | 278 | |
6bf4f823 | 279 | if (!authenticate) |
280 | return; | |
62e76326 | 281 | |
6bf4f823 | 282 | /* New request, no user details */ |
283 | if (auth_user_request == NULL) { | |
284 | debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type); | |
285 | httpHeaderPutStrf(&rep->header, type, "NTLM"); | |
286 | ||
287 | if (!keep_alive) { | |
62e76326 | 288 | /* drop the connection */ |
a9925b40 | 289 | rep->header.delByName("keep-alive"); |
62e76326 | 290 | request->flags.proxy_keepalive = 0; |
62e76326 | 291 | } |
6bf4f823 | 292 | } else { |
293 | ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request); | |
94439e4e | 294 | |
6bf4f823 | 295 | switch (ntlm_request->auth_state) { |
62e76326 | 296 | |
6bf4f823 | 297 | case AUTHENTICATE_STATE_FAILED: |
298 | /* here it makes sense to drop the connection, as auth is | |
299 | * tied to it, even if MAYBE the client could handle it - Kinkie */ | |
a9925b40 | 300 | rep->header.delByName("keep-alive"); |
6bf4f823 | 301 | request->flags.proxy_keepalive = 0; |
302 | /* fall through */ | |
94439e4e | 303 | |
6bf4f823 | 304 | case AUTHENTICATE_STATE_FINISHED: |
305 | /* Special case: authentication finished OK but disallowed by ACL. | |
306 | * Need to start over to give the client another chance. | |
307 | */ | |
308 | /* fall through */ | |
62e76326 | 309 | |
6bf4f823 | 310 | case AUTHENTICATE_STATE_NONE: |
311 | /* semantic change: do not drop the connection. | |
312 | * 2.5 implementation used to keep it open - Kinkie */ | |
313 | debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type); | |
314 | httpHeaderPutStrf(&rep->header, type, "NTLM"); | |
315 | break; | |
62e76326 | 316 | |
6bf4f823 | 317 | case AUTHENTICATE_STATE_IN_PROGRESS: |
318 | /* we're waiting for a response from the client. Pass it the blob */ | |
319 | debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->server_blob); | |
320 | httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob); | |
321 | request->flags.must_keepalive = 1; | |
322 | safe_free(ntlm_request->server_blob); | |
323 | break; | |
62e76326 | 324 | |
62e76326 | 325 | |
6bf4f823 | 326 | default: |
327 | debug(29, 0) ("AuthNTLMConfig::fixHeader: state %d.\n", ntlm_request->auth_state); | |
328 | fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n"); | |
329 | } | |
330 | } | |
331 | } | |
62e76326 | 332 | |
6bf4f823 | 333 | NTLMUser::~NTLMUser() |
334 | { | |
335 | debug(29, 5) ("NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '%p'\n",this); | |
94439e4e | 336 | } |
337 | ||
338 | static stateful_helper_callback_t | |
339 | authenticateNTLMHandleReply(void *data, void *lastserver, char *reply) | |
340 | { | |
e6ccf245 | 341 | authenticateStateData *r = static_cast<authenticateStateData *>(data); |
6bf4f823 | 342 | |
343 | int valid; | |
94439e4e | 344 | stateful_helper_callback_t result = S_HELPER_UNKNOWN; |
6bf4f823 | 345 | char *blob; |
346 | ||
94439e4e | 347 | auth_user_request_t *auth_user_request; |
6bf4f823 | 348 | AuthUser *auth_user; |
349 | NTLMUser *ntlm_user; | |
f5691f9c | 350 | AuthNTLMUserRequest *ntlm_request; |
62e76326 | 351 | |
6bf4f823 | 352 | debug(29, 8) ("authenticateNTLMHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>"); |
a6151f51 | 353 | valid = cbdataReferenceValid(r->data); |
6bf4f823 | 354 | |
355 | if (!valid) { | |
356 | debug(29, 1) ("authenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver); | |
62e76326 | 357 | cbdataReferenceDone(r->data); |
358 | authenticateStateFree(r); | |
6bf4f823 | 359 | debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE); |
62e76326 | 360 | return S_HELPER_RELEASE; |
9bea1d5b | 361 | } |
62e76326 | 362 | |
5d146f7d | 363 | if (!reply) { |
7da10d2b | 364 | debug(29, 1) ("authenticateNTLMHandleReply: Helper '%p' crashed!.\n", lastserver); |
c9c40182 | 365 | reply = (char *)"BH Internal error"; |
9bea1d5b | 366 | } |
62e76326 | 367 | |
6bf4f823 | 368 | auth_user_request = r->auth_user_request; |
369 | assert(auth_user_request != NULL); | |
370 | ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request); | |
62e76326 | 371 | |
6bf4f823 | 372 | assert(ntlm_request->waiting); |
373 | ntlm_request->waiting = 0; | |
374 | safe_free(ntlm_request->client_blob); | |
62e76326 | 375 | |
6bf4f823 | 376 | auth_user = ntlm_request->user(); |
377 | assert(auth_user != NULL); | |
378 | assert(auth_user->auth_type == AUTH_NTLM); | |
379 | ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user()); | |
62e76326 | 380 | |
6bf4f823 | 381 | if (ntlm_request->authserver == NULL) |
382 | ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver); | |
383 | else | |
384 | assert(ntlm_request->authserver == lastserver); | |
62e76326 | 385 | |
6bf4f823 | 386 | /* seperate out the useful data */ |
387 | blob = strchr(reply, ' '); | |
62e76326 | 388 | |
c9c40182 | 389 | if (blob) |
6bf4f823 | 390 | blob++; |
62e76326 | 391 | |
c9c40182 | 392 | if (strncasecmp(reply, "TT ", 3) == 0) { |
6bf4f823 | 393 | /* we have been given a blob to send to the client */ |
394 | safe_free(ntlm_request->server_blob); | |
395 | ntlm_request->server_blob = xstrdup(blob); | |
396 | ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS; | |
397 | auth_user_request->denyMessage("Authenication in progress"); | |
398 | debug(29, 4) ("authenticateNTLMHandleReply: Need to challenge the client with a server blob '%s'\n", blob); | |
399 | result = S_HELPER_RESERVE; | |
c9c40182 | 400 | } else if (strncasecmp(reply, "AF ", 3) == 0) { |
62e76326 | 401 | /* we're finished, release the helper */ |
6bf4f823 | 402 | ntlm_user->username(blob); |
403 | auth_user_request->denyMessage("Login successful"); | |
404 | safe_free(ntlm_request->server_blob); | |
405 | authenticateNTLMReleaseServer(ntlm_request); | |
406 | ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED; | |
62e76326 | 407 | |
6bf4f823 | 408 | result = S_HELPER_RELEASE; |
409 | debug(29, 4) ("authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '%s'\n", blob); | |
c9c40182 | 410 | } else if (strncasecmp(reply, "NA ", 3) == 0) { |
6bf4f823 | 411 | /* authentication failure (wrong password, etc.) */ |
412 | auth_user_request->denyMessage(blob); | |
62e76326 | 413 | ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; |
6bf4f823 | 414 | safe_free(ntlm_request->server_blob); |
415 | authenticateNTLMReleaseServer(ntlm_request); | |
416 | result = S_HELPER_RELEASE; | |
417 | debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", blob); | |
5d146f7d | 418 | } else if (strncasecmp(reply, "BH ", 3) == 0) { |
62e76326 | 419 | /* TODO kick off a refresh process. This can occur after a YR or after |
6bf4f823 | 420 | * a KK. If after a YR release the helper and resubmit the request via |
421 | * Authenticate NTLM start. | |
422 | * If after a KK deny the user's request w/ 407 and mark the helper as | |
62e76326 | 423 | * Needing YR. */ |
6bf4f823 | 424 | auth_user_request->denyMessage(blob); |
62e76326 | 425 | ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; |
6bf4f823 | 426 | safe_free(ntlm_request->server_blob); |
427 | authenticateNTLMReleaseServer(ntlm_request); | |
428 | result = S_HELPER_RELEASE; | |
429 | debug(29, 1) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply); | |
94439e4e | 430 | } else { |
6bf4f823 | 431 | /* protocol error */ |
432 | fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply); | |
94439e4e | 433 | } |
62e76326 | 434 | |
5d146f7d | 435 | r->handler(r->data, NULL); |
fa80a8ef | 436 | cbdataReferenceDone(r->data); |
94439e4e | 437 | authenticateStateFree(r); |
6bf4f823 | 438 | debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", result); |
94439e4e | 439 | return result; |
440 | } | |
441 | ||
94439e4e | 442 | static void |
443 | authenticateNTLMStats(StoreEntry * sentry) | |
444 | { | |
445 | storeAppendPrintf(sentry, "NTLM Authenticator Statistics:\n"); | |
446 | helperStatefulStats(sentry, ntlmauthenticators); | |
447 | } | |
448 | ||
94439e4e | 449 | |
450 | /* send the initial data to a stateful ntlm authenticator module */ | |
f5691f9c | 451 | void |
452 | AuthNTLMUserRequest::module_start(RH * handler, void *data) | |
94439e4e | 453 | { |
94439e4e | 454 | authenticateStateData *r = NULL; |
6bf4f823 | 455 | static char buf[8192]; |
94439e4e | 456 | ntlm_user_t *ntlm_user; |
6bf4f823 | 457 | auth_user_t *auth_user = user(); |
94439e4e | 458 | |
94439e4e | 459 | assert(data); |
6bf4f823 | 460 | assert(handler); |
461 | assert(auth_user); | |
9970d4b1 | 462 | assert(auth_user->auth_type == AUTH_NTLM); |
62e76326 | 463 | |
6bf4f823 | 464 | ntlm_user = dynamic_cast<ntlm_user_t *>(user()); |
62e76326 | 465 | |
6bf4f823 | 466 | debug(29, 8) ("AuthNTLMUserRequest::module_start: auth state is '%d'\n", auth_state); |
62e76326 | 467 | |
f5691f9c | 468 | if (ntlmConfig.authenticate == NULL) { |
6bf4f823 | 469 | debug(29, 0) ("AuthNTLMUserRequest::module_start: no NTLM program specified."); |
62e76326 | 470 | handler(data, NULL); |
471 | return; | |
94439e4e | 472 | } |
62e76326 | 473 | |
6bf4f823 | 474 | r = cbdataAlloc(authenticateStateData); |
475 | r->handler = handler; | |
1d412b78 | 476 | r->data = cbdataReference(data); |
6bf4f823 | 477 | r->auth_user_request = this; |
62e76326 | 478 | |
6bf4f823 | 479 | lock() |
62e76326 | 480 | |
6bf4f823 | 481 | ; |
482 | if (auth_state == AUTHENTICATE_STATE_INITIAL) { | |
483 | snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? | |
484 | } else { | |
485 | snprintf(buf, 8192, "KK %s\n", client_blob); | |
94439e4e | 486 | } |
62e76326 | 487 | |
6bf4f823 | 488 | waiting = 1; |
94439e4e | 489 | |
6bf4f823 | 490 | safe_free(client_blob); |
491 | helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver); | |
94439e4e | 492 | } |
493 | ||
94439e4e | 494 | /* clear the NTLM helper of being reserved for future requests */ |
c78aa667 | 495 | static void |
60d096f4 | 496 | authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request) |
94439e4e | 497 | { |
f5691f9c | 498 | AuthNTLMUserRequest *ntlm_request; |
499 | assert(auth_user_request->user()->auth_type == AUTH_NTLM); | |
500 | ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request); | |
33272404 | 501 | debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver); |
6bf4f823 | 502 | /* is it possible for the server to be NULL? hno seems to think so. |
503 | * Let's see what happens, might segfault in helperStatefulReleaseServer | |
504 | * if it does. I leave it like this not to cover possibly problematic | |
505 | * code-paths. Kinkie */ | |
60d096f4 | 506 | helperStatefulReleaseServer(ntlm_request->authserver); |
507 | ntlm_request->authserver = NULL; | |
94439e4e | 508 | } |
509 | ||
510 | /* clear any connection related authentication details */ | |
f5691f9c | 511 | void |
6bf4f823 | 512 | AuthNTLMUserRequest::onConnectionClose(ConnStateData *connection) |
94439e4e | 513 | { |
6bf4f823 | 514 | assert(connection != NULL); |
62e76326 | 515 | |
6bf4f823 | 516 | debug(29,8)("AuthNTLMUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this); |
62e76326 | 517 | |
6bf4f823 | 518 | if (connection->auth_user_request == NULL) { |
519 | debug(29,8)("AuthNTLMUserRequest::onConnectionClose: no auth_user_request\n"); | |
520 | return; | |
521 | } | |
62e76326 | 522 | |
6bf4f823 | 523 | if (authserver != NULL) |
524 | authenticateNTLMReleaseServer(this); | |
62e76326 | 525 | |
6bf4f823 | 526 | /* unlock the connection based lock */ |
527 | debug(29, 9) ("AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection); | |
62e76326 | 528 | |
6bf4f823 | 529 | /* This still breaks the abstraction, but is at least read only now. |
530 | * If needed, this could be ignored, as the conn deletion will also unlock | |
531 | * the auth user request. | |
532 | */ | |
533 | unlock(); | |
94439e4e | 534 | |
6bf4f823 | 535 | connection->auth_user_request = NULL; |
60d096f4 | 536 | } |
94439e4e | 537 | |
538 | /* | |
6bf4f823 | 539 | * Decode a NTLM [Proxy-]Auth string, placing the results in the passed |
94439e4e | 540 | * Auth_user structure. |
541 | */ | |
f5691f9c | 542 | AuthUserRequest * |
543 | AuthNTLMConfig::decode(char const *proxy_auth) | |
94439e4e | 544 | { |
f5691f9c | 545 | NTLMUser *newUser = new NTLMUser(&ntlmConfig); |
546 | AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest (); | |
547 | assert(auth_user_request->user() == NULL); | |
548 | auth_user_request->user(newUser); | |
549 | auth_user_request->user()->auth_type = AUTH_NTLM; | |
550 | auth_user_request->user()->addRequest(auth_user_request); | |
94439e4e | 551 | |
552 | /* all we have to do is identify that it's NTLM - the helper does the rest */ | |
6bf4f823 | 553 | debug(29, 9) ("AuthNTLMConfig::decode: NTLM authentication\n"); |
f5691f9c | 554 | return auth_user_request; |
94439e4e | 555 | } |
556 | ||
82b045dc | 557 | int |
f5691f9c | 558 | AuthNTLMUserRequest::authenticated() const |
94439e4e | 559 | { |
6bf4f823 | 560 | if (auth_state == AUTHENTICATE_STATE_FINISHED) { |
561 | debug(29, 9) ("AuthNTLMUserRequest::authenticated: user authenticated.\n"); | |
62e76326 | 562 | return 1; |
6bf4f823 | 563 | } |
62e76326 | 564 | |
6bf4f823 | 565 | debug(29, 9) ("AuthNTLMUserRequest::authenticated: user not fully authenticated.\n"); |
62e76326 | 566 | |
94439e4e | 567 | return 0; |
568 | } | |
569 | ||
82b045dc | 570 | void |
f5691f9c | 571 | AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type) |
94439e4e | 572 | { |
6bf4f823 | 573 | const char *proxy_auth, *blob; |
62e76326 | 574 | |
6bf4f823 | 575 | //ProxyAuthCachePointer *proxy_auth_hash = NULL; |
e6ccf245 | 576 | auth_user_hash_pointer *usernamehash; |
6bf4f823 | 577 | |
f5691f9c | 578 | /* TODO: rename this!! */ |
6bf4f823 | 579 | auth_user_t *local_auth_user; |
94439e4e | 580 | ntlm_user_t *ntlm_user; |
94439e4e | 581 | |
6bf4f823 | 582 | local_auth_user = user(); |
583 | assert(local_auth_user); | |
584 | assert(local_auth_user->auth_type == AUTH_NTLM); | |
585 | ntlm_user = dynamic_cast<ntlm_user_t *>(local_auth_user); | |
586 | assert (this); | |
587 | ||
3a73fe76 | 588 | /* Check that we are in the client side, where we can generate |
589 | * auth challenges */ | |
62e76326 | 590 | |
a2ac85d9 | 591 | if (conn.getRaw() == NULL) { |
6bf4f823 | 592 | auth_state = AUTHENTICATE_STATE_FAILED; |
593 | debug(29, 1) ("AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!\n"); | |
594 | return; | |
595 | } | |
596 | ||
597 | if (waiting) { | |
598 | debug(29, 1) ("AuthNTLMUserRequest::authenticate: waiting for helper reply!\n"); | |
62e76326 | 599 | return; |
3a73fe76 | 600 | } |
62e76326 | 601 | |
6bf4f823 | 602 | if (server_blob) { |
603 | debug(29,2)("AuthNTLMUserRequest::authenticate: need to challenge client '%s'!\n", server_blob); | |
604 | return; | |
605 | } | |
606 | ||
607 | /* get header */ | |
a9925b40 | 608 | proxy_auth = request->header.getStr(type); |
6bf4f823 | 609 | |
c9c40182 | 610 | /* locate second word */ |
611 | blob = proxy_auth; | |
612 | ||
613 | while (xisspace(*blob) && *blob) | |
614 | blob++; | |
615 | ||
616 | while (!xisspace(*blob) && *blob) | |
617 | blob++; | |
6bf4f823 | 618 | |
c9c40182 | 619 | while (xisspace(*blob) && *blob) |
6bf4f823 | 620 | blob++; |
621 | ||
622 | switch (auth_state) { | |
62e76326 | 623 | |
94439e4e | 624 | case AUTHENTICATE_STATE_NONE: |
6bf4f823 | 625 | /* we've recieved a ntlm request. pass to a helper */ |
626 | debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '%s'\n", proxy_auth); | |
627 | auth_state = AUTHENTICATE_STATE_INITIAL; | |
628 | safe_free(client_blob); | |
629 | client_blob=xstrdup(blob); | |
62e76326 | 630 | conn->auth_type = AUTH_NTLM; |
f5691f9c | 631 | conn->auth_user_request = this; |
6bf4f823 | 632 | conn = conn; |
f5691f9c | 633 | |
6bf4f823 | 634 | lock() |
f5691f9c | 635 | |
6bf4f823 | 636 | ; |
62e76326 | 637 | return; |
f5691f9c | 638 | |
62e76326 | 639 | break; |
640 | ||
6bf4f823 | 641 | case AUTHENTICATE_STATE_INITIAL: |
642 | debug(29,1)("AuthNTLMUserRequest::authenticate: need to ask helper\n"); | |
f5691f9c | 643 | |
62e76326 | 644 | return; |
f5691f9c | 645 | |
62e76326 | 646 | break; |
647 | ||
62e76326 | 648 | |
6bf4f823 | 649 | case AUTHENTICATE_STATE_IN_PROGRESS: |
650 | /* we should have received a blob from the client. Hand it off to | |
651 | * some helper */ | |
652 | safe_free(client_blob); | |
62e76326 | 653 | |
6bf4f823 | 654 | client_blob = xstrdup (blob); |
62e76326 | 655 | |
656 | return; | |
6bf4f823 | 657 | |
62e76326 | 658 | break; |
659 | ||
6bf4f823 | 660 | case AUTHENTICATE_STATE_FINISHED: |
661 | /* connection is authenticated */ | |
662 | debug(29, 4) ("AuthNTLMUserRequest::authenticate: authenticated user %s\n", ntlm_user->username()); | |
663 | ||
62e76326 | 664 | /* see if this is an existing user with a different proxy_auth |
665 | * string */ | |
6bf4f823 | 666 | usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username())); |
667 | ||
668 | while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0)) | |
669 | usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next); | |
670 | ||
671 | if (usernamehash) { | |
672 | /* we can't seamlessly recheck the username due to the | |
673 | * challenge-response nature of the protocol. | |
674 | * Just free the temporary auth_user */ | |
675 | usernamehash->user()->absorb(local_auth_user); | |
676 | //authenticateAuthUserMerge(local_auth_user, usernamehash->user()); | |
677 | local_auth_user = usernamehash->user(); | |
678 | _auth_user = local_auth_user; | |
62e76326 | 679 | } else { |
680 | /* store user in hash's */ | |
6bf4f823 | 681 | local_auth_user->addToNameCache(); |
682 | // authenticateUserNameCacheAdd(local_auth_user); | |
62e76326 | 683 | } |
684 | ||
685 | /* set these to now because this is either a new login from an | |
686 | * existing user or a new user */ | |
6bf4f823 | 687 | local_auth_user->expiretime = current_time.tv_sec; |
688 | ||
689 | authenticateNTLMReleaseServer(this); | |
690 | ||
691 | auth_state = AUTHENTICATE_STATE_DONE; | |
692 | ||
62e76326 | 693 | return; |
6bf4f823 | 694 | |
62e76326 | 695 | break; |
696 | ||
94439e4e | 697 | case AUTHENTICATE_STATE_DONE: |
6bf4f823 | 698 | fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n"); |
699 | ||
62e76326 | 700 | break; |
701 | ||
3c641669 | 702 | case AUTHENTICATE_STATE_FAILED: |
62e76326 | 703 | /* we've failed somewhere in authentication */ |
6bf4f823 | 704 | debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm failed. %s\n", proxy_auth); |
705 | ||
62e76326 | 706 | return; |
6bf4f823 | 707 | |
708 | break; | |
94439e4e | 709 | } |
62e76326 | 710 | |
94439e4e | 711 | return; |
712 | } | |
82b045dc | 713 | |
6bf4f823 | 714 | AuthNTLMUserRequest::AuthNTLMUserRequest() : |
715 | conn(NULL), auth_state(AUTHENTICATE_STATE_NONE), | |
716 | _theUser(NULL) | |
717 | { | |
718 | waiting=0; | |
719 | client_blob=0; | |
720 | server_blob=0; | |
721 | authserver=NULL; | |
722 | } | |
f5691f9c | 723 | |
724 | AuthNTLMUserRequest::~AuthNTLMUserRequest() | |
725 | { | |
6bf4f823 | 726 | safe_free(server_blob); |
727 | safe_free(client_blob); | |
f5691f9c | 728 | |
6bf4f823 | 729 | if (authserver != NULL) { |
730 | debug(29, 9) ("AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '%p'\n", authserver); | |
f5691f9c | 731 | helperStatefulReleaseServer(authserver); |
732 | authserver = NULL; | |
733 | } | |
734 | } | |
735 | ||
f5691f9c | 736 | void |
737 | NTLMUser::deleteSelf() const | |
738 | { | |
739 | delete this; | |
740 | } | |
741 | ||
f5691f9c | 742 | NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config) |
743 | { | |
744 | proxy_auth_list.head = proxy_auth_list.tail = NULL; | |
745 | } | |
746 | ||
747 | AuthConfig * | |
748 | ntlmScheme::createConfig() | |
749 | { | |
750 | return &ntlmConfig; | |
751 | } | |
752 | ||
6bf4f823 | 753 | const char * |
754 | AuthNTLMUserRequest::connLastHeader() | |
755 | { | |
756 | return NULL; | |
757 | } | |
758 |