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)
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
#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.
{
public:
typedef RefCount<AuthScheme> Pointer;
+ typedef Vector<AuthScheme::Pointer>::iterator iterator;
+ typedef Vector<AuthScheme::Pointer>::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<AuthScheme::Pointer> const &Schemes();
+
+ /**
+ * Locate an authentication scheme component by Name.
+ */
static AuthScheme::Pointer Find(const char *);
- typedef Vector<AuthScheme::Pointer>::iterator iterator;
- typedef Vector<AuthScheme::Pointer>::const_iterator const_iterator;
/* per scheme methods */
virtual char const *type () const = 0;
--- /dev/null
+#include "config.h"
+#include "auth/State.h"
+
+CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+void
+authenticateStateFree(authenticateStateData * r)
+{
+ r->auth_user_request = NULL;
+ cbdataFree(r);
+}
--- /dev/null
+#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__ */
/**
\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
{
#include "squid.h"
#include "auth/basic/auth_basic.h"
+#include "auth/basic/basicUserRequest.h"
#include "auth/Gadgets.h"
#include "CacheManager.h"
#include "Store.h"
#include "wordlist.h"
#include "SquidTime.h"
-static void
-authenticateStateFree(AuthenticateStateData * r)
-{
- cbdataFree(r);
-}
-
/* Basic Scheme */
-
static HLPCB authenticateBasicHandleReply;
static AUTHSSTATS authenticateBasicStats;
return basicScheme::GetInstance()->type();
}
-AuthBasicUserRequest::AuthBasicUserRequest()
-{}
-
-AuthBasicUserRequest::~AuthBasicUserRequest()
-{}
-
bool
BasicUser::authenticated() const
return false;
}
-int
-AuthBasicUserRequest::authenticated() const
-{
- BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(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<BasicUser *>(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<AuthBasicConfig*>(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<BasicUser *>(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<AuthBasicConfig*>(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)
{
AuthUserRequest::Pointer
AuthBasicConfig::decode(char const *proxy_auth)
{
- AuthUserRequest::Pointer auth_user_request = new AuthBasicUserRequest();
+ AuthUserRequest::Pointer auth_user_request = dynamic_cast<AuthUserRequest*>(new AuthBasicUserRequest);
/* decode the username */
/* trim BASIC from string */
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<basic_data *>(user());
- assert(basic_auth != NULL);
- debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
-
- if (static_cast<AuthBasicConfig*>(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];
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 */
--- /dev/null
+#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<BasicUser const *>(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<BasicUser *>(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<AuthBasicConfig*>(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<BasicUser *>(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<AuthBasicConfig*>(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<basic_data *>(user());
+ assert(basic_auth != NULL);
+ debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
+
+ if (static_cast<AuthBasicConfig*>(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);
+}
+
--- /dev/null
+#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 */
#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
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
#endif
-static void
+void
authDigestNonceUnlink(digest_nonce_h * nonce)
{
assert(nonce != NULL);
authenticateDigestNonceDelete(nonce);
}
-static const char *
-authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
+const char *
+authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
{
if (!nonce)
return NULL;
return nonce;
}
-static int
+int
authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
{
unsigned long intnc;
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)
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<digest_user_h*>(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<AuthDigestConfig*>(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<AuthDigestConfig*>(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<AuthDigestConfig*>(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)
}
}
-static void
-authenticateDigestHandleReply(void *data, char *reply)
-{
- DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
- char *t = NULL;
- void *cbdata;
- debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "<NULL>") << "}");
-
- 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
helperOpenServers(digestauthenticators);
- CBDATA_INIT_TYPE(DigestAuthenticateStateData);
+ CBDATA_INIT_TYPE(authenticateStateData);
}
}
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<digest_user_h*>(user());
- assert(digest_user != NULL);
- debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'");
-
- if (static_cast<AuthDigestConfig*>(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<AuthUserRequest*>(this);
- if (static_cast<AuthDigestConfig*>(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);
-}
#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;
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 */
} 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 */
/* strings */
#define QOP_AUTH "auth"
+extern helper *digestauthenticators;
+
#endif
--- /dev/null
+#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<digest_user_h*>(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<AuthDigestConfig*>(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<AuthDigestConfig*>(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<AuthDigestConfig*>(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<digest_user_h*>(user());
+ assert(digest_user != NULL);
+ debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'");
+
+ if (static_cast<AuthDigestConfig*>(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<AuthUserRequest*>(this);
+ if (static_cast<AuthDigestConfig*>(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 : "<NULL>") << "}");
+
+ 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);
+}
--- /dev/null
+#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 */
#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"
#include "SquidTime.h"
/** \todo remove this include */
#include "auth/negotiate/negotiateScheme.h"
+#include "auth/negotiate/negotiateUserRequest.h"
#include "wordlist.h"
/**
\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;
}
/* 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)
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<authenticateStateData *>(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 : "<NULL>") << "'");
- 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<AuthNegotiateUserRequest *>(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<negotiate_user_t *>(auth_user_request->user());
- assert(negotiate_user != NULL);
-
- if (negotiate_request->authserver == NULL)
- negotiate_request->authserver = static_cast<helper_stateful_server*>(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<AuthUserHashPointer *>(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<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- 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<negotiate_user_t *>(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.
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<negotiate_user_t *>(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;
-}
-
#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"
/**
/// \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
{
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
wordlist *authenticate;
};
-/// \ingroup AuthNegotiateAPI
-typedef class AuthNegotiateConfig auth_negotiate_config;
+extern AuthNegotiateConfig negotiateConfig;
#endif
--- /dev/null
+#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<NegotiateUser*>(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<NegotiateUser *>(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<authenticateStateData *>(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 : "<NULL>") << "'");
+ 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<AuthNegotiateUserRequest *>(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<NegotiateUser *>(auth_user_request->user());
+ assert(negotiate_user != NULL);
+
+ if (negotiate_request->authserver == NULL)
+ negotiate_request->authserver = static_cast<helper_stateful_server*>(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<AuthUserHashPointer *>(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<AuthUserHashPointer *>(usernamehash->next);
+ if (usernamehash) {
+ /* we can't seamlessly recheck the username due to the
+ * challenge-response nature of the protocol.
+ * Just free the temporary auth_user */
+ usernamehash->user()->absorb(local_auth_user);
+ //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
+ local_auth_user = usernamehash->user();
+ 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);
+}
+
--- /dev/null
+#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 */
#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"
#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;
/*
}
/* 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)
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<authenticateStateData *>(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 : "<NULL>") << "'");
- 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<AuthNTLMUserRequest *>(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<ntlm_user_t *>(auth_user_request->user());
-
- assert(ntlm_user != NULL);
-
- if (ntlm_request->authserver == NULL)
- ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(ntlm_request->authserver == lastserver);
-
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
-
- if (blob)
- blob++;
-
- if (strncasecmp(reply, "TT ", 3) == 0) {
- /* 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<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
- AuthUser *local_auth_user = ntlm_request->user();
- while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
- usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- ntlm_request->_auth_user = local_auth_user;
- } else {
- /* store user in hash's */
- local_auth_user->addToNameCache();
- // authenticateUserNameCacheAdd(local_auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- local_auth_user->expiretime = current_time.tv_sec;
- 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<ntlm_user_t *>(user());
-
- debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
-
- if (static_cast<AuthNTLMConfig*>(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.
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<ntlm_user_t *>(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
{
{
proxy_auth_list.head = proxy_auth_list.tail = NULL;
}
-
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
- return NULL;
-}
#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
{
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 */
typedef class AuthNTLMConfig auth_ntlm_config;
+extern statefulhelper *ntlmauthenticators;
+
#endif
--- /dev/null
+#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<ntlm_user_t *>(user());
+
+ debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
+
+ if (static_cast<AuthNTLMConfig*>(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<ntlm_user_t *>(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<authenticateStateData *>(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 : "<NULL>") << "'");
+ 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<AuthNTLMUserRequest *>(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<ntlm_user_t *>(auth_user_request->user());
+
+ assert(ntlm_user != NULL);
+
+ if (ntlm_request->authserver == NULL)
+ ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(ntlm_request->authserver == lastserver);
+
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
+
+ if (blob)
+ blob++;
+
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ /* 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<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
+ AuthUser *local_auth_user = ntlm_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
+ if (usernamehash) {
+ /* we can't seamlessly recheck the username due to the
+ * challenge-response nature of the protocol.
+ * Just free the temporary auth_user */
+ usernamehash->user()->absorb(local_auth_user);
+ //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
+ local_auth_user = usernamehash->user();
+ ntlm_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ // authenticateUserNameCacheAdd(local_auth_user);
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ local_auth_user->expiretime = current_time.tv_sec;
+ 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);
+}
--- /dev/null
+#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 */
}
#if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
#include "auth/basic/auth_basic.h"
/* AuthBasicUserRequest::AuthBasicUserRequest works
*/
};
#ifdef HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
class testAuthBasicUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthBasicUserRequest );
#endif
#ifdef HAVE_AUTH_MODULE_DIGEST
+#include "auth/digest/digestUserRequest.h"
class testAuthDigestUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthDigestUserRequest );
#endif
#ifdef HAVE_AUTH_MODULE_NTLM
+#include "auth/ntlm/ntlmUserRequest.h"
class testAuthNTLMUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthNTLMUserRequest );
#endif
#ifdef HAVE_AUTH_MODULE_NEGOTIATE
+#include "auth/negotiate/negotiateUserRequest.h"
class testAuthNegotiateUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthNegotiateUserRequest );