Similar to this question, I am trying to perform simple authentication to a 2003 Active Directory using python ldap (CentOS 6.2 x86_64, Python 2.6.6, python-ldap 2.3.10).
Despite following all the usual steps in the init, including
conn.set_option(ldap.OPT_REFERRALS, 0)
if I pass the correct credentials I always get a (97, [])
returned:
>>> import ldap
>>> conn = ldap.initialize('ldap://ad.server.domain.com')
>>> conn.protocol_version = 3
>>> conn.set_option(ldap.OPT_REFERRALS, 0)
>>> conn.simple_bind_s('user@domain.com', 'WrongPassword')
ldap.INVALID_CREDENTIALS: {'info': '80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 52e, vece', 'desc': 'Invalid credentials'}
>>> conn.simple_bind_s('user@domain.com', 'CorrectPassword')
(97, [])
Error code 97 is not a success; it's the LDAP_REFERRAL_LIMIT_EXCEEDED
error being returned from AD. Nor can I use it as a de facto success indicator, because:
>>> conn.simple_bind_s('', 'CorrectPassword')
(97, [])
>>> conn.simple_bind_s('', '')
(97, [])
Even more frustrating is that this script is a migration from an old Perl script using Net::LDAP, which does return 0 for a successful authenticated bind to the same AD and server.
All the information I can find on python-ldap indicates that what I am doing should Just Work; I would be inclined to think there's something wrong with the AD servers, but the Perl script does return the correct LDAP code on a successful bind.
I have tested python-ldap 2.2.0 and python 2.4.4 on an old CentOS 5.5 box I had lying around and it "fails" in exactly the same way.
Does anyone know what I am missing?
EDIT: Per request, here is the Perl script that works. Net::LDAP
returns the return code from the LDAP server, and the AD server is returning 0x00, "Successful request", AFAICT.
#!/usr/bin/perl -w
use strict;
use Net::LDAP;
## Corporate subdomains
my @domains = ("americas", "asia", "europe");
# AD connect timeout
my $timeout = 10;
# Set AD server info.
my $port = "389";
my $host = "server.domain.com";
my $user = shift @ARGV;
chomp $user;
my $password = <STDIN>;
$password =~ s/\r\n//;
chomp $password;
my $ldap = Net::LDAP->new($host, port => $port, timeout => $timeout ) ||
die "Unable to connect to LDAP server";
my $bind_return = 1;
foreach (@domains) {
my $result = $ldap->bind( "$user\@$_.domain.com", password => $password );
if( $result->code == 0) {
$bind_return = 0;
last;
}
}
## Unbind and return
$ldap->unbind;
if ($bind_return) { print "Authentication Failed. Access Denied\n" }
exit $bind_return;
This error means that your
conn.set_option(ldap.OPT_REFERRALS, 0)
isn't being affected.Therefore, try this:
Michael Ströder, the author of the python-ldap library, enlightened me thus:
The reason for these results:
is that out of the box 2003 Active Directory allows anonymous binds. So not providing a user id at all will still pass a simple bind check, if the only thing being tested is whether
simple_bind_s()
throws an error.2003 Active Directory does require authentication for any searches that aren't attributes of the rootDSE, so for our internal purposes we added a trivial search to the
try:
block: