]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Ban acl key changes in req_header, rep_header, and note ACLs (#1210)
authorAlex Rousskov <rousskov@measurement-factory.com>
Mon, 12 Dec 2022 13:34:12 +0000 (13:34 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Tue, 13 Dec 2022 04:19:22 +0000 (04:19 +0000)
* req_header and rep_header ACLs; the key is the header-name argument:

    acl barred req_header X-1 bar
    acl barred req_header X-2 bar # never matches since commit a0b240c
    http_access deny barred

* the "note" ACL; the key is the annotation name argument:

    acl banned note color green  # never matches since commit 39baccc
    acl banned note weight heavy
    http_access deny banned

Squid did write a cache.log ERROR for req_header/rep_header key changes
but was silent about the preceding "note" ACL rules being ineffective
after a key change (e.g., Squid was not denying "green" requests above).

Now, Squid rejects configurations containing such ACL key changes.

Also significantly reduced the corresponding parsing code duplication.

src/ConfigParser.cc
src/ConfigParser.h
src/acl/HttpHeaderData.cc
src/acl/NoteData.cc
src/tests/stub_acl.cc

index d8a426ace3b1ca8066af1d0ca89a79ccdac9589c..4fa3214d4f189e1cd2560c4350fdb8d5df3cfbd0 100644 (file)
@@ -504,6 +504,35 @@ ConfigParser::regex(const char *expectedRegexDescription)
     return std::unique_ptr<RegexPattern>(new RegexPattern(pattern, flags));
 }
 
+void
+ConfigParser::SetAclKey(SBuf &keyStorage, const char *keyParameterName)
+{
+    extern const char *AclMatchedName;
+
+    const auto newKey = strtokFile();
+    if (!newKey) {
+        throw TextException(ToSBuf("An acl declaration is missing a ", keyParameterName,
+                                   Debug::Extra, "ACL name: ", AclMatchedName),
+                            Here());
+    }
+
+    if (keyStorage.isEmpty()) {
+        keyStorage = newKey;
+        return;
+    }
+
+    if (keyStorage.caseCmp(newKey) == 0)
+        return; // no change
+
+    throw TextException(ToSBuf("Attempt to change the value of the ", keyParameterName, " argument in a subsequent acl declaration:",
+                               Debug::Extra, "previously seen value: ", keyStorage,
+                               Debug::Extra, "new/conflicting value: ", newKey,
+                               Debug::Extra, "ACL name: ", AclMatchedName,
+                               Debug::Extra, "advice: Use a dedicated ACL name for each distinct ", keyParameterName,
+                               " (and group those ACLs together using an 'any-of' ACL)."),
+                        Here());
+}
+
 CachePeer &
 ConfigParser::cachePeer(const char *peerNameTokenDescription)
 {
index 8f205639207f418ee012a1c5c7dfc2a7a7489e29..7039ffeb276e55346fd7f8bec9f3a8e74e7bf123 100644 (file)
@@ -129,6 +129,13 @@ public:
      */
     static bool NextKvPair(char * &key, char * &value);
 
+    // TODO: Convert into a non-static method after exposing the current parser.
+    /// Extract, validate, and store the ACL key parameter for ACL types
+    /// declared using "acl aclname type key argument..." declaration that
+    /// require unique key values for each aclname+type combination.
+    /// Key comparison is case-insensitive.
+    static void SetAclKey(SBuf &keyStorage, const char *keyParameterName);
+
     /**
      * Preview the next token. The next NextToken() and strtokFile() call
      * will return the same token.
index 7004393ad7b77599daa3aba505269e9118f166d4..c3bafedb26cae7c5baf97471e42cc211a18cfa3f 100644 (file)
@@ -75,22 +75,8 @@ ACLHTTPHeaderData::lineOptions()
 void
 ACLHTTPHeaderData::parse()
 {
-    char* t = ConfigParser::strtokFile();
-    if (!t) {
-        debugs(28, DBG_CRITICAL, "ERROR: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
-        debugs(28, DBG_CRITICAL, "ERROR: Missing header name in ACL");
-        return;
-    }
-
-    if (hdrName.isEmpty()) {
-        hdrName = t;
-        hdrId = Http::HeaderLookupTable.lookup(hdrName).id;
-    } else if (hdrName.caseCmp(t) != 0) {
-        debugs(28, DBG_CRITICAL, "ERROR: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
-        debugs(28, DBG_CRITICAL, "ERROR: ACL cannot match both " << hdrName << " and " << t << " headers. Use 'any-of' ACL instead.");
-        return;
-    }
-
+    ConfigParser::SetAclKey(hdrName, "header-name");
+    hdrId = Http::HeaderLookupTable.lookup(hdrName).id;
     regex_rule->parse();
 }
 
index 428b94599aad649c8560276d077157af9b403603..8dc5a3a0728fb8fb2b5a0363c76e865a4cba59c0 100644 (file)
@@ -47,9 +47,7 @@ ACLNoteData::dump() const
 void
 ACLNoteData::parse()
 {
-    char* t = ConfigParser::strtokFile();
-    assert (t != nullptr);
-    name = t;
+    ConfigParser::SetAclKey(name, "annotation name");
     values->parse();
 }
 
index cf942a792373890352828a8e0b95e63735c0f7e6..f4a95908ef8b01943d43e3093a8c3b521ff6df52 100644 (file)
@@ -9,10 +9,13 @@
 /* DEBUG: section 28    Access Control */
 
 #include "squid.h"
-#include "acl/Gadgets.h"
 
 #define STUB_API "acl/"
 #include "tests/STUB.h"
 
+#include "acl/Acl.h"
+const char *AclMatchedName = nullptr;
+
+#include "acl/Gadgets.h"
 size_t aclParseAclList(ConfigParser &, Acl::Tree **, const char *) STUB_RETVAL(0)