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