#include "config.h"
+#if REFCOUNT_DEBUG
+#include "Debug.h"
+#endif
+
#if HAVE_IOSTREAM
#include <iostream>
#endif
struct RefCountable_ {
RefCountable_():count_(0) {}
- virtual ~RefCountable_() {}
+ virtual ~RefCountable_() { assert(count_ == 0); }
/* Not private, to allow class hierarchies */
void RefCountReference() const {
-#include "squid.h"
-
+#include "config.h"
+#include "Debug.h"
+#include "protos.h"
#if HAVE_AUTH_MODULE_BASIC
#include "auth/basic/basicScheme.h"
#endif
-
-#if HAVE_AUTH_MODULE_NTLM
-#include "auth/ntlm/ntlmScheme.h"
-#endif
-
#if HAVE_AUTH_MODULE_DIGEST
#include "auth/digest/digestScheme.h"
#endif
-
#if HAVE_AUTH_MODULE_NEGOTIATE
#include "auth/negotiate/negotiateScheme.h"
#endif
-
-#if HAVE_AUTH_MODULE_BASIC
-static const char *basic_type = basicScheme::GetInstance().type();
-#endif
-
#if HAVE_AUTH_MODULE_NTLM
-static const char *ntlm_type = ntlmScheme::GetInstance().type();
+#include "auth/ntlm/ntlmScheme.h"
#endif
+/**
+ * Initialize the authentication modules (if any)
+ * This is required once, before any configuration actions are taken.
+ */
+void
+InitAuthSchemes()
+{
+ debugs(29,1,"Initializing Authentication Schemes ...");
+#if HAVE_AUTH_MODULE_BASIC
+ static const char *basic_type = basicScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << basic_type << "'");
+#endif
#if HAVE_AUTH_MODULE_DIGEST
-static const char *digest_type = digestScheme::GetInstance().type();
+ static const char *digest_type = digestScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << digest_type << "'");
#endif
-
#if HAVE_AUTH_MODULE_NEGOTIATE
-static const char *negotiate_type = negotiateScheme::GetInstance().type();
+ static const char *negotiate_type = negotiateScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << negotiate_type << "'");
#endif
-
+#if HAVE_AUTH_MODULE_NTLM
+ static const char *ntlm_type = ntlmScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << ntlm_type << "'");
+#endif
+ debugs(29,1,"Initializing Authentication Schemes Complete.");
+}
#if DELAY_POOLS
#include "squid.h"
+#include "auth/UserRequest.h"
#include "DelayPools.h"
#include "DelayIdComposite.h"
#include "CommRead.h"
class StoreEntry;
-class AuthUserRequest;
-
/// \ingroup DelayPoolsAPI
class CompositePoolNode : public RefCountable, public Updateable
{
CompositeSelectionDetails() {}
Ip::Address src_addr;
- AuthUserRequest *user;
+ AuthUserRequest::Pointer user;
String tag;
};
}
void
-DelayUserBucket::operator delete (void *address)
+DelayUserBucket::operator delete(void *address)
{
- DelayPools::MemoryUsed -= sizeof (DelayUserBucket);
- ::operator delete (address);
+ DelayPools::MemoryUsed -= sizeof(DelayUserBucket);
+ ::operator delete(address);
}
-DelayUserBucket::DelayUserBucket(AuthUser *aUser) : authUser (aUser)
+DelayUserBucket::DelayUserBucket(AuthUser::Pointer aUser) : authUser(aUser)
{
debugs(77, 3, "DelayUserBucket::DelayUserBucket");
-
- authUser->lock();
}
DelayUserBucket::~DelayUserBucket()
{
- authUser->unlock();
+ authUser = NULL;
debugs(77, 3, "DelayUserBucket::~DelayUserBucket");
}
DelayUserBucket::stats (StoreEntry *entry) const
{
storeAppendPrintf(entry, " %s:", authUser->username());
- theBucket.stats (entry);
+ theBucket.stats(entry);
}
-DelayUser::Id::Id(DelayUser::Pointer aDelayUser,AuthUser *aUser) : theUser(aDelayUser)
+DelayUser::Id::Id(DelayUser::Pointer aDelayUser, AuthUser::Pointer aUser) : theUser(aDelayUser)
{
theBucket = new DelayUserBucket(aUser);
DelayUserBucket::Pointer const *existing = theUser->buckets.find(theBucket, DelayUserCmp);
#include "squid.h"
#include "auth/Gadgets.h"
+#include "auth/User.h"
#include "CompositePoolNode.h"
#include "DelayIdComposite.h"
#include "DelayBucket.h"
void operator delete (void *);
void stats(StoreEntry *)const;
- DelayUserBucket(AuthUser *);
+ DelayUserBucket(AuthUser::Pointer);
~DelayUserBucket();
DelayBucket theBucket;
- AuthUser *authUser;
+ AuthUser::Pointer authUser;
};
/// \ingroup DelayPoolsAPI
public:
void *operator new(size_t);
void operator delete (void *);
- Id (RefCount<DelayUser>, AuthUser *);
+ Id(RefCount<DelayUser>, AuthUser::Pointer);
~Id();
virtual int bytesWanted (int min, int max) const;
virtual void bytesIn(int qty);
// points to a pipe that is owned and initiated by another object.
body_pipe = NULL;
- AUTHUSERREQUESTUNLOCK(auth_user_request, "request");
+ auth_user_request = NULL;
safe_free(canonical);
// may eventually need cloneNullAdaptationImmune() for that.
flags = aReq->flags.cloneAdaptationImmune();
- if (aReq->auth_user_request) {
- auth_user_request = aReq->auth_user_request;
- AUTHUSERREQUESTLOCK(auth_user_request, "inheritProperties");
- }
+ auth_user_request = aReq->auth_user_request;
if (aReq->pinned_connection) {
pinned_connection = cbdataReference(aReq->pinned_connection);
-
/*
* $Id$
*
public:
Ip::Address host_addr;
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
u_short port;
# libraries used by many targets
COMMON_LIBS = \
- base/libbase.la \
- libsquid.la \
auth/libacls.la \
ident/libident.la \
acl/libacls.la \
acl/libstate.la \
auth/libauth.la \
acl/libapi.la \
+ base/libbase.la \
+ libsquid.la \
ip/libip.la \
fs/libfs.la
globals.cc: globals.h mk-globals-c.awk
$(AWK) -f $(srcdir)/mk-globals-c.awk < $(srcdir)/globals.h > $@ || ($(RM) -f $@ && exit 1)
-## Generate files containing strng arrays for various enums....
+## Generate files containing string arrays for various enums....
hier_code.cc: hier_code.h mk-string-arrays.awk
$(AWK) -f $(srcdir)/mk-string-arrays.awk < $(srcdir)/hier_code.h > $@ || ($(RM) -f $@ && exit 1)
## Tests of the CacheManager module.
tests_testCacheManager_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
debug.cc \
HttpRequest.cc \
HttpRequestMethod.cc \
## Tests of the Even module.
tests_testEvent_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
debug.cc \
EventLoop.h \
EventLoop.cc \
## Tests of the EventLoop module.
tests_testEventLoop_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
debug.cc \
EventLoop.h \
EventLoop.cc \
$(SQUID_CPPUNIT_LA)
tests_test_http_range_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
tests/test_http_range.cc \
BodyPipe.cc \
cache_cf.cc \
## Tests of the HttpRequest module.
tests_testHttpRequest_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
debug.cc \
HttpRequest.cc \
HttpRequestMethod.cc \
## Tests of the URL module.
## TODO: Trim this down once the insanity is over.
tests_testURL_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
debug.cc \
url.cc \
URLScheme.cc \
CBDATA_CLASS_INIT(ACLFilledChecklist);
-#if MOVED
-int
-ACLFilledChecklist::authenticated()
-{
- http_hdr_type headertype;
-
- if (NULL == request) {
- fatal ("requiresRequest SHOULD have been true for this ACL!!");
- return 0;
- } else if (request->flags.accelerated) {
- /* WWW authorization on accelerated requests */
- headertype = HDR_AUTHORIZATION;
- } else if (request->flags.intercepted || request->flags.spoof_client_ip) {
- debugs(28, DBG_IMPORTANT, HERE << " authentication not applicable on intercepted requests.");
- return -1;
- } else {
- /* Proxy authorization on proxy requests */
- headertype = HDR_PROXY_AUTHORIZATION;
- }
-
- /* get authed here */
- /* Note: this fills in auth_user_request when applicable */
- /*
- * DPW 2007-05-08
- * tryToAuthenticateAndSetAuthUser used to try to lock and
- * unlock auth_user_request on our behalf, but it was too
- * ugly and hard to follow. Now we do our own locking here.
- *
- * I'm not sure what tryToAuthenticateAndSetAuthUser does when
- * auth_user_request is set before calling. I'm tempted to
- * unlock and set it to NULL, but it seems safer to save the
- * pointer before calling and unlock it afterwards. If the
- * pointer doesn't change then its a no-op.
- */
- AuthUserRequest *old_auth_user_request = auth_user_request;
- auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr);
- if (auth_user_request)
- AUTHUSERREQUESTLOCK(auth_user_request, "ACLFilledChecklist");
- AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLFilledChecklist");
- switch (result) {
-
- case AUTH_ACL_CANNOT_AUTHENTICATE:
- debugs(28, 4, "aclMatchAcl: returning 0 user authenticated but not authorised.");
- return 0;
-
- case AUTH_AUTHENTICATED:
-
- return 1;
- break;
-
- case AUTH_ACL_HELPER:
- debugs(28, 4, "aclMatchAcl: returning 0 sending credentials to helper.");
- changeState (ProxyAuthLookup::Instance());
- return 0;
-
- case AUTH_ACL_CHALLENGE:
- debugs(28, 4, "aclMatchAcl: returning 0 sending authentication challenge.");
- changeState (ProxyAuthNeeded::Instance());
- return 0;
-
- default:
- fatal("unexpected authenticateAuthenticate reply\n");
- return 0;
- }
-}
-#endif
-
void
ACLFilledChecklist::checkCallback(allow_t answer)
{
- debugs(28, 5, "ACLFilledChecklist::checkCallback: " << this << " answer=" << answer);
+ debugs(28, 5, HERE << this << " answer=" << answer);
/* During reconfigure, we can end up not finishing call
* sequences into the auth code */
- if (auth_user_request) {
+ if (auth_user_request != NULL) {
/* the filled_checklist lock */
- AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist");
-
+ auth_user_request = NULL;
/* it might have been connection based */
- /*
- * DPW 2007-05-08
- * yuck, this make me uncomfortable. why do this here?
- * ConnStateData will do its own unlocking.
- */
- /* BUG 2827: the connection may also not exist. ie fast ACL tests vs client disconnection. */
if (conn()) {
- AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLFilledChecklist");
- conn()->auth_type = AUTH_BROKEN;
+ conn()->auth_user_request = NULL;
}
}
HTTPMSGUNLOCK(reply);
- // no auth_user_request in builds without any Authentication configured
- if (auth_user_request)
- AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist destructor");
-
cbdataReferenceDone(conn_);
debugs(28, 4, HERE << "ACLFilledChecklist destroyed " << this);
#define SQUID_ACLFILLED_CHECKLIST_H
#include "acl/Checklist.h"
+#include "auth/UserRequest.h"
-class AuthUserRequest;
class ExternalACLEntry;
class ConnStateData;
HttpReply *reply;
char rfc931[USER_IDENT_SZ];
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
#if SQUID_SNMP
char *snmp_community;
void Adaptation::Icap::ModXact::makeUsernameHeader(const HttpRequest *request, MemBuf &buf)
{
- if (const AuthUserRequest *auth = request->auth_user_request) {
- if (char const *name = auth->username()) {
- const char *value = TheConfig.client_username_encode ?
- base64_encode(name) : name;
- buf.Printf("%s: %s\r\n", TheConfig.client_username_header,
- value);
+ if (request->auth_user_request != NULL) {
+ char const *name = (request->auth_user_request)->username();
+ if (name) {
+ const char *value = TheConfig.client_username_encode ? base64_encode(name) : name;
+ buf.Printf("%s: %s\r\n", TheConfig.client_username_header, value);
}
}
}
/* get authed here */
/* Note: this fills in auth_user_request when applicable */
- /*
- * DPW 2007-05-08
- * tryToAuthenticateAndSetAuthUser used to try to lock and
- * unlock auth_user_request on our behalf, but it was too
- * ugly and hard to follow. Now we do our own locking here.
- *
- * I'm not sure what tryToAuthenticateAndSetAuthUser does when
- * auth_user_request is set before calling. I'm tempted to
- * unlock and set it to NULL, but it seems safer to save the
- * pointer before calling and unlock it afterwards. If the
- * pointer doesn't change then its a no-op.
- */
- AuthUserRequest *old_auth_user_request = checklist->auth_user_request;
- const auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser(
+ const AuthAclState result = AuthUserRequest::tryToAuthenticateAndSetAuthUser(
&checklist->auth_user_request, headertype, request,
checklist->conn(), checklist->src_addr);
- if (checklist->auth_user_request)
- AUTHUSERREQUESTLOCK(checklist->auth_user_request, "ACLAuth::authenticated");
- AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLAuth");
switch (result) {
case AUTH_ACL_CANNOT_AUTHENTICATE:
* 1 : Match
*/
int
-ACLMaxUserIP::match(AuthUserRequest * auth_user_request, Ip::Address const &src_addr)
+ACLMaxUserIP::match(AuthUserRequest::Pointer auth_user_request, Ip::Address const &src_addr)
{
/*
* the logic for flush the ip list when the limit is hit vs keep
ti = match(checklist->auth_user_request, checklist->src_addr);
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ACLChecklist via ACLMaxUserIP");
+ checklist->auth_user_request = NULL;
return ti;
}
#include "acl/Acl.h"
#include "acl/Checklist.h"
-
-class AuthUserRequest;
+#include "auth/UserRequest.h"
/// \ingroup ACLAPI
class ACLMaxUserIP : public ACL
static Prototype RegistryProtoype;
static ACLMaxUserIP RegistryEntry_;
- int match(AuthUserRequest *, Ip::Address const &);
+ int match(AuthUserRequest::Pointer, Ip::Address const &);
char const *class_;
int maximum;
ACLFilledChecklist *checklist = Filled(cl);
checklist->asyncInProgress(true);
- debugs(28, 3, "ACLChecklist::checkForAsync: checking password via authenticator");
+ debugs(28, 3, HERE << "checking password via authenticator");
- AuthUserRequest *auth_user_request;
/* make sure someone created auth_user_request for us */
assert(checklist->auth_user_request != NULL);
- auth_user_request = checklist->auth_user_request;
-
- int validated = authenticateValidateUser(auth_user_request);
- assert(validated);
- auth_user_request->start(LookupDone, checklist);
+ assert(checklist->auth_user_request->valid());
+ checklist->auth_user_request->start(LookupDone, checklist);
}
void
if (result != NULL)
fatal("AclLookupProxyAuthDone: Old code floating around somewhere.\nMake clean and if that doesn't work, report a bug to the squid developers.\n");
- if (!authenticateValidateUser(checklist->auth_user_request) || checklist->conn() == NULL) {
+ if (checklist->auth_user_request == NULL || !checklist->auth_user_request->valid() || checklist->conn() == NULL) {
/* credentials could not be checked either way
* restart the whole process */
/* OR the connection was closed, there's no way to continue */
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ProxyAuthLookup");
+ checklist->auth_user_request = NULL;
if (checklist->conn() != NULL) {
- AUTHUSERREQUESTUNLOCK(checklist->conn()->auth_user_request, "conn via ProxyAuthLookup"); // DPW discomfort
- checklist->conn()->auth_type = AUTH_BROKEN;
+ checklist->conn()->auth_user_request = NULL;
}
}
ACLProxyAuth::matchForCache(ACLChecklist *cl)
{
ACLFilledChecklist *checklist = Filled(cl);
- assert (checklist->auth_user_request);
+ assert (checklist->auth_user_request != NULL);
return data->match(checklist->auth_user_request->username());
}
ACLProxyAuth::matchProxyAuth(ACLChecklist *cl)
{
ACLFilledChecklist *checklist = Filled(cl);
- checkAuthForCaching(checklist);
+ if (!authenticateUserAuthenticated(Filled(checklist)->auth_user_request)) {
+ return 0;
+ }
/* check to see if we have matched the user-acl before */
- int result = cacheMatchAcl(&checklist->auth_user_request->user()->
- proxy_match_cache, checklist);
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ACLChecklist via ACLProxyAuth");
+ int result = cacheMatchAcl(&checklist->auth_user_request->user()->proxy_match_cache, checklist);
+ checklist->auth_user_request = NULL;
return result;
}
-
-void
-ACLProxyAuth::checkAuthForCaching(ACLChecklist *checklist)const
-{
- /* for completeness */
- /* consistent parameters ? */
- assert(authenticateUserAuthenticated(Filled(checklist)->auth_user_request));
- /* this check completed */
-}
-
virtual bool empty () const;
virtual bool requiresRequest() const {return true;}
- virtual ACL *clone()const;
+ virtual ACL *clone() const;
virtual int matchForCache(ACLChecklist *checklist);
private:
static Prototype RegexRegistryProtoype;
static ACLProxyAuth RegexRegistryEntry_;
int matchProxyAuth(ACLChecklist *);
- void checkAuthForCaching(ACLChecklist *) const;
ACLData<char const *> *data;
char const *type_;
};
--- /dev/null
+#ifndef _SQUID__SRC_AUTH_AUTHACLSTATE_H
+#define _SQUID__SRC_AUTH_AUTHACLSTATE_H
+
+typedef enum {
+ AUTH_ACL_CHALLENGE = -2,
+ AUTH_ACL_HELPER = -1,
+ AUTH_ACL_CANNOT_AUTHENTICATE = 0,
+ AUTH_AUTHENTICATED = 1
+} AuthAclState;
+
+#endif
--- /dev/null
+#ifndef _SQUID__SRC_AUTH_AUTHTYPE_H
+#define _SQUID__SRC_AUTH_AUTHTYPE_H
+
+typedef enum {
+ AUTH_UNKNOWN, /* default */
+ AUTH_BASIC,
+ AUTH_NTLM,
+ AUTH_DIGEST,
+ AUTH_NEGOTIATE,
+ AUTH_BROKEN /* known type, but broken data */
+} AuthType;
+
+extern const char *AuthType_str[];
+
+#endif
-
/*
* $Id$
*
#include "auth/Config.h"
#include "auth/UserRequest.h"
-/* Get Auth User: Return a filled out auth_user structure for the given
+Auth::authConfig Auth::TheConfig;
+
+/**
+ * Get Auth User: Return a filled out auth_user structure for the given
* Proxy Auth (or Auth) header. It may be a cached Auth User or a new
* Unauthenticated structure. The structure is given an inital lock here.
* It may also be NULL reflecting that no user could be created.
*/
-AuthUserRequest *
+AuthUserRequest::Pointer
AuthConfig::CreateAuthUser(const char *proxy_auth)
{
assert(proxy_auth != NULL);
return NULL;
}
- AuthUserRequest *result = config->decode (proxy_auth);
-
- /*
- * DPW 2007-05-08
- * Do not lock the AuthUserRequest on the caller's behalf.
- * Callers must manage their own locks.
- */
- return result;
+ return config->decode(proxy_auth);
}
AuthConfig *
AuthConfig::Find(const char *proxy_auth)
{
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i)
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
if (strncasecmp(proxy_auth, (*i)->type(), strlen((*i)->type())) == 0)
return *i;
#ifndef SQUID_AUTHCONFIG_H
#define SQUID_AUTHCONFIG_H
-class AuthUserRequest;
+#include "auth/UserRequest.h"
+#include "HelperChildConfig.h"
+
class StoreEntry;
class HttpReply;
class HttpRequest;
+class wordlist;
/* for http_hdr_type parameters-by-value */
#include "HttpHeader.h"
{
public:
- static AuthUserRequest *CreateAuthUser (const char *proxy_auth);
+ static AuthUserRequest::Pointer CreateAuthUser(const char *proxy_auth);
static AuthConfig *Find(const char *proxy_auth);
- AuthConfig() {}
+ AuthConfig() : authenticateChildren(20), authenticate(NULL) {}
virtual ~AuthConfig() {}
\param proxy_auth Login Pattern to parse.
\retval * Details needed to authenticate.
*/
- virtual AuthUserRequest *decode(char const *proxy_auth) = 0;
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth) = 0;
/**
* squid is finished with this config, release any unneeded resources.
*/
virtual bool configured() const = 0;
+ /**
+ * Shutdown just the auth helpers.
+ * For use by log rotate etc. where auth needs to stay running, with the helpers restarted.
+ */
+ virtual void rotateHelpers(void) = 0;
+
/**
* Responsible for writing to the StoreEntry the configuration parameters that a user
* would put in a config file to recreate the running configuration.
virtual void dump(StoreEntry *, const char *, AuthConfig *) = 0;
/** add headers as needed when challenging for auth */
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *) = 0;
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *) = 0;
/** prepare to handle requests */
virtual void init(AuthConfig *) = 0;
/** expose any/all statistics to a CacheManager */
virtual void parse(AuthConfig *, int, char *) = 0;
/** the http string id */
virtual const char * type() const = 0;
+
+public:
+ HelperChildConfig authenticateChildren;
+ wordlist *authenticate;
};
+namespace Auth
+{
+
+typedef Vector<AuthConfig *> authConfig;
+
+extern authConfig TheConfig;
+
+}; // namespace Auth
+
#endif /* SQUID_AUTHCONFIG_H */
-
/*
* $Id$
*
{
int rv = 0;
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i)
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
if ((*i)->configured())
++rv;
- debugs(29, 9, "authenticateActiveSchemeCount: " << rv << " active.");
+ debugs(29, 9, HERE << rv << " active.");
return rv;
}
int
authenticateSchemeCount(void)
{
- int rv = AuthScheme::Schemes().size();
+ int rv = AuthScheme::GetSchemes().size();
- debugs(29, 9, "authenticateSchemeCount: " << rv << " active.");
+ debugs(29, 9, HERE << rv << " active.");
return rv;
}
static void
-authenticateRegisterWithCacheManager(authConfig * config)
+authenticateRegisterWithCacheManager(Auth::authConfig * config)
{
- for (authConfig::iterator i = config->begin(); i != config->end(); ++i) {
+ for (Auth::authConfig::iterator i = config->begin(); i != config->end(); ++i) {
AuthConfig *scheme = *i;
scheme->registerWithCacheManager();
}
}
void
-authenticateInit(authConfig * config)
+authenticateInit(Auth::authConfig * config)
{
- for (authConfig::iterator i = config->begin(); i != config->end(); ++i) {
- AuthConfig *scheme = *i;
+ /* Do this first to clear memory and remove dead state on a reconfigure */
+ if (proxy_auth_username_cache)
+ AuthUser::CachedACLsReset();
+
+ /* If we do not have any auth config state to create stop now. */
+ if (!config)
+ return;
- if (scheme->configured())
- scheme->init(scheme);
+ for (Auth::authConfig::iterator i = config->begin(); i != config->end(); ++i) {
+ AuthConfig *schemeCfg = *i;
+
+ if (schemeCfg->configured())
+ schemeCfg->init(schemeCfg);
}
if (!proxy_auth_username_cache)
AuthUser::cacheInit();
- else
- AuthUser::CachedACLsReset();
- authenticateRegisterWithCacheManager(&Config.authConfiguration);
+ authenticateRegisterWithCacheManager(config);
}
void
-authenticateShutdown(void)
-{
- debugs(29, 2, "authenticateShutdown: shutting down auth schemes");
- /* free the cache if we are shutting down */
-
- if (shutting_down) {
- hashFreeItems(proxy_auth_username_cache, AuthUserHashPointer::removeFromCache);
- AuthScheme::FreeAll();
- } else {
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i)
- (*i)->done();
- }
-}
-
-/**
- \retval 0 not in use
- \retval ? in use
- */
-int
-authenticateAuthUserInuse(AuthUser * auth_user)
+authenticateRotate(void)
{
- assert(auth_user != NULL);
- return auth_user->references;
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
+ if ((*i)->configured())
+ (*i)->rotateHelpers();
}
void
-authenticateAuthUserMerge(AuthUser * from, AuthUser * to)
+authenticateReset(void)
{
- to->absorb (from);
-}
+ debugs(29, 2, HERE << "Reset authentication State.");
-/**
- * Cleans all config-dependent data from the auth_user cache.
- \note It DOES NOT Flush the user cache.
- */
-void
-authenticateUserCacheRestart(void)
-{
- AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- debugs(29, 3, HERE << "Clearing config dependent cache data.");
+ /* free all username cache entries */
hash_first(proxy_auth_username_cache);
-
+ AuthUserHashPointer *usernamehash;
while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
- auth_user = usernamehash->user();
- debugs(29, 5, "authenticateUserCacheRestat: Clearing cache ACL results for user: " << auth_user->username());
+ debugs(29, 5, HERE << "Clearing entry for user: " << usernamehash->user()->username());
+ hash_remove_link(proxy_auth_username_cache, (hash_link *)usernamehash);
+ delete usernamehash;
}
-}
+ /* schedule shutdown of the helpers */
+ authenticateRotate();
-void
-AuthUserHashPointer::removeFromCache(void *usernamehash_p)
-{
- AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(usernamehash_p);
- AuthUser *auth_user = usernamehash->auth_user;
-
- if ((authenticateAuthUserInuse(auth_user) - 1))
- debugs(29, 1, "AuthUserHashPointer::removeFromCache: entry in use - not freeing");
-
- auth_user->unlock();
-
- /** \todo change behaviour - we remove from the auth user list here, and then unlock, and the
- * delete ourselves.
- */
+ /* free current global config details too. */
+ Auth::TheConfig.clean();
}
-AuthUserHashPointer::AuthUserHashPointer(AuthUser * anAuth_user):
+AuthUserHashPointer::AuthUserHashPointer(AuthUser::Pointer anAuth_user):
auth_user(anAuth_user)
{
key = (void *)anAuth_user->username();
next = NULL;
hash_join(proxy_auth_username_cache, (hash_link *) this);
-
- /** lock for presence in the cache */
- auth_user->lock();
}
-AuthUser *
+AuthUser::Pointer
AuthUserHashPointer::user() const
{
return auth_user;
#include "hash.h"
#include "MemPool.h"
-#include "typedefs.h" /* for authConfig */
+#include "auth/Config.h"
+#include "auth/User.h"
class AuthUser;
/**
\ingroup AuthAPI
*
- * This is used to link auth_users into the username cache.
+ * This is used to link AuthUsers objects into the username cache.
* Because some schemes may link in aliases to a user,
* the link is not part of the AuthUser structure itself.
*
- \todo Inheritance in a struct? this should be a class.
+ * Code must not hold onto copies of these objects.
+ * They may exist only so long as the AuthUser being referenced
+ * is recorded in the cache. Any caller using hash_remove_link
+ * must then delete the AuthUserHashPointer.
*/
-struct AuthUserHashPointer : public hash_link {
+class AuthUserHashPointer : public hash_link {
/* first two items must be same as hash_link */
public:
- static void removeFromCache (void *anAuthUserHashPointer);
MEMPROXY_CLASS(AuthUserHashPointer);
- AuthUserHashPointer(AuthUser *);
+ AuthUserHashPointer(AuthUser::Pointer);
+ ~AuthUserHashPointer() { auth_user = NULL; };
- AuthUser *user() const;
+ AuthUser::Pointer user() const;
private:
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
};
MEMPROXY_CLASS_INLINE(AuthUserHashPointer);
*/
typedef void AUTHSSTATS(StoreEntry *);
-/**
- \ingroup AuthAPI
- * subsumed by the C++ interface
- \todo does 'subsumed' mean deprecated use a C++ API call?
+/// \ingroup AuthAPI
+extern void authenticateInit(Auth::authConfig *);
+
+/** \ingroup AuthAPI
+ * Remove all idle authentication state. Intended for use by reconfigure.
+ *
+ * Removes the username cache contents and global configuration state.
+ * Stops just short of detaching the auth components completely.
+ *
+ * Currently active requests should finish. Howevee new requests will not use
+ * authentication unless something causes the global config to be rebuilt.
+ * Such as a configure load action adding config and re-running authenticateInit().
*/
-extern void authenticateAuthUserMerge(AuthUser *, AuthUser *);
+extern void authenticateReset(void);
-/// \ingroup AuthAPI
-extern void authenticateInit(authConfig *);
-/// \ingroup AuthAPI
-extern void authenticateShutdown(void);
-/// \ingroup AuthAPI
-extern int authenticateAuthUserInuse(AuthUser * auth_user);
+extern void authenticateRotate(void);
/// \ingroup AuthAPI
extern void authenticateFreeProxyAuthUserACLResults(void *data);
/// \ingroup AuthAPI
extern int authenticateSchemeCount(void);
-/// \ingroup AuthAPI
-extern void authenticateUserCacheRestart(void);
/// \ingroup AuthAPI
extern void authenticateOnCloseConnection(ConnStateData * conn);
## authentication framework; this library is always built
libauth_la_SOURCES = \
+ AuthType.h \
+ AuthType.cc \
Config.cc \
Config.h \
+ Gadgets.cc \
+ Gadgets.h \
Scheme.cc \
Scheme.h \
+ State.h \
+ State.cc \
User.h \
User.cci \
User.cc \
UserRequest.h \
- UserRequest.cc \
- Gadgets.cc \
- Gadgets.h
+ UserRequest.cc
libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD)
libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD)
AclMaxUserIp.cc \
AclMaxUserIp.h \
AclProxyAuth.cc \
- AclProxyAuth.h
-
+ AclProxyAuth.h \
+ AuthAclState.h
libbasic_la_SOURCES = \
basic/basicScheme.cc \
basic/basicScheme.h \
basic/auth_basic.cc \
- basic/auth_basic.h
+ basic/auth_basic.h \
+ basic/basicUserRequest.cc \
+ basic/basicUserRequest.h
libdigest_la_SOURCES = \
digest/digestScheme.cc \
digest/digestScheme.h \
digest/auth_digest.cc \
- digest/auth_digest.h
+ digest/auth_digest.h \
+ digest/digestUserRequest.cc \
+ digest/digestUserRequest.h
libntlm_la_SOURCES = \
ntlm/ntlmScheme.cc \
ntlm/ntlmScheme.h \
ntlm/auth_ntlm.cc \
- ntlm/auth_ntlm.h
+ ntlm/auth_ntlm.h \
+ ntlm/ntlmUserRequest.cc \
+ ntlm/ntlmUserRequest.h
libnegotiate_la_SOURCES = \
negotiate/negotiateScheme.cc \
negotiate/negotiateScheme.h \
negotiate/auth_negotiate.cc \
- negotiate/auth_negotiate.h
+ negotiate/auth_negotiate.h \
+ negotiate/negotiateUserRequest.cc \
+ negotiate/negotiateUserRequest.h
+
+AuthType.cc: AuthType.h $(top_srcdir)/src/mk-string-arrays.awk
+ $(AWK) -f $(top_srcdir)/src/mk-string-arrays.awk < $(srcdir)/AuthType.h > $@ || (rm -f $@ ; exit 1)
TESTS += testHeaders
-
/*
* $Id$
*
#include "auth/Gadgets.h"
#include "auth/Config.h"
-Vector<AuthScheme*> *AuthScheme::_Schemes = NULL;
+Vector<AuthScheme::Pointer> *AuthScheme::_Schemes = NULL;
void
-AuthScheme::AddScheme(AuthScheme &instance)
+AuthScheme::AddScheme(AuthScheme::Pointer instance)
{
iterator i = GetSchemes().begin();
while (i != GetSchemes().end()) {
- assert(strcmp((*i)->type(), instance.type()) != 0);
+ assert(strcmp((*i)->type(), instance->type()) != 0);
++i;
}
- GetSchemes().push_back (&instance);
+ GetSchemes().push_back(instance);
}
-AuthScheme *
+AuthScheme::Pointer
AuthScheme::Find(const char *typestr)
{
for (iterator i = GetSchemes().begin(); i != GetSchemes().end(); ++i) {
return *i;
}
- return NULL;
-}
-
-Vector<AuthScheme *> const &
-AuthScheme::Schemes()
-{
- return GetSchemes();
+ return AuthScheme::Pointer(NULL);
}
-Vector<AuthScheme*> &
+Vector<AuthScheme::Pointer> &
AuthScheme::GetSchemes()
{
if (!_Schemes)
- _Schemes = new Vector<AuthScheme *>;
+ _Schemes = new Vector<AuthScheme::Pointer>;
return *_Schemes;
}
-/*
- * called when a graceful shutdown is to occur
- * of each scheme module.
+/**
+ * Called when a graceful shutdown is to occur of each scheme module.
+ * On completion the auth components are to be considered deleted.
+ * None will be available globally. Some may remain around for their
+ * currently active connections to close, but only those active
+ * connections will retain pointers to them.
*/
void
AuthScheme::FreeAll()
{
+// assert(shutting_down);
+
while (GetSchemes().size()) {
- AuthScheme *scheme = GetSchemes().back();
+ AuthScheme::Pointer scheme = GetSchemes().back();
GetSchemes().pop_back();
scheme->done();
}
-
/*
* $Id$
*
#ifndef SQUID_AUTHSCHEME_H
#define SQUID_AUTHSCHEME_H
-#include "squid.h"
+#include "config.h"
#include "Array.h"
+#include "RefCount.h"
+
+class AuthConfig;
/**
\defgroup AuthSchemeAPI Authentication Scheme API
*/
/**
- \ingroup AuthAPI
- \ingroup AuthSchemeAPI
- \par
+ * \ingroup AuthAPI
+ * \ingroup AuthSchemeAPI
+ * \par
* I represent an authentication scheme. For now my children
- * store both the scheme metadata, and the scheme configuration.
- \par
+ * store the scheme metadata.
+ * \par
* Should we need multiple configs of a single scheme,
* a new class AuthConfiguration should be made, and the
* config specific calls on AuthScheme moved to it.
*/
-class AuthScheme
+class AuthScheme : public RefCountable
{
+public:
+ typedef RefCount<AuthScheme> Pointer;
+ typedef Vector<AuthScheme::Pointer>::iterator iterator;
+ typedef Vector<AuthScheme::Pointer>::const_iterator const_iterator;
public:
- static void AddScheme(AuthScheme &);
+ AuthScheme() : initialised (false) {};
+ virtual ~AuthScheme() {};
+
+ static void AddScheme(AuthScheme::Pointer);
+
+ /**
+ * Final termination of all authentication components.
+ * To be used only on shutdown. All global pointers are released.
+ * After this all schemes will appear completely unsupported
+ * until a call to InitAuthModules().
+ * Release the Auth::TheConfig handles instead to disable authentication
+ * without terminiating all support.
+ */
static void FreeAll();
- static Vector<AuthScheme*> const &Schemes();
- static AuthScheme *Find(const char *);
- typedef Vector<AuthScheme*>::iterator iterator;
- typedef Vector<AuthScheme*>::const_iterator const_iterator;
- AuthScheme() : initialised (false) {}
- virtual ~AuthScheme() {}
+ /**
+ * Locate an authentication scheme component by Name.
+ */
+ static AuthScheme::Pointer Find(const char *);
/* per scheme methods */
virtual char const *type () const = 0;
virtual void done() = 0;
virtual AuthConfig *createConfig() = 0;
+
// Not implemented
AuthScheme(AuthScheme const &);
AuthScheme &operator=(AuthScheme const&);
+ static Vector<AuthScheme::Pointer> &GetSchemes();
+
protected:
bool initialised;
private:
- static Vector<AuthScheme*> &GetSchemes();
- static Vector<AuthScheme*> *_Schemes;
+ static Vector<AuthScheme::Pointer> *_Schemes;
};
#endif /* SQUID_AUTHSCHEME_H */
--- /dev/null
+#include "config.h"
+#include "auth/State.h"
+
+CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+void
+authenticateStateFree(authenticateStateData * r)
+{
+ r->auth_user_request = NULL;
+ cbdataFree(r);
+}
--- /dev/null
+#ifndef __AUTH_AUTHENTICATE_STATE_T__
+#define __AUTH_AUTHENTICATE_STATE_T__
+
+#include "auth/UserRequest.h"
+
+/**
+ * CBDATA state for NTLM, Negotiate, and Digest stateful authentication.
+ */
+typedef struct {
+ void *data;
+ AuthUserRequest::Pointer auth_user_request;
+ RH *handler;
+} authenticateStateData;
+
+extern CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+extern void authenticateStateFree(authenticateStateData * r);
+
+#endif /* __AUTH_AUTHENTICATE_STATE_T__ */
#include "acl/Gadgets.h"
#include "event.h"
#include "SquidTime.h"
+#include "Store.h"
#if !_USE_INLINE_
#include "auth/User.cci"
#endif
// This should be converted into a pooled type. Does not need to be cbdata
-CBDATA_TYPE(auth_user_ip_t);
+CBDATA_TYPE(AuthUserIP);
-AuthUser::AuthUser (AuthConfig *aConfig) :
- auth_type (AUTH_UNKNOWN), config(aConfig),
- usernamehash (NULL), ipcount (0), expiretime (0), references (0), username_(NULL)
+time_t AuthUser::last_discard = 0;
+
+char *CredentialsState_str[] = { "Unchecked", "Ok", "Pending", "Handshake", "Failed" };
+
+
+AuthUser::AuthUser(AuthConfig *aConfig) :
+ auth_type(AUTH_UNKNOWN),
+ config(aConfig),
+ ipcount(0),
+ expiretime(0),
+ credentials_state(Unchecked),
+ username_(NULL)
{
proxy_auth_list.head = proxy_auth_list.tail = NULL;
proxy_match_cache.head = proxy_match_cache.tail = NULL;
ip_list.head = ip_list.tail = NULL;
- requests.head = requests.tail = NULL;
- debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "' with refcount '" << references << "'.");
+ debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "'.");
+}
+
+AuthUser::CredentialsState
+AuthUser::credentials() const
+{
+ return credentials_state;
}
-/* Combine two user structs. ONLY to be called from within a scheme
+void
+AuthUser::credentials(CredentialsState newCreds)
+{
+ credentials_state = newCreds;
+}
+
+
+/**
+ * Combine two user structs. ONLY to be called from within a scheme
* module. The scheme module is responsible for ensuring that the
* two users _can_ be merged without invalidating all the request
* scheme data. The scheme is also responsible for merging any user
* related scheme data itself.
*/
void
-AuthUser::absorb (AuthUser *from)
+AuthUser::absorb(AuthUser::Pointer from)
{
- AuthUserRequest *auth_user_request;
+
+ /* RefCount children CANNOT be merged like this. The external AuthUser::Pointer's cannot be changed. */
+
+ /* check that we only have the two references:
+ * 1) our function scope
+ * 2) the parsing function scope)
+ */
+ assert(from->RefCountCount() == 2);
+
/*
- * XXX combine two authuser structs. Incomplete: it should merge
- * in hash references too and ask the module to merge in scheme
- * data
+ * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
+ * dlink_list proxy_auth_list;
+ * dlink_list proxy_match_cache;
*/
+
debugs(29, 5, "authenticateAuthUserMerge auth_user '" << from << "' into auth_user '" << this << "'.");
- dlink_node *link = from->requests.head;
-
- while (link) {
- auth_user_request = static_cast<AuthUserRequest *>(link->data);
- dlink_node *tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &from->requests);
- dlinkAddTail(auth_user_request, tmplink, &requests);
- auth_user_request->user(this);
- }
- references += from->references;
- from->references = 0;
- delete from;
+ /* absorb the list of IP address sources (for max_user_ip controls) */
+ AuthUserIP *new_ipdata;
+ while (from->ip_list.head != NULL) {
+ new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data);
+
+ /* If this IP has expired - ignore the expensive merge actions. */
+ if (new_ipdata->ip_expiretime + Config.authenticateIpTTL < squid_curtime) {
+ /* This IP has expired - remove from the source list */
+ dlinkDelete(&new_ipdata->node, &(from->ip_list));
+ cbdataFree(new_ipdata);
+ /* catch incipient underflow */
+ from->ipcount--;
+ } else {
+ /* add to our list. replace if already present. */
+ AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data);
+ bool found = false;
+ while (ipdata) {
+ AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data);
+
+ if (ipdata->ipaddr == new_ipdata->ipaddr) {
+ /* This IP has already been seen. */
+ found = true;
+ /* update IP ttl and stop searching. */
+ ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime);
+ break;
+ } else if (ipdata->ip_expiretime + Config.authenticateIpTTL < squid_curtime) {
+ /* This IP has expired - cleanup the destination list */
+ dlinkDelete(&ipdata->node, &ip_list);
+ cbdataFree(ipdata);
+ /* catch incipient underflow */
+ assert(ipcount);
+ ipcount--;
+ }
+
+ ipdata = tempnode;
+ }
+
+ if (!found) {
+ /* This ip is not in the seen list. Add it. */
+ dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list);
+ ipcount++;
+ /* remove from the source list */
+ dlinkDelete(&new_ipdata->node, &(from->ip_list));
+ from->ipcount--;
+ }
+ }
+ }
}
AuthUser::~AuthUser()
{
- AuthUserRequest *auth_user_request;
- dlink_node *link, *tmplink;
- debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "' with refcount '" << references << "'.");
- assert(references == 0);
- /* were they linked in by username ? */
-
- if (usernamehash) {
- assert(usernamehash->user() == this);
- debugs(29, 5, "AuthUser::~AuthUser: removing usernamehash entry '" << usernamehash << "'");
- hash_remove_link(proxy_auth_username_cache,
- (hash_link *) usernamehash);
- /* don't free the key as we use the same user string as the auth_user
- * structure */
- delete usernamehash;
- }
-
- /* remove any outstanding requests */
- link = requests.head;
-
- while (link) {
- debugs(29, 5, "AuthUser::~AuthUser: removing request entry '" << link->data << "'");
- auth_user_request = static_cast<AuthUserRequest *>(link->data);
- tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &requests);
- dlinkNodeDelete(tmplink);
- delete auth_user_request;
- }
+ debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "'.");
+ assert(RefCountCount() == 0);
/* free cached acl results */
aclCacheMatchFlush(&proxy_match_cache);
{
if (!proxy_auth_username_cache) {
/* First time around, 7921 should be big enough */
- proxy_auth_username_cache =
- hash_create((HASHCMP *) strcmp, 7921, hash_string);
+ proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
assert(proxy_auth_username_cache);
eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1);
+ last_discard = squid_curtime;
}
}
AuthUser::CachedACLsReset()
{
/*
- * We walk the hash by username as that is the unique key we use.
* This must complete all at once, because we are ensuring correctness.
*/
AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- char const *username = NULL;
+ AuthUser::Pointer auth_user;
debugs(29, 3, "AuthUser::CachedACLsReset: Flushing the ACL caches for all users.");
hash_first(proxy_auth_username_cache);
while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
auth_user = usernamehash->user();
- username = auth_user->username();
/* free cached acl results */
aclCacheMatchFlush(&auth_user->proxy_match_cache);
-
}
debugs(29, 3, "AuthUser::CachedACLsReset: Finished.");
* entries at a time. Lets see how it flys first.
*/
AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
char const *username = NULL;
debugs(29, 3, "AuthUser::cacheCleanup: Cleaning the user cache now");
debugs(29, 3, "AuthUser::cacheCleanup: Current time: " << current_time.tv_sec);
auth_user = usernamehash->user();
username = auth_user->username();
- /* if we need to have inpedendent expiry clauses, insert a module call
+ /* if we need to have indedendent expiry clauses, insert a module call
* here */
debugs(29, 4, "AuthUser::cacheCleanup: Cache entry:\n\tType: " <<
auth_user->auth_type << "\n\tUsername: " << username <<
"\n\texpires: " <<
(long int) (auth_user->expiretime + Config.authenticateTTL) <<
- "\n\treferences: " << (long int) auth_user->references);
+ "\n\treferences: " << (long int) auth_user->RefCountCount());
if (auth_user->expiretime + Config.authenticateTTL <= current_time.tv_sec) {
debugs(29, 5, "AuthUser::cacheCleanup: Removing user " << username << " from cache due to timeout.");
- /* the minus 1 accounts for the cache lock */
-
- if (!(authenticateAuthUserInuse(auth_user) - 1))
- /* we don't warn if we leave the user in the cache,
- * because other modules (ie delay pools) may keep
- * locks on users, and thats legitimate
- */
- auth_user->unlock();
+
+ /* Old credentials are always removed. Existing users must hold their own
+ * AuthUser::Pointer to the credentials. Cache exists only for finding
+ * and re-using current valid credentials.
+ */
+ hash_remove_link(proxy_auth_username_cache, usernamehash);
+ delete usernamehash;
}
}
debugs(29, 3, "AuthUser::cacheCleanup: Finished cleaning the user cache.");
eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1);
+ last_discard = squid_curtime;
}
void
AuthUser::clearIp()
{
- auth_user_ip_t *ipdata, *tempnode;
+ AuthUserIP *ipdata, *tempnode;
- ipdata = (auth_user_ip_t *) ip_list.head;
+ ipdata = (AuthUserIP *) ip_list.head;
while (ipdata) {
- tempnode = (auth_user_ip_t *) ipdata->node.next;
+ tempnode = (AuthUserIP *) ipdata->node.next;
/* walk the ip list */
dlinkDelete(&ipdata->node, &ip_list);
cbdataFree(ipdata);
void
AuthUser::removeIp(Ip::Address ipaddr)
{
- auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
+ AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
while (ipdata) {
/* walk the ip list */
return;
}
- ipdata = (auth_user_ip_t *) ipdata->node.next;
+ ipdata = (AuthUserIP *) ipdata->node.next;
}
}
void
AuthUser::addIp(Ip::Address ipaddr)
{
- auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
+ AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
int found = 0;
- CBDATA_INIT_TYPE(auth_user_ip_t);
+ CBDATA_INIT_TYPE(AuthUserIP);
/*
* we walk the entire list to prevent the first item in the list
* a timeout+reconfigure
*/
while (ipdata) {
- auth_user_ip_t *tempnode = (auth_user_ip_t *) ipdata->node.next;
+ AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
/* walk the ip list */
if (ipdata->ipaddr == ipaddr) {
return;
/* This ip is not in the seen list */
- ipdata = cbdataAlloc(auth_user_ip_t);
+ ipdata = cbdataAlloc(AuthUserIP);
ipdata->ip_expiretime = squid_curtime;
debugs(29, 2, "authenticateAuthUserAddIp: user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
}
-
+/**
+ * Add the AuthUser structure to the username cache.
+ */
void
-AuthUser::lock()
+AuthUser::addToNameCache()
{
- debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "'.");
- assert(this != NULL);
- references++;
- debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "' now at '" << references << "'.");
+ /* AuthUserHashPointer will self-register with the username cache */
+ new AuthUserHashPointer(this);
}
+/**
+ * Dump the username cache statictics for viewing...
+ */
void
-AuthUser::unlock()
+AuthUser::UsernameCacheStats(StoreEntry *output)
{
- debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "'.");
- assert(this != NULL);
+ AuthUserHashPointer *usernamehash;
- if (references > 0) {
- references--;
- } else {
- debugs(29, 1, "Attempt to lower Auth User " << this << " refcount below 0!");
- }
+ /* overview of username cache */
+ storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
+ storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n", static_cast<int32_t>(last_discard + Config.authenticateGCInterval - squid_curtime));
- debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "' now at '" << references << "'.");
+ /* cache dump column titles */
+ storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
+ "Type",
+ "State",
+ "Check TTL",
+ "Cache TTL",
+ "Username");
+ storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
- if (references == 0)
- delete this;
-}
-
-/* addToNameCache: add a auth_user structure to the username cache */
-void
-AuthUser::addToNameCache()
-{
- usernamehash = new AuthUserHashPointer (this);
+ hash_first(proxy_auth_username_cache);
+ while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
+ AuthUser::Pointer auth_user = usernamehash->user();
+
+ storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
+ AuthType_str[auth_user->auth_type],
+ CredentialsState_str[auth_user->credentials()],
+ auth_user->ttl(),
+ static_cast<int32_t>(auth_user->expiretime - squid_curtime + Config.authenticateTTL),
+ auth_user->username()
+ );
+ }
}
}
void
-AuthUser::username(char const*aString)
+AuthUser::username(char const *aString)
{
if (aString) {
assert(!username_);
safe_free(username_);
}
}
-
-void
-AuthUser::addRequest(AuthUserRequest *request)
-{
- /* lock for the request link */
-
- lock();
- dlink_node *node = dlinkNodeNew();
-
- dlinkAdd(request, node, &requests);
-}
#ifndef SQUID_AUTHUSER_H
#define SQUID_AUTHUSER_H
-class AuthUserRequest;
+#include "auth/AuthType.h"
+#include "dlink.h"
+#include "ip/Address.h"
+#include "RefCount.h"
+
class AuthConfig;
class AuthUserHashPointer;
-
-/* for auth_type_t */
-#include "enums.h"
-
-#include "ip/Address.h"
-#include "dlink.h"
+class StoreEntry;
/**
* \ingroup AuthAPI
* structure is the cached ACL match results. This structure, is private to
* the authentication framework.
*/
-class AuthUser
+class AuthUser : public RefCountable
{
-
public:
+ typedef RefCount<AuthUser> Pointer;
+
/* extra fields for proxy_auth */
/* auth_type and auth_module are deprecated. Do Not add new users of these fields.
* Aim to remove shortly
*/
/** \deprecated this determines what scheme owns the user data. */
- auth_type_t auth_type;
+ AuthType auth_type;
/** the config for this user */
AuthConfig *config;
- /** we only have one username associated with a given auth_user struct */
- AuthUserHashPointer *usernamehash;
/** we may have many proxy-authenticate strings that decode to the same user */
dlink_list proxy_auth_list;
dlink_list proxy_match_cache;
size_t ipcount;
long expiretime;
- /** how many references are outstanding to this instance */
- size_t references;
- /** the auth_user_request structures that link to this. Yes it could be a splaytree
- * but how many requests will a single username have in parallel? */
- dlink_list requests;
static void cacheInit();
static void CachedACLsReset();
- void absorb(AuthUser *from);
+ void absorb(AuthUser::Pointer from);
virtual ~AuthUser();
_SQUID_INLINE_ char const *username() const;
_SQUID_INLINE_ void username(char const *);
+
+ /**
+ * How long these credentials are still valid for.
+ * Negative numbers means already expired.
+ */
+ virtual int32_t ttl() const = 0;
+
+ /* Manage list of IPs using this username */
void clearIp();
void removeIp(Ip::Address);
void addIp(Ip::Address);
- _SQUID_INLINE_ void addRequest(AuthUserRequest *);
-
- void lock();
- void unlock();
void addToNameCache();
+ static void UsernameCacheStats(StoreEntry * output);
+
+ enum CredentialsState { Unchecked, Ok, Pending, Handshake, Failed };
+ CredentialsState credentials() const;
+ void credentials(CredentialsState);
+
+private:
+ /**
+ * The current state these credentials are in:
+ * Unchecked
+ * Authenticated
+ * Pending helper result
+ * Handshake happening in stateful auth.
+ * Failed auth
+ */
+ CredentialsState credentials_state;
protected:
- AuthUser (AuthConfig *);
+ AuthUser(AuthConfig *);
private:
- static void cacheCleanup (void *unused);
+ /**
+ * Garbage Collection for the username cache.
+ */
+ static void cacheCleanup(void *unused);
+ static time_t last_discard; /// Time of last username cache garbage collection.
/**
* DPW 2007-05-08
dlink_list ip_list;
};
+extern char *CredentialsState_str[];
+
#if _USE_INLINE_
#include "auth/User.cci"
#endif
-
/*
* $Id$
*
/* Generic Functions */
-size_t
-AuthUserRequest::refCount () const
-{
- return references;
-}
-
char const *
AuthUserRequest::username() const
{
- if (user())
+ if (user() != NULL)
return user()->username();
else
return NULL;
}
-size_t
-authenticateRequestRefCount (AuthUserRequest *aRequest)
-{
- return aRequest->refCount();
-}
-
/**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
/* send the initial data to an authenticator module */
AuthUserRequest::start(RH * handler, void *data)
{
assert(handler);
+ assert(data);
debugs(29, 9, "authenticateStart: auth_user_request '" << this << "'");
module_start(handler, data);
}
-/*
- * Check a auth_user pointer for validity. Does not check passwords, just data
- * sensability. Broken or Unknown auth_types are not valid for use...
- */
-
-int
-authenticateValidateUser(AuthUserRequest * auth_user_request)
+bool
+AuthUserRequest::valid() const
{
- debugs(29, 9, "authenticateValidateUser: Validating Auth_user request '" << auth_user_request << "'.");
-
- if (auth_user_request == NULL) {
- debugs(29, 4, "authenticateValidateUser: Auth_user_request was NULL!");
- return 0;
- }
+ debugs(29, 9, HERE << "Validating AuthUserRequest '" << this << "'.");
- if (auth_user_request->user() == NULL) {
- debugs(29, 4, "authenticateValidateUser: No associated auth_user structure");
- return 0;
+ if (user() == NULL) {
+ debugs(29, 4, HERE << "No associated AuthUser data");
+ return false;
}
- if (auth_user_request->user()->auth_type == AUTH_UNKNOWN) {
- debugs(29, 4, "authenticateValidateUser: Auth_user '" << auth_user_request->user() << "' uses unknown scheme.");
- return 0;
+ if (user()->auth_type == AUTH_UNKNOWN) {
+ debugs(29, 4, HERE << "AuthUser '" << user() << "' uses unknown scheme.");
+ return false;
}
- if (auth_user_request->user()->auth_type == AUTH_BROKEN) {
- debugs(29, 4, "authenticateValidateUser: Auth_user '" << auth_user_request->user() << "' is broken for it's scheme.");
- return 0;
+ if (user()->auth_type == AUTH_BROKEN) {
+ debugs(29, 4, HERE << "AuthUser '" << user() << "' is broken for it's scheme.");
+ return false;
}
/* any other sanity checks that we need in the future */
- /* Thus should a module call to something like authValidate */
-
/* finally return ok */
- debugs(29, 5, "authenticateValidateUser: Validated Auth_user request '" << auth_user_request << "'.");
-
- return 1;
-
+ debugs(29, 5, HERE << "Validated. AuthUserRequest '" << this << "'.");
+ return true;
}
void *
fatal ("AuthUserRequest child failed to override operator delete\n");
}
-AuthUserRequest::AuthUserRequest():_auth_user(NULL), message(NULL),
- references (0), lastReply (AUTH_ACL_CANNOT_AUTHENTICATE)
+AuthUserRequest::AuthUserRequest():
+ _auth_user(NULL),
+ message(NULL),
+ lastReply(AUTH_ACL_CANNOT_AUTHENTICATE)
{
- debugs(29, 5, "AuthUserRequest::AuthUserRequest: initialised request " <<
- this);
+ debugs(29, 5, "AuthUserRequest::AuthUserRequest: initialised request " << this);
}
AuthUserRequest::~AuthUserRequest()
{
- dlink_node *link;
- debugs(29, 5, "AuthUserRequest::~AuthUserRequest: freeing request " <<
- this);
- assert(references == 0);
-
- if (user()) {
- /* unlink from the auth_user struct */
- link = user()->requests.head;
-
- while (link && (link->data != this))
- link = link->next;
-
- assert(link != NULL);
-
- dlinkDelete(link, &user()->requests);
-
- dlinkNodeDelete(link);
-
- /* unlock the request structure's lock */
- user()->unlock();
+ assert(RefCountCount()==0);
+ debugs(29, 5, "AuthUserRequest::~AuthUserRequest: freeing request " << this);
+ if (user() != NULL) {
+ /* release our references to the user credentials */
user(NULL);
}
- safe_free (message);
+ safe_free(message);
}
void
-AuthUserRequest::setDenyMessage (char const *aString)
+AuthUserRequest::setDenyMessage(char const *aString)
{
- safe_free (message);
- message = xstrdup (aString);
+ safe_free(message);
+ message = xstrdup(aString);
}
char const *
-AuthUserRequest::getDenyMessage ()
+AuthUserRequest::getDenyMessage()
{
return message;
}
}
static void
-authenticateAuthUserRequestSetIp(AuthUserRequest * auth_user_request, Ip::Address &ipaddr)
+authenticateAuthUserRequestSetIp(AuthUserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
{
- AuthUser *auth_user = auth_user_request->user();
+ AuthUser::Pointer auth_user = auth_user_request->user();
if (!auth_user)
return;
}
void
-authenticateAuthUserRequestRemoveIp(AuthUserRequest * auth_user_request, Ip::Address const &ipaddr)
+authenticateAuthUserRequestRemoveIp(AuthUserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
{
- AuthUser *auth_user = auth_user_request->user();
+ AuthUser::Pointer auth_user = auth_user_request->user();
if (!auth_user)
return;
}
void
-authenticateAuthUserRequestClearIp(AuthUserRequest * auth_user_request)
+authenticateAuthUserRequestClearIp(AuthUserRequest::Pointer auth_user_request)
{
- if (auth_user_request)
+ if (auth_user_request != NULL)
auth_user_request->user()->clearIp();
}
int
-authenticateAuthUserRequestIPCount(AuthUserRequest * auth_user_request)
+authenticateAuthUserRequestIPCount(AuthUserRequest::Pointer auth_user_request)
{
- assert(auth_user_request);
- assert(auth_user_request->user());
+ assert(auth_user_request != NULL);
+ assert(auth_user_request->user() != NULL);
return auth_user_request->user()->ipcount;
}
* authenticateUserAuthenticated: is this auth_user structure logged in ?
*/
int
-authenticateUserAuthenticated(AuthUserRequest * auth_user_request)
+authenticateUserAuthenticated(AuthUserRequest::Pointer auth_user_request)
{
- if (!authenticateValidateUser(auth_user_request))
+ if (auth_user_request == NULL || !auth_user_request->valid())
return 0;
return auth_user_request->authenticated();
* This is basically a handle approach.
*/
static void
-authenticateAuthenticateUser(AuthUserRequest * auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+authenticateAuthenticateUser(AuthUserRequest::Pointer auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
{
- assert(auth_user_request != NULL);
+ assert(auth_user_request.getRaw() != NULL);
auth_user_request->authenticate(request, conn, type);
}
-static AuthUserRequest *
-authTryGetUser (AuthUserRequest **auth_user_request, ConnStateData * conn, HttpRequest * request)
+static AuthUserRequest::Pointer
+authTryGetUser(AuthUserRequest::Pointer auth_user_request, ConnStateData * conn, HttpRequest * request)
{
- if (*auth_user_request)
- return *auth_user_request;
- else if (request != NULL && request->auth_user_request)
+ if (auth_user_request != NULL)
+ return auth_user_request;
+ else if (request != NULL && request->auth_user_request != NULL)
return request->auth_user_request;
else if (conn != NULL)
return conn->auth_user_request;
*
* Caller is responsible for locking and unlocking their *auth_user_request!
*/
-auth_acl_t
-AuthUserRequest::authenticate(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
+AuthAclState
+AuthUserRequest::authenticate(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
{
const char *proxy_auth;
assert(headertype != 0);
* connection when we recieve no authentication header.
*/
- if (((proxy_auth == NULL) && (!authenticateUserAuthenticated(authTryGetUser(auth_user_request,conn,request))))
- || (conn != NULL && conn->auth_type == AUTH_BROKEN)) {
+ /* a) can we find other credentials to use? and b) are they logged in already? */
+ if (proxy_auth == NULL && !authenticateUserAuthenticated(authTryGetUser(*auth_user_request,conn,request))) {
/* no header or authentication failed/got corrupted - restart */
- debugs(29, 4, "authenticateAuthenticate: broken auth or no proxy_auth header. Requesting auth header.");
+ debugs(29, 4, HERE << "No Proxy-Auth header and no working alternative. Requesting auth header.");
+
/* something wrong with the AUTH credentials. Force a new attempt */
+ /* connection auth we must reset on auth errors */
if (conn != NULL) {
- conn->auth_type = AUTH_UNKNOWN;
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
+ conn->auth_user_request = NULL;
}
*auth_user_request = NULL;
* No check for function required in the if: its compulsory for conn based
* auth modules
*/
- if (proxy_auth && conn != NULL && conn->auth_user_request &&
+ if (proxy_auth && conn != NULL && conn->auth_user_request != NULL &&
authenticateUserAuthenticated(conn->auth_user_request) &&
conn->auth_user_request->connLastHeader() != NULL &&
strcmp(proxy_auth, conn->auth_user_request->connLastHeader())) {
- debugs(29, 2, "authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
+ debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
conn->auth_user_request << ", Current user '" <<
conn->auth_user_request->username() << "' proxy_auth " <<
proxy_auth);
- /* remove this request struct - the link is already authed and it can't be to
- * reauth.
- */
+ /* remove this request struct - the link is already authed and it can't be to reauth. */
/* This should _only_ ever occur on the first pass through
* authenticateAuthenticate
*/
assert(*auth_user_request == NULL);
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
- /* Set the connection auth type */
- conn->auth_type = AUTH_UNKNOWN;
+ conn->auth_user_request = NULL;
}
/* we have a proxy auth header and as far as we know this connection has
* not had bungled connection oriented authentication happen on it. */
- debugs(29, 9, "authenticateAuthenticate: header " << (proxy_auth ? proxy_auth : "-") << ".");
+ debugs(29, 9, HERE << "header " << (proxy_auth ? proxy_auth : "-") << ".");
if (*auth_user_request == NULL) {
- debugs(29, 9, "authenticateAuthenticate: This is a new checklist test on FD:" << (conn != NULL ? conn->fd : -1) );
+ debugs(29, 9, HERE << "This is a new checklist test on FD:" << (conn != NULL ? conn->fd : -1) );
- if (proxy_auth && !request->auth_user_request && conn != NULL && conn->auth_user_request) {
+ if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->auth_user_request != NULL) {
AuthConfig * scheme = AuthConfig::Find(proxy_auth);
- if (!conn->auth_user_request->user() || conn->auth_user_request->user()->config != scheme) {
- debugs(29, 1, "authenticateAuthenticate: Unexpected change of authentication scheme from '" <<
+ if (conn->auth_user_request->user() == NULL || conn->auth_user_request->user()->config != scheme) {
+ debugs(29, 1, "WARNING: Unexpected change of authentication scheme from '" <<
conn->auth_user_request->user()->config->type() <<
"' to '" << proxy_auth << "' (client " <<
src_addr << ")");
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
- conn->auth_type = AUTH_UNKNOWN;
+ conn->auth_user_request = NULL;
}
}
- if ((!request->auth_user_request)
- && (conn == NULL || conn->auth_type == AUTH_UNKNOWN)) {
+ if (request->auth_user_request == NULL && (conn == NULL || conn->auth_user_request == NULL)) {
/* beginning of a new request check */
- debugs(29, 4, "authenticateAuthenticate: no connection authentication type");
+ debugs(29, 4, HERE << "No connection authentication type");
*auth_user_request = AuthConfig::CreateAuthUser(proxy_auth);
- if (!authenticateValidateUser(*auth_user_request)) {
- if (*auth_user_request == NULL)
- return AUTH_ACL_CHALLENGE;
-
+ if (*auth_user_request == NULL)
+ return AUTH_ACL_CHALLENGE;
+ else if (!(*auth_user_request)->valid()) {
/* the decode might have left a username for logging, or a message to
* the user */
if ((*auth_user_request)->username()) {
request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
}
*auth_user_request = NULL;
return AUTH_ACL_CHALLENGE;
}
- /* the user_request comes prelocked for the caller to createAuthUser (us) */
- } else if (request->auth_user_request) {
+ } else if (request->auth_user_request != NULL) {
*auth_user_request = request->auth_user_request;
} else {
assert (conn != NULL);
- if (conn->auth_user_request) {
+ if (conn->auth_user_request != NULL) {
*auth_user_request = conn->auth_user_request;
} else {
/* failed connection based authentication */
- debugs(29, 4, "authenticateAuthenticate: Auth user request " <<
+ debugs(29, 4, HERE << "Auth user request " <<
*auth_user_request << " conn-auth user request " <<
conn->auth_user_request << " conn type " <<
- conn->auth_type << " authentication failed.");
+ conn->auth_user_request->user()->auth_type << " authentication failed.");
*auth_user_request = NULL;
return AUTH_ACL_CHALLENGE;
if (!authenticateUserAuthenticated(*auth_user_request)) {
/* User not logged in. Log them in */
- authenticateAuthenticateUser(*auth_user_request, request,
- conn, headertype);
+ authenticateAuthenticateUser(*auth_user_request, request, conn, headertype);
switch (authenticateDirection(*auth_user_request)) {
case 1:
- if (NULL == request->auth_user_request) {
+ if (request->auth_user_request == NULL) {
request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
}
/* fallthrough to -2 */
if ((*auth_user_request)->username()) {
if (!request->auth_user_request) {
request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
}
}
/* copy username to request for logging on client-side */
/* the credentials are correct at this point */
- if (NULL == request->auth_user_request) {
+ if (request->auth_user_request == NULL) {
request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
authenticateAuthUserRequestSetIp(*auth_user_request, src_addr);
}
return AUTH_AUTHENTICATED;
}
-auth_acl_t
-
-AuthUserRequest::tryToAuthenticateAndSetAuthUser(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
+AuthAclState
+AuthUserRequest::tryToAuthenticateAndSetAuthUser(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
{
/* If we have already been called, return the cached value */
- AuthUserRequest *t = authTryGetUser (auth_user_request, conn, request);
+ AuthUserRequest::Pointer t = authTryGetUser(*auth_user_request, conn, request);
- if (t && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE
- && t->lastReply != AUTH_ACL_HELPER) {
- if (!*auth_user_request)
+ if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
+ if (*auth_user_request == NULL)
*auth_user_request = t;
- if (!request->auth_user_request && t->lastReply == AUTH_AUTHENTICATED) {
+ if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
request->auth_user_request = t;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
}
return t->lastReply;
}
/* ok, call the actual authenticator routine. */
- auth_acl_t result = authenticate(auth_user_request, headertype, request, conn, src_addr);
+ AuthAclState result = authenticate(auth_user_request, headertype, request, conn, src_addr);
- t = authTryGetUser (auth_user_request, conn, request);
+ t = authTryGetUser(*auth_user_request, conn, request);
- if (t && result != AUTH_ACL_CANNOT_AUTHENTICATE &&
- result != AUTH_ACL_HELPER)
+ if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
t->lastReply = result;
return result;
* -2: authenticate broken in some fashion
*/
int
-authenticateDirection(AuthUserRequest * auth_user_request)
+authenticateDirection(AuthUserRequest::Pointer auth_user_request)
{
- if (!auth_user_request)
+ if (auth_user_request == NULL || auth_user_request->user() == NULL)
return -2;
return auth_user_request->direction();
}
void
-AuthUserRequest::addReplyAuthHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal)
+AuthUserRequest::addReplyAuthHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
/* send the auth types we are configured to support (and have compiled in!) */
{
http_hdr_type type;
else {
/* call each configured & running authscheme */
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i) {
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i) {
AuthConfig *scheme = *i;
if (scheme->active())
}
void
-authenticateFixHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal)
+authenticateFixHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
{
AuthUserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal);
}
/* call the active auth module and allow it to add a trailer to the request */
void
-authenticateAddTrailer(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated)
+authenticateAddTrailer(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated)
{
if (auth_user_request != NULL)
auth_user_request->addTrailer(rep, accelerated);
}
-void
-
-AuthUserRequest::_lock()
-{
- assert(this);
- debugs(29, 9, "AuthUserRequest::lock: auth_user request '" << this << " " << references << "->" << references+1);
- ++references;
-}
-
-void
-AuthUserRequest::_unlock()
-{
- assert(this != NULL);
-
- if (references > 0) {
- debugs(29, 9, "AuthUserRequest::unlock: auth_user request '" << this << " " << references << "->" << references-1);
- --references;
- } else {
- debugs(29, 1, "Attempt to lower Auth User request " << this << " refcount below 0!");
- }
-
- if (references == 0) {
- debugs(29, 9, "AuthUserRequest::unlock: deleting auth_user_request '" << this << "'.");
- /* not locked anymore */
- delete this;
- }
-}
-
-AuthScheme *
+AuthScheme::Pointer
AuthUserRequest::scheme() const
{
/* TODO: this should be overriden by the child and be essentially a no-op */
-
/*
* $Id$
*
#ifndef SQUID_AUTHUSERREQUEST_H
#define SQUID_AUTHUSERREQUEST_H
-#include "client_side.h"
-
-class AuthUser;
+#include "auth/AuthAclState.h"
+#include "auth/Scheme.h"
+#include "auth/User.h"
+#include "dlink.h"
+#include "ip/Address.h"
+#include "typedefs.h"
+#include "HttpHeader.h"
class ConnStateData;
+class HttpReply;
+class HttpRequest;
-class AuthScheme;
-
-struct AuthUserIP {
+/// \ingroup AuthAPI
+class AuthUserIP
+{
+public:
dlink_node node;
/* IP addr this user authenticated from */
/**
\ingroup AuthAPI
* This is a short lived structure is the visible aspect of the authentication framework.
+ *
+ * It and its children hold the state data while processing authentication for a client request.
+ * The AuthenticationStateData object is merely a CBDATA wrapper for one of these.
*/
-class AuthUserRequest
+class AuthUserRequest : public RefCountable
{
+public:
+ typedef RefCount<AuthUserRequest> Pointer;
public:
/**
* it has request specific data, and links to user specific data
* the user
*/
- AuthUser *_auth_user;
+ AuthUser::Pointer _auth_user;
/**
* Used by squid to determine what the next step in performing authentication for a given scheme is.
\retval false Timeouts on cached credentials have occurred or for any reason the credentials are not valid.
*/
virtual int authenticated() const = 0;
+
+ /**
+ * Check a auth_user pointer for validity.
+ * Does not check passwords, just data sensability. Broken or Unknown auth_types are not valid for use...
+ *
+ * \retval false User credentials are missing.
+ * \retval false User credentials use an unknown scheme type.
+ * \retval false User credentials are broken for their scheme.
+ *
+ * \retval true User credentials exist and may be able to authenticate.
+ */
+ bool valid() const;
+
virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) = 0;
/* template method */
virtual int module_direction() = 0;
*/
virtual void module_start(RH *handler, void *data) = 0;
- virtual AuthUser *user() {return _auth_user;}
+ virtual AuthUser::Pointer user() {return _auth_user;}
- virtual const AuthUser *user() const {return _auth_user;}
+ virtual const AuthUser::Pointer user() const {return _auth_user;}
- virtual void user(AuthUser *aUser) {_auth_user=aUser;}
+ virtual void user(AuthUser::Pointer aUser) {_auth_user=aUser;}
- static auth_acl_t tryToAuthenticateAndSetAuthUser(AuthUserRequest **, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &);
- static void addReplyAuthHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal);
+ static AuthAclState tryToAuthenticateAndSetAuthUser(AuthUserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &);
+ static void addReplyAuthHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal);
AuthUserRequest();
/** Possibly overrideable in future */
char const * getDenyMessage();
- size_t refCount() const;
- void _lock(); /**< \note please use AUTHUSERREQUESTLOCK() */
- void _unlock(); /**< \note please use AUTHUSERREQUESTUNLOCK() */
-
/**
* Squid does not make assumptions about where the username is stored.
* This function must return a pointer to a NULL terminated string to be used in logging the request.
*/
char const *username() const;
- AuthScheme *scheme() const;
+ AuthScheme::Pointer scheme() const;
virtual const char * connLastHeader();
private:
- static auth_acl_t authenticate(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr);
+ static AuthAclState authenticate(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr);
/** return a message on the 407 error pages */
char *message;
- /** how many 'processes' are working on this data */
- size_t references;
-
/**
* We only attempt authentication once per http request. This
* is to allow multiple auth acl references from different _access areas
* when using connection based authentication
*/
- auth_acl_t lastReply;
+ AuthAclState lastReply;
};
/* AuthUserRequest */
-/**
- \ingroup AuthAPI
- \deprecated Use AuthUserRequest::refCount() instead.
- */
-extern size_t authenticateRequestRefCount (AuthUserRequest *);
-
/// \ingroup AuthAPI
-extern void authenticateFixHeader(HttpReply *, AuthUserRequest *, HttpRequest *, int, int);
+extern void authenticateFixHeader(HttpReply *, AuthUserRequest::Pointer, HttpRequest *, int, int);
/// \ingroup AuthAPI
-extern void authenticateAddTrailer(HttpReply *, AuthUserRequest *, HttpRequest *, int);
+extern void authenticateAddTrailer(HttpReply *, AuthUserRequest::Pointer, HttpRequest *, int);
/// \ingroup AuthAPI
-extern void authenticateAuthUserRequestRemoveIp(AuthUserRequest *, Ip::Address const &);
+extern void authenticateAuthUserRequestRemoveIp(AuthUserRequest::Pointer, Ip::Address const &);
/// \ingroup AuthAPI
-extern void authenticateAuthUserRequestClearIp(AuthUserRequest *);
+extern void authenticateAuthUserRequestClearIp(AuthUserRequest::Pointer);
/// \ingroup AuthAPI
-extern int authenticateAuthUserRequestIPCount(AuthUserRequest *);
+extern int authenticateAuthUserRequestIPCount(AuthUserRequest::Pointer);
/// \ingroup AuthAPI
/// \deprecated Use AuthUserRequest::direction() instead.
-extern int authenticateDirection(AuthUserRequest *);
+extern int authenticateDirection(AuthUserRequest::Pointer);
/// \ingroup AuthAPI
/// See AuthUserRequest::authenticated()
-extern int authenticateUserAuthenticated(AuthUserRequest *);
-/// \ingroup AuthAPI
-extern int authenticateValidateUser(AuthUserRequest *);
-
-/// \todo Drop dead code? or make a debugging option.
-#if 0
-#define AUTHUSERREQUESTUNLOCK(a,b) if(a){(a)->_unlock();debugs(0,0,HERE << "auth_user_request " << a << " was unlocked for " << b); (a)=NULL;}
-#define AUTHUSERREQUESTLOCK(a,b) { (a)->_lock(); debugs(0,0,HERE << "auth_user_request " << a << " was locked for " << b); }
-#endif
-#define AUTHUSERREQUESTUNLOCK(a,b) if(a){(a)->_unlock();(a)=NULL;}
-#define AUTHUSERREQUESTLOCK(a,b) (a)->_lock()
+extern int authenticateUserAuthenticated(AuthUserRequest::Pointer);
#endif /* SQUID_AUTHUSERREQUEST_H */
#include "squid.h"
-#include "auth_basic.h"
+#include "auth/basic/auth_basic.h"
+#include "auth/basic/basicScheme.h"
+#include "auth/basic/basicUserRequest.h"
#include "auth/Gadgets.h"
+#include "auth/State.h"
#include "CacheManager.h"
#include "Store.h"
#include "HttpReply.h"
-#include "basicScheme.h"
#include "rfc1738.h"
#include "wordlist.h"
#include "SquidTime.h"
-static void
-authenticateStateFree(AuthenticateStateData * r)
-{
- cbdataFree(r);
-}
-
/* Basic Scheme */
-
static HLPCB authenticateBasicHandleReply;
static AUTHSSTATS authenticateBasicStats;
static helper *basicauthenticators = NULL;
-static AuthBasicConfig basicConfig;
-
static int authbasic_initialised = 0;
/* internal functions */
-/* TODO: move to basicScheme.cc - after all per request and user functions are moved out */
-void
-basicScheme::done()
-{
- /* TODO: this should be a Config call. */
-
- if (basicauthenticators)
- helperShutdown(basicauthenticators);
-
- authbasic_initialised = 0;
-
- if (!shutting_down)
- return;
-
- delete basicauthenticators;
- basicauthenticators = NULL;
-
- /* XXX Reinstate auth shutdown for dynamic schemes? */
- debugs(29, DBG_CRITICAL, HERE << "Basic authentication Shutdown.");
-}
-
bool
AuthBasicConfig::active() const
{
const char *
AuthBasicConfig::type() const
{
- return basicScheme::GetInstance().type();
+ return basicScheme::GetInstance()->type();
}
-AuthBasicUserRequest::AuthBasicUserRequest() : _theUser(NULL)
-{}
+int32_t
+BasicUser::ttl() const
+{
+ if (credentials() != Ok && credentials() != Pending)
+ return -1; // TTL is obsolete NOW.
-AuthBasicUserRequest::~AuthBasicUserRequest()
-{}
+ int32_t basic_ttl = expiretime - squid_curtime + static_cast<AuthBasicConfig*>(config)->credentialsTTL;
+ int32_t global_ttl = static_cast<int32_t>(expiretime - squid_curtime + Config.authenticateTTL);
+ return min(basic_ttl, global_ttl);
+}
bool
BasicUser::authenticated() const
{
- if ((flags.credentials_ok == 1) && (credentials_checkedtime + basicConfig.credentialsTTL > squid_curtime))
+ if ((credentials() == Ok) && (expiretime + static_cast<AuthBasicConfig*>(config)->credentialsTTL > squid_curtime))
return true;
debugs(29, 4, "User not authenticated or credentials need rechecking.");
return false;
}
-int
-AuthBasicUserRequest::authenticated() const
-{
- BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user());
-
- if (basic_auth && basic_auth->authenticated())
- return 1;
-
- return 0;
-}
-
-/* log a basic user in
- */
void
-AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+AuthBasicConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
{
- assert(user() != NULL);
-
- basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
-
- /* if the password is not ok, do an identity */
-
- if (!basic_auth || basic_auth->flags.credentials_ok != 1)
- return;
-
- /* are we about to recheck the credentials externally? */
- if ((basic_auth->credentials_checkedtime + basicConfig.credentialsTTL) <= squid_curtime) {
- debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking");
- return;
+ if (authenticate) {
+ debugs(29, 9, HERE << "Sending type:" << hdrType << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
+ httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"%s\"", basicAuthRealm);
}
-
- /* we have been through the external helper, and the credentials haven't expired */
- debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated");
-
- /* Decode now takes care of finding the AuthUser struct in the cache */
- /* after external auth occurs anyway */
- basic_auth->expiretime = current_time.tv_sec;
-
- return;
}
-int
-AuthBasicUserRequest::module_direction()
+void
+AuthBasicConfig::rotateHelpers()
{
- /* null auth_user is checked for by authenticateDirection */
- basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
- assert (basic_auth);
-
- switch (basic_auth->flags.credentials_ok) {
-
- case 0: /* not checked */
- return -1;
-
- case 1: /* checked & ok */
-
- if (basic_auth->credentials_checkedtime + basicConfig.credentialsTTL <= squid_curtime)
- return -1;
-
- return 0;
-
- case 2: /* paused while waiting for a username:password check on another request */
- return -1;
-
- case 3: /* authentication process failed. */
- return 0;
+ /* schedule closure of existing helpers */
+ if (basicauthenticators) {
+ helperShutdown(basicauthenticators);
}
- return -2;
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
}
+/** shutdown the auth helpers and free any allocated configuration details */
void
-AuthBasicConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+AuthBasicConfig::done()
{
- if (authenticate) {
- debugs(29, 9, HERE << "Sending type:" << hdrType << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
- httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"%s\"", basicAuthRealm);
+ authbasic_initialised = 0;
+
+ if (basicauthenticators) {
+ helperShutdown(basicauthenticators);
}
-}
-/* free any allocated configuration details */
-void
-AuthBasicConfig::done()
-{
+ delete basicauthenticators;
+ basicauthenticators = NULL;
+
if (authenticate)
wordlistDestroy(&authenticate);
static void
authenticateBasicHandleReply(void *data, char *reply)
{
- AuthenticateStateData *r = static_cast<AuthenticateStateData *>(data);
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
BasicAuthQueueNode *tmpnode;
char *t = NULL;
void *cbdata;
assert(r->auth_user_request != NULL);
assert(r->auth_user_request->user()->auth_type == AUTH_BASIC);
- basic_data *basic_auth = dynamic_cast<basic_data *>(r->auth_user_request->user());
+
+ /* this is okay since we only play with the BasicUser child fields below
+ * and dont pass the pointer itself anywhere */
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(r->auth_user_request->user().getRaw());
assert(basic_auth != NULL);
if (reply && (strncasecmp(reply, "OK", 2) == 0))
- basic_auth->flags.credentials_ok = 1;
+ basic_auth->credentials(AuthUser::Ok);
else {
- basic_auth->flags.credentials_ok = 3;
+ basic_auth->credentials(AuthUser::Failed);
if (t && *t)
r->auth_user_request->setDenyMessage(t);
}
- basic_auth->credentials_checkedtime = squid_curtime;
+ basic_auth->expiretime = squid_curtime;
if (cbdataReferenceValidDone(r->data, &cbdata))
r->handler(cbdata, NULL);
storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
}
-AuthBasicConfig::AuthBasicConfig() : authenticateChildren(20)
+AuthBasicConfig::AuthBasicConfig() :
+ credentialsTTL( 2*60*60 ),
+ casesensitive(0),
+ utf8(0)
{
- /* TODO: move into initialisation list */
- credentialsTTL = 2 * 60 * 60; /* two hours */
basicAuthRealm = xstrdup("Squid proxy-caching web server");
}
helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
}
-CBDATA_TYPE(AuthenticateStateData);
-
-static AuthUser *
+static AuthUser::Pointer
authBasicAuthUserFindUsername(const char *username)
{
AuthUserHashPointer *usernamehash;
delete this;
}
-BasicUser::BasicUser(AuthConfig *aConfig) : AuthUser (aConfig) , passwd (NULL), credentials_checkedtime(0), auth_queue(NULL), cleartext (NULL), currentRequest (NULL), httpAuthHeader (NULL)
-{
- flags.credentials_ok = 0;
-}
+BasicUser::BasicUser(AuthConfig *aConfig) :
+ AuthUser(aConfig),
+ passwd(NULL),
+ auth_queue(NULL),
+ cleartext(NULL),
+ currentRequest(NULL),
+ httpAuthHeader(NULL)
+{}
bool
BasicUser::decodeCleartext()
*seperator = ':';
}
- if (!basicConfig.casesensitive)
+ if (!static_cast<AuthBasicConfig*>(config)->casesensitive)
Tolower((char *)username());
}
}
void
-BasicUser::decode(char const *proxy_auth, AuthUserRequest *auth_user_request)
+BasicUser::decode(char const *proxy_auth, AuthUserRequest::Pointer auth_user_request)
{
currentRequest = auth_user_request;
httpAuthHeader = proxy_auth;
- if (decodeCleartext ()) {
+ if (decodeCleartext()) {
extractUsername();
extractPassword();
}
- currentRequest = NULL;
+ currentRequest = NULL; // AYJ: why ?? we have only just filled it with data!
+ // so that we dont have circular UserRequest->User->UseRequest loops persisting outside the auth decode sequence????
+
+ // okay we dont need the original buffer string any more.
httpAuthHeader = NULL;
}
return true;
}
+/**
+ * Generate a duplicate of the bad credentials before clearing the working copy.
+ */
void
-BasicUser::makeLoggingInstance(AuthBasicUserRequest *auth_user_request)
+BasicUser::makeLoggingInstance(AuthUserRequest::Pointer auth_user_request)
{
if (username()) {
/* log the username */
debugs(29, 9, HERE << "Creating new user for logging '" << username() << "'");
/* new scheme data */
- BasicUser *basic_auth = new BasicUser(& basicConfig);
+ AuthUser::Pointer basic_auth = dynamic_cast<AuthUser*>(new BasicUser(config));
auth_user_request->user(basic_auth);
/* save the credentials */
basic_auth->username(username());
username(NULL);
/* set the auth_user type */
basic_auth->auth_type = AUTH_BROKEN;
- /* link the request to the user */
- basic_auth->addRequest(auth_user_request);
}
}
-AuthUser *
-BasicUser::makeCachedFrom()
-{
- /* the user doesn't exist in the username cache yet */
- debugs(29, 9, HERE << "Creating new user '" << username() << "'");
- BasicUser *basic_user = new BasicUser(&basicConfig);
- /* save the credentials */
- basic_user->username(username());
- username(NULL);
- basic_user->passwd = passwd;
- passwd = NULL;
- /* set the auth_user type */
- basic_user->auth_type = AUTH_BASIC;
- /* current time for timeouts */
- basic_user->expiretime = current_time.tv_sec;
-
- /* this basic_user struct is the 'lucky one' to get added to the username cache */
- /* the requests after this link to the basic_user */
- /* store user in hash */
- basic_user->addToNameCache();
- return basic_user;
-}
-
void
BasicUser::updateCached(BasicUser *from)
{
- debugs(29, 9, HERE << "Found user '" << from->username() << "' in the user cache as '" << this << "'");
+ debugs(29, 9, HERE << "Found user '" << from->username() << "' already in the user cache as '" << this << "'");
+
+ assert(strcmp(from->username(), username()) == 0);
if (strcmp(from->passwd, passwd)) {
debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked");
- flags.credentials_ok = 0;
+ credentials(Unchecked);
xfree(passwd);
passwd = from->passwd;
from->passwd = NULL;
}
- if (flags.credentials_ok == 3) {
+ if (credentials() == Failed) {
debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked");
- flags.credentials_ok = 0;
+ credentials(Unchecked);
}
}
* "cannot decode credentials". Use the message field to return a
* descriptive message to the user.
*/
-AuthUserRequest *
+AuthUserRequest::Pointer
AuthBasicConfig::decode(char const *proxy_auth)
{
- AuthBasicUserRequest *auth_user_request = new AuthBasicUserRequest();
+ AuthUserRequest::Pointer auth_user_request = dynamic_cast<AuthUserRequest*>(new AuthBasicUserRequest);
/* decode the username */
/* trim BASIC from string */
while (xisgraph(*proxy_auth))
proxy_auth++;
- BasicUser *basic_auth, local_basic(&basicConfig);
+ /* decoder copy. maybe temporary. maybe added to hash if none already existing. */
+ BasicUser *local_basic = new BasicUser(this);
/* Trim leading whitespace before decoding */
while (xisspace(*proxy_auth))
proxy_auth++;
- local_basic.decode(proxy_auth, auth_user_request);
+ local_basic->decode(proxy_auth, auth_user_request);
- if (!local_basic.valid()) {
- local_basic.makeLoggingInstance(auth_user_request);
+ if (!local_basic->valid()) {
+ local_basic->makeLoggingInstance(auth_user_request);
return auth_user_request;
}
- /* now lookup and see if we have a matching auth_user structure in
- * memory. */
+ /* now lookup and see if we have a matching auth_user structure in memory. */
+ AuthUser::Pointer auth_user;
+
+ if ((auth_user = authBasicAuthUserFindUsername(local_basic->username())) == NULL) {
+ /* the user doesn't exist in the username cache yet */
+ /* save the credentials */
+ debugs(29, 9, HERE << "Creating new user '" << local_basic->username() << "'");
+ /* set the auth_user type */
+ local_basic->auth_type = AUTH_BASIC;
+ /* current time for timeouts */
+ local_basic->expiretime = current_time.tv_sec;
- AuthUser *auth_user;
+ /* this basic_user struct is the 'lucky one' to get added to the username cache */
+ /* the requests after this link to the basic_user */
+ /* store user in hash */
+ local_basic->addToNameCache();
- if ((auth_user = authBasicAuthUserFindUsername(local_basic.username())) == NULL) {
- auth_user = local_basic.makeCachedFrom();
- basic_auth = dynamic_cast<BasicUser *>(auth_user);
- assert (basic_auth);
+ auth_user = dynamic_cast<AuthUser*>(local_basic);
+ assert(auth_user != NULL);
} else {
- basic_auth = dynamic_cast<BasicUser *>(auth_user);
- assert (basic_auth);
- basic_auth->updateCached (&local_basic);
+ /* replace the current cached password with the new one */
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(auth_user.getRaw());
+ assert(basic_auth);
+ basic_auth->updateCached(local_basic);
+ auth_user = basic_auth;
}
/* link the request to the in-cache user */
- auth_user_request->user(basic_auth);
-
- basic_auth->addRequest(auth_user_request);
-
+ auth_user_request->user(auth_user);
return auth_user_request;
}
/** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
* config file */
void
-AuthBasicConfig::init(AuthConfig * scheme)
+AuthBasicConfig::init(AuthConfig * schemeCfg)
{
if (authenticate) {
authbasic_initialised = 1;
helperOpenServers(basicauthenticators);
- CBDATA_INIT_TYPE(AuthenticateStateData);
+ CBDATA_INIT_TYPE(authenticateStateData);
}
}
}
void
-BasicUser::queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
+BasicUser::queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
{
BasicAuthQueueNode *node;
- node = static_cast<BasicAuthQueueNode *>(xmalloc(sizeof(BasicAuthQueueNode)));
+ node = static_cast<BasicAuthQueueNode *>(xcalloc(1, sizeof(BasicAuthQueueNode)));
assert(node);
/* save the details */
node->next = auth_queue;
node->data = cbdataReference(data);
}
-/* send the initial data to a basic authenticator module */
void
-AuthBasicUserRequest::module_start(RH * handler, void *data)
+BasicUser::submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
{
- basic_data *basic_auth;
- assert(user()->auth_type == AUTH_BASIC);
- basic_auth = dynamic_cast<basic_data *>(user());
- assert(basic_auth != NULL);
- debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
-
- if (basicConfig.authenticate == NULL) {
- handler(data, NULL);
- return;
- }
-
- /* check to see if the auth_user already has a request outstanding */
- if (basic_auth->flags.credentials_ok == 2) {
- /* there is a request with the same credentials already being verified */
- basic_auth->queueRequest(this, handler, data);
- return;
- }
-
- basic_auth->submitRequest (this, handler, data);
-}
-
-void
-BasicUser::submitRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
-{
- /* mark the user as haveing verification in progress */
- flags.credentials_ok = 2;
- AuthenticateStateData *r = NULL;
+ /* mark the user as having verification in progress */
+ credentials(Pending);
+ authenticateStateData *r = NULL;
char buf[8192];
char user[1024], pass[1024];
- r = cbdataAlloc(AuthenticateStateData);
+ r = cbdataAlloc(authenticateStateData);
r->handler = handler;
r->data = cbdataReference(data);
r->auth_user_request = auth_user_request;
- if (basicConfig.utf8) {
+ if (static_cast<AuthBasicConfig*>(config)->utf8) {
latin1_to_utf8(user, sizeof(user), username());
latin1_to_utf8(pass, sizeof(pass), passwd);
xstrncpy(user, rfc1738_escape(user), sizeof(user));
snprintf(buf, sizeof(buf), "%s %s\n", user, pass);
helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
}
-
-AuthConfig *
-basicScheme::createConfig()
-{
- return &basicConfig;
-}
#ifndef __AUTH_BASIC_H__
#define __AUTH_BASIC_H__
+
#include "auth/Gadgets.h"
#include "auth/User.h"
#include "auth/UserRequest.h"
#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
-/* Generic */
-
-class AuthenticateStateData
-{
-
-public:
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-};
-
-/* queue of auth requests waiting for verification to occur */
-
+/** queue of auth requests waiting for verification to occur */
class BasicAuthQueueNode
{
public:
BasicAuthQueueNode *next;
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
RH *handler;
void *data;
};
-class AuthBasicUserRequest;
-
class BasicUser : public AuthUser
{
BasicUser(AuthConfig *);
~BasicUser();
bool authenticated() const;
- void queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data);
- void submitRequest (AuthUserRequest * auth_user_request, RH * handler, void *data);
- void decode(char const *credentials, AuthUserRequest *);
+ void queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data);
+ void submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data);
+ void decode(char const *credentials, AuthUserRequest::Pointer);
char *getCleartext() {return cleartext;}
bool valid() const;
- void makeLoggingInstance(AuthBasicUserRequest *auth_user_request);
- AuthUser * makeCachedFrom();
+ void makeLoggingInstance(AuthUserRequest::Pointer auth_user_request);
+
+ /** Update the cached password for a username. */
void updateCached(BasicUser *from);
- char *passwd;
- time_t credentials_checkedtime;
+ virtual int32_t ttl() const;
- struct {
+ char *passwd;
-unsigned int credentials_ok:
- 2; /*0=unchecked,1=ok,2=failed */
- } flags;
BasicAuthQueueNode *auth_queue;
private:
void extractUsername();
void extractPassword();
char *cleartext;
- AuthUserRequest *currentRequest;
+ AuthUserRequest::Pointer currentRequest;
char const *httpAuthHeader;
};
MEMPROXY_CLASS_INLINE(BasicUser);
-typedef class BasicUser basic_data;
-
-/* follows the http request around */
-
-class AuthBasicUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthBasicUserRequest);
-
- AuthBasicUserRequest();
- virtual ~AuthBasicUserRequest();
-
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
- virtual int module_direction();
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<BasicUser *>(aUser);}
-
-private:
- BasicUser *_theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
-
-#include "HelperChildConfig.h"
-
/* configuration runtime data */
class AuthBasicConfig : public AuthConfig
~AuthBasicConfig();
virtual bool active() const;
virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
virtual void done();
+ virtual void rotateHelpers();
virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
virtual void init(AuthConfig *);
virtual void parse(AuthConfig *, int, char *);
virtual void registerWithCacheManager(void);
virtual const char * type() const;
- HelperChildConfig authenticateChildren;
char *basicAuthRealm;
- wordlist *authenticate;
time_t credentialsTTL;
int casesensitive;
int utf8;
};
-#endif
+#endif /* __AUTH_BASIC_H__ */
-
/*
* $Id$
*
*
*/
-#include "basicScheme.h"
+#include "config.h"
+#include "auth/basic/basicScheme.h"
+#include "helper.h"
+
+/* for AuthConfig */
+#include "auth/basic/auth_basic.h"
+
+AuthScheme::Pointer basicScheme::_instance = NULL;
-AuthScheme &
+AuthScheme::Pointer
basicScheme::GetInstance()
{
- if (_instance == NULL)
+ if (_instance == NULL) {
_instance = new basicScheme();
- return *_instance;
-}
-
-basicScheme::basicScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
}
char const *
return "basic";
}
-basicScheme *basicScheme::_instance = NULL;
+void
+basicScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, DBG_CRITICAL, HERE << "Basic authentication Schema Detached.");
+}
+
+AuthConfig *
+basicScheme::createConfig()
+{
+ AuthBasicConfig *newCfg = new AuthBasicConfig;
+ return dynamic_cast<AuthConfig*>(newCfg);
+}
-
/*
* $Id$
*
#define SQUID_BASICSCHEME_H
#include "auth/Scheme.h"
+#include "auth/basic/auth_basic.h"
/// \ingroup AuthAPI
/// \ingroup AuthSchemeAPI
{
public:
- static AuthScheme &GetInstance();
- basicScheme();
+ static AuthScheme::Pointer GetInstance();
+ basicScheme() {};
virtual ~basicScheme() {}
/* per scheme */
basicScheme &operator=(basicScheme const &);
private:
- static basicScheme *_instance;
+ static AuthScheme::Pointer _instance;
+// AuthBasicConfig basicConfig;
};
#endif /* SQUID_BASICSCHEME_H */
--- /dev/null
+#include "config.h"
+#include "auth/basic/basicUserRequest.h"
+#include "SquidTime.h"
+
+#include "auth/basic/auth_basic.h"
+
+int
+AuthBasicUserRequest::authenticated() const
+{
+ BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user().getRaw());
+
+ if (basic_auth && basic_auth->authenticated())
+ return 1;
+
+ return 0;
+}
+
+/* log a basic user in
+ */
+void
+AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+{
+ assert(user() != NULL);
+
+ /* if the password is not ok, do an identity */
+ if (!user() || user()->credentials() != AuthUser::Ok)
+ return;
+
+ /* are we about to recheck the credentials externally? */
+ if ((user()->expiretime + static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->credentialsTTL) <= squid_curtime) {
+ debugs(29, 4, HERE << "credentials expired - rechecking");
+ return;
+ }
+
+ /* we have been through the external helper, and the credentials haven't expired */
+ debugs(29, 9, HERE << "user '" << user()->username() << "' authenticated");
+
+ /* Decode now takes care of finding the AuthUser struct in the cache */
+ /* after external auth occurs anyway */
+ user()->expiretime = current_time.tv_sec;
+
+ return;
+}
+
+int
+AuthBasicUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+ if (user()->auth_type != AUTH_BASIC)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ case AuthUser::Pending:
+ return -1;
+
+ case AuthUser::Ok:
+ if (user()->expiretime + static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->credentialsTTL <= squid_curtime)
+ return -1;
+ return 0;
+
+ case AuthUser::Failed:
+ return 0;
+
+ default:
+ return -2;
+ }
+}
+
+/* send the initial data to a basic authenticator module */
+void
+AuthBasicUserRequest::module_start(RH * handler, void *data)
+{
+ assert(user()->auth_type == AUTH_BASIC);
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(user().getRaw());
+ assert(basic_auth != NULL);
+ debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
+
+ if (static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Basic authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ /* check to see if the auth_user already has a request outstanding */
+ if (user()->credentials() == AuthUser::Pending) {
+ /* there is a request with the same credentials already being verified */
+ basic_auth->queueRequest(this, handler, data);
+ return;
+ }
+
+ basic_auth->submitRequest(this, handler, data);
+}
+
--- /dev/null
+#ifndef _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+#define _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+
+#include "MemPool.h"
+#include "auth/UserRequest.h"
+
+class ConnStateData;
+class HttpRequest;
+
+/* follows the http request around */
+
+class AuthBasicUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthBasicUserRequest);
+
+ AuthBasicUserRequest() {};
+ virtual ~AuthBasicUserRequest() { assert(RefCountCount()==0); };
+
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void module_start(RH *, void *);
+};
+
+MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_BASIC_USERREQUEST_H */
#include "squid.h"
#include "rfc2617.h"
-#include "auth_digest.h"
+#include "auth/digest/auth_digest.h"
#include "auth/Gadgets.h"
#include "event.h"
#include "CacheManager.h"
#include "wordlist.h"
#include "SquidTime.h"
/* TODO don't include this */
-#include "digestScheme.h"
+#include "auth/digest/digestScheme.h"
+#include "auth/digest/digestUserRequest.h"
/* Digest Scheme */
-static HLPCB authenticateDigestHandleReply;
static AUTHSSTATS authenticateDigestStats;
-static helper *digestauthenticators = NULL;
+helper *digestauthenticators = NULL;
static hash_table *digest_nonce_cache;
-static AuthDigestConfig digestConfig;
-
static int authdigest_initialised = 0;
static MemAllocator *digest_nonce_pool = NULL;
-CBDATA_TYPE(DigestAuthenticateStateData);
+// CBDATA_TYPE(DigestAuthenticateStateData);
enum http_digest_attr_type {
DIGEST_USERNAME,
static void authenticateDigestNonceSetup(void);
static void authenticateDigestNonceShutdown(void);
static void authenticateDigestNonceReconfigure(void);
-static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce);
-static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
static int authDigestNonceIsStale(digest_nonce_h * nonce);
static void authDigestNonceEncode(digest_nonce_h * nonce);
-static int authDigestNonceLastRequest(digest_nonce_h * nonce);
static void authDigestNonceLink(digest_nonce_h * nonce);
-static void authDigestNonceUnlink(digest_nonce_h * nonce);
#if NOT_USED
static int authDigestNonceLinks(digest_nonce_h * nonce);
#endif
if (!digest_nonce_cache) {
digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
assert(digest_nonce_cache);
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig.nonceGCInterval, 1);
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
}
}
debugs(29, 3, "authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.");
- if (digestConfig.active())
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig.nonceGCInterval, 1);
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->active())
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
}
static void
#endif
-static void
+void
authDigestNonceUnlink(digest_nonce_h * nonce)
{
assert(nonce != NULL);
authenticateDigestNonceDelete(nonce);
}
-static const char *
-authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
+const char *
+authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
{
if (!nonce)
return NULL;
return nonce;
}
-static int
+int
authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
{
unsigned long intnc;
}
/* is the nonce-count ok ? */
- if (!digestConfig.CheckNonceCount) {
+ if (!static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->CheckNonceCount) {
nonce->nc++;
return -1; /* forced OK by configuration */
}
- if ((digestConfig.NonceStrictness && intnc != nonce->nc + 1) ||
+ if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
intnc < nonce->nc + 1) {
debugs(29, 4, "authDigestNonceIsValid: Nonce count doesn't match");
nonce->flags.valid = 0;
return -1;
/* has it's max duration expired? */
- if (nonce->noncedata.creationtime + digestConfig.noncemaxduration < current_time.tv_sec) {
+ if (nonce->noncedata.creationtime + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration < current_time.tv_sec) {
debugs(29, 4, "authDigestNonceIsStale: Nonce is too old. " <<
nonce->noncedata.creationtime << " " <<
- digestConfig.noncemaxduration << " " <<
+ static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration << " " <<
current_time.tv_sec);
nonce->flags.valid = 0;
return -1;
}
- if (nonce->nc > digestConfig.noncemaxuses) {
+ if (nonce->nc > static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses) {
debugs(29, 4, "authDigestNoncelastRequest: Nonce count over user limit");
nonce->flags.valid = 0;
return -1;
return 0;
}
-/* return -1 if the digest will be stale on the next request */
-static int
+/**
+ * \retval 0 the digest is not stale yet
+ * \retval -1 the digest will be stale on the next request
+ */
+const int
authDigestNonceLastRequest(digest_nonce_h * nonce)
{
if (!nonce)
return -1;
}
- if (nonce->nc >= digestConfig.noncemaxuses - 1) {
+ if (nonce->nc >= static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses - 1) {
debugs(29, 4, "authDigestNoncelastRequest: Nonce count about to hit user limit");
return -1;
}
}
/* USER related functions */
-static AuthUser *
+static AuthUser::Pointer
authDigestUserFindUsername(const char *username)
{
AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
debugs(29, 9, HERE << "Looking for user '" << username << "'");
if (username && (usernamehash = static_cast < auth_user_hash_pointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
- while ((usernamehash->user()->auth_type != AUTH_DIGEST) &&
- (usernamehash->next))
- usernamehash = static_cast < auth_user_hash_pointer * >(usernamehash->next);
-
- auth_user = NULL;
+ while ((usernamehash->user()->auth_type != AUTH_DIGEST) && (usernamehash->next))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
if (usernamehash->user()->auth_type == AUTH_DIGEST) {
- auth_user = usernamehash->user();
+ return usernamehash->user();
}
-
- return auth_user;
}
return NULL;
}
-static void
-authDigestUserShutdown(void)
+void
+AuthDigestConfig::rotateHelpers()
{
- /** \todo Future work: the auth framework could flush it's cache */
- AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- hash_first(proxy_auth_username_cache);
-
- while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
- auth_user = usernamehash->user();
-
- if (strcmp(auth_user->config->type(), "digest") == 0)
- auth_user->unlock();
+ /* schedule closure of existing helpers */
+ if (digestauthenticators) {
+ helperShutdown(digestauthenticators);
}
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
}
+
/** delete the digest request structure. Does NOT delete related structures */
void
digestScheme::done()
delete digestauthenticators;
digestauthenticators = NULL;
- authDigestUserShutdown();
+ PurgeCredentialsCache();
authenticateDigestNonceShutdown();
debugs(29, 2, "authenticateDigestDone: Digest authentication shut down.");
+
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
}
void
return false;
}
-int
-AuthDigestUserRequest::authenticated() const
-{
- if (credentials() == Ok)
- return 1;
-
- return 0;
-}
-
-/** log a digest user in
- */
-void
-AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
-{
- AuthUser *auth_user;
- AuthDigestUserRequest *digest_request;
- digest_user_h *digest_user;
-
- HASHHEX SESSIONKEY;
- HASHHEX HA2 = "";
- HASHHEX Response;
-
- assert(authUser() != NULL);
- auth_user = authUser();
-
- digest_user = dynamic_cast < digest_user_h * >(auth_user);
-
- assert(digest_user != NULL);
-
- /* if the check has corrupted the user, just return */
-
- if (credentials() == Failed) {
- return;
- }
-
- digest_request = this;
-
- /* do we have the HA1 */
-
- if (!digest_user->HA1created) {
- credentials(Pending);
- return;
- }
-
- if (digest_request->nonce == NULL) {
- /* this isn't a nonce we issued */
- credentials(Failed);
- return;
- }
-
- DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
- authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->cnonce,
- digest_user->HA1, SESSIONKEY);
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(request->method), digest_request->uri, HA2, Response);
-
- debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
-
- if (strcasecmp(digest_request->response, Response) != 0) {
- if (!digest_request->flags.helper_queried) {
- /* Query the helper in case the password has changed */
- digest_request->flags.helper_queried = 1;
- digest_request->credentials_ok = Pending;
- return;
- }
-
- if (digestConfig.PostWorkaround && request->method != METHOD_GET) {
- /* Ugly workaround for certain very broken browsers using the
- * wrong method to calculate the request-digest on POST request.
- * This should be deleted once Digest authentication becomes more
- * widespread and such broken browsers no longer are commonly
- * used.
- */
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
-
- if (strcasecmp(digest_request->response, Response)) {
- credentials(Failed);
- digest_request->flags.invalid_password = 1;
- digest_request->setDenyMessage("Incorrect password");
- return;
- } else {
- const char *useragent = request->header.getStr(HDR_USER_AGENT);
-
- static Ip::Address last_broken_addr;
- static int seen_broken_client = 0;
-
- if (!seen_broken_client) {
- last_broken_addr.SetNoAddr();
- seen_broken_client = 1;
- }
-
- if (last_broken_addr != request->client_addr) {
- debugs(29, 1, "\nDigest POST bug detected from " <<
- request->client_addr << " using '" <<
- (useragent ? useragent : "-") <<
- "'. Please upgrade browser. See Bug #630 for details.");
-
- last_broken_addr = request->client_addr;
- }
- }
- } else {
- credentials(Failed);
- digest_request->flags.invalid_password = 1;
- digest_request->setDenyMessage("Incorrect password");
- return;
- }
-
- /* check for stale nonce */
- if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
- debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale");
- credentials(Failed);
- digest_request->setDenyMessage("Stale nonce");
- return;
- }
- }
-
- credentials(Ok);
-
- /* password was checked and did match */
- debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK");
-
- /* auth_user is now linked, we reset these values
- * after external auth occurs anyway */
- auth_user->expiretime = current_time.tv_sec;
- return;
-}
-
-int
-AuthDigestUserRequest::module_direction()
-{
- switch (credentials()) {
-
- case Unchecked:
- return -1;
-
- case Ok:
-
- return 0;
-
- case Pending:
- return -1;
-
- case Failed:
-
- /* send new challenge */
- return 1;
- }
-
- return -2;
-}
-
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addHeader(HttpReply * rep, int accel)
-{
- http_hdr_type type;
-
- /* don't add to authentication error pages */
-
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
-#if WAITING_FOR_TE
- /* test for http/1.1 transfer chunked encoding */
- if (chunkedtest)
- return;
-
-#endif
-
- if ((digestConfig.authenticate) && authDigestNonceLastRequest(nonce)) {
- flags.authinfo_sent = 1;
- debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#if WAITING_FOR_TE
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel)
-{
- int type;
-
- if (!auth_user_request)
- return;
-
-
- /* has the header already been send? */
- if (flags.authinfo_sent)
- return;
-
- /* don't add to authentication error pages */
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
- if ((digestConfig.authenticate) && authDigestNonceLastRequest(nonce)) {
- debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#endif
-
/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
void
-AuthDigestConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+AuthDigestConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
{
if (!authenticate)
return;
int stale = 0;
- if (auth_user_request) {
+ if (auth_user_request != NULL) {
AuthDigestUserRequest *digest_request;
- digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request);
+ digest_request = dynamic_cast<AuthDigestUserRequest*>(auth_user_request.getRaw());
assert (digest_request != NULL);
stale = !digest_request->flags.invalid_password;
DigestUser::~DigestUser()
{
-
dlink_node *link, *tmplink;
link = nonces.head;
}
}
-static void
-authenticateDigestHandleReply(void *data, char *reply)
+int32_t
+DigestUser::ttl() const
{
- DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
- AuthUserRequest *auth_user_request;
- AuthDigestUserRequest *digest_request;
- digest_user_h *digest_user;
- char *t = NULL;
- void *cbdata;
- debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "<NULL>") << "}");
-
- if (reply) {
- if ((t = strchr(reply, ' ')))
- *t++ = '\0';
-
- if (*reply == '\0' || *reply == '\n')
- reply = NULL;
- }
+ int32_t global_ttl = static_cast<int32_t>(expiretime - squid_curtime + Config.authenticateTTL);
- assert(replyData->auth_user_request != NULL);
- auth_user_request = replyData->auth_user_request;
- digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request);
- assert(digest_request);
-
- digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user());
- assert(digest_user != NULL);
-
- if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
- digest_request->credentials(AuthDigestUserRequest::Failed);
- digest_request->flags.invalid_password = 1;
+ /* find the longest lasting nonce. */
+ int32_t latest_nonce = -1;
+ dlink_node *link = nonces.head;
+ while (link) {
+ digest_nonce_h *nonce = static_cast<digest_nonce_h *>(link->data);
+ if (nonce->flags.valid && nonce->noncedata.creationtime > latest_nonce)
+ latest_nonce = nonce->noncedata.creationtime;
- if (t && *t)
- digest_request->setDenyMessage(t);
- } else if (reply) {
- CvtBin(reply, digest_user->HA1);
- digest_user->HA1created = 1;
+ link = link->next;
}
+ if (latest_nonce == -1)
+ return min(-1, global_ttl);
- if (cbdataReferenceValidDone(replyData->data, &cbdata))
- replyData->handler(cbdata, NULL);
+ int32_t nonce_ttl = latest_nonce - current_time.tv_sec + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration;
- //we know replyData->auth_user_request != NULL, or we'd have asserted
- AUTHUSERREQUESTUNLOCK(replyData->auth_user_request, "replyData");
-
- cbdataFree(replyData);
+ return min(nonce_ttl, global_ttl);
}
+
/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
* config file */
void
helperOpenServers(digestauthenticators);
- CBDATA_INIT_TYPE(DigestAuthenticateStateData);
+ CBDATA_INIT_TYPE(authenticateStateData);
}
}
safe_free(digestAuthRealm);
}
-AuthDigestConfig::AuthDigestConfig() : authenticateChildren(20)
+AuthDigestConfig::AuthDigestConfig()
{
/* TODO: move into initialisation list */
/* 5 minutes */
const char *
AuthDigestConfig::type() const
{
- return digestScheme::GetInstance().type();
+ return digestScheme::GetInstance()->type();
}
static void
authDigestNonceUserUnlink(digest_nonce_h * nonce)
{
- digest_user_h *digest_user;
+ DigestUser *digest_user;
dlink_node *link, *tmplink;
if (!nonce)
authDigestUserLinkNonce(DigestUser * user, digest_nonce_h * nonce)
{
dlink_node *node;
- digest_user_h *digest_user;
+ DigestUser *digest_user;
if (!user || !nonce)
return;
}
/* setup the necessary info to log the username */
-static AuthUserRequest *
-authDigestLogUsername(char *username, AuthDigestUserRequest *auth_user_request)
+static AuthUserRequest::Pointer
+authDigestLogUsername(char *username, AuthUserRequest::Pointer auth_user_request)
{
assert(auth_user_request != NULL);
/* log the username */
debugs(29, 9, "authDigestLogUsername: Creating new user for logging '" << username << "'");
- digest_user_h *digest_user = new DigestUser(&digestConfig);
+ AuthUser::Pointer digest_user = new DigestUser(static_cast<AuthDigestConfig*>(AuthConfig::Find("digest")));
/* save the credentials */
digest_user->username(username);
/* set the auth_user type */
digest_user->auth_type = AUTH_BROKEN;
/* link the request to the user */
- auth_user_request->authUser(digest_user);
auth_user_request->user(digest_user);
- digest_user->addRequest (auth_user_request);
return auth_user_request;
}
* Decode a Digest [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
-AuthUserRequest *
+AuthUserRequest::Pointer
AuthDigestConfig::decode(char const *proxy_auth)
{
const char *item;
if (!nonce) {
/* we couldn't find a matching nonce! */
debugs(29, 2, "authenticateDigestDecode: Unexpected or invalid nonce received");
- digest_request->credentials(AuthDigestUserRequest::Failed);
+ digest_request->user()->credentials(AuthUser::Failed);
return authDigestLogUsername(username, digest_request);
}
/* we don't send or parse opaques. Ok so we're flexable ... */
/* find the user */
- digest_user_h *digest_user;
+ DigestUser *digest_user;
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
/* the user doesn't exist in the username cache yet */
debugs(29, 9, "authDigestDecodeAuth: Creating new digest user '" << username << "'");
- digest_user = new DigestUser (&digestConfig);
+ digest_user = new DigestUser(this);
/* auth_user is a parent */
auth_user = digest_user;
/* save the username */
authDigestUserLinkNonce(digest_user, nonce);
} else {
debugs(29, 9, "authDigestDecodeAuth: Found user '" << username << "' in the user cache as '" << auth_user << "'");
- digest_user = static_cast < digest_user_h * >(auth_user);
+ digest_user = static_cast<DigestUser *>(auth_user.getRaw());
xfree(username);
}
/*link the request and the user */
assert(digest_request != NULL);
- digest_request->authUser (digest_user);
-
digest_request->user(digest_user);
-
- digest_user->addRequest (digest_request);
-
debugs(29, 9, "username = '" << digest_user->username() << "'\nrealm = '" <<
digest_request->realm << "'\nqop = '" << digest_request->qop <<
"'\nalgorithm = '" << digest_request->algorithm << "'\nuri = '" <<
return digest_request;
}
-/* send the initial data to a digest authenticator module */
-void
-AuthDigestUserRequest::module_start(RH * handler, void *data)
-{
- DigestAuthenticateStateData *r = NULL;
- char buf[8192];
- digest_user_h *digest_user;
- assert(user()->auth_type == AUTH_DIGEST);
- digest_user = dynamic_cast < digest_user_h * >(user());
- assert(digest_user != NULL);
- debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'");
-
- if (digestConfig.authenticate == NULL) {
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(DigestAuthenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
- if (digestConfig.utf8) {
- char userstr[1024];
- latin1_to_utf8(userstr, sizeof(userstr), digest_user->username());
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
- } else {
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm);
- }
-
- helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r);
-}
-
-DigestUser::DigestUser (AuthConfig *aConfig) : AuthUser (aConfig), HA1created (0)
+DigestUser::DigestUser(AuthConfig *aConfig) : AuthUser(aConfig), HA1created (0)
{}
-
-AuthUser *
-AuthDigestUserRequest::authUser() const
-{
- return const_cast<AuthUser *>(user());
-}
-
-void
-AuthDigestUserRequest::authUser(AuthUser *aUser)
-{
- assert(!authUser());
- user(aUser);
- user()->lock();
-}
-
-AuthDigestUserRequest::CredentialsState
-AuthDigestUserRequest::credentials() const
-{
- return credentials_ok;
-}
-
-void
-AuthDigestUserRequest::credentials(CredentialsState newCreds)
-{
- credentials_ok = newCreds;
-}
-
-AuthDigestUserRequest::AuthDigestUserRequest() : nonceb64(NULL) ,cnonce(NULL) ,realm(NULL),
- pszPass(NULL) ,algorithm(NULL) ,pszMethod(NULL),
- qop(NULL) ,uri(NULL) ,response(NULL),
- nonce(NULL), _theUser (NULL) ,
- credentials_ok (Unchecked)
-{}
-
-/** delete the digest request structure. Does NOT delete related structures */
-AuthDigestUserRequest::~AuthDigestUserRequest()
-{
- safe_free (nonceb64);
- safe_free (cnonce);
- safe_free (realm);
- safe_free (pszPass);
- safe_free (algorithm);
- safe_free (pszMethod);
- safe_free (qop);
- safe_free (uri);
- safe_free (response);
-
- if (nonce)
- authDigestNonceUnlink(nonce);
-}
-
-AuthConfig *
-digestScheme::createConfig()
-{
- return &digestConfig;
-}
-
#ifndef __AUTH_DIGEST_H__
#define __AUTH_DIGEST_H__
-#include "rfc2617.h"
+
+#include "auth/Config.h"
#include "auth/Gadgets.h"
+#include "auth/State.h"
#include "auth/User.h"
#include "auth/UserRequest.h"
-#include "auth/Config.h"
#include "helper.h"
+#include "rfc2617.h"
/* Generic */
-class DigestAuthenticateStateData
-{
-
-public:
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-};
-
typedef struct _digest_nonce_data digest_nonce_data;
typedef struct _digest_nonce_h digest_nonce_h;
DigestUser(AuthConfig *);
~DigestUser();
int authenticated() const;
+
+ virtual int32_t ttl() const;
+
HASH HA1;
int HA1created;
MEMPROXY_CLASS_INLINE(DigestUser);
-typedef class DigestUser digest_user_h;
-
-/* the digest_request structure is what follows the http_request around */
-
-class AuthDigestUserRequest : public AuthUserRequest
-{
-
-public:
- enum CredentialsState {Unchecked, Ok, Pending, Failed};
- MEMPROXY_CLASS(AuthDigestUserRequest);
-
- AuthDigestUserRequest();
- virtual ~AuthDigestUserRequest();
-
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void addHeader(HttpReply * rep, int accel);
-#if WAITING_FOR_TE
-
- virtual void addTrailer(HttpReply * rep, int accel);
-#endif
-
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user(AuthUser *aUser) {_theUser=dynamic_cast<DigestUser *>(aUser);}
-
- CredentialsState credentials() const;
- void credentials(CredentialsState);
-
- void authUser(AuthUser *);
- AuthUser *authUser() const;
-
- char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
- char *cnonce; /* "0a4f113b" */
- char *realm; /* = "testrealm@host.com" */
- char *pszPass; /* = "Circle Of Life" */
- char *algorithm; /* = "md5" */
- char nc[9]; /* = "00000001" */
- char *pszMethod; /* = "GET" */
- char *qop; /* = "auth" */
- char *uri; /* = "/dir/index.html" */
- char *response;
-
- struct {
- unsigned int authinfo_sent:1;
- unsigned int invalid_password:1;
- unsigned int helper_queried:1;
- } flags;
- digest_nonce_h *nonce;
-
-private:
- DigestUser *_theUser;
- CredentialsState credentials_ok;
-};
-
-MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
/* data to be encoded into the nonce's b64 representation */
} flags;
};
-#include "HelperChildConfig.h"
+extern void authDigestNonceUnlink(digest_nonce_h * nonce);
+extern int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
+extern const char *authenticateDigestNonceNonceb64(const digest_nonce_h * nonce);
+extern const int authDigestNonceLastRequest(digest_nonce_h * nonce);
/* configuration runtime data */
AuthDigestConfig();
virtual bool active() const;
virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
virtual void done();
+ virtual void rotateHelpers();
virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
virtual void init(AuthConfig *);
virtual void parse(AuthConfig *, int, char *);
virtual void registerWithCacheManager(void);
virtual const char * type() const;
- HelperChildConfig authenticateChildren;
char *digestAuthRealm;
- wordlist *authenticate;
time_t nonceGCInterval;
time_t noncemaxduration;
unsigned int noncemaxuses;
/* strings */
#define QOP_AUTH "auth"
+extern helper *digestauthenticators;
+
#endif
-
/*
* $Id$
*
*
*/
-#include "digestScheme.h"
+#include "config.h"
+#include "auth/digest/digestScheme.h"
+#include "helper.h"
-AuthScheme &
+AuthScheme::Pointer
digestScheme::GetInstance()
{
- if (_instance == NULL)
+ if (_instance == NULL) {
_instance = new digestScheme();
- return *_instance;
-}
-
-digestScheme::digestScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
}
char const *
return "digest";
}
-digestScheme *digestScheme::_instance = NULL;
+AuthScheme::Pointer digestScheme::_instance = NULL;
+
+AuthConfig *
+digestScheme::createConfig()
+{
+ AuthDigestConfig *digestCfg = new AuthDigestConfig;
+ return dynamic_cast<AuthConfig*>(digestCfg);
+}
+
+void
+digestScheme::PurgeCredentialsCache(void)
+{
+ AuthUserHashPointer *usernamehash;
+ AuthUser::Pointer auth_user;
+ hash_first(proxy_auth_username_cache);
+
+ while ((usernamehash = static_cast<AuthUserHashPointer *>(hash_next(proxy_auth_username_cache)) )) {
+ auth_user = usernamehash->user();
+
+ if (strcmp(auth_user->config->type(), "digest") == 0) {
+ hash_remove_link(proxy_auth_username_cache, static_cast<hash_link*>(usernamehash));
+ delete usernamehash;
+ }
+ }
+}
-
/*
* $Id$
*
#define SQUID_DIGESTSCHEME_H
#include "auth/Scheme.h"
+#include "auth/digest/auth_digest.h"
/// \ingroup AuthSchemeAPI
/// \ingroup AuthAPI
{
public:
- static AuthScheme &GetInstance();
- digestScheme();
+ static AuthScheme::Pointer GetInstance();
+ digestScheme() {};
virtual ~digestScheme() {}
/* per scheme */
virtual char const *type () const;
virtual void done();
virtual AuthConfig *createConfig();
+
/* Not implemented */
digestScheme (digestScheme const &);
digestScheme &operator=(digestScheme const &);
private:
- static digestScheme *_instance;
+ static AuthScheme::Pointer _instance;
+
+ /**
+ * Remove all cached user credentials from circulation.
+ * Intended for use during shutdown procedure.
+ * After calling this all newly received credentials must be re-authenticated.
+ */
+ static void PurgeCredentialsCache(void);
};
#endif /* SQUID_DIGESTSCHEME_H */
--- /dev/null
+#include "config.h"
+#include "auth/digest/auth_digest.h"
+#include "auth/digest/digestUserRequest.h"
+#include "auth/State.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+AuthDigestUserRequest::AuthDigestUserRequest() :
+ nonceb64(NULL),
+ cnonce(NULL),
+ realm(NULL),
+ pszPass(NULL),
+ algorithm(NULL),
+ pszMethod(NULL),
+ qop(NULL),
+ uri(NULL),
+ response(NULL),
+ nonce(NULL)
+{}
+
+/**
+ * Delete the digest request structure.
+ * Does NOT delete related AuthUser structures
+ */
+AuthDigestUserRequest::~AuthDigestUserRequest()
+{
+ assert(RefCountCount()==0);
+
+ safe_free(nonceb64);
+ safe_free(cnonce);
+ safe_free(realm);
+ safe_free(pszPass);
+ safe_free(algorithm);
+ safe_free(pszMethod);
+ safe_free(qop);
+ safe_free(uri);
+ safe_free(response);
+
+ if (nonce)
+ authDigestNonceUnlink(nonce);
+}
+
+int
+AuthDigestUserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == AuthUser::Ok)
+ return 1;
+
+ return 0;
+}
+
+/** log a digest user in
+ */
+void
+AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+{
+ HASHHEX SESSIONKEY;
+ HASHHEX HA2 = "";
+ HASHHEX Response;
+
+ /* if the check has corrupted the user, just return */
+ if (user() == NULL || user()->credentials() == AuthUser::Failed) {
+ return;
+ }
+
+ AuthUser::Pointer auth_user = user();
+
+ DigestUser *digest_user = dynamic_cast<DigestUser*>(auth_user.getRaw());
+ assert(digest_user != NULL);
+
+ AuthDigestUserRequest *digest_request = this;
+
+ /* do we have the HA1 */
+ if (!digest_user->HA1created) {
+ auth_user->credentials(AuthUser::Pending);
+ return;
+ }
+
+ if (digest_request->nonce == NULL) {
+ /* this isn't a nonce we issued */
+ auth_user->credentials(AuthUser::Failed);
+ return;
+ }
+
+ DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
+ authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->cnonce,
+ digest_user->HA1, SESSIONKEY);
+ DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->nc, digest_request->cnonce, digest_request->qop,
+ RequestMethodStr(request->method), digest_request->uri, HA2, Response);
+
+ debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
+
+ if (strcasecmp(digest_request->response, Response) != 0) {
+ if (!digest_request->flags.helper_queried) {
+ /* Query the helper in case the password has changed */
+ digest_request->flags.helper_queried = 1;
+ auth_user->credentials(AuthUser::Pending);
+ return;
+ }
+
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) {
+ /* Ugly workaround for certain very broken browsers using the
+ * wrong method to calculate the request-digest on POST request.
+ * This should be deleted once Digest authentication becomes more
+ * widespread and such broken browsers no longer are commonly
+ * used.
+ */
+ DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->nc, digest_request->cnonce, digest_request->qop,
+ RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
+
+ if (strcasecmp(digest_request->response, Response)) {
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+ digest_request->setDenyMessage("Incorrect password");
+ return;
+ } else {
+ const char *useragent = request->header.getStr(HDR_USER_AGENT);
+
+ static Ip::Address last_broken_addr;
+ static int seen_broken_client = 0;
+
+ if (!seen_broken_client) {
+ last_broken_addr.SetNoAddr();
+ seen_broken_client = 1;
+ }
+
+ if (last_broken_addr != request->client_addr) {
+ debugs(29, 1, "\nDigest POST bug detected from " <<
+ request->client_addr << " using '" <<
+ (useragent ? useragent : "-") <<
+ "'. Please upgrade browser. See Bug #630 for details.");
+
+ last_broken_addr = request->client_addr;
+ }
+ }
+ } else {
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+ digest_request->setDenyMessage("Incorrect password");
+ return;
+ }
+
+ /* check for stale nonce */
+ if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
+ debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK but nonce stale");
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->setDenyMessage("Stale nonce");
+ return;
+ }
+ }
+
+ auth_user->credentials(AuthUser::Ok);
+
+ /* password was checked and did match */
+ debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK");
+
+ /* auth_user is now linked, we reset these values
+ * after external auth occurs anyway */
+ auth_user->expiretime = current_time.tv_sec;
+ return;
+}
+
+int
+AuthDigestUserRequest::module_direction()
+{
+ if (user()->auth_type != AUTH_DIGEST)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Ok:
+ return 0;
+
+ case AuthUser::Failed:
+ /* send new challenge */
+ return 1;
+
+ case AuthUser::Unchecked:
+ case AuthUser::Pending:
+ return -1;
+
+ default:
+ return -2;
+ }
+}
+
+/* add the [proxy]authorisation header */
+void
+AuthDigestUserRequest::addHeader(HttpReply * rep, int accel)
+{
+ http_hdr_type type;
+
+ /* don't add to authentication error pages */
+
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+
+#if WAITING_FOR_TE
+ /* test for http/1.1 transfer chunked encoding */
+ if (chunkedtest)
+ return;
+#endif
+
+ if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) {
+ flags.authinfo_sent = 1;
+ debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
+ httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
+ }
+}
+
+#if WAITING_FOR_TE
+/** add the [proxy]authorisation header */
+void
+AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel)
+{
+ int type;
+
+ if (!auth_user_request)
+ return;
+
+ /* has the header already been send? */
+ if (flags.authinfo_sent)
+ return;
+
+ /* don't add to authentication error pages */
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+
+ if ((static_cast<AuthDigestConfig*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
+ debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
+ httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
+ }
+}
+#endif
+
+/* send the initial data to a digest authenticator module */
+void
+AuthDigestUserRequest::module_start(RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ char buf[8192];
+
+ assert(user() != NULL && user()->auth_type == AUTH_DIGEST);
+ debugs(29, 9, "authenticateStart: '\"" << user()->username() << "\":\"" << realm << "\"'");
+
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = static_cast<AuthUserRequest*>(this);
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->utf8) {
+ char userstr[1024];
+ latin1_to_utf8(userstr, sizeof(userstr), user()->username());
+ snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
+ } else {
+ snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
+ }
+
+ helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply, r);
+}
+
+void
+AuthDigestUserRequest::HandleReply(void *data, char *reply)
+{
+ authenticateStateData *replyData = static_cast < authenticateStateData * >(data);
+ char *t = NULL;
+ void *cbdata;
+ debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
+
+ if (reply) {
+ if ((t = strchr(reply, ' ')))
+ *t++ = '\0';
+
+ if (*reply == '\0' || *reply == '\n')
+ reply = NULL;
+ }
+
+ assert(replyData->auth_user_request != NULL);
+ AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request;
+
+ if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
+ /* allow this because the digest_request pointer is purely local */
+ AuthDigestUserRequest *digest_request = dynamic_cast<AuthDigestUserRequest *>(auth_user_request.getRaw());
+ assert(digest_request);
+
+ digest_request->user()->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+
+ if (t && *t)
+ digest_request->setDenyMessage(t);
+ } else if (reply) {
+ /* allow this because the digest_request pointer is purely local */
+ DigestUser *digest_user = dynamic_cast<DigestUser *>(auth_user_request->user().getRaw());
+ assert(digest_user != NULL);
+
+ CvtBin(reply, digest_user->HA1);
+ digest_user->HA1created = 1;
+ }
+
+ if (cbdataReferenceValidDone(replyData->data, &cbdata))
+ replyData->handler(cbdata, NULL);
+
+ replyData->auth_user_request = NULL;
+
+ cbdataFree(replyData);
+}
--- /dev/null
+#ifndef _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+#define _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "auth/digest/auth_digest.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+
+/**
+ * The AuthDigestUserRequest structure is what follows the http_request around
+ */
+class AuthDigestUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthDigestUserRequest);
+
+ AuthDigestUserRequest();
+ virtual ~AuthDigestUserRequest();
+
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void addHeader(HttpReply * rep, int accel);
+#if WAITING_FOR_TE
+
+ virtual void addTrailer(HttpReply * rep, int accel);
+#endif
+
+ virtual void module_start(RH *, void *);
+
+ char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
+ char *cnonce; /* "0a4f113b" */
+ char *realm; /* = "testrealm@host.com" */
+ char *pszPass; /* = "Circle Of Life" */
+ char *algorithm; /* = "md5" */
+ char nc[9]; /* = "00000001" */
+ char *pszMethod; /* = "GET" */
+ char *qop; /* = "auth" */
+ char *uri; /* = "/dir/index.html" */
+ char *response;
+
+ struct {
+ unsigned int authinfo_sent:1;
+ unsigned int invalid_password:1;
+ unsigned int helper_queried:1;
+ } flags;
+ digest_nonce_h *nonce;
+
+private:
+ static HLPCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H */
#include "squid.h"
-#include "auth_negotiate.h"
+#include "auth/negotiate/auth_negotiate.h"
#include "auth/Gadgets.h"
+#include "auth/State.h"
#include "CacheManager.h"
#include "Store.h"
#include "client_side.h"
#include "HttpRequest.h"
#include "SquidTime.h"
/** \todo remove this include */
-#include "negotiateScheme.h"
+#include "auth/negotiate/negotiateScheme.h"
+#include "auth/negotiate/negotiateUserRequest.h"
#include "wordlist.h"
/**
\ingroup AuthNegotiateAPI
*/
-/**
- * Maximum length (buffer size) for token strings.
- */
-// AYJ: must match re-definition in helpers/negotiate_auth/squid_kerb_auth/squid_kerb_auth.c
-#define MAX_AUTHTOKEN_LEN 32768
-
-
-/// \ingroup AuthNegotiateInternal
-static void
-authenticateStateFree(authenticateStateData * r)
-{
- AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
- cbdataFree(r);
-}
-
/* Negotiate Scheme */
-static HLPSCB authenticateNegotiateHandleReply;
static AUTHSSTATS authenticateNegotiateStats;
/// \ingroup AuthNegotiateInternal
-static statefulhelper *negotiateauthenticators = NULL;
-
-CBDATA_TYPE(authenticateStateData);
+statefulhelper *negotiateauthenticators = NULL;
/// \ingroup AuthNegotiateInternal
static int authnegotiate_initialised = 0;
/// \ingroup AuthNegotiateInternal
-static auth_negotiate_config negotiateConfig;
+AuthNegotiateConfig negotiateConfig;
/// \ingroup AuthNegotiateInternal
static hash_table *proxy_auth_cache = NULL;
*
*/
-/**
- \ingroup AuthNegotiateInternal
- \todo move to negotiateScheme.cc
- */
void
-negotiateScheme::done()
+AuthNegotiateConfig::rotateHelpers()
{
- /* TODO: this should be a Config call. */
- debugs(29, 2, "negotiateScheme::done: shutting down Negotiate authentication.");
-
- if (negotiateauthenticators)
+ /* schedule closure of existing helpers */
+ if (negotiateauthenticators) {
helperStatefulShutdown(negotiateauthenticators);
+ }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+void
+AuthNegotiateConfig::done()
+{
authnegotiate_initialised = 0;
+ if (negotiateauthenticators) {
+ helperStatefulShutdown(negotiateauthenticators);
+ }
+
if (!shutting_down)
return;
delete negotiateauthenticators;
negotiateauthenticators = NULL;
- debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
-}
-
-void
-AuthNegotiateConfig::done()
-{
if (authenticate)
wordlistDestroy(&authenticate);
+
+ debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
}
void
}
-AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(20), keep_alive(1)
+AuthNegotiateConfig::AuthNegotiateConfig() : keep_alive(1)
{ }
void
const char *
AuthNegotiateConfig::type() const
{
- return negotiateScheme::GetInstance().type();
+ return negotiateScheme::GetInstance()->type();
}
/**
}
/* Negotiate Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNegotiateUserRequest::module_direction()
-{
- /* null auth_user is checked for by authenticateDirection */
-
- if (waiting || client_blob)
- return -1; /* need helper response to continue */
-
- switch (auth_state) {
-
- /* no progress at all. */
-
- case AUTHENTICATE_STATE_NONE:
- debugs(29, 1, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev.");
- return -2; /* error */
-
- case AUTHENTICATE_STATE_FAILED:
- return -2; /* error */
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- assert(server_blob);
- return 1; /* send to client */
-
- case AUTHENTICATE_STATE_DONE:
- return 0; /* do nothing */
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
- return -2;
- }
-
- return -2;
-}
-
-/* add the [proxy]authorisation header */
void
-AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
-{
- http_hdr_type type;
-
- if (!server_blob)
- return;
-
- /* don't add to authentication error pages */
-
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
- httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
-
- safe_free(server_blob);
-}
-
-void
-AuthNegotiateConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
+AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
{
AuthNegotiateUserRequest *negotiate_request;
request->flags.proxy_keepalive = 0;
}
} else {
- negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
-
+ negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
assert(negotiate_request != NULL);
- switch (negotiate_request->auth_state) {
+ switch (negotiate_request->user()->credentials()) {
- case AUTHENTICATE_STATE_FAILED:
+ case AuthUser::Failed:
/* here it makes sense to drop the connection, as auth is
* tied to it, even if MAYBE the client could handle it - Kinkie */
rep->header.delByName("keep-alive");
request->flags.proxy_keepalive = 0;
/* fall through */
- case AUTHENTICATE_STATE_DONE:
+ case AuthUser::Ok:
/* Special case: authentication finished OK but disallowed by ACL.
* Need to start over to give the client another chance.
*/
-
if (negotiate_request->server_blob) {
debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
}
-
break;
- case AUTHENTICATE_STATE_NONE:
+ case AuthUser::Unchecked:
/* semantic change: do not drop the connection.
* 2.5 implementation used to keep it open - Kinkie */
debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
break;
- case AUTHENTICATE_STATE_IN_PROGRESS:
+ case AuthUser::Handshake:
/* we're waiting for a response from the client. Pass it the blob */
debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
safe_free(negotiate_request->server_blob);
break;
-
default:
- debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
+ debugs(29, DBG_CRITICAL, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->user()->credentials() << ".");
fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
}
}
debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
}
-static void
-authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
+int32_t
+NegotiateUser::ttl() const
{
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
-
- int valid;
- char *blob, *arg = NULL;
-
- AuthUserRequest *auth_user_request;
- AuthUser *auth_user;
- NegotiateUser *negotiate_user;
- AuthNegotiateUserRequest *negotiate_request;
-
- debugs(29, 8, "authenticateNegotiateHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
- valid = cbdataReferenceValid(r->data);
-
- if (!valid) {
- debugs(29, 1, "authenticateNegotiateHandleReply: invalid callback data. helper '" << lastserver << "'.");
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- return;
- }
-
- if (!reply) {
- debugs(29, 1, "authenticateNegotiateHandleReply: Helper '" << lastserver << "' crashed!.");
- reply = (char *)"BH Internal error";
- }
-
- auth_user_request = r->auth_user_request;
- assert(auth_user_request != NULL);
- negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
-
- assert(negotiate_request != NULL);
- 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());
-
- assert(negotiate_user != NULL);
-
- if (negotiate_request->authserver == NULL)
- negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(negotiate_request->authserver == lastserver);
-
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
-
- if (blob) {
- blob++;
- arg = strchr(blob + 1, ' ');
- } else {
- arg = NULL;
- }
-
- if (strncasecmp(reply, "TT ", 3) == 0) {
- /* we have been given a blob to send to the client */
- if (arg)
- *arg++ = '\0';
- safe_free(negotiate_request->server_blob);
- negotiate_request->request->flags.must_keepalive = 1;
- if (negotiate_request->request->flags.proxy_keepalive) {
- negotiate_request->server_blob = xstrdup(blob);
- negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
- auth_user_request->denyMessage("Authentication in progress");
- debugs(29, 4, "authenticateNegotiateHandleReply: Need to challenge the client with a server blob '" << blob << "'");
- } else {
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
- auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
- }
- } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
- /* we're finished, release the helper */
-
- if (arg)
- *arg++ = '\0';
-
- negotiate_user->username(arg);
-
- auth_user_request->denyMessage("Login successful");
-
- safe_free(negotiate_request->server_blob);
-
- negotiate_request->server_blob = xstrdup(blob);
-
- negotiate_request->releaseAuthServer();
-
- negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
-
- debugs(29, 4, "authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '" << blob << "'");
-
- /* connection is authenticated */
- debugs(29, 4, "AuthNegotiateUserRequest::authenticate: authenticated user " << negotiate_user->username());
- /* see if this is an existing user with a different proxy_auth
- * string */
- AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
- AuthUser *local_auth_user = negotiate_request->user();
- while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0))
- usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- negotiate_request->_auth_user = local_auth_user;
- } else {
- /* store user in hash's */
- local_auth_user->addToNameCache();
- // authenticateUserNameCacheAdd(local_auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- local_auth_user->expiretime = current_time.tv_sec;
- negotiate_request->releaseAuthServer();
- negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
-
- } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
- /* authentication failure (wrong password, etc.) */
-
- if (arg)
- *arg++ = '\0';
-
- auth_user_request->denyMessage(arg);
-
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
-
- safe_free(negotiate_request->server_blob);
-
- negotiate_request->server_blob = xstrdup(blob);
-
- negotiate_request->releaseAuthServer();
-
- debugs(29, 4, "authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '" << blob << "'");
- } else if (strncasecmp(reply, "BH ", 3) == 0) {
- /* TODO kick off a refresh process. This can occur after a YR or after
- * a KK. If after a YR release the helper and resubmit the request via
- * Authenticate Negotiate start.
- * If after a KK deny the user's request w/ 407 and mark the helper as
- * Needing YR. */
- auth_user_request->denyMessage(blob);
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(negotiate_request->server_blob);
- negotiate_request->releaseAuthServer();
- debugs(29, 1, "authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '" << reply << "'");
- } else {
- /* protocol error */
- fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- }
-
- if (negotiate_request->request) {
- HTTPMSGUNLOCK(negotiate_request->request);
- negotiate_request->request = NULL;
- }
- r->handler(r->data, NULL);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ return -1; // Negotiate cannot be cached.
}
static void
helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
}
-
-/** send the initial data to a stateful negotiate authenticator module */
-void
-AuthNegotiateUserRequest::module_start(RH * handler, void *data)
-{
- authenticateStateData *r = NULL;
- static char buf[MAX_AUTHTOKEN_LEN];
- negotiate_user_t *negotiate_user;
- AuthUser *auth_user = user();
-
- assert(data);
- assert(handler);
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NEGOTIATE);
-
- negotiate_user = dynamic_cast<negotiate_user_t *>(user());
-
- debugs(29, 8, "AuthNegotiateUserRequest::module_start: auth state is '" << auth_state << "'");
-
- if (negotiateConfig.authenticate == NULL) {
- debugs(29, 0, "AuthNegotiateUserRequest::module_start: no Negotiate program specified.");
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
-
- if (auth_state == AUTHENTICATE_STATE_INITIAL) {
- snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
- } else {
- snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
- }
-
- waiting = 1;
-
- safe_free(client_blob);
- helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
-}
-
-/**
- * Atomic action: properly release the Negotiate auth helpers which may have been reserved
- * for this request connections use.
- */
-void
-AuthNegotiateUserRequest::releaseAuthServer()
-{
- if (authserver) {
- debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- } else
- debugs(29, 6, HERE << "No Negotiate auth server to release.");
-}
-
-/* clear any connection related authentication details */
-void
-AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
- return;
- }
-
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
-
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
-}
-
/*
* Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
-AuthUserRequest *
+AuthUserRequest::Pointer
AuthNegotiateConfig::decode(char const *proxy_auth)
{
NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
- AuthNegotiateUserRequest *auth_user_request = new AuthNegotiateUserRequest ();
+ AuthUserRequest *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 */
debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
return auth_user_request;
}
-int
-AuthNegotiateUserRequest::authenticated() const
-{
- if (auth_state == AUTHENTICATE_STATE_DONE) {
- debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated.");
- return 1;
- }
-
- debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated.");
-
- return 0;
-}
-
-void
-AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
- const char *proxy_auth, *blob;
-
- /** \todo rename this!! */
- AuthUser *local_auth_user;
- negotiate_user_t *negotiate_user;
-
- local_auth_user = user();
- assert(local_auth_user);
- assert(local_auth_user->auth_type == AUTH_NEGOTIATE);
- negotiate_user = dynamic_cast<negotiate_user_t *>(local_auth_user);
- assert (this);
-
- /** Check that we are in the client side, where we can generate
- * auth challenges */
-
- if (conn == NULL) {
- auth_state = AUTHENTICATE_STATE_FAILED;
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!");
- return;
- }
-
- if (waiting) {
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: waiting for helper reply!");
- return;
- }
-
- if (server_blob) {
- debugs(29, 2, "AuthNegotiateUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
- return;
- }
-
- /* get header */
- proxy_auth = aRequest->header.getStr(type);
-
- /* locate second word */
- blob = proxy_auth;
-
- if (blob) {
- while (xisspace(*blob) && *blob)
- blob++;
-
- while (!xisspace(*blob) && *blob)
- blob++;
-
- while (xisspace(*blob) && *blob)
- blob++;
- }
-
- switch (auth_state) {
-
- case AUTHENTICATE_STATE_NONE:
- /* we've received a negotiate request. pass to a helper */
- debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '" << proxy_auth << "'");
- auth_state = AUTHENTICATE_STATE_INITIAL;
- safe_free(client_blob);
- client_blob=xstrdup(blob);
- conn->auth_type = AUTH_NEGOTIATE;
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
- AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: need to ask helper");
-
- return;
-
- break;
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- /* we should have received a blob from the client. Hand it off to
- * some helper */
- safe_free(client_blob);
-
- client_blob = xstrdup (blob);
-
- if (request)
- HTTPMSGUNLOCK(request);
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_DONE:
- fatal("AuthNegotiateUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
-
- break;
-
- case AUTHENTICATE_STATE_FAILED:
- /* we've failed somewhere in authentication */
- debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate failed. " << proxy_auth);
-
- return;
-
- break;
- }
-
- return;
-}
-
-AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
- /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
- _theUser(NULL)
-{
- waiting=0;
- client_blob=0;
- server_blob=0;
- authserver=NULL;
- request=NULL;
-}
-
-AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
-{
- safe_free(server_blob);
- safe_free(client_blob);
-
- if (authserver != NULL) {
- debugs(29, 9, "AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- }
- if (request) {
- HTTPMSGUNLOCK(request);
- request = NULL;
- }
-}
-
void
NegotiateUser::deleteSelf() const
{
delete this;
}
-NegotiateUser::NegotiateUser (AuthConfig *aConfig) : AuthUser (aConfig)
+NegotiateUser::NegotiateUser(AuthConfig *aConfig) : AuthUser (aConfig)
{
proxy_auth_list.head = proxy_auth_list.tail = NULL;
}
-
-AuthConfig *
-negotiateScheme::createConfig()
-{
- return &negotiateConfig;
-}
-
-const char *
-AuthNegotiateUserRequest::connLastHeader()
-{
- return NULL;
-}
-
#ifndef __AUTH_NEGOTIATE_H__
#define __AUTH_NEGOTIATE_H__
+
+#include "auth/Config.h"
#include "auth/Gadgets.h"
+#include "auth/State.h"
#include "auth/User.h"
#include "auth/UserRequest.h"
-#include "auth/Config.h"
#include "helper.h"
/**
/// \ingroup AuthNegotiateAPI
#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-
-/// \ingroup AuthNegotiateAPI
-typedef enum {
- AUTHENTICATE_STATE_NONE,
- AUTHENTICATE_STATE_INITIAL,
- AUTHENTICATE_STATE_IN_PROGRESS,
- AUTHENTICATE_STATE_DONE,
- AUTHENTICATE_STATE_FAILED
-} auth_state_t; /* connection level auth state */
-
-/* Generic */
-
-/// \ingroup AuthNegotiateAPI
-typedef struct {
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-} authenticateStateData;
-#endif
-
/// \ingroup AuthNegotiateAPI
class NegotiateUser : public AuthUser
{
public:
MEMPROXY_CLASS(NegotiateUser);
- virtual void deleteSelf() const;
NegotiateUser(AuthConfig *);
~NegotiateUser();
+ virtual void deleteSelf() const;
+ virtual int32_t ttl() const;
+
dlink_list proxy_auth_list;
};
MEMPROXY_CLASS_INLINE(NegotiateUser);
-/// \ingroup AuthNegotiateAPI
-typedef class NegotiateUser negotiate_user_t;
-
-/// \ingroup AuthNegotiateAPI
-class AuthNegotiateUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthNegotiateUserRequest);
-
- AuthNegotiateUserRequest();
- virtual ~AuthNegotiateUserRequest();
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void onConnectionClose(ConnStateData *);
- virtual void module_start(RH *, void *);
- virtual 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;
- void releaseAuthServer(void); ///< Release the authserver helper server properly.
-
- /* what connection is this associated with */
- /* ConnStateData * conn;*/
-
- /* how far through the authentication process are we? */
- auth_state_t auth_state;
-
- /* our current blob to pass to the client */
- char *server_blob;
- /* our current blob to pass to the server */
- char *client_blob;
-
- /* currently waiting for helper response */
- unsigned char waiting;
-
- /* need access to the request flags to mess around on pconn failure */
- HttpRequest *request;
-
-private:
- /* the user */
- NegotiateUser * _theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
-
-#include "HelperChildConfig.h"
+extern statefulhelper *negotiateauthenticators;
/* configuration runtime data */
AuthNegotiateConfig();
virtual bool active() const;
virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
virtual void done();
+ virtual void rotateHelpers();
virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
virtual void init(AuthConfig *);
virtual void parse(AuthConfig *, int, char *);
virtual void registerWithCacheManager(void);
virtual const char * type() const;
- HelperChildConfig authenticateChildren;
int keep_alive;
- wordlist *authenticate;
};
-/// \ingroup AuthNegotiateAPI
-typedef class AuthNegotiateConfig auth_negotiate_config;
+extern AuthNegotiateConfig negotiateConfig;
#endif
-
/*
* $Id$
*
*
*/
-#include "negotiateScheme.h"
+#include "config.h"
+#include "auth/negotiate/negotiateScheme.h"
+#include "helper.h"
-AuthScheme &
+AuthScheme::Pointer
negotiateScheme::GetInstance()
{
- if (_instance == NULL)
+ if (_instance == NULL) {
_instance = new negotiateScheme();
- return *_instance;
-}
-
-negotiateScheme::negotiateScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
}
char const *
return "negotiate";
}
-negotiateScheme *negotiateScheme::_instance = NULL;
+AuthScheme::Pointer negotiateScheme::_instance = NULL;
+
+/**
+ \ingroup AuthNegotiateInternal
+ \todo move to negotiateScheme.cc
+ */
+void
+negotiateScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
+}
+
+AuthConfig *
+negotiateScheme::createConfig()
+{
+ AuthNegotiateConfig *negotiateCfg = new AuthNegotiateConfig;
+ return dynamic_cast<AuthConfig*>(negotiateCfg);
+}
-
/*
* $Id$
*
#define SQUID_NEGOTIATESCHEME_H
#include "auth/Scheme.h"
+#include "auth/negotiate/auth_negotiate.h"
/// \ingroup AuthSchemeAPI
/// \ingroup AuthAPI
{
public:
- static AuthScheme &GetInstance();
- negotiateScheme();
+ static AuthScheme::Pointer 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;
+ static AuthScheme::Pointer _instance;
};
#endif /* SQUID_negotiateSCHEME_H */
--- /dev/null
+#include "config.h"
+#include "auth/negotiate/auth_negotiate.h"
+#include "auth/negotiate/negotiateUserRequest.h"
+#include "auth/User.h"
+#include "helper.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/**
+ * Maximum length (buffer size) for token strings.
+ */
+// AYJ: must match re-definition in helpers/negotiate_auth/kerberos/negotiate_kerb_auth.cc
+#define MAX_AUTHTOKEN_LEN 32768
+
+AuthNegotiateUserRequest::AuthNegotiateUserRequest()
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+ request=NULL;
+}
+
+AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
+{
+ assert(RefCountCount()==0);
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ if (authserver != NULL) {
+ debugs(29, 9, HERE << "releasing server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ }
+ if (request) {
+ HTTPMSGUNLOCK(request);
+ request = NULL;
+ }
+}
+
+const char *
+AuthNegotiateUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
+int
+AuthNegotiateUserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == AuthUser::Ok) {
+ debugs(29, 9, HERE << "user authenticated.");
+ return 1;
+ }
+
+ debugs(29, 9, HERE << "user not fully authenticated.");
+ return 0;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNegotiateUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+
+ if (waiting || client_blob)
+ return -1; /* need helper response to continue */
+
+ if (user()->auth_type != AUTH_NEGOTIATE)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Handshake:
+ assert(server_blob);
+ return 1; /* send to client */
+
+ case AuthUser::Ok:
+ return 0; /* do nothing */
+
+ case AuthUser::Failed:
+ return -2;
+
+ default:
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication in unexpected state: " << user()->credentials());
+ return -2;
+ }
+}
+
+/* add the [proxy]authorisation header */
+void
+AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
+{
+ http_hdr_type type;
+
+ if (!server_blob)
+ return;
+
+ /* don't add to authentication error pages */
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+ httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
+
+ safe_free(server_blob);
+}
+
+/** send the initial data to a stateful negotiate authenticator module */
+void
+AuthNegotiateUserRequest::module_start(RH * handler, void *data)
+{
+ static char buf[MAX_AUTHTOKEN_LEN];
+
+ assert(data);
+ assert(handler);
+
+ assert(user() != NULL);
+ assert(user()->auth_type == AUTH_NEGOTIATE);
+
+ debugs(29, 8, HERE << "auth state is '" << user()->credentials() << "'");
+
+ if (static_cast<AuthNegotiateConfig*>(AuthConfig::Find("negotiate"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Negotiate authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ authenticateStateData *r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = this;
+
+ if (user()->credentials() == AuthUser::Pending) {
+ snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ } else {
+ snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
+ }
+
+ waiting = 1;
+
+ safe_free(client_blob);
+ helperStatefulSubmit(negotiateauthenticators, buf, AuthNegotiateUserRequest::HandleReply, r, authserver);
+}
+
+/**
+ * Atomic action: properly release the Negotiate auth helpers which may have been reserved
+ * for this request connections use.
+ */
+void
+AuthNegotiateUserRequest::releaseAuthServer()
+{
+ if (authserver) {
+ debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ } else
+ debugs(29, 6, HERE << "No Negotiate auth server to release.");
+}
+
+/* clear any connection related authentication details */
+void
+AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
+{
+ assert(conn != NULL);
+
+ debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
+
+ if (conn->auth_user_request == NULL) {
+ debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
+ return;
+ }
+
+ releaseAuthServer();
+
+ /* unlock the connection based lock */
+ debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
+
+ conn->auth_user_request = NULL;
+}
+
+void
+AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
+{
+ assert (this);
+
+ /** Check that we are in the client side, where we can generate auth challenges */
+ if (conn == NULL) {
+ user()->credentials(AuthUser::Failed);
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication attempt to perform authentication without a connection!");
+ return;
+ }
+
+ if (waiting) {
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication waiting for helper reply!");
+ return;
+ }
+
+ if (server_blob) {
+ debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!");
+ return;
+ }
+
+ /* get header */
+ const char *proxy_auth = aRequest->header.getStr(type);
+
+ /* locate second word */
+ const char *blob = proxy_auth;
+
+ if (blob) {
+ while (xisspace(*blob) && *blob)
+ blob++;
+
+ while (!xisspace(*blob) && *blob)
+ blob++;
+
+ while (xisspace(*blob) && *blob)
+ blob++;
+ }
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ /* we've received a negotiate request. pass to a helper */
+ debugs(29, 9, HERE << "auth state negotiate none. Received blob: '" << proxy_auth << "'");
+ user()->credentials(AuthUser::Pending);
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ assert(conn->auth_user_request == NULL);
+ conn->auth_user_request = this;
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Pending:
+ debugs(29, 1, HERE << "need to ask helper");
+ break;
+
+ case AuthUser::Handshake:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
+ client_blob = xstrdup(blob);
+ if (request)
+ HTTPMSGUNLOCK(request);
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Ok:
+ fatal("AuthNegotiateUserRequest::authenticate: unexpected auth state DONE! Report a bug to the squid developers.\n");
+ break;
+
+ case AuthUser::Failed:
+ /* we've failed somewhere in authentication */
+ debugs(29, 9, HERE << "auth state negotiate failed. " << proxy_auth);
+ break;
+ }
+
+ return;
+}
+
+void
+AuthNegotiateUserRequest::HandleReply(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+ int valid;
+ char *blob, *arg = NULL;
+
+ debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+ valid = cbdataReferenceValid(r->data);
+
+ if (!valid) {
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication invalid callback data. helper '" << lastserver << "'.");
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ return;
+ }
+
+ if (!reply) {
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication Helper '" << lastserver << "' crashed!.");
+ reply = (char *)"BH Internal error";
+ }
+
+ AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+
+ AuthNegotiateUserRequest *negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
+ assert(negotiate_request != NULL);
+
+ assert(negotiate_request->waiting);
+ negotiate_request->waiting = 0;
+ safe_free(negotiate_request->client_blob);
+
+ assert(auth_user_request->user() != NULL);
+ assert(auth_user_request->user()->auth_type == AUTH_NEGOTIATE);
+
+ if (negotiate_request->authserver == NULL)
+ negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(negotiate_request->authserver == lastserver);
+
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
+
+ if (blob) {
+ blob++;
+ arg = strchr(blob + 1, ' ');
+ } else {
+ arg = NULL;
+ }
+
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ /* we have been given a blob to send to the client */
+ if (arg)
+ *arg++ = '\0';
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->request->flags.must_keepalive = 1;
+ if (negotiate_request->request->flags.proxy_keepalive) {
+ negotiate_request->server_blob = xstrdup(blob);
+ auth_user_request->user()->credentials(AuthUser::Handshake);
+ auth_user_request->denyMessage("Authentication in progress");
+ debugs(29, 4, HERE << "Need to challenge the client with a server blob '" << blob << "'");
+ } else {
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+ }
+ } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
+ /* we're finished, release the helper */
+
+ if (arg)
+ *arg++ = '\0';
+
+ auth_user_request->user()->username(arg);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->server_blob = xstrdup(blob);
+ negotiate_request->releaseAuthServer();
+ auth_user_request->user()->credentials(AuthUser::Ok);
+ debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << blob << "'");
+
+ /* connection is authenticated */
+ debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
+ AuthUser::Pointer local_auth_user = negotiate_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), auth_user_request->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 after merging as
+ * much of it new state into the existing one as possible */
+ usernamehash->user()->absorb(local_auth_user);
+ local_auth_user = usernamehash->user();
+ /* from here on we are working with the original cached credentials. */
+ negotiate_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ local_auth_user->expiretime = current_time.tv_sec;
+ negotiate_request->releaseAuthServer();
+ negotiate_request->user()->credentials(AuthUser::Ok);
+
+ } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
+ /* authentication failure (wrong password, etc.) */
+
+ if (arg)
+ *arg++ = '\0';
+
+ auth_user_request->denyMessage(arg);
+ negotiate_request->user()->credentials(AuthUser::Failed);
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->server_blob = xstrdup(blob);
+ negotiate_request->releaseAuthServer();
+ debugs(29, 4, HERE << "Failed validating user via Negotiate. Error returned '" << blob << "'");
+ } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ /* TODO kick off a refresh process. This can occur after a YR or after
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate Negotiate start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
+ * Needing YR. */
+ auth_user_request->denyMessage(blob);
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->releaseAuthServer();
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication validating user. Error returned '" << reply << "'");
+ } else {
+ /* protocol error */
+ fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ negotiate_request->request = NULL;
+ r->handler(r->data, NULL);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+}
+
--- /dev/null
+#ifndef _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "helper.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+/// \ingroup AuthNegotiateAPI
+class AuthNegotiateUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthNegotiateUserRequest);
+
+ AuthNegotiateUserRequest();
+ virtual ~AuthNegotiateUserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void onConnectionClose(ConnStateData *);
+ virtual void module_start(RH *, void *);
+
+ virtual void addHeader(HttpReply * rep, int accel);
+
+ virtual const char * connLastHeader();
+
+ /* we need to store the helper server between requests */
+ helper_stateful_server *authserver;
+ void releaseAuthServer(void); ///< Release the authserver helper server properly.
+
+ /* what connection is this associated with */
+ /* ConnStateData * conn;*/
+
+ /* our current blob to pass to the client */
+ char *server_blob;
+ /* our current blob to pass to the server */
+ char *client_blob;
+
+ /* currently waiting for helper response */
+ unsigned char waiting;
+
+ /* need access to the request flags to mess around on pconn failure */
+ HttpRequest *request;
+
+private:
+ static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H */
#include "squid.h"
-#include "auth_ntlm.h"
#include "auth/Gadgets.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/ntlm/ntlmScheme.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/State.h"
#include "CacheManager.h"
#include "Store.h"
#include "client_side.h"
#include "HttpReply.h"
#include "HttpRequest.h"
-/* TODO remove this include */
-#include "ntlmScheme.h"
#include "wordlist.h"
#include "SquidTime.h"
-
-static void
-authenticateStateFree(authenticateStateData * r)
-{
- AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
- cbdataFree(r);
-}
-
/* NTLM Scheme */
-static HLPSCB authenticateNTLMHandleReply;
static AUTHSSTATS authenticateNTLMStats;
-static statefulhelper *ntlmauthenticators = NULL;
-
-CBDATA_TYPE(authenticateStateData);
-
+statefulhelper *ntlmauthenticators = NULL;
static int authntlm_initialised = 0;
-static auth_ntlm_config ntlmConfig;
-
static hash_table *proxy_auth_cache = NULL;
/*
*
*/
-/* move to ntlmScheme.cc */
void
-ntlmScheme::done()
+AuthNTLMConfig::rotateHelpers()
{
- /* TODO: this should be a Config call. */
- debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
-
- if (ntlmauthenticators)
+ /* schedule closure of existing helpers */
+ if (ntlmauthenticators) {
helperStatefulShutdown(ntlmauthenticators);
+ }
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+/* free any allocated configuration details */
+void
+AuthNTLMConfig::done()
+{
authntlm_initialised = 0;
+ if (ntlmauthenticators) {
+ helperStatefulShutdown(ntlmauthenticators);
+ }
+
if (!shutting_down)
return;
delete ntlmauthenticators;
ntlmauthenticators = NULL;
- debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
-}
-
-/* free any allocated configuration details */
-void
-AuthNTLMConfig::done()
-{
if (authenticate)
wordlistDestroy(&authenticate);
+
+ debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
}
void
}
-AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(20), keep_alive(1)
+AuthNTLMConfig::AuthNTLMConfig() : keep_alive(1)
{ }
void
const char *
AuthNTLMConfig::type() const
{
- return ntlmScheme::GetInstance().type();
+ return ntlmScheme::GetInstance()->type();
}
/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
}
/* NTLM Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNTLMUserRequest::module_direction()
-{
- /* null auth_user is checked for by authenticateDirection */
-
- if (waiting || client_blob)
- return -1; /* need helper response to continue */
-
- switch (auth_state) {
-
- /* no progress at all. */
-
- case AUTHENTICATE_STATE_NONE:
- debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
- return -2; /* error */
-
- case AUTHENTICATE_STATE_FAILED:
- return -2; /* error */
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- assert(server_blob);
- return 1; /* send to client */
-
- case AUTHENTICATE_STATE_DONE:
- return 0; /* do nothing */
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
- return -2;
- }
-
- return -2;
-}
void
-AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+AuthNTLMConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
{
- AuthNTLMUserRequest *ntlm_request;
-
if (!authenticate)
return;
request->flags.proxy_keepalive = 0;
}
} else {
- ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
-
+ AuthNTLMUserRequest *ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request.getRaw());
assert(ntlm_request != NULL);
- switch (ntlm_request->auth_state) {
+ switch (ntlm_request->user()->credentials()) {
- case AUTHENTICATE_STATE_FAILED:
+ case AuthUser::Failed:
/* here it makes sense to drop the connection, as auth is
* tied to it, even if MAYBE the client could handle it - Kinkie */
request->flags.proxy_keepalive = 0;
/* fall through */
- case AUTHENTICATE_STATE_DONE:
+ case AuthUser::Ok:
/* Special case: authentication finished OK but disallowed by ACL.
* Need to start over to give the client another chance.
*/
/* fall through */
- case AUTHENTICATE_STATE_NONE:
+ case AuthUser::Unchecked:
/* semantic change: do not drop the connection.
* 2.5 implementation used to keep it open - Kinkie */
debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << hdrType << " header: 'NTLM'");
httpHeaderPutStrf(&rep->header, hdrType, "NTLM");
break;
- case AUTHENTICATE_STATE_IN_PROGRESS:
+ case AuthUser::Handshake:
/* we're waiting for a response from the client. Pass it the blob */
debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << hdrType << " header: 'NTLM " << ntlm_request->server_blob << "'");
httpHeaderPutStrf(&rep->header, hdrType, "NTLM %s", ntlm_request->server_blob);
safe_free(ntlm_request->server_blob);
break;
-
default:
- debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << ".");
+ debugs(29, DBG_CRITICAL, "AuthNTLMConfig::fixHeader: state " << ntlm_request->user()->credentials() << ".");
fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
}
}
debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
}
-static void
-authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
+int32_t
+NTLMUser::ttl() const
{
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
-
- int valid;
- char *blob;
-
- AuthUserRequest *auth_user_request;
- AuthUser *auth_user;
- NTLMUser *ntlm_user;
- AuthNTLMUserRequest *ntlm_request;
-
- debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
- valid = cbdataReferenceValid(r->data);
-
- if (!valid) {
- debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'.");
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- return;
- }
-
- if (!reply) {
- debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
- reply = (char *)"BH Internal error";
- }
-
- auth_user_request = r->auth_user_request;
- assert(auth_user_request != NULL);
- ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
-
- assert(ntlm_request != NULL);
- assert(ntlm_request->waiting);
- ntlm_request->waiting = 0;
- safe_free(ntlm_request->client_blob);
-
- auth_user = ntlm_request->user();
- assert(auth_user != NULL);
- assert(auth_user->auth_type == AUTH_NTLM);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
-
- assert(ntlm_user != NULL);
-
- if (ntlm_request->authserver == NULL)
- ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(ntlm_request->authserver == lastserver);
-
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
-
- if (blob)
- blob++;
-
- if (strncasecmp(reply, "TT ", 3) == 0) {
- /* we have been given a blob to send to the client */
- safe_free(ntlm_request->server_blob);
- ntlm_request->request->flags.must_keepalive = 1;
- if (ntlm_request->request->flags.proxy_keepalive) {
- ntlm_request->server_blob = xstrdup(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
- auth_user_request->denyMessage("Authentication in progress");
- debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
- } else {
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
- }
- } else if (strncasecmp(reply, "AF ", 3) == 0) {
- /* we're finished, release the helper */
- ntlm_user->username(blob);
- auth_user_request->denyMessage("Login successful");
- safe_free(ntlm_request->server_blob);
-
- debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
- /* connection is authenticated */
- debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_user->username());
- /* see if this is an existing user with a different proxy_auth
- * string */
- auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
- AuthUser *local_auth_user = ntlm_request->user();
- while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
- usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- ntlm_request->_auth_user = local_auth_user;
- } else {
- /* store user in hash's */
- local_auth_user->addToNameCache();
- // authenticateUserNameCacheAdd(local_auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- local_auth_user->expiretime = current_time.tv_sec;
- ntlm_request->releaseAuthServer();
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- } else if (strncasecmp(reply, "NA ", 3) == 0) {
- /* authentication failure (wrong password, etc.) */
- auth_user_request->denyMessage(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
- } else if (strncasecmp(reply, "BH ", 3) == 0) {
- /* TODO kick off a refresh process. This can occur after a YR or after
- * a KK. If after a YR release the helper and resubmit the request via
- * Authenticate NTLM start.
- * If after a KK deny the user's request w/ 407 and mark the helper as
- * Needing YR. */
- auth_user_request->denyMessage(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
- } else {
- /* protocol error */
- fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- }
-
- if (ntlm_request->request) {
- HTTPMSGUNLOCK(ntlm_request->request);
- ntlm_request->request = NULL;
- }
- r->handler(r->data, NULL);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ return -1; // NTLM credentials cannot be cached.
}
+
static void
authenticateNTLMStats(StoreEntry * sentry)
{
helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics");
}
-
-/* send the initial data to a stateful ntlm authenticator module */
-void
-AuthNTLMUserRequest::module_start(RH * handler, void *data)
-{
- authenticateStateData *r = NULL;
- static char buf[8192];
- ntlm_user_t *ntlm_user;
- AuthUser *auth_user = user();
-
- assert(data);
- assert(handler);
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NTLM);
-
- ntlm_user = dynamic_cast<ntlm_user_t *>(user());
-
- debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
-
- if (ntlmConfig.authenticate == NULL) {
- debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
-
- if (auth_state == AUTHENTICATE_STATE_INITIAL) {
- snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
- } else {
- snprintf(buf, 8192, "KK %s\n", client_blob);
- }
-
- waiting = 1;
-
- safe_free(client_blob);
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
-}
-
-/**
- * Atomic action: properly release the NTLM auth helpers which may have been reserved
- * for this request connections use.
- */
-void
-AuthNTLMUserRequest::releaseAuthServer()
-{
- if (authserver) {
- debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- } else
- debugs(29, 6, HERE << "No NTLM auth server to release.");
-}
-
-/* clear any connection related authentication details */
-void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
- return;
- }
-
- // unlock / un-reserve the helpers
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
-
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
-}
-
/*
* Decode a NTLM [Proxy-]Auth string, placing the results in the passed
* Auth_user structure.
*/
-AuthUserRequest *
+AuthUserRequest::Pointer
AuthNTLMConfig::decode(char const *proxy_auth)
{
- NTLMUser *newUser = new NTLMUser(&ntlmConfig);
- AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest ();
+ NTLMUser *newUser = new NTLMUser(AuthConfig::Find("ntlm"));
+ AuthUserRequest::Pointer auth_user_request = new AuthNTLMUserRequest();
assert(auth_user_request->user() == NULL);
+
auth_user_request->user(newUser);
auth_user_request->user()->auth_type = AUTH_NTLM;
- auth_user_request->user()->addRequest(auth_user_request);
/* all we have to do is identify that it's NTLM - the helper does the rest */
debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
return auth_user_request;
}
-int
-AuthNTLMUserRequest::authenticated() const
-{
- if (auth_state == AUTHENTICATE_STATE_DONE) {
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
- return 1;
- }
-
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
-
- return 0;
-}
-
-void
-AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
- const char *proxy_auth, *blob;
-
- /* TODO: rename this!! */
- AuthUser *local_auth_user;
- ntlm_user_t *ntlm_user;
-
- local_auth_user = user();
- assert(local_auth_user);
- assert(local_auth_user->auth_type == AUTH_NTLM);
- ntlm_user = dynamic_cast<ntlm_user_t *>(local_auth_user);
- assert (this);
-
- /* Check that we are in the client side, where we can generate
- * auth challenges */
-
- if (conn == NULL || !cbdataReferenceValid(conn)) {
- auth_state = AUTHENTICATE_STATE_FAILED;
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
- return;
- }
-
- if (waiting) {
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
- return;
- }
-
- if (server_blob) {
- debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
- return;
- }
-
- /* get header */
- proxy_auth = aRequest->header.getStr(type);
-
- /* locate second word */
- blob = proxy_auth;
-
- /* if proxy_auth is actually NULL, we'd better not manipulate it. */
- if (blob) {
- while (xisspace(*blob) && *blob)
- blob++;
-
- while (!xisspace(*blob) && *blob)
- blob++;
-
- while (xisspace(*blob) && *blob)
- blob++;
- }
-
- switch (auth_state) {
-
- case AUTHENTICATE_STATE_NONE:
- /* we've received a ntlm request. pass to a helper */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
- auth_state = AUTHENTICATE_STATE_INITIAL;
- safe_free(client_blob);
- client_blob=xstrdup(blob);
- conn->auth_type = AUTH_NTLM;
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
- AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
-
- return;
-
- break;
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- /* we should have received a blob from the client. Hand it off to
- * some helper */
- safe_free(client_blob);
-
- client_blob = xstrdup (blob);
-
- if (request)
- HTTPMSGUNLOCK(request);
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_DONE:
- fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
-
- break;
-
- case AUTHENTICATE_STATE_FAILED:
- /* we've failed somewhere in authentication */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
-
- return;
-
- break;
- }
-
- return;
-}
-
-AuthNTLMUserRequest::AuthNTLMUserRequest() :
- /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
- _theUser(NULL)
-{
- waiting=0;
- client_blob=0;
- server_blob=0;
- authserver=NULL;
- request = NULL;
-}
-
-AuthNTLMUserRequest::~AuthNTLMUserRequest()
-{
- safe_free(server_blob);
- safe_free(client_blob);
-
- releaseAuthServer();
-
- if (request) {
- HTTPMSGUNLOCK(request);
- request = NULL;
- }
-}
-
void
NTLMUser::deleteSelf() const
{
{
proxy_auth_list.head = proxy_auth_list.tail = NULL;
}
-
-AuthConfig *
-ntlmScheme::createConfig()
-{
- return &ntlmConfig;
-}
-
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
- return NULL;
-}
#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-typedef enum {
- AUTHENTICATE_STATE_NONE,
- AUTHENTICATE_STATE_INITIAL,
- AUTHENTICATE_STATE_IN_PROGRESS,
- AUTHENTICATE_STATE_DONE,
- AUTHENTICATE_STATE_FAILED
-} auth_state_t; /* connection level auth state */
-
-/* Generic */
-
-typedef struct {
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-} authenticateStateData;
-#endif
-
class NTLMUser : public AuthUser
{
public:
MEMPROXY_CLASS(NTLMUser);
- virtual void deleteSelf() const;
NTLMUser(AuthConfig *);
~NTLMUser();
+
+ virtual void deleteSelf() const;
+ virtual int32_t ttl() const;
+
dlink_list proxy_auth_list;
};
typedef class NTLMUser ntlm_user_t;
-class AuthNTLMUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthNTLMUserRequest);
-
- AuthNTLMUserRequest();
- virtual ~AuthNTLMUserRequest();
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void onConnectionClose(ConnStateData *);
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NTLMUser *>(aUser);}
-
- virtual const char * connLastHeader();
-
- /* we need to store the helper server between requests */
- helper_stateful_server *authserver;
- void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
-
- /* what connection is this associated with */
-// ConnStateData * conn;
-
- /* how far through the authentication process are we? */
- auth_state_t auth_state;
-
- /* our current blob to pass to the client */
- char *server_blob;
- /* our current blob to pass to the server */
- char *client_blob;
-
- /* currently waiting for helper response */
- unsigned char waiting;
-
- /* need access to the request flags to mess around on pconn failure */
- HttpRequest *request;
-
-private:
- /* the user */
- NTLMUser * _theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
-
-#include "HelperChildConfig.h"
-
/* configuration runtime data */
class AuthNTLMConfig : public AuthConfig
AuthNTLMConfig();
virtual bool active() const;
virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
virtual void done();
+ virtual void rotateHelpers();
virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
virtual void init(AuthConfig *);
virtual void parse(AuthConfig *, int, char *);
virtual void registerWithCacheManager(void);
virtual const char * type() const;
- HelperChildConfig authenticateChildren;
int keep_alive;
- wordlist *authenticate;
};
typedef class AuthNTLMConfig auth_ntlm_config;
+extern statefulhelper *ntlmauthenticators;
+
#endif
-
/*
* $Id$
*
*
*/
-#include "ntlmScheme.h"
+#include "config.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/ntlm/ntlmScheme.h"
+#include "helper.h"
-AuthScheme &
+AuthScheme::Pointer
ntlmScheme::GetInstance()
{
- if (_instance == NULL)
+ if (_instance == NULL) {
_instance = new ntlmScheme();
- return *_instance;
-}
-
-ntlmScheme::ntlmScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
}
char const *
return "ntlm";
}
-ntlmScheme *ntlmScheme::_instance = NULL;
+AuthScheme::Pointer ntlmScheme::_instance = NULL;
+
+void
+ntlmScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
+}
+
+AuthConfig *
+ntlmScheme::createConfig()
+{
+ auth_ntlm_config *ntlmCfg = new auth_ntlm_config;
+ return dynamic_cast<AuthConfig*>(ntlmCfg);
+}
#define SQUID_NTLMSCHEME_H
#include "auth/Scheme.h"
+#include "auth/ntlm/auth_ntlm.h"
/// \ingroup AuthSchemeAPI
/// \ingroup AuthAPI
{
public:
- static AuthScheme &GetInstance();
- ntlmScheme();
+ static AuthScheme::Pointer GetInstance();
+ ntlmScheme() {};
virtual ~ntlmScheme() {};
/* per scheme */
virtual char const *type () const;
virtual void done();
virtual AuthConfig *createConfig();
+
/* Not implemented */
ntlmScheme (ntlmScheme const &);
ntlmScheme &operator=(ntlmScheme const &);
private:
- static ntlmScheme *_instance;
+ /**
+ * Main instance of this authentication Scheme.
+ * NULL when the scheme is not being used.
+ */
+ static AuthScheme::Pointer _instance;
};
#endif /* SQUID_ntlmSCHEME_H */
--- /dev/null
+#include "config.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/State.h"
+#include "cbdata.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/* state wrapper functions */
+
+AuthNTLMUserRequest::AuthNTLMUserRequest()
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+ request = NULL;
+}
+
+AuthNTLMUserRequest::~AuthNTLMUserRequest()
+{
+ assert(RefCountCount()==0);
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ releaseAuthServer();
+
+ if (request) {
+ HTTPMSGUNLOCK(request);
+ request = NULL;
+ }
+}
+
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNTLMUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+
+ if (waiting || client_blob)
+ return -1; /* need helper response to continue */
+
+ if (user()->auth_type != AUTH_NTLM)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Handshake:
+ assert(server_blob);
+ return 1; /* send to client */
+
+ case AuthUser::Ok:
+ return 0; /* do nothing */
+
+ case AuthUser::Failed:
+ return -2;
+
+ default:
+ debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
+ return -2;
+ }
+}
+
+/* send the initial data to a stateful ntlm authenticator module */
+void
+AuthNTLMUserRequest::module_start(RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ static char buf[8192];
+
+ assert(data);
+ assert(handler);
+
+ debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
+
+ if (static_cast<AuthNTLMConfig*>(AuthConfig::Find("ntlm"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = this;
+
+ if (user()->credentials() == AuthUser::Pending) {
+ snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ } else {
+ snprintf(buf, 8192, "KK %s\n", client_blob);
+ }
+
+ waiting = 1;
+
+ safe_free(client_blob);
+ helperStatefulSubmit(ntlmauthenticators, buf, AuthNTLMUserRequest::HandleReply, r, authserver);
+}
+
+/**
+ * Atomic action: properly release the NTLM auth helpers which may have been reserved
+ * for this request connections use.
+ */
+void
+AuthNTLMUserRequest::releaseAuthServer()
+{
+ if (authserver) {
+ debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ } else
+ debugs(29, 6, HERE << "No NTLM auth server to release.");
+}
+
+void
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
+{
+ assert(conn != NULL);
+
+ debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
+
+ if (conn->auth_user_request == NULL) {
+ debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
+ return;
+ }
+
+ // unlock / un-reserve the helpers
+ releaseAuthServer();
+
+ /* unlock the connection based lock */
+ debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
+
+ conn->auth_user_request = NULL;
+}
+
+int
+AuthNTLMUserRequest::authenticated() const
+{
+ if (user()->credentials() == AuthUser::Ok) {
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
+ return 1;
+ }
+
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
+
+ return 0;
+}
+
+void
+AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
+{
+ const char *proxy_auth, *blob;
+
+ assert(this);
+
+ /* Check that we are in the client side, where we can generate
+ * auth challenges */
+
+ if (conn == NULL || !cbdataReferenceValid(conn)) {
+ user()->credentials(AuthUser::Failed);
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
+ return;
+ }
+
+ if (waiting) {
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
+ return;
+ }
+
+ if (server_blob) {
+ debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
+ return;
+ }
+
+ /* get header */
+ proxy_auth = aRequest->header.getStr(type);
+
+ /* locate second word */
+ blob = proxy_auth;
+
+ /* if proxy_auth is actually NULL, we'd better not manipulate it. */
+ if (blob) {
+ while (xisspace(*blob) && *blob)
+ blob++;
+
+ while (!xisspace(*blob) && *blob)
+ blob++;
+
+ while (xisspace(*blob) && *blob)
+ blob++;
+ }
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ /* we've received a ntlm request. pass to a helper */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
+ user()->credentials(AuthUser::Pending);
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ assert(conn->auth_user_request == NULL);
+ conn->auth_user_request = this;
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Pending:
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
+ break;
+
+ case AuthUser::Handshake:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
+ client_blob = xstrdup (blob);
+
+ if (request)
+ HTTPMSGUNLOCK(request);
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Ok:
+ fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
+ break;
+
+ case AuthUser::Failed:
+ /* we've failed somewhere in authentication */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
+ break;
+ }
+}
+
+void
+AuthNTLMUserRequest::HandleReply(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+ int valid;
+ char *blob;
+
+ debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+ valid = cbdataReferenceValid(r->data);
+
+ if (!valid) {
+ debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'.");
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ return;
+ }
+
+ if (!reply) {
+ debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
+ reply = (char *)"BH Internal error";
+ }
+
+ AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+
+ AuthNTLMUserRequest *ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request.getRaw());
+ assert(ntlm_request != NULL);
+ assert(ntlm_request->waiting);
+ assert(ntlm_request->user() != NULL);
+ assert(ntlm_request->user()->auth_type == AUTH_NTLM);
+
+ ntlm_request->waiting = 0;
+ safe_free(ntlm_request->client_blob);
+
+ if (ntlm_request->authserver == NULL)
+ ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(ntlm_request->authserver == lastserver);
+
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
+ if (blob)
+ blob++;
+
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ /* we have been given a blob to send to the client */
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->request->flags.must_keepalive = 1;
+ if (ntlm_request->request->flags.proxy_keepalive) {
+ ntlm_request->server_blob = xstrdup(blob);
+ ntlm_request->user()->credentials(AuthUser::Handshake);
+ auth_user_request->denyMessage("Authentication in progress");
+ debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
+ } else {
+ ntlm_request->user()->credentials(AuthUser::Failed);
+ auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+ }
+ } else if (strncasecmp(reply, "AF ", 3) == 0) {
+ /* we're finished, release the helper */
+ auth_user_request->user()->username(blob);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(ntlm_request->server_blob);
+
+ debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
+ /* connection is authenticated */
+ debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << auth_user_request->user()->username());
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
+ AuthUser::Pointer local_auth_user = ntlm_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), auth_user_request->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);
+ local_auth_user = usernamehash->user();
+ ntlm_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ local_auth_user->expiretime = current_time.tv_sec;
+ ntlm_request->releaseAuthServer();
+ local_auth_user->credentials(AuthUser::Ok);
+ } else if (strncasecmp(reply, "NA ", 3) == 0) {
+ /* authentication failure (wrong password, etc.) */
+ auth_user_request->denyMessage(blob);
+ ntlm_request->user()->credentials(AuthUser::Failed);
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->releaseAuthServer();
+ debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
+ } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ /* TODO kick off a refresh process. This can occur after a YR or after
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate NTLM start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
+ * Needing YR. */
+ auth_user_request->denyMessage(blob);
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->releaseAuthServer();
+ debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
+ } else {
+ /* protocol error */
+ fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ if (ntlm_request->request) {
+ HTTPMSGUNLOCK(ntlm_request->request);
+ ntlm_request->request = NULL;
+ }
+ r->handler(r->data, NULL);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+}
--- /dev/null
+#ifndef _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+class AuthNTLMUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthNTLMUserRequest);
+
+ AuthNTLMUserRequest();
+ virtual ~AuthNTLMUserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void onConnectionClose(ConnStateData *);
+ virtual void module_start(RH *, void *);
+
+ virtual const char * connLastHeader();
+
+ /* we need to store the helper server between requests */
+ helper_stateful_server *authserver;
+ void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
+
+ /* what connection is this associated with */
+// ConnStateData * conn;
+
+ /* our current blob to pass to the client */
+ char *server_blob;
+
+ /* our current blob to pass to the server */
+ char *client_blob;
+
+ /* currently waiting for helper response */
+ unsigned char waiting;
+
+ /* need access to the request flags to mess around on pconn failure */
+ HttpRequest *request;
+
+private:
+ static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NTLM_USERREQUEST_H */
int err_count = 0;
CacheManager *manager=CacheManager::GetInstance();
+ debugs(5, 4, HERE);
+
configFreeMemory();
ACLMethodData::ThePurgeCount = 0;
}
static void
-parse_authparam(authConfig * config)
+parse_authparam(Auth::authConfig * config)
{
char *type_str;
char *param_str;
if ((param_str = strtok(NULL, w_space)) == NULL)
self_destruct();
- /* find a configuration for the scheme */
- AuthConfig *scheme = AuthConfig::Find (type_str);
+ /* find a configuration for the scheme in the currently parsed configs... */
+ AuthConfig *schemeCfg = AuthConfig::Find(type_str);
- if (scheme == NULL) {
- /* Create a configuration */
- AuthScheme *theScheme;
+ if (schemeCfg == NULL) {
+ /* Create a configuration based on the scheme info */
+ AuthScheme::Pointer theScheme = AuthScheme::Find(type_str);
- if ((theScheme = AuthScheme::Find(type_str)) == NULL) {
- debugs(3, 0, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
- return;
+ if (theScheme == NULL) {
+ debugs(3, DBG_CRITICAL, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
+ self_destruct();
}
config->push_back(theScheme->createConfig());
- scheme = config->back();
- assert (scheme);
+ schemeCfg = AuthConfig::Find(type_str);
+ if (schemeCfg == NULL) {
+ debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
+ self_destruct();
+ }
}
- scheme->parse(scheme, config->size(), param_str);
+ schemeCfg->parse(schemeCfg, config->size(), param_str);
}
static void
-free_authparam(authConfig * cfg)
+free_authparam(Auth::authConfig * cfg)
{
- AuthConfig *scheme;
- /* DON'T FREE THESE FOR RECONFIGURE */
-
- if (reconfiguring)
- return;
+ /* Wipe the Auth globals and Detach/Destruct component config + state. */
+ cfg->clean();
+ /* remove our pointers to the probably-dead sub-configs */
while (cfg->size()) {
- scheme = cfg->pop_back();
- scheme->done();
+ cfg->pop_back();
+ }
+
+ /* on reconfigure initialize new auth schemes for the new config. */
+ if(reconfiguring) {
+ InitAuthSchemes();
}
}
NAME: auth_param
TYPE: authparam
-LOC: Config.authConfiguration
+LOC: Auth::TheConfig
DEFAULT: none
DOC_START
This is used to define parameters for the various authentication
"static void\n"
"dump_config(StoreEntry *entry)\n"
"{\n"
+ " debugs(5, 4, HERE);\n"
);
for (entry = head; entry != NULL; entry = entry->next) {
"static void\n"
"free_all(void)\n"
"{\n"
+ " debugs(5, 4, HERE);\n"
);
for (entry = head; entry != NULL; entry = entry->next) {
aLogEntry->cache.requestSize += request->content_length;
aLogEntry->cache.extuser = request->extacl_user.termedBuf();
- if (request->auth_user_request) {
+ if (request->auth_user_request != NULL) {
if (request->auth_user_request->username())
- aLogEntry->cache.authuser =
- xstrdup(request->auth_user_request->username());
+ aLogEntry->cache.authuser = xstrdup(request->auth_user_request->username());
- AUTHUSERREQUESTUNLOCK(request->auth_user_request, "request via clientPrepareLogWithRequestDetails");
+// WTF?? request->auth_user_request = NULL;
}
}
if (!flags.swanSang)
debugs(33, 1, "BUG: ConnStateData was not destroyed properly; FD " << fd);
- AUTHUSERREQUESTUNLOCK(auth_user_request, "~conn");
-
cbdataReferenceDone(port);
if (bodyPipe != NULL)
#ifndef SQUID_CLIENTSIDE_H
#define SQUID_CLIENTSIDE_H
+#include "auth/UserRequest.h"
#include "base/AsyncJob.h"
#include "BodyPipe.h"
#include "comm.h"
#include "StoreIOBuffer.h"
class ConnStateData;
-
class ClientHttpRequest;
-
class clientStreamNode;
-
-class AuthUserRequest;
-
class ChunkedCodingParser;
class HttpParser;
int64_t bodySizeLeft();
/**
- * Is this connection based authentication? if so what type it
- * is.
- */
- auth_type_t auth_type;
-
- /**
- * note this is ONLY connection based because NTLM is against HTTP spec.
+ * note this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
* the user details for connection based authentication
*/
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
/**
* used by the owner of the connection, opaque otherwise
clientReplyContext::setReplyToError(
err_type err, http_status status, const HttpRequestMethod& method, char const *uri,
Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest,
- AuthUserRequest * auth_user_request)
+ AuthUserRequest::Pointer auth_user_request)
{
- ErrorState *errstate =
- clientBuildError(err, status, uri, addr, failedrequest);
+ ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
if (unparsedrequest)
errstate->request_hdrs = xstrdup(unparsedrequest);
createStoreEntry(method, request_flags());
- if (auth_user_request) {
- errstate->auth_user_request = auth_user_request;
- AUTHUSERREQUESTLOCK(errstate->auth_user_request, "errstate");
- }
+ errstate->auth_user_request = auth_user_request;
assert(errstate->callback_data == NULL);
errorAppendEntry(http->storeEntry(), errstate);
* responses
*/
authenticateFixHeader(reply, request->auth_user_request, request, 0, 1);
- } else if (request->auth_user_request)
- authenticateFixHeader(reply, request->auth_user_request, request,
- http->flags.accel, 0);
+ } else if (request->auth_user_request != NULL)
+ authenticateFixHeader(reply, request->auth_user_request, request, http->flags.accel, 0);
/* Append X-Cache */
httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
int storeOKTransferDone() const;
int storeNotOKTransferDone() const;
- void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *, AuthUserRequest *);
+ void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *, AuthUserRequest::Pointer);
void createStoreEntry(const HttpRequestMethod& m, request_flags flags);
void removeStoreReference(store_client ** scp, StoreEntry ** ep);
void removeClientStoreReference(store_client **scp, ClientHttpRequest *http);
http->getConn() != NULL ? http->getConn()->peer : tmpnoaddr,
http->request,
NULL,
- http->getConn() != NULL && http->getConn()->auth_user_request ?
+ http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
http->getConn()->auth_user_request : http->request->auth_user_request);
node = (clientStreamNode *)http->client_stream.tail->data;
new_request->my_addr = old_request->my_addr;
new_request->flags = old_request->flags;
new_request->flags.redirected = 1;
-
- if (old_request->auth_user_request) {
- new_request->auth_user_request = old_request->auth_user_request;
- AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
- }
+ new_request->auth_user_request = old_request->auth_user_request;
if (old_request->body_pipe != NULL) {
new_request->body_pipe = old_request->body_pipe;
repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
request->method, NULL,
(c != NULL ? c->peer : noAddr), request, NULL,
- (c != NULL && c->auth_user_request ?
+ (c != NULL && c->auth_user_request != NULL ?
c->auth_user_request : request->auth_user_request));
node = (clientStreamNode *)client_stream.tail->data;
STREAM_FAILED
} clientStream_status_t;
-typedef enum {
- AUTH_ACL_CHALLENGE = -2,
- AUTH_ACL_HELPER = -1,
- AUTH_ACL_CANNOT_AUTHENTICATE = 0,
- AUTH_AUTHENTICATED = 1
-} auth_acl_t;
-
-typedef enum {
- AUTH_UNKNOWN, /* default */
- AUTH_BASIC,
- AUTH_NTLM,
- AUTH_DIGEST,
- AUTH_NEGOTIATE,
- AUTH_BROKEN /* known type, but broken data */
-} auth_type_t;
-
/* stateful helper callback response codes */
typedef enum {
S_HELPER_UNKNOWN,
wordlistDestroy(&err->ftp.server_msg);
safe_free(err->ftp.request);
safe_free(err->ftp.reply);
- AUTHUSERREQUESTUNLOCK(err->auth_user_request, "errstate");
+ err->auth_user_request = NULL;
safe_free(err->err_msg);
#if USE_ERR_LOCALES
if (err->err_language != Config.errorDefaultLanguage)
switch (token) {
case 'a':
- if (request && request->auth_user_request)
+ if (request && request->auth_user_request != NULL)
p = request->auth_user_request->username();
if (!p)
p = "-";
#define SQUID_ERRORPAGE_H
#include "squid.h"
+#include "auth/UserRequest.h"
#include "cbdata.h"
#include "ip/Address.h"
\endverbatim
*/
-class AuthUserRequest;
class HttpReply;
class MemBuf;
int page_id;
char *err_language;
http_status httpStatus;
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
HttpRequest *request;
char *url;
int xerrno;
dlink_list queue;
+ /**
+ * Configuration flag. May only be altered by the configuration parser.
+ *
+ * Indicates that all uses of this external_acl_type helper require authentication
+ * details to be processed. If none are available its a fail match.
+ */
bool require_auth;
enum {
if (acl->def->require_auth) {
int ti;
/* Make sure the user is authenticated */
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
if ((ti = AuthenticateAcl(ch)) != 1) {
debugs(82, 2, "aclMatchExternal: " << acl->def->name << " user not authenticated (" << ti << ")");
return ti;
}
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
}
key = makeExternalAclKey(ch, acl);
- if (acl->def->require_auth)
- AUTHUSERREQUESTUNLOCK(ch->auth_user_request, "ACLChecklist via aclMatchExternal");
-
if (!key) {
/* Not sufficient data to process */
return -1;
switch (format->type) {
case _external_acl_format::EXT_ACL_LOGIN:
- assert (ch->auth_user_request);
+ assert (ch->auth_user_request != NULL);
str = ch->auth_user_request->username();
break;
#if USE_IDENT
if (acl->def->require_auth) {
int ti;
/* Make sure the user is authenticated */
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
if ((ti = AuthenticateAcl(ch)) != 1) {
debugs(82, 1, "externalAclLookup: " << acl->def->name <<
callback(callback_data, NULL);
return;
}
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
}
const char *key = makeExternalAclKey(ch, acl);
if (orig_request->extacl_user.size())
username = orig_request->extacl_user.termedBuf();
- else if (orig_request->auth_user_request)
+ else if (orig_request->auth_user_request != NULL)
username = orig_request->auth_user_request->username();
snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
WIN32_svcstatusupdate(SERVICE_STOP_PENDING, (wait + 1) * 1000);
#endif
+ /* run the closure code which can be shared with reconfigure */
serverConnectionsClose();
+
+ /* detach the auth components (only do this on full shutdown) */
+ AuthScheme::FreeAll();
+
eventAdd("SquidShutdown", &StopEventLoop, this, (double) (wait + 1), 1, false);
}
#endif
redirectShutdown();
- authenticateShutdown();
+ authenticateReset();
externalAclShutdown();
storeDirCloseSwapLogs();
storeLogClose();
setEffectiveUser();
_db_init(Debug::cache_log, Debug::debugOptions);
ipcache_restart(); /* clear stuck entries */
- authenticateUserCacheRestart(); /* clear stuck ACL entries */
fqdncache_restart(); /* sigh, fqdncache too */
parseEtcHosts();
errorInitialize(); /* reload error pages */
#endif
redirectInit();
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
externalAclInit();
#if USE_WCCP
dnsShutdown();
#endif
redirectShutdown();
- authenticateShutdown();
+ authenticateRotate();
externalAclShutdown();
_db_rotate_log(); /* cache.log */
dnsInit();
#endif
redirectInit();
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
externalAclInit();
}
redirectInit();
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
externalAclInit();
/* we may want the parsing process to set this up in the future */
Store::Root(new StoreController);
+ InitAuthSchemes(); /* required for config parsing */
+
parse_err = parseConfigFile(ConfigFile);
Mem::Report();
DelayPools::FreePools();
#endif
- authenticateShutdown();
+ authenticateReset();
#if USE_WIN32_SERVICE
WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000);
if (n_userhash_peers == 0)
return NULL;
- if (request->auth_user_request)
+ if (request->auth_user_request != NULL)
key = request->auth_user_request->username();
if (!key)
/* upstream proxy authentication */
SQUIDCEXTERN char *peer_proxy_negotiate_auth(char *principal_name, char *proxy);
#endif
+
+/* call to ensure the auth component schemes exist. */
+SQUIDCEXTERN void InitAuthSchemes(void);
+
#endif /* SQUID_PROTOS_H */
r->client_addr.SetNoAddr();
r->client_ident = NULL;
- if (http->request->auth_user_request)
+ if (http->request->auth_user_request != NULL)
r->client_ident = http->request->auth_user_request->username();
else if (http->request->extacl_user.defined()) {
r->client_ident = http->request->extacl_user.termedBuf();
manager->registerAction("active_requests",
"Client-side Active Requests",
statClientRequests, 0, 1);
+ manager->registerAction("username_cache",
+ "Active Cached Usernames",
+ AuthUser::UsernameCacheStats, 0, 1);
#if DEBUG_OPENFD
manager->registerAction("openfd_objects", "Objects with Swapout files open",
statOpenfdObj, 0, 0);
(int) http->start_time.tv_usec,
tvSubDsec(http->start_time, current_time));
- if (http->request->auth_user_request)
+ if (http->request->auth_user_request != NULL)
p = http->request->auth_user_request->username();
else if (http->request->extacl_user.defined()) {
p = http->request->extacl_user.termedBuf();
#endif
} accessList;
acl_deny_info_list *denyInfoList;
- authConfig authConfiguration;
struct {
size_t list_width;
AuthConfig *
getConfig(char const *type_str)
{
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
/* find a configuration for the scheme */
- AuthConfig *scheme = AuthConfig::Find (type_str);
+ AuthConfig *scheme = AuthConfig::Find(type_str);
if (scheme == NULL) {
/* Create a configuration */
- AuthScheme *theScheme;
+ AuthScheme::Pointer theScheme = AuthScheme::Find(type_str);
- if ((theScheme = AuthScheme::Find(type_str)) == NULL) {
+ if (theScheme == NULL) {
return NULL;
//fatalf("Unknown authentication scheme '%s'.\n", type_str);
}
config.push_back(theScheme->createConfig());
scheme = config.back();
- assert (scheme);
+ assert(scheme);
}
return scheme;
void
setup_scheme(AuthConfig *scheme, char const **params, unsigned param_count)
{
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
for (unsigned position=0; position < param_count; position++) {
char *param_str=xstrdup(params[position]);
Mem::Init();
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
char const *digest_parms[]= {"program /home/robertc/install/squid/libexec/digest_pw_auth /home/robertc/install/squid/etc/digest.pwd",
"realm foo"
Debug::Levels[29]=9;
fake_auth_setup();
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i) {
- AuthUserRequest *authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
+ for (AuthScheme::iterator i = AuthScheme::GetSchemes().begin(); i != AuthScheme::GetSchemes().end(); ++i) {
+ AuthUserRequest::Pointer authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
CPPUNIT_ASSERT(authRequest != NULL);
}
}
Debug::Levels[29]=9;
fake_auth_setup();
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i) {
+ for (AuthScheme::iterator i = AuthScheme::GetSchemes().begin(); i != AuthScheme::GetSchemes().end(); ++i) {
// create a user request
// check its scheme matches *i
- AuthUserRequest *authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
+ AuthUserRequest::Pointer authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
CPPUNIT_ASSERT_EQUAL(authRequest->scheme(), *i);
}
}
#if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
#include "auth/basic/auth_basic.h"
/* AuthBasicUserRequest::AuthBasicUserRequest works
*/
void
testAuthBasicUserRequest::username()
{
- AuthBasicUserRequest();
- AuthBasicUserRequest *temp=new AuthBasicUserRequest();
+ AuthUserRequest::Pointer temp = new AuthBasicUserRequest();
BasicUser *basic_auth=new BasicUser(AuthConfig::Find("basic"));
basic_auth->username("John");
temp->user(basic_auth);
- basic_auth->addRequest(temp);
CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
}
#endif /* HAVE_AUTH_MODULE_BASIC */
void
testAuthDigestUserRequest::username()
{
- AuthDigestUserRequest();
- AuthDigestUserRequest *temp=new AuthDigestUserRequest();
- DigestUser *user=new DigestUser(AuthConfig::Find("digest"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthDigestUserRequest();
+ DigestUser *duser=new DigestUser(AuthConfig::Find("digest"));
+ duser->username("John");
+ temp->user(duser);
CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
}
#endif /* HAVE_AUTH_MODULE_DIGEST */
void
testAuthNTLMUserRequest::username()
{
- AuthNTLMUserRequest();
- AuthNTLMUserRequest *temp=new AuthNTLMUserRequest();
- NTLMUser *user=new NTLMUser(AuthConfig::Find("ntlm"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthNTLMUserRequest();
+ NTLMUser *nuser=new NTLMUser(AuthConfig::Find("ntlm"));
+ nuser->username("John");
+ temp->user(nuser);
CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
}
#endif /* HAVE_AUTH_MODULE_NTLM */
void
testAuthNegotiateUserRequest::username()
{
- AuthNegotiateUserRequest();
- AuthNegotiateUserRequest *temp=new AuthNegotiateUserRequest();
- NegotiateUser *user=new NegotiateUser(AuthConfig::Find("negotiate"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthNegotiateUserRequest();
+ NegotiateUser *nuser=new NegotiateUser(AuthConfig::Find("negotiate"));
+ nuser->username("John");
+ temp->user(nuser);
CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
}
#endif /* HAVE_AUTH_MODULE_NEGOTIATE */
};
#if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
class testAuthBasicUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthBasicUserRequest );
#endif
#if HAVE_AUTH_MODULE_DIGEST
+#include "auth/digest/digestUserRequest.h"
class testAuthDigestUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthDigestUserRequest );
#endif
#if HAVE_AUTH_MODULE_NTLM
+#include "auth/ntlm/ntlmUserRequest.h"
class testAuthNTLMUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthNTLMUserRequest );
#endif
#if HAVE_AUTH_MODULE_NEGOTIATE
+#include "auth/negotiate/negotiateUserRequest.h"
class testAuthNegotiateUserRequest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( testAuthNegotiateUserRequest );
/// \deprecated Use AuthUserHashPointer instead.
typedef struct AuthUserHashPointer auth_user_hash_pointer;
-/// \ingroup AuthAPI
-/// \deprecated Use AuthUserIP instead.
-typedef struct AuthUserIP auth_user_ip_t;
-
/* temporary: once Config is fully hidden, this shouldn't be needed */
#include "Array.h"
mem_hdr_test_LDADD = $(top_builddir)/src/stmem.o \
$(top_builddir)/src/mem_node.o $(LDADD)
MemPoolTest_SOURCES = MemPoolTest.cc
-refcount_SOURCES = refcount.cc
+refcount_SOURCES = refcount.cc $(DEBUG_SOURCE)
splay_SOURCES = splay.cc