Re: Bug Report: proxy_auth -i case-insensitive matching broken in Squid 6.x
Andre Bolinhas <[email protected]> Fri, 6 Mar 2026 19:19:56 +0000
| Newsgroups | gmane.comp.web.squid.general |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format.
--===============8457513259243793488==
Content-Type: multipart/alternative;
boundary="------------CEgG0PGhEe58ereiUyNKJ28D"
Content-Language: pt_PT
This is a multi-part message in MIME format.
--------------CEgG0PGhEe58ereiUyNKJ28D
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
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.
>>>
>
--------------CEgG0PGhEe58ereiUyNKJ28D
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<!DOCTYPE html>
<html data-lt-installed="true">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body style="padding-bottom: 1px;">
<p>This is the patch file, tested successfully on squid 6.x and 7.x</p>
<p>--- src/acl/UserData.h.orig 2026-03-06 16:23:44.415748741
+0000<br>
+++ src/acl/UserData.h 2026-03-06 16:23:44.431748826 +0000<br>
@@ -34,11 +34,15 @@<br>
/* ACLData API */<br>
const Acl::Options &lineOptions() override;<br>
<br>
- typedef std::set<SBuf,bool(*)(const SBuf&, const
SBuf&)> UserDataNames_t;<br>
- UserDataNames_t userDataNames;<br>
+ typedef std::set<SBuf, bool(*)(const SBuf&, const
SBuf&)> UserDataNames_t;<br>
+<br>
+ /// names added while -i (case-insensitive) was off<br>
+ UserDataNames_t caseSensitiveNames;<br>
+<br>
+ /// names added while -i (case-insensitive) was on; stored
lowercased<br>
+ UserDataNames_t caseInsensitiveNames;<br>
<br>
struct {<br>
- bool case_insensitive;<br>
bool required;<br>
} flags;<br>
<br>
--- src/acl/UserData.cc.orig 2026-03-06 16:23:44.435748847
+0000<br>
+++ src/acl/UserData.cc 2026-03-06 16:23:44.459748972 +0000<br>
@@ -23,7 +23,7 @@<br>
bool<br>
ACLUserData::match(char const *user)<br>
{<br>
- debugs(28, 7, "user is " << user << ",
case_insensitive is " << flags.case_insensitive);<br>
+ debugs(28, 7, "user is " << user);<br>
<br>
if (user == nullptr || strcmp(user, "-") == 0)<br>
return 0;<br>
@@ -33,9 +33,26 @@<br>
return 1;<br>
}<br>
<br>
- bool result = (userDataNames.find(SBuf(user)) !=
userDataNames.end());<br>
- debugs(28, 7, "returning " << result);<br>
- return result;<br>
+ const SBuf userKey(user);<br>
+<br>
+ // check case-sensitive set first (exact match)<br>
+ if (caseSensitiveNames.find(userKey) !=
caseSensitiveNames.end()) {<br>
+ debugs(28, 7, "returning 1 (case-sensitive match)");<br>
+ return 1;<br>
+ }<br>
+<br>
+ // check case-insensitive set (lowercased lookup)<br>
+ if (!caseInsensitiveNames.empty()) {<br>
+ SBuf lowerUser(userKey);<br>
+ lowerUser.toLower();<br>
+ if (caseInsensitiveNames.find(lowerUser) !=
caseInsensitiveNames.end()) {<br>
+ debugs(28, 7, "returning 1 (case-insensitive
match)");<br>
+ return 1;<br>
+ }<br>
+ }<br>
+<br>
+ debugs(28, 7, "returning 0");<br>
+ return 0;<br>
}<br>
<br>
SBufList<br>
@@ -48,14 +65,18 @@<br>
return sl;<br>
}<br>
<br>
- if (flags.case_insensitive)<br>
- sl.push_back(SBuf("-i"));<br>
+ // dump case-sensitive names first (no flag needed)<br>
+ sl.insert(sl.end(), caseSensitiveNames.begin(),
caseSensitiveNames.end());<br>
<br>
- sl.insert(sl.end(), userDataNames.begin(),
userDataNames.end());<br>
+ // dump case-insensitive names with -i prefix<br>
+ if (!caseInsensitiveNames.empty()) {<br>
+ sl.push_back(SBuf("-i"));<br>
+ sl.insert(sl.end(), caseInsensitiveNames.begin(),
caseInsensitiveNames.end());<br>
+ }<br>
<br>
- debugs(28,5, "ACLUserData dump output: " <<<br>
- JoinContainerToSBuf(userDataNames.begin(),
userDataNames.end(),<br>
- SBuf(" ")));<br>
+ debugs(28, 5, "ACLUserData dump output: " <<<br>
+ caseSensitiveNames.size() << " case-sensitive, "
<<<br>
+ caseInsensitiveNames.size() << "
case-insensitive users");<br>
return sl;<br>
}<br>
<br>
@@ -72,9 +93,9 @@<br>
}<br>
<br>
ACLUserData::ACLUserData() :<br>
- userDataNames(CaseSensitiveSBufCompare)<br>
+ caseSensitiveNames(CaseSensitiveSBufCompare),<br>
+ caseInsensitiveNames(CaseInsensitveSBufCompare)<br>
{<br>
- flags.case_insensitive = false;<br>
flags.required = false;<br>
}<br>
<br>
@@ -91,63 +112,61 @@<br>
ACLUserData::parse()<br>
{<br>
debugs(28, 2, "parsing user list");<br>
- flags.case_insensitive = bool(CaseInsensitive_);<br>
+<br>
+ bool caseInsensitive = bool(CaseInsensitive_);<br>
<br>
char *t = nullptr;<br>
- if ((t = ConfigParser::strtokFile())) {<br>
+ while ((t = ConfigParser::strtokFile())) {<br>
SBuf s(t);<br>
- debugs(28, 5, "first token is " << s);<br>
-<br>
- if (s.cmp("-i",2) == 0) {<br>
- debugs(28, 5, "Going case-insensitive");<br>
- flags.case_insensitive = true;<br>
- // due to how the std::set API work, if we want to
change<br>
- // the comparison function we have to create a new
std::set<br>
- UserDataNames_t newUdn(CaseInsensitveSBufCompare);<br>
- newUdn.insert(userDataNames.begin(),
userDataNames.end());<br>
- swap(userDataNames,newUdn);<br>
- } else if (s.cmp("REQUIRED") == 0) {<br>
- debugs(28, 5, "REQUIRED-type enabled");<br>
- flags.required = true;<br>
- } else {<br>
- if (flags.case_insensitive)<br>
- s.toLower();<br>
+ debugs(28, 6, "Got token: " << s);<br>
<br>
- debugs(28, 6, "Adding user " << s);<br>
- userDataNames.insert(s);<br>
+ if (s.cmp("-i", 2) == 0) {<br>
+ debugs(28, DBG_IMPORTANT, "WARNING: ACL uses '-i' as
a token in the user list; " <<<br>
+ "use 'acl ... proxy_auth -i ...' line option
instead");<br>
+ continue;<br>
}<br>
- }<br>
-<br>
- debugs(28, 3, "Case-insensitive-switch is " <<
flags.case_insensitive);<br>
- /* we might inherit from a previous declaration */<br>
<br>
- debugs(28, 4, "parsing following tokens");<br>
+ if (s.cmp("+i", 2) == 0) {<br>
+ debugs(28, DBG_IMPORTANT, "WARNING: ACL uses '+i' as
a token in the user list; " <<<br>
+ "use 'acl ... proxy_auth +i ...' line option
instead");<br>
+ continue;<br>
+ }<br>
<br>
- while ((t = ConfigParser::strtokFile())) {<br>
- SBuf s(t);<br>
- debugs(28, 6, "Got token: " << s);<br>
+ if (s.cmp("REQUIRED") == 0) {<br>
+ debugs(28, 5, "REQUIRED-type enabled");<br>
+ flags.required = true;<br>
+ continue;<br>
+ }<br>
<br>
- if (flags.case_insensitive)<br>
+ if (caseInsensitive) {<br>
s.toLower();<br>
-<br>
- debugs(28, 6, "Adding user " << s);<br>
- userDataNames.insert(s);<br>
+ debugs(28, 6, "Adding user (case-insensitive) "
<< s);<br>
+ caseInsensitiveNames.insert(s);<br>
+ } else {<br>
+ debugs(28, 6, "Adding user (case-sensitive) "
<< s);<br>
+ caseSensitiveNames.insert(s);<br>
+ }<br>
}<br>
<br>
- if (flags.required && !userDataNames.empty()) {<br>
+ if (flags.required && (!caseSensitiveNames.empty() ||
!caseInsensitiveNames.empty())) {<br>
debugs(28, DBG_PARSE_NOTE(1), "WARNING: detected attempt
to add usernames to an acl of type REQUIRED");<br>
- userDataNames.clear();<br>
+ caseSensitiveNames.clear();<br>
+ caseInsensitiveNames.clear();<br>
}<br>
<br>
- debugs(28,4, "ACL contains " << userDataNames.size()
<< " users");<br>
+ debugs(28, 4, "ACL contains " <<
caseSensitiveNames.size() <<<br>
+ " case-sensitive and " <<
caseInsensitiveNames.size() <<<br>
+ " case-insensitive users");<br>
}<br>
<br>
bool<br>
ACLUserData::empty() const<br>
{<br>
- debugs(28,6,"required: " << flags.required << ",
number of users: " << userDataNames.size());<br>
+ debugs(28, 6, "required: " << flags.required <<<br>
+ ", case-sensitive users: " <<
caseSensitiveNames.size() <<<br>
+ ", case-insensitive users: " <<
caseInsensitiveNames.size());<br>
if (flags.required)<br>
return false;<br>
- return userDataNames.empty();<br>
+ return caseSensitiveNames.empty() &&
caseInsensitiveNames.empty();<br>
}<br>
<br>
</p>
<div class="moz-cite-prefix">On 2026-03-06 6:43 p.m., Alex Rousskov
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:[email protected]">On
2026-03-06 12:32, Andre Bolinhas wrote:
<br>
<br>
<blockquote type="cite">I can't create the pull request, returns
the message "Pull request creation failed. Validation failed:
must be a collaborator"
<br>
</blockquote>
<br>
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.
<br>
<br>
The following wiki page has related git hints:
<br>
<a class="moz-txt-link-freetext" href="https://wiki.squid-cache.org/DeveloperResources/GitHints">https://wiki.squid-cache.org/DeveloperResources/GitHints</a>
<br>
<br>
HTH,
<br>
<br>
Alex.
<br>
<br>
<br>
<blockquote type="cite">On 2026-03-05 2:40 p.m., Alex Rousskov
wrote:
<br>
<blockquote type="cite">On 2026-03-04 17:44, Andre Bolinhas
wrote:
<br>
<br>
<blockquote type="cite">The |proxy_auth -i| ACL
(case-insensitive user matching) is broken in Squid 6.x.
<br>
</blockquote>
<br>
Yes, there are several bugs/problems there. See a long comment
above Acl::Option class declaration for how things are
supposed to work.
<br>
<br>
If you can volunteer to work on a fix, please post a pull
request as discussed at
<a class="moz-txt-link-freetext" href="https://wiki.squid-cache.org/MergeProcedure#pull-request">https://wiki.squid-cache.org/MergeProcedure#pull-request</a>
<br>
<br>
In that pull request, instead of Option A and Option B, please
do this:
<br>
<br>
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.
<br>
<br>
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.
<br>
<br>
3. Check other ACLs that use lineOptions() for similar bugs.
<br>
<br>
<br>
Thank you,
<br>
<br>
Alex.
<br>
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.
<br>
<br>
</blockquote>
</blockquote>
<br>
</blockquote>
</body>
<lt-container></lt-container>
</html>
--------------CEgG0PGhEe58ereiUyNKJ28D--
--===============8457513259243793488==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
squid-users mailing list
[email protected]
https://lists.squid-cache.org/listinfo/squid-users
--===============8457513259243793488==--