%% LyX 1.1 created this file.  For more info, see http://www.lyx.org/.
%% Do not edit unless you really know what you are doing.
\documentclass[a4paper,german]{article}
\usepackage[T1]{fontenc}
\usepackage[latin1]{inputenc}
%\pagestyle{empty}
\usepackage{babel}
\usepackage{graphics}
\usepackage{times}
%\date {2. M\"{a}rz 2003}

\makeatletter

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
\providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
 \newenvironment{lyxcode}
   {\begin{list}{}{
     \setlength{\rightmargin}{\leftmargin}
     \raggedright
     \setlength{\itemsep}{0pt}
     \setlength{\parsep}{0pt}
     \normalfont\ttfamily}%
    \item[]}
   {\end{list}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
\usepackage[breaklinks]{hyperref}
%\pagestyle{empty}

\makeatother
\begin{document}

\title{LDAP-Benutzung mit Java+Tomcat und Python+Zope}


\author{Holger Blasum, Andreas Meisl}

\maketitle
(Vortrag auf dem \href{http://www.guug.de/veranstaltungen/ffg2003/}{GUUG-Frühjahrsfachgespräch}
Bochum am 28. März 2003.)

\begin{abstract}
Wir berichten über Implementation und Installation von Klienten für
eDirectory von Novell mit Python und Java via Plaintext und TLS/SSL sowie Performancemessungen.
Note: You can find an English version of this paper as well as source
code at

\href {http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/}{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/}.
\end{abstract}

\section{Anwendungsfall }

%\thispagestyle{empty}

Die Tierärztliche Fakultät der Universität München verwendet Novell
eDirectory als Verzeichnisdienst für Studentennutzerdaten%
\footnote{Diese Nutzung gibt es auch an anderen bayerischen Universitäten, siehe
z.B. AK Netz PC, \href{http://www-lan.uni-regensburg.de/ak/netz/}{http://www-lan.uni-regensburg.de/ak/netz/}.
}. Einmal im Semester tragen sich die Studierenden in Wahlpflichtfächer
ein; dies geschieht seit Juli 2002 webbasiert%
\footnote{\href{http://www.vetmed.uni-muenchen.de/studium/wahlpflicht/kurse/index.html}{http://www.vetmed.uni-muenchen.de/studium/wahlpflicht/kurse/index.html}
}. 


\section{Verzeichnisdienste und LDAP}

Verzeichnisse sind etwa Telefonbücher oder das DNS-System, das Datenmodell
ist hierarchisch. Von CCITT(ITU) / ISO wurde insbesondere 1988 der
Standard X.500 für Verzeichnisdienste verabschiedet \cite{Kla01,Chad96}.
LDAP%
\footnote{RFC 1777 (Version 2), 2251 (Version 3)
} ist eine ein abgespeckte Variante des Verzeichniszugriffsprotokolls
(DAP) auf X.500-Verzeichnisse. Gedacht ursprünglich zur Replikation
von Verzeichnisdaten, wird LDAP nun von populären Verzeichnisdiensten
(also z.B. OpenLDAP%
\footnote{\href{http://www.openldap.org/}{http://www.openldap.org/}
}, Novell eDirectory%
\footnote{zuvor: Novell Directory Services (NDS), \href{http://www.novell.com/products/edirectory/}{http://www.novell.com/products/edirectory/}
} , oder Microsoft Active Directory%
\footnote{\href{http://www.microsoft.com/windows2000/technologies/directory/ad/default.asp}{http://www.microsoft.com/windows2000/technologies/directory/ad/default.asp}
}) als Schnittstelle angeboten.

In diesen Anwendungen wird LDAP typischerweise über TCP verwendet
(obwohl auch ein verbindungsloses UDP spezifiziert wurde%
\footnote{RFC 1798
}).

\vspace{0.3cm}
{\centering \resizebox*{1\textwidth}{!}{\includegraphics{image/ldapethereal.eps}} \par}
\vspace{0.3cm}

LDAP verwendet die ASN.1/BER-Kodierung. Ein tcpdump (\emph{tcpdump
-s 0 -w filename}) einer LDAP-Sitzung kann mit einem Paketanalysierer,
der auch ASN.1/BER und LDAP-Anwendungsklassen versteht (zum Debuggen
sehr hilfreich) wie Ethereal%
\footnote{\href{http://www.ethereal.com/}{http://www.ethereal.com/}
}, betrachtet werden. Im Bild sehen wir z.B. eine \emph{modify}-Anfrage
von LDAP Version 3.


\section{Ein Sandkasten}

Sowohl OpenLDAP als auch eDirectory sind frei erhältlich (wie Rede
im ersteren, wie Bier im zweiten Fall). Da wir jedoch nur Clients
testen wollen, bietet sich hier auch der Test-Site von Novell an%
\footnote{\href{http://www.nldap.com/}{http://www.nldap.com/}, in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/conf/nldapcom/nldapaccount.txt}{nldapaccount.txt}
finden sich auch genaue Zugangsdaten (\emph{cn/userPassword}) zu dem
derzeitigen Account. Wer sich bei nldap.com selbst einen Account aufsetzen
will, folge den Links \emph{eDirectory Account Adminstration->create
NDS Container \& its admin user}.
}.

\vspace{0.3cm}
{\resizebox*{1\textwidth}{!}{\includegraphics{image/repview.eps} \par}
\vspace{0.3cm}

Betritt man diesen über ein GUI wie etwa dem \emph{LDAP browser/editor}%
\footnote{\href{http://www.iit.edu/~gawojar/ldap/}{http://www.iit.edu/~gawojar/ldap/}
}, oder Novells hauseigenen Werkzeugen (nwadmin, Console1), so kann
man schnell die einfache Struktur der Sandbox überschauen. Es gibt
Zoologiekurse, die von Studenten belegt werden können (Register/Unregister).
Alternativ kann über die Kommandozeile ein (auch menschenlesbares)
\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/nldapcom/repositoryldif.txt}{LDIF-File} erzeugt werden: 

\begin{lyxcode}
ldapsearch~-vLx~-b~\char`\"{}ou=subldap,~ou=user,

o=novell\char`\"{}~-h~www.nldap.com

\char`\"{}(objectclass={*})\char`\"{}~>~subldap.ldif
\end{lyxcode}

\section{Einfache LDAP-Klienten auf Kom\-mando\-zeilen\-ebene}


\subsection{Installation}

Java: Wir haben die Blackdown ports%
\footnote{\href{http://www.blackdown.org/}{http://www.blackdown.org/}
} verwendet, vorzugsweise Java 1.4.1%
\footnote{Für Java 1.3 muss noch die Java Secure Socket Extension (JSSE) 1.0.2
in das Java-Erweiterungs-Verzeichnis installiert werden; JSSE ist
in Java 1.4 bereits enthalten.~
}. Als nächstes besorgen wir uns das Java Novell Developer Kit%
\footnote{\href{http://developer.novell.com/ndk/jldapunx.htm}{http://developer.novell.com/ndk/jldapunx.htm},
15. November 2002, auch gelinkt von \href{http://www.openldap.org/jldap/}{http://www.openldap.org/jldap/},
wobei Dokumentation und Beispiele ebenfalls an dieser Stelle zu finden
sind (ausführliche Dokumentation auch in \cite{Har00}). 
}, wirklich gebraucht wird davon jedoch nur die Datei \emph{ldap.jar},
die wir nach \emph{/usr/lib/j2sdk1.4.1/ jre/lib/ext/} installieren
(oder man erweitere alternativ den Klassenpfad).

Python: Wir verwenden Python 2.1.3 und die python-ldap Bibliothek%
\footnote{\href{http://python-ldap.sourceforge.net/}{http://python-ldap.sourceforge.net/}
} in Release 2.0.0pre06 (diese geht dann nach \emph{/usr/lib/python2.1/site-packages}).

Server; OS: Zusätzlich zur beschriebenen Sandbox haben wird als LDAP
Verzeichnis ein eDirectory 8.6.2 auf Netware 5.1%
\footnote{eDirectory läuft auch unter AIX, Linux, Solaris, Windows.
} verwendet, Python und Java Middleware wurden auf Debian Woody GNU/Linux
2.4.19 i686 installiert. 


\subsection{Kursliste anschauen (anonym)}

Wir wollen ein Programm schreiben, mit dem wir anonym den Inhalt des
Kursverzeichnisses anschauen können, es gibt also folgende Aufgaben:

\begin{itemize}
\item LDAP-Verbindung initialisieren
\item LDAP Suchanfrage an Server senden
\item die Ergebnisliste \emph{res} auslesen
\item Verbindung schließen
\end{itemize}
Beginnen wir also mit einer Implementation in Python (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.py}{ListCourses.py}):

\begin{lyxcode}
\#!/usr/bin/python~

import~ldap~

l=ldap.initialize(\char`\"{}ldap://www.nldap.com:389\char`\"{})

res~=~l.search\_s(\char`\"{}ou=200201,~ou=courses,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{},~ldap.SCOPE\_ONELEVEL,~

~~\char`\"{}objectclass={*}\char`\"{})~

for~r~in~res:~

~~print~r{[}0{]}

c.unbind\_s()
\end{lyxcode}
Dies ergibt dann (offensichtlich):

\begin{lyxcode}
blasum@promitheas:\$~./ListCourses.py

cn=Ant,ou=courses,ou=zoology,ou=user,o=NOVELL

cn=Bee,ou=courses,ou=zoology,ou=user,o=NOVELL

cn=Cow,ou=courses,ou=zoology,ou=user,o=NOVELL


\end{lyxcode}
Das {}``\_s'' heisst hier synchrone Verbindung (Client wartet, bis
die Daten da sind). In der JLDAP API wird die Unterscheidung synchron/asynchron
einfach durch Methodenüberladung geregelt. Der Java-Client, der hier,
wie auch in allen späteren Beispielen, ebenfalls synchron agiert,
sieht so aus: (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.java}{ListCourses.java}):

\begin{lyxcode}
import~java.io.{*};

import~com.novell.ldap.{*};

public~class~ListCourses~\{~

~~public~static~void~main~(String~{[}{]}~args)

~~~~throws~LDAPException~\{

~~~~LDAPConnection~c=new~LDAPConnection~();

~~~~c.connect~(\char`\"{}www.nldap.com\char`\"{},~389);~

~~~~LDAPSearchResults~res~=~c.search(~

~~~~~~\char`\"{}ou=200201,~ou=courses,~ou=zoology,

~~~~~~ou=subldap,~ou=user,~o=novell\char`\"{},~

~~~~~~LDAPConnection.SCOPE\_ONE,~null,~null,

~~~~~~false);

~~~~while~(res.hasMore())~\{

~~~~System.out.println(res.next().getDN());

~~~~c.disconnect();

~~~~\}

~~\}

\}


\end{lyxcode}
Abschweifung: Wenn wir Python schreiben, es aber von Java interpretieren
lassen wollen, gibt es dafür den Jython-Interpreter%
\footnote{\href{http://www.jython.org/}{http://www.jython.org/}
}, \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.jy}{ListCourses.jy}
sieht dann (als Mischung von Python und JLDAP) so aus:

\begin{lyxcode}
from~com.novell.ldap~import~{*}

c~=~LDAPConnection~()

c.connect~(\char`\"{}www.nldap.com\char`\"{},~389)

res~=~c.search(\char`\"{}ou=200201,~ou=courses,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{},~~~~~~~~

~~LDAPConnection.SCOPE\_ONE,~None,~None,~0)~

while~res.hasMore():

~~~print~res.next().getDN()

c.disconnect()


\end{lyxcode}
Beobachtung: der Durchlauf von Java hat 1,0 Sekunden, von Python 0,14
Sekunden, von Jython 4,5 Sekunden gebraucht (+ ca. 0,5-1 Sekunden
system response für die remote LDAP-Abfrage). Die Interfaces von JLDAP
und python-ldap sind sehr ähnlich, wobei python-ldap etwas schlanker
ist.


\subsection{Einschreibungsdaten ändern (als Administrator)}

Wir wollen mit einer authentifizierten Verbindung Einträge im Verzeichnis
verändern. Folgende Schritte sind notwendig:

\begin{itemize}
\item LDAP-Verbindung initialisieren
\item zum Server authentifizieren
\item \emph{modify}-Anfrage senden (bestehend aus einer Liste von Modifikationen)
\item Verbindung schließen
\end{itemize}
Hier ist eine Python-Version (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.py}{Register.py}): 

\begin{lyxcode}
import~ldap

import~sys

if~len~(sys.argv)~!=~3:~

~~print~\char`\"{}Usage:~Register.py~course~student\char`\"{}

~~sys.exit(1)

ldap.set\_option~(ldap.OPT\_PROTOCOL\_VERSION,
~~ldap.VERSION3)

c=ldap.initialize(\char`\"{}ldap://www.nldap.com:389\char`\"{})

c.simple\_bind\_s(\char`\"{}cn=admin,~ou=subldap,

~~ou=user,~o=novell\char`\"{},~\char`\"{}secret\char`\"{})~

c.modify\_s~(\char`\"{}cn=\char`\"{}~+~sys.argv{[}1{]}~+~\char`\"{},~\char`\"{}~+~

~~\char`\"{}ou=200201,~ou=courses,~ou=zoology,

~~~ou=subldap,~ou=user,~o=novell\char`\"{},

~~{[}(ldap.MOD\_ADD,~\char`\"{}member\char`\"{},~\char`\"{}cn=\char`\"{}~+

~~sys.argv{[}2{]}~+~\char`\"{},\char`\"{}~+~\char`\"{}ou=students,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{}){]})

c.unbind\_s()
\end{lyxcode}
Die Umkehroperation \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Unregister.py}{Unregister.py}
sieht genauso aus, nur wurde hier \newline \emph{MOD\_ADD} durch \emph{MOD\_DEL}
ersetzt. Die Java-Versionen \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.java}{Register.java}
und \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Unregister.java}{Unregister.java}
unterscheiden sich ebenfalls nur durch \emph{ADD} vs \emph{DELETE}.

Beobachtungen: der Zeitverbrauch ist 1,2 Sekunden für JLDAP, 0,11
Sekunden für python-ldap. Wenn man das eDirectory verwendet, muss
man explizit die LDAP Protokoll-Version auf 3 setzen (sonst scheitert
\emph{simple\_bind\_s} leise in python-ldap, JLDAP hat dagegen Version
3 als Default). Wenn \emph{simple\_bind\_s} erfolgreich ist, aber
die \emph{modify}-Anfrage scheitert, gibt es eine Ausnahme (die wir
hier nicht abfangen, um es einfach zu halten). 


\subsection{Eine Abstraktionsebene dazwischenquetschen: \emph{ChangeManager}}

Damit wir nicht direkt die Bibliotheksspezifika in die Anwendung verdrahten,
haben wir eine Abstraktionsklasse (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/ChangeManager.java}{ChangeManager.java}
bzw \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/ChangeManager.py}{ChangeManager.py})
erstellt, die sich um die LDAP-Befehle schmiegt (unter der Pr\"asentationslogik),
z.B.:

\begin{lyxcode}
Python~2.1.3~(\#1,~Sep~12~2002,~00:24:18)

{[}GCC~2.95.4~20011002~(Debian~prerelease){]}~on~linux2~

>\,{}>\,{}>~import~ChangeManager~

>\,{}>\,{}>~c~=~ChangeManager.ChangeManager()~

>\,{}>\,{}>~courses~=~c.getCourseList('03')
\end{lyxcode}
Indem wir die gesamte direkte LDAP-Interaktion in den ChangeManager
verbergen ist es dann auch noch möglich ein ganz anderes (z.B. relationales)
Datenrepositorium einzuhängen.

Die Gesamtarchitektur hat also drei Ebenen (three-tiered): LDAP/eDirectory
als Speicher, eine Mittelschicht (ChangeManager) für Logik und Zope
DTML oder Java Servlets für die Präsentationsschicht. Wenn man das
auf das Publisher/Subscriber Muster (etwa bei \cite{Gam94}) abbilden
will, so sind Subscriber/Observer Kommandozeilen oder Webinterface-Interaktion,
der Publisher liegt im Verzeichnis.


\section{Präsentationsschicht}


\subsection{Zope DTML}

Naiver Ansatz: Mit Zope 2.5.1 (läuft auf Python 2.1) haben wir (recht brutal) unser
Servlet \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.py}{Courses.py}
nach \emph{/usr/lib/zope/Extensions} kopiert (gesymlinkt), Infrastruktur
(alle anderen Pythonklassen) nach \emph{/usr/lib/python2.1/} kopiert.
In der \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/zope/wahl.xml}{Zope-Instanz}
gibt es nun eine DTML-Seite (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/zope/index.html.txt}{index.html})
die via 

\begin{lyxcode}
<dtml-var~expr=\char`\"{}doGet(~term=REQUEST{[}'term'{]},

course=REQUEST{[}'course'{]},~student=REQUEST{[}'student'{]},

password=REQUEST{[}'password'{]},

raction=REQUEST{[}'raction'{]})\char`\"{}>


\end{lyxcode}
eine Zope \emph{External Method} aufruft (\emph{doGet}), die einen
Verweis auf die Methode \emph{doGet} von \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.py}{Courses.py}
darstellt. 
Damit Änderungen im Code von Courses.py
wirksam werden, muss Zope neu gestartet werden.

Einbau als Zope-Komponente (''Produkt''): In dem Produkt \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Products/Courses}{Courses} lassen wir die LDAP-Verbindung nach Benutzung offen und \"offnen sie nur dann neu, wenn sie nicht mehr besteht (self.c == None). Wir werden sehen, dass diese Technik (abgeschaut von dem Zope-Produkt LDAPUserFolder) uns TLS/SSL-Handshakes ersparen wird. 

\subsection{Tomcat Servlets}

Der populärste Zugriff auf Java-Funktionalität über das Web sind Servlets
oder JSPs. Dazu verwenden wir Tomcat 4.1.18. Bei der Installation
und Entwicklung kann Tomcat recht einfach debug-freundlich gesetzt
werden (via debug-levels und reloadable in der \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/tomcat/server.xml}{server.xml}),
dies ist in der Produktionsversion ggf zurückzusetzen. Je nach den
Defaults des Java-Sicherheitsmodells kann es notwendig sein, \emph{SocketPermissions}
in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/tomcat/04webapps.policy}{policy files}
zu setzen (das war nötig bei dem Debian Tomcat-Paket, nicht aber bei
einem direkten Download von jakarta.apache.org). \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.java}{Courses.java}
ist das komplette Servlet.


\section{TLS/SSL }

Nach diesem Proof-of-Concept will man das Ganze natürlich noch sicher
machen: dabei gibt es einerseits die Kommunikation zwischen Webserver
und Browser, andererseits die Kommunikation zwischen Webserver und
LDAP-Daten\-spei\-cher (eDirectory-Server). 

SSL wurde ursprünglich von Netscape entwickelt und spezifiert, TLS
heißen die Weiterentwicklungen seit Übergabe an die gleichnamige IETF
Working Group, die Protokolle werden deswegen meist in einem Atemzug
genannt \cite{Re01}. Praxis ist, dass die meisten Clients beim Handshake
dem Server eine Vielzahl von Übertragungsoptionen anbieten, von denen
sich der Server eine heraussucht. Die zunehmende Popularität von TLS-Protokollen
sieht man daran, dass TLS Präferenz sowohl von OpenLDAP \emph{slapd}
2.1.12 wie auch eDirectory 8.7 ist (während sich eDirectory 8.6.2
dagegen mit dem SSL-Protokoll begnügt).

Bei LDAP über TLS/SSL wird der Port 636 verwendet, es wird zunächst
eine TLS/SSL-Verbindung aufgebaut; ist der Handshake erfolgreich,
so wird über TLS/SSL \emph{data transmissions} das LDAP-Protokoll
abgewickelt. Genauso ist es natürlich beim HTTPS-Protokoll (Port 443)%
\footnote{Etwas irreführend ist es an dieser Stelle, dass es auch eine RFC 2830
für die TLS-Initialisierung \emph{nach bereits erfolgtem} unverschlüsseltem
Verbindungsaufbau über LDAP gibt, diese wird jedoch selten implementiert. 
}.


\subsection{Webserver $\leftrightarrow$ Browser}

Obwohl sowohl Tomcat als auch Zope SSL-Unterstützung bieten, haben
wir beide hinter das \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/apache/vhost.conf}{SSL Modul von Apache}
(\emph{lib\_modapache\_ssl}) geschaltet, dies geht zum Beispiel über
die \emph{ProxyPass}-Anweisung (Vorteil: nur eine Stelle für Webserverzertifikate,
das SSL Modul von Apache ist erprobt).


\subsection{Webserver $\leftrightarrow$ LDAP-Server}


\subsubsection*{Generelles}

Um einen Man-in-the-Middle-Attack zu vermeiden, wird das X.509-Zertifikat
\emph{der den öffentlichen Schlüssel des LDAP-Servers zertifiziert
habenden Zertifizierungsstelle} (CA-Zertifikat; genau diese CA ist
nämlich auf dem LDAP-Server-Zertifikat vermerkt, das beim zweiten
Schritt des Handshakes übertragen wird) dem Client einmal manuell
zugeführt\cite{Mar02}, dabei ist zu beachten:

\begin{itemize}
\item Im LDAP-Server-Zertifikat muss als \emph{Subject name (cn)} der FQDN
(voller Domänenname) des LDAP-Servers stehen (ist genauso wie bei
den für HTTPS verwendeten Zertifikaten, aber auch hier haben die Novell-Tools
das genausowenig voreingestellt wie die \emph{openssl}-Tools).
\item Das CA-Zertifikat (z.B. der \emph{Institutional CA} bei einem eDirectory)
muss also in eine Datei exportiert werden (das \emph{b64}-Format bei
Novell ist genau das \emph{PEM}-Format bei den OpenLDAP-Tools), welche
dann dem LDAP-Klienten in geeigenter Form zugänglich gemacht wird. 
\end{itemize}
Zum Einblick speziell in TLS/SSL-Transaktionen ({}``wurde das nun
verschlüsselt oder unverschlüsselt übertragen, was für ein Protokoll
verwenden wir eigentlich gerade ?''), waren {\small äußerst} hilfreich:
\emph{ssldump}%
\footnote{\href{http://www.rtfm.com/ssldump/}{http://www.rtfm.com/ssldump/}
} (man sieht alle TLS/SSL-Transaktionen auf einem Interface vorbeilaufen),
\emph{ldapsearch -d 7}%
\footnote{\href{http://www.openldap.org/}{http://www.openldap.org/}
} (im hohen Debuglevel wie auch slapd sehr verbos). X.509 Zertifikate
schaut man sich entweder im laufenden Fluss dieser Debugger oder statisch
mit {}``\emph{openssl x509 -noout -text -in cert.pem}'' an.


\subsubsection*{Python-Client}

Seit Version 2.0.0pre06 läuft python-ldap auch über TLS/SSL (bei den
Protokollverhandlungen im Handshake einigte man sich dann unter in
dieser Hinsicht im Default belassenen OpenSSL/eDirectory 8.6/8.7 konkret
auf \emph{RSA with 3DES EDE CBC SHA}). Mit 

\begin{lyxcode}
ldap.set\_option~(ldap.OPT\_X\_TLS\_CACERTFILE,

~~\char`\"{}\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/nldapcacert.pem}{nldapcacert.pem}\char`\"{})
\end{lyxcode}
teilen wir dem Client mit, wo sich das CA-Zertifikat befindet.

Dann muss man nur noch die LDAP URL anpassen, also (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/RegisterSSL.py}{RegisterSSL.py}): 

\begin{lyxcode}
c~=~ldap.initialize~(\char`\"{}ldaps://www.nldap.com:636\char`\"{})~
\end{lyxcode}
statt zuvor (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.py}{Register.py}):

\begin{lyxcode}
c~=~ldap.initialize~(\char`\"{}ldap://www.nldap.com:389\char`\"{}).
\end{lyxcode}
Damit das Ganze funktioniert und nicht etwa in einem 

\begin{lyxcode}
ldap.SERVER\_DOWN:~\{'desc':~\char`\"{}Can't~contact~LDAP~server\char`\"{}\}
\end{lyxcode}
endet, sind insbesondere zu beachten: 

\begin{itemize}
\item Die auf dem Klienten installierten openldap-Bibliotheken müssen tatsächlich
TLS/SSL unterstützen (unter Debian z.B. muss derzeit auf einem \emph{stable}-System
explizit \emph{libldap2-tls} aus \emph{testing} installiert sein).
\end{itemize}

\subsubsection*{Java-Client}

Das CA-Zertifikat wird hier typischerweise mit dem \emph{keytool}
in einer \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/nldapcacert.keystore}{Datei} im Keystore-Format gespeichert, die dann von \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/RegisterSSL.java}{RegisterSSL.java} in einem
 \emph{java.security.Security. addProvider()}-Aufruf ausgelesen
wird%
\footnote{Bei der Installation folgen wir weitgehend den Hinweisen von Susan
Perrin, 04. Juni 2002, auf novell.devsup.ldap\_j.
}: Bei den Protokollverhandlungen im Handshake scheint JSSE als Default
(das auf Geschwindigkeit optimierte) \emph{RSA with RC4 128 MD5}
zu bevorzugen.


\section{Diskussion}


\subsection{Zugänglichkeit von eDirectory via LDAP}

Die LDAP-Schnittstelle von eDirectory \cite{Ser02} scheint zu LDAPv3
User Schema (RFC 2256) weitgehend konform; eine (sinnvolle) Besonderheit
war, dass das Attribut \emph{userPassword} nur vergleichbar (Zugriff
mit \emph{compare}-Operationen), nicht jedoch auslesbar war%
\footnote{\href{ http://groups.google.com/groups?threadm=r4Xy7.2399\%241v1.8347\%40prv-forum2.provo.novell.com}{Andy Chalarambous, 16. Okt 2001, auf novell.support.ds.nds-general}
}, sonst sind wir in dieser Hinsicht auf keine Abweichungen gestossen. 

Ein Unterschied der Bedienung von eDirectory via LDAP-Schnittstelle
gegenüber Novells nativeren Schnittstellen (DirXML, NJCL-Javabibliotheken)
liegt allerdings darin, dass LDAP als leichtgewichtiges Protokoll
allgemein keine Transaktionen zulässt, eDirectory jedoch zum Teil
nicht-atomare Operationen verwendet (z.B. ist Gruppenmitgliedschaft
in eDirectory sowohl ein Attribut der Gruppe als auch ein Attribut
des Users). Wir haben deshalb Gruppenmitgliedschaft (=Kursbelegung)
nur als Attribut der Gruppe definiert, was auch ohne weiteres möglich
ist. 

Bei der Gruppenmitgliedschaft war auch eine Stelle erkenntlich, an
der das Mapping des eDirectory-Schemas noch nicht ganz stabil war:
in Version 8.6.1 war es das Attribut \emph{member}, in Version 8.6.2
dagegen \emph{uniqueMember} (beides RFC 2256 kompatibel).


\subsection{Performance und Usability}

Mit 400 gleichzeitigen Benutzern für 60 Sekunden unter \emph{siege}%
\footnote{\href{http://www.joedog.org/siege/index.shtml}{http://www.joedog.org/siege/index.shtml}
} erreichen wir folgende Performance (399MHz PII, 796 bogomips, 128
MB RAM):

\begin{tabular}{|l|r|}
\hline 
Apache direkt&
25-35 Transaktionen/Sek.\\
\hline
\hline 
Apache+Java+Tomcat (ohne TLS/SSL)&
10-12 Transaktionen/Sek.\\
\hline 
Apache+Zope+Python (ohne TLS/SSL)&
10-12 Transaktionen/Sek.\\
\hline 
Apache+Java+Tomcat (mit TLS/SSL, kein Pooling)&
3,2 Transaktionen/Sek.\\
\hline 
Apache+Zope+Python (mit TLS/SSL, kein Pooling)&
3,2 Transaktionen/Sek.\\
\hline 
Apache+Zope+Python (mit TLS/SSL, Pooling)&
10-12 Transaktionen/Sek.\\
\hline
\end{tabular}

Der bei den Kommandozeilenklienten gemessene initiale Laufzeitüberhang
für das Laden der Java Virtual Machine verliert sich also bei Verwendung
einer aktuellen Tomcat-Version (wie 4.1.18; sehr viel schlechter schnitt
das Java+Tomcat-Duo bei Verwendung der älteren Tomcat Version 4.0.6
auf, hier gab es eine z.T. 2-8 mal schlechtere Performance). 

Offensichtlich wird die Performance verbessert, wenn statt einem neuen Verbindungsaufbau für jede Anfrage
über TLS/SSL zwischen Webserver und LDAP-Server eine einmal geöffnete
Verbindung wiederverwendet wird. Dieses Pooling hatten wir im Zope-Produkt durch Wiederverwertung der Verbindungen erreicht.

In Angesicht der gleichgelagerten Performancedaten kann unsere Nebeneinanderstellung
der Bibliotheks-Interfaces (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/doc/api.txt}{api.txt})
wohl hilfreich sein sowohl für Leute, die (wie wir) ihren Java-Prototypen
nun in endlich Python implementieren wollen, wie auch andersherum
(soll auch es auch geben): dass die Bibliotheken so ähnlich sind,
ist in Anbetracht der in RFC 1823 expliziert spezifizierten C API
kein Wunder, wobei der Preis in der feineren Typisierung in den JLDAP-Bibliotheken
(Rückgabewerte beim Suchen nach Attributen sind z.B. Instanzen von
\emph{com.novell.ldap.AttributeSets} in JLDAP, \emph{Hashes von Listen}
in python-ldap) für uns erhöhten Aufwand an Typkonversionen im ChangeManager
bedeutete.


\subsection{TLS/SSL f\"ur Python und Zope}

In einem früheren - diesem in mancher Hinsicht ähnlichen - Artikel
\cite{Ri01a} stellte die Nichtverfügbarkeit von LDAPv3 und damit
LDAP über TLS/SSL noch ein Problem für Pythonprogrammierer dar. Dies
ist nun gelöst, seit Michael Ströder im Sommer 2002 in die python-ldap-2.0.0pre06
LDADv3-Support eingebaut hat. Da z.B. die meisten Zope-Anwendungen
für LDAP (wie etwa \emph{LDAPUserFolder}, \emph{CMFUserFolder}, \emph{LDAPDirectoryManager},
\emph{LDAPAdapter}, \emph{ZopeLDAP}) python-ldap verwenden (eine Alternative
wäre ldaptor), ist die Verwendung von TLS/SSL sofort möglich, sobald
der Benutzer die openldap-Bibliotheken und python-ldap-Bibliothek
entsprechend upgraded und konfiguriert (siehe auch \cite{Chou03})
und für die Verbindungen benötigte CA-Zertifikate zur Verfügung stellt. 


\subsection{Verzeichnisse vs Datenbanken}

In der Frage {}``Verzeichnis oder Datenbank?'' ist unser Anwendungsfall
ein Grenzfall: Da sich die Kurswahl gut in die bestehende LDAP-Infrastruktur
einfügt (eine einfache Ergänzung von Gruppen und Gruppenattributen
in einem bestehenden Userverzeichnis, das Schema wurde z.B. nicht
erweitert) und nur leichtgewichtige Daten gespeichert werden, ist
die Verwendung des Verzeichnisses hierfür wohl noch vertretbar (ähnlich
wie bei \cite{Ri01b}); dies muss nicht für kompliziertere eher relational
strukturierte Anwendungsfälle gelten muss \cite{Koehn02,Ma01}. Ein
ausdrücklicher Gesichtspunkt bei der Entscheidung, diesen Anwendungsfall
in Verzeichnissen zu implementieren, war es, Erfahrungen über die
Zugänglichkeit von LDAP-Datenspeichern zu gewinnen, die auch bei typischeren
Aufgaben (Authentifizierung, Zertifikatsmanagement etc.) von Nutzen
sein können.


\section{Danksagungen}

Prof Klaus Osterkorn verdanken wir den Anwendungsfall; Christoph Wunder
und Helmut Naughton verdanken wir einige {}``SSL-Sitzungen''.


\section{Quellen, Addenda und Corrigenda}

\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/talkback/}{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/talkback/}.

\begin{thebibliography}{Koehn02}
\begin{raggedright}
\bibitem[Chad96]{Chad96}David Chadwick, Understanding X.500 - The Directory, \href{http://www.isi.salford.ac.uk/staff/dwc/Version.Web/Contents.htm}{http://www.isi.salford.ac.uk/staff/dwc/Version.Web/Contents.htm}.
\bibitem[Chou03]{Chou03}TC Chou, Setting LDAP, 2003, \href{http://zope.slat.org/Members/~tcchou/~index_html/setting_ldap}{http://zope.slat.org/Members/tcchou/index\_html/setting\_ldap}.
\bibitem[Gam94]{Gam94}Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns,
Addison-Wesley 1994.
\bibitem[Har00]{Har00}Robert G. Harrison, Jim Sermersheim, and Steve Trottier, LDAP Developer's
Guide, Novell Press 2000.
\bibitem[Kla01]{Kla01}Norbert Klasen, Directory Services for Linux in comparison with Novell
NDS and Microsoft Active Directory, Diploma Thesis, RWTH Aachen 2001,
\href{http://www.daasi.de/staff/norbert/thesis/html/}{http://www.daasi.de/staff/norbert/thesis/html/}
.
\bibitem[Koehn02]{Koehn02}Kristian Köhntopp, Directory Services vs. Relationale Datenbanken,
2002, \href{http://www.koehntopp.de/kris/artikel/dir-vs-rel/}{http://www.koehntopp.de/kris/artikel/dir-vs-rel/}.
\bibitem[Ma01]{Ma01}Vikas Mahajan, Directory, Database, or Both?, \href{http://www.ldapzone.com/~general\_interest.html\#2}{http://www.ldapzone.com/general\_interest.html\#2}.
\bibitem[Mar02]{Mar02}Franck Martin, SSL Certificates HOWTO, \href{http://www.ibiblio.org/~pub/Linux/docs/~HOWTO/~other-formats/html\_single/~SSL-Certificates-HOWTO.html}{http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html\_single/SSL-Certificates-HOWTO.html}.
\bibitem[Ra]{Ra}Raphinou, ldap installation with ssl, \href{http://www.raphinou.com/~ldaps/~LDAP-SSL.HOWTO}{http://www.raphinou.com/ldaps/LDAP-SSL.HOWTO}.
\bibitem[Re01]{Re01}Erich Rescorla, SSL and TLS, Addison-Wesley 2001.
\bibitem[Ri01a]{Ri01a}Mike Richichi, How to Use Perl, Python, and PHP to Access NDS eDirectory
8.5 via LDAP, Novell Appnotes, \href{http://developer.novell.com/research/appnotes/2001/august/05/~a010805.pdf}{http://developer.novell.com/research/appnotes/2001/august/05/a010805.pdf},
\href{ http://developer.novell.com/research/~appnotes/~2001/~august/05/apv.htm}{http://developer.novell.com/research/appnotes/2001/august/05/apv.htm}.
\bibitem[Ri01b]{Ri01b}Mike Richichi, ATTIC: a case study of directory-enabled course management,
Proceedings of 29th ACM SIGUCCS Conf, 2001, pp. 258 - 261.
\bibitem[Ser02]{Ser02}Jim Sermersheim, LDAP Schema for NDS, 2002, \href{http://www.ietf.org/internet-drafts/draft-sermersheim-nds-ldap-schema-03.txt}{http://www.ietf.org/internet-drafts/draft-sermersheim-nds-ldap-schema-03.txt}.
\end{raggedright}
\end{thebibliography}
\end{document}
