Bug 44883 - Compare DN with respect to case (in)sensitivity
Compare DN with respect to case (in)sensitivity
Status: RESOLVED MOVED
Product: UCS
Classification: Unclassified
Component: UDM (Generic)
UCS 5.0
Other Linux
: P5 normal (vote)
: ---
Assigned To: UMC maintainers
UMC maintainers
https://git.knut.univention.de/univen...
:
: 46679 (view as bug list)
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2017-06-28 17:42 CEST by Florian Best
Modified: 2024-02-20 15:00 CET (History)
5 users (show)

See Also:
What kind of report is it?: Development Internal
What type of bug is this?: ---
Who will be affected by this bug?: ---
How will those affected feel about the bug?: ---
User Pain:
Enterprise Customer affected?:
School Customer affected?:
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number:
Bug group (optional):
Max CVSS v3 score:
best: Patch_Available+


Attachments
patch (15.58 KB, patch)
2017-06-28 17:42 CEST, Florian Best
Details | Diff
44883_get_case_insensitive_attributes.py (412 bytes, text/x-python)
2017-06-28 17:42 CEST, Florian Best
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Florian Best univentionstaff 2017-06-28 17:42:09 CEST
Created attachment 8981 [details]
patch

Currently univention.uldap.access.compare_dn() compares different case of the values in a DN as unequal.

e.g. uid=Administrator,dc=base != uid=administrator,dc=base while they are equal from LDAP perspective.

Attached is a patch with a static list of attributes which compare case insensitive.
Attached is a script which returns these attributes.

This causes a lot of issues especially because lo.whoami_s() returns a lowercase DN if logged in in UMC via SAML.
Comment 1 Florian Best univentionstaff 2017-06-28 17:42:47 CEST
Created attachment 8982 [details]
44883_get_case_insensitive_attributes.py
Comment 2 Florian Best univentionstaff 2017-06-28 17:47:15 CEST
Another idea would be to execute the script as a singleton after any bind() which replaces the static list. Then we can reduce the static list and only list the most common attributes.
Comment 3 Florian Best univentionstaff 2018-03-16 11:27:41 CET
*** Bug 46679 has been marked as a duplicate of this bug. ***
Comment 4 Philipp Hahn univentionstaff 2018-10-09 14:45:31 CEST
Again: UCS Technical Training 2018-10-09

09.10.18 14:20:34.189  MAIN        ( WARN    ) : SAML binddn does not match: 'uid=administrator,cn=users,dc=schulung6,dc=ucs'
 != 'uid=Administrator,cn=users,dc=schulung6,dc=ucs'
