[squid-users] dynamic group using URI as group name on external acl with ext_ldap_group_acl

Amos Jeffries squid3 at treenet.co.nz
Mon Aug 22 14:49:26 UTC 2016


On 22/08/2016 10:54 a.m., Diogenes S. Jesus wrote:
> Hi everyone.
> 
> I've the following use case to be accomplished using ACL:
> 
> - Allow any authenticated user who is member of a group named after the URI
> 
> To construct this I've built the following squid.conf (snippet):
> 
> ---------
> auth_param negotiate program /usr/lib/squid3/negotiate_kerberos_auth -d -r
> auth_param negotiate children 10
> auth_param negotiate keep_alive on
> 
> external_acl_type ldap_HTTP %LOGIN %URI
> /usr/lib/squid/ext_ldap_group_acl -D "cn=admin,dc=example,dc=com" -w
> test -R -b "ou=authorization,dc=example,dc=com" -B
> "ou=people,dc=example,dc=com" -f
> '(&(objectclass=groupOfNames)(cn=%g)(member=uid=%u,ou=people,dc=example,dc=com))'
> -h ldap01.example.com -d


Please be aware that the %URI format does not perform any type of shell
or LDAP escaping to protect this helper lookup against shell-injection
attacks.

It is possible that a remote client can end a URL with ')' followed by
any LDAP commands they like and have that executed by your helper.

If you want to do things like this safely please upgrade to Squid-4
where the logformat codes are available. Those codes provide
customizable escaping and quoting styles so you can set one that
protects LDAP against these attacks to be ued on the URI field value
sent by Squid.


> 
> acl allow_HTTP_ACL external ldap_HTTP ""
> 
> http_access deny !allow_HTTP_ACL all
> http_access allow allow_HTTP_ACL
> http_access deny all
> ---------
> 
> I call it a "dynamic" acl, because the value of the group is the
> actual URI

Whereas everyone else calls it dynamic because the ACL test is highly
variable (aka dynamic) based on whatever the custom helper logic is.


> (the search filter will expand like:
> cn=<URI>,ou=authorization,dc=example,dc=com). For that
> "allow_HTTP_ACL" passes "" to ldap_HTTP.
> 
> This is working, however that's not documented. I was wondering how
> this works, so I debugged.
> 
> I found out the %<template filter var> expands as following for the
> following search filter:
> -f '(&(objectclass=groupOfNames)(<template_filter_reference>=%<template_filter>)(member=uid=%u,ou=people,dc=example,dc=com))'
> 
...
> 
> 4) '(&(objectclass=groupOfNames)(test=%test)(member=uid=%u,ou=people,dc=example,dc=com))':
> ERROR: Unknown filter template string %t
> ext_ldap_group_acl: ERROR: Failed to construct LDAP search filter.
> filter="(&(objectclass=groupOfNames)(test=?,?U", user="john_doe",
> group="http://web.example.com/"
> ERROR: Unknown filter template string %t
> ext_ldap_group_acl: ERROR: Failed to construct LDAP search filter.
> filter="(&(objectclass=groupOfNames)(test=?,?U", user="john_doe",
> group="GET"
> ERROR: Unknown filter template string %t
> ext_ldap_group_acl: ERROR: Failed to construct LDAP search filter.
> filter="(&(objectclass=groupOfNames)(test=?,?U", user="john_doe",
> group="80"
> 
> 5) '(&(objectclass=groupOfNames)(v=%v)(member=uid=%u,ou=people,dc=example,dc=com))':
> ext_ldap_group_acl.cc(718): pid=26314 :group filter
> '(&(objectclass=groupOfNames)(v=john_doe)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'
> ext_ldap_group_acl.cc(718): pid=26314 :group filter
> '(&(objectclass=groupOfNames)(v=john_doe)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'
> ext_ldap_group_acl.cc(718): pid=26314 :group filter
> '(&(objectclass=groupOfNames)(v=john_doe)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'
> 


I dont think you quite grok which part of these outputs is the garbage
(uninitialized memory dump), and which part is the incomplete prefix of
the filter string.

Notice the above 'garbage' outputs start with:
 filter="(&(objectclass=groupOfNames)(test=

then obvious garbage:
 ?,?U", user="john_doe", group="GET"

Its not always obvious. As in case #6:


> 
> 6) '(&(objectclass=groupOfNames)(g=%g)(member=uid=%u,ou=people,dc=example,dc=com))':
> ext_ldap_group_acl.cc(718): pid=26408 :group filter
> '(&(objectclass=groupOfNames)(g=http://web.example.com/)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'
> ext_ldap_group_acl.cc(718): pid=26408 :group filter
> '(&(objectclass=groupOfNames)(g=GET)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'
> ext_ldap_group_acl.cc(718): pid=26408 :group filter
> '(&(objectclass=groupOfNames)(g=80)(member=uid=john_doe,ou=people,dc=example,dc=com))',
> searchbase 'ou=authorization,dc=example,dc=com'

What is happening is that the helper expects the %LOGIN field to be
followed by a list of space-separated 'words'. Each 'word' is a group
name to be checked against the users account memberships. So the list of
words is looked up individually until one matches or none left to check.

So somehow the HTTP request URL/URI is the whole string:
 "GET http://web.example.com/ 80"

-> very odd. It is not even a valid HTTP request-line. Looks more like
some outdated Squid-1.1 URL re-write helper is mangling the URL. But the
order is slightly wrong even for that (GET would be after the actual URL).


Anyhow, that resuls in the ACL group helper receiving:
 john_does GET http://web.example.com/ 80

Meaning,
 username: "john_doe"
 group #1: "GET"
 group #2: "http://web.example.com/"
 group #3: "80"


> 
> This is all pretty much happening here
> [https://github.com/squid-cache/squid/blob/master/helpers/external_acl/LDAP_group/ext_ldap_group_acl.cc#L638]
> 
> So conclusions:
> - %v and %u both map to "user", which is expected (historical reasons
> & compatibility)

As documented.

> - %g and %a both map to "group", which is expected (historical reasons
> & compatibility)

As documented.

> - any other template filter (%b, %c, %test, etc) is trash (only %a,
> %u, %g, %v won't yield error)

Nod. The helper autor(s) reserve other %-code to be defined with any
arbitrary meaning at any time. So there is simply no documented
behaviour for them.
 A you found v and a have old meanings that are still supported, though
deprecated so removed from the documentation intentionally to prevent
future use.


> - when "" is passed to the acl ("acl <ACL_name> external ldap_HTTP
> ""), the helper will attempt all FORMAT values, mapping then to
> "group" (%a or %g)

It should mean Squid loads a file with undefined name (\0) and sends
that files content as the list of group names. Each line in the file
being a group name.


> 
> Although I can move on with this for now, I would be actually more
> relieved if I could use:
> acl allow_HTTP_ACL external ldap_HTTP
> <a_var_which_is_available_here_representing_URI>
>  instead of
> acl allow_HTTP_ACL external ldap_HTTP ""  + non-documented behavior of
> ext_ldap_group_acl

Dont specify anything at all in that position. The %URI field you
defined to be sent to the helper should be formatted in the place it
expects to find a group name "word" as mentioned above.

Just use:
 acl allow_HTTP_ACL external ldap_HTTP


Amos



More information about the squid-users mailing list