2 #include "AccessLogEntry.h"
3 #include "auth/ntlm/auth_ntlm.h"
4 #include "auth/ntlm/UserRequest.h"
5 #include "auth/State.h"
7 #include "client_side.h"
8 #include "format/Format.h"
11 #include "HttpRequest.h"
13 #include "SquidTime.h"
15 Auth::Ntlm::UserRequest::UserRequest()
24 Auth::Ntlm::UserRequest::~UserRequest()
26 assert(LockCount()==0);
27 safe_free(server_blob
);
28 safe_free(client_blob
);
33 HTTPMSGUNLOCK(request
);
39 Auth::Ntlm::UserRequest::connLastHeader()
45 Auth::Ntlm::UserRequest::authenticated() const
47 if (user() != NULL
&& user()->credentials() == Auth::Ok
) {
48 debugs(29, 9, HERE
<< "user authenticated.");
52 debugs(29, 9, HERE
<< "user not fully authenticated.");
57 Auth::Ntlm::UserRequest::credentialsStr()
59 static char buf
[MAX_AUTHTOKEN_LEN
];
60 if (user()->credentials() == Auth::Pending
) {
61 snprintf(buf
, sizeof(buf
), "YR %s\n", client_blob
);
63 snprintf(buf
, sizeof(buf
), "KK %s\n", client_blob
);
69 Auth::Ntlm::UserRequest::module_direction()
71 /* null auth_user is checked for by Auth::UserRequest::direction() */
73 if (waiting
|| client_blob
)
74 return Auth::CRED_LOOKUP
; /* need helper response to continue */
76 if (user()->auth_type
!= Auth::AUTH_NTLM
)
77 return Auth::CRED_ERROR
;
79 switch (user()->credentials()) {
83 return Auth::CRED_CHALLENGE
;
86 return Auth::CRED_VALID
;
89 return Auth::CRED_ERROR
; // XXX: really? not VALID or CHALLENGE?
92 debugs(29, DBG_IMPORTANT
, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
93 return Auth::CRED_ERROR
;
98 Auth::Ntlm::UserRequest::startHelperLookup(HttpRequest
*req
, AccessLogEntry::Pointer
&al
, AUTHCB
* handler
, void *data
)
100 static char buf
[MAX_AUTHTOKEN_LEN
];
105 if (static_cast<Auth::Ntlm::Config
*>(Auth::Config::Find("ntlm"))->authenticateProgram
== NULL
) {
106 debugs(29, DBG_CRITICAL
, "ERROR: NTLM Start: no NTLM program configured.");
111 debugs(29, 8, HERE
<< "credentials state is '" << user()->credentials() << "'");
113 const char *keyExtras
= helperRequestKeyExtras(request
, al
);
114 if (user()->credentials() == Auth::Pending
) {
116 snprintf(buf
, sizeof(buf
), "YR %s %s\n", client_blob
, keyExtras
);
118 snprintf(buf
, sizeof(buf
), "YR %s\n", client_blob
); //CHECKME: can ever client_blob be 0 here?
121 snprintf(buf
, sizeof(buf
), "KK %s %s\n", client_blob
, keyExtras
);
123 snprintf(buf
, sizeof(buf
), "KK %s\n", client_blob
);
127 safe_free(client_blob
);
128 helperStatefulSubmit(ntlmauthenticators
, buf
, Auth::Ntlm::UserRequest::HandleReply
,
129 new Auth::StateData(this, handler
, data
), authserver
);
133 * Atomic action: properly release the NTLM auth helpers which may have been reserved
134 * for this request connections use.
137 Auth::Ntlm::UserRequest::releaseAuthServer()
140 debugs(29, 6, HERE
<< "releasing NTLM auth server '" << authserver
<< "'");
141 helperStatefulReleaseServer(authserver
);
144 debugs(29, 6, HERE
<< "No NTLM auth server to release.");
148 Auth::Ntlm::UserRequest::authenticate(HttpRequest
* aRequest
, ConnStateData
* conn
, http_hdr_type type
)
152 /* Check that we are in the client side, where we can generate
155 if (conn
== NULL
|| !cbdataReferenceValid(conn
)) {
156 user()->credentials(Auth::Failed
);
157 debugs(29, DBG_IMPORTANT
, "WARNING: NTLM Authentication attempt to perform authentication without a connection!");
162 debugs(29, DBG_IMPORTANT
, "WARNING: NTLM Authentication waiting for helper reply!");
167 debugs(29, 2, HERE
<< "need to challenge client '" << server_blob
<< "'!");
172 const char *proxy_auth
= aRequest
->header
.getStr(type
);
174 /* locate second word */
175 const char *blob
= proxy_auth
;
177 /* if proxy_auth is actually NULL, we'd better not manipulate it. */
179 while (xisspace(*blob
) && *blob
)
182 while (!xisspace(*blob
) && *blob
)
185 while (xisspace(*blob
) && *blob
)
189 switch (user()->credentials()) {
191 case Auth::Unchecked
:
192 /* we've received a ntlm request. pass to a helper */
193 debugs(29, 9, HERE
<< "auth state ntlm none. Received blob: '" << proxy_auth
<< "'");
194 user()->credentials(Auth::Pending
);
195 safe_free(client_blob
);
196 client_blob
=xstrdup(blob
);
197 assert(conn
->getAuth() == NULL
);
198 conn
->setAuth(this, "new NTLM handshake request");
200 HTTPMSGLOCK(request
);
204 debugs(29, DBG_IMPORTANT
, HERE
<< "need to ask helper");
207 case Auth::Handshake
:
208 /* we should have received a blob from the client. Hand it off to
210 safe_free(client_blob
);
211 client_blob
= xstrdup(blob
);
213 HTTPMSGUNLOCK(request
);
215 HTTPMSGLOCK(request
);
219 fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
223 /* we've failed somewhere in authentication */
224 debugs(29, 9, HERE
<< "auth state ntlm failed. " << proxy_auth
);
230 Auth::Ntlm::UserRequest::HandleReply(void *data
, const HelperReply
&reply
)
232 Auth::StateData
*r
= static_cast<Auth::StateData
*>(data
);
234 debugs(29, 8, HERE
<< "helper: '" << reply
.whichServer
<< "' sent us reply=" << reply
);
236 if (!cbdataReferenceValid(r
->data
)) {
237 debugs(29, DBG_IMPORTANT
, "ERROR: NTLM Authentication invalid callback data. helper '" << reply
.whichServer
<< "'.");
242 Auth::UserRequest::Pointer auth_user_request
= r
->auth_user_request
;
243 assert(auth_user_request
!= NULL
);
245 // add new helper kv-pair notes to the credentials object
246 // so that any transaction using those credentials can access them
247 auth_user_request
->user()->notes
.appendNewOnly(&reply
.notes
);
249 Auth::Ntlm::UserRequest
*lm_request
= dynamic_cast<Auth::Ntlm::UserRequest
*>(auth_user_request
.getRaw());
250 assert(lm_request
!= NULL
);
251 assert(lm_request
->waiting
);
253 lm_request
->waiting
= 0;
254 safe_free(lm_request
->client_blob
);
256 assert(auth_user_request
->user() != NULL
);
257 assert(auth_user_request
->user()->auth_type
== Auth::AUTH_NTLM
);
259 if (lm_request
->authserver
== NULL
)
260 lm_request
->authserver
= reply
.whichServer
.get(); // XXX: no locking?
262 assert(reply
.whichServer
== lm_request
->authserver
);
264 switch (reply
.result
) {
265 case HelperReply::TT
:
266 /* we have been given a blob to send to the client */
267 safe_free(lm_request
->server_blob
);
268 lm_request
->request
->flags
.mustKeepalive
= true;
269 if (lm_request
->request
->flags
.proxyKeepalive
) {
270 const char *serverBlob
= reply
.notes
.findFirst("token");
271 lm_request
->server_blob
= xstrdup(serverBlob
);
272 auth_user_request
->user()->credentials(Auth::Handshake
);
273 auth_user_request
->denyMessage("Authentication in progress");
274 debugs(29, 4, HERE
<< "Need to challenge the client with a server token: '" << serverBlob
<< "'");
276 auth_user_request
->user()->credentials(Auth::Failed
);
277 auth_user_request
->denyMessage("NTLM authentication requires a persistent connection");
281 case HelperReply::Okay
: {
282 /* we're finished, release the helper */
283 const char *userLabel
= reply
.notes
.findFirst("user");
285 auth_user_request
->user()->credentials(Auth::Failed
);
286 safe_free(lm_request
->server_blob
);
287 lm_request
->releaseAuthServer();
288 debugs(29, DBG_CRITICAL
, "ERROR: NTLM Authentication helper returned no username. Result: " << reply
);
291 auth_user_request
->user()->username(userLabel
);
292 auth_user_request
->denyMessage("Login successful");
293 safe_free(lm_request
->server_blob
);
294 lm_request
->releaseAuthServer();
296 debugs(29, 4, HERE
<< "Successfully validated user via NTLM. Username '" << userLabel
<< "'");
297 /* connection is authenticated */
298 debugs(29, 4, HERE
<< "authenticated user " << auth_user_request
->user()->username());
299 /* see if this is an existing user with a different proxy_auth
301 AuthUserHashPointer
*usernamehash
= static_cast<AuthUserHashPointer
*>(hash_lookup(proxy_auth_username_cache
, auth_user_request
->user()->userKey()));
302 Auth::User::Pointer local_auth_user
= lm_request
->user();
303 while (usernamehash
&& (usernamehash
->user()->auth_type
!= Auth::AUTH_NTLM
||
304 strcmp(usernamehash
->user()->userKey(), auth_user_request
->user()->userKey()) != 0))
305 usernamehash
= static_cast<AuthUserHashPointer
*>(usernamehash
->next
);
307 /* we can't seamlessly recheck the username due to the
308 * challenge-response nature of the protocol.
309 * Just free the temporary auth_user after merging as
310 * much of it new state into the existing one as possible */
311 usernamehash
->user()->absorb(local_auth_user
);
312 /* from here on we are working with the original cached credentials. */
313 local_auth_user
= usernamehash
->user();
314 auth_user_request
->user(local_auth_user
);
316 /* store user in hash's */
317 local_auth_user
->addToNameCache();
319 /* set these to now because this is either a new login from an
320 * existing user or a new user */
321 local_auth_user
->expiretime
= current_time
.tv_sec
;
322 auth_user_request
->user()->credentials(Auth::Ok
);
323 debugs(29, 4, HERE
<< "Successfully validated user via NTLM. Username '" << auth_user_request
->user()->username() << "'");
327 case HelperReply::Error
: {
328 /* authentication failure (wrong password, etc.) */
329 const char *errNote
= reply
.notes
.find("message");
331 auth_user_request
->denyMessage(errNote
);
333 auth_user_request
->denyMessage("NTLM Authentication denied with no reason given");
334 auth_user_request
->user()->credentials(Auth::Failed
);
335 safe_free(lm_request
->server_blob
);
336 lm_request
->releaseAuthServer();
337 debugs(29, 4, "Failed validating user via NTLM. Result: " << reply
);
341 case HelperReply::Unknown
:
342 debugs(29, DBG_IMPORTANT
, "ERROR: NTLM Authentication Helper '" << reply
.whichServer
<< "' crashed!.");
343 /* continue to the next case */
345 case HelperReply::BrokenHelper
: {
346 /* TODO kick off a refresh process. This can occur after a YR or after
347 * a KK. If after a YR release the helper and resubmit the request via
348 * Authenticate NTLM start.
349 * If after a KK deny the user's request w/ 407 and mark the helper as
351 const char *errNote
= reply
.notes
.find("message");
352 if (reply
.result
== HelperReply::Unknown
)
353 auth_user_request
->denyMessage("Internal Error");
354 else if (errNote
!= NULL
)
355 auth_user_request
->denyMessage(errNote
);
357 auth_user_request
->denyMessage("NTLM Authentication failed with no reason given");
358 auth_user_request
->user()->credentials(Auth::Failed
);
359 safe_free(lm_request
->server_blob
);
360 lm_request
->releaseAuthServer();
361 debugs(29, DBG_IMPORTANT
, "ERROR: NTLM Authentication validating user. Result: " << reply
);
366 if (lm_request
->request
) {
367 HTTPMSGUNLOCK(lm_request
->request
);
368 lm_request
->request
= NULL
;