Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

Unable to escape comma when searching for an attribute whose value is a DN #109

Closed
billwolckenlmco opened this issue Jan 10, 2013 · 7 comments

Comments

@billwolckenlmco
Copy link

When searching with a filter where the desired attribute value is a DN, LDAP servers require that any commas which are not separators be escaped with a backslash. E.g. if I wanted to search for entries whose "manager" attribute equals the DN "CN=Doe, John,DC=example,DC=com" I would need to specify a search of "(manager=CN=Doe, John,DC=example,DC=com)" (note the backslash between "Doe" and the comma).

The problem is that I cannot get ldapjs to pass the backslash on to the LDAP server correctly, and hence I get no search results. I tried passing the filter both as a string and as an EqualityFilter object (per the workaround in issue 48) and tried using 0, 1, and 2 backslashes. I hacked my local copy of writeCallback in client.js to dump the value of message.toBer() to a file so I could see what was actually being sent and here's the results:

Using a string filter:
0 backslashes: CN=Doe, John,DC=...
1 backslash: CN=Doe, John,DC=... (backslash is lost)
2 backslashes: CN=Doe\5c, John,DC=... (backslash itself if escaped - treated as if I wanted a literal backslash as part of the data. note the \5c is literally 3 characters '', '5', and 'c')

Using an object filter:
0 backslashes: CN=Doe, John,DC=...
1 backslash: CN=Doe\5c, John,DC=...
2 backslashes: CN=Doe\5c\5c, John,DC=...

It needs to be CN=Doe, John,DC=... (..., 'e', '', ',', ' ', 'J', 'o', ...), and when I hacked my local copy to do that, the search returned the results I expected. (I just made _parseString in lib/filters/index.js just do a 1-for-1 replacement of a tilde with a backslash in the result of serializeTree, so it was just to test the behavior - not an actual fix for the issue.)

@billwolckenlmco
Copy link
Author

I'm using ldapjs version 0.5.7. Running on MacOS (although I duplicated the behavior on CentOS Linux). Running against Active Directory as the LDAP server.

@sastan
Copy link
Contributor

sastan commented Jan 10, 2013

We had the same problem. The error is the in the filter classes as they escape the value in the constructor which is correct for the string representation of the filter but invalid in the ber. The following coffeescript code monky-patches the builtin ldapjs filters

{filters} = require 'ldapjs'
escape    = require('ldapjs/lib/filters/escape').escape

module.exports = filters

patch = (name, type) ->
    class filters[name] extends filters[name]
        constructor: (options) ->
            super

            @attribute = options.attribute if @attribute?
            @value = options.value if @value?

        toString: -> "(#{escape @attribute}#{type}#{escape @value})"

patch 'ApproximateFilter', '~='
patch 'EqualityFilter', '='
patch 'GreaterThanEqualsFilter', '>='
patch 'LessThanEqualsFilter', '<='

class filters.PresenceFilter extends filters.PresenceFilter
    constructor: (options) ->
        super

        @attribute = options.attribute if @attribute?

    toString: -> "(#{escape @attribute}=*)"

class filters.SubstringFilter extends filters.SubstringFilter
    constructor: (options) ->
        super

        @attribute = options.attribute if @attribute?
        @initial = options.initial if @initial?
        @any = options.any[..] if @any.length
        @final = options.final if @final?

    toString: ->
        res = "(#{escape @attribute}="

        res += escape @initial if @initial

        res += '*'

        res += "#{escape any}*" for any in @any

        res += escape @final if @final

        res += ')'

@mcavage
Copy link
Contributor

mcavage commented Jan 10, 2013

@sastan - do you want to send a PR over for that?

@sastan
Copy link
Contributor

sastan commented Jan 11, 2013

@mcavage Doing it right now.

@billwolckenlmco
Copy link
Author

sastan's pull request resolved the issue for searching using the filter objects (e.g. EqualityFilter). It's been working great. Thank you!

I found that there still is an issue when using strings as filters from how parseFilter handles the backslashes, although it could just be a misunderstanding on my part. (It doesn't impact me, since I switched to using the filter objects for searching rather than filter strings.) It is probably best demonstrated with an example. The example queries for a person's entry then uses the "manager" attribute which holds a DN to query again for the person's manager. I perform the query for the manager three different ways: two using a string filter and one using and EqualityFilter object. Here's the snippet where I build the three filters:

stringFilter = '(distinguishedName=' + entry.object.manager + ')';
stringFilter2 = '(distinguishedName=' + entry.object.manager.replace(/\\/g, '\\\\') + ')';
objectFilter = new EqualityFilter({ attribute: 'distinguishedName',  value: entry.object.manager });

I also call parseFilter() on them just so I can print out what is going on:

stringFilterParsed = parseFilter(stringFilter);
stringFilter2Parsed = parseFilter(stringFilter2);

Then I print it all out. Any backslashes in the string filters will be printed as-is; any in the objects will be doubled by the JSON stringify.

console.log('value to seek: %s', entry.object.manager);
console.log('stringFilter:  %s', stringFilter);
console.log('stringFilter2:  %s', stringFilter2);
console.log('stringFilter parsed:  %j', stringFilterParsed);
console.log('stringFilter2 parsed:  %j', stringFilter2Parsed);
console.log('objectFilter parsed:  %j', objectFilter);

Finally, I perform three searches using each of the three filters. Below are the results. I have modified the names and domains, but otherwise the output (including the backslashes) is from a real test run.

value to seek: CN=Doe\, John,OU=Users,DC=example,DC=com
stringFilter:  (distinguishedName=CN=Doe\, John,OU=Users,DC=example,DC=com)
stringFilter2:  (distinguishedName=CN=Doe\\, John,OU=Users,DC=example,DC=com)
stringFilter parsed:  {"attribute":"distinguishedName","value":"CN=Doe, John,OU=Users,DC=example,DC=com","_type":163,"type":"equal","json":{"type":"EqualityMatch","attribute":"distinguishedName","value":"CN=Doe, John,OU=Users,DC=example,DC=com"}}
stringFilter2 parsed:  {"attribute":"distinguishedName","value":"CN=Doe\\, John,OU=Users,DC=example,DC=com","_type":163,"type":"equal","json":{"type":"EqualityMatch","attribute":"distinguishedName","value":"CN=Doe\\, John,OU=Users,DC=example,DC=com"}}
objectFilter parsed:  {"attribute":"distinguishedName","value":"CN=Doe\\, John,OU=Users,DC=example,DC=com","_type":163,"type":"equal","json":{"type":"EqualityMatch","attribute":"distinguishedName","value":"CN=Doe\\, John,OU=Users,DC=example,DC=com"}}

search using stringFilter:  0 results
search using stringFilter2:  1 results
search using objectFilter:  1 results

Is the behavior I'm seeing the way it is supposed to work - that if you want to build a string query using a value fetched from another entry it needs to be escaped first - or is there an issue with parseFilter?

@pfmooney
Copy link
Contributor

I'm closing out very old issues. Please comment if this should be reopened.

@jsumners
Copy link
Member

Resolved by #521.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants