dnl
dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
dnl
-dnl $Id: configure.in,v 1.387 2005/10/16 19:57:40 serassio Exp $
+dnl $Id: configure.in,v 1.388 2005/10/23 11:55:31 hno Exp $
dnl
dnl
dnl
AC_CONFIG_AUX_DIR(cfgaux)
AM_INIT_AUTOMAKE(squid, 3.0-PRE3-CVS)
AM_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.387 $)dnl
+AC_REVISION($Revision: 1.388 $)dnl
AC_PREFIX_DEFAULT(/usr/local/squid)
AM_MAINTAINER_MODE
esac
],
[ if test -z "$AUTH_MODULES"; then
- AUTH_MODULES="basic digest ntlm"
+ AUTH_MODULES="ntlm basic digest negotiate"
fi
])
if test -n "$AUTH_MODULES"; then
/*
- * $Id: ACL.h,v 1.13 2005/05/06 01:57:55 hno Exp $
+ * $Id: ACL.h,v 1.14 2005/10/23 11:55:31 hno Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
virtual bool requiresRequest() const;
virtual bool requiresReply() const;
virtual int match(ACLChecklist * checklist) = 0;
- virtual wordlist *dumpGeneric() const;
virtual wordlist *dump() const = 0;
virtual bool empty () const = 0;
virtual bool valid () const;
/*
- * $Id: ACLARP.cc,v 1.18 2005/05/08 22:35:03 hno Exp $
+ * $Id: ACLARP.cc,v 1.19 2005/10/23 11:55:31 hno Exp $
*
* DEBUG: section 28 Access Control
* AUTHOR: Duane Wessels
#else
- WRITE ME;
+#error "ARP type ACL not supported on this operating system."
#endif
/*
/*
- * $Id: ACLChecklist.cc,v 1.27 2005/10/16 14:52:52 serassio Exp $
+ * $Id: ACLChecklist.cc,v 1.28 2005/10/23 11:55:31 hno Exp $
*
* DEBUG: section 28 Access Control
* AUTHOR: Duane Wessels
}
/* get authed here */
- /* Note: this fills in auth_user_request when applicable (auth incomplete)*/
+ /* Note: this fills in auth_user_request when applicable */
switch (AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr)) {
case AUTH_ACL_CANNOT_AUTHENTICATE:
PF *callback_;
void *cbdata_;
debug(28, 3) ("ACLChecklist::checkCallback: %p answer=%d\n", this, answer);
+ /* During reconfigure, we can end up not finishing call
+ * sequences into the auth code */
+
+ if (auth_user_request) {
+ /* the checklist lock */
+ auth_user_request->unlock();
+ /* it might have been connection based */
+ assert(conn().getRaw() != NULL);
+ conn()->auth_user_request = NULL;
+ conn()->auth_type = AUTH_BROKEN;
+ auth_user_request = NULL;
+ }
callback_ = callback;
callback = NULL;
if (extacl_entry)
cbdataReferenceDone(extacl_entry);
- /* During reconfigure or if authentication is used in aclCheckFast without
- * first being authenticated in http_access we can end up not finishing call
- * sequences into the auth code. In such case we must make sure to forget
- * the authentication state completely
- */
- if (auth_user_request) {
- /* the checklist lock */
- auth_user_request->unlock();
- /* it might have been connection based */
- assert(conn().getRaw() != NULL);
- conn()->auth_user_request = NULL;
- conn()->auth_type = AUTH_BROKEN;
- auth_user_request = NULL;
- }
-
if (request)
requestUnlink(request);
/*
- * $Id: AuthUser.h,v 1.1 2004/08/30 03:28:56 robertc Exp $
+ * $Id: AuthUser.h,v 1.2 2005/10/23 11:55:31 hno Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
* but how many requests will a single username have in parallel? */
dlink_list requests;
-public:
static void cacheInit ();
static void CachedACLsReset();
/*
- * $Id: AuthUserRequest.cc,v 1.5 2005/10/16 16:47:02 serassio Exp $
+ * $Id: AuthUserRequest.cc,v 1.6 2005/10/23 11:55:31 hno Exp $
*
* DO NOT MODIFY NEXT 2 LINES:
* arch-tag: 6803fde1-d5a2-4c29-9034-1c0c9f650eb4
*/
if (proxy_auth && conn.getRaw() != NULL && conn->auth_user_request &&
authenticateUserAuthenticated(conn->auth_user_request) &&
+ conn->auth_user_request->connLastHeader() != NULL &&
strcmp(proxy_auth, conn->auth_user_request->connLastHeader()))
{
debug(28, 2) ("authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU %p, Current user '%s' proxy_auth %s\n", conn->auth_user_request, conn->auth_user_request->username(), proxy_auth);
conn.getRaw() != NULL ? conn->fd : -1);
if (proxy_auth && !request->auth_user_request && conn.getRaw() && conn->auth_user_request) {
- AuthScheme * id = AuthScheme::Find(proxy_auth);
+ AuthConfig * scheme = AuthConfig::Find(proxy_auth);
- if (!conn->auth_user_request->user() || AuthScheme::Find(conn->auth_user_request->user()->config->type()) != id) {
+ if (!conn->auth_user_request->user() || conn->auth_user_request->user()->config != scheme) {
debug(28, 1) ("authenticateAuthenticate: Unexpected change of authentication scheme from '%s' to '%s' (client %s)\n",
conn->auth_user_request->user()->config->type(), proxy_auth, inet_ntoa(src_addr));
conn->auth_user_request->unlock();
if (t && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE
&& t->lastReply != AUTH_ACL_HELPER)
{
- if (!*auth_user_request) {
+ if (!*auth_user_request)
*auth_user_request = t;
- (*auth_user_request)->lock()
-
- ;
- //TODO: check if needed. If there's a leak, it is not
- }
-
- if (!request->auth_user_request) {
- request->auth_user_request=t;
-
- request->auth_user_request->lock()
-
- ;
- }
-
return t->lastReply;
}
AuthUserRequest::lock()
{
- debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p'.\n", this);
- assert(this != NULL);
+ debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p' (%ld references).\n", this, (long int) references);
+ assert(this);
++references;
- debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p' now at '%ld'.\n", this, (long int) references);
}
void
AuthUserRequest::unlock()
{
- debug(29, 9) ("AuthUserRequest::unlock: auth_user request '%p'.\n", this);
+ debug(29, 9) ("AuthUserRequest::unlock: auth_user request '%p' (%ld references) .\n", this, (long int) references);
assert(this != NULL);
if (references > 0) {
debug(29, 1) ("Attempt to lower Auth User request %p refcount below 0!\n", this);
}
- debug(29, 9) ("AuthUserRequest::unlock: auth_user_request '%p' now at '%ld'.\n", this, (long int) references);
-
- if (references == 0)
+ if (references == 0) {
+ debug(29, 9) ("AuthUserRequest::unlock: deleting auth_user_request '%p'.\n", this);
/* not locked anymore */
delete this;
+ }
}
AuthScheme *
/*
- * $Id: AuthUserRequest.h,v 1.3 2005/05/06 01:57:55 hno Exp $
+ * $Id: AuthUserRequest.h,v 1.4 2005/10/23 11:55:31 hno Exp $
*
* DO NOT MODIFY NEXT 2 LINES:
* arch-tag: 674533af-8b21-4641-b71a-74c4639072a0
virtual void addHeader(HttpReply * rep, int accel);
virtual void addTrailer(HttpReply * rep, int accel);
virtual void onConnectionClose(ConnStateData *);
- virtual const char *connLastHeader();
/* template method */
virtual void module_start(RH *, void *) = 0;
virtual AuthUser *user() {return _auth_user;}
virtual void user (AuthUser *aUser) {_auth_user=aUser;}
-public:
-
static auth_acl_t tryToAuthenticateAndSetAuthUser(auth_user_request_t **, http_hdr_type, HttpRequest *, ConnStateData::Pointer, struct IN_ADDR);
static void addReplyAuthHeader(HttpReply * rep, auth_user_request_t * auth_user_request, HttpRequest * request, int accelerated, int internal);
void setDenyMessage (char const *);
char const * getDenyMessage ();
-
size_t refCount() const;
void lock ()
AuthScheme *scheme() const;
+ virtual const char * connLastHeader();
+
private:
static auth_acl_t authenticate(auth_user_request_t ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData::Pointer conn, struct IN_ADDR src_addr);
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.am,v 1.112 2005/10/16 19:57:40 serassio Exp $
+# $Id: Makefile.am,v 1.113 2005/10/23 11:55:31 hno Exp $
#
# Uncomment and customize the following to suit your needs:
#
auth/digest/digestScheme.cc \
auth/digest/digestScheme.h \
auth/ntlm/ntlmScheme.cc \
- auth/ntlm/ntlmScheme.h
+ auth/ntlm/ntlmScheme.h \
+ auth/negotiate/negotiateScheme.cc \
+ auth/negotiate/negotiateScheme.h
EXTRA_squid_SOURCES = \
$(all_FSMODULES) \
/*
- * $Id: acl.cc,v 1.315 2005/05/06 01:57:55 hno Exp $
+ * $Id: acl.cc,v 1.316 2005/10/23 11:55:32 hno Exp $
*
* DEBUG: section 28 Access Control
* AUTHOR: Duane Wessels
ACL::FindByName(const char *name)
{
ACL *a;
+ debug(28, 9) ("ACL::FindByName '%s'\n",name);
for (a = Config.aclList; a; a = a->next)
if (!strcasecmp(a->name, name))
return a;
+ debug(28,9) ("ACL::FindByName found no match\n");
+
return NULL;
}
acl_deny_info_list *A = NULL;
acl_name_list *L = NULL;
+ debug(28,9)("aclGetDenyInfoPage: got called for %s\n",name);
+
A = *head;
- if (NULL == *head) /* empty list */
+ if (NULL == *head) { /* empty list */
+ debug(28,9)("aclGetDenyInfoPage: called for an empty list\n");
return ERR_NONE;
+ }
while (A) {
L = A->acl_list;
- if (NULL == L) /* empty list should never happen, but in case */
+ if (NULL == L) { /* empty list should never happen, but in case */
+ debug(28,3)("aclGetDenyInfoPage: "
+ "WARNING, unexpected codepath taken\n");
continue;
+ }
while (L) {
- if (!strcmp(name, L->name))
+ if (!strcmp(name, L->name)) {
+ debug(28,8)("aclGetDenyInfoPage: match on %s\n",name);
return A->err_page_id;
+ }
L = L->next;
}
A = A->next;
}
+ debug(28,8)("aclGetDenyInfoPage: no match\n");
return ERR_NONE;
}
int
aclIsProxyAuth(const char *name)
{
+ debug(28,5)("aclIsProxyAuth: called for %s\n",name);
+
if (NULL == name)
return false;
ACL *a;
- if ((a = ACL::FindByName(name)))
+ if ((a = ACL::FindByName(name))) {
+ debug(28,5)("aclIsProxyAuth: returning %d\n",a->isProxyAuth());
return a->isProxyAuth();
+ }
+ debug(28,3)("aclIsProxyAuth: WARNING, called for nonexistent ACL\n");
return false;
}
/* first expect a page name */
if ((t = strtok(NULL, w_space)) == NULL) {
- debug(28, 0) ("%s line %d: %s\n",
+ debug(28, 0) ("aclParseDenyInfoLine: %s line %d: %s\n",
cfg_filename, config_lineno, config_input_line);
debug(28, 0) ("aclParseDenyInfoLine: missing 'error page' parameter.\n");
return;
}
if (A->acl_list == NULL) {
- debug(28, 0) ("%s line %d: %s\n",
+ debug(28, 0) ("aclParseDenyInfoLine: %s line %d: %s\n",
cfg_filename, config_lineno, config_input_line);
debug(28, 0) ("aclParseDenyInfoLine: deny_info line contains no ACL's, skipping\n");
memFree(A, MEM_ACL_DENY_INFO_LIST);
/* first expect either 'allow' or 'deny' */
if ((t = strtok(NULL, w_space)) == NULL) {
- debug(28, 0) ("%s line %d: %s\n",
+ debug(28, 0) ("aclParseAccessLine: %s line %d: %s\n",
cfg_filename, config_lineno, config_input_line);
debug(28, 0) ("aclParseAccessLine: missing 'allow' or 'deny'.\n");
return;
else if (!strcmp(t, "deny"))
A->allow = ACCESS_DENIED;
else {
- debug(28, 0) ("%s line %d: %s\n",
+ debug(28, 0) ("aclParseAccessLine: %s line %d: %s\n",
cfg_filename, config_lineno, config_input_line);
debug(28, 0) ("aclParseAccessLine: expecting 'allow' or 'deny', got '%s'.\n", t);
delete A;
* checked we check it and cache the result. This function is a template
* method to support caching of multiple acl types.
* Note that caching of time based acl's is not
- * wise in long lived caches (i.e. the auth_user proxy match cache.
+ * wise in long lived caches (i.e. the auth_user proxy match cache)
* RBC
+ * TODO: does a dlink_list perform well enough? Kinkie
*/
int
ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
auth_match = (acl_proxy_auth_match_cache *)link->data;
if (auth_match->acl_data == this) {
- debug(28, 4) ("ACL::cacheMatchAcl: cache hit on acl '%p'\n", this);
+ debug(28, 4) ("ACL::cacheMatchAcl: cache hit on acl '%s' (%p)\n", name, this);
return auth_match->matchrv;
}
auth_match->matchrv = matchForCache (checklist);
auth_match->acl_data = this;
dlinkAddTail(auth_match, &auth_match->link, cache);
+ debug(28,4)("ACL::cacheMatchAcl: miss for '%s'. Adding result %d\n",name,auth_match->matchrv);
return auth_match->matchrv;
}
dlink_node *link, *tmplink;
link = cache->head;
+ debug(28,8)("aclCacheMatchFlush called for cache %p\n",cache);
+
while (link) {
auth_match = (acl_proxy_auth_match_cache *)link->data;
tmplink = link;
int
ACL::checklistMatches(ACLChecklist *checklist)
{
+ int rv;
+
if (NULL == checklist->request && requiresRequest()) {
- debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
- " HTTP request -- not matching.\n", name);
+ debug(28, 1) ( "ACL::checklistMatches "
+ "WARNING: '%s' ACL is used but there is no"
+ " HTTP request -- not matching.\n", name);
return 0;
}
if (NULL == checklist->reply && requiresReply()) {
- debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
- " HTTP reply -- not matching.\n", name);
+ debug(28, 1) ( "ACL::checklistMatches "
+ "WARNING: '%s' ACL is used but there is no"
+ " HTTP reply -- not matching.\n", name);
return 0;
}
- debug(28, 3) ("aclMatchAcl: checking '%s'\n", cfgline);
- return match(checklist);
+ debug(28, 3) ("ACL::checklistMatches: checking '%s'\n", name);
+ rv= match(checklist);
+ debug(28,3) ("ACL::ChecklistMatches: result for '%s' is %d\n",name,rv);
+ return rv;
}
bool
op ? null_string : "!", _acl->name);
if (_acl->checklistMatches(checklist) != op) {
+ debug(28,4)("ACLList::matches: result is false\n");
return false;
}
+ debug(28,4)("ACLList::matches: result is true\n");
return true;
}
{
ACL *next = NULL;
+ debug(28,8)("aclDestroyACLs: invoked\n");
+
for (ACL *a = *head; a; a = next) {
next = a->next;
delete a;
ACL::~ACL()
{
- debug(28, 3) ("aclDestroyAcls: '%s'\n", cfgline);
+ debug(28, 3) ("ACL::~ACL: '%s'\n", cfgline);
safe_free(cfgline);
}
aclDestroyAclList(acl_list ** head)
{
acl_list *l;
+ debug(28,8)("aclDestroyAclList: invoked\n");
for (l = *head; l; l = *head) {
*head = l->next;
acl_name_list *l = NULL;
acl_name_list *l_next = NULL;
+ debug(28,8)("aclDestroyDenyInfoList: invoked\n");
+
for (a = *list; a; a = a_next) {
for (l = a->acl_list; l; l = l_next) {
l_next = l->next;
*list = NULL;
}
-wordlist *
-ACL::dumpGeneric () const
-{
- debug(28, 3) ("ACL::dumpGeneric: %s type %s\n", name, typeString());
- return dump();
-}
-
/*
* This function traverses all ACL elements referenced
* by an access list (presumably 'http_access'). If
acl_access const *a = this;
acl_list *b;
+ debug(28,6)("acl_access::containsPURGE: invoked for '%s'\n",cfgline);
+
for (; a; a = a->next) {
for (b = a->aclList; b; b = b->next) {
ACLStrategised<method_t> *tempAcl = dynamic_cast<ACLStrategised<method_t> *>(b->_acl);
- if (!tempAcl)
+ if (!tempAcl) {
+ debug(28,7)("acl_access::containsPURGE: can't create tempAcl\n");
continue;
+ }
- if (tempAcl->match(METHOD_PURGE))
+ if (tempAcl->match(METHOD_PURGE)) {
+ debug(28,6)("acl_access::containsPURGE: returning true\n");
return true;
+ }
}
}
+ debug(28,6)("acl_access::containsPURGE: returning false\n");
return false;
}
bool
ACL::Prototype::Registered(char const *aType)
{
+ debug(28,7)("ACL::Prototype::Registered: invoked for type %s\n",aType);
+
for (iterator i = Registry->begin(); i != Registry->end(); ++i)
- if (!strcmp (aType, (*i)->typeString))
+ if (!strcmp (aType, (*i)->typeString)) {
+ debug(28,7)("ACL::Prototype::Registered: yes\n");
return true;
+ }
+ debug(28,7)("ACL::Prototype::Registered: no\n");
return false;
}
ACL *
ACL::Prototype::Factory (char const *typeToClone)
{
+ debug(28,4)("ACL::Prototype::Factory: cloning an object for type '%s'\n",typeToClone);
+
for (iterator i = Registry->begin(); i != Registry->end(); ++i)
if (!strcmp (typeToClone, (*i)->typeString))
return (*i)->prototype->clone();
+ debug(28,4)("ACL::Prototype::Factory: cloning failed, no type '%s' available\n",typeToClone);
+
return NULL;
}
# Makefile for authentication modules in the Squid Object Cache server
#
-# $Id: Makefile.am,v 1.5 2005/08/20 21:08:38 serassio Exp $
+# $Id: Makefile.am,v 1.6 2005/10/23 11:55:38 hno Exp $
#
AUTOMAKE_OPTIONS = subdir-objects
AM_CFLAGS = @SQUID_CFLAGS@
##DIST_SUBDIRS = basic digest ntlm
##SUBDIRS = @AUTH_MODULES@
-EXTRA_LIBRARIES = libbasic.a libdigest.a libntlm.a
+EXTRA_LIBRARIES = libbasic.a libdigest.a libntlm.a libnegotiate.a
noinst_LIBRARIES = @AUTH_LIBS@
libbasic_a_SOURCES = basic/auth_basic.cc basic/auth_basic.h
libdigest_a_SOURCES = digest/auth_digest.cc digest/auth_digest.h
libntlm_a_SOURCES = ntlm/auth_ntlm.cc ntlm/auth_ntlm.h
+libnegotiate_a_SOURCES = negotiate/auth_negotiate.cc negotiate/auth_negotiate.h negotiate/negotiateScheme.cc negotiate/negotiateScheme.h
INCLUDES = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
-I$(top_srcdir)/src
--- /dev/null
+
+/*
+ * $Id: auth_negotiate.cc,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ * DEBUG: section 29 Negotiate Authenticator
+ * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/* The functions in this file handle authentication.
+ * They DO NOT perform access control or auditing.
+ * See acl.c for access control and client_side.c for auditing */
+
+
+#include "squid.h"
+#include "auth_negotiate.h"
+#include "authenticate.h"
+#include "Store.h"
+#include "client_side.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+/* TODO remove this include */
+#include "negotiateScheme.h"
+
+static void
+authenticateNegotiateReleaseServer(auth_user_request_t * auth_user_request);
+
+
+static void
+authenticateStateFree(authenticateStateData * r)
+{
+ cbdataFree(r);
+}
+
+/* Negotiate Scheme */
+static HLPSCB authenticateNegotiateHandleReply;
+static AUTHSSTATS authenticateNegotiateStats;
+
+static statefulhelper *negotiateauthenticators = NULL;
+
+CBDATA_TYPE(authenticateStateData);
+
+static int authnegotiate_initialised = 0;
+
+//static MemAllocatorProxy *negotiate_user_hash_pool = NULL;
+
+static auth_negotiate_config negotiateConfig;
+
+static hash_table *proxy_auth_cache = NULL;
+
+/*
+ *
+ * Private Functions
+ *
+ */
+
+/* move to negotiateScheme.cc */
+void
+negotiateScheme::done()
+{
+ /* TODO: this should be a Config call. */
+ debug(29, 2) ("negotiateScheme::done: shutting down Negotiate authentication.\n");
+
+ if (negotiateauthenticators)
+ helperStatefulShutdown(negotiateauthenticators);
+
+ authnegotiate_initialised = 0;
+
+ if (!shutting_down)
+ return;
+
+ if (negotiateauthenticators)
+ helperStatefulFree(negotiateauthenticators);
+
+ negotiateauthenticators = NULL;
+
+ debug(29, 2) ("negotiateScheme::done: Negotiate authentication Shutdown.\n");
+}
+
+/* free any allocated configuration details */
+void
+AuthNegotiateConfig::done()
+{
+ if (authenticate)
+ wordlistDestroy(&authenticate);
+}
+
+void
+AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
+{
+ wordlist *list = authenticate;
+ storeAppendPrintf(entry, "%s %s", name, "negotiate");
+
+ while (list != NULL) {
+ storeAppendPrintf(entry, " %s", list->key);
+ list = list->next;
+ }
+
+ storeAppendPrintf(entry, "\n%s negotiate children %d\n",
+ name, authenticateChildren);
+ storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off");
+
+}
+
+AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(5), keep_alive(1)
+{ }
+
+void
+AuthNegotiateConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
+{
+ if (strcasecmp(param_str, "program") == 0) {
+ if (authenticate)
+ wordlistDestroy(&authenticate);
+
+ parse_wordlist(&authenticate);
+
+ requirePathnameExists("authparam negotiate program", authenticate->key);
+ } else if (strcasecmp(param_str, "children") == 0) {
+ parse_int(&authenticateChildren);
+ } else if (strcasecmp(param_str, "keep_alive") == 0) {
+ parse_onoff(&keep_alive);
+ } else {
+ debug(28, 0) ("AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '%s'\n", param_str);
+ }
+
+ /*
+ * disable client side request pipelining. There is a race with
+ * Negotiate when the client sends a second request on an Negotiate
+ * connection before the authenticate challenge is sent. With
+ * this patch, the client may fail to authenticate, but squid's
+ * state will be preserved. Caveats: this should be a post-parse
+ * test, but that can wait for the modular parser to be integrated.
+ */
+ if (authenticate)
+ Config.onoff.pipeline_prefetch = 0;
+}
+
+const char *
+AuthNegotiateConfig::type() const
+{
+ return negotiateScheme::GetInstance().type();
+}
+
+/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
+ * config file */
+void
+AuthNegotiateConfig::init(AuthConfig * scheme)
+{
+ static unsigned char negotiate_was_already_initialised = 0;
+
+ if (authenticate) {
+#if PLACEHOLDER
+
+ if (!negotiate_user_hash_pool)
+
+ negotiate_user_hash_pool = new MemAllocatorProxy("Negotiate Header Hash Data", sizeof(struct ProxyAuthCachePointer));
+
+#endif
+
+ authnegotiate_initialised = 1;
+
+ if (negotiateauthenticators == NULL)
+ negotiateauthenticators = helperStatefulCreate("negotiateauthenticator");
+
+ if (!proxy_auth_cache)
+ proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
+
+ assert(proxy_auth_cache);
+
+ negotiateauthenticators->cmdline = authenticate;
+
+ negotiateauthenticators->n_to_start = authenticateChildren;
+
+ negotiateauthenticators->ipc_type = IPC_STREAM;
+
+ helperStatefulOpenServers(negotiateauthenticators);
+
+ if (!negotiate_was_already_initialised) {
+ cachemgrRegister("negotiateauthenticator",
+ "Negotiate User Authenticator Stats",
+ authenticateNegotiateStats, 0, 1);
+ negotiate_was_already_initialised++;
+ }
+
+ CBDATA_INIT_TYPE(authenticateStateData);
+ }
+}
+
+bool
+AuthNegotiateConfig::active() const
+{
+ return authnegotiate_initialised == 1;
+}
+
+bool
+AuthNegotiateConfig::configured() const
+{
+ if ((authenticate != NULL) && (authenticateChildren != 0)) {
+ debug(29, 9) ("AuthNegotiateConfig::configured: returning configured\n");
+ return true;
+ }
+
+ debug(29, 9) ("AuthNegotiateConfig::configured: returning unconfigured\n");
+ return false;
+}
+
+/* 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:
+ debug(29, 1) ("AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request %p!. Report a bug to squid-dev.\n",this);
+ 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_FINISHED:
+ return 0; /* do nothing */
+
+ case AUTHENTICATE_STATE_DONE:
+ return 0; /* do nothing */
+
+ case AUTHENTICATE_STATE_INITIAL:
+ debug(29, 1) ("AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+ 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(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
+{
+ AuthNegotiateUserRequest *negotiate_request;
+
+ if (!request->flags.proxy_keepalive)
+ return;
+
+ if (!authenticate)
+ return;
+
+ /* New request, no user details */
+ if (auth_user_request == NULL) {
+ debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'NEGOTIATE'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "NEGOTIATE");
+
+ if (!keep_alive) {
+ /* drop the connection */
+ httpHeaderDelByName(&rep->header, "keep-alive");
+ request->flags.proxy_keepalive = 0;
+ }
+ } else {
+ negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
+
+ switch (negotiate_request->auth_state) {
+
+ case AUTHENTICATE_STATE_FAILED:
+ /* here it makes sense to drop the connection, as auth is
+ * tied to it, even if MAYBE the client could handle it - Kinkie */
+ httpHeaderDelByName(&rep->header, "keep-alive");
+ request->flags.proxy_keepalive = 0;
+ /* fall through */
+
+ case AUTHENTICATE_STATE_FINISHED:
+ /* Special case: authentication finished OK but disallowed by ACL.
+ * Need to start over to give the client another chance.
+ */
+
+ if (negotiate_request->server_blob) {
+ debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob);
+ httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
+ safe_free(negotiate_request->server_blob);
+ } else {
+ debug(29, 9) ("authenticateNegotiateFixErrorHeader: Connection authenticated\n");
+ httpHeaderPutStrf(&rep->header, type, "Negotiate");
+ }
+
+ break;
+
+ case AUTHENTICATE_STATE_NONE:
+ /* semantic change: do not drop the connection.
+ * 2.5 implementation used to keep it open - Kinkie */
+ debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'NEGOTIATE'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "Negotiate");
+ break;
+
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ /* we're waiting for a response from the client. Pass it the blob */
+ debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob);
+ httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
+ request->flags.must_keepalive = 1;
+ safe_free(negotiate_request->server_blob);
+ break;
+
+
+ default:
+ debug(29, 0) ("AuthNegotiateConfig::fixHeader: state %d.\n", negotiate_request->auth_state);
+ fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
+ }
+ }
+}
+
+NegotiateUser::~NegotiateUser()
+{
+ debug(29, 5) ("NegotiateUser::~NegotiateUser: doing nothing to clearNEGOTIATE scheme data for '%p'\n",this);
+}
+
+static stateful_helper_callback_t
+authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+ int valid;
+ stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+ char *blob, *arg = NULL;
+
+ auth_user_request_t *auth_user_request;
+ AuthUser *auth_user;
+ NegotiateUser *negotiate_user;
+ AuthNegotiateUserRequest *negotiate_request;
+
+ debug(29, 8) ("authenticateNegotiateHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>");
+ valid = cbdataReferenceValid(data);
+
+ if (!valid) {
+ debug(29, 1) ("authenticateNegotiateHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ debug(29, 9) ("authenticateNegotiateHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE);
+ return S_HELPER_RELEASE;
+ }
+
+ if (!reply) {
+ /*
+ * TODO: this occurs when a helper crashes. We should clean
+ * up that helpers resources and queued requests.
+ */
+ fatal("authenticateNegotiateHandleReply: called with no result string\n");
+ }
+
+ auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+ negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
+
+ assert(negotiate_request->waiting);
+ negotiate_request->waiting = 0;
+ safe_free(negotiate_request->client_blob);
+
+ auth_user = negotiate_request->user();
+ assert(auth_user != NULL);
+ assert(auth_user->auth_type == AUTH_NEGOTIATE);
+ negotiate_user = dynamic_cast<negotiate_user_t *>(auth_user_request->user());
+
+ 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, ' ');
+
+ while (xisspace(*blob)) { // trim leading spaces in blob
+ blob++;
+ arg = strchr(blob + 1, ' ');
+ }
+
+ if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+ /* we have been given a blob to send to the client */
+
+ if (arg)
+ *arg++ = '\0';
+
+ safe_free(negotiate_request->server_blob);
+
+ negotiate_request->server_blob = xstrdup(blob);
+
+ negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+
+ auth_user_request->denyMessage("Authenication in progress");
+
+ debug(29, 4) ("authenticateNegotiateHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+
+ result = S_HELPER_RESERVE;
+ } else if (strncasecmp(reply, "AF ", 3) == 0 && blob != 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);
+
+ authenticateNegotiateReleaseServer(negotiate_request);
+
+ negotiate_request->auth_state = AUTHENTICATE_STATE_FINISHED;
+
+ result = S_HELPER_RELEASE;
+
+ debug(29, 4) ("authenticateNegotiateHandleReply: Successfully validated user via NEGOTIATE. Username '%s'\n", blob);
+ } else if (strncasecmp(reply, "NA ", 3) == 0 && blob != 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);
+
+ authenticateNegotiateReleaseServer(negotiate_request);
+
+ result = S_HELPER_RELEASE;
+
+ debug(29, 4) ("authenticateNegotiateHandleReply: Failed validating user via NEGOTIATE. Error returned '%s'\n", 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);
+ authenticateNegotiateReleaseServer(negotiate_request);
+ result = S_HELPER_RELEASE;
+ debug(29, 1) ("authenticateNegotiateHandleReply: Error validating user via NEGOTIATE. Error returned '%s'\n", reply);
+ } else {
+ /* protocol error */
+ fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ r->handler(r->data, NULL);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ debug(29, 9) ("authenticateNegotiateHandleReply: telling stateful helper : %d\n", result);
+ return result;
+}
+
+static void
+authenticateNegotiateStats(StoreEntry * sentry)
+{
+ storeAppendPrintf(sentry, "NEGOTIATE Authenticator Statistics:\n");
+ helperStatefulStats(sentry, negotiateauthenticators);
+}
+
+
+/* send the initial data to a stateful negotiate authenticator module */
+void
+AuthNegotiateUserRequest::module_start(RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ static char buf[8192];
+ negotiate_user_t *negotiate_user;
+ auth_user_t *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());
+
+ debug(29, 8) ("AuthNegotiateUserRequest::module_start: auth state is '%d'\n", auth_state);
+
+ if (negotiateConfig.authenticate == NULL) {
+ debug(29, 0) ("AuthNegotiateUserRequest::module_start: no NEGOTIATE program specified.");
+ handler(data, NULL);
+ return;
+ }
+
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ cbdataReference(data);
+ r->data = data;
+ r->auth_user_request = this;
+
+ lock()
+
+ ;
+ 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(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
+}
+
+/* clear the NEGOTIATE helper of being reserved for future requests */
+static void
+authenticateNegotiateReleaseServer(auth_user_request_t * auth_user_request)
+{
+ AuthNegotiateUserRequest *negotiate_request;
+ assert(auth_user_request->user()->auth_type == AUTH_NEGOTIATE);
+ negotiate_request = dynamic_cast< AuthNegotiateUserRequest *>(auth_user_request);
+ debug(29, 9) ("authenticateNegotiateReleaseServer: releasing server '%p'\n", negotiate_request->authserver);
+ /* is it possible for the server to be NULL? hno seems to think so.
+ * Let's see what happens, might segfault in helperStatefulReleaseServer
+ * if it does. I leave it like this not to cover possibly problematic
+ * code-paths. Kinkie */
+ helperStatefulReleaseServer(negotiate_request->authserver);
+ negotiate_request->authserver = NULL;
+}
+
+/* clear any connection related authentication details */
+void
+AuthNegotiateUserRequest::onConnectionClose(ConnStateData *connection)
+{
+ assert(connection != NULL);
+
+ debug(29,8)("AuthNegotiateUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this);
+
+ if (connection->auth_user_request == NULL) {
+ debug(29,8)("AuthNegotiateUserRequest::onConnectionClose: no auth_user_request\n");
+ return;
+ }
+
+ if (authserver != NULL)
+ authenticateNegotiateReleaseServer(this);
+
+ /* unlock the connection based lock */
+ debug(29, 9) ("AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection);
+
+ /* This still breaks the abstraction, but is at least read only now.
+ * If needed, this could be ignored, as the conn deletion will also unlock
+ * the auth user request.
+ */
+ unlock();
+
+ connection->auth_user_request = NULL;
+}
+
+/*
+ * Decode a NEGOTIATE [Proxy-]Auth string, placing the results in the passed
+ * Auth_user structure.
+ */
+AuthUserRequest *
+AuthNegotiateConfig::decode(char const *proxy_auth)
+{
+ NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
+ AuthNegotiateUserRequest *auth_user_request = new AuthNegotiateUserRequest ();
+ assert(auth_user_request->user() == NULL);
+ auth_user_request->user(newUser);
+ auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
+ auth_user_request->user()->addRequest(auth_user_request);
+
+ /* all we have to do is identify that it's NEGOTIATE - the helper does the rest */
+ debug(29, 9) ("AuthNegotiateConfig::decode: NEGOTIATE authentication\n");
+ return auth_user_request;
+}
+
+int
+AuthNegotiateUserRequest::authenticated() const
+{
+ if (auth_state == AUTHENTICATE_STATE_FINISHED) {
+ debug(29, 9) ("AuthNegotiateUserRequest::authenticated: user authenticated.\n");
+ return 1;
+ }
+
+ debug(29, 9) ("AuthNegotiateUserRequest::authenticated: user not fully authenticated.\n");
+
+ return 0;
+}
+
+void
+AuthNegotiateUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
+{
+ const char *proxy_auth, *blob;
+
+ //ProxyAuthCachePointer *proxy_auth_hash = NULL;
+ auth_user_hash_pointer *usernamehash;
+
+ /* TODO: rename this!! */
+ auth_user_t *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.getRaw() == NULL) {
+ auth_state = AUTHENTICATE_STATE_FAILED;
+ debug(29, 1) ("AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!\n");
+ return;
+ }
+
+ if (waiting) {
+ debug(29, 1) ("AuthNegotiateUserRequest::authenticate: waiting for helper reply!\n");
+ return;
+ }
+
+ if (server_blob) {
+ debug(29,2)("AuthNegotiateUserRequest::authenticate: need to challenge client '%s'!\n", server_blob);
+ return;
+ }
+
+ /* get header */
+ proxy_auth = httpHeaderGetStr(&request->header, type);
+
+ blob = proxy_auth + strlen("Negotiate");
+
+ while (xisspace(*blob)) // trim leading spaces in blob
+ blob++;
+
+ switch (auth_state) {
+
+ case AUTHENTICATE_STATE_NONE:
+ /* we've recieved a negotiate request. pass to a helper */
+ debug(29, 9) ("AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '%s'\n", proxy_auth);
+ auth_state = AUTHENTICATE_STATE_INITIAL;
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ conn->auth_type = AUTH_NEGOTIATE;
+ conn->auth_user_request = this;
+ conn = conn;
+
+ lock()
+
+ ;
+ return;
+
+ break;
+
+ case AUTHENTICATE_STATE_INITIAL:
+ debug(29,1)("AuthNegotiateUserRequest::authenticate: need to ask helper\n");
+
+ 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);
+
+ return;
+
+ break;
+
+ case AUTHENTICATE_STATE_FINISHED:
+ /* connection is authenticated */
+ debug(29, 4) ("AuthNegotiateUserRequest::authenticate: authenticated user %s\n", negotiate_user->username());
+
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
+
+ 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();
+ _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;
+
+ authenticateNegotiateReleaseServer(this);
+
+ auth_state = AUTHENTICATE_STATE_DONE;
+
+ 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 */
+ debug(29, 9) ("AuthNegotiateUserRequest::authenticate: auth state negotiate failed. %s\n", proxy_auth);
+
+ return;
+
+ break;
+ }
+
+ return;
+}
+
+AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
+ conn(NULL), auth_state(AUTHENTICATE_STATE_NONE),
+ _theUser(NULL)
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+}
+
+AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
+{
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ if (authserver != NULL) {
+ debug(29, 9) ("AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '%p'\n", authserver);
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ }
+}
+
+void
+NegotiateUser::deleteSelf() const
+{
+ delete this;
+}
+
+NegotiateUser::NegotiateUser (AuthConfig *config) : AuthUser (config)
+{
+ proxy_auth_list.head = proxy_auth_list.tail = NULL;
+}
+
+AuthConfig *
+negotiateScheme::createConfig()
+{
+ return &negotiateConfig;
+}
+
+const char *
+AuthNegotiateUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
--- /dev/null
+/*
+ * auth_negotiate.h
+ * Internal declarations for the negotiate auth module
+ */
+
+#ifndef __AUTH_NEGOTIATE_H__
+#define __AUTH_NEGOTIATE_H__
+#include "authenticate.h"
+#include "AuthUser.h"
+#include "AuthUserRequest.h"
+#include "AuthConfig.h"
+
+#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
+
+typedef enum {
+ AUTHENTICATE_STATE_NONE,
+ AUTHENTICATE_STATE_INITIAL,
+ AUTHENTICATE_STATE_IN_PROGRESS,
+ AUTHENTICATE_STATE_FINISHED,
+ AUTHENTICATE_STATE_DONE,
+ AUTHENTICATE_STATE_FAILED
+} auth_state_t; /* connection level auth state */
+
+/* Generic */
+
+typedef struct
+{
+ void *data;
+ auth_user_request_t *auth_user_request;
+ RH *handler;
+}
+
+authenticateStateData;
+
+class NegotiateUser : public AuthUser
+{
+
+public:
+ MEMPROXY_CLASS(NegotiateUser);
+ virtual void deleteSelf() const;
+ NegotiateUser(AuthConfig *);
+ ~NegotiateUser();
+ dlink_list proxy_auth_list;
+};
+
+MEMPROXY_CLASS_INLINE(NegotiateUser)
+
+typedef class NegotiateUser negotiate_user_t;
+
+class AuthNegotiateUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthNegotiateUserRequest);
+
+ AuthNegotiateUserRequest();
+ virtual ~AuthNegotiateUserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void onConnectionClose(ConnStateData *);
+ virtual void module_start(RH *, void *);
+ virtual AuthUser *user() {return _theUser;}
+
+ virtual const AuthUser *user() const {return _theUser;}
+
+ virtual void addHeader(HttpReply * rep, int accel);
+
+ virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NegotiateUser *>(aUser);}
+
+ virtual const char * connLastHeader();
+
+ /*we need to store the helper server between requests */
+ helper_stateful_server *authserver;
+ /* what connection is this associated with */
+ ConnStateData::Pointer 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;
+
+private:
+ /* the user */
+ NegotiateUser * _theUser;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest)
+
+/* configuration runtime data */
+
+class AuthNegotiateConfig : public AuthConfig
+{
+
+public:
+ AuthNegotiateConfig::AuthNegotiateConfig();
+ virtual bool active() const;
+ virtual bool configured() const;
+ virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual void done();
+ virtual void dump(StoreEntry *, const char *, AuthConfig *);
+ virtual void fixHeader(auth_user_request_t *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void init(AuthConfig *);
+ virtual void parse(AuthConfig *, int, char *);
+ virtual const char * type() const;
+ int authenticateChildren;
+ int keep_alive;
+ wordlist *authenticate;
+};
+
+typedef class AuthNegotiateConfig auth_negotiate_config;
+
+#endif
--- /dev/null
+
+/*
+ * $Id: negotiateScheme.cc,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "negotiateScheme.h"
+
+AuthScheme &
+negotiateScheme::GetInstance()
+{
+ return _instance;
+}
+
+negotiateScheme::negotiateScheme()
+{
+ AddScheme(*this);
+}
+
+char const *
+negotiateScheme::type () const
+{
+ return "negotiate";
+}
+
+negotiateScheme negotiateScheme::_instance;
--- /dev/null
+
+/*
+ * $Id: negotiateScheme.h,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef SQUID_NEGOTIATESCHEME_H
+#define SQUID_NEGOTIATESCHEME_H
+
+#include "AuthScheme.h"
+
+class negotiateScheme : public AuthScheme
+{
+
+public:
+ static AuthScheme &GetInstance();
+ negotiateScheme();
+ virtual ~negotiateScheme(){};
+
+ /* per scheme */
+ virtual char const *type () const;
+ virtual void done();
+ virtual AuthConfig *createConfig();
+ /* Not implemented */
+ negotiateScheme (negotiateScheme const &);
+ negotiateScheme &operator=(negotiateScheme const &);
+
+private:
+ static negotiateScheme _instance;
+};
+
+#endif /* SQUID_negotiateSCHEME_H */
/*
- * $Id: auth_ntlm.cc,v 1.49 2005/10/16 16:47:02 serassio Exp $
+ * $Id: auth_ntlm.cc,v 1.50 2005/10/23 11:55:38 hno Exp $
*
* DEBUG: section 29 NTLM Authenticator
- * AUTHOR: Robert Collins
+ * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
* ----------------------------------------------------------
/* TODO remove this include */
#include "ntlmScheme.h"
+static void
+authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request);
+
+
static void
authenticateStateFree(authenticateStateData * r)
{
- r->auth_user_request->unlock();
- r->auth_user_request = NULL;
cbdataFree(r);
}
/* NTLM Scheme */
static HLPSCB authenticateNTLMHandleReply;
-static HLPSCB authenticateNTLMHandleplaceholder;
static AUTHSSTATS authenticateNTLMStats;
-/* helper callbacks to handle per server state data */
-static HLPSAVAIL authenticateNTLMHelperServerAvailable;
-static HLPSONEQ authenticateNTLMHelperServerOnEmpty;
-
static statefulhelper *ntlmauthenticators = NULL;
CBDATA_TYPE(authenticateStateData);
static int authntlm_initialised = 0;
-static MemAllocatorProxy *ntlm_helper_state_pool = NULL;
-static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
+//static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
static auth_ntlm_config ntlmConfig;
ntlmScheme::done()
{
/* TODO: this should be a Config call. */
- debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
+ debug(29, 2) ("ntlmScheme::done: shutting down NTLM authentication.\n");
if (ntlmauthenticators)
helperStatefulShutdown(ntlmauthenticators);
ntlmauthenticators = NULL;
-#if DEBUGSHUTDOWN
-
- if (ntlm_helper_state_pool) {
- delete ntlm_helper_state_pool;
- ntlm_helper_state_pool = NULL;
- }
-
- /* Removed for some reason..
- if (ntlm_user_pool) {
- delete ntlm_user_pool;ntlm_user_pool = NULL;
- }
- */
-
-#endif
- debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
+ debug(29, 2) ("ntlmScheme::done: NTLM authentication Shutdown.\n");
}
/* free any allocated configuration details */
list = list->next;
}
- storeAppendPrintf(entry, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
- name, "ntlm", authenticateChildren,
- name, "ntlm", challengeuses,
- name, "ntlm", (int) challengelifetime);
+ storeAppendPrintf(entry, "\n%s ntlm children %d\n",
+ name, authenticateChildren);
+ storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "ntlm", keep_alive ? "on" : "off");
}
-AuthNTLMConfig::AuthNTLMConfig()
-{
- /* TODO Move into initialisation list */
- authenticateChildren = 5;
- challengeuses = 0;
- challengelifetime = 60;
-}
+AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1)
+{ }
void
AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
requirePathnameExists("authparam ntlm program", authenticate->key);
} else if (strcasecmp(param_str, "children") == 0) {
parse_int(&authenticateChildren);
- } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
- parse_int(&challengeuses);
- } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
- parse_time_t(&challengelifetime);
+ } else if (strcasecmp(param_str, "keep_alive") == 0) {
+ parse_onoff(&keep_alive);
} else {
- debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str);
+ debug(28, 0) ("AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '%s'\n", param_str);
}
/*
void
AuthNTLMConfig::init(AuthConfig * scheme)
{
- static int ntlminit = 0;
+ static unsigned char ntlm_was_already_initialised = 0;
if (authenticate) {
- if (!ntlm_helper_state_pool)
- ntlm_helper_state_pool = new MemAllocatorProxy("NTLM Helper State data", sizeof(ntlm_helper_state_t));
+#if PLACEHOLDER
if (!ntlm_user_hash_pool)
ntlm_user_hash_pool = new MemAllocatorProxy("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer));
+#endif
+
authntlm_initialised = 1;
if (ntlmauthenticators == NULL)
ntlmauthenticators->ipc_type = IPC_STREAM;
- ntlmauthenticators->datapool = ntlm_helper_state_pool;
-
- ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
-
- ntlmauthenticators->OnEmptyQueue = authenticateNTLMHelperServerOnEmpty;
-
helperStatefulOpenServers(ntlmauthenticators);
- /*
- * TODO: In here send the initial YR to preinitialise the
- * challenge cache
- */
- /*
- * Think about this... currently we ask when the challenge
- * is needed. Better?
- */
- if (!ntlminit) {
+ if (!ntlm_was_already_initialised) {
cachemgrRegister("ntlmauthenticator",
"NTLM User Authenticator Stats",
authenticateNTLMStats, 0, 1);
- ntlminit++;
+ ntlm_was_already_initialised++;
}
CBDATA_INIT_TYPE(authenticateStateData);
bool
AuthNTLMConfig::configured() const
{
- if ((authenticate != NULL) && (authenticateChildren != 0) && (challengeuses > -1) && (challengelifetime > -1)) {
- debug(29, 9) ("authNTLMConfigured: returning configured\n");
+ if ((authenticate != NULL) && (authenticateChildren != 0)) {
+ debug(29, 9) ("AuthNTLMConfig::configured: returning configured\n");
return true;
}
- debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
+ debug(29, 9) ("AuthNTLMConfig::configured: returning unconfigured\n");
return false;
}
/* 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:
- debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate!. Report a bug to squid-dev.\n");
- /* fall thru */
+ debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate for request %p!. Report a bug to squid-dev.\n",this);
+ return -2; /* error */
case AUTHENTICATE_STATE_FAILED:
- return -2;
+ return -2; /* error */
- /* send to helper */
- case AUTHENTICATE_STATE_NEGOTIATE:
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ assert(server_blob);
+ return 1; /* send to client */
- /*send to helper */
-
- case AUTHENTICATE_STATE_RESPONSE:
- return -1;
-
- /* send to client */
-
- case AUTHENTICATE_STATE_CHALLENGE:
- return 1;
-
- /* do nothing.. */
+ case AUTHENTICATE_STATE_FINISHED:
+ return 0; /* do nothing */
case AUTHENTICATE_STATE_DONE:
- return 0;
+ return 0; /* do nothing */
+
+ case AUTHENTICATE_STATE_INITIAL:
+ debug(29, 1) ("AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+ return -2;
}
return -2;
}
-/*
- * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
- * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
- * else.
- */
void
AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
{
if (!request->flags.proxy_keepalive)
return;
- if (authenticate) {
- /* New request, no user details */
+ if (!authenticate)
+ return;
- if (auth_user_request == NULL) {
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
- httpHeaderPutStrf(&rep->header, type, "NTLM");
+ /* New request, no user details */
+ if (auth_user_request == NULL) {
+ debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+
+ if (!keep_alive) {
/* drop the connection */
httpHeaderDelByName(&rep->header, "keep-alive");
- /* NTLM has problems if the initial connection is not dropped
- * I haven't checked the RFC compliance of this hack - RBCollins */
request->flags.proxy_keepalive = 0;
- } else {
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert (ntlm_request);
-
- switch (ntlm_request->auth_state) {
-
- case AUTHENTICATE_STATE_NONE:
-
- case AUTHENTICATE_STATE_FAILED:
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
- httpHeaderPutStrf(&rep->header, type, "NTLM");
- /* drop the connection */
- httpHeaderDelByName(&rep->header, "keep-alive");
- /* NTLM has problems if the initial connection is not dropped
- * I haven't checked the RFC compliance of this hack - RBCollins */
- request->flags.proxy_keepalive = 0;
- break;
-
- case AUTHENTICATE_STATE_CHALLENGE:
- /* we are 'waiting' for a response */
- /* pass the challenge to the client */
- debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->authchallenge);
- httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->authchallenge);
- request->flags.must_keepalive = 1;
- break;
-
- default:
- debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
- fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
- }
}
- }
-}
+ } else {
+ ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
-NTLMUser::~NTLMUser()
-{
- dlink_node *link, *tmplink;
- ProxyAuthCachePointer *proxy_auth_hash;
- debug(29, 5) ("NTLMUser::~NTLMUser: Clearing NTLM scheme data\n");
-
- /* were they linked in by one or more proxy-authenticate headers */
- link = proxy_auth_list.head;
-
- while (link) {
- debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link->data);
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(link->data);
- tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &proxy_auth_list);
- hash_remove_link(proxy_auth_cache, (hash_link *) proxy_auth_hash);
- /* free the key (usually the proxy_auth header) */
- xfree(proxy_auth_hash->key);
- ntlm_user_hash_pool->free(proxy_auth_hash);
- }
+ switch (ntlm_request->auth_state) {
-}
+ case AUTHENTICATE_STATE_FAILED:
+ /* here it makes sense to drop the connection, as auth is
+ * tied to it, even if MAYBE the client could handle it - Kinkie */
+ httpHeaderDelByName(&rep->header, "keep-alive");
+ request->flags.proxy_keepalive = 0;
+ /* fall through */
-static stateful_helper_callback_t
-authenticateNTLMHandleplaceholder(void *data, void *lastserver, char *reply)
-{
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
- stateful_helper_callback_t result = S_HELPER_UNKNOWN;
- /* we should only be called for placeholder requests - which have no reply string */
- assert(reply == NULL);
- assert(r->auth_user_request);
- /* standard callback stuff */
-
- if (!cbdataReferenceValid(r->data)) {
- debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
- return result;
- }
+ case AUTHENTICATE_STATE_FINISHED:
+ /* Special case: authentication finished OK but disallowed by ACL.
+ * Need to start over to give the client another chance.
+ */
+ /* fall through */
- /* call authenticateNTLMStart to retry this request */
- debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
+ case AUTHENTICATE_STATE_NONE:
+ /* semantic change: do not drop the connection.
+ * 2.5 implementation used to keep it open - Kinkie */
+ debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+ break;
- r->auth_user_request->start(r->handler, r->data);
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ /* we're waiting for a response from the client. Pass it the blob */
+ debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->server_blob);
+ httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob);
+ request->flags.must_keepalive = 1;
+ safe_free(ntlm_request->server_blob);
+ break;
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ default:
+ debug(29, 0) ("AuthNTLMConfig::fixHeader: state %d.\n", ntlm_request->auth_state);
+ fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
+ }
+ }
+}
- return result;
+NTLMUser::~NTLMUser()
+{
+ debug(29, 5) ("NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '%p'\n",this);
}
static stateful_helper_callback_t
authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
{
authenticateStateData *r = static_cast<authenticateStateData *>(data);
- ntlm_helper_state_t *helperstate;
+
+ int valid;
stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+ char *blob;
+
auth_user_request_t *auth_user_request;
- auth_user_t *auth_user;
- ntlm_user_t *ntlm_user;
+ AuthUser *auth_user;
+ NTLMUser *ntlm_user;
AuthNTLMUserRequest *ntlm_request;
- debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver, reply ? reply : "<NULL>");
- if (!cbdataReferenceValid(r->data)) {
- debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
+ debug(29, 8) ("authenticateNTLMHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>");
+ valid = cbdataReferenceValid(data);
+
+ if (!valid) {
+ debug(29, 1) ("authenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
cbdataReferenceDone(r->data);
authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE);
+ debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE);
return S_HELPER_RELEASE;
}
fatal("authenticateNTLMHandleReply: called with no result string\n");
}
- /* seperate out the useful data */
- if (strncasecmp(reply, "TT ", 3) == 0) {
- reply += 3;
- /* we have been given a Challenge */
- /* we should check we weren't given an empty challenge */
- /* copy the challenge to the state data */
- helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(static_cast<helper_stateful_server *>(lastserver)));
-
- if (helperstate == NULL)
- fatal("lost NTLM helper state! quitting\n");
-
- helperstate->challenge = xstrdup(reply);
-
- helperstate->challengeuses = 0;
-
- helperstate->renewed = squid_curtime;
-
- /* and we satisfy the request that happended on the refresh boundary */
- /* note this code is now in two places FIXME */
- assert(r->auth_user_request != NULL);
-
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-
- auth_user_request = r->auth_user_request;
-
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-
- assert(ntlm_request != NULL);
+ auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+ ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
- result = S_HELPER_DEFER;
+ assert(ntlm_request->waiting);
+ ntlm_request->waiting = 0;
+ safe_free(ntlm_request->client_blob);
- /* reserve the server for future authentication */
- ntlm_request->authserver_deferred = 1;
+ 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());
- debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver);
+ if (ntlm_request->authserver == NULL)
+ ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(ntlm_request->authserver == lastserver);
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
- ntlm_request->authserver = static_cast<helper_stateful_server *>(lastserver);
+ while (xisspace(*blob)) { // trim leading spaces in blob
+ blob++;
+ }
- ntlm_request->authchallenge = xstrdup(reply);
- } else if (strncasecmp(reply, "AF ", 3) == 0) {
+ if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+ /* we have been given a blob to send to the client */
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->server_blob = xstrdup(blob);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+ auth_user_request->denyMessage("Authenication in progress");
+ debug(29, 4) ("authenticateNTLMHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+ result = S_HELPER_RESERVE;
+ } else if (strncasecmp(reply, "AF ", 3) == 0 && blob != NULL) {
/* we're finished, release the helper */
- reply += 3;
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert(ntlm_request);
- auth_user = auth_user_request->user();
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
- assert(ntlm_user != NULL);
- result = S_HELPER_RELEASE;
- /* we only expect OK when finishing the handshake */
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
- ntlm_user->username(xstrdup(reply));
- ntlm_request->authserver = NULL;
-#ifdef NTLM_FAIL_OPEN
-
- } else if (strncasecmp(reply, "LD ", 3) == 0) {
- /* This is a variant of BH, which rather than deny access
- * allows the user through. The helper is starved and then refreshed
- * via YR, all pending authentications are likely to fail also.
- * It is meant for those helpers which occasionally fail for
- * no reason at all (casus belli, NTLMSSP helper on NT domain,
- * failing about 1 auth out of 1k.
- * The code is a merge from the BH case with snippets of the AF
- * case */
- /* AF code: mark user as authenticated */
- reply += 3;
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert(ntlm_request);
- auth_user = auth_user_request->user();
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
- assert(ntlm_user != NULL);
- result = S_HELPER_RELEASE;
- /* we only expect LD when finishing the handshake */
- assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
- ntlm_user->username_ = xstrdup(reply);
- helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
- ntlm_request->authserver = NULL;
- /* BH code: mark helper as broken */
- /* mark it for starving */
- helperstate->starve = 1;
-#endif
+ ntlm_user->username(blob);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(ntlm_request->server_blob);
+ authenticateNTLMReleaseServer(ntlm_request);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED;
- } else if (strncasecmp(reply, "NA ", 3) == 0) {
- /* TODO: only work with auth_user here if it exists */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->user();
- assert(auth_user != NULL);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- /* todo: action of Negotiate state on error */
- result = S_HELPER_RELEASE; /*some error has occured. no more requests */
- ntlm_request->authserver = NULL;
- debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
+ result = S_HELPER_RELEASE;
+ debug(29, 4) ("authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '%s'\n", blob);
+ } else if (strncasecmp(reply, "NA ", 3) == 0 && blob != NULL) {
+ /* authentication failure (wrong password, etc.) */
+ auth_user_request->denyMessage(blob);
ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- reply += 3;
-
- if (*reply)
- auth_user_request->setDenyMessage(reply);
- } else if (strncasecmp(reply, "NA", 2) == 0) {
- /* NTLM Helper protocol violation! */
- fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
+ safe_free(ntlm_request->server_blob);
+ authenticateNTLMReleaseServer(ntlm_request);
+ result = S_HELPER_RELEASE;
+ debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", 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
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate NTLM start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
* Needing YR. */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->user();
- assert(auth_user != NULL);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- /*some error has occured. no more requests for
- * this helper */
- result = S_HELPER_RELEASE;
- assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
- helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
- ntlm_request->authserver = NULL;
-
- if (ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE) {
- /* The helper broke on YR. It automatically
- * resets */
- debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver, reply);
- /* mark it for starving */
- helperstate->starve = 1;
- /* resubmit the request. This helper is currently busy, so we will get
- * a different one. Our auth state stays the same */
- auth_user_request->start(r->handler, r->data);
- /* don't call the callback */
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
- return result;
- }
-
- /* the helper broke on a KK */
- /* first the standard KK stuff */
- debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-
- /* now we mark the helper for resetting. */
- helperstate->starve = 1;
-
+ auth_user_request->denyMessage(blob);
ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-
- reply += 3;
-
- if (*reply)
- auth_user_request->setDenyMessage(reply);
+ safe_free(ntlm_request->server_blob);
+ authenticateNTLMReleaseServer(ntlm_request);
+ result = S_HELPER_RELEASE;
+ debug(29, 1) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
} else {
- /* TODO: only work with auth_user here if it exists */
- /* TODO: take the request state into consideration */
- assert(r->auth_user_request != NULL);
- assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
- auth_user_request = r->auth_user_request;
- auth_user = auth_user_request->user();
- assert(auth_user != NULL);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert((ntlm_user != NULL) && (ntlm_request != NULL));
- debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
- /* restart the authentication process */
- ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
- assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
- ntlm_request->authserver = NULL;
+ /* protocol error */
+ fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
}
r->handler(r->data, NULL);
cbdataReferenceDone(r->data);
authenticateStateFree(r);
- debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
+ debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", result);
return result;
}
helperStatefulStats(sentry, ntlmauthenticators);
}
-/* is a particular challenge still valid ? */
-static int
-authenticateNTLMValidChallenge(ntlm_helper_state_t * helperstate)
-{
- debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate->challenge ? "Valid" : "Invalid");
-
- if (helperstate->challenge == NULL)
- return 0;
-
- return 1;
-}
-
-/* does our policy call for changing the challenge now? */
-static int
-authenticateNTLMChangeChallenge_p(ntlm_helper_state_t * helperstate)
-{
- /* don't check for invalid challenges just for expiry choices */
- /* this is needed because we have to starve the helper until all old
- * requests have been satisfied */
-
- if (!helperstate->renewed) {
- /* first use, no challenge has been set. Without this check, it will
- * loop forever */
- debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
- return 0;
- }
-
- if (helperstate->challengeuses > ntlmConfig.challengeuses) {
- debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate->challengeuses, ntlmConfig.challengeuses);
- return 1;
- }
-
- if (helperstate->renewed + ntlmConfig.challengelifetime < squid_curtime) {
- debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime - (helperstate->renewed + ntlmConfig.challengelifetime)));
- return 1;
- }
-
- debug(29, 9) ("Challenge is to be reused\n");
- return 0;
-}
/* send the initial data to a stateful ntlm authenticator module */
void
AuthNTLMUserRequest::module_start(RH * handler, void *data)
{
authenticateStateData *r = NULL;
- helper_stateful_server *server;
- ntlm_helper_state_t *helperstate;
- char buf[8192];
- char *sent_string = NULL;
+ static char buf[8192];
ntlm_user_t *ntlm_user;
- auth_user_t *auth_user;
+ auth_user_t *auth_user = user();
- auth_user = this->user();
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- assert(ntlm_user);
assert(data);
+ assert(handler);
+ assert(auth_user);
assert(auth_user->auth_type == AUTH_NTLM);
- debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", auth_state);
- switch (auth_state) {
-
- case AUTHENTICATE_STATE_NEGOTIATE:
- sent_string = ntlmnegotiate;
- break;
+ ntlm_user = dynamic_cast<ntlm_user_t *>(user());
- case AUTHENTICATE_STATE_RESPONSE:
- sent_string = ntlmauthenticate;
- assert(authserver);
- debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", authserver);
- break;
-
- default:
- fatal("Invalid authenticate state for NTLMStart");
- }
-
- while (xisgraph(*sent_string)) /*trim NTLM */
- sent_string++;
-
- while (xisspace(*sent_string)) /*trim leading spaces */
- sent_string++;
-
- debug(29, 9) ("authenticateNTLMStart: state '%d'\n", auth_state);
-
- debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
+ debug(29, 8) ("AuthNTLMUserRequest::module_start: auth state is '%d'\n", auth_state);
if (ntlmConfig.authenticate == NULL) {
- debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string);
+ debug(29, 0) ("AuthNTLMUserRequest::module_start: no NTLM program specified.");
handler(data, NULL);
return;
}
- /* this is ugly TODO: move the challenge generation routines to their own function and
- * tidy the logic up to make use of the efficiency we now have */
- switch (auth_state) {
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ cbdataReference(data);
+ r->data = data;
+ r->auth_user_request = this;
- case AUTHENTICATE_STATE_NEGOTIATE:
- /*
- * 1: get a helper server
- * 2: does it have a challenge?
- * 3: tell it to get a challenge, or give ntlmauthdone the challenge
- */
- server = helperStatefulDefer(ntlmauthenticators);
- helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
-
- while ((server != NULL) && authenticateNTLMChangeChallenge_p(helperstate)) {
- /* flag this helper for challenge changing */
- helperstate->starve = 1;
- /* and release the deferred request */
- helperStatefulReleaseServer(server);
- /* Get another deferrable server */
- server = helperStatefulDefer(ntlmauthenticators);
- helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
- }
-
- if (server == NULL)
- debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
-
- authserver = server;
-
- /* tell the log what helper we have been given */
- debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server);
-
- /* server and valid challenge? */
- if ((server == NULL) || !authenticateNTLMValidChallenge(helperstate)) {
- /* No server, or server with invalid challenge */
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
-
- lock()
-
- ; /* locking myself */
-
- if (server == NULL) {
- helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, NULL);
- } else {
- /* Server with invalid challenge */
- snprintf(buf, 8192, "YR\n");
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
- }
- } else {
- /* (server != NULL and we have a valid challenge) */
- /* TODO: turn the below into a function and call from here and handlereply */
- /* increment the challenge uses */
- helperstate->challengeuses++;
- /* assign the challenge */
- authchallenge = xstrdup(helperstate->challenge);
- /* we're not actually submitting a request, so we need to release the helper
- * should the connection close unexpectedly
- */
- authserver_deferred = 1;
- handler(data, NULL);
- }
-
- break;
-
- case AUTHENTICATE_STATE_RESPONSE:
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
-
- lock()
-
- ;
- snprintf(buf, 8192, "KK %s\n", sent_string);
-
- /* getting rid of deferred request status */
- authserver_deferred = 0;
-
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
-
- debug(29, 9) ("authenticateNTLMstart: finished\n");
-
- break;
-
- default:
- fatal("Invalid authenticate state for NTLMStart");
- }
-}
-
-/* callback used by stateful helper routines */
-static int
-authenticateNTLMHelperServerAvailable(void *data)
-{
- ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
+ lock()
- if (statedata != NULL) {
- if (statedata->starve) {
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
- return 0;
- } else {
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
- return 1;
- }
+ ;
+ 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);
}
- debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
- return 0;
-}
+ waiting = 1;
-static void
-authenticateNTLMHelperServerOnEmpty(void *data)
-{
- ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
-
- if (statedata == NULL)
- return;
-
- if (statedata->starve) {
- /* we have been starving the helper */
- debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
- statedata->starve = 0;
- statedata->challengeuses = 0;
- statedata->renewed = 0;
- xfree(statedata->challenge);
- statedata->challenge = NULL;
- }
+ safe_free(client_blob);
+ helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
}
-
/* clear the NTLM helper of being reserved for future requests */
static void
authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
AuthNTLMUserRequest *ntlm_request;
assert(auth_user_request->user()->auth_type == AUTH_NTLM);
ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
- assert (ntlm_request);
debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver);
+ /* is it possible for the server to be NULL? hno seems to think so.
+ * Let's see what happens, might segfault in helperStatefulReleaseServer
+ * if it does. I leave it like this not to cover possibly problematic
+ * code-paths. Kinkie */
helperStatefulReleaseServer(ntlm_request->authserver);
ntlm_request->authserver = NULL;
}
/* clear any connection related authentication details */
void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *connection)
{
- assert(conn != NULL);
+ assert(connection != NULL);
- if (conn->auth_user_request != NULL) {
- assert (conn->auth_user_request == this);
- assert(this->conn == conn);
+ debug(29,8)("AuthNTLMUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this);
- if (authserver != NULL && authserver_deferred)
- authenticateNTLMReleaseServer(this);
+ if (connection->auth_user_request == NULL) {
+ debug(29,8)("AuthNTLMUserRequest::onConnectionClose: no auth_user_request\n");
+ return;
+ }
- /* unlock the connection based lock */
- debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
+ if (authserver != NULL)
+ authenticateNTLMReleaseServer(this);
- /* This still breaks the abstraction, but is at least read only now.
- * If needed, this could be ignored, as the conn deletion will also unlock
- * the auth user request.
- */
- this->unlock();
+ /* unlock the connection based lock */
+ debug(29, 9) ("AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection);
- conn->auth_user_request = NULL;
- }
-}
+ /* This still breaks the abstraction, but is at least read only now.
+ * If needed, this could be ignored, as the conn deletion will also unlock
+ * the auth user request.
+ */
+ unlock();
-/* NTLMLastHeader: return a pointer to the last header used in authenticating
- * the request/conneciton
- */
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
- return ntlmauthenticate;
+ connection->auth_user_request = NULL;
}
/*
- * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
+ * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
AuthUserRequest *
auth_user_request->user()->addRequest(auth_user_request);
/* all we have to do is identify that it's NTLM - the helper does the rest */
- debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
+ debug(29, 9) ("AuthNTLMConfig::decode: NTLM authentication\n");
return auth_user_request;
}
-static int
-authenticateNTLMcmpUsername(ntlm_user_t * u1, ntlm_user_t * u2)
-{
- return strcmp(u1->username(), u2->username());
-}
-
-
-/* there is a known race where a single client recieves the same challenge
- * and sends the same response to squid on a single select cycle.
- * Check for this and if found ignore the new link
- */
-static void
-authenticateProxyAuthCacheAddLink(const char *key, auth_user_t * auth_user)
-{
-
- struct ProxyAuthCachePointer *proxy_auth_hash;
- dlink_node *node;
- ntlm_user_t *ntlm_user;
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- node = ntlm_user->proxy_auth_list.head;
- /* prevent duplicates */
-
- while (node) {
-
- if (!strcmp(key, (char const *)((struct ProxyAuthCachePointer *) node->data)->key))
- return;
-
- node = node->next;
- }
-
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(ntlm_user_hash_pool->alloc());
- proxy_auth_hash->key = xstrdup(key);
- proxy_auth_hash->auth_user = auth_user;
- dlinkAddTail(proxy_auth_hash, &proxy_auth_hash->link, &ntlm_user->proxy_auth_list);
- hash_join(proxy_auth_cache, (hash_link *) proxy_auth_hash);
-}
-
int
AuthNTLMUserRequest::authenticated() const
{
- if (auth_state == AUTHENTICATE_STATE_DONE)
+ if (auth_state == AUTHENTICATE_STATE_FINISHED) {
+ debug(29, 9) ("AuthNTLMUserRequest::authenticated: user authenticated.\n");
return 1;
+ }
- debug(29, 9) ("User not fully authenticated.\n");
+ debug(29, 9) ("AuthNTLMUserRequest::authenticated: user not fully authenticated.\n");
return 0;
}
void
AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
{
- const char *proxy_auth;
+ const char *proxy_auth, *blob;
- struct ProxyAuthCachePointer *proxy_auth_hash = NULL;
+ //ProxyAuthCachePointer *proxy_auth_hash = NULL;
auth_user_hash_pointer *usernamehash;
+
/* TODO: rename this!! */
- auth_user_t *auth_user;
- AuthNTLMUserRequest *ntlm_request;
+ auth_user_t *local_auth_user;
ntlm_user_t *ntlm_user;
- LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
- /* get header */
- proxy_auth = httpHeaderGetStr(&request->header, type);
- auth_user = user();
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NTLM);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- ntlm_request = this;
- assert (ntlm_request);
+ 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.getRaw() == NULL) {
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
+ auth_state = AUTHENTICATE_STATE_FAILED;
+ debug(29, 1) ("AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!\n");
+ return;
+ }
+
+ if (waiting) {
+ debug(29, 1) ("AuthNTLMUserRequest::authenticate: waiting for helper reply!\n");
return;
}
- switch (ntlm_request->auth_state) {
+ if (server_blob) {
+ debug(29,2)("AuthNTLMUserRequest::authenticate: need to challenge client '%s'!\n", server_blob);
+ return;
+ }
+
+ /* get header */
+ proxy_auth = httpHeaderGetStr(&request->header, type);
+
+ blob = proxy_auth + strlen("NTLM");
+
+ while (xisspace(*blob)) // trim leading spaces in blob
+ blob++;
+
+ switch (auth_state) {
case AUTHENTICATE_STATE_NONE:
- /* we've recieved a negotiate request. pass to a helper */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth);
- ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
- ntlm_request->ntlmnegotiate = xstrdup(proxy_auth);
+ /* we've recieved a ntlm request. pass to a helper */
+ debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '%s'\n", proxy_auth);
+ auth_state = AUTHENTICATE_STATE_INITIAL;
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
conn->auth_type = AUTH_NTLM;
conn->auth_user_request = this;
- ntlm_request->conn = conn;
- /* and lock for the connection duration */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
+ conn = conn;
- this->lock()
+ lock()
- ;
+ ;
return;
break;
- case AUTHENTICATE_STATE_NEGOTIATE:
- ntlm_request->auth_state = AUTHENTICATE_STATE_CHALLENGE;
-
- /* We _MUST_ have the auth challenge by now */
- assert(ntlm_request->authchallenge);
+ case AUTHENTICATE_STATE_INITIAL:
+ debug(29,1)("AuthNTLMUserRequest::authenticate: need to ask helper\n");
return;
break;
- case AUTHENTICATE_STATE_CHALLENGE:
- /* we should have recieved a NTLM challenge. pass it to the same
- * helper process */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
-
- /* do a cache lookup here. If it matches it's a successful ntlm
- * challenge - release the helper and use the existing auth_user
- * details. */
-
- ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
- /* cache entries have authenticateauthheaderchallengestring */
- snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
- ntlm_request->ntlmauthenticate,
- ntlm_request->authchallenge);
+ case AUTHENTICATE_STATE_IN_PROGRESS:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
- /* see if we already know this user's authenticate */
- debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
-
- assert(proxy_auth_cache != NULL);
-
- proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(hash_lookup(proxy_auth_cache, ntlmhash));
-
- if (!proxy_auth_hash) { /* not in the hash table */
- debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
- ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
- /* verify with the ntlm helper */
- } else {
- debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
- /* throw away the temporary entry */
- ntlm_request->authserver_deferred = 0;
- authenticateNTLMReleaseServer(this);
- authenticateAuthUserMerge(auth_user, proxy_auth_hash->auth_user);
- auth_user = proxy_auth_hash->auth_user;
- this->user(auth_user);
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- /* we found one */
- debug(29, 9) ("found matching cache entry\n");
- assert(auth_user->auth_type == AUTH_NTLM);
- /* get the existing entries details */
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
- debug(29, 9) ("Username to be used is %s\n", ntlm_user->username());
- /* on ntlm auth we do not unlock the auth_user until the
- * connection is dropped. Thank MS for this quirk */
- auth_user->expiretime = current_time.tv_sec;
- }
+ client_blob = xstrdup (blob);
return;
+
break;
- case AUTHENTICATE_STATE_RESPONSE:
- /* auth-challenge pair cache miss. We've just got the response from the helper */
- /*add to cache and let them through */
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- /* this connection is authenticated */
- debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
- ntlm_request->authchallenge,
- ntlm_request->ntlmauthenticate,
- ntlm_user->username());
- /* cache entries have authenticateauthheaderchallengestring */
- snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
- ntlm_request->ntlmauthenticate,
- ntlm_request->authchallenge);
+ case AUTHENTICATE_STATE_FINISHED:
+ /* connection is authenticated */
+ debug(29, 4) ("AuthNTLMUserRequest::authenticate: authenticated user %s\n", ntlm_user->username());
+
/* see if this is an existing user with a different proxy_auth
* string */
-
- if ((usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username())))
- ) {
- while ((usernamehash->user()->auth_type != auth_user->auth_type) && (usernamehash->next) && !authenticateNTLMcmpUsername(dynamic_cast<ntlm_user_t *>(usernamehash->user()), ntlm_user)
- )
- usernamehash = static_cast<AuthUserHashPointer*>(usernamehash->next);
- if (usernamehash->user()->auth_type == auth_user->auth_type) {
- /*
- * add another link from the new proxy_auth to the
- * auth_user structure and update the information */
- assert(proxy_auth_hash == NULL);
- authenticateProxyAuthCacheAddLink(ntlmhash, usernamehash->user());
- /* we can't seamlessly recheck the username due to the
- * challenge nature of the protocol. Just free the
- * temporary auth_user */
- authenticateAuthUserMerge(auth_user, usernamehash->user());
- auth_user = usernamehash->user();
- this->user(auth_user);
- }
+ usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
+
+ 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();
+ _auth_user = local_auth_user;
} else {
/* store user in hash's */
- auth_user->addToNameCache();
- authenticateProxyAuthCacheAddLink(ntlmhash, auth_user);
+ 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 */
- auth_user->expiretime = current_time.tv_sec;
+ local_auth_user->expiretime = current_time.tv_sec;
+
+ authenticateNTLMReleaseServer(this);
+
+ auth_state = AUTHENTICATE_STATE_DONE;
+
return;
+
break;
case AUTHENTICATE_STATE_DONE:
- fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
+ fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
+
break;
case AUTHENTICATE_STATE_FAILED:
/* we've failed somewhere in authentication */
- debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth);
+ debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm failed. %s\n", proxy_auth);
+
return;
+
+ break;
}
return;
}
-AuthNTLMUserRequest::AuthNTLMUserRequest() : ntlmnegotiate(NULL), authchallenge(NULL), ntlmauthenticate(NULL),
- authserver(NULL), auth_state(AUTHENTICATE_STATE_NONE),
- authserver_deferred(0), conn(NULL), _theUser(NULL)
-{}
+AuthNTLMUserRequest::AuthNTLMUserRequest() :
+ conn(NULL), auth_state(AUTHENTICATE_STATE_NONE),
+ _theUser(NULL)
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+}
AuthNTLMUserRequest::~AuthNTLMUserRequest()
{
- if (ntlmnegotiate)
- xfree(ntlmnegotiate);
+ safe_free(server_blob);
+ safe_free(client_blob);
- if (authchallenge)
- xfree(authchallenge);
-
- if (ntlmauthenticate)
- xfree(ntlmauthenticate);
-
- if (authserver != NULL && authserver_deferred) {
- debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", authserver);
+ if (authserver != NULL) {
+ debug(29, 9) ("AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '%p'\n", authserver);
helperStatefulReleaseServer(authserver);
authserver = NULL;
}
return &ntlmConfig;
}
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
typedef enum {
AUTHENTICATE_STATE_NONE,
- AUTHENTICATE_STATE_NEGOTIATE,
- AUTHENTICATE_STATE_CHALLENGE,
- AUTHENTICATE_STATE_RESPONSE,
+ AUTHENTICATE_STATE_INITIAL,
+ AUTHENTICATE_STATE_IN_PROGRESS,
+ AUTHENTICATE_STATE_FINISHED,
AUTHENTICATE_STATE_DONE,
AUTHENTICATE_STATE_FAILED
} auth_state_t; /* connection level auth state */
virtual void authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type);
virtual int module_direction();
virtual void onConnectionClose(ConnStateData *);
- virtual const char *connLastHeader();
virtual void module_start(RH *, void *);
virtual AuthUser *user() {return _theUser;}
virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NTLMUser *>(aUser);}
- /* what negotiate string did the client use? */
- char *ntlmnegotiate;
- /* what challenge did we give the client? */
- char *authchallenge;
- /* what authenticate string did we get? */
- char *ntlmauthenticate;
- /*we need to store the NTLM server between requests */
+ virtual const char * connLastHeader();
+
+ /*we need to store the helper server between requests */
helper_stateful_server *authserver;
- /* how far through the authentication process are we? */
- auth_state_t auth_state;
- /* have we got the helper-server in a deferred state? */
- int authserver_deferred;
/* what connection is this associated with */
ConnStateData::Pointer 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;
+
private:
/* the user */
NTLMUser * _theUser;
MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest)
-struct _ntlm_helper_state_t
-{
- char *challenge; /* the challenge to use with this helper */
- int starve; /* 0= normal operation. 1=don't hand out any more challenges */
- int challengeuses; /* the number of times this challenge has been issued */
- time_t renewed;
-};
-
/* configuration runtime data */
class AuthNTLMConfig : public AuthConfig
virtual void parse(AuthConfig *, int, char *);
virtual const char * type() const;
int authenticateChildren;
+ int keep_alive;
wordlist *authenticate;
- int challengeuses;
- time_t challengelifetime;
};
-struct ProxyAuthCachePointer : public hash_link
-{
- dlink_node link;
- /* other hash entries that point to the same auth_user */
- auth_user_t *auth_user;
-};
-
-typedef struct _ntlm_helper_state_t ntlm_helper_state_t;
-
typedef class AuthNTLMConfig auth_ntlm_config;
#endif
/*
- * $Id: cache_cf.cc,v 1.481 2005/10/02 14:28:47 serassio Exp $
+ * $Id: cache_cf.cc,v 1.482 2005/10/23 11:55:33 hno Exp $
*
* DEBUG: section 3 Configuration File Parsing
* AUTHOR: Harvest Derived
name,
ae->name,
ae->typeString());
- v = w = ae->dumpGeneric();
+ v = w = ae->dump();
while (v != NULL) {
debug(3, 3) ("dump_acl: %s %s %s\n", name, ae->name, v->key);
#
-# $Id: cf.data.pre,v 1.400 2005/10/16 19:57:41 serassio Exp $
+# $Id: cf.data.pre,v 1.401 2005/10/23 11:55:33 hno Exp $
#
#
# SQUID Web Proxy Cache http://www.squid-cache.org/
processes.
auth_param ntlm children 5
- "max_challenge_reuses" number
- The maximum number of times a challenge given by a ntlm
- authentication helper can be reused. Increasing this number
- increases your exposure to replay attacks on your network.
- 0 means use the challenge only once. (disable challenge
- caching) See max_ntlm_challenge_lifetime for more information.
- auth_param ntlm max_challenge_reuses 0
+ === Options for configuring the NEGOTIATE auth-scheme follow ===
- "max_challenge_lifetime" timespan
- The maximum time period a ntlm challenge is reused
- over. The actual period will be the minimum of this time
- AND the number of reused challenges.
- auth_param ntlm max_challenge_lifetime 2 minutes
+ "program" cmdline
+ Specify the command for the external Negotiate authenticator.
+ This protocol is used in Microsoft Active-Directory enabled setups with
+ the Microsoft Internet Explorer or Mozilla Firefox browsers.
+ Its main purpose is to exchange credentials with the Squid proxy
+ using the Kerberos mechanisms.
+ If you use a Negotiate authenticator, make sure you have at least one acl
+ of type proxy_auth active. By default, the negotiate authenticator_program
+ is not used.
+ The only supported program for this role is the ntlm_auth
+ program distributed as part of Samba, version 3 or later.
+
+ auth_param negotiate program @DEFAULT_PREFIX@/bin/ntlm_auth --helper-protocol=gss-spnego
+
+ "children" numberofchildren
+ The number of authenticator processes to spawn (no default).
+ If you start too few Squid will have to wait for them to
+ process a backlog of credential verifications, slowing it
+ down. When crendential verifications are done via a (slow)
+ network you are likely to need lots of authenticator
+ processes.
+ auth_param negotiate children 5
+
+ "keep_alive" on|off
+ If you experience problems with PUT/POST requests when using the
+ Negotiate authentication scheme then you can try setting this to
+ off. This will cause Squid to forcibly close the connection on
+ the initial requests where the browser asks which schemes are
+ supported by the proxy.
+
+ auth_param negotiate keep_alive on
NOCOMMENT_START
#Recommended minimum configuration:
+#auth_param ntlm program <uncomment and complete this line to activate>
+#auth_param ntlm children 5
+#auth_param negotiate program <uncomment and complete this line to activate>
+#auth_param negotiate children 5
+#auth_param negotiate keep_alive on
#auth_param digest program <uncomment and complete this line>
#auth_param digest children 5
#auth_param digest realm Squid proxy-caching web server
#auth_param digest nonce_garbage_interval 5 minutes
#auth_param digest nonce_max_duration 30 minutes
#auth_param digest nonce_max_count 50
-#auth_param ntlm program <uncomment and complete this line to activate>
-#auth_param ntlm children 5
-#auth_param ntlm max_challenge_reuses 0
-#auth_param ntlm max_challenge_lifetime 2 minutes
#auth_param basic program <uncomment and complete this line>
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
/*
- * $Id: client_side.cc,v 1.698 2005/09/17 05:50:08 wessels Exp $
+ * $Id: client_side.cc,v 1.699 2005/10/23 11:55:36 hno Exp $
*
* DEBUG: section 33 Client-side Routines
* AUTHOR: Duane Wessels
assert(areAllContextsForThisConnection());
freeAllContexts();
- if (auth_user_request != NULL)
+ if (auth_user_request != NULL) {
+ debug(33,4)("ConnStateData::close: freeing auth_user_request '%p' (this is '%p')\n",
+ auth_user_request,this);
auth_user_request->onConnectionClose(this);
+ }
}
bool
/*
- * $Id: enums.h,v 1.243 2005/08/28 08:55:21 serassio Exp $
+ * $Id: enums.h,v 1.244 2005/10/23 11:55:36 hno Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
AUTH_BASIC,
AUTH_NTLM,
AUTH_DIGEST,
+ AUTH_NEGOTIATE,
AUTH_BROKEN /* known type, but broken data */
} auth_type_t;
/*
- * $Id: helper.cc,v 1.69 2005/09/17 05:50:08 wessels Exp $
+ * $Id: helper.cc,v 1.70 2005/10/23 11:55:37 hno Exp $
*
* DEBUG: section 84 Helper process maintenance
* AUTHOR: Harvest Derived?
char *msg = srv->rbuf;
int i = 0;
debug(84, 3) ("helperHandleRead: end of reply found\n");
+
+ if (t > srv->rbuf && t[-1] == '\r')
+ t[-1] = '\0';
+
*t++ = '\0';
if (hlp->concurrency) {
if ((t = strchr(srv->rbuf, '\n'))) {
/* end of reply found */
debug(84, 3) ("helperStatefulHandleRead: end of reply found\n");
+
+ if (t > srv->rbuf && t[-1] == '\r')
+ t[-1] = '\0';
+
*t = '\0';
if (cbdataReferenceValid(r->data)) {
--- /dev/null
+#!/bin/sh
+while read request; do
+data="`echo $request | cut -c4-`"
+blob="$$.$data-$challenge.`date +%s`"
+case $request in
+
+??" USER="*)
+ echo "AF Success-$blob `echo $request|cut -d= -f2-`"
+ ;;
+
+??" BAD"*)
+ echo "BH `echo $request|cut -c7-`"
+ ;;
+
+??" ERR"*)
+ echo "NA Invalid-$blob `echo $request|cut -c7-`"
+ ;;
+
+"YR"*)
+ challenge="$data.`date +%s`"
+ echo "TT Challenge-$$.$challenge *"
+ ;;
+
+"KK"*)
+ echo "TT Negotiate-$$.$data-$challenge.`date +%s` *"
+ ;;
+*)
+ echo "BH Invalid request"
+ ;;
+esac
+done
--- /dev/null
+#!/bin/sh
+while read request; do
+data="`echo $request | cut -c4-`"
+blob="$$.$data-$challenge.`date +%s`"
+case $request in
+
+??" USER="*)
+ echo "AF `echo $request|cut -d= -f2-`"
+ ;;
+
+??" BAD"*)
+ echo "BH `echo $request|cut -c7-`"
+ ;;
+
+??" ERR"*)
+ echo "NA `echo $request|cut -c7-`"
+ ;;
+
+"YR"*)
+ challenge="$data.`date +%s`"
+ echo "TT Challenge-$$.$challenge"
+ ;;
+
+"KK"*)
+ echo "TT Negotiate-$$.$data-$challenge.`date +%s`"
+ ;;
+*)
+ echo "BH Invalid request"
+ ;;
+esac
+done
--- /dev/null
+#!/bin/sh
+while read auth; do
+ echo "HEAD http://www.squid-cache.org/ HTTP/1.0"
+ if [ -n "$auth" ]; then
+ echo "Proxy-Authorization: Negotiate $auth"
+ fi
+ echo "Proxy-Connection: keep-alive"
+ echo
+done | tee -a /dev/fd/2 | nc localhost 3128
--- /dev/null
+#!/bin/sh
+while read auth; do
+ echo "HEAD http://www.squid-cache.org/ HTTP/1.0"
+ if [ -n "$auth" ]; then
+ echo "Proxy-Authorization: NTLM $auth"
+ fi
+ echo "Proxy-Connection: keep-alive"
+ echo
+done | tee -a /dev/fd/2 | nc localhost 3128