]>
Commit | Line | Data |
---|---|---|
f7f3304a | 1 | #include "squid.h" |
928f3421 | 2 | #include "auth/ntlm/auth_ntlm.h" |
616cfc4c | 3 | #include "auth/ntlm/UserRequest.h" |
d232141d | 4 | #include "auth/State.h" |
928f3421 AJ |
5 | #include "cbdata.h" |
6 | #include "HttpRequest.h" | |
7 | #include "SquidTime.h" | |
8 | ||
c7baff40 | 9 | Auth::Ntlm::UserRequest::UserRequest() |
928f3421 AJ |
10 | { |
11 | waiting=0; | |
12 | client_blob=0; | |
13 | server_blob=0; | |
14 | authserver=NULL; | |
7afc3bf2 | 15 | request=NULL; |
928f3421 AJ |
16 | } |
17 | ||
c7baff40 | 18 | Auth::Ntlm::UserRequest::~UserRequest() |
928f3421 | 19 | { |
ea0695f2 | 20 | assert(RefCountCount()==0); |
928f3421 AJ |
21 | safe_free(server_blob); |
22 | safe_free(client_blob); | |
23 | ||
24 | releaseAuthServer(); | |
25 | ||
26 | if (request) { | |
27 | HTTPMSGUNLOCK(request); | |
28 | request = NULL; | |
29 | } | |
30 | } | |
31 | ||
32 | const char * | |
c7baff40 | 33 | Auth::Ntlm::UserRequest::connLastHeader() |
928f3421 AJ |
34 | { |
35 | return NULL; | |
36 | } | |
37 | ||
7afc3bf2 | 38 | int |
c7baff40 | 39 | Auth::Ntlm::UserRequest::authenticated() const |
7afc3bf2 AJ |
40 | { |
41 | if (user() != NULL && user()->credentials() == Auth::Ok) { | |
42 | debugs(29, 9, HERE << "user authenticated."); | |
43 | return 1; | |
44 | } | |
45 | ||
46 | debugs(29, 9, HERE << "user not fully authenticated."); | |
47 | return 0; | |
48 | } | |
49 | ||
51a3dd58 | 50 | Auth::Direction |
c7baff40 | 51 | Auth::Ntlm::UserRequest::module_direction() |
928f3421 | 52 | { |
c7baff40 | 53 | /* null auth_user is checked for by Auth::UserRequest::direction() */ |
928f3421 AJ |
54 | |
55 | if (waiting || client_blob) | |
51a3dd58 | 56 | return Auth::CRED_LOOKUP; /* need helper response to continue */ |
928f3421 | 57 | |
616cfc4c | 58 | if (user()->auth_type != Auth::AUTH_NTLM) |
51a3dd58 | 59 | return Auth::CRED_ERROR; |
928f3421 | 60 | |
d232141d | 61 | switch (user()->credentials()) { |
928f3421 | 62 | |
d87154ee | 63 | case Auth::Handshake: |
928f3421 | 64 | assert(server_blob); |
7afc3bf2 | 65 | return Auth::CRED_CHALLENGE; |
928f3421 | 66 | |
d87154ee | 67 | case Auth::Ok: |
51a3dd58 | 68 | return Auth::CRED_VALID; |
928f3421 | 69 | |
d87154ee | 70 | case Auth::Failed: |
7afc3bf2 | 71 | return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE? |
928f3421 | 72 | |
d232141d AJ |
73 | default: |
74 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials()); | |
51a3dd58 | 75 | return Auth::CRED_ERROR; |
d232141d | 76 | } |
928f3421 AJ |
77 | } |
78 | ||
928f3421 | 79 | void |
4c535e87 | 80 | Auth::Ntlm::UserRequest::module_start(AUTHCB * handler, void *data) |
928f3421 | 81 | { |
7afc3bf2 | 82 | static char buf[MAX_AUTHTOKEN_LEN]; |
928f3421 AJ |
83 | |
84 | assert(data); | |
85 | assert(handler); | |
928f3421 | 86 | |
372fccd6 | 87 | if (static_cast<Auth::Ntlm::Config*>(Auth::Config::Find("ntlm"))->authenticateProgram == NULL) { |
d232141d | 88 | debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured."); |
4c535e87 | 89 | handler(data); |
ec5858ff | 90 | return; |
928f3421 AJ |
91 | } |
92 | ||
7afc3bf2 AJ |
93 | debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'"); |
94 | ||
d87154ee | 95 | if (user()->credentials() == Auth::Pending) { |
7afc3bf2 | 96 | snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? |
928f3421 | 97 | } else { |
7afc3bf2 | 98 | snprintf(buf, sizeof(buf), "KK %s\n", client_blob); |
928f3421 AJ |
99 | } |
100 | ||
101 | waiting = 1; | |
102 | ||
103 | safe_free(client_blob); | |
c7baff40 | 104 | helperStatefulSubmit(ntlmauthenticators, buf, Auth::Ntlm::UserRequest::HandleReply, |
1c756645 | 105 | new Auth::StateData(this, handler, data), authserver); |
928f3421 AJ |
106 | } |
107 | ||
108 | /** | |
109 | * Atomic action: properly release the NTLM auth helpers which may have been reserved | |
110 | * for this request connections use. | |
111 | */ | |
112 | void | |
c7baff40 | 113 | Auth::Ntlm::UserRequest::releaseAuthServer() |
928f3421 AJ |
114 | { |
115 | if (authserver) { | |
116 | debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'"); | |
117 | helperStatefulReleaseServer(authserver); | |
118 | authserver = NULL; | |
119 | } else | |
120 | debugs(29, 6, HERE << "No NTLM auth server to release."); | |
121 | } | |
122 | ||
123 | void | |
c7baff40 | 124 | Auth::Ntlm::UserRequest::onConnectionClose(ConnStateData *conn) |
928f3421 AJ |
125 | { |
126 | assert(conn != NULL); | |
127 | ||
c7baff40 | 128 | debugs(29, 8, HERE << "closing connection '" << conn << "' (this is '" << this << "')"); |
928f3421 AJ |
129 | |
130 | if (conn->auth_user_request == NULL) { | |
c7baff40 | 131 | debugs(29, 8, HERE << "no auth_user_request"); |
928f3421 AJ |
132 | return; |
133 | } | |
134 | ||
928f3421 AJ |
135 | releaseAuthServer(); |
136 | ||
137 | /* unlock the connection based lock */ | |
7afc3bf2 | 138 | debugs(29, 9, HERE << "Unlocking auth_user from the connection '" << conn << "'."); |
928f3421 AJ |
139 | |
140 | conn->auth_user_request = NULL; | |
141 | } | |
142 | ||
928f3421 | 143 | void |
c7baff40 | 144 | Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type) |
928f3421 | 145 | { |
56a49fda | 146 | assert(this); |
928f3421 AJ |
147 | |
148 | /* Check that we are in the client side, where we can generate | |
149 | * auth challenges */ | |
150 | ||
151 | if (conn == NULL || !cbdataReferenceValid(conn)) { | |
d87154ee | 152 | user()->credentials(Auth::Failed); |
7afc3bf2 | 153 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication attempt to perform authentication without a connection!"); |
928f3421 AJ |
154 | return; |
155 | } | |
156 | ||
157 | if (waiting) { | |
7afc3bf2 | 158 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication waiting for helper reply!"); |
928f3421 AJ |
159 | return; |
160 | } | |
161 | ||
162 | if (server_blob) { | |
7afc3bf2 | 163 | debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!"); |
928f3421 AJ |
164 | return; |
165 | } | |
166 | ||
167 | /* get header */ | |
7afc3bf2 | 168 | const char *proxy_auth = aRequest->header.getStr(type); |
928f3421 AJ |
169 | |
170 | /* locate second word */ | |
7afc3bf2 | 171 | const char *blob = proxy_auth; |
928f3421 AJ |
172 | |
173 | /* if proxy_auth is actually NULL, we'd better not manipulate it. */ | |
174 | if (blob) { | |
175 | while (xisspace(*blob) && *blob) | |
742a021b | 176 | ++blob; |
928f3421 AJ |
177 | |
178 | while (!xisspace(*blob) && *blob) | |
742a021b | 179 | ++blob; |
928f3421 AJ |
180 | |
181 | while (xisspace(*blob) && *blob) | |
742a021b | 182 | ++blob; |
928f3421 AJ |
183 | } |
184 | ||
d232141d | 185 | switch (user()->credentials()) { |
928f3421 | 186 | |
d87154ee | 187 | case Auth::Unchecked: |
928f3421 | 188 | /* we've received a ntlm request. pass to a helper */ |
7afc3bf2 | 189 | debugs(29, 9, HERE << "auth state ntlm none. Received blob: '" << proxy_auth << "'"); |
d87154ee | 190 | user()->credentials(Auth::Pending); |
928f3421 AJ |
191 | safe_free(client_blob); |
192 | client_blob=xstrdup(blob); | |
928f3421 AJ |
193 | assert(conn->auth_user_request == NULL); |
194 | conn->auth_user_request = this; | |
195 | request = aRequest; | |
196 | HTTPMSGLOCK(request); | |
197 | break; | |
198 | ||
d87154ee | 199 | case Auth::Pending: |
e0236918 | 200 | debugs(29, DBG_IMPORTANT, HERE << "need to ask helper"); |
928f3421 AJ |
201 | break; |
202 | ||
d87154ee | 203 | case Auth::Handshake: |
928f3421 AJ |
204 | /* we should have received a blob from the client. Hand it off to |
205 | * some helper */ | |
206 | safe_free(client_blob); | |
7afc3bf2 | 207 | client_blob = xstrdup(blob); |
928f3421 AJ |
208 | if (request) |
209 | HTTPMSGUNLOCK(request); | |
210 | request = aRequest; | |
211 | HTTPMSGLOCK(request); | |
212 | break; | |
213 | ||
d87154ee | 214 | case Auth::Ok: |
c7baff40 | 215 | fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n"); |
928f3421 AJ |
216 | break; |
217 | ||
d87154ee | 218 | case Auth::Failed: |
928f3421 | 219 | /* we've failed somewhere in authentication */ |
7afc3bf2 | 220 | debugs(29, 9, HERE << "auth state ntlm failed. " << proxy_auth); |
928f3421 AJ |
221 | break; |
222 | } | |
223 | } | |
224 | ||
225 | void | |
c7baff40 | 226 | Auth::Ntlm::UserRequest::HandleReply(void *data, void *lastserver, char *reply) |
928f3421 | 227 | { |
1c756645 | 228 | Auth::StateData *r = static_cast<Auth::StateData *>(data); |
928f3421 AJ |
229 | char *blob; |
230 | ||
7afc3bf2 | 231 | debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'"); |
928f3421 | 232 | |
1c756645 | 233 | if (!cbdataReferenceValid(r->data)) { |
7afc3bf2 | 234 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication invalid callback data. helper '" << lastserver << "'."); |
1c756645 | 235 | delete r; |
928f3421 AJ |
236 | return; |
237 | } | |
238 | ||
239 | if (!reply) { | |
7afc3bf2 | 240 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << lastserver << "' crashed!."); |
928f3421 AJ |
241 | reply = (char *)"BH Internal error"; |
242 | } | |
243 | ||
c7baff40 | 244 | Auth::UserRequest::Pointer auth_user_request = r->auth_user_request; |
928f3421 AJ |
245 | assert(auth_user_request != NULL); |
246 | ||
c7baff40 | 247 | Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw()); |
7afc3bf2 AJ |
248 | assert(lm_request != NULL); |
249 | assert(lm_request->waiting); | |
56a49fda | 250 | |
7afc3bf2 AJ |
251 | lm_request->waiting = 0; |
252 | safe_free(lm_request->client_blob); | |
928f3421 | 253 | |
7afc3bf2 AJ |
254 | assert(auth_user_request->user() != NULL); |
255 | assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM); | |
256 | ||
257 | if (lm_request->authserver == NULL) | |
258 | lm_request->authserver = static_cast<helper_stateful_server*>(lastserver); | |
928f3421 | 259 | else |
7afc3bf2 | 260 | assert(lm_request->authserver == lastserver); |
928f3421 AJ |
261 | |
262 | /* seperate out the useful data */ | |
263 | blob = strchr(reply, ' '); | |
928f3421 | 264 | if (blob) |
742a021b | 265 | ++blob; |
928f3421 AJ |
266 | |
267 | if (strncasecmp(reply, "TT ", 3) == 0) { | |
268 | /* we have been given a blob to send to the client */ | |
7afc3bf2 AJ |
269 | safe_free(lm_request->server_blob); |
270 | lm_request->request->flags.must_keepalive = 1; | |
271 | if (lm_request->request->flags.proxy_keepalive) { | |
272 | lm_request->server_blob = xstrdup(blob); | |
273 | auth_user_request->user()->credentials(Auth::Handshake); | |
928f3421 | 274 | auth_user_request->denyMessage("Authentication in progress"); |
7afc3bf2 | 275 | debugs(29, 4, HERE << "Need to challenge the client with a server blob '" << blob << "'"); |
928f3421 | 276 | } else { |
7afc3bf2 | 277 | auth_user_request->user()->credentials(Auth::Failed); |
928f3421 AJ |
278 | auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); |
279 | } | |
280 | } else if (strncasecmp(reply, "AF ", 3) == 0) { | |
281 | /* we're finished, release the helper */ | |
56a49fda | 282 | auth_user_request->user()->username(blob); |
928f3421 | 283 | auth_user_request->denyMessage("Login successful"); |
7afc3bf2 AJ |
284 | safe_free(lm_request->server_blob); |
285 | lm_request->releaseAuthServer(); | |
928f3421 | 286 | |
7afc3bf2 | 287 | debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << blob << "'"); |
928f3421 | 288 | /* connection is authenticated */ |
7afc3bf2 | 289 | debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username()); |
928f3421 AJ |
290 | /* see if this is an existing user with a different proxy_auth |
291 | * string */ | |
7afc3bf2 AJ |
292 | AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username())); |
293 | Auth::User::Pointer local_auth_user = lm_request->user(); | |
616cfc4c | 294 | while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM || |
c6cf8dee | 295 | strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0)) |
928f3421 AJ |
296 | usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next); |
297 | if (usernamehash) { | |
298 | /* we can't seamlessly recheck the username due to the | |
299 | * challenge-response nature of the protocol. | |
58e94342 AJ |
300 | * Just free the temporary auth_user after merging as |
301 | * much of it new state into the existing one as possible */ | |
928f3421 | 302 | usernamehash->user()->absorb(local_auth_user); |
7afc3bf2 | 303 | /* from here on we are working with the original cached credentials. */ |
928f3421 | 304 | local_auth_user = usernamehash->user(); |
58e94342 | 305 | auth_user_request->user(local_auth_user); |
928f3421 AJ |
306 | } else { |
307 | /* store user in hash's */ | |
308 | local_auth_user->addToNameCache(); | |
928f3421 AJ |
309 | } |
310 | /* set these to now because this is either a new login from an | |
311 | * existing user or a new user */ | |
312 | local_auth_user->expiretime = current_time.tv_sec; | |
7afc3bf2 AJ |
313 | auth_user_request->user()->credentials(Auth::Ok); |
314 | debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << blob << "'"); | |
315 | ||
928f3421 AJ |
316 | } else if (strncasecmp(reply, "NA ", 3) == 0) { |
317 | /* authentication failure (wrong password, etc.) */ | |
318 | auth_user_request->denyMessage(blob); | |
7afc3bf2 AJ |
319 | auth_user_request->user()->credentials(Auth::Failed); |
320 | safe_free(lm_request->server_blob); | |
321 | lm_request->releaseAuthServer(); | |
322 | debugs(29, 4, HERE << "Failed validating user via NTLM. Error returned '" << blob << "'"); | |
928f3421 AJ |
323 | } else if (strncasecmp(reply, "BH ", 3) == 0) { |
324 | /* TODO kick off a refresh process. This can occur after a YR or after | |
325 | * a KK. If after a YR release the helper and resubmit the request via | |
326 | * Authenticate NTLM start. | |
327 | * If after a KK deny the user's request w/ 407 and mark the helper as | |
328 | * Needing YR. */ | |
329 | auth_user_request->denyMessage(blob); | |
d87154ee | 330 | auth_user_request->user()->credentials(Auth::Failed); |
7afc3bf2 AJ |
331 | safe_free(lm_request->server_blob); |
332 | lm_request->releaseAuthServer(); | |
333 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Error returned '" << reply << "'"); | |
928f3421 AJ |
334 | } else { |
335 | /* protocol error */ | |
336 | fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply); | |
337 | } | |
338 | ||
7afc3bf2 AJ |
339 | if (lm_request->request) { |
340 | HTTPMSGUNLOCK(lm_request->request); | |
341 | lm_request->request = NULL; | |
928f3421 | 342 | } |
4c535e87 | 343 | r->handler(r->data); |
1c756645 | 344 | delete r; |
928f3421 | 345 | } |