]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Unwind UserRequest child objects. Unify duplicate authenticateStateData code.
authorAmos Jeffries <squid3@treenet.co.nz>
Fri, 12 Feb 2010 10:51:58 +0000 (23:51 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 12 Feb 2010 10:51:58 +0000 (23:51 +1300)
23 files changed:
src/auth/Makefile.am
src/auth/Scheme.h
src/auth/State.cc [new file with mode: 0644]
src/auth/State.h [new file with mode: 0644]
src/auth/UserRequest.h
src/auth/basic/auth_basic.cc
src/auth/basic/auth_basic.h
src/auth/basic/basicUserRequest.cc [new file with mode: 0644]
src/auth/basic/basicUserRequest.h [new file with mode: 0644]
src/auth/digest/auth_digest.cc
src/auth/digest/auth_digest.h
src/auth/digest/digestUserRequest.cc [new file with mode: 0644]
src/auth/digest/digestUserRequest.h [new file with mode: 0644]
src/auth/negotiate/auth_negotiate.cc
src/auth/negotiate/auth_negotiate.h
src/auth/negotiate/negotiateUserRequest.cc [new file with mode: 0644]
src/auth/negotiate/negotiateUserRequest.h [new file with mode: 0644]
src/auth/ntlm/auth_ntlm.cc
src/auth/ntlm/auth_ntlm.h
src/auth/ntlm/ntlmUserRequest.cc [new file with mode: 0644]
src/auth/ntlm/ntlmUserRequest.h [new file with mode: 0644]
src/tests/testAuth.cc
src/tests/testAuth.h

index 1abd1280808b909f5692ebd848f5252f83c77281..becf34eba6689e41c36132f029b0ad42b2fba16a 100644 (file)
@@ -19,7 +19,9 @@ libauth_la_SOURCES = \
        UserRequest.h \
        UserRequest.cc \
        Gadgets.cc \
-       Gadgets.h
+       Gadgets.h \
+       State.h \
+       State.cc
 
 libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD)
 libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD)
@@ -32,32 +34,39 @@ libacls_la_SOURCES = \
        AclMaxUserIp.cc \
        AclMaxUserIp.h \
        AclProxyAuth.cc \
-       AclProxyAuth.h  
-
+       AclProxyAuth.h
 
 libbasic_la_SOURCES = \
        basic/basicScheme.cc \
        basic/basicScheme.h \
        basic/auth_basic.cc \
-       basic/auth_basic.h
+       basic/auth_basic.h \
+       basic/basicUserRequest.cc \
+       basic/basicUserRequest.h
 
 libdigest_la_SOURCES = \
        digest/digestScheme.cc \
        digest/digestScheme.h \
        digest/auth_digest.cc \
-       digest/auth_digest.h
+       digest/auth_digest.h \
+       digest/digestUserRequest.cc \
+       digest/digestUserRequest.h
 
 libntlm_la_SOURCES = \
        ntlm/ntlmScheme.cc \
        ntlm/ntlmScheme.h \
        ntlm/auth_ntlm.cc \
-       ntlm/auth_ntlm.h
+       ntlm/auth_ntlm.h \
+       ntlm/ntlmUserRequest.cc \
+       ntlm/ntlmUserRequest.h
 
 libnegotiate_la_SOURCES = \
        negotiate/negotiateScheme.cc \
        negotiate/negotiateScheme.h \
        negotiate/auth_negotiate.cc \
-       negotiate/auth_negotiate.h
+       negotiate/auth_negotiate.h \
+       negotiate/negotiateUserRequest.cc \
+       negotiate/negotiateUserRequest.h
 
 
 TESTS += testHeaders
index c183f7703f720cd2456fd87b4c35b9c34373884a..12370ed4af3809e3080eae10cd97c384c6117ef8 100644 (file)
 #ifndef SQUID_AUTHSCHEME_H
 #define SQUID_AUTHSCHEME_H
 
-#include "squid.h"
+#include "config.h"
 #include "Array.h"
 #include "RefCount.h"
 
+class AuthConfig;
+
 /**
  \defgroup AuthSchemeAPI       Authentication Scheme API
  \ingroup AuthAPI
  */
 
 /**
- \ingroup AuthAPI
- \ingroup AuthSchemeAPI
- \par
\ingroup AuthAPI
\ingroup AuthSchemeAPI
\par
  * I represent an authentication scheme. For now my children
- * store both the scheme metadata, and the scheme configuration.
- \par
+ * store the scheme metadata.
\par
  * Should we need multiple configs of a single scheme,
  * a new class AuthConfiguration should be made, and the
  * config specific calls on AuthScheme moved to it.
@@ -57,17 +59,29 @@ class AuthScheme : public RefCountable
 {
 public:
     typedef RefCount<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;
diff --git a/src/auth/State.cc b/src/auth/State.cc
new file mode 100644 (file)
index 0000000..375bd23
--- /dev/null
@@ -0,0 +1,11 @@
+#include "config.h"
+#include "auth/State.h"
+
+CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+void
+authenticateStateFree(authenticateStateData * r)
+{
+    r->auth_user_request = NULL;
+    cbdataFree(r);
+}
diff --git a/src/auth/State.h b/src/auth/State.h
new file mode 100644 (file)
index 0000000..2d1f081
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __AUTH_AUTHENTICATE_STATE_T__
+#define __AUTH_AUTHENTICATE_STATE_T__
+
+#include "auth/UserRequest.h"
+
+typedef enum {
+    AUTHENTICATE_STATE_NONE,
+    AUTHENTICATE_STATE_INITIAL,
+    AUTHENTICATE_STATE_IN_PROGRESS,
+    AUTHENTICATE_STATE_DONE,
+    AUTHENTICATE_STATE_FAILED
+} auth_state_t;                 /* connection level auth state */
+
+/**
+ * CBDATA state for NTLM, Negotiate, and Digest stateful authentication.
+ */
+typedef struct {
+    void *data;
+    AuthUserRequest::Pointer auth_user_request;
+    RH *handler;
+} authenticateStateData;
+
+extern CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+extern void authenticateStateFree(authenticateStateData * r);
+
+#endif /* __AUTH_AUTHENTICATE_STATE_T__ */
index e97d9048e1979567a3a40e8a2ecff8d2baae8d4a..d95c2b5a312f51009d23d478c2cb24a49577cee7 100644 (file)
@@ -58,6 +58,9 @@ struct AuthUserIP {
 /**
  \ingroup AuthAPI
  * This is a short lived structure is the visible aspect of the authentication framework.
+ *
+ * It and its children hold the state data while processing authentication for a client request.
+ * The AuthenticationStateData object is merely a CBDATA wrapper for one of these.
  */
 class AuthUserRequest : public RefCountable
 {
index 3a91b594b216923df26cdc26c1e2651d9b18f991..a5479c7b63c622518696dc8e2bf838fd9fd8d348 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "squid.h"
 #include "auth/basic/auth_basic.h"
+#include "auth/basic/basicUserRequest.h"
 #include "auth/Gadgets.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "wordlist.h"
 #include "SquidTime.h"
 
-static void
-authenticateStateFree(AuthenticateStateData * r)
-{
-    cbdataFree(r);
-}
-
 /* Basic Scheme */
-
 static HLPCB authenticateBasicHandleReply;
 static AUTHSSTATS authenticateBasicStats;
 
@@ -97,12 +91,6 @@ AuthBasicConfig::type() const
     return basicScheme::GetInstance()->type();
 }
 
-AuthBasicUserRequest::AuthBasicUserRequest()
-{}
-
-AuthBasicUserRequest::~AuthBasicUserRequest()
-{}
-
 
 bool
 BasicUser::authenticated() const
@@ -115,76 +103,6 @@ BasicUser::authenticated() const
     return false;
 }
 
-int
-AuthBasicUserRequest::authenticated() const
-{
-    BasicUser const *basic_auth = dynamic_cast<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)
 {
@@ -538,7 +456,7 @@ BasicUser::updateCached(BasicUser *from)
 AuthUserRequest::Pointer
 AuthBasicConfig::decode(char const *proxy_auth)
 {
-    AuthUserRequest::Pointer auth_user_request = new AuthBasicUserRequest();
+    AuthUserRequest::Pointer auth_user_request = dynamic_cast<AuthUserRequest*>(new AuthBasicUserRequest);
     /* decode the username */
     /* trim BASIC from string */
 
@@ -627,35 +545,10 @@ BasicUser::queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler
     node->data = cbdataReference(data);
 }
 
-/* send the initial data to a basic authenticator module */
-void
-AuthBasicUserRequest::module_start(RH * handler, void *data)
-{
-    basic_data *basic_auth;
-    assert(user()->auth_type == AUTH_BASIC);
-    basic_auth = dynamic_cast<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];
index 2d88a985383f8f80ae21a93d2cfd82f7771c5ed8..8c2c33baf35c47f44b5d72ea1788243d9028e53a 100644 (file)
@@ -79,25 +79,6 @@ MEMPROXY_CLASS_INLINE(BasicUser);
 
 typedef class BasicUser basic_data;
 
-/* follows the http request around */
-
-class AuthBasicUserRequest : public AuthUserRequest
-{
-
-public:
-    MEMPROXY_CLASS(AuthBasicUserRequest);
-
-    AuthBasicUserRequest();
-    virtual ~AuthBasicUserRequest();
-
-    virtual int authenticated() const;
-    virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
-    virtual int module_direction();
-    virtual void module_start(RH *, void *);
-};
-
-MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
-
 #include "HelperChildConfig.h"
 
 /* configuration runtime data */
diff --git a/src/auth/basic/basicUserRequest.cc b/src/auth/basic/basicUserRequest.cc
new file mode 100644 (file)
index 0000000..dc45db4
--- /dev/null
@@ -0,0 +1,101 @@
+#include "config.h"
+#include "auth/basic/basicUserRequest.h"
+#include "SquidTime.h"
+
+#include "auth/basic/auth_basic.h"
+
+int
+AuthBasicUserRequest::authenticated() const
+{
+    BasicUser const *basic_auth = dynamic_cast<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);
+}
+
diff --git a/src/auth/basic/basicUserRequest.h b/src/auth/basic/basicUserRequest.h
new file mode 100644 (file)
index 0000000..7e1975a
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+#define _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+
+#include "MemPool.h"
+#include "auth/UserRequest.h"
+
+class ConnStateData;
+class HttpRequest;
+
+/* follows the http request around */
+
+class AuthBasicUserRequest : public AuthUserRequest
+{
+
+public:
+    MEMPROXY_CLASS(AuthBasicUserRequest);
+
+    AuthBasicUserRequest() {};
+    virtual ~AuthBasicUserRequest() {};
+
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
+    virtual int module_direction();
+    virtual void module_start(RH *, void *);
+};
+
+MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_BASIC_USERREQUEST_H */
index 5378ce027f3bf37debfd946a72bc532ae4dd455c..7f698d25c22edaf8dbc40dcc36573f14b021172e 100644 (file)
 #include "SquidTime.h"
 /* TODO don't include this */
 #include "auth/digest/digestScheme.h"
+#include "auth/digest/digestUserRequest.h"
 
 /* Digest Scheme */
 
-static HLPCB authenticateDigestHandleReply;
 static AUTHSSTATS authenticateDigestStats;
 
