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