]> git.ipfire.org Git - thirdparty/squid.git/blob - 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
1 #include "squid.h"
2 #include "auth/negotiate/auth_negotiate.h"
3 #include "auth/negotiate/UserRequest.h"
4 #include "auth/State.h"
5 #include "auth/User.h"
6 #include "helper.h"
7 #include "HttpReply.h"
8 #include "HttpRequest.h"
9 #include "SquidTime.h"
10
11 Auth::Negotiate::UserRequest::UserRequest()
12 {
13 waiting=0;
14 client_blob=0;
15 server_blob=0;
16 authserver=NULL;
17 request=NULL;
18 }
19
20 Auth::Negotiate::UserRequest::~UserRequest()
21 {
22 assert(RefCountCount()==0);
23 safe_free(server_blob);
24 safe_free(client_blob);
25
26 releaseAuthServer();
27
28 if (request) {
29 HTTPMSGUNLOCK(request);
30 request = NULL;
31 }
32 }
33
34 const char *
35 Auth::Negotiate::UserRequest::connLastHeader()
36 {
37 return NULL;
38 }
39
40 int
41 Auth::Negotiate::UserRequest::authenticated() const
42 {
43 if (user() != NULL && user()->credentials() == Auth::Ok) {
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
52 Auth::Direction
53 Auth::Negotiate::UserRequest::module_direction()
54 {
55 /* null auth_user is checked for by Auth::UserRequest::direction() */
56
57 if (waiting || client_blob)
58 return Auth::CRED_LOOKUP; /* need helper response to continue */
59
60 if (user()->auth_type != Auth::AUTH_NEGOTIATE)
61 return Auth::CRED_ERROR;
62
63 switch (user()->credentials()) {
64
65 case Auth::Handshake:
66 assert(server_blob);
67 return Auth::CRED_CHALLENGE;
68
69 case Auth::Ok:
70 return Auth::CRED_VALID;
71
72 case Auth::Failed:
73 return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE?
74
75 default:
76 debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication in unexpected state: " << user()->credentials());
77 return Auth::CRED_ERROR;
78 }
79 }
80
81 void
82 Auth::Negotiate::UserRequest::module_start(RH * handler, void *data)
83 {
84 static char buf[MAX_AUTHTOKEN_LEN];
85
86 assert(data);
87 assert(handler);
88
89 assert(user() != NULL);
90 assert(user()->auth_type == Auth::AUTH_NEGOTIATE);
91
92 if (static_cast<Auth::Negotiate::Config*>(Auth::Config::Find("negotiate"))->authenticateProgram == NULL) {
93 debugs(29, DBG_CRITICAL, "ERROR: No Negotiate authentication program configured.");
94 handler(data, NULL);
95 return;
96 }
97
98 debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
99
100 if (user()->credentials() == Auth::Pending) {
101 snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
102 } else {
103 snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
104 }
105
106 waiting = 1;
107
108 safe_free(client_blob);
109
110 helperStatefulSubmit(negotiateauthenticators, buf, Auth::Negotiate::UserRequest::HandleReply,
111 new Auth::StateData(this, handler, data), authserver);
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
119 Auth::Negotiate::UserRequest::releaseAuthServer()
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
131 Auth::Negotiate::UserRequest::onConnectionClose(ConnStateData *conn)
132 {
133 assert(conn != NULL);
134
135 debugs(29, 8, HERE << "closing connection '" << conn << "' (this is '" << this << "')");
136
137 if (conn->auth_user_request == NULL) {
138 debugs(29, 8, HERE << "no auth_user_request");
139 return;
140 }
141
142 releaseAuthServer();
143
144 /* unlock the connection based lock */
145 debugs(29, 9, HERE << "Unlocking auth_user from the connection '" << conn << "'.");
146
147 conn->auth_user_request = NULL;
148 }
149
150 void
151 Auth::Negotiate::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
152 {
153 assert(this);
154
155 /* Check that we are in the client side, where we can generate
156 * auth challenges */
157
158 if (conn == NULL || !cbdataReferenceValid(conn)) {
159 user()->credentials(Auth::Failed);
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 */
175 const char *proxy_auth = aRequest->header.getStr(type);
176
177 /* locate second word */
178 const char *blob = proxy_auth;
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
191 switch (user()->credentials()) {
192
193 case Auth::Unchecked:
194 /* we've received a negotiate request. pass to a helper */
195 debugs(29, 9, HERE << "auth state negotiate none. Received blob: '" << proxy_auth << "'");
196 user()->credentials(Auth::Pending);
197 safe_free(client_blob);
198 client_blob=xstrdup(blob);
199 assert(conn->auth_user_request == NULL);
200 conn->auth_user_request = this;
201 request = aRequest;
202 HTTPMSGLOCK(request);
203 break;
204
205 case Auth::Pending:
206 debugs(29, 1, HERE << "need to ask helper");
207 break;
208
209 case Auth::Handshake:
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
220 case Auth::Ok:
221 fatal("Auth::Negotiate::UserRequest::authenticate: unexpected auth state DONE! Report a bug to the squid developers.\n");
222 break;
223
224 case Auth::Failed:
225 /* we've failed somewhere in authentication */
226 debugs(29, 9, HERE << "auth state negotiate failed. " << proxy_auth);
227 break;
228 }
229 }
230
231 void
232 Auth::Negotiate::UserRequest::HandleReply(void *data, void *lastserver, char *reply)
233 {
234 Auth::StateData *r = static_cast<Auth::StateData *>(data);
235
236 char *blob, *arg = NULL;
237
238 debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
239
240 if (!cbdataReferenceValid(r->data)) {
241 debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication invalid callback data. helper '" << lastserver << "'.");
242 delete r;
243 return;
244 }
245
246 if (!reply) {
247 debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication Helper '" << lastserver << "' crashed!.");
248 reply = (char *)"BH Internal error";
249 }
250
251 Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
252 assert(auth_user_request != NULL);
253
254 Auth::Negotiate::UserRequest *lm_request = dynamic_cast<Auth::Negotiate::UserRequest *>(auth_user_request.getRaw());
255 assert(lm_request != NULL);
256 assert(lm_request->waiting);
257
258 lm_request->waiting = 0;
259 safe_free(lm_request->client_blob);
260
261 assert(auth_user_request->user() != NULL);
262 assert(auth_user_request->user()->auth_type == Auth::AUTH_NEGOTIATE);
263
264 if (lm_request->authserver == NULL)
265 lm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
266 else
267 assert(lm_request->authserver == lastserver);
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';
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);
287 auth_user_request->user()->credentials(Auth::Handshake);
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 {
291 auth_user_request->user()->credentials(Auth::Failed);
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
300 auth_user_request->user()->username(arg);
301 auth_user_request->denyMessage("Login successful");
302 safe_free(lm_request->server_blob);
303 lm_request->server_blob = xstrdup(blob);
304 lm_request->releaseAuthServer();
305
306 /* connection is authenticated */
307 debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
308 /* see if this is an existing user with a different proxy_auth
309 * string */
310 AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
311 Auth::User::Pointer local_auth_user = lm_request->user();
312 while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NEGOTIATE ||
313 strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
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.
318 * Just free the temporary auth_user after merging as
319 * much of it new state into the existing one as possible */
320 usernamehash->user()->absorb(local_auth_user);
321 /* from here on we are working with the original cached credentials. */
322 local_auth_user = usernamehash->user();
323 auth_user_request->user(local_auth_user);
324 } else {
325 /* store user in hash's */
326 local_auth_user->addToNameCache();
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;
331 auth_user_request->user()->credentials(Auth::Ok);
332 debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << blob << "'");
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);
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();
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);
353 auth_user_request->user()->credentials(Auth::Failed);
354 safe_free(lm_request->server_blob);
355 lm_request->releaseAuthServer();
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
362 lm_request->request = NULL;
363 r->handler(r->data, NULL);
364 delete r;
365 }
366
367 void
368 Auth::Negotiate::UserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
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 }