From: Amos Jeffries Date: Fri, 12 Feb 2010 10:51:58 +0000 (+1300) Subject: Unwind UserRequest child objects. Unify duplicate authenticateStateData code. X-Git-Tag: SQUID_3_2_0_1~167^2~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=928f342199ca686669f658f774a9bb7ab16314e3;p=thirdparty%2Fsquid.git Unwind UserRequest child objects. Unify duplicate authenticateStateData code. --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index 1abd128080..becf34eba6 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -19,7 +19,9 @@ libauth_la_SOURCES = \ UserRequest.h \ UserRequest.cc \ Gadgets.cc \ - Gadgets.h + Gadgets.h \ + State.h \ + State.cc libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD) libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD) @@ -32,32 +34,39 @@ libacls_la_SOURCES = \ AclMaxUserIp.cc \ AclMaxUserIp.h \ AclProxyAuth.cc \ - AclProxyAuth.h - + AclProxyAuth.h libbasic_la_SOURCES = \ basic/basicScheme.cc \ basic/basicScheme.h \ basic/auth_basic.cc \ - basic/auth_basic.h + basic/auth_basic.h \ + basic/basicUserRequest.cc \ + basic/basicUserRequest.h libdigest_la_SOURCES = \ digest/digestScheme.cc \ digest/digestScheme.h \ digest/auth_digest.cc \ - digest/auth_digest.h + digest/auth_digest.h \ + digest/digestUserRequest.cc \ + digest/digestUserRequest.h libntlm_la_SOURCES = \ ntlm/ntlmScheme.cc \ ntlm/ntlmScheme.h \ ntlm/auth_ntlm.cc \ - ntlm/auth_ntlm.h + ntlm/auth_ntlm.h \ + ntlm/ntlmUserRequest.cc \ + ntlm/ntlmUserRequest.h libnegotiate_la_SOURCES = \ negotiate/negotiateScheme.cc \ negotiate/negotiateScheme.h \ negotiate/auth_negotiate.cc \ - negotiate/auth_negotiate.h + negotiate/auth_negotiate.h \ + negotiate/negotiateUserRequest.cc \ + negotiate/negotiateUserRequest.h TESTS += testHeaders diff --git a/src/auth/Scheme.h b/src/auth/Scheme.h index c183f7703f..12370ed4af 100644 --- a/src/auth/Scheme.h +++ b/src/auth/Scheme.h @@ -33,22 +33,24 @@ #ifndef SQUID_AUTHSCHEME_H #define SQUID_AUTHSCHEME_H -#include "squid.h" +#include "config.h" #include "Array.h" #include "RefCount.h" +class AuthConfig; + /** \defgroup AuthSchemeAPI Authentication Scheme API \ingroup AuthAPI */ /** - \ingroup AuthAPI - \ingroup AuthSchemeAPI - \par + * \ingroup AuthAPI + * \ingroup AuthSchemeAPI + * \par * I represent an authentication scheme. For now my children - * store both the scheme metadata, and the scheme configuration. - \par + * store the scheme metadata. + * \par * Should we need multiple configs of a single scheme, * a new class AuthConfiguration should be made, and the * config specific calls on AuthScheme moved to it. @@ -57,17 +59,29 @@ class AuthScheme : public RefCountable { public: typedef RefCount Pointer; + typedef Vector::iterator iterator; + typedef Vector::const_iterator const_iterator; public: AuthScheme() : initialised (false) {}; virtual ~AuthScheme() {}; static void AddScheme(AuthScheme::Pointer); + + /** + * Final termination of all authentication components. + * To be used only on shutdown. All global pointers are released. + * After this all schemes will appear completely unsupported + * until a call to InitAuthModules(). + * Release the Auth::TheConfig handles instead to disable authentication + * without terminiating all support. + */ static void FreeAll(); -// static Vector const &Schemes(); + + /** + * Locate an authentication scheme component by Name. + */ static AuthScheme::Pointer Find(const char *); - typedef Vector::iterator iterator; - typedef Vector::const_iterator const_iterator; /* per scheme methods */ virtual char const *type () const = 0; diff --git a/src/auth/State.cc b/src/auth/State.cc new file mode 100644 index 0000000000..375bd232ca --- /dev/null +++ b/src/auth/State.cc @@ -0,0 +1,11 @@ +#include "config.h" +#include "auth/State.h" + +CBDATA_GLOBAL_TYPE(authenticateStateData); + +void +authenticateStateFree(authenticateStateData * r) +{ + r->auth_user_request = NULL; + cbdataFree(r); +} diff --git a/src/auth/State.h b/src/auth/State.h new file mode 100644 index 0000000000..2d1f081c12 --- /dev/null +++ b/src/auth/State.h @@ -0,0 +1,27 @@ +#ifndef __AUTH_AUTHENTICATE_STATE_T__ +#define __AUTH_AUTHENTICATE_STATE_T__ + +#include "auth/UserRequest.h" + +typedef enum { + AUTHENTICATE_STATE_NONE, + AUTHENTICATE_STATE_INITIAL, + AUTHENTICATE_STATE_IN_PROGRESS, + AUTHENTICATE_STATE_DONE, + AUTHENTICATE_STATE_FAILED +} auth_state_t; /* connection level auth state */ + +/** + * CBDATA state for NTLM, Negotiate, and Digest stateful authentication. + */ +typedef struct { + void *data; + AuthUserRequest::Pointer auth_user_request; + RH *handler; +} authenticateStateData; + +extern CBDATA_GLOBAL_TYPE(authenticateStateData); + +extern void authenticateStateFree(authenticateStateData * r); + +#endif /* __AUTH_AUTHENTICATE_STATE_T__ */ diff --git a/src/auth/UserRequest.h b/src/auth/UserRequest.h index e97d9048e1..d95c2b5a31 100644 --- a/src/auth/UserRequest.h +++ b/src/auth/UserRequest.h @@ -58,6 +58,9 @@ struct AuthUserIP { /** \ingroup AuthAPI * This is a short lived structure is the visible aspect of the authentication framework. + * + * It and its children hold the state data while processing authentication for a client request. + * The AuthenticationStateData object is merely a CBDATA wrapper for one of these. */ class AuthUserRequest : public RefCountable { diff --git a/src/auth/basic/auth_basic.cc b/src/auth/basic/auth_basic.cc index 3a91b594b2..a5479c7b63 100644 --- a/src/auth/basic/auth_basic.cc +++ b/src/auth/basic/auth_basic.cc @@ -39,6 +39,7 @@ #include "squid.h" #include "auth/basic/auth_basic.h" +#include "auth/basic/basicUserRequest.h" #include "auth/Gadgets.h" #include "CacheManager.h" #include "Store.h" @@ -48,14 +49,7 @@ #include "wordlist.h" #include "SquidTime.h" -static void -authenticateStateFree(AuthenticateStateData * r) -{ - cbdataFree(r); -} - /* Basic Scheme */ - static HLPCB authenticateBasicHandleReply; static AUTHSSTATS authenticateBasicStats; @@ -97,12 +91,6 @@ AuthBasicConfig::type() const return basicScheme::GetInstance()->type(); } -AuthBasicUserRequest::AuthBasicUserRequest() -{} - -AuthBasicUserRequest::~AuthBasicUserRequest() -{} - bool BasicUser::authenticated() const @@ -115,76 +103,6 @@ BasicUser::authenticated() const return false; } -int -AuthBasicUserRequest::authenticated() const -{ - BasicUser const *basic_auth = dynamic_cast(user()); - - if (basic_auth && basic_auth->authenticated()) - return 1; - - return 0; -} - -/* log a basic user in - */ -void -AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) -{ - assert(user() != NULL); - - basic_data *basic_auth = dynamic_cast(user()); - - /* if the password is not ok, do an identity */ - - if (!basic_auth || basic_auth->flags.credentials_ok != 1) - return; - - /* are we about to recheck the credentials externally? */ - if ((basic_auth->credentials_checkedtime + static_cast(AuthConfig::Find("basic"))->credentialsTTL) <= squid_curtime) { - debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking"); - return; - } - - /* we have been through the external helper, and the credentials haven't expired */ - debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated"); - - /* Decode now takes care of finding the AuthUser struct in the cache */ - /* after external auth occurs anyway */ - basic_auth->expiretime = current_time.tv_sec; - - return; -} - -int -AuthBasicUserRequest::module_direction() -{ - /* null auth_user is checked for by authenticateDirection */ - basic_data *basic_auth = dynamic_cast(user()); - assert (basic_auth); - - switch (basic_auth->flags.credentials_ok) { - - case 0: /* not checked */ - return -1; - - case 1: /* checked & ok */ - - if (basic_auth->credentials_checkedtime + static_cast(AuthConfig::Find("basic"))->credentialsTTL <= squid_curtime) - return -1; - - return 0; - - case 2: /* paused while waiting for a username:password check on another request */ - return -1; - - case 3: /* authentication process failed. */ - return 0; - } - - return -2; -} - void AuthBasicConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request) { @@ -538,7 +456,7 @@ BasicUser::updateCached(BasicUser *from) AuthUserRequest::Pointer AuthBasicConfig::decode(char const *proxy_auth) { - AuthUserRequest::Pointer auth_user_request = new AuthBasicUserRequest(); + AuthUserRequest::Pointer auth_user_request = dynamic_cast(new AuthBasicUserRequest); /* decode the username */ /* trim BASIC from string */ @@ -627,35 +545,10 @@ BasicUser::queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler node->data = cbdataReference(data); } -/* send the initial data to a basic authenticator module */ -void -AuthBasicUserRequest::module_start(RH * handler, void *data) -{ - basic_data *basic_auth; - assert(user()->auth_type == AUTH_BASIC); - basic_auth = dynamic_cast(user()); - assert(basic_auth != NULL); - debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'"); - - if (static_cast(AuthConfig::Find("basic"))->authenticate == NULL) { - handler(data, NULL); - return; - } - - /* check to see if the auth_user already has a request outstanding */ - if (basic_auth->flags.credentials_ok == 2) { - /* there is a request with the same credentials already being verified */ - basic_auth->queueRequest(this, handler, data); - return; - } - - basic_auth->submitRequest (this, handler, data); -} - void BasicUser::submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data) { - /* mark the user as haveing verification in progress */ + /* mark the user as having verification in progress */ flags.credentials_ok = 2; AuthenticateStateData *r = NULL; char buf[8192]; diff --git a/src/auth/basic/auth_basic.h b/src/auth/basic/auth_basic.h index 2d88a98538..8c2c33baf3 100644 --- a/src/auth/basic/auth_basic.h +++ b/src/auth/basic/auth_basic.h @@ -79,25 +79,6 @@ MEMPROXY_CLASS_INLINE(BasicUser); typedef class BasicUser basic_data; -/* follows the http request around */ - -class AuthBasicUserRequest : public AuthUserRequest -{ - -public: - MEMPROXY_CLASS(AuthBasicUserRequest); - - AuthBasicUserRequest(); - virtual ~AuthBasicUserRequest(); - - virtual int authenticated() const; - virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type); - virtual int module_direction(); - virtual void module_start(RH *, void *); -}; - -MEMPROXY_CLASS_INLINE(AuthBasicUserRequest); - #include "HelperChildConfig.h" /* configuration runtime data */ diff --git a/src/auth/basic/basicUserRequest.cc b/src/auth/basic/basicUserRequest.cc new file mode 100644 index 0000000000..dc45db4cd6 --- /dev/null +++ b/src/auth/basic/basicUserRequest.cc @@ -0,0 +1,101 @@ +#include "config.h" +#include "auth/basic/basicUserRequest.h" +#include "SquidTime.h" + +#include "auth/basic/auth_basic.h" + +int +AuthBasicUserRequest::authenticated() const +{ + BasicUser const *basic_auth = dynamic_cast(user()); + + if (basic_auth && basic_auth->authenticated()) + return 1; + + return 0; +} + +/* log a basic user in + */ +void +AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) +{ + assert(user() != NULL); + + basic_data *basic_auth = dynamic_cast(user()); + + /* if the password is not ok, do an identity */ + + if (!basic_auth || basic_auth->flags.credentials_ok != 1) + return; + + /* are we about to recheck the credentials externally? */ + if ((basic_auth->credentials_checkedtime + static_cast(AuthConfig::Find("basic"))->credentialsTTL) <= squid_curtime) { + debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking"); + return; + } + + /* we have been through the external helper, and the credentials haven't expired */ + debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated"); + + /* Decode now takes care of finding the AuthUser struct in the cache */ + /* after external auth occurs anyway */ + basic_auth->expiretime = current_time.tv_sec; + + return; +} + +int +AuthBasicUserRequest::module_direction() +{ + /* null auth_user is checked for by authenticateDirection */ + basic_data *basic_auth = dynamic_cast(user()); + assert (basic_auth); + + switch (basic_auth->flags.credentials_ok) { + + case 0: /* not checked */ + return -1; + + case 1: /* checked & ok */ + + if (basic_auth->credentials_checkedtime + static_cast(AuthConfig::Find("basic"))->credentialsTTL <= squid_curtime) + return -1; + + return 0; + + case 2: /* paused while waiting for a username:password check on another request */ + return -1; + + case 3: /* authentication process failed. */ + return 0; + } + + return -2; +} + +/* send the initial data to a basic authenticator module */ +void +AuthBasicUserRequest::module_start(RH * handler, void *data) +{ + basic_data *basic_auth; + assert(user()->auth_type == AUTH_BASIC); + basic_auth = dynamic_cast(user()); + assert(basic_auth != NULL); + debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'"); + + if (static_cast(AuthConfig::Find("basic"))->authenticate == NULL) { + handler(data, NULL); + return; + } + + /* check to see if the auth_user already has a request outstanding */ + if (basic_auth->flags.credentials_ok == 2) { + /* there is a request with the same credentials already being verified */ + basic_auth->queueRequest(this, handler, data); + return; + } + + basic_auth->submitRequest (this, handler, data); +} + diff --git a/src/auth/basic/basicUserRequest.h b/src/auth/basic/basicUserRequest.h new file mode 100644 index 0000000000..7e1975ad25 --- /dev/null +++ b/src/auth/basic/basicUserRequest.h @@ -0,0 +1,29 @@ +#ifndef _SQUID_SRC_AUTH_BASIC_USERREQUEST_H +#define _SQUID_SRC_AUTH_BASIC_USERREQUEST_H + +#include "MemPool.h" +#include "auth/UserRequest.h" + +class ConnStateData; +class HttpRequest; + +/* follows the http request around */ + +class AuthBasicUserRequest : public AuthUserRequest +{ + +public: + MEMPROXY_CLASS(AuthBasicUserRequest); + + AuthBasicUserRequest() {}; + virtual ~AuthBasicUserRequest() {}; + + virtual int authenticated() const; + virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type); + virtual int module_direction(); + virtual void module_start(RH *, void *); +}; + +MEMPROXY_CLASS_INLINE(AuthBasicUserRequest); + +#endif /* _SQUID_SRC_AUTH_BASIC_USERREQUEST_H */ diff --git a/src/auth/digest/auth_digest.cc b/src/auth/digest/auth_digest.cc index 5378ce027f..7f698d25c2 100644 --- a/src/auth/digest/auth_digest.cc +++ b/src/auth/digest/auth_digest.cc @@ -50,21 +50,19 @@ #include "SquidTime.h" /* TODO don't include this */ #include "auth/digest/digestScheme.h" +#include "auth/digest/digestUserRequest.h" /* Digest Scheme */ -static HLPCB authenticateDigestHandleReply; static AUTHSSTATS authenticateDigestStats; -static helper *digestauthenticators = NULL; +helper *digestauthenticators = NULL; static hash_table *digest_nonce_cache; static int authdigest_initialised = 0; static MemAllocator *digest_nonce_pool = NULL; -CBDATA_TYPE(DigestAuthenticateStateData); - /* * * Nonce Functions @@ -78,13 +76,9 @@ static void authenticateDigestNonceDelete(digest_nonce_h * nonce); static void authenticateDigestNonceSetup(void); static void authenticateDigestNonceShutdown(void); static void authenticateDigestNonceReconfigure(void); -static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce); -static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]); static int authDigestNonceIsStale(digest_nonce_h * nonce); static void authDigestNonceEncode(digest_nonce_h * nonce); -static int authDigestNonceLastRequest(digest_nonce_h * nonce); static void authDigestNonceLink(digest_nonce_h * nonce); -static void authDigestNonceUnlink(digest_nonce_h * nonce); #if NOT_USED static int authDigestNonceLinks(digest_nonce_h * nonce); #endif @@ -295,7 +289,7 @@ authDigestNonceLinks(digest_nonce_h * nonce) #endif -static void +void authDigestNonceUnlink(digest_nonce_h * nonce) { assert(nonce != NULL); @@ -312,8 +306,8 @@ authDigestNonceUnlink(digest_nonce_h * nonce) authenticateDigestNonceDelete(nonce); } -static const char * -authenticateDigestNonceNonceb64(digest_nonce_h * nonce) +const char * +authenticateDigestNonceNonceb64(const digest_nonce_h * nonce) { if (!nonce) return NULL; @@ -341,7 +335,7 @@ authenticateDigestNonceFindNonce(const char *nonceb64) return nonce; } -static int +int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]) { unsigned long intnc; @@ -415,8 +409,11 @@ authDigestNonceIsStale(digest_nonce_h * nonce) return 0; } -/* return -1 if the digest will be stale on the next request */ -static int +/** + * \retval 0 the digest is not stale yet + * \retval -1 the digest will be stale on the next request + */ +const int authDigestNonceLastRequest(digest_nonce_h * nonce) { if (!nonce) @@ -558,216 +555,6 @@ AuthDigestConfig::configured() const return false; } -int -AuthDigestUserRequest::authenticated() const -{ - if (credentials() == Ok) - return 1; - - return 0; -} - -/** log a digest user in - */ -void -AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) -{ - AuthDigestUserRequest *digest_request; - digest_user_h *digest_user; - - HASHHEX SESSIONKEY; - HASHHEX HA2 = ""; - HASHHEX Response; - - assert(user() != NULL); - AuthUser *auth_user = user(); - - digest_user = dynamic_cast(auth_user); - assert(digest_user != NULL); - - /* if the check has corrupted the user, just return */ - - if (credentials() == Failed) { - return; - } - - digest_request = this; - - /* do we have the HA1 */ - - if (!digest_user->HA1created) { - credentials(Pending); - return; - } - - if (digest_request->nonce == NULL) { - /* this isn't a nonce we issued */ - credentials(Failed); - return; - } - - DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL, - authenticateDigestNonceNonceb64(digest_request->nonce), - digest_request->cnonce, - digest_user->HA1, SESSIONKEY); - DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), - digest_request->nc, digest_request->cnonce, digest_request->qop, - RequestMethodStr(request->method), digest_request->uri, HA2, Response); - - debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'"); - - if (strcasecmp(digest_request->response, Response) != 0) { - if (!digest_request->flags.helper_queried) { - /* Query the helper in case the password has changed */ - digest_request->flags.helper_queried = 1; - digest_request->credentials_ok = Pending; - return; - } - - if (static_cast(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) { - /* Ugly workaround for certain very broken browsers using the - * wrong method to calculate the request-digest on POST request. - * This should be deleted once Digest authentication becomes more - * widespread and such broken browsers no longer are commonly - * used. - */ - DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), - digest_request->nc, digest_request->cnonce, digest_request->qop, - RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response); - - if (strcasecmp(digest_request->response, Response)) { - credentials(Failed); - digest_request->setDenyMessage("Incorrect password"); - return; - } else { - const char *useragent = request->header.getStr(HDR_USER_AGENT); - - static IpAddress last_broken_addr; - static int seen_broken_client = 0; - - if (!seen_broken_client) { - last_broken_addr.SetNoAddr(); - seen_broken_client = 1; - } - - if (last_broken_addr != request->client_addr) { - debugs(29, 1, "\nDigest POST bug detected from " << - request->client_addr << " using '" << - (useragent ? useragent : "-") << - "'. Please upgrade browser. See Bug #630 for details."); - - last_broken_addr = request->client_addr; - } - } - } else { - credentials(Failed); - digest_request->flags.invalid_password = 1; - digest_request->setDenyMessage("Incorrect password"); - return; - } - - /* check for stale nonce */ - if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) { - debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale"); - credentials(Failed); - digest_request->setDenyMessage("Stale nonce"); - return; - } - } - - credentials(Ok); - - /* password was checked and did match */ - debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK"); - - /* auth_user is now linked, we reset these values - * after external auth occurs anyway */ - auth_user->expiretime = current_time.tv_sec; - return; -} - -int -AuthDigestUserRequest::module_direction() -{ - switch (credentials()) { - - case Unchecked: - return -1; - - case Ok: - - return 0; - - case Pending: - return -1; - - case Failed: - - /* send new challenge */ - return 1; - } - - return -2; -} - -/* add the [proxy]authorisation header */ -void -AuthDigestUserRequest::addHeader(HttpReply * rep, int accel) -{ - http_hdr_type type; - - /* don't add to authentication error pages */ - - if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) - || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) - return; - - type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; - -#if WAITING_FOR_TE - /* test for http/1.1 transfer chunked encoding */ - if (chunkedtest) - return; - -#endif - - if ((static_cast(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) { - flags.authinfo_sent = 1; - debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); - httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); - } -} - -#if WAITING_FOR_TE -/* add the [proxy]authorisation header */ -void -AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel) -{ - int type; - - if (!auth_user_request) - return; - - - /* has the header already been send? */ - if (flags.authinfo_sent) - return; - - /* don't add to authentication error pages */ - if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) - || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) - return; - - type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; - - if ((static_cast(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) { - debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); - httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); - } -} - -#endif - /* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */ void AuthDigestConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request) @@ -813,51 +600,6 @@ DigestUser::~DigestUser() } } -static void -authenticateDigestHandleReply(void *data, char *reply) -{ - DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data); - char *t = NULL; - void *cbdata; - debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "") << "}"); - - if (reply) { - if ((t = strchr(reply, ' '))) - *t++ = '\0'; - - if (*reply == '\0' || *reply == '\n') - reply = NULL; - } - - assert(replyData->auth_user_request != NULL); - AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request; - - /* AYJ: 2009-12-12: allow this because the digest_request pointer is purely local */ - AuthDigestUserRequest *digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request.getRaw()); - assert(digest_request); - - digest_user_h *digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user()); - assert(digest_user != NULL); - - if (reply && (strncasecmp(reply, "ERR", 3) == 0)) { - digest_request->credentials(AuthDigestUserRequest::Failed); - digest_request->flags.invalid_password = 1; - - if (t && *t) - digest_request->setDenyMessage(t); - } else if (reply) { - CvtBin(reply, digest_user->HA1); - digest_user->HA1created = 1; - } - - if (cbdataReferenceValidDone(replyData->data, &cbdata)) - replyData->handler(cbdata, NULL); - - replyData->auth_user_request = NULL; - - cbdataFree(replyData); -} - /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the * config file */ void @@ -878,7 +620,7 @@ AuthDigestConfig::init(AuthConfig * scheme) helperOpenServers(digestauthenticators); - CBDATA_INIT_TYPE(DigestAuthenticateStateData); + CBDATA_INIT_TYPE(authenticateStateData); } } @@ -1345,73 +1087,5 @@ AuthDigestConfig::decode(char const *proxy_auth) return digest_request; } -/* send the initial data to a digest authenticator module */ -void -AuthDigestUserRequest::module_start(RH * handler, void *data) -{ - DigestAuthenticateStateData *r = NULL; - char buf[8192]; - digest_user_h *digest_user; - assert(user()->auth_type == AUTH_DIGEST); - digest_user = dynamic_cast(user()); - assert(digest_user != NULL); - debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'"); - - if (static_cast(AuthConfig::Find("digest"))->authenticate == NULL) { - handler(data, NULL); - return; - } - - r = cbdataAlloc(DigestAuthenticateStateData); - r->handler = handler; - r->data = cbdataReference(data); - r->auth_user_request = static_cast(this); - if (static_cast(AuthConfig::Find("digest"))->utf8) { - char userstr[1024]; - latin1_to_utf8(userstr, sizeof(userstr), digest_user->username()); - snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); - } else { - snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm); - } - - helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r); -} - DigestUser::DigestUser (AuthConfig *aConfig) : AuthUser (aConfig), HA1created (0) {} - -AuthDigestUserRequest::CredentialsState -AuthDigestUserRequest::credentials() const -{ - return credentials_ok; -} - -void -AuthDigestUserRequest::credentials(CredentialsState newCreds) -{ - credentials_ok = newCreds; -} - -AuthDigestUserRequest::AuthDigestUserRequest() : nonceb64(NULL) ,cnonce(NULL) ,realm(NULL), - pszPass(NULL) ,algorithm(NULL) ,pszMethod(NULL), - qop(NULL) ,uri(NULL) ,response(NULL), - nonce(NULL), - credentials_ok (Unchecked) -{} - -/** delete the digest request structure. Does NOT delete related structures */ -AuthDigestUserRequest::~AuthDigestUserRequest() -{ - safe_free (nonceb64); - safe_free (cnonce); - safe_free (realm); - safe_free (pszPass); - safe_free (algorithm); - safe_free (pszMethod); - safe_free (qop); - safe_free (uri); - safe_free (response); - - if (nonce) - authDigestNonceUnlink(nonce); -} diff --git a/src/auth/digest/auth_digest.h b/src/auth/digest/auth_digest.h index c5741ff7ec..8b021ddb2f 100644 --- a/src/auth/digest/auth_digest.h +++ b/src/auth/digest/auth_digest.h @@ -5,24 +5,17 @@ #ifndef __AUTH_DIGEST_H__ #define __AUTH_DIGEST_H__ -#include "rfc2617.h" + +#include "auth/Config.h" #include "auth/Gadgets.h" +#include "auth/State.h" #include "auth/User.h" #include "auth/UserRequest.h" -#include "auth/Config.h" #include "helper.h" +#include "rfc2617.h" /* Generic */ -class DigestAuthenticateStateData -{ - -public: - void *data; - AuthUserRequest::Pointer auth_user_request; - RH *handler; -}; - typedef struct _digest_nonce_data digest_nonce_data; typedef struct _digest_nonce_h digest_nonce_h; @@ -48,58 +41,6 @@ MEMPROXY_CLASS_INLINE(DigestUser); typedef class DigestUser digest_user_h; -/* the digest_request structure is what follows the http_request around */ - -class AuthDigestUserRequest : public AuthUserRequest -{ - -public: - enum CredentialsState {Unchecked, Ok, Pending, Failed}; - MEMPROXY_CLASS(AuthDigestUserRequest); - - AuthDigestUserRequest(); - virtual ~AuthDigestUserRequest(); - - virtual int authenticated() const; - virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); - virtual int module_direction(); - virtual void addHeader(HttpReply * rep, int accel); -#if WAITING_FOR_TE - - virtual void addTrailer(HttpReply * rep, int accel); -#endif - - virtual void module_start(RH *, void *); - - CredentialsState credentials() const; - void credentials(CredentialsState); - -// void authUser(AuthUser *); -// AuthUser *authUser() const; - - char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */ - char *cnonce; /* "0a4f113b" */ - char *realm; /* = "testrealm@host.com" */ - char *pszPass; /* = "Circle Of Life" */ - char *algorithm; /* = "md5" */ - char nc[9]; /* = "00000001" */ - char *pszMethod; /* = "GET" */ - char *qop; /* = "auth" */ - char *uri; /* = "/dir/index.html" */ - char *response; - - struct { - unsigned int authinfo_sent:1; - unsigned int invalid_password:1; - unsigned int helper_queried:1; - } flags; - digest_nonce_h *nonce; - -private: - CredentialsState credentials_ok; -}; - -MEMPROXY_CLASS_INLINE(AuthDigestUserRequest); /* data to be encoded into the nonce's b64 representation */ @@ -128,6 +69,11 @@ struct _digest_nonce_h : public hash_link { } flags; }; +extern void authDigestNonceUnlink(digest_nonce_h * nonce); +extern int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]); +extern const char *authenticateDigestNonceNonceb64(const digest_nonce_h * nonce); +extern const int authDigestNonceLastRequest(digest_nonce_h * nonce); + #include "HelperChildConfig.h" /* configuration runtime data */ @@ -164,4 +110,6 @@ typedef class AuthDigestConfig auth_digest_config; /* strings */ #define QOP_AUTH "auth" +extern helper *digestauthenticators; + #endif diff --git a/src/auth/digest/digestUserRequest.cc b/src/auth/digest/digestUserRequest.cc new file mode 100644 index 0000000000..de45320b3b --- /dev/null +++ b/src/auth/digest/digestUserRequest.cc @@ -0,0 +1,334 @@ +#include "config.h" +#include "auth/digest/auth_digest.h" +#include "auth/digest/digestUserRequest.h" +#include "auth/State.h" +#include "HttpReply.h" +#include "HttpRequest.h" +#include "SquidTime.h" + +AuthDigestUserRequest::AuthDigestUserRequest() : + nonceb64(NULL), + cnonce(NULL), + realm(NULL), + pszPass(NULL), + algorithm(NULL), + pszMethod(NULL), + qop(NULL), + uri(NULL), + response(NULL), + nonce(NULL), + credentials_ok(Unchecked) +{} + +/** + * Delete the digest request structure. + * Does NOT delete related AuthUser structures + */ +AuthDigestUserRequest::~AuthDigestUserRequest() +{ + safe_free(nonceb64); + safe_free(cnonce); + safe_free(realm); + safe_free(pszPass); + safe_free(algorithm); + safe_free(pszMethod); + safe_free(qop); + safe_free(uri); + safe_free(response); + + if (nonce) + authDigestNonceUnlink(nonce); +} + +AuthDigestUserRequest::CredentialsState +AuthDigestUserRequest::credentials() const +{ + return credentials_ok; +} + +void +AuthDigestUserRequest::credentials(CredentialsState newCreds) +{ + credentials_ok = newCreds; +} + +int +AuthDigestUserRequest::authenticated() const +{ + if (credentials() == Ok) + return 1; + + return 0; +} + +/** log a digest user in + */ +void +AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) +{ + AuthDigestUserRequest *digest_request; + digest_user_h *digest_user; + + HASHHEX SESSIONKEY; + HASHHEX HA2 = ""; + HASHHEX Response; + + assert(user() != NULL); + AuthUser *auth_user = user(); + + digest_user = dynamic_cast(auth_user); + assert(digest_user != NULL); + + /* if the check has corrupted the user, just return */ + + if (credentials() == Failed) { + return; + } + + digest_request = this; + + /* do we have the HA1 */ + + if (!digest_user->HA1created) { + credentials(Pending); + return; + } + + if (digest_request->nonce == NULL) { + /* this isn't a nonce we issued */ + credentials(Failed); + return; + } + + DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL, + authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->cnonce, + digest_user->HA1, SESSIONKEY); + DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->nc, digest_request->cnonce, digest_request->qop, + RequestMethodStr(request->method), digest_request->uri, HA2, Response); + + debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'"); + + if (strcasecmp(digest_request->response, Response) != 0) { + if (!digest_request->flags.helper_queried) { + /* Query the helper in case the password has changed */ + digest_request->flags.helper_queried = 1; + digest_request->credentials_ok = Pending; + return; + } + + if (static_cast(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) { + /* Ugly workaround for certain very broken browsers using the + * wrong method to calculate the request-digest on POST request. + * This should be deleted once Digest authentication becomes more + * widespread and such broken browsers no longer are commonly + * used. + */ + DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce), + digest_request->nc, digest_request->cnonce, digest_request->qop, + RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response); + + if (strcasecmp(digest_request->response, Response)) { + credentials(Failed); + digest_request->setDenyMessage("Incorrect password"); + return; + } else { + const char *useragent = request->header.getStr(HDR_USER_AGENT); + + static IpAddress last_broken_addr; + static int seen_broken_client = 0; + + if (!seen_broken_client) { + last_broken_addr.SetNoAddr(); + seen_broken_client = 1; + } + + if (last_broken_addr != request->client_addr) { + debugs(29, 1, "\nDigest POST bug detected from " << + request->client_addr << " using '" << + (useragent ? useragent : "-") << + "'. Please upgrade browser. See Bug #630 for details."); + + last_broken_addr = request->client_addr; + } + } + } else { + credentials(Failed); + digest_request->flags.invalid_password = 1; + digest_request->setDenyMessage("Incorrect password"); + return; + } + + /* check for stale nonce */ + if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) { + debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale"); + credentials(Failed); + digest_request->setDenyMessage("Stale nonce"); + return; + } + } + + credentials(Ok); + + /* password was checked and did match */ + debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK"); + + /* auth_user is now linked, we reset these values + * after external auth occurs anyway */ + auth_user->expiretime = current_time.tv_sec; + return; +} + +int +AuthDigestUserRequest::module_direction() +{ + switch (credentials()) { + + case Unchecked: + return -1; + + case Ok: + return 0; + + case Pending: + return -1; + + case Failed: + /* send new challenge */ + return 1; + } + return -2; +} + +/* add the [proxy]authorisation header */ +void +AuthDigestUserRequest::addHeader(HttpReply * rep, int accel) +{ + http_hdr_type type; + + /* don't add to authentication error pages */ + + if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) + || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) + return; + + type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; + +#if WAITING_FOR_TE + /* test for http/1.1 transfer chunked encoding */ + if (chunkedtest) + return; +#endif + + if ((static_cast(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) { + flags.authinfo_sent = 1; + debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); + httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); + } +} + +#if WAITING_FOR_TE +/** add the [proxy]authorisation header */ +void +AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel) +{ + int type; + + if (!auth_user_request) + return; + + /* has the header already been send? */ + if (flags.authinfo_sent) + return; + + /* don't add to authentication error pages */ + if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) + || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) + return; + + type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; + + if ((static_cast(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) { + debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\""); + httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce)); + } +} +#endif + +/* send the initial data to a digest authenticator module */ +void +AuthDigestUserRequest::module_start(RH * handler, void *data) +{ + authenticateStateData *r = NULL; + char buf[8192]; + digest_user_h *digest_user; + assert(user()->auth_type == AUTH_DIGEST); + digest_user = dynamic_cast(user()); + assert(digest_user != NULL); + debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'"); + + if (static_cast(AuthConfig::Find("digest"))->authenticate == NULL) { + handler(data, NULL); + return; + } + + r = cbdataAlloc(authenticateStateData); + r->handler = handler; + r->data = cbdataReference(data); + r->auth_user_request = static_cast(this); + if (static_cast(AuthConfig::Find("digest"))->utf8) { + char userstr[1024]; + latin1_to_utf8(userstr, sizeof(userstr), digest_user->username()); + snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); + } else { + snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm); + } + + helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply, r); +} + +void +AuthDigestUserRequest::HandleReply(void *data, char *reply) +{ + authenticateStateData *replyData = static_cast < authenticateStateData * >(data); + char *t = NULL; + void *cbdata; + debugs(29, 9, HERE << "{" << (reply ? reply : "") << "}"); + + if (reply) { + if ((t = strchr(reply, ' '))) + *t++ = '\0'; + + if (*reply == '\0' || *reply == '\n') + reply = NULL; + } + + assert(replyData->auth_user_request != NULL); + AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request; + + /* AYJ: 2009-12-12: allow this because the digest_request pointer is purely local */ + AuthDigestUserRequest *digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request.getRaw()); + assert(digest_request); + + digest_user_h *digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user()); + assert(digest_user != NULL); + + if (reply && (strncasecmp(reply, "ERR", 3) == 0)) { + digest_request->credentials(AuthDigestUserRequest::Failed); + digest_request->flags.invalid_password = 1; + + if (t && *t) + digest_request->setDenyMessage(t); + } else if (reply) { + CvtBin(reply, digest_user->HA1); + digest_user->HA1created = 1; + } + + if (cbdataReferenceValidDone(replyData->data, &cbdata)) + replyData->handler(cbdata, NULL); + + replyData->auth_user_request = NULL; + + cbdataFree(replyData); +} diff --git a/src/auth/digest/digestUserRequest.h b/src/auth/digest/digestUserRequest.h new file mode 100644 index 0000000000..da6be8bd3e --- /dev/null +++ b/src/auth/digest/digestUserRequest.h @@ -0,0 +1,65 @@ +#ifndef _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H +#define _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H + +#include "auth/UserRequest.h" +#include "auth/digest/auth_digest.h" +#include "MemPool.h" + +class ConnStateData; +class HttpReply; +class HttpRequest; + +/** + * The AuthDigestUserRequest structure is what follows the http_request around + */ +class AuthDigestUserRequest : public AuthUserRequest +{ + +public: + enum CredentialsState {Unchecked, Ok, Pending, Failed}; + MEMPROXY_CLASS(AuthDigestUserRequest); + + AuthDigestUserRequest(); + virtual ~AuthDigestUserRequest(); + + virtual int authenticated() const; + virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); + virtual int module_direction(); + virtual void addHeader(HttpReply * rep, int accel); +#if WAITING_FOR_TE + + virtual void addTrailer(HttpReply * rep, int accel); +#endif + + virtual void module_start(RH *, void *); + + CredentialsState credentials() const; + void credentials(CredentialsState); + + char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */ + char *cnonce; /* "0a4f113b" */ + char *realm; /* = "testrealm@host.com" */ + char *pszPass; /* = "Circle Of Life" */ + char *algorithm; /* = "md5" */ + char nc[9]; /* = "00000001" */ + char *pszMethod; /* = "GET" */ + char *qop; /* = "auth" */ + char *uri; /* = "/dir/index.html" */ + char *response; + + struct { + unsigned int authinfo_sent:1; + unsigned int invalid_password:1; + unsigned int helper_queried:1; + } flags; + digest_nonce_h *nonce; + +private: + CredentialsState credentials_ok; + + static HLPCB HandleReply; +}; + +MEMPROXY_CLASS_INLINE(AuthDigestUserRequest); + +#endif /* _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H */ diff --git a/src/auth/negotiate/auth_negotiate.cc b/src/auth/negotiate/auth_negotiate.cc index 68e38e0a7e..14f638d634 100644 --- a/src/auth/negotiate/auth_negotiate.cc +++ b/src/auth/negotiate/auth_negotiate.cc @@ -40,6 +40,7 @@ #include "squid.h" #include "auth/negotiate/auth_negotiate.h" #include "auth/Gadgets.h" +#include "auth/State.h" #include "CacheManager.h" #include "Store.h" #include "client_side.h" @@ -48,6 +49,7 @@ #include "SquidTime.h" /** \todo remove this include */ #include "auth/negotiate/negotiateScheme.h" +#include "auth/negotiate/negotiateUserRequest.h" #include "wordlist.h" /** @@ -55,35 +57,17 @@ \ingroup AuthNegotiateAPI */ -/** - * Maximum length (buffer size) for token strings. - */ -// AYJ: must match re-definition in helpers/negotiate_auth/squid_kerb_auth/squid_kerb_auth.c -#define MAX_AUTHTOKEN_LEN 32768 - - -/// \ingroup AuthNegotiateInternal -static void -authenticateStateFree(authenticateStateData * r) -{ - r->auth_user_request = NULL; - cbdataFree(r); -} - /* Negotiate Scheme */ -static HLPSCB authenticateNegotiateHandleReply; static AUTHSSTATS authenticateNegotiateStats; /// \ingroup AuthNegotiateInternal -static statefulhelper *negotiateauthenticators = NULL; - -CBDATA_TYPE(authenticateStateData); +statefulhelper *negotiateauthenticators = NULL; /// \ingroup AuthNegotiateInternal static int authnegotiate_initialised = 0; /// \ingroup AuthNegotiateInternal -static auth_negotiate_config negotiateConfig; +AuthNegotiateConfig negotiateConfig; /// \ingroup AuthNegotiateInternal static hash_table *proxy_auth_cache = NULL; @@ -230,63 +214,6 @@ AuthNegotiateConfig::configured() const } /* Negotiate Scheme */ -/* See AuthUserRequest.cc::authenticateDirection for return values */ -int -AuthNegotiateUserRequest::module_direction() -{ - /* null auth_user is checked for by authenticateDirection */ - - 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, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev."); - return -2; /* error */ - - case AUTHENTICATE_STATE_FAILED: - 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, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL"); - return -2; - } - - return -2; -} - -/* add the [proxy]authorisation header */ -void -AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel) -{ - http_hdr_type type; - - if (!server_blob) - return; - - /* don't add to authentication error pages */ - - if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) - || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) - return; - - type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; - - httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob); - - safe_free(server_blob); -} void AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request) @@ -366,250 +293,12 @@ NegotiateUser::~NegotiateUser() debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'"); } -static void -authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply) -{ - authenticateStateData *r = static_cast(data); - - int valid; - char *blob, *arg = NULL; - - AuthUser *auth_user; - NegotiateUser *negotiate_user; - AuthNegotiateUserRequest *negotiate_request; - - debugs(29, 8, "authenticateNegotiateHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "") << "'"); - valid = cbdataReferenceValid(r->data); - - if (!valid) { - debugs(29, 1, "authenticateNegotiateHandleReply: invalid callback data. helper '" << lastserver << "'."); - cbdataReferenceDone(r->data); - authenticateStateFree(r); - return; - } - - if (!reply) { - debugs(29, 1, "authenticateNegotiateHandleReply: Helper '" << lastserver << "' crashed!."); - reply = (char *)"BH Internal error"; - } - - AuthUserRequest::Pointer auth_user_request = r->auth_user_request; - assert(auth_user_request != NULL); - - negotiate_request = dynamic_cast(auth_user_request.getRaw()); - assert(negotiate_request != NULL); - - assert(negotiate_request->waiting); - negotiate_request->waiting = 0; - safe_free(negotiate_request->client_blob); - - auth_user = auth_user_request->user(); - assert(auth_user != NULL); - assert(auth_user->auth_type == AUTH_NEGOTIATE); - - negotiate_user = dynamic_cast(auth_user_request->user()); - assert(negotiate_user != NULL); - - if (negotiate_request->authserver == NULL) - negotiate_request->authserver = static_cast(lastserver); - else - assert(negotiate_request->authserver == lastserver); - - /* seperate out the useful data */ - blob = strchr(reply, ' '); - - if (blob) { - blob++; - arg = strchr(blob + 1, ' '); - } else { - arg = NULL; - } - - if (strncasecmp(reply, "TT ", 3) == 0) { - /* we have been given a blob to send to the client */ - if (arg) - *arg++ = '\0'; - safe_free(negotiate_request->server_blob); - negotiate_request->request->flags.must_keepalive = 1; - if (negotiate_request->request->flags.proxy_keepalive) { - negotiate_request->server_blob = xstrdup(blob); - negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS; - auth_user_request->denyMessage("Authentication in progress"); - debugs(29, 4, "authenticateNegotiateHandleReply: Need to challenge the client with a server blob '" << blob << "'"); - } else { - negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; - auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); - } - } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) { - /* we're finished, release the helper */ - - if (arg) - *arg++ = '\0'; - - negotiate_user->username(arg); - - auth_user_request->denyMessage("Login successful"); - - safe_free(negotiate_request->server_blob); - - negotiate_request->server_blob = xstrdup(blob); - - negotiate_request->releaseAuthServer(); - - negotiate_request->auth_state = AUTHENTICATE_STATE_DONE; - - debugs(29, 4, "authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '" << blob << "'"); - - /* connection is authenticated */ - debugs(29, 4, "AuthNegotiateUserRequest::authenticate: authenticated user " << negotiate_user->username()); - /* see if this is an existing user with a different proxy_auth - * string */ - AuthUserHashPointer *usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, negotiate_user->username())); - AuthUser *local_auth_user = negotiate_request->user(); - while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0)) - usernamehash = static_cast(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(); - negotiate_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; - negotiate_request->releaseAuthServer(); - negotiate_request->auth_state = AUTHENTICATE_STATE_DONE; - - } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) { - /* authentication failure (wrong password, etc.) */ - - if (arg) - *arg++ = '\0'; - - auth_user_request->denyMessage(arg); - - negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; - - safe_free(negotiate_request->server_blob); - - negotiate_request->server_blob = xstrdup(blob); - - negotiate_request->releaseAuthServer(); - - debugs(29, 4, "authenticateNegotiateHandleReply: Failed validating user via Negotiate. 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 Negotiate start. - * If after a KK deny the user's request w/ 407 and mark the helper as - * Needing YR. */ - auth_user_request->denyMessage(blob); - negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; - safe_free(negotiate_request->server_blob); - negotiate_request->releaseAuthServer(); - debugs(29, 1, "authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '" << reply << "'"); - } else { - /* protocol error */ - fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply); - } - - negotiate_request->request = NULL; - r->handler(r->data, NULL); - cbdataReferenceDone(r->data); - authenticateStateFree(r); -} - static void authenticateNegotiateStats(StoreEntry * sentry) { helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics"); } - -/** send the initial data to a stateful negotiate authenticator module */ -void -AuthNegotiateUserRequest::module_start(RH * handler, void *data) -{ - authenticateStateData *r = NULL; - static char buf[MAX_AUTHTOKEN_LEN]; - negotiate_user_t *negotiate_user; - AuthUser *auth_user = user(); - - assert(data); - assert(handler); - assert(auth_user); - assert(auth_user->auth_type == AUTH_NEGOTIATE); - - negotiate_user = dynamic_cast(user()); - - debugs(29, 8, "AuthNegotiateUserRequest::module_start: auth state is '" << auth_state << "'"); - - if (negotiateConfig.authenticate == NULL) { - debugs(29, 0, "AuthNegotiateUserRequest::module_start: no Negotiate program specified."); - handler(data, NULL); - return; - } - - r = cbdataAlloc(authenticateStateData); - r->handler = handler; - r->data = cbdataReference(data); - r->auth_user_request = this; - - if (auth_state == AUTHENTICATE_STATE_INITIAL) { - snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? - } else { - snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob); - } - - waiting = 1; - - safe_free(client_blob); - helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver); -} - -/** - * Atomic action: properly release the Negotiate auth helpers which may have been reserved - * for this request connections use. - */ -void -AuthNegotiateUserRequest::releaseAuthServer() -{ - if (authserver) { - debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'"); - helperStatefulReleaseServer(authserver); - authserver = NULL; - } else - debugs(29, 6, HERE << "No Negotiate auth server to release."); -} - -/* clear any connection related authentication details */ -void -AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn) -{ - assert(conn != NULL); - - debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')"); - - if (conn->auth_user_request == NULL) { - debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request"); - return; - } - - releaseAuthServer(); - - /* unlock the connection based lock */ - debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'."); - - conn->auth_user_request = NULL; -} - /* * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed * Auth_user structure. @@ -630,167 +319,13 @@ AuthNegotiateConfig::decode(char const *proxy_auth) return auth_user_request; } -int -AuthNegotiateUserRequest::authenticated() const -{ - if (auth_state == AUTHENTICATE_STATE_DONE) { - debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated."); - return 1; - } - - debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated."); - - return 0; -} - -void -AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type) -{ - const char *proxy_auth, *blob; - - /** \todo rename this!! */ - AuthUser *local_auth_user; - negotiate_user_t *negotiate_user; - - local_auth_user = user(); - assert(local_auth_user); - assert(local_auth_user->auth_type == AUTH_NEGOTIATE); - negotiate_user = dynamic_cast(local_auth_user); - assert (this); - - /** Check that we are in the client side, where we can generate - * auth challenges */ - - if (conn == NULL) { - auth_state = AUTHENTICATE_STATE_FAILED; - debugs(29, 1, "AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!"); - return; - } - - if (waiting) { - debugs(29, 1, "AuthNegotiateUserRequest::authenticate: waiting for helper reply!"); - return; - } - - if (server_blob) { - debugs(29, 2, "AuthNegotiateUserRequest::authenticate: need to challenge client '" << server_blob << "'!"); - return; - } - - /* get header */ - proxy_auth = aRequest->header.getStr(type); - - /* locate second word */ - blob = proxy_auth; - - if (blob) { - while (xisspace(*blob) && *blob) - blob++; - - while (!xisspace(*blob) && *blob) - blob++; - - while (xisspace(*blob) && *blob) - blob++; - } - - switch (auth_state) { - - case AUTHENTICATE_STATE_NONE: - /* we've received a negotiate request. pass to a helper */ - debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '" << proxy_auth << "'"); - auth_state = AUTHENTICATE_STATE_INITIAL; - safe_free(client_blob); - client_blob=xstrdup(blob); - conn->auth_type = AUTH_NEGOTIATE; - assert(conn->auth_user_request == NULL); - conn->auth_user_request = this; - request = aRequest; - HTTPMSGLOCK(request); - return; - - break; - - case AUTHENTICATE_STATE_INITIAL: - debugs(29, 1, "AuthNegotiateUserRequest::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 (request) - HTTPMSGUNLOCK(request); - request = aRequest; - HTTPMSGLOCK(request); - return; - - break; - - case AUTHENTICATE_STATE_DONE: - fatal("AuthNegotiateUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n"); - - break; - - case AUTHENTICATE_STATE_FAILED: - /* we've failed somewhere in authentication */ - debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate failed. " << proxy_auth); - - return; - - break; - } - - return; -} - -AuthNegotiateUserRequest::AuthNegotiateUserRequest() : - /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE) -{ - waiting=0; - client_blob=0; - server_blob=0; - authserver=NULL; - request=NULL; -} - -AuthNegotiateUserRequest::~AuthNegotiateUserRequest() -{ - safe_free(server_blob); - safe_free(client_blob); - - if (authserver != NULL) { - debugs(29, 9, "AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '" << authserver << "'"); - helperStatefulReleaseServer(authserver); - authserver = NULL; - } - if (request) { - HTTPMSGUNLOCK(request); - request = NULL; - } -} - void NegotiateUser::deleteSelf() const { delete this; } -NegotiateUser::NegotiateUser (AuthConfig *aConfig) : AuthUser (aConfig) +NegotiateUser::NegotiateUser(AuthConfig *aConfig) : AuthUser (aConfig) { proxy_auth_list.head = proxy_auth_list.tail = NULL; } - -const char * -AuthNegotiateUserRequest::connLastHeader() -{ - return NULL; -} - diff --git a/src/auth/negotiate/auth_negotiate.h b/src/auth/negotiate/auth_negotiate.h index 1dc858ff56..22f9b37dad 100644 --- a/src/auth/negotiate/auth_negotiate.h +++ b/src/auth/negotiate/auth_negotiate.h @@ -5,10 +5,12 @@ #ifndef __AUTH_NEGOTIATE_H__ #define __AUTH_NEGOTIATE_H__ + +#include "auth/Config.h" #include "auth/Gadgets.h" +#include "auth/State.h" #include "auth/User.h" #include "auth/UserRequest.h" -#include "auth/Config.h" #include "helper.h" /** @@ -19,28 +21,6 @@ /// \ingroup AuthNegotiateAPI #define DefaultAuthenticateChildrenMax 32 /* 32 processes */ -#ifndef __AUTH_AUTHENTICATE_STATE_T__ -#define __AUTH_AUTHENTICATE_STATE_T__ - -/// \ingroup AuthNegotiateAPI -typedef enum { - AUTHENTICATE_STATE_NONE, - AUTHENTICATE_STATE_INITIAL, - AUTHENTICATE_STATE_IN_PROGRESS, - AUTHENTICATE_STATE_DONE, - AUTHENTICATE_STATE_FAILED -} auth_state_t; /* connection level auth state */ - -/* Generic */ - -/// \ingroup AuthNegotiateAPI -typedef struct { - void *data; - AuthUserRequest::Pointer auth_user_request; - RH *handler; -} authenticateStateData; -#endif - /// \ingroup AuthNegotiateAPI class NegotiateUser : public AuthUser { @@ -55,54 +35,10 @@ public: MEMPROXY_CLASS_INLINE(NegotiateUser); -/// \ingroup AuthNegotiateAPI -typedef class NegotiateUser negotiate_user_t; - -/// \ingroup AuthNegotiateAPI -class AuthNegotiateUserRequest : public AuthUserRequest -{ - -public: - MEMPROXY_CLASS(AuthNegotiateUserRequest); - - AuthNegotiateUserRequest(); - virtual ~AuthNegotiateUserRequest(); - virtual int authenticated() const; - virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); - virtual int module_direction(); - virtual void onConnectionClose(ConnStateData *); - virtual void module_start(RH *, void *); - - virtual void addHeader(HttpReply * rep, int accel); - - virtual const char * connLastHeader(); - - /*we need to store the helper server between requests */ - helper_stateful_server *authserver; - void releaseAuthServer(void); ///< Release the authserver helper server properly. - - /* what connection is this associated with */ - /* ConnStateData * conn;*/ - - /* how far through the authentication process are we? */ - auth_state_t auth_state; - - /* our current blob to pass to the client */ - char *server_blob; - /* our current blob to pass to the server */ - char *client_blob; - - /* currently waiting for helper response */ - unsigned char waiting; - - /* need access to the request flags to mess around on pconn failure */ - HttpRequest *request; -}; - -MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest); - #include "HelperChildConfig.h" +extern statefulhelper *negotiateauthenticators; + /* configuration runtime data */ /// \ingroup AuthNegotiateAPI @@ -126,7 +62,6 @@ public: wordlist *authenticate; }; -/// \ingroup AuthNegotiateAPI -typedef class AuthNegotiateConfig auth_negotiate_config; +extern AuthNegotiateConfig negotiateConfig; #endif diff --git a/src/auth/negotiate/negotiateUserRequest.cc b/src/auth/negotiate/negotiateUserRequest.cc new file mode 100644 index 0000000000..9935dc8ef0 --- /dev/null +++ b/src/auth/negotiate/negotiateUserRequest.cc @@ -0,0 +1,433 @@ +#include "config.h" +#include "auth/negotiate/auth_negotiate.h" +#include "auth/negotiate/negotiateUserRequest.h" +#include "auth/User.h" +#include "helper.h" +#include "HttpReply.h" +#include "HttpRequest.h" +#include "SquidTime.h" + +/** + * Maximum length (buffer size) for token strings. + */ +// AYJ: must match re-definition in helpers/negotiate_auth/kerberos/negotiate_kerb_auth.cc +#define MAX_AUTHTOKEN_LEN 32768 + +AuthNegotiateUserRequest::AuthNegotiateUserRequest() : + /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE) +{ + waiting=0; + client_blob=0; + server_blob=0; + authserver=NULL; + request=NULL; +} + +AuthNegotiateUserRequest::~AuthNegotiateUserRequest() +{ + safe_free(server_blob); + safe_free(client_blob); + + if (authserver != NULL) { + debugs(29, 9, HERE << "releasing server '" << authserver << "'"); + helperStatefulReleaseServer(authserver); + authserver = NULL; + } + if (request) { + HTTPMSGUNLOCK(request); + request = NULL; + } +} + +const char * +AuthNegotiateUserRequest::connLastHeader() +{ + return NULL; +} + +int +AuthNegotiateUserRequest::authenticated() const +{ + if (auth_state == AUTHENTICATE_STATE_DONE) { + debugs(29, 9, HERE << "user authenticated."); + return 1; + } + + debugs(29, 9, HERE << "user not fully authenticated."); + return 0; +} + +/* See AuthUserRequest.cc::authenticateDirection for return values */ +int +AuthNegotiateUserRequest::module_direction() +{ + /* null auth_user is checked for by authenticateDirection */ + + if (waiting || client_blob) + return -1; /* need helper response to continue */ + + switch (auth_state) { + + /* no progress at all. */ + + case AUTHENTICATE_STATE_NONE: + debugs(29, DBG_CRITICAL, HERE << "called before Negotiate Authenticate for request " << this << "!. Report a bug to the Squid developers!"); + return -2; /* error */ + + case AUTHENTICATE_STATE_FAILED: + 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, DBG_IMPORTANT, "WARNING: Negotiate Authentcation in unexpected state AUTHENTICATE_STATE_INITIAL"); + return -2; + } + + return -2; +} + +/* add the [proxy]authorisation header */ +void +AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel) +{ + http_hdr_type type; + + if (!server_blob) + return; + + /* don't add to authentication error pages */ + if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED) + || (accel && rep->sline.status == HTTP_UNAUTHORIZED)) + return; + + type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO; + httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob); + + safe_free(server_blob); +} + +/** send the initial data to a stateful negotiate authenticator module */ +void +AuthNegotiateUserRequest::module_start(RH * handler, void *data) +{ + authenticateStateData *r = NULL; + static char buf[MAX_AUTHTOKEN_LEN]; + NegotiateUser *negotiate_user; + AuthUser *auth_user = user(); + + assert(data); + assert(handler); + assert(auth_user); + assert(auth_user->auth_type == AUTH_NEGOTIATE); + + negotiate_user = dynamic_cast(user()); + + debugs(29, 8, HERE << "auth state is '" << auth_state << "'"); + + if (negotiateConfig.authenticate == NULL) { + debugs(29, DBG_CRITICAL, "ERROR: No Negotiate authentication program configured."); + handler(data, NULL); + return; + } + + r = cbdataAlloc(authenticateStateData); + r->handler = handler; + r->data = cbdataReference(data); + r->auth_user_request = this; + + if (auth_state == AUTHENTICATE_STATE_INITIAL) { + snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? + } else { + snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob); + } + + waiting = 1; + + safe_free(client_blob); + helperStatefulSubmit(negotiateauthenticators, buf, AuthNegotiateUserRequest::HandleReply, r, authserver); +} + +/** + * Atomic action: properly release the Negotiate auth helpers which may have been reserved + * for this request connections use. + */ +void +AuthNegotiateUserRequest::releaseAuthServer() +{ + if (authserver) { + debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'"); + helperStatefulReleaseServer(authserver); + authserver = NULL; + } else + debugs(29, 6, HERE << "No Negotiate auth server to release."); +} + +/* clear any connection related authentication details */ +void +AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn) +{ + assert(conn != NULL); + + debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')"); + + if (conn->auth_user_request == NULL) { + debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request"); + return; + } + + releaseAuthServer(); + + /* unlock the connection based lock */ + debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'."); + + conn->auth_user_request = NULL; +} + +void +AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type) +{ + const char *proxy_auth, *blob; + + /** \todo rename this!! */ + AuthUser *local_auth_user; + NegotiateUser *negotiate_user; + + local_auth_user = user(); + assert(local_auth_user); + assert(local_auth_user->auth_type == AUTH_NEGOTIATE); + negotiate_user = dynamic_cast(local_auth_user); + assert (this); + + /** Check that we are in the client side, where we can generate auth challenges */ + if (conn == NULL) { + auth_state = AUTHENTICATE_STATE_FAILED; + debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication attempt to perform authentication without a connection!"); + return; + } + + if (waiting) { + debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication waiting for helper reply!"); + return; + } + + if (server_blob) { + debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!"); + return; + } + + /* get header */ + proxy_auth = aRequest->header.getStr(type); + + /* locate second word */ + blob = proxy_auth; + + if (blob) { + while (xisspace(*blob) && *blob) + blob++; + + while (!xisspace(*blob) && *blob) + blob++; + + while (xisspace(*blob) && *blob) + blob++; + } + + switch (auth_state) { + + case AUTHENTICATE_STATE_NONE: + /* we've received a negotiate request. pass to a helper */ + debugs(29, 9, HERE << "auth state negotiate none. Received blob: '" << proxy_auth << "'"); + auth_state = AUTHENTICATE_STATE_INITIAL; + safe_free(client_blob); + client_blob=xstrdup(blob); + conn->auth_type = AUTH_NEGOTIATE; + assert(conn->auth_user_request == NULL); + conn->auth_user_request = this; + request = aRequest; + HTTPMSGLOCK(request); + break; + + case AUTHENTICATE_STATE_INITIAL: + debugs(29, 1, HERE << "need to ask helper"); + 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 (request) + HTTPMSGUNLOCK(request); + request = aRequest; + HTTPMSGLOCK(request); + break; + + case AUTHENTICATE_STATE_DONE: + fatal("AuthNegotiateUserRequest::authenticate: unexpected auth state DONE! Report a bug to the squid developers.\n"); + break; + + case AUTHENTICATE_STATE_FAILED: + /* we've failed somewhere in authentication */ + debugs(29, 9, HERE << "auth state negotiate failed. " << proxy_auth); + break; + } + + return; +} + +void +AuthNegotiateUserRequest::HandleReply(void *data, void *lastserver, char *reply) +{ + authenticateStateData *r = static_cast(data); + + int valid; + char *blob, *arg = NULL; + + AuthUser *auth_user; + NegotiateUser *negotiate_user; + AuthNegotiateUserRequest *negotiate_request; + + debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "") << "'"); + valid = cbdataReferenceValid(r->data); + + if (!valid) { + debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication invalid callback data. helper '" << lastserver << "'."); + cbdataReferenceDone(r->data); + authenticateStateFree(r); + return; + } + + if (!reply) { + debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication Helper '" << lastserver << "' crashed!."); + reply = (char *)"BH Internal error"; + } + + AuthUserRequest::Pointer auth_user_request = r->auth_user_request; + assert(auth_user_request != NULL); + + negotiate_request = dynamic_cast(auth_user_request.getRaw()); + assert(negotiate_request != NULL); + + assert(negotiate_request->waiting); + negotiate_request->waiting = 0; + safe_free(negotiate_request->client_blob); + + auth_user = auth_user_request->user(); + assert(auth_user != NULL); + assert(auth_user->auth_type == AUTH_NEGOTIATE); + + negotiate_user = dynamic_cast(auth_user_request->user()); + assert(negotiate_user != NULL); + + if (negotiate_request->authserver == NULL) + negotiate_request->authserver = static_cast(lastserver); + else + assert(negotiate_request->authserver == lastserver); + + /* seperate out the useful data */ + blob = strchr(reply, ' '); + + if (blob) { + blob++; + arg = strchr(blob + 1, ' '); + } else { + arg = NULL; + } + + if (strncasecmp(reply, "TT ", 3) == 0) { + /* we have been given a blob to send to the client */ + if (arg) + *arg++ = '\0'; + safe_free(negotiate_request->server_blob); + negotiate_request->request->flags.must_keepalive = 1; + if (negotiate_request->request->flags.proxy_keepalive) { + negotiate_request->server_blob = xstrdup(blob); + negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS; + auth_user_request->denyMessage("Authentication in progress"); + debugs(29, 4, HERE << "Need to challenge the client with a server blob '" << blob << "'"); + } else { + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); + } + } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) { + /* we're finished, release the helper */ + + if (arg) + *arg++ = '\0'; + + negotiate_user->username(arg); + auth_user_request->denyMessage("Login successful"); + safe_free(negotiate_request->server_blob); + negotiate_request->server_blob = xstrdup(blob); + negotiate_request->releaseAuthServer(); + negotiate_request->auth_state = AUTHENTICATE_STATE_DONE; + debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << blob << "'"); + + /* connection is authenticated */ + debugs(29, 4, HERE << "authenticated user " << negotiate_user->username()); + /* see if this is an existing user with a different proxy_auth + * string */ + AuthUserHashPointer *usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, negotiate_user->username())); + AuthUser *local_auth_user = negotiate_request->user(); + while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0)) + usernamehash = static_cast(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(); + negotiate_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; + negotiate_request->releaseAuthServer(); + negotiate_request->auth_state = AUTHENTICATE_STATE_DONE; + + } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) { + /* authentication failure (wrong password, etc.) */ + + if (arg) + *arg++ = '\0'; + + auth_user_request->denyMessage(arg); + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + safe_free(negotiate_request->server_blob); + negotiate_request->server_blob = xstrdup(blob); + negotiate_request->releaseAuthServer(); + debugs(29, 4, HERE << "Failed validating user via Negotiate. 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 Negotiate start. + * If after a KK deny the user's request w/ 407 and mark the helper as + * Needing YR. */ + auth_user_request->denyMessage(blob); + negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED; + safe_free(negotiate_request->server_blob); + negotiate_request->releaseAuthServer(); + debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication validating user. Error returned '" << reply << "'"); + } else { + /* protocol error */ + fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply); + } + + negotiate_request->request = NULL; + r->handler(r->data, NULL); + cbdataReferenceDone(r->data); + authenticateStateFree(r); +} + diff --git a/src/auth/negotiate/negotiateUserRequest.h b/src/auth/negotiate/negotiateUserRequest.h new file mode 100644 index 0000000000..5f98035965 --- /dev/null +++ b/src/auth/negotiate/negotiateUserRequest.h @@ -0,0 +1,62 @@ +#ifndef _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H +#define _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H + +#include "auth/State.h" +#include "auth/UserRequest.h" +#include "helper.h" +#include "MemPool.h" + +// #include "typedefs.h" + +class ConnStateData; +class HttpReply; +class HttpRequest; +struct helper_stateful_server; + +/// \ingroup AuthNegotiateAPI +class AuthNegotiateUserRequest : public AuthUserRequest +{ + +public: + MEMPROXY_CLASS(AuthNegotiateUserRequest); + + AuthNegotiateUserRequest(); + virtual ~AuthNegotiateUserRequest(); + virtual int authenticated() const; + virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); + virtual int module_direction(); + virtual void onConnectionClose(ConnStateData *); + virtual void module_start(RH *, void *); + + virtual void addHeader(HttpReply * rep, int accel); + + virtual const char * connLastHeader(); + + /* we need to store the helper server between requests */ + helper_stateful_server *authserver; + void releaseAuthServer(void); ///< Release the authserver helper server properly. + + /* what connection is this associated with */ + /* ConnStateData * conn;*/ + + /* how far through the authentication process are we? */ + auth_state_t auth_state; + + /* our current blob to pass to the client */ + char *server_blob; + /* our current blob to pass to the server */ + char *client_blob; + + /* currently waiting for helper response */ + unsigned char waiting; + + /* need access to the request flags to mess around on pconn failure */ + HttpRequest *request; + +private: + static HLPSCB HandleReply; +}; + +MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest); + +#endif /* _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H */ diff --git a/src/auth/ntlm/auth_ntlm.cc b/src/auth/ntlm/auth_ntlm.cc index df807ceaea..f2798d1f29 100644 --- a/src/auth/ntlm/auth_ntlm.cc +++ b/src/auth/ntlm/auth_ntlm.cc @@ -41,6 +41,8 @@ #include "auth/Gadgets.h" #include "auth/ntlm/auth_ntlm.h" #include "auth/ntlm/ntlmScheme.h" +#include "auth/ntlm/ntlmUserRequest.h" +#include "auth/State.h" #include "CacheManager.h" #include "Store.h" #include "client_side.h" @@ -49,23 +51,12 @@ #include "wordlist.h" #include "SquidTime.h" - -static void -authenticateStateFree(authenticateStateData * r) -{ - r->auth_user_request = NULL; - cbdataFree(r); -} - /* NTLM Scheme */ -static HLPSCB authenticateNTLMHandleReply; static AUTHSSTATS authenticateNTLMStats; -static statefulhelper *ntlmauthenticators = NULL; +statefulhelper *ntlmauthenticators = NULL; static int authntlm_initialised = 0; -CBDATA_TYPE(authenticateStateData); - static hash_table *proxy_auth_cache = NULL; /* @@ -209,41 +200,6 @@ AuthNTLMConfig::configured() const } /* NTLM Scheme */ -/* See AuthUserRequest.cc::authenticateDirection for return values */ -int -AuthNTLMUserRequest::module_direction() -{ - /* null auth_user is checked for by authenticateDirection */ - - 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; /* 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; -} void AuthNTLMConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request) @@ -308,224 +264,12 @@ NTLMUser::~NTLMUser() debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'"); } -static void -authenticateNTLMHandleReply(void *data, void *lastserver, char *reply) -{ - authenticateStateData *r = static_cast(data); - - int valid; - char *blob; - - AuthUser *auth_user; - NTLMUser *ntlm_user; - AuthUserRequest::Pointer auth_user_request; - AuthNTLMUserRequest *ntlm_request; - - debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "") << "'"); - valid = cbdataReferenceValid(r->data); - - if (!valid) { - debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'."); - cbdataReferenceDone(r->data); - authenticateStateFree(r); - return; - } - - if (!reply) { - 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(auth_user_request.getRaw()); - 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(auth_user_request->user()); - - assert(ntlm_user != NULL); - - if (ntlm_request->authserver == NULL) - ntlm_request->authserver = static_cast(lastserver); - else - assert(ntlm_request->authserver == lastserver); - - /* seperate out the useful data */ - blob = strchr(reply, ' '); - - if (blob) - blob++; - - if (strncasecmp(reply, "TT ", 3) == 0) { - /* 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 << "'"); - } else { - ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; - auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); - } - } else if (strncasecmp(reply, "AF ", 3) == 0) { - /* we're finished, release the helper */ - ntlm_user->username(blob); - auth_user_request->denyMessage("Login successful"); - safe_free(ntlm_request->server_blob); - - 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(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(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; - ntlm_request->releaseAuthServer(); - ntlm_request->auth_state = AUTHENTICATE_STATE_DONE; - } else if (strncasecmp(reply, "NA ", 3) == 0) { - /* authentication failure (wrong password, etc.) */ - auth_user_request->denyMessage(blob); - ntlm_request->auth_state = AUTHENTICATE_STATE_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) { - /* 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); - 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); - } - - if (ntlm_request->request) { - HTTPMSGUNLOCK(ntlm_request->request); - ntlm_request->request = NULL; - } - r->handler(r->data, NULL); - cbdataReferenceDone(r->data); - authenticateStateFree(r); -} - static void authenticateNTLMStats(StoreEntry * sentry) { helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics"); } - -/* send the initial data to a stateful ntlm authenticator module */ -void -AuthNTLMUserRequest::module_start(RH * handler, void *data) -{ - authenticateStateData *r = NULL; - static char buf[8192]; - ntlm_user_t *ntlm_user; - AuthUser *auth_user = user(); - - assert(data); - assert(handler); - assert(auth_user); - assert(auth_user->auth_type == AUTH_NTLM); - - ntlm_user = dynamic_cast(user()); - - debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'"); - - if (static_cast(AuthConfig::Find("ntlm"))->authenticate == NULL) { - debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified."); - handler(data, NULL); - return; - } - - r = cbdataAlloc(authenticateStateData); - r->handler = handler; - r->data = cbdataReference(data); - r->auth_user_request = this; - - 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); -} - -/** - * Atomic action: properly release the NTLM auth helpers which may have been reserved - * for this request connections use. - */ -void -AuthNTLMUserRequest::releaseAuthServer() -{ - if (authserver) { - debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'"); - helperStatefulReleaseServer(authserver); - authserver = NULL; - } else - debugs(29, 6, HERE << "No NTLM auth server to release."); -} - -/* clear any connection related authentication details */ -void -AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn) -{ - 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; -} - /* * Decode a NTLM [Proxy-]Auth string, placing the results in the passed * Auth_user structure. @@ -546,151 +290,6 @@ AuthNTLMConfig::decode(char const *proxy_auth) return auth_user_request; } -int -AuthNTLMUserRequest::authenticated() const -{ - if (auth_state == AUTHENTICATE_STATE_DONE) { - 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; - - /* TODO: rename this!! */ - AuthUser *local_auth_user; - ntlm_user_t *ntlm_user; - - local_auth_user = user(); - assert(local_auth_user); - assert(local_auth_user->auth_type == AUTH_NTLM); - ntlm_user = dynamic_cast(local_auth_user); - assert (this); - - /* Check that we are in the client side, where we can generate - * auth challenges */ - - 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 = aRequest->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 (auth_state) { - - case AUTHENTICATE_STATE_NONE: - /* 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; - request = aRequest; - HTTPMSGLOCK(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 (request) - HTTPMSGUNLOCK(request); - request = aRequest; - HTTPMSGLOCK(request); - return; - - break; - - case AUTHENTICATE_STATE_DONE: - 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 */ - debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth); - - return; - - break; - } - - return; -} - -AuthNTLMUserRequest::AuthNTLMUserRequest() : - /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE) -{ - waiting=0; - client_blob=0; - server_blob=0; - authserver=NULL; - request = NULL; -} - -AuthNTLMUserRequest::~AuthNTLMUserRequest() -{ - safe_free(server_blob); - safe_free(client_blob); - - releaseAuthServer(); - - if (request) { - HTTPMSGUNLOCK(request); - request = NULL; - } -} - void NTLMUser::deleteSelf() const { @@ -701,9 +300,3 @@ NTLMUser::NTLMUser (AuthConfig *aConfig) : AuthUser (aConfig) { proxy_auth_list.head = proxy_auth_list.tail = NULL; } - -const char * -AuthNTLMUserRequest::connLastHeader() -{ - return NULL; -} diff --git a/src/auth/ntlm/auth_ntlm.h b/src/auth/ntlm/auth_ntlm.h index e70694b4e9..58f532d997 100644 --- a/src/auth/ntlm/auth_ntlm.h +++ b/src/auth/ntlm/auth_ntlm.h @@ -13,25 +13,6 @@ #define DefaultAuthenticateChildrenMax 32 /* 32 processes */ -#ifndef __AUTH_AUTHENTICATE_STATE_T__ -#define __AUTH_AUTHENTICATE_STATE_T__ -typedef enum { - AUTHENTICATE_STATE_NONE, - AUTHENTICATE_STATE_INITIAL, - AUTHENTICATE_STATE_IN_PROGRESS, - AUTHENTICATE_STATE_DONE, - AUTHENTICATE_STATE_FAILED -} auth_state_t; /* connection level auth state */ - -/* Generic */ - -typedef struct { - void *data; - AuthUserRequest::Pointer auth_user_request; - RH *handler; -} authenticateStateData; -#endif - class NTLMUser : public AuthUser { @@ -47,46 +28,6 @@ MEMPROXY_CLASS_INLINE(NTLMUser); typedef class NTLMUser ntlm_user_t; -class AuthNTLMUserRequest : public AuthUserRequest -{ - -public: - MEMPROXY_CLASS(AuthNTLMUserRequest); - - AuthNTLMUserRequest(); - virtual ~AuthNTLMUserRequest(); - virtual int authenticated() const; - virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); - virtual int module_direction(); - virtual void onConnectionClose(ConnStateData *); - virtual void module_start(RH *, void *); - - virtual const char * connLastHeader(); - - /* we need to store the helper server between requests */ - helper_stateful_server *authserver; - void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning. - - /* what connection is this associated with */ -// ConnStateData * conn; - - /* how far through the authentication process are we? */ - auth_state_t auth_state; - - /* our current blob to pass to the client */ - char *server_blob; - /* our current blob to pass to the server */ - char *client_blob; - - /* currently waiting for helper response */ - unsigned char waiting; - - /* need access to the request flags to mess around on pconn failure */ - HttpRequest *request; -}; - -MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest); - #include "HelperChildConfig.h" /* configuration runtime data */ @@ -113,4 +54,6 @@ public: typedef class AuthNTLMConfig auth_ntlm_config; +extern statefulhelper *ntlmauthenticators; + #endif diff --git a/src/auth/ntlm/ntlmUserRequest.cc b/src/auth/ntlm/ntlmUserRequest.cc new file mode 100644 index 0000000000..34317dbb7e --- /dev/null +++ b/src/auth/ntlm/ntlmUserRequest.cc @@ -0,0 +1,390 @@ +#include "config.h" +#include "auth/ntlm/ntlmUserRequest.h" +#include "auth/ntlm/auth_ntlm.h" +#include "cbdata.h" +#include "HttpRequest.h" +#include "SquidTime.h" + +/* state wrapper functions */ + +AuthNTLMUserRequest::AuthNTLMUserRequest() : + /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE) +{ + waiting=0; + client_blob=0; + server_blob=0; + authserver=NULL; + request = NULL; +} + +AuthNTLMUserRequest::~AuthNTLMUserRequest() +{ + safe_free(server_blob); + safe_free(client_blob); + + releaseAuthServer(); + + if (request) { + HTTPMSGUNLOCK(request); + request = NULL; + } +} + +const char * +AuthNTLMUserRequest::connLastHeader() +{ + return NULL; +} + +/* See AuthUserRequest.cc::authenticateDirection for return values */ +int +AuthNTLMUserRequest::module_direction() +{ + /* null auth_user is checked for by authenticateDirection */ + + 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; /* 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 initial data to a stateful ntlm authenticator module */ +void +AuthNTLMUserRequest::module_start(RH * handler, void *data) +{ + authenticateStateData *r = NULL; + static char buf[8192]; + ntlm_user_t *ntlm_user; + AuthUser *auth_user = user(); + + assert(data); + assert(handler); + assert(auth_user); + assert(auth_user->auth_type == AUTH_NTLM); + + ntlm_user = dynamic_cast(user()); + + debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'"); + + if (static_cast(AuthConfig::Find("ntlm"))->authenticate == NULL) { + debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified."); + handler(data, NULL); + return; + } + + r = cbdataAlloc(authenticateStateData); + r->handler = handler; + r->data = cbdataReference(data); + r->auth_user_request = this; + + 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, AuthNTLMUserRequest::HandleReply, r, authserver); +} + +/** + * Atomic action: properly release the NTLM auth helpers which may have been reserved + * for this request connections use. + */ +void +AuthNTLMUserRequest::releaseAuthServer() +{ + if (authserver) { + debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'"); + helperStatefulReleaseServer(authserver); + authserver = NULL; + } else + debugs(29, 6, HERE << "No NTLM auth server to release."); +} + +void +AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn) +{ + 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 (auth_state == AUTHENTICATE_STATE_DONE) { + 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; + + /* TODO: rename this!! */ + AuthUser *local_auth_user; + ntlm_user_t *ntlm_user; + + local_auth_user = user(); + assert(local_auth_user); + assert(local_auth_user->auth_type == AUTH_NTLM); + ntlm_user = dynamic_cast(local_auth_user); + assert (this); + + /* Check that we are in the client side, where we can generate + * auth challenges */ + + 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 = aRequest->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 (auth_state) { + + case AUTHENTICATE_STATE_NONE: + /* 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; + request = aRequest; + HTTPMSGLOCK(request); + break; + + case AUTHENTICATE_STATE_INITIAL: + debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper"); + 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 (request) + HTTPMSGUNLOCK(request); + request = aRequest; + HTTPMSGLOCK(request); + break; + + case AUTHENTICATE_STATE_DONE: + 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 */ + debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth); + break; + } +} + +void +AuthNTLMUserRequest::HandleReply(void *data, void *lastserver, char *reply) +{ + authenticateStateData *r = static_cast(data); + + int valid; + char *blob; + + AuthUser *auth_user; + NTLMUser *ntlm_user; + AuthUserRequest::Pointer auth_user_request; + AuthNTLMUserRequest *ntlm_request; + + debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "") << "'"); + valid = cbdataReferenceValid(r->data); + + if (!valid) { + debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'."); + cbdataReferenceDone(r->data); + authenticateStateFree(r); + return; + } + + if (!reply) { + 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(auth_user_request.getRaw()); + 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(auth_user_request->user()); + + assert(ntlm_user != NULL); + + if (ntlm_request->authserver == NULL) + ntlm_request->authserver = static_cast(lastserver); + else + assert(ntlm_request->authserver == lastserver); + + /* seperate out the useful data */ + blob = strchr(reply, ' '); + + if (blob) + blob++; + + if (strncasecmp(reply, "TT ", 3) == 0) { + /* 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 << "'"); + } else { + ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED; + auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); + } + } else if (strncasecmp(reply, "AF ", 3) == 0) { + /* we're finished, release the helper */ + ntlm_user->username(blob); + auth_user_request->denyMessage("Login successful"); + safe_free(ntlm_request->server_blob); + + 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(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(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; + ntlm_request->releaseAuthServer(); + ntlm_request->auth_state = AUTHENTICATE_STATE_DONE; + } else if (strncasecmp(reply, "NA ", 3) == 0) { + /* authentication failure (wrong password, etc.) */ + auth_user_request->denyMessage(blob); + ntlm_request->auth_state = AUTHENTICATE_STATE_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) { + /* 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); + 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); + } + + if (ntlm_request->request) { + HTTPMSGUNLOCK(ntlm_request->request); + ntlm_request->request = NULL; + } + r->handler(r->data, NULL); + cbdataReferenceDone(r->data); + authenticateStateFree(r); +} diff --git a/src/auth/ntlm/ntlmUserRequest.h b/src/auth/ntlm/ntlmUserRequest.h new file mode 100644 index 0000000000..9dfedf7d60 --- /dev/null +++ b/src/auth/ntlm/ntlmUserRequest.h @@ -0,0 +1,58 @@ +#ifndef _SQUID_SRC_AUTH_NTLM_USERREQUEST_H +#define _SQUID_SRC_AUTH_NTLM_USERREQUEST_H + +#include "auth/State.h" +#include "auth/UserRequest.h" +#include "auth/ntlm/auth_ntlm.h" +#include "MemPool.h" + +class ConnStateData; +class HttpReply; +class HttpRequest; +struct helper_stateful_server; + +class AuthNTLMUserRequest : public AuthUserRequest +{ + +public: + MEMPROXY_CLASS(AuthNTLMUserRequest); + + AuthNTLMUserRequest(); + virtual ~AuthNTLMUserRequest(); + virtual int authenticated() const; + virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); + virtual int module_direction(); + virtual void onConnectionClose(ConnStateData *); + virtual void module_start(RH *, void *); + + virtual const char * connLastHeader(); + + /* we need to store the helper server between requests */ + helper_stateful_server *authserver; + void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning. + + /* what connection is this associated with */ +// ConnStateData * conn; + + /* how far through the authentication process are we? */ + auth_state_t auth_state; + + /* our current blob to pass to the client */ + char *server_blob; + + /* our current blob to pass to the server */ + char *client_blob; + + /* currently waiting for helper response */ + unsigned char waiting; + + /* need access to the request flags to mess around on pconn failure */ + HttpRequest *request; + +private: + static HLPSCB HandleReply; +}; + +MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest); + +#endif /* _SQUID_SRC_AUTH_NTLM_USERREQUEST_H */ diff --git a/src/tests/testAuth.cc b/src/tests/testAuth.cc index d54f613d44..b4ea356396 100644 --- a/src/tests/testAuth.cc +++ b/src/tests/testAuth.cc @@ -183,6 +183,7 @@ testAuthUserRequest::scheme() } #if HAVE_AUTH_MODULE_BASIC +#include "auth/basic/basicUserRequest.h" #include "auth/basic/auth_basic.h" /* AuthBasicUserRequest::AuthBasicUserRequest works */ diff --git a/src/tests/testAuth.h b/src/tests/testAuth.h index b1704aa0ca..406fdbfb44 100644 --- a/src/tests/testAuth.h +++ b/src/tests/testAuth.h @@ -48,6 +48,7 @@ protected: }; #ifdef HAVE_AUTH_MODULE_BASIC +#include "auth/basic/basicUserRequest.h" class testAuthBasicUserRequest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( testAuthBasicUserRequest ); @@ -64,6 +65,7 @@ protected: #endif #ifdef HAVE_AUTH_MODULE_DIGEST +#include "auth/digest/digestUserRequest.h" class testAuthDigestUserRequest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( testAuthDigestUserRequest ); @@ -80,6 +82,7 @@ protected: #endif #ifdef HAVE_AUTH_MODULE_NTLM +#include "auth/ntlm/ntlmUserRequest.h" class testAuthNTLMUserRequest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( testAuthNTLMUserRequest ); @@ -96,6 +99,7 @@ protected: #endif #ifdef HAVE_AUTH_MODULE_NEGOTIATE +#include "auth/negotiate/negotiateUserRequest.h" class testAuthNegotiateUserRequest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( testAuthNegotiateUserRequest );