-static helper *digestauthenticators = NULL;
+helper *digestauthenticators = NULL;
 
 static hash_table *digest_nonce_cache;
 
 static int authdigest_initialised = 0;
 static MemAllocator *digest_nonce_pool = NULL;
 
-CBDATA_TYPE(DigestAuthenticateStateData);
-
 /*
  *
  * Nonce Functions
@@ -78,13 +76,9 @@ static void authenticateDigestNonceDelete(digest_nonce_h * nonce);
 static void authenticateDigestNonceSetup(void);
 static void authenticateDigestNonceShutdown(void);
 static void authenticateDigestNonceReconfigure(void);
-static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce);
-static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
 static int authDigestNonceIsStale(digest_nonce_h * nonce);
 static void authDigestNonceEncode(digest_nonce_h * nonce);
-static int authDigestNonceLastRequest(digest_nonce_h * nonce);
 static void authDigestNonceLink(digest_nonce_h * nonce);
-static void authDigestNonceUnlink(digest_nonce_h * nonce);
 #if NOT_USED
 static int authDigestNonceLinks(digest_nonce_h * nonce);
 #endif
@@ -295,7 +289,7 @@ authDigestNonceLinks(digest_nonce_h * nonce)
 
 #endif
 
-static void
+void
 authDigestNonceUnlink(digest_nonce_h * nonce)
 {
     assert(nonce != NULL);
@@ -312,8 +306,8 @@ authDigestNonceUnlink(digest_nonce_h * nonce)
         authenticateDigestNonceDelete(nonce);
 }
 
-static const char *
-authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
+const char *
+authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
 {
     if (!nonce)
         return NULL;
@@ -341,7 +335,7 @@ authenticateDigestNonceFindNonce(const char *nonceb64)
     return nonce;
 }
 
-static int
+int
 authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
 {
     unsigned long intnc;
@@ -415,8 +409,11 @@ authDigestNonceIsStale(digest_nonce_h * nonce)
     return 0;
 }
 
-/* return -1 if the digest will be stale on the next request */
-static int
+/**
+ * \retval  0    the digest is not stale yet
+ * \retval -1    the digest will be stale on the next request
+ */
+const int
 authDigestNonceLastRequest(digest_nonce_h * nonce)
 {
     if (!nonce)
@@ -558,216 +555,6 @@ AuthDigestConfig::configured() const
     return false;
 }
 
-int
-AuthDigestUserRequest::authenticated() const
-{
-    if (credentials() == Ok)
-        return 1;
-
-    return 0;
-}
-
-/** log a digest user in
- */
-void
-AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
-{
-    AuthDigestUserRequest *digest_request;
-    digest_user_h *digest_user;
-
-    HASHHEX SESSIONKEY;
-    HASHHEX HA2 = "";
-    HASHHEX Response;
-
-    assert(user() != NULL);
-    AuthUser *auth_user = user();
-
-    digest_user = dynamic_cast<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)
@@ -813,51 +600,6 @@ DigestUser::~DigestUser()
     }
 }
 
-static void
-authenticateDigestHandleReply(void *data, char *reply)
-{
-    DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
-    char *t = NULL;
-    void *cbdata;
-    debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "<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
@@ -878,7 +620,7 @@ AuthDigestConfig::init(AuthConfig * scheme)
 
         helperOpenServers(digestauthenticators);
 
-        CBDATA_INIT_TYPE(DigestAuthenticateStateData);
+        CBDATA_INIT_TYPE(authenticateStateData);
     }
 }
 
@@ -1345,73 +1087,5 @@ AuthDigestConfig::decode(char const *proxy_auth)
     return digest_request;
 }
 
-/* send the initial data to a digest authenticator module */
-void
-AuthDigestUserRequest::module_start(RH * handler, void *data)
-{
-    DigestAuthenticateStateData *r = NULL;
-    char buf[8192];
-    digest_user_h *digest_user;
-    assert(user()->auth_type == AUTH_DIGEST);
-    digest_user = dynamic_cast<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);
-}
index c5741ff7ecd44cd1367a1a06f114d14401671758..8b021ddb2f459719583ea0ef792ce4eb6e87597a 100644 (file)
@@ -5,24 +5,17 @@
 
 #ifndef __AUTH_DIGEST_H__
 #define __AUTH_DIGEST_H__
-#include "rfc2617.h"
+
+#include "auth/Config.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
-#include "auth/Config.h"
 #include "helper.h"
+#include "rfc2617.h"
 
 /* Generic */
 
-class DigestAuthenticateStateData
-{
-
-public:
-    void *data;
-    AuthUserRequest::Pointer auth_user_request;
-    RH *handler;
-};
-
 typedef struct _digest_nonce_data digest_nonce_data;
 
 typedef struct _digest_nonce_h digest_nonce_h;
@@ -48,58 +41,6 @@ MEMPROXY_CLASS_INLINE(DigestUser);
 
 typedef class DigestUser digest_user_h;
 
