Bug 53553 - UDM filters search for "&(|(cn=*)(description=*b'')))" containing bytestrings
UDM filters search for "&(|(cn=*)(description=*b'')))" containing bytestrings
Status: CLOSED FIXED
Product: UCS
Classification: Unclassified
Component: UDM (Generic)
UCS 5.0
Other Linux
: P5 normal (vote)
: UCS 5.0-0-errata
Assigned To: Florian Best
Jan Luttermann
:
Depends on:
Blocks: 52578
  Show dependency treegraph
 
Reported: 2021-07-07 13:14 CEST by Florian Best
Modified: 2024-02-23 13:43 CET (History)
1 user (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): Regression
Max CVSS v3 score:
best: Patch_Available+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Florian Best univentionstaff 2021-07-07 13:14:20 CEST
There are bytestrings in the log output of the LDAP filters generated by UDM:

```
import univention.management.console.log, sys, univention.admin
from ucsschool.lib.models.computer import SchoolComputer
lo, po = univention.admin.uldap.getMachineConnection()
univention.management.console.log.log_init('/dev/stdout', 99)
SchoolComputer.get_all(lo, 'DEMOSCHOOL', '(&(|(name=*)(description=*)))')
```
→
```
LDAP        ( INFO    ) : uldap.search filter=(&(objectClass=univentionHost)(objectClass=univentionClient)(!(objectClass=posixAccount))(&(objectClass=ucsschoolComputer)(&(|(cn=*b'')(description=*b''))))) base=cn=comp
uters,ou=DEMOSCHOOL,l=school,l=dev scope=sub attr=[] unique=0 required=0 timeout=-1 sizelimit=0
…
LDAP        ( INFO    ) : uldap.search filter=(&(objectClass=sambaSamAccount)(sambaAcctFlags=[I          ])(&(objectClass=ucsschoolComputer)(&(|(cn=*)(description=*b''))))) base=cn=computers,ou=DEMOSCHOOL,l=school,l=
dev scope=sub attr=[] unique=0 required=0 timeout=-1 sizelimit=0
…
```

Debugging session:

Create a breakpoint at the place where the log message is created:
```
diff --git base/univention-python/modules/uldap.py base/univention-python/modules/uldap.py
index 8d20eb2ad6..12b397826a 100644
--- base/univention-python/modules/uldap.py
+++ base/univention-python/modules/uldap.py
@@ -499,6 +499,8 @@ class access(object):
                """
                univention.debug.debug(univention.debug.LDAP, univention.debug.INFO, 'uldap.search filter=%s base=%s scope=%s attr=%s unique=%d required=%d timeout=%d sizelimit=%d' % (
                        filter, base, scope, attr, unique, required, timeout, sizelimit))
+               if "b''" in str(filter):
+                       breakpoint()
 
                if not base:
                        base = self.base
```

`scp base/univention-python/modules/uldap.py 10.200.XX.XX:/usr/lib/python3/dist-packages/univention/uldap.py`

```
# python3 repr.py
(Pdb) bt
  /root/repr.py(5)<module>()
-> SchoolComputer.get_all(lo, 'DEMOSCHOOL', '(&(|(name=*)(description=*)))')
  /usr/lib/python3/dist-packages/ucsschool/lib/models/base.py(991)get_all()
-> for udm_obj in cls.lookup(lo, school, complete_filter, superordinate=superordinate):
  /usr/lib/python3/dist-packages/ucsschool/lib/models/computer.py(233)lookup()
-> return super(SchoolComputer, cls).lookup(lo, school, school_computer_filter, superordinate)
  /usr/lib/python3/dist-packages/ucsschool/lib/models/base.py(1009)lookup()
-> superordinate=superordinate,
  /usr/lib/python3/dist-packages/univention/admin/modules.py(953)lookup()
-> tmpres = module.lookup(co, lo, filter, base=base, superordinate=superordinate, scope=scope, unique=unique, required=required, timeout=timeout, sizelimit=sizelimit)                                                                        
  /usr/lib/python3/dist-packages/univention/admin/handlers/computers/computer.py(133)lookup()
-> res.extend(computer.lookup(co, lo, filter_s, base, superordinate, scope, unique, required, timeout, sizelimit))
  /usr/lib/python3/dist-packages/univention/admin/handlers/computers/ipmanagedclient.py(261)lookup()
-> for dn, attrs in lo.search(str(filter), base, scope, [], unique, required, timeout, sizelimit, serverctrls, response):                                                                                                                     
  /usr/lib/python3/dist-packages/univention/admin/uldap.py(669)search()
-> return self.lo.search(filter, base, scope, attr, unique, required, timeout, sizelimit, serverctrls=serverctrls, response=response)                                                                                                         
  /usr/lib/python3/dist-packages/univention/uldap.py(208)_decorated()
-> return func(self, *args, **kwargs)
> /usr/lib/python3/dist-packages/univention/uldap.py(505)search()
-> if not base:

(Pdb) up
(Pdb) up
(Pdb) up
> /usr/lib/python3/dist-packages/univention/admin/handlers/computers/ipmanagedclient.py(261)lookup()
(Pdb) filter
conjunction('&', [expression('objectClass', 'univentionHost', '='), expression('objectClass', 'univentionClient', '='), conjunction('!', [expression('objectClass', 'posixAccount', '=')]), conjunction('&', [expression('objectClass', 'ucsschoolComputer', '='), conjunction('&', [conjunction('|', [expression('cn', b'', '=*'), expression('description', b'', '=*')])])])])
(Pdb) up
> /usr/lib/python3/dist-packages/univention/admin/handlers/computers/computer.py(133)lookup()
(Pdb) filter_s
conjunction('&', [expression('objectClass', 'ucsschoolComputer', '='), conjunction('&', [conjunction('|', [expression('name', '', '=*'), expression('description', '', '=*')])])])
```

