]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/ntlm/UserRequest.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / ntlm / UserRequest.cc
CommitLineData
bbc27441 1/*
f6e9a3ee 2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
f7f3304a 9#include "squid.h"
d4806c91 10#include "AccessLogEntry.h"
fde785ee 11#include "auth/CredentialsCache.h"
12daeef6 12#include "auth/ntlm/Config.h"
33e622d7 13#include "auth/ntlm/User.h"
616cfc4c 14#include "auth/ntlm/UserRequest.h"
d232141d 15#include "auth/State.h"
928f3421 16#include "cbdata.h"
582c2af2 17#include "client_side.h"
ed6e9fb9 18#include "fatal.h"
86c63190 19#include "format/Format.h"
582c2af2 20#include "globals.h"
24438ec5
AJ
21#include "helper.h"
22#include "helper/Reply.h"
d3dddfb5 23#include "http/Stream.h"
928f3421 24#include "HttpRequest.h"
d4806c91 25#include "MemBuf.h"
928f3421
AJ
26#include "SquidTime.h"
27
d59e4742
FC
28Auth::Ntlm::UserRequest::UserRequest() :
29 authserver(nullptr),
30 server_blob(nullptr),
31 client_blob(nullptr),
32 waiting(0),
33 request(nullptr)
34{}
928f3421 35
c7baff40 36Auth::Ntlm::UserRequest::~UserRequest()
928f3421 37{
8bf217bd 38 assert(LockCount()==0);
928f3421
AJ
39 safe_free(server_blob);
40 safe_free(client_blob);
41
42 releaseAuthServer();
43
44 if (request) {
45 HTTPMSGUNLOCK(request);
46 request = NULL;
47 }
48}
49
50const char *
c7baff40 51Auth::Ntlm::UserRequest::connLastHeader()
928f3421
AJ
52{
53 return NULL;
54}
55
7afc3bf2 56int
c7baff40 57Auth::Ntlm::UserRequest::authenticated() const
7afc3bf2
AJ
58{
59 if (user() != NULL && user()->credentials() == Auth::Ok) {
60 debugs(29, 9, HERE << "user authenticated.");
61 return 1;
62 }
63
64 debugs(29, 9, HERE << "user not fully authenticated.");
65 return 0;
66}
67
d4806c91
CT
68const char *
69Auth::Ntlm::UserRequest::credentialsStr()
70{
71 static char buf[MAX_AUTHTOKEN_LEN];
d0873e0c 72 int printResult;
d4806c91 73 if (user()->credentials() == Auth::Pending) {
d0873e0c 74 printResult = snprintf(buf, sizeof(buf), "YR %s\n", client_blob);
d4806c91 75 } else {
d0873e0c 76 printResult = snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
d4806c91 77 }
d0873e0c
CT
78
79 // truncation is OK because we are used only for logging
80 if (printResult < 0) {
81 debugs(29, 2, "Can not build ntlm authentication credentials.");
82 buf[0] = '\0';
83 } else if (printResult >= (int)sizeof(buf))
84 debugs(29, 2, "Ntlm authentication credentials truncated.");
85
d4806c91
CT
86 return buf;
87}
88
51a3dd58 89Auth::Direction
c7baff40 90Auth::Ntlm::UserRequest::module_direction()
928f3421 91{
c7baff40 92 /* null auth_user is checked for by Auth::UserRequest::direction() */
928f3421
AJ
93
94 if (waiting || client_blob)
51a3dd58 95 return Auth::CRED_LOOKUP; /* need helper response to continue */
928f3421 96
616cfc4c 97 if (user()->auth_type != Auth::AUTH_NTLM)
51a3dd58 98 return Auth::CRED_ERROR;
928f3421 99
d232141d 100 switch (user()->credentials()) {
928f3421 101
d87154ee 102 case Auth::Handshake:
928f3421 103 assert(server_blob);
7afc3bf2 104 return Auth::CRED_CHALLENGE;
928f3421 105
d87154ee 106 case Auth::Ok:
51a3dd58 107 return Auth::CRED_VALID;
928f3421 108
d87154ee 109 case Auth::Failed:
7afc3bf2 110 return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE?
928f3421 111
d232141d
AJ
112 default:
113 debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
51a3dd58 114 return Auth::CRED_ERROR;
d232141d 115 }
928f3421
AJ
116}
117
928f3421 118void
ced8def3 119Auth::Ntlm::UserRequest::startHelperLookup(HttpRequest *, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
928f3421 120{
7afc3bf2 121 static char buf[MAX_AUTHTOKEN_LEN];
928f3421
AJ
122
123 assert(data);
124 assert(handler);
928f3421 125
dc79fed8 126 if (static_cast<Auth::Ntlm::Config*>(Auth::SchemeConfig::Find("ntlm"))->authenticateProgram == NULL) {
d232141d 127 debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured.");
4c535e87 128 handler(data);
ec5858ff 129 return;
928f3421
AJ
130 }
131
7afc3bf2
AJ
132 debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
133
d4806c91 134 const char *keyExtras = helperRequestKeyExtras(request, al);
d0873e0c 135 int printResult = 0;
d87154ee 136 if (user()->credentials() == Auth::Pending) {
d4806c91 137 if (keyExtras)
d0873e0c 138 printResult = snprintf(buf, sizeof(buf), "YR %s %s\n", client_blob, keyExtras);
d4806c91 139 else
d0873e0c 140 printResult = snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
928f3421 141 } else {
d4806c91 142 if (keyExtras)
d0873e0c 143 printResult = snprintf(buf, sizeof(buf), "KK %s %s\n", client_blob, keyExtras);
d4806c91 144 else
d0873e0c 145 printResult = snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
928f3421 146 }
928f3421
AJ
147 waiting = 1;
148
d0873e0c
CT
149 if (printResult < 0 || printResult >= (int)sizeof(buf)) {
150 if (printResult < 0)
151 debugs(29, DBG_CRITICAL, "ERROR: Can not build ntlm authentication helper request");
152 else
153 debugs(29, DBG_CRITICAL, "ERROR: Ntlm authentication helper request too big for the " << sizeof(buf) << "-byte buffer.");
154 handler(data);
155 return;
156 }
157
928f3421 158 safe_free(client_blob);
c7baff40 159 helperStatefulSubmit(ntlmauthenticators, buf, Auth::Ntlm::UserRequest::HandleReply,
1c756645 160 new Auth::StateData(this, handler, data), authserver);
928f3421
AJ
161}
162
163/**
164 * Atomic action: properly release the NTLM auth helpers which may have been reserved
165 * for this request connections use.
166 */
167void
c7baff40 168Auth::Ntlm::UserRequest::releaseAuthServer()
928f3421
AJ
169{
170 if (authserver) {
171 debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
172 helperStatefulReleaseServer(authserver);
173 authserver = NULL;
174 } else
175 debugs(29, 6, HERE << "No NTLM auth server to release.");
176}
177
928f3421 178void
789217a2 179Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, Http::HdrType type)
928f3421 180{
928f3421
AJ
181 /* Check that we are in the client side, where we can generate
182 * auth challenges */
183
184 if (conn == NULL || !cbdataReferenceValid(conn)) {
d87154ee 185 user()->credentials(Auth::Failed);
7afc3bf2 186 debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication attempt to perform authentication without a connection!");
928f3421
AJ
187 return;
188 }
189
190 if (waiting) {
7afc3bf2 191 debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication waiting for helper reply!");
928f3421
AJ
192 return;
193 }
194
195 if (server_blob) {
7afc3bf2 196 debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!");
928f3421
AJ
197 return;
198 }
199
200 /* get header */
7afc3bf2 201 const char *proxy_auth = aRequest->header.getStr(type);
928f3421
AJ
202
203 /* locate second word */
7afc3bf2 204 const char *blob = proxy_auth;
928f3421
AJ
205
206 /* if proxy_auth is actually NULL, we'd better not manipulate it. */
207 if (blob) {
208 while (xisspace(*blob) && *blob)
742a021b 209 ++blob;
928f3421
AJ
210
211 while (!xisspace(*blob) && *blob)
742a021b 212 ++blob;
928f3421
AJ
213
214 while (xisspace(*blob) && *blob)
742a021b 215 ++blob;
928f3421
AJ
216 }
217
d232141d 218 switch (user()->credentials()) {
928f3421 219
d87154ee 220 case Auth::Unchecked:
928f3421 221 /* we've received a ntlm request. pass to a helper */
7afc3bf2 222 debugs(29, 9, HERE << "auth state ntlm none. Received blob: '" << proxy_auth << "'");
d87154ee 223 user()->credentials(Auth::Pending);
928f3421
AJ
224 safe_free(client_blob);
225 client_blob=xstrdup(blob);
cc1e110a
AJ
226 assert(conn->getAuth() == NULL);
227 conn->setAuth(this, "new NTLM handshake request");
928f3421
AJ
228 request = aRequest;
229 HTTPMSGLOCK(request);
230 break;
231
d87154ee 232 case Auth::Pending:
e0236918 233 debugs(29, DBG_IMPORTANT, HERE << "need to ask helper");
928f3421
AJ
234 break;
235
d87154ee 236 case Auth::Handshake:
928f3421
AJ
237 /* we should have received a blob from the client. Hand it off to
238 * some helper */
239 safe_free(client_blob);
7afc3bf2 240 client_blob = xstrdup(blob);
928f3421
AJ
241 if (request)
242 HTTPMSGUNLOCK(request);
243 request = aRequest;
244 HTTPMSGLOCK(request);
245 break;
246
d87154ee 247 case Auth::Ok:
c7baff40 248 fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
928f3421
AJ
249 break;
250
d87154ee 251 case Auth::Failed:
928f3421 252 /* we've failed somewhere in authentication */
7afc3bf2 253 debugs(29, 9, HERE << "auth state ntlm failed. " << proxy_auth);
928f3421
AJ
254 break;
255 }
256}
257
258void
24438ec5 259Auth::Ntlm::UserRequest::HandleReply(void *data, const Helper::Reply &reply)
928f3421 260{
1c756645 261 Auth::StateData *r = static_cast<Auth::StateData *>(data);
928f3421 262
0272dd08 263 debugs(29, 8, HERE << "helper: '" << reply.whichServer << "' sent us reply=" << reply);
928f3421 264
1c756645 265 if (!cbdataReferenceValid(r->data)) {
0272dd08 266 debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication invalid callback data. helper '" << reply.whichServer << "'.");
1c756645 267 delete r;
928f3421
AJ
268 return;
269 }
270
c7baff40 271 Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
928f3421
AJ
272 assert(auth_user_request != NULL);
273
71e7400c
AJ
274 // add new helper kv-pair notes to the credentials object
275 // so that any transaction using those credentials can access them
276 auth_user_request->user()->notes.appendNewOnly(&reply.notes);
c10ebce8
AJ
277 // remove any private credentials detail which got added.
278 auth_user_request->user()->notes.remove("token");
71e7400c 279
c7baff40 280 Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw());
7afc3bf2
AJ
281 assert(lm_request != NULL);
282 assert(lm_request->waiting);
56a49fda 283
7afc3bf2
AJ
284 lm_request->waiting = 0;
285 safe_free(lm_request->client_blob);
928f3421 286
7afc3bf2
AJ
287 assert(auth_user_request->user() != NULL);
288 assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM);
289
290 if (lm_request->authserver == NULL)
0272dd08 291 lm_request->authserver = reply.whichServer.get(); // XXX: no locking?
928f3421 292 else
e166785a 293 assert(reply.whichServer == lm_request->authserver);
928f3421 294
dacb64b9 295 switch (reply.result) {
2428ce02 296 case Helper::TT:
928f3421 297 /* we have been given a blob to send to the client */
7afc3bf2 298 safe_free(lm_request->server_blob);
e857372a 299 lm_request->request->flags.mustKeepalive = true;
450fe1cb 300 if (lm_request->request->flags.proxyKeepalive) {
cf9f0261
CT
301 const char *serverBlob = reply.notes.findFirst("token");
302 lm_request->server_blob = xstrdup(serverBlob);
7afc3bf2 303 auth_user_request->user()->credentials(Auth::Handshake);
6f9a30f8 304 auth_user_request->setDenyMessage("Authentication in progress");
cf9f0261 305 debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob << "'");
928f3421 306 } else {
7afc3bf2 307 auth_user_request->user()->credentials(Auth::Failed);
6f9a30f8 308 auth_user_request->setDenyMessage("NTLM authentication requires a persistent connection");
928f3421 309 }
0272dd08
AJ
310 break;
311
2428ce02 312 case Helper::Okay: {
928f3421 313 /* we're finished, release the helper */
cf9f0261 314 const char *userLabel = reply.notes.findFirst("user");
10f8979e
AJ
315 if (!userLabel) {
316 auth_user_request->user()->credentials(Auth::Failed);
317 safe_free(lm_request->server_blob);
318 lm_request->releaseAuthServer();
319 debugs(29, DBG_CRITICAL, "ERROR: NTLM Authentication helper returned no username. Result: " << reply);
320 break;
321 }
cf9f0261 322 auth_user_request->user()->username(userLabel);
6f9a30f8 323 auth_user_request->setDenyMessage("Login successful");
7afc3bf2
AJ
324 safe_free(lm_request->server_blob);
325 lm_request->releaseAuthServer();
928f3421 326
cf9f0261 327 debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel << "'");
928f3421 328 /* connection is authenticated */
7afc3bf2 329 debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
c10ebce8 330 /* see if this is an existing user */
33e622d7 331 auto local_auth_user = lm_request->user();
02aeb7ef 332 auto cached_user = Auth::Ntlm::User::Cache()->lookup(auth_user_request->user()->userKey());
33e622d7
FC
333 if (!cached_user) {
334 local_auth_user->addToNameCache();
335 } else {
928f3421
AJ
336 /* we can't seamlessly recheck the username due to the
337 * challenge-response nature of the protocol.
58e94342
AJ
338 * Just free the temporary auth_user after merging as
339 * much of it new state into the existing one as possible */
33e622d7 340 cached_user->absorb(local_auth_user);
7afc3bf2 341 /* from here on we are working with the original cached credentials. */
33e622d7 342 local_auth_user = cached_user;
58e94342 343 auth_user_request->user(local_auth_user);
928f3421
AJ
344 }
345 /* set these to now because this is either a new login from an
346 * existing user or a new user */
347 local_auth_user->expiretime = current_time.tv_sec;
7afc3bf2 348 auth_user_request->user()->credentials(Auth::Ok);
7bbefa01 349 debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << auth_user_request->user()->username() << "'");
0272dd08 350 }
dacb64b9 351 break;
7afc3bf2 352
0fc383ff 353 case Helper::Error:
928f3421 354 /* authentication failure (wrong password, etc.) */
6f9a30f8 355 auth_user_request->denyMessageFromHelper("NTLM", reply);
7afc3bf2
AJ
356 auth_user_request->user()->credentials(Auth::Failed);
357 safe_free(lm_request->server_blob);
358 lm_request->releaseAuthServer();
87a122b0 359 debugs(29, 4, "Failed validating user via NTLM. Result: " << reply);
0fc383ff 360 break;
0272dd08 361
2428ce02 362 case Helper::Unknown:
0272dd08 363 debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << reply.whichServer << "' crashed!.");
f53969cc 364 /* continue to the next case */
0272dd08 365
32fd6d8a 366 case Helper::TimedOut:
0fc383ff 367 case Helper::BrokenHelper:
928f3421
AJ
368 /* TODO kick off a refresh process. This can occur after a YR or after
369 * a KK. If after a YR release the helper and resubmit the request via
370 * Authenticate NTLM start.
371 * If after a KK deny the user's request w/ 407 and mark the helper as
372 * Needing YR. */
2428ce02 373 if (reply.result == Helper::Unknown)
6f9a30f8 374 auth_user_request->setDenyMessage("Internal Error");
7bbefa01 375 else
6f9a30f8 376 auth_user_request->denyMessageFromHelper("NTLM", reply);
d87154ee 377 auth_user_request->user()->credentials(Auth::Failed);
7afc3bf2
AJ
378 safe_free(lm_request->server_blob);
379 lm_request->releaseAuthServer();
87a122b0 380 debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Result: " << reply);
0fc383ff 381 break;
928f3421
AJ
382 }
383
7afc3bf2
AJ
384 if (lm_request->request) {
385 HTTPMSGUNLOCK(lm_request->request);
386 lm_request->request = NULL;
928f3421 387 }
4c535e87 388 r->handler(r->data);
1c756645 389 delete r;
928f3421 390}
f53969cc 391