-/* the digest_request structure is what follows the http_request around */
-
-class AuthDigestUserRequest : public AuthUserRequest
-{
-
-public:
-    enum CredentialsState {Unchecked, Ok, Pending, Failed};
-    MEMPROXY_CLASS(AuthDigestUserRequest);
-
-    AuthDigestUserRequest();
-    virtual ~AuthDigestUserRequest();
-
-    virtual int authenticated() const;
-    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
-    virtual int module_direction();
-    virtual void addHeader(HttpReply * rep, int accel);
-#if WAITING_FOR_TE
-
-    virtual void addTrailer(HttpReply * rep, int accel);
-#endif
-
-    virtual void module_start(RH *, void *);
-
-    CredentialsState credentials() const;
-    void credentials(CredentialsState);
-
-//    void authUser(AuthUser *);
-//    AuthUser *authUser() const;
-
-    char *nonceb64;            /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
-    char *cnonce;              /* "0a4f113b" */
-    char *realm;               /* = "testrealm@host.com" */
-    char *pszPass;             /* = "Circle Of Life" */
-    char *algorithm;           /* = "md5" */
-    char nc[9];                        /* = "00000001" */
-    char *pszMethod;           /* = "GET" */
-    char *qop;                 /* = "auth" */
-    char *uri;                 /* = "/dir/index.html" */
-    char *response;
-
-    struct {
-        unsigned int authinfo_sent:1;
-        unsigned int invalid_password:1;
-        unsigned int helper_queried:1;
-    } flags;
-    digest_nonce_h *nonce;
-
-private:
-    CredentialsState credentials_ok;
-};
-
-MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
 
 /* data to be encoded into the nonce's b64 representation */
 
@@ -128,6 +69,11 @@ struct _digest_nonce_h : public hash_link {
     } flags;
 };
 
+extern void authDigestNonceUnlink(digest_nonce_h * nonce);
+extern int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
+extern const char *authenticateDigestNonceNonceb64(const digest_nonce_h * nonce);
+extern const int authDigestNonceLastRequest(digest_nonce_h * nonce);
+
 #include "HelperChildConfig.h"
 
 /* configuration runtime data */
@@ -164,4 +110,6 @@ typedef class AuthDigestConfig auth_digest_config;
 /* strings */
 #define QOP_AUTH "auth"
 
+extern helper *digestauthenticators;
+
 #endif
diff --git a/src/auth/digest/digestUserRequest.cc b/src/auth/digest/digestUserRequest.cc
new file mode 100644 (file)
index 0000000..de45320
--- /dev/null
@@ -0,0 +1,334 @@
+#include "config.h"
+#include "auth/digest/auth_digest.h"
+#include "auth/digest/digestUserRequest.h"
+#include "auth/State.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+AuthDigestUserRequest::AuthDigestUserRequest() :
+        nonceb64(NULL),
+        cnonce(NULL),
+        realm(NULL),
+        pszPass(NULL),
+        algorithm(NULL),
+        pszMethod(NULL),
+        qop(NULL),
+        uri(NULL),
+        response(NULL),
+        nonce(NULL),
+        credentials_ok(Unchecked)
+{}
+
+/**
+ * Delete the digest request structure.
+ * Does NOT delete related AuthUser structures
+ */
+AuthDigestUserRequest::~AuthDigestUserRequest()
+{
+    safe_free(nonceb64);
+    safe_free(cnonce);
+    safe_free(realm);
+    safe_free(pszPass);
+    safe_free(algorithm);
+    safe_free(pszMethod);
+    safe_free(qop);
+    safe_free(uri);
+    safe_free(response);
+
+    if (nonce)
+        authDigestNonceUnlink(nonce);
+}
+
+AuthDigestUserRequest::CredentialsState
+AuthDigestUserRequest::credentials() const
+{
+    return credentials_ok;
+}
+
+void
+AuthDigestUserRequest::credentials(CredentialsState newCreds)
+{
+    credentials_ok = newCreds;
+}
+
+int
+AuthDigestUserRequest::authenticated() const
+{
+    if (credentials() == Ok)
+        return 1;
+
+    return 0;
+}
+
+/** log a digest user in
+ */
+void
+AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+{
+    AuthDigestUserRequest *digest_request;
+    digest_user_h *digest_user;
+
+    HASHHEX SESSIONKEY;
+    HASHHEX HA2 = "";
+    HASHHEX Response;
+
+    assert(user() != NULL);
+    AuthUser *auth_user = user();
+
+    digest_user = dynamic_cast<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);
+}
diff --git a/src/auth/digest/digestUserRequest.h b/src/auth/digest/digestUserRequest.h
new file mode 100644 (file)
index 0000000..da6be8b
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+#define _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "auth/digest/auth_digest.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+
+/**
+ * The AuthDigestUserRequest structure is what follows the http_request around
+ */
+class AuthDigestUserRequest : public AuthUserRequest
+{
+
+public:
+    enum CredentialsState {Unchecked, Ok, Pending, Failed};
+    MEMPROXY_CLASS(AuthDigestUserRequest);
+
+    AuthDigestUserRequest();
+    virtual ~AuthDigestUserRequest();
+
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+    virtual int module_direction();
+    virtual void addHeader(HttpReply * rep, int accel);
+#if WAITING_FOR_TE
+
+    virtual void addTrailer(HttpReply * rep, int accel);
+#endif
+
+    virtual void module_start(RH *, void *);
+
+    CredentialsState credentials() const;
+    void credentials(CredentialsState);
+
+    char *nonceb64;             /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
+    char *cnonce;               /* "0a4f113b" */
+    char *realm;                /* = "testrealm@host.com" */
+    char *pszPass;              /* = "Circle Of Life" */
+    char *algorithm;            /* = "md5" */
+    char nc[9];                 /* = "00000001" */
+    char *pszMethod;            /* = "GET" */
+    char *qop;                  /* = "auth" */
+    char *uri;                  /* = "/dir/index.html" */
+    char *response;
+
+    struct {
+        unsigned int authinfo_sent:1;
+        unsigned int invalid_password:1;
+        unsigned int helper_queried:1;
+    } flags;
+    digest_nonce_h *nonce;
+
+private:
+    CredentialsState credentials_ok;
+
+    static HLPCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H */
index 68e38e0a7e4258a88d9abb32450558ffee6fa1be..14f638d634934b29814ab27041494772ec725b51 100644 (file)
@@ -40,6 +40,7 @@
 #include "squid.h"
 #include "auth/negotiate/auth_negotiate.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "client_side.h"
@@ -48,6 +49,7 @@
 #include "SquidTime.h"
 /** \todo remove this include */
 #include "auth/negotiate/negotiateScheme.h"
+#include "auth/negotiate/negotiateUserRequest.h"
 #include "wordlist.h"
 
 /**
  \ingroup AuthNegotiateAPI
  */
 
-/**
- * Maximum length (buffer size) for token strings.
- */
-// AYJ: must match re-definition in helpers/negotiate_auth/squid_kerb_auth/squid_kerb_auth.c
-#define MAX_AUTHTOKEN_LEN   32768
-
-
-/// \ingroup AuthNegotiateInternal
-static void
-authenticateStateFree(authenticateStateData * r)
-{
-    r->auth_user_request = NULL;
-    cbdataFree(r);
-}
-
 /* Negotiate Scheme */
-static HLPSCB authenticateNegotiateHandleReply;
 static AUTHSSTATS authenticateNegotiateStats;
 
 /// \ingroup AuthNegotiateInternal
-static statefulhelper *negotiateauthenticators = NULL;
-
-CBDATA_TYPE(authenticateStateData);
+statefulhelper *negotiateauthenticators = NULL;
 
 /// \ingroup AuthNegotiateInternal
 static int authnegotiate_initialised = 0;
 
 /// \ingroup AuthNegotiateInternal
-static auth_negotiate_config negotiateConfig;
+AuthNegotiateConfig negotiateConfig;
 
 /// \ingroup AuthNegotiateInternal
 static hash_table *proxy_auth_cache = NULL;
@@ -230,63 +214,6 @@ AuthNegotiateConfig::configured() const
 }
 
 /* Negotiate Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNegotiateUserRequest::module_direction()
-{
-    /* null auth_user is checked for by authenticateDirection */
-
-    if (waiting || client_blob)
-        return -1; /* need helper response to continue */
-
-    switch (auth_state) {
-
-        /* no progress at all. */
-
-    case AUTHENTICATE_STATE_NONE:
-        debugs(29, 1, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev.");
-        return -2; /* error */
-
-    case AUTHENTICATE_STATE_FAILED:
-        return -2; /* error */
-
-
-    case AUTHENTICATE_STATE_IN_PROGRESS:
-        assert(server_blob);
-        return 1; /* send to client */
-
-    case AUTHENTICATE_STATE_DONE:
-        return 0; /* do nothing */
-
-    case AUTHENTICATE_STATE_INITIAL:
-        debugs(29, 1, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
-        return -2;
-    }
-
-    return -2;
-}
-
-/* add the [proxy]authorisation header */
-void
-AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
-{
-    http_hdr_type type;
-
-    if (!server_blob)
-        return;
-
-    /* don't add to authentication error pages */
-
-    if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
-            || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
-        return;
-
-    type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
-    httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
-
-    safe_free(server_blob);
-}
 
 void
 AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
@@ -366,250 +293,12 @@ NegotiateUser::~NegotiateUser()
     debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
 }
 
