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.