Aus Linux-Magazin 10/2025

LDAP-Clients mit der Libldap programmieren

© Dmitry Azovsky / 123RF.com

Möchten Sie in eigenen Anwendungen einen LDAP-Server anzapfen, können Sie sich von der Bibliothek Libldap unter die Arme greifen lassen. Wie unser Schnelleinstieg beweist, genügen vier Funktionsaufrufe, um an die benötigten Einträge zu gelangen.

Benutzerkonten, Rechnerkonfigurationen, Adressdaten und viele weitere Informationen lagern in zahlreichen Unternehmen auf LDAP-Servern. Damit ein selbstgeschriebenes Programm an die Daten herankommt, müssten Sie zunächst mühsam das zur Kommunikation genutzte Lightweight Directory Access Protocol (LDAP) implementieren. Netterweise gibt es für fast alle Programmiersprachen fertige Bibliotheken, die via LDAP mit dem gewünschten Server sprechen und ihm die benötigten Daten entlocken.

Eine dieser Bibliotheken ist Libldap. Sie gehört zur LDAP-Referenzimplementierung OpenLDAP [1], das die meisten Distributionen in ihren Repositories offerieren. Die Libldap steht dabei meist sogar in einem eignen Paket bereit. Einmal installiert, lässt sie sich direkt in C beziehungsweise C++ nutzen, für einige andere Sprachen existieren Wrapper. Python-Entwickler greifen beispielsweise zu python-ldap [2]. PHP [3] unterstützt die Libldap von Haus aus, vorausgesetzt, der LDAP-Support wurde in den Interpreter einkompiliert.

Abbildung 1: Der Zugriff auf einen LDAP-Server läuft in vier Schritten ab – übrigens nicht nur bei der Libldap.

Abbildung 1: Der Zugriff auf einen LDAP-Server läuft in vier Schritten ab – übrigens nicht nur bei der Libldap.

Die Arbeit mit der Libldap läuft immer nach dem Prinzip aus Abbildung 1 ab: Sie öffnen eine Verbindung zum LDAP-Server, melden sich dort an (Binding), durchsuchen oder modifizieren das Verzeichnis und schließen die Verbindung. Listing 1 zeigt dafür ein einfaches Beispiel in C. Es lässt sich auch ohne C-Kenntnisse leicht verstehen und auf andere Programmiersprachen übertragen. Listing 1 sucht nach der Anmeldung im LDAP-Verzeichnis nach allen Einträgen unterhalb der Organizational Unit (Zeile 29) und gibt, wie in Abbildung 2 zu sehen, ihre Distinguished Names (DN) aus.

Abbildung 2: Der Client aus <a href="#artRef-l1">Listing&nbsp;1</a> liefert die Distinguished Names von Frau Greiwes.

Abbildung 2: Der Client aus Listing 1 liefert die Distinguished Names von Frau Greiwes.

Listing 1

Einfacher LDAP-Client in C

#include <ldap.h>
#include <stdio.h>
int main( int argc, char **argv ) {
  int error;
  LDAP *ldap;
  LDAPMessage *result, *entry;
  char *entry_dn = "";
  char *host = "localhost";
  struct berval cred;
  struct berval *server_cred;
  int version;
  // Verbindung festlegen:
  error = ldap_initialize(&ldap, "ldap://localhost:389");
  if (error != LDAP_SUCCESS ) { printf("Fehler: %s\n", ldap_err2string(error)); return -1; }
  // Auf LDAP-Version 3 setzen:
  version = LDAP_VERSION3;
  ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
  // Anmelden:
  cred.bv_val = "secret";
  cred.bv_len = sizeof("secret")-1;
  ldap_sasl_bind_s(ldap, "cn=Manager,dc=zahnrad,dc=com", LDAP_SASL_SIMPLE, &cred, NULL, NULL, &server_cred);
  // Suchen:
  ldap_search_ext_s(ldap, "ou=munich,dc=zahnrad,dc=com", LDAP_SCOPE_SUBTREE, \
    "(&(objectClass=posixAccount))", NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &result);
  // Anzahl Funde ausgeben:
  printf("Anzahl Objekte: %d\n", ldap_count_entries(ldap, result));
  // DNs der gefundenen Einträge ausgeben:
  for ( entry = ldap_first_entry(ldap, result); entry != NULL; entry = ldap_next_entry(ldap, entry)) {
    entry_dn = ldap_get_dn(ldap, entry);
    printf("%s\n", entry_dn);
    ldap_memfree(entry_dn);
  }
  // Aufräumen:
  ldap_memfree(server_cred);
  ldap_msgfree(result);
  ldap_unbind_ext(ldap, NULL, NULL);
}

Stolperfallen

In Listing 1 finden Sie mehrere Funktionen mit der Endung »_s«. Sie zeigt an, dass die Funktion synchron arbeitet und folglich das Programm so lang anhält, bis der LDAP-Server eine Antwort liefert. Die gleichnamigen Funktionen ohne das Suffix »_s« arbeiten asynchron. Da sie dieselben Parameter akzeptieren, kann ein versehentlich vergessenes »_s« zu einem merkwürdigen Programmverhalten führen, ohne dass der Compiler vorher meckert.

Eine weitere Stolperfalle bergen die zahlreichen ausgemusterten Funktionen. So kennt die aktuelle Libldap beispielsweise nicht mehr »ldap_open()« und »ldap_init()«, die früher den Verbindungsaufbau vereinfacht haben. Stattdessen sollen Entwickler jetzt »ldap_initialize()« nutzen. Viele Tutorials im Internet setzen allerdings noch die veralteten Funktionen ein, was den Einstieg erschwert. Wenn sich der Compiler über eine fehlende Funktionsdefinition beklagt, sind Sie wahrscheinlich über eine solche gestrichene Funktion gestolpert. Die Wrapper für Python, PHP und Co. biegen die Aufrufe unter der Haube um. Deshalb tauchen dort teilweise anders benannte Funktionen als in Listing 1 auf. Zum Beispiel nutzt PHP für die Verbindungsdaten »ldap_connect()« anstelle von »ldap_initialize()«.

Das Beispiel in Listing 1 hinterlegt zunächst mit »ldap_initialize()« die Verbindungsdaten zum Server. Seinen Standort und Port verrät die URL im zweiten Parameter. Sofern der Server eine TLS-Verschlüsselung nutzt, verwenden Sie das Protokoll »ldaps://«, sodass sich etwa »ldaps://server.example.com:636« ergibt. Die Eckdaten der Verbindung schiebt »ldap_initialize()« in die Variable »ldap«, die alle anderen Libldap-Funktionen als ersten Parameter benötigen.

Wie alle Funktionen liefert »ldap_initialize()« bei Problemen einen Fehlercode zurück. Diesen Fall fängt Listing 1 ab: Ein Fehler liegt genau dann vor, wenn der Rückgabewert von der Konstanten »LDAP_SUCCESS« abweicht. Dann übersetzt »ldap_err2string()« den Fehlercode in eine menschenlesbare Meldung. Nach demselben Prinzip sollten Sie auch bei allen anderen Funktionen auftretende Fehler abfangen. Damit der Quellcode besser lesbar ist, verzichtet Listing 1 im weiteren Verlauf darauf.

Ausweiskontrolle

Die Eigenschaften der Verbindung manipuliert die Funktion »ldap_set_option()«. Mit ihr können Sie etwa die LDAP-Version 3 vorschreiben. Genau das macht auch der Quellcode in Listing 1: »LDAP_OPT_PROTOCOL_VERSION« (Zeile 21) zeigt eine Änderung auf die Protokollversion an, die in der Variablen »version« liegt. Das so eingestellte LDAPv3 setzt die nachfolgende Funktion »ldap_sasl_bind_s()« voraus – sonst würde der Aufruf scheitern.

Eben jene Funktion klopft nun beim Server an und führt eine Authentifizierung durch. Entgegen ihres Namens kann sie ebenfalls eine einfache Authentifizierung anstellen. Dazu schalten Sie sie wie in Listing 1 mit »LDAP_SASL_SIMPLE« (Zeile 26) in den entsprechenden Modus. Am Server meldet sich die Funktion dann als »”cn=Manager,dc=zahnrad,dc=com”« an. Dessen Passwort verpacken Sie in eine »struct berval«. Die wiederum möchte das eigentliche Passwort (»cred.bv_val«) und seine Länge (»cred.bv_len«) wissen. Die übrigen Parameter können bei einer einfachen Anmeldung auf »NULL« bleiben. In »server_cred« landen die vom Server zurückgelieferten Credentials, die jedoch hier keine Rolle mehr spielen.

Fahndungsarbeit

Die Funktion »ldap_search_ext_s()« sucht anhand der ihr übergebenen zahlreichen Kriterien im LDAP-Verzeichnis nach passenden Einträgen. Bei einfachen Abfragen können Sie die meisten der verlangten Informationen netterweise mit »NULL« oder »0« auslassen. In Listing 1 sieht sich die Funktion alle untergeordneten Einträge (»LDAP_SCOPE_SUBTREE«) an, die im Baum unterhalb von »ou=munich,dc=zahnrad,dc=com« hängen. Sie liefert dabei lediglich die Einträge zurück, denen die Objektklasse »posixAccount« zugewiesen wurde. »LDAP_NO_LIMIT« hebt das Zeitlimit für die Suche auf. Der Server darf also beliebig lange passende Ergebnisse herauskramen. In der Variablen »result« landen schließlich alle gefundenen Einträge. Die wiederum zählt »ldap_count_entries()«.

Die nachfolgende Schleife durchläuft sämtliche Fundstücke. Dabei hilft ihr »ldap_first_entry()« (Zeile 36), das den ersten gefundenen Eintrag ausgibt, und »ldap_next_entry()«, das zum jeweils nächsten wechselt. Den Distinguished Name des Eintrags verrät »ldap_get_dn()«. Abschließend räumt Listing 1 den Arbeitsspeicher auf: »ldap_memfree()« wirft viele von den Libldap-Funktionen über Bord, »ldap_msgfree()« kümmert sich um die Suchergebnisse. »ldap_unbind_ext()« schließt die Verbindung. Vor allem in Skriptsprachen können Sie sich das feuchte Durchwischen sparen, dort müssen Sie lediglich die Verbindung kappen. Unter PHP erledigt das »ldap_close()«.

Fazit

Vier Handgriffe genügen, um mit der Libldap ein LDAP-Verzeichnis anzubohren. Die Hauptarbeit besteht darin, die nötigen Daten für die wissensdurstigen Funktionen zusammenzusuchen. Neben »ldap_search_ext_s()« stellt die Libldap noch Funktionen bereit, über die Sie Einträge hinzufügen, verändern, miteinander vergleichen, umbenennen und löschen können. Sie alle dokumentieren die entsprechenden Manpages, die sich auf den Seiten [4] des OpenLDAP-Teams durchsuchen lassen. Sie setzen allerdings wie auch die Programmierung mit der Libldap entsprechendes Wissen um LDAP-Verzeichnisse voraus. (csi)

Blick über den Tellerrand

Viele LDAP-Bibliotheken setzen nicht auf der Libldap auf, sondern nutzen eine eigene LDAP-Implementierung. Das trifft etwa auf die Bibliotheken für Rust und Go zu. Dennoch funktionieren sie nach demselben Prinzip wie die Libldap. So dürfte nach der Lektüre des Artikels die Kontaktaufnahme mit dem LDAP-Package von Go sehr vertraut aussehen (Listing 2). Den Part von »ldap_initialize()« übernimmt »DialURL()«. Das Binding zerfällt hier allerdings in zwei Schritte: Zunächst presst »NewSimpleBindRequest()« die Anmeldeinformationen in ein entsprechendes Objekt, mit dem anschließend »SimpleBind()« das eigentliche Login ausführt.

Listing 2

LDAP in Go

ldap, _ := DialURL("ldap://localhost:389")
controls := []Control{}
controls = append(controls, NewControlBeheraPasswordPolicy())
bindRequest := NewSimpleBindRequest("cn=Manager,dc=zahnrad,dc=com", "secret", controls)
r, _ := ldap.SimpleBind(bindRequest)
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben