-
/*
- * $Id: auth_ntlm.cc,v 1.27 2003/02/04 23:24:42 robertc Exp $
+ * $Id$
*
* DEBUG: section 29 NTLM Authenticator
- * AUTHOR: Robert Collins
+ * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
* ----------------------------------------------------------
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
#include "squid.h"
#include "auth_ntlm.h"
#include "authenticate.h"
+#include "CacheManager.h"
#include "Store.h"
+#include "client_side.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+/* TODO remove this include */
+#include "ntlmScheme.h"
+#include "wordlist.h"
+#include "SquidTime.h"
+
+static void
+authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request);
-extern AUTHSSETUP authSchemeSetup_ntlm;
static void
authenticateStateFree(authenticateStateData * r)
{
+ AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
cbdataFree(r);
}
/* NTLM Scheme */
static HLPSCB authenticateNTLMHandleReply;
-static HLPSCB authenticateNTLMHandleplaceholder;
-static AUTHSACTIVE authenticateNTLMActive;
-static AUTHSAUTHED authNTLMAuthenticated;
-static AUTHSAUTHUSER authenticateNTLMAuthenticateUser;
-static AUTHSCONFIGURED authNTLMConfigured;
-static AUTHSFIXERR authenticateNTLMFixErrorHeader;
-static AUTHSFREE authenticateNTLMFreeUser;
-static AUTHSDIRECTION authenticateNTLMDirection;
-static AUTHSDECODE authenticateDecodeNTLMAuth;
-static AUTHSDUMP authNTLMCfgDump;
-static AUTHSFREECONFIG authNTLMFreeConfig;
-static AUTHSINIT authNTLMInit;
-static AUTHSONCLOSEC authenticateNTLMOnCloseConnection;
-static AUTHSCONNLASTHEADER NTLMLastHeader;
-static AUTHSUSERNAME authenticateNTLMUsername;
-static AUTHSREQFREE authNTLMAURequestFree;
-static AUTHSPARSE authNTLMParse;
-static AUTHSSTART authenticateNTLMStart;
static AUTHSSTATS authenticateNTLMStats;
-static AUTHSSHUTDOWN authNTLMDone;
-
-/* helper callbacks to handle per server state data */
-static HLPSAVAIL authenticateNTLMHelperServerAvailable;
-static HLPSONEQ authenticateNTLMHelperServerOnEmpty;
static statefulhelper *ntlmauthenticators = NULL;
static int authntlm_initialised = 0;
-static MemPool *ntlm_helper_state_pool = NULL;
-static MemPool *ntlm_user_pool = NULL;
-static MemPool *ntlm_request_pool = NULL;
-static MemPool *ntlm_user_hash_pool = NULL;
-
-static auth_ntlm_config *ntlmConfig = NULL;
+static auth_ntlm_config ntlmConfig;
static hash_table *proxy_auth_cache = NULL;
*
*/
-static void
-authNTLMDone(void)
+/* move to ntlmScheme.cc */
+void
+ntlmScheme::done()
{
- debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
+ /* TODO: this should be a Config call. */
+ debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
+
if (ntlmauthenticators)
- helperStatefulShutdown(ntlmauthenticators);
+ helperStatefulShutdown(ntlmauthenticators);
+
authntlm_initialised = 0;
+
if (!shutting_down)
- return;
+ return;
+
if (ntlmauthenticators)
- helperStatefulFree(ntlmauthenticators);
+ helperStatefulFree(ntlmauthenticators);
+
ntlmauthenticators = NULL;
- if (ntlm_helper_state_pool) {
- assert(memPoolInUseCount(ntlm_helper_state_pool) == 0);
- memPoolDestroy(&ntlm_helper_state_pool);
- }
- if (ntlm_request_pool) {
- assert(memPoolInUseCount(ntlm_request_pool) == 0);
- memPoolDestroy(&ntlm_request_pool);
- }
- if (ntlm_user_pool) {
- assert(memPoolInUseCount(ntlm_user_pool) == 0);
- memPoolDestroy(&ntlm_user_pool);
- }
- debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
+
+ debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
}
/* free any allocated configuration details */
-static void
-authNTLMFreeConfig(authScheme * scheme)
+void
+AuthNTLMConfig::done()
{
- if (ntlmConfig == NULL)
- return;
- assert(ntlmConfig == scheme->scheme_data);
- if (ntlmConfig->authenticate)
- wordlistDestroy(&ntlmConfig->authenticate);
- xfree(ntlmConfig);
- ntlmConfig = NULL;
+ if (authenticate)
+ wordlistDestroy(&authenticate);
}
-static void
-authNTLMCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
+void
+AuthNTLMConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
{
- auth_ntlm_config *config = static_cast<auth_ntlm_config *>(scheme->scheme_data);
- wordlist *list = config->authenticate;
+ wordlist *list = authenticate;
storeAppendPrintf(entry, "%s %s", name, "ntlm");
+
while (list != NULL) {
- storeAppendPrintf(entry, " %s", list->key);
- list = list->next;
+ storeAppendPrintf(entry, " %s", list->key);
+ list = list->next;
}
- storeAppendPrintf(entry, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
- name, "ntlm", config->authenticateChildren,
- name, "ntlm", config->challengeuses,
- name, "ntlm", (int) config->challengelifetime);
+
+ storeAppendPrintf(entry, "\n%s ntlm children %d\n",
+ name, authenticateChildren);
+ storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "ntlm", keep_alive ? "on" : "off");
}
-static void
-authNTLMParse(authScheme * scheme, int n_configured, char *param_str)
+AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1)
+{ }
+
+void
+AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
{
- if (scheme->scheme_data == NULL) {
- assert(ntlmConfig == NULL);
- /* this is the first param to be found */
- scheme->scheme_data = xmalloc(sizeof(auth_ntlm_config));
- memset(scheme->scheme_data, 0, sizeof(auth_ntlm_config));
- ntlmConfig = static_cast<auth_ntlm_config *>(scheme->scheme_data);
- ntlmConfig->authenticateChildren = 5;
- ntlmConfig->challengeuses = 0;
- ntlmConfig->challengelifetime = 60;
- }
- ntlmConfig = static_cast<auth_ntlm_config *>(scheme->scheme_data);
if (strcasecmp(param_str, "program") == 0) {
- if (ntlmConfig->authenticate)
- wordlistDestroy(&ntlmConfig->authenticate);
- parse_wordlist(&ntlmConfig->authenticate);
- requirePathnameExists("authparam ntlm program", ntlmConfig->authenticate->key);
+ if (authenticate)
+ wordlistDestroy(&authenticate);
+
+ parse_wordlist(&authenticate);
+
+ requirePathnameExists("authparam ntlm program", authenticate->key);
} else if (strcasecmp(param_str, "children") == 0) {
- parse_int(&ntlmConfig->authenticateChildren);
- } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
- parse_int(&ntlmConfig->challengeuses);
- } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
- parse_time_t(&ntlmConfig->challengelifetime);
+ parse_int(&authenticateChildren);
+ } else if (strcasecmp(param_str, "keep_alive") == 0) {
+ parse_onoff(&keep_alive);
} else {
- debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str);
+ debugs(29, 0, "AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '" << param_str << "'");
}
+
/*
* disable client side request pipelining. There is a race with
* NTLM when the client sends a second request on an NTLM
* state will be preserved. Caveats: this should be a post-parse
* test, but that can wait for the modular parser to be integrated.
*/
- if (ntlmConfig->authenticate)
- Config.onoff.pipeline_prefetch = 0;
+ if (authenticate)
+ Config.onoff.pipeline_prefetch = 0;
}
-
-void
-authSchemeSetup_ntlm(authscheme_entry_t * authscheme)
+const char *
+AuthNTLMConfig::type() const
{
- assert(!authntlm_initialised);
- authscheme->Active = authenticateNTLMActive;
- authscheme->configured = authNTLMConfigured;
- authscheme->parse = authNTLMParse;
- authscheme->dump = authNTLMCfgDump;
- authscheme->requestFree = authNTLMAURequestFree;
- authscheme->freeconfig = authNTLMFreeConfig;
- authscheme->init = authNTLMInit;
- authscheme->authAuthenticate = authenticateNTLMAuthenticateUser;
- authscheme->authenticated = authNTLMAuthenticated;
- authscheme->authFixHeader = authenticateNTLMFixErrorHeader;
- authscheme->FreeUser = authenticateNTLMFreeUser;
- authscheme->authStart = authenticateNTLMStart;
- authscheme->authStats = authenticateNTLMStats;
- authscheme->authUserUsername = authenticateNTLMUsername;
- authscheme->getdirection = authenticateNTLMDirection;
- authscheme->decodeauth = authenticateDecodeNTLMAuth;
- authscheme->donefunc = authNTLMDone;
- authscheme->oncloseconnection = authenticateNTLMOnCloseConnection;
- authscheme->authConnLastHeader = NTLMLastHeader;
+ return ntlmScheme::GetInstance().type();
}
/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
* config file */
-static void
-authNTLMInit(authScheme * scheme)
+void
+AuthNTLMConfig::init(AuthConfig * scheme)
{
- static int ntlminit = 0;
- if (ntlmConfig->authenticate) {
- if (!ntlm_helper_state_pool)
- ntlm_helper_state_pool = memPoolCreate("NTLM Helper State data", sizeof(ntlm_helper_state_t));
- if (!ntlm_user_pool)
- ntlm_user_pool = memPoolCreate("NTLM Scheme User Data", sizeof(ntlm_user_t));
- if (!ntlm_request_pool)
- ntlm_request_pool = memPoolCreate("NTLM Scheme Request Data", sizeof(ntlm_request_t));
- if (!ntlm_user_hash_pool)
- ntlm_user_hash_pool = memPoolCreate("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer));
- authntlm_initialised = 1;
- if (ntlmauthenticators == NULL)
- ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
- if (!proxy_auth_cache)
- proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
- assert(proxy_auth_cache);
- ntlmauthenticators->cmdline = ntlmConfig->authenticate;
- ntlmauthenticators->n_to_start = ntlmConfig->authenticateChildren;
- ntlmauthenticators->ipc_type = IPC_STREAM;
- ntlmauthenticators->datapool = ntlm_helper_state_pool;
- ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
- ntlmauthenticators->OnEmptyQueue = authenticateNTLMHelperServerOnEmpty;
- helperStatefulOpenServers(ntlmauthenticators);
- /*
- * TODO: In here send the initial YR to preinitialise the
- * challenge cache
- */
- /*
- * Think about this... currently we ask when the challenge
- * is needed. Better?
- */
- if (!ntlminit) {
- cachemgrRegister("ntlmauthenticator",
- "NTLM User Authenticator Stats",
- authenticateNTLMStats, 0, 1);
- ntlminit++;
- }
- CBDATA_INIT_TYPE(authenticateStateData);
+ if (authenticate) {
+
+ authntlm_initialised = 1;
+
+ if (ntlmauthenticators == NULL)
+ ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
+
+ if (!proxy_auth_cache)
+ proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
+
+ assert(proxy_auth_cache);
+
+ ntlmauthenticators->cmdline = authenticate;
+
+ ntlmauthenticators->n_to_start = authenticateChildren;
+
+ ntlmauthenticators->ipc_type = IPC_STREAM;
+
+ helperStatefulOpenServers(ntlmauthenticators);
+
+ CBDATA_INIT_TYPE(authenticateStateData);
}
}
-static int
-authenticateNTLMActive()
+void
+AuthNTLMConfig::registerWithCacheManager(void)
{
- return (authntlm_initialised == 1) ? 1 : 0;
+ CacheManager::GetInstance()->
+ registerAction("ntlmauthenticator",
+ "NTLM User Authenticator Stats",
+ authenticateNTLMStats, 0, 1);
}
+bool
+AuthNTLMConfig::active() const
+{
+ return authntlm_initialised == 1;
+}
-static int
-authNTLMConfigured()
+bool
+AuthNTLMConfig::configured() const
{
- if ((ntlmConfig != NULL) && (ntlmConfig->authenticate != NULL) && (ntlmConfig->authenticateChildren != 0) && (ntlmConfig->challengeuses > -1) && (ntlmConfig->challengelifetime > -1)) {
- debug(29, 9) ("authNTLMConfigured: returning configured\n");
- return 1;
+ if ((authenticate != NULL) && (authenticateChildren != 0)) {
+ debugs(29, 9, "AuthNTLMConfig::configured: returning configured");
+ return true;
}
- debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
- return 0;
+
+ debugs(29, 9, "AuthNTLMConfig::configured: returning unconfigured");
+ return false;
}
/* NTLM Scheme */
-
-static int
-authenticateNTLMDirection(auth_user_request_t * auth_user_request)
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNTLMUserRequest::module_direction()
{
- ntlm_request_t *ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
/* null auth_user is checked for by authenticateDirection */
- switch (ntlm_request->auth_state) {
- case AUTHENTICATE_STATE_NONE: /* no progress at all. */
- debug(29, 1) ("authenticateNTLMDirection: called before NTLM Authenticate!. Report a bug to squid-dev. au %p\n", auth_user_request);
- /* fall thru */
+
+ if (waiting || client_blob)
+ return -1; /* need helper response to continue */
+
+ switch (auth_state) {
+
+ /* no progress at all. */
+
+ case AUTHENTICATE_STATE_NONE:
+ debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
+ return -2; /* error */
+
case AUTHENTICATE_STATE_FAILED:
- return -2;
- case AUTHENTICATE_STATE_NEGOTIATE: /* send to helper */
- case AUTHENTICATE_STATE_RESPONSE: /*send to helper */
- return -1;
- case AUTHENTICATE_STATE_CHALLENGE: /* send to client */
- return 1;
- case AUTHENTICATE_STATE_DONE: /* do nothing.. */
- return 0;
+ return -2; /* error */
+
+
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ assert(server_blob);
+ return 1; /* send to client */
+
+ case AUTHENTICATE_STATE_DONE:
+ return 0; /* do nothing */
+
+ case AUTHENTICATE_STATE_INITIAL:
+ debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
+ return -2;
}
+
return -2;
}
-/*
- * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
- * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
- * else.
- */
-static void
-authenticateNTLMFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
+void
+AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
{
- ntlm_request_t *ntlm_request;
- if (ntlmConfig->authenticate) {
- /* New request, no user details */
- if (auth_user_request == NULL) {
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
- httpHeaderPutStrf(&rep->header, type, "NTLM");
- /* drop the connection */
- httpHeaderDelByName(&rep->header, "keep-alive");
- /* NTLM has problems if the initial connection is not dropped
- * I haven't checked the RFC compliance of this hack - RBCollins */
- request->flags.proxy_keepalive = 0;
- } else {
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- switch (ntlm_request->auth_state) {
- case AUTHENTICATE_STATE_NONE:
- case AUTHENTICATE_STATE_FAILED:
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
- httpHeaderPutStrf(&rep->header, type, "NTLM");
- /* drop the connection */
- httpHeaderDelByName(&rep->header, "keep-alive");
- /* NTLM has problems if the initial connection is not dropped
- * I haven't checked the RFC compliance of this hack - RBCollins */
- request->flags.proxy_keepalive = 0;
- break;
- case AUTHENTICATE_STATE_CHALLENGE:
- /* we are 'waiting' for a response */
- /* pass the challenge to the client */
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->authchallenge);
- httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->authchallenge);
- break;
- default:
- debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
- fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
- }
- }
- }
-}
+ AuthNTLMUserRequest *ntlm_request;
-static void
-authNTLMRequestFree(ntlm_request_t * ntlm_request)
-{
- if (!ntlm_request)
- return;
- if (ntlm_request->ntlmnegotiate)
- xfree(ntlm_request->ntlmnegotiate);
- if (ntlm_request->authchallenge)
- xfree(ntlm_request->authchallenge);
- if (ntlm_request->ntlmauthenticate)
- xfree(ntlm_request->ntlmauthenticate);
- if (ntlm_request->authserver != NULL && ntlm_request->authserver_deferred) {
- debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", ntlm_request->authserver);
- helperStatefulReleaseServer(ntlm_request->authserver);
- ntlm_request->authserver = NULL;
- }
- memPoolFree(ntlm_request_pool, ntlm_request);
-}
+ if (!authenticate)
+ return;
-static void
-authNTLMAURequestFree(auth_user_request_t * auth_user_request)
-{
- if (auth_user_request->scheme_data)
- authNTLMRequestFree(static_cast< ntlm_request_t *>(auth_user_request->scheme_data));
- auth_user_request->scheme_data = NULL;
-}
+ /* Need keep-alive */
+ if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
+ return;
-static void
-authenticateNTLMFreeUser(auth_user_t * auth_user)
-{
- dlink_node *link, *tmplink;
- ntlm_user_t *ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ProxyAuthCachePointer *proxy_auth_hash;
-
- debug(29, 5) ("authenticateNTLMFreeUser: Clearing NTLM scheme data\n");
- if (ntlm_user->username)
- xfree(ntlm_user->username);
- /* were they linked in by one or more proxy-authenticate headers */
- link = ntlm_user->proxy_auth_list.head;
- while (link) {
- debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link->data);
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(link->data);
- tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &ntlm_user->proxy_auth_list);
- hash_remove_link(proxy_auth_cache, (hash_link *) proxy_auth_hash);
- /* free the key (usually the proxy_auth header) */
- xfree(proxy_auth_hash->key);
- memPoolFree(ntlm_user_hash_pool, proxy_auth_hash);
+ /* New request, no user details */
+ if (auth_user_request == NULL) {
+ debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+
+ if (!keep_alive) {
+ /* drop the connection */
+ request->flags.proxy_keepalive = 0;
+ }
+ } else {
+ ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
+
+ assert(ntlm_request != NULL);
+
+ switch (ntlm_request->auth_state) {
+
+ case AUTHENTICATE_STATE_FAILED:
+ /* here it makes sense to drop the connection, as auth is
+ * tied to it, even if MAYBE the client could handle it - Kinkie */
+ request->flags.proxy_keepalive = 0;
+ /* fall through */
+
+ case AUTHENTICATE_STATE_DONE:
+ /* Special case: authentication finished OK but disallowed by ACL.
+ * Need to start over to give the client another chance.
+ */
+ /* fall through */
+
+ case AUTHENTICATE_STATE_NONE:
+ /* semantic change: do not drop the connection.
+ * 2.5 implementation used to keep it open - Kinkie */
+ debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+ break;
+
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ /* we're waiting for a response from the client. Pass it the blob */
+ debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM " << ntlm_request->server_blob << "'");
+ httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob);
+ safe_free(ntlm_request->server_blob);
+ break;
+
+
+ default:
+ debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << ".");
+ fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
+ }
}
- memPoolFree(ntlm_user_pool, ntlm_user);
- auth_user->scheme_data = NULL;
}
-static stateful_helper_callback_t
-authenticateNTLMHandleplaceholder(void *data, void *lastserver, char *reply)
+NTLMUser::~NTLMUser()
{
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
- stateful_helper_callback_t result = S_HELPER_UNKNOWN;
- /* we should only be called for placeholder requests - which have no reply string */
- assert(reply == NULL);
- assert(r->auth_user_request);
- /* standard callback stuff */
- if (!cbdataReferenceValid(r->data)) {
- debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
- return result;
- }
- /* call authenticateNTLMStart to retry this request */
- debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
- authenticateNTLMStart(r->auth_user_request, r->handler, r->data);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- return result;
+ debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
}
static stateful_helper_callback_t
authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
{
authenticateStateData *r = static_cast<authenticateStateData *>(data);
- ntlm_helper_state_t *helperstate;
+
+ int valid;
stateful_helper_callback_t result = S_HELPER_UNKNOWN;
- char *t = NULL;
- auth_user_request_t *auth_user_request;
- auth_user_t *auth_user;
- ntlm_user_t *ntlm_user;
- ntlm_request_t *ntlm_request;
- debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver, reply ? reply : "<NULL>");
- if (!cbdataReferenceValid(r->data)) {
- debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE);
- return S_HELPER_RELEASE;
+ char *blob;
+
+ AuthUserRequest *auth_user_request;
+ AuthUser *auth_user;
+ NTLMUser *ntlm_user;
+ AuthNTLMUserRequest *ntlm_request;
+
+ debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+ valid = cbdataReferenceValid(r->data);
+
+ if (!valid) {
+ debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. Releasing helper '" << lastserver << "'.");
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << S_HELPER_RELEASE);
+ return S_HELPER_RELEASE;
}
+
if (!reply) {
- /*
- * TODO: this occurs when a helper crashes. We should clean
- * up that helpers resources and queued requests.
- */
- fatal("authenticateNTLMHandleReply: called with no result string\n");
+ debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
+ reply = (char *)"BH Internal error";
}
+
+ auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+ ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
+
+ assert(ntlm_request != NULL);
+ assert(ntlm_request->waiting);
+ ntlm_request->waiting = 0;
+ safe_free(ntlm_request->client_blob);
+
+ auth_user = ntlm_request->user();
+ assert(auth_user != NULL);
+ assert(auth_user->auth_type == AUTH_NTLM);
+ ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
+
+ assert(ntlm_user != NULL);
+
+ if (ntlm_request->authserver == NULL)
+ ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(ntlm_request->authserver == lastserver);
+
/* seperate out the useful data */
+ blob = strchr(reply, ' ');
+
+ if (blob)
+ blob++;
+
if (strncasecmp(reply, "TT ", 3) == 0) {
- reply += 3;
- /* we have been given a Challenge */
- /* we should check we weren't given an empty challenge */
- /* copy the challenge to the state data */
- helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(static_cast<helper_stateful_server *>(lastserver)));
- if (helperstate == NULL)
- fatal("lost NTLM helper state! quitting\n");
- helperstate->challenge = xstrndup(reply, NTLM_CHALLENGE_SZ + 5);
- helperstate->challengeuses = 0;
- helperstate->renewed = squid_curtime;
- /* and we satisfy the request that happended on the refresh boundary */
- /* note this code is now in two places FIXME */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- assert(ntlm_request != NULL);
- result = S_HELPER_DEFER;
- /* reserve the server for future authentication */
- ntlm_request->authserver_deferred = 1;
- debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver);
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
- ntlm_request->authserver = static_cast<helper_stateful_server *>(lastserver);
- ntlm_request->authchallenge = xstrndup(reply, NTLM_CHALLENGE_SZ + 5);
+ /* 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->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+ auth_user_request->denyMessage("Authentication in progress");
+ debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
+ result = S_HELPER_RESERVE;
+ } else {
+ ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+ auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+ result = S_HELPER_RELEASE;
+ }
} else if (strncasecmp(reply, "AF ", 3) == 0) {
- /* we're finished, release the helper */
- reply += 3;
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- assert(auth_user_request->scheme_data != NULL);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- auth_user = auth_user_request->auth_user;
- ntlm_user = static_cast<ntlm_user_t *>(auth_user_request->auth_user->scheme_data);
- assert(ntlm_user != NULL);
- result = S_HELPER_RELEASE;
- /* we only expect OK when finishing the handshake */
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
- ntlm_user->username = xstrndup(reply, MAX_LOGIN_SZ);
- ntlm_request->authserver = NULL;
-#ifdef NTLM_FAIL_OPEN
- } else if (strncasecmp(reply, "LD ", 3) == 0) {
- /* This is a variant of BH, which rather than deny access
- * allows the user through. The helper is starved and then refreshed
- * via YR, all pending authentications are likely to fail also.
- * It is meant for those helpers which occasionally fail for
- * no reason at all (casus belli, NTLMSSP helper on NT domain,
- * failing about 1 auth out of 1k.
- * The code is a merge from the BH case with snippets of the AF
- * case */
- /* AF code: mark user as authenticated */
- reply += 3;
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- assert(auth_user_request->scheme_data != NULL);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- auth_user = auth_user_request->auth_user;
- ntlm_user = static_cast<ntlm_user_t *>(auth_user_request->auth_user->scheme_data);
- assert(ntlm_user != NULL);
- result = S_HELPER_RELEASE;
- /* we only expect LD when finishing the handshake */
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
- ntlm_user->username = xstrndup(reply, MAX_LOGIN_SZ);
- helperstate = helperStatefulServerGetData(ntlm_request->authserver);
- ntlm_request->authserver = NULL;
- /* BH code: mark helper as broken */
- /* mark it for starving */
- helperstate->starve = 1;
-#endif
+ /* we're finished, release the helper */
+ ntlm_user->username(blob);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(ntlm_request->server_blob);
+
+ result = S_HELPER_RELEASE;
+ debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
+ /* connection is authenticated */
+ debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_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, ntlm_user->username()));
+ AuthUser *local_auth_user = ntlm_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 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 */
+ usernamehash->user()->absorb(local_auth_user);
+ //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
+ local_auth_user = usernamehash->user();
+ ntlm_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ // authenticateUserNameCacheAdd(local_auth_user);
+ }
+ /* 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;
+ authenticateNTLMReleaseServer(ntlm_request);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
} else if (strncasecmp(reply, "NA ", 3) == 0) {
- /* TODO: only work with auth_user here if it exists */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->auth_user;
- assert(auth_user != NULL);
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- /* todo: action of Negotiate state on error */
- result = S_HELPER_RELEASE; /*some error has occured. no more requests */
- ntlm_request->authserver = NULL;
- debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- if ((t = strchr(reply, ' '))) /* strip after a space */
- *t = '\0';
- } else if (strncasecmp(reply, "NA", 2) == 0) {
- /* NTLM Helper protocol violation! */
- fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
+ /* authentication failure (wrong password, etc.) */
+ auth_user_request->denyMessage(blob);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+ safe_free(ntlm_request->server_blob);
+ authenticateNTLMReleaseServer(ntlm_request);
+ result = S_HELPER_RELEASE;
+ debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
} else if (strncasecmp(reply, "BH ", 3) == 0) {
- /* 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. */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->auth_user;
- assert(auth_user != NULL);
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- result = S_HELPER_RELEASE; /*some error has occured. no more requests for
- * this helper */
- assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
- helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
- ntlm_request->authserver = NULL;
- if (ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE) {
- /* The helper broke on YR. It automatically
- * resets */
- debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver, reply);
- /* mark it for starving */
- helperstate->starve = 1;
- /* resubmit the request. This helper is currently busy, so we will get
- * a different one. Our auth state stays the same */
- authenticateNTLMStart(auth_user_request, r->handler, r->data);
- /* don't call the callback */
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
- return result;
- }
- /* the helper broke on a KK */
- /* first the standard KK stuff */
- debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
- if ((t = strchr(reply, ' '))) /* strip after a space */
- *t = '\0';
- /* now we mark the helper for resetting. */
- helperstate->starve = 1;
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+ /* 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);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+ safe_free(ntlm_request->server_blob);
+ authenticateNTLMReleaseServer(ntlm_request);
+ result = S_HELPER_RELEASE;
+ debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
} else {
- /* TODO: only work with auth_user here if it exists */
- /* TODO: take the request state into consideration */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->auth_user;
- assert(auth_user != NULL);
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
- /* restart the authentication process */
- ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
- assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
- ntlm_request->authserver = NULL;
+ /* protocol error */
+ fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ if (ntlm_request->request) {
+ HTTPMSGUNLOCK(ntlm_request->request);
+ ntlm_request->request = NULL;
}
r->handler(r->data, NULL);
cbdataReferenceDone(r->data);
authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
+ debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << result);
return result;
}
static void
authenticateNTLMStats(StoreEntry * sentry)
{
- storeAppendPrintf(sentry, "NTLM Authenticator Statistics:\n");
- helperStatefulStats(sentry, ntlmauthenticators);
+ helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics");
}
-/* is a particular challenge still valid ? */
-static int
-authenticateNTLMValidChallenge(ntlm_helper_state_t * helperstate)
-{
- debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate->challenge ? "Valid" : "Invalid");
- if (helperstate->challenge == NULL)
- return 0;
- return 1;
-}
-
-/* does our policy call for changing the challenge now? */
-static int
-authenticateNTLMChangeChallenge_p(ntlm_helper_state_t * helperstate)
-{
- /* don't check for invalid challenges just for expiry choices */
- /* this is needed because we have to starve the helper until all old
- * requests have been satisfied */
- if (!helperstate->renewed) {
- /* first use, no challenge has been set. Without this check, it will
- * loop forever */
- debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
- return 0;
- }
- if (helperstate->challengeuses > ntlmConfig->challengeuses) {
- debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate->challengeuses, ntlmConfig->challengeuses);
- return 1;
- }
- if (helperstate->renewed + ntlmConfig->challengelifetime < squid_curtime) {
- debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime - (helperstate->renewed + ntlmConfig->challengelifetime)));
- return 1;
- }
- debug(29, 9) ("Challenge is to be reused\n");
- return 0;
-}
/* send the initial data to a stateful ntlm authenticator module */
-static void
-authenticateNTLMStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
+void
+AuthNTLMUserRequest::module_start(RH * handler, void *data)
{
authenticateStateData *r = NULL;
- helper_stateful_server *server;
- ntlm_helper_state_t *helperstate;
- char buf[8192];
- char *sent_string = NULL;
+ static char buf[8192];
ntlm_user_t *ntlm_user;
- ntlm_request_t *ntlm_request;
- auth_user_t *auth_user;
-
- assert(auth_user_request);
- auth_user = auth_user_request->auth_user;
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- assert(ntlm_user);
- assert(ntlm_request);
- assert(handler);
+ AuthUser *auth_user = user();
+
assert(data);
- assert(auth_user->auth_type = AUTH_NTLM);
- debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", ntlm_request->auth_state);
- switch (ntlm_request->auth_state) {
- case AUTHENTICATE_STATE_NEGOTIATE:
- sent_string = ntlm_request->ntlmnegotiate;
- break;
- case AUTHENTICATE_STATE_RESPONSE:
- sent_string = ntlm_request->ntlmauthenticate;
- assert(ntlm_request->authserver);
- debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", ntlm_request->authserver);
- break;
- default:
- fatal("Invalid authenticate state for NTLMStart");
- }
+ assert(handler);
+ assert(auth_user);
+ assert(auth_user->auth_type == AUTH_NTLM);
- while (!xisspace(*sent_string)) /*trim NTLM */
- sent_string++;
+ ntlm_user = dynamic_cast<ntlm_user_t *>(user());
- while (xisspace(*sent_string)) /*trim leading spaces */
- sent_string++;
+ debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
- debug(29, 9) ("authenticateNTLMStart: state '%d'\n", ntlm_request->auth_state);
- debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
- if (ntlmConfig->authenticate == NULL) {
- debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string);
- handler(data, NULL);
- return;
- }
- /* this is ugly TODO: move the challenge generation routines to their own function and
- * tidy the logic up to make use of the efficiency we now have */
- switch (ntlm_request->auth_state) {
- case AUTHENTICATE_STATE_NEGOTIATE:
- /*
- * 1: get a helper server
- * 2: does it have a challenge?
- * 3: tell it to get a challenge, or give ntlmauthdone the challenge
- */
- server = helperStatefulDefer(ntlmauthenticators);
- helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
- while ((server != NULL) && authenticateNTLMChangeChallenge_p(helperstate)) {
- /* flag this helper for challenge changing */
- helperstate->starve = 1;
- /* and release the deferred request */
- helperStatefulReleaseServer(server);
- /* Get another deferrable server */
- server = helperStatefulDefer(ntlmauthenticators);
- helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
- }
- if (server == NULL)
- debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
-
- ntlm_request->authserver = server;
- /* tell the log what helper we have been given */
- debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server);
- /* server and valid challenge? */
- if ((server == NULL) || !authenticateNTLMValidChallenge(helperstate)) {
- /* No server, or server with invalid challenge */
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = auth_user_request;
- if (server == NULL) {
- helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, NULL);
- } else {
- /* Server with invalid challenge */
- snprintf(buf, 8192, "YR\n");
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authserver);
- }
- } else {
- /* (server != NULL and we have a valid challenge) */
- /* TODO: turn the below into a function and call from here and handlereply */
- /* increment the challenge uses */
- helperstate->challengeuses++;
- /* assign the challenge */
- ntlm_request->authchallenge = xstrndup(helperstate->challenge, NTLM_CHALLENGE_SZ + 5);
- /* we're not actually submitting a request, so we need to release the helper
- * should the connection close unexpectedly
- */
- ntlm_request->authserver_deferred = 1;
- handler(data, NULL);
- }
-
- break;
- case AUTHENTICATE_STATE_RESPONSE:
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = auth_user_request;
- snprintf(buf, 8192, "KK %s\n", sent_string);
- /* getting rid of deferred request status */
- ntlm_request->authserver_deferred = 0;
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authserver);
- debug(29, 9) ("authenticateNTLMstart: finished\n");
- break;
- default:
- fatal("Invalid authenticate state for NTLMStart");
+ if (ntlmConfig.authenticate == NULL) {
+ debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
+ handler(data, NULL);
+ return;
}
-}
-/* callback used by stateful helper routines */
-static int
-authenticateNTLMHelperServerAvailable(void *data)
-{
- ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
- if (statedata != NULL) {
- if (statedata->starve) {
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
- return 0;
- } else {
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
- return 1;
- }
- }
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
- return 0;
-}
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = this;
+ AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
-static void
-authenticateNTLMHelperServerOnEmpty(void *data)
-{
- ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
- if (statedata == NULL)
- return;
- if (statedata->starve) {
- /* we have been starving the helper */
- debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
- statedata->starve = 0;
- statedata->challengeuses = 0;
- statedata->renewed = 0;
- xfree(statedata->challenge);
- statedata->challenge = NULL;
+ if (auth_state == AUTHENTICATE_STATE_INITIAL) {
+ snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ } else {
+ snprintf(buf, 8192, "KK %s\n", client_blob);
}
-}
+ waiting = 1;
+
+ safe_free(client_blob);
+ helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
+}
/* clear the NTLM helper of being reserved for future requests */
static void
-authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
+authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request)
{
- ntlm_request_t *ntlm_request;
- assert(auth_user_request->auth_user->auth_type == AUTH_NTLM);
- assert(auth_user_request->scheme_data != NULL);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver);
- helperStatefulReleaseServer(ntlm_request->authserver);
- ntlm_request->authserver = NULL;
+ AuthNTLMUserRequest *ntlm_request;
+ assert(auth_user_request->user()->auth_type == AUTH_NTLM);
+ ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
+ debugs(29, 9, "authenticateNTLMReleaseServer: releasing server '" << ntlm_request->authserver << "'");
+ /* is it possible for the server to be NULL? hno seems to think so.
+ * Let's see what happens, might segfault in helperStatefulReleaseServer
+ * if it does. I leave it like this not to cover possibly problematic
+ * code-paths. Kinkie */
+ /* DPW 2007-05-07
+ * yes, it is possible */
+ assert(ntlm_request != NULL);
+ if (ntlm_request->authserver) {
+ helperStatefulReleaseServer(ntlm_request->authserver);
+ ntlm_request->authserver = NULL;
+ }
}
/* clear any connection related authentication details */
-static void
-authenticateNTLMOnCloseConnection(ConnStateData * conn)
+void
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
{
- ntlm_request_t *ntlm_request;
assert(conn != NULL);
- if (conn->auth_user_request != NULL) {
- assert(conn->auth_user_request->scheme_data != NULL);
- ntlm_request = static_cast< ntlm_request_t *>(conn->auth_user_request->scheme_data);
- assert(ntlm_request->conn == conn);
- if (ntlm_request->authserver != NULL && ntlm_request->authserver_deferred)
- authenticateNTLMReleaseServer(conn->auth_user_request);
- /* unlock the connection based lock */
- debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
- /* This still breaks the abstraction, but is at least read only now */
- /* Ensure that the auth user request will be getting closed */
- /* IFF we start persisting the struct after the conn closes - say for logging
- * then this test may become invalid
- */
- assert(authenticateRequestRefCount(conn->auth_user_request) == 1);
- authenticateAuthUserRequestUnlock(conn->auth_user_request);
- conn->auth_user_request = 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;
}
-}
-/* authenticateUserUsername: return a pointer to the username in the */
-static const char *
-authenticateNTLMUsername(auth_user_t const * auth_user)
-{
- ntlm_user_t *ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- if (ntlm_user)
- return ntlm_user->username;
- return NULL;
-}
+ if (authserver != NULL)
+ authenticateNTLMReleaseServer(this);
-/* NTLMLastHeader: return a pointer to the last header used in authenticating
- * the request/conneciton
- */
-static const char *
-NTLMLastHeader(auth_user_request_t * auth_user_request)
-{
- ntlm_request_t *ntlm_request;
- assert(auth_user_request != NULL);
- assert(auth_user_request->scheme_data != NULL);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- return ntlm_request->ntlmauthenticate;
+ /* unlock the connection based lock */
+ debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
+
+ AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
}
/*
- * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
+ * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
-
-static void
-authenticateDecodeNTLMAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
+AuthUserRequest *
+AuthNTLMConfig::decode(char const *proxy_auth)
{
- dlink_node *node;
- assert(auth_user_request->auth_user == NULL);
- auth_user_request->auth_user = authenticateAuthUserNew("ntlm");
- auth_user_request->auth_user->auth_type = AUTH_NTLM;
- auth_user_request->auth_user->scheme_data = memPoolAlloc(ntlm_user_pool);
- auth_user_request->scheme_data = memPoolAlloc(ntlm_request_pool);
- memset(auth_user_request->scheme_data, '\0', sizeof(ntlm_request_t));
- /* lock for the auth_user_request link */
- authenticateAuthUserLock(auth_user_request->auth_user);
- node = dlinkNodeNew();
- dlinkAdd(auth_user_request, node, &auth_user_request->auth_user->requests);
+ NTLMUser *newUser = new NTLMUser(&ntlmConfig);
+ AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest ();
+ assert(auth_user_request->user() == NULL);
+ auth_user_request->user(newUser);
+ auth_user_request->user()->auth_type = AUTH_NTLM;
+ auth_user_request->user()->addRequest(auth_user_request);
/* all we have to do is identify that it's NTLM - the helper does the rest */
- debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
- return;
+ debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
+ return auth_user_request;
}
-static int
-authenticateNTLMcmpUsername(ntlm_user_t * u1, ntlm_user_t * u2)
+int
+AuthNTLMUserRequest::authenticated() const
{
- return strcmp(u1->username, u2->username);
-}
-
-
-/* there is a known race where a single client recieves the same challenge
- * and sends the same response to squid on a single select cycle.
- * Check for this and if found ignore the new link
- */
-static void
-authenticateProxyAuthCacheAddLink(const char *key, auth_user_t * auth_user)
-{
- struct ProxyAuthCachePointer *proxy_auth_hash;
- dlink_node *node;
- ntlm_user_t *ntlm_user;
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- node = ntlm_user->proxy_auth_list.head;
- /* prevent duplicates */
- while (node) {
- if (!strcmp(key, (char const *)((struct ProxyAuthCachePointer *) node->data)->key))
- return;
- node = node->next;
+ if (auth_state == AUTHENTICATE_STATE_DONE) {
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
+ return 1;
}
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(memPoolAlloc(ntlm_user_hash_pool));
- proxy_auth_hash->key = xstrdup(key);
- proxy_auth_hash->auth_user = auth_user;
- dlinkAddTail(proxy_auth_hash, &proxy_auth_hash->link, &ntlm_user->proxy_auth_list);
- hash_join(proxy_auth_cache, (hash_link *) proxy_auth_hash);
-}
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
-static int
-authNTLMAuthenticated(auth_user_request_t * auth_user_request)
-{
- ntlm_request_t *ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
- if (ntlm_request->auth_state == AUTHENTICATE_STATE_DONE)
- return 1;
- debug(29, 9) ("User not fully authenticated.\n");
return 0;
}
-static void
-authenticateNTLMAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
+void
+AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
{
- const char *proxy_auth;
- struct ProxyAuthCachePointer *proxy_auth_hash = NULL;
- auth_user_hash_pointer *usernamehash;
- auth_user_t *auth_user;
- ntlm_request_t *ntlm_request;
+ const char *proxy_auth, *blob;
+
+ /* TODO: rename this!! */
+ AuthUser *local_auth_user;
ntlm_user_t *ntlm_user;
- LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
- /* get header */
- proxy_auth = httpHeaderGetStr(&request->header, type);
- auth_user = auth_user_request->auth_user;
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NTLM);
- assert(auth_user->scheme_data != NULL);
- assert(auth_user_request->scheme_data != NULL);
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- ntlm_request = static_cast< ntlm_request_t *>(auth_user_request->scheme_data);
+ local_auth_user = user();
+ assert(local_auth_user);
+ assert(local_auth_user->auth_type == AUTH_NTLM);
+ ntlm_user = dynamic_cast<ntlm_user_t *>(local_auth_user);
+ assert (this);
+
/* Check that we are in the client side, where we can generate
* auth challenges */
- if (!conn) {
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
- return;
+
+ if (conn == NULL || !cbdataReferenceValid(conn)) {
+ auth_state = AUTHENTICATE_STATE_FAILED;
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
+ return;
+ }
+
+ if (waiting) {
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
+ return;
+ }
+
+ if (server_blob) {
+ debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
+ return;
+ }
+
+ /* get header */
+ proxy_auth = request->header.getStr(type);
+
+ /* locate second word */
+ blob = proxy_auth;
+
+ /* if proxy_auth is actually NULL, we'd better not manipulate it. */
+ if (blob) {
+ while (xisspace(*blob) && *blob)
+ blob++;
+
+ while (!xisspace(*blob) && *blob)
+ blob++;
+
+ while (xisspace(*blob) && *blob)
+ blob++;
}
-
- switch (ntlm_request->auth_state) {
+
+ switch (auth_state) {
+
case AUTHENTICATE_STATE_NONE:
- /* we've recieved a negotiate request. pass to a helper */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth);
- ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
- ntlm_request->ntlmnegotiate = xstrndup(proxy_auth, NTLM_CHALLENGE_SZ + 5);
- conn->auth_type = AUTH_NTLM;
- conn->auth_user_request = auth_user_request;
- ntlm_request->conn = conn;
- /* and lock for the connection duration */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
- authenticateAuthUserRequestLock(auth_user_request);
- return;
- break;
- case AUTHENTICATE_STATE_NEGOTIATE:
- ntlm_request->auth_state = AUTHENTICATE_STATE_CHALLENGE;
- /* We _MUST_ have the auth challenge by now */
- assert(ntlm_request->authchallenge);
- return;
- break;
- case AUTHENTICATE_STATE_CHALLENGE:
- /* we should have recieved a NTLM challenge. pass it to the same
- * helper process */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
- /* do a cache lookup here. If it matches it's a successful ntlm
- * challenge - release the helper and use the existing auth_user
- * details. */
- if (strncmp("NTLM ", proxy_auth, 5) == 0) {
- ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
- } else {
- fatal("Incorrect scheme in auth header\n");
- /* TODO: more fault tolerance.. reset the auth scheme here */
- }
- /* cache entries have authenticateauthheaderchallengestring */
- snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
- ntlm_request->ntlmauthenticate,
- ntlm_request->authchallenge);
- /* see if we already know this user's authenticate */
- debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
- assert(proxy_auth_cache != NULL);
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(hash_lookup(proxy_auth_cache, ntlmhash));
- if (!proxy_auth_hash) { /* not in the hash table */
- debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
- ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
- /* verify with the ntlm helper */
- } else {
- debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
- /* throw away the temporary entry */
- ntlm_request->authserver_deferred = 0;
- authenticateNTLMReleaseServer(auth_user_request);
- authenticateAuthUserMerge(auth_user, proxy_auth_hash->auth_user);
- auth_user = proxy_auth_hash->auth_user;
- auth_user_request->auth_user = auth_user;
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- /* we found one */
- debug(29, 9) ("found matching cache entry\n");
- assert(auth_user->auth_type == AUTH_NTLM);
- /* get the existing entries details */
- ntlm_user = static_cast<ntlm_user_t *>(auth_user->scheme_data);
- debug(29, 9) ("Username to be used is %s\n", ntlm_user->username);
- /* on ntlm auth we do not unlock the auth_user until the
- * connection is dropped. Thank MS for this quirk */
- auth_user->expiretime = current_time.tv_sec;
- }
- return;
- break;
- case AUTHENTICATE_STATE_RESPONSE:
- /* auth-challenge pair cache miss. We've just got the response from the helper */
- /*add to cache and let them through */
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- /* this connection is authenticated */
- debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
- ntlm_request->authchallenge,
- ntlm_request->ntlmauthenticate,
- ntlm_user->username);
- /* cache entries have authenticateauthheaderchallengestring */
- snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
- ntlm_request->ntlmauthenticate,
- ntlm_request->authchallenge);
- /* see if this is an existing user with a different proxy_auth
- * string */
- if ((usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username)))) {
- while ((authUserHashPointerUser(usernamehash)->auth_type != auth_user->auth_type) && (usernamehash->next) && !authenticateNTLMcmpUsername(static_cast<ntlm_user_t *>(authUserHashPointerUser(usernamehash)->scheme_data), ntlm_user))
- usernamehash = static_cast<AuthUserHashPointer*>(usernamehash->next);
- if (authUserHashPointerUser(usernamehash)->auth_type == auth_user->auth_type) {
- /*
- * add another link from the new proxy_auth to the
- * auth_user structure and update the information */
- assert(proxy_auth_hash == NULL);
- authenticateProxyAuthCacheAddLink(ntlmhash, authUserHashPointerUser(usernamehash));
- /* we can't seamlessly recheck the username due to the
- * challenge nature of the protocol. Just free the
- * temporary auth_user */
- authenticateAuthUserMerge(auth_user, authUserHashPointerUser(usernamehash));
- auth_user = authUserHashPointerUser(usernamehash);
- auth_user_request->auth_user = auth_user;
- }
- } else {
- /* store user in hash's */
- authenticateUserNameCacheAdd(auth_user);
- authenticateProxyAuthCacheAddLink(ntlmhash, auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- auth_user->expiretime = current_time.tv_sec;
- return;
- break;
+ /* we've received a ntlm request. pass to a helper */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
+ auth_state = AUTHENTICATE_STATE_INITIAL;
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ conn->auth_type = AUTH_NTLM;
+ assert(conn->auth_user_request == NULL);
+ conn->auth_user_request = this;
+ AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
+ this->request = request;
+ HTTPMSGLOCK(this->request);
+ return;
+
+ break;
+
+ case AUTHENTICATE_STATE_INITIAL:
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
+
+ return;
+
+ break;
+
+
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
+
+ client_blob = xstrdup (blob);
+
+ if (this->request)
+ HTTPMSGUNLOCK(this->request);
+ this->request = request;
+ HTTPMSGLOCK(this->request);
+ return;
+
+ break;
+
case AUTHENTICATE_STATE_DONE:
- fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
- break;
+ fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
+
+ break;
+
case AUTHENTICATE_STATE_FAILED:
- /* we've failed somewhere in authentication */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth);
- return;
+ /* we've failed somewhere in authentication */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
+
+ return;
+
+ break;
}
+
return;
}
+
+AuthNTLMUserRequest::AuthNTLMUserRequest() :
+ /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
+ _theUser(NULL)
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+ request = NULL;
+}
+
+AuthNTLMUserRequest::~AuthNTLMUserRequest()
+{
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ if (authserver != NULL) {
+ debugs(29, 9, "AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ }
+ if (request) {
+ HTTPMSGUNLOCK(request);
+ request = NULL;
+ }
+}
+
+void
+NTLMUser::deleteSelf() const
+{
+ delete this;
+}
+
+NTLMUser::NTLMUser (AuthConfig *config) : AuthUser (config)
+{
+ proxy_auth_list.head = proxy_auth_list.tail = NULL;
+}
+
+AuthConfig *
+ntlmScheme::createConfig()
+{
+ return &ntlmConfig;
+}
+
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+ return NULL;
+}
+