Hello,

This patch introduces a new 'auth_schemes' squid.conf directive.

This directive may be used to customize authentication
schemes presence and order in Squid's HTTP 401 (Unauthorized) and 407
(Proxy Authentication Required) responses. The defaults remain the same.

Also refactored configuration code for options with custom
ACL-controlled actions:

* Reduced parsing code duplication by adding ParseAclWithAction().

* Used functors for generic action-to-string conversions. It is possible
  now to perform such conversions providing lambda expressions.

* Used vectors of strings instead of C-style arrays for storing
  conversion tables and check against bounds with vector::at().


Regards,
Eduard.

Added auth_schemes to control schemes presence and order in 401s/407s.

The new squid.conf directive may be used to customize authentication
schemes presence and order in Squid's HTTP 401 (Unauthorized) and 407
(Proxy Authentication Required) responses. The defaults remain the same.

Also refactored configuration code for options with custom
ACL-controlled actions:

* Reduced parsing code duplication by adding ParseAclWithAction().

* Used functors for generic action-to-string conversions. It is possible
  now to perform such conversions providing lambda expressions.

* Used vectors of strings instead of C-style arrays for storing
  conversion tables and check against bounds with vector::at().

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2016-11-07 05:24:26 +0000
+++ src/SquidConfig.h	2016-11-18 15:03:18 +0000
@@ -1,42 +1,45 @@
 /*
  * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_SQUIDCONFIG_H_
 #define SQUID_SQUIDCONFIG_H_
 
 #include "acl/forward.h"
+#if USE_AUTH
+#include "auth/SchemesConfig.h"
+#endif
 #include "base/RefCount.h"
 #include "base/YesNoNone.h"
 #include "ClientDelayConfig.h"
 #include "DelayConfig.h"
 #include "helper/ChildConfig.h"
 #include "HttpHeaderTools.h"
 #include "ip/Address.h"
 #include "Notes.h"
 #include "security/forward.h"
 #include "SquidTime.h"
 #if USE_OPENSSL
 #include "ssl/support.h"
 #endif
 #include "store/forward.h"
 
 #if USE_OPENSSL
 class sslproxy_cert_sign;
 class sslproxy_cert_adapt;
 #endif
 
 namespace Mgr
 {
 class ActionPasswordList;
 } // namespace Mgr
 class CachePeer;
 class CustomLog;
 class CpuAffinityMap;
 class external_acl;
 class HeaderManglers;
 class RefreshPattern;
@@ -370,60 +373,63 @@
 #endif
         acl_access *redirector;
         acl_access *store_id;
         acl_access *reply;
         Acl::Address *outgoing_address;
 #if USE_HTCP
 
         acl_access *htcp;
         acl_access *htcp_clr;
 #endif
 
 #if USE_OPENSSL
         acl_access *ssl_bump;
 #endif
 #if FOLLOW_X_FORWARDED_FOR
         acl_access *followXFF;
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
         /// acceptible PROXY protocol clients
         acl_access *proxyProtocol;
 
         /// spoof_client_ip squid.conf acl.
         /// nil unless configured
         acl_access* spoof_client_ip;
         acl_access *on_unsupported_protocol;
 
         acl_access *ftp_epsv;
 
         acl_access *forceRequestBodyContinuation;
         acl_access *serverPconnForNonretriable;
+#if USE_AUTH
+        acl_access *authSchemes;
+#endif
     } accessList;
     AclDenyInfoList *denyInfoList;
 
     struct {
         size_t list_width;
         int list_wrap;
         char *anon_user;
         int passive;
         int epsv_all;
         int epsv;
         int eprt;
         int sanitycheck;
         int telnet;
     } Ftp;
     RefreshPattern *Refresh;
 
     Store::DiskConfig cacheSwap;
 
     struct {
         char *directory;
         int use_short_names;
     } icons;
     char *errorDirectory;
 #if USE_ERR_LOCALES
     char *errorDefaultLanguage;
     int errorLogMissingLanguages;
 #endif
     char *errorStylesheet;
 
     struct {
@@ -511,52 +517,55 @@
         sslproxy_cert_adapt *cert_adapt;
 #endif
     } ssl_client;
 
     char *accept_filter;
     int umask;
     int max_filedescriptors;
     int workers;
     CpuAffinityMap *cpuAffinityMap;
 
 #if USE_LOADABLE_MODULES
     wordlist *loadable_module_names;
 #endif
 
     int client_ip_max_connections;
 
     char *redirector_extras;
 
     struct UrlHelperTimeout {
         int action;
         char *response;
     } onUrlRewriteTimeout;
 
     char *storeId_extras;
 
     struct {
         int v4_first;       ///< Place IPv4 first in the order of DNS results.
         ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
     } dns;
 
+#if USE_AUTH
+    Auth::SchemesConfigs authSchemesConfigs;
+#endif
 };
 
 extern SquidConfig Config;
 
 class SquidConfig2
 {
 public:
     void clear() {
         *this = SquidConfig2();
     }
 
     struct {
         int enable_purge = 0;
     } onoff;
     uid_t effectiveUserID = 0;
     gid_t effectiveGroupID = 0;
 };
 
 extern SquidConfig2 Config2;
 
 #endif /* SQUID_SQUIDCONFIG_H_ */
 

=== modified file 'src/acl/Tree.cc'
--- src/acl/Tree.cc	2016-01-01 00:12:18 +0000
+++ src/acl/Tree.cc	2016-11-18 15:03:18 +0000
@@ -30,70 +30,41 @@
 /// computes action that corresponds to the position of the matched rule
 allow_t
 Acl::Tree::actionAt(const Nodes::size_type pos) const
 {
     assert(pos < nodes.size());
     if (actions.size()) {
         assert(actions.size() == nodes.size());
         return actions[pos];
     }
     // default for matched rules in trees without actions
     return ACCESS_ALLOWED;
 }
 
 void
 Acl::Tree::add(ACL *rule, const allow_t &action)
 {
     // either all rules have actions or none
     assert(nodes.size() == actions.size());
     InnerNode::add(rule);
     actions.push_back(action);
 }
 
 void
 Acl::Tree::add(ACL *rule)
 {
     // either all rules have actions or none
     assert(actions.empty());
     InnerNode::add(rule);
 }
 
-SBufList
-Acl::Tree::treeDump(const char *prefix, const ActionToString &convert) const
-{
-    SBufList text;
-    Actions::const_iterator action = actions.begin();
-    typedef Nodes::const_iterator NCI;
-    for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
-
-        text.push_back(SBuf(prefix));
-
-        if (action != actions.end()) {
-            const char *act = convert ? convert[action->kind] :
-                              (*action == ACCESS_ALLOWED ? "allow" : "deny");
-            text.push_back(act?SBuf(act):SBuf("???"));
-            ++action;
-        }
-
-#if __cplusplus >= 201103L
-        text.splice(text.end(), (*node)->dump());
-#else
-        // temp is needed until c++11 move constructor
-        SBufList temp = (*node)->dump();
-        text.splice(text.end(), temp);
-#endif
-        text.push_back(SBuf("\n"));
-    }
-    return text;
-}
-
 bool
 Acl::Tree::bannedAction(ACLChecklist *checklist, Nodes::const_iterator node) const
 {
     if (actions.size()) {
         assert(actions.size() == nodes.size());
         const Nodes::size_type pos = node - nodes.begin();
         return checklist->bannedAction(actions.at(pos));
     }
     return false;
 }
 

=== modified file 'src/acl/Tree.h'
--- src/acl/Tree.h	2016-03-01 09:58:44 +0000
+++ src/acl/Tree.h	2016-11-18 15:03:18 +0000
@@ -1,55 +1,91 @@
 /*
  * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_ACL_TREE_H
 #define SQUID_ACL_TREE_H
 
 #include "acl/BoolOps.h"
 #include "sbuf/List.h"
 
 namespace Acl
 {
 
 /// An ORed set of rules at the top of the ACL expression tree, providing two
 /// unique properties: cbdata protection and optional rule actions.
 class Tree: public OrNode
 {
     // XXX: We should use refcounting instead, but it requires making ACLs
     // refcounted as well. Otherwise, async lookups will reach deleted ACLs.
     CBDATA_CLASS(Tree);
 
 public:
     /// dumps <name, action, rule, new line> tuples
-    /// action.kind is mapped to a string using the supplied conversion table
-    typedef const char **ActionToString;
-    SBufList treeDump(const char *name, const ActionToString &convert) const;
+    /// the supplied converter maps action.kind to a string
+    template <class ActionToStringConverter>
+    SBufList treeDump(const char *name, ActionToStringConverter converter) const;
 
     /// Returns the corresponding action after a successful tree match.
     allow_t winningAction() const;
 
     /// what action to use if no nodes matched
     allow_t lastAction() const;
 
     /// appends and takes control over the rule with a given action
     void add(ACL *rule, const allow_t &action);
     void add(ACL *rule); ///< same as InnerNode::add()
 
 protected:
     /// Acl::OrNode API
     virtual bool bannedAction(ACLChecklist *, Nodes::const_iterator) const override;
     allow_t actionAt(const Nodes::size_type pos) const;
 
     /// if not empty, contains actions corresponding to InnerNode::nodes
     typedef std::vector<allow_t> Actions;
     Actions actions;
 };
 
+inline const char *
+AllowOrDeny(const allow_t &action)
+{
+    return action == ACCESS_ALLOWED ? "allow" : "deny";
+}
+
+template <class ActionToStringConverter>
+inline SBufList
+Tree::treeDump(const char *prefix, ActionToStringConverter converter) const
+{
+    SBufList text;
+    Actions::const_iterator action = actions.begin();
+    typedef Nodes::const_iterator NCI;
+    for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
+
+        text.push_back(SBuf(prefix));
+
+        if (action != actions.end()) {
+            static const SBuf DefaultActString("???");
+            const char *act = converter(*action);
+            text.push_back(act ? SBuf(act) : DefaultActString);
+            ++action;
+        }
+
+#if __cplusplus >= 201103L
+        text.splice(text.end(), (*node)->dump());
+#else
+        // temp is needed until c++11 move constructor
+        SBufList temp = (*node)->dump();
+        text.splice(text.end(), temp);
+#endif
+        text.push_back(SBuf("\n"));
+    }
+    return text;
+}
+
 } // namespace Acl
 
 #endif /* SQUID_ACL_TREE_H */
 