> /usr/lib/python3/dist-packages/univention/admin/handlers/computers/computer.py(133)lookup()
→ still fixed

> /usr/lib/python3/dist-packages/univention/admin/handlers/computers/ipmanagedclient.py(261)lookup()
→ broken

→ so, between those two stack frame the transformation broke it. now code review what happens there.

```
(Pdb) list 251
246     def lookup(co, lo, filter_s, base='', superordinate=None, scope='sub', unique=False, required=False, timeout=-1, sizelimit=0, serverctrls=None, response=None):
247             res = []
248             filter_s = univention.admin.filter.replace_fqdn_filter(filter_s)
249             filter_s = univention.admin.handlers.dns.alias.lookup_alias_filter(lo, filter_s)
250             filter = univention.admin.filter.conjunction('&', [
251                     univention.admin.filter.expression('objectClass', 'univentionHost'),
252                     univention.admin.filter.expression('objectClass', 'univentionClient'),
253                     univention.admin.filter.conjunction('!', [univention.admin.filter.expression('objectClass', 'posixAccount')]),
254             ])
255  
256             if filter_s:
(Pdb) 
257                     filter_p = univention.admin.filter.parse(filter_s)
258                     univention.admin.filter.walk(filter_p, rewrite, arg=mapping)
259                     filter.expressions.append(filter_p)
260  
261  ->         for dn, attrs in lo.search(str(filter), base, scope, [], unique, required, timeout, sizelimit, serverctrls, response):
262                     res.append(object(co, lo, None, dn, attributes=attrs))
263             return res
```

Now we define a breakpoint at the very top of the lookup() function and step through it:

```
(Pdb) filter_p
conjunction('&', [expression('objectClass', 'ucsschoolComputer', '='), conjunction('&', [conjunction('|', [expression('name', '', '=*'), expression('description', '', '=*')])])])
(Pdb) n
…
(Pdb) n
-> filter.expressions.append(filter_p)
(Pdb) filter_p
conjunction('&', [expression('objectClass', 'ucsschoolComputer', '='), conjunction('&', [conjunction('|', [expression('cn', b'', '=*'), expression('description', b'', '=*')])])])
(Pdb) list
 13     ])
 14  
 15     if filter_s:
 16             filter_p = univention.admin.filter.parse(filter_s)
 17             univention.admin.filter.walk(filter_p, rewrite, arg=mapping)
 18  ->         filter.expressions.append(filter_p)
 19  
 20     print(str(filter))
```
So now we know, that `univention.admin.filter.walk(filter_p, rewrite, arg=mapping)` breaks it.

Further debugging where it breaks:

```
from univention.admin.filter import conjunction, expression
import univention.admin
from univention.admin.handlers.computers.ipmanagedclient import mapping
lo, po = univention.admin.uldap.getMachineConnection()
filter_s = conjunction('&', [expression('objectClass', 'ucsschoolComputer', '='), conjunction('&', [conjunction('|', [expression('name', '', '=*'), expression('description', '', '=*')])])])

# extracted code
print(repr(filter_s))
exp = filter_s.expressions[-1].expressions[-1].expressions[-1]
univention.admin.mapping.mapRewrite(exp, mapping)
print(repr(filter_s))

# the cause in mapRewrite()
print(mapping.mapValue('description', 'x'))
```

The following patch fixes this:
 
```
diff --git management/univention-directory-manager-modules/modules/univention/admin/mapping.py management/univention-directory-manager-modules/modules/univention/admin/mapping.py
index 0b9580fe78..3c575728a7 100644
--- management/univention-directory-manager-modules/modules/univention/admin/mapping.py
+++ management/univention-directory-manager-modules/modules/univention/admin/mapping.py
@@ -767,7 +767,7 @@ def mapRewrite(filter, mapping):
                if not mapping.shouldMap(key):
                        return
                k = mapping.mapName(key)
-               v = mapping.mapValue(key, filter.value)
+               v = mapping.mapValueDecoded(key, filter.value)
        except KeyError:
                return
        if k:
```
Comment 1 Florian Best univentionstaff 2021-07-07 13:17:19 CEST
Affected are search filters which use the UDM property name and not an LDAP attribute name (e.g. username=foo instead of uid=foo).
Comment 2 Florian Best univentionstaff 2021-07-07 23:54:26 CEST
Fixed in:

univention-directory-manager-modules.yaml
660322806fa3 | Bug #53553: fix broken LDAP filter generation containing bytestring-representations

univention-directory-manager-modules (15.0.11-10)
660322806fa3 | Bug #53553: fix broken LDAP filter generation containing bytestring-representations
Comment 4 Jan Luttermann univentionstaff 2021-07-09 12:16:47 CEST
Tested by trying to recreate the problem. -> output is fixed
Code reviewd -> OK
Comment 5 Erik Damrose univentionstaff 2021-08-04 16:25:40 CEST
<https://errata.software-univention.de/#/?erratum=5.0x61>