-static void
-authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
-{
-    authenticateStateData *r = static_cast<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.
@@ -630,167 +319,13 @@ AuthNegotiateConfig::decode(char const *proxy_auth)
     return auth_user_request;
 }
 
-int
-AuthNegotiateUserRequest::authenticated() const
-{
-    if (auth_state == AUTHENTICATE_STATE_DONE) {
-        debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated.");
-        return 1;
-    }
-
-    debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated.");
-
-    return 0;
-}
-
-void
-AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
-    const char *proxy_auth, *blob;
-
-    /** \todo rename this!! */
-    AuthUser *local_auth_user;
-    negotiate_user_t *negotiate_user;
-
-    local_auth_user = user();
-    assert(local_auth_user);
-    assert(local_auth_user->auth_type == AUTH_NEGOTIATE);
-    negotiate_user = dynamic_cast<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;
-}
-
index 1dc858ff56914590c1a33a91e9e5aacd7565785b..22f9b37dad4287dcdbadb568ab3b191b77c57012 100644 (file)
@@ -5,10 +5,12 @@
 
 #ifndef __AUTH_NEGOTIATE_H__
 #define __AUTH_NEGOTIATE_H__
+
+#include "auth/Config.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
-#include "auth/Config.h"
 #include "helper.h"
 
 /**
 /// \ingroup AuthNegotiateAPI
 #define DefaultAuthenticateChildrenMax  32     /* 32 processes */
 
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-
-/// \ingroup AuthNegotiateAPI
-typedef enum {
-    AUTHENTICATE_STATE_NONE,
-    AUTHENTICATE_STATE_INITIAL,
-    AUTHENTICATE_STATE_IN_PROGRESS,
-    AUTHENTICATE_STATE_DONE,
-    AUTHENTICATE_STATE_FAILED
-} auth_state_t;                 /* connection level auth state */
-
-/* Generic */
-
-/// \ingroup AuthNegotiateAPI
-typedef struct {
-    void *data;
-    AuthUserRequest::Pointer auth_user_request;
-    RH *handler;
-} authenticateStateData;
-#endif
-
 /// \ingroup AuthNegotiateAPI
 class NegotiateUser : public AuthUser
 {
@@ -55,54 +35,10 @@ public:
 
 MEMPROXY_CLASS_INLINE(NegotiateUser);
 
-/// \ingroup AuthNegotiateAPI
-typedef class NegotiateUser negotiate_user_t;
-
-/// \ingroup AuthNegotiateAPI
-class AuthNegotiateUserRequest : public AuthUserRequest
-{
-
-public:
-    MEMPROXY_CLASS(AuthNegotiateUserRequest);
-
-    AuthNegotiateUserRequest();
-    virtual ~AuthNegotiateUserRequest();
-    virtual int authenticated() const;
-    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
-    virtual int module_direction();
-    virtual void onConnectionClose(ConnStateData *);
-    virtual void module_start(RH *, void *);
-
-    virtual void addHeader(HttpReply * rep, int accel);
-
-    virtual const char * connLastHeader();
-
-    /*we need to store the helper server between requests */
-    helper_stateful_server *authserver;
-    void releaseAuthServer(void); ///< Release the authserver helper server properly.
-
-    /* what connection is this associated with */
-    /* ConnStateData * conn;*/
-
-    /* how far through the authentication process are we? */
-    auth_state_t auth_state;
-
-    /* our current blob to pass to the client */
-    char *server_blob;
-    /* our current blob to pass to the server */
-    char *client_blob;
-
-    /* currently waiting for helper response */
-    unsigned char waiting;
-
-    /* need access to the request flags to mess around on pconn failure */
-    HttpRequest *request;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
-
 #include "HelperChildConfig.h"
 
+extern statefulhelper *negotiateauthenticators;
+
 /* configuration runtime data */
 
 /// \ingroup AuthNegotiateAPI
@@ -126,7 +62,6 @@ public:
     wordlist *authenticate;
 };
 
-/// \ingroup AuthNegotiateAPI
-typedef class AuthNegotiateConfig auth_negotiate_config;
+extern AuthNegotiateConfig negotiateConfig;
 
 #endif
diff --git a/src/auth/negotiate/negotiateUserRequest.cc b/src/auth/negotiate/negotiateUserRequest.cc
new file mode 100644 (file)
index 0000000..9935dc8
--- /dev/null
@@ -0,0 +1,433 @@
+#include "config.h"
+#include "auth/negotiate/auth_negotiate.h"
+#include "auth/negotiate/negotiateUserRequest.h"
+#include "auth/User.h"
+#include "helper.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/**
+ * Maximum length (buffer size) for token strings.
+ */
+// AYJ: must match re-definition in helpers/negotiate_auth/kerberos/negotiate_kerb_auth.cc
+#define MAX_AUTHTOKEN_LEN   32768
+
+AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
+        /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE)
+{
+    waiting=0;
+    client_blob=0;
+    server_blob=0;
+    authserver=NULL;
+    request=NULL;
+}
+
+AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
+{
+    safe_free(server_blob);
+    safe_free(client_blob);
+
+    if (authserver != NULL) {
+        debugs(29, 9, HERE << "releasing server '" << authserver << "'");
+        helperStatefulReleaseServer(authserver);
+        authserver = NULL;
+    }
+    if (request) {
+        HTTPMSGUNLOCK(request);
+        request = NULL;
+    }
+}
+
+const char *
+AuthNegotiateUserRequest::connLastHeader()
+{
+    return NULL;
+}
+
+int
+AuthNegotiateUserRequest::authenticated() const
+{
+    if (auth_state == AUTHENTICATE_STATE_DONE) {
+        debugs(29, 9, HERE << "user authenticated.");
+        return 1;
+    }
+
+    debugs(29, 9, HERE << "user not fully authenticated.");
+    return 0;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNegotiateUserRequest::module_direction()
+{
+    /* null auth_user is checked for by authenticateDirection */
+
+    if (waiting || client_blob)
+        return -1; /* need helper response to continue */
+
+    switch (auth_state) {
+
+        /* no progress at all. */
+
+    case AUTHENTICATE_STATE_NONE:
+        debugs(29, DBG_CRITICAL, HERE << "called before Negotiate Authenticate for request " << this << "!. Report a bug to the Squid developers!");
+        return -2; /* error */
+
+   case AUTHENTICATE_STATE_FAILED:
+        return -2; /* error */
+
+    case AUTHENTICATE_STATE_IN_PROGRESS:
+        assert(server_blob);
+        return 1; /* send to client */
+
+    case AUTHENTICATE_STATE_DONE:
+        return 0; /* do nothing */
+
+    case AUTHENTICATE_STATE_INITIAL:
+        debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentcation in unexpected state AUTHENTICATE_STATE_INITIAL");
+        return -2;
+    }
+
+    return -2;
+}
+
+/* add the [proxy]authorisation header */
+void
+AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
+{
+    http_hdr_type type;
+
+    if (!server_blob)
+        return;
+
+    /* don't add to authentication error pages */
+    if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+            || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+        return;
+
+    type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+    httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
+
+    safe_free(server_blob);
+}
+
+/** send the initial data to a stateful negotiate authenticator module */
+void
+AuthNegotiateUserRequest::module_start(RH * handler, void *data)
+{
+    authenticateStateData *r = NULL;
+    static char buf[MAX_AUTHTOKEN_LEN];
+    NegotiateUser *negotiate_user;
+    AuthUser *auth_user = user();
+
+    assert(data);
+    assert(handler);
+    assert(auth_user);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
+
+    negotiate_user = dynamic_cast<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);
+}
+
diff --git a/src/auth/negotiate/negotiateUserRequest.h b/src/auth/negotiate/negotiateUserRequest.h
new file mode 100644 (file)
index 0000000..5f98035
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+
+#include "auth/State.h"
+#include "auth/UserRequest.h"
+#include "helper.h"
+#include "MemPool.h"
+
+// #include "typedefs.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+/// \ingroup AuthNegotiateAPI
+class AuthNegotiateUserRequest : public AuthUserRequest
+{
+
+public:
+    MEMPROXY_CLASS(AuthNegotiateUserRequest);
+
+    AuthNegotiateUserRequest();
+    virtual ~AuthNegotiateUserRequest();
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+    virtual int module_direction();
+    virtual void onConnectionClose(ConnStateData *);
+    virtual void module_start(RH *, void *);
+
+    virtual void addHeader(HttpReply * rep, int accel);
+
+    virtual const char * connLastHeader();
+
+    /* we need to store the helper server between requests */
+    helper_stateful_server *authserver;
+    void releaseAuthServer(void); ///< Release the authserver helper server properly.
+
+    /* what connection is this associated with */
+    /* ConnStateData * conn;*/
+
+    /* how far through the authentication process are we? */
+    auth_state_t auth_state;
+
+    /* our current blob to pass to the client */
+    char *server_blob;
+    /* our current blob to pass to the server */
+    char *client_blob;
+
+    /* currently waiting for helper response */
+    unsigned char waiting;
+
+    /* need access to the request flags to mess around on pconn failure */
+    HttpRequest *request;
+
+private:
+    static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H */
index df807ceaea33e9f4c8bae5d50645396f17c9dc08..f2798d1f29a4c258ed42cef8adf65640666addb3 100644 (file)
@@ -41,6 +41,8 @@
 #include "auth/Gadgets.h"
 #include "auth/ntlm/auth_ntlm.h"
 #include "auth/ntlm/ntlmScheme.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/State.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "client_side.h"
 #include "wordlist.h"
 #include "SquidTime.h"
 
-
-static void
-authenticateStateFree(authenticateStateData * r)
-{
-    r->auth_user_request = NULL;
-    cbdataFree(r);
-}
-
 /* NTLM Scheme */
-static HLPSCB authenticateNTLMHandleReply;
 static AUTHSSTATS authenticateNTLMStats;
 
-static statefulhelper *ntlmauthenticators = NULL;
+statefulhelper *ntlmauthenticators = NULL;
 static int authntlm_initialised = 0;
 
-CBDATA_TYPE(authenticateStateData);
-
 static hash_table *proxy_auth_cache = NULL;
 
 /*
@@ -209,41 +200,6 @@ AuthNTLMConfig::configured() const
 }
 
 /* NTLM Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNTLMUserRequest::module_direction()
-{
-    /* null auth_user is checked for by authenticateDirection */
-
-    if (waiting || client_blob)
-        return -1; /* need helper response to continue */
-
-    switch (auth_state) {
-
-        /* no progress at all. */
-
-    case AUTHENTICATE_STATE_NONE:
-        debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
-        return -2; /* error */
-
-    case AUTHENTICATE_STATE_FAILED:
-        return -2; /* error */
-
-
-    case AUTHENTICATE_STATE_IN_PROGRESS:
-        assert(server_blob);
-        return 1; /* send to client */
-
-    case AUTHENTICATE_STATE_DONE:
-        return 0; /* do nothing */
-
-    case AUTHENTICATE_STATE_INITIAL:
-        debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
-        return -2;
-    }
-
-    return -2;
-}
 
 void
 AuthNTLMConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
