Bug 56247

Summary: Join fails if objectClass or attributeType in LDAP schema >2000bytes
Product: UCS Reporter: Lukas Zumvorde <zumvorde>
Component: LDAPAssignee: UCS maintainers <ucs-maintainers>
Status: NEW --- QA Contact: UCS maintainers <ucs-maintainers>
Severity: normal    
Priority: P5 CC: best, hahn, turfeld
Version: UCS 5.2   
Target Milestone: ---   
Hardware: Other   
OS: Linux   
See Also: https://forge.univention.org/bugzilla/show_bug.cgi?id=46743
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

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!