Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem with LDAP search filter containing a backslash ('\')

136 views
Skip to first unread message

Clément OUDOT

unread,
Jul 20, 2011, 5:02:33 AM7/20/11
to perl...@perl.org
Hi,

I have a piece of code where I build a search filter with some
variables, like this:

my $searchFilter =
"(&(objectClass=" . $portal->{ldapGroupObjectClass} . ")(|";
foreach ( split( $portal->{multiValuesSeparator}, $value ) ) {
$searchFilter .= "(" . $key . "=" . $_ . ")";
}
$searchFilter .= "))";

This works well, excepted when the value (in the key=value syntax)
contains a backslash ('\'). This is the case for example if the value
is a DN like this : cn=OUDOT\, Clement, ou=users, dc=example, dc=com

To make this works, I added this line :

$searchFilter =~ s/\\/\\\\/;


My question: is this a bug in my code, or can this be a Perl-LDAP bug?
I am using version 0.4001.


Thanks for your help,


Clément OUDOT
http://lemonldap-ng.org

Clément OUDOT

unread,
Jul 20, 2011, 6:34:25 AM7/20/11
to Chris Ridd, perl...@perl.org
Le 20 juillet 2011 12:24, Chris Ridd <chri...@mac.com> a écrit :
> I think it is a bug in your code :-(
>
> LDAP search filter strings consider certain characters as "special" when used in assertion values, so there is an escape mechanism defined - which is to use backslash and the hex-encoding of the character (eg \xx) or backslash and a single character (eg \c).
>
> So your $_ value needs to be escaped correctly before inserting it into an LDAP filter string. You need to escape more than backslashes - what if your input value was ")"? Doing a subsequent regexp replace of \ to \\ is not really robust.
>
> RFC 4515 should list all the characters that you have to escape. Actually, the Net::LDAP::Filter documentation lists them as well :-)
>
> You could also consider building your filter using Net::LDAP::Filter instead of as a string.

Hi,

I will have a look to Net::LDAP::Filter, but I see in Net::LDAP that a
new Net::LDAP::Filter is created in the search subroutine when filter
is a string. Why do the Net::LDAP::Filter object do not escape the
special characters from the string? Am I misunderstanding the code?

Clément.

Clément OUDOT

unread,
Jul 20, 2011, 8:12:15 AM7/20/11
to Chris Ridd, perl...@perl.org
Le 20 juillet 2011 13:13, Chris Ridd <chri...@mac.com> a écrit :

>
> On 20 Jul 2011, at 11:34, Clément OUDOT wrote:
>
>> Hi,
>>
>> I will have a look to Net::LDAP::Filter, but I see in Net::LDAP that a
>> new Net::LDAP::Filter is created in the search subroutine when filter
>> is a string. Why do the Net::LDAP::Filter object do not escape the
>> special characters from the string? Am I misunderstanding the code?
>
> I think it is because the new() method sees the two characters "\," and assumes you are escaping the comma. It is legal to escape an arbitrary character like that, so your filter is valid but not what you want it to be.
>
> I'm not sure what the best way to use the Filter class really is, given that it requires a validly escaped input string. I suppose you could call new() with a string containing placeholders for each value, and then manipulate the returned object to insert "proper" (and unescaped) values for each placeholder.
>
> Something like this (typed in Mail):
>
> $filter = Net::LDAP::Filter->new("(&(objectClass=?)(|(foo=bar)))");
> $filter->{and}->[0]->{equalityMatch}->{assertionValue} = $portal->{ldapGroupObjectClass};
> $filter->{and}->[1]->{or}->[0]->{equalityMatch}->{assertionValue} = "clement";
> $filter->{and}->[1]->{or}->[0]->{equalityMatch}->{assertionDesc} = "sn";
>
> (etc) You'd need to loop adding new equalityMatch hashes to the 'or' array.
>
> Kind of ugly, but the alternative is for you to escape all your values before building the string.

And what about doing this:

$filter = Net::LDAP::Filter::_escape( $filter );

Would this work?


Clément.

Francis Swasey

unread,
Jul 20, 2011, 8:32:25 AM7/20/11
to Chris Ridd, Clément OUDOT, perl...@perl.org

On Jul 20, 2011, at 8:26, Chris Ridd <chri...@mac.com> wrote:

>
>>
>>
>>
>
> Graham, should _escape be made public? It seems like it would be useful. Or is manipulating the data structure returned from new the better approach?

Perhaps a flag on the new call that indicates there are no escapes in the string so that the existing \, is not assumed to be a pre-existing escape and the \ gets escaped?

Frank

Clément OUDOT

unread,
Jul 22, 2011, 3:28:59 AM7/22/11
to Chris Ridd, Graham Barr, Francis Swasey, perl...@perl.org
2011/7/20 Chris Ridd <chri...@mac.com>:
>
> On 20 Jul 2011, at 13:36, Graham Barr wrote:
>> That would not work consistently. consider (attr=())
>>
>> Some guessing would have to go one to know that the first ) needs to be escaped
>>
>> I think exporting _escape as escape_ldap_filter is the best approach and users should use that as they build their filters
>
> I'd call it escape_value - or at least something with "value" in the name - to make it clearer that you shouldn't pass a complete filter string into it.
>

The solution seems ok to me. Should I open an issue on the CPAN tracker?


Clément.

Peter Marschall

unread,
Jul 27, 2011, 3:19:58 PM7/27/11
to perl...@perl.org, Graham Barr, Francis Swasey, Chris Ridd, Clément OUDOT
Hi,

On Wednesday, 20. July 2011, Graham Barr wrote:
> On Jul 20, 2011, at 07:32 , Francis Swasey wrote:

> > On Jul 20, 2011, at 8:26, Chris Ridd <chri...@mac.com> wrote:
> >> Graham, should _escape be made public? It seems like it would be useful.

> > Perhaps a flag on the new call that indicates there are no escapes in the
> > string so that the existing \, is not assumed to be a pre-existing
> > escape and the \ gets escaped?
>

> That would not work consistently. consider (attr=())
>
> Some guessing would have to go one to know that the first ) needs to be
> escaped
>
> I think exporting _escape as escape_ldap_filter is the best approach and
> users should use that as they build their filters

What about using the documented functions in Net::LDAP::Util ?
* escape_filter_value()
* unescape_filter_value()

And for DNs the equivalents
* escape_dn_value()
* unescape_dn_value()

Best regards
Peter

--
Peter Marschall
pe...@adpm.de

Clément OUDOT

unread,
Jul 27, 2011, 4:05:53 PM7/27/11
to Peter Marschall, perl...@perl.org, Graham Barr, Francis Swasey, Chris Ridd
2011/7/27 Peter Marschall <pe...@adpm.de>:

Indeed, that is exactly what I was looking for.

Thanks for the pointer.

Clément.

0 new messages