From: Christos Tsantilas Date: Fri, 6 Dec 2013 14:59:47 +0000 (+0200) Subject: Add key_extras to proxy authentication schemes. X-Git-Tag: SQUID_3_5_0_1~473 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d4806c91642a65454c9dc6f65e7ed03e50a76313;p=thirdparty%2Fsquid.git Add key_extras to proxy authentication schemes. The key_extras value is a "quoted string" with logformat %macro support. It is appended to request line for the authentication helper. Example usage: auth_param basic key_extras "thePort:%>lp" auth_param digest key_extras "LocalIP=%>la:%lp" This is a Measurement Factory project. --- diff --git a/src/AccessLogEntry.h b/src/AccessLogEntry.h index b302c63bb8..73da24e3ea 100644 --- a/src/AccessLogEntry.h +++ b/src/AccessLogEntry.h @@ -169,7 +169,7 @@ public: ssluser(NULL), #endif port(NULL) { - ; + caddr.setNoAddr(); } Ip::Address caddr; diff --git a/src/FwdState.cc b/src/FwdState.cc index 82fb20f04d..05d8e82ca9 100644 --- a/src/FwdState.cc +++ b/src/FwdState.cc @@ -163,7 +163,7 @@ void FwdState::start(Pointer aSelf) #endif // do full route options selection - peerSelect(&serverDestinations, request, entry, fwdPeerSelectionCompleteWrapper, this); + peerSelect(&serverDestinations, request, al, entry, fwdPeerSelectionCompleteWrapper, this); } #if STRICT_ORIGINAL_DST diff --git a/src/PeerSelectState.h b/src/PeerSelectState.h index 84634404a7..f58885edec 100644 --- a/src/PeerSelectState.h +++ b/src/PeerSelectState.h @@ -33,6 +33,7 @@ #ifndef SQUID_PEERSELECTSTATE_H #define SQUID_PEERSELECTSTATE_H +#include "AccessLogEntry.h" #include "acl/Checklist.h" #include "base/Vector.h" #include "cbdata.h" @@ -47,7 +48,7 @@ class ErrorState; typedef void PSC(Comm::ConnectionList *, ErrorState *, void *); -void peerSelect(Comm::ConnectionList *, HttpRequest *, StoreEntry *, PSC *, void *data); +void peerSelect(Comm::ConnectionList *, HttpRequest *, AccessLogEntry::Pointer const&, StoreEntry *, PSC *, void *data); void peerSelectInit(void); /** @@ -79,6 +80,7 @@ public: const char * url() const; HttpRequest *request; + AccessLogEntry::Pointer al; ///< info for the future access.log entry StoreEntry *entry; allow_t always_direct; allow_t never_direct; diff --git a/src/acl/FilledChecklist.h b/src/acl/FilledChecklist.h index 05131181ac..23b69b4f9a 100644 --- a/src/acl/FilledChecklist.h +++ b/src/acl/FilledChecklist.h @@ -1,6 +1,7 @@ #ifndef SQUID_ACLFILLED_CHECKLIST_H #define SQUID_ACLFILLED_CHECKLIST_H +#include "AccessLogEntry.h" #include "acl/Checklist.h" #include "acl/forward.h" #include "ip/Address.h" @@ -75,6 +76,8 @@ public: Ssl::X509_Pointer serverCert; #endif + AccessLogEntry::Pointer al; ///< info for the future access.log entry + ExternalACLEntry *extacl_entry; private: diff --git a/src/adaptation/AccessCheck.cc b/src/adaptation/AccessCheck.cc index fdda1c2336..10cd0fb917 100644 --- a/src/adaptation/AccessCheck.cc +++ b/src/adaptation/AccessCheck.cc @@ -126,6 +126,7 @@ Adaptation::AccessCheck::checkCandidates() acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str); if ((acl_checklist->reply = filter.reply)) HTTPMSGLOCK(acl_checklist->reply); + acl_checklist->al = filter.al; acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this); return; } diff --git a/src/auth/Acl.cc b/src/auth/Acl.cc index 5a6736e3d6..cde58e641e 100644 --- a/src/auth/Acl.cc +++ b/src/auth/Acl.cc @@ -46,7 +46,7 @@ AuthenticateAcl(ACLChecklist *ch) /* Note: this fills in auth_user_request when applicable */ const AuthAclState result = Auth::UserRequest::tryToAuthenticateAndSetAuthUser( &checklist->auth_user_request, headertype, request, - checklist->conn(), checklist->src_addr); + checklist->conn(), checklist->src_addr, checklist->al); switch (result) { case AUTH_ACL_CANNOT_AUTHENTICATE: diff --git a/src/auth/AclProxyAuth.cc b/src/auth/AclProxyAuth.cc index 0c05e5270b..91e2bc1647 100644 --- a/src/auth/AclProxyAuth.cc +++ b/src/auth/AclProxyAuth.cc @@ -145,7 +145,7 @@ ProxyAuthLookup::checkForAsync(ACLChecklist *cl) const /* make sure someone created auth_user_request for us */ assert(checklist->auth_user_request != NULL); assert(checklist->auth_user_request->valid()); - checklist->auth_user_request->start(LookupDone, checklist); + checklist->auth_user_request->start(checklist->request, checklist->al, LookupDone, checklist); } void diff --git a/src/auth/Config.cc b/src/auth/Config.cc index d396b42704..dba69a7db6 100644 --- a/src/auth/Config.cc +++ b/src/auth/Config.cc @@ -33,8 +33,12 @@ #include "squid.h" #include "auth/Config.h" #include "auth/UserRequest.h" +#include "cache_cf.h" +#include "ConfigParser.h" #include "Debug.h" +#include "format/Format.h" #include "globals.h" +#include "Store.h" Auth::ConfigVector Auth::TheConfig; @@ -46,7 +50,7 @@ Auth::ConfigVector Auth::TheConfig; * It may also be NULL reflecting that no user could be created. */ Auth::UserRequest::Pointer -Auth::Config::CreateAuthUser(const char *proxy_auth) +Auth::Config::CreateAuthUser(const char *proxy_auth, AccessLogEntry::Pointer &al) { assert(proxy_auth != NULL); debugs(29, 9, HERE << "header = '" << proxy_auth << "'"); @@ -58,8 +62,17 @@ Auth::Config::CreateAuthUser(const char *proxy_auth) "Unsupported or unconfigured/inactive proxy-auth scheme, '" << proxy_auth << "'"); return NULL; } + static MemBuf rmb; + rmb.reset(); + if (config->keyExtras) { + // %credentials and %username, which normally included in + // request_format, are - at this time, but that is OK + // because user name is added to key explicitly, and we do + // not want to store authenticated credentials at all. + config->keyExtras->assemble(rmb, al, 0); + } - return config->decode(proxy_auth); + return config->decode(proxy_auth, rmb.hasContent() ? rmb.content() : NULL); } Auth::Config * @@ -76,3 +89,43 @@ Auth::Config::Find(const char *proxy_auth) void Auth::Config::registerWithCacheManager(void) {} + +void +Auth::Config::parse(Auth::Config * scheme, int n_configured, char *param_str) +{ + if (strcmp(param_str, "key_extras") == 0) { + keyExtrasLine = ConfigParser::NextQuotedToken(); + Format::Format *nlf = new ::Format::Format(scheme->type()); + if (!nlf->parse(keyExtrasLine.termedBuf())) { + debugs(29, DBG_CRITICAL, "FATAL: Failed parsing key_extras formatting value"); + self_destruct(); + return; + } + if (keyExtras) + delete keyExtras; + + keyExtras = nlf; + + if (char *t = strtok(NULL, w_space)) { + debugs(29, DBG_CRITICAL, "FATAL: Unexpected argument '" << t << "' after request_format specification"); + self_destruct(); + } + } else { + debugs(29, DBG_CRITICAL, "Unrecognised " << scheme->type() << " auth scheme parameter '" << param_str << "'"); + } +} + +void +Auth::Config::dump(StoreEntry *entry, const char *name, Auth::Config *scheme) +{ + if (keyExtrasLine.size() > 0) + storeAppendPrintf(entry, "%s %s key_extras \"%s\"\n", name, scheme->type(), keyExtrasLine.termedBuf()); +} + +void +Auth::Config::done() +{ + delete keyExtras; + keyExtras = NULL; + keyExtrasLine.clean(); +} diff --git a/src/auth/Config.h b/src/auth/Config.h index ec29ed06dc..6f1c981a41 100644 --- a/src/auth/Config.h +++ b/src/auth/Config.h @@ -32,6 +32,7 @@ #if USE_AUTH +#include "AccessLogEntry.h" #include "auth/UserRequest.h" #include "HelperChildConfig.h" @@ -43,6 +44,11 @@ class wordlist; /* for http_hdr_type parameters-by-value */ #include "HttpHeader.h" +namespace Format +{ + class Format; +} + namespace Auth { @@ -61,10 +67,10 @@ class Config { public: - static UserRequest::Pointer CreateAuthUser(const char *proxy_auth); + static UserRequest::Pointer CreateAuthUser(const char *proxy_auth, AccessLogEntry::Pointer &al); static Config *Find(const char *proxy_auth); - Config() : authenticateChildren(20), authenticateProgram(NULL) {} + Config() : authenticateChildren(20), authenticateProgram(NULL), keyExtras(NULL) {} virtual ~Config() {} @@ -86,7 +92,7 @@ public: \param proxy_auth Login Pattern to parse. \retval * Details needed to authenticate. */ - virtual UserRequest::Pointer decode(char const *proxy_auth) = 0; + virtual UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm) = 0; /** * squid is finished with this config, release any unneeded resources. @@ -95,7 +101,7 @@ public: * \todo we need a 'done for reconfigure' and a 'done permanently' concept. */ - virtual void done() = 0; + virtual void done(); /** * The configured function is used to see if the auth module has been given valid @@ -117,7 +123,7 @@ public: * 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 *, Config *) = 0; + virtual void dump(StoreEntry *, const char *, Config *); /** add headers as needed when challenging for auth */ virtual void fixHeader(UserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *) = 0; @@ -129,7 +135,7 @@ public: virtual void registerWithCacheManager(void); /** parse config options */ - virtual void parse(Config *, int, char *) = 0; + virtual void parse(Config *, int, char *); /** the http string id */ virtual const char * type() const = 0; @@ -137,6 +143,8 @@ public: public: HelperChildConfig authenticateChildren; wordlist *authenticateProgram; ///< Helper program to run, includes all parameters + String keyExtrasLine; ///< The format of the request to the auth helper + Format::Format *keyExtras; ///< The compiled request format }; typedef Vector ConfigVector; diff --git a/src/auth/Gadgets.cc b/src/auth/Gadgets.cc index 8a025dc5a8..f31c9049cd 100644 --- a/src/auth/Gadgets.cc +++ b/src/auth/Gadgets.cc @@ -139,7 +139,7 @@ authenticateReset(void) AuthUserHashPointer::AuthUserHashPointer(Auth::User::Pointer anAuth_user): auth_user(anAuth_user) { - key = (void *)anAuth_user->username(); + key = (void *)anAuth_user->userKey(); next = NULL; hash_join(proxy_auth_username_cache, (hash_link *) this); } diff --git a/src/auth/User.cc b/src/auth/User.cc index d0938dd576..739cbc2e24 100644 --- a/src/auth/User.cc +++ b/src/auth/User.cc @@ -49,14 +49,15 @@ CBDATA_TYPE(AuthUserIP); time_t Auth::User::last_discard = 0; -Auth::User::User(Auth::Config *aConfig) : +Auth::User::User(Auth::Config *aConfig, const char *aRequestRealm) : auth_type(Auth::AUTH_UNKNOWN), config(aConfig), ipcount(0), expiretime(0), notes(), credentials_state(Auth::Unchecked), - username_(NULL) + username_(NULL), + requestRealm_(aRequestRealm) { proxy_auth_list.head = proxy_auth_list.tail = NULL; proxy_match_cache.head = proxy_match_cache.tail = NULL; @@ -338,6 +339,14 @@ Auth::User::addIp(Ip::Address ipaddr) debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")"); } +SBuf +Auth::User::BuildUserKey(const char *username, const char *realm) +{ + SBuf key; + key.Printf("%s:%s", username, realm); + return key; +} + /** * Add the Auth::User structure to the username cache. */ @@ -390,6 +399,8 @@ Auth::User::username(char const *aString) if (aString) { assert(!username_); username_ = xstrdup(aString); + if (!requestRealm_.isEmpty()) + userKey_ = BuildUserKey(username_, requestRealm_.c_str()); } else { safe_free(username_); } diff --git a/src/auth/User.h b/src/auth/User.h index c6c9b1f19f..74de93cfd8 100644 --- a/src/auth/User.h +++ b/src/auth/User.h @@ -40,6 +40,7 @@ #include "dlink.h" #include "ip/Address.h" #include "Notes.h" +#include "SBuf.h" class AuthUserHashPointer; class StoreEntry; @@ -82,12 +83,15 @@ public: public: static void cacheInit(); static void CachedACLsReset(); + static SBuf BuildUserKey(const char *username, const char *realm); void absorb(Auth::User::Pointer from); virtual ~User(); char const *username() const { return username_; } void username(char const *); + const char *userKey() {return !userKey_.isEmpty() ? userKey_.c_str() : username_;} + /** * How long these credentials are still valid for. * Negative numbers means already expired. @@ -117,7 +121,7 @@ private: CredentialState credentials_state; protected: - User(Auth::Config *); + User(Auth::Config *, const char *requestRealm); private: /** @@ -133,6 +137,17 @@ private: */ const char *username_; + /** + * A realm for the user depending on request, designed to identify users, + * with the same username and different authentication domains. + */ + SBuf requestRealm_; + + /** + * A Unique key for the user, consist by username and requestRealm_ + */ + SBuf userKey_; + /** what ip addresses has this user been seen at?, plus a list length cache */ dlink_list ip_list; }; diff --git a/src/auth/UserRequest.cc b/src/auth/UserRequest.cc index d4bf5e6452..5315c7cb4b 100644 --- a/src/auth/UserRequest.cc +++ b/src/auth/UserRequest.cc @@ -43,6 +43,8 @@ #include "comm/Connection.h" #include "HttpReply.h" #include "HttpRequest.h" +#include "format/Format.h" +#include "MemBuf.h" /* Generic Functions */ @@ -59,12 +61,12 @@ Auth::UserRequest::username() const /* send the initial data to an authenticator module */ void -Auth::UserRequest::start(AUTHCB * handler, void *data) +Auth::UserRequest::start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) { assert(handler); assert(data); debugs(29, 9, HERE << "auth_user_request '" << this << "'"); - module_start(handler, data); + module_start(request, al, handler, data); } bool @@ -293,7 +295,7 @@ authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * con * Caller is responsible for locking and unlocking their *auth_user_request! */ AuthAclState -Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr) +Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr, AccessLogEntry::Pointer &al) { const char *proxy_auth; assert(headertype != 0); @@ -372,7 +374,7 @@ Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, /* beginning of a new request check */ debugs(29, 4, HERE << "No connection authentication type"); - *auth_user_request = Auth::Config::CreateAuthUser(proxy_auth); + *auth_user_request = Auth::Config::CreateAuthUser(proxy_auth, al); if (*auth_user_request == NULL) return AUTH_ACL_CHALLENGE; else if (!(*auth_user_request)->valid()) { @@ -455,7 +457,7 @@ Auth::UserRequest::authenticate(Auth::UserRequest::Pointer * auth_user_request, } AuthAclState -Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * aUR, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr) +Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * aUR, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr, AccessLogEntry::Pointer &al) { // If we have already been called, return the cached value Auth::UserRequest::Pointer t = authTryGetUser(*aUR, conn, request); @@ -471,7 +473,7 @@ Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * } // ok, call the actual authenticator routine. - AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr); + AuthAclState result = authenticate(aUR, headertype, request, conn, src_addr, al); // auth process may have changed the UserRequest we are dealing with t = authTryGetUser(*aUR, conn, request); @@ -564,3 +566,20 @@ Auth::UserRequest::scheme() const { return Auth::Scheme::Find(user()->config->type()); } + +const char * +Auth::UserRequest::helperRequestKeyExtras(HttpRequest *request, AccessLogEntry::Pointer &al) +{ + if (Format::Format *reqFmt = user()->config->keyExtras) { + static MemBuf mb; + mb.reset(); + // We should pass AccessLogEntry as second argument .... + Auth::UserRequest::Pointer oldReq = request->auth_user_request; + request->auth_user_request = this; + reqFmt->assemble(mb, al, 0); + request->auth_user_request = oldReq; + debugs(29, 5, "Assembled line to send :" << mb.content()); + return mb.content(); + } + return NULL; +} diff --git a/src/auth/UserRequest.h b/src/auth/UserRequest.h index 0bdebdfe4e..7ca884e24c 100644 --- a/src/auth/UserRequest.h +++ b/src/auth/UserRequest.h @@ -32,6 +32,7 @@ #if USE_AUTH +#include "AccessLogEntry.h" #include "auth/AuthAclState.h" #include "auth/Scheme.h" #include "auth/User.h" @@ -164,7 +165,7 @@ public: * \param handler Handler to process the callback when its run * \param data CBDATA for handler */ - virtual void module_start(AUTHCB *handler, void *data) = 0; + virtual void module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *handler, void *data) = 0; // User credentials object this UserRequest is managing virtual User::Pointer user() {return _auth_user;} @@ -189,12 +190,12 @@ public: * * \return Some AUTH_ACL_* state */ - static AuthAclState tryToAuthenticateAndSetAuthUser(UserRequest::Pointer *aUR, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &); + static AuthAclState tryToAuthenticateAndSetAuthUser(UserRequest::Pointer *aUR, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &); /// Add the appropriate [Proxy-]Authenticate header to the given reply static void addReplyAuthHeader(HttpReply * rep, UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal); - void start(AUTHCB *handler, void *data); + void start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *handler, void *data); char const * denyMessage(char const * const default_message = NULL); /** Possibly overrideable in future */ @@ -217,9 +218,15 @@ public: virtual const char * connLastHeader(); + /** + * The string representation of the credentials send by client + */ + virtual const char *credentialsStr() = 0; + + const char *helperRequestKeyExtras(HttpRequest *, AccessLogEntry::Pointer &al); private: - static AuthAclState authenticate(UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr); + static AuthAclState authenticate(UserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr, AccessLogEntry::Pointer &al); /** return a message on the 407 error pages */ char *message; diff --git a/src/auth/basic/User.cc b/src/auth/basic/User.cc index 66320d2241..273ee33f2f 100644 --- a/src/auth/basic/User.cc +++ b/src/auth/basic/User.cc @@ -5,8 +5,8 @@ #include "SquidConfig.h" #include "SquidTime.h" -Auth::Basic::User::User(Auth::Config *aConfig) : - Auth::User(aConfig), +Auth::Basic::User::User(Auth::Config *aConfig, const char *aRequestRealm) : + Auth::User(aConfig, aRequestRealm), passwd(NULL), queue(NULL), currentRequest(NULL) diff --git a/src/auth/basic/User.h b/src/auth/basic/User.h index 52ed43cfb5..1226fb736c 100644 --- a/src/auth/basic/User.h +++ b/src/auth/basic/User.h @@ -19,7 +19,7 @@ class User : public Auth::User public: MEMPROXY_CLASS(Auth::Basic::User); - User(Auth::Config *); + User(Auth::Config *, const char *requestRealm); ~User(); bool authenticated() const; bool valid() const; diff --git a/src/auth/basic/UserRequest.cc b/src/auth/basic/UserRequest.cc index ef9dc6fa81..a9f6305ec7 100644 --- a/src/auth/basic/UserRequest.cc +++ b/src/auth/basic/UserRequest.cc @@ -7,6 +7,10 @@ #include "charset.h" #include "Debug.h" #include "HelperReply.h" +#include "HttpMsg.h" +#include "HttpRequest.h" +#include "format/Format.h" +#include "MemBuf.h" #include "rfc1738.h" #include "SquidTime.h" @@ -25,6 +29,15 @@ Auth::Basic::UserRequest::authenticated() const return 0; } +const char * +Auth::Basic::UserRequest::credentialsStr() +{ + Auth::Basic::User const *basic_auth = dynamic_cast(user().getRaw()); + if (basic_auth) + return basic_auth->passwd; + return NULL; +} + /* log a basic user in */ void @@ -80,7 +93,7 @@ Auth::Basic::UserRequest::module_direction() /* send the initial data to a basic authenticator module */ void -Auth::Basic::UserRequest::module_start(AUTHCB * handler, void *data) +Auth::Basic::UserRequest::module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) { assert(user()->auth_type == Auth::AUTH_BASIC); Auth::Basic::User *basic_auth = dynamic_cast(user().getRaw()); @@ -120,7 +133,12 @@ Auth::Basic::UserRequest::module_start(AUTHCB * handler, void *data) xstrncpy(usern, rfc1738_escape(user()->username()), sizeof(usern)); xstrncpy(pass, rfc1738_escape(basic_auth->passwd), sizeof(pass)); } - int sz = snprintf(buf, sizeof(buf), "%s %s\n", usern, pass); + int sz = 0; + if (const char *keyExtras = helperRequestKeyExtras(request, al)) + sz = snprintf(buf, sizeof(buf), "%s %s %s\n", usern, pass, keyExtras); + else + sz = snprintf(buf, sizeof(buf), "%s %s\n", usern, pass); + if (sz<=0) { debugs(9, DBG_CRITICAL, "ERROR: Basic Authentication Failure. Can not build helper validation request."); handler(data); diff --git a/src/auth/basic/UserRequest.h b/src/auth/basic/UserRequest.h index 4dafb094f3..7fad9169c7 100644 --- a/src/auth/basic/UserRequest.h +++ b/src/auth/basic/UserRequest.h @@ -26,7 +26,8 @@ public: virtual int authenticated() const; virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type); virtual Auth::Direction module_direction(); - virtual void module_start(AUTHCB *, void *); + virtual void module_start(HttpRequest * request, AccessLogEntry::Pointer &al, AUTHCB *, void *); + virtual const char *credentialsStr(); private: static HLPCB HandleReply; diff --git a/src/auth/basic/auth_basic.cc b/src/auth/basic/auth_basic.cc index 7ece07ed63..8b47206a19 100644 --- a/src/auth/basic/auth_basic.cc +++ b/src/auth/basic/auth_basic.cc @@ -116,6 +116,8 @@ Auth::Basic::Config::rotateHelpers() void Auth::Basic::Config::done() { + Auth::Config::done(); + authbasic_initialised = 0; if (basicauthenticators) { @@ -149,6 +151,7 @@ Auth::Basic::Config::dump(StoreEntry * entry, const char *name, Auth::Config * s storeAppendPrintf(entry, "%s basic children %d startup=%d idle=%d concurrency=%d\n", name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency); storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL); storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off"); + Auth::Config::dump(entry, name, scheme); } Auth::Basic::Config::Config() : @@ -184,9 +187,8 @@ Auth::Basic::Config::parse(Auth::Config * scheme, int n_configured, char *param_ parse_onoff(&casesensitive); } else if (strcmp(param_str, "utf8") == 0) { parse_onoff(&utf8); - } else { - debugs(29, DBG_CRITICAL, HERE << "unrecognised basic auth scheme parameter '" << param_str << "'"); - } + } else + Auth::Config::parse(scheme, n_configured, param_str); } static void @@ -196,15 +198,15 @@ authenticateBasicStats(StoreEntry * sentry) } static Auth::User::Pointer -authBasicAuthUserFindUsername(const char *username) +authBasicAuthUserFindUsername(const char *userkey) { AuthUserHashPointer *usernamehash; - debugs(29, 9, HERE << "Looking for user '" << username << "'"); + debugs(29, 9, "Looking for user '" << userkey << "'"); - if (username && (usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, username)))) { + if (userkey && (usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, userkey)))) { while (usernamehash) { if ((usernamehash->user()->auth_type == Auth::AUTH_BASIC) && - !strcmp(username, (char const *)usernamehash->key)) + !strcmp(userkey, (char const *)usernamehash->key)) return usernamehash->user(); usernamehash = static_cast(usernamehash->next); @@ -257,7 +259,7 @@ Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader) * descriptive message to the user. */ Auth::UserRequest::Pointer -Auth::Basic::Config::decode(char const *proxy_auth) +Auth::Basic::Config::decode(char const *proxy_auth, const char *aRequestRealm) { Auth::UserRequest::Pointer auth_user_request = dynamic_cast(new Auth::Basic::UserRequest); /* decode the username */ @@ -275,18 +277,18 @@ Auth::Basic::Config::decode(char const *proxy_auth) char *seperator = strchr(cleartext, ':'); - lb = local_basic = new Auth::Basic::User(this); - if (seperator == NULL) { - local_basic->username(cleartext); - } else { + lb = local_basic = new Auth::Basic::User(this, aRequestRealm); + + if (seperator) { /* terminate the username */ *seperator = '\0'; - local_basic->username(cleartext); local_basic->passwd = xstrdup(seperator+1); } if (!casesensitive) - Tolower((char *)local_basic->username()); + Tolower(cleartext); + local_basic->username(cleartext); + if (local_basic->passwd == NULL) { debugs(29, 4, HERE << "no password in proxy authorization header '" << proxy_auth << "'"); @@ -310,7 +312,7 @@ Auth::Basic::Config::decode(char const *proxy_auth) /* now lookup and see if we have a matching auth_user structure in memory. */ Auth::User::Pointer auth_user; - if ((auth_user = authBasicAuthUserFindUsername(lb->username())) == NULL) { + if ((auth_user = authBasicAuthUserFindUsername(lb->userKey())) == NULL) { /* the user doesn't exist in the username cache yet */ /* save the credentials */ debugs(29, 9, HERE << "Creating new user '" << lb->username() << "'"); diff --git a/src/auth/basic/auth_basic.h b/src/auth/basic/auth_basic.h index 0c2e6a4b87..e15fe26747 100644 --- a/src/auth/basic/auth_basic.h +++ b/src/auth/basic/auth_basic.h @@ -26,7 +26,7 @@ public: ~Config(); virtual bool active() const; virtual bool configured() const; - virtual Auth::UserRequest::Pointer decode(char const *proxy_auth); + virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm); virtual void done(); virtual void rotateHelpers(); virtual void dump(StoreEntry *, const char *, Auth::Config *); diff --git a/src/auth/digest/User.cc b/src/auth/digest/User.cc index 1cf7dd0ba5..ecb2b19888 100644 --- a/src/auth/digest/User.cc +++ b/src/auth/digest/User.cc @@ -6,8 +6,8 @@ #include "SquidConfig.h" #include "SquidTime.h" -Auth::Digest::User::User(Auth::Config *aConfig) : - Auth::User(aConfig), +Auth::Digest::User::User(Auth::Config *aConfig, const char *aRequestRealm) : + Auth::User(aConfig, aRequestRealm), HA1created(0) { memset(HA1, 0, sizeof(HA1)); diff --git a/src/auth/digest/User.h b/src/auth/digest/User.h index 2e86534d0f..08ccae847d 100644 --- a/src/auth/digest/User.h +++ b/src/auth/digest/User.h @@ -14,7 +14,7 @@ class User : public Auth::User public: MEMPROXY_CLASS(Auth::Digest::User); - User(Auth::Config *); + User(Auth::Config *, const char *requestRealm); ~User(); int authenticated() const; diff --git a/src/auth/digest/UserRequest.cc b/src/auth/digest/UserRequest.cc index 161161f806..0a4e9452ee 100644 --- a/src/auth/digest/UserRequest.cc +++ b/src/auth/digest/UserRequest.cc @@ -1,4 +1,5 @@ #include "squid.h" +#include "AccessLogEntry.h" #include "auth/digest/auth_digest.h" #include "auth/digest/User.h" #include "auth/digest/UserRequest.h" @@ -7,6 +8,8 @@ #include "HttpHeaderTools.h" #include "HttpReply.h" #include "HttpRequest.h" +#include "format/Format.h" +#include "MemBuf.h" #include "SquidTime.h" Auth::Digest::UserRequest::UserRequest() : @@ -56,6 +59,12 @@ Auth::Digest::UserRequest::authenticated() const return 0; } +const char * +Auth::Digest::UserRequest::credentialsStr() +{ + return realm; +} + /** log a digest user in */ void @@ -248,7 +257,7 @@ Auth::Digest::UserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int acc /* send the initial data to a digest authenticator module */ void -Auth::Digest::UserRequest::module_start(AUTHCB * handler, void *data) +Auth::Digest::UserRequest::module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) { char buf[8192]; @@ -261,12 +270,19 @@ Auth::Digest::UserRequest::module_start(AUTHCB * handler, void *data) return; } + const char *keyExtras = helperRequestKeyExtras(request, al); if (static_cast(Auth::Config::Find("digest"))->utf8) { char userstr[1024]; latin1_to_utf8(userstr, sizeof(userstr), user()->username()); - snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); + if (keyExtras) + snprintf(buf, 8192, "\"%s\":\"%s\" %s\n", userstr, realm, keyExtras); + else + snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm); } else { - snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm); + if (keyExtras) + snprintf(buf, 8192, "\"%s\":\"%s\" %s\n", user()->username(), realm, keyExtras); + else + snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm); } helperSubmit(digestauthenticators, buf, Auth::Digest::UserRequest::HandleReply, diff --git a/src/auth/digest/UserRequest.h b/src/auth/digest/UserRequest.h index 55787a2837..85415d1801 100644 --- a/src/auth/digest/UserRequest.h +++ b/src/auth/digest/UserRequest.h @@ -34,7 +34,8 @@ public: virtual void addAuthenticationInfoTrailer(HttpReply * rep, int accel); #endif - virtual void module_start(AUTHCB *, void *); + virtual void module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *, void *); + virtual const char *credentialsStr(); char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */ char *cnonce; /* "0a4f113b" */ diff --git a/src/auth/digest/auth_digest.cc b/src/auth/digest/auth_digest.cc index 09313093eb..1b7c738a3f 100644 --- a/src/auth/digest/auth_digest.cc +++ b/src/auth/digest/auth_digest.cc @@ -477,12 +477,12 @@ authDigestNoncePurge(digest_nonce_h * nonce) /* USER related functions */ static Auth::User::Pointer -authDigestUserFindUsername(const char *username) +authDigestUserFindUsername(const char *userkey) { AuthUserHashPointer *usernamehash; - debugs(29, 9, HERE << "Looking for user '" << username << "'"); + debugs(29, 9, "Looking for user '" << userkey << "'"); - if (username && (usernamehash = static_cast < AuthUserHashPointer * >(hash_lookup(proxy_auth_username_cache, username)))) { + if ((usernamehash = static_cast < AuthUserHashPointer * >(hash_lookup(proxy_auth_username_cache, userkey)))) { while ((usernamehash->user()->auth_type != Auth::AUTH_DIGEST) && (usernamehash->next)) usernamehash = static_cast(usernamehash->next); @@ -523,6 +523,7 @@ Auth::Digest::Config::dump(StoreEntry * entry, const char *name, Auth::Config * name, "digest", noncemaxuses, name, "digest", (int) noncemaxduration, name, "digest", (int) nonceGCInterval); + Auth::Config::dump(entry, name, scheme); } bool @@ -605,6 +606,8 @@ Auth::Digest::Config::registerWithCacheManager(void) void Auth::Digest::Config::done() { + Auth::Config::done(); + authdigest_initialised = 0; if (digestauthenticators) @@ -666,9 +669,8 @@ Auth::Digest::Config::parse(Auth::Config * scheme, int n_configured, char *param parse_onoff(&PostWorkaround); } else if (strcmp(param_str, "utf8") == 0) { parse_onoff(&utf8); - } else { - debugs(29, DBG_CRITICAL, "unrecognised digest auth scheme parameter '" << param_str << "'"); - } + } else + Auth::Config::parse(scheme, n_configured, param_str); } const char * @@ -758,13 +760,13 @@ authDigestUserLinkNonce(Auth::Digest::User * user, digest_nonce_h * nonce) /* setup the necessary info to log the username */ static Auth::UserRequest::Pointer -authDigestLogUsername(char *username, Auth::UserRequest::Pointer auth_user_request) +authDigestLogUsername(char *username, Auth::UserRequest::Pointer auth_user_request, const char *requestRealm) { assert(auth_user_request != NULL); /* log the username */ debugs(29, 9, "Creating new user for logging '" << (username?username:"[no username]") << "'"); - Auth::User::Pointer digest_user = new Auth::Digest::User(static_cast(Auth::Config::Find("digest"))); + Auth::User::Pointer digest_user = new Auth::Digest::User(static_cast(Auth::Config::Find("digest")), requestRealm); /* save the credentials */ digest_user->username(username); /* set the auth_user type */ @@ -779,7 +781,7 @@ authDigestLogUsername(char *username, Auth::UserRequest::Pointer auth_user_reque * Auth_user structure. */ Auth::UserRequest::Pointer -Auth::Digest::Config::decode(char const *proxy_auth) +Auth::Digest::Config::decode(char const *proxy_auth, const char *aRequestRealm) { const char *item; const char *p; @@ -944,7 +946,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* do we have a username ? */ if (!username || username[0] == '\0') { debugs(29, 2, "Empty or not present username"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -955,7 +957,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) */ if (strchr(username, '"')) { debugs(29, 2, "Unacceptable username '" << username << "'"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -963,7 +965,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* do we have a realm ? */ if (!digest_request->realm || digest_request->realm[0] == '\0') { debugs(29, 2, "Empty or not present realm"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -971,7 +973,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* and a nonce? */ if (!digest_request->nonceb64 || digest_request->nonceb64[0] == '\0') { debugs(29, 2, "Empty or not present nonce"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -980,7 +982,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) * authenticate phase, but needs to be given */ if (!digest_request->uri || digest_request->uri[0] == '\0') { debugs(29, 2, "Missing URI field"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -988,7 +990,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* is the response the correct length? */ if (!digest_request->response || strlen(digest_request->response) != 32) { debugs(29, 2, "Response length invalid"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -999,7 +1001,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) else if (strcmp(digest_request->algorithm, "MD5") && strcmp(digest_request->algorithm, "MD5-sess")) { debugs(29, 2, "invalid algorithm specified!"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1011,7 +1013,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) if (strcmp(digest_request->qop, QOP_AUTH) != 0) { /* we received a qop option we didn't send */ debugs(29, 2, "Invalid qop option received"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1019,7 +1021,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* check cnonce */ if (!digest_request->cnonce || digest_request->cnonce[0] == '\0') { debugs(29, 2, "Missing cnonce field"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1027,7 +1029,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* check nc */ if (strlen(digest_request->nc) != 8 || strspn(digest_request->nc, "0123456789abcdefABCDEF") != 8) { debugs(29, 2, "invalid nonce count"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1035,7 +1037,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* cnonce and nc both require qop */ if (digest_request->cnonce || digest_request->nc[0] != '\0') { debugs(29, 2, "missing qop!"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1050,7 +1052,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) debugs(29, 2, "Unexpected or invalid nonce received"); if (digest_request->user() != NULL) digest_request->user()->credentials(Auth::Failed); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1061,7 +1063,7 @@ Auth::Digest::Config::decode(char const *proxy_auth) /* check that we're not being hacked / the username hasn't changed */ if (nonce->user && strcmp(username, nonce->user->username())) { debugs(29, 2, "Username for the nonce does not equal the username for the request"); - rv = authDigestLogUsername(username, digest_request); + rv = authDigestLogUsername(username, digest_request, aRequestRealm); safe_free(username); return rv; } @@ -1075,10 +1077,11 @@ Auth::Digest::Config::decode(char const *proxy_auth) Auth::User::Pointer auth_user; - if ((auth_user = authDigestUserFindUsername(username)) == NULL) { + SBuf key = Auth::User::BuildUserKey(username, aRequestRealm); + if (key.isEmpty() || (auth_user = authDigestUserFindUsername(key.c_str())) == NULL) { /* the user doesn't exist in the username cache yet */ debugs(29, 9, HERE << "Creating new digest user '" << username << "'"); - digest_user = new Auth::Digest::User(this); + digest_user = new Auth::Digest::User(this, aRequestRealm); /* auth_user is a parent */ auth_user = digest_user; /* save the username */ diff --git a/src/auth/digest/auth_digest.h b/src/auth/digest/auth_digest.h index 1b3892d618..386109a3a1 100644 --- a/src/auth/digest/auth_digest.h +++ b/src/auth/digest/auth_digest.h @@ -69,7 +69,7 @@ public: Config(); virtual bool active() const; virtual bool configured() const; - virtual Auth::UserRequest::Pointer decode(char const *proxy_auth); + virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm); virtual void done(); virtual void rotateHelpers(); virtual void dump(StoreEntry *, const char *, Auth::Config *); diff --git a/src/auth/negotiate/User.cc b/src/auth/negotiate/User.cc index ca73352da2..a4b9c42373 100644 --- a/src/auth/negotiate/User.cc +++ b/src/auth/negotiate/User.cc @@ -3,8 +3,8 @@ #include "auth/negotiate/User.h" #include "Debug.h" -Auth::Negotiate::User::User(Auth::Config *aConfig) : - Auth::User(aConfig) +Auth::Negotiate::User::User(Auth::Config *aConfig, const char *aRequestRealm) : + Auth::User(aConfig, aRequestRealm) { proxy_auth_list.head = proxy_auth_list.tail = NULL; } diff --git a/src/auth/negotiate/User.h b/src/auth/negotiate/User.h index b6e84588f8..57de9e5eff 100644 --- a/src/auth/negotiate/User.h +++ b/src/auth/negotiate/User.h @@ -16,7 +16,7 @@ class User : public Auth::User { public: MEMPROXY_CLASS(Auth::Negotiate::User); - User(Auth::Config *); + User(Auth::Config *, const char *requestRealm); ~User(); virtual int32_t ttl() const; diff --git a/src/auth/negotiate/UserRequest.cc b/src/auth/negotiate/UserRequest.cc index 086da9f7b9..ed34ff2d63 100644 --- a/src/auth/negotiate/UserRequest.cc +++ b/src/auth/negotiate/UserRequest.cc @@ -1,4 +1,5 @@ #include "squid.h" +#include "AccessLogEntry.h" #include "auth/negotiate/auth_negotiate.h" #include "auth/negotiate/UserRequest.h" #include "auth/State.h" @@ -9,6 +10,8 @@ #include "HttpHeaderTools.h" #include "HttpReply.h" #include "HttpRequest.h" +#include "format/Format.h" +#include "MemBuf.h" #include "SquidTime.h" Auth::Negotiate::UserRequest::UserRequest() @@ -52,6 +55,18 @@ Auth::Negotiate::UserRequest::authenticated() const return 0; } +const char * +Auth::Negotiate::UserRequest::credentialsStr() +{ + static char buf[MAX_AUTHTOKEN_LEN]; + if (user()->credentials() == Auth::Pending) { + snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? + } else { + snprintf(buf, sizeof(buf), "KK %s\n", client_blob); + } + return buf; +} + Auth::Direction Auth::Negotiate::UserRequest::module_direction() { @@ -82,7 +97,7 @@ Auth::Negotiate::UserRequest::module_direction() } void -Auth::Negotiate::UserRequest::module_start(AUTHCB * handler, void *data) +Auth::Negotiate::UserRequest::module_start(HttpRequest *req, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) { static char buf[MAX_AUTHTOKEN_LEN]; @@ -100,10 +115,17 @@ Auth::Negotiate::UserRequest::module_start(AUTHCB * handler, void *data) debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'"); + const char *keyExtras = helperRequestKeyExtras(request, al); if (user()->credentials() == Auth::Pending) { - snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? + if (keyExtras) + snprintf(buf, sizeof(buf), "YR %s %s\n", client_blob, keyExtras); + else + snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? } else { - snprintf(buf, sizeof(buf), "KK %s\n", client_blob); + if (keyExtras) + snprintf(buf, sizeof(buf), "KK %s %s\n", client_blob, keyExtras); + else + snprintf(buf, sizeof(buf), "KK %s\n", client_blob); } waiting = 1; @@ -283,10 +305,10 @@ Auth::Negotiate::UserRequest::HandleReply(void *data, const HelperReply &reply) 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(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username())); + AuthUserHashPointer *usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->userKey())); Auth::User::Pointer local_auth_user = lm_request->user(); while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NEGOTIATE || - strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0)) + strcmp(usernamehash->user()->userKey(), auth_user_request->user()->userKey()) != 0)) usernamehash = static_cast(usernamehash->next); if (usernamehash) { /* we can't seamlessly recheck the username due to the diff --git a/src/auth/negotiate/UserRequest.h b/src/auth/negotiate/UserRequest.h index 98276841d4..b7af9311ec 100644 --- a/src/auth/negotiate/UserRequest.h +++ b/src/auth/negotiate/UserRequest.h @@ -26,7 +26,8 @@ public: virtual int authenticated() const; virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); virtual Direction module_direction(); - virtual void module_start(AUTHCB *, void *); + virtual void module_start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB *, void *); + virtual const char *credentialsStr(); virtual void addAuthenticationInfoHeader(HttpReply * rep, int accel); diff --git a/src/auth/negotiate/auth_negotiate.cc b/src/auth/negotiate/auth_negotiate.cc index 2455e6f2cb..9ce6010ae2 100644 --- a/src/auth/negotiate/auth_negotiate.cc +++ b/src/auth/negotiate/auth_negotiate.cc @@ -88,6 +88,8 @@ Auth::Negotiate::Config::rotateHelpers() void Auth::Negotiate::Config::done() { + Auth::Config::done(); + authnegotiate_initialised = 0; if (negotiateauthenticators) { @@ -120,7 +122,7 @@ Auth::Negotiate::Config::dump(StoreEntry * entry, const char *name, Auth::Config storeAppendPrintf(entry, "\n%s negotiate children %d startup=%d idle=%d concurrency=%d\n", name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency); storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off"); - + Auth::Config::dump(entry, name, scheme); } Auth::Negotiate::Config::Config() : keep_alive(1) @@ -140,9 +142,8 @@ Auth::Negotiate::Config::parse(Auth::Config * scheme, int n_configured, char *pa authenticateChildren.parseConfig(); } else if (strcmp(param_str, "keep_alive") == 0) { parse_onoff(&keep_alive); - } else { - debugs(29, DBG_CRITICAL, "ERROR: unrecognised Negotiate auth scheme parameter '" << param_str << "'"); - } + } else + Auth::Config::parse(scheme, n_configured, param_str); } const char * @@ -287,9 +288,9 @@ authenticateNegotiateStats(StoreEntry * sentry) * Auth_user structure. */ Auth::UserRequest::Pointer -Auth::Negotiate::Config::decode(char const *proxy_auth) +Auth::Negotiate::Config::decode(char const *proxy_auth, const char *aRequestRealm) { - Auth::Negotiate::User *newUser = new Auth::Negotiate::User(Auth::Config::Find("negotiate")); + Auth::Negotiate::User *newUser = new Auth::Negotiate::User(Auth::Config::Find("negotiate"), aRequestRealm); Auth::UserRequest *auth_user_request = new Auth::Negotiate::UserRequest(); assert(auth_user_request->user() == NULL); diff --git a/src/auth/negotiate/auth_negotiate.h b/src/auth/negotiate/auth_negotiate.h index b5414f5bf7..b1dcfb8e34 100644 --- a/src/auth/negotiate/auth_negotiate.h +++ b/src/auth/negotiate/auth_negotiate.h @@ -31,7 +31,7 @@ public: Config(); virtual bool active() const; virtual bool configured() const; - virtual Auth::UserRequest::Pointer decode(char const *proxy_auth); + virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm); virtual void done(); virtual void rotateHelpers(); virtual void dump(StoreEntry *, const char *, Auth::Config *); diff --git a/src/auth/ntlm/User.cc b/src/auth/ntlm/User.cc index 61457b0983..cc5d9c704d 100644 --- a/src/auth/ntlm/User.cc +++ b/src/auth/ntlm/User.cc @@ -3,8 +3,8 @@ #include "auth/ntlm/User.h" #include "Debug.h" -Auth::Ntlm::User::User(Auth::Config *aConfig) : - Auth::User(aConfig) +Auth::Ntlm::User::User(Auth::Config *aConfig, const char *aRequestRealm) : + Auth::User(aConfig, aRequestRealm) { proxy_auth_list.head = proxy_auth_list.tail = NULL; } diff --git a/src/auth/ntlm/User.h b/src/auth/ntlm/User.h index efdee13f41..0dd72f96d5 100644 --- a/src/auth/ntlm/User.h +++ b/src/auth/ntlm/User.h @@ -16,7 +16,7 @@ class User : public Auth::User { public: MEMPROXY_CLASS(Auth::Ntlm::User); - User(Auth::Config *); + User(Auth::Config *, const char *requestRealm); ~User(); virtual int32_t ttl() const; diff --git a/src/auth/ntlm/UserRequest.cc b/src/auth/ntlm/UserRequest.cc index 4ce04ebf41..9c6d58c9ee 100644 --- a/src/auth/ntlm/UserRequest.cc +++ b/src/auth/ntlm/UserRequest.cc @@ -1,11 +1,15 @@ #include "squid.h" +#include "AccessLogEntry.h" #include "auth/ntlm/auth_ntlm.h" #include "auth/ntlm/UserRequest.h" #include "auth/State.h" #include "cbdata.h" #include "client_side.h" #include "globals.h" +#include "HttpMsg.h" #include "HttpRequest.h" +#include "format/Format.h" +#include "MemBuf.h" #include "SquidTime.h" Auth::Ntlm::UserRequest::UserRequest() @@ -49,6 +53,18 @@ Auth::Ntlm::UserRequest::authenticated() const return 0; } +const char * +Auth::Ntlm::UserRequest::credentialsStr() +{ + static char buf[MAX_AUTHTOKEN_LEN]; + if (user()->credentials() == Auth::Pending) { + snprintf(buf, sizeof(buf), "YR %s\n", client_blob); + } else { + snprintf(buf, sizeof(buf), "KK %s\n", client_blob); + } + return buf; +} + Auth::Direction Auth::Ntlm::UserRequest::module_direction() { @@ -79,7 +95,7 @@ Auth::Ntlm::UserRequest::module_direction() } void -Auth::Ntlm::UserRequest::module_start(AUTHCB * handler, void *data) +Auth::Ntlm::UserRequest::module_start(HttpRequest *req, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) { static char buf[MAX_AUTHTOKEN_LEN]; @@ -94,12 +110,18 @@ Auth::Ntlm::UserRequest::module_start(AUTHCB * handler, void *data) debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'"); + const char *keyExtras = helperRequestKeyExtras(request, al); if (user()->credentials() == Auth::Pending) { - snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? + if (keyExtras) + snprintf(buf, sizeof(buf), "YR %s %s\n", client_blob, keyExtras); + else + snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? } else { - snprintf(buf, sizeof(buf), "KK %s\n", client_blob); + if (keyExtras) + snprintf(buf, sizeof(buf), "KK %s %s\n", client_blob, keyExtras); + else + snprintf(buf, sizeof(buf), "KK %s\n", client_blob); } - waiting = 1; safe_free(client_blob); @@ -276,10 +298,10 @@ Auth::Ntlm::UserRequest::HandleReply(void *data, const HelperReply &reply) 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(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username())); + AuthUserHashPointer *usernamehash = static_cast(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->userKey())); Auth::User::Pointer local_auth_user = lm_request->user(); while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM || - strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0)) + strcmp(usernamehash->user()->userKey(), auth_user_request->user()->userKey()) != 0)) usernamehash = static_cast(usernamehash->next); if (usernamehash) { /* we can't seamlessly recheck the username due to the diff --git a/src/auth/ntlm/UserRequest.h b/src/auth/ntlm/UserRequest.h index b353c416c7..7bed6b1279 100644 --- a/src/auth/ntlm/UserRequest.h +++ b/src/auth/ntlm/UserRequest.h @@ -26,7 +26,8 @@ public: virtual int authenticated() const; virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type); virtual Auth::Direction module_direction(); - virtual void module_start(AUTHCB *, void *); + virtual void module_start(HttpRequest *req, AccessLogEntry::Pointer &al, AUTHCB *, void *); + virtual const char *credentialsStr(); virtual const char * connLastHeader(); diff --git a/src/auth/ntlm/auth_ntlm.cc b/src/auth/ntlm/auth_ntlm.cc index 4515183e3b..03d62f1dfd 100644 --- a/src/auth/ntlm/auth_ntlm.cc +++ b/src/auth/ntlm/auth_ntlm.cc @@ -80,6 +80,8 @@ Auth::Ntlm::Config::rotateHelpers() void Auth::Ntlm::Config::done() { + Auth::Config::done(); + authntlm_initialised = 0; if (ntlmauthenticators) { @@ -112,7 +114,7 @@ Auth::Ntlm::Config::dump(StoreEntry * entry, const char *name, Auth::Config * sc storeAppendPrintf(entry, "\n%s ntlm children %d startup=%d idle=%d concurrency=%d\n", name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency); storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "ntlm", keep_alive ? "on" : "off"); - + Auth::Config::dump(entry, name, scheme); } Auth::Ntlm::Config::Config() : keep_alive(1) @@ -132,9 +134,8 @@ Auth::Ntlm::Config::parse(Auth::Config * scheme, int n_configured, char *param_s authenticateChildren.parseConfig(); } else if (strcmp(param_str, "keep_alive") == 0) { parse_onoff(&keep_alive); - } else { - debugs(29, DBG_CRITICAL, "ERROR unrecognised NTLM auth scheme parameter '" << param_str << "'"); - } + } else + Auth::Config::parse(scheme, n_configured, param_str); } const char * @@ -267,9 +268,9 @@ authenticateNTLMStats(StoreEntry * sentry) * Auth_user structure. */ Auth::UserRequest::Pointer -Auth::Ntlm::Config::decode(char const *proxy_auth) +Auth::Ntlm::Config::decode(char const *proxy_auth, const char *aRequestRealm) { - Auth::Ntlm::User *newUser = new Auth::Ntlm::User(Auth::Config::Find("ntlm")); + Auth::Ntlm::User *newUser = new Auth::Ntlm::User(Auth::Config::Find("ntlm"), aRequestRealm); Auth::UserRequest::Pointer auth_user_request = new Auth::Ntlm::UserRequest(); assert(auth_user_request->user() == NULL); diff --git a/src/auth/ntlm/auth_ntlm.h b/src/auth/ntlm/auth_ntlm.h index 04c0c43c7d..e704c2a739 100644 --- a/src/auth/ntlm/auth_ntlm.h +++ b/src/auth/ntlm/auth_ntlm.h @@ -27,7 +27,7 @@ public: Config(); virtual bool active() const; virtual bool configured() const; - virtual Auth::UserRequest::Pointer decode(char const *proxy_auth); + virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm); virtual void done(); virtual void rotateHelpers(); virtual void dump(StoreEntry *, const char *, Auth::Config *); diff --git a/src/cf.data.pre b/src/cf.data.pre index b0dbe9176b..533b726ac6 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -319,12 +319,30 @@ DOC_START Ports flagged 'transparent', 'intercept', or 'tproxy' have authentication disabled. + === Parameters common to all schemes. === + + "program" cmdline + Specifies the command for the external authenticator. Such a program + runs a loop that, on every iteration, reads a request line from + the standard and responds with a scheme-specific answer. The loop + stops when all input is exchausted (EOF). See scheme-specific + "program" descriptions below for details. + + "key_extras" format + Specifies a string to be append to request line format for the + authentication helper. "Quoted" format values may contain spaces and + logformat %macros. In theory, any logformat %macro can be used. + In practice, a %macro expands as a dash (-) if the helper request is + sent before the required macro information is available to Squid. + By default, Squid uses request formats provided in scheme-specific + examples below (search for %credentials). + === Parameters for the basic scheme follow. === "program" cmdline Specify the command for the external authenticator. Such a program - reads a line containing "username password" and replies with one of - three results: + reads a request_format line ("username password" by default) and + replies with one of three results: OK the user exists. @@ -407,8 +425,8 @@ DOC_START === Parameters for the digest scheme follow === "program" cmdline - Specify the command for the external authenticator. Such - a program reads a line containing "username":"realm" and + Specify the command for the external authenticator. Such a program + reads a request_format line ("username":"realm" by default) and replies with one of three results: OK ha1="..." @@ -576,7 +594,6 @@ DOC_START supported by the proxy. auth_param negotiate keep_alive on - Examples: @@ -3726,6 +3743,11 @@ DOC_START ue User name from external acl helper ui User name from ident us User name from SSL + credentials Client credentials. The exact meaning depends on + the authentication scheme: For Basic authentication, + it is the password; for Digest, the realm sent by the + client; for NTLM and Negotiate, the client challenge + or client credentials prefixed with "YR " or "KK ". HTTP related format codes: diff --git a/src/client_side.cc b/src/client_side.cc index 249e72a0b3..ae05be6bff 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -633,13 +633,6 @@ ClientHttpRequest::logRequest() if (loggingEntry() && loggingEntry()->mem_obj) al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ? - al->cache.caddr.setNoAddr(); - - if (getConn() != NULL) { - al->cache.caddr = getConn()->log_addr; - al->cache.port = cbdataReference(getConn()->port); - } - al->http.clientRequestSz.header = req_sz; al->http.clientReplySz.header = out.headers_sz; // XXX: calculate without payload encoding or headers !! @@ -4323,7 +4316,7 @@ clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http) ConnStateData * conn = http->getConn(); ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request, cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str); - + ch->al = http->al; /* * hack for ident ACL. It needs to get full addresses, and a place to store * the ident result on persistent connections... diff --git a/src/client_side_request.cc b/src/client_side_request.cc index e7df8a2515..fd1effe838 100644 --- a/src/client_side_request.cc +++ b/src/client_side_request.cc @@ -164,6 +164,9 @@ ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) : al = new AccessLogEntry; al->cache.start_time = current_time; al->tcpClient = clientConnection = aConn->clientConnection; + al->cache.port = cbdataReference(aConn->port); + al->cache.caddr = aConn->log_addr; + #if USE_SSL if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) { if (SSL *ssl = fd_table[aConn->clientConnection->fd].ssl) @@ -507,6 +510,7 @@ clientFollowXForwardedForCheck(allow_t answer, void *data) */ ConnStateData *conn = http->getConn(); conn->log_addr = request->indirect_client_addr; + http->al->cache.caddr = conn->log_addr; } request->x_forwarded_for_iterator.clean(); request->flags.done_follow_x_forwarded_for = true; diff --git a/src/format/ByteCode.h b/src/format/ByteCode.h index 43bd5ea02a..e19d7d4a1e 100644 --- a/src/format/ByteCode.h +++ b/src/format/ByteCode.h @@ -193,6 +193,7 @@ typedef enum { LFT_ICAP_OUTCOME, LFT_ICAP_STATUS_CODE, #endif + LFT_CREDENTIALS, #if USE_SSL LFT_SSL_BUMP_MODE, diff --git a/src/format/Format.cc b/src/format/Format.cc index dcd64034d9..15bef5e868 100644 --- a/src/format/Format.cc +++ b/src/format/Format.cc @@ -1115,6 +1115,14 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS } break; + case LFT_CREDENTIALS: +#if USE_AUTH + if (al->request && al->request->auth_user_request != NULL) + out = strOrNull(al->request->auth_user_request->credentialsStr()); +#endif + + break; + case LFT_PERCENT: out = "%"; diff --git a/src/format/Token.cc b/src/format/Token.cc index e10cef5121..a0341be0d8 100644 --- a/src/format/Token.cc +++ b/src/format/Token.cc @@ -129,6 +129,7 @@ static TokenTableEntry TokenTableMisc[] = { {"err_code", LFT_SQUID_ERROR }, {"err_detail", LFT_SQUID_ERROR_DETAIL }, {"note", LFT_NOTE }, + {"credentials", LFT_CREDENTIALS}, {NULL, LFT_NONE} /* this must be last */ }; diff --git a/src/peer_select.cc b/src/peer_select.cc index 9c92594e43..2c6e4d4dfe 100644 --- a/src/peer_select.cc +++ b/src/peer_select.cc @@ -141,6 +141,7 @@ peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry) void peerSelect(Comm::ConnectionList * paths, HttpRequest * request, + AccessLogEntry::Pointer const &al, StoreEntry * entry, PSC * callback, void *callback_data) @@ -156,6 +157,7 @@ peerSelect(Comm::ConnectionList * paths, psstate->request = request; HTTPMSGLOCK(psstate->request); + psstate->al = al; psstate->entry = entry; psstate->paths = paths; @@ -439,13 +441,17 @@ peerSelectFoo(ps_state * ps) if (ps->always_direct == ACCESS_DUNNO) { debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)"); /** check always_direct; */ - ps->acl_checklist = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL); + ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL); + ch->al = ps->al; + ps->acl_checklist = ch; ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, ps); return; } else if (ps->never_direct == ACCESS_DUNNO) { debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)"); /** check never_direct; */ - ps->acl_checklist = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL); + ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL); + ch->al = ps->al; + ps->acl_checklist = ch; ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, ps); return; } else if (request->flags.noDirect) { diff --git a/src/tests/stub_libauth.cc b/src/tests/stub_libauth.cc index 090d5d6c87..a14c3151b7 100644 --- a/src/tests/stub_libauth.cc +++ b/src/tests/stub_libauth.cc @@ -5,7 +5,7 @@ #if USE_AUTH #include "auth/Config.h" -Auth::UserRequest::Pointer Auth::Config::CreateAuthUser(const char *) STUB_RETVAL(NULL) +Auth::UserRequest::Pointer Auth::Config::CreateAuthUser(const char *, AccessLogEntry::Pointer &al) STUB_RETVAL(NULL) Auth::Config * Auth::Config::Find(const char *) STUB_RETVAL(NULL) void Auth::Config::registerWithCacheManager(void) STUB_NOP Auth::ConfigVector Auth::TheConfig; @@ -28,7 +28,7 @@ Vector & Auth::Scheme::GetSchemes() STUB_RETVAL(*_Schemes void Auth::Scheme::FreeAll() STUB #include "auth/User.h" -Auth::User::User(Auth::Config *) STUB +Auth::User::User(Auth::Config *, const char *) STUB Auth::CredentialState Auth::User::credentials() const STUB_RETVAL(credentials_state) void Auth::User::credentials(CredentialState) STUB void Auth::User::absorb(Auth::User::Pointer) STUB @@ -44,7 +44,7 @@ void Auth::User::UsernameCacheStats(StoreEntry *) STUB #include "auth/UserRequest.h" char const * Auth::UserRequest::username() const STUB_RETVAL("stub_username") -void Auth::UserRequest::start(AUTHCB *, void *) STUB +void Auth::UserRequest::start(HttpRequest *, AccessLogEntry::Pointer &, AUTHCB *, void *) STUB bool Auth::UserRequest::valid() const STUB_RETVAL(false) void * Auth::UserRequest::operator new (size_t) STUB_RETVAL((void *)1) void Auth::UserRequest::operator delete (void *) STUB @@ -62,8 +62,8 @@ void Auth::UserRequest::addAuthenticationInfoHeader(HttpReply *, int) STUB void Auth::UserRequest::addAuthenticationInfoTrailer(HttpReply *, int) STUB void Auth::UserRequest::releaseAuthServer() STUB const char * Auth::UserRequest::connLastHeader() STUB_RETVAL("stub") -AuthAclState Auth::UserRequest::authenticate(Auth::UserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &) STUB_RETVAL(AUTH_AUTHENTICATED) -AuthAclState Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &) STUB_RETVAL(AUTH_AUTHENTICATED) +AuthAclState Auth::UserRequest::authenticate(Auth::UserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &) STUB_RETVAL(AUTH_AUTHENTICATED) +AuthAclState Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &) STUB_RETVAL(AUTH_AUTHENTICATED) void Auth::UserRequest::addReplyAuthHeader(HttpReply *, Auth::UserRequest::Pointer, HttpRequest *, int, int) STUB void authenticateFixHeader(HttpReply *, Auth::UserRequest::Pointer, HttpRequest *, int, int) STUB void authenticateAddTrailer(HttpReply *, Auth::UserRequest::Pointer, HttpRequest *, int) STUB diff --git a/src/tunnel.cc b/src/tunnel.cc index c44c21848b..66c6b08b8c 100644 --- a/src/tunnel.cc +++ b/src/tunnel.cc @@ -901,7 +901,7 @@ tunnelStart(ClientHttpRequest * http, int64_t * size_ptr, int *status_ptr, const CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall); - peerSelect(&(tunnelState->serverDestinations), request, + peerSelect(&(tunnelState->serverDestinations), request, al, NULL, tunnelPeerSelectComplete, tunnelState);