]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/negotiate/UserRequest.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / auth / negotiate / UserRequest.cc
CommitLineData
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 11Auth::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 20Auth::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
34const char *
c7baff40 35Auth::Negotiate::UserRequest::connLastHeader()
928f3421
AJ
36{
37 return NULL;
38}
39
40int
c7baff40 41Auth::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 52Auth::Direction
c7baff40 53Auth::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 81void
c7baff40 82Auth::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 */
118void
c7baff40 119Auth::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 */
130void
c7baff40 131Auth::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
150void
c7baff40 151Auth::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
231void
c7baff40 232Auth::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 367void
c7baff40 368Auth::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}