Comment 6 Christina Scheinig univentionstaff 2020-08-19 13:40:38 CEST
Found it in a school environment.
Could this cause trouble with long answers to a proxy?
Comment 7 Florian Best univentionstaff 2020-08-19 13:42:50 CEST
(In reply to Christina Scheinig from comment #6)
> Found it in a school environment.
> Could this cause trouble with long answers to a proxy?
No.
Comment 8 Florian Best univentionstaff 2021-09-07 16:50:40 CEST
We had a internal discussion about this issue:

> OpenLDAP has the code, does it right, is maintained externally, so why should we duplicate the code? If "*feels* too slow" is the only reason, then IMHO we should approach this differently.

OpenLDAP's DN-compare (7~k loc https://github.com/openldap/openldap/blob/master/servers/slapd/schema_init.c) has many special cases:

`surname=Hahn+givenName=Philipp` == `givenName=Philipp+surname=Hahn`
`UID=Foo` == `uid = foo`
`cn=bar` == `commonName=bar`
… (etc, e.g. integer matching rules, "fuzzy match", "custom rules" or overlay extensions)


A pragmatic approach suggests to implement a small solution which fits into our problems but only ~95% of all use cases plus that we make sure one cannot use the solution wrong.
A listener module could write all known schema attributes/rules into a local file.

> I realize that only the LDAP server can decide this correctly. Therefore, I think that it should be asked about this. But I would wish that it would offer the possibility to check a request "are these two AVAs identical?" without having to search the object out of the database again. The client sends him two AVAs and asks "is this the same according to the current schema?". But we don't have that and we won't get that upstream even if we would implement it ourselves. So I agree with you: correct at the moment is to ask the LDAP server again. Costs: A round trip. We have to see, how this will work out and if it will bring advantages compared to Florian's current solution. Sönke's suggestion was just another "improvement" suggestion to maintain Florian's static list dynamically. But also there the question is, if the effort is worth it.

Critique was: 
> And implement *all* existing and future comparisons in Python again? Perhaps also in JavaScript, so that we can compare even more performant directly in the browser, in which we can save the round trip to the UMC/REST API server.... Or is it then again only the most important 90% and the remaining 10% then lead to obscure bugs.

Our conclusion is that we have a lack of knowledge and should do experiments about performance:

1. for this it would be good if we could get statistics more easily, i.e. how often do LDAP connections turn up and down, how long does an `ldapsearch` last, .... Then we could also ask customers to provide us with this data (anonymized) in case of (performance) problems.
2. instrument the places where a `compareDN` happens to see how often this occurs in which context.

Some past experiences showed that it was more expensive to run a ldapsearch for 1000 users than to use a ldapsearch to get all groups including uniqueMember and check the group membership manually in Python.

For the UDM group memberships we are doing manual DN string comparisions, while I think we could also use the LDAP compare_s() function, which could compare e.g. the `uniqueMember` attribute of an exising object with a value we have in Python.

The compare_s() functionality can be measured with:
# python -m timeit -s 'import univention.uldap; l = univention.uldap.getAdminConnection().lo' "l.compare_s('uid=Administrator,cn=users,l=school,l=dev', 'uid', b'Administrator')"
10000 loops, best of 3: 86 usec per loop
# python3 -m timeit -s 'import univention.uldap; l = univention.uldap.getAdminConnection().lo' "l.compare_s('uid=Administrator,cn=users,l=school,l=dev', 'uid', b'Administrator')"
2000 loops, best of 5: 80 usec per loop
# python -m timeit -s 'import univention.uldap; l = univention.uldap.getAdminConnection().lo' "l.compare_s('uid=Administrator,cn=users,l=school,l=dev', 'description', b'Administrator')"
10000 loops, best of 3: 85.5 usec per loop
# python3 -m timeit -s 'import univention.uldap; l = univention.uldap.getAdminConnection().lo' "l.compare_s('uid=Administrator,cn=users,l=school,l=dev', 'description', b'Administrator')"
5000 loops, best of 5: 92.6 usec per loop

A short python script which can also compare a DN with an existing object:

> import ldap
> from ldap.dn import str2dn, dn2str
> import univention.uldap
> lo = univention.uldap.getAdminConnection()
> l = lo.lo
> 
> 
> def compare_dn(dn, real):
>     dn = str2dn(dn)
>     real = str2dn(real)
> 
>     for i, part in enumerate(reversed(real)):
>         for attr, value, _ in dn[-i - 1]:
>             try:
>                 if not l.compare_s(dn2str(real[-i - 1 :]), attr, value):
>                     return False
>             except ldap.NO_SUCH_OBJECT:
>                 if attr == real[-1][0][0][0]:
>                     continue
>                 raise
>     return True
> 
> print(compare_dn('Uid = Administrator , cn = usErs, l=school , l = dev', 'uid=Administrator,cn=users,l=school,l=dev'))
> print(compare_dn('Uid = Administrato , cn = usErs, l=school , l = dev', 'uid=Administrator,cn=users,l=school,l=dev'))

I am not yet aware of a LDAP function which compares 2 given attribute values, which would be the requirement for a LDAP compare function.
Comment 9 Philipp Hahn univentionstaff 2021-09-15 10:50:34 CEST
Beware problems such as in Bug #53776 comment 8:

# python3 -c 'print("ß".upper())'
SS

And my favorite for ignoring language rules: http://www.i18nguy.com/unicode/turkish-i18n.html

UDL also already has some C-code for comparing DNs in src/utils.c:122
Comment 10 Florian Best univentionstaff 2022-11-09 21:09:00 CET
pragmatic approach in https://git.knut.univention.de/univention/ucs/-/merge_requests/559.