=== modified file 'src/auth/Config.cc'
--- src/auth/Config.cc	2016-01-01 00:12:18 +0000
+++ src/auth/Config.cc	2016-11-18 15:03:18 +0000
@@ -38,60 +38,69 @@
     Auth::Config *config = Find(proxy_auth);
 
     if (config == NULL || !config->active()) {
         debugs(29, (shutting_down?3:DBG_IMPORTANT), (shutting_down?"":"WARNING: ") <<
                "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, rmb.hasContent() ? rmb.content() : NULL);
 }
 
 Auth::Config *
 Auth::Config::Find(const char *proxy_auth)
 {
     for (Auth::ConfigVector::iterator  i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
         if (strncasecmp(proxy_auth, (*i)->type(), strlen((*i)->type())) == 0)
             return *i;
 
     return NULL;
 }
 
+Auth::Config *
+Auth::Config::GetParsed(const char *proxy_auth)
+{
+    if (auto *cfg = Find(proxy_auth))
+        return cfg;
+    fatalf("auth_schemes: required authentication method '%s' is not configured", proxy_auth);
+    return nullptr;
+}
+
 /** Default behaviour is to expose nothing */
 void
 Auth::Config::registerWithCacheManager(void)
 {}
 
 void
 Auth::Config::parse(Auth::Config * scheme, int, char *param_str)
 {
     if (strcmp(param_str, "program") == 0) {
         if (authenticateProgram)
             wordlistDestroy(&authenticateProgram);
 
         parse_wordlist(&authenticateProgram);
 
         requirePathnameExists("Authentication helper program", authenticateProgram->key);
 
     } else if (strcmp(param_str, "realm") == 0) {
         realm.clear();
 
         char *token = ConfigParser::NextQuotedOrToEol();
 
         while (token && *token && xisspace(*token))
             ++token;
 
         if (!token || !*token) {
             debugs(29, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Missing auth_param " << scheme->type() << " realm");
             self_destruct();
             return;
         }
 

=== modified file 'src/auth/Config.h'
--- src/auth/Config.h	2016-01-01 00:12:18 +0000
+++ src/auth/Config.h	2016-11-18 15:03:18 +0000
@@ -22,60 +22,63 @@
 
 /* for Http::HdrType parameters-by-value */
 #include "HttpHeader.h"
 
 namespace Format
 {
 class Format;
 }
 
 namespace Auth
 {
 
 /**
  * \ingroup AuthAPI
  * \par
  * I am the configuration for an auth scheme.
  * Currently each scheme has only one instance of me,
  * but this may change.
  * \par
  * This class is treated like a ref counted class.
  * If the children ever stop being singletons, implement the
  * ref counting...
  */
 class Config
 {
 
 public:
     static UserRequest::Pointer CreateAuthUser(const char *proxy_auth, AccessLogEntry::Pointer &al);
 
     static Config *Find(const char *proxy_auth);
+    /// Call this method if you need a guarantee that all auth schemes has been
+    /// already configured.
+    static Config *GetParsed(const char *proxy_auth);
     Config() : authenticateChildren(20), authenticateProgram(NULL), keyExtras(NULL) {}
 
     virtual ~Config() {}
 
     /**
      * Used by squid to determine whether the auth module has successfully initialised itself with the current configuration.
      *
      \retval true   Authentication Module loaded and running.
      \retval false  No Authentication Module loaded.
      */
     virtual bool active() const = 0;
 
     /**
      * new decode API: virtual factory pattern
      \par
      * Responsible for decoding the passed authentication header, creating or
      * linking to a AuthUser object and for storing any needed details to complete
      * authentication in Auth::UserRequest::authenticate().
      *
      \param proxy_auth  Login Pattern to parse.
      \retval *      Details needed to authenticate.
      */
     virtual UserRequest::Pointer decode(char const *proxy_auth, const char *requestRealm) = 0;
 
     /**
      * squid is finished with this config, release any unneeded resources.
      * If a singleton, delete will not occur. if not a singleton (future),
      * delete will occur when no references are held.
      *
      \todo we need a 'done for reconfigure' and a 'done permanently' concept.

=== modified file 'src/auth/Makefile.am'
--- src/auth/Makefile.am	2016-01-01 00:12:18 +0000
+++ src/auth/Makefile.am	2016-11-18 15:03:18 +0000
@@ -3,58 +3,60 @@
 ## Squid software is distributed under GPLv2+ license and includes
 ## contributions from numerous individuals and organizations.
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
 SUBDIRS = $(AUTH_MODULES)
 DIST_SUBDIRS = basic digest negotiate ntlm
 
 noinst_LTLIBRARIES = libauth.la libacls.la
 ## not needed? $(AUTH_LIBS_TO_BUILD)
 ## EXTRA_LTLIBRARIES = libdigest.la libntlm.la libnegotiate.la
 
 ## authentication framework; this library is always built
 libauth_la_SOURCES = \
 	Type.h \
 	Type.cc \
 	Config.cc \
 	Config.h \
 	CredentialsCache.h \
 	CredentialsCache.cc \
 	CredentialState.cc \
 	CredentialState.h \
 	Gadgets.cc \
 	Gadgets.h \
 	QueueNode.h \
 	Scheme.cc \
 	Scheme.h \
+	SchemesConfig.h \
+	SchemesConfig.cc \
 	State.h \
 	State.cc \
 	User.h \
 	User.cc \
 	UserRequest.h \
 	UserRequest.cc
 
 libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD)
 libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD)
 
 ## authentication-dependent ACLs and authentication code they share
 libacls_la_SOURCES = \
 	Acl.cc \
 	Acl.h \
 	\
 	AclMaxUserIp.cc \
 	AclMaxUserIp.h \
 	AclProxyAuth.cc \
 	AclProxyAuth.h \
 	AuthAclState.h
 
 CredentialState.cc: CredentialState.h $(top_srcdir)/src/mk-string-arrays.awk
 	$(AWK) -f $(top_srcdir)/src/mk-string-arrays.awk < $(srcdir)/CredentialState.h > $@ || (rm -f $@ ; exit 1)
 
 Type.cc: Type.h $(top_srcdir)/src/mk-string-arrays.awk
 	$(AWK) -f $(top_srcdir)/src/mk-string-arrays.awk < $(srcdir)/Type.h > $@ || (rm -f $@ ; exit 1)
 
 CLEANFILES += CredentialState.cc Type.cc

=== added file 'src/auth/SchemesConfig.cc'
--- src/auth/SchemesConfig.cc	1970-01-01 00:00:00 +0000
+++ src/auth/SchemesConfig.cc	2016-11-18 15:03:18 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "auth/Config.h"
+#include "auth/SchemesConfig.h"
+#include "fatal.h"
+#include "parser/Tokenizer.h"
+
+static void
+addUnique(const SBuf &scheme, std::vector<SBuf> &vec)
+{
+    static const SBuf all("ALL");
+    if (scheme == all) {
+        for (const auto config: Auth::TheConfig)
+            addUnique(SBuf(config->type()), vec);
+    } else if (std::find(vec.begin(), vec.end(), scheme) == vec.end())
+        vec.push_back(scheme);
+}
+
+void
+Auth::SchemesConfig::expand()
+{
+    static const CharacterSet delimiters("delimiters", ",");
+    static const CharacterSet quotedDelimiters("quotedDelimiters", ", ");
+    const CharacterSet *resultDelimiters = quoted ? &quotedDelimiters : &delimiters;
+    std::vector<SBuf> expanded;
+    Parser::Tokenizer t(schemes);
+    SBuf scheme;
+    while (t.token(scheme, *resultDelimiters))
+        addUnique(scheme, expanded);
+    t.skipAllTrailing(CharacterSet::SP + CharacterSet::HTAB);
+    if (!t.remaining().isEmpty())
+        addUnique(t.remaining(), expanded);
+
+    authConfigs.clear();
+    transform(expanded.begin(), expanded.end(),
+    back_inserter(authConfigs), [](SBuf &s) {
+        return Auth::Config::GetParsed(s.c_str());
+    });
+}
+

=== added file 'src/auth/SchemesConfig.h'
--- src/auth/SchemesConfig.h	1970-01-01 00:00:00 +0000
+++ src/auth/SchemesConfig.h	2016-11-18 15:03:18 +0000
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_SCHEMES_CONFIG_H
+#define SQUID_SCHEMES_CONFIG_H
+
+#if USE_AUTH
+
+#include "auth/Config.h"
+
+namespace Auth
+{
+
+/**
+ * \ingroup AuthAPI
+ * Stores authentication schemes list, configured by auth_schemes
+ * directive.
+ */
+class SchemesConfig
+{
+public:
+    SchemesConfig(const char *s, const bool q) : schemes(s), quoted(q), rawSchemes(schemes.c_str()) {}
+    /// Expands special "ALL" scheme name (if provided), removes
+    /// duplicates and fills authConfigs vector.
+    void expand();
+
+public:
+    /// corresponding vector of Auth::Config objects
+    ConfigVector authConfigs;
+
+private:
+    /// raw auth schemes list (may have duplicates)
+    SBuf schemes;
+    const bool quoted;
+
+public:
+    /// optimization for storing schemes.c_str()
+    const char *rawSchemes;
+};
+
+typedef std::vector<SchemesConfig> SchemesConfigs;
+
+} // namespace Auth
+
+#endif /* USE_AUTH */
+#endif /* SQUID_SCHEMES_CONFIG_H */
+

=== modified file 'src/auth/UserRequest.cc'
--- src/auth/UserRequest.cc	2016-01-24 17:41:43 +0000
+++ src/auth/UserRequest.cc	2016-11-18 15:03:18 +0000
@@ -1,57 +1,60 @@
 /*
  * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 29    Authenticator */
 
 /* The functions in this file handle authentication.
  * They DO NOT perform access control or auditing.
  * See acl.c for access control and client_side.c for auditing */
 
 #include "squid.h"
+#include "acl/FilledChecklist.h"
 #include "auth/Config.h"
 #include "auth/Scheme.h"
+#include "auth/SchemesConfig.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
 #include "client_side.h"
 #include "comm/Connection.h"
 #include "fatal.h"
 #include "format/Format.h"
 #include "http/Stream.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
+#include "SquidConfig.h"
 
 /* Generic Functions */
 
 char const *
 Auth::UserRequest::username() const
 {
     if (user() != NULL)
         return user()->username();
     else
         return NULL;
 }
 
 /**** PUBLIC FUNCTIONS (ALL GENERIC!)  ****/
 
 /* send the initial data to an authenticator module */
 void
 Auth::UserRequest::start(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
 {
     assert(handler);
     assert(data);
     debugs(29, 9, this);
     startHelperLookup(request, al, handler, data);
 }
 
 bool
 Auth::UserRequest::valid() const
 {
     debugs(29, 9, HERE << "Validating Auth::UserRequest '" << this << "'.");
 
     if (user() == NULL) {
@@ -434,99 +437,113 @@
 }
 
 AuthAclState
 Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer * aUR, Http::HdrType 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);
 
     if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
         if (*aUR == NULL)
             *aUR = t;
 
         if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
             request->auth_user_request = t;
         }
         return t->lastReply;
     }
 
     // ok, call the actual authenticator routine.
     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);
 
     if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
         t->lastReply = result;
 
     return result;
 }
 
+static Auth::ConfigVector &
+schemesConfig(HttpRequest *request, HttpReply *rep)
+{
+    if (::Config.accessList.authSchemes) {
+        ACLFilledChecklist ch(NULL, request, NULL);
+        ch.reply = rep;
+        HTTPMSGLOCK(ch.reply);
+        const allow_t answer = ch.fastCheck(::Config.accessList.authSchemes);
+        if (answer == ACCESS_ALLOWED)
+            return ::Config.authSchemesConfigs.at(answer.kind).authConfigs;
+    }
+    return Auth::TheConfig;
+}
+
 void
 Auth::UserRequest::addReplyAuthHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
 /* send the auth types we are configured to support (and have compiled in!) */
 {
     Http::HdrType type;
 
     switch (rep->sline.status()) {
 
     case Http::scProxyAuthenticationRequired:
         /* Proxy authorisation needed */
         type = Http::HdrType::PROXY_AUTHENTICATE;
         break;
 
     case Http::scUnauthorized:
         /* WWW Authorisation needed */
         type = Http::HdrType::WWW_AUTHENTICATE;
         break;
 
     default:
         /* Keep GCC happy */
         /* some other HTTP status */
         type = Http::HdrType::BAD_HDR;
         break;
     }
 
     debugs(29, 9, "headertype:" << type << " authuser:" << auth_user_request);
 
     if (((rep->sline.status() == Http::scProxyAuthenticationRequired)
             || (rep->sline.status() == Http::scUnauthorized)) && internal)
         /* this is a authenticate-needed response */
     {
 
         if (auth_user_request != NULL && auth_user_request->direction() == Auth::CRED_CHALLENGE)
             /* add the scheme specific challenge header to the response */
             auth_user_request->user()->config->fixHeader(auth_user_request, rep, type, request);
         else {
             /* call each configured & running authscheme */
-
-            for (Auth::ConfigVector::iterator  i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i) {
+            Auth::ConfigVector &configs = schemesConfig(request, rep);
+            for (Auth::ConfigVector::iterator i = configs.begin(); i != configs.end(); ++i) {
                 Auth::Config *scheme = *i;
 
                 if (scheme->active()) {
                     if (auth_user_request != NULL && auth_user_request->scheme()->type() == scheme->type())
                         scheme->fixHeader(auth_user_request, rep, type, request);
                     else
                         scheme->fixHeader(NULL, rep, type, request);
                 } else
                     debugs(29, 4, HERE << "Configured scheme " << scheme->type() << " not Active");
             }
         }
 
     }
 
     /*
      * allow protocol specific headers to be _added_ to the existing
      * response - currently Digest or Negotiate auth
      */
     if (auth_user_request != NULL) {
         auth_user_request->addAuthenticationInfoHeader(rep, accelerated);
         if (auth_user_request->lastReply != AUTH_AUTHENTICATED)
             auth_user_request->lastReply = AUTH_ACL_CANNOT_AUTHENTICATE;
     }
 }
 
 // TODO remove wrapper.
 void
 authenticateFixHeader(HttpReply * rep, Auth::UserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
 {
     Auth::UserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal);

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2016-11-01 01:41:39 +0000
+++ src/cache_cf.cc	2016-11-18 15:27:35 +0000
@@ -53,60 +53,61 @@
 #include "rfc1738.h"
 #include "sbuf/List.h"
 #include "SquidConfig.h"
 #include "SquidString.h"
 #include "ssl/ProxyCerts.h"
 #include "Store.h"
 #include "store/Disk.h"
 #include "store/Disks.h"
 #include "StoreFileSystem.h"
 #include "tools.h"
 #include "util.h"
 #include "wordlist.h"
 /* wccp2 has its own conditional definitions */
 #include "wccp2.h"
 #if USE_ADAPTATION
 #include "adaptation/Config.h"
 #endif
 #if ICAP_CLIENT
 #include "adaptation/icap/Config.h"
 #endif
 #if USE_ECAP
 #include "adaptation/ecap/Config.h"
 #endif
 #if USE_OPENSSL
 #include "ssl/Config.h"
 #include "ssl/support.h"
 #endif
 #if USE_AUTH
 #include "auth/Config.h"
 #include "auth/Scheme.h"
+#include "auth/SchemesConfig.h"
 #endif
 #if USE_SQUID_ESI
 #include "esi/Parser.h"
 #endif
 #if SQUID_SNMP
 #include "snmp.h"
 #endif
 
 #if HAVE_GLOB_H
 #include <glob.h>
 #endif
 #include <limits>
 #include <list>
 #if HAVE_PWD_H
 #include <pwd.h>
 #endif
 #if HAVE_GRP_H
 #include <grp.h>
 #endif
 #if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 
 #if USE_OPENSSL
 #include "ssl/gadgets.h"
 #endif
 
 #if USE_ADAPTATION
 static void parse_adaptation_service_set_type();
 static void parse_adaptation_service_chain_type();
@@ -916,60 +917,68 @@
                " Change client_request_buffer_max or request_header_max_size limits.",
                (uint32_t)Config.maxRequestBufferSize, (uint32_t)Config.maxRequestHeaderSize);
     }
 
     /*
      * Disable client side request pipelining if client_persistent_connections OFF.
      * Waste of resources queueing any pipelined requests when the first will close the connection.
      */
     if (Config.pipeline_max_prefetch > 0 && !Config.onoff.client_pconns) {
         debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: pipeline_prefetch " << Config.pipeline_max_prefetch <<
                " requires client_persistent_connections ON. Forced pipeline_prefetch 0.");
         Config.pipeline_max_prefetch = 0;
     }
 
 #if USE_AUTH
     /*
      * disable client side request pipelining. There is a race with
      * Negotiate and NTLM when the client sends a second request on an
      * connection before the authenticate challenge is sent. With
      * pipelining OFF, the client may fail to authenticate, but squid's
      * state will be preserved.
      */
     if (Config.pipeline_max_prefetch > 0) {
         Auth::Config *nego = Auth::Config::Find("Negotiate");
         Auth::Config *ntlm = Auth::Config::Find("NTLM");
         if ((nego && nego->active()) || (ntlm && ntlm->active())) {
             debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: pipeline_prefetch breaks NTLM and Negotiate authentication. Forced pipeline_prefetch 0.");
             Config.pipeline_max_prefetch = 0;
         }
     }
+
+    for (auto &authSchemes: Config.authSchemesConfigs) {
+        authSchemes.expand();
+        if (authSchemes.authConfigs.empty()) {
+            debugs(3, DBG_CRITICAL, "auth_schemes: at least one scheme name is required; got: " << authSchemes.rawSchemes);
+            self_destruct();
+        }
+    }
 #endif
 }
 
 /** Parse a line containing an obsolete directive.
  * To upgrade it where possible instead of just "Bungled config" for
  * directives which cannot be marked as simply aliases of the some name.
  * For example if the parameter order and content has changed.
  * Or if the directive has been completely removed.
  */
 void
 parse_obsolete(const char *name)
 {
     // Directives which have been radically changed rather than removed
     if (!strcmp(name, "url_rewrite_concurrency")) {
         int cval;
         parse_int(&cval);
         debugs(3, DBG_CRITICAL, "WARNING: url_rewrite_concurrency upgrade overriding url_rewrite_children settings.");
         Config.redirectChildren.concurrency = cval;
     }
 
     if (!strcmp(name, "log_access")) {
         self_destruct();
         return;
     }
 
     if (!strcmp(name, "log_icap")) {
         self_destruct();
         return;
     }
 
@@ -1313,61 +1322,61 @@
                           ae->name,
                           ae->typeString(),
                           ae->flags.flagsStr());
         dump_SBufList(entry, ae->dump());
         ae = ae->next;
     }
 }
 
 static void
 parse_acl(ACL ** ae)
 {
     ACL::ParseAclLine(LegacyParser, ae);
 }
 
 static void
 free_acl(ACL ** ae)
 {
     aclDestroyAcls(ae);
 }
 
 void
 dump_acl_list(StoreEntry * entry, ACLList * head)
 {
     dump_SBufList(entry, head->dump());
 }
 
 void
 dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
 {
     if (head)
-        dump_SBufList(entry, head->treeDump(name,NULL));
+        dump_SBufList(entry, head->treeDump(name, &Acl::AllowOrDeny));
 }
 
 static void
 parse_acl_access(acl_access ** head)
 {
     aclParseAccessLine(cfg_directive, LegacyParser, head);
 }
 
 static void
 free_acl_access(acl_access ** head)
 {
     aclDestroyAccessList(head);
 }
 
 static void
 dump_address(StoreEntry * entry, const char *name, Ip::Address &addr)
 {
     char buf[MAX_IPSTRLEN];
     storeAppendPrintf(entry, "%s %s\n", name, addr.toStr(buf,MAX_IPSTRLEN) );
 }
 
 static void
 parse_address(Ip::Address *addr)
 {
     char *token = ConfigParser::NextToken();
 
     if (!token) {
         self_destruct();
         return;
     }
@@ -1797,60 +1806,108 @@
         config->push_back(theScheme->createConfig());
         schemeCfg = Auth::Config::Find(type_str);
         if (schemeCfg == NULL) {
             debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
             self_destruct();
             return;
         }
     }
 
     schemeCfg->parse(schemeCfg, config->size(), param_str);
 }
 
 static void
 free_authparam(Auth::ConfigVector * cfg)
 {
     /* Wipe the Auth globals and Detach/Destruct component config + state. */
     cfg->clear();
 
     /* on reconfigure initialize new auth schemes for the new config. */
     if (reconfiguring) {
         Auth::Init();
     }
 }
 
 static void
 dump_authparam(StoreEntry * entry, const char *name, Auth::ConfigVector cfg)
 {
     for (Auth::ConfigVector::iterator  i = cfg.begin(); i != cfg.end(); ++i)
         (*i)->dump(entry, name, (*i));
 }
+
+static void
+ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl = nullptr)
+{
+    assert(access);
+    SBuf name;
+    if (!*access) {
+        *access = new Acl::Tree;
+        name.Printf("(%s rules)", desc);
+        (*access)->context(name.c_str(), config_input_line);
+    }
+    Acl::AndNode *rule = new Acl::AndNode;
+    name.Printf("(%s rule)", desc);
+    rule->context(name.c_str(), config_input_line);
+    acl ? rule->add(acl) : rule->lineParse();
+    (*access)->add(rule, action);
+}
+
+static void
+parse_AuthSchemes(acl_access **authSchemes)
+{
+    const char *tok = ConfigParser::NextQuotedToken();
+    if (!tok) {
+        debugs(29, DBG_CRITICAL, "FATAL: auth_schemes missing the parameter");
+        self_destruct();
+        return;
+    }
+    Config.authSchemesConfigs.push_back(Auth::SchemesConfig(tok, ConfigParser::LastTokenWasQuoted));
+    const allow_t action = allow_t(ACCESS_ALLOWED, Config.authSchemesConfigs.size() - 1);
+    ParseAclWithAction(authSchemes, action, "auth_schemes");
+}
+
+static void
+free_AuthSchemes(acl_access **authSchemes)
+{
+    Config.authSchemesConfigs.clear();
+    free_acl_access(authSchemes);
+}
+
+static void
+dump_AuthSchemes(StoreEntry *entry, const char *name, acl_access *authSchemes)
+{
+    if (authSchemes)
+        dump_SBufList(entry, authSchemes->treeDump(name, [](const allow_t &action) {
+                    return Config.authSchemesConfigs.at(action.kind).rawSchemes;
+        }));
+}
+
 #endif /* USE_AUTH */
 
 /* TODO: just return the object, the # is irrelevant */
 static int
 find_fstype(char *type)
 {
     for (size_t i = 0; i < StoreFileSystem::FileSystems().size(); ++i)
         if (strcasecmp(type, StoreFileSystem::FileSystems().at(i)->type()) == 0)
             return (int)i;
 
     return (-1);
 }
 
 static void
 parse_cachedir(Store::DiskConfig *swap)
 {
     char *type_str = ConfigParser::NextToken();
     if (!type_str) {
         self_destruct();
         return;
     }
 
     char *path_str = ConfigParser::NextToken();
     if (!path_str) {
         self_destruct();
         return;
     }
 
     int fs = find_fstype(type_str);
     if (fs < 0) {
@@ -4599,78 +4656,70 @@
     } else if (strcmp(bm, "allow") == 0) {
         debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
                "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
                "is usually inferior to the newer server-first "
                "bumping mode. Update your ssl_bump rules.");
         action.kind = Ssl::bumpClientFirst;
         bumpCfgStyleNow = bcsOld;
         sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst;
     } else if (strcmp(bm, "deny") == 0) {
         debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
                "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
                "your ssl_bump rules.");
         action.kind = Ssl::bumpNone;
         bumpCfgStyleNow = bcsOld;
         sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone;
     } else {
         debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
         self_destruct();
         return;
     }
 
     if (bumpCfgStyleLast != bcsNone && bumpCfgStyleNow != bumpCfgStyleLast) {
         debugs(3, DBG_CRITICAL, "FATAL: do not mix " << bumpCfgStyleNow << " actions with " <<
                bumpCfgStyleLast << " actions. Update your ssl_bump rules.");
         self_destruct();
         return;
     }
 
     bumpCfgStyleLast = bumpCfgStyleNow;
 
-    Acl::AndNode *rule = new Acl::AndNode;
-    rule->context("(ssl_bump rule)", config_input_line);
-    rule->lineParse();
     // empty rule OK
-
-    assert(ssl_bump);
-    if (!*ssl_bump) {
-        *ssl_bump = new Acl::Tree;
-        (*ssl_bump)->context("(ssl_bump rules)", config_input_line);
-    }
-
-    (*ssl_bump)->add(rule, action);
+    ParseAclWithAction(ssl_bump, action, "ssl_bump");
 }
 
 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
 {
     if (ssl_bump)
-        dump_SBufList(entry, ssl_bump->treeDump(name, Ssl::BumpModeStr));
+        dump_SBufList(entry, ssl_bump->treeDump(name, [](const allow_t &action) {
+                    return Ssl::BumpModeStr.at(action.kind);
+        }));
 }
 
 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
 {
     free_acl_access(ssl_bump);
 }
 
 #endif
 
 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
 {
     if (!headers)
         return;
 
     for (HeaderWithAclList::iterator hwa = headers->begin(); hwa != headers->end(); ++hwa) {
         storeAppendPrintf(entry, "%s %s %s", name, hwa->fieldName.c_str(), hwa->fieldValue.c_str());
         if (hwa->aclList)
             dump_acl_list(entry, hwa->aclList);
         storeAppendPrintf(entry, "\n");
     }
 }
 
 static void parse_HeaderWithAclList(HeaderWithAclList **headers)
 {
     char *fn;
     if (!*headers) {
         *headers = new HeaderWithAclList;
     }
     if ((fn = ConfigParser::NextToken()) == NULL) {
         self_destruct();
@@ -4743,85 +4792,80 @@
     char *t = ConfigParser::PeekAtToken();
     if (!t) {
         self_destruct();
         return;
     }
 
     if (!strcmp(t, "off")) {
         (void)ConfigParser::NextToken();
         ftpEpsvIsDeprecatedRule = true;
         ftpEpsvDeprecatedAction = allow_t(ACCESS_DENIED);
     } else if (!strcmp(t, "on")) {
         (void)ConfigParser::NextToken();
         ftpEpsvIsDeprecatedRule = true;
         ftpEpsvDeprecatedAction = allow_t(ACCESS_ALLOWED);
     }
 
     // Check for mixing "ftp_epsv on|off" and "ftp_epsv allow|deny .." rules:
     //   1) if this line is "ftp_epsv allow|deny ..." and already exist rules of "ftp_epsv on|off"
     //   2) if this line is "ftp_epsv on|off" and already exist rules of "ftp_epsv allow|deny ..."
     // then abort
     if ((!ftpEpsvIsDeprecatedRule && FtpEspvDeprecated) ||
             (ftpEpsvIsDeprecatedRule && !FtpEspvDeprecated && *ftp_epsv != NULL)) {
         debugs(3, DBG_CRITICAL, "FATAL: do not mix \"ftp_epsv on|off\" cfg lines with \"ftp_epsv allow|deny ...\" cfg lines. Update your ftp_epsv rules.");
         self_destruct();
         return;
     }
 
     if (ftpEpsvIsDeprecatedRule) {
         // overwrite previous ftp_epsv lines
         delete *ftp_epsv;
+        *ftp_epsv = nullptr;
+
         if (ftpEpsvDeprecatedAction == allow_t(ACCESS_DENIED)) {
-            Acl::AndNode *ftpEpsvRule = new Acl::AndNode;
-            ftpEpsvRule->context("(ftp_epsv rule)", config_input_line);
-            ACL *a = ACL::FindByName("all");
-            if (!a) {
-                delete ftpEpsvRule;
+            if (ACL *a = ACL::FindByName("all"))
+                ParseAclWithAction(ftp_epsv, ftpEpsvDeprecatedAction, "ftp_epsv", a);
+            else {
                 self_destruct();
                 return;
             }
-            ftpEpsvRule->add(a);
-            *ftp_epsv = new Acl::Tree;
-            (*ftp_epsv)->context("(ftp_epsv rules)", config_input_line);
-            (*ftp_epsv)->add(ftpEpsvRule, ftpEpsvDeprecatedAction);
-        } else
-            *ftp_epsv = NULL;
+        }
         FtpEspvDeprecated = true;
     } else {
         aclParseAccessLine(cfg_directive, LegacyParser, ftp_epsv);
     }
 }
 
 static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
 {
     if (ftp_epsv)
-        dump_SBufList(entry, ftp_epsv->treeDump(name, NULL));
+        dump_SBufList(entry, ftp_epsv->treeDump(name, Acl::AllowOrDeny));
 }
 
 static void free_ftp_epsv(acl_access **ftp_epsv)
 {
     free_acl_access(ftp_epsv);
     FtpEspvDeprecated = false;
 }
 
 static void
 parse_UrlHelperTimeout(SquidConfig::UrlHelperTimeout *config)
 {
     time_msec_t tval;
     parseTimeLine(&tval, T_SECOND_STR, false, true);
     Config.Timeout.urlRewrite = static_cast<time_t>(tval/1000);
 
     char *key, *value;
     while(ConfigParser::NextKvPair(key, value)) {
         if (strcasecmp(key, "on_timeout") == 0) {
             if (strcasecmp(value, "bypass") == 0)
                 config->action = toutActBypass;
             else if (strcasecmp(value, "fail") == 0)
                 config->action = toutActFail;
             else if (strcasecmp(value, "retry") == 0)
                 config->action = toutActRetry;
             else if (strcasecmp(value, "use_configured_response") == 0) {
                 config->action = toutActUseConfiguredResponse;
             } else {
                 debugs(3, DBG_CRITICAL, "FATAL: unsuported \"on_timeout\"  action:" << value);
                 self_destruct();
                 return;
@@ -4892,65 +4936,56 @@
     dump_onoff(entry, name, val);
 }
 
 static void
 free_configuration_includes_quoted_values(bool *)
 {
     ConfigParser::RecognizeQuotedValues = false;
     ConfigParser::StrictMode = false;
 }
 
 static void
 parse_on_unsupported_protocol(acl_access **access)
 {
     char *tm;
     if ((tm = ConfigParser::NextToken()) == NULL) {
         self_destruct();
         return;
     }
 
     allow_t action = allow_t(ACCESS_ALLOWED);
     if (strcmp(tm, "tunnel") == 0)
         action.kind = 1;
     else if (strcmp(tm, "respond") == 0)
         action.kind = 2;
     else {
         debugs(3, DBG_CRITICAL, "FATAL: unknown on_unsupported_protocol mode: " << tm);
         self_destruct();
         return;
     }
 
-    Acl::AndNode *rule = new Acl::AndNode;
-    rule->context("(on_unsupported_protocol rule)", config_input_line);
-    rule->lineParse();
     // empty rule OK
-
-    assert(access);
-    if (!*access) {
-        *access = new Acl::Tree;
-        (*access)->context("(on_unsupported_protocol rules)", config_input_line);
-    }
-
-    (*access)->add(rule, action);
+    ParseAclWithAction(access, action, "on_unsupported_protocol");
 }
 
 static void
 dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access)
 {
-    const char *on_error_tunnel_mode_str[] = {
+    static const std::vector<const char *> onErrorTunnelMode = {
         "none",
         "tunnel",
-        "respond",
-        NULL
+        "respond"
     };
     if (access) {
-        SBufList lines = access->treeDump(name, on_error_tunnel_mode_str);
+        SBufList lines = access->treeDump(name, [](const allow_t &action) {
+                return onErrorTunnelMode.at(action.kind);
+        });
         dump_SBufList(entry, lines);
     }
 }
 
 static void
 free_on_unsupported_protocol(acl_access **access)
 {
     free_acl_access(access);
 }
 

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2016-03-30 11:55:02 +0000
+++ src/cf.data.depend	2016-11-18 15:03:18 +0000
@@ -1,48 +1,49 @@
 ## Copyright (C) 1996-2016 The Squid Software Foundation and contributors
 ##
 ## Squid software is distributed under GPLv2+ license and includes
 ## contributions from numerous individuals and organizations.
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 #
 # type			dependencies
 #
 access_log		acl	logformat
 acl			external_acl_type auth_param
 acl_access		acl
 acl_address		acl
 acl_b_size_t		acl
 acl_tos			acl
 acl_nfmark		acl
 address
 authparam
+AuthSchemes
 b_int64_t
 b_size_t
 b_ssize_t
 cachedir		cache_replacement_policy
 cachemgrpasswd
 ConfigAclTos
 configuration_includes_quoted_values
 CpuAffinityMap
 debug
 delay_pool_access	acl	delay_class
 delay_pool_class	delay_pools
 delay_pool_count
 delay_pool_rates	delay_class
 client_delay_pool_access	acl
 client_delay_pool_count
 client_delay_pool_rates
 denyinfo		acl
 eol
 externalAclHelper	auth_param
 HelperChildConfig
 hostdomain		cache_peer
 hostdomaintype		cache_peer
 http_header_access	acl
 http_header_replace
 HeaderWithAclList	acl
 adaptation_access_type	adaptation_service_set adaptation_service_chain acl icap_service icap_class
 adaptation_service_set_type	icap_service ecap_service
 adaptation_service_chain_type	icap_service ecap_service
 icap_access_type	icap_class acl
 icap_class_type		icap_service

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2016-10-28 08:31:53 +0000
+++ src/cf.data.pre	2016-11-18 15:03:18 +0000
@@ -744,60 +744,105 @@
 	This is a trade-off between memory utilization (long intervals - say
 	2 days) and CPU (short intervals - say 1 minute). Only change if you
 	have good reason to.
 DOC_END
 
 NAME: authenticate_ttl
 TYPE: time_t
 DEFAULT: 1 hour
 LOC: Config.authenticateTTL
 DOC_START
 	The time a user & their credentials stay in the logged in
 	user cache since their last request. When the garbage
 	interval passes, all user credentials that have passed their
 	TTL are removed from memory.
 DOC_END
 
 NAME: authenticate_ip_ttl
 TYPE: time_t
 LOC: Config.authenticateIpTTL
 DEFAULT: 1 second
 DOC_START
 	If you use proxy authentication and the 'max_user_ip' ACL,
 	this directive controls how long Squid remembers the IP
 	addresses associated with each user.  Use a small value
 	(e.g., 60 seconds) if your users might change addresses
 	quickly, as is the case with dialup.   You might be safe
 	using a larger value (e.g., 2 hours) in a corporate LAN
 	environment with relatively static address assignments.
 DOC_END
 
+NAME: auth_schemes
+TYPE: AuthSchemes
+IFDEF: USE_AUTH
+LOC: Config.accessList.authSchemes
+DEFAULT: none
+DEFAULT_DOC: use all auth_param schemes in their configuration order
+DOC_START
+	Use this directive to customize authentication schemes presence and
+	order in Squid's Unauthorized and Authentication Required responses.
+
+		auth_schemes scheme1,scheme2,... [!]aclname ...
+
+	where schemeN is the name of one of the authentication schemes
+	configured using auth_param directives. At least one scheme name is
+	required. Multiple scheme names are separated by commas. Either
+	avoid whitespace or quote the entire schemes list.
+
+	A special "ALL" scheme name expands to all auth_param-configured
+	schemes in their configuration order. This directive cannot be used
+	to configure Squid to offer no authentication schemes at all.
+
+	The first matching auth_schemes rule determines the schemes order
+	for the current Authentication Required transaction. Note that the
+	future response is not yet available during auth_schemes evaluation.
+
+	If this directive is not used or none of its rules match, then Squid
+	responds with all configured authentication schemes in the order of
+	auth_param directives in the configuration file.
+
+	This directive does not determine when authentication is used or
+	how each authentication scheme authenticates clients.
+
+	The following example sends basic and negotiate authentication
+	schemes, in that order, when requesting authentication of HTTP
+	requests matching the isIE ACL (not shown) while sending all
+	auth_param schemes in their configuration order to other clients:
+
+		auth_schemes basic,negotiate isIE
+		auth_schemes ALL all # explicit default
+
+	This directive supports fast ACLs only.
+
+	See also: auth_param.
+DOC_END
+
 COMMENT_START
  ACCESS CONTROLS
  -----------------------------------------------------------------------------
 COMMENT_END
 
 NAME: external_acl_type
 TYPE: externalAclHelper
 LOC: Config.externalAclHelperList
 DEFAULT: none
 DOC_START
 	This option defines external acl classes using a helper program
 	to look up the status
 
 	  external_acl_type name [options] FORMAT /path/to/helper [helper arguments]
 
 	Options:
 
 	  ttl=n		TTL in seconds for cached results (defaults to 3600
 			for 1 hour)
 
 	  negative_ttl=n
 			TTL for cached negative lookups (default same
 			as ttl)
 
 	  grace=n	Percentage remaining of TTL where a refresh of a
 			cached entry should be initiated without needing to
 			wait for a new reply. (default is for no grace period)
 
 	  cache=n	The maximum number of entries in the result cache. The
 			default limit is 262144 entries.  Each cache entry usually

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2016-11-08 06:34:59 +0000
+++ src/ssl/support.cc	2016-11-18 15:03:18 +0000
@@ -17,71 +17,70 @@
 
 #include "acl/FilledChecklist.h"
 #include "anyp/PortCfg.h"
 #include "fatal.h"
 #include "fd.h"
 #include "fde.h"
 #include "globals.h"
 #include "ipc/MemMap.h"
 #include "security/CertError.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "ssl/bio.h"
 #include "ssl/Config.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/gadgets.h"
 #include "ssl/support.h"
 #include "URL.h"
 
 #include <cerrno>
 
 // TODO: Move ssl_ex_index_* global variables from global.cc here.
 int ssl_ex_index_ssl_untrusted_chain = -1;
 
 Ipc::MemMap *Ssl::SessionCache = NULL;
 const char *Ssl::SessionCacheName = "ssl_session_cache";
 
 static Ssl::CertsIndexedList SquidUntrustedCerts;
 
 const EVP_MD *Ssl::DefaultSignHash = NULL;
 
-const char *Ssl::BumpModeStr[] = {
+std::vector<const char *> Ssl::BumpModeStr = {
     "none",
     "client-first",
     "server-first",
     "peek",
     "stare",
     "bump",
     "splice",
-    "terminate",
-    /*"err",*/
-    NULL
+    "terminate"
+    /*,"err"*/
 };
 
 /**
  \defgroup ServerProtocolSSLInternal Server-Side SSL Internals
  \ingroup ServerProtocolSSLAPI
  */
 
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata)
 {
     FILE *in;
     int len = 0;
     char cmdline[1024];
 
     snprintf(cmdline, sizeof(cmdline), "\"%s\" \"%s\"", Config.Program.ssl_password, (const char *)userdata);
     in = popen(cmdline, "r");
 
     if (fgets(buf, size, in))
 
         len = strlen(buf);
 
     while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
         --len;
 
     buf[len] = '\0';
 
     pclose(in);
 
     return len;

=== modified file 'src/ssl/support.h'
--- src/ssl/support.h	2016-11-07 10:49:37 +0000
+++ src/ssl/support.h	2016-11-18 15:03:18 +0000
@@ -124,69 +124,69 @@
 const char *sslGetUserCertificateChainPEM(SSL *ssl);
 
 namespace Ssl
 {
 /// \ingroup ServerProtocolSSLAPI
 typedef char const *GETX509ATTRIBUTE(X509 *, const char *);
 
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509UserAttribute;
 
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509CAAttribute;
 
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509Fingerprint;
 
 extern const EVP_MD *DefaultSignHash;
 
 /**
   \ingroup ServerProtocolSSLAPI
  * Supported ssl-bump modes
  */
 enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeek, bumpStare, bumpBump, bumpSplice, bumpTerminate, /*bumpErr,*/ bumpEnd};
 
 enum BumpStep {bumpStep1, bumpStep2, bumpStep3};
 
 /**
  \ingroup  ServerProtocolSSLAPI
  * Short names for ssl-bump modes
  */
-extern const char *BumpModeStr[];
+extern std::vector<const char *>BumpModeStr;
 
 /**
  \ingroup ServerProtocolSSLAPI
  * Return the short name of the ssl-bump mode "bm"
  */
 inline const char *bumpMode(int bm)
 {
-    return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr[bm] : NULL;
+    return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr.at(bm) : NULL;
 }
 
 /// certificates indexed by issuer name
 typedef std::multimap<SBuf, X509 *> CertsIndexedList;
 
 /**
  * Load PEM-encoded certificates from the given file.
  */
 bool loadCerts(const char *certsFile, Ssl::CertsIndexedList &list);
 
 /**
  * Load PEM-encoded certificates to the squid untrusteds certificates
  * internal DB from the given file.
  */
 bool loadSquidUntrusted(const char *path);
 
 /**
  * Removes all certificates from squid untrusteds certificates
  * internal DB and frees all memory
  */
 void unloadSquidUntrusted();
 
 /**
  * Add the certificate cert to ssl object untrusted certificates.
  * Squid uses an attached to SSL object list of untrusted certificates,
  * with certificates which can be used to complete incomplete chains sent
  * by the SSL server.
  */
 void SSL_add_untrusted_cert(SSL *ssl, X509 *cert);
 

=== modified file 'src/tests/stub_libauth.cc'
--- src/tests/stub_libauth.cc	2016-01-01 00:12:18 +0000
+++ src/tests/stub_libauth.cc	2016-11-18 15:03:18 +0000
@@ -47,32 +47,35 @@
 #include "auth/UserRequest.h"
 char const * Auth::UserRequest::username() const STUB_RETVAL("stub_username")
 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
 Auth::UserRequest::UserRequest() STUB
 Auth::UserRequest::~UserRequest() STUB
 void Auth::UserRequest::setDenyMessage(char const *) STUB
 char const * Auth::UserRequest::getDenyMessage() STUB_RETVAL("stub")
 char const * Auth::UserRequest::denyMessage(char const * const) STUB_RETVAL("stub")
 void authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer, Ip::Address const &) STUB
 void authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer) STUB
 int authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer) STUB_RETVAL(0)
 int authenticateUserAuthenticated(Auth::UserRequest::Pointer) STUB_RETVAL(0)
 Auth::Direction Auth::UserRequest::direction() STUB_RETVAL(Auth::CRED_ERROR)
 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::HdrType, HttpRequest *, ConnStateData *, Ip::Address &, AccessLogEntry::Pointer &) STUB_RETVAL(AUTH_AUTHENTICATED)
 AuthAclState Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer *, Http::HdrType, 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
 Auth::Scheme::Pointer Auth::UserRequest::scheme() const STUB_RETVAL(NULL)
 
 #include "AuthReg.h"
 void Auth::Init() STUB_NOP
 
+#include "auth/SchemesConfig.h"
+void Auth::SchemesConfig::expand() STUB
+
 #endif /* USE_AUTH */
 

=== modified file 'src/tests/stub_libsslsquid.cc'
--- src/tests/stub_libsslsquid.cc	2016-09-22 14:21:12 +0000
+++ src/tests/stub_libsslsquid.cc	2016-11-18 15:04:39 +0000
@@ -39,45 +39,45 @@
 { fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0,0); return &v; }
 void Ssl::GlobalContextStorage::reconfigureStart() STUB
 //Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;
 
 #include "ssl/ErrorDetail.h"
 Security::ErrorCode parseErrorString(const char *name) STUB_RETVAL(0)
 //const char *Ssl::getErrorName(Security::ErrorCode value) STUB_RETVAL(NULL)
 Ssl::ErrorDetail::ErrorDetail(Security::ErrorCode, X509 *, X509 *, const char *) STUB
 Ssl::ErrorDetail::ErrorDetail(ErrorDetail const &) STUB
 const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
 
 #include "ssl/support.h"
 namespace Ssl
 {
 bool InitServerContext(Security::ContextPointer &, AnyP::PortCfg &) STUB_RETVAL(false)
 bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, long, const char *) STUB_RETVAL(false)
 } // namespace Ssl
 int ssl_read_method(int, char *, int) STUB_RETVAL(0)
 int ssl_write_method(int, const char *, int) STUB_RETVAL(0)
 void ssl_shutdown_method(SSL *ssl) STUB
 const char *sslGetUserEmail(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
 const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
 const char *sslGetUserCertificatePEM(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserCertificateChainPEM(SSL *ssl) STUB_RETVAL(NULL)
 namespace Ssl
 {
 //GETX509ATTRIBUTE GetX509UserAttribute;
 //GETX509ATTRIBUTE GetX509CAAttribute;
 //GETX509ATTRIBUTE GetX509Fingerprint;
-const char *BumpModeStr[] = {""};
+std::vector<const char *> BumpModeStr = {""};
 bool generateUntrustedCert(Security::CertPointer & untrustedCert, EVP_PKEY_Pointer & untrustedPkey, Security::CertPointer const & cert, EVP_PKEY_Pointer const & pkey) STUB_RETVAL(false)
 Security::ContextPointer generateSslContext(CertificateProperties const &, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
 bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &) STUB_RETVAL(false)
 Security::ContextPointer generateSslContextUsingPkeyAndCertFromMemory(const char *, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
 void addChainToSslContext(Security::ContextPointer &, STACK_OF(X509) *) STUB
 void readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename) STUB
 int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data,  ASN1_STRING *cn_data)) STUB_RETVAL(0)
 bool checkX509ServerValidity(X509 *cert, const char *server) STUB_RETVAL(false)
 int asn1timeToString(ASN1_TIME *tm, char *buf, int len) STUB_RETVAL(0)
 bool setClientSNI(SSL *ssl, const char *fqdn) STUB_RETVAL(false)
 } //namespace Ssl
 
 #endif
 

_______________________________________________
squid-dev mailing list
squid-dev@lists.squid-cache.org
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to