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