]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Added auth_schemes to control schemes presence and order in 401s/407s.
authorEduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Sat, 10 Dec 2016 04:48:25 +0000 (21:48 -0700)
committerAlex Rousskov <rousskov@measurement-factory.com>
Sat, 10 Dec 2016 04:48:25 +0000 (21:48 -0700)
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.

src/SquidConfig.h
src/auth/Config.cc
src/auth/Config.h
src/auth/Makefile.am
src/auth/SchemesConfig.cc [new file with mode: 0644]
src/auth/SchemesConfig.h [new file with mode: 0644]
src/auth/UserRequest.cc
src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/tests/stub_libauth.cc

index 8710c4aa493d6957318a2a8b674cb13fef8e1461..799a2aa6575ddb347c1c067d51ab905cf37ddace 100644 (file)
@@ -10,6 +10,9 @@
 #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"
@@ -397,6 +400,9 @@ public:
 
         acl_access *forceRequestBodyContinuation;
         acl_access *serverPconnForNonretriable;
+#if USE_AUTH
+        acl_access *authSchemes;
+#endif
     } accessList;
     AclDenyInfoList *denyInfoList;
 
@@ -538,6 +544,9 @@ public:
         ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
     } dns;
 
+#if USE_AUTH
+    Auth::SchemesConfigs authSchemesConfigs;
+#endif
 };
 
 extern SquidConfig Config;
index 5495ba8056b579bfcf7d10141adf9e7f84af04e0..2d45e572c08d9fb4f8740e37cdb6a48cdf21f09d 100644 (file)
@@ -65,6 +65,15 @@ Auth::Config::Find(const char *proxy_auth)
     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)
index e042d9d42fb56e7a7cf2e1a86923aac4efd764f9..deae9bdaad49e9449f0acd35e42e59ed5fac5092 100644 (file)
@@ -49,6 +49,9 @@ 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() {}
index 6bdf6a3f8ac397f3ab781317e4d04fdb255f851b..f61083a3f55de11d64a16caad9d774eb4630a7de 100644 (file)
@@ -30,6 +30,8 @@ libauth_la_SOURCES = \
        QueueNode.h \
        Scheme.cc \
        Scheme.h \
+       SchemesConfig.h \
+       SchemesConfig.cc \
        State.h \
        State.cc \
        User.h \
diff --git a/src/auth/SchemesConfig.cc b/src/auth/SchemesConfig.cc
new file mode 100644 (file)
index 0000000..f86c6ec
--- /dev/null
@@ -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());
+    });
+}
+
diff --git a/src/auth/SchemesConfig.h b/src/auth/SchemesConfig.h
new file mode 100644 (file)
index 0000000..3392c98
--- /dev/null
@@ -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 */
+
index 6e2cc08c36491f9b7268c6e83ec7a9ecfd95b5d0..5647219ca8def801c5a0ce0ee638ae7a0d4e2dc4 100644 (file)
  * 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"
@@ -25,6 +27,7 @@
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
+#include "SquidConfig.h"
 
 /* Generic Functions */
 
@@ -461,6 +464,20 @@ Auth::UserRequest::tryToAuthenticateAndSetAuthUser(Auth::UserRequest::Pointer *
     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!) */
@@ -498,8 +515,8 @@ Auth::UserRequest::addReplyAuthHeader(HttpReply * rep, Auth::UserRequest::Pointe
             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()) {
index 4e2e6695496fcb213c9e4435152771f2fb00e191..b62ec8546da8dc421a117f4b26dd501de178d0ca 100644 (file)
@@ -80,6 +80,7 @@
 #if USE_AUTH
 #include "auth/Config.h"
 #include "auth/Scheme.h"
+#include "auth/SchemesConfig.h"
 #endif
 #if USE_SQUID_ESI
 #include "esi/Parser.h"
@@ -245,6 +246,7 @@ static void free_configuration_includes_quoted_values(bool *recognizeQuotedValue
 static void parse_on_unsupported_protocol(acl_access **access);
 static void dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access);
 static void free_on_unsupported_protocol(acl_access **access);
+static void ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl = nullptr);
 
 /*
  * LegacyParser is a parser for legacy code that uses the global
@@ -943,6 +945,14 @@ configDoConfigure(void)
             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
 }
 
@@ -1824,10 +1834,41 @@ 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
+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 */
 
 static void
-ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl = nullptr)
+ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl)
 {
     assert(access);
     SBuf name;
index 6f325a7388237b2f3b999e3d9fca50a16104120a..f1d23599b11aaa46b2a8ba6cbf6ca0abe7668322 100644 (file)
@@ -16,6 +16,7 @@ acl_tos                       acl
 acl_nfmark             acl
 address
 authparam
+AuthSchemes
 b_int64_t
 b_size_t
 b_ssize_t
index 79f1e3d0dca7735b51fc7b19b5126bba7e20cd08..cf495e37720a9617a99e2062795fe53136b06d22 100644 (file)
@@ -771,6 +771,51 @@ DOC_START
        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
  -----------------------------------------------------------------------------
index 57149474f8904f2c0ecbbd999d0d587e0ebffe77..5cfa53a876640d5fd7c6949d3fb68455935c4f81 100644 (file)
@@ -74,5 +74,8 @@ 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 */