This is the patch file, tested successfully on squid 6.x and 7.x

--- src/acl/UserData.h.orig     2026-03-06 16:23:44.415748741 +0000
+++ src/acl/UserData.h  2026-03-06 16:23:44.431748826 +0000
@@ -34,11 +34,15 @@
     /* ACLData API */
     const Acl::Options &lineOptions() override;

-    typedef std::set<SBuf,bool(*)(const SBuf&, const SBuf&)> UserDataNames_t;
-    UserDataNames_t userDataNames;
+    typedef std::set<SBuf, bool(*)(const SBuf&, const SBuf&)> UserDataNames_t;
+
+    /// names added while -i (case-insensitive) was off
+    UserDataNames_t caseSensitiveNames;
+
+    /// names added while -i (case-insensitive) was on; stored lowercased
+    UserDataNames_t caseInsensitiveNames;

     struct {
-        bool case_insensitive;
         bool required;
     } flags;

--- src/acl/UserData.cc.orig    2026-03-06 16:23:44.435748847 +0000
+++ src/acl/UserData.cc 2026-03-06 16:23:44.459748972 +0000
@@ -23,7 +23,7 @@
 bool
 ACLUserData::match(char const *user)
 {
-    debugs(28, 7, "user is " << user << ", case_insensitive is " << flags.case_insensitive);
+    debugs(28, 7, "user is " << user);

     if (user == nullptr || strcmp(user, "-") == 0)
         return 0;
@@ -33,9 +33,26 @@
         return 1;
     }

-    bool result = (userDataNames.find(SBuf(user)) != userDataNames.end());
-    debugs(28, 7, "returning " << result);
-    return result;
+    const SBuf userKey(user);
+
+    // check case-sensitive set first (exact match)
+    if (caseSensitiveNames.find(userKey) != caseSensitiveNames.end()) {
+        debugs(28, 7, "returning 1 (case-sensitive match)");
+        return 1;
+    }
+
+    // check case-insensitive set (lowercased lookup)
+    if (!caseInsensitiveNames.empty()) {
+        SBuf lowerUser(userKey);
+        lowerUser.toLower();
+        if (caseInsensitiveNames.find(lowerUser) != caseInsensitiveNames.end()) {
+            debugs(28, 7, "returning 1 (case-insensitive match)");
+            return 1;
+        }
+    }
+
+    debugs(28, 7, "returning 0");
+    return 0;
 }

 SBufList
@@ -48,14 +65,18 @@
         return sl;
     }

-    if (flags.case_insensitive)
-        sl.push_back(SBuf("-i"));
+    // dump case-sensitive names first (no flag needed)
+    sl.insert(sl.end(), caseSensitiveNames.begin(), caseSensitiveNames.end());

-    sl.insert(sl.end(), userDataNames.begin(), userDataNames.end());
+    // dump case-insensitive names with -i prefix
+    if (!caseInsensitiveNames.empty()) {
+        sl.push_back(SBuf("-i"));
+        sl.insert(sl.end(), caseInsensitiveNames.begin(), caseInsensitiveNames.end());
+    }

-    debugs(28,5, "ACLUserData dump output: " <<
-           JoinContainerToSBuf(userDataNames.begin(), userDataNames.end(),
-                               SBuf(" ")));
+    debugs(28, 5, "ACLUserData dump output: " <<
+           caseSensitiveNames.size() << " case-sensitive, " <<
+           caseInsensitiveNames.size() << " case-insensitive users");
     return sl;
 }

@@ -72,9 +93,9 @@
 }

 ACLUserData::ACLUserData() :
-    userDataNames(CaseSensitiveSBufCompare)
+    caseSensitiveNames(CaseSensitiveSBufCompare),
+    caseInsensitiveNames(CaseInsensitveSBufCompare)
 {
-    flags.case_insensitive = false;
     flags.required = false;
 }

