Bug 56247 - Join fails if objectClass or attributeType in LDAP schema >2000bytes
Join fails if objectClass or attributeType in LDAP schema >2000bytes
Status: NEW
Product: UCS
Classification: Unclassified
Component: LDAP
UCS 5.2
Other Linux
: P5 normal (vote)
: ---
Assigned To: UCS maintainers
UCS maintainers
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2023-07-03 11:25 CEST by Lukas Zumvorde
Modified: 2023-07-24 08:18 CEST (History)
3 users (show)

See Also:
What kind of report is it?: Bug Report
What type of bug is this?: 5: Major Usability: Impairs usability in key scenarios
Who will be affected by this bug?: 2: Will only affect a few installed domains
How will those affected feel about the bug?: 5: Blocking further progress on the daily work
User Pain: 0.286
Enterprise Customer affected?: Yes
School Customer affected?: Yes
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number:
Bug group (optional):
Max CVSS v3 score:


Attachments
patch (1.19 KB, patch)
2023-07-03 11:25 CEST, Lukas Zumvorde
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Lukas Zumvorde univentionstaff 2023-07-03 11:25:02 CEST
Created attachment 11080 [details]
patch

Slapd can only read lines from the config that are at most 2000bytes long (see https://www.man7.org/linux/man-pages/man5/slapd.conf.5.html). During the join of a UCS system (with LDAP) into the domain we copy the schema from the master. during this process all attributeTypes and objectClasses are written in 1 line each. If any of them is longer than 2000bytes slapd will fail to start and the domain join will fail. 

To fix this issue we need to wrap long lines in the schema.conf during the transfer. This can be accomplished with the following change.

modified   management/univention-directory-listener/tools/univention-get-ldif-from-master.py
@@ -40,6 +40,7 @@ import optparse
 import os
 import sys
 from typing import IO  # noqa F401
+import textwrap
 
 import ldap
 import ldif
@@ -64,13 +65,15 @@ def _update_schema(fp, attr):
 			if oid in OIDS:
 				continue
 			obj = subschema.get_obj(ldap.schema.AttributeType, oid)
-			fp.write('attributetype %s\n' % (obj,))
+			obj_wraped = "\n ".join(textwrap.wrap(obj, 1500, break_long_words=False))
+			fp.write('attributetype %s\n' % (obj_wraped,))
 
 		for oid in replication.subschema_sort(subschema, ldap.schema.ObjectClass):
 			if oid in OIDS:
 				continue
 			obj = subschema.get_obj(ldap.schema.ObjectClass, oid)
-			fp.write('objectclass %s\n' % (obj,))
+			obj_wraped = "\n ".join(textwrap.wrap(obj, 1500, break_long_words=False))
+			fp.write('objectclass %s\n' % (obj_wraped,))
 

In this patch we break arbitrarily at 1500 chars. This number could also be chosen closer to 2000.
Comment 1 Lukas Zumvorde univentionstaff 2023-07-03 12:21:53 CEST
To replicate the issue you need to define an objectclass or attributetype of sufficient length (>2000 chars) on the master/primary.

For example this abomination

objectclass ( 1.3.6.1.4.1.10176.99999.9838.0.0 NAME 'myTestObjectclass' DESC 'my test object class' SUP top AUXILIARY MUST uid MAY ( 
 univentionWindowsReinstall &
 univentionServerReinstall &
 univentionService &
 univentionServerInstallationProfile &
 univentionServerInstallationText &
 univentionServerInstallationOption &
 univentionServerInstallationPath &
 univentionNetworkLink &
 univentionInventoryNumber &
 univentionOperatingSystem &
 univentionOperatingSystemVersion &
 univentionHost &
 univentionClient &
 univentionMacOSClient &
 univentionMobileClient &
 univentionThinClient &
 univentionWindows &
 univentionMemberServer &
 univentionDomainController &
 univentionUbuntuClient &
 univentionLinuxClient &
 univentionDomain &
 univentionBase &
 prohibitedUsername &
 printerModel &
 univentionPackageDefinition &
 printerURI &
 univentionSambaPasswordHistory &
 univentionSambaMinPasswordLength &
 univentionSambaMinPasswordAge &
 univentionSambaBadLockoutAttempts &
 univentionSambaLogonToChangePW &
 univentionSambaMaxPasswordAge &
 univentionSambaLockoutDuration &
 univentionSambaResetCountMinutes &
 univentionSambaDisconnectTime &
 univentionSambaRefuseMachinePWChange &
 univentionConsoleOperation &
 univentionConsoleACLCategory &
 univentionConsoleACLHost &
 univentionConsoleACLBase &
 univentionConsoleACLCommand &
 univentionSambaPrivilegeList &
 sambaLMPassword &
 sambaNTPassword &
 sambaAcctFlags &
 sambaPwdLastSet &
 sambaPwdCanChange &
 sambaPwdMustChange &
 sambaLogonTime &
 sambaLogoffTime &
 sambaKickoffTime &
 sambaBadPasswordCount &
 sambaBadPasswordTime &
 sambaLogonHours &
 sambaHomeDrive &
 sambaLogonScript &
 sambaProfilePath &
 sambaUserWorkstations &
 sambaHomePath &
 sambaDomainName &
 sambaMungedDial &
 sambaPasswordHistory &
 sambaSID &
 sambaPrimaryGroupSID &
 sambaSIDList &
 sambaGroupType &
 sambaNextUserRid &
 sambaNextGroupRid &
 sambaNextRid &
 sambaAlgorithmicRidBase &
 sambaShareName &
 sambaOptionName &
 sambaBoolOption &
 sambaIntegerOption &
 sambaStringOption &
 sambaStringListOption &
 sambaTrustFlags &
 sambaMinPwdLength &
 sambaPwdHistoryLength &
 sambaLogonToChgPwd &
 sambaMaxPwdAge &
 sambaMinPwdAge &
 sambaLockoutDuration &
 sambaLockoutObservationWindow &
 sambaLockoutThreshold &
 sambaForceLogoff &
 sambaRefuseMachinePwdChange &
 sambaClearTextPassword &
 sambaPreviousClearTextPassword &
 univentionSamba4SID &
 univentionSamba4pwdProperties ) )

On a slave/replica or backup run univention-join to initiate the join process.
Comment 2 Florian Best univentionstaff 2023-07-03 12:34:25 CEST
We already did the same in management/univention-directory-replication/replication.py in Bug #46743 git:a91dc1ee1ea770b8906d7e0b1ad39241115acced.

That currently uses the following code:
    def _insert_linebereak(obj: str) -> str:
    ¦   # Bug 46743: Ensure lines are not longer than 2000 characters or slapd fails to start
    ¦   max_length = 2000
    ¦   obj_lines = []
    ¦   while len(obj) > max_length:
    ¦   ¦   linebreak_postion = obj.rindex(' ', 0, max_length)
    ¦   ¦   obj_lines.append(obj[:linebreak_postion])
    ¦   ¦   obj = obj[linebreak_postion + 1:]
    ¦   obj_lines.append(obj)
    ¦   return '\n '.join(obj_lines)

→ we should use the same implementation for both!