使用python-ldap身份验证到Active Directory始终返回(97,[])

15

这个问题类似,我正在尝试使用python ldap(CentOS 6.2 x86_64,Python 2.6.6,python-ldap 2.3.10)对2003 Active Directory执行简单身份验证。

尽管按照初始化中的所有常规步骤进行操作,包括:

conn.set_option(ldap.OPT_REFERRALS, 0)
如果我传递正确的凭据,我总是会得到一个返回值为(97, [])的结果:
>>> 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, [])

错误代码97并不是成功的表示,而是从AD返回的LDAP_REFERRAL_LIMIT_EXCEEDED错误。我也不能将其用作事实上的成功指标,因为:

>>> conn.simple_bind_s('', 'CorrectPassword')
(97, [])
>>> conn.simple_bind_s('', '')
(97, [])
更令人沮丧的是,这个脚本是从一个使用 Net::LDAP 的旧 Perl 脚本迁移而来的,后者在向同一 AD 和服务器进行成功身份验证绑定时返回 0。
我能找到的有关 python-ldap 的所有信息都表明我的做法应该可以正常工作;我倾向于认为 AD 服务器出了问题,但 Perl 脚本在成功绑定时确实返回了正确的 LDAP 代码。
我已经在一个我刚好有的旧 CentOS 5.5 上测试了 python-ldap 2.2.0 和 python 2.4.4,结果以完全相同的方式“失败”。
有谁知道我错过了什么吗?
编辑:根据请求,这里是能够正常工作的 Perl 脚本。Net::LDAP 返回 LDAP 服务器的返回代码,AD 服务器返回的是 0x00,“成功请求”,据我所知。
#!/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;

你能发布相关的Perl代码吗? - stark
我假设你指的是ad.server.domain.com来匹配Python,而不是server.domain.com。此外,默认端口是389,但最好在URL中显式添加“:389”。 - stark
2个回答

17

python-ldap库的作者Michael Ströder这样告诉我:

97不是LDAP结果代码,它是结果类型   ldap.RES_BIND。通常情况下,您不必查看LDAPObject.simple_bind_s()返回的结果(除非您想提取绑定响应控件)。

     

如果LDAP结果代码不为0,则会引发相应的异常,例如您示例中的ldap.INVALID_CREDENTIALS。

     

因此,您的代码应该像这样:

try:
  conn.simple_bind_s('user@domain.com', 'WrongPassword')
except ldap.INVALID_CREDENTIALS:
  user_error_msg('wrong password provided')
这些结果的原因是:
>>> conn.simple_bind_s('', 'CorrectPassword')
(97, [])
>>> conn.simple_bind_s('', '')
(97, [])

2003年的Active Directory默认允许匿名绑定。因此,如果简单的绑定检查只测试simple_bind_s()是否会抛出错误,则根本不提供用户ID仍然会通过检查。

对于除了rootDSE属性之外的任何搜索,2003年的Active Directory确实需要身份验证,因此为了我们内部目的,我们在try:块中添加了一个微不足道的搜索:

try:
  conn.simple_bind_s('user@domain.com', 'SubmittedPassword')
  conn.search_st('DC=domain,DC=com', ldap.SCOPE_SUBTREE, '(objectClass=container)', 'name', 0, 30)
except ldap.INVALID_CREDENTIALS:
  user_error_msg('wrong password provided')

0
这个错误意味着你的 conn.set_option(ldap.OPT_REFERRALS, 0) 没有生效。
因此,请尝试以下操作:
import ldap

ldap.set_option(ldap.OPT_REFERRALS,0)
ldap.protocol_version = 3
conn = ldap.initialize('ldap://....')
conn.simple_bind_s('user@domain.com', 'RightPassword')

很抱歉,没有成功。仍然返回(97, [])。我已经通过conn.get_option()验证了这些选项是否正确设置为conn SimpleLDAPObject实例。 - Chris Doherty
我可能错了,但我认为在Perl脚本中,它正在使用user@europe.domain.com - Burhan Khalid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接