Archives / Search ›

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.

No comments yet. Be the first.

Leave a reply