@@ -91,63 +112,61 @@
 ACLUserData::parse()
 {
     debugs(28, 2, "parsing user list");
-    flags.case_insensitive = bool(CaseInsensitive_);
+
+    bool caseInsensitive = bool(CaseInsensitive_);

     char *t = nullptr;
-    if ((t = ConfigParser::strtokFile())) {
+    while ((t = ConfigParser::strtokFile())) {
         SBuf s(t);
-        debugs(28, 5, "first token is " << s);
-
-        if (s.cmp("-i",2) == 0) {
-            debugs(28, 5, "Going case-insensitive");
-            flags.case_insensitive = true;
-            // due to how the std::set API work, if we want to change
-            // the comparison function we have to create a new std::set
-            UserDataNames_t newUdn(CaseInsensitveSBufCompare);
-            newUdn.insert(userDataNames.begin(), userDataNames.end());
-            swap(userDataNames,newUdn);
-        } else if (s.cmp("REQUIRED") == 0) {
-            debugs(28, 5, "REQUIRED-type enabled");
-            flags.required = true;
-        } else {
-            if (flags.case_insensitive)
-                s.toLower();
+        debugs(28, 6, "Got token: " << s);

-            debugs(28, 6, "Adding user " << s);
-            userDataNames.insert(s);
+        if (s.cmp("-i", 2) == 0) {
+            debugs(28, DBG_IMPORTANT, "WARNING: ACL uses '-i' as a token in the user list; " <<
+                   "use 'acl ... proxy_auth -i ...' line option instead");
+            continue;
         }
-    }
-
-    debugs(28, 3, "Case-insensitive-switch is " << flags.case_insensitive);
-    /* we might inherit from a previous declaration */

-    debugs(28, 4, "parsing following tokens");
+        if (s.cmp("+i", 2) == 0) {
+            debugs(28, DBG_IMPORTANT, "WARNING: ACL uses '+i' as a token in the user list; " <<
+                   "use 'acl ... proxy_auth +i ...' line option instead");
+            continue;
+        }

-    while ((t = ConfigParser::strtokFile())) {
-        SBuf s(t);
-        debugs(28, 6, "Got token: " << s);
+        if (s.cmp("REQUIRED") == 0) {
+            debugs(28, 5, "REQUIRED-type enabled");
+            flags.required = true;
+            continue;
+        }

-        if (flags.case_insensitive)
+        if (caseInsensitive) {
             s.toLower();
-
-        debugs(28, 6, "Adding user " << s);
-        userDataNames.insert(s);
+            debugs(28, 6, "Adding user (case-insensitive) " << s);
+            caseInsensitiveNames.insert(s);
+        } else {
+            debugs(28, 6, "Adding user (case-sensitive) " << s);
+            caseSensitiveNames.insert(s);
+        }
     }

-    if (flags.required && !userDataNames.empty()) {
+    if (flags.required && (!caseSensitiveNames.empty() || !caseInsensitiveNames.empty())) {          debugs(28, DBG_PARSE_NOTE(1), "WARNING: detected attempt to add usernames to an acl of type REQUIRED");
-        userDataNames.clear();
+        caseSensitiveNames.clear();
+        caseInsensitiveNames.clear();
     }

-    debugs(28,4, "ACL contains " << userDataNames.size() << " users");
+    debugs(28, 4, "ACL contains " << caseSensitiveNames.size() <<
+           " case-sensitive and " << caseInsensitiveNames.size() <<
+           " case-insensitive users");
 }

 bool
 ACLUserData::empty() const
 {
-    debugs(28,6,"required: " << flags.required << ", number of users: " << userDataNames.size());
+    debugs(28, 6, "required: " << flags.required <<
+           ", case-sensitive users: " << caseSensitiveNames.size() <<
+           ", case-insensitive users: " << caseInsensitiveNames.size());
     if (flags.required)
         return false;
-    return userDataNames.empty();
+    return caseSensitiveNames.empty() && caseInsensitiveNames.empty();
 }

On 2026-03-06 6:43 p.m., Alex Rousskov wrote:
On 2026-03-06 12:32, Andre Bolinhas wrote:

I can't create the pull request, returns the message "Pull request creation failed. Validation failed: must be a collaborator"

Googling suggests that you might be trying to modify the official git repository directly. Instead, fork the official git repository, make your changes in your forked repository, and then submit a pull request to merge your changes into the official repository. This process is typical for open source projects.

The following wiki page has related git hints:
https://wiki.squid-cache.org/DeveloperResources/GitHints

HTH,

Alex.


On 2026-03-05 2:40 p.m., Alex Rousskov wrote:
On 2026-03-04 17:44, Andre Bolinhas wrote:

The |proxy_auth -i| ACL (case-insensitive user matching) is broken in Squid 6.x.

Yes, there are several bugs/problems there. See a long comment above Acl::Option class declaration for how things are supposed to work.

If you can volunteer to work on a fix, please post a pull request as discussed at https://wiki.squid-cache.org/MergeProcedure#pull-request

In that pull request, instead of Option A and Option B, please do this:

1. Split ACLUserData::userDataNames into two sets: caseSensitiveNames and caseInsensitiveNames. Add tokens to the right set, depending on the current CaseInsensitive_ value. Search/print both sets as needed. Remove flags.case_insensitive.

2. Ignore any '-i' and '+i' tokens in ACLUserData::parse(), with a level-1 warning, instead of adding them to a set as if they were user names.

3. Check other ACLs that use lineOptions() for similar bugs.


Thank you,

Alex.
P.S. I am sorry that our Bugzilla is still down, preventing you from using it to report this bug. We can continue to discuss this on GitHub.

_______________________________________________
squid-users mailing list
[email protected]
https://lists.squid-cache.org/listinfo/squid-users

Reply via email to