-#include "config.h"
-#include "auth/ntlm/auth_ntlm.h"
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "AccessLogEntry.h"
+#include "auth/ntlm/Config.h"
#include "auth/ntlm/UserRequest.h"
#include "auth/State.h"
#include "cbdata.h"
+#include "client_side.h"
+#include "fatal.h"
+#include "format/Format.h"
+#include "globals.h"
+#include "helper.h"
+#include "helper/Reply.h"
+#include "HttpMsg.h"
#include "HttpRequest.h"
+#include "MemBuf.h"
#include "SquidTime.h"
-/* state wrapper functions */
-
-AuthNTLMUserRequest::AuthNTLMUserRequest()
+Auth::Ntlm::UserRequest::UserRequest()
{
waiting=0;
client_blob=0;
server_blob=0;
authserver=NULL;
- request = NULL;
+ request=NULL;
}
-AuthNTLMUserRequest::~AuthNTLMUserRequest()
+Auth::Ntlm::UserRequest::~UserRequest()
{
- assert(RefCountCount()==0);
+ assert(LockCount()==0);
safe_free(server_blob);
safe_free(client_blob);
}
const char *
-AuthNTLMUserRequest::connLastHeader()
+Auth::Ntlm::UserRequest::connLastHeader()
{
return NULL;
}
-/* See AuthUserRequest.cc::authenticateDirection for return values */
int
-AuthNTLMUserRequest::module_direction()
+Auth::Ntlm::UserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == Auth::Ok) {
+ debugs(29, 9, HERE << "user authenticated.");
+ return 1;
+ }
+
+ debugs(29, 9, HERE << "user not fully authenticated.");
+ return 0;
+}
+
+const char *
+Auth::Ntlm::UserRequest::credentialsStr()
+{
+ static char buf[MAX_AUTHTOKEN_LEN];
+ if (user()->credentials() == Auth::Pending) {
+ snprintf(buf, sizeof(buf), "YR %s\n", client_blob);
+ } else {
+ snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
+ }
+ return buf;
+}
+
+Auth::Direction
+Auth::Ntlm::UserRequest::module_direction()
{
- /* null auth_user is checked for by authenticateDirection */
+ /* null auth_user is checked for by Auth::UserRequest::direction() */
if (waiting || client_blob)
- return -1; /* need helper response to continue */
+ return Auth::CRED_LOOKUP; /* need helper response to continue */
if (user()->auth_type != Auth::AUTH_NTLM)
- return -2;
+ return Auth::CRED_ERROR;
switch (user()->credentials()) {
- case AuthUser::Handshake:
+ case Auth::Handshake:
assert(server_blob);
- return 1; /* send to client */
+ return Auth::CRED_CHALLENGE;
- case AuthUser::Ok:
- return 0; /* do nothing */
+ case Auth::Ok:
+ return Auth::CRED_VALID;
- case AuthUser::Failed:
- return -2;
+ case Auth::Failed:
+ return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE?
default:
debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
- return -2;
+ return Auth::CRED_ERROR;
}
}
-/* send the initial data to a stateful ntlm authenticator module */
void
-AuthNTLMUserRequest::module_start(RH * handler, void *data)
+Auth::Ntlm::UserRequest::startHelperLookup(HttpRequest *, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
{
- authenticateStateData *r = NULL;
- static char buf[8192];
+ static char buf[MAX_AUTHTOKEN_LEN];
assert(data);
assert(handler);
- debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
-
if (static_cast<Auth::Ntlm::Config*>(Auth::Config::Find("ntlm"))->authenticateProgram == NULL) {
debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured.");
- handler(data, NULL);
+ handler(data);
return;
}
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
+ debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
- if (user()->credentials() == AuthUser::Pending) {
- snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ const char *keyExtras = helperRequestKeyExtras(request, al);
+ if (user()->credentials() == Auth::Pending) {
+ if (keyExtras)
+ snprintf(buf, sizeof(buf), "YR %s %s\n", client_blob, keyExtras);
+ else
+ snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
} else {
- snprintf(buf, 8192, "KK %s\n", client_blob);
+ if (keyExtras)
+ snprintf(buf, sizeof(buf), "KK %s %s\n", client_blob, keyExtras);
+ else
+ snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
}
-
waiting = 1;
safe_free(client_blob);
- helperStatefulSubmit(ntlmauthenticators, buf, AuthNTLMUserRequest::HandleReply, r, authserver);
+ helperStatefulSubmit(ntlmauthenticators, buf, Auth::Ntlm::UserRequest::HandleReply,
+ new Auth::StateData(this, handler, data), authserver);
}
/**
* for this request connections use.
*/
void
-AuthNTLMUserRequest::releaseAuthServer()
+Auth::Ntlm::UserRequest::releaseAuthServer()
{
if (authserver) {
debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
}
void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
+Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
{
- assert(conn != NULL);
-
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
- return;
- }
-
- // unlock / un-reserve the helpers
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
-
- conn->auth_user_request = NULL;
-}
-
-int
-AuthNTLMUserRequest::authenticated() const
-{
- if (user()->credentials() == AuthUser::Ok) {
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
- return 1;
- }
-
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
-
- return 0;
-}
-
-void
-AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
- const char *proxy_auth, *blob;
-
- assert(this);
-
/* Check that we are in the client side, where we can generate
* auth challenges */
if (conn == NULL || !cbdataReferenceValid(conn)) {
- user()->credentials(AuthUser::Failed);
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
+ user()->credentials(Auth::Failed);
+ debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication attempt to perform authentication without a connection!");
return;
}
if (waiting) {
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
+ debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication waiting for helper reply!");
return;
}
if (server_blob) {
- debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
+ debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!");
return;
}
/* get header */
- proxy_auth = aRequest->header.getStr(type);
+ const char *proxy_auth = aRequest->header.getStr(type);
/* locate second word */
- blob = proxy_auth;
+ const char *blob = proxy_auth;
/* if proxy_auth is actually NULL, we'd better not manipulate it. */
if (blob) {
while (xisspace(*blob) && *blob)
- blob++;
+ ++blob;
while (!xisspace(*blob) && *blob)
- blob++;
+ ++blob;
while (xisspace(*blob) && *blob)
- blob++;
+ ++blob;
}
switch (user()->credentials()) {
- case AuthUser::Unchecked:
+ case Auth::Unchecked:
/* we've received a ntlm request. pass to a helper */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
- user()->credentials(AuthUser::Pending);
+ debugs(29, 9, HERE << "auth state ntlm none. Received blob: '" << proxy_auth << "'");
+ user()->credentials(Auth::Pending);
safe_free(client_blob);
client_blob=xstrdup(blob);
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
+ assert(conn->getAuth() == NULL);
+ conn->setAuth(this, "new NTLM handshake request");
request = aRequest;
HTTPMSGLOCK(request);
break;
- case AuthUser::Pending:
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
+ case Auth::Pending:
+ debugs(29, DBG_IMPORTANT, HERE << "need to ask helper");
break;
- case AuthUser::Handshake:
+ case Auth::Handshake:
/* we should have received a blob from the client. Hand it off to
* some helper */
safe_free(client_blob);
- client_blob = xstrdup (blob);
-
+ client_blob = xstrdup(blob);
if (request)
HTTPMSGUNLOCK(request);
request = aRequest;
HTTPMSGLOCK(request);
break;
- case AuthUser::Ok:
- fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
+ case Auth::Ok:
+ fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
break;
- case AuthUser::Failed:
+ case Auth::Failed:
/* we've failed somewhere in authentication */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
+ debugs(29, 9, HERE << "auth state ntlm failed. " << proxy_auth);
break;
}
}
void
-AuthNTLMUserRequest::HandleReply(void *data, void *lastserver, char *reply)
+Auth::Ntlm::UserRequest::HandleReply(void *data, const Helper::Reply &reply)
{
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
-
- int valid;
- char *blob;
+ Auth::StateData *r = static_cast<Auth::StateData *>(data);
- debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
- valid = cbdataReferenceValid(r->data);
+ debugs(29, 8, HERE << "helper: '" << reply.whichServer << "' sent us reply=" << reply);
- if (!valid) {
- debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'.");
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ if (!cbdataReferenceValid(r->data)) {
+ debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication invalid callback data. helper '" << reply.whichServer << "'.");
+ delete r;
return;
}
- if (!reply) {
- debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
- reply = (char *)"BH Internal error";
- }
-
- AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
+ Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
assert(auth_user_request != NULL);
- AuthNTLMUserRequest *ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request.getRaw());
- assert(ntlm_request != NULL);
- assert(ntlm_request->waiting);
- assert(ntlm_request->user() != NULL);
- assert(ntlm_request->user()->auth_type == Auth::AUTH_NTLM);
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ auth_user_request->user()->notes.appendNewOnly(&reply.notes);
- ntlm_request->waiting = 0;
- safe_free(ntlm_request->client_blob);
+ Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw());
+ assert(lm_request != NULL);
+ assert(lm_request->waiting);
- if (ntlm_request->authserver == NULL)
- ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(ntlm_request->authserver == lastserver);
+ lm_request->waiting = 0;
+ safe_free(lm_request->client_blob);
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
- if (blob)
- blob++;
+ assert(auth_user_request->user() != NULL);
+ assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM);
+
+ if (lm_request->authserver == NULL)
+ lm_request->authserver = reply.whichServer.get(); // XXX: no locking?
+ else
+ assert(reply.whichServer == lm_request->authserver);
- if (strncasecmp(reply, "TT ", 3) == 0) {
+ switch (reply.result) {
+ case Helper::TT:
/* we have been given a blob to send to the client */
- safe_free(ntlm_request->server_blob);
- ntlm_request->request->flags.must_keepalive = 1;
- if (ntlm_request->request->flags.proxy_keepalive) {
- ntlm_request->server_blob = xstrdup(blob);
- ntlm_request->user()->credentials(AuthUser::Handshake);
+ safe_free(lm_request->server_blob);
+ lm_request->request->flags.mustKeepalive = true;
+ if (lm_request->request->flags.proxyKeepalive) {
+ const char *serverBlob = reply.notes.findFirst("token");
+ lm_request->server_blob = xstrdup(serverBlob);
+ auth_user_request->user()->credentials(Auth::Handshake);
auth_user_request->denyMessage("Authentication in progress");
- debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
+ debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob << "'");
} else {
- ntlm_request->user()->credentials(AuthUser::Failed);
+ auth_user_request->user()->credentials(Auth::Failed);
auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
}
- } else if (strncasecmp(reply, "AF ", 3) == 0) {
+ break;
+
+ case Helper::Okay: {
/* we're finished, release the helper */
- auth_user_request->user()->username(blob);
+ const char *userLabel = reply.notes.findFirst("user");
+ if (!userLabel) {
+ auth_user_request->user()->credentials(Auth::Failed);
+ safe_free(lm_request->server_blob);
+ lm_request->releaseAuthServer();
+ debugs(29, DBG_CRITICAL, "ERROR: NTLM Authentication helper returned no username. Result: " << reply);
+ break;
+ }
+ auth_user_request->user()->username(userLabel);
auth_user_request->denyMessage("Login successful");
- safe_free(ntlm_request->server_blob);
+ safe_free(lm_request->server_blob);
+ lm_request->releaseAuthServer();
- debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
+ debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel << "'");
/* connection is authenticated */
- debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << auth_user_request->user()->username());
+ debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
/* see if this is an existing user with a different proxy_auth
* string */
- auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
- AuthUser::Pointer local_auth_user = ntlm_request->user();
+ AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->userKey()));
+ Auth::User::Pointer local_auth_user = lm_request->user();
while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM ||
- strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
+ strcmp(usernamehash->user()->userKey(), auth_user_request->user()->userKey()) != 0))
usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
if (usernamehash) {
/* we can't seamlessly recheck the username due to the
* challenge-response nature of the protocol.
- * Just free the temporary auth_user */
+ * Just free the temporary auth_user after merging as
+ * much of it new state into the existing one as possible */
usernamehash->user()->absorb(local_auth_user);
+ /* from here on we are working with the original cached credentials. */
local_auth_user = usernamehash->user();
- ntlm_request->_auth_user = local_auth_user;
+ auth_user_request->user(local_auth_user);
} else {
/* store user in hash's */
local_auth_user->addToNameCache();
/* set these to now because this is either a new login from an
* existing user or a new user */
local_auth_user->expiretime = current_time.tv_sec;
- ntlm_request->releaseAuthServer();
- local_auth_user->credentials(AuthUser::Ok);
- } else if (strncasecmp(reply, "NA ", 3) == 0) {
+ auth_user_request->user()->credentials(Auth::Ok);
+ debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << auth_user_request->user()->username() << "'");
+ }
+ break;
+
+ case Helper::Error: {
/* authentication failure (wrong password, etc.) */
- auth_user_request->denyMessage(blob);
- ntlm_request->user()->credentials(AuthUser::Failed);
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
- } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ const char *errNote = reply.notes.find("message");
+ if (errNote != NULL)
+ auth_user_request->denyMessage(errNote);
+ else
+ auth_user_request->denyMessage("NTLM Authentication denied with no reason given");
+ auth_user_request->user()->credentials(Auth::Failed);
+ safe_free(lm_request->server_blob);
+ lm_request->releaseAuthServer();
+ debugs(29, 4, "Failed validating user via NTLM. Result: " << reply);
+ }
+ break;
+
+ case Helper::Unknown:
+ debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << reply.whichServer << "' crashed!.");
+ /* continue to the next case */
+
+ case Helper::TimedOut:
+ case Helper::BrokenHelper: {
/* TODO kick off a refresh process. This can occur after a YR or after
* a KK. If after a YR release the helper and resubmit the request via
* Authenticate NTLM start.
* If after a KK deny the user's request w/ 407 and mark the helper as
* Needing YR. */
- auth_user_request->denyMessage(blob);
- auth_user_request->user()->credentials(AuthUser::Failed);
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
- } else {
- /* protocol error */
- fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ const char *errNote = reply.notes.find("message");
+ if (reply.result == Helper::Unknown)
+ auth_user_request->denyMessage("Internal Error");
+ else if (errNote != NULL)
+ auth_user_request->denyMessage(errNote);
+ else
+ auth_user_request->denyMessage("NTLM Authentication failed with no reason given");
+ auth_user_request->user()->credentials(Auth::Failed);
+ safe_free(lm_request->server_blob);
+ lm_request->releaseAuthServer();
+ debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Result: " << reply);
+ }
+ break;
}
- if (ntlm_request->request) {
- HTTPMSGUNLOCK(ntlm_request->request);
- ntlm_request->request = NULL;
+ if (lm_request->request) {
+ HTTPMSGUNLOCK(lm_request->request);
+ lm_request->request = NULL;
}
- r->handler(r->data, NULL);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ r->handler(r->data);
+ delete r;
}
+