@@ -308,224 +264,12 @@ NTLMUser::~NTLMUser()
     debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
 }
 
-static void
-authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
-{
-    authenticateStateData *r = static_cast<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.
@@ -546,151 +290,6 @@ AuthNTLMConfig::decode(char const *proxy_auth)
     return auth_user_request;
 }
 
-int
-AuthNTLMUserRequest::authenticated() const
-{
-    if (auth_state == AUTHENTICATE_STATE_DONE) {
-        debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
-        return 1;
-    }
-
-    debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
-
-    return 0;
-}
-
-void
-AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
-    const char *proxy_auth, *blob;
-
-    /* TODO: rename this!! */
-    AuthUser *local_auth_user;
-    ntlm_user_t *ntlm_user;
-
-    local_auth_user = user();
-    assert(local_auth_user);
-    assert(local_auth_user->auth_type == AUTH_NTLM);
-    ntlm_user = dynamic_cast<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
 {
@@ -701,9 +300,3 @@ NTLMUser::NTLMUser (AuthConfig *aConfig) : AuthUser (aConfig)
 {
     proxy_auth_list.head = proxy_auth_list.tail = NULL;
 }
-
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
-    return NULL;
-}
index e70694b4e9a341ca89a170f9f622de22d4ea9cc0..58f532d9978ccc02c018af73b7115cecb0641edc 100644 (file)
 
 #define DefaultAuthenticateChildrenMax  32     /* 32 processes */
 
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-typedef enum {
-    AUTHENTICATE_STATE_NONE,
-    AUTHENTICATE_STATE_INITIAL,
-    AUTHENTICATE_STATE_IN_PROGRESS,
-    AUTHENTICATE_STATE_DONE,
-    AUTHENTICATE_STATE_FAILED
-} auth_state_t;                 /* connection level auth state */
-
-/* Generic */
-
-typedef struct {
-    void *data;
-    AuthUserRequest::Pointer auth_user_request;
-    RH *handler;
-} authenticateStateData;
-#endif
-
 class NTLMUser : public AuthUser
 {
 
@@ -47,46 +28,6 @@ MEMPROXY_CLASS_INLINE(NTLMUser);
 
 typedef class NTLMUser ntlm_user_t;
 
-class AuthNTLMUserRequest : public AuthUserRequest
-{
-
-public:
-    MEMPROXY_CLASS(AuthNTLMUserRequest);
-
-    AuthNTLMUserRequest();
-    virtual ~AuthNTLMUserRequest();
-    virtual int authenticated() const;
-    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
-    virtual int module_direction();
-    virtual void onConnectionClose(ConnStateData *);
-    virtual void module_start(RH *, void *);
-
-    virtual const char * connLastHeader();
-
-    /* we need to store the helper server between requests */
-    helper_stateful_server *authserver;
-    void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
-
-    /* what connection is this associated with */
-//    ConnStateData * conn;
-
-    /* how far through the authentication process are we? */
-    auth_state_t auth_state;
-
-    /* our current blob to pass to the client */
-    char *server_blob;
-    /* our current blob to pass to the server */
-    char *client_blob;
-
-    /* currently waiting for helper response */
-    unsigned char waiting;
-
-    /* need access to the request flags to mess around on pconn failure */
-    HttpRequest *request;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
-
 #include "HelperChildConfig.h"
 
 /* configuration runtime data */
@@ -113,4 +54,6 @@ public:
 
 typedef class AuthNTLMConfig auth_ntlm_config;
 
+extern statefulhelper *ntlmauthenticators;
+
 #endif
diff --git a/src/auth/ntlm/ntlmUserRequest.cc b/src/auth/ntlm/ntlmUserRequest.cc
new file mode 100644 (file)
index 0000000..34317db
--- /dev/null
@@ -0,0 +1,390 @@
+#include "config.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "cbdata.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/* state wrapper functions */
+
+AuthNTLMUserRequest::AuthNTLMUserRequest() :
+        /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE)
+{
+    waiting=0;
+    client_blob=0;
+    server_blob=0;
+    authserver=NULL;
+    request = NULL;
+}
+
+AuthNTLMUserRequest::~AuthNTLMUserRequest()
+{
+    safe_free(server_blob);
+    safe_free(client_blob);
+
+    releaseAuthServer();
+
+    if (request) {
+        HTTPMSGUNLOCK(request);
+        request = NULL;
+    }
+}
+
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+    return NULL;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNTLMUserRequest::module_direction()
+{
+    /* null auth_user is checked for by authenticateDirection */
+
+    if (waiting || client_blob)
+        return -1; /* need helper response to continue */
+
+    switch (auth_state) {
+
+        /* no progress at all. */
+
+    case AUTHENTICATE_STATE_NONE:
+        debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
+        return -2; /* error */
+
+    case AUTHENTICATE_STATE_FAILED:
+        return -2; /* error */
+
+
+    case AUTHENTICATE_STATE_IN_PROGRESS:
+        assert(server_blob);
+        return 1; /* send to client */
+
+    case AUTHENTICATE_STATE_DONE:
+        return 0; /* do nothing */
+
+    case AUTHENTICATE_STATE_INITIAL:
+        debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
+        return -2;
+    }
+
+    return -2;
+}
+
+/* send the initial data to a stateful ntlm authenticator module */
+void
+AuthNTLMUserRequest::module_start(RH * handler, void *data)
+{
+    authenticateStateData *r = NULL;
+    static char buf[8192];
+    ntlm_user_t *ntlm_user;
+    AuthUser *auth_user = user();
+
+    assert(data);
+    assert(handler);
+    assert(auth_user);
+    assert(auth_user->auth_type == AUTH_NTLM);
+
+    ntlm_user = dynamic_cast<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);
+}
diff --git a/src/auth/ntlm/ntlmUserRequest.h b/src/auth/ntlm/ntlmUserRequest.h
new file mode 100644 (file)
index 0000000..9dfedf7
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+
+#include "auth/State.h"
+#include "auth/UserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+class AuthNTLMUserRequest : public AuthUserRequest
+{
+
+public:
+    MEMPROXY_CLASS(AuthNTLMUserRequest);
+
+    AuthNTLMUserRequest();
+    virtual ~AuthNTLMUserRequest();
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+    virtual int module_direction();
+    virtual void onConnectionClose(ConnStateData *);
+    virtual void module_start(RH *, void *);
+
+    virtual const char * connLastHeader();
+
+    /* we need to store the helper server between requests */
+    helper_stateful_server *authserver;
+    void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
+
+    /* what connection is this associated with */
+//    ConnStateData * conn;
+
+    /* how far through the authentication process are we? */
+    auth_state_t auth_state;
+
+    /* our current blob to pass to the client */
+    char *server_blob;
+
+    /* our current blob to pass to the server */
+    char *client_blob;
+
+    /* currently waiting for helper response */
+    unsigned char waiting;
+
+    /* need access to the request flags to mess around on pconn failure */
+    HttpRequest *request;
+
+private:
+    static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NTLM_USERREQUEST_H */
index d54f613d448bdfbd9286c592b9091a7d64808445..b4ea3563963bf05c602e4ab4266985f6f607bfb4 100644 (file)
@@ -183,6 +183,7 @@ testAuthUserRequest::scheme()
 }
 
 #if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
 #include "auth/basic/auth_basic.h"
 /* AuthBasicUserRequest::AuthBasicUserRequest works
  */
index b1704aa0cab801730ee0089c8e2a906b4c8c79ab..406fdbfb44a95e0c541c3c98c34c619444fd18b2 100644 (file)
@@ -48,6 +48,7 @@ protected:
 };
 
 #ifdef HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
 class testAuthBasicUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthBasicUserRequest );
@@ -64,6 +65,7 @@ protected:
 #endif
 
 #ifdef HAVE_AUTH_MODULE_DIGEST
+#include "auth/digest/digestUserRequest.h"
 class testAuthDigestUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthDigestUserRequest );
@@ -80,6 +82,7 @@ protected:
 #endif
 
 #ifdef HAVE_AUTH_MODULE_NTLM
+#include "auth/ntlm/ntlmUserRequest.h"
 class testAuthNTLMUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthNTLMUserRequest );
@@ -96,6 +99,7 @@ protected:
 #endif
 
 #ifdef HAVE_AUTH_MODULE_NEGOTIATE
+#include "auth/negotiate/negotiateUserRequest.h"
 class testAuthNegotiateUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthNegotiateUserRequest );