Wednesday, 2 June 2004
Kerberized LDAP from a scripting language?
Any scripting language? Any scripting language at all?
My goal: to set up an virtual map in Postfix, mapping usernames to email addresses where present, using a LDAP server (Windows 2000 Active Directory) as a data source, in a reasonably secure fashion. Using OpenLDAP 2.0.27, Cyrus SASL 2.1.15, ldapsearch on the command line did exactly what I wanted, so I thought it’d be simple enough to script. Was I ever wrong!
- Attempt 1, /etc/postfix/main.cf:
virtual_mailbox_maps = ldap:medicine medicine_server_host = med-banting.med.uiuc.edu medicine_query_filter = userPrincipalName=%s@med.uiuc.edu medicine_search_base = dc=med,dc=uiuc,dc=edu medicine_result_attribute = mail [...]
What to do for the [...]? Can’t use a simple bind: too insecure. The AD server is not configured to support SSL. Postfix doesn’t support GSSAPI. That leaves anonymous binding, which I couldn’t, after many hours with ADSI Edit, get to do what I wanted. Even if I accepted the security problem of allowing people to enumerate all the users at our site, I couldn’t get anything beyond a DN for domain administrators, some of whom require the above email mapping. It does not seem possible to limit access via IP address.
So, I resigned myself to running a cron job to populate a hash map or similar with periodic dumps of the LDAP information.
- Attempt 2, python-ldap 2.0.0, search.py:
#!/usr/bin/env python2.3 import ldap, ldap.sasl server = ldap.initialize("ldap://med-banting.med.uiuc.edu") server.sasl_interactive_bind_s("", ldap.sasl.gssapi("")) res = ldap.search_s("dc=med,dc=uiuc,dc=edu", ldap.SCOPE_BASE, "(&(mail=*)(objectClass=user))") print res server.unbind()
In competition for the least helpful error message of the century, up there with “An error has occurred: Success”:
% ./search.py Traceback (most recent call last): File "./search.py", line 8, in ? server.sasl_interactive_bind_s("", ldap.sasl.gssapi("")) File "/usr/lib/python2.3/site-packages/ldap/ldapobject.py", line 196, in sasl_interactive_bind_s return self._ldap_call(self._l.sasl_interactive_bind_s,who,auth,serverctrls,clientctrls) File "/usr/lib/python2.3/site-packages/ldap/ldapobject.py", line 94, in _ldap_call result = func(*args,**kwargs) ldap.LOCAL_ERROR: {'desc': 'Local error'}
I am not the only person with this problem.
- Attempt 3, Net::LDAP (part of perl-ldap 0.31), Authen::SASL::Cyrus 0.11, search.pl:
#!/usr/bin/perl use Net::LDAP; use Authen::SASL; my \$sasl = Authen::SASL->new('GSSAPI', 'user' => ''); my \$ldap = Net::LDAP->new('med-brevis.med.uiuc.edu', version => 3) || die "$@"; my \$mesg = \$ldap->bind("", sasl => \$sasl); \$mesg->code && die \$mesg->error; \$mesg = \$ldap->search(filter => '(&(mail=*)(objectClass=user))', base => 'dc=med,dc=uiuc,dc=edu', attrs => ['mail', 'userPrincipalName']); \$mesg->code && die \$mesg->error; @entries = \$mesg->entries; foreach \$entry (@entries) { \$entry->dump; } \$ldap->unbind;
This looks so good, as just a month ago, people claimed it worked. It doesn’t for me.
% ./search.pl No SASL mechanism found at /usr/lib/perl5/site_perl/5.8.0/Authen/SASL.pm line 62
After over eight hours of work on this, and assuming I don’t hear back from anyone by tomorrow, attempt #4 is going to be parsing the output of ldapsearch. I was going to get research work done today, too…
Posting code from PyDS’s RPC interface is a complete nightmare; it unescapes everything constantly, tries to interpret $-macros, etc… I guess Georg just uses reStructuredText, but that’s no use until I have a weblog editor that supports it.