LDAP Extended Controls

Abstract

Das Lightweight Directory Access Protocol (LDAP) kennt nur eine begrenzte Anzahl von Operationen. Einige davon sind: Bind Operation, Search Operation, Modify Operation, Add Operation, Delete Operation, Abandon Operation. Die RFC 4511 beschreibt alle Operationen. Nun zeigt die Alltagspraxis aber, dass es wünschenswert wäre, wenn es weitere Operationen, oder auch Erweiterungen...

Das Lightweight Directory Access Protocol (LDAP) kennt nur eine begrenzte Anzahl von Operationen. Einige davon sind:

  • Bind Operation
  • Search Operation
  • Modify Operation
  • Add Operation
  • Delete Operation
  • Abandon Operation

Die RFC 4511 beschreibt alle Operationen. Nun zeigt die Alltagspraxis aber, dass es wünschenswert wäre, wenn es weitere Operationen, oder auch Erweiterungen der bestehenden Operationen geben würde. In der Tat erlaubt das Protokoll beide Möglichkeiten. Zu den zusätzlichen Operationen kann z.B. die startTLS Operation gezählt werden. In diesem und den folgenden Beiträgen möchte ich aber auf die vielfältigen Möglichkeiten der Operationserweiterungen hinweisen.

Die Fähigkeiten eines Verzeichnisdienstes prüfen

Jedes Verzeichnisdienst-Produkt beherrscht unterschiedliche Protokollanforderungen und Protokollerweiterungen. Daher ist es für einen LDAP-Client unabdingbar, die Fähigkeiten des Servers zu prüfen. Die erforderlichen Informationen werden im Root Directory Special Entry (rootDSE) ausgewiesen und sind mit LDAP-Operationen auslesbar. Üblicherweise sollten diese Informationen jedem zugänglich sein, ein anonymous Bind sollte eine Leseoperation auf rootDSE ermöglichen.

In den nächsten Beispielen werde ich vorzugsweise die Software von OpenLDAP und OpenDJ als Beispiele heranziehen. Leider ist es mir nicht gestattet, andere Produkte zu beschreiben. RootDSE wird als leeres Suffix dargestellt, also " ", die Suchbasis ist immer base, da die Fähigkeiten eines Verzeichnisdienstes als Attributtyp geführt werden. Ein Beispiel für OpenLDAP:

$ ldapsearch -LLL -Y EXTERNAL -b "" -s base +
dn:
structuralObjectClass: OpenLDAProotDSE
configContext: cn=config
namingContexts: cn=log
namingContexts: o=avci,c=de
monitorContext: cn=Monitor
supportedControl: 1.3.6.1.4.1.4203.1.9.1.1
supportedControl: 2.16.840.1.113730.3.4.18
supportedControl: 2.16.840.1.113730.3.4.2
supportedControl: 1.3.6.1.4.1.4203.1.10.1
supportedControl: 1.2.840.113556.1.4.319
supportedControl: 1.2.826.0.1.3344810.2.3
supportedControl: 1.3.6.1.1.13.2
supportedControl: 1.3.6.1.1.13.1
supportedControl: 1.3.6.1.1.12
supportedExtension: 1.3.6.1.4.1.1466.20037
supportedExtension: 1.3.6.1.4.1.4203.1.11.1
supportedExtension: 1.3.6.1.4.1.4203.1.11.3
supportedExtension: 1.3.6.1.1.8
supportedFeatures: 1.3.6.1.1.14
supportedFeatures: 1.3.6.1.4.1.4203.1.5.1
supportedFeatures: 1.3.6.1.4.1.4203.1.5.2
supportedFeatures: 1.3.6.1.4.1.4203.1.5.3
supportedFeatures: 1.3.6.1.4.1.4203.1.5.4
supportedFeatures: 1.3.6.1.4.1.4203.1.5.5
supportedLDAPVersion: 3
supportedSASLMechanisms: CRAM-MD5
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: GSSAPI
supportedSASLMechanisms: OTP
supportedSASLMechanisms: EXTERNAL
entryDN:
subschemaSubentry: cn=Subschema

Assertion Control

In dieser Betrachtung interessieren uns nur die beiden supported Controls 1.3.6.1.1.12 (Assertion Control) und 1.3.6.1.1.13.2 (Post-Read Control).

Häufig möchte man eine Operation, sei es nun Search, add, modify, von einer definierten Kondition abhängig machen, sei es nun dass ein bestimmter Attributwert gegeben ist, oder auch nur eine definierte Schreibweise eines Attributwertes vorhanden ist. Mittels Assertion Control ist dies möglich. Ein Beispiel soll dies verdeutlichen. Es gibt den Eintrag dn: cn=Billy Kid,ou=tombstone,o=avci,c=de mit dem Attributwert l=Tombstone, also localityName=Tombstone mit kapitalem T. Im ersten Beispiel soll nur geprüft werden, ob der Attributwert l=Tombstone vorhanden ist. Da die Syntax für diesen Wert caseInsensitiv ist, hat bei der Prüfung die Schreibweise keine Bedeutung.

Bevor ich mit den Beispielen beginne, einige Erläuterungen. Ich habe für libldap eine ~/.ldaprc mit folgenden Inhalt erstellt:

BASE o=avci,c=de
URI ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi

Dies ermöglicht es, keine Basis und keinen Host in einem ldapsearch definieren zu müssen, dies gilt allerdings nur für OpenLDAP und Anwendungen, die mit libldap gelinkt sind. Dies gilt also nicht für OpenDJ und andere Verzeichnisdienste. Auf die einzelnen Operatoren für ldapsearch und ldapmodify möchte ich hier nicht eingehen, die jeweiligen Manual Pages geben hierzu ausreichend Auskunft. Die nachstehend zitierte Datei modify-test.ldif hat folgenden Inhalt:

dn: cn=Billy Kid,ou=tombstone,o=avci,c=de
changetype: modify
replace: telephoneNumber
telephoneNumber: +1555 553 267

In der nachfolgenden ldapmodify Operation soll der Inhalt der Datei modify-test-ldif nur ausgeführt werden, wenn der Attributwert l=Tombstone vorhanden ist.

ldapmodify -Y EXTERNAL -f modify-test.ldif -e assert=l=Tombstone

Es besteht auch noch die Möglichkeit, die ldapmodify Operation nur auszuführen, wenn der Attributwert Tombstone mit großem T vorhanden ist:

ldapmodify -Y EXTERNAL -f modify-test.ldif -e assert=l:caseExactmatch:=Tombstone

Nun möchte ein mancher auch prüfen, ob denn der richtige Wert modifiziert wurde. Die ist mit dem Post-Red Control möglich. Hier nun das gesamte Bespiel mit dem Ergebnis

$ ldapmodify -Y EXTERNAL -f modify-test.ldif -e assert=l:caseExactMatch:=Tombstone'\
-e postread=telephoneNumber
SASL/EXTERNAL authentication started
SASL username: gidNumber=100+uidNumber=1000,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "cn=Billy Kid,ou=tombstone,o=avci,c=de"
control: 1.3.6.1.1.13.2 false ZE0EJWNuPUJpbGx5IEtpZCxvdT10b21ic3RvbmUsbz1hdmNp
LGM9ZGUwJDAiBA90ZWxlcGhvbmVOdW1iZXIxDwQNKzE1NTUgNTUzIDI2Nw==
# ==> postread
dn: cn=Billy Kid,ou=tombstone,o=avci,c=de
telephoneNumber: +1555 553 267
# <== postread

Der Attributwert des Attributtyps telephoneNumber ist also korrekt modifiziert worden. Als Ergebnis wird unter anderem die Zeile control: 1.3.6.1.1.13.2 false ausgewiesen. Der Terminus false bezieht sich hier auf die Criticality der Control, die nicht gegeben ist.

Dieter Klünter, 02. July 2013