14

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

尽管遵循 init 中的所有常规步骤,包括

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 不成功;这LDAP_REFERRAL_LIMIT_EXCEEDED是从 AD 返回的错误。我也不能将其用作事实上的成功指标,因为:

>>> 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, "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;
4

2 回答 2

15

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 仍然会通过简单的绑定检查。

2003 Active Directory确实需要对不属于 rootDSE 属性的任何搜索进行身份验证,因此出于内部目的,我们在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')
于 2012-05-24T19:06:00.010 回答
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')
于 2012-05-24T16:50:35.380 回答